]> git.itanic.dy.fi Git - linux-stable/commitdiff
perf annotate: Add --data-type option
authorNamhyung Kim <namhyung@kernel.org>
Wed, 13 Dec 2023 00:13:20 +0000 (16:13 -0800)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Sun, 24 Dec 2023 01:39:43 +0000 (22:39 -0300)
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>
tools/perf/Documentation/perf-annotate.txt
tools/perf/builtin-annotate.c
tools/perf/util/annotate-data.c
tools/perf/util/annotate.c
tools/perf/util/sort.c
tools/perf/util/symbol_conf.h

index fe168e8165c8d22dd51933da407c9388c685f252..0e6a49b7795c5f9dc13480173c62a17cdd9cc547 100644 (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]
index d880f1b039fdf376106319144d201edad77d0720..8acfbbc1b9c2d0a40d6633ba5dbe548af8233bb0 100644 (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
index 868f52ccdb74c6bf2db3d5a2e5ba2b00c356a256..df9689f46619c8b9c301070064805b4b0c3a1fb2 100644 (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;
index f966e8f83c5ecd13f42f6f7604b6c548e2fe7c16..68424ee0215e4daa39f2729a4f43090c0ed2703a 100644 (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;
        }
index 0cbbd5ba8175a4af9657436e8981847813b45ff3..30254eb637099b07427d73944a6bb4e7baffc724 100644 (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);
index 6040286e07a652caa679eb1269801c2bf27b0a8b..c114bbceef4013f099b05cd39ef73b7b1813750f 100644 (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,