QEMU Module Debug Printing
Module debug printing allows verbose logging for a QEMU virtual peripheral, such as a SPI controller.
This will allow you to see register reads/writes (if the module supports it), and any time a debug print statement is used in a module.
Module Debug Printing by Using the Command Line
QEMU provides a way to print debugging output by passing in the -d
command-line parameter.
This output typically informs the user of things such as guest errors, unimplemented features in a module, and what a module is currently doing.
However, you could also use -d
to see information on lower level things, such as CPU and MMU activity.
Passing in the Command-Line Argument
-d
can take in 1 or more arguments separated by a comma (with no space between arguments), or help
and trace:help
to show a full list of what can be printed using -d
.
See the QEMU Options and Commands page for syntax.
Example Output
For this example we'll be passing in
-d trace:m25p80_command_decoded
to see what commands our SPI flash receives.
The output shown below is ZCU102 Linux boot with the added -d trace:m25p80_command_decoded
command-line parameter:
201160@1598298783.243037:m25p80_command_decoded [0x55555ada87d0] new command:0x9f 201160@1598298783.254801:m25p80_command_decoded [0x55555ada87d0] new command:0x5a 201160@1598298783.261425:m25p80_command_decoded [0x55555ada87d0] new command:0x6 201160@1598298783.262073:m25p80_command_decoded [0x55555ada87d0] new command:0x1 201160@1598298783.263738:m25p80_command_decoded [0x55555ada87d0] new command:0x5 201160@1598298783.269035:m25p80_command_decoded [0x55555ada87d0] new command:0x6 201160@1598298783.269038:m25p80_command_decoded [0x55555ada9d10] new command:0x6 201160@1598298783.269714:m25p80_command_decoded [0x55555ada87d0] new command:0xb7 201160@1598298783.269717:m25p80_command_decoded [0x55555ada9d10] new command:0xb7 201160@1598298783.270148:m25p80_command_decoded [0x55555ada9d10] new command:0x4 201160@1598298783.271772:m25p80_command_decoded [0x55555ada87d0] new command:0x6 201160@1598298783.271775:m25p80_command_decoded [0x55555ada9d10] new command:0x6 201160@1598298783.272155:m25p80_command_decoded [0x55555ada87d0] new command:0x1 201160@1598298783.272157:m25p80_command_decoded [0x55555ada9d10] new command:0x1 201160@1598298783.272779:m25p80_command_decoded [0x55555ada87d0] new command:0x5 201160@1598298783.272781:m25p80_command_decoded [0x55555ada9d10] new command:0x5 201160@1598298783.273502:m25p80_command_decoded [0x55555ada87d0] new command:0x70 201160@1598298783.273504:m25p80_command_decoded [0x55555ada9d10] new command:0x70 201160@1598298783.274585:m25p80_command_decoded [0x55555ada87d0] new command:0x6 201160@1598298783.274588:m25p80_command_decoded [0x55555ada9d10] new command:0x6 201160@1598298783.275370:m25p80_command_decoded [0x55555ada87d0] new command:0xb7 201160@1598298783.275373:m25p80_command_decoded [0x55555ada9d10] new command:0xb7 201160@1598298783.275769:m25p80_command_decoded [0x55555ada87d0] new command:0x4 201160@1598298783.275771:m25p80_command_decoded [0x55555ada9d10] new command:0x4 [ 16.903020] m25p80 spi0.0: n25q512a (131072 Kbytes) [ 16.911027] 3 fixed-partitions partitions found on MTD device spi0.0 [ 16.911781] Creating 3 MTD partitions on "spi0.0":
Note that in this case some commands are repeated, because the flash is in a dual parallel configuration (as shown by the different addresses QEMU prints in the m25p80_command_decoded
lines).
Module Debug Printing by Modifying Source Code
Some modules have extra debug printing that can be enabled by modifying the source code of the module.
These print statements usually show register accesses and what the module is currently doing.
Additional print statements could also be added by the user, if desired.
Finding the Module
When using a device tree, there are many ways to ways to find the module files that you want to add debug printing to. This section will cover two of them.
Using info qtree
In the QEMU monitor, info qtree
will show you the device tree model that QEMU is using.
A short snippet is shown below.
(qemu) info qtree # ... dev: cdns.spi-r1p6, id "ps7-spi@0xFF050000" gpio-out "" 4 gpio-out "sysbus-irq" 1 num-busses = 1 (0x1) num-ss-bits = 4 (0x4) num-txrx-bytes = 1 (0x1) mmio ffffffffffffffff/0000000000000800 bus: spi0 type SSI dev: sst25wf080, id "spi1_flash3@0" gpio-in "ssi-gpio-cs" 1 nonvolatile-cfg = 36863 (0x8fff) spansion-cr1nv = 0 (0x0) spansion-cr2nv = 8 (0x8) spansion-cr3nv = 2 (0x2) spansion-cr4nv = 16 (0x10) len-nv-cfg-large-stage = 0 (0x0) drive = "" dev: sst25wf080, id "spi1_flash2@0" gpio-in "ssi-gpio-cs" 1 nonvolatile-cfg = 36863 (0x8fff) spansion-cr1nv = 0 (0x0) spansion-cr2nv = 8 (0x8) spansion-cr3nv = 2 (0x2) spansion-cr4nv = 16 (0x10) len-nv-cfg-large-stage = 0 (0x0) drive = "" dev: sst25wf080, id "spi1_flash1@0" gpio-in "ssi-gpio-cs" 1 nonvolatile-cfg = 36863 (0x8fff) spansion-cr1nv = 0 (0x0) spansion-cr2nv = 8 (0x8) spansion-cr3nv = 2 (0x2) spansion-cr4nv = 16 (0x10) len-nv-cfg-large-stage = 0 (0x0) drive = "" dev: sst25wf080, id "spi1_flash0@0" gpio-in "ssi-gpio-cs" 1 nonvolatile-cfg = 36863 (0x8fff) spansion-cr1nv = 0 (0x0) spansion-cr2nv = 8 (0x8) spansion-cr3nv = 2 (0x2) spansion-cr4nv = 16 (0x10) len-nv-cfg-large-stage = 0 (0x0) drive = "" # ... dev: xlnx.zynqmp-csu-core, id "csu_core" gpio-out "sysbus-irq" 1 version-platform = 3 (0x3) version-ps-version = 3 (0x3) idcode = 73400467 (0x4600093) mmio ffffffffffffffff/0000000000005038 # ...
On more complex systems, such as the Zynq UltraScale+ MPSoC or Versal Adaptive SoC, the output of info qtree
can be very long.
For finding the module, the only part we care about is the line that says dev:
.
The value to the right of dev:
, e.g. xlnx.zynqmp-csu-core
, is the object model name that QEMU uses.
We can use grep
to find what file the object model name was defined in, which will most likely be the module file.
komlodi@machine:/scratch/proj/qemu/build$ grep -rin xlnx.zynqmp-csu-core --exclude-dir=build .. ../hw/misc/csu_core.c:42:#define TYPE_XLNX_CSU_CORE "xlnx.zynqmp-csu-core"
In this case, the Zynq UltraScale+ MPSoC CSU core source file is in hw/misc/csu_core.c
.
We can verify this by looking at the top of the file and seeing a reference to debug printing for the CSU core.
#ifndef XLNX_CSU_CORE_ERR_DEBUG #define XLNX_CSU_CORE_ERR_DEBUG 0 #endif
Now let's look for the SPI controller, cdns.spi-r1p6
.
komlodi@machine:/scratch/proj/qemu/build$ grep -rin cdns.spi-r1p6 --exclude-dir=build .. ../hw/core/fdt_generic_devices_cadence.c:50: { .name = "cdns.spi-r1p6", .parent = "xlnx.ps7-spi" },
In fdt_generic_devices_cadence.c
there is a reference to debug printing, but it isn't for a SPI controller.
#ifndef FDT_GENERIC_UTIL_ERR_DEBUG #define FDT_GENERIC_UTIL_ERR_DEBUG 0 #endif
If we look deeper, we see a reference to a parent QEMU object model.
static const TypeInfo fdt_qom_aliases[] = { { .name = "xlnx.ps7-ethernet", .parent = "cadence_gem" }, { .name = "cdns,gem", .parent = "cadence_gem" }, { .name = "cdns,zynq-gem", .parent = "cadence_gem" }, { .name = "cdns,zynqmp-gem", .parent = "cadence_gem" }, { .name = "xlnx.ps7-ttc", .parent = "cadence_ttc" }, { .name = "cdns.ttc", .parent = "cadence_ttc" }, { .name = "cdns.uart", .parent = "cadence_uart" }, { .name = "xlnx.ps7-uart", .parent = "cadence_uart" }, { .name = "cdns.spi-r1p6", .parent = "xlnx.ps7-spi" }, // <-- { .name = "xlnx.xuartps", .parent = "cadence_uart" }, };
This module name should be the one that contains the debug printing macro we want.
komlodi@machine:/scratch/proj/qemu/build$ grep -rin xlnx.ps7-spi --exclude-dir=build .. ../hw/core/fdt_generic_devices_cadence.c:50: { .name = "cdns.spi-r1p6", .parent = "xlnx.ps7-spi" }, ../hw/arm/xilinx_zynq.c:124: dev = qdev_create(NULL, is_qspi ? "xlnx.ps7-qspi" : "xlnx.ps7-spi"); ../include/hw/ssi/xilinx_spips.h:144:#define TYPE_XILINX_SPIPS "xlnx.ps7-spi"
We found #define TYPE_XILINX_SPIPS "xlnx.ps7-spi"
, but it's in a .h
file.
We can assume that include/hw/ssi/xilinx_spips.h
will be used in a .c
file named hw/ssi/xilinx_spips.c
, but that can be verified with grep
.
komlodi@machine:/scratch/proj/qemu/build$ grep -rn xilinx_spips.h --exclude-dir=build .. ../hw/ssi/xilinx_spips.c:33:#include "hw/ssi/xilinx_spips.h" Binary file ../.git/index matches ../MAINTAINERS:795:F: include/hw/ssi/xilinx_spips.h ../include/hw/arm/xlnx-zynqmp.h:27:#include "hw/ssi/xilinx_spips.h"
Verify that it has the debug printing macro we're looking for.
komlodi@machine:/scratch/proj/qemu/build$ vim ../hw/ssi/xilinx_spips.c // ... #ifndef XILINX_SPIPS_ERR_DEBUG #define XILINX_SPIPS_ERR_DEBUG 0 #endif #define DB_PRINT_L(level, ...) do { \ if (XILINX_SPIPS_ERR_DEBUG > (level)) { \ fprintf(stderr, ": %s: ", __func__); \ fprintf(stderr, ## __VA_ARGS__); \ } \ } while (0)
Using the DTS or DTB Files
Using the DTS or DTB files to find the module file is similar to using info qtree
.
If using a DTB file, you first need to un-flatten it.
komlodi@machine:/path/to/xilinx-zcu102-2020.2/pre-built/linux/images$ dtc -I dtb -O dts system.dtb -o system.dts komlodi@machine:/path/to/xilinx-zcu102-2020.2/pre-built/linux/images$ vim system.dts
In the DTS file, find the peripheral that you want to enable debug printing for.
For this example, we'll add debug printing to the ZynpMP's UART.
By looking at the register layout of the Zynq UltraScale+ MPSoC, we know UART0 is at address 0xFF000000
. We can use that to identify the node in the DTS file.
serial@ff000000 { u-boot,dm-pre-reloc; compatible = "cdns,uart-r1p12", "xlnx,xuartps"; status = "okay"; interrupt-parent = <0x4>; interrupts = <0x0 0x15 0x4>; reg = <0x0 0xff000000 0x0 0x1000>; clock-names = "uart_clk", "pclk"; power-domains = <0xc 0x21>; clocks = <0x3 0x38 0x3 0x1f>; pinctrl-names = "default"; pinctrl-0 = <0x1d>; cts-override; device_type = "serial"; port-number = <0x0>; };
The line we care about is compatible = "cdns,uart-r1p12", "xlnx,xuartps";
.
QEMU will go left-to-right and look for modules that are compatible with those strings.
Usually you can grep
the compatible
string and find the module file that way.
komlodi@machine:/scratch/proj/qemu/build$ grep -rin cdns,uart .. komlodi@machine:/scratch/proj/qemu/build$
However, that didn't work, so we need to look for something more generic.
komlodi@machine:/scratch/proj/qemu/build$ find .. -name "*uart*" ../hw/riscv/sifive_uart.c ../hw/char/lm32_juart.c ../hw/char/cmsdk-apb-uart.c ../hw/char/omap_uart.c ../hw/char/grlib_apbuart.c ../hw/char/xilinx_iomod_uart.c ../hw/char/exynos4210_uart.c ../hw/char/milkymist-uart.c ../hw/char/nrf51_uart.c ../hw/char/mcf_uart.c ../hw/char/xilinx_uartlite.c ../hw/char/lm32_uart.c ../hw/char/cadence_uart.c ../hw/char/digic-uart.c
hw/char/cadence_uart.c
looks like the most likely file, verify it has a debug macro.
komlodi@machine:/scratch/proj/qemu/build$ vim ../hw/char/cadence_uart.c // ... #ifdef CADENCE_UART_ERR_DEBUG #define DB_PRINT(...) do { \ fprintf(stderr, ": %s: ", __func__); \ fprintf(stderr, ## __VA_ARGS__); \ } while (0) #else #define DB_PRINT(...) #endif
This confirms hw/char/cadence_uart.c
is the file we're looking for.
If we did not find it, we would search for the next string in the compatible
string list and repeat the same steps above.
Enabling Debug Printing
Modules have different ways to enable debug printing, but the underlying concepts are the same.
Changing the Debug Print Level
With modules that have a debug macro similar to
komlodi@machine:/scratch/proj/qemu/build$ vim ../hw/ssi/xilinx_spips.c // ... #ifndef XILINX_SPIPS_ERR_DEBUG #define XILINX_SPIPS_ERR_DEBUG 0 #endif #define DB_PRINT_L(level, ...) do { \ if (XILINX_SPIPS_ERR_DEBUG > (level)) { \ fprintf(stderr, ": %s: ", __func__); \ fprintf(stderr, ## __VA_ARGS__); \ } \ } while (0)
The DB_PRINT_L
definition says that it will print if the level is above a threshold.
Therefore, to make it print, make XILINX_SPIPS_ERR_DEBUG
greater than zero and rebuild QEMU. A higher debug level means more verbose printing.
An example QSPI debug output during ZCU102 Linux boot is shown below:
# ... : xlnx_zynqmp_qspips_flush_fifo_g: Dummy GQSPI Delay Command Entry, Do nothing: xlnx_zynqmp_qspips_flush_fifo_g: GQSPI command: f404 : xlnx_zynqmp_qspips_flush_fifo_g: GQSPI command: 0 : xlnx_zynqmp_qspips_flush_fifo_g: Dummy GQSPI Delay Command Entry, Do nothing: xlnx_zynqmp_qspips_flush_fifo_g: GQSPI command: 1f501 : xlnx_zynqmp_qspips_flush_fifo_g: GQSPI command: fd00 : xlnx_zynqmp_qspips_flush_fifo_g: GQSPI command: 1f501 : xlnx_zynqmp_qspips_flush_fifo_g: GQSPI command: fd00 : xlnx_zynqmp_qspips_flush_fifo_g: GQSPI command: c403 : xlnx_zynqmp_qspips_flush_fifo_g: GQSPI command: 0 : xlnx_zynqmp_qspips_flush_fifo_g: Dummy GQSPI Delay Command Entry, Do nothing: xlnx_zynqmp_qspips_flush_fifo_g: GQSPI command: f404 : xlnx_zynqmp_qspips_flush_fifo_g: GQSPI command: 0 : xlnx_zynqmp_qspips_flush_fifo_g: Dummy GQSPI Delay Command Entry, Do nothing: xlnx_zynqmp_qspips_flush_fifo_g: GQSPI command: 1f501 : xlnx_zynqmp_qspips_flush_fifo_g: GQSPI command: fd00 : xlnx_zynqmp_qspips_flush_fifo_g: GQSPI command: 6f502 : xlnx_zynqmp_qspips_flush_fifo_g: GQSPI command: 0 : xlnx_zynqmp_qspips_flush_fifo_g: Dummy GQSPI Delay Command Entry, Do nothing: xlnx_zynqmp_qspips_flush_fifo_g: GQSPI command: c403 : xlnx_zynqmp_qspips_flush_fifo_g: GQSPI command: 0 : xlnx_zynqmp_qspips_flush_fifo_g: Dummy GQSPI Delay Command Entry, Do nothing: xlnx_zynqmp_qspips_flush_fifo_g: GQSPI command: f404 : xlnx_zynqmp_qspips_flush_fifo_g: GQSPI command: 0 : xlnx_zynqmp_qspips_flush_fifo_g: Dummy GQSPI Delay Command Entry, Do nothing: xlnx_zynqmp_qspips_flush_fifo_g: GQSPI command: 1f501 : xlnx_zynqmp_qspips_flush_fifo_g: GQSPI command: fd00 : xlnx_zynqmp_qspips_flush_fifo_g: GQSPI command: 6f502 : xlnx_zynqmp_qspips_flush_fifo_g: GQSPI command: 0 : xlnx_zynqmp_qspips_flush_fifo_g: Dummy GQSPI Delay Command Entry, Do nothing: xlnx_zynqmp_qspips_flush_fifo_g: GQSPI command: c403 : xlnx_zynqmp_qspips_flush_fifo_g: GQSPI command: 0 : xlnx_zynqmp_qspips_flush_fifo_g: Dummy GQSPI Delay Command Entry, Do nothing: xlnx_zynqmp_qspips_flush_fifo_g: GQSPI command: f404 : xlnx_zynqmp_qspips_flush_fifo_g: GQSPI command: 0 : xlnx_zynqmp_qspips_flush_fifo_g: Dummy GQSPI Delay Command Entry, Do nothing: xlnx_zynqmp_qspips_flush_fifo_g: GQSPI command: 1f501 : xlnx_zynqmp_qspips_flush_fifo_g: GQSPI command: fd00 : xlnx_zynqmp_qspips_flush_fifo_g: GQSPI command: c403 : xlnx_zynqmp_qspips_flush_fifo_g: GQSPI command: 0 : xlnx_zynqmp_qspips_flush_fifo_g: Dummy GQSPI Delay Command Entry, Do nothing: xlnx_zynqmp_qspips_flush_fifo_g: GQSPI command: f404 : xlnx_zynqmp_qspips_flush_fifo_g: GQSPI command: 0 : xlnx_zynqmp_qspips_flush_fifo_g: Dummy GQSPI Delay Command Entry, Do nothing: xlnx_zynqmp_qspips_flush_fifo_g: GQSPI command: 1f501 : xlnx_zynqmp_qspips_flush_fifo_g: GQSPI command: fd00 : xlnx_zynqmp_qspips_flush_fifo_g: GQSPI command: c403 : xlnx_zynqmp_qspips_flush_fifo_g: GQSPI command: 0 : xlnx_zynqmp_qspips_flush_fifo_g: Dummy GQSPI Delay Command Entry, Do nothing: xlnx_zynqmp_qspips_flush_fifo_g: GQSPI command: f404 : xlnx_zynqmp_qspips_flush_fifo_g: GQSPI command: 0 : xlnx_zynqmp_qspips_flush_fifo_g: Dummy GQSPI Delay Command Entry, Do nothing: xlnx_zynqmp_qspips_flush_fifo_g: GQSPI command: 1f501 : xlnx_zynqmp_qspips_flush_fifo_g: GQSPI command: fd00 : xlnx_zynqmp_qspips_flush_fifo_g: GQSPI command: c403 : xlnx_zynqmp_qspips_flush_fifo_g: GQSPI command: 0 [ 16.844785] m25p80 spi0.0: n25q512a (131072 Kbytes) [ 16.852754] 3 fixed-partitions partitions found on MTD device spi0.0 [ 16.854076] Creating 3 MTD partitions on "spi0.0": [ 16.856450] 0x000000000000-0x000001e00000 : "boot" [ 16.882958] 0x000001e00000-0x000001e40000 : "bootenv" [ 16.896354] 0x000001e40000-0x000004240000 : "kernel"
Adding a Debug Definition
With modules that don't mention print levels, such as
#ifdef CADENCE_UART_ERR_DEBUG #define DB_PRINT(...) do { \ fprintf(stderr, ": %s: ", __func__); \ fprintf(stderr, ## __VA_ARGS__); \ } while (0) #else #define DB_PRINT(...) #endif
Add a definition for CADENCE_UART_ERR_DEBUG
and debug printing will be enabled.
Caveats
In some situations, modules are accessed very frequently.
This means that enabling debug printing for that module will cause a lot of text to be printed.
For example, if enabling debug printing for hw/char/cadence_uart.c
on a ZCU102 default PetaLinux image, we will see all of stdout and stderr redirected to the UART.
# ... p: uart_read: offset:0 data:00000010 : uart_read: offset:2c data:0000000a : uart_write: offset:30 data:00000072 r: uart_read: offset:0 data:00000010 : uart_read: offset:2c data:0000000a : uart_write: offset:30 data:0000006f o: uart_read: offset:0 data:00000010 : uart_read: offset:2c data:0000000a : uart_write: offset:30 data:00000063 c: uart_read: offset:0 data:00000010 : uart_read: offset:2c data:0000000a : uart_write: offset:30 data:00000065 e: uart_read: offset:0 data:00000010 : uart_read: offset:2c data:0000000a : uart_write: offset:30 data:00000073 s: uart_read: offset:0 data:00000010 : uart_read: offset:2c data:0000000a : uart_write: offset:30 data:00000073 s: uart_read: offset:0 data:00000010 : uart_read: offset:2c data:0000000a : uart_write: offset:30 data:00000069 i: uart_read: offset:0 data:00000010 : uart_read: offset:2c data:0000000a : uart_write: offset:30 data:0000006e n: uart_read: offset:0 data:00000010 : uart_read: offset:2c data:0000000a : uart_write: offset:30 data:00000067 g: uart_read: offset:0 data:00000010 : uart_read: offset:2c data:0000000a : uart_write: offset:30 data:00000020 : uart_read: offset:0 data:00000010 : uart_read: offset:2c data:0000000a : uart_write: offset:30 data:00000046 F: uart_read: offset:0 data:00000010 : uart_read: offset:2c data:0000000a : uart_write: offset:30 data:00000044 D: uart_read: offset:0 data:00000010 : uart_read: offset:2c data:0000000a : uart_write: offset:30 data:00000054 T: uart_read: offset:0 data:00000010 : uart_read: offset:2c data:0000000a : uart_write: offset:30 data:0000000d : uart_read: offset:0 data:00000010 : uart_read: offset:2c data:0000000a : uart_write: offset:30 data:0000000a # ...
© Copyright 2019 - 2022 Xilinx Inc. Privacy Policy