mirror of
https://github.com/LCTT/TranslateProject.git
synced 2025-01-13 22:30:37 +08:00
256 lines
8.1 KiB
Markdown
256 lines
8.1 KiB
Markdown
[#]: 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 <stdio.h>
|
||
|
||
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 <bar+4>: 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 <bar+8>: 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 <none> 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 <none> n num
|
||
2 y n <none> 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
|