From: Simon Glass <simon.glass@canonical.com>
Add a helper function that checks the 'expo_log_filter' environment
variable. If not set, all objects are logged. If set, it contains a
comma-separated list of filters; only objects whose name contains one
of the filter strings are logged.
The feature is controlled by CONFIG_EXPO_LOG_FILTER, which is enabled
by default for sandbox.
Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
---
boot/Kconfig | 10 ++++++++++
boot/scene.c | 32 ++++++++++++++++++++++++++++++++
boot/scene_internal.h | 12 ++++++++++++
doc/develop/expo.rst | 27 +++++++++++++++++++++++++++
test/boot/expo.c | 37 +++++++++++++++++++++++++++++++++++++
5 files changed, 118 insertions(+)
@@ -1002,6 +1002,16 @@ config EXPO_TEST
variable is set to 1. This is useful for debugging and performance
analysis.
+config EXPO_LOG_FILTER
+ bool "Enable expo log filter"
+ depends on EXPO
+ default y if SANDBOX
+ help
+ Enable the expo log filter. When enabled, the 'expo_log_filter'
+ environment variable can be set to filter log output by object name.
+ Only objects whose name contains the filter string are logged. This
+ is useful for debugging specific expo objects.
+
config BOOTMETH_SANDBOX
def_bool y
depends on SANDBOX
@@ -10,6 +10,7 @@
#include <alist.h>
#include <dm.h>
+#include <env.h>
#include <expo.h>
#include <malloc.h>
#include <mapmem.h>
@@ -42,6 +43,37 @@ static const char *const scene_obj_type_names[] = {
"textline",
};
+bool scene_chklog(const char *name)
+{
+ const char *filter, *end, *p;
+ int len;
+
+ if (!CONFIG_IS_ENABLED(EXPO_LOG_FILTER))
+ return true;
+
+ filter = env_get("expo_log_filter");
+ if (!filter)
+ return true;
+
+ /* Check each comma-separated filter */
+ while (*filter) {
+ end = strchrnul(filter, ',');
+ len = end - filter;
+
+ /* Check if this filter segment appears in name */
+ for (p = name; *p; p++) {
+ if (!strncmp(p, filter, len))
+ return true;
+ }
+
+ if (!*end)
+ break;
+ filter = end + 1;
+ }
+
+ return false;
+}
+
int scene_new(struct expo *exp, const char *name, uint id, struct scene **scnp)
{
struct scene *scn;
@@ -613,6 +613,18 @@ int scene_calc_arrange(struct scene *scn, struct expo_arrange_info *arr);
int scene_txt_generic_init(struct expo *exp, struct scene_txt_generic *gen,
const char *name, uint str_id, const char *str);
+/**
+ * scene_chklog() - Check if logging is enabled for an object
+ *
+ * This checks the 'expo_log_filter' environment variable. If not set, all
+ * objects are logged. If set, it contains a comma-separated list of filters;
+ * only objects whose name contains one of the filter strings are logged.
+ *
+ * @name: Object name to check
+ * Return: true if logging should happen, false to skip
+ */
+bool scene_chklog(const char *name);
+
/**
* scene_flag_name() - Get the name of a scene flag
*
@@ -605,6 +605,33 @@ These metrics help identify performance bottlenecks and verify that expo is
operating efficiently. The timing information is particularly useful when
optimizing display drivers or debugging slow rendering issues.
+Log filter
+~~~~~~~~~~
+
+Expo supports filtering log output by object name, which is useful when
+debugging specific objects. Set the ``expo_log_filter`` environment variable
+to a substring that matches the object names you want to log.
+
+To enable log filtering::
+
+ => setenv expo_log_filter texted
+ => log filter-add -d console -A -c expo -l debug
+
+This shows debug logs only for objects whose name contains "texted".
+
+Multiple filters can be specified as a comma-separated list::
+
+ => setenv expo_log_filter menu,text
+
+This logs objects matching either "menu" or "text".
+
+Remove the filter to see all objects::
+
+ => setenv expo_log_filter
+
+This feature requires ``CONFIG_EXPO_LOG_FILTER`` which is enabled by default
+for sandbox.
+
Writing expo tests
------------------
@@ -1260,6 +1260,43 @@ static int expo_scene_obj_type_name(struct unit_test_state *uts)
}
BOOTSTD_TEST(expo_scene_obj_type_name, 0);
+/* Test scene_chklog() */
+static int expo_scene_chklog(struct unit_test_state *uts)
+{
+ /* Without filter, all objects should be logged */
+ env_set("expo_log_filter", NULL);
+ ut_assert(scene_chklog("my-menu"));
+ ut_assert(scene_chklog("textline"));
+
+ /* With a single filter, only matching objects should be logged */
+ env_set("expo_log_filter", "menu");
+ ut_assert(scene_chklog("my-menu"));
+ ut_assert(scene_chklog("menu-item"));
+ ut_assert(!scene_chklog("textline"));
+ ut_assert(!scene_chklog("other"));
+
+ /* With comma-separated filters, any match should pass */
+ env_set("expo_log_filter", "menu,text");
+ ut_assert(scene_chklog("my-menu"));
+ ut_assert(scene_chklog("textline"));
+ ut_assert(scene_chklog("textedit"));
+ ut_assert(!scene_chklog("other"));
+ ut_assert(!scene_chklog("image"));
+
+ /* Test with three filters */
+ env_set("expo_log_filter", "menu,text,img");
+ ut_assert(scene_chklog("my-menu"));
+ ut_assert(scene_chklog("textline"));
+ ut_assert(scene_chklog("img-logo"));
+ ut_assert(!scene_chklog("other"));
+
+ /* Clear the filter */
+ env_set("expo_log_filter", NULL);
+
+ return 0;
+}
+BOOTSTD_TEST(expo_scene_chklog, 0);
+
/* Test scene_find_obj_within() */
static int expo_find_obj_within(struct unit_test_state *uts)
{