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