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 12 enum sort_type sort__first_dimension; 13 14 char * field_sep; 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 (field_sep && n > 0) { 26 char *sep = bf; 27 28 while (1) { 29 sep = strchr(sep, *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 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 111 sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) 112 { 113 struct dso *dso_l = left->ms.map ? left->ms.map->dso : NULL; 114 struct dso *dso_r = right->ms.map ? right->ms.map->dso : NULL; 115 const char *dso_name_l, *dso_name_r; 116 117 if (!dso_l || !dso_r) 118 return cmp_null(dso_l, dso_r); 119 120 if (verbose) { 121 dso_name_l = dso_l->long_name; 122 dso_name_r = dso_r->long_name; 123 } else { 124 dso_name_l = dso_l->short_name; 125 dso_name_r = dso_r->short_name; 126 } 127 128 return strcmp(dso_name_l, dso_name_r); 129 } 130 131 static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf, 132 size_t size, unsigned int width) 133 { 134 if (self->ms.map && self->ms.map->dso) { 135 const char *dso_name = !verbose ? self->ms.map->dso->short_name : 136 self->ms.map->dso->long_name; 137 return repsep_snprintf(bf, size, "%-*s", width, dso_name); 138 } 139 140 return repsep_snprintf(bf, size, "%-*s", width, "[unknown]"); 141 } 142 143 struct sort_entry sort_dso = { 144 .se_header = "Shared Object", 145 .se_cmp = sort__dso_cmp, 146 .se_snprintf = hist_entry__dso_snprintf, 147 .se_width_idx = HISTC_DSO, 148 }; 149 150 /* --sort symbol */ 151 152 static int64_t 153 sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) 154 { 155 u64 ip_l, ip_r; 156 157 if (!left->ms.sym && !right->ms.sym) 158 return right->level - left->level; 159 160 if (!left->ms.sym || !right->ms.sym) 161 return cmp_null(left->ms.sym, right->ms.sym); 162 163 if (left->ms.sym == right->ms.sym) 164 return 0; 165 166 ip_l = left->ms.sym->start; 167 ip_r = right->ms.sym->start; 168 169 return (int64_t)(ip_r - ip_l); 170 } 171 172 static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, 173 size_t size, unsigned int width __used) 174 { 175 size_t ret = 0; 176 177 if (verbose) { 178 char o = self->ms.map ? dso__symtab_origin(self->ms.map->dso) : '!'; 179 ret += repsep_snprintf(bf, size, "%-#*llx %c ", 180 BITS_PER_LONG / 4, self->ip, o); 181 } 182 183 if (!sort_dso.elide) 184 ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", self->level); 185 186 if (self->ms.sym) 187 ret += repsep_snprintf(bf + ret, size - ret, "%s", 188 self->ms.sym->name); 189 else 190 ret += repsep_snprintf(bf + ret, size - ret, "%-#*llx", 191 BITS_PER_LONG / 4, self->ip); 192 193 return ret; 194 } 195 196 struct sort_entry sort_sym = { 197 .se_header = "Symbol", 198 .se_cmp = sort__sym_cmp, 199 .se_snprintf = hist_entry__sym_snprintf, 200 .se_width_idx = HISTC_SYMBOL, 201 }; 202 203 /* --sort parent */ 204 205 static int64_t 206 sort__parent_cmp(struct hist_entry *left, struct hist_entry *right) 207 { 208 struct symbol *sym_l = left->parent; 209 struct symbol *sym_r = right->parent; 210 211 if (!sym_l || !sym_r) 212 return cmp_null(sym_l, sym_r); 213 214 return strcmp(sym_l->name, sym_r->name); 215 } 216 217 static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf, 218 size_t size, unsigned int width) 219 { 220 return repsep_snprintf(bf, size, "%-*s", width, 221 self->parent ? self->parent->name : "[other]"); 222 } 223 224 struct sort_entry sort_parent = { 225 .se_header = "Parent symbol", 226 .se_cmp = sort__parent_cmp, 227 .se_snprintf = hist_entry__parent_snprintf, 228 .se_width_idx = HISTC_PARENT, 229 }; 230 231 /* --sort cpu */ 232 233 static int64_t 234 sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right) 235 { 236 return right->cpu - left->cpu; 237 } 238 239 static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf, 240 size_t size, unsigned int width) 241 { 242 return repsep_snprintf(bf, size, "%-*d", width, self->cpu); 243 } 244 245 struct sort_entry sort_cpu = { 246 .se_header = "CPU", 247 .se_cmp = sort__cpu_cmp, 248 .se_snprintf = hist_entry__cpu_snprintf, 249 .se_width_idx = HISTC_CPU, 250 }; 251 252 struct sort_dimension { 253 const char *name; 254 struct sort_entry *entry; 255 int taken; 256 }; 257 258 static struct sort_dimension sort_dimensions[] = { 259 { .name = "pid", .entry = &sort_thread, }, 260 { .name = "comm", .entry = &sort_comm, }, 261 { .name = "dso", .entry = &sort_dso, }, 262 { .name = "symbol", .entry = &sort_sym, }, 263 { .name = "parent", .entry = &sort_parent, }, 264 { .name = "cpu", .entry = &sort_cpu, }, 265 }; 266 267 int sort_dimension__add(const char *tok) 268 { 269 unsigned int i; 270 271 for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) { 272 struct sort_dimension *sd = &sort_dimensions[i]; 273 274 if (strncasecmp(tok, sd->name, strlen(tok))) 275 continue; 276 277 if (sd->entry == &sort_parent) { 278 int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED); 279 if (ret) { 280 char err[BUFSIZ]; 281 282 regerror(ret, &parent_regex, err, sizeof(err)); 283 pr_err("Invalid regex: %s\n%s", parent_pattern, err); 284 return -EINVAL; 285 } 286 sort__has_parent = 1; 287 } 288 289 if (sd->taken) 290 return 0; 291 292 if (sd->entry->se_collapse) 293 sort__need_collapse = 1; 294 295 if (list_empty(&hist_entry__sort_list)) { 296 if (!strcmp(sd->name, "pid")) 297 sort__first_dimension = SORT_PID; 298 else if (!strcmp(sd->name, "comm")) 299 sort__first_dimension = SORT_COMM; 300 else if (!strcmp(sd->name, "dso")) 301 sort__first_dimension = SORT_DSO; 302 else if (!strcmp(sd->name, "symbol")) 303 sort__first_dimension = SORT_SYM; 304 else if (!strcmp(sd->name, "parent")) 305 sort__first_dimension = SORT_PARENT; 306 else if (!strcmp(sd->name, "cpu")) 307 sort__first_dimension = SORT_CPU; 308 } 309 310 list_add_tail(&sd->entry->list, &hist_entry__sort_list); 311 sd->taken = 1; 312 313 return 0; 314 } 315 316 return -ESRCH; 317 } 318 319 void setup_sorting(const char * const usagestr[], const struct option *opts) 320 { 321 char *tmp, *tok, *str = strdup(sort_order); 322 323 for (tok = strtok_r(str, ", ", &tmp); 324 tok; tok = strtok_r(NULL, ", ", &tmp)) { 325 if (sort_dimension__add(tok) < 0) { 326 error("Unknown --sort key: `%s'", tok); 327 usage_with_options(usagestr, opts); 328 } 329 } 330 331 free(str); 332 } 333 334 void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list, 335 const char *list_name, FILE *fp) 336 { 337 if (list && strlist__nr_entries(list) == 1) { 338 if (fp != NULL) 339 fprintf(fp, "# %s: %s\n", list_name, 340 strlist__entry(list, 0)->s); 341 self->elide = true; 342 } 343 } 344