perf branch: Extend branch type classification
This updates the perf tool with generic branch type classification with new
ABI extender place holder i.e PERF_BR_EXTEND_ABI, the new 4 bit branch type
field i.e perf_branch_entry.new_type, new generic page fault related branch
types and some arch specific branch types as added earlier in the kernel.
Committer note:
Add an extra entry to the branch_type_name array to cope with
PERF_BR_EXTEND_ABI, to address build warnings on some compiler/systems,
like:
75 8.89 ubuntu:20.04-x-powerpc64el : FAIL gcc version 10.3.0 (Ubuntu 10.3.0-1ubuntu1~20.04)
inlined from 'branch_type_stat_display' at util/branch.c:152:4:
/usr/powerpc64le-linux-gnu/include/bits/stdio2.h:100:10: error: '%8s' directive argument is null [-Werror=format-overflow=]
100 | return __fprintf_chk (__stream, __USE_FORTIFY_LEVEL - 1, __fmt,
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
101 | __va_arg_pack ());
| ~~~~~~~~~~~~~~~~~
Signed-off-by: Anshuman Khandual <anshuman.khandual@arm.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Clark <james.clark@arm.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Robin Murphy <robin.murphy@arm.com>
Cc: Stephen Rothwell <sfr@canb.auug.org.au>
Cc: Suzuki Poulouse <suzuki.poulose@arm.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Will Deacon <will@kernel.org>
Cc: linux-arm-kernel@lists.infradead.org
Link: https://lore.kernel.org/r/20220824044822.70230-7-anshuman.khandual@arm.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
committed by
Arnaldo Carvalho de Melo
parent
1c96b6e45f
commit
0ddea8e2a0
@@ -255,9 +255,22 @@ enum {
|
|||||||
PERF_BR_IRQ = 12, /* irq */
|
PERF_BR_IRQ = 12, /* irq */
|
||||||
PERF_BR_SERROR = 13, /* system error */
|
PERF_BR_SERROR = 13, /* system error */
|
||||||
PERF_BR_NO_TX = 14, /* not in transaction */
|
PERF_BR_NO_TX = 14, /* not in transaction */
|
||||||
|
PERF_BR_EXTEND_ABI = 15, /* extend ABI */
|
||||||
PERF_BR_MAX,
|
PERF_BR_MAX,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
PERF_BR_NEW_FAULT_ALGN = 0, /* Alignment fault */
|
||||||
|
PERF_BR_NEW_FAULT_DATA = 1, /* Data fault */
|
||||||
|
PERF_BR_NEW_FAULT_INST = 2, /* Inst fault */
|
||||||
|
PERF_BR_NEW_ARCH_1 = 3, /* Architecture specific */
|
||||||
|
PERF_BR_NEW_ARCH_2 = 4, /* Architecture specific */
|
||||||
|
PERF_BR_NEW_ARCH_3 = 5, /* Architecture specific */
|
||||||
|
PERF_BR_NEW_ARCH_4 = 6, /* Architecture specific */
|
||||||
|
PERF_BR_NEW_ARCH_5 = 7, /* Architecture specific */
|
||||||
|
PERF_BR_NEW_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
#define PERF_SAMPLE_BRANCH_PLM_ALL \
|
#define PERF_SAMPLE_BRANCH_PLM_ALL \
|
||||||
(PERF_SAMPLE_BRANCH_USER|\
|
(PERF_SAMPLE_BRANCH_USER|\
|
||||||
PERF_SAMPLE_BRANCH_KERNEL|\
|
PERF_SAMPLE_BRANCH_KERNEL|\
|
||||||
@@ -1375,7 +1388,8 @@ struct perf_branch_entry {
|
|||||||
abort:1, /* transaction abort */
|
abort:1, /* transaction abort */
|
||||||
cycles:16, /* cycle count to last branch */
|
cycles:16, /* cycle count to last branch */
|
||||||
type:4, /* branch type */
|
type:4, /* branch type */
|
||||||
reserved:40;
|
new_type:4, /* additional branch type */
|
||||||
|
reserved:36;
|
||||||
};
|
};
|
||||||
|
|
||||||
union perf_sample_weight {
|
union perf_sample_weight {
|
||||||
|
|||||||
@@ -882,7 +882,7 @@ static int print_bstack_flags(FILE *fp, struct branch_entry *br)
|
|||||||
br->flags.in_tx ? 'X' : '-',
|
br->flags.in_tx ? 'X' : '-',
|
||||||
br->flags.abort ? 'A' : '-',
|
br->flags.abort ? 'A' : '-',
|
||||||
br->flags.cycles,
|
br->flags.cycles,
|
||||||
br->flags.type ? branch_type_name(br->flags.type) : "-");
|
get_branch_type(br));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int perf_sample__fprintf_brstack(struct perf_sample *sample,
|
static int perf_sample__fprintf_brstack(struct perf_sample *sample,
|
||||||
|
|||||||
@@ -21,6 +21,9 @@ void branch_type_count(struct branch_type_stat *st, struct branch_flags *flags,
|
|||||||
if (flags->type == PERF_BR_UNKNOWN || from == 0)
|
if (flags->type == PERF_BR_UNKNOWN || from == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (flags->type == PERF_BR_EXTEND_ABI)
|
||||||
|
st->new_counts[flags->new_type]++;
|
||||||
|
else
|
||||||
st->counts[flags->type]++;
|
st->counts[flags->type]++;
|
||||||
|
|
||||||
if (flags->type == PERF_BR_COND) {
|
if (flags->type == PERF_BR_COND) {
|
||||||
@@ -36,6 +39,25 @@ void branch_type_count(struct branch_type_stat *st, struct branch_flags *flags,
|
|||||||
st->cross_4k++;
|
st->cross_4k++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char *branch_new_type_name(int new_type)
|
||||||
|
{
|
||||||
|
const char *branch_new_names[PERF_BR_NEW_MAX] = {
|
||||||
|
"FAULT_ALGN",
|
||||||
|
"FAULT_DATA",
|
||||||
|
"FAULT_INST",
|
||||||
|
"ARCH_1",
|
||||||
|
"ARCH_2",
|
||||||
|
"ARCH_3",
|
||||||
|
"ARCH_4",
|
||||||
|
"ARCH_5"
|
||||||
|
};
|
||||||
|
|
||||||
|
if (new_type >= 0 && new_type < PERF_BR_NEW_MAX)
|
||||||
|
return branch_new_names[new_type];
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
const char *branch_type_name(int type)
|
const char *branch_type_name(int type)
|
||||||
{
|
{
|
||||||
const char *branch_names[PERF_BR_MAX] = {
|
const char *branch_names[PERF_BR_MAX] = {
|
||||||
@@ -53,7 +75,8 @@ const char *branch_type_name(int type)
|
|||||||
"ERET",
|
"ERET",
|
||||||
"IRQ",
|
"IRQ",
|
||||||
"SERROR",
|
"SERROR",
|
||||||
"NO_TX"
|
"NO_TX",
|
||||||
|
"", // Needed for PERF_BR_EXTEND_ABI that ends up triggering some compiler warnings about NULL deref
|
||||||
};
|
};
|
||||||
|
|
||||||
if (type >= 0 && type < PERF_BR_MAX)
|
if (type >= 0 && type < PERF_BR_MAX)
|
||||||
@@ -62,6 +85,17 @@ const char *branch_type_name(int type)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char *get_branch_type(struct branch_entry *e)
|
||||||
|
{
|
||||||
|
if (e->flags.type == PERF_BR_UNKNOWN)
|
||||||
|
return "";
|
||||||
|
|
||||||
|
if (e->flags.type == PERF_BR_EXTEND_ABI)
|
||||||
|
return branch_new_type_name(e->flags.new_type);
|
||||||
|
|
||||||
|
return branch_type_name(e->flags.type);
|
||||||
|
}
|
||||||
|
|
||||||
void branch_type_stat_display(FILE *fp, struct branch_type_stat *st)
|
void branch_type_stat_display(FILE *fp, struct branch_type_stat *st)
|
||||||
{
|
{
|
||||||
u64 total = 0;
|
u64 total = 0;
|
||||||
@@ -108,6 +142,15 @@ void branch_type_stat_display(FILE *fp, struct branch_type_stat *st)
|
|||||||
100.0 *
|
100.0 *
|
||||||
(double)st->counts[i] / (double)total);
|
(double)st->counts[i] / (double)total);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < PERF_BR_NEW_MAX; i++) {
|
||||||
|
if (st->new_counts[i] > 0)
|
||||||
|
fprintf(fp, "\n%8s: %5.1f%%",
|
||||||
|
branch_new_type_name(i),
|
||||||
|
100.0 *
|
||||||
|
(double)st->new_counts[i] / (double)total);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int count_str_scnprintf(int idx, const char *str, char *bf, int size)
|
static int count_str_scnprintf(int idx, const char *str, char *bf, int size)
|
||||||
@@ -123,6 +166,9 @@ int branch_type_str(struct branch_type_stat *st, char *bf, int size)
|
|||||||
for (i = 0; i < PERF_BR_MAX; i++)
|
for (i = 0; i < PERF_BR_MAX; i++)
|
||||||
total += st->counts[i];
|
total += st->counts[i];
|
||||||
|
|
||||||
|
for (i = 0; i < PERF_BR_NEW_MAX; i++)
|
||||||
|
total += st->new_counts[i];
|
||||||
|
|
||||||
if (total == 0)
|
if (total == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@@ -140,6 +186,11 @@ int branch_type_str(struct branch_type_stat *st, char *bf, int size)
|
|||||||
printed += count_str_scnprintf(j++, branch_type_name(i), bf + printed, size - printed);
|
printed += count_str_scnprintf(j++, branch_type_name(i), bf + printed, size - printed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < PERF_BR_NEW_MAX; i++) {
|
||||||
|
if (st->new_counts[i] > 0)
|
||||||
|
printed += count_str_scnprintf(j++, branch_new_type_name(i), bf + printed, size - printed);
|
||||||
|
}
|
||||||
|
|
||||||
if (st->cross_4k > 0)
|
if (st->cross_4k > 0)
|
||||||
printed += count_str_scnprintf(j++, "CROSS_4K", bf + printed, size - printed);
|
printed += count_str_scnprintf(j++, "CROSS_4K", bf + printed, size - printed);
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ struct branch_flags {
|
|||||||
u64 abort:1;
|
u64 abort:1;
|
||||||
u64 cycles:16;
|
u64 cycles:16;
|
||||||
u64 type:4;
|
u64 type:4;
|
||||||
u64 reserved:40;
|
u64 new_type:4;
|
||||||
|
u64 reserved:36;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -72,6 +73,7 @@ static inline struct branch_entry *perf_sample__branch_entries(struct perf_sampl
|
|||||||
struct branch_type_stat {
|
struct branch_type_stat {
|
||||||
bool branch_to;
|
bool branch_to;
|
||||||
u64 counts[PERF_BR_MAX];
|
u64 counts[PERF_BR_MAX];
|
||||||
|
u64 new_counts[PERF_BR_NEW_MAX];
|
||||||
u64 cond_fwd;
|
u64 cond_fwd;
|
||||||
u64 cond_bwd;
|
u64 cond_bwd;
|
||||||
u64 cross_4k;
|
u64 cross_4k;
|
||||||
@@ -82,6 +84,8 @@ void branch_type_count(struct branch_type_stat *st, struct branch_flags *flags,
|
|||||||
u64 from, u64 to);
|
u64 from, u64 to);
|
||||||
|
|
||||||
const char *branch_type_name(int type);
|
const char *branch_type_name(int type);
|
||||||
|
const char *branch_new_type_name(int new_type);
|
||||||
|
const char *get_branch_type(struct branch_entry *e);
|
||||||
void branch_type_stat_display(FILE *fp, struct branch_type_stat *st);
|
void branch_type_stat_display(FILE *fp, struct branch_type_stat *st);
|
||||||
int branch_type_str(struct branch_type_stat *st, char *bf, int bfsize);
|
int branch_type_str(struct branch_type_stat *st, char *bf, int bfsize);
|
||||||
|
|
||||||
|
|||||||
@@ -1180,7 +1180,7 @@ static void branch_stack__printf(struct perf_sample *sample, bool callstack)
|
|||||||
e->flags.abort ? "A" : " ",
|
e->flags.abort ? "A" : " ",
|
||||||
e->flags.in_tx ? "T" : " ",
|
e->flags.in_tx ? "T" : " ",
|
||||||
(unsigned)e->flags.reserved,
|
(unsigned)e->flags.reserved,
|
||||||
e->flags.type ? branch_type_name(e->flags.type) : "");
|
get_branch_type(e));
|
||||||
} else {
|
} else {
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
printf("..... %2"PRIu64": %016" PRIx64 "\n"
|
printf("..... %2"PRIu64": %016" PRIx64 "\n"
|
||||||
|
|||||||
Reference in New Issue
Block a user