Author: Armin Rigo <ar...@tunes.org> Branch: reverse-debugger Changeset: r86966:9f8dd66da2a9 Date: 2016-09-08 18:01 +0200 http://bitbucket.org/pypy/pypy/changeset/9f8dd66da2a9/
Log: Remove the runtime part, move it to http://bitbucket.org/pypy/revdb/ diff --git a/rpython/translator/revdb/ancillary.py b/rpython/translator/revdb/ancillary.py deleted file mode 100644 --- a/rpython/translator/revdb/ancillary.py +++ /dev/null @@ -1,57 +0,0 @@ -import py -import os, sys - - -def build(tmpdir): - import cffi - ffibuilder = cffi.FFI() - - ffibuilder.cdef(""" - int ancil_send_fds(int, const int *, unsigned); - int ancil_recv_fds(int, int *, unsigned); - """) - - local_dir = os.path.dirname(os.path.abspath(__file__)) - src_dir = os.path.join(local_dir, 'src-revdb') - - ffibuilder.set_source("_ancillary_cffi", """ - #include <ancillary.h> - """, include_dirs=[src_dir], - sources=[os.path.join(src_dir, 'fd_send.c'), - os.path.join(src_dir, 'fd_recv.c')]) - - ffibuilder.compile(tmpdir=tmpdir, verbose=True) - -def import_(verbose=False): - import rpython - basedir = py.path.local(rpython.__file__).dirpath() - tmpdir = str(basedir.ensure('_cache', 'ancillary', dir=1)) - if verbose: - print tmpdir - old_sys_path = sys.path[:] - sys.path.insert(0, tmpdir) - try: - import _ancillary_cffi - except ImportError: - build(tmpdir) - import _ancillary_cffi - sys.path[:] = old_sys_path - return _ancillary_cffi.ffi, _ancillary_cffi.lib - - -def send_fds(pipe_num, fd_list): - ffi, lib = import_() - if lib.ancil_send_fds(pipe_num, fd_list, len(fd_list)) < 0: - raise OSError(ffi.errno, "ancil_send_fds() failed") - -def recv_fds(pipe_num, fd_count): - ffi, lib = import_() - p = ffi.new("int[]", fd_count) - result = lib.ancil_recv_fds(pipe_num, p, fd_count) - if result < 0: - raise OSError(ffi.errno, "ancil_recv_fds() failed") - return [p[i] for i in xrange(result)] - - -if __name__ == '__main__': - import_(verbose=True) diff --git a/rpython/translator/revdb/interact.py b/rpython/translator/revdb/interact.py deleted file mode 100644 --- a/rpython/translator/revdb/interact.py +++ /dev/null @@ -1,492 +0,0 @@ -import sys, os, re -import subprocess, socket -import traceback, linecache -from contextlib import contextmanager -try: - import readline -except ImportError: - pass - -from rpython.translator.revdb.process import ReplayProcessGroup -from rpython.translator.revdb.process import Breakpoint - -r_cmdline = re.compile(r"([a-zA-Z0-9_]\S*|.)\s*(.*)") -r_dollar_num = re.compile(r"\$(\d+)\b") - - -class RevDebugControl(object): - - def __init__(self, revdb_log_filename, executable=None, - pygments_background=None): - with open(revdb_log_filename, 'rb') as f: - header = f.readline() - assert header.endswith('\n') - fields = header[:-1].split('\t') - if len(fields) < 2 or fields[0] != 'RevDB:': - raise ValueError("file %r is not a RevDB log" % ( - revdb_log_filename,)) - if executable is None: - executable = fields[1] - if not os.path.isfile(executable): - raise ValueError("executable %r not found" % (executable,)) - linecacheoutput = self.getlinecacheoutput(pygments_background) - self.pgroup = ReplayProcessGroup(executable, revdb_log_filename, - linecacheoutput) - self.print_extra_pending_info = None - - def interact(self): - self.last_command = 'help' - self.previous_time = None - self.previous_thread = 0 - while True: - prompt = self.print_lines_before_prompt() - try: - while True: - cmdline = self.display_prompt(prompt) - self.run_command(cmdline) - prompt = self.print_lines_before_prompt() - except KeyboardInterrupt: - rtime = self.previous_time or 1 - print - print 'KeyboardInterrupt: restoring state at time %d...' % ( - rtime,) - self.pgroup.recreate_subprocess(rtime) - print "(type 'q' or Ctrl-D to quit)" - self.last_command = '' - self.previous_thread = '?' - self.previous_time = '?' - - def print_lines_before_prompt(self): - last_time = self.pgroup.get_current_time() - if last_time != self.previous_time: - print - if self.pgroup.get_current_thread() != self.previous_thread: - self.previous_thread = self.pgroup.get_current_thread() - if self.previous_thread == 0: - print ('-------------------- in main thread #0 ' - '--------------------') - else: - print ('-------------------- in non-main thread ' - '#%d --------------------' % (self.previous_thread,)) - self.pgroup.update_watch_values() - last_time = self.pgroup.get_current_time() - if self.print_extra_pending_info: - print self.print_extra_pending_info - self.print_extra_pending_info = None - if last_time != self.previous_time: - self.pgroup.show_backtrace(complete=0) - self.previous_time = last_time - prompt = '(%d)$ ' % last_time - return prompt - - def display_prompt(self, prompt): - try: - cmdline = raw_input(prompt).strip() - except EOFError: - print - cmdline = 'quit' - if not cmdline: - cmdline = self.last_command - return cmdline - - def run_command(self, cmdline): - match = r_cmdline.match(cmdline) - if not match: - return - self.last_command = cmdline - command, argument = match.groups() - try: - runner = getattr(self, 'command_' + command) - except AttributeError: - print >> sys.stderr, "no command '%s', try 'help'" % (command,) - else: - try: - runner(argument) - except KeyboardInterrupt: - raise - except Exception as e: - traceback.print_exc() - print >> sys.stderr - print >> sys.stderr, 'Something went wrong. You are now', - print >> sys.stderr, 'in a pdb; press Ctrl-D to continue.' - import pdb; pdb.post_mortem(sys.exc_info()[2]) - print >> sys.stderr - print >> sys.stderr, 'You are back running %s.' % ( - sys.argv[0],) - - def command_help(self, argument): - """Display commands summary""" - print 'Available commands:' - lst = dir(self) - commands = [(name[len('command_'):], getattr(self, name)) - for name in lst - if name.startswith('command_')] - seen = {} - for name, func in commands: - seen.setdefault(func, []).append(name) - for _, func in commands: - if func in seen: - names = seen.pop(func) - names.sort(key=len, reverse=True) - docstring = func.__doc__ or 'undocumented' - print '\t%-16s %s' % (', '.join(names), docstring) - - def command_quit(self, argument): - """Exit the debugger""" - self.pgroup.close() - sys.exit(0) - command_q = command_quit - - def command_go(self, argument): - """Jump to time ARG""" - arg = int(argument or self.pgroup.get_current_time()) - self.pgroup.jump_in_time(arg) - - def command_info(self, argument): - """Display various info ('info help' for more)""" - display = getattr(self, 'cmd_info_' + argument, self.cmd_info_help) - return display() - - def cmd_info_help(self): - """Display info topics summary""" - print 'Available info topics:' - for name in dir(self): - if name.startswith('cmd_info_'): - command = name[len('cmd_info_'):] - docstring = getattr(self, name).__doc__ or 'undocumented' - print '\tinfo %-12s %s' % (command, docstring) - - def cmd_info_paused(self): - """List current paused subprocesses""" - lst = [str(n) for n in sorted(self.pgroup.paused)] - print ', '.join(lst) - - def _bp_kind(self, num): - break_at = self.pgroup.all_breakpoints.num2break.get(num, '??') - if break_at[0] == 'B': - kind = 'breakpoint' - name = break_at[4:] - elif break_at[0] == 'W': - kind = 'watchpoint' - name = self.pgroup.all_breakpoints.sources.get(num, '??') - elif num == -3: - kind = 'stoppoint' - name = 'explicit stop' - elif num == -4: - kind = 'switchpoint' - name = 'thread switch' - else: - kind = '?????point' - name = repr(break_at) - return kind, name - - def _bp_new(self, source_expr, break_code, break_at, nids=None): - b = self.pgroup.edit_breakpoints() - new = 1 - while new in b.num2break: - new += 1 - b.set_num2break(new, break_code, break_at) - b.sources[new] = source_expr - if break_code == 'W': - b.watchvalues[new] = '' - if nids: - b.watchuids[new] = self.pgroup.nids_to_uids(nids) - return new - - def cmd_info_breakpoints(self): - """List current breakpoints and watchpoints""" - lst = self.pgroup.all_breakpoints.num2break.keys() - if lst: - for num in sorted(lst): - kind, name = self._bp_kind(num) - print '\t%s %d: %s' % (kind, num, name) - else: - print 'no breakpoints/watchpoints.' - cmd_info_watchpoints = cmd_info_breakpoints - - def move_forward(self, steps): - self.remove_tainting() - try: - self.pgroup.go_forward(steps) - return None - except Breakpoint as b: - self.hit_breakpoints(b) - return b - - def move_backward(self, steps): - try: - self.pgroup.go_backward(steps) - return None - except Breakpoint as b: - self.hit_breakpoints(b, backward=True) - return b - - def hit_breakpoints(self, b, backward=False): - printing = [] - for num in b.regular_breakpoint_nums(): - kind, name = self._bp_kind(num) - printing.append('%s %s%s: %s' % ( - 'Reverse-hit' if backward else 'Hit', - kind, - '' if kind == 'stoppoint' else ' %d' % (num,), - name)) - self.print_extra_pending_info = '\n'.join(printing) - if self.pgroup.get_current_time() != b.time: - target_time = b.time - if backward and any(self._bp_kind(num)[0] == 'watchpoint' - for num in b.regular_breakpoint_nums()): - target_time += 1 - self.pgroup.jump_in_time(target_time) - - def remove_tainting(self): - if self.pgroup.is_tainted(): - self.pgroup.jump_in_time(self.pgroup.get_current_time()) - assert not self.pgroup.is_tainted() - - def command_step(self, argument): - """Run forward ARG steps (default 1)""" - arg = int(argument or '1') - self.move_forward(arg) - command_s = command_step - - def command_bstep(self, argument): - """Run backward ARG steps (default 1)""" - arg = int(argument or '1') - self.move_backward(arg) - command_bs = command_bstep - - @contextmanager - def _stack_id_break(self, stack_id): - # add temporarily a breakpoint that hits when we enter/leave - # a frame from/to the frame identified by 'stack_id' - b = self.pgroup.edit_breakpoints() - b.stack_id = stack_id - try: - yield - finally: - b.stack_id = 0 - - @contextmanager - def _thread_num_break(self, thread_num): - # add temporarily a breakpoint that hits when we enter/leave - # the given thread - b = self.pgroup.edit_breakpoints() - b.thread_num = thread_num - try: - yield - finally: - b.thread_num = -1 - - def command_next(self, argument): - """Run forward for one step, skipping calls""" - while True: - stack_id = self.pgroup.get_stack_id(is_parent=False) - with self._stack_id_break(stack_id): - b = self.move_forward(1) - while b is not None: - # if we hit a regular breakpoint, stop - if any(b.regular_breakpoint_nums()): - return - # we hit only calls and returns inside stack_id. If the - # last one of these is a "return", then we're now back inside - # stack_id, so stop - if b.nums[-1] == -2: - break - # else, the last one is a "call", so we entered another frame. - # Continue running until the next call/return event occurs - # inside stack_id - with self._stack_id_break(stack_id): - b = self.move_forward(self.pgroup.get_max_time() - - self.pgroup.get_current_time()) - # and then look at that 'b' again (closes the loop) - - # we might be at a "<<" position on the same line as before, - # which returns a get_hiddenpos_level() value of 1. Continue - # until we reach a get_hiddenpos_level() value of 0. - if b is None or self.pgroup.get_hiddenpos_level() == 0: - break - command_n = command_next - - def command_bnext(self, argument): - """Run backward for one step, skipping calls""" - while True: - stack_id = self.pgroup.get_stack_id(is_parent=False) - with self._stack_id_break(stack_id): - b = self.move_backward(1) - while b is not None: - # if we hit a regular breakpoint, stop - if any(b.regular_breakpoint_nums()): - return - # we hit only calls and returns inside stack_id. If the - # first one of these is a "call", then we're now back inside - # stack_id, so stop - if b.nums[0] == -1: - break - # else, the first one is a "return", so before, we were - # inside a different frame. Continue running until the next - # call/return event occurs inside stack_id - with self._stack_id_break(stack_id): - b = self.move_backward(self.pgroup.get_current_time() - 1) - # and then look at that 'b' again (closes the loop) - - # we might be at a "<<" position on the same line as before, - # which returns a get_hiddenpos_level() value of 1. Continue - # until we reach a get_hiddenpos_level() value of 0. - if self.pgroup.get_hiddenpos_level() == 0: - break - command_bn = command_bnext - - def command_finish(self, argument): - """Run forward until the current function finishes""" - stack_id = self.pgroup.get_stack_id(is_parent=True) - if stack_id == 0: - print 'No caller.' - else: - with self._stack_id_break(stack_id): - self.command_continue('') - - def command_bfinish(self, argument): - """Run backward until the current function is called""" - stack_id = self.pgroup.get_stack_id(is_parent=True) - if stack_id == 0: - print 'No caller.' - else: - with self._stack_id_break(stack_id): - self.command_bcontinue('') - - def command_continue(self, argument): - """Run forward""" - self.move_forward(self.pgroup.get_max_time() - - self.pgroup.get_current_time()) - command_c = command_continue - - def command_bcontinue(self, argument): - """Run backward""" - self.move_backward(self.pgroup.get_current_time() - 1) - command_bc = command_bcontinue - - def _cmd_thread(self, argument, cmd_continue): - argument = argument.lstrip('#') - if argument: - arg = int(argument) - if arg == self.pgroup.get_current_thread(): - print 'Thread #%d is already the current one.' % (arg,) - return - else: - # use the current thread number to detect switches to any - # other thread (this works because revdb.c issues a - # breakpoint whenever there is a switch FROM or TO the - # thread '#arg'). - arg = self.pgroup.get_current_thread() - # - with self._thread_num_break(arg): - cmd_continue('') - - def command_nthread(self, argument): - """Run forward until thread switch (optionally to #ARG)""" - self._cmd_thread(argument, self.command_continue) - - def command_bthread(self, argument): - """Run backward until thread switch (optionally to #ARG)""" - self._cmd_thread(argument, self.command_bcontinue) - - def command_print(self, argument): - """Print an expression or execute a line of code""" - # locate which $NUM appear used in the expression - nids = map(int, r_dollar_num.findall(argument)) - self.pgroup.print_cmd(argument, nids=nids) - command_p = command_print - locals()['command_!'] = command_print - - def command_backtrace(self, argument): - """Show the backtrace""" - self.pgroup.show_backtrace(complete=1) - command_bt = command_backtrace - - def command_list(self, argument): - """Show the current function""" - self.pgroup.show_backtrace(complete=2) - - def command_locals(self, argument): - """Show the locals""" - self.pgroup.show_locals() - - def command_break(self, argument): - """Add a breakpoint""" - if not argument: - print "Break where?" - return - num = self._bp_new(argument, 'B', argument) - self.pgroup.update_breakpoints() - b = self.pgroup.edit_breakpoints() - if num not in b.num2break: - print "Breakpoint not added" - else: - kind, name = self._bp_kind(num) - print "Breakpoint %d added: %s" % (num, name) - command_b = command_break - - def command_delete(self, argument): - """Delete a breakpoint/watchpoint""" - b = self.pgroup.edit_breakpoints() - try: - arg = int(argument) - except ValueError: - for arg in b.num2break: - if self._bp_kind(arg)[1] == argument: - break - else: - print "No such breakpoint/watchpoint: %s" % (argument,) - return - if arg not in b.num2break: - print "No breakpoint/watchpoint number %d" % (arg,) - else: - kind, name = self._bp_kind(arg) - b.num2break.pop(arg, '') - b.sources.pop(arg, '') - b.watchvalues.pop(arg, '') - b.watchuids.pop(arg, '') - print "%s %d deleted: %s" % (kind.capitalize(), arg, name) - command_del = command_delete - - def command_watch(self, argument): - """Add a watchpoint (use $NUM in the expression to watch)""" - if not argument: - print "Watch what?" - return - # - ok_flag, compiled_code = self.pgroup.compile_watchpoint_expr(argument) - if not ok_flag: - print compiled_code # the error message - print 'Watchpoint not added' - return - # - nids = map(int, r_dollar_num.findall(argument)) - ok_flag, text = self.pgroup.check_watchpoint_expr(compiled_code, nids) - if not ok_flag: - print text - print 'Watchpoint not added' - return - # - new = self._bp_new(argument, 'W', compiled_code, nids=nids) - self.pgroup.update_watch_values() - print "Watchpoint %d added" % (new,) - - def getlinecacheoutput(self, pygments_background): - if not pygments_background or pygments_background == 'off': - return - try: - from pygments import highlight - from pygments.lexers import PythonLexer - from pygments.formatters import TerminalFormatter - except ImportError as e: - print >> sys.stderr, 'ImportError: %s' % (e,) - return None - # - lexer = PythonLexer() - fmt = TerminalFormatter(bg=pygments_background) - # - def linecacheoutput(filename, lineno): - line = linecache.getline(filename, lineno) - return highlight(line, lexer, fmt) - return linecacheoutput diff --git a/rpython/translator/revdb/message.py b/rpython/translator/revdb/message.py deleted file mode 100644 --- a/rpython/translator/revdb/message.py +++ /dev/null @@ -1,120 +0,0 @@ - -INIT_VERSION_NUMBER = 0xd80100 - - -# See the corresponding answers for details about messages. - -CMD_FORK = -1 # Message(CMD_FORK) -CMD_QUIT = -2 # Message(CMD_QUIT) -CMD_FORWARD = -3 # Message(CMD_FORWARD, steps, breakpoint_mode) -CMD_FUTUREIDS = -4 # Message(CMD_FUTUREIDS, extra=list-of-8bytes-uids) -CMD_PING = -5 # Message(CMD_PING) -# extra commands which are not handled by revdb.c, but -# by revdb.register_debug_command() -CMD_PRINT = 1 # Message(CMD_PRINT, extra=expression) -CMD_BACKTRACE = 2 # Message(CMD_BACKTRACE) -CMD_LOCALS = 3 # Message(CMD_LOCALS) -CMD_BREAKPOINTS = 4 # Message(CMD_BREAKPOINTS, stack_id, - # extra="\0-separated names") -CMD_STACKID = 5 # Message(CMD_STACKID, parent-flag) -CMD_ATTACHID = 6 # Message(CMD_ATTACHID, small-num, unique-id) -CMD_COMPILEWATCH= 7 # Message(CMD_COMPILEWATCH, extra=expression) -CMD_CHECKWATCH = 8 # Message(CMD_CHECKWATCH, extra=compiled_code) -CMD_WATCHVALUES = 9 # Message(CMD_WATCHVALUES, extra=texts) - - -# the first message sent by the first child: -# Message(ANSWER_INIT, INIT_VERSION_NUMBER, total_stop_points) -ANSWER_INIT = -20 - -# sent when the child is done and waiting for the next command: -# Message(ANSWER_READY, current_time, currently_created_objects) -ANSWER_READY = -21 - -# sent after CMD_FORK: -# Message(ANSWER_FORKED, child_pid) -ANSWER_FORKED = -22 - -# sent when a child reaches the end (should not occur with process.py) -# Message(ANSWER_AT_END) (the child exits afterwards) -ANSWER_AT_END = -23 - -# breakpoint detected in CMD_FORWARD: -# Message(ANSWER_BREAKPOINT, break_time, break_created_objects, bpkt_num) -# if breakpoint_mode=='b': sent immediately when seeing a breakpoint, -# followed by ANSWER_STD with the same time -# if breakpoint_mode=='r': sent when we're done going forward, about -# the most recently seen breakpoint -# if breakpoint_mode=='i': ignored, never sent -ANSWER_BREAKPOINT = -24 - -# sent after an Attempted to do I/O or access raw memory, as the last message -ANSWER_ATTEMPT_IO = -25 - - -# print one line of a file to the console, for CMD_PRINT -# Message(ANSWER_LINECACHE, linenum, extra=filename) -ANSWER_LINECACHE = 19 - -# print text to the console, for CMD_PRINT and others -# Message(ANSWER_TEXT, extra=text) -ANSWER_TEXT = 20 - -# CMD_STACKID returns the id of the current or parent frame (depending -# on the 'parent-flag' passed in), or 0 if not found. The id can be just -# the stack depth, or it can be the unique id of the frame object. When -# used in CMD_BREAKPOINTS, it means "break if we are entering/leaving a -# frame from/to the given frame". -# Message(ANSWER_STACKID, stack-id) -ANSWER_STACKID = 21 - -# sent from CMD_PRINT to record the existence of a recallable object -# Message(ANSWER_NEXTNID, unique-id) -ANSWER_NEXTNID = 22 - -# sent after CMD_COMPILEWATCH: -# Message(ANSWER_WATCH, ok_flag, extra=marshalled_code) -# sent after CMD_CHECKWATCH: -# Message(ANSWER_WATCH, ok_flag, extra=result_of_expr) -ANSWER_WATCH = 23 - -# sent sometimes after CMD_BREAKPOINTS: -# Message(ANSWER_CHBKPT, bkpt_num, extra=new_breakpoint_text) -ANSWER_CHBKPT = 24 - - -# ____________________________________________________________ - - -class Message(object): - """Represent messages sent and received to subprocesses - started with --revdb-replay. - """ - - def __init__(self, cmd, arg1=0, arg2=0, arg3=0, extra=""): - self.cmd = cmd - self.arg1 = arg1 - self.arg2 = arg2 - self.arg3 = arg3 - self.extra = extra - - def __repr__(self): - cmd = self.cmd - for key, value in globals().items(): - if (key.startswith('CMD_') or key.startswith('ANSWER_')) and ( - value == cmd): - cmd = key - break - return 'Message(%s, %d, %d, %d, %r)' % (cmd, self.arg1, - self.arg2, self.arg3, - self.extra) - - def __eq__(self, other): - return (self.cmd == other.cmd and - self.arg1 == other.arg1 and - self.arg2 == other.arg2 and - self.arg3 == other.arg3 and - self.extra == other.extra) - - def __ne__(self, other): - return not (self == other) diff --git a/rpython/translator/revdb/process.py b/rpython/translator/revdb/process.py deleted file mode 100644 --- a/rpython/translator/revdb/process.py +++ /dev/null @@ -1,637 +0,0 @@ -import sys, os, struct, socket, errno, subprocess -import linecache -from rpython.translator.revdb import ancillary -from rpython.translator.revdb.message import * - - -class Breakpoint(Exception): - def __init__(self, time): - self.time = time # time of the previous stop_point - self.nums = [] # list of breakpoint numbers that occurred, in order - - def record_num(self, num): - self.nums.append(num) - - def regular_breakpoint_nums(self): - for num in self.nums: - if num != -1 and num != -2: - yield num - - def __repr__(self): - return 'Breakpoint(%d, %r)' % (self.time, self.nums) - __str__ = __repr__ - - -class AllBreakpoints(object): - - def __init__(self): - self.num2break = {} # {small number: encoded break/watchpoint} - self.sources = {} # {small number: src text} - self.watchvalues = {} # {small number: resulting text} - self.watchuids = {} # {small number: [uid...]} - self.stack_id = 0 # breaks when leaving/entering a frame from/to - # the frame identified by 'stack_id' - self.thread_num = -1 # breaks when leaving/entering the thread_num - - def __repr__(self): - return 'AllBreakpoints(%r, %r, %r, %r, %r)' % ( - self.num2break, self.watchvalues, self.watchuids, - self.stack_id, self.thread_num) - - def compare(self, other): - if (self.num2break == other.num2break and - self.stack_id == other.stack_id and - self.thread_num == other.thread_num): - if self.watchvalues == other.watchvalues: - return 2 # completely equal - else: - return 1 # equal, but watchvalues out-of-date - else: - return 0 # different - - def is_empty(self): - return (len(self.num2break) == 0 and self.stack_id == 0 - and self.thread_num == -1) - - def duplicate(self): - a = AllBreakpoints() - a.num2break.update(self.num2break) - a.stack_id = self.stack_id - a.thread_num = self.thread_num - return a - - def set_num2break(self, new, break_code, break_at): - if len(break_at) > 0xFFFFFF: - raise OverflowError("break/watchpoint too complex") - self.num2break[new] = (break_code + - chr(len(break_at) & 0xFF) + - chr((len(break_at) >> 8) & 0xFF) + - chr(len(break_at) >> 16) + - break_at) - - -class RecreateSubprocess(Exception): - pass - - -class ReplayProcess(object): - """Represent one replaying subprocess. - - It can be either the one started with --revdb-replay, or a fork. - """ - - def __init__(self, pid, control_socket, - breakpoints_cache=AllBreakpoints(), - printed_objects=frozenset(), - linecacheoutput=None): - self.pid = pid - self.control_socket = control_socket - self.tainted = False - self.breakpoints_cache = breakpoints_cache # don't mutate this - self.printed_objects = printed_objects # don't mutate this - # ^^^ frozenset containing the uids of the objects that are - # either already discovered in this child - # (if uid < currently_created_objects), or that will - # automatically be discovered when we move forward - self.linecacheoutput = linecacheoutput or linecache.getline - - def _recv_all(self, size): - pieces = [] - while size > 0: - data = self.control_socket.recv(size) - if not data: - raise EOFError - size -= len(data) - pieces.append(data) - return ''.join(pieces) - - def send(self, msg): - #print 'SENT:', self.pid, msg - binary = struct.pack("iIqqq", msg.cmd, len(msg.extra), - msg.arg1, msg.arg2, msg.arg3) - self.control_socket.sendall(binary + msg.extra) - - def recv(self): - binary = self._recv_all(struct.calcsize("iIqqq")) - cmd, size, arg1, arg2, arg3 = struct.unpack("iIqqq", binary) - extra = self._recv_all(size) - msg = Message(cmd, arg1, arg2, arg3, extra) - #print 'RECV:', self.pid, msg - return msg - - def expect(self, cmd, arg1=0, arg2=0, arg3=0, extra=""): - msg = self.recv() - assert msg.cmd == cmd, msg - if arg1 is not Ellipsis: - assert msg.arg1 == arg1, msg - if arg2 is not Ellipsis: - assert msg.arg2 == arg2, msg - if arg3 is not Ellipsis: - assert msg.arg3 == arg3, msg - if extra is not Ellipsis: - assert msg.extra == extra, msg - return msg - - def expect_ready(self): - msg = self.expect(ANSWER_READY, Ellipsis, Ellipsis, Ellipsis) - self.update_times(msg) - - def update_times(self, msg): - self.current_time = msg.arg1 - self.currently_created_objects = msg.arg2 - self.current_thread = msg.arg3 - - def clone(self, activate=False): - """Fork this subprocess. Returns a new ReplayProcess() that is - an identical copy. - """ - self.send(Message(CMD_FORK, int(activate))) - s1, s2 = socket.socketpair() - ancillary.send_fds(self.control_socket.fileno(), [s2.fileno()]) - s2.close() - msg = self.expect(ANSWER_FORKED, Ellipsis) - child_pid = msg.arg1 - self.expect_ready() - other = ReplayProcess(child_pid, s1, - breakpoints_cache=self.breakpoints_cache, - printed_objects=self.printed_objects, - linecacheoutput=self.linecacheoutput) - other.expect_ready() - #print >> sys.stderr, 'CLONED', self.current_time - return other - - def close(self): - """Close this subprocess.""" - try: - self.send(Message(CMD_QUIT)) - except socket.error: - pass - - def forward(self, steps, breakpoint_mode): - """Move this subprocess forward in time. - Returns the Breakpoint or None. - """ - assert not self.tainted - - # <DEBUGGING ONLY> - currents = self.current_time, self.currently_created_objects - self.send(Message(CMD_PING)) - self.expect_ready() - assert currents == (self.current_time, self.currently_created_objects) - # </DEBUGGING ONLY> - - self.send(Message(CMD_FORWARD, steps, ord(breakpoint_mode))) - # - # record all breakpoints that occur together during the *last* step - bkpt = None - while True: - msg = self.recv() - if msg.cmd != ANSWER_BREAKPOINT: - break - if bkpt is None or bkpt.time != msg.arg1: - bkpt = Breakpoint(msg.arg1) - bkpt.record_num(msg.arg3) - assert msg.cmd == ANSWER_READY, msg - self.update_times(msg) - return bkpt - - def print_text_answer(self, pgroup=None): - while True: - msg = self.recv() - if msg.cmd == ANSWER_TEXT: - sys.stdout.write(msg.extra) - sys.stdout.flush() - elif msg.cmd == ANSWER_READY: - self.update_times(msg) - break - elif msg.cmd == ANSWER_LINECACHE: - line = self.linecacheoutput(msg.extra, msg.arg1) - if line == '': - line = '?' - if msg.arg2: # strip? - line = line.strip() - else: - line = line.rstrip('\r\n') - sys.stdout.write(line + '\n') - sys.stdout.flush() - elif msg.cmd == ANSWER_NEXTNID and pgroup is not None: - uid = msg.arg1 - if uid < pgroup.initial_uid: - continue # created before the first stop point, ignore - self.printed_objects = self.printed_objects.union([uid]) - new_nid = len(pgroup.all_printed_objects_lst) - nid = pgroup.all_printed_objects.setdefault(uid, new_nid) - if nid == new_nid: - pgroup.all_printed_objects_lst.append(uid) - sys.stdout.write('$%d = ' % nid) - sys.stdout.flush() - elif msg.cmd == ANSWER_ATTEMPT_IO: - raise RecreateSubprocess - elif msg.cmd == ANSWER_CHBKPT and pgroup is not None: - # change the breakpoint definition. Needed for - # ":linenum" breakpoints which must be expanded to the - # current file only once - b = pgroup.edit_breakpoints() - assert b.num2break[msg.arg1][0] == 'B' - if msg.extra: - b.set_num2break(msg.arg1, 'B', msg.extra) - else: - del b.num2break[msg.arg1] - else: - print >> sys.stderr, "unexpected %r" % (msg,) - - -class ReplayProcessGroup(object): - """Handle a family of subprocesses. - """ - MAX_SUBPROCESSES = 31 # maximum number of subprocesses - STEP_RATIO = 0.25 # subprocess n is between subprocess n-1 - # and the end, at this fraction of interval - - def __init__(self, executable, revdb_log_filename, linecacheoutput=None): - s1, s2 = socket.socketpair() - initial_subproc = subprocess.Popen( - [executable, '--revdb-replay', revdb_log_filename, - str(s2.fileno())], preexec_fn=s1.close) - s2.close() - child = ReplayProcess(initial_subproc.pid, s1, - linecacheoutput=linecacheoutput) - msg = child.expect(ANSWER_INIT, INIT_VERSION_NUMBER, Ellipsis) - self.total_stop_points = msg.arg2 - if self.total_stop_points == 0: - raise ValueError("%r does not contain any stop point" % - (revdb_log_filename,)) - child.expect_ready() - self.initial_uid = child.currently_created_objects - - self.active = child - self.paused = {1: child.clone()} # {time: subprocess} - self.all_breakpoints = AllBreakpoints() - self.all_printed_objects = {} - self.all_printed_objects_lst = [] - - def get_current_time(self): - return self.active.current_time - - def get_currently_created_objects(self): - return self.active.currently_created_objects - - def get_current_thread(self): - return self.active.current_thread - - def _check_current_time(self, time): - assert self.get_current_time() == time - self.active.send(Message(CMD_FORWARD, 0)) - return self.active.expect(ANSWER_READY, time, Ellipsis, Ellipsis) - - def get_max_time(self): - return self.total_stop_points - - def get_next_clone_time(self): - # if 'active' has more printed_objects than the next process - # already in 'paused', then we re-clone 'active'. - cur_time = self.get_current_time() - future = [time for time in self.paused if time > cur_time] - if future: - for futime in sorted(future): - if (self.paused[futime].printed_objects != - frozenset(self.all_printed_objects_lst)): - # 'futime' is the time of the first "future" childs - # with an incomplete 'printed_objects'. This will - # be re-cloned. - return futime - # - if len(self.paused) >= self.MAX_SUBPROCESSES: - next_time = self.total_stop_points + 1 - else: - latest_done = max(self.paused) - range_not_done = self.total_stop_points - latest_done - next_time = latest_done + int(self.STEP_RATIO * range_not_done) + 1 - return next_time - - def is_tainted(self): - return self.active.tainted - - def go_forward(self, steps, breakpoint_mode='b'): - """Go forward, for the given number of 'steps' of time. - - If needed, it will leave clones at intermediate times. - Does not close the active subprocess. Note that - is_tainted() must return false in order to use this. - - breakpoint_mode: - 'b' = regular mode where hitting a breakpoint stops - 'i' = ignore breakpoints - 'r' = record the occurrence of a breakpoint but continue - """ - assert steps >= 0 - if breakpoint_mode != 'i': - self.update_breakpoints() - latest_bkpt = None - while True: - cur_time = self.get_current_time() - if cur_time + steps > self.total_stop_points: - steps = self.total_stop_points - cur_time - next_clone = self.get_next_clone_time() - rel_next_clone = next_clone - cur_time - if rel_next_clone > steps: - break - assert rel_next_clone >= 0 - if rel_next_clone > 0: - bkpt = self.active.forward(rel_next_clone, breakpoint_mode) - if breakpoint_mode == 'r': - latest_bkpt = bkpt or latest_bkpt - elif bkpt: - raise bkpt - steps -= rel_next_clone - if self.active.current_time in self.paused: - self.paused[self.active.current_time].close() - clone = self.active.clone() - self.paused[clone.current_time] = clone - bkpt = self.active.forward(steps, breakpoint_mode) - if breakpoint_mode == 'r': - bkpt = bkpt or latest_bkpt - if bkpt: - raise bkpt - - def go_backward(self, steps, ignore_breakpoints=False): - """Go backward, for the given number of 'steps' of time. - - Closes the active process. Implemented as jump_in_time() - and then forward-searching for breakpoint locations (if any). - """ - assert steps >= 0 - initial_time = self.get_current_time() - if self.all_breakpoints.is_empty() or ignore_breakpoints: - self.jump_in_time(initial_time - steps) - else: - if self.all_breakpoints.watchvalues: - first_steps = 97 # use smaller steps, because that's costly - else: - first_steps = 957 - self._backward_search_forward( - search_start_time = initial_time - first_steps, - search_stop_time = initial_time, - search_go_on_until_time = initial_time - steps) - - def _backward_search_forward(self, search_start_time, search_stop_time, - search_go_on_until_time=1): - while True: - self.jump_in_time(max(search_start_time, search_go_on_until_time)) - search_start_time = self.get_current_time() - time_range_to_search = search_stop_time - search_start_time - if time_range_to_search <= 0: - print "[search end]" - return - print "[searching %d..%d]" % (search_start_time, - search_stop_time) - self.go_forward(time_range_to_search, breakpoint_mode='r') - # If at least one breakpoint was found, the Breakpoint - # exception is raised with the *last* such breakpoint. - # Otherwise, we continue here. Search farther along a - # 3-times-bigger range. - search_stop_time = search_start_time - search_start_time -= time_range_to_search * 3 - - def _update_watchpoints_uids(self): - if self.all_breakpoints.watchuids: - uids = set() - uids.update(*self.all_breakpoints.watchuids.values()) - #print self.all_breakpoints - #print '\t===>', uids - self.attach_printed_objects(uids, watch_env=True) - - def update_breakpoints(self): - self._update_watchpoints_uids() - cmp = self.all_breakpoints.compare(self.active.breakpoints_cache) - #print 'compare:', cmp, self.all_breakpoints.watchvalues - if cmp == 2: - return # up-to-date - - # update the breakpoints/watchpoints - self.active.breakpoints_cache = None - num2break = self.all_breakpoints.num2break - N = (max(num2break) + 1) if num2break else 0 - if cmp == 0: - flat = [num2break.get(n, '\x00') for n in range(N)] - arg1 = self.all_breakpoints.stack_id - arg2 = self.all_breakpoints.thread_num - extra = ''.join(flat) - self.active.send(Message(CMD_BREAKPOINTS, arg1, arg2, extra=extra)) - self.active.print_text_answer(pgroup=self) - else: - assert cmp == 1 - - # update the watchpoint values - if any(name.startswith('W') for name in num2break.values()): - watchvalues = self.all_breakpoints.watchvalues - flat = [] - for n in range(N): - text = '' - name = num2break.get(n, '') - if name.startswith('W'): - text = watchvalues[n] - flat.append(text) - extra = '\x00'.join(flat) - self.active.send(Message(CMD_WATCHVALUES, extra=extra)) - self.active.expect_ready() - - self.active.breakpoints_cache = self.all_breakpoints.duplicate() - - def update_watch_values(self): - try: - self._update_watchpoints_uids() - except socket.error as e: - print >> sys.stderr, "socket.error: %s" % (e,) - print >> sys.stderr, "restarting at position 1" - self.jump_in_time(1) - self._update_watchpoints_uids() - seen = set() - for num, name in self.all_breakpoints.num2break.items(): - if name.startswith('W'): - _, text = self.check_watchpoint_expr(name[4:]) - if text != self.all_breakpoints.watchvalues[num]: - #print self.active.pid - print 'updating watchpoint value: %s => %s' % ( - self.all_breakpoints.sources[num], text) - self.all_breakpoints.watchvalues[num] = text - seen.add(num) - assert set(self.all_breakpoints.watchvalues) == seen - - def compile_watchpoint_expr(self, expr): - self.active.send(Message(CMD_COMPILEWATCH, extra=expr)) - msg = self.active.expect(ANSWER_WATCH, Ellipsis, extra=Ellipsis) - self.active.expect_ready() - return msg.arg1, msg.extra - - def check_watchpoint_expr(self, compiled_code, nids=None): - if nids: - self.ensure_nids_to_uids(nids) - uids = self.nids_to_uids(nids) - self.attach_printed_objects(uids, watch_env=True) - self.active.send(Message(CMD_CHECKWATCH, extra=compiled_code)) - msg = self.active.expect(ANSWER_WATCH, Ellipsis, extra=Ellipsis) - self.active.expect_ready() - return msg.arg1, msg.extra - - def _resume(self, from_time): - clone_me = self.paused[from_time] - if self.active is not None: - self.active.close() - self.active = clone_me.clone(activate=True) - - def jump_in_time(self, target_time): - """Jump in time at the given 'target_time'. - - This function always closes the active subprocess. - """ - if target_time < 1: - target_time = 1 - if target_time > self.total_stop_points: - target_time = self.total_stop_points - uids = set() - uids.update(*self.all_breakpoints.watchuids.values()) - self.ensure_printed_objects(uids, forced_time = target_time) - - def close(self): - """Close all subprocesses. - """ - for subp in [self.active] + self.paused.values(): - subp.close() - - def ensure_printed_objects(self, uids, forced_time=None): - """Ensure that all the given unique_ids are loaded in the active - child, if necessary by forking another child from earlier. - """ - if forced_time is None: - initial_time = self.get_current_time() - child = self.active - else: - initial_time = forced_time - stop_time = max(time for time in self.paused - if time <= initial_time) - child = self.paused[stop_time] - - while True: - uid_limit = child.currently_created_objects - missing_uids = [uid for uid in uids - if uid < uid_limit - and uid not in child.printed_objects] - if not missing_uids: - break - # pick the earlier fork - start_time = child.current_time - stop_time = max(time for time in self.paused if time < start_time) - child = self.paused[stop_time] - - # No missing_uids left: all uids are either already in - # self.active.printed_objects, or in the future. - future_uids = [uid for uid in uids if uid >= uid_limit] - if child is self.active: - assert not future_uids - else: - self._resume(stop_time) - if future_uids: - future_uids.sort() - pack_uids = [struct.pack('q', uid) for uid in future_uids] - pack_uids = ''.join(pack_uids) - #print '%d: from %d: CMD_FUTUREIDS %r' % ( - # self.active.pid, - # self.active.current_time, - # future_uids) - self.active.send(Message(CMD_FUTUREIDS, extra=pack_uids)) - self.active.expect_ready() - self.active.printed_objects = ( - self.active.printed_objects.union(future_uids)) - self.go_forward(initial_time - self.get_current_time(), - breakpoint_mode='i') - assert self.active.printed_objects.issuperset(uids) - - def nids_to_uids(self, nids, skip_futures=False): - uids = [] - for nid in set(nids): - try: - uid = self.all_printed_objects_lst[nid] - except IndexError: - continue - if skip_futures and uid >= self.get_currently_created_objects(): - #print >> sys.stderr, ( - # "note: '$%d' refers to an object that is " - # "only created later in time" % nid) - continue - uids.append(uid) - return uids - - def ensure_nids_to_uids(self, nids): - # Take the objects listed in nids which are alive at the - # current time, and return a list of uids of them. This - # might require some replaying. - uids = [] - if nids: - uids = self.nids_to_uids(nids, skip_futures=True) - self.ensure_printed_objects(uids) - return uids - - def attach_printed_objects(self, uids, watch_env): - for uid in uids: - nid = self.all_printed_objects[uid] - #print '%d: %s => %s (watch_env=%d)' % (self.active.pid, nid, uid, - # watch_env) - self.active.send(Message(CMD_ATTACHID, nid, uid, int(watch_env))) - self.active.expect_ready() - - def recreate_subprocess(self, target_time=None): - # recreate a subprocess at the given time, or by default the - # current time - if target_time is None: - target_time = self.get_current_time() - self.active = None - self.jump_in_time(target_time) - - def print_cmd(self, expression, nids=[]): - """Print an expression. - """ - uids = self.ensure_nids_to_uids(nids) - self.active.tainted = True - self.attach_printed_objects(uids, watch_env=False) - self.active.send(Message(CMD_PRINT, extra=expression)) - try: - self.active.print_text_answer(pgroup=self) - except RecreateSubprocess: - self.recreate_subprocess() - - def show_backtrace(self, complete=1): - """Show the backtrace. - """ - if complete: - self.active.tainted = True - self.active.send(Message(CMD_BACKTRACE, complete)) - try: - self.active.print_text_answer() - except RecreateSubprocess: - self.recreate_subprocess() - - def show_locals(self): - """Show the locals. - """ - self.active.tainted = True - self.active.send(Message(CMD_LOCALS)) - try: - self.active.print_text_answer() - except RecreateSubprocess: - self.recreate_subprocess() - - def edit_breakpoints(self): - return self.all_breakpoints - - def _stack_id(self, is_parent=0): - self.active.send(Message(CMD_STACKID, is_parent)) - msg = self.active.expect(ANSWER_STACKID, Ellipsis, Ellipsis) - self.active.expect_ready() - return msg - - def get_stack_id(self, is_parent): - return self._stack_id(is_parent).arg1 - - def get_hiddenpos_level(self): - return self._stack_id().arg2 diff --git a/rpython/translator/revdb/revdb.py b/rpython/translator/revdb/revdb.py deleted file mode 100755 --- a/rpython/translator/revdb/revdb.py +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env python2 - -import sys, os - - -if __name__ == '__main__': - import argparse - parser = argparse.ArgumentParser(description='Reverse debugger') - parser.add_argument('log', metavar='LOG', help='log file name') - parser.add_argument('-x', '--executable', dest='executable', - help='name of the executable file ' - 'that recorded the log') - parser.add_argument('-c', '--color', dest='color', - help='colorize source code (dark,light,off)') - options = parser.parse_args() - - sys.path.insert(0, os.path.abspath( - os.path.join(__file__, '..', '..', '..', '..'))) - - from rpython.translator.revdb.interact import RevDebugControl - ctrl = RevDebugControl(options.log, executable=options.executable, - pygments_background=options.color) - ctrl.interact() diff --git a/rpython/translator/revdb/src-revdb/fd_send.c b/rpython/translator/revdb/src-revdb/fd_send.c deleted file mode 100644 --- a/rpython/translator/revdb/src-revdb/fd_send.c +++ /dev/null @@ -1,92 +0,0 @@ -/*************************************************************************** - * libancillary - black magic on Unix domain sockets - * (C) Nicolas George - * fd_send.c - sending file descriptors - ***************************************************************************/ - -/* - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef _XPG4_2 /* Solaris sucks */ -# define _XPG4_2 -#endif - -#include <stdlib.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/uio.h> -#include <assert.h> -#if defined(__FreeBSD__) -# include <sys/param.h> /* FreeBSD sucks */ -#endif - -#include "ancillary.h" - -int -ancil_send_fds_with_buffer(int sock, const int *fds, unsigned n_fds, void *buffer) -{ - struct msghdr msghdr; - char nothing = '!'; - struct iovec nothing_ptr; - struct cmsghdr *cmsg; - int i; - - nothing_ptr.iov_base = ¬hing; - nothing_ptr.iov_len = 1; - msghdr.msg_name = NULL; - msghdr.msg_namelen = 0; - msghdr.msg_iov = ¬hing_ptr; - msghdr.msg_iovlen = 1; - msghdr.msg_flags = 0; - msghdr.msg_control = buffer; - msghdr.msg_controllen = sizeof(struct cmsghdr) + sizeof(int) * n_fds; - cmsg = CMSG_FIRSTHDR(&msghdr); - cmsg->cmsg_len = msghdr.msg_controllen; - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - for(i = 0; i < n_fds; i++) - ((int *)CMSG_DATA(cmsg))[i] = fds[i]; - return(sendmsg(sock, &msghdr, 0) >= 0 ? 0 : -1); -} - -#ifndef SPARE_SEND_FDS -int -ancil_send_fds(int sock, const int *fds, unsigned n_fds) -{ - ANCIL_FD_BUFFER(ANCIL_MAX_N_FDS) buffer; - - assert(n_fds <= ANCIL_MAX_N_FDS); - return(ancil_send_fds_with_buffer(sock, fds, n_fds, &buffer)); -} -#endif /* SPARE_SEND_FDS */ - -#ifndef SPARE_SEND_FD -int -ancil_send_fd(int sock, int fd) -{ - ANCIL_FD_BUFFER(1) buffer; - - return(ancil_send_fds_with_buffer(sock, &fd, 1, &buffer)); -} -#endif /* SPARE_SEND_FD */ _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit