summaryrefslogtreecommitdiffstats
path: root/tools/perf/util
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/util')
-rw-r--r--tools/perf/util/data_map.c167
-rw-r--r--tools/perf/util/data_map.h32
-rw-r--r--tools/perf/util/event.c214
-rw-r--r--tools/perf/util/event.h64
-rw-r--r--tools/perf/util/header.c39
-rw-r--r--tools/perf/util/header.h4
-rw-r--r--tools/perf/util/hist.c518
-rw-r--r--tools/perf/util/hist.h55
-rw-r--r--tools/perf/util/map.c93
-rw-r--r--tools/perf/util/parse-events.c17
-rw-r--r--tools/perf/util/parse-options.c3
-rw-r--r--tools/perf/util/probe-event.c299
-rw-r--r--tools/perf/util/probe-event.h12
-rw-r--r--tools/perf/util/probe-finder.c6
-rw-r--r--tools/perf/util/probe-finder.h58
-rw-r--r--tools/perf/util/session.c150
-rw-r--r--tools/perf/util/session.h61
-rw-r--r--tools/perf/util/sort.c26
-rw-r--r--tools/perf/util/sort.h12
-rw-r--r--tools/perf/util/string.c25
-rw-r--r--tools/perf/util/string.h2
-rw-r--r--tools/perf/util/strlist.c6
-rw-r--r--tools/perf/util/strlist.h41
-rw-r--r--tools/perf/util/symbol.c355
-rw-r--r--tools/perf/util/symbol.h45
-rw-r--r--tools/perf/util/thread.c100
-rw-r--r--tools/perf/util/thread.h50
-rw-r--r--tools/perf/util/trace-event-parse.c4
-rw-r--r--tools/perf/util/trace-event-perl.c107
-rw-r--r--tools/perf/util/trace-event-perl.h4
-rw-r--r--tools/perf/util/trace-event-read.c3
-rw-r--r--tools/perf/util/trace-event.h2
32 files changed, 1956 insertions, 618 deletions
diff --git a/tools/perf/util/data_map.c b/tools/perf/util/data_map.c
index ca0bedf637c..b557b836de3 100644
--- a/tools/perf/util/data_map.c
+++ b/tools/perf/util/data_map.c
@@ -1,20 +1,17 @@
-#include "data_map.h"
#include "symbol.h"
#include "util.h"
#include "debug.h"
+#include "thread.h"
+#include "session.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)
+static int process_event_stub(event_t *event __used,
+ struct perf_session *session __used)
{
dump_printf(": unhandled!\n");
return 0;
}
-void register_perf_file_handler(struct perf_file_handler *handler)
+static void perf_event_ops__fill_defaults(struct perf_event_ops *handler)
{
if (!handler->process_sample_event)
handler->process_sample_event = process_event_stub;
@@ -34,8 +31,6 @@ void register_perf_file_handler(struct perf_file_handler *handler)
handler->process_throttle_event = process_event_stub;
if (!handler->process_unthrottle_event)
handler->process_unthrottle_event = process_event_stub;
-
- curr_handler = handler;
}
static const char *event__name[] = {
@@ -61,8 +56,9 @@ void event__print_totals(void)
event__name[i], event__total[i]);
}
-static int
-process_event(event_t *event, unsigned long offset, unsigned long head)
+static int process_event(event_t *event, struct perf_session *session,
+ struct perf_event_ops *ops,
+ unsigned long offset, unsigned long head)
{
trace_event(event);
@@ -77,34 +73,34 @@ process_event(event_t *event, unsigned long offset, unsigned long head)
switch (event->header.type) {
case PERF_RECORD_SAMPLE:
- return curr_handler->process_sample_event(event);
+ return ops->process_sample_event(event, session);
case PERF_RECORD_MMAP:
- return curr_handler->process_mmap_event(event);
+ return ops->process_mmap_event(event, session);
case PERF_RECORD_COMM:
- return curr_handler->process_comm_event(event);
+ return ops->process_comm_event(event, session);
case PERF_RECORD_FORK:
- return curr_handler->process_fork_event(event);
+ return ops->process_fork_event(event, session);
case PERF_RECORD_EXIT:
- return curr_handler->process_exit_event(event);
+ return ops->process_exit_event(event, session);
case PERF_RECORD_LOST:
- return curr_handler->process_lost_event(event);
+ return ops->process_lost_event(event, session);
case PERF_RECORD_READ:
- return curr_handler->process_read_event(event);
+ return ops->process_read_event(event, session);
case PERF_RECORD_THROTTLE:
- return curr_handler->process_throttle_event(event);
+ return ops->process_throttle_event(event, session);
case PERF_RECORD_UNTHROTTLE:
- return curr_handler->process_unthrottle_event(event);
+ return ops->process_unthrottle_event(event, session);
default:
- curr_handler->total_unknown++;
+ ops->total_unknown++;
return -1;
}
}
-int perf_header__read_build_ids(int input, off_t offset, off_t size)
+int perf_header__read_build_ids(int input, u64 offset, u64 size)
{
struct build_id_event bev;
char filename[PATH_MAX];
- off_t limit = offset + size;
+ u64 limit = offset + size;
int err = -1;
while (offset < limit) {
@@ -129,88 +125,58 @@ out:
return err;
}
-int mmap_dispatch_perf_file(struct perf_header **pheader,
- const char *input_name,
- int force,
- int full_paths,
- int *cwdlen,
- char **cwd)
+static struct thread *perf_session__register_idle_thread(struct perf_session *self)
+{
+ struct thread *thread = perf_session__findnew(self, 0);
+
+ if (!thread || thread__set_comm(thread, "swapper")) {
+ pr_err("problem inserting idle task.\n");
+ thread = NULL;
+ }
+
+ return thread;
+}
+
+int perf_session__process_events(struct perf_session *self,
+ struct perf_event_ops *ops)
{
int err;
- 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 == NULL) {
- pr_debug("Forgot to register perf file handler\n");
- return -EINVAL;
- }
-
- page_size = getpagesize();
-
- input = open(input_name, O_RDONLY);
- if (input < 0) {
- pr_err("Failed to open file: %s", input_name);
- if (!strcmp(input_name, "perf.data"))
- pr_err(" (try 'perf record' first)");
- pr_err("\n");
- return -errno;
- }
-
- if (fstat(input, &input_stat) < 0) {
- pr_err("failed to stat file");
- err = -errno;
- goto out_close;
- }
-
- err = -EACCES;
- if (!force && input_stat.st_uid && (input_stat.st_uid != geteuid())) {
- pr_err("file: %s not owned by current user or root\n",
- input_name);
- goto out_close;
- }
-
- if (input_stat.st_size == 0) {
- pr_info("zero-sized file, nothing to do!\n");
- goto done;
- }
+ if (perf_session__register_idle_thread(self) == NULL)
+ return -ENOMEM;
- err = -ENOMEM;
- header = perf_header__new();
- if (header == NULL)
- goto out_close;
+ perf_event_ops__fill_defaults(ops);
- err = perf_header__read(header, input);
- if (err < 0)
- goto out_delete;
- *pheader = header;
- head = header->data_offset;
+ page_size = getpagesize();
- sample_type = perf_header__sample_type(header);
+ head = self->header.data_offset;
+ self->sample_type = perf_header__sample_type(&self->header);
err = -EINVAL;
- if (curr_handler->sample_type_check &&
- curr_handler->sample_type_check(sample_type) < 0)
- goto out_delete;
+ if (ops->sample_type_check && ops->sample_type_check(self) < 0)
+ goto out_err;
- if (!full_paths) {
- if (getcwd(__cwd, sizeof(__cwd)) == NULL) {
- pr_err("failed to get the current directory\n");
+ if (!ops->full_paths) {
+ char bf[PATH_MAX];
+
+ if (getcwd(bf, sizeof(bf)) == NULL) {
err = -errno;
- goto out_delete;
+out_getcwd_err:
+ pr_err("failed to get the current directory\n");
+ goto out_err;
+ }
+ self->cwd = strdup(bf);
+ if (self->cwd == NULL) {
+ err = -ENOMEM;
+ goto out_getcwd_err;
}
- *cwd = __cwd;
- *cwdlen = strlen(*cwd);
- } else {
- *cwd = NULL;
- *cwdlen = 0;
+ self->cwdlen = strlen(self->cwd);
}
shift = page_size * (head / page_size);
@@ -218,12 +184,12 @@ int mmap_dispatch_perf_file(struct perf_header **pheader,
head -= shift;
remap:
- buf = mmap(NULL, page_size * mmap_window, PROT_READ,
- MAP_SHARED, input, offset);
+ buf = mmap(NULL, page_size * self->mmap_window, PROT_READ,
+ MAP_SHARED, self->fd, offset);
if (buf == MAP_FAILED) {
pr_err("failed to mmap file\n");
err = -errno;
- goto out_delete;
+ goto out_err;
}
more:
@@ -233,12 +199,12 @@ more:
if (!size)
size = 8;
- if (head + event->header.size >= page_size * mmap_window) {
+ if (head + event->header.size >= page_size * self->mmap_window) {
int munmap_ret;
shift = page_size * (head / page_size);
- munmap_ret = munmap(buf, page_size * mmap_window);
+ munmap_ret = munmap(buf, page_size * self->mmap_window);
assert(munmap_ret == 0);
offset += shift;
@@ -253,7 +219,7 @@ more:
(void *)(long)event->header.size,
event->header.type);
- if (!size || process_event(event, offset, head) < 0) {
+ if (!size || process_event(event, self, ops, offset, head) < 0) {
dump_printf("%p [%p]: skipping unknown header type: %d\n",
(void *)(offset + head),
@@ -273,19 +239,14 @@ more:
head += size;
- if (offset + head >= header->data_offset + header->data_size)
+ if (offset + head >= self->header.data_offset + self->header.data_size)
goto done;
- if (offset + head < (unsigned long)input_stat.st_size)
+ if (offset + head < self->size)
goto more;
done:
err = 0;
-out_close:
- close(input);
-
+out_err:
return err;
-out_delete:
- perf_header__delete(header);
- goto out_close;
}
diff --git a/tools/perf/util/data_map.h b/tools/perf/util/data_map.h
deleted file mode 100644
index 3180ff7e363..00000000000
--- a/tools/perf/util/data_map.h
+++ /dev/null
@@ -1,32 +0,0 @@
-#ifndef __PERF_DATAMAP_H
-#define __PERF_DATAMAP_H
-
-#include "event.h"
-#include "header.h"
-
-typedef int (*event_type_handler_t)(event_t *);
-
-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);
-int perf_header__read_build_ids(int input, off_t offset, off_t file_size);
-
-#endif
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
index 414b89d1bde..bb0fd6da2d5 100644
--- a/tools/perf/util/event.c
+++ b/tools/perf/util/event.c
@@ -1,11 +1,16 @@
#include <linux/types.h>
#include "event.h"
#include "debug.h"
+#include "session.h"
+#include "sort.h"
#include "string.h"
+#include "strlist.h"
#include "thread.h"
static pid_t event__synthesize_comm(pid_t pid, int full,
- int (*process)(event_t *event))
+ int (*process)(event_t *event,
+ struct perf_session *session),
+ struct perf_session *session)
{
event_t ev;
char filename[PATH_MAX];
@@ -54,7 +59,7 @@ out_race:
if (!full) {
ev.comm.tid = pid;
- process(&ev);
+ process(&ev, session);
goto out_fclose;
}
@@ -72,7 +77,7 @@ out_race:
ev.comm.tid = pid;
- process(&ev);
+ process(&ev, session);
}
closedir(tasks);
@@ -86,7 +91,9 @@ out_failure:
}
static int event__synthesize_mmap_events(pid_t pid, pid_t tgid,
- int (*process)(event_t *event))
+ int (*process)(event_t *event,
+ struct perf_session *session),
+ struct perf_session *session)
{
char filename[PATH_MAX];
FILE *fp;
@@ -141,7 +148,7 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid,
ev.mmap.pid = tgid;
ev.mmap.tid = pid;
- process(&ev);
+ process(&ev, session);
}
}
@@ -149,15 +156,20 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid,
return 0;
}
-int event__synthesize_thread(pid_t pid, int (*process)(event_t *event))
+int event__synthesize_thread(pid_t pid,
+ int (*process)(event_t *event,
+ struct perf_session *session),
+ struct perf_session *session)
{
- pid_t tgid = event__synthesize_comm(pid, 1, process);
+ pid_t tgid = event__synthesize_comm(pid, 1, process, session);
if (tgid == -1)
return -1;
- return event__synthesize_mmap_events(pid, tgid, process);
+ return event__synthesize_mmap_events(pid, tgid, process, session);
}
-void event__synthesize_threads(int (*process)(event_t *event))
+void event__synthesize_threads(int (*process)(event_t *event,
+ struct perf_session *session),
+ struct perf_session *session)
{
DIR *proc;
struct dirent dirent, *next;
@@ -171,24 +183,47 @@ void event__synthesize_threads(int (*process)(event_t *event))
if (*end) /* only interested in proper numerical dirents */
continue;
- event__synthesize_thread(pid, process);
+ event__synthesize_thread(pid, process, session);
}
closedir(proc);
}
-char *event__cwd;
-int event__cwdlen;
+static void thread__comm_adjust(struct thread *self)
+{
+ char *comm = self->comm;
+
+ if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep &&
+ (!symbol_conf.comm_list ||
+ strlist__has_entry(symbol_conf.comm_list, comm))) {
+ unsigned int slen = strlen(comm);
+
+ if (slen > comms__col_width) {
+ comms__col_width = slen;
+ threads__col_width = slen + 6;
+ }
+ }
+}
+
+static int thread__set_comm_adjust(struct thread *self, const char *comm)
+{
+ int ret = thread__set_comm(self, comm);
+
+ if (ret)
+ return ret;
-struct events_stats event__stats;
+ thread__comm_adjust(self);
-int event__process_comm(event_t *self)
+ return 0;
+}
+
+int event__process_comm(event_t *self, struct perf_session *session)
{
- struct thread *thread = threads__findnew(self->comm.pid);
+ struct thread *thread = perf_session__findnew(session, self->comm.pid);
dump_printf(": %s:%d\n", self->comm.comm, self->comm.pid);
- if (thread == NULL || thread__set_comm(thread, self->comm.comm)) {
+ if (thread == NULL || thread__set_comm_adjust(thread, self->comm.comm)) {
dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n");
return -1;
}
@@ -196,18 +231,18 @@ int event__process_comm(event_t *self)
return 0;
}
-int event__process_lost(event_t *self)
+int event__process_lost(event_t *self, struct perf_session *session)
{
dump_printf(": id:%Ld: lost:%Ld\n", self->lost.id, self->lost.lost);
- event__stats.lost += self->lost.lost;
+ session->events_stats.lost += self->lost.lost;
return 0;
}
-int event__process_mmap(event_t *self)
+int event__process_mmap(event_t *self, struct perf_session *session)
{
- struct thread *thread = threads__findnew(self->mmap.pid);
+ struct thread *thread = perf_session__findnew(session, self->mmap.pid);
struct map *map = map__new(&self->mmap, MAP__FUNCTION,
- event__cwd, event__cwdlen);
+ session->cwd, session->cwdlen);
dump_printf(" %d/%d: [%p(%p) @ %p]: %s\n",
self->mmap.pid, self->mmap.tid,
@@ -224,10 +259,10 @@ int event__process_mmap(event_t *self)
return 0;
}
-int event__process_task(event_t *self)
+int event__process_task(event_t *self, struct perf_session *session)
{
- struct thread *thread = threads__findnew(self->fork.pid);
- struct thread *parent = threads__findnew(self->fork.ppid);
+ struct thread *thread = perf_session__findnew(session, self->fork.pid);
+ struct thread *parent = perf_session__findnew(session, self->fork.ppid);
dump_printf("(%d:%d):(%d:%d)\n", self->fork.pid, self->fork.tid,
self->fork.ppid, self->fork.ptid);
@@ -249,18 +284,20 @@ int event__process_task(event_t *self)
return 0;
}
-void thread__find_addr_location(struct thread *self, u8 cpumode,
+void thread__find_addr_location(struct thread *self,
+ struct perf_session *session, u8 cpumode,
enum map_type type, u64 addr,
struct addr_location *al,
symbol_filter_t filter)
{
- struct thread *thread = al->thread = self;
+ struct map_groups *mg = &self->mg;
+ al->thread = self;
al->addr = addr;
if (cpumode & PERF_RECORD_MISC_KERNEL) {
al->level = 'k';
- thread = kthread;
+ mg = &session->kmaps;
} else if (cpumode & PERF_RECORD_MISC_USER)
al->level = '.';
else {
@@ -270,7 +307,7 @@ void thread__find_addr_location(struct thread *self, u8 cpumode,
return;
}
try_again:
- al->map = thread__find_map(thread, type, al->addr);
+ al->map = map_groups__find(mg, type, al->addr);
if (al->map == NULL) {
/*
* If this is outside of all known maps, and is a negative
@@ -281,32 +318,139 @@ try_again:
* "[vdso]" dso, but for now lets use the old trick of looking
* in the whole kernel symbol list.
*/
- if ((long long)al->addr < 0 && thread != kthread) {
- thread = kthread;
+ if ((long long)al->addr < 0 && mg != &session->kmaps) {
+ mg = &session->kmaps;
goto try_again;
}
al->sym = NULL;
} else {
al->addr = al->map->map_ip(al->map, al->addr);
- al->sym = map__find_symbol(al->map, al->addr, filter);
+ al->sym = map__find_symbol(al->map, session, al->addr, filter);
}
}
-int event__preprocess_sample(const event_t *self, struct addr_location *al,
- symbol_filter_t filter)
+static void dso__calc_col_width(struct dso *self)
+{
+ if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep &&
+ (!symbol_conf.dso_list ||
+ strlist__has_entry(symbol_conf.dso_list, self->name))) {
+ unsigned int slen = strlen(self->name);
+ if (slen > dsos__col_width)
+ dsos__col_width = slen;
+ }
+
+ self->slen_calculated = 1;
+}
+
+int event__preprocess_sample(const event_t *self, struct perf_session *session,
+ struct addr_location *al, symbol_filter_t filter)
{
u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
- struct thread *thread = threads__findnew(self->ip.pid);
+ struct thread *thread = perf_session__findnew(session, self->ip.pid);
if (thread == NULL)
return -1;
+ if (symbol_conf.comm_list &&
+ !strlist__has_entry(symbol_conf.comm_list, thread->comm))
+ goto out_filtered;
+
dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid);
- thread__find_addr_location(thread, cpumode, MAP__FUNCTION,
+ thread__find_addr_location(thread, session, cpumode, MAP__FUNCTION,
self->ip.ip, al, filter);
dump_printf(" ...... dso: %s\n",
al->map ? al->map->dso->long_name :
al->level == 'H' ? "[hypervisor]" : "<not found>");
+ /*
+ * We have to do this here as we may have a dso with no symbol hit that
+ * has a name longer than the ones with symbols sampled.
+ */
+ if (al->map && !sort_dso.elide && !al->map->dso->slen_calculated)
+ dso__calc_col_width(al->map->dso);
+
+ if (symbol_conf.dso_list &&
+ (!al->map || !al->map->dso ||
+ !(strlist__has_entry(symbol_conf.dso_list, al->map->dso->short_name) ||
+ (al->map->dso->short_name != al->map->dso->long_name &&
+ strlist__has_entry(symbol_conf.dso_list, al->map->dso->long_name)))))
+ goto out_filtered;
+
+ if (symbol_conf.sym_list && al->sym &&
+ !strlist__has_entry(symbol_conf.sym_list, al->sym->name))
+ goto out_filtered;
+
+ al->filtered = false;
+ return 0;
+
+out_filtered:
+ al->filtered = true;
+ return 0;
+}
+
+int event__parse_sample(event_t *event, u64 type, struct sample_data *data)
+{
+ u64 *array = event->sample.array;
+
+ if (type & PERF_SAMPLE_IP) {
+ data->ip = event->ip.ip;
+ array++;
+ }
+
+ if (type & PERF_SAMPLE_TID) {
+ u32 *p = (u32 *)array;
+ data->pid = p[0];
+ data->tid = p[1];
+ array++;
+ }
+
+ if (type & PERF_SAMPLE_TIME) {
+ data->time = *array;
+ array++;
+ }
+
+ if (type & PERF_SAMPLE_ADDR) {
+ data->addr = *array;
+ array++;
+ }
+
+ if (type & PERF_SAMPLE_ID) {
+ data->id = *array;
+ array++;
+ }
+
+ if (type & PERF_SAMPLE_STREAM_ID) {
+ data->stream_id = *array;
+ array++;
+ }
+
+ if (type & PERF_SAMPLE_CPU) {
+ u32 *p = (u32 *)array;
+ data->cpu = *p;
+ array++;
+ }
+
+ if (type & PERF_SAMPLE_PERIOD) {
+ data->period = *array;
+ array++;
+ }
+
+ if (type & PERF_SAMPLE_READ) {
+ pr_debug("PERF_SAMPLE_READ is unsuported for now\n");
+ return -1;
+ }
+
+ if (type & PERF_SAMPLE_CALLCHAIN) {
+ data->callchain = (struct ip_callchain *)array;
+ array += 1 + data->callchain->nr;
+ }
+
+ if (type & PERF_SAMPLE_RAW) {
+ u32 *p = (u32 *)array;
+ data->raw_size = *p;
+ p++;
+ data->raw_data = p;
+ }
+
return 0;
}
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
index a4cc8105cf6..690a96d0467 100644
--- a/tools/perf/util/event.h
+++ b/tools/perf/util/event.h
@@ -56,11 +56,25 @@ struct read_event {
u64 id;
};
-struct sample_event{
+struct sample_event {
struct perf_event_header header;
u64 array[];
};
+struct sample_data {
+ u64 ip;
+ u32 pid, tid;
+ u64 time;
+ u64 addr;
+ u64 id;
+ u64 stream_id;
+ u32 cpu;
+ u64 period;
+ struct ip_callchain *callchain;
+ u32 raw_size;
+ void *raw_data;
+};
+
#define BUILD_ID_SIZE 20
struct build_id_event {
@@ -81,18 +95,19 @@ typedef union event_union {
} event_t;
struct events_stats {
- unsigned long total;
- unsigned long lost;
+ u64 total;
+ u64 lost;
};
void event__print_totals(void);
enum map_type {
MAP__FUNCTION = 0,
-
- MAP__NR_TYPES,
+ MAP__VARIABLE,
};
+#define MAP__NR_TYPES (MAP__VARIABLE + 1)
+
struct map {
union {
struct rb_node rb_node;
@@ -134,26 +149,35 @@ void map__delete(struct map *self);
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);
-struct symbol *map__find_symbol(struct map *self, u64 addr,
- symbol_filter_t filter);
+
+struct perf_session;
+
+int map__load(struct map *self, struct perf_session *session,
+ symbol_filter_t filter);
+struct symbol *map__find_symbol(struct map *self, struct perf_session *session,
+ u64 addr, symbol_filter_t filter);
+struct symbol *map__find_symbol_by_name(struct map *self, const char *name,
+ struct perf_session *session,
+ symbol_filter_t filter);
void map__fixup_start(struct map *self);
void map__fixup_end(struct map *self);
-int event__synthesize_thread(pid_t pid, int (*process)(event_t *event));
-void event__synthesize_threads(int (*process)(event_t *event));
-
-extern char *event__cwd;
-extern int event__cwdlen;
-extern struct events_stats event__stats;
-extern unsigned long event__total[PERF_RECORD_MAX];
+int event__synthesize_thread(pid_t pid,
+ int (*process)(event_t *event,
+ struct perf_session *session),
+ struct perf_session *session);
+void event__synthesize_threads(int (*process)(event_t *event,
+ struct perf_session *session),
+ struct perf_session *session);
-int event__process_comm(event_t *self);
-int event__process_lost(event_t *self);
-int event__process_mmap(event_t *self);
-int event__process_task(event_t *self);
+int event__process_comm(event_t *self, struct perf_session *session);
+int event__process_lost(event_t *self, struct perf_session *session);
+int event__process_mmap(event_t *self, struct perf_session *session);
+int event__process_task(event_t *self, struct perf_session *session);
struct addr_location;
-int event__preprocess_sample(const event_t *self, struct addr_location *al,
- symbol_filter_t filter);
+int event__preprocess_sample(const event_t *self, struct perf_session *session,
+ struct addr_location *al, symbol_filter_t filter);
+int event__parse_sample(event_t *event, u64 type, struct sample_data *data);
#endif /* __PERF_RECORD_H */
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 4805e6dfd23..8a0bca55106 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -8,8 +8,8 @@
#include "header.h"
#include "../perf.h"
#include "trace-event.h"
+#include "session.h"
#include "symbol.h"
-#include "data_map.h"
#include "debug.h"
/*
@@ -58,35 +58,19 @@ int perf_header_attr__add_id(struct perf_header_attr *self, u64 id)
return 0;
}
-/*
- * Create new perf.data header:
- */
-struct perf_header *perf_header__new(void)
+int perf_header__init(struct perf_header *self)
{
- struct perf_header *self = zalloc(sizeof(*self));
-
- if (self != NULL) {
- self->size = 1;
- self->attr = malloc(sizeof(void *));
-
- if (self->attr == NULL) {
- free(self);
- self = NULL;
- }
- }
-
- return self;
+ self->size = 1;
+ self->attr = malloc(sizeof(void *));
+ return self->attr == NULL ? -ENOMEM : 0;
}
-void perf_header__delete(struct perf_header *self)
+void perf_header__exit(struct perf_header *self)
{
int i;
-
for (i = 0; i < self->attrs; ++i)
- perf_header_attr__delete(self->attr[i]);
-
+ perf_header_attr__delete(self->attr[i]);
free(self->attr);
- free(self);
}
int perf_header__add_attr(struct perf_header *self,
@@ -187,7 +171,9 @@ static int do_write(int fd, const void *buf, size_t size)
static int __dsos__write_buildid_table(struct list_head *head, int fd)
{
+#define NAME_ALIGN 64
struct dso *pos;
+ static const char zero_buf[NAME_ALIGN];
list_for_each_entry(pos, head, node) {
int err;
@@ -197,14 +183,17 @@ static int __dsos__write_buildid_table(struct list_head *head, int fd)
if (!pos->has_build_id)
continue;
len = pos->long_name_len + 1;
- len = ALIGN(len, 64);
+ len = ALIGN(len, NAME_ALIGN);
memset(&b, 0, sizeof(b));
memcpy(&b.build_id, pos->build_id, sizeof(pos->build_id));
b.header.size = sizeof(b) + len;
err = do_write(fd, &b, sizeof(b));
if (err < 0)
return err;
- err = do_write(fd, pos->long_name, len);
+ err = do_write(fd, pos->long_name, pos->long_name_len + 1);
+ if (err < 0)
+ return err;
+ err = do_write(fd, zero_buf, len - pos->long_name_len - 1);
if (err < 0)
return err;
}
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h
index d1dbe2b79c4..d118d05d3ab 100644
--- a/tools/perf/util/header.h
+++ b/tools/perf/util/header.h
@@ -55,8 +55,8 @@ struct perf_header {
DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS);
};
-struct perf_header *perf_header__new(void);
-void perf_header__delete(struct perf_header *self);
+int perf_header__init(struct perf_header *self);
+void perf_header__exit(struct perf_header *self);
int perf_header__read(struct perf_header *self, int fd);
int perf_header__write(struct perf_header *self, int fd, bool at_exit);
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index 0ebf6ee16ca..e8daf5ca6fd 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -1,9 +1,7 @@
#include "hist.h"
-
-struct rb_root hist;
-struct rb_root collapse_hists;
-struct rb_root output_hists;
-int callchain;
+#include "session.h"
+#include "sort.h"
+#include <math.h>
struct callchain_param callchain_param = {
.mode = CHAIN_GRAPH_REL,
@@ -14,11 +12,12 @@ struct callchain_param callchain_param = {
* histogram, sorted on item, collects counts
*/
-struct hist_entry *__hist_entry__add(struct addr_location *al,
- struct symbol *sym_parent,
- u64 count, bool *hit)
+struct hist_entry *__perf_session__add_hist_entry(struct perf_session *self,
+ struct addr_location *al,
+ struct symbol *sym_parent,
+ u64 count, bool *hit)
{
- struct rb_node **p = &hist.rb_node;
+ struct rb_node **p = &self->hists.rb_node;
struct rb_node *parent = NULL;
struct hist_entry *he;
struct hist_entry entry = {
@@ -54,7 +53,7 @@ struct hist_entry *__hist_entry__add(struct addr_location *al,
return NULL;
*he = entry;
rb_link_node(&he->rb_node, parent, p);
- rb_insert_color(&he->rb_node, &hist);
+ rb_insert_color(&he->rb_node, &self->hists);
*hit = false;
return he;
}
@@ -102,9 +101,9 @@ void hist_entry__free(struct hist_entry *he)
* collapse the histogram
*/
-void collapse__insert_entry(struct hist_entry *he)
+static void collapse__insert_entry(struct rb_root *root, struct hist_entry *he)
{
- struct rb_node **p = &collapse_hists.rb_node;
+ struct rb_node **p = &root->rb_node;
struct rb_node *parent = NULL;
struct hist_entry *iter;
int64_t cmp;
@@ -128,38 +127,45 @@ void collapse__insert_entry(struct hist_entry *he)
}
rb_link_node(&he->rb_node, parent, p);
- rb_insert_color(&he->rb_node, &collapse_hists);
+ rb_insert_color(&he->rb_node, root);
}
-void collapse__resort(void)
+void perf_session__collapse_resort(struct perf_session *self)
{
+ struct rb_root tmp;
struct rb_node *next;
struct hist_entry *n;
if (!sort__need_collapse)
return;
- next = rb_first(&hist);
+ tmp = RB_ROOT;
+ next = rb_first(&self->hists);
+
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);
+ rb_erase(&n->rb_node, &self->hists);
+ collapse__insert_entry(&tmp, n);
}
+
+ self->hists = tmp;
}
/*
* reverse the map, sort on count.
*/
-void output__insert_entry(struct hist_entry *he, u64 min_callchain_hits)
+static void perf_session__insert_output_hist_entry(struct rb_root *root,
+ struct hist_entry *he,
+ u64 min_callchain_hits)
{
- struct rb_node **p = &output_hists.rb_node;
+ struct rb_node **p = &root->rb_node;
struct rb_node *parent = NULL;
struct hist_entry *iter;
- if (callchain)
+ if (symbol_conf.use_callchain)
callchain_param.sort(&he->sorted_chain, &he->callchain,
min_callchain_hits, &callchain_param);
@@ -174,29 +180,483 @@ void output__insert_entry(struct hist_entry *he, u64 min_callchain_hits)
}
rb_link_node(&he->rb_node, parent, p);
- rb_insert_color(&he->rb_node, &output_hists);
+ rb_insert_color(&he->rb_node, root);
}
-void output__resort(u64 total_samples)
+void perf_session__output_resort(struct perf_session *self, u64 total_samples)
{
+ struct rb_root tmp;
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);
+ tmp = RB_ROOT;
+ next = rb_first(&self->hists);
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);
+ rb_erase(&n->rb_node, &self->hists);
+ perf_session__insert_output_hist_entry(&tmp, n,
+ min_callchain_hits);
+ }
+
+ self->hists = tmp;
+}
+
+static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin)
+{
+ int i;
+ int ret = fprintf(fp, " ");
+
+ for (i = 0; i < left_margin; i++)
+ ret += fprintf(fp, " ");
+
+ return ret;
+}
+
+static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask,
+ int left_margin)
+{
+ int i;
+ size_t ret = callchain__fprintf_left_margin(fp, left_margin);
+
+ for (i = 0; i < depth; i++)
+ if (depth_mask & (1 << i))
+ ret += fprintf(fp, "| ");
+ else
+ ret += fprintf(fp, " ");
+
+ ret += fprintf(fp, "\n");
+
+ return ret;
+}
+
+static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain,
+ int depth, int depth_mask, int count,
+ u64 total_samples, int hits,
+ int left_margin)
+{
+ int i;
+ size_t ret = 0;
+
+ ret += callchain__fprintf_left_margin(fp, left_margin);
+ for (i = 0; i < depth; i++) {
+ if (depth_mask & (1 << i))
+ ret += fprintf(fp, "|");
+ else
+ ret += fprintf(fp, " ");
+ if (!count && i == depth - 1) {
+ double percent;
+
+ percent = hits * 100.0 / total_samples;
+ ret += percent_color_fprintf(fp, "--%2.2f%%-- ", percent);
+ } else
+ ret += fprintf(fp, "%s", " ");
+ }
+ if (chain->sym)
+ ret += fprintf(fp, "%s\n", chain->sym->name);
+ else
+ ret += fprintf(fp, "%p\n", (void *)(long)chain->ip);
+
+ return ret;
+}
+
+static struct symbol *rem_sq_bracket;
+static struct callchain_list rem_hits;
+
+static void init_rem_hits(void)
+{
+ rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6);
+ if (!rem_sq_bracket) {
+ fprintf(stderr, "Not enough memory to display remaining hits\n");
+ return;
+ }
+
+ strcpy(rem_sq_bracket->name, "[...]");
+ rem_hits.sym = rem_sq_bracket;
+}
+
+static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
+ u64 total_samples, int depth,
+ int depth_mask, int left_margin)
+{
+ struct rb_node *node, *next;
+ struct callchain_node *child;
+ struct callchain_list *chain;
+ int new_depth_mask = depth_mask;
+ u64 new_total;
+ u64 remaining;
+ size_t ret = 0;
+ int i;
+
+ if (callchain_param.mode == CHAIN_GRAPH_REL)
+ new_total = self->children_hit;
+ else
+ new_total = total_samples;
+
+ remaining = new_total;
+
+ node = rb_first(&self->rb_root);
+ while (node) {
+ u64 cumul;
+
+ child = rb_entry(node, struct callchain_node, rb_node);
+ cumul = cumul_hits(child);
+ remaining -= cumul;
+
+ /*
+ * The depth mask manages the output of pipes that show
+ * the depth. We don't want to keep the pipes of the current
+ * level for the last child of this depth.
+ * Except if we have remaining filtered hits. They will
+ * supersede the last child
+ */
+ next = rb_next(node);
+ if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining))
+ new_depth_mask &= ~(1 << (depth - 1));
+
+ /*
+ * But we keep the older depth mask for the line seperator
+ * to keep the level link until we reach the last child
+ */
+ ret += ipchain__fprintf_graph_line(fp, depth, depth_mask,
+ left_margin);
+ i = 0;
+ list_for_each_entry(chain, &child->val, list) {
+ if (chain->ip >= PERF_CONTEXT_MAX)
+ continue;
+ ret += ipchain__fprintf_graph(fp, chain, depth,
+ new_depth_mask, i++,
+ new_total,
+ cumul,
+ left_margin);
+ }
+ ret += __callchain__fprintf_graph(fp, child, new_total,
+ depth + 1,
+ new_depth_mask | (1 << depth),
+ left_margin);
+ node = next;
+ }
+
+ if (callchain_param.mode == CHAIN_GRAPH_REL &&
+ remaining && remaining != new_total) {
+
+ if (!rem_sq_bracket)
+ return ret;
+
+ new_depth_mask &= ~(1 << (depth - 1));
+
+ ret += ipchain__fprintf_graph(fp, &rem_hits, depth,
+ new_depth_mask, 0, new_total,
+ remaining, left_margin);
+ }
+
+ return ret;
+}
+
+static size_t callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
+ u64 total_samples, int left_margin)
+{
+ struct callchain_list *chain;
+ bool printed = false;
+ int i = 0;
+ int ret = 0;
+
+ list_for_each_entry(chain, &self->val, list) {
+ if (chain->ip >= PERF_CONTEXT_MAX)
+ continue;
+
+ if (!i++ && sort__first_dimension == SORT_SYM)
+ continue;
+
+ if (!printed) {
+ ret += callchain__fprintf_left_margin(fp, left_margin);
+ ret += fprintf(fp, "|\n");
+ ret += callchain__fprintf_left_margin(fp, left_margin);
+ ret += fprintf(fp, "---");
+
+ left_margin += 3;
+ printed = true;
+ } else
+ ret += callchain__fprintf_left_margin(fp, left_margin);
+
+ if (chain->sym)
+ ret += fprintf(fp, " %s\n", chain->sym->name);
+ else
+ ret += fprintf(fp, " %p\n", (void *)(long)chain->ip);
+ }
+
+ ret += __callchain__fprintf_graph(fp, self, total_samples, 1, 1, left_margin);
+
+ return ret;
+}
+
+static size_t callchain__fprintf_flat(FILE *fp, struct callchain_node *self,
+ u64 total_samples)
+{
+ struct callchain_list *chain;
+ size_t ret = 0;
+
+ if (!self)
+ return 0;
+
+ ret += callchain__fprintf_flat(fp, self->parent, total_samples);
+
+
+ list_for_each_entry(chain, &self->val, list) {
+ if (chain->ip >= PERF_CONTEXT_MAX)
+ continue;
+ if (chain->sym)
+ ret += fprintf(fp, " %s\n", chain->sym->name);
+ else
+ ret += fprintf(fp, " %p\n",
+ (void *)(long)chain->ip);
+ }
+
+ return ret;
+}
+
+static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self,
+ u64 total_samples, int left_margin)
+{
+ struct rb_node *rb_node;
+ struct callchain_node *chain;
+ size_t ret = 0;
+
+ rb_node = rb_first(&self->sorted_chain);
+ while (rb_node) {
+ double percent;
+
+ chain = rb_entry(rb_node, struct callchain_node, rb_node);
+ percent = chain->hit * 100.0 / total_samples;
+ switch (callchain_param.mode) {
+ case CHAIN_FLAT:
+ ret += percent_color_fprintf(fp, " %6.2f%%\n",
+ percent);
+ ret += callchain__fprintf_flat(fp, chain, total_samples);
+ break;
+ case CHAIN_GRAPH_ABS: /* Falldown */
+ case CHAIN_GRAPH_REL:
+ ret += callchain__fprintf_graph(fp, chain, total_samples,
+ left_margin);
+ case CHAIN_NONE:
+ default:
+ break;
+ }
+ ret += fprintf(fp, "\n");
+ rb_node = rb_next(rb_node);
+ }
+
+ return ret;
+}
+
+static size_t hist_entry__fprintf(struct hist_entry *self,
+ struct perf_session *session,
+ struct perf_session *pair_session,
+ bool show_displacement,
+ long displacement, FILE *fp)
+{
+ struct sort_entry *se;
+ u64 count, total;
+ const char *sep = symbol_conf.field_sep;
+ size_t ret;
+
+ if (symbol_conf.exclude_other && !self->parent)
+ return 0;
+
+ if (pair_session) {
+ count = self->pair ? self->pair->count : 0;
+ total = pair_session->events_stats.total;
+ } else {
+ count = self->count;
+ total = session->events_stats.total;
+ }
+
+ if (total)
+ ret = percent_color_fprintf(fp, sep ? "%.2f" : " %6.2f%%",
+ (count * 100.0) / total);
+ else
+ ret = fprintf(fp, sep ? "%lld" : "%12lld ", count);
+
+ if (symbol_conf.show_nr_samples) {
+ if (sep)
+ fprintf(fp, "%c%lld", *sep, count);
+ else
+ fprintf(fp, "%11lld", count);
+ }
+
+ if (pair_session) {
+ char bf[32];
+ double old_percent = 0, new_percent = 0, diff;
+
+ if (total > 0)
+ old_percent = (count * 100.0) / total;
+ if (session->events_stats.total > 0)
+ new_percent = (self->count * 100.0) / session->events_stats.total;
+
+ diff = new_percent - old_percent;
+
+ if (fabs(diff) >= 0.01)
+ snprintf(bf, sizeof(bf), "%+4.2F%%", diff);
+ else
+ snprintf(bf, sizeof(bf), " ");
+
+ if (sep)
+ ret += fprintf(fp, "%c%s", *sep, bf);
+ else
+ ret += fprintf(fp, "%11.11s", bf);
+
+ if (show_displacement) {
+ if (displacement)
+ snprintf(bf, sizeof(bf), "%+4ld", displacement);
+ else
+ snprintf(bf, sizeof(bf), " ");
+
+ if (sep)
+ fprintf(fp, "%c%s", *sep, bf);
+ else
+ fprintf(fp, "%6.6s", bf);
+ }
+ }
+
+ list_for_each_entry(se, &hist_entry__sort_list, list) {
+ if (se->elide)
+ continue;
+
+ fprintf(fp, "%s", sep ?: " ");
+ ret += se->print(fp, self, se->width ? *se->width : 0);
+ }
+
+ ret += fprintf(fp, "\n");
+
+ if (symbol_conf.use_callchain) {
+ int left_margin = 0;
+
+ if (sort__first_dimension == SORT_COMM) {
+ se = list_first_entry(&hist_entry__sort_list, typeof(*se),
+ list);
+ left_margin = se->width ? *se->width : 0;
+ left_margin -= thread__comm_len(self->thread);
+ }
+
+ hist_entry_callchain__fprintf(fp, self, session->events_stats.total,
+ left_margin);
+ }
+
+ return ret;
+}
+
+size_t perf_session__fprintf_hists(struct perf_session *self,
+ struct perf_session *pair,
+ bool show_displacement, FILE *fp)
+{
+ struct sort_entry *se;
+ struct rb_node *nd;
+ size_t ret = 0;
+ unsigned long position = 1;
+ long displacement = 0;
+ unsigned int width;
+ const char *sep = symbol_conf.field_sep;
+ char *col_width = symbol_conf.col_width_list_str;
+
+ init_rem_hits();
+
+ fprintf(fp, "# %s", pair ? "Baseline" : "Overhead");
+
+ if (symbol_conf.show_nr_samples) {
+ if (sep)
+ fprintf(fp, "%cSamples", *sep);
+ else
+ fputs(" Samples ", fp);
+ }
+
+ if (pair) {
+ if (sep)
+ ret += fprintf(fp, "%cDelta", *sep);
+ else
+ ret += fprintf(fp, " Delta ");
+
+ if (show_displacement) {
+ if (sep)
+ ret += fprintf(fp, "%cDisplacement", *sep);
+ else
+ ret += fprintf(fp, " Displ");
+ }
+ }
+
+ list_for_each_entry(se, &hist_entry__sort_list, list) {
+ if (se->elide)
+ continue;
+ if (sep) {
+ fprintf(fp, "%c%s", *sep, se->header);
+ continue;
+ }
+ width = strlen(se->header);
+ if (se->width) {
+ if (symbol_conf.col_width_list_str) {
+ if (col_width) {
+ *se->width = atoi(col_width);
+ col_width = strchr(col_width, ',');
+ if (col_width)
+ ++col_width;
+ }
+ }
+ width = *se->width = max(*se->width, width);
+ }
+ fprintf(fp, " %*s", width, se->header);
+ }
+ fprintf(fp, "\n");
+
+ if (sep)
+ goto print_entries;
+
+ fprintf(fp, "# ........");
+ if (symbol_conf.show_nr_samples)
+ fprintf(fp, " ..........");
+ if (pair) {
+ fprintf(fp, " ..........");
+ if (show_displacement)
+ fprintf(fp, " .....");
+ }
+ list_for_each_entry(se, &hist_entry__sort_list, list) {
+ unsigned int i;
+
+ if (se->elide)
+ continue;
+
+ fprintf(fp, " ");
+ if (se->width)
+ width = *se->width;
+ else
+ width = strlen(se->header);
+ for (i = 0; i < width; i++)
+ fprintf(fp, ".");
+ }
+
+ fprintf(fp, "\n#\n");
+
+print_entries:
+ for (nd = rb_first(&self->hists); nd; nd = rb_next(nd)) {
+ struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
+
+ if (show_displacement) {
+ if (h->pair != NULL)
+ displacement = ((long)h->pair->position -
+ (long)position);
+ else
+ displacement = 0;
+ ++position;
+ }
+ ret += hist_entry__fprintf(h, self, pair, show_displacement,
+ displacement, fp);
}
+
+ free(rem_sq_bracket);
+
+ return ret;
}
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h
index 3020db0c929..e5f99b24048 100644
--- a/tools/perf/util/hist.h
+++ b/tools/perf/util/hist.h
@@ -1,50 +1,27 @@
#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 <linux/types.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 addr_location *al,
- struct symbol *parent,
- u64 count, bool *hit);
+struct perf_session;
+struct hist_entry;
+struct addr_location;
+struct symbol;
+
+struct hist_entry *__perf_session__add_hist_entry(struct perf_session *self,
+ struct addr_location *al,
+ struct symbol *parent,
+ u64 count, 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);
+void hist_entry__free(struct hist_entry *);
+void perf_session__output_resort(struct perf_session *self, u64 total_samples);
+void perf_session__collapse_resort(struct perf_session *self);
+size_t perf_session__fprintf_hists(struct perf_session *self,
+ struct perf_session *pair,
+ bool show_displacement, FILE *fp);
#endif /* __PERF_HIST_H */
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
index 69f94fe9db2..c4d55a0da2e 100644
--- a/tools/perf/util/map.c
+++ b/tools/perf/util/map.c
@@ -104,43 +104,70 @@ void map__fixup_end(struct map *self)
#define DSO__DELETED "(deleted)"
-struct symbol *map__find_symbol(struct map *self, u64 addr,
- symbol_filter_t filter)
+int map__load(struct map *self, struct perf_session *session,
+ symbol_filter_t filter)
{
- if (!dso__loaded(self->dso, self->type)) {
- int nr = dso__load(self->dso, self, filter);
-
- if (nr < 0) {
- if (self->dso->has_build_id) {
- char sbuild_id[BUILD_ID_SIZE * 2 + 1];
-
- build_id__sprintf(self->dso->build_id,
- sizeof(self->dso->build_id),
- sbuild_id);
- pr_warning("%s with build id %s not found",
- self->dso->long_name, sbuild_id);
- } else
- pr_warning("Failed to open %s",
- self->dso->long_name);
- pr_warning(", continuing without symbols\n");
- return NULL;
- } else if (nr == 0) {
- const char *name = self->dso->long_name;
- const size_t len = strlen(name);
- const size_t real_len = len - sizeof(DSO__DELETED);
-
- if (len > sizeof(DSO__DELETED) &&
- strcmp(name + real_len + 1, DSO__DELETED) == 0) {
- pr_warning("%.*s was updated, restart the long running apps that use it!\n",
- (int)real_len, name);
- } else {
- pr_warning("no symbols found in %s, maybe install a debug package?\n", name);
- }
- return NULL;
+ const char *name = self->dso->long_name;
+ int nr;
+
+ if (dso__loaded(self->dso, self->type))
+ return 0;
+
+ nr = dso__load(self->dso, self, session, filter);
+ if (nr < 0) {
+ if (self->dso->has_build_id) {
+ char sbuild_id[BUILD_ID_SIZE * 2 + 1];
+
+ build_id__sprintf(self->dso->build_id,
+ sizeof(self->dso->build_id),
+ sbuild_id);
+ pr_warning("%s with build id %s not found",
+ name, sbuild_id);
+ } else
+ pr_warning("Failed to open %s", name);
+
+ pr_warning(", continuing without symbols\n");
+ return -1;
+ } else if (nr == 0) {
+ const size_t len = strlen(name);
+ const size_t real_len = len - sizeof(DSO__DELETED);
+
+ if (len > sizeof(DSO__DELETED) &&
+ strcmp(name + real_len + 1, DSO__DELETED) == 0) {
+ pr_warning("%.*s was updated, restart the long "
+ "running apps that use it!\n",
+ (int)real_len, name);
+ } else {
+ pr_warning("no symbols found in %s, maybe install "
+ "a debug package?\n", name);
}
+
+ return -1;
}
- return self->dso->find_symbol(self->dso, self->type, addr);
+ return 0;
+}
+
+struct symbol *map__find_symbol(struct map *self, struct perf_session *session,
+ u64 addr, symbol_filter_t filter)
+{
+ if (map__load(self, session, filter) < 0)
+ return NULL;
+
+ return dso__find_symbol(self->dso, self->type, addr);
+}
+
+struct symbol *map__find_symbol_by_name(struct map *self, const char *name,
+ struct perf_session *session,
+ symbol_filter_t filter)
+{
+ if (map__load(self, session, filter) < 0)
+ return NULL;
+
+ if (!dso__sorted_by_name(self->dso, self->type))
+ dso__sort_by_name(self->dso, self->type);
+
+ return dso__find_symbol_by_name(self->dso, self->type, name);
}
struct map *map__clone(struct map *self)
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 9e5dbd66d34..e5bc0fb016b 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -197,7 +197,7 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config)
if (id == config) {
closedir(evt_dir);
closedir(sys_dir);
- path = zalloc(sizeof(path));
+ path = zalloc(sizeof(*path));
path->system = malloc(MAX_EVENT_LENGTH);
if (!path->system) {
free(path);
@@ -467,7 +467,6 @@ parse_subsystem_tracepoint_event(char *sys_name, char *flags)
while ((evt_ent = readdir(evt_dir))) {
char event_opt[MAX_EVOPT_LEN + 1];
int len;
- unsigned int rem = MAX_EVOPT_LEN;
if (!strcmp(evt_ent->d_name, ".")
|| !strcmp(evt_ent->d_name, "..")
@@ -475,20 +474,12 @@ parse_subsystem_tracepoint_event(char *sys_name, char *flags)
|| !strcmp(evt_ent->d_name, "filter"))
continue;
- len = snprintf(event_opt, MAX_EVOPT_LEN, "%s:%s", sys_name,
- evt_ent->d_name);
+ len = snprintf(event_opt, MAX_EVOPT_LEN, "%s:%s%s%s", sys_name,
+ evt_ent->d_name, flags ? ":" : "",
+ flags ?: "");
if (len < 0)
return EVT_FAILED;
- rem -= len;
- if (flags) {
- if (rem < strlen(flags) + 1)
- return EVT_FAILED;
-
- strcat(event_opt, ":");
- strcat(event_opt, flags);
- }
-
if (parse_events(NULL, event_opt, 0))
return EVT_FAILED;
}
diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c
index 6d8af48c925..efebd5b476b 100644
--- a/tools/perf/util/parse-options.c
+++ b/tools/perf/util/parse-options.c
@@ -430,6 +430,9 @@ int usage_with_options_internal(const char * const *usagestr,
pos = fprintf(stderr, " ");
if (opts->short_name)
pos += fprintf(stderr, "-%c", opts->short_name);
+ else
+ pos += fprintf(stderr, " ");
+
if (opts->long_name && opts->short_name)
pos += fprintf(stderr, ", ");
if (opts->long_name)
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index cd7fbda5e2a..29465d44004 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -48,6 +48,9 @@
/* If there is no space to write, returns -E2BIG. */
static int e_snprintf(char *str, size_t size, const char *format, ...)
+ __attribute__((format(printf, 3, 4)));
+
+static int e_snprintf(char *str, size_t size, const char *format, ...)
{
int ret;
va_list ap;
@@ -59,6 +62,18 @@ static int e_snprintf(char *str, size_t size, const char *format, ...)
return ret;
}
+/* Check the name is good for event/group */
+static bool check_event_name(const char *name)
+{
+ if (!isalpha(*name) && *name != '_')
+ return false;
+ while (*++name != '\0') {
+ if (!isalpha(*name) && !isdigit(*name) && *name != '_')
+ return false;
+ }
+ return true;
+}
+
/* Parse probepoint definition. */
static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp)
{
@@ -66,10 +81,26 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp)
char c, nc = 0;
/*
* <Syntax>
- * perf probe SRC:LN
- * perf probe FUNC[+OFFS|%return][@SRC]
+ * perf probe [EVENT=]SRC:LN
+ * perf probe [EVENT=]FUNC[+OFFS|%return][@SRC]
+ *
+ * TODO:Group name support
*/
+ ptr = strchr(arg, '=');
+ if (ptr) { /* Event name */
+ *ptr = '\0';
+ tmp = ptr + 1;
+ ptr = strchr(arg, ':');
+ if (ptr) /* Group name is not supported yet. */
+ semantic_error("Group name is not supported yet.");
+ if (!check_event_name(arg))
+ semantic_error("%s is bad for event name -it must "
+ "follow C symbol-naming rule.", arg);
+ pp->event = strdup(arg);
+ arg = tmp;
+ }
+
ptr = strpbrk(arg, ":+@%");
if (ptr) {
nc = *ptr;
@@ -147,10 +178,13 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp)
}
/* Parse perf-probe event definition */
-int parse_perf_probe_event(const char *str, struct probe_point *pp)
+void parse_perf_probe_event(const char *str, struct probe_point *pp,
+ bool *need_dwarf)
{
char **argv;
- int argc, i, need_dwarf = 0;
+ int argc, i;
+
+ *need_dwarf = false;
argv = argv_split(str, &argc);
if (!argv)
@@ -161,7 +195,7 @@ int parse_perf_probe_event(const char *str, struct probe_point *pp)
/* Parse probe point */
parse_perf_probe_probepoint(argv[0], pp);
if (pp->file || pp->line)
- need_dwarf = 1;
+ *need_dwarf = true;
/* Copy arguments and ensure return probe has no C argument */
pp->nr_args = argc - 1;
@@ -174,17 +208,15 @@ int parse_perf_probe_event(const char *str, struct probe_point *pp)
if (pp->retprobe)
semantic_error("You can't specify local"
" variable for kretprobe");
- need_dwarf = 1;
+ *need_dwarf = true;
}
}
argv_free(argv);
- return need_dwarf;
}
/* Parse kprobe_events event into struct probe_point */
-void parse_trace_kprobe_event(const char *str, char **group, char **event,
- struct probe_point *pp)
+void parse_trace_kprobe_event(const char *str, struct probe_point *pp)
{
char pr;
char *p;
@@ -200,18 +232,17 @@ void parse_trace_kprobe_event(const char *str, char **group, char **event,
/* Scan event and group name. */
ret = sscanf(argv[0], "%c:%a[^/ \t]/%a[^ \t]",
- &pr, (float *)(void *)group, (float *)(void *)event);
+ &pr, (float *)(void *)&pp->group,
+ (float *)(void *)&pp->event);
if (ret != 3)
semantic_error("Failed to parse event name: %s", argv[0]);
- pr_debug("Group:%s Event:%s probe:%c\n", *group, *event, pr);
-
- if (!pp)
- goto end;
+ pr_debug("Group:%s Event:%s probe:%c\n", pp->group, pp->event, pr);
pp->retprobe = (pr == 'r');
/* Scan function name and offset */
- ret = sscanf(argv[1], "%a[^+]+%d", (float *)(void *)&pp->function, &pp->offset);
+ ret = sscanf(argv[1], "%a[^+]+%d", (float *)(void *)&pp->function,
+ &pp->offset);
if (ret == 1)
pp->offset = 0;
@@ -230,15 +261,15 @@ void parse_trace_kprobe_event(const char *str, char **group, char **event,
die("Failed to copy argument.");
}
-end:
argv_free(argv);
}
-int synthesize_perf_probe_event(struct probe_point *pp)
+/* Synthesize only probe point (not argument) */
+int synthesize_perf_probe_point(struct probe_point *pp)
{
char *buf;
char offs[64] = "", line[64] = "";
- int i, len, ret;
+ int ret;
pp->probes[0] = buf = zalloc(MAX_CMDLEN);
if (!buf)
@@ -258,11 +289,25 @@ int synthesize_perf_probe_event(struct probe_point *pp)
ret = e_snprintf(buf, MAX_CMDLEN, "%s%s%s%s", pp->function,
offs, pp->retprobe ? "%return" : "", line);
else
- ret = e_snprintf(buf, MAX_CMDLEN, "%s%s%s%s", pp->file, line);
- if (ret <= 0)
- goto error;
- len = ret;
+ ret = e_snprintf(buf, MAX_CMDLEN, "%s%s", pp->file, line);
+ if (ret <= 0) {
+error:
+ free(pp->probes[0]);
+ pp->probes[0] = NULL;
+ }
+ return ret;
+}
+
+int synthesize_perf_probe_event(struct probe_point *pp)
+{
+ char *buf;
+ int i, len, ret;
+
+ len = synthesize_perf_probe_point(pp);
+ if (len < 0)
+ return 0;
+ buf = pp->probes[0];
for (i = 0; i < pp->nr_args; i++) {
ret = e_snprintf(&buf[len], MAX_CMDLEN - len, " %s",
pp->args[i]);
@@ -275,6 +320,7 @@ int synthesize_perf_probe_event(struct probe_point *pp)
return pp->found;
error:
free(pp->probes[0]);
+ pp->probes[0] = NULL;
return ret;
}
@@ -304,6 +350,7 @@ int synthesize_trace_kprobe_event(struct probe_point *pp)
return pp->found;
error:
free(pp->probes[0]);
+ pp->probes[0] = NULL;
return ret;
}
@@ -363,6 +410,10 @@ static void clear_probe_point(struct probe_point *pp)
{
int i;
+ if (pp->event)
+ free(pp->event);
+ if (pp->group)
+ free(pp->group);
if (pp->function)
free(pp->function);
if (pp->file)
@@ -373,15 +424,33 @@ static void clear_probe_point(struct probe_point *pp)
free(pp->args);
for (i = 0; i < pp->found; i++)
free(pp->probes[i]);
- memset(pp, 0, sizeof(pp));
+ memset(pp, 0, sizeof(*pp));
+}
+
+/* Show an event */
+static void show_perf_probe_event(const char *event, const char *place,
+ struct probe_point *pp)
+{
+ int i, ret;
+ char buf[128];
+
+ ret = e_snprintf(buf, 128, "%s:%s", pp->group, event);
+ if (ret < 0)
+ die("Failed to copy event: %s", strerror(-ret));
+ printf(" %-40s (on %s", buf, place);
+
+ if (pp->nr_args > 0) {
+ printf(" with");
+ for (i = 0; i < pp->nr_args; i++)
+ printf(" %s", pp->args[i]);
+ }
+ printf(")\n");
}
/* List up current perf-probe events */
void show_perf_probe_events(void)
{
- unsigned int i;
int fd;
- char *group, *event;
struct probe_point pp;
struct strlist *rawlist;
struct str_node *ent;
@@ -390,13 +459,12 @@ void show_perf_probe_events(void)
rawlist = get_trace_kprobe_event_rawlist(fd);
close(fd);
- for (i = 0; i < strlist__nr_entries(rawlist); i++) {
- ent = strlist__entry(rawlist, i);
- parse_trace_kprobe_event(ent->s, &group, &event, &pp);
- synthesize_perf_probe_event(&pp);
- printf("[%s:%s]\t%s\n", group, event, pp.probes[0]);
- free(group);
- free(event);
+ strlist__for_each(ent, rawlist) {
+ parse_trace_kprobe_event(ent->s, &pp);
+ /* Synthesize only event probe point */
+ synthesize_perf_probe_point(&pp);
+ /* Show an event */
+ show_perf_probe_event(pp.event, pp.probes[0], &pp);
clear_probe_point(&pp);
}
@@ -404,21 +472,27 @@ void show_perf_probe_events(void)
}
/* Get current perf-probe event names */
-static struct strlist *get_perf_event_names(int fd)
+static struct strlist *get_perf_event_names(int fd, bool include_group)
{
- unsigned int i;
- char *group, *event;
+ char buf[128];
struct strlist *sl, *rawlist;
struct str_node *ent;
+ struct probe_point pp;
+ memset(&pp, 0, sizeof(pp));
rawlist = get_trace_kprobe_event_rawlist(fd);
- sl = strlist__new(false, NULL);
- for (i = 0; i < strlist__nr_entries(rawlist); i++) {
- ent = strlist__entry(rawlist, i);
- parse_trace_kprobe_event(ent->s, &group, &event, NULL);
- strlist__add(sl, event);
- free(group);
+ sl = strlist__new(true, NULL);
+ strlist__for_each(ent, rawlist) {
+ parse_trace_kprobe_event(ent->s, &pp);
+ if (include_group) {
+ if (e_snprintf(buf, 128, "%s:%s", pp.group,
+ pp.event) < 0)
+ die("Failed to copy group:event name.");
+ strlist__add(sl, buf);
+ } else
+ strlist__add(sl, pp.event);
+ clear_probe_point(&pp);
}
strlist__delete(rawlist);
@@ -426,24 +500,36 @@ static struct strlist *get_perf_event_names(int fd)
return sl;
}
-static int write_trace_kprobe_event(int fd, const char *buf)
+static void write_trace_kprobe_event(int fd, const char *buf)
{
int ret;
+ pr_debug("Writing event: %s\n", buf);
ret = write(fd, buf, strlen(buf));
if (ret <= 0)
- die("Failed to create event.");
- else
- printf("Added new event: %s\n", buf);
-
- return ret;
+ die("Failed to write event: %s", strerror(errno));
}
static void get_new_event_name(char *buf, size_t len, const char *base,
- struct strlist *namelist)
+ struct strlist *namelist, bool allow_suffix)
{
int i, ret;
- for (i = 0; i < MAX_EVENT_INDEX; i++) {
+
+ /* Try no suffix */
+ ret = e_snprintf(buf, len, "%s", base);
+ if (ret < 0)
+ die("snprintf() failed: %s", strerror(-ret));
+ if (!strlist__has_entry(namelist, buf))
+ return;
+
+ if (!allow_suffix) {
+ pr_warning("Error: event \"%s\" already exists. "
+ "(Use -f to force duplicates.)\n", base);
+ die("Can't add new event.");
+ }
+
+ /* Try to add suffix */
+ for (i = 1; i < MAX_EVENT_INDEX; i++) {
ret = e_snprintf(buf, len, "%s_%d", base, i);
if (ret < 0)
die("snprintf() failed: %s", strerror(-ret));
@@ -454,31 +540,138 @@ static void get_new_event_name(char *buf, size_t len, const char *base,
die("Too many events are on the same function.");
}
-void add_trace_kprobe_events(struct probe_point *probes, int nr_probes)
+void add_trace_kprobe_events(struct probe_point *probes, int nr_probes,
+ bool force_add)
{
int i, j, fd;
struct probe_point *pp;
char buf[MAX_CMDLEN];
char event[64];
struct strlist *namelist;
+ bool allow_suffix;
fd = open_kprobe_events(O_RDWR, O_APPEND);
/* Get current event names */
- namelist = get_perf_event_names(fd);
+ namelist = get_perf_event_names(fd, false);
for (j = 0; j < nr_probes; j++) {
pp = probes + j;
+ if (!pp->event)
+ pp->event = strdup(pp->function);
+ if (!pp->group)
+ pp->group = strdup(PERFPROBE_GROUP);
+ DIE_IF(!pp->event || !pp->group);
+ /* If force_add is true, suffix search is allowed */
+ allow_suffix = force_add;
for (i = 0; i < pp->found; i++) {
/* Get an unused new event name */
- get_new_event_name(event, 64, pp->function, namelist);
+ get_new_event_name(event, 64, pp->event, namelist,
+ allow_suffix);
snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s\n",
pp->retprobe ? 'r' : 'p',
- PERFPROBE_GROUP, event,
+ pp->group, event,
pp->probes[i]);
write_trace_kprobe_event(fd, buf);
+ printf("Added new event:\n");
+ /* Get the first parameter (probe-point) */
+ sscanf(pp->probes[i], "%s", buf);
+ show_perf_probe_event(event, buf, pp);
/* Add added event name to namelist */
strlist__add(namelist, event);
+ /*
+ * Probes after the first probe which comes from same
+ * user input are always allowed to add suffix, because
+ * there might be several addresses corresponding to
+ * one code line.
+ */
+ allow_suffix = true;
+ }
+ }
+ /* Show how to use the event. */
+ printf("\nYou can now use it on all perf tools, such as:\n\n");
+ printf("\tperf record -e %s:%s -a sleep 1\n\n", PERFPROBE_GROUP, event);
+
+ strlist__delete(namelist);
+ close(fd);
+}
+
+static void __del_trace_kprobe_event(int fd, struct str_node *ent)
+{
+ char *p;
+ char buf[128];
+
+ /* Convert from perf-probe event to trace-kprobe event */
+ if (e_snprintf(buf, 128, "-:%s", ent->s) < 0)
+ die("Failed to copy event.");
+ p = strchr(buf + 2, ':');
+ if (!p)
+ die("Internal error: %s should have ':' but not.", ent->s);
+ *p = '/';
+
+ write_trace_kprobe_event(fd, buf);
+ printf("Remove event: %s\n", ent->s);
+}
+
+static void del_trace_kprobe_event(int fd, const char *group,
+ const char *event, struct strlist *namelist)
+{
+ char buf[128];
+ struct str_node *ent, *n;
+ int found = 0;
+
+ if (e_snprintf(buf, 128, "%s:%s", group, event) < 0)
+ die("Failed to copy event.");
+
+ if (strpbrk(buf, "*?")) { /* Glob-exp */
+ strlist__for_each_safe(ent, n, namelist)
+ if (strglobmatch(ent->s, buf)) {
+ found++;
+ __del_trace_kprobe_event(fd, ent);
+ strlist__remove(namelist, ent);
+ }
+ } else {
+ ent = strlist__find(namelist, buf);
+ if (ent) {
+ found++;
+ __del_trace_kprobe_event(fd, ent);
+ strlist__remove(namelist, ent);
}
}
+ if (found == 0)
+ pr_info("Info: event \"%s\" does not exist, could not remove it.\n", buf);
+}
+
+void del_trace_kprobe_events(struct strlist *dellist)
+{
+ int fd;
+ const char *group, *event;
+ char *p, *str;
+ struct str_node *ent;
+ struct strlist *namelist;
+
+ fd = open_kprobe_events(O_RDWR, O_APPEND);
+ /* Get current event names */
+ namelist = get_perf_event_names(fd, true);
+
+ strlist__for_each(ent, dellist) {
+ str = strdup(ent->s);
+ if (!str)
+ die("Failed to copy event.");
+ pr_debug("Parsing: %s\n", str);
+ p = strchr(str, ':');
+ if (p) {
+ group = str;
+ *p = '\0';
+ event = p + 1;
+ } else {
+ group = "*";
+ event = str;
+ }
+ pr_debug("Group: %s, Event: %s\n", group, event);
+ del_trace_kprobe_event(fd, group, event, namelist);
+ free(str);
+ }
+ strlist__delete(namelist);
close(fd);
}
+
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index 0c6fe56fe38..7f1d499118c 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -1,15 +1,19 @@
#ifndef _PROBE_EVENT_H
#define _PROBE_EVENT_H
+#include <stdbool.h>
#include "probe-finder.h"
#include "strlist.h"
-extern int parse_perf_probe_event(const char *str, struct probe_point *pp);
+extern void parse_perf_probe_event(const char *str, struct probe_point *pp,
+ bool *need_dwarf);
+extern int synthesize_perf_probe_point(struct probe_point *pp);
extern int synthesize_perf_probe_event(struct probe_point *pp);
-extern void parse_trace_kprobe_event(const char *str, char **group,
- char **event, struct probe_point *pp);
+extern void parse_trace_kprobe_event(const char *str, struct probe_point *pp);
extern int synthesize_trace_kprobe_event(struct probe_point *pp);
-extern void add_trace_kprobe_events(struct probe_point *probes, int nr_probes);
+extern void add_trace_kprobe_events(struct probe_point *probes, int nr_probes,
+ bool force_add);
+extern void del_trace_kprobe_events(struct strlist *dellist);
extern void show_perf_probe_events(void);
/* Maximum index number of event-name postfix */
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index 293cdfc1b8c..4b852c0d16a 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -106,7 +106,7 @@ static int strtailcmp(const char *s1, const char *s2)
{
int i1 = strlen(s1);
int i2 = strlen(s2);
- while (--i1 > 0 && --i2 > 0) {
+ while (--i1 >= 0 && --i2 >= 0) {
if (s1[i1] != s2[i2])
return s1[i1] - s2[i2];
}
@@ -687,10 +687,8 @@ int find_probepoint(int fd, struct probe_point *pp)
struct probe_finder pf = {.pp = pp};
ret = dwarf_init(fd, DW_DLC_READ, 0, 0, &__dw_debug, &__dw_error);
- if (ret != DW_DLV_OK) {
- pr_warning("No dwarf info found in the vmlinux - please rebuild with CONFIG_DEBUG_INFO.\n");
+ if (ret != DW_DLV_OK)
return -ENOENT;
- }
pp->found = 0;
while (++cu_number) {
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h
index bdebca6697d..a4086aaddb7 100644
--- a/tools/perf/util/probe-finder.h
+++ b/tools/perf/util/probe-finder.h
@@ -1,9 +1,9 @@
#ifndef _PROBE_FINDER_H
#define _PROBE_FINDER_H
-#define MAX_PATH_LEN 256
-#define MAX_PROBE_BUFFER 1024
-#define MAX_PROBES 128
+#define MAX_PATH_LEN 256
+#define MAX_PROBE_BUFFER 1024
+#define MAX_PROBES 128
static inline int is_c_varname(const char *name)
{
@@ -12,45 +12,53 @@ static inline int is_c_varname(const char *name)
}
struct probe_point {
+ char *event; /* Event name */
+ char *group; /* Event group */
+
/* Inputs */
- char *file; /* File name */
- int line; /* Line number */
+ char *file; /* File name */
+ int line; /* Line number */
- char *function; /* Function name */
- int offset; /* Offset bytes */
+ char *function; /* Function name */
+ int offset; /* Offset bytes */
- int nr_args; /* Number of arguments */
- char **args; /* Arguments */
+ int nr_args; /* Number of arguments */
+ char **args; /* Arguments */
- int retprobe; /* Return probe */
+ int retprobe; /* Return probe */
/* Output */
- int found; /* Number of found probe points */
- char *probes[MAX_PROBES]; /* Output buffers (will be allocated)*/
+ int found; /* Number of found probe points */
+ char *probes[MAX_PROBES]; /* Output buffers (will be allocated)*/
};
#ifndef NO_LIBDWARF
extern int find_probepoint(int fd, struct probe_point *pp);
-#include <libdwarf/dwarf.h>
-#include <libdwarf/libdwarf.h>
+/* Workaround for undefined _MIPS_SZLONG bug in libdwarf.h: */
+#ifndef _MIPS_SZLONG
+# define _MIPS_SZLONG 0
+#endif
+
+#include <dwarf.h>
+#include <libdwarf.h>
struct probe_finder {
- struct probe_point *pp; /* Target probe point */
+ struct probe_point *pp; /* Target probe point */
/* For function searching */
- Dwarf_Addr addr; /* Address */
- Dwarf_Unsigned fno; /* File number */
- Dwarf_Unsigned lno; /* Line number */
- Dwarf_Off inl_offs; /* Inline offset */
- Dwarf_Die cu_die; /* Current CU */
+ Dwarf_Addr addr; /* Address */
+ Dwarf_Unsigned fno; /* File number */
+ Dwarf_Unsigned lno; /* Line number */
+ Dwarf_Off inl_offs; /* Inline offset */
+ Dwarf_Die cu_die; /* Current CU */
/* For variable searching */
- Dwarf_Addr cu_base; /* Current CU base address */
- Dwarf_Locdesc fbloc; /* Location of Current Frame Base */
- const char *var; /* Current variable name */
- char *buf; /* Current output buffer */
- int len; /* Length of output buffer */
+ Dwarf_Addr cu_base; /* Current CU base address */
+ Dwarf_Locdesc fbloc; /* Location of Current Frame Base */
+ const char *var; /* Current variable name */
+ char *buf; /* Current output buffer */
+ int len; /* Length of output buffer */
};
#endif /* NO_LIBDWARF */
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
new file mode 100644
index 00000000000..ce3a6c8abe7
--- /dev/null
+++ b/tools/perf/util/session.c
@@ -0,0 +1,150 @@
+#include <linux/kernel.h>
+
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "session.h"
+#include "sort.h"
+#include "util.h"
+
+static int perf_session__open(struct perf_session *self, bool force)
+{
+ struct stat input_stat;
+
+ self->fd = open(self->filename, O_RDONLY);
+ if (self->fd < 0) {
+ pr_err("failed to open file: %s", self->filename);
+ if (!strcmp(self->filename, "perf.data"))
+ pr_err(" (try 'perf record' first)");
+ pr_err("\n");
+ return -errno;
+ }
+
+ if (fstat(self->fd, &input_stat) < 0)
+ goto out_close;
+
+ if (!force && input_stat.st_uid && (input_stat.st_uid != geteuid())) {
+ pr_err("file %s not owned by current user or root\n",
+ self->filename);
+ goto out_close;
+ }
+
+ if (!input_stat.st_size) {
+ pr_info("zero-sized file (%s), nothing to do!\n",
+ self->filename);
+ goto out_close;
+ }
+
+ if (perf_header__read(&self->header, self->fd) < 0) {
+ pr_err("incompatible file format");
+ goto out_close;
+ }
+
+ self->size = input_stat.st_size;
+ return 0;
+
+out_close:
+ close(self->fd);
+ self->fd = -1;
+ return -1;
+}
+
+struct perf_session *perf_session__new(const char *filename, int mode, bool force)
+{
+ size_t len = filename ? strlen(filename) + 1 : 0;
+ struct perf_session *self = zalloc(sizeof(*self) + len);
+
+ if (self == NULL)
+ goto out;
+
+ if (perf_header__init(&self->header) < 0)
+ goto out_free;
+
+ memcpy(self->filename, filename, len);
+ self->threads = RB_ROOT;
+ self->last_match = NULL;
+ self->mmap_window = 32;
+ self->cwd = NULL;
+ self->cwdlen = 0;
+ map_groups__init(&self->kmaps);
+
+ if (perf_session__create_kernel_maps(self) < 0)
+ goto out_delete;
+
+ if (mode == O_RDONLY && perf_session__open(self, force) < 0)
+ goto out_delete;
+out:
+ return self;
+out_free:
+ free(self);
+ return NULL;
+out_delete:
+ perf_session__delete(self);
+ return NULL;
+}
+
+void perf_session__delete(struct perf_session *self)
+{
+ perf_header__exit(&self->header);
+ close(self->fd);
+ free(self->cwd);
+ free(self);
+}
+
+static bool symbol__match_parent_regex(struct symbol *sym)
+{
+ if (sym->name && !regexec(&parent_regex, sym->name, 0, NULL, 0))
+ return 1;
+
+ return 0;
+}
+
+struct symbol **perf_session__resolve_callchain(struct perf_session *self,
+ struct thread *thread,
+ struct ip_callchain *chain,
+ struct symbol **parent)
+{
+ u8 cpumode = PERF_RECORD_MISC_USER;
+ struct symbol **syms = NULL;
+ unsigned int i;
+
+ if (symbol_conf.use_callchain) {
+ syms = calloc(chain->nr, sizeof(*syms));
+ if (!syms) {
+ fprintf(stderr, "Can't allocate memory for symbols\n");
+ exit(-1);
+ }
+ }
+
+ for (i = 0; i < chain->nr; i++) {
+ u64 ip = chain->ips[i];
+ struct addr_location al;
+
+ if (ip >= PERF_CONTEXT_MAX) {
+ switch (ip) {
+ case PERF_CONTEXT_HV:
+ cpumode = PERF_RECORD_MISC_HYPERVISOR; break;
+ case PERF_CONTEXT_KERNEL:
+ cpumode = PERF_RECORD_MISC_KERNEL; break;
+ case PERF_CONTEXT_USER:
+ cpumode = PERF_RECORD_MISC_USER; break;
+ default:
+ break;
+ }
+ continue;
+ }
+
+ thread__find_addr_location(thread, self, cpumode,
+ MAP__FUNCTION, ip, &al, NULL);
+ if (al.sym != NULL) {
+ if (sort__has_parent && !*parent &&
+ symbol__match_parent_regex(al.sym))
+ *parent = al.sym;
+ if (!symbol_conf.use_callchain)
+ break;
+ syms[i] = al.sym;
+ }
+ }
+
+ return syms;
+}
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
new file mode 100644
index 00000000000..32eaa1bada0
--- /dev/null
+++ b/tools/perf/util/session.h
@@ -0,0 +1,61 @@
+#ifndef __PERF_SESSION_H
+#define __PERF_SESSION_H
+
+#include "event.h"
+#include "header.h"
+#include "thread.h"
+#include <linux/rbtree.h>
+#include "../../../include/linux/perf_event.h"
+
+struct ip_callchain;
+struct thread;
+struct symbol;
+
+struct perf_session {
+ struct perf_header header;
+ unsigned long size;
+ unsigned long mmap_window;
+ struct map_groups kmaps;
+ struct rb_root threads;
+ struct thread *last_match;
+ struct events_stats events_stats;
+ unsigned long event_total[PERF_RECORD_MAX];
+ struct rb_root hists;
+ u64 sample_type;
+ int fd;
+ int cwdlen;
+ char *cwd;
+ char filename[0];
+};
+
+typedef int (*event_op)(event_t *self, struct perf_session *session);
+
+struct perf_event_ops {
+ event_op process_sample_event;
+ event_op process_mmap_event;
+ event_op process_comm_event;
+ event_op process_fork_event;
+ event_op process_exit_event;
+ event_op process_lost_event;
+ event_op process_read_event;
+ event_op process_throttle_event;
+ event_op process_unthrottle_event;
+ int (*sample_type_check)(struct perf_session *session);
+ unsigned long total_unknown;
+ bool full_paths;
+};
+
+struct perf_session *perf_session__new(const char *filename, int mode, bool force);
+void perf_session__delete(struct perf_session *self);
+
+int perf_session__process_events(struct perf_session *self,
+ struct perf_event_ops *event_ops);
+
+struct symbol **perf_session__resolve_callchain(struct perf_session *self,
+ struct thread *thread,
+ struct ip_callchain *chain,
+ struct symbol **parent);
+
+int perf_header__read_build_ids(int input, u64 offset, u64 file_size);
+
+#endif /* __PERF_SESSION_H */
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c
index b490354d1b2..cb0f327de9e 100644
--- a/tools/perf/util/sort.c
+++ b/tools/perf/util/sort.c
@@ -288,3 +288,29 @@ int sort_dimension__add(const char *tok)
return -ESRCH;
}
+
+void setup_sorting(const char * const usagestr[], const struct option *opts)
+{
+ char *tmp, *tok, *str = strdup(sort_order);
+
+ for (tok = strtok_r(str, ", ", &tmp);
+ tok; tok = strtok_r(NULL, ", ", &tmp)) {
+ if (sort_dimension__add(tok) < 0) {
+ error("Unknown --sort key: `%s'", tok);
+ usage_with_options(usagestr, opts);
+ }
+ }
+
+ free(str);
+}
+
+void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list,
+ const char *list_name, FILE *fp)
+{
+ if (list && strlist__nr_entries(list) == 1) {
+ if (fp != NULL)
+ fprintf(fp, "# %s: %s\n", list_name,
+ strlist__entry(list, 0)->s);
+ self->elide = true;
+ }
+}
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h
index 333e664ff45..753f9ea99fb 100644
--- a/tools/perf/util/sort.h
+++ b/tools/perf/util/sort.h
@@ -49,9 +49,13 @@ struct hist_entry {
struct symbol *sym;
u64 ip;
char level;
- struct symbol *parent;
+ struct symbol *parent;
struct callchain_node callchain;
- struct rb_root sorted_chain;
+ union {
+ unsigned long position;
+ struct hist_entry *pair;
+ struct rb_root sorted_chain;
+ };
};
enum sort_type {
@@ -81,6 +85,8 @@ struct sort_entry {
extern struct sort_entry sort_thread;
extern struct list_head hist_entry__sort_list;
+void setup_sorting(const char * const usagestr[], const struct option *opts);
+
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);
@@ -95,5 +101,7 @@ 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 *);
+void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list,
+ const char *list_name, FILE *fp);
#endif /* __PERF_SORT_H */
diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c
index f24a8cc933d..5352d7dccc6 100644
--- a/tools/perf/util/string.c
+++ b/tools/perf/util/string.c
@@ -226,3 +226,28 @@ fail:
argv_free(argv);
return NULL;
}
+
+/* Glob expression pattern matching */
+bool strglobmatch(const char *str, const char *pat)
+{
+ while (*str && *pat && *pat != '*') {
+ if (*pat == '?') {
+ str++;
+ pat++;
+ } else
+ if (*str++ != *pat++)
+ return false;
+ }
+ /* Check wild card */
+ if (*pat == '*') {
+ while (*pat == '*')
+ pat++;
+ if (!*pat) /* Tail wild card matches all */
+ return true;
+ while (*str)
+ if (strglobmatch(str++, pat))
+ return true;
+ }
+ return !*str && !*pat;
+}
+
diff --git a/tools/perf/util/string.h b/tools/perf/util/string.h
index bfecec265a1..02ede58c54b 100644
--- a/tools/perf/util/string.h
+++ b/tools/perf/util/string.h
@@ -1,6 +1,7 @@
#ifndef __PERF_STRING_H_
#define __PERF_STRING_H_
+#include <stdbool.h>
#include "types.h"
int hex2u64(const char *ptr, u64 *val);
@@ -8,6 +9,7 @@ char *strxfrchar(char *s, char from, char to);
s64 perf_atoll(const char *str);
char **argv_split(const char *str, int *argcp);
void argv_free(char **argv);
+bool strglobmatch(const char *str, const char *pat);
#define _STR(x) #x
#define STR(x) _STR(x)
diff --git a/tools/perf/util/strlist.c b/tools/perf/util/strlist.c
index 7ad38171dc2..6783a204355 100644
--- a/tools/perf/util/strlist.c
+++ b/tools/perf/util/strlist.c
@@ -102,7 +102,7 @@ void strlist__remove(struct strlist *self, struct str_node *sn)
str_node__delete(sn, self->dupstr);
}
-bool strlist__has_entry(struct strlist *self, const char *entry)
+struct str_node *strlist__find(struct strlist *self, const char *entry)
{
struct rb_node **p = &self->entries.rb_node;
struct rb_node *parent = NULL;
@@ -120,10 +120,10 @@ bool strlist__has_entry(struct strlist *self, const char *entry)
else if (rc < 0)
p = &(*p)->rb_right;
else
- return true;
+ return sn;
}
- return false;
+ return NULL;
}
static int strlist__parse_list_entry(struct strlist *self, const char *s)
diff --git a/tools/perf/util/strlist.h b/tools/perf/util/strlist.h
index cb4659306d7..3ba839007d2 100644
--- a/tools/perf/util/strlist.h
+++ b/tools/perf/util/strlist.h
@@ -23,7 +23,12 @@ int strlist__load(struct strlist *self, const char *filename);
int strlist__add(struct strlist *self, const char *str);
struct str_node *strlist__entry(const struct strlist *self, unsigned int idx);
-bool strlist__has_entry(struct strlist *self, const char *entry);
+struct str_node *strlist__find(struct strlist *self, const char *entry);
+
+static inline bool strlist__has_entry(struct strlist *self, const char *entry)
+{
+ return strlist__find(self, entry) != NULL;
+}
static inline bool strlist__empty(const struct strlist *self)
{
@@ -35,5 +40,39 @@ static inline unsigned int strlist__nr_entries(const struct strlist *self)
return self->nr_entries;
}
+/* For strlist iteration */
+static inline struct str_node *strlist__first(struct strlist *self)
+{
+ struct rb_node *rn = rb_first(&self->entries);
+ return rn ? rb_entry(rn, struct str_node, rb_node) : NULL;
+}
+static inline struct str_node *strlist__next(struct str_node *sn)
+{
+ struct rb_node *rn;
+ if (!sn)
+ return NULL;
+ rn = rb_next(&sn->rb_node);
+ return rn ? rb_entry(rn, struct str_node, rb_node) : NULL;
+}
+
+/**
+ * strlist_for_each - iterate over a strlist
+ * @pos: the &struct str_node to use as a loop cursor.
+ * @self: the &struct strlist for loop.
+ */
+#define strlist__for_each(pos, self) \
+ for (pos = strlist__first(self); pos; pos = strlist__next(pos))
+
+/**
+ * strlist_for_each_safe - iterate over a strlist safe against removal of
+ * str_node
+ * @pos: the &struct str_node to use as a loop cursor.
+ * @n: another &struct str_node to use as temporary storage.
+ * @self: the &struct strlist for loop.
+ */
+#define strlist__for_each_safe(pos, n, self) \
+ for (pos = strlist__first(self), n = strlist__next(pos); pos;\
+ pos = n, n = strlist__next(n))
+
int strlist__parse_list(struct strlist *self, const char *s);
#endif /* __PERF_STRLIST_H */
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index fffcb937cdc..ab92763edb0 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -1,5 +1,7 @@
#include "util.h"
#include "../perf.h"
+#include "session.h"
+#include "sort.h"
#include "string.h"
#include "symbol.h"
#include "thread.h"
@@ -29,33 +31,50 @@ enum dso_origin {
};
static void dsos__add(struct list_head *head, struct dso *dso);
-static struct map *thread__find_map_by_name(struct thread *self, char *name);
static struct map *map__new2(u64 start, struct dso *dso, enum map_type type);
-struct symbol *dso__find_symbol(struct dso *self, enum map_type type, u64 addr);
static int dso__load_kernel_sym(struct dso *self, struct map *map,
- struct thread *thread, symbol_filter_t filter);
-unsigned int symbol__priv_size;
+ struct perf_session *session, symbol_filter_t filter);
static int vmlinux_path__nr_entries;
static char **vmlinux_path;
-static struct symbol_conf symbol_conf__defaults = {
+struct symbol_conf symbol_conf = {
+ .exclude_other = true,
.use_modules = true,
.try_vmlinux_path = true,
};
-static struct thread kthread_mem;
-struct thread *kthread = &kthread_mem;
-
bool dso__loaded(const struct dso *self, enum map_type type)
{
return self->loaded & (1 << type);
}
+bool dso__sorted_by_name(const struct dso *self, enum map_type type)
+{
+ return self->sorted_by_name & (1 << type);
+}
+
static void dso__set_loaded(struct dso *self, enum map_type type)
{
self->loaded |= (1 << type);
}
+static void dso__set_sorted_by_name(struct dso *self, enum map_type type)
+{
+ self->sorted_by_name |= (1 << type);
+}
+
+static bool symbol_type__is_a(char symbol_type, enum map_type map_type)
+{
+ switch (map_type) {
+ case MAP__FUNCTION:
+ return symbol_type == 'T' || symbol_type == 'W';
+ case MAP__VARIABLE:
+ return symbol_type == 'D' || symbol_type == 'd';
+ default:
+ return false;
+ }
+}
+
static void symbols__fixup_end(struct rb_root *self)
{
struct rb_node *nd, *prevnd = rb_first(self);
@@ -79,7 +98,7 @@ static void symbols__fixup_end(struct rb_root *self)
curr->end = roundup(curr->start, 4096);
}
-static void __thread__fixup_maps_end(struct thread *self, enum map_type type)
+static void __map_groups__fixup_end(struct map_groups *self, enum map_type type)
{
struct map *prev, *curr;
struct rb_node *nd, *prevnd = rb_first(&self->maps[type]);
@@ -102,23 +121,23 @@ static void __thread__fixup_maps_end(struct thread *self, enum map_type type)
curr->end = ~0UL;
}
-static void thread__fixup_maps_end(struct thread *self)
+static void map_groups__fixup_end(struct map_groups *self)
{
int i;
for (i = 0; i < MAP__NR_TYPES; ++i)
- __thread__fixup_maps_end(self, i);
+ __map_groups__fixup_end(self, i);
}
static struct symbol *symbol__new(u64 start, u64 len, const char *name)
{
size_t namelen = strlen(name) + 1;
- struct symbol *self = zalloc(symbol__priv_size +
+ struct symbol *self = zalloc(symbol_conf.priv_size +
sizeof(*self) + namelen);
if (self == NULL)
return NULL;
- if (symbol__priv_size)
- self = ((void *)self) + symbol__priv_size;
+ if (symbol_conf.priv_size)
+ self = ((void *)self) + symbol_conf.priv_size;
self->start = start;
self->end = len ? start + len - 1 : start;
@@ -132,7 +151,7 @@ static struct symbol *symbol__new(u64 start, u64 len, const char *name)
static void symbol__delete(struct symbol *self)
{
- free(((void *)self) - symbol__priv_size);
+ free(((void *)self) - symbol_conf.priv_size);
}
static size_t symbol__fprintf(struct symbol *self, FILE *fp)
@@ -164,11 +183,11 @@ struct dso *dso__new(const char *name)
dso__set_long_name(self, self->name);
self->short_name = self->name;
for (i = 0; i < MAP__NR_TYPES; ++i)
- self->symbols[i] = RB_ROOT;
- self->find_symbol = dso__find_symbol;
+ self->symbols[i] = self->symbol_names[i] = RB_ROOT;
self->slen_calculated = 0;
self->origin = DSO__ORIG_NOT_FOUND;
self->loaded = 0;
+ self->sorted_by_name = 0;
self->has_build_id = 0;
}
@@ -246,11 +265,85 @@ static struct symbol *symbols__find(struct rb_root *self, u64 ip)
return NULL;
}
-struct symbol *dso__find_symbol(struct dso *self, enum map_type type, u64 addr)
+struct symbol_name_rb_node {
+ struct rb_node rb_node;
+ struct symbol sym;
+};
+
+static void symbols__insert_by_name(struct rb_root *self, struct symbol *sym)
+{
+ struct rb_node **p = &self->rb_node;
+ struct rb_node *parent = NULL;
+ struct symbol_name_rb_node *symn = ((void *)sym) - sizeof(*parent), *s;
+
+ while (*p != NULL) {
+ parent = *p;
+ s = rb_entry(parent, struct symbol_name_rb_node, rb_node);
+ if (strcmp(sym->name, s->sym.name) < 0)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+ rb_link_node(&symn->rb_node, parent, p);
+ rb_insert_color(&symn->rb_node, self);
+}
+
+static void symbols__sort_by_name(struct rb_root *self, struct rb_root *source)
+{
+ struct rb_node *nd;
+
+ for (nd = rb_first(source); nd; nd = rb_next(nd)) {
+ struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
+ symbols__insert_by_name(self, pos);
+ }
+}
+
+static struct symbol *symbols__find_by_name(struct rb_root *self, const char *name)
+{
+ struct rb_node *n;
+
+ if (self == NULL)
+ return NULL;
+
+ n = self->rb_node;
+
+ while (n) {
+ struct symbol_name_rb_node *s;
+ int cmp;
+
+ s = rb_entry(n, struct symbol_name_rb_node, rb_node);
+ cmp = strcmp(name, s->sym.name);
+
+ if (cmp < 0)
+ n = n->rb_left;
+ else if (cmp > 0)
+ n = n->rb_right;
+ else
+ return &s->sym;
+ }
+
+ return NULL;
+}
+
+struct symbol *dso__find_symbol(struct dso *self,
+ enum map_type type, u64 addr)
{
return symbols__find(&self->symbols[type], addr);
}
+struct symbol *dso__find_symbol_by_name(struct dso *self, enum map_type type,
+ const char *name)
+{
+ return symbols__find_by_name(&self->symbol_names[type], name);
+}
+
+void dso__sort_by_name(struct dso *self, enum map_type type)
+{
+ dso__set_sorted_by_name(self, type);
+ return symbols__sort_by_name(&self->symbol_names[type],
+ &self->symbols[type]);
+}
+
int build_id__sprintf(u8 *self, int len, char *bf)
{
char *bid = bf;
@@ -327,10 +420,7 @@ static int dso__load_all_kallsyms(struct dso *self, struct map *map)
continue;
symbol_type = toupper(line[len]);
- /*
- * We're interested only in code ('T'ext)
- */
- if (symbol_type != 'T' && symbol_type != 'W')
+ if (!symbol_type__is_a(symbol_type, map->type))
continue;
symbol_name = line + len + 2;
@@ -364,8 +454,8 @@ out_failure:
* kernel range is broken in several maps, named [kernel].N, as we don't have
* the original ELF section names vmlinux have.
*/
-static int dso__split_kallsyms(struct dso *self, struct map *map, struct thread *thread,
- symbol_filter_t filter)
+static int dso__split_kallsyms(struct dso *self, struct map *map,
+ struct perf_session *session, symbol_filter_t filter)
{
struct map *curr_map = map;
struct symbol *pos;
@@ -382,13 +472,13 @@ static int dso__split_kallsyms(struct dso *self, struct map *map, struct thread
module = strchr(pos->name, '\t');
if (module) {
- if (!thread->use_modules)
+ if (!symbol_conf.use_modules)
goto discard_symbol;
*module++ = '\0';
if (strcmp(self->name, module)) {
- curr_map = thread__find_map_by_name(thread, module);
+ curr_map = map_groups__find_by_name(&session->kmaps, map->type, module);
if (curr_map == NULL) {
pr_debug("/proc/{kallsyms,modules} "
"inconsistency!\n");
@@ -419,7 +509,7 @@ static int dso__split_kallsyms(struct dso *self, struct map *map, struct thread
}
curr_map->map_ip = curr_map->unmap_ip = identity__map_ip;
- __thread__insert_map(thread, curr_map);
+ map_groups__insert(&session->kmaps, curr_map);
++kernel_range;
}
@@ -440,7 +530,7 @@ discard_symbol: rb_erase(&pos->rb_node, root);
static int dso__load_kallsyms(struct dso *self, struct map *map,
- struct thread *thread, symbol_filter_t filter)
+ struct perf_session *session, symbol_filter_t filter)
{
if (dso__load_all_kallsyms(self, map) < 0)
return -1;
@@ -448,14 +538,7 @@ static int dso__load_kallsyms(struct dso *self, struct map *map,
symbols__fixup_end(&self->symbols[map->type]);
self->origin = DSO__ORIG_KERNEL;
- return dso__split_kallsyms(self, map, thread, filter);
-}
-
-size_t kernel_maps__fprintf(FILE *fp)
-{
- size_t printed = fprintf(fp, "Kernel maps:\n");
- printed += thread__fprintf_maps(kthread, fp);
- return printed + fprintf(fp, "END kernel maps\n");
+ return dso__split_kallsyms(self, map, session, filter);
}
static int dso__load_perf_map(struct dso *self, struct map *map,
@@ -544,6 +627,13 @@ static inline int elf_sym__is_function(const GElf_Sym *sym)
sym->st_shndx != SHN_UNDEF;
}
+static inline bool elf_sym__is_object(const GElf_Sym *sym)
+{
+ return elf_sym__type(sym) == STT_OBJECT &&
+ sym->st_name != 0 &&
+ sym->st_shndx != SHN_UNDEF;
+}
+
static inline int elf_sym__is_label(const GElf_Sym *sym)
{
return elf_sym__type(sym) == STT_NOTYPE &&
@@ -564,6 +654,12 @@ static inline int elf_sec__is_text(const GElf_Shdr *shdr,
return strstr(elf_sec__name(shdr, secstrs), "text") != NULL;
}
+static inline bool elf_sec__is_data(const GElf_Shdr *shdr,
+ const Elf_Data *secstrs)
+{
+ return strstr(elf_sec__name(shdr, secstrs), "data") != NULL;
+}
+
static inline const char *elf_sym__name(const GElf_Sym *sym,
const Elf_Data *symstrs)
{
@@ -744,8 +840,32 @@ out:
return 0;
}
+static bool elf_sym__is_a(GElf_Sym *self, enum map_type type)
+{
+ switch (type) {
+ case MAP__FUNCTION:
+ return elf_sym__is_function(self);
+ case MAP__VARIABLE:
+ return elf_sym__is_object(self);
+ default:
+ return false;
+ }
+}
+
+static bool elf_sec__is_a(GElf_Shdr *self, Elf_Data *secstrs, enum map_type type)
+{
+ switch (type) {
+ case MAP__FUNCTION:
+ return elf_sec__is_text(self, secstrs);
+ case MAP__VARIABLE:
+ return elf_sec__is_data(self, secstrs);
+ default:
+ return false;
+ }
+}
+
static int dso__load_sym(struct dso *self, struct map *map,
- struct thread *thread, const char *name, int fd,
+ struct perf_session *session, const char *name, int fd,
symbol_filter_t filter, int kernel, int kmodule)
{
struct map *curr_map = map;
@@ -818,7 +938,7 @@ static int dso__load_sym(struct dso *self, struct map *map,
int is_label = elf_sym__is_label(&sym);
const char *section_name;
- if (!is_label && !elf_sym__is_function(&sym))
+ if (!is_label && !elf_sym__is_a(&sym, map->type))
continue;
sec = elf_getscn(elf, sym.st_shndx);
@@ -827,7 +947,7 @@ static int dso__load_sym(struct dso *self, struct map *map,
gelf_getshdr(sec, &shdr);
- if (is_label && !elf_sec__is_text(&shdr, secstrs))
+ if (is_label && !elf_sec__is_a(&shdr, secstrs, map->type))
continue;
elf_name = elf_sym__name(&sym, symstrs);
@@ -849,7 +969,7 @@ static int dso__load_sym(struct dso *self, struct map *map,
snprintf(dso_name, sizeof(dso_name),
"%s%s", self->short_name, section_name);
- curr_map = thread__find_map_by_name(thread, dso_name);
+ curr_map = map_groups__find_by_name(&session->kmaps, map->type, dso_name);
if (curr_map == NULL) {
u64 start = sym.st_value;
@@ -868,7 +988,7 @@ static int dso__load_sym(struct dso *self, struct map *map,
curr_map->map_ip = identity__map_ip;
curr_map->unmap_ip = identity__map_ip;
curr_dso->origin = DSO__ORIG_KERNEL;
- __thread__insert_map(kthread, curr_map);
+ map_groups__insert(&session->kmaps, curr_map);
dsos__add(&dsos__kernel, curr_dso);
} else
curr_dso = curr_map->dso;
@@ -938,8 +1058,9 @@ static bool __dsos__read_build_ids(struct list_head *head)
bool dsos__read_build_ids(void)
{
- return __dsos__read_build_ids(&dsos__kernel) ||
- __dsos__read_build_ids(&dsos__user);
+ bool kbuildids = __dsos__read_build_ids(&dsos__kernel),
+ ubuildids = __dsos__read_build_ids(&dsos__user);
+ return kbuildids || ubuildids;
}
/*
@@ -1082,7 +1203,8 @@ char dso__symtab_origin(const struct dso *self)
return origin[self->origin];
}
-int dso__load(struct dso *self, struct map *map, symbol_filter_t filter)
+int dso__load(struct dso *self, struct map *map, struct perf_session *session,
+ symbol_filter_t filter)
{
int size = PATH_MAX;
char *name;
@@ -1093,7 +1215,7 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter)
dso__set_loaded(self, map->type);
if (self->kernel)
- return dso__load_kernel_sym(self, map, kthread, filter);
+ return dso__load_kernel_sym(self, map, session, filter);
name = malloc(size);
if (!name)
@@ -1179,11 +1301,12 @@ out:
return ret;
}
-static struct map *thread__find_map_by_name(struct thread *self, char *name)
+struct map *map_groups__find_by_name(struct map_groups *self,
+ enum map_type type, const char *name)
{
struct rb_node *nd;
- for (nd = rb_first(&self->maps[MAP__FUNCTION]); nd; nd = rb_next(nd)) {
+ for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) {
struct map *map = rb_entry(nd, struct map, rb_node);
if (map->dso && strcmp(map->dso->name, name) == 0)
@@ -1193,7 +1316,7 @@ static struct map *thread__find_map_by_name(struct thread *self, char *name)
return NULL;
}
-static int dsos__set_modules_path_dir(char *dirname)
+static int perf_session__set_modules_path_dir(struct perf_session *self, char *dirname)
{
struct dirent *dent;
DIR *dir = opendir(dirname);
@@ -1213,7 +1336,7 @@ static int dsos__set_modules_path_dir(char *dirname)
snprintf(path, sizeof(path), "%s/%s",
dirname, dent->d_name);
- if (dsos__set_modules_path_dir(path) < 0)
+ if (perf_session__set_modules_path_dir(self, path) < 0)
goto failure;
} else {
char *dot = strrchr(dent->d_name, '.'),
@@ -1227,7 +1350,7 @@ static int dsos__set_modules_path_dir(char *dirname)
(int)(dot - dent->d_name), dent->d_name);
strxfrchar(dso_name, '-', '_');
- map = thread__find_map_by_name(kthread, dso_name);
+ map = map_groups__find_by_name(&self->kmaps, MAP__FUNCTION, dso_name);
if (map == NULL)
continue;
@@ -1247,7 +1370,7 @@ failure:
return -1;
}
-static int dsos__set_modules_path(void)
+static int perf_session__set_modules_path(struct perf_session *self)
{
struct utsname uts;
char modules_path[PATH_MAX];
@@ -1258,7 +1381,7 @@ static int dsos__set_modules_path(void)
snprintf(modules_path, sizeof(modules_path), "/lib/modules/%s/kernel",
uts.release);
- return dsos__set_modules_path_dir(modules_path);
+ return perf_session__set_modules_path_dir(self, modules_path);
}
/*
@@ -1280,7 +1403,7 @@ static struct map *map__new2(u64 start, struct dso *dso, enum map_type type)
return self;
}
-static int thread__create_module_maps(struct thread *self)
+static int perf_session__create_module_maps(struct perf_session *self)
{
char *line = NULL;
size_t n;
@@ -1337,14 +1460,14 @@ static int thread__create_module_maps(struct thread *self)
dso->has_build_id = true;
dso->origin = DSO__ORIG_KMODULE;
- __thread__insert_map(self, map);
+ map_groups__insert(&self->kmaps, map);
dsos__add(&dsos__kernel, dso);
}
free(line);
fclose(file);
- return dsos__set_modules_path();
+ return perf_session__set_modules_path(self);
out_delete_line:
free(line);
@@ -1352,7 +1475,8 @@ out_failure:
return -1;
}
-static int dso__load_vmlinux(struct dso *self, struct map *map, struct thread *thread,
+static int dso__load_vmlinux(struct dso *self, struct map *map,
+ struct perf_session *session,
const char *vmlinux, symbol_filter_t filter)
{
int err = -1, fd;
@@ -1386,14 +1510,14 @@ static int dso__load_vmlinux(struct dso *self, struct map *map, struct thread *t
return -1;
dso__set_loaded(self, map->type);
- err = dso__load_sym(self, map, thread, self->long_name, fd, filter, 1, 0);
+ err = dso__load_sym(self, map, session, self->long_name, fd, filter, 1, 0);
close(fd);
return err;
}
static int dso__load_kernel_sym(struct dso *self, struct map *map,
- struct thread *thread, symbol_filter_t filter)
+ struct perf_session *session, symbol_filter_t filter)
{
int err;
bool is_kallsyms;
@@ -1403,7 +1527,7 @@ static int dso__load_kernel_sym(struct dso *self, struct map *map,
pr_debug("Looking at the vmlinux_path (%d entries long)\n",
vmlinux_path__nr_entries);
for (i = 0; i < vmlinux_path__nr_entries; ++i) {
- err = dso__load_vmlinux(self, map, thread,
+ err = dso__load_vmlinux(self, map, session,
vmlinux_path[i], filter);
if (err > 0) {
pr_debug("Using %s for symbols\n",
@@ -1419,12 +1543,12 @@ static int dso__load_kernel_sym(struct dso *self, struct map *map,
if (is_kallsyms)
goto do_kallsyms;
- err = dso__load_vmlinux(self, map, thread, self->long_name, filter);
+ err = dso__load_vmlinux(self, map, session, self->long_name, filter);
if (err <= 0) {
pr_info("The file %s cannot be used, "
"trying to use /proc/kallsyms...", self->long_name);
do_kallsyms:
- err = dso__load_kallsyms(self, map, thread, filter);
+ err = dso__load_kallsyms(self, map, session, filter);
if (err > 0 && !is_kallsyms)
dso__set_long_name(self, strdup("[kernel.kallsyms]"));
}
@@ -1507,42 +1631,59 @@ size_t dsos__fprintf_buildid(FILE *fp)
__dsos__fprintf_buildid(&dsos__user, fp));
}
-static int thread__create_kernel_map(struct thread *self, const char *vmlinux)
+static struct dso *dsos__create_kernel( const char *vmlinux)
{
- struct map *kmap;
struct dso *kernel = dso__new(vmlinux ?: "[kernel.kallsyms]");
if (kernel == NULL)
- return -1;
-
- kmap = map__new2(0, kernel, MAP__FUNCTION);
- if (kmap == NULL)
- goto out_delete_kernel_dso;
+ return NULL;
- kmap->map_ip = kmap->unmap_ip = identity__map_ip;
kernel->short_name = "[kernel]";
kernel->kernel = 1;
vdso = dso__new("[vdso]");
if (vdso == NULL)
- goto out_delete_kernel_map;
+ goto out_delete_kernel_dso;
dso__set_loaded(vdso, MAP__FUNCTION);
if (sysfs__read_build_id("/sys/kernel/notes", kernel->build_id,
sizeof(kernel->build_id)) == 0)
kernel->has_build_id = true;
- __thread__insert_map(self, kmap);
dsos__add(&dsos__kernel, kernel);
dsos__add(&dsos__user, vdso);
- return 0;
+ return kernel;
-out_delete_kernel_map:
- map__delete(kmap);
out_delete_kernel_dso:
dso__delete(kernel);
- return -1;
+ return NULL;
+}
+
+static int map_groups__create_kernel_maps(struct map_groups *self, const char *vmlinux)
+{
+ struct map *functions, *variables;
+ struct dso *kernel = dsos__create_kernel(vmlinux);
+
+ if (kernel == NULL)
+ return -1;
+
+ functions = map__new2(0, kernel, MAP__FUNCTION);
+ if (functions == NULL)
+ return -1;
+
+ variables = map__new2(0, kernel, MAP__VARIABLE);
+ if (variables == NULL) {
+ map__delete(functions);
+ return -1;
+ }
+
+ functions->map_ip = functions->unmap_ip =
+ variables->map_ip = variables->unmap_ip = identity__map_ip;
+ map_groups__insert(self, functions);
+ map_groups__insert(self, variables);
+
+ return 0;
}
static void vmlinux_path__exit(void)
@@ -1600,29 +1741,69 @@ out_fail:
return -1;
}
-int symbol__init(struct symbol_conf *conf)
+static int setup_list(struct strlist **list, const char *list_str,
+ const char *list_name)
{
- const struct symbol_conf *pconf = conf ?: &symbol_conf__defaults;
+ if (list_str == NULL)
+ return 0;
+ *list = strlist__new(true, list_str);
+ if (!*list) {
+ pr_err("problems parsing %s list\n", list_name);
+ return -1;
+ }
+ return 0;
+}
+
+int symbol__init(void)
+{
elf_version(EV_CURRENT);
- symbol__priv_size = pconf->priv_size;
- thread__init(kthread, 0);
+ if (symbol_conf.sort_by_name)
+ symbol_conf.priv_size += (sizeof(struct symbol_name_rb_node) -
+ sizeof(struct symbol));
- if (pconf->try_vmlinux_path && vmlinux_path__init() < 0)
+ if (symbol_conf.try_vmlinux_path && vmlinux_path__init() < 0)
return -1;
- if (thread__create_kernel_map(kthread, pconf->vmlinux_name) < 0) {
- vmlinux_path__exit();
+ if (symbol_conf.field_sep && *symbol_conf.field_sep == '.') {
+ pr_err("'.' is the only non valid --field-separator argument\n");
return -1;
}
- kthread->use_modules = pconf->use_modules;
- if (pconf->use_modules && thread__create_module_maps(kthread) < 0)
- pr_debug("Failed to load list of modules in use, "
- "continuing...\n");
+ if (setup_list(&symbol_conf.dso_list,
+ symbol_conf.dso_list_str, "dso") < 0)
+ return -1;
+
+ if (setup_list(&symbol_conf.comm_list,
+ symbol_conf.comm_list_str, "comm") < 0)
+ goto out_free_dso_list;
+
+ if (setup_list(&symbol_conf.sym_list,
+ symbol_conf.sym_list_str, "symbol") < 0)
+ goto out_free_comm_list;
+
+ return 0;
+
+out_free_dso_list:
+ strlist__delete(symbol_conf.dso_list);
+out_free_comm_list:
+ strlist__delete(symbol_conf.comm_list);
+ return -1;
+}
+
+int perf_session__create_kernel_maps(struct perf_session *self)
+{
+ if (map_groups__create_kernel_maps(&self->kmaps,
+ symbol_conf.vmlinux_name) < 0)
+ return -1;
+
+ if (symbol_conf.use_modules &&
+ perf_session__create_module_maps(self) < 0)
+ pr_debug("Failed to load list of modules for session %s, "
+ "continuing...\n", self->filename);
/*
* Now that we have all the maps created, just set the ->end of them:
*/
- thread__fixup_maps_end(kthread);
+ map_groups__fixup_end(&self->kmaps);
return 0;
}
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 17003efa0b3..8aded2356f7 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -49,18 +49,32 @@ struct symbol {
char name[0];
};
+struct strlist;
+
struct symbol_conf {
unsigned short priv_size;
bool try_vmlinux_path,
- use_modules;
- const char *vmlinux_name;
+ use_modules,
+ sort_by_name,
+ show_nr_samples,
+ use_callchain,
+ exclude_other;
+ const char *vmlinux_name,
+ *field_sep;
+ char *dso_list_str,
+ *comm_list_str,
+ *sym_list_str,
+ *col_width_list_str;
+ struct strlist *dso_list,
+ *comm_list,
+ *sym_list;
};
-extern unsigned int symbol__priv_size;
+extern struct symbol_conf symbol_conf;
static inline void *symbol__priv(struct symbol *self)
{
- return ((void *)self) - symbol__priv_size;
+ return ((void *)self) - symbol_conf.priv_size;
}
struct addr_location {
@@ -69,18 +83,19 @@ struct addr_location {
struct symbol *sym;
u64 addr;
char level;
+ bool filtered;
};
struct dso {
struct list_head node;
struct rb_root symbols[MAP__NR_TYPES];
- struct symbol *(*find_symbol)(struct dso *self,
- enum map_type type, u64 addr);
+ struct rb_root symbol_names[MAP__NR_TYPES];
u8 adjust_symbols:1;
u8 slen_calculated:1;
u8 has_build_id:1;
u8 kernel:1;
unsigned char origin;
+ u8 sorted_by_name;
u8 loaded;
u8 build_id[BUILD_ID_SIZE];
u16 long_name_len;
@@ -93,9 +108,15 @@ struct dso *dso__new(const char *name);
void dso__delete(struct dso *self);
bool dso__loaded(const struct dso *self, enum map_type type);
+bool dso__sorted_by_name(const struct dso *self, enum map_type type);
+
+void dso__sort_by_name(struct dso *self, enum map_type type);
+
+struct perf_session;
struct dso *dsos__findnew(const char *name);
-int dso__load(struct dso *self, struct map *map, symbol_filter_t filter);
+int dso__load(struct dso *self, struct map *map, struct perf_session *session,
+ symbol_filter_t filter);
void dsos__fprintf(FILE *fp);
size_t dsos__fprintf_buildid(FILE *fp);
@@ -103,18 +124,18 @@ size_t dso__fprintf_buildid(struct dso *self, FILE *fp);
size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp);
char dso__symtab_origin(const struct dso *self);
void dso__set_build_id(struct dso *self, void *build_id);
+struct symbol *dso__find_symbol(struct dso *self, enum map_type type, u64 addr);
+struct symbol *dso__find_symbol_by_name(struct dso *self, enum map_type type,
+ const char *name);
int filename__read_build_id(const char *filename, void *bf, size_t size);
int sysfs__read_build_id(const char *filename, void *bf, size_t size);
bool dsos__read_build_ids(void);
int build_id__sprintf(u8 *self, int len, char *bf);
-size_t kernel_maps__fprintf(FILE *fp);
-
-int symbol__init(struct symbol_conf *conf);
+int symbol__init(void);
+int perf_session__create_kernel_maps(struct perf_session *self);
-struct thread;
-struct thread *kthread;
extern struct list_head dsos__user, dsos__kernel;
extern struct dso *vdso;
#endif /* __PERF_SYMBOL */
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
index 603f5610861..4a08dcf50b6 100644
--- a/tools/perf/util/thread.c
+++ b/tools/perf/util/thread.c
@@ -2,18 +2,14 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
+#include "session.h"
#include "thread.h"
#include "util.h"
#include "debug.h"
-static struct rb_root threads;
-static struct thread *last_match;
-
-void thread__init(struct thread *self, pid_t pid)
+void map_groups__init(struct map_groups *self)
{
int i;
- self->pid = pid;
- self->comm = NULL;
for (i = 0; i < MAP__NR_TYPES; ++i) {
self->maps[i] = RB_ROOT;
INIT_LIST_HEAD(&self->removed_maps[i]);
@@ -25,7 +21,8 @@ static struct thread *thread__new(pid_t pid)
struct thread *self = zalloc(sizeof(*self));
if (self != NULL) {
- thread__init(self, pid);
+ map_groups__init(&self->mg);
+ self->pid = pid;
self->comm = malloc(32);
if (self->comm)
snprintf(self->comm, 32, ":%d", self->pid);
@@ -55,10 +52,11 @@ int thread__comm_len(struct thread *self)
static const char *map_type__name[MAP__NR_TYPES] = {
[MAP__FUNCTION] = "Functions",
+ [MAP__VARIABLE] = "Variables",
};
-static size_t __thread__fprintf_maps(struct thread *self,
- enum map_type type, FILE *fp)
+static size_t __map_groups__fprintf_maps(struct map_groups *self,
+ enum map_type type, FILE *fp)
{
size_t printed = fprintf(fp, "%s:\n", map_type__name[type]);
struct rb_node *nd;
@@ -76,16 +74,16 @@ static size_t __thread__fprintf_maps(struct thread *self,
return printed;
}
-size_t thread__fprintf_maps(struct thread *self, FILE *fp)
+size_t map_groups__fprintf_maps(struct map_groups *self, FILE *fp)
{
size_t printed = 0, i;
for (i = 0; i < MAP__NR_TYPES; ++i)
- printed += __thread__fprintf_maps(self, i, fp);
+ printed += __map_groups__fprintf_maps(self, i, fp);
return printed;
}
-static size_t __thread__fprintf_removed_maps(struct thread *self,
- enum map_type type, FILE *fp)
+static size_t __map_groups__fprintf_removed_maps(struct map_groups *self,
+ enum map_type type, FILE *fp)
{
struct map *pos;
size_t printed = 0;
@@ -101,25 +99,30 @@ static size_t __thread__fprintf_removed_maps(struct thread *self,
return printed;
}
-static size_t thread__fprintf_removed_maps(struct thread *self, FILE *fp)
+static size_t map_groups__fprintf_removed_maps(struct map_groups *self, FILE *fp)
{
size_t printed = 0, i;
for (i = 0; i < MAP__NR_TYPES; ++i)
- printed += __thread__fprintf_removed_maps(self, i, fp);
+ printed += __map_groups__fprintf_removed_maps(self, i, fp);
return printed;
}
-static size_t thread__fprintf(struct thread *self, FILE *fp)
+static size_t map_groups__fprintf(struct map_groups *self, FILE *fp)
{
- size_t printed = fprintf(fp, "Thread %d %s\n", self->pid, self->comm);
- printed += thread__fprintf_removed_maps(self, fp);
+ size_t printed = map_groups__fprintf_maps(self, fp);
printed += fprintf(fp, "Removed maps:\n");
- return printed + thread__fprintf_removed_maps(self, fp);
+ return printed + map_groups__fprintf_removed_maps(self, fp);
+}
+
+static size_t thread__fprintf(struct thread *self, FILE *fp)
+{
+ return fprintf(fp, "Thread %d %s\n", self->pid, self->comm) +
+ map_groups__fprintf(&self->mg, fp);
}
-struct thread *threads__findnew(pid_t pid)
+struct thread *perf_session__findnew(struct perf_session *self, pid_t pid)
{
- struct rb_node **p = &threads.rb_node;
+ struct rb_node **p = &self->threads.rb_node;
struct rb_node *parent = NULL;
struct thread *th;
@@ -128,15 +131,15 @@ struct thread *threads__findnew(pid_t pid)
* 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 (self->last_match && self->last_match->pid == pid)
+ return self->last_match;
while (*p != NULL) {
parent = *p;
th = rb_entry(parent, struct thread, rb_node);
if (th->pid == pid) {
- last_match = th;
+ self->last_match = th;
return th;
}
@@ -149,26 +152,15 @@ struct thread *threads__findnew(pid_t pid)
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, &self->threads);
+ self->last_match = th;
}
return th;
}
-struct thread *register_idle_thread(void)
-{
- struct thread *thread = threads__findnew(0);
-
- if (!thread || thread__set_comm(thread, "swapper")) {
- fprintf(stderr, "problem inserting idle task.\n");
- exit(-1);
- }
-
- return thread;
-}
-
-static void thread__remove_overlappings(struct thread *self, struct map *map)
+static void map_groups__remove_overlappings(struct map_groups *self,
+ struct map *map)
{
struct rb_root *root = &self->maps[map->type];
struct rb_node *next = rb_first(root);
@@ -238,12 +230,15 @@ struct map *maps__find(struct rb_root *maps, u64 ip)
void thread__insert_map(struct thread *self, struct map *map)
{
- thread__remove_overlappings(self, map);
- maps__insert(&self->maps[map->type], map);
+ map_groups__remove_overlappings(&self->mg, map);
+ map_groups__insert(&self->mg, map);
}
-static int thread__clone_maps(struct thread *self, struct thread *parent,
- enum map_type type)
+/*
+ * XXX This should not really _copy_ te maps, but refcount them.
+ */
+static int map_groups__clone(struct map_groups *self,
+ struct map_groups *parent, enum map_type type)
{
struct rb_node *nd;
for (nd = rb_first(&parent->maps[type]); nd; nd = rb_next(nd)) {
@@ -251,7 +246,7 @@ static int thread__clone_maps(struct thread *self, struct thread *parent,
struct map *new = map__clone(map);
if (new == NULL)
return -ENOMEM;
- thread__insert_map(self, new);
+ map_groups__insert(self, new);
}
return 0;
}
@@ -267,17 +262,17 @@ int thread__fork(struct thread *self, struct thread *parent)
return -ENOMEM;
for (i = 0; i < MAP__NR_TYPES; ++i)
- if (thread__clone_maps(self, parent, i) < 0)
+ if (map_groups__clone(&self->mg, &parent->mg, i) < 0)
return -ENOMEM;
return 0;
}
-size_t threads__fprintf(FILE *fp)
+size_t perf_session__fprintf(struct perf_session *self, FILE *fp)
{
size_t ret = 0;
struct rb_node *nd;
- for (nd = rb_first(&threads); nd; nd = rb_next(nd)) {
+ for (nd = rb_first(&self->threads); nd; nd = rb_next(nd)) {
struct thread *pos = rb_entry(nd, struct thread, rb_node);
ret += thread__fprintf(pos, fp);
@@ -286,14 +281,15 @@ size_t threads__fprintf(FILE *fp)
return ret;
}
-struct symbol *thread__find_symbol(struct thread *self,
- enum map_type type, u64 addr,
- symbol_filter_t filter)
+struct symbol *map_groups__find_symbol(struct map_groups *self,
+ struct perf_session *session,
+ enum map_type type, u64 addr,
+ symbol_filter_t filter)
{
- struct map *map = thread__find_map(self, type, addr);
+ struct map *map = map_groups__find(self, type, addr);
if (map != NULL)
- return map__find_symbol(map, map->map_ip(map, addr), filter);
+ return map__find_symbol(map, session, map->map_ip(map, addr), filter);
return NULL;
}
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
index 686d6e914d9..c206f72c888 100644
--- a/tools/perf/util/thread.h
+++ b/tools/perf/util/thread.h
@@ -5,52 +5,66 @@
#include <unistd.h>
#include "symbol.h"
-struct thread {
- struct rb_node rb_node;
+struct map_groups {
struct rb_root maps[MAP__NR_TYPES];
struct list_head removed_maps[MAP__NR_TYPES];
+};
+
+struct thread {
+ struct rb_node rb_node;
+ struct map_groups mg;
pid_t pid;
- bool use_modules;
char shortname[3];
char *comm;
int comm_len;
};
-void thread__init(struct thread *self, pid_t pid);
+void map_groups__init(struct map_groups *self);
int thread__set_comm(struct thread *self, const char *comm);
int thread__comm_len(struct thread *self);
-struct thread *threads__findnew(pid_t pid);
-struct thread *register_idle_thread(void);
+struct thread *perf_session__findnew(struct perf_session *self, pid_t pid);
void thread__insert_map(struct thread *self, struct map *map);
int thread__fork(struct thread *self, struct thread *parent);
-size_t thread__fprintf_maps(struct thread *self, FILE *fp);
-size_t threads__fprintf(FILE *fp);
+size_t map_groups__fprintf_maps(struct map_groups *self, FILE *fp);
+size_t perf_session__fprintf(struct perf_session *self, FILE *fp);
void maps__insert(struct rb_root *maps, struct map *map);
struct map *maps__find(struct rb_root *maps, u64 addr);
-static inline struct map *thread__find_map(struct thread *self,
+static inline void map_groups__insert(struct map_groups *self, struct map *map)
+{
+ maps__insert(&self->maps[map->type], map);
+}
+
+static inline struct map *map_groups__find(struct map_groups *self,
enum map_type type, u64 addr)
{
- return self ? maps__find(&self->maps[type], addr) : NULL;
+ return maps__find(&self->maps[type], addr);
}
-static inline void __thread__insert_map(struct thread *self, struct map *map)
+static inline struct map *thread__find_map(struct thread *self,
+ enum map_type type, u64 addr)
{
- maps__insert(&self->maps[map->type], map);
+ return self ? map_groups__find(&self->mg, type, addr) : NULL;
}
-void thread__find_addr_location(struct thread *self, u8 cpumode,
+void thread__find_addr_location(struct thread *self,
+ struct perf_session *session, u8 cpumode,
enum map_type type, u64 addr,
struct addr_location *al,
symbol_filter_t filter);
-struct symbol *thread__find_symbol(struct thread *self,
- enum map_type type, u64 addr,
- symbol_filter_t filter);
+struct symbol *map_groups__find_symbol(struct map_groups *self,
+ struct perf_session *session,
+ enum map_type type, u64 addr,
+ symbol_filter_t filter);
static inline struct symbol *
-thread__find_function(struct thread *self, u64 addr, symbol_filter_t filter)
+map_groups__find_function(struct map_groups *self, struct perf_session *session,
+ u64 addr, symbol_filter_t filter)
{
- return thread__find_symbol(self, MAP__FUNCTION, addr, filter);
+ return map_groups__find_symbol(self, session, MAP__FUNCTION, addr, filter);
}
+
+struct map *map_groups__find_by_name(struct map_groups *self,
+ enum map_type type, const char *name);
#endif /* __PERF_THREAD_H */
diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c
index 0302405aa2c..c5c32be040b 100644
--- a/tools/perf/util/trace-event-parse.c
+++ b/tools/perf/util/trace-event-parse.c
@@ -177,7 +177,7 @@ void parse_proc_kallsyms(char *file, unsigned int size __unused)
func_count++;
}
- func_list = malloc_or_die(sizeof(*func_list) * func_count + 1);
+ func_list = malloc_or_die(sizeof(*func_list) * (func_count + 1));
i = 0;
while (list) {
@@ -1477,7 +1477,7 @@ process_fields(struct event *event, struct print_flag_sym **list, char **tok)
goto out_free;
field = malloc_or_die(sizeof(*field));
- memset(field, 0, sizeof(field));
+ memset(field, 0, sizeof(*field));
value = arg_eval(arg);
field->value = strdup(value);
diff --git a/tools/perf/util/trace-event-perl.c b/tools/perf/util/trace-event-perl.c
index 51e833fd58c..6d6d76b8a21 100644
--- a/tools/perf/util/trace-event-perl.c
+++ b/tools/perf/util/trace-event-perl.c
@@ -32,9 +32,6 @@
void xs_init(pTHX);
-void boot_Perf__Trace__Context(pTHX_ CV *cv);
-void boot_DynaLoader(pTHX_ CV *cv);
-
void xs_init(pTHX)
{
const char *file = __FILE__;
@@ -270,7 +267,7 @@ int common_lock_depth(struct scripting_context *context)
}
static void perl_process_event(int cpu, void *data,
- int size __attribute((unused)),
+ int size __unused,
unsigned long long nsecs, char *comm)
{
struct format_field *field;
@@ -362,28 +359,46 @@ static void run_start_sub(void)
/*
* Start trace script
*/
-static int perl_start_script(const char *script)
+static int perl_start_script(const char *script, int argc, const char **argv)
{
- const char *command_line[2] = { "", NULL };
+ const char **command_line;
+ int i, err = 0;
+ command_line = malloc((argc + 2) * sizeof(const char *));
+ command_line[0] = "";
command_line[1] = script;
+ for (i = 2; i < argc + 2; i++)
+ command_line[i] = argv[i - 2];
my_perl = perl_alloc();
perl_construct(my_perl);
- if (perl_parse(my_perl, xs_init, 2, (char **)command_line,
- (char **)NULL))
- return -1;
+ if (perl_parse(my_perl, xs_init, argc + 2, (char **)command_line,
+ (char **)NULL)) {
+ err = -1;
+ goto error;
+ }
- perl_run(my_perl);
- if (SvTRUE(ERRSV))
- return -1;
+ if (perl_run(my_perl)) {
+ err = -1;
+ goto error;
+ }
+
+ if (SvTRUE(ERRSV)) {
+ err = -1;
+ goto error;
+ }
run_start_sub();
+ free(command_line);
fprintf(stderr, "perf trace started with Perl script %s\n\n", script);
-
return 0;
+error:
+ perl_free(my_perl);
+ free(command_line);
+
+ return err;
}
/*
@@ -573,26 +588,74 @@ struct scripting_ops perl_scripting_ops = {
.generate_script = perl_generate_script,
};
-#ifdef NO_LIBPERL
-void setup_perl_scripting(void)
+static void print_unsupported_msg(void)
{
fprintf(stderr, "Perl scripting not supported."
- " Install libperl and rebuild perf to enable it. e.g. "
- "apt-get install libperl-dev (ubuntu), yum install "
- "perl-ExtUtils-Embed (Fedora), etc.\n");
+ " Install libperl and rebuild perf to enable it.\n"
+ "For example:\n # apt-get install libperl-dev (ubuntu)"
+ "\n # yum install perl-ExtUtils-Embed (Fedora)"
+ "\n etc.\n");
}
-#else
-void setup_perl_scripting(void)
+
+static int perl_start_script_unsupported(const char *script __unused,
+ int argc __unused,
+ const char **argv __unused)
+{
+ print_unsupported_msg();
+
+ return -1;
+}
+
+static int perl_stop_script_unsupported(void)
+{
+ return 0;
+}
+
+static void perl_process_event_unsupported(int cpu __unused,
+ void *data __unused,
+ int size __unused,
+ unsigned long long nsecs __unused,
+ char *comm __unused)
+{
+}
+
+static int perl_generate_script_unsupported(const char *outfile __unused)
+{
+ print_unsupported_msg();
+
+ return -1;
+}
+
+struct scripting_ops perl_scripting_unsupported_ops = {
+ .name = "Perl",
+ .start_script = perl_start_script_unsupported,
+ .stop_script = perl_stop_script_unsupported,
+ .process_event = perl_process_event_unsupported,
+ .generate_script = perl_generate_script_unsupported,
+};
+
+static void register_perl_scripting(struct scripting_ops *scripting_ops)
{
int err;
- err = script_spec_register("Perl", &perl_scripting_ops);
+ err = script_spec_register("Perl", scripting_ops);
if (err)
die("error registering Perl script extension");
- err = script_spec_register("pl", &perl_scripting_ops);
+ err = script_spec_register("pl", scripting_ops);
if (err)
die("error registering pl script extension");
scripting_context = malloc(sizeof(struct scripting_context));
}
+
+#ifdef NO_LIBPERL
+void setup_perl_scripting(void)
+{
+ register_perl_scripting(&perl_scripting_unsupported_ops);
+}
+#else
+void setup_perl_scripting(void)
+{
+ register_perl_scripting(&perl_scripting_ops);
+}
#endif
diff --git a/tools/perf/util/trace-event-perl.h b/tools/perf/util/trace-event-perl.h
index 8fe0d866fe1..e88fb26137b 100644
--- a/tools/perf/util/trace-event-perl.h
+++ b/tools/perf/util/trace-event-perl.h
@@ -34,9 +34,13 @@ typedef int INTERP;
#define dXSUB_SYS
#define pTHX_
static inline void newXS(const char *a, void *b, const char *c) {}
+static void boot_Perf__Trace__Context(pTHX_ CV *cv) {}
+static void boot_DynaLoader(pTHX_ CV *cv) {}
#else
#include <EXTERN.h>
#include <perl.h>
+void boot_Perf__Trace__Context(pTHX_ CV *cv);
+void boot_DynaLoader(pTHX_ CV *cv);
typedef PerlInterpreter * INTERP;
#endif
diff --git a/tools/perf/util/trace-event-read.c b/tools/perf/util/trace-event-read.c
index 342dfdd43f8..1744422cafc 100644
--- a/tools/perf/util/trace-event-read.c
+++ b/tools/perf/util/trace-event-read.c
@@ -145,8 +145,9 @@ static void read_proc_kallsyms(void)
if (!size)
return;
- buf = malloc_or_die(size);
+ buf = malloc_or_die(size + 1);
read_or_die(buf, size);
+ buf[size] = '\0';
parse_proc_kallsyms(buf, size);
diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h
index 81698d5e650..6ad405620c9 100644
--- a/tools/perf/util/trace-event.h
+++ b/tools/perf/util/trace-event.h
@@ -270,7 +270,7 @@ enum trace_flag_type {
struct scripting_ops {
const char *name;
- int (*start_script) (const char *);
+ int (*start_script) (const char *script, int argc, const char **argv);
int (*stop_script) (void);
void (*process_event) (int cpu, void *data, int size,
unsigned long long nsecs, char *comm);