]> Git Repo - linux.git/blob - tools/perf/util/block-info.c
Linux 6.14-rc3
[linux.git] / tools / perf / util / block-info.c
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
16 static 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         [PERF_HPP_REPORT__BLOCK_BRANCH_COUNTER] = {
45                 .name = "Branch Counter",
46                 .width = 30,
47         }
48 };
49
50 static struct block_info *block_info__new(unsigned int br_cntr_nr)
51 {
52         struct block_info *bi = zalloc(sizeof(struct block_info));
53
54         if (bi && br_cntr_nr) {
55                 bi->br_cntr = calloc(br_cntr_nr, sizeof(u64));
56                 if (!bi->br_cntr) {
57                         free(bi);
58                         return NULL;
59                 }
60         }
61
62         return bi;
63 }
64
65 void block_info__delete(struct block_info *bi)
66 {
67         if (bi)
68                 free(bi->br_cntr);
69         free(bi);
70 }
71
72 int64_t __block_info__cmp(struct hist_entry *left, struct hist_entry *right)
73 {
74         struct block_info *bi_l = left->block_info;
75         struct block_info *bi_r = right->block_info;
76         int cmp;
77
78         if (!bi_l->sym || !bi_r->sym) {
79                 if (!bi_l->sym && !bi_r->sym)
80                         return -1;
81                 else if (!bi_l->sym)
82                         return -1;
83                 else
84                         return 1;
85         }
86
87         cmp = strcmp(bi_l->sym->name, bi_r->sym->name);
88         if (cmp)
89                 return cmp;
90
91         if (bi_l->start != bi_r->start)
92                 return (int64_t)(bi_r->start - bi_l->start);
93
94         return (int64_t)(bi_r->end - bi_l->end);
95 }
96
97 int64_t block_info__cmp(struct perf_hpp_fmt *fmt __maybe_unused,
98                         struct hist_entry *left, struct hist_entry *right)
99 {
100         return __block_info__cmp(left, right);
101 }
102
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)
107 {
108         bi->sym = sym;
109         bi->start = ch->start;
110         bi->end = offset;
111         bi->cycles = ch->cycles;
112         bi->cycles_aggr = ch->cycles_aggr;
113         bi->num = ch->num;
114         bi->num_aggr = ch->num_aggr;
115         bi->total_cycles = total_cycles;
116
117         memcpy(bi->cycles_spark, ch->cycles_spark,
118                NUM_SPARKS * sizeof(u64));
119
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));
124         }
125         bi->evsel = evsel;
126 }
127
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)
131 {
132         struct annotation *notes;
133         struct cyc_hist *ch;
134         static struct addr_location al;
135         u64 cycles = 0;
136
137         if (!he->ms.map || !he->ms.sym)
138                 return 0;
139
140         memset(&al, 0, sizeof(al));
141         al.map = he->ms.map;
142         al.sym = he->ms.sym;
143
144         notes = symbol__annotation(he->ms.sym);
145         if (!notes || !notes->branch || !notes->branch->cycles_hist)
146                 return 0;
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;
152
153                         bi = block_info__new(br_cntr_nr);
154                         if (!bi)
155                                 return -1;
156
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;
162
163                         he_block = hists__add_entry_block(&bh->block_hists,
164                                                           &al, bi);
165                         if (!he_block) {
166                                 block_info__delete(bi);
167                                 return -1;
168                         }
169                 }
170         }
171
172         if (block_cycles_aggr)
173                 *block_cycles_aggr += cycles;
174
175         return 0;
176 }
177
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)
183 {
184         struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
185
186         return scnprintf(hpp->buf, hpp->size, "%*s", block_fmt->width,
187                          block_fmt->header);
188 }
189
190 static int block_column_width(struct perf_hpp_fmt *fmt,
191                               struct perf_hpp *hpp __maybe_unused,
192                               struct hists *hists __maybe_unused)
193 {
194         struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
195
196         return block_fmt->width;
197 }
198
199 static int color_pct(struct perf_hpp *hpp, int width, double pct)
200 {
201 #ifdef HAVE_SLANG_SUPPORT
202         if (use_browser) {
203                 return __hpp__slsmg_color_printf(hpp, "%*.2f%%",
204                                                  width - 1, pct);
205         }
206 #endif
207         return hpp_color_scnprintf(hpp, "%*.2f%%", width - 1, pct);
208 }
209
210 static int block_total_cycles_pct_entry(struct perf_hpp_fmt *fmt,
211                                         struct perf_hpp *hpp,
212                                         struct hist_entry *he)
213 {
214         struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
215         struct block_info *bi = he->block_info;
216         double ratio = 0.0;
217
218         if (block_fmt->total_cycles)
219                 ratio = (double)bi->cycles_aggr / (double)block_fmt->total_cycles;
220
221         return color_pct(hpp, block_fmt->width, 100.0 * ratio);
222 }
223
224 static int64_t block_total_cycles_pct_sort(struct perf_hpp_fmt *fmt,
225                                            struct hist_entry *left,
226                                            struct hist_entry *right)
227 {
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;
231         double l, r;
232
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;
239         }
240
241         return 0;
242 }
243
244 static void cycles_string(u64 cycles, char *buf, int size)
245 {
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);
250         else
251                 scnprintf(buf, size, "%1d", cycles);
252 }
253
254 static int block_cycles_lbr_entry(struct perf_hpp_fmt *fmt,
255                                   struct perf_hpp *hpp, struct hist_entry *he)
256 {
257         struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
258         struct block_info *bi = he->block_info;
259         char cycles_buf[16];
260
261         cycles_string(bi->cycles_aggr, cycles_buf, sizeof(cycles_buf));
262
263         return scnprintf(hpp->buf, hpp->size, "%*s", block_fmt->width,
264                          cycles_buf);
265 }
266
267 static int block_cycles_pct_entry(struct perf_hpp_fmt *fmt,
268                                   struct perf_hpp *hpp, 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         double ratio = 0.0;
273         u64 avg;
274
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;
278         }
279
280         return color_pct(hpp, block_fmt->width, 100.0 * ratio);
281 }
282
283 static int block_avg_cycles_entry(struct perf_hpp_fmt *fmt,
284                                   struct perf_hpp *hpp,
285                                   struct hist_entry *he)
286 {
287         struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
288         struct block_info *bi = he->block_info;
289         char cycles_buf[16];
290
291         cycles_string(bi->cycles_aggr / bi->num_aggr, cycles_buf,
292                       sizeof(cycles_buf));
293
294         return scnprintf(hpp->buf, hpp->size, "%*s", block_fmt->width,
295                          cycles_buf);
296 }
297
298 static int block_range_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
299                              struct hist_entry *he)
300 {
301         struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
302         struct block_info *bi = he->block_info;
303         char buf[128];
304         char *start_line, *end_line;
305
306         symbol_conf.disable_add2line_warn = true;
307
308         start_line = map__srcline(he->ms.map, bi->sym->start + bi->start,
309                                   he->ms.sym);
310
311         end_line = map__srcline(he->ms.map, bi->sym->start + bi->end,
312                                 he->ms.sym);
313
314         if (start_line != SRCLINE_UNKNOWN &&
315             end_line != SRCLINE_UNKNOWN) {
316                 scnprintf(buf, sizeof(buf), "[%s -> %s]",
317                           start_line, end_line);
318         } else {
319                 scnprintf(buf, sizeof(buf), "[%7lx -> %7lx]",
320                           bi->start, bi->end);
321         }
322
323         zfree_srcline(&start_line);
324         zfree_srcline(&end_line);
325
326         return scnprintf(hpp->buf, hpp->size, "%*s", block_fmt->width, buf);
327 }
328
329 static int block_dso_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
330                            struct hist_entry *he)
331 {
332         struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
333         struct map *map = he->ms.map;
334
335         if (map && map__dso(map)) {
336                 return scnprintf(hpp->buf, hpp->size, "%*s", block_fmt->width,
337                                  dso__short_name(map__dso(map)));
338         }
339
340         return scnprintf(hpp->buf, hpp->size, "%*s", block_fmt->width,
341                          "[unknown]");
342 }
343
344 static void init_block_header(struct block_fmt *block_fmt)
345 {
346         struct perf_hpp_fmt *fmt = &block_fmt->fmt;
347
348         BUG_ON(block_fmt->idx >= PERF_HPP_REPORT__BLOCK_MAX_INDEX);
349
350         block_fmt->header = block_columns[block_fmt->idx].name;
351         block_fmt->width = block_columns[block_fmt->idx].width;
352
353         fmt->header = block_column_header;
354         fmt->width = block_column_width;
355 }
356
357 static int block_branch_counter_entry(struct perf_hpp_fmt *fmt,
358                                       struct perf_hpp *hpp,
359                                       struct hist_entry *he)
360 {
361         struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
362         struct block_info *bi = he->block_info;
363         char *buf;
364         int ret;
365
366         if (annotation_br_cntr_entry(&buf, bi->br_cntr_nr, bi->br_cntr,
367                                      bi->num_aggr, bi->evsel))
368                 return 0;
369
370         ret = scnprintf(hpp->buf, hpp->size, "%*s", block_fmt->width, buf);
371         free(buf);
372         return ret;
373 }
374
375 static void hpp_register(struct block_fmt *block_fmt, int idx,
376                          struct perf_hpp_list *hpp_list)
377 {
378         struct perf_hpp_fmt *fmt = &block_fmt->fmt;
379
380         block_fmt->idx = idx;
381         INIT_LIST_HEAD(&fmt->list);
382         INIT_LIST_HEAD(&fmt->sort_list);
383
384         switch (idx) {
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;
389                 break;
390         case PERF_HPP_REPORT__BLOCK_LBR_CYCLES:
391                 fmt->entry = block_cycles_lbr_entry;
392                 break;
393         case PERF_HPP_REPORT__BLOCK_CYCLES_PCT:
394                 fmt->color = block_cycles_pct_entry;
395                 break;
396         case PERF_HPP_REPORT__BLOCK_AVG_CYCLES:
397                 fmt->entry = block_avg_cycles_entry;
398                 break;
399         case PERF_HPP_REPORT__BLOCK_RANGE:
400                 fmt->entry = block_range_entry;
401                 break;
402         case PERF_HPP_REPORT__BLOCK_DSO:
403                 fmt->entry = block_dso_entry;
404                 break;
405         case PERF_HPP_REPORT__BLOCK_BRANCH_COUNTER:
406                 fmt->entry = block_branch_counter_entry;
407                 break;
408         default:
409                 return;
410         }
411
412         init_block_header(block_fmt);
413         perf_hpp_list__column_register(hpp_list, fmt);
414 }
415
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)
419 {
420         for (int i = 0; i < nr_hpps; i++)
421                 hpp_register(&block_fmts[i], block_hpps[i], hpp_list);
422 }
423
424 static void init_block_hist(struct block_hist *bh, struct block_fmt *block_fmts,
425                             int *block_hpps, int nr_hpps)
426 {
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;
430
431         register_block_columns(&bh->block_list, block_fmts,
432                                block_hpps, nr_hpps);
433
434         /* Sort by the first fmt */
435         perf_hpp_list__register_sort_field(&bh->block_list, &block_fmts[0].fmt);
436 }
437
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)
442 {
443         struct rb_node *next = rb_first_cached(&hists->entries);
444         struct block_hist *bh = &block_report->hist;
445         struct hist_entry *he;
446
447         if (nr_hpps > PERF_HPP_REPORT__BLOCK_MAX_INDEX)
448                 return -1;
449
450         block_report->nr_fmts = nr_hpps;
451         init_block_hist(bh, block_report->fmts, block_hpps, nr_hpps);
452
453         while (next) {
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);
458         }
459
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;
463         }
464
465         hists__output_resort(&bh->block_hists, NULL);
466         return 0;
467 }
468
469 struct block_report *block_info__create_report(struct evlist *evlist,
470                                                u64 total_cycles,
471                                                int *block_hpps, int nr_hpps,
472                                                int *nr_reps)
473 {
474         struct block_report *block_reports;
475         int nr_hists = evlist->core.nr_entries, i = 0;
476         struct evsel *pos;
477
478         block_reports = calloc(nr_hists, sizeof(struct block_report));
479         if (!block_reports)
480                 return NULL;
481
482         evlist__for_each_entry(evlist, pos) {
483                 struct hists *hists = evsel__hists(pos);
484
485                 process_block_report(hists, &block_reports[i], total_cycles,
486                                      block_hpps, nr_hpps, evlist->nr_br_cntr);
487                 i++;
488         }
489
490         *nr_reps = nr_hists;
491         return block_reports;
492 }
493
494 void block_info__free_report(struct block_report *reps, int nr_reps)
495 {
496         for (int i = 0; i < nr_reps; i++)
497                 hists__delete_entries(&reps[i].hist.block_hists);
498
499         free(reps);
500 }
501
502 int report__browse_block_hists(struct block_hist *bh, float min_percent,
503                                struct evsel *evsel, struct perf_env *env)
504 {
505         int ret;
506
507         switch (use_browser) {
508         case 0:
509                 symbol_conf.report_individual_block = true;
510                 hists__fprintf(&bh->block_hists, true, 0, 0, min_percent,
511                                stdout, true);
512                 return 0;
513         case 1:
514                 symbol_conf.report_individual_block = true;
515                 ret = block_hists_tui_browse(bh, evsel, min_percent, env);
516                 return ret;
517         default:
518                 return -1;
519         }
520
521         return 0;
522 }
523
524 float block_info__total_cycles_percent(struct hist_entry *he)
525 {
526         struct block_info *bi = he->block_info;
527
528         if (bi->total_cycles)
529                 return bi->cycles * 100.0 / bi->total_cycles;
530
531         return 0.0;
532 }
This page took 0.058699 seconds and 4 git commands to generate.