/* * Copyright 2015 Rockchip Electronics Co. LTD * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define MODULE_TAG "mpp_time" #include #include #include #include #include "mpp_mem.h" #include "mpp_time.h" #include "mpp_debug.h" #include "mpp_common.h" #include "mpp_thread.h" #if _WIN32 #include #include RK_S64 mpp_time() { struct timeb tb; ftime(&tb); return ((RK_S64)tb.time * 1000 + (RK_S64)tb.millitm) * 1000; } #else #include RK_S64 mpp_time() { struct timespec time = {0, 0}; clock_gettime(CLOCK_MONOTONIC, &time); return (RK_S64)time.tv_sec * 1000000 + (RK_S64)time.tv_nsec / 1000; } #endif void mpp_time_diff(RK_S64 start, RK_S64 end, RK_S64 limit, const char *fmt) { RK_S64 diff = end - start; if (diff >= limit) mpp_dbg(MPP_DBG_TIMING, "%s timing %lld us\n", fmt, diff); } typedef struct MppClockImpl_t { const char *check; char name[16]; RK_U32 enable; RK_S64 base; RK_S64 time; RK_S64 sum; RK_S64 count; } MppClockImpl; static const char *clock_name = "mpp_clock"; MPP_RET check_is_mpp_clock(void *clock) { if (clock && ((MppClockImpl*)clock)->check == clock_name) return MPP_OK; mpp_err_f("pointer %p failed on check\n", clock); mpp_abort(); return MPP_NOK; } MppClock mpp_clock_get(const char *name) { MppClockImpl *impl = mpp_calloc(MppClockImpl, 1); if (impl) { impl->check = clock_name; snprintf(impl->name, sizeof(impl->name) - 1, name, NULL); } else mpp_err_f("malloc failed\n"); return impl; } void mpp_clock_put(MppClock clock) { if (NULL == clock || check_is_mpp_clock(clock)) { mpp_err_f("invalid clock %p\n", clock); return ; } mpp_free(clock); } void mpp_clock_enable(MppClock clock, RK_U32 enable) { if (NULL == clock || check_is_mpp_clock(clock)) { mpp_err_f("invalid clock %p\n", clock); } else { MppClockImpl *p = (MppClockImpl *)clock; p->enable = (enable) ? (1) : (0); } } RK_S64 mpp_clock_start(MppClock clock) { if (NULL == clock || check_is_mpp_clock(clock)) { mpp_err_f("invalid clock %p\n", clock); return 0; } MppClockImpl *p = (MppClockImpl *)clock; if (!p->enable) return 0; p->base = mpp_time(); p->time = 0; return p->base; } RK_S64 mpp_clock_pause(MppClock clock) { if (NULL == clock || check_is_mpp_clock(clock)) { mpp_err_f("invalid clock %p\n", clock); return 0; } MppClockImpl *p = (MppClockImpl *)clock; if (!p->enable) return 0; RK_S64 time = mpp_time(); if (!p->time) { // first pause after start p->sum += time - p->base; p->count++; } p->time = time; return p->time - p->base; } RK_S64 mpp_clock_reset(MppClock clock) { if (NULL == clock || check_is_mpp_clock(clock)) { mpp_err_f("invalid clock %p\n", clock); } else { MppClockImpl *p = (MppClockImpl *)clock; p->base = 0; p->time = 0; p->sum = 0; p->count = 0; } return 0; } RK_S64 mpp_clock_get_sum(MppClock clock) { if (NULL == clock || check_is_mpp_clock(clock)) { mpp_err_f("invalid clock %p\n", clock); return 0; } MppClockImpl *p = (MppClockImpl *)clock; return (p->enable) ? (p->sum) : (0); } RK_S64 mpp_clock_get_count(MppClock clock) { if (NULL == clock || check_is_mpp_clock(clock)) { mpp_err_f("invalid clock %p\n", clock); return 0; } MppClockImpl *p = (MppClockImpl *)clock; return (p->enable) ? (p->count) : (0); } const char *mpp_clock_get_name(MppClock clock) { if (NULL == clock || check_is_mpp_clock(clock)) { mpp_err_f("invalid clock %p\n", clock); return NULL; } MppClockImpl *p = (MppClockImpl *)clock; return p->name; } typedef struct MppTimerImpl_t { const char *check; char name[16]; RK_S32 enabled; RK_S32 initial; RK_S32 interval; RK_S32 timer_fd; RK_S32 epoll_fd; MppThread *thd; MppThreadFunc func; void *ctx; } MppTimerImpl; static const char *timer_name = "mpp_timer"; MPP_RET check_is_mpp_timer(void *timer) { if (timer && ((MppTimerImpl*)timer)->check == timer_name) return MPP_OK; mpp_err_f("pointer %p failed on check\n", timer); mpp_abort(); return MPP_NOK; } static void *mpp_timer_thread(void *ctx) { struct itimerspec ts; RK_S32 ret = 0; MppTimerImpl *impl = (MppTimerImpl *)ctx; MppThread *thd = impl->thd; RK_S32 timer_fd = impl->timer_fd; // first expire time ts.it_value.tv_sec = impl->initial / 1000; ts.it_value.tv_nsec = (impl->initial % 1000) * 1000; // last expire time ts.it_interval.tv_sec = impl->interval / 1000; ts.it_interval.tv_nsec = (impl->interval % 1000) * 1000 * 1000; ret = timerfd_settime(timer_fd, 0, &ts, NULL); if (ret < 0) { mpp_err("timerfd_settime error, Error:[%d:%s]", errno, strerror(errno)); return NULL; } while (1) { if (MPP_THREAD_RUNNING != thd->get_status()) break; struct epoll_event events; memset(&events, 0, sizeof(events)); /* wait epoll event */ RK_S32 fd_cnt = epoll_wait(impl->epoll_fd, &events, 1, 500); if (fd_cnt && (events.events & EPOLLIN) && (events.data.fd == timer_fd)) { RK_U64 exp = 0; ssize_t cnt = read(timer_fd, &exp, sizeof(exp)); mpp_assert(cnt == sizeof(exp)); impl->func(impl->ctx); } } return NULL; } MppTimer mpp_timer_get(const char *name) { RK_S32 timer_fd = -1; RK_S32 epoll_fd = -1; MppTimerImpl *impl = NULL; do { struct epoll_event event; impl = mpp_calloc(MppTimerImpl, 1); if (NULL == impl) { mpp_err_f("malloc failed\n"); break; } timer_fd = timerfd_create(CLOCK_REALTIME, 0); if (timer_fd < 0) break; epoll_fd = epoll_create(1); if (epoll_fd < 0) break; memset(&event, 0, sizeof(event)); event.data.fd = timer_fd; event.events = EPOLLIN | EPOLLET; if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, timer_fd, &event) < 0) break; impl->timer_fd = timer_fd; impl->epoll_fd = epoll_fd; /* default 1 second (1000ms) looper */ impl->initial = 1000; impl->interval = 1000; impl->check = timer_name; snprintf(impl->name, sizeof(impl->name) - 1, name, NULL); return impl; } while (0); mpp_err_f("failed to create timer\n"); if (impl) { mpp_free(impl); impl = NULL; } if (timer_fd >= 0) { close(timer_fd); timer_fd = -1; } if (epoll_fd >= 0) { close(epoll_fd); epoll_fd = -1; } return NULL; } void mpp_timer_set_callback(MppTimer timer, MppThreadFunc func, void *ctx) { if (NULL == timer || check_is_mpp_timer(timer)) { mpp_err_f("invalid timer %p\n", timer); return ; } if (NULL == func) { mpp_err_f("invalid NULL callback\n"); return ; } MppTimerImpl *impl = (MppTimerImpl *)timer; impl->func = func; impl->ctx = ctx; } void mpp_timer_set_timing(MppTimer timer, RK_S32 initial, RK_S32 interval) { if (NULL == timer || check_is_mpp_timer(timer)) { mpp_err_f("invalid timer %p\n", timer); return ; } MppTimerImpl *impl = (MppTimerImpl *)timer; impl->initial = initial; impl->interval = interval; } void mpp_timer_set_enable(MppTimer timer, RK_S32 enable) { if (NULL == timer || check_is_mpp_timer(timer)) { mpp_err_f("invalid timer %p\n", timer); return ; } MppTimerImpl *impl = (MppTimerImpl *)timer; if (NULL == impl->func || impl->initial < 0 || impl->interval < 0) { mpp_err_f("invalid func %p initial %d interval %d\n", impl->func, impl->initial, impl->interval); return ; } if (enable) { if (!impl->enabled && NULL == impl->thd) { MppThread *thd = new MppThread(mpp_timer_thread, impl, impl->name); if (thd) { impl->thd = thd; impl->enabled = 1; thd->start(); } } } else { if (impl->enabled && impl->thd) { impl->thd->stop(); impl->enabled = 0; } } } void mpp_timer_put(MppTimer timer) { if (NULL == timer || check_is_mpp_timer(timer)) { mpp_err_f("invalid timer %p\n", timer); return ; } MppTimerImpl *impl = (MppTimerImpl *)timer; if (impl->enabled) mpp_timer_set_enable(timer, 0); if (impl->timer_fd >= 0) { close(impl->timer_fd); impl->timer_fd = -1; } if (impl->epoll_fd >= 0) { close(impl->epoll_fd); impl->epoll_fd = -1; } if (impl->thd) { delete impl->thd; impl->thd = NULL; } if (impl) { mpp_free(impl); impl = NULL; } } AutoTiming::AutoTiming(const char *name) { mStart = mpp_time(); mName = name; } AutoTiming::~AutoTiming() { mEnd = mpp_time(); mpp_log("%s timing %lld us\n", mName, mEnd - mStart); } #define STOPWATCH_TRACE_STR_LEN 64 typedef struct MppStopwatchNode_t { char event[STOPWATCH_TRACE_STR_LEN]; RK_S64 time; } MppStopwatchNode; typedef struct MppStopwatchImpl_t { const char *check; char name[STOPWATCH_TRACE_STR_LEN]; RK_S32 max_count; RK_S32 filled_count; RK_S32 show_on_exit; RK_S32 log_len; RK_S64 time_elipsed; MppStopwatchNode *nodes; } MppStopwatchImpl; static const char *stopwatch_name = "mpp_stopwatch"; MPP_RET check_is_mpp_stopwatch(void *stopwatch) { if (stopwatch && ((MppStopwatchImpl*)stopwatch)->check == stopwatch_name) return MPP_OK; mpp_err_f("pointer %p failed on check\n", stopwatch); mpp_abort(); return MPP_NOK; } MppStopwatch mpp_stopwatch_get(const char *name) { MppStopwatchImpl *impl = mpp_calloc(MppStopwatchImpl, 1); MppStopwatchNode *nodes = mpp_calloc(MppStopwatchNode, 8); if (impl && nodes) { impl->check = stopwatch_name; snprintf(impl->name, sizeof(impl->name) - 1, name, NULL); impl->nodes = nodes; impl->max_count = 8; } else { mpp_err_f("malloc failed\n"); MPP_FREE(impl); MPP_FREE(nodes); } return impl; } void mpp_stopwatch_set_show_on_exit(MppStopwatch stopwatch, RK_S32 show_on_exit) { if (NULL == stopwatch || check_is_mpp_stopwatch(stopwatch)) { mpp_err_f("invalid stopwatch %p\n", stopwatch); return ; } MppStopwatchImpl *impl = (MppStopwatchImpl *)stopwatch; impl->show_on_exit = show_on_exit; } void mpp_stopwatch_record(MppStopwatch stopwatch, const char *event) { /* do not print noisy log */ if (NULL == stopwatch) return ; if (check_is_mpp_stopwatch(stopwatch)) { mpp_err_f("invalid stopwatch %p on %s\n", stopwatch, event); return ; } MppStopwatchImpl *impl = (MppStopwatchImpl *)stopwatch; if (impl->filled_count >= impl->max_count) { RK_S32 max_count = impl->max_count * 2; MppStopwatchNode *nodes = mpp_realloc(impl->nodes, MppStopwatchNode, max_count); if (nodes) { impl->nodes = nodes; impl->max_count = max_count; } } if (impl->filled_count < impl->max_count) { MppStopwatchNode *node = impl->nodes + impl->filled_count; node->time = mpp_time(); if (event) { RK_S32 len = snprintf(node->event, sizeof(node->event) - 1, "%s", event); if (len > impl->log_len) impl->log_len = len; } impl->filled_count++; } } void mpp_stopwatch_put(MppStopwatch stopwatch) { if (NULL == stopwatch || check_is_mpp_stopwatch(stopwatch)) { mpp_err_f("invalid stopwatch %p\n", stopwatch); return ; } MppStopwatchImpl *impl = (MppStopwatchImpl *)stopwatch; if (impl->show_on_exit && impl->nodes && impl->filled_count) { MppStopwatchNode *node = impl->nodes; RK_S64 last_time = node->time; RK_S32 i; char fmt[32]; snprintf(fmt, sizeof(fmt) - 1, "%%s %%-%ds: %%6.2f\n", impl->log_len); node++; for (i = 1; i < impl->filled_count; i++) { mpp_log(fmt, impl->name, node->event, (float)(node->time - last_time) / 1000); last_time = node->time; node++; } } MPP_FREE(impl->nodes); MPP_FREE(impl); } RK_S64 mpp_stopwatch_elapsed_time(MppStopwatch stopwatch) { if (NULL == stopwatch || check_is_mpp_stopwatch(stopwatch)) { mpp_err_f("invalid stopwatch %p\n", stopwatch); return 0; } MppStopwatchImpl *impl = (MppStopwatchImpl *)stopwatch; if (impl->filled_count < 2) return 0; RK_S64 base_time = impl->nodes[0].time; RK_S64 curr_time = impl->nodes[impl->filled_count - 1].time; RK_S64 elapsed_time = curr_time - base_time; return elapsed_time; }