Skip to content

Commit

Permalink
perf annotate-data: Support global variables
Browse files Browse the repository at this point in the history
Global variables are accessed using PC-relative address so it needs to
be handled separately.  The PC-rel addressing is detected by using
DWARF_REG_PC.  On x86, %rip register would be used.

The address can be calculated using the ip and offset in the
instruction.  But it should start from the next instruction so add
calculate_pcrel_addr() to do it properly.

But global variables defined in a different file would only have a
declaration which doesn't include a location list.  So it first tries
to get the type info using the address, and then looks up the variable
declarations using name.  The name of global variables should be get
from the symbol table.  The declaration would have the type info.

So extend find_var_type() to take both address and name for global
variables.

The stat is now looks like:

  Annotate data type stats:
  total 294, ok 153 (52.0%), bad 141 (48.0%)
  -----------------------------------------------------------
          30 : no_sym
          32 : no_mem_ops
          61 : no_var
          10 : no_typeinfo
           8 : bad_offset

Reviewed-by: Ian Rogers <[email protected]>
Cc: Stephane Eranian <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Namhyung Kim <[email protected]>
  • Loading branch information
namhyung committed Jan 22, 2024
1 parent 83bfa06 commit 5f7cdde
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 13 deletions.
38 changes: 29 additions & 9 deletions tools/perf/util/annotate-data.c
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,8 @@ static int check_variable(Dwarf_Die *var_die, Dwarf_Die *type_die, int offset,

/* The result will be saved in @type_die */
static int find_data_type_die(struct debuginfo *di, u64 pc, u64 addr,
struct annotated_op_loc *loc, Dwarf_Die *type_die)
const char *var_name, struct annotated_op_loc *loc,
Dwarf_Die *type_die)
{
Dwarf_Die cu_die, var_die;
Dwarf_Die *scopes = NULL;
Expand All @@ -258,11 +259,21 @@ static int find_data_type_die(struct debuginfo *di, u64 pc, u64 addr,
reg = loc->reg1;
offset = loc->offset;

if (reg == DWARF_REG_PC &&
die_find_variable_by_addr(&cu_die, pc, addr, &var_die, &offset)) {
ret = check_variable(&var_die, type_die, offset,
/*is_pointer=*/false);
goto out;
if (reg == DWARF_REG_PC) {
if (die_find_variable_by_addr(&cu_die, pc, addr, &var_die, &offset)) {
ret = check_variable(&var_die, type_die, offset,
/*is_pointer=*/false);
loc->offset = offset;
goto out;
}

if (var_name && die_find_variable_at(&cu_die, var_name, pc,
&var_die)) {
ret = check_variable(&var_die, type_die, 0,
/*is_pointer=*/false);
/* loc->offset will be updated by the caller */
goto out;
}
}

/* Get a list of nested scopes - i.e. (inlined) functions and blocks. */
Expand All @@ -285,6 +296,7 @@ static int find_data_type_die(struct debuginfo *di, u64 pc, u64 addr,
/* Found a variable, see if it's correct */
ret = check_variable(&var_die, type_die, offset,
reg != DWARF_REG_PC);
loc->offset = offset;
goto out;
}

Expand All @@ -306,13 +318,21 @@ static int find_data_type_die(struct debuginfo *di, u64 pc, u64 addr,
* @ms: map and symbol at the location
* @ip: instruction address of the memory access
* @loc: instruction operand location
* @addr: data address of the memory access
* @var_name: global variable name
*
* This functions searches the debug information of the binary to get the data
* type it accesses. The exact location is expressed by (ip, reg, offset).
* type it accesses. The exact location is expressed by (@ip, reg, offset)
* for pointer variables or (@ip, @addr) for global variables. Note that global
* variables might update the @loc->offset after finding the start of the variable.
* If it cannot find a global variable by address, it tried to fine a declaration
* of the variable using @var_name. In that case, @loc->offset won't be updated.
*
* It return %NULL if not found.
*/
struct annotated_data_type *find_data_type(struct map_symbol *ms, u64 ip,
struct annotated_op_loc *loc)
struct annotated_op_loc *loc, u64 addr,
const char *var_name)
{
struct annotated_data_type *result = NULL;
struct dso *dso = map__dso(ms->map);
Expand All @@ -332,7 +352,7 @@ struct annotated_data_type *find_data_type(struct map_symbol *ms, u64 ip,
* a file address for DWARF processing.
*/
pc = map__rip_2objdump(ms->map, ip);
if (find_data_type_die(di, pc, 0, loc, &type_die) < 0)
if (find_data_type_die(di, pc, addr, var_name, loc, &type_die) < 0)
goto out;

result = dso__findnew_data_type(dso, &type_die);
Expand Down
6 changes: 4 additions & 2 deletions tools/perf/util/annotate-data.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,8 @@ extern struct annotated_data_stat ann_data_stat;

/* Returns data type at the location (ip, reg, offset) */
struct annotated_data_type *find_data_type(struct map_symbol *ms, u64 ip,
struct annotated_op_loc *loc);
struct annotated_op_loc *loc, u64 addr,
const char *var_name);

/* Update type access histogram at the given offset */
int annotated_data_type__update_samples(struct annotated_data_type *adt,
Expand All @@ -121,7 +122,8 @@ void annotated_data_type__tree_delete(struct rb_root *root);

static inline struct annotated_data_type *
find_data_type(struct map_symbol *ms __maybe_unused, u64 ip __maybe_unused,
struct annotated_op_loc *loc __maybe_unused)
struct annotated_op_loc *loc __maybe_unused,
u64 addr __maybe_unused, const char *var_name __maybe_unused)
{
return NULL;
}
Expand Down
57 changes: 55 additions & 2 deletions tools/perf/util/annotate.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include "util/sharded_mutex.h"
#include "arch/common.h"
#include "namespaces.h"
#include "thread.h"
#include <regex.h>
#include <linux/bitops.h>
#include <linux/kernel.h>
Expand Down Expand Up @@ -3744,6 +3745,30 @@ static bool is_stack_operation(struct arch *arch, struct disasm_line *dl)
return false;
}

u64 annotate_calc_pcrel(struct map_symbol *ms, u64 ip, int offset,
struct disasm_line *dl)
{
struct annotation *notes;
struct disasm_line *next;
u64 addr;

notes = symbol__annotation(ms->sym);
/*
* PC-relative addressing starts from the next instruction address
* But the IP is for the current instruction. Since disasm_line
* doesn't have the instruction size, calculate it using the next
* disasm_line. If it's the last one, we can use symbol's end
* address directly.
*/
if (&dl->al.node == notes->src->source.prev)
addr = ms->sym->end + offset;
else {
next = list_next_entry(dl, al.node);
addr = ip + (next->al.offset - dl->al.offset) + offset;
}
return map__rip_2objdump(ms->map, addr);
}

/**
* hist_entry__get_data_type - find data type for given hist entry
* @he: hist entry
Expand All @@ -3763,7 +3788,9 @@ struct annotated_data_type *hist_entry__get_data_type(struct hist_entry *he)
struct annotated_op_loc *op_loc;
struct annotated_data_type *mem_type;
struct annotated_item_stat *istat;
u64 ip = he->ip;
u64 ip = he->ip, addr = 0;
const char *var_name = NULL;
int var_offset;
int i;

ann_data_stat.total++;
Expand Down Expand Up @@ -3822,12 +3849,38 @@ struct annotated_data_type *hist_entry__get_data_type(struct hist_entry *he)
/* Recalculate IP because of LOCK prefix or insn fusion */
ip = ms->sym->start + dl->al.offset;

mem_type = find_data_type(ms, ip, op_loc);
var_offset = op_loc->offset;

/* PC-relative addressing */
if (op_loc->reg1 == DWARF_REG_PC) {
struct addr_location al;
struct symbol *var;
u64 map_addr;

addr = annotate_calc_pcrel(ms, ip, op_loc->offset, dl);
/* Kernel symbols might be relocated */
map_addr = addr + map__reloc(ms->map);

addr_location__init(&al);
var = thread__find_symbol_fb(he->thread, he->cpumode,
map_addr, &al);
if (var) {
var_name = var->name;
/* Calculate type offset from the start of variable */
var_offset = map_addr - map__unmap_ip(al.map, var->start);
}
addr_location__exit(&al);
}

mem_type = find_data_type(ms, ip, op_loc, addr, var_name);
if (mem_type)
istat->good++;
else
istat->bad++;

if (mem_type && var_name)
op_loc->offset = var_offset;

if (symbol_conf.annotate_data_sample) {
annotated_data_type__update_samples(mem_type, evsel,
op_loc->offset,
Expand Down
4 changes: 4 additions & 0 deletions tools/perf/util/annotate.h
Original file line number Diff line number Diff line change
Expand Up @@ -491,4 +491,8 @@ struct annotated_item_stat {
};
extern struct list_head ann_insn_stat;

/* Calculate PC-relative address */
u64 annotate_calc_pcrel(struct map_symbol *ms, u64 ip, int offset,
struct disasm_line *dl);

#endif /* __PERF_ANNOTATE_H */

0 comments on commit 5f7cdde

Please sign in to comment.