162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include "util/debug.h"
362306a36Sopenharmony_ci#include "util/event.h"
462306a36Sopenharmony_ci#include <subcmd/parse-options.h>
562306a36Sopenharmony_ci#include "util/parse-branch-options.h"
662306a36Sopenharmony_ci#include <stdlib.h>
762306a36Sopenharmony_ci#include <string.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#define BRANCH_OPT(n, m) \
1062306a36Sopenharmony_ci	{ .name = n, .mode = (m) }
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#define BRANCH_END { .name = NULL }
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cistruct branch_mode {
1562306a36Sopenharmony_ci	const char *name;
1662306a36Sopenharmony_ci	int mode;
1762306a36Sopenharmony_ci};
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistatic const struct branch_mode branch_modes[] = {
2062306a36Sopenharmony_ci	BRANCH_OPT("u", PERF_SAMPLE_BRANCH_USER),
2162306a36Sopenharmony_ci	BRANCH_OPT("k", PERF_SAMPLE_BRANCH_KERNEL),
2262306a36Sopenharmony_ci	BRANCH_OPT("hv", PERF_SAMPLE_BRANCH_HV),
2362306a36Sopenharmony_ci	BRANCH_OPT("any", PERF_SAMPLE_BRANCH_ANY),
2462306a36Sopenharmony_ci	BRANCH_OPT("any_call", PERF_SAMPLE_BRANCH_ANY_CALL),
2562306a36Sopenharmony_ci	BRANCH_OPT("any_ret", PERF_SAMPLE_BRANCH_ANY_RETURN),
2662306a36Sopenharmony_ci	BRANCH_OPT("ind_call", PERF_SAMPLE_BRANCH_IND_CALL),
2762306a36Sopenharmony_ci	BRANCH_OPT("abort_tx", PERF_SAMPLE_BRANCH_ABORT_TX),
2862306a36Sopenharmony_ci	BRANCH_OPT("in_tx", PERF_SAMPLE_BRANCH_IN_TX),
2962306a36Sopenharmony_ci	BRANCH_OPT("no_tx", PERF_SAMPLE_BRANCH_NO_TX),
3062306a36Sopenharmony_ci	BRANCH_OPT("cond", PERF_SAMPLE_BRANCH_COND),
3162306a36Sopenharmony_ci	BRANCH_OPT("ind_jmp", PERF_SAMPLE_BRANCH_IND_JUMP),
3262306a36Sopenharmony_ci	BRANCH_OPT("call", PERF_SAMPLE_BRANCH_CALL),
3362306a36Sopenharmony_ci	BRANCH_OPT("no_flags", PERF_SAMPLE_BRANCH_NO_FLAGS),
3462306a36Sopenharmony_ci	BRANCH_OPT("no_cycles", PERF_SAMPLE_BRANCH_NO_CYCLES),
3562306a36Sopenharmony_ci	BRANCH_OPT("save_type", PERF_SAMPLE_BRANCH_TYPE_SAVE),
3662306a36Sopenharmony_ci	BRANCH_OPT("stack", PERF_SAMPLE_BRANCH_CALL_STACK),
3762306a36Sopenharmony_ci	BRANCH_OPT("hw_index", PERF_SAMPLE_BRANCH_HW_INDEX),
3862306a36Sopenharmony_ci	BRANCH_OPT("priv", PERF_SAMPLE_BRANCH_PRIV_SAVE),
3962306a36Sopenharmony_ci	BRANCH_END
4062306a36Sopenharmony_ci};
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ciint parse_branch_str(const char *str, __u64 *mode)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci#define ONLY_PLM \
4562306a36Sopenharmony_ci	(PERF_SAMPLE_BRANCH_USER	|\
4662306a36Sopenharmony_ci	 PERF_SAMPLE_BRANCH_KERNEL	|\
4762306a36Sopenharmony_ci	 PERF_SAMPLE_BRANCH_HV)
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	int ret = 0;
5062306a36Sopenharmony_ci	char *p, *s;
5162306a36Sopenharmony_ci	char *os = NULL;
5262306a36Sopenharmony_ci	const struct branch_mode *br;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	if (str == NULL) {
5562306a36Sopenharmony_ci		*mode = PERF_SAMPLE_BRANCH_ANY;
5662306a36Sopenharmony_ci		return 0;
5762306a36Sopenharmony_ci	}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	/* because str is read-only */
6062306a36Sopenharmony_ci	s = os = strdup(str);
6162306a36Sopenharmony_ci	if (!s)
6262306a36Sopenharmony_ci		return -1;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	for (;;) {
6562306a36Sopenharmony_ci		p = strchr(s, ',');
6662306a36Sopenharmony_ci		if (p)
6762306a36Sopenharmony_ci			*p = '\0';
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci		for (br = branch_modes; br->name; br++) {
7062306a36Sopenharmony_ci			if (!strcasecmp(s, br->name))
7162306a36Sopenharmony_ci				break;
7262306a36Sopenharmony_ci		}
7362306a36Sopenharmony_ci		if (!br->name) {
7462306a36Sopenharmony_ci			ret = -1;
7562306a36Sopenharmony_ci			pr_warning("unknown branch filter %s,"
7662306a36Sopenharmony_ci				    " check man page\n", s);
7762306a36Sopenharmony_ci			goto error;
7862306a36Sopenharmony_ci		}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci		*mode |= br->mode;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci		if (!p)
8362306a36Sopenharmony_ci			break;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci		s = p + 1;
8662306a36Sopenharmony_ci	}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	/* default to any branch */
8962306a36Sopenharmony_ci	if ((*mode & ~ONLY_PLM) == 0) {
9062306a36Sopenharmony_ci		*mode = PERF_SAMPLE_BRANCH_ANY;
9162306a36Sopenharmony_ci	}
9262306a36Sopenharmony_cierror:
9362306a36Sopenharmony_ci	free(os);
9462306a36Sopenharmony_ci	return ret;
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ciint
9862306a36Sopenharmony_ciparse_branch_stack(const struct option *opt, const char *str, int unset)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	__u64 *mode = (__u64 *)opt->value;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	if (unset)
10362306a36Sopenharmony_ci		return 0;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	/*
10662306a36Sopenharmony_ci	 * cannot set it twice, -b + --branch-filter for instance
10762306a36Sopenharmony_ci	 */
10862306a36Sopenharmony_ci	if (*mode) {
10962306a36Sopenharmony_ci		pr_err("Error: Can't use --branch-any (-b) with --branch-filter (-j).\n");
11062306a36Sopenharmony_ci		return -1;
11162306a36Sopenharmony_ci	}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	return parse_branch_str(str, mode);
11462306a36Sopenharmony_ci}
115