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