top of page
  • Linkedin

Embedded User Interface

Public·2 members

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 -a

The 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 -q

Start the graphical environment:

$ ./stg.sh

You 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.

ree
The lvglsim application running in the QEMU/virt64 environment

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)

ree
Uncomment the line to enable Debug (line 13)

Then clean the user build and rebuild with debug info:

$ cd linux/usr
$ rm -rf build/
$ ./build.sh
# or from repo root:
$ build.sh -u

This 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.

ree
Inspect the source code and stack trace of a running LVGL application

 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!

21 Views
bottom of page