GDB 和 LLDB 都无法在简单的 C 文件中可靠地执行断点命令

Posted

技术标签:

【中文标题】GDB 和 LLDB 都无法在简单的 C 文件中可靠地执行断点命令【英文标题】:Both GDB and LLDB failing to reliably execute breakpoint commands in simple C file 【发布时间】:2017-12-16 01:32:00 【问题描述】:

作为研究项目的一部分,我正在尝试编写一个 gdb 命令文件,该文件输出任意 C 源文件中每一行代码的某些信息,直到程序终止。这似乎很容易通过一个while循环来完成,在循环中输出我想要的任何数据,然后在循环结束时调用“next”。 (我知道我希望“step”进入函数调用;我现在并不担心。)

但是,除了我在每一行输出的数据之外,我还想在某些断点处执行特殊命令。这似乎很容易通过“命令”完成。但是,我遇到了 while 循环和断点命令不能同时工作的问题。

这是我正在使用的极其简单的 C 文件,用于测试目的:

int global;

int main() 
  int x;
  x=-1;
  global = 5;
  return(0);

我用gcc -g -o simple simple.c 编译它。然后我运行gdb -x commands.txt。如果 commands.txt 的内容如下:

set confirm off

exec-file simple
file simple

set logging file gdb_output.txt
set logging on
set pagination off

#Special commands I want to execute on certain breakpoints
break 5
command
  echo COMMAND 1 ACTIVATED\n
end

break 6
command
  echo COMMAND 2 ACTIVATED\n
end

break 7
command
  echo COMMAND 3 ACTIVATED\n
end

run

next
next
next
continue

quit

...那么 gdb_output.txt 的内容如预期的那样如下:

Breakpoint 1 at 0x4004da: file simple.c, line 5.
Breakpoint 2 at 0x4004e1: file simple.c, line 6.
Breakpoint 3 at 0x4004eb: file simple.c, line 7.

Breakpoint 1, main () at simple.c:5
5     x=-1;
COMMAND 1 ACTIVATED

Breakpoint 2, main () at simple.c:6
6     global = 5;
COMMAND 2 ACTIVATED

Breakpoint 3, main () at simple.c:7
7     return(0);
COMMAND 3 ACTIVATED
8   
[Inferior 1 (process 29631) exited normally]

但是,如果我编辑命令文件以尝试作为循环执行,则替换

next
next
next
continue

while true
  next
end

但是让脚本的其余部分完全一样,那么我为第 6 行和第 7 行的断点指定的命令永远不会执行,运行修改后的命令文件后 gdb_output.txt 的内容证明了这一点:

Breakpoint 1 at 0x4004da: file simple.c, line 5.
Breakpoint 2 at 0x4004e1: file simple.c, line 6.
Breakpoint 3 at 0x4004eb: file simple.c, line 7.

Breakpoint 1, main () at simple.c:5
5     x=-1;
COMMAND 1 ACTIVATED

Breakpoint 2, main () at simple.c:6
6     global = 5;

Breakpoint 3, main () at simple.c:7
7     return(0);
8   
__libc_start_main (main=0x4004d6 <main()>, argc=1, argv=0x7fffffffe128, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffe118) at ../csu/libc-start.c:325
325 ../csu/libc-start.c: No such file or directory.
[Inferior 1 (process 29652) exited normally]
commands.txt:30: Error in sourced command file:
The program is not being run.

我知道当前形式的循环是有问题的,因为它会一直调用“next”直到程序终止(因此它永远不会到达脚本底部的“退出”),但这似乎没有就像它应该停止运行断点命令一样——但这似乎正在发生。 (如果断点命令正在执行,我可以让while循环在遇到C程序退出点之前设置的断点时终止。)

这是 GDB 中的错误,还是我误解了什么?如果这种构造从根本上不起作用,那么有没有办法在程序运行的每一步上执行一系列固定的 GDB 命令,直到程序终止,同时还执行在某些断点处指定的命令——或者这从根本上是不可能的GDB 脚本?

(我的 gdb 版本是 7.11.1,如果重要的话,我的操作系统是 Linux。)


更新

我决定试一试 lldb,但遇到了一些更令人困惑的问题(使用与上面相同的 C 文件,使用相同的命令编译)。这是我的 lldb 脚本:

target create --no-dependents --arch x86_64 simple

breakpoint set --file simple.c --line 5
breakpoint command add
  script print "COMMAND 1 ACTIVATED"
DONE

breakpoint set --file simple.c --line 6
breakpoint command add
  script print "COMMAND 2 ACTIVATED"
DONE

breakpoint set --file simple.c --line 7
breakpoint command add
  script print "COMMAND 3 ACTIVATED"
DONE

run

frame variable x
continue

frame variable x
continue

frame variable x
continue

quit

这表现出相当奇怪的行为。上面的版本命中第一个断点,执行相关的命令,然后忽略后面的所有断点。如果我注释掉只是第二个断点,它的关联命令,以及相应的frame variable xcontinue,那么断点 1 和 3 都会被命中并执行相应的命令。仅注释掉第一个或第三个断点及其关联的命令和frame variable xcontinue 会导致只有第一个未注释的断点被命中,并且其关联的命令运行。简而言之,似乎在两行连续代码上设置断点会导致第一行之后的所有断点都被忽略。

有人知道这里发生了什么吗?有没有办法可以在每一行都设置一个断点并让它们都被击中?这个问题是否与上述 gdb 问题有任何关系?

【问题讨论】:

旁注:`return 是一个语句,而不是一个函数。为表达式加上括号以使其看起来像一个函数是误导性的,并且会使拼写错误未被检测到。 如果你用 g++ 编译它是 C++,而不是 C。不同的语言! 我不明白。你说它是一个 C 文件,具有 C 扩展名,但你编译为 C++。 C 和 C++ 是不同的语言。例如,C++ 有std::cout,而 C 没有。尝试使用gcc 这不是问题:如果我使用 gcc 编译,行为是相同的。 对于 lldb,命令文件中的命令以“异步”模式运行,这意味着只要进程再次启动,继续您正在调试的进程的任何命令都会返回,然后获取下一个命令。这是行不通的,因为“帧变量”命令在进程尚未停止时执行。您可以通过在第一个 run 命令之前将 'script lldb.debugger.SetAsync(False)' 放在命令文件中来切换模式。如果我这样做,您的命令文件对我有用。 【参考方案1】:

我还没有弄清楚 为什么 gdb 和 lldb 会以它们的方式行事,但我确实设计了一种替代方法来完成我想要的。我编写了一个脚本来使用两个命名管道与 lldb 通信,其中脚本的标准输出链接到 lldb 的标准输入,反之亦然,因此脚本可以发送 lldb 命令(frame variable -Lbtstep 等)然后获取 lldb 的输出并解析它。该脚本当然可以循环所有它想要的,所以这绕过了我无法让 gdb 或 lldb 命令文件正确循环的问题。

【讨论】:

以上是关于GDB 和 LLDB 都无法在简单的 C 文件中可靠地执行断点命令的主要内容,如果未能解决你的问题,请参考以下文章

如何在 OS X 10.9 (Mavericks) 上使用 GDB 而不是 LLDB 调试 ELF 文件?

堆栈跟踪中没有函数名称,GDB,但出现在LLDB中[重复]

iOS调试奇巧淫技之LLDB

lldb和gdb命令对照表

Rust LLDB 调试入门指北

VsCode 执行 debug(lldb/gdb) 命令