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

Reply via email to