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",
46 struct block_info
*block_info__get(struct block_info
*bi
)
49 refcount_inc(&bi
->refcnt
);
53 void block_info__put(struct block_info
*bi
)
55 if (bi
&& refcount_dec_and_test(&bi
->refcnt
))
59 struct block_info
*block_info__new(void)
61 struct block_info
*bi
= zalloc(sizeof(*bi
));
64 refcount_set(&bi
->refcnt
, 1);
68 int64_t __block_info__cmp(struct hist_entry
*left
, struct hist_entry
*right
)
70 struct block_info
*bi_l
= left
->block_info
;
71 struct block_info
*bi_r
= right
->block_info
;
74 if (!bi_l
->sym
|| !bi_r
->sym
) {
75 if (!bi_l
->sym
&& !bi_r
->sym
)
83 cmp
= strcmp(bi_l
->sym
->name
, bi_r
->sym
->name
);
87 if (bi_l
->start
!= bi_r
->start
)
88 return (int64_t)(bi_r
->start
- bi_l
->start
);
90 return (int64_t)(bi_r
->end
- bi_l
->end
);
93 int64_t block_info__cmp(struct perf_hpp_fmt
*fmt __maybe_unused
,
94 struct hist_entry
*left
, struct hist_entry
*right
)
96 return __block_info__cmp(left
, right
);
99 static void init_block_info(struct block_info
*bi
, struct symbol
*sym
,
100 struct cyc_hist
*ch
, int offset
,
104 bi
->start
= ch
->start
;
106 bi
->cycles
= ch
->cycles
;
107 bi
->cycles_aggr
= ch
->cycles_aggr
;
109 bi
->num_aggr
= ch
->num_aggr
;
110 bi
->total_cycles
= total_cycles
;
112 memcpy(bi
->cycles_spark
, ch
->cycles_spark
,
113 NUM_SPARKS
* sizeof(u64
));
116 int block_info__process_sym(struct hist_entry
*he
, struct block_hist
*bh
,
117 u64
*block_cycles_aggr
, u64 total_cycles
)
119 struct annotation
*notes
;
121 static struct addr_location al
;
124 if (!he
->ms
.map
|| !he
->ms
.sym
)
127 memset(&al
, 0, sizeof(al
));
131 notes
= symbol__annotation(he
->ms
.sym
);
132 if (!notes
|| !notes
->src
|| !notes
->src
->cycles_hist
)
134 ch
= notes
->src
->cycles_hist
;
135 for (unsigned int i
= 0; i
< symbol__size(he
->ms
.sym
); i
++) {
136 if (ch
[i
].num_aggr
) {
137 struct block_info
*bi
;
138 struct hist_entry
*he_block
;
140 bi
= block_info__new();
144 init_block_info(bi
, he
->ms
.sym
, &ch
[i
], i
,
146 cycles
+= bi
->cycles_aggr
/ bi
->num_aggr
;
148 he_block
= hists__add_entry_block(&bh
->block_hists
,
157 if (block_cycles_aggr
)
158 *block_cycles_aggr
+= cycles
;
163 static 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
)
169 struct block_fmt
*block_fmt
= container_of(fmt
, struct block_fmt
, fmt
);
171 return scnprintf(hpp
->buf
, hpp
->size
, "%*s", block_fmt
->width
,
175 static int block_column_width(struct perf_hpp_fmt
*fmt
,
176 struct perf_hpp
*hpp __maybe_unused
,
177 struct hists
*hists __maybe_unused
)
179 struct block_fmt
*block_fmt
= container_of(fmt
, struct block_fmt
, fmt
);
181 return block_fmt
->width
;
184 static int color_pct(struct perf_hpp
*hpp
, int width
, double pct
)
186 #ifdef HAVE_SLANG_SUPPORT
188 return __hpp__slsmg_color_printf(hpp
, "%*.2f%%",
192 return hpp_color_scnprintf(hpp
, "%*.2f%%", width
- 1, pct
);
195 static int block_total_cycles_pct_entry(struct perf_hpp_fmt
*fmt
,
196 struct perf_hpp
*hpp
,
197 struct hist_entry
*he
)
199 struct block_fmt
*block_fmt
= container_of(fmt
, struct block_fmt
, fmt
);
200 struct block_info
*bi
= he
->block_info
;
203 if (block_fmt
->total_cycles
)
204 ratio
= (double)bi
->cycles
/ (double)block_fmt
->total_cycles
;
206 return color_pct(hpp
, block_fmt
->width
, 100.0 * ratio
);
209 static int64_t block_total_cycles_pct_sort(struct perf_hpp_fmt
*fmt
,
210 struct hist_entry
*left
,
211 struct hist_entry
*right
)
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
;
218 if (block_fmt
->total_cycles
) {
219 l
= ((double)bi_l
->cycles
/
220 (double)block_fmt
->total_cycles
) * 100000.0;
221 r
= ((double)bi_r
->cycles
/
222 (double)block_fmt
->total_cycles
) * 100000.0;
223 return (int64_t)l
- (int64_t)r
;
229 static void cycles_string(u64 cycles
, char *buf
, int size
)
231 if (cycles
>= 1000000)
232 scnprintf(buf
, size
, "%.1fM", (double)cycles
/ 1000000.0);
233 else if (cycles
>= 1000)
234 scnprintf(buf
, size
, "%.1fK", (double)cycles
/ 1000.0);
236 scnprintf(buf
, size
, "%1d", cycles
);
239 static int block_cycles_lbr_entry(struct perf_hpp_fmt
*fmt
,
240 struct perf_hpp
*hpp
, struct hist_entry
*he
)
242 struct block_fmt
*block_fmt
= container_of(fmt
, struct block_fmt
, fmt
);
243 struct block_info
*bi
= he
->block_info
;
246 cycles_string(bi
->cycles_aggr
, cycles_buf
, sizeof(cycles_buf
));
248 return scnprintf(hpp
->buf
, hpp
->size
, "%*s", block_fmt
->width
,
252 static int block_cycles_pct_entry(struct perf_hpp_fmt
*fmt
,
253 struct perf_hpp
*hpp
, struct hist_entry
*he
)
255 struct block_fmt
*block_fmt
= container_of(fmt
, struct block_fmt
, fmt
);
256 struct block_info
*bi
= he
->block_info
;
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
;
265 return color_pct(hpp
, block_fmt
->width
, 100.0 * ratio
);
268 static int block_avg_cycles_entry(struct perf_hpp_fmt
*fmt
,
269 struct perf_hpp
*hpp
,
270 struct hist_entry
*he
)
272 struct block_fmt
*block_fmt
= container_of(fmt
, struct block_fmt
, fmt
);
273 struct block_info
*bi
= he
->block_info
;
276 cycles_string(bi
->cycles_aggr
/ bi
->num_aggr
, cycles_buf
,
279 return scnprintf(hpp
->buf
, hpp
->size
, "%*s", block_fmt
->width
,
283 static int block_range_entry(struct perf_hpp_fmt
*fmt
, struct perf_hpp
*hpp
,
284 struct hist_entry
*he
)
286 struct block_fmt
*block_fmt
= container_of(fmt
, struct block_fmt
, fmt
);
287 struct block_info
*bi
= he
->block_info
;
289 char *start_line
, *end_line
;
291 symbol_conf
.disable_add2line_warn
= true;
293 start_line
= map__srcline(he
->ms
.map
, bi
->sym
->start
+ bi
->start
,
296 end_line
= map__srcline(he
->ms
.map
, bi
->sym
->start
+ bi
->end
,
299 if ((strncmp(start_line
, SRCLINE_UNKNOWN
, strlen(SRCLINE_UNKNOWN
)) != 0) &&
300 (strncmp(end_line
, SRCLINE_UNKNOWN
, strlen(SRCLINE_UNKNOWN
)) != 0)) {
301 scnprintf(buf
, sizeof(buf
), "[%s -> %s]",
302 start_line
, end_line
);
304 scnprintf(buf
, sizeof(buf
), "[%7lx -> %7lx]",
308 free_srcline(start_line
);
309 free_srcline(end_line
);
311 return scnprintf(hpp
->buf
, hpp
->size
, "%*s", block_fmt
->width
, buf
);
314 static int block_dso_entry(struct perf_hpp_fmt
*fmt
, struct perf_hpp
*hpp
,
315 struct hist_entry
*he
)
317 struct block_fmt
*block_fmt
= container_of(fmt
, struct block_fmt
, fmt
);
318 struct map
*map
= he
->ms
.map
;
320 if (map
&& map
->dso
) {
321 return scnprintf(hpp
->buf
, hpp
->size
, "%*s", block_fmt
->width
,
322 map
->dso
->short_name
);
325 return scnprintf(hpp
->buf
, hpp
->size
, "%*s", block_fmt
->width
,
329 static void init_block_header(struct block_fmt
*block_fmt
)
331 struct perf_hpp_fmt
*fmt
= &block_fmt
->fmt
;
333 BUG_ON(block_fmt
->idx
>= PERF_HPP_REPORT__BLOCK_MAX_INDEX
);
335 block_fmt
->header
= block_columns
[block_fmt
->idx
].name
;
336 block_fmt
->width
= block_columns
[block_fmt
->idx
].width
;
338 fmt
->header
= block_column_header
;
339 fmt
->width
= block_column_width
;
342 static void hpp_register(struct block_fmt
*block_fmt
, int idx
,
343 struct perf_hpp_list
*hpp_list
)
345 struct perf_hpp_fmt
*fmt
= &block_fmt
->fmt
;
347 block_fmt
->idx
= idx
;
348 INIT_LIST_HEAD(&fmt
->list
);
349 INIT_LIST_HEAD(&fmt
->sort_list
);
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
;
357 case PERF_HPP_REPORT__BLOCK_LBR_CYCLES
:
358 fmt
->entry
= block_cycles_lbr_entry
;
360 case PERF_HPP_REPORT__BLOCK_CYCLES_PCT
:
361 fmt
->color
= block_cycles_pct_entry
;
363 case PERF_HPP_REPORT__BLOCK_AVG_CYCLES
:
364 fmt
->entry
= block_avg_cycles_entry
;
366 case PERF_HPP_REPORT__BLOCK_RANGE
:
367 fmt
->entry
= block_range_entry
;
369 case PERF_HPP_REPORT__BLOCK_DSO
:
370 fmt
->entry
= block_dso_entry
;
376 init_block_header(block_fmt
);
377 perf_hpp_list__column_register(hpp_list
, fmt
);
380 static void register_block_columns(struct perf_hpp_list
*hpp_list
,
381 struct block_fmt
*block_fmts
,
382 int *block_hpps
, int nr_hpps
)
384 for (int i
= 0; i
< nr_hpps
; i
++)
385 hpp_register(&block_fmts
[i
], block_hpps
[i
], hpp_list
);
388 static void init_block_hist(struct block_hist
*bh
, struct block_fmt
*block_fmts
,
389 int *block_hpps
, int nr_hpps
)
391 __hists__init(&bh
->block_hists
, &bh
->block_list
);
392 perf_hpp_list__init(&bh
->block_list
);
393 bh
->block_list
.nr_header_lines
= 1;
395 register_block_columns(&bh
->block_list
, block_fmts
,
396 block_hpps
, nr_hpps
);
398 /* Sort by the first fmt */
399 perf_hpp_list__register_sort_field(&bh
->block_list
, &block_fmts
[0].fmt
);
402 static int process_block_report(struct hists
*hists
,
403 struct block_report
*block_report
,
404 u64 total_cycles
, int *block_hpps
,
407 struct rb_node
*next
= rb_first_cached(&hists
->entries
);
408 struct block_hist
*bh
= &block_report
->hist
;
409 struct hist_entry
*he
;
411 if (nr_hpps
> PERF_HPP_REPORT__BLOCK_MAX_INDEX
)
414 block_report
->nr_fmts
= nr_hpps
;
415 init_block_hist(bh
, block_report
->fmts
, block_hpps
, nr_hpps
);
418 he
= rb_entry(next
, struct hist_entry
, rb_node
);
419 block_info__process_sym(he
, bh
, &block_report
->cycles
,
421 next
= rb_next(&he
->rb_node
);
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
;
429 hists__output_resort(&bh
->block_hists
, NULL
);
433 struct block_report
*block_info__create_report(struct evlist
*evlist
,
435 int *block_hpps
, int nr_hpps
,
438 struct block_report
*block_reports
;
439 int nr_hists
= evlist
->core
.nr_entries
, i
= 0;
442 block_reports
= calloc(nr_hists
, sizeof(struct block_report
));
446 evlist__for_each_entry(evlist
, pos
) {
447 struct hists
*hists
= evsel__hists(pos
);
449 process_block_report(hists
, &block_reports
[i
], total_cycles
,
450 block_hpps
, nr_hpps
);
455 return block_reports
;
458 void block_info__free_report(struct block_report
*reps
, int nr_reps
)
460 for (int i
= 0; i
< nr_reps
; i
++)
461 hists__delete_entries(&reps
[i
].hist
.block_hists
);
466 int report__browse_block_hists(struct block_hist
*bh
, float min_percent
,
467 struct evsel
*evsel
, struct perf_env
*env
,
468 struct annotation_options
*annotation_opts
)
472 switch (use_browser
) {
474 symbol_conf
.report_individual_block
= true;
475 hists__fprintf(&bh
->block_hists
, true, 0, 0, min_percent
,
479 symbol_conf
.report_individual_block
= true;
480 ret
= block_hists_tui_browse(bh
, evsel
, min_percent
,
481 env
, annotation_opts
);
490 float block_info__total_cycles_percent(struct hist_entry
*he
)
492 struct block_info
*bi
= he
->block_info
;
494 if (bi
->total_cycles
)
495 return bi
->cycles
* 100.0 / bi
->total_cycles
;