Skip to content

Getting into the Debugger

rocky edited this page Apr 23, 2023 · 5 revisions

Entering the Trepan Debugger

.. toctree::

There are a couple ways you can enter the debugger:

  • Run the debugger initially
  • Run the debugger from inside IPython or python -i
  • Add a call to the debugger inside your code
  • Run post-mortem debugging when an unexpected exception occurs
  • Set up an exception handler to enter the debugger on a signal

The simplest way to debug your program is to call run trepan2 (or trepan3k for Python 3). Give the name of your program and its options and any debugger options:

$ cat test.py
print('Hello, World!')

$ trepan2 test.py # or trepan3k test.py

For help on trepan2's or trepan3k's options, use the --help option.

$ trepan2 --help
Usage: trepan2 [debugger-options] [python-script [script-options...]]
...

To separate options to the program you want to debug from trepan2's options put -- after the debugger's options:

$ trepan2 --trace -- test.py --test-option1 b c

If you have previously set up remote debugging using trepan2 --server, you'll want to run the client version of trepan2 which is a separate program trepan2c.

Use the trepan IPython extension.

To install execute the the following code snippet in an IPython shell or IPython notebook cell:

%install_ext https://raw.github.com/rocky/ipython-trepan/master/trepanmagic.py
%load_ext trepanmagic

or put trepanmagic.py in $HOME/.python/profile_default/startup:

cd `$HOME/.python/profile_default/startup`:
wget https://raw.github.com/rocky/ipython-trepan/master/trepanmagic.py

After installing the trepan extension, the following IPython magic functions are added:

  • %trepan_eval evaluate a Python statement under the debugger
  • %trepan run the debugger on a Python program
  • %trepan_pm do post-mortem debugging
$ ipython
Python 2.7.8 (default, Apr  6 2015, 16:25:30)
...

In [1]: %load_ext trepanmagic
trepanmagic.py loaded
In [2]: import os.path
In [3]: %trepan_eval(os.path.join('foo', 'bar'))
(/tmp/eval_stringS9ST2e.py:1 remapped <string>): <module>
-> 1 (os.path.join('foo', 'bar'))
(trepan2) s
(/home/rocky/.pyenv/versions/2.7.8/lib/python2.7/posixpath.py:68): join
-> 68 def join(a, *p):
(trepan2) s
(/home/rocky/.pyenv/versions/2.7.8/lib/python2.7/posixpath.py:73): join
-- 73     path = a
(trepan2) c
Out[3]: 'foo/bar'
In [4]:

See also the examples directory.

Note: by "interactive python shell" I mean running "python" or "python -i" and this is distinct from going into IPython which was covered in the last section.

Put these lines in a file:

import inspect
from trepan.api import run_eval
def debug(str):
  frame = inspect.currentframe()
  return run_eval(str, globals_=frame.f_globals, locals_=frame.f_locals)
print("pythonrc loaded") # customize or remove this

A copy of the above can be found here. I usually put these line in $HOME/.pythonrc. Set the environment variable PYTHONSTARTUP to $HOME/.pythonrc.

After doing this, when you run python -i you should see on entry the print message from the file. For example:

$ python -i
Python ...
Type "help", "copyright", "credits" or "license" for more information.
pythonrc loaded
>>>

If you see the above "pythonrc" message, great! If not, it might be that PYTHONSTARTUP is not defined. Here run:

>>> path="pythonrc" # customize to location of file
>>> exec(open(path).read())
pythonrc loaded
>>>

and you should see the "pythonrc" message as shown above.

Once that code is loaded, the debug() function is defined. To debug some python code, you can call that function. Here is an example:

>>> import os.path
>>> debug('os.path.join("a", "b")')
(/tmp/eval_stringBMzXCQ.py:1 remapped <string>): <module>
-> 1 os.path.join("a", "b")
(trepan2) step
(/home/rocky/.pyenv/versions/2.7.8/lib/python2.7/posixpath.py:68): join
-> 68 def join(a, *p):
(trepan2) continue
'a/b'
>>>

Note in the above, we pass to the debug() function a string. That is, we pass 'os.path.join("a", "b")', not os.path.join("a", "b") which would have the effect of running the code to be evaluated first before calling debug(). This is not an error, but debugging evaluating a string, is probably not what you want to do.

To do: add and document run_call()

Sometimes it is not feasible to invoke the program from the debugger. Although the debugger tries to set things up to make it look like your program is called, sometimes the differences matter. Also the debugger adds overhead and slows down your program.

Another possibility then is to add statements into your program to call the debugger at the spot in the program you want. To do this, import trepan.api and make a call to trepan.api.debug(). For example:

# Code run here trepan2/trepan3k doesn't even see at all.
# ...
from trepan.api import debug
# trepan is accessible but inactive.
# work, work, work...
debug() # Get me into the debugger!

Since debug() is a function, call it can be nested inside some sort of conditional statement allowing one to be very precise about the conditions you want to debug under. And until first call to debug(), there is no debugger overhead.

debug() causes the statement after the call to be stopped at. Sometimes though there is no after statement. In this case, adding the named parameter step_ignore=0 will cause the debugger to be entered inside the debug() call:

# ...
def foo():
   # some code
   debug(step_ignore=0) # Stop before even returning from the debug() call
foo()  # Note there's no statement following foo()

This is really just a variation of one of the other methods. To install and call the debugger on signal USR1:

import signal
def signal_handler(num, f):
  from trepan.api import debug; debug()
  return
signal.signal(signal.SIGUSR1, signal_handler)
# Go about your business...

However, if you have entered the debugger either by running intially or previously via a debug() call, trepan has already set up such default handlers for many of the popular signals, like SIGINT. To see what trepan2 has installed use the info signals command:

(trepan2) info signals INT
 Signal        Stop   Print   Stack   Pass    Description
 SIGINT        Yes    Yes     No      No      Interrupt
(trepan2) info signals
Signal        Stop    Print   Stack   Pass    Description

SIGHUP        Yes     Yes     No      No      Hangup
SIGSYS        Yes     Yes     No      No      Bad system call
...

Commonly occuring signals like CHILD and unmaskable signals like KILL are not intercepted.

A startup profile is a text file that contains debugger commands. For example it might look like this:

$ cat ~/.configi/profile
set autolist
set different on
set autoeval on
set style colorful
print("My trepan startup file loaded")
$