xref: /linux/tools/perf/util/stat-display.c (revision 44f57d78)
1 #include <stdio.h>
2 #include <inttypes.h>
3 #include <linux/time64.h>
4 #include <math.h>
5 #include "color.h"
6 #include "evlist.h"
7 #include "evsel.h"
8 #include "stat.h"
9 #include "top.h"
10 #include "thread_map.h"
11 #include "cpumap.h"
12 #include "string2.h"
13 #include "sane_ctype.h"
14 #include "cgroup.h"
15 #include <math.h>
16 #include <api/fs/fs.h>
17 
18 #define CNTR_NOT_SUPPORTED	"<not supported>"
19 #define CNTR_NOT_COUNTED	"<not counted>"
20 
21 static void print_running(struct perf_stat_config *config,
22 			  u64 run, u64 ena)
23 {
24 	if (config->csv_output) {
25 		fprintf(config->output, "%s%" PRIu64 "%s%.2f",
26 					config->csv_sep,
27 					run,
28 					config->csv_sep,
29 					ena ? 100.0 * run / ena : 100.0);
30 	} else if (run != ena) {
31 		fprintf(config->output, "  (%.2f%%)", 100.0 * run / ena);
32 	}
33 }
34 
35 static void print_noise_pct(struct perf_stat_config *config,
36 			    double total, double avg)
37 {
38 	double pct = rel_stddev_stats(total, avg);
39 
40 	if (config->csv_output)
41 		fprintf(config->output, "%s%.2f%%", config->csv_sep, pct);
42 	else if (pct)
43 		fprintf(config->output, "  ( +-%6.2f%% )", pct);
44 }
45 
46 static void print_noise(struct perf_stat_config *config,
47 			struct perf_evsel *evsel, double avg)
48 {
49 	struct perf_stat_evsel *ps;
50 
51 	if (config->run_count == 1)
52 		return;
53 
54 	ps = evsel->stats;
55 	print_noise_pct(config, stddev_stats(&ps->res_stats[0]), avg);
56 }
57 
58 static void print_cgroup(struct perf_stat_config *config, struct perf_evsel *evsel)
59 {
60 	if (nr_cgroups) {
61 		const char *cgrp_name = evsel->cgrp ? evsel->cgrp->name  : "";
62 		fprintf(config->output, "%s%s", config->csv_sep, cgrp_name);
63 	}
64 }
65 
66 
67 static void aggr_printout(struct perf_stat_config *config,
68 			  struct perf_evsel *evsel, int id, int nr)
69 {
70 	switch (config->aggr_mode) {
71 	case AGGR_CORE:
72 		fprintf(config->output, "S%d-C%*d%s%*d%s",
73 			cpu_map__id_to_socket(id),
74 			config->csv_output ? 0 : -8,
75 			cpu_map__id_to_cpu(id),
76 			config->csv_sep,
77 			config->csv_output ? 0 : 4,
78 			nr,
79 			config->csv_sep);
80 		break;
81 	case AGGR_SOCKET:
82 		fprintf(config->output, "S%*d%s%*d%s",
83 			config->csv_output ? 0 : -5,
84 			id,
85 			config->csv_sep,
86 			config->csv_output ? 0 : 4,
87 			nr,
88 			config->csv_sep);
89 			break;
90 	case AGGR_NONE:
91 		if (evsel->percore) {
92 			fprintf(config->output, "S%d-C%*d%s",
93 				cpu_map__id_to_socket(id),
94 				config->csv_output ? 0 : -5,
95 				cpu_map__id_to_cpu(id), config->csv_sep);
96 		} else {
97 			fprintf(config->output, "CPU%*d%s ",
98 				config->csv_output ? 0 : -5,
99 				perf_evsel__cpus(evsel)->map[id],
100 				config->csv_sep);
101 		}
102 		break;
103 	case AGGR_THREAD:
104 		fprintf(config->output, "%*s-%*d%s",
105 			config->csv_output ? 0 : 16,
106 			thread_map__comm(evsel->threads, id),
107 			config->csv_output ? 0 : -8,
108 			thread_map__pid(evsel->threads, id),
109 			config->csv_sep);
110 		break;
111 	case AGGR_GLOBAL:
112 	case AGGR_UNSET:
113 	default:
114 		break;
115 	}
116 }
117 
118 struct outstate {
119 	FILE *fh;
120 	bool newline;
121 	const char *prefix;
122 	int  nfields;
123 	int  id, nr;
124 	struct perf_evsel *evsel;
125 };
126 
127 #define METRIC_LEN  35
128 
129 static void new_line_std(struct perf_stat_config *config __maybe_unused,
130 			 void *ctx)
131 {
132 	struct outstate *os = ctx;
133 
134 	os->newline = true;
135 }
136 
137 static void do_new_line_std(struct perf_stat_config *config,
138 			    struct outstate *os)
139 {
140 	fputc('\n', os->fh);
141 	fputs(os->prefix, os->fh);
142 	aggr_printout(config, os->evsel, os->id, os->nr);
143 	if (config->aggr_mode == AGGR_NONE)
144 		fprintf(os->fh, "        ");
145 	fprintf(os->fh, "                                                 ");
146 }
147 
148 static void print_metric_std(struct perf_stat_config *config,
149 			     void *ctx, const char *color, const char *fmt,
150 			     const char *unit, double val)
151 {
152 	struct outstate *os = ctx;
153 	FILE *out = os->fh;
154 	int n;
155 	bool newline = os->newline;
156 
157 	os->newline = false;
158 
159 	if (unit == NULL || fmt == NULL) {
160 		fprintf(out, "%-*s", METRIC_LEN, "");
161 		return;
162 	}
163 
164 	if (newline)
165 		do_new_line_std(config, os);
166 
167 	n = fprintf(out, " # ");
168 	if (color)
169 		n += color_fprintf(out, color, fmt, val);
170 	else
171 		n += fprintf(out, fmt, val);
172 	fprintf(out, " %-*s", METRIC_LEN - n - 1, unit);
173 }
174 
175 static void new_line_csv(struct perf_stat_config *config, void *ctx)
176 {
177 	struct outstate *os = ctx;
178 	int i;
179 
180 	fputc('\n', os->fh);
181 	if (os->prefix)
182 		fprintf(os->fh, "%s%s", os->prefix, config->csv_sep);
183 	aggr_printout(config, os->evsel, os->id, os->nr);
184 	for (i = 0; i < os->nfields; i++)
185 		fputs(config->csv_sep, os->fh);
186 }
187 
188 static void print_metric_csv(struct perf_stat_config *config __maybe_unused,
189 			     void *ctx,
190 			     const char *color __maybe_unused,
191 			     const char *fmt, const char *unit, double val)
192 {
193 	struct outstate *os = ctx;
194 	FILE *out = os->fh;
195 	char buf[64], *vals, *ends;
196 
197 	if (unit == NULL || fmt == NULL) {
198 		fprintf(out, "%s%s", config->csv_sep, config->csv_sep);
199 		return;
200 	}
201 	snprintf(buf, sizeof(buf), fmt, val);
202 	ends = vals = ltrim(buf);
203 	while (isdigit(*ends) || *ends == '.')
204 		ends++;
205 	*ends = 0;
206 	while (isspace(*unit))
207 		unit++;
208 	fprintf(out, "%s%s%s%s", config->csv_sep, vals, config->csv_sep, unit);
209 }
210 
211 /* Filter out some columns that don't work well in metrics only mode */
212 
213 static bool valid_only_metric(const char *unit)
214 {
215 	if (!unit)
216 		return false;
217 	if (strstr(unit, "/sec") ||
218 	    strstr(unit, "hz") ||
219 	    strstr(unit, "Hz") ||
220 	    strstr(unit, "CPUs utilized"))
221 		return false;
222 	return true;
223 }
224 
225 static const char *fixunit(char *buf, struct perf_evsel *evsel,
226 			   const char *unit)
227 {
228 	if (!strncmp(unit, "of all", 6)) {
229 		snprintf(buf, 1024, "%s %s", perf_evsel__name(evsel),
230 			 unit);
231 		return buf;
232 	}
233 	return unit;
234 }
235 
236 static void print_metric_only(struct perf_stat_config *config,
237 			      void *ctx, const char *color, const char *fmt,
238 			      const char *unit, double val)
239 {
240 	struct outstate *os = ctx;
241 	FILE *out = os->fh;
242 	char buf[1024], str[1024];
243 	unsigned mlen = config->metric_only_len;
244 
245 	if (!valid_only_metric(unit))
246 		return;
247 	unit = fixunit(buf, os->evsel, unit);
248 	if (mlen < strlen(unit))
249 		mlen = strlen(unit) + 1;
250 
251 	if (color)
252 		mlen += strlen(color) + sizeof(PERF_COLOR_RESET) - 1;
253 
254 	color_snprintf(str, sizeof(str), color ?: "", fmt, val);
255 	fprintf(out, "%*s ", mlen, str);
256 }
257 
258 static void print_metric_only_csv(struct perf_stat_config *config __maybe_unused,
259 				  void *ctx, const char *color __maybe_unused,
260 				  const char *fmt,
261 				  const char *unit, double val)
262 {
263 	struct outstate *os = ctx;
264 	FILE *out = os->fh;
265 	char buf[64], *vals, *ends;
266 	char tbuf[1024];
267 
268 	if (!valid_only_metric(unit))
269 		return;
270 	unit = fixunit(tbuf, os->evsel, unit);
271 	snprintf(buf, sizeof buf, fmt, val);
272 	ends = vals = ltrim(buf);
273 	while (isdigit(*ends) || *ends == '.')
274 		ends++;
275 	*ends = 0;
276 	fprintf(out, "%s%s", vals, config->csv_sep);
277 }
278 
279 static void new_line_metric(struct perf_stat_config *config __maybe_unused,
280 			    void *ctx __maybe_unused)
281 {
282 }
283 
284 static void print_metric_header(struct perf_stat_config *config,
285 				void *ctx, const char *color __maybe_unused,
286 				const char *fmt __maybe_unused,
287 				const char *unit, double val __maybe_unused)
288 {
289 	struct outstate *os = ctx;
290 	char tbuf[1024];
291 
292 	if (!valid_only_metric(unit))
293 		return;
294 	unit = fixunit(tbuf, os->evsel, unit);
295 	if (config->csv_output)
296 		fprintf(os->fh, "%s%s", unit, config->csv_sep);
297 	else
298 		fprintf(os->fh, "%*s ", config->metric_only_len, unit);
299 }
300 
301 static int first_shadow_cpu(struct perf_stat_config *config,
302 			    struct perf_evsel *evsel, int id)
303 {
304 	struct perf_evlist *evlist = evsel->evlist;
305 	int i;
306 
307 	if (!config->aggr_get_id)
308 		return 0;
309 
310 	if (config->aggr_mode == AGGR_NONE)
311 		return id;
312 
313 	if (config->aggr_mode == AGGR_GLOBAL)
314 		return 0;
315 
316 	for (i = 0; i < perf_evsel__nr_cpus(evsel); i++) {
317 		int cpu2 = perf_evsel__cpus(evsel)->map[i];
318 
319 		if (config->aggr_get_id(config, evlist->cpus, cpu2) == id)
320 			return cpu2;
321 	}
322 	return 0;
323 }
324 
325 static void abs_printout(struct perf_stat_config *config,
326 			 int id, int nr, struct perf_evsel *evsel, double avg)
327 {
328 	FILE *output = config->output;
329 	double sc =  evsel->scale;
330 	const char *fmt;
331 
332 	if (config->csv_output) {
333 		fmt = floor(sc) != sc ?  "%.2f%s" : "%.0f%s";
334 	} else {
335 		if (config->big_num)
336 			fmt = floor(sc) != sc ? "%'18.2f%s" : "%'18.0f%s";
337 		else
338 			fmt = floor(sc) != sc ? "%18.2f%s" : "%18.0f%s";
339 	}
340 
341 	aggr_printout(config, evsel, id, nr);
342 
343 	fprintf(output, fmt, avg, config->csv_sep);
344 
345 	if (evsel->unit)
346 		fprintf(output, "%-*s%s",
347 			config->csv_output ? 0 : config->unit_width,
348 			evsel->unit, config->csv_sep);
349 
350 	fprintf(output, "%-*s", config->csv_output ? 0 : 25, perf_evsel__name(evsel));
351 
352 	print_cgroup(config, evsel);
353 }
354 
355 static bool is_mixed_hw_group(struct perf_evsel *counter)
356 {
357 	struct perf_evlist *evlist = counter->evlist;
358 	u32 pmu_type = counter->attr.type;
359 	struct perf_evsel *pos;
360 
361 	if (counter->nr_members < 2)
362 		return false;
363 
364 	evlist__for_each_entry(evlist, pos) {
365 		/* software events can be part of any hardware group */
366 		if (pos->attr.type == PERF_TYPE_SOFTWARE)
367 			continue;
368 		if (pmu_type == PERF_TYPE_SOFTWARE) {
369 			pmu_type = pos->attr.type;
370 			continue;
371 		}
372 		if (pmu_type != pos->attr.type)
373 			return true;
374 	}
375 
376 	return false;
377 }
378 
379 static void printout(struct perf_stat_config *config, int id, int nr,
380 		     struct perf_evsel *counter, double uval,
381 		     char *prefix, u64 run, u64 ena, double noise,
382 		     struct runtime_stat *st)
383 {
384 	struct perf_stat_output_ctx out;
385 	struct outstate os = {
386 		.fh = config->output,
387 		.prefix = prefix ? prefix : "",
388 		.id = id,
389 		.nr = nr,
390 		.evsel = counter,
391 	};
392 	print_metric_t pm = print_metric_std;
393 	new_line_t nl;
394 
395 	if (config->metric_only) {
396 		nl = new_line_metric;
397 		if (config->csv_output)
398 			pm = print_metric_only_csv;
399 		else
400 			pm = print_metric_only;
401 	} else
402 		nl = new_line_std;
403 
404 	if (config->csv_output && !config->metric_only) {
405 		static int aggr_fields[] = {
406 			[AGGR_GLOBAL] = 0,
407 			[AGGR_THREAD] = 1,
408 			[AGGR_NONE] = 1,
409 			[AGGR_SOCKET] = 2,
410 			[AGGR_CORE] = 2,
411 		};
412 
413 		pm = print_metric_csv;
414 		nl = new_line_csv;
415 		os.nfields = 3;
416 		os.nfields += aggr_fields[config->aggr_mode];
417 		if (counter->cgrp)
418 			os.nfields++;
419 	}
420 	if (run == 0 || ena == 0 || counter->counts->scaled == -1) {
421 		if (config->metric_only) {
422 			pm(config, &os, NULL, "", "", 0);
423 			return;
424 		}
425 		aggr_printout(config, counter, id, nr);
426 
427 		fprintf(config->output, "%*s%s",
428 			config->csv_output ? 0 : 18,
429 			counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED,
430 			config->csv_sep);
431 
432 		if (counter->supported) {
433 			config->print_free_counters_hint = 1;
434 			if (is_mixed_hw_group(counter))
435 				config->print_mixed_hw_group_error = 1;
436 		}
437 
438 		fprintf(config->output, "%-*s%s",
439 			config->csv_output ? 0 : config->unit_width,
440 			counter->unit, config->csv_sep);
441 
442 		fprintf(config->output, "%*s",
443 			config->csv_output ? 0 : -25,
444 			perf_evsel__name(counter));
445 
446 		print_cgroup(config, counter);
447 
448 		if (!config->csv_output)
449 			pm(config, &os, NULL, NULL, "", 0);
450 		print_noise(config, counter, noise);
451 		print_running(config, run, ena);
452 		if (config->csv_output)
453 			pm(config, &os, NULL, NULL, "", 0);
454 		return;
455 	}
456 
457 	if (!config->metric_only)
458 		abs_printout(config, id, nr, counter, uval);
459 
460 	out.print_metric = pm;
461 	out.new_line = nl;
462 	out.ctx = &os;
463 	out.force_header = false;
464 
465 	if (config->csv_output && !config->metric_only) {
466 		print_noise(config, counter, noise);
467 		print_running(config, run, ena);
468 	}
469 
470 	perf_stat__print_shadow_stats(config, counter, uval,
471 				first_shadow_cpu(config, counter, id),
472 				&out, &config->metric_events, st);
473 	if (!config->csv_output && !config->metric_only) {
474 		print_noise(config, counter, noise);
475 		print_running(config, run, ena);
476 	}
477 }
478 
479 static void aggr_update_shadow(struct perf_stat_config *config,
480 			       struct perf_evlist *evlist)
481 {
482 	int cpu, s2, id, s;
483 	u64 val;
484 	struct perf_evsel *counter;
485 
486 	for (s = 0; s < config->aggr_map->nr; s++) {
487 		id = config->aggr_map->map[s];
488 		evlist__for_each_entry(evlist, counter) {
489 			val = 0;
490 			for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) {
491 				s2 = config->aggr_get_id(config, evlist->cpus, cpu);
492 				if (s2 != id)
493 					continue;
494 				val += perf_counts(counter->counts, cpu, 0)->val;
495 			}
496 			perf_stat__update_shadow_stats(counter, val,
497 					first_shadow_cpu(config, counter, id),
498 					&rt_stat);
499 		}
500 	}
501 }
502 
503 static void uniquify_event_name(struct perf_evsel *counter)
504 {
505 	char *new_name;
506 	char *config;
507 
508 	if (counter->uniquified_name ||
509 	    !counter->pmu_name || !strncmp(counter->name, counter->pmu_name,
510 					   strlen(counter->pmu_name)))
511 		return;
512 
513 	config = strchr(counter->name, '/');
514 	if (config) {
515 		if (asprintf(&new_name,
516 			     "%s%s", counter->pmu_name, config) > 0) {
517 			free(counter->name);
518 			counter->name = new_name;
519 		}
520 	} else {
521 		if (asprintf(&new_name,
522 			     "%s [%s]", counter->name, counter->pmu_name) > 0) {
523 			free(counter->name);
524 			counter->name = new_name;
525 		}
526 	}
527 
528 	counter->uniquified_name = true;
529 }
530 
531 static void collect_all_aliases(struct perf_stat_config *config, struct perf_evsel *counter,
532 			    void (*cb)(struct perf_stat_config *config, struct perf_evsel *counter, void *data,
533 				       bool first),
534 			    void *data)
535 {
536 	struct perf_evlist *evlist = counter->evlist;
537 	struct perf_evsel *alias;
538 
539 	alias = list_prepare_entry(counter, &(evlist->entries), node);
540 	list_for_each_entry_continue (alias, &evlist->entries, node) {
541 		if (strcmp(perf_evsel__name(alias), perf_evsel__name(counter)) ||
542 		    alias->scale != counter->scale ||
543 		    alias->cgrp != counter->cgrp ||
544 		    strcmp(alias->unit, counter->unit) ||
545 		    perf_evsel__is_clock(alias) != perf_evsel__is_clock(counter))
546 			break;
547 		alias->merged_stat = true;
548 		cb(config, alias, data, false);
549 	}
550 }
551 
552 static bool collect_data(struct perf_stat_config *config, struct perf_evsel *counter,
553 			    void (*cb)(struct perf_stat_config *config, struct perf_evsel *counter, void *data,
554 				       bool first),
555 			    void *data)
556 {
557 	if (counter->merged_stat)
558 		return false;
559 	cb(config, counter, data, true);
560 	if (config->no_merge)
561 		uniquify_event_name(counter);
562 	else if (counter->auto_merge_stats)
563 		collect_all_aliases(config, counter, cb, data);
564 	return true;
565 }
566 
567 struct aggr_data {
568 	u64 ena, run, val;
569 	int id;
570 	int nr;
571 	int cpu;
572 };
573 
574 static void aggr_cb(struct perf_stat_config *config,
575 		    struct perf_evsel *counter, void *data, bool first)
576 {
577 	struct aggr_data *ad = data;
578 	int cpu, s2;
579 
580 	for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) {
581 		struct perf_counts_values *counts;
582 
583 		s2 = config->aggr_get_id(config, perf_evsel__cpus(counter), cpu);
584 		if (s2 != ad->id)
585 			continue;
586 		if (first)
587 			ad->nr++;
588 		counts = perf_counts(counter->counts, cpu, 0);
589 		/*
590 		 * When any result is bad, make them all to give
591 		 * consistent output in interval mode.
592 		 */
593 		if (counts->ena == 0 || counts->run == 0 ||
594 		    counter->counts->scaled == -1) {
595 			ad->ena = 0;
596 			ad->run = 0;
597 			break;
598 		}
599 		ad->val += counts->val;
600 		ad->ena += counts->ena;
601 		ad->run += counts->run;
602 	}
603 }
604 
605 static void print_counter_aggrdata(struct perf_stat_config *config,
606 				   struct perf_evsel *counter, int s,
607 				   char *prefix, bool metric_only,
608 				   bool *first)
609 {
610 	struct aggr_data ad;
611 	FILE *output = config->output;
612 	u64 ena, run, val;
613 	int id, nr;
614 	double uval;
615 
616 	ad.id = id = config->aggr_map->map[s];
617 	ad.val = ad.ena = ad.run = 0;
618 	ad.nr = 0;
619 	if (!collect_data(config, counter, aggr_cb, &ad))
620 		return;
621 
622 	nr = ad.nr;
623 	ena = ad.ena;
624 	run = ad.run;
625 	val = ad.val;
626 	if (*first && metric_only) {
627 		*first = false;
628 		aggr_printout(config, counter, id, nr);
629 	}
630 	if (prefix && !metric_only)
631 		fprintf(output, "%s", prefix);
632 
633 	uval = val * counter->scale;
634 	printout(config, id, nr, counter, uval, prefix,
635 		 run, ena, 1.0, &rt_stat);
636 	if (!metric_only)
637 		fputc('\n', output);
638 }
639 
640 static void print_aggr(struct perf_stat_config *config,
641 		       struct perf_evlist *evlist,
642 		       char *prefix)
643 {
644 	bool metric_only = config->metric_only;
645 	FILE *output = config->output;
646 	struct perf_evsel *counter;
647 	int s;
648 	bool first;
649 
650 	if (!(config->aggr_map || config->aggr_get_id))
651 		return;
652 
653 	aggr_update_shadow(config, evlist);
654 
655 	/*
656 	 * With metric_only everything is on a single line.
657 	 * Without each counter has its own line.
658 	 */
659 	for (s = 0; s < config->aggr_map->nr; s++) {
660 		if (prefix && metric_only)
661 			fprintf(output, "%s", prefix);
662 
663 		first = true;
664 		evlist__for_each_entry(evlist, counter) {
665 			print_counter_aggrdata(config, counter, s,
666 					       prefix, metric_only,
667 					       &first);
668 		}
669 		if (metric_only)
670 			fputc('\n', output);
671 	}
672 }
673 
674 static int cmp_val(const void *a, const void *b)
675 {
676 	return ((struct perf_aggr_thread_value *)b)->val -
677 		((struct perf_aggr_thread_value *)a)->val;
678 }
679 
680 static struct perf_aggr_thread_value *sort_aggr_thread(
681 					struct perf_evsel *counter,
682 					int nthreads, int ncpus,
683 					int *ret,
684 					struct target *_target)
685 {
686 	int cpu, thread, i = 0;
687 	double uval;
688 	struct perf_aggr_thread_value *buf;
689 
690 	buf = calloc(nthreads, sizeof(struct perf_aggr_thread_value));
691 	if (!buf)
692 		return NULL;
693 
694 	for (thread = 0; thread < nthreads; thread++) {
695 		u64 ena = 0, run = 0, val = 0;
696 
697 		for (cpu = 0; cpu < ncpus; cpu++) {
698 			val += perf_counts(counter->counts, cpu, thread)->val;
699 			ena += perf_counts(counter->counts, cpu, thread)->ena;
700 			run += perf_counts(counter->counts, cpu, thread)->run;
701 		}
702 
703 		uval = val * counter->scale;
704 
705 		/*
706 		 * Skip value 0 when enabling --per-thread globally,
707 		 * otherwise too many 0 output.
708 		 */
709 		if (uval == 0.0 && target__has_per_thread(_target))
710 			continue;
711 
712 		buf[i].counter = counter;
713 		buf[i].id = thread;
714 		buf[i].uval = uval;
715 		buf[i].val = val;
716 		buf[i].run = run;
717 		buf[i].ena = ena;
718 		i++;
719 	}
720 
721 	qsort(buf, i, sizeof(struct perf_aggr_thread_value), cmp_val);
722 
723 	if (ret)
724 		*ret = i;
725 
726 	return buf;
727 }
728 
729 static void print_aggr_thread(struct perf_stat_config *config,
730 			      struct target *_target,
731 			      struct perf_evsel *counter, char *prefix)
732 {
733 	FILE *output = config->output;
734 	int nthreads = thread_map__nr(counter->threads);
735 	int ncpus = cpu_map__nr(counter->cpus);
736 	int thread, sorted_threads, id;
737 	struct perf_aggr_thread_value *buf;
738 
739 	buf = sort_aggr_thread(counter, nthreads, ncpus, &sorted_threads, _target);
740 	if (!buf) {
741 		perror("cannot sort aggr thread");
742 		return;
743 	}
744 
745 	for (thread = 0; thread < sorted_threads; thread++) {
746 		if (prefix)
747 			fprintf(output, "%s", prefix);
748 
749 		id = buf[thread].id;
750 		if (config->stats)
751 			printout(config, id, 0, buf[thread].counter, buf[thread].uval,
752 				 prefix, buf[thread].run, buf[thread].ena, 1.0,
753 				 &config->stats[id]);
754 		else
755 			printout(config, id, 0, buf[thread].counter, buf[thread].uval,
756 				 prefix, buf[thread].run, buf[thread].ena, 1.0,
757 				 &rt_stat);
758 		fputc('\n', output);
759 	}
760 
761 	free(buf);
762 }
763 
764 struct caggr_data {
765 	double avg, avg_enabled, avg_running;
766 };
767 
768 static void counter_aggr_cb(struct perf_stat_config *config __maybe_unused,
769 			    struct perf_evsel *counter, void *data,
770 			    bool first __maybe_unused)
771 {
772 	struct caggr_data *cd = data;
773 	struct perf_stat_evsel *ps = counter->stats;
774 
775 	cd->avg += avg_stats(&ps->res_stats[0]);
776 	cd->avg_enabled += avg_stats(&ps->res_stats[1]);
777 	cd->avg_running += avg_stats(&ps->res_stats[2]);
778 }
779 
780 /*
781  * Print out the results of a single counter:
782  * aggregated counts in system-wide mode
783  */
784 static void print_counter_aggr(struct perf_stat_config *config,
785 			       struct perf_evsel *counter, char *prefix)
786 {
787 	bool metric_only = config->metric_only;
788 	FILE *output = config->output;
789 	double uval;
790 	struct caggr_data cd = { .avg = 0.0 };
791 
792 	if (!collect_data(config, counter, counter_aggr_cb, &cd))
793 		return;
794 
795 	if (prefix && !metric_only)
796 		fprintf(output, "%s", prefix);
797 
798 	uval = cd.avg * counter->scale;
799 	printout(config, -1, 0, counter, uval, prefix, cd.avg_running, cd.avg_enabled,
800 		 cd.avg, &rt_stat);
801 	if (!metric_only)
802 		fprintf(output, "\n");
803 }
804 
805 static void counter_cb(struct perf_stat_config *config __maybe_unused,
806 		       struct perf_evsel *counter, void *data,
807 		       bool first __maybe_unused)
808 {
809 	struct aggr_data *ad = data;
810 
811 	ad->val += perf_counts(counter->counts, ad->cpu, 0)->val;
812 	ad->ena += perf_counts(counter->counts, ad->cpu, 0)->ena;
813 	ad->run += perf_counts(counter->counts, ad->cpu, 0)->run;
814 }
815 
816 /*
817  * Print out the results of a single counter:
818  * does not use aggregated count in system-wide
819  */
820 static void print_counter(struct perf_stat_config *config,
821 			  struct perf_evsel *counter, char *prefix)
822 {
823 	FILE *output = config->output;
824 	u64 ena, run, val;
825 	double uval;
826 	int cpu;
827 
828 	for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) {
829 		struct aggr_data ad = { .cpu = cpu };
830 
831 		if (!collect_data(config, counter, counter_cb, &ad))
832 			return;
833 		val = ad.val;
834 		ena = ad.ena;
835 		run = ad.run;
836 
837 		if (prefix)
838 			fprintf(output, "%s", prefix);
839 
840 		uval = val * counter->scale;
841 		printout(config, cpu, 0, counter, uval, prefix, run, ena, 1.0,
842 			 &rt_stat);
843 
844 		fputc('\n', output);
845 	}
846 }
847 
848 static void print_no_aggr_metric(struct perf_stat_config *config,
849 				 struct perf_evlist *evlist,
850 				 char *prefix)
851 {
852 	int cpu;
853 	int nrcpus = 0;
854 	struct perf_evsel *counter;
855 	u64 ena, run, val;
856 	double uval;
857 
858 	nrcpus = evlist->cpus->nr;
859 	for (cpu = 0; cpu < nrcpus; cpu++) {
860 		bool first = true;
861 
862 		if (prefix)
863 			fputs(prefix, config->output);
864 		evlist__for_each_entry(evlist, counter) {
865 			if (first) {
866 				aggr_printout(config, counter, cpu, 0);
867 				first = false;
868 			}
869 			val = perf_counts(counter->counts, cpu, 0)->val;
870 			ena = perf_counts(counter->counts, cpu, 0)->ena;
871 			run = perf_counts(counter->counts, cpu, 0)->run;
872 
873 			uval = val * counter->scale;
874 			printout(config, cpu, 0, counter, uval, prefix, run, ena, 1.0,
875 				 &rt_stat);
876 		}
877 		fputc('\n', config->output);
878 	}
879 }
880 
881 static int aggr_header_lens[] = {
882 	[AGGR_CORE] = 18,
883 	[AGGR_SOCKET] = 12,
884 	[AGGR_NONE] = 6,
885 	[AGGR_THREAD] = 24,
886 	[AGGR_GLOBAL] = 0,
887 };
888 
889 static const char *aggr_header_csv[] = {
890 	[AGGR_CORE] 	= 	"core,cpus,",
891 	[AGGR_SOCKET] 	= 	"socket,cpus",
892 	[AGGR_NONE] 	= 	"cpu,",
893 	[AGGR_THREAD] 	= 	"comm-pid,",
894 	[AGGR_GLOBAL] 	=	""
895 };
896 
897 static void print_metric_headers(struct perf_stat_config *config,
898 				 struct perf_evlist *evlist,
899 				 const char *prefix, bool no_indent)
900 {
901 	struct perf_stat_output_ctx out;
902 	struct perf_evsel *counter;
903 	struct outstate os = {
904 		.fh = config->output
905 	};
906 
907 	if (prefix)
908 		fprintf(config->output, "%s", prefix);
909 
910 	if (!config->csv_output && !no_indent)
911 		fprintf(config->output, "%*s",
912 			aggr_header_lens[config->aggr_mode], "");
913 	if (config->csv_output) {
914 		if (config->interval)
915 			fputs("time,", config->output);
916 		fputs(aggr_header_csv[config->aggr_mode], config->output);
917 	}
918 
919 	/* Print metrics headers only */
920 	evlist__for_each_entry(evlist, counter) {
921 		os.evsel = counter;
922 		out.ctx = &os;
923 		out.print_metric = print_metric_header;
924 		out.new_line = new_line_metric;
925 		out.force_header = true;
926 		os.evsel = counter;
927 		perf_stat__print_shadow_stats(config, counter, 0,
928 					      0,
929 					      &out,
930 					      &config->metric_events,
931 					      &rt_stat);
932 	}
933 	fputc('\n', config->output);
934 }
935 
936 static void print_interval(struct perf_stat_config *config,
937 			   struct perf_evlist *evlist,
938 			   char *prefix, struct timespec *ts)
939 {
940 	bool metric_only = config->metric_only;
941 	unsigned int unit_width = config->unit_width;
942 	FILE *output = config->output;
943 	static int num_print_interval;
944 
945 	if (config->interval_clear)
946 		puts(CONSOLE_CLEAR);
947 
948 	sprintf(prefix, "%6lu.%09lu%s", ts->tv_sec, ts->tv_nsec, config->csv_sep);
949 
950 	if ((num_print_interval == 0 && !config->csv_output) || config->interval_clear) {
951 		switch (config->aggr_mode) {
952 		case AGGR_SOCKET:
953 			fprintf(output, "#           time socket cpus");
954 			if (!metric_only)
955 				fprintf(output, "             counts %*s events\n", unit_width, "unit");
956 			break;
957 		case AGGR_CORE:
958 			fprintf(output, "#           time core         cpus");
959 			if (!metric_only)
960 				fprintf(output, "             counts %*s events\n", unit_width, "unit");
961 			break;
962 		case AGGR_NONE:
963 			fprintf(output, "#           time CPU    ");
964 			if (!metric_only)
965 				fprintf(output, "                counts %*s events\n", unit_width, "unit");
966 			break;
967 		case AGGR_THREAD:
968 			fprintf(output, "#           time             comm-pid");
969 			if (!metric_only)
970 				fprintf(output, "                  counts %*s events\n", unit_width, "unit");
971 			break;
972 		case AGGR_GLOBAL:
973 		default:
974 			fprintf(output, "#           time");
975 			if (!metric_only)
976 				fprintf(output, "             counts %*s events\n", unit_width, "unit");
977 		case AGGR_UNSET:
978 			break;
979 		}
980 	}
981 
982 	if ((num_print_interval == 0 || config->interval_clear) && metric_only)
983 		print_metric_headers(config, evlist, " ", true);
984 	if (++num_print_interval == 25)
985 		num_print_interval = 0;
986 }
987 
988 static void print_header(struct perf_stat_config *config,
989 			 struct target *_target,
990 			 int argc, const char **argv)
991 {
992 	FILE *output = config->output;
993 	int i;
994 
995 	fflush(stdout);
996 
997 	if (!config->csv_output) {
998 		fprintf(output, "\n");
999 		fprintf(output, " Performance counter stats for ");
1000 		if (_target->system_wide)
1001 			fprintf(output, "\'system wide");
1002 		else if (_target->cpu_list)
1003 			fprintf(output, "\'CPU(s) %s", _target->cpu_list);
1004 		else if (!target__has_task(_target)) {
1005 			fprintf(output, "\'%s", argv ? argv[0] : "pipe");
1006 			for (i = 1; argv && (i < argc); i++)
1007 				fprintf(output, " %s", argv[i]);
1008 		} else if (_target->pid)
1009 			fprintf(output, "process id \'%s", _target->pid);
1010 		else
1011 			fprintf(output, "thread id \'%s", _target->tid);
1012 
1013 		fprintf(output, "\'");
1014 		if (config->run_count > 1)
1015 			fprintf(output, " (%d runs)", config->run_count);
1016 		fprintf(output, ":\n\n");
1017 	}
1018 }
1019 
1020 static int get_precision(double num)
1021 {
1022 	if (num > 1)
1023 		return 0;
1024 
1025 	return lround(ceil(-log10(num)));
1026 }
1027 
1028 static void print_table(struct perf_stat_config *config,
1029 			FILE *output, int precision, double avg)
1030 {
1031 	char tmp[64];
1032 	int idx, indent = 0;
1033 
1034 	scnprintf(tmp, 64, " %17.*f", precision, avg);
1035 	while (tmp[indent] == ' ')
1036 		indent++;
1037 
1038 	fprintf(output, "%*s# Table of individual measurements:\n", indent, "");
1039 
1040 	for (idx = 0; idx < config->run_count; idx++) {
1041 		double run = (double) config->walltime_run[idx] / NSEC_PER_SEC;
1042 		int h, n = 1 + abs((int) (100.0 * (run - avg)/run) / 5);
1043 
1044 		fprintf(output, " %17.*f (%+.*f) ",
1045 			precision, run, precision, run - avg);
1046 
1047 		for (h = 0; h < n; h++)
1048 			fprintf(output, "#");
1049 
1050 		fprintf(output, "\n");
1051 	}
1052 
1053 	fprintf(output, "\n%*s# Final result:\n", indent, "");
1054 }
1055 
1056 static double timeval2double(struct timeval *t)
1057 {
1058 	return t->tv_sec + (double) t->tv_usec/USEC_PER_SEC;
1059 }
1060 
1061 static void print_footer(struct perf_stat_config *config)
1062 {
1063 	double avg = avg_stats(config->walltime_nsecs_stats) / NSEC_PER_SEC;
1064 	FILE *output = config->output;
1065 	int n;
1066 
1067 	if (!config->null_run)
1068 		fprintf(output, "\n");
1069 
1070 	if (config->run_count == 1) {
1071 		fprintf(output, " %17.9f seconds time elapsed", avg);
1072 
1073 		if (config->ru_display) {
1074 			double ru_utime = timeval2double(&config->ru_data.ru_utime);
1075 			double ru_stime = timeval2double(&config->ru_data.ru_stime);
1076 
1077 			fprintf(output, "\n\n");
1078 			fprintf(output, " %17.9f seconds user\n", ru_utime);
1079 			fprintf(output, " %17.9f seconds sys\n", ru_stime);
1080 		}
1081 	} else {
1082 		double sd = stddev_stats(config->walltime_nsecs_stats) / NSEC_PER_SEC;
1083 		/*
1084 		 * Display at most 2 more significant
1085 		 * digits than the stddev inaccuracy.
1086 		 */
1087 		int precision = get_precision(sd) + 2;
1088 
1089 		if (config->walltime_run_table)
1090 			print_table(config, output, precision, avg);
1091 
1092 		fprintf(output, " %17.*f +- %.*f seconds time elapsed",
1093 			precision, avg, precision, sd);
1094 
1095 		print_noise_pct(config, sd, avg);
1096 	}
1097 	fprintf(output, "\n\n");
1098 
1099 	if (config->print_free_counters_hint &&
1100 	    sysctl__read_int("kernel/nmi_watchdog", &n) >= 0 &&
1101 	    n > 0)
1102 		fprintf(output,
1103 "Some events weren't counted. Try disabling the NMI watchdog:\n"
1104 "	echo 0 > /proc/sys/kernel/nmi_watchdog\n"
1105 "	perf stat ...\n"
1106 "	echo 1 > /proc/sys/kernel/nmi_watchdog\n");
1107 
1108 	if (config->print_mixed_hw_group_error)
1109 		fprintf(output,
1110 			"The events in group usually have to be from "
1111 			"the same PMU. Try reorganizing the group.\n");
1112 }
1113 
1114 static void print_percore(struct perf_stat_config *config,
1115 			  struct perf_evsel *counter, char *prefix)
1116 {
1117 	bool metric_only = config->metric_only;
1118 	FILE *output = config->output;
1119 	int s;
1120 	bool first = true;
1121 
1122 	if (!(config->aggr_map || config->aggr_get_id))
1123 		return;
1124 
1125 	for (s = 0; s < config->aggr_map->nr; s++) {
1126 		if (prefix && metric_only)
1127 			fprintf(output, "%s", prefix);
1128 
1129 		print_counter_aggrdata(config, counter, s,
1130 				       prefix, metric_only,
1131 				       &first);
1132 	}
1133 
1134 	if (metric_only)
1135 		fputc('\n', output);
1136 }
1137 
1138 void
1139 perf_evlist__print_counters(struct perf_evlist *evlist,
1140 			    struct perf_stat_config *config,
1141 			    struct target *_target,
1142 			    struct timespec *ts,
1143 			    int argc, const char **argv)
1144 {
1145 	bool metric_only = config->metric_only;
1146 	int interval = config->interval;
1147 	struct perf_evsel *counter;
1148 	char buf[64], *prefix = NULL;
1149 
1150 	if (interval)
1151 		print_interval(config, evlist, prefix = buf, ts);
1152 	else
1153 		print_header(config, _target, argc, argv);
1154 
1155 	if (metric_only) {
1156 		static int num_print_iv;
1157 
1158 		if (num_print_iv == 0 && !interval)
1159 			print_metric_headers(config, evlist, prefix, false);
1160 		if (num_print_iv++ == 25)
1161 			num_print_iv = 0;
1162 		if (config->aggr_mode == AGGR_GLOBAL && prefix)
1163 			fprintf(config->output, "%s", prefix);
1164 	}
1165 
1166 	switch (config->aggr_mode) {
1167 	case AGGR_CORE:
1168 	case AGGR_SOCKET:
1169 		print_aggr(config, evlist, prefix);
1170 		break;
1171 	case AGGR_THREAD:
1172 		evlist__for_each_entry(evlist, counter) {
1173 			print_aggr_thread(config, _target, counter, prefix);
1174 		}
1175 		break;
1176 	case AGGR_GLOBAL:
1177 		evlist__for_each_entry(evlist, counter) {
1178 			print_counter_aggr(config, counter, prefix);
1179 		}
1180 		if (metric_only)
1181 			fputc('\n', config->output);
1182 		break;
1183 	case AGGR_NONE:
1184 		if (metric_only)
1185 			print_no_aggr_metric(config, evlist, prefix);
1186 		else {
1187 			evlist__for_each_entry(evlist, counter) {
1188 				if (counter->percore)
1189 					print_percore(config, counter, prefix);
1190 				else
1191 					print_counter(config, counter, prefix);
1192 			}
1193 		}
1194 		break;
1195 	case AGGR_UNSET:
1196 	default:
1197 		break;
1198 	}
1199 
1200 	if (!interval && !config->csv_output)
1201 		print_footer(config);
1202 
1203 	fflush(config->output);
1204 }
1205