summaryrefslogtreecommitdiffstats
path: root/tools/perf/builtin-stat.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/builtin-stat.c')
-rw-r--r--tools/perf/builtin-stat.c156
1 files changed, 136 insertions, 20 deletions
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
index 54a4c152edb3..f17dc601b0f3 100644
--- a/tools/perf/builtin-stat.c
+++ b/tools/perf/builtin-stat.c
@@ -168,9 +168,11 @@ static struct timespec ref_time;
static struct cpu_map *aggr_map;
static aggr_get_id_t aggr_get_id;
static bool append_file;
+static bool interval_count;
static const char *output_name;
static int output_fd;
static int print_free_counters_hint;
+static int print_mixed_hw_group_error;
struct perf_stat {
bool record;
@@ -507,14 +509,13 @@ static int perf_stat_synthesize_config(bool is_pipe)
#define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y))
-static int __store_counter_ids(struct perf_evsel *counter,
- struct cpu_map *cpus,
- struct thread_map *threads)
+static int __store_counter_ids(struct perf_evsel *counter)
{
int cpu, thread;
- for (cpu = 0; cpu < cpus->nr; cpu++) {
- for (thread = 0; thread < threads->nr; thread++) {
+ for (cpu = 0; cpu < xyarray__max_x(counter->fd); cpu++) {
+ for (thread = 0; thread < xyarray__max_y(counter->fd);
+ thread++) {
int fd = FD(counter, cpu, thread);
if (perf_evlist__id_add_fd(evsel_list, counter,
@@ -534,7 +535,7 @@ static int store_counter_ids(struct perf_evsel *counter)
if (perf_evsel__alloc_id(counter, cpus->nr, threads->nr))
return -ENOMEM;
- return __store_counter_ids(counter, cpus, threads);
+ return __store_counter_ids(counter);
}
static bool perf_evsel__should_store_id(struct perf_evsel *counter)
@@ -571,6 +572,8 @@ static struct perf_evsel *perf_evsel__reset_weak_group(struct perf_evsel *evsel)
static int __run_perf_stat(int argc, const char **argv)
{
int interval = stat_config.interval;
+ int times = stat_config.times;
+ int timeout = stat_config.timeout;
char msg[BUFSIZ];
unsigned long long t0, t1;
struct perf_evsel *counter;
@@ -584,6 +587,9 @@ static int __run_perf_stat(int argc, const char **argv)
if (interval) {
ts.tv_sec = interval / USEC_PER_MSEC;
ts.tv_nsec = (interval % USEC_PER_MSEC) * NSEC_PER_MSEC;
+ } else if (timeout) {
+ ts.tv_sec = timeout / USEC_PER_MSEC;
+ ts.tv_nsec = (timeout % USEC_PER_MSEC) * NSEC_PER_MSEC;
} else {
ts.tv_sec = 1;
ts.tv_nsec = 0;
@@ -632,7 +638,19 @@ try_again:
if (verbose > 0)
ui__warning("%s\n", msg);
goto try_again;
- }
+ } else if (target__has_per_thread(&target) &&
+ evsel_list->threads &&
+ evsel_list->threads->err_thread != -1) {
+ /*
+ * For global --per-thread case, skip current
+ * error thread.
+ */
+ if (!thread_map__remove(evsel_list->threads,
+ evsel_list->threads->err_thread)) {
+ evsel_list->threads->err_thread = -1;
+ goto try_again;
+ }
+ }
perf_evsel__open_strerror(counter, &target,
errno, msg, sizeof(msg));
@@ -696,10 +714,14 @@ try_again:
perf_evlist__start_workload(evsel_list);
enable_counters();
- if (interval) {
+ if (interval || timeout) {
while (!waitpid(child_pid, &status, WNOHANG)) {
nanosleep(&ts, NULL);
+ if (timeout)
+ break;
process_interval();
+ if (interval_count && !(--times))
+ break;
}
}
waitpid(child_pid, &status, 0);
@@ -716,8 +738,13 @@ try_again:
enable_counters();
while (!done) {
nanosleep(&ts, NULL);
- if (interval)
+ if (timeout)
+ break;
+ if (interval) {
process_interval();
+ if (interval_count && !(--times))
+ break;
+ }
}
}
@@ -1100,6 +1127,30 @@ static void abs_printout(int id, int nr, struct perf_evsel *evsel, double avg)
fprintf(output, "%s%s", csv_sep, evsel->cgrp->name);
}
+static bool is_mixed_hw_group(struct perf_evsel *counter)
+{
+ struct perf_evlist *evlist = counter->evlist;
+ u32 pmu_type = counter->attr.type;
+ struct perf_evsel *pos;
+
+ if (counter->nr_members < 2)
+ return false;
+
+ evlist__for_each_entry(evlist, pos) {
+ /* software events can be part of any hardware group */
+ if (pos->attr.type == PERF_TYPE_SOFTWARE)
+ continue;
+ if (pmu_type == PERF_TYPE_SOFTWARE) {
+ pmu_type = pos->attr.type;
+ continue;
+ }
+ if (pmu_type != pos->attr.type)
+ return true;
+ }
+
+ return false;
+}
+
static void printout(int id, int nr, struct perf_evsel *counter, double uval,
char *prefix, u64 run, u64 ena, double noise,
struct runtime_stat *st)
@@ -1152,8 +1203,11 @@ static void printout(int id, int nr, struct perf_evsel *counter, double uval,
counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED,
csv_sep);
- if (counter->supported)
+ if (counter->supported) {
print_free_counters_hint = 1;
+ if (is_mixed_hw_group(counter))
+ print_mixed_hw_group_error = 1;
+ }
fprintf(stat_config.output, "%-*s%s",
csv_output ? 0 : unit_width,
@@ -1225,6 +1279,34 @@ static void aggr_update_shadow(void)
}
}
+static void uniquify_event_name(struct perf_evsel *counter)
+{
+ char *new_name;
+ char *config;
+
+ if (counter->uniquified_name ||
+ !counter->pmu_name || !strncmp(counter->name, counter->pmu_name,
+ strlen(counter->pmu_name)))
+ return;
+
+ config = strchr(counter->name, '/');
+ if (config) {
+ if (asprintf(&new_name,
+ "%s%s", counter->pmu_name, config) > 0) {
+ free(counter->name);
+ counter->name = new_name;
+ }
+ } else {
+ if (asprintf(&new_name,
+ "%s [%s]", counter->name, counter->pmu_name) > 0) {
+ free(counter->name);
+ counter->name = new_name;
+ }
+ }
+
+ counter->uniquified_name = true;
+}
+
static void collect_all_aliases(struct perf_evsel *counter,
void (*cb)(struct perf_evsel *counter, void *data,
bool first),
@@ -1253,7 +1335,9 @@ static bool collect_data(struct perf_evsel *counter,
if (counter->merged_stat)
return false;
cb(counter, data, true);
- if (!no_merge && counter->auto_merge_stats)
+ if (no_merge)
+ uniquify_event_name(counter);
+ else if (counter->auto_merge_stats)
collect_all_aliases(counter, cb, data);
return true;
}
@@ -1704,6 +1788,11 @@ static void print_footer(void)
" echo 0 > /proc/sys/kernel/nmi_watchdog\n"
" perf stat ...\n"
" echo 1 > /proc/sys/kernel/nmi_watchdog\n");
+
+ if (print_mixed_hw_group_error)
+ fprintf(output,
+ "The events in group usually have to be from "
+ "the same PMU. Try reorganizing the group.\n");
}
static void print_counters(struct timespec *ts, int argc, const char **argv)
@@ -1890,7 +1979,12 @@ static const struct option stat_options[] = {
OPT_STRING(0, "post", &post_cmd, "command",
"command to run after to the measured command"),
OPT_UINTEGER('I', "interval-print", &stat_config.interval,
- "print counts at regular interval in ms (>= 10)"),
+ "print counts at regular interval in ms "
+ "(overhead is possible for values <= 100ms)"),
+ OPT_INTEGER(0, "interval-count", &stat_config.times,
+ "print counts for fixed number of times"),
+ OPT_UINTEGER(0, "timeout", &stat_config.timeout,
+ "stop workload and print counts after a timeout period in ms (>= 10ms)"),
OPT_SET_UINT(0, "per-socket", &stat_config.aggr_mode,
"aggregate counts per processor socket", AGGR_SOCKET),
OPT_SET_UINT(0, "per-core", &stat_config.aggr_mode,
@@ -2274,11 +2368,16 @@ static int add_default_attributes(void)
return 0;
if (transaction_run) {
+ struct parse_events_error errinfo;
+
if (pmu_have_event("cpu", "cycles-ct") &&
pmu_have_event("cpu", "el-start"))
- err = parse_events(evsel_list, transaction_attrs, NULL);
+ err = parse_events(evsel_list, transaction_attrs,
+ &errinfo);
else
- err = parse_events(evsel_list, transaction_limited_attrs, NULL);
+ err = parse_events(evsel_list,
+ transaction_limited_attrs,
+ &errinfo);
if (err) {
fprintf(stderr, "Cannot set up transaction events\n");
return -1;
@@ -2688,7 +2787,7 @@ int cmd_stat(int argc, const char **argv)
int status = -EINVAL, run_idx;
const char *mode;
FILE *output = stderr;
- unsigned int interval;
+ unsigned int interval, timeout;
const char * const stat_subcommands[] = { "record", "report" };
setlocale(LC_ALL, "");
@@ -2719,6 +2818,7 @@ int cmd_stat(int argc, const char **argv)
return __cmd_report(argc, argv);
interval = stat_config.interval;
+ timeout = stat_config.timeout;
/*
* For record command the -o is already taken care of.
@@ -2860,16 +2960,32 @@ int cmd_stat(int argc, const char **argv)
}
}
- if (interval && interval < 100) {
- if (interval < 10) {
- pr_err("print interval must be >= 10ms\n");
- parse_options_usage(stat_usage, stat_options, "I", 1);
+ if (stat_config.times && interval)
+ interval_count = true;
+ else if (stat_config.times && !interval) {
+ pr_err("interval-count option should be used together with "
+ "interval-print.\n");
+ parse_options_usage(stat_usage, stat_options, "interval-count", 0);
+ parse_options_usage(stat_usage, stat_options, "I", 1);
+ goto out;
+ }
+
+ if (timeout && timeout < 100) {
+ if (timeout < 10) {
+ pr_err("timeout must be >= 10ms.\n");
+ parse_options_usage(stat_usage, stat_options, "timeout", 0);
goto out;
} else
- pr_warning("print interval < 100ms. "
+ pr_warning("timeout < 100ms. "
"The overhead percentage could be high in some cases. "
"Please proceed with caution.\n");
}
+ if (timeout && interval) {
+ pr_err("timeout option is not supported with interval-print.\n");
+ parse_options_usage(stat_usage, stat_options, "timeout", 0);
+ parse_options_usage(stat_usage, stat_options, "I", 1);
+ goto out;
+ }
if (perf_evlist__alloc_stats(evsel_list, interval))
goto out;
OpenPOWER on IntegriCloud