1
0
mirror of https://github.com/lvgl/lvgl.git synced 2025-01-28 07:03:00 +08:00

feat(profiler): add multithreading support and testcase (#5490)

Signed-off-by: FASTSHIFT <vifextech@foxmail.com>
Co-authored-by: pengyiqiang <pengyiqiang@xiaomi.com>
This commit is contained in:
_VIFEXTech 2024-01-29 14:09:33 +08:00 committed by GitHub
parent 26e29c13f5
commit 265ae0f56a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 259 additions and 66 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 KiB

After

Width:  |  Height:  |  Size: 135 KiB

View File

@ -42,6 +42,8 @@ To enable the profiler, set :c:macro:`LV_USE_PROFILER` in ``lv_conf.h`` and conf
.. code:: c
#include <sys/syscall.h>
#include <sys/types.h>
#include <time.h>
static uint32_t my_get_tick_us_cb(void)
@ -51,12 +53,24 @@ To enable the profiler, set :c:macro:`LV_USE_PROFILER` in ``lv_conf.h`` and conf
return ts.tv_sec * 1000000 + ts.tv_nsec / 1000;
}
static int my_get_tid_cb(void)
{
return (int)syscall(SYS_gettid);
}
static int my_get_cpu_cb(void)
{
return (int)syscall(SYS_getcpu);
}
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_us_cb;
config.tid_get_cb = my_get_tid_cb;
config.cpu_get_cb = my_get_cpu_cb;
lv_profiler_builtin_init(&config);
}
@ -202,8 +216,7 @@ Please check the completeness of the logs. If the logs are incomplete, it may be
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. Cross-thread calling of :c:macro:`LV_PROFILER_BEGIN/END`.The built-in LVGL profiler is designed for single-threaded use, so calling it from multiple threads can lead to thread safety issues. If you need to use it in a multi-threaded environment, you can use profiler interfaces provided by your operating system that ensure thread safety.
4. Make sure that the string passed in by c:macro:`LV_PROFILER_BEGIN_TAG/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.
3. Make sure that the string passed in by :c:macro:`LV_PROFILER_BEGIN_TAG/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
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -27,7 +27,6 @@ extern "C" {
#include "../misc/lv_color_op.h"
#include "../misc/lv_ll.h"
#include "../misc/lv_log.h"
#include "../misc/lv_profiler_builtin.h"
#include "../misc/lv_style.h"
#include "../misc/lv_timer.h"
#include "../others/sysmon/lv_sysmon.h"
@ -57,6 +56,10 @@ struct _snippet_stack;
struct _lv_freetype_context_t;
#endif
#if LV_USE_PROFILER && LV_USE_PROFILER_BUILTIN
struct _lv_profiler_builtin_ctx_t;
#endif
typedef struct _lv_global_t {
bool inited;
bool deinit_in_progress; /**< Can be used e.g. in the LV_EVENT_DELETE to deinit the drivers too */
@ -170,7 +173,7 @@ typedef struct _lv_global_t {
#endif
#if LV_USE_PROFILER && LV_USE_PROFILER_BUILTIN
lv_profiler_builtin_ctx_t profiler_context;
struct _lv_profiler_builtin_ctx_t * profiler_context;
#endif
#if LV_USE_FILE_EXPLORER != 0

View File

@ -22,15 +22,57 @@
#define LV_PROFILER_STR_MAX_LEN 128
#define LV_PROFILER_TICK_PER_SEC_MAX 1000000
#if LV_USE_OS
#define LV_PROFILER_MULTEX_INIT lv_mutex_init(&profiler_ctx->mutex)
#define LV_PROFILER_MULTEX_DEINIT lv_mutex_delete(&profiler_ctx->mutex)
#define LV_PROFILER_MULTEX_LOCK lv_mutex_lock(&profiler_ctx->mutex)
#define LV_PROFILER_MULTEX_UNLOCK lv_mutex_unlock(&profiler_ctx->mutex)
#else
#define LV_PROFILER_MULTEX_INIT
#define LV_PROFILER_MULTEX_DEINIT
#define LV_PROFILER_MULTEX_LOCK
#define LV_PROFILER_MULTEX_UNLOCK
#endif
/**********************
* TYPEDEFS
**********************/
/**
* @brief Structure representing a built-in profiler item in LVGL
*/
typedef struct {
char tag; /**< The tag of the profiler item */
uint32_t tick; /**< The tick value of the profiler item */
const char * func; /**< A pointer to the function associated with the profiler item */
#if LV_USE_OS
int tid; /**< The thread ID of the profiler item */
int cpu; /**< The CPU ID of the profiler item */
#endif
} lv_profiler_builtin_item_t;
/**
* @brief Structure representing a context for the LVGL built-in profiler
*/
typedef struct _lv_profiler_builtin_ctx_t {
lv_profiler_builtin_item_t * item_arr; /**< Pointer to an array of profiler items */
uint32_t item_num; /**< Number of profiler items in the array */
uint32_t cur_index; /**< Index of the current profiler item */
lv_profiler_builtin_config_t config; /**< Configuration for the built-in profiler */
bool enable; /**< Whether the built-in profiler is enabled */
#if LV_USE_OS
lv_mutex_t mutex; /**< Mutex to protect the built-in profiler */
#endif
} lv_profiler_builtin_ctx_t;
/**********************
* STATIC PROTOTYPES
**********************/
static void default_flush_cb(const char * buf);
static int default_tid_get_cb(void);
static int default_cpu_get_cb(void);
static void flush_no_lock(void);
/**********************
* STATIC VARIABLES
@ -52,6 +94,8 @@ void lv_profiler_builtin_config_init(lv_profiler_builtin_config_t * config)
config->tick_per_sec = 1000;
config->tick_get_cb = lv_tick_get;
config->flush_cb = default_flush_cb;
config->tid_get_cb = default_tid_get_cb;
config->cpu_get_cb = default_cpu_get_cb;
}
void lv_profiler_builtin_init(const lv_profiler_builtin_config_t * config)
@ -71,26 +115,30 @@ void lv_profiler_builtin_init(const lv_profiler_builtin_config_t * config)
}
/*Free the old item_arr memory*/
if(profiler_ctx.item_arr != NULL) {
if(profiler_ctx) {
lv_profiler_builtin_uninit();
}
lv_memzero(&profiler_ctx, sizeof(profiler_ctx));
profiler_ctx.item_arr = lv_malloc(num * sizeof(lv_profiler_builtin_item_t));
LV_ASSERT_MALLOC(profiler_ctx.item_arr);
profiler_ctx = lv_malloc_zeroed(sizeof(lv_profiler_builtin_ctx_t));
LV_ASSERT_MALLOC(profiler_ctx);
if(profiler_ctx.item_arr == NULL) {
profiler_ctx->item_arr = lv_malloc(num * sizeof(lv_profiler_builtin_item_t));
LV_ASSERT_MALLOC(profiler_ctx->item_arr);
if(profiler_ctx->item_arr == NULL) {
lv_free(profiler_ctx);
profiler_ctx = NULL;
LV_LOG_ERROR("malloc failed for item_arr");
return;
}
profiler_ctx.item_num = num;
profiler_ctx.config = *config;
LV_PROFILER_MULTEX_INIT;
profiler_ctx->item_num = num;
profiler_ctx->config = *config;
if(profiler_ctx.config.flush_cb) {
if(profiler_ctx->config.flush_cb) {
/* add profiler header for perfetto */
profiler_ctx.config.flush_cb("# tracer: nop\n");
profiler_ctx.config.flush_cb("#\n");
profiler_ctx->config.flush_cb("# tracer: nop\n");
profiler_ctx->config.flush_cb("#\n");
}
lv_profiler_builtin_set_enable(true);
@ -100,60 +148,60 @@ void lv_profiler_builtin_init(const lv_profiler_builtin_config_t * config)
void lv_profiler_builtin_uninit(void)
{
LV_ASSERT_NULL(profiler_ctx.item_arr);
lv_free(profiler_ctx.item_arr);
lv_memzero(&profiler_ctx, sizeof(profiler_ctx));
LV_ASSERT_NULL(profiler_ctx);
LV_PROFILER_MULTEX_DEINIT;
lv_free(profiler_ctx->item_arr);
lv_free(profiler_ctx);
profiler_ctx = NULL;
}
void lv_profiler_builtin_set_enable(bool enable)
{
profiler_ctx.enable = enable;
if(!profiler_ctx) {
return;
}
profiler_ctx->enable = enable;
}
void lv_profiler_builtin_flush(void)
{
LV_ASSERT_NULL(profiler_ctx.item_arr);
if(!profiler_ctx.config.flush_cb) {
LV_LOG_WARN("flush_cb is not registered");
return;
}
LV_ASSERT_NULL(profiler_ctx);
uint32_t cur = 0;
char buf[LV_PROFILER_STR_MAX_LEN];
uint32_t tick_per_sec = profiler_ctx.config.tick_per_sec;
while(cur < profiler_ctx.cur_index) {
lv_profiler_builtin_item_t * item = &profiler_ctx.item_arr[cur++];
uint32_t sec = item->tick / tick_per_sec;
uint32_t usec = (item->tick % tick_per_sec) * (LV_PROFILER_TICK_PER_SEC_MAX / tick_per_sec);
lv_snprintf(buf, sizeof(buf),
" LVGL-1 [0] %" LV_PRIu32 ".%06" LV_PRIu32 ": tracing_mark_write: %c|1|%s\n",
sec,
usec,
item->tag,
item->func);
profiler_ctx.config.flush_cb(buf);
}
LV_PROFILER_MULTEX_LOCK;
flush_no_lock();
LV_PROFILER_MULTEX_UNLOCK;
}
void lv_profiler_builtin_write(const char * func, char tag)
{
LV_ASSERT_NULL(profiler_ctx.item_arr);
LV_ASSERT_NULL(profiler_ctx);
LV_ASSERT_NULL(func);
if(!profiler_ctx.enable) {
if(!profiler_ctx->enable) {
return;
}
if(profiler_ctx.cur_index >= profiler_ctx.item_num) {
lv_profiler_builtin_flush();
profiler_ctx.cur_index = 0;
LV_PROFILER_MULTEX_LOCK;
if(profiler_ctx->cur_index >= profiler_ctx->item_num) {
flush_no_lock();
profiler_ctx->cur_index = 0;
}
lv_profiler_builtin_item_t * item = &profiler_ctx.item_arr[profiler_ctx.cur_index];
lv_profiler_builtin_item_t * item = &profiler_ctx->item_arr[profiler_ctx->cur_index];
item->func = func;
item->tag = tag;
item->tick = profiler_ctx.config.tick_get_cb();
profiler_ctx.cur_index++;
item->tick = profiler_ctx->config.tick_get_cb();
#if LV_USE_OS
item->tid = profiler_ctx->config.tid_get_cb();
item->cpu = profiler_ctx->config.cpu_get_cb();
#endif
profiler_ctx->cur_index++;
LV_PROFILER_MULTEX_UNLOCK;
}
/**********************
@ -165,4 +213,50 @@ static void default_flush_cb(const char * buf)
LV_LOG("%s", buf);
}
static int default_tid_get_cb(void)
{
return 1;
}
static int default_cpu_get_cb(void)
{
return 0;
}
static void flush_no_lock(void)
{
if(!profiler_ctx->config.flush_cb) {
LV_LOG_WARN("flush_cb is not registered");
return;
}
uint32_t cur = 0;
char buf[LV_PROFILER_STR_MAX_LEN];
uint32_t tick_per_sec = profiler_ctx->config.tick_per_sec;
while(cur < profiler_ctx->cur_index) {
lv_profiler_builtin_item_t * item = &profiler_ctx->item_arr[cur++];
uint32_t sec = item->tick / tick_per_sec;
uint32_t usec = (item->tick % tick_per_sec) * (LV_PROFILER_TICK_PER_SEC_MAX / tick_per_sec);
#if LV_USE_OS
lv_snprintf(buf, sizeof(buf),
" LVGL-%d [%d] %" LV_PRIu32 ".%06" LV_PRIu32 ": tracing_mark_write: %c|1|%s\n",
item->tid,
item->cpu,
sec,
usec,
item->tag,
item->func);
#else
lv_snprintf(buf, sizeof(buf),
" LVGL-1 [0] %" LV_PRIu32 ".%06" LV_PRIu32 ": tracing_mark_write: %c|1|%s\n",
sec,
usec,
item->tag,
item->func);
#endif
profiler_ctx->config.flush_cb(buf);
}
}
#endif /*LV_USE_PROFILER_BUILTIN*/

View File

@ -43,28 +43,10 @@ typedef struct {
uint32_t tick_per_sec; /**< The number of ticks per second */
uint32_t (*tick_get_cb)(void); /**< Callback function to get the current tick count */
void (*flush_cb)(const char * buf); /**< Callback function to flush the profiling data */
int (*tid_get_cb)(void); /**< Callback function to get the current thread ID */
int (*cpu_get_cb)(void); /**< Callback function to get the current CPU */
} lv_profiler_builtin_config_t;
/**
* @brief Structure representing a built-in profiler item in LVGL
*/
typedef struct {
char tag; /**< The tag of the profiler item */
uint32_t tick; /**< The tick value of the profiler item */
const char * func; /**< A pointer to the function associated with the profiler item */
} lv_profiler_builtin_item_t;
/**
* @brief Structure representing a context for the LVGL built-in profiler
*/
typedef struct {
lv_profiler_builtin_item_t * item_arr; /**< Pointer to an array of profiler items */
uint32_t item_num; /**< Number of profiler items in the array */
uint32_t cur_index; /**< Index of the current profiler item */
lv_profiler_builtin_config_t config; /**< Configuration for the built-in profiler */
bool enable; /**< Whether the built-in profiler is enabled */
} lv_profiler_builtin_ctx_t;
/**********************
* GLOBAL PROTOTYPES
**********************/

View File

@ -79,6 +79,8 @@
#define LV_USE_LZ4 1
#define LV_USE_LZ4_INTERNAL 1
#define LV_USE_VECTOR_GRAPHIC 1
#define LV_USE_PROFILER 1
#define LV_PROFILER_INCLUDE "lv_profiler_builtin.h"
#define LV_BUILD_EXAMPLES 1
#define LV_USE_DEMO_WIDGETS 1

View File

@ -0,0 +1,99 @@
#if LV_BUILD_TEST
#include "../lvgl.h"
#include "unity/unity.h"
#define OUTPUT_LINE_MAX 8
#define OUTPUT_BUF_MAX 128
static uint32_t profiler_tick = 0;
static int output_line = 0;
static char output_buf[OUTPUT_LINE_MAX][OUTPUT_BUF_MAX];
static uint32_t get_tick_cb(void)
{
return profiler_tick++;
}
static void flush_cb(const char * buf)
{
TEST_ASSERT_LESS_THAN(OUTPUT_LINE_MAX, output_line);
TEST_ASSERT_LESS_THAN(OUTPUT_BUF_MAX, strlen(buf));
lv_strcpy(output_buf[output_line], buf);
output_line++;
}
void setUp(void)
{
lv_profiler_builtin_config_t config;
lv_profiler_builtin_config_init(&config);
config.buf_size = 1024;
config.tick_per_sec = 1; /* One second is equal to 1000000 microseconds */
config.tick_get_cb = get_tick_cb;
config.flush_cb = flush_cb;
lv_profiler_builtin_init(&config);
}
void tearDown(void)
{
lv_profiler_builtin_uninit();
}
void test_profiler_normal(void)
{
/* enable profier */
lv_profiler_builtin_set_enable(true);
/* reset */
profiler_tick = 0;
output_line = 0;
lv_memzero(output_buf, sizeof(output_buf));
/* test profiler */
LV_PROFILER_BEGIN;
LV_PROFILER_END;
LV_PROFILER_BEGIN_TAG("custom_tag");
LV_PROFILER_END_TAG("custom_tag");
/* flush output */
lv_profiler_builtin_flush();
/* check output */
TEST_ASSERT_EQUAL_INT(output_line, 4);
TEST_ASSERT_EQUAL_INT(profiler_tick, 4);
TEST_ASSERT_EQUAL_STRING(output_buf[0], " LVGL-1 [0] 0.000000: tracing_mark_write: B|1|test_profiler_normal\n");
TEST_ASSERT_EQUAL_STRING(output_buf[1], " LVGL-1 [0] 1.000000: tracing_mark_write: E|1|test_profiler_normal\n");
TEST_ASSERT_EQUAL_STRING(output_buf[2], " LVGL-1 [0] 2.000000: tracing_mark_write: B|1|custom_tag\n");
TEST_ASSERT_EQUAL_STRING(output_buf[3], " LVGL-1 [0] 3.000000: tracing_mark_write: E|1|custom_tag\n");
}
void test_profiler_disable(void)
{
/* disable profier */
lv_profiler_builtin_set_enable(false);
/* reset */
profiler_tick = 0;
output_line = 0;
lv_memzero(output_buf, sizeof(output_buf));
/* test profiler */
LV_PROFILER_BEGIN;
LV_PROFILER_END;
LV_PROFILER_BEGIN_TAG("custom_tag");
LV_PROFILER_END_TAG("custom_tag");
/* flush output */
lv_profiler_builtin_flush();
/* check output */
TEST_ASSERT_EQUAL_INT(output_line, 0);
TEST_ASSERT_EQUAL_INT(profiler_tick, 0);
TEST_ASSERT_EQUAL_CHAR(output_buf[1][0], '\0');
TEST_ASSERT_EQUAL_CHAR(output_buf[2][0], '\0');
TEST_ASSERT_EQUAL_CHAR(output_buf[3][0], '\0');
TEST_ASSERT_EQUAL_CHAR(output_buf[4][0], '\0');
}
#endif