Embedded Systems Architecture
上QQ阅读APP看书,第一时间看更新

The GDB session

Regardless of the complexity of the project we are working on, most of the development time will be spent trying to understand what our software does, or most likely, what has gone wrong and why the software is not behaving as we would expect when the code was written the first time. The debugger is the most powerful tool in our toolchain, allowing us to communicate directly with the CPU, place breakpoints, control the execution flow instruction by instruction, and check the values of CPU registers, local variables, and memory areas. A good knowledge of the debugger means less time spent trying to figure out what is going on, and a more effective hunt for bugs and defects.

The arm-none-eabi toolchain includes a GDB capable of interpreting the memory and the register layout of the remote target, and can be accessed with the same interfaces as the host GDB, provided that its backend can communicate with the embedded platform, using OpenOCD or a similar host tool providing communication with the target through the GDB server protocol. As previously described, OpenOCD can be configured to provide a GDB server interface, which in the proposed configuration is on port 3333.

After starting arm-none-eabi-gdb, we may connect to the running tool using the GDB target command. Connecting to the GDB server while OpenOCD is running can be done using the target command:

> target remote localhost:3333

All GDB commands can be abbreviated, so the command often becomes:

> tar rem :3333

Once connected, the target would typically stop the execution, allowing GDB to retrieve the information about the instruction that is being currently executed, the stack trace, and the values of the CPU registers.

From this moment on, the debugger interface can be used normally to step through the code, place breakpoints and watchpoints, and inspect and alter CPU registers and writable memory areas at runtime.

GDB can be used entirely from its command-line interface, using shortcuts and commands to start and stop the execution, and access memory and registers.

The following reference table enumerates a few of the GDB commands available in a debug session, and provides a quick explanation of their usage:

Command Description
file name Load all the symbols from the ELF file on the host filesystem. If the ELF file has been compiled with the GCC -g option, it will contain all the data to facilitate debugging.
load Upload the currently loaded symbols to the target. Used to flash new versions of the software during a debug session.
mon Access monitor commands, platform-specific. The OpenOCD monitor interfaces provide commands, such as mon reset and mon init, to power-cycle the core and initialize the execution.

Break (b)

b function_name

b line_number

b file.c:line_num

b address

Place a breakpoint in the code, at the specified location. The location can be specified in different ways. When the execution hits the instruction with the breakpoint, the CPU is temporarily stopped and the control returns to the debugger for inspection.
Watch (w) address Like break, but instead of placing a breakpoint on a specific location in the code, observe a variable in memory at the given address and interrupt the execution whenever the value changes. Useful to trace the point in the code where a specific value in the memory is altered.
info b Provide information about the breakpoints and watchpoints currently set in this debugging session.
delete (d) n Remove breakpoint n.
print (p) ... Inspect the value of a variable, or a C expression that can be evaluated using variables and memory addresses.
display ... Like print, but refresh the value of the expression every time that the control returns to the debugger, useful to track the changes while stepping through the code.
Next (n) Execute the next instruction. If the next instruction is a function call, place an automatic breakpoint on the function return, and return the control to the debugger only when the called function has returned.
Step (s) Execute the next instruction. If the next instruction is a function call, place an automatic breakpoint on the function return, and return the control to the debugger only when the called function has returned.
Stepi (si) Like step, but only execute the next machine instruction instead of the whole C line that generated it. Useful to step through single machine instructions.
Continue (c) Resume the execution on the target from the current position. The control is returned to the debugger if a breakpoint is hit, or if Ctrl C is pressed from the GDB console.

set var=...

set $cpu_reg=...

Assign the desired value (or the result of an expression) to a variable or a CPU register, referenced using the $ symbol.
Backtrace (bt) Inspect the contents of the stack in the reverse order, including the call trace to the current location.

up

down

Move the stack pointer through the current call trace, to switch to the context of the caller toward outer functions use up, to switch back to inner functions use down.

GDB is a very powerful and complete debugger, and the commands that have been shown in this section are a small portion of its actual potential. The reader is advised to discover the other features offered by GDB by going through its manual, in order to find the set of commands that best fit their needs.