我怎么知道我的python脚本挂在哪里?

Joh*_*nny 47 python debugging

所以我正在调试我的python程序并遇到一个使程序挂起的错误,就好像在无限循环中一样.现在,我之前遇到了无限循环的问题,但是当它挂断时我可以杀死程序并且python吐出一个有用的异常,告诉我当我发送kill命令时程序终止的位置.然而,现在,当程序挂断并且我按下ctrl-c时,它不会中止但会继续运行.我有什么工具可以用来找到挂机吗?我不熟悉分析,但据我所知,分析器只能为您提供有关已成功完成的程序的信息.或者您可以使用分析器来调试此类挂起吗?

dhr*_*ird 93

我们假设您运行的程序如下:

python YOURSCRIPT.py
Run Code Online (Sandbox Code Playgroud)

尝试运行您的程序:

python -m trace --trace YOURSCRIPT.py
Run Code Online (Sandbox Code Playgroud)

屏幕上印有很多东西,并有一些耐心.如果你有一个无限循环,它会一直持续(停止问题).如果它被卡在某处,那么大多数时候你会陷入I/O或者它是一个僵局.

  • +1.您可以使用--ignore-dir或--ignore-module选项来减少输出量,例如停止跟踪所有标准模块.您还可以将输出重定向到文件以供以后检查. (13认同)

fiv*_*vef 25

从 Python 3.3 开始,有一个内置的故障处理程序模块。当发生通常致命的信号时打印所有线程的堆栈跟踪:

import faulthandler
faulthandler.enable()
Run Code Online (Sandbox Code Playgroud)

对于挂起的进程,设置故障处理程序以按需打印堆栈跟踪更为有用。这可以通过以下方式完成:

import faulthandler
import signal
faulthandler.register(signal.SIGUSR1.value)
Run Code Online (Sandbox Code Playgroud)

然后,一旦进程挂起,您就可以发送信号来触发堆栈跟踪的打印:

import faulthandler
faulthandler.enable()
Run Code Online (Sandbox Code Playgroud)

该信号不会终止进程,您可以多次发送以查看执行中不同点的堆栈跟踪。

请注意,signal.SIGUSR1 需要 Python 3.5 或更高版本。对于旧版本,您可以只对信号编号进行硬编码(对于最常见的 Linux 架构为 10)。

faulthandler.dump_traceback可以与 一起使用,通过 的十六进制 IDthreading.enumerate来识别必须daemon=False缩小到悬挂线程的线程hex(t.ident)


dka*_*ins 24

哇!已有5个答案,没有人提出最明显和最简单的答案:

  1. 尝试找到导致悬挂行为的可重现的测试用例.
  2. 将记录添加到您的代码中.这可以作为基本print "**010",print "**020"等通过主要领域穿插.
  3. 运行代码.看看它挂在哪里.无法理解为什么?添加更多日志记录 (即如果在**020和**030之间,请添加**023,**025,**027等)
  4. 转到3.

这些天孩子们使用他们的花哨的调试器和IDE ......有时候工程问题最简单的解决方法是提供更多信息的原始工具.

  • 对于简单的脚本,没有复杂性,这是有效的.但是,对于长时间运行的复杂程序,这是没用的. (59认同)
  • @xlash对于复杂程序,将"**010"更改为"Starting blaster module"并将"**020"更改为"Connecting to fusion server"等.然后更改"print"以使用"logging"模块.如果你不这样做,那你做错了. (9认同)
  • 不,代码太大了.不能到处添加打印语句.GOT是一个更好的答案 (5认同)
  • +1,这是我经常做的.当然,调试器和IDE也很有用,但我发现当我已经粗略了解要查找源的哪个部分时,这是确定错误位置的最快/最简单的方法.(只是我的意见,当然) (2认同)

Dav*_*rby 9

如果您的程序太大而且太复杂而无法通过pdb单步执行或使用跟踪模块打印每一行,那么您可以尝试从我的8位游戏编程时代开始.从Python 2.5开始,pdb可以使用该commands命令将代码与断点相关联.您可以使用它来打印消息并继续运行:

(Pdb) commands 1
(com) print "*** Breakpoint 1 ***"
(com) continue
(com) end
(Pdb)
Run Code Online (Sandbox Code Playgroud)

这将打印一条消息,并在命中断点1时继续运行.为其他一些断点定义类似的命令.

您可以使用它来对代码进行二进制搜索.在代码中的关键位置附加断点并运行它直到它挂起.您可以从上一条消息中判断出它所遇到的最后一个断点.然后,您可以移动其他断点并重新运行以缩小代码挂起的位置.冲洗并重复.

顺便提一下,在8位微处理器(Commodore 64,Spectrum等)上,你可以将一个值戳到注册表位置,以改变屏幕边框的颜色.我曾经设置了几个断点来用不同的颜色做这个,所以当程序运行时它会给出一个迷幻的彩虹显示,直到它挂起,然后边框会变成一种颜色,告诉你最后一个断点是什么.您还可以通过彩虹中每种颜色的数量来感受不同代码部分的相对性能.有时我会想念这些新奇的"Windows"机器.


Use*_*ser 9

我写了一个模块,打印出在一个地方挂了10秒钟的线程. hanging_threads.py

这是一个示例输出:

--------------------    Thread 5588     --------------------
  File "C:\python33\lib\threading.py", line 844, in _exitfunc
        t.join()
  File "C:\python33\lib\threading.py", line 743, in join
        self._block.wait()
  File "C:\python33\lib\threading.py", line 184, in wait
        waiter.acquire()
Run Code Online (Sandbox Code Playgroud)

