Debugging with XGDB#

XGDB is an extension of GDB which adds support for multi-tile debugging of Xcore applications in the form of XE files. Therefore, to the greatest possible extent, XGDB behaves like GDB and therefore most information can be found on third-party websites and resources.

Its use for debugging multi-tile applications is more unusual; this example aims at demonstrating how XGDB can be used to follow an Xcore application as control passes from core to core and tile to tile.

Create example#

To illustrate the use of XGDB for debugging, we create a token-passing ring operating across two tiles:

Listing 9 multitile.xc#
#include <platform.h>

typedef chanend chanend_t;

extern "C" {
  void main_tile0(chanend_t, chanend_t);
  void main_tile1(chanend_t, chanend_t);
}

int main(void)
{
  chan tile0_to_tile1;
  chan tile1_to_tile0;

  par {
    on tile[0]: main_tile0(tile1_to_tile0, tile0_to_tile1);
    on tile[1]: main_tile1(tile0_to_tile1, tile1_to_tile0);
  }

  return 0;
}

On each tile, we pass the token via two cores. The ring is kick-started by tile[1]:

Listing 10 main.c#
 1#include <stdio.h>
 2#include <xcore/channel.h>
 3#include <xcore/parallel.h>
 4
 5DECLARE_JOB(process, (chanend_t, chanend_t, char *, int));
 6void process(chanend_t cIn, chanend_t cOut, char * name, int init)
 7{
 8  if (init)
 9  {
10    chan_out_word(cOut, 1);
11  }
12
13  while (1)
14  {
15    int token = chan_in_word(cIn);
16    printf("%s\n", name);
17    chan_out_word(cOut, token);
18  }
19}
20
21void main_tile0(chanend_t cIn, chanend_t cOut)
22{
23  channel_t chan = chan_alloc();
24
25  PAR_JOBS(
26    PJOB(process, (cIn, chan.end_a, "tile0-core0", 0)),
27    PJOB(process, (chan.end_b, cOut, "tile0-core1", 0)));
28
29  chan_free(chan);
30}
31
32void main_tile1(chanend_t cIn, chanend_t cOut)
33{
34  channel_t chan = chan_alloc();
35
36  PAR_JOBS(
37    PJOB(process, (cIn, chan.end_a, "tile1-core0", 0)),
38    PJOB(process, (chan.end_b, cOut, "tile1-core1", 1))); // Kick-start the ring
39
40  chan_free(chan);
41}

Build the example as normal, remembering to use xcc -g:

$ xcc -target=XCORE-200-EXPLORER -g multitile.xc main.c

Running the example produces the expected results:

$ xrun --io a.xe
tile0-core0
tile0-core1
tile1-core0
tile1-core1
tile0-core0
tile0-core1
...

Interactive debugging#

We’ll now use XGDB to observe the multi-tile example as the token is passed from core to core and tile to tile.

To start an interactive debug session:

$ xgdb a.xe

A GDB prompt will be shown. Connect to a target and load the application using the connect and load commands:

(gdb) connect
0x00040000 in _start ()
(gdb) load
Loading setup image to XCore 0
...

Using the tile command, select a tile as the focus of subsequent commands:

(gdb) tile 0
[Switching to inferior 1 (tile[0])]
[Switching to thread 1 (tile[0] core[0])]
#0  0x00040000 in _start ()

Add a breakpoint in the usual way:

(gdb) break main.c:16
Breakpoint 1 at 0x40156: file examples/debug/main.c, line 16. (2 locations)

Note that when setting a breakpoint on a line of source, that breakpoint will be set on every tile which that specific line of source can run on. In this case, the process function is called on both tiles, so a breakpoint gets set on each of the tiles.

Notice how the breakpoint appears at different addresses on each tile - this is not unexpected, as the two tiles have entirely independent address spaces with potentially different contents:

(gdb) info breakpoints
Num Type           Disp Enb  Address    What
1   breakpoint     keep y    <MULTIPLE>
1.1                     y    0x00040156 in process at examples/debug/main.c:16 inf 1
1.2                     y    0x0004015a in process at examples/debug/main.c:16 inf 2

Note

To restrict the breakpoint to only be set on a single tile, you can use the additional inferior argument to the breakpoint command, such as break main.c:16 inferior 1.

To find the relevant inferior numbers, the info inferiors command can be used:

(gdb) info inferiors
  Num  Description       Connection           Executable
  1    tile[0]           1 (remote :39211)    /tmp/.xgdb/593782/0/a.xe.i0_n0_t0.elf
* 2    tile[1]           1 (remote :39211)    /tmp/.xgdb/593782/0/a.xe.i1_n0_t1.elf

The * indicates the currently selected inferior.

We’re now ready to run the multi-tile application. Use the continue and info threads commands to run the application to the next breakpoint and examine which tile/core it has halted at:

(gdb) continue
[Switching to tile[0] core[0]]

Breakpoint 1.1, process (cIn=2147614978, cOut=2147615234, name=0x4511c "tile0-core0", init=0) at examples/debug/main.c:16
16          printf("%s\n", name);
Current language:  auto; currently minimal
(gdb) info threads
* 1.1  tile[0] core[0]  process (cIn=2147614978, cOut=2147615234, name=0x4511c "tile0-core0", init=0) at examples/debug/main.c:16
  1.2  tile[0] core[1] 0x000403f4 in chan_in_word ()
  2.1  tile[1] core[0] 0x000403c4 in chan_in_word ()
  2.2  tile[1] core[1] 0x000403c4 in chan_in_word ()

The thread with the asterisk next to it shows which tile/core currently has focus for subsequent commands. You can observe the asterisk moving as the token moves around the ring by repeatedly issuing continue and info threads.

To end the interactive debug session:

(gdb) quit

Scripted debugging#

Use of XGDB can be fully or partially scripted using Command Files (just like GDB). Scripted debugging using Command Files can be very powerful. It allows developers to quickly reproduce a particular scenario, and even share those scenarios with other developers.

To start a scripted debug session, use xgdb -x with the command filename:

$ xgdb --batch -x cmds.txt a.xe

This causes the following command file to be executed prior to any interactive debugging; the xgdb --batch option in the example command means that there there will be no interaction, gdb will terminate when the script file has been fully processed.

Listing 11 cmds.txt#
# Setup
connect
load

# Add breakpoints
tile 0
break main.c:16
tile 1
break main.c:16
info breakpoints

# Run
set $count=5
while $count > -1
  continue
  info threads
  set $count=$count-1
end

Further information for scripting with XGDB can be found in the reference manual.

Summary#

This tutorial has demonstrated how XGDB can be used to debug a multi-tile Xcore application. To start exploring further XMOS-specific commands built on top of GDB, see XMOS XGDB commands.