---
url: /kb/embedded/qemu/qemu-debugging/index.md
---
# QEMU Debugging

This page covers QEMU's internal debug mechanisms: the `-d` flag log categories, the Monitor command reference, the `qemu-system-arm -d help` output decoded, and how to debug QEMU itself as a C program.

This is distinct from the GDB-based guest debugging covered in [QEMU GDB Debugging](/kb/embedded/qemu/qemu-gdb-debugging/). Here the subject is QEMU's own instrumentation for observing emulation internals.

***

## The `-d` Flag

`-d` activates QEMU's internal debug log. Output goes to stderr by default, or to a file with `-D <path>`.

```bash
qemu-system-arm -M mps2-an385 -kernel firmware.elf -nographic \
    -d in_asm,int -D /tmp/qemu-debug.log
```

### Full Flag Reference

```bash
qemu-system-arm -d help
```

| Flag | Description |
|------|-------------|
| `in_asm` | Guest assembly instructions as decoded from the binary |
| `out_asm` | Host assembly instructions generated by TCG |
| `op` | TCG intermediate ops before optimization |
| `op_opt` | TCG intermediate ops after optimization (final form) |
| `op_ind` | TCG ops with instruction indirection info |
| `int` | Hardware interrupts and CPU exceptions (ARM: IRQ, FIQ, SVC, prefetch abort, etc.) |
| `exec` | Every translation block executed (very verbose, slows execution ~10x) |
| `cpu` | CPU register state dump before each TB execution |
| `cpu_reset` | CPU register dump at every reset event |
| `mmu` | TLB misses and refills (softmmu page table walks) |
| `pcall` | x86 protected-mode far calls and task switches |
| `cpu_io` | CPU-level I/O port accesses (x86 IN/OUT instructions) |
| `nochain` | Disable TB chaining; every TB ends with a return to the dispatcher |
| `page` | Page table updates |
| `strace` | Linux system call trace (qemu-user mode only) |
| `unimp` | Guest accesses to unimplemented registers or features |
| `guest_errors` | Guest-level errors: misaligned accesses, undefined instructions |
| `trace:*` | Enable trace events matching a pattern (see Tracing page) |

Multiple flags are comma-separated: `-d in_asm,op_opt,int`

***

## Reading `in_asm` Output

`in_asm` shows guest code as QEMU decodes it for translation:

```
----------------
IN: Reset_Handler
0x00000008:  e59f0010  ldr r0, [pc, #16]   ; load _sidata
0x0000000c:  e59f1010  ldr r1, [pc, #16]   ; load _sdata
0x00000010:  e59f2010  ldr r2, [pc, #16]   ; load _edata
```

The format is: `<physical address>: <hex encoding>  <disassembly>`

The TB boundary is shown by the `IN:` header. Each new `IN:` starts a new translation block (e.g., after a branch or interrupt).

***

## Reading `out_asm` Output

`out_asm` shows the x86\_64 (or host architecture) code TCG generated:

```
OUT: [size=108]
0x7f3c4a000000:  push   %rbp
0x7f3c4a000001:  mov    %rsp,%rbp
0x7f3c4a000010:  mov    0x18(%rbx),%eax     ; load ARM r0
0x7f3c4a000014:  mov    0x50(%rbx),%ecx     ; load ARM pc
0x7f3c4a000018:  add    $0x10,%ecx          ; pc + 16
0x7f3c4a00001c:  mov    (%rcx),%eax         ; LDR r0, [pc, #16]
```

`%rbx` is QEMU's CPU state pointer (`CPUARMState *`). Register accesses are struct field loads/stores.

***

## Reading `int` Output

`int` logs exception entry and exit:

```
IRQ 5 raised              ; NVIC asserts IRQ5 (e.g., UART0)
Entering exception 21     ; IRQ5 = exception number 5+16=21
...
Returning from exception 21
```

For debugging interrupt handling in firmware, `-d int` is invaluable. Common ARM abort types visible in this log:

| Exception name | Exception number | Likely cause |
|---------------|-----------------|--------------|
| Reset | 1 | Watchdog, manual reset |
| HardFault | 3 | Unhandled fault escalation |
| MemManage | 4 | MPU violation |
| BusFault | 5 | Bad memory address |
| UsageFault | 6 | Undefined instruction, divide-by-zero |
| SVC | 11 | Software interrupt (RTOS syscall) |
| PendSV | 14 | Context switch (RTOS) |
| SysTick | 15 | Timer tick |
| IRQ0–239 | 16–255 | Peripheral interrupts |

***

## Reading `mmu` Output

`mmu` logs TLB fills and page table walks (relevant for Linux guests, not bare-metal):

