Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 #include <stdlib.h>
0003 #include <string.h>
0004 #include <linux/zalloc.h>
0005 #include "block-info.h"
0006 #include "sort.h"
0007 #include "annotate.h"
0008 #include "symbol.h"
0009 #include "dso.h"
0010 #include "map.h"
0011 #include "srcline.h"
0012 #include "evlist.h"
0013 #include "hist.h"
0014 #include "ui/browsers/hists.h"
0015 
0016 static struct block_header_column {
0017     const char *name;
0018     int width;
0019 } block_columns[PERF_HPP_REPORT__BLOCK_MAX_INDEX] = {
0020     [PERF_HPP_REPORT__BLOCK_TOTAL_CYCLES_PCT] = {
0021         .name = "Sampled Cycles%",
0022         .width = 15,
0023     },
0024     [PERF_HPP_REPORT__BLOCK_LBR_CYCLES] = {
0025         .name = "Sampled Cycles",
0026         .width = 14,
0027     },
0028     [PERF_HPP_REPORT__BLOCK_CYCLES_PCT] = {
0029         .name = "Avg Cycles%",
0030         .width = 11,
0031     },
0032     [PERF_HPP_REPORT__BLOCK_AVG_CYCLES] = {
0033         .name = "Avg Cycles",
0034         .width = 10,
0035     },
0036     [PERF_HPP_REPORT__BLOCK_RANGE] = {
0037         .name = "[Program Block Range]",
0038         .width = 70,
0039     },
0040     [PERF_HPP_REPORT__BLOCK_DSO] = {
0041         .name = "Shared Object",
0042         .width = 20,
0043     }
0044 };
0045 
0046 struct block_info *block_info__get(struct block_info *bi)
0047 {
0048     if (bi)
0049         refcount_inc(&bi->refcnt);
0050     return bi;
0051 }
0052 
0053 void block_info__put(struct block_info *bi)
0054 {
0055     if (bi && refcount_dec_and_test(&bi->refcnt))
0056         free(bi);
0057 }
0058 
0059 struct block_info *block_info__new(void)
0060 {
0061     struct block_info *bi = zalloc(sizeof(*bi));
0062 
0063     if (bi)
0064         refcount_set(&bi->refcnt, 1);
0065     return bi;
0066 }
0067 
0068 int64_t __block_info__cmp(struct hist_entry *left, struct hist_entry *right)
0069 {
0070     struct block_info *bi_l = left->block_info;
0071     struct block_info *bi_r = right->block_info;
0072     int cmp;
0073 
0074     if (!bi_l->sym || !bi_r->sym) {
0075         if (!bi_l->sym && !bi_r->sym)
0076             return -1;
0077         else if (!bi_l->sym)
0078             return -1;
0079         else
0080             return 1;
0081     }
0082 
0083     cmp = strcmp(bi_l->sym->name, bi_r->sym->name);
0084     if (cmp)
0085         return cmp;
0086 
0087     if (bi_l->start != bi_r->start)
0088         return (int64_t)(bi_r->start - bi_l->start);
0089 
0090     return (int64_t)(bi_r->end - bi_l->end);
0091 }
0092 
0093 int64_t block_info__cmp(struct perf_hpp_fmt *fmt __maybe_unused,
0094             struct hist_entry *left, struct hist_entry *right)
0095 {
0096     return __block_info__cmp(left, right);
0097 }
0098 
0099 static void init_block_info(struct block_info *bi, struct symbol *sym,
0100                 struct cyc_hist *ch, int offset,
0101                 u64 total_cycles)
0102 {
0103     bi->sym = sym;
0104     bi->start = ch->start;
0105     bi->end = offset;
0106     bi->cycles = ch->cycles;
0107     bi->cycles_aggr = ch->cycles_aggr;
0108     bi->num = ch->num;
0109     bi->num_aggr = ch->num_aggr;
0110     bi->total_cycles = total_cycles;
0111 
0112     memcpy(bi->cycles_spark, ch->cycles_spark,
0113            NUM_SPARKS * sizeof(u64));
0114 }
0115 
0116 int block_info__process_sym(struct hist_entry *he, struct block_hist *bh,
0117                 u64 *block_cycles_aggr, u64 total_cycles)
0118 {
0119     struct annotation *notes;
0120     struct cyc_hist *ch;
0121     static struct addr_location al;
0122     u64 cycles = 0;
0123 
0124     if (!he->ms.map || !he->ms.sym)
0125         return 0;
0126 
0127     memset(&al, 0, sizeof(al));
0128     al.map = he->ms.map;
0129     al.sym = he->ms.sym;
0130 
0131     notes = symbol__annotation(he->ms.sym);
0132     if (!notes || !notes->src || !notes->src->cycles_hist)
0133         return 0;
0134     ch = notes->src->cycles_hist;
0135     for (unsigned int i = 0; i < symbol__size(he->ms.sym); i++) {
0136         if (ch[i].num_aggr) {
0137             struct block_info *bi;
0138             struct hist_entry *he_block;
0139 
0140             bi = block_info__new();
0141             if (!bi)
0142                 return -1;
0143 
0144             init_block_info(bi, he->ms.sym, &ch[i], i,
0145                     total_cycles);
0146             cycles += bi->cycles_aggr / bi->num_aggr;
0147 
0148             he_block = hists__add_entry_block(&bh->block_hists,
0149                               &al, bi);
0150             if (!he_block) {
0151                 block_info__put(bi);
0152                 return -1;
0153             }
0154         }
0155     }
0156 
0157     if (block_cycles_aggr)
0158         *block_cycles_aggr += cycles;
0159 
0160     return 0;
0161 }
0162 
0163 static int block_column_header(struct perf_hpp_fmt *fmt,
0164                    struct perf_hpp *hpp,
0165                    struct hists *hists __maybe_unused,
0166                    int line __maybe_unused,
0167                    int *span __maybe_unused)
0168 {
0169     struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
0170 
0171     return scnprintf(hpp->buf, hpp->size, "%*s", block_fmt->width,
0172              block_fmt->header);
0173 }
0174 
0175 static int block_column_width(struct perf_hpp_fmt *fmt,
0176                   struct perf_hpp *hpp __maybe_unused,
0177                   struct hists *hists __maybe_unused)
0178 {
0179     struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
0180 
0181     return block_fmt->width;
0182 }
0183 
0184 static int color_pct(struct perf_hpp *hpp, int width, double pct)
0185 {
0186 #ifdef HAVE_SLANG_SUPPORT
0187     if (use_browser) {
0188         return __hpp__slsmg_color_printf(hpp, "%*.2f%%",
0189                          width - 1, pct);
0190     }
0191 #endif
0192     return hpp_color_scnprintf(hpp, "%*.2f%%", width - 1, pct);
0193 }
0194 
0195 static int block_total_cycles_pct_entry(struct perf_hpp_fmt *fmt,
0196                     struct perf_hpp *hpp,
0197                     struct hist_entry *he)
0198 {
0199     struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
0200     struct block_info *bi = he->block_info;
0201     double ratio = 0.0;
0202 
0203     if (block_fmt->total_cycles)
0204         ratio = (double)bi->cycles_aggr / (double)block_fmt->total_cycles;
0205 
0206     return color_pct(hpp, block_fmt->width, 100.0 * ratio);
0207 }
0208 
0209 static int64_t block_total_cycles_pct_sort(struct perf_hpp_fmt *fmt,
0210                        struct hist_entry *left,
0211                        struct hist_entry *right)
0212 {
0213     struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
0214     struct block_info *bi_l = left->block_info;
0215     struct block_info *bi_r = right->block_info;
0216     double l, r;
0217 
0218     if (block_fmt->total_cycles) {
0219         l = ((double)bi_l->cycles_aggr /
0220             (double)block_fmt->total_cycles) * 100000.0;
0221         r = ((double)bi_r->cycles_aggr /
0222             (double)block_fmt->total_cycles) * 100000.0;
0223         return (int64_t)l - (int64_t)r;
0224     }
0225 
0226     return 0;
0227 }
0228 
0229 static void cycles_string(u64 cycles, char *buf, int size)
0230 {
0231     if (cycles >= 1000000)
0232         scnprintf(buf, size, "%.1fM", (double)cycles / 1000000.0);
0233     else if (cycles >= 1000)
0234         scnprintf(buf, size, "%.1fK", (double)cycles / 1000.0);
0235     else
0236         scnprintf(buf, size, "%1d", cycles);
0237 }
0238 
0239 static int block_cycles_lbr_entry(struct perf_hpp_fmt *fmt,
0240                   struct perf_hpp *hpp, struct hist_entry *he)
0241 {
0242     struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
0243     struct block_info *bi = he->block_info;
0244     char cycles_buf[16];
0245 
0246     cycles_string(bi->cycles_aggr, cycles_buf, sizeof(cycles_buf));
0247 
0248     return scnprintf(hpp->buf, hpp->size, "%*s", block_fmt->width,
0249              cycles_buf);
0250 }
0251 
0252 static int block_cycles_pct_entry(struct perf_hpp_fmt *fmt,
0253                   struct perf_hpp *hpp, struct hist_entry *he)
0254 {
0255     struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
0256     struct block_info *bi = he->block_info;
0257     double ratio = 0.0;
0258     u64 avg;
0259 
0260     if (block_fmt->block_cycles && bi->num_aggr) {
0261         avg = bi->cycles_aggr / bi->num_aggr;
0262         ratio = (double)avg / (double)block_fmt->block_cycles;
0263     }
0264 
0265     return color_pct(hpp, block_fmt->width, 100.0 * ratio);
0266 }
0267 
0268 static int block_avg_cycles_entry(struct perf_hpp_fmt *fmt,
0269                   struct perf_hpp *hpp,
0270                   struct hist_entry *he)
0271 {
0272     struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
0273     struct block_info *bi = he->block_info;
0274     char cycles_buf[16];
0275 
0276     cycles_string(bi->cycles_aggr / bi->num_aggr, cycles_buf,
0277               sizeof(cycles_buf));
0278 
0279     return scnprintf(hpp->buf, hpp->size, "%*s", block_fmt->width,
0280              cycles_buf);
0281 }
0282 
0283 static int block_range_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
0284                  struct hist_entry *he)
0285 {
0286     struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
0287     struct block_info *bi = he->block_info;
0288     char buf[128];
0289     char *start_line, *end_line;
0290 
0291     symbol_conf.disable_add2line_warn = true;
0292 
0293     start_line = map__srcline(he->ms.map, bi->sym->start + bi->start,
0294                   he->ms.sym);
0295 
0296     end_line = map__srcline(he->ms.map, bi->sym->start + bi->end,
0297                 he->ms.sym);
0298 
0299     if ((strncmp(start_line, SRCLINE_UNKNOWN, strlen(SRCLINE_UNKNOWN)) != 0) &&
0300         (strncmp(end_line, SRCLINE_UNKNOWN, strlen(SRCLINE_UNKNOWN)) != 0)) {
0301         scnprintf(buf, sizeof(buf), "[%s -> %s]",
0302               start_line, end_line);
0303     } else {
0304         scnprintf(buf, sizeof(buf), "[%7lx -> %7lx]",
0305               bi->start, bi->end);
0306     }
0307 
0308     free_srcline(start_line);
0309     free_srcline(end_line);
0310 
0311     return scnprintf(hpp->buf, hpp->size, "%*s", block_fmt->width, buf);
0312 }
0313 
0314 static int block_dso_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
0315                struct hist_entry *he)
0316 {
0317     struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
0318     struct map *map = he->ms.map;
0319 
0320     if (map && map->dso) {
0321         return scnprintf(hpp->buf, hpp->size, "%*s", block_fmt->width,
0322                  map->dso->short_name);
0323     }
0324 
0325     return scnprintf(hpp->buf, hpp->size, "%*s", block_fmt->width,
0326              "[unknown]");
0327 }
0328 
0329 static void init_block_header(struct block_fmt *block_fmt)
0330 {
0331     struct perf_hpp_fmt *fmt = &block_fmt->fmt;
0332 
0333     BUG_ON(block_fmt->idx >= PERF_HPP_REPORT__BLOCK_MAX_INDEX);
0334 
0335     block_fmt->header = block_columns[block_fmt->idx].name;
0336     block_fmt->width = block_columns[block_fmt->idx].width;
0337 
0338     fmt->header = block_column_header;
0339     fmt->width = block_column_width;
0340 }
0341 
0342 static void hpp_register(struct block_fmt *block_fmt, int idx,
0343              struct perf_hpp_list *hpp_list)
0344 {
0345     struct perf_hpp_fmt *fmt = &block_fmt->fmt;
0346 
0347     block_fmt->idx = idx;
0348     INIT_LIST_HEAD(&fmt->list);
0349     INIT_LIST_HEAD(&fmt->sort_list);
0350 
0351     switch (idx) {
0352     case PERF_HPP_REPORT__BLOCK_TOTAL_CYCLES_PCT:
0353         fmt->color = block_total_cycles_pct_entry;
0354         fmt->cmp = block_info__cmp;
0355         fmt->sort = block_total_cycles_pct_sort;
0356         break;
0357     case PERF_HPP_REPORT__BLOCK_LBR_CYCLES:
0358         fmt->entry = block_cycles_lbr_entry;
0359         break;
0360     case PERF_HPP_REPORT__BLOCK_CYCLES_PCT:
0361         fmt->color = block_cycles_pct_entry;
0362         break;
0363     case PERF_HPP_REPORT__BLOCK_AVG_CYCLES:
0364         fmt->entry = block_avg_cycles_entry;
0365         break;
0366     case PERF_HPP_REPORT__BLOCK_RANGE:
0367         fmt->entry = block_range_entry;
0368         break;
0369     case PERF_HPP_REPORT__BLOCK_DSO:
0370         fmt->entry = block_dso_entry;
0371         break;
0372     default:
0373         return;
0374     }
0375 
0376     init_block_header(block_fmt);
0377     perf_hpp_list__column_register(hpp_list, fmt);
0378 }
0379 
0380 static void register_block_columns(struct perf_hpp_list *hpp_list,
0381                    struct block_fmt *block_fmts,
0382                    int *block_hpps, int nr_hpps)
0383 {
0384     for (int i = 0; i < nr_hpps; i++)
0385         hpp_register(&block_fmts[i], block_hpps[i], hpp_list);
0386 }
0387 
0388 static void init_block_hist(struct block_hist *bh, struct block_fmt *block_fmts,
0389                 int *block_hpps, int nr_hpps)
0390 {
0391     __hists__init(&bh->block_hists, &bh->block_list);
0392     perf_hpp_list__init(&bh->block_list);
0393     bh->block_list.nr_header_lines = 1;
0394 
0395     register_block_columns(&bh->block_list, block_fmts,
0396                    block_hpps, nr_hpps);
0397 
0398     /* Sort by the first fmt */
0399     perf_hpp_list__register_sort_field(&bh->block_list, &block_fmts[0].fmt);
0400 }
0401 
0402 static int process_block_report(struct hists *hists,
0403                 struct block_report *block_report,
0404                 u64 total_cycles, int *block_hpps,
0405                 int nr_hpps)
0406 {
0407     struct rb_node *next = rb_first_cached(&hists->entries);
0408     struct block_hist *bh = &block_report->hist;
0409     struct hist_entry *he;
0410 
0411     if (nr_hpps > PERF_HPP_REPORT__BLOCK_MAX_INDEX)
0412         return -1;
0413 
0414     block_report->nr_fmts = nr_hpps;
0415     init_block_hist(bh, block_report->fmts, block_hpps, nr_hpps);
0416 
0417     while (next) {
0418         he = rb_entry(next, struct hist_entry, rb_node);
0419         block_info__process_sym(he, bh, &block_report->cycles,
0420                     total_cycles);
0421         next = rb_next(&he->rb_node);
0422     }
0423 
0424     for (int i = 0; i < nr_hpps; i++) {
0425         block_report->fmts[i].total_cycles = total_cycles;
0426         block_report->fmts[i].block_cycles = block_report->cycles;
0427     }
0428 
0429     hists__output_resort(&bh->block_hists, NULL);
0430     return 0;
0431 }
0432 
0433 struct block_report *block_info__create_report(struct evlist *evlist,
0434                            u64 total_cycles,
0435                            int *block_hpps, int nr_hpps,
0436                            int *nr_reps)
0437 {
0438     struct block_report *block_reports;
0439     int nr_hists = evlist->core.nr_entries, i = 0;
0440     struct evsel *pos;
0441 
0442     block_reports = calloc(nr_hists, sizeof(struct block_report));
0443     if (!block_reports)
0444         return NULL;
0445 
0446     evlist__for_each_entry(evlist, pos) {
0447         struct hists *hists = evsel__hists(pos);
0448 
0449         process_block_report(hists, &block_reports[i], total_cycles,
0450                      block_hpps, nr_hpps);
0451         i++;
0452     }
0453 
0454     *nr_reps = nr_hists;
0455     return block_reports;
0456 }
0457 
0458 void block_info__free_report(struct block_report *reps, int nr_reps)
0459 {
0460     for (int i = 0; i < nr_reps; i++)
0461         hists__delete_entries(&reps[i].hist.block_hists);
0462 
0463     free(reps);
0464 }
0465 
0466 int report__browse_block_hists(struct block_hist *bh, float min_percent,
0467                    struct evsel *evsel, struct perf_env *env,
0468                    struct annotation_options *annotation_opts)
0469 {
0470     int ret;
0471 
0472     switch (use_browser) {
0473     case 0:
0474         symbol_conf.report_individual_block = true;
0475         hists__fprintf(&bh->block_hists, true, 0, 0, min_percent,
0476                    stdout, true);
0477         return 0;
0478     case 1:
0479         symbol_conf.report_individual_block = true;
0480         ret = block_hists_tui_browse(bh, evsel, min_percent,
0481                          env, annotation_opts);
0482         return ret;
0483     default:
0484         return -1;
0485     }
0486 
0487     return 0;
0488 }
0489 
0490 float block_info__total_cycles_percent(struct hist_entry *he)
0491 {
0492     struct block_info *bi = he->block_info;
0493 
0494     if (bi->total_cycles)
0495         return bi->cycles * 100.0 / bi->total_cycles;
0496 
0497     return 0.0;
0498 }