]> asedeno.scripts.mit.edu Git - linux.git/blob - tools/perf/util/block-info.c
perf report: Sort by sampled cycles percent per block for tui
[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 "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 }