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 return n; 37 } 38 39 static int64_t cmp_null(void *l, void *r) 40 { 41 if (!l && !r) 42 return 0; 43 else if (!l) 44 return -1; 45 else 46 return 1; 47 } 48 49 /* --sort pid */ 50 51 static int64_t 52 sort__thread_cmp(struct hist_entry *left, struct hist_entry *right) 53 { 54 return right->thread->pid - left->thread->pid; 55 } 56 57 static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf, 58 size_t size, unsigned int width) 59 { 60 return repsep_snprintf(bf, size, "%*s:%5d", width, 61 self->thread->comm ?: "", self->thread->pid); 62 } 63 64 struct sort_entry sort_thread = { 65 .se_header = "Command: Pid", 66 .se_cmp = sort__thread_cmp, 67 .se_snprintf = hist_entry__thread_snprintf, 68 .se_width_idx = HISTC_THREAD, 69 }; 70 71 /* --sort comm */ 72 73 static int64_t 74 sort__comm_cmp(struct hist_entry *left, struct hist_entry *right) 75 { 76 return right->thread->pid - left->thread->pid; 77 } 78 79 static int64_t 80 sort__comm_collapse(struct hist_entry *left, struct hist_entry *right) 81 { 82 char *comm_l = left->thread->comm; 83 char *comm_r = right->thread->comm; 84 85 if (!comm_l || !comm_r) 86 return cmp_null(comm_l, comm_r); 87 88 return strcmp(comm_l, comm_r); 89 } 90 91 static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf, 92 size_t size, unsigned int width) 93 { 94 return repsep_snprintf(bf, size, "%*s", width, self->thread->comm); 95 } 96 97 struct sort_entry sort_comm = { 98 .se_header = "Command", 99 .se_cmp = sort__comm_cmp, 100 .se_collapse = sort__comm_collapse, 101 .se_snprintf = hist_entry__comm_snprintf, 102 .se_width_idx = HISTC_COMM, 103 }; 104 105 /* --sort dso */ 106 107 static int64_t 108 sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) 109 { 110 struct dso *dso_l = left->ms.map ? left->ms.map->dso : NULL; 111 struct dso *dso_r = right->ms.map ? right->ms.map->dso : NULL; 112 const char *dso_name_l, *dso_name_r; 113 114 if (!dso_l || !dso_r) 115 return cmp_null(dso_l, dso_r); 116 117 if (verbose) { 118 dso_name_l = dso_l->long_name; 119 dso_name_r = dso_r->long_name; 120 } else { 121 dso_name_l = dso_l->short_name; 122 dso_name_r = dso_r->short_name; 123 } 124 125 return strcmp(dso_name_l, dso_name_r); 126 } 127 128 static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf, 129 size_t size, unsigned int width) 130 { 131 if (self->ms.map && self->ms.map->dso) { 132 const char *dso_name = !verbose ? self->ms.map->dso->short_name : 133 self->ms.map->dso->long_name; 134 return repsep_snprintf(bf, size, "%-*s", width, dso_name); 135 } 136 137 return repsep_snprintf(bf, size, "%-*s", width, "[unknown]"); 138 } 139 140 struct sort_entry sort_dso = { 141 .se_header = "Shared Object", 142 .se_cmp = sort__dso_cmp, 143 .se_snprintf = hist_entry__dso_snprintf, 144 .se_width_idx = HISTC_DSO, 145 }; 146 147 /* --sort symbol */ 148 149 static int64_t 150 sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) 151 { 152 u64 ip_l, ip_r; 153 154 if (!left->ms.sym && !right->ms.sym) 155 return right->level - left->level; 156 157 if (!left->ms.sym || !right->ms.sym) 158 return cmp_null(left->ms.sym, right->ms.sym); 159 160 if (left->ms.sym == right->ms.sym) 161 return 0; 162 163 ip_l = left->ms.sym->start; 164 ip_r = right->ms.sym->start; 165 166 return (int64_t)(ip_r - ip_l); 167 } 168 169 static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, 170 size_t size, unsigned int width __used) 171 { 172 size_t ret = 0; 173 174 if (verbose) { 175 char o = self->ms.map ? dso__symtab_origin(self->ms.map->dso) : '!'; 176 ret += repsep_snprintf(bf, size, "%-#*llx %c ", 177 BITS_PER_LONG / 4, self->ip, o); 178 } 179 180 if (!sort_dso.elide) 181 ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", self->level); 182 183 if (self->ms.sym) 184 ret += repsep_snprintf(bf + ret, size - ret, "%s", 185 self->ms.sym->name); 186 else 187 ret += repsep_snprintf(bf + ret, size - ret, "%-#*llx", 188 BITS_PER_LONG / 4, self->ip); 189 190 return ret; 191 } 192 193 struct sort_entry sort_sym = { 194 .se_header = "Symbol", 195 .se_cmp = sort__sym_cmp, 196 .se_snprintf = hist_entry__sym_snprintf, 197 .se_width_idx = HISTC_SYMBOL, 198 }; 199 200 /* --sort parent */ 201 202 static int64_t 203 sort__parent_cmp(struct hist_entry *left, struct hist_entry *right) 204 { 205 struct symbol *sym_l = left->parent; 206 struct symbol *sym_r = right->parent; 207 208 if (!sym_l || !sym_r) 209 return cmp_null(sym_l, sym_r); 210 211 return strcmp(sym_l->name, sym_r->name); 212 } 213 214 static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf, 215 size_t size, unsigned int width) 216 { 217 return repsep_snprintf(bf, size, "%-*s", width, 218 self->parent ? self->parent->name : "[other]"); 219 } 220 221 struct sort_entry sort_parent = { 222 .se_header = "Parent symbol", 223 .se_cmp = sort__parent_cmp, 224 .se_snprintf = hist_entry__parent_snprintf, 225 .se_width_idx = HISTC_PARENT, 226 }; 227 228 /* --sort cpu */ 229 230 static int64_t 231 sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right) 232 { 233 return right->cpu - left->cpu; 234 } 235 236 static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf, 237 size_t size, unsigned int width) 238 { 239 return repsep_snprintf(bf, size, "%-*d", width, self->cpu); 240 } 241 242 struct sort_entry sort_cpu = { 243 .se_header = "CPU", 244 .se_cmp = sort__cpu_cmp, 245 .se_snprintf = hist_entry__cpu_snprintf, 246 .se_width_idx = HISTC_CPU, 247 }; 248 249 struct sort_dimension { 250 const char *name; 251 struct sort_entry *entry; 252 int taken; 253 }; 254 255 static struct sort_dimension sort_dimensions[] = { 256 { .name = "pid", .entry = &sort_thread, }, 257 { .name = "comm", .entry = &sort_comm, }, 258 { .name = "dso", .entry = &sort_dso, }, 259 { .name = "symbol", .entry = &sort_sym, }, 260 { .name = "parent", .entry = &sort_parent, }, 261 { .name = "cpu", .entry = &sort_cpu, }, 262 }; 263 264 int sort_dimension__add(const char *tok) 265 { 266 unsigned int i; 267 268 for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) { 269 struct sort_dimension *sd = &sort_dimensions[i]; 270 271 if (strncasecmp(tok, sd->name, strlen(tok))) 272 continue; 273 274 if (sd->entry == &sort_parent) { 275 int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED); 276 if (ret) { 277 char err[BUFSIZ]; 278 279 regerror(ret, &parent_regex, err, sizeof(err)); 280 pr_err("Invalid regex: %s\n%s", parent_pattern, err); 281 return -EINVAL; 282 } 283 sort__has_parent = 1; 284 } 285 286 if (sd->taken) 287 return 0; 288 289 if (sd->entry->se_collapse) 290 sort__need_collapse = 1; 291 292 if (list_empty(&hist_entry__sort_list)) { 293 if (!strcmp(sd->name, "pid")) 294 sort__first_dimension = SORT_PID; 295 else if (!strcmp(sd->name, "comm")) 296 sort__first_dimension = SORT_COMM; 297 else if (!strcmp(sd->name, "dso")) 298 sort__first_dimension = SORT_DSO; 299 else if (!strcmp(sd->name, "symbol")) 300 sort__first_dimension = SORT_SYM; 301 else if (!strcmp(sd->name, "parent")) 302 sort__first_dimension = SORT_PARENT; 303 else if (!strcmp(sd->name, "cpu")) 304 sort__first_dimension = SORT_CPU; 305 } 306 307 list_add_tail(&sd->entry->list, &hist_entry__sort_list); 308 sd->taken = 1; 309 310 return 0; 311 } 312 313 return -ESRCH; 314 } 315 316 void setup_sorting(const char * const usagestr[], const struct option *opts) 317 { 318 char *tmp, *tok, *str = strdup(sort_order); 319 320 for (tok = strtok_r(str, ", ", &tmp); 321 tok; tok = strtok_r(NULL, ", ", &tmp)) { 322 if (sort_dimension__add(tok) < 0) { 323 error("Unknown --sort key: `%s'", tok); 324 usage_with_options(usagestr, opts); 325 } 326 } 327 328 free(str); 329 } 330 331 void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list, 332 const char *list_name, FILE *fp) 333 { 334 if (list && strlist__nr_entries(list) == 1) { 335 if (fp != NULL) 336 fprintf(fp, "# %s: %s\n", list_name, 337 strlist__entry(list, 0)->s); 338 self->elide = true; 339 } 340 } 341