1 /* SPDX-License-Identifier: Apache-2.0 OR MIT */
2 /*
3 * Copyright (c) 2015 Rockchip Electronics Co., Ltd.
4 */
5
6 #define MODULE_TAG "mpp_time"
7
8 #include <time.h>
9 #include <errno.h>
10 #include <string.h>
11 #include <sys/timerfd.h>
12 #include <sys/epoll.h>
13
14 #include "mpp_mem.h"
15 #include "mpp_time.h"
16 #include "mpp_debug.h"
17 #include "mpp_common.h"
18 #include "mpp_thread.h"
19
mpp_time()20 rk_s64 mpp_time()
21 {
22 struct timespec time = {0, 0};
23
24 clock_gettime(CLOCK_MONOTONIC, &time);
25 return (rk_s64)time.tv_sec * 1000000 + (rk_s64)time.tv_nsec / 1000;
26 }
27
mpp_time_diff(rk_s64 start,rk_s64 end,rk_s64 limit,const char * fmt)28 void mpp_time_diff(rk_s64 start, rk_s64 end, rk_s64 limit, const char *fmt)
29 {
30 rk_s64 diff = end - start;
31
32 if (diff >= limit)
33 mpp_dbg(MPP_DBG_TIMING, "%s timing %lld us\n", fmt, diff);
34 }
35
36 typedef struct MppClockImpl_t {
37 const char *check;
38 char name[16];
39 rk_u32 enable;
40 rk_s64 base;
41 rk_s64 time;
42 rk_s64 sum;
43 rk_s64 count;
44 } MppClockImpl;
45
46 static const char *clock_name = "mpp_clock";
47
check_is_mpp_clock(void * clock)48 MPP_RET check_is_mpp_clock(void *clock)
49 {
50 if (clock && ((MppClockImpl*)clock)->check == clock_name)
51 return MPP_OK;
52
53 mpp_err_f("pointer %p failed on check\n", clock);
54 mpp_abort();
55 return MPP_NOK;
56 }
57
mpp_clock_get(const char * name)58 MppClock mpp_clock_get(const char *name)
59 {
60 MppClockImpl *impl = mpp_calloc(MppClockImpl, 1);
61
62 if (impl) {
63 impl->check = clock_name;
64 snprintf(impl->name, sizeof(impl->name) - 1, name, NULL);
65 } else
66 mpp_err_f("malloc failed\n");
67
68 return impl;
69 }
70
mpp_clock_put(MppClock clock)71 void mpp_clock_put(MppClock clock)
72 {
73 if (check_is_mpp_clock(clock)) {
74 mpp_err_f("invalid clock %p\n", clock);
75 return;
76 }
77
78 mpp_free(clock);
79 }
80
mpp_clock_enable(MppClock clock,rk_u32 enable)81 void mpp_clock_enable(MppClock clock, rk_u32 enable)
82 {
83 if (check_is_mpp_clock(clock)) {
84 mpp_err_f("invalid clock %p\n", clock);
85 } else {
86 MppClockImpl *p = (MppClockImpl *)clock;
87
88 p->enable = (enable) ? (1) : (0);
89 }
90 }
91
mpp_clock_start(MppClock clock)92 rk_s64 mpp_clock_start(MppClock clock)
93 {
94 MppClockImpl *p = (MppClockImpl *)clock;
95
96 if (check_is_mpp_clock(p)) {
97 mpp_err_f("invalid clock %p\n", p);
98 return 0;
99 }
100
101 if (!p->enable)
102 return 0;
103
104 p->base = mpp_time();
105 p->time = 0;
106 return p->base;
107 }
108
mpp_clock_pause(MppClock clock)109 rk_s64 mpp_clock_pause(MppClock clock)
110 {
111 MppClockImpl *p = (MppClockImpl *)clock;
112 rk_s64 time;
113
114 if (check_is_mpp_clock(p)) {
115 mpp_err_f("invalid clock %p\n", p);
116 return 0;
117 }
118
119 if (!p->enable)
120 return 0;
121
122 time = mpp_time();
123
124 if (!p->time) {
125 // first pause after start
126 p->sum += time - p->base;
127 p->count++;
128 }
129
130 p->time = time;
131
132 return p->time - p->base;
133 }
134
mpp_clock_reset(MppClock clock)135 rk_s64 mpp_clock_reset(MppClock clock)
136 {
137 MppClockImpl *p = (MppClockImpl *)clock;
138
139 if (check_is_mpp_clock(p)) {
140 mpp_err_f("invalid clock %p\n", p);
141 } else {
142 p->base = 0;
143 p->time = 0;
144 p->sum = 0;
145 p->count = 0;
146 }
147
148 return 0;
149 }
150
mpp_clock_get_sum(MppClock clock)151 rk_s64 mpp_clock_get_sum(MppClock clock)
152 {
153 MppClockImpl *p = (MppClockImpl *)clock;
154
155 if (check_is_mpp_clock(p)) {
156 mpp_err_f("invalid clock %p\n", p);
157 return 0;
158 }
159
160 return (p->enable) ? (p->sum) : (0);
161 }
162
mpp_clock_get_count(MppClock clock)163 rk_s64 mpp_clock_get_count(MppClock clock)
164 {
165 MppClockImpl *p = (MppClockImpl *)clock;
166
167 if (check_is_mpp_clock(p)) {
168 mpp_err_f("invalid clock %p\n", p);
169 return 0;
170 }
171
172 return (p->enable) ? (p->count) : (0);
173 }
174
mpp_clock_get_name(MppClock clock)175 const char *mpp_clock_get_name(MppClock clock)
176 {
177 MppClockImpl *p = (MppClockImpl *)clock;
178
179 if (check_is_mpp_clock(p)) {
180 mpp_err_f("invalid clock %p\n", p);
181 return NULL;
182 }
183
184 return p->name;
185 }
186
187 typedef struct MppTimerImpl_t {
188 const char *check;
189 char name[16];
190
191 rk_s32 enabled;
192 rk_s32 initial;
193 rk_s32 interval;
194 rk_s32 timer_fd;
195 rk_s32 epoll_fd;
196
197 MppThread *thd;
198 MppThreadFunc func;
199 void *ctx;
200 } MppTimerImpl;
201
202 static const char *timer_name = "mpp_timer";
203
check_is_mpp_timer(void * timer)204 MPP_RET check_is_mpp_timer(void *timer)
205 {
206 if (timer && ((MppTimerImpl*)timer)->check == timer_name)
207 return MPP_OK;
208
209 mpp_err_f("pointer %p failed on check\n", timer);
210 mpp_abort();
211 return MPP_NOK;
212 }
213
mpp_timer_thread(void * ctx)214 static void *mpp_timer_thread(void *ctx)
215 {
216 struct itimerspec ts;
217 MppTimerImpl *impl = (MppTimerImpl *)ctx;
218 MppThread *thd = impl->thd;
219 rk_s32 timer_fd = impl->timer_fd;
220 rk_s32 ret = 0;
221
222 // first expire time
223 ts.it_value.tv_sec = impl->initial / 1000;
224 ts.it_value.tv_nsec = (impl->initial % 1000) * 1000;
225
226 // last expire time
227 ts.it_interval.tv_sec = impl->interval / 1000;
228 ts.it_interval.tv_nsec = (impl->interval % 1000) * 1000 * 1000;
229
230 ret = timerfd_settime(timer_fd, 0, &ts, NULL);
231 if (ret < 0) {
232 mpp_err("timerfd_settime error, Error:[%d:%s]", errno, strerror(errno));
233 return NULL;
234 }
235
236 while (1) {
237 struct epoll_event events;
238 rk_s32 fd_cnt;
239
240 if (MPP_THREAD_RUNNING != mpp_thread_get_status(thd, THREAD_WORK))
241 break;
242
243 memset(&events, 0, sizeof(events));
244
245 /* wait epoll event */
246 fd_cnt = epoll_wait(impl->epoll_fd, &events, 1, 500);
247 if (fd_cnt && (events.events & EPOLLIN) && (events.data.fd == timer_fd)) {
248 rk_u64 exp = 0;
249 ssize_t cnt = read(timer_fd, &exp, sizeof(exp));
250
251 mpp_assert(cnt == sizeof(exp));
252 impl->func(impl->ctx);
253 }
254 }
255
256 return NULL;
257 }
258
mpp_timer_get(const char * name)259 MppTimer mpp_timer_get(const char *name)
260 {
261 MppTimerImpl *impl = NULL;
262 rk_s32 timer_fd = -1;
263 rk_s32 epoll_fd = -1;
264
265 do {
266 struct epoll_event event;
267
268 impl = mpp_calloc(MppTimerImpl, 1);
269 if (!impl) {
270 mpp_err_f("malloc failed\n");
271 break;
272 }
273
274 timer_fd = timerfd_create(CLOCK_REALTIME, 0);
275 if (timer_fd < 0)
276 break;
277
278 epoll_fd = epoll_create(1);
279 if (epoll_fd < 0)
280 break;
281
282 memset(&event, 0, sizeof(event));
283 event.data.fd = timer_fd;
284 event.events = EPOLLIN | EPOLLET;
285
286 if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, timer_fd, &event) < 0)
287 break;
288
289 impl->timer_fd = timer_fd;
290 impl->epoll_fd = epoll_fd;
291 /* default 1 second (1000ms) looper */
292 impl->initial = 1000;
293 impl->interval = 1000;
294 impl->check = timer_name;
295 snprintf(impl->name, sizeof(impl->name) - 1, name, NULL);
296
297 return impl;
298 } while (0);
299
300 mpp_err_f("failed to create timer\n");
301
302 if (impl) {
303 mpp_free(impl);
304 impl = NULL;
305 }
306
307 if (timer_fd >= 0) {
308 close(timer_fd);
309 timer_fd = -1;
310 }
311
312 if (epoll_fd >= 0) {
313 close(epoll_fd);
314 epoll_fd = -1;
315 }
316
317 return NULL;
318 }
319
mpp_timer_set_callback(MppTimer timer,MppThreadFunc func,void * ctx)320 void mpp_timer_set_callback(MppTimer timer, MppThreadFunc func, void *ctx)
321 {
322 MppTimerImpl *impl = (MppTimerImpl *)timer;
323
324 if (check_is_mpp_timer(impl)) {
325 mpp_err_f("invalid timer %p\n", impl);
326 return;
327 }
328
329 if (!func) {
330 mpp_err_f("invalid NULL callback\n");
331 return;
332 }
333
334 impl->func = func;
335 impl->ctx = ctx;
336 }
337
mpp_timer_set_timing(MppTimer timer,rk_s32 initial,rk_s32 interval)338 void mpp_timer_set_timing(MppTimer timer, rk_s32 initial, rk_s32 interval)
339 {
340 MppTimerImpl *impl = (MppTimerImpl *)timer;
341
342 if (check_is_mpp_timer(impl)) {
343 mpp_err_f("invalid timer %p\n", impl);
344 return;
345 }
346
347 impl->initial = initial;
348 impl->interval = interval;
349 }
350
mpp_timer_set_enable(MppTimer timer,rk_s32 enable)351 void mpp_timer_set_enable(MppTimer timer, rk_s32 enable)
352 {
353 MppTimerImpl *impl = (MppTimerImpl *)timer;
354
355 if (check_is_mpp_timer(impl)) {
356 mpp_err_f("invalid timer %p\n", impl);
357 return;
358 }
359
360 if (!impl->func || impl->initial < 0 || impl->interval < 0) {
361 mpp_err_f("invalid func %p initial %d interval %d\n",
362 impl->func, impl->initial, impl->interval);
363 return;
364 }
365
366 if (enable) {
367 if (!impl->enabled && !impl->thd) {
368 MppThread *thd = mpp_thread_create(mpp_timer_thread, impl, impl->name);
369
370 if (thd) {
371 impl->thd = thd;
372 impl->enabled = 1;
373 mpp_thread_start(impl->thd);
374 }
375 }
376 } else {
377 if (impl->enabled && impl->thd) {
378 mpp_thread_stop(impl->thd);
379 impl->enabled = 0;
380 }
381 }
382 }
383
mpp_timer_put(MppTimer timer)384 void mpp_timer_put(MppTimer timer)
385 {
386 MppTimerImpl *impl = (MppTimerImpl *)timer;
387
388 if (check_is_mpp_timer(impl)) {
389 mpp_err_f("invalid timer %p\n", impl);
390 return;
391 }
392
393 if (impl->enabled)
394 mpp_timer_set_enable(impl, 0);
395
396 if (impl->timer_fd >= 0) {
397 close(impl->timer_fd);
398 impl->timer_fd = -1;
399 }
400
401 if (impl->epoll_fd >= 0) {
402 close(impl->epoll_fd);
403 impl->epoll_fd = -1;
404 }
405
406 if (impl->thd) {
407 mpp_thread_destroy(impl->thd);
408 impl->thd = NULL;
409 }
410
411 if (impl) {
412 mpp_free(impl);
413 impl = NULL;
414 }
415 }
416
417 #define STOPWATCH_TRACE_STR_LEN 64
418
419 typedef struct MppStopwatchNode_t {
420 char event[STOPWATCH_TRACE_STR_LEN];
421 rk_s64 time;
422 } MppStopwatchNode;
423
424 typedef struct MppStopwatchImpl_t {
425 const char *check;
426 char name[STOPWATCH_TRACE_STR_LEN];
427
428 rk_s32 max_count;
429 rk_s32 filled_count;
430 rk_s32 show_on_exit;
431 rk_s32 log_len;
432 rk_s64 time_elipsed;
433
434 MppStopwatchNode *nodes;
435 } MppStopwatchImpl;
436
437 static const char *stopwatch_name = "mpp_stopwatch";
438
check_is_mpp_stopwatch(void * stopwatch)439 MPP_RET check_is_mpp_stopwatch(void *stopwatch)
440 {
441 if (stopwatch && ((MppStopwatchImpl*)stopwatch)->check == stopwatch_name)
442 return MPP_OK;
443
444 mpp_err_f("pointer %p failed on check\n", stopwatch);
445 mpp_abort();
446 return MPP_NOK;
447 }
448
mpp_stopwatch_get(const char * name)449 MppStopwatch mpp_stopwatch_get(const char *name)
450 {
451 MppStopwatchImpl *impl = mpp_calloc(MppStopwatchImpl, 1);
452 MppStopwatchNode *nodes = mpp_calloc(MppStopwatchNode, 8);
453
454 if (impl && nodes) {
455 impl->check = stopwatch_name;
456 snprintf(impl->name, sizeof(impl->name) - 1, name, NULL);
457 impl->nodes = nodes;
458 impl->max_count = 8;
459 } else {
460 mpp_err_f("malloc failed\n");
461 MPP_FREE(impl);
462 MPP_FREE(nodes);
463 }
464
465 return impl;
466 }
467
mpp_stopwatch_set_show_on_exit(MppStopwatch stopwatch,rk_s32 show_on_exit)468 void mpp_stopwatch_set_show_on_exit(MppStopwatch stopwatch, rk_s32 show_on_exit)
469 {
470 MppStopwatchImpl *impl = (MppStopwatchImpl *)stopwatch;
471
472 if (check_is_mpp_stopwatch(impl)) {
473 mpp_err_f("invalid stopwatch %p\n", impl);
474 return;
475 }
476
477 impl->show_on_exit = show_on_exit;
478 }
479
mpp_stopwatch_record(MppStopwatch stopwatch,const char * event)480 void mpp_stopwatch_record(MppStopwatch stopwatch, const char *event)
481 {
482 MppStopwatchImpl *impl = (MppStopwatchImpl *)stopwatch;
483
484 /* do not print noisy log */
485 if (!impl)
486 return;
487
488 if (check_is_mpp_stopwatch(impl)) {
489 mpp_err_f("invalid stopwatch %p on %s\n", impl, event);
490 return;
491 }
492
493 if (impl->filled_count >= impl->max_count) {
494 rk_s32 max_count = impl->max_count * 2;
495 MppStopwatchNode *nodes = mpp_realloc(impl->nodes, MppStopwatchNode,
496 max_count);
497
498 if (nodes) {
499 impl->nodes = nodes;
500 impl->max_count = max_count;
501 }
502 }
503
504 if (impl->filled_count < impl->max_count) {
505 MppStopwatchNode *node = impl->nodes + impl->filled_count;
506
507 node->time = mpp_time();
508 if (event) {
509 rk_s32 len = snprintf(node->event, sizeof(node->event) - 1, "%s", event);
510
511 if (len > impl->log_len)
512 impl->log_len = len;
513 }
514 impl->filled_count++;
515 }
516 }
517
mpp_stopwatch_put(MppStopwatch stopwatch)518 void mpp_stopwatch_put(MppStopwatch stopwatch)
519 {
520 MppStopwatchImpl *impl = (MppStopwatchImpl *)stopwatch;
521
522 if (check_is_mpp_stopwatch(impl)) {
523 mpp_err_f("invalid stopwatch %p\n", impl);
524 return;
525 }
526
527 if (impl->show_on_exit && impl->nodes && impl->filled_count) {
528 MppStopwatchNode *node = impl->nodes;
529 rk_s64 last_time = node->time;
530 rk_s32 i;
531 char fmt[32];
532
533 snprintf(fmt, sizeof(fmt) - 1, "%%s %%-%ds: %%6.2f\n", impl->log_len);
534 node++;
535
536 for (i = 1; i < impl->filled_count; i++) {
537 mpp_log(fmt, impl->name, node->event,
538 (float)(node->time - last_time) / 1000);
539 last_time = node->time;
540 node++;
541 }
542 }
543 MPP_FREE(impl->nodes);
544 MPP_FREE(impl);
545 }
546
mpp_stopwatch_elapsed_time(MppStopwatch stopwatch)547 rk_s64 mpp_stopwatch_elapsed_time(MppStopwatch stopwatch)
548 {
549 MppStopwatchImpl *impl = (MppStopwatchImpl *)stopwatch;
550
551 if (check_is_mpp_stopwatch(impl)) {
552 mpp_err_f("invalid stopwatch %p\n", impl);
553 return 0;
554 }
555
556 if (impl->filled_count < 2)
557 return 0;
558
559 rk_s64 base_time = impl->nodes[0].time;
560 rk_s64 curr_time = impl->nodes[impl->filled_count - 1].time;
561 rk_s64 elapsed_time = curr_time - base_time;
562 return elapsed_time;
563 }
564