[pypy-commit] pypy reverse-debugger: bug fixes, there is more

arigo pypy.commits at gmail.com
Thu Jun 30 06:35:12 EDT 2016


Author: Armin Rigo <arigo at tunes.org>
Branch: reverse-debugger
Changeset: r85468:550befa14f60
Date: 2016-06-30 12:36 +0200
http://bitbucket.org/pypy/pypy/changeset/550befa14f60/

Log:	bug fixes, there is more

diff --git a/pypy/interpreter/reverse_debugging.py b/pypy/interpreter/reverse_debugging.py
--- a/pypy/interpreter/reverse_debugging.py
+++ b/pypy/interpreter/reverse_debugging.py
@@ -140,10 +140,13 @@
 def stop_point_at_start_of_line():
     if revdb.watch_save_state():
         any_watch_point = False
+        space = dbstate.space
         for prog, watch_id, expected in dbstate.watch_progs:
             any_watch_point = True
-            got = _run_watch(prog)
-            if got != expected:
+            try:
+                if _run_watch(space, prog) != expected:
+                    break
+            except OperationError:
                 break
         else:
             watch_id = -1
@@ -466,19 +469,15 @@
     space = dbstate.space
     try:
         code = interp_marshal.loads(space, space.wrap(marshalled_code))
-        w_res = code.exec_code(space, space.builtin, space.builtin)
-        text = space.str_w(space.repr(w_res))
+        text = _run_watch(space, code)
     except OperationError as e:
         revdb.send_watch(e.errorstr(space), ok_flag=0)
     else:
         revdb.send_watch(text, ok_flag=1)
 lambda_checkwatch = lambda: command_checkwatch
 
-def _run_watch(code):
-    space = dbstate.space
-    try:
-        w_res = code.exec_code(space, space.builtin, space.builtin)
-        text = space.str_w(space.repr(w_res))
-    except OperationError as e:
-        return e.errorstr(space)
-    return text
+
+def _run_watch(space, prog):
+    w_dict = space.builtin.w_dict
+    w_res = prog.exec_code(space, w_dict, w_dict)
+    return space.str_w(space.repr(w_res))
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
@@ -29,7 +29,7 @@
         ll_malloc_varsize = mh.ll_malloc_varsize
 
         fields = [("hash", lltype.Signed)]
-        if translator.config.translation.reverse_debugger:
+        if translator and translator.config.translation.reverse_debugger:
             fields.append(("uid", lltype.SignedLongLong))
         self.HDR = lltype.Struct("header", *fields)
         HDRPTR = lltype.Ptr(self.HDR)
@@ -74,11 +74,21 @@
         # XXX same behavior for zero=True: in theory that's wrong
         if TYPE._is_atomic():
             funcptr = self.malloc_fixedsize_atomic_ptr
+            opname = 'boehm_malloc_atomic'
         else:
             funcptr = self.malloc_fixedsize_ptr
-        v_raw = hop.genop("direct_call",
-                          [funcptr, c_size],
-                          resulttype=llmemory.GCREF)
+            opname = 'boehm_malloc'
+        tr = self.translator
+        if tr and tr.config.translation.reverse_debugger:
+            # Don't check for NULLs after the operation (it crashes anyway
+            # with an explicit error message in case of out-of-memory).
+            # Avoiding a direct_call lets _RPY_REVDB_PRUID() prints the
+            # right file/line, at least for fixed-size mallocs.
+            v_raw = hop.genop(opname, [c_size], resulttype=llmemory.GCREF)
+        else:
+            v_raw = hop.genop("direct_call",
+                              [funcptr, c_size],
+                              resulttype=llmemory.GCREF)
         finalizer_ptr = self.finalizer_funcptr_for_type(TYPE)
         if finalizer_ptr:
             c_finalizer_ptr = Constant(finalizer_ptr, self.FINALIZER_PTR)
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
@@ -279,20 +279,13 @@
 {
     /* Boehm only */
     if (obj->h_hash == 0) {
-        Signed h;
-        if (flag_io_disabled != FID_REGULAR_MODE) {
-            /* This is when running debug commands.  Don't cache the
-               hash on the object at all. */
-            return ~((Signed)obj);
-        }
-        /* When recording, we get the hash the normal way from the
-           pointer casted to an int, and record that.  When replaying,
-           we read it from the record.  In both cases, we cache the
-           hash in the object, so that we record/replay only once per
-           object. */
-        RPY_REVDB_EMIT(h = ~((Signed)obj);, Signed _e, h);
-        assert(h != 0);
-        obj->h_hash = h;
+        /* We never need to record anything: if h_hash is zero (which
+           is the case for all newly allocated objects), then we just
+           copy h_uid.  This gives a stable answer.  This would give
+           0 for all prebuilt objects, but these should not have a
+           null h_hash anyway.
+        */
+        obj->h_hash = obj->h_uid;
     }
     return obj->h_hash;
 }
