如何使用traceit报告堆栈跟踪中的函数输入变量

时间:2010-04-11 13:22:19

标签: python debugging trace stack-trace

我一直在使用以下代码来跟踪程序的执行情况:

import sys
import linecache
import random

def traceit(frame, event, arg):
    if event == "line":
        lineno = frame.f_lineno
        filename = frame.f_globals["__file__"]
        if filename == "<stdin>":
            filename = "traceit.py"
        if (filename.endswith(".pyc") or
            filename.endswith(".pyo")):
            filename = filename[:-1]
        name = frame.f_globals["__name__"]
        line = linecache.getline(filename, lineno)
        print "%s:%s:%s: %s" % (name,  lineno,frame.f_code.co_name , line.rstrip())
    return traceit


def main():
    print "In main"
    for i in range(5):
        print i, random.randrange(0, 10)
    print "Done."

sys.settrace(traceit)
main()

使用此代码或类似代码,是否可以报告某些函数参数的值?换句话说,上面的代码告诉我“调用了哪些”函数,我想知道那些函数调用的输入变量的相应值是“什么”。

提前致谢。

4 个答案:

答案 0 :(得分:3)

您发布的traceit函数可用于在执行每行代码时打印信息。如果你需要的是调用某些函数时的函数名和参数,我建议改为使用这个跟踪装饰器:

import functools

def trace(f):
    '''This decorator shows how the function was called'''
    @functools.wraps(f)
    def wrapper(*arg,**kw):        
        arg_str=','.join(['%r'%a for a in arg]+['%s=%s'%(key,kw[key]) for key in kw])
        print "%s(%s)" % (f.__name__, arg_str)
        return f(*arg, **kw)
    return wrapper

您可以按如下方式使用它:

@trace
def foo(*args,**kws):
    pass

foo(1)
# foo(1)       
foo(y=1)
# foo(y=1)
foo(1,2,3)
# foo(1,2,3)

修改:以下是使用tracetraceit的示例: 下面,trace以两种不同的方式使用。通常的方法是装饰你定义的函数:

@trace
def foo(i):
    ....

但你也可以“修补”任何功能,无论你是否定义它:

random.randrange=trace(random.randrange)

所以这是一个例子:

import sys
import linecache
import random
import functools

def trace(f):
    '''This decorator shows how the function was called'''
    @functools.wraps(f)
    def wrapper(*arg,**kw):        
        arg_str=','.join(['%r'%a for a in arg]+['%s=%s'%(key,kw[key]) for key in kw])
        print "%s(%s)" % (f.__name__, arg_str)
        return f(*arg, **kw)
    return wrapper

def traceit(frame, event, arg):
    if event == "line":
        lineno = frame.f_lineno
        filename = frame.f_globals["__file__"]
        if filename == "<stdin>":
            filename = "traceit.py"
        if (filename.endswith(".pyc") or
            filename.endswith(".pyo")):
            filename = filename[:-1]
        name = frame.f_globals["__name__"]
        line = linecache.getline(filename, lineno)
        print "%s:%s:%s: %s" % (name,  lineno,frame.f_code.co_name , line.rstrip())
    return traceit

random.randrange=trace(random.randrange)

@trace
def foo(i):
    print i, random.randrange(0, 10)

def main():
    print "In main"
    for i in range(5):
        foo(i)
    print "Done."

sys.settrace(traceit)
main()

答案 1 :(得分:2)

frame.f_locals会给你局部变量的值,我猜你可以跟踪你看到的最后一帧,如果frame.f_back不是lastframe dump frame.f_locals。

我预测,虽然有太多的数据,你很快就会下雪。

以下是您修改的代码:

import sys
import linecache
import random

class Tracer(object):
    def __init__(self):
        self.lastframe = None

    def traceit(self, frame, event, arg):
        if event == "line":
            lineno = frame.f_lineno
            filename = frame.f_globals["__file__"]
            if filename == "<stdin>":
                filename = "traceit.py"
            if (filename.endswith(".pyc") or
                filename.endswith(".pyo")):
                filename = filename[:-1]
            name = frame.f_globals["__name__"]
            line = linecache.getline(filename, lineno)
            if frame.f_back is self.lastframe:
                print "%s:%s:%s: %s" % (name,  lineno,frame.f_code.co_name , line.rstrip())
            else:
                print "%s:%s:%s(%s)" % (name,  lineno,frame.f_code.co_name , str.join(', ', ("%s=%r" % item for item in frame.f_locals.iteritems())))

                print "%s:%s:%s: %s" % (name,  lineno,frame.f_code.co_name , line.rstrip())
                #print frame.f_locals
            self.lastframe = frame.f_back
        return self.traceit


def main():
    print "In main"
    for i in range(5):
        print i, random.randrange(0, 10)
    print "Done."

sys.settrace(Tracer().traceit)
main()

答案 2 :(得分:1)

web.py有一个名为“upvars”的方法做了类似的事情,从调用框架中获取变量。请注意评论:

def upvars(level=2):
    """Guido van Rossum sez: don't use this function."""
    return dictadd(
      sys._getframe(level).f_globals,
      sys._getframe(level).f_locals)

答案 3 :(得分:0)

在跟踪中比在执行时转储所有变量状态对我更有用的是对每行代码进行评估,即:

for modname in modnames:                    | for init in ., init, encoding
                                            |
    if not modname or '.' in modname:       | if not init or '.' in init
         continue                           | continue
                                            |
    try:                                    |

ie:其中实线代码位于左侧,每行代码位于右侧。我在perl中实现了这个,它就是一个LIFESAVER。我正在尝试在python中实现它,但我对语言并不熟悉,因此需要一些时间。

无论如何,如果有人有想法如何实现这一点,我很乐意听到他们。据我所知,它归结为这个功能

 interpolate_frame(frame, string)

传递给跟踪函数的帧在哪里,字符串是要用当前帧中的变量插值的代码行。然后,上面的代码变为:

 print "%s:%s:%s: %s|%s" % (name,  lineno,frame.f_code.co_name, 
     padded(line.rstrip(),10),padded(interpolate_frame(frame, line.rstrip()),100)

我将尝试破解这一点,但是,如果有人对此有任何想法我欢迎听到他们。