blob: ce9837d98e225cab104e787f79f406de683900ed [file] [log] [blame]
Neil Leeder4832fc22011-11-22 16:49:08 -05001/*
Duy Truong790f06d2013-02-13 16:38:12 -08002 * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
Neil Leeder4832fc22011-11-22 16:49:08 -05003 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14/*
15 * A very simple perf program to periodically print the performance
16 * counter reqested on the command line to standard out at the rate
17 * specified.
18 *
19 * This is valuable for showing the output in a simple plot or
20 * exporting the counter data for post processing. No attempt
21 * to process the data is made.
22 *
23 * Scaling is not supported, use only as many counters as are
24 * provided by the hardware.
25 *
26 * Math functions are support to combine counter results by using
27 * the -m flag.
28 *
29 * The -r -w flags supports user signalling for input. This assumes
30 * that a pipe/fifo is needed so the -rw cmd line arg is a string
31 * that is the name of the named pipe to open for read/write. User
32 * sends data on the read pipe to the process to collect a sample.
33 * Commands are also supported on the pipe.
34 *
35 */
36
37#include "perf.h"
38#include "builtin.h"
39#include "util/util.h"
40#include "util/parse-options.h"
41#include "util/parse-events.h"
42#include "util/event.h"
43#include "util/evsel.h"
44#include "util/evlist.h"
45#include "util/debug.h"
46#include "util/header.h"
47#include "util/cpumap.h"
48#include "util/thread.h"
49#include <signal.h>
50#include <sys/types.h>
51
52#define PERF_PERIODIC_ERROR -1
53
54/* number of pieces of data on each read. */
55#define DATA_SIZE 2
56
57#define DEFAULT_FIFO_NAME "xxbadFiFo"
58#define MAX_NAMELEN 50
59
60struct perf_evlist *evsel_list;
61
62/*
63 * command line variables and settings
64 * Default to current process, no_inherit, process
65 */
66static pid_t target_pid = -1; /* all */
67static bool system_wide;
68static int cpumask = -1; /* all */
69static int ncounts;
70static int ms_sleep = 1000; /* 1 second */
71static char const *operations = "nnnnnnnnnnnnnnnn"; /* nop */
72static bool math_enabled;
73static bool calc_delta;
74static double old_accum, accum;
75static int math_op_index;
76static char const *wfifo_name = DEFAULT_FIFO_NAME;
77static char const *rfifo_name = DEFAULT_FIFO_NAME;
78static bool use_fifo;
79static bool is_ratio;
80static FILE *fd_in, *fd_out;
81
82static FILE *tReadFifo, *tWriteFifo;
83
84/*
85 * Raw results from perf, we track the current value and
86 * the old value.
87 */
88struct perf_raw_results_s {
89 u64 values;
90 u64 old_value;
91};
92
93/*
94 * Everything we need to support a perf counter across multiple
95 * CPUs. We need to support multiple file descriptors (perf_fd)
96 * because perf requires a fd per counter, so 1 per core enabled.
97 *
98 * Raw results values are calculated across all the cores as they
99 * are read.
100 */
101struct perf_setup_s {
102 int event_index;
103 struct perf_event_attr *attr;
104 int perf_fd[MAX_NR_CPUS];
105 pid_t pid;
106 int cpu;
107 int flags;
108 int group;
109 struct perf_raw_results_s data;
110 struct perf_raw_results_s totals;
111 struct perf_raw_results_s output;
112};
113
114static void do_cleanup(void)
115{
116 if (fd_in) {
117 if (0 != fclose(fd_in))
118 error("Error closing fd_in\n");
119 }
120 if (fd_out) {
121 if (0 != fclose(fd_out))
122 error("Error closing fd_out\n");
123 }
124 if (use_fifo) {
125 if (0 != unlink(rfifo_name))
126 error("Error unlinking rfifo\n");
127 if (0 != unlink(wfifo_name))
128 error("Error unlinking wfifo\n");
129 }
130}
131
132/*
133 * Unexpected signal for error indication, cleanup
134 */
135static int sig_dummy;
136static void sig_do_cleanup(int sig)
137{
138 sig_dummy = sig;
139 do_cleanup();
140 exit(0);
141}
142
143#define PERIODIC_MAX_STRLEN 100
144/*
145 * Delay for either a timed period or the wait on the read_fifo
146 */
147static void delay(unsigned long milli)
148{
149 char tmp_stg[PERIODIC_MAX_STRLEN];
150 int done;
151 int ret;
152
153 if (use_fifo) {
154 do {
155 done = true;
156 ret = fscanf(tReadFifo, "%s", tmp_stg);
157 if (ret == 0)
158 return;
159 /*
160 * Look for a command request, and if we get a command
161 * Need to process and then wait again w/o sending data.
162 */
163 if (strncmp(tmp_stg, "PID", strnlen(tmp_stg,
164 PERIODIC_MAX_STRLEN)) == 0) {
165 fprintf(fd_out, " %u\n", getpid());
166 fflush(fd_out);
167 done = false;
168 } else if (strncmp(tmp_stg, "EXIT",
169 strnlen(tmp_stg, PERIODIC_MAX_STRLEN))
170 == 0) {
171 do_cleanup();
172 exit(0);
173 }
174
175 } while (done != true);
176 } else
177 usleep(milli*1000);
178}
179
180/*
181 * Create a perf counter event.
182 * Some interesting behaviour that is not documented anywhere else:
183 * the CPU will not work if out of range.
184 * The CPU will only work for a single CPU, so to collect the counts
185 * on the system in SMP based systems a counter needs to be created
186 * for each CPU.
187 */
188static int create_perf_counter(struct perf_setup_s *p)
189{
190 struct cpu_map *cpus;
191 int cpu;
192
193 cpus = cpu_map__new(NULL);
194 if (p == NULL)
195 return PERF_PERIODIC_ERROR;
196 for (cpu = 0; cpu < cpus->nr; cpu++) {
Neil Leederfb587022012-02-10 12:50:27 -0500197 if (((1 << cpu) & cpumask) == 0)
198 continue;
Neil Leeder4832fc22011-11-22 16:49:08 -0500199 p->perf_fd[cpu] = sys_perf_event_open(p->attr, target_pid, cpu,
200 -1, 0);
201 if (p->perf_fd[cpu] < 0)
202 return PERF_PERIODIC_ERROR;
203 }
204 return 0;
205}
206
207/*
208 * Perf init setup
209 */
210static int perf_setup_init(struct perf_setup_s *p)
211{
212 if (p == NULL)
213 return PERF_PERIODIC_ERROR;
214
215 bzero(p, sizeof(struct perf_setup_s));
216 p->group = -1;
217 p->flags = 0;
218
219 p->output.values = 0;
220 p->output.old_value = 0;
221 p->data.values = 0;
222 p->data.old_value = 0;
223 p->totals.old_value = 0;
224 p->totals.values = 0;
225
226 return 0;
227}
228
229/*
230 * Read in ALL the performance counters configured for the CPU,
231 * one performance monitor per core that was configured during
232 * "all" mode
233 */
234static int perf_setup_read(struct perf_setup_s *p)
235{
236 u64 data[DATA_SIZE];
237 int i, status;
238
239 p->totals.values = 0;
240 p->data.values = 0;
241 for (i = 0; i < MAX_NR_CPUS; i++) {
242 if (p->perf_fd[i] == 0)
243 continue;
244 status = read(p->perf_fd[i], &data, sizeof(data));
245 p->data.values += data[0];
246 p->totals.values += data[0];
247 }
248
249 /*
250 * Normally we show totals, we want to support
251 * showing deltas from the previous value so external apps do not have
252 * to do this...
253 */
254 if (calc_delta) {
255 p->output.values = p->data.values - p->data.old_value;
256 p->data.old_value = p->data.values;
257 } else
258 p->output.values = p->totals.values;
259 return 0;
260}
261
262static int perf_setup_show(struct perf_setup_s *p)
263{
264 if (p == NULL)
265 return PERF_PERIODIC_ERROR;
266 fprintf(fd_out, " %llu", p->output.values);
267 return 0;
268}
269
270
271static const char * const periodic_usage[] = {
272 "perf periodic [<options>]",
273 NULL
274};
275
276static const struct option options[] = {
277 OPT_CALLBACK('e', "event", &evsel_list, "event",
278 "event selector. use 'perf list' to list available events",
Ashwin Chaugule4235d772012-06-27 17:00:39 -0400279 parse_events_option),
Neil Leeder4832fc22011-11-22 16:49:08 -0500280 OPT_STRING('m', "math-operations", &operations, "nnnnnn",
281 "math operation to perform on values collected asmd in order"),
282 OPT_STRING('r', "readpipe", &rfifo_name, "xxbadFiFo",
283 "wait for a user input fifo - will be created"),
284 OPT_STRING('w', "writepipe", &wfifo_name, "xxbadFifo",
285 "write data out on this pipe - pipe is created"),
286 OPT_INTEGER('i', "increment", &ncounts,
287 "number of times periods to count/iterate (default 0-forever)"),
288 OPT_INTEGER('p', "pid", &target_pid,
289 "stat events on existing process id"),
290 OPT_INTEGER('c', "cpumask", &cpumask,
291 "cpumask to enable counters, default all (-1)"),
292 OPT_INTEGER('s', "sleep", &ms_sleep,
293 "how long to sleep in ms between each sample (default 1000)"),
294 OPT_BOOLEAN('a', "all-cpus", &system_wide,
295 "system-wide collection from all CPUs overrides cpumask"),
296 OPT_BOOLEAN('d', "delta", &calc_delta,
297 "calculate and display the delta values math funcs will use delta"),
298 OPT_INCR('v', "verbose", &verbose,
299 "be more verbose (show counter open errors, etc)"),
300 OPT_END()
301};
302
303/*
304 * After every period we reset any math that was performed.
305 */
306static void reset_math(void)
307{
308 math_op_index = 0;
309 old_accum = accum;
310 accum = 0;
311}
312
313static void do_math_op(struct perf_setup_s *p)
314{
315 if (!math_enabled)
316 return;
317 switch (operations[math_op_index++]) {
318 case 'm':
319 accum *= (double)p->output.values; break;
320 case 'a':
321 accum += (double)p->output.values; break;
322 case 's':
323 accum -= (double)p->output.values; break;
324 case 'd':
325 accum /= (double)p->output.values; break;
326 case 'z':
327 accum = 0; break;
328 case 't':
329 accum = (double)p->output.values; break; /*transfer*/
330 case 'T':
331 accum += old_accum; break; /*total*/
332 case 'i': /* ignore */
333 default:
334 break;
335 }
336}
337
338int cmd_periodic(int argc, const char **argv, const char *prefix __used)
339{
340 int status = 0;
341 int c, i;
342 struct perf_setup_s *p[MAX_COUNTERS];
343 struct perf_evsel *counter;
344 FILE *fp;
345 int nr_counters = 0;
346
347 evsel_list = perf_evlist__new(NULL, NULL);
348 if (evsel_list == NULL)
349 return -ENOMEM;
350
351 argc = parse_options(argc, argv, options, periodic_usage,
352 PARSE_OPT_STOP_AT_NON_OPTION);
353
354 if (system_wide)
355 cpumask = -1;
356
357 /*
358 * The r & w option redirects stdout to a newly created pipe and
359 * waits for input on the read pipe before continuing
360 */
361 fd_in = stdin;
362 fd_out = stdout;
363 if (strncmp(rfifo_name, DEFAULT_FIFO_NAME,
364 strnlen(rfifo_name, MAX_NAMELEN))) {
365 fp = fopen(rfifo_name, "r");
366 if (fp != NULL) {
367 fclose(fp);
368 remove(rfifo_name);
369 }
370 if (mkfifo(rfifo_name, 0777) == -1) {
371 error("Could not open read fifo\n");
372 do_cleanup();
373 return PERF_PERIODIC_ERROR;
374 }
375 tReadFifo = fopen(rfifo_name, "r+");
376 if (tReadFifo == 0) {
377 do_cleanup();
378 error("Could not open read fifo file\n");
379 return PERF_PERIODIC_ERROR;
380 }
381 use_fifo = true;
382 }
383 if (strncmp(wfifo_name, DEFAULT_FIFO_NAME,
384 strnlen(wfifo_name, MAX_NAMELEN))) {
385 fp = fopen(wfifo_name, "r");
386 if (fp != NULL) {
387 fclose(fp);
388 remove(wfifo_name);
389 }
390 if (mkfifo(wfifo_name, 0777) == -1) {
391 do_cleanup();
392 error("Could not open write fifo\n");
393 return PERF_PERIODIC_ERROR;
394 }
395 fd_out = fopen(wfifo_name, "w+");
396 if (fd_out == 0) {
397 do_cleanup();
398 error("Could not open write fifo file\n");
399 return PERF_PERIODIC_ERROR;
400 }
401 tWriteFifo = fd_out;
402 }
403
404 math_enabled = (operations[0] != 'n');
405
406 /*
407 * If we don't ignore SIG_PIPE then when the other side
408 * of a pipe closes we shutdown too...
409 */
410 signal(SIGPIPE, SIG_IGN);
411 signal(SIGINT, sig_do_cleanup);
412 signal(SIGQUIT, sig_do_cleanup);
413 signal(SIGKILL, sig_do_cleanup);
414 signal(SIGTERM, sig_do_cleanup);
415
416 i = 0;
417 list_for_each_entry(counter, &evsel_list->entries, node) {
418 p[i] = malloc(sizeof(struct perf_setup_s));
419 if (p[i] == NULL) {
420 error("Error allocating perf_setup_s\n");
421 do_cleanup();
422 return PERF_PERIODIC_ERROR;
423 }
424 bzero(p[i], sizeof(struct perf_setup_s));
425 perf_setup_init(p[i]);
426 p[i]->attr = &(counter->attr);
427 p[i]->event_index = counter->idx;
428 if (create_perf_counter(p[i]) < 0) {
429 do_cleanup();
430 die("Not all events could be opened.\n");
431 return PERF_PERIODIC_ERROR;
432 }
433 i++;
434 nr_counters++;
435 }
436 i = 0;
437 while (1) {
438
439 /*
440 * Wait first otherwise single sample will print w/o signal
441 * when using the -u (user signal) flag
442 */
443 delay(ms_sleep);
444
445 /*
446 * Do the collection, read and then perform any math operations
447 */
448 for (c = 0; c < nr_counters; c++) {
449 status = perf_setup_read(p[c]);
450 do_math_op(p[c]);
451 }
452
453 /*
454 * After all collection and math, we perform one last math
455 * to allow totaling, if enabled etc, then either printout
456 * a single float value when the math is enabled or ...
457 */
458 if (math_enabled) {
459 do_math_op(p[c]);
460 if (is_ratio)
461 fprintf(fd_out, "%#f\n", accum*100);
462 else
463 fprintf(fd_out, "%#f\n", accum);
464 } else {
465 /*
466 * ... print out one integer value for each counter
467 */
468 for (c = 0; c < nr_counters; c++)
469 status = perf_setup_show(p[c]);
470 fprintf(fd_out, "\n");
471 }
472
473 /*
474 * Did the user give us an iteration count?
475 */
476 if ((ncounts != 0) && (++i >= ncounts))
477 break;
478 reset_math();
479 fflush(fd_out); /* make sure data is flushed out the pipe*/
480 }
481
482 do_cleanup();
483
484 return status;
485}