Understanding and using GDB debugging - Basics

Posted by melvincr on Tue, 30 Nov 2021 22:25:29 +0100

Start debugging

1. Which programs can be debugged

  • For C and C + + programs, adding the - g parameter during compilation will retain debugging information, otherwise GDB cannot be used for debugging.

2. How to judge whether a file can be debugged

  • Run directly with the gdb file name. If it is not debuggable, you will be prompted accordingly.
  • Readelf view section information: readelf -S helloWorld|grep debug
  • File view strip status: file helloworld. If the last prompt is striped, it indicates that the symbol table information and debugging information of the file have been removed. However, if not striped, it does not necessarily mean that gdb debugging can be performed.

3. Startup and commissioning of non parametric program

$ gdb helloWorld
(gdb)
(gdb) run

Enter the run command to run the program directly.

4. Startup and commissioning of program with parameters

  • Mode 1: take the required parameters with the run command
$ gdb hello
(gdb)run Argument1
  • Mode 2: use set args before the run command
$ gdb hello
(gdb) set args Argument1
(gdb) run

5. Debug core files

core file introduction: https://www.jianshu.com/p/e38a3f1cf7f7

  • When the program exits abnormally under Linux, the kernel generates a core file in the current working directory to record the memory image and debugging information at that time. You can use gdb to view the core file.
  • The premise of generating core file is that the - g parameter is taken during compilation, and the generation of core file is not limited.
  • Focus on the generation switch and size limit of core files.
  • Focus on the noun and generation path of the core file.
$ gdb [exec file] [core file]

6. Debug the running program

The process id of the program running with the ps command

$ ps -ef|grep Process name

Or you can write your own test files and use bg and fg to switch the front and background.

Use attach to directly debug processes with related process IDs. If you are prompted that you do not have permission, you can sudo gdb

$ gdb
(gdb) attach 20829

7. The program has been run without debugging information

In order to save disk space, programs that have been run usually have no debugging information. And you can't stop the current program for recompiling and debugging. At this time, you can use the same code to compile another version with debugging information.

$ gdb
(gdb) file hello
Reading symbols from hello...done.
(gdb)attach 20829

breakpoint setting

1. View the set breakpoints

The command info breakpoints to view the set breakpoints.

2. Set breakpoint according to line number

Either way:

b 9  #break can be abbreviated as b
b test.c:9

3. Set the breakpoint according to the function name

A breakpoint occurs when the program calls the function funcName

b funcName

4. Set breakpoints according to conditions

The specific number of lines + variables determine the composition condition setting. If the condition is true, a breakpoint can be formed for observation:

break test.c:23 if b==0

When b equals 0, the program will break at line 23.

The condition command has a similar effect:

condition 1 b==0 

Meaning: when b is equal to 0, breakpoint 1 is generated.

5. Set breakpoints according to rules

#Usage: rbreak file:regex
rbreak . 
rbreak test.c:. #Set breakpoints for all functions in test.c
rbreak test.c:^print #Set a breakpoint on a function that starts with print

6. Set temporary breakpoints

A temporary breakpoint can be set if the breakpoint at a certain place only takes effect once:

tbreak test.c:l0  #Set a temporary breakpoint on line 10

7. Skip setting breakpoints multiple times

For a breakpoint, the first 30 times will not cause problems. You can skip the first 30 times:

ignore 1 30

Where 1 is the breakpoint sequence number queried through info breakpoints.

8. Generate breakpoints according to the change of expression value

Observe a specific expression or value. When it changes, generate a breakpoint and print the relevant content:

watch a  
# When the value changes, it will be printed out
Hardware watchpoint 2: a
Old value = 12
New value = 11

rwatch and awatch can also set observation points. The former is broken when the variable value is read, and the latter is broken when it is read or rewritten.

9. Disable, enable and delete breakpoints

For breakpoints that do not need to be used temporarily but cannot be deleted, you can choose to disable them temporarily

