class ExitCommand(DebuggerCommand): """**exit** [*exitcode*] Hard exit of the debugged program. The program being debugged is exited via *sys.exit()*. If a return code is given, that is the return code passed to *sys.exit()*, the return code that will be passed back to the OS. See also: --------- See `quit` and `kill`.""" short_help = "Exit program via sys.exit()" DebuggerCommand.setup(locals(), category="support", max_args=1) def run(self, args): self.core.stop() self.core.execution_status = "Exit command" if len(args) <= 1: exit_code = 0 else: exit_code = self.proc.get_int(args[1], default=0, cmdname="exit") if exit_code is None: return False pass # FIXME: allow setting a return code. import sys sys.exit(int(exit_code)) # Not reached return True
class EnableCommand(DebuggerCommand): """**enable** *bpnumber* [*bpnumber* ...] Enables the breakpoints given as a space separated list of breakpoint numbers. To enable all breakpoints, give no argument. See also `info break` to get a list. See also: --------- `disable`""" aliases = ("en", ) short_help = "Enable some breakpoints" complete = complete_bpnumber DebuggerCommand.setup(locals(), category="breakpoints") def run(self, args): if len(args) == 1: self.msg( self.core.bpmgr.en_disable_all_breakpoints(do_enable=True)) return # if args[1] == 'display': # self.display_enable(args[2:], 0) # return for i in args[1:]: success, msg = self.core.bpmgr.en_disable_breakpoint_by_number( i, do_enable=True) if not success: self.errmsg(msg) else: self.msg("Breakpoint %s enabled." % i) pass pass return
class UnaliasCommand(DebuggerCommand): """**unalias** *alias-name* Remove alias *alias-name* See also: --------- 'alias'""" min_args = 1 short_help = "Remove an alias" DebuggerCommand.setup(locals(), category="support", max_args=1) def complete(self, prefix): return complete_token(self.proc.aliases.keys(), prefix) # Run command. def run(self, args): for arg in args[1:]: if arg in self.proc.aliases: del self.proc.aliases[arg] self.msg("Alias for %s removed." % arg) else: self.msg("No alias found for %s" % arg) pass pass return pass
class CDCommand(DebuggerCommand): """**cd** *directory* Set working directory to *directory* for debugger and program being debugged. """ aliases = ("chdir", ) short_help = ("Set working directory to DIR for debugger " "and program being debugged") DebuggerCommand.setup( locals(), category="files", max_args=1, min_args=1, ) def run(self, args): try: os.chdir(args[1]) self.msg("Working directory %s." % os.getcwd()) except OSError: self.errmsg("cd: %s." % sys.exc_info()[1]) pass return pass
class PrettyPrintCommand(DebuggerCommand): """**pp** *expression* Pretty-print the value of the expression. Simple arrays are shown columnized horizontally. Other values are printed via *pprint.pformat()*. See also: --------- `pr` and `examine` for commands which do more in the way of formatting. """ short_help = "Pretty print value of expression EXP" complete = complete_identifier DebuggerCommand.setup(locals(), category="data", min_args=1) def run(self, args): arg = " ".join(args[1:]) val = self.proc.eval(arg) pp(val, self.settings["width"], self.msg_nocr, self.msg) return False pass
class UpCommand(DebuggerCommand): signum = -1 # This is what distinguishes us from "down" DebuggerCommand.setup(locals(), category="stack", need_stack=True, max_args=1) short_help = ("Move frame in the direction of the caller of " "the last-selected frame") def complete(self, prefix): proc_obj = self.proc return frame_complete(proc_obj, prefix, self.signum) def run(self, args): """**up** [*count*] Move the current frame up in the stack trace (to an older frame). 0 is the most recent frame. If no count is given, move up 1. See also: --------- `down` and `frame`.""" adjust_relative(self.proc, self.name, args, self.signum) return False
class RunCommand(DebuggerCommand): """**run** Soft restart debugger and program via a *DebuggerRestart* exception. See also: --------- `restart` for another way to restart the debugged program. See `quit`, `exit` or `kill` for termination commands.""" aliases = ("R", ) short_help = "(Soft) restart program via a DebuggerRestart exception" DebuggerCommand.setup(locals(), category="support", max_args=0) def run(self, args): confirmed = self.confirm("Soft restart", False) if confirmed: self.core.step_ignore = 0 self.core.step_events = None raise DebuggerRestart(self.core.debugger.restart_argv()) pass pass
class AliasCommand(DebuggerCommand): """**alias** *alias-name* *debugger-command* Add alias *alias-name* for a debugger command *debugger-command*. You might do this if you want shorter command names or more commands that have more familiar meanings. Another related use is as a command abbreviation for a command that would otherwise be ambiguous. For example, by default we make `s` be an alias of `step` to force it to be used. Without the alias, `s` might be `step`, `show`, or `set` among others. Examples: -------- alias cat list # "cat prog.py" is the same as "list prog.py" alias s step # "s" is now an alias for "step". # The above example is done by default. See also: --------- `unalias` and `show alias`. """ name = "alias" short_help = "Add an alias for a debugger command" DebuggerCommand.setup(locals(), category="support", max_args=2, need_stack=True) # Run command. def run(self, args): if len(args) == 1: self.proc.commands["show"].run(["show", "alias"]) elif len(args) == 2: self.proc.commands["show"].run(["show", "alias", args[1]]) else: junk, al, command = args if command in self.proc.commands: if al in self.proc.aliases: old_command = self.proc.aliases[al] self.msg(("Alias '%s#' for command '%s'replaced old " + "alias for '%s'.") % (al, command, old_command)) else: self.msg("New alias '%s' for command '%s' created." % (al, command)) pass self.proc.aliases[al] = command else: self.errmsg(("You must alias to a command name, and '%s' " + "and is not one.") % command) pass return pass pass
class EvalCommand(DebuggerCommand): """**eval** *python-statement* Run *python-statement* in the context of the current frame. If no string is given, we run the string from the current source code about to be run. If the command ends `?` (via an alias) and no string is given, the following translations occur: assert = <expr> => <expr> {if|elif} <expr> : => <expr> while <expr> : => <expr> return <expr> => <expr> for <var> in <expr> : => <expr> and <expr> => <expr> or <expr> => <expr> <var> = <expr> => <expr> The above is done via regular expression matching. No fancy parsing is done, say, to look to see if *expr* is split across a line or whether var an assignment might have multiple variables on the left-hand side. Examples: --------- eval 1+2 # 3 eval # Run current source-code line eval? # but strips off leading 'if', 'while', .. # from command See also: --------- `deval`, `set autoeval`, `pr`, `pp` and `examine`.""" aliases = ("eval?", "?") short_help = "Print value of expression EXP" DebuggerCommand.setup(locals(), category="data", need_stack=True) def run(self, args): if 1 == len(args): if self.proc.current_source_text: text = self.proc.current_source_text.rstrip("\n") if "?" == args[0][-1]: text = extract_expression(text) self.msg("eval: %s" % text) pass else: self.errmsg("Don't have 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
class DisplayCommand(DebuggerCommand): """**display** [*format*] *expression* Print value of expression *expression* each time the program stops. *format* may be used before *expression* and may be one of `/c` for char, `/x` for hex, `/o` for octal, `/f` for float or `/s` for string. For now, display expressions are only evaluated when in the same code as the frame that was in effect when the display expression was set. This is a departure from gdb and we may allow for more flexibility in the future to specify whether this should be the case or not. With no argument, evaluate and display all currently requested auto-display expressions. Use `undisplay` to cancel display requests previously made.""" short_help = "Display expressions when entering debugger" format_specs = ("/c", "/x", "/o", "/f", "/s") DebuggerCommand.setup(locals(), category="data") def complete(self, prefix): return DisplayCommand.format_specs + complete_id_and_builtins( self, prefix) def run_eval_display(self, args=None): for line in self.proc.display_mgr.display(self.proc.curframe): self.msg(line) return def run(self, args): if len(args) == 1: # Display anything active self.run_eval_display(self) else: if args[1] in DisplayCommand.format_specs: if len(args) == 2: self.errmsg("Expecting an expression after the format") return format = args[1] expr = " ".join(args[2:]) else: format = None expr = " ".join(args[1:]) pass dp = self.proc.display_mgr.add(self.proc.curframe, expr, format) if dp is None: self.errmsg('Error evaluating "%s" in the current frame' % expr) return self.msg(dp.format(show_enabled=False)) self.proc.add_preloop_hook(self.run_eval_display) self.msg(dp.to_s(self.proc.curframe)) return False pass
def __init__(self, proc, name=None, base="trepan"): """Initialize show subcommands. Note: instance variable name has to be setcmds ('set' + 'cmds') for subcommand completion to work.""" DebuggerCommand.__init__(self, proc) # Name is set in testing if name is None: name = self.__module__.split(".")[-1] self.__class__.name = name self.cmds = Subcmd(name, self) self.name = name self._load_debugger_subcommands(name, base) self.proc = proc return
class DEvalCommand(DebuggerCommand): """**deval** **deval?** Run a the current deparsed expression in the context of the current frame. Normally we are stopped before an expression so the thing that corresponds to the `eval` command is running the parent construct. `deval?` will run just the command associated with the next piece of code to be run. Examples: --------- deval # Run *parent* of current deparsed code deval? # Run current deparsed code See also: --------- `eval` """ aliases = ("deval?",) short_help = "Print value of deparsed expression" DebuggerCommand.setup(locals(), category="data", need_stack=True, max_args=0) def run(self, args): co = self.proc.curframe.f_code name = co.co_name last_i = self.proc.curframe.f_lasti if last_i == -1: last_i = 0 sys_version = version_info[0] + (version_info[1] / 10.0) try: deparsed = deparse_code(sys_version, co, is_pypy=IS_PYPY) nodeInfo = deparsed_find((name, last_i), deparsed, co) if not nodeInfo: self.errmsg("Can't find exact offset %d" % last_i) return except: self.errmsg("error in deparsing code") return if "?" == args[0][-1]: extractInfo = deparsed.extract_node_info(nodeInfo.node) else: extractInfo, _ = deparsed.extract_parent_info(nodeInfo.node) text = extractInfo.selectedText text = text.strip() self.msg("Evaluating: %s" % text) try: self.proc.exec_line(text) except: pass
class ContinueCommand(DebuggerCommand): """**continue** Leave the debugger read-eval print loop and continue execution. Subsequent entry to the debugger however may occur via breakpoints or explicit calls, or exceptions. Examples: --------- continue # Continue execution continue 5 # Continue with a one-time breakpoint at line 5 continue basename # Go to os.path.basename if we have basename imported continue /usr/lib/python3.8/posixpath.py:110 # Possibly the same as # the above using file # and line number See also: --------- `step` `jump`, `next`, `finish` and `help syntax location` """ aliases = ("c", "continue!") execution_set = ["Running"] short_help = "Continue execution of debugged program" DebuggerCommand.setup(locals(), category="running", max_args=1, need_stack=True) def run(self, args): proc = self.proc if len(args) > 1: # FIXME: DRY this code. Better is to hook into tbreak. func, filename, lineno, condition = parse_break_cmd(proc, args) if not set_break(self, func, filename, lineno, condition, True, args): return False elif args[0][-1] == "!": proc.vm.frame.event_flags = proc.vm.event_flags = 0 # ignore all events else: # Until we hook in breakpoints into the debugger proper, we'll # treat continue like step in the VM interpreter but not # from our filtering process proc.vm.frame.event_flags = PyVMEVENT_ALL proc.core.event_flags = (PyVMEVENT_LINE | PyVMEVENT_INSTRUCTION | PyVMEVENT_CALL | PyVMEVENT_FATAL) self.core.step_ignore = -1 self.proc.continue_running = True # Break out of command read loop self.return_status = "continue" # Tell interpreter to continue running pass
class EditCommand(DebuggerCommand): """**edit** *location* Edit specified file or module. With no argument, edits file containing most recent line listed. See also: --------- `list`""" aliases = ("ed",) short_help = "Edit specified file or module" DebuggerCommand.setup( locals(), category="files", max_args=1, ) def run(self, args): curframe = self.proc.curframe if len(args) == 1: if curframe is None: self.errmsg( "edit: no stack to pick up position from. " "Use edit FILE:LINE form." ) return filename = curframe.f_code.co_filename lineno = curframe.f_lineno elif len(args) > 1: location = parse_location(self.proc, args) if not location: return if not location.path: return filename = location.path if not location.line_number: return lineno = location.line_number editor = "ex" if "EDITOR" in os.environ: editor = os.environ["EDITOR"] pass if os.path.exists(filename): os.system("%s +%d %s" % (editor, lineno, filename)) else: self.errmsg("edit: file %s doesn't exist" % filename) pass return pass
class SourceCommand(DebuggerCommand): """**source** [**-v**][**-Y**|**-N**][**-c**] *file* Read debugger commands from a file named *file*. Optional *-v* switch (before the filename) causes each command in *file* to be echoed as it is executed. Option *-Y* sets the default value in any confirmation command to be "yes" and *-N* sets the default value to "no". Note that the command startup file `.trepan3krc` is read automatically via a *source* command the debugger is started. An error in any command terminates execution of the command file unless option `-c` is given.""" short_help = "Read and run debugger commands from a file" DebuggerCommand.setup(locals(), category="support", max_args=1) def complete(self, prefix): # files = Readline::FILENAME_COMPLETION_PROC.call(prefix) || [] opts = ["-v", "-Y", "-N", "-c"] # + files return complete_token(opts, prefix) def run(self, args): parms = args[1:-1] opts = {} for arg in parms: if arg == "-v": opts["verbose"] = True elif arg == "-Y": opts["confirm_val"] = True elif arg == "-N": opts["confirm_val"] = False elif arg == "-c": opts["abort_on_error"] = False pass filename = args[-1] expanded_file = osp.expanduser(filename) if not readable(expanded_file): self.errmsg("Debugger command file '%s' is not a readable file" % filename) return False # Push a new interface. script_intf = ScriptInterface(expanded_file, opts=opts, out=self.debugger.intf[-1].output) self.debugger.intf.append(script_intf) return False
class TempBreakCommand(DebuggerCommand): """**tbreak** [*location*] [**if** *condition*] Sets a temporary breakpoint, i.e. one that is removed the after the first time it is encountered. See the help for location for what can be specified there. Without arguments or an empty *location*, the temporary breakpoint is set at the current stopped location. If the word `if` is given after *location*, subsequent arguments given a boolean condition which must evaluate to True before the breakpoint is honored. Examples: --------- tbreak # Break where we are current stopped at tbreak if i < j # Break at current line if i < j tbreak 10 # Break on line 10 of the file we are # currently stopped at tbreak os.path.join() # Break in function os.path.join tbreak x[i].fn() if x # break in function specified by x[i].fn # if x is set tbreak os.path:45 # Break on line 45 file holding module os.path tbreak myfile.py:45 # Break on line 45 of myfile.py break '''c:\\foo.bat''':1" # One way to specify a Windows file name, break '''/My Docs/foo.py''':1" # One way to specify path with blanks in it See also: --------- `break`, `condition` and `help syntax location`.""" aliases = ("tb", "tbreak!", "tb!") min_args = 0 short_help = "Set temporary breakpoint at specified line or function" DebuggerCommand.setup(locals(), category="breakpoints", need_stack=True) complete = complete_break_linenumber def run(self, args): func, filename, lineno, condition, offset = parse_break_cmd( self.proc, args) if not (func == None and filename == None): set_break(self, func, filename, lineno, condition, True, args) return
class FinishCommand(DebuggerCommand): """**finish** [*level*] Continue execution until leaving the current function. When *level* is specified, that many frame levels need to be popped. Note that *yield* and exceptions raised my reduce the number of stack frames. Also, if a thread is switched, we stop ignoring levels. See the `break` command if you want to stop at a particular point in a See also: --------- `continue`, `step`, `next`.""" # FIXME: add finish [levels|fn] # If fn is given, that's a short-hand way of looking up how many levels # until that frame. However the same provisions regarding stopping, # exceptions, 'yield'ing and so on still apply. aliases = ("fin", ) execution_set = ["Running"] max_args = 1 short_help = "Execute until selected stack frame returns" DebuggerCommand.setup(locals(), category="running", max_args=1, need_stack=True) def run(self, args): if self.proc.stack is None: return False if len(args) <= 1: levels = 1 else: levels = self.proc.get_int(args[1], default=1, cmdname="finish") if levels is None: return False pass # print "+++ %d" % levels self.core.step_events = ["return"] self.core.stop_on_finish = True self.core.stop_level = count_frames(self.proc.frame) - levels self.core.last_frame = self.proc.frame self.proc.continue_running = True # Break out of command read loop return True pass
class ContinueCommand(DebuggerCommand): """**continue** [*location*] Leave the debugger read-eval print loop and continue execution. Subsequent entry to the debugger however may occur via breakpoints or explicit calls, or exceptions. If *location* is given, a temporary breakpoint is set at that position before continuing. Examples: --------- continue # Continue execution continue 5 # Continue with a one-time breakpoint at line 5 continue basename # Go to os.path.basename if we have basename imported continue /usr/lib/python3.8/posixpath.py:110 # Possibly the same as # the above using file # and line number See also: --------- `step` `jump`, `next`, `finish` and `help syntax location`""" aliases = ("c", ) execution_set = ["Running"] short_help = "Continue execution of debugged program" DebuggerCommand.setup(locals(), category="running", max_args=1, need_stack=True) def run(self, args): if len(args) > 1: # FIXME: DRY this code. Better is to hook into tbreak. func, filename, lineno, condition, offset = parse_break_cmd( self.proc, args) if not set_break(self, func, filename, lineno, condition, True, args): return False self.core.step_events = None # All events self.core.step_ignore = -1 self.proc.continue_running = True # Break out of command read loop return True pass
class DeleteCommand(DebuggerCommand): """**delete** [*bpnumber* [*bpnumber*...]] Delete some breakpoints. Arguments are breakpoint numbers with spaces in between. To delete all breakpoints, give no argument. Without arguments, clear all breaks (but first ask for confirmation). See also: --------- `clear`""" aliases = ("delete!", ) short_help = "Delete some breakpoints or auto-display expressions" complete = complete_bpnumber DebuggerCommand.setup(locals(), category="breakpoints") def run(self, args): if len(args) <= 1: if "!" != args[0][-1]: confirmed = self.confirm("Delete all breakpoints", False) else: confirmed = True if confirmed: self.msg(self.core.bpmgr.delete_all_breakpoints()) return for arg in args[1:]: i = self.proc.get_int(arg, min_value=1, default=None, cmdname="delete") if i is None: continue success, msg = self.core.bpmgr.delete_breakpoint_by_number(i) if not success: self.errmsg(msg) else: self.msg("Deleted breakpoint %d" % i) pass pass return
class HandleCommand(DebuggerCommand): """**handle** [*signal-name* [*action1* *action2* ...]] Specify how to handle a signal *signal-name*. *signal-name* can be a signal name like `SIGINT` or a signal number like 2. The absolute value is used for numbers so -9 is the same as 9 (`SIGKILL`). When signal names are used, you can drop off the leading "SIG" if you want. Also letter case is not important either. Arguments are signals and actions to apply to those signals. recognized actions include `stop`, `nostop`, `print`, `noprint`, `pass`, `nopass`, `ignore`, or `noignore`. `stop` means reenter debugger if this signal happens (implies `print` and `nopass`). `Print` means print a message if this signal happens. `Pass` means let program see this signal; otherwise the program see it. `Ignore` is a synonym for `nopass`; `noignore` is a synonym for `pass`. Without any action names the current settings are shown. **Examples:** handle INT # Show current settings of SIGINT handle SIGINT # same as above handle int # same as above handle 2 # Probably the same as above handle -2 # the same as above handle INT nostop # Don't stop in the debugger on SIGINT """ short_help = "Specify how to handle a signal" DebuggerCommand.setup(locals(), category="running", min_args=1) def run(self, args): if self.debugger.sigmgr.action(" ".join(args[1:])) and len(args) > 2: # Show results of recent change self.debugger.sigmgr.info_signal([args[1]]) pass return pass
class NextCommand(DebuggerCommand): """**next**[**+**|**-**] [*count*] Execute the current simple statement stopping at the next event but ignoring steps into function calls at this level, With an integer argument, perform `next` that many times. However if an exception occurs at this level, or we *return*, *yield* or the thread changes, we stop regardless of count. A suffix of `+` on the command or an alias to the command forces to move to another line, while a suffix of `-` does the opposite and disables the requiring a move to a new line. If no suffix is given, the debugger setting 'different-line' determines this behavior. See also: --------- `step`, `skip`, `jump` (there's no `hop` yet), `continue`, and `finish` for other ways to progress execution.""" aliases = ("next+", "next-", "n", "n-", "n+") execution_set = ["Running"] short_help = "Step over" DebuggerCommand.setup(locals(), category="running", need_stack=True, max_args=1) def run(self, args): if len(args) <= 1: step_ignore = 0 else: step_ignore = self.proc.get_int(args[1], default=1, cmdname="next") if step_ignore is None: return False # 0 means stop now or step 1, so we subtract 1. step_ignore -= 1 pass self.core.different_line = want_different_line( args[0], self.debugger.settings["different"] ) self.core.set_next(self.proc.frame, step_ignore) self.proc.continue_running = True # Break out of command read loop return True pass
class PCommand(DebuggerCommand): """**print** *expression* Print the value of the expression. Variables accessible are those of the environment of the selected stack frame, plus globals. The expression may be preceded with */fmt* where *fmt* is one of the format letters 'c', 'x', 'o', 'f', or 's' for chr, hex, oct, float or str respectively. If the length output string large, the first part of the value is shown and `...` indicates it has been truncated. See also: --------- `pp` and `examine` for commands which do more in the way of formatting.""" aliases = ("print", "pr") short_help = "Print value of expression EXP" complete = complete_identifier DebuggerCommand.setup(locals(), category="data", need_stack=True, min_args=1) def run(self, args): if len(args) > 2 and "/" == args[1][0]: fmt = args[1] del args[1] else: fmt = None pass arg = " ".join(args[1:]) try: val = self.proc.eval(arg) if fmt: val = printf(val, fmt) pass self.msg(self.proc._saferepr(val)) except: pass
class UndisplayCommand(DebuggerCommand): """**undisplay** *display-number*... Cancel some expressions to be displayed when program stops. Arguments are the code numbers of the expressions to stop displaying. No argument cancels all automatic-display expressions and is the same as `delete display`. See also: --------- `info display` to see current list of code numbers. """ aliases = ('und', ) short_help = ('Cancel some expressions to be displayed ' 'when program stops') DebuggerCommand.setup(locals(), category="data", min_args=1) def complete(self, prefix): completions = [str(disp.number) for disp in self.proc.display_mgr.list] return Mcomplete.complete_token(completions, prefix) def run(self, args): if len(args) == 1: self.proc.display_mgr.clear() return for i in args[1:]: i = self.proc.get_an_int(i, '%r must be a display number' % i) if i is not None: if not self.proc.display_mgr.delete_index(i): self.errmsg("No display number %d." % i) return pass pass return False pass
class RestartCommand(DebuggerCommand): """**restart** Restart debugger and program via an *exec()* call. All state is lost, and new copy of the debugger is used. See also: --------- `run` for another way to restart the debugged program. See `quit`, `exit` or `kill` for termination commands.""" short_help = "(Hard) restart of program via execv()" DebuggerCommand.setup(locals(), category="support", max_args=0) def run(self, args): sys_argv = self.debugger.restart_argv() if sys_argv and len(sys_argv) > 0: confirmed = self.confirm("Restart (execv)", False) if confirmed: self.msg( wrapped_lines("Re exec'ing:", repr(sys_argv), self.settings["width"])) # Run atexit finalize routines. This seems to be Kosher: # http://mail.python.org/pipermail/python-dev/2009-February/085791.html # NOQA try: atexit._run_exitfuncs() except: pass os.execvp(sys_argv[0], sys_argv) pass pass else: self.errmsg("No executable file and command options recorded.") pass return pass
class ClearCommand(DebuggerCommand): """**clear** [*linenumber*] Clear some breakpoints by line number. See also: --------- `delete` """ short_help = "Delete some breakpoints on a line" DebuggerCommand.setup(locals(), category="breakpoints", need_stack=True) complete = complete_bpnumber def run(self, args): proc = self.proc curframe = proc.curframe filename = proc.list_filename if len(args) <= 1: lineno = inspect.getlineno(curframe) else: lineno = proc.get_an_int( args[1], "The 'clear' command argument when given should be " "a line number. Got %s", min_value=1, ) if lineno is None: return linenos = self.core.bpmgr.delete_breakpoints_by_lineno(filename, lineno) if len(linenos) == 0: self.errmsg("No breakpoint at line %d" % lineno) elif len(linenos) == 1: self.msg("Deleted breakpoint %d" % linenos[0]) elif len(linenos) > 1: self.msg("Deleted breakpoints %s" % " ".join([str(i) for i in linenos])) return
class ConditionCommand(DebuggerCommand): """**condition** *bp_number* *condition* *bp_number* is a breakpoint number. *condition* is an expression which must evaluate to *True* before the breakpoint is honored. If *condition* is absent, any existing condition is removed; i.e., the breakpoint is made unconditional. Examples: --------- condition 5 x > 10 # Breakpoint 5 now has condition x > 10 condition 5 # Remove above condition See also: --------- `break`, `tbreak`.""" aliases = ("cond", ) short_help = "Specify breakpoint number N to break only if COND is True" DebuggerCommand.setup(locals(), category="breakpoints", min_args=1) complete = complete_bpnumber def run(self, args): success, msg, bp = self.core.bpmgr.get_breakpoint(int(args[1])) if not success: self.errmsg(msg) return if len(args) > 2: condition = " ".join(args[2:]) else: condition = None self.msg("Breakpoint %d is now unconditional." % bp.number) pass bp.condition = condition return
class ExamineCommand(DebuggerCommand): """**examine** *expr1* [*expr2* ...] Examine value, type and object attributes of an expression. In contrast to normal Python expressions, expressions should not have blanks which would cause shlex to see them as different tokens. Examples: --------- examine x+1 # ok examine x + 1 # not ok See also: --------- `pr`, `pp`, and `whatis`.""" aliases = ("x", ) short_help = "Examine value, type, and object attributes " "of an expression" DebuggerCommand.setup( locals(), category="data", need_stack=True, min_args=1, ) def run(self, args): for arg in args[1:]: s = print_obj(arg, self.proc.curframe) self.msg(s) pass return pass
class IPythonCommand(DebuggerCommand): """**ipython** [**-d**] Run IPython as a command subshell. If *-d* is passed, you can access debugger state via local variable *debugger*. To issue a debugger command use function *dbgr()*. For example: dbgr('info program') See also: --------- `python`, `bpython`""" short_help = "Run IPython as a command subshell" DebuggerCommand.setup(locals(), category="support", max_args=1) def dbgr(self, string): """Invoke a debugger command from inside a IPython shell called inside the debugger. """ self.proc.cmd_queue.append(string) self.proc.process_command() return def run(self, args): # See if python's code module is around # Python does it's own history thing. # Make sure it doesn't damage ours. have_line_edit = self.debugger.intf[-1].input.line_edit if have_line_edit: try: self.proc.write_history_file() except IOError: pass pass cfg = Config() banner_tmpl = """IPython trepan3k shell%s Use dbgr(*string*) to issue non-continuing debugger command: *string*""" debug = len(args) > 1 and args[1] == "-d" if debug: banner_tmpl += "\nVariable 'debugger' contains a trepan " "debugger object." pass try: from IPython.terminal.embed import InteractiveShellEmbed except ImportError: from IPython.frontend.terminal.embed import InteractiveShellEmbed # Now create an instance of the embeddable shell. The first # argument is a string with options exactly as you would type them # if you were starting IPython at the system command line. Any # parameters you want to define for configuration can thus be # specified here. # Add common classes and methods our namespace here so that # inside the ipython shell users don't have run imports my_locals = {} my_globals = None if self.proc.curframe: my_globals = self.proc.curframe.f_globals if self.proc.curframe.f_locals: my_locals = self.proc.curframe.f_locals pass pass # Give IPython and the user a way to get access to the debugger. if debug: my_locals["debugger"] = self.debugger my_locals["dbgr"] = self.dbgr cfg.TerminalInteractiveShell.confirm_exit = False # sys.ps1 = 'trepan3 >>> ' if len(my_locals): banner = banner_tmpl % " with locals" else: banner = banner_tmpl % "" pass InteractiveShellEmbed( config=cfg, banner1=banner, user_ns=my_locals, module=my_globals, exit_msg="IPython exiting to trepan3k...", )() # restore completion and our history if we can do so. if hasattr(self.proc.intf[-1], "complete"): try: from readline import set_completer, parse_and_bind parse_and_bind("tab: complete") set_completer(self.proc.intf[-1].complete) except ImportError: pass pass if have_line_edit: self.proc.read_history_file() pass return pass
class WhatisCommand(DebuggerCommand): """**whatis** *arg* Prints the information argument which can be a Python expression. When possible, we give information about: * type of argument * doc string for the argument (if a module, class, or function) * comments around the definition of the argument (module) * the module it was defined in * where the argument was defined We get this most of this information via the *inspect* module. See also: -------- the *inspect* module.""" aliases = () min_args = 1 short_help = "Print data type of expression EXP" complete = complete_id_and_builtins DebuggerCommand.setup(locals(), category="data", min_args=1, need_stack=True) def run(self, args): proc = self.proc arg = proc.cmd_argstr try: if not proc.curframe: # ?? Should we have set up a dummy globals # to have persistence? value = eval(arg, None, None) else: value = eval(arg, proc.curframe.f_globals, proc.curframe.f_locals) except: t, v = sys.exc_info()[:2] if type(t) == str: exc_type_name = t else: exc_type_name = t.__name__ if exc_type_name == "NameError": self.errmsg("Name Error: %s" % arg) else: self.errmsg("%s: %s" % (exc_type_name, proc._saferepr(v))) return False self.section("What is for %s" % arg) get_doc = False if inspect.ismethod(value): get_doc = True self.msg("method %s%s" % ( value.func_code.co_name, inspect.formatargspec(inspect.getargspec(value)), )) elif inspect.isfunction(value): get_doc = True self.msg("function %s%s" % ( value.func_code.co_name, inspect.formatargspec(inspect.getargspec(value)), )) elif (inspect.isabstract(value) or inspect.isbuiltin(value) or inspect.isclass(value) or inspect.isgeneratorfunction(value) or inspect.ismethoddescriptor(value)): get_doc = True self.msg(type(value)) doc = inspect.getdoc(value) if get_doc and doc: self.msg(" doc:\n%s" % doc) comments = inspect.getcomments(value) if comments: self.msg(" comments:\n%s" % comments) try: m = inspect.getmodule(value) if m: self.msg(" module:\t%s" % m) except: try: f = inspect.getfile(value) self.msg(" file: %s" % f) except: pass pass return False pass
class DisassembleCommand(DebuggerCommand): """**disassemble** [*thing*] **disassemble** [*address-range*] Disassembles bytecode. See `help syntax range` for what can go in a list range. Without arguments, print lines starting from where the last list left off since the last entry to the debugger. We start off at the location indicated by the current stack. in addition you can also use: - a '.' for the location of the current frame - a '-' for the lines before the last list - a '+' for the lines after the last list With a class, method, function, pyc-file, code or string argument disassemble that. Examples: -------- :: disassemble # Possibly current frame disassemble + # Same as above disassemble os.path # Disassemble all of os.path # xxx disassemble os.path.normcase() # Disaasemble just method os.path.normcase disassemble 3 # Disassemble starting from line 3 disassemble 3, 10 # Disassemble lines 3 to 10 disassemble *0, *10 # Disassemble offset 0..10 (10 not included) disassemble myprog.pyc # Disassemble file myprog.pyc See also: --------- `help syntax arange`, `deparse`, `list`, `info pc`. """ aliases = ("disasm", ) # Note: we will have disable short_help = "Disassemble Python bytecode" DebuggerCommand.setup(locals(), category="data", max_args=2) def run(self, args): proc = self.proc # FIXME: add a setting for assembler list size listsize = 4 opts = { "highlight": self.settings["highlight"], "start_line": 1, "end_line": None, "start_offset": None, "end_offset": None, "relative_pos": False, "asm_format": self.settings["asmfmt"], } curframe = proc.curframe if curframe: line_no = inspect.getlineno(curframe) opts["start_line"] = line_no - 1 opts["end_line"] = line_no + 1 do_parse = True if len(args) == 2: if args[1].endswith("()"): eval_args = args[1][:-2] else: eval_args = args[1] try: obj = self.proc.eval(eval_args, show_error=False) except: obj = None else: if (inspect.ismethod(obj) or inspect.isfunction(obj) or inspect.isgeneratorfunction(obj) or inspect.isgenerator(obj) or inspect.isframe(obj) or inspect.iscode(obj)): opts["start_offset"] = 0 last_is_offset = is_offset = True start = 0 opts["start_line"] = 0 opts["end_line"] = last = None do_parse = False bytecode_file = None if do_parse: ( bytecode_file, start, is_offset, last, last_is_offset, obj, ) = parse_addr_list_cmd(proc, args, listsize) if bytecode_file is None: return if is_offset: opts["start_offset"] = start else: opts["start_line"] = start if last_is_offset: opts["end_offset"] = last else: opts["end_line"] = last if not obj and (bytecode_file and (not bytecode_file.endswith(".pyo") or bytecode_file.endswith("pyc"))): # bytecode_file may be a source file. Try to tun it into a bytecode file for diassembly. bytecode_file = cache_from_source(bytecode_file) if bytecode_file and Mfile.readable(bytecode_file): self.msg("Reading %s ..." % bytecode_file) ( version, timestamp, magic_int, obj, is_pypy, source_size, sip_hash, ) = load_module(bytecode_file) elif not curframe: self.errmsg("No frame selected.") return else: try: obj = self.proc.eval(args[1]) opts["start_line"] = -1 except: self.errmsg(("Object '%s' is not something we can" + " disassemble.") % args[1]) return # We now have all information. Do the listing. (obj, proc.list_offset) = dis(self.msg, self.msg_nocr, self.section, self.errmsg, obj, **opts) return False