Skip to content

Using GDB for debugging

Carlos Moratelli edited this page Apr 6, 2017 · 38 revisions

Using GDB (The GNU Project Debugger) you can stop and look inside of one or multiple guests. This is a quick tutorial on how to use GDB to debug the prplHypervisor and guests with the OpenOCD and BB probe. First, make sure that the BB probe is connected to your board and that you have installed the correct version of OpenOCD on your host machine.

Running OpenOCD and GDB

Suppose that you have a virtualized system composed of two guests, and you need to debug the communication between them. In this case, we will use the ping-pong application as an example of simultaneous debugging of two guests.

Step 1. Compile and load the ping-pong example. Go to the Chipkit Wi-Fire platform folder and make sure that you have the CFG_FILE as cfg/sample-ping-pong.cfg. Then, perform make and make load commands.

~/prpl-hypervisor/platform/pic32mz_chipkit_Wifire/$ make && make load 

Step 2. From the Chipkit Wi-Fire platform folder execute the openOCD.

~/prpl-hypervisor/platform/pic32mz_chipkit_Wifire/$ sudo openocd -f debug/dp_busblaster.cfg -f debug/wifire.cfg -c "init"

Step 3. In another terminal, but still in the Chipkit Wi-Fire platform folder, run the GDB using the gdbinit file as a parameter.

~/prpl-hypervisor/platform/pic32mz_chipkit_Wifire/$ mips-mti-elf-gdb -x debug/gdbinit

Now you should see the GDB console as shown below. You can use GDB in the same way as you would in in any other situation, for example, executing step by step, reading/writing the variable, etc. The only difference is that every breakpoint you configure must be associated with a guest ID to make sure that the target will stop at the correct guest. Otherwise, the target may stop when it gets to any guest, or even the hypervisor, because they all execute the same virtual address.

GNU gdb (Codescape GNU Tools 2016.05-03 for MIPS MTI Bare Metal) 7.9.1
Copyright (C) 2015 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying" and "show warranty" for details.
This GDB was configured as "--host=x86_64-pc-linux-gnu --target=mips-mti-elf".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://imgtec.com/mips-sdk-support/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word".
0x00000000 in ?? ()
The target is assumed to be little endian
scan delay: 20000000 nsec
running in legacy mode
JTAG tap: pic32mz.cpu tap/device found: 0x1720e053 (mfg: 0x029 (MicrochipTechnology), part: 0x720e, ver: 0x1)
target halted in MIPS32 mode due to debug-request, pc: 0xbfc00000
(gdb) 

Setting Breakpoints

Up to this point, we haven’t loaded any symbol files (.elf) in the GDB. The prplHypervisor and each guest correspond to a different .elf file. Because all of these files have the same base virtual address, it is not possible to load them at the same time. To choose breakpoints before loading the symbol file you can look at the .lst files generated during the compilation. Now, let’s set your first breakpoint.

Step 1. Go to the folder of each bare metal application and open the corresponding .lst file.

~/prpl-hypervisor/bare-metal-apps/apps/ping/ping.lst
~/prpl-hypervisor/bare-metal-apps/apps/pong/pong.lst

If you need to check the variable values of the ping and pong applications, you will need to find the address of the main() function of each application. For example, for the ping application look for main in the ping.lst file:

9d0055b0 <main>:
9d0055b0:	27bdffa0 	addiu	sp,sp,-96
9d0055b4:	afbf005c 	sw	ra,92(sp)
9d0055b8:	afa0001c 	sw	zero,28(sp)

Now, you need to set a conditional breakpoint to the address 0x9d0055b0. The stop condition is guest ID equal to 1.

Step 2. In GDB console type:

(gdb) break *0x9d0052a0 if ($guestctl1 & 0x3f) == 1
(gdb) command
file ~/prpl-hypervisor/bare-metal-apps/apps/ping/ping.elf
mon target guest_offset 0x8000
end

The above command will create a conditional breakpoint and load the ping.elf symbol file automatically when the target stops. The mon target guest_offset command will be explained in the next section.

Step 3. Do the same for the pong application.

Find the virtual address of the main() call:

9d00584c <main>:
9d00584c:	27bdffd0 	addiu	sp,sp,-48
9d005850:	afbf002c 	sw	ra,44(sp)
9d005854:	24040002 	li	a0,2
9d005858:	0f401655 	jal	9d005954 <serial_select>

Set a breakpoint at the address 0x9d00584c.

(gdb) break *0x9d00584c if ($guestctl1 & 0x3f) == 2
(gdb) command
file ~/prpl-hypervisor/bare-metal-apps/apps/pong/pong.elf
mon target guest_offset 0x10000
end

Note: See that the guest ID is 2 for the second guest.

Use the info break GDB command to check your breakpoints:

(gdb) info break
Num     Type           Disp Enb Address    What
1       breakpoint     keep y   0x9d0055b0 
	stop only if ($guestctl1 & 0x3f) == 1
        file ~/hyper/prpl-hypervisor/bare-metal-apps/apps/ping/ping.elf
        mon target guest_offset 0x8000
2       breakpoint     keep y   0x9d00584c 
	stop only if ($guestctl1 & 0x3f) == 2
        file ~/hyper/prpl-hypervisor/bare-metal-apps/apps/pong/pong.elf
        mon target guest_offset 0x10000

Now you are ready to start the target execution.

Debugging The Target

All you need to do is to use the GDB debugging commands as you wish. Additionally, you can use the monitor command to send commands directly to OpenOCD. For example, to halt the target board.

(gdb) monitor reset halt

With the target board in halt state use the continue command to resume the execution. The target should stop at the main() call in guest 1. Use the commands next and print to execute step by step and check the variable values.

(gdb)(gdb) continue
Continuing.
Note: automatically using hardware breakpoints for read-only addresses.

Breakpoint 1, 0x9d0055b0 in ?? ()
guest_offset: 0x8000
(gdb) next
51		uint32_t worst_one_way = 0;
(gdb) next
52		uint32_t worst_round_trip = 0;
(gdb) next
53		uint32_t best_one_way = 9999999;
(gdb) next
54		uint32_t best_round_trip = 9999999;
(gdb) print best_one_way 
$1 = 9999999

In this test, if you type continue the target will stop at the second breakpoint in guest 2.

Breakpoint 2, 0x9d00584c in main () at ../../apps/pong/pong.c:38
guest_offset: 0x10000
(gdb) next
43		serial_select(UART2);
(gdb) next
45		printf("\nWait...");
(gdb) next
47		memset(message_buffer, 0, sizeof(message_buffer));
(gdb) next
51			ret = ReceiveMessage(&source, message_buffer, sizeof(message_buffer), 1);
(gdb) next
52			if (ret<0){
(gdb) print ret
$2 = 64

The guest_offset Command

When the target stops at a breakpoint, the Root TLB is disabled. That means, when a variable value is shown, it is not getting correctly mapped, and consequently, you see the wrong value.

To workaround this limitation, use the mon target guest_offset command to set a memory offset to the guest's RAM. You can see the memory base address for a virtual machine by looking at the ram_base variable in the following file:

~/prpl-hypervisor/platform/pic32mz_chipkit_Wifire/include/config.h

The RAM start address is 0x80000000. Thus, if the guest's ram_base is 0x80008000 then the offset is 0x80008000 - 0x80000000 = 0x8000. In this case, to check the variable values using GDB you must apply the following command:

mon target guest_offset 0x8000

You can do this automatically on a breakpoint, as shown above.