[PATCH 45/48] perf annotate-data: Handle this-cpu variables in kernel
From: Namhyung Kim
Date: Wed Oct 11 2023 - 23:56:06 EST
On x86, the kernel gets the current task using the current macro like
below:
#define current get_current()
static __always_inline struct task_struct *get_current(void)
{
return this_cpu_read_stable(pcpu_hot.current_task);
}
So it returns the current_task field of struct pcpu_hot which is the
first member. On my build, it's located at 0x32940.
$ nm vmlinux | grep pcpu_hot
0000000000032940 D pcpu_hot
And the current macro generates the instructions like below:
mov %gs:0x32940, %rcx
So the %gs segment register points to the beginning of the per-cpu
region of this cpu and it points the variable with a constant.
Let's update the instruction location info to have a segment register
and handle %gs in kernel to look up a global variable. The new
get_percpu_var_info() helper is to get information about the variable.
Pretend it as a global variable by changing the register number to
DWARF_REG_PC.
Signed-off-by: Namhyung Kim <namhyung@xxxxxxxxxx>
---
tools/perf/util/annotate.c | 31 +++++++++++++++++++++++++++++++
tools/perf/util/annotate.h | 4 ++++
2 files changed, 35 insertions(+)
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index 7a097f64a28a..414ae45b7c06 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -3766,6 +3766,27 @@ void get_global_var_info(struct thread *thread, struct map_symbol *ms, u64 ip,
addr_location__exit(&al);
}
+void get_percpu_var_info(struct thread *thread, struct map_symbol *ms,
+ u8 cpumode, u64 var_addr, const char **var_name,
+ int *poffset)
+{
+ struct addr_location al;
+ struct symbol *var;
+ u64 map_addr;
+
+ /* Kernel symbols might be relocated */
+ map_addr = var_addr + map__reloc(ms->map);
+
+ addr_location__init(&al);
+ var = thread__find_symbol_fb(thread, cpumode, map_addr, &al);
+ if (var) {
+ *var_name = var->name;
+ /* Calculate type offset from the start of variable */
+ *poffset = map_addr - map__unmap_ip(al.map, var->start);
+ }
+ addr_location__exit(&al);
+}
+
/**
* hist_entry__get_data_type - find data type for given hist entry
* @he: hist entry
@@ -3857,6 +3878,16 @@ struct annotated_data_type *hist_entry__get_data_type(struct hist_entry *he)
&dloc.type_offset);
}
+ /* This CPU access in kernel - pretend PC-relative addressing */
+ if (op_loc->reg1 == -1 && ms->map->dso->kernel &&
+ arch__is(arch, "x86") && op_loc->segment == INSN_SEG_X86_GS) {
+ dloc.var_addr = op_loc->offset;
+ get_percpu_var_info(he->thread, ms, he->cpumode,
+ dloc.var_addr, &dloc.var_name,
+ &dloc.type_offset);
+ op_loc->reg1 = DWARF_REG_PC;
+ }
+
mem_type = find_data_type(&dloc);
if (mem_type)
istat->good++;
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h
index 076b5338ade1..c090cea1abdc 100644
--- a/tools/perf/util/annotate.h
+++ b/tools/perf/util/annotate.h
@@ -511,6 +511,10 @@ void get_global_var_info(struct thread *thread, struct map_symbol *ms, u64 ip,
struct disasm_line *dl, u8 cpumode, u64 *var_addr,
const char **var_name, int *poffset);
+void get_percpu_var_info(struct thread *thread, struct map_symbol *ms,
+ u8 cpumode, u64 var_addr, const char **var_name,
+ int *poffset);
+
/**
* struct annotated_basic_block - Basic block of instructions
* @list: List node
--
2.42.0.655.g421f12c284-goog