* [PATCH 1/3] perf dwarf-aux: Add die_is_compound_type() to handle C++ class types
2026-08-21 5:02 [PATCH 0/3] perf annotate: Data type profiling support for C++ classes and virtual calls Yanbo Zhao
@ 2026-08-21 5:02 ` Yanbo Zhao
2026-08-24 20:35 ` Namhyung Kim
2026-08-21 5:02 ` [PATCH 2/3] perf dwarf-aux: Add C++ class, inheritance and vtable helpers Yanbo Zhao
2026-08-21 5:02 ` [PATCH 3/3] perf disasm & annotate x86: Track C++ vptr and resolve virtual function calls Yanbo Zhao
2 siblings, 1 reply; 6+ messages in thread
From: Yanbo Zhao @ 2026-08-21 5:02 UTC (permalink / raw)
To: Namhyung Kim, Arnaldo Carvalho de Melo
Cc: Peter Zijlstra, Ingo Molnar, Mark Rutland, Alexander Shishkin,
Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark, Zecheng Li,
Xu Liu, linux-perf-users, linux-kernel, Yanbo Zhao
Introduce the die_is_compound_type() helper which checks for
DW_TAG_structure_type, DW_TAG_union_type, and DW_TAG_class_type, and
convert all the existing open-coded struct/union tag checks to use it:
- die_get_member_type() in dwarf-aux.c.
- __add_member_cb(), is_compound_type(), and set_stack_state() in
annotate-data.c.
Also accept DW_TAG_inheritance in the member lookup callbacks
(__die_find_member_offset_cb() and __add_member_cb()) so that member
lookup by offset descends into C++ base class subobjects.
This extends the existing member type resolution and data type
profiling state handling to C++ classes with inheritance without
changing behavior for C struct/union types.
Signed-off-by: Yanbo Zhao <yzhao62@ncsu.edu>
---
tools/perf/util/annotate-data.c | 29 ++++++-----------------------
tools/perf/util/dwarf-aux.c | 17 ++++++++++++-----
tools/perf/util/dwarf-aux.h | 3 +++
3 files changed, 21 insertions(+), 28 deletions(-)
diff --git a/tools/perf/util/annotate-data.c b/tools/perf/util/annotate-data.c
index 4e4c58764082..ee6bd2d0012d 100644
--- a/tools/perf/util/annotate-data.c
+++ b/tools/perf/util/annotate-data.c
@@ -230,9 +230,9 @@ static int __add_member_cb(Dwarf_Die *die, void *arg)
Dwarf_Word size, loc, bit_size = 0;
Dwarf_Attribute attr;
struct strbuf sb;
- int tag;
+ int tag = dwarf_tag(die);
- if (dwarf_tag(die) != DW_TAG_member)
+ if (tag != DW_TAG_member && tag != DW_TAG_inheritance)
return DIE_FIND_CB_SIBLING;
member = zalloc(sizeof(*member));
@@ -292,15 +292,8 @@ static int __add_member_cb(Dwarf_Die *die, void *arg)
INIT_LIST_HEAD(&member->children);
list_add_tail(&member->node, &parent->children);
- tag = dwarf_tag(&die_mem);
- switch (tag) {
- case DW_TAG_structure_type:
- case DW_TAG_union_type:
+ if (die_is_compound_type(&die_mem))
die_find_child(&die_mem, __add_member_cb, member, &die_mem);
- break;
- default:
- break;
- }
return DIE_FIND_CB_SIBLING;
}
@@ -464,9 +457,7 @@ static const char *match_result_str(enum type_match_result tmr)
static bool is_compound_type(Dwarf_Die *type_die)
{
- int tag = dwarf_tag(type_die);
-
- return tag == DW_TAG_structure_type || tag == DW_TAG_union_type;
+ return die_is_compound_type(type_die);
}
/* returns if Type B has better information than Type A */
@@ -584,7 +575,6 @@ struct type_state_stack *find_stack_state(struct type_state *state,
void set_stack_state(struct type_state_stack *stack, int offset, u8 kind,
Dwarf_Die *type_die, int ptr_offset)
{
- int tag;
Dwarf_Word size;
if (kind == TSR_KIND_POINTER) {
@@ -605,17 +595,10 @@ void set_stack_state(struct type_state_stack *stack, int offset, u8 kind,
return;
}
- tag = dwarf_tag(type_die);
-
- switch (tag) {
- case DW_TAG_structure_type:
- case DW_TAG_union_type:
+ if (die_is_compound_type(type_die))
stack->compound = (kind != TSR_KIND_PERCPU_POINTER);
- break;
- default:
+ else
stack->compound = false;
- break;
- }
}
struct type_state_stack *findnew_stack_state(struct type_state *state,
diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c
index d7160f87ac7d..88ba0ec23a68 100644
--- a/tools/perf/util/dwarf-aux.c
+++ b/tools/perf/util/dwarf-aux.c
@@ -60,6 +60,14 @@ const char *cu_get_comp_dir(Dwarf_Die *cu_die)
return dwarf_formstring(&attr);
}
+bool die_is_compound_type(Dwarf_Die *type_die)
+{
+ int tag = dwarf_tag(type_die);
+
+ return tag == DW_TAG_structure_type || tag == DW_TAG_union_type ||
+ tag == DW_TAG_class_type;
+}
+
/* Unlike dwarf_getsrc_die(), cu_getsrc_die() only returns statement line */
static Dwarf_Line *cu_getsrc_die(Dwarf_Die *cu_die, Dwarf_Addr addr)
{
@@ -2053,7 +2061,7 @@ static int __die_find_member_offset_cb(Dwarf_Die *die_mem, void *arg)
Dwarf_Word offset = (long)arg;
int tag = dwarf_tag(die_mem);
- if (tag != DW_TAG_member)
+ if (tag != DW_TAG_member && tag != DW_TAG_inheritance)
return DIE_FIND_CB_SIBLING;
/* Unions might not have location */
@@ -2104,7 +2112,7 @@ Dwarf_Die *die_get_member_type(Dwarf_Die *type_die, int offset,
tag = dwarf_tag(type_die);
/* If it's not a compound type, return the type directly */
- if (tag != DW_TAG_structure_type && tag != DW_TAG_union_type) {
+ if (!die_is_compound_type(type_die)) {
Dwarf_Word size;
if (dwarf_aggregate_size(type_die, &size) < 0)
@@ -2119,7 +2127,7 @@ Dwarf_Die *die_get_member_type(Dwarf_Die *type_die, int offset,
mb_type = *type_die;
/* TODO: Handle union types better? */
- while (tag == DW_TAG_structure_type || tag == DW_TAG_union_type) {
+ while (die_is_compound_type(&mb_type)) {
member = die_find_child(&mb_type, __die_find_member_offset_cb,
(void *)(long)offset, die_mem);
if (member == NULL)
@@ -2130,8 +2138,7 @@ Dwarf_Die *die_get_member_type(Dwarf_Die *type_die, int offset,
tag = dwarf_tag(&mb_type);
- if (tag == DW_TAG_structure_type || tag == DW_TAG_union_type ||
- tag == DW_TAG_array_type) {
+ if (die_is_compound_type(&mb_type) || tag == DW_TAG_array_type) {
Dwarf_Word loc;
/* Update offset for the start of the member struct */
diff --git a/tools/perf/util/dwarf-aux.h b/tools/perf/util/dwarf-aux.h
index 161f0bf980b6..855c45fec5bb 100644
--- a/tools/perf/util/dwarf-aux.h
+++ b/tools/perf/util/dwarf-aux.h
@@ -23,6 +23,9 @@ const char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname);
/* Get DW_AT_comp_dir (should be NULL with older gcc) */
const char *cu_get_comp_dir(Dwarf_Die *cu_die);
+/* Check if DIE is a compound type (structure, union, or class) */
+bool die_is_compound_type(Dwarf_Die *type_die);
+
/* Get a line number and file name for given address */
int cu_find_lineinfo(Dwarf_Die *cudie, Dwarf_Addr addr,
const char **fname, int *lineno);
--
2.25.1
^ permalink raw reply related [flat|nested] 6+ messages in thread* Re: [PATCH 1/3] perf dwarf-aux: Add die_is_compound_type() to handle C++ class types
2026-08-21 5:02 ` [PATCH 1/3] perf dwarf-aux: Add die_is_compound_type() to handle C++ class types Yanbo Zhao
@ 2026-08-24 20:35 ` Namhyung Kim
0 siblings, 0 replies; 6+ messages in thread
From: Namhyung Kim @ 2026-08-24 20:35 UTC (permalink / raw)
To: Yanbo Zhao
Cc: Arnaldo Carvalho de Melo, Peter Zijlstra, Ingo Molnar,
Mark Rutland, Alexander Shishkin, Jiri Olsa, Ian Rogers,
Adrian Hunter, James Clark, Zecheng Li, Xu Liu, linux-perf-users,
linux-kernel
Hello,
On Fri, Aug 21, 2026 at 01:02:05AM -0400, Yanbo Zhao wrote:
> Introduce the die_is_compound_type() helper which checks for
> DW_TAG_structure_type, DW_TAG_union_type, and DW_TAG_class_type, and
> convert all the existing open-coded struct/union tag checks to use it:
> - die_get_member_type() in dwarf-aux.c.
> - __add_member_cb(), is_compound_type(), and set_stack_state() in
> annotate-data.c.
I think this part is fine.
>
> Also accept DW_TAG_inheritance in the member lookup callbacks
> (__die_find_member_offset_cb() and __add_member_cb()) so that member
> lookup by offset descends into C++ base class subobjects.
>
> This extends the existing member type resolution and data type
> profiling state handling to C++ classes with inheritance without
> changing behavior for C struct/union types.
But this part is a little unclear. Can you please share an example
DWARF of a class that has inherited members? It'd be helpful for others
to understand why it needs to handle the tag in the same way.
Thanks,
Namhyung
>
> Signed-off-by: Yanbo Zhao <yzhao62@ncsu.edu>
> ---
> tools/perf/util/annotate-data.c | 29 ++++++-----------------------
> tools/perf/util/dwarf-aux.c | 17 ++++++++++++-----
> tools/perf/util/dwarf-aux.h | 3 +++
> 3 files changed, 21 insertions(+), 28 deletions(-)
>
> diff --git a/tools/perf/util/annotate-data.c b/tools/perf/util/annotate-data.c
> index 4e4c58764082..ee6bd2d0012d 100644
> --- a/tools/perf/util/annotate-data.c
> +++ b/tools/perf/util/annotate-data.c
> @@ -230,9 +230,9 @@ static int __add_member_cb(Dwarf_Die *die, void *arg)
> Dwarf_Word size, loc, bit_size = 0;
> Dwarf_Attribute attr;
> struct strbuf sb;
> - int tag;
> + int tag = dwarf_tag(die);
>
> - if (dwarf_tag(die) != DW_TAG_member)
> + if (tag != DW_TAG_member && tag != DW_TAG_inheritance)
> return DIE_FIND_CB_SIBLING;
>
> member = zalloc(sizeof(*member));
> @@ -292,15 +292,8 @@ static int __add_member_cb(Dwarf_Die *die, void *arg)
> INIT_LIST_HEAD(&member->children);
> list_add_tail(&member->node, &parent->children);
>
> - tag = dwarf_tag(&die_mem);
> - switch (tag) {
> - case DW_TAG_structure_type:
> - case DW_TAG_union_type:
> + if (die_is_compound_type(&die_mem))
> die_find_child(&die_mem, __add_member_cb, member, &die_mem);
> - break;
> - default:
> - break;
> - }
> return DIE_FIND_CB_SIBLING;
> }
>
> @@ -464,9 +457,7 @@ static const char *match_result_str(enum type_match_result tmr)
>
> static bool is_compound_type(Dwarf_Die *type_die)
> {
> - int tag = dwarf_tag(type_die);
> -
> - return tag == DW_TAG_structure_type || tag == DW_TAG_union_type;
> + return die_is_compound_type(type_die);
> }
>
> /* returns if Type B has better information than Type A */
> @@ -584,7 +575,6 @@ struct type_state_stack *find_stack_state(struct type_state *state,
> void set_stack_state(struct type_state_stack *stack, int offset, u8 kind,
> Dwarf_Die *type_die, int ptr_offset)
> {
> - int tag;
> Dwarf_Word size;
>
> if (kind == TSR_KIND_POINTER) {
> @@ -605,17 +595,10 @@ void set_stack_state(struct type_state_stack *stack, int offset, u8 kind,
> return;
> }
>
> - tag = dwarf_tag(type_die);
> -
> - switch (tag) {
> - case DW_TAG_structure_type:
> - case DW_TAG_union_type:
> + if (die_is_compound_type(type_die))
> stack->compound = (kind != TSR_KIND_PERCPU_POINTER);
> - break;
> - default:
> + else
> stack->compound = false;
> - break;
> - }
> }
>
> struct type_state_stack *findnew_stack_state(struct type_state *state,
> diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c
> index d7160f87ac7d..88ba0ec23a68 100644
> --- a/tools/perf/util/dwarf-aux.c
> +++ b/tools/perf/util/dwarf-aux.c
> @@ -60,6 +60,14 @@ const char *cu_get_comp_dir(Dwarf_Die *cu_die)
> return dwarf_formstring(&attr);
> }
>
> +bool die_is_compound_type(Dwarf_Die *type_die)
> +{
> + int tag = dwarf_tag(type_die);
> +
> + return tag == DW_TAG_structure_type || tag == DW_TAG_union_type ||
> + tag == DW_TAG_class_type;
> +}
> +
> /* Unlike dwarf_getsrc_die(), cu_getsrc_die() only returns statement line */
> static Dwarf_Line *cu_getsrc_die(Dwarf_Die *cu_die, Dwarf_Addr addr)
> {
> @@ -2053,7 +2061,7 @@ static int __die_find_member_offset_cb(Dwarf_Die *die_mem, void *arg)
> Dwarf_Word offset = (long)arg;
> int tag = dwarf_tag(die_mem);
>
> - if (tag != DW_TAG_member)
> + if (tag != DW_TAG_member && tag != DW_TAG_inheritance)
> return DIE_FIND_CB_SIBLING;
>
> /* Unions might not have location */
> @@ -2104,7 +2112,7 @@ Dwarf_Die *die_get_member_type(Dwarf_Die *type_die, int offset,
>
> tag = dwarf_tag(type_die);
> /* If it's not a compound type, return the type directly */
> - if (tag != DW_TAG_structure_type && tag != DW_TAG_union_type) {
> + if (!die_is_compound_type(type_die)) {
> Dwarf_Word size;
>
> if (dwarf_aggregate_size(type_die, &size) < 0)
> @@ -2119,7 +2127,7 @@ Dwarf_Die *die_get_member_type(Dwarf_Die *type_die, int offset,
>
> mb_type = *type_die;
> /* TODO: Handle union types better? */
> - while (tag == DW_TAG_structure_type || tag == DW_TAG_union_type) {
> + while (die_is_compound_type(&mb_type)) {
> member = die_find_child(&mb_type, __die_find_member_offset_cb,
> (void *)(long)offset, die_mem);
> if (member == NULL)
> @@ -2130,8 +2138,7 @@ Dwarf_Die *die_get_member_type(Dwarf_Die *type_die, int offset,
>
> tag = dwarf_tag(&mb_type);
>
> - if (tag == DW_TAG_structure_type || tag == DW_TAG_union_type ||
> - tag == DW_TAG_array_type) {
> + if (die_is_compound_type(&mb_type) || tag == DW_TAG_array_type) {
> Dwarf_Word loc;
>
> /* Update offset for the start of the member struct */
> diff --git a/tools/perf/util/dwarf-aux.h b/tools/perf/util/dwarf-aux.h
> index 161f0bf980b6..855c45fec5bb 100644
> --- a/tools/perf/util/dwarf-aux.h
> +++ b/tools/perf/util/dwarf-aux.h
> @@ -23,6 +23,9 @@ const char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname);
> /* Get DW_AT_comp_dir (should be NULL with older gcc) */
> const char *cu_get_comp_dir(Dwarf_Die *cu_die);
>
> +/* Check if DIE is a compound type (structure, union, or class) */
> +bool die_is_compound_type(Dwarf_Die *type_die);
> +
> /* Get a line number and file name for given address */
> int cu_find_lineinfo(Dwarf_Die *cudie, Dwarf_Addr addr,
> const char **fname, int *lineno);
> --
> 2.25.1
>
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 2/3] perf dwarf-aux: Add C++ class, inheritance and vtable helpers
2026-08-21 5:02 [PATCH 0/3] perf annotate: Data type profiling support for C++ classes and virtual calls Yanbo Zhao
2026-08-21 5:02 ` [PATCH 1/3] perf dwarf-aux: Add die_is_compound_type() to handle C++ class types Yanbo Zhao
@ 2026-08-21 5:02 ` Yanbo Zhao
2026-08-24 21:04 ` Namhyung Kim
2026-08-21 5:02 ` [PATCH 3/3] perf disasm & annotate x86: Track C++ vptr and resolve virtual function calls Yanbo Zhao
2 siblings, 1 reply; 6+ messages in thread
From: Yanbo Zhao @ 2026-08-21 5:02 UTC (permalink / raw)
To: Namhyung Kim, Arnaldo Carvalho de Melo
Cc: Peter Zijlstra, Ingo Molnar, Mark Rutland, Alexander Shishkin,
Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark, Zecheng Li,
Xu Liu, linux-perf-users, linux-kernel, Yanbo Zhao
Add DWARF helper functions for C++ virtual function support:
- cu_get_language() and cu_is_cplusplus() to detect source language.
- die_get_base_class() and die_get_vtable_index() for virtual table
inspection.
- die_get_parent() to retrieve enclosing scopes.
- die_find_member_by_offset() and die_find_virtual_func() to locate
class members and virtual function DIEs.
- die_is_vptr_member() to check if a member is a virtual table pointer.
- Define DW_AT_vtable_elem_index and DW_LANG_C_plus_plus_17/20
fallbacks for older elfutils that lack these DWARF 5 constants.
Signed-off-by: Yanbo Zhao <yzhao62@ncsu.edu>
---
tools/perf/util/dwarf-aux.c | 192 ++++++++++++++++++++++++++++++++++++
tools/perf/util/dwarf-aux.h | 24 +++++
2 files changed, 216 insertions(+)
diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c
index 88ba0ec23a68..e991eed963a8 100644
--- a/tools/perf/util/dwarf-aux.c
+++ b/tools/perf/util/dwarf-aux.c
@@ -12,6 +12,19 @@
#include "dwarf-regs.h"
#include "strbuf.h"
#include "string2.h"
+#include "symbol.h"
+
+#ifndef DW_AT_vtable_elem_index
+#define DW_AT_vtable_elem_index 0x8c
+#endif
+
+/* Added in DWARF 5, may be missing in older elfutils */
+#ifndef DW_LANG_C_plus_plus_17
+#define DW_LANG_C_plus_plus_17 0x2a
+#endif
+#ifndef DW_LANG_C_plus_plus_20
+#define DW_LANG_C_plus_plus_20 0x2b
+#endif
/**
* cu_find_realpath - Find the realpath of the target file
@@ -60,6 +73,37 @@ const char *cu_get_comp_dir(Dwarf_Die *cu_die)
return dwarf_formstring(&attr);
}
+Dwarf_Word cu_get_language(Dwarf_Die *cu_die)
+{
+ Dwarf_Attribute attr;
+ Dwarf_Word lang;
+
+ if (dwarf_attr(cu_die, DW_AT_language, &attr) == NULL)
+ return 0;
+
+ if (dwarf_formudata(&attr, &lang) != 0)
+ return 0;
+
+ return lang;
+}
+
+bool cu_is_cplusplus(Dwarf_Die *cu_die)
+{
+ Dwarf_Word lang = cu_get_language(cu_die);
+
+ switch (lang) {
+ case DW_LANG_C_plus_plus:
+ case DW_LANG_C_plus_plus_03:
+ case DW_LANG_C_plus_plus_11:
+ case DW_LANG_C_plus_plus_14:
+ case DW_LANG_C_plus_plus_17:
+ case DW_LANG_C_plus_plus_20:
+ return true;
+ default:
+ return false;
+ }
+}
+
bool die_is_compound_type(Dwarf_Die *type_die)
{
int tag = dwarf_tag(type_die);
@@ -68,6 +112,90 @@ bool die_is_compound_type(Dwarf_Die *type_die)
tag == DW_TAG_class_type;
}
+static int __die_find_inheritance_cb(Dwarf_Die *die_mem, void *arg)
+{
+ int tag = dwarf_tag(die_mem);
+
+ if (tag == DW_TAG_inheritance) {
+ *(Dwarf_Die *)arg = *die_mem;
+ return DIE_FIND_CB_END;
+ }
+
+ return DIE_FIND_CB_SIBLING;
+}
+
+Dwarf_Die *die_get_base_class(Dwarf_Die *class_die, Dwarf_Die *base_die, int *offset)
+{
+ Dwarf_Die inherit_die;
+ Dwarf_Attribute attr;
+ Dwarf_Word loc;
+
+ if (die_find_child(class_die, __die_find_inheritance_cb,
+ &inherit_die, &inherit_die) == NULL)
+ return NULL;
+
+ if (__die_get_real_type(&inherit_die, base_die) == NULL)
+ return NULL;
+
+ if (dwarf_attr_integrate(&inherit_die, DW_AT_data_member_location, &attr) &&
+ dwarf_formudata(&attr, &loc) == 0) {
+ *offset = loc;
+ } else {
+ *offset = 0;
+ }
+
+ return base_die;
+}
+
+int die_get_vtable_index(Dwarf_Die *func_die, int *index)
+{
+ Dwarf_Attribute attr;
+ Dwarf_Word idx;
+
+ /* Try DW_AT_vtable_elem_index first (DWARF 5+) */
+ if (dwarf_attr(func_die, DW_AT_vtable_elem_index, &attr) &&
+ dwarf_formudata(&attr, &idx) == 0) {
+ *index = idx;
+ return 0;
+ }
+
+ /* Fallback to DW_AT_vtable_elem_location (older DWARF) */
+ if (dwarf_attr(func_die, DW_AT_vtable_elem_location, &attr)) {
+ Dwarf_Op *expr;
+ size_t expr_len;
+
+ /* Compile often emits it as a simple constant expression or block */
+ if (dwarf_getlocation(&attr, &expr, &expr_len) == 0 && expr_len > 0) {
+ if (expr[0].atom == DW_OP_constu) {
+ *index = expr[0].number;
+ return 0;
+ }
+ if (expr[0].atom >= DW_OP_lit0 && expr[0].atom <= DW_OP_lit31) {
+ *index = expr[0].atom - DW_OP_lit0;
+ return 0;
+ }
+ }
+ }
+
+ return -1;
+}
+
+Dwarf_Die *die_get_parent(Dwarf_Die *die, Dwarf_Die *parent_die)
+{
+ Dwarf_Die *scopes = NULL;
+ int n = dwarf_getscopes_die(die, &scopes);
+
+ if (n <= 1) {
+ free(scopes);
+ return NULL;
+ }
+
+ /* scopes[0] is the DIE itself, scopes[1] is the parent */
+ *parent_die = scopes[1];
+ free(scopes);
+ return parent_die;
+}
+
/* Unlike dwarf_getsrc_die(), cu_getsrc_die() only returns statement line */
static Dwarf_Line *cu_getsrc_die(Dwarf_Die *cu_die, Dwarf_Addr addr)
{
@@ -2164,6 +2292,70 @@ Dwarf_Die *die_get_member_type(Dwarf_Die *type_die, int offset,
return die_mem;
}
+Dwarf_Die *die_find_member_by_offset(Dwarf_Die *type_die, int offset, Dwarf_Die *member_die)
+{
+ if (!die_is_compound_type(type_die))
+ return NULL;
+
+ return die_find_child(type_die, __die_find_member_offset_cb,
+ (void *)(long)offset, member_die);
+}
+
+struct find_virtual_func_data {
+ int index;
+ Dwarf_Die func_die;
+ bool found;
+};
+
+static int __die_find_virtual_func_cb(Dwarf_Die *die_mem, void *arg)
+{
+ struct find_virtual_func_data *ad = arg;
+ int tag = dwarf_tag(die_mem);
+
+ if (tag == DW_TAG_subprogram) {
+ int idx;
+
+ if (die_get_vtable_index(die_mem, &idx) == 0 && idx == ad->index) {
+ ad->func_die = *die_mem;
+ ad->found = true;
+ return DIE_FIND_CB_END;
+ }
+ }
+
+ if (tag == DW_TAG_inheritance) {
+ Dwarf_Die base_type;
+
+ if (__die_get_real_type(die_mem, &base_type)) {
+ if (die_find_child(&base_type,
+ __die_find_virtual_func_cb,
+ ad, &ad->func_die))
+ return DIE_FIND_CB_END;
+ }
+ }
+
+ return DIE_FIND_CB_SIBLING;
+}
+
+Dwarf_Die *die_find_virtual_func(Dwarf_Die *class_die, int index, Dwarf_Die *die_mem)
+{
+ struct find_virtual_func_data ad = {
+ .index = index,
+ .found = false,
+ };
+
+ if (die_find_child(class_die, __die_find_virtual_func_cb, &ad, die_mem))
+ return die_mem;
+
+ return NULL;
+}
+
+bool die_is_vptr_member(Dwarf_Die *die)
+{
+ const char *name = dwarf_diename(die);
+
+ return name && strstarts(name, "_vptr");
+}
+
/**
* die_deref_ptr_type - Return type info for pointer access
* @ptr_die: a pointer type DIE
diff --git a/tools/perf/util/dwarf-aux.h b/tools/perf/util/dwarf-aux.h
index 855c45fec5bb..0f30f44ad863 100644
--- a/tools/perf/util/dwarf-aux.h
+++ b/tools/perf/util/dwarf-aux.h
@@ -23,9 +23,33 @@ const char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname);
/* Get DW_AT_comp_dir (should be NULL with older gcc) */
const char *cu_get_comp_dir(Dwarf_Die *cu_die);
+/* Get source language of CU */
+Dwarf_Word cu_get_language(Dwarf_Die *cu_die);
+
+/* Check if CU is C++ */
+bool cu_is_cplusplus(Dwarf_Die *cu_die);
+
/* Check if DIE is a compound type (structure, union, or class) */
bool die_is_compound_type(Dwarf_Die *type_die);
+/* Get base class of a class DIE */
+Dwarf_Die *die_get_base_class(Dwarf_Die *class_die, Dwarf_Die *base_die, int *offset);
+
+/* Get vtable index of a virtual function */
+int die_get_vtable_index(Dwarf_Die *func_die, int *index);
+
+/* Get parent DIE */
+Dwarf_Die *die_get_parent(Dwarf_Die *die, Dwarf_Die *parent_die);
+
+/* Find member DIE by offset */
+Dwarf_Die *die_find_member_by_offset(Dwarf_Die *type_die, int offset, Dwarf_Die *member_die);
+
+/* Find virtual function by vtable index */
+Dwarf_Die *die_find_virtual_func(Dwarf_Die *class_die, int index, Dwarf_Die *die_mem);
+
+/* Check if member DIE is a vtable pointer (_vptr) */
+bool die_is_vptr_member(Dwarf_Die *die);
+
/* Get a line number and file name for given address */
int cu_find_lineinfo(Dwarf_Die *cudie, Dwarf_Addr addr,
const char **fname, int *lineno);
--
2.25.1
^ permalink raw reply related [flat|nested] 6+ messages in thread* Re: [PATCH 2/3] perf dwarf-aux: Add C++ class, inheritance and vtable helpers
2026-08-21 5:02 ` [PATCH 2/3] perf dwarf-aux: Add C++ class, inheritance and vtable helpers Yanbo Zhao
@ 2026-08-24 21:04 ` Namhyung Kim
0 siblings, 0 replies; 6+ messages in thread
From: Namhyung Kim @ 2026-08-24 21:04 UTC (permalink / raw)
To: Yanbo Zhao
Cc: Arnaldo Carvalho de Melo, Peter Zijlstra, Ingo Molnar,
Mark Rutland, Alexander Shishkin, Jiri Olsa, Ian Rogers,
Adrian Hunter, James Clark, Zecheng Li, Xu Liu, linux-perf-users,
linux-kernel
On Fri, Aug 21, 2026 at 01:02:06AM -0400, Yanbo Zhao wrote:
> Add DWARF helper functions for C++ virtual function support:
> - cu_get_language() and cu_is_cplusplus() to detect source language.
> - die_get_base_class() and die_get_vtable_index() for virtual table
> inspection.
> - die_get_parent() to retrieve enclosing scopes.
> - die_find_member_by_offset() and die_find_virtual_func() to locate
> class members and virtual function DIEs.
> - die_is_vptr_member() to check if a member is a virtual table pointer.
> - Define DW_AT_vtable_elem_index and DW_LANG_C_plus_plus_17/20
> fallbacks for older elfutils that lack these DWARF 5 constants.
>
> Signed-off-by: Yanbo Zhao <yzhao62@ncsu.edu>
> ---
> tools/perf/util/dwarf-aux.c | 192 ++++++++++++++++++++++++++++++++++++
> tools/perf/util/dwarf-aux.h | 24 +++++
> 2 files changed, 216 insertions(+)
>
> diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c
> index 88ba0ec23a68..e991eed963a8 100644
> --- a/tools/perf/util/dwarf-aux.c
> +++ b/tools/perf/util/dwarf-aux.c
> @@ -12,6 +12,19 @@
> #include "dwarf-regs.h"
> #include "strbuf.h"
> #include "string2.h"
> +#include "symbol.h"
> +
> +#ifndef DW_AT_vtable_elem_index
> +#define DW_AT_vtable_elem_index 0x8c
> +#endif
I'm not sure about this. My system has dwarf.h with the following.
DW_AT_vtable_elem_location = 0x4d,
DW_AT_loclists_base = 0x8c,
> +
> +/* Added in DWARF 5, may be missing in older elfutils */
> +#ifndef DW_LANG_C_plus_plus_17
> +#define DW_LANG_C_plus_plus_17 0x2a
> +#endif
> +#ifndef DW_LANG_C_plus_plus_20
> +#define DW_LANG_C_plus_plus_20 0x2b
> +#endif
Also, these are enum constants, not macros. So it won't be defined even
on recent versions. And there are DW_LANG_C_plus_plus_23 (0x3a) too.
>
> /**
> * cu_find_realpath - Find the realpath of the target file
> @@ -60,6 +73,37 @@ const char *cu_get_comp_dir(Dwarf_Die *cu_die)
> return dwarf_formstring(&attr);
> }
>
> +Dwarf_Word cu_get_language(Dwarf_Die *cu_die)
> +{
> + Dwarf_Attribute attr;
> + Dwarf_Word lang;
> +
> + if (dwarf_attr(cu_die, DW_AT_language, &attr) == NULL)
> + return 0;
> +
> + if (dwarf_formudata(&attr, &lang) != 0)
> + return 0;
> +
> + return lang;
> +}
If it's not used elsewhere, probably better to make it static.
> +
> +bool cu_is_cplusplus(Dwarf_Die *cu_die)
> +{
> + Dwarf_Word lang = cu_get_language(cu_die);
> +
> + switch (lang) {
> + case DW_LANG_C_plus_plus:
> + case DW_LANG_C_plus_plus_03:
> + case DW_LANG_C_plus_plus_11:
> + case DW_LANG_C_plus_plus_14:
> + case DW_LANG_C_plus_plus_17:
> + case DW_LANG_C_plus_plus_20:
> + return true;
> + default:
> + return false;
> + }
> +}
> +
> bool die_is_compound_type(Dwarf_Die *type_die)
> {
> int tag = dwarf_tag(type_die);
> @@ -68,6 +112,90 @@ bool die_is_compound_type(Dwarf_Die *type_die)
> tag == DW_TAG_class_type;
> }
>
> +static int __die_find_inheritance_cb(Dwarf_Die *die_mem, void *arg)
> +{
> + int tag = dwarf_tag(die_mem);
> +
> + if (tag == DW_TAG_inheritance) {
> + *(Dwarf_Die *)arg = *die_mem;
> + return DIE_FIND_CB_END;
> + }
> +
> + return DIE_FIND_CB_SIBLING;
> +}
> +
> +Dwarf_Die *die_get_base_class(Dwarf_Die *class_die, Dwarf_Die *base_die, int *offset)
> +{
> + Dwarf_Die inherit_die;
> + Dwarf_Attribute attr;
> + Dwarf_Word loc;
> +
> + if (die_find_child(class_die, __die_find_inheritance_cb,
> + &inherit_die, &inherit_die) == NULL)
> + return NULL;
> +
> + if (__die_get_real_type(&inherit_die, base_die) == NULL)
> + return NULL;
> +
> + if (dwarf_attr_integrate(&inherit_die, DW_AT_data_member_location, &attr) &&
> + dwarf_formudata(&attr, &loc) == 0) {
As Sashiko said, you need to handle other formats.
> + *offset = loc;
> + } else {
> + *offset = 0;
> + }
> +
> + return base_die;
> +}
> +
> +int die_get_vtable_index(Dwarf_Die *func_die, int *index)
It'd be great to clarify what it returns. It's not clear to me what the
index means here. Is it for an index of the @func_die in the vtable of
the current class?
> +{
> + Dwarf_Attribute attr;
> + Dwarf_Word idx;
> +
> + /* Try DW_AT_vtable_elem_index first (DWARF 5+) */
> + if (dwarf_attr(func_die, DW_AT_vtable_elem_index, &attr) &&
> + dwarf_formudata(&attr, &idx) == 0) {
> + *index = idx;
> + return 0;
> + }
> +
> + /* Fallback to DW_AT_vtable_elem_location (older DWARF) */
> + if (dwarf_attr(func_die, DW_AT_vtable_elem_location, &attr)) {
According to the Sashiko review, it would return a byte offset instead
of an index. If so, it should be converted for consistency.
> + Dwarf_Op *expr;
> + size_t expr_len;
> +
> + /* Compile often emits it as a simple constant expression or block */
> + if (dwarf_getlocation(&attr, &expr, &expr_len) == 0 && expr_len > 0) {
> + if (expr[0].atom == DW_OP_constu) {
> + *index = expr[0].number;
> + return 0;
> + }
> + if (expr[0].atom >= DW_OP_lit0 && expr[0].atom <= DW_OP_lit31) {
> + *index = expr[0].atom - DW_OP_lit0;
> + return 0;
> + }
> + }
> + }
> +
> + return -1;
> +}
> +
> +Dwarf_Die *die_get_parent(Dwarf_Die *die, Dwarf_Die *parent_die)
> +{
> + Dwarf_Die *scopes = NULL;
> + int n = dwarf_getscopes_die(die, &scopes);
> +
> + if (n <= 1) {
> + free(scopes);
> + return NULL;
> + }
> +
> + /* scopes[0] is the DIE itself, scopes[1] is the parent */
> + *parent_die = scopes[1];
> + free(scopes);
> + return parent_die;
> +}
Where is this function used?
> +
> /* Unlike dwarf_getsrc_die(), cu_getsrc_die() only returns statement line */
> static Dwarf_Line *cu_getsrc_die(Dwarf_Die *cu_die, Dwarf_Addr addr)
> {
> @@ -2164,6 +2292,70 @@ Dwarf_Die *die_get_member_type(Dwarf_Die *type_die, int offset,
> return die_mem;
> }
>
> +Dwarf_Die *die_find_member_by_offset(Dwarf_Die *type_die, int offset, Dwarf_Die *member_die)
> +{
> + if (!die_is_compound_type(type_die))
> + return NULL;
> +
> + return die_find_child(type_die, __die_find_member_offset_cb,
> + (void *)(long)offset, member_die);
> +}
Isn't it basically the same as die_get_member_type()?
Thanks,
Namhyung
> +
> +struct find_virtual_func_data {
> + int index;
> + Dwarf_Die func_die;
> + bool found;
> +};
> +
> +static int __die_find_virtual_func_cb(Dwarf_Die *die_mem, void *arg)
> +{
> + struct find_virtual_func_data *ad = arg;
> + int tag = dwarf_tag(die_mem);
> +
> + if (tag == DW_TAG_subprogram) {
> + int idx;
> +
> + if (die_get_vtable_index(die_mem, &idx) == 0 && idx == ad->index) {
> + ad->func_die = *die_mem;
> + ad->found = true;
> + return DIE_FIND_CB_END;
> + }
> + }
> +
> + if (tag == DW_TAG_inheritance) {
> + Dwarf_Die base_type;
> +
> + if (__die_get_real_type(die_mem, &base_type)) {
> + if (die_find_child(&base_type,
> + __die_find_virtual_func_cb,
> + ad, &ad->func_die))
> + return DIE_FIND_CB_END;
> + }
> + }
> +
> + return DIE_FIND_CB_SIBLING;
> +}
> +
> +Dwarf_Die *die_find_virtual_func(Dwarf_Die *class_die, int index, Dwarf_Die *die_mem)
> +{
> + struct find_virtual_func_data ad = {
> + .index = index,
> + .found = false,
> + };
> +
> + if (die_find_child(class_die, __die_find_virtual_func_cb, &ad, die_mem))
> + return die_mem;
> +
> + return NULL;
> +}
> +
> +bool die_is_vptr_member(Dwarf_Die *die)
> +{
> + const char *name = dwarf_diename(die);
> +
> + return name && strstarts(name, "_vptr");
> +}
> +
> /**
> * die_deref_ptr_type - Return type info for pointer access
> * @ptr_die: a pointer type DIE
> diff --git a/tools/perf/util/dwarf-aux.h b/tools/perf/util/dwarf-aux.h
> index 855c45fec5bb..0f30f44ad863 100644
> --- a/tools/perf/util/dwarf-aux.h
> +++ b/tools/perf/util/dwarf-aux.h
> @@ -23,9 +23,33 @@ const char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname);
> /* Get DW_AT_comp_dir (should be NULL with older gcc) */
> const char *cu_get_comp_dir(Dwarf_Die *cu_die);
>
> +/* Get source language of CU */
> +Dwarf_Word cu_get_language(Dwarf_Die *cu_die);
> +
> +/* Check if CU is C++ */
> +bool cu_is_cplusplus(Dwarf_Die *cu_die);
> +
> /* Check if DIE is a compound type (structure, union, or class) */
> bool die_is_compound_type(Dwarf_Die *type_die);
>
> +/* Get base class of a class DIE */
> +Dwarf_Die *die_get_base_class(Dwarf_Die *class_die, Dwarf_Die *base_die, int *offset);
> +
> +/* Get vtable index of a virtual function */
> +int die_get_vtable_index(Dwarf_Die *func_die, int *index);
> +
> +/* Get parent DIE */
> +Dwarf_Die *die_get_parent(Dwarf_Die *die, Dwarf_Die *parent_die);
> +
> +/* Find member DIE by offset */
> +Dwarf_Die *die_find_member_by_offset(Dwarf_Die *type_die, int offset, Dwarf_Die *member_die);
> +
> +/* Find virtual function by vtable index */
> +Dwarf_Die *die_find_virtual_func(Dwarf_Die *class_die, int index, Dwarf_Die *die_mem);
> +
> +/* Check if member DIE is a vtable pointer (_vptr) */
> +bool die_is_vptr_member(Dwarf_Die *die);
> +
> /* Get a line number and file name for given address */
> int cu_find_lineinfo(Dwarf_Die *cudie, Dwarf_Addr addr,
> const char **fname, int *lineno);
> --
> 2.25.1
>
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 3/3] perf disasm & annotate x86: Track C++ vptr and resolve virtual function calls
2026-08-21 5:02 [PATCH 0/3] perf annotate: Data type profiling support for C++ classes and virtual calls Yanbo Zhao
2026-08-21 5:02 ` [PATCH 1/3] perf dwarf-aux: Add die_is_compound_type() to handle C++ class types Yanbo Zhao
2026-08-21 5:02 ` [PATCH 2/3] perf dwarf-aux: Add C++ class, inheritance and vtable helpers Yanbo Zhao
@ 2026-08-21 5:02 ` Yanbo Zhao
2 siblings, 0 replies; 6+ messages in thread
From: Yanbo Zhao @ 2026-08-21 5:02 UTC (permalink / raw)
To: Namhyung Kim, Arnaldo Carvalho de Melo
Cc: Peter Zijlstra, Ingo Molnar, Mark Rutland, Alexander Shishkin,
Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark, Zecheng Li,
Xu Liu, linux-perf-users, linux-kernel, Yanbo Zhao
Implement disassembler and x86 type profiling support for C++ virtual
dispatch and indirect call resolution:
- In call__parse() for indirect calls, duplicate and store ops->raw into
ops->target.raw so instruction location extraction can retrieve target
operand registers and offsets.
- In extract_reg_offset(), skip any leading '*' prefix (e.g. *%rax).
- Add TSR_KIND_VTABLE_PTR to enum type_state_kind and handle it in
pr_debug_type_name().
- In update_insn_state_x86() for mov instructions, inspect member
name and identify '_vptr' access, setting register kind to
TSR_KIND_VTABLE_PTR via die_is_vptr_member() helper.
- For call instructions targeting virtual function tables, look up
the virtual function DIE via die_find_virtual_func() before
register state invalidation. Only the primary vtable is handled
for now (TODO: multiple inheritance).
- Update the receiver ('this' pointer) register to the C++ class DIE
upon virtual call resolution when CU is C++. The register number
comes from the new type_state::arg0_reg field initialized per arch
like ret_reg, instead of being hardcoded.
- Move caller-saved register invalidation after reading target
operands to prevent losing state required for virtual call
resolution.
Signed-off-by: Yanbo Zhao <yzhao62@ncsu.edu>
---
tools/perf/util/annotate-arch/annotate-x86.c | 103 ++++++++++++++++---
tools/perf/util/annotate-data.c | 7 ++
tools/perf/util/annotate-data.h | 3 +
tools/perf/util/annotate.c | 4 +
tools/perf/util/disasm.c | 1 +
5 files changed, 105 insertions(+), 13 deletions(-)
diff --git a/tools/perf/util/annotate-arch/annotate-x86.c b/tools/perf/util/annotate-arch/annotate-x86.c
index 7e6136536393..cfb37a6130ba 100644
--- a/tools/perf/util/annotate-arch/annotate-x86.c
+++ b/tools/perf/util/annotate-arch/annotate-x86.c
@@ -11,6 +11,8 @@
#include "../map.h"
#include "../string2.h" // strstarts
#include "../symbol.h"
+#include "../hist.h"
+#include "../dwarf-aux.h"
/*
* x86 instruction nmemonic table to parse disasm lines for annotate.
@@ -234,6 +236,11 @@ static void update_insn_state_x86(struct type_state *state,
struct symbol *func = dl->ops.target.sym;
const char *call_name;
u64 call_addr;
+ Dwarf_Die target_func_die;
+ bool resolved_statically = false;
+ Dwarf_Die class_die;
+ bool has_class_die = false;
+ struct annotated_op_loc *target_op;
/* Try to resolve the call target name */
if (func)
@@ -245,12 +252,42 @@ static void update_insn_state_x86(struct type_state *state,
if (call_name && !strcmp(call_name, "__fentry__"))
return;
+ /*
+ * 1. Resolve target statically (virtual call fallback) FIRST
+ * (before invalidation)
+ */
+ target_op = &loc.ops[INSN_OP_TARGET];
+
+ if (target_op->mem_ref && has_reg_type(state, target_op->reg1)) {
+ struct type_state_reg *reg = &state->regs[target_op->reg1];
+
+ if (reg->ok && reg->kind == TSR_KIND_VTABLE_PTR) {
+ /* TODO: handle multiple inheritance (non-primary vtables) */
+ int vtable_index = target_op->offset / 8;
+ Dwarf_Die target_class_die = reg->type;
+
+ if (die_find_virtual_func(&target_class_die, vtable_index,
+ &target_func_die) &&
+ die_get_type(&target_func_die, &type_die)) {
+ resolved_statically = true;
+ class_die = target_class_die;
+ has_class_die = true;
+ }
+ }
+ }
+
+ /* Fallback to name-based resolution for direct calls */
+ if (!resolved_statically && call_name) {
+ if (die_find_func_rettype(cu_die, call_name, &type_die))
+ resolved_statically = true;
+ }
+
if (call_name)
pr_debug_dtp("call [%x] %s\n", insn_offset, call_name);
else
pr_debug_dtp("call [%x] <unknown>\n", insn_offset);
- /* Invalidate caller-saved registers after call */
+ /* 2. Invalidate caller-saved registers after we read them for resolution */
call_addr = map__rip_2objdump(dloc->ms->map,
dloc->ms->sym->start + dl->al.offset);
for (unsigned i = 0; i < ARRAY_SIZE(state->regs); i++) {
@@ -264,8 +301,8 @@ static void update_insn_state_x86(struct type_state *state,
invalidate_reg_state(reg);
}
- /* Update register with the return type (if any) */
- if (call_name && die_find_func_rettype(cu_die, call_name, &type_die)) {
+ /* 3. Apply resolved types to registers */
+ if (resolved_statically) {
tsr = &state->regs[state->ret_reg];
tsr->type = type_die;
tsr->kind = TSR_KIND_TYPE;
@@ -275,6 +312,20 @@ static void update_insn_state_x86(struct type_state *state,
pr_debug_dtp("call [%x] return -> reg%d",
insn_offset, state->ret_reg);
pr_debug_type_name(&type_die, tsr->kind);
+
+ /* Update receiver ('this' pointer) register if C++ */
+ if (has_class_die && cu_is_cplusplus(cu_die)) {
+ struct type_state_reg *recv_tsr = &state->regs[state->arg0_reg];
+
+ if (recv_tsr->ok &&
+ (recv_tsr->kind == TSR_KIND_TYPE ||
+ recv_tsr->kind == TSR_KIND_POINTER)) {
+ recv_tsr->type = class_die;
+ pr_debug_dtp("call [%x] update receiver reg%d to C++ class",
+ insn_offset, state->arg0_reg);
+ pr_debug_type_name(&class_die, recv_tsr->kind);
+ }
+ }
}
return;
}
@@ -622,17 +673,43 @@ static void update_insn_state_x86(struct type_state *state,
}
/* And then dereference the pointer if it has one */
else if (has_reg_type(state, sreg) && state->regs[sreg].ok &&
- state->regs[sreg].kind == TSR_KIND_TYPE &&
- die_deref_ptr_type(&state->regs[sreg].type,
- src->offset + state->regs[sreg].offset, &type_die)) {
- tsr->type = type_die;
- tsr->kind = TSR_KIND_TYPE;
- tsr->offset = 0;
- tsr->ok = true;
+ state->regs[sreg].kind == TSR_KIND_TYPE) {
+ Dwarf_Die class_type;
+ Dwarf_Die member;
+ int total_offset = src->offset + state->regs[sreg].offset;
+ bool is_vptr = false;
+
+ if (die_get_real_type(&state->regs[sreg].type, &class_type) &&
+ die_is_compound_type(&class_type) &&
+ die_find_member_by_offset(&class_type, total_offset, &member) &&
+ die_is_vptr_member(&member)) {
+ tsr->type = class_type;
+ tsr->kind = TSR_KIND_VTABLE_PTR;
+ tsr->offset = 0;
+ tsr->ok = true;
- pr_debug_dtp("mov [%x] %#x(reg%d) -> reg%d",
- insn_offset, src->offset, sreg, dst->reg1);
- pr_debug_type_name(&tsr->type, tsr->kind);
+ pr_debug_dtp("mov [%x] %#x(reg%d) -> reg%d (vptr)",
+ insn_offset, src->offset,
+ sreg, dst->reg1);
+ pr_debug_type_name(&class_type, tsr->kind);
+ is_vptr = true;
+ }
+
+ if (!is_vptr) {
+ if (die_deref_ptr_type(&state->regs[sreg].type,
+ total_offset, &type_die)) {
+ tsr->type = type_die;
+ tsr->kind = TSR_KIND_TYPE;
+ tsr->offset = 0;
+ tsr->ok = true;
+
+ pr_debug_dtp("mov [%x] %#x(reg%d) -> reg%d",
+ insn_offset, src->offset, sreg, dst->reg1);
+ pr_debug_type_name(&tsr->type, tsr->kind);
+ } else {
+ invalidate_reg_state(tsr);
+ }
+ }
}
/* Handle dereference of TSR_KIND_POINTER registers */
else if (has_reg_type(state, sreg) && state->regs[sreg].ok &&
diff --git a/tools/perf/util/annotate-data.c b/tools/perf/util/annotate-data.c
index ee6bd2d0012d..875e5ba2fe9f 100644
--- a/tools/perf/util/annotate-data.c
+++ b/tools/perf/util/annotate-data.c
@@ -28,6 +28,8 @@
/* register number of the stack pointer */
#define X86_REG_SP 7
+/* register number of the first integer argument (%rdi) */
+#define X86_REG_DI 5
static void delete_var_types(struct die_var_type *var_types);
@@ -66,6 +68,10 @@ void pr_debug_type_name(Dwarf_Die *die, enum type_state_kind kind)
pr_info(" pointer");
/* it also prints the type info */
break;
+ case TSR_KIND_VTABLE_PTR:
+ pr_info(" C++ vtable pointer");
+ /* it also prints the type info */
+ break;
case TSR_KIND_CANARY:
pr_info(" stack canary\n");
return;
@@ -177,6 +183,7 @@ static void init_type_state(struct type_state *state, const struct arch *arch)
state->regs[10].caller_saved = true;
state->regs[11].caller_saved = true;
state->ret_reg = 0;
+ state->arg0_reg = X86_REG_DI;
state->stack_reg = X86_REG_SP;
}
}
diff --git a/tools/perf/util/annotate-data.h b/tools/perf/util/annotate-data.h
index c26130744260..785e1d1777ce 100644
--- a/tools/perf/util/annotate-data.h
+++ b/tools/perf/util/annotate-data.h
@@ -36,6 +36,7 @@ enum type_state_kind {
TSR_KIND_CONST,
TSR_KIND_PERCPU_POINTER,
TSR_KIND_POINTER,
+ TSR_KIND_VTABLE_PTR,
TSR_KIND_CANARY,
};
@@ -224,6 +225,8 @@ struct type_state {
struct list_head stack_vars;
/* return value register */
int ret_reg;
+ /* first integer argument register (holds 'this' for C++ methods) */
+ int arg0_reg;
/* stack pointer register */
int stack_reg;
};
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index df70e95a8470..b4c6b45b4da3 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -2509,6 +2509,10 @@ static int extract_reg_offset(const struct arch *arch, const char *str,
if (arch->objdump.register_char == 0)
return -1;
+ /* Skip leading '*' for indirect call/jmp operands (e.g. *%rax) */
+ if (*str == '*')
+ str++;
+
/*
* It should start from offset, but it's possible to skip 0
* in the asm. So 0(%rax) should be same as (%rax).
diff --git a/tools/perf/util/disasm.c b/tools/perf/util/disasm.c
index 6cfdbabbb8c7..01266eeddbb4 100644
--- a/tools/perf/util/disasm.c
+++ b/tools/perf/util/disasm.c
@@ -279,6 +279,7 @@ static int call__parse(const struct arch *arch, struct ins_operands *ops, struct
return 0;
indirect_call:
+ ops->target.raw = strdup(ops->raw);
tok = strchr(endptr, '*');
if (tok != NULL) {
endptr++;
--
2.25.1
^ permalink raw reply related [flat|nested] 6+ messages in thread