我的Python进程在Ubuntu系统上某个点开始占用大量CPU,这是自动化脚本。 我正在尝试在GDB中调试此问题,但我对GDB还不太熟悉。有没有GDB命令可以提供关于哪个线程使用了大部分CPU的信息。查看线程堆栈并不能真正揭示出来。
在Windows Windbg世界中,命令'! runaway'可以提供进程中每个线程消耗的时间信息。我们这里有类似的命令吗?还有其他调试问题的建议吗?
我的Python进程在Ubuntu系统上某个点开始占用大量CPU,这是自动化脚本。 我正在尝试在GDB中调试此问题,但我对GDB还不太熟悉。有没有GDB命令可以提供关于哪个线程使用了大部分CPU的信息。查看线程堆栈并不能真正揭示出来。
在Windows Windbg世界中,命令'! runaway'可以提供进程中每个线程消耗的时间信息。我们这里有类似的命令吗?还有其他调试问题的建议吗?
为了澄清诊断此问题所需的所有步骤(感谢大家的帖子):
以下命令显示进程列表及其CPU /内存使用情况:
$ ps auxf
下面的命令可以列出一个进程的所有线程,并按 CPU 使用率排序:
$ top -H -p [PID]
*PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND*
**1654** root 20 0 1416m 1.2g 24m t **100** 36.8 21:26.23 python
1687 root 20 0 1416m 1.2g 24m t 0 36.8 0:05.07 python
线程1654正在消耗CPU资源。请使用gdb
附加到该进程:
$ gdb /path/of/executable [pid]
使用以下命令在 gdb
中获取线程列表:
(gdb) info threads
2 Thread 0xa7bffb40 (LWP 20736) "python" 0xb7736424 in __kernel_vsyscall ()
1 Thread 0xb73a56c0 (LWP **1654**) "python" 0xb7736424 in __kernel_vsyscall ()
gdb
中切换线程以检查其堆栈:(gdb) thread 1
(gdb) bt
gdb /path/of/process [pid]
- nngeek/bin/python
。 - user202729ps -eLf
命令,它会在第四列中打印出 LWP
,并显示它所消耗的时间以及更多的列。 - Linasthread find 1654
让gdb为您完成工作,而不是使用info threads
。 - hackerb9top -H -p PID
更好。 -p:仅监视具有给定进程ID的进程。此标志最多可以给出二十次。
- user184968$ top -H
PID | USER | PR | NI | VIRT | RES | SHR | S | %CPU | %MEM | TIME+ | COMMAND |
---|---|---|---|---|---|---|---|---|---|---|---|
1654 | root | 20 | 0 | 1416m | 1.2g | 24m | R | 100 | 36.8 | 21:26.23 | python |
1687 | root | 20 | 0 | 1416m | 1.2g | 24m | S | 0 | 36.8 | 0:05.07 | python |
q
$ gdb -p 1654
(gdb) bt
运行top -H
以显示在您的计算机上消耗最多CPU的线程。(如果没有-H,top通常会为每组线程显示一个PID。)如果您有一个失控的线程,您将在列表顶部看到它。请注意其PID.¹
*PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND*
**1654** root 20 0 1416m 1.2g 24m R **100** 36.8 21:26.23 python
1687 root 20 0 1416m 1.2g 24m S 0 36.8 0:05.07 python
按下 q
键退出 top 命令。(或者,您可以打开第二个终端窗口。)
使用 -p 选项运行 gdb,并告诉它要附加到您想诊断的线程的进程ID(PID)。您将会得到一个 (gdb) 提示符。
$ gdb -p 1654
(gdb)
在(gdb)提示符下,键入bt
以查看回溯信息。最有用的gdb命令,用于检查调用堆栈和变量的是:up
,down
,list
和p
。(我还强烈建议使用set print pretty on
,使得p
的输出更易读。)
(gdb) bt
#0 0x000000000057279a in std::_Hashtable<terminal::renderer::ImageFragmentKey, std::pair<terminal::renderer::ImageFragmentKey const, terminal::renderer::ImageRenderer::Metadata>, std::allocator<std::pair<terminal::renderer::ImageFragmentKey const, terminal::renderer::ImageRenderer::Metadata> >, std::__detail::_Select1st, std::equal_to<terminal::renderer::ImageFragmentKey>, std::hash<terminal::renderer::ImageFragmentKey>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::_M_find_before_node(unsigned long, terminal::renderer::ImageFragmentKey const&, unsigned long) const
(this=this@entry=0x1c11780, __bkt=__bkt@entry=69247, __k=..., __code=11352315644114232719) at /usr/include/c++/10/bits/hashtable.h:1577
#1 0x0000000000573638 in std::_Hashtable<terminal::renderer::ImageFragmentKey, std::pair<terminal::renderer::ImageFragmentKey const, terminal::renderer::ImageRenderer::Metadata>, std::allocator<std::pair<terminal::renderer::ImageFragmentKey const, terminal::renderer::ImageRenderer::Metadata> >, std::__detail::_Select1st, std::equal_to<terminal::renderer::ImageFragmentKey>, std::hash<terminal::renderer::ImageFragmentKey>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::_M_find_node(unsigned long, terminal::renderer::ImageFragmentKey const&, unsigned long) const
(__c=11352315644114232719, __key=..., __bkt=69247, this=0x1c11780) at /usr/include/c++/10/bits/hashtable.h:693
#2 std::_Hashtable<terminal::renderer::ImageFragmentKey, std::pair<terminal::renderer::ImageFragmentKey const, terminal::renderer::ImageRenderer::Metadata>, std::allocator<std::pair<terminal::renderer::ImageFragmentKey const, terminal::renderer::ImageRenderer::Metadata> >, std::__detail::_Select1st, std::equal_to<terminal::renderer::ImageFragmentKey>, std::hash<terminal::renderer::ImageFragmentKey>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::find(terminal::renderer::ImageFragmentKey const&) (__k=..., this=0x1c11780) at /usr/include/c++/10/bits/hashtable.h:1454
#3 std::unordered_map<terminal::renderer::ImageFragmentKey, terminal::renderer::ImageRenderer::Metadata, std::hash<terminal::renderer::ImageFragmentKey>, std::equal_to<terminal::renderer::ImageFragmentKey>, std::allocator<std::pair<terminal::renderer::ImageFragmentKey const, terminal::renderer::ImageRenderer::Metadata> > >::find(terminal::renderer::ImageFragmentKey const&) (__x=..., this=0x1c11780) at /usr/include/c++/10/bits/unordered_map.h:920
...
如果您按照我在这里建议的方法使用gdb -p
连接到单个线程,gdb将表现得好像只有一个线程。如果您希望使用thread
命令切换要调试的线程,则必须连接到主进程的PID。
为什么我说“PID”(进程ID)而不是“TID”(线程ID)?因为在Linux中,线程只是轻量级进程,并且在内核中使用与进程相同的内部结构。每个新创建的线程都有一个不同于父进程PID的PID。为了将它们逻辑上分组为单个“进程”,每个线程都有另一个字段称为TGID(线程组ID),它记住了父进程的PID。