533 lines · c
1// SPDX-License-Identifier: GPL-2.02#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 16static 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 50static 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 65void block_info__delete(struct block_info *bi)66{67 if (bi)68 free(bi->br_cntr);69 free(bi);70}71 72int64_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 else84 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 97int64_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 103static 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 128int 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 178static 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 190static 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 199static int color_pct(struct perf_hpp *hpp, int width, double pct)200{201#ifdef HAVE_SLANG_SUPPORT202 if (use_browser) {203 return __hpp__slsmg_color_printf(hpp, "%*.2f%%",204 width - 1, pct);205 }206#endif207 return hpp_color_scnprintf(hpp, "%*.2f%%", width - 1, pct);208}209 210static 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 224static 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 244static 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 else251 scnprintf(buf, size, "%1d", cycles);252}253 254static 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 267static 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 283static 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 298static 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 329static 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 344static 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 357static 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 375static 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 416static 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 424static 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 438static 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 469struct 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 494void 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 502int 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 524float 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}533