ARM GDB调试器常用命令用法解析

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 要求的可执行文件

帮助信息与基本信息命令

CommandDescription
help <command>Command description. eg help show to list the show commands
info breakpoints/b/breakList breakpoints
info break <breakpoint-number>List info about specific breakpoint.
info registers/rList 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::functionNameSuspend program at specified function of line number.
break +offset break -offsetSet a breakpoint specified number of lines forward or back from the position at which execution stopped.
break filename:functionDon't specify path, just the file name and function name.
break filename:line-numberDon't specify path, just the file name and line number. break Directory/Path/filename.cpp:62
break *addressSuspend processing at an instruction address. Used when you do not have source.
break line-number if conditionWhere condition is an expression. i.e. x > 5 Suspend when boolean expression is true.
watch conditionSuspend processing when condition is met. i.e. x > 5
clear clear function clear line-numberDelete breakpoints as identified by command option. Delete all breakpoints in function Delete breakpoints at a given line
delete dDelete all breakpoints, watchpoints, or catchpoints.
delete breakpoint-number delete rangeDelete the breakpoints, watchpoints, or catchpoints of the breakpoint ranges specified as arguments.
disable breakpoint-number-or-range enable breakpoint-number-or-rangeDoes not delete breakpoints. Just enables/disables them. Example: Show breakpoints: info break Disable: disable 2-9
continue cContinue executing until next break point/watchpoint.
continue numberContinue but ignore current breakpoint number times. Usefull for breakpoints within a loop.
finishContinue 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-performStep to next line of code. Will step into a function.
next n next numberExecute next line of code. Will not enter functions.
until until line-numberContinue processing until you reach a specified line number. Also: function name, address, filename:function or filename:line-number.
Machine Language
info line info line numberDisplays 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 0xendDisplays 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 nistep/next assembly/processor instruction.
0xaddress x/nfu 0xaddressExamine 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:functionList 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-namePrint value stored in variable.
p *array-variable@lengthPrint first # values of array specified by length. Good for pointers to dynamicaly allocated memory.
p/x variablePrint as integer variable in hex.
p/d variablePrint variable as a signed integer.
p/u variablePrint variable as a un-signed integer.
p/o variablePrint variable as a octal.
p/t variable x/b address x/b &variablePrint as integer value in binary. (1 byte/8bits)
p/c variablePrint integer as character.
p/f variablePrint variable as floating point number.
p/a variablePrint as a hex address.
x/w address x/4b &variablePrint binary representation of 4 bytes (1 32 bit word) of memory pointed to by address.
ptype variable ptype data-typePrints 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-depthShow trace of where you are currently. Which functions you are in. Prints stack backtrace.
backtrace fullPrint values of local variables.
frame frame number f numberShow current stack frame (function where you are stopped) Select frame number. (can also user up/down to navigate frames)
up down up number down numberMove 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 frameList 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调试器用法全面解析

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值