[pypy-commit] pypy reverse-debugger: Weakrefs, in-progress

arigo pypy.commits at gmail.com
Fri Jun 24 13:40:41 EDT 2016


Author: Armin Rigo <arigo at tunes.org>
Branch: reverse-debugger
Changeset: r85376:fbdd9dca6850
Date: 2016-06-24 19:41 +0200
http://bitbucket.org/pypy/pypy/changeset/fbdd9dca6850/

Log:	Weakrefs, in-progress

diff --git a/rpython/config/translationoption.py b/rpython/config/translationoption.py
--- a/rpython/config/translationoption.py
+++ b/rpython/config/translationoption.py
@@ -284,7 +284,6 @@
                requires=[('translation.split_gc_address_space', True),
                          ('translation.jit', False),
                          ('translation.gc', 'boehm'),
-                         ("translation.rweakref", False),
                          ('translation.thread', False),
                          ('translation.continuation', False)]),
 ])
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
@@ -43,11 +43,14 @@
             self.malloc_varsize_ptr = self.inittime_helper(
                 ll_malloc_varsize, [lltype.Signed]*4, llmemory.GCREF, inline=False)
             if self.translator.config.translation.rweakref:
+                (ll_weakref_create, ll_weakref_deref,
+                 self.WEAKLINK, self.convert_weakref_to
+                        ) = build_weakref(self.translator.config)
                 self.weakref_create_ptr = self.inittime_helper(
                     ll_weakref_create, [llmemory.Address], llmemory.WeakRefPtr,
                     inline=False)
                 self.weakref_deref_ptr = self.inittime_helper(
-                    ll_weakref_deref, [llmemory.WeakRefPtr], llmemory.Address)
+                    ll_weakref_deref, [llmemory.WeakRefPtr], llmemory.GCREF)
 
             if not translator.config.translation.reverse_debugger:
                 def ll_identityhash(addr):
@@ -125,10 +128,10 @@
 
     def gct_weakref_create(self, hop):
         v_instance, = hop.spaceop.args
-        v_addr = hop.genop("cast_ptr_to_adr", [v_instance],
-                           resulttype=llmemory.Address)
+        v_gcref = hop.genop("cast_opaque_ptr", [v_instance],
+                            resulttype=llmemory.GCREF)
         v_wref = hop.genop("direct_call",
-                           [self.weakref_create_ptr, v_addr],
+                           [self.weakref_create_ptr, v_gcref],
                            resulttype=llmemory.WeakRefPtr)
         hop.cast_result(v_wref)
 
@@ -140,10 +143,10 @@
 
     def gct_weakref_deref(self, hop):
         v_wref, = hop.spaceop.args
-        v_addr = hop.genop("direct_call",
-                           [self.weakref_deref_ptr, v_wref],
-                           resulttype=llmemory.Address)
-        hop.cast_result(v_addr)
+        v_gcref = hop.genop("direct_call",
+                            [self.weakref_deref_ptr, v_wref],
+                            resulttype=llmemory.GCREF)
+        hop.cast_result(v_gcref)
 
     def gct_gc_writebarrier_before_copy(self, hop):
         # no write barrier needed
@@ -180,32 +183,50 @@
 # disappearing link.  We don't have to hide the link's value with
 # HIDE_POINTER(), because we explicitly use GC_MALLOC_ATOMIC().
 
-WEAKLINK = lltype.FixedSizeArray(llmemory.Address, 1)
-sizeof_weakreflink = llmemory.sizeof(WEAKLINK)
-empty_weaklink = lltype.malloc(WEAKLINK, immortal=True)
-empty_weaklink[0] = llmemory.NULL
+def build_weakref(config):
+    revdb = config.translation.reverse_debugger
+    if not revdb:
+        WEAKLINK = lltype.Struct('WEAKLINK',
+                                 ('addr', llmemory.Address))
+    else:
+        WEAKLINK = lltype.Struct('REVDB_WEAKLINK',
+                                 ('addr', llmemory.Address),
+                                 ('off_prev', lltype.SignedLongLong))
+    sizeof_weakreflink = llmemory.sizeof(WEAKLINK)
+    empty_weaklink = lltype.malloc(WEAKLINK, immortal=True, zero=True)
 
