summaryrefslogtreecommitdiff
path: root/tools/perf/util
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/util')
-rw-r--r--tools/perf/util/bitmap.c21
-rw-r--r--tools/perf/util/callchain.c13
-rw-r--r--tools/perf/util/callchain.h4
-rw-r--r--tools/perf/util/event.c138
-rw-r--r--tools/perf/util/event.h23
-rw-r--r--tools/perf/util/header.c151
-rw-r--r--tools/perf/util/header.h4
-rw-r--r--tools/perf/util/hist.c486
-rw-r--r--tools/perf/util/hist.h115
-rw-r--r--tools/perf/util/hweight.c31
-rw-r--r--tools/perf/util/include/asm/bitops.h18
-rw-r--r--tools/perf/util/include/asm/hweight.h8
-rw-r--r--tools/perf/util/include/dwarf-regs.h8
-rw-r--r--tools/perf/util/include/linux/bitmap.h38
-rw-r--r--tools/perf/util/include/linux/bitops.h20
-rw-r--r--tools/perf/util/include/linux/kernel.h2
-rw-r--r--tools/perf/util/map.c125
-rw-r--r--tools/perf/util/map.h86
-rw-r--r--tools/perf/util/newt.c645
-rw-r--r--tools/perf/util/parse-events.c41
-rw-r--r--tools/perf/util/parse-events.h1
-rw-r--r--tools/perf/util/parse-options.c48
-rw-r--r--tools/perf/util/parse-options.h27
-rw-r--r--tools/perf/util/probe-event.c54
-rw-r--r--tools/perf/util/probe-event.h4
-rw-r--r--tools/perf/util/probe-finder.c70
-rw-r--r--tools/perf/util/probe-finder.h6
-rw-r--r--tools/perf/util/pstack.c75
-rw-r--r--tools/perf/util/pstack.h12
-rw-r--r--tools/perf/util/scripting-engines/trace-event-perl.c3
-rw-r--r--tools/perf/util/scripting-engines/trace-event-python.c4
-rw-r--r--tools/perf/util/session.c299
-rw-r--r--tools/perf/util/session.h116
-rw-r--r--tools/perf/util/sort.c8
-rw-r--r--tools/perf/util/sort.h25
-rw-r--r--tools/perf/util/symbol.c244
-rw-r--r--tools/perf/util/symbol.h28
-rw-r--r--tools/perf/util/trace-event-info.c11
-rw-r--r--tools/perf/util/trace-event-parse.c91
-rw-r--r--tools/perf/util/trace-event-read.c31
-rw-r--r--tools/perf/util/trace-event.h3
-rw-r--r--tools/perf/util/util.c22
-rw-r--r--tools/perf/util/util.h1
43 files changed, 2144 insertions, 1016 deletions
diff --git a/tools/perf/util/bitmap.c b/tools/perf/util/bitmap.c
new file mode 100644
index 00000000000..5e230acae1e
--- /dev/null
+++ b/tools/perf/util/bitmap.c
@@ -0,0 +1,21 @@
+/*
+ * From lib/bitmap.c
+ * Helper functions for bitmap.h.
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+#include <linux/bitmap.h>
+
+int __bitmap_weight(const unsigned long *bitmap, int bits)
+{
+ int k, w = 0, lim = bits/BITS_PER_LONG;
+
+ for (k = 0; k < lim; k++)
+ w += hweight_long(bitmap[k]);
+
+ if (bits % BITS_PER_LONG)
+ w += hweight_long(bitmap[k] & BITMAP_LAST_WORD_MASK(bits));
+
+ return w;
+}
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c
index db628af6d20..21a52e0a443 100644
--- a/tools/perf/util/callchain.c
+++ b/tools/perf/util/callchain.c
@@ -17,6 +17,13 @@
#include "callchain.h"
+bool ip_callchain__valid(struct ip_callchain *chain, event_t *event)
+{
+ unsigned int chain_size = event->header.size;
+ chain_size -= (unsigned long)&event->ip.__more_data - (unsigned long)event;
+ return chain->nr * sizeof(u64) <= chain_size;
+}
+
#define chain_for_each_child(child, parent) \
list_for_each_entry(child, &parent->children, brothers)
@@ -160,7 +167,7 @@ create_child(struct callchain_node *parent, bool inherit_children)
{
struct callchain_node *new;
- new = malloc(sizeof(*new));
+ new = zalloc(sizeof(*new));
if (!new) {
perror("not enough memory to create child for code path tree");
return NULL;
@@ -206,7 +213,7 @@ fill_node(struct callchain_node *node, struct resolved_chain *chain, int start)
for (i = start; i < chain->nr; i++) {
struct callchain_list *call;
- call = malloc(sizeof(*call));
+ call = zalloc(sizeof(*call));
if (!call) {
perror("not enough memory for the code path tree");
return;
@@ -379,7 +386,7 @@ int append_chain(struct callchain_node *root, struct ip_callchain *chain,
if (!chain->nr)
return 0;
- filtered = malloc(sizeof(*filtered) +
+ filtered = zalloc(sizeof(*filtered) +
chain->nr * sizeof(struct resolved_ip));
if (!filtered)
return -ENOMEM;
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h
index 8a7e8bbd0fd..1cba1f5504e 100644
--- a/tools/perf/util/callchain.h
+++ b/tools/perf/util/callchain.h
@@ -4,6 +4,7 @@
#include "../perf.h"
#include <linux/list.h>
#include <linux/rbtree.h>
+#include "event.h"
#include "util.h"
#include "symbol.h"
@@ -33,6 +34,7 @@ typedef void (*sort_chain_func_t)(struct rb_root *, struct callchain_node *,
struct callchain_param {
enum chain_mode mode;
+ u32 print_limit;
double min_percent;
sort_chain_func_t sort;
};
@@ -58,4 +60,6 @@ static inline u64 cumul_hits(struct callchain_node *node)
int register_callchain_param(struct callchain_param *param);
int append_chain(struct callchain_node *root, struct ip_callchain *chain,
struct map_symbol *syms);
+
+bool ip_callchain__valid(struct ip_callchain *chain, event_t *event);
#endif /* __PERF_CALLCHAIN_H */
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
index e3fa8d3d11b..50771b5813e 100644
--- a/tools/perf/util/event.c
+++ b/tools/perf/util/event.c
@@ -7,6 +7,23 @@
#include "strlist.h"
#include "thread.h"
+const char *event__name[] = {
+ [0] = "TOTAL",
+ [PERF_RECORD_MMAP] = "MMAP",
+ [PERF_RECORD_LOST] = "LOST",
+ [PERF_RECORD_COMM] = "COMM",
+ [PERF_RECORD_EXIT] = "EXIT",
+ [PERF_RECORD_THROTTLE] = "THROTTLE",
+ [PERF_RECORD_UNTHROTTLE] = "UNTHROTTLE",
+ [PERF_RECORD_FORK] = "FORK",
+ [PERF_RECORD_READ] = "READ",
+ [PERF_RECORD_SAMPLE] = "SAMPLE",
+ [PERF_RECORD_HEADER_ATTR] = "ATTR",
+ [PERF_RECORD_HEADER_EVENT_TYPE] = "EVENT_TYPE",
+ [PERF_RECORD_HEADER_TRACING_DATA] = "TRACING_DATA",
+ [PERF_RECORD_HEADER_BUILD_ID] = "BUILD_ID",
+};
+
static pid_t event__synthesize_comm(pid_t pid, int full,
event__handler_t process,
struct perf_session *session)
@@ -172,17 +189,17 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid,
int event__synthesize_modules(event__handler_t process,
struct perf_session *session,
- struct kernel_info *kerninfo)
+ struct machine *machine)
{
struct rb_node *nd;
- struct map_groups *kmaps = &kerninfo->kmaps;
+ struct map_groups *kmaps = &machine->kmaps;
u16 misc;
/*
* kernel uses 0 for user space maps, see kernel/perf_event.c
* __perf_event_mmap
*/
- if (is_host_kernel(kerninfo))
+ if (machine__is_host(machine))
misc = PERF_RECORD_MISC_KERNEL;
else
misc = PERF_RECORD_MISC_GUEST_KERNEL;
@@ -204,7 +221,7 @@ int event__synthesize_modules(event__handler_t process,
(sizeof(ev.mmap.filename) - size));
ev.mmap.start = pos->start;
ev.mmap.len = pos->end - pos->start;
- ev.mmap.pid = kerninfo->pid;
+ ev.mmap.pid = machine->pid;
memcpy(ev.mmap.filename, pos->dso->long_name,
pos->dso->long_name_len + 1);
@@ -267,7 +284,7 @@ static int find_symbol_cb(void *arg, const char *name, char type, u64 start)
int event__synthesize_kernel_mmap(event__handler_t process,
struct perf_session *session,
- struct kernel_info *kerninfo,
+ struct machine *machine,
const char *symbol_name)
{
size_t size;
@@ -288,8 +305,8 @@ int event__synthesize_kernel_mmap(event__handler_t process,
*/
struct process_symbol_args args = { .name = symbol_name, };
- mmap_name = kern_mmap_name(kerninfo, name_buff);
- if (is_host_kernel(kerninfo)) {
+ mmap_name = machine__mmap_name(machine, name_buff, sizeof(name_buff));
+ if (machine__is_host(machine)) {
/*
* kernel uses PERF_RECORD_MISC_USER for user space maps,
* see kernel/perf_event.c __perf_event_mmap
@@ -298,10 +315,10 @@ int event__synthesize_kernel_mmap(event__handler_t process,
filename = "/proc/kallsyms";
} else {
ev.header.misc = PERF_RECORD_MISC_GUEST_KERNEL;
- if (is_default_guest(kerninfo))
+ if (machine__is_default_guest(machine))
filename = (char *) symbol_conf.default_guest_kallsyms;
else {
- sprintf(path, "%s/proc/kallsyms", kerninfo->root_dir);
+ sprintf(path, "%s/proc/kallsyms", machine->root_dir);
filename = path;
}
}
@@ -309,7 +326,7 @@ int event__synthesize_kernel_mmap(event__handler_t process,
if (kallsyms__parse(filename, &args, find_symbol_cb) <= 0)
return -ENOENT;
- map = kerninfo->vmlinux_maps[MAP__FUNCTION];
+ map = machine->vmlinux_maps[MAP__FUNCTION];
size = snprintf(ev.mmap.filename, sizeof(ev.mmap.filename),
"%s%s", mmap_name, symbol_name) + 1;
size = ALIGN(size, sizeof(u64));
@@ -318,7 +335,7 @@ int event__synthesize_kernel_mmap(event__handler_t process,
ev.mmap.pgoff = args.start;
ev.mmap.start = map->start;
ev.mmap.len = map->end - ev.mmap.start;
- ev.mmap.pid = kerninfo->pid;
+ ev.mmap.pid = machine->pid;
return process(&ev, session);
}
@@ -368,7 +385,7 @@ int event__process_comm(event_t *self, struct perf_session *session)
int event__process_lost(event_t *self, struct perf_session *session)
{
dump_printf(": id:%Ld: lost:%Ld\n", self->lost.id, self->lost.lost);
- session->events_stats.lost += self->lost.lost;
+ session->hists.stats.total_lost += self->lost.lost;
return 0;
}
@@ -389,18 +406,18 @@ static int event__process_kernel_mmap(event_t *self,
{
struct map *map;
char kmmap_prefix[PATH_MAX];
- struct kernel_info *kerninfo;
+ struct machine *machine;
enum dso_kernel_type kernel_type;
bool is_kernel_mmap;
- kerninfo = kerninfo__findnew(&session->kerninfo_root, self->mmap.pid);
- if (!kerninfo) {
- pr_err("Can't find id %d's kerninfo\n", self->mmap.pid);
+ machine = perf_session__findnew_machine(session, self->mmap.pid);
+ if (!machine) {
+ pr_err("Can't find id %d's machine\n", self->mmap.pid);
goto out_problem;
}
- kern_mmap_name(kerninfo, kmmap_prefix);
- if (is_host_kernel(kerninfo))
+ machine__mmap_name(machine, kmmap_prefix, sizeof(kmmap_prefix));
+ if (machine__is_host(machine))
kernel_type = DSO_TYPE_KERNEL;
else
kernel_type = DSO_TYPE_GUEST_KERNEL;
@@ -429,10 +446,8 @@ static int event__process_kernel_mmap(event_t *self,
} else
strcpy(short_module_name, self->mmap.filename);
- map = map_groups__new_module(&kerninfo->kmaps,
- self->mmap.start,
- self->mmap.filename,
- kerninfo);
+ map = machine__new_module(machine, self->mmap.start,
+ self->mmap.filename);
if (map == NULL)
goto out_problem;
@@ -449,27 +464,25 @@ static int event__process_kernel_mmap(event_t *self,
* Should be there already, from the build-id table in
* the header.
*/
- struct dso *kernel = __dsos__findnew(&kerninfo->dsos__kernel,
- kmmap_prefix);
+ struct dso *kernel = __dsos__findnew(&machine->kernel_dsos,
+ kmmap_prefix);
if (kernel == NULL)
goto out_problem;
kernel->kernel = kernel_type;
- if (__map_groups__create_kernel_maps(&kerninfo->kmaps,
- kerninfo->vmlinux_maps, kernel) < 0)
+ if (__machine__create_kernel_maps(machine, kernel) < 0)
goto out_problem;
- event_set_kernel_mmap_len(kerninfo->vmlinux_maps, self);
- perf_session__set_kallsyms_ref_reloc_sym(kerninfo->vmlinux_maps,
- symbol_name,
- self->mmap.pgoff);
- if (is_default_guest(kerninfo)) {
+ event_set_kernel_mmap_len(machine->vmlinux_maps, self);
+ perf_session__set_kallsyms_ref_reloc_sym(machine->vmlinux_maps,
+ symbol_name,
+ self->mmap.pgoff);
+ if (machine__is_default_guest(machine)) {
/*
* preload dso of guest kernel and modules
*/
- dso__load(kernel,
- kerninfo->vmlinux_maps[MAP__FUNCTION],
- NULL);
+ dso__load(kernel, machine->vmlinux_maps[MAP__FUNCTION],
+ NULL);
}
}
return 0;
@@ -479,7 +492,7 @@ out_problem:
int event__process_mmap(event_t *self, struct perf_session *session)
{
- struct kernel_info *kerninfo;
+ struct machine *machine;
struct thread *thread;
struct map *map;
u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
@@ -497,9 +510,11 @@ int event__process_mmap(event_t *self, struct perf_session *session)
return 0;
}
+ machine = perf_session__find_host_machine(session);
+ if (machine == NULL)
+ goto out_problem;
thread = perf_session__findnew(session, self->mmap.pid);
- kerninfo = kerninfo__findhost(&session->kerninfo_root);
- map = map__new(&kerninfo->dsos__user, self->mmap.start,
+ map = map__new(&machine->user_dsos, self->mmap.start,
self->mmap.len, self->mmap.pgoff,
self->mmap.pid, self->mmap.filename,
MAP__FUNCTION, session->cwd, session->cwdlen);
@@ -546,7 +561,7 @@ void thread__find_addr_map(struct thread *self,
struct addr_location *al)
{
struct map_groups *mg = &self->mg;
- struct kernel_info *kerninfo = NULL;
+ struct machine *machine = NULL;
al->thread = self;
al->addr = addr;
@@ -555,19 +570,23 @@ void thread__find_addr_map(struct thread *self,
if (cpumode == PERF_RECORD_MISC_KERNEL && perf_host) {
al->level = 'k';
- kerninfo = kerninfo__findhost(&session->kerninfo_root);
- mg = &kerninfo->kmaps;
+ machine = perf_session__find_host_machine(session);
+ if (machine == NULL) {
+ al->map = NULL;
+ return;
+ }
+ mg = &machine->kmaps;
} else if (cpumode == PERF_RECORD_MISC_USER && perf_host) {
al->level = '.';
- kerninfo = kerninfo__findhost(&session->kerninfo_root);
+ machine = perf_session__find_host_machine(session);
} else if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL && perf_guest) {
al->level = 'g';
- kerninfo = kerninfo__find(&session->kerninfo_root, pid);
- if (!kerninfo) {
+ machine = perf_session__find_machine(session, pid);
+ if (machine == NULL) {
al->map = NULL;
return;
}
- mg = &kerninfo->kmaps;
+ mg = &machine->kmaps;
} else {
/*
* 'u' means guest os user space.
@@ -603,10 +622,9 @@ try_again:
* in the whole kernel symbol list.
*/
if ((long long)al->addr < 0 &&
- cpumode == PERF_RECORD_MISC_KERNEL &&
- kerninfo &&
- mg != &kerninfo->kmaps) {
- mg = &kerninfo->kmaps;
+ cpumode == PERF_RECORD_MISC_KERNEL &&
+ machine && mg != &machine->kmaps) {
+ mg = &machine->kmaps;
goto try_again;
}
} else
@@ -631,8 +649,10 @@ 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)
+ u16 slen = self->short_name_len;
+ if (verbose)
+ slen = self->long_name_len;
+ if (dsos__col_width < slen)
dsos__col_width = slen;
}
@@ -653,6 +673,16 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session,
goto out_filtered;
dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid);
+ /*
+ * Have we already created the kernel maps for the host machine?
+ *
+ * This should have happened earlier, when we processed the kernel MMAP
+ * events, but for older perf.data files there was no such thing, so do
+ * it now.
+ */
+ if (cpumode == PERF_RECORD_MISC_KERNEL &&
+ session->host_machine.vmlinux_maps[MAP__FUNCTION] == NULL)
+ machine__create_kernel_maps(&session->host_machine);
thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION,
self->ip.pid, self->ip.ip, al);
@@ -679,6 +709,13 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session,
dso__calc_col_width(al->map->dso);
al->sym = map__find_symbol(al->map, al->addr, filter);
+ } else {
+ const unsigned int unresolved_col_width = BITS_PER_LONG / 4;
+
+ if (dsos__col_width < unresolved_col_width &&
+ !symbol_conf.col_width_list_str && !symbol_conf.field_sep &&
+ !symbol_conf.dso_list)
+ dsos__col_width = unresolved_col_width;
}
if (symbol_conf.sym_list && al->sym &&
@@ -718,6 +755,7 @@ int event__parse_sample(event_t *event, u64 type, struct sample_data *data)
array++;
}
+ data->id = -1ULL;
if (type & PERF_SAMPLE_ID) {
data->id = *array;
array++;
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
index 4af2ed5d48a..8577085db06 100644
--- a/tools/perf/util/event.h
+++ b/tools/perf/util/event.h
@@ -84,11 +84,12 @@ struct build_id_event {
char filename[];
};
-enum perf_header_event_type { /* above any possible kernel type */
+enum perf_user_event_type { /* above any possible kernel type */
PERF_RECORD_HEADER_ATTR = 64,
PERF_RECORD_HEADER_EVENT_TYPE = 65,
PERF_RECORD_HEADER_TRACING_DATA = 66,
PERF_RECORD_HEADER_BUILD_ID = 67,
+ PERF_RECORD_FINISHED_ROUND = 68,
PERF_RECORD_HEADER_MAX
};
@@ -130,20 +131,6 @@ typedef union event_union {
struct build_id_event build_id;
} event_t;
-struct events_stats {
- u64 total;
- u64 lost;
-};
-
-struct event_stat_id {
- struct rb_node rb_node;
- struct rb_root hists;
- struct events_stats stats;
- u64 config;
- u64 event_stream;
- u32 type;
-};
-
void event__print_totals(void);
struct perf_session;
@@ -156,12 +143,12 @@ void event__synthesize_threads(event__handler_t process,
struct perf_session *session);
int event__synthesize_kernel_mmap(event__handler_t process,
struct perf_session *session,
- struct kernel_info *kerninfo,
+ struct machine *machine,
const char *symbol_name);
int event__synthesize_modules(event__handler_t process,
struct perf_session *session,
- struct kernel_info *kerninfo);
+ struct machine *machine);
int event__process_comm(event_t *self, struct perf_session *session);
int event__process_lost(event_t *self, struct perf_session *session);
@@ -173,4 +160,6 @@ 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);
+extern const char *event__name[];
+
#endif /* __PERF_RECORD_H */
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 75d01676802..8847bec64c5 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -229,10 +229,9 @@ static int dsos__write_buildid_table(struct perf_header *header, int fd)
int err = 0;
u16 kmisc, umisc;
- for (nd = rb_first(&session->kerninfo_root); nd; nd = rb_next(nd)) {
- struct kernel_info *pos = rb_entry(nd, struct kernel_info,
- rb_node);
- if (is_host_kernel(pos)) {
+ for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) {
+ struct machine *pos = rb_entry(nd, struct machine, rb_node);
+ if (machine__is_host(pos)) {
kmisc = PERF_RECORD_MISC_KERNEL;
umisc = PERF_RECORD_MISC_USER;
} else {
@@ -240,11 +239,11 @@ static int dsos__write_buildid_table(struct perf_header *header, int fd)
umisc = PERF_RECORD_MISC_GUEST_USER;
}
- err = __dsos__write_buildid_table(&pos->dsos__kernel, pos->pid,
- kmisc, fd);
+ err = __dsos__write_buildid_table(&pos->kernel_dsos, pos->pid,
+ kmisc, fd);
if (err == 0)
- err = __dsos__write_buildid_table(&pos->dsos__user,
- pos->pid, umisc, fd);
+ err = __dsos__write_buildid_table(&pos->user_dsos,
+ pos->pid, umisc, fd);
if (err)
break;
}
@@ -378,11 +377,10 @@ static int dsos__cache_build_ids(struct perf_header *self)
if (mkdir(debugdir, 0755) != 0 && errno != EEXIST)
return -1;
- for (nd = rb_first(&session->kerninfo_root); nd; nd = rb_next(nd)) {
- struct kernel_info *pos = rb_entry(nd, struct kernel_info,
- rb_node);
- ret |= __dsos__cache_build_ids(&pos->dsos__kernel, debugdir);
- ret |= __dsos__cache_build_ids(&pos->dsos__user, debugdir);
+ for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) {
+ struct machine *pos = rb_entry(nd, struct machine, rb_node);
+ ret |= __dsos__cache_build_ids(&pos->kernel_dsos, debugdir);
+ ret |= __dsos__cache_build_ids(&pos->user_dsos, debugdir);
}
return ret ? -1 : 0;
}
@@ -394,11 +392,10 @@ static bool dsos__read_build_ids(struct perf_header *self, bool with_hits)
struct perf_session, header);
struct rb_node *nd;
- for (nd = rb_first(&session->kerninfo_root); nd; nd = rb_next(nd)) {
- struct kernel_info *pos = rb_entry(nd, struct kernel_info,
- rb_node);
- ret |= __dsos__read_build_ids(&pos->dsos__kernel, with_hits);
- ret |= __dsos__read_build_ids(&pos->dsos__user, with_hits);
+ for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) {
+ struct machine *pos = rb_entry(nd, struct machine, rb_node);
+ ret |= __dsos__read_build_ids(&pos->kernel_dsos, with_hits);
+ ret |= __dsos__read_build_ids(&pos->user_dsos, with_hits);
}
return ret;
@@ -439,7 +436,6 @@ static int perf_header__adds_write(struct perf_header *self, int fd)
trace_sec->size = lseek(fd, 0, SEEK_CUR) - trace_sec->offset;
}
-
if (perf_header__has_feat(self, HEADER_BUILD_ID)) {
struct perf_file_section *buildid_sec;
@@ -685,13 +681,13 @@ static int __event_process_build_id(struct build_id_event *bev,
{
int err = -1;
struct list_head *head;
- struct kernel_info *kerninfo;
+ struct machine *machine;
u16 misc;
struct dso *dso;
enum dso_kernel_type dso_type;
- kerninfo = kerninfo__findnew(&session->kerninfo_root, bev->pid);
- if (!kerninfo)
+ machine = perf_session__findnew_machine(session, bev->pid);
+ if (!machine)
goto out;
misc = bev->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
@@ -699,16 +695,16 @@ static int __event_process_build_id(struct build_id_event *bev,
switch (misc) {
case PERF_RECORD_MISC_KERNEL:
dso_type = DSO_TYPE_KERNEL;
- head = &kerninfo->dsos__kernel;
+ head = &machine->kernel_dsos;
break;
case PERF_RECORD_MISC_GUEST_KERNEL:
dso_type = DSO_TYPE_GUEST_KERNEL;
- head = &kerninfo->dsos__kernel;
+ head = &machine->kernel_dsos;
break;
case PERF_RECORD_MISC_USER:
case PERF_RECORD_MISC_GUEST_USER:
dso_type = DSO_TYPE_USER;
- head = &kerninfo->dsos__user;
+ head = &machine->user_dsos;
break;
default:
goto out;
@@ -716,10 +712,18 @@ static int __event_process_build_id(struct build_id_event *bev,
dso = __dsos__findnew(head, filename);
if (dso != NULL) {
+ char sbuild_id[BUILD_ID_SIZE * 2 + 1];
+
dso__set_build_id(dso, &bev->build_id);
- if (filename[0] == '[')
- dso->kernel = dso_type;
- }
+
+ if (filename[0] == '[')
+ dso->kernel = dso_type;
+
+ build_id__sprintf(dso->build_id, sizeof(dso->build_id),
+ sbuild_id);
+ pr_debug("build id event received for %s: %s\n",
+ dso->long_name, sbuild_id);
+ }
err = 0;
out:
@@ -770,7 +774,7 @@ static int perf_file_section__process(struct perf_file_section *self,
switch (feat) {
case HEADER_TRACE_INFO:
- trace_report(fd);
+ trace_report(fd, false);
break;
case HEADER_BUILD_ID:
@@ -785,12 +789,16 @@ static int perf_file_section__process(struct perf_file_section *self,
}
static int perf_file_header__read_pipe(struct perf_pipe_file_header *self,
- struct perf_header *ph, int fd)
+ struct perf_header *ph, int fd,
+ bool repipe)
{
if (do_read(fd, self, sizeof(*self)) <= 0 ||
memcmp(&self->magic, __perf_magic, sizeof(self->magic)))
return -1;
+ if (repipe && do_write(STDOUT_FILENO, self, sizeof(*self)) < 0)
+ return -1;
+
if (self->size != sizeof(*self)) {
u64 size = bswap_64(self->size);
@@ -808,7 +816,8 @@ static int perf_header__read_pipe(struct perf_session *session, int fd)
struct perf_header *self = &session->header;
struct perf_pipe_file_header f_header;
- if (perf_file_header__read_pipe(&f_header, self, fd) < 0) {
+ if (perf_file_header__read_pipe(&f_header, self, fd,
+ session->repipe) < 0) {
pr_debug("incompatible file format\n");
return -EINVAL;
}
@@ -913,6 +922,14 @@ perf_header__find_attr(u64 id, struct perf_header *header)
{
int i;
+ /*
+ * We set id to -1 if the data file doesn't contain sample
+ * ids. Check for this and avoid walking through the entire
+ * list of ids which may be large.
+ */
+ if (id == -1ULL)
+ return NULL;
+
for (i = 0; i < header->attrs; i++) {
struct perf_header_attr *attr = header->attr[i];
int j;
@@ -1099,12 +1116,17 @@ int event__process_tracing_data(event_t *self,
lseek(session->fd, offset + sizeof(struct tracing_data_event),
SEEK_SET);
- size_read = trace_report(session->fd);
+ size_read = trace_report(session->fd, session->repipe);
padding = ALIGN(size_read, sizeof(u64)) - size_read;
if (read(session->fd, buf, padding) < 0)
die("reading input file");
+ if (session->repipe) {
+ int retw = write(STDOUT_FILENO, buf, padding);
+ if (retw <= 0 || retw != padding)
+ die("repiping tracing data padding");
+ }
if (size_read + padding != size)
die("tracing data size mismatch");
@@ -1114,7 +1136,7 @@ int event__process_tracing_data(event_t *self,
int event__synthesize_build_id(struct dso *pos, u16 misc,
event__handler_t process,
- struct kernel_info *kerninfo,
+ struct machine *machine,
struct perf_session *session)
{
event_t ev;
@@ -1131,7 +1153,7 @@ int event__synthesize_build_id(struct dso *pos, u16 misc,
memcpy(&ev.build_id.build_id, pos->build_id, sizeof(pos->build_id));
ev.build_id.header.type = PERF_RECORD_HEADER_BUILD_ID;
ev.build_id.header.misc = misc;
- ev.build_id.pid = kerninfo->pid;
+ ev.build_id.pid = machine->pid;
ev.build_id.header.size = sizeof(ev.build_id) + len;
memcpy(&ev.build_id.filename, pos->long_name, pos->long_name_len);
@@ -1140,67 +1162,6 @@ int event__synthesize_build_id(struct dso *pos, u16 misc,
return err;
}
-static int __event_synthesize_build_ids(struct list_head *head, u16 misc,
- event__handler_t process,
- struct kernel_info *kerninfo,
- struct perf_session *session)
-{
- struct dso *pos;
-
- dsos__for_each_with_build_id(pos, head) {
- int err;
- if (!pos->hit)
- continue;
-
- err = event__synthesize_build_id(pos, misc, process,
- kerninfo, session);
- if (err < 0)
- return err;
- }
-
- return 0;
-}
-
-int event__synthesize_build_ids(event__handler_t process,
- struct perf_session *session)
-{
- int err = 0;
- u16 kmisc, umisc;
- struct kernel_info *pos;
- struct rb_node *nd;
-
- if (!dsos__read_build_ids(&session->header, true))
- return 0;
-
- for (nd = rb_first(&session->kerninfo_root); nd; nd = rb_next(nd)) {
- pos = rb_entry(nd, struct kernel_info, rb_node);
- if (is_host_kernel(pos)) {
- kmisc = PERF_RECORD_MISC_KERNEL;
- umisc = PERF_RECORD_MISC_USER;
- } else {
- kmisc = PERF_RECORD_MISC_GUEST_KERNEL;
- umisc = PERF_RECORD_MISC_GUEST_USER;
- }
-
- err = __event_synthesize_build_ids(&pos->dsos__kernel,
- kmisc, process, pos, session);
- if (err == 0)
- err = __event_synthesize_build_ids(&pos->dsos__user,
- umisc, process, pos, session);
- if (err)
- break;
- }
-
- if (err < 0) {
- pr_debug("failed to synthesize build ids\n");
- return err;
- }
-
- dsos__cache_build_ids(&session->header);
-
- return 0;
-}
-
int event__process_build_id(event_t *self,
struct perf_session *session)
{
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h
index 27591545814..402ac2454cf 100644
--- a/tools/perf/util/header.h
+++ b/tools/perf/util/header.h
@@ -120,10 +120,8 @@ int event__process_tracing_data(event_t *self,
int event__synthesize_build_id(struct dso *pos, u16 misc,
event__handler_t process,
- struct kernel_info *kerninfo,
+ struct machine *machine,
struct perf_session *session);
-int event__synthesize_build_ids(event__handler_t process,
- struct perf_session *session);
int event__process_build_id(event_t *self, struct perf_session *session);
#endif /* __PERF_HEADER_H */
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index ad6b22dde27..9a71c94f057 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -1,3 +1,4 @@
+#include "util.h"
#include "hist.h"
#include "session.h"
#include "sort.h"
@@ -8,24 +9,21 @@ struct callchain_param callchain_param = {
.min_percent = 0.5
};
-void __perf_session__add_count(struct hist_entry *he,
- struct addr_location *al,
- u64 count)
+static void hist_entry__add_cpumode_period(struct hist_entry *self,
+ unsigned int cpumode, u64 period)
{
- he->count += count;
-
- switch (al->cpumode) {
+ switch (cpumode) {
case PERF_RECORD_MISC_KERNEL:
- he->count_sys += count;
+ self->period_sys += period;
break;
case PERF_RECORD_MISC_USER:
- he->count_us += count;
+ self->period_us += period;
break;
case PERF_RECORD_MISC_GUEST_KERNEL:
- he->count_guest_sys += count;
+ self->period_guest_sys += period;
break;
case PERF_RECORD_MISC_GUEST_USER:
- he->count_guest_us += count;
+ self->period_guest_us += period;
break;
default:
break;
@@ -33,15 +31,36 @@ void __perf_session__add_count(struct hist_entry *he,
}
/*
- * histogram, sorted on item, collects counts
+ * histogram, sorted on item, collects periods
*/
-struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists,
- struct addr_location *al,
- struct symbol *sym_parent,
- u64 count, bool *hit)
+static struct hist_entry *hist_entry__new(struct hist_entry *template)
+{
+ size_t callchain_size = symbol_conf.use_callchain ? sizeof(struct callchain_node) : 0;
+ struct hist_entry *self = malloc(sizeof(*self) + callchain_size);
+
+ if (self != NULL) {
+ *self = *template;
+ self->nr_events = 1;
+ if (symbol_conf.use_callchain)
+ callchain_init(self->callchain);
+ }
+
+ return self;
+}
+
+static void hists__inc_nr_entries(struct hists *self, struct hist_entry *entry)
{
- struct rb_node **p = &hists->rb_node;
+ if (entry->ms.sym && self->max_sym_namelen < entry->ms.sym->namelen)
+ self->max_sym_namelen = entry->ms.sym->namelen;
+ ++self->nr_entries;
+}
+
+struct hist_entry *__hists__add_entry(struct hists *self,
+ struct addr_location *al,
+ struct symbol *sym_parent, u64 period)
+{
+ struct rb_node **p = &self->entries.rb_node;
struct rb_node *parent = NULL;
struct hist_entry *he;
struct hist_entry entry = {
@@ -52,7 +71,7 @@ struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists,
},
.ip = al->addr,
.level = al->level,
- .count = count,
+ .period = period,
.parent = sym_parent,
};
int cmp;
@@ -64,8 +83,9 @@ struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists,
cmp = hist_entry__cmp(&entry, he);
if (!cmp) {
- *hit = true;
- return he;
+ he->period += period;
+ ++he->nr_events;
+ goto out;
}
if (cmp < 0)
@@ -74,14 +94,14 @@ struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists,
p = &(*p)->rb_right;
}
- he = malloc(sizeof(*he) + (symbol_conf.use_callchain ?
- sizeof(struct callchain_node) : 0));
+ he = hist_entry__new(&entry);
if (!he)
return NULL;
- *he = entry;
rb_link_node(&he->rb_node, parent, p);
- rb_insert_color(&he->rb_node, hists);
- *hit = false;
+ rb_insert_color(&he->rb_node, &self->entries);
+ hists__inc_nr_entries(self, he);
+out:
+ hist_entry__add_cpumode_period(he, al->cpumode, period);
return he;
}
@@ -128,7 +148,7 @@ void hist_entry__free(struct hist_entry *he)
* collapse the histogram
*/
-static void collapse__insert_entry(struct rb_root *root, struct hist_entry *he)
+static bool collapse__insert_entry(struct rb_root *root, struct hist_entry *he)
{
struct rb_node **p = &root->rb_node;
struct rb_node *parent = NULL;
@@ -142,9 +162,9 @@ static void collapse__insert_entry(struct rb_root *root, struct hist_entry *he)
cmp = hist_entry__collapse(iter, he);
if (!cmp) {
- iter->count += he->count;
+ iter->period += he->period;
hist_entry__free(he);
- return;
+ return false;
}
if (cmp < 0)
@@ -155,9 +175,10 @@ static void collapse__insert_entry(struct rb_root *root, struct hist_entry *he)
rb_link_node(&he->rb_node, parent, p);
rb_insert_color(&he->rb_node, root);
+ return true;
}
-void perf_session__collapse_resort(struct rb_root *hists)
+void hists__collapse_resort(struct hists *self)
{
struct rb_root tmp;
struct rb_node *next;
@@ -167,28 +188,31 @@ void perf_session__collapse_resort(struct rb_root *hists)
return;
tmp = RB_ROOT;
- next = rb_first(hists);
+ next = rb_first(&self->entries);
+ self->nr_entries = 0;
+ self->max_sym_namelen = 0;
while (next) {
n = rb_entry(next, struct hist_entry, rb_node);
next = rb_next(&n->rb_node);
- rb_erase(&n->rb_node, hists);
- collapse__insert_entry(&tmp, n);
+ rb_erase(&n->rb_node, &self->entries);
+ if (collapse__insert_entry(&tmp, n))
+ hists__inc_nr_entries(self, n);
}
- *hists = tmp;
+ self->entries = tmp;
}
/*
- * reverse the map, sort on count.
+ * reverse the map, sort on period.
*/
-static void perf_session__insert_output_hist_entry(struct rb_root *root,
- struct hist_entry *he,
- u64 min_callchain_hits)
+static void __hists__insert_output_entry(struct rb_root *entries,
+ struct hist_entry *he,
+ u64 min_callchain_hits)
{
- struct rb_node **p = &root->rb_node;
+ struct rb_node **p = &entries->rb_node;
struct rb_node *parent = NULL;
struct hist_entry *iter;
@@ -200,42 +224,41 @@ static void perf_session__insert_output_hist_entry(struct rb_root *root,
parent = *p;
iter = rb_entry(parent, struct hist_entry, rb_node);
- if (he->count > iter->count)
+ if (he->period > iter->period)
p = &(*p)->rb_left;
else
p = &(*p)->rb_right;
}
rb_link_node(&he->rb_node, parent, p);
- rb_insert_color(&he->rb_node, root);
+ rb_insert_color(&he->rb_node, entries);
}
-u64 perf_session__output_resort(struct rb_root *hists, u64 total_samples)
+void hists__output_resort(struct hists *self)
{
struct rb_root tmp;
struct rb_node *next;
struct hist_entry *n;
u64 min_callchain_hits;
- u64 nr_hists = 0;
- min_callchain_hits =
- total_samples * (callchain_param.min_percent / 100);
+ min_callchain_hits = self->stats.total_period * (callchain_param.min_percent / 100);
tmp = RB_ROOT;
- next = rb_first(hists);
+ next = rb_first(&self->entries);
+
+ self->nr_entries = 0;
+ self->max_sym_namelen = 0;
while (next) {
n = rb_entry(next, struct hist_entry, rb_node);
next = rb_next(&n->rb_node);
- rb_erase(&n->rb_node, hists);
- perf_session__insert_output_hist_entry(&tmp, n,
- min_callchain_hits);
- ++nr_hists;
+ rb_erase(&n->rb_node, &self->entries);
+ __hists__insert_output_entry(&tmp, n, min_callchain_hits);
+ hists__inc_nr_entries(self, n);
}
- *hists = tmp;
- return nr_hists;
+ self->entries = tmp;
}
static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin)
@@ -267,7 +290,7 @@ static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask,
}
static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain,
- int depth, int depth_mask, int count,
+ int depth, int depth_mask, int period,
u64 total_samples, int hits,
int left_margin)
{
@@ -280,7 +303,7 @@ static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain,
ret += fprintf(fp, "|");
else
ret += fprintf(fp, " ");
- if (!count && i == depth - 1) {
+ if (!period && i == depth - 1) {
double percent;
percent = hits * 100.0 / total_samples;
@@ -323,6 +346,7 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
u64 remaining;
size_t ret = 0;
int i;
+ uint entries_printed = 0;
if (callchain_param.mode == CHAIN_GRAPH_REL)
new_total = self->children_hit;
@@ -369,6 +393,8 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
new_depth_mask | (1 << depth),
left_margin);
node = next;
+ if (++entries_printed == callchain_param.print_limit)
+ break;
}
if (callchain_param.mode == CHAIN_GRAPH_REL &&
@@ -394,6 +420,7 @@ static size_t callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
bool printed = false;
int i = 0;
int ret = 0;
+ u32 entries_printed = 0;
list_for_each_entry(chain, &self->val, list) {
if (!i++ && sort__first_dimension == SORT_SYM)
@@ -414,6 +441,9 @@ static size_t callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
ret += fprintf(fp, " %s\n", chain->ms.sym->name);
else
ret += fprintf(fp, " %p\n", (void *)(long)chain->ip);
+
+ if (++entries_printed == callchain_param.print_limit)
+ break;
}
ret += __callchain__fprintf_graph(fp, self, total_samples, 1, 1, left_margin);
@@ -452,6 +482,7 @@ static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self,
struct rb_node *rb_node;
struct callchain_node *chain;
size_t ret = 0;
+ u32 entries_printed = 0;
rb_node = rb_first(&self->sorted_chain);
while (rb_node) {
@@ -474,89 +505,88 @@ static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self,
break;
}
ret += fprintf(fp, "\n");
+ if (++entries_printed == callchain_param.print_limit)
+ break;
rb_node = rb_next(rb_node);
}
return ret;
}
-int hist_entry__snprintf(struct hist_entry *self,
- char *s, size_t size,
- struct perf_session *pair_session,
- bool show_displacement,
- long displacement, bool color,
- u64 session_total)
+int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size,
+ struct hists *pair_hists, bool show_displacement,
+ long displacement, bool color, u64 session_total)
{
struct sort_entry *se;
- u64 count, total, count_sys, count_us, count_guest_sys, count_guest_us;
+ u64 period, total, period_sys, period_us, period_guest_sys, period_guest_us;
const char *sep = symbol_conf.field_sep;
int 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;
- count_sys = self->pair ? self->pair->count_sys : 0;
- count_us = self->pair ? self->pair->count_us : 0;
- count_guest_sys = self->pair ? self->pair->count_guest_sys : 0;
- count_guest_us = self->pair ? self->pair->count_guest_us : 0;
+ if (pair_hists) {
+ period = self->pair ? self->pair->period : 0;
+ total = pair_hists->stats.total_period;
+ period_sys = self->pair ? self->pair->period_sys : 0;
+ period_us = self->pair ? self->pair->period_us : 0;
+ period_guest_sys = self->pair ? self->pair->period_guest_sys : 0;
+ period_guest_us = self->pair ? self->pair->period_guest_us : 0;
} else {
- count = self->count;
+ period = self->period;
total = session_total;
- count_sys = self->count_sys;
- count_us = self->count_us;
- count_guest_sys = self->count_guest_sys;
- count_guest_us = self->count_guest_us;
+ period_sys = self->period_sys;
+ period_us = self->period_us;
+ period_guest_sys = self->period_guest_sys;
+ period_guest_us = self->period_guest_us;
}
if (total) {
if (color)
ret = percent_color_snprintf(s, size,
sep ? "%.2f" : " %6.2f%%",
- (count * 100.0) / total);
+ (period * 100.0) / total);
else
ret = snprintf(s, size, sep ? "%.2f" : " %6.2f%%",
- (count * 100.0) / total);
+ (period * 100.0) / total);
if (symbol_conf.show_cpu_utilization) {
ret += percent_color_snprintf(s + ret, size - ret,
sep ? "%.2f" : " %6.2f%%",
- (count_sys * 100.0) / total);
+ (period_sys * 100.0) / total);
ret += percent_color_snprintf(s + ret, size - ret,
sep ? "%.2f" : " %6.2f%%",
- (count_us * 100.0) / total);
+ (period_us * 100.0) / total);
if (perf_guest) {
ret += percent_color_snprintf(s + ret,
size - ret,
sep ? "%.2f" : " %6.2f%%",
- (count_guest_sys * 100.0) /
+ (period_guest_sys * 100.0) /
total);
ret += percent_color_snprintf(s + ret,
size - ret,
sep ? "%.2f" : " %6.2f%%",
- (count_guest_us * 100.0) /
+ (period_guest_us * 100.0) /
total);
}
}
} else
- ret = snprintf(s, size, sep ? "%lld" : "%12lld ", count);
+ ret = snprintf(s, size, sep ? "%lld" : "%12lld ", period);
if (symbol_conf.show_nr_samples) {
if (sep)
- ret += snprintf(s + ret, size - ret, "%c%lld", *sep, count);
+ ret += snprintf(s + ret, size - ret, "%c%lld", *sep, period);
else
- ret += snprintf(s + ret, size - ret, "%11lld", count);
+ ret += snprintf(s + ret, size - ret, "%11lld", period);
}
- if (pair_session) {
+ if (pair_hists) {
char bf[32];
double old_percent = 0, new_percent = 0, diff;
if (total > 0)
- old_percent = (count * 100.0) / total;
+ old_percent = (period * 100.0) / total;
if (session_total > 0)
- new_percent = (self->count * 100.0) / session_total;
+ new_percent = (self->period * 100.0) / session_total;
diff = new_percent - old_percent;
@@ -595,14 +625,12 @@ int hist_entry__snprintf(struct hist_entry *self,
return ret;
}
-int hist_entry__fprintf(struct hist_entry *self,
- struct perf_session *pair_session,
- bool show_displacement,
- long displacement, FILE *fp,
+int hist_entry__fprintf(struct hist_entry *self, struct hists *pair_hists,
+ bool show_displacement, long displacement, FILE *fp,
u64 session_total)
{
char bf[512];
- hist_entry__snprintf(self, bf, sizeof(bf), pair_session,
+ hist_entry__snprintf(self, bf, sizeof(bf), pair_hists,
show_displacement, displacement,
true, session_total);
return fprintf(fp, "%s\n", bf);
@@ -624,10 +652,8 @@ static size_t hist_entry__fprintf_callchain(struct hist_entry *self, FILE *fp,
left_margin);
}
-size_t perf_session__fprintf_hists(struct rb_root *hists,
- struct perf_session *pair,
- bool show_displacement, FILE *fp,
- u64 session_total)
+size_t hists__fprintf(struct hists *self, struct hists *pair,
+ bool show_displacement, FILE *fp)
{
struct sort_entry *se;
struct rb_node *nd;
@@ -636,7 +662,7 @@ size_t perf_session__fprintf_hists(struct rb_root *hists,
long displacement = 0;
unsigned int width;
const char *sep = symbol_conf.field_sep;
- char *col_width = symbol_conf.col_width_list_str;
+ const char *col_width = symbol_conf.col_width_list_str;
init_rem_hits();
@@ -733,7 +759,7 @@ size_t perf_session__fprintf_hists(struct rb_root *hists,
fprintf(fp, "\n#\n");
print_entries:
- for (nd = rb_first(hists); nd; nd = rb_next(nd)) {
+ for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) {
struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
if (show_displacement) {
@@ -745,10 +771,10 @@ print_entries:
++position;
}
ret += hist_entry__fprintf(h, pair, show_displacement,
- displacement, fp, session_total);
+ displacement, fp, self->stats.total_period);
if (symbol_conf.use_callchain)
- ret += hist_entry__fprintf_callchain(h, fp, session_total);
+ ret += hist_entry__fprintf_callchain(h, fp, self->stats.total_period);
if (h->ms.map == NULL && verbose > 1) {
__map_groups__fprintf_maps(&h->thread->mg,
@@ -761,3 +787,271 @@ print_entries:
return ret;
}
+
+enum hist_filter {
+ HIST_FILTER__DSO,
+ HIST_FILTER__THREAD,
+};
+
+void hists__filter_by_dso(struct hists *self, const struct dso *dso)
+{
+ struct rb_node *nd;
+
+ self->nr_entries = self->stats.total_period = 0;
+ self->stats.nr_events[PERF_RECORD_SAMPLE] = 0;
+ self->max_sym_namelen = 0;
+
+ for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) {
+ struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
+
+ if (symbol_conf.exclude_other && !h->parent)
+ continue;
+
+ if (dso != NULL && (h->ms.map == NULL || h->ms.map->dso != dso)) {
+ h->filtered |= (1 << HIST_FILTER__DSO);
+ continue;
+ }
+
+ h->filtered &= ~(1 << HIST_FILTER__DSO);
+ if (!h->filtered) {
+ ++self->nr_entries;
+ self->stats.total_period += h->period;
+ self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events;
+ if (h->ms.sym &&
+ self->max_sym_namelen < h->ms.sym->namelen)
+ self->max_sym_namelen = h->ms.sym->namelen;
+ }
+ }
+}
+
+void hists__filter_by_thread(struct hists *self, const struct thread *thread)
+{
+ struct rb_node *nd;
+
+ self->nr_entries = self->stats.total_period = 0;
+ self->stats.nr_events[PERF_RECORD_SAMPLE] = 0;
+ self->max_sym_namelen = 0;
+
+ for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) {
+ struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
+
+ if (thread != NULL && h->thread != thread) {
+ h->filtered |= (1 << HIST_FILTER__THREAD);
+ continue;
+ }
+ h->filtered &= ~(1 << HIST_FILTER__THREAD);
+ if (!h->filtered) {
+ ++self->nr_entries;
+ self->stats.total_period += h->period;
+ self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events;
+ if (h->ms.sym &&
+ self->max_sym_namelen < h->ms.sym->namelen)
+ self->max_sym_namelen = h->ms.sym->namelen;
+ }
+ }
+}
+
+static int symbol__alloc_hist(struct symbol *self)
+{
+ struct sym_priv *priv = symbol__priv(self);
+ const int size = (sizeof(*priv->hist) +
+ (self->end - self->start) * sizeof(u64));
+
+ priv->hist = zalloc(size);
+ return priv->hist == NULL ? -1 : 0;
+}
+
+int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip)
+{
+ unsigned int sym_size, offset;
+ struct symbol *sym = self->ms.sym;
+ struct sym_priv *priv;
+ struct sym_hist *h;
+
+ if (!sym || !self->ms.map)
+ return 0;
+
+ priv = symbol__priv(sym);
+ if (priv->hist == NULL && symbol__alloc_hist(sym) < 0)
+ return -ENOMEM;
+
+ sym_size = sym->end - sym->start;
+ offset = ip - sym->start;
+
+ pr_debug3("%s: ip=%#Lx\n", __func__, self->ms.map->unmap_ip(self->ms.map, ip));
+
+ if (offset >= sym_size)
+ return 0;
+
+ h = priv->hist;
+ h->sum++;
+ h->ip[offset]++;
+
+ pr_debug3("%#Lx %s: period++ [ip: %#Lx, %#Lx] => %Ld\n", self->ms.sym->start,
+ self->ms.sym->name, ip, ip - self->ms.sym->start, h->ip[offset]);
+ return 0;
+}
+
+static struct objdump_line *objdump_line__new(s64 offset, char *line)
+{
+ struct objdump_line *self = malloc(sizeof(*self));
+
+ if (self != NULL) {
+ self->offset = offset;
+ self->line = line;
+ }
+
+ return self;
+}
+
+void objdump_line__free(struct objdump_line *self)
+{
+ free(self->line);
+ free(self);
+}
+
+static void objdump__add_line(struct list_head *head, struct objdump_line *line)
+{
+ list_add_tail(&line->node, head);
+}
+
+struct objdump_line *objdump__get_next_ip_line(struct list_head *head,
+ struct objdump_line *pos)
+{
+ list_for_each_entry_continue(pos, head, node)
+ if (pos->offset >= 0)
+ return pos;
+
+ return NULL;
+}
+
+static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file,
+ struct list_head *head)
+{
+ struct symbol *sym = self->ms.sym;
+ struct objdump_line *objdump_line;
+ char *line = NULL, *tmp, *tmp2, *c;
+ size_t line_len;
+ s64 line_ip, offset = -1;
+
+ if (getline(&line, &line_len, file) < 0)
+ return -1;
+
+ if (!line)
+ return -1;
+
+ while (line_len != 0 && isspace(line[line_len - 1]))
+ line[--line_len] = '\0';
+
+ c = strchr(line, '\n');
+ if (c)
+ *c = 0;
+
+ line_ip = -1;
+
+ /*
+ * Strip leading spaces:
+ */
+ tmp = line;
+ while (*tmp) {
+ if (*tmp != ' ')
+ break;
+ tmp++;
+ }
+
+ if (*tmp) {
+ /*
+ * Parse hexa addresses followed by ':'
+ */
+ line_ip = strtoull(tmp, &tmp2, 16);
+ if (*tmp2 != ':')
+ line_ip = -1;
+ }
+
+ if (line_ip != -1) {
+ u64 start = map__rip_2objdump(self->ms.map, sym->start);
+ offset = line_ip - start;
+ }
+
+ objdump_line = objdump_line__new(offset, line);
+ if (objdump_line == NULL) {
+ free(line);
+ return -1;
+ }
+ objdump__add_line(head, objdump_line);
+
+ return 0;
+}
+
+int hist_entry__annotate(struct hist_entry *self, struct list_head *head)
+{
+ struct symbol *sym = self->ms.sym;
+ struct map *map = self->ms.map;
+ struct dso *dso = map->dso;
+ const char *filename = dso->long_name;
+ char command[PATH_MAX * 2];
+ FILE *file;
+ u64 len;
+
+ if (!filename)
+ return -1;
+
+ if (dso->origin == DSO__ORIG_KERNEL) {
+ if (dso->annotate_warned)
+ return 0;
+ dso->annotate_warned = 1;
+ pr_err("Can't annotate %s: No vmlinux file was found in the "
+ "path:\n", sym->name);
+ vmlinux_path__fprintf(stderr);
+ return -1;
+ }
+
+ pr_debug("%s: filename=%s, sym=%s, start=%#Lx, end=%#Lx\n", __func__,
+ filename, sym->name, map->unmap_ip(map, sym->start),
+ map->unmap_ip(map, sym->end));
+
+ len = sym->end - sym->start;
+
+ pr_debug("annotating [%p] %30s : [%p] %30s\n",
+ dso, dso->long_name, sym, sym->name);
+
+ snprintf(command, sizeof(command),
+ "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s|grep -v %s|expand",
+ map__rip_2objdump(map, sym->start),
+ map__rip_2objdump(map, sym->end),
+ filename, filename);
+
+ pr_debug("Executing: %s\n", command);
+
+ file = popen(command, "r");
+ if (!file)
+ return -1;
+
+ while (!feof(file))
+ if (hist_entry__parse_objdump_line(self, file, head) < 0)
+ break;
+
+ pclose(file);
+ return 0;
+}
+
+void hists__inc_nr_events(struct hists *self, u32 type)
+{
+ ++self->stats.nr_events[0];
+ ++self->stats.nr_events[type];
+}
+
+size_t hists__fprintf_nr_events(struct hists *self, FILE *fp)
+{
+ int i;
+ size_t ret = 0;
+
+ for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) {
+ if (!event__name[i])
+ continue;
+ ret += fprintf(fp, "%10s events: %10d\n",
+ event__name[i], self->stats.nr_events[i]);
+ }
+
+ return ret;
+}
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h
index 9df1c340ec9..6f17dcd8412 100644
--- a/tools/perf/util/hist.h
+++ b/tools/perf/util/hist.h
@@ -6,37 +6,104 @@
extern struct callchain_param callchain_param;
-struct perf_session;
struct hist_entry;
struct addr_location;
struct symbol;
struct rb_root;
-void __perf_session__add_count(struct hist_entry *he,
- struct addr_location *al,
- u64 count);
-struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists,
- struct addr_location *al,
- struct symbol *parent,
- u64 count, bool *hit);
+struct objdump_line {
+ struct list_head node;
+ s64 offset;
+ char *line;
+};
+
+void objdump_line__free(struct objdump_line *self);
+struct objdump_line *objdump__get_next_ip_line(struct list_head *head,
+ struct objdump_line *pos);
+
+struct sym_hist {
+ u64 sum;
+ u64 ip[0];
+};
+
+struct sym_ext {
+ struct rb_node node;
+ double percent;
+ char *path;
+};
+
+struct sym_priv {
+ struct sym_hist *hist;
+ struct sym_ext *ext;
+};
+
+/*
+ * The kernel collects the number of events it couldn't send in a stretch and
+ * when possible sends this number in a PERF_RECORD_LOST event. The number of
+ * such "chunks" of lost events is stored in .nr_events[PERF_EVENT_LOST] while
+ * total_lost tells exactly how many events the kernel in fact lost, i.e. it is
+ * the sum of all struct lost_event.lost fields reported.
+ *
+ * The total_period is needed because by default auto-freq is used, so
+ * multipling nr_events[PERF_EVENT_SAMPLE] by a frequency isn't possible to get
+ * the total number of low level events, it is necessary to to sum all struct
+ * sample_event.period and stash the result in total_period.
+ */
+struct events_stats {
+ u64 total_period;
+ u64 total_lost;
+ u32 nr_events[PERF_RECORD_HEADER_MAX];
+ u32 nr_unknown_events;
+};
+
+struct hists {
+ struct rb_node rb_node;
+ struct rb_root entries;
+ u64 nr_entries;
+ struct events_stats stats;
+ u64 config;
+ u64 event_stream;
+ u32 type;
+ u32 max_sym_namelen;
+};
+
+struct hist_entry *__hists__add_entry(struct hists *self,
+ struct addr_location *al,
+ struct symbol *parent, u64 period);
extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *);
extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *);
-int hist_entry__fprintf(struct hist_entry *self,
- struct perf_session *pair_session,
- bool show_displacement,
- long displacement, FILE *fp,
- u64 session_total);
-int hist_entry__snprintf(struct hist_entry *self,
- char *bf, size_t size,
- struct perf_session *pair_session,
- bool show_displacement, long displacement,
- bool color, u64 session_total);
+int hist_entry__fprintf(struct hist_entry *self, struct hists *pair_hists,
+ bool show_displacement, long displacement, FILE *fp,
+ u64 total);
+int hist_entry__snprintf(struct hist_entry *self, char *bf, size_t size,
+ struct hists *pair_hists, bool show_displacement,
+ long displacement, bool color, u64 total);
void hist_entry__free(struct hist_entry *);
-u64 perf_session__output_resort(struct rb_root *hists, u64 total_samples);
-void perf_session__collapse_resort(struct rb_root *hists);
-size_t perf_session__fprintf_hists(struct rb_root *hists,
- struct perf_session *pair,
- bool show_displacement, FILE *fp,
- u64 session_total);
+void hists__output_resort(struct hists *self);
+void hists__collapse_resort(struct hists *self);
+
+void hists__inc_nr_events(struct hists *self, u32 type);
+size_t hists__fprintf_nr_events(struct hists *self, FILE *fp);
+
+size_t hists__fprintf(struct hists *self, struct hists *pair,
+ bool show_displacement, FILE *fp);
+
+int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip);
+int hist_entry__annotate(struct hist_entry *self, struct list_head *head);
+
+void hists__filter_by_dso(struct hists *self, const struct dso *dso);
+void hists__filter_by_thread(struct hists *self, const struct thread *thread);
+
+#ifdef NO_NEWT_SUPPORT
+static inline int hists__browse(struct hists *self __used,
+ const char *helpline __used,
+ const char *input_name __used)
+{
+ return 0;
+}
+#else
+int hists__browse(struct hists *self, const char *helpline,
+ const char *input_name);
+#endif
#endif /* __PERF_HIST_H */
diff --git a/tools/perf/util/hweight.c b/tools/perf/util/hweight.c
new file mode 100644
index 00000000000..5c1d0d099f0
--- /dev/null
+++ b/tools/perf/util/hweight.c
@@ -0,0 +1,31 @@
+#include <linux/bitops.h>
+
+/**
+ * hweightN - returns the hamming weight of a N-bit word
+ * @x: the word to weigh
+ *
+ * The Hamming Weight of a number is the total number of bits set in it.
+ */
+
+unsigned int hweight32(unsigned int w)
+{
+ unsigned int res = w - ((w >> 1) & 0x55555555);
+ res = (res & 0x33333333) + ((res >> 2) & 0x33333333);
+ res = (res + (res >> 4)) & 0x0F0F0F0F;
+ res = res + (res >> 8);
+ return (res + (res >> 16)) & 0x000000FF;
+}
+
+unsigned long hweight64(__u64 w)
+{
+#if BITS_PER_LONG == 32
+ return hweight32((unsigned int)(w >> 32)) + hweight32((unsigned int)w);
+#elif BITS_PER_LONG == 64
+ __u64 res = w - ((w >> 1) & 0x5555555555555555ul);
+ res = (res & 0x3333333333333333ul) + ((res >> 2) & 0x3333333333333333ul);
+ res = (res + (res >> 4)) & 0x0F0F0F0F0F0F0F0Ful;
+ res = res + (res >> 8);
+ res = res + (res >> 16);
+ return (res + (res >> 32)) & 0x00000000000000FFul;
+#endif
+}
diff --git a/tools/perf/util/include/asm/bitops.h b/tools/perf/util/include/asm/bitops.h
deleted file mode 100644
index 58e9817ffae..00000000000
--- a/tools/perf/util/include/asm/bitops.h
+++ /dev/null
@@ -1,18 +0,0 @@
-#ifndef _PERF_ASM_BITOPS_H_
-#define _PERF_ASM_BITOPS_H_
-
-#include <sys/types.h>
-#include "../../types.h"
-#include <linux/compiler.h>
-
-/* CHECKME: Not sure both always match */
-#define BITS_PER_LONG __WORDSIZE
-
-#include "../../../../include/asm-generic/bitops/__fls.h"
-#include "../../../../include/asm-generic/bitops/fls.h"
-#include "../../../../include/asm-generic/bitops/fls64.h"
-#include "../../../../include/asm-generic/bitops/__ffs.h"
-#include "../../../../include/asm-generic/bitops/ffz.h"
-#include "../../../../include/asm-generic/bitops/hweight.h"
-
-#endif
diff --git a/tools/perf/util/include/asm/hweight.h b/tools/perf/util/include/asm/hweight.h
new file mode 100644
index 00000000000..36cf26d434a
--- /dev/null
+++ b/tools/perf/util/include/asm/hweight.h
@@ -0,0 +1,8 @@
+#ifndef PERF_HWEIGHT_H
+#define PERF_HWEIGHT_H
+
+#include <linux/types.h>
+unsigned int hweight32(unsigned int w);
+unsigned long hweight64(__u64 w);
+
+#endif /* PERF_HWEIGHT_H */
diff --git a/tools/perf/util/include/dwarf-regs.h b/tools/perf/util/include/dwarf-regs.h
new file mode 100644
index 00000000000..cf6727e99c4
--- /dev/null
+++ b/tools/perf/util/include/dwarf-regs.h
@@ -0,0 +1,8 @@
+#ifndef _PERF_DWARF_REGS_H_
+#define _PERF_DWARF_REGS_H_
+
+#ifdef DWARF_SUPPORT
+const char *get_arch_regstr(unsigned int n);
+#endif
+
+#endif
diff --git a/tools/perf/util/include/linux/bitmap.h b/tools/perf/util/include/linux/bitmap.h
index 94507639a8c..eda4416efa0 100644
--- a/tools/perf/util/include/linux/bitmap.h
+++ b/tools/perf/util/include/linux/bitmap.h
@@ -1,3 +1,35 @@
-#include "../../../../include/linux/bitmap.h"
-#include "../../../../include/asm-generic/bitops/find.h"
-#include <linux/errno.h>
+#ifndef _PERF_BITOPS_H
+#define _PERF_BITOPS_H
+
+#include <string.h>
+#include <linux/bitops.h>
+
+int __bitmap_weight(const unsigned long *bitmap, int bits);
+
+#define BITMAP_LAST_WORD_MASK(nbits) \
+( \
+ ((nbits) % BITS_PER_LONG) ? \
+ (1UL<<((nbits) % BITS_PER_LONG))-1 : ~0UL \
+)
+
+#define small_const_nbits(nbits) \
+ (__builtin_constant_p(nbits) && (nbits) <= BITS_PER_LONG)
+
+static inline void bitmap_zero(unsigned long *dst, int nbits)
+{
+ if (small_const_nbits(nbits))
+ *dst = 0UL;
+ else {
+ int len = BITS_TO_LONGS(nbits) * sizeof(unsigned long);
+ memset(dst, 0, len);
+ }
+}
+
+static inline int bitmap_weight(const unsigned long *src, int nbits)
+{
+ if (small_const_nbits(nbits))
+ return hweight_long(*src & BITMAP_LAST_WORD_MASK(nbits));
+ return __bitmap_weight(src, nbits);
+}
+
+#endif /* _PERF_BITOPS_H */
diff --git a/tools/perf/util/include/linux/bitops.h b/tools/perf/util/include/linux/bitops.h
index 8d63116e943..bb4ac2e0538 100644
--- a/tools/perf/util/include/linux/bitops.h
+++ b/tools/perf/util/include/linux/bitops.h
@@ -1,13 +1,12 @@
#ifndef _PERF_LINUX_BITOPS_H_
#define _PERF_LINUX_BITOPS_H_
-#define __KERNEL__
+#include <linux/kernel.h>
+#include <asm/hweight.h>
-#define CONFIG_GENERIC_FIND_NEXT_BIT
-#define CONFIG_GENERIC_FIND_FIRST_BIT
-#include "../../../../include/linux/bitops.h"
-
-#undef __KERNEL__
+#define BITS_PER_LONG __WORDSIZE
+#define BITS_PER_BYTE 8
+#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))
static inline void set_bit(int nr, unsigned long *addr)
{
@@ -20,10 +19,9 @@ static __always_inline int test_bit(unsigned int nr, const unsigned long *addr)
(((unsigned long *)addr)[nr / BITS_PER_LONG])) != 0;
}
-unsigned long generic_find_next_zero_le_bit(const unsigned long *addr, unsigned
- long size, unsigned long offset);
-
-unsigned long generic_find_next_le_bit(const unsigned long *addr, unsigned
- long size, unsigned long offset);
+static inline unsigned long hweight_long(unsigned long w)
+{
+ return sizeof(w) == 4 ? hweight32(w) : hweight64(w);
+}
#endif
diff --git a/tools/perf/util/include/linux/kernel.h b/tools/perf/util/include/linux/kernel.h
index 388ab1bfd11..1eb804fd3fb 100644
--- a/tools/perf/util/include/linux/kernel.h
+++ b/tools/perf/util/include/linux/kernel.h
@@ -28,6 +28,8 @@
(type *)((char *)__mptr - offsetof(type, member)); })
#endif
+#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))
+
#ifndef max
#define max(x, y) ({ \
typeof(x) _max1 = (x); \
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
index 7facd016ec9..e672f2fef65 100644
--- a/tools/perf/util/map.c
+++ b/tools/perf/util/map.c
@@ -245,7 +245,7 @@ void map_groups__init(struct map_groups *self)
self->maps[i] = RB_ROOT;
INIT_LIST_HEAD(&self->removed_maps[i]);
}
- self->this_kerninfo = NULL;
+ self->machine = NULL;
}
void map_groups__flush(struct map_groups *self)
@@ -513,133 +513,116 @@ struct map *maps__find(struct rb_root *maps, u64 ip)
return NULL;
}
-struct kernel_info *add_new_kernel_info(struct rb_root *kerninfo_root,
- pid_t pid, const char *root_dir)
+int machine__init(struct machine *self, const char *root_dir, pid_t pid)
{
- struct rb_node **p = &kerninfo_root->rb_node;
+ map_groups__init(&self->kmaps);
+ RB_CLEAR_NODE(&self->rb_node);
+ INIT_LIST_HEAD(&self->user_dsos);
+ INIT_LIST_HEAD(&self->kernel_dsos);
+
+ self->kmaps.machine = self;
+ self->pid = pid;
+ self->root_dir = strdup(root_dir);
+ return self->root_dir == NULL ? -ENOMEM : 0;
+}
+
+struct machine *machines__add(struct rb_root *self, pid_t pid,
+ const char *root_dir)
+{
+ struct rb_node **p = &self->rb_node;
struct rb_node *parent = NULL;
- struct kernel_info *kerninfo, *pos;
+ struct machine *pos, *machine = malloc(sizeof(*machine));
- kerninfo = malloc(sizeof(struct kernel_info));
- if (!kerninfo)
+ if (!machine)
return NULL;
- kerninfo->pid = pid;
- map_groups__init(&kerninfo->kmaps);
- kerninfo->root_dir = strdup(root_dir);
- RB_CLEAR_NODE(&kerninfo->rb_node);
- INIT_LIST_HEAD(&kerninfo->dsos__user);
- INIT_LIST_HEAD(&kerninfo->dsos__kernel);
- kerninfo->kmaps.this_kerninfo = kerninfo;
+ if (machine__init(machine, root_dir, pid) != 0) {
+ free(machine);
+ return NULL;
+ }
while (*p != NULL) {
parent = *p;
- pos = rb_entry(parent, struct kernel_info, rb_node);
+ pos = rb_entry(parent, struct machine, rb_node);
if (pid < pos->pid)
p = &(*p)->rb_left;
else
p = &(*p)->rb_right;
}
- rb_link_node(&kerninfo->rb_node, parent, p);
- rb_insert_color(&kerninfo->rb_node, kerninfo_root);
-
- return kerninfo;
-}
-
-struct kernel_info *kerninfo__find(struct rb_root *kerninfo_root, pid_t pid)
-{
- struct rb_node **p = &kerninfo_root->rb_node;
- struct rb_node *parent = NULL;
- struct kernel_info *kerninfo;
- struct kernel_info *default_kerninfo = NULL;
-
- while (*p != NULL) {
- parent = *p;
- kerninfo = rb_entry(parent, struct kernel_info, rb_node);
- if (pid < kerninfo->pid)
- p = &(*p)->rb_left;
- else if (pid > kerninfo->pid)
- p = &(*p)->rb_right;
- else
- return kerninfo;
- if (!kerninfo->pid)
- default_kerninfo = kerninfo;
- }
+ rb_link_node(&machine->rb_node, parent, p);
+ rb_insert_color(&machine->rb_node, self);
- return default_kerninfo;
+ return machine;
}
-struct kernel_info *kerninfo__findhost(struct rb_root *kerninfo_root)
+struct machine *machines__find(struct rb_root *self, pid_t pid)
{
- struct rb_node **p = &kerninfo_root->rb_node;
+ struct rb_node **p = &self->rb_node;
struct rb_node *parent = NULL;
- struct kernel_info *kerninfo;
- pid_t pid = HOST_KERNEL_ID;
+ struct machine *machine;
+ struct machine *default_machine = NULL;
while (*p != NULL) {
parent = *p;
- kerninfo = rb_entry(parent, struct kernel_info, rb_node);
- if (pid < kerninfo->pid)
+ machine = rb_entry(parent, struct machine, rb_node);
+ if (pid < machine->pid)
p = &(*p)->rb_left;
- else if (pid > kerninfo->pid)
+ else if (pid > machine->pid)
p = &(*p)->rb_right;
else
- return kerninfo;
+ return machine;
+ if (!machine->pid)
+ default_machine = machine;
}
- return NULL;
+ return default_machine;
}
-struct kernel_info *kerninfo__findnew(struct rb_root *kerninfo_root, pid_t pid)
+struct machine *machines__findnew(struct rb_root *self, pid_t pid)
{
char path[PATH_MAX];
const char *root_dir;
- int ret;
- struct kernel_info *kerninfo = kerninfo__find(kerninfo_root, pid);
+ struct machine *machine = machines__find(self, pid);
- if (!kerninfo || kerninfo->pid != pid) {
+ if (!machine || machine->pid != pid) {
if (pid == HOST_KERNEL_ID || pid == DEFAULT_GUEST_KERNEL_ID)
root_dir = "";
else {
if (!symbol_conf.guestmount)
goto out;
sprintf(path, "%s/%d", symbol_conf.guestmount, pid);
- ret = access(path, R_OK);
- if (ret) {
+ if (access(path, R_OK)) {
pr_err("Can't access file %s\n", path);
goto out;
}
root_dir = path;
}
- kerninfo = add_new_kernel_info(kerninfo_root, pid, root_dir);
+ machine = machines__add(self, pid, root_dir);
}
out:
- return kerninfo;
+ return machine;
}
-void kerninfo__process_allkernels(struct rb_root *kerninfo_root,
- process_kernel_info process,
- void *data)
+void machines__process(struct rb_root *self, machine__process_t process, void *data)
{
struct rb_node *nd;
- for (nd = rb_first(kerninfo_root); nd; nd = rb_next(nd)) {
- struct kernel_info *pos = rb_entry(nd, struct kernel_info,
- rb_node);
+ for (nd = rb_first(self); nd; nd = rb_next(nd)) {
+ struct machine *pos = rb_entry(nd, struct machine, rb_node);
process(pos, data);
}
}
-char *kern_mmap_name(struct kernel_info *kerninfo, char *buff)
+char *machine__mmap_name(struct machine *self, char *bf, size_t size)
{
- if (is_host_kernel(kerninfo))
- sprintf(buff, "[%s]", "kernel.kallsyms");
- else if (is_default_guest(kerninfo))
- sprintf(buff, "[%s]", "guest.kernel.kallsyms");
+ if (machine__is_host(self))
+ snprintf(bf, size, "[%s]", "kernel.kallsyms");
+ else if (machine__is_default_guest(self))
+ snprintf(bf, size, "[%s]", "guest.kernel.kallsyms");
else
- sprintf(buff, "[%s.%d]", "guest.kernel.kallsyms", kerninfo->pid);
+ snprintf(bf, size, "[%s.%d]", "guest.kernel.kallsyms", self->pid);
- return buff;
+ return bf;
}
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h
index 30d38d634e0..f3913451282 100644
--- a/tools/perf/util/map.h
+++ b/tools/perf/util/map.h
@@ -5,6 +5,7 @@
#include <linux/list.h>
#include <linux/rbtree.h>
#include <stdio.h>
+#include <stdbool.h>
#include "types.h"
enum map_type {
@@ -19,7 +20,7 @@ extern const char *map_type__name[MAP__NR_TYPES];
struct dso;
struct ref_reloc_sym;
struct map_groups;
-struct kernel_info;
+struct machine;
struct map {
union {
@@ -29,6 +30,7 @@ struct map {
u64 start;
u64 end;
enum map_type type;
+ u32 priv;
u64 pgoff;
/* ip -> dso rip */
@@ -46,25 +48,31 @@ struct kmap {
};
struct map_groups {
- struct rb_root maps[MAP__NR_TYPES];
- struct list_head removed_maps[MAP__NR_TYPES];
- struct kernel_info *this_kerninfo;
+ struct rb_root maps[MAP__NR_TYPES];
+ struct list_head removed_maps[MAP__NR_TYPES];
+ struct machine *machine;
};
-/* Native host kernel uses -1 as pid index in kernel_info */
+/* Native host kernel uses -1 as pid index in machine */
#define HOST_KERNEL_ID (-1)
#define DEFAULT_GUEST_KERNEL_ID (0)
-struct kernel_info {
- struct rb_node rb_node;
- pid_t pid;
- char *root_dir;
- struct list_head dsos__user;
- struct list_head dsos__kernel;
+struct machine {
+ struct rb_node rb_node;
+ pid_t pid;
+ char *root_dir;
+ struct list_head user_dsos;
+ struct list_head kernel_dsos;
struct map_groups kmaps;
- struct map *vmlinux_maps[MAP__NR_TYPES];
+ struct map *vmlinux_maps[MAP__NR_TYPES];
};
+static inline
+struct map *machine__kernel_map(struct machine *self, enum map_type type)
+{
+ return self->vmlinux_maps[type];
+}
+
static inline struct kmap *map__kmap(struct map *self)
{
return (struct kmap *)(self + 1);
@@ -124,36 +132,31 @@ int map_groups__clone(struct map_groups *self,
size_t map_groups__fprintf(struct map_groups *self, int verbose, FILE *fp);
size_t map_groups__fprintf_maps(struct map_groups *self, int verbose, FILE *fp);
-struct kernel_info *add_new_kernel_info(struct rb_root *kerninfo_root,
- pid_t pid, const char *root_dir);
-struct kernel_info *kerninfo__find(struct rb_root *kerninfo_root, pid_t pid);
-struct kernel_info *kerninfo__findnew(struct rb_root *kerninfo_root, pid_t pid);
-struct kernel_info *kerninfo__findhost(struct rb_root *kerninfo_root);
-char *kern_mmap_name(struct kernel_info *kerninfo, char *buff);
+typedef void (*machine__process_t)(struct machine *self, void *data);
+
+void machines__process(struct rb_root *self, machine__process_t process, void *data);
+struct machine *machines__add(struct rb_root *self, pid_t pid,
+ const char *root_dir);
+struct machine *machines__find_host(struct rb_root *self);
+struct machine *machines__find(struct rb_root *self, pid_t pid);
+struct machine *machines__findnew(struct rb_root *self, pid_t pid);
+char *machine__mmap_name(struct machine *self, char *bf, size_t size);
+int machine__init(struct machine *self, const char *root_dir, pid_t pid);
/*
* Default guest kernel is defined by parameter --guestkallsyms
* and --guestmodules
*/
-static inline int is_default_guest(struct kernel_info *kerninfo)
+static inline bool machine__is_default_guest(struct machine *self)
{
- if (!kerninfo)
- return 0;
- return kerninfo->pid == DEFAULT_GUEST_KERNEL_ID;
+ return self ? self->pid == DEFAULT_GUEST_KERNEL_ID : false;
}
-static inline int is_host_kernel(struct kernel_info *kerninfo)
+static inline bool machine__is_host(struct machine *self)
{
- if (!kerninfo)
- return 0;
- return kerninfo->pid == HOST_KERNEL_ID;
+ return self ? self->pid == HOST_KERNEL_ID : false;
}
-typedef void (*process_kernel_info)(struct kernel_info *kerninfo, void *data);
-void kerninfo__process_allkernels(struct rb_root *kerninfo_root,
- process_kernel_info process,
- void *data);
-
static inline void map_groups__insert(struct map_groups *self, struct map *map)
{
maps__insert(&self->maps[map->type], map);
@@ -178,10 +181,20 @@ struct symbol *map_groups__find_symbol_by_name(struct map_groups *self,
symbol_filter_t filter);
static inline
-struct symbol *map_groups__find_function(struct map_groups *self, u64 addr,
- struct map **mapp, symbol_filter_t filter)
+struct symbol *machine__find_kernel_symbol(struct machine *self,
+ enum map_type type, u64 addr,
+ struct map **mapp,
+ symbol_filter_t filter)
+{
+ return map_groups__find_symbol(&self->kmaps, type, addr, mapp, filter);
+}
+
+static inline
+struct symbol *machine__find_kernel_function(struct machine *self, u64 addr,
+ struct map **mapp,
+ symbol_filter_t filter)
{
- return map_groups__find_symbol(self, MAP__FUNCTION, addr, mapp, filter);
+ return machine__find_kernel_symbol(self, MAP__FUNCTION, addr, mapp, filter);
}
static inline
@@ -197,10 +210,7 @@ int map_groups__fixup_overlappings(struct map_groups *self, struct map *map,
struct map *map_groups__find_by_name(struct map_groups *self,
enum map_type type, const char *name);
-struct map *map_groups__new_module(struct map_groups *self,
- u64 start,
- const char *filename,
- struct kernel_info *kerninfo);
+struct map *machine__new_module(struct machine *self, u64 start, const char *filename);
void map_groups__flush(struct map_groups *self);
diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c
index 7a123a94e3f..ccb7c5bb269 100644
--- a/tools/perf/util/newt.c
+++ b/tools/perf/util/newt.c
@@ -2,16 +2,29 @@
#include <stdio.h>
#undef _GNU_SOURCE
+#include <slang.h>
#include <stdlib.h>
#include <newt.h>
#include <sys/ttydefaults.h>
#include "cache.h"
#include "hist.h"
+#include "pstack.h"
#include "session.h"
#include "sort.h"
#include "symbol.h"
+#if SLANG_VERSION < 20104
+#define slsmg_printf(msg, args...) SLsmg_printf((char *)msg, ##args)
+#define slsmg_write_nstring(msg, len) SLsmg_write_nstring((char *)msg, len)
+#define sltt_set_color(obj, name, fg, bg) SLtt_set_color(obj,(char *)name,\
+ (char *)fg, (char *)bg)
+#else
+#define slsmg_printf SLsmg_printf
+#define slsmg_write_nstring SLsmg_write_nstring
+#define sltt_set_color SLtt_set_color
+#endif
+
struct ui_progress {
newtComponent form, scale;
};
@@ -31,7 +44,7 @@ struct ui_progress *ui_progress__new(const char *title, u64 total)
self->scale = newtScale(0, 0, cols, total);
if (self->scale == NULL)
goto out_free_form;
- newtFormAddComponents(self->form, self->scale, NULL);
+ newtFormAddComponent(self->form, self->scale);
newtRefresh();
}
@@ -57,6 +70,43 @@ void ui_progress__delete(struct ui_progress *self)
free(self);
}
+static void ui_helpline__pop(void)
+{
+ newtPopHelpLine();
+}
+
+static void ui_helpline__push(const char *msg)
+{
+ newtPushHelpLine(msg);
+}
+
+static void ui_helpline__vpush(const char *fmt, va_list ap)
+{
+ char *s;
+
+ if (vasprintf(&s, fmt, ap) < 0)
+ vfprintf(stderr, fmt, ap);
+ else {
+ ui_helpline__push(s);
+ free(s);
+ }
+}
+
+static void ui_helpline__fpush(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ ui_helpline__vpush(fmt, ap);
+ va_end(ap);
+}
+
+static void ui_helpline__puts(const char *msg)
+{
+ ui_helpline__pop();
+ ui_helpline__push(msg);
+}
+
static char browser__last_msg[1024];
int browser__show_help(const char *format, va_list ap)
@@ -69,8 +119,7 @@ int browser__show_help(const char *format, va_list ap)
backlog += ret;
if (browser__last_msg[backlog - 1] == '\n') {
- newtPopHelpLine();
- newtPushHelpLine(browser__last_msg);
+ ui_helpline__puts(browser__last_msg);
newtRefresh();
backlog = 0;
}
@@ -80,6 +129,7 @@ int browser__show_help(const char *format, va_list ap)
static void newt_form__set_exit_keys(newtComponent self)
{
+ newtFormAddHotKey(self, NEWT_KEY_LEFT);
newtFormAddHotKey(self, NEWT_KEY_ESCAPE);
newtFormAddHotKey(self, 'Q');
newtFormAddHotKey(self, 'q');
@@ -107,7 +157,7 @@ static int popup_menu(int argc, char * const argv[])
if (listbox == NULL)
goto out_destroy_form;
- newtFormAddComponents(form, listbox, NULL);
+ newtFormAddComponent(form, listbox);
for (i = 0; i < argc; ++i) {
int len = strlen(argv[i]);
@@ -128,6 +178,48 @@ out_destroy_form:
return rc;
}
+static int ui__help_window(const char *text)
+{
+ struct newtExitStruct es;
+ newtComponent tb, form = newt_form__new();
+ int rc = -1;
+ int max_len = 0, nr_lines = 0;
+ const char *t;
+
+ if (form == NULL)
+ return -1;
+
+ t = text;
+ while (1) {
+ const char *sep = strchr(t, '\n');
+ int len;
+
+ if (sep == NULL)
+ sep = strchr(t, '\0');
+ len = sep - t;
+ if (max_len < len)
+ max_len = len;
+ ++nr_lines;
+ if (*sep == '\0')
+ break;
+ t = sep + 1;
+ }
+
+ tb = newtTextbox(0, 0, max_len, nr_lines, 0);
+ if (tb == NULL)
+ goto out_destroy_form;
+
+ newtTextboxSetText(tb, text);
+ newtFormAddComponent(form, tb);
+ newtCenteredWindow(max_len, nr_lines, NULL);
+ newtFormRun(form, &es);
+ newtPopWindow();
+ rc = 0;
+out_destroy_form:
+ newtFormDestroy(form);
+ return rc;
+}
+
static bool dialog_yesno(const char *msg)
{
/* newtWinChoice should really be accepting const char pointers... */
@@ -135,6 +227,255 @@ static bool dialog_yesno(const char *msg)
return newtWinChoice(NULL, yes, no, (char *)msg) == 1;
}
+#define HE_COLORSET_TOP 50
+#define HE_COLORSET_MEDIUM 51
+#define HE_COLORSET_NORMAL 52
+#define HE_COLORSET_SELECTED 53
+#define HE_COLORSET_CODE 54
+
+static int ui_browser__percent_color(double percent, bool current)
+{
+ if (current)
+ return HE_COLORSET_SELECTED;
+ if (percent >= MIN_RED)
+ return HE_COLORSET_TOP;
+ if (percent >= MIN_GREEN)
+ return HE_COLORSET_MEDIUM;
+ return HE_COLORSET_NORMAL;
+}
+
+struct ui_browser {
+ newtComponent form, sb;
+ u64 index, first_visible_entry_idx;
+ void *first_visible_entry, *entries;
+ u16 top, left, width, height;
+ void *priv;
+ u32 nr_entries;
+};
+
+static void ui_browser__refresh_dimensions(struct ui_browser *self)
+{
+ int cols, rows;
+ newtGetScreenSize(&cols, &rows);
+
+ if (self->width > cols - 4)
+ self->width = cols - 4;
+ self->height = rows - 5;
+ if (self->height > self->nr_entries)
+ self->height = self->nr_entries;
+ self->top = (rows - self->height) / 2;
+ self->left = (cols - self->width) / 2;
+}
+
+static void ui_browser__reset_index(struct ui_browser *self)
+{
+ self->index = self->first_visible_entry_idx = 0;
+ self->first_visible_entry = NULL;
+}
+
+static int objdump_line__show(struct objdump_line *self, struct list_head *head,
+ int width, struct hist_entry *he, int len,
+ bool current_entry)
+{
+ if (self->offset != -1) {
+ struct symbol *sym = he->ms.sym;
+ unsigned int hits = 0;
+ double percent = 0.0;
+ int color;
+ struct sym_priv *priv = symbol__priv(sym);
+ struct sym_ext *sym_ext = priv->ext;
+ struct sym_hist *h = priv->hist;
+ s64 offset = self->offset;
+ struct objdump_line *next = objdump__get_next_ip_line(head, self);
+
+ while (offset < (s64)len &&
+ (next == NULL || offset < next->offset)) {
+ if (sym_ext) {
+ percent += sym_ext[offset].percent;
+ } else
+ hits += h->ip[offset];
+
+ ++offset;
+ }
+
+ if (sym_ext == NULL && h->sum)
+ percent = 100.0 * hits / h->sum;
+
+ color = ui_browser__percent_color(percent, current_entry);
+ SLsmg_set_color(color);
+ slsmg_printf(" %7.2f ", percent);
+ if (!current_entry)
+ SLsmg_set_color(HE_COLORSET_CODE);
+ } else {
+ int color = ui_browser__percent_color(0, current_entry);
+ SLsmg_set_color(color);
+ slsmg_write_nstring(" ", 9);
+ }
+
+ SLsmg_write_char(':');
+ slsmg_write_nstring(" ", 8);
+ if (!*self->line)
+ slsmg_write_nstring(" ", width - 18);
+ else
+ slsmg_write_nstring(self->line, width - 18);
+
+ return 0;
+}
+
+static int ui_browser__refresh_entries(struct ui_browser *self)
+{
+ struct objdump_line *pos;
+ struct list_head *head = self->entries;
+ struct hist_entry *he = self->priv;
+ int row = 0;
+ int len = he->ms.sym->end - he->ms.sym->start;
+
+ if (self->first_visible_entry == NULL || self->first_visible_entry == self->entries)
+ self->first_visible_entry = head->next;
+
+ pos = list_entry(self->first_visible_entry, struct objdump_line, node);
+
+ list_for_each_entry_from(pos, head, node) {
+ bool current_entry = (self->first_visible_entry_idx + row) == self->index;
+ SLsmg_gotorc(self->top + row, self->left);
+ objdump_line__show(pos, head, self->width,
+ he, len, current_entry);
+ if (++row == self->height)
+ break;
+ }
+
+ SLsmg_set_color(HE_COLORSET_NORMAL);
+ SLsmg_fill_region(self->top + row, self->left,
+ self->height - row, self->width, ' ');
+
+ return 0;
+}
+
+static int ui_browser__run(struct ui_browser *self, const char *title,
+ struct newtExitStruct *es)
+{
+ if (self->form) {
+ newtFormDestroy(self->form);
+ newtPopWindow();
+ }
+
+ ui_browser__refresh_dimensions(self);
+ newtCenteredWindow(self->width + 2, self->height, title);
+ self->form = newt_form__new();
+ if (self->form == NULL)
+ return -1;
+
+ self->sb = newtVerticalScrollbar(self->width + 1, 0, self->height,
+ HE_COLORSET_NORMAL,
+ HE_COLORSET_SELECTED);
+ if (self->sb == NULL)
+ return -1;
+
+ newtFormAddHotKey(self->form, NEWT_KEY_UP);
+ newtFormAddHotKey(self->form, NEWT_KEY_DOWN);
+ newtFormAddHotKey(self->form, NEWT_KEY_PGUP);
+ newtFormAddHotKey(self->form, NEWT_KEY_PGDN);
+ newtFormAddHotKey(self->form, NEWT_KEY_HOME);
+ newtFormAddHotKey(self->form, NEWT_KEY_END);
+
+ if (ui_browser__refresh_entries(self) < 0)
+ return -1;
+ newtFormAddComponent(self->form, self->sb);
+
+ while (1) {
+ unsigned int offset;
+
+ newtFormRun(self->form, es);
+
+ if (es->reason != NEWT_EXIT_HOTKEY)
+ break;
+ switch (es->u.key) {
+ case NEWT_KEY_DOWN:
+ if (self->index == self->nr_entries - 1)
+ break;
+ ++self->index;
+ if (self->index == self->first_visible_entry_idx + self->height) {
+ struct list_head *pos = self->first_visible_entry;
+ ++self->first_visible_entry_idx;
+ self->first_visible_entry = pos->next;
+ }
+ break;
+ case NEWT_KEY_UP:
+ if (self->index == 0)
+ break;
+ --self->index;
+ if (self->index < self->first_visible_entry_idx) {
+ struct list_head *pos = self->first_visible_entry;
+ --self->first_visible_entry_idx;
+ self->first_visible_entry = pos->prev;
+ }
+ break;
+ case NEWT_KEY_PGDN:
+ if (self->first_visible_entry_idx + self->height > self->nr_entries - 1)
+ break;
+
+ offset = self->height;
+ if (self->index + offset > self->nr_entries - 1)
+ offset = self->nr_entries - 1 - self->index;
+ self->index += offset;
+ self->first_visible_entry_idx += offset;
+
+ while (offset--) {
+ struct list_head *pos = self->first_visible_entry;
+ self->first_visible_entry = pos->next;
+ }
+
+ break;
+ case NEWT_KEY_PGUP:
+ if (self->first_visible_entry_idx == 0)
+ break;
+
+ if (self->first_visible_entry_idx < self->height)
+ offset = self->first_visible_entry_idx;
+ else
+ offset = self->height;
+
+ self->index -= offset;
+ self->first_visible_entry_idx -= offset;
+
+ while (offset--) {
+ struct list_head *pos = self->first_visible_entry;
+ self->first_visible_entry = pos->prev;
+ }
+ break;
+ case NEWT_KEY_HOME:
+ ui_browser__reset_index(self);
+ break;
+ case NEWT_KEY_END: {
+ struct list_head *head = self->entries;
+ offset = self->height - 1;
+
+ if (offset > self->nr_entries)
+ offset = self->nr_entries;
+
+ self->index = self->first_visible_entry_idx = self->nr_entries - 1 - offset;
+ self->first_visible_entry = head->prev;
+ while (offset-- != 0) {
+ struct list_head *pos = self->first_visible_entry;
+ self->first_visible_entry = pos->prev;
+ }
+ }
+ break;
+ case NEWT_KEY_ESCAPE:
+ case NEWT_KEY_LEFT:
+ case CTRL('c'):
+ case 'Q':
+ case 'q':
+ return 0;
+ default:
+ continue;
+ }
+ if (ui_browser__refresh_entries(self) < 0)
+ return -1;
+ }
+ return 0;
+}
+
/*
* When debugging newt problems it was useful to be able to "unroll"
* the calls to newtCheckBoxTreeAdd{Array,Item}, so that we can generate
@@ -317,62 +658,40 @@ static size_t hist_entry__append_browser(struct hist_entry *self,
return ret;
}
-static void map_symbol__annotate_browser(const struct map_symbol *self,
- const char *input_name)
+static void hist_entry__annotate_browser(struct hist_entry *self)
{
- FILE *fp;
- int cols, rows;
- newtComponent form, tree;
+ struct ui_browser browser;
struct newtExitStruct es;
- char *str;
- size_t line_len, max_line_len = 0;
- size_t max_usable_width;
- char *line = NULL;
+ struct objdump_line *pos, *n;
+ LIST_HEAD(head);
- if (self->sym == NULL)
+ if (self->ms.sym == NULL)
return;
- if (asprintf(&str, "perf annotate -i \"%s\" -d \"%s\" %s 2>&1 | expand",
- input_name, self->map->dso->name, self->sym->name) < 0)
+ if (hist_entry__annotate(self, &head) < 0)
return;
- fp = popen(str, "r");
- if (fp == NULL)
- goto out_free_str;
-
- newtPushHelpLine("Press ESC to exit");
- newtGetScreenSize(&cols, &rows);
- tree = newtListbox(0, 0, rows - 5, NEWT_FLAG_SCROLL);
-
- while (!feof(fp)) {
- if (getline(&line, &line_len, fp) < 0 || !line_len)
- break;
- while (line_len != 0 && isspace(line[line_len - 1]))
- line[--line_len] = '\0';
+ ui_helpline__push("Press <- or ESC to exit");
- if (line_len > max_line_len)
- max_line_len = line_len;
- newtListboxAppendEntry(tree, line, NULL);
+ memset(&browser, 0, sizeof(browser));
+ browser.entries = &head;
+ browser.priv = self;
+ list_for_each_entry(pos, &head, node) {
+ size_t line_len = strlen(pos->line);
+ if (browser.width < line_len)
+ browser.width = line_len;
+ ++browser.nr_entries;
}
- fclose(fp);
- free(line);
-
- max_usable_width = cols - 22;
- if (max_line_len > max_usable_width)
- max_line_len = max_usable_width;
- newtListboxSetWidth(tree, max_line_len);
-
- newtCenteredWindow(max_line_len + 2, rows - 5, self->sym->name);
- form = newt_form__new();
- newtFormAddComponents(form, tree, NULL);
-
- newtFormRun(form, &es);
- newtFormDestroy(form);
+ browser.width += 18; /* Percentage */
+ ui_browser__run(&browser, self->ms.sym->name, &es);
+ newtFormDestroy(browser.form);
newtPopWindow();
- newtPopHelpLine();
-out_free_str:
- free(str);
+ list_for_each_entry_safe(pos, n, &head, node) {
+ list_del(&pos->node);
+ objdump_line__free(pos);
+ }
+ ui_helpline__pop();
}
static const void *newt__symbol_tree_get_current(newtComponent self)
@@ -410,23 +729,25 @@ static void hist_browser__delete(struct hist_browser *self)
free(self);
}
-static int hist_browser__populate(struct hist_browser *self, struct rb_root *hists,
- u64 nr_hists, u64 session_total, const char *title)
+static int hist_browser__populate(struct hist_browser *self, struct hists *hists,
+ const char *title)
{
int max_len = 0, idx, cols, rows;
struct ui_progress *progress;
struct rb_node *nd;
u64 curr_hist = 0;
- char seq[] = ".";
+ char seq[] = ".", unit;
char str[256];
+ unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
if (self->form) {
newtFormDestroy(self->form);
newtPopWindow();
}
- snprintf(str, sizeof(str), "Samples: %Ld ",
- session_total);
+ nr_events = convert_unit(nr_events, &unit);
+ snprintf(str, sizeof(str), "Events: %lu%c ",
+ nr_events, unit);
newtDrawRootText(0, 0, str);
newtGetScreenSize(NULL, &rows);
@@ -442,24 +763,25 @@ static int hist_browser__populate(struct hist_browser *self, struct rb_root *his
newtComponentAddCallback(self->tree, hist_browser__selection,
&self->selection);
- progress = ui_progress__new("Adding entries to the browser...", nr_hists);
+ progress = ui_progress__new("Adding entries to the browser...",
+ hists->nr_entries);
if (progress == NULL)
return -1;
idx = 0;
- for (nd = rb_first(hists); nd; nd = rb_next(nd)) {
+ for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
int len;
if (h->filtered)
continue;
- len = hist_entry__append_browser(h, self->tree, session_total);
+ len = hist_entry__append_browser(h, self->tree, hists->stats.total_period);
if (len > max_len)
max_len = len;
if (symbol_conf.use_callchain)
hist_entry__append_callchain_browser(h, self->tree,
- session_total, idx++);
+ hists->stats.total_period, idx++);
++curr_hist;
if (curr_hist % 5)
ui_progress__update(progress, curr_hist);
@@ -483,6 +805,14 @@ static int hist_browser__populate(struct hist_browser *self, struct rb_root *his
newtFormAddHotKey(self->form, 'A');
newtFormAddHotKey(self->form, 'a');
+ newtFormAddHotKey(self->form, 'D');
+ newtFormAddHotKey(self->form, 'd');
+ newtFormAddHotKey(self->form, 'T');
+ newtFormAddHotKey(self->form, 't');
+ newtFormAddHotKey(self->form, '?');
+ newtFormAddHotKey(self->form, 'H');
+ newtFormAddHotKey(self->form, 'h');
+ newtFormAddHotKey(self->form, NEWT_KEY_F1);
newtFormAddHotKey(self->form, NEWT_KEY_RIGHT);
newtFormAddComponents(self->form, self->tree, NULL);
self->selection = newt__symbol_tree_get_current(self->tree);
@@ -490,58 +820,7 @@ static int hist_browser__populate(struct hist_browser *self, struct rb_root *his
return 0;
}
-enum hist_filter {
- HIST_FILTER__DSO,
- HIST_FILTER__THREAD,
-};
-
-static u64 hists__filter_by_dso(struct rb_root *hists, const struct dso *dso,
- u64 *session_total)
-{
- struct rb_node *nd;
- u64 nr_hists = 0;
-
- *session_total = 0;
-
- for (nd = rb_first(hists); nd; nd = rb_next(nd)) {
- struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
-
- if (dso != NULL && (h->ms.map == NULL || h->ms.map->dso != dso)) {
- h->filtered |= (1 << HIST_FILTER__DSO);
- continue;
- }
- h->filtered &= ~(1 << HIST_FILTER__DSO);
- ++nr_hists;
- *session_total += h->count;
- }
-
- return nr_hists;
-}
-
-static u64 hists__filter_by_thread(struct rb_root *hists, const struct thread *thread,
- u64 *session_total)
-{
- struct rb_node *nd;
- u64 nr_hists = 0;
-
- *session_total = 0;
-
- for (nd = rb_first(hists); nd; nd = rb_next(nd)) {
- struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
-
- if (thread != NULL && h->thread != thread) {
- h->filtered |= (1 << HIST_FILTER__THREAD);
- continue;
- }
- h->filtered &= ~(1 << HIST_FILTER__THREAD);
- ++nr_hists;
- *session_total += h->count;
- }
-
- return nr_hists;
-}
-
-static struct thread *hist_browser__selected_thread(struct hist_browser *self)
+static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self)
{
int *indexes;
@@ -557,7 +836,13 @@ static struct thread *hist_browser__selected_thread(struct hist_browser *self)
}
return NULL;
out:
- return *(struct thread **)(self->selection + 1);
+ return container_of(self->selection, struct hist_entry, ms);
+}
+
+static struct thread *hist_browser__selected_thread(struct hist_browser *self)
+{
+ struct hist_entry *he = hist_browser__selected_entry(self);
+ return he ? he->thread : NULL;
}
static int hist_browser__title(char *bf, size_t size, const char *input_name,
@@ -577,11 +862,10 @@ static int hist_browser__title(char *bf, size_t size, const char *input_name,
return printed ?: snprintf(bf, size, "Report: %s", input_name);
}
-int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists,
- u64 session_total, const char *helpline,
- const char *input_name)
+int hists__browse(struct hists *self, const char *helpline, const char *input_name)
{
struct hist_browser *browser = hist_browser__new();
+ struct pstack *fstack = pstack__new(2);
const struct thread *thread_filter = NULL;
const struct dso *dso_filter = NULL;
struct newtExitStruct es;
@@ -591,12 +875,16 @@ int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists,
if (browser == NULL)
return -1;
- newtPushHelpLine(helpline);
+ fstack = pstack__new(2);
+ if (fstack == NULL)
+ goto out;
+
+ ui_helpline__push(helpline);
hist_browser__title(msg, sizeof(msg), input_name,
dso_filter, thread_filter);
- if (hist_browser__populate(browser, hists, nr_hists, session_total, msg) < 0)
- goto out;
+ if (hist_browser__populate(browser, self, msg) < 0)
+ goto out_free_stack;
while (1) {
const struct thread *thread;
@@ -606,17 +894,56 @@ int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists,
annotate = -2, zoom_dso = -2, zoom_thread = -2;
newtFormRun(browser->form, &es);
+
+ thread = hist_browser__selected_thread(browser);
+ dso = browser->selection->map ? browser->selection->map->dso : NULL;
+
if (es.reason == NEWT_EXIT_HOTKEY) {
- if (toupper(es.u.key) == 'A')
+ if (es.u.key == NEWT_KEY_F1)
+ goto do_help;
+
+ switch (toupper(es.u.key)) {
+ case 'A':
goto do_annotate;
- if (es.u.key == NEWT_KEY_ESCAPE ||
- toupper(es.u.key) == 'Q' ||
- es.u.key == CTRL('c')) {
+ case 'D':
+ goto zoom_dso;
+ case 'T':
+ goto zoom_thread;
+ case 'H':
+ case '?':
+do_help:
+ ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n"
+ "<- Zoom out\n"
+ "a Annotate current symbol\n"
+ "h/?/F1 Show this window\n"
+ "d Zoom into current DSO\n"
+ "t Zoom into current Thread\n"
+ "q/CTRL+C Exit browser");
+ continue;
+ default:;
+ }
+ if (toupper(es.u.key) == 'Q' ||
+ es.u.key == CTRL('c'))
+ break;
+ if (es.u.key == NEWT_KEY_ESCAPE) {
if (dialog_yesno("Do you really want to exit?"))
break;
else
continue;
}
+
+ if (es.u.key == NEWT_KEY_LEFT) {
+ const void *top;
+
+ if (pstack__empty(fstack))
+ continue;
+ top = pstack__pop(fstack);
+ if (top == &dso_filter)
+ goto zoom_out_dso;
+ if (top == &thread_filter)
+ goto zoom_out_thread;
+ continue;
+ }
}
if (browser->selection->sym != NULL &&
@@ -624,7 +951,6 @@ int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists,
browser->selection->sym->name) > 0)
annotate = nr_options++;
- thread = hist_browser__selected_thread(browser);
if (thread != NULL &&
asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
(thread_filter ? "out of" : "into"),
@@ -632,7 +958,6 @@ int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists,
thread->pid) > 0)
zoom_thread = nr_options++;
- dso = browser->selection->map ? browser->selection->map->dso : NULL;
if (dso != NULL &&
asprintf(&options[nr_options], "Zoom %s %s DSO",
(dso_filter ? "out of" : "into"),
@@ -651,66 +976,100 @@ int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists,
if (choice == -1)
continue;
-do_annotate:
+
if (choice == annotate) {
+ struct hist_entry *he;
+do_annotate:
if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) {
- newtPopHelpLine();
- newtPushHelpLine("No vmlinux file found, can't "
+ ui_helpline__puts("No vmlinux file found, can't "
"annotate with just a "
"kallsyms file");
continue;
}
- map_symbol__annotate_browser(browser->selection, input_name);
+
+ he = hist_browser__selected_entry(browser);
+ if (he == NULL)
+ continue;
+
+ hist_entry__annotate_browser(he);
} else if (choice == zoom_dso) {
+zoom_dso:
if (dso_filter) {
- newtPopHelpLine();
+ pstack__remove(fstack, &dso_filter);
+zoom_out_dso:
+ ui_helpline__pop();
dso_filter = NULL;
} else {
- snprintf(msg, sizeof(msg),
- "To zoom out press -> + \"Zoom out of %s DSO\"",
- dso->kernel ? "the Kernel" : dso->short_name);
- newtPushHelpLine(msg);
+ if (dso == NULL)
+ continue;
+ ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
+ dso->kernel ? "the Kernel" : dso->short_name);
dso_filter = dso;
+ pstack__push(fstack, &dso_filter);
}
- nr_hists = hists__filter_by_dso(hists, dso_filter, &session_total);
+ hists__filter_by_dso(self, dso_filter);
hist_browser__title(msg, sizeof(msg), input_name,
dso_filter, thread_filter);
- if (hist_browser__populate(browser, hists, nr_hists, session_total, msg) < 0)
+ if (hist_browser__populate(browser, self, msg) < 0)
goto out;
} else if (choice == zoom_thread) {
+zoom_thread:
if (thread_filter) {
- newtPopHelpLine();
+ pstack__remove(fstack, &thread_filter);
+zoom_out_thread:
+ ui_helpline__pop();
thread_filter = NULL;
} else {
- snprintf(msg, sizeof(msg),
- "To zoom out press -> + \"Zoom out of %s(%d) thread\"",
- (thread->comm_set ? thread->comm : ""),
- thread->pid);
- newtPushHelpLine(msg);
+ ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
+ thread->comm_set ? thread->comm : "",
+ thread->pid);
thread_filter = thread;
+ pstack__push(fstack, &thread_filter);
}
- nr_hists = hists__filter_by_thread(hists, thread_filter, &session_total);
+ hists__filter_by_thread(self, thread_filter);
hist_browser__title(msg, sizeof(msg), input_name,
dso_filter, thread_filter);
- if (hist_browser__populate(browser, hists, nr_hists, session_total, msg) < 0)
+ if (hist_browser__populate(browser, self, msg) < 0)
goto out;
}
}
err = 0;
+out_free_stack:
+ pstack__delete(fstack);
out:
hist_browser__delete(browser);
return err;
}
+static struct newtPercentTreeColors {
+ const char *topColorFg, *topColorBg;
+ const char *mediumColorFg, *mediumColorBg;
+ const char *normalColorFg, *normalColorBg;
+ const char *selColorFg, *selColorBg;
+ const char *codeColorFg, *codeColorBg;
+} defaultPercentTreeColors = {
+ "red", "lightgray",
+ "green", "lightgray",
+ "black", "lightgray",
+ "lightgray", "magenta",
+ "blue", "lightgray",
+};
+
void setup_browser(void)
{
+ struct newtPercentTreeColors *c = &defaultPercentTreeColors;
if (!isatty(1))
return;
use_browser = true;
newtInit();
newtCls();
- newtPushHelpLine(" ");
+ ui_helpline__puts(" ");
+ sltt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg);
+ sltt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg);
+ sltt_set_color(HE_COLORSET_NORMAL, NULL, c->normalColorFg, c->normalColorBg);
+ sltt_set_color(HE_COLORSET_SELECTED, NULL, c->selColorFg, c->selColorBg);
+ sltt_set_color(HE_COLORSET_CODE, NULL, c->codeColorFg, c->codeColorBg);
}
void exit_browser(bool wait_for_ok)
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 3b4ec679756..9bf0f402ca7 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -418,12 +418,6 @@ parse_single_tracepoint_event(char *sys_name,
u64 id;
int fd;
- attr->sample_type |= PERF_SAMPLE_RAW;
- attr->sample_type |= PERF_SAMPLE_TIME;
- attr->sample_type |= PERF_SAMPLE_CPU;
-
- attr->sample_period = 1;
-
snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", debugfs_path,
sys_name, evt_name);
@@ -442,6 +436,13 @@ parse_single_tracepoint_event(char *sys_name,
attr->type = PERF_TYPE_TRACEPOINT;
*strp = evt_name + evt_length;
+ attr->sample_type |= PERF_SAMPLE_RAW;
+ attr->sample_type |= PERF_SAMPLE_TIME;
+ attr->sample_type |= PERF_SAMPLE_CPU;
+
+ attr->sample_period = 1;
+
+
return EVT_HANDLED;
}
@@ -653,10 +654,6 @@ parse_raw_event(const char **strp, struct perf_event_attr *attr)
return EVT_FAILED;
n = hex2u64(str + 1, &config);
if (n > 0) {
- if (str[n+1] == 'p') {
- attr->precise = 1;
- n++;
- }
*strp = str + n + 1;
attr->type = PERF_TYPE_RAW;
attr->config = config;
@@ -691,19 +688,29 @@ static enum event_result
parse_event_modifier(const char **strp, struct perf_event_attr *attr)
{
const char *str = *strp;
- int eu = 1, ek = 1, eh = 1;
+ int exclude = 0;
+ int eu = 0, ek = 0, eh = 0, precise = 0;
if (*str++ != ':')
return 0;
while (*str) {
- if (*str == 'u')
+ if (*str == 'u') {
+ if (!exclude)
+ exclude = eu = ek = eh = 1;
eu = 0;
- else if (*str == 'k')
+ } else if (*str == 'k') {
+ if (!exclude)
+ exclude = eu = ek = eh = 1;
ek = 0;
- else if (*str == 'h')
+ } else if (*str == 'h') {
+ if (!exclude)
+ exclude = eu = ek = eh = 1;
eh = 0;
- else
+ } else if (*str == 'p') {
+ precise++;
+ } else
break;
+
++str;
}
if (str >= *strp + 2) {
@@ -711,6 +718,7 @@ parse_event_modifier(const char **strp, struct perf_event_attr *attr)
attr->exclude_user = eu;
attr->exclude_kernel = ek;
attr->exclude_hv = eh;
+ attr->precise_ip = precise;
return 1;
}
return 0;
@@ -935,7 +943,8 @@ void print_events(void)
printf("\n");
printf(" %-42s [%s]\n",
- "rNNN", event_type_descriptors[PERF_TYPE_RAW]);
+ "rNNN (see 'perf list --help' on how to encode it)",
+ event_type_descriptors[PERF_TYPE_RAW]);
printf("\n");
printf(" %-42s [%s]\n",
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h
index b8c1f64bc93..fc4ab3fe877 100644
--- a/tools/perf/util/parse-events.h
+++ b/tools/perf/util/parse-events.h
@@ -13,6 +13,7 @@ struct tracepoint_path {
};
extern struct tracepoint_path *tracepoint_id_to_path(u64 config);
+extern bool have_tracepoints(struct perf_event_attr *pattrs, int nb_events);
extern int nr_counters;
diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c
index ed887642460..99d02aa57db 100644
--- a/tools/perf/util/parse-options.c
+++ b/tools/perf/util/parse-options.c
@@ -51,7 +51,7 @@ static int get_value(struct parse_opt_ctx_t *p,
case OPTION_BOOLEAN:
case OPTION_INCR:
case OPTION_BIT:
- case OPTION_SET_INT:
+ case OPTION_SET_UINT:
case OPTION_SET_PTR:
return opterror(opt, "takes no value", flags);
case OPTION_END:
@@ -59,7 +59,9 @@ static int get_value(struct parse_opt_ctx_t *p,
case OPTION_GROUP:
case OPTION_STRING:
case OPTION_INTEGER:
+ case OPTION_UINTEGER:
case OPTION_LONG:
+ case OPTION_U64:
default:
break;
}
@@ -81,8 +83,8 @@ static int get_value(struct parse_opt_ctx_t *p,
*(int *)opt->value = unset ? 0 : *(int *)opt->value + 1;
return 0;
- case OPTION_SET_INT:
- *(int *)opt->value = unset ? 0 : opt->defval;
+ case OPTION_SET_UINT:
+ *(unsigned int *)opt->value = unset ? 0 : opt->defval;
return 0;
case OPTION_SET_PTR:
@@ -125,6 +127,22 @@ static int get_value(struct parse_opt_ctx_t *p,
return opterror(opt, "expects a numerical value", flags);
return 0;
+ case OPTION_UINTEGER:
+ if (unset) {
+ *(unsigned int *)opt->value = 0;
+ return 0;
+ }
+ if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
+ *(unsigned int *)opt->value = opt->defval;
+ return 0;
+ }
+ if (get_arg(p, opt, flags, &arg))
+ return -1;
+ *(unsigned int *)opt->value = strtol(arg, (char **)&s, 10);
+ if (*s)
+ return opterror(opt, "expects a numerical value", flags);
+ return 0;
+
case OPTION_LONG:
if (unset) {
*(long *)opt->value = 0;
@@ -141,6 +159,22 @@ static int get_value(struct parse_opt_ctx_t *p,
return opterror(opt, "expects a numerical value", flags);
return 0;
+ case OPTION_U64:
+ if (unset) {
+ *(u64 *)opt->value = 0;
+ return 0;
+ }
+ if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
+ *(u64 *)opt->value = opt->defval;
+ return 0;
+ }
+ if (get_arg(p, opt, flags, &arg))
+ return -1;
+ *(u64 *)opt->value = strtoull(arg, (char **)&s, 10);
+ if (*s)
+ return opterror(opt, "expects a numerical value", flags);
+ return 0;
+
case OPTION_END:
case OPTION_ARGUMENT:
case OPTION_GROUP:
@@ -446,7 +480,10 @@ int usage_with_options_internal(const char * const *usagestr,
switch (opts->type) {
case OPTION_ARGUMENT:
break;
+ case OPTION_LONG:
+ case OPTION_U64:
case OPTION_INTEGER:
+ case OPTION_UINTEGER:
if (opts->flags & PARSE_OPT_OPTARG)
if (opts->long_name)
pos += fprintf(stderr, "[=<n>]");
@@ -478,15 +515,14 @@ int usage_with_options_internal(const char * const *usagestr,
pos += fprintf(stderr, " ...");
}
break;
- default: /* OPTION_{BIT,BOOLEAN,SET_INT,SET_PTR} */
+ default: /* OPTION_{BIT,BOOLEAN,SET_UINT,SET_PTR} */
case OPTION_END:
case OPTION_GROUP:
case OPTION_BIT:
case OPTION_BOOLEAN:
case OPTION_INCR:
- case OPTION_SET_INT:
+ case OPTION_SET_UINT:
case OPTION_SET_PTR:
- case OPTION_LONG:
break;
}
diff --git a/tools/perf/util/parse-options.h b/tools/perf/util/parse-options.h
index b2da725f102..c7d72dce54b 100644
--- a/tools/perf/util/parse-options.h
+++ b/tools/perf/util/parse-options.h
@@ -1,6 +1,9 @@
#ifndef __PERF_PARSE_OPTIONS_H
#define __PERF_PARSE_OPTIONS_H
+#include <linux/kernel.h>
+#include <stdbool.h>
+
enum parse_opt_type {
/* special types */
OPTION_END,
@@ -10,13 +13,15 @@ enum parse_opt_type {
OPTION_BIT,
OPTION_BOOLEAN,
OPTION_INCR,
- OPTION_SET_INT,
+ OPTION_SET_UINT,
OPTION_SET_PTR,
/* options with arguments (usually) */
OPTION_STRING,
OPTION_INTEGER,
OPTION_LONG,
OPTION_CALLBACK,
+ OPTION_U64,
+ OPTION_UINTEGER,
};
enum parse_opt_flags {
@@ -74,7 +79,7 @@ typedef int parse_opt_cb(const struct option *, const char *arg, int unset);
*
* `defval`::
* default value to fill (*->value) with for PARSE_OPT_OPTARG.
- * OPTION_{BIT,SET_INT,SET_PTR} store the {mask,integer,pointer} to put in
+ * OPTION_{BIT,SET_UINT,SET_PTR} store the {mask,integer,pointer} to put in
* the value when met.
* CALLBACKS can use it like they want.
*/
@@ -91,17 +96,21 @@ struct option {
intptr_t defval;
};
+#define check_vtype(v, type) ( BUILD_BUG_ON_ZERO(!__builtin_types_compatible_p(typeof(v), type)) + v )
+
#define OPT_END() { .type = OPTION_END }
#define OPT_ARGUMENT(l, h) { .type = OPTION_ARGUMENT, .long_name = (l), .help = (h) }
#define OPT_GROUP(h) { .type = OPTION_GROUP, .help = (h) }
-#define OPT_BIT(s, l, v, h, b) { .type = OPTION_BIT, .short_name = (s), .long_name = (l), .value = (v), .help = (h), .defval = (b) }
-#define OPT_BOOLEAN(s, l, v, h) { .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), .value = (v), .help = (h) }
-#define OPT_INCR(s, l, v, h) { .type = OPTION_INCR, .short_name = (s), .long_name = (l), .value = (v), .help = (h) }
-#define OPT_SET_INT(s, l, v, h, i) { .type = OPTION_SET_INT, .short_name = (s), .long_name = (l), .value = (v), .help = (h), .defval = (i) }
+#define OPT_BIT(s, l, v, h, b) { .type = OPTION_BIT, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h), .defval = (b) }
+#define OPT_BOOLEAN(s, l, v, h) { .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), .value = check_vtype(v, bool *), .help = (h) }
+#define OPT_INCR(s, l, v, h) { .type = OPTION_INCR, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h) }
+#define OPT_SET_UINT(s, l, v, h, i) { .type = OPTION_SET_UINT, .short_name = (s), .long_name = (l), .value = check_vtype(v, unsigned int *), .help = (h), .defval = (i) }
#define OPT_SET_PTR(s, l, v, h, p) { .type = OPTION_SET_PTR, .short_name = (s), .long_name = (l), .value = (v), .help = (h), .defval = (p) }
-#define OPT_INTEGER(s, l, v, h) { .type = OPTION_INTEGER, .short_name = (s), .long_name = (l), .value = (v), .help = (h) }
-#define OPT_LONG(s, l, v, h) { .type = OPTION_LONG, .short_name = (s), .long_name = (l), .value = (v), .help = (h) }
-#define OPT_STRING(s, l, v, a, h) { .type = OPTION_STRING, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h) }
+#define OPT_INTEGER(s, l, v, h) { .type = OPTION_INTEGER, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h) }
+#define OPT_UINTEGER(s, l, v, h) { .type = OPTION_UINTEGER, .short_name = (s), .long_name = (l), .value = check_vtype(v, unsigned int *), .help = (h) }
+#define OPT_LONG(s, l, v, h) { .type = OPTION_LONG, .short_name = (s), .long_name = (l), .value = check_vtype(v, long *), .help = (h) }
+#define OPT_U64(s, l, v, h) { .type = OPTION_U64, .short_name = (s), .long_name = (l), .value = check_vtype(v, u64 *), .help = (h) }
+#define OPT_STRING(s, l, v, a, h) { .type = OPTION_STRING, .short_name = (s), .long_name = (l), .value = check_vtype(v, const char **), (a), .help = (h) }
#define OPT_DATE(s, l, v, h) \
{ .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), .argh = "time", .help = (h), .callback = parse_opt_approxidate_cb }
#define OPT_CALLBACK(s, l, v, a, h, f) \
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 4fb480367c3..914c67095d9 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -72,8 +72,7 @@ static int e_snprintf(char *str, size_t size, const char *format, ...)
}
static char *synthesize_perf_probe_point(struct perf_probe_point *pp);
-static struct map_groups kmap_groups;
-static struct map *kmaps[MAP__NR_TYPES];
+static struct machine machine;
/* Initialize symbol maps and path of vmlinux */
static int init_vmlinux(void)
@@ -92,12 +91,15 @@ static int init_vmlinux(void)
goto out;
}
+ ret = machine__init(&machine, "/", 0);
+ if (ret < 0)
+ goto out;
+
kernel = dso__new_kernel(symbol_conf.vmlinux_name);
if (kernel == NULL)
die("Failed to create kernel dso.");
- map_groups__init(&kmap_groups);
- ret = __map_groups__create_kernel_maps(&kmap_groups, kmaps, kernel);
+ ret = __machine__create_kernel_maps(&machine, kernel);
if (ret < 0)
pr_debug("Failed to create kernel maps.\n");
@@ -110,12 +112,12 @@ out:
#ifdef DWARF_SUPPORT
static int open_vmlinux(void)
{
- if (map__load(kmaps[MAP__FUNCTION], NULL) < 0) {
+ if (map__load(machine.vmlinux_maps[MAP__FUNCTION], NULL) < 0) {
pr_debug("Failed to load kernel map.\n");
return -EINVAL;
}
- pr_debug("Try to open %s\n", kmaps[MAP__FUNCTION]->dso->long_name);
- return open(kmaps[MAP__FUNCTION]->dso->long_name, O_RDONLY);
+ pr_debug("Try to open %s\n", machine.vmlinux_maps[MAP__FUNCTION]->dso->long_name);
+ return open(machine.vmlinux_maps[MAP__FUNCTION]->dso->long_name, O_RDONLY);
}
/* Convert trace point to probe point with debuginfo */
@@ -125,7 +127,7 @@ static int convert_to_perf_probe_point(struct kprobe_trace_point *tp,
struct symbol *sym;
int fd, ret = -ENOENT;
- sym = map__find_symbol_by_name(kmaps[MAP__FUNCTION],
+ sym = map__find_symbol_by_name(machine.vmlinux_maps[MAP__FUNCTION],
tp->symbol, NULL);
if (sym) {
fd = open_vmlinux();
@@ -150,7 +152,8 @@ static int convert_to_perf_probe_point(struct kprobe_trace_point *tp,
/* Try to find perf_probe_event with debuginfo */
static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev,
- struct kprobe_trace_event **tevs)
+ struct kprobe_trace_event **tevs,
+ int max_tevs)
{
bool need_dwarf = perf_probe_event_need_dwarf(pev);
int fd, ntevs;
@@ -166,7 +169,7 @@ static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev,
}
/* Searching trace events corresponding to probe event */
- ntevs = find_kprobe_trace_events(fd, pev, tevs);
+ ntevs = find_kprobe_trace_events(fd, pev, tevs, max_tevs);
close(fd);
if (ntevs > 0) { /* Succeeded to find trace events */
@@ -180,15 +183,16 @@ static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev,
return -ENOENT;
}
/* Error path : ntevs < 0 */
- if (need_dwarf) {
- if (ntevs == -EBADF)
- pr_warning("No dwarf info found in the vmlinux - "
- "please rebuild with CONFIG_DEBUG_INFO=y.\n");
- return ntevs;
+ pr_debug("An error occurred in debuginfo analysis (%d).\n", ntevs);
+ if (ntevs == -EBADF) {
+ pr_warning("Warning: No dwarf info found in the vmlinux - "
+ "please rebuild kernel with CONFIG_DEBUG_INFO=y.\n");
+ if (!need_dwarf) {
+ pr_debug("Trying to use symbols.\nn");
+ return 0;
+ }
}
- pr_debug("An error occurred in debuginfo analysis."
- " Try to use symbols.\n");
- return 0;
+ return ntevs;
}
#define LINEBUF_SIZE 256
@@ -317,7 +321,8 @@ static int convert_to_perf_probe_point(struct kprobe_trace_point *tp,
}
static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev,
- struct kprobe_trace_event **tevs __unused)
+ struct kprobe_trace_event **tevs __unused,
+ int max_tevs __unused)
{
if (perf_probe_event_need_dwarf(pev)) {
pr_warning("Debuginfo-analysis is not supported.\n");
@@ -1407,14 +1412,15 @@ static int __add_kprobe_trace_events(struct perf_probe_event *pev,
}
static int convert_to_kprobe_trace_events(struct perf_probe_event *pev,
- struct kprobe_trace_event **tevs)
+ struct kprobe_trace_event **tevs,
+ int max_tevs)
{
struct symbol *sym;
int ret = 0, i;
struct kprobe_trace_event *tev;
/* Convert perf_probe_event with debuginfo */
- ret = try_to_find_kprobe_trace_events(pev, tevs);
+ ret = try_to_find_kprobe_trace_events(pev, tevs, max_tevs);
if (ret != 0)
return ret;
@@ -1462,7 +1468,7 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev,
}
/* Currently just checking function name from symbol map */
- sym = map__find_symbol_by_name(kmaps[MAP__FUNCTION],
+ sym = map__find_symbol_by_name(machine.vmlinux_maps[MAP__FUNCTION],
tev->point.symbol, NULL);
if (!sym) {
pr_warning("Kernel symbol \'%s\' not found.\n",
@@ -1486,7 +1492,7 @@ struct __event_package {
};
int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
- bool force_add)
+ bool force_add, int max_tevs)
{
int i, j, ret;
struct __event_package *pkgs;
@@ -1505,7 +1511,7 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
pkgs[i].pev = &pevs[i];
/* Convert with or without debuginfo */
ret = convert_to_kprobe_trace_events(pkgs[i].pev,
- &pkgs[i].tevs);
+ &pkgs[i].tevs, max_tevs);
if (ret < 0)
goto end;
pkgs[i].ntevs = ret;
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index e7ff0d02c0d..e9db1a214ca 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -115,8 +115,8 @@ extern void clear_kprobe_trace_event(struct kprobe_trace_event *tev);
extern int parse_line_range_desc(const char *cmd, struct line_range *lr);
-extern int add_perf_probe_events(struct perf_probe_event *pevs, int ntevs,
- bool force_add);
+extern int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
+ bool force_add, int max_probe_points);
extern int del_perf_probe_events(struct strlist *dellist);
extern int show_perf_probe_events(void);
extern int show_line_range(struct line_range *lr);
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index 3e7977560be..562b1443e78 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -31,6 +31,7 @@
#include <string.h>
#include <stdarg.h>
#include <ctype.h>
+#include <dwarf-regs.h>
#include "string.h"
#include "event.h"
@@ -38,61 +39,9 @@
#include "util.h"
#include "probe-finder.h"
-
-/*
- * Generic dwarf analysis helpers
- */
-
-#define X86_32_MAX_REGS 8
-const char *x86_32_regs_table[X86_32_MAX_REGS] = {
- "%ax",
- "%cx",
- "%dx",
- "%bx",
- "$stack", /* Stack address instead of %sp */
- "%bp",
- "%si",
- "%di",
-};
-
-#define X86_64_MAX_REGS 16
-const char *x86_64_regs_table[X86_64_MAX_REGS] = {
- "%ax",
- "%dx",
- "%cx",
- "%bx",
- "%si",
- "%di",
- "%bp",
- "%sp",
- "%r8",
- "%r9",
- "%r10",
- "%r11",
- "%r12",
- "%r13",
- "%r14",
- "%r15",
-};
-
-/* TODO: switching by dwarf address size */
-#ifdef __x86_64__
-#define ARCH_MAX_REGS X86_64_MAX_REGS
-#define arch_regs_table x86_64_regs_table
-#else
-#define ARCH_MAX_REGS X86_32_MAX_REGS
-#define arch_regs_table x86_32_regs_table
-#endif
-
/* Kprobe tracer basic type is up to u64 */
#define MAX_BASIC_TYPE_BITS 64
-/* Return architecture dependent register string (for kprobe-tracer) */
-static const char *get_arch_regstr(unsigned int n)
-{
- return (n <= ARCH_MAX_REGS) ? arch_regs_table[n] : NULL;
-}
-
/*
* Compare the tail of two strings.
* Return 0 if whole of either string is same as another's tail part.
@@ -447,7 +396,7 @@ static int convert_location(Dwarf_Op *op, struct probe_finder *pf)
regs = get_arch_regstr(regn);
if (!regs) {
- pr_warning("%u exceeds max register number.\n", regn);
+ pr_warning("Mapping for DWARF register number %u missing on this architecture.", regn);
return -ERANGE;
}
@@ -677,8 +626,9 @@ static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)
Dwarf_Attribute fb_attr;
size_t nops;
- if (pf->ntevs == MAX_PROBES) {
- pr_warning("Too many( > %d) probe point found.\n", MAX_PROBES);
+ if (pf->ntevs == pf->max_tevs) {
+ pr_warning("Too many( > %d) probe point found.\n",
+ pf->max_tevs);
return -ERANGE;
}
tev = &pf->tevs[pf->ntevs++];
@@ -922,6 +872,8 @@ static int probe_point_inline_cb(Dwarf_Die *in_die, void *data)
(uintmax_t)pf->addr);
param->retval = convert_probe_point(in_die, pf);
+ if (param->retval < 0)
+ return DWARF_CB_ABORT;
}
return DWARF_CB_OK;
@@ -981,9 +933,9 @@ static int find_probe_point_by_func(struct probe_finder *pf)
/* Find kprobe_trace_events specified by perf_probe_event from debuginfo */
int find_kprobe_trace_events(int fd, struct perf_probe_event *pev,
- struct kprobe_trace_event **tevs)
+ struct kprobe_trace_event **tevs, int max_tevs)
{
- struct probe_finder pf = {.pev = pev};
+ struct probe_finder pf = {.pev = pev, .max_tevs = max_tevs};
struct perf_probe_point *pp = &pev->point;
Dwarf_Off off, noff;
size_t cuhl;
@@ -991,7 +943,7 @@ int find_kprobe_trace_events(int fd, struct perf_probe_event *pev,
Dwarf *dbg;
int ret = 0;
- pf.tevs = zalloc(sizeof(struct kprobe_trace_event) * MAX_PROBES);
+ pf.tevs = zalloc(sizeof(struct kprobe_trace_event) * max_tevs);
if (pf.tevs == NULL)
return -ENOMEM;
*tevs = pf.tevs;
@@ -1157,6 +1109,8 @@ static int line_range_funcdecl_cb(Dwarf_Die *sp_die, void *data)
return DWARF_CB_OK;
param->retval = line_range_add_line(src, lineno, lf->lr);
+ if (param->retval < 0)
+ return DWARF_CB_ABORT;
return DWARF_CB_OK;
}
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h
index 310ce897229..66f1980e385 100644
--- a/tools/perf/util/probe-finder.h
+++ b/tools/perf/util/probe-finder.h
@@ -18,7 +18,8 @@ static inline int is_c_varname(const char *name)
#ifdef DWARF_SUPPORT
/* Find kprobe_trace_events specified by perf_probe_event from debuginfo */
extern int find_kprobe_trace_events(int fd, struct perf_probe_event *pev,
- struct kprobe_trace_event **tevs);
+ struct kprobe_trace_event **tevs,
+ int max_tevs);
/* Find a perf_probe_point from debuginfo */
extern int find_perf_probe_point(int fd, unsigned long addr,
@@ -32,7 +33,8 @@ extern int find_line_range(int fd, struct line_range *lr);
struct probe_finder {
struct perf_probe_event *pev; /* Target probe event */
struct kprobe_trace_event *tevs; /* Result trace events */
- int ntevs; /* number of trace events */
+ int ntevs; /* Number of trace events */
+ int max_tevs; /* Max number of trace events */
/* For function searching */
int lno; /* Line number */
diff --git a/tools/perf/util/pstack.c b/tools/perf/util/pstack.c
new file mode 100644
index 00000000000..13d36faf64e
--- /dev/null
+++ b/tools/perf/util/pstack.c
@@ -0,0 +1,75 @@
+/*
+ * Simple pointer stack
+ *
+ * (c) 2010 Arnaldo Carvalho de Melo <acme@redhat.com>
+ */
+
+#include "util.h"
+#include "pstack.h"
+#include <linux/kernel.h>
+#include <stdlib.h>
+
+struct pstack {
+ unsigned short top;
+ unsigned short max_nr_entries;
+ void *entries[0];
+};
+
+struct pstack *pstack__new(unsigned short max_nr_entries)
+{
+ struct pstack *self = zalloc((sizeof(*self) +
+ max_nr_entries * sizeof(void *)));
+ if (self != NULL)
+ self->max_nr_entries = max_nr_entries;
+ return self;
+}
+
+void pstack__delete(struct pstack *self)
+{
+ free(self);
+}
+
+bool pstack__empty(const struct pstack *self)
+{
+ return self->top == 0;
+}
+
+void pstack__remove(struct pstack *self, void *key)
+{
+ unsigned short i = self->top, last_index = self->top - 1;
+
+ while (i-- != 0) {
+ if (self->entries[i] == key) {
+ if (i < last_index)
+ memmove(self->entries + i,
+ self->entries + i + 1,
+ (last_index - i) * sizeof(void *));
+ --self->top;
+ return;
+ }
+ }
+ pr_err("%s: %p not on the pstack!\n", __func__, key);
+}
+
+void pstack__push(struct pstack *self, void *key)
+{
+ if (self->top == self->max_nr_entries) {
+ pr_err("%s: top=%d, overflow!\n", __func__, self->top);
+ return;
+ }
+ self->entries[self->top++] = key;
+}
+
+void *pstack__pop(struct pstack *self)
+{
+ void *ret;
+
+ if (self->top == 0) {
+ pr_err("%s: underflow!\n", __func__);
+ return NULL;
+ }
+
+ ret = self->entries[--self->top];
+ self->entries[self->top] = NULL;
+ return ret;
+}
diff --git a/tools/perf/util/pstack.h b/tools/perf/util/pstack.h
new file mode 100644
index 00000000000..5ad07023504
--- /dev/null
+++ b/tools/perf/util/pstack.h
@@ -0,0 +1,12 @@
+#ifndef _PERF_PSTACK_
+#define _PERF_PSTACK_
+
+struct pstack;
+struct pstack *pstack__new(unsigned short max_nr_entries);
+void pstack__delete(struct pstack *self);
+bool pstack__empty(const struct pstack *self);
+void pstack__remove(struct pstack *self, void *key);
+void pstack__push(struct pstack *self, void *key);
+void *pstack__pop(struct pstack *self);
+
+#endif /* _PERF_PSTACK_ */
diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c
index 5376378e0cf..b059dc50cc2 100644
--- a/tools/perf/util/scripting-engines/trace-event-perl.c
+++ b/tools/perf/util/scripting-engines/trace-event-perl.c
@@ -371,7 +371,6 @@ static int perl_start_script(const char *script, int argc, const char **argv)
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);
@@ -394,8 +393,6 @@ static int perl_stop_script(void)
perl_destruct(my_perl);
perl_free(my_perl);
- fprintf(stderr, "\nperf trace Perl script stopped\n");
-
return 0;
}
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c
index 6a72f14c598..81f39cab3aa 100644
--- a/tools/perf/util/scripting-engines/trace-event-python.c
+++ b/tools/perf/util/scripting-engines/trace-event-python.c
@@ -374,8 +374,6 @@ static int python_start_script(const char *script, int argc, const char **argv)
}
free(command_line);
- fprintf(stderr, "perf trace started with Python script %s\n\n",
- script);
return err;
error:
@@ -407,8 +405,6 @@ out:
Py_XDECREF(main_module);
Py_Finalize();
- fprintf(stderr, "\nperf trace Python script stopped\n");
-
return err;
}
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index 7d88ae5c270..25bfca4f10f 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -69,16 +69,14 @@ void perf_session__update_sample_type(struct perf_session *self)
int perf_session__create_kernel_maps(struct perf_session *self)
{
- int ret;
- struct rb_root *root = &self->kerninfo_root;
+ int ret = machine__create_kernel_maps(&self->host_machine);
- ret = map_groups__create_kernel_maps(root, HOST_KERNEL_ID);
if (ret >= 0)
- ret = map_groups__create_guest_kernel_maps(root);
+ ret = machines__create_guest_kernel_maps(&self->machines);
return ret;
}
-struct perf_session *perf_session__new(const char *filename, int mode, bool force)
+struct perf_session *perf_session__new(const char *filename, int mode, bool force, bool repipe)
{
size_t len = filename ? strlen(filename) + 1 : 0;
struct perf_session *self = zalloc(sizeof(*self) + len);
@@ -91,13 +89,15 @@ struct perf_session *perf_session__new(const char *filename, int mode, bool forc
memcpy(self->filename, filename, len);
self->threads = RB_ROOT;
- self->stats_by_id = RB_ROOT;
+ self->hists_tree = RB_ROOT;
self->last_match = NULL;
self->mmap_window = 32;
self->cwd = NULL;
self->cwdlen = 0;
- self->unknown_events = 0;
- self->kerninfo_root = RB_ROOT;
+ self->machines = RB_ROOT;
+ self->repipe = repipe;
+ INIT_LIST_HEAD(&self->ordered_samples.samples_head);
+ machine__init(&self->host_machine, "", HOST_KERNEL_ID);
if (mode == O_RDONLY) {
if (perf_session__open(self, force) < 0)
@@ -192,6 +192,18 @@ static int process_event_stub(event_t *event __used,
return 0;
}
+static int process_finished_round_stub(event_t *event __used,
+ struct perf_session *session __used,
+ struct perf_event_ops *ops __used)
+{
+ dump_printf(": unhandled!\n");
+ return 0;
+}
+
+static int process_finished_round(event_t *event,
+ struct perf_session *session,
+ struct perf_event_ops *ops);
+
static void perf_event_ops__fill_defaults(struct perf_event_ops *handler)
{
if (handler->sample == NULL)
@@ -220,35 +232,11 @@ static void perf_event_ops__fill_defaults(struct perf_event_ops *handler)
handler->tracing_data = process_event_stub;
if (handler->build_id == NULL)
handler->build_id = process_event_stub;
-}
-
-static const char *event__name[] = {
- [0] = "TOTAL",
- [PERF_RECORD_MMAP] = "MMAP",
- [PERF_RECORD_LOST] = "LOST",
- [PERF_RECORD_COMM] = "COMM",
- [PERF_RECORD_EXIT] = "EXIT",
- [PERF_RECORD_THROTTLE] = "THROTTLE",
- [PERF_RECORD_UNTHROTTLE] = "UNTHROTTLE",
- [PERF_RECORD_FORK] = "FORK",
- [PERF_RECORD_READ] = "READ",
- [PERF_RECORD_SAMPLE] = "SAMPLE",
- [PERF_RECORD_HEADER_ATTR] = "ATTR",
- [PERF_RECORD_HEADER_EVENT_TYPE] = "EVENT_TYPE",
- [PERF_RECORD_HEADER_TRACING_DATA] = "TRACING_DATA",
- [PERF_RECORD_HEADER_BUILD_ID] = "BUILD_ID",
-};
-
-unsigned long event__total[PERF_RECORD_HEADER_MAX];
-
-void event__print_totals(void)
-{
- int i;
- for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) {
- if (!event__name[i])
- continue;
- pr_info("%10s events: %10ld\n",
- event__name[i], event__total[i]);
+ if (handler->finished_round == NULL) {
+ if (handler->ordered_samples)
+ handler->finished_round = process_finished_round;
+ else
+ handler->finished_round = process_finished_round_stub;
}
}
@@ -351,6 +339,205 @@ static event__swap_op event__swap_ops[] = {
[PERF_RECORD_HEADER_MAX] = NULL,
};
+struct sample_queue {
+ u64 timestamp;
+ struct sample_event *event;
+ struct list_head list;
+};
+
+static void flush_sample_queue(struct perf_session *s,
+ struct perf_event_ops *ops)
+{
+ struct list_head *head = &s->ordered_samples.samples_head;
+ u64 limit = s->ordered_samples.next_flush;
+ struct sample_queue *tmp, *iter;
+
+ if (!ops->ordered_samples || !limit)
+ return;
+
+ list_for_each_entry_safe(iter, tmp, head, list) {
+ if (iter->timestamp > limit)
+ return;
+
+ if (iter == s->ordered_samples.last_inserted)
+ s->ordered_samples.last_inserted = NULL;
+
+ ops->sample((event_t *)iter->event, s);
+
+ s->ordered_samples.last_flush = iter->timestamp;
+ list_del(&iter->list);
+ free(iter->event);
+ free(iter);
+ }
+}
+
+/*
+ * When perf record finishes a pass on every buffers, it records this pseudo
+ * event.
+ * We record the max timestamp t found in the pass n.
+ * Assuming these timestamps are monotonic across cpus, we know that if
+ * a buffer still has events with timestamps below t, they will be all
+ * available and then read in the pass n + 1.
+ * Hence when we start to read the pass n + 2, we can safely flush every
+ * events with timestamps below t.
+ *
+ * ============ PASS n =================
+ * CPU 0 | CPU 1
+ * |
+ * cnt1 timestamps | cnt2 timestamps
+ * 1 | 2
+ * 2 | 3
+ * - | 4 <--- max recorded
+ *
+ * ============ PASS n + 1 ==============
+ * CPU 0 | CPU 1
+ * |
+ * cnt1 timestamps | cnt2 timestamps
+ * 3 | 5
+ * 4 | 6
+ * 5 | 7 <---- max recorded
+ *
+ * Flush every events below timestamp 4
+ *
+ * ============ PASS n + 2 ==============
+ * CPU 0 | CPU 1
+ * |
+ * cnt1 timestamps | cnt2 timestamps
+ * 6 | 8
+ * 7 | 9
+ * - | 10
+ *
+ * Flush every events below timestamp 7
+ * etc...
+ */
+static int process_finished_round(event_t *event __used,
+ struct perf_session *session,
+ struct perf_event_ops *ops)
+{
+ flush_sample_queue(session, ops);
+ session->ordered_samples.next_flush = session->ordered_samples.max_timestamp;
+
+ return 0;
+}
+
+static void __queue_sample_end(struct sample_queue *new, struct list_head *head)
+{
+ struct sample_queue *iter;
+
+ list_for_each_entry_reverse(iter, head, list) {
+ if (iter->timestamp < new->timestamp) {
+ list_add(&new->list, &iter->list);
+ return;
+ }
+ }
+
+ list_add(&new->list, head);
+}
+
+static void __queue_sample_before(struct sample_queue *new,
+ struct sample_queue *iter,
+ struct list_head *head)
+{
+ list_for_each_entry_continue_reverse(iter, head, list) {
+ if (iter->timestamp < new->timestamp) {
+ list_add(&new->list, &iter->list);
+ return;
+ }
+ }
+
+ list_add(&new->list, head);
+}
+
+static void __queue_sample_after(struct sample_queue *new,
+ struct sample_queue *iter,
+ struct list_head *head)
+{
+ list_for_each_entry_continue(iter, head, list) {
+ if (iter->timestamp > new->timestamp) {
+ list_add_tail(&new->list, &iter->list);
+ return;
+ }
+ }
+ list_add_tail(&new->list, head);
+}
+
+/* The queue is ordered by time */
+static void __queue_sample_event(struct sample_queue *new,
+ struct perf_session *s)
+{
+ struct sample_queue *last_inserted = s->ordered_samples.last_inserted;
+ struct list_head *head = &s->ordered_samples.samples_head;
+
+
+ if (!last_inserted) {
+ __queue_sample_end(new, head);
+ return;
+ }
+
+ /*
+ * Most of the time the current event has a timestamp
+ * very close to the last event inserted, unless we just switched
+ * to another event buffer. Having a sorting based on a list and
+ * on the last inserted event that is close to the current one is
+ * probably more efficient than an rbtree based sorting.
+ */
+ if (last_inserted->timestamp >= new->timestamp)
+ __queue_sample_before(new, last_inserted, head);
+ else
+ __queue_sample_after(new, last_inserted, head);
+}
+
+static int queue_sample_event(event_t *event, struct sample_data *data,
+ struct perf_session *s)
+{
+ u64 timestamp = data->time;
+ struct sample_queue *new;
+
+
+ if (timestamp < s->ordered_samples.last_flush) {
+ printf("Warning: Timestamp below last timeslice flush\n");
+ return -EINVAL;
+ }
+
+ new = malloc(sizeof(*new));
+ if (!new)
+ return -ENOMEM;
+
+ new->timestamp = timestamp;
+
+ new->event = malloc(event->header.size);
+ if (!new->event) {
+ free(new);
+ return -ENOMEM;
+ }
+
+ memcpy(new->event, event, event->header.size);
+
+ __queue_sample_event(new, s);
+ s->ordered_samples.last_inserted = new;
+
+ if (new->timestamp > s->ordered_samples.max_timestamp)
+ s->ordered_samples.max_timestamp = new->timestamp;
+
+ return 0;
+}
+
+static int perf_session__process_sample(event_t *event, struct perf_session *s,
+ struct perf_event_ops *ops)
+{
+ struct sample_data data;
+
+ if (!ops->ordered_samples)
+ return ops->sample(event, s);
+
+ bzero(&data, sizeof(struct sample_data));
+ event__parse_sample(event, s->sample_type, &data);
+
+ queue_sample_event(event, &data, s);
+
+ return 0;
+}
+
static int perf_session__process_event(struct perf_session *self,
event_t *event,
struct perf_event_ops *ops,
@@ -362,8 +549,7 @@ static int perf_session__process_event(struct perf_session *self,
dump_printf("%#Lx [%#x]: PERF_RECORD_%s",
offset + head, event->header.size,
event__name[event->header.type]);
- ++event__total[0];
- ++event__total[event->header.type];
+ hists__inc_nr_events(&self->hists, event->header.type);
}
if (self->header.needs_swap && event__swap_ops[event->header.type])
@@ -371,7 +557,7 @@ static int perf_session__process_event(struct perf_session *self,
switch (event->header.type) {
case PERF_RECORD_SAMPLE:
- return ops->sample(event, self);
+ return perf_session__process_sample(event, self, ops);
case PERF_RECORD_MMAP:
return ops->mmap(event, self);
case PERF_RECORD_COMM:
@@ -398,8 +584,10 @@ static int perf_session__process_event(struct perf_session *self,
return ops->tracing_data(event, self);
case PERF_RECORD_HEADER_BUILD_ID:
return ops->build_id(event, self);
+ case PERF_RECORD_FINISHED_ROUND:
+ return ops->finished_round(event, self, ops);
default:
- self->unknown_events++;
+ ++self->hists.stats.nr_unknown_events;
return -1;
}
}
@@ -476,15 +664,18 @@ more:
p = &event;
p += sizeof(struct perf_event_header);
- err = do_read(self->fd, p, size - sizeof(struct perf_event_header));
- if (err <= 0) {
- if (err == 0) {
- pr_err("unexpected end of event stream\n");
- goto done;
- }
+ if (size - sizeof(struct perf_event_header)) {
+ err = do_read(self->fd, p,
+ size - sizeof(struct perf_event_header));
+ if (err <= 0) {
+ if (err == 0) {
+ pr_err("unexpected end of event stream\n");
+ goto done;
+ }
- pr_err("failed to read event data\n");
- goto out_err;
+ pr_err("failed to read event data\n");
+ goto out_err;
+ }
}
if (size == 0 ||
@@ -611,6 +802,9 @@ more:
goto more;
done:
err = 0;
+ /* do the final flush for ordered samples */
+ self->ordered_samples.next_flush = ULLONG_MAX;
+ flush_sample_queue(self, ops);
out_err:
ui_progress__delete(progress);
return err;
@@ -693,3 +887,10 @@ int perf_session__set_kallsyms_ref_reloc_sym(struct map **maps,
return 0;
}
+
+size_t perf_session__fprintf_dsos(struct perf_session *self, FILE *fp)
+{
+ return __dsos__fprintf(&self->host_machine.kernel_dsos, fp) +
+ __dsos__fprintf(&self->host_machine.user_dsos, fp) +
+ machines__fprintf_dsos(&self->machines, fp);
+}
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
index 5e47c87b926..e7fce486ebe 100644
--- a/tools/perf/util/session.h
+++ b/tools/perf/util/session.h
@@ -1,6 +1,7 @@
#ifndef __PERF_SESSION_H
#define __PERF_SESSION_H
+#include "hist.h"
#include "event.h"
#include "header.h"
#include "symbol.h"
@@ -8,48 +9,69 @@
#include <linux/rbtree.h>
#include "../../../include/linux/perf_event.h"
+struct sample_queue;
struct ip_callchain;
struct thread;
+struct ordered_samples {
+ u64 last_flush;
+ u64 next_flush;
+ u64 max_timestamp;
+ struct list_head samples_head;
+ struct sample_queue *last_inserted;
+};
+
struct perf_session {
struct perf_header header;
unsigned long size;
unsigned long mmap_window;
struct rb_root threads;
struct thread *last_match;
- struct rb_root kerninfo_root;
- struct events_stats events_stats;
- struct rb_root stats_by_id;
- unsigned long event_total[PERF_RECORD_MAX];
- unsigned long unknown_events;
- struct rb_root hists;
+ struct machine host_machine;
+ struct rb_root machines;
+ struct rb_root hists_tree;
+ /*
+ * FIXME: should point to the first entry in hists_tree and
+ * be a hists instance. Right now its only 'report'
+ * that is using ->hists_tree while all the rest use
+ * ->hists.
+ */
+ struct hists hists;
u64 sample_type;
int fd;
bool fd_pipe;
+ bool repipe;
int cwdlen;
char *cwd;
+ struct ordered_samples ordered_samples;
char filename[0];
};
+struct perf_event_ops;
+
typedef int (*event_op)(event_t *self, struct perf_session *session);
+typedef int (*event_op2)(event_t *self, struct perf_session *session,
+ struct perf_event_ops *ops);
struct perf_event_ops {
- event_op sample,
- mmap,
- comm,
- fork,
- exit,
- lost,
- read,
- throttle,
- unthrottle,
- attr,
- event_type,
- tracing_data,
- build_id;
+ event_op sample,
+ mmap,
+ comm,
+ fork,
+ exit,
+ lost,
+ read,
+ throttle,
+ unthrottle,
+ attr,
+ event_type,
+ tracing_data,
+ build_id;
+ event_op2 finished_round;
+ bool ordered_samples;
};
-struct perf_session *perf_session__new(const char *filename, int mode, bool force);
+struct perf_session *perf_session__new(const char *filename, int mode, bool force, bool repipe);
void perf_session__delete(struct perf_session *self);
void perf_event_header__bswap(struct perf_event_header *self);
@@ -78,18 +100,48 @@ int perf_session__create_kernel_maps(struct perf_session *self);
int do_read(int fd, void *buf, size_t size);
void perf_session__update_sample_type(struct perf_session *self);
-#ifdef NO_NEWT_SUPPORT
-static inline int perf_session__browse_hists(struct rb_root *hists __used,
- u64 nr_hists __used,
- u64 session_total __used,
- const char *helpline __used,
- const char *input_name __used)
+static inline
+struct machine *perf_session__find_host_machine(struct perf_session *self)
+{
+ return &self->host_machine;
+}
+
+static inline
+struct machine *perf_session__find_machine(struct perf_session *self, pid_t pid)
+{
+ if (pid == HOST_KERNEL_ID)
+ return &self->host_machine;
+ return machines__find(&self->machines, pid);
+}
+
+static inline
+struct machine *perf_session__findnew_machine(struct perf_session *self, pid_t pid)
+{
+ if (pid == HOST_KERNEL_ID)
+ return &self->host_machine;
+ return machines__findnew(&self->machines, pid);
+}
+
+static inline
+void perf_session__process_machines(struct perf_session *self,
+ machine__process_t process)
+{
+ process(&self->host_machine, self);
+ return machines__process(&self->machines, process, self);
+}
+
+size_t perf_session__fprintf_dsos(struct perf_session *self, FILE *fp);
+
+static inline
+size_t perf_session__fprintf_dsos_buildid(struct perf_session *self, FILE *fp,
+ bool with_hits)
+{
+ return machines__fprintf_dsos_buildid(&self->machines, fp, with_hits);
+}
+
+static inline
+size_t perf_session__fprintf_nr_events(struct perf_session *self, FILE *fp)
{
- return 0;
+ return hists__fprintf_nr_events(&self->hists, fp);
}
-#else
-int perf_session__browse_hists(struct rb_root *hists, u64 nr_hists,
- u64 session_total, const char *helpline,
- const char *input_name);
-#endif
#endif /* __PERF_SESSION_H */
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c
index da30b305fba..2316cb5a411 100644
--- a/tools/perf/util/sort.c
+++ b/tools/perf/util/sort.c
@@ -1,10 +1,10 @@
#include "sort.h"
regex_t parent_regex;
-char default_parent_pattern[] = "^sys_|^do_page_fault";
-char *parent_pattern = default_parent_pattern;
-char default_sort_order[] = "comm,dso,symbol";
-char *sort_order = default_sort_order;
+const char default_parent_pattern[] = "^sys_|^do_page_fault";
+const char *parent_pattern = default_parent_pattern;
+const char default_sort_order[] = "comm,dso,symbol";
+const char *sort_order = default_sort_order;
int sort__need_collapse = 0;
int sort__has_parent = 0;
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h
index b7c54eeed9c..0d61c4082f4 100644
--- a/tools/perf/util/sort.h
+++ b/tools/perf/util/sort.h
@@ -25,10 +25,10 @@
#include "sort.h"
extern regex_t parent_regex;
-extern char *sort_order;
-extern char default_parent_pattern[];
-extern char *parent_pattern;
-extern char default_sort_order[];
+extern const char *sort_order;
+extern const char default_parent_pattern[];
+extern const char *parent_pattern;
+extern const char default_sort_order[];
extern int sort__need_collapse;
extern int sort__has_parent;
extern char *field_sep;
@@ -43,20 +43,15 @@ extern enum sort_type sort__first_dimension;
struct hist_entry {
struct rb_node rb_node;
- u64 count;
- u64 count_sys;
- u64 count_us;
- u64 count_guest_sys;
- u64 count_guest_us;
-
- /*
- * XXX WARNING!
- * thread _has_ to come after ms, see
- * hist_browser__selected_thread in util/newt.c
- */
+ u64 period;
+ u64 period_sys;
+ u64 period_us;
+ u64 period_guest_sys;
+ u64 period_guest_us;
struct map_symbol ms;
struct thread *thread;
u64 ip;
+ u32 nr_events;
char level;
u8 filtered;
struct symbol *parent;
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index e782e7db16c..a06131f6259 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -130,8 +130,9 @@ static struct symbol *symbol__new(u64 start, u64 len, const char *name)
if (symbol_conf.priv_size)
self = ((void *)self) + symbol_conf.priv_size;
- self->start = start;
- self->end = len ? start + len - 1 : start;
+ self->start = start;
+ self->end = len ? start + len - 1 : start;
+ self->namelen = namelen - 1;
pr_debug4("%s: %s %#Lx-%#Lx\n", __func__, name, start, self->end);
@@ -189,6 +190,7 @@ struct dso *dso__new(const char *name)
self->sorted_by_name = 0;
self->has_build_id = 0;
self->kernel = DSO_TYPE_USER;
+ INIT_LIST_HEAD(&self->node);
}
return self;
@@ -484,7 +486,7 @@ static int dso__split_kallsyms(struct dso *self, struct map *map,
symbol_filter_t filter)
{
struct map_groups *kmaps = map__kmap(map)->kmaps;
- struct kernel_info *kerninfo = kmaps->this_kerninfo;
+ struct machine *machine = kmaps->machine;
struct map *curr_map = map;
struct symbol *pos;
int count = 0;
@@ -507,8 +509,8 @@ static int dso__split_kallsyms(struct dso *self, struct map *map,
if (strcmp(curr_map->dso->short_name, module)) {
if (curr_map != map &&
- self->kernel == DSO_TYPE_GUEST_KERNEL &&
- is_default_guest(kerninfo)) {
+ self->kernel == DSO_TYPE_GUEST_KERNEL &&
+ machine__is_default_guest(machine)) {
/*
* We assume all symbols of a module are
* continuous in * kallsyms, so curr_map
@@ -523,16 +525,16 @@ static int dso__split_kallsyms(struct dso *self, struct map *map,
curr_map = map_groups__find_by_name(kmaps,
map->type, module);
if (curr_map == NULL) {
- pr_err("%s/proc/{kallsyms,modules} "
+ pr_debug("%s/proc/{kallsyms,modules} "
"inconsistency while looking "
"for \"%s\" module!\n",
- kerninfo->root_dir, module);
+ machine->root_dir, module);
curr_map = map;
goto discard_symbol;
}
if (curr_map->dso->loaded &&
- !is_default_guest(kmaps->this_kerninfo))
+ !machine__is_default_guest(machine))
goto discard_symbol;
}
/*
@@ -585,7 +587,7 @@ discard_symbol: rb_erase(&pos->rb_node, root);
if (curr_map != map &&
self->kernel == DSO_TYPE_GUEST_KERNEL &&
- is_default_guest(kmaps->this_kerninfo)) {
+ machine__is_default_guest(kmaps->machine)) {
dso__set_loaded(curr_map->dso, curr_map->type);
}
@@ -1290,7 +1292,7 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter)
char build_id_hex[BUILD_ID_SIZE * 2 + 1];
int ret = -1;
int fd;
- struct kernel_info *kerninfo;
+ struct machine *machine;
const char *root_dir;
dso__set_loaded(self, map->type);
@@ -1300,10 +1302,10 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter)
else if (self->kernel == DSO_TYPE_GUEST_KERNEL)
return dso__load_guest_kernel_sym(self, map, filter);
- if (map->groups && map->groups->this_kerninfo)
- kerninfo = map->groups->this_kerninfo;
+ if (map->groups && map->groups->machine)
+ machine = map->groups->machine;
else
- kerninfo = NULL;
+ machine = NULL;
name = malloc(size);
if (!name)
@@ -1358,8 +1360,8 @@ more:
snprintf(name, size, "%s", self->long_name);
break;
case DSO__ORIG_GUEST_KMODULE:
- if (map->groups && map->groups->this_kerninfo)
- root_dir = map->groups->this_kerninfo->root_dir;
+ if (map->groups && map->groups->machine)
+ root_dir = map->groups->machine->root_dir;
else
root_dir = "";
snprintf(name, size, "%s%s", root_dir, self->long_name);
@@ -1527,21 +1529,20 @@ static char *get_kernel_version(const char *root_dir)
return strdup(name);
}
-static int map_groups__set_modules_path(struct map_groups *self,
- const char *root_dir)
+static int machine__set_modules_path(struct machine *self)
{
char *version;
char modules_path[PATH_MAX];
- version = get_kernel_version(root_dir);
+ version = get_kernel_version(self->root_dir);
if (!version)
return -1;
snprintf(modules_path, sizeof(modules_path), "%s/lib/modules/%s/kernel",
- root_dir, version);
+ self->root_dir, version);
free(version);
- return map_groups__set_modules_path_dir(self, modules_path);
+ return map_groups__set_modules_path_dir(&self->kmaps, modules_path);
}
/*
@@ -1563,14 +1564,12 @@ static struct map *map__new2(u64 start, struct dso *dso, enum map_type type)
return self;
}
-struct map *map_groups__new_module(struct map_groups *self, u64 start,
- const char *filename,
- struct kernel_info *kerninfo)
+struct map *machine__new_module(struct machine *self, u64 start,
+ const char *filename)
{
struct map *map;
- struct dso *dso;
+ struct dso *dso = __dsos__findnew(&self->kernel_dsos, filename);
- dso = __dsos__findnew(&kerninfo->dsos__kernel, filename);
if (dso == NULL)
return NULL;
@@ -1578,28 +1577,27 @@ struct map *map_groups__new_module(struct map_groups *self, u64 start,
if (map == NULL)
return NULL;
- if (is_host_kernel(kerninfo))
+ if (machine__is_host(self))
dso->origin = DSO__ORIG_KMODULE;
else
dso->origin = DSO__ORIG_GUEST_KMODULE;
- map_groups__insert(self, map);
+ map_groups__insert(&self->kmaps, map);
return map;
}
-static int map_groups__create_modules(struct kernel_info *kerninfo)
+static int machine__create_modules(struct machine *self)
{
char *line = NULL;
size_t n;
FILE *file;
struct map *map;
- const char *root_dir;
const char *modules;
char path[PATH_MAX];
- if (is_default_guest(kerninfo))
+ if (machine__is_default_guest(self))
modules = symbol_conf.default_guest_modules;
else {
- sprintf(path, "%s/proc/modules", kerninfo->root_dir);
+ sprintf(path, "%s/proc/modules", self->root_dir);
modules = path;
}
@@ -1607,8 +1605,6 @@ static int map_groups__create_modules(struct kernel_info *kerninfo)
if (file == NULL)
return -1;
- root_dir = kerninfo->root_dir;
-
while (!feof(file)) {
char name[PATH_MAX];
u64 start;
@@ -1637,17 +1633,16 @@ static int map_groups__create_modules(struct kernel_info *kerninfo)
*sep = '\0';
snprintf(name, sizeof(name), "[%s]", line);
- map = map_groups__new_module(&kerninfo->kmaps,
- start, name, kerninfo);
+ map = machine__new_module(self, start, name);
if (map == NULL)
goto out_delete_line;
- dso__kernel_module_get_build_id(map->dso, root_dir);
+ dso__kernel_module_get_build_id(map->dso, self->root_dir);
}
free(line);
fclose(file);
- return map_groups__set_modules_path(&kerninfo->kmaps, root_dir);
+ return machine__set_modules_path(self);
out_delete_line:
free(line);
@@ -1819,16 +1814,16 @@ static int dso__load_guest_kernel_sym(struct dso *self, struct map *map,
{
int err;
const char *kallsyms_filename = NULL;
- struct kernel_info *kerninfo;
+ struct machine *machine;
char path[PATH_MAX];
if (!map->groups) {
pr_debug("Guest kernel map hasn't the point to groups\n");
return -1;
}
- kerninfo = map->groups->this_kerninfo;
+ machine = map->groups->machine;
- if (is_default_guest(kerninfo)) {
+ if (machine__is_default_guest(machine)) {
/*
* if the user specified a vmlinux filename, use it and only
* it, reporting errors to the user if it cannot be used.
@@ -1844,7 +1839,7 @@ static int dso__load_guest_kernel_sym(struct dso *self, struct map *map,
if (!kallsyms_filename)
return -1;
} else {
- sprintf(path, "%s/proc/kallsyms", kerninfo->root_dir);
+ sprintf(path, "%s/proc/kallsyms", machine->root_dir);
kallsyms_filename = path;
}
@@ -1855,9 +1850,8 @@ static int dso__load_guest_kernel_sym(struct dso *self, struct map *map,
out_try_fixup:
if (err > 0) {
if (kallsyms_filename != NULL) {
- kern_mmap_name(kerninfo, path);
- dso__set_long_name(self,
- strdup(path));
+ machine__mmap_name(machine, path, sizeof(path));
+ dso__set_long_name(self, strdup(path));
}
map__fixup_start(map);
map__fixup_end(map);
@@ -1896,27 +1890,32 @@ struct dso *__dsos__findnew(struct list_head *head, const char *name)
return dso;
}
-static void __dsos__fprintf(struct list_head *head, FILE *fp)
+size_t __dsos__fprintf(struct list_head *head, FILE *fp)
{
struct dso *pos;
+ size_t ret = 0;
list_for_each_entry(pos, head, node) {
int i;
for (i = 0; i < MAP__NR_TYPES; ++i)
- dso__fprintf(pos, i, fp);
+ ret += dso__fprintf(pos, i, fp);
}
+
+ return ret;
}
-void dsos__fprintf(struct rb_root *kerninfo_root, FILE *fp)
+size_t machines__fprintf_dsos(struct rb_root *self, FILE *fp)
{
struct rb_node *nd;
+ size_t ret = 0;
- for (nd = rb_first(kerninfo_root); nd; nd = rb_next(nd)) {
- struct kernel_info *pos = rb_entry(nd, struct kernel_info,
- rb_node);
- __dsos__fprintf(&pos->dsos__kernel, fp);
- __dsos__fprintf(&pos->dsos__user, fp);
+ for (nd = rb_first(self); nd; nd = rb_next(nd)) {
+ struct machine *pos = rb_entry(nd, struct machine, rb_node);
+ ret += __dsos__fprintf(&pos->kernel_dsos, fp);
+ ret += __dsos__fprintf(&pos->user_dsos, fp);
}
+
+ return ret;
}
static size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp,
@@ -1934,19 +1933,15 @@ static size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp,
return ret;
}
-size_t dsos__fprintf_buildid(struct rb_root *kerninfo_root,
- FILE *fp, bool with_hits)
+size_t machines__fprintf_dsos_buildid(struct rb_root *self, FILE *fp, bool with_hits)
{
struct rb_node *nd;
size_t ret = 0;
- for (nd = rb_first(kerninfo_root); nd; nd = rb_next(nd)) {
- struct kernel_info *pos = rb_entry(nd, struct kernel_info,
- rb_node);
- ret += __dsos__fprintf_buildid(&pos->dsos__kernel,
- fp, with_hits);
- ret += __dsos__fprintf_buildid(&pos->dsos__user,
- fp, with_hits);
+ for (nd = rb_first(self); nd; nd = rb_next(nd)) {
+ struct machine *pos = rb_entry(nd, struct machine, rb_node);
+ ret += __dsos__fprintf_buildid(&pos->kernel_dsos, fp, with_hits);
+ ret += __dsos__fprintf_buildid(&pos->user_dsos, fp, with_hits);
}
return ret;
}
@@ -1963,14 +1958,12 @@ struct dso *dso__new_kernel(const char *name)
return self;
}
-static struct dso *dso__new_guest_kernel(struct kernel_info *kerninfo,
+static struct dso *dso__new_guest_kernel(struct machine *machine,
const char *name)
{
- char buff[PATH_MAX];
- struct dso *self;
+ char bf[PATH_MAX];
+ struct dso *self = dso__new(name ?: machine__mmap_name(machine, bf, sizeof(bf)));
- kern_mmap_name(kerninfo, buff);
- self = dso__new(name ?: buff);
if (self != NULL) {
dso__set_short_name(self, "[guest.kernel]");
self->kernel = DSO_TYPE_GUEST_KERNEL;
@@ -1979,64 +1972,78 @@ static struct dso *dso__new_guest_kernel(struct kernel_info *kerninfo,
return self;
}
-void dso__read_running_kernel_build_id(struct dso *self,
- struct kernel_info *kerninfo)
+void dso__read_running_kernel_build_id(struct dso *self, struct machine *machine)
{
char path[PATH_MAX];
- if (is_default_guest(kerninfo))
+ if (machine__is_default_guest(machine))
return;
- sprintf(path, "%s/sys/kernel/notes", kerninfo->root_dir);
+ sprintf(path, "%s/sys/kernel/notes", machine->root_dir);
if (sysfs__read_build_id(path, self->build_id,
sizeof(self->build_id)) == 0)
self->has_build_id = true;
}
-static struct dso *dsos__create_kernel(struct kernel_info *kerninfo)
+static struct dso *machine__create_kernel(struct machine *self)
{
const char *vmlinux_name = NULL;
struct dso *kernel;
- if (is_host_kernel(kerninfo)) {
+ if (machine__is_host(self)) {
vmlinux_name = symbol_conf.vmlinux_name;
kernel = dso__new_kernel(vmlinux_name);
} else {
- if (is_default_guest(kerninfo))
+ if (machine__is_default_guest(self))
vmlinux_name = symbol_conf.default_guest_vmlinux_name;
- kernel = dso__new_guest_kernel(kerninfo, vmlinux_name);
+ kernel = dso__new_guest_kernel(self, vmlinux_name);
}
if (kernel != NULL) {
- dso__read_running_kernel_build_id(kernel, kerninfo);
- dsos__add(&kerninfo->dsos__kernel, kernel);
+ dso__read_running_kernel_build_id(kernel, self);
+ dsos__add(&self->kernel_dsos, kernel);
}
return kernel;
}
-int __map_groups__create_kernel_maps(struct map_groups *self,
- struct map *vmlinux_maps[MAP__NR_TYPES],
- struct dso *kernel)
+int __machine__create_kernel_maps(struct machine *self, struct dso *kernel)
{
enum map_type type;
for (type = 0; type < MAP__NR_TYPES; ++type) {
struct kmap *kmap;
- vmlinux_maps[type] = map__new2(0, kernel, type);
- if (vmlinux_maps[type] == NULL)
+ self->vmlinux_maps[type] = map__new2(0, kernel, type);
+ if (self->vmlinux_maps[type] == NULL)
return -1;
- vmlinux_maps[type]->map_ip =
- vmlinux_maps[type]->unmap_ip = identity__map_ip;
+ self->vmlinux_maps[type]->map_ip =
+ self->vmlinux_maps[type]->unmap_ip = identity__map_ip;
- kmap = map__kmap(vmlinux_maps[type]);
- kmap->kmaps = self;
- map_groups__insert(self, vmlinux_maps[type]);
+ kmap = map__kmap(self->vmlinux_maps[type]);
+ kmap->kmaps = &self->kmaps;
+ map_groups__insert(&self->kmaps, self->vmlinux_maps[type]);
}
return 0;
}
+int machine__create_kernel_maps(struct machine *self)
+{
+ struct dso *kernel = machine__create_kernel(self);
+
+ if (kernel == NULL ||
+ __machine__create_kernel_maps(self, kernel) < 0)
+ return -1;
+
+ if (symbol_conf.use_modules && machine__create_modules(self) < 0)
+ pr_debug("Problems creating module maps, continuing anyway...\n");
+ /*
+ * Now that we have all the maps created, just set the ->end of them:
+ */
+ map_groups__fixup_end(&self->kmaps);
+ return 0;
+}
+
static void vmlinux_path__exit(void)
{
while (--vmlinux_path__nr_entries >= 0) {
@@ -2153,30 +2160,14 @@ out_free_comm_list:
return -1;
}
-int map_groups__create_kernel_maps(struct rb_root *kerninfo_root, pid_t pid)
+int machines__create_kernel_maps(struct rb_root *self, pid_t pid)
{
- struct kernel_info *kerninfo;
- struct dso *kernel;
-
- kerninfo = kerninfo__findnew(kerninfo_root, pid);
- if (kerninfo == NULL)
- return -1;
- kernel = dsos__create_kernel(kerninfo);
- if (kernel == NULL)
- return -1;
+ struct machine *machine = machines__findnew(self, pid);
- if (__map_groups__create_kernel_maps(&kerninfo->kmaps,
- kerninfo->vmlinux_maps, kernel) < 0)
+ if (machine == NULL)
return -1;
- if (symbol_conf.use_modules &&
- map_groups__create_modules(kerninfo) < 0)
- pr_debug("Problems creating module maps, continuing anyway...\n");
- /*
- * Now that we have all the maps created, just set the ->end of them:
- */
- map_groups__fixup_end(&kerninfo->kmaps);
- return 0;
+ return machine__create_kernel_maps(machine);
}
static int hex(char ch)
@@ -2222,7 +2213,7 @@ char *strxfrchar(char *s, char from, char to)
return s;
}
-int map_groups__create_guest_kernel_maps(struct rb_root *kerninfo_root)
+int machines__create_guest_kernel_maps(struct rb_root *self)
{
int ret = 0;
struct dirent **namelist = NULL;
@@ -2233,8 +2224,7 @@ int map_groups__create_guest_kernel_maps(struct rb_root *kerninfo_root)
if (symbol_conf.default_guest_vmlinux_name ||
symbol_conf.default_guest_modules ||
symbol_conf.default_guest_kallsyms) {
- map_groups__create_kernel_maps(kerninfo_root,
- DEFAULT_GUEST_KERNEL_ID);
+ machines__create_kernel_maps(self, DEFAULT_GUEST_KERNEL_ID);
}
if (symbol_conf.guestmount) {
@@ -2255,8 +2245,7 @@ int map_groups__create_guest_kernel_maps(struct rb_root *kerninfo_root)
pr_debug("Can't access file %s\n", path);
goto failure;
}
- map_groups__create_kernel_maps(kerninfo_root,
- pid);
+ machines__create_kernel_maps(self, pid);
}
failure:
free(namelist);
@@ -2264,3 +2253,36 @@ failure:
return ret;
}
+
+int machine__load_kallsyms(struct machine *self, const char *filename,
+ enum map_type type, symbol_filter_t filter)
+{
+ struct map *map = self->vmlinux_maps[type];
+ int ret = dso__load_kallsyms(map->dso, filename, map, filter);
+
+ if (ret > 0) {
+ dso__set_loaded(map->dso, type);
+ /*
+ * Since /proc/kallsyms will have multiple sessions for the
+ * kernel, with modules between them, fixup the end of all
+ * sections.
+ */
+ __map_groups__fixup_end(&self->kmaps, type);
+ }
+
+ return ret;
+}
+
+int machine__load_vmlinux_path(struct machine *self, enum map_type type,
+ symbol_filter_t filter)
+{
+ struct map *map = self->vmlinux_maps[type];
+ int ret = dso__load_vmlinux_path(map->dso, map, filter);
+
+ if (ret > 0) {
+ dso__set_loaded(map->dso, type);
+ map__reloc_vmlinux(map);
+ }
+
+ return ret;
+}
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 478f5ab3778..032469e4187 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -54,6 +54,7 @@ struct symbol {
struct rb_node rb_node;
u64 start;
u64 end;
+ u16 namelen;
char name[0];
};
@@ -77,7 +78,7 @@ struct symbol_conf {
*default_guest_kallsyms,
*default_guest_modules;
const char *guestmount;
- char *dso_list_str,
+ const char *dso_list_str,
*comm_list_str,
*sym_list_str,
*col_width_list_str;
@@ -162,9 +163,15 @@ int dso__load_vmlinux_path(struct dso *self, struct map *map,
symbol_filter_t filter);
int dso__load_kallsyms(struct dso *self, const char *filename, struct map *map,
symbol_filter_t filter);
-void dsos__fprintf(struct rb_root *kerninfo_root, FILE *fp);
-size_t dsos__fprintf_buildid(struct rb_root *kerninfo_root,
- FILE *fp, bool with_hits);
+int machine__load_kallsyms(struct machine *self, const char *filename,
+ enum map_type type, symbol_filter_t filter);
+int machine__load_vmlinux_path(struct machine *self, enum map_type type,
+ symbol_filter_t filter);
+
+size_t __dsos__fprintf(struct list_head *head, FILE *fp);
+
+size_t machines__fprintf_dsos(struct rb_root *self, FILE *fp);
+size_t machines__fprintf_dsos_buildid(struct rb_root *self, FILE *fp, bool with_hits);
size_t dso__fprintf_buildid(struct dso *self, FILE *fp);
size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp);
@@ -186,8 +193,7 @@ enum dso_origin {
char dso__symtab_origin(const struct dso *self);
void dso__set_long_name(struct dso *self, char *name);
void dso__set_build_id(struct dso *self, void *build_id);
-void dso__read_running_kernel_build_id(struct dso *self,
- struct kernel_info *kerninfo);
+void dso__read_running_kernel_build_id(struct dso *self, struct machine *machine);
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);
@@ -200,11 +206,11 @@ int kallsyms__parse(const char *filename, void *arg,
int (*process_symbol)(void *arg, const char *name,
char type, u64 start));
-int __map_groups__create_kernel_maps(struct map_groups *self,
- struct map *vmlinux_maps[MAP__NR_TYPES],
- struct dso *kernel);
-int map_groups__create_kernel_maps(struct rb_root *kerninfo_root, pid_t pid);
-int map_groups__create_guest_kernel_maps(struct rb_root *kerninfo_root);
+int __machine__create_kernel_maps(struct machine *self, struct dso *kernel);
+int machine__create_kernel_maps(struct machine *self);
+
+int machines__create_kernel_maps(struct rb_root *self, pid_t pid);
+int machines__create_guest_kernel_maps(struct rb_root *self);
int symbol__init(void);
bool symbol_type__is_a(char symbol_type, enum map_type map_type);
diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c
index 30cd9b57595..b1572601286 100644
--- a/tools/perf/util/trace-event-info.c
+++ b/tools/perf/util/trace-event-info.c
@@ -487,6 +487,17 @@ get_tracepoints_path(struct perf_event_attr *pattrs, int nb_events)
return nr_tracepoints > 0 ? path.next : NULL;
}
+bool have_tracepoints(struct perf_event_attr *pattrs, int nb_events)
+{
+ int i;
+
+ for (i = 0; i < nb_events; i++)
+ if (pattrs[i].type == PERF_TYPE_TRACEPOINT)
+ return true;
+
+ return false;
+}
+
int read_tracing_data(int fd, struct perf_event_attr *pattrs, int nb_events)
{
char buf[BUFSIZ];
diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c
index d6ef414075a..73a02223c62 100644
--- a/tools/perf/util/trace-event-parse.c
+++ b/tools/perf/util/trace-event-parse.c
@@ -691,11 +691,6 @@ static int __read_expected(enum event_type expect, const char *str,
return ret;
}
-static int read_expected_warn(enum event_type expect, const char *str, bool warn)
-{
- return __read_expected(expect, str, 1, warn);
-}
-
static int read_expected(enum event_type expect, const char *str)
{
return __read_expected(expect, str, 1, true);
@@ -1942,7 +1937,7 @@ void *raw_field_ptr(struct event *event, const char *name, void *data)
if (!field)
return NULL;
- if (field->flags & FIELD_IS_STRING) {
+ if (field->flags & FIELD_IS_DYNAMIC) {
int offset;
offset = *(int *)(data + field->offset);
@@ -3104,90 +3099,6 @@ static void print_args(struct print_arg *args)
}
}
-static void parse_header_field(const char *field,
- int *offset, int *size, bool warn)
-{
- char *token;
- int type;
-
- if (read_expected(EVENT_ITEM, "field") < 0)
- return;
- if (read_expected(EVENT_OP, ":") < 0)
- return;
-
- /* type */
- if (read_expect_type(EVENT_ITEM, &token) < 0)
- goto fail;
- free_token(token);
-
- if (read_expected_warn(EVENT_ITEM, field, warn) < 0)
- return;
- if (read_expected(EVENT_OP, ";") < 0)
- return;
- if (read_expected(EVENT_ITEM, "offset") < 0)
- return;
- if (read_expected(EVENT_OP, ":") < 0)
- return;
- if (read_expect_type(EVENT_ITEM, &token) < 0)
- goto fail;
- *offset = atoi(token);
- free_token(token);
- if (read_expected(EVENT_OP, ";") < 0)
- return;
- if (read_expected(EVENT_ITEM, "size") < 0)
- return;
- if (read_expected(EVENT_OP, ":") < 0)
- return;
- if (read_expect_type(EVENT_ITEM, &token) < 0)
- goto fail;
- *size = atoi(token);
- free_token(token);
- if (read_expected(EVENT_OP, ";") < 0)
- return;
- type = read_token(&token);
- if (type != EVENT_NEWLINE) {
- /* newer versions of the kernel have a "signed" type */
- if (type != EVENT_ITEM)
- goto fail;
-
- if (strcmp(token, "signed") != 0)
- goto fail;
-
- free_token(token);
-
- if (read_expected(EVENT_OP, ":") < 0)
- return;
-
- if (read_expect_type(EVENT_ITEM, &token))
- goto fail;
-
- free_token(token);
- if (read_expected(EVENT_OP, ";") < 0)
- return;
-
- if (read_expect_type(EVENT_NEWLINE, &token))
- goto fail;
- }
- fail:
- free_token(token);
-}
-
-int parse_header_page(char *buf, unsigned long size)
-{
- init_input_buf(buf, size);
-
- parse_header_field("timestamp", &header_page_ts_offset,
- &header_page_ts_size, true);
- parse_header_field("commit", &header_page_size_offset,
- &header_page_size_size, true);
- parse_header_field("overwrite", &header_page_overwrite_offset,
- &header_page_overwrite_size, false);
- parse_header_field("data", &header_page_data_offset,
- &header_page_data_size, true);
-
- return 0;
-}
-
int parse_ftrace_file(char *buf, unsigned long size)
{
struct format_field *field;
diff --git a/tools/perf/util/trace-event-read.c b/tools/perf/util/trace-event-read.c
index 44889c9b563..cb54cd002f4 100644
--- a/tools/perf/util/trace-event-read.c
+++ b/tools/perf/util/trace-event-read.c
@@ -51,6 +51,13 @@ static int long_size;
static unsigned long page_size;
static ssize_t calc_data_size;
+static bool repipe;
+
+/* If it fails, the next read will report it */
+static void skip(int size)
+{
+ lseek(input_fd, size, SEEK_CUR);
+}
static int do_read(int fd, void *buf, int size)
{
@@ -62,6 +69,13 @@ static int do_read(int fd, void *buf, int size)
if (ret <= 0)
return -1;
+ if (repipe) {
+ int retw = write(STDOUT_FILENO, buf, ret);
+
+ if (retw <= 0 || retw != ret)
+ die("repiping input file");
+ }
+
size -= ret;
buf += ret;
}
@@ -116,6 +130,13 @@ static char *read_string(void)
if (!r)
die("no data");
+ if (repipe) {
+ int retw = write(STDOUT_FILENO, &c, 1);
+
+ if (retw <= 0 || retw != r)
+ die("repiping input file string");
+ }
+
buf[size++] = c;
if (!c)
@@ -169,7 +190,6 @@ static void read_ftrace_printk(void)
static void read_header_files(void)
{
unsigned long long size;
- char *header_page;
char *header_event;
char buf[BUFSIZ];
@@ -179,10 +199,7 @@ static void read_header_files(void)
die("did not read header page");
size = read8();
- header_page = malloc_or_die(size);
- read_or_die(header_page, size);
- parse_header_page(header_page, size);
- free(header_page);
+ skip(size);
/*
* The size field in the page is of type long,
@@ -454,7 +471,7 @@ struct record *trace_read_data(int cpu)
return data;
}
-ssize_t trace_report(int fd)
+ssize_t trace_report(int fd, bool __repipe)
{
char buf[BUFSIZ];
char test[] = { 23, 8, 68 };
@@ -465,6 +482,7 @@ ssize_t trace_report(int fd)
ssize_t size;
calc_data_size = 1;
+ repipe = __repipe;
input_fd = fd;
@@ -499,6 +517,7 @@ ssize_t trace_report(int fd)
size = calc_data_size - 1;
calc_data_size = 0;
+ repipe = false;
if (show_funcs) {
print_funcs();
diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h
index 1f45d468fd9..406d452956d 100644
--- a/tools/perf/util/trace-event.h
+++ b/tools/perf/util/trace-event.h
@@ -163,7 +163,7 @@ struct record *trace_read_data(int cpu);
void parse_set_info(int nr_cpus, int long_sz);
-ssize_t trace_report(int fd);
+ssize_t trace_report(int fd, bool repipe);
void *malloc_or_die(unsigned int size);
@@ -244,7 +244,6 @@ extern int header_page_data_size;
extern bool latency_format;
-int parse_header_page(char *buf, unsigned long size);
int trace_parse_common_type(void *data);
int trace_parse_common_pid(void *data);
int parse_common_pc(void *data);
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c
index f9b890fde68..214265674dd 100644
--- a/tools/perf/util/util.c
+++ b/tools/perf/util/util.c
@@ -92,3 +92,25 @@ out_close_from:
out:
return err;
}
+
+unsigned long convert_unit(unsigned long value, char *unit)
+{
+ *unit = ' ';
+
+ if (value > 1000) {
+ value /= 1000;
+ *unit = 'K';
+ }
+
+ if (value > 1000) {
+ value /= 1000;
+ *unit = 'M';
+ }
+
+ if (value > 1000) {
+ value /= 1000;
+ *unit = 'G';
+ }
+
+ return value;
+}
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h
index fbf45d1b26f..0795bf304b1 100644
--- a/tools/perf/util/util.h
+++ b/tools/perf/util/util.h
@@ -423,6 +423,7 @@ char **argv_split(const char *str, int *argcp);
void argv_free(char **argv);
bool strglobmatch(const char *str, const char *pat);
bool strlazymatch(const char *str, const char *pat);
+unsigned long convert_unit(unsigned long value, char *unit);
#define _STR(x) #x
#define STR(x) _STR(x)