lavu/timer.h: add Linux Perf API support
authorClément Bœsch <cboesch@gopro.com>
Sat, 2 Sep 2017 17:25:10 +0000 (19:25 +0200)
committerClément Bœsch <u@pkh.me>
Fri, 8 Sep 2017 16:51:05 +0000 (18:51 +0200)
Refer to "checkasm: use perf API on Linux ARM*" commit for the
rationale.

The implementation is somehow duplicated with checkasm, but so is the
current usage of AV_READ_TIME(). Until these implementations and
heuristics are made consistent, I don't see a way of sharing that code.

Note: when using libavutil/timer.h, it is now important to include
before any other include due to the _GNU_SOURCE requirement.

libavutil/timer.h

index da0761b..f7ab455 100644 (file)
 #ifndef AVUTIL_TIMER_H
 #define AVUTIL_TIMER_H
 
+#include "config.h"
+
+#if CONFIG_LINUX_PERF
+# ifndef _GNU_SOURCE
+#  define _GNU_SOURCE
+# endif
+# include <unistd.h> // read(3)
+# include <sys/ioctl.h>
+# include <asm/unistd.h>
+# include <linux/perf_event.h>
+#endif
+
 #include <stdlib.h>
 #include <stdint.h>
 #include <inttypes.h>
 
-#include "config.h"
-
 #if HAVE_MACH_MACH_TIME_H
 #include <mach/mach_time.h>
 #endif
         }                                                                 \
     }
 
-#ifdef AV_READ_TIME
+#if CONFIG_LINUX_PERF
+
+#define START_TIMER                                                         \
+    static int linux_perf_fd;                                               \
+    uint64_t tperf;                                                         \
+    if (!linux_perf_fd) {                                                   \
+        struct perf_event_attr attr = {                                     \
+            .type           = PERF_TYPE_HARDWARE,                           \
+            .size           = sizeof(struct perf_event_attr),               \
+            .config         = PERF_COUNT_HW_CPU_CYCLES,                     \
+            .disabled       = 1,                                            \
+            .exclude_kernel = 1,                                            \
+            .exclude_hv     = 1,                                            \
+        };                                                                  \
+        linux_perf_fd = syscall(__NR_perf_event_open, &attr,                \
+                                0, -1, -1, 0);                              \
+    }                                                                       \
+    if (linux_perf_fd == -1) {                                              \
+        av_log(NULL, AV_LOG_ERROR, "perf_event_open failed: %s\n",          \
+               av_err2str(AVERROR(errno)));                                 \
+    } else {                                                                \
+        ioctl(linux_perf_fd, PERF_EVENT_IOC_RESET, 0);                      \
+        ioctl(linux_perf_fd, PERF_EVENT_IOC_ENABLE, 0);                     \
+    }
+
+#define STOP_TIMER(id)                                                      \
+    ioctl(linux_perf_fd, PERF_EVENT_IOC_DISABLE, 0);                        \
+    read(linux_perf_fd, &tperf, sizeof(tperf));                             \
+    TIMER_REPORT(id, tperf)
+
+#elif defined(AV_READ_TIME)
 #define START_TIMER                             \
     uint64_t tend;                              \
     uint64_t tstart = AV_READ_TIME();           \