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