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