Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
This decorator method can be used to get GDB style backtraces (Stack trace) of currently active stack frames during the execution of a python program, optionally printing positional/variable/keyword arguments for the functions in the outer frames.
import sys, inspect
def getBackTrace(printArgs=None , logger=sys.stdout.write):
'''
Related blog post - http://42bits.wordpress.com/2011/01/23/pdb-style-function-backtrace-for-python-using-inspect-module/
This decorator method can be used to get GDB style backtraces (Stack trace) of currently
active stack frames during the execution of a program,
optionally printing positional/variable/keyword arguments for the
functions in the outer frames.
It can be used to easily trace the function call flow in a running application,
without having to use a external debugger like pdb.
@Input
printArgs - This is a boolean value which is true/false depending on whether function arguments need to be
displayed.
logger - This can be a python logger instance to log information to a log file.
By default information is printed on stdout.
@Output
Output is similar to GNU Debugger Stack traces, the difference being in the way arguments are displayed.
More info on GDB backtrace command here - http://inside.mines.edu/fs_home/lwiencke/elab/gdb/gdb_42.html
Note: AFAIK, Once a function is called with the respective arguments, these arguments become part of locals()
function scope in python, and they can be mutated by function code. So, the argument values may be **DIFFERENT**
than what the function initially may have got and I am only displaying
values the variables had at the time stack trace was done.
'''
log = logger or logger.debug
log('*'*50 + ' BackTrace START ' + '*'*50+'\n')
def wrap(f):
log("#0 %s at %s\n"%(f.__name__, f.__module__))
def wrapped_f(*args, **kwargs):
outerFrames = inspect.stack()[1:-1]
for frame_idx, frame in enumerate(outerFrames):
functionName = frame[3]
moduleName = inspect.getmodulename(frame[1]) or frame[1]
traceStr = "#%d %s at %s:%s\n"%(frame_idx+1, functionName, moduleName, frame[2])
if printArgs:
argObj = inspect.getargvalues(frame[0])
posArgs = sorted([ [arg, argObj.locals.get(arg)] for arg in argObj.args or []])
varArgs = sorted(argObj.locals.get(argObj.varargs,{}))
keywordArgs = sorted(argObj.locals.get(argObj.keywords,{}).items())
prettyArgsString = ', '.join('%s=%s'%(str(item[0]), str(item[1])) for item in posArgs)
prettyVarArgsString = ', '.join('%s'%(str(item)) for item in varArgs)
prettyKeywordArgsString = ', '.join('%s=%s'%(str(item[0]), str(item[1])) for item in keywordArgs)
traceStr+="%-10sPositional Arguments : %s\n"%('',prettyArgsString)
traceStr+="%-10s*Variable Aruguments : %s\n"%('',prettyVarArgsString)
traceStr+="%-10s**Keyword Arguments : %s\n"%('',prettyKeywordArgsString)
traceStr+= '\n'
log(traceStr)
log('\n')
log('*'*50 + ' BackTrace END ' + '*'*50+'\n\n')
f(*args, **kwargs) #Call the function
return wrapped_f
return wrap
###############################################################
## Example Usage
## Output
"""
************************************************** BackTrace START **************************************************
#0 func3 at __main__
#1 func2 at getBackTrace:67
Positional Arguments : i=10000
*Variable Aruguments : 1230, 12345
**Keyword Arguments : oi=20
#2 func1 at getBackTrace:64
Positional Arguments : i=100
*Variable Aruguments :
**Keyword Arguments :
************************************************** BackTrace END **************************************************
"""
def func1(i):
func2(i**2, 1230, 12345, oi=20)
def func2(i, *args, **kwargs):
func3(i**2)
#@getBackTrace()
@getBackTrace(printArgs=True)
def func3(i, **kwargs):
pass
if __name__ == '__main__':
func1(100)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment