Author: Armin Rigo <[email protected]>
Branch: reverse-debugger
Changeset: r85405:e8bcbbe2187a
Date: 2016-06-27 17:53 +0200
http://bitbucket.org/pypy/pypy/changeset/e8bcbbe2187a/
Log: old-style finalizers, recording
diff --git a/rpython/memory/gctransform/boehm.py
b/rpython/memory/gctransform/boehm.py
--- a/rpython/memory/gctransform/boehm.py
+++ b/rpython/memory/gctransform/boehm.py
@@ -10,7 +10,7 @@
class BoehmGCTransformer(GCTransformer):
malloc_zero_filled = True
- FINALIZER_PTR = lltype.Ptr(lltype.FuncType([llmemory.Address],
lltype.Void))
+ FINALIZER_PTR = lltype.Ptr(lltype.FuncType([llmemory.GCREF], lltype.Void))
def __init__(self, translator, inline=False):
super(BoehmGCTransformer, self).__init__(translator, inline=inline)
@@ -112,18 +112,18 @@
destrptr = None
DESTR_ARG = None
- if self.translator.config.translation.reverse_debugger:
- destrptr = None # XXX for now
-
if destrptr:
EXC_INSTANCE_TYPE =
self.translator.rtyper.exceptiondata.lltype_of_exception_value
typename = TYPE.__name__
- def ll_finalizer(addr):
+ revdb = self.translator.config.translation.reverse_debugger
+ def ll_finalizer(gcref):
exc_instance = llop.gc_fetch_exception(EXC_INSTANCE_TYPE)
- v = llmemory.cast_adr_to_ptr(addr, DESTR_ARG)
+ if revdb:
+ llop.revdb_call_destructor(lltype.Void, gcref)
+ v = lltype.cast_opaque_ptr(DESTR_ARG, gcref)
ll_call_destructor(destrptr, v, typename)
llop.gc_restore_exception(lltype.Void, exc_instance)
- fptr = self.annotate_finalizer(ll_finalizer, [llmemory.Address],
lltype.Void)
+ fptr = self.annotate_finalizer(ll_finalizer, [llmemory.GCREF],
lltype.Void)
else:
fptr = lltype.nullptr(self.FINALIZER_PTR.TO)
diff --git a/rpython/rtyper/lltypesystem/lloperation.py
b/rpython/rtyper/lltypesystem/lloperation.py
--- a/rpython/rtyper/lltypesystem/lloperation.py
+++ b/rpython/rtyper/lltypesystem/lloperation.py
@@ -574,6 +574,7 @@
'revdb_watch_restore_state': LLOp(),
'revdb_weakref_create': LLOp(),
'revdb_weakref_deref': LLOp(),
+ 'revdb_call_destructor': LLOp(),
}
# ***** Run test_lloperation after changes. *****
diff --git a/rpython/translator/c/funcgen.py b/rpython/translator/c/funcgen.py
--- a/rpython/translator/c/funcgen.py
+++ b/rpython/translator/c/funcgen.py
@@ -597,6 +597,9 @@
return self._op_boehm_malloc(op, 1)
def OP_BOEHM_REGISTER_FINALIZER(self, op):
+ if self.db.reverse_debugger:
+ from rpython.translator.revdb import gencsupp
+ return gencsupp.boehm_register_finalizer(self, op)
return 'GC_REGISTER_FINALIZER(%s, (GC_finalization_proc)%s, NULL,
NULL, NULL);' \
% (self.expr(op.args[0]), self.expr(op.args[1]))
diff --git a/rpython/translator/revdb/gencsupp.py
b/rpython/translator/revdb/gencsupp.py
--- a/rpython/translator/revdb/gencsupp.py
+++ b/rpython/translator/revdb/gencsupp.py
@@ -21,6 +21,10 @@
def record_malloc_uid(expr):
return ' RPY_REVDB_REC_UID(%s);' % (expr,)
+def boehm_register_finalizer(funcgen, op):
+ return 'rpy_reverse_db_register_destructor(%s, %s);' % (
+ funcgen.expr(op.args[0]), funcgen.expr(op.args[1]))
+
def prepare_database(db):
FUNCPTR = lltype.Ptr(lltype.FuncType([revdb._CMDPTR, lltype.Ptr(rstr.STR)],
diff --git a/rpython/translator/revdb/src-revdb/revdb.c
b/rpython/translator/revdb/src-revdb/revdb.c
--- a/rpython/translator/revdb/src-revdb/revdb.c
+++ b/rpython/translator/revdb/src-revdb/revdb.c
@@ -211,6 +211,8 @@
boehm_fq_trigger[i++]();
}
+static long in_invoke_finalizers;
+
static void record_stop_point(void)
{
/* Invoke the finalizers now. This will call boehm_fq_callback(),
@@ -219,24 +221,54 @@
*/
int i;
char *p = rpy_rev_buffer;
+ int64_t done;
rpy_reverse_db_flush();
- GC_invoke_finalizers();
fq_trigger();
- /* This should all be done without emitting anything to the rdb
+ /* This should be done without emitting anything to the rdb
log. We check that, and emit just a ASYNC_FINALIZER_TRIGGER.
*/
if (current_packet_size() != 0) {
fprintf(stderr,
- "record_stop_point emitted unexpectedly to the rdb log\n");
+ "record_stop_point emitted unexpected data into the rdb
log\n");
exit(1);
}
*(int16_t *)p = ASYNC_FINALIZER_TRIGGER;
memcpy(rpy_revdb.buf_p, &rpy_revdb.stop_point_seen, sizeof(uint64_t));
rpy_revdb.buf_p += sizeof(uint64_t);
flush_buffer();
+
+ /* Invoke all Boehm finalizers. For new-style finalizers, this
+ will only cause them to move to the queues, where
+ boehm_fq_next_dead() will be able to fetch them later. For
+ old-style finalizers, this will really call the finalizers,
+ which first emit to the rdb log the uid of the object. So
+ after we do that any number of times, we emit the uid -1 to
+ mean "now done, continue with the rest of the program".
+ */
+ in_invoke_finalizers++;
+ GC_invoke_finalizers();
+ in_invoke_finalizers--;
+ RPY_REVDB_EMIT(done = -1;, int64_t _e, done);
+}
+
+RPY_EXTERN
+void rpy_reverse_db_call_destructor(void *obj)
+{
+ /* old-style finalizers. Should occur only from the
+ GC_invoke_finalizers() call above.
+ */
+ int64_t uid;
+
+ if (RPY_RDB_REPLAY)
+ return;
+ if (!in_invoke_finalizers) {
+ fprintf(stderr, "call_destructor: called at an unexpected time\n");
+ exit(1);
+ }
+ RPY_REVDB_EMIT(uid = ((struct pypy_header0 *)obj)->h_uid;, int64_t _e,
uid);
}
RPY_EXTERN
@@ -462,7 +494,7 @@
static int last_recorded_breakpoint_num;
static char breakpoint_mode;
static uint64_t *future_ids, *future_next_id;
-static void *finalizer_tree;
+static void *finalizer_tree, *destructor_tree;
static void attach_gdb(void)
{
@@ -649,7 +681,6 @@
exit(1);
}
- fetch_more_if_needed:
keep = rpy_revdb.buf_readend - rpy_revdb.buf_p;
assert(keep >= 0);
@@ -681,7 +712,10 @@
}
finalizer_trigger_saved_break = rpy_revdb.stop_point_break;
rpy_revdb.stop_point_break = bp;
- goto fetch_more_if_needed;
+ /* Now we should not fetch anything more until we reach
+ that finalizer_trigger_saved_break point. */
+ rpy_revdb.buf_limit = rpy_revdb.buf_p;
+ return;
default:
fprintf(stderr, "bad packet header %d", (int)header);
@@ -945,13 +979,12 @@
rpy_revdb.watch_enabled = any_watch_point;
}
+static void replay_call_destructors(void);
+
static void replay_stop_point(void)
{
- if (finalizer_trigger_saved_break != 0) {
- rpy_revdb.stop_point_break = finalizer_trigger_saved_break;
- finalizer_trigger_saved_break = 0;
- fq_trigger();
- }
+ if (finalizer_trigger_saved_break != 0)
+ replay_call_destructors();
while (rpy_revdb.stop_point_break == rpy_revdb.stop_point_seen) {
save_state();
@@ -1140,4 +1173,85 @@
return result;
}
+struct destructor_s {
+ void *obj;
+ void (*callback)(void *);
+};
+
+ static int _dtree_compare(const void *obj1, const void *obj2)
+{
+ const struct destructor_s *d1 = obj1;
+ const struct destructor_s *d2 = obj2;
+ const struct pypy_header0 *h1 = d1->obj;
+ const struct pypy_header0 *h2 = d2->obj;
+ if (h1->h_uid < h2->h_uid)
+ return -1;
+ if (h1->h_uid == h2->h_uid)
+ return 0;
+ else
+ return 1;
+}
+
+RPY_EXTERN
+void rpy_reverse_db_register_destructor(void *obj, void (*callback)(void *))
+{
+ if (!RPY_RDB_REPLAY) {
+ GC_REGISTER_FINALIZER(obj, (GC_finalization_proc)callback,
+ NULL, NULL, NULL);
+ }
+ else {
+ struct destructor_s **item;
+ struct destructor_s *node = malloc(sizeof(struct destructor_s));
+ if (!node) {
+ fprintf(stderr, "register_destructor: malloc: out of memory\n");
+ exit(1);
+ }
+ node->obj = obj;
+ node->callback = callback;
+ item = tsearch(node, &destructor_tree, _dtree_compare);
+ if (item == NULL) {
+ fprintf(stderr, "register_destructor: tsearch: out of memory\n");
+ exit(1);
+ }
+ if (*item != node) {
+ fprintf(stderr, "register_destructor: duplicate object\n");
+ exit(1);
+ }
+ }
+}
+
+static void replay_call_destructors(void)
+{
+ rpy_revdb.stop_point_break = finalizer_trigger_saved_break;
+ finalizer_trigger_saved_break = 0;
+ fq_trigger();
+
+ /* Re-enable fetching more, and fetch the uid's of objects
+ with old-style destructors that die now.
+ */
+ rpy_reverse_db_fetch(__FILE__, __LINE__);
+ while (1) {
+ int64_t uid;
+ struct destructor_s d_dummy, *entry, **item;
+ struct pypy_header0 o_dummy;
+ RPY_REVDB_EMIT(abort();, int64_t _e, uid);
+ if (uid == -1)
+ break;
+
+ d_dummy.obj = &o_dummy;
+ o_dummy.h_uid = uid;
+ item = tfind(&d_dummy, &destructor_tree, _dtree_compare);
+ if (item == NULL) {
+ fprintf(stderr, "next_dead: object not found\n");
+ exit(1);
+ }
+ entry = *item;
+ assert(((struct pypy_header0 *)entry->obj)->h_uid == uid);
+ tdelete(entry, &destructor_tree, _dtree_compare);
+
+ entry->callback(entry->obj);
+ free(entry);
+ }
+}
+
/* ------------------------------------------------------------ */
diff --git a/rpython/translator/revdb/src-revdb/revdb_include.h
b/rpython/translator/revdb/src-revdb/revdb_include.h
--- a/rpython/translator/revdb/src-revdb/revdb_include.h
+++ b/rpython/translator/revdb/src-revdb/revdb_include.h
@@ -126,8 +126,9 @@
RPY_EXTERN void rpy_reverse_db_watch_restore_state(bool_t any_watch_point);
RPY_EXTERN void *rpy_reverse_db_weakref_create(void *target);
RPY_EXTERN void *rpy_reverse_db_weakref_deref(void *weakref);
-//RPY_EXTERN void rpy_reverse_db_call_destructor(void *obj);
RPY_EXTERN int rpy_reverse_db_fq_register(void *obj);
RPY_EXTERN void *rpy_reverse_db_next_dead(void *result);
+RPY_EXTERN void rpy_reverse_db_register_destructor(void *obj, void(*)(void *));
+RPY_EXTERN void rpy_reverse_db_call_destructor(void *obj);
/* ------------------------------------------------------------ */
diff --git a/rpython/translator/revdb/test/test_weak.py
b/rpython/translator/revdb/test/test_weak.py
--- a/rpython/translator/revdb/test/test_weak.py
+++ b/rpython/translator/revdb/test/test_weak.py
@@ -28,6 +28,7 @@
def get_finalizer_queue_main():
from rpython.rtyper.lltypesystem import lltype, rffi
+ #
from rpython.translator.tool.cbuild import ExternalCompilationInfo
eci = ExternalCompilationInfo(
pre_include_bits=["#define foobar(x) x\n"])
@@ -71,6 +72,36 @@
return 9
return main
+def get_old_style_finalizer_main():
+ from rpython.rtyper.lltypesystem import lltype, rffi
+ from rpython.translator.tool.cbuild import ExternalCompilationInfo
+ #
+ eci = ExternalCompilationInfo(
+ pre_include_bits=["#define foobar(x) x\n"])
+ foobar = rffi.llexternal('foobar', [lltype.Signed], lltype.Signed,
+ compilation_info=eci, _nowrapper=True)
+ class Glob:
+ pass
+ glob = Glob()
+ class X:
+ def __del__(self):
+ assert foobar(-7) == -7
+ glob.count += 1
+ def main(argv):
+ glob.count = 0
+ lst = [X() for i in range(3000)]
+ x = -1
+ for i in range(3000):
+ lst[i] = None
+ if i % 300 == 150:
+ rgc.collect()
+ revdb.stop_point()
+ x = glob.count
+ assert foobar(x) == x
+ print x
+ return 9
+ return main
+
class TestRecording(BaseRecordingTests):
@@ -174,47 +205,36 @@
total = intmask(total * 3 + d[uid])
assert total == expected
- def test_finalizer_recorded(self):
- py.test.skip("in-progress")
- from rpython.rtyper.lltypesystem import lltype, rffi
- from rpython.translator.tool.cbuild import ExternalCompilationInfo
- eci = ExternalCompilationInfo(
- pre_include_bits=["#define foobar(x) x\n"])
- foobar = rffi.llexternal('foobar', [lltype.Signed], lltype.Signed,
- compilation_info=eci)
- class Glob:
- pass
- glob = Glob()
- class X:
- def __del__(self):
- glob.count += 1
- def main(argv):
- glob.count = 0
- lst = [X() for i in range(3000)]
- x = -1
- for i in range(3000):
- lst[i] = None
- if i % 300 == 150:
- rgc.collect()
- revdb.stop_point()
- x = glob.count
- assert foobar(x) == x
- print x
- return 9
+ def test_old_style_finalizer(self):
+ main = get_old_style_finalizer_main()
self.compile(main, backendopt=False)
out = self.run('Xx')
assert 1500 < int(out) <= 3000
rdb = self.fetch_rdb([self.exename, 'Xx'])
- counts = [rdb.next() for i in range(3000)]
- assert counts[0] >= 0
- for i in range(len(counts)-1):
- assert counts[i] <= counts[i + 1]
- assert counts[-1] == int(out)
+ seen_uids = set()
+ for i in range(3000):
+ triggered = False
+ if rdb.is_special_packet():
+ time, = rdb.special_packet(ASYNC_FINALIZER_TRIGGER, 'q')
+ assert time == i + 1
+ triggered = True
+ x = intmask(rdb.next())
+ while True:
+ assert x != -1
+ assert x not in seen_uids
+ seen_uids.add(x)
+ y = intmask(rdb.next())
+ assert y == -7 # from the __del__
+ x = intmask(rdb.next())
+ if x == -1:
+ break
+ x = rdb.next()
+ assert x == len(seen_uids)
+ assert len(seen_uids) == int(out)
# write() call
x = rdb.next(); assert x == len(out)
x = rdb.next('i'); assert x == 0 # errno
x = rdb.next('q'); assert x == 3000 # number of stop points
- assert rdb.done()
class TestReplayingWeakref(InteractiveTests):
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit