1

perf annotate-data: Add a cache for global variable types

They are often searched by many different places.  Let's add a cache
for them to reduce the duplicate DWARF access.

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>
Link: https://lore.kernel.org/r/20240319055115.4063940-23-namhyung@kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
Namhyung Kim 2024-03-18 22:51:14 -07:00 committed by Arnaldo Carvalho de Melo
parent b3c95109c1
commit 55ee3d005d
4 changed files with 118 additions and 4 deletions

View File

@ -433,6 +433,91 @@ static struct type_state_stack *findnew_stack_state(struct type_state *state,
return stack;
}
/* Maintain a cache for quick global variable lookup */
struct global_var_entry {
struct rb_node node;
char *name;
u64 start;
u64 end;
u64 die_offset;
};
static int global_var_cmp(const void *_key, const struct rb_node *node)
{
const u64 addr = (uintptr_t)_key;
struct global_var_entry *gvar;
gvar = rb_entry(node, struct global_var_entry, node);
if (gvar->start <= addr && addr < gvar->end)
return 0;
return gvar->start > addr ? -1 : 1;
}
static bool global_var_less(struct rb_node *node_a, const struct rb_node *node_b)
{
struct global_var_entry *gvar_a, *gvar_b;
gvar_a = rb_entry(node_a, struct global_var_entry, node);
gvar_b = rb_entry(node_b, struct global_var_entry, node);
return gvar_a->start < gvar_b->start;
}
static struct global_var_entry *global_var__find(struct data_loc_info *dloc, u64 addr)
{
struct dso *dso = map__dso(dloc->ms->map);
struct rb_node *node;
node = rb_find((void *)(uintptr_t)addr, &dso->global_vars, global_var_cmp);
if (node == NULL)
return NULL;
return rb_entry(node, struct global_var_entry, node);
}
static bool global_var__add(struct data_loc_info *dloc, u64 addr,
const char *name, Dwarf_Die *type_die)
{
struct dso *dso = map__dso(dloc->ms->map);
struct global_var_entry *gvar;
Dwarf_Word size;
if (dwarf_aggregate_size(type_die, &size) < 0)
return false;
gvar = malloc(sizeof(*gvar));
if (gvar == NULL)
return false;
gvar->name = strdup(name);
if (gvar->name == NULL) {
free(gvar);
return false;
}
gvar->start = addr;
gvar->end = addr + size;
gvar->die_offset = dwarf_dieoffset(type_die);
rb_add(&gvar->node, &dso->global_vars, global_var_less);
return true;
}
void global_var_type__tree_delete(struct rb_root *root)
{
struct global_var_entry *gvar;
while (!RB_EMPTY_ROOT(root)) {
struct rb_node *node = rb_first(root);
rb_erase(node, root);
gvar = rb_entry(node, struct global_var_entry, node);
free(gvar->name);
free(gvar);
}
}
static bool get_global_var_info(struct data_loc_info *dloc, u64 addr,
const char **var_name, int *var_offset)
{
@ -467,14 +552,25 @@ static bool get_global_var_type(Dwarf_Die *cu_die, struct data_loc_info *dloc,
u64 pc;
int offset;
bool is_pointer = false;
const char *var_name;
const char *var_name = NULL;
struct global_var_entry *gvar;
Dwarf_Die var_die;
gvar = global_var__find(dloc, var_addr);
if (gvar) {
if (!dwarf_offdie(dloc->di->dbg, gvar->die_offset, type_die))
return false;
*var_offset = var_addr - gvar->start;
return true;
}
/* Try to get the variable by address first */
if (die_find_variable_by_addr(cu_die, var_addr, &var_die, &offset) &&
check_variable(&var_die, type_die, offset, is_pointer) == 0) {
var_name = dwarf_diename(&var_die);
*var_offset = offset;
return true;
goto ok;
}
if (!get_global_var_info(dloc, var_addr, &var_name, var_offset))
@ -485,9 +581,14 @@ static bool get_global_var_type(Dwarf_Die *cu_die, struct data_loc_info *dloc,
/* Try to get the name of global variable */
if (die_find_variable_at(cu_die, var_name, pc, &var_die) &&
check_variable(&var_die, type_die, *var_offset, is_pointer) == 0)
return true;
goto ok;
return false;
ok:
/* The address should point to the start of the variable */
global_var__add(dloc, var_addr - *var_offset, var_name, type_die);
return true;
}
/**

View File

@ -153,6 +153,9 @@ int annotated_data_type__update_samples(struct annotated_data_type *adt,
/* Release all data type information in the tree */
void annotated_data_type__tree_delete(struct rb_root *root);
/* Release all global variable information in the tree */
void global_var_type__tree_delete(struct rb_root *root);
#else /* HAVE_DWARF_SUPPORT */
static inline struct annotated_data_type *
@ -175,6 +178,10 @@ static inline void annotated_data_type__tree_delete(struct rb_root *root __maybe
{
}
static inline void global_var_type__tree_delete(struct rb_root *root __maybe_unused)
{
}
#endif /* HAVE_DWARF_SUPPORT */
#endif /* _PERF_ANNOTATE_DATA_H */

View File

@ -1329,6 +1329,7 @@ struct dso *dso__new_id(const char *name, struct dso_id *id)
dso->inlined_nodes = RB_ROOT_CACHED;
dso->srclines = RB_ROOT_CACHED;
dso->data_types = RB_ROOT;
dso->global_vars = RB_ROOT;
dso->data.fd = -1;
dso->data.status = DSO_DATA_STATUS_UNKNOWN;
dso->symtab_type = DSO_BINARY_TYPE__NOT_FOUND;
@ -1373,6 +1374,7 @@ void dso__delete(struct dso *dso)
dso->symbol_names_len = 0;
zfree(&dso->symbol_names);
annotated_data_type__tree_delete(&dso->data_types);
global_var_type__tree_delete(&dso->global_vars);
if (dso->short_name_allocated) {
zfree((char **)&dso->short_name);

View File

@ -154,7 +154,8 @@ struct dso {
size_t symbol_names_len;
struct rb_root_cached inlined_nodes;
struct rb_root_cached srclines;
struct rb_root data_types;
struct rb_root data_types;
struct rb_root global_vars;
struct {
u64 addr;
@ -411,4 +412,7 @@ int dso__strerror_load(struct dso *dso, char *buf, size_t buflen);
void reset_fd_limit(void);
u64 dso__find_global_type(struct dso *dso, u64 addr);
u64 dso__findnew_global_type(struct dso *dso, u64 addr, u64 offset);
#endif /* __PERF_DSO */