1 #include <sys/mman.h> 2 #include "sort.h" 3 #include "hist.h" 4 #include "comm.h" 5 #include "symbol.h" 6 #include "evsel.h" 7 8 regex_t parent_regex; 9 const char default_parent_pattern[] = "^sys_|^do_page_fault"; 10 const char *parent_pattern = default_parent_pattern; 11 const char default_sort_order[] = "comm,dso,symbol"; 12 const char default_branch_sort_order[] = "comm,dso_from,symbol_from,symbol_to,cycles"; 13 const char default_mem_sort_order[] = "local_weight,mem,sym,dso,symbol_daddr,dso_daddr,snoop,tlb,locked"; 14 const char default_top_sort_order[] = "dso,symbol"; 15 const char default_diff_sort_order[] = "dso,symbol"; 16 const char *sort_order; 17 const char *field_order; 18 regex_t ignore_callees_regex; 19 int have_ignore_callees = 0; 20 int sort__need_collapse = 0; 21 int sort__has_parent = 0; 22 int sort__has_sym = 0; 23 int sort__has_dso = 0; 24 enum sort_mode sort__mode = SORT_MODE__NORMAL; 25 26 27 static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...) 28 { 29 int n; 30 va_list ap; 31 32 va_start(ap, fmt); 33 n = vsnprintf(bf, size, fmt, ap); 34 if (symbol_conf.field_sep && n > 0) { 35 char *sep = bf; 36 37 while (1) { 38 sep = strchr(sep, *symbol_conf.field_sep); 39 if (sep == NULL) 40 break; 41 *sep = '.'; 42 } 43 } 44 va_end(ap); 45 46 if (n >= (int)size) 47 return size - 1; 48 return n; 49 } 50 51 static int64_t cmp_null(const void *l, const void *r) 52 { 53 if (!l && !r) 54 return 0; 55 else if (!l) 56 return -1; 57 else 58 return 1; 59 } 60 61 /* --sort pid */ 62 63 static int64_t 64 sort__thread_cmp(struct hist_entry *left, struct hist_entry *right) 65 { 66 return right->thread->tid - left->thread->tid; 67 } 68 69 static int hist_entry__thread_snprintf(struct hist_entry *he, char *bf, 70 size_t size, unsigned int width) 71 { 72 const char *comm = thread__comm_str(he->thread); 73 74 width = max(7U, width) - 6; 75 return repsep_snprintf(bf, size, "%5d:%-*.*s", he->thread->tid, 76 width, width, comm ?: ""); 77 } 78 79 struct sort_entry sort_thread = { 80 .se_header = " Pid:Command", 81 .se_cmp = sort__thread_cmp, 82 .se_snprintf = hist_entry__thread_snprintf, 83 .se_width_idx = HISTC_THREAD, 84 }; 85 86 /* --sort comm */ 87 88 static int64_t 89 sort__comm_cmp(struct hist_entry *left, struct hist_entry *right) 90 { 91 /* Compare the addr that should be unique among comm */ 92 return strcmp(comm__str(right->comm), comm__str(left->comm)); 93 } 94 95 static int64_t 96 sort__comm_collapse(struct hist_entry *left, struct hist_entry *right) 97 { 98 /* Compare the addr that should be unique among comm */ 99 return strcmp(comm__str(right->comm), comm__str(left->comm)); 100 } 101 102 static int64_t 103 sort__comm_sort(struct hist_entry *left, struct hist_entry *right) 104 { 105 return strcmp(comm__str(right->comm), comm__str(left->comm)); 106 } 107 108 static int hist_entry__comm_snprintf(struct hist_entry *he, char *bf, 109 size_t size, unsigned int width) 110 { 111 return repsep_snprintf(bf, size, "%-*.*s", width, width, comm__str(he->comm)); 112 } 113 114 struct sort_entry sort_comm = { 115 .se_header = "Command", 116 .se_cmp = sort__comm_cmp, 117 .se_collapse = sort__comm_collapse, 118 .se_sort = sort__comm_sort, 119 .se_snprintf = hist_entry__comm_snprintf, 120 .se_width_idx = HISTC_COMM, 121 }; 122 123 /* --sort dso */ 124 125 static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r) 126 { 127 struct dso *dso_l = map_l ? map_l->dso : NULL; 128 struct dso *dso_r = map_r ? map_r->dso : NULL; 129 const char *dso_name_l, *dso_name_r; 130 131 if (!dso_l || !dso_r) 132 return cmp_null(dso_r, dso_l); 133 134 if (verbose) { 135 dso_name_l = dso_l->long_name; 136 dso_name_r = dso_r->long_name; 137 } else { 138 dso_name_l = dso_l->short_name; 139 dso_name_r = dso_r->short_name; 140 } 141 142 return strcmp(dso_name_l, dso_name_r); 143 } 144 145 static int64_t 146 sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) 147 { 148 return _sort__dso_cmp(right->ms.map, left->ms.map); 149 } 150 151 static int _hist_entry__dso_snprintf(struct map *map, char *bf, 152 size_t size, unsigned int width) 153 { 154 if (map && map->dso) { 155 const char *dso_name = !verbose ? map->dso->short_name : 156 map->dso->long_name; 157 return repsep_snprintf(bf, size, "%-*.*s", width, width, dso_name); 158 } 159 160 return repsep_snprintf(bf, size, "%-*.*s", width, width, "[unknown]"); 161 } 162 163 static int hist_entry__dso_snprintf(struct hist_entry *he, char *bf, 164 size_t size, unsigned int width) 165 { 166 return _hist_entry__dso_snprintf(he->ms.map, bf, size, width); 167 } 168 169 struct sort_entry sort_dso = { 170 .se_header = "Shared Object", 171 .se_cmp = sort__dso_cmp, 172 .se_snprintf = hist_entry__dso_snprintf, 173 .se_width_idx = HISTC_DSO, 174 }; 175 176 /* --sort symbol */ 177 178 static int64_t _sort__addr_cmp(u64 left_ip, u64 right_ip) 179 { 180 return (int64_t)(right_ip - left_ip); 181 } 182 183 static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r) 184 { 185 if (!sym_l || !sym_r) 186 return cmp_null(sym_l, sym_r); 187 188 if (sym_l == sym_r) 189 return 0; 190 191 if (sym_l->start != sym_r->start) 192 return (int64_t)(sym_r->start - sym_l->start); 193 194 return (int64_t)(sym_r->end - sym_l->end); 195 } 196 197 static int64_t 198 sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) 199 { 200 int64_t ret; 201 202 if (!left->ms.sym && !right->ms.sym) 203 return _sort__addr_cmp(left->ip, right->ip); 204 205 /* 206 * comparing symbol address alone is not enough since it's a 207 * relative address within a dso. 208 */ 209 if (!sort__has_dso) { 210 ret = sort__dso_cmp(left, right); 211 if (ret != 0) 212 return ret; 213 } 214 215 return _sort__sym_cmp(left->ms.sym, right->ms.sym); 216 } 217 218 static int64_t 219 sort__sym_sort(struct hist_entry *left, struct hist_entry *right) 220 { 221 if (!left->ms.sym || !right->ms.sym) 222 return cmp_null(left->ms.sym, right->ms.sym); 223 224 return strcmp(right->ms.sym->name, left->ms.sym->name); 225 } 226 227 static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym, 228 u64 ip, char level, char *bf, size_t size, 229 unsigned int width) 230 { 231 size_t ret = 0; 232 233 if (verbose) { 234 char o = map ? dso__symtab_origin(map->dso) : '!'; 235 ret += repsep_snprintf(bf, size, "%-#*llx %c ", 236 BITS_PER_LONG / 4 + 2, ip, o); 237 } 238 239 ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level); 240 if (sym && map) { 241 if (map->type == MAP__VARIABLE) { 242 ret += repsep_snprintf(bf + ret, size - ret, "%s", sym->name); 243 ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx", 244 ip - map->unmap_ip(map, sym->start)); 245 ret += repsep_snprintf(bf + ret, size - ret, "%-*s", 246 width - ret, ""); 247 } else { 248 ret += repsep_snprintf(bf + ret, size - ret, "%-*s", 249 width - ret, 250 sym->name); 251 } 252 } else { 253 size_t len = BITS_PER_LONG / 4; 254 ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx", 255 len, ip); 256 ret += repsep_snprintf(bf + ret, size - ret, "%-*s", 257 width - ret, ""); 258 } 259 260 if (ret > width) 261 bf[width] = '\0'; 262 263 return width; 264 } 265 266 static int hist_entry__sym_snprintf(struct hist_entry *he, char *bf, 267 size_t size, unsigned int width) 268 { 269 return _hist_entry__sym_snprintf(he->ms.map, he->ms.sym, he->ip, 270 he->level, bf, size, width); 271 } 272 273 struct sort_entry sort_sym = { 274 .se_header = "Symbol", 275 .se_cmp = sort__sym_cmp, 276 .se_sort = sort__sym_sort, 277 .se_snprintf = hist_entry__sym_snprintf, 278 .se_width_idx = HISTC_SYMBOL, 279 }; 280 281 /* --sort srcline */ 282 283 static int64_t 284 sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right) 285 { 286 if (!left->srcline) { 287 if (!left->ms.map) 288 left->srcline = SRCLINE_UNKNOWN; 289 else { 290 struct map *map = left->ms.map; 291 left->srcline = get_srcline(map->dso, 292 map__rip_2objdump(map, left->ip), 293 left->ms.sym, true); 294 } 295 } 296 if (!right->srcline) { 297 if (!right->ms.map) 298 right->srcline = SRCLINE_UNKNOWN; 299 else { 300 struct map *map = right->ms.map; 301 right->srcline = get_srcline(map->dso, 302 map__rip_2objdump(map, right->ip), 303 right->ms.sym, true); 304 } 305 } 306 return strcmp(right->srcline, left->srcline); 307 } 308 309 static int hist_entry__srcline_snprintf(struct hist_entry *he, char *bf, 310 size_t size, unsigned int width) 311 { 312 return repsep_snprintf(bf, size, "%-*.*s", width, width, he->srcline); 313 } 314 315 struct sort_entry sort_srcline = { 316 .se_header = "Source:Line", 317 .se_cmp = sort__srcline_cmp, 318 .se_snprintf = hist_entry__srcline_snprintf, 319 .se_width_idx = HISTC_SRCLINE, 320 }; 321 322 /* --sort srcfile */ 323 324 static char no_srcfile[1]; 325 326 static char *get_srcfile(struct hist_entry *e) 327 { 328 char *sf, *p; 329 struct map *map = e->ms.map; 330 331 sf = get_srcline(map->dso, map__rip_2objdump(map, e->ip), 332 e->ms.sym, true); 333 if (!strcmp(sf, SRCLINE_UNKNOWN)) 334 return no_srcfile; 335 p = strchr(sf, ':'); 336 if (p && *sf) { 337 *p = 0; 338 return sf; 339 } 340 free(sf); 341 return no_srcfile; 342 } 343 344 static int64_t 345 sort__srcfile_cmp(struct hist_entry *left, struct hist_entry *right) 346 { 347 if (!left->srcfile) { 348 if (!left->ms.map) 349 left->srcfile = no_srcfile; 350 else 351 left->srcfile = get_srcfile(left); 352 } 353 if (!right->srcfile) { 354 if (!right->ms.map) 355 right->srcfile = no_srcfile; 356 else 357 right->srcfile = get_srcfile(right); 358 } 359 return strcmp(right->srcfile, left->srcfile); 360 } 361 362 static int hist_entry__srcfile_snprintf(struct hist_entry *he, char *bf, 363 size_t size, unsigned int width) 364 { 365 return repsep_snprintf(bf, size, "%-*.*s", width, width, he->srcfile); 366 } 367 368 struct sort_entry sort_srcfile = { 369 .se_header = "Source File", 370 .se_cmp = sort__srcfile_cmp, 371 .se_snprintf = hist_entry__srcfile_snprintf, 372 .se_width_idx = HISTC_SRCFILE, 373 }; 374 375 /* --sort parent */ 376 377 static int64_t 378 sort__parent_cmp(struct hist_entry *left, struct hist_entry *right) 379 { 380 struct symbol *sym_l = left->parent; 381 struct symbol *sym_r = right->parent; 382 383 if (!sym_l || !sym_r) 384 return cmp_null(sym_l, sym_r); 385 386 return strcmp(sym_r->name, sym_l->name); 387 } 388 389 static int hist_entry__parent_snprintf(struct hist_entry *he, char *bf, 390 size_t size, unsigned int width) 391 { 392 return repsep_snprintf(bf, size, "%-*.*s", width, width, 393 he->parent ? he->parent->name : "[other]"); 394 } 395 396 struct sort_entry sort_parent = { 397 .se_header = "Parent symbol", 398 .se_cmp = sort__parent_cmp, 399 .se_snprintf = hist_entry__parent_snprintf, 400 .se_width_idx = HISTC_PARENT, 401 }; 402 403 /* --sort cpu */ 404 405 static int64_t 406 sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right) 407 { 408 return right->cpu - left->cpu; 409 } 410 411 static int hist_entry__cpu_snprintf(struct hist_entry *he, char *bf, 412 size_t size, unsigned int width) 413 { 414 return repsep_snprintf(bf, size, "%*.*d", width, width, he->cpu); 415 } 416 417 struct sort_entry sort_cpu = { 418 .se_header = "CPU", 419 .se_cmp = sort__cpu_cmp, 420 .se_snprintf = hist_entry__cpu_snprintf, 421 .se_width_idx = HISTC_CPU, 422 }; 423 424 /* sort keys for branch stacks */ 425 426 static int64_t 427 sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right) 428 { 429 if (!left->branch_info || !right->branch_info) 430 return cmp_null(left->branch_info, right->branch_info); 431 432 return _sort__dso_cmp(left->branch_info->from.map, 433 right->branch_info->from.map); 434 } 435 436 static int hist_entry__dso_from_snprintf(struct hist_entry *he, char *bf, 437 size_t size, unsigned int width) 438 { 439 if (he->branch_info) 440 return _hist_entry__dso_snprintf(he->branch_info->from.map, 441 bf, size, width); 442 else 443 return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A"); 444 } 445 446 static int64_t 447 sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right) 448 { 449 if (!left->branch_info || !right->branch_info) 450 return cmp_null(left->branch_info, right->branch_info); 451 452 return _sort__dso_cmp(left->branch_info->to.map, 453 right->branch_info->to.map); 454 } 455 456 static int hist_entry__dso_to_snprintf(struct hist_entry *he, char *bf, 457 size_t size, unsigned int width) 458 { 459 if (he->branch_info) 460 return _hist_entry__dso_snprintf(he->branch_info->to.map, 461 bf, size, width); 462 else 463 return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A"); 464 } 465 466 static int64_t 467 sort__sym_from_cmp(struct hist_entry *left, struct hist_entry *right) 468 { 469 struct addr_map_symbol *from_l = &left->branch_info->from; 470 struct addr_map_symbol *from_r = &right->branch_info->from; 471 472 if (!left->branch_info || !right->branch_info) 473 return cmp_null(left->branch_info, right->branch_info); 474 475 from_l = &left->branch_info->from; 476 from_r = &right->branch_info->from; 477 478 if (!from_l->sym && !from_r->sym) 479 return _sort__addr_cmp(from_l->addr, from_r->addr); 480 481 return _sort__sym_cmp(from_l->sym, from_r->sym); 482 } 483 484 static int64_t 485 sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right) 486 { 487 struct addr_map_symbol *to_l, *to_r; 488 489 if (!left->branch_info || !right->branch_info) 490 return cmp_null(left->branch_info, right->branch_info); 491 492 to_l = &left->branch_info->to; 493 to_r = &right->branch_info->to; 494 495 if (!to_l->sym && !to_r->sym) 496 return _sort__addr_cmp(to_l->addr, to_r->addr); 497 498 return _sort__sym_cmp(to_l->sym, to_r->sym); 499 } 500 501 static int hist_entry__sym_from_snprintf(struct hist_entry *he, char *bf, 502 size_t size, unsigned int width) 503 { 504 if (he->branch_info) { 505 struct addr_map_symbol *from = &he->branch_info->from; 506 507 return _hist_entry__sym_snprintf(from->map, from->sym, from->addr, 508 he->level, bf, size, width); 509 } 510 511 return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A"); 512 } 513 514 static int hist_entry__sym_to_snprintf(struct hist_entry *he, char *bf, 515 size_t size, unsigned int width) 516 { 517 if (he->branch_info) { 518 struct addr_map_symbol *to = &he->branch_info->to; 519 520 return _hist_entry__sym_snprintf(to->map, to->sym, to->addr, 521 he->level, bf, size, width); 522 } 523 524 return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A"); 525 } 526 527 struct sort_entry sort_dso_from = { 528 .se_header = "Source Shared Object", 529 .se_cmp = sort__dso_from_cmp, 530 .se_snprintf = hist_entry__dso_from_snprintf, 531 .se_width_idx = HISTC_DSO_FROM, 532 }; 533 534 struct sort_entry sort_dso_to = { 535 .se_header = "Target Shared Object", 536 .se_cmp = sort__dso_to_cmp, 537 .se_snprintf = hist_entry__dso_to_snprintf, 538 .se_width_idx = HISTC_DSO_TO, 539 }; 540 541 struct sort_entry sort_sym_from = { 542 .se_header = "Source Symbol", 543 .se_cmp = sort__sym_from_cmp, 544 .se_snprintf = hist_entry__sym_from_snprintf, 545 .se_width_idx = HISTC_SYMBOL_FROM, 546 }; 547 548 struct sort_entry sort_sym_to = { 549 .se_header = "Target Symbol", 550 .se_cmp = sort__sym_to_cmp, 551 .se_snprintf = hist_entry__sym_to_snprintf, 552 .se_width_idx = HISTC_SYMBOL_TO, 553 }; 554 555 static int64_t 556 sort__mispredict_cmp(struct hist_entry *left, struct hist_entry *right) 557 { 558 unsigned char mp, p; 559 560 if (!left->branch_info || !right->branch_info) 561 return cmp_null(left->branch_info, right->branch_info); 562 563 mp = left->branch_info->flags.mispred != right->branch_info->flags.mispred; 564 p = left->branch_info->flags.predicted != right->branch_info->flags.predicted; 565 return mp || p; 566 } 567 568 static int hist_entry__mispredict_snprintf(struct hist_entry *he, char *bf, 569 size_t size, unsigned int width){ 570 static const char *out = "N/A"; 571 572 if (he->branch_info) { 573 if (he->branch_info->flags.predicted) 574 out = "N"; 575 else if (he->branch_info->flags.mispred) 576 out = "Y"; 577 } 578 579 return repsep_snprintf(bf, size, "%-*.*s", width, width, out); 580 } 581 582 static int64_t 583 sort__cycles_cmp(struct hist_entry *left, struct hist_entry *right) 584 { 585 return left->branch_info->flags.cycles - 586 right->branch_info->flags.cycles; 587 } 588 589 static int hist_entry__cycles_snprintf(struct hist_entry *he, char *bf, 590 size_t size, unsigned int width) 591 { 592 if (he->branch_info->flags.cycles == 0) 593 return repsep_snprintf(bf, size, "%-*s", width, "-"); 594 return repsep_snprintf(bf, size, "%-*hd", width, 595 he->branch_info->flags.cycles); 596 } 597 598 struct sort_entry sort_cycles = { 599 .se_header = "Basic Block Cycles", 600 .se_cmp = sort__cycles_cmp, 601 .se_snprintf = hist_entry__cycles_snprintf, 602 .se_width_idx = HISTC_CYCLES, 603 }; 604 605 /* --sort daddr_sym */ 606 static int64_t 607 sort__daddr_cmp(struct hist_entry *left, struct hist_entry *right) 608 { 609 uint64_t l = 0, r = 0; 610 611 if (left->mem_info) 612 l = left->mem_info->daddr.addr; 613 if (right->mem_info) 614 r = right->mem_info->daddr.addr; 615 616 return (int64_t)(r - l); 617 } 618 619 static int hist_entry__daddr_snprintf(struct hist_entry *he, char *bf, 620 size_t size, unsigned int width) 621 { 622 uint64_t addr = 0; 623 struct map *map = NULL; 624 struct symbol *sym = NULL; 625 626 if (he->mem_info) { 627 addr = he->mem_info->daddr.addr; 628 map = he->mem_info->daddr.map; 629 sym = he->mem_info->daddr.sym; 630 } 631 return _hist_entry__sym_snprintf(map, sym, addr, he->level, bf, size, 632 width); 633 } 634 635 static int64_t 636 sort__dso_daddr_cmp(struct hist_entry *left, struct hist_entry *right) 637 { 638 struct map *map_l = NULL; 639 struct map *map_r = NULL; 640 641 if (left->mem_info) 642 map_l = left->mem_info->daddr.map; 643 if (right->mem_info) 644 map_r = right->mem_info->daddr.map; 645 646 return _sort__dso_cmp(map_l, map_r); 647 } 648 649 static int hist_entry__dso_daddr_snprintf(struct hist_entry *he, char *bf, 650 size_t size, unsigned int width) 651 { 652 struct map *map = NULL; 653 654 if (he->mem_info) 655 map = he->mem_info->daddr.map; 656 657 return _hist_entry__dso_snprintf(map, bf, size, width); 658 } 659 660 static int64_t 661 sort__locked_cmp(struct hist_entry *left, struct hist_entry *right) 662 { 663 union perf_mem_data_src data_src_l; 664 union perf_mem_data_src data_src_r; 665 666 if (left->mem_info) 667 data_src_l = left->mem_info->data_src; 668 else 669 data_src_l.mem_lock = PERF_MEM_LOCK_NA; 670 671 if (right->mem_info) 672 data_src_r = right->mem_info->data_src; 673 else 674 data_src_r.mem_lock = PERF_MEM_LOCK_NA; 675 676 return (int64_t)(data_src_r.mem_lock - data_src_l.mem_lock); 677 } 678 679 static int hist_entry__locked_snprintf(struct hist_entry *he, char *bf, 680 size_t size, unsigned int width) 681 { 682 const char *out; 683 u64 mask = PERF_MEM_LOCK_NA; 684 685 if (he->mem_info) 686 mask = he->mem_info->data_src.mem_lock; 687 688 if (mask & PERF_MEM_LOCK_NA) 689 out = "N/A"; 690 else if (mask & PERF_MEM_LOCK_LOCKED) 691 out = "Yes"; 692 else 693 out = "No"; 694 695 return repsep_snprintf(bf, size, "%-*s", width, out); 696 } 697 698 static int64_t 699 sort__tlb_cmp(struct hist_entry *left, struct hist_entry *right) 700 { 701 union perf_mem_data_src data_src_l; 702 union perf_mem_data_src data_src_r; 703 704 if (left->mem_info) 705 data_src_l = left->mem_info->data_src; 706 else 707 data_src_l.mem_dtlb = PERF_MEM_TLB_NA; 708 709 if (right->mem_info) 710 data_src_r = right->mem_info->data_src; 711 else 712 data_src_r.mem_dtlb = PERF_MEM_TLB_NA; 713 714 return (int64_t)(data_src_r.mem_dtlb - data_src_l.mem_dtlb); 715 } 716 717 static const char * const tlb_access[] = { 718 "N/A", 719 "HIT", 720 "MISS", 721 "L1", 722 "L2", 723 "Walker", 724 "Fault", 725 }; 726 #define NUM_TLB_ACCESS (sizeof(tlb_access)/sizeof(const char *)) 727 728 static int hist_entry__tlb_snprintf(struct hist_entry *he, char *bf, 729 size_t size, unsigned int width) 730 { 731 char out[64]; 732 size_t sz = sizeof(out) - 1; /* -1 for null termination */ 733 size_t l = 0, i; 734 u64 m = PERF_MEM_TLB_NA; 735 u64 hit, miss; 736 737 out[0] = '\0'; 738 739 if (he->mem_info) 740 m = he->mem_info->data_src.mem_dtlb; 741 742 hit = m & PERF_MEM_TLB_HIT; 743 miss = m & PERF_MEM_TLB_MISS; 744 745 /* already taken care of */ 746 m &= ~(PERF_MEM_TLB_HIT|PERF_MEM_TLB_MISS); 747 748 for (i = 0; m && i < NUM_TLB_ACCESS; i++, m >>= 1) { 749 if (!(m & 0x1)) 750 continue; 751 if (l) { 752 strcat(out, " or "); 753 l += 4; 754 } 755 strncat(out, tlb_access[i], sz - l); 756 l += strlen(tlb_access[i]); 757 } 758 if (*out == '\0') 759 strcpy(out, "N/A"); 760 if (hit) 761 strncat(out, " hit", sz - l); 762 if (miss) 763 strncat(out, " miss", sz - l); 764 765 return repsep_snprintf(bf, size, "%-*s", width, out); 766 } 767 768 static int64_t 769 sort__lvl_cmp(struct hist_entry *left, struct hist_entry *right) 770 { 771 union perf_mem_data_src data_src_l; 772 union perf_mem_data_src data_src_r; 773 774 if (left->mem_info) 775 data_src_l = left->mem_info->data_src; 776 else 777 data_src_l.mem_lvl = PERF_MEM_LVL_NA; 778 779 if (right->mem_info) 780 data_src_r = right->mem_info->data_src; 781 else 782 data_src_r.mem_lvl = PERF_MEM_LVL_NA; 783 784 return (int64_t)(data_src_r.mem_lvl - data_src_l.mem_lvl); 785 } 786 787 static const char * const mem_lvl[] = { 788 "N/A", 789 "HIT", 790 "MISS", 791 "L1", 792 "LFB", 793 "L2", 794 "L3", 795 "Local RAM", 796 "Remote RAM (1 hop)", 797 "Remote RAM (2 hops)", 798 "Remote Cache (1 hop)", 799 "Remote Cache (2 hops)", 800 "I/O", 801 "Uncached", 802 }; 803 #define NUM_MEM_LVL (sizeof(mem_lvl)/sizeof(const char *)) 804 805 static int hist_entry__lvl_snprintf(struct hist_entry *he, char *bf, 806 size_t size, unsigned int width) 807 { 808 char out[64]; 809 size_t sz = sizeof(out) - 1; /* -1 for null termination */ 810 size_t i, l = 0; 811 u64 m = PERF_MEM_LVL_NA; 812 u64 hit, miss; 813 814 if (he->mem_info) 815 m = he->mem_info->data_src.mem_lvl; 816 817 out[0] = '\0'; 818 819 hit = m & PERF_MEM_LVL_HIT; 820 miss = m & PERF_MEM_LVL_MISS; 821 822 /* already taken care of */ 823 m &= ~(PERF_MEM_LVL_HIT|PERF_MEM_LVL_MISS); 824 825 for (i = 0; m && i < NUM_MEM_LVL; i++, m >>= 1) { 826 if (!(m & 0x1)) 827 continue; 828 if (l) { 829 strcat(out, " or "); 830 l += 4; 831 } 832 strncat(out, mem_lvl[i], sz - l); 833 l += strlen(mem_lvl[i]); 834 } 835 if (*out == '\0') 836 strcpy(out, "N/A"); 837 if (hit) 838 strncat(out, " hit", sz - l); 839 if (miss) 840 strncat(out, " miss", sz - l); 841 842 return repsep_snprintf(bf, size, "%-*s", width, out); 843 } 844 845 static int64_t 846 sort__snoop_cmp(struct hist_entry *left, struct hist_entry *right) 847 { 848 union perf_mem_data_src data_src_l; 849 union perf_mem_data_src data_src_r; 850 851 if (left->mem_info) 852 data_src_l = left->mem_info->data_src; 853 else 854 data_src_l.mem_snoop = PERF_MEM_SNOOP_NA; 855 856 if (right->mem_info) 857 data_src_r = right->mem_info->data_src; 858 else 859 data_src_r.mem_snoop = PERF_MEM_SNOOP_NA; 860 861 return (int64_t)(data_src_r.mem_snoop - data_src_l.mem_snoop); 862 } 863 864 static const char * const snoop_access[] = { 865 "N/A", 866 "None", 867 "Miss", 868 "Hit", 869 "HitM", 870 }; 871 #define NUM_SNOOP_ACCESS (sizeof(snoop_access)/sizeof(const char *)) 872 873 static int hist_entry__snoop_snprintf(struct hist_entry *he, char *bf, 874 size_t size, unsigned int width) 875 { 876 char out[64]; 877 size_t sz = sizeof(out) - 1; /* -1 for null termination */ 878 size_t i, l = 0; 879 u64 m = PERF_MEM_SNOOP_NA; 880 881 out[0] = '\0'; 882 883 if (he->mem_info) 884 m = he->mem_info->data_src.mem_snoop; 885 886 for (i = 0; m && i < NUM_SNOOP_ACCESS; i++, m >>= 1) { 887 if (!(m & 0x1)) 888 continue; 889 if (l) { 890 strcat(out, " or "); 891 l += 4; 892 } 893 strncat(out, snoop_access[i], sz - l); 894 l += strlen(snoop_access[i]); 895 } 896 897 if (*out == '\0') 898 strcpy(out, "N/A"); 899 900 return repsep_snprintf(bf, size, "%-*s", width, out); 901 } 902 903 static inline u64 cl_address(u64 address) 904 { 905 /* return the cacheline of the address */ 906 return (address & ~(cacheline_size - 1)); 907 } 908 909 static int64_t 910 sort__dcacheline_cmp(struct hist_entry *left, struct hist_entry *right) 911 { 912 u64 l, r; 913 struct map *l_map, *r_map; 914 915 if (!left->mem_info) return -1; 916 if (!right->mem_info) return 1; 917 918 /* group event types together */ 919 if (left->cpumode > right->cpumode) return -1; 920 if (left->cpumode < right->cpumode) return 1; 921 922 l_map = left->mem_info->daddr.map; 923 r_map = right->mem_info->daddr.map; 924 925 /* if both are NULL, jump to sort on al_addr instead */ 926 if (!l_map && !r_map) 927 goto addr; 928 929 if (!l_map) return -1; 930 if (!r_map) return 1; 931 932 if (l_map->maj > r_map->maj) return -1; 933 if (l_map->maj < r_map->maj) return 1; 934 935 if (l_map->min > r_map->min) return -1; 936 if (l_map->min < r_map->min) return 1; 937 938 if (l_map->ino > r_map->ino) return -1; 939 if (l_map->ino < r_map->ino) return 1; 940 941 if (l_map->ino_generation > r_map->ino_generation) return -1; 942 if (l_map->ino_generation < r_map->ino_generation) return 1; 943 944 /* 945 * Addresses with no major/minor numbers are assumed to be 946 * anonymous in userspace. Sort those on pid then address. 947 * 948 * The kernel and non-zero major/minor mapped areas are 949 * assumed to be unity mapped. Sort those on address. 950 */ 951 952 if ((left->cpumode != PERF_RECORD_MISC_KERNEL) && 953 (!(l_map->flags & MAP_SHARED)) && 954 !l_map->maj && !l_map->min && !l_map->ino && 955 !l_map->ino_generation) { 956 /* userspace anonymous */ 957 958 if (left->thread->pid_ > right->thread->pid_) return -1; 959 if (left->thread->pid_ < right->thread->pid_) return 1; 960 } 961 962 addr: 963 /* al_addr does all the right addr - start + offset calculations */ 964 l = cl_address(left->mem_info->daddr.al_addr); 965 r = cl_address(right->mem_info->daddr.al_addr); 966 967 if (l > r) return -1; 968 if (l < r) return 1; 969 970 return 0; 971 } 972 973 static int hist_entry__dcacheline_snprintf(struct hist_entry *he, char *bf, 974 size_t size, unsigned int width) 975 { 976 977 uint64_t addr = 0; 978 struct map *map = NULL; 979 struct symbol *sym = NULL; 980 char level = he->level; 981 982 if (he->mem_info) { 983 addr = cl_address(he->mem_info->daddr.al_addr); 984 map = he->mem_info->daddr.map; 985 sym = he->mem_info->daddr.sym; 986 987 /* print [s] for shared data mmaps */ 988 if ((he->cpumode != PERF_RECORD_MISC_KERNEL) && 989 map && (map->type == MAP__VARIABLE) && 990 (map->flags & MAP_SHARED) && 991 (map->maj || map->min || map->ino || 992 map->ino_generation)) 993 level = 's'; 994 else if (!map) 995 level = 'X'; 996 } 997 return _hist_entry__sym_snprintf(map, sym, addr, level, bf, size, 998 width); 999 } 1000 1001 struct sort_entry sort_mispredict = { 1002 .se_header = "Branch Mispredicted", 1003 .se_cmp = sort__mispredict_cmp, 1004 .se_snprintf = hist_entry__mispredict_snprintf, 1005 .se_width_idx = HISTC_MISPREDICT, 1006 }; 1007 1008 static u64 he_weight(struct hist_entry *he) 1009 { 1010 return he->stat.nr_events ? he->stat.weight / he->stat.nr_events : 0; 1011 } 1012 1013 static int64_t 1014 sort__local_weight_cmp(struct hist_entry *left, struct hist_entry *right) 1015 { 1016 return he_weight(left) - he_weight(right); 1017 } 1018 1019 static int hist_entry__local_weight_snprintf(struct hist_entry *he, char *bf, 1020 size_t size, unsigned int width) 1021 { 1022 return repsep_snprintf(bf, size, "%-*llu", width, he_weight(he)); 1023 } 1024 1025 struct sort_entry sort_local_weight = { 1026 .se_header = "Local Weight", 1027 .se_cmp = sort__local_weight_cmp, 1028 .se_snprintf = hist_entry__local_weight_snprintf, 1029 .se_width_idx = HISTC_LOCAL_WEIGHT, 1030 }; 1031 1032 static int64_t 1033 sort__global_weight_cmp(struct hist_entry *left, struct hist_entry *right) 1034 { 1035 return left->stat.weight - right->stat.weight; 1036 } 1037 1038 static int hist_entry__global_weight_snprintf(struct hist_entry *he, char *bf, 1039 size_t size, unsigned int width) 1040 { 1041 return repsep_snprintf(bf, size, "%-*llu", width, he->stat.weight); 1042 } 1043 1044 struct sort_entry sort_global_weight = { 1045 .se_header = "Weight", 1046 .se_cmp = sort__global_weight_cmp, 1047 .se_snprintf = hist_entry__global_weight_snprintf, 1048 .se_width_idx = HISTC_GLOBAL_WEIGHT, 1049 }; 1050 1051 struct sort_entry sort_mem_daddr_sym = { 1052 .se_header = "Data Symbol", 1053 .se_cmp = sort__daddr_cmp, 1054 .se_snprintf = hist_entry__daddr_snprintf, 1055 .se_width_idx = HISTC_MEM_DADDR_SYMBOL, 1056 }; 1057 1058 struct sort_entry sort_mem_daddr_dso = { 1059 .se_header = "Data Object", 1060 .se_cmp = sort__dso_daddr_cmp, 1061 .se_snprintf = hist_entry__dso_daddr_snprintf, 1062 .se_width_idx = HISTC_MEM_DADDR_SYMBOL, 1063 }; 1064 1065 struct sort_entry sort_mem_locked = { 1066 .se_header = "Locked", 1067 .se_cmp = sort__locked_cmp, 1068 .se_snprintf = hist_entry__locked_snprintf, 1069 .se_width_idx = HISTC_MEM_LOCKED, 1070 }; 1071 1072 struct sort_entry sort_mem_tlb = { 1073 .se_header = "TLB access", 1074 .se_cmp = sort__tlb_cmp, 1075 .se_snprintf = hist_entry__tlb_snprintf, 1076 .se_width_idx = HISTC_MEM_TLB, 1077 }; 1078 1079 struct sort_entry sort_mem_lvl = { 1080 .se_header = "Memory access", 1081 .se_cmp = sort__lvl_cmp, 1082 .se_snprintf = hist_entry__lvl_snprintf, 1083 .se_width_idx = HISTC_MEM_LVL, 1084 }; 1085 1086 struct sort_entry sort_mem_snoop = { 1087 .se_header = "Snoop", 1088 .se_cmp = sort__snoop_cmp, 1089 .se_snprintf = hist_entry__snoop_snprintf, 1090 .se_width_idx = HISTC_MEM_SNOOP, 1091 }; 1092 1093 struct sort_entry sort_mem_dcacheline = { 1094 .se_header = "Data Cacheline", 1095 .se_cmp = sort__dcacheline_cmp, 1096 .se_snprintf = hist_entry__dcacheline_snprintf, 1097 .se_width_idx = HISTC_MEM_DCACHELINE, 1098 }; 1099 1100 static int64_t 1101 sort__abort_cmp(struct hist_entry *left, struct hist_entry *right) 1102 { 1103 if (!left->branch_info || !right->branch_info) 1104 return cmp_null(left->branch_info, right->branch_info); 1105 1106 return left->branch_info->flags.abort != 1107 right->branch_info->flags.abort; 1108 } 1109 1110 static int hist_entry__abort_snprintf(struct hist_entry *he, char *bf, 1111 size_t size, unsigned int width) 1112 { 1113 static const char *out = "N/A"; 1114 1115 if (he->branch_info) { 1116 if (he->branch_info->flags.abort) 1117 out = "A"; 1118 else 1119 out = "."; 1120 } 1121 1122 return repsep_snprintf(bf, size, "%-*s", width, out); 1123 } 1124 1125 struct sort_entry sort_abort = { 1126 .se_header = "Transaction abort", 1127 .se_cmp = sort__abort_cmp, 1128 .se_snprintf = hist_entry__abort_snprintf, 1129 .se_width_idx = HISTC_ABORT, 1130 }; 1131 1132 static int64_t 1133 sort__in_tx_cmp(struct hist_entry *left, struct hist_entry *right) 1134 { 1135 if (!left->branch_info || !right->branch_info) 1136 return cmp_null(left->branch_info, right->branch_info); 1137 1138 return left->branch_info->flags.in_tx != 1139 right->branch_info->flags.in_tx; 1140 } 1141 1142 static int hist_entry__in_tx_snprintf(struct hist_entry *he, char *bf, 1143 size_t size, unsigned int width) 1144 { 1145 static const char *out = "N/A"; 1146 1147 if (he->branch_info) { 1148 if (he->branch_info->flags.in_tx) 1149 out = "T"; 1150 else 1151 out = "."; 1152 } 1153 1154 return repsep_snprintf(bf, size, "%-*s", width, out); 1155 } 1156 1157 struct sort_entry sort_in_tx = { 1158 .se_header = "Branch in transaction", 1159 .se_cmp = sort__in_tx_cmp, 1160 .se_snprintf = hist_entry__in_tx_snprintf, 1161 .se_width_idx = HISTC_IN_TX, 1162 }; 1163 1164 static int64_t 1165 sort__transaction_cmp(struct hist_entry *left, struct hist_entry *right) 1166 { 1167 return left->transaction - right->transaction; 1168 } 1169 1170 static inline char *add_str(char *p, const char *str) 1171 { 1172 strcpy(p, str); 1173 return p + strlen(str); 1174 } 1175 1176 static struct txbit { 1177 unsigned flag; 1178 const char *name; 1179 int skip_for_len; 1180 } txbits[] = { 1181 { PERF_TXN_ELISION, "EL ", 0 }, 1182 { PERF_TXN_TRANSACTION, "TX ", 1 }, 1183 { PERF_TXN_SYNC, "SYNC ", 1 }, 1184 { PERF_TXN_ASYNC, "ASYNC ", 0 }, 1185 { PERF_TXN_RETRY, "RETRY ", 0 }, 1186 { PERF_TXN_CONFLICT, "CON ", 0 }, 1187 { PERF_TXN_CAPACITY_WRITE, "CAP-WRITE ", 1 }, 1188 { PERF_TXN_CAPACITY_READ, "CAP-READ ", 0 }, 1189 { 0, NULL, 0 } 1190 }; 1191 1192 int hist_entry__transaction_len(void) 1193 { 1194 int i; 1195 int len = 0; 1196 1197 for (i = 0; txbits[i].name; i++) { 1198 if (!txbits[i].skip_for_len) 1199 len += strlen(txbits[i].name); 1200 } 1201 len += 4; /* :XX<space> */ 1202 return len; 1203 } 1204 1205 static int hist_entry__transaction_snprintf(struct hist_entry *he, char *bf, 1206 size_t size, unsigned int width) 1207 { 1208 u64 t = he->transaction; 1209 char buf[128]; 1210 char *p = buf; 1211 int i; 1212 1213 buf[0] = 0; 1214 for (i = 0; txbits[i].name; i++) 1215 if (txbits[i].flag & t) 1216 p = add_str(p, txbits[i].name); 1217 if (t && !(t & (PERF_TXN_SYNC|PERF_TXN_ASYNC))) 1218 p = add_str(p, "NEITHER "); 1219 if (t & PERF_TXN_ABORT_MASK) { 1220 sprintf(p, ":%" PRIx64, 1221 (t & PERF_TXN_ABORT_MASK) >> 1222 PERF_TXN_ABORT_SHIFT); 1223 p += strlen(p); 1224 } 1225 1226 return repsep_snprintf(bf, size, "%-*s", width, buf); 1227 } 1228 1229 struct sort_entry sort_transaction = { 1230 .se_header = "Transaction ", 1231 .se_cmp = sort__transaction_cmp, 1232 .se_snprintf = hist_entry__transaction_snprintf, 1233 .se_width_idx = HISTC_TRANSACTION, 1234 }; 1235 1236 struct sort_dimension { 1237 const char *name; 1238 struct sort_entry *entry; 1239 int taken; 1240 }; 1241 1242 #define DIM(d, n, func) [d] = { .name = n, .entry = &(func) } 1243 1244 static struct sort_dimension common_sort_dimensions[] = { 1245 DIM(SORT_PID, "pid", sort_thread), 1246 DIM(SORT_COMM, "comm", sort_comm), 1247 DIM(SORT_DSO, "dso", sort_dso), 1248 DIM(SORT_SYM, "symbol", sort_sym), 1249 DIM(SORT_PARENT, "parent", sort_parent), 1250 DIM(SORT_CPU, "cpu", sort_cpu), 1251 DIM(SORT_SRCLINE, "srcline", sort_srcline), 1252 DIM(SORT_SRCFILE, "srcfile", sort_srcfile), 1253 DIM(SORT_LOCAL_WEIGHT, "local_weight", sort_local_weight), 1254 DIM(SORT_GLOBAL_WEIGHT, "weight", sort_global_weight), 1255 DIM(SORT_TRANSACTION, "transaction", sort_transaction), 1256 }; 1257 1258 #undef DIM 1259 1260 #define DIM(d, n, func) [d - __SORT_BRANCH_STACK] = { .name = n, .entry = &(func) } 1261 1262 static struct sort_dimension bstack_sort_dimensions[] = { 1263 DIM(SORT_DSO_FROM, "dso_from", sort_dso_from), 1264 DIM(SORT_DSO_TO, "dso_to", sort_dso_to), 1265 DIM(SORT_SYM_FROM, "symbol_from", sort_sym_from), 1266 DIM(SORT_SYM_TO, "symbol_to", sort_sym_to), 1267 DIM(SORT_MISPREDICT, "mispredict", sort_mispredict), 1268 DIM(SORT_IN_TX, "in_tx", sort_in_tx), 1269 DIM(SORT_ABORT, "abort", sort_abort), 1270 DIM(SORT_CYCLES, "cycles", sort_cycles), 1271 }; 1272 1273 #undef DIM 1274 1275 #define DIM(d, n, func) [d - __SORT_MEMORY_MODE] = { .name = n, .entry = &(func) } 1276 1277 static struct sort_dimension memory_sort_dimensions[] = { 1278 DIM(SORT_MEM_DADDR_SYMBOL, "symbol_daddr", sort_mem_daddr_sym), 1279 DIM(SORT_MEM_DADDR_DSO, "dso_daddr", sort_mem_daddr_dso), 1280 DIM(SORT_MEM_LOCKED, "locked", sort_mem_locked), 1281 DIM(SORT_MEM_TLB, "tlb", sort_mem_tlb), 1282 DIM(SORT_MEM_LVL, "mem", sort_mem_lvl), 1283 DIM(SORT_MEM_SNOOP, "snoop", sort_mem_snoop), 1284 DIM(SORT_MEM_DCACHELINE, "dcacheline", sort_mem_dcacheline), 1285 }; 1286 1287 #undef DIM 1288 1289 struct hpp_dimension { 1290 const char *name; 1291 struct perf_hpp_fmt *fmt; 1292 int taken; 1293 }; 1294 1295 #define DIM(d, n) { .name = n, .fmt = &perf_hpp__format[d], } 1296 1297 static struct hpp_dimension hpp_sort_dimensions[] = { 1298 DIM(PERF_HPP__OVERHEAD, "overhead"), 1299 DIM(PERF_HPP__OVERHEAD_SYS, "overhead_sys"), 1300 DIM(PERF_HPP__OVERHEAD_US, "overhead_us"), 1301 DIM(PERF_HPP__OVERHEAD_GUEST_SYS, "overhead_guest_sys"), 1302 DIM(PERF_HPP__OVERHEAD_GUEST_US, "overhead_guest_us"), 1303 DIM(PERF_HPP__OVERHEAD_ACC, "overhead_children"), 1304 DIM(PERF_HPP__SAMPLES, "sample"), 1305 DIM(PERF_HPP__PERIOD, "period"), 1306 }; 1307 1308 #undef DIM 1309 1310 struct hpp_sort_entry { 1311 struct perf_hpp_fmt hpp; 1312 struct sort_entry *se; 1313 }; 1314 1315 bool perf_hpp__same_sort_entry(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b) 1316 { 1317 struct hpp_sort_entry *hse_a; 1318 struct hpp_sort_entry *hse_b; 1319 1320 if (!perf_hpp__is_sort_entry(a) || !perf_hpp__is_sort_entry(b)) 1321 return false; 1322 1323 hse_a = container_of(a, struct hpp_sort_entry, hpp); 1324 hse_b = container_of(b, struct hpp_sort_entry, hpp); 1325 1326 return hse_a->se == hse_b->se; 1327 } 1328 1329 void perf_hpp__reset_sort_width(struct perf_hpp_fmt *fmt, struct hists *hists) 1330 { 1331 struct hpp_sort_entry *hse; 1332 1333 if (!perf_hpp__is_sort_entry(fmt)) 1334 return; 1335 1336 hse = container_of(fmt, struct hpp_sort_entry, hpp); 1337 hists__new_col_len(hists, hse->se->se_width_idx, strlen(fmt->name)); 1338 } 1339 1340 static int __sort__hpp_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, 1341 struct perf_evsel *evsel) 1342 { 1343 struct hpp_sort_entry *hse; 1344 size_t len = fmt->user_len; 1345 1346 hse = container_of(fmt, struct hpp_sort_entry, hpp); 1347 1348 if (!len) 1349 len = hists__col_len(evsel__hists(evsel), hse->se->se_width_idx); 1350 1351 return scnprintf(hpp->buf, hpp->size, "%-*.*s", len, len, fmt->name); 1352 } 1353 1354 static int __sort__hpp_width(struct perf_hpp_fmt *fmt, 1355 struct perf_hpp *hpp __maybe_unused, 1356 struct perf_evsel *evsel) 1357 { 1358 struct hpp_sort_entry *hse; 1359 size_t len = fmt->user_len; 1360 1361 hse = container_of(fmt, struct hpp_sort_entry, hpp); 1362 1363 if (!len) 1364 len = hists__col_len(evsel__hists(evsel), hse->se->se_width_idx); 1365 1366 return len; 1367 } 1368 1369 static int __sort__hpp_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, 1370 struct hist_entry *he) 1371 { 1372 struct hpp_sort_entry *hse; 1373 size_t len = fmt->user_len; 1374 1375 hse = container_of(fmt, struct hpp_sort_entry, hpp); 1376 1377 if (!len) 1378 len = hists__col_len(he->hists, hse->se->se_width_idx); 1379 1380 return hse->se->se_snprintf(he, hpp->buf, hpp->size, len); 1381 } 1382 1383 static int64_t __sort__hpp_cmp(struct perf_hpp_fmt *fmt, 1384 struct hist_entry *a, struct hist_entry *b) 1385 { 1386 struct hpp_sort_entry *hse; 1387 1388 hse = container_of(fmt, struct hpp_sort_entry, hpp); 1389 return hse->se->se_cmp(a, b); 1390 } 1391 1392 static int64_t __sort__hpp_collapse(struct perf_hpp_fmt *fmt, 1393 struct hist_entry *a, struct hist_entry *b) 1394 { 1395 struct hpp_sort_entry *hse; 1396 int64_t (*collapse_fn)(struct hist_entry *, struct hist_entry *); 1397 1398 hse = container_of(fmt, struct hpp_sort_entry, hpp); 1399 collapse_fn = hse->se->se_collapse ?: hse->se->se_cmp; 1400 return collapse_fn(a, b); 1401 } 1402 1403 static int64_t __sort__hpp_sort(struct perf_hpp_fmt *fmt, 1404 struct hist_entry *a, struct hist_entry *b) 1405 { 1406 struct hpp_sort_entry *hse; 1407 int64_t (*sort_fn)(struct hist_entry *, struct hist_entry *); 1408 1409 hse = container_of(fmt, struct hpp_sort_entry, hpp); 1410 sort_fn = hse->se->se_sort ?: hse->se->se_cmp; 1411 return sort_fn(a, b); 1412 } 1413 1414 static struct hpp_sort_entry * 1415 __sort_dimension__alloc_hpp(struct sort_dimension *sd) 1416 { 1417 struct hpp_sort_entry *hse; 1418 1419 hse = malloc(sizeof(*hse)); 1420 if (hse == NULL) { 1421 pr_err("Memory allocation failed\n"); 1422 return NULL; 1423 } 1424 1425 hse->se = sd->entry; 1426 hse->hpp.name = sd->entry->se_header; 1427 hse->hpp.header = __sort__hpp_header; 1428 hse->hpp.width = __sort__hpp_width; 1429 hse->hpp.entry = __sort__hpp_entry; 1430 hse->hpp.color = NULL; 1431 1432 hse->hpp.cmp = __sort__hpp_cmp; 1433 hse->hpp.collapse = __sort__hpp_collapse; 1434 hse->hpp.sort = __sort__hpp_sort; 1435 1436 INIT_LIST_HEAD(&hse->hpp.list); 1437 INIT_LIST_HEAD(&hse->hpp.sort_list); 1438 hse->hpp.elide = false; 1439 hse->hpp.len = 0; 1440 hse->hpp.user_len = 0; 1441 1442 return hse; 1443 } 1444 1445 bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format) 1446 { 1447 return format->header == __sort__hpp_header; 1448 } 1449 1450 static int __sort_dimension__add_hpp_sort(struct sort_dimension *sd) 1451 { 1452 struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd); 1453 1454 if (hse == NULL) 1455 return -1; 1456 1457 perf_hpp__register_sort_field(&hse->hpp); 1458 return 0; 1459 } 1460 1461 static int __sort_dimension__add_hpp_output(struct sort_dimension *sd) 1462 { 1463 struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd); 1464 1465 if (hse == NULL) 1466 return -1; 1467 1468 perf_hpp__column_register(&hse->hpp); 1469 return 0; 1470 } 1471 1472 static int __sort_dimension__add(struct sort_dimension *sd) 1473 { 1474 if (sd->taken) 1475 return 0; 1476 1477 if (__sort_dimension__add_hpp_sort(sd) < 0) 1478 return -1; 1479 1480 if (sd->entry->se_collapse) 1481 sort__need_collapse = 1; 1482 1483 sd->taken = 1; 1484 1485 return 0; 1486 } 1487 1488 static int __hpp_dimension__add(struct hpp_dimension *hd) 1489 { 1490 if (!hd->taken) { 1491 hd->taken = 1; 1492 1493 perf_hpp__register_sort_field(hd->fmt); 1494 } 1495 return 0; 1496 } 1497 1498 static int __sort_dimension__add_output(struct sort_dimension *sd) 1499 { 1500 if (sd->taken) 1501 return 0; 1502 1503 if (__sort_dimension__add_hpp_output(sd) < 0) 1504 return -1; 1505 1506 sd->taken = 1; 1507 return 0; 1508 } 1509 1510 static int __hpp_dimension__add_output(struct hpp_dimension *hd) 1511 { 1512 if (!hd->taken) { 1513 hd->taken = 1; 1514 1515 perf_hpp__column_register(hd->fmt); 1516 } 1517 return 0; 1518 } 1519 1520 int sort_dimension__add(const char *tok) 1521 { 1522 unsigned int i; 1523 1524 for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) { 1525 struct sort_dimension *sd = &common_sort_dimensions[i]; 1526 1527 if (strncasecmp(tok, sd->name, strlen(tok))) 1528 continue; 1529 1530 if (sd->entry == &sort_parent) { 1531 int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED); 1532 if (ret) { 1533 char err[BUFSIZ]; 1534 1535 regerror(ret, &parent_regex, err, sizeof(err)); 1536 pr_err("Invalid regex: %s\n%s", parent_pattern, err); 1537 return -EINVAL; 1538 } 1539 sort__has_parent = 1; 1540 } else if (sd->entry == &sort_sym) { 1541 sort__has_sym = 1; 1542 /* 1543 * perf diff displays the performance difference amongst 1544 * two or more perf.data files. Those files could come 1545 * from different binaries. So we should not compare 1546 * their ips, but the name of symbol. 1547 */ 1548 if (sort__mode == SORT_MODE__DIFF) 1549 sd->entry->se_collapse = sort__sym_sort; 1550 1551 } else if (sd->entry == &sort_dso) { 1552 sort__has_dso = 1; 1553 } 1554 1555 return __sort_dimension__add(sd); 1556 } 1557 1558 for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) { 1559 struct hpp_dimension *hd = &hpp_sort_dimensions[i]; 1560 1561 if (strncasecmp(tok, hd->name, strlen(tok))) 1562 continue; 1563 1564 return __hpp_dimension__add(hd); 1565 } 1566 1567 for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) { 1568 struct sort_dimension *sd = &bstack_sort_dimensions[i]; 1569 1570 if (strncasecmp(tok, sd->name, strlen(tok))) 1571 continue; 1572 1573 if (sort__mode != SORT_MODE__BRANCH) 1574 return -EINVAL; 1575 1576 if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to) 1577 sort__has_sym = 1; 1578 1579 __sort_dimension__add(sd); 1580 return 0; 1581 } 1582 1583 for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) { 1584 struct sort_dimension *sd = &memory_sort_dimensions[i]; 1585 1586 if (strncasecmp(tok, sd->name, strlen(tok))) 1587 continue; 1588 1589 if (sort__mode != SORT_MODE__MEMORY) 1590 return -EINVAL; 1591 1592 if (sd->entry == &sort_mem_daddr_sym) 1593 sort__has_sym = 1; 1594 1595 __sort_dimension__add(sd); 1596 return 0; 1597 } 1598 1599 return -ESRCH; 1600 } 1601 1602 static const char *get_default_sort_order(void) 1603 { 1604 const char *default_sort_orders[] = { 1605 default_sort_order, 1606 default_branch_sort_order, 1607 default_mem_sort_order, 1608 default_top_sort_order, 1609 default_diff_sort_order, 1610 }; 1611 1612 BUG_ON(sort__mode >= ARRAY_SIZE(default_sort_orders)); 1613 1614 return default_sort_orders[sort__mode]; 1615 } 1616 1617 static int setup_sort_order(void) 1618 { 1619 char *new_sort_order; 1620 1621 /* 1622 * Append '+'-prefixed sort order to the default sort 1623 * order string. 1624 */ 1625 if (!sort_order || is_strict_order(sort_order)) 1626 return 0; 1627 1628 if (sort_order[1] == '\0') { 1629 error("Invalid --sort key: `+'"); 1630 return -EINVAL; 1631 } 1632 1633 /* 1634 * We allocate new sort_order string, but we never free it, 1635 * because it's checked over the rest of the code. 1636 */ 1637 if (asprintf(&new_sort_order, "%s,%s", 1638 get_default_sort_order(), sort_order + 1) < 0) { 1639 error("Not enough memory to set up --sort"); 1640 return -ENOMEM; 1641 } 1642 1643 sort_order = new_sort_order; 1644 return 0; 1645 } 1646 1647 static int __setup_sorting(void) 1648 { 1649 char *tmp, *tok, *str; 1650 const char *sort_keys; 1651 int ret = 0; 1652 1653 ret = setup_sort_order(); 1654 if (ret) 1655 return ret; 1656 1657 sort_keys = sort_order; 1658 if (sort_keys == NULL) { 1659 if (is_strict_order(field_order)) { 1660 /* 1661 * If user specified field order but no sort order, 1662 * we'll honor it and not add default sort orders. 1663 */ 1664 return 0; 1665 } 1666 1667 sort_keys = get_default_sort_order(); 1668 } 1669 1670 str = strdup(sort_keys); 1671 if (str == NULL) { 1672 error("Not enough memory to setup sort keys"); 1673 return -ENOMEM; 1674 } 1675 1676 for (tok = strtok_r(str, ", ", &tmp); 1677 tok; tok = strtok_r(NULL, ", ", &tmp)) { 1678 ret = sort_dimension__add(tok); 1679 if (ret == -EINVAL) { 1680 error("Invalid --sort key: `%s'", tok); 1681 break; 1682 } else if (ret == -ESRCH) { 1683 error("Unknown --sort key: `%s'", tok); 1684 break; 1685 } 1686 } 1687 1688 free(str); 1689 return ret; 1690 } 1691 1692 void perf_hpp__set_elide(int idx, bool elide) 1693 { 1694 struct perf_hpp_fmt *fmt; 1695 struct hpp_sort_entry *hse; 1696 1697 perf_hpp__for_each_format(fmt) { 1698 if (!perf_hpp__is_sort_entry(fmt)) 1699 continue; 1700 1701 hse = container_of(fmt, struct hpp_sort_entry, hpp); 1702 if (hse->se->se_width_idx == idx) { 1703 fmt->elide = elide; 1704 break; 1705 } 1706 } 1707 } 1708 1709 static bool __get_elide(struct strlist *list, const char *list_name, FILE *fp) 1710 { 1711 if (list && strlist__nr_entries(list) == 1) { 1712 if (fp != NULL) 1713 fprintf(fp, "# %s: %s\n", list_name, 1714 strlist__entry(list, 0)->s); 1715 return true; 1716 } 1717 return false; 1718 } 1719 1720 static bool get_elide(int idx, FILE *output) 1721 { 1722 switch (idx) { 1723 case HISTC_SYMBOL: 1724 return __get_elide(symbol_conf.sym_list, "symbol", output); 1725 case HISTC_DSO: 1726 return __get_elide(symbol_conf.dso_list, "dso", output); 1727 case HISTC_COMM: 1728 return __get_elide(symbol_conf.comm_list, "comm", output); 1729 default: 1730 break; 1731 } 1732 1733 if (sort__mode != SORT_MODE__BRANCH) 1734 return false; 1735 1736 switch (idx) { 1737 case HISTC_SYMBOL_FROM: 1738 return __get_elide(symbol_conf.sym_from_list, "sym_from", output); 1739 case HISTC_SYMBOL_TO: 1740 return __get_elide(symbol_conf.sym_to_list, "sym_to", output); 1741 case HISTC_DSO_FROM: 1742 return __get_elide(symbol_conf.dso_from_list, "dso_from", output); 1743 case HISTC_DSO_TO: 1744 return __get_elide(symbol_conf.dso_to_list, "dso_to", output); 1745 default: 1746 break; 1747 } 1748 1749 return false; 1750 } 1751 1752 void sort__setup_elide(FILE *output) 1753 { 1754 struct perf_hpp_fmt *fmt; 1755 struct hpp_sort_entry *hse; 1756 1757 perf_hpp__for_each_format(fmt) { 1758 if (!perf_hpp__is_sort_entry(fmt)) 1759 continue; 1760 1761 hse = container_of(fmt, struct hpp_sort_entry, hpp); 1762 fmt->elide = get_elide(hse->se->se_width_idx, output); 1763 } 1764 1765 /* 1766 * It makes no sense to elide all of sort entries. 1767 * Just revert them to show up again. 1768 */ 1769 perf_hpp__for_each_format(fmt) { 1770 if (!perf_hpp__is_sort_entry(fmt)) 1771 continue; 1772 1773 if (!fmt->elide) 1774 return; 1775 } 1776 1777 perf_hpp__for_each_format(fmt) { 1778 if (!perf_hpp__is_sort_entry(fmt)) 1779 continue; 1780 1781 fmt->elide = false; 1782 } 1783 } 1784 1785 static int output_field_add(char *tok) 1786 { 1787 unsigned int i; 1788 1789 for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) { 1790 struct sort_dimension *sd = &common_sort_dimensions[i]; 1791 1792 if (strncasecmp(tok, sd->name, strlen(tok))) 1793 continue; 1794 1795 return __sort_dimension__add_output(sd); 1796 } 1797 1798 for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) { 1799 struct hpp_dimension *hd = &hpp_sort_dimensions[i]; 1800 1801 if (strncasecmp(tok, hd->name, strlen(tok))) 1802 continue; 1803 1804 return __hpp_dimension__add_output(hd); 1805 } 1806 1807 for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) { 1808 struct sort_dimension *sd = &bstack_sort_dimensions[i]; 1809 1810 if (strncasecmp(tok, sd->name, strlen(tok))) 1811 continue; 1812 1813 return __sort_dimension__add_output(sd); 1814 } 1815 1816 for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) { 1817 struct sort_dimension *sd = &memory_sort_dimensions[i]; 1818 1819 if (strncasecmp(tok, sd->name, strlen(tok))) 1820 continue; 1821 1822 return __sort_dimension__add_output(sd); 1823 } 1824 1825 return -ESRCH; 1826 } 1827 1828 static void reset_dimensions(void) 1829 { 1830 unsigned int i; 1831 1832 for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) 1833 common_sort_dimensions[i].taken = 0; 1834 1835 for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) 1836 hpp_sort_dimensions[i].taken = 0; 1837 1838 for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) 1839 bstack_sort_dimensions[i].taken = 0; 1840 1841 for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) 1842 memory_sort_dimensions[i].taken = 0; 1843 } 1844 1845 bool is_strict_order(const char *order) 1846 { 1847 return order && (*order != '+'); 1848 } 1849 1850 static int __setup_output_field(void) 1851 { 1852 char *tmp, *tok, *str, *strp; 1853 int ret = -EINVAL; 1854 1855 if (field_order == NULL) 1856 return 0; 1857 1858 reset_dimensions(); 1859 1860 strp = str = strdup(field_order); 1861 if (str == NULL) { 1862 error("Not enough memory to setup output fields"); 1863 return -ENOMEM; 1864 } 1865 1866 if (!is_strict_order(field_order)) 1867 strp++; 1868 1869 if (!strlen(strp)) { 1870 error("Invalid --fields key: `+'"); 1871 goto out; 1872 } 1873 1874 for (tok = strtok_r(strp, ", ", &tmp); 1875 tok; tok = strtok_r(NULL, ", ", &tmp)) { 1876 ret = output_field_add(tok); 1877 if (ret == -EINVAL) { 1878 error("Invalid --fields key: `%s'", tok); 1879 break; 1880 } else if (ret == -ESRCH) { 1881 error("Unknown --fields key: `%s'", tok); 1882 break; 1883 } 1884 } 1885 1886 out: 1887 free(str); 1888 return ret; 1889 } 1890 1891 int setup_sorting(void) 1892 { 1893 int err; 1894 1895 err = __setup_sorting(); 1896 if (err < 0) 1897 return err; 1898 1899 if (parent_pattern != default_parent_pattern) { 1900 err = sort_dimension__add("parent"); 1901 if (err < 0) 1902 return err; 1903 } 1904 1905 reset_dimensions(); 1906 1907 /* 1908 * perf diff doesn't use default hpp output fields. 1909 */ 1910 if (sort__mode != SORT_MODE__DIFF) 1911 perf_hpp__init(); 1912 1913 err = __setup_output_field(); 1914 if (err < 0) 1915 return err; 1916 1917 /* copy sort keys to output fields */ 1918 perf_hpp__setup_output_field(); 1919 /* and then copy output fields to sort keys */ 1920 perf_hpp__append_sort_keys(); 1921 1922 return 0; 1923 } 1924 1925 void reset_output_field(void) 1926 { 1927 sort__need_collapse = 0; 1928 sort__has_parent = 0; 1929 sort__has_sym = 0; 1930 sort__has_dso = 0; 1931 1932 field_order = NULL; 1933 sort_order = NULL; 1934 1935 reset_dimensions(); 1936 perf_hpp__reset_output_field(); 1937 } 1938