disable  #disable all breakpoints 
disable bnum #Disable breakpoints labeled bnum
enable  #Enable all breakpoints
enable bnum #Enable breakpoints labeled bnum
clear   #Delete all breakpoints in the current row
clear function  #Delete the breakpoint at the function named function
clear filename:function #Delete the breakpoint at function in file filename
clear lineNum #Delete the breakpoint at line number lineNum
clear f:lename: lineNum #Delete the breakpoint at line number lineNum in file filename
delete  #Delete all breakpoints,watchpoints and catchpoints
delete bnum #Delete the breakpoint with breakpoint number bnum

Variable view

1. Print basic data types: variable, array and string

Print the variable contents using print (which can be abbreviated as p):

(gdb) p a
$1 = 10
(gdb) p b
$2 = {1, 2, 3, 5}
(gdb) p c
$3 = "hello,shouwang"
(gdb)

You can add a function name or file name to distinguish variables with the same name:

(gdb) p 'testGdb.h'::a
$1 = 11
(gdb) p 'main'::b
$2 = {1, 2, 3, 5}
(gdb) 

2. The print pointer points to the content

  • If the pointer is printed as a normal variable, the pointer address will be printed:
(gdb) p d
$1 = (int *) 0x602010
  • If you need to print the content pointed to by the pointer, you need to dereference:
(gdb) p *d
$2 = 0
(gdb) p *d@10
$3 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
(gdb) 

Use the @ symbol to keep up with the length you want to print.

  • $is the previous variable in gdb
(gdb) p *linkNode
(Show here linkNode Node content)
(gdb) p *$.next
(Show here linkNode Node the content of the next node)
  • Setting gdb variables and using accumulation
(gdb) set $index=0
(gdb) p b[$index++]
$11 = 1
(gdb) p b[$index++]
$12 = 2
(gdb) p b[$index++]
$13 = 3

3. Print variables in a specific format

  • x displays variables in hexadecimal format.
  • d displays variables in decimal format.
  • u displays unsigned integers in hexadecimal format.
  • o display variables in octal format.
  • Tdisplay variables in binary forma t.
  • a displays variables in hexadecimal format.
  • c display variables in character format.
  • f displays variables in floating point format.
(gdb) p/x c
$19 = {0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x73, 0x68, 0x6f, 0x75, 0x77, 0x61, 
  0x6e, 0x67, 0x0}
(gdb)

4. Check the contents of memory and register

Examine (abbreviated as x) can be used to view the values in the memory address:

x/[n][f][u] addr

Of which:

  • n indicates the number of memory units to display. The default value is 1
  • f indicates the format to be printed. Format control characters have been mentioned earlier
  • u unit length to print
    • b byte
    • h half word, i.e. double byte
    • w word, i.e. four bytes
    • g octet
  • addr memory address
(gdb) x/4tb &e  # &E start with 4 bytes of memory and print in binary
0x7fffffffdbd4:    00000000    00000000    00001000    01000001
(gdb) 

The command info registers allows you to view the register contents:

(gdb)info registers
rax            0x0    0
rbx            0x0    0
rcx            0x7ffff7dd1b00    140737351850752
rdx            0x0    0
rsi            0x7ffff7dd1b30    140737351850800
rdi            0xffffffff    4294967295
rbp            0x7fffffffdc10    0x7fffffffdc10

5. Automatically print variable content at breakpoint

If you want the program to automatically print the value of a variable at a breakpoint, you can use the display command:

(gdb) display e
1: e = 8.5

To see which variables are set to display:

(gdb)into display

To clear:

delete display num #Num is the number before the previous variable. If there is no num, all variables will be cleared.

Single step debugging

1. print the source of the current debugger.

(gdb) list

2. Single step program next

If you have started debugging and stopped at a breakpoint, you can continue to execute the next statement by using the next command (abbreviated as n). If it follows the number number, it means number times:

$ gdb gdbStep   #Start debugging
(gdb)b 25       #Set breakpoint at line 12
(gdb)run        #Run program
Breakpoint 1, main () at gdbStep.c:25
25        int b = 7;
(gdb) n     #Single step execution
26        printf("it will calc a + b\n");
(gdb) n 2   #Execute twice
it will calc a + b
28        printf("%d + %d = %d\n",a,b,c);
(gdb) 

