0001
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
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 }