diff options
author | Arnaldo Carvalho de Melo <acme@redhat.com> | 2010-01-13 10:22:17 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2010-01-13 11:39:43 -0500 |
commit | b7cece76783c68fb391f9882235b4b0c9c300c46 (patch) | |
tree | 8a0224493acc3cf74c218384a3b76b3e47c131a2 /tools/perf/util/symbol.c | |
parent | ff314d3903c2843de65c2148f66f277f2440ed26 (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/symbol.c')
-rw-r--r-- | tools/perf/util/symbol.c | 116 |
1 files changed, 84 insertions, 32 deletions
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 8e6627e6b778..381999dd5c1f 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 | ||
164 | static void dso__set_long_name(struct dso *self, char *name) | 164 | void 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 | ||
177 | struct dso *dso__new(const char *name) | 177 | struct 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 | ||
1357 | static 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 | |||
1353 | static int perf_session__set_modules_path_dir(struct perf_session *self, char *dirname) | 1377 | static 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 | ||
1465 | struct 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 | |||
1440 | static int perf_session__create_module_maps(struct perf_session *self) | 1483 | static 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 | ||
1623 | do_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 | ||
1697 | static int map_groups__create_kernel_maps(struct map_groups *self, | 1741 | int __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 | ||
1760 | static 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 | |||
1720 | static void vmlinux_path__exit(void) | 1772 | static void vmlinux_path__exit(void) |
1721 | { | 1773 | { |
1722 | while (--vmlinux_path__nr_entries >= 0) { | 1774 | while (--vmlinux_path__nr_entries >= 0) { |