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>,
	linux-toolchains@vger.kernel.org,
	linux-trace-devel@vger.kernel.org
Subject: [PATCH 43/48] perf annotate-data: Implement instruction tracking
Date: Wed, 11 Oct 2023 20:51:06 -0700	[thread overview]
Message-ID: <20231012035111.676789-44-namhyung@kernel.org> (raw)
In-Reply-To: <20231012035111.676789-1-namhyung@kernel.org>

If it failed to find a variable for the location directly, it might be
due to a missing variable in the source code.  For example, accessing
pointer variables in a chain can result in the case like below:

  struct foo *foo = ...;

  int i = foo->bar->baz;

The DWARF debug information is created for each variable so it'd have
one for 'foo'.  But there's no variable for 'foo->bar' and then it
cannot know the type of 'bar' and 'baz'.

The above source code can be compiled to the follow x86 instructions:

  mov  0x8(%rax), %rcx
  mov  0x4(%rcx), %rdx   <=== PMU sample
  mov  %rdx, -4(%rbp)

Let's say 'foo' is located in the %rax and it has a pointer to struct
foo.  But perf sample is captured in the second instruction and there
is no variable or type info for the %rcx.

It'd be great if compiler could generate debug info for %rcx, but we
should handle it on our side.  So this patch implements the logic to
iterate instructions and update the type table for each location.

As it already collected a list of scopes including the target
instruction, we can use it to construct the type table smartly.

  +----------------  scope[0] subprogram
  |
  | +--------------  scope[1] lexical_block
  | |
  | | +------------  scope[2] inlined_subroutine
  | | |
  | | | +----------  scope[3] inlined_subroutine
  | | | |
  | | | | +--------  scope[4] lexical_block
  | | | | |
  | | | | |     ***  target instruction
  ...

Image the target instruction has 5 scopes, each scope will have its own
variables and parameters.  Then it can start with the innermost scope
(4).  So it'd search the shortest path from the start of scope[4] to
the target address and build a list of basic blocks.  Then it iterates
the basic blocks with the variables in the scope and update the table.
If it finds a type at the target instruction, then returns it.

Otherwise, it moves to the upper scope[3].  Now it'd search the shortest
path from the start of scope[3] to the start of scope[4].  Then connect
it to the existing basic block list.  Then it'd iterate the blocks with
variables for both scopes.  It can repeat this until it finds a type at
the target instruction or reaches to the top scope[0].

As the basic blocks contain the shortest path, it won't worry about
branches and can update the table simply.

With this change, the stat now looks like below:

  Annotate data type stats:
  total 294, ok 185 (62.9%), bad 109 (37.1%)
  -----------------------------------------------------------
          30 : no_sym
          32 : no_mem_ops
          27 : no_var
          13 : no_typeinfo
           7 : bad_offset

Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
 tools/perf/util/annotate-data.c | 232 ++++++++++++++++++++++++++++++++
 1 file changed, 232 insertions(+)

diff --git a/tools/perf/util/annotate-data.c b/tools/perf/util/annotate-data.c
index f3f85cb9ac00..1992ef20f71d 100644
--- a/tools/perf/util/annotate-data.c
+++ b/tools/perf/util/annotate-data.c
@@ -614,6 +614,231 @@ void update_insn_state(struct type_state *state, struct data_loc_info *dloc,
 	/* Case 4. memory to memory transfers (not handled for now) */
 }
 
