Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

perf parse-events: Print all errors

Prior to this patch the first and the last error encountered during
parsing are printed. To see other errors verbose needs
enabling. Unfortunately this can drop useful errors, in particular on
terms. This patch changes the errors so that instead of the first and
last all errors are recorded and printed, the underlying data
structure is changed to a list.

Before:
```
$ perf stat -e 'slots/edge=2/' true
event syntax error: 'slots/edge=2/'
\___ Bad event or PMU

Unable to find PMU or event on a PMU of 'slots'

Initial error:
event syntax error: 'slots/edge=2/'
\___ Cannot find PMU `slots'. Missing kernel support?
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
```

After:
```
$ perf stat -e 'slots/edge=2/' true
event syntax error: 'slots/edge=2/'
\___ Bad event or PMU

Unable to find PMU or event on a PMU of 'slots'

event syntax error: 'slots/edge=2/'
\___ value too big for format (edge), maximum is 1

event syntax error: 'slots/edge=2/'
\___ Cannot find PMU `slots'. Missing kernel support?
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: Ian Rogers <irogers@google.com>
Reviewed-by: James Clark <james.clark@arm.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: tchen168@asu.edu
Cc: Kan Liang <kan.liang@linux.intel.com>
Cc: Michael Petlan <mpetlan@redhat.com>
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Link: https://lore.kernel.org/r/20240131134940.593788-3-irogers@google.com

authored by

Ian Rogers and committed by
Namhyung Kim
fd7b8e8f f5144eca

+69 -60
+2 -3
tools/perf/arch/x86/tests/hybrid.c
··· 259 259 parse_events_error__init(&err); 260 260 ret = parse_events(evlist, e->name, &err); 261 261 if (ret) { 262 - pr_debug("failed to parse event '%s', err %d, str '%s'\n", 263 - e->name, ret, err.str); 262 + pr_debug("failed to parse event '%s', err %d\n", e->name, ret); 264 263 parse_events_error__print(&err, e->name); 265 264 ret = TEST_FAIL; 266 - if (strstr(err.str, "can't access trace events")) 265 + if (parse_events_error__contains(&err, "can't access trace events")) 267 266 ret = TEST_SKIP; 268 267 } else { 269 268 ret = e->check(evlist);
+1 -2
tools/perf/tests/expand-cgroup.c
··· 127 127 parse_events_error__init(&err); 128 128 ret = parse_events(evlist, event_str, &err); 129 129 if (ret < 0) { 130 - pr_debug("failed to parse event '%s', err %d, str '%s'\n", 131 - event_str, ret, err.str); 130 + pr_debug("failed to parse event '%s', err %d\n", event_str, ret); 132 131 parse_events_error__print(&err, event_str); 133 132 goto out; 134 133 }
+4 -5
tools/perf/tests/parse-events.c
··· 2506 2506 parse_events_error__init(&err); 2507 2507 ret = parse_events(evlist, e->name, &err); 2508 2508 if (ret) { 2509 - pr_debug("failed to parse event '%s', err %d, str '%s'\n", 2510 - e->name, ret, err.str); 2509 + pr_debug("failed to parse event '%s', err %d\n", e->name, ret); 2511 2510 parse_events_error__print(&err, e->name); 2512 2511 ret = TEST_FAIL; 2513 - if (err.str && strstr(err.str, "can't access trace events")) 2512 + if (parse_events_error__contains(&err, "can't access trace events")) 2514 2513 ret = TEST_SKIP; 2515 2514 } else { 2516 2515 ret = e->check(evlist); ··· 2534 2535 ret = __parse_events(evlist, str, /*pmu_filter=*/NULL, &err, 2535 2536 &perf_pmu__fake, /*warn_if_reordered=*/true); 2536 2537 if (ret) { 2537 - pr_debug("failed to parse event '%s', err %d, str '%s'\n", 2538 - str, ret, err.str); 2538 + pr_debug("failed to parse event '%s', err %d\n", 2539 + str, ret); 2539 2540 parse_events_error__print(&err, str); 2540 2541 } 2541 2542
+57 -39
tools/perf/util/parse-events.c
··· 2181 2181 return ret; 2182 2182 } 2183 2183 2184 + struct parse_events_error_entry { 2185 + /** @list: The list the error is part of. */ 2186 + struct list_head list; 2187 + /** @idx: index in the parsed string */ 2188 + int idx; 2189 + /** @str: string to display at the index */ 2190 + char *str; 2191 + /** @help: optional help string */ 2192 + char *help; 2193 + }; 2194 + 2184 2195 void parse_events_error__init(struct parse_events_error *err) 2185 2196 { 2186 - bzero(err, sizeof(*err)); 2197 + INIT_LIST_HEAD(&err->list); 2187 2198 } 2188 2199 2189 2200 void parse_events_error__exit(struct parse_events_error *err) 2190 2201 { 2191 - zfree(&err->str); 2192 - zfree(&err->help); 2193 - zfree(&err->first_str); 2194 - zfree(&err->first_help); 2202 + struct parse_events_error_entry *pos, *tmp; 2203 + 2204 + list_for_each_entry_safe(pos, tmp, &err->list, list) { 2205 + zfree(&pos->str); 2206 + zfree(&pos->help); 2207 + list_del_init(&pos->list); 2208 + free(pos); 2209 + } 2195 2210 } 2196 2211 2197 2212 void parse_events_error__handle(struct parse_events_error *err, int idx, 2198 2213 char *str, char *help) 2199 2214 { 2215 + struct parse_events_error_entry *entry; 2216 + 2200 2217 if (WARN(!str || !err, "WARNING: failed to provide error string or struct\n")) 2201 2218 goto out_free; 2202 - switch (err->num_errors) { 2203 - case 0: 2204 - err->idx = idx; 2205 - err->str = str; 2206 - err->help = help; 2207 - break; 2208 - case 1: 2209 - err->first_idx = err->idx; 2210 - err->idx = idx; 2211 - err->first_str = err->str; 2212 - err->str = str; 2213 - err->first_help = err->help; 2214 - err->help = help; 2215 - break; 2216 - default: 2217 - pr_debug("Multiple errors dropping message: %s (%s)\n", 2218 - err->str, err->help ?: "<no help>"); 2219 - free(err->str); 2220 - err->str = str; 2221 - free(err->help); 2222 - err->help = help; 2223 - break; 2224 - } 2225 - err->num_errors++; 2226 - return; 2227 2219 2220 + entry = zalloc(sizeof(*entry)); 2221 + if (!entry) { 2222 + pr_err("Failed to allocate memory for event parsing error: %s (%s)\n", 2223 + str, help ?: "<no help>"); 2224 + goto out_free; 2225 + } 2226 + entry->idx = idx; 2227 + entry->str = str; 2228 + entry->help = help; 2229 + list_add(&entry->list, &err->list); 2230 + return; 2228 2231 out_free: 2229 2232 free(str); 2230 2233 free(help); ··· 2297 2294 } 2298 2295 } 2299 2296 2300 - void parse_events_error__print(struct parse_events_error *err, 2297 + void parse_events_error__print(const struct parse_events_error *err, 2301 2298 const char *event) 2302 2299 { 2303 - if (!err->num_errors) 2304 - return; 2300 + struct parse_events_error_entry *pos; 2301 + bool first = true; 2305 2302 2306 - __parse_events_error__print(err->idx, err->str, err->help, event); 2307 - 2308 - if (err->num_errors > 1) { 2309 - fputs("\nInitial error:\n", stderr); 2310 - __parse_events_error__print(err->first_idx, err->first_str, 2311 - err->first_help, event); 2303 + list_for_each_entry(pos, &err->list, list) { 2304 + if (!first) 2305 + fputs("\n", stderr); 2306 + __parse_events_error__print(pos->idx, pos->str, pos->help, event); 2307 + first = false; 2312 2308 } 2309 + } 2310 + 2311 + /* 2312 + * In the list of errors err, do any of the error strings (str) contain the 2313 + * given needle string? 2314 + */ 2315 + bool parse_events_error__contains(const struct parse_events_error *err, 2316 + const char *needle) 2317 + { 2318 + struct parse_events_error_entry *pos; 2319 + 2320 + list_for_each_entry(pos, &err->list, list) { 2321 + if (strstr(pos->str, needle) != NULL) 2322 + return true; 2323 + } 2324 + return false; 2313 2325 } 2314 2326 2315 2327 #undef MAX_WIDTH
+5 -9
tools/perf/util/parse-events.h
··· 130 130 }; 131 131 132 132 struct parse_events_error { 133 - int num_errors; /* number of errors encountered */ 134 - int idx; /* index in the parsed string */ 135 - char *str; /* string to display at the index */ 136 - char *help; /* optional help string */ 137 - int first_idx;/* as above, but for the first encountered error */ 138 - char *first_str; 139 - char *first_help; 133 + /** @list: The head of a list of errors. */ 134 + struct list_head list; 140 135 }; 141 136 142 137 /* A wrapper around a list of terms for the sake of better type safety. */ ··· 242 247 void parse_events_error__exit(struct parse_events_error *err); 243 248 void parse_events_error__handle(struct parse_events_error *err, int idx, 244 249 char *str, char *help); 245 - void parse_events_error__print(struct parse_events_error *err, 250 + void parse_events_error__print(const struct parse_events_error *err, 246 251 const char *event); 247 - 252 + bool parse_events_error__contains(const struct parse_events_error *err, 253 + const char *needle); 248 254 #ifdef HAVE_LIBELF_SUPPORT 249 255 /* 250 256 * If the probe point starts with '%',
-2
tools/perf/util/parse-events.y
··· 536 536 list = alloc_list(); 537 537 if (!list) 538 538 YYNOMEM; 539 - if (error) 540 - error->idx = @1.first_column; 541 539 542 540 err = parse_events_add_tracepoint(list, &parse_state->idx, $1.sys, $1.event, 543 541 error, $2, &@1);