@@ -853,6 +846,13 @@
 static void check_at_end(uint64_t stop_points)
 {
     char dummy[1];
+    if (rpy_revdb.buf_p != rpy_revdb.buf_limit - 1 ||
+            read(rpy_rev_fileno, dummy, 1) > 0) {
+        fprintf(stderr, "RevDB file error: corrupted file (too much data or, "
+                        "more likely, non-deterministic run, e.g. a "
+                        "watchpoint with side effects)\n");
+        exit(1);
+    }
     if (stop_points != rpy_revdb.stop_point_seen) {
         fprintf(stderr, "Bad number of stop points "
                 "(seen %lld, recorded %lld)\n",
@@ -860,11 +860,6 @@
                 (long long)stop_points);
         exit(1);
     }
-    if (rpy_revdb.buf_p != rpy_revdb.buf_limit - 1 ||
-            read(rpy_rev_fileno, dummy, 1) > 0) {
-        fprintf(stderr, "RevDB file error: corrupted file (too much data?)\n");
-        exit(1);
-    }
     if (stop_points != total_stop_points) {
         fprintf(stderr, "RevDB file modified while reading?\n");
         exit(1);
@@ -971,6 +966,7 @@
         memcpy(future_ids, extra, cmd->extra_size);
         future_ids[cmd->extra_size / sizeof(uint64_t)] = 0;
         uid_break = *future_ids;
+        attach_gdb();
     }
     future_next_id = future_ids;
 }
@@ -1168,10 +1164,17 @@
     return uid;
 }
 
+struct destructor_s {
+    void *d_obj;
+    void (*d_callback)(void *);
+};
+
 static int _ftree_compare(const void *obj1, const void *obj2)
 {
-    const struct pypy_header0 *h1 = obj1;
-    const struct pypy_header0 *h2 = obj2;
+    const struct destructor_s *d1 = obj1;
+    const struct destructor_s *d2 = obj2;
+    struct pypy_header0 *h1 = d1->d_obj;
+    struct pypy_header0 *h2 = d2->d_obj;
     if (h1->h_uid < h2->h_uid)
         return -1;
     if (h1->h_uid == h2->h_uid)
@@ -1180,26 +1183,60 @@
         return 1;
 }
 
+static void _ftree_add(void **tree, void *obj, void (*callback)(void *))
+{
+    /* Note: we always allocate an indirection through a 
+       struct destructor_s, so that Boehm knows that 'obj' must be
+       kept alive. */
+    struct destructor_s *node, **item;
+    node = GC_MALLOC_UNCOLLECTABLE(sizeof(struct destructor_s));
+    node->d_obj = obj;
+    node->d_callback = callback;
+    item = tsearch(node, tree, _ftree_compare);
+    if (item == NULL) {
+        fprintf(stderr, "_ftree_add: out of memory\n");
+        exit(1);
+    }
+    if (*item != node) {
+        fprintf(stderr, "_ftree_add: duplicate object\n");
+        exit(1);
+    }
+}
+
+static struct pypy_header0 *_ftree_pop(void **tree, uint64_t uid,
+                                       void (**callback_out)(void *))
+{
+    struct destructor_s d_dummy, *entry, **item;
+    struct pypy_header0 o_dummy, *result;
+
+    d_dummy.d_obj = &o_dummy;
+    o_dummy.h_uid = uid;
+    item = tfind(&d_dummy, tree, _ftree_compare);
+    if (item == NULL) {
+        fprintf(stderr, "_ftree_pop: object not found\n");
+        exit(1);
+    }
+    entry = *item;
+    result = entry->d_obj;
+    if (callback_out)
+        *callback_out = entry->d_callback;
+    assert(result->h_uid == uid);
+    tdelete(entry, tree, _ftree_compare);
+    GC_FREE(entry);
+    return result;
+}
+
 RPY_EXTERN
 int rpy_reverse_db_fq_register(void *obj)
 {
-    /*fprintf(stderr, "FINALIZER_TREE: %lld -> %p\n",
+    fprintf(stderr, "FINALIZER_TREE: %lld -> %p\n",
               ((struct pypy_header0 *)obj)->h_uid, obj);
-    */
     if (!RPY_RDB_REPLAY) {
         return 0;     /* recording */
     }
     else {
-        /* add the object into the finalizer_tree, keyed by the h_uid */
-        void **item = tsearch(obj, &finalizer_tree, _ftree_compare);
-        if (item == NULL) {
-            fprintf(stderr, "fq_register: out of memory\n");
-            exit(1);
-        }
-        if (*item != obj) {
-            fprintf(stderr, "fq_register: duplicate object\n");
-            exit(1);
-        }
+        /* add the object into the finalizer_tree, keyed by the h_uid. */
+        _ftree_add(&finalizer_tree, obj, NULL);
         return 1;     /* replaying */
     }
 }
