Re: [PATCH] perf probe: Improve C++ support in arguments

From: Ian Rogers

Date: Wed Apr 01 2026 - 17:04:10 EST


On Fri, Dec 5, 2025 at 4:36 AM Steinar H. Gunderson <sesse@xxxxxxxxxx> wrote:
>
> For purposes of specifying arguments in tracepoints, C++ classes
> work exactly like structs, and C++ references work almost exactly
> like pointers (they are dereferenced with . instead of ->).
> However, they use different tags in DWARF, which means you cannot
> use them in arguments.
>
> Implement support for both, based on the existing struct and pointer
> support. This allows us to to something like this in Chromium:
>
> perf probe --exec content_shell --add 'PerformanceMark=\
> _ZN5blink15PerformanceMark6CreateEPNS_11ScriptStateERKNS_12\
> AtomicStringEPNS_22PerformanceMarkOptionsERNS_14ExceptionStateE \
> mark_name.string_.impl_.ptr_'
>
> even though mark_name is an class AtomicString&.
>
> We don't support advanced C++ features, such as operator overloading.
> Unfortunately, we also don't support C++-style strings where there
> is a separate length field instead of null-termination. We also cannot
> go past ptr_ in the example above (e.g. add ->length_), due to
> unrelated DWARF issues.
>
> Signed-off-by: Steinar H. Gunderson <sesse@xxxxxxxxxx>
> Cc: Ian Rogers <irogers@xxxxxxxxxx>

This change makes sense to me, Namhyung?

Thanks,
Ian

> ---
> tools/perf/util/annotate-data.c | 3 ++-
> tools/perf/util/dwarf-aux.c | 13 ++++++++++---
> tools/perf/util/probe-finder.c | 24 +++++++++++++++++-------
> 3 files changed, 29 insertions(+), 11 deletions(-)
>
> diff --git a/tools/perf/util/annotate-data.c b/tools/perf/util/annotate-data.c
> index 07cf9c334be0..d97b5fc61e6f 100644
> --- a/tools/perf/util/annotate-data.c
> +++ b/tools/perf/util/annotate-data.c
> @@ -459,7 +459,8 @@ static bool is_pointer_type(Dwarf_Die *type_die)
> {
> int tag = dwarf_tag(type_die);
>
> - return tag == DW_TAG_pointer_type || tag == DW_TAG_array_type;
> + return tag == DW_TAG_pointer_type || tag == DW_TAG_reference_type ||
> + tag == DW_TAG_array_type;
> }
>
> static bool is_compound_type(Dwarf_Die *type_die)
> diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c
> index 9267af204c7d..25bbc840d1a8 100644
> --- a/tools/perf/util/dwarf-aux.c
> +++ b/tools/perf/util/dwarf-aux.c
> @@ -1112,6 +1112,8 @@ int die_get_typename_from_type(Dwarf_Die *type_die, struct strbuf *buf)
> tag = dwarf_tag(type_die);
> if (tag == DW_TAG_pointer_type)
> tmp = "*";
> + else if (tag == DW_TAG_reference_type)
> + tmp = "&";
> else if (tag == DW_TAG_array_type)
> tmp = "[]";
> else if (tag == DW_TAG_subroutine_type) {
> @@ -1124,6 +1126,8 @@ int die_get_typename_from_type(Dwarf_Die *type_die, struct strbuf *buf)
> tmp = "union ";
> else if (tag == DW_TAG_structure_type)
> tmp = "struct ";
> + else if (tag == DW_TAG_class_type)
> + tmp = "class ";
> else if (tag == DW_TAG_enumeration_type)
> tmp = "enum ";
> else if (name == NULL)
> @@ -2065,7 +2069,8 @@ 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 (tag != DW_TAG_structure_type && tag != DW_TAG_class_type &&
> + tag != DW_TAG_union_type) {
> Dwarf_Word size;
>
> if (dwarf_aggregate_size(type_die, &size) < 0)
> @@ -2080,7 +2085,8 @@ 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 (tag == DW_TAG_structure_type || tag == DW_TAG_class_type ||
> + tag == DW_TAG_union_type) {
> member = die_find_child(&mb_type, __die_find_member_offset_cb,
> (void *)(long)offset, die_mem);
> if (member == NULL)
> @@ -2091,7 +2097,8 @@ 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) {
> + if (tag == DW_TAG_structure_type || tag == DW_TAG_class_type ||
> + tag == DW_TAG_union_type) {
> Dwarf_Word loc;
>
> /* Update offset for the start of the member struct */
> diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
> index 5ffd97ee4898..ca4734c75bba 100644
> --- a/tools/perf/util/probe-finder.c
> +++ b/tools/perf/util/probe-finder.c
> @@ -360,12 +360,20 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
> ref->offset += dwarf_bytesize(&type) * field->index;
> ref->user_access = user_access;
> goto next;
> - } else if (tag == DW_TAG_pointer_type) {
> + } else if (tag == DW_TAG_pointer_type || tag == DW_TAG_reference_type) {
> /* Check the pointer and dereference */
> - if (!field->ref) {
> - pr_err("Semantic error: %s must be referred by '->'\n",
> - field->name);
> - return -EINVAL;
> + if (tag == DW_TAG_pointer_type) {
> + if (!field->ref) {
> + pr_err("Semantic error: %s must be referred by '->'\n",
> + field->name);
> + return -EINVAL;
> + }
> + } else {
> + if (field->ref) {
> + pr_err("Semantic error: %s must be referred by '.'\n",
> + field->name);
> + return -EINVAL;
> + }
> }
> /* Get the type pointed by this pointer */
> if (die_get_real_type(&type, &type) == NULL) {
> @@ -374,7 +382,8 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
> }
> /* Verify it is a data structure */
> tag = dwarf_tag(&type);
> - if (tag != DW_TAG_structure_type && tag != DW_TAG_union_type) {
> + if (tag != DW_TAG_structure_type && tag != DW_TAG_union_type &&
> + tag != DW_TAG_class_type) {
> pr_warning("%s is not a data structure nor a union.\n",
> varname);
> return -EINVAL;
> @@ -389,7 +398,8 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
> *ref_ptr = ref;
> } else {
> /* Verify it is a data structure */
> - if (tag != DW_TAG_structure_type && tag != DW_TAG_union_type) {
> + if (tag != DW_TAG_structure_type && tag != DW_TAG_union_type &&
> + tag != DW_TAG_class_type) {
> pr_warning("%s is not a data structure nor a union.\n",
> varname);
> return -EINVAL;
> --
> 2.51.0
>