[pypy-commit] pypy reverse-debugger: in-progress
arigo
pypy.commits at gmail.com
Thu Aug 11 03:54:50 EDT 2016
Author: Armin Rigo <arigo at tunes.org>
Branch: reverse-debugger
Changeset: r86133:710590b97b42
Date: 2016-08-11 09:54 +0200
http://bitbucket.org/pypy/pypy/changeset/710590b97b42/
Log: in-progress
diff --git a/rpython/translator/c/src/entrypoint.c b/rpython/translator/c/src/entrypoint.c
--- a/rpython/translator/c/src/entrypoint.c
+++ b/rpython/translator/c/src/entrypoint.c
@@ -101,10 +101,10 @@
RPython_StartupCode();
+#ifndef RPY_REVERSE_DEBUGGER
exitcode = STANDALONE_ENTRY_POINT(argc, argv);
-
-#ifdef RPY_REVERSE_DEBUGGER
- rpy_reverse_db_teardown();
+#else
+ exitcode = rpy_reverse_db_main(STANDALONE_ENTRY_POINT, argc, argv);
#endif
pypy_debug_alloc_results();
diff --git a/rpython/translator/revdb/gencsupp.py b/rpython/translator/revdb/gencsupp.py
--- a/rpython/translator/revdb/gencsupp.py
+++ b/rpython/translator/revdb/gencsupp.py
@@ -84,16 +84,16 @@
# that the calls should really be done
#
# hack: we don't need the flag for at least this common function
+ if call_code == 'RPyGilAcquire();':
+ return 'RPY_REVDB_CALL_GILCTRL(%s);' % (call_code,)
if call_code == 'RPyGilRelease();':
- return 'RPY_REVDB_CALL_GILCTRL(%s);' % (call_code,)
- if call_code == 'RPyGilAcquire();':
# Could also work with a regular RPY_REVDB_CALL_VOID, but we
# use a different byte (0xFD instead of 0xFC) to detect more
# sync misses. In a single-threaded environment this 0xFD
# byte is not needed at all, but in a multi-threaded
- # environment it ensures that during replaying, we don't go
- # past the RPyGilAcquire() in case a different thread must run
- # next.
+ # environment it ensures that during replaying, just after
+ # reading the 0xFD, we switch to a different thread if needed
+ # (actually implemented with stacklets).
return 'RPY_REVDB_CALL_GIL(%s);' % (call_code,)
#
tp = funcgen.lltypename(v_result)
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
@@ -44,9 +44,10 @@
typedef struct {
Signed version;
- uint64_t reserved1, reserved2;
+ uint64_t main_thread_id;
+ uint64_t reserved2;
void *ptr1, *ptr2;
- int reversed3;
+ int reserved3;
int argc;
char **argv;
} rdb_header_t;
@@ -130,8 +131,7 @@
_RPY_REVDB_UNLOCK();
}
-RPY_EXTERN
-void rpy_reverse_db_teardown(void)
+static void reverse_db_teardown(void)
{
uint64_t stop_points;
if (!RPY_RDB_REPLAY) {
@@ -237,6 +237,7 @@
h.ptr2 = &rpy_revdb;
h.argc = argc;
h.argv = argv;
+ h.main_thread_id = (uint64_t)pthread_self();
write_all((const char *)&h, sizeof(h));
/* write the whole content of rpy_rdb_struct */
@@ -254,7 +255,7 @@
rpy_revdb.buf_limit = rpy_rev_buffer + sizeof(rpy_rev_buffer) - 32;
rpy_revdb.unique_id_seen = 1;
- rpy_active_thread = 0; /* write an ASYNC_THREAD_SWITCH first in the log */
+ rpy_active_thread = 1;
rpy_active_thread_ptr = &rpy_active_thread;
pthread_atfork(NULL, NULL, close_revdb_fileno_in_fork_child);
@@ -625,6 +626,7 @@
*/
#include "src-revdb/fd_recv.c"
+#include "src/stacklet/stacklet.c" /* for replaying threads */
#define INIT_VERSION_NUMBER 0xd80100
@@ -658,6 +660,148 @@
static uint64_t *future_ids, *future_next_id;
static void *finalizer_tree, *destructor_tree;
+static stacklet_thread_handle st_thread;
+static stacklet_handle st_outer_controller_h;
+static uint64_t current_thread_id, target_thread_id;
+static void *thread_tree_root;
+
+
+struct replay_thread_main_s {
+ Signed (*entry_point)(Signed, char **);
+ int argc;
+ char **argv;
+};
+struct replay_thread_s {
+ uint64_t tid;
+ stacklet_handle h;
+};
+
+static stacklet_handle replay_thread_main(stacklet_handle h, void *arg)
+{
+ /* main thread starts */
+ struct replay_thread_main_s *m = arg;
+ st_outer_controller_h = h;
+ m->entry_point(m->argc, m->argv);
+
+ /* main thread finished, program stops */
+ reverse_db_teardown();
+
+ /* unreachable */
+ abort();
+}
+
+static void replay_invoke_callback(unsigned char e);
+
+static stacklet_handle replay_thread_sub(stacklet_handle h, void *ignored)
+{
+ /* A non-main thread starts. What is does is invoke a "callback",
+ which is the argument passed to rthread.ll_start_new_thread().
+ We get it here because the first thing stored in the log about
+ this thread should be a callback identifier.
+ */
+ unsigned char e1;
+ st_outer_controller_h = h;
+
+ if (rpy_revdb.buf_limit >= rpy_revdb.buf_p)
+ rpy_reverse_db_fetch(__FILE__, __LINE__);
+
+ _RPY_REVDB_EMIT_REPLAY(unsigned char _e, e1)
+ replay_invoke_callback(e1);
+
+ /* the thread finishes here. Return to the outer controller. */
+ return st_outer_controller_h;
+}
+
+static int compare_replay_thread(const void *a, const void *b)
+{
+ uint64_t ta = ((const struct replay_thread_s *)a)->tid;
+ uint64_t tb = ((const struct replay_thread_s *)b)->tid;
+ if (ta < tb)
+ return -1;
+ if (ta == tb)
+ return 0;
+ else
+ return 1;
+}
+
+RPY_EXTERN
+int rpy_reverse_db_main(Signed entry_point(Signed, char**),
+ int argc, char **argv)
+{
+ if (!RPY_RDB_REPLAY) {
+ int exitcode = (int)entry_point(argc, argv);
+ reverse_db_teardown();
+ return exitcode;
+ }
+ else {
+ /* start the entry point inside a new stacklet, so that we
+ can switch it away at any point later */
+ struct replay_thread_main_s m;
+ stacklet_handle h;
+ m.entry_point = entry_point;
+ m.argc = argc;
+ m.argv = argv;
+ h = stacklet_new(st_thread, replay_thread_main, &m);
+
+ /* We reach this point only if we start a second thread. This
+ is done by revdb_switch_thread(), which switches back to
+ 'st_outer_controller_h'. This is the outer controller
+ loop.
+ */
+ attach_gdb();
+ while (1) {
+ struct replay_thread_s *node, **item, dummy;
+
+ if (h == NULL)
+ goto out_of_memory;
+
+ if (h != EMPTY_STACKLET_HANDLE) {
+ /* save 'h' as the stacklet handle for the thread
+ 'current_thread_id' */
+ node = malloc(sizeof(struct replay_thread_s));
+ if (!node)
+ goto out_of_memory;
+ node->tid = current_thread_id;
+ node->h = h;
+ item = tsearch(node, &thread_tree_root, compare_replay_thread);
+ if (item == NULL)
+ goto out_of_memory;
+
+ if (*item != node) {
+ fprintf(stderr, "thread switch: duplicate thread\n");
+ exit(1);
+ }
+ }
+ else {
+ /* current_thread_id terminated */
+ }
+
+ /* fetch out (and delete) the handle for the target thread */
+ current_thread_id = target_thread_id;
+ dummy.tid = target_thread_id;
+ item = tfind(&dummy, &thread_tree_root, compare_replay_thread);
+ if (item == NULL) {
+ /* it's a new thread, start it now */
+ h = stacklet_new(st_thread, replay_thread_sub, NULL);
+ }
+ else {
+ node = *item;
+ assert(node->tid == target_thread_id);
+ h = node->h;
+ tdelete(node, &thread_tree_root, compare_replay_thread);
+ free(node);
+
+ h = stacklet_switch(h);
+ }
+ }
+ abort(); /* unreachable */
+
+ out_of_memory:
+ fprintf(stderr, "thread switch: out of memory\n");
+ exit(1);
+ }
+}
+
RPY_EXTERN
void attach_gdb(void)
{
@@ -796,6 +940,7 @@
(long)h.version, (long)RDB_VERSION);
exit(1);
}
+ current_thread_id = h.main_thread_id;
if (h.ptr1 != &rpy_reverse_db_stop_point ||
h.ptr2 != &rpy_revdb) {
fprintf(stderr,
@@ -833,6 +978,7 @@
set_revdb_breakpoints();
empty_string = make_rpy_string(0);
+ st_thread = stacklet_newthread(); /* replaying doesn't use real threads */
write_answer(ANSWER_INIT, INIT_VERSION_NUMBER, total_stop_points, 0);
@@ -887,8 +1033,6 @@
fprintf(stderr, "bad log format: incomplete packet\n");
exit(1);
}
-
- read_next_packet:
keep = rpy_revdb.buf_readend - rpy_revdb.buf_p;
assert(keep >= 0);
@@ -923,8 +1067,13 @@
return;
case ASYNC_THREAD_SWITCH:
- fetch_async_block();
- goto read_next_packet;
+ target_thread_id = fetch_async_block();
+ _RPY_REVDB_PRINT("[THRD]", target_thread_id);
+ rpy_revdb.buf_limit = rpy_revdb.buf_p;
+ st_outer_controller_h = stacklet_switch(st_outer_controller_h);
+ if (rpy_revdb.buf_limit == rpy_revdb.buf_p)
+ rpy_reverse_db_fetch(__FILE__, __LINE__);
+ return;
default:
fprintf(stderr, "bad packet header %d\n", (int)header);
@@ -1157,7 +1306,6 @@
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;
}
@@ -1501,6 +1649,22 @@
RPY_CALLBACKLOCS /* macro from revdb_def.h */
};
+static void replay_invoke_callback(unsigned char e)
+{
+ unsigned long index;
+ unsigned char e2;
+ void (*pfn)(void);
+ _RPY_REVDB_EMIT_REPLAY(unsigned char _e, e2)
+ index = (e << 8) | e2;
+ index -= 300;
+ if (index >= (sizeof(callbacklocs) / sizeof(callbacklocs[0]))) {
+ fprintf(stderr, "bad callback index %lx\n", index);
+ exit(1);
+ }
+ pfn = callbacklocs[index];
+ pfn();
+}
+
RPY_EXTERN
void rpy_reverse_db_invoke_callback(unsigned char e)
{
@@ -1509,19 +1673,7 @@
callback identifier. */
do {
- unsigned long index;
- unsigned char e2;
- void (*pfn)(void);
- _RPY_REVDB_EMIT_REPLAY(unsigned char _e, e2)
- index = (e << 8) | e2;
- index -= 300;
- if (index >= (sizeof(callbacklocs) / sizeof(callbacklocs[0]))) {
- fprintf(stderr, "bad callback index\n");
- exit(1);
- }
- pfn = callbacklocs[index];
- pfn();
-
+ replay_invoke_callback(e);
_RPY_REVDB_EMIT_REPLAY(unsigned char _e, e)
} while (e != 0xFC);
}
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
@@ -29,7 +29,8 @@
/* ------------------------------------------------------------ */
RPY_EXTERN void rpy_reverse_db_setup(int *argc_p, char **argv_p[]);
-RPY_EXTERN void rpy_reverse_db_teardown(void);
+RPY_EXTERN int rpy_reverse_db_main(Signed entry_point(Signed, char**),
+ int argc, char **argv);
/* enable to print locations to stderr of all the EMITs */
#ifdef RPY_REVDB_PRINT_ALL
@@ -92,7 +93,7 @@
char *_end1 = _src + sizeof(_e); \
memcpy(&_e, _src, sizeof(_e)); \
rpy_revdb.buf_p = _end1; \
- _RPY_REVDB_PRINT("[read]", _e); \
+ _RPY_REVDB_PRINT("[ rd ]", _e); \
if (_end1 >= rpy_revdb.buf_limit) \
rpy_reverse_db_fetch(__FILE__, __LINE__); \
variable = _e; \
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
@@ -25,7 +25,7 @@
assert header == 'RevDB:\t' + '\t'.join(expected_argv) + '\n\x00'
#
x = self.read1('P'); assert x == 0x00FF0003
- x = self.read1('P'); assert x == 0
+ x = self.read1('P'); self.main_thread_id = x
x = self.read1('P'); assert x == 0
x = self.read1('P'); #assert x == &rpy_reverse_db_stop_point
x = self.read1('P'); #assert x == &rpy_revdb
@@ -33,7 +33,6 @@
self.argc = self.read1('i')
self.argv = self.read1('P')
self.current_packet_end = self.cur
- self.main_thread_id = self.switch_thread()
self.read_check_argv(expected_argv)
def read1(self, mode):
@@ -91,16 +90,16 @@
def write_call(self, expected_string):
x = self.next() # raw_malloc: the pointer we got
+ self.gil_release()
self.same_stack() # write
x = self.next(); assert x == len(expected_string)
self.same_stack() # errno
x = self.next('i'); assert x == 0 # errno
- self.gil_acquire()
def same_stack(self):
x = self.next('c'); assert x == '\xFC'
- def gil_acquire(self):
+ def gil_release(self):
x = self.next('c'); assert x == '\xFD'
def switch_thread(self, expected=None):
diff --git a/rpython/translator/revdb/test/test_callback.py b/rpython/translator/revdb/test/test_callback.py
--- a/rpython/translator/revdb/test/test_callback.py
+++ b/rpython/translator/revdb/test/test_callback.py
@@ -63,19 +63,19 @@
self.compile(main, backendopt=False)
out = self.run('Xx')
rdb = self.fetch_rdb([self.exename, 'Xx'])
+ rdb.gil_release()
rdb.same_stack() # callmesimple()
x = rdb.next('i'); assert x == 55555
- rdb.gil_acquire()
rdb.write_call('55555\n')
+ rdb.gil_release()
b = rdb.next('!h'); assert 300 <= b < 310 # -> callback
x = rdb.next('i'); assert x == 40 # arg n
- rdb.gil_acquire()
+ rdb.gil_release()
x = rdb.next('!h'); assert x == b # -> callback
x = rdb.next('i'); assert x == 3 # arg n
- rdb.gil_acquire()
+ rdb.gil_release()
rdb.same_stack() # <- return in main thread
x = rdb.next('i'); assert x == 4000 * 300 # return from callme()
- rdb.gil_acquire()
rdb.write_call('%s\n' % (4000 * 300,))
x = rdb.next('q'); assert x == 0 # number of stop points
assert rdb.done()
@@ -85,17 +85,17 @@
self.compile(main, backendopt=False)
out = self.run('Xx')
rdb = self.fetch_rdb([self.exename, 'Xx'])
+ rdb.gil_release()
b = rdb.next('!h'); assert 300 <= b < 310 # -> callback
x = rdb.next('i'); assert x == 40 # arg n
- rdb.gil_acquire()
rdb.write_call('40\n')
+ rdb.gil_release()
x = rdb.next('!h'); assert x == b # -> callback again
x = rdb.next('i'); assert x == 3 # arg n
- rdb.gil_acquire()
rdb.write_call('3\n')
+ rdb.gil_release()
rdb.same_stack() # -> return in main thread
x = rdb.next('i'); assert x == 120 # <- return from callme()
- rdb.gil_acquire()
rdb.write_call('120\n')
x = rdb.next('q'); assert x == 2 # number of stop points
assert rdb.done()
More information about the pypy-commit
mailing list