summaryrefslogtreecommitdiff
path: root/tools/perf/util
diff options
context:
space:
mode:
authorIngo Molnar <mingo@elte.hu>2009-10-23 08:23:20 +0200
committerIngo Molnar <mingo@elte.hu>2009-10-23 08:23:20 +0200
commit43315956509ca6913764861ac7dec128b91eb1ec (patch)
tree60fd5647f150a46e63093a41417c2eef3e776b3d /tools/perf/util
parent9bf4e7fba8006d19846fec877b6da0616b2772de (diff)
parent6beba7adbe092e63dfe8d09fbd1e3ec140474a13 (diff)
downloadkernel-common-43315956509ca6913764861ac7dec128b91eb1ec.tar.gz
kernel-common-43315956509ca6913764861ac7dec128b91eb1ec.tar.bz2
kernel-common-43315956509ca6913764861ac7dec128b91eb1ec.zip
Merge branch 'perf/core' into perf/probes
Conflicts: tools/perf/Makefile Merge reason: - fix the conflict - pick up the pr_*() infrastructure to queue up dependent patch Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'tools/perf/util')
-rwxr-xr-xtools/perf/util/PERF-VERSION-GEN2
-rw-r--r--tools/perf/util/cache.h11
-rw-r--r--tools/perf/util/callchain.c2
-rw-r--r--tools/perf/util/callchain.h2
-rw-r--r--tools/perf/util/color.h6
-rw-r--r--tools/perf/util/data_map.c222
-rw-r--r--tools/perf/util/data_map.h31
-rw-r--r--tools/perf/util/debug.c4
-rw-r--r--tools/perf/util/debug.h7
-rw-r--r--tools/perf/util/event.h30
-rw-r--r--tools/perf/util/exec_cmd.h6
-rw-r--r--tools/perf/util/header.c74
-rw-r--r--tools/perf/util/header.h29
-rw-r--r--tools/perf/util/help.h6
-rw-r--r--tools/perf/util/hist.c210
-rw-r--r--tools/perf/util/hist.h50
-rw-r--r--tools/perf/util/include/asm/asm-offsets.h1
-rw-r--r--tools/perf/util/include/asm/bitops.h18
-rw-r--r--tools/perf/util/include/asm/byteorder.h2
-rw-r--r--tools/perf/util/include/asm/swab.h1
-rw-r--r--tools/perf/util/include/asm/uaccess.h14
-rw-r--r--tools/perf/util/include/linux/bitmap.h2
-rw-r--r--tools/perf/util/include/linux/bitops.h27
-rw-r--r--tools/perf/util/include/linux/compiler.h10
-rw-r--r--tools/perf/util/include/linux/ctype.h1
-rw-r--r--tools/perf/util/include/linux/kernel.h76
-rw-r--r--tools/perf/util/include/linux/string.h1
-rw-r--r--tools/perf/util/include/linux/types.h9
-rw-r--r--tools/perf/util/levenshtein.h6
-rw-r--r--tools/perf/util/map.c26
-rw-r--r--tools/perf/util/module.c545
-rw-r--r--tools/perf/util/module.h53
-rw-r--r--tools/perf/util/parse-events.c26
-rw-r--r--tools/perf/util/parse-events.h8
-rw-r--r--tools/perf/util/parse-options.h6
-rw-r--r--tools/perf/util/quote.h6
-rw-r--r--tools/perf/util/run-command.h6
-rw-r--r--tools/perf/util/sigchain.h6
-rw-r--r--tools/perf/util/sort.c290
-rw-r--r--tools/perf/util/sort.h99
-rw-r--r--tools/perf/util/strbuf.h6
-rw-r--r--tools/perf/util/string.c11
-rw-r--r--tools/perf/util/string.h7
-rw-r--r--tools/perf/util/strlist.h6
-rw-r--r--tools/perf/util/svghelper.c2
-rw-r--r--tools/perf/util/svghelper.h6
-rw-r--r--tools/perf/util/symbol.c785
-rw-r--r--tools/perf/util/symbol.h34
-rw-r--r--tools/perf/util/thread.c167
-rw-r--r--tools/perf/util/thread.h31
-rw-r--r--tools/perf/util/trace-event-info.c6
-rw-r--r--tools/perf/util/trace-event-parse.c537
-rw-r--r--tools/perf/util/trace-event-read.c7
-rw-r--r--tools/perf/util/trace-event.h41
-rw-r--r--tools/perf/util/types.h6
-rw-r--r--tools/perf/util/values.h6
56 files changed, 2433 insertions, 1156 deletions
diff --git a/tools/perf/util/PERF-VERSION-GEN b/tools/perf/util/PERF-VERSION-GEN
index c561d1538c03..54552a00a117 100755
--- a/tools/perf/util/PERF-VERSION-GEN
+++ b/tools/perf/util/PERF-VERSION-GEN
@@ -1,7 +1,7 @@
#!/bin/sh
GVF=PERF-VERSION-FILE
-DEF_VER=v0.0.1.PERF
+DEF_VER=v0.0.2.PERF
LF='
'
diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h
index 6f8ea9d210b6..918eb376abe3 100644
--- a/tools/perf/util/cache.h
+++ b/tools/perf/util/cache.h
@@ -1,10 +1,15 @@
-#ifndef CACHE_H
-#define CACHE_H
+#ifndef __PERF_CACHE_H
+#define __PERF_CACHE_H
#include "util.h"
#include "strbuf.h"
#include "../perf.h"
+#define CMD_EXEC_PATH "--exec-path"
+#define CMD_PERF_DIR "--perf-dir="
+#define CMD_WORK_TREE "--work-tree="
+#define CMD_DEBUGFS_DIR "--debugfs-dir="
+
#define PERF_DIR_ENVIRONMENT "PERF_DIR"
#define PERF_WORK_TREE_ENVIRONMENT "PERF_WORK_TREE"
#define DEFAULT_PERF_DIR_ENVIRONMENT ".perf"
@@ -117,4 +122,4 @@ extern char *perf_pathdup(const char *fmt, ...)
extern size_t strlcpy(char *dest, const char *src, size_t size);
-#endif /* CACHE_H */
+#endif /* __PERF_CACHE_H */
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c
index 3b8380f1b478..b3b71258272a 100644
--- a/tools/perf/util/callchain.c
+++ b/tools/perf/util/callchain.c
@@ -206,7 +206,7 @@ fill_node(struct callchain_node *node, struct ip_callchain *chain,
}
node->val_nr = chain->nr - start;
if (!node->val_nr)
- printf("Warning: empty node in callchain tree\n");
+ pr_warning("Warning: empty node in callchain tree\n");
}
static void
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h
index 43cf3ea9e088..ad4626de4c2b 100644
--- a/tools/perf/util/callchain.h
+++ b/tools/perf/util/callchain.h
@@ -58,4 +58,4 @@ static inline u64 cumul_hits(struct callchain_node *node)
int register_callchain_param(struct callchain_param *param);
void append_chain(struct callchain_node *root, struct ip_callchain *chain,
struct symbol **syms);
-#endif
+#endif /* __PERF_CALLCHAIN_H */
diff --git a/tools/perf/util/color.h b/tools/perf/util/color.h
index 58d597564b99..24e8809210bb 100644
--- a/tools/perf/util/color.h
+++ b/tools/perf/util/color.h
@@ -1,5 +1,5 @@
-#ifndef COLOR_H
-#define COLOR_H
+#ifndef __PERF_COLOR_H
+#define __PERF_COLOR_H
/* "\033[1;38;5;2xx;48;5;2xxm\0" is 23 bytes */
#define COLOR_MAXLEN 24
@@ -39,4 +39,4 @@ int color_fwrite_lines(FILE *fp, const char *color, size_t count, const char *bu
int percent_color_fprintf(FILE *fp, const char *fmt, double percent);
const char *get_percent_color(double percent);
-#endif /* COLOR_H */
+#endif /* __PERF_COLOR_H */
diff --git a/tools/perf/util/data_map.c b/tools/perf/util/data_map.c
new file mode 100644
index 000000000000..18accb8fee4d
--- /dev/null
+++ b/tools/perf/util/data_map.c
@@ -0,0 +1,222 @@
+#include "data_map.h"
+#include "symbol.h"
+#include "util.h"
+#include "debug.h"
+
+
+static struct perf_file_handler *curr_handler;
+static unsigned long mmap_window = 32;
+static char __cwd[PATH_MAX];
+
+static int
+process_event_stub(event_t *event __used,
+ unsigned long offset __used,
+ unsigned long head __used)
+{
+ return 0;
+}
+
+void register_perf_file_handler(struct perf_file_handler *handler)
+{
+ if (!handler->process_sample_event)
+ handler->process_sample_event = process_event_stub;
+ if (!handler->process_mmap_event)
+ handler->process_mmap_event = process_event_stub;
+ if (!handler->process_comm_event)
+ handler->process_comm_event = process_event_stub;
+ if (!handler->process_fork_event)
+ handler->process_fork_event = process_event_stub;
+ if (!handler->process_exit_event)
+ handler->process_exit_event = process_event_stub;
+ if (!handler->process_lost_event)
+ handler->process_lost_event = process_event_stub;
+ if (!handler->process_read_event)
+ handler->process_read_event = process_event_stub;
+ if (!handler->process_throttle_event)
+ handler->process_throttle_event = process_event_stub;
+ if (!handler->process_unthrottle_event)
+ handler->process_unthrottle_event = process_event_stub;
+
+ curr_handler = handler;
+}
+
+static int
+process_event(event_t *event, unsigned long offset, unsigned long head)
+{
+ trace_event(event);
+
+ switch (event->header.type) {
+ case PERF_RECORD_SAMPLE:
+ return curr_handler->process_sample_event(event, offset, head);
+ case PERF_RECORD_MMAP:
+ return curr_handler->process_mmap_event(event, offset, head);
+ case PERF_RECORD_COMM:
+ return curr_handler->process_comm_event(event, offset, head);
+ case PERF_RECORD_FORK:
+ return curr_handler->process_fork_event(event, offset, head);
+ case PERF_RECORD_EXIT:
+ return curr_handler->process_exit_event(event, offset, head);
+ case PERF_RECORD_LOST:
+ return curr_handler->process_lost_event(event, offset, head);
+ case PERF_RECORD_READ:
+ return curr_handler->process_read_event(event, offset, head);
+ case PERF_RECORD_THROTTLE:
+ return curr_handler->process_throttle_event(event, offset, head);
+ case PERF_RECORD_UNTHROTTLE:
+ return curr_handler->process_unthrottle_event(event, offset, head);
+ default:
+ curr_handler->total_unknown++;
+ return -1;
+ }
+}
+
+int mmap_dispatch_perf_file(struct perf_header **pheader,
+ const char *input_name,
+ int force,
+ int full_paths,
+ int *cwdlen,
+ char **cwd)
+{
+ int ret, rc = EXIT_FAILURE;
+ struct perf_header *header;
+ unsigned long head, shift;
+ unsigned long offset = 0;
+ struct stat input_stat;
+ size_t page_size;
+ u64 sample_type;
+ event_t *event;
+ uint32_t size;
+ int input;
+ char *buf;
+
+ if (!curr_handler)
+ die("Forgot to register perf file handler");
+
+ page_size = getpagesize();
+
+ input = open(input_name, O_RDONLY);
+ if (input < 0) {
+ fprintf(stderr, " failed to open file: %s", input_name);
+ if (!strcmp(input_name, "perf.data"))
+ fprintf(stderr, " (try 'perf record' first)");
+ fprintf(stderr, "\n");
+ exit(-1);
+ }
+
+ ret = fstat(input, &input_stat);
+ if (ret < 0) {
+ perror("failed to stat file");
+ exit(-1);
+ }
+
+ if (!force && input_stat.st_uid && (input_stat.st_uid != geteuid())) {
+ fprintf(stderr, "file: %s not owned by current user or root\n",
+ input_name);
+ exit(-1);
+ }
+
+ if (!input_stat.st_size) {
+ fprintf(stderr, "zero-sized file, nothing to do!\n");
+ exit(0);
+ }
+
+ *pheader = perf_header__read(input);
+ header = *pheader;
+ head = header->data_offset;
+
+ sample_type = perf_header__sample_type(header);
+
+ if (curr_handler->sample_type_check)
+ if (curr_handler->sample_type_check(sample_type) < 0)
+ exit(-1);
+
+ if (load_kernel(0, NULL) < 0) {
+ perror("failed to load kernel symbols");
+ return EXIT_FAILURE;
+ }
+
+ if (!full_paths) {
+ if (getcwd(__cwd, sizeof(__cwd)) == NULL) {
+ perror("failed to get the current directory");
+ return EXIT_FAILURE;
+ }
+ *cwd = __cwd;
+ *cwdlen = strlen(*cwd);
+ } else {
+ *cwd = NULL;
+ *cwdlen = 0;
+ }
+
+ shift = page_size * (head / page_size);
+ offset += shift;
+ head -= shift;
+
+remap:
+ buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ,
+ MAP_SHARED, input, offset);
+ if (buf == MAP_FAILED) {
+ perror("failed to mmap file");
+ exit(-1);
+ }
+
+more:
+ event = (event_t *)(buf + head);
+
+ size = event->header.size;
+ if (!size)
+ size = 8;
+
+ if (head + event->header.size >= page_size * mmap_window) {
+ int munmap_ret;
+
+ shift = page_size * (head / page_size);
+
+ munmap_ret = munmap(buf, page_size * mmap_window);
+ assert(munmap_ret == 0);
+
+ offset += shift;
+ head -= shift;
+ goto remap;
+ }
+
+ size = event->header.size;
+
+ dump_printf("\n%p [%p]: event: %d\n",
+ (void *)(offset + head),
+ (void *)(long)event->header.size,
+ event->header.type);
+
+ if (!size || process_event(event, offset, head) < 0) {
+
+ dump_printf("%p [%p]: skipping unknown header type: %d\n",
+ (void *)(offset + head),
+ (void *)(long)(event->header.size),
+ event->header.type);
+
+ /*
+ * assume we lost track of the stream, check alignment, and
+ * increment a single u64 in the hope to catch on again 'soon'.
+ */
+
+ if (unlikely(head & 7))
+ head &= ~7ULL;
+
+ size = 8;
+ }
+
+ head += size;
+
+ if (offset + head >= header->data_offset + header->data_size)
+ goto done;
+
+ if (offset + head < (unsigned long)input_stat.st_size)
+ goto more;
+
+done:
+ rc = EXIT_SUCCESS;
+ close(input);
+
+ return rc;
+}
+
+
diff --git a/tools/perf/util/data_map.h b/tools/perf/util/data_map.h
new file mode 100644
index 000000000000..716d1053b074
--- /dev/null
+++ b/tools/perf/util/data_map.h
@@ -0,0 +1,31 @@
+#ifndef __PERF_DATAMAP_H
+#define __PERF_DATAMAP_H
+
+#include "event.h"
+#include "header.h"
+
+typedef int (*event_type_handler_t)(event_t *, unsigned long, unsigned long);
+
+struct perf_file_handler {
+ event_type_handler_t process_sample_event;
+ event_type_handler_t process_mmap_event;
+ event_type_handler_t process_comm_event;
+ event_type_handler_t process_fork_event;
+ event_type_handler_t process_exit_event;
+ event_type_handler_t process_lost_event;
+ event_type_handler_t process_read_event;
+ event_type_handler_t process_throttle_event;
+ event_type_handler_t process_unthrottle_event;
+ int (*sample_type_check)(u64 sample_type);
+ unsigned long total_unknown;
+};
+
+void register_perf_file_handler(struct perf_file_handler *handler);
+int mmap_dispatch_perf_file(struct perf_header **pheader,
+ const char *input_name,
+ int force,
+ int full_paths,
+ int *cwdlen,
+ char **cwd);
+
+#endif
diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c
index e8ca98fe0bd4..28d520d5a1fb 100644
--- a/tools/perf/util/debug.c
+++ b/tools/perf/util/debug.c
@@ -13,12 +13,12 @@
int verbose = 0;
int dump_trace = 0;
-int eprintf(const char *fmt, ...)
+int eprintf(int level, const char *fmt, ...)
{
va_list args;
int ret = 0;
- if (verbose) {
+ if (verbose >= level) {
va_start(args, fmt);
ret = vfprintf(stderr, fmt, args);
va_end(args);
diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h
index 437eea58ce40..e8b18a1f87a4 100644
--- a/tools/perf/util/debug.h
+++ b/tools/perf/util/debug.h
@@ -1,8 +1,13 @@
/* For debugging general purposes */
+#ifndef __PERF_DEBUG_H
+#define __PERF_DEBUG_H
extern int verbose;
extern int dump_trace;
-int eprintf(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
+int eprintf(int level,
+ const char *fmt, ...) __attribute__((format(printf, 2, 3)));
int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
void trace_event(event_t *event);
+
+#endif /* __PERF_DEBUG_H */
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
index 2c9c26d6ded0..d972b4b0d38c 100644
--- a/tools/perf/util/event.h
+++ b/tools/perf/util/event.h
@@ -1,14 +1,10 @@
#ifndef __PERF_RECORD_H
#define __PERF_RECORD_H
+
#include "../perf.h"
#include "util.h"
#include <linux/list.h>
-
-enum {
- SHOW_KERNEL = 1,
- SHOW_USER = 2,
- SHOW_HV = 4,
-};
+#include <linux/rbtree.h>
/*
* PERF_SAMPLE_IP | PERF_SAMPLE_TID | *
@@ -78,11 +74,15 @@ typedef union event_union {
} event_t;
struct map {
- struct list_head node;
+ union {
+ struct rb_node rb_node;
+ struct list_head node;
+ };
u64 start;
u64 end;
u64 pgoff;
u64 (*map_ip)(struct map *, u64);
+ u64 (*unmap_ip)(struct map *, u64);
struct dso *dso;
};
@@ -91,14 +91,24 @@ static inline u64 map__map_ip(struct map *map, u64 ip)
return ip - map->start + map->pgoff;
}
-static inline u64 vdso__map_ip(struct map *map __used, u64 ip)
+static inline u64 map__unmap_ip(struct map *map, u64 ip)
+{
+ return ip + map->start - map->pgoff;
+}
+
+static inline u64 identity__map_ip(struct map *map __used, u64 ip)
{
return ip;
}
-struct map *map__new(struct mmap_event *event, char *cwd, int cwdlen);
+struct symbol;
+
+typedef int (*symbol_filter_t)(struct map *map, struct symbol *sym);
+
+struct map *map__new(struct mmap_event *event, char *cwd, int cwdlen,
+ unsigned int sym_priv_size, symbol_filter_t filter);
struct map *map__clone(struct map *self);
int map__overlap(struct map *l, struct map *r);
size_t map__fprintf(struct map *self, FILE *fp);
-#endif
+#endif /* __PERF_RECORD_H */
diff --git a/tools/perf/util/exec_cmd.h b/tools/perf/util/exec_cmd.h
index effe25eb1545..31647ac92ed1 100644
--- a/tools/perf/util/exec_cmd.h
+++ b/tools/perf/util/exec_cmd.h
@@ -1,5 +1,5 @@
-#ifndef PERF_EXEC_CMD_H
-#define PERF_EXEC_CMD_H
+#ifndef __PERF_EXEC_CMD_H
+#define __PERF_EXEC_CMD_H
extern void perf_set_argv_exec_path(const char *exec_path);
extern const char *perf_extract_argv0_path(const char *path);
@@ -10,4 +10,4 @@ extern int execv_perf_cmd(const char **argv); /* NULL terminated */
extern int execl_perf_cmd(const char *cmd, ...);
extern const char *system_path(const char *path);
-#endif /* PERF_EXEC_CMD_H */
+#endif /* __PERF_EXEC_CMD_H */
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index e306857b2c2b..7d26659b806c 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -5,6 +5,8 @@
#include "util.h"
#include "header.h"
+#include "../perf.h"
+#include "trace-event.h"
/*
* Create new perf.data header attribute:
@@ -46,23 +48,17 @@ void perf_header_attr__add_id(struct perf_header_attr *self, u64 id)
*/
struct perf_header *perf_header__new(void)
{
- struct perf_header *self = malloc(sizeof(*self));
+ struct perf_header *self = calloc(sizeof(*self), 1);
if (!self)
die("nomem");
- self->frozen = 0;
-
- self->attrs = 0;
self->size = 1;
self->attr = malloc(sizeof(void *));
if (!self->attr)
die("nomem");
- self->data_offset = 0;
- self->data_size = 0;
-
return self;
}
@@ -97,7 +93,7 @@ static struct perf_trace_event_type *events;
void perf_header__push_event(u64 id, const char *name)
{
if (strlen(name) > MAX_EVENT_NAME)
- printf("Event %s will be truncated\n", name);
+ pr_warning("Event %s will be truncated\n", name);
if (!events) {
events = malloc(sizeof(struct perf_trace_event_type));
@@ -145,8 +141,14 @@ struct perf_file_header {
struct perf_file_section attrs;
struct perf_file_section data;
struct perf_file_section event_types;
+ DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS);
};
+void perf_header__feat_trace_info(struct perf_header *header)
+{
+ set_bit(HEADER_TRACE_INFO, header->adds_features);
+}
+
static void do_write(int fd, void *buf, size_t size)
{
while (size) {
@@ -160,6 +162,32 @@ static void do_write(int fd, void *buf, size_t size)
}
}
+static void perf_header__adds_write(struct perf_header *self, int fd)
+{
+ struct perf_file_section trace_sec;
+ u64 cur_offset = lseek(fd, 0, SEEK_CUR);
+ unsigned long *feat_mask = self->adds_features;
+
+ if (test_bit(HEADER_TRACE_INFO, feat_mask)) {
+ /* Write trace info */
+ trace_sec.offset = lseek(fd, sizeof(trace_sec), SEEK_CUR);
+ read_tracing_data(fd, attrs, nr_counters);
+ trace_sec.size = lseek(fd, 0, SEEK_CUR) - trace_sec.offset;
+
+ /* Write trace info headers */
+ lseek(fd, cur_offset, SEEK_SET);
+ do_write(fd, &trace_sec, sizeof(trace_sec));
+
+ /*
+ * Update cur_offset. So that other (future)
+ * features can set their own infos in this place. But if we are
+ * the only feature, at least that seeks to the place the data
+ * should begin.
+ */
+ cur_offset = lseek(fd, trace_sec.offset + trace_sec.size, SEEK_SET);
+ }
+};
+
void perf_header__write(struct perf_header *self, int fd)
{
struct perf_file_header f_header;
@@ -198,6 +226,7 @@ void perf_header__write(struct perf_header *self, int fd)
if (events)
do_write(fd, events, self->event_size);
+ perf_header__adds_write(self, fd);
self->data_offset = lseek(fd, 0, SEEK_CUR);
@@ -219,6 +248,8 @@ void perf_header__write(struct perf_header *self, int fd)
},
};
+ memcpy(&f_header.adds_features, &self->adds_features, sizeof(self->adds_features));
+
lseek(fd, 0, SEEK_SET);
do_write(fd, &f_header, sizeof(f_header));
lseek(fd, self->data_offset + self->data_size, SEEK_SET);
@@ -241,6 +272,20 @@ static void do_read(int fd, void *buf, size_t size)
}
}
+static void perf_header__adds_read(struct perf_header *self, int fd)
+{
+ const unsigned long *feat_mask = self->adds_features;
+
+ if (test_bit(HEADER_TRACE_INFO, feat_mask)) {
+ struct perf_file_section trace_sec;
+
+ do_read(fd, &trace_sec, sizeof(trace_sec));
+ lseek(fd, trace_sec.offset, SEEK_SET);
+ trace_report(fd);
+ lseek(fd, trace_sec.offset + trace_sec.size, SEEK_SET);
+ }
+};
+
struct perf_header *perf_header__read(int fd)
{
struct perf_header *self = perf_header__new();
@@ -254,10 +299,16 @@ struct perf_header *perf_header__read(int fd)
do_read(fd, &f_header, sizeof(f_header));
if (f_header.magic != PERF_MAGIC ||
- f_header.size != sizeof(f_header) ||
f_header.attr_size != sizeof(f_attr))
die("incompatible file format");
+ if (f_header.size != sizeof(f_header)) {
+ /* Support the previous format */
+ if (f_header.size == offsetof(typeof(f_header), adds_features))
+ bitmap_zero(f_header.adds_features, HEADER_FEAT_BITS);
+ else
+ die("incompatible file format");
+ }
nr_attrs = f_header.attrs.size / sizeof(f_attr);
lseek(fd, f_header.attrs.offset, SEEK_SET);
@@ -290,6 +341,11 @@ struct perf_header *perf_header__read(int fd)
do_read(fd, events, f_header.event_types.size);
event_count = f_header.event_types.size / sizeof(struct perf_trace_event_type);
}
+
+ memcpy(&self->adds_features, &f_header.adds_features, sizeof(f_header.adds_features));
+
+ perf_header__adds_read(self, fd);
+
self->event_offset = f_header.event_types.offset;
self->event_size = f_header.event_types.size;
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h
index a0761bc7863c..2ea9dfb1236a 100644
--- a/tools/perf/util/header.h
+++ b/tools/perf/util/header.h
@@ -1,10 +1,12 @@
-#ifndef _PERF_HEADER_H
-#define _PERF_HEADER_H
+#ifndef __PERF_HEADER_H
+#define __PERF_HEADER_H
#include "../../../include/linux/perf_event.h"
#include <sys/types.h>
#include "types.h"
+#include <linux/bitmap.h>
+
struct perf_header_attr {
struct perf_event_attr attr;
int ids, size;
@@ -12,15 +14,20 @@ struct perf_header_attr {
off_t id_offset;
};
+#define HEADER_TRACE_INFO 1
+
+#define HEADER_FEAT_BITS 256
+
struct perf_header {
- int frozen;
- int attrs, size;
+ int frozen;
+ int attrs, size;
struct perf_header_attr **attr;
- s64 attr_offset;
- u64 data_offset;
- u64 data_size;
- u64 event_offset;
- u64 event_size;
+ s64 attr_offset;
+ u64 data_offset;
+ u64 data_size;
+ u64 event_offset;
+ u64 event_size;
+ DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS);
};
struct perf_header *perf_header__read(int fd);
@@ -40,8 +47,8 @@ void perf_header_attr__add_id(struct perf_header_attr *self, u64 id);
u64 perf_header__sample_type(struct perf_header *header);
struct perf_event_attr *
perf_header__find_attr(u64 id, struct perf_header *header);
-
+void perf_header__feat_trace_info(struct perf_header *header);
struct perf_header *perf_header__new(void);
-#endif /* _PERF_HEADER_H */
+#endif /* __PERF_HEADER_H */
diff --git a/tools/perf/util/help.h b/tools/perf/util/help.h
index 7128783637b4..7f5c6dedd714 100644
--- a/tools/perf/util/help.h
+++ b/tools/perf/util/help.h
@@ -1,5 +1,5 @@
-#ifndef HELP_H
-#define HELP_H
+#ifndef __PERF_HELP_H
+#define __PERF_HELP_H
struct cmdnames {
size_t alloc;
@@ -26,4 +26,4 @@ int is_in_cmdlist(struct cmdnames *c, const char *s);
void list_commands(const char *title, struct cmdnames *main_cmds,
struct cmdnames *other_cmds);
-#endif /* HELP_H */
+#endif /* __PERF_HELP_H */
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
new file mode 100644
index 000000000000..7393a02fd8d4
--- /dev/null
+++ b/tools/perf/util/hist.c
@@ -0,0 +1,210 @@
+#include "hist.h"
+
+struct rb_root hist;
+struct rb_root collapse_hists;
+struct rb_root output_hists;
+int callchain;
+
+struct callchain_param callchain_param = {
+ .mode = CHAIN_GRAPH_REL,
+ .min_percent = 0.5
+};
+
+unsigned long total;
+unsigned long total_mmap;
+unsigned long total_comm;
+unsigned long total_fork;
+unsigned long total_unknown;
+unsigned long total_lost;
+
+/*
+ * histogram, sorted on item, collects counts
+ */
+
+struct hist_entry *__hist_entry__add(struct thread *thread, struct map *map,
+ struct symbol *sym,
+ struct symbol *sym_parent,
+ u64 ip, u64 count, char level, bool *hit)
+{
+ struct rb_node **p = &hist.rb_node;
+ struct rb_node *parent = NULL;
+ struct hist_entry *he;
+ struct hist_entry entry = {
+ .thread = thread,
+ .map = map,
+ .sym = sym,
+ .ip = ip,
+ .level = level,
+ .count = count,
+ .parent = sym_parent,
+ };
+ int cmp;
+
+ while (*p != NULL) {
+ parent = *p;
+ he = rb_entry(parent, struct hist_entry, rb_node);
+
+ cmp = hist_entry__cmp(&entry, he);
+
+ if (!cmp) {
+ *hit = true;
+ return he;
+ }
+
+ if (cmp < 0)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+
+ he = malloc(sizeof(*he));
+ if (!he)
+ return NULL;
+ *he = entry;
+ rb_link_node(&he->rb_node, parent, p);
+ rb_insert_color(&he->rb_node, &hist);
+ *hit = false;
+ return he;
+}
+
+int64_t
+hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)
+{
+ struct sort_entry *se;
+ int64_t cmp = 0;
+
+ list_for_each_entry(se, &hist_entry__sort_list, list) {
+ cmp = se->cmp(left, right);
+ if (cmp)
+ break;
+ }
+
+ return cmp;
+}
+
+int64_t
+hist_entry__collapse(struct hist_entry *left, struct hist_entry *right)
+{
+ struct sort_entry *se;
+ int64_t cmp = 0;
+
+ list_for_each_entry(se, &hist_entry__sort_list, list) {
+ int64_t (*f)(struct hist_entry *, struct hist_entry *);
+
+ f = se->collapse ?: se->cmp;
+
+ cmp = f(left, right);
+ if (cmp)
+ break;
+ }
+
+ return cmp;
+}
+
+void hist_entry__free(struct hist_entry *he)
+{
+ free(he);
+}
+
+/*
+ * collapse the histogram
+ */
+
+void collapse__insert_entry(struct hist_entry *he)
+{
+ struct rb_node **p = &collapse_hists.rb_node;
+ struct rb_node *parent = NULL;
+ struct hist_entry *iter;
+ int64_t cmp;
+
+ while (*p != NULL) {
+ parent = *p;
+ iter = rb_entry(parent, struct hist_entry, rb_node);
+
+ cmp = hist_entry__collapse(iter, he);
+
+ if (!cmp) {
+ iter->count += he->count;
+ hist_entry__free(he);
+ return;
+ }
+
+ if (cmp < 0)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+
+ rb_link_node(&he->rb_node, parent, p);
+ rb_insert_color(&he->rb_node, &collapse_hists);
+}
+
+void collapse__resort(void)
+{
+ struct rb_node *next;
+ struct hist_entry *n;
+
+ if (!sort__need_collapse)
+ return;
+
+ next = rb_first(&hist);
+ while (next) {
+ n = rb_entry(next, struct hist_entry, rb_node);
+ next = rb_next(&n->rb_node);
+
+ rb_erase(&n->rb_node, &hist);
+ collapse__insert_entry(n);
+ }
+}
+
+/*
+ * reverse the map, sort on count.
+ */
+
+void output__insert_entry(struct hist_entry *he, u64 min_callchain_hits)
+{
+ struct rb_node **p = &output_hists.rb_node;
+ struct rb_node *parent = NULL;
+ struct hist_entry *iter;
+
+ if (callchain)
+ callchain_param.sort(&he->sorted_chain, &he->callchain,
+ min_callchain_hits, &callchain_param);
+
+ while (*p != NULL) {
+ parent = *p;
+ iter = rb_entry(parent, struct hist_entry, rb_node);
+
+ if (he->count > iter->count)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+
+ rb_link_node(&he->rb_node, parent, p);
+ rb_insert_color(&he->rb_node, &output_hists);
+}
+
+void output__resort(u64 total_samples)
+{
+ struct rb_node *next;
+ struct hist_entry *n;
+ struct rb_root *tree = &hist;
+ u64 min_callchain_hits;
+
+ min_callchain_hits =
+ total_samples * (callchain_param.min_percent / 100);
+
+ if (sort__need_collapse)
+ tree = &collapse_hists;
+
+ next = rb_first(tree);
+
+ while (next) {
+ n = rb_entry(next, struct hist_entry, rb_node);
+ next = rb_next(&n->rb_node);
+
+ rb_erase(&n->rb_node, tree);
+ output__insert_entry(n, min_callchain_hits);
+ }
+}
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h
new file mode 100644
index 000000000000..ac2149c559b0
--- /dev/null
+++ b/tools/perf/util/hist.h
@@ -0,0 +1,50 @@
+#ifndef __PERF_HIST_H
+#define __PERF_HIST_H
+#include "../builtin.h"
+
+#include "util.h"
+
+#include "color.h"
+#include <linux/list.h>
+#include "cache.h"
+#include <linux/rbtree.h>
+#include "symbol.h"
+#include "string.h"
+#include "callchain.h"
+#include "strlist.h"
+#include "values.h"
+
+#include "../perf.h"
+#include "debug.h"
+#include "header.h"
+
+#include "parse-options.h"
+#include "parse-events.h"
+
+#include "thread.h"
+#include "sort.h"
+
+extern struct rb_root hist;
+extern struct rb_root collapse_hists;
+extern struct rb_root output_hists;
+extern int callchain;
+extern struct callchain_param callchain_param;
+extern unsigned long total;
+extern unsigned long total_mmap;
+extern unsigned long total_comm;
+extern unsigned long total_fork;
+extern unsigned long total_unknown;
+extern unsigned long total_lost;
+
+struct hist_entry *__hist_entry__add(struct thread *thread, struct map *map,
+ struct symbol *sym, struct symbol *parent,
+ u64 ip, u64 count, char level, bool *hit);
+extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *);
+extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *);
+extern void hist_entry__free(struct hist_entry *);
+extern void collapse__insert_entry(struct hist_entry *);
+extern void collapse__resort(void);
+extern void output__insert_entry(struct hist_entry *, u64);
+extern void output__resort(u64);
+
+#endif /* __PERF_HIST_H */
diff --git a/tools/perf/util/include/asm/asm-offsets.h b/tools/perf/util/include/asm/asm-offsets.h
new file mode 100644
index 000000000000..ed538942523d
--- /dev/null
+++ b/tools/perf/util/include/asm/asm-offsets.h
@@ -0,0 +1 @@
+/* stub */
diff --git a/tools/perf/util/include/asm/bitops.h b/tools/perf/util/include/asm/bitops.h
new file mode 100644
index 000000000000..58e9817ffae0
--- /dev/null
+++ b/tools/perf/util/include/asm/bitops.h
@@ -0,0 +1,18 @@
+#ifndef _PERF_ASM_BITOPS_H_
+#define _PERF_ASM_BITOPS_H_
+
+#include <sys/types.h>
+#include "../../types.h"
+#include <linux/compiler.h>
+
+/* CHECKME: Not sure both always match */
+#define BITS_PER_LONG __WORDSIZE
+
+#include "../../../../include/asm-generic/bitops/__fls.h"
+#include "../../../../include/asm-generic/bitops/fls.h"
+#include "../../../../include/asm-generic/bitops/fls64.h"
+#include "../../../../include/asm-generic/bitops/__ffs.h"
+#include "../../../../include/asm-generic/bitops/ffz.h"
+#include "../../../../include/asm-generic/bitops/hweight.h"
+
+#endif
diff --git a/tools/perf/util/include/asm/byteorder.h b/tools/perf/util/include/asm/byteorder.h
new file mode 100644
index 000000000000..b722abe3a626
--- /dev/null
+++ b/tools/perf/util/include/asm/byteorder.h
@@ -0,0 +1,2 @@
+#include <asm/types.h>
+#include "../../../../include/linux/swab.h"
diff --git a/tools/perf/util/include/asm/swab.h b/tools/perf/util/include/asm/swab.h
new file mode 100644
index 000000000000..ed538942523d
--- /dev/null
+++ b/tools/perf/util/include/asm/swab.h
@@ -0,0 +1 @@
+/* stub */
diff --git a/tools/perf/util/include/asm/uaccess.h b/tools/perf/util/include/asm/uaccess.h
new file mode 100644
index 000000000000..d0f72b8fcc35
--- /dev/null
+++ b/tools/perf/util/include/asm/uaccess.h
@@ -0,0 +1,14 @@
+#ifndef _PERF_ASM_UACCESS_H_
+#define _PERF_ASM_UACCESS_H_
+
+#define __get_user(src, dest) \
+({ \
+ (src) = *dest; \
+ 0; \
+})
+
+#define get_user __get_user
+
+#define access_ok(type, addr, size) 1
+
+#endif
diff --git a/tools/perf/util/include/linux/bitmap.h b/tools/perf/util/include/linux/bitmap.h
new file mode 100644
index 000000000000..821c1033bccf
--- /dev/null
+++ b/tools/perf/util/include/linux/bitmap.h
@@ -0,0 +1,2 @@
+#include "../../../../include/linux/bitmap.h"
+#include "../../../../include/asm-generic/bitops/find.h"
diff --git a/tools/perf/util/include/linux/bitops.h b/tools/perf/util/include/linux/bitops.h
new file mode 100644
index 000000000000..ace57c36d1d0
--- /dev/null
+++ b/tools/perf/util/include/linux/bitops.h
@@ -0,0 +1,27 @@
+#ifndef _PERF_LINUX_BITOPS_H_
+#define _PERF_LINUX_BITOPS_H_
+
+#define __KERNEL__
+
+#define CONFIG_GENERIC_FIND_NEXT_BIT
+#define CONFIG_GENERIC_FIND_FIRST_BIT
+#include "../../../../include/linux/bitops.h"
+
+static inline void set_bit(int nr, unsigned long *addr)
+{
+ addr[nr / BITS_PER_LONG] |= 1UL << (nr % BITS_PER_LONG);
+}
+
+static __always_inline int test_bit(unsigned int nr, const unsigned long *addr)
+{
+ return ((1UL << (nr % BITS_PER_LONG)) &
+ (((unsigned long *)addr)[nr / BITS_PER_LONG])) != 0;
+}
+
+unsigned long generic_find_next_zero_le_bit(const unsigned long *addr, unsigned
+ long size, unsigned long offset);
+
+unsigned long generic_find_next_le_bit(const unsigned long *addr, unsigned
+ long size, unsigned long offset);
+
+#endif
diff --git a/tools/perf/util/include/linux/compiler.h b/tools/perf/util/include/linux/compiler.h
new file mode 100644
index 000000000000..dfb0713ed47f
--- /dev/null
+++ b/tools/perf/util/include/linux/compiler.h
@@ -0,0 +1,10 @@
+#ifndef _PERF_LINUX_COMPILER_H_
+#define _PERF_LINUX_COMPILER_H_
+
+#ifndef __always_inline
+#define __always_inline inline
+#endif
+#define __user
+#define __attribute_const__
+
+#endif
diff --git a/tools/perf/util/include/linux/ctype.h b/tools/perf/util/include/linux/ctype.h
new file mode 100644
index 000000000000..bae5783282ef
--- /dev/null
+++ b/tools/perf/util/include/linux/ctype.h
@@ -0,0 +1 @@
+#include "../../../../include/linux/ctype.h"
diff --git a/tools/perf/util/include/linux/kernel.h b/tools/perf/util/include/linux/kernel.h
index a6b87390cb52..21c0274c02fa 100644
--- a/tools/perf/util/include/linux/kernel.h
+++ b/tools/perf/util/include/linux/kernel.h
@@ -1,6 +1,16 @@
#ifndef PERF_LINUX_KERNEL_H_
#define PERF_LINUX_KERNEL_H_
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
+
+#define ALIGN(x,a) __ALIGN_MASK(x,(typeof(x))(a)-1)
+#define __ALIGN_MASK(x,mask) (((x)+(mask))&~(mask))
+
#ifndef offsetof
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif
@@ -26,4 +36,70 @@
_max1 > _max2 ? _max1 : _max2; })
#endif
+#ifndef min
+#define min(x, y) ({ \
+ typeof(x) _min1 = (x); \
+ typeof(y) _min2 = (y); \
+ (void) (&_min1 == &_min2); \
+ _min1 < _min2 ? _min1 : _min2; })
+#endif
+
+#ifndef BUG_ON
+#define BUG_ON(cond) assert(!(cond))
+#endif
+
+/*
+ * Both need more care to handle endianness
+ * (Don't use bitmap_copy_le() for now)
+ */
+#define cpu_to_le64(x) (x)
+#define cpu_to_le32(x) (x)
+
+static inline int
+vscnprintf(char *buf, size_t size, const char *fmt, va_list args)
+{
+ int i;
+ ssize_t ssize = size;
+
+ i = vsnprintf(buf, size, fmt, args);
+
+ return (i >= ssize) ? (ssize - 1) : i;
+}
+
+static inline int scnprintf(char * buf, size_t size, const char * fmt, ...)
+{
+ va_list args;
+ ssize_t ssize = size;
+ int i;
+
+ va_start(args, fmt);
+ i = vsnprintf(buf, size, fmt, args);
+ va_end(args);
+
+ return (i >= ssize) ? (ssize - 1) : i;
+}
+
+static inline unsigned long
+simple_strtoul(const char *nptr, char **endptr, int base)
+{
+ return strtoul(nptr, endptr, base);
+}
+
+#ifndef pr_fmt
+#define pr_fmt(fmt) fmt
+#endif
+
+#define pr_err(fmt, ...) \
+ do { fprintf(stderr, pr_fmt(fmt), ##__VA_ARGS__); } while (0)
+#define pr_warning(fmt, ...) \
+ do { fprintf(stderr, pr_fmt(fmt), ##__VA_ARGS__); } while (0)
+#define pr_info(fmt, ...) \
+ do { fprintf(stderr, pr_fmt(fmt), ##__VA_ARGS__); } while (0)
+#define pr_debug(fmt, ...) \
+ eprintf(1, pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_debugN(n, fmt, ...) \
+ eprintf(n, pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_debug2(fmt, ...) pr_debugN(2, pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_debug3(fmt, ...) pr_debugN(3, pr_fmt(fmt), ##__VA_ARGS__)
+
#endif
diff --git a/tools/perf/util/include/linux/string.h b/tools/perf/util/include/linux/string.h
new file mode 100644
index 000000000000..3b2f5900276f
--- /dev/null
+++ b/tools/perf/util/include/linux/string.h
@@ -0,0 +1 @@
+#include <string.h>
diff --git a/tools/perf/util/include/linux/types.h b/tools/perf/util/include/linux/types.h
new file mode 100644
index 000000000000..196862a81a21
--- /dev/null
+++ b/tools/perf/util/include/linux/types.h
@@ -0,0 +1,9 @@
+#ifndef _PERF_LINUX_TYPES_H_
+#define _PERF_LINUX_TYPES_H_
+
+#include <asm/types.h>
+
+#define DECLARE_BITMAP(name,bits) \
+ unsigned long name[BITS_TO_LONGS(bits)]
+
+#endif
diff --git a/tools/perf/util/levenshtein.h b/tools/perf/util/levenshtein.h
index 0173abeef52c..b0fcb6d8a881 100644
--- a/tools/perf/util/levenshtein.h
+++ b/tools/perf/util/levenshtein.h
@@ -1,8 +1,8 @@
-#ifndef LEVENSHTEIN_H
-#define LEVENSHTEIN_H
+#ifndef __PERF_LEVENSHTEIN_H
+#define __PERF_LEVENSHTEIN_H
int levenshtein(const char *string1, const char *string2,
int swap_penalty, int substition_penalty,
int insertion_penalty, int deletion_penalty);
-#endif
+#endif /* __PERF_LEVENSHTEIN_H */
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
index 804e02382739..c1c556825343 100644
--- a/tools/perf/util/map.c
+++ b/tools/perf/util/map.c
@@ -3,6 +3,7 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
+#include "debug.h"
static inline int is_anon_memory(const char *filename)
{
@@ -19,7 +20,8 @@ static int strcommon(const char *pathname, char *cwd, int cwdlen)
return n;
}
- struct map *map__new(struct mmap_event *event, char *cwd, int cwdlen)
+struct map *map__new(struct mmap_event *event, char *cwd, int cwdlen,
+ unsigned int sym_priv_size, symbol_filter_t filter)
{
struct map *self = malloc(sizeof(*self));
@@ -27,6 +29,7 @@ static int strcommon(const char *pathname, char *cwd, int cwdlen)
const char *filename = event->filename;
char newfilename[PATH_MAX];
int anon;
+ bool new_dso;
if (cwd) {
int n = strcommon(filename, cwd, cwdlen);
@@ -49,14 +52,29 @@ static int strcommon(const char *pathname, char *cwd, int cwdlen)
self->end = event->start + event->len;
self->pgoff = event->pgoff;
- self->dso = dsos__findnew(filename);
+ self->dso = dsos__findnew(filename, sym_priv_size, &new_dso);
if (self->dso == NULL)
goto out_delete;
+ if (new_dso) {
+ int nr = dso__load(self->dso, self, filter);
+
+ if (nr < 0)
+ pr_warning("Failed to open %s, continuing "
+ "without symbols\n",
+ self->dso->long_name);
+ else if (nr == 0)
+ pr_warning("No symbols found in %s, maybe "
+ "install a debug package?\n",
+ self->dso->long_name);
+ }
+
if (self->dso == vdso || anon)
- self->map_ip = vdso__map_ip;
- else
+ self->map_ip = self->unmap_ip = identity__map_ip;
+ else {
self->map_ip = map__map_ip;
+ self->unmap_ip = map__unmap_ip;
+ }
}
return self;
out_delete:
diff --git a/tools/perf/util/module.c b/tools/perf/util/module.c
deleted file mode 100644
index 0d8c85defcd2..000000000000
--- a/tools/perf/util/module.c
+++ /dev/null
@@ -1,545 +0,0 @@
-#include "util.h"
-#include "../perf.h"
-#include "string.h"
-#include "module.h"
-
-#include <libelf.h>
-#include <libgen.h>
-#include <gelf.h>
-#include <elf.h>
-#include <dirent.h>
-#include <sys/utsname.h>
-
-static unsigned int crc32(const char *p, unsigned int len)
-{
- int i;
- unsigned int crc = 0;
-
- while (len--) {
- crc ^= *p++;
- for (i = 0; i < 8; i++)
- crc = (crc >> 1) ^ ((crc & 1) ? 0xedb88320 : 0);
- }
- return crc;
-}
-
-/* module section methods */
-
-struct sec_dso *sec_dso__new_dso(const char *name)
-{
- struct sec_dso *self = malloc(sizeof(*self) + strlen(name) + 1);
-
- if (self != NULL) {
- strcpy(self->name, name);
- self->secs = RB_ROOT;
- self->find_section = sec_dso__find_section;
- }
-
- return self;
-}
-
-static void sec_dso__delete_section(struct section *self)
-{
- free(((void *)self));
-}
-
-void sec_dso__delete_sections(struct sec_dso *self)
-{
- struct section *pos;
- struct rb_node *next = rb_first(&self->secs);
-
- while (next) {
- pos = rb_entry(next, struct section, rb_node);
- next = rb_next(&pos->rb_node);
- rb_erase(&pos->rb_node, &self->secs);
- sec_dso__delete_section(pos);
- }
-}
-
-void sec_dso__delete_self(struct sec_dso *self)
-{
- sec_dso__delete_sections(self);
- free(self);
-}
-
-static void sec_dso__insert_section(struct sec_dso *self, struct section *sec)
-{
- struct rb_node **p = &self->secs.rb_node;
- struct rb_node *parent = NULL;
- const u64 hash = sec->hash;
- struct section *s;
-
- while (*p != NULL) {
- parent = *p;
- s = rb_entry(parent, struct section, rb_node);
- if (hash < s->hash)
- p = &(*p)->rb_left;
- else
- p = &(*p)->rb_right;
- }
- rb_link_node(&sec->rb_node, parent, p);
- rb_insert_color(&sec->rb_node, &self->secs);
-}
-
-struct section *sec_dso__find_section(struct sec_dso *self, const char *name)
-{
- struct rb_node *n;
- u64 hash;
- int len;
-
- if (self == NULL)
- return NULL;
-
- len = strlen(name);
- hash = crc32(name, len);
-
- n = self->secs.rb_node;
-
- while (n) {
- struct section *s = rb_entry(n, struct section, rb_node);
-
- if (hash < s->hash)
- n = n->rb_left;
- else if (hash > s->hash)
- n = n->rb_right;
- else {
- if (!strcmp(name, s->name))
- return s;
- else
- n = rb_next(&s->rb_node);
- }
- }
-
- return NULL;
-}
-
-static size_t sec_dso__fprintf_section(struct section *self, FILE *fp)
-{
- return fprintf(fp, "name:%s vma:%llx path:%s\n",
- self->name, self->vma, self->path);
-}
-
-size_t sec_dso__fprintf(struct sec_dso *self, FILE *fp)
-{
- size_t ret = fprintf(fp, "dso: %s\n", self->name);
-
- struct rb_node *nd;
- for (nd = rb_first(&self->secs); nd; nd = rb_next(nd)) {
- struct section *pos = rb_entry(nd, struct section, rb_node);
- ret += sec_dso__fprintf_section(pos, fp);
- }
-
- return ret;
-}
-
-static struct section *section__new(const char *name, const char *path)
-{
- struct section *self = calloc(1, sizeof(*self));
-
- if (!self)
- goto out_failure;
-
- self->name = calloc(1, strlen(name) + 1);
- if (!self->name)
- goto out_failure;
-
- self->path = calloc(1, strlen(path) + 1);
- if (!self->path)
- goto out_failure;
-
- strcpy(self->name, name);
- strcpy(self->path, path);
- self->hash = crc32(self->name, strlen(name));
-
- return self;
-
-out_failure:
- if (self) {
- if (self->name)
- free(self->name);
- if (self->path)
- free(self->path);
- free(self);
- }
-
- return NULL;
-}
-
-/* module methods */
-
-struct mod_dso *mod_dso__new_dso(const char *name)
-{
- struct mod_dso *self = malloc(sizeof(*self) + strlen(name) + 1);
-
- if (self != NULL) {
- strcpy(self->name, name);
- self->mods = RB_ROOT;
- self->find_module = mod_dso__find_module;
- }
-
- return self;
-}
-
-static void mod_dso__delete_module(struct module *self)
-{
- free(((void *)self));
-}
-
-void mod_dso__delete_modules(struct mod_dso *self)
-{
- struct module *pos;
- struct rb_node *next = rb_first(&self->mods);
-
- while (next) {
- pos = rb_entry(next, struct module, rb_node);
- next = rb_next(&pos->rb_node);
- rb_erase(&pos->rb_node, &self->mods);
- mod_dso__delete_module(pos);
- }
-}
-
-void mod_dso__delete_self(struct mod_dso *self)
-{
- mod_dso__delete_modules(self);
- free(self);
-}
-
-static void mod_dso__insert_module(struct mod_dso *self, struct module *mod)
-{
- struct rb_node **p = &self->mods.rb_node;
- struct rb_node *parent = NULL;
- const u64 hash = mod->hash;
- struct module *m;
-
- while (*p != NULL) {
- parent = *p;
- m = rb_entry(parent, struct module, rb_node);
- if (hash < m->hash)
- p = &(*p)->rb_left;
- else
- p = &(*p)->rb_right;
- }
- rb_link_node(&mod->rb_node, parent, p);
- rb_insert_color(&mod->rb_node, &self->mods);
-}
-
-struct module *mod_dso__find_module(struct mod_dso *self, const char *name)
-{
- struct rb_node *n;
- u64 hash;
- int len;
-
- if (self == NULL)
- return NULL;
-
- len = strlen(name);
- hash = crc32(name, len);
-
- n = self->mods.rb_node;
-
- while (n) {
- struct module *m = rb_entry(n, struct module, rb_node);
-
- if (hash < m->hash)
- n = n->rb_left;
- else if (hash > m->hash)
- n = n->rb_right;
- else {
- if (!strcmp(name, m->name))
- return m;
- else
- n = rb_next(&m->rb_node);
- }
- }
-
- return NULL;
-}
-
-static size_t mod_dso__fprintf_module(struct module *self, FILE *fp)
-{
- return fprintf(fp, "name:%s path:%s\n", self->name, self->path);
-}
-
-size_t mod_dso__fprintf(struct mod_dso *self, FILE *fp)
-{
- struct rb_node *nd;
- size_t ret;
-
- ret = fprintf(fp, "dso: %s\n", self->name);
-
- for (nd = rb_first(&self->mods); nd; nd = rb_next(nd)) {
- struct module *pos = rb_entry(nd, struct module, rb_node);
-
- ret += mod_dso__fprintf_module(pos, fp);
- }
-
- return ret;
-}
-
-static struct module *module__new(const char *name, const char *path)
-{
- struct module *self = calloc(1, sizeof(*self));
-
- if (!self)
- goto out_failure;
-
- self->name = calloc(1, strlen(name) + 1);
- if (!self->name)
- goto out_failure;
-
- self->path = calloc(1, strlen(path) + 1);
- if (!self->path)
- goto out_failure;
-
- strcpy(self->name, name);
- strcpy(self->path, path);
- self->hash = crc32(self->name, strlen(name));
-
- return self;
-
-out_failure:
- if (self) {
- if (self->name)
- free(self->name);
- if (self->path)
- free(self->path);
- free(self);
- }
-
- return NULL;
-}
-
-static int mod_dso__load_sections(struct module *mod)
-{
- int count = 0, path_len;
- struct dirent *entry;
- char *line = NULL;
- char *dir_path;
- DIR *dir;
- size_t n;
-
- path_len = strlen("/sys/module/");
- path_len += strlen(mod->name);
- path_len += strlen("/sections/");
-
- dir_path = calloc(1, path_len + 1);
- if (dir_path == NULL)
- goto out_failure;
-
- strcat(dir_path, "/sys/module/");
- strcat(dir_path, mod->name);
- strcat(dir_path, "/sections/");
-
- dir = opendir(dir_path);
- if (dir == NULL)
- goto out_free;
-
- while ((entry = readdir(dir))) {
- struct section *section;
- char *path, *vma;
- int line_len;
- FILE *file;
-
- if (!strcmp(".", entry->d_name) || !strcmp("..", entry->d_name))
- continue;
-
- path = calloc(1, path_len + strlen(entry->d_name) + 1);
- if (path == NULL)
- break;
- strcat(path, dir_path);
- strcat(path, entry->d_name);
-
- file = fopen(path, "r");
- if (file == NULL) {
- free(path);
- break;
- }
-
- line_len = getline(&line, &n, file);
- if (line_len < 0) {
- free(path);
- fclose(file);
- break;
- }
-
- if (!line) {
- free(path);
- fclose(file);
- break;
- }
-
- line[--line_len] = '\0'; /* \n */
-
- vma = strstr(line, "0x");
- if (!vma) {
- free(path);
- fclose(file);
- break;
- }
- vma += 2;
-
- section = section__new(entry->d_name, path);
- if (!section) {
- fprintf(stderr, "load_sections: allocation error\n");
- free(path);
- fclose(file);
- break;
- }
-
- hex2u64(vma, &section->vma);
- sec_dso__insert_section(mod->sections, section);
-
- free(path);
- fclose(file);
- count++;
- }
-
- closedir(dir);
- free(line);
- free(dir_path);
-
- return count;
-
-out_free:
- free(dir_path);
-
-out_failure:
- return count;
-}
-
-static int mod_dso__load_module_paths(struct mod_dso *self)
-{
- struct utsname uts;
- int count = 0, len, err = -1;
- char *line = NULL;
- FILE *file;
- char *dpath, *dir;
- size_t n;
-
- if (uname(&uts) < 0)
- return err;
-
- len = strlen("/lib/modules/");
- len += strlen(uts.release);
- len += strlen("/modules.dep");
-
- dpath = calloc(1, len + 1);
- if (dpath == NULL)
- return err;
-
- strcat(dpath, "/lib/modules/");
- strcat(dpath, uts.release);
- strcat(dpath, "/modules.dep");
-
- file = fopen(dpath, "r");
- if (file == NULL)
- goto out_failure;
-
- dir = dirname(dpath);
- if (!dir)
- goto out_failure;
- strcat(dir, "/");
-
- while (!feof(file)) {
- struct module *module;
- char *name, *path, *tmp;
- FILE *modfile;
- int line_len;
-
- line_len = getline(&line, &n, file);
- if (line_len < 0)
- break;
-
- if (!line)
- break;
-
- line[--line_len] = '\0'; /* \n */
-
- path = strchr(line, ':');
- if (!path)
- break;
- *path = '\0';
-
- path = strdup(line);
- if (!path)
- break;
-
- if (!strstr(path, dir)) {
- if (strncmp(path, "kernel/", 7))
- break;
-
- free(path);
- path = calloc(1, strlen(dir) + strlen(line) + 1);
- if (!path)
- break;
- strcat(path, dir);
- strcat(path, line);
- }
-
- modfile = fopen(path, "r");
- if (modfile == NULL)
- break;
- fclose(modfile);
-
- name = strdup(path);
- if (!name)
- break;
-
- name = strtok(name, "/");
- tmp = name;
-
- while (tmp) {
- tmp = strtok(NULL, "/");
- if (tmp)
- name = tmp;
- }
-
- name = strsep(&name, ".");
- if (!name)
- break;
-
- /* Quirk: replace '-' with '_' in all modules */
- for (len = strlen(name); len; len--) {
- if (*(name+len) == '-')
- *(name+len) = '_';
- }
-
- module = module__new(name, path);
- if (!module)
- break;
- mod_dso__insert_module(self, module);
-
- module->sections = sec_dso__new_dso("sections");
- if (!module->sections)
- break;
-
- module->active = mod_dso__load_sections(module);
-
- if (module->active > 0)
- count++;
- }
-
- if (feof(file))
- err = count;
- else
- fprintf(stderr, "load_module_paths: modules.dep parsing failure!\n");
-
-out_failure:
- if (dpath)
- free(dpath);
- if (file)
- fclose(file);
- if (line)
- free(line);
-
- return err;
-}
-
-int mod_dso__load_modules(struct mod_dso *dso)
-{
- int err;
-
- err = mod_dso__load_module_paths(dso);
-
- return err;
-}
diff --git a/tools/perf/util/module.h b/tools/perf/util/module.h
deleted file mode 100644
index 8a592ef641ca..000000000000
--- a/tools/perf/util/module.h
+++ /dev/null
@@ -1,53 +0,0 @@
-#ifndef _PERF_MODULE_
-#define _PERF_MODULE_ 1
-
-#include <linux/types.h>
-#include "../types.h"
-#include <linux/list.h>
-#include <linux/rbtree.h>
-
-struct section {
- struct rb_node rb_node;
- u64 hash;
- u64 vma;
- char *name;
- char *path;
-};
-
-struct sec_dso {
- struct list_head node;
- struct rb_root secs;
- struct section *(*find_section)(struct sec_dso *, const char *name);
- char name[0];
-};
-
-struct module {
- struct rb_node rb_node;
- u64 hash;
- char *name;
- char *path;
- struct sec_dso *sections;
- int active;
-};
-
-struct mod_dso {
- struct list_head node;
- struct rb_root mods;
- struct module *(*find_module)(struct mod_dso *, const char *name);
- char name[0];
-};
-
-struct sec_dso *sec_dso__new_dso(const char *name);
-void sec_dso__delete_sections(struct sec_dso *self);
-void sec_dso__delete_self(struct sec_dso *self);
-size_t sec_dso__fprintf(struct sec_dso *self, FILE *fp);
-struct section *sec_dso__find_section(struct sec_dso *self, const char *name);
-
-struct mod_dso *mod_dso__new_dso(const char *name);
-void mod_dso__delete_modules(struct mod_dso *self);
-void mod_dso__delete_self(struct mod_dso *self);
-size_t mod_dso__fprintf(struct mod_dso *self, FILE *fp);
-struct module *mod_dso__find_module(struct mod_dso *self, const char *name);
-int mod_dso__load_modules(struct mod_dso *dso);
-
-#endif /* _PERF_MODULE_ */
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 8cfb48cbbea0..b097570e9623 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -8,9 +8,10 @@
#include "cache.h"
#include "header.h"
-int nr_counters;
+int nr_counters;
struct perf_event_attr attrs[MAX_COUNTERS];
+char *filters[MAX_COUNTERS];
struct event_symbol {
u8 type;
@@ -708,7 +709,6 @@ static void store_event_type(const char *orgname)
perf_header__push_event(id, orgname);
}
-
int parse_events(const struct option *opt __used, const char *str, int unset __used)
{
struct perf_event_attr attr;
@@ -745,6 +745,28 @@ int parse_events(const struct option *opt __used, const char *str, int unset __u
return 0;
}
+int parse_filter(const struct option *opt __used, const char *str,
+ int unset __used)
+{
+ int i = nr_counters - 1;
+ int len = strlen(str);
+
+ if (i < 0 || attrs[i].type != PERF_TYPE_TRACEPOINT) {
+ fprintf(stderr,
+ "-F option should follow a -e tracepoint option\n");
+ return -1;
+ }
+
+ filters[i] = malloc(len + 1);
+ if (!filters[i]) {
+ fprintf(stderr, "not enough memory to hold filter string\n");
+ return -1;
+ }
+ strcpy(filters[i], str);
+
+ return 0;
+}
+
static const char * const event_type_descriptors[] = {
"",
"Hardware event",
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h
index 30c608112845..b8c1f64bc935 100644
--- a/tools/perf/util/parse-events.h
+++ b/tools/perf/util/parse-events.h
@@ -1,5 +1,5 @@
-#ifndef _PARSE_EVENTS_H
-#define _PARSE_EVENTS_H
+#ifndef __PERF_PARSE_EVENTS_H
+#define __PERF_PARSE_EVENTS_H
/*
* Parse symbolic events/counts passed in as options:
*/
@@ -17,11 +17,13 @@ extern struct tracepoint_path *tracepoint_id_to_path(u64 config);
extern int nr_counters;
extern struct perf_event_attr attrs[MAX_COUNTERS];
+extern char *filters[MAX_COUNTERS];
extern const char *event_name(int ctr);
extern const char *__event_name(int type, u64 config);
extern int parse_events(const struct option *opt, const char *str, int unset);
+extern int parse_filter(const struct option *opt, const char *str, int unset);
#define EVENTS_HELP_MAX (128*1024)
@@ -31,4 +33,4 @@ extern char debugfs_path[];
extern int valid_debugfs_mount(const char *debugfs);
-#endif /* _PARSE_EVENTS_H */
+#endif /* __PERF_PARSE_EVENTS_H */
diff --git a/tools/perf/util/parse-options.h b/tools/perf/util/parse-options.h
index 2ee248ff27e5..948805af43c2 100644
--- a/tools/perf/util/parse-options.h
+++ b/tools/perf/util/parse-options.h
@@ -1,5 +1,5 @@
-#ifndef PARSE_OPTIONS_H
-#define PARSE_OPTIONS_H
+#ifndef __PERF_PARSE_OPTIONS_H
+#define __PERF_PARSE_OPTIONS_H
enum parse_opt_type {
/* special types */
@@ -174,4 +174,4 @@ extern int parse_opt_verbosity_cb(const struct option *, const char *, int);
extern const char *parse_options_fix_filename(const char *prefix, const char *file);
-#endif
+#endif /* __PERF_PARSE_OPTIONS_H */
diff --git a/tools/perf/util/quote.h b/tools/perf/util/quote.h
index a5454a1d1c13..b6a019733919 100644
--- a/tools/perf/util/quote.h
+++ b/tools/perf/util/quote.h
@@ -1,5 +1,5 @@
-#ifndef QUOTE_H
-#define QUOTE_H
+#ifndef __PERF_QUOTE_H
+#define __PERF_QUOTE_H
#include <stddef.h>
#include <stdio.h>
@@ -65,4 +65,4 @@ extern void perl_quote_print(FILE *stream, const char *src);
extern void python_quote_print(FILE *stream, const char *src);
extern void tcl_quote_print(FILE *stream, const char *src);
-#endif
+#endif /* __PERF_QUOTE_H */
diff --git a/tools/perf/util/run-command.h b/tools/perf/util/run-command.h
index cc1837deba88..d79028727ce2 100644
--- a/tools/perf/util/run-command.h
+++ b/tools/perf/util/run-command.h
@@ -1,5 +1,5 @@
-#ifndef RUN_COMMAND_H
-#define RUN_COMMAND_H
+#ifndef __PERF_RUN_COMMAND_H
+#define __PERF_RUN_COMMAND_H
enum {
ERR_RUN_COMMAND_FORK = 10000,
@@ -85,4 +85,4 @@ struct async {
int start_async(struct async *async);
int finish_async(struct async *async);
-#endif
+#endif /* __PERF_RUN_COMMAND_H */
diff --git a/tools/perf/util/sigchain.h b/tools/perf/util/sigchain.h
index 618083bce0c6..1a53c11265fd 100644
--- a/tools/perf/util/sigchain.h
+++ b/tools/perf/util/sigchain.h
@@ -1,5 +1,5 @@
-#ifndef SIGCHAIN_H
-#define SIGCHAIN_H
+#ifndef __PERF_SIGCHAIN_H
+#define __PERF_SIGCHAIN_H
typedef void (*sigchain_fun)(int);
@@ -8,4 +8,4 @@ int sigchain_pop(int sig);
void sigchain_push_common(sigchain_fun f);
-#endif /* SIGCHAIN_H */
+#endif /* __PERF_SIGCHAIN_H */
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c
new file mode 100644
index 000000000000..b490354d1b23
--- /dev/null
+++ b/tools/perf/util/sort.c
@@ -0,0 +1,290 @@
+#include "sort.h"
+
+regex_t parent_regex;
+char default_parent_pattern[] = "^sys_|^do_page_fault";
+char *parent_pattern = default_parent_pattern;
+char default_sort_order[] = "comm,dso,symbol";
+char *sort_order = default_sort_order;
+int sort__need_collapse = 0;
+int sort__has_parent = 0;
+
+enum sort_type sort__first_dimension;
+
+unsigned int dsos__col_width;
+unsigned int comms__col_width;
+unsigned int threads__col_width;
+static unsigned int parent_symbol__col_width;
+char * field_sep;
+
+LIST_HEAD(hist_entry__sort_list);
+
+struct sort_entry sort_thread = {
+ .header = "Command: Pid",
+ .cmp = sort__thread_cmp,
+ .print = sort__thread_print,
+ .width = &threads__col_width,
+};
+
+struct sort_entry sort_comm = {
+ .header = "Command",
+ .cmp = sort__comm_cmp,
+ .collapse = sort__comm_collapse,
+ .print = sort__comm_print,
+ .width = &comms__col_width,
+};
+
+struct sort_entry sort_dso = {
+ .header = "Shared Object",
+ .cmp = sort__dso_cmp,
+ .print = sort__dso_print,
+ .width = &dsos__col_width,
+};
+
+struct sort_entry sort_sym = {
+ .header = "Symbol",
+ .cmp = sort__sym_cmp,
+ .print = sort__sym_print,
+};
+
+struct sort_entry sort_parent = {
+ .header = "Parent symbol",
+ .cmp = sort__parent_cmp,
+ .print = sort__parent_print,
+ .width = &parent_symbol__col_width,
+};
+
+struct sort_dimension {
+ const char *name;
+ struct sort_entry *entry;
+ int taken;
+};
+
+static struct sort_dimension sort_dimensions[] = {
+ { .name = "pid", .entry = &sort_thread, },
+ { .name = "comm", .entry = &sort_comm, },
+ { .name = "dso", .entry = &sort_dso, },
+ { .name = "symbol", .entry = &sort_sym, },
+ { .name = "parent", .entry = &sort_parent, },
+};
+
+int64_t cmp_null(void *l, void *r)
+{
+ if (!l && !r)
+ return 0;
+ else if (!l)
+ return -1;
+ else
+ return 1;
+}
+
+/* --sort pid */
+
+int64_t
+sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)
+{
+ return right->thread->pid - left->thread->pid;
+}
+
+int repsep_fprintf(FILE *fp, const char *fmt, ...)
+{
+ int n;
+ va_list ap;
+
+ va_start(ap, fmt);
+ if (!field_sep)
+ n = vfprintf(fp, fmt, ap);
+ else {
+ char *bf = NULL;
+ n = vasprintf(&bf, fmt, ap);
+ if (n > 0) {
+ char *sep = bf;
+
+ while (1) {
+ sep = strchr(sep, *field_sep);
+ if (sep == NULL)
+ break;
+ *sep = '.';
+ }
+ }
+ fputs(bf, fp);
+ free(bf);
+ }
+ va_end(ap);
+ return n;
+}
+
+size_t
+sort__thread_print(FILE *fp, struct hist_entry *self, unsigned int width)
+{
+ return repsep_fprintf(fp, "%*s:%5d", width - 6,
+ self->thread->comm ?: "", self->thread->pid);
+}
+
+size_t
+sort__comm_print(FILE *fp, struct hist_entry *self, unsigned int width)
+{
+ return repsep_fprintf(fp, "%*s", width, self->thread->comm);
+}
+
+/* --sort dso */
+
+int64_t
+sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
+{
+ struct dso *dso_l = left->map ? left->map->dso : NULL;
+ struct dso *dso_r = right->map ? right->map->dso : NULL;
+ const char *dso_name_l, *dso_name_r;
+
+ if (!dso_l || !dso_r)
+ return cmp_null(dso_l, dso_r);
+
+ if (verbose) {
+ dso_name_l = dso_l->long_name;
+ dso_name_r = dso_r->long_name;
+ } else {
+ dso_name_l = dso_l->short_name;
+ dso_name_r = dso_r->short_name;
+ }
+
+ return strcmp(dso_name_l, dso_name_r);
+}
+
+size_t
+sort__dso_print(FILE *fp, struct hist_entry *self, unsigned int width)
+{
+ if (self->map && self->map->dso) {
+ const char *dso_name = !verbose ? self->map->dso->short_name :
+ self->map->dso->long_name;
+ return repsep_fprintf(fp, "%-*s", width, dso_name);
+ }
+
+ return repsep_fprintf(fp, "%*llx", width, (u64)self->ip);
+}
+
+/* --sort symbol */
+
+int64_t
+sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
+{
+ u64 ip_l, ip_r;
+
+ if (left->sym == right->sym)
+ return 0;
+
+ ip_l = left->sym ? left->sym->start : left->ip;
+ ip_r = right->sym ? right->sym->start : right->ip;
+
+ return (int64_t)(ip_r - ip_l);
+}
+
+
+size_t
+sort__sym_print(FILE *fp, struct hist_entry *self, unsigned int width __used)
+{
+ size_t ret = 0;
+
+ if (verbose) {
+ char o = self->map ? dso__symtab_origin(self->map->dso) : '!';
+ ret += repsep_fprintf(fp, "%#018llx %c ", (u64)self->ip, o);
+ }
+
+ ret += repsep_fprintf(fp, "[%c] ", self->level);
+ if (self->sym)
+ ret += repsep_fprintf(fp, "%s", self->sym->name);
+ else
+ ret += repsep_fprintf(fp, "%#016llx", (u64)self->ip);
+
+ return ret;
+}
+
+/* --sort comm */
+
+int64_t
+sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)
+{
+ return right->thread->pid - left->thread->pid;
+}
+
+int64_t
+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)
+ return cmp_null(comm_l, comm_r);
+
+ return strcmp(comm_l, comm_r);
+}
+
+/* --sort parent */
+
+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);
+}
+
+size_t
+sort__parent_print(FILE *fp, struct hist_entry *self, unsigned int width)
+{
+ return repsep_fprintf(fp, "%-*s", width,
+ self->parent ? self->parent->name : "[other]");
+}
+
+int sort_dimension__add(const char *tok)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) {
+ struct sort_dimension *sd = &sort_dimensions[i];
+
+ if (sd->taken)
+ continue;
+
+ if (strncasecmp(tok, sd->name, strlen(tok)))
+ continue;
+
+ if (sd->entry->collapse)
+ sort__need_collapse = 1;
+
+ if (sd->entry == &sort_parent) {
+ int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED);
+ if (ret) {
+ char err[BUFSIZ];
+
+ regerror(ret, &parent_regex, err, sizeof(err));
+ fprintf(stderr, "Invalid regex: %s\n%s",
+ parent_pattern, err);
+ exit(-1);
+ }
+ sort__has_parent = 1;
+ }
+
+ if (list_empty(&hist_entry__sort_list)) {
+ if (!strcmp(sd->name, "pid"))
+ sort__first_dimension = SORT_PID;
+ else if (!strcmp(sd->name, "comm"))
+ sort__first_dimension = SORT_COMM;
+ else if (!strcmp(sd->name, "dso"))
+ sort__first_dimension = SORT_DSO;
+ else if (!strcmp(sd->name, "symbol"))
+ sort__first_dimension = SORT_SYM;
+ else if (!strcmp(sd->name, "parent"))
+ sort__first_dimension = SORT_PARENT;
+ }
+
+ list_add_tail(&sd->entry->list, &hist_entry__sort_list);
+ sd->taken = 1;
+
+ return 0;
+ }
+
+ return -ESRCH;
+}
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h
new file mode 100644
index 000000000000..333e664ff45f
--- /dev/null
+++ b/tools/perf/util/sort.h
@@ -0,0 +1,99 @@
+#ifndef __PERF_SORT_H
+#define __PERF_SORT_H
+#include "../builtin.h"
+
+#include "util.h"
+
+#include "color.h"
+#include <linux/list.h>
+#include "cache.h"
+#include <linux/rbtree.h>
+#include "symbol.h"
+#include "string.h"
+#include "callchain.h"
+#include "strlist.h"
+#include "values.h"
+
+#include "../perf.h"
+#include "debug.h"
+#include "header.h"
+
+#include "parse-options.h"
+#include "parse-events.h"
+
+#include "thread.h"
+#include "sort.h"
+
+extern regex_t parent_regex;
+extern char *sort_order;
+extern char default_parent_pattern[];
+extern char *parent_pattern;
+extern char default_sort_order[];
+extern int sort__need_collapse;
+extern int sort__has_parent;
+extern char *field_sep;
+extern struct sort_entry sort_comm;
+extern struct sort_entry sort_dso;
+extern struct sort_entry sort_sym;
+extern struct sort_entry sort_parent;
+extern unsigned int dsos__col_width;
+extern unsigned int comms__col_width;
+extern unsigned int threads__col_width;
+extern enum sort_type sort__first_dimension;
+
+struct hist_entry {
+ struct rb_node rb_node;
+ u64 count;
+ struct thread *thread;
+ struct map *map;
+ struct symbol *sym;
+ u64 ip;
+ char level;
+ struct symbol *parent;
+ struct callchain_node callchain;
+ struct rb_root sorted_chain;
+};
+
+enum sort_type {
+ SORT_PID,
+ SORT_COMM,
+ SORT_DSO,
+ SORT_SYM,
+ SORT_PARENT
+};
+
+/*
+ * configurable sorting bits
+ */
+
+struct sort_entry {
+ struct list_head list;
+
+ const char *header;
+
+ int64_t (*cmp)(struct hist_entry *, struct hist_entry *);
+ int64_t (*collapse)(struct hist_entry *, struct hist_entry *);
+ size_t (*print)(FILE *fp, struct hist_entry *, unsigned int width);
+ unsigned int *width;
+ bool elide;
+};
+
+extern struct sort_entry sort_thread;
+extern struct list_head hist_entry__sort_list;
+
+extern int repsep_fprintf(FILE *fp, const char *fmt, ...);
+extern size_t sort__thread_print(FILE *, struct hist_entry *, unsigned int);
+extern size_t sort__comm_print(FILE *, struct hist_entry *, unsigned int);
+extern size_t sort__dso_print(FILE *, struct hist_entry *, unsigned int);
+extern size_t sort__sym_print(FILE *, struct hist_entry *, unsigned int __used);
+extern int64_t cmp_null(void *, void *);
+extern int64_t sort__thread_cmp(struct hist_entry *, struct hist_entry *);
+extern int64_t sort__comm_cmp(struct hist_entry *, struct hist_entry *);
+extern int64_t sort__comm_collapse(struct hist_entry *, struct hist_entry *);
+extern int64_t sort__dso_cmp(struct hist_entry *, struct hist_entry *);
+extern int64_t sort__sym_cmp(struct hist_entry *, struct hist_entry *);
+extern int64_t sort__parent_cmp(struct hist_entry *, struct hist_entry *);
+extern size_t sort__parent_print(FILE *, struct hist_entry *, unsigned int);
+extern int sort_dimension__add(const char *);
+
+#endif /* __PERF_SORT_H */
diff --git a/tools/perf/util/strbuf.h b/tools/perf/util/strbuf.h
index d2aa86c014c1..a3d121d6c83e 100644
--- a/tools/perf/util/strbuf.h
+++ b/tools/perf/util/strbuf.h
@@ -1,5 +1,5 @@
-#ifndef STRBUF_H
-#define STRBUF_H
+#ifndef __PERF_STRBUF_H
+#define __PERF_STRBUF_H
/*
* Strbuf's can be use in many ways: as a byte array, or to store arbitrary
@@ -134,4 +134,4 @@ extern int launch_editor(const char *path, struct strbuf *buffer, const char *co
extern int strbuf_branchname(struct strbuf *sb, const char *name);
extern int strbuf_check_branch_ref(struct strbuf *sb, const char *name);
-#endif /* STRBUF_H */
+#endif /* __PERF_STRBUF_H */
diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c
index c93eca9a7be3..04743d3e9039 100644
--- a/tools/perf/util/string.c
+++ b/tools/perf/util/string.c
@@ -1,3 +1,4 @@
+#include <string.h>
#include "string.h"
static int hex(char ch)
@@ -32,3 +33,13 @@ int hex2u64(const char *ptr, u64 *long_val)
return p - ptr;
}
+
+char *strxfrchar(char *s, char from, char to)
+{
+ char *p = s;
+
+ while ((p = strchr(p, from)) != NULL)
+ *p++ = to;
+
+ return s;
+}
diff --git a/tools/perf/util/string.h b/tools/perf/util/string.h
index bf39dfadfd24..2c84bf65ba0f 100644
--- a/tools/perf/util/string.h
+++ b/tools/perf/util/string.h
@@ -1,11 +1,12 @@
-#ifndef _PERF_STRING_H_
-#define _PERF_STRING_H_
+#ifndef __PERF_STRING_H_
+#define __PERF_STRING_H_
#include "types.h"
int hex2u64(const char *ptr, u64 *val);
+char *strxfrchar(char *s, char from, char to);
#define _STR(x) #x
#define STR(x) _STR(x)
-#endif
+#endif /* __PERF_STRING_H */
diff --git a/tools/perf/util/strlist.h b/tools/perf/util/strlist.h
index 921818e44a54..cb4659306d7b 100644
--- a/tools/perf/util/strlist.h
+++ b/tools/perf/util/strlist.h
@@ -1,5 +1,5 @@
-#ifndef STRLIST_H_
-#define STRLIST_H_
+#ifndef __PERF_STRLIST_H
+#define __PERF_STRLIST_H
#include <linux/rbtree.h>
#include <stdbool.h>
@@ -36,4 +36,4 @@ static inline unsigned int strlist__nr_entries(const struct strlist *self)
}
int strlist__parse_list(struct strlist *self, const char *s);
-#endif /* STRLIST_H_ */
+#endif /* __PERF_STRLIST_H */
diff --git a/tools/perf/util/svghelper.c b/tools/perf/util/svghelper.c
index 856655d8b0b8..b3637db025a2 100644
--- a/tools/perf/util/svghelper.c
+++ b/tools/perf/util/svghelper.c
@@ -103,7 +103,7 @@ void open_svg(const char *filename, int cpus, int rows, u64 start, u64 end)
fprintf(svgfile, " rect.process2 { fill:rgb(180,180,180); fill-opacity:0.9; stroke-width:0; stroke:rgb( 0, 0, 0); } \n");
fprintf(svgfile, " rect.sample { fill:rgb( 0, 0,255); fill-opacity:0.8; stroke-width:0; stroke:rgb( 0, 0, 0); } \n");
fprintf(svgfile, " rect.blocked { fill:rgb(255, 0, 0); fill-opacity:0.5; stroke-width:0; stroke:rgb( 0, 0, 0); } \n");
- fprintf(svgfile, " rect.waiting { fill:rgb(214,214, 0); fill-opacity:0.3; stroke-width:0; stroke:rgb( 0, 0, 0); } \n");
+ fprintf(svgfile, " rect.waiting { fill:rgb(224,214, 0); fill-opacity:0.8; stroke-width:0; stroke:rgb( 0, 0, 0); } \n");
fprintf(svgfile, " rect.WAITING { fill:rgb(255,214, 48); fill-opacity:0.6; stroke-width:0; stroke:rgb( 0, 0, 0); } \n");
fprintf(svgfile, " rect.cpu { fill:rgb(192,192,192); fill-opacity:0.2; stroke-width:0.5; stroke:rgb(128,128,128); } \n");
fprintf(svgfile, " rect.pstate { fill:rgb(128,128,128); fill-opacity:0.8; stroke-width:0; } \n");
diff --git a/tools/perf/util/svghelper.h b/tools/perf/util/svghelper.h
index cd93195aedb3..e0781989cc31 100644
--- a/tools/perf/util/svghelper.h
+++ b/tools/perf/util/svghelper.h
@@ -1,5 +1,5 @@
-#ifndef _INCLUDE_GUARD_SVG_HELPER_
-#define _INCLUDE_GUARD_SVG_HELPER_
+#ifndef __PERF_SVGHELPER_H
+#define __PERF_SVGHELPER_H
#include "types.h"
@@ -25,4 +25,4 @@ extern void svg_close(void);
extern int svg_page_width;
-#endif
+#endif /* __PERF_SVGHELPER_H */
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 47ea0609a760..8f0208ce237a 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -2,14 +2,14 @@
#include "../perf.h"
#include "string.h"
#include "symbol.h"
+#include "thread.h"
#include "debug.h"
#include <libelf.h>
#include <gelf.h>
#include <elf.h>
-
-const char *sym_hist_filter;
+#include <sys/utsname.h>
enum dso_origin {
DSO__ORIG_KERNEL = 0,
@@ -18,12 +18,65 @@ enum dso_origin {
DSO__ORIG_UBUNTU,
DSO__ORIG_BUILDID,
DSO__ORIG_DSO,
+ DSO__ORIG_KMODULE,
DSO__ORIG_NOT_FOUND,
};
-static struct symbol *symbol__new(u64 start, u64 len,
- const char *name, unsigned int priv_size,
- u64 obj_start, int v)
+static void dsos__add(struct dso *dso);
+static struct dso *dsos__find(const char *name);
+static struct map *map__new2(u64 start, struct dso *dso);
+static void kernel_maps__insert(struct map *map);
+
+static struct rb_root kernel_maps;
+
+static void dso__fixup_sym_end(struct dso *self)
+{
+ struct rb_node *nd, *prevnd = rb_first(&self->syms);
+ struct symbol *curr, *prev;
+
+ if (prevnd == NULL)
+ return;
+
+ curr = rb_entry(prevnd, struct symbol, rb_node);
+
+ for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) {
+ prev = curr;
+ curr = rb_entry(nd, struct symbol, rb_node);
+
+ if (prev->end == prev->start)
+ prev->end = curr->start - 1;
+ }
+
+ /* Last entry */
+ if (curr->end == curr->start)
+ curr->end = roundup(curr->start, 4096);
+}
+
+static void kernel_maps__fixup_end(void)
+{
+ struct map *prev, *curr;
+ struct rb_node *nd, *prevnd = rb_first(&kernel_maps);
+
+ if (prevnd == NULL)
+ return;
+
+ curr = rb_entry(prevnd, struct map, rb_node);
+
+ for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) {
+ prev = curr;
+ curr = rb_entry(nd, struct map, rb_node);
+ prev->end = curr->start - 1;
+ }
+
+ nd = rb_last(&curr->dso->syms);
+ if (nd) {
+ struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
+ curr->end = sym->end;
+ }
+}
+
+static struct symbol *symbol__new(u64 start, u64 len, const char *name,
+ unsigned int priv_size)
{
size_t namelen = strlen(name) + 1;
struct symbol *self = calloc(1, priv_size + sizeof(*self) + namelen);
@@ -31,23 +84,15 @@ static struct symbol *symbol__new(u64 start, u64 len,
if (!self)
return NULL;
- if (v >= 2)
- printf("new symbol: %016Lx [%08lx]: %s, hist: %p, obj_start: %p\n",
- (u64)start, (unsigned long)len, name, self->hist, (void *)(unsigned long)obj_start);
-
- self->obj_start= obj_start;
- self->hist = NULL;
- self->hist_sum = 0;
-
- if (sym_hist_filter && !strcmp(name, sym_hist_filter))
- self->hist = calloc(sizeof(u64), len);
-
if (priv_size) {
memset(self, 0, priv_size);
self = ((void *)self) + priv_size;
}
self->start = start;
self->end = len ? start + len - 1 : start;
+
+ pr_debug3("%s: %s %#Lx-%#Lx\n", __func__, name, start, self->end);
+
memcpy(self->name, name, namelen);
return self;
@@ -60,12 +105,8 @@ static void symbol__delete(struct symbol *self, unsigned int priv_size)
static size_t symbol__fprintf(struct symbol *self, FILE *fp)
{
- if (!self->module)
- return fprintf(fp, " %llx-%llx %s\n",
+ return fprintf(fp, " %llx-%llx %s\n",
self->start, self->end, self->name);
- else
- return fprintf(fp, " %llx-%llx %s \t[%s]\n",
- self->start, self->end, self->name, self->module->name);
}
struct dso *dso__new(const char *name, unsigned int sym_priv_size)
@@ -74,6 +115,8 @@ struct dso *dso__new(const char *name, unsigned int sym_priv_size)
if (self != NULL) {
strcpy(self->name, name);
+ self->long_name = self->name;
+ self->short_name = self->name;
self->syms = RB_ROOT;
self->sym_priv_size = sym_priv_size;
self->find_symbol = dso__find_symbol;
@@ -100,6 +143,8 @@ static void dso__delete_symbols(struct dso *self)
void dso__delete(struct dso *self)
{
dso__delete_symbols(self);
+ if (self->long_name != self->name)
+ free(self->long_name);
free(self);
}
@@ -147,7 +192,7 @@ struct symbol *dso__find_symbol(struct dso *self, u64 ip)
size_t dso__fprintf(struct dso *self, FILE *fp)
{
- size_t ret = fprintf(fp, "dso: %s\n", self->name);
+ size_t ret = fprintf(fp, "dso: %s\n", self->short_name);
struct rb_node *nd;
for (nd = rb_first(&self->syms); nd; nd = rb_next(nd)) {
@@ -158,13 +203,16 @@ size_t dso__fprintf(struct dso *self, FILE *fp)
return ret;
}
-static int dso__load_kallsyms(struct dso *self, symbol_filter_t filter, int v)
+/*
+ * Loads the function entries in /proc/kallsyms into kernel_map->dso,
+ * so that we can in the next step set the symbol ->end address and then
+ * call kernel_maps__split_kallsyms.
+ */
+static int kernel_maps__load_all_kallsyms(void)
{
- struct rb_node *nd, *prevnd;
char *line = NULL;
size_t n;
FILE *file = fopen("/proc/kallsyms", "r");
- int count = 0;
if (file == NULL)
goto out_failure;
@@ -174,6 +222,7 @@ static int dso__load_kallsyms(struct dso *self, symbol_filter_t filter, int v)
struct symbol *sym;
int line_len, len;
char symbol_type;
+ char *symbol_name;
line_len = getline(&line, &n, file);
if (line_len < 0)
@@ -196,44 +245,24 @@ static int dso__load_kallsyms(struct dso *self, symbol_filter_t filter, int v)
*/
if (symbol_type != 'T' && symbol_type != 'W')
continue;
+
+ symbol_name = line + len + 2;
/*
- * Well fix up the end later, when we have all sorted.
+ * Will fix up the end later, when we have all symbols sorted.
*/
- sym = symbol__new(start, 0xdead, line + len + 2,
- self->sym_priv_size, 0, v);
+ sym = symbol__new(start, 0, symbol_name,
+ kernel_map->dso->sym_priv_size);
if (sym == NULL)
goto out_delete_line;
- if (filter && filter(self, sym))
- symbol__delete(sym, self->sym_priv_size);
- else {
- dso__insert_symbol(self, sym);
- count++;
- }
- }
-
- /*
- * Now that we have all sorted out, just set the ->end of all
- * symbols
- */
- prevnd = rb_first(&self->syms);
-
- if (prevnd == NULL)
- goto out_delete_line;
-
- for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) {
- struct symbol *prev = rb_entry(prevnd, struct symbol, rb_node),
- *curr = rb_entry(nd, struct symbol, rb_node);
-
- prev->end = curr->start - 1;
- prevnd = nd;
+ dso__insert_symbol(kernel_map->dso, sym);
}
free(line);
fclose(file);
- return count;
+ return 0;
out_delete_line:
free(line);
@@ -241,14 +270,124 @@ out_failure:
return -1;
}
-static int dso__load_perf_map(struct dso *self, symbol_filter_t filter, int v)
+/*
+ * Split the symbols into maps, making sure there are no overlaps, i.e. the
+ * kernel range is broken in several maps, named [kernel].N, as we don't have
+ * the original ELF section names vmlinux have.
+ */
+static int kernel_maps__split_kallsyms(symbol_filter_t filter, int use_modules)
+{
+ struct map *map = kernel_map;
+ struct symbol *pos;
+ int count = 0;
+ struct rb_node *next = rb_first(&kernel_map->dso->syms);
+ int kernel_range = 0;
+
+ while (next) {
+ char *module;
+
+ pos = rb_entry(next, struct symbol, rb_node);
+ next = rb_next(&pos->rb_node);
+
+ module = strchr(pos->name, '\t');
+ if (module) {
+ if (!use_modules)
+ goto delete_symbol;
+
+ *module++ = '\0';
+
+ if (strcmp(map->dso->name, module)) {
+ map = kernel_maps__find_by_dso_name(module);
+ if (!map) {
+ pr_err("/proc/{kallsyms,modules} "
+ "inconsistency!\n");
+ return -1;
+ }
+ }
+ /*
+ * So that we look just like we get from .ko files,
+ * i.e. not prelinked, relative to map->start.
+ */
+ pos->start = map->map_ip(map, pos->start);
+ pos->end = map->map_ip(map, pos->end);
+ } else if (map != kernel_map) {
+ char dso_name[PATH_MAX];
+ struct dso *dso;
+
+ snprintf(dso_name, sizeof(dso_name), "[kernel].%d",
+ kernel_range++);
+
+ dso = dso__new(dso_name,
+ kernel_map->dso->sym_priv_size);
+ if (dso == NULL)
+ return -1;
+
+ map = map__new2(pos->start, dso);
+ if (map == NULL) {
+ dso__delete(dso);
+ return -1;
+ }
+
+ map->map_ip = map->unmap_ip = identity__map_ip;
+ kernel_maps__insert(map);
+ ++kernel_range;
+ }
+
+ if (filter && filter(map, pos)) {
+delete_symbol:
+ rb_erase(&pos->rb_node, &kernel_map->dso->syms);
+ symbol__delete(pos, kernel_map->dso->sym_priv_size);
+ } else {
+ if (map != kernel_map) {
+ rb_erase(&pos->rb_node, &kernel_map->dso->syms);
+ dso__insert_symbol(map->dso, pos);
+ }
+ count++;
+ }
+ }
+
+ return count;
+}
+
+
+static int kernel_maps__load_kallsyms(symbol_filter_t filter, int use_modules)
+{
+ if (kernel_maps__load_all_kallsyms())
+ return -1;
+
+ dso__fixup_sym_end(kernel_map->dso);
+
+ return kernel_maps__split_kallsyms(filter, use_modules);
+}
+
+static size_t kernel_maps__fprintf(FILE *fp)
+{
+ size_t printed = fprintf(fp, "Kernel maps:\n");
+ struct rb_node *nd;
+
+ for (nd = rb_first(&kernel_maps); nd; nd = rb_next(nd)) {
+ struct map *pos = rb_entry(nd, struct map, rb_node);
+
+ printed += fprintf(fp, "Map:");
+ printed += map__fprintf(pos, fp);
+ if (verbose > 1) {
+ printed += dso__fprintf(pos->dso, fp);
+ printed += fprintf(fp, "--\n");
+ }
+ }
+
+ return printed + fprintf(fp, "END kernel maps\n");
+}
+
+static int dso__load_perf_map(struct dso *self, struct map *map,
+ symbol_filter_t filter)
{
char *line = NULL;
size_t n;
FILE *file;
int nr_syms = 0;
- file = fopen(self->name, "r");
+ file = fopen(self->long_name, "r");
if (file == NULL)
goto out_failure;
@@ -279,12 +418,12 @@ static int dso__load_perf_map(struct dso *self, symbol_filter_t filter, int v)
continue;
sym = symbol__new(start, size, line + len,
- self->sym_priv_size, start, v);
+ self->sym_priv_size);
if (sym == NULL)
goto out_delete_line;
- if (filter && filter(self, sym))
+ if (filter && filter(map, sym))
symbol__delete(sym, self->sym_priv_size);
else {
dso__insert_symbol(self, sym);
@@ -393,7 +532,7 @@ static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep,
* And always look at the original dso, not at debuginfo packages, that
* have the PLT data stripped out (shdr_rel_plt.sh_type == SHT_NOBITS).
*/
-static int dso__synthesize_plt_symbols(struct dso *self, int v)
+static int dso__synthesize_plt_symbols(struct dso *self)
{
uint32_t nr_rel_entries, idx;
GElf_Sym sym;
@@ -409,7 +548,7 @@ static int dso__synthesize_plt_symbols(struct dso *self, int v)
Elf *elf;
int nr = 0, symidx, fd, err = 0;
- fd = open(self->name, O_RDONLY);
+ fd = open(self->long_name, O_RDONLY);
if (fd < 0)
goto out;
@@ -477,7 +616,7 @@ static int dso__synthesize_plt_symbols(struct dso *self, int v)
"%s@plt", elf_sym__name(&sym, symstrs));
f = symbol__new(plt_offset, shdr_plt.sh_entsize,
- sympltname, self->sym_priv_size, 0, v);
+ sympltname, self->sym_priv_size);
if (!f)
goto out_elf_end;
@@ -495,7 +634,7 @@ static int dso__synthesize_plt_symbols(struct dso *self, int v)
"%s@plt", elf_sym__name(&sym, symstrs));
f = symbol__new(plt_offset, shdr_plt.sh_entsize,
- sympltname, self->sym_priv_size, 0, v);
+ sympltname, self->sym_priv_size);
if (!f)
goto out_elf_end;
@@ -513,14 +652,18 @@ out_close:
if (err == 0)
return nr;
out:
- fprintf(stderr, "%s: problems reading %s PLT info.\n",
- __func__, self->name);
+ pr_warning("%s: problems reading %s PLT info.\n",
+ __func__, self->long_name);
return 0;
}
-static int dso__load_sym(struct dso *self, int fd, const char *name,
- symbol_filter_t filter, int v, struct module *mod)
+static int dso__load_sym(struct dso *self, struct map *map, const char *name,
+ int fd, symbol_filter_t filter, int kernel,
+ int kmodule)
{
+ struct map *curr_map = map;
+ struct dso *curr_dso = self;
+ size_t dso_name_len = strlen(self->short_name);
Elf_Data *symstrs, *secstrs;
uint32_t nr_syms;
int err = -1;
@@ -531,19 +674,16 @@ static int dso__load_sym(struct dso *self, int fd, const char *name,
GElf_Sym sym;
Elf_Scn *sec, *sec_strndx;
Elf *elf;
- int nr = 0, kernel = !strcmp("[kernel]", self->name);
+ int nr = 0;
elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
if (elf == NULL) {
- if (v)
- fprintf(stderr, "%s: cannot read %s ELF file.\n",
- __func__, name);
+ pr_err("%s: cannot read %s ELF file.\n", __func__, name);
goto out_close;
}
if (gelf_getehdr(elf, &ehdr) == NULL) {
- if (v)
- fprintf(stderr, "%s: cannot get elf header.\n", __func__);
+ pr_err("%s: cannot get elf header.\n", __func__);
goto out_elf_end;
}
@@ -587,9 +727,7 @@ static int dso__load_sym(struct dso *self, int fd, const char *name,
elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) {
struct symbol *f;
const char *elf_name;
- char *demangled;
- u64 obj_start;
- struct section *section = NULL;
+ char *demangled = NULL;
int is_label = elf_sym__is_label(&sym);
const char *section_name;
@@ -605,52 +743,85 @@ static int dso__load_sym(struct dso *self, int fd, const char *name,
if (is_label && !elf_sec__is_text(&shdr, secstrs))
continue;
+ elf_name = elf_sym__name(&sym, symstrs);
section_name = elf_sec__name(&shdr, secstrs);
- obj_start = sym.st_value;
- if (self->adjust_symbols) {
- if (v >= 2)
- printf("adjusting symbol: st_value: %Lx sh_addr: %Lx sh_offset: %Lx\n",
- (u64)sym.st_value, (u64)shdr.sh_addr, (u64)shdr.sh_offset);
+ if (kernel || kmodule) {
+ char dso_name[PATH_MAX];
- sym.st_value -= shdr.sh_addr - shdr.sh_offset;
- }
+ if (strcmp(section_name,
+ curr_dso->short_name + dso_name_len) == 0)
+ goto new_symbol;
- if (mod) {
- section = mod->sections->find_section(mod->sections, section_name);
- if (section)
- sym.st_value += section->vma;
- else {
- fprintf(stderr, "dso__load_sym() module %s lookup of %s failed\n",
- mod->name, section_name);
- goto out_elf_end;
+ if (strcmp(section_name, ".text") == 0) {
+ curr_map = map;
+ curr_dso = self;
+ goto new_symbol;
}
+
+ snprintf(dso_name, sizeof(dso_name),
+ "%s%s", self->short_name, section_name);
+
+ curr_map = kernel_maps__find_by_dso_name(dso_name);
+ if (curr_map == NULL) {
+ u64 start = sym.st_value;
+
+ if (kmodule)
+ start += map->start + shdr.sh_offset;
+
+ curr_dso = dso__new(dso_name, self->sym_priv_size);
+ if (curr_dso == NULL)
+ goto out_elf_end;
+ curr_map = map__new2(start, curr_dso);
+ if (curr_map == NULL) {
+ dso__delete(curr_dso);
+ goto out_elf_end;
+ }
+ curr_map->map_ip = identity__map_ip;
+ curr_map->unmap_ip = identity__map_ip;
+ curr_dso->origin = DSO__ORIG_KERNEL;
+ kernel_maps__insert(curr_map);
+ dsos__add(curr_dso);
+ } else
+ curr_dso = curr_map->dso;
+
+ goto new_symbol;
+ }
+
+ if (curr_dso->adjust_symbols) {
+ pr_debug2("adjusting symbol: st_value: %Lx sh_addr: "
+ "%Lx sh_offset: %Lx\n", (u64)sym.st_value,
+ (u64)shdr.sh_addr, (u64)shdr.sh_offset);
+ sym.st_value -= shdr.sh_addr - shdr.sh_offset;
}
/*
* We need to figure out if the object was created from C++ sources
* DWARF DW_compile_unit has this, but we don't always have access
* to it...
*/
- elf_name = elf_sym__name(&sym, symstrs);
demangled = bfd_demangle(NULL, elf_name, DMGL_PARAMS | DMGL_ANSI);
if (demangled != NULL)
elf_name = demangled;
-
+new_symbol:
f = symbol__new(sym.st_value, sym.st_size, elf_name,
- self->sym_priv_size, obj_start, v);
+ curr_dso->sym_priv_size);
free(demangled);
if (!f)
goto out_elf_end;
- if (filter && filter(self, f))
- symbol__delete(f, self->sym_priv_size);
+ if (filter && filter(curr_map, f))
+ symbol__delete(f, curr_dso->sym_priv_size);
else {
- f->module = mod;
- dso__insert_symbol(self, f);
+ dso__insert_symbol(curr_dso, f);
nr++;
}
}
+ /*
+ * For misannotated, zeroed, ASM function sizes.
+ */
+ if (nr > 0)
+ dso__fixup_sym_end(self);
err = nr;
out_elf_end:
elf_end(elf);
@@ -660,7 +831,7 @@ out_close:
#define BUILD_ID_SIZE 128
-static char *dso__read_build_id(struct dso *self, int v)
+static char *dso__read_build_id(struct dso *self)
{
int i;
GElf_Ehdr ehdr;
@@ -670,22 +841,20 @@ static char *dso__read_build_id(struct dso *self, int v)
char *build_id = NULL, *bid;
unsigned char *raw;
Elf *elf;
- int fd = open(self->name, O_RDONLY);
+ int fd = open(self->long_name, O_RDONLY);
if (fd < 0)
goto out;
elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
if (elf == NULL) {
- if (v)
- fprintf(stderr, "%s: cannot read %s ELF file.\n",
- __func__, self->name);
+ pr_err("%s: cannot read %s ELF file.\n", __func__,
+ self->long_name);
goto out_close;
}
if (gelf_getehdr(elf, &ehdr) == NULL) {
- if (v)
- fprintf(stderr, "%s: cannot get elf header.\n", __func__);
+ pr_err("%s: cannot get elf header.\n", __func__);
goto out_elf_end;
}
@@ -707,8 +876,7 @@ static char *dso__read_build_id(struct dso *self, int v)
++raw;
bid += 2;
}
- if (v >= 2)
- printf("%s(%s): %s\n", __func__, self->name, build_id);
+ pr_debug2("%s(%s): %s\n", __func__, self->long_name, build_id);
out_elf_end:
elf_end(elf);
out_close:
@@ -726,6 +894,7 @@ char dso__symtab_origin(const struct dso *self)
[DSO__ORIG_UBUNTU] = 'u',
[DSO__ORIG_BUILDID] = 'b',
[DSO__ORIG_DSO] = 'd',
+ [DSO__ORIG_KMODULE] = 'K',
};
if (self == NULL || self->origin == DSO__ORIG_NOT_FOUND)
@@ -733,7 +902,7 @@ char dso__symtab_origin(const struct dso *self)
return origin[self->origin];
}
-int dso__load(struct dso *self, symbol_filter_t filter, int v)
+int dso__load(struct dso *self, struct map *map, symbol_filter_t filter)
{
int size = PATH_MAX;
char *name = malloc(size), *build_id = NULL;
@@ -746,7 +915,7 @@ int dso__load(struct dso *self, symbol_filter_t filter, int v)
self->adjust_symbols = 0;
if (strncmp(self->name, "/tmp/perf-", 10) == 0) {
- ret = dso__load_perf_map(self, filter, v);
+ ret = dso__load_perf_map(self, map, filter);
self->origin = ret > 0 ? DSO__ORIG_JAVA_JIT :
DSO__ORIG_NOT_FOUND;
return ret;
@@ -759,13 +928,15 @@ more:
self->origin++;
switch (self->origin) {
case DSO__ORIG_FEDORA:
- snprintf(name, size, "/usr/lib/debug%s.debug", self->name);
+ snprintf(name, size, "/usr/lib/debug%s.debug",
+ self->long_name);
break;
case DSO__ORIG_UBUNTU:
- snprintf(name, size, "/usr/lib/debug%s", self->name);
+ snprintf(name, size, "/usr/lib/debug%s",
+ self->long_name);
break;
case DSO__ORIG_BUILDID:
- build_id = dso__read_build_id(self, v);
+ build_id = dso__read_build_id(self);
if (build_id != NULL) {
snprintf(name, size,
"/usr/lib/debug/.build-id/%.2s/%s.debug",
@@ -776,7 +947,7 @@ more:
self->origin++;
/* Fall thru */
case DSO__ORIG_DSO:
- snprintf(name, size, "%s", self->name);
+ snprintf(name, size, "%s", self->long_name);
break;
default:
@@ -786,7 +957,7 @@ more:
fd = open(name, O_RDONLY);
} while (fd < 0);
- ret = dso__load_sym(self, fd, name, filter, v, NULL);
+ ret = dso__load_sym(self, map, name, fd, filter, 0, 0);
close(fd);
/*
@@ -796,7 +967,7 @@ more:
goto more;
if (ret > 0) {
- int nr_plt = dso__synthesize_plt_symbols(self, v);
+ int nr_plt = dso__synthesize_plt_symbols(self);
if (nr_plt > 0)
ret += nr_plt;
}
@@ -807,137 +978,321 @@ out:
return ret;
}
-static int dso__load_module(struct dso *self, struct mod_dso *mods, const char *name,
- symbol_filter_t filter, int v)
+struct map *kernel_map;
+
+static void kernel_maps__insert(struct map *map)
{
- struct module *mod = mod_dso__find_module(mods, name);
- int err = 0, fd;
+ maps__insert(&kernel_maps, map);
+}
- if (mod == NULL || !mod->active)
- return err;
+struct symbol *kernel_maps__find_symbol(u64 ip, struct map **mapp)
+{
+ struct map *map = maps__find(&kernel_maps, ip);
- fd = open(mod->path, O_RDONLY);
+ if (mapp)
+ *mapp = map;
- if (fd < 0)
+ if (map) {
+ ip = map->map_ip(map, ip);
+ return map->dso->find_symbol(map->dso, ip);
+ }
+
+ return NULL;
+}
+
+struct map *kernel_maps__find_by_dso_name(const char *name)
+{
+ struct rb_node *nd;
+
+ for (nd = rb_first(&kernel_maps); nd; nd = rb_next(nd)) {
+ struct map *map = rb_entry(nd, struct map, rb_node);
+
+ if (map->dso && strcmp(map->dso->name, name) == 0)
+ return map;
+ }
+
+ return NULL;
+}
+
+static int dso__load_module_sym(struct dso *self, struct map *map,
+ symbol_filter_t filter)
+{
+ int err = 0, fd = open(self->long_name, O_RDONLY);
+
+ if (fd < 0) {
+ pr_err("%s: cannot open %s\n", __func__, self->long_name);
return err;
+ }
- err = dso__load_sym(self, fd, name, filter, v, mod);
+ err = dso__load_sym(self, map, self->long_name, fd, filter, 0, 1);
close(fd);
return err;
}
-int dso__load_modules(struct dso *self, symbol_filter_t filter, int v)
+static int dsos__load_modules_sym_dir(char *dirname, symbol_filter_t filter)
{
- struct mod_dso *mods = mod_dso__new_dso("modules");
- struct module *pos;
- struct rb_node *next;
- int err, count = 0;
+ struct dirent *dent;
+ int nr_symbols = 0, err;
+ DIR *dir = opendir(dirname);
- err = mod_dso__load_modules(mods);
+ if (!dir) {
+ pr_err("%s: cannot open %s dir\n", __func__, dirname);
+ return -1;
+ }
- if (err <= 0)
- return err;
+ while ((dent = readdir(dir)) != NULL) {
+ char path[PATH_MAX];
+
+ if (dent->d_type == DT_DIR) {
+ if (!strcmp(dent->d_name, ".") ||
+ !strcmp(dent->d_name, ".."))
+ continue;
+
+ snprintf(path, sizeof(path), "%s/%s",
+ dirname, dent->d_name);
+ err = dsos__load_modules_sym_dir(path, filter);
+ if (err < 0)
+ goto failure;
+ } else {
+ char *dot = strrchr(dent->d_name, '.'),
+ dso_name[PATH_MAX];
+ struct map *map;
+ struct rb_node *last;
+
+ if (dot == NULL || strcmp(dot, ".ko"))
+ continue;
+ snprintf(dso_name, sizeof(dso_name), "[%.*s]",
+ (int)(dot - dent->d_name), dent->d_name);
+
+ strxfrchar(dso_name, '-', '_');
+ map = kernel_maps__find_by_dso_name(dso_name);
+ if (map == NULL)
+ continue;
+
+ snprintf(path, sizeof(path), "%s/%s",
+ dirname, dent->d_name);
+
+ map->dso->long_name = strdup(path);
+ if (map->dso->long_name == NULL)
+ goto failure;
+
+ err = dso__load_module_sym(map->dso, map, filter);
+ if (err < 0)
+ goto failure;
+ last = rb_last(&map->dso->syms);
+ if (last) {
+ struct symbol *sym;
+ /*
+ * We do this here as well, even having the
+ * symbol size found in the symtab because
+ * misannotated ASM symbols may have the size
+ * set to zero.
+ */
+ dso__fixup_sym_end(map->dso);
+
+ sym = rb_entry(last, struct symbol, rb_node);
+ map->end = map->start + sym->end;
+ }
+ }
+ nr_symbols += err;
+ }
- /*
- * Iterate over modules, and load active symbols.
- */
- next = rb_first(&mods->mods);
- while (next) {
- pos = rb_entry(next, struct module, rb_node);
- err = dso__load_module(self, mods, pos->name, filter, v);
+ return nr_symbols;
+failure:
+ closedir(dir);
+ return -1;
+}
- if (err < 0)
- break;
+static int dsos__load_modules_sym(symbol_filter_t filter)
+{
+ struct utsname uts;
+ char modules_path[PATH_MAX];
- next = rb_next(&pos->rb_node);
- count += err;
- }
+ if (uname(&uts) < 0)
+ return -1;
- if (err < 0) {
- mod_dso__delete_modules(mods);
- mod_dso__delete_self(mods);
- return err;
- }
+ snprintf(modules_path, sizeof(modules_path), "/lib/modules/%s/kernel",
+ uts.release);
- return count;
+ return dsos__load_modules_sym_dir(modules_path, filter);
}
-static inline void dso__fill_symbol_holes(struct dso *self)
+/*
+ * Constructor variant for modules (where we know from /proc/modules where
+ * they are loaded) and for vmlinux, where only after we load all the
+ * symbols we'll know where it starts and ends.
+ */
+static struct map *map__new2(u64 start, struct dso *dso)
{
- struct symbol *prev = NULL;
- struct rb_node *nd;
+ struct map *self = malloc(sizeof(*self));
- for (nd = rb_last(&self->syms); nd; nd = rb_prev(nd)) {
- struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
+ if (self != NULL) {
+ self->start = start;
+ /*
+ * Will be filled after we load all the symbols
+ */
+ self->end = 0;
- if (prev) {
- u64 hole = 0;
- int alias = pos->start == prev->start;
+ self->pgoff = 0;
+ self->dso = dso;
+ self->map_ip = map__map_ip;
+ self->unmap_ip = map__unmap_ip;
+ RB_CLEAR_NODE(&self->rb_node);
+ }
+ return self;
+}
- if (!alias)
- hole = prev->start - pos->end - 1;
+static int dsos__load_modules(unsigned int sym_priv_size)
+{
+ char *line = NULL;
+ size_t n;
+ FILE *file = fopen("/proc/modules", "r");
+ struct map *map;
- if (hole || alias) {
- if (alias)
- pos->end = prev->end;
- else if (hole)
- pos->end = prev->start - 1;
- }
+ if (file == NULL)
+ return -1;
+
+ while (!feof(file)) {
+ char name[PATH_MAX];
+ u64 start;
+ struct dso *dso;
+ char *sep;
+ int line_len;
+
+ line_len = getline(&line, &n, file);
+ if (line_len < 0)
+ break;
+
+ if (!line)
+ goto out_failure;
+
+ line[--line_len] = '\0'; /* \n */
+
+ sep = strrchr(line, 'x');
+ if (sep == NULL)
+ continue;
+
+ hex2u64(sep + 1, &start);
+
+ sep = strchr(line, ' ');
+ if (sep == NULL)
+ continue;
+
+ *sep = '\0';
+
+ snprintf(name, sizeof(name), "[%s]", line);
+ dso = dso__new(name, sym_priv_size);
+
+ if (dso == NULL)
+ goto out_delete_line;
+
+ map = map__new2(start, dso);
+ if (map == NULL) {
+ dso__delete(dso);
+ goto out_delete_line;
}
- prev = pos;
+
+ dso->origin = DSO__ORIG_KMODULE;
+ kernel_maps__insert(map);
+ dsos__add(dso);
}
+
+ free(line);
+ fclose(file);
+
+ return 0;
+
+out_delete_line:
+ free(line);
+out_failure:
+ return -1;
}
-static int dso__load_vmlinux(struct dso *self, const char *vmlinux,
- symbol_filter_t filter, int v)
+static int dso__load_vmlinux(struct dso *self, struct map *map,
+ const char *vmlinux, symbol_filter_t filter)
{
int err, fd = open(vmlinux, O_RDONLY);
if (fd < 0)
return -1;
- err = dso__load_sym(self, fd, vmlinux, filter, v, NULL);
-
- if (err > 0)
- dso__fill_symbol_holes(self);
+ err = dso__load_sym(self, map, self->long_name, fd, filter, 1, 0);
close(fd);
return err;
}
-int dso__load_kernel(struct dso *self, const char *vmlinux,
- symbol_filter_t filter, int v, int use_modules)
+int dsos__load_kernel(const char *vmlinux, unsigned int sym_priv_size,
+ symbol_filter_t filter, int use_modules)
{
int err = -1;
+ struct dso *dso = dso__new(vmlinux, sym_priv_size);
+
+ if (dso == NULL)
+ return -1;
+
+ dso->short_name = "[kernel]";
+ kernel_map = map__new2(0, dso);
+ if (kernel_map == NULL)
+ goto out_delete_dso;
+
+ kernel_map->map_ip = kernel_map->unmap_ip = identity__map_ip;
+
+ if (use_modules && dsos__load_modules(sym_priv_size) < 0) {
+ pr_warning("Failed to load list of modules in use! "
+ "Continuing...\n");
+ use_modules = 0;
+ }
if (vmlinux) {
- err = dso__load_vmlinux(self, vmlinux, filter, v);
+ err = dso__load_vmlinux(dso, kernel_map, vmlinux, filter);
if (err > 0 && use_modules) {
- int syms = dso__load_modules(self, filter, v);
+ int syms = dsos__load_modules_sym(filter);
- if (syms < 0) {
- fprintf(stderr, "dso__load_modules failed!\n");
- return syms;
- }
- err += syms;
+ if (syms < 0)
+ pr_warning("Failed to read module symbols!"
+ " Continuing...\n");
+ else
+ err += syms;
}
}
if (err <= 0)
- err = dso__load_kallsyms(self, filter, v);
+ err = kernel_maps__load_kallsyms(filter, use_modules);
+
+ if (err > 0) {
+ struct rb_node *node = rb_first(&dso->syms);
+ struct symbol *sym = rb_entry(node, struct symbol, rb_node);
+
+ kernel_map->start = sym->start;
+ node = rb_last(&dso->syms);
+ sym = rb_entry(node, struct symbol, rb_node);
+ kernel_map->end = sym->end;
+
+ dso->origin = DSO__ORIG_KERNEL;
+ kernel_maps__insert(kernel_map);
+ /*
+ * Now that we have all sorted out, just set the ->end of all
+ * maps:
+ */
+ kernel_maps__fixup_end();
+ dsos__add(dso);
- if (err > 0)
- self->origin = DSO__ORIG_KERNEL;
+ if (verbose)
+ kernel_maps__fprintf(stderr);
+ }
return err;
+
+out_delete_dso:
+ dso__delete(dso);
+ return -1;
}
LIST_HEAD(dsos);
-struct dso *kernel_dso;
struct dso *vdso;
-struct dso *hypervisor_dso;
const char *vmlinux_name = "vmlinux";
int modules;
@@ -957,33 +1312,21 @@ static struct dso *dsos__find(const char *name)
return NULL;
}
-struct dso *dsos__findnew(const char *name)
+struct dso *dsos__findnew(const char *name, unsigned int sym_priv_size,
+ bool *is_new)
{
struct dso *dso = dsos__find(name);
- int nr;
- if (dso)
- return dso;
-
- dso = dso__new(name, 0);
- if (!dso)
- goto out_delete_dso;
-
- nr = dso__load(dso, NULL, verbose);
- if (nr < 0) {
- eprintf("Failed to open: %s\n", name);
- goto out_delete_dso;
- }
- if (!nr)
- eprintf("No symbols found in: %s, maybe install a debug package?\n", name);
-
- dsos__add(dso);
+ if (!dso) {
+ dso = dso__new(name, sym_priv_size);
+ if (dso) {
+ dsos__add(dso);
+ *is_new = true;
+ }
+ } else
+ *is_new = false;
return dso;
-
-out_delete_dso:
- dso__delete(dso);
- return NULL;
}
void dsos__fprintf(FILE *fp)
@@ -994,43 +1337,21 @@ void dsos__fprintf(FILE *fp)
dso__fprintf(pos, fp);
}
-static struct symbol *vdso__find_symbol(struct dso *dso, u64 ip)
-{
- return dso__find_symbol(dso, ip);
-}
-
-int load_kernel(void)
+int load_kernel(unsigned int sym_priv_size, symbol_filter_t filter)
{
- int err;
-
- kernel_dso = dso__new("[kernel]", 0);
- if (!kernel_dso)
+ if (dsos__load_kernel(vmlinux_name, sym_priv_size, filter,
+ modules) <= 0)
return -1;
- err = dso__load_kernel(kernel_dso, vmlinux_name, NULL, verbose, modules);
- if (err <= 0) {
- dso__delete(kernel_dso);
- kernel_dso = NULL;
- } else
- dsos__add(kernel_dso);
-
vdso = dso__new("[vdso]", 0);
if (!vdso)
return -1;
- vdso->find_symbol = vdso__find_symbol;
-
dsos__add(vdso);
- hypervisor_dso = dso__new("[hypervisor]", 0);
- if (!hypervisor_dso)
- return -1;
- dsos__add(hypervisor_dso);
-
- return err;
+ return 0;
}
-
void symbol__init(void)
{
elf_version(EV_CURRENT);
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 6e8490716408..77b7b3e42417 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -1,11 +1,11 @@
-#ifndef _PERF_SYMBOL_
-#define _PERF_SYMBOL_ 1
+#ifndef __PERF_SYMBOL
+#define __PERF_SYMBOL 1
#include <linux/types.h>
+#include <stdbool.h>
#include "types.h"
#include <linux/list.h>
#include <linux/rbtree.h>
-#include "module.h"
#include "event.h"
#ifdef HAVE_CPLUS_DEMANGLE
@@ -36,11 +36,6 @@ struct symbol {
struct rb_node rb_node;
u64 start;
u64 end;
- u64 obj_start;
- u64 hist_sum;
- u64 *hist;
- struct module *module;
- void *priv;
char name[0];
};
@@ -52,13 +47,11 @@ struct dso {
unsigned char adjust_symbols;
unsigned char slen_calculated;
unsigned char origin;
+ const char *short_name;
+ char *long_name;
char name[0];
};
-extern const char *sym_hist_filter;
-
-typedef int (*symbol_filter_t)(struct dso *self, struct symbol *sym);
-
struct dso *dso__new(const char *name, unsigned int sym_priv_size);
void dso__delete(struct dso *self);
@@ -69,24 +62,23 @@ static inline void *dso__sym_priv(struct dso *self, struct symbol *sym)
struct symbol *dso__find_symbol(struct dso *self, u64 ip);
-int dso__load_kernel(struct dso *self, const char *vmlinux,
- symbol_filter_t filter, int verbose, int modules);
-int dso__load_modules(struct dso *self, symbol_filter_t filter, int verbose);
-int dso__load(struct dso *self, symbol_filter_t filter, int verbose);
-struct dso *dsos__findnew(const char *name);
+int dsos__load_kernel(const char *vmlinux, unsigned int sym_priv_size,
+ symbol_filter_t filter, int modules);
+struct dso *dsos__findnew(const char *name, unsigned int sym_priv_size,
+ bool *is_new);
+int dso__load(struct dso *self, struct map *map, symbol_filter_t filter);
void dsos__fprintf(FILE *fp);
size_t dso__fprintf(struct dso *self, FILE *fp);
char dso__symtab_origin(const struct dso *self);
-int load_kernel(void);
+int load_kernel(unsigned int sym_priv_size, symbol_filter_t filter);
void symbol__init(void);
extern struct list_head dsos;
-extern struct dso *kernel_dso;
+extern struct map *kernel_map;
extern struct dso *vdso;
-extern struct dso *hypervisor_dso;
extern const char *vmlinux_name;
extern int modules;
-#endif /* _PERF_SYMBOL_ */
+#endif /* __PERF_SYMBOL */
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
index 45efb5db0d19..0f6d78c9863a 100644
--- a/tools/perf/util/thread.c
+++ b/tools/perf/util/thread.c
@@ -6,6 +6,9 @@
#include "util.h"
#include "debug.h"
+static struct rb_root threads;
+static struct thread *last_match;
+
static struct thread *thread__new(pid_t pid)
{
struct thread *self = calloc(1, sizeof(*self));
@@ -15,7 +18,8 @@ static struct thread *thread__new(pid_t pid)
self->comm = malloc(32);
if (self->comm)
snprintf(self->comm, 32, ":%d", self->pid);
- INIT_LIST_HEAD(&self->maps);
+ self->maps = RB_ROOT;
+ INIT_LIST_HEAD(&self->removed_maps);
}
return self;
@@ -29,21 +33,40 @@ int thread__set_comm(struct thread *self, const char *comm)
return self->comm ? 0 : -ENOMEM;
}
+int thread__comm_len(struct thread *self)
+{
+ if (!self->comm_len) {
+ if (!self->comm)
+ return 0;
+ self->comm_len = strlen(self->comm);
+ }
+
+ return self->comm_len;
+}
+
static size_t thread__fprintf(struct thread *self, FILE *fp)
{
+ struct rb_node *nd;
struct map *pos;
- size_t ret = fprintf(fp, "Thread %d %s\n", self->pid, self->comm);
+ size_t ret = fprintf(fp, "Thread %d %s\nCurrent maps:\n",
+ self->pid, self->comm);
- list_for_each_entry(pos, &self->maps, node)
+ for (nd = rb_first(&self->maps); nd; nd = rb_next(nd)) {
+ pos = rb_entry(nd, struct map, rb_node);
+ ret += map__fprintf(pos, fp);
+ }
+
+ ret = fprintf(fp, "Removed maps:\n");
+
+ list_for_each_entry(pos, &self->removed_maps, node)
ret += map__fprintf(pos, fp);
return ret;
}
-struct thread *
-threads__findnew(pid_t pid, struct rb_root *threads, struct thread **last_match)
+struct thread *threads__findnew(pid_t pid)
{
- struct rb_node **p = &threads->rb_node;
+ struct rb_node **p = &threads.rb_node;
struct rb_node *parent = NULL;
struct thread *th;
@@ -52,15 +75,15 @@ threads__findnew(pid_t pid, struct rb_root *threads, struct thread **last_match)
* so most of the time we dont have to look up
* the full rbtree:
*/
- if (*last_match && (*last_match)->pid == pid)
- return *last_match;
+ if (last_match && last_match->pid == pid)
+ return last_match;
while (*p != NULL) {
parent = *p;
th = rb_entry(parent, struct thread, rb_node);
if (th->pid == pid) {
- *last_match = th;
+ last_match = th;
return th;
}
@@ -73,17 +96,16 @@ threads__findnew(pid_t pid, struct rb_root *threads, struct thread **last_match)
th = thread__new(pid);
if (th != NULL) {
rb_link_node(&th->rb_node, parent, p);
- rb_insert_color(&th->rb_node, threads);
- *last_match = th;
+ rb_insert_color(&th->rb_node, &threads);
+ last_match = th;
}
return th;
}
-struct thread *
-register_idle_thread(struct rb_root *threads, struct thread **last_match)
+struct thread *register_idle_thread(void)
{
- struct thread *thread = threads__findnew(0, threads, last_match);
+ struct thread *thread = threads__findnew(0);
if (!thread || thread__set_comm(thread, "swapper")) {
fprintf(stderr, "problem inserting idle task.\n");
@@ -93,42 +115,82 @@ register_idle_thread(struct rb_root *threads, struct thread **last_match)
return thread;
}
-void thread__insert_map(struct thread *self, struct map *map)
+static void thread__remove_overlappings(struct thread *self, struct map *map)
{
- struct map *pos, *tmp;
-
- list_for_each_entry_safe(pos, tmp, &self->maps, node) {
- if (map__overlap(pos, map)) {
- if (verbose >= 2) {
- printf("overlapping maps:\n");
- map__fprintf(map, stdout);
- map__fprintf(pos, stdout);
- }
-
- if (map->start <= pos->start && map->end > pos->start)
- pos->start = map->end;
-
- if (map->end >= pos->end && map->start < pos->end)
- pos->end = map->start;
-
- if (verbose >= 2) {
- printf("after collision:\n");
- map__fprintf(pos, stdout);
- }
-
- if (pos->start >= pos->end) {
- list_del_init(&pos->node);
- free(pos);
- }
+ struct rb_node *next = rb_first(&self->maps);
+
+ while (next) {
+ struct map *pos = rb_entry(next, struct map, rb_node);
+ next = rb_next(&pos->rb_node);
+
+ if (!map__overlap(pos, map))
+ continue;
+
+ if (verbose >= 2) {
+ fputs("overlapping maps:\n", stderr);
+ map__fprintf(map, stderr);
+ map__fprintf(pos, stderr);
}
+
+ rb_erase(&pos->rb_node, &self->maps);
+ /*
+ * We may have references to this map, for instance in some
+ * hist_entry instances, so just move them to a separate
+ * list.
+ */
+ list_add_tail(&pos->node, &self->removed_maps);
+ }
+}
+
+void maps__insert(struct rb_root *maps, struct map *map)
+{
+ struct rb_node **p = &maps->rb_node;
+ struct rb_node *parent = NULL;
+ const u64 ip = map->start;
+ struct map *m;
+
+ while (*p != NULL) {
+ parent = *p;
+ m = rb_entry(parent, struct map, rb_node);
+ if (ip < m->start)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
}
- list_add_tail(&map->node, &self->maps);
+ rb_link_node(&map->rb_node, parent, p);
+ rb_insert_color(&map->rb_node, maps);
+}
+
+struct map *maps__find(struct rb_root *maps, u64 ip)
+{
+ struct rb_node **p = &maps->rb_node;
+ struct rb_node *parent = NULL;
+ struct map *m;
+
+ while (*p != NULL) {
+ parent = *p;
+ m = rb_entry(parent, struct map, rb_node);
+ if (ip < m->start)
+ p = &(*p)->rb_left;
+ else if (ip > m->end)
+ p = &(*p)->rb_right;
+ else
+ return m;
+ }
+
+ return NULL;
+}
+
+void thread__insert_map(struct thread *self, struct map *map)
+{
+ thread__remove_overlappings(self, map);
+ maps__insert(&self->maps, map);
}
int thread__fork(struct thread *self, struct thread *parent)
{
- struct map *map;
+ struct rb_node *nd;
if (self->comm)
free(self->comm);
@@ -136,7 +198,8 @@ int thread__fork(struct thread *self, struct thread *parent)
if (!self->comm)
return -ENOMEM;
- list_for_each_entry(map, &parent->maps, node) {
+ for (nd = rb_first(&parent->maps); nd; nd = rb_next(nd)) {
+ struct map *map = rb_entry(nd, struct map, rb_node);
struct map *new = map__clone(map);
if (!new)
return -ENOMEM;
@@ -146,26 +209,12 @@ int thread__fork(struct thread *self, struct thread *parent)
return 0;
}
-struct map *thread__find_map(struct thread *self, u64 ip)
-{
- struct map *pos;
-
- if (self == NULL)
- return NULL;
-
- list_for_each_entry(pos, &self->maps, node)
- if (ip >= pos->start && ip <= pos->end)
- return pos;
-
- return NULL;
-}
-
-size_t threads__fprintf(FILE *fp, struct rb_root *threads)
+size_t threads__fprintf(FILE *fp)
{
size_t ret = 0;
struct rb_node *nd;
- for (nd = rb_first(threads); nd; nd = rb_next(nd)) {
+ for (nd = rb_first(&threads); nd; nd = rb_next(nd)) {
struct thread *pos = rb_entry(nd, struct thread, rb_node);
ret += thread__fprintf(pos, fp);
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
index 32aea3c1c2ad..53addd77ce8f 100644
--- a/tools/perf/util/thread.h
+++ b/tools/perf/util/thread.h
@@ -1,22 +1,37 @@
+#ifndef __PERF_THREAD_H
+#define __PERF_THREAD_H
+
#include <linux/rbtree.h>
-#include <linux/list.h>
#include <unistd.h>
#include "symbol.h"
struct thread {
struct rb_node rb_node;
- struct list_head maps;
+ struct rb_root maps;
+ struct list_head removed_maps;
pid_t pid;
char shortname[3];
char *comm;
+ int comm_len;
};
int thread__set_comm(struct thread *self, const char *comm);
-struct thread *
-threads__findnew(pid_t pid, struct rb_root *threads, struct thread **last_match);
-struct thread *
-register_idle_thread(struct rb_root *threads, struct thread **last_match);
+int thread__comm_len(struct thread *self);
+struct thread *threads__findnew(pid_t pid);
+struct thread *register_idle_thread(void);
void thread__insert_map(struct thread *self, struct map *map);
int thread__fork(struct thread *self, struct thread *parent);
-struct map *thread__find_map(struct thread *self, u64 ip);
-size_t threads__fprintf(FILE *fp, struct rb_root *threads);
+size_t threads__fprintf(FILE *fp);
+
+void maps__insert(struct rb_root *maps, struct map *map);
+struct map *maps__find(struct rb_root *maps, u64 ip);
+
+struct symbol *kernel_maps__find_symbol(const u64 ip, struct map **mapp);
+struct map *kernel_maps__find_by_dso_name(const char *name);
+
+static inline struct map *thread__find_map(struct thread *self, u64 ip)
+{
+ return self ? maps__find(&self->maps, ip) : NULL;
+}
+
+#endif /* __PERF_THREAD_H */
diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c
index af4b0573b37f..831052d4b4fb 100644
--- a/tools/perf/util/trace-event-info.c
+++ b/tools/perf/util/trace-event-info.c
@@ -496,14 +496,12 @@ get_tracepoints_path(struct perf_event_attr *pattrs, int nb_events)
return path.next;
}
-void read_tracing_data(struct perf_event_attr *pattrs, int nb_events)
+void read_tracing_data(int fd, struct perf_event_attr *pattrs, int nb_events)
{
char buf[BUFSIZ];
struct tracepoint_path *tps;
- output_fd = open(output_file, O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE, 0644);
- if (output_fd < 0)
- die("creating file '%s'", output_file);
+ output_fd = fd;
buf[0] = 23;
buf[1] = 8;
diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c
index 55c9659a56e2..eae560503086 100644
--- a/tools/perf/util/trace-event-parse.c
+++ b/tools/perf/util/trace-event-parse.c
@@ -40,6 +40,8 @@ int header_page_size_size;
int header_page_data_offset;
int header_page_data_size;
+int latency_format;
+
static char *input_buf;
static unsigned long long input_buf_ptr;
static unsigned long long input_buf_siz;
@@ -284,18 +286,19 @@ void parse_ftrace_printk(char *file, unsigned int size __unused)
char *line;
char *next = NULL;
char *addr_str;
- int ret;
int i;
line = strtok_r(file, "\n", &next);
while (line) {
+ addr_str = strsep(&line, ":");
+ if (!line) {
+ warning("error parsing print strings");
+ break;
+ }
item = malloc_or_die(sizeof(*item));
- ret = sscanf(line, "%as : %as",
- (float *)(void *)&addr_str, /* workaround gcc warning */
- (float *)(void *)&item->printk);
item->addr = strtoull(addr_str, NULL, 16);
- free(addr_str);
-
+ /* fmt still has a space, skip it */
+ item->printk = strdup(line+1);
item->next = list;
list = item;
line = strtok_r(NULL, "\n", &next);
@@ -522,7 +525,10 @@ static enum event_type __read_token(char **tok)
last_ch = ch;
ch = __read_char();
buf[i++] = ch;
- } while (ch != quote_ch && last_ch != '\\');
+ /* the '\' '\' will cancel itself */
+ if (ch == '\\' && last_ch == '\\')
+ last_ch = 0;
+ } while (ch != quote_ch || last_ch == '\\');
/* remove the last quote */
i--;
goto out;
@@ -610,7 +616,7 @@ static enum event_type read_token_item(char **tok)
static int test_type(enum event_type type, enum event_type expect)
{
if (type != expect) {
- die("Error: expected type %d but read %d",
+ warning("Error: expected type %d but read %d",
expect, type);
return -1;
}
@@ -621,13 +627,13 @@ static int test_type_token(enum event_type type, char *token,
enum event_type expect, const char *expect_tok)
{
if (type != expect) {
- die("Error: expected type %d but read %d",
+ warning("Error: expected type %d but read %d",
expect, type);
return -1;
}
if (strcmp(token, expect_tok) != 0) {
- die("Error: expected '%s' but read '%s'",
+ warning("Error: expected '%s' but read '%s'",
expect_tok, token);
return -1;
}
@@ -665,7 +671,7 @@ static int __read_expected(enum event_type expect, const char *str, int newline_
free_token(token);
- return 0;
+ return ret;
}
static int read_expected(enum event_type expect, const char *str)
@@ -682,10 +688,10 @@ static char *event_read_name(void)
{
char *token;
- if (read_expected(EVENT_ITEM, (char *)"name") < 0)
+ if (read_expected(EVENT_ITEM, "name") < 0)
return NULL;
- if (read_expected(EVENT_OP, (char *)":") < 0)
+ if (read_expected(EVENT_OP, ":") < 0)
return NULL;
if (read_expect_type(EVENT_ITEM, &token) < 0)
@@ -703,10 +709,10 @@ static int event_read_id(void)
char *token;
int id;
- if (read_expected_item(EVENT_ITEM, (char *)"ID") < 0)
+ if (read_expected_item(EVENT_ITEM, "ID") < 0)
return -1;
- if (read_expected(EVENT_OP, (char *)":") < 0)
+ if (read_expected(EVENT_OP, ":") < 0)
return -1;
if (read_expect_type(EVENT_ITEM, &token) < 0)
@@ -721,6 +727,24 @@ static int event_read_id(void)
return -1;
}
+static int field_is_string(struct format_field *field)
+{
+ if ((field->flags & FIELD_IS_ARRAY) &&
+ (!strstr(field->type, "char") || !strstr(field->type, "u8") ||
+ !strstr(field->type, "s8")))
+ return 1;
+
+ return 0;
+}
+
+static int field_is_dynamic(struct format_field *field)
+{
+ if (!strcmp(field->type, "__data_loc"))
+ return 1;
+
+ return 0;
+}
+
static int event_read_fields(struct event *event, struct format_field **fields)
{
struct format_field *field = NULL;
@@ -738,7 +762,7 @@ static int event_read_fields(struct event *event, struct format_field **fields)
count++;
- if (test_type_token(type, token, EVENT_ITEM, (char *)"field"))
+ if (test_type_token(type, token, EVENT_ITEM, "field"))
goto fail;
free_token(token);
@@ -753,7 +777,7 @@ static int event_read_fields(struct event *event, struct format_field **fields)
type = read_token(&token);
}
- if (test_type_token(type, token, EVENT_OP, (char *)":") < 0)
+ if (test_type_token(type, token, EVENT_OP, ":") < 0)
return -1;
if (read_expect_type(EVENT_ITEM, &token) < 0)
@@ -865,14 +889,20 @@ static int event_read_fields(struct event *event, struct format_field **fields)
free(brackets);
}
- if (test_type_token(type, token, EVENT_OP, (char *)";"))
+ if (field_is_string(field)) {
+ field->flags |= FIELD_IS_STRING;
+ if (field_is_dynamic(field))
+ field->flags |= FIELD_IS_DYNAMIC;
+ }
+
+ if (test_type_token(type, token, EVENT_OP, ";"))
goto fail;
free_token(token);
- if (read_expected(EVENT_ITEM, (char *)"offset") < 0)
+ if (read_expected(EVENT_ITEM, "offset") < 0)
goto fail_expect;
- if (read_expected(EVENT_OP, (char *)":") < 0)
+ if (read_expected(EVENT_OP, ":") < 0)
goto fail_expect;
if (read_expect_type(EVENT_ITEM, &token))
@@ -880,13 +910,13 @@ static int event_read_fields(struct event *event, struct format_field **fields)
field->offset = strtoul(token, NULL, 0);
free_token(token);
- if (read_expected(EVENT_OP, (char *)";") < 0)
+ if (read_expected(EVENT_OP, ";") < 0)
goto fail_expect;
- if (read_expected(EVENT_ITEM, (char *)"size") < 0)
+ if (read_expected(EVENT_ITEM, "size") < 0)
goto fail_expect;
- if (read_expected(EVENT_OP, (char *)":") < 0)
+ if (read_expected(EVENT_OP, ":") < 0)
goto fail_expect;
if (read_expect_type(EVENT_ITEM, &token))
@@ -894,11 +924,33 @@ static int event_read_fields(struct event *event, struct format_field **fields)
field->size = strtoul(token, NULL, 0);
free_token(token);
- if (read_expected(EVENT_OP, (char *)";") < 0)
+ if (read_expected(EVENT_OP, ";") < 0)
goto fail_expect;
- if (read_expect_type(EVENT_NEWLINE, &token) < 0)
- goto fail;
+ type = read_token(&token);
+ if (type != EVENT_NEWLINE) {
+ /* newer versions of the kernel have a "signed" type */
+ if (test_type_token(type, token, EVENT_ITEM, "signed"))
+ goto fail;
+
+ free_token(token);
+
+ if (read_expected(EVENT_OP, ":") < 0)
+ goto fail_expect;
+
+ if (read_expect_type(EVENT_ITEM, &token))
+ goto fail;
+
+ /* add signed type */
+
+ free_token(token);
+ if (read_expected(EVENT_OP, ";") < 0)
+ goto fail_expect;
+
+ if (read_expect_type(EVENT_NEWLINE, &token))
+ goto fail;
+ }
+
free_token(token);
*fields = field;
@@ -921,10 +973,10 @@ static int event_read_format(struct event *event)
char *token;
int ret;
- if (read_expected_item(EVENT_ITEM, (char *)"format") < 0)
+ if (read_expected_item(EVENT_ITEM, "format") < 0)
return -1;
- if (read_expected(EVENT_OP, (char *)":") < 0)
+ if (read_expected(EVENT_OP, ":") < 0)
return -1;
if (read_expect_type(EVENT_NEWLINE, &token))
@@ -984,7 +1036,7 @@ process_cond(struct event *event, struct print_arg *top, char **tok)
*tok = NULL;
type = process_arg(event, left, &token);
- if (test_type_token(type, token, EVENT_OP, (char *)":"))
+ if (test_type_token(type, token, EVENT_OP, ":"))
goto out_free;
arg->op.op = token;
@@ -1004,6 +1056,35 @@ out_free:
return EVENT_ERROR;
}
+static enum event_type
+process_array(struct event *event, struct print_arg *top, char **tok)
+{
+ struct print_arg *arg;
+ enum event_type type;
+ char *token = NULL;
+
+ arg = malloc_or_die(sizeof(*arg));
+ memset(arg, 0, sizeof(*arg));
+
+ *tok = NULL;
+ type = process_arg(event, arg, &token);
+ if (test_type_token(type, token, EVENT_OP, "]"))
+ goto out_free;
+
+ top->op.right = arg;
+
+ free_token(token);
+ type = read_token_item(&token);
+ *tok = token;
+
+ return type;
+
+out_free:
+ free_token(*tok);
+ free_arg(arg);
+ return EVENT_ERROR;
+}
+
static int get_op_prio(char *op)
{
if (!op[1]) {
@@ -1128,6 +1209,8 @@ process_op(struct event *event, struct print_arg *arg, char **tok)
strcmp(token, "*") == 0 ||
strcmp(token, "^") == 0 ||
strcmp(token, "/") == 0 ||
+ strcmp(token, "<") == 0 ||
+ strcmp(token, ">") == 0 ||
strcmp(token, "==") == 0 ||
strcmp(token, "!=") == 0) {
@@ -1144,17 +1227,46 @@ process_op(struct event *event, struct print_arg *arg, char **tok)
right = malloc_or_die(sizeof(*right));
- type = process_arg(event, right, tok);
+ type = read_token_item(&token);
+ *tok = token;
+
+ /* could just be a type pointer */
+ if ((strcmp(arg->op.op, "*") == 0) &&
+ type == EVENT_DELIM && (strcmp(token, ")") == 0)) {
+ if (left->type != PRINT_ATOM)
+ die("bad pointer type");
+ left->atom.atom = realloc(left->atom.atom,
+ sizeof(left->atom.atom) + 3);
+ strcat(left->atom.atom, " *");
+ *arg = *left;
+ free(arg);
+
+ return type;
+ }
+
+ type = process_arg_token(event, right, tok, type);
arg->op.right = right;
+ } else if (strcmp(token, "[") == 0) {
+
+ left = malloc_or_die(sizeof(*left));
+ *left = *arg;
+
+ arg->type = PRINT_OP;
+ arg->op.op = token;
+ arg->op.left = left;
+
+ arg->op.prio = 0;
+ type = process_array(event, arg, tok);
+
} else {
- die("unknown op '%s'", token);
+ warning("unknown op '%s'", token);
+ event->flags |= EVENT_FL_FAILED;
/* the arg is now the left side */
return EVENT_NONE;
}
-
if (type == EVENT_OP) {
int prio;
@@ -1178,7 +1290,7 @@ process_entry(struct event *event __unused, struct print_arg *arg,
char *field;
char *token;
- if (read_expected(EVENT_OP, (char *)"->") < 0)
+ if (read_expected(EVENT_OP, "->") < 0)
return EVENT_ERROR;
if (read_expect_type(EVENT_ITEM, &token) < 0)
@@ -1338,14 +1450,14 @@ process_fields(struct event *event, struct print_flag_sym **list, char **tok)
do {
free_token(token);
type = read_token_item(&token);
- if (test_type_token(type, token, EVENT_OP, (char *)"{"))
+ if (test_type_token(type, token, EVENT_OP, "{"))
break;
arg = malloc_or_die(sizeof(*arg));
free_token(token);
type = process_arg(event, arg, &token);
- if (test_type_token(type, token, EVENT_DELIM, (char *)","))
+ if (test_type_token(type, token, EVENT_DELIM, ","))
goto out_free;
field = malloc_or_die(sizeof(*field));
@@ -1356,7 +1468,7 @@ process_fields(struct event *event, struct print_flag_sym **list, char **tok)
free_token(token);
type = process_arg(event, arg, &token);
- if (test_type_token(type, token, EVENT_OP, (char *)"}"))
+ if (test_type_token(type, token, EVENT_OP, "}"))
goto out_free;
value = arg_eval(arg);
@@ -1391,13 +1503,13 @@ process_flags(struct event *event, struct print_arg *arg, char **tok)
memset(arg, 0, sizeof(*arg));
arg->type = PRINT_FLAGS;
- if (read_expected_item(EVENT_DELIM, (char *)"(") < 0)
+ if (read_expected_item(EVENT_DELIM, "(") < 0)
return EVENT_ERROR;
field = malloc_or_die(sizeof(*field));
type = process_arg(event, field, &token);
- if (test_type_token(type, token, EVENT_DELIM, (char *)","))
+ if (test_type_token(type, token, EVENT_DELIM, ","))
goto out_free;
arg->flags.field = field;
@@ -1408,11 +1520,11 @@ process_flags(struct event *event, struct print_arg *arg, char **tok)
type = read_token_item(&token);
}
- if (test_type_token(type, token, EVENT_DELIM, (char *)","))
+ if (test_type_token(type, token, EVENT_DELIM, ","))
goto out_free;
type = process_fields(event, &arg->flags.flags, &token);
- if (test_type_token(type, token, EVENT_DELIM, (char *)")"))
+ if (test_type_token(type, token, EVENT_DELIM, ")"))
goto out_free;
free_token(token);
@@ -1434,19 +1546,19 @@ process_symbols(struct event *event, struct print_arg *arg, char **tok)
memset(arg, 0, sizeof(*arg));
arg->type = PRINT_SYMBOL;
- if (read_expected_item(EVENT_DELIM, (char *)"(") < 0)
+ if (read_expected_item(EVENT_DELIM, "(") < 0)
return EVENT_ERROR;
field = malloc_or_die(sizeof(*field));
type = process_arg(event, field, &token);
- if (test_type_token(type, token, EVENT_DELIM, (char *)","))
+ if (test_type_token(type, token, EVENT_DELIM, ","))
goto out_free;
arg->symbol.field = field;
type = process_fields(event, &arg->symbol.symbols, &token);
- if (test_type_token(type, token, EVENT_DELIM, (char *)")"))
+ if (test_type_token(type, token, EVENT_DELIM, ")"))
goto out_free;
free_token(token);
@@ -1463,7 +1575,6 @@ process_paren(struct event *event, struct print_arg *arg, char **tok)
{
struct print_arg *item_arg;
enum event_type type;
- int ptr_cast = 0;
char *token;
type = process_arg(event, arg, &token);
@@ -1471,28 +1582,13 @@ process_paren(struct event *event, struct print_arg *arg, char **tok)
if (type == EVENT_ERROR)
return EVENT_ERROR;
- if (type == EVENT_OP) {
- /* handle the ptr casts */
- if (!strcmp(token, "*")) {
- /*
- * FIXME: should we zapp whitespaces before ')' ?
- * (may require a peek_token_item())
- */
- if (__peek_char() == ')') {
- ptr_cast = 1;
- free_token(token);
- type = read_token_item(&token);
- }
- }
- if (!ptr_cast) {
- type = process_op(event, arg, &token);
+ if (type == EVENT_OP)
+ type = process_op(event, arg, &token);
- if (type == EVENT_ERROR)
- return EVENT_ERROR;
- }
- }
+ if (type == EVENT_ERROR)
+ return EVENT_ERROR;
- if (test_type_token(type, token, EVENT_DELIM, (char *)")")) {
+ if (test_type_token(type, token, EVENT_DELIM, ")")) {
free_token(token);
return EVENT_ERROR;
}
@@ -1516,13 +1612,6 @@ process_paren(struct event *event, struct print_arg *arg, char **tok)
item_arg = malloc_or_die(sizeof(*item_arg));
arg->type = PRINT_TYPE;
- if (ptr_cast) {
- char *old = arg->atom.atom;
-
- arg->atom.atom = malloc_or_die(strlen(old + 3));
- sprintf(arg->atom.atom, "%s *", old);
- free(old);
- }
arg->typecast.type = arg->atom.atom;
arg->typecast.item = item_arg;
type = process_arg_token(event, item_arg, &token, type);
@@ -1540,7 +1629,7 @@ process_str(struct event *event __unused, struct print_arg *arg, char **tok)
enum event_type type;
char *token;
- if (read_expected(EVENT_DELIM, (char *)"(") < 0)
+ if (read_expected(EVENT_DELIM, "(") < 0)
return EVENT_ERROR;
if (read_expect_type(EVENT_ITEM, &token) < 0)
@@ -1550,7 +1639,7 @@ process_str(struct event *event __unused, struct print_arg *arg, char **tok)
arg->string.string = token;
arg->string.offset = -1;
- if (read_expected(EVENT_DELIM, (char *)")") < 0)
+ if (read_expected(EVENT_DELIM, ")") < 0)
return EVENT_ERROR;
type = read_token(&token);
@@ -1637,12 +1726,18 @@ process_arg_token(struct event *event, struct print_arg *arg,
static int event_read_print_args(struct event *event, struct print_arg **list)
{
- enum event_type type;
+ enum event_type type = EVENT_ERROR;
struct print_arg *arg;
char *token;
int args = 0;
do {
+ if (type == EVENT_NEWLINE) {
+ free_token(token);
+ type = read_token_item(&token);
+ continue;
+ }
+
arg = malloc_or_die(sizeof(*arg));
memset(arg, 0, sizeof(*arg));
@@ -1683,18 +1778,19 @@ static int event_read_print(struct event *event)
char *token;
int ret;
- if (read_expected_item(EVENT_ITEM, (char *)"print") < 0)
+ if (read_expected_item(EVENT_ITEM, "print") < 0)
return -1;
- if (read_expected(EVENT_ITEM, (char *)"fmt") < 0)
+ if (read_expected(EVENT_ITEM, "fmt") < 0)
return -1;
- if (read_expected(EVENT_OP, (char *)":") < 0)
+ if (read_expected(EVENT_OP, ":") < 0)
return -1;
if (read_expect_type(EVENT_DQUOTE, &token) < 0)
goto fail;
+ concat:
event->print_fmt.format = token;
event->print_fmt.args = NULL;
@@ -1704,7 +1800,22 @@ static int event_read_print(struct event *event)
if (type == EVENT_NONE)
return 0;
- if (test_type_token(type, token, EVENT_DELIM, (char *)","))
+ /* Handle concatination of print lines */
+ if (type == EVENT_DQUOTE) {
+ char *cat;
+
+ cat = malloc_or_die(strlen(event->print_fmt.format) +
+ strlen(token) + 1);
+ strcpy(cat, event->print_fmt.format);
+ strcat(cat, token);
+ free_token(token);
+ free_token(event->print_fmt.format);
+ event->print_fmt.format = NULL;
+ token = cat;
+ goto concat;
+ }
+
+ if (test_type_token(type, token, EVENT_DELIM, ","))
goto fail;
free_token(token);
@@ -1713,7 +1824,7 @@ static int event_read_print(struct event *event)
if (ret < 0)
return -1;
- return 0;
+ return ret;
fail:
free_token(token);
@@ -1822,37 +1933,67 @@ static int get_common_info(const char *type, int *offset, int *size)
return 0;
}
-int trace_parse_common_type(void *data)
+static int __parse_common(void *data, int *size, int *offset,
+ const char *name)
{
- static int type_offset;
- static int type_size;
int ret;
- if (!type_size) {
- ret = get_common_info("common_type",
- &type_offset,
- &type_size);
+ if (!*size) {
+ ret = get_common_info(name, offset, size);
if (ret < 0)
return ret;
}
- return read_size(data + type_offset, type_size);
+ return read_size(data + *offset, *size);
+}
+
+int trace_parse_common_type(void *data)
+{
+ static int type_offset;
+ static int type_size;
+
+ return __parse_common(data, &type_size, &type_offset,
+ "common_type");
}
static int parse_common_pid(void *data)
{
static int pid_offset;
static int pid_size;
+
+ return __parse_common(data, &pid_size, &pid_offset,
+ "common_pid");
+}
+
+static int parse_common_pc(void *data)
+{
+ static int pc_offset;
+ static int pc_size;
+
+ return __parse_common(data, &pc_size, &pc_offset,
+ "common_preempt_count");
+}
+
+static int parse_common_flags(void *data)
+{
+ static int flags_offset;
+ static int flags_size;
+
+ return __parse_common(data, &flags_size, &flags_offset,
+ "common_flags");
+}
+
+static int parse_common_lock_depth(void *data)
+{
+ static int ld_offset;
+ static int ld_size;
int ret;
- if (!pid_size) {
- ret = get_common_info("common_pid",
- &pid_offset,
- &pid_size);
- if (ret < 0)
- return ret;
- }
+ ret = __parse_common(data, &ld_size, &ld_offset,
+ "common_lock_depth");
+ if (ret < 0)
+ return -1;
- return read_size(data + pid_offset, pid_size);
+ return ret;
}
struct event *trace_find_event(int id)
@@ -1871,6 +2012,7 @@ static unsigned long long eval_num_arg(void *data, int size,
{
unsigned long long val = 0;
unsigned long long left, right;
+ struct print_arg *larg;
switch (arg->type) {
case PRINT_NULL:
@@ -1897,6 +2039,26 @@ static unsigned long long eval_num_arg(void *data, int size,
return 0;
break;
case PRINT_OP:
+ if (strcmp(arg->op.op, "[") == 0) {
+ /*
+ * Arrays are special, since we don't want
+ * to read the arg as is.
+ */
+ if (arg->op.left->type != PRINT_FIELD)
+ goto default_op; /* oops, all bets off */
+ larg = arg->op.left;
+ if (!larg->field.field) {
+ larg->field.field =
+ find_any_field(event, larg->field.name);
+ if (!larg->field.field)
+ die("field %s not found", larg->field.name);
+ }
+ right = eval_num_arg(data, size, event, arg->op.right);
+ val = read_size(data + larg->field.field->offset +
+ right * long_size, long_size);
+ break;
+ }
+ default_op:
left = eval_num_arg(data, size, event, arg->op.left);
right = eval_num_arg(data, size, event, arg->op.right);
switch (arg->op.op[0]) {
@@ -1947,6 +2109,12 @@ static unsigned long long eval_num_arg(void *data, int size,
die("unknown op '%s'", arg->op.op);
val = left == right;
break;
+ case '-':
+ val = left - right;
+ break;
+ case '+':
+ val = left + right;
+ break;
default:
die("unknown op '%s'", arg->op.op);
}
@@ -2145,8 +2313,9 @@ static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struc
case 'u':
case 'x':
case 'i':
- bptr = (void *)(((unsigned long)bptr + (long_size - 1)) &
- ~(long_size - 1));
+ /* the pointers are always 4 bytes aligned */
+ bptr = (void *)(((unsigned long)bptr + 3) &
+ ~3);
switch (ls) {
case 0:
case 1:
@@ -2270,7 +2439,27 @@ static void pretty_print(void *data, int size, struct event *event)
for (; *ptr; ptr++) {
ls = 0;
- if (*ptr == '%') {
+ if (*ptr == '\\') {
+ ptr++;
+ switch (*ptr) {
+ case 'n':
+ printf("\n");
+ break;
+ case 't':
+ printf("\t");
+ break;
+ case 'r':
+ printf("\r");
+ break;
+ case '\\':
+ printf("\\");
+ break;
+ default:
+ printf("%c", *ptr);
+ break;
+ }
+
+ } else if (*ptr == '%') {
saveptr = ptr;
show_func = 0;
cont_process:
@@ -2377,6 +2566,41 @@ static inline int log10_cpu(int nb)
return 1;
}
+static void print_lat_fmt(void *data, int size __unused)
+{
+ unsigned int lat_flags;
+ unsigned int pc;
+ int lock_depth;
+ int hardirq;
+ int softirq;
+
+ lat_flags = parse_common_flags(data);
+ pc = parse_common_pc(data);
+ lock_depth = parse_common_lock_depth(data);
+
+ hardirq = lat_flags & TRACE_FLAG_HARDIRQ;
+ softirq = lat_flags & TRACE_FLAG_SOFTIRQ;
+
+ printf("%c%c%c",
+ (lat_flags & TRACE_FLAG_IRQS_OFF) ? 'd' :
+ (lat_flags & TRACE_FLAG_IRQS_NOSUPPORT) ?
+ 'X' : '.',
+ (lat_flags & TRACE_FLAG_NEED_RESCHED) ?
+ 'N' : '.',
+ (hardirq && softirq) ? 'H' :
+ hardirq ? 'h' : softirq ? 's' : '.');
+
+ if (pc)
+ printf("%x", pc);
+ else
+ printf(".");
+
+ if (lock_depth < 0)
+ printf(".");
+ else
+ printf("%d", lock_depth);
+}
+
/* taken from Linux, written by Frederic Weisbecker */
static void print_graph_cpu(int cpu)
{
@@ -2620,6 +2844,11 @@ pretty_print_func_ent(void *data, int size, struct event *event,
printf(" | ");
+ if (latency_format) {
+ print_lat_fmt(data, size);
+ printf(" | ");
+ }
+
field = find_field(event, "func");
if (!field)
die("function entry does not have func field");
@@ -2663,6 +2892,11 @@ pretty_print_func_ret(void *data, int size __unused, struct event *event,
printf(" | ");
+ if (latency_format) {
+ print_lat_fmt(data, size);
+ printf(" | ");
+ }
+
field = find_field(event, "rettime");
if (!field)
die("can't find rettime in return graph");
@@ -2724,7 +2958,7 @@ void print_event(int cpu, void *data, int size, unsigned long long nsecs,
event = trace_find_event(type);
if (!event) {
- printf("ug! no event found for type %d\n", type);
+ warning("ug! no event found for type %d", type);
return;
}
@@ -2734,9 +2968,20 @@ void print_event(int cpu, void *data, int size, unsigned long long nsecs,
return pretty_print_func_graph(data, size, event, cpu,
pid, comm, secs, usecs);
- printf("%16s-%-5d [%03d] %5lu.%09Lu: %s: ",
- comm, pid, cpu,
- secs, nsecs, event->name);
+ if (latency_format) {
+ printf("%8.8s-%-5d %3d",
+ comm, pid, cpu);
+ print_lat_fmt(data, size);
+ } else
+ printf("%16s-%-5d [%03d]", comm, pid, cpu);
+
+ printf(" %5lu.%06lu: %s: ", secs, usecs, event->name);
+
+ if (event->flags & EVENT_FL_FAILED) {
+ printf("EVENT '%s' FAILED TO PARSE\n",
+ event->name);
+ return;
+ }
pretty_print(data, size, event);
printf("\n");
@@ -2807,46 +3052,71 @@ static void print_args(struct print_arg *args)
}
}
-static void parse_header_field(char *type,
+static void parse_header_field(const char *field,
int *offset, int *size)
{
char *token;
+ int type;
- if (read_expected(EVENT_ITEM, (char *)"field") < 0)
+ if (read_expected(EVENT_ITEM, "field") < 0)
return;
- if (read_expected(EVENT_OP, (char *)":") < 0)
+ if (read_expected(EVENT_OP, ":") < 0)
return;
+
/* type */
if (read_expect_type(EVENT_ITEM, &token) < 0)
- return;
+ goto fail;
free_token(token);
- if (read_expected(EVENT_ITEM, type) < 0)
+ if (read_expected(EVENT_ITEM, field) < 0)
return;
- if (read_expected(EVENT_OP, (char *)";") < 0)
+ if (read_expected(EVENT_OP, ";") < 0)
return;
- if (read_expected(EVENT_ITEM, (char *)"offset") < 0)
+ if (read_expected(EVENT_ITEM, "offset") < 0)
return;
- if (read_expected(EVENT_OP, (char *)":") < 0)
+ if (read_expected(EVENT_OP, ":") < 0)
return;
if (read_expect_type(EVENT_ITEM, &token) < 0)
- return;
+ goto fail;
*offset = atoi(token);
free_token(token);
- if (read_expected(EVENT_OP, (char *)";") < 0)
+ if (read_expected(EVENT_OP, ";") < 0)
return;
- if (read_expected(EVENT_ITEM, (char *)"size") < 0)
+ if (read_expected(EVENT_ITEM, "size") < 0)
return;
- if (read_expected(EVENT_OP, (char *)":") < 0)
+ if (read_expected(EVENT_OP, ":") < 0)
return;
if (read_expect_type(EVENT_ITEM, &token) < 0)
- return;
+ goto fail;
*size = atoi(token);
free_token(token);
- if (read_expected(EVENT_OP, (char *)";") < 0)
- return;
- if (read_expect_type(EVENT_NEWLINE, &token) < 0)
+ if (read_expected(EVENT_OP, ";") < 0)
return;
+ type = read_token(&token);
+ if (type != EVENT_NEWLINE) {
+ /* newer versions of the kernel have a "signed" type */
+ if (type != EVENT_ITEM)
+ goto fail;
+
+ if (strcmp(token, "signed") != 0)
+ goto fail;
+
+ free_token(token);
+
+ if (read_expected(EVENT_OP, ":") < 0)
+ return;
+
+ if (read_expect_type(EVENT_ITEM, &token))
+ goto fail;
+
+ free_token(token);
+ if (read_expected(EVENT_OP, ";") < 0)
+ return;
+
+ if (read_expect_type(EVENT_NEWLINE, &token))
+ goto fail;
+ }
+ fail:
free_token(token);
}
@@ -2854,11 +3124,11 @@ int parse_header_page(char *buf, unsigned long size)
{
init_input_buf(buf, size);
- parse_header_field((char *)"timestamp", &header_page_ts_offset,
+ parse_header_field("timestamp", &header_page_ts_offset,
&header_page_ts_size);
- parse_header_field((char *)"commit", &header_page_size_offset,
+ parse_header_field("commit", &header_page_size_offset,
&header_page_size_size);
- parse_header_field((char *)"data", &header_page_data_offset,
+ parse_header_field("data", &header_page_data_offset,
&header_page_data_size);
return 0;
@@ -2909,6 +3179,9 @@ int parse_ftrace_file(char *buf, unsigned long size)
if (ret < 0)
die("failed to read ftrace event print fmt");
+ /* New ftrace handles args */
+ if (ret > 0)
+ return 0;
/*
* The arguments for ftrace files are parsed by the fields.
* Set up the fields as their arguments.
@@ -2926,7 +3199,7 @@ int parse_ftrace_file(char *buf, unsigned long size)
return 0;
}
-int parse_event_file(char *buf, unsigned long size, char *system__unused __unused)
+int parse_event_file(char *buf, unsigned long size, char *sys)
{
struct event *event;
int ret;
@@ -2946,12 +3219,18 @@ int parse_event_file(char *buf, unsigned long size, char *system__unused __unuse
die("failed to read event id");
ret = event_read_format(event);
- if (ret < 0)
- die("failed to read event format");
+ if (ret < 0) {
+ warning("failed to read event format for %s", event->name);
+ goto event_failed;
+ }
ret = event_read_print(event);
- if (ret < 0)
- die("failed to read event print fmt");
+ if (ret < 0) {
+ warning("failed to read event print fmt for %s", event->name);
+ goto event_failed;
+ }
+
+ event->system = strdup(sys);
#define PRINT_ARGS 0
if (PRINT_ARGS && event->print_fmt.args)
@@ -2959,6 +3238,12 @@ int parse_event_file(char *buf, unsigned long size, char *system__unused __unuse
add_event(event);
return 0;
+
+ event_failed:
+ event->flags |= EVENT_FL_FAILED;
+ /* still add it even if it failed */
+ add_event(event);
+ return -1;
}
void parse_set_info(int nr_cpus, int long_sz)
diff --git a/tools/perf/util/trace-event-read.c b/tools/perf/util/trace-event-read.c
index 1b5c847d2c22..44292e06cca4 100644
--- a/tools/perf/util/trace-event-read.c
+++ b/tools/perf/util/trace-event-read.c
@@ -458,9 +458,8 @@ struct record *trace_read_data(int cpu)
return data;
}
-void trace_report(void)
+void trace_report(int fd)
{
- const char *input_file = "trace.info";
char buf[BUFSIZ];
char test[] = { 23, 8, 68 };
char *version;
@@ -468,9 +467,7 @@ void trace_report(void)
int show_funcs = 0;
int show_printk = 0;
- input_fd = open(input_file, O_RDONLY);
- if (input_fd < 0)
- die("opening '%s'\n", input_file);
+ input_fd = fd;
read_or_die(buf, 3);
if (memcmp(buf, test, 3) != 0)
diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h
index 693f815c9429..f6637c2fa1fe 100644
--- a/tools/perf/util/trace-event.h
+++ b/tools/perf/util/trace-event.h
@@ -1,5 +1,5 @@
-#ifndef _TRACE_EVENTS_H
-#define _TRACE_EVENTS_H
+#ifndef __PERF_TRACE_EVENTS_H
+#define __PERF_TRACE_EVENTS_H
#include "parse-events.h"
@@ -26,6 +26,9 @@ enum {
enum format_flags {
FIELD_IS_ARRAY = 1,
FIELD_IS_POINTER = 2,
+ FIELD_IS_SIGNED = 4,
+ FIELD_IS_STRING = 8,
+ FIELD_IS_DYNAMIC = 16,
};
struct format_field {
@@ -132,15 +135,18 @@ struct event {
int flags;
struct format format;
struct print_fmt print_fmt;
+ char *system;
};
enum {
- EVENT_FL_ISFTRACE = 1,
- EVENT_FL_ISPRINT = 2,
- EVENT_FL_ISBPRINT = 4,
- EVENT_FL_ISFUNC = 8,
- EVENT_FL_ISFUNCENT = 16,
- EVENT_FL_ISFUNCRET = 32,
+ EVENT_FL_ISFTRACE = 0x01,
+ EVENT_FL_ISPRINT = 0x02,
+ EVENT_FL_ISBPRINT = 0x04,
+ EVENT_FL_ISFUNC = 0x08,
+ EVENT_FL_ISFUNCENT = 0x10,
+ EVENT_FL_ISFUNCRET = 0x20,
+
+ EVENT_FL_FAILED = 0x80000000
};
struct record {
@@ -154,7 +160,7 @@ struct record *trace_read_data(int cpu);
void parse_set_info(int nr_cpus, int long_sz);
-void trace_report(void);
+void trace_report(int fd);
void *malloc_or_die(unsigned int size);
@@ -166,7 +172,7 @@ void print_funcs(void);
void print_printk(void);
int parse_ftrace_file(char *buf, unsigned long size);
-int parse_event_file(char *buf, unsigned long size, char *system);
+int parse_event_file(char *buf, unsigned long size, char *sys);
void print_event(int cpu, void *data, int size, unsigned long long nsecs,
char *comm);
@@ -233,6 +239,8 @@ extern int header_page_size_size;
extern int header_page_data_offset;
extern int header_page_data_size;
+extern int latency_format;
+
int parse_header_page(char *buf, unsigned long size);
int trace_parse_common_type(void *data);
struct event *trace_find_event(int id);
@@ -240,6 +248,15 @@ unsigned long long
raw_field_value(struct event *event, const char *name, void *data);
void *raw_field_ptr(struct event *event, const char *name, void *data);
-void read_tracing_data(struct perf_event_attr *pattrs, int nb_events);
+void read_tracing_data(int fd, struct perf_event_attr *pattrs, int nb_events);
+
+/* taken from kernel/trace/trace.h */
+enum trace_flag_type {
+ TRACE_FLAG_IRQS_OFF = 0x01,
+ TRACE_FLAG_IRQS_NOSUPPORT = 0x02,
+ TRACE_FLAG_NEED_RESCHED = 0x04,
+ TRACE_FLAG_HARDIRQ = 0x08,
+ TRACE_FLAG_SOFTIRQ = 0x10,
+};
-#endif /* _TRACE_EVENTS_H */
+#endif /* __PERF_TRACE_EVENTS_H */
diff --git a/tools/perf/util/types.h b/tools/perf/util/types.h
index 5e75f9005940..7d6b8331f898 100644
--- a/tools/perf/util/types.h
+++ b/tools/perf/util/types.h
@@ -1,5 +1,5 @@
-#ifndef _PERF_TYPES_H
-#define _PERF_TYPES_H
+#ifndef __PERF_TYPES_H
+#define __PERF_TYPES_H
/*
* We define u64 as unsigned long long for every architecture
@@ -14,4 +14,4 @@ typedef signed short s16;
typedef unsigned char u8;
typedef signed char s8;
-#endif /* _PERF_TYPES_H */
+#endif /* __PERF_TYPES_H */
diff --git a/tools/perf/util/values.h b/tools/perf/util/values.h
index cadf8cf2a590..2fa967e1a88a 100644
--- a/tools/perf/util/values.h
+++ b/tools/perf/util/values.h
@@ -1,5 +1,5 @@
-#ifndef _PERF_VALUES_H
-#define _PERF_VALUES_H
+#ifndef __PERF_VALUES_H
+#define __PERF_VALUES_H
#include "types.h"
@@ -24,4 +24,4 @@ void perf_read_values_add_value(struct perf_read_values *values,
void perf_read_values_display(FILE *fp, struct perf_read_values *values,
int raw);
-#endif /* _PERF_VALUES_H */
+#endif /* __PERF_VALUES_H */