| 1 | #include <errno.h> |
| 2 | #include <stdlib.h> |
| 3 | #include <time.h> |
| 4 | |
| 5 | #if defined(__GNUC__) && (defined(__i386__) || defined (__x86_64__)) |
| 6 | |
| 7 | struct cycle_counter { |
| 8 | char dummy; |
| 9 | }; |
| 10 | |
| 11 | static int init_counter(struct cycle_counter *cy) { return (0); } |
| 12 | static void teardown_counter(struct cycle_counter *cy) { ; } |
| 13 | |
| 14 | int cycles(struct cycle_counter *cy, unsigned long long *t_out) |
| 15 | { |
| 16 | unsigned lo, hi; |
| 17 | |
| 18 | __asm__("rdtsc" : "=a"(lo), "=d"(hi)); |
| 19 | *t_out = lo | ((unsigned long long)hi << 32); |
| 20 | return (0); |
| 21 | } |
| 22 | |
| 23 | #elif defined(__linux__) |
| 24 | |
| 25 | #include <unistd.h> |
| 26 | #include <linux/perf_event.h> |
| 27 | #include <asm/unistd.h> |
| 28 | |
| 29 | struct cycle_counter { |
| 30 | int fd; |
| 31 | }; |
| 32 | |
| 33 | static int init_counter(struct cycle_counter *cy) |
| 34 | { |
| 35 | struct perf_event_attr attr = { 0 }; |
| 36 | |
| 37 | attr.type = PERF_TYPE_HARDWARE; |
| 38 | attr.size = sizeof(attr); |
| 39 | attr.config = PERF_COUNT_HW_CPU_CYCLES; |
| 40 | attr.disabled = 0; |
| 41 | attr.exclude_kernel = 1; |
| 42 | attr.exclude_hv = 1; |
| 43 | |
| 44 | cy->fd = syscall(__NR_perf_event_open, &attr, 0, -1, -1, 0); |
| 45 | if (cy->fd < 0) return (-1); |
| 46 | return (0); |
| 47 | } |
| 48 | |
| 49 | static void teardown_counter(struct cycle_counter *cy) |
| 50 | { if (cy->fd >= 0) close(cy->fd); } |
| 51 | |
| 52 | int cycles(struct cycle_counter *cy, unsigned long long *t_out) |
| 53 | { |
| 54 | ssize_t n; |
| 55 | |
| 56 | if (cy->fd < 0) { errno = EBADF; return (-1); } |
| 57 | n = read(cy->fd, t_out, sizeof(*t_out)); |
| 58 | if (n < sizeof(*t_out)) { |
| 59 | if (n >= 0) errno = ENODATA; |
| 60 | close(cy->fd); cy->fd = -1; |
| 61 | return (-1); |
| 62 | } |
| 63 | return (0); |
| 64 | } |
| 65 | |
| 66 | #else |
| 67 | |
| 68 | struct cycle_counter { |
| 69 | char dummy; |
| 70 | }; |
| 71 | |
| 72 | static int init_counter(struct cycle_counter *cy) |
| 73 | { errno = ENOSYS; return (-1); } |
| 74 | static void teardown_counter(struct cycle_counter *cy) { ; } |
| 75 | int cycles(struct cycle_counter *cy, unsigned long long *t_out) |
| 76 | { return (-1); } |
| 77 | |
| 78 | #endif |
| 79 | |
| 80 | struct cycle_counter *open_cycle_counter(void) |
| 81 | { |
| 82 | struct cycle_counter *cy = 0, *cy_ret = 0; |
| 83 | |
| 84 | cy = malloc(sizeof(*cy)); if (!cy) goto end; |
| 85 | if (init_counter(cy)) goto end; |
| 86 | cy_ret = cy; cy = 0; |
| 87 | end: |
| 88 | free(cy); |
| 89 | return (cy_ret); |
| 90 | } |
| 91 | |
| 92 | void close_cycle_counter(struct cycle_counter *cy) |
| 93 | { teardown_counter(cy); free(cy); } |
| 94 | |
| 95 | struct clock { |
| 96 | long long sec; |
| 97 | unsigned nsec; |
| 98 | }; |
| 99 | |
| 100 | int thread_clock(struct clock *clk_out) |
| 101 | { |
| 102 | struct timespec tv; |
| 103 | |
| 104 | if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tv)) return (-1); |
| 105 | clk_out->sec = tv.tv_sec; clk_out->nsec = tv.tv_nsec; |
| 106 | return (0); |
| 107 | } |
| 108 | |
| 109 | int get_errno(void) { return (errno); } |