Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

perf annotate-data: Add find_data_type() to get type from memory access

The find_data_type() is to get a data type from the memory access at the
given address (IP) using a register and an offset.

It requires DWARF debug info in the DSO and searches the list of
variables and function parameters in the scope.

In a pseudo code, it does basically the following:

find_data_type(dso, ip, reg, offset)
{
pc = map__rip_2objdump(ip);
CU = dwarf_addrdie(dso->dwarf, pc);
scopes = die_get_scopes(CU, pc);
for_each_scope(S, scopes) {
V = die_find_variable_by_reg(S, pc, reg);
if (V && V.type == pointer_type) {
T = die_get_real_type(V);
if (offset < T.size)
return T;
}
}
return NULL;
}

Committer notes:

The 'size' variable in check_variable() is 64-bit, so use PRIu64 and
inttypes.h to debug it.

Ditto at find_data_type_die().

Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Ian Rogers <irogers@google.com>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Cc: linux-toolchains@vger.kernel.org
Cc: linux-trace-devel@vger.kernel.org
Link: https://lore.kernel.org/r/20231213001323.718046-4-namhyung@kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Namhyung Kim and committed by
Arnaldo Carvalho de Melo
b9c87f53 3eee6067

+205
+1
tools/perf/util/Build
··· 196 196 perf-$(CONFIG_DWARF) += dwarf-aux.o 197 197 perf-$(CONFIG_DWARF) += dwarf-regs.o 198 198 perf-$(CONFIG_DWARF) += debuginfo.o 199 + perf-$(CONFIG_DWARF) += annotate-data.o 199 200 200 201 perf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o 201 202 perf-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind-local.o
+164
tools/perf/util/annotate-data.c
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * Convert sample address to data type using DWARF debug info. 4 + * 5 + * Written by Namhyung Kim <namhyung@kernel.org> 6 + */ 7 + 8 + #include <stdio.h> 9 + #include <stdlib.h> 10 + #include <inttypes.h> 11 + 12 + #include "annotate-data.h" 13 + #include "debuginfo.h" 14 + #include "debug.h" 15 + #include "dso.h" 16 + #include "map.h" 17 + #include "map_symbol.h" 18 + #include "strbuf.h" 19 + #include "symbol.h" 20 + 21 + static bool find_cu_die(struct debuginfo *di, u64 pc, Dwarf_Die *cu_die) 22 + { 23 + Dwarf_Off off, next_off; 24 + size_t header_size; 25 + 26 + if (dwarf_addrdie(di->dbg, pc, cu_die) != NULL) 27 + return cu_die; 28 + 29 + /* 30 + * There are some kernels don't have full aranges and contain only a few 31 + * aranges entries. Fallback to iterate all CU entries in .debug_info 32 + * in case it's missing. 33 + */ 34 + off = 0; 35 + while (dwarf_nextcu(di->dbg, off, &next_off, &header_size, 36 + NULL, NULL, NULL) == 0) { 37 + if (dwarf_offdie(di->dbg, off + header_size, cu_die) && 38 + dwarf_haspc(cu_die, pc)) 39 + return true; 40 + 41 + off = next_off; 42 + } 43 + return false; 44 + } 45 + 46 + /* The type info will be saved in @type_die */ 47 + static int check_variable(Dwarf_Die *var_die, Dwarf_Die *type_die, int offset) 48 + { 49 + Dwarf_Word size; 50 + 51 + /* Get the type of the variable */ 52 + if (die_get_real_type(var_die, type_die) == NULL) { 53 + pr_debug("variable has no type\n"); 54 + return -1; 55 + } 56 + 57 + /* 58 + * It expects a pointer type for a memory access. 59 + * Convert to a real type it points to. 60 + */ 61 + if (dwarf_tag(type_die) != DW_TAG_pointer_type || 62 + die_get_real_type(type_die, type_die) == NULL) { 63 + pr_debug("no pointer or no type\n"); 64 + return -1; 65 + } 66 + 67 + /* Get the size of the actual type */ 68 + if (dwarf_aggregate_size(type_die, &size) < 0) { 69 + pr_debug("type size is unknown\n"); 70 + return -1; 71 + } 72 + 73 + /* Minimal sanity check */ 74 + if ((unsigned)offset >= size) { 75 + pr_debug("offset: %d is bigger than size: %" PRIu64 "\n", offset, size); 76 + return -1; 77 + } 78 + 79 + return 0; 80 + } 81 + 82 + /* The result will be saved in @type_die */ 83 + static int find_data_type_die(struct debuginfo *di, u64 pc, 84 + int reg, int offset, Dwarf_Die *type_die) 85 + { 86 + Dwarf_Die cu_die, var_die; 87 + Dwarf_Die *scopes = NULL; 88 + int ret = -1; 89 + int i, nr_scopes; 90 + 91 + /* Get a compile_unit for this address */ 92 + if (!find_cu_die(di, pc, &cu_die)) { 93 + pr_debug("cannot find CU for address %" PRIx64 "\n", pc); 94 + return -1; 95 + } 96 + 97 + /* Get a list of nested scopes - i.e. (inlined) functions and blocks. */ 98 + nr_scopes = die_get_scopes(&cu_die, pc, &scopes); 99 + 100 + /* Search from the inner-most scope to the outer */ 101 + for (i = nr_scopes - 1; i >= 0; i--) { 102 + /* Look up variables/parameters in this scope */ 103 + if (!die_find_variable_by_reg(&scopes[i], pc, reg, &var_die)) 104 + continue; 105 + 106 + /* Found a variable, see if it's correct */ 107 + ret = check_variable(&var_die, type_die, offset); 108 + break; 109 + } 110 + 111 + free(scopes); 112 + return ret; 113 + } 114 + 115 + /** 116 + * find_data_type - Return a data type at the location 117 + * @ms: map and symbol at the location 118 + * @ip: instruction address of the memory access 119 + * @reg: register that holds the base address 120 + * @offset: offset from the base address 121 + * 122 + * This functions searches the debug information of the binary to get the data 123 + * type it accesses. The exact location is expressed by (ip, reg, offset). 124 + * It return %NULL if not found. 125 + */ 126 + struct annotated_data_type *find_data_type(struct map_symbol *ms, u64 ip, 127 + int reg, int offset) 128 + { 129 + struct annotated_data_type *result = NULL; 130 + struct dso *dso = map__dso(ms->map); 131 + struct debuginfo *di; 132 + Dwarf_Die type_die; 133 + struct strbuf sb; 134 + u64 pc; 135 + 136 + di = debuginfo__new(dso->long_name); 137 + if (di == NULL) { 138 + pr_debug("cannot get the debug info\n"); 139 + return NULL; 140 + } 141 + 142 + /* 143 + * IP is a relative instruction address from the start of the map, as 144 + * it can be randomized/relocated, it needs to translate to PC which is 145 + * a file address for DWARF processing. 146 + */ 147 + pc = map__rip_2objdump(ms->map, ip); 148 + if (find_data_type_die(di, pc, reg, offset, &type_die) < 0) 149 + goto out; 150 + 151 + result = zalloc(sizeof(*result)); 152 + if (result == NULL) 153 + goto out; 154 + 155 + strbuf_init(&sb, 32); 156 + if (die_get_typename_from_type(&type_die, &sb) < 0) 157 + strbuf_add(&sb, "(unknown type)", 14); 158 + 159 + result->type_name = strbuf_detach(&sb, NULL); 160 + 161 + out: 162 + debuginfo__delete(di); 163 + return result; 164 + }
+40
tools/perf/util/annotate-data.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + #ifndef _PERF_ANNOTATE_DATA_H 3 + #define _PERF_ANNOTATE_DATA_H 4 + 5 + #include <errno.h> 6 + #include <linux/compiler.h> 7 + #include <linux/types.h> 8 + 9 + struct map_symbol; 10 + 11 + /** 12 + * struct annotated_data_type - Data type to profile 13 + * @type_name: Name of the data type 14 + * @type_size: Size of the data type 15 + * 16 + * This represents a data type accessed by samples in the profile data. 17 + */ 18 + struct annotated_data_type { 19 + char *type_name; 20 + int type_size; 21 + }; 22 + 23 + #ifdef HAVE_DWARF_SUPPORT 24 + 25 + /* Returns data type at the location (ip, reg, offset) */ 26 + struct annotated_data_type *find_data_type(struct map_symbol *ms, u64 ip, 27 + int reg, int offset); 28 + 29 + #else /* HAVE_DWARF_SUPPORT */ 30 + 31 + static inline struct annotated_data_type * 32 + find_data_type(struct map_symbol *ms __maybe_unused, u64 ip __maybe_unused, 33 + int reg __maybe_unused, int offset __maybe_unused) 34 + { 35 + return NULL; 36 + } 37 + 38 + #endif /* HAVE_DWARF_SUPPORT */ 39 + 40 + #endif /* _PERF_ANNOTATE_DATA_H */