Skip to content

Commit b9c87f5

Browse files
namhyungacmel
authored andcommitted
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>
1 parent 3eee606 commit b9c87f5

3 files changed

Lines changed: 205 additions & 0 deletions

File tree

tools/perf/util/Build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ perf-$(CONFIG_DWARF) += probe-finder.o
196196
perf-$(CONFIG_DWARF) += dwarf-aux.o
197197
perf-$(CONFIG_DWARF) += dwarf-regs.o
198198
perf-$(CONFIG_DWARF) += debuginfo.o
199+
perf-$(CONFIG_DWARF) += annotate-data.o
199200

200201
perf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
201202
perf-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind-local.o

tools/perf/util/annotate-data.c

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
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+
}

tools/perf/util/annotate-data.h

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
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 */

0 commit comments

Comments
 (0)