diff options
Diffstat (limited to 'tools/perf/builtin-stat.c')
-rw-r--r-- | tools/perf/builtin-stat.c | 156 |
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; |