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