如何在pdb中观察变量
我正在调试一个Python脚本,想要监视一个变量的变化(就像在gdb中可以监视内存地址一样)。有没有办法做到这一点?
6 个回答
这里有一种很简单的方法可以用来实现这个功能,使用的是 pdb
。你可以把这些命令放到你的 ~/.pdbrc
文件里,这样每次使用 pdb
时就会自动加载。
!global __currentframe, __stack; from inspect import currentframe as __currentframe, stack as __stack
!global __copy; from copy import copy as __copy
!global __Pdb; from pdb import Pdb as __Pdb
!global __pdb; __pdb = [__framerec[0].f_locals.get("pdb") or __framerec[0].f_locals.get("self") for __framerec in __stack() if (__framerec[0].f_locals.get("pdb") or __framerec[0].f_locals.get("self")).__class__ == __Pdb][-1]
alias _setup_watchpoint !global __key, __dict, __val; __key = '%1'; __dict = __currentframe().f_locals if __currentframe().f_locals.has_key(__key) else __currentframe().f_globals; __val = __copy(%1)
alias _nextwatch_internal next;; !if __dict[__key] == __val: __pdb.cmdqueue.append("_nextwatch_internal %1")
alias _stepwatch_internal step;; !if __dict[__key] == __val: __pdb.cmdqueue.append("_stepwatch_internal %1")
alias nextwatch __pdb.cmdqueue.extend(["_setup_watchpoint %1", "_nextwatch_internal"])
alias stepwatch __pdb.cmdqueue.extend(["_setup_watchpoint %1", "_stepwatch_internal"])
这个方法添加了两个命令,分别是 nextwatch
和 stepwatch
,它们都需要一个变量名 varname 作为参数。如果可能的话,这两个命令会对当前的局部变量 varname 进行一个浅拷贝,然后分别继续执行 next
或 step
,直到这个变量指向的内容发生变化。
这个方法在 CPython 2.7.2 中有效,但它依赖于一些 pdb
的内部实现,所以在其他地方可能会出现问题。
对于Python 3:
你可以使用display这个功能来帮助调试。
当你到达断点时,只需要输入:
ipdb> display 表达式
举个例子:
ipdb> display instance
display instance: <AppUser: dmitry4>
ipdb> display instance.id
display instance.id: 9
ipdb> display instance.university
display instance.university: <University: @domain.com>
ipdb> display
Currently displaying:
instance.university: <University: @domain.com>
instance.id: 9
instance: <AppUser: dmitry4>
ipdb>
如你所见,每次输入display时,它都会打印出你设置的所有监视变量(表达式)。你可以使用内置的undisplay
函数来移除某个监视变量。
你还可以使用pp 表达式来美化输出的表达式(这个功能非常有用)。
使用PDB进行数据断点
...就像你可以在gdb中监视一个内存地址一样...
- GDB支持数据断点,这得益于硬件的支持(硬件监视点)。这通常涉及将内存页面标记为只读,这样在访问内存时就会触发异常处理程序。当没有硬件监视点时,它会使用软件监视点,这种方法只适用于单线程,并且速度较慢。
- PDB不支持数据断点,所以简单来说,不可以,PDB无法直接做到这一点。
在PDB中遇到断点时打印变量
当你在遇到断点时想要查看一个变量,可以使用commands
命令。例如,在遇到断点#1时打印some_variable
(这是来自pdb
文档的经典示例)。
(Pdb) commands 1
(com) print(some_variable)
(com) end
(Pdb)
另外,你可以使用condition
命令,确保只有在变量取某个特定值时才会触发断点。
例如:
(Pdb) condition 1 some_variable==some_value
其他解决方案
你可以使用跟踪/分析功能,通过sys.settrace
逐步检查代码,并查看正在执行的操作码。
这里有一些代码可以帮助你入门:
import sys
import dis
def tracefn(frame, event, arg):
if event == 'call':
print("## CALL", frame)
frame.f_trace_opcodes = True
elif event == 'opcode':
opcode = frame.f_code.co_code[frame.f_lasti]
opname = dis.opname[opcode]
print("## OPCODE", opname)
return tracefn
watchme = 123
def foo():
global watchme
watchme = 122
sys.settrace(tracefn)
foo()
你可能需要监视所有的STORE_*
操作码。https://docs.python.org/3/library/dis.html