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