aboutsummaryrefslogtreecommitdiff
path: root/tools/perf/util
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/util')
-rw-r--r--tools/perf/util/event.c64
-rw-r--r--tools/perf/util/event.h4
-rw-r--r--tools/perf/util/session.c46
-rw-r--r--tools/perf/util/session.h10
-rw-r--r--tools/perf/util/symbol.c7
5 files changed, 126 insertions, 5 deletions
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
index bb0fd6da2d5..1a31feb9999 100644
--- a/tools/perf/util/event.c
+++ b/tools/perf/util/event.c
@@ -189,6 +189,50 @@ void event__synthesize_threads(int (*process)(event_t *event,
closedir(proc);
}
+struct process_symbol_args {
+ const char *name;
+ u64 start;
+};
+
+static int find_symbol_cb(void *arg, const char *name, char type, u64 start)
+{
+ struct process_symbol_args *args = arg;
+
+ if (!symbol_type__is_a(type, MAP__FUNCTION) || strcmp(name, args->name))
+ return 0;
+
+ args->start = start;
+ return 1;
+}
+
+int event__synthesize_kernel_mmap(int (*process)(event_t *event,
+ struct perf_session *session),
+ struct perf_session *session,
+ const char *symbol_name)
+{
+ size_t size;
+ event_t ev = {
+ .header = { .type = PERF_RECORD_MMAP },
+ };
+ /*
+ * We should get this from /sys/kernel/sections/.text, but till that is
+ * available use this, and after it is use this as a fallback for older
+ * kernels.
+ */
+ struct process_symbol_args args = { .name = symbol_name, };
+
+ if (kallsyms__parse(&args, find_symbol_cb) <= 0)
+ return -ENOENT;
+
+ size = snprintf(ev.mmap.filename, sizeof(ev.mmap.filename),
+ "[kernel.kallsyms.%s]", symbol_name) + 1;
+ size = ALIGN(size, sizeof(u64));
+ ev.mmap.header.size = (sizeof(ev.mmap) - (sizeof(ev.mmap.filename) - size));
+ ev.mmap.start = args.start;
+
+ return process(&ev, session);
+}
+
static void thread__comm_adjust(struct thread *self)
{
char *comm = self->comm;
@@ -240,9 +284,9 @@ int event__process_lost(event_t *self, struct perf_session *session)
int event__process_mmap(event_t *self, struct perf_session *session)
{
- struct thread *thread = perf_session__findnew(session, self->mmap.pid);
- struct map *map = map__new(&self->mmap, MAP__FUNCTION,
- session->cwd, session->cwdlen);
+ struct thread *thread;
+ struct map *map;
+ static const char kmmap_prefix[] = "[kernel.kallsyms.";
dump_printf(" %d/%d: [%p(%p) @ %p]: %s\n",
self->mmap.pid, self->mmap.tid,
@@ -251,6 +295,20 @@ int event__process_mmap(event_t *self, struct perf_session *session)
(void *)(long)self->mmap.pgoff,
self->mmap.filename);
+ if (self->mmap.pid == 0 &&
+ memcmp(self->mmap.filename, kmmap_prefix,
+ sizeof(kmmap_prefix) - 1) == 0) {
+ const char *symbol_name = (self->mmap.filename +
+ sizeof(kmmap_prefix) - 1);
+ perf_session__set_kallsyms_ref_reloc_sym(session, symbol_name,
+ self->mmap.start);
+ return 0;
+ }
+
+ thread = perf_session__findnew(session, self->mmap.pid);
+ map = map__new(&self->mmap, MAP__FUNCTION,
+ session->cwd, session->cwdlen);
+
if (thread == NULL || map == NULL)
dump_printf("problem processing PERF_RECORD_MMAP, skipping event.\n");
else
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
index 80fb3653c80..61fc0dc658c 100644
--- a/tools/perf/util/event.h
+++ b/tools/perf/util/event.h
@@ -110,6 +110,10 @@ int event__synthesize_thread(pid_t pid,
void event__synthesize_threads(int (*process)(event_t *event,
struct perf_session *session),
struct perf_session *session);
+int event__synthesize_kernel_mmap(int (*process)(event_t *event,
+ struct perf_session *session),
+ struct perf_session *session,
+ const char *symbol_name);
int event__process_comm(event_t *self, struct perf_session *session);
int event__process_lost(event_t *self, struct perf_session *session);
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index 7f0537d1add..e0e6a075489 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -401,3 +401,49 @@ bool perf_session__has_traces(struct perf_session *self, const char *msg)
return true;
}
+
+int perf_session__set_kallsyms_ref_reloc_sym(struct perf_session *self,
+ const char *symbol_name,
+ u64 addr)
+{
+ char *bracket;
+
+ self->ref_reloc_sym.name = strdup(symbol_name);
+ if (self->ref_reloc_sym.name == NULL)
+ return -ENOMEM;
+
+ bracket = strchr(self->ref_reloc_sym.name, ']');
+ if (bracket)
+ *bracket = '\0';
+
+ self->ref_reloc_sym.addr = addr;
+ return 0;
+}
+
+static u64 map__reloc_map_ip(struct map *map, u64 ip)
+{
+ return ip + (s64)map->pgoff;
+}
+
+static u64 map__reloc_unmap_ip(struct map *map, u64 ip)
+{
+ return ip - (s64)map->pgoff;
+}
+
+void perf_session__reloc_vmlinux_maps(struct perf_session *self,
+ u64 unrelocated_addr)
+{
+ enum map_type type;
+ s64 reloc = unrelocated_addr - self->ref_reloc_sym.addr;
+
+ if (!reloc)
+ return;
+
+ for (type = 0; type < MAP__NR_TYPES; ++type) {
+ struct map *map = self->vmlinux_maps[type];
+
+ map->map_ip = map__reloc_map_ip;
+ map->unmap_ip = map__reloc_unmap_ip;
+ map->pgoff = reloc;
+ }
+}
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
index 8db37bbf0e6..d4a9d20f8d4 100644
--- a/tools/perf/util/session.h
+++ b/tools/perf/util/session.h
@@ -24,6 +24,10 @@ struct perf_session {
unsigned long unknown_events;
struct rb_root hists;
u64 sample_type;
+ struct {
+ const char *name;
+ u64 addr;
+ } ref_reloc_sym;
int fd;
int cwdlen;
char *cwd;
@@ -59,4 +63,10 @@ bool perf_session__has_traces(struct perf_session *self, const char *msg);
int perf_header__read_build_ids(int input, u64 offset, u64 file_size);
+int perf_session__set_kallsyms_ref_reloc_sym(struct perf_session *self,
+ const char *symbol_name,
+ u64 addr);
+void perf_session__reloc_vmlinux_maps(struct perf_session *self,
+ u64 unrelocated_addr);
+
#endif /* __PERF_SESSION_H */
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index e290429e9c0..da2f07f1af8 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -956,11 +956,15 @@ static int dso__load_sym(struct dso *self, struct map *map,
elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) {
struct symbol *f;
- const char *elf_name;
+ const char *elf_name = elf_sym__name(&sym, symstrs);
char *demangled = NULL;
int is_label = elf_sym__is_label(&sym);
const char *section_name;
+ if (kernel && session->ref_reloc_sym.name != NULL &&
+ strcmp(elf_name, session->ref_reloc_sym.name) == 0)
+ perf_session__reloc_vmlinux_maps(session, sym.st_value);
+
if (!is_label && !elf_sym__is_a(&sym, map->type))
continue;
@@ -973,7 +977,6 @@ static int dso__load_sym(struct dso *self, struct map *map,
if (is_label && !elf_sec__is_a(&shdr, secstrs, map->type))
continue;
- elf_name = elf_sym__name(&sym, symstrs);
section_name = elf_sec__name(&shdr, secstrs);
if (kernel || kmodule) {