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