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