Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v6.17-rc5 1272 lines 32 kB view raw
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 28static 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 52struct hpp_fmt_value { 53 struct hists *hists; 54 u64 val; 55 int samples; 56}; 57 58static 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 122int 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 142int 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 155int 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 197static 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 206static 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; 230out_free: 231 free(fa); 232 free(fb); 233 *fields_a = *fields_b = NULL; 234 return -1; 235} 236 237static 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 270out: 271 free(fields_a); 272 free(fields_b); 273 274 return ret; 275} 276 277static 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 309out: 310 free(fields_a); 311 free(fields_b); 312 313 return ret; 314} 315 316static 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 341static bool perf_hpp__is_mem_stat_entry(struct perf_hpp_fmt *fmt); 342 343static 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 366static 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 372static 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 398static 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 411static 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 473int 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 489static 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) \ 503static u64 he_get_##_field(struct hist_entry *he) \ 504{ \ 505 return he->stat._field; \ 506} \ 507 \ 508static 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) \ 516static 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) \ 524static 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) \ 531static u64 he_get_acc_##_field(struct hist_entry *he) \ 532{ \ 533 return he->stat_acc->_field; \ 534} \ 535 \ 536static 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) \ 544static 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) \ 552static 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) \ 559static u64 he_get_raw_##_field(struct hist_entry *he) \ 560{ \ 561 return he->stat._field; \ 562} \ 563 \ 564static 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) \ 572static 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) \ 579static u64 he_get_##_field(struct hist_entry *he) \ 580{ \ 581 return he->stat._field; \ 582} \ 583 \ 584static 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) \ 592static 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) \ 599static 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) \ 608static 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 638HPP_PERCENT_FNS(overhead, period, PERF_HPP_FMT_TYPE__PERCENT) 639HPP_PERCENT_FNS(latency, latency, PERF_HPP_FMT_TYPE__LATENCY) 640HPP_PERCENT_FNS(overhead_sys, period_sys, PERF_HPP_FMT_TYPE__PERCENT) 641HPP_PERCENT_FNS(overhead_us, period_us, PERF_HPP_FMT_TYPE__PERCENT) 642HPP_PERCENT_FNS(overhead_guest_sys, period_guest_sys, PERF_HPP_FMT_TYPE__PERCENT) 643HPP_PERCENT_FNS(overhead_guest_us, period_guest_us, PERF_HPP_FMT_TYPE__PERCENT) 644HPP_PERCENT_ACC_FNS(overhead_acc, period, PERF_HPP_FMT_TYPE__PERCENT) 645HPP_PERCENT_ACC_FNS(latency_acc, latency, PERF_HPP_FMT_TYPE__LATENCY) 646 647HPP_RAW_FNS(samples, nr_events) 648HPP_RAW_FNS(period, period) 649 650HPP_AVERAGE_FNS(weight1, weight1) 651HPP_AVERAGE_FNS(weight2, weight2) 652HPP_AVERAGE_FNS(weight3, weight3) 653 654HPP_MEM_STAT_FNS(op, OP) 655HPP_MEM_STAT_FNS(cache, CACHE) 656HPP_MEM_STAT_FNS(memory, MEMORY) 657HPP_MEM_STAT_FNS(snoop, SNOOP) 658HPP_MEM_STAT_FNS(dtlb, DTLB) 659 660static 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 667static bool perf_hpp__is_mem_stat_entry(struct perf_hpp_fmt *fmt) 668{ 669 return fmt->sort == hpp__sort_mem_stat; 670} 671 672static bool perf_hpp__is_hpp_entry(struct perf_hpp_fmt *a) 673{ 674 return a->header == hpp__header_fn; 675} 676 677static 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 685static 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 748struct 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 769struct 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 802static 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 815static bool fmt_equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b) 816{ 817 return a->equal && a->equal(a, b); 818} 819 820void 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 884void 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 890void 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 896void 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 902static 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 909void 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 949void 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 980void 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); 998next: 999 continue; 1000 } 1001} 1002 1003void 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); 1017next: 1018 continue; 1019 } 1020} 1021 1022 1023void 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 */ 1039unsigned 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 1064unsigned 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 1086void 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 1134void 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 1149void 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 1167static 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 1207int 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 1235int 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}