1 // SPDX-License-Identifier: GPL-2.0
4 #include <linux/zalloc.h>
5 #include "block-info.h"
14 #include "ui/browsers/hists.h"
16 static struct block_header_column
{
19 } block_columns
[PERF_HPP_REPORT__BLOCK_MAX_INDEX
] = {
20 [PERF_HPP_REPORT__BLOCK_TOTAL_CYCLES_PCT
] = {
21 .name
= "Sampled Cycles%",
24 [PERF_HPP_REPORT__BLOCK_LBR_CYCLES
] = {
25 .name
= "Sampled Cycles",
28 [PERF_HPP_REPORT__BLOCK_CYCLES_PCT
] = {
29 .name
= "Avg Cycles%",
32 [PERF_HPP_REPORT__BLOCK_AVG_CYCLES
] = {
36 [PERF_HPP_REPORT__BLOCK_RANGE
] = {
37 .name
= "[Program Block Range]",
40 [PERF_HPP_REPORT__BLOCK_DSO
] = {
41 .name
= "Shared Object",
44 [PERF_HPP_REPORT__BLOCK_BRANCH_COUNTER
] = {
45 .name
= "Branch Counter",
50 static struct block_info
*block_info__new(unsigned int br_cntr_nr
)
52 struct block_info
*bi
= zalloc(sizeof(struct block_info
));
54 if (bi
&& br_cntr_nr
) {
55 bi
->br_cntr
= calloc(br_cntr_nr
, sizeof(u64
));
65 void block_info__delete(struct block_info
*bi
)
72 int64_t __block_info__cmp(struct hist_entry
*left
, struct hist_entry
*right
)
74 struct block_info
*bi_l
= left
->block_info
;
75 struct block_info
*bi_r
= right
->block_info
;
78 if (!bi_l
->sym
|| !bi_r
->sym
) {
79 if (!bi_l
->sym
&& !bi_r
->sym
)
87 cmp
= strcmp(bi_l
->sym
->name
, bi_r
->sym
->name
);
91 if (bi_l
->start
!= bi_r
->start
)
92 return (int64_t)(bi_r
->start
- bi_l
->start
);
94 return (int64_t)(bi_r
->end
- bi_l
->end
);
97 int64_t block_info__cmp(struct perf_hpp_fmt
*fmt __maybe_unused
,
98 struct hist_entry
*left
, struct hist_entry
*right
)
100 return __block_info__cmp(left
, right
);
103 static void init_block_info(struct block_info
*bi
, struct symbol
*sym
,
104 struct cyc_hist
*ch
, int offset
,
105 u64 total_cycles
, unsigned int br_cntr_nr
,
106 u64
*br_cntr
, struct evsel
*evsel
)
109 bi
->start
= ch
->start
;
111 bi
->cycles
= ch
->cycles
;
112 bi
->cycles_aggr
= ch
->cycles_aggr
;
114 bi
->num_aggr
= ch
->num_aggr
;
115 bi
->total_cycles
= total_cycles
;
117 memcpy(bi
->cycles_spark
, ch
->cycles_spark
,
118 NUM_SPARKS
* sizeof(u64
));
120 if (br_cntr
&& br_cntr_nr
) {
121 bi
->br_cntr_nr
= br_cntr_nr
;
122 memcpy(bi
->br_cntr
, &br_cntr
[offset
* br_cntr_nr
],
123 br_cntr_nr
* sizeof(u64
));
128 int block_info__process_sym(struct hist_entry
*he
, struct block_hist
*bh
,
129 u64
*block_cycles_aggr
, u64 total_cycles
,
130 unsigned int br_cntr_nr
)
132 struct annotation
*notes
;
134 static struct addr_location al
;
137 if (!he
->ms
.map
|| !he
->ms
.sym
)
140 memset(&al
, 0, sizeof(al
));
144 notes
= symbol__annotation(he
->ms
.sym
);
145 if (!notes
|| !notes
->branch
|| !notes
->branch
->cycles_hist
)
147 ch
= notes
->branch
->cycles_hist
;
148 for (unsigned int i
= 0; i
< symbol__size(he
->ms
.sym
); i
++) {
149 if (ch
[i
].num_aggr
) {
150 struct block_info
*bi
;
151 struct hist_entry
*he_block
;
153 bi
= block_info__new(br_cntr_nr
);
157 init_block_info(bi
, he
->ms
.sym
, &ch
[i
], i
,
158 total_cycles
, br_cntr_nr
,
159 notes
->branch
->br_cntr
,
160 hists_to_evsel(he
->hists
));
161 cycles
+= bi
->cycles_aggr
/ bi
->num_aggr
;
163 he_block
= hists__add_entry_block(&bh
->block_hists
,
166 block_info__delete(bi
);
172 if (block_cycles_aggr
)
173 *block_cycles_aggr
+= cycles
;
178 static int block_column_header(struct perf_hpp_fmt
*fmt
,
179 struct perf_hpp
*hpp
,
180 struct hists
*hists __maybe_unused
,
181 int line __maybe_unused
,
182 int *span __maybe_unused
)
184 struct block_fmt
*block_fmt
= container_of(fmt
, struct block_fmt
, fmt
);
186 return scnprintf(hpp
->buf
, hpp
->size
, "%*s", block_fmt
->width
,
190 static int block_column_width(struct perf_hpp_fmt
*fmt
,
191 struct perf_hpp
*hpp __maybe_unused
,
192 struct hists
*hists __maybe_unused
)
194 struct block_fmt
*block_fmt
= container_of(fmt
, struct block_fmt
, fmt
);
196 return block_fmt
->width
;
199 static int color_pct(struct perf_hpp
*hpp
, int width
, double pct
)
201 #ifdef HAVE_SLANG_SUPPORT
203 return __hpp__slsmg_color_printf(hpp
, "%*.2f%%",
207 return hpp_color_scnprintf(hpp
, "%*.2f%%", width
- 1, pct
);
210 static int block_total_cycles_pct_entry(struct perf_hpp_fmt
*fmt
,
211 struct perf_hpp
*hpp
,
212 struct hist_entry
*he
)
214 struct block_fmt
*block_fmt
= container_of(fmt
, struct block_fmt
, fmt
);
215 struct block_info
*bi
= he
->block_info
;
218 if (block_fmt
->total_cycles
)
219 ratio
= (double)bi
->cycles_aggr
/ (double)block_fmt
->total_cycles
;
221 return color_pct(hpp
, block_fmt
->width
, 100.0 * ratio
);
224 static int64_t block_total_cycles_pct_sort(struct perf_hpp_fmt
*fmt
,
225 struct hist_entry
*left
,
226 struct hist_entry
*right
)
228 struct block_fmt
*block_fmt
= container_of(fmt
, struct block_fmt
, fmt
);
229 struct block_info
*bi_l
= left
->block_info
;
230 struct block_info
*bi_r
= right
->block_info
;
233 if (block_fmt
->total_cycles
) {
234 l
= ((double)bi_l
->cycles_aggr
/
235 (double)block_fmt
->total_cycles
) * 100000.0;
236 r
= ((double)bi_r
->cycles_aggr
/
237 (double)block_fmt
->total_cycles
) * 100000.0;
238 return (int64_t)l
- (int64_t)r
;
244 static void cycles_string(u64 cycles
, char *buf
, int size
)
246 if (cycles
>= 1000000)
247 scnprintf(buf
, size
, "%.1fM", (double)cycles
/ 1000000.0);
248 else if (cycles
>= 1000)
249 scnprintf(buf
, size
, "%.1fK", (double)cycles
/ 1000.0);
251 scnprintf(buf
, size
, "%1d", cycles
);
254 static int block_cycles_lbr_entry(struct perf_hpp_fmt
*fmt
,
255 struct perf_hpp
*hpp
, struct hist_entry
*he
)
257 struct block_fmt
*block_fmt
= container_of(fmt
, struct block_fmt
, fmt
);
258 struct block_info
*bi
= he
->block_info
;
261 cycles_string(bi
->cycles_aggr
, cycles_buf
, sizeof(cycles_buf
));
263 return scnprintf(hpp
->buf
, hpp
->size
, "%*s", block_fmt
->width
,
267 static int block_cycles_pct_entry(struct perf_hpp_fmt
*fmt
,
268 struct perf_hpp
*hpp
, struct hist_entry
*he
)
270 struct block_fmt
*block_fmt
= container_of(fmt
, struct block_fmt
, fmt
);
271 struct block_info
*bi
= he
->block_info
;
275 if (block_fmt
->block_cycles
&& bi
->num_aggr
) {
276 avg
= bi
->cycles_aggr
/ bi
->num_aggr
;
277 ratio
= (double)avg
/ (double)block_fmt
->block_cycles
;
280 return color_pct(hpp
, block_fmt
->width
, 100.0 * ratio
);
283 static int block_avg_cycles_entry(struct perf_hpp_fmt
*fmt
,
284 struct perf_hpp
*hpp
,
285 struct hist_entry
*he
)
287 struct block_fmt
*block_fmt
= container_of(fmt
, struct block_fmt
, fmt
);
288 struct block_info
*bi
= he
->block_info
;
291 cycles_string(bi
->cycles_aggr
/ bi
->num_aggr
, cycles_buf
,
294 return scnprintf(hpp
->buf
, hpp
->size
, "%*s", block_fmt
->width
,
298 static int block_range_entry(struct perf_hpp_fmt
*fmt
, struct perf_hpp
*hpp
,
299 struct hist_entry
*he
)
301 struct block_fmt
*block_fmt
= container_of(fmt
, struct block_fmt
, fmt
);
302 struct block_info
*bi
= he
->block_info
;
304 char *start_line
, *end_line
;
306 symbol_conf
.disable_add2line_warn
= true;
308 start_line
= map__srcline(he
->ms
.map
, bi
->sym
->start
+ bi
->start
,
311 end_line
= map__srcline(he
->ms
.map
, bi
->sym
->start
+ bi
->end
,
314 if (start_line
!= SRCLINE_UNKNOWN
&&
315 end_line
!= SRCLINE_UNKNOWN
) {
316 scnprintf(buf
, sizeof(buf
), "[%s -> %s]",
317 start_line
, end_line
);
319 scnprintf(buf
, sizeof(buf
), "[%7lx -> %7lx]",
323 zfree_srcline(&start_line
);
324 zfree_srcline(&end_line
);
326 return scnprintf(hpp
->buf
, hpp
->size
, "%*s", block_fmt
->width
, buf
);
329 static int block_dso_entry(struct perf_hpp_fmt
*fmt
, struct perf_hpp
*hpp
,
330 struct hist_entry
*he
)
332 struct block_fmt
*block_fmt
= container_of(fmt
, struct block_fmt
, fmt
);
333 struct map
*map
= he
->ms
.map
;
335 if (map
&& map__dso(map
)) {
336 return scnprintf(hpp
->buf
, hpp
->size
, "%*s", block_fmt
->width
,
337 dso__short_name(map__dso(map
)));
340 return scnprintf(hpp
->buf
, hpp
->size
, "%*s", block_fmt
->width
,
344 static void init_block_header(struct block_fmt
*block_fmt
)
346 struct perf_hpp_fmt
*fmt
= &block_fmt
->fmt
;
348 BUG_ON(block_fmt
->idx
>= PERF_HPP_REPORT__BLOCK_MAX_INDEX
);
350 block_fmt
->header
= block_columns
[block_fmt
->idx
].name
;
351 block_fmt
->width
= block_columns
[block_fmt
->idx
].width
;
353 fmt
->header
= block_column_header
;
354 fmt
->width
= block_column_width
;
357 static int block_branch_counter_entry(struct perf_hpp_fmt
*fmt
,
358 struct perf_hpp
*hpp
,
359 struct hist_entry
*he
)
361 struct block_fmt
*block_fmt
= container_of(fmt
, struct block_fmt
, fmt
);
362 struct block_info
*bi
= he
->block_info
;
366 if (annotation_br_cntr_entry(&buf
, bi
->br_cntr_nr
, bi
->br_cntr
,
367 bi
->num_aggr
, bi
->evsel
))
370 ret
= scnprintf(hpp
->buf
, hpp
->size
, "%*s", block_fmt
->width
, buf
);
375 static void hpp_register(struct block_fmt
*block_fmt
, int idx
,
376 struct perf_hpp_list
*hpp_list
)
378 struct perf_hpp_fmt
*fmt
= &block_fmt
->fmt
;
380 block_fmt
->idx
= idx
;
381 INIT_LIST_HEAD(&fmt
->list
);
382 INIT_LIST_HEAD(&fmt
->sort_list
);
385 case PERF_HPP_REPORT__BLOCK_TOTAL_CYCLES_PCT
:
386 fmt
->color
= block_total_cycles_pct_entry
;
387 fmt
->cmp
= block_info__cmp
;
388 fmt
->sort
= block_total_cycles_pct_sort
;
390 case PERF_HPP_REPORT__BLOCK_LBR_CYCLES
:
391 fmt
->entry
= block_cycles_lbr_entry
;
393 case PERF_HPP_REPORT__BLOCK_CYCLES_PCT
:
394 fmt
->color
= block_cycles_pct_entry
;
396 case PERF_HPP_REPORT__BLOCK_AVG_CYCLES
:
397 fmt
->entry
= block_avg_cycles_entry
;
399 case PERF_HPP_REPORT__BLOCK_RANGE
:
400 fmt
->entry
= block_range_entry
;
402 case PERF_HPP_REPORT__BLOCK_DSO
:
403 fmt
->entry
= block_dso_entry
;
405 case PERF_HPP_REPORT__BLOCK_BRANCH_COUNTER
:
406 fmt
->entry
= block_branch_counter_entry
;
412 init_block_header(block_fmt
);
413 perf_hpp_list__column_register(hpp_list
, fmt
);
416 static void register_block_columns(struct perf_hpp_list
*hpp_list
,
417 struct block_fmt
*block_fmts
,
418 int *block_hpps
, int nr_hpps
)
420 for (int i
= 0; i
< nr_hpps
; i
++)
421 hpp_register(&block_fmts
[i
], block_hpps
[i
], hpp_list
);
424 static void init_block_hist(struct block_hist
*bh
, struct block_fmt
*block_fmts
,
425 int *block_hpps
, int nr_hpps
)
427 __hists__init(&bh
->block_hists
, &bh
->block_list
);
428 perf_hpp_list__init(&bh
->block_list
);
429 bh
->block_list
.nr_header_lines
= 1;
431 register_block_columns(&bh
->block_list
, block_fmts
,
432 block_hpps
, nr_hpps
);
434 /* Sort by the first fmt */
435 perf_hpp_list__register_sort_field(&bh
->block_list
, &block_fmts
[0].fmt
);
438 static int process_block_report(struct hists
*hists
,
439 struct block_report
*block_report
,
440 u64 total_cycles
, int *block_hpps
,
441 int nr_hpps
, unsigned int br_cntr_nr
)
443 struct rb_node
*next
= rb_first_cached(&hists
->entries
);
444 struct block_hist
*bh
= &block_report
->hist
;
445 struct hist_entry
*he
;
447 if (nr_hpps
> PERF_HPP_REPORT__BLOCK_MAX_INDEX
)
450 block_report
->nr_fmts
= nr_hpps
;
451 init_block_hist(bh
, block_report
->fmts
, block_hpps
, nr_hpps
);
454 he
= rb_entry(next
, struct hist_entry
, rb_node
);
455 block_info__process_sym(he
, bh
, &block_report
->cycles
,
456 total_cycles
, br_cntr_nr
);
457 next
= rb_next(&he
->rb_node
);
460 for (int i
= 0; i
< nr_hpps
; i
++) {
461 block_report
->fmts
[i
].total_cycles
= total_cycles
;
462 block_report
->fmts
[i
].block_cycles
= block_report
->cycles
;
465 hists__output_resort(&bh
->block_hists
, NULL
);
469 struct block_report
*block_info__create_report(struct evlist
*evlist
,
471 int *block_hpps
, int nr_hpps
,
474 struct block_report
*block_reports
;
475 int nr_hists
= evlist
->core
.nr_entries
, i
= 0;
478 block_reports
= calloc(nr_hists
, sizeof(struct block_report
));
482 evlist__for_each_entry(evlist
, pos
) {
483 struct hists
*hists
= evsel__hists(pos
);
485 process_block_report(hists
, &block_reports
[i
], total_cycles
,
486 block_hpps
, nr_hpps
, evlist
->nr_br_cntr
);
491 return block_reports
;
494 void block_info__free_report(struct block_report
*reps
, int nr_reps
)
496 for (int i
= 0; i
< nr_reps
; i
++)
497 hists__delete_entries(&reps
[i
].hist
.block_hists
);
502 int report__browse_block_hists(struct block_hist
*bh
, float min_percent
,
503 struct evsel
*evsel
, struct perf_env
*env
)
507 switch (use_browser
) {
509 symbol_conf
.report_individual_block
= true;
510 hists__fprintf(&bh
->block_hists
, true, 0, 0, min_percent
,
514 symbol_conf
.report_individual_block
= true;
515 ret
= block_hists_tui_browse(bh
, evsel
, min_percent
, env
);
524 float block_info__total_cycles_percent(struct hist_entry
*he
)
526 struct block_info
*bi
= he
->block_info
;
528 if (bi
->total_cycles
)
529 return bi
->cycles
* 100.0 / bi
->total_cycles
;