-def ll_weakref_create(targetaddr):
-    link = llop.boehm_malloc_atomic(llmemory.Address, sizeof_weakreflink)
-    if not link:
-        raise MemoryError
-    plink = llmemory.cast_adr_to_ptr(link, lltype.Ptr(WEAKLINK))
-    plink[0] = targetaddr
-    llop.boehm_disappearing_link(lltype.Void, link, targetaddr)
-    return llmemory.cast_ptr_to_weakrefptr(plink)
+    def ll_weakref_create(target_gcref):
+        if revdb:
+            plink = llop.revdb_weakref_create(lltype.Ptr(WEAKLINK),
+                                              target_gcref)
+        else:
+            link = llop.boehm_malloc_atomic(llmemory.Address,
+                                            sizeof_weakreflink)
+            if not link:
+                raise MemoryError
+            plink = llmemory.cast_adr_to_ptr(link, lltype.Ptr(WEAKLINK))
+            plink.addr = llmemory.cast_ptr_to_adr(target_gcref)
+            llop.boehm_disappearing_link(lltype.Void, link, target_gcref)
+        return llmemory.cast_ptr_to_weakrefptr(plink)
 
-def ll_weakref_deref(wref):
-    plink = llmemory.cast_weakrefptr_to_ptr(lltype.Ptr(WEAKLINK), wref)
-    return plink[0]
+    def ll_weakref_deref(wref):
+        plink = llmemory.cast_weakrefptr_to_ptr(lltype.Ptr(WEAKLINK), wref)
+        if revdb:
+            result = llop.revdb_weakref_deref(llmemory.GCREF, plink)
+        else:
+            result = llmemory.cast_adr_to_ptr(plink.addr, llmemory.GCREF)
+        return result
 
-def convert_weakref_to(targetptr):
-    # Prebuilt weakrefs don't really need to be weak at all,
-    # but we need to emulate the structure expected by ll_weakref_deref().
-    # This is essentially the same code as in ll_weakref_create(), but I'm
-    # not sure trying to share it is worth the hassle...
-    if not targetptr:
-        return empty_weaklink
-    else:
-        plink = lltype.malloc(WEAKLINK, immortal=True)
-        plink[0] = llmemory.cast_ptr_to_adr(targetptr)
-        return plink
+    def convert_weakref_to(targetptr):
+        # Prebuilt weakrefs don't really need to be weak at all,
+        # but we need to emulate the structure expected by ll_weakref_deref().
+        # This is essentially the same code as in ll_weakref_create(), but I'm
+        # not sure trying to share it is worth the hassle...
+        if not targetptr:
+            return empty_weaklink
+        else:
+            plink = lltype.malloc(WEAKLINK, immortal=True, zero=True)
+            plink.addr = llmemory.cast_ptr_to_adr(targetptr)
+            return plink
+
+    return ll_weakref_create, ll_weakref_deref, WEAKLINK, convert_weakref_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
@@ -572,6 +572,8 @@
     'revdb_get_unique_id':  LLOp(sideeffects=False),
     'revdb_watch_save_state':    LLOp(),
     'revdb_watch_restore_state': LLOp(),
+    'revdb_weakref_create': LLOp(),
+    'revdb_weakref_deref':  LLOp(),
 }
 # ***** Run test_lloperation after changes. *****
 
diff --git a/rpython/translator/c/gc.py b/rpython/translator/c/gc.py
--- a/rpython/translator/c/gc.py
+++ b/rpython/translator/c/gc.py
@@ -222,12 +222,10 @@
         yield 'boehm_gc_startup_code();'
 
     def get_real_weakref_type(self):
-        from rpython.memory.gctransform import boehm
-        return boehm.WEAKLINK
+        return self.db.gctransformer.WEAKLINK
 
     def convert_weakref_to(self, ptarget):
-        from rpython.memory.gctransform import boehm
-        return boehm.convert_weakref_to(ptarget)
+        return self.db.gctransformer.convert_weakref_to(ptarget)
 
     def OP_GC__COLLECT(self, funcgen, op):
         return 'GC_gcollect();'
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
@@ -1,6 +1,8 @@
 #include "common_header.h"
 #include <stdlib.h>
+#include <stdio.h>
 #include <string.h>
+#include <assert.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <fcntl.h>
@@ -13,11 +15,15 @@
 #include "forwarddecl.h"
 #include "preimpl.h"
 #include "src/rtyper.h"
+#include "src/mem.h"
 #include "src-revdb/revdb_include.h"
 
 #define RDB_SIGNATURE   "RevDB:"
 #define RDB_VERSION     0x00FF0001
 
+#define WEAKREF_AFTERWARDS_DEAD    ((char)0xf2)
+#define WEAKREF_AFTERWARDS_ALIVE   ((char)0xeb)
+
 
 typedef struct {
     Signed version;
@@ -170,6 +176,124 @@
     return obj->h_hash;
 }
 
+static int64_t recording_offset(void)
+{
+    off_t base_offset;
+    ssize_t extra_size = rpy_revdb.buf_p - rpy_rev_buffer;
+
+    if (rpy_rev_buffer < 0)
+        return -1;
+    base_offset = lseek(rpy_rev_fileno, 0, SEEK_CUR);
+    if (base_offset < 0) {
+        perror("lseek");
+        exit(1);
+    }
+    return base_offset + extra_size;
+}
+
+static void patch_prev_offset(int64_t offset, char old, char new)
+{
+    off_t base_offset;
+    if (rpy_rev_fileno < 0)
+        return;
+    base_offset = lseek(rpy_rev_fileno, 0, SEEK_CUR);
+    if (base_offset < 0) {
+        perror("lseek");
+        exit(1);
+    }
+    if (offset < base_offset) {
+        char got;
+        if (pread(rpy_rev_fileno, &got, 1, offset) != 1) {
+            fprintf(stderr, "can't read log position %lld for checking: %m\n",
+                    (long long)offset);
+            exit(1);
+        }
+        if (got != old) {
+            fprintf(stderr,
+                    "bad byte at log position %lld (%d instead of %d)\n",
+                    (long long)offset, got, old);
+            exit(1);
+        }
+        if (pwrite(rpy_rev_fileno, &new, 1, offset) != 1) {
+            fprintf(stderr, "can't patch log position %lld\n", offset);
+            exit(1);
+        }
+    }
+    else {
+        ssize_t buffered_size = rpy_revdb.buf_p - rpy_rev_buffer;
+        int64_t buf_ofs = offset - base_offset;
+        if (buf_ofs >= buffered_size) {
+            fprintf(stderr, "invalid patch position %lld\n",
+                    (long long)offset);
+            exit(1);
+        }
+        if (rpy_rev_buffer[buf_ofs] != old) {
+            fprintf(stderr,
+                    "bad byte at log position %lld (%d instead of %d)\n",
+                    (long long)offset, rpy_rev_buffer[buf_ofs], old);
+            exit(1);
+        }
+        rpy_rev_buffer[buf_ofs] = new;
+    }
+}
+
+RPY_EXTERN
+void *rpy_reverse_db_weakref_create(void *target)
+{
+    /* see comments in ../test/test_weak.py */
+    struct pypy_REVDB_WEAKLINK0 *r;
+    r = malloc(sizeof(struct pypy_REVDB_WEAKLINK0));
+    if (!r) {
+        fprintf(stderr, "out of memory for a weakref\n");
+        exit(1);
+    }
+    r->re_addr = target;
+    r->re_off_prev = 0;
+
+    if (!flag_io_disabled) {
+        char alive;
+        /* Emit WEAKREF_AFTERWARDS_DEAD, but remember where we emit it.
+           If we deref the weakref and it is still alive, we will patch
+           it with WEAKREF_AFTERWARDS_ALIVE. */
+        if (!RPY_RDB_REPLAY)
+            r->re_off_prev = recording_offset();
+
+        RPY_REVDB_EMIT(alive = WEAKREF_AFTERWARDS_DEAD;, char _e, alive);
+
+        if (!RPY_RDB_REPLAY) {
+            OP_BOEHM_DISAPPEARING_LINK(r, target, /*nothing*/);
+        }
+        else if (alive == WEAKREF_AFTERWARDS_DEAD)
+            r->re_addr = NULL;
+    }
+    return r;
+}
+
+RPY_EXTERN
+void *rpy_reverse_db_weakref_deref(void *weakref)
+{
+    struct pypy_REVDB_WEAKLINK0 *r = (struct pypy_REVDB_WEAKLINK0 *)weakref;
+    void *result = r->re_addr;
+    if (result && !flag_io_disabled) {
+        char alive;
+        if (!RPY_RDB_REPLAY) {
+            if (r->re_off_prev <= 0) {
+                fprintf(stderr, "bug in weakrefs: bad previous offset %lld\n",
+                        (long long)r->re_off_prev);
+                exit(1);
+            }
+            patch_prev_offset(r->re_off_prev, WEAKREF_AFTERWARDS_DEAD,
+                                              WEAKREF_AFTERWARDS_ALIVE);
+            r->re_off_prev = recording_offset();
+        }
+        RPY_REVDB_EMIT(alive = WEAKREF_AFTERWARDS_DEAD;, char _e, alive);
+
+        if (RPY_RDB_REPLAY && alive == WEAKREF_AFTERWARDS_DEAD)
+            r->re_addr = NULL;
+    }
+    return result;
+}
+
 
 /* ------------------------------------------------------------ */
 /* Replaying mode                                               */
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
@@ -107,6 +107,12 @@
 #define OP_REVDB_WATCH_RESTORE_STATE(any_watch_point, r)                \
     rpy_reverse_db_watch_restore_state(any_watch_point)
 
+#define OP_REVDB_WEAKREF_CREATE(target, r)                              \
+    r = rpy_reverse_db_weakref_create(target)
+
+#define OP_REVDB_WEAKREF_DEREF(weakref, r)                              \
+    r = rpy_reverse_db_weakref_deref(weakref)
+
 RPY_EXTERN void rpy_reverse_db_flush(void);
 RPY_EXTERN char *rpy_reverse_db_fetch(int expected_size,
                                       const char *file, int line);
@@ -119,5 +125,7 @@
 RPY_EXTERN uint64_t rpy_reverse_db_unique_id_break(void *new_object);
 RPY_EXTERN void rpy_reverse_db_watch_save_state(void);
 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);
 
 /* ------------------------------------------------------------ */
diff --git a/rpython/translator/revdb/src-revdb/revdb_preinclude.h b/rpython/translator/revdb/src-revdb/revdb_preinclude.h
--- a/rpython/translator/revdb/src-revdb/revdb_preinclude.h
+++ b/rpython/translator/revdb/src-revdb/revdb_preinclude.h
@@ -6,4 +6,5 @@
     int64_t arg1;
     int64_t arg2;
     int64_t arg3;
+    /* char extra[extra_size]; */
 } rpy_revdb_command_t;
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
@@ -64,7 +64,6 @@
     t = Translation(entry_point, None, gc="boehm")
     self.t = t
     t.config.translation.reverse_debugger = True
-    t.config.translation.rweakref = False
     t.config.translation.lldebug0 = True
     if withsmallfuncsets is not None:
         t.config.translation.withsmallfuncsets = withsmallfuncsets
