Debugging LVGL on Infrabase (QEMU, no hardware needed) — a step-by-step guide
Infrabase's build infrastructure — based on BitBake and OpenEmbedded, but optimized for simplicity and flexibility — continues to evolve, with numerous improvements since our initial release. Today’s guide walks through source-level debugging of an LVGL application, all inside a QEMU/virt64 environment. No hardware needed — just a modern Linux host and developer tools
Today we’ll dive into the execution of an LVGL application and explore the LVGL library internals — all inside a QEMU/virt64 emulated environment. Yep: no hardware required. 🙂
What you’ll do
Build the default Infrabase environment and QEMU.
Enable debug symbols for your LVGL app and the LVGL library.
Launch the emulated GUI and attach VS Code + gdb-multiarch.
Inspect the call stack and step through the LVGL main loop.
1) Build the base system
From a fresh clone, bootstrap and build the default configuration:
$ source env.sh
$ build.sh -aThe first build takes ~45 minutes (it compiles a Buildroot-based root filesystem).
Since we’re using an emulated target, build QEMU as well:
$ build.sh -qStart the graphical environment:
$ ./stg.shYou should see the LVGL demo windows pop up in a desktop-like session.
Tip: stg.sh launches QEMU with a GDB server so your debugger can attach on localhost:1234.

2) Turn on debug symbols for the app and LVGL
We want source-level debugging with function names and line info.
Edit linux/usr/src/lvgl/lv_port_linux/CMakeLists.txt and set a debug build. If there’s a line that sets CMAKE_BUILD_TYPE to Debug, uncomment it (or add this if needed):
set(CMAKE_BUILD_TYPE Debug)
Then clean the user build and rebuild with debug info:
$ cd linux/usr
$ rm -rf build/
$ ./build.sh
# or from repo root:
$ build.sh -uThis ensures your binaries are compiled with -g.
Heads-up: Make sure any stripping step is disabled for your debug build (don’t strip symbols).
3) VS Code debug configuration
Install the C/C++ extension (Microsoft) and ensure gdb-multiarch is available on your host (e.g., sudo apt install gdb-multiarch on Debian/Ubuntu).
Create or update .vscode/launch.json with:
{
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) USR lvglsim",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/linux/usr/build/bin/lvglsim",
"args": [],
"stopAtEntry": false,
"cwd": "${fileDirname}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"miDebuggerPath": "/usr/bin/gdb-multiarch",
"miDebuggerServerAddress": "localhost:1234",
"setupCommands": [
{ "description": "Enable pretty-printing for gdb", "text": "-enable-pretty-printing", "ignoreFailures": true },
{ "text": "set remote Z-packet on" },
{ "text": "set output-radix 16" }
]
}
]
}Why this works: When stg.sh starts QEMU, it exposes a GDB server on localhost:1234. VS Code’s cppdbg connects to it using gdb-multiarch, while program points to your local ELF with symbols so the debugger can resolve sources.
4) Attach, pause, and inspect the stack
Launch the “(gdb) USR lvglsim” configuration in VS Code.
It will auto-connect to QEMU (localhost:1234).
Hit Pause to freeze the target and open the Call Stack.
You should land somewhere inside the LVGL driver/timer machinery.

Typical path:
main()
→ run_loop_fbdev()
→ lv_timer_handler()
→ lv_timer_exec() // internal timer execution
→ … draw/invalidations …
LVGL runs a timer-driven event loop that triggers area invalidation and re-drawing. Stepping through this loop is incredibly useful for understanding rendering flow, timing, and where your app’s events slot in.
5) Handy breakpoints to try
main() — entry point of your app.
run_loop_fbdev() — where the frame-buffer loop hooks into LVGL.
lv_timer_handler() — heart of LVGL’s scheduling.
lv_refr_area() / lv_disp_refr_* — screen refresh paths.
Your widget callbacks (e.g., event handlers) — to trace UI interaction flow.
Pro-tip: If you see an enormous stack, use stack filtering in VS Code and add function breakpoints for specific LVGL symbols to jump right where it matters.
6) Troubleshooting
Can’t connect to localhost:1234?Make sure stg.sh/QEMU is running. If your setup uses a different port, update miDebuggerServerAddress.
No source lines / function names?Confirm CMAKE_BUILD_TYPE=Debug is active and that you rebuilt after cleaning linux/usr/build/. Check your final binary with file linux/usr/build/bin/lvglsim (should mention “with debug_info”).
Wrong architecture warnings in gdb?Use gdb-multiarch (not the host’s default gdb). The target is aarch64/virt64.
What you learn
How LVGL’s timer engine orchestrates redraws.
Where your app code participates in the event → draw pipeline.
How to pause anywhere, read a precise call stack, and step through rendering.
That’s it — enjoy LVGL debugging with Infrabase!
If you haven’t read the intro to our BitBake/OpenEmbedded setup yet, start here.Questions or tips from your own sessions are very welcome in the thread!


