[PATCH] perf symbols: Cannot disassemble some routines when debuginfo present

Eric Saint-Etienne
2018-11-23T10:00:28Z

When the kernel is compiled with -ffunction-sections and perf uses the
kernel debuginfo, perf fails the very first symbol lookup and ends up with
an hex offset inside [kernel.vmlinux]. It's due to how perf loads the maps.

Indeed only .text gets loaded by map_groups__find() into al->map.
Consequently al->map address range encompass the whole code.
But map__load() has just loaded many function maps by splitting al->map,
which reduced al->map range drastically. Very likely the target address is
now in one of those newly created function maps, so we need to lookup the
map again to find that new map.

This issue is not specific to the kernel but to how the image is linked.
For the kernel, when we're not using the kernel debuginfo, perf will
fallback to using kallsyms and then the first lookup will work.

This patch makes sure that the event address we're looking-up is indeed
within the map we've found, otherwise we lookup another map again.
Only one extra lookup at most is required for the proper map to be found,
if it exists.

Signed-off-by: Eric Saint-Etienne <eric.saint.etienne@oracle.com>
Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
---
tools/perf/util/event.c | 23 ++++++++++++++++++++++-
1 file changed, 22 insertions(+), 1 deletion(-)

diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
index e9c108a..a69ef52 100644
--- a/tools/perf/util/event.c
+++ b/tools/perf/util/event.c
@@ -1571,7 +1571,28 @@ struct map *thread__find_map(struct thread *thread, u8 cpumode, u64 addr,
*/
if (load_map)
map__load(al->map);
- al->addr = al->map->map_ip(al->map, al->addr);
+
+ /*
+ * When using -ffunction-sections, only .text gets loaded by
+ * map_groups__find() into al->map. Consequently al->map address
+ * range encompass the whole code.
+ *
+ * But map__load() has just loaded many function maps by
+ * splitting al->map, which reduced al->map range drastically.
+ * Very likely the target address is now in one of those newly
+ * created function maps, so we need to lookup the map again
+ * to find that new map.
+ */
+ if (al->addr < al->map->start || al->addr >= al->map->end)
+ al->map = map_groups__find(mg, al->addr);
+
+ /*
+ * The new map *ought* to exist because the initial al->map
+ * contained that address and subsequently has been split into
+ * many *contiguous* maps.
+ */
+ if (al->map != NULL)
+ al->addr = al->map->map_ip(al->map, al->addr);
}

return al->map;
--
1.8.3.1