1 #include "sort.h" 2 #include "hist.h" 3 #include "comm.h" 4 #include "symbol.h" 5 6 regex_t parent_regex; 7 const char default_parent_pattern[] = "^sys_|^do_page_fault"; 8 const char *parent_pattern = default_parent_pattern; 9 const char default_sort_order[] = "comm,dso,symbol"; 10 const char *sort_order = default_sort_order; 11 regex_t ignore_callees_regex; 12 int have_ignore_callees = 0; 13 int sort__need_collapse = 0; 14 int sort__has_parent = 0; 15 int sort__has_sym = 0; 16 enum sort_mode sort__mode = SORT_MODE__NORMAL; 17 18 enum sort_type sort__first_dimension; 19 20 LIST_HEAD(hist_entry__sort_list); 21 22 static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...) 23 { 24 int n; 25 va_list ap; 26 27 va_start(ap, fmt); 28 n = vsnprintf(bf, size, fmt, ap); 29 if (symbol_conf.field_sep && n > 0) { 30 char *sep = bf; 31 32 while (1) { 33 sep = strchr(sep, *symbol_conf.field_sep); 34 if (sep == NULL) 35 break; 36 *sep = '.'; 37 } 38 } 39 va_end(ap); 40 41 if (n >= (int)size) 42 return size - 1; 43 return n; 44 } 45 46 static int64_t cmp_null(const void *l, const void *r) 47 { 48 if (!l && !r) 49 return 0; 50 else if (!l) 51 return -1; 52 else 53 return 1; 54 } 55 56 /* --sort pid */ 57 58 static int64_t 59 sort__thread_cmp(struct hist_entry *left, struct hist_entry *right) 60 { 61 return right->thread->tid - left->thread->tid; 62 } 63 64 static int hist_entry__thread_snprintf(struct hist_entry *he, char *bf, 65 size_t size, unsigned int width) 66 { 67 const char *comm = thread__comm_str(he->thread); 68 return repsep_snprintf(bf, size, "%*s:%5d", width - 6, 69 comm ?: "", he->thread->tid); 70 } 71 72 struct sort_entry sort_thread = { 73 .se_header = "Command: Pid", 74 .se_cmp = sort__thread_cmp, 75 .se_snprintf = hist_entry__thread_snprintf, 76 .se_width_idx = HISTC_THREAD, 77 }; 78 79 /* --sort comm */ 80 81 static int64_t 82 sort__comm_cmp(struct hist_entry *left, struct hist_entry *right) 83 { 84 /* Compare the addr that should be unique among comm */ 85 return comm__str(right->comm) - comm__str(left->comm); 86 } 87 88 static int64_t 89 sort__comm_collapse(struct hist_entry *left, struct hist_entry *right) 90 { 91 /* Compare the addr that should be unique among comm */ 92 return comm__str(right->comm) - comm__str(left->comm); 93 } 94 95 static int hist_entry__comm_snprintf(struct hist_entry *he, char *bf, 96 size_t size, unsigned int width) 97 { 98 return repsep_snprintf(bf, size, "%*s", width, comm__str(he->comm)); 99 } 100 101 struct sort_entry sort_comm = { 102 .se_header = "Command", 103 .se_cmp = sort__comm_cmp, 104 .se_collapse = sort__comm_collapse, 105 .se_snprintf = hist_entry__comm_snprintf, 106 .se_width_idx = HISTC_COMM, 107 }; 108 109 /* --sort dso */ 110 111 static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r) 112 { 113 struct dso *dso_l = map_l ? map_l->dso : NULL; 114 struct dso *dso_r = map_r ? map_r->dso : NULL; 115 const char *dso_name_l, *dso_name_r; 116 117 if (!dso_l || !dso_r) 118 return cmp_null(dso_l, dso_r); 119 120 if (verbose) { 121 dso_name_l = dso_l->long_name; 122 dso_name_r = dso_r->long_name; 123 } else { 124 dso_name_l = dso_l->short_name; 125 dso_name_r = dso_r->short_name; 126 } 127 128 return strcmp(dso_name_l, dso_name_r); 129 } 130 131 static int64_t 132 sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) 133 { 134 return _sort__dso_cmp(left->ms.map, right->ms.map); 135 } 136 137 static int _hist_entry__dso_snprintf(struct map *map, char *bf, 138 size_t size, unsigned int width) 139 { 140 if (map && map->dso) { 141 const char *dso_name = !verbose ? map->dso->short_name : 142 map->dso->long_name; 143 return repsep_snprintf(bf, size, "%-*s", width, dso_name); 144 } 145 146 return repsep_snprintf(bf, size, "%-*s", width, "[unknown]"); 147 } 148 149 static int hist_entry__dso_snprintf(struct hist_entry *he, char *bf, 150 size_t size, unsigned int width) 151 { 152 return _hist_entry__dso_snprintf(he->ms.map, bf, size, width); 153 } 154 155 struct sort_entry sort_dso = { 156 .se_header = "Shared Object", 157 .se_cmp = sort__dso_cmp, 158 .se_snprintf = hist_entry__dso_snprintf, 159 .se_width_idx = HISTC_DSO, 160 }; 161 162 /* --sort symbol */ 163 164 static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r) 165 { 166 u64 ip_l, ip_r; 167 168 if (!sym_l || !sym_r) 169 return cmp_null(sym_l, sym_r); 170 171 if (sym_l == sym_r) 172 return 0; 173 174 ip_l = sym_l->start; 175 ip_r = sym_r->start; 176 177 return (int64_t)(ip_r - ip_l); 178 } 179 180 static int64_t 181 sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) 182 { 183 int64_t ret; 184 185 if (!left->ms.sym && !right->ms.sym) 186 return right->level - left->level; 187 188 /* 189 * comparing symbol address alone is not enough since it's a 190 * relative address within a dso. 191 */ 192 ret = sort__dso_cmp(left, right); 193 if (ret != 0) 194 return ret; 195 196 return _sort__sym_cmp(left->ms.sym, right->ms.sym); 197 } 198 199 static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym, 200 u64 ip, char level, char *bf, size_t size, 201 unsigned int width) 202 { 203 size_t ret = 0; 204 205 if (verbose) { 206 char o = map ? dso__symtab_origin(map->dso) : '!'; 207 ret += repsep_snprintf(bf, size, "%-#*llx %c ", 208 BITS_PER_LONG / 4 + 2, ip, o); 209 } 210 211 ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level); 212 if (sym && map) { 213 if (map->type == MAP__VARIABLE) { 214 ret += repsep_snprintf(bf + ret, size - ret, "%s", sym->name); 215 ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx", 216 ip - map->unmap_ip(map, sym->start)); 217 ret += repsep_snprintf(bf + ret, size - ret, "%-*s", 218 width - ret, ""); 219 } else { 220 ret += repsep_snprintf(bf + ret, size - ret, "%-*s", 221 width - ret, 222 sym->name); 223 } 224 } else { 225 size_t len = BITS_PER_LONG / 4; 226 ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx", 227 len, ip); 228 ret += repsep_snprintf(bf + ret, size - ret, "%-*s", 229 width - ret, ""); 230 } 231 232 return ret; 233 } 234 235 static int hist_entry__sym_snprintf(struct hist_entry *he, char *bf, 236 size_t size, unsigned int width) 237 { 238 return _hist_entry__sym_snprintf(he->ms.map, he->ms.sym, he->ip, 239 he->level, bf, size, width); 240 } 241 242 struct sort_entry sort_sym = { 243 .se_header = "Symbol", 244 .se_cmp = sort__sym_cmp, 245 .se_snprintf = hist_entry__sym_snprintf, 246 .se_width_idx = HISTC_SYMBOL, 247 }; 248 249 /* --sort srcline */ 250 251 static int64_t 252 sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right) 253 { 254 if (!left->srcline) { 255 if (!left->ms.map) 256 left->srcline = SRCLINE_UNKNOWN; 257 else { 258 struct map *map = left->ms.map; 259 left->srcline = get_srcline(map->dso, 260 map__rip_2objdump(map, left->ip)); 261 } 262 } 263 if (!right->srcline) { 264 if (!right->ms.map) 265 right->srcline = SRCLINE_UNKNOWN; 266 else { 267 struct map *map = right->ms.map; 268 right->srcline = get_srcline(map->dso, 269 map__rip_2objdump(map, right->ip)); 270 } 271 } 272 return strcmp(left->srcline, right->srcline); 273 } 274 275 static int hist_entry__srcline_snprintf(struct hist_entry *he, char *bf, 276 size_t size, 277 unsigned int width __maybe_unused) 278 { 279 return repsep_snprintf(bf, size, "%s", he->srcline); 280 } 281 282 struct sort_entry sort_srcline = { 283 .se_header = "Source:Line", 284 .se_cmp = sort__srcline_cmp, 285 .se_snprintf = hist_entry__srcline_snprintf, 286 .se_width_idx = HISTC_SRCLINE, 287 }; 288 289 /* --sort parent */ 290 291 static int64_t 292 sort__parent_cmp(struct hist_entry *left, struct hist_entry *right) 293 { 294 struct symbol *sym_l = left->parent; 295 struct symbol *sym_r = right->parent; 296 297 if (!sym_l || !sym_r) 298 return cmp_null(sym_l, sym_r); 299 300 return strcmp(sym_l->name, sym_r->name); 301 } 302 303 static int hist_entry__parent_snprintf(struct hist_entry *he, char *bf, 304 size_t size, unsigned int width) 305 { 306 return repsep_snprintf(bf, size, "%-*s", width, 307 he->parent ? he->parent->name : "[other]"); 308 } 309 310 struct sort_entry sort_parent = { 311 .se_header = "Parent symbol", 312 .se_cmp = sort__parent_cmp, 313 .se_snprintf = hist_entry__parent_snprintf, 314 .se_width_idx = HISTC_PARENT, 315 }; 316 317 /* --sort cpu */ 318 319 static int64_t 320 sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right) 321 { 322 return right->cpu - left->cpu; 323 } 324 325 static int hist_entry__cpu_snprintf(struct hist_entry *he, char *bf, 326 size_t size, unsigned int width) 327 { 328 return repsep_snprintf(bf, size, "%*d", width, he->cpu); 329 } 330 331 struct sort_entry sort_cpu = { 332 .se_header = "CPU", 333 .se_cmp = sort__cpu_cmp, 334 .se_snprintf = hist_entry__cpu_snprintf, 335 .se_width_idx = HISTC_CPU, 336 }; 337 338 /* sort keys for branch stacks */ 339 340 static int64_t 341 sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right) 342 { 343 return _sort__dso_cmp(left->branch_info->from.map, 344 right->branch_info->from.map); 345 } 346 347 static int hist_entry__dso_from_snprintf(struct hist_entry *he, char *bf, 348 size_t size, unsigned int width) 349 { 350 return _hist_entry__dso_snprintf(he->branch_info->from.map, 351 bf, size, width); 352 } 353 354 static int64_t 355 sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right) 356 { 357 return _sort__dso_cmp(left->branch_info->to.map, 358 right->branch_info->to.map); 359 } 360 361 static int hist_entry__dso_to_snprintf(struct hist_entry *he, char *bf, 362 size_t size, unsigned int width) 363 { 364 return _hist_entry__dso_snprintf(he->branch_info->to.map, 365 bf, size, width); 366 } 367 368 static int64_t 369 sort__sym_from_cmp(struct hist_entry *left, struct hist_entry *right) 370 { 371 struct addr_map_symbol *from_l = &left->branch_info->from; 372 struct addr_map_symbol *from_r = &right->branch_info->from; 373 374 if (!from_l->sym && !from_r->sym) 375 return right->level - left->level; 376 377 return _sort__sym_cmp(from_l->sym, from_r->sym); 378 } 379 380 static int64_t 381 sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right) 382 { 383 struct addr_map_symbol *to_l = &left->branch_info->to; 384 struct addr_map_symbol *to_r = &right->branch_info->to; 385 386 if (!to_l->sym && !to_r->sym) 387 return right->level - left->level; 388 389 return _sort__sym_cmp(to_l->sym, to_r->sym); 390 } 391 392 static int hist_entry__sym_from_snprintf(struct hist_entry *he, char *bf, 393 size_t size, unsigned int width) 394 { 395 struct addr_map_symbol *from = &he->branch_info->from; 396 return _hist_entry__sym_snprintf(from->map, from->sym, from->addr, 397 he->level, bf, size, width); 398 399 } 400 401 static int hist_entry__sym_to_snprintf(struct hist_entry *he, char *bf, 402 size_t size, unsigned int width) 403 { 404 struct addr_map_symbol *to = &he->branch_info->to; 405 return _hist_entry__sym_snprintf(to->map, to->sym, to->addr, 406 he->level, bf, size, width); 407 408 } 409 410 struct sort_entry sort_dso_from = { 411 .se_header = "Source Shared Object", 412 .se_cmp = sort__dso_from_cmp, 413 .se_snprintf = hist_entry__dso_from_snprintf, 414 .se_width_idx = HISTC_DSO_FROM, 415 }; 416 417 struct sort_entry sort_dso_to = { 418 .se_header = "Target Shared Object", 419 .se_cmp = sort__dso_to_cmp, 420 .se_snprintf = hist_entry__dso_to_snprintf, 421 .se_width_idx = HISTC_DSO_TO, 422 }; 423 424 struct sort_entry sort_sym_from = { 425 .se_header = "Source Symbol", 426 .se_cmp = sort__sym_from_cmp, 427 .se_snprintf = hist_entry__sym_from_snprintf, 428 .se_width_idx = HISTC_SYMBOL_FROM, 429 }; 430 431 struct sort_entry sort_sym_to = { 432 .se_header = "Target Symbol", 433 .se_cmp = sort__sym_to_cmp, 434 .se_snprintf = hist_entry__sym_to_snprintf, 435 .se_width_idx = HISTC_SYMBOL_TO, 436 }; 437 438 static int64_t 439 sort__mispredict_cmp(struct hist_entry *left, struct hist_entry *right) 440 { 441 const unsigned char mp = left->branch_info->flags.mispred != 442 right->branch_info->flags.mispred; 443 const unsigned char p = left->branch_info->flags.predicted != 444 right->branch_info->flags.predicted; 445 446 return mp || p; 447 } 448 449 static int hist_entry__mispredict_snprintf(struct hist_entry *he, char *bf, 450 size_t size, unsigned int width){ 451 static const char *out = "N/A"; 452 453 if (he->branch_info->flags.predicted) 454 out = "N"; 455 else if (he->branch_info->flags.mispred) 456 out = "Y"; 457 458 return repsep_snprintf(bf, size, "%-*s", width, out); 459 } 460 461 /* --sort daddr_sym */ 462 static int64_t 463 sort__daddr_cmp(struct hist_entry *left, struct hist_entry *right) 464 { 465 uint64_t l = 0, r = 0; 466 467 if (left->mem_info) 468 l = left->mem_info->daddr.addr; 469 if (right->mem_info) 470 r = right->mem_info->daddr.addr; 471 472 return (int64_t)(r - l); 473 } 474 475 static int hist_entry__daddr_snprintf(struct hist_entry *he, char *bf, 476 size_t size, unsigned int width) 477 { 478 uint64_t addr = 0; 479 struct map *map = NULL; 480 struct symbol *sym = NULL; 481 482 if (he->mem_info) { 483 addr = he->mem_info->daddr.addr; 484 map = he->mem_info->daddr.map; 485 sym = he->mem_info->daddr.sym; 486 } 487 return _hist_entry__sym_snprintf(map, sym, addr, he->level, bf, size, 488 width); 489 } 490 491 static int64_t 492 sort__dso_daddr_cmp(struct hist_entry *left, struct hist_entry *right) 493 { 494 struct map *map_l = NULL; 495 struct map *map_r = NULL; 496 497 if (left->mem_info) 498 map_l = left->mem_info->daddr.map; 499 if (right->mem_info) 500 map_r = right->mem_info->daddr.map; 501 502 return _sort__dso_cmp(map_l, map_r); 503 } 504 505 static int hist_entry__dso_daddr_snprintf(struct hist_entry *he, char *bf, 506 size_t size, unsigned int width) 507 { 508 struct map *map = NULL; 509 510 if (he->mem_info) 511 map = he->mem_info->daddr.map; 512 513 return _hist_entry__dso_snprintf(map, bf, size, width); 514 } 515 516 static int64_t 517 sort__locked_cmp(struct hist_entry *left, struct hist_entry *right) 518 { 519 union perf_mem_data_src data_src_l; 520 union perf_mem_data_src data_src_r; 521 522 if (left->mem_info) 523 data_src_l = left->mem_info->data_src; 524 else 525 data_src_l.mem_lock = PERF_MEM_LOCK_NA; 526 527 if (right->mem_info) 528 data_src_r = right->mem_info->data_src; 529 else 530 data_src_r.mem_lock = PERF_MEM_LOCK_NA; 531 532 return (int64_t)(data_src_r.mem_lock - data_src_l.mem_lock); 533 } 534 535 static int hist_entry__locked_snprintf(struct hist_entry *he, char *bf, 536 size_t size, unsigned int width) 537 { 538 const char *out; 539 u64 mask = PERF_MEM_LOCK_NA; 540 541 if (he->mem_info) 542 mask = he->mem_info->data_src.mem_lock; 543 544 if (mask & PERF_MEM_LOCK_NA) 545 out = "N/A"; 546 else if (mask & PERF_MEM_LOCK_LOCKED) 547 out = "Yes"; 548 else 549 out = "No"; 550 551 return repsep_snprintf(bf, size, "%-*s", width, out); 552 } 553 554 static int64_t 555 sort__tlb_cmp(struct hist_entry *left, struct hist_entry *right) 556 { 557 union perf_mem_data_src data_src_l; 558 union perf_mem_data_src data_src_r; 559 560 if (left->mem_info) 561 data_src_l = left->mem_info->data_src; 562 else 563 data_src_l.mem_dtlb = PERF_MEM_TLB_NA; 564 565 if (right->mem_info) 566 data_src_r = right->mem_info->data_src; 567 else 568 data_src_r.mem_dtlb = PERF_MEM_TLB_NA; 569 570 return (int64_t)(data_src_r.mem_dtlb - data_src_l.mem_dtlb); 571 } 572 573 static const char * const tlb_access[] = { 574 "N/A", 575 "HIT", 576 "MISS", 577 "L1", 578 "L2", 579 "Walker", 580 "Fault", 581 }; 582 #define NUM_TLB_ACCESS (sizeof(tlb_access)/sizeof(const char *)) 583 584 static int hist_entry__tlb_snprintf(struct hist_entry *he, char *bf, 585 size_t size, unsigned int width) 586 { 587 char out[64]; 588 size_t sz = sizeof(out) - 1; /* -1 for null termination */ 589 size_t l = 0, i; 590 u64 m = PERF_MEM_TLB_NA; 591 u64 hit, miss; 592 593 out[0] = '\0'; 594 595 if (he->mem_info) 596 m = he->mem_info->data_src.mem_dtlb; 597 598 hit = m & PERF_MEM_TLB_HIT; 599 miss = m & PERF_MEM_TLB_MISS; 600 601 /* already taken care of */ 602 m &= ~(PERF_MEM_TLB_HIT|PERF_MEM_TLB_MISS); 603 604 for (i = 0; m && i < NUM_TLB_ACCESS; i++, m >>= 1) { 605 if (!(m & 0x1)) 606 continue; 607 if (l) { 608 strcat(out, " or "); 609 l += 4; 610 } 611 strncat(out, tlb_access[i], sz - l); 612 l += strlen(tlb_access[i]); 613 } 614 if (*out == '\0') 615 strcpy(out, "N/A"); 616 if (hit) 617 strncat(out, " hit", sz - l); 618 if (miss) 619 strncat(out, " miss", sz - l); 620 621 return repsep_snprintf(bf, size, "%-*s", width, out); 622 } 623 624 static int64_t 625 sort__lvl_cmp(struct hist_entry *left, struct hist_entry *right) 626 { 627 union perf_mem_data_src data_src_l; 628 union perf_mem_data_src data_src_r; 629 630 if (left->mem_info) 631 data_src_l = left->mem_info->data_src; 632 else 633 data_src_l.mem_lvl = PERF_MEM_LVL_NA; 634 635 if (right->mem_info) 636 data_src_r = right->mem_info->data_src; 637 else 638 data_src_r.mem_lvl = PERF_MEM_LVL_NA; 639 640 return (int64_t)(data_src_r.mem_lvl - data_src_l.mem_lvl); 641 } 642 643 static const char * const mem_lvl[] = { 644 "N/A", 645 "HIT", 646 "MISS", 647 "L1", 648 "LFB", 649 "L2", 650 "L3", 651 "Local RAM", 652 "Remote RAM (1 hop)", 653 "Remote RAM (2 hops)", 654 "Remote Cache (1 hop)", 655 "Remote Cache (2 hops)", 656 "I/O", 657 "Uncached", 658 }; 659 #define NUM_MEM_LVL (sizeof(mem_lvl)/sizeof(const char *)) 660 661 static int hist_entry__lvl_snprintf(struct hist_entry *he, char *bf, 662 size_t size, unsigned int width) 663 { 664 char out[64]; 665 size_t sz = sizeof(out) - 1; /* -1 for null termination */ 666 size_t i, l = 0; 667 u64 m = PERF_MEM_LVL_NA; 668 u64 hit, miss; 669 670 if (he->mem_info) 671 m = he->mem_info->data_src.mem_lvl; 672 673 out[0] = '\0'; 674 675 hit = m & PERF_MEM_LVL_HIT; 676 miss = m & PERF_MEM_LVL_MISS; 677 678 /* already taken care of */ 679 m &= ~(PERF_MEM_LVL_HIT|PERF_MEM_LVL_MISS); 680 681 for (i = 0; m && i < NUM_MEM_LVL; i++, m >>= 1) { 682 if (!(m & 0x1)) 683 continue; 684 if (l) { 685 strcat(out, " or "); 686 l += 4; 687 } 688 strncat(out, mem_lvl[i], sz - l); 689 l += strlen(mem_lvl[i]); 690 } 691 if (*out == '\0') 692 strcpy(out, "N/A"); 693 if (hit) 694 strncat(out, " hit", sz - l); 695 if (miss) 696 strncat(out, " miss", sz - l); 697 698 return repsep_snprintf(bf, size, "%-*s", width, out); 699 } 700 701 static int64_t 702 sort__snoop_cmp(struct hist_entry *left, struct hist_entry *right) 703 { 704 union perf_mem_data_src data_src_l; 705 union perf_mem_data_src data_src_r; 706 707 if (left->mem_info) 708 data_src_l = left->mem_info->data_src; 709 else 710 data_src_l.mem_snoop = PERF_MEM_SNOOP_NA; 711 712 if (right->mem_info) 713 data_src_r = right->mem_info->data_src; 714 else 715 data_src_r.mem_snoop = PERF_MEM_SNOOP_NA; 716 717 return (int64_t)(data_src_r.mem_snoop - data_src_l.mem_snoop); 718 } 719 720 static const char * const snoop_access[] = { 721 "N/A", 722 "None", 723 "Miss", 724 "Hit", 725 "HitM", 726 }; 727 #define NUM_SNOOP_ACCESS (sizeof(snoop_access)/sizeof(const char *)) 728 729 static int hist_entry__snoop_snprintf(struct hist_entry *he, char *bf, 730 size_t size, unsigned int width) 731 { 732 char out[64]; 733 size_t sz = sizeof(out) - 1; /* -1 for null termination */ 734 size_t i, l = 0; 735 u64 m = PERF_MEM_SNOOP_NA; 736 737 out[0] = '\0'; 738 739 if (he->mem_info) 740 m = he->mem_info->data_src.mem_snoop; 741 742 for (i = 0; m && i < NUM_SNOOP_ACCESS; i++, m >>= 1) { 743 if (!(m & 0x1)) 744 continue; 745 if (l) { 746 strcat(out, " or "); 747 l += 4; 748 } 749 strncat(out, snoop_access[i], sz - l); 750 l += strlen(snoop_access[i]); 751 } 752 753 if (*out == '\0') 754 strcpy(out, "N/A"); 755 756 return repsep_snprintf(bf, size, "%-*s", width, out); 757 } 758 759 struct sort_entry sort_mispredict = { 760 .se_header = "Branch Mispredicted", 761 .se_cmp = sort__mispredict_cmp, 762 .se_snprintf = hist_entry__mispredict_snprintf, 763 .se_width_idx = HISTC_MISPREDICT, 764 }; 765 766 static u64 he_weight(struct hist_entry *he) 767 { 768 return he->stat.nr_events ? he->stat.weight / he->stat.nr_events : 0; 769 } 770 771 static int64_t 772 sort__local_weight_cmp(struct hist_entry *left, struct hist_entry *right) 773 { 774 return he_weight(left) - he_weight(right); 775 } 776 777 static int hist_entry__local_weight_snprintf(struct hist_entry *he, char *bf, 778 size_t size, unsigned int width) 779 { 780 return repsep_snprintf(bf, size, "%-*llu", width, he_weight(he)); 781 } 782 783 struct sort_entry sort_local_weight = { 784 .se_header = "Local Weight", 785 .se_cmp = sort__local_weight_cmp, 786 .se_snprintf = hist_entry__local_weight_snprintf, 787 .se_width_idx = HISTC_LOCAL_WEIGHT, 788 }; 789 790 static int64_t 791 sort__global_weight_cmp(struct hist_entry *left, struct hist_entry *right) 792 { 793 return left->stat.weight - right->stat.weight; 794 } 795 796 static int hist_entry__global_weight_snprintf(struct hist_entry *he, char *bf, 797 size_t size, unsigned int width) 798 { 799 return repsep_snprintf(bf, size, "%-*llu", width, he->stat.weight); 800 } 801 802 struct sort_entry sort_global_weight = { 803 .se_header = "Weight", 804 .se_cmp = sort__global_weight_cmp, 805 .se_snprintf = hist_entry__global_weight_snprintf, 806 .se_width_idx = HISTC_GLOBAL_WEIGHT, 807 }; 808 809 struct sort_entry sort_mem_daddr_sym = { 810 .se_header = "Data Symbol", 811 .se_cmp = sort__daddr_cmp, 812 .se_snprintf = hist_entry__daddr_snprintf, 813 .se_width_idx = HISTC_MEM_DADDR_SYMBOL, 814 }; 815 816 struct sort_entry sort_mem_daddr_dso = { 817 .se_header = "Data Object", 818 .se_cmp = sort__dso_daddr_cmp, 819 .se_snprintf = hist_entry__dso_daddr_snprintf, 820 .se_width_idx = HISTC_MEM_DADDR_SYMBOL, 821 }; 822 823 struct sort_entry sort_mem_locked = { 824 .se_header = "Locked", 825 .se_cmp = sort__locked_cmp, 826 .se_snprintf = hist_entry__locked_snprintf, 827 .se_width_idx = HISTC_MEM_LOCKED, 828 }; 829 830 struct sort_entry sort_mem_tlb = { 831 .se_header = "TLB access", 832 .se_cmp = sort__tlb_cmp, 833 .se_snprintf = hist_entry__tlb_snprintf, 834 .se_width_idx = HISTC_MEM_TLB, 835 }; 836 837 struct sort_entry sort_mem_lvl = { 838 .se_header = "Memory access", 839 .se_cmp = sort__lvl_cmp, 840 .se_snprintf = hist_entry__lvl_snprintf, 841 .se_width_idx = HISTC_MEM_LVL, 842 }; 843 844 struct sort_entry sort_mem_snoop = { 845 .se_header = "Snoop", 846 .se_cmp = sort__snoop_cmp, 847 .se_snprintf = hist_entry__snoop_snprintf, 848 .se_width_idx = HISTC_MEM_SNOOP, 849 }; 850 851 static int64_t 852 sort__abort_cmp(struct hist_entry *left, struct hist_entry *right) 853 { 854 return left->branch_info->flags.abort != 855 right->branch_info->flags.abort; 856 } 857 858 static int hist_entry__abort_snprintf(struct hist_entry *he, char *bf, 859 size_t size, unsigned int width) 860 { 861 static const char *out = "."; 862 863 if (he->branch_info->flags.abort) 864 out = "A"; 865 return repsep_snprintf(bf, size, "%-*s", width, out); 866 } 867 868 struct sort_entry sort_abort = { 869 .se_header = "Transaction abort", 870 .se_cmp = sort__abort_cmp, 871 .se_snprintf = hist_entry__abort_snprintf, 872 .se_width_idx = HISTC_ABORT, 873 }; 874 875 static int64_t 876 sort__in_tx_cmp(struct hist_entry *left, struct hist_entry *right) 877 { 878 return left->branch_info->flags.in_tx != 879 right->branch_info->flags.in_tx; 880 } 881 882 static int hist_entry__in_tx_snprintf(struct hist_entry *he, char *bf, 883 size_t size, unsigned int width) 884 { 885 static const char *out = "."; 886 887 if (he->branch_info->flags.in_tx) 888 out = "T"; 889 890 return repsep_snprintf(bf, size, "%-*s", width, out); 891 } 892 893 struct sort_entry sort_in_tx = { 894 .se_header = "Branch in transaction", 895 .se_cmp = sort__in_tx_cmp, 896 .se_snprintf = hist_entry__in_tx_snprintf, 897 .se_width_idx = HISTC_IN_TX, 898 }; 899 900 static int64_t 901 sort__transaction_cmp(struct hist_entry *left, struct hist_entry *right) 902 { 903 return left->transaction - right->transaction; 904 } 905 906 static inline char *add_str(char *p, const char *str) 907 { 908 strcpy(p, str); 909 return p + strlen(str); 910 } 911 912 static struct txbit { 913 unsigned flag; 914 const char *name; 915 int skip_for_len; 916 } txbits[] = { 917 { PERF_TXN_ELISION, "EL ", 0 }, 918 { PERF_TXN_TRANSACTION, "TX ", 1 }, 919 { PERF_TXN_SYNC, "SYNC ", 1 }, 920 { PERF_TXN_ASYNC, "ASYNC ", 0 }, 921 { PERF_TXN_RETRY, "RETRY ", 0 }, 922 { PERF_TXN_CONFLICT, "CON ", 0 }, 923 { PERF_TXN_CAPACITY_WRITE, "CAP-WRITE ", 1 }, 924 { PERF_TXN_CAPACITY_READ, "CAP-READ ", 0 }, 925 { 0, NULL, 0 } 926 }; 927 928 int hist_entry__transaction_len(void) 929 { 930 int i; 931 int len = 0; 932 933 for (i = 0; txbits[i].name; i++) { 934 if (!txbits[i].skip_for_len) 935 len += strlen(txbits[i].name); 936 } 937 len += 4; /* :XX<space> */ 938 return len; 939 } 940 941 static int hist_entry__transaction_snprintf(struct hist_entry *he, char *bf, 942 size_t size, unsigned int width) 943 { 944 u64 t = he->transaction; 945 char buf[128]; 946 char *p = buf; 947 int i; 948 949 buf[0] = 0; 950 for (i = 0; txbits[i].name; i++) 951 if (txbits[i].flag & t) 952 p = add_str(p, txbits[i].name); 953 if (t && !(t & (PERF_TXN_SYNC|PERF_TXN_ASYNC))) 954 p = add_str(p, "NEITHER "); 955 if (t & PERF_TXN_ABORT_MASK) { 956 sprintf(p, ":%" PRIx64, 957 (t & PERF_TXN_ABORT_MASK) >> 958 PERF_TXN_ABORT_SHIFT); 959 p += strlen(p); 960 } 961 962 return repsep_snprintf(bf, size, "%-*s", width, buf); 963 } 964 965 struct sort_entry sort_transaction = { 966 .se_header = "Transaction ", 967 .se_cmp = sort__transaction_cmp, 968 .se_snprintf = hist_entry__transaction_snprintf, 969 .se_width_idx = HISTC_TRANSACTION, 970 }; 971 972 struct sort_dimension { 973 const char *name; 974 struct sort_entry *entry; 975 int taken; 976 }; 977 978 #define DIM(d, n, func) [d] = { .name = n, .entry = &(func) } 979 980 static struct sort_dimension common_sort_dimensions[] = { 981 DIM(SORT_PID, "pid", sort_thread), 982 DIM(SORT_COMM, "comm", sort_comm), 983 DIM(SORT_DSO, "dso", sort_dso), 984 DIM(SORT_SYM, "symbol", sort_sym), 985 DIM(SORT_PARENT, "parent", sort_parent), 986 DIM(SORT_CPU, "cpu", sort_cpu), 987 DIM(SORT_SRCLINE, "srcline", sort_srcline), 988 DIM(SORT_LOCAL_WEIGHT, "local_weight", sort_local_weight), 989 DIM(SORT_GLOBAL_WEIGHT, "weight", sort_global_weight), 990 DIM(SORT_TRANSACTION, "transaction", sort_transaction), 991 }; 992 993 #undef DIM 994 995 #define DIM(d, n, func) [d - __SORT_BRANCH_STACK] = { .name = n, .entry = &(func) } 996 997 static struct sort_dimension bstack_sort_dimensions[] = { 998 DIM(SORT_DSO_FROM, "dso_from", sort_dso_from), 999 DIM(SORT_DSO_TO, "dso_to", sort_dso_to), 1000 DIM(SORT_SYM_FROM, "symbol_from", sort_sym_from), 1001 DIM(SORT_SYM_TO, "symbol_to", sort_sym_to), 1002 DIM(SORT_MISPREDICT, "mispredict", sort_mispredict), 1003 DIM(SORT_IN_TX, "in_tx", sort_in_tx), 1004 DIM(SORT_ABORT, "abort", sort_abort), 1005 }; 1006 1007 #undef DIM 1008 1009 #define DIM(d, n, func) [d - __SORT_MEMORY_MODE] = { .name = n, .entry = &(func) } 1010 1011 static struct sort_dimension memory_sort_dimensions[] = { 1012 DIM(SORT_MEM_DADDR_SYMBOL, "symbol_daddr", sort_mem_daddr_sym), 1013 DIM(SORT_MEM_DADDR_DSO, "dso_daddr", sort_mem_daddr_dso), 1014 DIM(SORT_MEM_LOCKED, "locked", sort_mem_locked), 1015 DIM(SORT_MEM_TLB, "tlb", sort_mem_tlb), 1016 DIM(SORT_MEM_LVL, "mem", sort_mem_lvl), 1017 DIM(SORT_MEM_SNOOP, "snoop", sort_mem_snoop), 1018 }; 1019 1020 #undef DIM 1021 1022 static void __sort_dimension__add(struct sort_dimension *sd, enum sort_type idx) 1023 { 1024 if (sd->taken) 1025 return; 1026 1027 if (sd->entry->se_collapse) 1028 sort__need_collapse = 1; 1029 1030 if (list_empty(&hist_entry__sort_list)) 1031 sort__first_dimension = idx; 1032 1033 list_add_tail(&sd->entry->list, &hist_entry__sort_list); 1034 sd->taken = 1; 1035 } 1036 1037 int sort_dimension__add(const char *tok) 1038 { 1039 unsigned int i; 1040 1041 for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) { 1042 struct sort_dimension *sd = &common_sort_dimensions[i]; 1043 1044 if (strncasecmp(tok, sd->name, strlen(tok))) 1045 continue; 1046 1047 if (sd->entry == &sort_parent) { 1048 int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED); 1049 if (ret) { 1050 char err[BUFSIZ]; 1051 1052 regerror(ret, &parent_regex, err, sizeof(err)); 1053 pr_err("Invalid regex: %s\n%s", parent_pattern, err); 1054 return -EINVAL; 1055 } 1056 sort__has_parent = 1; 1057 } else if (sd->entry == &sort_sym) { 1058 sort__has_sym = 1; 1059 } 1060 1061 __sort_dimension__add(sd, i); 1062 return 0; 1063 } 1064 1065 for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) { 1066 struct sort_dimension *sd = &bstack_sort_dimensions[i]; 1067 1068 if (strncasecmp(tok, sd->name, strlen(tok))) 1069 continue; 1070 1071 if (sort__mode != SORT_MODE__BRANCH) 1072 return -EINVAL; 1073 1074 if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to) 1075 sort__has_sym = 1; 1076 1077 __sort_dimension__add(sd, i + __SORT_BRANCH_STACK); 1078 return 0; 1079 } 1080 1081 for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) { 1082 struct sort_dimension *sd = &memory_sort_dimensions[i]; 1083 1084 if (strncasecmp(tok, sd->name, strlen(tok))) 1085 continue; 1086 1087 if (sort__mode != SORT_MODE__MEMORY) 1088 return -EINVAL; 1089 1090 if (sd->entry == &sort_mem_daddr_sym) 1091 sort__has_sym = 1; 1092 1093 __sort_dimension__add(sd, i + __SORT_MEMORY_MODE); 1094 return 0; 1095 } 1096 1097 return -ESRCH; 1098 } 1099 1100 int setup_sorting(void) 1101 { 1102 char *tmp, *tok, *str = strdup(sort_order); 1103 int ret = 0; 1104 1105 if (str == NULL) { 1106 error("Not enough memory to setup sort keys"); 1107 return -ENOMEM; 1108 } 1109 1110 for (tok = strtok_r(str, ", ", &tmp); 1111 tok; tok = strtok_r(NULL, ", ", &tmp)) { 1112 ret = sort_dimension__add(tok); 1113 if (ret == -EINVAL) { 1114 error("Invalid --sort key: `%s'", tok); 1115 break; 1116 } else if (ret == -ESRCH) { 1117 error("Unknown --sort key: `%s'", tok); 1118 break; 1119 } 1120 } 1121 1122 free(str); 1123 return ret; 1124 } 1125 1126 static void sort_entry__setup_elide(struct sort_entry *se, 1127 struct strlist *list, 1128 const char *list_name, FILE *fp) 1129 { 1130 if (list && strlist__nr_entries(list) == 1) { 1131 if (fp != NULL) 1132 fprintf(fp, "# %s: %s\n", list_name, 1133 strlist__entry(list, 0)->s); 1134 se->elide = true; 1135 } 1136 } 1137 1138 void sort__setup_elide(FILE *output) 1139 { 1140 struct sort_entry *se; 1141 1142 sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, 1143 "dso", output); 1144 sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, 1145 "comm", output); 1146 sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, 1147 "symbol", output); 1148 1149 if (sort__mode == SORT_MODE__BRANCH) { 1150 sort_entry__setup_elide(&sort_dso_from, 1151 symbol_conf.dso_from_list, 1152 "dso_from", output); 1153 sort_entry__setup_elide(&sort_dso_to, 1154 symbol_conf.dso_to_list, 1155 "dso_to", output); 1156 sort_entry__setup_elide(&sort_sym_from, 1157 symbol_conf.sym_from_list, 1158 "sym_from", output); 1159 sort_entry__setup_elide(&sort_sym_to, 1160 symbol_conf.sym_to_list, 1161 "sym_to", output); 1162 } else if (sort__mode == SORT_MODE__MEMORY) { 1163 sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, 1164 "symbol_daddr", output); 1165 sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, 1166 "dso_daddr", output); 1167 sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, 1168 "mem", output); 1169 sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, 1170 "local_weight", output); 1171 sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, 1172 "tlb", output); 1173 sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, 1174 "snoop", output); 1175 } 1176 1177 /* 1178 * It makes no sense to elide all of sort entries. 1179 * Just revert them to show up again. 1180 */ 1181 list_for_each_entry(se, &hist_entry__sort_list, list) { 1182 if (!se->elide) 1183 return; 1184 } 1185 1186 list_for_each_entry(se, &hist_entry__sort_list, list) 1187 se->elide = false; 1188 } 1189