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:
parent
26e29c13f5
commit
265ae0f56a
Binary file not shown.
Before Width: | Height: | Size: 130 KiB After Width: | Height: | Size: 135 KiB |
@ -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
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
@ -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
|
||||
|
@ -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*/
|
||||
|
@ -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
|
||||
**********************/
|
||||
|
@ -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
|
||||
|
99
tests/src/test_cases/test_profiler.c
Normal file
99
tests/src/test_cases/test_profiler.c
Normal 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
|
Loading…
x
Reference in New Issue
Block a user