赞
踩
GDB是GNU开源组织发布的一个强大的UNIX下的程序调试工具,GDB主要可帮助工程师完成下面4个方面的功能:
- 启动程序,可以按照工程师自定义的要求随心所欲的运行程序。
- 让被调试的程序在工程师指定的断点处停住,断点可以是条件表达式。
- 当程序被停住时,可以检查此时程序中所发生的事,并追索上文。
- 动态地改变程序的执行环境。
我这里讲述的是GNU Arm Embedded GCC里的GDB调试器。GNU Arm Embedded GCC由xPack论坛提供,支持多个系统安装包括(Windows/macOS/GNU/Linux)。相关Release版本获取,大家可以去The xPack GNU Arm Embedded GCC releases | The xPack Build Framework下载相应的版本。
GDB 的主要功能就是监控程序的执行流程。这也就意味着,只有当源程序文件编译为可执行文件并执行时,并且该文件中必须包含必要的调试信息(比如各行代码所在的行号、包含程序中所有变量名称的列表(又称为符号表)等),GDB才会派上用场。
所以在编译时需要使用 gcc/g++ -g 选项编译源文件,才可生成满足 GDB 要求的可执行文件
帮助信息与基本信息命令
Command | Description |
---|---|
help <command> | Command description. eg help show to list the show commands |
info breakpoints/b/break | List breakpoints |
info break <breakpoint-number> | List info about specific breakpoint. |
info registers/r | List registers in use |
help <command> 命令
在gdb中运行help命令(缩写h)可以查询相关命令使用方法。
- (gdb) help list
- list, l
- List specified function or line.
- With no argument, lists ten more lines after or around previous listing.
- "list -" lists the ten lines before a previous ten-line listing.
- One argument specifies a line, and ten lines are listed around that line.
- Two arguments with comma between specify starting and ending lines to list.
- Lines can be specified in these ways:
- LINENUM, to list around that line in current file,
- FILE:LINENUM, to list around that line in that file,
- FUNCTION, to list around beginning of that function,
- FILE:FUNCTION, to distinguish among like-named static functions.
- *ADDRESS, to list around the line containing that address.
- With two args, if one is empty, it stands for ten lines away from
- the other arg.
-
- By default, when a single location is given, display ten lines.
- This can be changed using "set listsize", and the current value
- can be shown using "show listsize".
info <args> 命令
在gdb中运行info命令(缩写i)可以查询相关断点或者MCU/CPU registers使用信息。
- (gdb) info register # info r / i r
- r0 0x0 0
- r1 0x20 32
- r2 0xa 10
- r3 0x3 3
- r4 0x20000364 536871780
- r5 0x3f 63
- r6 0x20003d08 536886536
- r7 0x20007fb0 536903600
- r8 0x20000010 536870928
- r9 0x0 0
- r10 0x20001200 536875520
- r11 0x1 1
- r12 0x800bda5 134266277
- sp 0x20007fb0 0x20007fb0
- lr 0x8003ab7 134232759
- pc 0x8011172 0x8011172 <main+6>
- xpsr 0x2100000f 553648143
- msp 0x20007fb0 536903600
- psp 0x20001120 536875296
- primask 0x0 0
- basepri 0xbe 190
- faultmask 0x0 0
- control 0x0 0
-
- (gdb) info breakpoints # info b / i b
- Num Type Disp Enb Address What
- 1 breakpoint keep y 0x0800144c Device/Source/ARM/startup_ac78xx.s:91
- breakpoint already hit 1 time
- 2 breakpoint keep y 0x08011172 in main at App/main.c:70
- breakpoint already hit 1 time
- 3 breakpoint keep y 0x08004622 in Show_CPU_Type
- at Bios/hw_init.c:173
- breakpoint already hit 1 time
-
- (gdb) info b 3
- Num Type Disp Enb Address What
- 3 breakpoint keep y 0x08004622 in Show_CPU_Type
- at Bios/hw_init.c:173
- breakpoint already hit 1 time
Break and Watch | |
---|---|
break funtion-name break line-number break ClassName::functionName | Suspend program at specified function of line number. |
break +offset break -offset | Set a breakpoint specified number of lines forward or back from the position at which execution stopped. |
break filename:function | Don't specify path, just the file name and function name. |
break filename:line-number | Don't specify path, just the file name and line number. break Directory/Path/filename.cpp:62 |
break *address | Suspend processing at an instruction address. Used when you do not have source. |
break line-number if condition | Where condition is an expression. i.e. x > 5 Suspend when boolean expression is true. |
watch condition | Suspend processing when condition is met. i.e. x > 5 |
clear clear function clear line-number | Delete breakpoints as identified by command option. Delete all breakpoints in function Delete breakpoints at a given line |
delete d | Delete all breakpoints, watchpoints, or catchpoints. |
delete breakpoint-number delete range | Delete the breakpoints, watchpoints, or catchpoints of the breakpoint ranges specified as arguments. |
disable breakpoint-number-or-range enable breakpoint-number-or-range | Does not delete breakpoints. Just enables/disables them. Example: Show breakpoints: info break Disable: disable 2-9 |
continue c | Continue executing until next break point/watchpoint. |
continue number | Continue but ignore current breakpoint number times. Usefull for breakpoints within a loop. |
finish | Continue to end of function. |
break命令
在gdb中用break命令(缩写b)来设置断点,设置断点的方法包括:
break <function> 在进入指定函数时停住,C++中可以使用class::function或function(type, type)格式来指定函数名。
- (gdb) b main
- Breakpoint 2 at 0x8011172: file App/main.c, line 70.
- Thread 2 hit Breakpoint 2, main () at App/main.c:70
- 70 CPU_Init();
- (gdb) l
- 65 * @brief main function
- 66 *
- 67 */
- 68 int main(void)
- 69 {
- 70 CPU_Init();
- 71
- 72 CPU_Running = 0x00;
break <linenum> 在指定行号停住。
- (gdb) b 81
- Reading 64 bytes @ address 0x08011140
- Read 2 bytes @ address 0x08011182 (Data = 0xF7F8)
- Breakpoint 3 at 0x8011182: file App/main.c, line 81.
break +offset / break -offset 在当前行号的前面或后面的offset行停住,offiset为自然数。
break filename:linenum 在源文件filename的linenum行处停住。
- (gdb) b interprt.c:630
- Reading 64 bytes @ address 0x08005440
- Read 4 bytes @ address 0x08005700 (Data = 0x20000BB8)
- Read 2 bytes @ address 0x080056B0 (Data = 0xF008)
- Breakpoint 6 at 0x80056b0: file Bios/interprt.c, line 630.
break filename:function 在源文件filename的function函数的入口处停住。
- (gdb) b interprt.c:interpreter
- Read 4 bytes @ address 0x08005700 (Data = 0x20000BB8)
- Read 2 bytes @ address 0x08005450 (Data = 0x4BAB)
- Breakpoint 7 at 0x8005450: file Bios/interprt.c, line 590.
break *address 在程序运行的内存地址处停住。
break break命令没有参数时,表示在下一条指令处停住。
break ... if <condition> “...”可以是上述的break <linenum>、break +offset / break –offset中的参数,condition表示条件,在条件成立时停住。比如在循环体中,可以设置break if i=100,表示当i为100时停住程序。
查看断点时,可使用info命令,如info breakpoints [n]、info break [n](n表示断点号)。
watch命令
watch一般来观察某个表达式(变量也是一种表达式)的值是否有变化了,如果有变化,马上停住程序。我们有下面的几种方法来设置观察点: watch <expr>:为表达式(变量)expr设置一个观察点。一量表达式值有变化时,马上停住程序。rwatch <expr>:当表达式(变量)expr被读时,停住程序。awatch <expr>:当表达式(变量)的值被读或被写时,停住程序。info watchpoints:列出当前所设置了的所有观察点。 下面演示了观察i并在连续运行next时一旦发现i变化,i值就会显示出来的过程:
- (gdb) watch i
- Hardware watchpoint 3: i
- (gdb) next
- 23 for (i = 0; i < 10; i++)
- (gdb) next
- Hardware watchpoint 3: i
-
- Old value = 0
- New value = 1
- 0x0804838d in main () at gdb_example.c:23
- 23 for (i = 0; i < 10; i++)
- (gdb) next
-
- Breakpoint 1, main () at gdb_example.c:25
- 25 sum[i] = add(array1[i], array2[i]);
- (gdb) next
- 23 for (i = 0; i < 10; i++)
- (gdb) next
- Hardware watchpoint 3: i
-
- Old value = 1
- New value = 2
- 0x0804838d in main () at gdb_example.c:23
- 23 for (i = 0; i < 10; i++)
continue命令
当程序被停住后,可以使用continue命令(缩写c,fg命令同continue命令)恢复程序的运行直到程序结束,或到达下一个断点,命令格式为:
- continue [ignore-count]
- c [ignore-count]
- fg [ignore-count]
ignore-count表示忽略其后多少次断点。 假设我们设置了函数断点add(),并watch i,则在continue过程中,每次遇到add()函数或i发生变化,程序就会停住,如:
- (gdb) continue
- Continuing.
- Hardware watchpoint 3: i
-
- Old value = 2
- New value = 3
- 0x0804838d in main () at gdb_example.c:23
- 23 for (i = 0; i < 10; i++)
- (gdb) continue
- Continuing.
-
- Breakpoint 1, main () at gdb_example.c:25
- 25 sum[i] = add(array1[i], array2[i]);
- (gdb) continue
- Continuing.
- Hardware watchpoint 3: i
-
- Old value = 3
- New value = 4
- 0x0804838d in main () at gdb_example.c:23
- 23 for (i = 0; i < 10; i++)
finish命令
运行程序,直到当前函数完成返回,并打印函数返回时的堆栈地址和返回值及参数值等信息。
- Breakpoint 4, CPU_Init () at Bios/hw_init.c:71 #断点在CPU_Init
- 71 disable_WDOG();
- (gdb) finish
- Read 4 bytes @ address 0x08011176 (Data = 0x22004B54)
- Run till exit from #0 CPU_Init () at Bios/hw_init.c:71
- Read 2 bytes @ address 0x08011176 (Data = 0x4B54)
- Setting breakpoint @ address 0x0800144C, Size = 2, BPHandle = 0x0009
- Setting breakpoint @ address 0x08011172, Size = 2, BPHandle = 0x000A
- Setting breakpoint @ address 0x08011176, Size = 2, BPHandle = 0x000B
- Setting breakpoint @ address 0x0801128C, Size = 2, BPHandle = 0x000C
- Setting breakpoint @ address 0x08017EF4, Size = 2, BPHandle = 0x000D
- Performing single step...
- ...Breakpoint reached @ address 0x0800457C
- Reading all registers
- Setting breakpoint @ address 0x0800458C, Size = 2, BPHandle = 0x000E
- Starting target CPU...
- ...Breakpoint reached @ address 0x08011176
- Reading all registers
- Read 4 bytes @ address 0x08011176 (Data = 0x22004B54)
- Reading 64 bytes @ address 0x20007FC0
- Removing breakpoint @ address 0x08011176, Size = 2
- Removing breakpoint @ address 0x08017EF4, Size = 2
- Removing breakpoint @ address 0x0800144C, Size = 2
- Removing breakpoint @ address 0x0800458C, Size = 2
- Removing breakpoint @ address 0x08011172, Size = 2
- Removing breakpoint @ address 0x0801128C, Size = 2
- Read 4 bytes @ address 0x08011176 (Data = 0x22004B54)
- main () at App/main.c:72
- 72 CPU_Running = 0x00;
- (gdb)
Line Execution | |
---|---|
step s step number-of-steps-to-perform | Step to next line of code. Will step into a function. |
next n next number | Execute next line of code. Will not enter functions. |
until until line-number | Continue processing until you reach a specified line number. Also: function name, address, filename:function or filename:line-number. |
Machine Language | |
---|---|
info line info line number | Displays the start and end position in object code for the current line in source. Display position in object code for a specified line in source. |
disassemble 0xstart 0xend | Displays machine code for positions in object code specified (can use start and end hex memory values given by the info line command. |
stepi si nexti ni | step/next assembly/processor instruction. |
x 0xaddress x/nfu 0xaddress | Examine the contents of memory. Examine the contents of memory and specify formatting. n: number of display items to print f: specify the format for the output u: specify the size of the data unit (eg. byte, word, ...) Example: x/4dw var |
在调试过程中,next命令用于单步执行,类似VC++中的step over。next的单步不会进入函数的内部,与next对应的step(缩写s)命令则在单步执行一个函数时,会进入其内部,类似VC++中的step into。下面演示了step命令的执行情况,在23行的add()函数调用处执行step会进入其内部的“return a+b;”语句:
- (gdb) break 25
- Breakpoint 1 at 0x8048362: file gdb_example.c, line 25.
- (gdb) run
- Starting program: /driver_study/gdb_example
-
- Breakpoint 1, main () at gdb_example.c:25
- 25 sum[i] = add(array1[i], array2[i]);
- (gdb) step
- add (a=48, b=85) at gdb_example.c:3
- 3 return a + b;
单步执行的更复杂用法包括:
step命令
当程序被停住后,可以使用step命令单步跟踪(缩写s)。单步跟踪,如果有函数调用,则***进入该函数***(进入函数的前提是,此函数被编译有debug信息)。step后面不加count表示一条条地执行,加表示执行后面的count条指令,然后再停住。
- Breakpoint 5, main () at App/main.c:84
- 84 msgScript(AUTORUNSCRIPT, 0, 200);
- (gdb) info line
- Line 84 of "App/main.c" starts at address 0x8011186 <main+26>
- and ends at 0x8011192 <main+38>.
- (gdb) l # 查看main函数断点在84行
- 79
- 80 // start default mbc channels
- 81 Init_Gateway_Standard();
- 82
- 83 #if (SW_SCRIPT == 1)
- 84 msgScript(AUTORUNSCRIPT, 0, 200);
- 85
- 86 // run msgPoll till both autorun are executed; force broadcast to all known interfaces
- 87 while (msgPoll(true))
- 88 {};
- (gdb) l msgScript #查看 msgScript 函数内容
- 1515 //--------------------------------------------------------------------------------------------------------
- 1516 // Input:
- 1517 // Output: none
- 1518 //********************************************************************************************************
- 1519 unsigned msgScript(uint32_t auto_packet, uint32_t auto_flash, uint32_t auto_delay)
- 1520 {
- 1521 static unsigned state = S_INIT;
- 1522 static unsigned cnt = 0;
- 1523 static uint32_t time;
- 1524 static uint32_t script;
- (gdb) l
- 1525 static uint32_t script_next;
- 1526 static uint32_t script_delay;
- 1527 unsigned error;
- 1528
- 1529 switch (state)
- 1530 {
- 1531 case S_INIT: // init
- 1532 script = auto_packet;
- 1533 script_next = auto_flash;
- 1534 script_delay = auto_delay;
- (gdb)
-
- (gdb) s # 单步调试进入 msgScript函数
- Setting breakpoint @ address 0x08017EF4, Size = 2, BPHandle = 0x001C
- Performing single step...
- ...Breakpoint reached @ address 0x08011188
- Reading all registers
- ......
- ......
- msgScript (auto_packet=134331756, auto_flash=0, auto_delay=200)
- at Bios/comm_mbc.c:1529
- 1529 switch (state) #停在msgScript函数的第一条代码
next命令
当程序被停住后,可以使用next命令单步跟踪(缩写n)。单步跟踪,如果有函数调用,不会进入该函数。同样地,next后面不加count表示一条条地执行,加表示执行后面的count条指令,然后再停住。
- (gdb) l 117 #显示当前函数执行位置内容
- 113 hprintf(" Built on %s at %s.\n", __DATE__, __TIME__);
- 114 hprintf(" Startaddress : 0x%08lX\n", BiosVersionInfo.Programstart);
- 115 hprintf(" CPU Clock : %dHz\n", __SYSTEM_CLOCK);
- 116
- 117 Show_CPU_Type(1);
- 118 hprintf("\n");
- 119
- 120 ADCDrv_Init();
- (gdb)
- (gdb) n # 单步调试,跟踪下一行
- Setting breakpoint @ address 0x08011186, Size = 2, BPHandle = 0x0023
- ......
- ......
- Read 4 bytes @ address 0x08011260 (Data = 0xF7F9481B)
- 118 hprintf("\n");
- (gdb) n # 单步调试,跟踪下一行
- Setting breakpoint @ address 0x08011186, Size = 2, BPHandle = 0x0027
- ......
- ......
- Read 4 bytes @ address 0x08011266 (Data = 0xFC49F7F9)
- 121 ADCDrv_Init();
- (gdb)
until命令
一直在循环体内执行单步,退不出来是一件令人烦恼的事情,until命令可以运行程序直到退出循环体。
- Breakpoint 3, Get_Portproperties (channel=3 '\003',
- port_isvalid_mask=0x20007f80, port_isgpio_mask=0x20007f7c)
- at Bios/port.c:86
- 86 *port_isgpio_mask = 0x00000000;
- (gdb) l
- 81 // but may be configured as alternate function
- 82 *port_isvalid_mask = (uint32_t)(((uint32_t)1 << productinfo[Cputype].portn_pin_count[channel]) -1);
- 83
- 84 // create info on gpio function
- 85 // every bitnumber is portpin, which is configured as gpio
- 87 for (i=0; i < GPIO_ONE_GROUP_NUM; i++)
- 88 { // scan all bits
- 89 if ((*port_isvalid_mask & (0x00000001 << i)) != 0)
- 90 { // portpin is existing, now additional check wether configured as gpio
- 91
- 92 if ((GPIO_GetPinFunc(channel*GPIO_ONE_GROUP_NUM + i)) == 0)
- 93 { // mark portpin as gpio pin
- 94 *port_isgpio_mask |= 0x00000001 << i;
- 95 }
- 96 }
- 97 }
- 98
- 99 return (TRUE);
- 100 }
- (gdb) until 99 #直到停在Bios/port.c的99行
- Reading 64 bytes @ address 0x0800D580
- Read 2 bytes @ address 0x0800DE10 (Data = 0x4603)
- Read 2 bytes @ address 0x0800D630 (Data = 0x2301)
- ......
- ......
- Read 4 bytes @ address 0x0800D630 (Data = 0x46182301)
- Read 4 bytes @ address 0x0800DE10 (Data = 0xF0834603)
- Reading 64 bytes @ address 0x20007F80
- Get_Portproperties (channel=3 '\003', port_isvalid_mask=0x20007f80,
- port_isgpio_mask=0x20007f7c) at Bios/port.c:99
- 99 return (TRUE);
stepi和nexti命令
stepi和nexti用于单步跟踪一条机器指令,一条程序代码有可能由数条机器指令完成,stepi和nexti可以单步执行机器指令。 另外,运行“display/i $pc”命令后,单步跟踪会在打出程序代码的同时打出机器指令,即汇编代码。
- (gdb) display/i $pc # 单步跟踪打出程序汇编代码
- Reading 64 bytes @ address 0x0800D600
- 1: x/i $pc
- => 0x800d630 <Get_Portproperties+180>: movs r3, #1
- (gdb) si # 单步执行机器指令
- Setting breakpoint @ address 0x0800D5E0, Size = 2, BPHandle = 0x000C
- Setting breakpoint @ address 0x08011172, Size = 2, BPHandle = 0x000D
- Performing single step...
- ...Breakpoint reached @ address 0x0800D634
- Reading all registers
- Read 4 bytes @ address 0x0800D634 (Data = 0x46BD3718)
- Reading 64 bytes @ address 0x20007F40
- Removing breakpoint @ address 0x0800D5E0, Size = 2
- Removing breakpoint @ address 0x08011172, Size = 2
- Read 4 bytes @ address 0x0800D634 (Data = 0x46BD3718)
- 0x0800d634 100 }
- Reading 64 bytes @ address 0x0800D600
- 1: x/i $pc
- => 0x800d634 <Get_Portproperties+184>: adds r7, #24
- (gdb) ni # 单步执行机器指令
- Setting breakpoint @ address 0x0800D5E0, Size = 2, BPHandle = 0x000E
- Setting breakpoint @ address 0x08011172, Size = 2, BPHandle = 0x000F
- Setting breakpoint @ address 0x08017EF4, Size = 2, BPHandle = 0x0010
- Performing single step...
- ...Breakpoint reached @ address 0x0800D636
- Reading all registers
- Read 4 bytes @ address 0x0800D636 (Data = 0xBD8046BD)
- Reading 64 bytes @ address 0x20007F40
- Removing breakpoint @ address 0x08017EF4, Size = 2
- Removing breakpoint @ address 0x0800D5E0, Size = 2
- Removing breakpoint @ address 0x08011172, Size = 2
- Read 4 bytes @ address 0x0800D636 (Data = 0xBD8046BD)
- 0x0800d636 100 }
- Reading 64 bytes @ address 0x0800D600
- 1: x/i $pc
- => 0x800d636 <Get_Portproperties+186>: mov sp, r7
- (gdb)
Source Code | |
---|---|
list l list line-number list function list - list start#,end# list filename:function | List source code. |
list命令
在gdb中运行list命令(缩写l)可以列出代码,list的具体形式包括:
list <linenum> ,显示程序第linenum行周围的源程序,如:
- (gdb) list 15
- 10
- 11 int array1[10] =
- 12 {
- 13 48, 56, 77, 33, 33, 11, 226, 544, 78, 90
- 14 };
- 15 int array2[10] =
- 16 {
- 17 85, 99, 66, 0x199, 393, 11, 1, 2, 3, 4
- 18 };
- 19
list <function> ,显示函数名为function的函数的源程序,如:
- (gdb) list main
- 2 {
- 3 return a + b;
- 4 }
- 5
- 6 main()
- 7 {
- 8 int sum[10];
- 9 int i;
- 10
- 11 int array1[10] =
- list,显示当前行后面的源程序。
- list - ,显示当前行前面的源程序。
下面演示了使用gdb中的run(缩写r)、break(缩写b)、next(缩写n)命令控制程序的运行,并使用print(缩写p)命令打印程序中的变量sum的过程:
- (gdb) break add
- Breakpoint 1 at 0x80482f7: file gdb_example.c, line 3.
- (gdb) run
- Starting program: /driver_study/gdb_example
-
- Breakpoint 1, add (a=48, b=85) at gdb_example.c:3
- warning: Source file is more recent than executable.
-
- 3 return a + b;
- (gdb) next
- 4 }
- (gdb) next
- main () at gdb_example.c:23
- 23 for (i = 0; i < 10; i++)
- (gdb) next
- 25 sum[i] = add(array1[i], array2[i]);
- (gdb) print sum
- $1 = {133, 0, 0, 0, 0, 0, 0, 0, 0, 0}
- gdb) break add
- Breakpoint 1 at 0x80482f7: file gdb_example.c, line 3.
- (gdb) run
- Starting program: /driver_study/gdb_example
-
- Breakpoint 1, add (a=48, b=85) at gdb_example.c:3
- warning: Source file is more recent than executable.
-
- 3 return a + b;
- (gdb) next
- 4 }
- (gdb) next
- main () at gdb_example.c:23
- 23 for (i = 0; i < 10; i++)
- (gdb) next
- 25 sum[i] = add(array1[i], array2[i]);
- (gdb) print sum
- $1 = {133, 0, 0, 0, 0, 0, 0, 0, 0, 0}
Examine Variables | |
---|---|
print variable-name p variable-name p file-name::variable-name p 'file-name'::variable-name | Print value stored in variable. |
p *array-variable@length | Print first # values of array specified by length. Good for pointers to dynamicaly allocated memory. |
p/x variable | Print as integer variable in hex. |
p/d variable | Print variable as a signed integer. |
p/u variable | Print variable as a un-signed integer. |
p/o variable | Print variable as a octal. |
p/t variable x/b address x/b &variable | Print as integer value in binary. (1 byte/8bits) |
p/c variable | Print integer as character. |
p/f variable | Print variable as floating point number. |
p/a variable | Print as a hex address. |
x/w address x/4b &variable | Print binary representation of 4 bytes (1 32 bit word) of memory pointed to by address. |
ptype variable ptype data-type | Prints type definition of the variable or declared variable type. Helpful for viewing class or struct definitions while debugging. |
print命令
在调试程序时,当程序被停住时,可以使用print命令(缩写为p),或是同义命令inspect来查看当前程序的运行数据。print命令的格式是:
- print <expr>
- print /<f> <expr>
<expr>是表达式,是被调试的程序中的表达式,<f>是输出的格式,比如,如果要把表达式按16进制的格式输出,那么就是/x。在表达式中,有几种GDB所支持的操作符,它们可以用在任何一种语言中,“@”是一个和数组有关的操作符,“::”指定一个在文件或是函数中的变量,“{<type>} <addr>”表示一个指向内存地址<addr>的类型为type的一个对象。
下面演示了查看sum[]数组的值的过程:
- (gdb) print sum
- $2 = {133, 155, 0, 0, 0, 0, 0, 0, 0, 0}
- (gdb) next
-
- Breakpoint 1, main () at gdb_example.c:25
- 25 sum[i] = add(array1[i], array2[i]);
- (gdb) next
- 23 for (i = 0; i < 10; i++)
- (gdb) print sum
- $3 = {133, 155, 143, 0, 0, 0, 0, 0, 0, 0}
当需要查看一段连续内存空间的值的时间,可以使用GDB的“@”操作符,“@”的左边是第一个内存地址,“@”的右边则是想查看内存的长度。例如如下动态申请的内存:
- int *array = (int *) malloc (len * sizeof (int));
- *array = (int *) malloc (len * sizeof (int));
在GDB调试过程中这样显示出这个动态数组的值:
- p *array@len
- *array@len
print的输出格式包括:
- x 按十六进制格式显示变量。
- d 按十进制格式显示变量。
- u 按十六进制格式显示无符号整型。
- o 按八进制格式显示变量。
- t 按二进制格式显示变量。
- a 按十六进制格式显示变量。
- c 按字符格式显示变量。
- f 按浮点数格式显示变量。
我们可用display命令设置一些自动显示的变量,当程序停住时,或是单步跟踪时,这些变量会自动显示。 如果要修改变量,如x的值,可使用如下命令:
- print x=4
- x=4
当用GDB的print查看程序运行时的数据时,每一个print都会被GDB记录下来。GDB会以$1,$2,$3 …这样的方式为每一个print命令编号。我们可以使用这个编号访问以前的表达式,如$1。
Stack | |
---|---|
backtrace bt bt inner-function-nesting-depth bt -outer-function-nesting-depth | Show trace of where you are currently. Which functions you are in. Prints stack backtrace. |
backtrace full | Print values of local variables. |
frame frame number f number | Show current stack frame (function where you are stopped) Select frame number. (can also user up/down to navigate frames) |
up down up number down number | Move up a single frame (element in the call stack) Move down a single frame Move up/down the specified number of frames in the stack. |
info frame | List address, language, address of arguments/local variables and which registers were saved in frame. |
backtrace命令
backtrace
命令用于打印当前调试环境中所有栈帧的信息,常用的语法格式如下:
(gdb) backtrace [-full] [n]
其中,用 [ ] 括起来的参数为可选项,它们的含义分别为:
n:一个整数值,当为正整数时,表示打印最里层的 n 个栈帧的信息;n为负整数时,那么表示打印最外层n个栈帧的信息;
-full:打印栈帧信息的同时,打印出局部变量的值。
注意,当调试多线程程序时,该命令仅用于打印当前线程中所有栈帧的信息。如果想要打印所有线程的栈帧信息,应执行thread apply all backtrace命令。
- (gdb) bt
- #0 0x0800d636 in Get_Portproperties (channel=3 '\003',
- port_isvalid_mask=0x20007f80, port_isgpio_mask=0x20007f7c)
- at Bios/port.c:100
- #1 0x0800de10 in Port_WR_32bit () at Bios/port.c:619
- #2 0x0800d8b8 in Port_WR () at Bios/port.c:285
- #3 0x080056a8 in interpreter () at Bios/interprt.c:624
- #4 0x080079a4 in msgPoll (broadcast=true) at Bios/comm_mbc.c:1719
- #5 0x0801119a in main () at App/main.c:87
- (gdb) backtrace full
- #0 0x0800d636 in Get_Portproperties (channel=3 '\003',
- port_isvalid_mask=0x20007f80, port_isgpio_mask=0x20007f7c)
- at Bios/port.c:100
- i = 16 '\020'
- #1 0x0800de10 in Port_WR_32bit () at Bios/port.c:619
- Read 4 bytes @ address 0x0800D8B8 (Data = 0x4B64E0C4)
- Reading 64 bytes @ address 0x20007FC0
- error = 0
- gpio = 0x800a6cb <hprintf+22>
- port_isvalid_mask = 7
- port_isgpio_mask = 1
- port_mask = 134320932
- channel = 3 '\003'
- port_bit = 32 ' '
- port_altfunc = 0 '\000'
- port_intfunc = 32 ' '
- i = 536903588
- #2 0x0800d8b8 in Port_WR () at Bios/port.c:285
- Read 4 bytes @ address 0x080056A8 (Data = 0xF007E027)
- channel_old_command = 32 ' '
- channel_new_command = 13 '\r'
- channel_shift = 0
- command = 0 '\000'
- param1 = 536874464
- param2 = 0
- port_isvalid_mask = 536878716
- port_isgpio_mask = 0
- #3 0x080056a8 in interpreter () at Bios/interprt.c:624
- Read 4 bytes @ address 0x080079A4 (Data = 0x7BFBBF00)
- Read 4 bytes @ address 0x0801119A (Data = 0x2B004603)
- No locals.
- #4 0x080079a4 in msgPoll (broadcast=true) at Bios/comm_mbc.c:1719
- ch = 0 '\000'
- txMsg = "\020\006"
- #5 0x0801119a in main () at App/main.c:87
- Read 4 bytes @ address 0x0800147A (Data = 0xB3AC4770)
- Reading 64 bytes @ address 0x08
frame 命令
frame
命令的常用形式有 2 个:
根据栈帧编号或者栈帧地址,选定要查看的栈帧,语法格式如下:
(gdb) frame spec
该命令可以将 spec 参数指定的栈帧选定为当前栈帧。spec 参数的值,常用的指定方法有 3 种:
通过栈帧的编号指定。0 为当前被调用函数对应的栈帧号,最大编号的栈帧对应的函数通常就是 main() 主函数; 借助栈帧的地址指定。栈帧地址可以通过 info frame 命令(后续会讲)打印出的信息中看到; 通过函数的函数名指定。注意,如果是类似递归函数,其对应多个栈帧的话,通过此方法指定的是编号最小的那个栈帧。 除此之外,对于选定一个栈帧作为当前栈帧,GDB 调试器还提供有up 和down两个命令。其中,up命令的语法格式为:
- (gdb) up n
-
- (gdb) bt
- #0 0x0800d636 in Get_Portproperties (channel=3 '\003',
- port_isvalid_mask=0x20007f80, port_isgpio_mask=0x20007f7c)
- at Bios/port.c:100
- #1 0x0800de10 in Port_WR_32bit () at Bios/port.c:619
- #2 0x0800d8b8 in Port_WR () at Bios/port.c:285
- #3 0x080056a8 in interpreter () at Bios/interprt.c:624
- #4 0x080079a4 in msgPoll (broadcast=true) at Bios/comm_mbc.c:1719
- #5 0x0801119a in main () at App/main.c:87
- (gdb) frame
- #2 0x0800d8b8 in Port_WR () at Bios/port.c:285
- 285 Port_WR_32bit();
- (gdb) up 1
- #3 0x080056a8 in interpreter () at Bios/interprt.c:624
- 624 case PORT_SCHREIBEN: Port_WR();
其中 n为整数,默认值为 1。该命令表示在当前栈帧编号(假设为 m)的基础上,选定 m+n为编号的栈帧作为新的当前栈帧。
相对地,down 命令的语法格式为:
- (gdb) down n
-
- (gdb) bt
- #0 0x0800d636 in Get_Portproperties (channel=3 '\003',
- port_isvalid_mask=0x20007f80, port_isgpio_mask=0x20007f7c)
- at Bios/port.c:100
- #1 0x0800de10 in Port_WR_32bit () at Bios/port.c:619
- #2 0x0800d8b8 in Port_WR () at Bios/port.c:285
- #3 0x080056a8 in interpreter () at Bios/interprt.c:624
- #4 0x080079a4 in msgPoll (broadcast=true) at Bios/comm_mbc.c:1719
- #5 0x0801119a in main () at App/main.c:87
- (gdb) frame
- #2 0x0800d8b8 in Port_WR () at Bios/port.c:285
- 285 Port_WR_32bit();
- (gdb) down 1
- #1 0x0800de10 in Port_WR_32bit () at Bios/port.c:619
- 619 if (!Get_Portproperties (channel, &port_isvalid_mask, &port_isgpio_mask))
- (gdb)
其中n为整数,默认值为 1。该命令表示在当前栈帧编号(假设为 m)的基础上,选定m-n 为编号的栈帧作为新的当前栈帧。
借助如下命令,我们可以查看当前栈帧中存储的信息:
- (gdb) info frame
- Reading 64 bytes @ address 0x0800D840
- Read 4 bytes @ address 0x0800DA4E (Data = 0xB5802000)
- Reading register (MSP = 0x20007F58)
- Reading register (PSP = 0x 0)
- Reading register (PRIMASK = 0x 0)
- Reading register (BASEPRI = 0x 0)
- Reading register (FAULTMASK = 0x 0)
- Reading register (CONTROL = 0x 0)
- Stack level 2, frame at 0x20007fc8:
- pc = 0x800d8b8 in Port_WR (port.c:285); saved pc = 0x80056a8
- called by frame at 0x20007fd0, caller of frame at 0x20007fa8
- source language c.
- Arglist at 0x20007fa8, args:
- Locals at 0x20007fa8, Previous frame's sp is 0x20007fc8
- Saved registers:
- r7 at 0x20007fc0, lr at 0x20007fc4
- (gdb)
该命令会依次打印出当前栈帧的如下信息:
- 当前栈帧的编号,以及栈帧的地址;
- 当前栈帧对应函数的存储地址,以及该函数被调用时的代码存储的地址
- 当前函数的调用者,对应的栈帧的地址;
- 编写此栈帧所用的编程语言;
- 函数参数的存储地址以及值;
- 函数中局部变量的存储地址;
- 栈帧中存储的寄存器变量,例如指令寄存器(64位环境中用 rip 表示,32为环境中用eip 表示)、堆栈基指针寄存器(64位环境用 rbp表示,32位环境用 ebp表示)等。
除此之外,还可以使用info args命令查看当前函数各个参数的值;使用info locals命令查看当前函数中各局部变量的值。
- (gdb) l Port_WR
- 255 //* Quality : (x) not tested ( ) partly tested ( ) fully tested *
- 256 //****************************************************************************
- 257
- 258 #if (CP_PORT_RW == 2)
- 259 void Port_WR (void)
- 260 {
- 261 //*--------------------------------------------------------------------------*
- 262 //* Local variables *
- 263 //*--------------------------------------------------------------------------*
- 264
- (gdb) l
- 265 uint8_t channel_old_command;
- 266 uint8_t channel_new_command;
- 267 uint32_t channel_shift;
- 268 uint8_t command;
- 269 uint32_t param1, param2;
- 270 uint32_t port_isvalid_mask;
- 271 uint32_t port_isgpio_mask;
- 272
- (gdb) info locals # 查看当前函数各局部变量的值
- Read 4 bytes @ address 0x080056A8 (Data = 0xF007E027)
- Reading 64 bytes @ address 0x20007F80
- channel_old_command = 32 ' '
- channel_new_command = 13 '\r'
- channel_shift = 0
- command = 0 '\000'
- param1 = 536874464
- param2 = 0
- port_isvalid_mask = 536878716
- port_isgpio_mask = 0
Linux Tutorial - GNU GDB Debugger Command Cheat Sheet (yolinux.com)
GDB调试命令详解
宋宝华的Linux gdb调试器用法全面解析
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。