The Anatomy of a Traceback

 

 

Nir Cohen @ strigo.io

nir0s@github

@thinkops

BEFORE TRACEBACK
...

Traceback (most recent call last):
  File "test.py", line 115, in <module>
    i = Moose()
  File "test.py", line 17, in __init__
    self.a_moose_once_bit_my_sister()
  File "test.py", line 20, in a_moose_once_bit_my_sister
    _mynd_you()
  File "test.py", line 110, in _mynd_you
    test2.moose_bites_kan_be_pretty_nasti()
  File "...test2.py", line 10, in moose_bites_kan_be_pretty_nasti
    moose_func(ex)
  File "...test2.py", line 14, in moose_func
    raise raise_moose(ex)
  File "...test2.py", line 18, in raise_moose
    raise MooseError(ex)
test2.MooseError: [Errno 2] No such file or directory: \
    'certainly_a_non_existing_moose'

The traceback object


try:
    raise Exception
except Exception as e:
    # py3 specific. In py2 would use:
    # _, __, tb = sys.exc_info()
    # raise Exception(tb)
    raise Exception(e.__traceback__)

...


Traceback (most recent call last):
  File "test.py", line 7, in <module>
    raise Exception(e.__traceback__)
Exception: <traceback object at 0x7ff5e7f38e08>

Call stack

The call to all functions during execution, where a call to a single function is called a Stack Frame

tb object properties

  • `tb_frame` - frame object at this level
  • `tb_lasti` - index of last attempted instruction in bytecode
  • `tb_lineno` - current line number in Python source code
  • `tb_next` - next inner traceback object

The frame object

  • `f_back` - next outer frame object
  • `f_builtins` - builtins namespace seen by this frame
  • `f_code` - code object being executed by this frame
  • `f_globals` - global namespace seen by this frame
  • `f_lasti` - ...
  • `f_lineno` - ...
  • `f_locals` - local namespace seen by this frame
  • `f_trace` - tracing function for this frame, or `None`

The code object

Too much information.

(See https://goo.gl/lCCxXr)

Use case

Let's say we want to create a logger which will take a parsed/reformatted traceback and ship it for analysis.


from .logger.tracelogger import parse, send

tracelogger.setup(host=..., port=..., format='json')

try:
    raise Exception
except Exception as e:
    send(parse(traceback.extract_tb(e.__traceback__)))

Replacing the excepthook


def new_excepthook(tpe, value, tb):
    # tpe - exception type (e.g. MyException)
    # value - the value of the exception 
    # (e.g. No such file or directory ...)
    # tb - the traceback object
    do_something_with_exception_type(tpe)
    do_something_with_except_value(value)
    reformat_traceback(tb)

sys.excepthook = new_excepthook

Extract some info


import traceback
 
def reformat_traceback(tb):
    # Again, a bit different in py2
    tb_entries = traceback.extract_tb(e.__traceback__)
    for entry in tb_entries:
        print((entry.filename, entry.lineno, entry.name, entry.line))

...
('test.py', 140, 'i = Moose()', '<module>')
('test.py', 42, 'self.a_moose_once_bit_my_sister()', '__init__')
('test.py', 45, '_mynd_you()', 'a_moose_once_bit_my_sister')
('test.py', 135, 'test2.moose_bites_kan_be_pretty_nasti()', '_mynd_you')
('.../test2.py', 10, 'moose_func(ex)', 'moose_bites_kan_be_pretty_nasti')
('.../test2.py', 14, 'raise raise_moose(ex)', 'moose_func')
('.../test2.py', 18, 'raise MooseError(ex)', 'raise_moose')

Another use case

Collecting all tracebacks from multiple threads and handling in the main thread

backtrace

Formats tracebacks

Usage (Python)


import backtrace

backtrace.hook(
    reverse=False,
    align=True,
    strip_path=True,
    enable_on_envvar_only=False,
    on_tty=False,
    conservative=False,
    styles={})

Usage (Bash)


$ python raise_exception.py 2>&1 | backtrace
...

Traceback (Most recent call last):
115 test.py <module> -->      i = Moose()
17 test.py __init__ -->      self.a_moose_once_bit_my_sister()
20 test.py a_moose_once_bit_my_sister -->      _mynd_you()
110 test.py _mynd_you -->      test2.moose_bites_kan_be_pretty_nasti()
...

Project Page

References

  • traceback.py source: https://goo.gl/70bGY8
  • traceback.c source: https://goo.gl/0AC9Me
  • sysmodule source ref: https://goo.gl/URJBb6
  • frame object source ref: https://goo.gl/1FR5ZG
  • https://late.am/post/2012/03/26/exploring-python-code-objects.html
  • Stack vs. Frame: https://goo.gl/abuRHm
  • GDB Frames: https://goo.gl/zqc0zR
  • https://docs.python.org/3/library/traceback.html

The Anatomy of a Traceback

By Nir Cohen

The Anatomy of a Traceback

  • 1,745