aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf
diff options
context:
space:
mode:
authorArnaldo Carvalho de Melo <acme@redhat.com>2010-01-05 13:50:31 -0500
committerIngo Molnar <mingo@elte.hu>2010-01-13 04:09:11 -0500
commit56b03f3c4d641dbdbce2e52a2969712e85b0e030 (patch)
tree3c15eb7ed163ca63bed395b233535f6fca2cccec /tools/perf
parentb9a63b9b56d6910a25e3d4905525aef150420a9b (diff)
perf tools: Handle relocatable kernels
DSOs don't have this problem because the kernel emits a PERF_MMAP for each new executable mapping it performs on monitored threads. To fix the kernel case we simulate the same behaviour, by having 'perf record' to synthesize a PERF_MMAP for the kernel, encoded like this: [root@doppio ~]# perf record -a -f sleep 1 [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.344 MB perf.data (~15038 samples) ] [root@doppio ~]# perf report -D | head -10 0xd0 [0x40]: event: 1 . . ... raw event: size 64 bytes . 0000: 01 00 00 00 00 00 40 00 00 00 00 00 00 00 00 00 ......@........ . 0010: 00 00 00 81 ff ff ff ff 00 00 00 00 00 00 00 00 ............... . 0020: 00 00 00 00 00 00 00 00 5b 6b 65 72 6e 65 6c 2e ........ [kernel . 0030: 6b 61 6c 6c 73 79 6d 73 2e 5f 74 65 78 74 5d 00 kallsyms._text] . 0xd0 [0x40]: PERF_RECORD_MMAP 0/0: [0xffffffff81000000((nil)) @ (nil)]: [kernel.kallsyms._text] I.e. we identify such event as having: .pid = 0 .filename = [kernel.kallsyms.REFNAME] .start = REFNAME addr in /proc/kallsyms at 'perf record' time and use now a hardcoded value of '.text' for REFNAME. Then, later, in 'perf report', if there are any kernel hits and thus we need to resolve kernel symbols, we search for REFNAME and if its address changed, relocation happened and we thus must change the kernel mapping routines to one that uses .pgoff as the relocation to apply. This way we use the same mechanism used for the other DSOs and don't have to do a two pass in all the kernel symbols. Reported-by: Xiao Guangrong <xiaoguangrong@cn.fujitsu.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Frédéric Weisbecker <fweisbec@gmail.com> Cc: "H. Peter Anvin" <hpa@zytor.com> Cc: Mike Galbraith <efault@gmx.de> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: Paul Mackerras <paulus@samba.org> Cc: Xiao Guangrong <xiaoguangrong@cn.fujitsu.com> LKML-Reference: <1262717431-1246-1-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'tools/perf')
-rw-r--r--tools/perf/builtin-record.c7
-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
6 files changed, 133 insertions, 5 deletions
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 265425322734..8f88420e066b 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -551,6 +551,13 @@ static int __cmd_record(int argc, const char **argv)
551 return err; 551 return err;
552 } 552 }
553 553
554 err = event__synthesize_kernel_mmap(process_synthesized_event,
555 session, "_text");
556 if (err < 0) {
557 pr_err("Couldn't record kernel reference relocation symbol.\n");
558 return err;
559 }
560
554 if (!system_wide && profile_cpu == -1) 561 if (!system_wide && profile_cpu == -1)
555 event__synthesize_thread(pid, process_synthesized_event, 562 event__synthesize_thread(pid, process_synthesized_event,
556 session); 563 session);
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
index bb0fd6da2d56..1a31feb9999f 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,
189 closedir(proc); 189 closedir(proc);
190} 190}
191 191
192struct process_symbol_args {
193 const char *name;
194 u64 start;
195};
196
197static int find_symbol_cb(void *arg, const char *name, char type, u64 start)
198{
199 struct process_symbol_args *args = arg;
200
201 if (!symbol_type__is_a(type, MAP__FUNCTION) || strcmp(name, args->name))
202 return 0;
203
204 args->start = start;
205 return 1;
206}
207
208int event__synthesize_kernel_mmap(int (*process)(event_t *event,
209 struct perf_session *session),
210 struct perf_session *session,
211 const char *symbol_name)
212{
213 size_t size;
214 event_t ev = {
215 .header = { .type = PERF_RECORD_MMAP },
216 };
217 /*
218 * We should get this from /sys/kernel/sections/.text, but till that is
219 * available use this, and after it is use this as a fallback for older
220 * kernels.
221 */
222 struct process_symbol_args args = { .name = symbol_name, };
223
224 if (kallsyms__parse(&args, find_symbol_cb) <= 0)
225 return -ENOENT;
226
227 size = snprintf(ev.mmap.filename, sizeof(ev.mmap.filename),
228 "[kernel.kallsyms.%s]", symbol_name) + 1;
229 size = ALIGN(size, sizeof(u64));
230 ev.mmap.header.size = (sizeof(ev.mmap) - (sizeof(ev.mmap.filename) - size));
231 ev.mmap.start = args.start;
232
233 return process(&ev, session);
234}
235
192static void thread__comm_adjust(struct thread *self) 236static void thread__comm_adjust(struct thread *self)
193{ 237{
194 char *comm = self->comm; 238 char *comm = self->comm;
@@ -240,9 +284,9 @@ int event__process_lost(event_t *self, struct perf_session *session)
240 284
241int event__process_mmap(event_t *self, struct perf_session *session) 285int event__process_mmap(event_t *self, struct perf_session *session)
242{ 286{
243 struct thread *thread = perf_session__findnew(session, self->mmap.pid); 287 struct thread *thread;
244 struct map *map = map__new(&self->mmap, MAP__FUNCTION, 288 struct map *map;
245 session->cwd, session->cwdlen); 289 static const char kmmap_prefix[] = "[kernel.kallsyms.";
246 290
247 dump_printf(" %d/%d: [%p(%p) @ %p]: %s\n", 291 dump_printf(" %d/%d: [%p(%p) @ %p]: %s\n",
248 self->mmap.pid, self->mmap.tid, 292 self->mmap.pid, self->mmap.tid,
@@ -251,6 +295,20 @@ int event__process_mmap(event_t *self, struct perf_session *session)
251 (void *)(long)self->mmap.pgoff, 295 (void *)(long)self->mmap.pgoff,
252 self->mmap.filename); 296 self->mmap.filename);
253 297
298 if (self->mmap.pid == 0 &&
299 memcmp(self->mmap.filename, kmmap_prefix,
300 sizeof(kmmap_prefix) - 1) == 0) {
301 const char *symbol_name = (self->mmap.filename +
302 sizeof(kmmap_prefix) - 1);
303 perf_session__set_kallsyms_ref_reloc_sym(session, symbol_name,
304 self->mmap.start);
305 return 0;
306 }
307
308 thread = perf_session__findnew(session, self->mmap.pid);
309 map = map__new(&self->mmap, MAP__FUNCTION,
310 session->cwd, session->cwdlen);
311
254 if (thread == NULL || map == NULL) 312 if (thread == NULL || map == NULL)
255 dump_printf("problem processing PERF_RECORD_MMAP, skipping event.\n"); 313 dump_printf("problem processing PERF_RECORD_MMAP, skipping event.\n");
256 else 314 else
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
index 80fb3653c809..61fc0dc658c2 100644
--- a/tools/perf/util/event.h
+++ b/tools/perf/util/event.h
@@ -110,6 +110,10 @@ int event__synthesize_thread(pid_t pid,
110void event__synthesize_threads(int (*process)(event_t *event, 110void event__synthesize_threads(int (*process)(event_t *event,
111 struct perf_session *session), 111 struct perf_session *session),
112 struct perf_session *session); 112 struct perf_session *session);
113int event__synthesize_kernel_mmap(int (*process)(event_t *event,
114 struct perf_session *session),
115 struct perf_session *session,
116 const char *symbol_name);
113 117
114int event__process_comm(event_t *self, struct perf_session *session); 118int event__process_comm(event_t *self, struct perf_session *session);
115int event__process_lost(event_t *self, struct perf_session *session); 119int 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 7f0537d1add8..e0e6a075489e 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)
401 401
402 return true; 402 return true;
403} 403}
404
405int perf_session__set_kallsyms_ref_reloc_sym(struct perf_session *self,
406 const char *symbol_name,
407 u64 addr)
408{
409 char *bracket;
410
411 self->ref_reloc_sym.name = strdup(symbol_name);
412 if (self->ref_reloc_sym.name == NULL)
413 return -ENOMEM;
414
415 bracket = strchr(self->ref_reloc_sym.name, ']');
416 if (bracket)
417 *bracket = '\0';
418
419 self->ref_reloc_sym.addr = addr;
420 return 0;
421}
422
423static u64 map__reloc_map_ip(struct map *map, u64 ip)
424{
425 return ip + (s64)map->pgoff;
426}
427
428static u64 map__reloc_unmap_ip(struct map *map, u64 ip)
429{
430 return ip - (s64)map->pgoff;
431}
432
433void perf_session__reloc_vmlinux_maps(struct perf_session *self,
434 u64 unrelocated_addr)
435{
436 enum map_type type;
437 s64 reloc = unrelocated_addr - self->ref_reloc_sym.addr;
438
439 if (!reloc)
440 return;
441
442 for (type = 0; type < MAP__NR_TYPES; ++type) {
443 struct map *map = self->vmlinux_maps[type];
444
445 map->map_ip = map__reloc_map_ip;
446 map->unmap_ip = map__reloc_unmap_ip;
447 map->pgoff = reloc;
448 }
449}
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
index 8db37bbf0e62..d4a9d20f8d44 100644
--- a/tools/perf/util/session.h
+++ b/tools/perf/util/session.h
@@ -24,6 +24,10 @@ struct perf_session {
24 unsigned long unknown_events; 24 unsigned long unknown_events;
25 struct rb_root hists; 25 struct rb_root hists;
26 u64 sample_type; 26 u64 sample_type;
27 struct {
28 const char *name;
29 u64 addr;
30 } ref_reloc_sym;
27 int fd; 31 int fd;
28 int cwdlen; 32 int cwdlen;
29 char *cwd; 33 char *cwd;
@@ -59,4 +63,10 @@ bool perf_session__has_traces(struct perf_session *self, const char *msg);
59 63
60int perf_header__read_build_ids(int input, u64 offset, u64 file_size); 64int perf_header__read_build_ids(int input, u64 offset, u64 file_size);
61 65
66int perf_session__set_kallsyms_ref_reloc_sym(struct perf_session *self,
67 const char *symbol_name,
68 u64 addr);
69void perf_session__reloc_vmlinux_maps(struct perf_session *self,
70 u64 unrelocated_addr);
71
62#endif /* __PERF_SESSION_H */ 72#endif /* __PERF_SESSION_H */
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index e290429e9c00..da2f07f1af8f 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,
956 956
957 elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) { 957 elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) {
958 struct symbol *f; 958 struct symbol *f;
959 const char *elf_name; 959 const char *elf_name = elf_sym__name(&sym, symstrs);
960 char *demangled = NULL; 960 char *demangled = NULL;
961 int is_label = elf_sym__is_label(&sym); 961 int is_label = elf_sym__is_label(&sym);
962 const char *section_name; 962 const char *section_name;
963 963
964 if (kernel && session->ref_reloc_sym.name != NULL &&
965 strcmp(elf_name, session->ref_reloc_sym.name) == 0)
966 perf_session__reloc_vmlinux_maps(session, sym.st_value);
967
964 if (!is_label && !elf_sym__is_a(&sym, map->type)) 968 if (!is_label && !elf_sym__is_a(&sym, map->type))
965 continue; 969 continue;
966 970
@@ -973,7 +977,6 @@ static int dso__load_sym(struct dso *self, struct map *map,
973 if (is_label && !elf_sec__is_a(&shdr, secstrs, map->type)) 977 if (is_label && !elf_sec__is_a(&shdr, secstrs, map->type))
974 continue; 978 continue;
975 979
976 elf_name = elf_sym__name(&sym, symstrs);
977 section_name = elf_sec__name(&shdr, secstrs); 980 section_name = elf_sec__name(&shdr, secstrs);
978 981
979 if (kernel || kmodule) { 982 if (kernel || kmodule) {