1 /**
2 * @file lv_sysmon.c
3 *
4 */
5
6 /*********************
7 * INCLUDES
8 *********************/
9
10 #include "lv_sysmon_private.h"
11 #include "../../misc/lv_timer_private.h"
12
13 #if LV_USE_SYSMON
14
15 #include "../../core/lv_global.h"
16 #include "../../misc/lv_async.h"
17 #include "../../stdlib/lv_string.h"
18 #include "../../widgets/label/lv_label.h"
19 #include "../../display/lv_display_private.h"
20
21 /*********************
22 * DEFINES
23 *********************/
24 #ifndef LV_SYSMON_REFR_PERIOD_DEF
25 #define LV_SYSMON_REFR_PERIOD_DEF 300 /* ms */
26 #endif
27
28 #if LV_USE_MEM_MONITOR
29 #define sysmon_mem LV_GLOBAL_DEFAULT()->sysmon_mem
30 #endif
31
32 /**********************
33 * TYPEDEFS
34 **********************/
35
36 /**********************
37 * STATIC PROTOTYPES
38 **********************/
39
40 #if LV_USE_PERF_MONITOR
41 static void perf_update_timer_cb(lv_timer_t * t);
42 static void perf_observer_cb(lv_observer_t * observer, lv_subject_t * subject);
43 static void perf_monitor_disp_event_cb(lv_event_t * e);
44 #endif
45
46 #if LV_USE_MEM_MONITOR
47 static void mem_update_timer_cb(lv_timer_t * t);
48 static void mem_observer_cb(lv_observer_t * observer, lv_subject_t * subject);
49 #endif
50
51 /**********************
52 * STATIC VARIABLES
53 **********************/
54
55 /**********************
56 * MACROS
57 **********************/
58
59 /**********************
60 * GLOBAL FUNCTIONS
61 **********************/
62
lv_sysmon_builtin_init(void)63 void lv_sysmon_builtin_init(void)
64 {
65
66 #if LV_USE_MEM_MONITOR
67 static lv_mem_monitor_t mem_info;
68 lv_subject_init_pointer(&sysmon_mem.subject, &mem_info);
69 sysmon_mem.timer = lv_timer_create(mem_update_timer_cb, LV_SYSMON_REFR_PERIOD_DEF, &mem_info);
70 #endif
71 }
72
lv_sysmon_builtin_deinit(void)73 void lv_sysmon_builtin_deinit(void)
74 {
75 #if LV_USE_MEM_MONITOR
76 lv_timer_delete(sysmon_mem.timer);
77 #endif
78 }
79
lv_sysmon_create(lv_display_t * disp)80 lv_obj_t * lv_sysmon_create(lv_display_t * disp)
81 {
82 LV_LOG_INFO("begin");
83 if(disp == NULL) disp = lv_display_get_default();
84 if(disp == NULL) {
85 LV_LOG_WARN("There is no default display");
86 return NULL;
87 }
88
89 lv_obj_t * label = lv_label_create(lv_display_get_layer_sys(disp));
90 lv_obj_set_style_bg_opa(label, LV_OPA_50, 0);
91 lv_obj_set_style_bg_color(label, lv_color_black(), 0);
92 lv_obj_set_style_text_color(label, lv_color_white(), 0);
93 lv_obj_set_style_pad_all(label, 3, 0);
94 lv_label_set_text(label, "?");
95 return label;
96 }
97
98 #if LV_USE_PERF_MONITOR
99
lv_sysmon_show_performance(lv_display_t * disp)100 void lv_sysmon_show_performance(lv_display_t * disp)
101 {
102 if(disp == NULL) disp = lv_display_get_default();
103 if(disp == NULL) {
104 LV_LOG_WARN("There is no default display");
105 return;
106 }
107
108 if(disp->perf_label == NULL) {
109 disp->perf_label = lv_sysmon_create(disp);
110 if(disp->perf_label == NULL) {
111 LV_LOG_WARN("Couldn't create sysmon");
112 return;
113 }
114
115 lv_subject_init_pointer(&disp->perf_sysmon_backend.subject, &disp->perf_sysmon_info);
116 lv_obj_align(disp->perf_label, LV_USE_PERF_MONITOR_POS, 0, 0);
117 lv_subject_add_observer_obj(&disp->perf_sysmon_backend.subject, perf_observer_cb, disp->perf_label, NULL);
118 disp->perf_sysmon_backend.timer = lv_timer_create(perf_update_timer_cb, LV_SYSMON_REFR_PERIOD_DEF, disp);
119 lv_display_add_event_cb(disp, perf_monitor_disp_event_cb, LV_EVENT_ALL, NULL);
120 }
121
122 #if LV_USE_PERF_MONITOR_LOG_MODE
123 lv_obj_add_flag(disp->perf_label, LV_OBJ_FLAG_HIDDEN);
124 #else
125 lv_obj_remove_flag(disp->perf_label, LV_OBJ_FLAG_HIDDEN);
126 #endif
127 }
128
lv_sysmon_hide_performance(lv_display_t * disp)129 void lv_sysmon_hide_performance(lv_display_t * disp)
130 {
131 if(disp == NULL) disp = lv_display_get_default();
132 if(disp == NULL) {
133 LV_LOG_WARN("There is no default display");
134 return;
135 }
136
137 lv_obj_add_flag(disp->perf_label, LV_OBJ_FLAG_HIDDEN);
138 }
139
140 #endif
141
142 #if LV_USE_MEM_MONITOR
143
lv_sysmon_show_memory(lv_display_t * disp)144 void lv_sysmon_show_memory(lv_display_t * disp)
145 {
146 if(disp == NULL) disp = lv_display_get_default();
147 if(disp == NULL) {
148 LV_LOG_WARN("There is no default display");
149 return;
150 }
151
152 if(disp->mem_label == NULL) {
153 disp->mem_label = lv_sysmon_create(disp);
154 if(disp->mem_label == NULL) {
155 LV_LOG_WARN("Couldn't create sysmon");
156 return;
157 }
158
159 lv_obj_align(disp->mem_label, LV_USE_MEM_MONITOR_POS, 0, 0);
160 lv_subject_add_observer_obj(&sysmon_mem.subject, mem_observer_cb, disp->mem_label, NULL);
161 }
162
163 lv_obj_remove_flag(disp->mem_label, LV_OBJ_FLAG_HIDDEN);
164 }
165
lv_sysmon_hide_memory(lv_display_t * disp)166 void lv_sysmon_hide_memory(lv_display_t * disp)
167 {
168 if(disp == NULL) disp = lv_display_get_default();
169 if(disp == NULL) {
170 LV_LOG_WARN("There is no default display");
171 return;
172 }
173
174 lv_obj_add_flag(disp->mem_label, LV_OBJ_FLAG_HIDDEN);
175 }
176
177 #endif
178
179 /**********************
180 * STATIC FUNCTIONS
181 **********************/
182
183 #if LV_USE_PERF_MONITOR
184
perf_monitor_disp_event_cb(lv_event_t * e)185 static void perf_monitor_disp_event_cb(lv_event_t * e)
186 {
187 lv_display_t * disp = lv_event_get_target(e);
188 lv_event_code_t code = lv_event_get_code(e);
189 lv_sysmon_perf_info_t * info = &disp->perf_sysmon_info;
190
191 switch(code) {
192 case LV_EVENT_REFR_START:
193 info->measured.refr_interval_sum += lv_tick_elaps(info->measured.refr_start);
194 info->measured.refr_start = lv_tick_get();
195 break;
196 case LV_EVENT_REFR_READY:
197 info->measured.refr_elaps_sum += lv_tick_elaps(info->measured.refr_start);
198 info->measured.refr_cnt++;
199 break;
200 case LV_EVENT_RENDER_START:
201 info->measured.render_in_progress = 1;
202 info->measured.render_start = lv_tick_get();
203 break;
204 case LV_EVENT_RENDER_READY:
205 info->measured.render_in_progress = 0;
206 info->measured.render_elaps_sum += lv_tick_elaps(info->measured.render_start);
207 info->measured.render_cnt++;
208 break;
209 case LV_EVENT_FLUSH_START:
210 case LV_EVENT_FLUSH_WAIT_START:
211 if(info->measured.render_in_progress) {
212 info->measured.flush_in_render_start = lv_tick_get();
213 }
214 else {
215 info->measured.flush_not_in_render_start = lv_tick_get();
216 }
217 break;
218 case LV_EVENT_FLUSH_FINISH:
219 case LV_EVENT_FLUSH_WAIT_FINISH:
220 if(info->measured.render_in_progress) {
221 info->measured.flush_in_render_elaps_sum += lv_tick_elaps(info->measured.flush_in_render_start);
222 }
223 else {
224 info->measured.flush_not_in_render_elaps_sum += lv_tick_elaps(info->measured.flush_not_in_render_start);
225 }
226 break;
227 case LV_EVENT_DELETE:
228 lv_timer_delete(disp->perf_sysmon_backend.timer);
229 lv_subject_deinit(&disp->perf_sysmon_backend.subject);
230 break;
231 default:
232 break;
233 }
234 }
235
perf_update_timer_cb(lv_timer_t * t)236 static void perf_update_timer_cb(lv_timer_t * t)
237 {
238 lv_display_t * disp = lv_timer_get_user_data(t);
239
240 uint32_t LV_SYSMON_GET_IDLE(void);
241
242 lv_sysmon_perf_info_t * info = &disp->perf_sysmon_info;
243 info->calculated.run_cnt++;
244
245 uint32_t time_since_last_report = lv_tick_elaps(info->measured.last_report_timestamp);
246 lv_timer_t * disp_refr_timer = lv_display_get_refr_timer(NULL);
247 uint32_t disp_refr_period = disp_refr_timer ? disp_refr_timer->period : LV_DEF_REFR_PERIOD;
248
249 info->calculated.fps = info->measured.refr_interval_sum ? (1000 * info->measured.refr_cnt / time_since_last_report) : 0;
250 info->calculated.fps = LV_MIN(info->calculated.fps,
251 1000 / disp_refr_period); /*Limit due to possible off-by-one error*/
252
253 info->calculated.cpu = 100 - LV_SYSMON_GET_IDLE();
254 info->calculated.refr_avg_time = info->measured.refr_cnt ? (info->measured.refr_elaps_sum / info->measured.refr_cnt) :
255 0;
256
257 info->calculated.flush_avg_time = info->measured.render_cnt ?
258 ((info->measured.flush_in_render_elaps_sum + info->measured.flush_not_in_render_elaps_sum)
259 / info->measured.render_cnt) : 0;
260 /*Flush time was measured in rendering time so subtract it*/
261 info->calculated.render_avg_time = info->measured.render_cnt ? ((info->measured.render_elaps_sum -
262 info->measured.flush_in_render_elaps_sum) /
263 info->measured.render_cnt) : 0;
264
265 info->calculated.cpu_avg_total = ((info->calculated.cpu_avg_total * (info->calculated.run_cnt - 1)) +
266 info->calculated.cpu) / info->calculated.run_cnt;
267 info->calculated.fps_avg_total = ((info->calculated.fps_avg_total * (info->calculated.run_cnt - 1)) +
268 info->calculated.fps) / info->calculated.run_cnt;
269
270 lv_subject_set_pointer(&disp->perf_sysmon_backend.subject, info);
271
272 lv_sysmon_perf_info_t prev_info = *info;
273 lv_memzero(info, sizeof(lv_sysmon_perf_info_t));
274 info->measured.refr_start = prev_info.measured.refr_start;
275 info->calculated.cpu_avg_total = prev_info.calculated.cpu_avg_total;
276 info->calculated.fps_avg_total = prev_info.calculated.fps_avg_total;
277 info->calculated.run_cnt = prev_info.calculated.run_cnt;
278
279 info->measured.last_report_timestamp = lv_tick_get();
280 }
281
perf_observer_cb(lv_observer_t * observer,lv_subject_t * subject)282 static void perf_observer_cb(lv_observer_t * observer, lv_subject_t * subject)
283 {
284 lv_obj_t * label = lv_observer_get_target(observer);
285 const lv_sysmon_perf_info_t * perf = lv_subject_get_pointer(subject);
286
287 #if LV_USE_PERF_MONITOR_LOG_MODE
288 LV_UNUSED(label);
289 LV_LOG("sysmon: "
290 "%" LV_PRIu32 " FPS (refr_cnt: %" LV_PRIu32 " | redraw_cnt: %" LV_PRIu32"), "
291 "refr %" LV_PRIu32 "ms (render %" LV_PRIu32 "ms | flush %" LV_PRIu32 "ms), "
292 "CPU %" LV_PRIu32 "%%\n",
293 perf->calculated.fps, perf->measured.refr_cnt, perf->measured.render_cnt,
294 perf->calculated.refr_avg_time, perf->calculated.render_avg_time, perf->calculated.flush_avg_time,
295 perf->calculated.cpu);
296 #else
297 lv_label_set_text_fmt(
298 label,
299 "%" LV_PRIu32" FPS, %" LV_PRIu32 "%% CPU\n"
300 "%" LV_PRIu32" ms (%" LV_PRIu32" | %" LV_PRIu32")",
301 perf->calculated.fps, perf->calculated.cpu,
302 perf->calculated.render_avg_time + perf->calculated.flush_avg_time,
303 perf->calculated.render_avg_time, perf->calculated.flush_avg_time
304 );
305 #endif /*LV_USE_PERF_MONITOR_LOG_MODE*/
306 }
307
308 #endif
309
310 #if LV_USE_MEM_MONITOR
311
mem_update_timer_cb(lv_timer_t * t)312 static void mem_update_timer_cb(lv_timer_t * t)
313 {
314 lv_mem_monitor_t * mem_mon = lv_timer_get_user_data(t);
315 lv_mem_monitor(mem_mon);
316 lv_subject_set_pointer(&sysmon_mem.subject, mem_mon);
317 }
318
mem_observer_cb(lv_observer_t * observer,lv_subject_t * subject)319 static void mem_observer_cb(lv_observer_t * observer, lv_subject_t * subject)
320 {
321 lv_obj_t * label = lv_observer_get_target(observer);
322 const lv_mem_monitor_t * mon = lv_subject_get_pointer(subject);
323
324 size_t used_size = mon->total_size - mon->free_size;;
325 size_t used_kb = used_size / 1024;
326 size_t used_kb_tenth = (used_size - (used_kb * 1024)) / 102;
327 size_t max_used_kb = mon->max_used / 1024;
328 size_t max_used_kb_tenth = (mon->max_used - (max_used_kb * 1024)) / 102;
329 lv_label_set_text_fmt(label,
330 "%zu.%zu kB (%d%%)\n"
331 "%zu.%zu kB max, %d%% frag.",
332 used_kb, used_kb_tenth, mon->used_pct,
333 max_used_kb, max_used_kb_tenth,
334 mon->frag_pct);
335 }
336
337 #endif
338
339 #endif /*LV_USE_SYSMON*/
340