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