aboutsummaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/perf/Makefile10
-rw-r--r--tools/perf/builtin-annotate.c262
-rw-r--r--tools/perf/builtin-record.c163
-rw-r--r--tools/perf/builtin-report.c439
-rw-r--r--tools/perf/builtin-stat.c308
-rw-r--r--tools/perf/builtin-top.c24
-rw-r--r--tools/perf/perf.h7
-rw-r--r--tools/perf/types.h17
-rw-r--r--tools/perf/util/ctype.c17
-rw-r--r--tools/perf/util/parse-events.c14
-rw-r--r--tools/perf/util/string.c2
-rw-r--r--tools/perf/util/string.h4
-rw-r--r--tools/perf/util/symbol.c20
-rw-r--r--tools/perf/util/symbol.h16
-rw-r--r--tools/perf/util/util.h18
15 files changed, 992 insertions, 329 deletions
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index 0cbd5d6874e..36d7eef4991 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -157,10 +157,15 @@ uname_R := $(shell sh -c 'uname -r 2>/dev/null || echo not')
uname_P := $(shell sh -c 'uname -p 2>/dev/null || echo not')
uname_V := $(shell sh -c 'uname -v 2>/dev/null || echo not')
+# If we're on a 64-bit kernel, use -m64
+ifneq ($(patsubst %64,%,$(uname_M)),$(uname_M))
+ M64 := -m64
+endif
+
# CFLAGS and LDFLAGS are for the users to override from the command line.
-CFLAGS = -ggdb3 -Wall -Werror -Wstrict-prototypes -Wmissing-declarations -Wmissing-prototypes -std=gnu99 -Wdeclaration-after-statement -O6
-LDFLAGS = -lpthread -lrt -lelf
+CFLAGS = $(M64) -ggdb3 -Wall -Wstrict-prototypes -Wmissing-declarations -Wmissing-prototypes -std=gnu99 -Wdeclaration-after-statement -Werror -O6
+LDFLAGS = -lpthread -lrt -lelf -lm
ALL_CFLAGS = $(CFLAGS)
ALL_LDFLAGS = $(LDFLAGS)
STRIP ?= strip
@@ -285,6 +290,7 @@ LIB_FILE=libperf.a
LIB_H += ../../include/linux/perf_counter.h
LIB_H += perf.h
+LIB_H += types.h
LIB_H += util/list.h
LIB_H += util/rbtree.h
LIB_H += util/levenshtein.h
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index b1ed5f766cb..7e58e3ad150 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -25,6 +25,10 @@
#define SHOW_USER 2
#define SHOW_HV 4
+#define MIN_GREEN 0.5
+#define MIN_RED 5.0
+
+
static char const *input_name = "perf.data";
static char *vmlinux = "vmlinux";
@@ -39,40 +43,42 @@ static int dump_trace = 0;
static int verbose;
+static int print_line;
+
static unsigned long page_size;
static unsigned long mmap_window = 32;
struct ip_event {
struct perf_event_header header;
- __u64 ip;
- __u32 pid, tid;
+ u64 ip;
+ u32 pid, tid;
};
struct mmap_event {
struct perf_event_header header;
- __u32 pid, tid;
- __u64 start;
- __u64 len;
- __u64 pgoff;
+ u32 pid, tid;
+ u64 start;
+ u64 len;
+ u64 pgoff;
char filename[PATH_MAX];
};
struct comm_event {
struct perf_event_header header;
- __u32 pid, tid;
+ u32 pid, tid;
char comm[16];
};
struct fork_event {
struct perf_event_header header;
- __u32 pid, ppid;
+ u32 pid, ppid;
};
struct period_event {
struct perf_event_header header;
- __u64 time;
- __u64 id;
- __u64 sample_period;
+ u64 time;
+ u64 id;
+ u64 sample_period;
};
typedef union event_union {
@@ -84,6 +90,13 @@ typedef union event_union {
struct period_event period;
} event_t;
+
+struct sym_ext {
+ struct rb_node node;
+ double percent;
+ char *path;
+};
+
static LIST_HEAD(dsos);
static struct dso *kernel_dso;
static struct dso *vdso;
@@ -145,7 +158,7 @@ static void dsos__fprintf(FILE *fp)
dso__fprintf(pos, fp);
}
-static struct symbol *vdso__find_symbol(struct dso *dso, __u64 ip)
+static struct symbol *vdso__find_symbol(struct dso *dso, u64 ip)
{
return dso__find_symbol(kernel_dso, ip);
}
@@ -178,19 +191,19 @@ static int load_kernel(void)
struct map {
struct list_head node;
- __u64 start;
- __u64 end;
- __u64 pgoff;
- __u64 (*map_ip)(struct map *, __u64);
+ u64 start;
+ u64 end;
+ u64 pgoff;
+ u64 (*map_ip)(struct map *, u64);
struct dso *dso;
};
-static __u64 map__map_ip(struct map *map, __u64 ip)
+static u64 map__map_ip(struct map *map, u64 ip)
{
return ip - map->start + map->pgoff;
}
-static __u64 vdso__map_ip(struct map *map, __u64 ip)
+static u64 vdso__map_ip(struct map *map, u64 ip)
{
return ip;
}
@@ -373,7 +386,7 @@ static int thread__fork(struct thread *self, struct thread *parent)
return 0;
}
-static struct map *thread__find_map(struct thread *self, __u64 ip)
+static struct map *thread__find_map(struct thread *self, u64 ip)
{
struct map *pos;
@@ -414,7 +427,7 @@ struct hist_entry {
struct map *map;
struct dso *dso;
struct symbol *sym;
- __u64 ip;
+ u64 ip;
char level;
uint32_t count;
@@ -519,7 +532,7 @@ sort__dso_print(FILE *fp, struct hist_entry *self)
if (self->dso)
return fprintf(fp, "%-25s", self->dso->name);
- return fprintf(fp, "%016llx ", (__u64)self->ip);
+ return fprintf(fp, "%016llx ", (u64)self->ip);
}
static struct sort_entry sort_dso = {
@@ -533,7 +546,7 @@ static struct sort_entry sort_dso = {
static int64_t
sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
{
- __u64 ip_l, ip_r;
+ u64 ip_l, ip_r;
if (left->sym == right->sym)
return 0;
@@ -550,13 +563,13 @@ sort__sym_print(FILE *fp, struct hist_entry *self)
size_t ret = 0;
if (verbose)
- ret += fprintf(fp, "%#018llx ", (__u64)self->ip);
+ ret += fprintf(fp, "%#018llx ", (u64)self->ip);
if (self->sym) {
ret += fprintf(fp, "[%c] %s",
self->dso == kernel_dso ? 'k' : '.', self->sym->name);
} else {
- ret += fprintf(fp, "%#016llx", (__u64)self->ip);
+ ret += fprintf(fp, "%#016llx", (u64)self->ip);
}
return ret;
@@ -647,7 +660,7 @@ hist_entry__collapse(struct hist_entry *left, struct hist_entry *right)
/*
* collect histogram counts
*/
-static void hist_hit(struct hist_entry *he, __u64 ip)
+static void hist_hit(struct hist_entry *he, u64 ip)
{
unsigned int sym_size, offset;
struct symbol *sym = he->sym;
@@ -676,7 +689,7 @@ static void hist_hit(struct hist_entry *he, __u64 ip)
static int
hist_entry__add(struct thread *thread, struct map *map, struct dso *dso,
- struct symbol *sym, __u64 ip, char level)
+ struct symbol *sym, u64 ip, char level)
{
struct rb_node **p = &hist.rb_node;
struct rb_node *parent = NULL;
@@ -848,7 +861,7 @@ process_overflow_event(event_t *event, unsigned long offset, unsigned long head)
int show = 0;
struct dso *dso = NULL;
struct thread *thread = threads__findnew(event->ip.pid);
- __u64 ip = event->ip.ip;
+ u64 ip = event->ip.ip;
struct map *map = NULL;
dprintf("%p [%p]: PERF_EVENT (IP, %d): %d: %p\n",
@@ -1030,13 +1043,33 @@ process_event(event_t *event, unsigned long offset, unsigned long head)
return 0;
}
+static char *get_color(double percent)
+{
+ char *color = PERF_COLOR_NORMAL;
+
+ /*
+ * We color high-overhead entries in red, mid-overhead
+ * entries in green - and keep the low overhead places
+ * normal:
+ */
+ if (percent >= MIN_RED)
+ color = PERF_COLOR_RED;
+ else {
+ if (percent > MIN_GREEN)
+ color = PERF_COLOR_GREEN;
+ }
+ return color;
+}
+
static int
-parse_line(FILE *file, struct symbol *sym, __u64 start, __u64 len)
+parse_line(FILE *file, struct symbol *sym, u64 start, u64 len)
{
char *line = NULL, *tmp, *tmp2;
+ static const char *prev_line;
+ static const char *prev_color;
unsigned int offset;
size_t line_len;
- __u64 line_ip;
+ u64 line_ip;
int ret;
char *c;
@@ -1073,27 +1106,36 @@ parse_line(FILE *file, struct symbol *sym, __u64 start, __u64 len)
}
if (line_ip != -1) {
+ const char *path = NULL;
unsigned int hits = 0;
double percent = 0.0;
- char *color = PERF_COLOR_NORMAL;
+ char *color;
+ struct sym_ext *sym_ext = sym->priv;
offset = line_ip - start;
if (offset < len)
hits = sym->hist[offset];
- if (sym->hist_sum)
+ if (offset < len && sym_ext) {
+ path = sym_ext[offset].path;
+ percent = sym_ext[offset].percent;
+ } else if (sym->hist_sum)
percent = 100.0 * hits / sym->hist_sum;
+ color = get_color(percent);
+
/*
- * We color high-overhead entries in red, mid-overhead
- * entries in green - and keep the low overhead places
- * normal:
+ * Also color the filename and line if needed, with
+ * the same color than the percentage. Don't print it
+ * twice for close colored ip with the same filename:line
*/
- if (percent >= 5.0)
- color = PERF_COLOR_RED;
- else {
- if (percent > 0.5)
- color = PERF_COLOR_GREEN;
+ if (path) {
+ if (!prev_line || strcmp(prev_line, path)
+ || color != prev_color) {
+ color_fprintf(stdout, color, " %s", path);
+ prev_line = path;
+ prev_color = color;
+ }
}
color_fprintf(stdout, color, " %7.2f", percent);
@@ -1109,10 +1151,125 @@ parse_line(FILE *file, struct symbol *sym, __u64 start, __u64 len)
return 0;
}
+static struct rb_root root_sym_ext;
+
+static void insert_source_line(struct sym_ext *sym_ext)
+{
+ struct sym_ext *iter;
+ struct rb_node **p = &root_sym_ext.rb_node;
+ struct rb_node *parent = NULL;
+
+ while (*p != NULL) {
+ parent = *p;
+ iter = rb_entry(parent, struct sym_ext, node);
+
+ if (sym_ext->percent > iter->percent)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+
+ rb_link_node(&sym_ext->node, parent, p);
+ rb_insert_color(&sym_ext->node, &root_sym_ext);
+}
+
+static void free_source_line(struct symbol *sym, int len)
+{
+ struct sym_ext *sym_ext = sym->priv;
+ int i;
+
+ if (!sym_ext)
+ return;
+
+ for (i = 0; i < len; i++)
+ free(sym_ext[i].path);
+ free(sym_ext);
+
+ sym->priv = NULL;
+ root_sym_ext = RB_ROOT;
+}
+
+/* Get the filename:line for the colored entries */
+static void
+get_source_line(struct symbol *sym, u64 start, int len, char *filename)
+{
+ int i;
+ char cmd[PATH_MAX * 2];
+ struct sym_ext *sym_ext;
+
+ if (!sym->hist_sum)
+ return;
+
+ sym->priv = calloc(len, sizeof(struct sym_ext));
+ if (!sym->priv)
+ return;
+
+ sym_ext = sym->priv;
+
+ for (i = 0; i < len; i++) {
+ char *path = NULL;
+ size_t line_len;
+ u64 offset;
+ FILE *fp;
+
+ sym_ext[i].percent = 100.0 * sym->hist[i] / sym->hist_sum;
+ if (sym_ext[i].percent <= 0.5)
+ continue;
+
+ offset = start + i;
+ sprintf(cmd, "addr2line -e %s %016llx", filename, offset);
+ fp = popen(cmd, "r");
+ if (!fp)
+ continue;
+
+ if (getline(&path, &line_len, fp) < 0 || !line_len)
+ goto next;
+
+ sym_ext[i].path = malloc(sizeof(char) * line_len + 1);
+ if (!sym_ext[i].path)
+ goto next;
+
+ strcpy(sym_ext[i].path, path);
+ insert_source_line(&sym_ext[i]);
+
+ next:
+ pclose(fp);
+ }
+}
+
+static void print_summary(char *filename)
+{
+ struct sym_ext *sym_ext;
+ struct rb_node *node;
+
+ printf("\nSorted summary for file %s\n", filename);
+ printf("----------------------------------------------\n\n");
+
+ if (RB_EMPTY_ROOT(&root_sym_ext)) {
+ printf(" Nothing higher than %1.1f%%\n", MIN_GREEN);
+ return;
+ }
+
+ node = rb_first(&root_sym_ext);
+ while (node) {
+ double percent;
+ char *color;
+ char *path;
+
+ sym_ext = rb_entry(node, struct sym_ext, node);
+ percent = sym_ext->percent;
+ color = get_color(percent);
+ path = sym_ext->path;
+
+ color_fprintf(stdout, color, " %7.2f %s", percent, path);
+ node = rb_next(node);
+ }
+}
+
static void annotate_sym(struct dso *dso, struct symbol *sym)
{
char *filename = dso->name;
- __u64 start, end, len;
+ u64 start, end, len;
char command[PATH_MAX*2];
FILE *file;
@@ -1121,13 +1278,6 @@ static void annotate_sym(struct dso *dso, struct symbol *sym)
if (dso == kernel_dso)
filename = vmlinux;
- printf("\n------------------------------------------------\n");
- printf(" Percent | Source code & Disassembly of %s\n", filename);
- printf("------------------------------------------------\n");
-
- if (verbose >= 2)
- printf("annotating [%p] %30s : [%p] %30s\n", dso, dso->name, sym, sym->name);
-
start = sym->obj_start;
if (!start)
start = sym->start;
@@ -1135,7 +1285,19 @@ static void annotate_sym(struct dso *dso, struct symbol *sym)
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", (__u64)start, (__u64)end, filename);
+ if (print_line) {
+ get_source_line(sym, start, len, filename);
+ print_summary(filename);
+ }
+
+ printf("\n\n------------------------------------------------\n");
+ printf(" Percent | Source code & Disassembly of %s\n", filename);
+ printf("------------------------------------------------\n");
+
+ if (verbose >= 2)
+ printf("annotating [%p] %30s : [%p] %30s\n", dso, dso->name, sym, sym->name);
+
+ sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s", (u64)start, (u64)end, filename);
if (verbose >= 3)
printf("doing: %s\n", command);
@@ -1150,6 +1312,8 @@ static void annotate_sym(struct dso *dso, struct symbol *sym)
}
pclose(file);
+ if (print_line)
+ free_source_line(sym, len);
}
static void find_annotations(void)
@@ -1308,6 +1472,8 @@ static const struct option options[] = {
OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
"dump raw trace in ASCII"),
OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"),
+ OPT_BOOLEAN('l', "print-line", &print_line,
+ "print matching source lines (may be slow)"),
OPT_END()
};
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 0f5771f615d..d7ebbd75754 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -37,33 +37,37 @@ static pid_t target_pid = -1;
static int inherit = 1;
static int force = 0;
static int append_file = 0;
+static int call_graph = 0;
static int verbose = 0;
static long samples;
static struct timeval last_read;
static struct timeval this_read;
-static __u64 bytes_written;
+static u64 bytes_written;
static struct pollfd event_array[MAX_NR_CPUS * MAX_COUNTERS];
static int nr_poll;
static int nr_cpu;
+static int file_new = 1;
+static struct perf_file_header file_header;
+
struct mmap_event {
struct perf_event_header header;
- __u32 pid;
- __u32 tid;
- __u64 start;
- __u64 len;
- __u64 pgoff;
+ u32 pid;
+ u32 tid;
+ u64 start;
+ u64 len;
+ u64 pgoff;
char filename[PATH_MAX];
};
struct comm_event {
struct perf_event_header header;
- __u32 pid;
- __u32 tid;
+ u32 pid;
+ u32 tid;
char comm[16];
};
@@ -77,10 +81,10 @@ struct mmap_data {
static struct mmap_data mmap_array[MAX_NR_CPUS][MAX_COUNTERS];
-static unsigned int mmap_read_head(struct mmap_data *md)
+static unsigned long mmap_read_head(struct mmap_data *md)
{
struct perf_counter_mmap_page *pc = md->base;
- int head;
+ long head;
head = pc->data_head;
rmb();
@@ -88,6 +92,32 @@ static unsigned int mmap_read_head(struct mmap_data *md)
return head;
}
+static void mmap_write_tail(struct mmap_data *md, unsigned long tail)
+{
+ struct perf_counter_mmap_page *pc = md->base;
+
+ /*
+ * ensure all reads are done before we write the tail out.
+ */
+ /* mb(); */
+ pc->data_tail = tail;
+}
+
+static void write_output(void *buf, size_t size)
+{
+ while (size) {
+ int ret = write(output, buf, size);
+
+ if (ret < 0)
+ die("failed to write");
+
+ size -= ret;
+ buf += ret;
+
+ bytes_written += ret;
+ }
+}
+
static void mmap_read(struct mmap_data *md)
{
unsigned int head = mmap_read_head(md);
@@ -108,7 +138,7 @@ static void mmap_read(struct mmap_data *md)
* In either case, truncate and restart at head.
*/
diff = head - old;
- if (diff > md->mask / 2 || diff < 0) {
+ if (diff < 0) {
struct timeval iv;
unsigned long msecs;
@@ -136,36 +166,17 @@ static void mmap_read(struct mmap_data *md)
size = md->mask + 1 - (old & md->mask);
old += size;
- while (size) {
- int ret = write(output, buf, size);
-
- if (ret < 0)
- die("failed to write");
-
- size -= ret;
- buf += ret;
-
- bytes_written += ret;
- }
+ write_output(buf, size);
}
buf = &data[old & md->mask];
size = head - old;
old += size;
- while (size) {
- int ret = write(output, buf, size);
-
- if (ret < 0)
- die("failed to write");
-
- size -= ret;
- buf += ret;
-
- bytes_written += ret;
- }
+ write_output(buf, size);
md->prev = old;
+ mmap_write_tail(md, old);
}
static volatile int done = 0;
@@ -191,7 +202,7 @@ static void pid_synthesize_comm_event(pid_t pid, int full)
struct comm_event comm_ev;
char filename[PATH_MAX];
char bf[BUFSIZ];
- int fd, ret;
+ int fd;
size_t size;
char *field, *sep;
DIR *tasks;
@@ -201,8 +212,12 @@ static void pid_synthesize_comm_event(pid_t pid, int full)
fd = open(filename, O_RDONLY);
if (fd < 0) {
- fprintf(stderr, "couldn't open %s\n", filename);
- exit(EXIT_FAILURE);
+ /*
+ * We raced with a task exiting - just return:
+ */
+ if (verbose)
+ fprintf(stderr, "couldn't open %s\n", filename);
+ return;
}
if (read(fd, bf, sizeof(bf)) < 0) {
fprintf(stderr, "couldn't read %s\n", filename);
@@ -223,17 +238,13 @@ static void pid_synthesize_comm_event(pid_t pid, int full)
comm_ev.pid = pid;
comm_ev.header.type = PERF_EVENT_COMM;
- size = ALIGN(size, sizeof(__u64));
+ size = ALIGN(size, sizeof(u64));
comm_ev.header.size = sizeof(comm_ev) - (sizeof(comm_ev.comm) - size);
if (!full) {
comm_ev.tid = pid;
- ret = write(output, &comm_ev, comm_ev.header.size);
- if (ret < 0) {
- perror("failed to write");
- exit(-1);
- }
+ write_output(&comm_ev, comm_ev.header.size);
return;
}
@@ -248,11 +259,7 @@ static void pid_synthesize_comm_event(pid_t pid, int full)
comm_ev.tid = pid;
- ret = write(output, &comm_ev, comm_ev.header.size);
- if (ret < 0) {
- perror("failed to write");
- exit(-1);
- }
+ write_output(&comm_ev, comm_ev.header.size);
}
closedir(tasks);
return;
@@ -272,8 +279,12 @@ static void pid_synthesize_mmap_samples(pid_t pid)
fp = fopen(filename, "r");
if (fp == NULL) {
- fprintf(stderr, "couldn't open %s\n", filename);
- exit(EXIT_FAILURE);
+ /*
+ * We raced with a task exiting - just return:
+ */
+ if (verbose)
+ fprintf(stderr, "couldn't open %s\n", filename);
+ return;
}
while (1) {
char bf[BUFSIZ], *pbf = bf;
@@ -304,17 +315,14 @@ static void pid_synthesize_mmap_samples(pid_t pid)
size = strlen(execname);
execname[size - 1] = '\0'; /* Remove \n */
memcpy(mmap_ev.filename, execname, size);
- size = ALIGN(size, sizeof(__u64));
+ size = ALIGN(size, sizeof(u64));
mmap_ev.len -= mmap_ev.start;
mmap_ev.header.size = (sizeof(mmap_ev) -
(sizeof(mmap_ev.filename) - size));
mmap_ev.pid = pid;
mmap_ev.tid = pid;
- if (write(output, &mmap_ev, mmap_ev.header.size) < 0) {
- perror("failed to write");
- exit(-1);
- }
+ write_output(&mmap_ev, mmap_ev.header.size);
}
}
@@ -351,11 +359,25 @@ static void create_counter(int counter, int cpu, pid_t pid)
int track = 1;
attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID;
+
if (freq) {
attr->sample_type |= PERF_SAMPLE_PERIOD;
attr->freq = 1;
attr->sample_freq = freq;
}
+
+ if (call_graph)
+ attr->sample_type |= PERF_SAMPLE_CALLCHAIN;
+
+ if (file_new) {
+ file_header.sample_type = attr->sample_type;
+ } else {
+ if (file_header.sample_type != attr->sample_type) {
+ fprintf(stderr, "incompatible append\n");
+ exit(-1);
+ }
+ }
+
attr->mmap = track;
attr->comm = track;
attr->inherit = (cpu < 0) && inherit;
@@ -410,7 +432,7 @@ try_again:
mmap_array[nr_cpu][counter].prev = 0;
mmap_array[nr_cpu][counter].mask = mmap_pages*page_size - 1;
mmap_array[nr_cpu][counter].base = mmap(NULL, (mmap_pages+1)*page_size,
- PROT_READ, MAP_SHARED, fd[nr_cpu][counter], 0);
+ PROT_READ|PROT_WRITE, MAP_SHARED, fd[nr_cpu][counter], 0);
if (mmap_array[nr_cpu][counter].base == MAP_FAILED) {
error("failed to mmap with %d (%s)\n", errno, strerror(errno));
exit(-1);
@@ -435,6 +457,14 @@ static void open_counters(int cpu, pid_t pid)
nr_cpu++;
}
+static void atexit_header(void)
+{
+ file_header.data_size += bytes_written;
+
+ if (pwrite(output, &file_header, sizeof(file_header), 0) == -1)
+ perror("failed to write on file headers");
+}
+
static int __cmd_record(int argc, const char **argv)
{
int i, counter;
@@ -448,6 +478,10 @@ static int __cmd_record(int argc, const char **argv)
assert(nr_cpus <= MAX_NR_CPUS);
assert(nr_cpus >= 0);
+ atexit(sig_atexit);
+ signal(SIGCHLD, sig_handler);
+ signal(SIGINT, sig_handler);
+
if (!stat(output_name, &st) && !force && !append_file) {
fprintf(stderr, "Error, output file %s exists, use -A to append or -f to overwrite.\n",
output_name);
@@ -456,7 +490,7 @@ static int __cmd_record(int argc, const char **argv)
flags = O_CREAT|O_RDWR;
if (append_file)
- flags |= O_APPEND;
+ file_new = 0;
else
flags |= O_TRUNC;
@@ -466,15 +500,22 @@ static int __cmd_record(int argc, const char **argv)
exit(-1);
}
+ if (!file_new) {
+ if (read(output, &file_header, sizeof(file_header)) == -1) {
+ perror("failed to read file headers");
+ exit(-1);
+ }
+
+ lseek(output, file_header.data_size, SEEK_CUR);
+ }
+
+ atexit(atexit_header);
+
if (!system_wide) {
open_counters(-1, target_pid != -1 ? target_pid : getpid());
} else for (i = 0; i < nr_cpus; i++)
open_counters(i, target_pid);
- atexit(sig_atexit);
- signal(SIGCHLD, sig_handler);
- signal(SIGINT, sig_handler);
-
if (target_pid == -1 && argc) {
pid = fork();
if (pid < 0)
@@ -555,6 +596,8 @@ static const struct option options[] = {
"profile at this frequency"),
OPT_INTEGER('m', "mmap-pages", &mmap_pages,
"number of mmap data pages"),
+ OPT_BOOLEAN('g', "call-graph", &call_graph,
+ "do call-graph (stack chain/backtrace) recording"),
OPT_BOOLEAN('v', "verbose", &verbose,
"be more verbose (show counter open errors, etc)"),
OPT_END()
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 82fa93b4db9..5eb5566f0c9 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -36,45 +36,65 @@ static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV;
static int dump_trace = 0;
#define dprintf(x...) do { if (dump_trace) printf(x); } while (0)
+#define cdprintf(x...) do { if (dump_trace) color_fprintf(stdout, color, x); } while (0)
static int verbose;
+#define eprintf(x...) do { if (verbose) fprintf(stderr, x); } while (0)
+
static int full_paths;
static unsigned long page_size;
static unsigned long mmap_window = 32;
+static char default_parent_pattern[] = "^sys_|^do_page_fault";
+static char *parent_pattern = default_parent_pattern;
+static regex_t parent_regex;
+
+static int exclude_other = 1;
+
struct ip_event {
struct perf_event_header header;
- __u64 ip;
- __u32 pid, tid;
- __u64 period;
+ u64 ip;
+ u32 pid, tid;
+ unsigned char __more_data[];
+};
+
+struct ip_callchain {
+ u64 nr;
+ u64 ips[0];
};
struct mmap_event {
struct perf_event_header header;
- __u32 pid, tid;
- __u64 start;
- __u64 len;
- __u64 pgoff;
+ u32 pid, tid;
+ u64 start;
+ u64 len;
+ u64 pgoff;
char filename[PATH_MAX];
};
struct comm_event {
struct perf_event_header header;
- __u32 pid, tid;
+ u32 pid, tid;
char comm[16];
};
struct fork_event {
struct perf_event_header header;
- __u32 pid, ppid;
+ u32 pid, ppid;
};
struct period_event {
struct perf_event_header header;
- __u64 time;
- __u64 id;
- __u64 sample_period;
+ u64 time;
+ u64 id;
+ u64 sample_period;
+};
+
+struct lost_event {
+ struct perf_event_header header;
+ u64 id;
+ u64 lost;
};
typedef union event_union {
@@ -84,6 +104,7 @@ typedef union event_union {
struct comm_event comm;
struct fork_event fork;
struct period_event period;
+ struct lost_event lost;
} event_t;
static LIST_HEAD(dsos);
@@ -119,15 +140,11 @@ static struct dso *dsos__findnew(const char *name)
nr = dso__load(dso, NULL, verbose);
if (nr < 0) {
- if (verbose)
- fprintf(stderr, "Failed to open: %s\n", name);
+ eprintf("Failed to open: %s\n", name);
goto out_delete_dso;
}
- if (!nr && verbose) {
- fprintf(stderr,
- "No symbols found in: %s, maybe install a debug package?\n",
- name);
- }
+ if (!nr)
+ eprintf("No symbols found in: %s, maybe install a debug package?\n", name);
dsos__add(dso);
@@ -146,7 +163,7 @@ static void dsos__fprintf(FILE *fp)
dso__fprintf(pos, fp);
}
-static struct symbol *vdso__find_symbol(struct dso *dso, __u64 ip)
+static struct symbol *vdso__find_symbol(struct dso *dso, u64 ip)
{
return dso__find_symbol(kernel_dso, ip);
}
@@ -193,19 +210,19 @@ static int strcommon(const char *pathname)
struct map {
struct list_head node;
- __u64 start;
- __u64 end;
- __u64 pgoff;
- __u64 (*map_ip)(struct map *, __u64);
+ u64 start;
+ u64 end;
+ u64 pgoff;
+ u64 (*map_ip)(struct map *, u64);
struct dso *dso;
};
-static __u64 map__map_ip(struct map *map, __u64 ip)
+static u64 map__map_ip(struct map *map, u64 ip)
{
return ip - map->start + map->pgoff;
}
-static __u64 vdso__map_ip(struct map *map, __u64 ip)
+static u64 vdso__map_ip(struct map *map, u64 ip)
{
return ip;
}
@@ -412,7 +429,7 @@ static int thread__fork(struct thread *self, struct thread *parent)
return 0;
}
-static struct map *thread__find_map(struct thread *self, __u64 ip)
+static struct map *thread__find_map(struct thread *self, u64 ip)
{
struct map *pos;
@@ -453,10 +470,11 @@ struct hist_entry {
struct map *map;
struct dso *dso;
struct symbol *sym;
- __u64 ip;
+ struct symbol *parent;
+ u64 ip;
char level;
- __u64 count;
+ u64 count;
};
/*
@@ -473,6 +491,16 @@ struct sort_entry {
size_t (*print)(FILE *fp, struct hist_entry *);
};
+static int64_t cmp_null(void *l, void *r)
+{
+ if (!l && !r)
+ return 0;
+ else if (!l)
+ return -1;
+ else
+ return 1;
+}
+
/* --sort pid */
static int64_t
@@ -507,14 +535,8 @@ sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)
char *comm_l = left->thread->comm;
char *comm_r = right->thread->comm;
- if (!comm_l || !comm_r) {
- if (!comm_l && !comm_r)
- return 0;
- else if (!comm_l)
- return -1;
- else
- return 1;
- }
+ if (!comm_l || !comm_r)
+ return cmp_null(comm_l, comm_r);
return strcmp(comm_l, comm_r);
}
@@ -540,14 +562,8 @@ sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
struct dso *dso_l = left->dso;
struct dso *dso_r = right->dso;
- if (!dso_l || !dso_r) {
- if (!dso_l && !dso_r)
- return 0;
- else if (!dso_l)
- return -1;
- else
- return 1;
- }
+ if (!dso_l || !dso_r)
+ return cmp_null(dso_l, dso_r);
return strcmp(dso_l->name, dso_r->name);
}
@@ -558,7 +574,7 @@ sort__dso_print(FILE *fp, struct hist_entry *self)
if (self->dso)
return fprintf(fp, "%-25s", self->dso->name);
- return fprintf(fp, "%016llx ", (__u64)self->ip);
+ return fprintf(fp, "%016llx ", (u64)self->ip);
}
static struct sort_entry sort_dso = {
@@ -572,7 +588,7 @@ static struct sort_entry sort_dso = {
static int64_t
sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
{
- __u64 ip_l, ip_r;
+ u64 ip_l, ip_r;
if (left->sym == right->sym)
return 0;
@@ -589,13 +605,13 @@ sort__sym_print(FILE *fp, struct hist_entry *self)
size_t ret = 0;
if (verbose)
- ret += fprintf(fp, "%#018llx ", (__u64)self->ip);
+ ret += fprintf(fp, "%#018llx ", (u64)self->ip);
if (self->sym) {
ret += fprintf(fp, "[%c] %s",
self->dso == kernel_dso ? 'k' : '.', self->sym->name);
} else {
- ret += fprintf(fp, "%#016llx", (__u64)self->ip);
+ ret += fprintf(fp, "%#016llx", (u64)self->ip);
}
return ret;
@@ -607,7 +623,38 @@ static struct sort_entry sort_sym = {
.print = sort__sym_print,
};
+/* --sort parent */
+
+static int64_t
+sort__parent_cmp(struct hist_entry *left, struct hist_entry *right)
+{
+ struct symbol *sym_l = left->parent;
+ struct symbol *sym_r = right->parent;
+
+ if (!sym_l || !sym_r)
+ return cmp_null(sym_l, sym_r);
+
+ return strcmp(sym_l->name, sym_r->name);
+}
+
+static size_t
+sort__parent_print(FILE *fp, struct hist_entry *self)
+{
+ size_t ret = 0;
+
+ ret += fprintf(fp, "%-20s", self->parent ? self->parent->name : "[other]");
+
+ return ret;
+}
+
+static struct sort_entry sort_parent = {
+ .header = "Parent symbol ",
+ .cmp = sort__parent_cmp,
+ .print = sort__parent_print,
+};
+
static int sort__need_collapse = 0;
+static int sort__has_parent = 0;
struct sort_dimension {
char *name;
@@ -620,6 +667,7 @@ static struct sort_dimension sort_dimensions[] = {
{ .name = "comm", .entry = &sort_comm, },
{ .name = "dso", .entry = &sort_dso, },
{ .name = "symbol", .entry = &sort_sym, },
+ { .name = "parent", .entry = &sort_parent, },
};
static LIST_HEAD(hist_entry__sort_list);
@@ -640,6 +688,19 @@ static int sort_dimension__add(char *tok)
if (sd->entry->collapse)
sort__need_collapse = 1;
+ if (sd->entry == &sort_pa