3. Step into step

If you need to follow the inside of the function to check the situation, you can use the step command (abbreviated as s) to track the inside of the derivative function step by step, provided that the function has debugging information and source code information.

$ gdb gdbStep    #Start debugging
(gdb) b 25       #Set breakpoint on line 12
Breakpoint 1 at 0x4005d3: file gdbStep.c, line 25.
(gdb) run        #Run program
Breakpoint 1, main () at gdbStep.c:25
25        int b = 7;
(gdb) s          
26        printf("it will calc a + b\n");
(gdb) s     #Step in, but there is no source file information for this function
_IO_puts (str=0x4006b8 "it will calc a + b") at ioputs.c:33
33    ioputs.c: No such file or directory.
(gdb) finish    #Continue to complete the function call
Run till exit from #0  _IO_puts (str=0x4006b8 "it will calc a + b")
    at ioputs.c:33
it will calc a + b
main () at gdbStep.c:27
27        int c = add(a,b);
Value returned is $1 = 19
(gdb) s        #Step in. Now it has entered the add function
add (a=13, b=57) at gdbStep.c:6
6        int c = a + b;

The s command will try to enter the function, but if there is no source code of the function, you need to skip the execution of the function. You can use the finish command to continue the subsequent execution.

  • The s command can set options to select whether to skip functions without debugging information by default:
(gdb) show step-mode 
Mode of the step operation is off.
(gdb) set step-mode on
(gdb) set step-mode off
  • The s command executes one program statement at a time. You can use stepi (abbreviated as si) to execute one machine instruction at a time.

4. Continue to the next breakpoint continue

Using the continue command (which can be abbreviated as c) will continue to execute the current program until the breakpoint is encountered again.

5. Continue to run to the specified number of rows until

If we want to continue running until a specific number of lines stops, we can use the until command (abbreviated as u):

6. skip execution

Skip can skip some functions you don't want to pay attention to or the code of a file during step:

$ gdb gdbStep
(gdb) b 27
Breakpoint 1 at 0x4005e4: file gdbStep.c, line 27.
(gdb) skip function add    # Skip the add function when step
Function add will be skipped when stepping.
(gdb) info skip   # Check step status
Num     Type           Enb What
1       function       y   add
(gdb) run
Starting program: /home/hyb/workspaces/gdb/gdbStep 
it will calc a + b

Breakpoint 1, main () at gdbStep.c:27
27        int c = add(a,b);
(gdb) s
28        printf("%d + %d = %d\n",a,b,c);
(gdb)skip file gdbStep.c # Skip all functions in the file

Other related commands:

  • skip delete [num] delete skip
  • skip enable [num] enables skip
  • skip disable [num] to enable skip

Source view

During debugging, you generally need to check the whole or part of the source code. Under GDB debugging, you can quickly view the source code or edit the source code.

1. Source code printing

  • Direct printing source code: list command (abbreviated l)
  • List the source code near the specified line: list command + line number
(gdb) l 9
  • List the source code of the guide function attachment: list command + function name
  • Set the number of lines listed in the source code at a time. Generally, the print source code displays 10 lines by default. Set through the listsize property.
(gdb) set listsize 20
(gdb) show listsize
  • list the source code of the area between the specified lines: list + start line number + end line number
(gdb) l 3,15 # List the source code between 3 and 15 lines
  • List the source code of the guidance document
(gdb) l test.c:1
(gdb) l test.c:printNum1
(gdb) l test.c:1,test.c:3

2. Specify the source code path

Before viewing the source code, you need to ensure that the program can be associated with the source code file. However, when the source code file is moved, the source code cannot be viewed directly through the lsit command.

  • Scenario 1: source file movement

Move the source file main.c to the temp directory, and then execute the list command

(gdb) l
1    main.c: No such file or directory.
(gdb) 

Re specify the source code path through the dir command:

(gdb) dir ./temp
Source directories searched: /home/hyb/workspaces/gdb/sourceCode/./temp:$cdir:$cwd
  • Scenario 2: change the source directory

