当尝试按照this SO answer的建议反汇编程序的每条指令时,我发现gdb
在将指令步进到gettimeofday
后永远不会完成处理。下面是一个最小可重复的示例:
在main.c
中:
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
int main()
{
struct timeval tv;
gettimeofday(&tv, NULL);
return 0;
}
字符串
使用gcc main.c
编译
运行:gdb -silent a.out
Reading symbols from a.out...
(gdb) set height 0
(gdb) b main
Breakpoint 1 at 0x1175: file main.c, line 7.
(gdb) set logging file ./log.txt
(gdb) set logging redirect on
(gdb) set logging on
Redirecting output to ./log.txt.
Copying debug output to ./log.txt.
(gdb) r
(gdb) while 1
>si
>end
型log.txt
显示:
Starting program: /home/user/tst-gdb-step/a.out
Breakpoint 1, main () at main.c:7
7 {
0x000055555555517e 7 {
0x0000555555555182 7 {
10 gettimeofday(&tv, NULL);
0x0000555555555188 10 gettimeofday(&tv, NULL);
0x000055555555518d 10 gettimeofday(&tv, NULL);
0x0000555555555190 10 gettimeofday(&tv, NULL);
0x0000555555555070 in gettimeofday@plt ()
0x0000555555555074 in gettimeofday@plt ()
0x00007ffff7fcd690 in gettimeofday ()
0x00007ffff7fcd691 in gettimeofday ()
0x00007ffff7fcd698 in gettimeofday ()
0x00007ffff7fcd69b in gettimeofday ()
0x00007ffff7fcd69d in gettimeofday ()
0x00007ffff7fcd69e in gettimeofday ()
0x00007ffff7fcd6a1 in gettimeofday ()
0x00007ffff7fcd6a7 in gettimeofday ()
0x00007ffff7fcd6aa in gettimeofday ()
0x00007ffff7fcd6ae in gettimeofday ()
0x00007ffff7fcd6b4 in gettimeofday ()
0x00007ffff7fcd6ba in gettimeofday ()
0x00007ffff7fcd6bd in gettimeofday ()
0x00007ffff7fcd6c3 in gettimeofday ()
0x00007ffff7fcd6c6 in gettimeofday ()
0x00007ffff7fcd6c8 in gettimeofday ()
0x00007ffff7fcd6cc in gettimeofday ()
0x00007ffff7fcd6cf in gettimeofday ()
0x00007ffff7fcd6d2 in gettimeofday ()
0x00007ffff7fcd6d8 in gettimeofday ()
0x00007ffff7fcd6df in gettimeofday ()
0x00007ffff7fcd6e6 in gettimeofday ()
0x00007ffff7fcd6ed in gettimeofday ()
0x00007ffff7fcd6f0 in gettimeofday ()
0x00007ffff7fcd6f2 in gettimeofday ()
0x00007ffff7fcd6f5 in gettimeofday ()
0x00007ffff7fcd6f9 in gettimeofday ()
0x00007ffff7fcd6fc in gettimeofday ()
0x00007ffff7fcd702 in gettimeofday ()
0x00007ffff7fcd709 in gettimeofday ()
0x00007ffff7fcd70c in gettimeofday ()
0x00007ffff7fcd70f in gettimeofday ()
0x00007ffff7fcd6a7 in gettimeofday ()
0x00007ffff7fcd6aa in gettimeofday ()
0x00007ffff7fcd6ae in gettimeofday ()
0x00007ffff7fcd6b4 in gettimeofday ()
0x00007ffff7fcd6ba in gettimeofday ()
0x00007ffff7fcd6bd in gettimeofday ()
0x00007ffff7fcd6c3 in gettimeofday ()
0x00007ffff7fcd6c6 in gettimeofday ()
0x00007ffff7fcd6c8 in gettimeofday ()
0x00007ffff7fcd6cc in gettimeofday ()
0x00007ffff7fcd6cf in gettimeofday ()
0x00007ffff7fcd6d2 in gettimeofday ()
0x00007ffff7fcd6d8 in gettimeofday ()
0x00007ffff7fcd6df in gettimeofday ()
...
型
在执行了整整3分钟后,gdb
向log.txt
写入了近500,000行(并且没有任何停止的标志),这显然不是正常情况,因为vdso
的速度很快。日志还显示无限循环。
但是如果使用n
而不是si
,则程序可以退出,没有任何问题。
我使用的工具:
$ uname -a
Linux 5.15.0-69-generic #76~20.04.1-Ubuntu SMP Mon Mar 20 15:54:19 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux
$ gcc --version
gcc (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0
$ gdb --version
GNU gdb (Ubuntu 10.2-0ubuntu1~20.04~1) 10.2
型
我想知道为什么会这样。谢谢你,谢谢
2条答案
按热度按时间hwazgwia1#
这是一个基于源潜水的猜测:我相信单步执行
gettimeofday
的核心会大大减慢这些核心的速度,以至于它们陷入了一个无休止的循环,试图满足结果的内部精度要求。x86-64/Linux上的
gettimeofday
代码位于linux/lib/vdso/gettimeofday.c
中。我所说的“guts”是do_hres
函数。(我已经链接到内核5.15.0,因为这就是你所拥有的,但是这个文件并不经常改变。)注意这个函数中的循环。在循环内部,它计算时间,本质上是(由内核维护的粗略计数器)+(经常溢出的硬件高精度时钟)。每次内核更新粗略计数器时,它也会更新在循环条件中读取的“序列号”。设计者希望这个循环最多循环两次--关键是,如果你在错误的时刻进入循环,并读取了关于粗略计数器的 * 不一致 * 数据,从而计算出一个无意义的时间,你会重试,第二次就能正确。但是,即使是以自动化的方式,一个指令一个指令地执行这个代码指令,也会使它变得如此缓慢,以至于内核总是在循环体执行时更改粗略计数器和序列号,因此您会陷入循环中。
(Do不要低估与正常执行相比,一条指令一条指令地执行机器代码的速度有多慢。您在 * 每条指令 * 中添加了两个上下文切换和几个命令的时间开销,这些命令使用的是GDB速度并不特别快的脚本语言。如果上下文切换和随之而来的缓存和TLB颠簸足以触发这个活锁,我不会感到惊讶。)
fumotvh32#
下面是我用来绕过这个问题的gdb脚本:
字符串
这并不适用于所有情况(例如,在与时间相关的事件上进行分支),并且显然很慢,但它确实适合我。