Re: [PATCH v3 0/3] perf annotate: Data type profiling support for C++ classes and virtual calls

From: Yanbo Zhao

Date: Wed Oct 07 2026 - 12:16:11 EST


Hi Namhyung,

Thanks very much for your review and help!

Best,
Yanbo

On Wed, Oct 7, 2026 at 12:02 PM Namhyung Kim <namhyung@xxxxxxxxxx> wrote:
>
> Hello Yanbo,
>
> On Mon, Oct 05, 2026 at 04:10:07PM -0400, Yanbo Zhao wrote:
> > Hello,
> >
> > Data type profiling currently only understands C struct/union types.
> > For C++ workloads, member accesses through base class subobjects
> > cannot be resolved, and virtual function calls (indirect calls through
> > the vtable) lose the return type of the callee: the register holding
> > the returned value becomes unknown, which matters when it's used
> > directly to access memory like 'p->next()->val' where no DWARF
> > variable describes the temporary.
> >
> > This series extends data type profiling to C++:
> >
> > Patch 1 introduces die_is_compound_type() covering DW_TAG_class_type
> > as well and accepts DW_TAG_inheritance in the offset-based member
> > lookup so that it descends into base class subobjects. It handles
> > empty base classes, members placed in the tail padding of a base and
> > virtual base classes.
> >
> > Patch 2 adds the DWARF helpers for virtual calls: the vtable slot index
> > of a virtual function, the virtual function at a slot of a class
> > (following the primary base class chain), and the class of the vtable
> > pointer at an offset of a type.
> >
> > Patch 3 tracks the vtable pointer and the virtual function pointer
> > loaded from it in the x86 instruction tracking, and resolves the
> > return type of 'call *N(%reg)' and 'call *%reg' through them.
> >
> > Tested on x86-64 with GCC 15 (-O2 -g) using small programs covering
> > single/multiple inheritance, empty base optimization, tail padding
> > reuse, virtual inheritance, direct calls through the vtable and the
> > speculatively devirtualized form. In all cases the access to the
> > returned pointer after a virtual call is annotated with the right type
> > and member, e.g.:
> >
> > movq (%rbp), %rax # data-type: struct Node +0 (_vptr.Node)
> > movq (%rax), %rax
> > cmpq %r14, %rax
> > je 0x1240
> > movq %rbp, %rdi
> > callq *%rax
> > addq 8(%rax), %r12 # data-type: struct Node +0x8 (val)
> >
> > The existing results for C code are unchanged and the added cost on
> > the common path (a pointer dereference) is a tag check on the resolved
> > member type.
>
> Thanks for working on this!
>
> Reviewed-by: Namhyung Kim <namhyung@xxxxxxxxxx>
>
> Thanks,
> Namhyung
>
> >
> > Changes in v3:
> > - Rebased onto the current perf-tools-next.
> >
> > Patch 1:
> > - No change.
> >
> > Patch 2:
> > - Check the value of DW_AT_virtuality instead of its presence (Sashiko,
> > Namhyung).
> > - Bound the base class walks in die_find_virtual_func() and
> > die_get_vptr_class() with MAX_TYPE_CHASE (Sashiko, Namhyung).
> >
> > Patch 3:
> > - Check !src->multi_regs when loading a function pointer from the
> > vtable and when marking a register as the vtable pointer (Sashiko,
> > Namhyung).
> > - Set ops->target.multi_regs in call__parse() so that an indirect call
> > with an index register is not resolved by the displacement alone.
> >
> > The recursion depth limit in __die_find_member_offset_cb() reported
> > for the patch 1 will be sent as a separate patch on top of this series
> > as discussed.
> >
> > Changes in v2:
> >
> > Patch 1:
> > - Explain DW_TAG_inheritance with an example DWARF in the commit
> > message (Namhyung).
> > - Skip virtual base classes whose location is a runtime expression
> > instead of falling back to offset 0 (Sashiko).
> > - Match a base class in the offset lookup only if it actually has a
> > member at the offset, to handle empty base optimization and tail
> > padding reuse where a member of the derived class shares the offset
> > with the base (Sashiko).
> > - Keep looking at the next sibling in fill_member_name() when an
> > anonymous child (base class) has nothing at the offset.
> >
> > Patch 2:
> > - Drop the non-existent DW_AT_vtable_elem_index and the DW_LANG_*
> > fallback macros (Namhyung).
> > - Drop cu_get_language(), cu_is_cplusplus(), die_get_base_class(),
> > die_get_parent() and die_find_member_by_offset() which are not
> > needed anymore (Namhyung).
> > - Document that die_get_vtable_index() returns the vtable slot index
> > and that GCC and Clang both emit the index as DW_OP_constu
> > (Namhyung, Sashiko).
> > - Fix die_find_virtual_func() to return the function DIE instead of
> > the DW_TAG_inheritance DIE when found in a base class (Sashiko).
> > - Follow only the primary base class chain in die_find_virtual_func()
> > since non-primary bases have their own secondary vtables, and skip
> > an empty base at offset 0 which is not the primary base.
> > - Add die_get_vptr_class() to find the class of the vtable pointer
> > through base class subobjects, and die_is_vtbl_ptr_type() to
> > identify the vtable pointer by its type ('__vtbl_ptr_type').
> >
> > Patch 3:
> > - Remove the receiver ('this' pointer) register update after the call
> > which was dead code and not needed, and the arg0_reg field (Sashiko,
> > Namhyung). The 'this' pointer lives in a callee-saved register or
> > on the stack across the call and DWARF location lists cover it.
> > - Handle 'call *%reg' by tracking the function pointer loaded from the
> > vtable as TSR_KIND_VFUNC_PTR with its return type (Sashiko). This
> > form is common due to speculative devirtualization by GCC.
> > - Strip the leading '*' of an indirect call operand in call__parse()
> > instead of extract_reg_offset() so that both forms are parsed.
> > - Ignore void virtual functions instead of aborting (Namhyung).
> > - Keep the existing pointer dereference branch and its fall-through
> > intact; the vtable pointer is detected from the resolved member
> > type there instead of a separate lookup before it.
> > - Treat the new register kinds as pointers when saved to the stack.
> >
> > v2: https://lore.kernel.org/r/20260930210038.196928-1-yzhao62@xxxxxxxx
> > v1: https://lore.kernel.org/r/20260821050207.4517-1-yzhao62@xxxxxxxx
> >
> > Thanks,
> > Yanbo
> >
> > Yanbo Zhao (3):
> > perf dwarf-aux: Add die_is_compound_type() to handle C++ class types
> > perf dwarf-aux: Add C++ vtable helpers
> > perf annotate: Resolve C++ virtual function calls in x86 insn tracking
> >
> > tools/perf/util/annotate-arch/annotate-x86.c | 77 ++++-
> > tools/perf/util/annotate-data.c | 61 ++--
> > tools/perf/util/annotate-data.h | 4 +
> > tools/perf/util/disasm.c | 7 +
> > tools/perf/util/dwarf-aux.c | 282 ++++++++++++++++++-
> > tools/perf/util/dwarf-aux.h | 21 ++
> > 6 files changed, 426 insertions(+), 26 deletions(-)
> >
> > base-commit: 1dc462fc214907671600172280c2e79ef9fe6fcf
> > --
> > 2.53.0
> >