|
@@ -31,6 +31,8 @@
|
|
|
#include <fcntl.h>
|
|
|
|
|
|
#include <stdio.h>
|
|
|
+#include <termios.h>
|
|
|
+#include <unistd.h>
|
|
|
|
|
|
#include <errno.h>
|
|
|
#include <time.h>
|
|
@@ -54,7 +56,7 @@ static int system_wide = 0;
|
|
|
|
|
|
static int default_interval = 100000;
|
|
|
|
|
|
-static u64 count_filter = 5;
|
|
|
+static int count_filter = 5;
|
|
|
static int print_entries = 15;
|
|
|
|
|
|
static int target_pid = -1;
|
|
@@ -69,14 +71,27 @@ static int freq = 0;
|
|
|
static int verbose = 0;
|
|
|
static char *vmlinux = NULL;
|
|
|
|
|
|
-static char *sym_filter;
|
|
|
-static unsigned long filter_start;
|
|
|
-static unsigned long filter_end;
|
|
|
-
|
|
|
static int delay_secs = 2;
|
|
|
static int zero;
|
|
|
static int dump_symtab;
|
|
|
|
|
|
+/*
|
|
|
+ * Source
|
|
|
+ */
|
|
|
+
|
|
|
+struct source_line {
|
|
|
+ u64 eip;
|
|
|
+ unsigned long count[MAX_COUNTERS];
|
|
|
+ char *line;
|
|
|
+ struct source_line *next;
|
|
|
+};
|
|
|
+
|
|
|
+static char *sym_filter = NULL;
|
|
|
+struct sym_entry *sym_filter_entry = NULL;
|
|
|
+static int sym_pcnt_filter = 5;
|
|
|
+static int sym_counter = 0;
|
|
|
+static int display_weighted = -1;
|
|
|
+
|
|
|
/*
|
|
|
* Symbols
|
|
|
*/
|
|
@@ -91,9 +106,237 @@ struct sym_entry {
|
|
|
unsigned long snap_count;
|
|
|
double weight;
|
|
|
int skip;
|
|
|
+ struct source_line *source;
|
|
|
+ struct source_line *lines;
|
|
|
+ struct source_line **lines_tail;
|
|
|
+ pthread_mutex_t source_lock;
|
|
|
};
|
|
|
|
|
|
-struct sym_entry *sym_filter_entry;
|
|
|
+/*
|
|
|
+ * Source functions
|
|
|
+ */
|
|
|
+
|
|
|
+static void parse_source(struct sym_entry *syme)
|
|
|
+{
|
|
|
+ struct symbol *sym;
|
|
|
+ struct module *module;
|
|
|
+ struct section *section = NULL;
|
|
|
+ FILE *file;
|
|
|
+ char command[PATH_MAX*2], *path = vmlinux;
|
|
|
+ u64 start, end, len;
|
|
|
+
|
|
|
+ if (!syme)
|
|
|
+ return;
|
|
|
+
|
|
|
+ if (syme->lines) {
|
|
|
+ pthread_mutex_lock(&syme->source_lock);
|
|
|
+ goto out_assign;
|
|
|
+ }
|
|
|
+
|
|
|
+ sym = (struct symbol *)(syme + 1);
|
|
|
+ module = sym->module;
|
|
|
+
|
|
|
+ if (module)
|
|
|
+ path = module->path;
|
|
|
+ if (!path)
|
|
|
+ return;
|
|
|
+
|
|
|
+ start = sym->obj_start;
|
|
|
+ if (!start)
|
|
|
+ start = sym->start;
|
|
|
+
|
|
|
+ if (module) {
|
|
|
+ section = module->sections->find_section(module->sections, ".text");
|
|
|
+ if (section)
|
|
|
+ start -= section->vma;
|
|
|
+ }
|
|
|
+
|
|
|
+ end = start + sym->end - sym->start + 1;
|
|
|
+ len = sym->end - sym->start;
|
|
|
+
|
|
|
+ sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s", start, end, path);
|
|
|
+
|
|
|
+ file = popen(command, "r");
|
|
|
+ if (!file)
|
|
|
+ return;
|
|
|
+
|
|
|
+ pthread_mutex_lock(&syme->source_lock);
|
|
|
+ syme->lines_tail = &syme->lines;
|
|
|
+ while (!feof(file)) {
|
|
|
+ struct source_line *src;
|
|
|
+ size_t dummy = 0;
|
|
|
+ char *c;
|
|
|
+
|
|
|
+ src = malloc(sizeof(struct source_line));
|
|
|
+ assert(src != NULL);
|
|
|
+ memset(src, 0, sizeof(struct source_line));
|
|
|
+
|
|
|
+ if (getline(&src->line, &dummy, file) < 0)
|
|
|
+ break;
|
|
|
+ if (!src->line)
|
|
|
+ break;
|
|
|
+
|
|
|
+ c = strchr(src->line, '\n');
|
|
|
+ if (c)
|
|
|
+ *c = 0;
|
|
|
+
|
|
|
+ src->next = NULL;
|
|
|
+ *syme->lines_tail = src;
|
|
|
+ syme->lines_tail = &src->next;
|
|
|
+
|
|
|
+ if (strlen(src->line)>8 && src->line[8] == ':') {
|
|
|
+ src->eip = strtoull(src->line, NULL, 16);
|
|
|
+ if (section)
|
|
|
+ src->eip += section->vma;
|
|
|
+ }
|
|
|
+ if (strlen(src->line)>8 && src->line[16] == ':') {
|
|
|
+ src->eip = strtoull(src->line, NULL, 16);
|
|
|
+ if (section)
|
|
|
+ src->eip += section->vma;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ pclose(file);
|
|
|
+out_assign:
|
|
|
+ sym_filter_entry = syme;
|
|
|
+ pthread_mutex_unlock(&syme->source_lock);
|
|
|
+}
|
|
|
+
|
|
|
+static void __zero_source_counters(struct sym_entry *syme)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+ struct source_line *line;
|
|
|
+
|
|
|
+ line = syme->lines;
|
|
|
+ while (line) {
|
|
|
+ for (i = 0; i < nr_counters; i++)
|
|
|
+ line->count[i] = 0;
|
|
|
+ line = line->next;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void record_precise_ip(struct sym_entry *syme, int counter, u64 ip)
|
|
|
+{
|
|
|
+ struct source_line *line;
|
|
|
+
|
|
|
+ if (syme != sym_filter_entry)
|
|
|
+ return;
|
|
|
+
|
|
|
+ if (pthread_mutex_trylock(&syme->source_lock))
|
|
|
+ return;
|
|
|
+
|
|
|
+ if (!syme->source)
|
|
|
+ goto out_unlock;
|
|
|
+
|
|
|
+ for (line = syme->lines; line; line = line->next) {
|
|
|
+ if (line->eip == ip) {
|
|
|
+ line->count[counter]++;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (line->eip > ip)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+out_unlock:
|
|
|
+ pthread_mutex_unlock(&syme->source_lock);
|
|
|
+}
|
|
|
+
|
|
|
+static void lookup_sym_source(struct sym_entry *syme)
|
|
|
+{
|
|
|
+ struct symbol *symbol = (struct symbol *)(syme + 1);
|
|
|
+ struct source_line *line;
|
|
|
+ char pattern[PATH_MAX];
|
|
|
+ char *idx;
|
|
|
+
|
|
|
+ sprintf(pattern, "<%s>:", symbol->name);
|
|
|
+
|
|
|
+ if (symbol->module) {
|
|
|
+ idx = strstr(pattern, "\t");
|
|
|
+ if (idx)
|
|
|
+ *idx = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ pthread_mutex_lock(&syme->source_lock);
|
|
|
+ for (line = syme->lines; line; line = line->next) {
|
|
|
+ if (strstr(line->line, pattern)) {
|
|
|
+ syme->source = line;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ pthread_mutex_unlock(&syme->source_lock);
|
|
|
+}
|
|
|
+
|
|
|
+static void show_lines(struct source_line *queue, int count, int total)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+ struct source_line *line;
|
|
|
+
|
|
|
+ line = queue;
|
|
|
+ for (i = 0; i < count; i++) {
|
|
|
+ float pcnt = 100.0*(float)line->count[sym_counter]/(float)total;
|
|
|
+
|
|
|
+ printf("%8li %4.1f%%\t%s\n", line->count[sym_counter], pcnt, line->line);
|
|
|
+ line = line->next;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+#define TRACE_COUNT 3
|
|
|
+
|
|
|
+static void show_details(struct sym_entry *syme)
|
|
|
+{
|
|
|
+ struct symbol *symbol;
|
|
|
+ struct source_line *line;
|
|
|
+ struct source_line *line_queue = NULL;
|
|
|
+ int displayed = 0;
|
|
|
+ int line_queue_count = 0, total = 0, more = 0;
|
|
|
+
|
|
|
+ if (!syme)
|
|
|
+ return;
|
|
|
+
|
|
|
+ if (!syme->source)
|
|
|
+ lookup_sym_source(syme);
|
|
|
+
|
|
|
+ if (!syme->source)
|
|
|
+ return;
|
|
|
+
|
|
|
+ symbol = (struct symbol *)(syme + 1);
|
|
|
+ printf("Showing %s for %s\n", event_name(sym_counter), symbol->name);
|
|
|
+ printf(" Events Pcnt (>=%d%%)\n", sym_pcnt_filter);
|
|
|
+
|
|
|
+ pthread_mutex_lock(&syme->source_lock);
|
|
|
+ line = syme->source;
|
|
|
+ while (line) {
|
|
|
+ total += line->count[sym_counter];
|
|
|
+ line = line->next;
|
|
|
+ }
|
|
|
+
|
|
|
+ line = syme->source;
|
|
|
+ while (line) {
|
|
|
+ float pcnt = 0.0;
|
|
|
+
|
|
|
+ if (!line_queue_count)
|
|
|
+ line_queue = line;
|
|
|
+ line_queue_count++;
|
|
|
+
|
|
|
+ if (line->count[sym_counter])
|
|
|
+ pcnt = 100.0 * line->count[sym_counter] / (float)total;
|
|
|
+ if (pcnt >= (float)sym_pcnt_filter) {
|
|
|
+ if (displayed <= print_entries)
|
|
|
+ show_lines(line_queue, line_queue_count, total);
|
|
|
+ else more++;
|
|
|
+ displayed += line_queue_count;
|
|
|
+ line_queue_count = 0;
|
|
|
+ line_queue = NULL;
|
|
|
+ } else if (line_queue_count > TRACE_COUNT) {
|
|
|
+ line_queue = line_queue->next;
|
|
|
+ line_queue_count--;
|
|
|
+ }
|
|
|
+
|
|
|
+ line->count[sym_counter] = zero ? 0 : line->count[sym_counter] * 7 / 8;
|
|
|
+ line = line->next;
|
|
|
+ }
|
|
|
+ pthread_mutex_unlock(&syme->source_lock);
|
|
|
+ if (more)
|
|
|
+ printf("%d lines not displayed, maybe increase display entries [e]\n", more);
|
|
|
+}
|
|
|
|
|
|
struct dso *kernel_dso;
|
|
|
|
|
@@ -112,6 +355,9 @@ static double sym_weight(const struct sym_entry *sym)
|
|
|
double weight = sym->snap_count;
|
|
|
int counter;
|
|
|
|
|
|
+ if (!display_weighted)
|
|
|
+ return weight;
|
|
|
+
|
|
|
for (counter = 1; counter < nr_counters-1; counter++)
|
|
|
weight *= sym->count[counter];
|
|
|
|
|
@@ -159,7 +405,7 @@ static void rb_insert_active_sym(struct rb_root *tree, struct sym_entry *se)
|
|
|
static void print_sym_table(void)
|
|
|
{
|
|
|
int printed = 0, j;
|
|
|
- int counter;
|
|
|
+ int counter, snap = !display_weighted ? sym_counter : 0;
|
|
|
float samples_per_sec = samples/delay_secs;
|
|
|
float ksamples_per_sec = (samples-userspace_samples)/delay_secs;
|
|
|
float sum_ksamples = 0.0;
|
|
@@ -175,7 +421,7 @@ static void print_sym_table(void)
|
|
|
pthread_mutex_unlock(&active_symbols_lock);
|
|
|
|
|
|
list_for_each_entry_safe_from(syme, n, &active_symbols, node) {
|
|
|
- syme->snap_count = syme->count[0];
|
|
|
+ syme->snap_count = syme->count[snap];
|
|
|
if (syme->snap_count != 0) {
|
|
|
syme->weight = sym_weight(syme);
|
|
|
rb_insert_active_sym(&tmp, syme);
|
|
@@ -195,7 +441,7 @@ static void print_sym_table(void)
|
|
|
samples_per_sec,
|
|
|
100.0 - (100.0*((samples_per_sec-ksamples_per_sec)/samples_per_sec)));
|
|
|
|
|
|
- if (nr_counters == 1) {
|
|
|
+ if (nr_counters == 1 || !display_weighted) {
|
|
|
printf("%Ld", (u64)attrs[0].sample_period);
|
|
|
if (freq)
|
|
|
printf("Hz ");
|
|
@@ -203,7 +449,9 @@ static void print_sym_table(void)
|
|
|
printf(" ");
|
|
|
}
|
|
|
|
|
|
- for (counter = 0; counter < nr_counters; counter++) {
|
|
|
+ if (!display_weighted)
|
|
|
+ printf("%s", event_name(sym_counter));
|
|
|
+ else for (counter = 0; counter < nr_counters; counter++) {
|
|
|
if (counter)
|
|
|
printf("/");
|
|
|
|
|
@@ -228,6 +476,11 @@ static void print_sym_table(void)
|
|
|
|
|
|
printf("------------------------------------------------------------------------------\n\n");
|
|
|
|
|
|
+ if (sym_filter_entry) {
|
|
|
+ show_details(sym_filter_entry);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
if (nr_counters == 1)
|
|
|
printf(" samples pcnt");
|
|
|
else
|
|
@@ -242,13 +495,13 @@ static void print_sym_table(void)
|
|
|
struct symbol *sym = (struct symbol *)(syme + 1);
|
|
|
double pcnt;
|
|
|
|
|
|
- if (++printed > print_entries || syme->snap_count < count_filter)
|
|
|
+ if (++printed > print_entries || (int)syme->snap_count < count_filter)
|
|
|
continue;
|
|
|
|
|
|
pcnt = 100.0 - (100.0 * ((sum_ksamples - syme->snap_count) /
|
|
|
sum_ksamples));
|
|
|
|
|
|
- if (nr_counters == 1)
|
|
|
+ if (nr_counters == 1 || !display_weighted)
|
|
|
printf("%20.2f - ", syme->weight);
|
|
|
else
|
|
|
printf("%9.1f %10ld - ", syme->weight, syme->snap_count);
|
|
@@ -261,19 +514,250 @@ static void print_sym_table(void)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+static void prompt_integer(int *target, const char *msg)
|
|
|
+{
|
|
|
+ char *buf = malloc(0), *p;
|
|
|
+ size_t dummy = 0;
|
|
|
+ int tmp;
|
|
|
+
|
|
|
+ fprintf(stdout, "\n%s: ", msg);
|
|
|
+ if (getline(&buf, &dummy, stdin) < 0)
|
|
|
+ return;
|
|
|
+
|
|
|
+ p = strchr(buf, '\n');
|
|
|
+ if (p)
|
|
|
+ *p = 0;
|
|
|
+
|
|
|
+ p = buf;
|
|
|
+ while(*p) {
|
|
|
+ if (!isdigit(*p))
|
|
|
+ goto out_free;
|
|
|
+ p++;
|
|
|
+ }
|
|
|
+ tmp = strtoul(buf, NULL, 10);
|
|
|
+ *target = tmp;
|
|
|
+out_free:
|
|
|
+ free(buf);
|
|
|
+}
|
|
|
+
|
|
|
+static void prompt_percent(int *target, const char *msg)
|
|
|
+{
|
|
|
+ int tmp = 0;
|
|
|
+
|
|
|
+ prompt_integer(&tmp, msg);
|
|
|
+ if (tmp >= 0 && tmp <= 100)
|
|
|
+ *target = tmp;
|
|
|
+}
|
|
|
+
|
|
|
+static void prompt_symbol(struct sym_entry **target, const char *msg)
|
|
|
+{
|
|
|
+ char *buf = malloc(0), *p;
|
|
|
+ struct sym_entry *syme = *target, *n, *found = NULL;
|
|
|
+ size_t dummy = 0;
|
|
|
+
|
|
|
+ /* zero counters of active symbol */
|
|
|
+ if (syme) {
|
|
|
+ pthread_mutex_lock(&syme->source_lock);
|
|
|
+ __zero_source_counters(syme);
|
|
|
+ *target = NULL;
|
|
|
+ pthread_mutex_unlock(&syme->source_lock);
|
|
|
+ }
|
|
|
+
|
|
|
+ fprintf(stdout, "\n%s: ", msg);
|
|
|
+ if (getline(&buf, &dummy, stdin) < 0)
|
|
|
+ goto out_free;
|
|
|
+
|
|
|
+ p = strchr(buf, '\n');
|
|
|
+ if (p)
|
|
|
+ *p = 0;
|
|
|
+
|
|
|
+ pthread_mutex_lock(&active_symbols_lock);
|
|
|
+ syme = list_entry(active_symbols.next, struct sym_entry, node);
|
|
|
+ pthread_mutex_unlock(&active_symbols_lock);
|
|
|
+
|
|
|
+ list_for_each_entry_safe_from(syme, n, &active_symbols, node) {
|
|
|
+ struct symbol *sym = (struct symbol *)(syme + 1);
|
|
|
+
|
|
|
+ if (!strcmp(buf, sym->name)) {
|
|
|
+ found = syme;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!found) {
|
|
|
+ fprintf(stderr, "Sorry, %s is not active.\n", sym_filter);
|
|
|
+ sleep(1);
|
|
|
+ return;
|
|
|
+ } else
|
|
|
+ parse_source(found);
|
|
|
+
|
|
|
+out_free:
|
|
|
+ free(buf);
|
|
|
+}
|
|
|
+
|
|
|
+static void print_mapped_keys(void)
|
|
|
+{
|
|
|
+ char *name = NULL;
|
|
|
+
|
|
|
+ if (sym_filter_entry) {
|
|
|
+ struct symbol *sym = (struct symbol *)(sym_filter_entry+1);
|
|
|
+ name = sym->name;
|
|
|
+ }
|
|
|
+
|
|
|
+ fprintf(stdout, "\nMapped keys:\n");
|
|
|
+ fprintf(stdout, "\t[d] display refresh delay. \t(%d)\n", delay_secs);
|
|
|
+ fprintf(stdout, "\t[e] display entries (lines). \t(%d)\n", print_entries);
|
|
|
+
|
|
|
+ if (nr_counters > 1)
|
|
|
+ fprintf(stdout, "\t[E] active event counter. \t(%s)\n", event_name(sym_counter));
|
|
|
+
|
|
|
+ fprintf(stdout, "\t[f] profile display filter (count). \t(%d)\n", count_filter);
|
|
|
+
|
|
|
+ if (vmlinux) {
|
|
|
+ fprintf(stdout, "\t[F] annotate display filter (percent). \t(%d%%)\n", sym_pcnt_filter);
|
|
|
+ fprintf(stdout, "\t[s] annotate symbol. \t(%s)\n", name?: "NULL");
|
|
|
+ fprintf(stdout, "\t[S] stop annotation.\n");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (nr_counters > 1)
|
|
|
+ fprintf(stdout, "\t[w] toggle display weighted/count[E]r. \t(%d)\n", display_weighted ? 1 : 0);
|
|
|
+
|
|
|
+ fprintf(stdout, "\t[z] toggle sample zeroing. \t(%d)\n", zero ? 1 : 0);
|
|
|
+ fprintf(stdout, "\t[qQ] quit.\n");
|
|
|
+}
|
|
|
+
|
|
|
+static int key_mapped(int c)
|
|
|
+{
|
|
|
+ switch (c) {
|
|
|
+ case 'd':
|
|
|
+ case 'e':
|
|
|
+ case 'f':
|
|
|
+ case 'z':
|
|
|
+ case 'q':
|
|
|
+ case 'Q':
|
|
|
+ return 1;
|
|
|
+ case 'E':
|
|
|
+ case 'w':
|
|
|
+ return nr_counters > 1 ? 1 : 0;
|
|
|
+ case 'F':
|
|
|
+ case 's':
|
|
|
+ case 'S':
|
|
|
+ return vmlinux ? 1 : 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void handle_keypress(int c)
|
|
|
+{
|
|
|
+ if (!key_mapped(c)) {
|
|
|
+ struct pollfd stdin_poll = { .fd = 0, .events = POLLIN };
|
|
|
+ struct termios tc, save;
|
|
|
+
|
|
|
+ print_mapped_keys();
|
|
|
+ fprintf(stdout, "\nEnter selection, or unmapped key to continue: ");
|
|
|
+ fflush(stdout);
|
|
|
+
|
|
|
+ tcgetattr(0, &save);
|
|
|
+ tc = save;
|
|
|
+ tc.c_lflag &= ~(ICANON | ECHO);
|
|
|
+ tc.c_cc[VMIN] = 0;
|
|
|
+ tc.c_cc[VTIME] = 0;
|
|
|
+ tcsetattr(0, TCSANOW, &tc);
|
|
|
+
|
|
|
+ poll(&stdin_poll, 1, -1);
|
|
|
+ c = getc(stdin);
|
|
|
+
|
|
|
+ tcsetattr(0, TCSAFLUSH, &save);
|
|
|
+ if (!key_mapped(c))
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ switch (c) {
|
|
|
+ case 'd':
|
|
|
+ prompt_integer(&delay_secs, "Enter display delay");
|
|
|
+ break;
|
|
|
+ case 'e':
|
|
|
+ prompt_integer(&print_entries, "Enter display entries (lines)");
|
|
|
+ break;
|
|
|
+ case 'E':
|
|
|
+ if (nr_counters > 1) {
|
|
|
+ int i;
|
|
|
+
|
|
|
+ fprintf(stderr, "\nAvailable events:");
|
|
|
+ for (i = 0; i < nr_counters; i++)
|
|
|
+ fprintf(stderr, "\n\t%d %s", i, event_name(i));
|
|
|
+
|
|
|
+ prompt_integer(&sym_counter, "Enter details event counter");
|
|
|
+
|
|
|
+ if (sym_counter >= nr_counters) {
|
|
|
+ fprintf(stderr, "Sorry, no such event, using %s.\n", event_name(0));
|
|
|
+ sym_counter = 0;
|
|
|
+ sleep(1);
|
|
|
+ }
|
|
|
+ } else sym_counter = 0;
|
|
|
+ break;
|
|
|
+ case 'f':
|
|
|
+ prompt_integer(&count_filter, "Enter display event count filter");
|
|
|
+ break;
|
|
|
+ case 'F':
|
|
|
+ prompt_percent(&sym_pcnt_filter, "Enter details display event filter (percent)");
|
|
|
+ break;
|
|
|
+ case 'q':
|
|
|
+ case 'Q':
|
|
|
+ printf("exiting.\n");
|
|
|
+ exit(0);
|
|
|
+ case 's':
|
|
|
+ prompt_symbol(&sym_filter_entry, "Enter details symbol");
|
|
|
+ break;
|
|
|
+ case 'S':
|
|
|
+ if (!sym_filter_entry)
|
|
|
+ break;
|
|
|
+ else {
|
|
|
+ struct sym_entry *syme = sym_filter_entry;
|
|
|
+
|
|
|
+ pthread_mutex_lock(&syme->source_lock);
|
|
|
+ sym_filter_entry = NULL;
|
|
|
+ __zero_source_counters(syme);
|
|
|
+ pthread_mutex_unlock(&syme->source_lock);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case 'w':
|
|
|
+ display_weighted = ~display_weighted;
|
|
|
+ break;
|
|
|
+ case 'z':
|
|
|
+ zero = ~zero;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
static void *display_thread(void *arg __used)
|
|
|
{
|
|
|
struct pollfd stdin_poll = { .fd = 0, .events = POLLIN };
|
|
|
- int delay_msecs = delay_secs * 1000;
|
|
|
+ struct termios tc, save;
|
|
|
+ int delay_msecs, c;
|
|
|
+
|
|
|
+ tcgetattr(0, &save);
|
|
|
+ tc = save;
|
|
|
+ tc.c_lflag &= ~(ICANON | ECHO);
|
|
|
+ tc.c_cc[VMIN] = 0;
|
|
|
+ tc.c_cc[VTIME] = 0;
|
|
|
|
|
|
- printf("PerfTop refresh period: %d seconds\n", delay_secs);
|
|
|
+repeat:
|
|
|
+ delay_msecs = delay_secs * 1000;
|
|
|
+ tcsetattr(0, TCSANOW, &tc);
|
|
|
+ /* trash return*/
|
|
|
+ getc(stdin);
|
|
|
|
|
|
do {
|
|
|
print_sym_table();
|
|
|
} while (!poll(&stdin_poll, 1, delay_msecs) == 1);
|
|
|
|
|
|
- printf("key pressed - exiting.\n");
|
|
|
- exit(0);
|
|
|
+ c = getc(stdin);
|
|
|
+ tcsetattr(0, TCSAFLUSH, &save);
|
|
|
+
|
|
|
+ handle_keypress(c);
|
|
|
+ goto repeat;
|
|
|
|
|
|
return NULL;
|
|
|
}
|
|
@@ -293,7 +777,6 @@ static const char *skip_symbols[] = {
|
|
|
|
|
|
static int symbol_filter(struct dso *self, struct symbol *sym)
|
|
|
{
|
|
|
- static int filter_match;
|
|
|
struct sym_entry *syme;
|
|
|
const char *name = sym->name;
|
|
|
int i;
|
|
@@ -315,6 +798,10 @@ static int symbol_filter(struct dso *self, struct symbol *sym)
|
|
|
return 1;
|
|
|
|
|
|
syme = dso__sym_priv(self, sym);
|
|
|
+ pthread_mutex_init(&syme->source_lock, NULL);
|
|
|
+ if (!sym_filter_entry && sym_filter && !strcmp(name, sym_filter))
|
|
|
+ sym_filter_entry = syme;
|
|
|
+
|
|
|
for (i = 0; skip_symbols[i]; i++) {
|
|
|
if (!strcmp(skip_symbols[i], name)) {
|
|
|
syme->skip = 1;
|
|
@@ -322,29 +809,6 @@ static int symbol_filter(struct dso *self, struct symbol *sym)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if (filter_match == 1) {
|
|
|
- filter_end = sym->start;
|
|
|
- filter_match = -1;
|
|
|
- if (filter_end - filter_start > 10000) {
|
|
|
- fprintf(stderr,
|
|
|
- "hm, too large filter symbol <%s> - skipping.\n",
|
|
|
- sym_filter);
|
|
|
- fprintf(stderr, "symbol filter start: %016lx\n",
|
|
|
- filter_start);
|
|
|
- fprintf(stderr, " end: %016lx\n",
|
|
|
- filter_end);
|
|
|
- filter_end = filter_start = 0;
|
|
|
- sym_filter = NULL;
|
|
|
- sleep(1);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (filter_match == 0 && sym_filter && !strcmp(name, sym_filter)) {
|
|
|
- filter_match = 1;
|
|
|
- filter_start = sym->start;
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -380,8 +844,6 @@ out_delete_dso:
|
|
|
return -1;
|
|
|
}
|
|
|
|
|
|
-#define TRACE_COUNT 3
|
|
|
-
|
|
|
/*
|
|
|
* Binary search in the histogram table and record the hit:
|
|
|
*/
|
|
@@ -394,6 +856,7 @@ static void record_ip(u64 ip, int counter)
|
|
|
|
|
|
if (!syme->skip) {
|
|
|
syme->count[counter]++;
|
|
|
+ record_precise_ip(syme, counter, ip);
|
|
|
pthread_mutex_lock(&active_symbols_lock);
|
|
|
if (list_empty(&syme->node) || !syme->node.next)
|
|
|
__list_insert_active_sym(syme);
|
|
@@ -690,8 +1153,8 @@ static const struct option options[] = {
|
|
|
"put the counters into a counter group"),
|
|
|
OPT_BOOLEAN('i', "inherit", &inherit,
|
|
|
"child tasks inherit counters"),
|
|
|
- OPT_STRING('s', "sym-filter", &sym_filter, "pattern",
|
|
|
- "only display symbols matchig this pattern"),
|
|
|
+ OPT_STRING('s', "sym-annotate", &sym_filter, "symbol name",
|
|
|
+ "symbol to annotate - requires -k option"),
|
|
|
OPT_BOOLEAN('z', "zero", &zero,
|
|
|
"zero history across updates"),
|
|
|
OPT_INTEGER('F', "freq", &freq,
|
|
@@ -734,6 +1197,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)
|
|
|
delay_secs = 1;
|
|
|
|
|
|
parse_symbols();
|
|
|
+ parse_source(sym_filter_entry);
|
|
|
|
|
|
/*
|
|
|
* Fill in the ones not specifically initialized via -c:
|