aboutsummaryrefslogtreecommitdiffstats
path: root/bin/perfcounters.c
diff options
context:
space:
mode:
Diffstat (limited to 'bin/perfcounters.c')
-rw-r--r--bin/perfcounters.c197
1 files changed, 197 insertions, 0 deletions
diff --git a/bin/perfcounters.c b/bin/perfcounters.c
new file mode 100644
index 0000000..6302164
--- /dev/null
+++ b/bin/perfcounters.c
@@ -0,0 +1,197 @@
1#include "asm/unistd.h" /* from kernel source tree */
2#include <unistd.h> /* for syscall */
3
4#include <sys/ioctl.h>
5
6#include "perfcounters.h"
7
8#define C(x) (PERF_COUNT_HW_CACHE_##x)
9#define ATTR_CONFIG_CACHE(cache, op, result) \
10 (((C(cache) & 0xffULL) << 0) | \
11 ((C(op) & 0xffULL) << 8) | \
12 ((C(result) & 0xffULL) << 16))
13
14#define ATTR_CONFIG(event, umask) \
15 ((((event) & 0xffULL) << 0) | \
16 (((umask) & 0xffULL) << 8))
17
18static struct perf_event_attr perf_event_attr = {
19 .type = 0, /* set per initilized event */
20 .size = 0, /* set later */
21 .config = 0, /* set per initilized event */
22 { .sample_period = 0, }, /* is a counter, so no period */
23 .disabled = 0, /* event is enabled */
24 .inherit = 0, /* children don't inherit */
25 .pinned = 0, /* set per initilized event */
26 .exclusive = 0, /* set per initilized event */
27 .exclude_user = 0, /* don't count user (when set) */
28 .exclude_kernel = 0, /* ditto kernel */
29 .exclude_hv = 0, /* ditto hypervisor */
30 .exclude_idle = 0, /* don't count when idle */
31 .mmap = 0, /* include mmap data */
32 .comm = 0, /* include comm data */
33};
34
35struct perf_counter_setup {
36 char *name;
37 enum perf_type_id type;
38 uint64_t config;
39};
40
41#if 0
42/* these events are always zero */
43static struct perf_fd perf_fds[] = {
44 {
45 .fd = -1,
46 .name = "MEM_UNCORE_RETIRED.REMOTE_CACHE_LOCAL_HOME_HIT",
47 .type = PERF_TYPE_RAW,
48 .config = ATTR_CONFIG(0x0f, 0x08),
49 .exclusive = 0,
50 .pinned = 0,
51 },
52 {
53 .fd = -1,
54 .name = "MEM_UNCORE_RETIRED.REMOTE_DRAM",
55 .type = PERF_TYPE_RAW,
56 .config = ATTR_CONFIG(0x0f, 0x10),
57 .exclusive = 0, /* child events cannot be exclusive */
58 .pinned = 0, /* child events cannot be pinned */
59 },
60 { },
61};
62#endif
63
64static struct perf_counter_setup perf_setup[NR_PERF_COUNTERS] = {
65#if 0
66 {
67 .name = "MEM_UNCORE_RETIRED.LOCAL_DRAM",
68 .type = PERF_TYPE_RAW,
69 .config = ATTR_CONFIG(0x0f, 0x20),
70 },
71 {
72 .name = "L2_RQSTS.PREFETCH_HIT",
73 .type = PERF_TYPE_RAW,
74 .config = ATTR_CONFIG(0x24, 0x40),
75 },
76 {
77 .name = "L2_RQSTS.PREFETCH_MISS",
78 .type = PERF_TYPE_RAW,
79 .config = ATTR_CONFIG(0x24, 0x80),
80 },
81#endif
82 {
83 .name = "MEM_LOAD_RETIRED.OTHER_CORE_L2_HIT_HITM",
84 .type = PERF_TYPE_RAW,
85 .config = ATTR_CONFIG(0xcb, 0x08),
86 },
87 {
88 .name = "MEM_LOAD_RETIRED.L3_UNSHARED_HIT",
89 .type = PERF_TYPE_RAW,
90 .config = ATTR_CONFIG(0xcb, 0x04),
91 },
92 {
93 .name = "MEM_LOAD_RETIRED.L3_MISS",
94 .type = PERF_TYPE_RAW,
95 .config = ATTR_CONFIG(0xcb, 0x10),
96 },
97 {
98 .name = "Off Core Response Counter",
99 .type = PERF_TYPE_HW_CACHE,
100 .config = ATTR_CONFIG_CACHE(LL, OP_READ, RESULT_MISS),
101#if 0
102 /* read misses */
103 .config = ATTR_CONFIG_CACHE(LL, OP_READ, RESULT_MISS),
104 /* write misses */
105 .config = ATTR_CONFIG_CACHE(LL, OP_WRITE, RESULT_MISS),
106 /* prefetch misses */
107 .config = ATTR_CONFIG_CACHE(LL, OP_PREFETCH, RESULT_MISS),
108#endif
109 },
110};
111
112
113/* from kernel tools/perf/perf.h */
114int sys_perf_event_open(struct perf_event_attr *attr, pid_t pid,
115 int cpu, int group_fd, unsigned long flags)
116{
117 attr->size = sizeof(*attr);
118 return syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags);
119}
120
121/* make the temporary attributes shadow those in the perf_fd temporarially */
122static void write_global_perf_attr(const struct perf_counter_setup *p)
123{
124 perf_event_attr.type = p->type;
125 perf_event_attr.config = p->config;
126 perf_event_attr.pinned = 0;
127 perf_event_attr.exclusive = 0;
128}
129
130int setup_cpu_perf(const int cpu, const int group_leader,
131 struct perf_counter *perf_counters)
132{
133 const int perf_pid = -1; /* -1: all tasks */
134 int err = 0, i;
135
136 if (-1 == group_leader) {
137 /* first element determines the group for all others */
138 perf_counters->fd = -1;
139 }
140
141 for (i = 0; i < NR_PERF_COUNTERS; i++) {
142 int perf_group;
143
144 /* setup the attributes to pass in */
145 write_global_perf_attr(&perf_setup[i]);
146
147 if (0 == i && -1 == group_leader) {
148 /* but group leader is pinned and exclusive */
149 perf_event_attr.exclusive = 1;
150 perf_event_attr.pinned = 1;
151 perf_group = -1;
152 } else if (-1 == group_leader) {
153 /* not first counter, but no group passed in */
154 perf_group = perf_counters[0].fd;
155 }
156
157 perf_counters[i].fd = sys_perf_event_open(&perf_event_attr,
158 perf_pid, cpu, perf_group, 0);
159
160 if (0 > perf_counters[i].fd) {
161 err = -1;
162 goto out;
163 }
164
165 /* save the attributes in the user-visible configuration */
166 perf_counters[i].type = perf_setup[i].type;
167 perf_counters[i].config = perf_setup[i].config;
168 }
169out:
170 return err;
171}
172
173static inline int perf_setup_match(const struct perf_counter_setup* ps,
174 const struct perf_counter *pc)
175{
176 return (ps->type == pc->type && ps->config == pc->config);
177}
178
179const char* get_perf_name(const struct perf_counter* perf_counter)
180{
181 char *ret = NULL;
182 int i;
183
184 for (i = 0; i < NR_PERF_COUNTERS; i++) {
185 if (perf_setup_match(&perf_setup[i], perf_counter)) {
186 ret = perf_setup[i].name;
187 break;
188 }
189 }
190 return ret;
191}
192
193int read_perf_counter(const struct perf_counter* perf_counter, uint64_t *val)
194{
195 ssize_t ret = read(perf_counter->fd, val, sizeof(*val));
196 return (ret <= 0);
197}