Commit | Line | Data |
---|---|---|
f2a39fe8 | 1 | #include <stdlib.h> |
088519f3 JO |
2 | #include <stdio.h> |
3 | #include <inttypes.h> | |
810826ac | 4 | #include <linux/string.h> |
088519f3 JO |
5 | #include <linux/time64.h> |
6 | #include <math.h> | |
7ea82fbe | 7 | #include <perf/cpumap.h> |
8a249c73 | 8 | #include "color.h" |
bfc49182 | 9 | #include "counts.h" |
d38461e9 | 10 | #include "debug.h" |
088519f3 JO |
11 | #include "evlist.h" |
12 | #include "evsel.h" | |
13 | #include "stat.h" | |
14 | #include "top.h" | |
15 | #include "thread_map.h" | |
16 | #include "cpumap.h" | |
17 | #include "string2.h" | |
3052ba56 | 18 | #include <linux/ctype.h> |
088519f3 | 19 | #include "cgroup.h" |
088519f3 | 20 | #include <api/fs/fs.h> |
2a14c1bf | 21 | #include "util.h" |
f07952b1 | 22 | #include "iostat.h" |
3d88055f | 23 | #include "pmu.h" |
1eaf496e | 24 | #include "pmus.h" |
240505b2 | 25 | #include "tool_pmu.h" |
088519f3 JO |
26 | |
27 | #define CNTR_NOT_SUPPORTED "<not supported>" | |
28 | #define CNTR_NOT_COUNTED "<not counted>" | |
29 | ||
6a80d794 | 30 | #define MGROUP_LEN 50 |
33c4ed47 NK |
31 | #define METRIC_LEN 38 |
32 | #define EVNAME_LEN 32 | |
33 | #define COUNTS_LEN 18 | |
34 | #define INTERVAL_LEN 16 | |
35 | #define CGROUP_LEN 16 | |
36 | #define COMM_LEN 16 | |
37 | #define PID_LEN 7 | |
38 | #define CPUS_LEN 4 | |
39 | ||
40 | static int aggr_header_lens[] = { | |
41 | [AGGR_CORE] = 18, | |
995ed074 | 42 | [AGGR_CACHE] = 22, |
caa463bb | 43 | [AGGR_CLUSTER] = 20, |
33c4ed47 NK |
44 | [AGGR_DIE] = 12, |
45 | [AGGR_SOCKET] = 6, | |
46 | [AGGR_NODE] = 6, | |
47 | [AGGR_NONE] = 6, | |
48 | [AGGR_THREAD] = 16, | |
49 | [AGGR_GLOBAL] = 0, | |
50 | }; | |
51 | ||
52 | static const char *aggr_header_csv[] = { | |
53 | [AGGR_CORE] = "core,cpus,", | |
995ed074 | 54 | [AGGR_CACHE] = "cache,cpus,", |
caa463bb | 55 | [AGGR_CLUSTER] = "cluster,cpus,", |
33c4ed47 NK |
56 | [AGGR_DIE] = "die,cpus,", |
57 | [AGGR_SOCKET] = "socket,cpus,", | |
58 | [AGGR_NONE] = "cpu,", | |
59 | [AGGR_THREAD] = "comm-pid,", | |
60 | [AGGR_NODE] = "node,", | |
61 | [AGGR_GLOBAL] = "" | |
62 | }; | |
63 | ||
64 | static const char *aggr_header_std[] = { | |
65 | [AGGR_CORE] = "core", | |
995ed074 | 66 | [AGGR_CACHE] = "cache", |
caa463bb | 67 | [AGGR_CLUSTER] = "cluster", |
33c4ed47 NK |
68 | [AGGR_DIE] = "die", |
69 | [AGGR_SOCKET] = "socket", | |
70 | [AGGR_NONE] = "cpu", | |
71 | [AGGR_THREAD] = "comm-pid", | |
72 | [AGGR_NODE] = "node", | |
73 | [AGGR_GLOBAL] = "" | |
74 | }; | |
75 | ||
37b77ae9 IR |
76 | const char *metric_threshold_classify__color(enum metric_threshold_classify thresh) |
77 | { | |
78 | const char * const colors[] = { | |
79 | "", /* unknown */ | |
80 | PERF_COLOR_RED, /* bad */ | |
81 | PERF_COLOR_MAGENTA, /* nearly bad */ | |
82 | PERF_COLOR_YELLOW, /* less good */ | |
83 | PERF_COLOR_GREEN, /* good */ | |
84 | }; | |
85 | static_assert(ARRAY_SIZE(colors) - 1 == METRIC_THRESHOLD_GOOD, "missing enum value"); | |
86 | return colors[thresh]; | |
87 | } | |
88 | ||
f9825601 IR |
89 | static const char *metric_threshold_classify__str(enum metric_threshold_classify thresh) |
90 | { | |
91 | const char * const strs[] = { | |
92 | "unknown", | |
93 | "bad", | |
94 | "nearly bad", | |
95 | "less good", | |
96 | "good", | |
97 | }; | |
98 | static_assert(ARRAY_SIZE(strs) - 1 == METRIC_THRESHOLD_GOOD, "missing enum value"); | |
99 | return strs[thresh]; | |
100 | } | |
101 | ||
31bf6aea | 102 | static void print_running_std(struct perf_stat_config *config, u64 run, u64 ena) |
088519f3 | 103 | { |
31bf6aea NK |
104 | if (run != ena) |
105 | fprintf(config->output, " (%.2f%%)", 100.0 * run / ena); | |
106 | } | |
df936cad | 107 | |
31bf6aea NK |
108 | static void print_running_csv(struct perf_stat_config *config, u64 run, u64 ena) |
109 | { | |
df936cad CJ |
110 | double enabled_percent = 100; |
111 | ||
112 | if (run != ena) | |
113 | enabled_percent = 100 * run / ena; | |
31bf6aea NK |
114 | fprintf(config->output, "%s%" PRIu64 "%s%.2f", |
115 | config->csv_sep, run, config->csv_sep, enabled_percent); | |
116 | } | |
96736489 | 117 | struct outstate { |
ed60738a | 118 | /* Std mode: insert a newline before the next metric */ |
96736489 | 119 | bool newline; |
ed60738a | 120 | /* JSON mode: track need for comma for a previous field or not */ |
96736489 | 121 | bool first; |
ed60738a JC |
122 | /* Num CSV separators remaining to pad out when not all fields are printed */ |
123 | int csv_col_pad; | |
124 | ||
125 | /* | |
126 | * The following don't track state across fields, but are here as a shortcut to | |
127 | * pass data to the print functions. The alternative would be to update the | |
128 | * function signatures of the entire print stack to pass them through. | |
129 | */ | |
130 | /* Place to output to */ | |
131 | FILE * const fh; | |
dd566687 JC |
132 | /* Lines are timestamped in --interval-print mode */ |
133 | char timestamp[64]; | |
ed60738a JC |
134 | /* Num items aggregated in current line. See struct perf_stat_aggr.nr */ |
135 | int aggr_nr; | |
136 | /* Core/socket/die etc ID for the current line */ | |
96736489 | 137 | struct aggr_cpu_id id; |
ed60738a | 138 | /* Event for current line */ |
96736489 | 139 | struct evsel *evsel; |
ed60738a | 140 | /* Cgroup for current line */ |
96736489 JC |
141 | struct cgroup *cgrp; |
142 | }; | |
31bf6aea | 143 | |
96736489 JC |
144 | static const char *json_sep(struct outstate *os) |
145 | { | |
146 | const char *sep = os->first ? "" : ", "; | |
147 | ||
148 | os->first = false; | |
149 | return sep; | |
150 | } | |
151 | ||
152 | #define json_out(os, format, ...) fprintf((os)->fh, "%s" format, json_sep(os), ##__VA_ARGS__) | |
153 | ||
154 | static void print_running_json(struct outstate *os, u64 run, u64 ena) | |
31bf6aea NK |
155 | { |
156 | double enabled_percent = 100; | |
157 | ||
158 | if (run != ena) | |
159 | enabled_percent = 100 * run / ena; | |
96736489 JC |
160 | json_out(os, "\"event-runtime\" : %" PRIu64 ", \"pcnt-running\" : %.2f", |
161 | run, enabled_percent); | |
31bf6aea NK |
162 | } |
163 | ||
96736489 | 164 | static void print_running(struct perf_stat_config *config, struct outstate *os, |
df46a3c9 | 165 | u64 run, u64 ena, bool before_metric) |
31bf6aea | 166 | { |
df46a3c9 NK |
167 | if (config->json_output) { |
168 | if (before_metric) | |
96736489 | 169 | print_running_json(os, run, ena); |
df46a3c9 NK |
170 | } else if (config->csv_output) { |
171 | if (before_metric) | |
172 | print_running_csv(config, run, ena); | |
173 | } else { | |
174 | if (!before_metric) | |
175 | print_running_std(config, run, ena); | |
176 | } | |
088519f3 JO |
177 | } |
178 | ||
def99d60 NK |
179 | static void print_noise_pct_std(struct perf_stat_config *config, |
180 | double pct) | |
181 | { | |
182 | if (pct) | |
183 | fprintf(config->output, " ( +-%6.2f%% )", pct); | |
184 | } | |
185 | ||
186 | static void print_noise_pct_csv(struct perf_stat_config *config, | |
187 | double pct) | |
188 | { | |
189 | fprintf(config->output, "%s%.2f%%", config->csv_sep, pct); | |
190 | } | |
191 | ||
96736489 | 192 | static void print_noise_pct_json(struct outstate *os, |
def99d60 NK |
193 | double pct) |
194 | { | |
96736489 | 195 | json_out(os, "\"variance\" : %.2f", pct); |
def99d60 NK |
196 | } |
197 | ||
96736489 | 198 | static void print_noise_pct(struct perf_stat_config *config, struct outstate *os, |
df46a3c9 | 199 | double total, double avg, bool before_metric) |
088519f3 JO |
200 | { |
201 | double pct = rel_stddev_stats(total, avg); | |
202 | ||
df46a3c9 NK |
203 | if (config->json_output) { |
204 | if (before_metric) | |
96736489 | 205 | print_noise_pct_json(os, pct); |
df46a3c9 NK |
206 | } else if (config->csv_output) { |
207 | if (before_metric) | |
208 | print_noise_pct_csv(config, pct); | |
209 | } else { | |
210 | if (!before_metric) | |
211 | print_noise_pct_std(config, pct); | |
212 | } | |
088519f3 JO |
213 | } |
214 | ||
96736489 | 215 | static void print_noise(struct perf_stat_config *config, struct outstate *os, |
df46a3c9 | 216 | struct evsel *evsel, double avg, bool before_metric) |
088519f3 JO |
217 | { |
218 | struct perf_stat_evsel *ps; | |
219 | ||
220 | if (config->run_count == 1) | |
221 | return; | |
222 | ||
223 | ps = evsel->stats; | |
96736489 | 224 | print_noise_pct(config, os, stddev_stats(&ps->res_stats), avg, before_metric); |
088519f3 JO |
225 | } |
226 | ||
41cb8752 NK |
227 | static void print_cgroup_std(struct perf_stat_config *config, const char *cgrp_name) |
228 | { | |
33c4ed47 | 229 | fprintf(config->output, " %-*s", CGROUP_LEN, cgrp_name); |
41cb8752 NK |
230 | } |
231 | ||
232 | static void print_cgroup_csv(struct perf_stat_config *config, const char *cgrp_name) | |
233 | { | |
234 | fprintf(config->output, "%s%s", config->csv_sep, cgrp_name); | |
235 | } | |
236 | ||
96736489 | 237 | static void print_cgroup_json(struct outstate *os, const char *cgrp_name) |
41cb8752 | 238 | { |
96736489 | 239 | json_out(os, "\"cgroup\" : \"%s\"", cgrp_name); |
41cb8752 NK |
240 | } |
241 | ||
96736489 JC |
242 | static void print_cgroup(struct perf_stat_config *config, struct outstate *os, |
243 | struct cgroup *cgrp) | |
bc4da38a | 244 | { |
67f8b7eb NK |
245 | if (nr_cgroups || config->cgroup_list) { |
246 | const char *cgrp_name = cgrp ? cgrp->name : ""; | |
df936cad CJ |
247 | |
248 | if (config->json_output) | |
96736489 | 249 | print_cgroup_json(os, cgrp_name); |
15792642 | 250 | else if (config->csv_output) |
41cb8752 | 251 | print_cgroup_csv(config, cgrp_name); |
df936cad | 252 | else |
41cb8752 | 253 | print_cgroup_std(config, cgrp_name); |
bc4da38a SE |
254 | } |
255 | } | |
256 | ||
33b2e2c2 | 257 | static void print_aggr_id_std(struct perf_stat_config *config, |
8945bef3 | 258 | struct evsel *evsel, struct aggr_cpu_id id, int aggr_nr) |
088519f3 | 259 | { |
33b2e2c2 | 260 | FILE *output = config->output; |
33c4ed47 NK |
261 | int idx = config->aggr_mode; |
262 | char buf[128]; | |
df936cad | 263 | |
33b2e2c2 NK |
264 | switch (config->aggr_mode) { |
265 | case AGGR_CORE: | |
33c4ed47 | 266 | snprintf(buf, sizeof(buf), "S%d-D%d-C%d", id.socket, id.die, id.core); |
33b2e2c2 | 267 | break; |
995ed074 PN |
268 | case AGGR_CACHE: |
269 | snprintf(buf, sizeof(buf), "S%d-D%d-L%d-ID%d", | |
270 | id.socket, id.die, id.cache_lvl, id.cache); | |
271 | break; | |
cbc917a1 YY |
272 | case AGGR_CLUSTER: |
273 | snprintf(buf, sizeof(buf), "S%d-D%d-CLS%d", id.socket, id.die, id.cluster); | |
274 | break; | |
33b2e2c2 | 275 | case AGGR_DIE: |
33c4ed47 | 276 | snprintf(buf, sizeof(buf), "S%d-D%d", id.socket, id.die); |
33b2e2c2 NK |
277 | break; |
278 | case AGGR_SOCKET: | |
33c4ed47 | 279 | snprintf(buf, sizeof(buf), "S%d", id.socket); |
33b2e2c2 NK |
280 | break; |
281 | case AGGR_NODE: | |
33c4ed47 | 282 | snprintf(buf, sizeof(buf), "N%d", id.node); |
33b2e2c2 NK |
283 | break; |
284 | case AGGR_NONE: | |
285 | if (evsel->percore && !config->percore_show_thread) { | |
33c4ed47 NK |
286 | snprintf(buf, sizeof(buf), "S%d-D%d-C%d ", |
287 | id.socket, id.die, id.core); | |
288 | fprintf(output, "%-*s ", | |
289 | aggr_header_lens[AGGR_CORE], buf); | |
33b2e2c2 | 290 | } else if (id.cpu.cpu > -1) { |
33c4ed47 NK |
291 | fprintf(output, "CPU%-*d ", |
292 | aggr_header_lens[AGGR_NONE] - 3, id.cpu.cpu); | |
33b2e2c2 | 293 | } |
33c4ed47 | 294 | return; |
33b2e2c2 | 295 | case AGGR_THREAD: |
33c4ed47 NK |
296 | fprintf(output, "%*s-%-*d ", |
297 | COMM_LEN, perf_thread_map__comm(evsel->core.threads, id.thread_idx), | |
298 | PID_LEN, perf_thread_map__pid(evsel->core.threads, id.thread_idx)); | |
299 | return; | |
33b2e2c2 NK |
300 | case AGGR_GLOBAL: |
301 | case AGGR_UNSET: | |
302 | case AGGR_MAX: | |
303 | default: | |
33c4ed47 | 304 | return; |
33b2e2c2 | 305 | } |
33c4ed47 | 306 | |
8945bef3 | 307 | fprintf(output, "%-*s %*d ", aggr_header_lens[idx], buf, 4, aggr_nr); |
33b2e2c2 | 308 | } |
df936cad | 309 | |
33b2e2c2 | 310 | static void print_aggr_id_csv(struct perf_stat_config *config, |
8945bef3 | 311 | struct evsel *evsel, struct aggr_cpu_id id, int aggr_nr) |
33b2e2c2 NK |
312 | { |
313 | FILE *output = config->output; | |
314 | const char *sep = config->csv_sep; | |
df936cad | 315 | |
088519f3 JO |
316 | switch (config->aggr_mode) { |
317 | case AGGR_CORE: | |
33b2e2c2 | 318 | fprintf(output, "S%d-D%d-C%d%s%d%s", |
8945bef3 | 319 | id.socket, id.die, id.core, sep, aggr_nr, sep); |
088519f3 | 320 | break; |
995ed074 PN |
321 | case AGGR_CACHE: |
322 | fprintf(config->output, "S%d-D%d-L%d-ID%d%s%d%s", | |
323 | id.socket, id.die, id.cache_lvl, id.cache, sep, aggr_nr, sep); | |
324 | break; | |
cbc917a1 YY |
325 | case AGGR_CLUSTER: |
326 | fprintf(config->output, "S%d-D%d-CLS%d%s%d%s", | |
327 | id.socket, id.die, id.cluster, sep, aggr_nr, sep); | |
328 | break; | |
db5742b6 | 329 | case AGGR_DIE: |
33b2e2c2 | 330 | fprintf(output, "S%d-D%d%s%d%s", |
8945bef3 | 331 | id.socket, id.die, sep, aggr_nr, sep); |
db5742b6 | 332 | break; |
088519f3 | 333 | case AGGR_SOCKET: |
33b2e2c2 | 334 | fprintf(output, "S%d%s%d%s", |
8945bef3 | 335 | id.socket, sep, aggr_nr, sep); |
df936cad | 336 | break; |
86895b48 | 337 | case AGGR_NODE: |
33b2e2c2 | 338 | fprintf(output, "N%d%s%d%s", |
8945bef3 | 339 | id.node, sep, aggr_nr, sep); |
df936cad | 340 | break; |
088519f3 | 341 | case AGGR_NONE: |
33b2e2c2 NK |
342 | if (evsel->percore && !config->percore_show_thread) { |
343 | fprintf(output, "S%d-D%d-C%d%s", | |
344 | id.socket, id.die, id.core, sep); | |
345 | } else if (id.cpu.cpu > -1) { | |
346 | fprintf(output, "CPU%d%s", | |
347 | id.cpu.cpu, sep); | |
4fc4d8df | 348 | } |
088519f3 JO |
349 | break; |
350 | case AGGR_THREAD: | |
33b2e2c2 NK |
351 | fprintf(output, "%s-%d%s", |
352 | perf_thread_map__comm(evsel->core.threads, id.thread_idx), | |
353 | perf_thread_map__pid(evsel->core.threads, id.thread_idx), | |
354 | sep); | |
355 | break; | |
356 | case AGGR_GLOBAL: | |
357 | case AGGR_UNSET: | |
358 | case AGGR_MAX: | |
359 | default: | |
360 | break; | |
361 | } | |
362 | } | |
363 | ||
96736489 | 364 | static void print_aggr_id_json(struct perf_stat_config *config, struct outstate *os, |
8945bef3 | 365 | struct evsel *evsel, struct aggr_cpu_id id, int aggr_nr) |
33b2e2c2 | 366 | { |
33b2e2c2 NK |
367 | switch (config->aggr_mode) { |
368 | case AGGR_CORE: | |
96736489 | 369 | json_out(os, "\"core\" : \"S%d-D%d-C%d\", \"aggregate-number\" : %d", |
8945bef3 | 370 | id.socket, id.die, id.core, aggr_nr); |
33b2e2c2 | 371 | break; |
995ed074 | 372 | case AGGR_CACHE: |
96736489 | 373 | json_out(os, "\"cache\" : \"S%d-D%d-L%d-ID%d\", \"aggregate-number\" : %d", |
995ed074 PN |
374 | id.socket, id.die, id.cache_lvl, id.cache, aggr_nr); |
375 | break; | |
cbc917a1 | 376 | case AGGR_CLUSTER: |
96736489 | 377 | json_out(os, "\"cluster\" : \"S%d-D%d-CLS%d\", \"aggregate-number\" : %d", |
cbc917a1 YY |
378 | id.socket, id.die, id.cluster, aggr_nr); |
379 | break; | |
33b2e2c2 | 380 | case AGGR_DIE: |
96736489 | 381 | json_out(os, "\"die\" : \"S%d-D%d\", \"aggregate-number\" : %d", |
8945bef3 | 382 | id.socket, id.die, aggr_nr); |
33b2e2c2 NK |
383 | break; |
384 | case AGGR_SOCKET: | |
96736489 | 385 | json_out(os, "\"socket\" : \"S%d\", \"aggregate-number\" : %d", |
8945bef3 | 386 | id.socket, aggr_nr); |
33b2e2c2 NK |
387 | break; |
388 | case AGGR_NODE: | |
96736489 | 389 | json_out(os, "\"node\" : \"N%d\", \"aggregate-number\" : %d", |
8945bef3 | 390 | id.node, aggr_nr); |
33b2e2c2 NK |
391 | break; |
392 | case AGGR_NONE: | |
393 | if (evsel->percore && !config->percore_show_thread) { | |
96736489 | 394 | json_out(os, "\"core\" : \"S%d-D%d-C%d\"", |
33b2e2c2 NK |
395 | id.socket, id.die, id.core); |
396 | } else if (id.cpu.cpu > -1) { | |
96736489 | 397 | json_out(os, "\"cpu\" : \"%d\"", |
33b2e2c2 | 398 | id.cpu.cpu); |
df936cad | 399 | } |
088519f3 | 400 | break; |
33b2e2c2 | 401 | case AGGR_THREAD: |
96736489 | 402 | json_out(os, "\"thread\" : \"%s-%d\"", |
33b2e2c2 NK |
403 | perf_thread_map__comm(evsel->core.threads, id.thread_idx), |
404 | perf_thread_map__pid(evsel->core.threads, id.thread_idx)); | |
405 | break; | |
088519f3 JO |
406 | case AGGR_GLOBAL: |
407 | case AGGR_UNSET: | |
df936cad | 408 | case AGGR_MAX: |
088519f3 JO |
409 | default: |
410 | break; | |
411 | } | |
412 | } | |
413 | ||
96736489 | 414 | static void aggr_printout(struct perf_stat_config *config, struct outstate *os, |
8945bef3 | 415 | struct evsel *evsel, struct aggr_cpu_id id, int aggr_nr) |
33b2e2c2 NK |
416 | { |
417 | if (config->json_output) | |
96736489 | 418 | print_aggr_id_json(config, os, evsel, id, aggr_nr); |
33b2e2c2 | 419 | else if (config->csv_output) |
8945bef3 | 420 | print_aggr_id_csv(config, evsel, id, aggr_nr); |
33b2e2c2 | 421 | else |
8945bef3 | 422 | print_aggr_id_std(config, evsel, id, aggr_nr); |
33b2e2c2 NK |
423 | } |
424 | ||
088519f3 JO |
425 | static void new_line_std(struct perf_stat_config *config __maybe_unused, |
426 | void *ctx) | |
427 | { | |
428 | struct outstate *os = ctx; | |
429 | ||
430 | os->newline = true; | |
431 | } | |
432 | ||
6a80d794 KL |
433 | static inline void __new_line_std_csv(struct perf_stat_config *config, |
434 | struct outstate *os) | |
088519f3 JO |
435 | { |
436 | fputc('\n', os->fh); | |
dd566687 JC |
437 | if (config->interval) |
438 | fputs(os->timestamp, os->fh); | |
96736489 | 439 | aggr_printout(config, os, os->evsel, os->id, os->aggr_nr); |
6a80d794 KL |
440 | } |
441 | ||
442 | static inline void __new_line_std(struct outstate *os) | |
443 | { | |
444 | fprintf(os->fh, " "); | |
445 | } | |
446 | ||
447 | static void do_new_line_std(struct perf_stat_config *config, | |
448 | struct outstate *os) | |
449 | { | |
450 | __new_line_std_csv(config, os); | |
088519f3 JO |
451 | if (config->aggr_mode == AGGR_NONE) |
452 | fprintf(os->fh, " "); | |
6a80d794 | 453 | __new_line_std(os); |
088519f3 JO |
454 | } |
455 | ||
456 | static void print_metric_std(struct perf_stat_config *config, | |
37b77ae9 IR |
457 | void *ctx, enum metric_threshold_classify thresh, |
458 | const char *fmt, const char *unit, double val) | |
088519f3 JO |
459 | { |
460 | struct outstate *os = ctx; | |
461 | FILE *out = os->fh; | |
462 | int n; | |
463 | bool newline = os->newline; | |
37b77ae9 | 464 | const char *color = metric_threshold_classify__color(thresh); |
088519f3 JO |
465 | |
466 | os->newline = false; | |
467 | ||
468 | if (unit == NULL || fmt == NULL) { | |
469 | fprintf(out, "%-*s", METRIC_LEN, ""); | |
470 | return; | |
471 | } | |
472 | ||
473 | if (newline) | |
474 | do_new_line_std(config, os); | |
475 | ||
476 | n = fprintf(out, " # "); | |
477 | if (color) | |
478 | n += color_fprintf(out, color, fmt, val); | |
479 | else | |
480 | n += fprintf(out, fmt, val); | |
481 | fprintf(out, " %-*s", METRIC_LEN - n - 1, unit); | |
482 | } | |
483 | ||
484 | static void new_line_csv(struct perf_stat_config *config, void *ctx) | |
485 | { | |
486 | struct outstate *os = ctx; | |
487 | int i; | |
488 | ||
6a80d794 | 489 | __new_line_std_csv(config, os); |
ed60738a | 490 | for (i = 0; i < os->csv_col_pad; i++) |
088519f3 JO |
491 | fputs(config->csv_sep, os->fh); |
492 | } | |
493 | ||
494 | static void print_metric_csv(struct perf_stat_config *config __maybe_unused, | |
495 | void *ctx, | |
37b77ae9 | 496 | enum metric_threshold_classify thresh __maybe_unused, |
088519f3 JO |
497 | const char *fmt, const char *unit, double val) |
498 | { | |
499 | struct outstate *os = ctx; | |
500 | FILE *out = os->fh; | |
501 | char buf[64], *vals, *ends; | |
502 | ||
503 | if (unit == NULL || fmt == NULL) { | |
504 | fprintf(out, "%s%s", config->csv_sep, config->csv_sep); | |
505 | return; | |
506 | } | |
507 | snprintf(buf, sizeof(buf), fmt, val); | |
32858480 | 508 | ends = vals = skip_spaces(buf); |
088519f3 JO |
509 | while (isdigit(*ends) || *ends == '.') |
510 | ends++; | |
511 | *ends = 0; | |
810826ac | 512 | fprintf(out, "%s%s%s%s", config->csv_sep, vals, config->csv_sep, skip_spaces(unit)); |
088519f3 JO |
513 | } |
514 | ||
df936cad CJ |
515 | static void print_metric_json(struct perf_stat_config *config __maybe_unused, |
516 | void *ctx, | |
f9825601 | 517 | enum metric_threshold_classify thresh, |
df936cad CJ |
518 | const char *fmt __maybe_unused, |
519 | const char *unit, double val) | |
520 | { | |
521 | struct outstate *os = ctx; | |
522 | FILE *out = os->fh; | |
523 | ||
f9825601 | 524 | if (unit) { |
96736489 | 525 | json_out(os, "\"metric-value\" : \"%f\", \"metric-unit\" : \"%s\"", val, unit); |
f9825601 | 526 | if (thresh != METRIC_THRESHOLD_UNKNOWN) { |
96736489 | 527 | json_out(os, "\"metric-threshold\" : \"%s\"", |
f9825601 IR |
528 | metric_threshold_classify__str(thresh)); |
529 | } | |
530 | } | |
df936cad CJ |
531 | if (!config->metric_only) |
532 | fprintf(out, "}"); | |
533 | } | |
534 | ||
535 | static void new_line_json(struct perf_stat_config *config, void *ctx) | |
536 | { | |
537 | struct outstate *os = ctx; | |
538 | ||
117195d9 | 539 | fputs("\n{", os->fh); |
96736489 | 540 | os->first = true; |
dd566687 JC |
541 | if (config->interval) |
542 | json_out(os, "%s", os->timestamp); | |
96736489 JC |
543 | |
544 | aggr_printout(config, os, os->evsel, os->id, os->aggr_nr); | |
df936cad CJ |
545 | } |
546 | ||
6a80d794 KL |
547 | static void print_metricgroup_header_json(struct perf_stat_config *config, |
548 | void *ctx, | |
549 | const char *metricgroup_name) | |
550 | { | |
551 | if (!metricgroup_name) | |
552 | return; | |
553 | ||
96736489 | 554 | json_out((struct outstate *) ctx, "\"metricgroup\" : \"%s\"}", metricgroup_name); |
6a80d794 KL |
555 | new_line_json(config, ctx); |
556 | } | |
557 | ||
558 | static void print_metricgroup_header_csv(struct perf_stat_config *config, | |
559 | void *ctx, | |
560 | const char *metricgroup_name) | |
561 | { | |
562 | struct outstate *os = ctx; | |
563 | int i; | |
564 | ||
565 | if (!metricgroup_name) { | |
566 | /* Leave space for running and enabling */ | |
ed60738a | 567 | for (i = 0; i < os->csv_col_pad - 2; i++) |
6a80d794 KL |
568 | fputs(config->csv_sep, os->fh); |
569 | return; | |
570 | } | |
571 | ||
ed60738a | 572 | for (i = 0; i < os->csv_col_pad; i++) |
6a80d794 KL |
573 | fputs(config->csv_sep, os->fh); |
574 | fprintf(config->output, "%s", metricgroup_name); | |
575 | new_line_csv(config, ctx); | |
576 | } | |
577 | ||
578 | static void print_metricgroup_header_std(struct perf_stat_config *config, | |
579 | void *ctx, | |
580 | const char *metricgroup_name) | |
581 | { | |
582 | struct outstate *os = ctx; | |
583 | int n; | |
584 | ||
585 | if (!metricgroup_name) { | |
586 | __new_line_std(os); | |
587 | return; | |
588 | } | |
589 | ||
590 | n = fprintf(config->output, " %*s", EVNAME_LEN, metricgroup_name); | |
591 | ||
592 | fprintf(config->output, "%*s", MGROUP_LEN - n - 1, ""); | |
593 | } | |
594 | ||
088519f3 JO |
595 | /* Filter out some columns that don't work well in metrics only mode */ |
596 | ||
597 | static bool valid_only_metric(const char *unit) | |
598 | { | |
599 | if (!unit) | |
600 | return false; | |
601 | if (strstr(unit, "/sec") || | |
088519f3 JO |
602 | strstr(unit, "CPUs utilized")) |
603 | return false; | |
604 | return true; | |
605 | } | |
606 | ||
32dcd021 | 607 | static const char *fixunit(char *buf, struct evsel *evsel, |
088519f3 JO |
608 | const char *unit) |
609 | { | |
610 | if (!strncmp(unit, "of all", 6)) { | |
8ab2e96d | 611 | snprintf(buf, 1024, "%s %s", evsel__name(evsel), |
088519f3 JO |
612 | unit); |
613 | return buf; | |
614 | } | |
615 | return unit; | |
616 | } | |
617 | ||
618 | static void print_metric_only(struct perf_stat_config *config, | |
37b77ae9 IR |
619 | void *ctx, enum metric_threshold_classify thresh, |
620 | const char *fmt, const char *unit, double val) | |
088519f3 JO |
621 | { |
622 | struct outstate *os = ctx; | |
623 | FILE *out = os->fh; | |
624 | char buf[1024], str[1024]; | |
625 | unsigned mlen = config->metric_only_len; | |
37b77ae9 | 626 | const char *color = metric_threshold_classify__color(thresh); |
088519f3 JO |
627 | |
628 | if (!valid_only_metric(unit)) | |
629 | return; | |
630 | unit = fixunit(buf, os->evsel, unit); | |
631 | if (mlen < strlen(unit)) | |
632 | mlen = strlen(unit) + 1; | |
633 | ||
634 | if (color) | |
635 | mlen += strlen(color) + sizeof(PERF_COLOR_RESET) - 1; | |
636 | ||
2543947c | 637 | color_snprintf(str, sizeof(str), color ?: "", fmt ?: "", val); |
088519f3 | 638 | fprintf(out, "%*s ", mlen, str); |
ab6baaae | 639 | os->first = false; |
088519f3 JO |
640 | } |
641 | ||
642 | static void print_metric_only_csv(struct perf_stat_config *config __maybe_unused, | |
37b77ae9 IR |
643 | void *ctx, |
644 | enum metric_threshold_classify thresh __maybe_unused, | |
088519f3 JO |
645 | const char *fmt, |
646 | const char *unit, double val) | |
647 | { | |
648 | struct outstate *os = ctx; | |
649 | FILE *out = os->fh; | |
650 | char buf[64], *vals, *ends; | |
651 | char tbuf[1024]; | |
652 | ||
653 | if (!valid_only_metric(unit)) | |
654 | return; | |
655 | unit = fixunit(tbuf, os->evsel, unit); | |
58a8d2ed | 656 | snprintf(buf, sizeof(buf), fmt ?: "", val); |
32858480 | 657 | ends = vals = skip_spaces(buf); |
088519f3 JO |
658 | while (isdigit(*ends) || *ends == '.') |
659 | ends++; | |
660 | *ends = 0; | |
661 | fprintf(out, "%s%s", vals, config->csv_sep); | |
ab6baaae | 662 | os->first = false; |
088519f3 JO |
663 | } |
664 | ||
df936cad | 665 | static void print_metric_only_json(struct perf_stat_config *config __maybe_unused, |
37b77ae9 IR |
666 | void *ctx, |
667 | enum metric_threshold_classify thresh __maybe_unused, | |
df936cad CJ |
668 | const char *fmt, |
669 | const char *unit, double val) | |
670 | { | |
671 | struct outstate *os = ctx; | |
1133e7f7 | 672 | char buf[64], *ends; |
df936cad | 673 | char tbuf[1024]; |
1133e7f7 | 674 | const char *vals; |
df936cad CJ |
675 | |
676 | if (!valid_only_metric(unit)) | |
677 | return; | |
678 | unit = fixunit(tbuf, os->evsel, unit); | |
1133e7f7 IR |
679 | if (!unit[0]) |
680 | return; | |
58a8d2ed | 681 | snprintf(buf, sizeof(buf), fmt ?: "", val); |
1133e7f7 | 682 | vals = ends = skip_spaces(buf); |
df936cad CJ |
683 | while (isdigit(*ends) || *ends == '.') |
684 | ends++; | |
685 | *ends = 0; | |
1133e7f7 IR |
686 | if (!vals[0]) |
687 | vals = "none"; | |
96736489 | 688 | json_out(os, "\"%s\" : \"%s\"", unit, vals); |
df936cad CJ |
689 | } |
690 | ||
088519f3 | 691 | static void print_metric_header(struct perf_stat_config *config, |
37b77ae9 IR |
692 | void *ctx, |
693 | enum metric_threshold_classify thresh __maybe_unused, | |
088519f3 JO |
694 | const char *fmt __maybe_unused, |
695 | const char *unit, double val __maybe_unused) | |
696 | { | |
697 | struct outstate *os = ctx; | |
698 | char tbuf[1024]; | |
699 | ||
f07952b1 AA |
700 | /* In case of iostat, print metric header for first root port only */ |
701 | if (config->iostat_run && | |
702 | os->evsel->priv != os->evsel->evlist->selected->priv) | |
703 | return; | |
704 | ||
67f8b7eb NK |
705 | if (os->evsel->cgrp != os->cgrp) |
706 | return; | |
707 | ||
fdc7d608 | 708 | if (!valid_only_metric(unit)) |
088519f3 JO |
709 | return; |
710 | unit = fixunit(tbuf, os->evsel, unit); | |
df936cad CJ |
711 | |
712 | if (config->json_output) | |
ab6baaae | 713 | return; |
df936cad | 714 | else if (config->csv_output) |
088519f3 JO |
715 | fprintf(os->fh, "%s%s", unit, config->csv_sep); |
716 | else | |
717 | fprintf(os->fh, "%*s ", config->metric_only_len, unit); | |
718 | } | |
719 | ||
c2019f84 | 720 | static void print_counter_value_std(struct perf_stat_config *config, |
d6aeb861 | 721 | struct evsel *evsel, double avg, bool ok) |
088519f3 JO |
722 | { |
723 | FILE *output = config->output; | |
724 | double sc = evsel->scale; | |
725 | const char *fmt; | |
d6aeb861 | 726 | const char *bad_count = evsel->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED; |
088519f3 | 727 | |
c2019f84 | 728 | if (config->big_num) |
33c4ed47 | 729 | fmt = floor(sc) != sc ? "%'*.2f " : "%'*.0f "; |
c2019f84 | 730 | else |
33c4ed47 | 731 | fmt = floor(sc) != sc ? "%*.2f " : "%*.0f "; |
088519f3 | 732 | |
d6aeb861 | 733 | if (ok) |
33c4ed47 | 734 | fprintf(output, fmt, COUNTS_LEN, avg); |
d6aeb861 | 735 | else |
33c4ed47 | 736 | fprintf(output, "%*s ", COUNTS_LEN, bad_count); |
088519f3 | 737 | |
c2019f84 NK |
738 | if (evsel->unit) |
739 | fprintf(output, "%-*s ", config->unit_width, evsel->unit); | |
088519f3 | 740 | |
33c4ed47 | 741 | fprintf(output, "%-*s", EVNAME_LEN, evsel__name(evsel)); |
c2019f84 NK |
742 | } |
743 | ||
744 | static void print_counter_value_csv(struct perf_stat_config *config, | |
d6aeb861 | 745 | struct evsel *evsel, double avg, bool ok) |
c2019f84 NK |
746 | { |
747 | FILE *output = config->output; | |
748 | double sc = evsel->scale; | |
749 | const char *sep = config->csv_sep; | |
750 | const char *fmt = floor(sc) != sc ? "%.2f%s" : "%.0f%s"; | |
d6aeb861 | 751 | const char *bad_count = evsel->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED; |
c2019f84 | 752 | |
d6aeb861 NK |
753 | if (ok) |
754 | fprintf(output, fmt, avg, sep); | |
755 | else | |
756 | fprintf(output, "%s%s", bad_count, sep); | |
088519f3 | 757 | |
c2019f84 NK |
758 | if (evsel->unit) |
759 | fprintf(output, "%s%s", evsel->unit, sep); | |
760 | ||
761 | fprintf(output, "%s", evsel__name(evsel)); | |
762 | } | |
763 | ||
96736489 | 764 | static void print_counter_value_json(struct outstate *os, |
d6aeb861 | 765 | struct evsel *evsel, double avg, bool ok) |
c2019f84 | 766 | { |
d6aeb861 | 767 | const char *bad_count = evsel->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED; |
c2019f84 | 768 | |
d6aeb861 | 769 | if (ok) |
96736489 | 770 | json_out(os, "\"counter-value\" : \"%f\"", avg); |
d6aeb861 | 771 | else |
96736489 | 772 | json_out(os, "\"counter-value\" : \"%s\"", bad_count); |
c2019f84 NK |
773 | |
774 | if (evsel->unit) | |
96736489 | 775 | json_out(os, "\"unit\" : \"%s\"", evsel->unit); |
c2019f84 | 776 | |
96736489 | 777 | json_out(os, "\"event\" : \"%s\"", evsel__name(evsel)); |
c2019f84 NK |
778 | } |
779 | ||
96736489 | 780 | static void print_counter_value(struct perf_stat_config *config, struct outstate *os, |
d6aeb861 | 781 | struct evsel *evsel, double avg, bool ok) |
c2019f84 | 782 | { |
df936cad | 783 | if (config->json_output) |
96736489 | 784 | print_counter_value_json(os, evsel, avg, ok); |
c2019f84 | 785 | else if (config->csv_output) |
d6aeb861 | 786 | print_counter_value_csv(config, evsel, avg, ok); |
df936cad | 787 | else |
d6aeb861 | 788 | print_counter_value_std(config, evsel, avg, ok); |
c2019f84 | 789 | } |
088519f3 | 790 | |
c2019f84 | 791 | static void abs_printout(struct perf_stat_config *config, |
96736489 | 792 | struct outstate *os, |
8945bef3 | 793 | struct aggr_cpu_id id, int aggr_nr, |
d6aeb861 | 794 | struct evsel *evsel, double avg, bool ok) |
c2019f84 | 795 | { |
96736489 JC |
796 | aggr_printout(config, os, evsel, id, aggr_nr); |
797 | print_counter_value(config, os, evsel, avg, ok); | |
798 | print_cgroup(config, os, evsel->cgrp); | |
088519f3 JO |
799 | } |
800 | ||
4b531377 | 801 | static bool evlist__has_hybrid_pmus(struct evlist *evlist) |
b167b530 IR |
802 | { |
803 | struct evsel *evsel; | |
4b531377 | 804 | struct perf_pmu *last_core_pmu = NULL; |
b167b530 | 805 | |
94f9eb95 | 806 | if (perf_pmus__num_core_pmus() == 1) |
3d88055f IR |
807 | return false; |
808 | ||
b167b530 | 809 | evlist__for_each_entry(evlist, evsel) { |
4b531377 IR |
810 | if (evsel->core.is_pmu_core) { |
811 | struct perf_pmu *pmu = evsel__find_pmu(evsel); | |
812 | ||
813 | if (pmu == last_core_pmu) | |
814 | continue; | |
815 | ||
816 | if (last_core_pmu == NULL) { | |
817 | last_core_pmu = pmu; | |
818 | continue; | |
819 | } | |
820 | /* A distinct core PMU. */ | |
b167b530 | 821 | return true; |
4b531377 | 822 | } |
b167b530 IR |
823 | } |
824 | ||
825 | return false; | |
826 | } | |
827 | ||
e7f4da31 | 828 | static void printout(struct perf_stat_config *config, struct outstate *os, |
8945bef3 | 829 | double uval, u64 run, u64 ena, double noise, int aggr_idx) |
088519f3 JO |
830 | { |
831 | struct perf_stat_output_ctx out; | |
df936cad | 832 | print_metric_t pm; |
088519f3 | 833 | new_line_t nl; |
6a80d794 | 834 | print_metricgroup_header_t pmh; |
df46a3c9 | 835 | bool ok = true; |
e7f4da31 | 836 | struct evsel *counter = os->evsel; |
088519f3 | 837 | |
df936cad | 838 | if (config->csv_output) { |
df936cad | 839 | pm = config->metric_only ? print_metric_only_csv : print_metric_csv; |
d226f434 | 840 | nl = config->metric_only ? NULL : new_line_csv; |
6a80d794 | 841 | pmh = print_metricgroup_header_csv; |
ed60738a | 842 | os->csv_col_pad = 4 + (counter->cgrp ? 1 : 0); |
df936cad CJ |
843 | } else if (config->json_output) { |
844 | pm = config->metric_only ? print_metric_only_json : print_metric_json; | |
d226f434 | 845 | nl = config->metric_only ? NULL : new_line_json; |
6a80d794 | 846 | pmh = print_metricgroup_header_json; |
df936cad CJ |
847 | } else { |
848 | pm = config->metric_only ? print_metric_only : print_metric_std; | |
d226f434 | 849 | nl = config->metric_only ? NULL : new_line_std; |
6a80d794 | 850 | pmh = print_metricgroup_header_std; |
088519f3 | 851 | } |
0bdad978 | 852 | |
088519f3 JO |
853 | if (run == 0 || ena == 0 || counter->counts->scaled == -1) { |
854 | if (config->metric_only) { | |
9f1df755 JC |
855 | pm(config, os, METRIC_THRESHOLD_UNKNOWN, /*format=*/NULL, |
856 | /*unit=*/NULL, /*val=*/0); | |
088519f3 JO |
857 | return; |
858 | } | |
d6aeb861 | 859 | |
df46a3c9 | 860 | ok = false; |
088519f3 JO |
861 | |
862 | if (counter->supported) { | |
4b531377 | 863 | if (!evlist__has_hybrid_pmus(counter->evlist)) { |
493be70a | 864 | config->print_free_counters_hint = 1; |
493be70a | 865 | } |
088519f3 | 866 | } |
088519f3 JO |
867 | } |
868 | ||
088519f3 JO |
869 | out.print_metric = pm; |
870 | out.new_line = nl; | |
6a80d794 | 871 | out.print_metricgroup_header = pmh; |
e7f4da31 | 872 | out.ctx = os; |
088519f3 JO |
873 | out.force_header = false; |
874 | ||
6a80d794 | 875 | if (!config->metric_only && !counter->default_metricgroup) { |
96736489 | 876 | abs_printout(config, os, os->id, os->aggr_nr, counter, uval, ok); |
df46a3c9 | 877 | |
96736489 JC |
878 | print_noise(config, os, counter, noise, /*before_metric=*/true); |
879 | print_running(config, os, run, ena, /*before_metric=*/true); | |
df46a3c9 NK |
880 | } |
881 | ||
882 | if (ok) { | |
6a80d794 KL |
883 | if (!config->metric_only && counter->default_metricgroup) { |
884 | void *from = NULL; | |
885 | ||
96736489 | 886 | aggr_printout(config, os, os->evsel, os->id, os->aggr_nr); |
6a80d794 KL |
887 | /* Print out all the metricgroup with the same metric event. */ |
888 | do { | |
889 | int num = 0; | |
890 | ||
891 | /* Print out the new line for the next new metricgroup. */ | |
892 | if (from) { | |
893 | if (config->json_output) | |
894 | new_line_json(config, (void *)os); | |
895 | else | |
896 | __new_line_std_csv(config, os); | |
897 | } | |
898 | ||
96736489 JC |
899 | print_noise(config, os, counter, noise, /*before_metric=*/true); |
900 | print_running(config, os, run, ena, /*before_metric=*/true); | |
6a80d794 KL |
901 | from = perf_stat__print_shadow_stats_metricgroup(config, counter, aggr_idx, |
902 | &num, from, &out, | |
903 | &config->metric_events); | |
904 | } while (from != NULL); | |
905 | } else | |
906 | perf_stat__print_shadow_stats(config, counter, uval, aggr_idx, | |
907 | &out, &config->metric_events); | |
df46a3c9 | 908 | } else { |
9f1df755 | 909 | pm(config, os, METRIC_THRESHOLD_UNKNOWN, /*format=*/NULL, /*unit=*/NULL, /*val=*/0); |
088519f3 JO |
910 | } |
911 | ||
df46a3c9 | 912 | if (!config->metric_only) { |
96736489 JC |
913 | print_noise(config, os, counter, noise, /*before_metric=*/false); |
914 | print_running(config, os, run, ena, /*before_metric=*/false); | |
088519f3 JO |
915 | } |
916 | } | |
917 | ||
dd15480a NK |
918 | /** |
919 | * should_skip_zero_count() - Check if the event should print 0 values. | |
920 | * @config: The perf stat configuration (including aggregation mode). | |
921 | * @counter: The evsel with its associated cpumap. | |
922 | * @id: The aggregation id that is being queried. | |
923 | * | |
924 | * Due to mismatch between the event cpumap or thread-map and the | |
925 | * aggregation mode, sometimes it'd iterate the counter with the map | |
926 | * which does not contain any values. | |
927 | * | |
928 | * For example, uncore events have dedicated CPUs to manage them, | |
929 | * result for other CPUs should be zero and skipped. | |
930 | * | |
931 | * Return: %true if the value should NOT be printed, %false if the value | |
932 | * needs to be printed like "<not counted>" or "<not supported>". | |
933 | */ | |
934 | static bool should_skip_zero_counter(struct perf_stat_config *config, | |
935 | struct evsel *counter, | |
936 | const struct aggr_cpu_id *id) | |
937 | { | |
938 | struct perf_cpu cpu; | |
939 | int idx; | |
940 | ||
d38461e9 IR |
941 | /* |
942 | * Skip unsupported default events when not verbose. (default events | |
943 | * are all marked 'skippable'). | |
944 | */ | |
945 | if (verbose == 0 && counter->skippable && !counter->supported) | |
946 | return true; | |
947 | ||
dd15480a NK |
948 | /* |
949 | * Skip value 0 when enabling --per-thread globally, | |
950 | * otherwise it will have too many 0 output. | |
951 | */ | |
952 | if (config->aggr_mode == AGGR_THREAD && config->system_wide) | |
953 | return true; | |
487ae3b4 | 954 | |
240505b2 | 955 | /* |
f60c3f44 IR |
956 | * In per-thread mode the aggr_map and aggr_get_id functions may be |
957 | * NULL, assume all 0 values should be output in that case. | |
958 | */ | |
959 | if (!config->aggr_map || !config->aggr_get_id) | |
960 | return false; | |
961 | ||
962 | /* | |
963 | * Tool events may be gathered on all logical CPUs, for example | |
964 | * system_time, but for many the first index is the only one used, for | |
965 | * example num_cores. Don't skip for the first index. | |
240505b2 | 966 | */ |
06905723 IR |
967 | if (evsel__is_tool(counter)) { |
968 | struct aggr_cpu_id own_id = | |
969 | config->aggr_get_id(config, (struct perf_cpu){ .cpu = 0 }); | |
970 | ||
971 | return !aggr_cpu_id__equal(id, &own_id); | |
972 | } | |
dd15480a | 973 | /* |
f60c3f44 IR |
974 | * Skip value 0 when the counter's cpumask doesn't match the given aggr |
975 | * id. | |
dd15480a | 976 | */ |
dd15480a | 977 | |
f60c3f44 | 978 | perf_cpu_map__for_each_cpu(cpu, idx, counter->core.cpus) { |
dd15480a NK |
979 | struct aggr_cpu_id own_id = config->aggr_get_id(config, cpu); |
980 | ||
981 | if (aggr_cpu_id__equal(id, &own_id)) | |
982 | return false; | |
983 | } | |
984 | return true; | |
985 | } | |
986 | ||
40480a81 | 987 | static void print_counter_aggrdata(struct perf_stat_config *config, |
8945bef3 | 988 | struct evsel *counter, int aggr_idx, |
5f334d88 | 989 | struct outstate *os) |
40480a81 | 990 | { |
40480a81 JY |
991 | FILE *output = config->output; |
992 | u64 ena, run, val; | |
40480a81 | 993 | double uval; |
91f85f98 | 994 | struct perf_stat_evsel *ps = counter->stats; |
8945bef3 IR |
995 | struct perf_stat_aggr *aggr = &ps->aggr[aggr_idx]; |
996 | struct aggr_cpu_id id = config->aggr_map->map[aggr_idx]; | |
91f85f98 | 997 | double avg = aggr->counts.val; |
ce551ec9 | 998 | bool metric_only = config->metric_only; |
5f334d88 NK |
999 | |
1000 | os->id = id; | |
8945bef3 | 1001 | os->aggr_nr = aggr->nr; |
5f334d88 | 1002 | os->evsel = counter; |
40480a81 | 1003 | |
b8976135 | 1004 | /* Skip already merged uncore/hybrid events */ |
137359b7 IR |
1005 | if (config->aggr_mode != AGGR_NONE) { |
1006 | if (evsel__is_hybrid(counter)) { | |
1007 | if (config->hybrid_merge && counter->first_wildcard_match != NULL) | |
1008 | return; | |
1009 | } else { | |
1010 | if (counter->first_wildcard_match != NULL) | |
1011 | return; | |
1012 | } | |
1013 | } | |
40480a81 | 1014 | |
91f85f98 NK |
1015 | val = aggr->counts.val; |
1016 | ena = aggr->counts.ena; | |
1017 | run = aggr->counts.run; | |
92637cc7 | 1018 | |
6a80d794 KL |
1019 | if (perf_stat__skip_metric_event(counter, &config->metric_events, ena, run)) |
1020 | return; | |
1021 | ||
dd15480a | 1022 | if (val == 0 && should_skip_zero_counter(config, counter, &id)) |
b8976135 NK |
1023 | return; |
1024 | ||
6d74ed36 | 1025 | if (!metric_only) { |
96736489 JC |
1026 | if (config->json_output) { |
1027 | os->first = true; | |
ab6baaae | 1028 | fputc('{', output); |
96736489 | 1029 | } |
dd566687 | 1030 | if (config->interval) { |
96736489 | 1031 | if (config->json_output) |
dd566687 | 1032 | json_out(os, "%s", os->timestamp); |
96736489 | 1033 | else |
dd566687 | 1034 | fprintf(output, "%s", os->timestamp); |
96736489 | 1035 | } else if (config->summary && config->csv_output && |
dd566687 | 1036 | !config->no_csv_summary) |
8e55ae24 | 1037 | fprintf(output, "%s%s", "summary", config->csv_sep); |
6d74ed36 | 1038 | } |
40480a81 JY |
1039 | |
1040 | uval = val * counter->scale; | |
7365f105 | 1041 | |
8945bef3 | 1042 | printout(config, os, uval, run, ena, avg, aggr_idx); |
91f85f98 | 1043 | |
40480a81 JY |
1044 | if (!metric_only) |
1045 | fputc('\n', output); | |
1046 | } | |
1047 | ||
78670dae NK |
1048 | static void print_metric_begin(struct perf_stat_config *config, |
1049 | struct evlist *evlist, | |
922ae948 | 1050 | struct outstate *os, int aggr_idx) |
78670dae NK |
1051 | { |
1052 | struct perf_stat_aggr *aggr; | |
1053 | struct aggr_cpu_id id; | |
1054 | struct evsel *evsel; | |
1055 | ||
ab6baaae | 1056 | os->first = true; |
78670dae NK |
1057 | if (!config->metric_only) |
1058 | return; | |
1059 | ||
ab6baaae NK |
1060 | if (config->json_output) |
1061 | fputc('{', config->output); | |
78670dae | 1062 | |
dd566687 | 1063 | if (config->interval) { |
96736489 | 1064 | if (config->json_output) |
dd566687 | 1065 | json_out(os, "%s", os->timestamp); |
96736489 | 1066 | else |
dd566687 | 1067 | fprintf(config->output, "%s", os->timestamp); |
96736489 | 1068 | } |
78670dae NK |
1069 | evsel = evlist__first(evlist); |
1070 | id = config->aggr_map->map[aggr_idx]; | |
1071 | aggr = &evsel->stats->aggr[aggr_idx]; | |
96736489 | 1072 | aggr_printout(config, os, evsel, id, aggr->nr); |
67f8b7eb | 1073 | |
96736489 | 1074 | print_cgroup(config, os, os->cgrp ? : evsel->cgrp); |
78670dae NK |
1075 | } |
1076 | ||
765d4e49 | 1077 | static void print_metric_end(struct perf_stat_config *config, struct outstate *os) |
78670dae | 1078 | { |
765d4e49 NK |
1079 | FILE *output = config->output; |
1080 | ||
78670dae NK |
1081 | if (!config->metric_only) |
1082 | return; | |
1083 | ||
765d4e49 NK |
1084 | if (config->json_output) { |
1085 | if (os->first) | |
1086 | fputs("\"metric-value\" : \"none\"", output); | |
1087 | fputc('}', output); | |
1088 | } | |
1089 | fputc('\n', output); | |
78670dae NK |
1090 | } |
1091 | ||
088519f3 | 1092 | static void print_aggr(struct perf_stat_config *config, |
63503dba | 1093 | struct evlist *evlist, |
5f334d88 | 1094 | struct outstate *os) |
088519f3 | 1095 | { |
32dcd021 | 1096 | struct evsel *counter; |
8945bef3 | 1097 | int aggr_idx; |
088519f3 | 1098 | |
c0c652fc | 1099 | if (!config->aggr_map || !config->aggr_get_id) |
088519f3 JO |
1100 | return; |
1101 | ||
088519f3 JO |
1102 | /* |
1103 | * With metric_only everything is on a single line. | |
1104 | * Without each counter has its own line. | |
1105 | */ | |
8945bef3 IR |
1106 | cpu_aggr_map__for_each_idx(aggr_idx, config->aggr_map) { |
1107 | print_metric_begin(config, evlist, os, aggr_idx); | |
088519f3 | 1108 | |
088519f3 | 1109 | evlist__for_each_entry(evlist, counter) { |
8945bef3 | 1110 | print_counter_aggrdata(config, counter, aggr_idx, os); |
088519f3 | 1111 | } |
765d4e49 | 1112 | print_metric_end(config, os); |
088519f3 JO |
1113 | } |
1114 | } | |
1115 | ||
4dd7ff4a NK |
1116 | static void print_aggr_cgroup(struct perf_stat_config *config, |
1117 | struct evlist *evlist, | |
5f334d88 | 1118 | struct outstate *os) |
4dd7ff4a | 1119 | { |
4dd7ff4a | 1120 | struct evsel *counter, *evsel; |
8945bef3 | 1121 | int aggr_idx; |
4dd7ff4a NK |
1122 | |
1123 | if (!config->aggr_map || !config->aggr_get_id) | |
1124 | return; | |
1125 | ||
1126 | evlist__for_each_entry(evlist, evsel) { | |
5f334d88 | 1127 | if (os->cgrp == evsel->cgrp) |
4dd7ff4a NK |
1128 | continue; |
1129 | ||
5f334d88 | 1130 | os->cgrp = evsel->cgrp; |
4dd7ff4a | 1131 | |
8945bef3 IR |
1132 | cpu_aggr_map__for_each_idx(aggr_idx, config->aggr_map) { |
1133 | print_metric_begin(config, evlist, os, aggr_idx); | |
4dd7ff4a NK |
1134 | |
1135 | evlist__for_each_entry(evlist, counter) { | |
5f334d88 | 1136 | if (counter->cgrp != os->cgrp) |
4dd7ff4a NK |
1137 | continue; |
1138 | ||
8945bef3 | 1139 | print_counter_aggrdata(config, counter, aggr_idx, os); |
4dd7ff4a | 1140 | } |
765d4e49 | 1141 | print_metric_end(config, os); |
4dd7ff4a NK |
1142 | } |
1143 | } | |
1144 | } | |
1145 | ||
088519f3 | 1146 | static void print_counter(struct perf_stat_config *config, |
5f334d88 | 1147 | struct evsel *counter, struct outstate *os) |
088519f3 | 1148 | { |
8945bef3 | 1149 | int aggr_idx; |
088519f3 | 1150 | |
91f85f98 NK |
1151 | /* AGGR_THREAD doesn't have config->aggr_get_id */ |
1152 | if (!config->aggr_map) | |
1153 | return; | |
088519f3 | 1154 | |
8945bef3 IR |
1155 | cpu_aggr_map__for_each_idx(aggr_idx, config->aggr_map) { |
1156 | print_counter_aggrdata(config, counter, aggr_idx, os); | |
088519f3 JO |
1157 | } |
1158 | } | |
1159 | ||
1160 | static void print_no_aggr_metric(struct perf_stat_config *config, | |
63503dba | 1161 | struct evlist *evlist, |
5f334d88 | 1162 | struct outstate *os) |
088519f3 | 1163 | { |
6d18804b IR |
1164 | int all_idx; |
1165 | struct perf_cpu cpu; | |
088519f3 | 1166 | |
0df6ade7 | 1167 | perf_cpu_map__for_each_cpu(cpu, all_idx, evlist->core.user_requested_cpus) { |
7365f105 | 1168 | struct evsel *counter; |
088519f3 JO |
1169 | bool first = true; |
1170 | ||
088519f3 | 1171 | evlist__for_each_entry(evlist, counter) { |
7365f105 IR |
1172 | u64 ena, run, val; |
1173 | double uval; | |
91f85f98 | 1174 | struct perf_stat_evsel *ps = counter->stats; |
bafd4e75 | 1175 | int aggr_idx = 0; |
7365f105 | 1176 | |
bafd4e75 | 1177 | if (!perf_cpu_map__has(evsel__cpus(counter), cpu)) |
7365f105 IR |
1178 | continue; |
1179 | ||
bafd4e75 IR |
1180 | cpu_aggr_map__for_each_idx(aggr_idx, config->aggr_map) { |
1181 | if (config->aggr_map->map[aggr_idx].cpu.cpu == cpu.cpu) | |
1182 | break; | |
1183 | } | |
1184 | ||
5f334d88 NK |
1185 | os->evsel = counter; |
1186 | os->id = aggr_cpu_id__cpu(cpu, /*data=*/NULL); | |
088519f3 | 1187 | if (first) { |
8945bef3 | 1188 | print_metric_begin(config, evlist, os, aggr_idx); |
088519f3 JO |
1189 | first = false; |
1190 | } | |
8945bef3 IR |
1191 | val = ps->aggr[aggr_idx].counts.val; |
1192 | ena = ps->aggr[aggr_idx].counts.ena; | |
1193 | run = ps->aggr[aggr_idx].counts.run; | |
088519f3 JO |
1194 | |
1195 | uval = val * counter->scale; | |
8945bef3 | 1196 | printout(config, os, uval, run, ena, 1.0, aggr_idx); |
088519f3 | 1197 | } |
570c44a0 | 1198 | if (!first) |
765d4e49 | 1199 | print_metric_end(config, os); |
088519f3 JO |
1200 | } |
1201 | } | |
1202 | ||
b2d9832e | 1203 | static void print_metric_headers_std(struct perf_stat_config *config, |
f123b2d8 | 1204 | bool no_indent) |
b2d9832e | 1205 | { |
f123b2d8 | 1206 | fputc(' ', config->output); |
33c4ed47 | 1207 | |
b2d9832e | 1208 | if (!no_indent) { |
33c4ed47 NK |
1209 | int len = aggr_header_lens[config->aggr_mode]; |
1210 | ||
67f8b7eb | 1211 | if (nr_cgroups || config->cgroup_list) |
33c4ed47 NK |
1212 | len += CGROUP_LEN + 1; |
1213 | ||
1214 | fprintf(config->output, "%*s", len, ""); | |
b2d9832e NK |
1215 | } |
1216 | } | |
1217 | ||
1218 | static void print_metric_headers_csv(struct perf_stat_config *config, | |
b2d9832e NK |
1219 | bool no_indent __maybe_unused) |
1220 | { | |
b195701e NK |
1221 | const char *p; |
1222 | ||
b2d9832e | 1223 | if (config->interval) |
b195701e NK |
1224 | fprintf(config->output, "time%s", config->csv_sep); |
1225 | if (config->iostat_run) | |
1226 | return; | |
1227 | ||
1228 | p = aggr_header_csv[config->aggr_mode]; | |
1229 | while (*p) { | |
1230 | if (*p == ',') | |
1231 | fputs(config->csv_sep, config->output); | |
1232 | else | |
1233 | fputc(*p, config->output); | |
1234 | p++; | |
1235 | } | |
b2d9832e NK |
1236 | } |
1237 | ||
ab6baaae | 1238 | static void print_metric_headers_json(struct perf_stat_config *config __maybe_unused, |
b2d9832e NK |
1239 | bool no_indent __maybe_unused) |
1240 | { | |
b2d9832e NK |
1241 | } |
1242 | ||
088519f3 | 1243 | static void print_metric_headers(struct perf_stat_config *config, |
f123b2d8 | 1244 | struct evlist *evlist, bool no_indent) |
088519f3 | 1245 | { |
32dcd021 | 1246 | struct evsel *counter; |
088519f3 JO |
1247 | struct outstate os = { |
1248 | .fh = config->output | |
1249 | }; | |
f4e55f88 NK |
1250 | struct perf_stat_output_ctx out = { |
1251 | .ctx = &os, | |
1252 | .print_metric = print_metric_header, | |
d226f434 | 1253 | .new_line = NULL, |
f4e55f88 NK |
1254 | .force_header = true, |
1255 | }; | |
088519f3 | 1256 | |
b2d9832e | 1257 | if (config->json_output) |
f123b2d8 | 1258 | print_metric_headers_json(config, no_indent); |
b2d9832e | 1259 | else if (config->csv_output) |
f123b2d8 | 1260 | print_metric_headers_csv(config, no_indent); |
b2d9832e | 1261 | else |
f123b2d8 | 1262 | print_metric_headers_std(config, no_indent); |
088519f3 | 1263 | |
f07952b1 AA |
1264 | if (config->iostat_run) |
1265 | iostat_print_header_prefix(config); | |
088519f3 | 1266 | |
67f8b7eb NK |
1267 | if (config->cgroup_list) |
1268 | os.cgrp = evlist__first(evlist)->cgrp; | |
1269 | ||
088519f3 JO |
1270 | /* Print metrics headers only */ |
1271 | evlist__for_each_entry(evlist, counter) { | |
26156393 YY |
1272 | if (!config->iostat_run && |
1273 | config->aggr_mode != AGGR_NONE && counter->metric_leader != counter) | |
193a9e30 IR |
1274 | continue; |
1275 | ||
088519f3 | 1276 | os.evsel = counter; |
f4e55f88 | 1277 | |
088519f3 JO |
1278 | perf_stat__print_shadow_stats(config, counter, 0, |
1279 | 0, | |
1280 | &out, | |
cc26ffaa | 1281 | &config->metric_events); |
088519f3 | 1282 | } |
ab6baaae NK |
1283 | |
1284 | if (!config->json_output) | |
1285 | fputc('\n', config->output); | |
088519f3 JO |
1286 | } |
1287 | ||
dd566687 JC |
1288 | static void prepare_timestamp(struct perf_stat_config *config, |
1289 | struct outstate *os, struct timespec *ts) | |
208cbcd2 NK |
1290 | { |
1291 | if (config->iostat_run) | |
1292 | return; | |
1293 | ||
ab6baaae | 1294 | if (config->json_output) |
dd566687 | 1295 | scnprintf(os->timestamp, sizeof(os->timestamp), "\"interval\" : %lu.%09lu", |
ab6baaae NK |
1296 | (unsigned long) ts->tv_sec, ts->tv_nsec); |
1297 | else if (config->csv_output) | |
dd566687 | 1298 | scnprintf(os->timestamp, sizeof(os->timestamp), "%lu.%09lu%s", |
a7ec1dd2 | 1299 | (unsigned long) ts->tv_sec, ts->tv_nsec, config->csv_sep); |
208cbcd2 | 1300 | else |
dd566687 | 1301 | scnprintf(os->timestamp, sizeof(os->timestamp), "%6lu.%09lu ", |
a7ec1dd2 | 1302 | (unsigned long) ts->tv_sec, ts->tv_nsec); |
208cbcd2 NK |
1303 | } |
1304 | ||
4c86b664 NK |
1305 | static void print_header_interval_std(struct perf_stat_config *config, |
1306 | struct target *_target __maybe_unused, | |
1307 | struct evlist *evlist, | |
1308 | int argc __maybe_unused, | |
1309 | const char **argv __maybe_unused) | |
088519f3 | 1310 | { |
088519f3 | 1311 | FILE *output = config->output; |
088519f3 | 1312 | |
4c86b664 NK |
1313 | switch (config->aggr_mode) { |
1314 | case AGGR_NODE: | |
1315 | case AGGR_SOCKET: | |
1316 | case AGGR_DIE: | |
cbc917a1 | 1317 | case AGGR_CLUSTER: |
995ed074 | 1318 | case AGGR_CACHE: |
4c86b664 NK |
1319 | case AGGR_CORE: |
1320 | fprintf(output, "#%*s %-*s cpus", | |
1321 | INTERVAL_LEN - 1, "time", | |
1322 | aggr_header_lens[config->aggr_mode], | |
1323 | aggr_header_std[config->aggr_mode]); | |
1324 | break; | |
1325 | case AGGR_NONE: | |
1326 | fprintf(output, "#%*s %-*s", | |
1327 | INTERVAL_LEN - 1, "time", | |
1328 | aggr_header_lens[config->aggr_mode], | |
1329 | aggr_header_std[config->aggr_mode]); | |
1330 | break; | |
1331 | case AGGR_THREAD: | |
1332 | fprintf(output, "#%*s %*s-%-*s", | |
1333 | INTERVAL_LEN - 1, "time", | |
1334 | COMM_LEN, "comm", PID_LEN, "pid"); | |
1335 | break; | |
1336 | case AGGR_GLOBAL: | |
1337 | default: | |
1338 | if (!config->iostat_run) | |
1339 | fprintf(output, "#%*s", | |
1340 | INTERVAL_LEN - 1, "time"); | |
1341 | case AGGR_UNSET: | |
1342 | case AGGR_MAX: | |
1343 | break; | |
1344 | } | |
088519f3 | 1345 | |
4c86b664 | 1346 | if (config->metric_only) |
f123b2d8 | 1347 | print_metric_headers(config, evlist, true); |
4c86b664 NK |
1348 | else |
1349 | fprintf(output, " %*s %*s events\n", | |
1350 | COUNTS_LEN, "counts", config->unit_width, "unit"); | |
1351 | } | |
33c4ed47 | 1352 | |
4c86b664 NK |
1353 | static void print_header_std(struct perf_stat_config *config, |
1354 | struct target *_target, struct evlist *evlist, | |
1355 | int argc, const char **argv) | |
1356 | { | |
1357 | FILE *output = config->output; | |
1358 | int i; | |
1359 | ||
1360 | fprintf(output, "\n"); | |
1361 | fprintf(output, " Performance counter stats for "); | |
1362 | if (_target->bpf_str) | |
1363 | fprintf(output, "\'BPF program(s) %s", _target->bpf_str); | |
1364 | else if (_target->system_wide) | |
1365 | fprintf(output, "\'system wide"); | |
1366 | else if (_target->cpu_list) | |
1367 | fprintf(output, "\'CPU(s) %s", _target->cpu_list); | |
1368 | else if (!target__has_task(_target)) { | |
1369 | fprintf(output, "\'%s", argv ? argv[0] : "pipe"); | |
1370 | for (i = 1; argv && (i < argc); i++) | |
1371 | fprintf(output, " %s", argv[i]); | |
1372 | } else if (_target->pid) | |
1373 | fprintf(output, "process id \'%s", _target->pid); | |
1374 | else | |
1375 | fprintf(output, "thread id \'%s", _target->tid); | |
088519f3 | 1376 | |
4c86b664 NK |
1377 | fprintf(output, "\'"); |
1378 | if (config->run_count > 1) | |
1379 | fprintf(output, " (%d runs)", config->run_count); | |
1380 | fprintf(output, ":\n\n"); | |
1381 | ||
1382 | if (config->metric_only) | |
f123b2d8 | 1383 | print_metric_headers(config, evlist, false); |
4c86b664 NK |
1384 | } |
1385 | ||
1386 | static void print_header_csv(struct perf_stat_config *config, | |
1387 | struct target *_target __maybe_unused, | |
1388 | struct evlist *evlist, | |
1389 | int argc __maybe_unused, | |
1390 | const char **argv __maybe_unused) | |
1391 | { | |
1392 | if (config->metric_only) | |
f123b2d8 | 1393 | print_metric_headers(config, evlist, true); |
4c86b664 NK |
1394 | } |
1395 | static void print_header_json(struct perf_stat_config *config, | |
1396 | struct target *_target __maybe_unused, | |
1397 | struct evlist *evlist, | |
1398 | int argc __maybe_unused, | |
1399 | const char **argv __maybe_unused) | |
1400 | { | |
1401 | if (config->metric_only) | |
f123b2d8 | 1402 | print_metric_headers(config, evlist, true); |
088519f3 JO |
1403 | } |
1404 | ||
1405 | static void print_header(struct perf_stat_config *config, | |
1406 | struct target *_target, | |
4c86b664 | 1407 | struct evlist *evlist, |
088519f3 JO |
1408 | int argc, const char **argv) |
1409 | { | |
4c86b664 | 1410 | static int num_print_iv; |
088519f3 JO |
1411 | |
1412 | fflush(stdout); | |
1413 | ||
4c86b664 NK |
1414 | if (config->interval_clear) |
1415 | puts(CONSOLE_CLEAR); | |
088519f3 | 1416 | |
4c86b664 NK |
1417 | if (num_print_iv == 0 || config->interval_clear) { |
1418 | if (config->json_output) | |
1419 | print_header_json(config, _target, evlist, argc, argv); | |
1420 | else if (config->csv_output) | |
1421 | print_header_csv(config, _target, evlist, argc, argv); | |
1422 | else if (config->interval) | |
1423 | print_header_interval_std(config, _target, evlist, argc, argv); | |
1424 | else | |
1425 | print_header_std(config, _target, evlist, argc, argv); | |
088519f3 | 1426 | } |
4c86b664 NK |
1427 | |
1428 | if (num_print_iv++ == 25) | |
1429 | num_print_iv = 0; | |
088519f3 JO |
1430 | } |
1431 | ||
1432 | static int get_precision(double num) | |
1433 | { | |
1434 | if (num > 1) | |
1435 | return 0; | |
1436 | ||
1437 | return lround(ceil(-log10(num))); | |
1438 | } | |
1439 | ||
1440 | static void print_table(struct perf_stat_config *config, | |
1441 | FILE *output, int precision, double avg) | |
1442 | { | |
1443 | char tmp[64]; | |
1444 | int idx, indent = 0; | |
1445 | ||
1446 | scnprintf(tmp, 64, " %17.*f", precision, avg); | |
1447 | while (tmp[indent] == ' ') | |
1448 | indent++; | |
1449 | ||
1450 | fprintf(output, "%*s# Table of individual measurements:\n", indent, ""); | |
1451 | ||
1452 | for (idx = 0; idx < config->run_count; idx++) { | |
1453 | double run = (double) config->walltime_run[idx] / NSEC_PER_SEC; | |
1454 | int h, n = 1 + abs((int) (100.0 * (run - avg)/run) / 5); | |
1455 | ||
1456 | fprintf(output, " %17.*f (%+.*f) ", | |
1457 | precision, run, precision, run - avg); | |
1458 | ||
1459 | for (h = 0; h < n; h++) | |
1460 | fprintf(output, "#"); | |
1461 | ||
1462 | fprintf(output, "\n"); | |
1463 | } | |
1464 | ||
1465 | fprintf(output, "\n%*s# Final result:\n", indent, ""); | |
1466 | } | |
1467 | ||
1468 | static double timeval2double(struct timeval *t) | |
1469 | { | |
1470 | return t->tv_sec + (double) t->tv_usec/USEC_PER_SEC; | |
1471 | } | |
1472 | ||
1473 | static void print_footer(struct perf_stat_config *config) | |
1474 | { | |
1475 | double avg = avg_stats(config->walltime_nsecs_stats) / NSEC_PER_SEC; | |
1476 | FILE *output = config->output; | |
088519f3 | 1477 | |
453279d5 NK |
1478 | if (config->interval || config->csv_output || config->json_output) |
1479 | return; | |
1480 | ||
088519f3 JO |
1481 | if (!config->null_run) |
1482 | fprintf(output, "\n"); | |
1483 | ||
1484 | if (config->run_count == 1) { | |
1485 | fprintf(output, " %17.9f seconds time elapsed", avg); | |
1486 | ||
1487 | if (config->ru_display) { | |
1488 | double ru_utime = timeval2double(&config->ru_data.ru_utime); | |
1489 | double ru_stime = timeval2double(&config->ru_data.ru_stime); | |
1490 | ||
1491 | fprintf(output, "\n\n"); | |
1492 | fprintf(output, " %17.9f seconds user\n", ru_utime); | |
1493 | fprintf(output, " %17.9f seconds sys\n", ru_stime); | |
1494 | } | |
1495 | } else { | |
1496 | double sd = stddev_stats(config->walltime_nsecs_stats) / NSEC_PER_SEC; | |
1497 | /* | |
1498 | * Display at most 2 more significant | |
1499 | * digits than the stddev inaccuracy. | |
1500 | */ | |
1501 | int precision = get_precision(sd) + 2; | |
1502 | ||
1503 | if (config->walltime_run_table) | |
1504 | print_table(config, output, precision, avg); | |
1505 | ||
1506 | fprintf(output, " %17.*f +- %.*f seconds time elapsed", | |
1507 | precision, avg, precision, sd); | |
1508 | ||
96736489 | 1509 | print_noise_pct(config, NULL, sd, avg, /*before_metric=*/false); |
088519f3 JO |
1510 | } |
1511 | fprintf(output, "\n\n"); | |
1512 | ||
2a14c1bf | 1513 | if (config->print_free_counters_hint && sysctl__nmi_watchdog_enabled()) |
088519f3 JO |
1514 | fprintf(output, |
1515 | "Some events weren't counted. Try disabling the NMI watchdog:\n" | |
1516 | " echo 0 > /proc/sys/kernel/nmi_watchdog\n" | |
1517 | " perf stat ...\n" | |
1518 | " echo 1 > /proc/sys/kernel/nmi_watchdog\n"); | |
088519f3 JO |
1519 | } |
1520 | ||
4fc4d8df | 1521 | static void print_percore(struct perf_stat_config *config, |
5f334d88 | 1522 | struct evsel *counter, struct outstate *os) |
4fc4d8df JY |
1523 | { |
1524 | bool metric_only = config->metric_only; | |
1525 | FILE *output = config->output; | |
cec94d69 | 1526 | struct cpu_aggr_map *core_map; |
8945bef3 | 1527 | int aggr_idx, core_map_len = 0; |
4fc4d8df | 1528 | |
c0c652fc | 1529 | if (!config->aggr_map || !config->aggr_get_id) |
4fc4d8df JY |
1530 | return; |
1531 | ||
1af62ce6 | 1532 | if (config->percore_show_thread) |
5f334d88 | 1533 | return print_counter(config, counter, os); |
1af62ce6 | 1534 | |
8945bef3 IR |
1535 | /* |
1536 | * core_map will hold the aggr_cpu_id for the cores that have been | |
1537 | * printed so that each core is printed just once. | |
1538 | */ | |
cec94d69 NK |
1539 | core_map = cpu_aggr_map__empty_new(config->aggr_map->nr); |
1540 | if (core_map == NULL) { | |
1541 | fprintf(output, "Cannot allocate per-core aggr map for display\n"); | |
1542 | return; | |
1543 | } | |
1544 | ||
8945bef3 IR |
1545 | cpu_aggr_map__for_each_idx(aggr_idx, config->aggr_map) { |
1546 | struct perf_cpu curr_cpu = config->aggr_map->map[aggr_idx].cpu; | |
cec94d69 NK |
1547 | struct aggr_cpu_id core_id = aggr_cpu_id__core(curr_cpu, NULL); |
1548 | bool found = false; | |
1549 | ||
8945bef3 | 1550 | for (int i = 0; i < core_map_len; i++) { |
cec94d69 NK |
1551 | if (aggr_cpu_id__equal(&core_map->map[i], &core_id)) { |
1552 | found = true; | |
1553 | break; | |
1554 | } | |
1555 | } | |
1556 | if (found) | |
1557 | continue; | |
1558 | ||
8945bef3 | 1559 | print_counter_aggrdata(config, counter, aggr_idx, os); |
cec94d69 | 1560 | |
8945bef3 | 1561 | core_map->map[core_map_len++] = core_id; |
4fc4d8df | 1562 | } |
cec94d69 | 1563 | free(core_map); |
4fc4d8df JY |
1564 | |
1565 | if (metric_only) | |
1566 | fputc('\n', output); | |
1567 | } | |
1568 | ||
67f8b7eb | 1569 | static void print_cgroup_counter(struct perf_stat_config *config, struct evlist *evlist, |
5f334d88 | 1570 | struct outstate *os) |
67f8b7eb | 1571 | { |
67f8b7eb NK |
1572 | struct evsel *counter; |
1573 | ||
1574 | evlist__for_each_entry(evlist, counter) { | |
5f334d88 NK |
1575 | if (os->cgrp != counter->cgrp) { |
1576 | if (os->cgrp != NULL) | |
765d4e49 | 1577 | print_metric_end(config, os); |
67f8b7eb | 1578 | |
5f334d88 NK |
1579 | os->cgrp = counter->cgrp; |
1580 | print_metric_begin(config, evlist, os, /*aggr_idx=*/0); | |
67f8b7eb NK |
1581 | } |
1582 | ||
5f334d88 | 1583 | print_counter(config, counter, os); |
67f8b7eb | 1584 | } |
5f334d88 | 1585 | if (os->cgrp) |
765d4e49 | 1586 | print_metric_end(config, os); |
67f8b7eb NK |
1587 | } |
1588 | ||
71273724 | 1589 | void evlist__print_counters(struct evlist *evlist, struct perf_stat_config *config, |
5f334d88 NK |
1590 | struct target *_target, struct timespec *ts, |
1591 | int argc, const char **argv) | |
088519f3 JO |
1592 | { |
1593 | bool metric_only = config->metric_only; | |
32dcd021 | 1594 | struct evsel *counter; |
92ccf7f1 NK |
1595 | struct outstate os = { |
1596 | .fh = config->output, | |
ab6baaae | 1597 | .first = true, |
92ccf7f1 | 1598 | }; |
088519f3 | 1599 | |
7d45f402 | 1600 | evlist__uniquify_evsel_names(evlist, config); |
057f8bfc | 1601 | |
f07952b1 AA |
1602 | if (config->iostat_run) |
1603 | evlist->selected = evlist__first(evlist); | |
1604 | ||
dd566687 JC |
1605 | if (config->interval) |
1606 | prepare_timestamp(config, &os, ts); | |
088519f3 | 1607 | |
4c86b664 | 1608 | print_header(config, _target, evlist, argc, argv); |
088519f3 JO |
1609 | |
1610 | switch (config->aggr_mode) { | |
1611 | case AGGR_CORE: | |
995ed074 | 1612 | case AGGR_CACHE: |
cbc917a1 | 1613 | case AGGR_CLUSTER: |
db5742b6 | 1614 | case AGGR_DIE: |
088519f3 | 1615 | case AGGR_SOCKET: |
86895b48 | 1616 | case AGGR_NODE: |
4dd7ff4a | 1617 | if (config->cgroup_list) |
5f334d88 | 1618 | print_aggr_cgroup(config, evlist, &os); |
4dd7ff4a | 1619 | else |
5f334d88 | 1620 | print_aggr(config, evlist, &os); |
088519f3 JO |
1621 | break; |
1622 | case AGGR_THREAD: | |
088519f3 | 1623 | case AGGR_GLOBAL: |
67f8b7eb | 1624 | if (config->iostat_run) { |
dd566687 | 1625 | iostat_print_counters(evlist, config, ts, os.timestamp, |
5f334d88 | 1626 | (iostat_print_counter_t)print_counter, &os); |
67f8b7eb | 1627 | } else if (config->cgroup_list) { |
5f334d88 | 1628 | print_cgroup_counter(config, evlist, &os); |
67f8b7eb | 1629 | } else { |
922ae948 | 1630 | print_metric_begin(config, evlist, &os, /*aggr_idx=*/0); |
f07952b1 | 1631 | evlist__for_each_entry(evlist, counter) { |
5f334d88 | 1632 | print_counter(config, counter, &os); |
f07952b1 | 1633 | } |
765d4e49 | 1634 | print_metric_end(config, &os); |
088519f3 | 1635 | } |
088519f3 JO |
1636 | break; |
1637 | case AGGR_NONE: | |
1638 | if (metric_only) | |
5f334d88 | 1639 | print_no_aggr_metric(config, evlist, &os); |
088519f3 JO |
1640 | else { |
1641 | evlist__for_each_entry(evlist, counter) { | |
4fc4d8df | 1642 | if (counter->percore) |
5f334d88 | 1643 | print_percore(config, counter, &os); |
4fc4d8df | 1644 | else |
5f334d88 | 1645 | print_counter(config, counter, &os); |
088519f3 JO |
1646 | } |
1647 | } | |
1648 | break; | |
df936cad | 1649 | case AGGR_MAX: |
088519f3 JO |
1650 | case AGGR_UNSET: |
1651 | default: | |
1652 | break; | |
1653 | } | |
1654 | ||
453279d5 | 1655 | print_footer(config); |
088519f3 JO |
1656 | |
1657 | fflush(config->output); | |
1658 | } |