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

perf tools: Add parse_events_error interface

Adding support to return error information from parse_events function.
Following struct will be populated by parse_events function on return:

struct parse_events_error {
int idx;
char *str;
char *help;
};

where 'idx' is the position in the string where the parsing failed,
'str' contains dynamically allocated error string describing the error
and 'help' is optional help string.

The change contains reporting function, which currently does not display
anything. The code changes to supply error data for specific event types
are coming in next patches. However this is what the expected output is:

$ sudo perf record -e 'sched:krava' ls
event syntax error: 'sched:krava'
\___ unknown tracepoint
...

$ perf record -e 'cpu/even=0x1/' ls
event syntax error: 'cpu/even=0x1/'
\___ unknown term

valid terms: pc,any,inv,edge,cmask,event,in_tx,ldlat,umask,in_tx_cp,offcore_rsp,config,config1,config2,name,period,branch_type
...

$ perf record -e cycles,cache-mises ls
event syntax error: '..es,cache-mises'
\___ parser error
...

The output functions cut the beginning of the event string so the error
starts up to 10th character and cut the end of the string of it crosses
the terminal width.

Signed-off-by: Jiri Olsa <jolsa@kernel.org>
Cc: David Ahern <dsahern@gmail.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/1429729824-13932-2-git-send-email-jolsa@kernel.org
[ Renamed 'error' variables to 'err', not to clash with util.h error() ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Jiri Olsa and committed by
Arnaldo Carvalho de Melo
b39b8393 70d73de4

+127 -32
+1 -1
tools/perf/builtin-stat.c
··· 1541 1541 unsigned i; 1542 1542 1543 1543 for (i = 0; i < len; i++) { 1544 - if (parse_events(evsel_list, attrs[i])) 1544 + if (parse_events(evsel_list, attrs[i], NULL)) 1545 1545 return -1; 1546 1546 } 1547 1547 return 0;
+1 -1
tools/perf/tests/code-reading.c
··· 482 482 else 483 483 str = "cycles"; 484 484 pr_debug("Parsing event '%s'\n", str); 485 - ret = parse_events(evlist, str); 485 + ret = parse_events(evlist, str, NULL); 486 486 if (ret < 0) { 487 487 pr_debug("parse_events failed\n"); 488 488 goto out_err;
+2 -2
tools/perf/tests/evsel-roundtrip-name.c
··· 23 23 for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) { 24 24 __perf_evsel__hw_cache_type_op_res_name(type, op, i, 25 25 name, sizeof(name)); 26 - err = parse_events(evlist, name); 26 + err = parse_events(evlist, name, NULL); 27 27 if (err) 28 28 ret = err; 29 29 } ··· 71 71 return -ENOMEM; 72 72 73 73 for (i = 0; i < nr_names; ++i) { 74 - err = parse_events(evlist, names[i]); 74 + err = parse_events(evlist, names[i], NULL); 75 75 if (err) { 76 76 pr_debug("failed to parse event '%s', err %d\n", 77 77 names[i], err);
+1 -1
tools/perf/tests/hists_cumulate.c
··· 695 695 696 696 TEST_ASSERT_VAL("No memory", evlist); 697 697 698 - err = parse_events(evlist, "cpu-clock"); 698 + err = parse_events(evlist, "cpu-clock", NULL); 699 699 if (err) 700 700 goto out; 701 701
+2 -2
tools/perf/tests/hists_filter.c
··· 108 108 109 109 TEST_ASSERT_VAL("No memory", evlist); 110 110 111 - err = parse_events(evlist, "cpu-clock"); 111 + err = parse_events(evlist, "cpu-clock", NULL); 112 112 if (err) 113 113 goto out; 114 - err = parse_events(evlist, "task-clock"); 114 + err = parse_events(evlist, "task-clock", NULL); 115 115 if (err) 116 116 goto out; 117 117
+2 -2
tools/perf/tests/hists_link.c
··· 282 282 if (evlist == NULL) 283 283 return -ENOMEM; 284 284 285 - err = parse_events(evlist, "cpu-clock"); 285 + err = parse_events(evlist, "cpu-clock", NULL); 286 286 if (err) 287 287 goto out; 288 - err = parse_events(evlist, "task-clock"); 288 + err = parse_events(evlist, "task-clock", NULL); 289 289 if (err) 290 290 goto out; 291 291
+1 -1
tools/perf/tests/hists_output.c
··· 590 590 591 591 TEST_ASSERT_VAL("No memory", evlist); 592 592 593 - err = parse_events(evlist, "cpu-clock"); 593 + err = parse_events(evlist, "cpu-clock", NULL); 594 594 if (err) 595 595 goto out; 596 596
+2 -2
tools/perf/tests/keep-tracking.c
··· 78 78 79 79 perf_evlist__set_maps(evlist, cpus, threads); 80 80 81 - CHECK__(parse_events(evlist, "dummy:u")); 82 - CHECK__(parse_events(evlist, "cycles:u")); 81 + CHECK__(parse_events(evlist, "dummy:u", NULL)); 82 + CHECK__(parse_events(evlist, "cycles:u", NULL)); 83 83 84 84 perf_evlist__config(evlist, &opts); 85 85
+1 -1
tools/perf/tests/parse-events.c
··· 1571 1571 if (evlist == NULL) 1572 1572 return -ENOMEM; 1573 1573 1574 - ret = parse_events(evlist, e->name); 1574 + ret = parse_events(evlist, e->name, NULL); 1575 1575 if (ret) { 1576 1576 pr_debug("failed to parse event '%s', err %d\n", 1577 1577 e->name, ret);
+1 -1
tools/perf/tests/perf-time-to-tsc.c
··· 68 68 69 69 perf_evlist__set_maps(evlist, cpus, threads); 70 70 71 - CHECK__(parse_events(evlist, "cycles:u")); 71 + CHECK__(parse_events(evlist, "cycles:u", NULL)); 72 72 73 73 perf_evlist__config(evlist, &opts); 74 74
+4 -4
tools/perf/tests/switch-tracking.c
··· 347 347 perf_evlist__set_maps(evlist, cpus, threads); 348 348 349 349 /* First event */ 350 - err = parse_events(evlist, "cpu-clock:u"); 350 + err = parse_events(evlist, "cpu-clock:u", NULL); 351 351 if (err) { 352 352 pr_debug("Failed to parse event dummy:u\n"); 353 353 goto out_err; ··· 356 356 cpu_clocks_evsel = perf_evlist__last(evlist); 357 357 358 358 /* Second event */ 359 - err = parse_events(evlist, "cycles:u"); 359 + err = parse_events(evlist, "cycles:u", NULL); 360 360 if (err) { 361 361 pr_debug("Failed to parse event cycles:u\n"); 362 362 goto out_err; ··· 371 371 goto out; 372 372 } 373 373 374 - err = parse_events(evlist, sched_switch); 374 + err = parse_events(evlist, sched_switch, NULL); 375 375 if (err) { 376 376 pr_debug("Failed to parse event %s\n", sched_switch); 377 377 goto out_err; ··· 401 401 perf_evsel__set_sample_bit(cycles_evsel, TIME); 402 402 403 403 /* Fourth event */ 404 - err = parse_events(evlist, "dummy:u"); 404 + err = parse_events(evlist, "dummy:u", NULL); 405 405 if (err) { 406 406 pr_debug("Failed to parse event dummy:u\n"); 407 407 goto out_err;
+92 -8
tools/perf/util/parse-events.c
··· 17 17 #include "parse-events-flex.h" 18 18 #include "pmu.h" 19 19 #include "thread_map.h" 20 + #include "asm/bug.h" 20 21 21 22 #define MAX_NAME_LEN 100 22 23 ··· 1020 1019 return ret; 1021 1020 } 1022 1021 1023 - int parse_events(struct perf_evlist *evlist, const char *str) 1022 + int parse_events(struct perf_evlist *evlist, const char *str, 1023 + struct parse_events_error *err) 1024 1024 { 1025 1025 struct parse_events_evlist data = { 1026 - .list = LIST_HEAD_INIT(data.list), 1027 - .idx = evlist->nr_entries, 1026 + .list = LIST_HEAD_INIT(data.list), 1027 + .idx = evlist->nr_entries, 1028 + .error = err, 1028 1029 }; 1029 1030 int ret; 1030 1031 ··· 1047 1044 return ret; 1048 1045 } 1049 1046 1047 + #define MAX_WIDTH 1000 1048 + static int get_term_width(void) 1049 + { 1050 + struct winsize ws; 1051 + 1052 + get_term_dimensions(&ws); 1053 + return ws.ws_col > MAX_WIDTH ? MAX_WIDTH : ws.ws_col; 1054 + } 1055 + 1056 + static void parse_events_print_error(struct parse_events_error *err, 1057 + const char *event) 1058 + { 1059 + const char *str = "invalid or unsupported event: "; 1060 + char _buf[MAX_WIDTH]; 1061 + char *buf = (char *) event; 1062 + int idx = 0; 1063 + 1064 + if (err->str) { 1065 + /* -2 for extra '' in the final fprintf */ 1066 + int width = get_term_width() - 2; 1067 + int len_event = strlen(event); 1068 + int len_str, max_len, cut = 0; 1069 + 1070 + /* 1071 + * Maximum error index indent, we will cut 1072 + * the event string if it's bigger. 1073 + */ 1074 + int max_err_idx = 10; 1075 + 1076 + /* 1077 + * Let's be specific with the message when 1078 + * we have the precise error. 1079 + */ 1080 + str = "event syntax error: "; 1081 + len_str = strlen(str); 1082 + max_len = width - len_str; 1083 + 1084 + buf = _buf; 1085 + 1086 + /* We're cutting from the beggining. */ 1087 + if (err->idx > max_err_idx) 1088 + cut = err->idx - max_err_idx; 1089 + 1090 + strncpy(buf, event + cut, max_len); 1091 + 1092 + /* Mark cut parts with '..' on both sides. */ 1093 + if (cut) 1094 + buf[0] = buf[1] = '.'; 1095 + 1096 + if ((len_event - cut) > max_len) { 1097 + buf[max_len - 1] = buf[max_len - 2] = '.'; 1098 + buf[max_len] = 0; 1099 + } 1100 + 1101 + idx = len_str + err->idx - cut; 1102 + } 1103 + 1104 + fprintf(stderr, "%s'%s'\n", str, buf); 1105 + if (idx) { 1106 + fprintf(stderr, "%*s\\___ %s\n", idx + 1, "", err->str); 1107 + if (err->help) 1108 + fprintf(stderr, "\n%s\n", err->help); 1109 + free(err->str); 1110 + free(err->help); 1111 + } 1112 + 1113 + fprintf(stderr, "Run 'perf list' for a list of valid events\n"); 1114 + } 1115 + 1116 + #undef MAX_WIDTH 1117 + 1050 1118 int parse_events_option(const struct option *opt, const char *str, 1051 1119 int unset __maybe_unused) 1052 1120 { 1053 1121 struct perf_evlist *evlist = *(struct perf_evlist **)opt->value; 1054 - int ret = parse_events(evlist, str); 1122 + struct parse_events_error err = { .idx = 0, }; 1123 + int ret = parse_events(evlist, str, &err); 1055 1124 1056 - if (ret) { 1057 - fprintf(stderr, "invalid or unsupported event: '%s'\n", str); 1058 - fprintf(stderr, "Run 'perf list' for a list of valid events\n"); 1059 - } 1125 + if (ret) 1126 + parse_events_print_error(&err, str); 1127 + 1060 1128 return ret; 1061 1129 } 1062 1130 ··· 1608 1534 1609 1535 list_for_each_entry_safe(term, h, terms, list) 1610 1536 free(term); 1537 + } 1538 + 1539 + void parse_events_evlist_error(struct parse_events_evlist *data, 1540 + int idx, const char *str) 1541 + { 1542 + struct parse_events_error *err = data->error; 1543 + 1544 + err->idx = idx; 1545 + err->str = strdup(str); 1546 + WARN_ONCE(!err->str, "WARNING: failed to allocate error string"); 1611 1547 }
+15 -4
tools/perf/util/parse-events.h
··· 12 12 struct list_head; 13 13 struct perf_evsel; 14 14 struct perf_evlist; 15 + struct parse_events_error; 15 16 16 17 struct option; 17 18 ··· 30 29 31 30 extern int parse_events_option(const struct option *opt, const char *str, 32 31 int unset); 33 - extern int parse_events(struct perf_evlist *evlist, const char *str); 32 + extern int parse_events(struct perf_evlist *evlist, const char *str, 33 + struct parse_events_error *error); 34 34 extern int parse_events_terms(struct list_head *terms, const char *str); 35 35 extern int parse_filter(const struct option *opt, const char *str, int unset); 36 36 ··· 76 74 bool used; 77 75 }; 78 76 77 + struct parse_events_error { 78 + int idx; /* index in the parsed string */ 79 + char *str; /* string to display at the index */ 80 + char *help; /* optional help string */ 81 + }; 82 + 79 83 struct parse_events_evlist { 80 - struct list_head list; 81 - int idx; 82 - int nr_groups; 84 + struct list_head list; 85 + int idx; 86 + int nr_groups; 87 + struct parse_events_error *error; 83 88 }; 84 89 85 90 struct parse_events_terms { ··· 123 114 void parse_events_update_lists(struct list_head *list_event, 124 115 struct list_head *list_all); 125 116 void parse_events_error(void *data, void *scanner, char const *msg); 117 + void parse_events_evlist_error(struct parse_events_evlist *data, 118 + int idx, const char *str); 126 119 127 120 void print_events(const char *event_glob, bool name_only); 128 121
+2 -2
tools/perf/util/record.c
··· 20 20 if (!evlist) 21 21 return -ENOMEM; 22 22 23 - if (parse_events(evlist, str)) 23 + if (parse_events(evlist, str, NULL)) 24 24 goto out_delete; 25 25 26 26 evsel = perf_evlist__first(evlist); ··· 216 216 if (!temp_evlist) 217 217 return false; 218 218 219 - err = parse_events(temp_evlist, str); 219 + err = parse_events(temp_evlist, str, NULL); 220 220 if (err) 221 221 goto out_delete; 222 222