When all source files are moved to another directory, you can add the source search path using the method in scenario 1 above, or use set substitute path from to replace the original path with a new path

You can view the original source code path through the readelf command:

$ readelf main -p .debug_str
  [     0]  long unsigned int
  [    12]  short int
  [    1c]  /home/hyb/workspaces/gdb/sourceCode
  [    40]  main.c
(Display part)

Replace path:

(gdb) set substitute-path /home/hyb/workspaces/gdb/sourceCode /home/hyb/workspaces/gdb/sourceCode/temp
(gdb) show substitute-path
List of all source path substitution rules:
  `/home/hyb/workspaces/gdb/sourceCode' -> `/home/hyb/workspaces/gdb/sourceCode/temp'.
(gdb)

You can cancel the replacement by unset substitute path [path].

3. Edit source code

After debugging, if you need to edit the source code, you can edit the source code directly in gdb mode. The default editor used by gdb is / bin/ex. you can set the replacement editor:

$ EDITOR=/usr/bin/vim
$ export EDITOR

Edit the source code under the gdb debugging model, and use the edit command:

(gdb)edit 3  #Edit the third line
(gdb)edit printNum #Edit printNum function
(gdb)edit test.c:5 #Edit the fifth line of test.c

After editing and saving in vim editor, you can directly recompile the program:

(gdb)shell gcc -g -o main main.c test.c

To execute a shell command in gdb mode, you need to add a shell before the command.

Other references

Command summary

https://www.toutiao.com/i6914639382385295886/?tt_from=weixin&utm_campaign=client_share&wxshare_count=1&timestamp=1637332354&app=news_article&utm_source=weixin&utm_medium=toutiao_ios&use_new_style=1&req_id=202111192232330101502202011138A213&share_token=5985B6A6-7323-40D4-897E-3CF9544B6CAD&group_id=6914639382385295886&wid=1637663728310

The command is listed again:

  • File: load the debugger source file
  • kill: terminate the debugger
  • list: print source code
  • break: set breakpoint
  • run: execute the program
  • quit: Launch gdb
  • Step: single step entry
  • next: single step execution
  • Continue: continue running until the next breakpoint
  • Print: print variables, etc
  • watch: monitor the change of variable value
  • display: the variable is printed once for each breakpoint
  • start: starts executing the program and stops before the first statement of the main function
  • info: gdb related information
  • set var name = value: sets the value of the variable
  • backtrace: View function call information (stack)
  • Frame: View stack frame
  • Return: force the function to return
  • Where: lists the location where the current program is running
  • whatis: view the types of variables and functions
  • examine: view memory contents

Debug script patch

https://www.toutiao.com/i6836648226666316295/?tt_from=weixin&utm_campaign=client_share&wxshare_count=1&timestamp=1637326374&app=news_article&utm_source=weixin&utm_medium=toutiao_ios&use_new_style=1&req_id=20211119205254010151180208251D4D47&share_token=16A0B9FE-C93F-427F-B735-24C97F5A0096&group_id=6836648226666316295

  • Disassembly
(gdb) disassemble Function name
  • GDB breakpoint preset command

GDB provides a function. For a specified breakpoint, GDB allows users to preset a set of operations (usually debugging commands). When the breakpoint is triggered, GDB will automatically execute this set of preset operations.

Set one or more breakpoints first; Then use the commands command to perform preset operations (the id is the breakpoint id):

commands [id...]
  command-list
end
  • In the process of debugging, preset commands are used to quickly repair known bugs and avoid the tedious of multiple compilation.
b *do_stuff
commands
  printf "\n ESI = %d\n",$esi
  set $esi=10
  printf "\n ESI = %d\n",$esi
  continue
end
# do_ Set a breakpoint at the entrance of the stuff function, and modify the function input parameter transfer by modifying the esi register.
  • The above can be written as a hot patch file. test.fix.1. Add the silent command to shield the print information when the breakpoint is triggered to avoid visual interference.
# test.fix.1
b *do_stuff
commands
  slient
  set $esi=10
  continue
end
  • GDB re debug the run, and load the script file with the - x parameter
gdb test -x test.fix.1