diff options
-rw-r--r-- | Documentation/perf_counter/builtin-top.c | 303 |
1 files changed, 117 insertions, 186 deletions
diff --git a/Documentation/perf_counter/builtin-top.c b/Documentation/perf_counter/builtin-top.c index a890872638c1..52ba9f4216cf 100644 --- a/Documentation/perf_counter/builtin-top.c +++ b/Documentation/perf_counter/builtin-top.c | |||
@@ -44,8 +44,9 @@ | |||
44 | 44 | ||
45 | #include "perf.h" | 45 | #include "perf.h" |
46 | #include "builtin.h" | 46 | #include "builtin.h" |
47 | #include "util/symbol.h" | ||
47 | #include "util/util.h" | 48 | #include "util/util.h" |
48 | #include "util/util.h" | 49 | #include "util/rbtree.h" |
49 | #include "util/parse-options.h" | 50 | #include "util/parse-options.h" |
50 | #include "util/parse-events.h" | 51 | #include "util/parse-events.h" |
51 | 52 | ||
@@ -125,19 +126,21 @@ static uint64_t min_ip; | |||
125 | static uint64_t max_ip = -1ll; | 126 | static uint64_t max_ip = -1ll; |
126 | 127 | ||
127 | struct sym_entry { | 128 | struct sym_entry { |
128 | unsigned long long addr; | 129 | struct rb_node rb_node; |
129 | char *sym; | 130 | struct list_head node; |
130 | unsigned long count[MAX_COUNTERS]; | 131 | unsigned long count[MAX_COUNTERS]; |
131 | int skip; | 132 | int skip; |
132 | }; | 133 | }; |
133 | 134 | ||
134 | #define MAX_SYMS 100000 | ||
135 | |||
136 | static int sym_table_count; | ||
137 | |||
138 | struct sym_entry *sym_filter_entry; | 135 | struct sym_entry *sym_filter_entry; |
139 | 136 | ||
140 | static struct sym_entry sym_table[MAX_SYMS]; | 137 | struct dso *kernel_dso; |
138 | |||
139 | /* | ||
140 | * Symbols will be added here in record_ip and will get out | ||
141 | * after decayed. | ||
142 | */ | ||
143 | static LIST_HEAD(active_symbols); | ||
141 | 144 | ||
142 | /* | 145 | /* |
143 | * Ordering weight: count-1 * count-2 * ... / count-n | 146 | * Ordering weight: count-1 * count-2 * ... / count-n |
@@ -157,42 +160,60 @@ static double sym_weight(const struct sym_entry *sym) | |||
157 | return weight; | 160 | return weight; |
158 | } | 161 | } |
159 | 162 | ||
160 | static int compare(const void *__sym1, const void *__sym2) | ||
161 | { | ||
162 | const struct sym_entry *sym1 = __sym1, *sym2 = __sym2; | ||
163 | |||
164 | return sym_weight(sym1) < sym_weight(sym2); | ||
165 | } | ||
166 | |||
167 | static long events; | 163 | static long events; |
168 | static long userspace_events; | 164 | static long userspace_events; |
169 | static const char CONSOLE_CLEAR[] = "[H[2J"; | 165 | static const char CONSOLE_CLEAR[] = "[H[2J"; |
170 | 166 | ||
171 | static struct sym_entry tmp[MAX_SYMS]; | 167 | static void list_insert_active_sym(struct sym_entry *syme) |
168 | { | ||
169 | list_add(&syme->node, &active_symbols); | ||
170 | } | ||
171 | |||
172 | static void rb_insert_active_sym(struct rb_root *tree, struct sym_entry *se) | ||
173 | { | ||
174 | struct rb_node **p = &tree->rb_node; | ||
175 | struct rb_node *parent = NULL; | ||
176 | struct sym_entry *iter; | ||
177 | |||
178 | while (*p != NULL) { | ||
179 | parent = *p; | ||
180 | iter = rb_entry(parent, struct sym_entry, rb_node); | ||
181 | |||
182 | if (sym_weight(se) > sym_weight(iter)) | ||
183 | p = &(*p)->rb_left; | ||
184 | else | ||
185 | p = &(*p)->rb_right; | ||
186 | } | ||
187 | |||
188 | rb_link_node(&se->rb_node, parent, p); | ||
189 | rb_insert_color(&se->rb_node, tree); | ||
190 | } | ||
172 | 191 | ||
173 | static void print_sym_table(void) | 192 | static void print_sym_table(void) |
174 | { | 193 | { |
175 | int i, j, active_count, printed; | 194 | int printed, j; |
176 | int counter; | 195 | int counter; |
177 | float events_per_sec = events/delay_secs; | 196 | float events_per_sec = events/delay_secs; |
178 | float kevents_per_sec = (events-userspace_events)/delay_secs; | 197 | float kevents_per_sec = (events-userspace_events)/delay_secs; |
179 | float sum_kevents = 0.0; | 198 | float sum_kevents = 0.0; |
199 | struct sym_entry *syme, *n; | ||
200 | struct rb_root tmp = RB_ROOT; | ||
201 | struct rb_node *nd; | ||
180 | 202 | ||
181 | events = userspace_events = 0; | 203 | events = userspace_events = 0; |
182 | 204 | ||
183 | /* Iterate over symbol table and copy/tally/decay active symbols. */ | 205 | /* Sort the active symbols */ |
184 | for (i = 0, active_count = 0; i < sym_table_count; i++) { | 206 | list_for_each_entry_safe(syme, n, &active_symbols, node) { |
185 | if (sym_table[i].count[0]) { | 207 | if (syme->count[0] != 0) { |
186 | tmp[active_count++] = sym_table[i]; | 208 | rb_insert_active_sym(&tmp, syme); |
187 | sum_kevents += sym_table[i].count[0]; | 209 | sum_kevents += syme->count[0]; |
188 | 210 | ||
189 | for (j = 0; j < nr_counters; j++) | 211 | for (j = 0; j < nr_counters; j++) |
190 | sym_table[i].count[j] = zero ? 0 : sym_table[i].count[j] * 7 / 8; | 212 | syme->count[j] = zero ? 0 : syme->count[j] * 7 / 8; |
191 | } | 213 | } else |
214 | list_del_init(&syme->node); | ||
192 | } | 215 | } |
193 | 216 | ||
194 | qsort(tmp, active_count + 1, sizeof(tmp[0]), compare); | ||
195 | |||
196 | write(1, CONSOLE_CLEAR, strlen(CONSOLE_CLEAR)); | 217 | write(1, CONSOLE_CLEAR, strlen(CONSOLE_CLEAR)); |
197 | 218 | ||
198 | printf( | 219 | printf( |
@@ -238,23 +259,25 @@ static void print_sym_table(void) | |||
238 | " ______ ______ _____ ________________ _______________\n\n" | 259 | " ______ ______ _____ ________________ _______________\n\n" |
239 | ); | 260 | ); |
240 | 261 | ||
241 | for (i = 0, printed = 0; i < active_count; i++) { | 262 | for (nd = rb_first(&tmp); nd; nd = rb_next(nd)) { |
263 | struct sym_entry *syme = rb_entry(nd, struct sym_entry, rb_node); | ||
264 | struct symbol *sym = (struct symbol *)(syme + 1); | ||
242 | float pcnt; | 265 | float pcnt; |
243 | 266 | ||
244 | if (++printed > 18 || tmp[i].count[0] < count_filter) | 267 | if (++printed > 18 || syme->count[0] < count_filter) |
245 | break; | 268 | break; |
246 | 269 | ||
247 | pcnt = 100.0 - (100.0*((sum_kevents-tmp[i].count[0])/sum_kevents)); | 270 | pcnt = 100.0 - (100.0 * ((sum_kevents - syme->count[0]) / |
271 | sum_kevents)); | ||
248 | 272 | ||
249 | if (nr_counters == 1) | 273 | if (nr_counters == 1) |
250 | printf("%19.2f - %4.1f%% - %016llx : %s\n", | 274 | printf("%19.2f - %4.1f%% - %016llx : %s\n", |
251 | sym_weight(tmp + i), | 275 | sym_weight(syme), |
252 | pcnt, tmp[i].addr, tmp[i].sym); | 276 | pcnt, sym->start, sym->name); |
253 | else | 277 | else |
254 | printf("%8.1f %10ld - %4.1f%% - %016llx : %s\n", | 278 | printf("%8.1f %10ld - %4.1f%% - %016llx : %s\n", |
255 | sym_weight(tmp + i), | 279 | sym_weight(syme), syme->count[0], |
256 | tmp[i].count[0], | 280 | pcnt, sym->start, sym->name); |
257 | pcnt, tmp[i].addr, tmp[i].sym); | ||
258 | } | 281 | } |
259 | 282 | ||
260 | { | 283 | { |
@@ -277,146 +300,85 @@ static void *display_thread(void *arg) | |||
277 | return NULL; | 300 | return NULL; |
278 | } | 301 | } |
279 | 302 | ||
280 | static int read_symbol(FILE *in, struct sym_entry *s) | 303 | static int symbol_filter(struct dso *self, struct symbol *sym) |
281 | { | 304 | { |
282 | static int filter_match = 0; | 305 | static int filter_match; |
283 | char *sym, stype; | 306 | struct sym_entry *syme; |
284 | char str[500]; | 307 | const char *name = sym->name; |
285 | int rc, pos; | 308 | |
286 | 309 | if (!strcmp(name, "_text") || | |
287 | rc = fscanf(in, "%llx %c %499s", &s->addr, &stype, str); | 310 | !strcmp(name, "_etext") || |
288 | if (rc == EOF) | 311 | !strcmp(name, "_sinittext") || |
289 | return -1; | 312 | !strncmp("init_module", name, 11) || |
290 | 313 | !strncmp("cleanup_module", name, 14) || | |
291 | assert(rc == 3); | 314 | strstr(name, "_text_start") || |
292 | 315 | strstr(name, "_text_end")) | |
293 | /* skip until end of line: */ | ||
294 | pos = strlen(str); | ||
295 | do { | ||
296 | rc = fgetc(in); | ||
297 | if (rc == '\n' || rc == EOF || pos >= 499) | ||
298 | break; | ||
299 | str[pos] = rc; | ||
300 | pos++; | ||
301 | } while (1); | ||
302 | str[pos] = 0; | ||
303 | |||
304 | sym = str; | ||
305 | |||
306 | /* Filter out known duplicates and non-text symbols. */ | ||
307 | if (!strcmp(sym, "_text")) | ||
308 | return 1; | ||
309 | if (!min_ip && !strcmp(sym, "_stext")) | ||
310 | return 1; | ||
311 | if (!strcmp(sym, "_etext") || !strcmp(sym, "_sinittext")) | ||
312 | return 1; | ||
313 | if (stype != 'T' && stype != 't') | ||
314 | return 1; | ||
315 | if (!strncmp("init_module", sym, 11) || !strncmp("cleanup_module", sym, 14)) | ||
316 | return 1; | ||
317 | if (strstr(sym, "_text_start") || strstr(sym, "_text_end")) | ||
318 | return 1; | 316 | return 1; |
319 | 317 | ||
320 | s->sym = malloc(strlen(str)+1); | 318 | syme = dso__sym_priv(self, sym); |
321 | assert(s->sym); | ||
322 | |||
323 | strcpy((char *)s->sym, str); | ||
324 | s->skip = 0; | ||
325 | |||
326 | /* Tag events to be skipped. */ | 319 | /* Tag events to be skipped. */ |
327 | if (!strcmp("default_idle", s->sym) || !strcmp("cpu_idle", s->sym)) | 320 | if (!strcmp("default_idle", name) || |
328 | s->skip = 1; | 321 | !strcmp("cpu_idle", name) || |
329 | else if (!strcmp("enter_idle", s->sym) || !strcmp("exit_idle", s->sym)) | 322 | !strcmp("enter_idle", name) || |
330 | s->skip = 1; | 323 | !strcmp("exit_idle", name) || |
331 | else if (!strcmp("mwait_idle", s->sym)) | 324 | !strcmp("mwait_idle", name)) |
332 | s->skip = 1; | 325 | syme->skip = 1; |
333 | 326 | ||
334 | if (filter_match == 1) { | 327 | if (filter_match == 1) { |
335 | filter_end = s->addr; | 328 | filter_end = sym->start; |
336 | filter_match = -1; | 329 | filter_match = -1; |
337 | if (filter_end - filter_start > 10000) { | 330 | if (filter_end - filter_start > 10000) { |
338 | printf("hm, too large filter symbol <%s> - skipping.\n", | 331 | fprintf(stderr, |
332 | "hm, too large filter symbol <%s> - skipping.\n", | ||
339 | sym_filter); | 333 | sym_filter); |
340 | printf("symbol filter start: %016lx\n", filter_start); | 334 | fprintf(stderr, "symbol filter start: %016lx\n", |
341 | printf(" end: %016lx\n", filter_end); | 335 | filter_start); |
336 | fprintf(stderr, " end: %016lx\n", | ||
337 | filter_end); | ||
342 | filter_end = filter_start = 0; | 338 | filter_end = filter_start = 0; |
343 | sym_filter = NULL; | 339 | sym_filter = NULL; |
344 | sleep(1); | 340 | sleep(1); |
345 | } | 341 | } |
346 | } | 342 | } |
347 | if (filter_match == 0 && sym_filter && !strcmp(s->sym, sym_filter)) { | 343 | |
344 | if (filter_match == 0 && sym_filter && !strcmp(name, sym_filter)) { | ||
348 | filter_match = 1; | 345 | filter_match = 1; |
349 | filter_start = s->addr; | 346 | filter_start = sym->start; |
350 | } | 347 | } |
351 | 348 | ||
349 | |||
352 | return 0; | 350 | return 0; |
353 | } | 351 | } |
354 | 352 | ||
355 | static int compare_addr(const void *__sym1, const void *__sym2) | 353 | static int parse_symbols(void) |
356 | { | 354 | { |
357 | const struct sym_entry *sym1 = __sym1, *sym2 = __sym2; | 355 | struct rb_node *node; |
356 | struct symbol *sym; | ||
358 | 357 | ||
359 | return sym1->addr > sym2->addr; | 358 | kernel_dso = dso__new("[kernel]", sizeof(struct sym_entry)); |
360 | } | 359 | if (kernel_dso == NULL) |
361 | 360 | return -1; | |
362 | static void sort_symbol_table(void) | ||
363 | { | ||
364 | int i, dups; | ||
365 | |||
366 | do { | ||
367 | qsort(sym_table, sym_table_count, sizeof(sym_table[0]), compare_addr); | ||
368 | for (i = 0, dups = 0; i < sym_table_count; i++) { | ||
369 | if (sym_table[i].addr == sym_table[i+1].addr) { | ||
370 | sym_table[i+1].addr = -1ll; | ||
371 | dups++; | ||
372 | } | ||
373 | } | ||
374 | sym_table_count -= dups; | ||
375 | } while(dups); | ||
376 | } | ||
377 | |||
378 | static void parse_symbols(void) | ||
379 | { | ||
380 | struct sym_entry *last; | ||
381 | 361 | ||
382 | FILE *kallsyms = fopen("/proc/kallsyms", "r"); | 362 | if (dso__load_kernel(kernel_dso, NULL, symbol_filter) != 0) |
363 | goto out_delete_dso; | ||
383 | 364 | ||
384 | if (!kallsyms) { | 365 | node = rb_first(&kernel_dso->syms); |
385 | printf("Could not open /proc/kallsyms - no CONFIG_KALLSYMS_ALL=y?\n"); | 366 | sym = rb_entry(node, struct symbol, rb_node); |
386 | exit(-1); | 367 | min_ip = sym->start; |
387 | } | ||
388 | 368 | ||
389 | while (!feof(kallsyms)) { | 369 | node = rb_last(&kernel_dso->syms); |
390 | if (read_symbol(kallsyms, &sym_table[sym_table_count]) == 0) { | 370 | sym = rb_entry(node, struct symbol, rb_node); |
391 | sym_table_count++; | 371 | max_ip = sym->start; |
392 | assert(sym_table_count <= MAX_SYMS); | ||
393 | } | ||
394 | } | ||
395 | 372 | ||
396 | sort_symbol_table(); | 373 | if (dump_symtab) |
397 | min_ip = sym_table[0].addr; | 374 | dso__fprintf(kernel_dso, stdout); |
398 | max_ip = sym_table[sym_table_count-1].addr; | ||
399 | last = sym_table + sym_table_count++; | ||
400 | 375 | ||
401 | last->addr = -1ll; | 376 | return 0; |
402 | last->sym = "<end>"; | ||
403 | |||
404 | if (filter_end) { | ||
405 | int count; | ||
406 | for (count=0; count < sym_table_count; count ++) { | ||
407 | if (!strcmp(sym_table[count].sym, sym_filter)) { | ||
408 | sym_filter_entry = &sym_table[count]; | ||
409 | break; | ||
410 | } | ||
411 | } | ||
412 | } | ||
413 | if (dump_symtab) { | ||
414 | int i; | ||
415 | 377 | ||
416 | for (i = 0; i < sym_table_count; i++) | 378 | out_delete_dso: |
417 | fprintf(stderr, "%llx %s\n", | 379 | dso__delete(kernel_dso); |
418 | sym_table[i].addr, sym_table[i].sym); | 380 | kernel_dso = NULL; |
419 | } | 381 | return -1; |
420 | } | 382 | } |
421 | 383 | ||
422 | #define TRACE_COUNT 3 | 384 | #define TRACE_COUNT 3 |
@@ -426,51 +388,20 @@ static void parse_symbols(void) | |||
426 | */ | 388 | */ |
427 | static void record_ip(uint64_t ip, int counter) | 389 | static void record_ip(uint64_t ip, int counter) |
428 | { | 390 | { |
429 | int left_idx, middle_idx, right_idx, idx; | 391 | struct symbol *sym = dso__find_symbol(kernel_dso, ip); |
430 | unsigned long left, middle, right; | ||
431 | |||
432 | left_idx = 0; | ||
433 | right_idx = sym_table_count-1; | ||
434 | assert(ip <= max_ip && ip >= min_ip); | ||
435 | |||
436 | while (left_idx + 1 < right_idx) { | ||
437 | middle_idx = (left_idx + right_idx) / 2; | ||
438 | 392 | ||
439 | left = sym_table[ left_idx].addr; | 393 | if (sym != NULL) { |
440 | middle = sym_table[middle_idx].addr; | 394 | struct sym_entry *syme = dso__sym_priv(kernel_dso, sym); |
441 | right = sym_table[ right_idx].addr; | ||
442 | 395 | ||
443 | if (!(left <= middle && middle <= right)) { | 396 | if (!syme->skip) { |
444 | printf("%016lx...\n%016lx...\n%016lx\n", left, middle, right); | 397 | syme->count[counter]++; |
445 | printf("%d %d %d\n", left_idx, middle_idx, right_idx); | 398 | if (list_empty(&syme->node) || !syme->node.next) |
399 | list_insert_active_sym(syme); | ||
400 | return; | ||
446 | } | 401 | } |
447 | assert(left <= middle && middle <= right); | ||
448 | if (!(left <= ip && ip <= right)) { | ||
449 | printf(" left: %016lx\n", left); | ||
450 | printf(" ip: %016lx\n", (unsigned long)ip); | ||
451 | printf("right: %016lx\n", right); | ||
452 | } | ||
453 | assert(left <= ip && ip <= right); | ||
454 | /* | ||
455 | * [ left .... target .... middle .... right ] | ||
456 | * => right := middle | ||
457 | */ | ||
458 | if (ip < middle) { | ||
459 | right_idx = middle_idx; | ||
460 | continue; | ||
461 | } | ||
462 | /* | ||
463 | * [ left .... middle ... target ... right ] | ||
464 | * => left := middle | ||
465 | */ | ||
466 | left_idx = middle_idx; | ||
467 | } | 402 | } |
468 | 403 | ||
469 | idx = left_idx; | 404 | events--; |
470 | |||
471 | if (!sym_table[idx].skip) | ||
472 | sym_table[idx].count[counter]++; | ||
473 | else events--; | ||
474 | } | 405 | } |
475 | 406 | ||
476 | static void process_event(uint64_t ip, int counter) | 407 | static void process_event(uint64_t ip, int counter) |