mirror of
https://github.com/torvalds/linux.git
synced 2024-12-01 16:41:39 +00:00
perf report: Add 'typeoff' sort key
The typeoff sort key shows the data type name, offset and the name of the field. This is useful to see which field in the struct is accessed most frequently. $ perf report -s type,typeoff --hierarchy --stdio ... # Overhead Data Type / Data Type Offset # ............ ............................ # ... 1.23% struct cfs_rq 0.19% struct cfs_rq +404 (throttle_count) 0.19% struct cfs_rq +0 (load.weight) 0.19% struct cfs_rq +336 (leaf_cfs_rq_list.next) 0.09% struct cfs_rq +272 (propagate) 0.09% struct cfs_rq +196 (removed.nr) 0.09% struct cfs_rq +80 (curr) 0.09% struct cfs_rq +544 (lt_b_children_throttled) 0.06% struct cfs_rq +320 (rq) Committer testing: Again with the perf.data from the previous csets: # perf report --stdio -s type,typeoff # To display the perf.data header info, please use --header/--header-only options. # # # Total Lost Samples: 0 # # Samples: 4 of event 'cpu_atom/mem-loads,ldlat=30/P' # Event count (approx.): 7 # # Overhead Data Type Data Type Offset # ........ ......... ................ # 42.86% struct list_head struct list_head +8 (prev) 42.86% (unknown) (unknown) +0 (no field) 14.29% char char +0 (no field) # # (Tip: To see callchains in a more compact form: perf report -g folded) # # perf report --stdio -s dso,type,typeoff # To display the perf.data header info, please use --header/--header-only options. # # # Total Lost Samples: 0 # # Samples: 4 of event 'cpu_atom/mem-loads,ldlat=30/P' # Event count (approx.): 7 # # Overhead Shared Object Data Type Data Type Offset # ........ .................... ......... ................ # 42.86% [kernel.kallsyms] struct list_head struct list_head +8 (prev) 28.57% libc.so.6 (unknown) (unknown) +0 (no field) 14.29% [kernel.kallsyms] char char +0 (no field) 14.29% ld-linux-x86-64.so.2 (unknown) (unknown) +0 (no field) # # (Tip: If you have debuginfo enabled, try: perf report -s sym,srcline) # # 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-13-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
parent
9bd7ddd157
commit
871304a79f
@ -119,6 +119,7 @@ OPTIONS
|
||||
to the previous instruction in cycles. And currently supported only on X86
|
||||
- simd: Flags describing a SIMD operation. "e" for empty Arm SVE predicate. "p" for partial Arm SVE predicate
|
||||
- type: Data type of sample memory access.
|
||||
- typeoff: Offset in the data type of sample memory access.
|
||||
|
||||
By default, comm, dso and symbol keys are used.
|
||||
(i.e. --sort comm,dso,symbol)
|
||||
|
@ -3716,6 +3716,7 @@ struct annotated_data_type *hist_entry__get_data_type(struct hist_entry *he)
|
||||
op_loc->offset,
|
||||
he->stat.nr_events,
|
||||
he->stat.period);
|
||||
he->mem_type_off = op_loc->offset;
|
||||
return mem_type;
|
||||
}
|
||||
return NULL;
|
||||
|
@ -83,6 +83,7 @@ enum hist_column {
|
||||
HISTC_ADDR,
|
||||
HISTC_SIMD,
|
||||
HISTC_TYPE,
|
||||
HISTC_TYPE_OFFSET,
|
||||
HISTC_NR_COLS, /* Last entry */
|
||||
};
|
||||
|
||||
|
@ -2153,8 +2153,10 @@ static void sort__type_init(struct hist_entry *he)
|
||||
return;
|
||||
|
||||
he->mem_type = hist_entry__get_data_type(he);
|
||||
if (he->mem_type == NULL)
|
||||
if (he->mem_type == NULL) {
|
||||
he->mem_type = &unknown_type;
|
||||
he->mem_type_off = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int64_t
|
||||
@ -2198,6 +2200,84 @@ struct sort_entry sort_type = {
|
||||
.se_width_idx = HISTC_TYPE,
|
||||
};
|
||||
|
||||
/* --sort typeoff */
|
||||
|
||||
static int64_t
|
||||
sort__typeoff_sort(struct hist_entry *left, struct hist_entry *right)
|
||||
{
|
||||
struct annotated_data_type *left_type = left->mem_type;
|
||||
struct annotated_data_type *right_type = right->mem_type;
|
||||
int64_t ret;
|
||||
|
||||
if (!left_type) {
|
||||
sort__type_init(left);
|
||||
left_type = left->mem_type;
|
||||
}
|
||||
|
||||
if (!right_type) {
|
||||
sort__type_init(right);
|
||||
right_type = right->mem_type;
|
||||
}
|
||||
|
||||
ret = strcmp(left_type->self.type_name, right_type->self.type_name);
|
||||
if (ret)
|
||||
return ret;
|
||||
return left->mem_type_off - right->mem_type_off;
|
||||
}
|
||||
|
||||
static void fill_member_name(char *buf, size_t sz, struct annotated_member *m,
|
||||
int offset, bool first)
|
||||
{
|
||||
struct annotated_member *child;
|
||||
|
||||
if (list_empty(&m->children))
|
||||
return;
|
||||
|
||||
list_for_each_entry(child, &m->children, node) {
|
||||
if (child->offset <= offset && offset < child->offset + child->size) {
|
||||
int len = 0;
|
||||
|
||||
/* It can have anonymous struct/union members */
|
||||
if (child->var_name) {
|
||||
len = scnprintf(buf, sz, "%s%s",
|
||||
first ? "" : ".", child->var_name);
|
||||
first = false;
|
||||
}
|
||||
|
||||
fill_member_name(buf + len, sz - len, child, offset, first);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int hist_entry__typeoff_snprintf(struct hist_entry *he, char *bf,
|
||||
size_t size, unsigned int width __maybe_unused)
|
||||
{
|
||||
struct annotated_data_type *he_type = he->mem_type;
|
||||
char buf[4096];
|
||||
|
||||
buf[0] = '\0';
|
||||
if (list_empty(&he_type->self.children))
|
||||
snprintf(buf, sizeof(buf), "no field");
|
||||
else
|
||||
fill_member_name(buf, sizeof(buf), &he_type->self,
|
||||
he->mem_type_off, true);
|
||||
buf[4095] = '\0';
|
||||
|
||||
return repsep_snprintf(bf, size, "%s %+d (%s)", he_type->self.type_name,
|
||||
he->mem_type_off, buf);
|
||||
}
|
||||
|
||||
struct sort_entry sort_type_offset = {
|
||||
.se_header = "Data Type Offset",
|
||||
.se_cmp = sort__type_cmp,
|
||||
.se_collapse = sort__typeoff_sort,
|
||||
.se_sort = sort__typeoff_sort,
|
||||
.se_init = sort__type_init,
|
||||
.se_snprintf = hist_entry__typeoff_snprintf,
|
||||
.se_width_idx = HISTC_TYPE_OFFSET,
|
||||
};
|
||||
|
||||
|
||||
struct sort_dimension {
|
||||
const char *name;
|
||||
@ -2254,6 +2334,7 @@ static struct sort_dimension common_sort_dimensions[] = {
|
||||
DIM(SORT_GLOBAL_RETIRE_LAT, "retire_lat", sort_global_p_stage_cyc),
|
||||
DIM(SORT_SIMD, "simd", sort_simd),
|
||||
DIM(SORT_ANNOTATE_DATA_TYPE, "type", sort_type),
|
||||
DIM(SORT_ANNOTATE_DATA_TYPE_OFFSET, "typeoff", sort_type_offset),
|
||||
};
|
||||
|
||||
#undef DIM
|
||||
|
@ -113,6 +113,7 @@ struct hist_entry {
|
||||
u64 p_stage_cyc;
|
||||
u8 cpumode;
|
||||
u8 depth;
|
||||
int mem_type_off;
|
||||
struct simd_flags simd_flags;
|
||||
|
||||
/* We are added by hists__add_dummy_entry. */
|
||||
@ -247,6 +248,7 @@ enum sort_type {
|
||||
SORT_GLOBAL_RETIRE_LAT,
|
||||
SORT_SIMD,
|
||||
SORT_ANNOTATE_DATA_TYPE,
|
||||
SORT_ANNOTATE_DATA_TYPE_OFFSET,
|
||||
|
||||
/* branch stack specific sort keys */
|
||||
__SORT_BRANCH_STACK,
|
||||
|
Loading…
Reference in New Issue
Block a user