xref: /linux/tools/perf/util/block-info.c (revision 2975489458c59ce2e348b1b3aef5d8d2acb5cc8d)
1 // SPDX-License-Identifier: GPL-2.0
2 #include <stdlib.h>
3 #include <string.h>
4 #include <linux/zalloc.h>
5 #include "block-info.h"
6 #include "sort.h"
7 #include "annotate.h"
8 #include "symbol.h"
9 #include "dso.h"
10 #include "map.h"
11 #include "srcline.h"
12 #include "evlist.h"
13 #include "ui/browsers/hists.h"
14 
15 static struct block_header_column {
16 	const char *name;
17 	int width;
18 } block_columns[PERF_HPP_REPORT__BLOCK_MAX_INDEX] = {
19 	[PERF_HPP_REPORT__BLOCK_TOTAL_CYCLES_PCT] = {
20 		.name = "Sampled Cycles%",
21 		.width = 15,
22 	},
23 	[PERF_HPP_REPORT__BLOCK_LBR_CYCLES] = {
24 		.name = "Sampled Cycles",
25 		.width = 14,
26 	},
27 	[PERF_HPP_REPORT__BLOCK_CYCLES_PCT] = {
28 		.name = "Avg Cycles%",
29 		.width = 11,
30 	},
31 	[PERF_HPP_REPORT__BLOCK_AVG_CYCLES] = {
32 		.name = "Avg Cycles",
33 		.width = 10,
34 	},
35 	[PERF_HPP_REPORT__BLOCK_RANGE] = {
36 		.name = "[Program Block Range]",
37 		.width = 70,
38 	},
39 	[PERF_HPP_REPORT__BLOCK_DSO] = {
40 		.name = "Shared Object",
41 		.width = 20,
42 	}
43 };
44 
45 struct block_info *block_info__get(struct block_info *bi)
46 {
47 	if (bi)
48 		refcount_inc(&bi->refcnt);
49 	return bi;
50 }
51 
52 void block_info__put(struct block_info *bi)
53 {
54 	if (bi && refcount_dec_and_test(&bi->refcnt))
55 		free(bi);
56 }
57 
58 struct block_info *block_info__new(void)
59 {
60 	struct block_info *bi = zalloc(sizeof(*bi));
61 
62 	if (bi)
63 		refcount_set(&bi->refcnt, 1);
64 	return bi;
65 }
66 
67 int64_t block_info__cmp(struct perf_hpp_fmt *fmt __maybe_unused,
68 			struct hist_entry *left, struct hist_entry *right)
69 {
70 	struct block_info *bi_l = left->block_info;
71 	struct block_info *bi_r = right->block_info;
72 	int cmp;
73 
74 	if (!bi_l->sym || !bi_r->sym) {
75 		if (!bi_l->sym && !bi_r->sym)
76 			return 0;
77 		else if (!bi_l->sym)
78 			return -1;
79 		else
80 			return 1;
81 	}
82 
83 	if (bi_l->sym == bi_r->sym) {
84 		if (bi_l->start == bi_r->start) {
85 			if (bi_l->end == bi_r->end)
86 				return 0;
87 			else
88 				return (int64_t)(bi_r->end - bi_l->end);
89 		} else
90 			return (int64_t)(bi_r->start - bi_l->start);
91 	} else {
92 		cmp = strcmp(bi_l->sym->name, bi_r->sym->name);
93 		return cmp;
94 	}
95 
96 	if (bi_l->sym->start != bi_r->sym->start)
97 		return (int64_t)(bi_r->sym->start - bi_l->sym->start);
98 
99 	return (int64_t)(bi_r->sym->end - bi_l->sym->end);
100 }
101 
102 static void init_block_info(struct block_info *bi, struct symbol *sym,
103 			    struct cyc_hist *ch, int offset,
104 			    u64 total_cycles)
105 {
106 	bi->sym = sym;
107 	bi->start = ch->start;
108 	bi->end = offset;
109 	bi->cycles = ch->cycles;
110 	bi->cycles_aggr = ch->cycles_aggr;
111 	bi->num = ch->num;
112 	bi->num_aggr = ch->num_aggr;
113 	bi->total_cycles = total_cycles;
114 
115 	memcpy(bi->cycles_spark, ch->cycles_spark,
116 	       NUM_SPARKS * sizeof(u64));
117 }
118 
119 int block_info__process_sym(struct hist_entry *he, struct block_hist *bh,
120 			    u64 *block_cycles_aggr, u64 total_cycles)
121 {
122 	struct annotation *notes;
123 	struct cyc_hist *ch;
124 	static struct addr_location al;
125 	u64 cycles = 0;
126 
127 	if (!he->ms.map || !he->ms.sym)
128 		return 0;
129 
130 	memset(&al, 0, sizeof(al));
131 	al.map = he->ms.map;
132 	al.sym = he->ms.sym;
133 
134 	notes = symbol__annotation(he->ms.sym);
135 	if (!notes || !notes->src || !notes->src->cycles_hist)
136 		return 0;
137 	ch = notes->src->cycles_hist;
138 	for (unsigned int i = 0; i < symbol__size(he->ms.sym); i++) {
139 		if (ch[i].num_aggr) {
140 			struct block_info *bi;
141 			struct hist_entry *he_block;
142 
143 			bi = block_info__new();
144 			if (!bi)
145 				return -1;
146 
147 			init_block_info(bi, he->ms.sym, &ch[i], i,
148 					total_cycles);
149 			cycles += bi->cycles_aggr / bi->num_aggr;
150 
151 			he_block = hists__add_entry_block(&bh->block_hists,
152 							  &al, bi);
153 			if (!he_block) {
154 				block_info__put(bi);
155 				return -1;
156 			}
157 		}
158 	}
159 
160 	if (block_cycles_aggr)
161 		*block_cycles_aggr += cycles;
162 
163 	return 0;
164 }
165 
166 static int block_column_header(struct perf_hpp_fmt *fmt,
167 			       struct perf_hpp *hpp,
168 			       struct hists *hists __maybe_unused,
169 			       int line __maybe_unused,
170 			       int *span __maybe_unused)
171 {
172 	struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
173 
174 	return scnprintf(hpp->buf, hpp->size, "%*s", block_fmt->width,
175 			 block_fmt->header);
176 }
177 
178 static int block_column_width(struct perf_hpp_fmt *fmt,
179 			      struct perf_hpp *hpp __maybe_unused,
180 			      struct hists *hists __maybe_unused)
181 {
182 	struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
183 
184 	return block_fmt->width;
185 }
186 
187 static int block_total_cycles_pct_entry(struct perf_hpp_fmt *fmt,
188 					struct perf_hpp *hpp,
189 					struct hist_entry *he)
190 {
191 	struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
192 	struct block_info *bi = he->block_info;
193 	double ratio = 0.0;
194 	char buf[16];
195 
196 	if (block_fmt->total_cycles)
197 		ratio = (double)bi->cycles / (double)block_fmt->total_cycles;
198 
199 	sprintf(buf, "%.2f%%", 100.0 * ratio);
200 
201 	return scnprintf(hpp->buf, hpp->size, "%*s", block_fmt->width, buf);
202 }
203 
204 static int64_t block_total_cycles_pct_sort(struct perf_hpp_fmt *fmt,
205 					   struct hist_entry *left,
206 					   struct hist_entry *right)
207 {
208 	struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
209 	struct block_info *bi_l = left->block_info;
210 	struct block_info *bi_r = right->block_info;
211 	double l, r;
212 
213 	if (block_fmt->total_cycles) {
214 		l = ((double)bi_l->cycles /
215 			(double)block_fmt->total_cycles) * 100000.0;
216 		r = ((double)bi_r->cycles /
217 			(double)block_fmt->total_cycles) * 100000.0;
218 		return (int64_t)l - (int64_t)r;
219 	}
220 
221 	return 0;
222 }
223 
224 static void cycles_string(u64 cycles, char *buf, int size)
225 {
226 	if (cycles >= 1000000)
227 		scnprintf(buf, size, "%.1fM", (double)cycles / 1000000.0);
228 	else if (cycles >= 1000)
229 		scnprintf(buf, size, "%.1fK", (double)cycles / 1000.0);
230 	else
231 		scnprintf(buf, size, "%1d", cycles);
232 }
233 
234 static int block_cycles_lbr_entry(struct perf_hpp_fmt *fmt,
235 				  struct perf_hpp *hpp, struct hist_entry *he)
236 {
237 	struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
238 	struct block_info *bi = he->block_info;
239 	char cycles_buf[16];
240 
241 	cycles_string(bi->cycles_aggr, cycles_buf, sizeof(cycles_buf));
242 
243 	return scnprintf(hpp->buf, hpp->size, "%*s", block_fmt->width,
244 			 cycles_buf);
245 }
246 
247 static int block_cycles_pct_entry(struct perf_hpp_fmt *fmt,
248 				  struct perf_hpp *hpp, struct hist_entry *he)
249 {
250 	struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
251 	struct block_info *bi = he->block_info;
252 	double ratio = 0.0;
253 	u64 avg;
254 	char buf[16];
255 
256 	if (block_fmt->block_cycles && bi->num_aggr) {
257 		avg = bi->cycles_aggr / bi->num_aggr;
258 		ratio = (double)avg / (double)block_fmt->block_cycles;
259 	}
260 
261 	sprintf(buf, "%.2f%%", 100.0 * ratio);
262 
263 	return scnprintf(hpp->buf, hpp->size, "%*s", block_fmt->width, buf);
264 }
265 
266 static int block_avg_cycles_entry(struct perf_hpp_fmt *fmt,
267 				  struct perf_hpp *hpp,
268 				  struct hist_entry *he)
269 {
270 	struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
271 	struct block_info *bi = he->block_info;
272 	char cycles_buf[16];
273 
274 	cycles_string(bi->cycles_aggr / bi->num_aggr, cycles_buf,
275 		      sizeof(cycles_buf));
276 
277 	return scnprintf(hpp->buf, hpp->size, "%*s", block_fmt->width,
278 			 cycles_buf);
279 }
280 
281 static int block_range_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
282 			     struct hist_entry *he)
283 {
284 	struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
285 	struct block_info *bi = he->block_info;
286 	char buf[128];
287 	char *start_line, *end_line;
288 
289 	symbol_conf.disable_add2line_warn = true;
290 
291 	start_line = map__srcline(he->ms.map, bi->sym->start + bi->start,
292 				  he->ms.sym);
293 
294 	end_line = map__srcline(he->ms.map, bi->sym->start + bi->end,
295 				he->ms.sym);
296 
297 	if ((start_line != SRCLINE_UNKNOWN) && (end_line != SRCLINE_UNKNOWN)) {
298 		scnprintf(buf, sizeof(buf), "[%s -> %s]",
299 			  start_line, end_line);
300 	} else {
301 		scnprintf(buf, sizeof(buf), "[%7lx -> %7lx]",
302 			  bi->start, bi->end);
303 	}
304 
305 	free_srcline(start_line);
306 	free_srcline(end_line);
307 
308 	return scnprintf(hpp->buf, hpp->size, "%*s", block_fmt->width, buf);
309 }
310 
311 static int block_dso_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
312 			   struct hist_entry *he)
313 {
314 	struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
315 	struct map *map = he->ms.map;
316 
317 	if (map && map->dso) {
318 		return scnprintf(hpp->buf, hpp->size, "%*s", block_fmt->width,
319 				 map->dso->short_name);
320 	}
321 
322 	return scnprintf(hpp->buf, hpp->size, "%*s", block_fmt->width,
323 			 "[unknown]");
324 }
325 
326 static void init_block_header(struct block_fmt *block_fmt)
327 {
328 	struct perf_hpp_fmt *fmt = &block_fmt->fmt;
329 
330 	BUG_ON(block_fmt->idx >= PERF_HPP_REPORT__BLOCK_MAX_INDEX);
331 
332 	block_fmt->header = block_columns[block_fmt->idx].name;
333 	block_fmt->width = block_columns[block_fmt->idx].width;
334 
335 	fmt->header = block_column_header;
336 	fmt->width = block_column_width;
337 }
338 
339 static void hpp_register(struct block_fmt *block_fmt, int idx,
340 			 struct perf_hpp_list *hpp_list)
341 {
342 	struct perf_hpp_fmt *fmt = &block_fmt->fmt;
343 
344 	block_fmt->idx = idx;
345 	INIT_LIST_HEAD(&fmt->list);
346 	INIT_LIST_HEAD(&fmt->sort_list);
347 
348 	switch (idx) {
349 	case PERF_HPP_REPORT__BLOCK_TOTAL_CYCLES_PCT:
350 		fmt->entry = block_total_cycles_pct_entry;
351 		fmt->cmp = block_info__cmp;
352 		fmt->sort = block_total_cycles_pct_sort;
353 		break;
354 	case PERF_HPP_REPORT__BLOCK_LBR_CYCLES:
355 		fmt->entry = block_cycles_lbr_entry;
356 		break;
357 	case PERF_HPP_REPORT__BLOCK_CYCLES_PCT:
358 		fmt->entry = block_cycles_pct_entry;
359 		break;
360 	case PERF_HPP_REPORT__BLOCK_AVG_CYCLES:
361 		fmt->entry = block_avg_cycles_entry;
362 		break;
363 	case PERF_HPP_REPORT__BLOCK_RANGE:
364 		fmt->entry = block_range_entry;
365 		break;
366 	case PERF_HPP_REPORT__BLOCK_DSO:
367 		fmt->entry = block_dso_entry;
368 		break;
369 	default:
370 		return;
371 	}
372 
373 	init_block_header(block_fmt);
374 	perf_hpp_list__column_register(hpp_list, fmt);
375 }
376 
377 static void register_block_columns(struct perf_hpp_list *hpp_list,
378 				   struct block_fmt *block_fmts)
379 {
380 	for (int i = 0; i < PERF_HPP_REPORT__BLOCK_MAX_INDEX; i++)
381 		hpp_register(&block_fmts[i], i, hpp_list);
382 }
383 
384 static void init_block_hist(struct block_hist *bh, struct block_fmt *block_fmts)
385 {
386 	__hists__init(&bh->block_hists, &bh->block_list);
387 	perf_hpp_list__init(&bh->block_list);
388 	bh->block_list.nr_header_lines = 1;
389 
390 	register_block_columns(&bh->block_list, block_fmts);
391 
392 	perf_hpp_list__register_sort_field(&bh->block_list,
393 		&block_fmts[PERF_HPP_REPORT__BLOCK_TOTAL_CYCLES_PCT].fmt);
394 }
395 
396 static void process_block_report(struct hists *hists,
397 				 struct block_report *block_report,
398 				 u64 total_cycles)
399 {
400 	struct rb_node *next = rb_first_cached(&hists->entries);
401 	struct block_hist *bh = &block_report->hist;
402 	struct hist_entry *he;
403 
404 	init_block_hist(bh, block_report->fmts);
405 
406 	while (next) {
407 		he = rb_entry(next, struct hist_entry, rb_node);
408 		block_info__process_sym(he, bh, &block_report->cycles,
409 					total_cycles);
410 		next = rb_next(&he->rb_node);
411 	}
412 
413 	for (int i = 0; i < PERF_HPP_REPORT__BLOCK_MAX_INDEX; i++) {
414 		block_report->fmts[i].total_cycles = total_cycles;
415 		block_report->fmts[i].block_cycles = block_report->cycles;
416 	}
417 
418 	hists__output_resort(&bh->block_hists, NULL);
419 }
420 
421 struct block_report *block_info__create_report(struct evlist *evlist,
422 					       u64 total_cycles)
423 {
424 	struct block_report *block_reports;
425 	int nr_hists = evlist->core.nr_entries, i = 0;
426 	struct evsel *pos;
427 
428 	block_reports = calloc(nr_hists, sizeof(struct block_report));
429 	if (!block_reports)
430 		return NULL;
431 
432 	evlist__for_each_entry(evlist, pos) {
433 		struct hists *hists = evsel__hists(pos);
434 
435 		process_block_report(hists, &block_reports[i], total_cycles);
436 		i++;
437 	}
438 
439 	return block_reports;
440 }
441 
442 #ifdef HAVE_SLANG_SUPPORT
443 static int block_hists_browser__title(struct hist_browser *browser, char *bf,
444 				      size_t size)
445 {
446 	struct hists *hists = evsel__hists(browser->block_evsel);
447 	const char *evname = perf_evsel__name(browser->block_evsel);
448 	unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
449 	int ret;
450 
451 	ret = scnprintf(bf, size, "# Samples: %lu", nr_samples);
452 	if (evname)
453 		scnprintf(bf + ret, size -  ret, " of event '%s'", evname);
454 
455 	return 0;
456 }
457 
458 static int block_hists_tui_browse(struct block_hist *bh, struct evsel *evsel,
459 				  float min_percent)
460 {
461 	struct hists *hists = &bh->block_hists;
462 	struct hist_browser *browser;
463 	int key = -1;
464 	static const char help[] =
465 	" q             Quit \n";
466 
467 	browser = hist_browser__new(hists);
468 	if (!browser)
469 		return -1;
470 
471 	browser->block_evsel = evsel;
472 	browser->title = block_hists_browser__title;
473 	browser->min_pcnt = min_percent;
474 
475 	/* reset abort key so that it can get Ctrl-C as a key */
476 	SLang_reset_tty();
477 	SLang_init_tty(0, 0, 0);
478 
479 	while (1) {
480 		key = hist_browser__run(browser, "? - help", true);
481 
482 		switch (key) {
483 		case 'q':
484 			goto out;
485 		case '?':
486 			ui_browser__help_window(&browser->b, help);
487 			break;
488 		default:
489 			break;
490 		}
491 	}
492 
493 out:
494 	hist_browser__delete(browser);
495 	return 0;
496 }
497 #else
498 static int block_hists_tui_browse(struct block_hist *bh __maybe_unused,
499 				  struct evsel *evsel __maybe_unused,
500 				  float min_percent __maybe_unused)
501 {
502 	return 0;
503 }
504 #endif
505 
506 int report__browse_block_hists(struct block_hist *bh, float min_percent,
507 			       struct evsel *evsel)
508 {
509 	int ret;
510 
511 	switch (use_browser) {
512 	case 0:
513 		symbol_conf.report_individual_block = true;
514 		hists__fprintf(&bh->block_hists, true, 0, 0, min_percent,
515 			       stdout, true);
516 		hists__delete_entries(&bh->block_hists);
517 		return 0;
518 	case 1:
519 		symbol_conf.report_individual_block = true;
520 		ret = block_hists_tui_browse(bh, evsel, min_percent);
521 		hists__delete_entries(&bh->block_hists);
522 		return ret;
523 	default:
524 		return -1;
525 	}
526 
527 	return 0;
528 }
529 
530 float block_info__total_cycles_percent(struct hist_entry *he)
531 {
532 	struct block_info *bi = he->block_info;
533 
534 	if (bi->total_cycles)
535 		return bi->cycles * 100.0 / bi->total_cycles;
536 
537 	return 0.0;
538 }
539