perf annotate: Add --data-type option

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.

Committer testing:

With the perf.data from the previous csets, a very simple, short
duration one:

  # perf annotate --data-type
  Annotate type: 'struct list_head' in [kernel.kallsyms] (1 samples):
  ============================================================================
      samples     offset       size  field
            1          0         16  struct list_head      {
            0          0          8      struct list_head*        next;
            1          8          8      struct list_head*        prev;
                                     };

  Annotate type: 'char' in [kernel.kallsyms] (1 samples):
  ============================================================================
      samples     offset       size  field
            1          0          1  char ;

  #

Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Ian Rogers <irogers@google.com>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Cc: linux-toolchains@vger.kernel.org
Cc: linux-trace-devel@vger.kernel.org
Link: https://lore.kernel.org/r/20231213001323.718046-15-namhyung@kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
Namhyung Kim 2023-12-12 16:13:20 -08:00 committed by Arnaldo Carvalho de Melo
parent e2c1c8ff2d
commit 263925bf84
6 changed files with 118 additions and 11 deletions

View file

@ -155,6 +155,14 @@ include::itrace.txt[]
stdio or stdio2 (Default: 0). Note that this is about selection of
functions to display, not about lines within the function.
--data-type[=TYPE_NAME]::
Display data type annotation instead of code. It infers data type of
samples (if they are memory accessing instructions) using DWARF debug
information. It can take an optional argument of data type name. In
that case it'd show annotation for the type only, otherwise it'd show
all data types it finds.
SEE ALSO
--------
linkperf:perf-record[1], linkperf:perf-report[1]

View file

@ -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"
@ -55,9 +56,11 @@ 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;
const char *target_data_type;
DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
};
@ -322,6 +325,32 @@ static int hist_entry__tty_annotate(struct hist_entry *he,
return symbol__tty_annotate2(&he->ms, evsel);
}
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)
@ -361,6 +390,40 @@ static void hists__find_annotations(struct hists *hists,
continue;
}
if (ann->data_type) {
struct dso *dso = map__dso(he->ms.map);
/* skip unknown type */
if (he->mem_type->histograms == NULL)
goto find_next;
if (ann->target_data_type) {
const char *type_name = he->mem_type->self.type_name;
/* skip 'struct ' prefix in the type name */
if (strncmp(ann->target_data_type, "struct ", 7) &&
!strncmp(type_name, "struct ", 7))
type_name += 7;
/* skip 'union ' prefix in the type name */
if (strncmp(ann->target_data_type, "union ", 6) &&
!strncmp(type_name, "union ", 6))
type_name += 6;
if (strcmp(ann->target_data_type, type_name))
goto find_next;
}
printf("Annotate type: '%s' in %s (%d samples):\n",
he->mem_type->self.type_name, 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,
@ -496,6 +559,17 @@ static int parse_percent_limit(const struct option *opt, const char *str,
return 0;
}
static int parse_data_type(const struct option *opt, const char *str, int unset)
{
struct perf_annotate *ann = opt->value;
ann->data_type = !unset;
if (str)
ann->target_data_type = strdup(str);
return 0;
}
static const char * const annotate_usage[] = {
"perf annotate [<options>]",
NULL
@ -607,6 +681,9 @@ 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_CALLBACK_OPTARG(0, "data-type", &annotate, NULL, "name",
"Show data type annotate for the memory accesses",
parse_data_type),
OPT_END()
};
@ -661,6 +738,13 @@ int cmd_annotate(int argc, const char **argv)
}
#endif
#ifndef HAVE_DWARF_GETLOCATIONS_SUPPORT
if (annotate.data_type) {
pr_err("Error: Data type profiling is disabled due to missing DWARF support\n");
return -ENOTSUP;
}
#endif
ret = symbol__validate_sym_arguments();
if (ret)
return ret;
@ -703,6 +787,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);
/*
@ -710,7 +802,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

View file

@ -19,6 +19,7 @@
#include "map_symbol.h"
#include "strbuf.h"
#include "symbol.h"
#include "symbol_conf.h"
/*
* Compare type name and size to maintain them in a tree.
@ -158,11 +159,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;

View file

@ -3712,10 +3712,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;
}

View file

@ -3401,6 +3401,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);

View file

@ -44,7 +44,9 @@ struct symbol_conf {
buildid_mmap2,
guest_code,
lazy_load_kernel_maps,
keep_exited_threads;
keep_exited_threads,
annotate_data_member,
annotate_data_sample;
const char *vmlinux_name,
*kallsyms_name,
*source_prefix,