mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-29 05:44:11 +00:00
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:
parent
e2c1c8ff2d
commit
263925bf84
6 changed files with 118 additions and 11 deletions
|
@ -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]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue