Added new perf utility to access CPU counters from userspace
[akaros.git] / tools / profile / perf / perf.c
1 /* Copyright (c) 2015 Google Inc
2  * Davide Libenzi <dlibenzi@google.com>
3  * See LICENSE for details.
4  */
5
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <sys/wait.h>
9 #include <stdint.h>
10 #include <stdio.h>
11 #include <string.h>
12 #include <stdlib.h>
13 #include <unistd.h>
14 #include <fcntl.h>
15 #include <limits.h>
16 #include <errno.h>
17 #include <parlib/parlib.h>
18 #include "xlib.h"
19 #include "akaros.h"
20 #include "perf_core.h"
21
22 static struct perf_context_config perf_cfg = {
23         .perf_file = "#arch/perf",
24         .kpctl_file = "#kprof/kpctl",
25 };
26
27 static void usage(const char *prg)
28 {
29         fprintf(stderr,
30                         "Use: %s {list,cpucaps,record} [-mkecxh] -- CMD [ARGS ...]\n"
31                         "\tlist            Lists all the available events and their meaning.\n"
32                         "\tcpucaps         Shows the system CPU capabilities in term of "
33                         "performance counters.\n"
34                         "\trecord           Setups the configured counters, runs CMD, and "
35                         "shows the values of the counters.\n"
36                         "Options:\n"
37                         "\t-m PATH          Sets the path of the PERF file ('%s').\n"
38                         "\t-k PATH          Sets the path of the KPROF control file "
39                         "('%s').\n"
40                         "\t-e EVENT_SPEC    Adds an event to be tracked.\n"
41                         "\t-c CPUS_STR      Selects the CPU set on which the counters "
42                         "should be active.\n"
43                         "\t                 The following format is supported for the CPU "
44                         "set:\n"
45                         "\t                   !      = Negates the following set\n"
46                         "\t                   all    = Enable all CPUs\n"
47                         "\t                   llall  = Enable all low latency CPUs\n"
48                         "\t                   I.J.K  = Enable CPUs I, J, and K\n"
49                         "\t                   N-M    = Enable CPUs from N to M, included\n"
50                         "\t                 Examples: all:!3.4.7  0-15:!3.5.7\n"
51                         "\t-x EVENT_RX      Sets the event name regular expression for "
52                         "list.\n"
53                         "\t-h               Displays this help screen.\n", prg,
54                         perf_cfg.perf_file, perf_cfg.kpctl_file);
55         exit(1);
56 }
57
58 static void show_perf_arch_info(const struct perf_arch_info *pai, FILE *file)
59 {
60         fprintf(file,
61                         "PERF.version             = %u\n"
62                         "PERF.proc_arch_events    = %u\n"
63                         "PERF.bits_x_counter      = %u\n"
64                         "PERF.counters_x_proc     = %u\n"
65                         "PERF.bits_x_fix_counter  = %u\n"
66                         "PERF.fix_counters_x_proc = %u\n",
67                         pai->perfmon_version, pai->proc_arch_events, pai->bits_x_counter,
68                         pai->counters_x_proc, pai->bits_x_fix_counter,
69                         pai->fix_counters_x_proc);
70 }
71
72 static void run_process_and_wait(int argc, const char * const *argv,
73                                                                  const struct core_set *cores)
74 {
75         int pid, status;
76         size_t max_cores = ros_total_cores();
77         struct core_set pvcores;
78
79         pid = sys_proc_create(argv[0], strlen(argv[0]), argv, NULL, 0);
80         if (pid < 0) {
81                 perror(argv[0]);
82                 exit(1);
83         }
84
85         ros_get_low_latency_core_set(&pvcores);
86         ros_not_core_set(&pvcores);
87         ros_and_core_sets(&pvcores, cores);
88         for (size_t i = 0; i < max_cores; i++) {
89                 if (ros_get_bit(&pvcores, i)) {
90                         if (sys_provision(pid, RES_CORES, i)) {
91                                 fprintf(stderr,
92                                                 "Unable to provision CPU %lu to PID %d: cmd='%s'\n",
93                                                 i, pid, argv[0]);
94                                 exit(1);
95                         }
96                 }
97         }
98
99         sys_proc_run(pid);
100         waitpid(pid, &status, 0);
101 }
102
103 int main(int argc, const char * const *argv)
104 {
105         int i, icmd = -1, num_events = 0;
106         const char *cmd = argv[1], *show_rx = NULL;
107         struct perf_context *pctx;
108         struct core_set cores;
109         const char *events[MAX_CPU_EVENTS];
110
111         ros_get_all_cores_set(&cores);
112
113         for (i = 2; i < argc; i++) {
114                 if (!strcmp(argv[i], "-m")) {
115                         if (++i < argc)
116                                 perf_cfg.perf_file = argv[i];
117                 } else if (!strcmp(argv[i], "-k")) {
118                         if (++i < argc)
119                                 perf_cfg.kpctl_file = argv[i];
120                 } else if (!strcmp(argv[i], "-e")) {
121                         if (++i < argc) {
122                                 if (num_events >= MAX_CPU_EVENTS) {
123                                         fprintf(stderr, "Too many events: %d\n", num_events);
124                                         return 1;
125                                 }
126                                 events[num_events++] = argv[i];
127                         }
128                 } else if (!strcmp(argv[i], "-x")) {
129                         if (++i < argc)
130                                 show_rx = argv[i];
131                 } else if (!strcmp(argv[i], "-c")) {
132                         if (++i < argc)
133                                 ros_parse_cores(argv[i], &cores);
134                 } else if (!strcmp(argv[i], "--")) {
135                         icmd = i + 1;
136                         break;
137                 } else {
138                         usage(argv[0]);
139                 }
140         }
141         if (!cmd)
142                 usage(argv[0]);
143
144         perf_initialize(argc, argv);
145         pctx = perf_create_context(&perf_cfg);
146
147         if (!strcmp(cmd, "list")) {
148                 perf_show_events(show_rx, stdout);
149         } else if (!strcmp(cmd, "cpucaps")) {
150                 show_perf_arch_info(perf_context_get_arch_info(pctx), stdout);
151         } else if (!strcmp(cmd, "record")) {
152                 if (icmd < 0)
153                         usage(argv[0]);
154
155                 for (i = 0; i < num_events; i++) {
156                         struct perf_eventsel sel;
157
158                         perf_parse_event(events[i], &sel);
159                         perf_context_event_submit(pctx, &cores, &sel);
160                 }
161
162                 if (!strcmp(argv[icmd], "sleep") && (icmd + 1) < argc)
163                         sleep(atoi(argv[icmd + 1]));
164                 else
165                         run_process_and_wait(argc - icmd, argv + icmd, &cores);
166
167                 perf_context_show_values(pctx, stdout);
168         } else {
169                 usage(argv[0]);
170         }
171         perf_free_context(pctx);
172         perf_finalize();
173
174         return 0;
175 }