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:
Mutahhar Mustafa Khan
2026-04-22 14:59:08 +01:00
committed by GitHub
parent fc1452573c
commit 4750fe054b
699 changed files with 34355 additions and 69004 deletions
+399
View File
@@ -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
```
-442
View File
@@ -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
+7
View File
@@ -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.
-20
View File
@@ -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
+73
View File
@@ -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
-89
View File
@@ -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
+13
View File
@@ -0,0 +1,13 @@
{
"title": "Debugging",
"pages": [
"gdb_plugin",
"log",
"monkey",
"obj_id",
"profiler",
"sysmon",
"test",
"vg_lite_tvg"
]
}
+55
View File
@@ -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
-63
View File
@@ -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
+176
View File
@@ -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" /> &lt;= <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.
-197
View File
@@ -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.
+185
View File
@@ -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:
![Perfetto Ui](/_static/images/perfetto_ui.png)
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.
-230
View File
@@ -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.
+132
View File
@@ -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" />)
-171
View File
@@ -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`)
+153
View File
@@ -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
-176
View File
@@ -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_
+25
View File
@@ -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.
-34
View File
@@ -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.