| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * builtin-stat.c |
|---|
| 3 | 4 | * |
|---|
| .. | .. |
|---|
| 37 | 38 | * Mike Galbraith <efault@gmx.de> |
|---|
| 38 | 39 | * Paul Mackerras <paulus@samba.org> |
|---|
| 39 | 40 | * Jaswinder Singh Rajput <jaswinder@kernel.org> |
|---|
| 40 | | - * |
|---|
| 41 | | - * Released under the GPL v2. (and only v2, not any later version) |
|---|
| 42 | 41 | */ |
|---|
| 43 | 42 | |
|---|
| 44 | | -#include "perf.h" |
|---|
| 45 | 43 | #include "builtin.h" |
|---|
| 44 | +#include "perf.h" |
|---|
| 46 | 45 | #include "util/cgroup.h" |
|---|
| 47 | | -#include "util/util.h" |
|---|
| 48 | 46 | #include <subcmd/parse-options.h> |
|---|
| 49 | 47 | #include "util/parse-events.h" |
|---|
| 50 | 48 | #include "util/pmu.h" |
|---|
| .. | .. |
|---|
| 52 | 50 | #include "util/evlist.h" |
|---|
| 53 | 51 | #include "util/evsel.h" |
|---|
| 54 | 52 | #include "util/debug.h" |
|---|
| 55 | | -#include "util/drv_configs.h" |
|---|
| 56 | 53 | #include "util/color.h" |
|---|
| 57 | 54 | #include "util/stat.h" |
|---|
| 58 | 55 | #include "util/header.h" |
|---|
| 59 | 56 | #include "util/cpumap.h" |
|---|
| 60 | | -#include "util/thread.h" |
|---|
| 61 | 57 | #include "util/thread_map.h" |
|---|
| 62 | 58 | #include "util/counts.h" |
|---|
| 63 | | -#include "util/group.h" |
|---|
| 59 | +#include "util/topdown.h" |
|---|
| 64 | 60 | #include "util/session.h" |
|---|
| 65 | 61 | #include "util/tool.h" |
|---|
| 66 | 62 | #include "util/string2.h" |
|---|
| 67 | 63 | #include "util/metricgroup.h" |
|---|
| 64 | +#include "util/synthetic-events.h" |
|---|
| 65 | +#include "util/target.h" |
|---|
| 66 | +#include "util/time-utils.h" |
|---|
| 68 | 67 | #include "util/top.h" |
|---|
| 68 | +#include "util/affinity.h" |
|---|
| 69 | +#include "util/pfm.h" |
|---|
| 69 | 70 | #include "asm/bug.h" |
|---|
| 70 | 71 | |
|---|
| 71 | 72 | #include <linux/time64.h> |
|---|
| 73 | +#include <linux/zalloc.h> |
|---|
| 72 | 74 | #include <api/fs/fs.h> |
|---|
| 73 | 75 | #include <errno.h> |
|---|
| 74 | 76 | #include <signal.h> |
|---|
| .. | .. |
|---|
| 83 | 85 | #include <unistd.h> |
|---|
| 84 | 86 | #include <sys/time.h> |
|---|
| 85 | 87 | #include <sys/resource.h> |
|---|
| 86 | | -#include <sys/wait.h> |
|---|
| 88 | +#include <linux/err.h> |
|---|
| 87 | 89 | |
|---|
| 88 | | -#include "sane_ctype.h" |
|---|
| 90 | +#include <linux/ctype.h> |
|---|
| 91 | +#include <perf/evlist.h> |
|---|
| 89 | 92 | |
|---|
| 90 | 93 | #define DEFAULT_SEPARATOR " " |
|---|
| 91 | | -#define CNTR_NOT_SUPPORTED "<not supported>" |
|---|
| 92 | | -#define CNTR_NOT_COUNTED "<not counted>" |
|---|
| 93 | 94 | #define FREEZE_ON_SMI_PATH "devices/cpu/freeze_on_smi" |
|---|
| 94 | 95 | |
|---|
| 95 | 96 | static void print_counters(struct timespec *ts, int argc, const char **argv); |
|---|
| .. | .. |
|---|
| 127 | 128 | NULL, |
|---|
| 128 | 129 | }; |
|---|
| 129 | 130 | |
|---|
| 131 | +static const char *topdown_metric_attrs[] = { |
|---|
| 132 | + "slots", |
|---|
| 133 | + "topdown-retiring", |
|---|
| 134 | + "topdown-bad-spec", |
|---|
| 135 | + "topdown-fe-bound", |
|---|
| 136 | + "topdown-be-bound", |
|---|
| 137 | + NULL, |
|---|
| 138 | +}; |
|---|
| 139 | + |
|---|
| 130 | 140 | static const char *smi_cost_attrs = { |
|---|
| 131 | 141 | "{" |
|---|
| 132 | 142 | "msr/aperf/," |
|---|
| .. | .. |
|---|
| 135 | 145 | "}" |
|---|
| 136 | 146 | }; |
|---|
| 137 | 147 | |
|---|
| 138 | | -static struct perf_evlist *evsel_list; |
|---|
| 139 | | - |
|---|
| 140 | | -static struct rblist metric_events; |
|---|
| 148 | +static struct evlist *evsel_list; |
|---|
| 141 | 149 | |
|---|
| 142 | 150 | static struct target target = { |
|---|
| 143 | 151 | .uid = UINT_MAX, |
|---|
| 144 | 152 | }; |
|---|
| 145 | 153 | |
|---|
| 146 | | -typedef int (*aggr_get_id_t)(struct cpu_map *m, int cpu); |
|---|
| 147 | | - |
|---|
| 148 | 154 | #define METRIC_ONLY_LEN 20 |
|---|
| 149 | 155 | |
|---|
| 150 | | -static int run_count = 1; |
|---|
| 151 | | -static bool no_inherit = false; |
|---|
| 152 | 156 | static volatile pid_t child_pid = -1; |
|---|
| 153 | | -static bool null_run = false; |
|---|
| 154 | 157 | static int detailed_run = 0; |
|---|
| 155 | 158 | static bool transaction_run; |
|---|
| 156 | 159 | static bool topdown_run = false; |
|---|
| 157 | 160 | static bool smi_cost = false; |
|---|
| 158 | 161 | static bool smi_reset = false; |
|---|
| 159 | | -static bool big_num = true; |
|---|
| 160 | 162 | static int big_num_opt = -1; |
|---|
| 161 | | -static const char *csv_sep = NULL; |
|---|
| 162 | | -static bool csv_output = false; |
|---|
| 163 | 163 | static bool group = false; |
|---|
| 164 | 164 | static const char *pre_cmd = NULL; |
|---|
| 165 | 165 | static const char *post_cmd = NULL; |
|---|
| 166 | 166 | static bool sync_run = false; |
|---|
| 167 | | -static unsigned int initial_delay = 0; |
|---|
| 168 | | -static unsigned int unit_width = 4; /* strlen("unit") */ |
|---|
| 169 | 167 | static bool forever = false; |
|---|
| 170 | | -static bool metric_only = false; |
|---|
| 171 | 168 | static bool force_metric_only = false; |
|---|
| 172 | | -static bool no_merge = false; |
|---|
| 173 | | -static bool walltime_run_table = false; |
|---|
| 174 | 169 | static struct timespec ref_time; |
|---|
| 175 | | -static struct cpu_map *aggr_map; |
|---|
| 176 | | -static aggr_get_id_t aggr_get_id; |
|---|
| 177 | 170 | static bool append_file; |
|---|
| 178 | 171 | static bool interval_count; |
|---|
| 179 | | -static bool interval_clear; |
|---|
| 180 | 172 | static const char *output_name; |
|---|
| 181 | 173 | static int output_fd; |
|---|
| 182 | | -static int print_free_counters_hint; |
|---|
| 183 | | -static int print_mixed_hw_group_error; |
|---|
| 184 | | -static u64 *walltime_run; |
|---|
| 185 | | -static bool ru_display = false; |
|---|
| 186 | | -static struct rusage ru_data; |
|---|
| 187 | | -static unsigned int metric_only_len = METRIC_ONLY_LEN; |
|---|
| 188 | 174 | |
|---|
| 189 | 175 | struct perf_stat { |
|---|
| 190 | 176 | bool record; |
|---|
| .. | .. |
|---|
| 193 | 179 | u64 bytes_written; |
|---|
| 194 | 180 | struct perf_tool tool; |
|---|
| 195 | 181 | bool maps_allocated; |
|---|
| 196 | | - struct cpu_map *cpus; |
|---|
| 197 | | - struct thread_map *threads; |
|---|
| 182 | + struct perf_cpu_map *cpus; |
|---|
| 183 | + struct perf_thread_map *threads; |
|---|
| 198 | 184 | enum aggr_mode aggr_mode; |
|---|
| 199 | 185 | }; |
|---|
| 200 | 186 | |
|---|
| .. | .. |
|---|
| 204 | 190 | static volatile int done = 0; |
|---|
| 205 | 191 | |
|---|
| 206 | 192 | static struct perf_stat_config stat_config = { |
|---|
| 207 | | - .aggr_mode = AGGR_GLOBAL, |
|---|
| 208 | | - .scale = true, |
|---|
| 193 | + .aggr_mode = AGGR_GLOBAL, |
|---|
| 194 | + .scale = true, |
|---|
| 195 | + .unit_width = 4, /* strlen("unit") */ |
|---|
| 196 | + .run_count = 1, |
|---|
| 197 | + .metric_only_len = METRIC_ONLY_LEN, |
|---|
| 198 | + .walltime_nsecs_stats = &walltime_nsecs_stats, |
|---|
| 199 | + .big_num = true, |
|---|
| 200 | + .ctl_fd = -1, |
|---|
| 201 | + .ctl_fd_ack = -1 |
|---|
| 209 | 202 | }; |
|---|
| 210 | 203 | |
|---|
| 211 | | -static bool is_duration_time(struct perf_evsel *evsel) |
|---|
| 204 | +static bool cpus_map_matched(struct evsel *a, struct evsel *b) |
|---|
| 212 | 205 | { |
|---|
| 213 | | - return !strcmp(evsel->name, "duration_time"); |
|---|
| 206 | + if (!a->core.cpus && !b->core.cpus) |
|---|
| 207 | + return true; |
|---|
| 208 | + |
|---|
| 209 | + if (!a->core.cpus || !b->core.cpus) |
|---|
| 210 | + return false; |
|---|
| 211 | + |
|---|
| 212 | + if (a->core.cpus->nr != b->core.cpus->nr) |
|---|
| 213 | + return false; |
|---|
| 214 | + |
|---|
| 215 | + for (int i = 0; i < a->core.cpus->nr; i++) { |
|---|
| 216 | + if (a->core.cpus->map[i] != b->core.cpus->map[i]) |
|---|
| 217 | + return false; |
|---|
| 218 | + } |
|---|
| 219 | + |
|---|
| 220 | + return true; |
|---|
| 221 | +} |
|---|
| 222 | + |
|---|
| 223 | +static void evlist__check_cpu_maps(struct evlist *evlist) |
|---|
| 224 | +{ |
|---|
| 225 | + struct evsel *evsel, *pos, *leader; |
|---|
| 226 | + char buf[1024]; |
|---|
| 227 | + |
|---|
| 228 | + evlist__for_each_entry(evlist, evsel) { |
|---|
| 229 | + leader = evsel->leader; |
|---|
| 230 | + |
|---|
| 231 | + /* Check that leader matches cpus with each member. */ |
|---|
| 232 | + if (leader == evsel) |
|---|
| 233 | + continue; |
|---|
| 234 | + if (cpus_map_matched(leader, evsel)) |
|---|
| 235 | + continue; |
|---|
| 236 | + |
|---|
| 237 | + /* If there's mismatch disable the group and warn user. */ |
|---|
| 238 | + WARN_ONCE(1, "WARNING: grouped events cpus do not match, disabling group:\n"); |
|---|
| 239 | + evsel__group_desc(leader, buf, sizeof(buf)); |
|---|
| 240 | + pr_warning(" %s\n", buf); |
|---|
| 241 | + |
|---|
| 242 | + if (verbose) { |
|---|
| 243 | + cpu_map__snprint(leader->core.cpus, buf, sizeof(buf)); |
|---|
| 244 | + pr_warning(" %s: %s\n", leader->name, buf); |
|---|
| 245 | + cpu_map__snprint(evsel->core.cpus, buf, sizeof(buf)); |
|---|
| 246 | + pr_warning(" %s: %s\n", evsel->name, buf); |
|---|
| 247 | + } |
|---|
| 248 | + |
|---|
| 249 | + for_each_group_evsel(pos, leader) { |
|---|
| 250 | + pos->leader = pos; |
|---|
| 251 | + pos->core.nr_members = 0; |
|---|
| 252 | + } |
|---|
| 253 | + evsel->leader->core.nr_members = 0; |
|---|
| 254 | + } |
|---|
| 214 | 255 | } |
|---|
| 215 | 256 | |
|---|
| 216 | 257 | static inline void diff_timespec(struct timespec *r, struct timespec *a, |
|---|
| .. | .. |
|---|
| 234 | 275 | |
|---|
| 235 | 276 | for (i = 0; i < stat_config.stats_num; i++) |
|---|
| 236 | 277 | perf_stat__reset_shadow_per_stat(&stat_config.stats[i]); |
|---|
| 237 | | -} |
|---|
| 238 | | - |
|---|
| 239 | | -static int create_perf_stat_counter(struct perf_evsel *evsel) |
|---|
| 240 | | -{ |
|---|
| 241 | | - struct perf_event_attr *attr = &evsel->attr; |
|---|
| 242 | | - struct perf_evsel *leader = evsel->leader; |
|---|
| 243 | | - |
|---|
| 244 | | - if (stat_config.scale) { |
|---|
| 245 | | - attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | |
|---|
| 246 | | - PERF_FORMAT_TOTAL_TIME_RUNNING; |
|---|
| 247 | | - } |
|---|
| 248 | | - |
|---|
| 249 | | - /* |
|---|
| 250 | | - * The event is part of non trivial group, let's enable |
|---|
| 251 | | - * the group read (for leader) and ID retrieval for all |
|---|
| 252 | | - * members. |
|---|
| 253 | | - */ |
|---|
| 254 | | - if (leader->nr_members > 1) |
|---|
| 255 | | - attr->read_format |= PERF_FORMAT_ID|PERF_FORMAT_GROUP; |
|---|
| 256 | | - |
|---|
| 257 | | - attr->inherit = !no_inherit; |
|---|
| 258 | | - |
|---|
| 259 | | - /* |
|---|
| 260 | | - * Some events get initialized with sample_(period/type) set, |
|---|
| 261 | | - * like tracepoints. Clear it up for counting. |
|---|
| 262 | | - */ |
|---|
| 263 | | - attr->sample_period = 0; |
|---|
| 264 | | - |
|---|
| 265 | | - /* |
|---|
| 266 | | - * But set sample_type to PERF_SAMPLE_IDENTIFIER, which should be harmless |
|---|
| 267 | | - * while avoiding that older tools show confusing messages. |
|---|
| 268 | | - * |
|---|
| 269 | | - * However for pipe sessions we need to keep it zero, |
|---|
| 270 | | - * because script's perf_evsel__check_attr is triggered |
|---|
| 271 | | - * by attr->sample_type != 0, and we can't run it on |
|---|
| 272 | | - * stat sessions. |
|---|
| 273 | | - */ |
|---|
| 274 | | - if (!(STAT_RECORD && perf_stat.data.is_pipe)) |
|---|
| 275 | | - attr->sample_type = PERF_SAMPLE_IDENTIFIER; |
|---|
| 276 | | - |
|---|
| 277 | | - /* |
|---|
| 278 | | - * Disabling all counters initially, they will be enabled |
|---|
| 279 | | - * either manually by us or by kernel via enable_on_exec |
|---|
| 280 | | - * set later. |
|---|
| 281 | | - */ |
|---|
| 282 | | - if (perf_evsel__is_group_leader(evsel)) { |
|---|
| 283 | | - attr->disabled = 1; |
|---|
| 284 | | - |
|---|
| 285 | | - /* |
|---|
| 286 | | - * In case of initial_delay we enable tracee |
|---|
| 287 | | - * events manually. |
|---|
| 288 | | - */ |
|---|
| 289 | | - if (target__none(&target) && !initial_delay) |
|---|
| 290 | | - attr->enable_on_exec = 1; |
|---|
| 291 | | - } |
|---|
| 292 | | - |
|---|
| 293 | | - if (target__has_cpu(&target) && !target__has_per_thread(&target)) |
|---|
| 294 | | - return perf_evsel__open_per_cpu(evsel, perf_evsel__cpus(evsel)); |
|---|
| 295 | | - |
|---|
| 296 | | - return perf_evsel__open_per_thread(evsel, evsel_list->threads); |
|---|
| 297 | 278 | } |
|---|
| 298 | 279 | |
|---|
| 299 | 280 | static int process_synthesized_event(struct perf_tool *tool __maybe_unused, |
|---|
| .. | .. |
|---|
| 320 | 301 | #define WRITE_STAT_ROUND_EVENT(time, interval) \ |
|---|
| 321 | 302 | write_stat_round_event(time, PERF_STAT_ROUND_TYPE__ ## interval) |
|---|
| 322 | 303 | |
|---|
| 323 | | -#define SID(e, x, y) xyarray__entry(e->sample_id, x, y) |
|---|
| 304 | +#define SID(e, x, y) xyarray__entry(e->core.sample_id, x, y) |
|---|
| 324 | 305 | |
|---|
| 325 | | -static int |
|---|
| 326 | | -perf_evsel__write_stat_event(struct perf_evsel *counter, u32 cpu, u32 thread, |
|---|
| 327 | | - struct perf_counts_values *count) |
|---|
| 306 | +static int evsel__write_stat_event(struct evsel *counter, u32 cpu, u32 thread, |
|---|
| 307 | + struct perf_counts_values *count) |
|---|
| 328 | 308 | { |
|---|
| 329 | 309 | struct perf_sample_id *sid = SID(counter, cpu, thread); |
|---|
| 330 | 310 | |
|---|
| .. | .. |
|---|
| 332 | 312 | process_synthesized_event, NULL); |
|---|
| 333 | 313 | } |
|---|
| 334 | 314 | |
|---|
| 315 | +static int read_single_counter(struct evsel *counter, int cpu, |
|---|
| 316 | + int thread, struct timespec *rs) |
|---|
| 317 | +{ |
|---|
| 318 | + if (counter->tool_event == PERF_TOOL_DURATION_TIME) { |
|---|
| 319 | + u64 val = rs->tv_nsec + rs->tv_sec*1000000000ULL; |
|---|
| 320 | + struct perf_counts_values *count = |
|---|
| 321 | + perf_counts(counter->counts, cpu, thread); |
|---|
| 322 | + count->ena = count->run = val; |
|---|
| 323 | + count->val = val; |
|---|
| 324 | + return 0; |
|---|
| 325 | + } |
|---|
| 326 | + return evsel__read_counter(counter, cpu, thread); |
|---|
| 327 | +} |
|---|
| 328 | + |
|---|
| 335 | 329 | /* |
|---|
| 336 | 330 | * Read out the results of a single counter: |
|---|
| 337 | 331 | * do not aggregate counts across CPUs in system-wide mode |
|---|
| 338 | 332 | */ |
|---|
| 339 | | -static int read_counter(struct perf_evsel *counter) |
|---|
| 333 | +static int read_counter_cpu(struct evsel *counter, struct timespec *rs, int cpu) |
|---|
| 340 | 334 | { |
|---|
| 341 | | - int nthreads = thread_map__nr(evsel_list->threads); |
|---|
| 342 | | - int ncpus, cpu, thread; |
|---|
| 343 | | - |
|---|
| 344 | | - if (target__has_cpu(&target) && !target__has_per_thread(&target)) |
|---|
| 345 | | - ncpus = perf_evsel__nr_cpus(counter); |
|---|
| 346 | | - else |
|---|
| 347 | | - ncpus = 1; |
|---|
| 335 | + int nthreads = perf_thread_map__nr(evsel_list->core.threads); |
|---|
| 336 | + int thread; |
|---|
| 348 | 337 | |
|---|
| 349 | 338 | if (!counter->supported) |
|---|
| 350 | 339 | return -ENOENT; |
|---|
| 351 | 340 | |
|---|
| 352 | | - if (counter->system_wide) |
|---|
| 341 | + if (counter->core.system_wide) |
|---|
| 353 | 342 | nthreads = 1; |
|---|
| 354 | 343 | |
|---|
| 355 | 344 | for (thread = 0; thread < nthreads; thread++) { |
|---|
| 356 | | - for (cpu = 0; cpu < ncpus; cpu++) { |
|---|
| 357 | | - struct perf_counts_values *count; |
|---|
| 345 | + struct perf_counts_values *count; |
|---|
| 358 | 346 | |
|---|
| 359 | | - count = perf_counts(counter->counts, cpu, thread); |
|---|
| 347 | + count = perf_counts(counter->counts, cpu, thread); |
|---|
| 360 | 348 | |
|---|
| 361 | | - /* |
|---|
| 362 | | - * The leader's group read loads data into its group members |
|---|
| 363 | | - * (via perf_evsel__read_counter) and sets threir count->loaded. |
|---|
| 364 | | - */ |
|---|
| 365 | | - if (!count->loaded && |
|---|
| 366 | | - perf_evsel__read_counter(counter, cpu, thread)) { |
|---|
| 367 | | - counter->counts->scaled = -1; |
|---|
| 368 | | - perf_counts(counter->counts, cpu, thread)->ena = 0; |
|---|
| 369 | | - perf_counts(counter->counts, cpu, thread)->run = 0; |
|---|
| 349 | + /* |
|---|
| 350 | + * The leader's group read loads data into its group members |
|---|
| 351 | + * (via evsel__read_counter()) and sets their count->loaded. |
|---|
| 352 | + */ |
|---|
| 353 | + if (!perf_counts__is_loaded(counter->counts, cpu, thread) && |
|---|
| 354 | + read_single_counter(counter, cpu, thread, rs)) { |
|---|
| 355 | + counter->counts->scaled = -1; |
|---|
| 356 | + perf_counts(counter->counts, cpu, thread)->ena = 0; |
|---|
| 357 | + perf_counts(counter->counts, cpu, thread)->run = 0; |
|---|
| 358 | + return -1; |
|---|
| 359 | + } |
|---|
| 360 | + |
|---|
| 361 | + perf_counts__set_loaded(counter->counts, cpu, thread, false); |
|---|
| 362 | + |
|---|
| 363 | + if (STAT_RECORD) { |
|---|
| 364 | + if (evsel__write_stat_event(counter, cpu, thread, count)) { |
|---|
| 365 | + pr_err("failed to write stat event\n"); |
|---|
| 370 | 366 | return -1; |
|---|
| 371 | 367 | } |
|---|
| 368 | + } |
|---|
| 372 | 369 | |
|---|
| 373 | | - count->loaded = false; |
|---|
| 374 | | - |
|---|
| 375 | | - if (STAT_RECORD) { |
|---|
| 376 | | - if (perf_evsel__write_stat_event(counter, cpu, thread, count)) { |
|---|
| 377 | | - pr_err("failed to write stat event\n"); |
|---|
| 378 | | - return -1; |
|---|
| 379 | | - } |
|---|
| 380 | | - } |
|---|
| 381 | | - |
|---|
| 382 | | - if (verbose > 1) { |
|---|
| 383 | | - fprintf(stat_config.output, |
|---|
| 384 | | - "%s: %d: %" PRIu64 " %" PRIu64 " %" PRIu64 "\n", |
|---|
| 385 | | - perf_evsel__name(counter), |
|---|
| 386 | | - cpu, |
|---|
| 387 | | - count->val, count->ena, count->run); |
|---|
| 388 | | - } |
|---|
| 370 | + if (verbose > 1) { |
|---|
| 371 | + fprintf(stat_config.output, |
|---|
| 372 | + "%s: %d: %" PRIu64 " %" PRIu64 " %" PRIu64 "\n", |
|---|
| 373 | + evsel__name(counter), |
|---|
| 374 | + cpu, |
|---|
| 375 | + count->val, count->ena, count->run); |
|---|
| 389 | 376 | } |
|---|
| 390 | 377 | } |
|---|
| 391 | 378 | |
|---|
| 392 | 379 | return 0; |
|---|
| 393 | 380 | } |
|---|
| 394 | 381 | |
|---|
| 395 | | -static void read_counters(void) |
|---|
| 382 | +static int read_affinity_counters(struct timespec *rs) |
|---|
| 396 | 383 | { |
|---|
| 397 | | - struct perf_evsel *counter; |
|---|
| 398 | | - int ret; |
|---|
| 384 | + struct evsel *counter; |
|---|
| 385 | + struct affinity affinity; |
|---|
| 386 | + int i, ncpus, cpu; |
|---|
| 387 | + |
|---|
| 388 | + if (affinity__setup(&affinity) < 0) |
|---|
| 389 | + return -1; |
|---|
| 390 | + |
|---|
| 391 | + ncpus = perf_cpu_map__nr(evsel_list->core.all_cpus); |
|---|
| 392 | + if (!target__has_cpu(&target) || target__has_per_thread(&target)) |
|---|
| 393 | + ncpus = 1; |
|---|
| 394 | + evlist__for_each_cpu(evsel_list, i, cpu) { |
|---|
| 395 | + if (i >= ncpus) |
|---|
| 396 | + break; |
|---|
| 397 | + affinity__set(&affinity, cpu); |
|---|
| 398 | + |
|---|
| 399 | + evlist__for_each_entry(evsel_list, counter) { |
|---|
| 400 | + if (evsel__cpu_iter_skip(counter, cpu)) |
|---|
| 401 | + continue; |
|---|
| 402 | + if (!counter->err) { |
|---|
| 403 | + counter->err = read_counter_cpu(counter, rs, |
|---|
| 404 | + counter->cpu_iter - 1); |
|---|
| 405 | + } |
|---|
| 406 | + } |
|---|
| 407 | + } |
|---|
| 408 | + affinity__cleanup(&affinity); |
|---|
| 409 | + return 0; |
|---|
| 410 | +} |
|---|
| 411 | + |
|---|
| 412 | +static void read_counters(struct timespec *rs) |
|---|
| 413 | +{ |
|---|
| 414 | + struct evsel *counter; |
|---|
| 415 | + |
|---|
| 416 | + if (!stat_config.stop_read_counter && (read_affinity_counters(rs) < 0)) |
|---|
| 417 | + return; |
|---|
| 399 | 418 | |
|---|
| 400 | 419 | evlist__for_each_entry(evsel_list, counter) { |
|---|
| 401 | | - ret = read_counter(counter); |
|---|
| 402 | | - if (ret) |
|---|
| 420 | + if (counter->err) |
|---|
| 403 | 421 | pr_debug("failed to read counter %s\n", counter->name); |
|---|
| 404 | | - |
|---|
| 405 | | - if (ret == 0 && perf_stat_process_counter(&stat_config, counter)) |
|---|
| 422 | + if (counter->err == 0 && perf_stat_process_counter(&stat_config, counter)) |
|---|
| 406 | 423 | pr_warning("failed to process counter %s\n", counter->name); |
|---|
| 424 | + counter->err = 0; |
|---|
| 407 | 425 | } |
|---|
| 426 | +} |
|---|
| 427 | + |
|---|
| 428 | +static int runtime_stat_new(struct perf_stat_config *config, int nthreads) |
|---|
| 429 | +{ |
|---|
| 430 | + int i; |
|---|
| 431 | + |
|---|
| 432 | + config->stats = calloc(nthreads, sizeof(struct runtime_stat)); |
|---|
| 433 | + if (!config->stats) |
|---|
| 434 | + return -1; |
|---|
| 435 | + |
|---|
| 436 | + config->stats_num = nthreads; |
|---|
| 437 | + |
|---|
| 438 | + for (i = 0; i < nthreads; i++) |
|---|
| 439 | + runtime_stat__init(&config->stats[i]); |
|---|
| 440 | + |
|---|
| 441 | + return 0; |
|---|
| 442 | +} |
|---|
| 443 | + |
|---|
| 444 | +static void runtime_stat_delete(struct perf_stat_config *config) |
|---|
| 445 | +{ |
|---|
| 446 | + int i; |
|---|
| 447 | + |
|---|
| 448 | + if (!config->stats) |
|---|
| 449 | + return; |
|---|
| 450 | + |
|---|
| 451 | + for (i = 0; i < config->stats_num; i++) |
|---|
| 452 | + runtime_stat__exit(&config->stats[i]); |
|---|
| 453 | + |
|---|
| 454 | + zfree(&config->stats); |
|---|
| 455 | +} |
|---|
| 456 | + |
|---|
| 457 | +static void runtime_stat_reset(struct perf_stat_config *config) |
|---|
| 458 | +{ |
|---|
| 459 | + int i; |
|---|
| 460 | + |
|---|
| 461 | + if (!config->stats) |
|---|
| 462 | + return; |
|---|
| 463 | + |
|---|
| 464 | + for (i = 0; i < config->stats_num; i++) |
|---|
| 465 | + perf_stat__reset_shadow_per_stat(&config->stats[i]); |
|---|
| 408 | 466 | } |
|---|
| 409 | 467 | |
|---|
| 410 | 468 | static void process_interval(void) |
|---|
| 411 | 469 | { |
|---|
| 412 | 470 | struct timespec ts, rs; |
|---|
| 413 | 471 | |
|---|
| 414 | | - read_counters(); |
|---|
| 415 | | - |
|---|
| 416 | 472 | clock_gettime(CLOCK_MONOTONIC, &ts); |
|---|
| 417 | 473 | diff_timespec(&rs, &ts, &ref_time); |
|---|
| 474 | + |
|---|
| 475 | + perf_stat__reset_shadow_per_stat(&rt_stat); |
|---|
| 476 | + runtime_stat_reset(&stat_config); |
|---|
| 477 | + read_counters(&rs); |
|---|
| 418 | 478 | |
|---|
| 419 | 479 | if (STAT_RECORD) { |
|---|
| 420 | 480 | if (WRITE_STAT_ROUND_EVENT(rs.tv_sec * NSEC_PER_SEC + rs.tv_nsec, INTERVAL)) |
|---|
| .. | .. |
|---|
| 426 | 486 | print_counters(&rs, 0, NULL); |
|---|
| 427 | 487 | } |
|---|
| 428 | 488 | |
|---|
| 489 | +static bool handle_interval(unsigned int interval, int *times) |
|---|
| 490 | +{ |
|---|
| 491 | + if (interval) { |
|---|
| 492 | + process_interval(); |
|---|
| 493 | + if (interval_count && !(--(*times))) |
|---|
| 494 | + return true; |
|---|
| 495 | + } |
|---|
| 496 | + return false; |
|---|
| 497 | +} |
|---|
| 498 | + |
|---|
| 429 | 499 | static void enable_counters(void) |
|---|
| 430 | 500 | { |
|---|
| 431 | | - if (initial_delay) |
|---|
| 432 | | - usleep(initial_delay * USEC_PER_MSEC); |
|---|
| 501 | + if (stat_config.initial_delay < 0) { |
|---|
| 502 | + pr_info(EVLIST_DISABLED_MSG); |
|---|
| 503 | + return; |
|---|
| 504 | + } |
|---|
| 505 | + |
|---|
| 506 | + if (stat_config.initial_delay > 0) { |
|---|
| 507 | + pr_info(EVLIST_DISABLED_MSG); |
|---|
| 508 | + usleep(stat_config.initial_delay * USEC_PER_MSEC); |
|---|
| 509 | + } |
|---|
| 433 | 510 | |
|---|
| 434 | 511 | /* |
|---|
| 435 | 512 | * We need to enable counters only if: |
|---|
| 436 | 513 | * - we don't have tracee (attaching to task or cpu) |
|---|
| 437 | 514 | * - we have initial delay configured |
|---|
| 438 | 515 | */ |
|---|
| 439 | | - if (!target__none(&target) || initial_delay) |
|---|
| 440 | | - perf_evlist__enable(evsel_list); |
|---|
| 516 | + if (!target__none(&target) || stat_config.initial_delay) { |
|---|
| 517 | + evlist__enable(evsel_list); |
|---|
| 518 | + if (stat_config.initial_delay > 0) |
|---|
| 519 | + pr_info(EVLIST_ENABLED_MSG); |
|---|
| 520 | + } |
|---|
| 441 | 521 | } |
|---|
| 442 | 522 | |
|---|
| 443 | 523 | static void disable_counters(void) |
|---|
| .. | .. |
|---|
| 448 | 528 | * from counting before reading their constituent counters. |
|---|
| 449 | 529 | */ |
|---|
| 450 | 530 | if (!target__none(&target)) |
|---|
| 451 | | - perf_evlist__disable(evsel_list); |
|---|
| 531 | + evlist__disable(evsel_list); |
|---|
| 452 | 532 | } |
|---|
| 453 | 533 | |
|---|
| 454 | 534 | static volatile int workload_exec_errno; |
|---|
| .. | .. |
|---|
| 464 | 544 | workload_exec_errno = info->si_value.sival_int; |
|---|
| 465 | 545 | } |
|---|
| 466 | 546 | |
|---|
| 467 | | -static int perf_stat_synthesize_config(bool is_pipe) |
|---|
| 547 | +static bool evsel__should_store_id(struct evsel *counter) |
|---|
| 468 | 548 | { |
|---|
| 469 | | - int err; |
|---|
| 549 | + return STAT_RECORD || counter->core.attr.read_format & PERF_FORMAT_ID; |
|---|
| 550 | +} |
|---|
| 470 | 551 | |
|---|
| 471 | | - if (is_pipe) { |
|---|
| 472 | | - err = perf_event__synthesize_attrs(NULL, perf_stat.session, |
|---|
| 473 | | - process_synthesized_event); |
|---|
| 474 | | - if (err < 0) { |
|---|
| 475 | | - pr_err("Couldn't synthesize attrs.\n"); |
|---|
| 476 | | - return err; |
|---|
| 552 | +static bool is_target_alive(struct target *_target, |
|---|
| 553 | + struct perf_thread_map *threads) |
|---|
| 554 | +{ |
|---|
| 555 | + struct stat st; |
|---|
| 556 | + int i; |
|---|
| 557 | + |
|---|
| 558 | + if (!target__has_task(_target)) |
|---|
| 559 | + return true; |
|---|
| 560 | + |
|---|
| 561 | + for (i = 0; i < threads->nr; i++) { |
|---|
| 562 | + char path[PATH_MAX]; |
|---|
| 563 | + |
|---|
| 564 | + scnprintf(path, PATH_MAX, "%s/%d", procfs__mountpoint(), |
|---|
| 565 | + threads->map[i].pid); |
|---|
| 566 | + |
|---|
| 567 | + if (!stat(path, &st)) |
|---|
| 568 | + return true; |
|---|
| 569 | + } |
|---|
| 570 | + |
|---|
| 571 | + return false; |
|---|
| 572 | +} |
|---|
| 573 | + |
|---|
| 574 | +static void process_evlist(struct evlist *evlist, unsigned int interval) |
|---|
| 575 | +{ |
|---|
| 576 | + enum evlist_ctl_cmd cmd = EVLIST_CTL_CMD_UNSUPPORTED; |
|---|
| 577 | + |
|---|
| 578 | + if (evlist__ctlfd_process(evlist, &cmd) > 0) { |
|---|
| 579 | + switch (cmd) { |
|---|
| 580 | + case EVLIST_CTL_CMD_ENABLE: |
|---|
| 581 | + pr_info(EVLIST_ENABLED_MSG); |
|---|
| 582 | + if (interval) |
|---|
| 583 | + process_interval(); |
|---|
| 584 | + break; |
|---|
| 585 | + case EVLIST_CTL_CMD_DISABLE: |
|---|
| 586 | + if (interval) |
|---|
| 587 | + process_interval(); |
|---|
| 588 | + pr_info(EVLIST_DISABLED_MSG); |
|---|
| 589 | + break; |
|---|
| 590 | + case EVLIST_CTL_CMD_SNAPSHOT: |
|---|
| 591 | + case EVLIST_CTL_CMD_ACK: |
|---|
| 592 | + case EVLIST_CTL_CMD_UNSUPPORTED: |
|---|
| 593 | + default: |
|---|
| 594 | + break; |
|---|
| 595 | + } |
|---|
| 596 | + } |
|---|
| 597 | +} |
|---|
| 598 | + |
|---|
| 599 | +static void compute_tts(struct timespec *time_start, struct timespec *time_stop, |
|---|
| 600 | + int *time_to_sleep) |
|---|
| 601 | +{ |
|---|
| 602 | + int tts = *time_to_sleep; |
|---|
| 603 | + struct timespec time_diff; |
|---|
| 604 | + |
|---|
| 605 | + diff_timespec(&time_diff, time_stop, time_start); |
|---|
| 606 | + |
|---|
| 607 | + tts -= time_diff.tv_sec * MSEC_PER_SEC + |
|---|
| 608 | + time_diff.tv_nsec / NSEC_PER_MSEC; |
|---|
| 609 | + |
|---|
| 610 | + if (tts < 0) |
|---|
| 611 | + tts = 0; |
|---|
| 612 | + |
|---|
| 613 | + *time_to_sleep = tts; |
|---|
| 614 | +} |
|---|
| 615 | + |
|---|
| 616 | +static int dispatch_events(bool forks, int timeout, int interval, int *times) |
|---|
| 617 | +{ |
|---|
| 618 | + int child_exited = 0, status = 0; |
|---|
| 619 | + int time_to_sleep, sleep_time; |
|---|
| 620 | + struct timespec time_start, time_stop; |
|---|
| 621 | + |
|---|
| 622 | + if (interval) |
|---|
| 623 | + sleep_time = interval; |
|---|
| 624 | + else if (timeout) |
|---|
| 625 | + sleep_time = timeout; |
|---|
| 626 | + else |
|---|
| 627 | + sleep_time = 1000; |
|---|
| 628 | + |
|---|
| 629 | + time_to_sleep = sleep_time; |
|---|
| 630 | + |
|---|
| 631 | + while (!done) { |
|---|
| 632 | + if (forks) |
|---|
| 633 | + child_exited = waitpid(child_pid, &status, WNOHANG); |
|---|
| 634 | + else |
|---|
| 635 | + child_exited = !is_target_alive(&target, evsel_list->core.threads) ? 1 : 0; |
|---|
| 636 | + |
|---|
| 637 | + if (child_exited) |
|---|
| 638 | + break; |
|---|
| 639 | + |
|---|
| 640 | + clock_gettime(CLOCK_MONOTONIC, &time_start); |
|---|
| 641 | + if (!(evlist__poll(evsel_list, time_to_sleep) > 0)) { /* poll timeout or EINTR */ |
|---|
| 642 | + if (timeout || handle_interval(interval, times)) |
|---|
| 643 | + break; |
|---|
| 644 | + time_to_sleep = sleep_time; |
|---|
| 645 | + } else { /* fd revent */ |
|---|
| 646 | + process_evlist(evsel_list, interval); |
|---|
| 647 | + clock_gettime(CLOCK_MONOTONIC, &time_stop); |
|---|
| 648 | + compute_tts(&time_start, &time_stop, &time_to_sleep); |
|---|
| 477 | 649 | } |
|---|
| 478 | 650 | } |
|---|
| 479 | 651 | |
|---|
| 480 | | - err = perf_event__synthesize_extra_attr(NULL, |
|---|
| 481 | | - evsel_list, |
|---|
| 482 | | - process_synthesized_event, |
|---|
| 483 | | - is_pipe); |
|---|
| 484 | | - |
|---|
| 485 | | - err = perf_event__synthesize_thread_map2(NULL, evsel_list->threads, |
|---|
| 486 | | - process_synthesized_event, |
|---|
| 487 | | - NULL); |
|---|
| 488 | | - if (err < 0) { |
|---|
| 489 | | - pr_err("Couldn't synthesize thread map.\n"); |
|---|
| 490 | | - return err; |
|---|
| 491 | | - } |
|---|
| 492 | | - |
|---|
| 493 | | - err = perf_event__synthesize_cpu_map(NULL, evsel_list->cpus, |
|---|
| 494 | | - process_synthesized_event, NULL); |
|---|
| 495 | | - if (err < 0) { |
|---|
| 496 | | - pr_err("Couldn't synthesize thread map.\n"); |
|---|
| 497 | | - return err; |
|---|
| 498 | | - } |
|---|
| 499 | | - |
|---|
| 500 | | - err = perf_event__synthesize_stat_config(NULL, &stat_config, |
|---|
| 501 | | - process_synthesized_event, NULL); |
|---|
| 502 | | - if (err < 0) { |
|---|
| 503 | | - pr_err("Couldn't synthesize config.\n"); |
|---|
| 504 | | - return err; |
|---|
| 505 | | - } |
|---|
| 506 | | - |
|---|
| 507 | | - return 0; |
|---|
| 652 | + return status; |
|---|
| 508 | 653 | } |
|---|
| 509 | 654 | |
|---|
| 510 | | -#define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) |
|---|
| 655 | +enum counter_recovery { |
|---|
| 656 | + COUNTER_SKIP, |
|---|
| 657 | + COUNTER_RETRY, |
|---|
| 658 | + COUNTER_FATAL, |
|---|
| 659 | +}; |
|---|
| 511 | 660 | |
|---|
| 512 | | -static int __store_counter_ids(struct perf_evsel *counter) |
|---|
| 661 | +static enum counter_recovery stat_handle_error(struct evsel *counter) |
|---|
| 513 | 662 | { |
|---|
| 514 | | - int cpu, thread; |
|---|
| 515 | | - |
|---|
| 516 | | - for (cpu = 0; cpu < xyarray__max_x(counter->fd); cpu++) { |
|---|
| 517 | | - for (thread = 0; thread < xyarray__max_y(counter->fd); |
|---|
| 518 | | - thread++) { |
|---|
| 519 | | - int fd = FD(counter, cpu, thread); |
|---|
| 520 | | - |
|---|
| 521 | | - if (perf_evlist__id_add_fd(evsel_list, counter, |
|---|
| 522 | | - cpu, thread, fd) < 0) |
|---|
| 523 | | - return -1; |
|---|
| 524 | | - } |
|---|
| 525 | | - } |
|---|
| 526 | | - |
|---|
| 527 | | - return 0; |
|---|
| 528 | | -} |
|---|
| 529 | | - |
|---|
| 530 | | -static int store_counter_ids(struct perf_evsel *counter) |
|---|
| 531 | | -{ |
|---|
| 532 | | - struct cpu_map *cpus = counter->cpus; |
|---|
| 533 | | - struct thread_map *threads = counter->threads; |
|---|
| 534 | | - |
|---|
| 535 | | - if (perf_evsel__alloc_id(counter, cpus->nr, threads->nr)) |
|---|
| 536 | | - return -ENOMEM; |
|---|
| 537 | | - |
|---|
| 538 | | - return __store_counter_ids(counter); |
|---|
| 539 | | -} |
|---|
| 540 | | - |
|---|
| 541 | | -static bool perf_evsel__should_store_id(struct perf_evsel *counter) |
|---|
| 542 | | -{ |
|---|
| 543 | | - return STAT_RECORD || counter->attr.read_format & PERF_FORMAT_ID; |
|---|
| 544 | | -} |
|---|
| 545 | | - |
|---|
| 546 | | -static struct perf_evsel *perf_evsel__reset_weak_group(struct perf_evsel *evsel) |
|---|
| 547 | | -{ |
|---|
| 548 | | - struct perf_evsel *c2, *leader; |
|---|
| 549 | | - bool is_open = true; |
|---|
| 550 | | - |
|---|
| 551 | | - leader = evsel->leader; |
|---|
| 552 | | - pr_debug("Weak group for %s/%d failed\n", |
|---|
| 553 | | - leader->name, leader->nr_members); |
|---|
| 554 | | - |
|---|
| 663 | + char msg[BUFSIZ]; |
|---|
| 555 | 664 | /* |
|---|
| 556 | | - * for_each_group_member doesn't work here because it doesn't |
|---|
| 557 | | - * include the first entry. |
|---|
| 665 | + * PPC returns ENXIO for HW counters until 2.6.37 |
|---|
| 666 | + * (behavior changed with commit b0a873e). |
|---|
| 558 | 667 | */ |
|---|
| 559 | | - evlist__for_each_entry(evsel_list, c2) { |
|---|
| 560 | | - if (c2 == evsel) |
|---|
| 561 | | - is_open = false; |
|---|
| 562 | | - if (c2->leader == leader) { |
|---|
| 563 | | - if (is_open) |
|---|
| 564 | | - perf_evsel__close(c2); |
|---|
| 565 | | - c2->leader = c2; |
|---|
| 566 | | - c2->nr_members = 0; |
|---|
| 668 | + if (errno == EINVAL || errno == ENOSYS || |
|---|
| 669 | + errno == ENOENT || errno == EOPNOTSUPP || |
|---|
| 670 | + errno == ENXIO) { |
|---|
| 671 | + if (verbose > 0) |
|---|
| 672 | + ui__warning("%s event is not supported by the kernel.\n", |
|---|
| 673 | + evsel__name(counter)); |
|---|
| 674 | + counter->supported = false; |
|---|
| 675 | + /* |
|---|
| 676 | + * errored is a sticky flag that means one of the counter's |
|---|
| 677 | + * cpu event had a problem and needs to be reexamined. |
|---|
| 678 | + */ |
|---|
| 679 | + counter->errored = true; |
|---|
| 680 | + |
|---|
| 681 | + if ((counter->leader != counter) || |
|---|
| 682 | + !(counter->leader->core.nr_members > 1)) |
|---|
| 683 | + return COUNTER_SKIP; |
|---|
| 684 | + } else if (evsel__fallback(counter, errno, msg, sizeof(msg))) { |
|---|
| 685 | + if (verbose > 0) |
|---|
| 686 | + ui__warning("%s\n", msg); |
|---|
| 687 | + return COUNTER_RETRY; |
|---|
| 688 | + } else if (target__has_per_thread(&target) && |
|---|
| 689 | + evsel_list->core.threads && |
|---|
| 690 | + evsel_list->core.threads->err_thread != -1) { |
|---|
| 691 | + /* |
|---|
| 692 | + * For global --per-thread case, skip current |
|---|
| 693 | + * error thread. |
|---|
| 694 | + */ |
|---|
| 695 | + if (!thread_map__remove(evsel_list->core.threads, |
|---|
| 696 | + evsel_list->core.threads->err_thread)) { |
|---|
| 697 | + evsel_list->core.threads->err_thread = -1; |
|---|
| 698 | + return COUNTER_RETRY; |
|---|
| 567 | 699 | } |
|---|
| 568 | 700 | } |
|---|
| 569 | | - return leader; |
|---|
| 701 | + |
|---|
| 702 | + evsel__open_strerror(counter, &target, errno, msg, sizeof(msg)); |
|---|
| 703 | + ui__error("%s\n", msg); |
|---|
| 704 | + |
|---|
| 705 | + if (child_pid != -1) |
|---|
| 706 | + kill(child_pid, SIGTERM); |
|---|
| 707 | + return COUNTER_FATAL; |
|---|
| 570 | 708 | } |
|---|
| 571 | 709 | |
|---|
| 572 | 710 | static int __run_perf_stat(int argc, const char **argv, int run_idx) |
|---|
| .. | .. |
|---|
| 576 | 714 | int timeout = stat_config.timeout; |
|---|
| 577 | 715 | char msg[BUFSIZ]; |
|---|
| 578 | 716 | unsigned long long t0, t1; |
|---|
| 579 | | - struct perf_evsel *counter; |
|---|
| 580 | | - struct timespec ts; |
|---|
| 717 | + struct evsel *counter; |
|---|
| 581 | 718 | size_t l; |
|---|
| 582 | 719 | int status = 0; |
|---|
| 583 | 720 | const bool forks = (argc > 0); |
|---|
| 584 | 721 | bool is_pipe = STAT_RECORD ? perf_stat.data.is_pipe : false; |
|---|
| 585 | | - struct perf_evsel_config_term *err_term; |
|---|
| 586 | | - |
|---|
| 587 | | - if (interval) { |
|---|
| 588 | | - ts.tv_sec = interval / USEC_PER_MSEC; |
|---|
| 589 | | - ts.tv_nsec = (interval % USEC_PER_MSEC) * NSEC_PER_MSEC; |
|---|
| 590 | | - } else if (timeout) { |
|---|
| 591 | | - ts.tv_sec = timeout / USEC_PER_MSEC; |
|---|
| 592 | | - ts.tv_nsec = (timeout % USEC_PER_MSEC) * NSEC_PER_MSEC; |
|---|
| 593 | | - } else { |
|---|
| 594 | | - ts.tv_sec = 1; |
|---|
| 595 | | - ts.tv_nsec = 0; |
|---|
| 596 | | - } |
|---|
| 722 | + struct affinity affinity; |
|---|
| 723 | + int i, cpu; |
|---|
| 724 | + bool second_pass = false; |
|---|
| 597 | 725 | |
|---|
| 598 | 726 | if (forks) { |
|---|
| 599 | 727 | if (perf_evlist__prepare_workload(evsel_list, &target, argv, is_pipe, |
|---|
| .. | .. |
|---|
| 607 | 735 | if (group) |
|---|
| 608 | 736 | perf_evlist__set_leader(evsel_list); |
|---|
| 609 | 737 | |
|---|
| 610 | | - evlist__for_each_entry(evsel_list, counter) { |
|---|
| 738 | + if (affinity__setup(&affinity) < 0) |
|---|
| 739 | + return -1; |
|---|
| 740 | + |
|---|
| 741 | + evlist__for_each_cpu (evsel_list, i, cpu) { |
|---|
| 742 | + affinity__set(&affinity, cpu); |
|---|
| 743 | + |
|---|
| 744 | + evlist__for_each_entry(evsel_list, counter) { |
|---|
| 745 | + if (evsel__cpu_iter_skip(counter, cpu)) |
|---|
| 746 | + continue; |
|---|
| 747 | + if (counter->reset_group || counter->errored) |
|---|
| 748 | + continue; |
|---|
| 611 | 749 | try_again: |
|---|
| 612 | | - if (create_perf_stat_counter(counter) < 0) { |
|---|
| 750 | + if (create_perf_stat_counter(counter, &stat_config, &target, |
|---|
| 751 | + counter->cpu_iter - 1) < 0) { |
|---|
| 613 | 752 | |
|---|
| 614 | | - /* Weak group failed. Reset the group. */ |
|---|
| 615 | | - if ((errno == EINVAL || errno == EBADF) && |
|---|
| 616 | | - counter->leader != counter && |
|---|
| 617 | | - counter->weak_group) { |
|---|
| 618 | | - counter = perf_evsel__reset_weak_group(counter); |
|---|
| 619 | | - goto try_again; |
|---|
| 620 | | - } |
|---|
| 621 | | - |
|---|
| 622 | | - /* |
|---|
| 623 | | - * PPC returns ENXIO for HW counters until 2.6.37 |
|---|
| 624 | | - * (behavior changed with commit b0a873e). |
|---|
| 625 | | - */ |
|---|
| 626 | | - if (errno == EINVAL || errno == ENOSYS || |
|---|
| 627 | | - errno == ENOENT || errno == EOPNOTSUPP || |
|---|
| 628 | | - errno == ENXIO) { |
|---|
| 629 | | - if (verbose > 0) |
|---|
| 630 | | - ui__warning("%s event is not supported by the kernel.\n", |
|---|
| 631 | | - perf_evsel__name(counter)); |
|---|
| 632 | | - counter->supported = false; |
|---|
| 633 | | - |
|---|
| 634 | | - if ((counter->leader != counter) || |
|---|
| 635 | | - !(counter->leader->nr_members > 1)) |
|---|
| 636 | | - continue; |
|---|
| 637 | | - } else if (perf_evsel__fallback(counter, errno, msg, sizeof(msg))) { |
|---|
| 638 | | - if (verbose > 0) |
|---|
| 639 | | - ui__warning("%s\n", msg); |
|---|
| 640 | | - goto try_again; |
|---|
| 641 | | - } else if (target__has_per_thread(&target) && |
|---|
| 642 | | - evsel_list->threads && |
|---|
| 643 | | - evsel_list->threads->err_thread != -1) { |
|---|
| 644 | 753 | /* |
|---|
| 645 | | - * For global --per-thread case, skip current |
|---|
| 646 | | - * error thread. |
|---|
| 754 | + * Weak group failed. We cannot just undo this here |
|---|
| 755 | + * because earlier CPUs might be in group mode, and the kernel |
|---|
| 756 | + * doesn't support mixing group and non group reads. Defer |
|---|
| 757 | + * it to later. |
|---|
| 758 | + * Don't close here because we're in the wrong affinity. |
|---|
| 647 | 759 | */ |
|---|
| 648 | | - if (!thread_map__remove(evsel_list->threads, |
|---|
| 649 | | - evsel_list->threads->err_thread)) { |
|---|
| 650 | | - evsel_list->threads->err_thread = -1; |
|---|
| 651 | | - goto try_again; |
|---|
| 760 | + if ((errno == EINVAL || errno == EBADF) && |
|---|
| 761 | + counter->leader != counter && |
|---|
| 762 | + counter->weak_group) { |
|---|
| 763 | + perf_evlist__reset_weak_group(evsel_list, counter, false); |
|---|
| 764 | + assert(counter->reset_group); |
|---|
| 765 | + second_pass = true; |
|---|
| 766 | + continue; |
|---|
| 652 | 767 | } |
|---|
| 768 | + |
|---|
| 769 | + switch (stat_handle_error(counter)) { |
|---|
| 770 | + case COUNTER_FATAL: |
|---|
| 771 | + return -1; |
|---|
| 772 | + case COUNTER_RETRY: |
|---|
| 773 | + goto try_again; |
|---|
| 774 | + case COUNTER_SKIP: |
|---|
| 775 | + continue; |
|---|
| 776 | + default: |
|---|
| 777 | + break; |
|---|
| 778 | + } |
|---|
| 779 | + |
|---|
| 653 | 780 | } |
|---|
| 654 | | - |
|---|
| 655 | | - perf_evsel__open_strerror(counter, &target, |
|---|
| 656 | | - errno, msg, sizeof(msg)); |
|---|
| 657 | | - ui__error("%s\n", msg); |
|---|
| 658 | | - |
|---|
| 659 | | - if (child_pid != -1) |
|---|
| 660 | | - kill(child_pid, SIGTERM); |
|---|
| 661 | | - |
|---|
| 662 | | - return -1; |
|---|
| 781 | + counter->supported = true; |
|---|
| 663 | 782 | } |
|---|
| 664 | | - counter->supported = true; |
|---|
| 783 | + } |
|---|
| 784 | + |
|---|
| 785 | + if (second_pass) { |
|---|
| 786 | + /* |
|---|
| 787 | + * Now redo all the weak group after closing them, |
|---|
| 788 | + * and also close errored counters. |
|---|
| 789 | + */ |
|---|
| 790 | + |
|---|
| 791 | + evlist__for_each_cpu(evsel_list, i, cpu) { |
|---|
| 792 | + affinity__set(&affinity, cpu); |
|---|
| 793 | + /* First close errored or weak retry */ |
|---|
| 794 | + evlist__for_each_entry(evsel_list, counter) { |
|---|
| 795 | + if (!counter->reset_group && !counter->errored) |
|---|
| 796 | + continue; |
|---|
| 797 | + if (evsel__cpu_iter_skip_no_inc(counter, cpu)) |
|---|
| 798 | + continue; |
|---|
| 799 | + perf_evsel__close_cpu(&counter->core, counter->cpu_iter); |
|---|
| 800 | + } |
|---|
| 801 | + /* Now reopen weak */ |
|---|
| 802 | + evlist__for_each_entry(evsel_list, counter) { |
|---|
| 803 | + if (!counter->reset_group && !counter->errored) |
|---|
| 804 | + continue; |
|---|
| 805 | + if (evsel__cpu_iter_skip(counter, cpu)) |
|---|
| 806 | + continue; |
|---|
| 807 | + if (!counter->reset_group) |
|---|
| 808 | + continue; |
|---|
| 809 | +try_again_reset: |
|---|
| 810 | + pr_debug2("reopening weak %s\n", evsel__name(counter)); |
|---|
| 811 | + if (create_perf_stat_counter(counter, &stat_config, &target, |
|---|
| 812 | + counter->cpu_iter - 1) < 0) { |
|---|
| 813 | + |
|---|
| 814 | + switch (stat_handle_error(counter)) { |
|---|
| 815 | + case COUNTER_FATAL: |
|---|
| 816 | + return -1; |
|---|
| 817 | + case COUNTER_RETRY: |
|---|
| 818 | + goto try_again_reset; |
|---|
| 819 | + case COUNTER_SKIP: |
|---|
| 820 | + continue; |
|---|
| 821 | + default: |
|---|
| 822 | + break; |
|---|
| 823 | + } |
|---|
| 824 | + } |
|---|
| 825 | + counter->supported = true; |
|---|
| 826 | + } |
|---|
| 827 | + } |
|---|
| 828 | + } |
|---|
| 829 | + affinity__cleanup(&affinity); |
|---|
| 830 | + |
|---|
| 831 | + evlist__for_each_entry(evsel_list, counter) { |
|---|
| 832 | + if (!counter->supported) { |
|---|
| 833 | + perf_evsel__free_fd(&counter->core); |
|---|
| 834 | + continue; |
|---|
| 835 | + } |
|---|
| 665 | 836 | |
|---|
| 666 | 837 | l = strlen(counter->unit); |
|---|
| 667 | | - if (l > unit_width) |
|---|
| 668 | | - unit_width = l; |
|---|
| 838 | + if (l > stat_config.unit_width) |
|---|
| 839 | + stat_config.unit_width = l; |
|---|
| 669 | 840 | |
|---|
| 670 | | - if (perf_evsel__should_store_id(counter) && |
|---|
| 671 | | - store_counter_ids(counter)) |
|---|
| 841 | + if (evsel__should_store_id(counter) && |
|---|
| 842 | + evsel__store_ids(counter, evsel_list)) |
|---|
| 672 | 843 | return -1; |
|---|
| 673 | 844 | } |
|---|
| 674 | 845 | |
|---|
| 675 | 846 | if (perf_evlist__apply_filters(evsel_list, &counter)) { |
|---|
| 676 | 847 | pr_err("failed to set filter \"%s\" on event %s with %d (%s)\n", |
|---|
| 677 | | - counter->filter, perf_evsel__name(counter), errno, |
|---|
| 848 | + counter->filter, evsel__name(counter), errno, |
|---|
| 678 | 849 | str_error_r(errno, msg, sizeof(msg))); |
|---|
| 679 | | - return -1; |
|---|
| 680 | | - } |
|---|
| 681 | | - |
|---|
| 682 | | - if (perf_evlist__apply_drv_configs(evsel_list, &counter, &err_term)) { |
|---|
| 683 | | - pr_err("failed to set config \"%s\" on event %s with %d (%s)\n", |
|---|
| 684 | | - err_term->val.drv_cfg, perf_evsel__name(counter), errno, |
|---|
| 685 | | - str_error_r(errno, msg, sizeof(msg))); |
|---|
| 686 | 850 | return -1; |
|---|
| 687 | 851 | } |
|---|
| 688 | 852 | |
|---|
| .. | .. |
|---|
| 699 | 863 | if (err < 0) |
|---|
| 700 | 864 | return err; |
|---|
| 701 | 865 | |
|---|
| 702 | | - err = perf_stat_synthesize_config(is_pipe); |
|---|
| 866 | + err = perf_event__synthesize_stat_events(&stat_config, NULL, evsel_list, |
|---|
| 867 | + process_synthesized_event, is_pipe); |
|---|
| 703 | 868 | if (err < 0) |
|---|
| 704 | 869 | return err; |
|---|
| 705 | 870 | } |
|---|
| .. | .. |
|---|
| 714 | 879 | perf_evlist__start_workload(evsel_list); |
|---|
| 715 | 880 | enable_counters(); |
|---|
| 716 | 881 | |
|---|
| 717 | | - if (interval || timeout) { |
|---|
| 718 | | - while (!waitpid(child_pid, &status, WNOHANG)) { |
|---|
| 719 | | - nanosleep(&ts, NULL); |
|---|
| 720 | | - if (timeout) |
|---|
| 721 | | - break; |
|---|
| 722 | | - process_interval(); |
|---|
| 723 | | - if (interval_count && !(--times)) |
|---|
| 724 | | - break; |
|---|
| 725 | | - } |
|---|
| 882 | + if (interval || timeout || evlist__ctlfd_initialized(evsel_list)) |
|---|
| 883 | + status = dispatch_events(forks, timeout, interval, ×); |
|---|
| 884 | + if (child_pid != -1) { |
|---|
| 885 | + if (timeout) |
|---|
| 886 | + kill(child_pid, SIGTERM); |
|---|
| 887 | + wait4(child_pid, &status, 0, &stat_config.ru_data); |
|---|
| 726 | 888 | } |
|---|
| 727 | | - wait4(child_pid, &status, 0, &ru_data); |
|---|
| 728 | 889 | |
|---|
| 729 | 890 | if (workload_exec_errno) { |
|---|
| 730 | 891 | const char *emsg = str_error_r(workload_exec_errno, msg, sizeof(msg)); |
|---|
| .. | .. |
|---|
| 736 | 897 | psignal(WTERMSIG(status), argv[0]); |
|---|
| 737 | 898 | } else { |
|---|
| 738 | 899 | enable_counters(); |
|---|
| 739 | | - while (!done) { |
|---|
| 740 | | - nanosleep(&ts, NULL); |
|---|
| 741 | | - if (timeout) |
|---|
| 742 | | - break; |
|---|
| 743 | | - if (interval) { |
|---|
| 744 | | - process_interval(); |
|---|
| 745 | | - if (interval_count && !(--times)) |
|---|
| 746 | | - break; |
|---|
| 747 | | - } |
|---|
| 748 | | - } |
|---|
| 900 | + status = dispatch_events(forks, timeout, interval, ×); |
|---|
| 749 | 901 | } |
|---|
| 750 | 902 | |
|---|
| 751 | 903 | disable_counters(); |
|---|
| 752 | 904 | |
|---|
| 753 | 905 | t1 = rdclock(); |
|---|
| 754 | 906 | |
|---|
| 755 | | - if (walltime_run_table) |
|---|
| 756 | | - walltime_run[run_idx] = t1 - t0; |
|---|
| 907 | + if (stat_config.walltime_run_table) |
|---|
| 908 | + stat_config.walltime_run[run_idx] = t1 - t0; |
|---|
| 757 | 909 | |
|---|
| 758 | | - update_stats(&walltime_nsecs_stats, t1 - t0); |
|---|
| 910 | + if (interval && stat_config.summary) { |
|---|
| 911 | + stat_config.interval = 0; |
|---|
| 912 | + stat_config.stop_read_counter = true; |
|---|
| 913 | + init_stats(&walltime_nsecs_stats); |
|---|
| 914 | + update_stats(&walltime_nsecs_stats, t1 - t0); |
|---|
| 915 | + |
|---|
| 916 | + if (stat_config.aggr_mode == AGGR_GLOBAL) |
|---|
| 917 | + perf_evlist__save_aggr_prev_raw_counts(evsel_list); |
|---|
| 918 | + |
|---|
| 919 | + perf_evlist__copy_prev_raw_counts(evsel_list); |
|---|
| 920 | + perf_evlist__reset_prev_raw_counts(evsel_list); |
|---|
| 921 | + runtime_stat_reset(&stat_config); |
|---|
| 922 | + perf_stat__reset_shadow_per_stat(&rt_stat); |
|---|
| 923 | + } else |
|---|
| 924 | + update_stats(&walltime_nsecs_stats, t1 - t0); |
|---|
| 759 | 925 | |
|---|
| 760 | 926 | /* |
|---|
| 761 | 927 | * Closing a group leader splits the group, and as we only disable |
|---|
| .. | .. |
|---|
| 763 | 929 | * avoid arbitrary skew, we must read all counters before closing any |
|---|
| 764 | 930 | * group leaders. |
|---|
| 765 | 931 | */ |
|---|
| 766 | | - read_counters(); |
|---|
| 767 | | - perf_evlist__close(evsel_list); |
|---|
| 932 | + read_counters(&(struct timespec) { .tv_nsec = t1-t0 }); |
|---|
| 933 | + |
|---|
| 934 | + /* |
|---|
| 935 | + * We need to keep evsel_list alive, because it's processed |
|---|
| 936 | + * later the evsel_list will be closed after. |
|---|
| 937 | + */ |
|---|
| 938 | + if (!STAT_RECORD) |
|---|
| 939 | + evlist__close(evsel_list); |
|---|
| 768 | 940 | |
|---|
| 769 | 941 | return WEXITSTATUS(status); |
|---|
| 770 | 942 | } |
|---|
| .. | .. |
|---|
| 795 | 967 | return ret; |
|---|
| 796 | 968 | } |
|---|
| 797 | 969 | |
|---|
| 798 | | -static void print_running(u64 run, u64 ena) |
|---|
| 799 | | -{ |
|---|
| 800 | | - if (csv_output) { |
|---|
| 801 | | - fprintf(stat_config.output, "%s%" PRIu64 "%s%.2f", |
|---|
| 802 | | - csv_sep, |
|---|
| 803 | | - run, |
|---|
| 804 | | - csv_sep, |
|---|
| 805 | | - ena ? 100.0 * run / ena : 100.0); |
|---|
| 806 | | - } else if (run != ena) { |
|---|
| 807 | | - fprintf(stat_config.output, " (%.2f%%)", 100.0 * run / ena); |
|---|
| 808 | | - } |
|---|
| 809 | | -} |
|---|
| 810 | | - |
|---|
| 811 | | -static void print_noise_pct(double total, double avg) |
|---|
| 812 | | -{ |
|---|
| 813 | | - double pct = rel_stddev_stats(total, avg); |
|---|
| 814 | | - |
|---|
| 815 | | - if (csv_output) |
|---|
| 816 | | - fprintf(stat_config.output, "%s%.2f%%", csv_sep, pct); |
|---|
| 817 | | - else if (pct) |
|---|
| 818 | | - fprintf(stat_config.output, " ( +-%6.2f%% )", pct); |
|---|
| 819 | | -} |
|---|
| 820 | | - |
|---|
| 821 | | -static void print_noise(struct perf_evsel *evsel, double avg) |
|---|
| 822 | | -{ |
|---|
| 823 | | - struct perf_stat_evsel *ps; |
|---|
| 824 | | - |
|---|
| 825 | | - if (run_count == 1) |
|---|
| 826 | | - return; |
|---|
| 827 | | - |
|---|
| 828 | | - ps = evsel->stats; |
|---|
| 829 | | - print_noise_pct(stddev_stats(&ps->res_stats[0]), avg); |
|---|
| 830 | | -} |
|---|
| 831 | | - |
|---|
| 832 | | -static void aggr_printout(struct perf_evsel *evsel, int id, int nr) |
|---|
| 833 | | -{ |
|---|
| 834 | | - switch (stat_config.aggr_mode) { |
|---|
| 835 | | - case AGGR_CORE: |
|---|
| 836 | | - fprintf(stat_config.output, "S%d-C%*d%s%*d%s", |
|---|
| 837 | | - cpu_map__id_to_socket(id), |
|---|
| 838 | | - csv_output ? 0 : -8, |
|---|
| 839 | | - cpu_map__id_to_cpu(id), |
|---|
| 840 | | - csv_sep, |
|---|
| 841 | | - csv_output ? 0 : 4, |
|---|
| 842 | | - nr, |
|---|
| 843 | | - csv_sep); |
|---|
| 844 | | - break; |
|---|
| 845 | | - case AGGR_SOCKET: |
|---|
| 846 | | - fprintf(stat_config.output, "S%*d%s%*d%s", |
|---|
| 847 | | - csv_output ? 0 : -5, |
|---|
| 848 | | - id, |
|---|
| 849 | | - csv_sep, |
|---|
| 850 | | - csv_output ? 0 : 4, |
|---|
| 851 | | - nr, |
|---|
| 852 | | - csv_sep); |
|---|
| 853 | | - break; |
|---|
| 854 | | - case AGGR_NONE: |
|---|
| 855 | | - fprintf(stat_config.output, "CPU%*d%s", |
|---|
| 856 | | - csv_output ? 0 : -4, |
|---|
| 857 | | - perf_evsel__cpus(evsel)->map[id], csv_sep); |
|---|
| 858 | | - break; |
|---|
| 859 | | - case AGGR_THREAD: |
|---|
| 860 | | - fprintf(stat_config.output, "%*s-%*d%s", |
|---|
| 861 | | - csv_output ? 0 : 16, |
|---|
| 862 | | - thread_map__comm(evsel->threads, id), |
|---|
| 863 | | - csv_output ? 0 : -8, |
|---|
| 864 | | - thread_map__pid(evsel->threads, id), |
|---|
| 865 | | - csv_sep); |
|---|
| 866 | | - break; |
|---|
| 867 | | - case AGGR_GLOBAL: |
|---|
| 868 | | - case AGGR_UNSET: |
|---|
| 869 | | - default: |
|---|
| 870 | | - break; |
|---|
| 871 | | - } |
|---|
| 872 | | -} |
|---|
| 873 | | - |
|---|
| 874 | | -struct outstate { |
|---|
| 875 | | - FILE *fh; |
|---|
| 876 | | - bool newline; |
|---|
| 877 | | - const char *prefix; |
|---|
| 878 | | - int nfields; |
|---|
| 879 | | - int id, nr; |
|---|
| 880 | | - struct perf_evsel *evsel; |
|---|
| 881 | | -}; |
|---|
| 882 | | - |
|---|
| 883 | | -#define METRIC_LEN 35 |
|---|
| 884 | | - |
|---|
| 885 | | -static void new_line_std(void *ctx) |
|---|
| 886 | | -{ |
|---|
| 887 | | - struct outstate *os = ctx; |
|---|
| 888 | | - |
|---|
| 889 | | - os->newline = true; |
|---|
| 890 | | -} |
|---|
| 891 | | - |
|---|
| 892 | | -static void do_new_line_std(struct outstate *os) |
|---|
| 893 | | -{ |
|---|
| 894 | | - fputc('\n', os->fh); |
|---|
| 895 | | - fputs(os->prefix, os->fh); |
|---|
| 896 | | - aggr_printout(os->evsel, os->id, os->nr); |
|---|
| 897 | | - if (stat_config.aggr_mode == AGGR_NONE) |
|---|
| 898 | | - fprintf(os->fh, " "); |
|---|
| 899 | | - fprintf(os->fh, " "); |
|---|
| 900 | | -} |
|---|
| 901 | | - |
|---|
| 902 | | -static void print_metric_std(void *ctx, const char *color, const char *fmt, |
|---|
| 903 | | - const char *unit, double val) |
|---|
| 904 | | -{ |
|---|
| 905 | | - struct outstate *os = ctx; |
|---|
| 906 | | - FILE *out = os->fh; |
|---|
| 907 | | - int n; |
|---|
| 908 | | - bool newline = os->newline; |
|---|
| 909 | | - |
|---|
| 910 | | - os->newline = false; |
|---|
| 911 | | - |
|---|
| 912 | | - if (unit == NULL || fmt == NULL) { |
|---|
| 913 | | - fprintf(out, "%-*s", METRIC_LEN, ""); |
|---|
| 914 | | - return; |
|---|
| 915 | | - } |
|---|
| 916 | | - |
|---|
| 917 | | - if (newline) |
|---|
| 918 | | - do_new_line_std(os); |
|---|
| 919 | | - |
|---|
| 920 | | - n = fprintf(out, " # "); |
|---|
| 921 | | - if (color) |
|---|
| 922 | | - n += color_fprintf(out, color, fmt, val); |
|---|
| 923 | | - else |
|---|
| 924 | | - n += fprintf(out, fmt, val); |
|---|
| 925 | | - fprintf(out, " %-*s", METRIC_LEN - n - 1, unit); |
|---|
| 926 | | -} |
|---|
| 927 | | - |
|---|
| 928 | | -static void new_line_csv(void *ctx) |
|---|
| 929 | | -{ |
|---|
| 930 | | - struct outstate *os = ctx; |
|---|
| 931 | | - int i; |
|---|
| 932 | | - |
|---|
| 933 | | - fputc('\n', os->fh); |
|---|
| 934 | | - if (os->prefix) |
|---|
| 935 | | - fprintf(os->fh, "%s%s", os->prefix, csv_sep); |
|---|
| 936 | | - aggr_printout(os->evsel, os->id, os->nr); |
|---|
| 937 | | - for (i = 0; i < os->nfields; i++) |
|---|
| 938 | | - fputs(csv_sep, os->fh); |
|---|
| 939 | | -} |
|---|
| 940 | | - |
|---|
| 941 | | -static void print_metric_csv(void *ctx, |
|---|
| 942 | | - const char *color __maybe_unused, |
|---|
| 943 | | - const char *fmt, const char *unit, double val) |
|---|
| 944 | | -{ |
|---|
| 945 | | - struct outstate *os = ctx; |
|---|
| 946 | | - FILE *out = os->fh; |
|---|
| 947 | | - char buf[64], *vals, *ends; |
|---|
| 948 | | - |
|---|
| 949 | | - if (unit == NULL || fmt == NULL) { |
|---|
| 950 | | - fprintf(out, "%s%s", csv_sep, csv_sep); |
|---|
| 951 | | - return; |
|---|
| 952 | | - } |
|---|
| 953 | | - snprintf(buf, sizeof(buf), fmt, val); |
|---|
| 954 | | - ends = vals = ltrim(buf); |
|---|
| 955 | | - while (isdigit(*ends) || *ends == '.') |
|---|
| 956 | | - ends++; |
|---|
| 957 | | - *ends = 0; |
|---|
| 958 | | - while (isspace(*unit)) |
|---|
| 959 | | - unit++; |
|---|
| 960 | | - fprintf(out, "%s%s%s%s", csv_sep, vals, csv_sep, unit); |
|---|
| 961 | | -} |
|---|
| 962 | | - |
|---|
| 963 | | -/* Filter out some columns that don't work well in metrics only mode */ |
|---|
| 964 | | - |
|---|
| 965 | | -static bool valid_only_metric(const char *unit) |
|---|
| 966 | | -{ |
|---|
| 967 | | - if (!unit) |
|---|
| 968 | | - return false; |
|---|
| 969 | | - if (strstr(unit, "/sec") || |
|---|
| 970 | | - strstr(unit, "hz") || |
|---|
| 971 | | - strstr(unit, "Hz") || |
|---|
| 972 | | - strstr(unit, "CPUs utilized")) |
|---|
| 973 | | - return false; |
|---|
| 974 | | - return true; |
|---|
| 975 | | -} |
|---|
| 976 | | - |
|---|
| 977 | | -static const char *fixunit(char *buf, struct perf_evsel *evsel, |
|---|
| 978 | | - const char *unit) |
|---|
| 979 | | -{ |
|---|
| 980 | | - if (!strncmp(unit, "of all", 6)) { |
|---|
| 981 | | - snprintf(buf, 1024, "%s %s", perf_evsel__name(evsel), |
|---|
| 982 | | - unit); |
|---|
| 983 | | - return buf; |
|---|
| 984 | | - } |
|---|
| 985 | | - return unit; |
|---|
| 986 | | -} |
|---|
| 987 | | - |
|---|
| 988 | | -static void print_metric_only(void *ctx, const char *color, const char *fmt, |
|---|
| 989 | | - const char *unit, double val) |
|---|
| 990 | | -{ |
|---|
| 991 | | - struct outstate *os = ctx; |
|---|
| 992 | | - FILE *out = os->fh; |
|---|
| 993 | | - char buf[1024], str[1024]; |
|---|
| 994 | | - unsigned mlen = metric_only_len; |
|---|
| 995 | | - |
|---|
| 996 | | - if (!valid_only_metric(unit)) |
|---|
| 997 | | - return; |
|---|
| 998 | | - unit = fixunit(buf, os->evsel, unit); |
|---|
| 999 | | - if (mlen < strlen(unit)) |
|---|
| 1000 | | - mlen = strlen(unit) + 1; |
|---|
| 1001 | | - |
|---|
| 1002 | | - if (color) |
|---|
| 1003 | | - mlen += strlen(color) + sizeof(PERF_COLOR_RESET) - 1; |
|---|
| 1004 | | - |
|---|
| 1005 | | - color_snprintf(str, sizeof(str), color ?: "", fmt, val); |
|---|
| 1006 | | - fprintf(out, "%*s ", mlen, str); |
|---|
| 1007 | | -} |
|---|
| 1008 | | - |
|---|
| 1009 | | -static void print_metric_only_csv(void *ctx, const char *color __maybe_unused, |
|---|
| 1010 | | - const char *fmt, |
|---|
| 1011 | | - const char *unit, double val) |
|---|
| 1012 | | -{ |
|---|
| 1013 | | - struct outstate *os = ctx; |
|---|
| 1014 | | - FILE *out = os->fh; |
|---|
| 1015 | | - char buf[64], *vals, *ends; |
|---|
| 1016 | | - char tbuf[1024]; |
|---|
| 1017 | | - |
|---|
| 1018 | | - if (!valid_only_metric(unit)) |
|---|
| 1019 | | - return; |
|---|
| 1020 | | - unit = fixunit(tbuf, os->evsel, unit); |
|---|
| 1021 | | - snprintf(buf, sizeof buf, fmt, val); |
|---|
| 1022 | | - ends = vals = ltrim(buf); |
|---|
| 1023 | | - while (isdigit(*ends) || *ends == '.') |
|---|
| 1024 | | - ends++; |
|---|
| 1025 | | - *ends = 0; |
|---|
| 1026 | | - fprintf(out, "%s%s", vals, csv_sep); |
|---|
| 1027 | | -} |
|---|
| 1028 | | - |
|---|
| 1029 | | -static void new_line_metric(void *ctx __maybe_unused) |
|---|
| 1030 | | -{ |
|---|
| 1031 | | -} |
|---|
| 1032 | | - |
|---|
| 1033 | | -static void print_metric_header(void *ctx, const char *color __maybe_unused, |
|---|
| 1034 | | - const char *fmt __maybe_unused, |
|---|
| 1035 | | - const char *unit, double val __maybe_unused) |
|---|
| 1036 | | -{ |
|---|
| 1037 | | - struct outstate *os = ctx; |
|---|
| 1038 | | - char tbuf[1024]; |
|---|
| 1039 | | - |
|---|
| 1040 | | - if (!valid_only_metric(unit)) |
|---|
| 1041 | | - return; |
|---|
| 1042 | | - unit = fixunit(tbuf, os->evsel, unit); |
|---|
| 1043 | | - if (csv_output) |
|---|
| 1044 | | - fprintf(os->fh, "%s%s", unit, csv_sep); |
|---|
| 1045 | | - else |
|---|
| 1046 | | - fprintf(os->fh, "%*s ", metric_only_len, unit); |
|---|
| 1047 | | -} |
|---|
| 1048 | | - |
|---|
| 1049 | | -static int first_shadow_cpu(struct perf_evsel *evsel, int id) |
|---|
| 1050 | | -{ |
|---|
| 1051 | | - int i; |
|---|
| 1052 | | - |
|---|
| 1053 | | - if (!aggr_get_id) |
|---|
| 1054 | | - return 0; |
|---|
| 1055 | | - |
|---|
| 1056 | | - if (stat_config.aggr_mode == AGGR_NONE) |
|---|
| 1057 | | - return id; |
|---|
| 1058 | | - |
|---|
| 1059 | | - if (stat_config.aggr_mode == AGGR_GLOBAL) |
|---|
| 1060 | | - return 0; |
|---|
| 1061 | | - |
|---|
| 1062 | | - for (i = 0; i < perf_evsel__nr_cpus(evsel); i++) { |
|---|
| 1063 | | - int cpu2 = perf_evsel__cpus(evsel)->map[i]; |
|---|
| 1064 | | - |
|---|
| 1065 | | - if (aggr_get_id(evsel_list->cpus, cpu2) == id) |
|---|
| 1066 | | - return cpu2; |
|---|
| 1067 | | - } |
|---|
| 1068 | | - return 0; |
|---|
| 1069 | | -} |
|---|
| 1070 | | - |
|---|
| 1071 | | -static void abs_printout(int id, int nr, struct perf_evsel *evsel, double avg) |
|---|
| 1072 | | -{ |
|---|
| 1073 | | - FILE *output = stat_config.output; |
|---|
| 1074 | | - double sc = evsel->scale; |
|---|
| 1075 | | - const char *fmt; |
|---|
| 1076 | | - |
|---|
| 1077 | | - if (csv_output) { |
|---|
| 1078 | | - fmt = floor(sc) != sc ? "%.2f%s" : "%.0f%s"; |
|---|
| 1079 | | - } else { |
|---|
| 1080 | | - if (big_num) |
|---|
| 1081 | | - fmt = floor(sc) != sc ? "%'18.2f%s" : "%'18.0f%s"; |
|---|
| 1082 | | - else |
|---|
| 1083 | | - fmt = floor(sc) != sc ? "%18.2f%s" : "%18.0f%s"; |
|---|
| 1084 | | - } |
|---|
| 1085 | | - |
|---|
| 1086 | | - aggr_printout(evsel, id, nr); |
|---|
| 1087 | | - |
|---|
| 1088 | | - fprintf(output, fmt, avg, csv_sep); |
|---|
| 1089 | | - |
|---|
| 1090 | | - if (evsel->unit) |
|---|
| 1091 | | - fprintf(output, "%-*s%s", |
|---|
| 1092 | | - csv_output ? 0 : unit_width, |
|---|
| 1093 | | - evsel->unit, csv_sep); |
|---|
| 1094 | | - |
|---|
| 1095 | | - fprintf(output, "%-*s", csv_output ? 0 : 25, perf_evsel__name(evsel)); |
|---|
| 1096 | | - |
|---|
| 1097 | | - if (evsel->cgrp) |
|---|
| 1098 | | - fprintf(output, "%s%s", csv_sep, evsel->cgrp->name); |
|---|
| 1099 | | -} |
|---|
| 1100 | | - |
|---|
| 1101 | | -static bool is_mixed_hw_group(struct perf_evsel *counter) |
|---|
| 1102 | | -{ |
|---|
| 1103 | | - struct perf_evlist *evlist = counter->evlist; |
|---|
| 1104 | | - u32 pmu_type = counter->attr.type; |
|---|
| 1105 | | - struct perf_evsel *pos; |
|---|
| 1106 | | - |
|---|
| 1107 | | - if (counter->nr_members < 2) |
|---|
| 1108 | | - return false; |
|---|
| 1109 | | - |
|---|
| 1110 | | - evlist__for_each_entry(evlist, pos) { |
|---|
| 1111 | | - /* software events can be part of any hardware group */ |
|---|
| 1112 | | - if (pos->attr.type == PERF_TYPE_SOFTWARE) |
|---|
| 1113 | | - continue; |
|---|
| 1114 | | - if (pmu_type == PERF_TYPE_SOFTWARE) { |
|---|
| 1115 | | - pmu_type = pos->attr.type; |
|---|
| 1116 | | - continue; |
|---|
| 1117 | | - } |
|---|
| 1118 | | - if (pmu_type != pos->attr.type) |
|---|
| 1119 | | - return true; |
|---|
| 1120 | | - } |
|---|
| 1121 | | - |
|---|
| 1122 | | - return false; |
|---|
| 1123 | | -} |
|---|
| 1124 | | - |
|---|
| 1125 | | -static void printout(int id, int nr, struct perf_evsel *counter, double uval, |
|---|
| 1126 | | - char *prefix, u64 run, u64 ena, double noise, |
|---|
| 1127 | | - struct runtime_stat *st) |
|---|
| 1128 | | -{ |
|---|
| 1129 | | - struct perf_stat_output_ctx out; |
|---|
| 1130 | | - struct outstate os = { |
|---|
| 1131 | | - .fh = stat_config.output, |
|---|
| 1132 | | - .prefix = prefix ? prefix : "", |
|---|
| 1133 | | - .id = id, |
|---|
| 1134 | | - .nr = nr, |
|---|
| 1135 | | - .evsel = counter, |
|---|
| 1136 | | - }; |
|---|
| 1137 | | - print_metric_t pm = print_metric_std; |
|---|
| 1138 | | - void (*nl)(void *); |
|---|
| 1139 | | - |
|---|
| 1140 | | - if (metric_only) { |
|---|
| 1141 | | - nl = new_line_metric; |
|---|
| 1142 | | - if (csv_output) |
|---|
| 1143 | | - pm = print_metric_only_csv; |
|---|
| 1144 | | - else |
|---|
| 1145 | | - pm = print_metric_only; |
|---|
| 1146 | | - } else |
|---|
| 1147 | | - nl = new_line_std; |
|---|
| 1148 | | - |
|---|
| 1149 | | - if (csv_output && !metric_only) { |
|---|
| 1150 | | - static int aggr_fields[] = { |
|---|
| 1151 | | - [AGGR_GLOBAL] = 0, |
|---|
| 1152 | | - [AGGR_THREAD] = 1, |
|---|
| 1153 | | - [AGGR_NONE] = 1, |
|---|
| 1154 | | - [AGGR_SOCKET] = 2, |
|---|
| 1155 | | - [AGGR_CORE] = 2, |
|---|
| 1156 | | - }; |
|---|
| 1157 | | - |
|---|
| 1158 | | - pm = print_metric_csv; |
|---|
| 1159 | | - nl = new_line_csv; |
|---|
| 1160 | | - os.nfields = 3; |
|---|
| 1161 | | - os.nfields += aggr_fields[stat_config.aggr_mode]; |
|---|
| 1162 | | - if (counter->cgrp) |
|---|
| 1163 | | - os.nfields++; |
|---|
| 1164 | | - } |
|---|
| 1165 | | - if (run == 0 || ena == 0 || counter->counts->scaled == -1) { |
|---|
| 1166 | | - if (metric_only) { |
|---|
| 1167 | | - pm(&os, NULL, "", "", 0); |
|---|
| 1168 | | - return; |
|---|
| 1169 | | - } |
|---|
| 1170 | | - aggr_printout(counter, id, nr); |
|---|
| 1171 | | - |
|---|
| 1172 | | - fprintf(stat_config.output, "%*s%s", |
|---|
| 1173 | | - csv_output ? 0 : 18, |
|---|
| 1174 | | - counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED, |
|---|
| 1175 | | - csv_sep); |
|---|
| 1176 | | - |
|---|
| 1177 | | - if (counter->supported) { |
|---|
| 1178 | | - print_free_counters_hint = 1; |
|---|
| 1179 | | - if (is_mixed_hw_group(counter)) |
|---|
| 1180 | | - print_mixed_hw_group_error = 1; |
|---|
| 1181 | | - } |
|---|
| 1182 | | - |
|---|
| 1183 | | - fprintf(stat_config.output, "%-*s%s", |
|---|
| 1184 | | - csv_output ? 0 : unit_width, |
|---|
| 1185 | | - counter->unit, csv_sep); |
|---|
| 1186 | | - |
|---|
| 1187 | | - fprintf(stat_config.output, "%*s", |
|---|
| 1188 | | - csv_output ? 0 : -25, |
|---|
| 1189 | | - perf_evsel__name(counter)); |
|---|
| 1190 | | - |
|---|
| 1191 | | - if (counter->cgrp) |
|---|
| 1192 | | - fprintf(stat_config.output, "%s%s", |
|---|
| 1193 | | - csv_sep, counter->cgrp->name); |
|---|
| 1194 | | - |
|---|
| 1195 | | - if (!csv_output) |
|---|
| 1196 | | - pm(&os, NULL, NULL, "", 0); |
|---|
| 1197 | | - print_noise(counter, noise); |
|---|
| 1198 | | - print_running(run, ena); |
|---|
| 1199 | | - if (csv_output) |
|---|
| 1200 | | - pm(&os, NULL, NULL, "", 0); |
|---|
| 1201 | | - return; |
|---|
| 1202 | | - } |
|---|
| 1203 | | - |
|---|
| 1204 | | - if (!metric_only) |
|---|
| 1205 | | - abs_printout(id, nr, counter, uval); |
|---|
| 1206 | | - |
|---|
| 1207 | | - out.print_metric = pm; |
|---|
| 1208 | | - out.new_line = nl; |
|---|
| 1209 | | - out.ctx = &os; |
|---|
| 1210 | | - out.force_header = false; |
|---|
| 1211 | | - |
|---|
| 1212 | | - if (csv_output && !metric_only) { |
|---|
| 1213 | | - print_noise(counter, noise); |
|---|
| 1214 | | - print_running(run, ena); |
|---|
| 1215 | | - } |
|---|
| 1216 | | - |
|---|
| 1217 | | - perf_stat__print_shadow_stats(counter, uval, |
|---|
| 1218 | | - first_shadow_cpu(counter, id), |
|---|
| 1219 | | - &out, &metric_events, st); |
|---|
| 1220 | | - if (!csv_output && !metric_only) { |
|---|
| 1221 | | - print_noise(counter, noise); |
|---|
| 1222 | | - print_running(run, ena); |
|---|
| 1223 | | - } |
|---|
| 1224 | | -} |
|---|
| 1225 | | - |
|---|
| 1226 | | -static void aggr_update_shadow(void) |
|---|
| 1227 | | -{ |
|---|
| 1228 | | - int cpu, s2, id, s; |
|---|
| 1229 | | - u64 val; |
|---|
| 1230 | | - struct perf_evsel *counter; |
|---|
| 1231 | | - |
|---|
| 1232 | | - for (s = 0; s < aggr_map->nr; s++) { |
|---|
| 1233 | | - id = aggr_map->map[s]; |
|---|
| 1234 | | - evlist__for_each_entry(evsel_list, counter) { |
|---|
| 1235 | | - val = 0; |
|---|
| 1236 | | - for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) { |
|---|
| 1237 | | - s2 = aggr_get_id(evsel_list->cpus, cpu); |
|---|
| 1238 | | - if (s2 != id) |
|---|
| 1239 | | - continue; |
|---|
| 1240 | | - val += perf_counts(counter->counts, cpu, 0)->val; |
|---|
| 1241 | | - } |
|---|
| 1242 | | - perf_stat__update_shadow_stats(counter, val, |
|---|
| 1243 | | - first_shadow_cpu(counter, id), |
|---|
| 1244 | | - &rt_stat); |
|---|
| 1245 | | - } |
|---|
| 1246 | | - } |
|---|
| 1247 | | -} |
|---|
| 1248 | | - |
|---|
| 1249 | | -static void uniquify_event_name(struct perf_evsel *counter) |
|---|
| 1250 | | -{ |
|---|
| 1251 | | - char *new_name; |
|---|
| 1252 | | - char *config; |
|---|
| 1253 | | - |
|---|
| 1254 | | - if (counter->uniquified_name || |
|---|
| 1255 | | - !counter->pmu_name || !strncmp(counter->name, counter->pmu_name, |
|---|
| 1256 | | - strlen(counter->pmu_name))) |
|---|
| 1257 | | - return; |
|---|
| 1258 | | - |
|---|
| 1259 | | - config = strchr(counter->name, '/'); |
|---|
| 1260 | | - if (config) { |
|---|
| 1261 | | - if (asprintf(&new_name, |
|---|
| 1262 | | - "%s%s", counter->pmu_name, config) > 0) { |
|---|
| 1263 | | - free(counter->name); |
|---|
| 1264 | | - counter->name = new_name; |
|---|
| 1265 | | - } |
|---|
| 1266 | | - } else { |
|---|
| 1267 | | - if (asprintf(&new_name, |
|---|
| 1268 | | - "%s [%s]", counter->name, counter->pmu_name) > 0) { |
|---|
| 1269 | | - free(counter->name); |
|---|
| 1270 | | - counter->name = new_name; |
|---|
| 1271 | | - } |
|---|
| 1272 | | - } |
|---|
| 1273 | | - |
|---|
| 1274 | | - counter->uniquified_name = true; |
|---|
| 1275 | | -} |
|---|
| 1276 | | - |
|---|
| 1277 | | -static void collect_all_aliases(struct perf_evsel *counter, |
|---|
| 1278 | | - void (*cb)(struct perf_evsel *counter, void *data, |
|---|
| 1279 | | - bool first), |
|---|
| 1280 | | - void *data) |
|---|
| 1281 | | -{ |
|---|
| 1282 | | - struct perf_evsel *alias; |
|---|
| 1283 | | - |
|---|
| 1284 | | - alias = list_prepare_entry(counter, &(evsel_list->entries), node); |
|---|
| 1285 | | - list_for_each_entry_continue (alias, &evsel_list->entries, node) { |
|---|
| 1286 | | - if (strcmp(perf_evsel__name(alias), perf_evsel__name(counter)) || |
|---|
| 1287 | | - alias->scale != counter->scale || |
|---|
| 1288 | | - alias->cgrp != counter->cgrp || |
|---|
| 1289 | | - strcmp(alias->unit, counter->unit) || |
|---|
| 1290 | | - perf_evsel__is_clock(alias) != perf_evsel__is_clock(counter)) |
|---|
| 1291 | | - break; |
|---|
| 1292 | | - alias->merged_stat = true; |
|---|
| 1293 | | - cb(alias, data, false); |
|---|
| 1294 | | - } |
|---|
| 1295 | | -} |
|---|
| 1296 | | - |
|---|
| 1297 | | -static bool collect_data(struct perf_evsel *counter, |
|---|
| 1298 | | - void (*cb)(struct perf_evsel *counter, void *data, |
|---|
| 1299 | | - bool first), |
|---|
| 1300 | | - void *data) |
|---|
| 1301 | | -{ |
|---|
| 1302 | | - if (counter->merged_stat) |
|---|
| 1303 | | - return false; |
|---|
| 1304 | | - cb(counter, data, true); |
|---|
| 1305 | | - if (no_merge) |
|---|
| 1306 | | - uniquify_event_name(counter); |
|---|
| 1307 | | - else if (counter->auto_merge_stats) |
|---|
| 1308 | | - collect_all_aliases(counter, cb, data); |
|---|
| 1309 | | - return true; |
|---|
| 1310 | | -} |
|---|
| 1311 | | - |
|---|
| 1312 | | -struct aggr_data { |
|---|
| 1313 | | - u64 ena, run, val; |
|---|
| 1314 | | - int id; |
|---|
| 1315 | | - int nr; |
|---|
| 1316 | | - int cpu; |
|---|
| 1317 | | -}; |
|---|
| 1318 | | - |
|---|
| 1319 | | -static void aggr_cb(struct perf_evsel *counter, void *data, bool first) |
|---|
| 1320 | | -{ |
|---|
| 1321 | | - struct aggr_data *ad = data; |
|---|
| 1322 | | - int cpu, s2; |
|---|
| 1323 | | - |
|---|
| 1324 | | - for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) { |
|---|
| 1325 | | - struct perf_counts_values *counts; |
|---|
| 1326 | | - |
|---|
| 1327 | | - s2 = aggr_get_id(perf_evsel__cpus(counter), cpu); |
|---|
| 1328 | | - if (s2 != ad->id) |
|---|
| 1329 | | - continue; |
|---|
| 1330 | | - if (first) |
|---|
| 1331 | | - ad->nr++; |
|---|
| 1332 | | - counts = perf_counts(counter->counts, cpu, 0); |
|---|
| 1333 | | - /* |
|---|
| 1334 | | - * When any result is bad, make them all to give |
|---|
| 1335 | | - * consistent output in interval mode. |
|---|
| 1336 | | - */ |
|---|
| 1337 | | - if (counts->ena == 0 || counts->run == 0 || |
|---|
| 1338 | | - counter->counts->scaled == -1) { |
|---|
| 1339 | | - ad->ena = 0; |
|---|
| 1340 | | - ad->run = 0; |
|---|
| 1341 | | - break; |
|---|
| 1342 | | - } |
|---|
| 1343 | | - ad->val += counts->val; |
|---|
| 1344 | | - ad->ena += counts->ena; |
|---|
| 1345 | | - ad->run += counts->run; |
|---|
| 1346 | | - } |
|---|
| 1347 | | -} |
|---|
| 1348 | | - |
|---|
| 1349 | | -static void print_aggr(char *prefix) |
|---|
| 1350 | | -{ |
|---|
| 1351 | | - FILE *output = stat_config.output; |
|---|
| 1352 | | - struct perf_evsel *counter; |
|---|
| 1353 | | - int s, id, nr; |
|---|
| 1354 | | - double uval; |
|---|
| 1355 | | - u64 ena, run, val; |
|---|
| 1356 | | - bool first; |
|---|
| 1357 | | - |
|---|
| 1358 | | - if (!(aggr_map || aggr_get_id)) |
|---|
| 1359 | | - return; |
|---|
| 1360 | | - |
|---|
| 1361 | | - aggr_update_shadow(); |
|---|
| 1362 | | - |
|---|
| 1363 | | - /* |
|---|
| 1364 | | - * With metric_only everything is on a single line. |
|---|
| 1365 | | - * Without each counter has its own line. |
|---|
| 1366 | | - */ |
|---|
| 1367 | | - for (s = 0; s < aggr_map->nr; s++) { |
|---|
| 1368 | | - struct aggr_data ad; |
|---|
| 1369 | | - if (prefix && metric_only) |
|---|
| 1370 | | - fprintf(output, "%s", prefix); |
|---|
| 1371 | | - |
|---|
| 1372 | | - ad.id = id = aggr_map->map[s]; |
|---|
| 1373 | | - first = true; |
|---|
| 1374 | | - evlist__for_each_entry(evsel_list, counter) { |
|---|
| 1375 | | - if (is_duration_time(counter)) |
|---|
| 1376 | | - continue; |
|---|
| 1377 | | - |
|---|
| 1378 | | - ad.val = ad.ena = ad.run = 0; |
|---|
| 1379 | | - ad.nr = 0; |
|---|
| 1380 | | - if (!collect_data(counter, aggr_cb, &ad)) |
|---|
| 1381 | | - continue; |
|---|
| 1382 | | - nr = ad.nr; |
|---|
| 1383 | | - ena = ad.ena; |
|---|
| 1384 | | - run = ad.run; |
|---|
| 1385 | | - val = ad.val; |
|---|
| 1386 | | - if (first && metric_only) { |
|---|
| 1387 | | - first = false; |
|---|
| 1388 | | - aggr_printout(counter, id, nr); |
|---|
| 1389 | | - } |
|---|
| 1390 | | - if (prefix && !metric_only) |
|---|
| 1391 | | - fprintf(output, "%s", prefix); |
|---|
| 1392 | | - |
|---|
| 1393 | | - uval = val * counter->scale; |
|---|
| 1394 | | - printout(id, nr, counter, uval, prefix, run, ena, 1.0, |
|---|
| 1395 | | - &rt_stat); |
|---|
| 1396 | | - if (!metric_only) |
|---|
| 1397 | | - fputc('\n', output); |
|---|
| 1398 | | - } |
|---|
| 1399 | | - if (metric_only) |
|---|
| 1400 | | - fputc('\n', output); |
|---|
| 1401 | | - } |
|---|
| 1402 | | -} |
|---|
| 1403 | | - |
|---|
| 1404 | | -static int cmp_val(const void *a, const void *b) |
|---|
| 1405 | | -{ |
|---|
| 1406 | | - return ((struct perf_aggr_thread_value *)b)->val - |
|---|
| 1407 | | - ((struct perf_aggr_thread_value *)a)->val; |
|---|
| 1408 | | -} |
|---|
| 1409 | | - |
|---|
| 1410 | | -static struct perf_aggr_thread_value *sort_aggr_thread( |
|---|
| 1411 | | - struct perf_evsel *counter, |
|---|
| 1412 | | - int nthreads, int ncpus, |
|---|
| 1413 | | - int *ret) |
|---|
| 1414 | | -{ |
|---|
| 1415 | | - int cpu, thread, i = 0; |
|---|
| 1416 | | - double uval; |
|---|
| 1417 | | - struct perf_aggr_thread_value *buf; |
|---|
| 1418 | | - |
|---|
| 1419 | | - buf = calloc(nthreads, sizeof(struct perf_aggr_thread_value)); |
|---|
| 1420 | | - if (!buf) |
|---|
| 1421 | | - return NULL; |
|---|
| 1422 | | - |
|---|
| 1423 | | - for (thread = 0; thread < nthreads; thread++) { |
|---|
| 1424 | | - u64 ena = 0, run = 0, val = 0; |
|---|
| 1425 | | - |
|---|
| 1426 | | - for (cpu = 0; cpu < ncpus; cpu++) { |
|---|
| 1427 | | - val += perf_counts(counter->counts, cpu, thread)->val; |
|---|
| 1428 | | - ena += perf_counts(counter->counts, cpu, thread)->ena; |
|---|
| 1429 | | - run += perf_counts(counter->counts, cpu, thread)->run; |
|---|
| 1430 | | - } |
|---|
| 1431 | | - |
|---|
| 1432 | | - uval = val * counter->scale; |
|---|
| 1433 | | - |
|---|
| 1434 | | - /* |
|---|
| 1435 | | - * Skip value 0 when enabling --per-thread globally, |
|---|
| 1436 | | - * otherwise too many 0 output. |
|---|
| 1437 | | - */ |
|---|
| 1438 | | - if (uval == 0.0 && target__has_per_thread(&target)) |
|---|
| 1439 | | - continue; |
|---|
| 1440 | | - |
|---|
| 1441 | | - buf[i].counter = counter; |
|---|
| 1442 | | - buf[i].id = thread; |
|---|
| 1443 | | - buf[i].uval = uval; |
|---|
| 1444 | | - buf[i].val = val; |
|---|
| 1445 | | - buf[i].run = run; |
|---|
| 1446 | | - buf[i].ena = ena; |
|---|
| 1447 | | - i++; |
|---|
| 1448 | | - } |
|---|
| 1449 | | - |
|---|
| 1450 | | - qsort(buf, i, sizeof(struct perf_aggr_thread_value), cmp_val); |
|---|
| 1451 | | - |
|---|
| 1452 | | - if (ret) |
|---|
| 1453 | | - *ret = i; |
|---|
| 1454 | | - |
|---|
| 1455 | | - return buf; |
|---|
| 1456 | | -} |
|---|
| 1457 | | - |
|---|
| 1458 | | -static void print_aggr_thread(struct perf_evsel *counter, char *prefix) |
|---|
| 1459 | | -{ |
|---|
| 1460 | | - FILE *output = stat_config.output; |
|---|
| 1461 | | - int nthreads = thread_map__nr(counter->threads); |
|---|
| 1462 | | - int ncpus = cpu_map__nr(counter->cpus); |
|---|
| 1463 | | - int thread, sorted_threads, id; |
|---|
| 1464 | | - struct perf_aggr_thread_value *buf; |
|---|
| 1465 | | - |
|---|
| 1466 | | - buf = sort_aggr_thread(counter, nthreads, ncpus, &sorted_threads); |
|---|
| 1467 | | - if (!buf) { |
|---|
| 1468 | | - perror("cannot sort aggr thread"); |
|---|
| 1469 | | - return; |
|---|
| 1470 | | - } |
|---|
| 1471 | | - |
|---|
| 1472 | | - for (thread = 0; thread < sorted_threads; thread++) { |
|---|
| 1473 | | - if (prefix) |
|---|
| 1474 | | - fprintf(output, "%s", prefix); |
|---|
| 1475 | | - |
|---|
| 1476 | | - id = buf[thread].id; |
|---|
| 1477 | | - if (stat_config.stats) |
|---|
| 1478 | | - printout(id, 0, buf[thread].counter, buf[thread].uval, |
|---|
| 1479 | | - prefix, buf[thread].run, buf[thread].ena, 1.0, |
|---|
| 1480 | | - &stat_config.stats[id]); |
|---|
| 1481 | | - else |
|---|
| 1482 | | - printout(id, 0, buf[thread].counter, buf[thread].uval, |
|---|
| 1483 | | - prefix, buf[thread].run, buf[thread].ena, 1.0, |
|---|
| 1484 | | - &rt_stat); |
|---|
| 1485 | | - fputc('\n', output); |
|---|
| 1486 | | - } |
|---|
| 1487 | | - |
|---|
| 1488 | | - free(buf); |
|---|
| 1489 | | -} |
|---|
| 1490 | | - |
|---|
| 1491 | | -struct caggr_data { |
|---|
| 1492 | | - double avg, avg_enabled, avg_running; |
|---|
| 1493 | | -}; |
|---|
| 1494 | | - |
|---|
| 1495 | | -static void counter_aggr_cb(struct perf_evsel *counter, void *data, |
|---|
| 1496 | | - bool first __maybe_unused) |
|---|
| 1497 | | -{ |
|---|
| 1498 | | - struct caggr_data *cd = data; |
|---|
| 1499 | | - struct perf_stat_evsel *ps = counter->stats; |
|---|
| 1500 | | - |
|---|
| 1501 | | - cd->avg += avg_stats(&ps->res_stats[0]); |
|---|
| 1502 | | - cd->avg_enabled += avg_stats(&ps->res_stats[1]); |
|---|
| 1503 | | - cd->avg_running += avg_stats(&ps->res_stats[2]); |
|---|
| 1504 | | -} |
|---|
| 1505 | | - |
|---|
| 1506 | | -/* |
|---|
| 1507 | | - * Print out the results of a single counter: |
|---|
| 1508 | | - * aggregated counts in system-wide mode |
|---|
| 1509 | | - */ |
|---|
| 1510 | | -static void print_counter_aggr(struct perf_evsel *counter, char *prefix) |
|---|
| 1511 | | -{ |
|---|
| 1512 | | - FILE *output = stat_config.output; |
|---|
| 1513 | | - double uval; |
|---|
| 1514 | | - struct caggr_data cd = { .avg = 0.0 }; |
|---|
| 1515 | | - |
|---|
| 1516 | | - if (!collect_data(counter, counter_aggr_cb, &cd)) |
|---|
| 1517 | | - return; |
|---|
| 1518 | | - |
|---|
| 1519 | | - if (prefix && !metric_only) |
|---|
| 1520 | | - fprintf(output, "%s", prefix); |
|---|
| 1521 | | - |
|---|
| 1522 | | - uval = cd.avg * counter->scale; |
|---|
| 1523 | | - printout(-1, 0, counter, uval, prefix, cd.avg_running, cd.avg_enabled, |
|---|
| 1524 | | - cd.avg, &rt_stat); |
|---|
| 1525 | | - if (!metric_only) |
|---|
| 1526 | | - fprintf(output, "\n"); |
|---|
| 1527 | | -} |
|---|
| 1528 | | - |
|---|
| 1529 | | -static void counter_cb(struct perf_evsel *counter, void *data, |
|---|
| 1530 | | - bool first __maybe_unused) |
|---|
| 1531 | | -{ |
|---|
| 1532 | | - struct aggr_data *ad = data; |
|---|
| 1533 | | - |
|---|
| 1534 | | - ad->val += perf_counts(counter->counts, ad->cpu, 0)->val; |
|---|
| 1535 | | - ad->ena += perf_counts(counter->counts, ad->cpu, 0)->ena; |
|---|
| 1536 | | - ad->run += perf_counts(counter->counts, ad->cpu, 0)->run; |
|---|
| 1537 | | -} |
|---|
| 1538 | | - |
|---|
| 1539 | | -/* |
|---|
| 1540 | | - * Print out the results of a single counter: |
|---|
| 1541 | | - * does not use aggregated count in system-wide |
|---|
| 1542 | | - */ |
|---|
| 1543 | | -static void print_counter(struct perf_evsel *counter, char *prefix) |
|---|
| 1544 | | -{ |
|---|
| 1545 | | - FILE *output = stat_config.output; |
|---|
| 1546 | | - u64 ena, run, val; |
|---|
| 1547 | | - double uval; |
|---|
| 1548 | | - int cpu; |
|---|
| 1549 | | - |
|---|
| 1550 | | - for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) { |
|---|
| 1551 | | - struct aggr_data ad = { .cpu = cpu }; |
|---|
| 1552 | | - |
|---|
| 1553 | | - if (!collect_data(counter, counter_cb, &ad)) |
|---|
| 1554 | | - return; |
|---|
| 1555 | | - val = ad.val; |
|---|
| 1556 | | - ena = ad.ena; |
|---|
| 1557 | | - run = ad.run; |
|---|
| 1558 | | - |
|---|
| 1559 | | - if (prefix) |
|---|
| 1560 | | - fprintf(output, "%s", prefix); |
|---|
| 1561 | | - |
|---|
| 1562 | | - uval = val * counter->scale; |
|---|
| 1563 | | - printout(cpu, 0, counter, uval, prefix, run, ena, 1.0, |
|---|
| 1564 | | - &rt_stat); |
|---|
| 1565 | | - |
|---|
| 1566 | | - fputc('\n', output); |
|---|
| 1567 | | - } |
|---|
| 1568 | | -} |
|---|
| 1569 | | - |
|---|
| 1570 | | -static void print_no_aggr_metric(char *prefix) |
|---|
| 1571 | | -{ |
|---|
| 1572 | | - int cpu; |
|---|
| 1573 | | - int nrcpus = 0; |
|---|
| 1574 | | - struct perf_evsel *counter; |
|---|
| 1575 | | - u64 ena, run, val; |
|---|
| 1576 | | - double uval; |
|---|
| 1577 | | - |
|---|
| 1578 | | - nrcpus = evsel_list->cpus->nr; |
|---|
| 1579 | | - for (cpu = 0; cpu < nrcpus; cpu++) { |
|---|
| 1580 | | - bool first = true; |
|---|
| 1581 | | - |
|---|
| 1582 | | - if (prefix) |
|---|
| 1583 | | - fputs(prefix, stat_config.output); |
|---|
| 1584 | | - evlist__for_each_entry(evsel_list, counter) { |
|---|
| 1585 | | - if (is_duration_time(counter)) |
|---|
| 1586 | | - continue; |
|---|
| 1587 | | - if (first) { |
|---|
| 1588 | | - aggr_printout(counter, cpu, 0); |
|---|
| 1589 | | - first = false; |
|---|
| 1590 | | - } |
|---|
| 1591 | | - val = perf_counts(counter->counts, cpu, 0)->val; |
|---|
| 1592 | | - ena = perf_counts(counter->counts, cpu, 0)->ena; |
|---|
| 1593 | | - run = perf_counts(counter->counts, cpu, 0)->run; |
|---|
| 1594 | | - |
|---|
| 1595 | | - uval = val * counter->scale; |
|---|
| 1596 | | - printout(cpu, 0, counter, uval, prefix, run, ena, 1.0, |
|---|
| 1597 | | - &rt_stat); |
|---|
| 1598 | | - } |
|---|
| 1599 | | - fputc('\n', stat_config.output); |
|---|
| 1600 | | - } |
|---|
| 1601 | | -} |
|---|
| 1602 | | - |
|---|
| 1603 | | -static int aggr_header_lens[] = { |
|---|
| 1604 | | - [AGGR_CORE] = 18, |
|---|
| 1605 | | - [AGGR_SOCKET] = 12, |
|---|
| 1606 | | - [AGGR_NONE] = 6, |
|---|
| 1607 | | - [AGGR_THREAD] = 24, |
|---|
| 1608 | | - [AGGR_GLOBAL] = 0, |
|---|
| 1609 | | -}; |
|---|
| 1610 | | - |
|---|
| 1611 | | -static const char *aggr_header_csv[] = { |
|---|
| 1612 | | - [AGGR_CORE] = "core,cpus,", |
|---|
| 1613 | | - [AGGR_SOCKET] = "socket,cpus", |
|---|
| 1614 | | - [AGGR_NONE] = "cpu,", |
|---|
| 1615 | | - [AGGR_THREAD] = "comm-pid,", |
|---|
| 1616 | | - [AGGR_GLOBAL] = "" |
|---|
| 1617 | | -}; |
|---|
| 1618 | | - |
|---|
| 1619 | | -static void print_metric_headers(const char *prefix, bool no_indent) |
|---|
| 1620 | | -{ |
|---|
| 1621 | | - struct perf_stat_output_ctx out; |
|---|
| 1622 | | - struct perf_evsel *counter; |
|---|
| 1623 | | - struct outstate os = { |
|---|
| 1624 | | - .fh = stat_config.output |
|---|
| 1625 | | - }; |
|---|
| 1626 | | - |
|---|
| 1627 | | - if (prefix) |
|---|
| 1628 | | - fprintf(stat_config.output, "%s", prefix); |
|---|
| 1629 | | - |
|---|
| 1630 | | - if (!csv_output && !no_indent) |
|---|
| 1631 | | - fprintf(stat_config.output, "%*s", |
|---|
| 1632 | | - aggr_header_lens[stat_config.aggr_mode], ""); |
|---|
| 1633 | | - if (csv_output) { |
|---|
| 1634 | | - if (stat_config.interval) |
|---|
| 1635 | | - fputs("time,", stat_config.output); |
|---|
| 1636 | | - fputs(aggr_header_csv[stat_config.aggr_mode], |
|---|
| 1637 | | - stat_config.output); |
|---|
| 1638 | | - } |
|---|
| 1639 | | - |
|---|
| 1640 | | - /* Print metrics headers only */ |
|---|
| 1641 | | - evlist__for_each_entry(evsel_list, counter) { |
|---|
| 1642 | | - if (is_duration_time(counter)) |
|---|
| 1643 | | - continue; |
|---|
| 1644 | | - os.evsel = counter; |
|---|
| 1645 | | - out.ctx = &os; |
|---|
| 1646 | | - out.print_metric = print_metric_header; |
|---|
| 1647 | | - out.new_line = new_line_metric; |
|---|
| 1648 | | - out.force_header = true; |
|---|
| 1649 | | - os.evsel = counter; |
|---|
| 1650 | | - perf_stat__print_shadow_stats(counter, 0, |
|---|
| 1651 | | - 0, |
|---|
| 1652 | | - &out, |
|---|
| 1653 | | - &metric_events, |
|---|
| 1654 | | - &rt_stat); |
|---|
| 1655 | | - } |
|---|
| 1656 | | - fputc('\n', stat_config.output); |
|---|
| 1657 | | -} |
|---|
| 1658 | | - |
|---|
| 1659 | | -static void print_interval(char *prefix, struct timespec *ts) |
|---|
| 1660 | | -{ |
|---|
| 1661 | | - FILE *output = stat_config.output; |
|---|
| 1662 | | - static int num_print_interval; |
|---|
| 1663 | | - |
|---|
| 1664 | | - if (interval_clear) |
|---|
| 1665 | | - puts(CONSOLE_CLEAR); |
|---|
| 1666 | | - |
|---|
| 1667 | | - sprintf(prefix, "%6lu.%09lu%s", ts->tv_sec, ts->tv_nsec, csv_sep); |
|---|
| 1668 | | - |
|---|
| 1669 | | - if ((num_print_interval == 0 && !csv_output) || interval_clear) { |
|---|
| 1670 | | - switch (stat_config.aggr_mode) { |
|---|
| 1671 | | - case AGGR_SOCKET: |
|---|
| 1672 | | - fprintf(output, "# time socket cpus"); |
|---|
| 1673 | | - if (!metric_only) |
|---|
| 1674 | | - fprintf(output, " counts %*s events\n", unit_width, "unit"); |
|---|
| 1675 | | - break; |
|---|
| 1676 | | - case AGGR_CORE: |
|---|
| 1677 | | - fprintf(output, "# time core cpus"); |
|---|
| 1678 | | - if (!metric_only) |
|---|
| 1679 | | - fprintf(output, " counts %*s events\n", unit_width, "unit"); |
|---|
| 1680 | | - break; |
|---|
| 1681 | | - case AGGR_NONE: |
|---|
| 1682 | | - fprintf(output, "# time CPU "); |
|---|
| 1683 | | - if (!metric_only) |
|---|
| 1684 | | - fprintf(output, " counts %*s events\n", unit_width, "unit"); |
|---|
| 1685 | | - break; |
|---|
| 1686 | | - case AGGR_THREAD: |
|---|
| 1687 | | - fprintf(output, "# time comm-pid"); |
|---|
| 1688 | | - if (!metric_only) |
|---|
| 1689 | | - fprintf(output, " counts %*s events\n", unit_width, "unit"); |
|---|
| 1690 | | - break; |
|---|
| 1691 | | - case AGGR_GLOBAL: |
|---|
| 1692 | | - default: |
|---|
| 1693 | | - fprintf(output, "# time"); |
|---|
| 1694 | | - if (!metric_only) |
|---|
| 1695 | | - fprintf(output, " counts %*s events\n", unit_width, "unit"); |
|---|
| 1696 | | - case AGGR_UNSET: |
|---|
| 1697 | | - break; |
|---|
| 1698 | | - } |
|---|
| 1699 | | - } |
|---|
| 1700 | | - |
|---|
| 1701 | | - if ((num_print_interval == 0 || interval_clear) && metric_only) |
|---|
| 1702 | | - print_metric_headers(" ", true); |
|---|
| 1703 | | - if (++num_print_interval == 25) |
|---|
| 1704 | | - num_print_interval = 0; |
|---|
| 1705 | | -} |
|---|
| 1706 | | - |
|---|
| 1707 | | -static void print_header(int argc, const char **argv) |
|---|
| 1708 | | -{ |
|---|
| 1709 | | - FILE *output = stat_config.output; |
|---|
| 1710 | | - int i; |
|---|
| 1711 | | - |
|---|
| 1712 | | - fflush(stdout); |
|---|
| 1713 | | - |
|---|
| 1714 | | - if (!csv_output) { |
|---|
| 1715 | | - fprintf(output, "\n"); |
|---|
| 1716 | | - fprintf(output, " Performance counter stats for "); |
|---|
| 1717 | | - if (target.system_wide) |
|---|
| 1718 | | - fprintf(output, "\'system wide"); |
|---|
| 1719 | | - else if (target.cpu_list) |
|---|
| 1720 | | - fprintf(output, "\'CPU(s) %s", target.cpu_list); |
|---|
| 1721 | | - else if (!target__has_task(&target)) { |
|---|
| 1722 | | - fprintf(output, "\'%s", argv ? argv[0] : "pipe"); |
|---|
| 1723 | | - for (i = 1; argv && (i < argc); i++) |
|---|
| 1724 | | - fprintf(output, " %s", argv[i]); |
|---|
| 1725 | | - } else if (target.pid) |
|---|
| 1726 | | - fprintf(output, "process id \'%s", target.pid); |
|---|
| 1727 | | - else |
|---|
| 1728 | | - fprintf(output, "thread id \'%s", target.tid); |
|---|
| 1729 | | - |
|---|
| 1730 | | - fprintf(output, "\'"); |
|---|
| 1731 | | - if (run_count > 1) |
|---|
| 1732 | | - fprintf(output, " (%d runs)", run_count); |
|---|
| 1733 | | - fprintf(output, ":\n\n"); |
|---|
| 1734 | | - } |
|---|
| 1735 | | -} |
|---|
| 1736 | | - |
|---|
| 1737 | | -static int get_precision(double num) |
|---|
| 1738 | | -{ |
|---|
| 1739 | | - if (num > 1) |
|---|
| 1740 | | - return 0; |
|---|
| 1741 | | - |
|---|
| 1742 | | - return lround(ceil(-log10(num))); |
|---|
| 1743 | | -} |
|---|
| 1744 | | - |
|---|
| 1745 | | -static void print_table(FILE *output, int precision, double avg) |
|---|
| 1746 | | -{ |
|---|
| 1747 | | - char tmp[64]; |
|---|
| 1748 | | - int idx, indent = 0; |
|---|
| 1749 | | - |
|---|
| 1750 | | - scnprintf(tmp, 64, " %17.*f", precision, avg); |
|---|
| 1751 | | - while (tmp[indent] == ' ') |
|---|
| 1752 | | - indent++; |
|---|
| 1753 | | - |
|---|
| 1754 | | - fprintf(output, "%*s# Table of individual measurements:\n", indent, ""); |
|---|
| 1755 | | - |
|---|
| 1756 | | - for (idx = 0; idx < run_count; idx++) { |
|---|
| 1757 | | - double run = (double) walltime_run[idx] / NSEC_PER_SEC; |
|---|
| 1758 | | - int h, n = 1 + abs((int) (100.0 * (run - avg)/run) / 5); |
|---|
| 1759 | | - |
|---|
| 1760 | | - fprintf(output, " %17.*f (%+.*f) ", |
|---|
| 1761 | | - precision, run, precision, run - avg); |
|---|
| 1762 | | - |
|---|
| 1763 | | - for (h = 0; h < n; h++) |
|---|
| 1764 | | - fprintf(output, "#"); |
|---|
| 1765 | | - |
|---|
| 1766 | | - fprintf(output, "\n"); |
|---|
| 1767 | | - } |
|---|
| 1768 | | - |
|---|
| 1769 | | - fprintf(output, "\n%*s# Final result:\n", indent, ""); |
|---|
| 1770 | | -} |
|---|
| 1771 | | - |
|---|
| 1772 | | -static double timeval2double(struct timeval *t) |
|---|
| 1773 | | -{ |
|---|
| 1774 | | - return t->tv_sec + (double) t->tv_usec/USEC_PER_SEC; |
|---|
| 1775 | | -} |
|---|
| 1776 | | - |
|---|
| 1777 | | -static void print_footer(void) |
|---|
| 1778 | | -{ |
|---|
| 1779 | | - double avg = avg_stats(&walltime_nsecs_stats) / NSEC_PER_SEC; |
|---|
| 1780 | | - FILE *output = stat_config.output; |
|---|
| 1781 | | - int n; |
|---|
| 1782 | | - |
|---|
| 1783 | | - if (!null_run) |
|---|
| 1784 | | - fprintf(output, "\n"); |
|---|
| 1785 | | - |
|---|
| 1786 | | - if (run_count == 1) { |
|---|
| 1787 | | - fprintf(output, " %17.9f seconds time elapsed", avg); |
|---|
| 1788 | | - |
|---|
| 1789 | | - if (ru_display) { |
|---|
| 1790 | | - double ru_utime = timeval2double(&ru_data.ru_utime); |
|---|
| 1791 | | - double ru_stime = timeval2double(&ru_data.ru_stime); |
|---|
| 1792 | | - |
|---|
| 1793 | | - fprintf(output, "\n\n"); |
|---|
| 1794 | | - fprintf(output, " %17.9f seconds user\n", ru_utime); |
|---|
| 1795 | | - fprintf(output, " %17.9f seconds sys\n", ru_stime); |
|---|
| 1796 | | - } |
|---|
| 1797 | | - } else { |
|---|
| 1798 | | - double sd = stddev_stats(&walltime_nsecs_stats) / NSEC_PER_SEC; |
|---|
| 1799 | | - /* |
|---|
| 1800 | | - * Display at most 2 more significant |
|---|
| 1801 | | - * digits than the stddev inaccuracy. |
|---|
| 1802 | | - */ |
|---|
| 1803 | | - int precision = get_precision(sd) + 2; |
|---|
| 1804 | | - |
|---|
| 1805 | | - if (walltime_run_table) |
|---|
| 1806 | | - print_table(output, precision, avg); |
|---|
| 1807 | | - |
|---|
| 1808 | | - fprintf(output, " %17.*f +- %.*f seconds time elapsed", |
|---|
| 1809 | | - precision, avg, precision, sd); |
|---|
| 1810 | | - |
|---|
| 1811 | | - print_noise_pct(sd, avg); |
|---|
| 1812 | | - } |
|---|
| 1813 | | - fprintf(output, "\n\n"); |
|---|
| 1814 | | - |
|---|
| 1815 | | - if (print_free_counters_hint && |
|---|
| 1816 | | - sysctl__read_int("kernel/nmi_watchdog", &n) >= 0 && |
|---|
| 1817 | | - n > 0) |
|---|
| 1818 | | - fprintf(output, |
|---|
| 1819 | | -"Some events weren't counted. Try disabling the NMI watchdog:\n" |
|---|
| 1820 | | -" echo 0 > /proc/sys/kernel/nmi_watchdog\n" |
|---|
| 1821 | | -" perf stat ...\n" |
|---|
| 1822 | | -" echo 1 > /proc/sys/kernel/nmi_watchdog\n"); |
|---|
| 1823 | | - |
|---|
| 1824 | | - if (print_mixed_hw_group_error) |
|---|
| 1825 | | - fprintf(output, |
|---|
| 1826 | | - "The events in group usually have to be from " |
|---|
| 1827 | | - "the same PMU. Try reorganizing the group.\n"); |
|---|
| 1828 | | -} |
|---|
| 1829 | | - |
|---|
| 1830 | 970 | static void print_counters(struct timespec *ts, int argc, const char **argv) |
|---|
| 1831 | 971 | { |
|---|
| 1832 | | - int interval = stat_config.interval; |
|---|
| 1833 | | - struct perf_evsel *counter; |
|---|
| 1834 | | - char buf[64], *prefix = NULL; |
|---|
| 1835 | | - |
|---|
| 1836 | 972 | /* Do not print anything if we record to the pipe. */ |
|---|
| 1837 | 973 | if (STAT_RECORD && perf_stat.data.is_pipe) |
|---|
| 1838 | 974 | return; |
|---|
| 1839 | 975 | |
|---|
| 1840 | | - if (interval) |
|---|
| 1841 | | - print_interval(prefix = buf, ts); |
|---|
| 1842 | | - else |
|---|
| 1843 | | - print_header(argc, argv); |
|---|
| 1844 | | - |
|---|
| 1845 | | - if (metric_only) { |
|---|
| 1846 | | - static int num_print_iv; |
|---|
| 1847 | | - |
|---|
| 1848 | | - if (num_print_iv == 0 && !interval) |
|---|
| 1849 | | - print_metric_headers(prefix, false); |
|---|
| 1850 | | - if (num_print_iv++ == 25) |
|---|
| 1851 | | - num_print_iv = 0; |
|---|
| 1852 | | - if (stat_config.aggr_mode == AGGR_GLOBAL && prefix) |
|---|
| 1853 | | - fprintf(stat_config.output, "%s", prefix); |
|---|
| 1854 | | - } |
|---|
| 1855 | | - |
|---|
| 1856 | | - switch (stat_config.aggr_mode) { |
|---|
| 1857 | | - case AGGR_CORE: |
|---|
| 1858 | | - case AGGR_SOCKET: |
|---|
| 1859 | | - print_aggr(prefix); |
|---|
| 1860 | | - break; |
|---|
| 1861 | | - case AGGR_THREAD: |
|---|
| 1862 | | - evlist__for_each_entry(evsel_list, counter) { |
|---|
| 1863 | | - if (is_duration_time(counter)) |
|---|
| 1864 | | - continue; |
|---|
| 1865 | | - print_aggr_thread(counter, prefix); |
|---|
| 1866 | | - } |
|---|
| 1867 | | - break; |
|---|
| 1868 | | - case AGGR_GLOBAL: |
|---|
| 1869 | | - evlist__for_each_entry(evsel_list, counter) { |
|---|
| 1870 | | - if (is_duration_time(counter)) |
|---|
| 1871 | | - continue; |
|---|
| 1872 | | - print_counter_aggr(counter, prefix); |
|---|
| 1873 | | - } |
|---|
| 1874 | | - if (metric_only) |
|---|
| 1875 | | - fputc('\n', stat_config.output); |
|---|
| 1876 | | - break; |
|---|
| 1877 | | - case AGGR_NONE: |
|---|
| 1878 | | - if (metric_only) |
|---|
| 1879 | | - print_no_aggr_metric(prefix); |
|---|
| 1880 | | - else { |
|---|
| 1881 | | - evlist__for_each_entry(evsel_list, counter) { |
|---|
| 1882 | | - if (is_duration_time(counter)) |
|---|
| 1883 | | - continue; |
|---|
| 1884 | | - print_counter(counter, prefix); |
|---|
| 1885 | | - } |
|---|
| 1886 | | - } |
|---|
| 1887 | | - break; |
|---|
| 1888 | | - case AGGR_UNSET: |
|---|
| 1889 | | - default: |
|---|
| 1890 | | - break; |
|---|
| 1891 | | - } |
|---|
| 1892 | | - |
|---|
| 1893 | | - if (!interval && !csv_output) |
|---|
| 1894 | | - print_footer(); |
|---|
| 1895 | | - |
|---|
| 1896 | | - fflush(stat_config.output); |
|---|
| 976 | + perf_evlist__print_counters(evsel_list, &stat_config, &target, |
|---|
| 977 | + ts, argc, argv); |
|---|
| 1897 | 978 | } |
|---|
| 1898 | 979 | |
|---|
| 1899 | 980 | static volatile int signr = -1; |
|---|
| .. | .. |
|---|
| 1939 | 1020 | kill(getpid(), signr); |
|---|
| 1940 | 1021 | } |
|---|
| 1941 | 1022 | |
|---|
| 1023 | +void perf_stat__set_big_num(int set) |
|---|
| 1024 | +{ |
|---|
| 1025 | + stat_config.big_num = (set != 0); |
|---|
| 1026 | +} |
|---|
| 1027 | + |
|---|
| 1942 | 1028 | static int stat__set_big_num(const struct option *opt __maybe_unused, |
|---|
| 1943 | 1029 | const char *s __maybe_unused, int unset) |
|---|
| 1944 | 1030 | { |
|---|
| 1945 | 1031 | big_num_opt = unset ? 0 : 1; |
|---|
| 1032 | + perf_stat__set_big_num(!unset); |
|---|
| 1946 | 1033 | return 0; |
|---|
| 1947 | 1034 | } |
|---|
| 1948 | 1035 | |
|---|
| .. | .. |
|---|
| 1950 | 1037 | const char *s __maybe_unused, int unset) |
|---|
| 1951 | 1038 | { |
|---|
| 1952 | 1039 | force_metric_only = true; |
|---|
| 1953 | | - metric_only = !unset; |
|---|
| 1040 | + stat_config.metric_only = !unset; |
|---|
| 1954 | 1041 | return 0; |
|---|
| 1955 | 1042 | } |
|---|
| 1956 | 1043 | |
|---|
| .. | .. |
|---|
| 1958 | 1045 | const char *str, |
|---|
| 1959 | 1046 | int unset __maybe_unused) |
|---|
| 1960 | 1047 | { |
|---|
| 1961 | | - return metricgroup__parse_groups(opt, str, &metric_events); |
|---|
| 1048 | + return metricgroup__parse_groups(opt, str, |
|---|
| 1049 | + stat_config.metric_no_group, |
|---|
| 1050 | + stat_config.metric_no_merge, |
|---|
| 1051 | + &stat_config.metric_events); |
|---|
| 1052 | +} |
|---|
| 1053 | + |
|---|
| 1054 | +static int parse_control_option(const struct option *opt, |
|---|
| 1055 | + const char *str, |
|---|
| 1056 | + int unset __maybe_unused) |
|---|
| 1057 | +{ |
|---|
| 1058 | + struct perf_stat_config *config = opt->value; |
|---|
| 1059 | + |
|---|
| 1060 | + return evlist__parse_control(str, &config->ctl_fd, &config->ctl_fd_ack, &config->ctl_fd_close); |
|---|
| 1061 | +} |
|---|
| 1062 | + |
|---|
| 1063 | +static int parse_stat_cgroups(const struct option *opt, |
|---|
| 1064 | + const char *str, int unset) |
|---|
| 1065 | +{ |
|---|
| 1066 | + if (stat_config.cgroup_list) { |
|---|
| 1067 | + pr_err("--cgroup and --for-each-cgroup cannot be used together\n"); |
|---|
| 1068 | + return -1; |
|---|
| 1069 | + } |
|---|
| 1070 | + |
|---|
| 1071 | + return parse_cgroups(opt, str, unset); |
|---|
| 1962 | 1072 | } |
|---|
| 1963 | 1073 | |
|---|
| 1964 | 1074 | static struct option stat_options[] = { |
|---|
| .. | .. |
|---|
| 1969 | 1079 | parse_events_option), |
|---|
| 1970 | 1080 | OPT_CALLBACK(0, "filter", &evsel_list, "filter", |
|---|
| 1971 | 1081 | "event filter", parse_filter), |
|---|
| 1972 | | - OPT_BOOLEAN('i', "no-inherit", &no_inherit, |
|---|
| 1082 | + OPT_BOOLEAN('i', "no-inherit", &stat_config.no_inherit, |
|---|
| 1973 | 1083 | "child tasks do not inherit counters"), |
|---|
| 1974 | 1084 | OPT_STRING('p', "pid", &target.pid, "pid", |
|---|
| 1975 | 1085 | "stat events on existing process id"), |
|---|
| .. | .. |
|---|
| 1979 | 1089 | "system-wide collection from all CPUs"), |
|---|
| 1980 | 1090 | OPT_BOOLEAN('g', "group", &group, |
|---|
| 1981 | 1091 | "put the counters into a counter group"), |
|---|
| 1982 | | - OPT_BOOLEAN('c', "scale", &stat_config.scale, "scale/normalize counters"), |
|---|
| 1092 | + OPT_BOOLEAN(0, "scale", &stat_config.scale, |
|---|
| 1093 | + "Use --no-scale to disable counter scaling for multiplexing"), |
|---|
| 1983 | 1094 | OPT_INCR('v', "verbose", &verbose, |
|---|
| 1984 | 1095 | "be more verbose (show counter open errors, etc)"), |
|---|
| 1985 | | - OPT_INTEGER('r', "repeat", &run_count, |
|---|
| 1096 | + OPT_INTEGER('r', "repeat", &stat_config.run_count, |
|---|
| 1986 | 1097 | "repeat command and print average + stddev (max: 100, forever: 0)"), |
|---|
| 1987 | | - OPT_BOOLEAN(0, "table", &walltime_run_table, |
|---|
| 1098 | + OPT_BOOLEAN(0, "table", &stat_config.walltime_run_table, |
|---|
| 1988 | 1099 | "display details about each run (only with -r option)"), |
|---|
| 1989 | | - OPT_BOOLEAN('n', "null", &null_run, |
|---|
| 1100 | + OPT_BOOLEAN('n', "null", &stat_config.null_run, |
|---|
| 1990 | 1101 | "null run - dont start any counters"), |
|---|
| 1991 | 1102 | OPT_INCR('d', "detailed", &detailed_run, |
|---|
| 1992 | 1103 | "detailed run - start a lot of events"), |
|---|
| .. | .. |
|---|
| 1999 | 1110 | "list of cpus to monitor in system-wide"), |
|---|
| 2000 | 1111 | OPT_SET_UINT('A', "no-aggr", &stat_config.aggr_mode, |
|---|
| 2001 | 1112 | "disable CPU count aggregation", AGGR_NONE), |
|---|
| 2002 | | - OPT_BOOLEAN(0, "no-merge", &no_merge, "Do not merge identical named events"), |
|---|
| 2003 | | - OPT_STRING('x', "field-separator", &csv_sep, "separator", |
|---|
| 1113 | + OPT_BOOLEAN(0, "no-merge", &stat_config.no_merge, "Do not merge identical named events"), |
|---|
| 1114 | + OPT_STRING('x', "field-separator", &stat_config.csv_sep, "separator", |
|---|
| 2004 | 1115 | "print counts with custom separator"), |
|---|
| 2005 | 1116 | OPT_CALLBACK('G', "cgroup", &evsel_list, "name", |
|---|
| 2006 | | - "monitor event in cgroup name only", parse_cgroups), |
|---|
| 1117 | + "monitor event in cgroup name only", parse_stat_cgroups), |
|---|
| 1118 | + OPT_STRING(0, "for-each-cgroup", &stat_config.cgroup_list, "name", |
|---|
| 1119 | + "expand events for each cgroup"), |
|---|
| 2007 | 1120 | OPT_STRING('o', "output", &output_name, "file", "output file name"), |
|---|
| 2008 | 1121 | OPT_BOOLEAN(0, "append", &append_file, "append to the output file"), |
|---|
| 2009 | 1122 | OPT_INTEGER(0, "log-fd", &output_fd, |
|---|
| .. | .. |
|---|
| 2017 | 1130 | "(overhead is possible for values <= 100ms)"), |
|---|
| 2018 | 1131 | OPT_INTEGER(0, "interval-count", &stat_config.times, |
|---|
| 2019 | 1132 | "print counts for fixed number of times"), |
|---|
| 2020 | | - OPT_BOOLEAN(0, "interval-clear", &interval_clear, |
|---|
| 1133 | + OPT_BOOLEAN(0, "interval-clear", &stat_config.interval_clear, |
|---|
| 2021 | 1134 | "clear screen in between new interval"), |
|---|
| 2022 | 1135 | OPT_UINTEGER(0, "timeout", &stat_config.timeout, |
|---|
| 2023 | 1136 | "stop workload and print counts after a timeout period in ms (>= 10ms)"), |
|---|
| 2024 | 1137 | OPT_SET_UINT(0, "per-socket", &stat_config.aggr_mode, |
|---|
| 2025 | 1138 | "aggregate counts per processor socket", AGGR_SOCKET), |
|---|
| 1139 | + OPT_SET_UINT(0, "per-die", &stat_config.aggr_mode, |
|---|
| 1140 | + "aggregate counts per processor die", AGGR_DIE), |
|---|
| 2026 | 1141 | OPT_SET_UINT(0, "per-core", &stat_config.aggr_mode, |
|---|
| 2027 | 1142 | "aggregate counts per physical processor core", AGGR_CORE), |
|---|
| 2028 | 1143 | OPT_SET_UINT(0, "per-thread", &stat_config.aggr_mode, |
|---|
| 2029 | 1144 | "aggregate counts per thread", AGGR_THREAD), |
|---|
| 2030 | | - OPT_UINTEGER('D', "delay", &initial_delay, |
|---|
| 2031 | | - "ms to wait before starting measurement after program start"), |
|---|
| 2032 | | - OPT_CALLBACK_NOOPT(0, "metric-only", &metric_only, NULL, |
|---|
| 1145 | + OPT_SET_UINT(0, "per-node", &stat_config.aggr_mode, |
|---|
| 1146 | + "aggregate counts per numa node", AGGR_NODE), |
|---|
| 1147 | + OPT_INTEGER('D', "delay", &stat_config.initial_delay, |
|---|
| 1148 | + "ms to wait before starting measurement after program start (-1: start with events disabled)"), |
|---|
| 1149 | + OPT_CALLBACK_NOOPT(0, "metric-only", &stat_config.metric_only, NULL, |
|---|
| 2033 | 1150 | "Only print computed metrics. No raw values", enable_metric_only), |
|---|
| 1151 | + OPT_BOOLEAN(0, "metric-no-group", &stat_config.metric_no_group, |
|---|
| 1152 | + "don't group metric events, impacts multiplexing"), |
|---|
| 1153 | + OPT_BOOLEAN(0, "metric-no-merge", &stat_config.metric_no_merge, |
|---|
| 1154 | + "don't try to share events between metrics in a group"), |
|---|
| 2034 | 1155 | OPT_BOOLEAN(0, "topdown", &topdown_run, |
|---|
| 2035 | 1156 | "measure topdown level 1 statistics"), |
|---|
| 2036 | 1157 | OPT_BOOLEAN(0, "smi-cost", &smi_cost, |
|---|
| .. | .. |
|---|
| 2038 | 1159 | OPT_CALLBACK('M', "metrics", &evsel_list, "metric/metric group list", |
|---|
| 2039 | 1160 | "monitor specified metrics or metric groups (separated by ,)", |
|---|
| 2040 | 1161 | parse_metric_groups), |
|---|
| 1162 | + OPT_BOOLEAN_FLAG(0, "all-kernel", &stat_config.all_kernel, |
|---|
| 1163 | + "Configure all used events to run in kernel space.", |
|---|
| 1164 | + PARSE_OPT_EXCLUSIVE), |
|---|
| 1165 | + OPT_BOOLEAN_FLAG(0, "all-user", &stat_config.all_user, |
|---|
| 1166 | + "Configure all used events to run in user space.", |
|---|
| 1167 | + PARSE_OPT_EXCLUSIVE), |
|---|
| 1168 | + OPT_BOOLEAN(0, "percore-show-thread", &stat_config.percore_show_thread, |
|---|
| 1169 | + "Use with 'percore' event qualifier to show the event " |
|---|
| 1170 | + "counts of one hardware thread by sum up total hardware " |
|---|
| 1171 | + "threads of same physical core"), |
|---|
| 1172 | + OPT_BOOLEAN(0, "summary", &stat_config.summary, |
|---|
| 1173 | + "print summary for interval mode"), |
|---|
| 1174 | +#ifdef HAVE_LIBPFM |
|---|
| 1175 | + OPT_CALLBACK(0, "pfm-events", &evsel_list, "event", |
|---|
| 1176 | + "libpfm4 event selector. use 'perf list' to list available events", |
|---|
| 1177 | + parse_libpfm_events_option), |
|---|
| 1178 | +#endif |
|---|
| 1179 | + OPT_CALLBACK(0, "control", &stat_config, "fd:ctl-fd[,ack-fd] or fifo:ctl-fifo[,ack-fifo]", |
|---|
| 1180 | + "Listen on ctl-fd descriptor for command to control measurement ('enable': enable events, 'disable': disable events).\n" |
|---|
| 1181 | + "\t\t\t Optionally send control command completion ('ack\\n') to ack-fd descriptor.\n" |
|---|
| 1182 | + "\t\t\t Alternatively, ctl-fifo / ack-fifo will be opened and used as ctl-fd / ack-fd.", |
|---|
| 1183 | + parse_control_option), |
|---|
| 2041 | 1184 | OPT_END() |
|---|
| 2042 | 1185 | }; |
|---|
| 2043 | 1186 | |
|---|
| 2044 | | -static int perf_stat__get_socket(struct cpu_map *map, int cpu) |
|---|
| 1187 | +static int perf_stat__get_socket(struct perf_stat_config *config __maybe_unused, |
|---|
| 1188 | + struct perf_cpu_map *map, int cpu) |
|---|
| 2045 | 1189 | { |
|---|
| 2046 | 1190 | return cpu_map__get_socket(map, cpu, NULL); |
|---|
| 2047 | 1191 | } |
|---|
| 2048 | 1192 | |
|---|
| 2049 | | -static int perf_stat__get_core(struct cpu_map *map, int cpu) |
|---|
| 1193 | +static int perf_stat__get_die(struct perf_stat_config *config __maybe_unused, |
|---|
| 1194 | + struct perf_cpu_map *map, int cpu) |
|---|
| 1195 | +{ |
|---|
| 1196 | + return cpu_map__get_die(map, cpu, NULL); |
|---|
| 1197 | +} |
|---|
| 1198 | + |
|---|
| 1199 | +static int perf_stat__get_core(struct perf_stat_config *config __maybe_unused, |
|---|
| 1200 | + struct perf_cpu_map *map, int cpu) |
|---|
| 2050 | 1201 | { |
|---|
| 2051 | 1202 | return cpu_map__get_core(map, cpu, NULL); |
|---|
| 2052 | 1203 | } |
|---|
| 2053 | 1204 | |
|---|
| 2054 | | -static int cpu_map__get_max(struct cpu_map *map) |
|---|
| 1205 | +static int perf_stat__get_node(struct perf_stat_config *config __maybe_unused, |
|---|
| 1206 | + struct perf_cpu_map *map, int cpu) |
|---|
| 2055 | 1207 | { |
|---|
| 2056 | | - int i, max = -1; |
|---|
| 2057 | | - |
|---|
| 2058 | | - for (i = 0; i < map->nr; i++) { |
|---|
| 2059 | | - if (map->map[i] > max) |
|---|
| 2060 | | - max = map->map[i]; |
|---|
| 2061 | | - } |
|---|
| 2062 | | - |
|---|
| 2063 | | - return max; |
|---|
| 1208 | + return cpu_map__get_node(map, cpu, NULL); |
|---|
| 2064 | 1209 | } |
|---|
| 2065 | 1210 | |
|---|
| 2066 | | -static struct cpu_map *cpus_aggr_map; |
|---|
| 2067 | | - |
|---|
| 2068 | | -static int perf_stat__get_aggr(aggr_get_id_t get_id, struct cpu_map *map, int idx) |
|---|
| 1211 | +static int perf_stat__get_aggr(struct perf_stat_config *config, |
|---|
| 1212 | + aggr_get_id_t get_id, struct perf_cpu_map *map, int idx) |
|---|
| 2069 | 1213 | { |
|---|
| 2070 | 1214 | int cpu; |
|---|
| 2071 | 1215 | |
|---|
| .. | .. |
|---|
| 2074 | 1218 | |
|---|
| 2075 | 1219 | cpu = map->map[idx]; |
|---|
| 2076 | 1220 | |
|---|
| 2077 | | - if (cpus_aggr_map->map[cpu] == -1) |
|---|
| 2078 | | - cpus_aggr_map->map[cpu] = get_id(map, idx); |
|---|
| 1221 | + if (config->cpus_aggr_map->map[cpu] == -1) |
|---|
| 1222 | + config->cpus_aggr_map->map[cpu] = get_id(config, map, idx); |
|---|
| 2079 | 1223 | |
|---|
| 2080 | | - return cpus_aggr_map->map[cpu]; |
|---|
| 1224 | + return config->cpus_aggr_map->map[cpu]; |
|---|
| 2081 | 1225 | } |
|---|
| 2082 | 1226 | |
|---|
| 2083 | | -static int perf_stat__get_socket_cached(struct cpu_map *map, int idx) |
|---|
| 1227 | +static int perf_stat__get_socket_cached(struct perf_stat_config *config, |
|---|
| 1228 | + struct perf_cpu_map *map, int idx) |
|---|
| 2084 | 1229 | { |
|---|
| 2085 | | - return perf_stat__get_aggr(perf_stat__get_socket, map, idx); |
|---|
| 1230 | + return perf_stat__get_aggr(config, perf_stat__get_socket, map, idx); |
|---|
| 2086 | 1231 | } |
|---|
| 2087 | 1232 | |
|---|
| 2088 | | -static int perf_stat__get_core_cached(struct cpu_map *map, int idx) |
|---|
| 1233 | +static int perf_stat__get_die_cached(struct perf_stat_config *config, |
|---|
| 1234 | + struct perf_cpu_map *map, int idx) |
|---|
| 2089 | 1235 | { |
|---|
| 2090 | | - return perf_stat__get_aggr(perf_stat__get_core, map, idx); |
|---|
| 1236 | + return perf_stat__get_aggr(config, perf_stat__get_die, map, idx); |
|---|
| 1237 | +} |
|---|
| 1238 | + |
|---|
| 1239 | +static int perf_stat__get_core_cached(struct perf_stat_config *config, |
|---|
| 1240 | + struct perf_cpu_map *map, int idx) |
|---|
| 1241 | +{ |
|---|
| 1242 | + return perf_stat__get_aggr(config, perf_stat__get_core, map, idx); |
|---|
| 1243 | +} |
|---|
| 1244 | + |
|---|
| 1245 | +static int perf_stat__get_node_cached(struct perf_stat_config *config, |
|---|
| 1246 | + struct perf_cpu_map *map, int idx) |
|---|
| 1247 | +{ |
|---|
| 1248 | + return perf_stat__get_aggr(config, perf_stat__get_node, map, idx); |
|---|
| 1249 | +} |
|---|
| 1250 | + |
|---|
| 1251 | +static bool term_percore_set(void) |
|---|
| 1252 | +{ |
|---|
| 1253 | + struct evsel *counter; |
|---|
| 1254 | + |
|---|
| 1255 | + evlist__for_each_entry(evsel_list, counter) { |
|---|
| 1256 | + if (counter->percore) |
|---|
| 1257 | + return true; |
|---|
| 1258 | + } |
|---|
| 1259 | + |
|---|
| 1260 | + return false; |
|---|
| 2091 | 1261 | } |
|---|
| 2092 | 1262 | |
|---|
| 2093 | 1263 | static int perf_stat_init_aggr_mode(void) |
|---|
| .. | .. |
|---|
| 2096 | 1266 | |
|---|
| 2097 | 1267 | switch (stat_config.aggr_mode) { |
|---|
| 2098 | 1268 | case AGGR_SOCKET: |
|---|
| 2099 | | - if (cpu_map__build_socket_map(evsel_list->cpus, &aggr_map)) { |
|---|
| 1269 | + if (cpu_map__build_socket_map(evsel_list->core.cpus, &stat_config.aggr_map)) { |
|---|
| 2100 | 1270 | perror("cannot build socket map"); |
|---|
| 2101 | 1271 | return -1; |
|---|
| 2102 | 1272 | } |
|---|
| 2103 | | - aggr_get_id = perf_stat__get_socket_cached; |
|---|
| 1273 | + stat_config.aggr_get_id = perf_stat__get_socket_cached; |
|---|
| 1274 | + break; |
|---|
| 1275 | + case AGGR_DIE: |
|---|
| 1276 | + if (cpu_map__build_die_map(evsel_list->core.cpus, &stat_config.aggr_map)) { |
|---|
| 1277 | + perror("cannot build die map"); |
|---|
| 1278 | + return -1; |
|---|
| 1279 | + } |
|---|
| 1280 | + stat_config.aggr_get_id = perf_stat__get_die_cached; |
|---|
| 2104 | 1281 | break; |
|---|
| 2105 | 1282 | case AGGR_CORE: |
|---|
| 2106 | | - if (cpu_map__build_core_map(evsel_list->cpus, &aggr_map)) { |
|---|
| 1283 | + if (cpu_map__build_core_map(evsel_list->core.cpus, &stat_config.aggr_map)) { |
|---|
| 2107 | 1284 | perror("cannot build core map"); |
|---|
| 2108 | 1285 | return -1; |
|---|
| 2109 | 1286 | } |
|---|
| 2110 | | - aggr_get_id = perf_stat__get_core_cached; |
|---|
| 1287 | + stat_config.aggr_get_id = perf_stat__get_core_cached; |
|---|
| 1288 | + break; |
|---|
| 1289 | + case AGGR_NODE: |
|---|
| 1290 | + if (cpu_map__build_node_map(evsel_list->core.cpus, &stat_config.aggr_map)) { |
|---|
| 1291 | + perror("cannot build core map"); |
|---|
| 1292 | + return -1; |
|---|
| 1293 | + } |
|---|
| 1294 | + stat_config.aggr_get_id = perf_stat__get_node_cached; |
|---|
| 2111 | 1295 | break; |
|---|
| 2112 | 1296 | case AGGR_NONE: |
|---|
| 1297 | + if (term_percore_set()) { |
|---|
| 1298 | + if (cpu_map__build_core_map(evsel_list->core.cpus, |
|---|
| 1299 | + &stat_config.aggr_map)) { |
|---|
| 1300 | + perror("cannot build core map"); |
|---|
| 1301 | + return -1; |
|---|
| 1302 | + } |
|---|
| 1303 | + stat_config.aggr_get_id = perf_stat__get_core_cached; |
|---|
| 1304 | + } |
|---|
| 1305 | + break; |
|---|
| 2113 | 1306 | case AGGR_GLOBAL: |
|---|
| 2114 | 1307 | case AGGR_THREAD: |
|---|
| 2115 | 1308 | case AGGR_UNSET: |
|---|
| .. | .. |
|---|
| 2122 | 1315 | * taking the highest cpu number to be the size of |
|---|
| 2123 | 1316 | * the aggregation translate cpumap. |
|---|
| 2124 | 1317 | */ |
|---|
| 2125 | | - nr = cpu_map__get_max(evsel_list->cpus); |
|---|
| 2126 | | - cpus_aggr_map = cpu_map__empty_new(nr + 1); |
|---|
| 2127 | | - return cpus_aggr_map ? 0 : -ENOMEM; |
|---|
| 1318 | + nr = perf_cpu_map__max(evsel_list->core.cpus); |
|---|
| 1319 | + stat_config.cpus_aggr_map = perf_cpu_map__empty_new(nr + 1); |
|---|
| 1320 | + return stat_config.cpus_aggr_map ? 0 : -ENOMEM; |
|---|
| 2128 | 1321 | } |
|---|
| 2129 | 1322 | |
|---|
| 2130 | 1323 | static void perf_stat__exit_aggr_mode(void) |
|---|
| 2131 | 1324 | { |
|---|
| 2132 | | - cpu_map__put(aggr_map); |
|---|
| 2133 | | - cpu_map__put(cpus_aggr_map); |
|---|
| 2134 | | - aggr_map = NULL; |
|---|
| 2135 | | - cpus_aggr_map = NULL; |
|---|
| 1325 | + perf_cpu_map__put(stat_config.aggr_map); |
|---|
| 1326 | + perf_cpu_map__put(stat_config.cpus_aggr_map); |
|---|
| 1327 | + stat_config.aggr_map = NULL; |
|---|
| 1328 | + stat_config.cpus_aggr_map = NULL; |
|---|
| 2136 | 1329 | } |
|---|
| 2137 | 1330 | |
|---|
| 2138 | | -static inline int perf_env__get_cpu(struct perf_env *env, struct cpu_map *map, int idx) |
|---|
| 1331 | +static inline int perf_env__get_cpu(struct perf_env *env, struct perf_cpu_map *map, int idx) |
|---|
| 2139 | 1332 | { |
|---|
| 2140 | 1333 | int cpu; |
|---|
| 2141 | 1334 | |
|---|
| .. | .. |
|---|
| 2150 | 1343 | return cpu; |
|---|
| 2151 | 1344 | } |
|---|
| 2152 | 1345 | |
|---|
| 2153 | | -static int perf_env__get_socket(struct cpu_map *map, int idx, void *data) |
|---|
| 1346 | +static int perf_env__get_socket(struct perf_cpu_map *map, int idx, void *data) |
|---|
| 2154 | 1347 | { |
|---|
| 2155 | 1348 | struct perf_env *env = data; |
|---|
| 2156 | 1349 | int cpu = perf_env__get_cpu(env, map, idx); |
|---|
| .. | .. |
|---|
| 2158 | 1351 | return cpu == -1 ? -1 : env->cpu[cpu].socket_id; |
|---|
| 2159 | 1352 | } |
|---|
| 2160 | 1353 | |
|---|
| 2161 | | -static int perf_env__get_core(struct cpu_map *map, int idx, void *data) |
|---|
| 1354 | +static int perf_env__get_die(struct perf_cpu_map *map, int idx, void *data) |
|---|
| 1355 | +{ |
|---|
| 1356 | + struct perf_env *env = data; |
|---|
| 1357 | + int die_id = -1, cpu = perf_env__get_cpu(env, map, idx); |
|---|
| 1358 | + |
|---|
| 1359 | + if (cpu != -1) { |
|---|
| 1360 | + /* |
|---|
| 1361 | + * Encode socket in bit range 15:8 |
|---|
| 1362 | + * die_id is relative to socket, |
|---|
| 1363 | + * we need a global id. So we combine |
|---|
| 1364 | + * socket + die id |
|---|
| 1365 | + */ |
|---|
| 1366 | + if (WARN_ONCE(env->cpu[cpu].socket_id >> 8, "The socket id number is too big.\n")) |
|---|
| 1367 | + return -1; |
|---|
| 1368 | + |
|---|
| 1369 | + if (WARN_ONCE(env->cpu[cpu].die_id >> 8, "The die id number is too big.\n")) |
|---|
| 1370 | + return -1; |
|---|
| 1371 | + |
|---|
| 1372 | + die_id = (env->cpu[cpu].socket_id << 8) | (env->cpu[cpu].die_id & 0xff); |
|---|
| 1373 | + } |
|---|
| 1374 | + |
|---|
| 1375 | + return die_id; |
|---|
| 1376 | +} |
|---|
| 1377 | + |
|---|
| 1378 | +static int perf_env__get_core(struct perf_cpu_map *map, int idx, void *data) |
|---|
| 2162 | 1379 | { |
|---|
| 2163 | 1380 | struct perf_env *env = data; |
|---|
| 2164 | 1381 | int core = -1, cpu = perf_env__get_cpu(env, map, idx); |
|---|
| 2165 | 1382 | |
|---|
| 2166 | 1383 | if (cpu != -1) { |
|---|
| 2167 | | - int socket_id = env->cpu[cpu].socket_id; |
|---|
| 2168 | | - |
|---|
| 2169 | 1384 | /* |
|---|
| 2170 | | - * Encode socket in upper 16 bits |
|---|
| 2171 | | - * core_id is relative to socket, and |
|---|
| 1385 | + * Encode socket in bit range 31:24 |
|---|
| 1386 | + * encode die id in bit range 23:16 |
|---|
| 1387 | + * core_id is relative to socket and die, |
|---|
| 2172 | 1388 | * we need a global id. So we combine |
|---|
| 2173 | | - * socket + core id. |
|---|
| 1389 | + * socket + die id + core id |
|---|
| 2174 | 1390 | */ |
|---|
| 2175 | | - core = (socket_id << 16) | (env->cpu[cpu].core_id & 0xffff); |
|---|
| 1391 | + if (WARN_ONCE(env->cpu[cpu].socket_id >> 8, "The socket id number is too big.\n")) |
|---|
| 1392 | + return -1; |
|---|
| 1393 | + |
|---|
| 1394 | + if (WARN_ONCE(env->cpu[cpu].die_id >> 8, "The die id number is too big.\n")) |
|---|
| 1395 | + return -1; |
|---|
| 1396 | + |
|---|
| 1397 | + if (WARN_ONCE(env->cpu[cpu].core_id >> 16, "The core id number is too big.\n")) |
|---|
| 1398 | + return -1; |
|---|
| 1399 | + |
|---|
| 1400 | + core = (env->cpu[cpu].socket_id << 24) | |
|---|
| 1401 | + (env->cpu[cpu].die_id << 16) | |
|---|
| 1402 | + (env->cpu[cpu].core_id & 0xffff); |
|---|
| 2176 | 1403 | } |
|---|
| 2177 | 1404 | |
|---|
| 2178 | 1405 | return core; |
|---|
| 2179 | 1406 | } |
|---|
| 2180 | 1407 | |
|---|
| 2181 | | -static int perf_env__build_socket_map(struct perf_env *env, struct cpu_map *cpus, |
|---|
| 2182 | | - struct cpu_map **sockp) |
|---|
| 1408 | +static int perf_env__get_node(struct perf_cpu_map *map, int idx, void *data) |
|---|
| 1409 | +{ |
|---|
| 1410 | + int cpu = perf_env__get_cpu(data, map, idx); |
|---|
| 1411 | + |
|---|
| 1412 | + return perf_env__numa_node(data, cpu); |
|---|
| 1413 | +} |
|---|
| 1414 | + |
|---|
| 1415 | +static int perf_env__build_socket_map(struct perf_env *env, struct perf_cpu_map *cpus, |
|---|
| 1416 | + struct perf_cpu_map **sockp) |
|---|
| 2183 | 1417 | { |
|---|
| 2184 | 1418 | return cpu_map__build_map(cpus, sockp, perf_env__get_socket, env); |
|---|
| 2185 | 1419 | } |
|---|
| 2186 | 1420 | |
|---|
| 2187 | | -static int perf_env__build_core_map(struct perf_env *env, struct cpu_map *cpus, |
|---|
| 2188 | | - struct cpu_map **corep) |
|---|
| 1421 | +static int perf_env__build_die_map(struct perf_env *env, struct perf_cpu_map *cpus, |
|---|
| 1422 | + struct perf_cpu_map **diep) |
|---|
| 1423 | +{ |
|---|
| 1424 | + return cpu_map__build_map(cpus, diep, perf_env__get_die, env); |
|---|
| 1425 | +} |
|---|
| 1426 | + |
|---|
| 1427 | +static int perf_env__build_core_map(struct perf_env *env, struct perf_cpu_map *cpus, |
|---|
| 1428 | + struct perf_cpu_map **corep) |
|---|
| 2189 | 1429 | { |
|---|
| 2190 | 1430 | return cpu_map__build_map(cpus, corep, perf_env__get_core, env); |
|---|
| 2191 | 1431 | } |
|---|
| 2192 | 1432 | |
|---|
| 2193 | | -static int perf_stat__get_socket_file(struct cpu_map *map, int idx) |
|---|
| 1433 | +static int perf_env__build_node_map(struct perf_env *env, struct perf_cpu_map *cpus, |
|---|
| 1434 | + struct perf_cpu_map **nodep) |
|---|
| 1435 | +{ |
|---|
| 1436 | + return cpu_map__build_map(cpus, nodep, perf_env__get_node, env); |
|---|
| 1437 | +} |
|---|
| 1438 | + |
|---|
| 1439 | +static int perf_stat__get_socket_file(struct perf_stat_config *config __maybe_unused, |
|---|
| 1440 | + struct perf_cpu_map *map, int idx) |
|---|
| 2194 | 1441 | { |
|---|
| 2195 | 1442 | return perf_env__get_socket(map, idx, &perf_stat.session->header.env); |
|---|
| 2196 | 1443 | } |
|---|
| 1444 | +static int perf_stat__get_die_file(struct perf_stat_config *config __maybe_unused, |
|---|
| 1445 | + struct perf_cpu_map *map, int idx) |
|---|
| 1446 | +{ |
|---|
| 1447 | + return perf_env__get_die(map, idx, &perf_stat.session->header.env); |
|---|
| 1448 | +} |
|---|
| 2197 | 1449 | |
|---|
| 2198 | | -static int perf_stat__get_core_file(struct cpu_map *map, int idx) |
|---|
| 1450 | +static int perf_stat__get_core_file(struct perf_stat_config *config __maybe_unused, |
|---|
| 1451 | + struct perf_cpu_map *map, int idx) |
|---|
| 2199 | 1452 | { |
|---|
| 2200 | 1453 | return perf_env__get_core(map, idx, &perf_stat.session->header.env); |
|---|
| 1454 | +} |
|---|
| 1455 | + |
|---|
| 1456 | +static int perf_stat__get_node_file(struct perf_stat_config *config __maybe_unused, |
|---|
| 1457 | + struct perf_cpu_map *map, int idx) |
|---|
| 1458 | +{ |
|---|
| 1459 | + return perf_env__get_node(map, idx, &perf_stat.session->header.env); |
|---|
| 2201 | 1460 | } |
|---|
| 2202 | 1461 | |
|---|
| 2203 | 1462 | static int perf_stat_init_aggr_mode_file(struct perf_stat *st) |
|---|
| .. | .. |
|---|
| 2206 | 1465 | |
|---|
| 2207 | 1466 | switch (stat_config.aggr_mode) { |
|---|
| 2208 | 1467 | case AGGR_SOCKET: |
|---|
| 2209 | | - if (perf_env__build_socket_map(env, evsel_list->cpus, &aggr_map)) { |
|---|
| 1468 | + if (perf_env__build_socket_map(env, evsel_list->core.cpus, &stat_config.aggr_map)) { |
|---|
| 2210 | 1469 | perror("cannot build socket map"); |
|---|
| 2211 | 1470 | return -1; |
|---|
| 2212 | 1471 | } |
|---|
| 2213 | | - aggr_get_id = perf_stat__get_socket_file; |
|---|
| 1472 | + stat_config.aggr_get_id = perf_stat__get_socket_file; |
|---|
| 1473 | + break; |
|---|
| 1474 | + case AGGR_DIE: |
|---|
| 1475 | + if (perf_env__build_die_map(env, evsel_list->core.cpus, &stat_config.aggr_map)) { |
|---|
| 1476 | + perror("cannot build die map"); |
|---|
| 1477 | + return -1; |
|---|
| 1478 | + } |
|---|
| 1479 | + stat_config.aggr_get_id = perf_stat__get_die_file; |
|---|
| 2214 | 1480 | break; |
|---|
| 2215 | 1481 | case AGGR_CORE: |
|---|
| 2216 | | - if (perf_env__build_core_map(env, evsel_list->cpus, &aggr_map)) { |
|---|
| 1482 | + if (perf_env__build_core_map(env, evsel_list->core.cpus, &stat_config.aggr_map)) { |
|---|
| 2217 | 1483 | perror("cannot build core map"); |
|---|
| 2218 | 1484 | return -1; |
|---|
| 2219 | 1485 | } |
|---|
| 2220 | | - aggr_get_id = perf_stat__get_core_file; |
|---|
| 1486 | + stat_config.aggr_get_id = perf_stat__get_core_file; |
|---|
| 1487 | + break; |
|---|
| 1488 | + case AGGR_NODE: |
|---|
| 1489 | + if (perf_env__build_node_map(env, evsel_list->core.cpus, &stat_config.aggr_map)) { |
|---|
| 1490 | + perror("cannot build core map"); |
|---|
| 1491 | + return -1; |
|---|
| 1492 | + } |
|---|
| 1493 | + stat_config.aggr_get_id = perf_stat__get_node_file; |
|---|
| 2221 | 1494 | break; |
|---|
| 2222 | 1495 | case AGGR_NONE: |
|---|
| 2223 | 1496 | case AGGR_GLOBAL: |
|---|
| .. | .. |
|---|
| 2228 | 1501 | } |
|---|
| 2229 | 1502 | |
|---|
| 2230 | 1503 | return 0; |
|---|
| 2231 | | -} |
|---|
| 2232 | | - |
|---|
| 2233 | | -static int topdown_filter_events(const char **attr, char **str, bool use_group) |
|---|
| 2234 | | -{ |
|---|
| 2235 | | - int off = 0; |
|---|
| 2236 | | - int i; |
|---|
| 2237 | | - int len = 0; |
|---|
| 2238 | | - char *s; |
|---|
| 2239 | | - |
|---|
| 2240 | | - for (i = 0; attr[i]; i++) { |
|---|
| 2241 | | - if (pmu_have_event("cpu", attr[i])) { |
|---|
| 2242 | | - len += strlen(attr[i]) + 1; |
|---|
| 2243 | | - attr[i - off] = attr[i]; |
|---|
| 2244 | | - } else |
|---|
| 2245 | | - off++; |
|---|
| 2246 | | - } |
|---|
| 2247 | | - attr[i - off] = NULL; |
|---|
| 2248 | | - |
|---|
| 2249 | | - *str = malloc(len + 1 + 2); |
|---|
| 2250 | | - if (!*str) |
|---|
| 2251 | | - return -1; |
|---|
| 2252 | | - s = *str; |
|---|
| 2253 | | - if (i - off == 0) { |
|---|
| 2254 | | - *s = 0; |
|---|
| 2255 | | - return 0; |
|---|
| 2256 | | - } |
|---|
| 2257 | | - if (use_group) |
|---|
| 2258 | | - *s++ = '{'; |
|---|
| 2259 | | - for (i = 0; attr[i]; i++) { |
|---|
| 2260 | | - strcpy(s, attr[i]); |
|---|
| 2261 | | - s += strlen(s); |
|---|
| 2262 | | - *s++ = ','; |
|---|
| 2263 | | - } |
|---|
| 2264 | | - if (use_group) { |
|---|
| 2265 | | - s[-1] = '}'; |
|---|
| 2266 | | - *s = 0; |
|---|
| 2267 | | - } else |
|---|
| 2268 | | - s[-1] = 0; |
|---|
| 2269 | | - return 0; |
|---|
| 2270 | | -} |
|---|
| 2271 | | - |
|---|
| 2272 | | -__weak bool arch_topdown_check_group(bool *warn) |
|---|
| 2273 | | -{ |
|---|
| 2274 | | - *warn = false; |
|---|
| 2275 | | - return false; |
|---|
| 2276 | | -} |
|---|
| 2277 | | - |
|---|
| 2278 | | -__weak void arch_topdown_group_warn(void) |
|---|
| 2279 | | -{ |
|---|
| 2280 | 1504 | } |
|---|
| 2281 | 1505 | |
|---|
| 2282 | 1506 | /* |
|---|
| .. | .. |
|---|
| 2401 | 1625 | struct parse_events_error errinfo; |
|---|
| 2402 | 1626 | |
|---|
| 2403 | 1627 | /* Set attrs if no event is selected and !null_run: */ |
|---|
| 2404 | | - if (null_run) |
|---|
| 1628 | + if (stat_config.null_run) |
|---|
| 2405 | 1629 | return 0; |
|---|
| 2406 | 1630 | |
|---|
| 1631 | + bzero(&errinfo, sizeof(errinfo)); |
|---|
| 2407 | 1632 | if (transaction_run) { |
|---|
| 2408 | 1633 | /* Handle -T as -M transaction. Once platform specific metrics |
|---|
| 2409 | 1634 | * support has been added to the json files, all archictures |
|---|
| .. | .. |
|---|
| 2414 | 1639 | struct option opt = { .value = &evsel_list }; |
|---|
| 2415 | 1640 | |
|---|
| 2416 | 1641 | return metricgroup__parse_groups(&opt, "transaction", |
|---|
| 2417 | | - &metric_events); |
|---|
| 1642 | + stat_config.metric_no_group, |
|---|
| 1643 | + stat_config.metric_no_merge, |
|---|
| 1644 | + &stat_config.metric_events); |
|---|
| 2418 | 1645 | } |
|---|
| 2419 | 1646 | |
|---|
| 2420 | 1647 | if (pmu_have_event("cpu", "cycles-ct") && |
|---|
| .. | .. |
|---|
| 2452 | 1679 | if (pmu_have_event("msr", "aperf") && |
|---|
| 2453 | 1680 | pmu_have_event("msr", "smi")) { |
|---|
| 2454 | 1681 | if (!force_metric_only) |
|---|
| 2455 | | - metric_only = true; |
|---|
| 1682 | + stat_config.metric_only = true; |
|---|
| 2456 | 1683 | err = parse_events(evsel_list, smi_cost_attrs, &errinfo); |
|---|
| 2457 | 1684 | } else { |
|---|
| 2458 | 1685 | fprintf(stderr, "To measure SMI cost, it needs " |
|---|
| .. | .. |
|---|
| 2461 | 1688 | return -1; |
|---|
| 2462 | 1689 | } |
|---|
| 2463 | 1690 | if (err) { |
|---|
| 1691 | + parse_events_print_error(&errinfo, smi_cost_attrs); |
|---|
| 2464 | 1692 | fprintf(stderr, "Cannot set up SMI cost events\n"); |
|---|
| 2465 | 1693 | return -1; |
|---|
| 2466 | 1694 | } |
|---|
| .. | .. |
|---|
| 2470 | 1698 | if (topdown_run) { |
|---|
| 2471 | 1699 | char *str = NULL; |
|---|
| 2472 | 1700 | bool warn = false; |
|---|
| 1701 | + |
|---|
| 1702 | + if (!force_metric_only) |
|---|
| 1703 | + stat_config.metric_only = true; |
|---|
| 1704 | + |
|---|
| 1705 | + if (topdown_filter_events(topdown_metric_attrs, &str, 1) < 0) { |
|---|
| 1706 | + pr_err("Out of memory\n"); |
|---|
| 1707 | + return -1; |
|---|
| 1708 | + } |
|---|
| 1709 | + if (topdown_metric_attrs[0] && str) { |
|---|
| 1710 | + if (!stat_config.interval && !stat_config.metric_only) { |
|---|
| 1711 | + fprintf(stat_config.output, |
|---|
| 1712 | + "Topdown accuracy may decrease when measuring long periods.\n" |
|---|
| 1713 | + "Please print the result regularly, e.g. -I1000\n"); |
|---|
| 1714 | + } |
|---|
| 1715 | + goto setup_metrics; |
|---|
| 1716 | + } |
|---|
| 1717 | + |
|---|
| 1718 | + zfree(&str); |
|---|
| 2473 | 1719 | |
|---|
| 2474 | 1720 | if (stat_config.aggr_mode != AGGR_GLOBAL && |
|---|
| 2475 | 1721 | stat_config.aggr_mode != AGGR_CORE) { |
|---|
| .. | .. |
|---|
| 2482 | 1728 | return -1; |
|---|
| 2483 | 1729 | } |
|---|
| 2484 | 1730 | |
|---|
| 2485 | | - if (!force_metric_only) |
|---|
| 2486 | | - metric_only = true; |
|---|
| 2487 | 1731 | if (topdown_filter_events(topdown_attrs, &str, |
|---|
| 2488 | 1732 | arch_topdown_check_group(&warn)) < 0) { |
|---|
| 2489 | 1733 | pr_err("Out of memory\n"); |
|---|
| .. | .. |
|---|
| 2492 | 1736 | if (topdown_attrs[0] && str) { |
|---|
| 2493 | 1737 | if (warn) |
|---|
| 2494 | 1738 | arch_topdown_group_warn(); |
|---|
| 1739 | +setup_metrics: |
|---|
| 2495 | 1740 | err = parse_events(evsel_list, str, &errinfo); |
|---|
| 2496 | 1741 | if (err) { |
|---|
| 2497 | 1742 | fprintf(stderr, |
|---|
| .. | .. |
|---|
| 2508 | 1753 | free(str); |
|---|
| 2509 | 1754 | } |
|---|
| 2510 | 1755 | |
|---|
| 2511 | | - if (!evsel_list->nr_entries) { |
|---|
| 1756 | + if (!evsel_list->core.nr_entries) { |
|---|
| 2512 | 1757 | if (target__has_cpu(&target)) |
|---|
| 2513 | 1758 | default_attrs0[0].config = PERF_COUNT_SW_CPU_CLOCK; |
|---|
| 2514 | 1759 | |
|---|
| 2515 | | - if (perf_evlist__add_default_attrs(evsel_list, default_attrs0) < 0) |
|---|
| 1760 | + if (evlist__add_default_attrs(evsel_list, default_attrs0) < 0) |
|---|
| 2516 | 1761 | return -1; |
|---|
| 2517 | 1762 | if (pmu_have_event("cpu", "stalled-cycles-frontend")) { |
|---|
| 2518 | | - if (perf_evlist__add_default_attrs(evsel_list, |
|---|
| 2519 | | - frontend_attrs) < 0) |
|---|
| 1763 | + if (evlist__add_default_attrs(evsel_list, frontend_attrs) < 0) |
|---|
| 2520 | 1764 | return -1; |
|---|
| 2521 | 1765 | } |
|---|
| 2522 | 1766 | if (pmu_have_event("cpu", "stalled-cycles-backend")) { |
|---|
| 2523 | | - if (perf_evlist__add_default_attrs(evsel_list, |
|---|
| 2524 | | - backend_attrs) < 0) |
|---|
| 1767 | + if (evlist__add_default_attrs(evsel_list, backend_attrs) < 0) |
|---|
| 2525 | 1768 | return -1; |
|---|
| 2526 | 1769 | } |
|---|
| 2527 | | - if (perf_evlist__add_default_attrs(evsel_list, default_attrs1) < 0) |
|---|
| 1770 | + if (evlist__add_default_attrs(evsel_list, default_attrs1) < 0) |
|---|
| 2528 | 1771 | return -1; |
|---|
| 2529 | 1772 | } |
|---|
| 2530 | 1773 | |
|---|
| .. | .. |
|---|
| 2534 | 1777 | return 0; |
|---|
| 2535 | 1778 | |
|---|
| 2536 | 1779 | /* Append detailed run extra attributes: */ |
|---|
| 2537 | | - if (perf_evlist__add_default_attrs(evsel_list, detailed_attrs) < 0) |
|---|
| 1780 | + if (evlist__add_default_attrs(evsel_list, detailed_attrs) < 0) |
|---|
| 2538 | 1781 | return -1; |
|---|
| 2539 | 1782 | |
|---|
| 2540 | 1783 | if (detailed_run < 2) |
|---|
| 2541 | 1784 | return 0; |
|---|
| 2542 | 1785 | |
|---|
| 2543 | 1786 | /* Append very detailed run extra attributes: */ |
|---|
| 2544 | | - if (perf_evlist__add_default_attrs(evsel_list, very_detailed_attrs) < 0) |
|---|
| 1787 | + if (evlist__add_default_attrs(evsel_list, very_detailed_attrs) < 0) |
|---|
| 2545 | 1788 | return -1; |
|---|
| 2546 | 1789 | |
|---|
| 2547 | 1790 | if (detailed_run < 3) |
|---|
| 2548 | 1791 | return 0; |
|---|
| 2549 | 1792 | |
|---|
| 2550 | 1793 | /* Append very, very detailed run extra attributes: */ |
|---|
| 2551 | | - return perf_evlist__add_default_attrs(evsel_list, very_very_detailed_attrs); |
|---|
| 1794 | + return evlist__add_default_attrs(evsel_list, very_very_detailed_attrs); |
|---|
| 2552 | 1795 | } |
|---|
| 2553 | 1796 | |
|---|
| 2554 | 1797 | static const char * const stat_record_usage[] = { |
|---|
| .. | .. |
|---|
| 2563 | 1806 | for (feat = HEADER_FIRST_FEATURE; feat < HEADER_LAST_FEATURE; feat++) |
|---|
| 2564 | 1807 | perf_header__set_feat(&session->header, feat); |
|---|
| 2565 | 1808 | |
|---|
| 1809 | + perf_header__clear_feat(&session->header, HEADER_DIR_FORMAT); |
|---|
| 2566 | 1810 | perf_header__clear_feat(&session->header, HEADER_BUILD_ID); |
|---|
| 2567 | 1811 | perf_header__clear_feat(&session->header, HEADER_TRACING_DATA); |
|---|
| 2568 | 1812 | perf_header__clear_feat(&session->header, HEADER_BRANCH_STACK); |
|---|
| .. | .. |
|---|
| 2578 | 1822 | PARSE_OPT_STOP_AT_NON_OPTION); |
|---|
| 2579 | 1823 | |
|---|
| 2580 | 1824 | if (output_name) |
|---|
| 2581 | | - data->file.path = output_name; |
|---|
| 1825 | + data->path = output_name; |
|---|
| 2582 | 1826 | |
|---|
| 2583 | | - if (run_count != 1 || forever) { |
|---|
| 1827 | + if (stat_config.run_count != 1 || forever) { |
|---|
| 2584 | 1828 | pr_err("Cannot use -r option with perf stat record.\n"); |
|---|
| 2585 | 1829 | return -1; |
|---|
| 2586 | 1830 | } |
|---|
| 2587 | 1831 | |
|---|
| 2588 | 1832 | session = perf_session__new(data, false, NULL); |
|---|
| 2589 | | - if (session == NULL) { |
|---|
| 2590 | | - pr_err("Perf session creation failed.\n"); |
|---|
| 2591 | | - return -1; |
|---|
| 1833 | + if (IS_ERR(session)) { |
|---|
| 1834 | + pr_err("Perf session creation failed\n"); |
|---|
| 1835 | + return PTR_ERR(session); |
|---|
| 2592 | 1836 | } |
|---|
| 2593 | 1837 | |
|---|
| 2594 | 1838 | init_features(session); |
|---|
| .. | .. |
|---|
| 2599 | 1843 | return argc; |
|---|
| 2600 | 1844 | } |
|---|
| 2601 | 1845 | |
|---|
| 2602 | | -static int process_stat_round_event(struct perf_tool *tool __maybe_unused, |
|---|
| 2603 | | - union perf_event *event, |
|---|
| 2604 | | - struct perf_session *session) |
|---|
| 1846 | +static int process_stat_round_event(struct perf_session *session, |
|---|
| 1847 | + union perf_event *event) |
|---|
| 2605 | 1848 | { |
|---|
| 2606 | | - struct stat_round_event *stat_round = &event->stat_round; |
|---|
| 2607 | | - struct perf_evsel *counter; |
|---|
| 1849 | + struct perf_record_stat_round *stat_round = &event->stat_round; |
|---|
| 1850 | + struct evsel *counter; |
|---|
| 2608 | 1851 | struct timespec tsh, *ts = NULL; |
|---|
| 2609 | 1852 | const char **argv = session->header.env.cmdline_argv; |
|---|
| 2610 | 1853 | int argc = session->header.env.nr_cmdline; |
|---|
| .. | .. |
|---|
| 2626 | 1869 | } |
|---|
| 2627 | 1870 | |
|---|
| 2628 | 1871 | static |
|---|
| 2629 | | -int process_stat_config_event(struct perf_tool *tool, |
|---|
| 2630 | | - union perf_event *event, |
|---|
| 2631 | | - struct perf_session *session __maybe_unused) |
|---|
| 1872 | +int process_stat_config_event(struct perf_session *session, |
|---|
| 1873 | + union perf_event *event) |
|---|
| 2632 | 1874 | { |
|---|
| 1875 | + struct perf_tool *tool = session->tool; |
|---|
| 2633 | 1876 | struct perf_stat *st = container_of(tool, struct perf_stat, tool); |
|---|
| 2634 | 1877 | |
|---|
| 2635 | 1878 | perf_event__read_stat_config(&stat_config, &event->stat_config); |
|---|
| 2636 | 1879 | |
|---|
| 2637 | | - if (cpu_map__empty(st->cpus)) { |
|---|
| 1880 | + if (perf_cpu_map__empty(st->cpus)) { |
|---|
| 2638 | 1881 | if (st->aggr_mode != AGGR_UNSET) |
|---|
| 2639 | 1882 | pr_warning("warning: processing task data, aggregation mode not set\n"); |
|---|
| 2640 | 1883 | return 0; |
|---|
| .. | .. |
|---|
| 2659 | 1902 | if (WARN_ONCE(st->maps_allocated, "stats double allocation\n")) |
|---|
| 2660 | 1903 | return -EINVAL; |
|---|
| 2661 | 1904 | |
|---|
| 2662 | | - perf_evlist__set_maps(evsel_list, st->cpus, st->threads); |
|---|
| 1905 | + perf_evlist__set_maps(&evsel_list->core, st->cpus, st->threads); |
|---|
| 2663 | 1906 | |
|---|
| 2664 | 1907 | if (perf_evlist__alloc_stats(evsel_list, true)) |
|---|
| 2665 | 1908 | return -ENOMEM; |
|---|
| .. | .. |
|---|
| 2669 | 1912 | } |
|---|
| 2670 | 1913 | |
|---|
| 2671 | 1914 | static |
|---|
| 2672 | | -int process_thread_map_event(struct perf_tool *tool, |
|---|
| 2673 | | - union perf_event *event, |
|---|
| 2674 | | - struct perf_session *session __maybe_unused) |
|---|
| 1915 | +int process_thread_map_event(struct perf_session *session, |
|---|
| 1916 | + union perf_event *event) |
|---|
| 2675 | 1917 | { |
|---|
| 1918 | + struct perf_tool *tool = session->tool; |
|---|
| 2676 | 1919 | struct perf_stat *st = container_of(tool, struct perf_stat, tool); |
|---|
| 2677 | 1920 | |
|---|
| 2678 | 1921 | if (st->threads) { |
|---|
| .. | .. |
|---|
| 2688 | 1931 | } |
|---|
| 2689 | 1932 | |
|---|
| 2690 | 1933 | static |
|---|
| 2691 | | -int process_cpu_map_event(struct perf_tool *tool, |
|---|
| 2692 | | - union perf_event *event, |
|---|
| 2693 | | - struct perf_session *session __maybe_unused) |
|---|
| 1934 | +int process_cpu_map_event(struct perf_session *session, |
|---|
| 1935 | + union perf_event *event) |
|---|
| 2694 | 1936 | { |
|---|
| 1937 | + struct perf_tool *tool = session->tool; |
|---|
| 2695 | 1938 | struct perf_stat *st = container_of(tool, struct perf_stat, tool); |
|---|
| 2696 | | - struct cpu_map *cpus; |
|---|
| 1939 | + struct perf_cpu_map *cpus; |
|---|
| 2697 | 1940 | |
|---|
| 2698 | 1941 | if (st->cpus) { |
|---|
| 2699 | 1942 | pr_warning("Extra cpu map event, ignoring.\n"); |
|---|
| .. | .. |
|---|
| 2706 | 1949 | |
|---|
| 2707 | 1950 | st->cpus = cpus; |
|---|
| 2708 | 1951 | return set_maps(st); |
|---|
| 2709 | | -} |
|---|
| 2710 | | - |
|---|
| 2711 | | -static int runtime_stat_new(struct perf_stat_config *config, int nthreads) |
|---|
| 2712 | | -{ |
|---|
| 2713 | | - int i; |
|---|
| 2714 | | - |
|---|
| 2715 | | - config->stats = calloc(nthreads, sizeof(struct runtime_stat)); |
|---|
| 2716 | | - if (!config->stats) |
|---|
| 2717 | | - return -1; |
|---|
| 2718 | | - |
|---|
| 2719 | | - config->stats_num = nthreads; |
|---|
| 2720 | | - |
|---|
| 2721 | | - for (i = 0; i < nthreads; i++) |
|---|
| 2722 | | - runtime_stat__init(&config->stats[i]); |
|---|
| 2723 | | - |
|---|
| 2724 | | - return 0; |
|---|
| 2725 | | -} |
|---|
| 2726 | | - |
|---|
| 2727 | | -static void runtime_stat_delete(struct perf_stat_config *config) |
|---|
| 2728 | | -{ |
|---|
| 2729 | | - int i; |
|---|
| 2730 | | - |
|---|
| 2731 | | - if (!config->stats) |
|---|
| 2732 | | - return; |
|---|
| 2733 | | - |
|---|
| 2734 | | - for (i = 0; i < config->stats_num; i++) |
|---|
| 2735 | | - runtime_stat__exit(&config->stats[i]); |
|---|
| 2736 | | - |
|---|
| 2737 | | - free(config->stats); |
|---|
| 2738 | 1952 | } |
|---|
| 2739 | 1953 | |
|---|
| 2740 | 1954 | static const char * const stat_report_usage[] = { |
|---|
| .. | .. |
|---|
| 2762 | 1976 | OPT_STRING('i', "input", &input_name, "file", "input file name"), |
|---|
| 2763 | 1977 | OPT_SET_UINT(0, "per-socket", &perf_stat.aggr_mode, |
|---|
| 2764 | 1978 | "aggregate counts per processor socket", AGGR_SOCKET), |
|---|
| 1979 | + OPT_SET_UINT(0, "per-die", &perf_stat.aggr_mode, |
|---|
| 1980 | + "aggregate counts per processor die", AGGR_DIE), |
|---|
| 2765 | 1981 | OPT_SET_UINT(0, "per-core", &perf_stat.aggr_mode, |
|---|
| 2766 | 1982 | "aggregate counts per physical processor core", AGGR_CORE), |
|---|
| 1983 | + OPT_SET_UINT(0, "per-node", &perf_stat.aggr_mode, |
|---|
| 1984 | + "aggregate counts per numa node", AGGR_NODE), |
|---|
| 2767 | 1985 | OPT_SET_UINT('A', "no-aggr", &perf_stat.aggr_mode, |
|---|
| 2768 | 1986 | "disable CPU count aggregation", AGGR_NONE), |
|---|
| 2769 | 1987 | OPT_END() |
|---|
| .. | .. |
|---|
| 2780 | 1998 | input_name = "perf.data"; |
|---|
| 2781 | 1999 | } |
|---|
| 2782 | 2000 | |
|---|
| 2783 | | - perf_stat.data.file.path = input_name; |
|---|
| 2784 | | - perf_stat.data.mode = PERF_DATA_MODE_READ; |
|---|
| 2001 | + perf_stat.data.path = input_name; |
|---|
| 2002 | + perf_stat.data.mode = PERF_DATA_MODE_READ; |
|---|
| 2785 | 2003 | |
|---|
| 2786 | 2004 | session = perf_session__new(&perf_stat.data, false, &perf_stat.tool); |
|---|
| 2787 | | - if (session == NULL) |
|---|
| 2788 | | - return -1; |
|---|
| 2005 | + if (IS_ERR(session)) |
|---|
| 2006 | + return PTR_ERR(session); |
|---|
| 2789 | 2007 | |
|---|
| 2790 | 2008 | perf_stat.session = session; |
|---|
| 2791 | 2009 | stat_config.output = stderr; |
|---|
| .. | .. |
|---|
| 2816 | 2034 | if (!forks) |
|---|
| 2817 | 2035 | target.system_wide = true; |
|---|
| 2818 | 2036 | else { |
|---|
| 2819 | | - struct perf_evsel *counter; |
|---|
| 2037 | + struct evsel *counter; |
|---|
| 2820 | 2038 | |
|---|
| 2821 | 2039 | evlist__for_each_entry(evsel_list, counter) { |
|---|
| 2822 | | - if (!counter->system_wide) |
|---|
| 2040 | + if (!counter->core.system_wide && |
|---|
| 2041 | + strcmp(counter->name, "duration_time")) { |
|---|
| 2823 | 2042 | return; |
|---|
| 2043 | + } |
|---|
| 2824 | 2044 | } |
|---|
| 2825 | 2045 | |
|---|
| 2826 | | - if (evsel_list->nr_entries) |
|---|
| 2046 | + if (evsel_list->core.nr_entries) |
|---|
| 2827 | 2047 | target.system_wide = true; |
|---|
| 2828 | 2048 | } |
|---|
| 2829 | 2049 | } |
|---|
| .. | .. |
|---|
| 2842 | 2062 | |
|---|
| 2843 | 2063 | setlocale(LC_ALL, ""); |
|---|
| 2844 | 2064 | |
|---|
| 2845 | | - evsel_list = perf_evlist__new(); |
|---|
| 2065 | + evsel_list = evlist__new(); |
|---|
| 2846 | 2066 | if (evsel_list == NULL) |
|---|
| 2847 | 2067 | return -ENOMEM; |
|---|
| 2848 | 2068 | |
|---|
| .. | .. |
|---|
| 2859 | 2079 | perf_stat__collect_metric_expr(evsel_list); |
|---|
| 2860 | 2080 | perf_stat__init_shadow_stats(); |
|---|
| 2861 | 2081 | |
|---|
| 2862 | | - if (csv_sep) { |
|---|
| 2863 | | - csv_output = true; |
|---|
| 2864 | | - if (!strcmp(csv_sep, "\\t")) |
|---|
| 2865 | | - csv_sep = "\t"; |
|---|
| 2082 | + if (stat_config.csv_sep) { |
|---|
| 2083 | + stat_config.csv_output = true; |
|---|
| 2084 | + if (!strcmp(stat_config.csv_sep, "\\t")) |
|---|
| 2085 | + stat_config.csv_sep = "\t"; |
|---|
| 2866 | 2086 | } else |
|---|
| 2867 | | - csv_sep = DEFAULT_SEPARATOR; |
|---|
| 2087 | + stat_config.csv_sep = DEFAULT_SEPARATOR; |
|---|
| 2868 | 2088 | |
|---|
| 2869 | 2089 | if (argc && !strncmp(argv[0], "rec", 3)) { |
|---|
| 2870 | 2090 | argc = __cmd_record(argc, argv); |
|---|
| .. | .. |
|---|
| 2889 | 2109 | goto out; |
|---|
| 2890 | 2110 | } |
|---|
| 2891 | 2111 | |
|---|
| 2892 | | - if (metric_only && stat_config.aggr_mode == AGGR_THREAD) { |
|---|
| 2112 | + if (stat_config.metric_only && stat_config.aggr_mode == AGGR_THREAD) { |
|---|
| 2893 | 2113 | fprintf(stderr, "--metric-only is not supported with --per-thread\n"); |
|---|
| 2894 | 2114 | goto out; |
|---|
| 2895 | 2115 | } |
|---|
| 2896 | 2116 | |
|---|
| 2897 | | - if (metric_only && run_count > 1) { |
|---|
| 2117 | + if (stat_config.metric_only && stat_config.run_count > 1) { |
|---|
| 2898 | 2118 | fprintf(stderr, "--metric-only is not supported with -r\n"); |
|---|
| 2899 | 2119 | goto out; |
|---|
| 2900 | 2120 | } |
|---|
| 2901 | 2121 | |
|---|
| 2902 | | - if (walltime_run_table && run_count <= 1) { |
|---|
| 2122 | + if (stat_config.walltime_run_table && stat_config.run_count <= 1) { |
|---|
| 2903 | 2123 | fprintf(stderr, "--table is only supported with -r\n"); |
|---|
| 2904 | 2124 | parse_options_usage(stat_usage, stat_options, "r", 1); |
|---|
| 2905 | 2125 | parse_options_usage(NULL, stat_options, "table", 0); |
|---|
| .. | .. |
|---|
| 2937 | 2157 | /* |
|---|
| 2938 | 2158 | * let the spreadsheet do the pretty-printing |
|---|
| 2939 | 2159 | */ |
|---|
| 2940 | | - if (csv_output) { |
|---|
| 2160 | + if (stat_config.csv_output) { |
|---|
| 2941 | 2161 | /* User explicitly passed -B? */ |
|---|
| 2942 | 2162 | if (big_num_opt == 1) { |
|---|
| 2943 | 2163 | fprintf(stderr, "-B option not supported with -x\n"); |
|---|
| .. | .. |
|---|
| 2945 | 2165 | parse_options_usage(NULL, stat_options, "x", 1); |
|---|
| 2946 | 2166 | goto out; |
|---|
| 2947 | 2167 | } else /* Nope, so disable big number formatting */ |
|---|
| 2948 | | - big_num = false; |
|---|
| 2168 | + stat_config.big_num = false; |
|---|
| 2949 | 2169 | } else if (big_num_opt == 0) /* User passed --no-big-num */ |
|---|
| 2950 | | - big_num = false; |
|---|
| 2170 | + stat_config.big_num = false; |
|---|
| 2951 | 2171 | |
|---|
| 2952 | 2172 | setup_system_wide(argc); |
|---|
| 2953 | 2173 | |
|---|
| .. | .. |
|---|
| 2955 | 2175 | * Display user/system times only for single |
|---|
| 2956 | 2176 | * run and when there's specified tracee. |
|---|
| 2957 | 2177 | */ |
|---|
| 2958 | | - if ((run_count == 1) && target__none(&target)) |
|---|
| 2959 | | - ru_display = true; |
|---|
| 2178 | + if ((stat_config.run_count == 1) && target__none(&target)) |
|---|
| 2179 | + stat_config.ru_display = true; |
|---|
| 2960 | 2180 | |
|---|
| 2961 | | - if (run_count < 0) { |
|---|
| 2181 | + if (stat_config.run_count < 0) { |
|---|
| 2962 | 2182 | pr_err("Run count must be a positive number\n"); |
|---|
| 2963 | 2183 | parse_options_usage(stat_usage, stat_options, "r", 1); |
|---|
| 2964 | 2184 | goto out; |
|---|
| 2965 | | - } else if (run_count == 0) { |
|---|
| 2185 | + } else if (stat_config.run_count == 0) { |
|---|
| 2966 | 2186 | forever = true; |
|---|
| 2967 | | - run_count = 1; |
|---|
| 2187 | + stat_config.run_count = 1; |
|---|
| 2968 | 2188 | } |
|---|
| 2969 | 2189 | |
|---|
| 2970 | | - if (walltime_run_table) { |
|---|
| 2971 | | - walltime_run = zalloc(run_count * sizeof(walltime_run[0])); |
|---|
| 2972 | | - if (!walltime_run) { |
|---|
| 2190 | + if (stat_config.walltime_run_table) { |
|---|
| 2191 | + stat_config.walltime_run = zalloc(stat_config.run_count * sizeof(stat_config.walltime_run[0])); |
|---|
| 2192 | + if (!stat_config.walltime_run) { |
|---|
| 2973 | 2193 | pr_err("failed to setup -r option"); |
|---|
| 2974 | 2194 | goto out; |
|---|
| 2975 | 2195 | } |
|---|
| .. | .. |
|---|
| 3006 | 2226 | if (add_default_attributes()) |
|---|
| 3007 | 2227 | goto out; |
|---|
| 3008 | 2228 | |
|---|
| 2229 | + if (stat_config.cgroup_list) { |
|---|
| 2230 | + if (nr_cgroups > 0) { |
|---|
| 2231 | + pr_err("--cgroup and --for-each-cgroup cannot be used together\n"); |
|---|
| 2232 | + parse_options_usage(stat_usage, stat_options, "G", 1); |
|---|
| 2233 | + parse_options_usage(NULL, stat_options, "for-each-cgroup", 0); |
|---|
| 2234 | + goto out; |
|---|
| 2235 | + } |
|---|
| 2236 | + |
|---|
| 2237 | + if (evlist__expand_cgroup(evsel_list, stat_config.cgroup_list, |
|---|
| 2238 | + &stat_config.metric_events, true) < 0) |
|---|
| 2239 | + goto out; |
|---|
| 2240 | + } |
|---|
| 2241 | + |
|---|
| 3009 | 2242 | target__validate(&target); |
|---|
| 3010 | 2243 | |
|---|
| 3011 | 2244 | if ((stat_config.aggr_mode == AGGR_THREAD) && (target.system_wide)) |
|---|
| .. | .. |
|---|
| 3024 | 2257 | goto out; |
|---|
| 3025 | 2258 | } |
|---|
| 3026 | 2259 | |
|---|
| 2260 | + evlist__check_cpu_maps(evsel_list); |
|---|
| 2261 | + |
|---|
| 3027 | 2262 | /* |
|---|
| 3028 | 2263 | * Initialize thread_map with comm names, |
|---|
| 3029 | 2264 | * so we could print it out on output. |
|---|
| 3030 | 2265 | */ |
|---|
| 3031 | 2266 | if (stat_config.aggr_mode == AGGR_THREAD) { |
|---|
| 3032 | | - thread_map__read_comms(evsel_list->threads); |
|---|
| 2267 | + thread_map__read_comms(evsel_list->core.threads); |
|---|
| 3033 | 2268 | if (target.system_wide) { |
|---|
| 3034 | 2269 | if (runtime_stat_new(&stat_config, |
|---|
| 3035 | | - thread_map__nr(evsel_list->threads))) { |
|---|
| 2270 | + perf_thread_map__nr(evsel_list->core.threads))) { |
|---|
| 3036 | 2271 | goto out; |
|---|
| 3037 | 2272 | } |
|---|
| 3038 | 2273 | } |
|---|
| 3039 | 2274 | } |
|---|
| 2275 | + |
|---|
| 2276 | + if (stat_config.aggr_mode == AGGR_NODE) |
|---|
| 2277 | + cpu__setup_cpunode_map(); |
|---|
| 3040 | 2278 | |
|---|
| 3041 | 2279 | if (stat_config.times && interval) |
|---|
| 3042 | 2280 | interval_count = true; |
|---|
| .. | .. |
|---|
| 3072 | 2310 | goto out; |
|---|
| 3073 | 2311 | |
|---|
| 3074 | 2312 | /* |
|---|
| 2313 | + * Set sample_type to PERF_SAMPLE_IDENTIFIER, which should be harmless |
|---|
| 2314 | + * while avoiding that older tools show confusing messages. |
|---|
| 2315 | + * |
|---|
| 2316 | + * However for pipe sessions we need to keep it zero, |
|---|
| 2317 | + * because script's perf_evsel__check_attr is triggered |
|---|
| 2318 | + * by attr->sample_type != 0, and we can't run it on |
|---|
| 2319 | + * stat sessions. |
|---|
| 2320 | + */ |
|---|
| 2321 | + stat_config.identifier = !(STAT_RECORD && perf_stat.data.is_pipe); |
|---|
| 2322 | + |
|---|
| 2323 | + /* |
|---|
| 3075 | 2324 | * We dont want to block the signals - that would cause |
|---|
| 3076 | 2325 | * child tasks to inherit that and Ctrl-C would not work. |
|---|
| 3077 | 2326 | * What we want is for Ctrl-C to work in the exec()-ed |
|---|
| .. | .. |
|---|
| 3084 | 2333 | signal(SIGALRM, skip_signal); |
|---|
| 3085 | 2334 | signal(SIGABRT, skip_signal); |
|---|
| 3086 | 2335 | |
|---|
| 2336 | + if (evlist__initialize_ctlfd(evsel_list, stat_config.ctl_fd, stat_config.ctl_fd_ack)) |
|---|
| 2337 | + goto out; |
|---|
| 2338 | + |
|---|
| 3087 | 2339 | status = 0; |
|---|
| 3088 | | - for (run_idx = 0; forever || run_idx < run_count; run_idx++) { |
|---|
| 3089 | | - if (run_count != 1 && verbose > 0) |
|---|
| 2340 | + for (run_idx = 0; forever || run_idx < stat_config.run_count; run_idx++) { |
|---|
| 2341 | + if (stat_config.run_count != 1 && verbose > 0) |
|---|
| 3090 | 2342 | fprintf(output, "[ perf stat: executing run #%d ... ]\n", |
|---|
| 3091 | 2343 | run_idx + 1); |
|---|
| 3092 | 2344 | |
|---|
| .. | .. |
|---|
| 3100 | 2352 | } |
|---|
| 3101 | 2353 | } |
|---|
| 3102 | 2354 | |
|---|
| 3103 | | - if (!forever && status != -1 && !interval) |
|---|
| 2355 | + if (!forever && status != -1 && (!interval || stat_config.summary)) |
|---|
| 3104 | 2356 | print_counters(NULL, argc, argv); |
|---|
| 2357 | + |
|---|
| 2358 | + evlist__finalize_ctlfd(evsel_list); |
|---|
| 3105 | 2359 | |
|---|
| 3106 | 2360 | if (STAT_RECORD) { |
|---|
| 3107 | 2361 | /* |
|---|
| .. | .. |
|---|
| 3135 | 2389 | perf_session__write_header(perf_stat.session, evsel_list, fd, true); |
|---|
| 3136 | 2390 | } |
|---|
| 3137 | 2391 | |
|---|
| 2392 | + evlist__close(evsel_list); |
|---|
| 3138 | 2393 | perf_session__delete(perf_stat.session); |
|---|
| 3139 | 2394 | } |
|---|
| 3140 | 2395 | |
|---|
| 3141 | 2396 | perf_stat__exit_aggr_mode(); |
|---|
| 3142 | 2397 | perf_evlist__free_stats(evsel_list); |
|---|
| 3143 | 2398 | out: |
|---|
| 3144 | | - free(walltime_run); |
|---|
| 2399 | + zfree(&stat_config.walltime_run); |
|---|
| 3145 | 2400 | |
|---|
| 3146 | 2401 | if (smi_cost && smi_reset) |
|---|
| 3147 | 2402 | sysfs__write_int(FREEZE_ON_SMI_PATH, 0); |
|---|
| 3148 | 2403 | |
|---|
| 3149 | | - perf_evlist__delete(evsel_list); |
|---|
| 2404 | + evlist__delete(evsel_list); |
|---|
| 3150 | 2405 | |
|---|
| 2406 | + metricgroup__rblist_exit(&stat_config.metric_events); |
|---|
| 3151 | 2407 | runtime_stat_delete(&stat_config); |
|---|
| 2408 | + evlist__close_control(stat_config.ctl_fd, stat_config.ctl_fd_ack, &stat_config.ctl_fd_close); |
|---|
| 3152 | 2409 | |
|---|
| 3153 | 2410 | return status; |
|---|
| 3154 | 2411 | } |
|---|