diff options
Diffstat (limited to 'tools/perf/builtin-stat.c')
| -rw-r--r-- | tools/perf/builtin-stat.c | 190 |
1 files changed, 164 insertions, 26 deletions
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 13b54999ad79..01b589e3c3a6 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c | |||
| @@ -140,12 +140,14 @@ static unsigned int unit_width = 4; /* strlen("unit") */ | |||
| 140 | static bool forever = false; | 140 | static bool forever = false; |
| 141 | static bool metric_only = false; | 141 | static bool metric_only = false; |
| 142 | static bool force_metric_only = false; | 142 | static bool force_metric_only = false; |
| 143 | static bool no_merge = false; | ||
| 143 | static struct timespec ref_time; | 144 | static struct timespec ref_time; |
| 144 | static struct cpu_map *aggr_map; | 145 | static struct cpu_map *aggr_map; |
| 145 | static aggr_get_id_t aggr_get_id; | 146 | static aggr_get_id_t aggr_get_id; |
| 146 | static bool append_file; | 147 | static bool append_file; |
| 147 | static const char *output_name; | 148 | static const char *output_name; |
| 148 | static int output_fd; | 149 | static int output_fd; |
| 150 | static int print_free_counters_hint; | ||
| 149 | 151 | ||
| 150 | struct perf_stat { | 152 | struct perf_stat { |
| 151 | bool record; | 153 | bool record; |
| @@ -1109,6 +1111,9 @@ static void printout(int id, int nr, struct perf_evsel *counter, double uval, | |||
| 1109 | counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED, | 1111 | counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED, |
| 1110 | csv_sep); | 1112 | csv_sep); |
| 1111 | 1113 | ||
| 1114 | if (counter->supported) | ||
| 1115 | print_free_counters_hint = 1; | ||
| 1116 | |||
| 1112 | fprintf(stat_config.output, "%-*s%s", | 1117 | fprintf(stat_config.output, "%-*s%s", |
| 1113 | csv_output ? 0 : unit_width, | 1118 | csv_output ? 0 : unit_width, |
| 1114 | counter->unit, csv_sep); | 1119 | counter->unit, csv_sep); |
| @@ -1140,6 +1145,7 @@ static void printout(int id, int nr, struct perf_evsel *counter, double uval, | |||
| 1140 | out.print_metric = pm; | 1145 | out.print_metric = pm; |
| 1141 | out.new_line = nl; | 1146 | out.new_line = nl; |
| 1142 | out.ctx = &os; | 1147 | out.ctx = &os; |
| 1148 | out.force_header = false; | ||
| 1143 | 1149 | ||
| 1144 | if (csv_output && !metric_only) { | 1150 | if (csv_output && !metric_only) { |
| 1145 | print_noise(counter, noise); | 1151 | print_noise(counter, noise); |
| @@ -1178,11 +1184,81 @@ static void aggr_update_shadow(void) | |||
| 1178 | } | 1184 | } |
| 1179 | } | 1185 | } |
| 1180 | 1186 | ||
| 1187 | static void collect_all_aliases(struct perf_evsel *counter, | ||
| 1188 | void (*cb)(struct perf_evsel *counter, void *data, | ||
| 1189 | bool first), | ||
| 1190 | void *data) | ||
| 1191 | { | ||
| 1192 | struct perf_evsel *alias; | ||
| 1193 | |||
| 1194 | alias = list_prepare_entry(counter, &(evsel_list->entries), node); | ||
| 1195 | list_for_each_entry_continue (alias, &evsel_list->entries, node) { | ||
| 1196 | if (strcmp(perf_evsel__name(alias), perf_evsel__name(counter)) || | ||
| 1197 | alias->scale != counter->scale || | ||
| 1198 | alias->cgrp != counter->cgrp || | ||
| 1199 | strcmp(alias->unit, counter->unit) || | ||
| 1200 | nsec_counter(alias) != nsec_counter(counter)) | ||
| 1201 | break; | ||
| 1202 | alias->merged_stat = true; | ||
| 1203 | cb(alias, data, false); | ||
| 1204 | } | ||
| 1205 | } | ||
| 1206 | |||
| 1207 | static bool collect_data(struct perf_evsel *counter, | ||
| 1208 | void (*cb)(struct perf_evsel *counter, void *data, | ||
| 1209 | bool first), | ||
| 1210 | void *data) | ||
| 1211 | { | ||
| 1212 | if (counter->merged_stat) | ||
| 1213 | return false; | ||
| 1214 | cb(counter, data, true); | ||
| 1215 | if (!no_merge) | ||
| 1216 | collect_all_aliases(counter, cb, data); | ||
| 1217 | return true; | ||
| 1218 | } | ||
| 1219 | |||
| 1220 | struct aggr_data { | ||
| 1221 | u64 ena, run, val; | ||
| 1222 | int id; | ||
| 1223 | int nr; | ||
| 1224 | int cpu; | ||
| 1225 | }; | ||
| 1226 | |||
| 1227 | static void aggr_cb(struct perf_evsel *counter, void *data, bool first) | ||
| 1228 | { | ||
| 1229 | struct aggr_data *ad = data; | ||
| 1230 | int cpu, s2; | ||
| 1231 | |||
| 1232 | for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) { | ||
| 1233 | struct perf_counts_values *counts; | ||
| 1234 | |||
| 1235 | s2 = aggr_get_id(perf_evsel__cpus(counter), cpu); | ||
| 1236 | if (s2 != ad->id) | ||
| 1237 | continue; | ||
| 1238 | if (first) | ||
| 1239 | ad->nr++; | ||
| 1240 | counts = perf_counts(counter->counts, cpu, 0); | ||
| 1241 | /* | ||
| 1242 | * When any result is bad, make them all to give | ||
| 1243 | * consistent output in interval mode. | ||
| 1244 | */ | ||
| 1245 | if (counts->ena == 0 || counts->run == 0 || | ||
| 1246 | counter->counts->scaled == -1) { | ||
| 1247 | ad->ena = 0; | ||
| 1248 | ad->run = 0; | ||
| 1249 | break; | ||
| 1250 | } | ||
| 1251 | ad->val += counts->val; | ||
| 1252 | ad->ena += counts->ena; | ||
| 1253 | ad->run += counts->run; | ||
| 1254 | } | ||
| 1255 | } | ||
| 1256 | |||
| 1181 | static void print_aggr(char *prefix) | 1257 | static void print_aggr(char *prefix) |
| 1182 | { | 1258 | { |
| 1183 | FILE *output = stat_config.output; | 1259 | FILE *output = stat_config.output; |
| 1184 | struct perf_evsel *counter; | 1260 | struct perf_evsel *counter; |
| 1185 | int cpu, s, s2, id, nr; | 1261 | int s, id, nr; |
| 1186 | double uval; | 1262 | double uval; |
| 1187 | u64 ena, run, val; | 1263 | u64 ena, run, val; |
| 1188 | bool first; | 1264 | bool first; |
| @@ -1197,23 +1273,21 @@ static void print_aggr(char *prefix) | |||
| 1197 | * Without each counter has its own line. | 1273 | * Without each counter has its own line. |
| 1198 | */ | 1274 | */ |
| 1199 | for (s = 0; s < aggr_map->nr; s++) { | 1275 | for (s = 0; s < aggr_map->nr; s++) { |
| 1276 | struct aggr_data ad; | ||
| 1200 | if (prefix && metric_only) | 1277 | if (prefix && metric_only) |
| 1201 | fprintf(output, "%s", prefix); | 1278 | fprintf(output, "%s", prefix); |
| 1202 | 1279 | ||
| 1203 | id = aggr_map->map[s]; | 1280 | ad.id = id = aggr_map->map[s]; |
| 1204 | first = true; | 1281 | first = true; |
| 1205 | evlist__for_each_entry(evsel_list, counter) { | 1282 | evlist__for_each_entry(evsel_list, counter) { |
| 1206 | val = ena = run = 0; | 1283 | ad.val = ad.ena = ad.run = 0; |
| 1207 | nr = 0; | 1284 | ad.nr = 0; |
| 1208 | for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) { | 1285 | if (!collect_data(counter, aggr_cb, &ad)) |
| 1209 | s2 = aggr_get_id(perf_evsel__cpus(counter), cpu); | 1286 | continue; |
| 1210 | if (s2 != id) | 1287 | nr = ad.nr; |
| 1211 | continue; | 1288 | ena = ad.ena; |
| 1212 | val += perf_counts(counter->counts, cpu, 0)->val; | 1289 | run = ad.run; |
| 1213 | ena += perf_counts(counter->counts, cpu, 0)->ena; | 1290 | val = ad.val; |
| 1214 | run += perf_counts(counter->counts, cpu, 0)->run; | ||
| 1215 | nr++; | ||
| 1216 | } | ||
| 1217 | if (first && metric_only) { | 1291 | if (first && metric_only) { |
| 1218 | first = false; | 1292 | first = false; |
| 1219 | aggr_printout(counter, id, nr); | 1293 | aggr_printout(counter, id, nr); |
| @@ -1257,6 +1331,21 @@ static void print_aggr_thread(struct perf_evsel *counter, char *prefix) | |||
| 1257 | } | 1331 | } |
| 1258 | } | 1332 | } |
| 1259 | 1333 | ||
| 1334 | struct caggr_data { | ||
| 1335 | double avg, avg_enabled, avg_running; | ||
| 1336 | }; | ||
| 1337 | |||
| 1338 | static void counter_aggr_cb(struct perf_evsel *counter, void *data, | ||
| 1339 | bool first __maybe_unused) | ||
| 1340 | { | ||
| 1341 | struct caggr_data *cd = data; | ||
| 1342 | struct perf_stat_evsel *ps = counter->priv; | ||
| 1343 | |||
| 1344 | cd->avg += avg_stats(&ps->res_stats[0]); | ||
| 1345 | cd->avg_enabled += avg_stats(&ps->res_stats[1]); | ||
| 1346 | cd->avg_running += avg_stats(&ps->res_stats[2]); | ||
| 1347 | } | ||
| 1348 | |||
| 1260 | /* | 1349 | /* |
| 1261 | * Print out the results of a single counter: | 1350 | * Print out the results of a single counter: |
| 1262 | * aggregated counts in system-wide mode | 1351 | * aggregated counts in system-wide mode |
| @@ -1264,23 +1353,31 @@ static void print_aggr_thread(struct perf_evsel *counter, char *prefix) | |||
| 1264 | static void print_counter_aggr(struct perf_evsel *counter, char *prefix) | 1353 | static void print_counter_aggr(struct perf_evsel *counter, char *prefix) |
| 1265 | { | 1354 | { |
| 1266 | FILE *output = stat_config.output; | 1355 | FILE *output = stat_config.output; |
| 1267 | struct perf_stat_evsel *ps = counter->priv; | ||
| 1268 | double avg = avg_stats(&ps->res_stats[0]); | ||
| 1269 | double uval; | 1356 | double uval; |
| 1270 | double avg_enabled, avg_running; | 1357 | struct caggr_data cd = { .avg = 0.0 }; |
| 1271 | 1358 | ||
| 1272 | avg_enabled = avg_stats(&ps->res_stats[1]); | 1359 | if (!collect_data(counter, counter_aggr_cb, &cd)) |
| 1273 | avg_running = avg_stats(&ps->res_stats[2]); | 1360 | return; |
| 1274 | 1361 | ||
| 1275 | if (prefix && !metric_only) | 1362 | if (prefix && !metric_only) |
| 1276 | fprintf(output, "%s", prefix); | 1363 | fprintf(output, "%s", prefix); |
| 1277 | 1364 | ||
| 1278 | uval = avg * counter->scale; | 1365 | uval = cd.avg * counter->scale; |
| 1279 | printout(-1, 0, counter, uval, prefix, avg_running, avg_enabled, avg); | 1366 | printout(-1, 0, counter, uval, prefix, cd.avg_running, cd.avg_enabled, cd.avg); |
| 1280 | if (!metric_only) | 1367 | if (!metric_only) |
| 1281 | fprintf(output, "\n"); | 1368 | fprintf(output, "\n"); |
| 1282 | } | 1369 | } |
| 1283 | 1370 | ||
| 1371 | static void counter_cb(struct perf_evsel *counter, void *data, | ||
| 1372 | bool first __maybe_unused) | ||
| 1373 | { | ||
| 1374 | struct aggr_data *ad = data; | ||
| 1375 | |||
| 1376 | ad->val += perf_counts(counter->counts, ad->cpu, 0)->val; | ||
| 1377 | ad->ena += perf_counts(counter->counts, ad->cpu, 0)->ena; | ||
| 1378 | ad->run += perf_counts(counter->counts, ad->cpu, 0)->run; | ||
| 1379 | } | ||
| 1380 | |||
| 1284 | /* | 1381 | /* |
| 1285 | * Print out the results of a single counter: | 1382 | * Print out the results of a single counter: |
| 1286 | * does not use aggregated count in system-wide | 1383 | * does not use aggregated count in system-wide |
| @@ -1293,9 +1390,13 @@ static void print_counter(struct perf_evsel *counter, char *prefix) | |||
| 1293 | int cpu; | 1390 | int cpu; |
| 1294 | 1391 | ||
| 1295 | for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) { | 1392 | for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) { |
| 1296 | val = perf_counts(counter->counts, cpu, 0)->val; | 1393 | struct aggr_data ad = { .cpu = cpu }; |
| 1297 | ena = perf_counts(counter->counts, cpu, 0)->ena; | 1394 | |
| 1298 | run = perf_counts(counter->counts, cpu, 0)->run; | 1395 | if (!collect_data(counter, counter_cb, &ad)) |
| 1396 | return; | ||
| 1397 | val = ad.val; | ||
| 1398 | ena = ad.ena; | ||
| 1399 | run = ad.run; | ||
| 1299 | 1400 | ||
| 1300 | if (prefix) | 1401 | if (prefix) |
| 1301 | fprintf(output, "%s", prefix); | 1402 | fprintf(output, "%s", prefix); |
| @@ -1380,6 +1481,7 @@ static void print_metric_headers(const char *prefix, bool no_indent) | |||
| 1380 | out.ctx = &os; | 1481 | out.ctx = &os; |
| 1381 | out.print_metric = print_metric_header; | 1482 | out.print_metric = print_metric_header; |
| 1382 | out.new_line = new_line_metric; | 1483 | out.new_line = new_line_metric; |
| 1484 | out.force_header = true; | ||
| 1383 | os.evsel = counter; | 1485 | os.evsel = counter; |
| 1384 | perf_stat__print_shadow_stats(counter, 0, | 1486 | perf_stat__print_shadow_stats(counter, 0, |
| 1385 | 0, | 1487 | 0, |
| @@ -1477,6 +1579,13 @@ static void print_footer(void) | |||
| 1477 | avg_stats(&walltime_nsecs_stats)); | 1579 | avg_stats(&walltime_nsecs_stats)); |
| 1478 | } | 1580 | } |
| 1479 | fprintf(output, "\n\n"); | 1581 | fprintf(output, "\n\n"); |
| 1582 | |||
| 1583 | if (print_free_counters_hint) | ||
| 1584 | fprintf(output, | ||
| 1585 | "Some events weren't counted. Try disabling the NMI watchdog:\n" | ||
| 1586 | " echo 0 > /proc/sys/kernel/nmi_watchdog\n" | ||
| 1587 | " perf stat ...\n" | ||
| 1588 | " echo 1 > /proc/sys/kernel/nmi_watchdog\n"); | ||
| 1480 | } | 1589 | } |
| 1481 | 1590 | ||
| 1482 | static void print_counters(struct timespec *ts, int argc, const char **argv) | 1591 | static void print_counters(struct timespec *ts, int argc, const char **argv) |
| @@ -1633,6 +1742,7 @@ static const struct option stat_options[] = { | |||
| 1633 | "list of cpus to monitor in system-wide"), | 1742 | "list of cpus to monitor in system-wide"), |
| 1634 | OPT_SET_UINT('A', "no-aggr", &stat_config.aggr_mode, | 1743 | OPT_SET_UINT('A', "no-aggr", &stat_config.aggr_mode, |
| 1635 | "disable CPU count aggregation", AGGR_NONE), | 1744 | "disable CPU count aggregation", AGGR_NONE), |
| 1745 | OPT_BOOLEAN(0, "no-merge", &no_merge, "Do not merge identical named events"), | ||
| 1636 | OPT_STRING('x', "field-separator", &csv_sep, "separator", | 1746 | OPT_STRING('x', "field-separator", &csv_sep, "separator", |
| 1637 | "print counts with custom separator"), | 1747 | "print counts with custom separator"), |
| 1638 | OPT_CALLBACK('G', "cgroup", &evsel_list, "name", | 1748 | OPT_CALLBACK('G', "cgroup", &evsel_list, "name", |
| @@ -2339,6 +2449,35 @@ static int __cmd_report(int argc, const char **argv) | |||
| 2339 | return 0; | 2449 | return 0; |
| 2340 | } | 2450 | } |
| 2341 | 2451 | ||
| 2452 | static void setup_system_wide(int forks) | ||
| 2453 | { | ||
| 2454 | /* | ||
| 2455 | * Make system wide (-a) the default target if | ||
| 2456 | * no target was specified and one of following | ||
| 2457 | * conditions is met: | ||
| 2458 | * | ||
| 2459 | * - there's no workload specified | ||
| 2460 | * - there is workload specified but all requested | ||
| 2461 | * events are system wide events | ||
| 2462 | */ | ||
| 2463 | if (!target__none(&target)) | ||
| 2464 | return; | ||
| 2465 | |||
| 2466 | if (!forks) | ||
| 2467 | target.system_wide = true; | ||
| 2468 | else { | ||
| 2469 | struct perf_evsel *counter; | ||
| 2470 | |||
| 2471 | evlist__for_each_entry(evsel_list, counter) { | ||
| 2472 | if (!counter->system_wide) | ||
| 2473 | return; | ||
| 2474 | } | ||
| 2475 | |||
| 2476 | if (evsel_list->nr_entries) | ||
| 2477 | target.system_wide = true; | ||
| 2478 | } | ||
| 2479 | } | ||
| 2480 | |||
| 2342 | int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) | 2481 | int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) |
| 2343 | { | 2482 | { |
| 2344 | const char * const stat_usage[] = { | 2483 | const char * const stat_usage[] = { |
| @@ -2361,6 +2500,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 2361 | argc = parse_options_subcommand(argc, argv, stat_options, stat_subcommands, | 2500 | argc = parse_options_subcommand(argc, argv, stat_options, stat_subcommands, |
| 2362 | (const char **) stat_usage, | 2501 | (const char **) stat_usage, |
| 2363 | PARSE_OPT_STOP_AT_NON_OPTION); | 2502 | PARSE_OPT_STOP_AT_NON_OPTION); |
| 2503 | perf_stat__collect_metric_expr(evsel_list); | ||
| 2364 | perf_stat__init_shadow_stats(); | 2504 | perf_stat__init_shadow_stats(); |
| 2365 | 2505 | ||
| 2366 | if (csv_sep) { | 2506 | if (csv_sep) { |
| @@ -2445,9 +2585,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 2445 | } else if (big_num_opt == 0) /* User passed --no-big-num */ | 2585 | } else if (big_num_opt == 0) /* User passed --no-big-num */ |
| 2446 | big_num = false; | 2586 | big_num = false; |
| 2447 | 2587 | ||
| 2448 | /* Make system wide (-a) the default target. */ | 2588 | setup_system_wide(argc); |
| 2449 | if (!argc && target__none(&target)) | ||
| 2450 | target.system_wide = true; | ||
| 2451 | 2589 | ||
| 2452 | if (run_count < 0) { | 2590 | if (run_count < 0) { |
| 2453 | pr_err("Run count must be a positive number\n"); | 2591 | pr_err("Run count must be a positive number\n"); |
