9fbb4b0230
Add a new branch filter, "counter", for the branch counter option. It is used to mark the events which should be logged in the branch. If it is applied with the -j option, the counters of all the events should be logged in the branch. If the legacy kernel doesn't support the new branch sample type, switching off the branch counter filter. The stored counter values in each branch are displayed right after the regular branch stack information via perf report -D. Usage examples: # perf record -e "{branch-instructions,branch-misses}:S" -j any,counter Only the first event, branch-instructions, collect the LBR. Both branch-instructions and branch-misses are marked as logged events. The occurrences information of them can be found in the branch stack extension space of each branch. # perf record -e "{cpu/branch-instructions,branch_type=any/,cpu/branch-misses,branch_type=counter/}" Only the first event, branch-instructions, collect the LBR. Only the branch-misses event is marked as a logged event. Committer notes: I noticed 'perf test "Sample parsing"' failing, reported to the list and Kan provided a patch that checks if the evsel has a leader and that evsel->evlist is set, the comment in the source code further explains it. Reviewed-by: Ian Rogers <irogers@google.com> Signed-off-by: Kan Liang <kan.liang@linux.intel.com> Cc: Adrian Hunter <adrian.hunter@intel.com> Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com> Cc: Alexey Bayduraev <alexey.v.bayduraev@linux.intel.com> Cc: Andi Kleen <ak@linux.intel.com> Cc: Ingo Molnar <mingo@redhat.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: Stephane Eranian <eranian@google.com> Cc: Tinghao Zhang <tinghao.zhang@intel.com> Link: https://lore.kernel.org/r/20231025201626.3000228-8-kan.liang@linux.intel.com Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
116 lines
2.6 KiB
C
116 lines
2.6 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
#include "util/debug.h"
|
|
#include "util/event.h"
|
|
#include <subcmd/parse-options.h>
|
|
#include "util/parse-branch-options.h"
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#define BRANCH_OPT(n, m) \
|
|
{ .name = n, .mode = (m) }
|
|
|
|
#define BRANCH_END { .name = NULL }
|
|
|
|
struct branch_mode {
|
|
const char *name;
|
|
int mode;
|
|
};
|
|
|
|
static const struct branch_mode branch_modes[] = {
|
|
BRANCH_OPT("u", PERF_SAMPLE_BRANCH_USER),
|
|
BRANCH_OPT("k", PERF_SAMPLE_BRANCH_KERNEL),
|
|
BRANCH_OPT("hv", PERF_SAMPLE_BRANCH_HV),
|
|
BRANCH_OPT("any", PERF_SAMPLE_BRANCH_ANY),
|
|
BRANCH_OPT("any_call", PERF_SAMPLE_BRANCH_ANY_CALL),
|
|
BRANCH_OPT("any_ret", PERF_SAMPLE_BRANCH_ANY_RETURN),
|
|
BRANCH_OPT("ind_call", PERF_SAMPLE_BRANCH_IND_CALL),
|
|
BRANCH_OPT("abort_tx", PERF_SAMPLE_BRANCH_ABORT_TX),
|
|
BRANCH_OPT("in_tx", PERF_SAMPLE_BRANCH_IN_TX),
|
|
BRANCH_OPT("no_tx", PERF_SAMPLE_BRANCH_NO_TX),
|
|
BRANCH_OPT("cond", PERF_SAMPLE_BRANCH_COND),
|
|
BRANCH_OPT("ind_jmp", PERF_SAMPLE_BRANCH_IND_JUMP),
|
|
BRANCH_OPT("call", PERF_SAMPLE_BRANCH_CALL),
|
|
BRANCH_OPT("no_flags", PERF_SAMPLE_BRANCH_NO_FLAGS),
|
|
BRANCH_OPT("no_cycles", PERF_SAMPLE_BRANCH_NO_CYCLES),
|
|
BRANCH_OPT("save_type", PERF_SAMPLE_BRANCH_TYPE_SAVE),
|
|
BRANCH_OPT("stack", PERF_SAMPLE_BRANCH_CALL_STACK),
|
|
BRANCH_OPT("hw_index", PERF_SAMPLE_BRANCH_HW_INDEX),
|
|
BRANCH_OPT("priv", PERF_SAMPLE_BRANCH_PRIV_SAVE),
|
|
BRANCH_OPT("counter", PERF_SAMPLE_BRANCH_COUNTERS),
|
|
BRANCH_END
|
|
};
|
|
|
|
int parse_branch_str(const char *str, __u64 *mode)
|
|
{
|
|
#define ONLY_PLM \
|
|
(PERF_SAMPLE_BRANCH_USER |\
|
|
PERF_SAMPLE_BRANCH_KERNEL |\
|
|
PERF_SAMPLE_BRANCH_HV)
|
|
|
|
int ret = 0;
|
|
char *p, *s;
|
|
char *os = NULL;
|
|
const struct branch_mode *br;
|
|
|
|
if (str == NULL) {
|
|
*mode = PERF_SAMPLE_BRANCH_ANY;
|
|
return 0;
|
|
}
|
|
|
|
/* because str is read-only */
|
|
s = os = strdup(str);
|
|
if (!s)
|
|
return -1;
|
|
|
|
for (;;) {
|
|
p = strchr(s, ',');
|
|
if (p)
|
|
*p = '\0';
|
|
|
|
for (br = branch_modes; br->name; br++) {
|
|
if (!strcasecmp(s, br->name))
|
|
break;
|
|
}
|
|
if (!br->name) {
|
|
ret = -1;
|
|
pr_warning("unknown branch filter %s,"
|
|
" check man page\n", s);
|
|
goto error;
|
|
}
|
|
|
|
*mode |= br->mode;
|
|
|
|
if (!p)
|
|
break;
|
|
|
|
s = p + 1;
|
|
}
|
|
|
|
/* default to any branch */
|
|
if ((*mode & ~ONLY_PLM) == 0) {
|
|
*mode = PERF_SAMPLE_BRANCH_ANY;
|
|
}
|
|
error:
|
|
free(os);
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
parse_branch_stack(const struct option *opt, const char *str, int unset)
|
|
{
|
|
__u64 *mode = (__u64 *)opt->value;
|
|
|
|
if (unset)
|
|
return 0;
|
|
|
|
/*
|
|
* cannot set it twice, -b + --branch-filter for instance
|
|
*/
|
|
if (*mode) {
|
|
pr_err("Error: Can't use --branch-any (-b) with --branch-filter (-j).\n");
|
|
return -1;
|
|
}
|
|
|
|
return parse_branch_str(str, mode);
|
|
}
|