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 }; 45 46 struct block_info *block_info__get(struct block_info *bi) 47 { 48 if (bi) 49 refcount_inc(&bi->refcnt); 50 return bi; 51 } 52 53 void block_info__put(struct block_info *bi) 54 { 55 if (bi && refcount_dec_and_test(&bi->refcnt)) 56 free(bi); 57 } 58 59 struct block_info *block_info__new(void) 60 { 61 struct block_info *bi = zalloc(sizeof(*bi)); 62 63 if (bi) 64 refcount_set(&bi->refcnt, 1); 65 return bi; 66 } 67 68 int64_t __block_info__cmp(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 -1; 77 else if (!bi_l->sym) 78 return -1; 79 else 80 return 1; 81 } 82 83 cmp = strcmp(bi_l->sym->name, bi_r->sym->name); 84 if (cmp) 85 return cmp; 86 87 if (bi_l->start != bi_r->start) 88 return (int64_t)(bi_r->start - bi_l->start); 89 90 return (int64_t)(bi_r->end - bi_l->end); 91 } 92 93 int64_t block_info__cmp(struct perf_hpp_fmt *fmt __maybe_unused, 94 struct hist_entry *left, struct hist_entry *right) 95 { 96 return __block_info__cmp(left, right); 97 } 98 99 static void init_block_info(struct block_info *bi, struct symbol *sym, 100 struct cyc_hist *ch, int offset, 101 u64 total_cycles) 102 { 103 bi->sym = sym; 104 bi->start = ch->start; 105 bi->end = offset; 106 bi->cycles = ch->cycles; 107 bi->cycles_aggr = ch->cycles_aggr; 108 bi->num = ch->num; 109 bi->num_aggr = ch->num_aggr; 110 bi->total_cycles = total_cycles; 111 112 memcpy(bi->cycles_spark, ch->cycles_spark, 113 NUM_SPARKS * sizeof(u64)); 114 } 115 116 int block_info__process_sym(struct hist_entry *he, struct block_hist *bh, 117 u64 *block_cycles_aggr, u64 total_cycles) 118 { 119 struct annotation *notes; 120 struct cyc_hist *ch; 121 static struct addr_location al; 122 u64 cycles = 0; 123 124 if (!he->ms.map || !he->ms.sym) 125 return 0; 126 127 memset(&al, 0, sizeof(al)); 128 al.map = he->ms.map; 129 al.sym = he->ms.sym; 130 131 notes = symbol__annotation(he->ms.sym); 132 if (!notes || !notes->src || !notes->src->cycles_hist) 133 return 0; 134 ch = notes->src->cycles_hist; 135 for (unsigned int i = 0; i < symbol__size(he->ms.sym); i++) { 136 if (ch[i].num_aggr) { 137 struct block_info *bi; 138 struct hist_entry *he_block; 139 140 bi = block_info__new(); 141 if (!bi) 142 return -1; 143 144 init_block_info(bi, he->ms.sym, &ch[i], i, 145 total_cycles); 146 cycles += bi->cycles_aggr / bi->num_aggr; 147 148 he_block = hists__add_entry_block(&bh->block_hists, 149 &al, bi); 150 if (!he_block) { 151 block_info__put(bi); 152 return -1; 153 } 154 } 155 } 156 157 if (block_cycles_aggr) 158 *block_cycles_aggr += cycles; 159 160 return 0; 161 } 162 163 static int block_column_header(struct perf_hpp_fmt *fmt, 164 struct perf_hpp *hpp, 165 struct hists *hists __maybe_unused, 166 int line __maybe_unused, 167 int *span __maybe_unused) 168 { 169 struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt); 170 171 return scnprintf(hpp->buf, hpp->size, "%*s", block_fmt->width, 172 block_fmt->header); 173 } 174 175 static int block_column_width(struct perf_hpp_fmt *fmt, 176 struct perf_hpp *hpp __maybe_unused, 177 struct hists *hists __maybe_unused) 178 { 179 struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt); 180 181 return block_fmt->width; 182 } 183 184 static int color_pct(struct perf_hpp *hpp, int width, double pct) 185 { 186 #ifdef HAVE_SLANG_SUPPORT 187 if (use_browser) { 188 return __hpp__slsmg_color_printf(hpp, "%*.2f%%", 189 width - 1, pct); 190 } 191 #endif 192 return hpp_color_scnprintf(hpp, "%*.2f%%", width - 1, pct); 193 } 194 195 static int block_total_cycles_pct_entry(struct perf_hpp_fmt *fmt, 196 struct perf_hpp *hpp, 197 struct hist_entry *he) 198 { 199 struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt); 200 struct block_info *bi = he->block_info; 201 double ratio = 0.0; 202 203 if (block_fmt->total_cycles) 204 ratio = (double)bi->cycles_aggr / (double)block_fmt->total_cycles; 205 206 return color_pct(hpp, block_fmt->width, 100.0 * ratio); 207 } 208 209 static int64_t block_total_cycles_pct_sort(struct perf_hpp_fmt *fmt, 210 struct hist_entry *left, 211 struct hist_entry *right) 212 { 213 struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt); 214 struct block_info *bi_l = left->block_info; 215 struct block_info *bi_r = right->block_info; 216 double l, r; 217 218 if (block_fmt->total_cycles) { 219 l = ((double)bi_l->cycles_aggr / 220 (double)block_fmt->total_cycles) * 100000.0; 221 r = ((double)bi_r->cycles_aggr / 222 (double)block_fmt->total_cycles) * 100000.0; 223 return (int64_t)l - (int64_t)r; 224 } 225 226 return 0; 227 } 228 229 static void cycles_string(u64 cycles, char *buf, int size) 230 { 231 if (cycles >= 1000000) 232 scnprintf(buf, size, "%.1fM", (double)cycles / 1000000.0); 233 else if (cycles >= 1000) 234 scnprintf(buf, size, "%.1fK", (double)cycles / 1000.0); 235 else 236 scnprintf(buf, size, "%1d", cycles); 237 } 238 239 static int block_cycles_lbr_entry(struct perf_hpp_fmt *fmt, 240 struct perf_hpp *hpp, struct hist_entry *he) 241 { 242 struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt); 243 struct block_info *bi = he->block_info; 244 char cycles_buf[16]; 245 246 cycles_string(bi->cycles_aggr, cycles_buf, sizeof(cycles_buf)); 247 248 return scnprintf(hpp->buf, hpp->size, "%*s", block_fmt->width, 249 cycles_buf); 250 } 251 252 static int block_cycles_pct_entry(struct perf_hpp_fmt *fmt, 253 struct perf_hpp *hpp, struct hist_entry *he) 254 { 255 struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt); 256 struct block_info *bi = he->block_info; 257 double ratio = 0.0; 258 u64 avg; 259 260 if (block_fmt->block_cycles && bi->num_aggr) { 261 avg = bi->cycles_aggr / bi->num_aggr; 262 ratio = (double)avg / (double)block_fmt->block_cycles; 263 } 264 265 return color_pct(hpp, block_fmt->width, 100.0 * ratio); 266 } 267 268 static int block_avg_cycles_entry(struct perf_hpp_fmt *fmt, 269 struct perf_hpp *hpp, 270 struct hist_entry *he) 271 { 272 struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt); 273 struct block_info *bi = he->block_info; 274 char cycles_buf[16]; 275 276 cycles_string(bi->cycles_aggr / bi->num_aggr, cycles_buf, 277 sizeof(cycles_buf)); 278 279 return scnprintf(hpp->buf, hpp->size, "%*s", block_fmt->width, 280 cycles_buf); 281 } 282 283 static int block_range_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, 284 struct hist_entry *he) 285 { 286 struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt); 287 struct block_info *bi = he->block_info; 288 char buf[128]; 289 char *start_line, *end_line; 290 291 symbol_conf.disable_add2line_warn = true; 292 293 start_line = map__srcline(he->ms.map, bi->sym->start + bi->start, 294 he->ms.sym); 295 296 end_line = map__srcline(he->ms.map, bi->sym->start + bi->end, 297 he->ms.sym); 298 299 if ((strncmp(start_line, SRCLINE_UNKNOWN, strlen(SRCLINE_UNKNOWN)) != 0) && 300 (strncmp(end_line, SRCLINE_UNKNOWN, strlen(SRCLINE_UNKNOWN)) != 0)) { 301 scnprintf(buf, sizeof(buf), "[%s -> %s]", 302 start_line, end_line); 303 } else { 304 scnprintf(buf, sizeof(buf), "[%7lx -> %7lx]", 305 bi->start, bi->end); 306 } 307 308 free_srcline(start_line); 309 free_srcline(end_line); 310 311 return scnprintf(hpp->buf, hpp->size, "%*s", block_fmt->width, buf); 312 } 313 314 static int block_dso_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, 315 struct hist_entry *he) 316 { 317 struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt); 318 struct map *map = he->ms.map; 319 320 if (map && map__dso(map)) { 321 return scnprintf(hpp->buf, hpp->size, "%*s", block_fmt->width, 322 map__dso(map)->short_name); 323 } 324 325 return scnprintf(hpp->buf, hpp->size, "%*s", block_fmt->width, 326 "[unknown]"); 327 } 328 329 static void init_block_header(struct block_fmt *block_fmt) 330 { 331 struct perf_hpp_fmt *fmt = &block_fmt->fmt; 332 333 BUG_ON(block_fmt->idx >= PERF_HPP_REPORT__BLOCK_MAX_INDEX); 334 335 block_fmt->header = block_columns[block_fmt->idx].name; 336 block_fmt->width = block_columns[block_fmt->idx].width; 337 338 fmt->header = block_column_header; 339 fmt->width = block_column_width; 340 } 341 342 static void hpp_register(struct block_fmt *block_fmt, int idx, 343 struct perf_hpp_list *hpp_list) 344 { 345 struct perf_hpp_fmt *fmt = &block_fmt->fmt; 346 347 block_fmt->idx = idx; 348 INIT_LIST_HEAD(&fmt->list); 349 INIT_LIST_HEAD(&fmt->sort_list); 350 351 switch (idx) { 352 case PERF_HPP_REPORT__BLOCK_TOTAL_CYCLES_PCT: 353 fmt->color = block_total_cycles_pct_entry; 354 fmt->cmp = block_info__cmp; 355 fmt->sort = block_total_cycles_pct_sort; 356 break; 357 case PERF_HPP_REPORT__BLOCK_LBR_CYCLES: 358 fmt->entry = block_cycles_lbr_entry; 359 break; 360 case PERF_HPP_REPORT__BLOCK_CYCLES_PCT: 361 fmt->color = block_cycles_pct_entry; 362 break; 363 case PERF_HPP_REPORT__BLOCK_AVG_CYCLES: 364 fmt->entry = block_avg_cycles_entry; 365 break; 366 case PERF_HPP_REPORT__BLOCK_RANGE: 367 fmt->entry = block_range_entry; 368 break; 369 case PERF_HPP_REPORT__BLOCK_DSO: 370 fmt->entry = block_dso_entry; 371 break; 372 default: 373 return; 374 } 375 376 init_block_header(block_fmt); 377 perf_hpp_list__column_register(hpp_list, fmt); 378 } 379 380 static void register_block_columns(struct perf_hpp_list *hpp_list, 381 struct block_fmt *block_fmts, 382 int *block_hpps, int nr_hpps) 383 { 384 for (int i = 0; i < nr_hpps; i++) 385 hpp_register(&block_fmts[i], block_hpps[i], hpp_list); 386 } 387 388 static void init_block_hist(struct block_hist *bh, struct block_fmt *block_fmts, 389 int *block_hpps, int nr_hpps) 390 { 391 __hists__init(&bh->block_hists, &bh->block_list); 392 perf_hpp_list__init(&bh->block_list); 393 bh->block_list.nr_header_lines = 1; 394 395 register_block_columns(&bh->block_list, block_fmts, 396 block_hpps, nr_hpps); 397 398 /* Sort by the first fmt */ 399 perf_hpp_list__register_sort_field(&bh->block_list, &block_fmts[0].fmt); 400 } 401 402 static int process_block_report(struct hists *hists, 403 struct block_report *block_report, 404 u64 total_cycles, int *block_hpps, 405 int nr_hpps) 406 { 407 struct rb_node *next = rb_first_cached(&hists->entries); 408 struct block_hist *bh = &block_report->hist; 409 struct hist_entry *he; 410 411 if (nr_hpps > PERF_HPP_REPORT__BLOCK_MAX_INDEX) 412 return -1; 413 414 block_report->nr_fmts = nr_hpps; 415 init_block_hist(bh, block_report->fmts, block_hpps, nr_hpps); 416 417 while (next) { 418 he = rb_entry(next, struct hist_entry, rb_node); 419 block_info__process_sym(he, bh, &block_report->cycles, 420 total_cycles); 421 next = rb_next(&he->rb_node); 422 } 423 424 for (int i = 0; i < nr_hpps; i++) { 425 block_report->fmts[i].total_cycles = total_cycles; 426 block_report->fmts[i].block_cycles = block_report->cycles; 427 } 428 429 hists__output_resort(&bh->block_hists, NULL); 430 return 0; 431 } 432 433 struct block_report *block_info__create_report(struct evlist *evlist, 434 u64 total_cycles, 435 int *block_hpps, int nr_hpps, 436 int *nr_reps) 437 { 438 struct block_report *block_reports; 439 int nr_hists = evlist->core.nr_entries, i = 0; 440 struct evsel *pos; 441 442 block_reports = calloc(nr_hists, sizeof(struct block_report)); 443 if (!block_reports) 444 return NULL; 445 446 evlist__for_each_entry(evlist, pos) { 447 struct hists *hists = evsel__hists(pos); 448 449 process_block_report(hists, &block_reports[i], total_cycles, 450 block_hpps, nr_hpps); 451 i++; 452 } 453 454 *nr_reps = nr_hists; 455 return block_reports; 456 } 457 458 void block_info__free_report(struct block_report *reps, int nr_reps) 459 { 460 for (int i = 0; i < nr_reps; i++) 461 hists__delete_entries(&reps[i].hist.block_hists); 462 463 free(reps); 464 } 465 466 int report__browse_block_hists(struct block_hist *bh, float min_percent, 467 struct evsel *evsel, struct perf_env *env, 468 struct annotation_options *annotation_opts) 469 { 470 int ret; 471 472 switch (use_browser) { 473 case 0: 474 symbol_conf.report_individual_block = true; 475 hists__fprintf(&bh->block_hists, true, 0, 0, min_percent, 476 stdout, true); 477 return 0; 478 case 1: 479 symbol_conf.report_individual_block = true; 480 ret = block_hists_tui_browse(bh, evsel, min_percent, 481 env, annotation_opts); 482 return ret; 483 default: 484 return -1; 485 } 486 487 return 0; 488 } 489 490 float block_info__total_cycles_percent(struct hist_entry *he) 491 { 492 struct block_info *bi = he->block_info; 493 494 if (bi->total_cycles) 495 return bi->cycles * 100.0 / bi->total_cycles; 496 497 return 0.0; 498 } 499