aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/util
diff options
context:
space:
mode:
authorArnaldo Carvalho de Melo <acme@redhat.com>2010-01-13 10:22:17 -0500
committerIngo Molnar <mingo@elte.hu>2010-01-13 11:39:43 -0500
commitb7cece76783c68fb391f9882235b4b0c9c300c46 (patch)
tree8a0224493acc3cf74c218384a3b76b3e47c131a2 /tools/perf/util
parentff314d3903c2843de65c2148f66f277f2440ed26 (diff)
perf tools: Encode kernel module mappings in perf.data
We were always looking at the running machine /proc/modules, even when processing a perf.data file, which only makes sense when we're doing 'perf record' and 'perf report' on the same machine, and in close sucession, or if we don't use modules at all, right Peter? ;-) Now, at 'perf record' time we read /proc/modules, find the long path for modules, and put them as PERF_MMAP events, just like we did to encode the reloc reference symbol for vmlinux. Talking about that now it is encoded in .pgoff, so that we can use .{start,len} to store the address boundaries for the kernel so that when we reconstruct the kmaps tree we can do lookups right away, without having to fixup the end of the kernel maps like we did in the past (and now only in perf record). One more step in the 'perf archive' direction when we'll finally be able to collect data in one machine and analyse in another. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Frédéric Weisbecker <fweisbec@gmail.com> Cc: Mike Galbraith <efault@gmx.de> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: Paul Mackerras <paulus@samba.org> LKML-Reference: <1263396139-4798-1-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'tools/perf/util')
-rw-r--r--tools/perf/util/event.c106
-rw-r--r--tools/perf/util/event.h2
-rw-r--r--tools/perf/util/session.c8
-rw-r--r--tools/perf/util/symbol.c116
-rw-r--r--tools/perf/util/symbol.h3
-rw-r--r--tools/perf/util/thread.h4
6 files changed, 191 insertions, 48 deletions
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
index bfb3d872b9f..4f3e7ef33b8 100644
--- a/tools/perf/util/event.c
+++ b/tools/perf/util/event.c
@@ -154,6 +154,36 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid,
154 return 0; 154 return 0;
155} 155}
156 156
157int event__synthesize_modules(event__handler_t process,
158 struct perf_session *session)
159{
160 struct rb_node *nd;
161
162 for (nd = rb_first(&session->kmaps.maps[MAP__FUNCTION]);
163 nd; nd = rb_next(nd)) {
164 event_t ev;
165 size_t size;
166 struct map *pos = rb_entry(nd, struct map, rb_node);
167
168 if (pos->dso->kernel)
169 continue;
170
171 size = ALIGN(pos->dso->long_name_len + 1, sizeof(u64));
172 memset(&ev, 0, sizeof(ev));
173 ev.mmap.header.type = PERF_RECORD_MMAP;
174 ev.mmap.header.size = (sizeof(ev.mmap) -
175 (sizeof(ev.mmap.filename) - size));
176 ev.mmap.start = pos->start;
177 ev.mmap.len = pos->end - pos->start;
178
179 memcpy(ev.mmap.filename, pos->dso->long_name,
180 pos->dso->long_name_len + 1);
181 process(&ev, session);
182 }
183
184 return 0;
185}
186
157int event__synthesize_thread(pid_t pid, event__handler_t process, 187int event__synthesize_thread(pid_t pid, event__handler_t process,
158 struct perf_session *session) 188 struct perf_session *session)
159{ 189{
@@ -222,7 +252,9 @@ int event__synthesize_kernel_mmap(event__handler_t process,
222 "[kernel.kallsyms.%s]", symbol_name) + 1; 252 "[kernel.kallsyms.%s]", symbol_name) + 1;
223 size = ALIGN(size, sizeof(u64)); 253 size = ALIGN(size, sizeof(u64));
224 ev.mmap.header.size = (sizeof(ev.mmap) - (sizeof(ev.mmap.filename) - size)); 254 ev.mmap.header.size = (sizeof(ev.mmap) - (sizeof(ev.mmap.filename) - size));
225 ev.mmap.start = args.start; 255 ev.mmap.pgoff = args.start;
256 ev.mmap.start = session->vmlinux_maps[MAP__FUNCTION]->start;
257 ev.mmap.len = session->vmlinux_maps[MAP__FUNCTION]->end - ev.mmap.start ;
226 258
227 return process(&ev, session); 259 return process(&ev, session);
228} 260}
@@ -280,7 +312,6 @@ int event__process_mmap(event_t *self, struct perf_session *session)
280{ 312{
281 struct thread *thread; 313 struct thread *thread;
282 struct map *map; 314 struct map *map;
283 static const char kmmap_prefix[] = "[kernel.kallsyms.";
284 315
285 dump_printf(" %d/%d: [%p(%p) @ %p]: %s\n", 316 dump_printf(" %d/%d: [%p(%p) @ %p]: %s\n",
286 self->mmap.pid, self->mmap.tid, 317 self->mmap.pid, self->mmap.tid,
@@ -289,13 +320,61 @@ int event__process_mmap(event_t *self, struct perf_session *session)
289 (void *)(long)self->mmap.pgoff, 320 (void *)(long)self->mmap.pgoff,
290 self->mmap.filename); 321 self->mmap.filename);
291 322
292 if (self->mmap.pid == 0 && 323 if (self->mmap.pid == 0) {
293 memcmp(self->mmap.filename, kmmap_prefix, 324 static const char kmmap_prefix[] = "[kernel.kallsyms.";
294 sizeof(kmmap_prefix) - 1) == 0) { 325
295 const char *symbol_name = (self->mmap.filename + 326 if (self->mmap.filename[0] == '/') {
296 sizeof(kmmap_prefix) - 1); 327 char short_module_name[1024];
297 perf_session__set_kallsyms_ref_reloc_sym(session, symbol_name, 328 char *name = strrchr(self->mmap.filename, '/'), *dot;
298 self->mmap.start); 329
330 if (name == NULL)
331 goto out_problem;
332
333 ++name; /* skip / */
334 dot = strrchr(name, '.');
335 if (dot == NULL)
336 goto out_problem;
337
338 snprintf(short_module_name, sizeof(short_module_name),
339 "[%.*s]", (int)(dot - name), name);
340 strxfrchar(short_module_name, '-', '_');
341
342 map = perf_session__new_module_map(session,
343 self->mmap.start,
344 short_module_name);
345 if (map == NULL)
346 goto out_problem;
347
348 name = strdup(self->mmap.filename);
349 if (name == NULL)
350 goto out_problem;
351
352 dso__set_long_name(map->dso, name);
353 map->end = map->start + self->mmap.len;
354 } else if (memcmp(self->mmap.filename, kmmap_prefix,
355 sizeof(kmmap_prefix) - 1) == 0) {
356 const char *symbol_name = (self->mmap.filename +
357 sizeof(kmmap_prefix) - 1);
358 /*
359 * Should be there already, from the build-id table in
360 * the header.
361 */
362 struct dso *kernel = __dsos__findnew(&dsos__kernel,
363 "[kernel.kallsyms]");
364 if (kernel == NULL)
365 goto out_problem;
366
367 if (__map_groups__create_kernel_maps(&session->kmaps,
368 session->vmlinux_maps,
369 kernel) < 0)
370 goto out_problem;
371
372 session->vmlinux_maps[MAP__FUNCTION]->start = self->mmap.start;
373 session->vmlinux_maps[MAP__FUNCTION]->end = self->mmap.start + self->mmap.len;
374
375 perf_session__set_kallsyms_ref_reloc_sym(session, symbol_name,
376 self->mmap.pgoff);
377 }
299 return 0; 378 return 0;
300 } 379 }
301 380
@@ -304,10 +383,13 @@ int event__process_mmap(event_t *self, struct perf_session *session)
304 session->cwd, session->cwdlen); 383 session->cwd, session->cwdlen);
305 384
306 if (thread == NULL || map == NULL) 385 if (thread == NULL || map == NULL)
307 dump_printf("problem processing PERF_RECORD_MMAP, skipping event.\n"); 386 goto out_problem;
308 else 387
309 thread__insert_map(thread, map); 388 thread__insert_map(thread, map);
389 return 0;
310 390
391out_problem:
392 dump_printf("problem processing PERF_RECORD_MMAP, skipping event.\n");
311 return 0; 393 return 0;
312} 394}
313 395
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
index 80356da8216..50a7132887f 100644
--- a/tools/perf/util/event.h
+++ b/tools/perf/util/event.h
@@ -112,6 +112,8 @@ void event__synthesize_threads(event__handler_t process,
112int event__synthesize_kernel_mmap(event__handler_t process, 112int event__synthesize_kernel_mmap(event__handler_t process,
113 struct perf_session *session, 113 struct perf_session *session,
114 const char *symbol_name); 114 const char *symbol_name);
115int event__synthesize_modules(event__handler_t process,
116 struct perf_session *session);
115 117
116int event__process_comm(event_t *self, struct perf_session *session); 118int event__process_comm(event_t *self, struct perf_session *session);
117int 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 378ac5422bc..fd1c5a39a5b 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -69,9 +69,6 @@ struct perf_session *perf_session__new(const char *filename, int mode, bool forc
69 self->unknown_events = 0; 69 self->unknown_events = 0;
70 map_groups__init(&self->kmaps); 70 map_groups__init(&self->kmaps);
71 71
72 if (perf_session__create_kernel_maps(self) < 0)
73 goto out_delete;
74
75 if (mode == O_RDONLY && perf_session__open(self, force) < 0) 72 if (mode == O_RDONLY && perf_session__open(self, force) < 0)
76 goto out_delete; 73 goto out_delete;
77 74
@@ -268,8 +265,11 @@ int perf_header__read_build_ids(int input, u64 offset, u64 size)
268 head = &dsos__kernel; 265 head = &dsos__kernel;
269 266
270 dso = __dsos__findnew(head, filename); 267 dso = __dsos__findnew(head, filename);
271 if (dso != NULL) 268 if (dso != NULL) {
272 dso__set_build_id(dso, &bev.build_id); 269 dso__set_build_id(dso, &bev.build_id);
270 if (head == &dsos__kernel && filename[0] == '[')
271 dso->kernel = 1;
272 }
273 273
274 offset += bev.header.size; 274 offset += bev.header.size;
275 } 275 }
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 8e6627e6b77..381999dd5c1 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -161,7 +161,7 @@ static size_t symbol__fprintf(struct symbol *self, FILE *fp)
161 self->start, self->end, self->name); 161 self->start, self->end, self->name);
162} 162}
163 163
164static void dso__set_long_name(struct dso *self, char *name) 164void dso__set_long_name(struct dso *self, char *name)
165{ 165{
166 if (name == NULL) 166 if (name == NULL)
167 return; 167 return;
@@ -176,7 +176,7 @@ static void dso__set_basename(struct dso *self)
176 176
177struct dso *dso__new(const char *name) 177struct dso *dso__new(const char *name)
178{ 178{
179 struct dso *self = malloc(sizeof(*self) + strlen(name) + 1); 179 struct dso *self = zalloc(sizeof(*self) + strlen(name) + 1);
180 180
181 if (self != NULL) { 181 if (self != NULL) {
182 int i; 182 int i;
@@ -500,13 +500,17 @@ static int dso__split_kallsyms(struct dso *self, struct map *map,
500 500
501 *module++ = '\0'; 501 *module++ = '\0';
502 502
503 if (strcmp(self->name, module)) { 503 if (strcmp(curr_map->dso->short_name, module)) {
504 curr_map = map_groups__find_by_name(&session->kmaps, map->type, module); 504 curr_map = map_groups__find_by_name(&session->kmaps, map->type, module);
505 if (curr_map == NULL) { 505 if (curr_map == NULL) {
506 pr_debug("/proc/{kallsyms,modules} " 506 pr_debug("/proc/{kallsyms,modules} "
507 "inconsistency!\n"); 507 "inconsistency while looking "
508 "for \"%s\" module!\n", module);
508 return -1; 509 return -1;
509 } 510 }
511
512 if (curr_map->dso->loaded)
513 goto discard_symbol;
510 } 514 }
511 /* 515 /*
512 * So that we look just like we get from .ko files, 516 * So that we look just like we get from .ko files,
@@ -1343,13 +1347,33 @@ struct map *map_groups__find_by_name(struct map_groups *self,
1343 for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) { 1347 for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) {
1344 struct map *map = rb_entry(nd, struct map, rb_node); 1348 struct map *map = rb_entry(nd, struct map, rb_node);
1345 1349
1346 if (map->dso && strcmp(map->dso->name, name) == 0) 1350 if (map->dso && strcmp(map->dso->short_name, name) == 0)
1347 return map; 1351 return map;
1348 } 1352 }
1349 1353
1350 return NULL; 1354 return NULL;
1351} 1355}
1352 1356
1357static int dso__kernel_module_get_build_id(struct dso *self)
1358{
1359 char filename[PATH_MAX];
1360 /*
1361 * kernel module short names are of the form "[module]" and
1362 * we need just "module" here.
1363 */
1364 const char *name = self->short_name + 1;
1365
1366 snprintf(filename, sizeof(filename),
1367 "/sys/module/%.*s/notes/.note.gnu.build-id",
1368 (int)strlen(name - 1), name);
1369
1370 if (sysfs__read_build_id(filename, self->build_id,
1371 sizeof(self->build_id)) == 0)
1372 self->has_build_id = true;
1373
1374 return 0;
1375}
1376
1353static int perf_session__set_modules_path_dir(struct perf_session *self, char *dirname) 1377static int perf_session__set_modules_path_dir(struct perf_session *self, char *dirname)
1354{ 1378{
1355 struct dirent *dent; 1379 struct dirent *dent;
@@ -1395,6 +1419,7 @@ static int perf_session__set_modules_path_dir(struct perf_session *self, char *d
1395 if (long_name == NULL) 1419 if (long_name == NULL)
1396 goto failure; 1420 goto failure;
1397 dso__set_long_name(map->dso, long_name); 1421 dso__set_long_name(map->dso, long_name);
1422 dso__kernel_module_get_build_id(map->dso);
1398 } 1423 }
1399 } 1424 }
1400 1425
@@ -1437,6 +1462,24 @@ static struct map *map__new2(u64 start, struct dso *dso, enum map_type type)
1437 return self; 1462 return self;
1438} 1463}
1439 1464
1465struct map *perf_session__new_module_map(struct perf_session *self, u64 start,
1466 const char *filename)
1467{
1468 struct map *map;
1469 struct dso *dso = __dsos__findnew(&dsos__kernel, filename);
1470
1471 if (dso == NULL)
1472 return NULL;
1473
1474 map = map__new2(start, dso, MAP__FUNCTION);
1475 if (map == NULL)
1476 return NULL;
1477
1478 dso->origin = DSO__ORIG_KMODULE;
1479 map_groups__insert(&self->kmaps, map);
1480 return map;
1481}
1482
1440static int perf_session__create_module_maps(struct perf_session *self) 1483static int perf_session__create_module_maps(struct perf_session *self)
1441{ 1484{
1442 char *line = NULL; 1485 char *line = NULL;
@@ -1450,7 +1493,6 @@ static int perf_session__create_module_maps(struct perf_session *self)
1450 while (!feof(file)) { 1493 while (!feof(file)) {
1451 char name[PATH_MAX]; 1494 char name[PATH_MAX];
1452 u64 start; 1495 u64 start;
1453 struct dso *dso;
1454 char *sep; 1496 char *sep;
1455 int line_len; 1497 int line_len;
1456 1498
@@ -1476,26 +1518,10 @@ static int perf_session__create_module_maps(struct perf_session *self)
1476 *sep = '\0'; 1518 *sep = '\0';
1477 1519
1478 snprintf(name, sizeof(name), "[%s]", line); 1520 snprintf(name, sizeof(name), "[%s]", line);
1479 dso = dso__new(name); 1521 map = perf_session__new_module_map(self, start, name);
1480 1522 if (map == NULL)
1481 if (dso == NULL)
1482 goto out_delete_line;
1483
1484 map = map__new2(start, dso, MAP__FUNCTION);
1485 if (map == NULL) {
1486 dso__delete(dso);
1487 goto out_delete_line; 1523 goto out_delete_line;
1488 } 1524 dso__kernel_module_get_build_id(map->dso);
1489
1490 snprintf(name, sizeof(name),
1491 "/sys/module/%s/notes/.note.gnu.build-id", line);
1492 if (sysfs__read_build_id(name, dso->build_id,
1493 sizeof(dso->build_id)) == 0)
1494 dso->has_build_id = true;
1495
1496 dso->origin = DSO__ORIG_KMODULE;
1497 map_groups__insert(&self->kmaps, map);
1498 dsos__add(&dsos__kernel, dso);
1499 } 1525 }
1500 1526
1501 free(line); 1527 free(line);
@@ -1573,10 +1599,28 @@ static int dso__load_kernel_sym(struct dso *self, struct map *map,
1573 } 1599 }
1574 } 1600 }
1575 1601
1602 /*
1603 * Say the kernel DSO was created when processing the build-id header table,
1604 * we have a build-id, so check if it is the same as the running kernel,
1605 * using it if it is.
1606 */
1607 if (self->has_build_id) {
1608 u8 kallsyms_build_id[BUILD_ID_SIZE];
1609
1610 if (sysfs__read_build_id("/sys/kernel/notes", kallsyms_build_id,
1611 sizeof(kallsyms_build_id)) == 0)
1612
1613 is_kallsyms = dso__build_id_equal(self, kallsyms_build_id);
1614 if (is_kallsyms)
1615 goto do_kallsyms;
1616 goto do_vmlinux;
1617 }
1618
1576 is_kallsyms = self->long_name[0] == '['; 1619 is_kallsyms = self->long_name[0] == '[';
1577 if (is_kallsyms) 1620 if (is_kallsyms)
1578 goto do_kallsyms; 1621 goto do_kallsyms;
1579 1622
1623do_vmlinux:
1580 err = dso__load_vmlinux(self, map, session, self->long_name, filter); 1624 err = dso__load_vmlinux(self, map, session, self->long_name, filter);
1581 if (err <= 0) { 1625 if (err <= 0) {
1582 pr_info("The file %s cannot be used, " 1626 pr_info("The file %s cannot be used, "
@@ -1694,16 +1738,12 @@ out_delete_kernel_dso:
1694 return NULL; 1738 return NULL;
1695} 1739}
1696 1740
1697static int map_groups__create_kernel_maps(struct map_groups *self, 1741int __map_groups__create_kernel_maps(struct map_groups *self,
1698 struct map *vmlinux_maps[MAP__NR_TYPES], 1742 struct map *vmlinux_maps[MAP__NR_TYPES],
1699 const char *vmlinux) 1743 struct dso *kernel)
1700{ 1744{
1701 struct dso *kernel = dsos__create_kernel(vmlinux);
1702 enum map_type type; 1745 enum map_type type;
1703 1746
1704 if (kernel == NULL)
1705 return -1;
1706
1707 for (type = 0; type < MAP__NR_TYPES; ++type) { 1747 for (type = 0; type < MAP__NR_TYPES; ++type) {
1708 vmlinux_maps[type] = map__new2(0, kernel, type); 1748 vmlinux_maps[type] = map__new2(0, kernel, type);
1709 if (vmlinux_maps[type] == NULL) 1749 if (vmlinux_maps[type] == NULL)
@@ -1717,6 +1757,18 @@ static int map_groups__create_kernel_maps(struct map_groups *self,
1717 return 0; 1757 return 0;
1718} 1758}
1719 1759
1760static int map_groups__create_kernel_maps(struct map_groups *self,
1761 struct map *vmlinux_maps[MAP__NR_TYPES],
1762 const char *vmlinux)
1763{
1764 struct dso *kernel = dsos__create_kernel(vmlinux);
1765
1766 if (kernel == NULL)
1767 return -1;
1768
1769 return __map_groups__create_kernel_maps(self, vmlinux_maps, kernel);
1770}
1771
1720static void vmlinux_path__exit(void) 1772static void vmlinux_path__exit(void)
1721{ 1773{
1722 while (--vmlinux_path__nr_entries >= 0) { 1774 while (--vmlinux_path__nr_entries >= 0) {
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index ee0b4593db7..594156e43b1 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -134,6 +134,7 @@ size_t dsos__fprintf_buildid(FILE *fp);
134size_t dso__fprintf_buildid(struct dso *self, FILE *fp); 134size_t dso__fprintf_buildid(struct dso *self, FILE *fp);
135size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp); 135size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp);
136char dso__symtab_origin(const struct dso *self); 136char dso__symtab_origin(const struct dso *self);
137void dso__set_long_name(struct dso *self, char *name);
137void dso__set_build_id(struct dso *self, void *build_id); 138void dso__set_build_id(struct dso *self, void *build_id);
138struct symbol *dso__find_symbol(struct dso *self, enum map_type type, u64 addr); 139struct symbol *dso__find_symbol(struct dso *self, enum map_type type, u64 addr);
139struct symbol *dso__find_symbol_by_name(struct dso *self, enum map_type type, 140struct symbol *dso__find_symbol_by_name(struct dso *self, enum map_type type,
@@ -151,5 +152,7 @@ bool symbol_type__is_a(char symbol_type, enum map_type map_type);
151 152
152int perf_session__create_kernel_maps(struct perf_session *self); 153int perf_session__create_kernel_maps(struct perf_session *self);
153 154
155struct map *perf_session__new_module_map(struct perf_session *self, u64 start,
156 const char *filename);
154extern struct dso *vdso; 157extern struct dso *vdso;
155#endif /* __PERF_SYMBOL */ 158#endif /* __PERF_SYMBOL */
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
index c206f72c888..c06c13535a7 100644
--- a/tools/perf/util/thread.h
+++ b/tools/perf/util/thread.h
@@ -67,4 +67,8 @@ map_groups__find_function(struct map_groups *self, struct perf_session *session,
67 67
68struct map *map_groups__find_by_name(struct map_groups *self, 68struct map *map_groups__find_by_name(struct map_groups *self,
69 enum map_type type, const char *name); 69 enum map_type type, const char *name);
70
71int __map_groups__create_kernel_maps(struct map_groups *self,
72 struct map *vmlinux_maps[MAP__NR_TYPES],
73 struct dso *kernel);
70#endif /* __PERF_THREAD_H */ 74#endif /* __PERF_THREAD_H */