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) 202 ret += repsep_snprintf(bf + ret, size - ret, "%-*s", 203 width - ret, 204 sym->name); 205 else { 206 size_t len = BITS_PER_LONG / 4; 207 ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx", 208 len, ip); 209 ret += repsep_snprintf(bf + ret, size - ret, "%-*s", 210 width - ret, ""); 211 } 212 213 return ret; 214 } 215 216 static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, 217 size_t size, unsigned int width) 218 { 219 return _hist_entry__sym_snprintf(self->ms.map, self->ms.sym, self->ip, 220 self->level, bf, size, width); 221 } 222 223 struct sort_entry sort_sym = { 224 .se_header = "Symbol", 225 .se_cmp = sort__sym_cmp, 226 .se_snprintf = hist_entry__sym_snprintf, 227 .se_width_idx = HISTC_SYMBOL, 228 }; 229 230 /* --sort srcline */ 231 232 static int64_t 233 sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right) 234 { 235 return (int64_t)(right->ip - left->ip); 236 } 237 238 static int hist_entry__srcline_snprintf(struct hist_entry *self, char *bf, 239 size_t size, 240 unsigned int width __maybe_unused) 241 { 242 FILE *fp = NULL; 243 char cmd[PATH_MAX + 2], *path = self->srcline, *nl; 244 size_t line_len; 245 246 if (path != NULL) 247 goto out_path; 248 249 if (!self->ms.map) 250 goto out_ip; 251 252 if (!strncmp(self->ms.map->dso->long_name, "/tmp/perf-", 10)) 253 goto out_ip; 254 255 snprintf(cmd, sizeof(cmd), "addr2line -e %s %016" PRIx64, 256 self->ms.map->dso->long_name, self->ip); 257 fp = popen(cmd, "r"); 258 if (!fp) 259 goto out_ip; 260 261 if (getline(&path, &line_len, fp) < 0 || !line_len) 262 goto out_ip; 263 self->srcline = strdup(path); 264 if (self->srcline == NULL) 265 goto out_ip; 266 267 nl = strchr(self->srcline, '\n'); 268 if (nl != NULL) 269 *nl = '\0'; 270 path = self->srcline; 271 out_path: 272 if (fp) 273 pclose(fp); 274 return repsep_snprintf(bf, size, "%s", path); 275 out_ip: 276 if (fp) 277 pclose(fp); 278 return repsep_snprintf(bf, size, "%-#*llx", BITS_PER_LONG / 4, self->ip); 279 } 280 281 struct sort_entry sort_srcline = { 282 .se_header = "Source:Line", 283 .se_cmp = sort__srcline_cmp, 284 .se_snprintf = hist_entry__srcline_snprintf, 285 .se_width_idx = HISTC_SRCLINE, 286 }; 287 288 /* --sort parent */ 289 290 static int64_t 291 sort__parent_cmp(struct hist_entry *left, struct hist_entry *right) 292 { 293 struct symbol *sym_l = left->parent; 294 struct symbol *sym_r = right->parent; 295 296 if (!sym_l || !sym_r) 297 return cmp_null(sym_l, sym_r); 298 299 return strcmp(sym_l->name, sym_r->name); 300 } 301 302 static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf, 303 size_t size, unsigned int width) 304 { 305 return repsep_snprintf(bf, size, "%-*s", width, 306 self->parent ? self->parent->name : "[other]"); 307 } 308 309 struct sort_entry sort_parent = { 310 .se_header = "Parent symbol", 311 .se_cmp = sort__parent_cmp, 312 .se_snprintf = hist_entry__parent_snprintf, 313 .se_width_idx = HISTC_PARENT, 314 }; 315 316 /* --sort cpu */ 317 318 static int64_t 319 sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right) 320 { 321 return right->cpu - left->cpu; 322 } 323 324 static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf, 325 size_t size, unsigned int width) 326 { 327 return repsep_snprintf(bf, size, "%*d", width, self->cpu); 328 } 329 330 struct sort_entry sort_cpu = { 331 .se_header = "CPU", 332 .se_cmp = sort__cpu_cmp, 333 .se_snprintf = hist_entry__cpu_snprintf, 334 .se_width_idx = HISTC_CPU, 335 }; 336 337 /* sort keys for branch stacks */ 338 339 static int64_t 340 sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right) 341 { 342 return _sort__dso_cmp(left->branch_info->from.map, 343 right->branch_info->from.map); 344 } 345 346 static int hist_entry__dso_from_snprintf(struct hist_entry *self, char *bf, 347 size_t size, unsigned int width) 348 { 349 return _hist_entry__dso_snprintf(self->branch_info->from.map, 350 bf, size, width); 351 } 352 353 static int64_t 354 sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right) 355 { 356 return _sort__dso_cmp(left->branch_info->to.map, 357 right->branch_info->to.map); 358 } 359 360 static int hist_entry__dso_to_snprintf(struct hist_entry *self, char *bf, 361 size_t size, unsigned int width) 362 { 363 return _hist_entry__dso_snprintf(self->branch_info->to.map, 364 bf, size, width); 365 } 366 367 static int64_t 368 sort__sym_from_cmp(struct hist_entry *left, struct hist_entry *right) 369 { 370 struct addr_map_symbol *from_l = &left->branch_info->from; 371 struct addr_map_symbol *from_r = &right->branch_info->from; 372 373 if (!from_l->sym && !from_r->sym) 374 return right->level - left->level; 375 376 return _sort__sym_cmp(from_l->sym, from_r->sym); 377 } 378 379 static int64_t 380 sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right) 381 { 382 struct addr_map_symbol *to_l = &left->branch_info->to; 383 struct addr_map_symbol *to_r = &right->branch_info->to; 384 385 if (!to_l->sym && !to_r->sym) 386 return right->level - left->level; 387 388 return _sort__sym_cmp(to_l->sym, to_r->sym); 389 } 390 391 static int hist_entry__sym_from_snprintf(struct hist_entry *self, char *bf, 392 size_t size, unsigned int width) 393 { 394 struct addr_map_symbol *from = &self->branch_info->from; 395 return _hist_entry__sym_snprintf(from->map, from->sym, from->addr, 396 self->level, bf, size, width); 397 398 } 399 400 static int hist_entry__sym_to_snprintf(struct hist_entry *self, char *bf, 401 size_t size, unsigned int width) 402 { 403 struct addr_map_symbol *to = &self->branch_info->to; 404 return _hist_entry__sym_snprintf(to->map, to->sym, to->addr, 405 self->level, bf, size, width); 406 407 } 408 409 struct sort_entry sort_dso_from = { 410 .se_header = "Source Shared Object", 411 .se_cmp = sort__dso_from_cmp, 412 .se_snprintf = hist_entry__dso_from_snprintf, 413 .se_width_idx = HISTC_DSO_FROM, 414 }; 415 416 struct sort_entry sort_dso_to = { 417 .se_header = "Target Shared Object", 418 .se_cmp = sort__dso_to_cmp, 419 .se_snprintf = hist_entry__dso_to_snprintf, 420 .se_width_idx = HISTC_DSO_TO, 421 }; 422 423 struct sort_entry sort_sym_from = { 424 .se_header = "Source Symbol", 425 .se_cmp = sort__sym_from_cmp, 426 .se_snprintf = hist_entry__sym_from_snprintf, 427 .se_width_idx = HISTC_SYMBOL_FROM, 428 }; 429 430 struct sort_entry sort_sym_to = { 431 .se_header = "Target Symbol", 432 .se_cmp = sort__sym_to_cmp, 433 .se_snprintf = hist_entry__sym_to_snprintf, 434 .se_width_idx = HISTC_SYMBOL_TO, 435 }; 436 437 static int64_t 438 sort__mispredict_cmp(struct hist_entry *left, struct hist_entry *right) 439 { 440 const unsigned char mp = left->branch_info->flags.mispred != 441 right->branch_info->flags.mispred; 442 const unsigned char p = left->branch_info->flags.predicted != 443 right->branch_info->flags.predicted; 444 445 return mp || p; 446 } 447 448 static int hist_entry__mispredict_snprintf(struct hist_entry *self, char *bf, 449 size_t size, unsigned int width){ 450 static const char *out = "N/A"; 451 452 if (self->branch_info->flags.predicted) 453 out = "N"; 454 else if (self->branch_info->flags.mispred) 455 out = "Y"; 456 457 return repsep_snprintf(bf, size, "%-*s", width, out); 458 } 459 460 struct sort_entry sort_mispredict = { 461 .se_header = "Branch Mispredicted", 462 .se_cmp = sort__mispredict_cmp, 463 .se_snprintf = hist_entry__mispredict_snprintf, 464 .se_width_idx = HISTC_MISPREDICT, 465 }; 466 467 struct sort_dimension { 468 const char *name; 469 struct sort_entry *entry; 470 int taken; 471 }; 472 473 #define DIM(d, n, func) [d] = { .name = n, .entry = &(func) } 474 475 static struct sort_dimension common_sort_dimensions[] = { 476 DIM(SORT_PID, "pid", sort_thread), 477 DIM(SORT_COMM, "comm", sort_comm), 478 DIM(SORT_DSO, "dso", sort_dso), 479 DIM(SORT_SYM, "symbol", sort_sym), 480 DIM(SORT_PARENT, "parent", sort_parent), 481 DIM(SORT_CPU, "cpu", sort_cpu), 482 DIM(SORT_SRCLINE, "srcline", sort_srcline), 483 }; 484 485 #undef DIM 486 487 #define DIM(d, n, func) [d - __SORT_BRANCH_STACK] = { .name = n, .entry = &(func) } 488 489 static struct sort_dimension bstack_sort_dimensions[] = { 490 DIM(SORT_DSO_FROM, "dso_from", sort_dso_from), 491 DIM(SORT_DSO_TO, "dso_to", sort_dso_to), 492 DIM(SORT_SYM_FROM, "symbol_from", sort_sym_from), 493 DIM(SORT_SYM_TO, "symbol_to", sort_sym_to), 494 DIM(SORT_MISPREDICT, "mispredict", sort_mispredict), 495 }; 496 497 #undef DIM 498 499 int sort_dimension__add(const char *tok) 500 { 501 unsigned int i; 502 503 for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) { 504 struct sort_dimension *sd = &common_sort_dimensions[i]; 505 506 if (strncasecmp(tok, sd->name, strlen(tok))) 507 continue; 508 509 if (sd->entry == &sort_parent) { 510 int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED); 511 if (ret) { 512 char err[BUFSIZ]; 513 514 regerror(ret, &parent_regex, err, sizeof(err)); 515 pr_err("Invalid regex: %s\n%s", parent_pattern, err); 516 return -EINVAL; 517 } 518 sort__has_parent = 1; 519 } else if (sd->entry == &sort_sym) { 520 sort__has_sym = 1; 521 } 522 523 if (sd->taken) 524 return 0; 525 526 if (sd->entry->se_collapse) 527 sort__need_collapse = 1; 528 529 if (list_empty(&hist_entry__sort_list)) 530 sort__first_dimension = i; 531 532 list_add_tail(&sd->entry->list, &hist_entry__sort_list); 533 sd->taken = 1; 534 535 return 0; 536 } 537 538 for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) { 539 struct sort_dimension *sd = &bstack_sort_dimensions[i]; 540 541 if (strncasecmp(tok, sd->name, strlen(tok))) 542 continue; 543 544 if (sort__branch_mode != 1) 545 return -EINVAL; 546 547 if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to) 548 sort__has_sym = 1; 549 550 if (sd->taken) 551 return 0; 552 553 if (sd->entry->se_collapse) 554 sort__need_collapse = 1; 555 556 if (list_empty(&hist_entry__sort_list)) 557 sort__first_dimension = i + __SORT_BRANCH_STACK; 558 559 list_add_tail(&sd->entry->list, &hist_entry__sort_list); 560 sd->taken = 1; 561 562 return 0; 563 } 564 565 return -ESRCH; 566 } 567 568 int setup_sorting(void) 569 { 570 char *tmp, *tok, *str = strdup(sort_order); 571 int ret = 0; 572 573 if (str == NULL) { 574 error("Not enough memory to setup sort keys"); 575 return -ENOMEM; 576 } 577 578 for (tok = strtok_r(str, ", ", &tmp); 579 tok; tok = strtok_r(NULL, ", ", &tmp)) { 580 ret = sort_dimension__add(tok); 581 if (ret == -EINVAL) { 582 error("Invalid --sort key: `%s'", tok); 583 break; 584 } else if (ret == -ESRCH) { 585 error("Unknown --sort key: `%s'", tok); 586 break; 587 } 588 } 589 590 free(str); 591 return ret; 592 } 593 594 void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list, 595 const char *list_name, FILE *fp) 596 { 597 if (list && strlist__nr_entries(list) == 1) { 598 if (fp != NULL) 599 fprintf(fp, "# %s: %s\n", list_name, 600 strlist__entry(list, 0)->s); 601 self->elide = true; 602 } 603 } 604