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