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