Author: Armin Rigo <ar...@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() _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit