Commit 448d732c authored by Ian Rogers's avatar Ian Rogers Committed by Arnaldo Carvalho de Melo
Browse files

perf parse: Add parse events handle error



Parse event error handling may overwrite one error string with another
creating memory leaks. Introduce a helper routine that warns about
multiple error messages as well as avoiding the memory leak.

A reproduction of this problem can be seen with:

  perf stat -e c/c/

After this change this produces:
WARNING: multiple event parsing errors
event syntax error: 'c/c/'
                       \___ unknown term

valid terms: event,filter_rem,filter_opc0,edge,filter_isoc,filter_tid,filter_loc,filter_nc,inv,umask,filter_opc1,tid_en,thresh,filter_all_op,filter_not_nm,filter_state,filter_nm,config,config1,config2,name,period,percore
Run 'perf list' for a list of valid events

 Usage: perf stat [<options>] [<command>]

    -e, --event <event>   event selector. use 'perf list' to list available events

Signed-off-by: default avatarIan Rogers <irogers@google.com>
Acked-by: default avatarJiri Olsa <jolsa@kernel.org>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: Jin Yao <yao.jin@linux.intel.com>
Cc: John Garry <john.garry@huawei.com>
Cc: Kan Liang <kan.liang@linux.intel.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Martin KaFai Lau <kafai@fb.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Song Liu <songliubraving@fb.com>
Cc: Stephane Eranian <eranian@google.com>
Cc: Yonghong Song <yhs@fb.com>
Cc: bpf@vger.kernel.org
Cc: clang-built-linux@googlegroups.com
Cc: netdev@vger.kernel.org
Link: http://lore.kernel.org/lkml/20191030223448.12930-2-irogers@google.com


Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent ef5502a1
Loading
Loading
Loading
Loading
+51 −31
Original line number Diff line number Diff line
@@ -182,6 +182,20 @@ static int tp_event_has_id(const char *dir_path, struct dirent *evt_dir)

#define MAX_EVENT_LENGTH 512

void parse_events__handle_error(struct parse_events_error *err, int idx,
				char *str, char *help)
{
	if (WARN(!str, "WARNING: failed to provide error string\n")) {
		free(help);
		return;
	}
	WARN_ONCE(err->str, "WARNING: multiple event parsing errors\n");
	err->idx = idx;
	free(err->str);
	err->str = str;
	free(err->help);
	err->help = help;
}

struct tracepoint_path *tracepoint_id_to_path(u64 config)
{
@@ -932,11 +946,11 @@ static int check_type_val(struct parse_events_term *term,
		return 0;

	if (err) {
		err->idx = term->err_val;
		if (type == PARSE_EVENTS__TERM_TYPE_NUM)
			err->str = strdup("expected numeric value");
		else
			err->str = strdup("expected string value");
		parse_events__handle_error(err, term->err_val,
					type == PARSE_EVENTS__TERM_TYPE_NUM
					? strdup("expected numeric value")
					: strdup("expected string value"),
					NULL);
	}
	return -EINVAL;
}
@@ -972,8 +986,11 @@ static bool config_term_shrinked;
static bool
config_term_avail(int term_type, struct parse_events_error *err)
{
	char *err_str;

	if (term_type < 0 || term_type >= __PARSE_EVENTS__TERM_TYPE_NR) {
		err->str = strdup("Invalid term_type");
		parse_events__handle_error(err, -1,
					strdup("Invalid term_type"), NULL);
		return false;
	}
	if (!config_term_shrinked)
@@ -992,9 +1009,9 @@ config_term_avail(int term_type, struct parse_events_error *err)
			return false;

		/* term_type is validated so indexing is safe */
		if (asprintf(&err->str, "'%s' is not usable in 'perf stat'",
			     config_term_names[term_type]) < 0)
			err->str = NULL;
		if (asprintf(&err_str, "'%s' is not usable in 'perf stat'",
				config_term_names[term_type]) >= 0)
			parse_events__handle_error(err, -1, err_str, NULL);
		return false;
	}
}
@@ -1036,17 +1053,20 @@ do { \
	case PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE:
		CHECK_TYPE_VAL(STR);
		if (strcmp(term->val.str, "no") &&
		    parse_branch_str(term->val.str, &attr->branch_sample_type)) {
			err->str = strdup("invalid branch sample type");
			err->idx = term->err_val;
		    parse_branch_str(term->val.str,
				    &attr->branch_sample_type)) {
			parse_events__handle_error(err, term->err_val,
					strdup("invalid branch sample type"),
					NULL);
			return -EINVAL;
		}
		break;
	case PARSE_EVENTS__TERM_TYPE_TIME:
		CHECK_TYPE_VAL(NUM);
		if (term->val.num > 1) {
			err->str = strdup("expected 0 or 1");
			err->idx = term->err_val;
			parse_events__handle_error(err, term->err_val,
						strdup("expected 0 or 1"),
						NULL);
			return -EINVAL;
		}
		break;
@@ -1080,8 +1100,9 @@ do { \
	case PARSE_EVENTS__TERM_TYPE_PERCORE:
		CHECK_TYPE_VAL(NUM);
		if ((unsigned int)term->val.num > 1) {
			err->str = strdup("expected 0 or 1");
			err->idx = term->err_val;
			parse_events__handle_error(err, term->err_val,
						strdup("expected 0 or 1"),
						NULL);
			return -EINVAL;
		}
		break;
@@ -1089,9 +1110,9 @@ do { \
		CHECK_TYPE_VAL(NUM);
		break;
	default:
		err->str = strdup("unknown term");
		err->idx = term->err_term;
		err->help = parse_events_formats_error_string(NULL);
		parse_events__handle_error(err, term->err_term,
				strdup("unknown term"),
				parse_events_formats_error_string(NULL));
		return -EINVAL;
	}

@@ -1142,9 +1163,9 @@ static int config_term_tracepoint(struct perf_event_attr *attr,
		return config_term_common(attr, term, err);
	default:
		if (err) {
			err->idx = term->err_term;
			err->str = strdup("unknown term");
			err->help = strdup("valid terms: call-graph,stack-size\n");
			parse_events__handle_error(err, term->err_term,
				strdup("unknown term"),
				strdup("valid terms: call-graph,stack-size\n"));
		}
		return -EINVAL;
	}
@@ -1323,10 +1344,12 @@ int parse_events_add_pmu(struct parse_events_state *parse_state,

	pmu = perf_pmu__find(name);
	if (!pmu) {
		if (asprintf(&err->str,
		char *err_str;

		if (asprintf(&err_str,
				"Cannot find PMU `%s'. Missing kernel support?",
				name) < 0)
			err->str = NULL;
				name) >= 0)
			parse_events__handle_error(err, -1, err_str, NULL);
		return -EINVAL;
	}

@@ -2802,13 +2825,10 @@ void parse_events__clear_array(struct parse_events_array *a)
void parse_events_evlist_error(struct parse_events_state *parse_state,
			       int idx, const char *str)
{
	struct parse_events_error *err = parse_state->error;

	if (!err)
	if (!parse_state->error)
		return;
	err->idx = idx;
	err->str = strdup(str);
	WARN_ONCE(!err->str, "WARNING: failed to allocate error string");

	parse_events__handle_error(parse_state->error, idx, strdup(str), NULL);
}

static void config_terms_list(char *buf, size_t buf_sz)
+2 −0
Original line number Diff line number Diff line
@@ -124,6 +124,8 @@ struct parse_events_state {
	struct list_head	  *terms;
};

void parse_events__handle_error(struct parse_events_error *err, int idx,
				char *str, char *help);
void parse_events__shrink_config_terms(void);
int parse_events__is_hardcoded_term(struct parse_events_term *term);
int parse_events_term__num(struct parse_events_term **term,
+18 −12
Original line number Diff line number Diff line
@@ -1050,9 +1050,9 @@ static int pmu_config_term(struct list_head *formats,
		if (err) {
			char *pmu_term = pmu_formats_string(formats);

			err->idx  = term->err_term;
			err->str  = strdup("unknown term");
			err->help = parse_events_formats_error_string(pmu_term);
			parse_events__handle_error(err, term->err_term,
				strdup("unknown term"),
				parse_events_formats_error_string(pmu_term));
			free(pmu_term);
		}
		return -EINVAL;
@@ -1080,8 +1080,9 @@ static int pmu_config_term(struct list_head *formats,
		if (term->no_value &&
		    bitmap_weight(format->bits, PERF_PMU_FORMAT_BITS) > 1) {
			if (err) {
				err->idx = term->err_val;
				err->str = strdup("no value assigned for term");
				parse_events__handle_error(err, term->err_val,
					   strdup("no value assigned for term"),
					   NULL);
			}
			return -EINVAL;
		}
@@ -1094,8 +1095,9 @@ static int pmu_config_term(struct list_head *formats,
						term->config, term->val.str);
			}
			if (err) {
				err->idx = term->err_val;
				err->str = strdup("expected numeric value");
				parse_events__handle_error(err, term->err_val,
					strdup("expected numeric value"),
					NULL);
			}
			return -EINVAL;
		}
@@ -1108,11 +1110,15 @@ static int pmu_config_term(struct list_head *formats,
	max_val = pmu_format_max_value(format->bits);
	if (val > max_val) {
		if (err) {
			err->idx = term->err_val;
			if (asprintf(&err->str,
			char *err_str;

			parse_events__handle_error(err, term->err_val,
				asprintf(&err_str,
				    "value too big for format, maximum is %llu",
				     (unsigned long long)max_val) < 0)
				err->str = strdup("value too big for format");
				    (unsigned long long)max_val) < 0
				    ? strdup("value too big for format")
				    : err_str,
				    NULL);
			return -EINVAL;
		}
		/*