```
TLB fill: vaddr=0xc0008000 paddr=0x80008000 prot=rwx
```

Useful for diagnosing page mapping issues or debugging early boot where the MMU is enabled/disabled.

***

## Reading `unimp` and `guest_errors`

```bash
-d unimp,guest_errors
```

`unimp` fires when guest code accesses a register QEMU hasn't implemented:

```
hw/arm/mps2-an385: unimplemented read of register at offset 0x500
```

`guest_errors` fires on architectural errors:

```
Guest ARM: Undefined instruction 0xe7f001f0 at pc=0x00000234
```

These are critical for discovering which peripherals your firmware uses that QEMU doesn't model.

***

## `-D <logfile>`: Redirecting Debug Output

By default `-d` output goes to stderr, which is mixed with serial console output in `-nographic` mode. Always redirect:

```bash
-D /tmp/qemu-debug.log -d in_asm,int,unimp
```

Then in another terminal:

```bash
tail -f /tmp/qemu-debug.log
```

***

## QEMU Monitor Reference

The Monitor is an interactive console for controlling the running VM. Access via:

* `Ctrl-A C` (when using `-nographic` or `-serial mon:stdio`)
* `-monitor stdio` (dedicate stdio to monitor)
* `-monitor unix:/tmp/mon.sock,server,nowait` (Unix socket)

### Execution Control

```
stop                    # pause all vCPUs
cont                    # resume
quit                    # exit QEMU
system_reset            # hardware reset
system_powerdown        # ACPI shutdown signal
```

### CPU Inspection

```
info registers          # all CPU registers (current vCPU)
info registers -a       # all vCPUs
info cpus               # list vCPUs and their state
```

### Memory Inspection

```
x /N[fmt][size] <addr>  # virtual memory examine
xp /N[fmt][size] <addr> # physical memory examine

# Formats: x=hex, d=decimal, u=unsigned, o=octal, s=string, i=instructions
# Sizes:   b=1, h=2, w=4, g=8
# Examples:
x /8xw 0x20000000       # 8 hex words from SRAM start
x /16i $pc             # 16 instructions from PC (requires ELF symbols loaded)
xp /4xg 0x00000000      # 4 quadwords at physical address 0

p <expr>               # evaluate expression
p $r0                  # print ARM register r0
p 0x40004000           # print address value
```

### Device and Memory Map Inspection

```
info mtree              # memory region tree (full address space layout)
info qtree              # QOM device tree
info block              # block devices
info network            # network interfaces
info chardev            # character devices
info usb                # USB devices
info pcie               # PCIe devices
info snapshots          # saved VM snapshots
```

### Block and Snapshots

```
drive_add 0 file=disk2.qcow2,format=qcow2,if=none,id=hd1
device_add virtio-blk-pci,drive=hd1      # hot-plug
device_del virtio-blk-pci-0              # hot-remove

savevm <tag>            # save snapshot
loadvm <tag>            # restore snapshot
delvm <tag>             # delete snapshot
migrate file:/tmp/vm.state   # save full state to file
```

***

## Debugging QEMU Source Itself

For contributors diagnosing QEMU bugs or developing device models:

### Build with Debug Symbols

```bash
cd qemu-build
../qemu/configure --target-list=arm-softmmu \
    --enable-debug \
    --disable-strip \
    --extra-cflags="-O0 -g3"
ninja -j$(nproc)
```

`--enable-debug` adds `-g` and disables some optimizations. `-O0` (via extra flags) further improves GDB stepping quality at the cost of QEMU performance.

### GDB on QEMU Itself

```bash
gdb --args ./arm-softmmu/qemu-system-arm \
    -M mps2-an385 -kernel firmware.elf -nographic

(gdb) b pl011_write                   # break in PL011 TX
(gdb) b memory_region_dispatch_write  # break on any MMIO write
(gdb) run
```

Useful breakpoint locations for device model debugging:

| Function | Purpose |
|----------|---------|
| `memory_region_dispatch_write` | Any guest MMIO write |
| `memory_region_dispatch_read` | Any guest MMIO read |
| `cpu_exec` | Before each TB execution |
| `tb_gen_code` | TCG translates a new TB |
| `arm_cpu_do_interrupt` | ARM exception entry |
| `qemu_set_irq` | IRQ assertion |
| `qemu_chr_fe_write_all` | Chardev output |

### AddressSanitizer

Build QEMU with ASAN to catch memory bugs in device models:

```bash
../qemu/configure --target-list=arm-softmmu \
    --enable-sanitizers
ninja -j$(nproc)
```

ASAN will report use-after-free, buffer overflows, and use-before-initialization in QEMU's C code.
