gdb remote server
The pyocd gdbserver
subcommand runs a gdb remote serial protocol (RSP) server that allows gdb to debug embedded targets.
To run a debug session, the pyOCD gdbserver needs to first be started. Then gdb connects to pyOCD and debugging can begin.
Running the gdbserver
The usual way to start the gdbserver is by running pyocd gdbserver
(or pyocd gdb
) on the command line. The gdbserver will remain available as long as the pyocd
process is running.
If using an IDE like Microsoft Visual Studio Code or Eclipse Embedded with plugins that support pyOCD, the gdbserver will automatically be started when you launch a debug session. Alternatively, the plugin can be configured to connect to an already-running gdbserver.
From the commander, a gdbserver can be run with the gdbserver
command.
The default gdbserver TCP/IP port number is 333. This can be changed using the -p
/ --port
or the gdbserver_port
session option.
Multicore targets
pyOCD always runs separate gdbservers for each core on multicore devices. This requires separate gdb instances and debug sessions for the cores being debugged, but is the most reliable method since gdb doesn’t support heterogenous multicore.
By default, a gdbserver is started for all cores on the target. This can be changed using the --core
argument, which takes a comma-separated list of core numbers.
The TCP/IP port numbers for each core’s gdbserver are determined by adding the core number to the base port number (see above). For a dual core device, core 0 uses the default base port of 3333, and core 1 uses port 3334.
See the multicore debug documentation for more information about multicore targets.
Connecting from gdb
To connect gdb to pyOCD’s gdbserver, use the target remote <host>:<port>
command. These take the server’s host name and port number separated by a colon as an argument. For a server running on the same host as gdb, using the default pyOCD port (see above for how to change it), this will be target remote localhost:3333
.
PyOCD also support extended remote mode. In this case, the connect command is target extended-remote <host>:<port>
. Extended remote mode allows using the gdb disconnect
command to disconnect from pyOCD while keeping the gdbserver running.
After connecting gdb, perform the following steps to program updated firmware and run the new code.
-
Program firmware using the
load
command. -
Reset the target and halt with
monitor reset halt
. This halts the core at the first instruction. -
Set breakpoints and resume, or use a command line
until main
to run to the first line ofmain()
.
Gdbserver exit
The pyOCD gdbserver process will normally exit automatically when gdb detaches using any of the detach
, kill
, or disconnect
commands. This can be changed with the --persist
argument or persist
session option, so that the gdbserver always remains running until terminated directly (by Control-C or equivalent signal).
When gdb connects in extended remote mode, the gdb disconnect
command will detach gdb but keep the gdbserver running even if persist isn’t enabled.
Useful commands
Gdb by default restricts memory accesses to regions defined in the target memory map provided by pyOCD. This has the unhappy side effect of preventing access to peripheral registers or other Device memory, since the memory maps do not include those regions.
To work around this, disable gbd’s mem inaccessible-by-default
setting.
This line can be added into your .gdbinit
file:
(gdb) set mem inaccessible-by-default off
It can also be useful to add an alias to make monitor commands (below) easier to access:
(gdb) alias m = monitor
To catch crashes and unexpected exceptions, use set vector-catch
to enable the M-profile vector catch feature:
(gdb) monitor set vector-catch all
If the core halts in an exception handler, use show fault
to print out the M-profile fault syndrome registers.
(gdb) monitor show fault
For most targets, peripheral registers can be accessed using pyOCD’s reg
and wreg
commands.
(gdb) monitor reg TIM21.PSC
Enabling access to all memory as described above is required for this to work.
Monitor commands
Commands can be sent directly to pyOCD using the gdb monitor
command. Any output from pyOCD is returned through gdb and printed on the console. This is effectively the same as running the pyocd commander
subcommand. All pyOCD commands are available.
pyOCD initial selects the core controlled by the gdbserver as the target for monitor commands. Similarly, the selected AP is initially set to the gdbserver’s core’s MEM-AP. PyOCD’s core
command can be used to select a different core. This might be useful in order to release a secondary core from reset before starting to debug it, although a user script could be a better choice.
Semihosting and RTT
The gdbserver supports semihosting, SEGGER RTT, and Arm SWV.
Caching
Several forms of caching are supported to improve performance when communicating with gdb.
Note that these caches are currently only used with gdb, not for pyOCD’s commander interface or the Target
Python API. (But they will be used for the DebugContext
objects return from SoCTarget.get_target_context()
, if that is used through the Python API.)
Reading memory from the ELF
If provided the firmware’s ELF executable file with the --elf
argument, pyOCD will read target memory contents present in the ELF from that file instead of reading from the target via SWD/JTAG. This can be faster, especially with slower debug probe connections or wire protocol speeds.
The cache.read_code_from_elf
session option (bool) controls whether this feature is enabled. It’s turned on by default, but of course requires the ELF to be passed to pyOCD. (Unfortunately, there is no way to access the executable through gdb.)
Memory and register cache
Caches for target memory and core register values are present and enabled by default. Both caches are invalidated every time the core is resumed or stepped.
The memory cache will cache any memory region marked as cacheable (all are by default). To disable caching for a memory region, a user script can be used to set its is_cacheable
property to False.
For example, to disable caching of the “iram” region:
def will_connect():
target.memory_map.get_first_matching_region(name="iram").is_cacheable = False
(Use the show map
command to see the list of memory regions.)
These session options allow control over the memory and register caches. Both are enabled by default.
cache.enable_memory
(bool)cache.enable_register
(bool)
RTOS thread awareness
The gdbserver supports thread awareness for several RTOSes. Additional RTOS support can be added with plugins.
When gdb connects, pyOCD will attempt to enable thread awareness. By default, all available RTOS plugins are queried. When the first one is successfully enabled, the process stops. If the rtos.name
session option is set to the name of an RTOS plugin, only that one will be queried. To completely disable thread awareness, set the rtos.enable
session option to false.
Builtin RTOS plugins are shown in the following table.
RTOS Plugin Name | Description |
---|---|
argon | Argon RTOS |
freertos | FreeRTOS |
rtx5 | RTX5 |
threadx | ThreadX |
zephyr | Zephyr |
Viewing and selecting threads
Within gdb, the set of current threads can be printed with info threads
. gdb assigns each thread a unique integer identifier used to reference the thread in other commands.
This example shows a gdb thread listing for a Zephyr RTOS program.
Id Target Id Frame
* 3 Thread 536871784 "idle" (Running; Priority 15) arch_cpu_idle ()
at /Users/creed/projects/zephyrproject/zephyr/arch/arm/core/aarch32/cpu_idle.S:126
4 Thread 536871584 "uart_out_id" (Pending; Priority 7) arch_swap (key=0)
at /Users/creed/projects/zephyrproject/zephyr/arch/arm/core/aarch32/swap.c:53
5 Thread 536871416 "blink1_id" (Suspended; Priority 7) arch_swap (key=key@entry=0)
at /Users/creed/projects/zephyrproject/zephyr/arch/arm/core/aarch32/swap.c:53
6 Thread 536871248 "blink0_id" (Suspended; Priority 7) arch_swap (key=key@entry=0)
at /Users/creed/projects/zephyrproject/zephyr/arch/arm/core/aarch32/swap.c:53
7 Thread 536872152 "sysworkq" (Pending; Priority -1) arch_swap (key=0)
at /Users/creed/projects/zephyrproject/zephyr/arch/arm/core/aarch32/swap.c:53
Note how the thread name is shown in quotes, and thread state and priority are shown in parentheses. The actual description of threads is specific to each RTOS plugin.
The thread
command takes a gdb thread ID to switches between threads; after switching, backtrace
will show the selected thread’s state.
Thread reporting
There can be issues caused by a mismatch between the target’s current memory contents and the expected location of RTOS related symbols. For instance, if a new version of firmware is being debugged but the target’s flash has not been reprogrammed yet, or if there is stale data in RAM. If this happens, an out of date or corrupt view of the RTOS state could be reported.
To prevent this from happening, pyOCD will disable reporting threads to gdb (and reading the RTOS data from target memory) until the first time the target is resumed after any of these events:
- gdb connects to pyOCD
- Target reset
- Flash is reprogrammed using gdb commands
Note that a single instruction step will not enable thread reporting. A full resume is required. Gdb will see the list of threads when the target halts after the first resume, eg which a breakpoint is hit or the target is manually halted.
The actual behaviour depends on the plugin, so there is some slight variation between RTOSes. But the general sequence applies.
This logic not perfect, so there is a threads
command that provides manual control. It takes an argument with one of these actions:
status
: Show whether thread reporting is enabled.enable
: Enable thread reporting.disable
: Disallow thread reporting.flush
: Forcibly invalidate the threads list.
If threads
is executed and an RTOS plugin has not successfully loaded, it will print “Threads are unavailable”.
After thread reporting is enabled or disabled manually, you must step or resume in gdb to force gdb to refresh its view of threads. (There is no flush or invalidate for threads in gdb.)
When thread reporting is disabled, gdb will see a single thread just like when no RTOS plugin is loaded.
RTOS notes
These sections document specific features or requirements for using thread awareness with different RTOSes.
Zephyr RTOS
To enable thread awareness, the CONFIG_DEBUG_THREAD_INFO
Kconfig setting must be enabled.
Handler mode thread
The RTOS plugins will report an artificial thread when an M-profile core is in Handler mode, eg in an exception or interrupt handler. This lets you view the state of the exception handler separately from RTOS threads.