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

source code of linux/tools/perf/util/block-info.c