Using GDB for debugging
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.
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)
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.
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
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.