Epiphany GDB multicore debugging tutorial. This tutorial assumes basic knowledge of GDB.
Start e-server in one terminal.
$ e-server --multiprocess
Using the HDF file: /opt/adapteva/esdk/bsps/current/parallella_E16G3_1GB.xml
Listening for RSP on port 51000
In a second terminal start a host program:
Let's use eprime from parallella-examples for this tutorial.
Build eprime:
$ cd ~/parallella-examples/apps/eprime
$ git pull
$ ./build.sh
Start the program. EHAL_GDBSERVER will make e-hal start any epiphany
program in a halted debug mode. This is useful if you want to be able to debug
a program from the beginning.
$ EHAL_GDBSERVER=yes ./run.sh
You will notice that eprime reports 0 primes factored / s. This is because the
epiphany cores are not running yet, because of EHAL_GDBSERVER. The next step
is to attach with the e-gdb client and start debugging.
In a third terminal, start e-gdb. You can either do this on your parallella
or on your local computer if you have esdk-2016.11.x86_64 installed.
$ cd ~/parallella-examples/apps/eprime
$ epiphany-elf-gdb e_prime.elf
Reading symbols from e_prime.elf...done.
Enable non-stop mode. This must to be done before connecting to e-server. We also need to disable pagination as it breaks non-stop.
(gdb) set pagination off
(gdb) set non-stop on
Connect to the e-server. You'll need to adjust localhost to
parallella.local. if you use e-gdb from your local computer.
(gdb) target extended-remote localhost:51000
Remote debugging using localhost:51000
List processes:
(gdb) info os processes
pid user command cores
1 root 0000,0001,0002,0003,0100,0101,0102,0103,0200,0201,0202,0203,0300,0301,0302,0303
The default process contains all
cores that have not yet been manually assigned to a workgroup. To create a new
workgroup, use the monitor workgroup command. The syntax is monitor workgroup [ROW0] [COL0] [ROWS] [COLS].
Attach to the default process (workgroup).
(gdb) attach 1
[New Thread 1.101]
...
[New Thread 1.404]
0x00000000 in _start ()
...
Thread 16 stopped.
0x00000000 in _start ()
Show status for all threads:
(gdb) info threads
Id Target Id Frame
* 1 Thread 1.101 (Core: 0000: halted, interruptible) 0x00000000 in _start ()
2 Thread 1.102 (Core: 0001: halted, interruptible) 0x00000000 in _start ()
...
16 Thread 1.404 (Core: 0303: halted, interruptible) 0x00000000 in _start ()
The * in front of thread id 1 indicates the current thread.
Ah, all theeads are stopped. This is because we started the host program with
EHAL_GDBSERVER=yes. That's why eprime reports zero primes/s. You'll notice
that all threads are stopped in _start(). This is the low level program entry
point, which is called even before main(). So it's possible to set a
breakpoint on any early function call in the epiphany program, even main().
Let's continue the current thread and see what happens in the eprime terminal.
(gdb) continue
Continuing.
Now we can see some primes being generated in the eprime terminal.
Core (00,00) Tests: 818032 Primes: 102391 Current: 26177027 SQ: 5116
...
Total tests: 1298458 Found primes: 157629
Iterations/sec: 12840.000000
Go back to the gdb terminal and stop the thread by pressing Ctrl-C.
^C
Thread 1 stopped.
0x00002210 in __umodsi3 ()
It's also possible to continue a thread in the background. Append & to
continue a thread asynchronously.
(gdb) continue &
Continuing.
(gdb)
Now it's possible to issue commands to gdb while the thread is running in the background. Let's verify that the thread is really running.
(gdb) info threads
* 1 Thread 1.101 (Core: 0000: running, interruptible) (running)
2 Thread 1.102 (Core: 0001: halted, interruptible) 0x00000000 in _start ()
...
16 Thread 1.404 (Core: 0303: halted, interruptible) 0x00000000 in _start ()
Use the interrupt command to stop a thread that was started asynchronously:
(gdb) interrupt
Thread 1 stopped.
0x0000221c in __umodsi3 ()
Verify that the thread is stopped:
(gdb) info thread 1
Id Target Id Frame
* 1 Thread 1.101 (Core: 0000: halted, interruptible) 0x0000221c in __umodsi3 ()
The -a flag is a shortcut to continue all threads:
(gdb) continue -a
Continuing.
In the eprime terminal you will now see that all cores are generating primes.
Press Ctrl-C to get back to the gdb prompt.
Similarly, appending an & continues the threads in the background.
(gdb) continue -a &
Continuing.
(gdb)
Use info threads to verify that the threads are running.
(gdb) info threads
Id Target Id Frame
* 1 Thread 1.101 (Core: 0000: running, interruptible) (running)
2 Thread 1.102 (Core: 0001: running, interruptible) (running)
...
To stop all threads, use interrupt -a:
(gdb) interrupt -a
Thread 1 stopped.
28 if(number % i == 0)
Thread 2 stopped.
...
Set a breakpoint (will be applied to all threads):
(gdb) break is_prime
Breakpoint 1 at 0x2048: file src/isprime.c, line 23.
(gdb)
Continue all threads asynchronously:
(gdb) continue -a &
Continuing.
(gdb)
All threads are running in the background. When a breakpoint is hit, GDB will report it:
Thread 2 hit Breakpoint 1, is_prime (number=608719203) at src/isprime.c:23
23 {
...
Finally when done debugging, detach from the process. A detach will resume all threads:
(gdb) detach
Detaching from program: e_prime.elf, process 1