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/mem-events.h" 15 #include "../util/string2.h" 16 #include "../util/thread.h" 17 #include "../util/util.h" 18 19 /* hist period print (hpp) functions */ 20 21 #define hpp__call_print_fn(hpp, fn, fmt, ...) \ 22 ({ \ 23 int __ret = fn(hpp, fmt, ##__VA_ARGS__); \ 24 advance_hpp(hpp, __ret); \ 25 __ret; \ 26 }) 27 28 static int __hpp__fmt_print(struct perf_hpp *hpp, struct hists *hists, u64 val, 29 int nr_samples, const char *fmt, int len, 30 hpp_snprint_fn print_fn, enum perf_hpp_fmt_type fmtype) 31 { 32 if (fmtype == PERF_HPP_FMT_TYPE__PERCENT || fmtype == PERF_HPP_FMT_TYPE__LATENCY) { 33 double percent = 0.0; 34 u64 total = fmtype == PERF_HPP_FMT_TYPE__PERCENT ? hists__total_period(hists) : 35 hists__total_latency(hists); 36 37 if (total) 38 percent = 100.0 * val / total; 39 40 return hpp__call_print_fn(hpp, print_fn, fmt, len, percent); 41 } 42 43 if (fmtype == PERF_HPP_FMT_TYPE__AVERAGE) { 44 double avg = nr_samples ? (1.0 * val / nr_samples) : 0; 45 46 return hpp__call_print_fn(hpp, print_fn, fmt, len, avg); 47 } 48 49 return hpp__call_print_fn(hpp, print_fn, fmt, len, val); 50 } 51 52 struct hpp_fmt_value { 53 struct hists *hists; 54 u64 val; 55 int samples; 56 }; 57 58 static int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, 59 hpp_field_fn get_field, const char *fmt, int len, 60 hpp_snprint_fn print_fn, enum perf_hpp_fmt_type fmtype) 61 { 62 int ret = 0; 63 struct hists *hists = he->hists; 64 struct evsel *evsel = hists_to_evsel(hists); 65 struct evsel *pos; 66 char *buf = hpp->buf; 67 size_t size = hpp->size; 68 int i = 0, nr_members = 1; 69 struct hpp_fmt_value *values; 70 71 if (evsel__is_group_event(evsel)) 72 nr_members = evsel->core.nr_members; 73 74 values = calloc(nr_members, sizeof(*values)); 75 if (values == NULL) 76 return 0; 77 78 values[0].hists = evsel__hists(evsel); 79 values[0].val = get_field(he); 80 values[0].samples = he->stat.nr_events; 81 82 if (evsel__is_group_event(evsel)) { 83 struct hist_entry *pair; 84 85 for_each_group_member(pos, evsel) 86 values[++i].hists = evsel__hists(pos); 87 88 list_for_each_entry(pair, &he->pairs.head, pairs.node) { 89 for (i = 0; i < nr_members; i++) { 90 if (values[i].hists != pair->hists) 91 continue; 92 93 values[i].val = get_field(pair); 94 values[i].samples = pair->stat.nr_events; 95 break; 96 } 97 } 98 } 99 100 for (i = 0; i < nr_members; i++) { 101 if (symbol_conf.skip_empty && 102 values[i].hists->stats.nr_samples == 0) 103 continue; 104 105 ret += __hpp__fmt_print(hpp, values[i].hists, values[i].val, 106 values[i].samples, fmt, len, 107 print_fn, fmtype); 108 } 109 110 free(values); 111 112 /* 113 * Restore original buf and size as it's where caller expects 114 * the result will be saved. 115 */ 116 hpp->buf = buf; 117 hpp->size = size; 118 119 return ret; 120 } 121 122 int hpp__fmt(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, 123 struct hist_entry *he, hpp_field_fn get_field, 124 const char *fmtstr, hpp_snprint_fn print_fn, 125 enum perf_hpp_fmt_type fmtype) 126 { 127 int len = max(fmt->user_len ?: fmt->len, (int)strlen(fmt->name)); 128 129 if (symbol_conf.field_sep) { 130 return __hpp__fmt(hpp, he, get_field, fmtstr, 1, 131 print_fn, fmtype); 132 } 133 134 if (fmtype == PERF_HPP_FMT_TYPE__PERCENT || fmtype == PERF_HPP_FMT_TYPE__LATENCY) 135 len -= 2; /* 2 for a space and a % sign */ 136 else 137 len -= 1; 138 139 return __hpp__fmt(hpp, he, get_field, fmtstr, len, print_fn, fmtype); 140 } 141 142 int hpp__fmt_acc(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, 143 struct hist_entry *he, hpp_field_fn get_field, 144 const char *fmtstr, hpp_snprint_fn print_fn, 145 enum perf_hpp_fmt_type fmtype) 146 { 147 if (!symbol_conf.cumulate_callchain) { 148 int len = fmt->user_len ?: fmt->len; 149 return snprintf(hpp->buf, hpp->size, " %*s", len - 1, "N/A"); 150 } 151 152 return hpp__fmt(fmt, hpp, he, get_field, fmtstr, print_fn, fmtype); 153 } 154 155 int hpp__fmt_mem_stat(struct perf_hpp_fmt *fmt __maybe_unused, struct perf_hpp *hpp, 156 struct hist_entry *he, enum mem_stat_type mst, 157 const char *fmtstr, hpp_snprint_fn print_fn) 158 { 159 struct hists *hists = he->hists; 160 int mem_stat_idx = -1; 161 char *buf = hpp->buf; 162 size_t size = hpp->size; 163 u64 total = 0; 164 int ret = 0; 165 166 for (int i = 0; i < hists->nr_mem_stats; i++) { 167 if (hists->mem_stat_types[i] == mst) { 168 mem_stat_idx = i; 169 break; 170 } 171 } 172 assert(mem_stat_idx != -1); 173 174 for (int i = 0; i < MEM_STAT_LEN; i++) 175 total += hists->mem_stat_total[mem_stat_idx].entries[i]; 176 assert(total != 0); 177 178 for (int i = 0; i < MEM_STAT_LEN; i++) { 179 u64 val = he->mem_stat[mem_stat_idx].entries[i]; 180 181 if (hists->mem_stat_total[mem_stat_idx].entries[i] == 0) 182 continue; 183 184 ret += hpp__call_print_fn(hpp, print_fn, fmtstr, 100.0 * val / total); 185 } 186 187 /* 188 * Restore original buf and size as it's where caller expects 189 * the result will be saved. 190 */ 191 hpp->buf = buf; 192 hpp->size = size; 193 194 return ret; 195 } 196 197 static int field_cmp(u64 field_a, u64 field_b) 198 { 199 if (field_a > field_b) 200 return 1; 201 if (field_a < field_b) 202 return -1; 203 return 0; 204 } 205 206 static int hist_entry__new_pair(struct hist_entry *a, struct hist_entry *b, 207 hpp_field_fn get_field, int nr_members, 208 u64 **fields_a, u64 **fields_b) 209 { 210 u64 *fa = calloc(nr_members, sizeof(*fa)), 211 *fb = calloc(nr_members, sizeof(*fb)); 212 struct hist_entry *pair; 213 214 if (!fa || !fb) 215 goto out_free; 216 217 list_for_each_entry(pair, &a->pairs.head, pairs.node) { 218 struct evsel *evsel = hists_to_evsel(pair->hists); 219 fa[evsel__group_idx(evsel)] = get_field(pair); 220 } 221 222 list_for_each_entry(pair, &b->pairs.head, pairs.node) { 223 struct evsel *evsel = hists_to_evsel(pair->hists); 224 fb[evsel__group_idx(evsel)] = get_field(pair); 225 } 226 227 *fields_a = fa; 228 *fields_b = fb; 229 return 0; 230 out_free: 231 free(fa); 232 free(fb); 233 *fields_a = *fields_b = NULL; 234 return -1; 235 } 236 237 static int __hpp__group_sort_idx(struct hist_entry *a, struct hist_entry *b, 238 hpp_field_fn get_field, int idx) 239 { 240 struct evsel *evsel = hists_to_evsel(a->hists); 241 u64 *fields_a, *fields_b; 242 int cmp, nr_members, ret, i; 243 244 cmp = field_cmp(get_field(a), get_field(b)); 245 if (!evsel__is_group_event(evsel)) 246 return cmp; 247 248 nr_members = evsel->core.nr_members; 249 if (idx < 1 || idx >= nr_members) 250 return cmp; 251 252 ret = hist_entry__new_pair(a, b, get_field, nr_members, &fields_a, &fields_b); 253 if (ret) { 254 ret = cmp; 255 goto out; 256 } 257 258 ret = field_cmp(fields_a[idx], fields_b[idx]); 259 if (ret) 260 goto out; 261 262 for (i = 1; i < nr_members; i++) { 263 if (i != idx) { 264 ret = field_cmp(fields_a[i], fields_b[i]); 265 if (ret) 266 goto out; 267 } 268 } 269 270 out: 271 free(fields_a); 272 free(fields_b); 273 274 return ret; 275 } 276 277 static int __hpp__sort(struct hist_entry *a, struct hist_entry *b, 278 hpp_field_fn get_field) 279 { 280 s64 ret; 281 int i, nr_members; 282 struct evsel *evsel; 283 u64 *fields_a, *fields_b; 284 285 if (symbol_conf.group_sort_idx && symbol_conf.event_group) { 286 return __hpp__group_sort_idx(a, b, get_field, 287 symbol_conf.group_sort_idx); 288 } 289 290 ret = field_cmp(get_field(a), get_field(b)); 291 if (ret || !symbol_conf.event_group) 292 return ret; 293 294 evsel = hists_to_evsel(a->hists); 295 if (!evsel__is_group_event(evsel)) 296 return ret; 297 298 nr_members = evsel->core.nr_members; 299 i = hist_entry__new_pair(a, b, get_field, nr_members, &fields_a, &fields_b); 300 if (i) 301 goto out; 302 303 for (i = 1; i < nr_members; i++) { 304 ret = field_cmp(fields_a[i], fields_b[i]); 305 if (ret) 306 break; 307 } 308 309 out: 310 free(fields_a); 311 free(fields_b); 312 313 return ret; 314 } 315 316 static int __hpp__sort_acc(struct hist_entry *a, struct hist_entry *b, 317 hpp_field_fn get_field) 318 { 319 s64 ret = 0; 320 321 if (symbol_conf.cumulate_callchain) { 322 /* 323 * Put caller above callee when they have equal period. 324 */ 325 ret = field_cmp(get_field(a), get_field(b)); 326 if (ret) 327 return ret; 328 329 if ((a->thread == NULL ? NULL : RC_CHK_ACCESS(a->thread)) != 330 (b->thread == NULL ? NULL : RC_CHK_ACCESS(b->thread)) || 331 !hist_entry__has_callchains(a) || !symbol_conf.use_callchain) 332 return 0; 333 334 ret = b->callchain->max_depth - a->callchain->max_depth; 335 if (callchain_param.order == ORDER_CALLER) 336 ret = -ret; 337 } 338 return ret; 339 } 340 341 static bool perf_hpp__is_mem_stat_entry(struct perf_hpp_fmt *fmt); 342 343 static enum mem_stat_type hpp__mem_stat_type(struct perf_hpp_fmt *fmt) 344 { 345 if (!perf_hpp__is_mem_stat_entry(fmt)) 346 return -1; 347 348 switch (fmt->idx) { 349 case PERF_HPP__MEM_STAT_OP: 350 return PERF_MEM_STAT_OP; 351 case PERF_HPP__MEM_STAT_CACHE: 352 return PERF_MEM_STAT_CACHE; 353 case PERF_HPP__MEM_STAT_MEMORY: 354 return PERF_MEM_STAT_MEMORY; 355 case PERF_HPP__MEM_STAT_SNOOP: 356 return PERF_MEM_STAT_SNOOP; 357 case PERF_HPP__MEM_STAT_DTLB: 358 return PERF_MEM_STAT_DTLB; 359 default: 360 break; 361 } 362 pr_debug("Should not reach here\n"); 363 return -1; 364 } 365 366 static int64_t hpp__sort_mem_stat(struct perf_hpp_fmt *fmt __maybe_unused, 367 struct hist_entry *a, struct hist_entry *b) 368 { 369 return a->stat.period - b->stat.period; 370 } 371 372 static int hpp__width_fn(struct perf_hpp_fmt *fmt, 373 struct perf_hpp *hpp __maybe_unused, 374 struct hists *hists) 375 { 376 int len = fmt->user_len ?: fmt->len; 377 struct evsel *evsel = hists_to_evsel(hists); 378 379 if (symbol_conf.event_group) { 380 int nr = 0; 381 struct evsel *pos; 382 383 for_each_group_evsel(pos, evsel) { 384 if (!symbol_conf.skip_empty || 385 evsel__hists(pos)->stats.nr_samples) 386 nr++; 387 } 388 389 len = max(len, nr * fmt->len); 390 } 391 392 if (len < (int)strlen(fmt->name)) 393 len = strlen(fmt->name); 394 395 return len; 396 } 397 398 static int hpp__header_fn(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, 399 struct hists *hists, int line, 400 int *span __maybe_unused) 401 { 402 int len = hpp__width_fn(fmt, hpp, hists); 403 const char *hdr = ""; 404 405 if (line == hists->hpp_list->nr_header_lines - 1) 406 hdr = fmt->name; 407 408 return scnprintf(hpp->buf, hpp->size, "%*s", len, hdr); 409 } 410 411 static int hpp__header_mem_stat_fn(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, 412 struct hists *hists, int line, 413 int *span __maybe_unused) 414 { 415 char *buf = hpp->buf; 416 int ret = 0; 417 int len; 418 enum mem_stat_type mst = hpp__mem_stat_type(fmt); 419 int mem_stat_idx = -1; 420 421 for (int i = 0; i < hists->nr_mem_stats; i++) { 422 if (hists->mem_stat_types[i] == mst) { 423 mem_stat_idx = i; 424 break; 425 } 426 } 427 assert(mem_stat_idx != -1); 428 429 if (line == 0) { 430 int left, right; 431 432 len = 0; 433 /* update fmt->len for acutally used columns only */ 434 for (int i = 0; i < MEM_STAT_LEN; i++) { 435 if (hists->mem_stat_total[mem_stat_idx].entries[i]) 436 len += MEM_STAT_PRINT_LEN; 437 } 438 fmt->len = len; 439 440 /* print header directly if single column only */ 441 if (len == MEM_STAT_PRINT_LEN) 442 return scnprintf(hpp->buf, hpp->size, "%*s", len, fmt->name); 443 444 left = (len - strlen(fmt->name)) / 2 - 1; 445 right = len - left - strlen(fmt->name) - 2; 446 447 if (left < 0) 448 left = 0; 449 if (right < 0) 450 right = 0; 451 452 return scnprintf(hpp->buf, hpp->size, "%.*s %s %.*s", 453 left, graph_dotted_line, fmt->name, right, graph_dotted_line); 454 } 455 456 457 len = hpp->size; 458 for (int i = 0; i < MEM_STAT_LEN; i++) { 459 int printed; 460 461 if (hists->mem_stat_total[mem_stat_idx].entries[i] == 0) 462 continue; 463 464 printed = scnprintf(buf, len, "%*s", MEM_STAT_PRINT_LEN, 465 mem_stat_name(mst, i)); 466 ret += printed; 467 buf += printed; 468 len -= printed; 469 } 470 return ret; 471 } 472 473 int hpp_color_scnprintf(struct perf_hpp *hpp, const char *fmt, ...) 474 { 475 va_list args; 476 ssize_t ssize = hpp->size; 477 double percent; 478 int ret, len; 479 480 va_start(args, fmt); 481 len = va_arg(args, int); 482 percent = va_arg(args, double); 483 ret = percent_color_len_snprintf(hpp->buf, hpp->size, fmt, len, percent); 484 va_end(args); 485 486 return (ret >= ssize) ? (ssize - 1) : ret; 487 } 488 489 static int hpp_entry_scnprintf(struct perf_hpp *hpp, const char *fmt, ...) 490 { 491 va_list args; 492 ssize_t ssize = hpp->size; 493 int ret; 494 495 va_start(args, fmt); 496 ret = vsnprintf(hpp->buf, hpp->size, fmt, args); 497 va_end(args); 498 499 return (ret >= ssize) ? (ssize - 1) : ret; 500 } 501 502 #define __HPP_COLOR_PERCENT_FN(_type, _field, _fmttype) \ 503 static u64 he_get_##_field(struct hist_entry *he) \ 504 { \ 505 return he->stat._field; \ 506 } \ 507 \ 508 static int hpp__color_##_type(struct perf_hpp_fmt *fmt, \ 509 struct perf_hpp *hpp, struct hist_entry *he) \ 510 { \ 511 return hpp__fmt(fmt, hpp, he, he_get_##_field, " %*.2f%%", \ 512 hpp_color_scnprintf, _fmttype); \ 513 } 514 515 #define __HPP_ENTRY_PERCENT_FN(_type, _field, _fmttype) \ 516 static int hpp__entry_##_type(struct perf_hpp_fmt *fmt, \ 517 struct perf_hpp *hpp, struct hist_entry *he) \ 518 { \ 519 return hpp__fmt(fmt, hpp, he, he_get_##_field, " %*.2f%%", \ 520 hpp_entry_scnprintf, _fmttype); \ 521 } 522 523 #define __HPP_SORT_FN(_type, _field) \ 524 static int64_t hpp__sort_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \ 525 struct hist_entry *a, struct hist_entry *b) \ 526 { \ 527 return __hpp__sort(a, b, he_get_##_field); \ 528 } 529 530 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field, _fmttype) \ 531 static u64 he_get_acc_##_field(struct hist_entry *he) \ 532 { \ 533 return he->stat_acc->_field; \ 534 } \ 535 \ 536 static int hpp__color_##_type(struct perf_hpp_fmt *fmt, \ 537 struct perf_hpp *hpp, struct hist_entry *he) \ 538 { \ 539 return hpp__fmt_acc(fmt, hpp, he, he_get_acc_##_field, " %*.2f%%", \ 540 hpp_color_scnprintf, _fmttype); \ 541 } 542 543 #define __HPP_ENTRY_ACC_PERCENT_FN(_type, _field, _fmttype) \ 544 static int hpp__entry_##_type(struct perf_hpp_fmt *fmt, \ 545 struct perf_hpp *hpp, struct hist_entry *he) \ 546 { \ 547 return hpp__fmt_acc(fmt, hpp, he, he_get_acc_##_field, " %*.2f%%", \ 548 hpp_entry_scnprintf, _fmttype); \ 549 } 550 551 #define __HPP_SORT_ACC_FN(_type, _field) \ 552 static int64_t hpp__sort_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \ 553 struct hist_entry *a, struct hist_entry *b) \ 554 { \ 555 return __hpp__sort_acc(a, b, he_get_acc_##_field); \ 556 } 557 558 #define __HPP_ENTRY_RAW_FN(_type, _field) \ 559 static u64 he_get_raw_##_field(struct hist_entry *he) \ 560 { \ 561 return he->stat._field; \ 562 } \ 563 \ 564 static int hpp__entry_##_type(struct perf_hpp_fmt *fmt, \ 565 struct perf_hpp *hpp, struct hist_entry *he) \ 566 { \ 567 return hpp__fmt(fmt, hpp, he, he_get_raw_##_field, " %*"PRIu64, \ 568 hpp_entry_scnprintf, PERF_HPP_FMT_TYPE__RAW); \ 569 } 570 571 #define __HPP_SORT_RAW_FN(_type, _field) \ 572 static int64_t hpp__sort_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \ 573 struct hist_entry *a, struct hist_entry *b) \ 574 { \ 575 return __hpp__sort(a, b, he_get_raw_##_field); \ 576 } 577 578 #define __HPP_ENTRY_AVERAGE_FN(_type, _field) \ 579 static u64 he_get_##_field(struct hist_entry *he) \ 580 { \ 581 return he->stat._field; \ 582 } \ 583 \ 584 static int hpp__entry_##_type(struct perf_hpp_fmt *fmt, \ 585 struct perf_hpp *hpp, struct hist_entry *he) \ 586 { \ 587 return hpp__fmt(fmt, hpp, he, he_get_##_field, " %*.1f", \ 588 hpp_entry_scnprintf, PERF_HPP_FMT_TYPE__AVERAGE); \ 589 } 590 591 #define __HPP_SORT_AVERAGE_FN(_type, _field) \ 592 static int64_t hpp__sort_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \ 593 struct hist_entry *a, struct hist_entry *b) \ 594 { \ 595 return __hpp__sort(a, b, he_get_##_field); \ 596 } 597 598 #define __HPP_COLOR_MEM_STAT_FN(_name, _type) \ 599 static int hpp__color_mem_stat_##_name(struct perf_hpp_fmt *fmt, \ 600 struct perf_hpp *hpp, \ 601 struct hist_entry *he) \ 602 { \ 603 return hpp__fmt_mem_stat(fmt, hpp, he, PERF_MEM_STAT_##_type, \ 604 " %5.1f%%", hpp_color_scnprintf); \ 605 } 606 607 #define __HPP_ENTRY_MEM_STAT_FN(_name, _type) \ 608 static int hpp__entry_mem_stat_##_name(struct perf_hpp_fmt *fmt, \ 609 struct perf_hpp *hpp, \ 610 struct hist_entry *he) \ 611 { \ 612 return hpp__fmt_mem_stat(fmt, hpp, he, PERF_MEM_STAT_##_type, \ 613 " %5.1f%%", hpp_entry_scnprintf); \ 614 } 615 616 #define HPP_PERCENT_FNS(_type, _field, _fmttype) \ 617 __HPP_COLOR_PERCENT_FN(_type, _field, _fmttype) \ 618 __HPP_ENTRY_PERCENT_FN(_type, _field, _fmttype) \ 619 __HPP_SORT_FN(_type, _field) 620 621 #define HPP_PERCENT_ACC_FNS(_type, _field, _fmttype) \ 622 __HPP_COLOR_ACC_PERCENT_FN(_type, _field, _fmttype) \ 623 __HPP_ENTRY_ACC_PERCENT_FN(_type, _field, _fmttype) \ 624 __HPP_SORT_ACC_FN(_type, _field) 625 626 #define HPP_RAW_FNS(_type, _field) \ 627 __HPP_ENTRY_RAW_FN(_type, _field) \ 628 __HPP_SORT_RAW_FN(_type, _field) 629 630 #define HPP_AVERAGE_FNS(_type, _field) \ 631 __HPP_ENTRY_AVERAGE_FN(_type, _field) \ 632 __HPP_SORT_AVERAGE_FN(_type, _field) 633 634 #define HPP_MEM_STAT_FNS(_name, _type) \ 635 __HPP_COLOR_MEM_STAT_FN(_name, _type) \ 636 __HPP_ENTRY_MEM_STAT_FN(_name, _type) 637 638 HPP_PERCENT_FNS(overhead, period, PERF_HPP_FMT_TYPE__PERCENT) 639 HPP_PERCENT_FNS(latency, latency, PERF_HPP_FMT_TYPE__LATENCY) 640 HPP_PERCENT_FNS(overhead_sys, period_sys, PERF_HPP_FMT_TYPE__PERCENT) 641 HPP_PERCENT_FNS(overhead_us, period_us, PERF_HPP_FMT_TYPE__PERCENT) 642 HPP_PERCENT_FNS(overhead_guest_sys, period_guest_sys, PERF_HPP_FMT_TYPE__PERCENT) 643 HPP_PERCENT_FNS(overhead_guest_us, period_guest_us, PERF_HPP_FMT_TYPE__PERCENT) 644 HPP_PERCENT_ACC_FNS(overhead_acc, period, PERF_HPP_FMT_TYPE__PERCENT) 645 HPP_PERCENT_ACC_FNS(latency_acc, latency, PERF_HPP_FMT_TYPE__LATENCY) 646 647 HPP_RAW_FNS(samples, nr_events) 648 HPP_RAW_FNS(period, period) 649 650 HPP_AVERAGE_FNS(weight1, weight1) 651 HPP_AVERAGE_FNS(weight2, weight2) 652 HPP_AVERAGE_FNS(weight3, weight3) 653 654 HPP_MEM_STAT_FNS(op, OP) 655 HPP_MEM_STAT_FNS(cache, CACHE) 656 HPP_MEM_STAT_FNS(memory, MEMORY) 657 HPP_MEM_STAT_FNS(snoop, SNOOP) 658 HPP_MEM_STAT_FNS(dtlb, DTLB) 659 660 static int64_t hpp__nop_cmp(struct perf_hpp_fmt *fmt __maybe_unused, 661 struct hist_entry *a __maybe_unused, 662 struct hist_entry *b __maybe_unused) 663 { 664 return 0; 665 } 666 667 static bool perf_hpp__is_mem_stat_entry(struct perf_hpp_fmt *fmt) 668 { 669 return fmt->sort == hpp__sort_mem_stat; 670 } 671 672 static bool perf_hpp__is_hpp_entry(struct perf_hpp_fmt *a) 673 { 674 return a->header == hpp__header_fn; 675 } 676 677 static bool hpp__equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b) 678 { 679 if (!perf_hpp__is_hpp_entry(a) || !perf_hpp__is_hpp_entry(b)) 680 return false; 681 682 return a->idx == b->idx; 683 } 684 685 static bool hpp__equal_mem_stat(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b) 686 { 687 if (!perf_hpp__is_mem_stat_entry(a) || !perf_hpp__is_mem_stat_entry(b)) 688 return false; 689 690 return a->entry == b->entry; 691 } 692 693 #define HPP__COLOR_PRINT_FNS(_name, _fn, _idx) \ 694 { \ 695 .name = _name, \ 696 .header = hpp__header_fn, \ 697 .width = hpp__width_fn, \ 698 .color = hpp__color_ ## _fn, \ 699 .entry = hpp__entry_ ## _fn, \ 700 .cmp = hpp__nop_cmp, \ 701 .collapse = hpp__nop_cmp, \ 702 .sort = hpp__sort_ ## _fn, \ 703 .idx = PERF_HPP__ ## _idx, \ 704 .equal = hpp__equal, \ 705 } 706 707 #define HPP__COLOR_ACC_PRINT_FNS(_name, _fn, _idx) \ 708 { \ 709 .name = _name, \ 710 .header = hpp__header_fn, \ 711 .width = hpp__width_fn, \ 712 .color = hpp__color_ ## _fn, \ 713 .entry = hpp__entry_ ## _fn, \ 714 .cmp = hpp__nop_cmp, \ 715 .collapse = hpp__nop_cmp, \ 716 .sort = hpp__sort_ ## _fn, \ 717 .idx = PERF_HPP__ ## _idx, \ 718 .equal = hpp__equal, \ 719 } 720 721 #define HPP__PRINT_FNS(_name, _fn, _idx) \ 722 { \ 723 .name = _name, \ 724 .header = hpp__header_fn, \ 725 .width = hpp__width_fn, \ 726 .entry = hpp__entry_ ## _fn, \ 727 .cmp = hpp__nop_cmp, \ 728 .collapse = hpp__nop_cmp, \ 729 .sort = hpp__sort_ ## _fn, \ 730 .idx = PERF_HPP__ ## _idx, \ 731 .equal = hpp__equal, \ 732 } 733 734 #define HPP__MEM_STAT_PRINT_FNS(_name, _fn, _type) \ 735 { \ 736 .name = _name, \ 737 .header = hpp__header_mem_stat_fn, \ 738 .width = hpp__width_fn, \ 739 .color = hpp__color_mem_stat_ ## _fn, \ 740 .entry = hpp__entry_mem_stat_ ## _fn, \ 741 .cmp = hpp__nop_cmp, \ 742 .collapse = hpp__nop_cmp, \ 743 .sort = hpp__sort_mem_stat, \ 744 .idx = PERF_HPP__MEM_STAT_ ## _type, \ 745 .equal = hpp__equal_mem_stat, \ 746 } 747 748 struct perf_hpp_fmt perf_hpp__format[] = { 749 HPP__COLOR_PRINT_FNS("Overhead", overhead, OVERHEAD), 750 HPP__COLOR_PRINT_FNS("Latency", latency, LATENCY), 751 HPP__COLOR_PRINT_FNS("sys", overhead_sys, OVERHEAD_SYS), 752 HPP__COLOR_PRINT_FNS("usr", overhead_us, OVERHEAD_US), 753 HPP__COLOR_PRINT_FNS("guest sys", overhead_guest_sys, OVERHEAD_GUEST_SYS), 754 HPP__COLOR_PRINT_FNS("guest usr", overhead_guest_us, OVERHEAD_GUEST_US), 755 HPP__COLOR_ACC_PRINT_FNS("Children", overhead_acc, OVERHEAD_ACC), 756 HPP__COLOR_ACC_PRINT_FNS("Latency", latency_acc, LATENCY_ACC), 757 HPP__PRINT_FNS("Samples", samples, SAMPLES), 758 HPP__PRINT_FNS("Period", period, PERIOD), 759 HPP__PRINT_FNS("Weight1", weight1, WEIGHT1), 760 HPP__PRINT_FNS("Weight2", weight2, WEIGHT2), 761 HPP__PRINT_FNS("Weight3", weight3, WEIGHT3), 762 HPP__MEM_STAT_PRINT_FNS("Mem Op", op, OP), 763 HPP__MEM_STAT_PRINT_FNS("Cache", cache, CACHE), 764 HPP__MEM_STAT_PRINT_FNS("Memory", memory, MEMORY), 765 HPP__MEM_STAT_PRINT_FNS("Snoop", snoop, SNOOP), 766 HPP__MEM_STAT_PRINT_FNS("D-TLB", dtlb, DTLB), 767 }; 768 769 struct perf_hpp_list perf_hpp_list = { 770 .fields = LIST_HEAD_INIT(perf_hpp_list.fields), 771 .sorts = LIST_HEAD_INIT(perf_hpp_list.sorts), 772 .nr_header_lines = 1, 773 }; 774 775 #undef HPP__COLOR_PRINT_FNS 776 #undef HPP__COLOR_ACC_PRINT_FNS 777 #undef HPP__PRINT_FNS 778 #undef HPP__MEM_STAT_PRINT_FNS 779 780 #undef HPP_PERCENT_FNS 781 #undef HPP_PERCENT_ACC_FNS 782 #undef HPP_RAW_FNS 783 #undef HPP_AVERAGE_FNS 784 #undef HPP_MEM_STAT_FNS 785 786 #undef __HPP_HEADER_FN 787 #undef __HPP_WIDTH_FN 788 #undef __HPP_COLOR_PERCENT_FN 789 #undef __HPP_ENTRY_PERCENT_FN 790 #undef __HPP_COLOR_ACC_PERCENT_FN 791 #undef __HPP_ENTRY_ACC_PERCENT_FN 792 #undef __HPP_ENTRY_RAW_FN 793 #undef __HPP_ENTRY_AVERAGE_FN 794 #undef __HPP_COLOR_MEM_STAT_FN 795 #undef __HPP_ENTRY_MEM_STAT_FN 796 797 #undef __HPP_SORT_FN 798 #undef __HPP_SORT_ACC_FN 799 #undef __HPP_SORT_RAW_FN 800 #undef __HPP_SORT_AVERAGE_FN 801 802 static void fmt_free(struct perf_hpp_fmt *fmt) 803 { 804 /* 805 * At this point fmt should be completely 806 * unhooked, if not it's a bug. 807 */ 808 BUG_ON(!list_empty(&fmt->list)); 809 BUG_ON(!list_empty(&fmt->sort_list)); 810 811 if (fmt->free) 812 fmt->free(fmt); 813 } 814 815 static bool fmt_equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b) 816 { 817 return a->equal && a->equal(a, b); 818 } 819 820 void perf_hpp__init(void) 821 { 822 int i; 823 824 for (i = 0; i < PERF_HPP__MAX_INDEX; i++) { 825 struct perf_hpp_fmt *fmt = &perf_hpp__format[i]; 826 827 INIT_LIST_HEAD(&fmt->list); 828 829 /* sort_list may be linked by setup_sorting() */ 830 if (fmt->sort_list.next == NULL) 831 INIT_LIST_HEAD(&fmt->sort_list); 832 } 833 834 /* 835 * If user specified field order, no need to setup default fields. 836 */ 837 if (is_strict_order(field_order)) 838 return; 839 840 /* 841 * Overhead and latency columns are added in setup_overhead(), 842 * so they are added implicitly here only if they were added 843 * by setup_overhead() before (have was_taken flag set). 844 * This is required because setup_overhead() has more complex 845 * logic, in particular it does not add "overhead" if user 846 * specified "latency" in sort order, and vise versa. 847 */ 848 if (symbol_conf.cumulate_callchain) { 849 /* 850 * Addition of fields is idempotent, so we add latency 851 * column twice to get desired order with simpler logic. 852 */ 853 if (symbol_conf.prefer_latency) 854 hpp_dimension__add_output(PERF_HPP__LATENCY_ACC, true); 855 hpp_dimension__add_output(PERF_HPP__OVERHEAD_ACC, true); 856 if (symbol_conf.enable_latency) 857 hpp_dimension__add_output(PERF_HPP__LATENCY_ACC, true); 858 perf_hpp__format[PERF_HPP__OVERHEAD].name = "Self"; 859 } 860 861 if (symbol_conf.prefer_latency) 862 hpp_dimension__add_output(PERF_HPP__LATENCY, true); 863 hpp_dimension__add_output(PERF_HPP__OVERHEAD, true); 864 if (symbol_conf.enable_latency) 865 hpp_dimension__add_output(PERF_HPP__LATENCY, true); 866 867 if (symbol_conf.show_cpu_utilization) { 868 hpp_dimension__add_output(PERF_HPP__OVERHEAD_SYS, false); 869 hpp_dimension__add_output(PERF_HPP__OVERHEAD_US, false); 870 871 if (perf_guest) { 872 hpp_dimension__add_output(PERF_HPP__OVERHEAD_GUEST_SYS, false); 873 hpp_dimension__add_output(PERF_HPP__OVERHEAD_GUEST_US, false); 874 } 875 } 876 877 if (symbol_conf.show_nr_samples) 878 hpp_dimension__add_output(PERF_HPP__SAMPLES, false); 879 880 if (symbol_conf.show_total_period) 881 hpp_dimension__add_output(PERF_HPP__PERIOD, false); 882 } 883 884 void perf_hpp_list__column_register(struct perf_hpp_list *list, 885 struct perf_hpp_fmt *format) 886 { 887 list_add_tail(&format->list, &list->fields); 888 } 889 890 void perf_hpp_list__register_sort_field(struct perf_hpp_list *list, 891 struct perf_hpp_fmt *format) 892 { 893 list_add_tail(&format->sort_list, &list->sorts); 894 } 895 896 void perf_hpp_list__prepend_sort_field(struct perf_hpp_list *list, 897 struct perf_hpp_fmt *format) 898 { 899 list_add(&format->sort_list, &list->sorts); 900 } 901 902 static void perf_hpp__column_unregister(struct perf_hpp_fmt *format) 903 { 904 list_del_init(&format->list); 905 list_del_init(&format->sort_list); 906 fmt_free(format); 907 } 908 909 void perf_hpp__cancel_cumulate(struct evlist *evlist) 910 { 911 struct perf_hpp_fmt *fmt, *acc, *ovh, *acc_lat, *tmp; 912 struct evsel *evsel; 913 914 if (is_strict_order(field_order)) 915 return; 916 917 ovh = &perf_hpp__format[PERF_HPP__OVERHEAD]; 918 acc = &perf_hpp__format[PERF_HPP__OVERHEAD_ACC]; 919 acc_lat = &perf_hpp__format[PERF_HPP__LATENCY_ACC]; 920 921 perf_hpp_list__for_each_format_safe(&perf_hpp_list, fmt, tmp) { 922 if (fmt_equal(acc, fmt) || fmt_equal(acc_lat, fmt)) { 923 perf_hpp__column_unregister(fmt); 924 continue; 925 } 926 927 if (fmt_equal(ovh, fmt)) 928 fmt->name = "Overhead"; 929 } 930 931 evlist__for_each_entry(evlist, evsel) { 932 struct hists *hists = evsel__hists(evsel); 933 struct perf_hpp_list_node *node; 934 935 list_for_each_entry(node, &hists->hpp_formats, list) { 936 perf_hpp_list__for_each_format_safe(&node->hpp, fmt, tmp) { 937 if (fmt_equal(acc, fmt) || fmt_equal(acc_lat, fmt)) { 938 perf_hpp__column_unregister(fmt); 939 continue; 940 } 941 942 if (fmt_equal(ovh, fmt)) 943 fmt->name = "Overhead"; 944 } 945 } 946 } 947 } 948 949 void perf_hpp__cancel_latency(struct evlist *evlist) 950 { 951 struct perf_hpp_fmt *fmt, *lat, *acc, *tmp; 952 struct evsel *evsel; 953 954 if (is_strict_order(field_order)) 955 return; 956 if (sort_order && strstr(sort_order, "latency")) 957 return; 958 959 lat = &perf_hpp__format[PERF_HPP__LATENCY]; 960 acc = &perf_hpp__format[PERF_HPP__LATENCY_ACC]; 961 962 perf_hpp_list__for_each_format_safe(&perf_hpp_list, fmt, tmp) { 963 if (fmt_equal(lat, fmt) || fmt_equal(acc, fmt)) 964 perf_hpp__column_unregister(fmt); 965 } 966 967 evlist__for_each_entry(evlist, evsel) { 968 struct hists *hists = evsel__hists(evsel); 969 struct perf_hpp_list_node *node; 970 971 list_for_each_entry(node, &hists->hpp_formats, list) { 972 perf_hpp_list__for_each_format_safe(&node->hpp, fmt, tmp) { 973 if (fmt_equal(lat, fmt) || fmt_equal(acc, fmt)) 974 perf_hpp__column_unregister(fmt); 975 } 976 } 977 } 978 } 979 980 void perf_hpp__setup_output_field(struct perf_hpp_list *list) 981 { 982 struct perf_hpp_fmt *fmt; 983 984 /* append sort keys to output field */ 985 perf_hpp_list__for_each_sort_list(list, fmt) { 986 struct perf_hpp_fmt *pos; 987 988 /* skip sort-only fields ("sort_compute" in perf diff) */ 989 if (!fmt->entry && !fmt->color) 990 continue; 991 992 perf_hpp_list__for_each_format(list, pos) { 993 if (fmt_equal(fmt, pos)) 994 goto next; 995 } 996 997 perf_hpp__column_register(fmt); 998 next: 999 continue; 1000 } 1001 } 1002 1003 void perf_hpp__append_sort_keys(struct perf_hpp_list *list) 1004 { 1005 struct perf_hpp_fmt *fmt; 1006 1007 /* append output fields to sort keys */ 1008 perf_hpp_list__for_each_format(list, fmt) { 1009 struct perf_hpp_fmt *pos; 1010 1011 perf_hpp_list__for_each_sort_list(list, pos) { 1012 if (fmt_equal(fmt, pos)) 1013 goto next; 1014 } 1015 1016 perf_hpp__register_sort_field(fmt); 1017 next: 1018 continue; 1019 } 1020 } 1021 1022 1023 void perf_hpp__reset_output_field(struct perf_hpp_list *list) 1024 { 1025 struct perf_hpp_fmt *fmt, *tmp; 1026 1027 /* reset output fields */ 1028 perf_hpp_list__for_each_format_safe(list, fmt, tmp) 1029 perf_hpp__column_unregister(fmt); 1030 1031 /* reset sort keys */ 1032 perf_hpp_list__for_each_sort_list_safe(list, fmt, tmp) 1033 perf_hpp__column_unregister(fmt); 1034 } 1035 1036 /* 1037 * See hists__fprintf to match the column widths 1038 */ 1039 unsigned int hists__sort_list_width(struct hists *hists) 1040 { 1041 struct perf_hpp_fmt *fmt; 1042 int ret = 0; 1043 bool first = true; 1044 struct perf_hpp dummy_hpp; 1045 1046 hists__for_each_format(hists, fmt) { 1047 if (perf_hpp__should_skip(fmt, hists)) 1048 continue; 1049 1050 if (first) 1051 first = false; 1052 else 1053 ret += 2; 1054 1055 ret += fmt->width(fmt, &dummy_hpp, hists); 1056 } 1057 1058 if (verbose > 0 && hists__has(hists, sym)) /* Addr + origin */ 1059 ret += 3 + BITS_PER_LONG / 4; 1060 1061 return ret; 1062 } 1063 1064 unsigned int hists__overhead_width(struct hists *hists) 1065 { 1066 struct perf_hpp_fmt *fmt; 1067 int ret = 0; 1068 bool first = true; 1069 struct perf_hpp dummy_hpp; 1070 1071 hists__for_each_format(hists, fmt) { 1072 if (perf_hpp__is_sort_entry(fmt) || perf_hpp__is_dynamic_entry(fmt)) 1073 break; 1074 1075 if (first) 1076 first = false; 1077 else 1078 ret += 2; 1079 1080 ret += fmt->width(fmt, &dummy_hpp, hists); 1081 } 1082 1083 return ret; 1084 } 1085 1086 void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists) 1087 { 1088 if (perf_hpp__is_sort_entry(fmt)) 1089 return perf_hpp__reset_sort_width(fmt, hists); 1090 1091 if (perf_hpp__is_dynamic_entry(fmt)) 1092 return; 1093 1094 BUG_ON(fmt->idx >= PERF_HPP__MAX_INDEX); 1095 1096 switch (fmt->idx) { 1097 case PERF_HPP__OVERHEAD: 1098 case PERF_HPP__LATENCY: 1099 case PERF_HPP__OVERHEAD_SYS: 1100 case PERF_HPP__OVERHEAD_US: 1101 case PERF_HPP__OVERHEAD_ACC: 1102 fmt->len = 8; 1103 break; 1104 1105 case PERF_HPP__OVERHEAD_GUEST_SYS: 1106 case PERF_HPP__OVERHEAD_GUEST_US: 1107 fmt->len = 9; 1108 break; 1109 1110 case PERF_HPP__SAMPLES: 1111 case PERF_HPP__PERIOD: 1112 fmt->len = 12; 1113 break; 1114 1115 case PERF_HPP__WEIGHT1: 1116 case PERF_HPP__WEIGHT2: 1117 case PERF_HPP__WEIGHT3: 1118 fmt->len = 8; 1119 break; 1120 1121 case PERF_HPP__MEM_STAT_OP: 1122 case PERF_HPP__MEM_STAT_CACHE: 1123 case PERF_HPP__MEM_STAT_MEMORY: 1124 case PERF_HPP__MEM_STAT_SNOOP: 1125 case PERF_HPP__MEM_STAT_DTLB: 1126 fmt->len = MEM_STAT_LEN * MEM_STAT_PRINT_LEN; 1127 break; 1128 1129 default: 1130 break; 1131 } 1132 } 1133 1134 void hists__reset_column_width(struct hists *hists) 1135 { 1136 struct perf_hpp_fmt *fmt; 1137 struct perf_hpp_list_node *node; 1138 1139 hists__for_each_format(hists, fmt) 1140 perf_hpp__reset_width(fmt, hists); 1141 1142 /* hierarchy entries have their own hpp list */ 1143 list_for_each_entry(node, &hists->hpp_formats, list) { 1144 perf_hpp_list__for_each_format(&node->hpp, fmt) 1145 perf_hpp__reset_width(fmt, hists); 1146 } 1147 } 1148 1149 void perf_hpp__set_user_width(const char *width_list_str) 1150 { 1151 struct perf_hpp_fmt *fmt; 1152 const char *ptr = width_list_str; 1153 1154 perf_hpp_list__for_each_format(&perf_hpp_list, fmt) { 1155 char *p; 1156 1157 int len = strtol(ptr, &p, 10); 1158 fmt->user_len = len; 1159 1160 if (*p == ',') 1161 ptr = p + 1; 1162 else 1163 break; 1164 } 1165 } 1166 1167 static int add_hierarchy_fmt(struct hists *hists, struct perf_hpp_fmt *fmt) 1168 { 1169 struct perf_hpp_list_node *node = NULL; 1170 struct perf_hpp_fmt *fmt_copy; 1171 bool found = false; 1172 bool skip = perf_hpp__should_skip(fmt, hists); 1173 1174 list_for_each_entry(node, &hists->hpp_formats, list) { 1175 if (node->level == fmt->level) { 1176 found = true; 1177 break; 1178 } 1179 } 1180 1181 if (!found) { 1182 node = malloc(sizeof(*node)); 1183 if (node == NULL) 1184 return -1; 1185 1186 node->skip = skip; 1187 node->level = fmt->level; 1188 perf_hpp_list__init(&node->hpp); 1189 1190 hists->nr_hpp_node++; 1191 list_add_tail(&node->list, &hists->hpp_formats); 1192 } 1193 1194 fmt_copy = perf_hpp_fmt__dup(fmt); 1195 if (fmt_copy == NULL) 1196 return -1; 1197 1198 if (!skip) 1199 node->skip = false; 1200 1201 list_add_tail(&fmt_copy->list, &node->hpp.fields); 1202 list_add_tail(&fmt_copy->sort_list, &node->hpp.sorts); 1203 1204 return 0; 1205 } 1206 1207 int perf_hpp__setup_hists_formats(struct perf_hpp_list *list, 1208 struct evlist *evlist) 1209 { 1210 struct evsel *evsel; 1211 struct perf_hpp_fmt *fmt; 1212 struct hists *hists; 1213 int ret; 1214 1215 if (!symbol_conf.report_hierarchy) 1216 return 0; 1217 1218 evlist__for_each_entry(evlist, evsel) { 1219 hists = evsel__hists(evsel); 1220 1221 perf_hpp_list__for_each_sort_list(list, fmt) { 1222 if (perf_hpp__is_dynamic_entry(fmt) && 1223 !perf_hpp__defined_dynamic_entry(fmt, hists)) 1224 continue; 1225 1226 ret = add_hierarchy_fmt(hists, fmt); 1227 if (ret < 0) 1228 return ret; 1229 } 1230 } 1231 1232 return 0; 1233 } 1234 1235 int perf_hpp__alloc_mem_stats(struct perf_hpp_list *list, struct evlist *evlist) 1236 { 1237 struct perf_hpp_fmt *fmt; 1238 struct evsel *evsel; 1239 enum mem_stat_type mst[16]; 1240 unsigned nr_mem_stats = 0; 1241 1242 perf_hpp_list__for_each_format(list, fmt) { 1243 if (!perf_hpp__is_mem_stat_entry(fmt)) 1244 continue; 1245 1246 assert(nr_mem_stats < ARRAY_SIZE(mst)); 1247 mst[nr_mem_stats++] = hpp__mem_stat_type(fmt); 1248 } 1249 1250 if (nr_mem_stats == 0) 1251 return 0; 1252 1253 list->nr_header_lines = 2; 1254 1255 evlist__for_each_entry(evlist, evsel) { 1256 struct hists *hists = evsel__hists(evsel); 1257 1258 hists->mem_stat_types = calloc(nr_mem_stats, 1259 sizeof(*hists->mem_stat_types)); 1260 if (hists->mem_stat_types == NULL) 1261 return -ENOMEM; 1262 1263 hists->mem_stat_total = calloc(nr_mem_stats, 1264 sizeof(*hists->mem_stat_total)); 1265 if (hists->mem_stat_total == NULL) 1266 return -ENOMEM; 1267 1268 memcpy(hists->mem_stat_types, mst, nr_mem_stats * sizeof(*mst)); 1269 hists->nr_mem_stats = nr_mem_stats; 1270 } 1271 return 0; 1272 } 1273