简体   繁体   English

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

[英]How to debug code that never stops running?

I have a large code base, and something is now taking too long to execute. 我有一个庞大的代码库,现在执行的东西太长了。 I have no idea what. 我不知道是什么。

The code never raises an exception, it just appears to keep on processing something. 代码永远不会引发异常,它似乎只是继续处理某些东西。

What I'd like to do is place timers around some functions to test which one is the culprit. 我想做的是在一些函数周围放置计时器来测试哪一个是罪魁祸首。 But I'm not sure if that is the right approach, or how to do it. 但我不确定这是否是正确的方法,或者如何做到这一点。

I can't easily raise exceptions at points around the code, because there are various loops that call the same functions, that perhaps only sometimes take too long. 我不能轻易地在代码周围的点处引发异常,因为有各种循环调用相同的函数,这可能有时只需要太长时间。

What is the best strategy? 什么是最好的策略?

One solution is to use cProfile , which comes built into Python, to tell what functions your code is spending the most time in. Critically, this profiling works even if you stop your code with a KeyboardInterrupt . 一种解决方案是使用内置于Python中的cProfile来说明代码花费最多时间的函数。重要的是, 即使您使用 KeyboardInterrupt 停止代码 ,此分析仍然有效 Thus, you can start your code running and profiling, stop it after a minute or two, and then see where it was spending its time. 因此,您可以启动代码运行和分析,在一两分钟后停止它,然后查看它花费时间的位置。

Run your code with these extra -m and -o arguments: 使用这些额外的-m-o参数运行代码:

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

and then once the program has finished running, run the following code (from another script, for example): 然后一旦程序运行完毕,运行以下代码(例如,从另一个脚本):

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

This will print a list of the functions sorted by the total time you spent in them. 这将打印按照您在其中花费的总时间排序的函数列表。

Here's a demonstration of using profiling to debug an infinite loop. 这是使用分析来调试无限循环的演示。 Let's say myscript.py had the following code. 假设myscript.py有以下代码。

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

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

f()

Of course this causes an infinite loop- g will be run many, many times. 当然,这会导致无限循环运行很多次。 So I run the profiling command above, but then I stop it after about 30-40 seconds (could be much shorter even). 所以我运行上面的profiling命令,但是我在大约30-40秒后停止它(甚至可以短得多)。 Its profile will then be printed as: 其简介将打印为:

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}

Notice that our infinitely looping function g is right at the top of the list in terms of time spent in the function. 请注意,我们的无限循环函数g就在函数中花费的时间而言位于列表的顶部。

Note : Just because the code is spending all its time in one function doesn't mean that the loop is directly around that function- it could be called by a function that is called by a function (etc) that is in an infinite loop (notice that append is near the top of the list since it is called inside g ). 注意 :仅仅因为代码在一个函数中花费所有时间并不意味着循环直接围绕该函数 - 它可以被一个由无限循环中的函数(etc)调用的函数调用(请注意, append位于列表顶部附近,因为它在g )内部被调用。 One alternative trick is to sort them according to the cumulative time spent in each function, using .sort_stats("cum") . 另一种方法是使用.sort_stats("cum")根据每个函数的累计时间对它们进行排序。 A combination of these two approaches, along with a little detective work (looking at the code and adding debug messages), should be able to identify the culprit. 这两种方法的结合,以及一些小的侦探工作(查看代码和添加调试消息)应该能够找出罪魁祸首。

I would suggest using the logging module to add debug statements to various points in the code. 我建议使用日志记录模块将调试语句添加到代码中的各个点。 Using the logging module allows you to easily turn on and off the debugging statements as well as control what information is displayed. 使用日志记录模块可以轻松打开和关闭调试语句,以及控制显示的信息。

Another option is to use an IDE where you can easily add break points, allowing you to figure out where the code goes into the long loop. 另一种选择是使用IDE,您可以在其中轻松添加断点,从而可以确定代码进入长循环的位置。

[Edit: I see you added the python-2.7 tag, so this isn't going to help you, but it might be useful or interesting to others.] [编辑:我看到你添加了python-2.7标签,所以这对你没有帮助,但对其他人来说可能有用或有趣。]

In Python 3, you can break into the debugger by typing Control-C . 在Python 3中,您可以通过键入Control-C进入调试器。 Then from the debugger you can examine the stack to see where the program is taking its time. 然后从调试器中检查堆栈以查看程序花​​费时间的位置。

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()

You can think of this technique as a kind of sample-based profiling with a single sample! 您可以将此技术视为一种基于样本的分析,只需一个样本! For many problems, a single sample is all you need. 对于许多问题,您只需要一个样品。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM