This guide is written so a first-time user can clone QEMU and JKDBG, copy JKDBG into contrib/plugins, build, and run.
JKDBG is a comprehensive debugging and monitoring toolkit for QEMU virtual machines, designed to streamline VM development, kernel debugging, and system analysis workflows. It bridges the gap between QEMU's powerful emulation capabilities and user-friendly debugging interfaces by providing an integrated GUI, real-time memory monitoring, and enhanced GDB integration.
JKDBG is a GUI-based time-travel debugger capable of virtual hardware debugging. It constructs an execution timeline through high-speed snapshots and provides CPU-centric debugging features such as register history tracking and classification by customizing GDB. It also offers real-time inspection and modification of the virtual machine’s entire physical memory, along with MMU-aware highlighting for intuitive visualization. These capabilities significantly enhance debugging accuracy and efficiency by enabling stable reproduction of nondeterministic errors that occur in kernel and driver environments, and by allowing developers to understand the global system state at a glance. In particular, the snapshot-based timeline can restore the system to any exact moment, allowing repeated root-cause analysis, while the register history and MMU visualization help clarify complex execution flows and memory-mapping structures. Compared to traditional console-based debugging, JKDBG greatly simplifies the analysis process and provides a safe, VM-isolated environment for experimentation, making it highly suitable for iterative debugging and advanced systems research.
- Integrated GUI Interface: Auto-launching Qt-based GUI that connects to QEMU's QMP, GDB, and VNC interfaces, providing unified control and visualization
- Real-time Memory Monitoring: Continuous physical memory dumping with configurable intervals, supporting both file output and POSIX shared memory for low-latency analysis
- Dirty Page Tracking: Efficient dirty page monitoring via message queues, enabling focused analysis of memory modifications during VM execution
- Enhanced GDB Experience: Python plugin with syntax-highlighted disassembly, color-coded register views, and comprehensive register history tracking
- Seamless Integration: Designed to work within QEMU's plugin architecture, requiring minimal configuration while offering extensive customization options
JKDBG is particularly valuable for:
- Embedded Systems Development: Debug ARM/RISC-V/MIPS kernels and bare-metal applications with full register and memory visibility
- Operating System Development: Track kernel behavior, memory allocation patterns, and page fault handling in real-time
- Security Research: Monitor memory modifications, analyze exploit behavior, and trace execution flow through vulnerable code paths
- Reverse Engineering: Capture and analyze memory snapshots at precise execution points with coordinated GDB breakpoints
- Educational Purposes: Learn system internals with enhanced visibility into CPU state, memory layout, and execution flow
- JKDBG plugin (
libjkdbg_plugin.so): boots a helper GUI (qt-vm-launcher), passes QMP/GDB/VNC info, and keeps a helper process alive for debugging/monitoring. - Physmem plugin (
libphysmem.so): periodically dumps guest physical memory to file or POSIX shared memory; can signal via POSIX message queue. - Dirtypages plugin (
libdirtypages.so): publishes dirty-page information via POSIX message queue for external consumers.
- Qt ≥ 6.2 (Widgets, Network) — e.g.
/home/<user>/Qt/6.7.3/gcc_64 - GLib 2.x dev (
libglib2.0-dev) - libvncclient dev (
libvncserver-dev) - CMake ≥ 3.16, Ninja (recommended) or Make, gcc/clang
sudo apt-get update
sudo apt-get install build-essential ninja-build cmake pkg-config \
libglib2.0-dev libvncserver-dev qt6-base-dev qt6-base-dev-tools \
qt6-tools-dev qt6-tools-dev-tools- If you use the Qt online/offline installer, install Qt 6.x (e.g. 6.7.3) and set
CMAKE_PREFIX_PATHto the kit path, e.g./home/<user>/Qt/6.7.3/gcc_64. - On distros without recent Qt packages, download the official Qt installer from https://www.qt.io/download-qt-installer, install desktop GCC kit, and export:
export CMAKE_PREFIX_PATH=/home/<user>/Qt/6.7.3/gcc_64
- Ensure
qmake6/qtpaths6(or Qt6 cmake config files) are discoverable viaCMAKE_PREFIX_PATH.
# QEMU
git clone https://github.com/qemu/qemu.git ~/qemu-10.1.2
# JKDBG (this repo)
git clone <JKDBG_REPO_URL> ~/JKDBGQEMU_ROOT=~/qemu-10.1.2
JKDBG_DIR=~/JKDBG
# Copy all plugin/GUI sources into contrib/plugins
cd $JKDBG_DIR
cp -r GUI_plugins/ "$QEMU_ROOT/contrib/plugins/"
cp dirtypages.c jkdbg_plugin.c physmem.c "$QEMU_ROOT/contrib/plugins/"- After copying, check the changes with
cd "$QEMU_ROOT" && git status. - The GUI CMakeLists should be configured to use the
contrib/plugins/...path.
cd "$QEMU_ROOT"
./configure --enable-plugins # add --target-list=... if needed- QEMU uses Meson/Ninja under the hood.
./configurewrites the Meson build dir ($QEMU_ROOT/build) and build graph. - For QEMU 10.1.3+, add plugins to meson.build in $QEMU_ROOT/contrib/plugins.
- If you change
contrib/plugins/meson.build(e.g., add a plugin), rerun either:./configure --enable-plugins(recreates Meson setup), ormeson setup build --reconfigure(from$QEMU_ROOT) to refresh the graph. Then rerunninja -C build ….
Assuming GUI lives at contrib/plugins/GUI_plugins:
cmake -S "$QEMU_ROOT/contrib/plugins/GUI_plugins" \
-B "$QEMU_ROOT/build/GUI_plugins" \
-DCMAKE_PREFIX_PATH=/home/<user>/Qt/<version>/gcc_64
cmake --build "$QEMU_ROOT/build/GUI_plugins"
# Example output: $QEMU_ROOT/build/GUI_plugins/qt-vm-launcherninja -C "$QEMU_ROOT/build" contrib/plugins/libjkdbg_plugin.so
# Output: $QEMU_ROOT/build/contrib/plugins/libjkdbg_plugin.so- To also build the physmem/dirtypages sample plugins (if needed):
The sources
ninja -C "$QEMU_ROOT/build" contrib/plugins/libphysmem.so ninja -C "$QEMU_ROOT/build" contrib/plugins/libdirtypages.so
physmem.canddirtypages.care copied intocontrib/plugins/by thersyncstep above.
In contrib/plugins/meson.build, append your plugins to the list so Meson creates the targets:
contrib_plugins += ['jkdbg_plugin', 'physmem', 'dirtypages']
if host_os != 'windows'
contrib_plugins += 'lockstep'
endif- File names should match
<name>.c(e.g.,jkdbg_plugin.c,physmem.c,dirtypages.cincontrib/plugins/). - After editing
meson.build, rerun configure to refresh the build graph:
cd "$QEMU_ROOT"
./configure --enable-pluginsThen rerun the ninja command above.
To confirm targets are visible in the build graph:
ninja -C "$QEMU_ROOT/build" -t targets | grep jkdbg
ninja -C "$QEMU_ROOT/build" -t targets | grep physmemcd "$QEMU_ROOT"
export JKDBG_QMP_PORT=4444
export JKDBG_GDB_PORT=1234
export JKDBG_VNC_DISPLAY=<vnc_display> # e.g. 3
# If you want to pin the GUI path:
# export JKDBG_GUI_PATH="$QEMU_ROOT/build/GUI-plugins/qt-vm-launcher"
./build/qemu-system-<arch> \
-L "$QEMU_ROOT/pc-bios" \
-M <machine> \
-m <mem_size> \
-kernel <kernel_image> \
-drive file=<disk_image>,format=qcow2,if=sd \
-display none \
-vnc :${JKDBG_VNC_DISPLAY} -k en-us \
-serial vc \
-monitor telnet:127.0.0.1:5555,server,nowait \
-qmp tcp:localhost:${JKDBG_QMP_PORT},server=on,wait=off \
-S \
-gdb tcp:localhost:${JKDBG_GDB_PORT},ipv4 \
-plugin "$QEMU_ROOT/build/contrib/plugins/libdirtypages.so",mq_name=/qemu-physmem-mq \
-plugin "$QEMU_ROOT/build/contrib/plugins/libphysmem.so",size=1M,out=shm,shm_name=/qemu-physmem,chunk=64K,interval_ms=1000,mq=1 \
-plugin "$QEMU_ROOT/build/contrib/plugins/libjkdbg_plugin.so",gui=1,vnc=${JKDBG_VNC_DISPLAY}- Fill
<arch>/<machine>/<mem_size>/<kernel_image>/<disk_image>/<vnc_display>to match your target (e.g. arm/realview-pb-a8/1G/…). - Suppress audio warnings:
-audio noneor-audiodev none,id=noaudio -machine audiodev=noaudio. - Keymaps/BIOS:
-L "$QEMU_ROOT/pc-bios"to read from the build tree.
JKDBG_GUI_PATHenv<plugin_dir>/jkdbg-gui<plugin_dir>/qt-vm-launcher<plugin_dir>/../qt-vm-launcher<plugin_dir>/plugins/qt-vm-launcher<plugin_dir>/plugins/GUI_plugins/qt-vm-launcher<plugin_dir>/plugins/GUI_plugins/build/qt-vm-launcher
- Tested with Qt ≥ 6.2; built on Qt 6.7.x.
- Qt5 is not recommended (CMake not set up for it).
- libvncclient (LibVNCServer): GPL-2.0-or-later. Linking (static/dynamic) triggers GPL obligations (source, notices, etc.).
- qtermwidget: GPL-2.0-or-later. Some files are LGPL-2.1+/BSD-3-Clause, but the library is distributed as GPL-2.0-or-later, so GPL obligations propagate.
- Qt 6: mostly LGPL-3.0-or-later (some modules GPL/commercial). With LGPL, ensure dynamic linking, notices, and user replacement rights where applicable.
- GLib: LGPL-2.1-or-later. Follow LGPL requirements when dynamically linked.
Summary: libvncclient and qtermwidget are GPL-2.0-or-later, so any binary linking against them (GUI or plugin) must satisfy GPL duties (provide source, preserve notices, same license on redistribution). Even for internal use, review the terms.
- JKDBG plugin sources (
jkdbg_plugin.c,physmem.c,dirtypages.c, etc., yieldingcontrib/plugins/libjkdbg_plugin.so): GPL-2.0-only (project authors). GPL obligations apply when distributing, due to libvncclient/qtermwidget. - GUI sources (
contrib/plugins/GUI_plugins*.cpp,*.h,resources, etc.): should be shipped under a GPL-compatible license; GPL duties apply because they link qtermwidget/libvncclient. - Bundled qtermwidget (
contrib/plugins/GUI_plugins/lib): GPL-2.0-or-later (contains some LGPL-2.1+/BSD-3-Clause files, but overall GPL-2.0-or-later). - External libs:
- libvncclient (LibVNCServer): GPL-2.0-or-later
- Qt 6 modules (Widgets/Network, etc.): mostly LGPL-3.0-or-later (some GPL/commercial)
- GLib: LGPL-2.1-or-later
- Project-authored plugin code is GPL-2.0-only; keep source and copyright/license notices.
- Because we link to GPL libs (libvncclient, qtermwidget), distributing the plugin/GUI requires GPL compliance (source, notices, same license).
- Include
LICENSE(GPL-2.0 text) at repo root and copies of third-party licenses (libvncclient, qtermwidget, Qt, GLib). Add license notices to distributions.
- Final release year: 2025
- Distributor: Team J.K.GO
Load plugins with -plugin <path>,key=value,... on the QEMU command line. Below are the main plugins in this tree and their knobs.
- Purpose: auto-launch the Qt GUI (
qt-vm-launcher) and pass QMP/GDB/VNC info to it. - Options:
gui=1|0— force enable/disable GUI launch. If omitted, behavior follows env/prompt logic (andJKDBG_AUTO_LAUNCH).vnc=<display>— pass VNC display (e.g.vnc=3for:3) to the GUI.
- Environment variables (override or supply defaults):
JKDBG_GUI_PATH— absolute path to the GUI binary (otherwise searched in plugin-relative locations).JKDBG_VNC_DISPLAY— VNC display number if not provided viavnc=.JKDBG_QMP_PORT— QMP port (default 4444).JKDBG_GDB_PORT— GDB port (default 1234).JKDBG_AUTO_LAUNCH—1/yes/trueauto-launches GUI,0/no/falsedisables; if unset, may prompt.
- Search order for GUI:
JKDBG_GUI_PATH, then relative to the plugin:jkdbg-gui,qt-vm-launcher,../qt-vm-launcher,plugins/qt-vm-launcher,plugins/GUI_plugins/qt-vm-launcher,plugins/GUI_plugins/build/qt-vm-launcher.
- Purpose: dump guest physical memory to file or shared memory at intervals; optional message queue coordination.
- Options:
size=<bytes>— total capture size (e.g.1M). Default: guest RAM size (if unset) or plugin default.out=<path|shm>— destination;shmuses POSIX shared memory, otherwise a file path.shm_name=<name>— POSIX shm name whenout=shm(e.g./qemu-physmem).chunk=<bytes>— write chunk size (e.g.64K). Smaller chunks reduce latency but increase overhead.interval_ms=<ms>— sampling interval (e.g.1000for 1s). Lower values sample more frequently.mq=1|0— enable/disable POSIX message queue notifications to coordinate with consumers.
- Purpose: report dirty pages via message queue.
- Options:
mq_name=<name>— POSIX message queue name (e.g./qemu-physmem-mq). The consumer should listen on the same queue.
Example (templated):
./build/qemu-system-<arch> ... \
-plugin "$QEMU_ROOT/build/contrib/plugins/libdirtypages.so",mq_name=/qemu-physmem-mq \
-plugin "$QEMU_ROOT/build/contrib/plugins/libphysmem.so",size=1M,out=shm,shm_name=/qemu-physmem,chunk=64K,interval_ms=1000,mq=1 \
-plugin "$QEMU_ROOT/build/contrib/plugins/libjkdbg_plugin.so",gui=1,vnc=${JKDBG_VNC_DISPLAY}- This plugin adds GUI-Friendly commands for controlling QENU execution and inspecting register output history.
| Command | Description | Usage |
|---|---|---|
c |
Continue execution. Used in the GUI to run QEMU. | c |
Ctrl + C |
Interrupt execution and break into the debugger. | (keyboard interrupt) |
jkhist |
Print register output history collected during the session. | jkhist [reg] -n [number] |
- jkhist Options
- [reg] : Register name to filter history
- -n [number] : Number of recent outputs to display