当用户报告你的程序崩溃了或者僵死了,有时你只能试着搜集有用信息,概括当时 的场景,以便能够重现错误。即使有了一个可靠的用户场景,作为开发者的你经常会 无法重现错误,因为你们的环境相差很大,比如操作系统和编译器。当用户安装了调试 工具时你会幸运一点,但大部分情况下你只能等待其他人能够从这相同的情形下获得 更多的有用信息。
致命错误
Python 3.3 中新引入的模块可以帮助处理这个问题: faulthandler. faulthandler 在Python碰到类似于段错误,除0, 中止程序和总线故障等的致命错误(fatal error) 时 能dump 出 trackback。 你可以通过在你的应用程序中加入 faulthandler.enable(), 或者调用Python 可执行程序时提供 -X faulthandler 参数,或者设置 PYTHONFAULTHANDLER=1 环境变量来启用该功能。 输出类似如下
Fatal Python error: Segmentation fault Current thread 0x00007f7babc6b700: File "Lib/test/crashers/gc_inspection.py", line 29 in g File "Lib/test/crashers/gc_inspection.py", line 32 in <module> Segmentation fault
超时
通过调用 faulthandler.dump_tracebacks_later(timeout), faulthandler 能 在超时时dump出traceback。再次调用该函数可以重启计时器,或者调用 faulthandler.cancel_dump_tracebacks_later() 来关闭计时器。 输出类似如下:
Timeout (0:01:00)! Current thread 0x00007f987d459700: File "Lib/test/crashers/infinite_loop_re.py", line 20 in <module>
使用 repeat=True 选项可以在每 timeout 秒后dump一次traceback, 或者用 exit=True 选项使程序立即不安全(不会把文件全部写入磁盘)退出。
用户信号
如果你有程序运行所在的主机的访问权限,你可以使用 faulthandler.register(signal) 来注册一个处理信号句柄, 当 single 信号收到时,可以dump 出 traceback。 在Unix系统上,你可以使用 SIGUSER1 信号: kill -USER1 <pid> 将把当前的 traceback 给dump 出来。这个特性在Windows系统上时没有的。 输出类似如下:
Current thread 0x00007fdc3da74700: File "Lib/test/crashers/infinite_loop_re.py", line 19 in <module>
另外一种方式是在你的程序中显式调用 faulthandler.dump_traceback() .
安全问题 以及 输出文件
由于安全因素 faulthandler 默认时关闭的, 主要是因为它保存了 sys.stderr 的文件描述符, 并把trackback写入那个文件描述符。如果sys.stderr 被关闭了, 但这个文件描述符被重用了,那这个文件描述符可能是个socket,管道,一个关键文件或者 其他什么。默认情况下,faulthandler 把traceback 写到 sys.stderr 中, 但你可以指定其他文件。 更多相关的信息可以查阅 faulthandler 文档.
旧版Python的第三方模块
在 PyPI 上``faulthandler`` 作为Python 2.5版本到 3.2版本的第三方模块维护。 Python 3.3 标准模块和第三方模块的主要区别时dump_tracebacks_later 的实现方式: Python 3.3 使用线程的锁超时,而第三方库使用了 SIGALRM 和 alarm() 。
作为Python 3.3的新特性, 锁超时的精度是1微妙。旧版Python上的 alarm() 的精度时1秒, SIGALRM 信号可能会中断当前的系统调用, 使系统以 EINTR 错误 失败。
早已取得成功
faulthandler 新模块已经在我们的buildbot项目中帮助我们跟踪竞争状态。我们希望 它也能在你的程序中帮助你。