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