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