1 /**
2  * @file lv_profiler_builtin.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 
10 #include "lv_profiler_builtin_private.h"
11 #include "../lvgl.h"
12 #include "../core/lv_global.h"
13 
14 /*********************
15  *      DEFINES
16  *********************/
17 
18 #if LV_USE_PROFILER && LV_USE_PROFILER_BUILTIN
19 
20 #define profiler_ctx LV_GLOBAL_DEFAULT()->profiler_context
21 
22 #define LV_PROFILER_STR_MAX_LEN 128
23 #define LV_PROFILER_TICK_PER_SEC_MAX 1000000000 /* Maximum accuracy: 1 nanosecond */
24 
25 #if LV_USE_OS
26     #define LV_PROFILER_MULTEX_INIT   lv_mutex_init(&profiler_ctx->mutex)
27     #define LV_PROFILER_MULTEX_DEINIT lv_mutex_delete(&profiler_ctx->mutex)
28     #define LV_PROFILER_MULTEX_LOCK   lv_mutex_lock(&profiler_ctx->mutex)
29     #define LV_PROFILER_MULTEX_UNLOCK lv_mutex_unlock(&profiler_ctx->mutex)
30 #else
31     #define LV_PROFILER_MULTEX_INIT
32     #define LV_PROFILER_MULTEX_DEINIT
33     #define LV_PROFILER_MULTEX_LOCK
34     #define LV_PROFILER_MULTEX_UNLOCK
35 #endif
36 
37 /**********************
38  *      TYPEDEFS
39  **********************/
40 
41 /**
42  * @brief Structure representing a built-in profiler item in LVGL
43  */
44 typedef struct {
45     uint64_t tick;     /**< The tick value of the profiler item */
46     char tag;          /**< The tag of the profiler item */
47     const char * func; /**< A pointer to the function associated with the profiler item */
48 #if LV_USE_OS
49     int tid;           /**< The thread ID of the profiler item */
50     int cpu;         /**< The CPU ID of the profiler item */
51 #endif
52 } lv_profiler_builtin_item_t;
53 
54 /**
55  * @brief Structure representing a context for the LVGL built-in profiler
56  */
57 typedef struct _lv_profiler_builtin_ctx_t {
58     lv_profiler_builtin_item_t * item_arr; /**< Pointer to an array of profiler items */
59     uint32_t item_num;                     /**< Number of profiler items in the array */
60     uint32_t cur_index;                    /**< Index of the current profiler item */
61     lv_profiler_builtin_config_t config;   /**< Configuration for the built-in profiler */
62     bool enable;                           /**< Whether the built-in profiler is enabled */
63 #if LV_USE_OS
64     lv_mutex_t mutex;                      /**< Mutex to protect the built-in profiler */
65 #endif
66 } lv_profiler_builtin_ctx_t;
67 
68 /**********************
69  *  STATIC PROTOTYPES
70  **********************/
71 
72 static uint64_t default_tick_get_cb(void);
73 static void default_flush_cb(const char * buf);
74 static int default_tid_get_cb(void);
75 static int default_cpu_get_cb(void);
76 static void flush_no_lock(void);
77 
78 /**********************
79  *  STATIC VARIABLES
80  **********************/
81 
82 /**********************
83  *      MACROS
84  **********************/
85 
86 /**********************
87  *   GLOBAL FUNCTIONS
88  **********************/
89 
lv_profiler_builtin_config_init(lv_profiler_builtin_config_t * config)90 void lv_profiler_builtin_config_init(lv_profiler_builtin_config_t * config)
91 {
92     LV_ASSERT_NULL(config);
93     lv_memzero(config, sizeof(lv_profiler_builtin_config_t));
94     config->buf_size = LV_PROFILER_BUILTIN_BUF_SIZE;
95     config->tick_per_sec = 1000;
96     config->tick_get_cb = default_tick_get_cb;
97     config->flush_cb = default_flush_cb;
98     config->tid_get_cb = default_tid_get_cb;
99     config->cpu_get_cb = default_cpu_get_cb;
100 }
101 
lv_profiler_builtin_init(const lv_profiler_builtin_config_t * config)102 void lv_profiler_builtin_init(const lv_profiler_builtin_config_t * config)
103 {
104     LV_ASSERT_NULL(config);
105     LV_ASSERT_NULL(config->tick_get_cb);
106 
107     uint32_t num = config->buf_size / sizeof(lv_profiler_builtin_item_t);
108     if(num == 0) {
109         LV_LOG_WARN("buf_size must > %d", (int)sizeof(lv_profiler_builtin_item_t));
110         return;
111     }
112 
113     if(config->tick_per_sec == 0 || config->tick_per_sec > LV_PROFILER_TICK_PER_SEC_MAX) {
114         LV_LOG_WARN("tick_per_sec range must be between 1~%d", LV_PROFILER_TICK_PER_SEC_MAX);
115         return;
116     }
117 
118     /*Free the old item_arr memory*/
119     if(profiler_ctx) {
120         lv_profiler_builtin_uninit();
121     }
122 
123     profiler_ctx = lv_malloc_zeroed(sizeof(lv_profiler_builtin_ctx_t));
124     LV_ASSERT_MALLOC(profiler_ctx);
125 
126     profiler_ctx->item_arr = lv_malloc(num * sizeof(lv_profiler_builtin_item_t));
127     LV_ASSERT_MALLOC(profiler_ctx->item_arr);
128     if(profiler_ctx->item_arr == NULL) {
129         lv_free(profiler_ctx);
130         profiler_ctx = NULL;
131         LV_LOG_ERROR("malloc failed for item_arr");
132         return;
133     }
134 
135     LV_PROFILER_MULTEX_INIT;
136     profiler_ctx->item_num = num;
137     profiler_ctx->config = *config;
138 
139     if(profiler_ctx->config.flush_cb) {
140         /* add profiler header for perfetto */
141         profiler_ctx->config.flush_cb("# tracer: nop\n");
142         profiler_ctx->config.flush_cb("#\n");
143     }
144 
145     lv_profiler_builtin_set_enable(true);
146 
147     LV_LOG_INFO("init OK, item_num = %d", (int)num);
148 }
149 
lv_profiler_builtin_uninit(void)150 void lv_profiler_builtin_uninit(void)
151 {
152     LV_ASSERT_NULL(profiler_ctx);
153     LV_PROFILER_MULTEX_DEINIT;
154     lv_free(profiler_ctx->item_arr);
155     lv_free(profiler_ctx);
156     profiler_ctx = NULL;
157 }
158 
lv_profiler_builtin_set_enable(bool enable)159 void lv_profiler_builtin_set_enable(bool enable)
160 {
161     if(!profiler_ctx) {
162         return;
163     }
164 
165     profiler_ctx->enable = enable;
166 }
167 
lv_profiler_builtin_flush(void)168 void lv_profiler_builtin_flush(void)
169 {
170     LV_ASSERT_NULL(profiler_ctx);
171 
172     LV_PROFILER_MULTEX_LOCK;
173     flush_no_lock();
174     LV_PROFILER_MULTEX_UNLOCK;
175 }
176 
lv_profiler_builtin_write(const char * func,char tag)177 void lv_profiler_builtin_write(const char * func, char tag)
178 {
179     LV_ASSERT_NULL(profiler_ctx);
180     LV_ASSERT_NULL(func);
181 
182     if(!profiler_ctx->enable) {
183         return;
184     }
185 
186     LV_PROFILER_MULTEX_LOCK;
187 
188     if(profiler_ctx->cur_index >= profiler_ctx->item_num) {
189         flush_no_lock();
190         profiler_ctx->cur_index = 0;
191     }
192 
193     lv_profiler_builtin_item_t * item = &profiler_ctx->item_arr[profiler_ctx->cur_index];
194     item->func = func;
195     item->tag = tag;
196     item->tick = profiler_ctx->config.tick_get_cb();
197 
198 #if LV_USE_OS
199     item->tid = profiler_ctx->config.tid_get_cb();
200     item->cpu = profiler_ctx->config.cpu_get_cb();
201 #endif
202 
203     profiler_ctx->cur_index++;
204 
205     LV_PROFILER_MULTEX_UNLOCK;
206 }
207 
208 /**********************
209  *   STATIC FUNCTIONS
210  **********************/
211 
default_tick_get_cb(void)212 static uint64_t default_tick_get_cb(void)
213 {
214     return lv_tick_get();
215 }
216 
default_flush_cb(const char * buf)217 static void default_flush_cb(const char * buf)
218 {
219     LV_LOG("%s", buf);
220 }
221 
default_tid_get_cb(void)222 static int default_tid_get_cb(void)
223 {
224     return 1;
225 }
226 
default_cpu_get_cb(void)227 static int default_cpu_get_cb(void)
228 {
229     return 0;
230 }
231 
flush_no_lock(void)232 static void flush_no_lock(void)
233 {
234     if(!profiler_ctx->config.flush_cb) {
235         LV_LOG_WARN("flush_cb is not registered");
236         return;
237     }
238 
239     uint32_t cur = 0;
240     char buf[LV_PROFILER_STR_MAX_LEN];
241     uint32_t tick_per_sec = profiler_ctx->config.tick_per_sec;
242     while(cur < profiler_ctx->cur_index) {
243         lv_profiler_builtin_item_t * item = &profiler_ctx->item_arr[cur++];
244         uint32_t sec = item->tick / tick_per_sec;
245         uint32_t nsec = (item->tick % tick_per_sec) * (LV_PROFILER_TICK_PER_SEC_MAX / tick_per_sec);
246 
247 #if LV_USE_OS
248         lv_snprintf(buf, sizeof(buf),
249                     "   LVGL-%d [%d] %" LV_PRIu32 ".%09" LV_PRIu32 ": tracing_mark_write: %c|1|%s\n",
250                     item->tid,
251                     item->cpu,
252                     sec,
253                     nsec,
254                     item->tag,
255                     item->func);
256 #else
257         lv_snprintf(buf, sizeof(buf),
258                     "   LVGL-1 [0] %" LV_PRIu32 ".%09" LV_PRIu32 ": tracing_mark_write: %c|1|%s\n",
259                     sec,
260                     nsec,
261                     item->tag,
262                     item->func);
263 #endif
264         profiler_ctx->config.flush_cb(buf);
265     }
266 }
267 
268 #endif /*LV_USE_PROFILER_BUILTIN*/
269