当您忘记将另一个线程设置为守护进程时,会在主线程的出口处发生这种情况.

  • 这对我来说非常有效。我建议您在答案中附加 pip install 命令和在代码顶部添加的代码片段以启用该模块。 (3认同)
  • @raacer,它是一个软件包,现在:https://pypi.python.org/pypi/hanging_threads (2认同)

ger*_*rit 9

多线程d\xc3\xa6mon;使用pyrasite检查正在运行的程序

\n

我有一个多线程 d\xc3\xa6mon,有时会在几个小时后卡住,有时会在几周后卡住。通过调试器运行它是不可行的,甚至可能没有帮助,因为调试多线程或多进程程序可能很痛苦。通过跟踪运行它可能会填满千兆字节(如果不是太字节),然后就会卡住。第二次 d\xc3\xa6mon 似乎挂起时,我想立即知道它在哪里,而无需重新启动它、添加检查代码、通过调试器运行它,并等待数小时、数天或数周让它挂起再次,情况尚待调查。

\n

我被Pyrasite救了,它让用户连接到正在运行的 Python 进程并交互式检查框架(受此要点启发的示例):

\n
$ pyrasite-shell 1071  # 1071 is the Process ID (PID)\nPyrasite Shell 2.0\nConnected to \'/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/bin/python3.8 /opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/bin/satpy_launcher.py -n localhost /opt/pytroll/pytroll_inst/config/trollflow2.yaml\'                                                                                               \nPython 3.8.6 | packaged by conda-forge | (default, Dec 26 2020, 05:05:16)\n[GCC 9.3.0] on linux\nType "help", "copyright", "credits" or "license" for more information.\n(DistantInteractiveConsole)\n\n>>> import sys\n>>> sys._current_frames()\n{139652793759488: <frame at 0x7f034b2c9040, file \'<console>\', line 1, code <module>>, 139653520578368: <frame at 0x7f034b232ac0, file \'/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/site-packages/pyresample/spherical.py\', line 112, code __init__>}\n
Run Code Online (Sandbox Code Playgroud)\n

第一帧没有提供任何信息;那是我们自己的硫铁矿壳。然而,第二帧显示当前我们的脚本卡在pyresample.spherical第 112 行的模块中。我们可以使用回溯模块来获取完整的回溯:

\n
>>> import traceback\n>>> traceback.print_stack(list(sys._current_frames().values())[1])\n  File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/bin/satpy_launcher.py", line 80, in <module>\n    main()\n  File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/bin/satpy_launcher.py", line 75, in main\n    run(prod_list, topics=topics, test_message=test_message,\n  File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/site-packages/trollflow2/launcher.py", line 152, in run\n    proc.start()\n  File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/multiprocessing/process.py", line 121, in start\n    self._popen = self._Popen(self)\n  File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/multiprocessing/context.py", line 224, in _Popen\n    return _default_context.get_context().Process._Popen(process_obj)\n  File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/multiprocessing/context.py", line 277, in _Popen\n    return Popen(process_obj)\n  File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/multiprocessing/popen_fork.py", line 19, in __init__\n    self._launch(process_obj)\n  File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/multiprocessing/popen_fork.py", line 75, in _launch\n    code = process_obj._bootstrap(parent_sentinel=child_r)\n  File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/multiprocessing/process.py", line 315, in _bootstrap\n    self.run()\n  File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/multiprocessing/process.py", line 108, in run\n    self._target(*self._args, **self._kwargs)\n  File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/site-packages/trollflow2/launcher.py", line 268, in process\n    cwrk.pop(\'fun\')(job, **cwrk)\n  File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/site-packages/trollflow2/plugins/__init__.py", line 403, in covers\n    cov = get_scene_coverage(platform_name, start_time, end_time,\n  File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/site-packages/trollflow2/plugins/__init__.py", line 425, in get_scene_coverage\n    return 100 * overpass.area_coverage(area_def)\n  File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/site-packages/trollsched/satpass.py", line 242, in area_coverage\n    inter = self.boundary.contour_poly.intersection(area_boundary)\n  File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/site-packages/pyresample/spherical.py", line 494, in intersection\n    return self._bool_oper(other, -1)\n  File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/site-packages/pyresample/spherical.py", line 475, in _bool_oper\n    inter, edge2 = edge1.get_next_intersection(narcs2, inter)\n  File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/site-packages/pyresample/spherical.py", line 326, in get_next_intersection\n    return None, None\n  File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/site-packages/pyresample/spherical.py", line 298, in intersection\n    return None\n  File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/site-packages/pyresample/spherical.py", line 264, in intersections\n    return (SCoordinate(lon, lat),\n  File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/site-packages/pyresample/spherical.py", line 62, in cross2cart\n    return res\n  File "/opt/pytroll/pytroll_inst/miniconda3/envs/pytroll-py38/lib/python3.8/site-packages/pyresample/spherical.py", line 112, in __init__\n    self.cart = np.array(cart)\n
Run Code Online (Sandbox Code Playgroud)\n

我们可以使用 Python 内省的所有功能来检查堆栈,以帮助我们重建陷入困境的情况。

\n


Dai*_*ood 5

您也可以尝试http://code.activestate.com/recipes/576515-debugging-a-running-python-process-by-interrupting/。只要 Python 进程没有屏蔽信号,它就应该可以工作,即使 Ctrl-C 不起作用,通常也是这种情况。


Mat*_*yra 0

我自己没有使用过,但我听说Eric IDE很好并且有一个很好的调试器。这也是我所知道的唯一具有 Python 调试器的 IDE