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