[PATCH 21/48] perf annotate: Add --data-type option

From: Namhyung Kim
Date: Wed Oct 11 2023 - 23:53:46 EST


Support data type annotation with new --data-type option. It internally
uses type sort key to collect sample histogram for the type and display
every members like below.

$ perf annotate --data-type
...
Annotate type: 'struct cfs_rq' in [kernel.kallsyms] (13 samples):
============================================================================
samples offset size field
13 0 640 struct cfs_rq {
2 0 16 struct load_weight load {
2 0 8 unsigned long weight;
0 8 4 u32 inv_weight;
};
0 16 8 unsigned long runnable_weight;
0 24 4 unsigned int nr_running;
1 28 4 unsigned int h_nr_running;
...

For simplicity it prints the number of samples per field for now.
But it should be easy to show the overhead percentage instead.

The number at the outer struct is a sum of the numbers of the inner
members. For example, struct cfs_rq got total 13 samples, and 2 came
from the load (struct load_weight) and 1 from h_nr_running. Similarly,
the struct load_weight got total 2 samples and they all came from the
weight field.

I've added two new flags in the symbol_conf for this. The
annotate_data_member is to get the members of the type. This is also
needed for perf report with typeoff sort key. The annotate_data_sample
is to update sample stats for each offset and used only in annotate.

Currently it only support stdio output mode, TUI support can be added
later.

Signed-off-by: Namhyung Kim <namhyung@xxxxxxxxxx>
---
tools/perf/builtin-annotate.c | 64 ++++++++++++++++++++++++++++++++-
tools/perf/util/annotate-data.c | 8 ++---
tools/perf/util/annotate.c | 10 +++---
tools/perf/util/sort.c | 2 ++
tools/perf/util/symbol_conf.h | 4 ++-
5 files changed, 77 insertions(+), 11 deletions(-)

diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index aeeb801f1ed7..6be15a37d2b7 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -20,6 +20,7 @@
#include "util/evlist.h"
#include "util/evsel.h"
#include "util/annotate.h"
+#include "util/annotate-data.h"
#include "util/event.h"
#include <subcmd/parse-options.h>
#include "util/parse-events.h"
@@ -56,6 +57,7 @@ struct perf_annotate {
bool skip_missing;
bool has_br_stack;
bool group_set;
+ bool data_type;
float min_percent;
const char *sym_hist_filter;
const char *cpu_list;
@@ -231,8 +233,12 @@ static int evsel__add_sample(struct evsel *evsel, struct perf_sample *sample,
{
struct hists *hists = evsel__hists(evsel);
struct hist_entry *he;
+ struct annotation *notes = al->sym ? symbol__annotation(al->sym) : NULL;
int ret;

+ if (notes)
+ notes->options = &ann->opts;
+
if ((!ann->has_br_stack || !has_annotation(ann)) &&
ann->sym_hist_filter != NULL &&
(al->sym == NULL ||
@@ -320,6 +326,32 @@ static int hist_entry__tty_annotate(struct hist_entry *he,
return symbol__tty_annotate2(&he->ms, evsel, &ann->opts);
}

+static void print_annotated_data_type(struct annotated_data_type *mem_type,
+ struct annotated_member *member,
+ struct evsel *evsel, int indent)
+{
+ struct annotated_member *child;
+ struct type_hist *h = mem_type->histograms[evsel->core.idx];
+ int i, samples = 0;
+
+ for (i = 0; i < member->size; i++)
+ samples += h->addr[member->offset + i].nr_samples;
+
+ printf(" %10d %10d %10d %*s%s\t%s",
+ samples, member->offset, member->size, indent, "", member->type_name,
+ member->var_name ?: "");
+
+ if (!list_empty(&member->children))
+ printf(" {\n");
+
+ list_for_each_entry(child, &member->children, node)
+ print_annotated_data_type(mem_type, child, evsel, indent + 4);
+
+ if (!list_empty(&member->children))
+ printf("%*s}", 35 + indent, "");
+ printf(";\n");
+}
+
static void hists__find_annotations(struct hists *hists,
struct evsel *evsel,
struct perf_annotate *ann)
@@ -359,6 +391,23 @@ static void hists__find_annotations(struct hists *hists,
continue;
}

+ if (ann->data_type) {
+ struct map *map = he->ms.map;
+
+ /* skip unknown type */
+ if (he->mem_type->histograms == NULL)
+ goto find_next;
+
+ printf("Annotate type: '%s' in %s (%d samples):\n",
+ he->mem_type->self.type_name, map->dso->name, he->stat.nr_events);
+ printf("============================================================================\n");
+ printf(" %10s %10s %10s %s\n", "samples", "offset", "size", "field");
+
+ print_annotated_data_type(he->mem_type, &he->mem_type->self, evsel, 0);
+ printf("\n");
+ goto find_next;
+ }
+
if (use_browser == 2) {
int ret;
int (*annotate)(struct hist_entry *he,
@@ -606,6 +655,8 @@ int cmd_annotate(int argc, const char **argv)
OPT_CALLBACK_OPTARG(0, "itrace", &itrace_synth_opts, NULL, "opts",
"Instruction Tracing options\n" ITRACE_HELP,
itrace_parse_synth_opts),
+ OPT_BOOLEAN(0, "data-type", &annotate.data_type,
+ "Show data type annotate for the memory accesses"),

OPT_END()
};
@@ -702,6 +753,14 @@ int cmd_annotate(int argc, const char **argv)
use_browser = 2;
#endif

+ /* FIXME: only support stdio for now */
+ if (annotate.data_type) {
+ use_browser = 0;
+ annotate.opts.annotate_src = false;
+ symbol_conf.annotate_data_member = true;
+ symbol_conf.annotate_data_sample = true;
+ }
+
setup_browser(true);

/*
@@ -709,7 +768,10 @@ int cmd_annotate(int argc, const char **argv)
* symbol, we do not care about the processes in annotate,
* set sort order to avoid repeated output.
*/
- sort_order = "dso,symbol";
+ if (annotate.data_type)
+ sort_order = "dso,type";
+ else
+ sort_order = "dso,symbol";

/*
* Set SORT_MODE__BRANCH so that annotate display IPC/Cycle
diff --git a/tools/perf/util/annotate-data.c b/tools/perf/util/annotate-data.c
index adeab45a3c63..ba7d35648b05 100644
--- a/tools/perf/util/annotate-data.c
+++ b/tools/perf/util/annotate-data.c
@@ -18,6 +18,7 @@
#include "map_symbol.h"
#include "strbuf.h"
#include "symbol.h"
+#include "symbol_conf.h"

/* Pseudo data types */
struct annotated_data_type unknown_type = {
@@ -165,11 +166,8 @@ static struct annotated_data_type *dso__findnew_data_type(struct dso *dso,
result->self.size = size;
INIT_LIST_HEAD(&result->self.children);

- /*
- * Fill member info unconditionally for now,
- * later perf annotate would need it.
- */
- add_member_types(result, type_die);
+ if (symbol_conf.annotate_data_member)
+ add_member_types(result, type_die);

rb_add(&result->node, &dso->data_types, data_type_less);
return result;
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index 49d5b61e19e6..3d9bb6b33e1a 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -3675,10 +3675,12 @@ struct annotated_data_type *hist_entry__get_data_type(struct hist_entry *he)

mem_type = find_data_type(ms, ip, op_loc->reg, op_loc->offset);

- annotated_data_type__update_samples(mem_type, evsel,
- op_loc->offset,
- he->stat.nr_events,
- he->stat.period);
+ if (symbol_conf.annotate_data_sample) {
+ annotated_data_type__update_samples(mem_type, evsel,
+ op_loc->offset,
+ he->stat.nr_events,
+ he->stat.period);
+ }
he->mem_type_off = op_loc->offset;
return mem_type;
}
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c
index e21bbd442637..35eb589c03ec 100644
--- a/tools/perf/util/sort.c
+++ b/tools/perf/util/sort.c
@@ -3394,6 +3394,8 @@ int sort_dimension__add(struct perf_hpp_list *list, const char *tok,
list->thread = 1;
} else if (sd->entry == &sort_comm) {
list->comm = 1;
+ } else if (sd->entry == &sort_type_offset) {
+ symbol_conf.annotate_data_member = true;
}

return __sort_dimension__add(sd, list, level);
diff --git a/tools/perf/util/symbol_conf.h b/tools/perf/util/symbol_conf.h
index 0b589570d1d0..e6a1c48ca3bf 100644
--- a/tools/perf/util/symbol_conf.h
+++ b/tools/perf/util/symbol_conf.h
@@ -42,7 +42,9 @@ struct symbol_conf {
inline_name,
disable_add2line_warn,
buildid_mmap2,
- guest_code;
+ guest_code,
+ annotate_data_member,
+ annotate_data_sample;
const char *vmlinux_name,
*kallsyms_name,
*source_prefix,
--
2.42.0.655.g421f12c284-goog