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
This commit is contained in:
@ -2181,50 +2181,53 @@ int parse_event(struct evlist *evlist, const char *str)
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct parse_events_error_entry {
|
||||
/** @list: The list the error is part of. */
|
||||
struct list_head list;
|
||||
/** @idx: index in the parsed string */
|
||||
int idx;
|
||||
/** @str: string to display at the index */
|
||||
char *str;
|
||||
/** @help: optional help string */
|
||||
char *help;
|
||||
};
|
||||
|
||||
void parse_events_error__init(struct parse_events_error *err)
|
||||
{
|
||||
bzero(err, sizeof(*err));
|
||||
INIT_LIST_HEAD(&err->list);
|
||||
}
|
||||
|
||||
void parse_events_error__exit(struct parse_events_error *err)
|
||||
{
|
||||
zfree(&err->str);
|
||||
zfree(&err->help);
|
||||
zfree(&err->first_str);
|
||||
zfree(&err->first_help);
|
||||
struct parse_events_error_entry *pos, *tmp;
|
||||
|
||||
list_for_each_entry_safe(pos, tmp, &err->list, list) {
|
||||
zfree(&pos->str);
|
||||
zfree(&pos->help);
|
||||
list_del_init(&pos->list);
|
||||
free(pos);
|
||||
}
|
||||
}
|
||||
|
||||
void parse_events_error__handle(struct parse_events_error *err, int idx,
|
||||
char *str, char *help)
|
||||
{
|
||||
struct parse_events_error_entry *entry;
|
||||
|
||||
if (WARN(!str || !err, "WARNING: failed to provide error string or struct\n"))
|
||||
goto out_free;
|
||||
switch (err->num_errors) {
|
||||
case 0:
|
||||
err->idx = idx;
|
||||
err->str = str;
|
||||
err->help = help;
|
||||
break;
|
||||
case 1:
|
||||
err->first_idx = err->idx;
|
||||
err->idx = idx;
|
||||
err->first_str = err->str;
|
||||
err->str = str;
|
||||
err->first_help = err->help;
|
||||
err->help = help;
|
||||
break;
|
||||
default:
|
||||
pr_debug("Multiple errors dropping message: %s (%s)\n",
|
||||
err->str, err->help ?: "<no help>");
|
||||
free(err->str);
|
||||
err->str = str;
|
||||
free(err->help);
|
||||
err->help = help;
|
||||
break;
|
||||
}
|
||||
err->num_errors++;
|
||||
return;
|
||||
|
||||
entry = zalloc(sizeof(*entry));
|
||||
if (!entry) {
|
||||
pr_err("Failed to allocate memory for event parsing error: %s (%s)\n",
|
||||
str, help ?: "<no help>");
|
||||
goto out_free;
|
||||
}
|
||||
entry->idx = idx;
|
||||
entry->str = str;
|
||||
entry->help = help;
|
||||
list_add(&entry->list, &err->list);
|
||||
return;
|
||||
out_free:
|
||||
free(str);
|
||||
free(help);
|
||||
@ -2294,21 +2297,36 @@ static void __parse_events_error__print(int err_idx, const char *err_str,
|
||||
}
|
||||
}
|
||||
|
||||
void parse_events_error__print(struct parse_events_error *err,
|
||||
void parse_events_error__print(const struct parse_events_error *err,
|
||||
const char *event)
|
||||
{
|
||||
if (!err->num_errors)
|
||||
return;
|
||||
struct parse_events_error_entry *pos;
|
||||
bool first = true;
|
||||
|
||||
__parse_events_error__print(err->idx, err->str, err->help, event);
|
||||
|
||||
if (err->num_errors > 1) {
|
||||
fputs("\nInitial error:\n", stderr);
|
||||
__parse_events_error__print(err->first_idx, err->first_str,
|
||||
err->first_help, event);
|
||||
list_for_each_entry(pos, &err->list, list) {
|
||||
if (!first)
|
||||
fputs("\n", stderr);
|
||||
__parse_events_error__print(pos->idx, pos->str, pos->help, event);
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* In the list of errors err, do any of the error strings (str) contain the
|
||||
* given needle string?
|
||||
*/
|
||||
bool parse_events_error__contains(const struct parse_events_error *err,
|
||||
const char *needle)
|
||||
{
|
||||
struct parse_events_error_entry *pos;
|
||||
|
||||
list_for_each_entry(pos, &err->list, list) {
|
||||
if (strstr(pos->str, needle) != NULL)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#undef MAX_WIDTH
|
||||
|
||||
int parse_events_option(const struct option *opt, const char *str,
|
||||
|
Reference in New Issue
Block a user