[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20240216235423.2343167-13-namhyung@kernel.org>
Date: Fri, 16 Feb 2024 15:54:21 -0800
From: Namhyung Kim <namhyung@...nel.org>
To: Arnaldo Carvalho de Melo <acme@...nel.org>,
Ian Rogers <irogers@...gle.com>
Cc: Jiri Olsa <jolsa@...nel.org>,
Adrian Hunter <adrian.hunter@...el.com>,
Peter Zijlstra <peterz@...radead.org>,
Ingo Molnar <mingo@...nel.org>,
LKML <linux-kernel@...r.kernel.org>,
linux-perf-users@...r.kernel.org,
Linus Torvalds <torvalds@...ux-foundation.org>,
Stephane Eranian <eranian@...gle.com>,
Masami Hiramatsu <mhiramat@...nel.org>,
linux-toolchains@...r.kernel.org,
linux-trace-devel@...r.kernel.org
Subject: [PATCH 12/14] perf annotate-data: Handle this-cpu variables in kernel
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@...nel.org>
---
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 86ac44c476bf..5f3136f57c62 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -3810,6 +3810,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
@@ -3906,6 +3927,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 < 0 && map__dso(ms->map)->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 2bd654620de3..490134d19c9d 100644
--- a/tools/perf/util/annotate.h
+++ b/tools/perf/util/annotate.h
@@ -513,6 +513,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.44.0.rc0.258.g7320e95886-goog
Powered by blists - more mailing lists