123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486 |
- /*
- * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
- /*
- * A very simple perf program to periodically print the performance
- * counter reqested on the command line to standard out at the rate
- * specified.
- *
- * This is valuable for showing the output in a simple plot or
- * exporting the counter data for post processing. No attempt
- * to process the data is made.
- *
- * Scaling is not supported, use only as many counters as are
- * provided by the hardware.
- *
- * Math functions are support to combine counter results by using
- * the -m flag.
- *
- * The -r -w flags supports user signalling for input. This assumes
- * that a pipe/fifo is needed so the -rw cmd line arg is a string
- * that is the name of the named pipe to open for read/write. User
- * sends data on the read pipe to the process to collect a sample.
- * Commands are also supported on the pipe.
- *
- */
- #include "perf.h"
- #include "builtin.h"
- #include "util/util.h"
- #include "util/parse-options.h"
- #include "util/parse-events.h"
- #include "util/event.h"
- #include "util/evsel.h"
- #include "util/evlist.h"
- #include "util/debug.h"
- #include "util/header.h"
- #include "util/cpumap.h"
- #include "util/thread.h"
- #include <signal.h>
- #include <sys/types.h>
- #define PERF_PERIODIC_ERROR -1
- /* number of pieces of data on each read. */
- #define DATA_SIZE 2
- #define DEFAULT_FIFO_NAME "xxbadFiFo"
- #define MAX_NAMELEN 50
- struct perf_evlist *evsel_list;
- /*
- * command line variables and settings
- * Default to current process, no_inherit, process
- */
- static pid_t target_pid = -1; /* all */
- static bool system_wide;
- static int cpumask = -1; /* all */
- static int ncounts;
- static int ms_sleep = 1000; /* 1 second */
- static char const *operations = "nnnnnnnnnnnnnnnn"; /* nop */
- static bool math_enabled;
- static bool calc_delta;
- static double old_accum, accum;
- static int math_op_index;
- static char const *wfifo_name = DEFAULT_FIFO_NAME;
- static char const *rfifo_name = DEFAULT_FIFO_NAME;
- static bool use_fifo;
- static bool is_ratio;
- static FILE *fd_in, *fd_out;
- static FILE *tReadFifo, *tWriteFifo;
- /*
- * Raw results from perf, we track the current value and
- * the old value.
- */
- struct perf_raw_results_s {
- u64 values;
- u64 old_value;
- };
- /*
- * Everything we need to support a perf counter across multiple
- * CPUs. We need to support multiple file descriptors (perf_fd)
- * because perf requires a fd per counter, so 1 per core enabled.
- *
- * Raw results values are calculated across all the cores as they
- * are read.
- */
- struct perf_setup_s {
- int event_index;
- struct perf_event_attr *attr;
- int perf_fd[MAX_NR_CPUS];
- pid_t pid;
- int cpu;
- int flags;
- int group;
- struct perf_raw_results_s data;
- struct perf_raw_results_s totals;
- struct perf_raw_results_s output;
- };
- static void do_cleanup(void)
- {
- if (fd_in) {
- if (0 != fclose(fd_in))
- error("Error closing fd_in\n");
- }
- if (fd_out) {
- if (0 != fclose(fd_out))
- error("Error closing fd_out\n");
- }
- if (use_fifo) {
- if (0 != unlink(rfifo_name))
- error("Error unlinking rfifo\n");
- if (0 != unlink(wfifo_name))
- error("Error unlinking wfifo\n");
- }
- }
- /*
- * Unexpected signal for error indication, cleanup
- */
- static int sig_dummy;
- static void sig_do_cleanup(int sig)
- {
- sig_dummy = sig;
- do_cleanup();
- exit(0);
- }
- #define PERIODIC_MAX_STRLEN 100
- /*
- * Delay for either a timed period or the wait on the read_fifo
- */
- static void delay(unsigned long milli)
- {
- char tmp_stg[PERIODIC_MAX_STRLEN];
- int done;
- int ret;
- if (use_fifo) {
- do {
- done = true;
- ret = fscanf(tReadFifo, "%s", tmp_stg);
- if (ret == 0)
- return;
- /*
- * Look for a command request, and if we get a command
- * Need to process and then wait again w/o sending data.
- */
- if (strncmp(tmp_stg, "PID", strnlen(tmp_stg,
- PERIODIC_MAX_STRLEN)) == 0) {
- fprintf(fd_out, " %u\n", getpid());
- fflush(fd_out);
- done = false;
- } else if (strncmp(tmp_stg, "EXIT",
- strnlen(tmp_stg, PERIODIC_MAX_STRLEN))
- == 0) {
- do_cleanup();
- exit(0);
- }
- } while (done != true);
- } else
- usleep(milli*1000);
- }
- /*
- * Create a perf counter event.
- * Some interesting behaviour that is not documented anywhere else:
- * the CPU will not work if out of range.
- * The CPU will only work for a single CPU, so to collect the counts
- * on the system in SMP based systems a counter needs to be created
- * for each CPU.
- */
- static int create_perf_counter(struct perf_setup_s *p)
- {
- struct cpu_map *cpus;
- int cpu;
- cpus = cpu_map__new(NULL);
- if (p == NULL)
- return PERF_PERIODIC_ERROR;
- for (cpu = 0; cpu < cpus->nr; cpu++) {
- if (((1 << cpu) & cpumask) == 0)
- continue;
- p->perf_fd[cpu] = sys_perf_event_open(p->attr, target_pid, cpu,
- -1, 0);
- if (p->perf_fd[cpu] < 0)
- return PERF_PERIODIC_ERROR;
- }
- return 0;
- }
- /*
- * Perf init setup
- */
- static int perf_setup_init(struct perf_setup_s *p)
- {
- if (p == NULL)
- return PERF_PERIODIC_ERROR;
- bzero(p, sizeof(struct perf_setup_s));
- p->group = -1;
- p->flags = 0;
- p->output.values = 0;
- p->output.old_value = 0;
- p->data.values = 0;
- p->data.old_value = 0;
- p->totals.old_value = 0;
- p->totals.values = 0;
- return 0;
- }
- /*
- * Read in ALL the performance counters configured for the CPU,
- * one performance monitor per core that was configured during
- * "all" mode
- */
- static int perf_setup_read(struct perf_setup_s *p)
- {
- u64 data[DATA_SIZE];
- int i, status;
- p->totals.values = 0;
- p->data.values = 0;
- for (i = 0; i < MAX_NR_CPUS; i++) {
- if (p->perf_fd[i] == 0)
- continue;
- status = read(p->perf_fd[i], &data, sizeof(data));
- p->data.values += data[0];
- p->totals.values += data[0];
- }
- /*
- * Normally we show totals, we want to support
- * showing deltas from the previous value so external apps do not have
- * to do this...
- */
- if (calc_delta) {
- p->output.values = p->data.values - p->data.old_value;
- p->data.old_value = p->data.values;
- } else
- p->output.values = p->totals.values;
- return 0;
- }
- static int perf_setup_show(struct perf_setup_s *p)
- {
- if (p == NULL)
- return PERF_PERIODIC_ERROR;
- fprintf(fd_out, " %llu", p->output.values);
- return 0;
- }
- static const char * const periodic_usage[] = {
- "perf periodic [<options>]",
- NULL
- };
- static const struct option options[] = {
- OPT_CALLBACK('e', "event", &evsel_list, "event",
- "event selector. use 'perf list' to list available events",
- parse_events_option),
- OPT_STRING('m', "math-operations", &operations, "nnnnnn",
- "math operation to perform on values collected asmd in order"),
- OPT_STRING('r', "readpipe", &rfifo_name, "xxbadFiFo",
- "wait for a user input fifo - will be created"),
- OPT_STRING('w', "writepipe", &wfifo_name, "xxbadFifo",
- "write data out on this pipe - pipe is created"),
- OPT_INTEGER('i', "increment", &ncounts,
- "number of times periods to count/iterate (default 0-forever)"),
- OPT_INTEGER('p', "pid", &target_pid,
- "stat events on existing process id"),
- OPT_INTEGER('c', "cpumask", &cpumask,
- "cpumask to enable counters, default all (-1)"),
- OPT_INTEGER('s', "sleep", &ms_sleep,
- "how long to sleep in ms between each sample (default 1000)"),
- OPT_BOOLEAN('a', "all-cpus", &system_wide,
- "system-wide collection from all CPUs overrides cpumask"),
- OPT_BOOLEAN('d', "delta", &calc_delta,
- "calculate and display the delta values math funcs will use delta"),
- OPT_INCR('v', "verbose", &verbose,
- "be more verbose (show counter open errors, etc)"),
- OPT_END()
- };
- /*
- * After every period we reset any math that was performed.
- */
- static void reset_math(void)
- {
- math_op_index = 0;
- old_accum = accum;
- accum = 0;
- }
- static void do_math_op(struct perf_setup_s *p)
- {
- if (!math_enabled)
- return;
- switch (operations[math_op_index++]) {
- case 'm':
- accum *= (double)p->output.values; break;
- case 'a':
- accum += (double)p->output.values; break;
- case 's':
- accum -= (double)p->output.values; break;
- case 'd':
- accum /= (double)p->output.values; break;
- case 'z':
- accum = 0; break;
- case 't':
- accum = (double)p->output.values; break; /*transfer*/
- case 'T':
- accum += old_accum; break; /*total*/
- case 'i': /* ignore */
- default:
- break;
- }
- }
- int cmd_periodic(int argc, const char **argv, const char *prefix __used)
- {
- int status = 0;
- int c, i;
- struct perf_setup_s *p[MAX_COUNTERS];
- struct perf_evsel *counter;
- FILE *fp;
- int nr_counters = 0;
- evsel_list = perf_evlist__new(NULL, NULL);
- if (evsel_list == NULL)
- return -ENOMEM;
- argc = parse_options(argc, argv, options, periodic_usage,
- PARSE_OPT_STOP_AT_NON_OPTION);
- if (system_wide)
- cpumask = -1;
- /*
- * The r & w option redirects stdout to a newly created pipe and
- * waits for input on the read pipe before continuing
- */
- fd_in = stdin;
- fd_out = stdout;
- if (strncmp(rfifo_name, DEFAULT_FIFO_NAME,
- strnlen(rfifo_name, MAX_NAMELEN))) {
- fp = fopen(rfifo_name, "r");
- if (fp != NULL) {
- fclose(fp);
- remove(rfifo_name);
- }
- if (mkfifo(rfifo_name, 0777) == -1) {
- error("Could not open read fifo\n");
- do_cleanup();
- return PERF_PERIODIC_ERROR;
- }
- tReadFifo = fopen(rfifo_name, "r+");
- if (tReadFifo == 0) {
- do_cleanup();
- error("Could not open read fifo file\n");
- return PERF_PERIODIC_ERROR;
- }
- use_fifo = true;
- }
- if (strncmp(wfifo_name, DEFAULT_FIFO_NAME,
- strnlen(wfifo_name, MAX_NAMELEN))) {
- fp = fopen(wfifo_name, "r");
- if (fp != NULL) {
- fclose(fp);
- remove(wfifo_name);
- }
- if (mkfifo(wfifo_name, 0777) == -1) {
- do_cleanup();
- error("Could not open write fifo\n");
- return PERF_PERIODIC_ERROR;
- }
- fd_out = fopen(wfifo_name, "w+");
- if (fd_out == 0) {
- do_cleanup();
- error("Could not open write fifo file\n");
- return PERF_PERIODIC_ERROR;
- }
- tWriteFifo = fd_out;
- }
- math_enabled = (operations[0] != 'n');
- /*
- * If we don't ignore SIG_PIPE then when the other side
- * of a pipe closes we shutdown too...
- */
- signal(SIGPIPE, SIG_IGN);
- signal(SIGINT, sig_do_cleanup);
- signal(SIGQUIT, sig_do_cleanup);
- signal(SIGKILL, sig_do_cleanup);
- signal(SIGTERM, sig_do_cleanup);
- i = 0;
- list_for_each_entry(counter, &evsel_list->entries, node) {
- p[i] = malloc(sizeof(struct perf_setup_s));
- if (p[i] == NULL) {
- error("Error allocating perf_setup_s\n");
- do_cleanup();
- return PERF_PERIODIC_ERROR;
- }
- bzero(p[i], sizeof(struct perf_setup_s));
- perf_setup_init(p[i]);
- p[i]->attr = &(counter->attr);
- p[i]->event_index = counter->idx;
- if (create_perf_counter(p[i]) < 0) {
- do_cleanup();
- die("Not all events could be opened.\n");
- return PERF_PERIODIC_ERROR;
- }
- i++;
- nr_counters++;
- }
- i = 0;
- while (1) {
- /*
- * Wait first otherwise single sample will print w/o signal
- * when using the -u (user signal) flag
- */
- delay(ms_sleep);
- /*
- * Do the collection, read and then perform any math operations
- */
- for (c = 0; c < nr_counters; c++) {
- status = perf_setup_read(p[c]);
- do_math_op(p[c]);
- }
- /*
- * After all collection and math, we perform one last math
- * to allow totaling, if enabled etc, then either printout
- * a single float value when the math is enabled or ...
- */
- if (math_enabled) {
- do_math_op(p[c]);
- if (is_ratio)
- fprintf(fd_out, "%#f\n", accum*100);
- else
- fprintf(fd_out, "%#f\n", accum);
- } else {
- /*
- * ... print out one integer value for each counter
- */
- for (c = 0; c < nr_counters; c++)
- status = perf_setup_show(p[c]);
- fprintf(fd_out, "\n");
- }
- /*
- * Did the user give us an iteration count?
- */
- if ((ncounts != 0) && (++i >= ncounts))
- break;
- reset_math();
- fflush(fd_out); /* make sure data is flushed out the pipe*/
- }
- do_cleanup();
- return status;
- }
|