Python中的事后调试允许单步执行或继续执行吗?

11

我一直在尝试进行事后调试(Post-mortem debugging),但遇到了一些问题。考虑下面这个名为example.py的Python脚本:

k = 0
print 1. / k
print 'continue ...'

我可以用以下方式运行:

> python -m pdb example.py

然后进入第二行 print 1. / k,然后设置 k = 1 并继续使用 pdb 命令 c

现在如果我使用事后调试来执行程序,则无法继续程序执行。 我使用以下命令运行:

> python -i example.py

然后在我被放入shell后,我执行:

import pdb
pdb.pm()

我仍然可以像以前一样更改k的值,但无法继续任何程序执行。Pdb只是简单地退出。

我无法找到任何明确说明您不能在事后逐步执行程序的地方。这似乎是事实。那么我想了解事后调试的价值。它的唯一价值是检查错误发生时代码的状态吗?


有像 python -m pdb_set_trace example.py 或者 python -m ipdb -c breakpoint example.py 这样的东西会很棒。 - fariaseduv
1个回答

15

当抛出异常时,会调用后期处理。

此时,堆栈不再“活动”,您不能再逐步执行代码。毕竟,异常刚刚被抛出,表明代码路径无法继续。例如,如果您有表达式result = 1. / k,您会期望result是什么?

字面上的Post-Mortem用于在患者死亡(拉丁文中的post mortem)后查看死因。pdb post-mortem也是如此。您可以看到程序在死亡时的状态,但不能使死者复活。

换句话说,后期处理的目的是通过详细检查程序在失败时的状态来了解程序为什么失败。

这个术语在pdb手册中没有明确记录,可能是因为“Post Mortem”这个术语被认为是常用的。调试的维基百科文章顺便提到了它,Python并不是唯一提供这种技术的语言;Windows调试器也提供了这个功能,R也是如此,其他语言也有类似的功能。
如果你希望从回溯中“重新激活”尸体,知道这里有很大的障碍需要克服。虽然异常的回溯确实包含了堆栈中每个帧的全局和本地命名空间的引用,但是活动的堆栈包含了更多未包含在回溯中的状态,这些状态是无法被重建的。这包括嵌套函数所附加的闭包单元(特别是父函数不是回溯的一部分时),或者处理try..excepttry..finallywith块所需的内部记录,以及由活动的forasync for循环使用的迭代器对象。
对于那些希望有所不同的人,我说:程序已经过去了!这个程序不存在了!它停止了!它已经到达了它的制造者!它死了!失去了生命,它安息在和平中!如果你没有把它钉在回溯上,它就会变成一堆柿子!它的评估循环簿记现在是历史!它被淘汰了!它已经踢了桶,离开了这个世界,跑下了幕布,加入了无形的合唱团!这是一个已经消失的程序。(向约翰·克里斯和迈克尔·佩林致歉)。

另外一点需要添加的是:不仅"需要处理try..excepttry..finallywith块的内部簿记"已经消失,而且在post mortem调试器调用和引发异常之间的堆栈上所有适用的异常处理块、finally块和上下文管理器__exit__方法都已经执行,可能释放了任何资源、关闭了任何文件或释放了任何锁,这些资源对于块的正确执行是必要的。因此,不仅不能让死者复活,而且在调试器中调用的表达式有时候也不能正常工作! - LeoRochael

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接