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__branch_mode = -1; /* -1 = means not set */ 12 13 enum sort_type sort__first_dimension; 14 15 char * field_sep; 16 17 LIST_HEAD(hist_entry__sort_list); 18 19 static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...) 20 { 21 int n; 22 va_list ap; 23 24 va_start(ap, fmt); 25 n = vsnprintf(bf, size, fmt, ap); 26 if (field_sep && n > 0) { 27 char *sep = bf; 28 29 while (1) { 30 sep = strchr(sep, *field_sep); 31 if (sep == NULL) 32 break; 33 *sep = '.'; 34 } 35 } 36 va_end(ap); 37 38 if (n >= (int)size) 39 return size - 1; 40 return n; 41 } 42 43 static int64_t cmp_null(void *l, void *r) 44 { 45 if (!l && !r) 46 return 0; 47 else if (!l) 48 return -1; 49 else 50 return 1; 51 } 52 53 /* --sort pid */ 54 55 static int64_t 56 sort__thread_cmp(struct hist_entry *left, struct hist_entry *right) 57 { 58 return right->thread->pid - left->thread->pid; 59 } 60 61 static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf, 62 size_t size, unsigned int width) 63 { 64 return repsep_snprintf(bf, size, "%*s:%5d", width, 65 self->thread->comm ?: "", self->thread->pid); 66 } 67 68 struct sort_entry sort_thread = { 69 .se_header = "Command: Pid", 70 .se_cmp = sort__thread_cmp, 71 .se_snprintf = hist_entry__thread_snprintf, 72 .se_width_idx = HISTC_THREAD, 73 }; 74 75 /* --sort comm */ 76 77 static int64_t 78 sort__comm_cmp(struct hist_entry *left, struct hist_entry *right) 79 { 80 return right->thread->pid - left->thread->pid; 81 } 82 83 static int64_t 84 sort__comm_collapse(struct hist_entry *left, struct hist_entry *right) 85 { 86 char *comm_l = left->thread->comm; 87 char *comm_r = right->thread->comm; 88 89 if (!comm_l || !comm_r) 90 return cmp_null(comm_l, comm_r); 91 92 return strcmp(comm_l, comm_r); 93 } 94 95 static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf, 96 size_t size, unsigned int width) 97 { 98 return repsep_snprintf(bf, size, "%*s", width, self->thread->comm); 99 } 100 101 static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r) 102 { 103 struct dso *dso_l = map_l ? map_l->dso : NULL; 104 struct dso *dso_r = map_r ? map_r->dso : NULL; 105 const char *dso_name_l, *dso_name_r; 106 107 if (!dso_l || !dso_r) 108 return cmp_null(dso_l, dso_r); 109 110 if (verbose) { 111 dso_name_l = dso_l->long_name; 112 dso_name_r = dso_r->long_name; 113 } else { 114 dso_name_l = dso_l->short_name; 115 dso_name_r = dso_r->short_name; 116 } 117 118 return strcmp(dso_name_l, dso_name_r); 119 } 120 121 struct sort_entry sort_comm = { 122 .se_header = "Command", 123 .se_cmp = sort__comm_cmp, 124 .se_collapse = sort__comm_collapse, 125 .se_snprintf = hist_entry__comm_snprintf, 126 .se_width_idx = HISTC_COMM, 127 }; 128 129 /* --sort dso */ 130 131 static int64_t 132 sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) 133 { 134 return _sort__dso_cmp(left->ms.map, right->ms.map); 135 } 136 137 138 static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r, 139 u64 ip_l, u64 ip_r) 140 { 141 if (!sym_l || !sym_r) 142 return cmp_null(sym_l, sym_r); 143 144 if (sym_l == sym_r) 145 return 0; 146 147 if (sym_l) 148 ip_l = sym_l->start; 149 if (sym_r) 150 ip_r = sym_r->start; 151 152 return (int64_t)(ip_r - ip_l); 153 } 154 155 static int _hist_entry__dso_snprintf(struct map *map, char *bf, 156 size_t size, unsigned int width) 157 { 158 if (map && map->dso) { 159 const char *dso_name = !verbose ? map->dso->short_name : 160 map->dso->long_name; 161 return repsep_snprintf(bf, size, "%-*s", width, dso_name); 162 } 163 164 return repsep_snprintf(bf, size, "%-*s", width, "[unknown]"); 165 } 166 167 static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf, 168 size_t size, unsigned int width) 169 { 170 return _hist_entry__dso_snprintf(self->ms.map, bf, size, width); 171 } 172 173 static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym, 174 u64 ip, char level, char *bf, size_t size, 175 unsigned int width __used) 176 { 177 size_t ret = 0; 178 179 if (verbose) { 180 char o = map ? dso__symtab_origin(map->dso) : '!'; 181 ret += repsep_snprintf(bf, size, "%-#*llx %c ", 182 BITS_PER_LONG / 4, ip, o); 183 } 184 185 ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level); 186 if (sym) 187 ret += repsep_snprintf(bf + ret, size - ret, "%-*s", 188 width - ret, 189 sym->name); 190 else { 191 size_t len = BITS_PER_LONG / 4; 192 ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx", 193 len, ip); 194 ret += repsep_snprintf(bf + ret, size - ret, "%-*s", 195 width - ret, ""); 196 } 197 198 return ret; 199 } 200 201 202 struct sort_entry sort_dso = { 203 .se_header = "Shared Object", 204 .se_cmp = sort__dso_cmp, 205 .se_snprintf = hist_entry__dso_snprintf, 206 .se_width_idx = HISTC_DSO, 207 }; 208 209 static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, 210 size_t size, unsigned int width __used) 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, unsigned int width __used) 254 { 255 FILE *fp; 256 char cmd[PATH_MAX + 2], *path = self->srcline, *nl; 257 size_t line_len; 258 259 if (path != NULL) 260 goto out_path; 261 262 snprintf(cmd, sizeof(cmd), "addr2line -e %s %016" PRIx64, 263 self->ms.map->dso->long_name, self->ip); 264 fp = popen(cmd, "r"); 265 if (!fp) 266 goto out_ip; 267 268 if (getline(&path, &line_len, fp) < 0 || !line_len) 269 goto out_ip; 270 fclose(fp); 271 self->srcline = strdup(path); 272 if (self->srcline == NULL) 273 goto out_ip; 274 275 nl = strchr(self->srcline, '\n'); 276 if (nl != NULL) 277 *nl = '\0'; 278 path = self->srcline; 279 out_path: 280 return repsep_snprintf(bf, size, "%s", path); 281 out_ip: 282 return repsep_snprintf(bf, size, "%-#*llx", BITS_PER_LONG / 4, self->ip); 283 } 284 285 struct sort_entry sort_srcline = { 286 .se_header = "Source:Line", 287 .se_cmp = sort__srcline_cmp, 288 .se_snprintf = hist_entry__srcline_snprintf, 289 .se_width_idx = HISTC_SRCLINE, 290 }; 291 292 /* --sort parent */ 293 294 static int64_t 295 sort__parent_cmp(struct hist_entry *left, struct hist_entry *right) 296 { 297 struct symbol *sym_l = left->parent; 298 struct symbol *sym_r = right->parent; 299 300 if (!sym_l || !sym_r) 301 return cmp_null(sym_l, sym_r); 302 303 return strcmp(sym_l->name, sym_r->name); 304 } 305 306 static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf, 307 size_t size, unsigned int width) 308 { 309 return repsep_snprintf(bf, size, "%-*s", width, 310 self->parent ? self->parent->name : "[other]"); 311 } 312 313 struct sort_entry sort_parent = { 314 .se_header = "Parent symbol", 315 .se_cmp = sort__parent_cmp, 316 .se_snprintf = hist_entry__parent_snprintf, 317 .se_width_idx = HISTC_PARENT, 318 }; 319 320 /* --sort cpu */ 321 322 static int64_t 323 sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right) 324 { 325 return right->cpu - left->cpu; 326 } 327 328 static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf, 329 size_t size, unsigned int width) 330 { 331 return repsep_snprintf(bf, size, "%-*d", width, self->cpu); 332 } 333 334 struct sort_entry sort_cpu = { 335 .se_header = "CPU", 336 .se_cmp = sort__cpu_cmp, 337 .se_snprintf = hist_entry__cpu_snprintf, 338 .se_width_idx = HISTC_CPU, 339 }; 340 341 static int64_t 342 sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right) 343 { 344 return _sort__dso_cmp(left->branch_info->from.map, 345 right->branch_info->from.map); 346 } 347 348 static int hist_entry__dso_from_snprintf(struct hist_entry *self, char *bf, 349 size_t size, unsigned int width) 350 { 351 return _hist_entry__dso_snprintf(self->branch_info->from.map, 352 bf, size, width); 353 } 354 355 struct sort_entry sort_dso_from = { 356 .se_header = "Source Shared Object", 357 .se_cmp = sort__dso_from_cmp, 358 .se_snprintf = hist_entry__dso_from_snprintf, 359 .se_width_idx = HISTC_DSO_FROM, 360 }; 361 362 static int64_t 363 sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right) 364 { 365 return _sort__dso_cmp(left->branch_info->to.map, 366 right->branch_info->to.map); 367 } 368 369 static int hist_entry__dso_to_snprintf(struct hist_entry *self, char *bf, 370 size_t size, unsigned int width) 371 { 372 return _hist_entry__dso_snprintf(self->branch_info->to.map, 373 bf, size, width); 374 } 375 376 static int64_t 377 sort__sym_from_cmp(struct hist_entry *left, struct hist_entry *right) 378 { 379 struct addr_map_symbol *from_l = &left->branch_info->from; 380 struct addr_map_symbol *from_r = &right->branch_info->from; 381 382 if (!from_l->sym && !from_r->sym) 383 return right->level - left->level; 384 385 return _sort__sym_cmp(from_l->sym, from_r->sym, from_l->addr, 386 from_r->addr); 387 } 388 389 static int64_t 390 sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right) 391 { 392 struct addr_map_symbol *to_l = &left->branch_info->to; 393 struct addr_map_symbol *to_r = &right->branch_info->to; 394 395 if (!to_l->sym && !to_r->sym) 396 return right->level - left->level; 397 398 return _sort__sym_cmp(to_l->sym, to_r->sym, to_l->addr, to_r->addr); 399 } 400 401 static int hist_entry__sym_from_snprintf(struct hist_entry *self, char *bf, 402 size_t size, unsigned int width __used) 403 { 404 struct addr_map_symbol *from = &self->branch_info->from; 405 return _hist_entry__sym_snprintf(from->map, from->sym, from->addr, 406 self->level, bf, size, width); 407 408 } 409 410 static int hist_entry__sym_to_snprintf(struct hist_entry *self, char *bf, 411 size_t size, unsigned int width __used) 412 { 413 struct addr_map_symbol *to = &self->branch_info->to; 414 return _hist_entry__sym_snprintf(to->map, to->sym, to->addr, 415 self->level, bf, size, width); 416 417 } 418 419 struct sort_entry sort_dso_to = { 420 .se_header = "Target Shared Object", 421 .se_cmp = sort__dso_to_cmp, 422 .se_snprintf = hist_entry__dso_to_snprintf, 423 .se_width_idx = HISTC_DSO_TO, 424 }; 425 426 struct sort_entry sort_sym_from = { 427 .se_header = "Source Symbol", 428 .se_cmp = sort__sym_from_cmp, 429 .se_snprintf = hist_entry__sym_from_snprintf, 430 .se_width_idx = HISTC_SYMBOL_FROM, 431 }; 432 433 struct sort_entry sort_sym_to = { 434 .se_header = "Target Symbol", 435 .se_cmp = sort__sym_to_cmp, 436 .se_snprintf = hist_entry__sym_to_snprintf, 437 .se_width_idx = HISTC_SYMBOL_TO, 438 }; 439 440 static int64_t 441 sort__mispredict_cmp(struct hist_entry *left, struct hist_entry *right) 442 { 443 const unsigned char mp = left->branch_info->flags.mispred != 444 right->branch_info->flags.mispred; 445 const unsigned char p = left->branch_info->flags.predicted != 446 right->branch_info->flags.predicted; 447 448 return mp || p; 449 } 450 451 static int hist_entry__mispredict_snprintf(struct hist_entry *self, char *bf, 452 size_t size, unsigned int width){ 453 static const char *out = "N/A"; 454 455 if (self->branch_info->flags.predicted) 456 out = "N"; 457 else if (self->branch_info->flags.mispred) 458 out = "Y"; 459 460 return repsep_snprintf(bf, size, "%-*s", width, out); 461 } 462 463 struct sort_entry sort_mispredict = { 464 .se_header = "Branch Mispredicted", 465 .se_cmp = sort__mispredict_cmp, 466 .se_snprintf = hist_entry__mispredict_snprintf, 467 .se_width_idx = HISTC_MISPREDICT, 468 }; 469 470 struct sort_dimension { 471 const char *name; 472 struct sort_entry *entry; 473 int taken; 474 }; 475 476 #define DIM(d, n, func) [d] = { .name = n, .entry = &(func) } 477 478 static struct sort_dimension sort_dimensions[] = { 479 DIM(SORT_PID, "pid", sort_thread), 480 DIM(SORT_COMM, "comm", sort_comm), 481 DIM(SORT_DSO, "dso", sort_dso), 482 DIM(SORT_DSO_FROM, "dso_from", sort_dso_from), 483 DIM(SORT_DSO_TO, "dso_to", sort_dso_to), 484 DIM(SORT_SYM, "symbol", sort_sym), 485 DIM(SORT_SYM_TO, "symbol_from", sort_sym_from), 486 DIM(SORT_SYM_FROM, "symbol_to", sort_sym_to), 487 DIM(SORT_PARENT, "parent", sort_parent), 488 DIM(SORT_CPU, "cpu", sort_cpu), 489 DIM(SORT_MISPREDICT, "mispredict", sort_mispredict), 490 DIM(SORT_SRCLINE, "srcline", sort_srcline), 491 }; 492 493 int sort_dimension__add(const char *tok) 494 { 495 unsigned int i; 496 497 for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) { 498 struct sort_dimension *sd = &sort_dimensions[i]; 499 500 if (strncasecmp(tok, sd->name, strlen(tok))) 501 continue; 502 if (sd->entry == &sort_parent) { 503 int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED); 504 if (ret) { 505 char err[BUFSIZ]; 506 507 regerror(ret, &parent_regex, err, sizeof(err)); 508 pr_err("Invalid regex: %s\n%s", parent_pattern, err); 509 return -EINVAL; 510 } 511 sort__has_parent = 1; 512 } 513 514 if (sd->taken) 515 return 0; 516 517 if (sd->entry->se_collapse) 518 sort__need_collapse = 1; 519 520 if (list_empty(&hist_entry__sort_list)) { 521 if (!strcmp(sd->name, "pid")) 522 sort__first_dimension = SORT_PID; 523 else if (!strcmp(sd->name, "comm")) 524 sort__first_dimension = SORT_COMM; 525 else if (!strcmp(sd->name, "dso")) 526 sort__first_dimension = SORT_DSO; 527 else if (!strcmp(sd->name, "symbol")) 528 sort__first_dimension = SORT_SYM; 529 else if (!strcmp(sd->name, "parent")) 530 sort__first_dimension = SORT_PARENT; 531 else if (!strcmp(sd->name, "cpu")) 532 sort__first_dimension = SORT_CPU; 533 else if (!strcmp(sd->name, "symbol_from")) 534 sort__first_dimension = SORT_SYM_FROM; 535 else if (!strcmp(sd->name, "symbol_to")) 536 sort__first_dimension = SORT_SYM_TO; 537 else if (!strcmp(sd->name, "dso_from")) 538 sort__first_dimension = SORT_DSO_FROM; 539 else if (!strcmp(sd->name, "dso_to")) 540 sort__first_dimension = SORT_DSO_TO; 541 else if (!strcmp(sd->name, "mispredict")) 542 sort__first_dimension = SORT_MISPREDICT; 543 } 544 545 list_add_tail(&sd->entry->list, &hist_entry__sort_list); 546 sd->taken = 1; 547 548 return 0; 549 } 550 return -ESRCH; 551 } 552 553 void setup_sorting(const char * const usagestr[], const struct option *opts) 554 { 555 char *tmp, *tok, *str = strdup(sort_order); 556 557 for (tok = strtok_r(str, ", ", &tmp); 558 tok; tok = strtok_r(NULL, ", ", &tmp)) { 559 if (sort_dimension__add(tok) < 0) { 560 error("Unknown --sort key: `%s'", tok); 561 usage_with_options(usagestr, opts); 562 } 563 } 564 565 free(str); 566 } 567 568 void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list, 569 const char *list_name, FILE *fp) 570 { 571 if (list && strlist__nr_entries(list) == 1) { 572 if (fp != NULL) 573 fprintf(fp, "# %s: %s\n", list_name, 574 strlist__entry(list, 0)->s); 575 self->elide = true; 576 } 577 } 578