1// SPDX-License-Identifier: GPL-2.0 2#include <inttypes.h> 3#include <math.h> 4#include <stdlib.h> 5#include <string.h> 6#include <linux/compiler.h> 7 8#include "../util/callchain.h" 9#include "../util/debug.h" 10#include "../util/hist.h" 11#include "../util/sort.h" 12#include "../util/evsel.h" 13#include "../util/evlist.h" 14#include "../util/thread.h" 15#include "../util/util.h" 16 17/* hist period print (hpp) functions */ 18 19#define hpp__call_print_fn(hpp, fn, fmt, ...) \ 20({ \ 21 int __ret = fn(hpp, fmt, ##__VA_ARGS__); \ 22 advance_hpp(hpp, __ret); \ 23 __ret; \ 24}) 25 26static int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, 27 hpp_field_fn get_field, const char *fmt, int len, 28 hpp_snprint_fn print_fn, bool fmt_percent) 29{ 30 int ret; 31 struct hists *hists = he->hists; 32 struct evsel *evsel = hists_to_evsel(hists); 33 char *buf = hpp->buf; 34 size_t size = hpp->size; 35 36 if (fmt_percent) { 37 double percent = 0.0; 38 u64 total = hists__total_period(hists); 39 40 if (total) 41 percent = 100.0 * get_field(he) / total; 42 43 ret = hpp__call_print_fn(hpp, print_fn, fmt, len, percent); 44 } else 45 ret = hpp__call_print_fn(hpp, print_fn, fmt, len, get_field(he)); 46 47 if (evsel__is_group_event(evsel)) { 48 int prev_idx, idx_delta; 49 struct hist_entry *pair; 50 int nr_members = evsel->core.nr_members; 51 52 prev_idx = evsel__group_idx(evsel); 53 54 list_for_each_entry(pair, &he->pairs.head, pairs.node) { 55 u64 period = get_field(pair); 56 u64 total = hists__total_period(pair->hists); 57 58 if (!total) 59 continue; 60 61 evsel = hists_to_evsel(pair->hists); 62 idx_delta = evsel__group_idx(evsel) - prev_idx - 1; 63 64 while (idx_delta--) { 65 /* 66 * zero-fill group members in the middle which 67 * have no sample 68 */ 69 if (fmt_percent) { 70 ret += hpp__call_print_fn(hpp, print_fn, 71 fmt, len, 0.0); 72 } else { 73 ret += hpp__call_print_fn(hpp, print_fn, 74 fmt, len, 0ULL); 75 } 76 } 77 78 if (fmt_percent) { 79 ret += hpp__call_print_fn(hpp, print_fn, fmt, len, 80 100.0 * period / total); 81 } else { 82 ret += hpp__call_print_fn(hpp, print_fn, fmt, 83 len, period); 84 } 85 86 prev_idx = evsel__group_idx(evsel); 87 } 88 89 idx_delta = nr_members - prev_idx - 1; 90 91 while (idx_delta--) { 92 /* 93 * zero-fill group members at last which have no sample 94 */ 95 if (fmt_percent) { 96 ret += hpp__call_print_fn(hpp, print_fn, 97 fmt, len, 0.0); 98 } else { 99 ret += hpp__call_print_fn(hpp, print_fn, 100 fmt, len, 0ULL); 101 } 102 } 103 } 104 105 /* 106 * Restore original buf and size as it's where caller expects 107 * the result will be saved. 108 */ 109 hpp->buf = buf; 110 hpp->size = size; 111 112 return ret; 113} 114 115int hpp__fmt(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, 116 struct hist_entry *he, hpp_field_fn get_field, 117 const char *fmtstr, hpp_snprint_fn print_fn, bool fmt_percent) 118{ 119 int len = fmt->user_len ?: fmt->len; 120 121 if (symbol_conf.field_sep) { 122 return __hpp__fmt(hpp, he, get_field, fmtstr, 1, 123 print_fn, fmt_percent); 124 } 125 126 if (fmt_percent) 127 len -= 2; /* 2 for a space and a % sign */ 128 else 129 len -= 1; 130 131 return __hpp__fmt(hpp, he, get_field, fmtstr, len, print_fn, fmt_percent); 132} 133 134int hpp__fmt_acc(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, 135 struct hist_entry *he, hpp_field_fn get_field, 136 const char *fmtstr, hpp_snprint_fn print_fn, bool fmt_percent) 137{ 138 if (!symbol_conf.cumulate_callchain) { 139 int len = fmt->user_len ?: fmt->len; 140 return snprintf(hpp->buf, hpp->size, " %*s", len - 1, "N/A"); 141 } 142 143 return hpp__fmt(fmt, hpp, he, get_field, fmtstr, print_fn, fmt_percent); 144} 145 146static int field_cmp(u64 field_a, u64 field_b) 147{ 148 if (field_a > field_b) 149 return 1; 150 if (field_a < field_b) 151 return -1; 152 return 0; 153} 154 155static int hist_entry__new_pair(struct hist_entry *a, struct hist_entry *b, 156 hpp_field_fn get_field, int nr_members, 157 u64 **fields_a, u64 **fields_b) 158{ 159 u64 *fa = calloc(nr_members, sizeof(*fa)), 160 *fb = calloc(nr_members, sizeof(*fb)); 161 struct hist_entry *pair; 162 163 if (!fa || !fb) 164 goto out_free; 165 166 list_for_each_entry(pair, &a->pairs.head, pairs.node) { 167 struct evsel *evsel = hists_to_evsel(pair->hists); 168 fa[evsel__group_idx(evsel)] = get_field(pair); 169 } 170 171 list_for_each_entry(pair, &b->pairs.head, pairs.node) { 172 struct evsel *evsel = hists_to_evsel(pair->hists); 173 fb[evsel__group_idx(evsel)] = get_field(pair); 174 } 175 176 *fields_a = fa; 177 *fields_b = fb; 178 return 0; 179out_free: 180 free(fa); 181 free(fb); 182 *fields_a = *fields_b = NULL; 183 return -1; 184} 185 186static int __hpp__group_sort_idx(struct hist_entry *a, struct hist_entry *b, 187 hpp_field_fn get_field, int idx) 188{ 189 struct evsel *evsel = hists_to_evsel(a->hists); 190 u64 *fields_a, *fields_b; 191 int cmp, nr_members, ret, i; 192 193 cmp = field_cmp(get_field(a), get_field(b)); 194 if (!evsel__is_group_event(evsel)) 195 return cmp; 196 197 nr_members = evsel->core.nr_members; 198 if (idx < 1 || idx >= nr_members) 199 return cmp; 200 201 ret = hist_entry__new_pair(a, b, get_field, nr_members, &fields_a, &fields_b); 202 if (ret) { 203 ret = cmp; 204 goto out; 205 } 206 207 ret = field_cmp(fields_a[idx], fields_b[idx]); 208 if (ret) 209 goto out; 210 211 for (i = 1; i < nr_members; i++) { 212 if (i != idx) { 213 ret = field_cmp(fields_a[i], fields_b[i]); 214 if (ret) 215 goto out; 216 } 217 } 218 219out: 220 free(fields_a); 221 free(fields_b); 222 223 return ret; 224} 225 226static int __hpp__sort(struct hist_entry *a, struct hist_entry *b, 227 hpp_field_fn get_field) 228{ 229 s64 ret; 230 int i, nr_members; 231 struct evsel *evsel; 232 u64 *fields_a, *fields_b; 233 234 if (symbol_conf.group_sort_idx && symbol_conf.event_group) { 235 return __hpp__group_sort_idx(a, b, get_field, 236 symbol_conf.group_sort_idx); 237 } 238 239 ret = field_cmp(get_field(a), get_field(b)); 240 if (ret || !symbol_conf.event_group) 241 return ret; 242 243 evsel = hists_to_evsel(a->hists); 244 if (!evsel__is_group_event(evsel)) 245 return ret; 246 247 nr_members = evsel->core.nr_members; 248 i = hist_entry__new_pair(a, b, get_field, nr_members, &fields_a, &fields_b); 249 if (i) 250 goto out; 251 252 for (i = 1; i < nr_members; i++) { 253 ret = field_cmp(fields_a[i], fields_b[i]); 254 if (ret) 255 break; 256 } 257 258out: 259 free(fields_a); 260 free(fields_b); 261 262 return ret; 263} 264 265static int __hpp__sort_acc(struct hist_entry *a, struct hist_entry *b, 266 hpp_field_fn get_field) 267{ 268 s64 ret = 0; 269 270 if (symbol_conf.cumulate_callchain) { 271 /* 272 * Put caller above callee when they have equal period. 273 */ 274 ret = field_cmp(get_field(a), get_field(b)); 275 if (ret) 276 return ret; 277 278 if ((a->thread == NULL ? NULL : RC_CHK_ACCESS(a->thread)) != 279 (b->thread == NULL ? NULL : RC_CHK_ACCESS(b->thread)) || 280 !hist_entry__has_callchains(a) || !symbol_conf.use_callchain) 281 return 0; 282 283 ret = b->callchain->max_depth - a->callchain->max_depth; 284 if (callchain_param.order == ORDER_CALLER) 285 ret = -ret; 286 } 287 return ret; 288} 289 290static int hpp__width_fn(struct perf_hpp_fmt *fmt, 291 struct perf_hpp *hpp __maybe_unused, 292 struct hists *hists) 293{ 294 int len = fmt->user_len ?: fmt->len; 295 struct evsel *evsel = hists_to_evsel(hists); 296 297 if (symbol_conf.event_group) 298 len = max(len, evsel->core.nr_members * fmt->len); 299 300 if (len < (int)strlen(fmt->name)) 301 len = strlen(fmt->name); 302 303 return len; 304} 305 306static int hpp__header_fn(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, 307 struct hists *hists, int line __maybe_unused, 308 int *span __maybe_unused) 309{ 310 int len = hpp__width_fn(fmt, hpp, hists); 311 return scnprintf(hpp->buf, hpp->size, "%*s", len, fmt->name); 312} 313 314int hpp_color_scnprintf(struct perf_hpp *hpp, const char *fmt, ...) 315{ 316 va_list args; 317 ssize_t ssize = hpp->size; 318 double percent; 319 int ret, len; 320 321 va_start(args, fmt); 322 len = va_arg(args, int); 323 percent = va_arg(args, double); 324 ret = percent_color_len_snprintf(hpp->buf, hpp->size, fmt, len, percent); 325 va_end(args); 326 327 return (ret >= ssize) ? (ssize - 1) : ret; 328} 329 330static int hpp_entry_scnprintf(struct perf_hpp *hpp, const char *fmt, ...) 331{ 332 va_list args; 333 ssize_t ssize = hpp->size; 334 int ret; 335 336 va_start(args, fmt); 337 ret = vsnprintf(hpp->buf, hpp->size, fmt, args); 338 va_end(args); 339 340 return (ret >= ssize) ? (ssize - 1) : ret; 341} 342 343#define __HPP_COLOR_PERCENT_FN(_type, _field) \ 344static u64 he_get_##_field(struct hist_entry *he) \ 345{ \ 346 return he->stat._field; \ 347} \ 348 \ 349static int hpp__color_##_type(struct perf_hpp_fmt *fmt, \ 350 struct perf_hpp *hpp, struct hist_entry *he) \ 351{ \ 352 return hpp__fmt(fmt, hpp, he, he_get_##_field, " %*.2f%%", \ 353 hpp_color_scnprintf, true); \ 354} 355 356#define __HPP_ENTRY_PERCENT_FN(_type, _field) \ 357static int hpp__entry_##_type(struct perf_hpp_fmt *fmt, \ 358 struct perf_hpp *hpp, struct hist_entry *he) \ 359{ \ 360 return hpp__fmt(fmt, hpp, he, he_get_##_field, " %*.2f%%", \ 361 hpp_entry_scnprintf, true); \ 362} 363 364#define __HPP_SORT_FN(_type, _field) \ 365static int64_t hpp__sort_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \ 366 struct hist_entry *a, struct hist_entry *b) \ 367{ \ 368 return __hpp__sort(a, b, he_get_##_field); \ 369} 370 371#define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \ 372static u64 he_get_acc_##_field(struct hist_entry *he) \ 373{ \ 374 return he->stat_acc->_field; \ 375} \ 376 \ 377static int hpp__color_##_type(struct perf_hpp_fmt *fmt, \ 378 struct perf_hpp *hpp, struct hist_entry *he) \ 379{ \ 380 return hpp__fmt_acc(fmt, hpp, he, he_get_acc_##_field, " %*.2f%%", \ 381 hpp_color_scnprintf, true); \ 382} 383 384#define __HPP_ENTRY_ACC_PERCENT_FN(_type, _field) \ 385static int hpp__entry_##_type(struct perf_hpp_fmt *fmt, \ 386 struct perf_hpp *hpp, struct hist_entry *he) \ 387{ \ 388 return hpp__fmt_acc(fmt, hpp, he, he_get_acc_##_field, " %*.2f%%", \ 389 hpp_entry_scnprintf, true); \ 390} 391 392#define __HPP_SORT_ACC_FN(_type, _field) \ 393static int64_t hpp__sort_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \ 394 struct hist_entry *a, struct hist_entry *b) \ 395{ \ 396 return __hpp__sort_acc(a, b, he_get_acc_##_field); \ 397} 398 399#define __HPP_ENTRY_RAW_FN(_type, _field) \ 400static u64 he_get_raw_##_field(struct hist_entry *he) \ 401{ \ 402 return he->stat._field; \ 403} \ 404 \ 405static int hpp__entry_##_type(struct perf_hpp_fmt *fmt, \ 406 struct perf_hpp *hpp, struct hist_entry *he) \ 407{ \ 408 return hpp__fmt(fmt, hpp, he, he_get_raw_##_field, " %*"PRIu64, \ 409 hpp_entry_scnprintf, false); \ 410} 411 412#define __HPP_SORT_RAW_FN(_type, _field) \ 413static int64_t hpp__sort_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \ 414 struct hist_entry *a, struct hist_entry *b) \ 415{ \ 416 return __hpp__sort(a, b, he_get_raw_##_field); \ 417} 418 419 420#define HPP_PERCENT_FNS(_type, _field) \ 421__HPP_COLOR_PERCENT_FN(_type, _field) \ 422__HPP_ENTRY_PERCENT_FN(_type, _field) \ 423__HPP_SORT_FN(_type, _field) 424 425#define HPP_PERCENT_ACC_FNS(_type, _field) \ 426__HPP_COLOR_ACC_PERCENT_FN(_type, _field) \ 427__HPP_ENTRY_ACC_PERCENT_FN(_type, _field) \ 428__HPP_SORT_ACC_FN(_type, _field) 429 430#define HPP_RAW_FNS(_type, _field) \ 431__HPP_ENTRY_RAW_FN(_type, _field) \ 432__HPP_SORT_RAW_FN(_type, _field) 433 434HPP_PERCENT_FNS(overhead, period) 435HPP_PERCENT_FNS(overhead_sys, period_sys) 436HPP_PERCENT_FNS(overhead_us, period_us) 437HPP_PERCENT_FNS(overhead_guest_sys, period_guest_sys) 438HPP_PERCENT_FNS(overhead_guest_us, period_guest_us) 439HPP_PERCENT_ACC_FNS(overhead_acc, period) 440 441HPP_RAW_FNS(samples, nr_events) 442HPP_RAW_FNS(period, period) 443 444static int64_t hpp__nop_cmp(struct perf_hpp_fmt *fmt __maybe_unused, 445 struct hist_entry *a __maybe_unused, 446 struct hist_entry *b __maybe_unused) 447{ 448 return 0; 449} 450 451static bool perf_hpp__is_hpp_entry(struct perf_hpp_fmt *a) 452{ 453 return a->header == hpp__header_fn; 454} 455 456static bool hpp__equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b) 457{ 458 if (!perf_hpp__is_hpp_entry(a) || !perf_hpp__is_hpp_entry(b)) 459 return false; 460 461 return a->idx == b->idx; 462} 463 464#define HPP__COLOR_PRINT_FNS(_name, _fn, _idx) \ 465 { \ 466 .name = _name, \ 467 .header = hpp__header_fn, \ 468 .width = hpp__width_fn, \ 469 .color = hpp__color_ ## _fn, \ 470 .entry = hpp__entry_ ## _fn, \ 471 .cmp = hpp__nop_cmp, \ 472 .collapse = hpp__nop_cmp, \ 473 .sort = hpp__sort_ ## _fn, \ 474 .idx = PERF_HPP__ ## _idx, \ 475 .equal = hpp__equal, \ 476 } 477 478#define HPP__COLOR_ACC_PRINT_FNS(_name, _fn, _idx) \ 479 { \ 480 .name = _name, \ 481 .header = hpp__header_fn, \ 482 .width = hpp__width_fn, \ 483 .color = hpp__color_ ## _fn, \ 484 .entry = hpp__entry_ ## _fn, \ 485 .cmp = hpp__nop_cmp, \ 486 .collapse = hpp__nop_cmp, \ 487 .sort = hpp__sort_ ## _fn, \ 488 .idx = PERF_HPP__ ## _idx, \ 489 .equal = hpp__equal, \ 490 } 491 492#define HPP__PRINT_FNS(_name, _fn, _idx) \ 493 { \ 494 .name = _name, \ 495 .header = hpp__header_fn, \ 496 .width = hpp__width_fn, \ 497 .entry = hpp__entry_ ## _fn, \ 498 .cmp = hpp__nop_cmp, \ 499 .collapse = hpp__nop_cmp, \ 500 .sort = hpp__sort_ ## _fn, \ 501 .idx = PERF_HPP__ ## _idx, \ 502 .equal = hpp__equal, \ 503 } 504 505struct perf_hpp_fmt perf_hpp__format[] = { 506 HPP__COLOR_PRINT_FNS("Overhead", overhead, OVERHEAD), 507 HPP__COLOR_PRINT_FNS("sys", overhead_sys, OVERHEAD_SYS), 508 HPP__COLOR_PRINT_FNS("usr", overhead_us, OVERHEAD_US), 509 HPP__COLOR_PRINT_FNS("guest sys", overhead_guest_sys, OVERHEAD_GUEST_SYS), 510 HPP__COLOR_PRINT_FNS("guest usr", overhead_guest_us, OVERHEAD_GUEST_US), 511 HPP__COLOR_ACC_PRINT_FNS("Children", overhead_acc, OVERHEAD_ACC), 512 HPP__PRINT_FNS("Samples", samples, SAMPLES), 513 HPP__PRINT_FNS("Period", period, PERIOD) 514}; 515 516struct perf_hpp_list perf_hpp_list = { 517 .fields = LIST_HEAD_INIT(perf_hpp_list.fields), 518 .sorts = LIST_HEAD_INIT(perf_hpp_list.sorts), 519 .nr_header_lines = 1, 520}; 521 522#undef HPP__COLOR_PRINT_FNS 523#undef HPP__COLOR_ACC_PRINT_FNS 524#undef HPP__PRINT_FNS 525 526#undef HPP_PERCENT_FNS 527#undef HPP_PERCENT_ACC_FNS 528#undef HPP_RAW_FNS 529 530#undef __HPP_HEADER_FN 531#undef __HPP_WIDTH_FN 532#undef __HPP_COLOR_PERCENT_FN 533#undef __HPP_ENTRY_PERCENT_FN 534#undef __HPP_COLOR_ACC_PERCENT_FN 535#undef __HPP_ENTRY_ACC_PERCENT_FN 536#undef __HPP_ENTRY_RAW_FN 537#undef __HPP_SORT_FN 538#undef __HPP_SORT_ACC_FN 539#undef __HPP_SORT_RAW_FN 540 541static void fmt_free(struct perf_hpp_fmt *fmt) 542{ 543 /* 544 * At this point fmt should be completely 545 * unhooked, if not it's a bug. 546 */ 547 BUG_ON(!list_empty(&fmt->list)); 548 BUG_ON(!list_empty(&fmt->sort_list)); 549 550 if (fmt->free) 551 fmt->free(fmt); 552} 553 554void perf_hpp__init(void) 555{ 556 int i; 557 558 for (i = 0; i < PERF_HPP__MAX_INDEX; i++) { 559 struct perf_hpp_fmt *fmt = &perf_hpp__format[i]; 560 561 INIT_LIST_HEAD(&fmt->list); 562 563 /* sort_list may be linked by setup_sorting() */ 564 if (fmt->sort_list.next == NULL) 565 INIT_LIST_HEAD(&fmt->sort_list); 566 } 567 568 /* 569 * If user specified field order, no need to setup default fields. 570 */ 571 if (is_strict_order(field_order)) 572 return; 573 574 if (symbol_conf.cumulate_callchain) { 575 hpp_dimension__add_output(PERF_HPP__OVERHEAD_ACC); 576 perf_hpp__format[PERF_HPP__OVERHEAD].name = "Self"; 577 } 578 579 hpp_dimension__add_output(PERF_HPP__OVERHEAD); 580 581 if (symbol_conf.show_cpu_utilization) { 582 hpp_dimension__add_output(PERF_HPP__OVERHEAD_SYS); 583 hpp_dimension__add_output(PERF_HPP__OVERHEAD_US); 584 585 if (perf_guest) { 586 hpp_dimension__add_output(PERF_HPP__OVERHEAD_GUEST_SYS); 587 hpp_dimension__add_output(PERF_HPP__OVERHEAD_GUEST_US); 588 } 589 } 590 591 if (symbol_conf.show_nr_samples) 592 hpp_dimension__add_output(PERF_HPP__SAMPLES); 593 594 if (symbol_conf.show_total_period) 595 hpp_dimension__add_output(PERF_HPP__PERIOD); 596} 597 598void perf_hpp_list__column_register(struct perf_hpp_list *list, 599 struct perf_hpp_fmt *format) 600{ 601 list_add_tail(&format->list, &list->fields); 602} 603 604void perf_hpp_list__register_sort_field(struct perf_hpp_list *list, 605 struct perf_hpp_fmt *format) 606{ 607 list_add_tail(&format->sort_list, &list->sorts); 608} 609 610void perf_hpp_list__prepend_sort_field(struct perf_hpp_list *list, 611 struct perf_hpp_fmt *format) 612{ 613 list_add(&format->sort_list, &list->sorts); 614} 615 616static void perf_hpp__column_unregister(struct perf_hpp_fmt *format) 617{ 618 list_del_init(&format->list); 619 fmt_free(format); 620} 621 622void perf_hpp__cancel_cumulate(void) 623{ 624 struct perf_hpp_fmt *fmt, *acc, *ovh, *tmp; 625 626 if (is_strict_order(field_order)) 627 return; 628 629 ovh = &perf_hpp__format[PERF_HPP__OVERHEAD]; 630 acc = &perf_hpp__format[PERF_HPP__OVERHEAD_ACC]; 631 632 perf_hpp_list__for_each_format_safe(&perf_hpp_list, fmt, tmp) { 633 if (acc->equal(acc, fmt)) { 634 perf_hpp__column_unregister(fmt); 635 continue; 636 } 637 638 if (ovh->equal(ovh, fmt)) 639 fmt->name = "Overhead"; 640 } 641} 642 643static bool fmt_equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b) 644{ 645 return a->equal && a->equal(a, b); 646} 647 648void perf_hpp__setup_output_field(struct perf_hpp_list *list) 649{ 650 struct perf_hpp_fmt *fmt; 651 652 /* append sort keys to output field */ 653 perf_hpp_list__for_each_sort_list(list, fmt) { 654 struct perf_hpp_fmt *pos; 655 656 /* skip sort-only fields ("sort_compute" in perf diff) */ 657 if (!fmt->entry && !fmt->color) 658 continue; 659 660 perf_hpp_list__for_each_format(list, pos) { 661 if (fmt_equal(fmt, pos)) 662 goto next; 663 } 664 665 perf_hpp__column_register(fmt); 666next: 667 continue; 668 } 669} 670 671void perf_hpp__append_sort_keys(struct perf_hpp_list *list) 672{ 673 struct perf_hpp_fmt *fmt; 674 675 /* append output fields to sort keys */ 676 perf_hpp_list__for_each_format(list, fmt) { 677 struct perf_hpp_fmt *pos; 678 679 perf_hpp_list__for_each_sort_list(list, pos) { 680 if (fmt_equal(fmt, pos)) 681 goto next; 682 } 683 684 perf_hpp__register_sort_field(fmt); 685next: 686 continue; 687 } 688} 689 690 691void perf_hpp__reset_output_field(struct perf_hpp_list *list) 692{ 693 struct perf_hpp_fmt *fmt, *tmp; 694 695 /* reset output fields */ 696 perf_hpp_list__for_each_format_safe(list, fmt, tmp) { 697 list_del_init(&fmt->list); 698 list_del_init(&fmt->sort_list); 699 fmt_free(fmt); 700 } 701 702 /* reset sort keys */ 703 perf_hpp_list__for_each_sort_list_safe(list, fmt, tmp) { 704 list_del_init(&fmt->list); 705 list_del_init(&fmt->sort_list); 706 fmt_free(fmt); 707 } 708} 709 710/* 711 * See hists__fprintf to match the column widths 712 */ 713unsigned int hists__sort_list_width(struct hists *hists) 714{ 715 struct perf_hpp_fmt *fmt; 716 int ret = 0; 717 bool first = true; 718 struct perf_hpp dummy_hpp; 719 720 hists__for_each_format(hists, fmt) { 721 if (perf_hpp__should_skip(fmt, hists)) 722 continue; 723 724 if (first) 725 first = false; 726 else 727 ret += 2; 728 729 ret += fmt->width(fmt, &dummy_hpp, hists); 730 } 731 732 if (verbose > 0 && hists__has(hists, sym)) /* Addr + origin */ 733 ret += 3 + BITS_PER_LONG / 4; 734 735 return ret; 736} 737 738unsigned int hists__overhead_width(struct hists *hists) 739{ 740 struct perf_hpp_fmt *fmt; 741 int ret = 0; 742 bool first = true; 743 struct perf_hpp dummy_hpp; 744 745 hists__for_each_format(hists, fmt) { 746 if (perf_hpp__is_sort_entry(fmt) || perf_hpp__is_dynamic_entry(fmt)) 747 break; 748 749 if (first) 750 first = false; 751 else 752 ret += 2; 753 754 ret += fmt->width(fmt, &dummy_hpp, hists); 755 } 756 757 return ret; 758} 759 760void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists) 761{ 762 if (perf_hpp__is_sort_entry(fmt)) 763 return perf_hpp__reset_sort_width(fmt, hists); 764 765 if (perf_hpp__is_dynamic_entry(fmt)) 766 return; 767 768 BUG_ON(fmt->idx >= PERF_HPP__MAX_INDEX); 769 770 switch (fmt->idx) { 771 case PERF_HPP__OVERHEAD: 772 case PERF_HPP__OVERHEAD_SYS: 773 case PERF_HPP__OVERHEAD_US: 774 case PERF_HPP__OVERHEAD_ACC: 775 fmt->len = 8; 776 break; 777 778 case PERF_HPP__OVERHEAD_GUEST_SYS: 779 case PERF_HPP__OVERHEAD_GUEST_US: 780 fmt->len = 9; 781 break; 782 783 case PERF_HPP__SAMPLES: 784 case PERF_HPP__PERIOD: 785 fmt->len = 12; 786 break; 787 788 default: 789 break; 790 } 791} 792 793void hists__reset_column_width(struct hists *hists) 794{ 795 struct perf_hpp_fmt *fmt; 796 struct perf_hpp_list_node *node; 797 798 hists__for_each_format(hists, fmt) 799 perf_hpp__reset_width(fmt, hists); 800 801 /* hierarchy entries have their own hpp list */ 802 list_for_each_entry(node, &hists->hpp_formats, list) { 803 perf_hpp_list__for_each_format(&node->hpp, fmt) 804 perf_hpp__reset_width(fmt, hists); 805 } 806} 807 808void perf_hpp__set_user_width(const char *width_list_str) 809{ 810 struct perf_hpp_fmt *fmt; 811 const char *ptr = width_list_str; 812 813 perf_hpp_list__for_each_format(&perf_hpp_list, fmt) { 814 char *p; 815 816 int len = strtol(ptr, &p, 10); 817 fmt->user_len = len; 818 819 if (*p == ',') 820 ptr = p + 1; 821 else 822 break; 823 } 824} 825 826static int add_hierarchy_fmt(struct hists *hists, struct perf_hpp_fmt *fmt) 827{ 828 struct perf_hpp_list_node *node = NULL; 829 struct perf_hpp_fmt *fmt_copy; 830 bool found = false; 831 bool skip = perf_hpp__should_skip(fmt, hists); 832 833 list_for_each_entry(node, &hists->hpp_formats, list) { 834 if (node->level == fmt->level) { 835 found = true; 836 break; 837 } 838 } 839 840 if (!found) { 841 node = malloc(sizeof(*node)); 842 if (node == NULL) 843 return -1; 844 845 node->skip = skip; 846 node->level = fmt->level; 847 perf_hpp_list__init(&node->hpp); 848 849 hists->nr_hpp_node++; 850 list_add_tail(&node->list, &hists->hpp_formats); 851 } 852 853 fmt_copy = perf_hpp_fmt__dup(fmt); 854 if (fmt_copy == NULL) 855 return -1; 856 857 if (!skip) 858 node->skip = false; 859 860 list_add_tail(&fmt_copy->list, &node->hpp.fields); 861 list_add_tail(&fmt_copy->sort_list, &node->hpp.sorts); 862 863 return 0; 864} 865 866int perf_hpp__setup_hists_formats(struct perf_hpp_list *list, 867 struct evlist *evlist) 868{ 869 struct evsel *evsel; 870 struct perf_hpp_fmt *fmt; 871 struct hists *hists; 872 int ret; 873 874 if (!symbol_conf.report_hierarchy) 875 return 0; 876 877 evlist__for_each_entry(evlist, evsel) { 878 hists = evsel__hists(evsel); 879 880 perf_hpp_list__for_each_sort_list(list, fmt) { 881 if (perf_hpp__is_dynamic_entry(fmt) && 882 !perf_hpp__defined_dynamic_entry(fmt, hists)) 883 continue; 884 885 ret = add_hierarchy_fmt(hists, fmt); 886 if (ret < 0) 887 return ret; 888 } 889 } 890 891 return 0; 892} 893