+/* Prepend this_list to full_list, removing duplicate disasm line */
+static void prepend_basic_blocks(struct list_head *this_blocks,
+				 struct list_head *full_blocks)
+{
+	struct annotated_basic_block *first_bb, *last_bb;
+
+	last_bb = list_last_entry(this_blocks, typeof(*last_bb), list);
+	first_bb = list_first_entry(full_blocks, typeof(*first_bb), list);
+
+	if (list_empty(full_blocks))
+		goto out;
+
+	if (last_bb->end != first_bb->begin) {
+		pr_debug("prepend basic blocks: mismatched disasm line %lx -> %lx\n",
+			 last_bb->end->al.offset, first_bb->begin->al.offset);
+		goto out;
+	}
+
+	/* Is the basic block have only one disasm_line? */
+	if (last_bb->begin == last_bb->end) {
+		list_del(&last_bb->list);
+		free(last_bb);
+		goto out;
+	}
+
+	last_bb->end = list_prev_entry(last_bb->end, al.node);
+
+out:
+	list_splice(this_blocks, full_blocks);
+}
+
+static void delete_basic_blocks(struct list_head *basic_blocks)
+{
+	struct annotated_basic_block *bb, *tmp;
+
+	list_for_each_entry_safe(bb, tmp, basic_blocks, list) {
+		list_del(&bb->list);
+		free(bb);
+	}
+}
+
+/* Make sure all variables have a valid start address */
+static void fixup_var_address(struct die_var_type *var_types, u64 addr)
+{
+	while (var_types) {
+		/*
+		 * Some variables have no address range meaning it's always
+		 * available in the whole scope.  Let's adjust the start
+		 * address to the start of the scope.
+		 */
+		if (var_types->addr == 0)
+			var_types->addr = addr;
+
+		var_types = var_types->next;
+	}
+}
+
+static void delete_var_types(struct die_var_type *var_types)
+{
+	while (var_types) {
+		struct die_var_type *next = var_types->next;
+
+		free(var_types);
+		var_types = next;
+	}
+}
+
+/* It's at the target address, check if it has a matching type */
+static bool find_matching_type(struct type_state *state,
+			       struct data_loc_info *dloc, int reg,
+			       Dwarf_Die *type_die)
+{
+	Dwarf_Word size;
+
+	if (state->regs[reg].ok) {
+		int tag = dwarf_tag(&state->regs[reg].type);
+
+		/*
+		 * Normal registers should hold a pointer (or array) to
+		 * dereference a memory location.
+		 */
+		if (tag != DW_TAG_pointer_type && tag != DW_TAG_array_type)
+			return false;
+
+		if (die_get_real_type(&state->regs[reg].type, type_die) == NULL)
+			return false;
+
+		dloc->type_offset = dloc->op->offset;
+
+		/* Get the size of the actual type */
+		if (dwarf_aggregate_size(type_die, &size) < 0 ||
+		    (unsigned)dloc->type_offset >= size)
+			return false;
+
+		return true;
+	}
+
+	if (reg == dloc->fbreg) {
+		struct type_state_stack *stack;
+
+		stack = find_stack_state(state, dloc->type_offset);
+		if (stack == NULL)
+			return false;
+
+		*type_die = stack->type;
+		/* Update the type offset from the start of slot */
+		dloc->type_offset -= stack->offset;
+		return true;
+	}
+
+	if (dloc->fb_cfa) {
+		struct type_state_stack *stack;
+		u64 pc = map__rip_2objdump(dloc->ms->map, dloc->ip);
+		int fbreg, fboff;
+
+		if (die_get_cfa(dloc->di->dbg, pc, &fbreg, &fboff) < 0)
+			fbreg = -1;
+
+		if (reg != fbreg)
+			return false;
+
+		stack = find_stack_state(state, dloc->type_offset - fboff);
+		if (stack == NULL)
+			return false;
+
+		*type_die = stack->type;
+		/* Update the type offset from the start of slot */
+		dloc->type_offset -= fboff + stack->offset;
+		return true;
+	}
+
+	return false;
+}
+
+/* Iterate instructions in basic blocks and update type table */
+static bool find_data_type_insn(struct data_loc_info *dloc, int reg,
+				struct list_head *basic_blocks,
+				struct die_var_type *var_types,
+				Dwarf_Die *cu_die, Dwarf_Die *type_die)
+{
+	struct type_state state;
+	struct symbol *sym = dloc->ms->sym;
+	struct annotation *notes = symbol__annotation(sym);
+	struct annotated_basic_block *bb;
+	bool found = false;
+
+	init_type_state(&state, dloc->arch);
+
+	list_for_each_entry(bb, basic_blocks, list) {
+		struct disasm_line *dl = bb->begin;
+
+		list_for_each_entry_from(dl, &notes->src->source, al.node) {
+			u64 this_ip = sym->start + dl->al.offset;
+			u64 addr = map__rip_2objdump(dloc->ms->map, this_ip);
+
+			/* Update variable type at this address */
+			update_var_state(&state, dloc, addr, var_types);
+
+			if (this_ip == dloc->ip) {
+				found = find_matching_type(&state, dloc, reg,
+							   type_die);
+				goto out;
+			}
+
+			/* Update type table after processing the instruction */
+			update_insn_state(&state, dloc, cu_die, dl);
+			if (dl == bb->end)
+				break;
+		}
+	}
+
+out:
+	exit_type_state(&state);
+	return found;
+}
+
+/*
+ * Construct a list of basic blocks for each scope with variables and try to find
+ * the data type by updating a type state table through instructions.
+ */
+static int find_data_type_block(struct data_loc_info *dloc, int reg,
+				Dwarf_Die *cu_die, Dwarf_Die *scopes,
+				int nr_scopes, Dwarf_Die *type_die)
+{
+	LIST_HEAD(basic_blocks);
+	struct die_var_type *var_types = NULL;
+	u64 src_ip, dst_ip;
+	int ret = -1;
+
+	dst_ip = dloc->ip;
+	for (int i = nr_scopes - 1; i >= 0; i--) {
+		Dwarf_Addr base, start, end;
+		LIST_HEAD(this_blocks);
+
+		if (dwarf_ranges(&scopes[i], 0, &base, &start, &end) < 0)
+			break;
+
+		src_ip = map__objdump_2rip(dloc->ms->map, start);
+
+		/* Get basic blocks for this scope */
+		if (annotate_get_basic_blocks(dloc->ms->sym, src_ip, dst_ip,
+					      &this_blocks) < 0)
+			continue;
+		prepend_basic_blocks(&this_blocks, &basic_blocks);
+
+		/* Get variable info for this scope and add to var_types list */
+		die_collect_vars(&scopes[i], &var_types);
+		fixup_var_address(var_types, start);
+
+		/* Find from start of this scope to the target instruction */
+		if (find_data_type_insn(dloc, reg, &basic_blocks, var_types,
+					cu_die, type_die)) {
+			ret = 0;
+			break;
+		}
+
+		/* Go up to the next scope and find blocks to the start */
+		dst_ip = src_ip;
+	}
+
+	delete_basic_blocks(&basic_blocks);
+	delete_var_types(var_types);
+	return ret;
+}
+
 /* The result will be saved in @type_die */
 static int find_data_type_die(struct data_loc_info *dloc, Dwarf_Die *type_die)
 {
@@ -714,6 +939,13 @@ static int find_data_type_die(struct data_loc_info *dloc, Dwarf_Die *type_die)
 		goto out;
 	}
 
