Argument Checking

LVGL validates the arguments of its public API functions at runtime, so an invalid call is reported and rejected instead of corrupting memory or crashing.

Edit on GitHub

Every public LVGL function validates its arguments before doing any work. If a check fails, LVGL logs a warning and the function returns early instead of running with an invalid pointer or an out-of-range value. This turns a whole class of hard-to-debug crashes into a single log line that names the function and the condition that failed.

This is controlled by LV_USE_CHECK_ARG, which is enabled by default.

What it does

The checks are implemented with the LV_CHECK_ARG and LV_CHECK_OBJ macros. For example, lv_label_set_text starts with:

 
void lv_label_set_text(lv_obj_t * obj, const char * text)
{
    LV_CHECK_OBJ(obj, &lv_label_class, return);
    ...
}

So calling it with a NULL object does nothing and produces:

[Warn]	(5.123, +12)	lv_label_set_text: Check failed: obj != NULL lv_label.c:134

The condition that failed is part of the message, so you can see exactly which argument was wrong. Log output requires LV_USE_LOG and a log mode other than NONE (see below); the early return happens either way.

Checks reject the call, they don't fix it

A failed check means the call had no effect. Getters return a default value (NULL, 0, etc.) and setters do nothing. It protects your application from undefined behavior, it does not make an invalid call succeed.

Configuration

OptionDefaultEffect
LV_USE_CHECK_ARG1Master switch. When 0, every check below compiles to nothing.
LV_CHECK_ARG_LOG_MODENONE in lv_conf.h, VERBOSE in Kconfig when logging is onHow much is logged on a failed check.
LV_CHECK_ARG_ASSERT_ON_FAIL0Also run LV_ASSERT_HANDLER (halt or breakpoint) before returning.
LV_USE_CHECK_OBJ_CLASSTYPE0Also verify that a widget argument has the expected class.
LV_USE_CHECK_OBJ_VALIDITY0Also verify that a widget argument is still part of the widget tree.
LV_USE_CHECK_OBJ_PARENT_LINK0While checking validity, also verify that parent and child point at each other.

Log mode

LV_CHECK_ARG_LOG_MODE selects how much detail a failed check produces. It requires LV_USE_LOG; with logging disabled, no output is produced regardless of the setting.

  • LV_CHECK_ARG_LOG_MODE_NONE means no output. The check still returns early.
  • LV_CHECK_ARG_LOG_MODE_MINIMAL logs "Check failed" plus file and line. Keeps the condition strings out of the binary, which saves flash.
  • LV_CHECK_ARG_LOG_MODE_VERBOSE logs "Check failed: <cond>" plus any message LVGL supplied for that check.

Widget checks

By default a widget argument is only checked for NULL. Two extra layers can be enabled while developing:

 
/* lv_conf.h */
#define LV_USE_CHECK_OBJ_CLASSTYPE  1   /* is it really a label? */
#define LV_USE_CHECK_OBJ_VALIDITY   1   /* is it still alive in the widget tree? */

LV_USE_CHECK_OBJ_CLASSTYPE catches passing a widget of the wrong type (a button to lv_label_set_text, for example). LV_USE_CHECK_OBJ_VALIDITY catches use-after-delete: a pointer to a widget that was already deleted is no longer in the widget tree, so the call is rejected instead of dereferencing freed memory.

LV_USE_CHECK_OBJ_PARENT_LINK extends the validity check to also verify that each parent's children list contains the child it is walking up from. It finds widget-tree corruption and requires LV_USE_CHECK_OBJ_VALIDITY and LV_USE_ASSERT.

Enable the widget checks only during development

lv_obj_has_class() walks the class hierarchy and lv_obj_is_in_widget_tree() walks the widget tree on every call. That is significant overhead in rendering-heavy code. Keep LV_USE_CHECK_OBJ_CLASSTYPE, LV_USE_CHECK_OBJ_VALIDITY and LV_USE_CHECK_OBJ_PARENT_LINK at 0 in production builds.

Asserting on failure

During a debugging session it is often easier to stop at the offending call than to read a log afterwards. Setting LV_CHECK_ARG_ASSERT_ON_FAIL to 1 runs LV_ASSERT_HANDLER before the early return, so you can use pair it with a debugger to check what the call stack looks like.

Suggested settings

Development: catch as much as possible.

 
#define LV_USE_CHECK_ARG            1
#define LV_CHECK_ARG_LOG_MODE       LV_CHECK_ARG_LOG_MODE_VERBOSE
#define LV_USE_CHECK_OBJ_CLASSTYPE  1
#define LV_USE_CHECK_OBJ_VALIDITY   1

Production: keep the guards, drop the expensive parts.

 
#define LV_USE_CHECK_ARG            1
#define LV_CHECK_ARG_LOG_MODE       LV_CHECK_ARG_LOG_MODE_MINIMAL
#define LV_USE_CHECK_OBJ_CLASSTYPE  0
#define LV_USE_CHECK_OBJ_VALIDITY   0

Only turn LV_USE_CHECK_ARG off if flash size or the last few percent of performance genuinely require it.

Disabling argument checks is dangerous

With LV_USE_CHECK_ARG = 0 there is no NULL guard and no early return anywhere in the public API. Passing an invalid argument to any LVGL function becomes undefined behavior. Only disable it if you can guarantee that every call site in your application passes valid arguments.

Last updated on

On this page