All of lore.kernel.org
 help / color / mirror / Atom feed
From: Namhyung Kim <namhyung@kernel.org>
To: Arnaldo Carvalho de Melo <acme@kernel.org>,
	Jiri Olsa <jolsa@kernel.org>,
	Peter Zijlstra <peterz@infradead.org>
Cc: Ian Rogers <irogers@google.com>,
	Adrian Hunter <adrian.hunter@intel.com>,
	Ingo Molnar <mingo@kernel.org>,
	LKML <linux-kernel@vger.kernel.org>,
	linux-perf-users@vger.kernel.org,
	Linus Torvalds <torvalds@linux-foundation.org>,
	Stephane Eranian <eranian@google.com>,
	Masami Hiramatsu <mhiramat@kernel.org>,
	Andi Kleen <ak@linux.intel.com>,
	linux-trace-devel@vger.kernel.org,
	linux-toolchains@vger.kernel.org
Subject: [PATCH 36/52] perf annotate-data: Support stack variables
Date: Thu,  9 Nov 2023 15:59:55 -0800	[thread overview]
Message-ID: <20231110000012.3538610-37-namhyung@kernel.org> (raw)
In-Reply-To: <20231110000012.3538610-1-namhyung@kernel.org>

Local variables are allocated in the stack and the location list
should look like base register(s) and an offset.  Extend the
die_find_variable_by_reg() to handle the following expressions

 * DW_OP_breg{0..31}
 * DW_OP_bregx
 * DW_OP_fbreg

Ususally DWARF subprogram entries have frame base information and
use it to locate stack variable like below:

 <2><43d1575>: Abbrev Number: 62 (DW_TAG_variable)
    <43d1576>   DW_AT_location    : 2 byte block: 91 7c         (DW_OP_fbreg: -4)  <--- here
    <43d1579>   DW_AT_name        : (indirect string, offset: 0x2c00c9): i
    <43d157d>   DW_AT_decl_file   : 1
    <43d157e>   DW_AT_decl_line   : 78
    <43d157f>   DW_AT_type        : <0x43d19d7>

I found some differences on saving the frame base between gcc and clang.
The gcc uses the CFA to get the base so it needs to check the current
frame's CFI info.  In this case, stack offset needs to be adjusted from
the start of the CFA.

 <1><1bb8d>: Abbrev Number: 102 (DW_TAG_subprogram)
    <1bb8e>   DW_AT_name        : (indirect string, offset: 0x74d41): kernel_init
    <1bb92>   DW_AT_decl_file   : 2
    <1bb92>   DW_AT_decl_line   : 1440
    <1bb94>   DW_AT_decl_column : 18
    <1bb95>   DW_AT_prototyped  : 1
    <1bb95>   DW_AT_type        : <0xcc>
    <1bb99>   DW_AT_low_pc      : 0xffffffff81bab9e0
    <1bba1>   DW_AT_high_pc     : 0x1b2
    <1bba9>   DW_AT_frame_base  : 1 byte block: 9c      (DW_OP_call_frame_cfa)  <------ here
    <1bbab>   DW_AT_call_all_calls: 1
    <1bbab>   DW_AT_sibling     : <0x1bf5a>

While clang sets it to a register directly and it can check the register
and offset in the instruction directly.

 <1><43d1542>: Abbrev Number: 60 (DW_TAG_subprogram)
    <43d1543>   DW_AT_low_pc      : 0xffffffff816a7c60
    <43d154b>   DW_AT_high_pc     : 0x98
    <43d154f>   DW_AT_frame_base  : 1 byte block: 56    (DW_OP_reg6 (rbp))  <---------- here
    <43d1551>   DW_AT_GNU_all_call_sites: 1
    <43d1551>   DW_AT_name        : (indirect string, offset: 0x3bce91): foo
    <43d1555>   DW_AT_decl_file   : 1
    <43d1556>   DW_AT_decl_line   : 75
    <43d1557>   DW_AT_prototyped  : 1
    <43d1557>   DW_AT_type        : <0x43c7332>
    <43d155b>   DW_AT_external    : 1

Also it needs to update the offset after finding the type like global
variables since the offset was from the frame base.  Factor out
match_var_offset() to check global and local variables in the same way.

The type stats are improved too:

  Annotate data type stats:
  total 294, ok 160 (54.4%), bad 134 (45.6%)
  -----------------------------------------------------------
          30 : no_sym
          32 : no_mem_ops
          51 : no_var
          14 : no_typeinfo
           7 : bad_offset

Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
 tools/perf/util/annotate-data.c | 35 +++++++++++++--
 tools/perf/util/dwarf-aux.c     | 79 ++++++++++++++++++++++++---------
 tools/perf/util/dwarf-aux.h     |  3 ++
 3 files changed, 93 insertions(+), 24 deletions(-)

diff --git a/tools/perf/util/annotate-data.c b/tools/perf/util/annotate-data.c
index 99ecf4b3665c..b60c24091360 100644
--- a/tools/perf/util/annotate-data.c
+++ b/tools/perf/util/annotate-data.c
@@ -208,7 +208,7 @@ static int check_variable(Dwarf_Die *var_die, Dwarf_Die *type_die, int offset,
 	/*
 	 * Usually it expects a pointer type for a memory access.
 	 * Convert to a real type it points to.  But global variables
-	 * are accessed directly without a pointer.
+	 * and local variables are accessed directly without a pointer.
 	 */
 	if (is_pointer) {
 		if ((dwarf_tag(type_die) != DW_TAG_pointer_type &&
@@ -247,6 +247,9 @@ static int find_data_type_die(struct debuginfo *di, u64 pc, u64 addr,
 	int reg, offset;
 	int ret = -1;
 	int i, nr_scopes;
+	int fbreg = -1;
+	bool is_fbreg = false;
+	int fb_offset = 0;
 
 	/* Get a compile_unit for this address */
 	if (!find_cu_die(di, pc, &cu_die)) {
@@ -278,7 +281,33 @@ static int find_data_type_die(struct debuginfo *di, u64 pc, u64 addr,
 	/* Get a list of nested scopes - i.e. (inlined) functions and blocks. */
 	nr_scopes = die_get_scopes(&cu_die, pc, &scopes);
 
+	if (reg != DWARF_REG_PC && dwarf_hasattr(&scopes[0], DW_AT_frame_base)) {
+		Dwarf_Attribute attr;
+		Dwarf_Block block;
+
+		/* Check if the 'reg' is assigned as frame base register */
+		if (dwarf_attr(&scopes[0], DW_AT_frame_base, &attr) != NULL &&
+		    dwarf_formblock(&attr, &block) == 0 && block.length == 1) {
+			switch (*block.data) {
+			case DW_OP_reg0 ... DW_OP_reg31:
+				fbreg = *block.data - DW_OP_reg0;
+				break;
+			case DW_OP_call_frame_cfa:
+				if (die_get_cfa(di->dbg, pc, &fbreg,
+						&fb_offset) < 0)
+					fbreg = -1;
+				break;
+			default:
+				break;
+			}
+		}
+	}
+
 retry:
+	is_fbreg = (reg == fbreg);
+	if (is_fbreg)
+		offset = loc->offset - fb_offset;
+
 	/* Search from the inner-most scope to the outer */
 	for (i = nr_scopes - 1; i >= 0; i--) {
 		if (reg == DWARF_REG_PC) {
@@ -288,13 +317,13 @@ static int find_data_type_die(struct debuginfo *di, u64 pc, u64 addr,
 		} else {
 			/* Look up variables/parameters in this scope */
 			if (!die_find_variable_by_reg(&scopes[i], pc, reg,
-						      &var_die))
+						      &offset, is_fbreg, &var_die))
 				continue;
 		}
 
 		/* Found a variable, see if it's correct */
 		ret = check_variable(&var_die, type_die, offset,
-				     reg != DWARF_REG_PC);
+				     reg != DWARF_REG_PC && !is_fbreg);
 		loc->offset = offset;
 		goto out;
 	}
diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c
index 3d42a8613869..7caf52fdc255 100644
--- a/tools/perf/util/dwarf-aux.c
+++ b/tools/perf/util/dwarf-aux.c
@@ -1272,11 +1272,39 @@ struct find_var_data {
 	unsigned reg;
 	/* Access offset, set for global data */
 	int offset;
+	/* True if the current register is the frame base */
+	bool is_fbreg;
 };
 
 /* Max number of registers DW_OP_regN supports */
 #define DWARF_OP_DIRECT_REGS  32
 
+static bool match_var_offset(Dwarf_Die *die_mem, struct find_var_data *data,
+			     u64 addr_offset, u64 addr_type)
+{
+	Dwarf_Die type_die;
+	Dwarf_Word size;
+
+	if (addr_offset == addr_type) {
+		/* Update offset relative to the start of the variable */
+		data->offset = 0;
+		return true;
+	}
+
+	if (die_get_real_type(die_mem, &type_die) == NULL)
+		return false;
+
+	if (dwarf_aggregate_size(&type_die, &size) < 0)
+		return false;
+
+	if (addr_offset >= addr_type + size)
+		return false;
+
+	/* Update offset relative to the start of the variable */
+	data->offset = addr_offset - addr_type;
+	return true;
+}
+
 /* Only checks direct child DIEs in the given scope. */
 static int __die_find_var_reg_cb(Dwarf_Die *die_mem, void *arg)
 {
@@ -1301,14 +1329,30 @@ static int __die_find_var_reg_cb(Dwarf_Die *die_mem, void *arg)
 		if (start > data->pc)
 			break;
 
+		/* Local variables accessed using frame base register */
+		if (data->is_fbreg && ops->atom == DW_OP_fbreg &&
+		    data->offset >= (int)ops->number &&
+		    match_var_offset(die_mem, data, data->offset, ops->number))
+			return DIE_FIND_CB_END;
+
 		/* Only match with a simple case */
 		if (data->reg < DWARF_OP_DIRECT_REGS) {
 			if (ops->atom == (DW_OP_reg0 + data->reg) && nops == 1)
 				return DIE_FIND_CB_END;
+
+			/* Local variables accessed by a register + offset */
+			if (ops->atom == (DW_OP_breg0 + data->reg) &&
+			    match_var_offset(die_mem, data, data->offset, ops->number))
+				return DIE_FIND_CB_END;
 		} else {
 			if (ops->atom == DW_OP_regx && ops->number == data->reg &&
 			    nops == 1)
 				return DIE_FIND_CB_END;
+
+			/* Local variables accessed by a register + offset */
+			if (ops->atom == DW_OP_bregx && data->reg == ops->number &&
+			    match_var_offset(die_mem, data, data->offset, ops->number2))
+				return DIE_FIND_CB_END;
 		}
 	}
 	return DIE_FIND_CB_SIBLING;
@@ -1319,18 +1363,29 @@ static int __die_find_var_reg_cb(Dwarf_Die *die_mem, void *arg)
  * @sc_die: a scope DIE
  * @pc: the program address to find
  * @reg: the register number to find
+ * @poffset: pointer to offset, will be updated for fbreg case
+ * @is_fbreg: boolean value if the current register is the frame base
  * @die_mem: a buffer to save the resulting DIE
  *
- * Find the variable DIE accessed by the given register.
+ * Find the variable DIE accessed by the given register.  It'll update the @offset
+ * when the variable is in the stack.
  */
 Dwarf_Die *die_find_variable_by_reg(Dwarf_Die *sc_die, Dwarf_Addr pc, int reg,
+				    int *poffset, bool is_fbreg,
 				    Dwarf_Die *die_mem)
 {
 	struct find_var_data data = {
 		.pc = pc,
 		.reg = reg,
+		.offset = *poffset,
+		.is_fbreg = is_fbreg,
 	};
-	return die_find_child(sc_die, __die_find_var_reg_cb, &data, die_mem);
+	Dwarf_Die *result;
+
+	result = die_find_child(sc_die, __die_find_var_reg_cb, &data, die_mem);
+	if (result)
+		*poffset = data.offset;
+	return result;
 }
 
 /* Only checks direct child DIEs in the given scope */
@@ -1341,8 +1396,6 @@ static int __die_find_var_addr_cb(Dwarf_Die *die_mem, void *arg)
 	ptrdiff_t off = 0;
 	Dwarf_Attribute attr;
 	Dwarf_Addr base, start, end;
-	Dwarf_Word size;
-	Dwarf_Die type_die;
 	Dwarf_Op *ops;
 	size_t nops;
 
@@ -1359,24 +1412,8 @@ static int __die_find_var_addr_cb(Dwarf_Die *die_mem, void *arg)
 		if (data->addr < ops->number)
 			continue;
 
-		if (data->addr == ops->number) {
-			/* Update offset relative to the start of the variable */
-			data->offset = 0;
+		if (match_var_offset(die_mem, data, data->addr, ops->number))
 			return DIE_FIND_CB_END;
-		}
-
-		if (die_get_real_type(die_mem, &type_die) == NULL)
-			continue;
-
-		if (dwarf_aggregate_size(&type_die, &size) < 0)
-			continue;
-
-		if (data->addr >= ops->number + size)
-			continue;
-
-		/* Update offset relative to the start of the variable */
-		data->offset = data->addr - ops->number;
-		return DIE_FIND_CB_END;
 	}
 	return DIE_FIND_CB_SIBLING;
 }
diff --git a/tools/perf/util/dwarf-aux.h b/tools/perf/util/dwarf-aux.h
index f209f9162908..85dd527ae1f7 100644
--- a/tools/perf/util/dwarf-aux.h
+++ b/tools/perf/util/dwarf-aux.h
@@ -142,6 +142,7 @@ int die_get_var_range(Dwarf_Die *sp_die, Dwarf_Die *vr_die, struct strbuf *buf);
 
 /* Find a variable saved in the 'reg' at given address */
 Dwarf_Die *die_find_variable_by_reg(Dwarf_Die *sc_die, Dwarf_Addr pc, int reg,
+				    int *poffset, bool is_fbreg,
 				    Dwarf_Die *die_mem);
 
 /* Find a (global) variable located in the 'addr' */
@@ -161,6 +162,8 @@ static inline int die_get_var_range(Dwarf_Die *sp_die __maybe_unused,
 static inline Dwarf_Die *die_find_variable_by_reg(Dwarf_Die *sc_die __maybe_unused,
 						  Dwarf_Addr pc __maybe_unused,
 						  int reg __maybe_unused,
+						  int *poffset __maybe_unused,
+						  bool is_fbreg __maybe_unused,
 						  Dwarf_Die *die_mem __maybe_unused)
 {
 	return NULL;
-- 
2.42.0.869.gea05f2083d-goog


  parent reply	other threads:[~2023-11-10  0:03 UTC|newest]

Thread overview: 69+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-11-09 23:59 [RFC 00/52] perf tools: Introduce data type profiling (v2) Namhyung Kim
2023-11-09 23:59 ` [PATCH 01/52] perf annotate: Pass "-l" option to objdump conditionally Namhyung Kim
2023-11-09 23:59 ` [PATCH 02/52] perf annotate: Move raw_comment and raw_func_start Namhyung Kim
2023-11-09 23:59 ` [PATCH 03/52] perf tools: Add util/debuginfo.[ch] files Namhyung Kim
2023-11-09 23:59 ` [PATCH 04/52] perf dwarf-aux: Fix die_get_typename() for void * Namhyung Kim
2023-11-09 23:59 ` [PATCH 05/52] perf dwarf-aux: Move #ifdef code to the header file Namhyung Kim
2023-11-09 23:59 ` [PATCH 06/52] perf dwarf-aux: Add die_get_scopes() helper Namhyung Kim
2023-11-09 23:59 ` [PATCH 07/52] perf dwarf-aux: Add die_find_variable_by_reg() helper Namhyung Kim
2023-11-09 23:59 ` [PATCH 08/52] perf build: Add feature check for dwarf_getcfi() Namhyung Kim
2023-11-10 10:26   ` Masami Hiramatsu
2023-11-09 23:59 ` [PATCH 09/52] perf probe: Convert to check dwarf_getcfi feature Namhyung Kim
2023-11-10 10:25   ` Masami Hiramatsu
2023-11-09 23:59 ` [PATCH 10/52] perf dwarf-aux: Factor out die_get_typename_from_type() Namhyung Kim
2023-11-09 23:59 ` [PATCH 11/52] perf dwarf-regs: Add get_dwarf_regnum() Namhyung Kim
2023-11-09 23:59 ` [PATCH 12/52] perf annotate-data: Add find_data_type() Namhyung Kim
     [not found]   ` <CA+JHD90fkWNrQWO5DrHeV8mCmFyKKqJ8fV=KwztRi7TSw+8yDg@mail.gmail.com>
2023-11-20 20:43     ` Namhyung Kim
2023-11-09 23:59 ` [PATCH 13/52] perf annotate-data: Add dso->data_types tree Namhyung Kim
2023-12-21 20:10   ` Arnaldo Carvalho de Melo
2023-12-21 20:13     ` Arnaldo Carvalho de Melo
2023-12-21 20:32       ` Arnaldo Carvalho de Melo
2023-11-09 23:59 ` [PATCH 14/52] perf annotate: Factor out evsel__get_arch() Namhyung Kim
2023-12-23 14:14   ` Arnaldo Carvalho de Melo
2023-11-09 23:59 ` [PATCH 15/52] perf annotate: Check if operand has multiple regs Namhyung Kim
2023-11-09 23:59 ` [PATCH 16/52] perf annotate: Add annotate_get_insn_location() Namhyung Kim
2023-11-09 23:59 ` [PATCH 17/52] perf annotate: Implement hist_entry__get_data_type() Namhyung Kim
2023-11-09 23:59 ` [PATCH 18/52] perf report: Add 'type' sort key Namhyung Kim
2023-11-21 17:55   ` Arnaldo Carvalho de Melo
2023-11-22 18:49     ` Namhyung Kim
2023-11-22 19:54       ` Arnaldo Carvalho de Melo
2023-11-22 21:13         ` Namhyung Kim
2023-11-23 13:40           ` Arnaldo Carvalho de Melo
2023-11-09 23:59 ` [PATCH 19/52] perf report: Support data type profiling Namhyung Kim
2023-11-09 23:59 ` [PATCH 20/52] perf annotate-data: Add member field in the data type Namhyung Kim
2023-11-09 23:59 ` [PATCH 21/52] perf annotate-data: Update sample histogram for type Namhyung Kim
2023-11-09 23:59 ` [PATCH 22/52] perf report: Add 'typeoff' sort key Namhyung Kim
2023-11-09 23:59 ` [PATCH 23/52] perf report: Add 'symoff' " Namhyung Kim
2023-12-23 14:29   ` Arnaldo Carvalho de Melo
2023-11-09 23:59 ` [PATCH 24/52] perf annotate: Add --data-type option Namhyung Kim
2023-11-09 23:59 ` [PATCH 25/52] perf annotate: Support event group display Namhyung Kim
2023-11-09 23:59 ` [PATCH 26/52] perf annotate: Add --type-stat option for debugging Namhyung Kim
2023-11-09 23:59 ` [PATCH 27/52] perf annotate: Add --insn-stat " Namhyung Kim
2023-11-09 23:59 ` [PATCH 28/52] perf annotate-data: Parse 'lock' prefix from llvm-objdump Namhyung Kim
2023-11-09 23:59 ` [PATCH 29/52] perf annotate-data: Handle macro fusion on x86 Namhyung Kim
2023-11-09 23:59 ` [PATCH 30/52] perf annotate-data: Handle array style accesses Namhyung Kim
2023-11-09 23:59 ` [PATCH 31/52] perf annotate-data: Add stack operation pseudo type Namhyung Kim
2023-11-09 23:59 ` [PATCH 32/52] perf dwarf-aux: Add die_find_variable_by_addr() Namhyung Kim
2023-11-27 22:07   ` Arnaldo Carvalho de Melo
2023-11-09 23:59 ` [PATCH 33/52] perf annotate-data: Handle PC-relative addressing Namhyung Kim
2023-11-09 23:59 ` [PATCH 34/52] perf annotate-data: Support global variables Namhyung Kim
2023-11-09 23:59 ` [PATCH 35/52] perf dwarf-aux: Add die_get_cfa() Namhyung Kim
2023-11-09 23:59 ` Namhyung Kim [this message]
2023-11-09 23:59 ` [PATCH 37/52] perf dwarf-aux: Check allowed DWARF Ops Namhyung Kim
2023-11-09 23:59 ` [PATCH 38/52] perf dwarf-aux: Add die_collect_vars() Namhyung Kim
2023-11-09 23:59 ` [PATCH 39/52] perf dwarf-aux: Handle type transfer for memory access Namhyung Kim
2023-11-09 23:59 ` [PATCH 40/52] perf annotate-data: Introduce struct data_loc_info Namhyung Kim
2023-11-10  0:00 ` [PATCH 41/52] perf map: Add map__objdump_2rip() Namhyung Kim
2023-11-10  0:00 ` [PATCH 42/52] perf annotate: Add annotate_get_basic_blocks() Namhyung Kim
2023-11-10  0:00 ` [PATCH 43/52] perf annotate-data: Maintain variable type info Namhyung Kim
2023-11-10  0:00 ` [PATCH 44/52] perf annotate-data: Add update_insn_state() Namhyung Kim
2023-11-10  0:00 ` [PATCH 45/52] perf annotate-data: Handle global variable access Namhyung Kim
2023-11-10  0:00 ` [PATCH 46/52] perf annotate-data: Handle call instructions Namhyung Kim
2023-11-10  0:00 ` [PATCH 47/52] perf annotate-data: Implement instruction tracking Namhyung Kim
2023-11-10  0:00 ` [PATCH 48/52] perf annotate: Parse x86 segment register location Namhyung Kim
2023-11-10  0:00 ` [PATCH 49/52] perf annotate-data: Handle this-cpu variables in kernel Namhyung Kim
2023-11-10  0:00 ` [PATCH 50/52] perf annotate-data: Track instructions with a this-cpu variable Namhyung Kim
2023-11-10  0:00 ` [PATCH 51/52] perf annotate-data: Add stack canary type Namhyung Kim
2023-11-10  0:00 ` [PATCH 52/52] perf annotate-data: Add debug message Namhyung Kim
2023-11-10 12:05 ` [RFC 00/52] perf tools: Introduce data type profiling (v2) Arnaldo Carvalho de Melo
2023-11-11  2:27   ` Namhyung Kim

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20231110000012.3538610-37-namhyung@kernel.org \
    --to=namhyung@kernel.org \
    --cc=acme@kernel.org \
    --cc=adrian.hunter@intel.com \
    --cc=ak@linux.intel.com \
    --cc=eranian@google.com \
    --cc=irogers@google.com \
    --cc=jolsa@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-perf-users@vger.kernel.org \
    --cc=linux-toolchains@vger.kernel.org \
    --cc=linux-trace-devel@vger.kernel.org \
    --cc=mhiramat@kernel.org \
    --cc=mingo@kernel.org \
    --cc=peterz@infradead.org \
    --cc=torvalds@linux-foundation.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.