如何调试永不停止运行的代码?

时间:2013-01-30 15:41:29

标签: python python-2.7

我有一个庞大的代码库,现在需要花费很长时间才能执行。我不知道是什么。

代码永远不会引发异常,它似乎继续处理某些事情。

我想做的是在某些功能周围放置计时器来测试哪一个是罪魁祸首。但我不确定这是否是正确的方法,或者如何做到这一点。

我不能轻易地在代码周围的点处引发异常,因为有各种循环调用相同的函数,这可能有时只需要太长时间。

最佳策略是什么?

3 个答案:

答案 0 :(得分:8)

一种解决方案是使用内置于Python中的cProfile来说明您的代码花费最多时间的函数。重要的是,即使您使用<停止代码,此分析也会起作用 / em> KeyboardInterrupt。因此,您可以开始运行和分析代码,在一两分钟后停止它,然后查看它花费的时间。

使用这些额外的-m-o参数运行代码:

python -m cProfile -o profile.txt myscript.py

然后程序运行完毕后,运行以下代码(例如,从另一个脚本):

import pstats
p = pstats.Stats('profile.txt')
p.strip_dirs().sort_stats("time").print_stats()

这将打印按照您在其中花费的总时间排序的功能列表。

这是使用分析来调试无限循环的演示。假设myscript.py有以下代码。

def f():
    while True:
        g(100000)

def g(n):
    x = []
    for i in range(n):
        x.append(n)

f()

当然这会导致无限循环运行很多次。所以我运行上面的profiling命令,但是我在大约30-40秒后停止它(甚至可以短得多)。其简介将打印为:

Wed Jan 30 10:58:50 2013    profile.txt

         115414854 function calls in 37.705 seconds

   Ordered by: internal time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     1155   25.787    0.022   37.020    0.032 test3.py:5(g)
115412541   10.060    0.000   10.060    0.000 {method 'append' of 'list' objects}
     1155    1.173    0.001    1.173    0.001 {range}
        1    0.685    0.685   37.705   37.705 test3.py:1(f)
        1    0.000    0.000   37.705   37.705 test3.py:1(<module>)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

请注意,我们的无限循环函数g就函数所花费的时间而言位于列表的顶部。

注意:仅仅因为代码在一个函数中花费所有时间并不意味着循环直接围绕该函数 - 它可以由函数调用的函数调用(等)处于无限循环中(注意append靠近列表顶部,因为它在g内调用。另一种方法是使用.sort_stats("cum")根据每个函数的累计时间对它们进行排序。这两种方法的结合,以及一些小的侦探工作(查看代码和添加调试消息),应该能够找出罪魁祸首。

答案 1 :(得分:1)

我建议使用logging module将调试语句添加到代码中的各个点。使用日志记录模块,您可以轻松打开和关闭调试语句,以及控制显示的信息。

另一种选择是使用IDE,您可以在其中轻松添加断点,从而可以确定代码进入长循环的位置。

答案 2 :(得分:1)

[编辑:我看到您添加了python-2.7标记,因此这不会对您有所帮助,但对其他人可能有用或有趣。]

在Python 3中,您可以通过键入 Control-C 进入调试器。然后从调试器中检查堆栈以查看程序花​​费时间的位置。

Python 3.3.0 (default, Nov 23 2012, 10:26:01) 
>>> import time, pdb
>>> def foo(): time.sleep(1); foo()
... 
>>> pdb.run('foo()')
> <string>(1)<module>()
(Pdb) c
^C
Program interrupted. (Use 'cont' to resume).
--Call--
> <stdin>(1)foo()
(Pdb) w
  <stdin>(1)<module>()
  /.../pdb.py(1556)run()
-> Pdb().run(statement, globals, locals)
  /.../bdb.py(405)run()
-> exec(cmd, globals, locals)
  <string>(1)<module>()
  <stdin>(1)foo()
  <stdin>(1)foo()
  <stdin>(1)foo()
  <stdin>(1)foo()
  <stdin>(1)foo()
> <stdin>(1)foo()

您可以将此技术视为一种基于样本的分析,只需一个样本!对于许多问题,您只需要一个样本。