mirror of
https://github.com/lvgl/lvgl.git
synced 2026-05-20 12:32:18 +08:00
docs: modernize to .md/.mdx for new Fumadocs-based docs engine (#9993)
Co-authored-by: André Costa <andre_miguel_costa@hotmail.com>
This commit is contained in:
committed by
GitHub
parent
fc1452573c
commit
4750fe054b
@@ -0,0 +1,399 @@
|
||||
---
|
||||
title: "GDB Plug-In"
|
||||
description: "To facilitate debugging LVGL with GDB, a GDB plugin is provided. This plugin can be found in the lvgl/scripts/gdb directory."
|
||||
---
|
||||
|
||||
## Debugging LVGL with GDB
|
||||
|
||||
To facilitate debugging LVGL with GDB, a GDB plugin is provided. This plugin
|
||||
can be found in the `lvgl/scripts/gdb` directory. The GDB plugin can be used
|
||||
with any target where GDB is available. For example, you can use it to debug a
|
||||
device connected to a PC via JLink, which provides a GDB server. Additionally,
|
||||
if your device crashes and you have a core dump, you can use GDB to analyze the
|
||||
core dump. To load the LVGL GDB plugin within GDB's command line, type the
|
||||
following command:
|
||||
|
||||
`source lvgl/scripts/gdb/gdbinit.py`
|
||||
|
||||
Example of usage:
|
||||
|
||||
```bash
|
||||
(gdb) source lvgl/scripts/gdb/gdbinit.py
|
||||
|
||||
(gdb) dump obj -L 2
|
||||
obj@0x60700000dd10 0,0,799,599
|
||||
tabview@0x608000204ca0 0,0,799,599
|
||||
obj@0x607000025da0 0,0,799,69
|
||||
obj@0x607000025e80 0,70,799,599
|
||||
obj@0x60700002bd70 743,543,791,591
|
||||
btn@0x60700002c7f0 747,547,787,587
|
||||
keyboard@0x60d0000f7040 0,300,799,599
|
||||
dropdown-list@0x608000205420 0,0,129,129
|
||||
label@0x60d0000f7ba0 22,22,56,39
|
||||
(gdb)
|
||||
```
|
||||
|
||||
The plugin provides the following commands.
|
||||
|
||||
- `dump obj`: Dump the object tree.
|
||||
- `dump display`: Export display draw buffers to image files (BMP/PNG).
|
||||
- `dump cache`: Dump image or image header cache entries.
|
||||
- `check cache`: Run sanity check on image or image header cache.
|
||||
- `dump anim`: List all active animations.
|
||||
- `dump timer`: List all active timers.
|
||||
- `dump indev`: List all input devices.
|
||||
- `dump group`: List all focus groups with objects.
|
||||
- `dump image_decoder`: List all registered image decoders.
|
||||
- `dump fs_drv`: List all registered filesystem drivers.
|
||||
- `dump draw_task <expr>`: List draw tasks from a layer.
|
||||
- `dump dashboard`: Generate an HTML dashboard of all LVGL runtime state.
|
||||
- `info style`: Inspect style properties of an `lv_style_t` or an `lv_obj_t`.
|
||||
- `info draw_unit`: Print raw struct details for each drawing unit.
|
||||
- `info obj_class <expr>`: Show object class hierarchy.
|
||||
- `info subject <expr>`: Show subject and its observers.
|
||||
- `lvglobal`: (NuttX only) Set which LVGL instance to inspect.
|
||||
|
||||
<Callout type="info">
|
||||
Some versions of `gdb` on Windows (e.g. those delivered with various versions
|
||||
of Perl) are compiled without Python support, so the `source` command will not
|
||||
be supported.
|
||||
</Callout>
|
||||
|
||||
## Dump Obj Tree
|
||||
|
||||
`dump obj`: Dump the object tree.
|
||||
|
||||
`dump obj -L 2`: Dump the object tree with a depth of 2.
|
||||
|
||||
`dump obj <root_expr>`: Dump the object tree starting from the specified object.
|
||||
|
||||
## Inspect Style
|
||||
|
||||
`info style <style_var>`: Inspect a single `lv_style_t` variable. Properties are
|
||||
displayed with resolved names and formatted values (colors shown as hex).
|
||||
|
||||
`info style --obj <obj_var>`: Inspect all styles of an `lv_obj_t`, grouped by
|
||||
style slot with selector and flags.
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
(gdb) info style my_style
|
||||
+-----------+---------+
|
||||
| prop | value |
|
||||
+-----------+---------+
|
||||
| BG_COLOR | #ff0000 |
|
||||
| BG_OPA | 255 |
|
||||
+-----------+---------+
|
||||
|
||||
(gdb) info style --obj lv_global->disp_default->act_scr
|
||||
[0] MAIN|DEFAULT local
|
||||
+-----------+---------+
|
||||
| prop | value |
|
||||
+-----------+---------+
|
||||
| BG_COLOR | #ff0000 |
|
||||
+-----------+---------+
|
||||
```
|
||||
|
||||
## Connecting to a Debugger
|
||||
|
||||
This command provides the ability to connect and debug GDB Python Script using IDE.
|
||||
|
||||
Connect to `PyCharm` / `VSCode` / `Eclipse (not supported yet)`
|
||||
|
||||
`debugger -t pycharm`
|
||||
|
||||
`debugger -t vscode`
|
||||
|
||||
`debugger -t eclipse`
|
||||
|
||||
Perform a web search for `pydevd_pycharm` or `debugpy` for details about how to
|
||||
use the debugger.
|
||||
|
||||
## Dump Display
|
||||
|
||||
`dump display`: Export the current display's draw buffers (buf_1, buf_2) to image files.
|
||||
|
||||
```bash
|
||||
(gdb) dump display
|
||||
(gdb) dump display -p /tmp/ -f png
|
||||
```
|
||||
|
||||
## Check Cache
|
||||
|
||||
`check cache <type>`: Run sanity check on image or image header cache, validating
|
||||
cross-pointers between red-black tree and linked list, decoded pointers, image sizes,
|
||||
and source pointers. `<type>` is `image` or `image_header`.
|
||||
|
||||
## Dump Animations
|
||||
|
||||
`dump anim`: List all active animations in a table with exec_cb, value range,
|
||||
duration, repeat count, and status.
|
||||
|
||||
`dump anim --detail`: Print detailed info for each animation.
|
||||
|
||||
## Dump Timers
|
||||
|
||||
`dump timer`: List all active timers with callback, period, frequency, last_run,
|
||||
repeat count, and paused state.
|
||||
|
||||
## Dump Input Devices
|
||||
|
||||
`dump indev`: List all registered input devices with type, state, read callback,
|
||||
and configuration (long_press_time, scroll_limit, group).
|
||||
|
||||
## Dump Focus Groups
|
||||
|
||||
`dump group`: List all focus groups with object count, frozen/editing/wrap state,
|
||||
and focused object.
|
||||
|
||||
## Dump Image Decoders
|
||||
|
||||
`dump image_decoder`: List all registered image decoders with name, info_cb,
|
||||
open_cb, and close_cb.
|
||||
|
||||
## Dump Filesystem Drivers
|
||||
|
||||
`dump fs_drv`: List all registered filesystem drivers with drive letter, driver type,
|
||||
cache size, and callbacks (open, read, write, close).
|
||||
|
||||
## Dump Draw Tasks
|
||||
|
||||
`dump draw_task <layer_expr>`: Walk the draw task linked list from a layer expression
|
||||
and display each task's type, state, area, opacity, and preferred draw unit id.
|
||||
|
||||
## Dump Dashboard
|
||||
|
||||
`dump dashboard`: Collect all LVGL runtime state (displays, object trees,
|
||||
animations, timers, caches, input devices, groups, draw units/tasks,
|
||||
subjects/observers, image decoders, filesystem drivers) and generate a
|
||||
self-contained HTML file for offline browsing.
|
||||
|
||||
The dashboard supports three output modes:
|
||||
|
||||
- `dump dashboard`: Generate `lvgl_dashboard.html` with all data embedded.
|
||||
- `dump dashboard --json`: Export raw JSON data to `lvgl_dashboard.json`.
|
||||
- `dump dashboard --viewer`: Generate an empty HTML viewer (`lvgl_viewer.html`)
|
||||
that can load JSON files via drag-and-drop.
|
||||
|
||||
Use `-o <path>` to specify a custom output path.
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
(gdb) dump dashboard
|
||||
Dashboard written to lvgl_dashboard.html (1.23s)
|
||||
|
||||
(gdb) dump dashboard --json -o /tmp/state.json
|
||||
Dashboard written to /tmp/state.json (0.98s)
|
||||
|
||||
(gdb) dump dashboard --viewer
|
||||
Viewer written to lvgl_viewer.html
|
||||
```
|
||||
|
||||
The generated HTML is fully self-contained (no external dependencies) and
|
||||
includes a sidebar for navigation, a search box for filtering, collapsible
|
||||
object trees with style details, framebuffer image previews, and cross-reference
|
||||
links between related objects.
|
||||
|
||||
## Inspect Object Class
|
||||
|
||||
`info obj_class <expr>`: Show the class hierarchy chain for an `lv_obj_class_t`.
|
||||
|
||||
`info obj_class --all`: List all registered object classes in a table.
|
||||
|
||||
Example:
|
||||
|
||||
```text
|
||||
(gdb) info obj_class lv_button_class
|
||||
ObjClass: lv_button -> lv_obj -> lv_obj
|
||||
name = lv_button
|
||||
base = lv_obj
|
||||
size = 48 editable=0 group_def=2
|
||||
editable = 0
|
||||
group_def = 2
|
||||
default_size = (CONTENT, CONTENT) theme_inheritable=True
|
||||
theme_inh = True
|
||||
```
|
||||
|
||||
## Inspect Subject
|
||||
|
||||
`info subject <expr>`: Show a subject's type and all its observers.
|
||||
|
||||
Example:
|
||||
|
||||
```text
|
||||
(gdb) info subject &my_subject
|
||||
Subject: type=INT subscribers=2
|
||||
Observer: cb=<my_cb> target=0x... for_obj=True
|
||||
```
|
||||
|
||||
## Set LVGL Instance (NuttX)
|
||||
|
||||
`lvglobal`: Set which LVGL instance to inspect by finding the `lv_global`
|
||||
pointer. On single-instance systems, it auto-detects the global. On NuttX
|
||||
multi-process systems, use `--pid` to specify the target process.
|
||||
|
||||
```bash
|
||||
(gdb) lvglobal
|
||||
(gdb) lvglobal --pid 3
|
||||
```
|
||||
|
||||
## Data Export API
|
||||
|
||||
Each wrapper class provides a `snapshot()` method that returns a `Snapshot
|
||||
object containing a pure Python dict (JSON-serializable) plus an optional
|
||||
reference to the original wrapper via source`.
|
||||
|
||||
```python
|
||||
from lvglgdb import LVTimer, curr_inst
|
||||
|
||||
timers = list(curr_inst().timers())
|
||||
snap = timers[0].snapshot()
|
||||
|
||||
# Dict-like access
|
||||
print(snap["timer_cb"], snap["period"])
|
||||
|
||||
# JSON serialization
|
||||
import json
|
||||
print(json.dumps(snap.as_dict(), indent=2))
|
||||
|
||||
# Bulk export
|
||||
snapshots = LVTimer.snapshots(timers)
|
||||
data = [s.as_dict() for s in snapshots]
|
||||
```
|
||||
|
||||
The `Snapshot` class supports dict-like read access (`[]`, `keys()`,
|
||||
`len()`, `in`, iteration) and `as_dict()` for JSON serialization.
|
||||
All values in `as_dict()` are pure Python types (`str`, `int`, `float`,
|
||||
`bool`, `None`, `dict`, `list`) with no `gdb.Value` references.
|
||||
|
||||
Wrapper classes with `snapshot()` support: `LVAnim`, `LVTimer`,
|
||||
`LVIndev`, `LVGroup`, `LVObject`, `LVObjClass`, `LVObserver`,
|
||||
`LVSubject`, `LVDrawTask`, `LVDrawUnit`, `LVFsDrv`,
|
||||
`LVImageDecoder`, `LVCache`, `LVDisplay`,
|
||||
`LVRedBlackTree`, `LVEventDsc`, `LVList`.
|
||||
|
||||
`LVStyle` provides `snapshots()` (plural) which returns a list of
|
||||
`Snapshot` objects for each style property, but does not have a singular
|
||||
`snapshot()` method.
|
||||
|
||||
Most wrapper classes also provide a static `snapshots(items)` method for
|
||||
bulk export (e.g. `LVAnim.snapshots(anims)`). Additionally,
|
||||
`LVImageCache` and `LVImageHeaderCache` provide instance-level
|
||||
`snapshots()` methods that export all cache entries.
|
||||
|
||||
## Architecture
|
||||
|
||||
The GDB plugin is organized into four layers. The overview below shows how
|
||||
terminal commands and the HTML dashboard both flow through the same snapshot
|
||||
abstraction down to raw GDB memory access:
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
subgraph "Rendering Layer"
|
||||
CLI["GDB Terminal<br/>dump obj, info style, ..."]
|
||||
DASH["HTML Dashboard<br/>dump dashboard"]
|
||||
end
|
||||
|
||||
subgraph "Formatter / Renderer"
|
||||
FMT["formatter.py<br/>print_info · print_spec_table"]
|
||||
HR["html_renderer.py<br/>template + CSS + JS"]
|
||||
end
|
||||
|
||||
subgraph "Data Collection"
|
||||
DC["data_collector.py<br/>collect_all() → JSON dict"]
|
||||
SNAP["Snapshot<br/>_data + _display_spec"]
|
||||
end
|
||||
|
||||
subgraph "Value Wrappers (lvgl/)"
|
||||
W["LVObject · LVDisplay · LVAnim<br/>LVCache · LVTimer · LVDrawBuf<br/>LVIndev · LVGroup · ..."]
|
||||
GDB["gdb.Value (C struct memory)"]
|
||||
end
|
||||
|
||||
CLI --> FMT
|
||||
FMT --> SNAP
|
||||
DASH --> HR
|
||||
HR --> DC
|
||||
DC --> SNAP
|
||||
SNAP --> W
|
||||
W --> GDB
|
||||
|
||||
style CLI fill:#4CAF50,color:#fff
|
||||
style DASH fill:#4CAF50,color:#fff
|
||||
style FMT fill:#FF9800,color:#fff
|
||||
style HR fill:#FF9800,color:#fff
|
||||
style DC fill:#2196F3,color:#fff
|
||||
style SNAP fill:#2196F3,color:#fff
|
||||
style W fill:#9C27B0,color:#fff
|
||||
style GDB fill:#616161,color:#fff
|
||||
```
|
||||
|
||||
Each wrapper class declares a `_DISPLAY_SPEC` describing its fields and
|
||||
exports a `snapshot()` method that returns a self-describing `Snapshot`
|
||||
object carrying both the data dict and the display spec. The `cmds/` layer
|
||||
simply passes snapshots to generic formatters (`print_info`,
|
||||
`print_spec_table`) which read the embedded spec to render output — no
|
||||
command needs to know the internal structure of any wrapper. The detailed
|
||||
snapshot flow is shown below:
|
||||
|
||||
```mermaid
|
||||
graph RL
|
||||
subgraph "cmds/ layer"
|
||||
CMD["GDB Commands<br/>(dump obj, info style, ...)"]
|
||||
end
|
||||
|
||||
subgraph "formatter.py"
|
||||
PI["print_info(snapshot)"]
|
||||
PT["print_table()"]
|
||||
PST["print_spec_table(snapshots)"]
|
||||
RTC["resolve_table_columns(spec)"]
|
||||
PST --> RTC
|
||||
PST --> PT
|
||||
end
|
||||
|
||||
subgraph "snapshot.py"
|
||||
SNAP["Snapshot<br/>._data (pure dict)<br/>._display_spec<br/>._source"]
|
||||
end
|
||||
|
||||
subgraph "data_utils.py"
|
||||
DU["ptr_or_none()<br/>fmt_cb()<br/>..."]
|
||||
end
|
||||
|
||||
subgraph "Wrapper classes"
|
||||
direction TB
|
||||
DS["_DISPLAY_SPEC<br/>{info, table, empty_msg}"]
|
||||
SN["snapshot() → Snapshot"]
|
||||
SNS["snapshots() → list[Snapshot]"]
|
||||
SN --> SNAP
|
||||
SN -. "display_spec=" .-> DS
|
||||
SNS --> SN
|
||||
end
|
||||
|
||||
subgraph "Wrappers"
|
||||
direction LR
|
||||
W1["LVAnim"]
|
||||
W2["LVTimer"]
|
||||
W3["LVCache"]
|
||||
W4["LVObject"]
|
||||
W5["LVGroup"]
|
||||
W6["LVIndev"]
|
||||
W7["...others"]
|
||||
end
|
||||
|
||||
CMD -- "wrapper.snapshot()" --> SN
|
||||
CMD -- "print_info(snap)" --> PI
|
||||
CMD -- "print_spec_table(snaps)" --> PST
|
||||
PI -- "reads _display_spec" --> SNAP
|
||||
PST -- "reads _display_spec" --> SNAP
|
||||
SN -- "uses" --> DU
|
||||
W1 & W2 & W3 & W4 & W5 & W6 & W7 -. "each defines" .-> DS
|
||||
|
||||
style PI fill:#4CAF50,color:#fff
|
||||
style PT fill:#4CAF50,color:#fff
|
||||
style PST fill:#4CAF50,color:#fff
|
||||
style RTC fill:#4CAF50,color:#fff
|
||||
style SNAP fill:#2196F3,color:#fff
|
||||
style DU fill:#FF9800,color:#fff
|
||||
style DS fill:#9C27B0,color:#fff
|
||||
```
|
||||
@@ -1,442 +0,0 @@
|
||||
.. _gdb_plugin:
|
||||
|
||||
===========
|
||||
GDB Plug-In
|
||||
===========
|
||||
|
||||
Debugging LVGL with GDB
|
||||
***********************
|
||||
|
||||
To facilitate debugging LVGL with GDB, a GDB plugin is provided. This plugin
|
||||
can be found in the ``lvgl/scripts/gdb`` directory. The GDB plugin can be used
|
||||
with any target where GDB is available. For example, you can use it to debug a
|
||||
device connected to a PC via JLink, which provides a GDB server. Additionally,
|
||||
if your device crashes and you have a core dump, you can use GDB to analyze the
|
||||
core dump. To load the LVGL GDB plugin within GDB's command line, type the
|
||||
following command:
|
||||
|
||||
``source lvgl/scripts/gdb/gdbinit.py``
|
||||
|
||||
|
||||
Example of usage:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
(gdb) source lvgl/scripts/gdb/gdbinit.py
|
||||
|
||||
(gdb) dump obj -L 2
|
||||
obj@0x60700000dd10 0,0,799,599
|
||||
tabview@0x608000204ca0 0,0,799,599
|
||||
obj@0x607000025da0 0,0,799,69
|
||||
obj@0x607000025e80 0,70,799,599
|
||||
obj@0x60700002bd70 743,543,791,591
|
||||
btn@0x60700002c7f0 747,547,787,587
|
||||
keyboard@0x60d0000f7040 0,300,799,599
|
||||
dropdown-list@0x608000205420 0,0,129,129
|
||||
label@0x60d0000f7ba0 22,22,56,39
|
||||
(gdb)
|
||||
|
||||
The plugin provides the following commands.
|
||||
|
||||
- ``dump obj``: Dump the object tree.
|
||||
- ``dump display``: Export display draw buffers to image files (BMP/PNG).
|
||||
- ``dump cache``: Dump image or image header cache entries.
|
||||
- ``check cache``: Run sanity check on image or image header cache.
|
||||
- ``dump anim``: List all active animations.
|
||||
- ``dump timer``: List all active timers.
|
||||
- ``dump indev``: List all input devices.
|
||||
- ``dump group``: List all focus groups with objects.
|
||||
- ``dump image_decoder``: List all registered image decoders.
|
||||
- ``dump fs_drv``: List all registered filesystem drivers.
|
||||
- ``dump draw_task <expr>``: List draw tasks from a layer.
|
||||
- ``dump dashboard``: Generate an HTML dashboard of all LVGL runtime state.
|
||||
- ``info style``: Inspect style properties of an ``lv_style_t`` or an ``lv_obj_t``.
|
||||
- ``info draw_unit``: Print raw struct details for each drawing unit.
|
||||
- ``info obj_class <expr>``: Show object class hierarchy.
|
||||
- ``info subject <expr>``: Show subject and its observers.
|
||||
- ``lvglobal``: (NuttX only) Set which LVGL instance to inspect.
|
||||
|
||||
.. note::
|
||||
|
||||
Some versions of ``gdb`` on Windows (e.g. those delivered with various versions
|
||||
of Perl) are compiled without Python support, so the ``source`` command will not
|
||||
be supported.
|
||||
|
||||
|
||||
|
||||
Dump Obj Tree
|
||||
*************
|
||||
|
||||
``dump obj``: Dump the object tree.
|
||||
|
||||
``dump obj -L 2``: Dump the object tree with a depth of 2.
|
||||
|
||||
``dump obj <root_expr>``: Dump the object tree starting from the specified object.
|
||||
|
||||
|
||||
|
||||
Inspect Style
|
||||
*************
|
||||
|
||||
``info style <style_var>``: Inspect a single ``lv_style_t`` variable. Properties are
|
||||
displayed with resolved names and formatted values (colors shown as hex).
|
||||
|
||||
``info style --obj <obj_var>``: Inspect all styles of an ``lv_obj_t``, grouped by
|
||||
style slot with selector and flags.
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
(gdb) info style my_style
|
||||
+-----------+---------+
|
||||
| prop | value |
|
||||
+-----------+---------+
|
||||
| BG_COLOR | #ff0000 |
|
||||
| BG_OPA | 255 |
|
||||
+-----------+---------+
|
||||
|
||||
(gdb) info style --obj lv_global->disp_default->act_scr
|
||||
[0] MAIN|DEFAULT local
|
||||
+-----------+---------+
|
||||
| prop | value |
|
||||
+-----------+---------+
|
||||
| BG_COLOR | #ff0000 |
|
||||
+-----------+---------+
|
||||
|
||||
|
||||
|
||||
Connecting to a Debugger
|
||||
************************
|
||||
|
||||
This command provides the ability to connect and debug GDB Python Script using IDE.
|
||||
|
||||
Connect to ``PyCharm`` / ``VSCode`` / ``Eclipse (not supported yet)``
|
||||
|
||||
``debugger -t pycharm``
|
||||
|
||||
``debugger -t vscode``
|
||||
|
||||
``debugger -t eclipse``
|
||||
|
||||
Perform a web search for ``pydevd_pycharm`` or ``debugpy`` for details about how to
|
||||
use the debugger.
|
||||
|
||||
|
||||
Dump Display
|
||||
************
|
||||
|
||||
``dump display``: Export the current display's draw buffers (buf_1, buf_2) to image files.
|
||||
|
||||
.. code:: bash
|
||||
|
||||
(gdb) dump display
|
||||
(gdb) dump display -p /tmp/ -f png
|
||||
|
||||
|
||||
Check Cache
|
||||
***********
|
||||
|
||||
``check cache <type>``: Run sanity check on image or image header cache, validating
|
||||
cross-pointers between red-black tree and linked list, decoded pointers, image sizes,
|
||||
and source pointers. ``<type>`` is ``image`` or ``image_header``.
|
||||
|
||||
|
||||
Dump Animations
|
||||
***************
|
||||
|
||||
``dump anim``: List all active animations in a table with exec_cb, value range,
|
||||
duration, repeat count, and status.
|
||||
|
||||
``dump anim --detail``: Print detailed info for each animation.
|
||||
|
||||
|
||||
Dump Timers
|
||||
***********
|
||||
|
||||
``dump timer``: List all active timers with callback, period, frequency, last_run,
|
||||
repeat count, and paused state.
|
||||
|
||||
|
||||
Dump Input Devices
|
||||
******************
|
||||
|
||||
``dump indev``: List all registered input devices with type, state, read callback,
|
||||
and configuration (long_press_time, scroll_limit, group).
|
||||
|
||||
|
||||
Dump Focus Groups
|
||||
*****************
|
||||
|
||||
``dump group``: List all focus groups with object count, frozen/editing/wrap state,
|
||||
and focused object.
|
||||
|
||||
|
||||
Dump Image Decoders
|
||||
*******************
|
||||
|
||||
``dump image_decoder``: List all registered image decoders with name, info_cb,
|
||||
open_cb, and close_cb.
|
||||
|
||||
|
||||
Dump Filesystem Drivers
|
||||
***********************
|
||||
|
||||
``dump fs_drv``: List all registered filesystem drivers with drive letter, driver type,
|
||||
cache size, and callbacks (open, read, write, close).
|
||||
|
||||
|
||||
Dump Draw Tasks
|
||||
***************
|
||||
|
||||
``dump draw_task <layer_expr>``: Walk the draw task linked list from a layer expression
|
||||
and display each task's type, state, area, opacity, and preferred draw unit id.
|
||||
|
||||
|
||||
Dump Dashboard
|
||||
**************
|
||||
|
||||
``dump dashboard``: Collect all LVGL runtime state (displays, object trees,
|
||||
animations, timers, caches, input devices, groups, draw units/tasks,
|
||||
subjects/observers, image decoders, filesystem drivers) and generate a
|
||||
self-contained HTML file for offline browsing.
|
||||
|
||||
The dashboard supports three output modes:
|
||||
|
||||
- ``dump dashboard``: Generate ``lvgl_dashboard.html`` with all data embedded.
|
||||
- ``dump dashboard --json``: Export raw JSON data to ``lvgl_dashboard.json``.
|
||||
- ``dump dashboard --viewer``: Generate an empty HTML viewer (``lvgl_viewer.html``)
|
||||
that can load JSON files via drag-and-drop.
|
||||
|
||||
Use ``-o <path>`` to specify a custom output path.
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
(gdb) dump dashboard
|
||||
Dashboard written to lvgl_dashboard.html (1.23s)
|
||||
|
||||
(gdb) dump dashboard --json -o /tmp/state.json
|
||||
Dashboard written to /tmp/state.json (0.98s)
|
||||
|
||||
(gdb) dump dashboard --viewer
|
||||
Viewer written to lvgl_viewer.html
|
||||
|
||||
The generated HTML is fully self-contained (no external dependencies) and
|
||||
includes a sidebar for navigation, a search box for filtering, collapsible
|
||||
object trees with style details, framebuffer image previews, and cross-reference
|
||||
links between related objects.
|
||||
|
||||
|
||||
Inspect Object Class
|
||||
********************
|
||||
|
||||
``info obj_class <expr>``: Show the class hierarchy chain for an ``lv_obj_class_t``.
|
||||
|
||||
``info obj_class --all``: List all registered object classes in a table.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
(gdb) info obj_class lv_button_class
|
||||
ObjClass: lv_button -> lv_obj -> lv_obj
|
||||
name = lv_button
|
||||
base = lv_obj
|
||||
size = 48 editable=0 group_def=2
|
||||
editable = 0
|
||||
group_def = 2
|
||||
default_size = (CONTENT, CONTENT) theme_inheritable=True
|
||||
theme_inh = True
|
||||
|
||||
Inspect Subject
|
||||
***************
|
||||
|
||||
``info subject <expr>``: Show a subject's type and all its observers.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
(gdb) info subject &my_subject
|
||||
Subject: type=INT subscribers=2
|
||||
Observer: cb=<my_cb> target=0x... for_obj=True
|
||||
|
||||
|
||||
Set LVGL Instance (NuttX)
|
||||
*************************
|
||||
|
||||
``lvglobal``: Set which LVGL instance to inspect by finding the ``lv_global``
|
||||
pointer. On single-instance systems, it auto-detects the global. On NuttX
|
||||
multi-process systems, use ``--pid`` to specify the target process.
|
||||
|
||||
.. code:: bash
|
||||
|
||||
(gdb) lvglobal
|
||||
(gdb) lvglobal --pid 3
|
||||
|
||||
|
||||
Data Export API
|
||||
***************
|
||||
|
||||
Each wrapper class provides a ``snapshot()`` method that returns a ``Snapshot``
|
||||
object containing a pure Python dict (JSON-serializable) plus an optional
|
||||
reference to the original wrapper via ``_source``.
|
||||
|
||||
.. code:: python
|
||||
|
||||
from lvglgdb import LVTimer, curr_inst
|
||||
|
||||
timers = list(curr_inst().timers())
|
||||
snap = timers[0].snapshot()
|
||||
|
||||
# Dict-like access
|
||||
print(snap["timer_cb"], snap["period"])
|
||||
|
||||
# JSON serialization
|
||||
import json
|
||||
print(json.dumps(snap.as_dict(), indent=2))
|
||||
|
||||
# Bulk export
|
||||
snapshots = LVTimer.snapshots(timers)
|
||||
data = [s.as_dict() for s in snapshots]
|
||||
|
||||
The ``Snapshot`` class supports dict-like read access (``[]``, ``keys()``,
|
||||
``len()``, ``in``, iteration) and ``as_dict()`` for JSON serialization.
|
||||
All values in ``as_dict()`` are pure Python types (``str``, ``int``, ``float``,
|
||||
``bool``, ``None``, ``dict``, ``list``) with no ``gdb.Value`` references.
|
||||
|
||||
Wrapper classes with ``snapshot()`` support: ``LVAnim``, ``LVTimer``,
|
||||
``LVIndev``, ``LVGroup``, ``LVObject``, ``LVObjClass``, ``LVObserver``,
|
||||
``LVSubject``, ``LVDrawTask``, ``LVDrawUnit``, ``LVFsDrv``,
|
||||
``LVImageDecoder``, ``LVCache``, ``LVDisplay``,
|
||||
``LVRedBlackTree``, ``LVEventDsc``, ``LVList``.
|
||||
|
||||
``LVStyle`` provides ``snapshots()`` (plural) which returns a list of
|
||||
``Snapshot`` objects for each style property, but does not have a singular
|
||||
``snapshot()`` method.
|
||||
|
||||
Most wrapper classes also provide a static ``snapshots(items)`` method for
|
||||
bulk export (e.g. ``LVAnim.snapshots(anims)``). Additionally,
|
||||
``LVImageCache`` and ``LVImageHeaderCache`` provide instance-level
|
||||
``snapshots()`` methods that export all cache entries.
|
||||
|
||||
|
||||
Architecture
|
||||
************
|
||||
|
||||
The GDB plugin is organized into four layers. The overview below shows how
|
||||
terminal commands and the HTML dashboard both flow through the same snapshot
|
||||
abstraction down to raw GDB memory access:
|
||||
|
||||
.. mermaid::
|
||||
:zoom:
|
||||
|
||||
graph TD
|
||||
subgraph "Rendering Layer"
|
||||
CLI["GDB Terminal<br/>dump obj, info style, ..."]
|
||||
DASH["HTML Dashboard<br/>dump dashboard"]
|
||||
end
|
||||
|
||||
subgraph "Formatter / Renderer"
|
||||
FMT["formatter.py<br/>print_info · print_spec_table"]
|
||||
HR["html_renderer.py<br/>template + CSS + JS"]
|
||||
end
|
||||
|
||||
subgraph "Data Collection"
|
||||
DC["data_collector.py<br/>collect_all() → JSON dict"]
|
||||
SNAP["Snapshot<br/>_data + _display_spec"]
|
||||
end
|
||||
|
||||
subgraph "Value Wrappers (lvgl/)"
|
||||
W["LVObject · LVDisplay · LVAnim<br/>LVCache · LVTimer · LVDrawBuf<br/>LVIndev · LVGroup · ..."]
|
||||
GDB["gdb.Value (C struct memory)"]
|
||||
end
|
||||
|
||||
CLI --> FMT
|
||||
FMT --> SNAP
|
||||
DASH --> HR
|
||||
HR --> DC
|
||||
DC --> SNAP
|
||||
SNAP --> W
|
||||
W --> GDB
|
||||
|
||||
style CLI fill:#4CAF50,color:#fff
|
||||
style DASH fill:#4CAF50,color:#fff
|
||||
style FMT fill:#FF9800,color:#fff
|
||||
style HR fill:#FF9800,color:#fff
|
||||
style DC fill:#2196F3,color:#fff
|
||||
style SNAP fill:#2196F3,color:#fff
|
||||
style W fill:#9C27B0,color:#fff
|
||||
style GDB fill:#616161,color:#fff
|
||||
|
||||
Each wrapper class declares a ``_DISPLAY_SPEC`` describing its fields and
|
||||
exports a ``snapshot()`` method that returns a self-describing ``Snapshot``
|
||||
object carrying both the data dict and the display spec. The ``cmds/`` layer
|
||||
simply passes snapshots to generic formatters (``print_info``,
|
||||
``print_spec_table``) which read the embedded spec to render output — no
|
||||
command needs to know the internal structure of any wrapper. The detailed
|
||||
snapshot flow is shown below:
|
||||
|
||||
.. mermaid::
|
||||
:zoom:
|
||||
|
||||
graph RL
|
||||
subgraph "cmds/ layer"
|
||||
CMD["GDB Commands<br/>(dump obj, info style, ...)"]
|
||||
end
|
||||
|
||||
subgraph "formatter.py"
|
||||
PI["print_info(snapshot)"]
|
||||
PT["print_table()"]
|
||||
PST["print_spec_table(snapshots)"]
|
||||
RTC["resolve_table_columns(spec)"]
|
||||
PST --> RTC
|
||||
PST --> PT
|
||||
end
|
||||
|
||||
subgraph "snapshot.py"
|
||||
SNAP["Snapshot<br/>._data (pure dict)<br/>._display_spec<br/>._source"]
|
||||
end
|
||||
|
||||
subgraph "data_utils.py"
|
||||
DU["ptr_or_none()<br/>fmt_cb()<br/>..."]
|
||||
end
|
||||
|
||||
subgraph "Wrapper classes"
|
||||
direction TB
|
||||
DS["_DISPLAY_SPEC<br/>{info, table, empty_msg}"]
|
||||
SN["snapshot() → Snapshot"]
|
||||
SNS["snapshots() → list[Snapshot]"]
|
||||
SN --> SNAP
|
||||
SN -. "display_spec=" .-> DS
|
||||
SNS --> SN
|
||||
end
|
||||
|
||||
subgraph "Wrappers"
|
||||
direction LR
|
||||
W1["LVAnim"]
|
||||
W2["LVTimer"]
|
||||
W3["LVCache"]
|
||||
W4["LVObject"]
|
||||
W5["LVGroup"]
|
||||
W6["LVIndev"]
|
||||
W7["...others"]
|
||||
end
|
||||
|
||||
CMD -- "wrapper.snapshot()" --> SN
|
||||
CMD -- "print_info(snap)" --> PI
|
||||
CMD -- "print_spec_table(snaps)" --> PST
|
||||
PI -- "reads _display_spec" --> SNAP
|
||||
PST -- "reads _display_spec" --> SNAP
|
||||
SN -- "uses" --> DU
|
||||
W1 & W2 & W3 & W4 & W5 & W6 & W7 -. "each defines" .-> DS
|
||||
|
||||
style PI fill:#4CAF50,color:#fff
|
||||
style PT fill:#4CAF50,color:#fff
|
||||
style PST fill:#4CAF50,color:#fff
|
||||
style RTC fill:#4CAF50,color:#fff
|
||||
style SNAP fill:#2196F3,color:#fff
|
||||
style DU fill:#FF9800,color:#fff
|
||||
style DS fill:#9C27B0,color:#fff
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
title: Debugging
|
||||
description: LVGL comes packaged with various tools that can help you debug problems you may encounter during development and testing.
|
||||
---
|
||||
|
||||
LVGL comes packaged with various tools that can help you debug problems you may
|
||||
encounter during development and testing.
|
||||
@@ -1,20 +0,0 @@
|
||||
.. _debugging:
|
||||
|
||||
=========
|
||||
Debugging
|
||||
=========
|
||||
|
||||
LVGL comes packaged with various tools that can help you debug problems you may
|
||||
encounter during development and testing.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
gdb_plugin
|
||||
log
|
||||
monkey
|
||||
obj_id
|
||||
profiler
|
||||
sysmon
|
||||
test
|
||||
vg_lite_tvg
|
||||
@@ -0,0 +1,73 @@
|
||||
---
|
||||
title: Logging
|
||||
description: "LVGL has a built-in Logging module to inform the user about what is happening in the library."
|
||||
---
|
||||
|
||||
LVGL has a built-in *Logging* module to inform the user about what is
|
||||
happening in the library.
|
||||
|
||||
## Configuring Logging
|
||||
|
||||
### Log Level
|
||||
|
||||
To enable logging, set <ApiLink name="LV_USE_LOG" /> in `lv_conf.h` to a non-zero value and
|
||||
set <ApiLink name="LV_LOG_LEVEL" /> to one of the following values. They are prioritized as
|
||||
follows (from most to least verbose):
|
||||
|
||||
- <ApiLink name="LV_LOG_LEVEL_TRACE" />: A lot of logs to give detailed information
|
||||
- <ApiLink name="LV_LOG_LEVEL_INFO" />: Log important events.
|
||||
- <ApiLink name="LV_LOG_LEVEL_WARN" />: Log if something unwanted happened but didn't cause a problem.
|
||||
- <ApiLink name="LV_LOG_LEVEL_ERROR" />: Log only critical issues, where the system may fail.
|
||||
- <ApiLink name="LV_LOG_LEVEL_USER" />: Log only custom log messages added by the user.
|
||||
- <ApiLink name="LV_LOG_LEVEL_NONE" />: Do not log anything.
|
||||
|
||||
When you set <ApiLink name="LV_LOG_LEVEL" /> to a certain level, only messages with that level
|
||||
or higher priority (less verbose) will be logged.
|
||||
|
||||
Example: you set <ApiLink name="LV_LOG_LEVEL" /> to <ApiLink name="LV_LOG_LEVEL_WARN" />, then
|
||||
<ApiLink name="LV_LOG_LEVEL_WARN" />, <ApiLink name="LV_LOG_LEVEL_ERROR" /> and
|
||||
<ApiLink name="LV_LOG_LEVEL_USER" /> messages will be logged.
|
||||
|
||||
### Log Output
|
||||
|
||||
If your system supports `printf`, you just need to enable
|
||||
<ApiLink name="LV_LOG_PRINTF" /> in `lv_conf.h` to output log messages with `printf`.
|
||||
|
||||
If you can't use `printf` or want to use a custom function to log, you
|
||||
can register a "logging" function with <ApiLink name="lv_log_register_print_cb" />.
|
||||
|
||||
For example:
|
||||
|
||||
```c
|
||||
void my_log_cb(lv_log_level_t level, const char * buf)
|
||||
{
|
||||
serial_send(buf, strlen(buf));
|
||||
}
|
||||
|
||||
...
|
||||
|
||||
|
||||
lv_log_register_print_cb(my_log_cb);
|
||||
```
|
||||
|
||||
## Using Logging
|
||||
|
||||
You use the log module via the following macros:
|
||||
|
||||
- <ApiLink name="LV_LOG_TRACE" />(text)
|
||||
- <ApiLink name="LV_LOG_INFO" />(text)
|
||||
- <ApiLink name="LV_LOG_WARN" />(text)
|
||||
- <ApiLink name="LV_LOG_ERROR" />(text)
|
||||
- <ApiLink name="LV_LOG_USER" />(text)
|
||||
- <ApiLink name="LV_LOG" />(text)
|
||||
|
||||
The first 5 macros append the following information to your `text`:
|
||||
|
||||
- Log Level name ("Trace", "Info", "Warn", "Error", "User")
|
||||
- `__FILE__`
|
||||
- `__LINE__`
|
||||
- `__func__`
|
||||
|
||||
<ApiLink name="LV_LOG" />(text) is similar to <ApiLink name="LV_LOG_USER" /> but has no extra information added.
|
||||
|
||||
## API
|
||||
@@ -1,89 +0,0 @@
|
||||
.. _logging:
|
||||
|
||||
=======
|
||||
Logging
|
||||
=======
|
||||
|
||||
LVGL has a built-in *Logging* module to inform the user about what is
|
||||
happening in the library.
|
||||
|
||||
|
||||
|
||||
Configuring Logging
|
||||
*******************
|
||||
|
||||
|
||||
Log Level
|
||||
---------
|
||||
|
||||
To enable logging, set :c:macro:`LV_USE_LOG` in ``lv_conf.h`` to a non-zero value and
|
||||
set :c:macro:`LV_LOG_LEVEL` to one of the following values. They are prioritized as
|
||||
follows (from most to least verbose):
|
||||
|
||||
- :c:macro:`LV_LOG_LEVEL_TRACE`: A lot of logs to give detailed information
|
||||
- :c:macro:`LV_LOG_LEVEL_INFO`: Log important events.
|
||||
- :c:macro:`LV_LOG_LEVEL_WARN`: Log if something unwanted happened but didn't cause a problem.
|
||||
- :c:macro:`LV_LOG_LEVEL_ERROR`: Log only critical issues, where the system may fail.
|
||||
- :c:macro:`LV_LOG_LEVEL_USER`: Log only custom log messages added by the user.
|
||||
- :c:macro:`LV_LOG_LEVEL_NONE`: Do not log anything.
|
||||
|
||||
When you set :c:macro:`LV_LOG_LEVEL` to a certain level, only messages with that level
|
||||
or higher priority (less verbose) will be logged.
|
||||
|
||||
Example: you set :c:macro:`LV_LOG_LEVEL` to :c:macro:`LV_LOG_LEVEL_WARN`, then
|
||||
:c:macro:`LV_LOG_LEVEL_WARN`, :c:macro:`LV_LOG_LEVEL_ERROR` and
|
||||
:c:macro:`LV_LOG_LEVEL_USER` messages will be logged.
|
||||
|
||||
|
||||
Log Output
|
||||
----------
|
||||
|
||||
If your system supports ``printf``, you just need to enable
|
||||
:c:macro:`LV_LOG_PRINTF` in ``lv_conf.h`` to output log messages with ``printf``.
|
||||
|
||||
If you can't use ``printf`` or want to use a custom function to log, you
|
||||
can register a "logging" function with :cpp:func:`lv_log_register_print_cb`.
|
||||
|
||||
For example:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
void my_log_cb(lv_log_level_t level, const char * buf)
|
||||
{
|
||||
serial_send(buf, strlen(buf));
|
||||
}
|
||||
|
||||
...
|
||||
|
||||
|
||||
lv_log_register_print_cb(my_log_cb);
|
||||
|
||||
|
||||
|
||||
Using Logging
|
||||
*************
|
||||
|
||||
You use the log module via the following macros:
|
||||
|
||||
- :c:macro:`LV_LOG_TRACE`\ (text)
|
||||
- :c:macro:`LV_LOG_INFO`\ (text)
|
||||
- :c:macro:`LV_LOG_WARN`\ (text)
|
||||
- :c:macro:`LV_LOG_ERROR`\ (text)
|
||||
- :c:macro:`LV_LOG_USER`\ (text)
|
||||
- :c:macro:`LV_LOG`\ (text)
|
||||
|
||||
The first 5 macros append the following information to your ``text``:
|
||||
|
||||
- Log Level name ("Trace", "Info", "Warn", "Error", "User")
|
||||
- ``__FILE__``
|
||||
- ``__LINE__``
|
||||
- ``__func__``
|
||||
|
||||
:c:macro:`LV_LOG`\ (text) is similar to :c:macro:`LV_LOG_USER` but has no extra information added.
|
||||
|
||||
|
||||
|
||||
API
|
||||
***
|
||||
|
||||
.. API equals: lv_log_register_print_cb
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"title": "Debugging",
|
||||
"pages": [
|
||||
"gdb_plugin",
|
||||
"log",
|
||||
"monkey",
|
||||
"obj_id",
|
||||
"profiler",
|
||||
"sysmon",
|
||||
"test",
|
||||
"vg_lite_tvg"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
---
|
||||
title: Monkey
|
||||
description: The Monkey module provides LVGL applications with a simple monkey test. Monkey Testing is a technique where the user tests the application or system by providing random inputs and checking the beha...
|
||||
---
|
||||
|
||||
The Monkey module provides LVGL applications with a simple monkey test. Monkey
|
||||
Testing is a technique where the user tests the application or system by providing
|
||||
random inputs and checking the behavior or seeing whether the application or system
|
||||
will crash. This module provides this service as simulated random input to stress
|
||||
test an LVGL application.
|
||||
|
||||
## Usage
|
||||
|
||||
First, enable <ApiLink name="LV_USE_MONKEY" /> in `lv_conf.h`.
|
||||
|
||||
Next, declare a variable (it can be local) of type <ApiLink name="lv_monkey_config_t" /> to
|
||||
define the configuration structure, initialize it using
|
||||
<ApiLink name="lv_monkey_config_init" display="lv_monkey_config_init(cfg)" /> then set its `type` member to the desired
|
||||
type of [input device](/main-modules/indev), and set the `min` and `max` values for its
|
||||
`period_range` and `input_range` members to set the time ranges (in milliseconds)
|
||||
and input ranges the Monkey module will use to generate random input at random times.
|
||||
|
||||
Next, call <ApiLink name="lv_monkey_create" display="lv_monkey_create(cfg)" /> to create the Monkey. It returns a
|
||||
pointer to the `lv_monkey_t` created.
|
||||
|
||||
Finally call <ApiLink name="lv_monkey_set_enable" display="lv_monkey_set_enable(monkey, true)" /> to enable Monkey.
|
||||
|
||||
If you want to pause the monkey, call
|
||||
<ApiLink name="lv_monkey_set_enable" display="lv_monkey_set_enable(monkey, false)" />. To delete it, call
|
||||
<ApiLink name="lv_monkey_delete" display="lv_monkey_delete(monkey)" />.
|
||||
|
||||
Note that `input_range` has different meanings depending on the `type` input device:
|
||||
|
||||
- <ApiLink name="LV_INDEV_TYPE_POINTER" />: No effect, click randomly within the pixels of the screen resolution.
|
||||
- <ApiLink name="LV_INDEV_TYPE_ENCODER" />: The minimum and maximum values of `enc_diff`.
|
||||
- <ApiLink name="LV_INDEV_TYPE_BUTTON" />: The minimum and maximum values of `btn_id`.
|
||||
Use <ApiLink name="lv_monkey_get_indev" /> to get the input device, and use
|
||||
<ApiLink name="lv_indev_set_button_points" /> to map the key ID to the coordinates.
|
||||
- <ApiLink name="LV_INDEV_TYPE_KEYPAD" />: No effect, Send random [Keys](/main-modules/indev/keypad).
|
||||
|
||||
## Examples
|
||||
|
||||
### Touchpad monkey example
|
||||
|
||||
<LvglExample name="lv_example_monkey_1" path="others/monkey/lv_example_monkey_1" />
|
||||
|
||||
### Encoder monkey example
|
||||
|
||||
<LvglExample name="lv_example_monkey_2" path="others/monkey/lv_example_monkey_2" />
|
||||
|
||||
### Button monkey example
|
||||
|
||||
<LvglExample name="lv_example_monkey_3" path="others/monkey/lv_example_monkey_3" />
|
||||
|
||||
## API
|
||||
@@ -1,63 +0,0 @@
|
||||
.. _monkey:
|
||||
|
||||
======
|
||||
Monkey
|
||||
======
|
||||
|
||||
The Monkey module provides LVGL applications with a simple monkey test. Monkey
|
||||
Testing is a technique where the user tests the application or system by providing
|
||||
random inputs and checking the behavior or seeing whether the application or system
|
||||
will crash. This module provides this service as simulated random input to stress
|
||||
test an LVGL application.
|
||||
|
||||
|
||||
|
||||
.. _monkey_usage:
|
||||
|
||||
Usage
|
||||
*****
|
||||
|
||||
First, enable :c:macro:`LV_USE_MONKEY` in ``lv_conf.h``.
|
||||
|
||||
Next, declare a variable (it can be local) of type :cpp:type:`lv_monkey_config_t` to
|
||||
define the configuration structure, initialize it using
|
||||
:cpp:expr:`lv_monkey_config_init(cfg)` then set its ``type`` member to the desired
|
||||
type of :ref:`input device <indev>`, and set the ``min`` and ``max`` values for its
|
||||
``period_range`` and ``input_range`` members to set the time ranges (in milliseconds)
|
||||
and input ranges the Monkey module will use to generate random input at random times.
|
||||
|
||||
Next, call :cpp:expr:`lv_monkey_create(cfg)` to create the Monkey. It returns a
|
||||
pointer to the ``lv_monkey_t`` created.
|
||||
|
||||
Finally call :cpp:expr:`lv_monkey_set_enable(monkey, true)` to enable Monkey.
|
||||
|
||||
If you want to pause the monkey, call
|
||||
:cpp:expr:`lv_monkey_set_enable(monkey, false)`. To delete it, call
|
||||
:cpp:expr:`lv_monkey_delete(monkey)`.
|
||||
|
||||
Note that ``input_range`` has different meanings depending on the ``type`` input device:
|
||||
|
||||
- :cpp:enumerator:`LV_INDEV_TYPE_POINTER`: No effect, click randomly within the pixels of the screen resolution.
|
||||
- :cpp:enumerator:`LV_INDEV_TYPE_ENCODER`: The minimum and maximum values of ``enc_diff``.
|
||||
- :cpp:enumerator:`LV_INDEV_TYPE_BUTTON`: The minimum and maximum values of ``btn_id``.
|
||||
Use :cpp:func:`lv_monkey_get_indev` to get the input device, and use
|
||||
:cpp:func:`lv_indev_set_button_points` to map the key ID to the coordinates.
|
||||
- :cpp:enumerator:`LV_INDEV_TYPE_KEYPAD`: No effect, Send random :ref:`indev_keys`.
|
||||
|
||||
|
||||
|
||||
.. _monkey_example:
|
||||
|
||||
Examples
|
||||
********
|
||||
|
||||
.. include:: /examples/others/monkey/index.rst
|
||||
|
||||
|
||||
|
||||
.. _monkey_api:
|
||||
|
||||
API
|
||||
***
|
||||
|
||||
.. API equals: lv_monkey_create
|
||||
@@ -0,0 +1,176 @@
|
||||
---
|
||||
title: Widget ID
|
||||
description: "Widgets can optionally have identifiers added to their functionality if needed for the application. Exactly how that happens is designed to be flexible, and can morph with the needs of the applicat..."
|
||||
---
|
||||
|
||||
Widgets can optionally have identifiers added to their functionality if needed for
|
||||
the application. Exactly how that happens is designed to be flexible, and can morph
|
||||
with the needs of the application. It can even be a timestamp or other data current
|
||||
at the time the Widget was created.
|
||||
|
||||
## Usage
|
||||
|
||||
Enable Widget ID functionality by setting <ApiLink name="LV_USE_OBJ_ID" /> to `1` in `lv_conf.h`.
|
||||
|
||||
Once enabled, several things change:
|
||||
|
||||
- each Widget will now have a `void *` field called `id`;
|
||||
- these two API functions become available:
|
||||
|
||||
- <ApiLink name="lv_obj_get_id" display="lv_obj_get_id(widget)" />,
|
||||
- <ApiLink name="lv_obj_find_by_id" display="lv_obj_find_by_id(widget, id)" />;
|
||||
- several more Widget-ID-related API functions become available if
|
||||
<ApiLink name="LV_USE_OBJ_ID_BUILTIN" /> is non-zero (more on this below);
|
||||
- two additional configuration macros both <ApiLink name="LV_OBJ_ID_AUTO_ASSIGN" /> and
|
||||
<ApiLink name="LV_USE_OBJ_ID_BUILTIN" /> now have meaning.
|
||||
|
||||
### `LV_OBJ_ID_AUTO_ASSIGN`
|
||||
|
||||
This macro in `lv_conf.h` defaults to whatever value <ApiLink name="LV_USE_OBJ_ID" />
|
||||
equates to. You can change this if you wish. Either way, if it equates to a
|
||||
non-zero value, it causes two things to happen:
|
||||
|
||||
- <ApiLink name="lv_obj_assign_id" display="lv_obj_assign_id(class_p, widget)" /> will be called at the end of each
|
||||
Widget's creation, and
|
||||
- <ApiLink name="lv_obj_free_id" display="lv_obj_free_id(widget)" /> will be called at the end of the sequence when
|
||||
each Widget is deleted.
|
||||
|
||||
Because of this timing, custom versions of these functions can be used according to
|
||||
the below, and they can even be used like "event hooks" to implement a trace
|
||||
operation that occurs when each Widget is created and deleted.
|
||||
|
||||
##### `lv_obj_assign_id(class_p, widget)`
|
||||
|
||||
This function (whether provided by LVGL or by you --- more on this below) is
|
||||
responsible for assigning a value to the Widget's `id` field, and possibly do
|
||||
other things, depending on the implementation.
|
||||
|
||||
##### `lv_obj_free_id(widget)`
|
||||
|
||||
This function (whether provided by LVGL or by you --- more on this below) is
|
||||
responsible for doing the clean-up of any resources allocated by
|
||||
<ApiLink name="lv_obj_assign_id" />
|
||||
|
||||
### `LV_USE_OBJ_ID_BUILTIN`
|
||||
|
||||
This macro in `lv_conf.h` equates to `1` by default. You can change this if you
|
||||
wish. When it equates to a non-zero value the following function implementations are
|
||||
provided by LVGL:
|
||||
|
||||
- <ApiLink name="lv_obj_assign_id" display="lv_obj_assign_id(class_p, widget)" />
|
||||
- <ApiLink name="lv_obj_free_id" display="lv_obj_free_id(widget)" />
|
||||
- <ApiLink name="lv_obj_set_id" display="lv_obj_set_id(widget, id)" />
|
||||
- <ApiLink name="lv_obj_stringify_id" display="lv_obj_stringify_id(widget, buf, len)" />
|
||||
- <ApiLink name="lv_obj_id_compare" display="lv_obj_id_compare(id1, id2)" />
|
||||
|
||||
These supply the default implementation for Widget IDs, namely that for each Widget
|
||||
created, <ApiLink name="lv_obj_stringify_id" display="lv_obj_stringify_id(widget, buf, len)" /> will produce a unique
|
||||
string for it. Example: if the following 6 Widgets are created in this sequence:
|
||||
|
||||
- Screen
|
||||
- Label
|
||||
- Button
|
||||
- Label
|
||||
- Label
|
||||
- Image
|
||||
|
||||
the strings produced by <ApiLink name="lv_obj_stringify_id" display="lv_obj_stringify_id(widget, buf, len)" /> would be
|
||||
|
||||
- obj1
|
||||
- label1
|
||||
- btn1
|
||||
- label2
|
||||
- label3
|
||||
- image1
|
||||
|
||||
respectively.
|
||||
|
||||
### Using a custom ID generator
|
||||
|
||||
If you wish, you can provide custom implementations for several Widget-ID related
|
||||
functions. You do this by first setting <ApiLink name="LV_USE_OBJ_ID_BUILTIN" /> to `0` in
|
||||
`lv_conf.h`.
|
||||
|
||||
You will then need to provide implementations for the following functions (and link
|
||||
them with LVGL):
|
||||
|
||||
```c
|
||||
const char * lv_obj_stringify_id(lv_obj_t * widget, char * buf, uint32_t len);
|
||||
int lv_obj_id_compare(const void * id1, const void * id2);
|
||||
```
|
||||
|
||||
If <ApiLink name="LV_OBJ_ID_AUTO_ASSIGN" /> equates to a non-zero value (or if you otherwise
|
||||
simply need to use them), you will also need to provide implementations for:
|
||||
|
||||
```c
|
||||
void lv_obj_assign_id(const lv_obj_class_t * class_p, lv_obj_t * widget);
|
||||
void lv_obj_free_id(lv_obj_t * widget);
|
||||
```
|
||||
|
||||
If <ApiLink name="LV_BUILD_TEST" /> equates to a non-zero value and you are including LVGL
|
||||
test code in your compile (or if you otherwise simply need to use them), you
|
||||
will also need to provide an implementation for:
|
||||
|
||||
```c
|
||||
void lv_obj_set_id(lv_obj_t * widget, void * id);
|
||||
```
|
||||
|
||||
Examples of implementations of these functions exist in `lv_obj_id_builtin.c`, but
|
||||
you are free to use a different design if needed.
|
||||
|
||||
<ApiLink name="lv_obj_stringify_id" /> converts the passed `widget` to a string
|
||||
representation (typically incorporating the `id` field) and writes it into the
|
||||
buffer provided in its `buf` argument.
|
||||
|
||||
<ApiLink name="lv_obj_id_compare" /> compares 2 `void * id` values and returns `0` when
|
||||
they are considered equal, and non-zero otherwise.
|
||||
|
||||
If <ApiLink name="LV_OBJ_ID_AUTO_ASSIGN" /> equates to a non-zero value,
|
||||
<ApiLink name="lv_obj_assign_id" /> is called when a Widget is created. It is responsible
|
||||
for assigning a value to the Widget's `id` field. A pointer to the Widget's final
|
||||
class is passed in its `class_p` argument in case it is needed for determining the
|
||||
value for the `id` field, or for other possible needs related to your design for
|
||||
Widget IDs. Note that this pointer may be different than <ApiLink name="widget" display="widget->class_p" />
|
||||
which is the class of the Widget currently being created.
|
||||
|
||||
If <ApiLink name="LV_OBJ_ID_AUTO_ASSIGN" /> equates to a non-zero value,
|
||||
<ApiLink name="lv_obj_free_id" /> is called when a Widget is deleted. It needs to perform
|
||||
the clean-up for any resources allocated by <ApiLink name="lv_obj_assign_id" />.
|
||||
|
||||
### Dumping a Widget Tree
|
||||
|
||||
Regardless of the state of any of the above macros, the function
|
||||
<ApiLink name="lv_obj_dump_tree" display="lv_obj_dump_tree(widget)" /> provides a "dump" of the Widget Tree for the
|
||||
specified Widget (that Widget plus all its children recursively) using the
|
||||
currently-configured method used by the <ApiLink name="LV_LOG_USER" /> macro. If NULL is
|
||||
passed instead of a pointer to a "root" Widget, the dump will include the Widget Tree
|
||||
for all Screens, for all [Displays](/main-modules/display) in the system.
|
||||
|
||||
For <ApiLink name="LV_LOG_USER" /> to produce output, the following needs to be true in
|
||||
`lv_conf.h`:
|
||||
|
||||
- <ApiLink name="LV_USE_LOG" /> must equate to a non-zero value
|
||||
- <ApiLink name="LV_LOG_LEVEL" /> <= <ApiLink name="LV_LOG_LEVEL_USER" />
|
||||
|
||||
It will recursively walk through all that Widget's children (starting with the Widget
|
||||
itself) and print the Widget's parent's address, the Widget's address, and if
|
||||
<ApiLink name="LV_USE_OBJ_ID" /> equates to a non-zero value, will also print the output of
|
||||
<ApiLink name="lv_obj_stringify_id" /> for that Widget.
|
||||
|
||||
This can be useful in the event of a UI crash. From that log you can examine the
|
||||
state of the Widget Tree when <ApiLink name="lv_obj_dump_tree" display="lv_obj_dump_tree(widget)" /> was called.
|
||||
|
||||
For example, if a pointer to a deleted Widget is stored in a Timer's
|
||||
<ApiLink name="timer" display="timer->user_data" /> field when the timer event callback is called, attempted
|
||||
use of that pointer will likely cause a crash because the pointer is not valid any
|
||||
more. However, a timely dump of the Widget Tree right before that point will show
|
||||
that the Widget no longer exists.
|
||||
|
||||
### Find child by ID
|
||||
|
||||
<Callout type="warn">
|
||||
`lv_obj_find_by_id(widget, id)` is deprecated. To find a widget use `obj_name`.
|
||||
</Callout>
|
||||
|
||||
<ApiLink name="lv_obj_find_by_id" display="lv_obj_find_by_id(widget, id)" /> will perform a recursive walk through
|
||||
`widget`'s children and return the first child encountered having the given ID.
|
||||
@@ -1,197 +0,0 @@
|
||||
.. _widget_id:
|
||||
|
||||
=========
|
||||
Widget ID
|
||||
=========
|
||||
|
||||
Widgets can optionally have identifiers added to their functionality if needed for
|
||||
the application. Exactly how that happens is designed to be flexible, and can morph
|
||||
with the needs of the application. It can even be a timestamp or other data current
|
||||
at the time the Widget was created.
|
||||
|
||||
|
||||
|
||||
.. _widget_id_usage:
|
||||
|
||||
Usage
|
||||
*****
|
||||
|
||||
Enable Widget ID functionality by setting :c:macro:`LV_USE_OBJ_ID` to ``1`` in ``lv_conf.h``.
|
||||
|
||||
Once enabled, several things change:
|
||||
|
||||
- each Widget will now have a ``void *`` field called ``id``;
|
||||
- these two API functions become available:
|
||||
|
||||
- :cpp:expr:`lv_obj_get_id(widget)`,
|
||||
- :cpp:expr:`lv_obj_find_by_id(widget, id)`;
|
||||
|
||||
- several more Widget-ID-related API functions become available if
|
||||
:c:macro:`LV_USE_OBJ_ID_BUILTIN` is non-zero (more on this below);
|
||||
- two additional configuration macros both :c:macro:`LV_OBJ_ID_AUTO_ASSIGN` and
|
||||
:c:macro:`LV_USE_OBJ_ID_BUILTIN` now have meaning.
|
||||
|
||||
|
||||
:c:macro:`LV_OBJ_ID_AUTO_ASSIGN`
|
||||
--------------------------------
|
||||
|
||||
This macro in ``lv_conf.h`` defaults to whatever value :c:macro:`LV_USE_OBJ_ID`
|
||||
equates to. You can change this if you wish. Either way, if it equates to a
|
||||
non-zero value, it causes two things to happen:
|
||||
|
||||
- :cpp:expr:`lv_obj_assign_id(class_p, widget)` will be called at the end of each
|
||||
Widget's creation, and
|
||||
- :cpp:expr:`lv_obj_free_id(widget)` will be called at the end of the sequence when
|
||||
each Widget is deleted.
|
||||
|
||||
Because of this timing, custom versions of these functions can be used according to
|
||||
the below, and they can even be used like "event hooks" to implement a trace
|
||||
operation that occurs when each Widget is created and deleted.
|
||||
|
||||
:cpp:expr:`lv_obj_assign_id(class_p, widget)`
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This function (whether provided by LVGL or by you --- more on this below) is
|
||||
responsible for assigning a value to the Widget's ``id`` field, and possibly do
|
||||
other things, depending on the implementation.
|
||||
|
||||
:cpp:expr:`lv_obj_free_id(widget)`
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This function (whether provided by LVGL or by you --- more on this below) is
|
||||
responsible for doing the clean-up of any resources allocated by
|
||||
:cpp:func:`lv_obj_assign_id`
|
||||
|
||||
|
||||
:c:macro:`LV_USE_OBJ_ID_BUILTIN`
|
||||
--------------------------------
|
||||
|
||||
This macro in ``lv_conf.h`` equates to ``1`` by default. You can change this if you
|
||||
wish. When it equates to a non-zero value the following function implementations are
|
||||
provided by LVGL:
|
||||
|
||||
- :cpp:expr:`lv_obj_assign_id(class_p, widget)`
|
||||
- :cpp:expr:`lv_obj_free_id(widget)`
|
||||
- :cpp:expr:`lv_obj_set_id(widget, id)`
|
||||
- :cpp:expr:`lv_obj_stringify_id(widget, buf, len)`
|
||||
- :cpp:expr:`lv_obj_id_compare(id1, id2)`
|
||||
|
||||
These supply the default implementation for Widget IDs, namely that for each Widget
|
||||
created, :cpp:expr:`lv_obj_stringify_id(widget, buf, len)` will produce a unique
|
||||
string for it. Example: if the following 6 Widgets are created in this sequence:
|
||||
|
||||
- Screen
|
||||
- Label
|
||||
- Button
|
||||
- Label
|
||||
- Label
|
||||
- Image
|
||||
|
||||
the strings produced by :cpp:expr:`lv_obj_stringify_id(widget, buf, len)` would be
|
||||
|
||||
- obj1
|
||||
- label1
|
||||
- btn1
|
||||
- label2
|
||||
- label3
|
||||
- image1
|
||||
|
||||
respectively.
|
||||
|
||||
|
||||
.. _widget_id_custom_generator:
|
||||
|
||||
Using a custom ID generator
|
||||
---------------------------
|
||||
|
||||
If you wish, you can provide custom implementations for several Widget-ID related
|
||||
functions. You do this by first setting :c:macro:`LV_USE_OBJ_ID_BUILTIN` to `0` in
|
||||
``lv_conf.h``.
|
||||
|
||||
You will then need to provide implementations for the following functions (and link
|
||||
them with LVGL):
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
const char * lv_obj_stringify_id(lv_obj_t * widget, char * buf, uint32_t len);
|
||||
int lv_obj_id_compare(const void * id1, const void * id2);
|
||||
|
||||
If :c:macro:`LV_OBJ_ID_AUTO_ASSIGN` equates to a non-zero value (or if you otherwise
|
||||
simply need to use them), you will also need to provide implementations for:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
void lv_obj_assign_id(const lv_obj_class_t * class_p, lv_obj_t * widget);
|
||||
void lv_obj_free_id(lv_obj_t * widget);
|
||||
|
||||
If :c:macro:`LV_BUILD_TEST` equates to a non-zero value and you are including LVGL
|
||||
test code in your compile (or if you otherwise simply need to use them), you
|
||||
will also need to provide an implementation for:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
void lv_obj_set_id(lv_obj_t * widget, void * id);
|
||||
|
||||
|
||||
Examples of implementations of these functions exist in ``lv_obj_id_builtin.c``, but
|
||||
you are free to use a different design if needed.
|
||||
|
||||
:cpp:func:`lv_obj_stringify_id` converts the passed ``widget`` to a string
|
||||
representation (typically incorporating the ``id`` field) and writes it into the
|
||||
buffer provided in its ``buf`` argument.
|
||||
|
||||
:cpp:func:`lv_obj_id_compare` compares 2 ``void * id`` values and returns ``0`` when
|
||||
they are considered equal, and non-zero otherwise.
|
||||
|
||||
If :c:macro:`LV_OBJ_ID_AUTO_ASSIGN` equates to a non-zero value,
|
||||
:cpp:func:`lv_obj_assign_id` is called when a Widget is created. It is responsible
|
||||
for assigning a value to the Widget's ``id`` field. A pointer to the Widget's final
|
||||
class is passed in its ``class_p`` argument in case it is needed for determining the
|
||||
value for the ``id`` field, or for other possible needs related to your design for
|
||||
Widget IDs. Note that this pointer may be different than :cpp:expr:`widget->class_p`
|
||||
which is the class of the Widget currently being created.
|
||||
|
||||
If :c:macro:`LV_OBJ_ID_AUTO_ASSIGN` equates to a non-zero value,
|
||||
:cpp:func:`lv_obj_free_id` is called when a Widget is deleted. It needs to perform
|
||||
the clean-up for any resources allocated by :cpp:func:`lv_obj_assign_id`.
|
||||
|
||||
|
||||
Dumping a Widget Tree
|
||||
---------------------
|
||||
|
||||
Regardless of the state of any of the above macros, the function
|
||||
:cpp:expr:`lv_obj_dump_tree(widget)` provides a "dump" of the Widget Tree for the
|
||||
specified Widget (that Widget plus all its children recursively) using the
|
||||
currently-configured method used by the :c:macro:`LV_LOG_USER` macro. If NULL is
|
||||
passed instead of a pointer to a "root" Widget, the dump will include the Widget Tree
|
||||
for all :ref:`Screens`, for all :ref:`Displays <display>` in the system.
|
||||
|
||||
For :c:macro:`LV_LOG_USER` to produce output, the following needs to be true in
|
||||
``lv_conf.h``:
|
||||
|
||||
- :c:macro:`LV_USE_LOG` must equate to a non-zero value
|
||||
- :c:macro:`LV_LOG_LEVEL` <= :c:macro:`LV_LOG_LEVEL_USER`
|
||||
|
||||
It will recursively walk through all that Widget's children (starting with the Widget
|
||||
itself) and print the Widget's parent's address, the Widget's address, and if
|
||||
:c:macro:`LV_USE_OBJ_ID` equates to a non-zero value, will also print the output of
|
||||
:cpp:func:`lv_obj_stringify_id` for that Widget.
|
||||
|
||||
This can be useful in the event of a UI crash. From that log you can examine the
|
||||
state of the Widget Tree when :cpp:expr:`lv_obj_dump_tree(widget)` was called.
|
||||
|
||||
For example, if a pointer to a deleted Widget is stored in a Timer's
|
||||
:cpp:expr:`timer->user_data` field when the timer event callback is called, attempted
|
||||
use of that pointer will likely cause a crash because the pointer is not valid any
|
||||
more. However, a timely dump of the Widget Tree right before that point will show
|
||||
that the Widget no longer exists.
|
||||
|
||||
|
||||
Find child by ID
|
||||
----------------
|
||||
|
||||
.. warning::
|
||||
``lv_obj_find_by_id(widget, id)`` is deprecated. To find a widget use ``obj_name``.
|
||||
|
||||
:cpp:expr:`lv_obj_find_by_id(widget, id)` will perform a recursive walk through
|
||||
``widget``\ 's children and return the first child encountered having the given ID.
|
||||
@@ -0,0 +1,185 @@
|
||||
---
|
||||
title: Profiler
|
||||
description: "As the complexity of the application increases, performance issues such as low FPS and frequent cache misses causing lag may arise. LVGL has internally set up some hooks for performance measurement..."
|
||||
---
|
||||
|
||||
As the complexity of the application increases, performance issues such as low FPS and frequent cache misses
|
||||
causing lag may arise. LVGL has internally set up some hooks for performance measurement to help developers
|
||||
analyze and locate performance issues.
|
||||
|
||||
## Introduction
|
||||
|
||||
LVGL has a built-in trace system to track and record the timestamps of important events that occur during runtime,
|
||||
such as rendering events and user input events. These event timestamps serve in measurements taken in performance analysis.
|
||||
|
||||
The trace system has a configurable record buffer that stores the names of event functions and their timestamps.
|
||||
When the buffer is full, the trace system prints the log information through the provided user interface.
|
||||
|
||||
The output trace logs are formatted according to Android's [systrace](https://developer.android.com/topic/performance/tracing)
|
||||
format and can be visualized using [Perfetto](https://ui.perfetto.dev).
|
||||
|
||||
## Usage
|
||||
|
||||
### Configuration
|
||||
|
||||
To enable the profiler, set <ApiLink name="LV_USE_PROFILER" /> in `lv_conf.h` and configure the following options:
|
||||
|
||||
1. Enable the built-in profiler functionality by setting <ApiLink name="LV_USE_PROFILER_BUILTIN" />. If you have POSIX environment support, you can enable <ApiLink name="LV_USE_PROFILER_BUILTIN_POSIX" />.
|
||||
2. Buffer configuration: Set the value of <ApiLink name="LV_PROFILER_BUILTIN_BUF_SIZE" /> to configure the buffer size. A larger buffer can store more trace event information, reducing interference with rendering. However, it also results in higher memory consumption.
|
||||
3. Timestamp configuration: LVGL uses the <ApiLink name="lv_tick_get" /> function with a precision of 1ms by default to obtain timestamps when events occur. Therefore, it cannot accurately measure intervals below 1ms. If your system environment can provide higher precision (e.g., 1us), you can configure the profiler as follows:
|
||||
|
||||
- Recommended configuration in **Arduino** environments:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
static uint64_t my_get_tick_cb(void)
|
||||
\{
|
||||
/* Use the microsecond time stamp provided by Arduino */
|
||||
return micros();
|
||||
\}
|
||||
|
||||
void my_profiler_init(void)
|
||||
\{
|
||||
lv_profiler_builtin_config_t config;
|
||||
lv_profiler_builtin_config_init(&config);
|
||||
config.tick_per_sec = 1000000; /* One second is equal to 1000000 microseconds */
|
||||
config.tick_get_cb = my_get_tick_cb;
|
||||
lv_profiler_builtin_init(&config);
|
||||
\}
|
||||
4. Log output configuration: LVGL uses the <ApiLink name="LV_LOG" /> interface by default to output trace information. If you want to use another interface to output log information (e.g. stdout), you can redirect the log output using the following code:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
static void my_log_print_cb(const char * buf)
|
||||
\{
|
||||
printf("%s", buf);
|
||||
\}
|
||||
|
||||
void my_profiler_init(void)
|
||||
\{
|
||||
lv_profiler_builtin_config_t config;
|
||||
lv_profiler_builtin_config_init(&config);
|
||||
... /* other configurations */
|
||||
config.flush_cb = my_log_print_cb;
|
||||
lv_profiler_builtin_init(&config);
|
||||
\}
|
||||
|
||||
### Run the test scenario
|
||||
|
||||
Run the UI scenario that you want to measure, such as scrolling a scrollable page up and down or entering/exiting an application.
|
||||
|
||||
### Process the logs
|
||||
|
||||
Save the output log as `my_trace.txt`, use `trace_filter.py` for filtering and preprocessing:
|
||||
|
||||
```bash
|
||||
./lvgl/scripts/trace_filter.py my_trace.txt
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```bash
|
||||
python3 ./lvgl/scripts/trace_filter.py my_trace.txt
|
||||
```
|
||||
|
||||
You will obtain a processed text file named `trace.systrace`, which roughly contains the following content:
|
||||
|
||||
```text
|
||||
# tracer: nop
|
||||
#
|
||||
LVGL-1 [0] 2892.002993: tracing_mark_write: B|1|lv_timer_handler
|
||||
LVGL-1 [0] 2892.002993: tracing_mark_write: B|1|_lv_display_refr_timer
|
||||
LVGL-1 [0] 2892.003459: tracing_mark_write: B|1|refr_invalid_areas
|
||||
LVGL-1 [0] 2892.003461: tracing_mark_write: B|1|lv_draw_rect
|
||||
LVGL-1 [0] 2892.003550: tracing_mark_write: E|1|lv_draw_rect
|
||||
LVGL-1 [0] 2892.003552: tracing_mark_write: B|1|lv_draw_rect
|
||||
LVGL-1 [0] 2892.003556: tracing_mark_write: E|1|lv_draw_rect
|
||||
LVGL-1 [0] 2892.003560: tracing_mark_write: B|1|lv_draw_rect
|
||||
LVGL-1 [0] 2892.003573: tracing_mark_write: E|1|lv_draw_rect
|
||||
...
|
||||
```
|
||||
|
||||
Import the processed [trace.systrace` file into `Perfetto](https://ui.perfetto.dev) and wait for it to be parsed.
|
||||
|
||||
### Performance analysis
|
||||
|
||||
If the log parsing is successful, you will see a screen similar to the following:
|
||||
|
||||

|
||||
|
||||
In the Perfetto UI, use the <Kbd>A</Kbd> or <Kbd>D</Kbd> keys to pan the timeline horizontally
|
||||
and the <Kbd>W</Kbd> or <Kbd>S</Kbd> keys to zoom in or out on the timeline.
|
||||
Use the mouse to move the focus and click on functions on the timeline to observe their execution time.
|
||||
|
||||
## Adding Measurement Points
|
||||
|
||||
Users can add their own measured functions:
|
||||
|
||||
```c
|
||||
void my_function_1(void)
|
||||
{
|
||||
LV_PROFILER_BEGIN;
|
||||
do_something();
|
||||
LV_PROFILER_END;
|
||||
}
|
||||
|
||||
void my_function_2(void)
|
||||
{
|
||||
LV_PROFILER_BEGIN_TAG("do_something_1");
|
||||
do_something_1();
|
||||
LV_PROFILER_END_TAG("do_something_1");
|
||||
|
||||
LV_PROFILER_BEGIN_TAG("do_something_2");
|
||||
do_something_2();
|
||||
LV_PROFILER_END_TAG("do_something_2");
|
||||
}
|
||||
```
|
||||
|
||||
## Custom Profiler Implementation
|
||||
|
||||
If you wish to use a profiler method provided by your operating system, you can modify the following macros in `lv_conf.h`:
|
||||
|
||||
- <ApiLink name="LV_PROFILER_INCLUDE" />: Provides a header file for the profiler function.
|
||||
- <ApiLink name="LV_PROFILER_BEGIN" />: Profiler start point function.
|
||||
- <ApiLink name="LV_PROFILER_END" />: Profiler end point function.
|
||||
- <ApiLink name="LV_PROFILER_BEGIN_TAG" />: Profiler start point function with custom tag.
|
||||
- <ApiLink name="LV_PROFILER_END_TAG" />: Profiler end point function with custom tag.
|
||||
|
||||
Taking [NuttX](https://github.com/apache/nuttx) RTOS as an example:
|
||||
|
||||
```c
|
||||
#define LV_PROFILER_INCLUDE "nuttx/sched_note.h"
|
||||
#define LV_PROFILER_BEGIN sched_note_begin(NOTE_TAG_ALWAYS)
|
||||
#define LV_PROFILER_END sched_note_end(NOTE_TAG_ALWAYS)
|
||||
#define LV_PROFILER_BEGIN_TAG(str) sched_note_beginex(NOTE_TAG_ALWAYS, str)
|
||||
#define LV_PROFILER_END_TAG(str) sched_note_endex(NOTE_TAG_ALWAYS, str)
|
||||
```
|
||||
|
||||
## FAQ
|
||||
|
||||
### Perfetto log parsing fails
|
||||
|
||||
Please check the completeness of the logs. If the logs are incomplete, it may be due to the following reasons:
|
||||
|
||||
1. Serial port reception errors caused by a high baud rate. You need to reduce the baud rate.
|
||||
2. Data corruption caused by other thread logs inserted during the printing of trace logs. You need to disable the log output of other threads or refer to the configuration above to use a separate log output interface.
|
||||
3. Make sure that the string passed in by <ApiLink name="LV_PROFILER_BEGIN_TAG" /> or <ApiLink name="LV_PROFILER_END_TAG" /> is not a local variable on the stack or a string in shared memory, because currently only the string address is recorded and the content is not copied.
|
||||
|
||||
### Function execution time displayed as 0s in Perfetto
|
||||
|
||||
If the function execution time is lower than the precision of the timestamps, this situation can occur. You can refer to the configuration instructions above to use a higher precision timestamp.
|
||||
See Configuration_ for an example of this.
|
||||
|
||||
### Significant stuttering occurs during profiling
|
||||
|
||||
When the buffer used to store trace events becomes full, the profiler will output all the data in the buffer, which can cause UI blocking and stuttering during the output. You can optimize this by taking the following measures:
|
||||
|
||||
1. Increase the value of <ApiLink name="LV_PROFILER_BUILTIN_BUF_SIZE" />. A larger buffer can reduce the frequency of log flushing, but it also consumes more memory.
|
||||
2. Optimize the execution time of log flushing functions, such as increasing the serial port baud rate or improving file writing speed.
|
||||
|
||||
### Trace logs are not being output
|
||||
|
||||
If the trace logs are not automatically printed when the buffer is not full, you can try the following methods to force the log output:
|
||||
|
||||
1. Reduce the value of <ApiLink name="LV_PROFILER_BUILTIN_BUF_SIZE" /> to fill the buffer more quickly and trigger automatic printing.
|
||||
2. Manually call or use a timer to call the <ApiLink name="lv_profiler_builtin_flush" /> function to force the log to be flushed.
|
||||
@@ -1,230 +0,0 @@
|
||||
.. _profiler:
|
||||
|
||||
========
|
||||
Profiler
|
||||
========
|
||||
|
||||
As the complexity of the application increases, performance issues such as low FPS and frequent cache misses
|
||||
causing lag may arise. LVGL has internally set up some hooks for performance measurement to help developers
|
||||
analyze and locate performance issues.
|
||||
|
||||
|
||||
|
||||
.. _profiler_introduction:
|
||||
|
||||
Introduction
|
||||
************
|
||||
|
||||
LVGL has a built-in trace system to track and record the timestamps of important events that occur during runtime,
|
||||
such as rendering events and user input events. These event timestamps serve in measurements taken in performance analysis.
|
||||
|
||||
The trace system has a configurable record buffer that stores the names of event functions and their timestamps.
|
||||
When the buffer is full, the trace system prints the log information through the provided user interface.
|
||||
|
||||
The output trace logs are formatted according to Android's `systrace <https://developer.android.com/topic/performance/tracing>`_
|
||||
format and can be visualized using `Perfetto <https://ui.perfetto.dev>`_.
|
||||
|
||||
|
||||
|
||||
.. _profiler_usage:
|
||||
|
||||
Usage
|
||||
*****
|
||||
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
To enable the profiler, set :c:macro:`LV_USE_PROFILER` in ``lv_conf.h`` and configure the following options:
|
||||
|
||||
1. Enable the built-in profiler functionality by setting :c:macro:`LV_USE_PROFILER_BUILTIN`. If you have POSIX environment support, you can enable :c:macro:`LV_USE_PROFILER_BUILTIN_POSIX`.
|
||||
|
||||
2. Buffer configuration: Set the value of :c:macro:`LV_PROFILER_BUILTIN_BUF_SIZE` to configure the buffer size. A larger buffer can store more trace event information, reducing interference with rendering. However, it also results in higher memory consumption.
|
||||
|
||||
3. Timestamp configuration: LVGL uses the :cpp:func:`lv_tick_get` function with a precision of 1ms by default to obtain timestamps when events occur. Therefore, it cannot accurately measure intervals below 1ms. If your system environment can provide higher precision (e.g., 1us), you can configure the profiler as follows:
|
||||
|
||||
- Recommended configuration in **Arduino** environments:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
static uint64_t my_get_tick_cb(void)
|
||||
{
|
||||
/* Use the microsecond time stamp provided by Arduino */
|
||||
return micros();
|
||||
}
|
||||
|
||||
void my_profiler_init(void)
|
||||
{
|
||||
lv_profiler_builtin_config_t config;
|
||||
lv_profiler_builtin_config_init(&config);
|
||||
config.tick_per_sec = 1000000; /* One second is equal to 1000000 microseconds */
|
||||
config.tick_get_cb = my_get_tick_cb;
|
||||
lv_profiler_builtin_init(&config);
|
||||
}
|
||||
|
||||
4. Log output configuration: LVGL uses the :cpp:func:`LV_LOG` interface by default to output trace information. If you want to use another interface to output log information (e.g. stdout), you can redirect the log output using the following code:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
static void my_log_print_cb(const char * buf)
|
||||
{
|
||||
printf("%s", buf);
|
||||
}
|
||||
|
||||
void my_profiler_init(void)
|
||||
{
|
||||
lv_profiler_builtin_config_t config;
|
||||
lv_profiler_builtin_config_init(&config);
|
||||
... /* other configurations */
|
||||
config.flush_cb = my_log_print_cb;
|
||||
lv_profiler_builtin_init(&config);
|
||||
}
|
||||
|
||||
|
||||
Run the test scenario
|
||||
---------------------
|
||||
|
||||
Run the UI scenario that you want to measure, such as scrolling a scrollable page up and down or entering/exiting an application.
|
||||
|
||||
|
||||
Process the logs
|
||||
----------------
|
||||
|
||||
Save the output log as `my_trace.txt`, use `trace_filter.py` for filtering and preprocessing:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
./lvgl/scripts/trace_filter.py my_trace.txt
|
||||
|
||||
or
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
python3 ./lvgl/scripts/trace_filter.py my_trace.txt
|
||||
|
||||
You will obtain a processed text file named `trace.systrace`, which roughly contains the following content:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
# tracer: nop
|
||||
#
|
||||
LVGL-1 [0] 2892.002993: tracing_mark_write: B|1|lv_timer_handler
|
||||
LVGL-1 [0] 2892.002993: tracing_mark_write: B|1|_lv_display_refr_timer
|
||||
LVGL-1 [0] 2892.003459: tracing_mark_write: B|1|refr_invalid_areas
|
||||
LVGL-1 [0] 2892.003461: tracing_mark_write: B|1|lv_draw_rect
|
||||
LVGL-1 [0] 2892.003550: tracing_mark_write: E|1|lv_draw_rect
|
||||
LVGL-1 [0] 2892.003552: tracing_mark_write: B|1|lv_draw_rect
|
||||
LVGL-1 [0] 2892.003556: tracing_mark_write: E|1|lv_draw_rect
|
||||
LVGL-1 [0] 2892.003560: tracing_mark_write: B|1|lv_draw_rect
|
||||
LVGL-1 [0] 2892.003573: tracing_mark_write: E|1|lv_draw_rect
|
||||
...
|
||||
|
||||
Import the processed `trace.systrace` file into `Perfetto <https://ui.perfetto.dev>`_ and wait for it to be parsed.
|
||||
|
||||
|
||||
Performance analysis
|
||||
--------------------
|
||||
|
||||
If the log parsing is successful, you will see a screen similar to the following:
|
||||
|
||||
.. image:: /_static/images/perfetto_ui.png
|
||||
|
||||
In the Perfetto UI, use the :kbd:`A` or :kbd:`D` keys to pan the timeline horizontally
|
||||
and the :kbd:`W` or :kbd:`S` keys to zoom in or out on the timeline.
|
||||
Use the mouse to move the focus and click on functions on the timeline to observe their execution time.
|
||||
|
||||
|
||||
|
||||
Adding Measurement Points
|
||||
*************************
|
||||
|
||||
Users can add their own measured functions:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
void my_function_1(void)
|
||||
{
|
||||
LV_PROFILER_BEGIN;
|
||||
do_something();
|
||||
LV_PROFILER_END;
|
||||
}
|
||||
|
||||
void my_function_2(void)
|
||||
{
|
||||
LV_PROFILER_BEGIN_TAG("do_something_1");
|
||||
do_something_1();
|
||||
LV_PROFILER_END_TAG("do_something_1");
|
||||
|
||||
LV_PROFILER_BEGIN_TAG("do_something_2");
|
||||
do_something_2();
|
||||
LV_PROFILER_END_TAG("do_something_2");
|
||||
}
|
||||
|
||||
|
||||
|
||||
.. _profiler_custom_implementation:
|
||||
|
||||
Custom Profiler Implementation
|
||||
******************************
|
||||
|
||||
If you wish to use a profiler method provided by your operating system, you can modify the following macros in ``lv_conf.h``:
|
||||
|
||||
- :c:macro:`LV_PROFILER_INCLUDE`: Provides a header file for the profiler function.
|
||||
- :c:macro:`LV_PROFILER_BEGIN`: Profiler start point function.
|
||||
- :c:macro:`LV_PROFILER_END`: Profiler end point function.
|
||||
- :c:macro:`LV_PROFILER_BEGIN_TAG`: Profiler start point function with custom tag.
|
||||
- :c:macro:`LV_PROFILER_END_TAG`: Profiler end point function with custom tag.
|
||||
|
||||
|
||||
Taking `NuttX <https://github.com/apache/nuttx>`_ RTOS as an example:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
#define LV_PROFILER_INCLUDE "nuttx/sched_note.h"
|
||||
#define LV_PROFILER_BEGIN sched_note_begin(NOTE_TAG_ALWAYS)
|
||||
#define LV_PROFILER_END sched_note_end(NOTE_TAG_ALWAYS)
|
||||
#define LV_PROFILER_BEGIN_TAG(str) sched_note_beginex(NOTE_TAG_ALWAYS, str)
|
||||
#define LV_PROFILER_END_TAG(str) sched_note_endex(NOTE_TAG_ALWAYS, str)
|
||||
|
||||
|
||||
|
||||
.. _profiler_faq:
|
||||
|
||||
FAQ
|
||||
***
|
||||
|
||||
|
||||
Perfetto log parsing fails
|
||||
--------------------------
|
||||
|
||||
Please check the completeness of the logs. If the logs are incomplete, it may be due to the following reasons:
|
||||
|
||||
1. Serial port reception errors caused by a high baud rate. You need to reduce the baud rate.
|
||||
2. Data corruption caused by other thread logs inserted during the printing of trace logs. You need to disable the log output of other threads or refer to the configuration above to use a separate log output interface.
|
||||
3. Make sure that the string passed in by :c:macro:`LV_PROFILER_BEGIN_TAG` or :c:macro:`LV_PROFILER_END_TAG` is not a local variable on the stack or a string in shared memory, because currently only the string address is recorded and the content is not copied.
|
||||
|
||||
|
||||
Function execution time displayed as 0s in Perfetto
|
||||
---------------------------------------------------
|
||||
|
||||
If the function execution time is lower than the precision of the timestamps, this situation can occur. You can refer to the configuration instructions above to use a higher precision timestamp.
|
||||
See Configuration_ for an example of this.
|
||||
|
||||
|
||||
Significant stuttering occurs during profiling
|
||||
----------------------------------------------
|
||||
|
||||
When the buffer used to store trace events becomes full, the profiler will output all the data in the buffer, which can cause UI blocking and stuttering during the output. You can optimize this by taking the following measures:
|
||||
|
||||
1. Increase the value of :c:macro:`LV_PROFILER_BUILTIN_BUF_SIZE`. A larger buffer can reduce the frequency of log flushing, but it also consumes more memory.
|
||||
2. Optimize the execution time of log flushing functions, such as increasing the serial port baud rate or improving file writing speed.
|
||||
|
||||
|
||||
Trace logs are not being output
|
||||
-------------------------------
|
||||
|
||||
If the trace logs are not automatically printed when the buffer is not full, you can try the following methods to force the log output:
|
||||
|
||||
1. Reduce the value of :c:macro:`LV_PROFILER_BUILTIN_BUF_SIZE` to fill the buffer more quickly and trigger automatic printing.
|
||||
2. Manually call or use a timer to call the :cpp:func:`lv_profiler_builtin_flush` function to force the log to be flushed.
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
---
|
||||
title: "System Monitor (sysmon)"
|
||||
description: "The System Monitor module provides real-time monitoring of system performance metrics directly on your display. It supports both performance monitoring (CPU usage and FPS) and memory monitoring (us..."
|
||||
---
|
||||
|
||||
The System Monitor module provides real-time monitoring of system performance
|
||||
metrics directly on your display. It supports both performance monitoring
|
||||
(CPU usage and FPS) and memory monitoring (used memory and fragmentation).
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires <ApiLink name="LV_USE_LABEL" /> = 1 in lv_conf.h
|
||||
- Requires <ApiLink name="LV_USE_OBSERVER" /> = 1 in lv_conf.h
|
||||
- Requires <ApiLink name="LV_USE_SYSMON" /> = 1 in lv_conf.h
|
||||
|
||||
## Usage
|
||||
|
||||
### Configuration
|
||||
|
||||
Enable in `lv_conf.h`:
|
||||
|
||||
```c
|
||||
/* Main sysmon enable */
|
||||
#define LV_USE_SYSMON 1
|
||||
|
||||
/* Performance monitor (CPU% and FPS) */
|
||||
#define LV_USE_PERF_MONITOR 1
|
||||
|
||||
/* Memory monitor (used + fragmentation) */
|
||||
#define LV_USE_MEM_MONITOR 1
|
||||
|
||||
/* Optional: refresh period in ms */
|
||||
#define LV_SYSMON_REFR_PERIOD_DEF 300
|
||||
|
||||
/* Optional: log to console instead of screen */
|
||||
#define LV_USE_PERF_MONITOR_LOG_MODE 0
|
||||
```
|
||||
|
||||
### Creating Monitors
|
||||
|
||||
```c
|
||||
/* Create generic monitor */
|
||||
lv_obj_t * sysmon = lv_sysmon_create(lv_display_get_default());
|
||||
|
||||
/* Create performance monitor */
|
||||
lv_sysmon_show_performance(NULL); /* NULL = default display */
|
||||
|
||||
/* Create memory monitor */
|
||||
lv_sysmon_show_memory(NULL);
|
||||
```
|
||||
|
||||
### Performance Monitor
|
||||
|
||||
Tracks:
|
||||
|
||||
- FPS (Frames Per Second)
|
||||
- CPU usage (%)
|
||||
- Render time (ms)
|
||||
- Flush time (ms)
|
||||
- Self CPU usage (%) if enabled
|
||||
|
||||
Display format:
|
||||
|
||||
```text
|
||||
32 FPS, 45% CPU
|
||||
8 ms
|
||||
```
|
||||
|
||||
Where:
|
||||
|
||||
- Line 1: FPS, Total CPU%
|
||||
- Line 2: Total time (Render | Flush)
|
||||
|
||||
## Pause and Resume
|
||||
|
||||
<ApiLink name="lv_sysmon_performance_pause" display="lv_sysmon_performance_pause(disp)" /> pauses the perf monitor.
|
||||
|
||||
<ApiLink name="lv_sysmon_performance_resume" display="lv_sysmon_performance_resume(disp)" /> resumes the perf monitor.
|
||||
|
||||
### Memory Monitor
|
||||
|
||||
Displays:
|
||||
|
||||
- Current memory usage (kB and %)
|
||||
- Peak memory usage (kB)
|
||||
- Fragmentation (%)
|
||||
|
||||
Display format:
|
||||
|
||||
```text
|
||||
24.8 kB (76%)
|
||||
32.4 kB max, 18% frag.
|
||||
```
|
||||
|
||||
### Positioning
|
||||
|
||||
Configure positions in lv_conf.h:
|
||||
|
||||
```c
|
||||
/* Top-right corner */
|
||||
#define LV_USE_PERF_MONITOR_POS LV_ALIGN_TOP_RIGHT
|
||||
|
||||
/* Bottom-right corner */
|
||||
#define LV_USE_MEM_MONITOR_POS LV_ALIGN_BOTTOM_RIGHT
|
||||
```
|
||||
|
||||
## Implementation Details
|
||||
|
||||
### Initialization
|
||||
|
||||
Maintains:
|
||||
|
||||
- Global memory monitor (`sysmon_mem`)
|
||||
- Per-display performance structures
|
||||
|
||||
### Performance Measurement
|
||||
|
||||
Event-based collection:
|
||||
|
||||
| Event | Measurement |
|
||||
| --- | --- |
|
||||
| LV_EVENT_REFR_START | Refresh interval start |
|
||||
| LV_EVENT_REFR_READY | Record refresh duration |
|
||||
| LV_EVENT_RENDER_START | Render time start |
|
||||
| LV_EVENT_RENDER_READY | Record render duration |
|
||||
| LV_EVENT_FLUSH_* | Measure flush operations |
|
||||
|
||||
### Timers
|
||||
|
||||
- Performance: `perf_update_timer_cb`
|
||||
- Memory: `mem_update_timer_cb`
|
||||
- Default period: 300ms (<ApiLink name="LV_SYSMON_REFR_PERIOD_DEF" />)
|
||||
@@ -1,171 +0,0 @@
|
||||
.. _sysmon:
|
||||
|
||||
=======================
|
||||
System Monitor (sysmon)
|
||||
=======================
|
||||
|
||||
The System Monitor module provides real-time monitoring of system performance
|
||||
metrics directly on your display. It supports both performance monitoring
|
||||
(CPU usage and FPS) and memory monitoring (used memory and fragmentation).
|
||||
|
||||
|
||||
|
||||
Dependencies
|
||||
************
|
||||
|
||||
- Requires :c:macro:`LV_USE_LABEL` = 1 in lv_conf.h
|
||||
- Requires :c:macro:`LV_USE_OBSERVER` = 1 in lv_conf.h
|
||||
- Requires :c:macro:`LV_USE_SYSMON` = 1 in lv_conf.h
|
||||
|
||||
|
||||
|
||||
.. _sysmon_usage:
|
||||
|
||||
Usage
|
||||
*****
|
||||
|
||||
|
||||
Configuration
|
||||
--------------
|
||||
|
||||
Enable in ``lv_conf.h``:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
/* Main sysmon enable */
|
||||
#define LV_USE_SYSMON 1
|
||||
|
||||
/* Performance monitor (CPU% and FPS) */
|
||||
#define LV_USE_PERF_MONITOR 1
|
||||
|
||||
/* Memory monitor (used + fragmentation) */
|
||||
#define LV_USE_MEM_MONITOR 1
|
||||
|
||||
/* Optional: refresh period in ms */
|
||||
#define LV_SYSMON_REFR_PERIOD_DEF 300
|
||||
|
||||
/* Optional: log to console instead of screen */
|
||||
#define LV_USE_PERF_MONITOR_LOG_MODE 0
|
||||
|
||||
|
||||
Creating Monitors
|
||||
-----------------
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
/* Create generic monitor */
|
||||
lv_obj_t * sysmon = lv_sysmon_create(lv_display_get_default());
|
||||
|
||||
/* Create performance monitor */
|
||||
lv_sysmon_show_performance(NULL); /* NULL = default display */
|
||||
|
||||
/* Create memory monitor */
|
||||
lv_sysmon_show_memory(NULL);
|
||||
|
||||
|
||||
Performance Monitor
|
||||
-------------------
|
||||
|
||||
Tracks:
|
||||
|
||||
- FPS (Frames Per Second)
|
||||
- CPU usage (%)
|
||||
- Render time (ms)
|
||||
- Flush time (ms)
|
||||
- Self CPU usage (%) if enabled
|
||||
|
||||
Display format:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
32 FPS, 45% CPU
|
||||
8 ms
|
||||
|
||||
Where:
|
||||
|
||||
- Line 1: FPS, Total CPU%
|
||||
- Line 2: Total time (Render | Flush)
|
||||
|
||||
|
||||
|
||||
Pause and Resume
|
||||
****************
|
||||
|
||||
:cpp:expr:`lv_sysmon_performance_pause(disp)` pauses the perf monitor.
|
||||
|
||||
:cpp:expr:`lv_sysmon_performance_resume(disp)` resumes the perf monitor.
|
||||
|
||||
|
||||
Memory Monitor
|
||||
--------------
|
||||
|
||||
Displays:
|
||||
|
||||
- Current memory usage (kB and %)
|
||||
- Peak memory usage (kB)
|
||||
- Fragmentation (%)
|
||||
|
||||
Display format:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
24.8 kB (76%)
|
||||
32.4 kB max, 18% frag.
|
||||
|
||||
|
||||
Positioning
|
||||
-----------
|
||||
|
||||
Configure positions in lv_conf.h:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
/* Top-right corner */
|
||||
#define LV_USE_PERF_MONITOR_POS LV_ALIGN_TOP_RIGHT
|
||||
|
||||
/* Bottom-right corner */
|
||||
#define LV_USE_MEM_MONITOR_POS LV_ALIGN_BOTTOM_RIGHT
|
||||
|
||||
|
||||
|
||||
Implementation Details
|
||||
**********************
|
||||
|
||||
|
||||
Initialization
|
||||
--------------
|
||||
|
||||
Maintains:
|
||||
|
||||
- Global memory monitor (``sysmon_mem``)
|
||||
- Per-display performance structures
|
||||
|
||||
|
||||
Performance Measurement
|
||||
-----------------------
|
||||
|
||||
Event-based collection:
|
||||
|
||||
.. container:: tighter-table-3
|
||||
|
||||
+----------------------+--------------------------------+
|
||||
| Event | Measurement |
|
||||
+======================+================================+
|
||||
| LV_EVENT_REFR_START | Refresh interval start |
|
||||
+----------------------+--------------------------------+
|
||||
| LV_EVENT_REFR_READY | Record refresh duration |
|
||||
+----------------------+--------------------------------+
|
||||
| LV_EVENT_RENDER_START| Render time start |
|
||||
+----------------------+--------------------------------+
|
||||
| LV_EVENT_RENDER_READY| Record render duration |
|
||||
+----------------------+--------------------------------+
|
||||
| LV_EVENT_FLUSH_* | Measure flush operations |
|
||||
+----------------------+--------------------------------+
|
||||
|
||||
|
||||
Timers
|
||||
------
|
||||
|
||||
- Performance: ``perf_update_timer_cb``
|
||||
- Memory: ``mem_update_timer_cb``
|
||||
- Default period: 300ms (:c:macro:`LV_SYSMON_REFR_PERIOD_DEF`)
|
||||
@@ -0,0 +1,153 @@
|
||||
---
|
||||
title: UI Testing
|
||||
description: "The Test module provides functions to emulate clicks, key presses, encoder turns, time passing, and compare the UI with reference images."
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
The Test module provides functions to emulate clicks, key presses, encoder turns, time passing, and
|
||||
compare the UI with reference images.
|
||||
|
||||
These functions can be easily used in any test framework (such as Unity, GoogleTest, etc.), and
|
||||
assertions can be performed to check if, for example:
|
||||
|
||||
- A widget's value is different from the expected value after emulating user inputs.
|
||||
- The values are incorrect after some time has passed.
|
||||
- The screen's content is different from the reference image.
|
||||
- Some event functions are not triggered.
|
||||
- Etc.
|
||||
|
||||
Note that it is assumed the tests are performed on a desktop or server environment,
|
||||
where there are no memory constraints.
|
||||
|
||||
## Usage
|
||||
|
||||
The Test module can be enabled by configuring <ApiLink name="LV_USE_TEST" /> to a non-zero value,
|
||||
and it consists of the following components:
|
||||
|
||||
- Helpers
|
||||
- Display emulation
|
||||
- Input device emulation
|
||||
- Screenshot comparison
|
||||
|
||||
### Helpers
|
||||
|
||||
##### Time
|
||||
|
||||
To emulate elapsed time, two functions can be used:
|
||||
|
||||
1. <ApiLink name="lv_test_wait" display="lv_test_wait(ms)" />: Emulates that `ms` milliseconds have elapsed, but it also calls `lv_timer_handler` after each millisecond.
|
||||
This is useful to check if events (e.g., long press, long press repeat) and timers were triggered correctly over time.
|
||||
2. <ApiLink name="lv_test_fast_forward" display="lv_test_fast_forward(ms)" />: Jumps `ms` milliseconds ahead and calls `lv_timer_handler` only once at the end.
|
||||
|
||||
<ApiLink name="lv_refr_now" display="lv_refr_now(NULL)" /> is called at the end of both functions to ensure that animations and
|
||||
widget coordinates are recalculated.
|
||||
|
||||
<ApiLink name="lv_refr_now" display="lv_refr_now(NULL)" /> can also be called manually to force LVGL to refresh the emulated display.
|
||||
|
||||
##### Memory Usage
|
||||
|
||||
If <ApiLink name="LV_USE_STDLIB_MALLOC" /> is set to <ApiLink name="LV_STDLIB_BUILTIN" />, memory usage and memory leaks can be monitored.
|
||||
|
||||
```c
|
||||
size_t mem1 = lv_test_get_free_mem();
|
||||
<create and delete items>
|
||||
size_t mem2 = lv_test_get_free_mem();
|
||||
if(mem1 != mem2) fail();
|
||||
```
|
||||
|
||||
It might make sense to create and delete items in a loop many times and add a small tolerance
|
||||
to the memory leakage test. This might be needed due to potential memory fragmentation. Empirically,
|
||||
a tolerance of 32 bytes is recommended.
|
||||
|
||||
```c
|
||||
if(LV_ABS((int64_t)mem2 - (int64_t)mem1) > 32) fail();
|
||||
```
|
||||
|
||||
### Display Emulation
|
||||
|
||||
By calling <ApiLink name="lv_test_display_create" display="lv_test_display_create(hor_res, ver_res)" />, a dummy display can be created.
|
||||
|
||||
It functions like any other normal display, but its content exists only in memory.
|
||||
|
||||
When creating this display, the horizontal and vertical resolutions must be passed. Internally,
|
||||
a framebuffer will be allocated for this size, and `XRGB8888` color format will be set.
|
||||
|
||||
The resolution and color format can be changed at any time by calling <ApiLink name="lv_display_set_resolution" /> and
|
||||
<ApiLink name="lv_display_set_color_format" />.
|
||||
|
||||
### Input Device Emulation
|
||||
|
||||
By calling <ApiLink name="lv_test_indev_create_all" />, three test input devices will be created:
|
||||
|
||||
1. A pointer (for touch or mouse)
|
||||
2. A keypad
|
||||
3. An encoder
|
||||
|
||||
For example, this is how a scroll gesture can be emulated:
|
||||
|
||||
```c
|
||||
lv_test_mouse_move_to(20, 30);
|
||||
lv_test_mouse_press();
|
||||
lv_test_wait(20);
|
||||
lv_test_mouse_move_by(0, 100);
|
||||
lv_test_wait(20);
|
||||
lv_test_mouse_release();
|
||||
lv_test_wait(20);
|
||||
```
|
||||
|
||||
It is recommended to add <ApiLink name="lv_test_wait" /> after user actions to ensure that
|
||||
the new state and coordinates are read and applied from the input device.
|
||||
|
||||
After that, the user can check if the given widget was really scrolled
|
||||
by getting the Y coordinate of a child.
|
||||
|
||||
```c
|
||||
int32_t y_start = lv_obj_get_y(child);
|
||||
<scroll emulation>
|
||||
int32_t y_end = lv_obj_get_y(child);
|
||||
if(y_start + 100 != y_end) fail();
|
||||
```
|
||||
|
||||
Please refer to the `lv_test_indev.h` link below (in the [API](/debugging/test) section)
|
||||
for the list of supported input device emulation functions.
|
||||
|
||||
### Screenshot Comparison
|
||||
|
||||
`lv_test_screenshot_result_t lv_test_screenshot_compare(const char * fn_ref)` is a useful function
|
||||
to compare the content of the emulated display with reference PNG images.
|
||||
|
||||
This function works in a practical way:
|
||||
|
||||
- If the folder(s) referenced in `fn_ref` do not exist, they will be created automatically.
|
||||
- If the reference image is not found, it will be created automatically from the rendered screen
|
||||
(unless <ApiLink name="LV_TEST_SCREENSHOT_CREATE_REFERENCE_IMAGE" /> is `0`).
|
||||
- If the comparison fails, an `<image_name>_err.png` file will be created with the rendered content next to the reference image.
|
||||
- If the comparison fails, the X and Y coordinates of the first divergent pixel, along with the actual and expected colors, will also be printed.
|
||||
|
||||
The reference PNG images should have a **32-bit color format** and match the display size.
|
||||
|
||||
The test display's content will be converted to `XRGB8888` to simplify comparison with the reference images.
|
||||
The conversion is supported from the following formats (i.e., the test display should have a color
|
||||
format in this list):
|
||||
|
||||
- <ApiLink name="LV_COLOR_FORMAT_XRGB8888" />
|
||||
- <ApiLink name="LV_COLOR_FORMAT_ARGB8888" />
|
||||
- <ApiLink name="LV_COLOR_FORMAT_ARGB8888_PREMULTIPLIED" />
|
||||
- <ApiLink name="LV_COLOR_FORMAT_RGB888" />
|
||||
- <ApiLink name="LV_COLOR_FORMAT_RGB565" />
|
||||
- <ApiLink name="LV_COLOR_FORMAT_RGB565_SWAPPED" />
|
||||
- <ApiLink name="LV_COLOR_FORMAT_L8" />
|
||||
- <ApiLink name="LV_COLOR_FORMAT_AL88" />
|
||||
- <ApiLink name="LV_COLOR_FORMAT_I1" />
|
||||
|
||||
To read and decode PNG images and to store the converted rendered image, a few MBs of RAM are dynamically allocated using the standard `malloc`
|
||||
(not <ApiLink name="lv_malloc" />).
|
||||
|
||||
The screenshot comparison uses `lodepng` which is built-in to LVGL and just needs to be enabled with
|
||||
<ApiLink name="LV_USE_LODEPNG" />.
|
||||
|
||||
To avoid making the entire Test module dependent on `lodepng`, screenshot comparison can be individually enabled by
|
||||
<ApiLink name="LV_USE_TEST_SCREENSHOT_COMPARE" />.
|
||||
|
||||
## API
|
||||
@@ -1,176 +0,0 @@
|
||||
.. _test:
|
||||
|
||||
==========
|
||||
UI Testing
|
||||
==========
|
||||
|
||||
Overview
|
||||
********
|
||||
|
||||
The Test module provides functions to emulate clicks, key presses, encoder turns, time passing, and
|
||||
compare the UI with reference images.
|
||||
|
||||
These functions can be easily used in any test framework (such as Unity, GoogleTest, etc.), and
|
||||
assertions can be performed to check if, for example:
|
||||
|
||||
- A widget's value is different from the expected value after emulating user inputs.
|
||||
- The values are incorrect after some time has passed.
|
||||
- The screen's content is different from the reference image.
|
||||
- Some event functions are not triggered.
|
||||
- Etc.
|
||||
|
||||
Note that it is assumed the tests are performed on a desktop or server environment,
|
||||
where there are no memory constraints.
|
||||
|
||||
|
||||
|
||||
Usage
|
||||
*****
|
||||
|
||||
The Test module can be enabled by configuring :c:macro:`LV_USE_TEST` to a non-zero value,
|
||||
and it consists of the following components:
|
||||
|
||||
- Helpers
|
||||
- Display emulation
|
||||
- Input device emulation
|
||||
- Screenshot comparison
|
||||
|
||||
|
||||
Helpers
|
||||
-------
|
||||
|
||||
Time
|
||||
~~~~
|
||||
|
||||
To emulate elapsed time, two functions can be used:
|
||||
|
||||
1. :cpp:expr:`lv_test_wait(ms)`: Emulates that ``ms`` milliseconds have elapsed, but it also calls ``lv_timer_handler`` after each millisecond.
|
||||
This is useful to check if events (e.g., long press, long press repeat) and timers were triggered correctly over time.
|
||||
2. :cpp:expr:`lv_test_fast_forward(ms)`: Jumps ``ms`` milliseconds ahead and calls ``lv_timer_handler`` only once at the end.
|
||||
|
||||
:cpp:expr:`lv_refr_now(NULL)` is called at the end of both functions to ensure that animations and
|
||||
widget coordinates are recalculated.
|
||||
|
||||
:cpp:expr:`lv_refr_now(NULL)` can also be called manually to force LVGL to refresh the emulated display.
|
||||
|
||||
Memory Usage
|
||||
~~~~~~~~~~~~
|
||||
|
||||
If :c:macro:`LV_USE_STDLIB_MALLOC` is set to :c:macro:`LV_STDLIB_BUILTIN`, memory usage and memory leaks can be monitored.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
size_t mem1 = lv_test_get_free_mem();
|
||||
<create and delete items>
|
||||
size_t mem2 = lv_test_get_free_mem();
|
||||
if(mem1 != mem2) fail();
|
||||
|
||||
It might make sense to create and delete items in a loop many times and add a small tolerance
|
||||
to the memory leakage test. This might be needed due to potential memory fragmentation. Empirically,
|
||||
a tolerance of 32 bytes is recommended.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
if(LV_ABS((int64_t)mem2 - (int64_t)mem1) > 32) fail();
|
||||
|
||||
|
||||
Display Emulation
|
||||
-----------------
|
||||
|
||||
By calling :cpp:expr:`lv_test_display_create(hor_res, ver_res)`, a dummy display can be created.
|
||||
|
||||
It functions like any other normal display, but its content exists only in memory.
|
||||
|
||||
When creating this display, the horizontal and vertical resolutions must be passed. Internally,
|
||||
a framebuffer will be allocated for this size, and ``XRGB8888`` color format will be set.
|
||||
|
||||
The resolution and color format can be changed at any time by calling :cpp:func:`lv_display_set_resolution` and
|
||||
:cpp:func:`lv_display_set_color_format`.
|
||||
|
||||
|
||||
Input Device Emulation
|
||||
----------------------
|
||||
|
||||
By calling :cpp:func:`lv_test_indev_create_all`, three test input devices will be created:
|
||||
|
||||
1. A pointer (for touch or mouse)
|
||||
2. A keypad
|
||||
3. An encoder
|
||||
|
||||
For example, this is how a scroll gesture can be emulated:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
lv_test_mouse_move_to(20, 30);
|
||||
lv_test_mouse_press();
|
||||
lv_test_wait(20);
|
||||
lv_test_mouse_move_by(0, 100);
|
||||
lv_test_wait(20);
|
||||
lv_test_mouse_release();
|
||||
lv_test_wait(20);
|
||||
|
||||
It is recommended to add :cpp:func:`lv_test_wait` after user actions to ensure that
|
||||
the new state and coordinates are read and applied from the input device.
|
||||
|
||||
After that, the user can check if the given widget was really scrolled
|
||||
by getting the Y coordinate of a child.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
int32_t y_start = lv_obj_get_y(child);
|
||||
<scroll emulation>
|
||||
int32_t y_end = lv_obj_get_y(child);
|
||||
if(y_start + 100 != y_end) fail();
|
||||
|
||||
Please refer to the ``lv_test_indev.h`` link below (in the :ref:`test_api` section)
|
||||
for the list of supported input device emulation functions.
|
||||
|
||||
|
||||
Screenshot Comparison
|
||||
---------------------
|
||||
|
||||
``lv_test_screenshot_result_t lv_test_screenshot_compare(const char * fn_ref)`` is a useful function
|
||||
to compare the content of the emulated display with reference PNG images.
|
||||
|
||||
|
||||
This function works in a practical way:
|
||||
|
||||
- If the folder(s) referenced in ``fn_ref`` do not exist, they will be created automatically.
|
||||
- If the reference image is not found, it will be created automatically from the rendered screen
|
||||
(unless :c:macro:`LV_TEST_SCREENSHOT_CREATE_REFERENCE_IMAGE` is ``0``).
|
||||
- If the comparison fails, an ``<image_name>_err.png`` file will be created with the rendered content next to the reference image.
|
||||
- If the comparison fails, the X and Y coordinates of the first divergent pixel, along with the actual and expected colors, will also be printed.
|
||||
|
||||
The reference PNG images should have a **32-bit color format** and match the display size.
|
||||
|
||||
The test display's content will be converted to ``XRGB8888`` to simplify comparison with the reference images.
|
||||
The conversion is supported from the following formats (i.e., the test display should have a color
|
||||
format in this list):
|
||||
|
||||
- :cpp:enumerator:`LV_COLOR_FORMAT_XRGB8888`
|
||||
- :cpp:enumerator:`LV_COLOR_FORMAT_ARGB8888`
|
||||
- :cpp:enumerator:`LV_COLOR_FORMAT_ARGB8888_PREMULTIPLIED`
|
||||
- :cpp:enumerator:`LV_COLOR_FORMAT_RGB888`
|
||||
- :cpp:enumerator:`LV_COLOR_FORMAT_RGB565`
|
||||
- :cpp:enumerator:`LV_COLOR_FORMAT_RGB565_SWAPPED`
|
||||
- :cpp:enumerator:`LV_COLOR_FORMAT_L8`
|
||||
- :cpp:enumerator:`LV_COLOR_FORMAT_AL88`
|
||||
- :cpp:enumerator:`LV_COLOR_FORMAT_I1`
|
||||
|
||||
To read and decode PNG images and to store the converted rendered image, a few MBs of RAM are dynamically allocated using the standard ``malloc``
|
||||
(not :cpp:expr:`lv_malloc`).
|
||||
|
||||
|
||||
The screenshot comparison uses ``lodepng`` which is built-in to LVGL and just needs to be enabled with
|
||||
:c:macro:`LV_USE_LODEPNG`.
|
||||
|
||||
To avoid making the entire Test module dependent on ``lodepng``, screenshot comparison can be individually enabled by
|
||||
:c:macro:`LV_USE_TEST_SCREENSHOT_COMPARE`.
|
||||
|
||||
|
||||
.. _test_api:
|
||||
|
||||
API
|
||||
***
|
||||
|
||||
.. API startswith: lv_test_
|
||||
@@ -0,0 +1,25 @@
|
||||
---
|
||||
title: "VG-Lite GPU Simulator"
|
||||
description: "LVGL integrates a VG-Lite GPU simulator based on ThorVG. Its purpose is to simplify the debugging of VG-Lite GPU adaptation and reduce the time spent debugging and locating problems on hardware dev..."
|
||||
---
|
||||
|
||||
LVGL integrates a VG-Lite GPU simulator based on ThorVG. Its purpose is to simplify
|
||||
the debugging of VG-Lite GPU adaptation and reduce the time spent debugging and
|
||||
locating problems on hardware devices.
|
||||
|
||||
It has been integrated into the CI automated compilation and testing process to ensure
|
||||
that the VG-Lite rendering backend can be fully tested after each Pull Request (PR) is
|
||||
merged into the repository.
|
||||
|
||||
## How It Works
|
||||
|
||||
Using the `vg_lite.h` header file, ThorVG re-implements the VG-Lite API, generating
|
||||
the same rendered images as the real VG-Lite GPU hardware.
|
||||
|
||||
## Configuration
|
||||
|
||||
1. Enable VG-Lite rendering backend, see [VG-Lite General GPU](/integration/chip_vendors/nxp/vg_lite_gpu).
|
||||
2. Enable ThorVG and turn on the configuration <ApiLink name="LV_USE_THORVG_INTERNAL" /> or <ApiLink name="LV_USE_THORVG_EXTERNAL" />.
|
||||
It is recommended to use the internal ThorVG library to ensure uniform rendering results.
|
||||
3. Enable <ApiLink name="LV_USE_VG_LITE_THORVG" /> and set <ApiLink name="LV_DRAW_BUF_ALIGN" /> to 64. The rest of the options can remain default.
|
||||
Make sure <ApiLink name="LV_VG_LITE_USE_GPU_INIT" /> is enabled, because the thorvg drawing context needs to be initialized before it can be used.
|
||||
@@ -1,34 +0,0 @@
|
||||
.. _vg_lite_tvg:
|
||||
|
||||
=====================
|
||||
VG-Lite GPU Simulator
|
||||
=====================
|
||||
|
||||
LVGL integrates a VG-Lite GPU simulator based on ThorVG. Its purpose is to simplify
|
||||
the debugging of VG-Lite GPU adaptation and reduce the time spent debugging and
|
||||
locating problems on hardware devices.
|
||||
|
||||
It has been integrated into the CI automated compilation and testing process to ensure
|
||||
that the VG-Lite rendering backend can be fully tested after each Pull Request (PR) is
|
||||
merged into the repository.
|
||||
|
||||
|
||||
|
||||
How It Works
|
||||
************
|
||||
|
||||
Using the ``vg_lite.h`` header file, ThorVG re-implements the VG-Lite API, generating
|
||||
the same rendered images as the real VG-Lite GPU hardware.
|
||||
|
||||
|
||||
|
||||
Configuration
|
||||
*************
|
||||
|
||||
1. Enable VG-Lite rendering backend, see :ref:`vg_lite`.
|
||||
|
||||
2. Enable ThorVG and turn on the configuration :c:macro:`LV_USE_THORVG_INTERNAL` or :c:macro:`LV_USE_THORVG_EXTERNAL`.
|
||||
It is recommended to use the internal ThorVG library to ensure uniform rendering results.
|
||||
|
||||
3. Enable :c:macro:`LV_USE_VG_LITE_THORVG` and set :c:macro:`LV_DRAW_BUF_ALIGN` to 64. The rest of the options can remain default.
|
||||
Make sure :c:macro:`LV_VG_LITE_USE_GPU_INIT` is enabled, because the thorvg drawing context needs to be initialized before it can be used.
|
||||
Reference in New Issue
Block a user