Merge tag 'hid-for-linus-2024020101' of git://git.kernel.org/pub/scm/linux/kernel...
[sfrench/cifs-2.6.git] / tools / perf / builtin-annotate.c
index aeeb801f1ed7b15f1118de904d4366feb7ed3a97..6c1cc797692d949f6fc07bb195007ecfad536dd6 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"
@@ -45,7 +46,6 @@
 struct perf_annotate {
        struct perf_tool tool;
        struct perf_session *session;
-       struct annotation_options opts;
 #ifdef HAVE_SLANG_SUPPORT
        bool       use_tui;
 #endif
@@ -56,9 +56,13 @@ struct perf_annotate {
        bool       skip_missing;
        bool       has_br_stack;
        bool       group_set;
+       bool       data_type;
+       bool       type_stat;
+       bool       insn_stat;
        float      min_percent;
        const char *sym_hist_filter;
        const char *cpu_list;
+       const char *target_data_type;
        DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
 };
 
@@ -94,6 +98,7 @@ static void process_basic_block(struct addr_map_symbol *start,
        struct annotation *notes = sym ? symbol__annotation(sym) : NULL;
        struct block_range_iter iter;
        struct block_range *entry;
+       struct annotated_branch *branch;
 
        /*
         * Sanity; NULL isn't executable and the CPU cannot execute backwards
@@ -105,6 +110,8 @@ static void process_basic_block(struct addr_map_symbol *start,
        if (!block_range_iter__valid(&iter))
                return;
 
+       branch = annotation__get_branch(notes);
+
        /*
         * First block in range is a branch target.
         */
@@ -118,8 +125,8 @@ static void process_basic_block(struct addr_map_symbol *start,
                entry->coverage++;
                entry->sym = sym;
 
-               if (notes)
-                       notes->max_coverage = max(notes->max_coverage, entry->coverage);
+               if (branch)
+                       branch->max_coverage = max(branch->max_coverage, entry->coverage);
 
        } while (block_range_iter__next(&iter));
 
@@ -315,9 +322,153 @@ static int hist_entry__tty_annotate(struct hist_entry *he,
                                    struct perf_annotate *ann)
 {
        if (!ann->use_stdio2)
-               return symbol__tty_annotate(&he->ms, evsel, &ann->opts);
+               return symbol__tty_annotate(&he->ms, evsel);
+
+       return symbol__tty_annotate2(&he->ms, evsel);
+}
+
+static void print_annotated_data_header(struct hist_entry *he, struct evsel *evsel)
+{
+       struct dso *dso = map__dso(he->ms.map);
+       int nr_members = 1;
+       int nr_samples = he->stat.nr_events;
+
+       if (evsel__is_group_event(evsel)) {
+               struct hist_entry *pair;
+
+               list_for_each_entry(pair, &he->pairs.head, pairs.node)
+                       nr_samples += pair->stat.nr_events;
+       }
+
+       printf("Annotate type: '%s' in %s (%d samples):\n",
+              he->mem_type->self.type_name, dso->name, nr_samples);
+
+       if (evsel__is_group_event(evsel)) {
+               struct evsel *pos;
+               int i = 0;
+
+               for_each_group_evsel(pos, evsel)
+                       printf(" event[%d] = %s\n", i++, pos->name);
+
+               nr_members = evsel->core.nr_members;
+       }
+
+       printf("============================================================================\n");
+       printf("%*s %10s %10s  %s\n", 11 * nr_members, "samples", "offset", "size", "field");
+}
+
+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, nr_events = 1, samples = 0;
+
+       for (i = 0; i < member->size; i++)
+               samples += h->addr[member->offset + i].nr_samples;
+       printf(" %10d", samples);
 
-       return symbol__tty_annotate2(&he->ms, evsel, &ann->opts);
+       if (evsel__is_group_event(evsel)) {
+               struct evsel *pos;
+
+               for_each_group_member(pos, evsel) {
+                       h = mem_type->histograms[pos->core.idx];
+
+                       samples = 0;
+                       for (i = 0; i < member->size; i++)
+                               samples += h->addr[member->offset + i].nr_samples;
+                       printf(" %10d", samples);
+               }
+               nr_events = evsel->core.nr_members;
+       }
+
+       printf(" %10d %10d  %*s%s\t%s",
+              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}", 11 * nr_events + 24 + indent, "");
+       printf(";\n");
+}
+
+static void print_annotate_data_stat(struct annotated_data_stat *s)
+{
+#define PRINT_STAT(fld) if (s->fld) printf("%10d : %s\n", s->fld, #fld)
+
+       int bad = s->no_sym +
+                       s->no_insn +
+                       s->no_insn_ops +
+                       s->no_mem_ops +
+                       s->no_reg +
+                       s->no_dbginfo +
+                       s->no_cuinfo +
+                       s->no_var +
+                       s->no_typeinfo +
+                       s->invalid_size +
+                       s->bad_offset;
+       int ok = s->total - bad;
+
+       printf("Annotate data type stats:\n");
+       printf("total %d, ok %d (%.1f%%), bad %d (%.1f%%)\n",
+               s->total, ok, 100.0 * ok / (s->total ?: 1), bad, 100.0 * bad / (s->total ?: 1));
+       printf("-----------------------------------------------------------\n");
+       PRINT_STAT(no_sym);
+       PRINT_STAT(no_insn);
+       PRINT_STAT(no_insn_ops);
+       PRINT_STAT(no_mem_ops);
+       PRINT_STAT(no_reg);
+       PRINT_STAT(no_dbginfo);
+       PRINT_STAT(no_cuinfo);
+       PRINT_STAT(no_var);
+       PRINT_STAT(no_typeinfo);
+       PRINT_STAT(invalid_size);
+       PRINT_STAT(bad_offset);
+       printf("\n");
+
+#undef PRINT_STAT
+}
+
+static void print_annotate_item_stat(struct list_head *head, const char *title)
+{
+       struct annotated_item_stat *istat, *pos, *iter;
+       int total_good, total_bad, total;
+       int sum1, sum2;
+       LIST_HEAD(tmp);
+
+       /* sort the list by count */
+       list_splice_init(head, &tmp);
+       total_good = total_bad = 0;
+
+       list_for_each_entry_safe(istat, pos, &tmp, list) {
+               total_good += istat->good;
+               total_bad += istat->bad;
+               sum1 = istat->good + istat->bad;
+
+               list_for_each_entry(iter, head, list) {
+                       sum2 = iter->good + iter->bad;
+                       if (sum1 > sum2)
+                               break;
+               }
+               list_move_tail(&istat->list, &iter->list);
+       }
+       total = total_good + total_bad;
+
+       printf("Annotate %s stats\n", title);
+       printf("total %d, ok %d (%.1f%%), bad %d (%.1f%%)\n\n", total,
+              total_good, 100.0 * total_good / (total ?: 1),
+              total_bad, 100.0 * total_bad / (total ?: 1));
+       printf("  %-10s: %5s %5s\n", "Name", "Good", "Bad");
+       printf("-----------------------------------------------------------\n");
+       list_for_each_entry(istat, head, list)
+               printf("  %-10s: %5d %5d\n", istat->name, istat->good, istat->bad);
+       printf("\n");
 }
 
 static void hists__find_annotations(struct hists *hists,
@@ -327,6 +478,11 @@ static void hists__find_annotations(struct hists *hists,
        struct rb_node *nd = rb_first_cached(&hists->entries), *next;
        int key = K_RIGHT;
 
+       if (ann->type_stat)
+               print_annotate_data_stat(&ann_data_stat);
+       if (ann->insn_stat)
+               print_annotate_item_stat(&ann_insn_stat, "Instruction");
+
        while (nd) {
                struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
                struct annotation *notes;
@@ -359,11 +515,38 @@ find_next:
                        continue;
                }
 
+               if (ann->data_type) {
+                       /* 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;
+                       }
+
+                       print_annotated_data_header(he, evsel);
+                       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,
                                        struct evsel *evsel,
-                                       struct annotation_options *options,
                                        struct hist_browser_timer *hbt);
 
                        annotate = dlsym(perf_gtk_handle,
@@ -373,14 +556,14 @@ find_next:
                                return;
                        }
 
-                       ret = annotate(he, evsel, &ann->opts, NULL);
+                       ret = annotate(he, evsel, NULL);
                        if (!ret || !ann->skip_missing)
                                return;
 
                        /* skip missing symbols */
                        nd = rb_next(nd);
                } else if (use_browser == 1) {
-                       key = hist_entry__tui_annotate(he, evsel, NULL, &ann->opts);
+                       key = hist_entry__tui_annotate(he, evsel, NULL);
 
                        switch (key) {
                        case -1:
@@ -422,9 +605,9 @@ static int __cmd_annotate(struct perf_annotate *ann)
                        goto out;
        }
 
-       if (!ann->opts.objdump_path) {
+       if (!annotate_opts.objdump_path) {
                ret = perf_env__lookup_objdump(&session->header.env,
-                                              &ann->opts.objdump_path);
+                                              &annotate_opts.objdump_path);
                if (ret)
                        goto out;
        }
@@ -457,8 +640,20 @@ static int __cmd_annotate(struct perf_annotate *ann)
                        evsel__reset_sample_bit(pos, CALLCHAIN);
                        evsel__output_resort(pos, NULL);
 
-                       if (symbol_conf.event_group && !evsel__is_group_leader(pos))
+                       /*
+                        * An event group needs to display other events too.
+                        * Let's delay printing until other events are processed.
+                        */
+                       if (symbol_conf.event_group) {
+                               if (!evsel__is_group_leader(pos)) {
+                                       struct hists *leader_hists;
+
+                                       leader_hists = evsel__hists(evsel__leader(pos));
+                                       hists__match(leader_hists, hists);
+                                       hists__link(leader_hists, hists);
+                               }
                                continue;
+                       }
 
                        hists__find_annotations(hists, pos, ann);
                }
@@ -469,6 +664,20 @@ static int __cmd_annotate(struct perf_annotate *ann)
                goto out;
        }
 
+       /* Display group events together */
+       evlist__for_each_entry(session->evlist, pos) {
+               struct hists *hists = evsel__hists(pos);
+               u32 nr_samples = hists->stats.nr_samples;
+
+               if (nr_samples == 0)
+                       continue;
+
+               if (!symbol_conf.event_group || !evsel__is_group_leader(pos))
+                       continue;
+
+               hists__find_annotations(hists, pos, ann);
+       }
+
        if (use_browser == 2) {
                void (*show_annotations)(void);
 
@@ -495,6 +704,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
@@ -558,9 +778,9 @@ int cmd_annotate(int argc, const char **argv)
                   "file", "vmlinux pathname"),
        OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules,
                    "load module symbols - WARNING: use only with -k and LIVE kernel"),
-       OPT_BOOLEAN('l', "print-line", &annotate.opts.print_lines,
+       OPT_BOOLEAN('l', "print-line", &annotate_opts.print_lines,
                    "print matching source lines (may be slow)"),
-       OPT_BOOLEAN('P', "full-paths", &annotate.opts.full_path,
+       OPT_BOOLEAN('P', "full-paths", &annotate_opts.full_path,
                    "Don't shorten the displayed pathnames"),
        OPT_BOOLEAN(0, "skip-missing", &annotate.skip_missing,
                    "Skip symbols that cannot be annotated"),
@@ -571,15 +791,15 @@ int cmd_annotate(int argc, const char **argv)
        OPT_CALLBACK(0, "symfs", NULL, "directory",
                     "Look for files with symbols relative to this directory",
                     symbol__config_symfs),
-       OPT_BOOLEAN(0, "source", &annotate.opts.annotate_src,
+       OPT_BOOLEAN(0, "source", &annotate_opts.annotate_src,
                    "Interleave source code with assembly code (default)"),
-       OPT_BOOLEAN(0, "asm-raw", &annotate.opts.show_asm_raw,
+       OPT_BOOLEAN(0, "asm-raw", &annotate_opts.show_asm_raw,
                    "Display raw encoding of assembly instructions (default)"),
        OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style",
                   "Specify disassembler style (e.g. -M intel for intel syntax)"),
-       OPT_STRING(0, "prefix", &annotate.opts.prefix, "prefix",
+       OPT_STRING(0, "prefix", &annotate_opts.prefix, "prefix",
                    "Add prefix to source file path names in programs (with --prefix-strip)"),
-       OPT_STRING(0, "prefix-strip", &annotate.opts.prefix_strip, "N",
+       OPT_STRING(0, "prefix-strip", &annotate_opts.prefix_strip, "N",
                    "Strip first N entries of source file path name in programs (with --prefix)"),
        OPT_STRING(0, "objdump", &objdump_path, "path",
                   "objdump binary to use for disassembly and annotations"),
@@ -598,7 +818,7 @@ int cmd_annotate(int argc, const char **argv)
        OPT_CALLBACK_DEFAULT(0, "stdio-color", NULL, "mode",
                             "'always' (default), 'never' or 'auto' only applicable to --stdio mode",
                             stdio__config_color, "always"),
-       OPT_CALLBACK(0, "percent-type", &annotate.opts, "local-period",
+       OPT_CALLBACK(0, "percent-type", &annotate_opts, "local-period",
                     "Set percent type local/global-period/hits",
                     annotate_parse_percent_type),
        OPT_CALLBACK(0, "percent-limit", &annotate, "percent",
@@ -606,7 +826,13 @@ 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_BOOLEAN(0, "type-stat", &annotate.type_stat,
+                   "Show stats for the data type annotation"),
+       OPT_BOOLEAN(0, "insn-stat", &annotate.insn_stat,
+                   "Show instruction stats for the data type annotation"),
        OPT_END()
        };
        int ret;
@@ -614,13 +840,13 @@ int cmd_annotate(int argc, const char **argv)
        set_option_flag(options, 0, "show-total-period", PARSE_OPT_EXCLUSIVE);
        set_option_flag(options, 0, "show-nr-samples", PARSE_OPT_EXCLUSIVE);
 
-       annotation_options__init(&annotate.opts);
+       annotation_options__init();
 
        ret = hists__init();
        if (ret < 0)
                return ret;
 
-       annotation_config__init(&annotate.opts);
+       annotation_config__init();
 
        argc = parse_options(argc, argv, options, annotate_usage, 0);
        if (argc) {
@@ -635,13 +861,13 @@ int cmd_annotate(int argc, const char **argv)
        }
 
        if (disassembler_style) {
-               annotate.opts.disassembler_style = strdup(disassembler_style);
-               if (!annotate.opts.disassembler_style)
+               annotate_opts.disassembler_style = strdup(disassembler_style);
+               if (!annotate_opts.disassembler_style)
                        return -ENOMEM;
        }
        if (objdump_path) {
-               annotate.opts.objdump_path = strdup(objdump_path);
-               if (!annotate.opts.objdump_path)
+               annotate_opts.objdump_path = strdup(objdump_path);
+               if (!annotate_opts.objdump_path)
                        return -ENOMEM;
        }
        if (addr2line_path) {
@@ -650,7 +876,7 @@ int cmd_annotate(int argc, const char **argv)
                        return -ENOMEM;
        }
 
-       if (annotate_check_args(&annotate.opts) < 0)
+       if (annotate_check_args() < 0)
                return -EINVAL;
 
 #ifdef HAVE_GTK2_SUPPORT
@@ -660,6 +886,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;
@@ -702,6 +935,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 +950,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
@@ -731,7 +975,7 @@ out_delete:
 #ifndef NDEBUG
        perf_session__delete(annotate.session);
 #endif
-       annotation_options__exit(&annotate.opts);
+       annotation_options__exit();
 
        return ret;
 }