[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