@@ -91,11 +90,14 @@
     return RDB(self.rdbname, map(str, expected_argv))
 
 
-class TestRecording(object):
+class BaseRecordingTests(object):
     compile = compile
     run = run
     fetch_rdb = fetch_rdb
 
+
+class TestRecording(BaseRecordingTests):
+
     def test_simple(self):
         def main(argv):
             print argv[1:]
diff --git a/rpython/translator/revdb/test/test_weak.py b/rpython/translator/revdb/test/test_weak.py
new file mode 100644
--- /dev/null
+++ b/rpython/translator/revdb/test/test_weak.py
@@ -0,0 +1,68 @@
+import weakref
+from rpython.translator.revdb.test.test_basic import BaseRecordingTests
+
+
+# Weakrefs: implemented so that the log file contains one byte
+# "afterwards_alive" or "afterwards_dead" at the point where the
+# weakref is created, and similarly one such byte at every point where
+# the weakref is dereferenced.  At every point, the weakref is _alive_
+# at the moment; but the byte tells whether it should stay alive until
+# the _next_ point or not.  Done by always emitting "afterwards_dead"
+# in the log, and patching that to "afterwards_alive" if later we find
+# a deref() where the weakref is still alive.  (If a deref() finds the
+# weakref dead, it doesn't do any recording or patching; it simply
+# leaves the previous "afterwards_dead" in place.)
+
+
+WEAKREF_AFTERWARDS_DEAD  = chr(0xf2)
+WEAKREF_AFTERWARDS_ALIVE = chr(0xeb)
+
+
+class TestRecordingWeakref(BaseRecordingTests):
+
+    def test_weakref_create(self):
+        class X:
+            pass
+        class Glob:
+            pass
+        glob = Glob()
+        def main(argv):
+            glob.r1 = weakref.ref(X())
+            glob.r2 = weakref.ref(X())
+            glob.r3 = weakref.ref(X())
+            return 9
+        self.compile(main, [], backendopt=False)
+        out = self.run('Xx')
+        rdb = self.fetch_rdb([self.exename, 'Xx'])
+        # find the extra WEAKREF_DEAD
+        x = rdb.next('c'); assert x == WEAKREF_AFTERWARDS_DEAD
+        x = rdb.next('c'); assert x == WEAKREF_AFTERWARDS_DEAD
+        x = rdb.next('c'); assert x == WEAKREF_AFTERWARDS_DEAD
+        x = rdb.next('q'); assert x == 0      # number of stop points
+        assert rdb.done()
+
+    def test_weakref_deref_nondead(self):
+        class X:
+            pass
+        class Glob:
+            pass
+        glob = Glob()
+        def main(argv):
+            x1 = X(); x2 = X()
+            r1 = weakref.ref(x1)
+            r2 = weakref.ref(x2)
+            assert r1() is x1
+            assert r2() is x2
+            assert r1() is x1
+            return 9
+        self.compile(main, [], backendopt=False)
+        out = self.run('Xx')
+        rdb = self.fetch_rdb([self.exename, 'Xx'])
+        # find the five WEAKREF_xxx
+        x = rdb.next('c'); assert x == WEAKREF_AFTERWARDS_ALIVE  # r1=ref(x1)
+        x = rdb.next('c'); assert x == WEAKREF_AFTERWARDS_ALIVE  # r2=ref(x2)
+        x = rdb.next('c'); assert x == WEAKREF_AFTERWARDS_ALIVE  # r1()
+        x = rdb.next('c'); assert x == WEAKREF_AFTERWARDS_DEAD   # r2()
+        x = rdb.next('c'); assert x == WEAKREF_AFTERWARDS_DEAD   # r1()
+        x = rdb.next('q'); assert x == 0      # number of stop points
+        assert rdb.done()


More information about the pypy-commit mailing list