[pypy-commit] pypy reverse-debugger: Finalizers: recording
arigo
pypy.commits at gmail.com
Mon Jun 27 08:59:02 EDT 2016
Author: Armin Rigo <arigo at tunes.org>
Branch: reverse-debugger
Changeset: r85397:0477d9c6b062
Date: 2016-06-27 15:00 +0200
http://bitbucket.org/pypy/pypy/changeset/0477d9c6b062/
Log: Finalizers: recording
diff --git a/rpython/translator/c/src/mem.c b/rpython/translator/c/src/mem.c
--- a/rpython/translator/c/src/mem.c
+++ b/rpython/translator/c/src/mem.c
@@ -80,6 +80,9 @@
RPY_EXTERN void (*boehm_fq_trigger[])(void);
int boehm_gc_finalizer_lock = 0;
+void boehm_gc_finalizer_notifier(void);
+
+#ifndef RPY_REVERSE_DEBUGGER
void boehm_gc_finalizer_notifier(void)
{
int i;
@@ -101,6 +104,10 @@
boehm_gc_finalizer_lock--;
}
+#else
+/* see revdb.c */
+RPY_EXTERN void rpy_reverse_db_next_dead(void *);
+#endif
static void mem_boehm_ignore(char *msg, GC_word arg)
{
@@ -128,12 +135,17 @@
void *boehm_fq_next_dead(struct boehm_fq_s **fqueue)
{
struct boehm_fq_s *node = *fqueue;
+ void *result;
if (node != NULL) {
*fqueue = node->next;
- return node->obj;
+ result = node->obj;
}
else
- return NULL;
+ result = NULL;
+#ifdef RPY_REVERSE_DEBUGGER
+ rpy_reverse_db_next_dead(result);
+#endif
+ return result;
}
#endif /* BOEHM GC */
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
@@ -25,7 +25,7 @@
#define WEAKREF_AFTERWARDS_DEAD ((char)0xf2)
#define WEAKREF_AFTERWARDS_ALIVE ((char)0xeb)
-//#define ASYNC_FINALIZER ((int16_t)0xff46)
+#define ASYNC_FINALIZER_TRIGGER ((int16_t)0xff46)
typedef struct {
@@ -82,6 +82,18 @@
check_at_end(stop_points);
}
+static void record_stop_point(void);
+static void replay_stop_point(void);
+
+RPY_EXTERN
+void rpy_reverse_db_stop_point(void)
+{
+ if (!RPY_RDB_REPLAY)
+ record_stop_point();
+ else
+ replay_stop_point();
+}
+
/* ------------------------------------------------------------ */
/* Recording mode */
@@ -145,23 +157,81 @@
static void flush_buffer(void)
{
/* write the current buffer content to the OS */
- ssize_t size = rpy_revdb.buf_p - rpy_rev_buffer;
+ ssize_t full_size = rpy_revdb.buf_p - rpy_rev_buffer;
rpy_revdb.buf_p = rpy_rev_buffer + sizeof(int16_t);
if (rpy_rev_fileno >= 0)
- write_all(rpy_rev_buffer, size);
+ write_all(rpy_rev_buffer, full_size);
+}
+
+static ssize_t current_packet_size(void)
+{
+ return rpy_revdb.buf_p - (rpy_rev_buffer + sizeof(int16_t));
}
RPY_EXTERN
void rpy_reverse_db_flush(void)
{
- ssize_t packet_size = rpy_revdb.buf_p - (rpy_rev_buffer + sizeof(int16_t));
- if (packet_size != 0) {
- assert(0 < packet_size && packet_size <= 32767);
- *(int16_t *)rpy_rev_buffer = packet_size;
+ ssize_t content_size = current_packet_size();
+ if (content_size != 0) {
+ assert(0 < content_size && content_size <= 32767);
+ *(int16_t *)rpy_rev_buffer = content_size;
flush_buffer();
}
}
+/*
+RPY_EXTERN
+void rpy_reverse_db_register_finalizer(void *obj, void (*finalizer)(void *))
+{
+ ...;
+}
+*/
+
+void boehm_gc_finalizer_notifier(void)
+{
+ /* This is called by Boehm when there are pending finalizers.
+ They are only invoked when we call GC_invoke_finalizers(),
+ which we only do at stop points in the case of revdb.
+ */
+ assert(rpy_revdb.stop_point_break <= rpy_revdb.stop_point_seen + 1);
+ rpy_revdb.stop_point_break = rpy_revdb.stop_point_seen + 1;
+}
+
+static void record_stop_point(void)
+{
+ /* Invoke the finalizers now. This will call boehm_fq_callback(),
+ which will enqueue the objects in the correct FinalizerQueue.
+ Then, call boehm_fq_trigger(), which calls finalizer_trigger().
+ */
+ int i;
+ rpy_reverse_db_flush();
+
+ GC_invoke_finalizers();
+ i = 0;
+ while (boehm_fq_trigger[i])
+ boehm_fq_trigger[i++]();
+
+ /* This should all 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");
+ exit(1);
+ }
+ *(int16_t *)rpy_rev_buffer = 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();
+}
+
+RPY_EXTERN
+void rpy_reverse_db_next_dead(void *result)
+{
+ int64_t uid = result ? ((struct pypy_header0 *)result)->h_uid : -1;
+ RPY_REVDB_EMIT(/* nothing */, int64_t _e, uid);
+}
+
RPY_EXTERN
Signed rpy_reverse_db_identityhash(struct pypy_header0 *obj)
{
@@ -828,8 +898,7 @@
rpy_revdb.watch_enabled = any_watch_point;
}
-RPY_EXTERN
-void rpy_reverse_db_stop_point(void)
+static void replay_stop_point(void)
{
while (rpy_revdb.stop_point_break == rpy_revdb.stop_point_seen) {
save_state();
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
@@ -112,6 +112,9 @@
#define OP_REVDB_WEAKREF_DEREF(weakref, r) \
r = rpy_reverse_db_weakref_deref(weakref)
+#define OP_REVDB_CALL_DESTRUCTOR(obj, r) \
+ rpy_reverse_db_call_destructor(obj)
+
RPY_EXTERN void rpy_reverse_db_flush(void);
RPY_EXTERN char *rpy_reverse_db_fetch(const char *file, int line);
RPY_EXTERN void rpy_reverse_db_stop_point(void);
@@ -125,5 +128,7 @@
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 void rpy_reverse_db_next_dead(void *result);
/* ------------------------------------------------------------ */
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
@@ -44,6 +44,21 @@
assert self.cur <= self.current_packet_end
return result
+ def is_special_packet(self):
+ if self.current_packet_end != self.cur:
+ assert self.current_packet_end > self.cur
+ return False
+ next_header = struct.unpack_from('h', self.buffer, self.cur)[0]
+ return (next_header & 0xFF00) == 0xFF00
+
+ def special_packet(self, expected, fmt):
+ assert self.current_packet_end == self.cur
+ next_id = self.read1('h')
+ assert next_id == expected
+ p = self.cur
+ self.cur = self.current_packet_end = p + struct.calcsize(fmt)
+ return struct.unpack_from(fmt, self.buffer, p)
+
def read_check_argv(self, expected):
assert self.argc == len(expected)
for i in range(self.argc):
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
@@ -2,6 +2,7 @@
from rpython.rlib import revdb, rgc
from rpython.rlib.debug import debug_print
from rpython.rlib.objectmodel import keepalive_until_here
+from rpython.rlib.rarithmetic import intmask
from rpython.translator.revdb.message import *
from rpython.translator.revdb.test.test_basic import BaseRecordingTests
from rpython.translator.revdb.test.test_basic import InteractiveTests
@@ -22,6 +23,8 @@
WEAKREF_AFTERWARDS_DEAD = chr(0xf2)
WEAKREF_AFTERWARDS_ALIVE = chr(0xeb)
+ASYNC_FINALIZER_TRIGGER = 0xff46 - 2**16
+
class TestRecording(BaseRecordingTests):
@@ -80,6 +83,8 @@
lst = [X() for i in range(3000)]
for i in range(3000):
lst[i] = None
+ if i % 300 == 150:
+ rgc.collect()
revdb.stop_point()
return 9
self.compile(main, [], backendopt=False)
@@ -88,6 +93,124 @@
x = rdb.next('q'); assert x == 3000 # number of stop points
assert rdb.done()
+ def test_finalizer_queue(self):
+ 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:
+ pass
+ class MyFinalizerQueue(rgc.FinalizerQueue):
+ Class = X
+ def finalizer_trigger(self):
+ glob.ping = True
+ fq = MyFinalizerQueue()
+ #
+ def main(argv):
+ glob.ping = False
+ lst1 = [X() for i in range(256)]
+ lst = [X() for i in range(3000)]
+ for i, x in enumerate(lst):
+ x.baz = i
+ fq.register_finalizer(x)
+ for i in range(3000):
+ lst[i] = None
+ if i % 300 == 150:
+ rgc.collect()
+ revdb.stop_point()
+ j = i + glob.ping * 1000000
+ assert foobar(j) == j
+ if glob.ping:
+ glob.ping = False
+ total = 0
+ while True:
+ x = fq.next_dead()
+ if x is None:
+ break
+ total = intmask(total * 3 + x.baz)
+ assert foobar(total) == total
+ keepalive_until_here(lst1)
+ return 9
+ self.compile(main, [], backendopt=False)
+ out = self.run('Xx')
+ rdb = self.fetch_rdb([self.exename, 'Xx'])
+ uid_seen = set()
+ totals = []
+ 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
+ j = rdb.next()
+ assert j == i + 1000000 * triggered
+ if triggered:
+ lst = []
+ while True:
+ uid = intmask(rdb.next())
+ if uid == -1:
+ break
+ assert uid > 0 and uid not in uid_seen
+ uid_seen.add(uid)
+ lst.append(uid)
+ totals.append((lst, intmask(rdb.next())))
+ x = rdb.next('q'); assert x == 3000 # number of stop points
+ #
+ assert 1500 <= len(uid_seen) <= 3000
+ d = dict(zip(sorted(uid_seen), range(len(uid_seen))))
+ for lst, expected in totals:
+ total = 0
+ for uid in lst:
+ 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
+ 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)
+ # 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 TestReplaying(InteractiveTests):
expected_stop_points = 1
More information about the pypy-commit
mailing list