+	if (reg != DWARF_REG_PC) {
+		ret = find_data_type_block(dloc, reg, &cu_die, scopes,
+					   nr_scopes, type_die);
+		if (ret == 0)
+			goto out;
+	}
+
 	if (loc->multi_regs && reg == loc->reg1 && loc->reg1 != loc->reg2) {
 		reg = loc->reg2;
 		goto retry;
-- 
2.42.0.655.g421f12c284-goog


  parent reply	other threads:[~2023-10-12  4:02 UTC|newest]

Thread overview: 96+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-10-12  3:50 [RFC 00/48] perf tools: Introduce data type profiling (v1) Namhyung Kim
2023-10-12  3:50 ` [PATCH 01/48] perf annotate: Move raw_comment and raw_func_start Namhyung Kim
2023-10-12  3:50 ` [PATCH 02/48] perf annotate: Check if operand has multiple regs Namhyung Kim
2023-11-27 19:05   ` Arnaldo Carvalho de Melo
2023-10-12  3:50 ` [PATCH 03/48] perf tools: Add util/debuginfo.[ch] files Namhyung Kim
2023-10-12  3:50 ` [PATCH 04/48] perf dwarf-aux: Fix die_get_typename() for void * Namhyung Kim
2023-11-04 10:52   ` Masami Hiramatsu
2023-10-12  3:50 ` [PATCH 05/48] perf dwarf-aux: Move #ifdef code to the header file Namhyung Kim
2023-11-04 10:59   ` Masami Hiramatsu
2023-10-12  3:50 ` [PATCH 06/48] perf dwarf-aux: Add die_get_scopes() helper Namhyung Kim
2023-11-05  9:50   ` Masami Hiramatsu
2023-10-12  3:50 ` [PATCH 07/48] perf dwarf-aux: Add die_find_variable_by_reg() helper Namhyung Kim
2023-11-05  9:48   ` Masami Hiramatsu
2023-10-12  3:50 ` [PATCH 08/48] perf dwarf-aux: Factor out __die_get_typename() Namhyung Kim
2023-11-05  9:07   ` Masami Hiramatsu
2023-11-06  4:01     ` Namhyung Kim
2023-10-12  3:50 ` [PATCH 09/48] perf dwarf-regs: Add get_dwarf_regnum() Namhyung Kim
2023-11-05  8:36   ` Masami Hiramatsu
2023-11-06  4:12     ` Namhyung Kim
2023-10-12  3:50 ` [PATCH 10/48] perf annotate-data: Add find_data_type() Namhyung Kim
2023-10-12  3:50 ` [PATCH 11/48] perf annotate-data: Add dso->data_types tree Namhyung Kim
2023-10-12  3:50 ` [PATCH 12/48] perf annotate: Factor out evsel__get_arch() Namhyung Kim
2023-10-12  3:50 ` [PATCH 13/48] perf annotate: Add annotate_get_insn_location() Namhyung Kim
2023-10-23 16:38   ` Arnaldo Carvalho de Melo
2023-10-24 19:10     ` Namhyung Kim
2023-10-26  5:26       ` Namhyung Kim
2023-10-26 19:37         ` Arnaldo Carvalho de Melo
2023-10-12  3:50 ` [PATCH 14/48] perf annotate: Implement hist_entry__get_data_type() Namhyung Kim
2023-10-12  3:50 ` [PATCH 15/48] perf report: Add 'type' sort key Namhyung Kim
2023-10-23 16:53   ` Arnaldo Carvalho de Melo
2023-10-24 19:11     ` Namhyung Kim
2023-10-12  3:50 ` [PATCH 16/48] perf report: Support data type profiling Namhyung Kim
2023-10-12  3:50 ` [PATCH 17/48] perf annotate-data: Add member field in the data type Namhyung Kim
2023-10-12  3:50 ` [PATCH 18/48] perf annotate-data: Update sample histogram for type Namhyung Kim
2023-10-12  3:50 ` [PATCH 19/48] perf report: Add 'typeoff' sort key Namhyung Kim
2023-10-12  3:50 ` [PATCH 20/48] perf report: Add 'symoff' " Namhyung Kim
2023-10-12  3:50 ` [PATCH 21/48] perf annotate: Add --data-type option Namhyung Kim
2023-10-12  3:50 ` [PATCH 22/48] perf annotate: Add --type-stat option for debugging Namhyung Kim
2023-10-23 17:28   ` Arnaldo Carvalho de Melo
2023-10-23 17:40     ` Arnaldo Carvalho de Melo
2023-10-24 19:12       ` Namhyung Kim
2023-10-12  3:50 ` [PATCH 23/48] perf annotate: Add --insn-stat " Namhyung Kim
2023-10-12  3:50 ` [PATCH 24/48] perf annotate-data: Parse 'lock' prefix from llvm-objdump Namhyung Kim
2023-10-12  3:50 ` [PATCH 25/48] perf annotate-data: Handle macro fusion on x86 Namhyung Kim
2023-10-12  3:50 ` [PATCH 26/48] perf annotate-data: Handle array style accesses Namhyung Kim
2023-10-12  3:50 ` [PATCH 27/48] perf annotate-data: Add stack operation pseudo type Namhyung Kim
2023-10-12  3:50 ` [PATCH 28/48] perf dwarf-aux: Add die_find_variable_by_addr() Namhyung Kim
2023-11-06 15:25   ` Masami Hiramatsu
2023-11-09  5:36     ` Namhyung Kim
2023-10-12  3:50 ` [PATCH 29/48] perf annotate-data: Handle PC-relative addressing Namhyung Kim
2023-10-12  3:50 ` [PATCH 30/48] perf annotate-data: Support global variables Namhyung Kim
2023-10-12  3:50 ` [PATCH 31/48] perf dwarf-aux: Add die_get_cfa() Namhyung Kim
2023-11-07  0:50   ` Masami Hiramatsu
2023-11-08  5:28     ` Namhyung Kim
2023-10-12  3:50 ` [PATCH 32/48] perf annotate-data: Support stack variables Namhyung Kim
2023-10-12  3:50 ` [PATCH 33/48] perf dwarf-aux: Check allowed DWARF Ops Namhyung Kim
2023-11-07  9:32   ` Masami Hiramatsu
2023-11-08  5:34     ` Namhyung Kim
2023-10-12  3:50 ` [PATCH 34/48] perf dwarf-aux: Add die_collect_vars() Namhyung Kim
2023-11-08 10:52   ` Masami Hiramatsu
2023-11-09  5:05     ` Namhyung Kim
2023-10-12  3:50 ` [PATCH 35/48] perf dwarf-aux: Handle type transfer for memory access Namhyung Kim
2023-11-08 10:57   ` Masami Hiramatsu
2023-10-12  3:50 ` [PATCH 36/48] perf annotate-data: Introduce struct data_loc_info Namhyung Kim
2023-12-03 16:22   ` Athira Rajeev
2023-12-05  0:10     ` Namhyung Kim
2023-12-05  7:17       ` Athira Rajeev
2023-10-12  3:51 ` [PATCH 37/48] perf map: Add map__objdump_2rip() Namhyung Kim
2023-10-12  3:51 ` [PATCH 38/48] perf annotate: Add annotate_get_basic_blocks() Namhyung Kim
2023-10-12  3:51 ` [PATCH 39/48] perf annotate-data: Maintain variable type info Namhyung Kim
2023-10-12  3:51 ` [PATCH 40/48] perf annotate-data: Add update_insn_state() Namhyung Kim
2023-10-12  3:51 ` [PATCH 41/48] perf annotate-data: Handle global variable access Namhyung Kim
2023-10-12  3:51 ` [PATCH 42/48] perf annotate-data: Handle call instructions Namhyung Kim
2023-10-12  3:51 ` Namhyung Kim [this message]
2023-10-12  3:51 ` [PATCH 44/48] perf annotate: Parse x86 segment register location Namhyung Kim
2023-10-12  3:51 ` [PATCH 45/48] perf annotate-data: Handle this-cpu variables in kernel Namhyung Kim
2023-10-12  3:51 ` [PATCH 46/48] perf annotate-data: Track instructions with a this-cpu variable Namhyung Kim
2023-10-12  3:51 ` [PATCH 47/48] perf annotate-data: Add stack canary type Namhyung Kim
2023-10-12  3:51 ` [PATCH 48/48] perf annotate-data: Add debug message Namhyung Kim
2023-10-12  6:03 ` [RFC 00/48] perf tools: Introduce data type profiling (v1) Ingo Molnar
2023-10-12 16:19   ` Namhyung Kim
2023-10-12 18:33     ` Ingo Molnar
2023-10-12 20:45       ` Namhyung Kim
2023-10-12  9:11 ` Peter Zijlstra
2023-10-12 16:41   ` Namhyung Kim
     [not found]     ` <CADzB+2mu98v9EUsA1Y-wVDSrXT2kznKi87Tb6QdN5y4mMFNsyg@mail.gmail.com>
2023-10-25  5:58       ` Namhyung Kim
2023-10-12  9:15 ` Peter Zijlstra
2023-10-12 16:52   ` Namhyung Kim
2023-10-13 14:15 ` Arnaldo Carvalho de Melo
2023-10-23 21:58 ` Andi Kleen
2023-10-24 19:16   ` Namhyung Kim
2023-10-25  2:09     ` Andi Kleen
2023-10-25  5:51       ` Namhyung Kim
2023-10-25 20:01         ` Andi Kleen
2023-11-08 17:12 ` Joe Mario
2023-11-09  4:48   ` 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=20231012035111.676789-44-namhyung@kernel.org \
    --to=namhyung@kernel.org \
    --cc=acme@kernel.org \
    --cc=adrian.hunter@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.