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