def run_eval(expression, debug_opts=None, start_opts=None, globals_=None, locals_=None, tb_fn=None): """Evaluate the expression (given as a string) under debugger control starting with the statement subsequent to the place that this appears in your program. This is a wrapper to Debugger.run_eval(), so see that. When run_eval() returns, it returns the value of the expression. Otherwise this function is similar to run(). """ dbg = Mdebugger.Trepan(opts=debug_opts) try: return dbg.run_eval(expression, start_opts=start_opts, globals_=globals_, locals_=locals_) except: dbg.core.trace_hook_suspend = True if start_opts and 'tb_fn' in start_opts: tb_fn = start_opts['tb_fn'] Mpost_mortem.uncaught_exception(dbg, tb_fn) finally: dbg.core.trace_hook_suspend = False return
def run_exec(statement, debug_opts=None, start_opts=None, globals_=None, locals_=None): """Execute the statement (given as a string) under debugger control starting with the statement subsequent to the place that this run_call appears in your program. This is a wrapper to Debugger.run_exec(), so see that. The debugger prompt appears before any code is executed; you can set breakpoints and type 'continue', or you can step through the statement using 'step' or 'next' The optional globals_ and locals_ arguments specify the environment in which the code is executed; by default the dictionary of the module __main__ is used.""" dbg = Mdebugger.Trepan(opts=debug_opts) try: return dbg.run_exec(statement, start_opts=start_opts, globals_=globals_, locals_=locals_) except: Mpost_mortem.uncaught_exception(dbg) pass return
def test_list_command(self): import inspect d = debugger.Trepan() cp = d.core.processor cp.curframe = inspect.currentframe() cp.list_filename = cp.curframe.f_code.co_filename self.cmd = Mlist.ListCommand(cp) self.cmd.msg = self.msg self.cmd.errmsg = self.errmsg d.settings['listsize'] = self.listsize d.settings['highlight'] = 'plain' # from trepan.api import debug; debug() # Simple list command. self.clear_run_check(['list'], list(range(1, self.listsize + 1))) # Check 2nd set of consecutive lines self.clear_run_check(['list'], list( range(self.listsize + 1, (2 * self.listsize) + 1))) # Try going backwards. self.clear_run_check(['list', '-'], list(range(1, 1 + self.listsize))) # And again. Since we hit the beginning it's the same as before self.clear_run_check(['list', '-'], list(range(1, 1 + self.listsize))) # BUG Simple arithmetic expression # self.clear_run_check(['list', '4+1'], range(4+1, 4+1+listsize)) # List first last self.clear_run_check(['list', '10', ',', '20'], list(range(10, 21))) # List first count self.clear_run_check(['list', '10', ',', '5'], list(range(10, 16))) # Module # BUG? without '1' below the default starts with self.listsize+1 self.clear_run_check(['os.path', '1'], list(range(1, self.listsize + 2))) # # Function # self.clear_run_checksize(['list', 'os.path.join()']) # self.clear_run_checksize(['list', 'self.setUp()']) def foo(): pass self.clear_run_checksize(['list', 'foo()']) # BUG # self.clear_run_check(['os.path:1'], range(1, self.listsize+1)) self.clear_run_check(['os.path', '10', ',5'], list(range(10, 16))) # Use a file name if 'APPVEYOR' not in os.environ: self.clear_run_check(['list', __file__ + ':3', ',4'], list(range(3, 5))) # BUGS - but possibly the windowing thing is happening? # self.clear_run_check(['list', __file__, '3'], list(range(3, 5))) # self.clear_run_check(['list', __file__, '20', '4'], list(range(20, 24))) # self.clear_run_check(['list', __file__, '3', '4'], list(range(3, 5))) return
def test_parse_break_cmd(self): import inspect d = debugger.Trepan() cp = d.core.processor cp.curframe = inspect.currentframe() self.cmd = Mbreak.BreakCommand(cp) self.cmd.msg = self.msg self.cmd.errmsg = self.errmsg fn, fi, li, cond = Mcmdbreak.parse_break_cmd(self.cmd, []) self.assertEqual((None, True, True), (fn, fi.endswith('test-break.py'), li > 1)) fn, fi, li, cond = Mcmdbreak.parse_break_cmd(self.cmd, ['11-1']) self.assertEqual((None, True, 10), (fn, fi.endswith('test-break.py'), li)) fn, fi, li, cond = Mcmdbreak.parse_break_cmd(self.cmd, [__file__ + ':10']) self.assertEqual((None, 10), (fn, li)) def foo(): return 'bar' fn, fi, li, cond = Mcmdbreak.parse_break_cmd(self.cmd, ['foo']) self.assertEqual((foo, True, True), (fn, fi.endswith('test-break.py'), li > 1)) fn, fi, li, cond = Mcmdbreak.parse_break_cmd(self.cmd, ['food']) self.assertEqual((None, None, None, None), (fn, fi, li, cond)) fn, fi, li, cond = Mcmdbreak.parse_break_cmd(self.cmd, ['os.path']) self.assertEqual((None, None), (fn, li)) fn, fi, li, cond = Mcmdbreak.parse_break_cmd(self.cmd, ['os.path', '5+1']) self.assertEqual((None, 6), (fn, li)) fn, fi, li, cond = Mcmdbreak.parse_break_cmd(self.cmd, ['os.path.join']) self.assertEqual((os.path.join, True), (fn, li > 1)) fn, fi, li, cond = Mcmdbreak.parse_break_cmd(self.cmd, ['if', 'True']) self.assertEqual((None, True, True), (fn, fi.endswith('test-break.py'), li > 1)) fn, fi, li, cond = Mcmdbreak.parse_break_cmd(self.cmd, ['foo', 'if', 'True']) self.assertEqual((foo, True, True), (fn, fi.endswith('test-break.py'), li > 1)) fn, fi, li, cond = Mcmdbreak.parse_break_cmd( self.cmd, ['os.path:10', 'if', 'True']) self.assertEqual(10, li) # FIXME: # Try a breakpoint with a symlink in the filename. # Also, add a unit test for canonic. return
def strarray_setup(debugger_cmds): ''' Common setup to create a debugger with stringio attached ''' stringin = Mstringarray.StringArrayInput(debugger_cmds) stringout = Mstringarray.StringArrayOutput() d_opts = {'input': stringin, 'output': stringout} d = Mdebugger.Trepan(d_opts) d.settings['basename'] = True d.settings['different'] = False d.settings['autoeval'] = False d.settings['highlight'] = 'plain' return d
def setUp(self): self.errors = [] self.msgs = [] d = Mdebugger.Trepan() self.cmdproc = d.core.processor self.cmdproc.curframe = inspect.currentframe() cmd = self.cmdproc.commands['alias'] cmd.msg = self.msg cmd.errmsg = self.errmsg cmd = self.cmdproc.commands['unalias'] cmd.msg = self.msg cmd.errmsg = self.errmsg return
def test_complete_identifier(self): from trepan.processor.command import base_cmd as mBaseCmd from trepan.processor import complete as mComplete self.dbgr = Mdebugger.Trepan() cmdproc = self.dbgr.core.processor cmdproc.curframe = inspect.currentframe() cmd = mBaseCmd.DebuggerCommand(cmdproc) self.assertEqual(mComplete.complete_id_and_builtins(cmd, 'ma'), ['map', 'max']) self.assertEqual(mComplete.complete_identifier(cmd, 'm'), ['mBaseCmd', 'mComplete']) return
def test_pdef(self): import inspect d = debugger.Trepan() cp = d.core.processor cp.curframe = inspect.currentframe() cmd = Mp.PrintDefCommand(cp) cmd.msg = self.msg cmd.errmsg = cp.errmsg = self.errmsg cmd.run(['pdef', 'self.test_pdef']) self.assertEqual('self.test_pdef(self)', self.msgs[-1]) cmd.run(['pdef', 'TestPDef']) self.assertEqual("TestPDef(self, methodName='runTest')", self.msgs[-1]) self.assertEqual(0, len(self.errors)) cmd.run(['pdef', 'FOO']) self.assertEqual(1, len(self.errors)) return
def test_info_file(self): d = Mdebugger.Trepan() d, cp = dbg_setup(d) command = Minfo.InfoCommand(cp, 'info') sub = MinfoFile.InfoFiles(command) self.setup_io(sub) sub.run([]) self.assertEqual([], self.msgs) cp.curframe = inspect.currentframe() for width in (80, 200): # sub.settings['width'] = width sub.run(['test-info-file.py', 'lines']) sub.run([]) pass pass
def test_pr(self): import inspect d = debugger.Trepan() cp = d.core.processor cp.curframe = inspect.currentframe() cmd = Mp.PrCommand(cp) cmd.msg = self.msg cmd.errmsg = self.errmsg me = 10 # NOQA cmd.run([cmd.name, 'me']) self.assertEqual('10', self.msgs[-1]) cmd.run([cmd.name, '/x', 'me']) self.assertEqual("'0xa'", self.msgs[-1]) cmd.run([cmd.name, '/o', 'me']) self.assertEqual("'0o12'", self.msgs[-1]) return
def run_call(func, debug_opts=None, start_opts=None, *args, **kwds): """Call the function (a function or method object, not a string) with the given arguments starting with the statement subsequent to the place that this appears in your program. When run_call() returns, it returns whatever the function call returned. The debugger prompt appears as soon as the function is entered.""" dbg = Mdebugger.Trepan(opts=debug_opts) try: return dbg.run_call(func, start_opts, *args, **kwds) except: Mpost_mortem.uncaught_exception(dbg) pass return
text = self.proc.current_source_text.rstrip('\n') if '?' == args[0][-1]: text = Meval.extract_expression(text) self.msg("eval: %s" % text) pass else: self.errmsg("Don't have find program source text") return else: text = self.proc.current_command[len(self.proc.cmd_name):] pass text = text.strip() try: self.proc.exec_line(text) except: pass if __name__ == '__main__': import inspect from trepan import debugger d = debugger.Trepan() cp = d.core.processor cp.curframe = inspect.currentframe() command = EvalCommand(cp) me = 10 # command.run([command.name, '1+2']) # command.run([command.name, 'if 5: x=1']) pass
def main(dbg=None, sys_argv=list(sys.argv)): """Routine which gets run if we were invoked directly""" global __title__ # Save the original just for use in the restart that works via exec. orig_sys_argv = list(sys_argv) opts, dbg_opts, sys_argv = Moptions.process_options(__title__, __version__, sys_argv) if opts.server is not None: if opts.server == 'tcp': connection_opts={'IO': 'TCP', 'PORT': opts.port} else: connection_opts={'IO': 'FIFO'} intf = Mserver.ServerInterface(connection_opts=connection_opts) dbg_opts['interface'] = intf if 'FIFO' == intf.server_type: print('Starting FIFO server for process %s.' % os.getpid()) elif 'TCP' == intf.server_type: print('Starting TCP server listening on port %s.' % intf.inout.PORT) pass elif opts.client: Mclient.main(opts, sys_argv) return dbg_opts['orig_sys_argv'] = orig_sys_argv if dbg is None: dbg = Mdebugger.Trepan(dbg_opts) dbg.core.add_ignore(main) pass Moptions._postprocess_options(dbg, opts) # process_options has munged sys.argv to remove any options that # options that belong to this debugger. The original options to # invoke the debugger and script are in global sys_argv if len(sys_argv) == 0: # No program given to debug. Set to go into a command loop # anyway mainpyfile = None else: mainpyfile = sys_argv[0] # Get script filename. if not os.path.isfile(mainpyfile): mainpyfile=Mclifns.whence_file(mainpyfile) is_readable = Mfile.readable(mainpyfile) if is_readable is None: print("%s: Python script file '%s' does not exist" % (__title__, mainpyfile,)) sys.exit(1) elif not is_readable: print("%s: Can't read Python script file '%s'" % (__title__, mainpyfile, )) sys.exit(1) return # If mainpyfile is an optimized Python script try to find and # use non-optimized alternative. mainpyfile_noopt = Mfile.file_pyc2py(mainpyfile) if mainpyfile != mainpyfile_noopt \ and Mfile.readable(mainpyfile_noopt): print("%s: Compiled Python script given and we can't use that." % __title__) print("%s: Substituting non-compiled name: %s" % ( __title__, mainpyfile_noopt,)) mainpyfile = mainpyfile_noopt pass # Replace trepan's dir with script's dir in front of # module search path. sys.path[0] = dbg.main_dirname = os.path.dirname(mainpyfile) # XXX If a signal has been received we continue in the loop, otherwise # the loop exits for some reason. dbg.sig_received = False # if not mainpyfile: # print('For now, you need to specify a Python script name!') # sys.exit(2) # pass while True: # Run the debugged script over and over again until we get it # right. try: if dbg.program_sys_argv and mainpyfile: normal_termination = dbg.run_script(mainpyfile) if not normal_termination: break else: dbg.core.execution_status = 'No program' dbg.core.processor.process_commands() pass dbg.core.execution_status = 'Terminated' dbg.intf[-1].msg("The program finished - quit or restart") dbg.core.processor.process_commands() except Mexcept.DebuggerQuit: break except Mexcept.DebuggerRestart: dbg.core.execution_status = 'Restart requested' if dbg.program_sys_argv: sys.argv = list(dbg.program_sys_argv) part1 = ('Restarting %s with arguments:' % dbg.core.filename(mainpyfile)) args = ' '.join(dbg.program_sys_argv[1:]) dbg.intf[-1].msg( Mmisc.wrapped_lines(part1, args, dbg.settings['width'])) else: break except SystemExit: # In most cases SystemExit does not warrant a post-mortem session. break pass # Restore old sys.argv sys.argv = orig_sys_argv return
def main(dbg=None, sys_argv=list(sys.argv)): """Routine which gets run if we were invoked directly""" global __title__ # Save the original just for use in the restart that works via exec. orig_sys_argv = list(sys_argv) opts, dbg_opts, sys_argv = process_options(__title__, __version__, sys_argv) dbg_opts["orig_sys_argv"] = sys_argv dbg_opts["interface"] = BWInterface() dbg_opts["processor"] = "bullwinkle" if dbg is None: dbg = Mdebugger.Trepan(dbg_opts) dbg.core.add_ignore(main) pass _postprocess_options(dbg, opts) # process_options has munged sys.argv to remove any options that # options that belong to this debugger. The original options to # invoke the debugger and script are in global sys_argv if len(sys_argv) == 0: # No program given to debug. Set to go into a command loop # anyway mainpyfile = None else: mainpyfile = sys_argv[0] # Get script filename. if not osp.isfile(mainpyfile): mainpyfile = Mclifns.whence_file(mainpyfile) is_readable = readable(mainpyfile) if is_readable is None: print( "%s: Python script file '%s' does not exist" % ( __title__, mainpyfile, ) ) sys.exit(1) elif not is_readable: print( "%s: Can't read Python script file '%s'" % ( __title__, mainpyfile, ) ) sys.exit(1) return # If mainpyfile is an optimized Python script try to find and # use non-optimized alternative. mainpyfile_noopt = pyficache.resolve_name_to_file(mainpyfile) if mainpyfile != mainpyfile_noopt and readable(mainpyfile_noopt): print("%s: Compiled Python script given and we can't use that." % __title__) print( "%s: Substituting non-compiled name: %s" % ( __title__, mainpyfile_noopt, ) ) mainpyfile = mainpyfile_noopt pass # Replace trepan's dir with script's dir in front of # module search path. sys.path[0] = dbg.main_dirname = osp.dirname(mainpyfile) # XXX If a signal has been received we continue in the loop, otherwise # the loop exits for some reason. dbg.sig_received = False # if not mainpyfile: # print('For now, you need to specify a Python script name!') # sys.exit(2) # pass while True: # Run the debugged script over and over again until we get it # right. try: if dbg.program_sys_argv and mainpyfile: normal_termination = dbg.run_script(mainpyfile) if not normal_termination: break else: dbg.core.execution_status = "No program" dbg.core.processor.process_commands() pass dbg.core.execution_status = "Terminated" dbg.intf[-1].msg("The program finished - quit or restart") dbg.core.processor.process_commands() except Mexcept.DebuggerQuit: break except Mexcept.DebuggerRestart: dbg.core.execution_status = "Restart requested" if dbg.program_sys_argv: sys.argv = list(dbg.program_sys_argv) part1 = "Restarting %s with arguments:" % dbg.core.filename(mainpyfile) args = " ".join(dbg.program_sys_argv[1:]) dbg.intf[-1].msg( Mmisc.wrapped_lines(part1, args, dbg.settings["width"]) ) else: break except SystemExit: # In most cases SystemExit does not warrant a post-mortem session. break pass # Restore old sys.argv sys.argv = orig_sys_argv return
else: val = self.proc.getval(name) pass Mpp.pp(val, self.settings['width'], self.msg_nocr, self.msg, prefix='%s =' % name) else: self.errmsg("%s is not a local variable" % name) pass return False pass if __name__ == '__main__': from trepan.processor.command import mock, info as Minfo from trepan import debugger as Mdebugger d = Mdebugger.Trepan() d, cp = mock.dbg_setup(d) i = Minfo.InfoCommand(cp) sub = InfoLocals(i) l = list(range(30)) # Add a simple array to the local mix printed below. import inspect cp.curframe = inspect.currentframe() sub.run([]) sub.run(['*']) sub.run(['Minfo']) pass
def debug(dbg_opts=None, start_opts=None, post_mortem=True, step_ignore=1, level=0): """ Enter the debugger. Parameters ---------- level : how many stack frames go back. Usually it will be the default 0. But sometimes though there may be calls in setup to the debugger that you may want to skip. step_ignore : how many line events to ignore after the debug() call. 0 means don't even wait for the debug() call to finish. param dbg_opts : is an optional "options" dictionary that gets fed trepan.Debugger(); `start_opts' are the optional "options" dictionary that gets fed to trepan.Debugger.core.start(). Use like this: .. code-block:: python ... # Possibly some Python code import trepan.api # Needed only once ... # Possibly some more Python code trepan.api.debug() # You can wrap inside conditional logic too pass # Stop will be here. # Below is code you want to use the debugger to do things. .... # more Python code # If you get to a place in the program where you aren't going # want to debug any more, but want to remove debugger trace overhead: trepan.api.stop() Parameter "level" specifies how many stack frames go back. Usually it will be the default 0. But sometimes though there may be calls in setup to the debugger that you may want to skip. Parameter "step_ignore" specifies how many line events to ignore after the debug() call. 0 means don't even wait for the debug() call to finish. In situations where you want an immediate stop in the "debug" call rather than the statement following it ("pass" above), add parameter step_ignore=0 to debug() like this:: import trepan.api # Needed only once # ... as before trepan.api.debug(step_ignore=0) # ... as before Module variable _debugger_obj_ from module trepan.debugger is used as the debugger instance variable; it can be subsequently used to change settings or alter behavior. It should be of type Debugger (found in module trepan). If not, it will get changed to that type:: $ python >>> from trepan.debugger import debugger_obj >>> type(debugger_obj) <type 'NoneType'> >>> import trepan.api >>> trepan.api.debug() ... (Trepan) c >>> from trepan.debugger import debugger_obj >>> debugger_obj <trepan.debugger.Debugger instance at 0x7fbcacd514d0> >>> If however you want your own separate debugger instance, you can create it from the debugger _class Debugger()_ from module trepan.debugger:: $ python >>> from trepan.debugger import Debugger >>> dbgr = Debugger() # Add options as desired >>> dbgr <trepan.debugger.Debugger instance at 0x2e25320> `dbg_opts' is an optional "options" dictionary that gets fed trepan.Debugger(); `start_opts' are the optional "options" dictionary that gets fed to trepan.Debugger.core.start(). """ if not isinstance(Mdebugger.debugger_obj, Mdebugger.Trepan): Mdebugger.debugger_obj = Mdebugger.Trepan(dbg_opts) Mdebugger.debugger_obj.core.add_ignore(debug, stop) pass core = Mdebugger.debugger_obj.core frame = sys._getframe(0 + level) core.set_next(frame) if start_opts and 'startup-profile' in start_opts and start_opts[ 'startup-profile']: dbg_initfiles = start_opts['startup-profile'] from trepan import options options.add_startup_file(dbg_initfiles) for init_cmdfile in dbg_initfiles: core.processor.queue_startfile(init_cmdfile) if not core.is_started(): core.start(start_opts) pass if post_mortem: debugger_on_post_mortem() pass if 0 == step_ignore: frame = sys._getframe(1 + level) core.stop_reason = 'at a debug() call' old_trace_hook_suspend = core.trace_hook_suspend core.trace_hook_suspend = True core.processor.event_processor(frame, 'line', None) core.trace_hook_suspend = old_trace_hook_suspend else: core.step_ignore = step_ignore - 1 pass return
for i in (15, -15, 300): print("lookup_signame(%d): %s" % (i, lookup_signame(i))) pass for i in ("term", "TERM", "NotThere"): print("lookup_signum(%s): %s" % (i, repr(lookup_signum(i)))) pass for i in ("15", "-15", "term", "sigterm", "TERM", "300", "bogus"): print("canonic_signame(%s): %s" % (i, canonic_signame(i))) pass from trepan import debugger as Mdebugger dbgr = Mdebugger.Trepan() h = SignalManager(dbgr) h.info_signal(["TRAP"]) # Set to known value h.action("SIGUSR1") h.action("usr1 print pass stop") h.info_signal(["USR1"]) # noprint implies no stop h.action("SIGUSR1 noprint") h.info_signal(["USR1"]) h.action("foo nostop") # stop keyword implies print h.action("SIGUSR1 stop") h.info_signal(["SIGUSR1"]) h.action("SIGUSR1 noprint") h.info_signal(["SIGUSR1"])
def main(dbg=None, sys_argv=list(sys.argv)): """Routine which gets run if we were invoked directly""" global __title__ # Save the original just for use in the restart that works via exec. orig_sys_argv = list(sys_argv) opts, dbg_opts, sys_argv = Moptions.process_options( __title__, VERSION, sys_argv) if opts.server is not None: if opts.server == 'tcp': connection_opts = {'IO': 'TCP', 'PORT': opts.port} else: connection_opts = {'IO': 'FIFO'} intf = Mserver.ServerInterface(connection_opts=connection_opts) dbg_opts['interface'] = intf if 'FIFO' == intf.server_type: print('Starting FIFO server for process %s.' % os.getpid()) elif 'TCP' == intf.server_type: print('Starting TCP server listening on port %s.' % intf.inout.PORT) pass elif opts.client: Mclient.main(opts, sys_argv) return dbg_opts['orig_sys_argv'] = orig_sys_argv if dbg is None: dbg = Mdebugger.Trepan(dbg_opts) dbg.core.add_ignore(main) pass Moptions._postprocess_options(dbg, opts) # process_options has munged sys.argv to remove any options that # options that belong to this debugger. The original options to # invoke the debugger and script are in global sys_argv if len(sys_argv) == 0: # No program given to debug. Set to go into a command loop # anyway mainpyfile = None else: mainpyfile = sys_argv[0] # Get script filename. if not osp.isfile(mainpyfile): mainpyfile = Mclifns.whence_file(mainpyfile) is_readable = Mfile.readable(mainpyfile) if is_readable is None: print("%s: Python script file '%s' does not exist" % ( __title__, mainpyfile, )) sys.exit(1) elif not is_readable: print("%s: Can't read Python script file '%s'" % ( __title__, mainpyfile, )) sys.exit(1) return if Mfile.is_compiled_py(mainpyfile): try: from xdis import load_module, PYTHON_VERSION, IS_PYPY (python_version, timestamp, magic_int, co, is_pypy, source_size) = load_module(mainpyfile, code_objects=None, fast_load=True) assert is_pypy == IS_PYPY assert python_version == PYTHON_VERSION, \ "bytecode is for version %s but we are version %s" % ( python_version, PYTHON_VERSION) # We should we check version magic_int py_file = co.co_filename if osp.isabs(py_file): try_file = py_file else: mainpydir = osp.dirname(mainpyfile) tag = sys.implementation.cache_tag dirnames = [ osp.join(mainpydir, tag), mainpydir ] + os.environ['PATH'].split(osp.pathsep) + ['.'] try_file = Mclifns.whence_file(py_file, dirnames) if osp.isfile(try_file): mainpyfile = try_file pass else: # Move onto the except branch raise IOError( "Python file name embedded in code %s not found" % try_file) except IOError: try: from uncompyle6 import decompile_file except ImportError: print( "%s: Compiled python file '%s', but uncompyle6 not found" % (__title__, mainpyfile), file=sys.stderr) sys.exit(1) return short_name = osp.basename(mainpyfile).strip('.pyc') fd = tempfile.NamedTemporaryFile(suffix='.py', prefix=short_name + "_", delete=False) old_write = fd.file.write def write_wrapper(*args, **kwargs): if isinstance(args[0], str): new_args = list(args) new_args[0] = args[0].encode('utf-8') old_write(*new_args, **kwargs) else: old_write(*args, **kwargs) fd.file.write = write_wrapper # from io import StringIO # linemap_io = StringIO() try: decompile_file(mainpyfile, fd.file, mapstream=fd) except: print("%s: error decompiling '%s'" % (__title__, mainpyfile), file=sys.stderr) sys.exit(1) return # # Get the line associations between the original and # # decompiled program # mapline = linemap_io.getvalue() # fd.write(mapline + "\n\n") # linemap = eval(mapline[3:]) mainpyfile = fd.name fd.close() # Since we are actually running the recreated source, # there is little no need to remap line numbers. # The mapping is given at the end of the file. # However we should consider adding this information # and original file name. print( "%s: couldn't find Python source so we recreated it at '%s'" % (__title__, mainpyfile), file=sys.stderr) pass # If mainpyfile is an optimized Python script try to find and # use non-optimized alternative. mainpyfile_noopt = pyficache.pyc2py(mainpyfile) if mainpyfile != mainpyfile_noopt \ and Mfile.readable(mainpyfile_noopt): print("%s: Compiled Python script given and we can't use that." % __title__) print("%s: Substituting non-compiled name: %s" % ( __title__, mainpyfile_noopt, )) mainpyfile = mainpyfile_noopt pass # Replace trepan's dir with script's dir in front of # module search path. sys.path[0] = dbg.main_dirname = osp.dirname(mainpyfile) # XXX If a signal has been received we continue in the loop, otherwise # the loop exits for some reason. dbg.sig_received = False # if not mainpyfile: # print('For now, you need to specify a Python script name!') # sys.exit(2) # pass while True: # Run the debugged script over and over again until we get it # right. try: if dbg.program_sys_argv and mainpyfile: normal_termination = dbg.run_script(mainpyfile) if not normal_termination: break else: dbg.core.execution_status = 'No program' dbg.core.processor.process_commands() pass dbg.core.execution_status = 'Terminated' dbg.intf[-1].msg("The program finished - quit or restart") dbg.core.processor.process_commands() except Mexcept.DebuggerQuit: break except Mexcept.DebuggerRestart: dbg.core.execution_status = 'Restart requested' if dbg.program_sys_argv: sys.argv = list(dbg.program_sys_argv) part1 = ('Restarting %s with arguments:' % dbg.core.filename(mainpyfile)) args = ' '.join(dbg.program_sys_argv[1:]) dbg.intf[-1].msg( Mmisc.wrapped_lines(part1, args, dbg.settings['width'])) else: break except SystemExit: # In most cases SystemExit does not warrant a post-mortem session. break pass # Restore old sys.argv sys.argv = orig_sys_argv return
def post_mortem(exc=None, frameno=1, dbg=None): """Enter debugger read loop after your program has crashed. exc is a triple like you get back from sys.exc_info. If no exc parameter, is supplied, the values from sys.last_type, sys.last_value, sys.last_traceback are used. And if these don't exist either we'll assume that sys.exc_info() contains what we want and frameno is the index location of where we want to start. 'frameno' specifies how many frames to ignore in the traceback. The default is 1, that is, we don't need to show the immediate call into post_mortem. If you have wrapper functions that call this one, you may want to increase frameno. """ if dbg is None: # Check for a global debugger object if Mdebugger.debugger_obj is None: Mdebugger.debugger_obj = Mdebugger.Trepan() pass dbg = Mdebugger.debugger_obj pass re_bogus_file = re.compile("^<.+>$") if exc[0] is None: # frameno+1 because we are about to add one more level of call # in get_last_or_frame_exception exc = get_last_or_frame_exception() if exc[0] is None: print("Can't find traceback for post_mortem " "in sys.last_traceback or sys.exec_info()") return pass exc_type, exc_value, exc_tb = exc dbg.core.execution_status = ('Terminated with unhandled exception %s' % exc_type) # tb has least-recent traceback entry first. We want the most-recent # entry. Also we'll pick out a mainpyfile name if it hasn't previously # been set. if exc_tb is not None: while exc_tb.tb_next is not None: filename = exc_tb.tb_frame.f_code.co_filename if (dbg.mainpyfile and 0 == len(dbg.mainpyfile) and not re_bogus_file.match(filename)): dbg.mainpyfile = filename pass exc_tb = exc_tb.tb_next pass dbg.core.processor.curframe = exc_tb.tb_frame pass if 0 == len(dbg.program_sys_argv): # Fake program (run command) args since we weren't called with any dbg.program_sys_argv = list(sys.argv[1:]) dbg.program_sys_argv[:0] = [dbg.mainpyfile] # if 0 == len(dbg._sys_argv): # # Fake script invocation (restart) args since we don't have any # dbg._sys_argv = list(dbg.program_sys_argv) # dbg._sys_argv[:0] = [__title__] try: # # FIXME: This can be called from except hook in which case we # # need this. Dunno why though. # try: # _pydb_trace.set_trace(t.tb_frame) # except: # pass # Possibly a bug in Python 2.5. Why f.f_lineno is # not always equal to t.tb_lineno, I don't know. f = exc_tb.tb_frame if f and f.f_lineno != exc_tb.tb_lineno: f = f.f_back dbg.core.processor.event_processor(f, 'exception', exc, 'Trepan3k:pm') except DebuggerRestart: while True: sys.argv = list(dbg._program_sys_argv) dbg.msg("Restarting %s with arguments:\n\t%s" % (dbg.filename( dbg.mainpyfile), " ".join(dbg._program_sys_argv[1:]))) try: dbg.run_script(dbg.mainpyfile) except DebuggerRestart: pass pass except DebuggerQuit: pass return
def test_parse_break_cmd(self): import inspect d = debugger.Trepan() cp = d.core.processor cp.curframe = inspect.currentframe() self.cmd = Mbreak.BreakCommand(cp) self.cmd.msg = self.msg self.cmd.errmsg = self.errmsg proc = self.cmd.proc fn, fi, li, cond = self.parse_break_cmd(proc, 'break') self.assertEqual((None, True, True), (fn, fi.endswith('test-break.py'), li > 1)) fn, fi, li, cond = self.parse_break_cmd(proc, 'break 11') self.assertEqual((None, True, 11), (fn, fi.endswith('test-break.py'), li)) if 'APPVEYOR' not in os.environ: if platform.system() == 'Windows': brk_cmd = 'b """%s""":8' % __file__ else: brk_cmd = 'b %s:8' % __file__ fn, fi, li, cond = self.parse_break_cmd(proc, brk_cmd) self.assertEqual((None, True, 8), (fn, isinstance(fi, str), li)) def foo(): return 'bar' fn, fi, li, cond = self.parse_break_cmd(proc, 'break foo()') self.assertEqual((foo, True, True), (fn, fi.endswith('test-break.py'), li > 1)) fn, fi, li, cond = self.parse_break_cmd(proc, 'break food()') self.assertEqual((None, None, None, None), (fn, fi, li, cond)) fn, fi, li, cond = self.parse_break_cmd(proc, 'b os.path:5') self.assertEqual((os.path, True, 5), (fn, isinstance(fi, str), li)) fn, fi, li, cond = self.parse_break_cmd(proc, 'b os.path.join()') self.assertEqual((os.path.join, True, True), (fn, isinstance(fi, str), li > 1)) fn, fi, li, cond = self.parse_break_cmd(proc, 'break if True') self.assertEqual((None, True, True), (fn, fi.endswith('test-break.py'), li > 1)) fn, fi, li, cond = self.parse_break_cmd(proc, 'b foo() if True') self.assertEqual((foo, True, True), (fn, fi.endswith('test-break.py'), li > 1)) fn, fi, li, cond = self.parse_break_cmd(proc, 'br os.path:10 if True') self.assertEqual((True, 10), (isinstance(fi, str), li)) # FIXME: # Try: # a breakpoint with a symlink in the filename. # breakpoint with a single quotes and embedded black # breakpoint with a double quotes and embedded \, # Triple quote things # # Also, add a unit test for canonic. return
def test_completion(self): self.dbgr = Mdebugger.Trepan() for line, expect_completion in [ ['set basename ', ['off', 'on']], ['where', ['where ']], # Single alias completion ['sho', ['show']], # Simple single completion ['un', ['unalias', 'undisplay']], # Simple multiple completion ['python ', []], # Don't add anything - no more ['set basename o', ['off', 'on']], ['set basename of', ['off']], # Multiple completion on two words ['set auto', ['autoeval', 'autolist', 'autopc', 'autopython']], # Completion when word is complete, without space. ['show', ['show ']], # Completion when word is complete with space. ['info ', ['args', 'break', 'builtins', 'code', 'display', 'files', 'frame', 'globals', 'line', "lines", 'locals', 'macro', "offsets", 'pc', 'program', 'return', 'signals', 'source', 'threads']], ['help sta', ['stack', 'status']], [' unalias c', ['c', 'chdir', 'cond']], # Any set style completion ['set style def', ['default']], # ['set auto eval ', '', ['off', 'on']], # Many 3-word completions # ['set auto ', ['eval', 'irb', 'list']], # Many two-word completions # ['set auto e', ['eval']], # ['disas', ['disassemble']], # Another single completion # ['help syn', ['syntax']], # ## FIXME: # ## ['help syntax co', ['command']], # ['help br', ['break', 'breakpoints']], ]: got = self.run_complete(line) self.assertEqual(expect_completion, got, "Completion of '%s', expecting %s, got %s" % (line, expect_completion, got)) pass got = self.run_complete('') self.assertTrue(len(got) > 30, 'Initial completion should return more ' 'than 30 commands') got = self.run_complete('info files ') self.assertTrue(len(got) > 0, 'info files completion should return a file') got = self.run_complete('unalias ') self.assertTrue(len(got) > 0, 'unalias should return lots of aliases') return