diff --git a/sources/tech/20221208.2 ⭐️⭐️ 7 pro tips for using the GDB step command.md b/sources/tech/20221208.2 ⭐️⭐️ 7 pro tips for using the GDB step command.md deleted file mode 100644 index 8d0a0fecfd..0000000000 --- a/sources/tech/20221208.2 ⭐️⭐️ 7 pro tips for using the GDB step command.md +++ /dev/null @@ -1,255 +0,0 @@ -[#]: subject: "7 pro tips for using the GDB step command" -[#]: via: "https://opensource.com/article/22/12/gdb-step-command" -[#]: author: "Alexandra https://opensource.com/users/ahajkova" -[#]: collector: "lkxed" -[#]: translator: "chai001125" -[#]: reviewer: " " -[#]: publisher: " " -[#]: url: " " - -7 pro tips for using the GDB step command -====== - -A debugger is software that runs your code and examines any problems it finds. [GNU Debugger][1] (GBD) is one of the most popular debuggers, and in this article, I examine GDB's `step` command and related commands for several common use cases. Step is a widely used command but there are a few lesser known things about it which might be confusing. Also, there are ways to step into a function without actually using the `step` command itself such as using the less known `advance` command. - -### No debugging symbols - -Consider a simple example program: - -``` -#include - -int num() { - return 2; -} - -void bar(int i) { - printf("i = %d\n", i); -} - -int main() { - bar(num()); - return 0; -} -``` - -If you compile without the debugging symbols first, set a breakpoint on `bar` and then try to step within it. The GDB gives an error message about no line number information: - -``` -gcc exmp.c -o exmp -gdb ./exmp -(gdb) b bar -Breakpoint 1 at 0x401135 -(gdb) r -Starting program: /home/ahajkova/exmp -Breakpoint 1, 0x0000000000401135 in bar () -(gdb) step -Single stepping until exit from function bar, -which has no line number information. -i = 2 -0x0000000000401168 in main () -``` - -### Stepi - -It is still possible to step inside the function that has no line number information but the `stepi` command should be used instead. Stepi executes just one instruction at a time. When using GDB's `stepi` command, it's often useful to first do `display/i $pc`. This causes the program counter value and corresponding machine instruction to be displayed after each step: - -``` -(gdb) b bar -Breakpoint 1 at 0x401135 -(gdb) r -Starting program: /home/ahajkova/exmp -Breakpoint 1, 0x0000000000401135 in bar () -(gdb) display/i $pc -1: x/i $pc -=> 0x401135 : sub $0x10,%rsp -``` - -In the above `display` command, the `i` stands for machine instructions and `$pc` is the program counter register. - -It can be useful to use info registers and print some register contents: - -``` -(gdb) info registers -rax 0x2 2 -rbx 0x7fffffffdbc8 140737488346056 -rcx 0x403e18 4210200 -(gdb) print $rax -$1 = 2 -(gdb) stepi -0x0000000000401139 in bar () -1: x/i $pc -=> 0x401139 : mov %edi,-0x4(%rbp) -``` - -### Complicated function call - -After recompiling the example program with debugging symbols you can set the breakpoint on the `bar` call in main using its line number and then try to step into `bar` again: - -``` -gcc -g exmp.c -o exmp -gdb ./exmp -(gdb) b exmp.c:14 -Breakpoint 1 at 0x401157: file exmp.c, line 14. -(gdb) r -Starting program: /home/ahajkova/exmp -Breakpoint 1, main () at exmp.c:14 -14 bar(num()); -``` - -Now, let's step into`bar()`: - -``` -(gdb) step -num () at exmp.c:4 -4 return 2; -``` - -The arguments for a function call need to be processed before the actual function call, so `num()` is expected to execute before `bar()`is called. But how do you step into the `bar` as was desired? You need to use the `finish` command and `step` again: - -``` -(gdb) finish -Run till exit from #0 num () at exmp.c:4 -0x0000000000401161 in main () at exmp.c:14 -14 bar(num()); -Value returned is $1 = 2 -(gdb) step -bar (i=2) at exmp.c:9 -9 printf("i = %d\n", i); -``` - -### Tbreak - -The `tbreak` command sets a temporary breakpoint. It's useful for situations where you don't want to set a permanent breakpoint. For example, if you want to step into a complicated function call like `f(g(h()), i(j()), ...)` , in such a case you need a long sequence of `step/finish/step` to step into `f` . Setting a temporary breakpoint and then using continue can help to avoid using such sequences. To demonstrate this, you need to set the breakpoint to the `bar` call in `main` as before. Then set the temporary breakpoint on `bar`.  As a temporary breakpoint it is automatically removed after being hit: - -``` -(gdb) r -Starting program: /home/ahajkova/exmp -Breakpoint 1, main () at exmp.c:14 -14 bar(num()); -(gdb) tbreak bar -Temporary breakpoint 2 at 0x40113c: file exmp.c, line 9. -``` - -After hitting the breakpoint on the call to `bar` and setting a temporary breakpoint on `bar`, you just need to continue to end up in  `bar`. - -``` -(gdb) continue -Continuing. -Temporary breakpoint 2, bar (i=2) at exmp.c:9 -9 printf("i = %d\n", i); -``` - -### Disable command - -Alternatively, you could set a normal breakpoint on `bar` , continue, and then disable this second breakpoint when it's no longer needed. This way you can achieve the same results as with the `tbreak` with one extra command: - -``` -(gdb) b exmp.c:14 -Breakpoint 1 at 0x401157: file exmp.c, line 14. -(gdb) r -Starting program: /home/ahajkova/exmp -Breakpoint 1, main () at exmp.c:14 -14 bar(num()); -(gdb) b bar -Breakpoint 2 at 0x40113c: file exmp.c, line 9. -(gdb) c -Continuing. -Breakpoint 2, bar (i=2) at exmp.c:9 -9 printf("i = %d\n", i); -(gdb) disable 2 -``` - -As you can see, the `info breakpoints`  command displays `n` under `Enb`which means it’s disabled but you can enable it later if it’s needed again. - -``` -(gdb) info breakpoints -Num Type Disp Enb Address What -1 breakpoint keep y 0x0000000000401157 in main at exmp.c:14 -breakpoint already hit 1 time -2 breakpoint keep n 0x000000000040113c in bar at exmp.c:9 -breakpoint already hit 1 time -(gdb) enable 2 -(gdb) info breakpoints -Num Type Disp Enb Address What -1 breakpoint keep y 0x000000000040116a in main at exmp.c:19 -breakpoint already hit 1 time -2 breakpoint keep y 0x0000000000401158 in bar at exmp.c:14 -breakpoint already hit 1 time -``` - -### Advance location - -Another option you can use is an `advance` command. Instead of `tbreak bar ; continue` , you can simply do `advance bar` . This command continues running the program up to the given location. - -The other cool thing about `advance` is that if the location that you try to advance to is not reached, GDB will stop after the current frame's function finishes. Thus, execution of the program is constrained: - -``` -Breakpoint 1 at 0x401157: file exmp.c, line 14. -(gdb) r -Starting program: /home/ahajkova/exmp -Breakpoint 1, main () at exmp.c:14 -14 bar(num()); -(gdb) advance bar -bar (i=2) at exmp.c:9 -9 printf("i = %d\n", i); -``` - -### Skipping a function - -Yet another way to step into the `bar,` avoiding `num`, is using the `skip` command: - -``` -(gdb) b exmp.c:14 -Breakpoint 1 at 0x401157: file exmp.c, line 14. -(gdb) skip num -Function num will be skipped when stepping. -(gdb) r -Starting program: /home/ahajkova/exmp -Breakpoint 1, main () at exmp.c:14 -14 bar(num()); -(gdb) step -bar (i=2) at exmp.c:9 -9 printf("i = %d\n", i); -``` - -To know which functions are currently skipped,  `info skip` is used.  The `num` function is marked as enabled to be skipped by `y`: - -``` -(gdb) info skip -Num Enb Glob File RE Function -1 y n n num -``` - -If `skip` is not needed any more it can be disabled (and re-enabled later) or deleted altogether. You can add another `skip` and disable the first one and then delete them all. To disable a certain `skip`, its number has to be specified, if not specified, each `skip`is disabled. It works the same for enabling or deleting a `skip`: - -``` -(gdb) skip bar -(gdb) skip disable 1 -(gdb) info skip -Num Enb Glob File RE Function -1 n n n num -2 y n n bar -(gdb) skip delete -(gdb) info skip -Not skipping any files or functions. -``` - -### GDB step command - -Using GDB's `step` command is a useful tool for debugging your application. There are several ways to step into even complicated functions, so give these GDB techniques a try next time you're troubleshooting your code. - --------------------------------------------------------------------------------- - -via: https://opensource.com/article/22/12/gdb-step-command - -作者:[Alexandra][a] -选题:[lkxed][b] -译者:[译者ID](https://github.com/译者ID) -校对:[校对者ID](https://github.com/校对者ID) - -本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出 - -[a]: https://opensource.com/users/ahajkova -[b]: https://github.com/lkxed -[1]: https://opensource.com/article/21/3/debug-code-gdb diff --git a/translated/tech/20221208.2 ⭐️⭐️ 7 pro tips for using the GDB step command.md b/translated/tech/20221208.2 ⭐️⭐️ 7 pro tips for using the GDB step command.md new file mode 100644 index 0000000000..ea398cd4a0 --- /dev/null +++ b/translated/tech/20221208.2 ⭐️⭐️ 7 pro tips for using the GDB step command.md @@ -0,0 +1,256 @@ +[#]: subject: "7 pro tips for using the GDB step command" +[#]: via: "https://opensource.com/article/22/12/gdb-step-command" +[#]: author: "Alexandra https://opensource.com/users/ahajkova" +[#]: collector: "lkxed" +[#]: translator: "chai001125" +[#]: reviewer: " " +[#]: publisher: " " +[#]: url: " " + +GDB 进入函数内部的 7 个命令 +====== + +>**调试器**是一个可以运行你的代码并检查问题的软件。[GNU Debugger][1](GBD)是最流行的调试器之一,在这篇文章中,我研究了 **GDB 进入函数内部的几个命令**,包含了 **`step` 命令**和其他几个**常见的 GDB 命令**。`step` 是一个被广泛使用的命令,但它有一些人们不太了解的地方,可能会使得他们十分困惑。此外,还有一些方法可以**在不使用 `step` 命令的情况下进入一个函数**,比如使用不太知名的 `advance` 命令。 + +### 1、无调试符号 + +考虑以下这个简单的示例程序: + +``` +#include + +int num() { + return 2; +} + +void bar(int i) { + printf("i = %d\n", i); +} + +int main() { + bar(num()); + return 0; +} +``` + +如果你在没有 调试符号 debugging sysbols 的情况下进行编译(即在使用 `gcc` 编译程序时上,没有写 `-g` 选项),然后在 `bar` 上设置一个断点,然后尝试在这个函数内使用 `step`,来单步执行语句。GDB 会给出一个 没有行号信息 no line number information 的错误信息。 + +``` +gcc exmp.c -o exmp +gdb ./exmp +(gdb) b bar +Breakpoint 1 at 0x401135 +(gdb) r +Starting program: /home/ahajkova/exmp +Breakpoint 1, 0x0000000000401135 in bar () +(gdb) step +Single stepping until exit from function bar, +which has no line number information. +i = 2 +0x0000000000401168 in main () +``` + +### 2、`stepi` 命令 + +但是你仍然可以在没有行号信息的函数内部单步执行语句,但要使用 `stepi` 命令来代替 `step`。`stepi` 一次只执行一条指令。当使用 GDB 的 `stepi` 命令时,先做 `display/i $pc` 通常很有用,这会在每一步之后**显示** **程序计数器的值** program counter value 和**相应的** **机器指令** machine instruction : + +``` +(gdb) b bar +Breakpoint 1 at 0x401135 +(gdb) r +Starting program: /home/ahajkova/exmp +Breakpoint 1, 0x0000000000401135 in bar () +(gdb) display/i $pc +1: x/i $pc +=> 0x401135 : sub $0x10,%rsp +``` + +在上述的 `display` 命令中,`i` 代表机器指令,`$pc` 表示程序计数器寄存器(即 PC 寄存器)。 + +使用 `info registers` 命令,来**打印寄存器的内容**,也是十分有用的。 + +``` +(gdb) info registers +rax 0x2 2 +rbx 0x7fffffffdbc8 140737488346056 +rcx 0x403e18 4210200 +(gdb) print $rax +$1 = 2 +(gdb) stepi +0x0000000000401139 in bar () +1: x/i $pc +=> 0x401139 : mov %edi,-0x4(%rbp) +``` + +### 3、复杂的函数调用 + +在带调试符号的 `-g` 选项,重新编译示例程序后,你可以用 `main` 中的行号,在 `bar` 上设置断点,然后再单步执行 `bar` 函数的语句: + +``` +gcc -g exmp.c -o exmp +gdb ./exmp +(gdb) b exmp.c:14 +Breakpoint 1 at 0x401157: file exmp.c, line 14. +(gdb) r +Starting program: /home/ahajkova/exmp +Breakpoint 1, main () at exmp.c:14 +14 bar(num()); +``` + +接下来,用 `step`,来单步执行 `bar()` 函数的语句: + +``` +(gdb) step +num () at exmp.c:4 +4 return 2; +``` + +函数调用的参数需要在实际的函数调用之前进行处理,`bar()` 函数调用了 `num()` 函数,所以 `num()` 会在 `bar()` 被调用之前执行。但是,通过 GDB 调试,你怎么才能如愿以偿地进入 `bar()` 函数呢?你可以使用 `finish` 命令,并再次使用 `step` 命令。 + +``` +(gdb) finish +Run till exit from #0 num () at exmp.c:4 +0x0000000000401161 in main () at exmp.c:14 +14 bar(num()); +Value returned is $1 = 2 +(gdb) step +bar (i=2) at exmp.c:9 +9 printf("i = %d\n", i); +``` + +### 4、`Tbreak` 命令 + +`tbreak` 命令会设置一个**临时断点**。如果你不想设置永久断点,那么这个命令是很有用的。举个例子🌰,你想进入一个复杂的函数调用,例如 `f(g(h()), i(j()), ...)`,在这种情况下,你需要一个很长的 `step/finish/step` 序列,才能到达 `f` 函数。如果你设置一个临时断点,然后再使用 `continue` 命令,这样就不需要以上的序列了。为了证明这一点,你需要像以前一样将断点设置在 `main` 的 `bar` 调用上。然后在 `bar` 上设置临时断点。当到达该临时断点后,临时断点会被自动删除。 + + +``` +(gdb) r +Starting program: /home/ahajkova/exmp +Breakpoint 1, main () at exmp.c:14 +14 bar(num()); +(gdb) tbreak bar +Temporary breakpoint 2 at 0x40113c: file exmp.c, line 9. +``` + +在调用 `bar` 的时候遇到断点,并在 `bar` 上设置临时断点后,你只需要使用`continue` 继续运行直到 `bar` 结束。 + +``` +(gdb) continue +Continuing. +Temporary breakpoint 2, bar (i=2) at exmp.c:9 +9 printf("i = %d\n", i); +``` + +### 5、`disable` 命令 + +类似地,你也可以在 `bar` 上设置一个正常的断点,然后执行 `continue`,然后在不再需要第二个断点时,使用`disable` 命令禁用这个断点,这样也能达到与 `tbreak` 相同的效果。 + +``` +(gdb) b exmp.c:14 +Breakpoint 1 at 0x401157: file exmp.c, line 14. +(gdb) r +Starting program: /home/ahajkova/exmp +Breakpoint 1, main () at exmp.c:14 +14 bar(num()); +(gdb) b bar +Breakpoint 2 at 0x40113c: file exmp.c, line 9. +(gdb) c +Continuing. +Breakpoint 2, bar (i=2) at exmp.c:9 +9 printf("i = %d\n", i); +(gdb) disable 2 +``` + +正如你所看到的,`info breakpoints` 命令在 `Enb` 下显示为 `n`,这意味着这个断点已被禁用。但你也能在再次需要这个断点时,再启用它。 + +``` +(gdb) info breakpoints +Num Type Disp Enb Address What +1 breakpoint keep y 0x0000000000401157 in main at exmp.c:14 +breakpoint already hit 1 time +2 breakpoint keep n 0x000000000040113c in bar at exmp.c:9 +breakpoint already hit 1 time +(gdb) enable 2 +(gdb) info breakpoints +Num Type Disp Enb Address What +1 breakpoint keep y 0x000000000040116a in main at exmp.c:19 +breakpoint already hit 1 time +2 breakpoint keep y 0x0000000000401158 in bar at exmp.c:14 +breakpoint already hit 1 time +``` + +### 6、`Advance` 命令运行程序到指定的位置 + +另一个进入函数内部的方法是 `advance` 命令。你可以简单地用 `advance bar`,来代替 `tbreak bar ; continue`。这一命令会将程序继续运行到指定的位置。 + +`advance` 命令的一个很棒的地方在于:如果程序并没有到达你试图进入的位置,那么 GDB 将在当前函数运行完成后停止。因此,程序的执行会受到限制: + +``` +Breakpoint 1 at 0x401157: file exmp.c, line 14. +(gdb) r +Starting program: /home/ahajkova/exmp +Breakpoint 1, main () at exmp.c:14 +14 bar(num()); +(gdb) advance bar +bar (i=2) at exmp.c:9 +9 printf("i = %d\n", i); +``` + +### 7、`skip` 命令 + +进入 `bar` 函数的另一种方式是使用 `skip num` 命令: + +``` +(gdb) b exmp.c:14 +Breakpoint 1 at 0x401157: file exmp.c, line 14. +(gdb) skip num +Function num will be skipped when stepping. +(gdb) r +Starting program: /home/ahajkova/exmp +Breakpoint 1, main () at exmp.c:14 +14 bar(num()); +(gdb) step +bar (i=2) at exmp.c:9 +9 printf("i = %d\n", i); +``` + +请使用 `info skip` 命令,来了解 GDB 跳过了哪些函数。`num()` 函数被标记为 `y`,表示跳过了 `num()` 函数: + +``` +(gdb) info skip +Num Enb Glob File RE Function +1 y n n num +``` + +如果不再需要 `skip`,可以禁用(稍后重新启用)或完全删除它。你可以添加另一个 `skip`,并禁用第一个 `skip`,然后全部删除。要禁用某个 `skip`,必须指定其编号(例如,`skip disable 1`),如果没有指定,则会禁用所有的 `skip`。启用或删除 `skip` 的工作原理相同: + +``` +(gdb) skip bar +(gdb) skip disable 1 +(gdb) info skip +Num Enb Glob File RE Function +1 n n n num +2 y n n bar +(gdb) skip delete +(gdb) info skip +Not skipping any files or functions. +``` + +### GDB 的 `step` 命令 + +使用 GDB 的 `step` 命令是调试程序的一个有用工具。即使是复杂的函数,也有几种方法可以进入这些函数,所以下次你在排除代码问题的时候,可以尝试一下这些 GDB 技术。 + +-------------------------------------------------------------------------------- + +via: https://opensource.com/article/22/12/gdb-step-command + +作者:[Alexandra][a] +选题:[lkxed][b] +译者:[chai001125](https://github.com/chai001125) +校对:[校对者ID](https://github.com/校对者ID) + +本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出 + +[a]: https://opensource.com/users/ahajkova +[b]: https://github.com/lkxed +[1]: https://opensource.com/article/21/3/debug-code-gdb