| 1 |
|
| 2 | %option reentrant
|
| 3 | %option bison-bridge
|
| 4 | %option prefix="parse_events_"
|
| 5 | %option stack
|
| 6 | %option bison-locations
|
| 7 | %option yylineno
|
| 8 | %option noyywrap
|
| 9 |
|
| 10 | %{
|
| 11 | #include <errno.h>
|
| 12 | #include <stdlib.h>
|
| 13 | #include <stdio.h>
|
| 14 | #include "parse-events.h"
|
| 15 | #include "parse-events-bison.h"
|
| 16 |
|
| 17 | char *parse_events_get_text(yyscan_t yyscanner) |
| 18 | YYSTYPE *parse_events_get_lval(yyscan_t yyscanner) |
| 19 | int parse_events_get_column(yyscan_t yyscanner) |
| 20 | int parse_events_get_leng(yyscan_t yyscanner) |
| 21 |
|
| 22 | static int get_column(yyscan_t scanner)
|
| 23 | {
|
| 24 | return parse_events_get_column(scanner) - parse_events_get_leng(scanner) |
| 25 | }
|
| 26 |
|
| 27 | static int value(struct parse_events_state *parse_state, yyscan_t scanner, int base)
|
| 28 | {
|
| 29 | YYSTYPE *yylval = parse_events_get_lval(scanner) |
| 30 | char *text = parse_events_get_text(scanner) |
| 31 | u64 num |
| 32 |
|
| 33 | errno = 0 |
| 34 | num = strtoull(text, NULL, base) |
| 35 | if (errno) {
|
| 36 | struct parse_events_error *error = parse_state->error |
| 37 | char *help = NULL |
| 38 |
|
| 39 | if (asprintf(&help, "Bad base %d number \"%s\"", base, text) > 0)
|
| 40 | parse_events_error__handle(error, get_column(scanner), help , NULL) |
| 41 |
|
| 42 | return PE_ERROR |
| 43 | }
|
| 44 |
|
| 45 | yylval->num = num |
| 46 | return PE_VALUE |
| 47 | }
|
| 48 |
|
| 49 | static int str(yyscan_t scanner, int token)
|
| 50 | {
|
| 51 | YYSTYPE *yylval = parse_events_get_lval(scanner) |
| 52 | char *text = parse_events_get_text(scanner) |
| 53 |
|
| 54 | yylval->str = strdup(text) |
| 55 | return token |
| 56 | }
|
| 57 |
|
| 58 | static int quoted_str(yyscan_t scanner, int token)
|
| 59 | {
|
| 60 | YYSTYPE *yylval = parse_events_get_lval(scanner) |
| 61 | char *text = parse_events_get_text(scanner) |
| 62 |
|
| 63 | /*
|
| 64 | * If a text tag specified on the command line
|
| 65 | * contains opening single quite ' then it is
|
| 66 | * expected that the tag ends with single quote
|
| 67 | * as well, like this:
|
| 68 | * name=\'CPU_CLK_UNHALTED.THREAD:cmask=1\'
|
| 69 | * quotes need to be escaped to bypass shell
|
| 70 | * processing.
|
| 71 | */
|
| 72 | yylval->str = strndup(&text[1], strlen(text) - 2) |
| 73 | return token |
| 74 | }
|
| 75 |
|
| 76 | /*
|
| 77 | * This function is called when the parser gets two kind of input:
|
| 78 | *
|
| 79 | * @cfg1 or @cfg2=config
|
| 80 | *
|
| 81 | * The leading '@' is stripped off before 'cfg1' and 'cfg2=config' are given to
|
| 82 | * bison. In the latter case it is necessary to keep the string intact so that
|
| 83 | * the PMU kernel driver can determine what configurable is associated to
|
| 84 | * 'config'.
|
| 85 | */
|
| 86 | static int drv_str(yyscan_t scanner, int token)
|
| 87 | {
|
| 88 | YYSTYPE *yylval = parse_events_get_lval(scanner) |
| 89 | char *text = parse_events_get_text(scanner) |
| 90 |
|
| 91 | /* Strip off the '@' */
|
| 92 | yylval->str = strdup(text + 1) |
| 93 | return token |
| 94 | }
|
| 95 |
|
| 96 | /*
|
| 97 | * Use yyless to return all the characaters to the input. Update the column for
|
| 98 | * location debugging. If __alloc is non-zero set yylval to the text for the
|
| 99 | * returned token's value.
|
| 100 | */
|
| 101 | #define REWIND(__alloc) \
|
| 102 | do { \
|
| 103 | YYSTYPE *__yylval = parse_events_get_lval(yyscanner) |
| 104 | char *text = parse_events_get_text(yyscanner) |
| 105 | \
|
| 106 | if (__alloc) \
|
| 107 | __yylval->str = strdup(text) |
| 108 | \
|
| 109 | yycolumn -= strlen(text) |
| 110 | yyless(0) |
| 111 | } while (0)
|
| 112 |
|
| 113 | static int term(yyscan_t scanner, enum parse_events__term_type type)
|
| 114 | {
|
| 115 | YYSTYPE *yylval = parse_events_get_lval(scanner) |
| 116 |
|
| 117 | yylval->term_type = type |
| 118 | return PE_TERM |
| 119 | }
|
| 120 |
|
| 121 | static void modifiers_error(struct parse_events_state *parse_state, yyscan_t scanner,
|
| 122 | int pos, char mod_char, const char *mod_name)
|
| 123 | {
|
| 124 | struct parse_events_error *error = parse_state->error |
| 125 | char *help = NULL |
| 126 |
|
| 127 | if (asprintf(&help, "Duplicate modifier '%c' (%s)", mod_char, mod_name) > 0)
|
| 128 | parse_events_error__handle(error, get_column(scanner) + pos, help , NULL) |
| 129 | }
|
| 130 |
|
| 131 | static int modifiers(struct parse_events_state *parse_state, yyscan_t scanner)
|
| 132 | {
|
| 133 | YYSTYPE *yylval = parse_events_get_lval(scanner) |
| 134 | char *text = parse_events_get_text(scanner) |
| 135 | struct parse_events_modifier mod = { .precise = 0, } |
| 136 |
|
| 137 | for (size_t i = 0, n = strlen(text) |
| 138 | #define CASE(c, field) \
|
| 139 | case c: \
|
| 140 | if (mod.field) { \
|
| 141 | modifiers_error(parse_state, scanner, i, c, #field) |
| 142 | return PE_ERROR |
| 143 | } \
|
| 144 | mod.field = true |
| 145 | break
|
| 146 |
|
| 147 | switch (text[i]) {
|
| 148 | CASE('u', user) |
| 149 | CASE('k', kernel) |
| 150 | CASE('h', hypervisor) |
| 151 | CASE('I', non_idle) |
| 152 | CASE('G', guest) |
| 153 | CASE('H', host) |
| 154 | case 'p':
|
| 155 | mod.precise++ |
| 156 | /*
|
| 157 | * precise ip:
|
| 158 | *
|
| 159 | * 0 - SAMPLE_IP can have arbitrary skid
|
| 160 | * 1 - SAMPLE_IP must have constant skid
|
| 161 | * 2 - SAMPLE_IP requested to have 0 skid
|
| 162 | * 3 - SAMPLE_IP must have 0 skid
|
| 163 | *
|
| 164 | * See also PERF_RECORD_MISC_EXACT_IP
|
| 165 | */
|
| 166 | if (mod.precise > 3) {
|
| 167 | struct parse_events_error *error = parse_state->error |
| 168 | char *help = strdup("Maximum precise value is 3") |
| 169 |
|
| 170 | if (help) {
|
| 171 | parse_events_error__handle(error, get_column(scanner) + i,
|
| 172 | help , NULL) |
| 173 | }
|
| 174 | return PE_ERROR |
| 175 | }
|
| 176 | break |
| 177 | CASE('P', precise_max) |
| 178 | CASE('S', sample_read) |
| 179 | CASE('D', pinned) |
| 180 | CASE('W', weak) |
| 181 | CASE('e', exclusive) |
| 182 | CASE('b', bpf) |
| 183 | CASE('R', retire_lat) |
| 184 | CASE('X', dont_regroup) |
| 185 | default:
|
| 186 | return PE_ERROR |
| 187 | }
|
| 188 | #undef CASE
|
| 189 | }
|
| 190 | yylval->mod = mod |
| 191 | return PE_MODIFIER_EVENT |
| 192 | }
|
| 193 |
|
| 194 | #define YY_USER_ACTION \
|
| 195 | do { \
|
| 196 | yylloc->last_column = yylloc->first_column |
| 197 | yylloc->first_column = yycolumn |
| 198 | yycolumn += yyleng |
| 199 | } while (0) |
| 200 |
|
| 201 | %}
|
| 202 |
|
| 203 | %x mem
|
| 204 | %s config
|
| 205 | %x event
|
| 206 |
|
| 207 | group [^,{}/]*[{][^}]*[}][^,{}/]*
|
| 208 | event_pmu [^,{}/]+[/][^/]*[/][^,{}/]*
|
| 209 | event [^,{}/]+
|
| 210 |
|
| 211 | num_dec [0-9]+
|
| 212 | num_hex 0x[a-fA-F0-9]{1,16}
|
| 213 | num_raw_hex [a-fA-F0-9]{1,16}
|
| 214 | /* Regular pattern to match the token PE_NAME. */
|
| 215 | name_start [a-zA-Z0-9_*?\[\]]
|
| 216 | name {name_start}[a-zA-Z0-9_*?.\[\]!\-]*
|
| 217 | /* PE_NAME token when inside a config term list, allows ':'. */
|
| 218 | term_name {name_start}[a-zA-Z0-9_*?.\[\]!\-:]*
|
| 219 | /*
|
| 220 | * PE_NAME token when quoted, allows ':,.='.
|
| 221 | * Matches the RHS of terms like: name='COMPLEX_CYCLES_NAME:orig=cycles,desc=chip-clock-ticks'.
|
| 222 | */
|
| 223 | quoted_name [\']{name_start}[a-zA-Z0-9_*?.\[\]!\-:,\.=]*[\']
|
| 224 | drv_cfg_term [a-zA-Z0-9_\.]+(=[a-zA-Z0-9_*?\.:]+)?
|
| 225 | /*
|
| 226 | * If you add a modifier you need to update modifiers().
|
| 227 | * Also, the letters in modifier_event must not be in modifier_bp.
|
| 228 | */
|
| 229 | modifier_event [ukhpPGHSDIWebRX]{1,17}
|
| 230 | modifier_bp [rwx]{1,3}
|
| 231 | digit [0-9]
|
| 232 | non_digit [^0-9]
|
| 233 |
|
| 234 | %%
|
| 235 |
|
| 236 | %{
|
| 237 | struct parse_events_state *_parse_state = parse_events_get_extra(yyscanner) |
| 238 | {
|
| 239 | int start_token = _parse_state->stoken |
| 240 |
|
| 241 | if (start_token == PE_START_TERMS)
|
| 242 | BEGIN(config) |
| 243 | else if (start_token == PE_START_EVENTS)
|
| 244 | BEGIN(event) |
| 245 |
|
| 246 | if (start_token) {
|
| 247 | _parse_state->stoken = 0 |
| 248 | /*
|
| 249 | * The flex parser does not init locations variable
|
| 250 | * via the scan_string interface, so we need do the
|
| 251 | * init in here.
|
| 252 | */
|
| 253 | yycolumn = 0 |
| 254 | return start_token |
| 255 | }
|
| 256 | }
|
| 257 | %}
|
| 258 |
|
| 259 | <event>{
|
| 260 |
|
| 261 | {group} {
|
| 262 | BEGIN(INITIAL) |
| 263 | REWIND(0) |
| 264 | }
|
| 265 |
|
| 266 | {event_pmu} |
|
| 267 | {event} {
|
| 268 | BEGIN(INITIAL) |
| 269 | REWIND(1) |
| 270 | return PE_EVENT_NAME |
| 271 | }
|
| 272 |
|
| 273 | <<EOF>> {
|
| 274 | BEGIN(INITIAL) |
| 275 | REWIND(0) |
| 276 | }
|
| 277 | , {
|
| 278 | return ',' |
| 279 | }
|
| 280 | }
|
| 281 |
|
| 282 | <config>{
|
| 283 | /*
|
| 284 | * Please update config_term_names when new static term is added.
|
| 285 | */
|
| 286 | config { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG) |
| 287 | config1 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG1) |
| 288 | config2 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG2) |
| 289 | config3 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG3) |
| 290 | config4 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG4) |
| 291 | name { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_NAME) |
| 292 | period { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD) |
| 293 | freq { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_SAMPLE_FREQ) |
| 294 | branch_type { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE) |
| 295 | time { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_TIME) |
| 296 | call-graph { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CALLGRAPH) |
| 297 | stack-size { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_STACKSIZE) |
| 298 | max-stack { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_MAX_STACK) |
| 299 | nr { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_MAX_EVENTS) |
| 300 | inherit { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_INHERIT) |
| 301 | no-inherit { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_NOINHERIT) |
| 302 | overwrite { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_OVERWRITE) |
| 303 | no-overwrite { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_NOOVERWRITE) |
| 304 | percore { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_PERCORE) |
| 305 | aux-output { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT) |
| 306 | aux-action { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_AUX_ACTION) |
| 307 | aux-sample-size { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_AUX_SAMPLE_SIZE) |
| 308 | metric-id { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_METRIC_ID) |
| 309 | cpu { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CPU) |
| 310 | ratio-to-prev { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_RATIO_TO_PREV) |
| 311 | legacy-hardware-config { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_LEGACY_HARDWARE_CONFIG) |
| 312 | legacy-cache-config { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_LEGACY_CACHE_CONFIG) |
| 313 | r{num_raw_hex} { return str(yyscanner, PE_RAW) |
| 314 | r0x{num_raw_hex} { return str(yyscanner, PE_RAW) |
| 315 | , { return ',' |
| 316 | "/" { BEGIN(INITIAL) |
| 317 | {num_dec} { return value(_parse_state, yyscanner, 10) |
| 318 | {num_hex} { return value(_parse_state, yyscanner, 16) |
| 319 | {term_name} { return str(yyscanner, PE_NAME) |
| 320 | @{drv_cfg_term} { return drv_str(yyscanner, PE_DRV_CFG_TERM) |
| 321 | }
|
| 322 |
|
| 323 | <mem>{
|
| 324 | {modifier_bp} { return str(yyscanner, PE_MODIFIER_BP) |
| 325 | /*
|
| 326 | * The colon before memory access modifiers can get mixed up with the
|
| 327 | * colon before event modifiers. Fortunately none of the option letters
|
| 328 | * are the same, so trailing context can be used disambiguate the two
|
| 329 | * cases.
|
| 330 | */
|
| 331 | ":"/{modifier_bp} { return PE_BP_COLON |
| 332 | /*
|
| 333 | * The slash before memory length can get mixed up with the slash before
|
| 334 | * config terms. Fortunately config terms do not start with a numeric
|
| 335 | * digit, so trailing context can be used disambiguate the two cases.
|
| 336 | */
|
| 337 | "/"/{digit} { return PE_BP_SLASH |
| 338 | "/"/{non_digit} { BEGIN(config) |
| 339 | {num_dec} { return value(_parse_state, yyscanner, 10) |
| 340 | {num_hex} { return value(_parse_state, yyscanner, 16) |
| 341 | /*
|
| 342 | * We need to separate 'mem:' scanner part, in order to get specific
|
| 343 | * modifier bits parsed out. Otherwise we would need to handle PE_NAME
|
| 344 | * and we'd need to parse it manually. During the escape from <mem>
|
| 345 | * state we need to put the escaping char back, so we dont miss it.
|
| 346 | */
|
| 347 | . { unput(*yytext) |
| 348 | /*
|
| 349 | * We destroy the scanner after reaching EOF,
|
| 350 | * but anyway just to be sure get back to INIT state.
|
| 351 | */
|
| 352 | <<EOF>> { BEGIN(INITIAL) |
| 353 | }
|
| 354 |
|
| 355 | mem: { BEGIN(mem) |
| 356 | r{num_raw_hex} { return str(yyscanner, PE_RAW) |
| 357 | {num_dec} { return value(_parse_state, yyscanner, 10) |
| 358 | {num_hex} { return value(_parse_state, yyscanner, 16) |
| 359 |
|
| 360 | {modifier_event} { return modifiers(_parse_state, yyscanner) |
| 361 | {name} { return str(yyscanner, PE_NAME) |
| 362 | {quoted_name} { return quoted_str(yyscanner, PE_NAME) |
| 363 | "/" { BEGIN(config) |
| 364 | , { BEGIN(event) |
| 365 | : { return ':' |
| 366 | "{" { BEGIN(event) |
| 367 | "}" { return '}' |
| 368 | = { return '=' |
| 369 | \n { }
|
| 370 | . { }
|
| 371 |
|
| 372 | %%
|