Added new perf utility to access CPU counters from userspace
[akaros.git] / tools / profile / perf / perf_core.c
1 /* Copyright (c) 2015 Google Inc
2  * Davide Libenzi <dlibenzi@google.com>
3  * See LICENSE for details.
4  */
5
6 #include <ros/arch/msr-index.h>
7 #include <ros/arch/perfmon.h>
8 #include <ros/common.h>
9 #include <ros/memops.h>
10 #include <sys/types.h>
11 #include <stdint.h>
12 #include <stdio.h>
13 #include <string.h>
14 #include <stdlib.h>
15 #include <unistd.h>
16 #include <ctype.h>
17 #include <limits.h>
18 #include <errno.h>
19 #include <regex.h>
20 #include <parlib/parlib.h>
21 #include <perfmon/err.h>
22 #include <perfmon/pfmlib.h>
23 #include "xlib.h"
24 #include "akaros.h"
25 #include "perf_core.h"
26
27 struct event_coords {
28         char *buffer;
29         const char *event;
30         const char *umask;
31 };
32
33 static const uint32_t invalid_mask = (uint32_t) -1;
34
35 static void perf_parse_event_coords(const char *name, struct event_coords *ec)
36 {
37         const char *cptr = strchr(name, ':');
38
39         if (cptr == NULL) {
40                 ec->buffer = NULL;
41                 ec->event = name;
42                 ec->umask = NULL;
43         } else {
44                 size_t cpos = cptr - name;
45
46                 ec->buffer = xstrdup(name);
47                 ec->event = ec->buffer;
48                 ec->umask = ec->buffer + cpos + 1;
49                 ec->buffer[cpos] = 0;
50         }
51 }
52
53 static void perf_free_event_coords(struct event_coords *ec)
54 {
55         free(ec->buffer);
56 }
57
58 static const char *perf_get_event_mask_name(const pfm_event_info_t *einfo,
59                                                                                         uint32_t mask)
60 {
61         int i;
62         pfm_event_attr_info_t ainfo;
63
64         ZERO_DATA(ainfo);
65         ainfo.size = sizeof(ainfo);
66         pfm_for_each_event_attr(i, einfo) {
67                 pfm_err_t err = pfm_get_event_attr_info(einfo->idx, i, PFM_OS_NONE,
68                                                                                                 &ainfo);
69
70                 if (err != PFM_SUCCESS) {
71                         fprintf(stderr, "Failed to get attribute info: %s\n",
72                                         pfm_strerror(err));
73                         exit(1);
74                 }
75                 if (ainfo.type == PFM_ATTR_UMASK) {
76                         if (mask == (uint32_t) ainfo.code)
77                                 return ainfo.name;
78                 }
79         }
80
81         return NULL;
82 }
83
84 static int perf_resolve_event_name(const char *name, uint32_t *event,
85                                                                    uint32_t *mask, uint32_t mask_hint)
86 {
87         int idx;
88         struct event_coords ec;
89
90         perf_parse_event_coords(name, &ec);
91
92         idx = pfm_find_event(ec.event);
93         if (idx >= 0) {
94                 int i;
95                 pfm_err_t err;
96                 pfm_event_info_t einfo;
97                 pfm_event_attr_info_t ainfo;
98
99                 ZERO_DATA(einfo);
100                 einfo.size = sizeof(einfo);
101                 err = pfm_get_event_info(idx, PFM_OS_NONE, &einfo);
102                 if (err != PFM_SUCCESS) {
103                         fprintf(stderr, "Unable to retrieve event (%s) info: %s\n",
104                                         name, pfm_strerror(err));
105                         exit(1);
106                 }
107
108                 *event = (uint32_t) einfo.code;
109                 *mask = invalid_mask;
110
111                 ZERO_DATA(ainfo);
112                 ainfo.size = sizeof(ainfo);
113                 pfm_for_each_event_attr(i, &einfo) {
114                         err = pfm_get_event_attr_info(idx, i, PFM_OS_NONE, &ainfo);
115                         if (err != PFM_SUCCESS) {
116                                 fprintf(stderr, "Failed to get attribute info: %s\n",
117                                                 pfm_strerror(err));
118                                 exit(1);
119                         }
120                         if (ainfo.type == PFM_ATTR_UMASK) {
121                                 if (mask_hint != invalid_mask) {
122                                         if (mask_hint == (uint32_t) ainfo.code) {
123                                                 *mask = (uint32_t) ainfo.code;
124                                                 break;
125                                         }
126                                 } else if (!ec.umask) {
127                                         *mask = (uint32_t) ainfo.code;
128                                         if (ainfo.is_dfl)
129                                                 break;
130                                 } else if (!strcmp(ec.umask, ainfo.name)) {
131                                         *mask = (uint32_t) ainfo.code;
132                                         break;
133                                 }
134                         }
135                 }
136         }
137         perf_free_event_coords(&ec);
138
139         return idx;
140 }
141
142 static int perf_find_event_by_id(uint32_t event, uint32_t mask)
143 {
144         int pmu;
145         pfm_pmu_info_t pinfo;
146         pfm_event_info_t info;
147
148     ZERO_DATA(pinfo);
149     pinfo.size = sizeof(pinfo);
150     ZERO_DATA(info);
151     info.size = sizeof(info);
152
153         pfm_for_all_pmus(pmu) {
154                 pfm_err_t err = pfm_get_pmu_info(pmu, &pinfo);
155
156                 if (err != PFM_SUCCESS || !pinfo.is_present)
157                         continue;
158
159                 for (int i = pinfo.first_event; i != -1; i = pfm_get_event_next(i)) {
160                         uint32_t cevent, cmask;
161
162                         err = pfm_get_event_info(i, PFM_OS_NONE, &info);
163                         if (err != PFM_SUCCESS) {
164                                 fprintf(stderr, "Failed to get event info: %s\n",
165                                                 pfm_strerror(err));
166                                 exit(1);
167                         }
168                         if (perf_resolve_event_name(info.name, &cevent, &cmask, mask) != i)
169                                 continue;
170                         if ((cevent == event) && (cmask == mask))
171                                 return i;
172                 }
173         }
174
175         return -1;
176 }
177
178 void perf_initialize(int argc, const char * const *argv)
179 {
180         pfm_err_t err = pfm_initialize();
181
182         if (err != PFM_SUCCESS) {
183                 fprintf(stderr, "Unable to initialize perfmon library: %s\n",
184                                 pfm_strerror(err));
185                 exit(1);
186         }
187 }
188
189 void perf_finalize(void)
190 {
191         pfm_terminate();
192 }
193
194 void perf_parse_event(const char *str, struct perf_eventsel *sel)
195 {
196         static const char *const event_spec =
197                 "{EVENT_ID:MASK,EVENT_NAME:MASK_NAME}[,os[={0,1}]][,usr[={0,1}]]"
198                 "[,int[={0,1}]][,invcmsk[={0,1}]][,cmask=MASK][,icount=COUNT]";
199         char *dstr = xstrdup(str), *sptr, *tok, *ev;
200
201         tok = strtok_r(dstr, ",", &sptr);
202         if (tok == NULL) {
203                 fprintf(stderr, "Invalid event spec string: '%s'\n\t%s\n", str,
204                                 event_spec);
205                 exit(1);
206         }
207         ZERO_DATA(*sel);
208         sel->eidx = -1;
209         sel->ev.flags = 0;
210         sel->ev.u.v = 0;
211         sel->ev.u.b.os = 1;
212         sel->ev.u.b.usr = 1;
213         sel->ev.u.b.en = 1;
214         if (isdigit(*tok)) {
215                 ev = strchr(tok, ':');
216                 if (ev == NULL) {
217                         fprintf(stderr, "Invalid event spec string: '%s'\n"
218                                         "\tShould be: %s\n", str, event_spec);
219                         exit(1);
220                 }
221                 *ev++ = 0;
222                 sel->ev.u.b.event = (uint8_t) strtoul(tok, NULL, 0);
223                 sel->ev.u.b.mask = (uint8_t) strtoul(ev, NULL, 0);
224         } else {
225                 uint32_t event, mask;
226
227                 sel->eidx = perf_resolve_event_name(tok, &event, &mask, invalid_mask);
228                 if (sel->eidx < 0) {
229                         fprintf(stderr, "Unable to find event: %s\n", tok);
230                         exit(1);
231                 }
232                 sel->ev.u.b.event = (uint8_t) event;
233                 sel->ev.u.b.mask = (uint8_t) mask;
234         }
235         while ((tok = strtok_r(NULL, ",", &sptr)) != NULL) {
236                 ev = strchr(tok, '=');
237                 if (ev)
238                         *ev++ = 0;
239                 if (!strcmp(tok, "os")) {
240                         sel->ev.u.b.os = (ev == NULL || atoi(ev) != 0) ? 1 : 0;
241                 } else if (!strcmp(tok, "usr")) {
242                         sel->ev.u.b.usr = (ev == NULL || atoi(ev) != 0) ? 1 : 0;
243                 } else if (!strcmp(tok, "int")) {
244                         sel->ev.u.b.inten = (ev == NULL || atoi(ev) != 0) ? 1 : 0;
245                 } else if (!strcmp(tok, "invcmsk")) {
246                         sel->ev.u.b.invcmsk = (ev == NULL || atoi(ev) != 0) ? 1 : 0;
247                 } else if (!strcmp(tok, "cmask")) {
248                         if (ev == NULL) {
249                                 fprintf(stderr, "Invalid event spec string: '%s'\n"
250                                                 "\tShould be: %s\n", str, event_spec);
251                                 exit(1);
252                         }
253                         sel->ev.u.b.cmask = (uint32_t) strtoul(ev, NULL, 0);
254                 } else if (!strcmp(tok, "icount")) {
255                         if (ev == NULL) {
256                                 fprintf(stderr, "Invalid event spec string: '%s'\n"
257                                                 "\tShould be: %s\n", str, event_spec);
258                                 exit(1);
259                         }
260                         sel->ev.trigger_count = (uint64_t) strtoul(ev, NULL, 0);
261                 }
262         }
263         if (sel->ev.u.b.inten && !sel->ev.trigger_count) {
264                 fprintf(stderr,
265                                 "Counter trigger count for interrupt is too small: %lu\n",
266                                 sel->ev.trigger_count);
267                 exit(1);
268         }
269         free(dstr);
270 }
271
272 static void perf_get_arch_info(int perf_fd, struct perf_arch_info *pai)
273 {
274         uint8_t cmdbuf[6 * sizeof(uint32_t)];
275         const uint8_t *rptr = cmdbuf;
276
277         cmdbuf[0] = PERFMON_CMD_CPU_CAPS;
278
279         xpwrite(perf_fd, cmdbuf, 1, 0);
280         xpread(perf_fd, cmdbuf, 6 * sizeof(uint32_t), 0);
281
282         rptr = get_le_u32(rptr, &pai->perfmon_version);
283         rptr = get_le_u32(rptr, &pai->proc_arch_events);
284         rptr = get_le_u32(rptr, &pai->bits_x_counter);
285         rptr = get_le_u32(rptr, &pai->counters_x_proc);
286         rptr = get_le_u32(rptr, &pai->bits_x_fix_counter);
287         rptr = get_le_u32(rptr, &pai->fix_counters_x_proc);
288 }
289
290 static int perf_open_event(int perf_fd, const struct core_set *cores,
291                                                    const struct perf_eventsel *sel)
292 {
293         uint8_t cmdbuf[1 + 3 * sizeof(uint64_t) + sizeof(uint32_t) +
294                                    CORE_SET_SIZE];
295         uint8_t *wptr = cmdbuf;
296         const uint8_t *rptr = cmdbuf;
297         uint32_t ped;
298         int i, j;
299
300         *wptr++ = PERFMON_CMD_COUNTER_OPEN;
301         wptr = put_le_u64(wptr, sel->ev.u.v);
302         wptr = put_le_u64(wptr, sel->ev.flags);
303         wptr = put_le_u64(wptr, sel->ev.trigger_count);
304
305         for (i = CORE_SET_SIZE - 1; (i >= 0) && !cores->core_set[i]; i--)
306                 ;
307         if (i < 0) {
308                 fprintf(stderr, "Performance event CPU set must not be empty\n");
309                 exit(1);
310         }
311         wptr = put_le_u32(wptr, i + 1);
312         for (j = 0; j <= i; j++)
313                 *wptr++ = cores->core_set[j];
314
315         xpwrite(perf_fd, cmdbuf, wptr - cmdbuf, 0);
316         xpread(perf_fd, cmdbuf, sizeof(uint32_t), 0);
317
318         rptr = get_le_u32(rptr, &ped);
319
320         return (int) ped;
321 }
322
323 static uint64_t *perf_get_event_values(int perf_fd, int ped,
324                                                                            struct perf_eventsel *sel,
325                                                                            size_t *pnvalues)
326 {
327         ssize_t rsize;
328         uint32_t i, n;
329         uint64_t *values;
330         size_t bufsize = 3 * sizeof(uint64_t) + sizeof(uint32_t) +
331                 MAX_NUM_CORES * sizeof(uint64_t);
332         uint8_t *cmdbuf = xmalloc(bufsize);
333         uint8_t *wptr = cmdbuf;
334         const uint8_t *rptr = cmdbuf;
335
336         *wptr++ = PERFMON_CMD_COUNTER_STATUS;
337         wptr = put_le_u32(wptr, ped);
338
339         xpwrite(perf_fd, cmdbuf, wptr - cmdbuf, 0);
340         rsize = pread(perf_fd, cmdbuf, bufsize, 0);
341
342         if (rsize < (3 * sizeof(uint64_t) + sizeof(uint32_t))) {
343                 fprintf(stderr, "Invalid read size while fetching event status: %ld\n",
344                                 rsize);
345                 exit(1);
346         }
347
348         rptr = get_le_u64(rptr, &sel->ev.u.v);
349         rptr = get_le_u64(rptr, &sel->ev.flags);
350         rptr = get_le_u64(rptr, &sel->ev.trigger_count);
351         rptr = get_le_u32(rptr, &n);
352         if (((rptr - cmdbuf) + n * sizeof(uint64_t)) > rsize) {
353                 fprintf(stderr, "Invalid read size while fetching event status: %ld\n",
354                                 rsize);
355                 exit(1);
356         }
357         values = xmalloc(n * sizeof(uint64_t));
358         for (i = 0; i < n; i++)
359                 rptr = get_le_u64(rptr, values + i);
360         free(cmdbuf);
361
362         *pnvalues = n;
363
364         return values;
365 }
366
367 static void perf_close_event(int perf_fd, int ped)
368 {
369         uint8_t cmdbuf[1 + sizeof(uint32_t)];
370         uint8_t *wptr = cmdbuf;
371
372         *wptr++ = PERFMON_CMD_COUNTER_CLOSE;
373         wptr = put_le_u32(wptr, ped);
374
375         xpwrite(perf_fd, cmdbuf, wptr - cmdbuf, 0);
376 }
377
378 static void perf_enable_sampling(int kpctl_fd)
379 {
380         static const char * const enable_str = "start";
381
382         xwrite(kpctl_fd, enable_str, strlen(enable_str));
383 }
384
385 static void perf_disable_sampling(int kpctl_fd)
386 {
387         static const char * const disable_str = "stop";
388
389         xwrite(kpctl_fd, disable_str, strlen(disable_str));
390 }
391
392 struct perf_context *perf_create_context(const struct perf_context_config *cfg)
393 {
394         struct perf_context *pctx = xzmalloc(sizeof(struct perf_context));
395
396         pctx->perf_fd = xopen(cfg->perf_file, O_RDWR, 0);
397         pctx->kpctl_fd = xopen(cfg->kpctl_file, O_RDWR, 0);
398         perf_get_arch_info(pctx->perf_fd, &pctx->pai);
399         perf_enable_sampling(pctx->kpctl_fd);
400
401         return pctx;
402 }
403
404 void perf_free_context(struct perf_context *pctx)
405 {
406         for (int i = 0; i < pctx->event_count; i++)
407                 perf_close_event(pctx->perf_fd, pctx->events[i].ped);
408         perf_disable_sampling(pctx->kpctl_fd);
409         close(pctx->kpctl_fd);
410         close(pctx->perf_fd);
411         free(pctx);
412 }
413
414 void perf_context_event_submit(struct perf_context *pctx,
415                                                            const struct core_set *cores,
416                                                            const struct perf_eventsel *sel)
417 {
418         struct perf_event *pevt = pctx->events + pctx->event_count;
419
420         if (pctx->event_count >= COUNT_OF(pctx->events)) {
421                 fprintf(stderr, "Too many open events: %d\n", pctx->event_count);
422                 exit(1);
423         }
424         pctx->event_count++;
425         pevt->cores = *cores;
426         pevt->sel = *sel;
427         pevt->ped = perf_open_event(pctx->perf_fd, cores, sel);
428 }
429
430 void perf_context_show_values(struct perf_context *pctx, FILE *file)
431 {
432         for (int i = 0; i < pctx->event_count; i++) {
433                 size_t nvalues;
434                 struct perf_eventsel sel;
435                 uint64_t *values = perf_get_event_values(pctx->perf_fd,
436                                                                                                  pctx->events[i].ped, &sel,
437                                                                                                  &nvalues);
438                 char ename[256];
439
440                 perf_get_event_string(&pctx->events[i].sel, ename, sizeof(ename));
441                 fprintf(file, "Event: %s\n\t", ename);
442                 for (size_t j = 0; j < nvalues; j++)
443                         fprintf(file, "%lu ", values[j]);
444                 fprintf(file, "\n");
445
446                 free(values);
447         }
448 }
449
450 static void perf_print_event_flags(const pfm_event_info_t *info, FILE *file)
451 {
452         int n = 0;
453
454         if (info->is_precise) {
455                 fputs("[precise] ", file);
456                 n++;
457         }
458         if (!n)
459                 fputs("None", file);
460 }
461
462 static void perf_print_attr_flags(const pfm_event_attr_info_t *info, FILE *file)
463 {
464         int n = 0;
465
466         if (info->is_dfl) {
467                 fputs("[default] ", file);
468                 n++;
469         }
470         if (info->is_precise) {
471                 fputs("[precise] ", file);
472                 n++;
473         }
474         if (!n)
475                 fputs("None ", file);
476 }
477
478 static void perf_show_event_info(const pfm_event_info_t *info,
479                                                                  const pfm_pmu_info_t *pinfo, FILE *file)
480 {
481         static const char * const srcs[PFM_ATTR_CTRL_MAX] = {
482                 [PFM_ATTR_CTRL_UNKNOWN] = "???",
483                 [PFM_ATTR_CTRL_PMU] = "PMU",
484                 [PFM_ATTR_CTRL_PERF_EVENT] = "perf_event",
485         };
486         pfm_event_attr_info_t ainfo;
487         int i, mod = 0, um = 0;
488
489         fprintf(file, "#-----------------------------\n"
490                         "IDX      : %d\n"
491                         "PMU name : %s (%s)\n"
492                         "Name     : %s\n"
493                         "Equiv    : %s\n",
494                         info->idx, pinfo->name, pinfo->desc,
495                         info->name, info->equiv ? info->equiv : "None");
496
497         fprintf(file, "Flags    : ");
498         perf_print_event_flags(info, file);
499         fputc('\n', file);
500
501         fprintf(file, "Desc     : %s\n", info->desc ? info->desc :
502                         "no description available");
503         fprintf(file, "Code     : 0x%"PRIx64"\n", info->code);
504
505         ZERO_DATA(ainfo);
506         ainfo.size = sizeof(ainfo);
507
508         pfm_for_each_event_attr(i, info) {
509                 const char *src;
510                 pfm_err_t err = pfm_get_event_attr_info(info->idx, i, PFM_OS_NONE,
511                                                                                                 &ainfo);
512
513                 if (err != PFM_SUCCESS) {
514                         fprintf(stderr, "Failed to get attribute info: %s\n",
515                                         pfm_strerror(err));
516                         exit(1);
517                 }
518
519                 if (ainfo.ctrl >= PFM_ATTR_CTRL_MAX) {
520                         fprintf(stderr, "event: %s has unsupported attribute source %d",
521                                         info->name, ainfo.ctrl);
522                         ainfo.ctrl = PFM_ATTR_CTRL_UNKNOWN;
523                 }
524                 src = srcs[ainfo.ctrl];
525                 switch (ainfo.type) {
526                         case PFM_ATTR_UMASK:
527                                 fprintf(file, "Umask-%02u : 0x%02"PRIx64" : %s : [%s] : ",
528                                                 um, ainfo.code, src, ainfo.name);
529                                 perf_print_attr_flags(&ainfo, file);
530                                 fputc(':', file);
531                                 if (ainfo.equiv)
532                                         fprintf(file, " Alias to %s", ainfo.equiv);
533                                 else
534                                         fprintf(file, " %s", ainfo.desc);
535                                 fputc('\n', file);
536                                 um++;
537                                 break;
538                         case PFM_ATTR_MOD_BOOL:
539                                 fprintf(file, "Modif-%02u : 0x%02"PRIx64" : %s : [%s] : "
540                                                 "%s (boolean)\n", mod, ainfo.code, src, ainfo.name,
541                                                 ainfo.desc);
542                                 mod++;
543                                 break;
544                         case PFM_ATTR_MOD_INTEGER:
545                                 fprintf(file, "Modif-%02u : 0x%02"PRIx64" : %s : [%s] : "
546                                                 "%s (integer)\n", mod, ainfo.code, src, ainfo.name,
547                                                 ainfo.desc);
548                                 mod++;
549                                 break;
550                         default:
551                                 fprintf(file, "Attr-%02u  : 0x%02"PRIx64" : %s : [%s] : %s\n",
552                                                 i, ainfo.code, ainfo.name, src, ainfo.desc);
553                 }
554         }
555 }
556
557 void perf_show_events(const char *rx, FILE *file)
558 {
559         int pmu;
560         regex_t crx;
561         pfm_pmu_info_t pinfo;
562         pfm_event_info_t info;
563         char fullname[256];
564
565         if (rx && regcomp(&crx, rx, REG_ICASE)) {
566                 fprintf(stderr, "Failed to compile event regex: '%s'\n", rx);
567                 exit(1);
568         }
569
570     ZERO_DATA(pinfo);
571     pinfo.size = sizeof(pinfo);
572     ZERO_DATA(info);
573     info.size = sizeof(info);
574
575         pfm_for_all_pmus(pmu) {
576                 pfm_err_t err = pfm_get_pmu_info(pmu, &pinfo);
577
578                 if (err != PFM_SUCCESS || !pinfo.is_present)
579                         continue;
580
581                 for (int i = pinfo.first_event; i != -1; i = pfm_get_event_next(i)) {
582                         err = pfm_get_event_info(i, PFM_OS_NONE, &info);
583                         if (err != PFM_SUCCESS) {
584                                 fprintf(stderr, "Failed to get event info: %s\n",
585                                                 pfm_strerror(err));
586                                 exit(1);
587                         }
588                         snprintf(fullname, sizeof(fullname), "%s::%s", pinfo.name,
589                                          info.name);
590                         if (!rx || regexec(&crx, fullname, 0, NULL, 0) == 0)
591                                 perf_show_event_info(&info, &pinfo, file);
592                 }
593         }
594         if (rx)
595                 regfree(&crx);
596 }
597
598 void perf_get_event_string(const struct perf_eventsel *sel, char *sbuf,
599                                                    size_t size)
600 {
601         pfm_event_info_t einfo;
602
603     ZERO_DATA(einfo);
604     einfo.size = sizeof(einfo);
605         if ((sel->eidx >= 0) &&
606                 (pfm_get_event_info(sel->eidx, PFM_OS_NONE, &einfo) == PFM_SUCCESS)) {
607                 const char *mask_name = perf_get_event_mask_name(&einfo,
608                                                                                                                  sel->ev.u.b.mask);
609
610                 if (mask_name)
611                         snprintf(sbuf, size, "%s:%s", einfo.name, mask_name);
612                 else
613                         snprintf(sbuf, size, "%s", einfo.name);
614         } else {
615                 snprintf(sbuf, size, "0x%02x:0x%02x", sel->ev.u.b.event,
616                                  sel->ev.u.b.mask);
617         }
618 }