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