@@ -1210,47 +1247,19 @@
     int64_t uid;
     RPY_REVDB_EMIT(uid = result ? ((struct pypy_header0 *)result)->h_uid : -1;,
                    int64_t _e, uid);
+    fprintf(stderr, "next_dead: object %lld\n", uid);
     if (RPY_RDB_REPLAY) {
         if (uid == -1) {
             result = NULL;
         }
         else {
             /* fetch and remove the object from the finalizer_tree */
-            void **item;
-            struct pypy_header0 dummy;
-            dummy.h_uid = uid;
-            item = tfind(&dummy, &finalizer_tree, _ftree_compare);
-            if (item == NULL) {
-                fprintf(stderr, "next_dead: object not found\n");
-                exit(1);
-            }
-            result = *item;
-            assert(((struct pypy_header0 *)result)->h_uid == uid);
-            tdelete(result, &finalizer_tree, _ftree_compare);
+            result = _ftree_pop(&finalizer_tree, uid, NULL);
         }
     }
     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 *))
 {
@@ -1259,23 +1268,7 @@
                               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);
-        }
+        _ftree_add(&destructor_tree, obj, callback);
     }
 }
 
@@ -1292,26 +1285,15 @@
 
     while (1) {
         int64_t uid;
-        struct destructor_s d_dummy, *entry, **item;
-        struct pypy_header0 o_dummy;
+        struct pypy_header0 *obj;
+        void (*callback)(void *);
 
         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, "replay_call_destructors: 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);
+        obj = _ftree_pop(&destructor_tree, uid, &callback);
+        callback(obj);
     }
 }
 
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
@@ -35,12 +35,19 @@
             "%s:%d: %0*llx\n",                                          \
             __FILE__, __LINE__, 2 * sizeof(_e),                         \
             ((unsigned long long)_e) & ((2ULL << (8*sizeof(_e)-1)) - 1))
+#endif
+
+#if 1    /* enable to print all allocs to stderr */
 #  define _RPY_REVDB_PRUID()                                    \
     fprintf(stderr,                                             \
             "%s:%d: obj %llu\n",                                \
             __FILE__, __LINE__, (unsigned long long) uid)
-#else
+#endif
+
+#ifndef _RPY_REVDB_PRINT
 #  define _RPY_REVDB_PRINT(mode)  /* nothing */
+#endif
+#ifndef _RPY_REVDB_PRUID
 #  define _RPY_REVDB_PRUID()      /* nothing */
 #endif
 
diff --git a/rpython/translator/revdb/test/test_basic.py b/rpython/translator/revdb/test/test_basic.py
--- a/rpython/translator/revdb/test/test_basic.py
+++ b/rpython/translator/revdb/test/test_basic.py
@@ -150,8 +150,7 @@
         assert match
         hash_value = int(match.group(1))
         rdb = self.fetch_rdb([self.exename, 'Xx'])
-        # compute_identity_hash() call, but only the first one
-        x = rdb.next(); assert intmask(x) == intmask(hash_value)
+        # compute_identity_hash() doesn't record anything
         # write() call
         x = rdb.next(); assert x == len(out)
         x = rdb.next('i'); assert x == 0      # errno


More information about the pypy-commit mailing list