linux/tools/perf/util/parse-events.l
Adrian Hunter f0617f526c perf parse: Allow config terms with breakpoints
Add config terms to the parsing of breakpoint events. Extend "Test event
parsing" to also cover using a confg term.

This makes breakpoint events consistent with other events which already
support config terms.

Example:

  $ cat dr_test.c
  #include <unistd.h>
  #include <stdio.h>

  void func0(void)
  {
  }

  int main()
  {
          printf("func0 %p\n", &func0);
          while (1) {
                  func0();
                  usleep(100000);
          }
          return 0;
  }
  $ gcc -g -O0 -o dr_test dr_test.c
  $ ./dr_test &
  [2] 19646
  func0 0x55feb98dd169
  $ perf record -e mem:0x55feb98dd169:x/name=breakpoint/ -p 19646 -- sleep 0.5
  [ perf record: Woken up 1 times to write data ]
  [ perf record: Captured and wrote 0.017 MB perf.data (5 samples) ]
  $ perf script
      dr_test 19646  5632.956628:          1 breakpoint:      55feb98dd169 func0+0x0 (/home/ahunter/git/work/dr_test)
      dr_test 19646  5633.056866:          1 breakpoint:      55feb98dd169 func0+0x0 (/home/ahunter/git/work/dr_test)
      dr_test 19646  5633.157084:          1 breakpoint:      55feb98dd169 func0+0x0 (/home/ahunter/git/work/dr_test)
      dr_test 19646  5633.257309:          1 breakpoint:      55feb98dd169 func0+0x0 (/home/ahunter/git/work/dr_test)
      dr_test 19646  5633.357532:          1 breakpoint:      55feb98dd169 func0+0x0 (/home/ahunter/git/work/dr_test)
  $ sudo perf test "Test event parsing"
    6: Parse event definition strings                                  :
    6.1: Test event parsing                                            : Ok
  $ sudo perf test -v "Test event parsing" |& grep mem
  running test 8 'mem:0'
  running test 9 'mem:0:x'
  running test 10 'mem:0:r'
  running test 11 'mem:0:w'
  running test 19 'mem:0:u'
  running test 20 'mem:0:x:k'
  running test 21 'mem:0:r:hp'
  running test 22 'mem:0:w:up'
  running test 26 'mem:0:rw'
  running test 27 'mem:0:rw:kp'
  running test 42 'mem:0/1'
  running test 43 'mem:0/2:w'
  running test 44 'mem:0/4:rw:u'
  running test 58 'mem:0/name=breakpoint/'
  running test 59 'mem:0:x/name=breakpoint/'
  running test 60 'mem:0:r/name=breakpoint/'
  running test 61 'mem:0:w/name=breakpoint/'
  running test 62 'mem:0/name=breakpoint/u'
  running test 63 'mem:0:x/name=breakpoint/k'
  running test 64 'mem:0:r/name=breakpoint/hp'
  running test 65 'mem:0:w/name=breakpoint/up'
  running test 66 'mem:0:rw/name=breakpoint/'
  running test 67 'mem:0:rw/name=breakpoint/kp'
  running test 68 'mem:0/1/name=breakpoint/'
  running test 69 'mem:0/2:w/name=breakpoint/'
  running test 70 'mem:0/4:rw/name=breakpoint/u'
  running test 71 'mem:0/1/name=breakpoint1/,mem:0/4:rw/name=breakpoint2/'

Committer notes:

Folded follow up patch (see 2nd link below) to address warnings about
unused tokens:

perf tools: Suppress bison unused value warnings

Patch "perf tools: Allow config terms with breakpoints" introduced parse
tokens for colons and slashes within breakpoint parsing to prevent mix
up with colons and slashes related to config terms.

The token values are not needed but introduce bison "unused value"
warnings.

Suppress those warnings.

Committer testing:

  # cat ~acme/c/mem_breakpoint.c
  #include <stdio.h>
  #include <unistd.h>

  void func1(void) { }
  void func2(void) { }
  void func3(void) { }
  void func4(void) { }
  void func5(void) { }

  int main()
  {
  	printf("func1 %p\n", &func1);
  	printf("func2 %p\n", &func2);
  	printf("func3 %p\n", &func3);
  	printf("func4 %p\n", &func4);
  	printf("func5 %p\n", &func5);
  	while (1) {
  		func1(); func2(); func3(); func4(); func5();
  		usleep(100000);
  	}
  	return 0;
  }

  # ~acme/c/mem_breakpoint &
  [1] 3186153
  func1 0x401136
  func2 0x40113d
  func3 0x401144
  func4 0x40114b
  func5 0x401152
  #

Trying to watch the first 4 functions for eXecutable access:

  # perf record -e mem:0x401136:x/name=breakpoint1/,mem:0x40113d:x/name=breakpoint2/,mem:0x401144:x/name=breakpoint3/,mem:0x40114b:x/name=breakpoint4/  -p 3186153 -- sleep 0.5
  [ perf record: Woken up 1 times to write data ]
  [ perf record: Captured and wrote 0.026 MB perf.data (20 samples) ]
  [root@five ~]# perf script
    mem_breakpoint 3186153 131612.864793:  1 breakpoint1:  401136 func1+0x0 (/var/home/acme/c/mem_breakpoint)
    mem_breakpoint 3186153 131612.864795:  1 breakpoint2:  40113d func2+0x0 (/var/home/acme/c/mem_breakpoint)
    mem_breakpoint 3186153 131612.864796:  1 breakpoint3:  401144 func3+0x0 (/var/home/acme/c/mem_breakpoint)
    mem_breakpoint 3186153 131612.864797:  1 breakpoint4:  40114b func4+0x0 (/var/home/acme/c/mem_breakpoint)
    mem_breakpoint 3186153 131612.964868:  1 breakpoint1:  401136 func1+0x0 (/var/home/acme/c/mem_breakpoint)
    mem_breakpoint 3186153 131612.964870:  1 breakpoint2:  40113d func2+0x0 (/var/home/acme/c/mem_breakpoint)
    mem_breakpoint 3186153 131612.964871:  1 breakpoint3:  401144 func3+0x0 (/var/home/acme/c/mem_breakpoint)
    mem_breakpoint 3186153 131612.964872:  1 breakpoint4:  40114b func4+0x0 (/var/home/acme/c/mem_breakpoint)
    mem_breakpoint 3186153 131613.064945:  1 breakpoint1:  401136 func1+0x0 (/var/home/acme/c/mem_breakpoint)
    mem_breakpoint 3186153 131613.064948:  1 breakpoint2:  40113d func2+0x0 (/var/home/acme/c/mem_breakpoint)
    mem_breakpoint 3186153 131613.064948:  1 breakpoint3:  401144 func3+0x0 (/var/home/acme/c/mem_breakpoint)
    mem_breakpoint 3186153 131613.064949:  1 breakpoint4:  40114b func4+0x0 (/var/home/acme/c/mem_breakpoint)
    mem_breakpoint 3186153 131613.165024:  1 breakpoint1:  401136 func1+0x0 (/var/home/acme/c/mem_breakpoint)
    mem_breakpoint 3186153 131613.165026:  1 breakpoint2:  40113d func2+0x0 (/var/home/acme/c/mem_breakpoint)
    mem_breakpoint 3186153 131613.165027:  1 breakpoint3:  401144 func3+0x0 (/var/home/acme/c/mem_breakpoint)
    mem_breakpoint 3186153 131613.165028:  1 breakpoint4:  40114b func4+0x0 (/var/home/acme/c/mem_breakpoint)
    mem_breakpoint 3186153 131613.265103:  1 breakpoint1:  401136 func1+0x0 (/var/home/acme/c/mem_breakpoint)
    mem_breakpoint 3186153 131613.265105:  1 breakpoint2:  40113d func2+0x0 (/var/home/acme/c/mem_breakpoint)
    mem_breakpoint 3186153 131613.265106:  1 breakpoint3:  401144 func3+0x0 (/var/home/acme/c/mem_breakpoint)
    mem_breakpoint 3186153 131613.265107:  1 breakpoint4:  40114b func4+0x0 (/var/home/acme/c/mem_breakpoint)
  #

Then all the 5 functions:

  # perf record -e mem:0x401136:x/name=breakpoint1/,mem:0x40113d:x/name=breakpoint2/,mem:0x401144:x/name=breakpoint3/,mem:0x40114b:x/name=breakpoint4/,mem:0x401152:x/name=breakpoint5/ -p 3186153 -- sleep 0.5
  Error:
  The sys_perf_event_open() syscall returned with 28 (No space left on device) for event (breakpoint5).
  /bin/dmesg | grep -i perf may provide additional information.

  # grep -m1 'model name' /proc/cpuinfo
  model name	: AMD Ryzen 9 5950X 16-Core Processor
  #

Reviewed-by: Ian Rogers <irogers@google.com>
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Link: https://lore.kernel.org/r/20230525082902.25332-2-adrian.hunter@intel.com
Link: https://lore.kernel.org/r/f7228dc9-fe18-a8e3-7d3f-52922e0e1113@intel.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2023-06-12 15:57:53 -03:00

396 lines
13 KiB
Plaintext

%option reentrant
%option bison-bridge
%option prefix="parse_events_"
%option stack
%option bison-locations
%option yylineno
%option reject
%{
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "parse-events.h"
#include "parse-events-bison.h"
#include "evsel.h"
char *parse_events_get_text(yyscan_t yyscanner);
YYSTYPE *parse_events_get_lval(yyscan_t yyscanner);
static int __value(YYSTYPE *yylval, char *str, int base, int token)
{
u64 num;
errno = 0;
num = strtoull(str, NULL, base);
if (errno)
return PE_ERROR;
yylval->num = num;
return token;
}
static int value(yyscan_t scanner, int base)
{
YYSTYPE *yylval = parse_events_get_lval(scanner);
char *text = parse_events_get_text(scanner);
return __value(yylval, text, base, PE_VALUE);
}
static int str(yyscan_t scanner, int token)
{
YYSTYPE *yylval = parse_events_get_lval(scanner);
char *text = parse_events_get_text(scanner);
if (text[0] != '\'') {
yylval->str = strdup(text);
} else {
/*
* If a text tag specified on the command line
* contains opening single quite ' then it is
* expected that the tag ends with single quote
* as well, like this:
* name=\'CPU_CLK_UNHALTED.THREAD:cmask=1\'
* quotes need to be escaped to bypass shell
* processing.
*/
yylval->str = strndup(&text[1], strlen(text) - 2);
}
return token;
}
static int lc_str(yyscan_t scanner, const struct parse_events_state *state)
{
return str(scanner, state->match_legacy_cache_terms ? PE_LEGACY_CACHE : PE_NAME);
}
static bool isbpf_suffix(char *text)
{
int len = strlen(text);
if (len < 2)
return false;
if ((text[len - 1] == 'c' || text[len - 1] == 'o') &&
text[len - 2] == '.')
return true;
if (len > 4 && !strcmp(text + len - 4, ".obj"))
return true;
return false;
}
static bool isbpf(yyscan_t scanner)
{
char *text = parse_events_get_text(scanner);
struct stat st;
if (!isbpf_suffix(text))
return false;
return stat(text, &st) == 0;
}
/*
* This function is called when the parser gets two kind of input:
*
* @cfg1 or @cfg2=config
*
* The leading '@' is stripped off before 'cfg1' and 'cfg2=config' are given to
* bison. In the latter case it is necessary to keep the string intact so that
* the PMU kernel driver can determine what configurable is associated to
* 'config'.
*/
static int drv_str(yyscan_t scanner, int token)
{
YYSTYPE *yylval = parse_events_get_lval(scanner);
char *text = parse_events_get_text(scanner);
/* Strip off the '@' */
yylval->str = strdup(text + 1);
return token;
}
#define REWIND(__alloc) \
do { \
YYSTYPE *__yylval = parse_events_get_lval(yyscanner); \
char *text = parse_events_get_text(yyscanner); \
\
if (__alloc) \
__yylval->str = strdup(text); \
\
yycolumn -= strlen(text); \
yyless(0); \
} while (0)
static int sym(yyscan_t scanner, int type, int config)
{
YYSTYPE *yylval = parse_events_get_lval(scanner);
yylval->num = (type << 16) + config;
return type == PERF_TYPE_HARDWARE ? PE_VALUE_SYM_HW : PE_VALUE_SYM_SW;
}
static int tool(yyscan_t scanner, enum perf_tool_event event)
{
YYSTYPE *yylval = parse_events_get_lval(scanner);
yylval->num = event;
return PE_VALUE_SYM_TOOL;
}
static int term(yyscan_t scanner, int type)
{
YYSTYPE *yylval = parse_events_get_lval(scanner);
yylval->num = type;
return PE_TERM;
}
static int hw_term(yyscan_t scanner, int config)
{
YYSTYPE *yylval = parse_events_get_lval(scanner);
char *text = parse_events_get_text(scanner);
yylval->hardware_term.str = strdup(text);
yylval->hardware_term.num = PERF_TYPE_HARDWARE + config;
return PE_TERM_HW;
}
#define YY_USER_ACTION \
do { \
yylloc->last_column = yylloc->first_column; \
yylloc->first_column = yycolumn; \
yycolumn += yyleng; \
} while (0);
#define USER_REJECT \
yycolumn -= yyleng; \
REJECT
%}
%x mem
%s config
%x event
%x array
group [^,{}/]*[{][^}]*[}][^,{}/]*
event_pmu [^,{}/]+[/][^/]*[/][^,{}/]*
event [^,{}/]+
bpf_object [^,{}]+\.(o|bpf)[a-zA-Z0-9._]*
bpf_source [^,{}]+\.c[a-zA-Z0-9._]*
num_dec [0-9]+
num_hex 0x[a-fA-F0-9]+
num_raw_hex [a-fA-F0-9]+
name [a-zA-Z_*?\[\]][a-zA-Z0-9_*?.\[\]!\-]*
name_tag [\'][a-zA-Z_*?\[\]][a-zA-Z0-9_*?\-,\.\[\]:=]*[\']
name_minus [a-zA-Z_*?][a-zA-Z0-9\-_*?.:]*
drv_cfg_term [a-zA-Z0-9_\.]+(=[a-zA-Z0-9_*?\.:]+)?
/*
* If you add a modifier you need to update check_modifier().
* Also, the letters in modifier_event must not be in modifier_bp.
*/
modifier_event [ukhpPGHSDIWeb]+
modifier_bp [rwx]{1,3}
lc_type (L1-dcache|l1-d|l1d|L1-data|L1-icache|l1-i|l1i|L1-instruction|LLC|L2|dTLB|d-tlb|Data-TLB|iTLB|i-tlb|Instruction-TLB|branch|branches|bpu|btb|bpc|node)
lc_op_result (load|loads|read|store|stores|write|prefetch|prefetches|speculative-read|speculative-load|refs|Reference|ops|access|misses|miss)
digit [0-9]
non_digit [^0-9]
%%
%{
struct parse_events_state *_parse_state = parse_events_get_extra(yyscanner);
{
int start_token = _parse_state->stoken;
if (start_token == PE_START_TERMS)
BEGIN(config);
else if (start_token == PE_START_EVENTS)
BEGIN(event);
if (start_token) {
_parse_state->stoken = 0;
/*
* The flex parser does not init locations variable
* via the scan_string interface, so we need do the
* init in here.
*/
yycolumn = 0;
return start_token;
}
}
%}
<event>{
{group} {
BEGIN(INITIAL);
REWIND(0);
}
{event_pmu} |
{bpf_object} |
{bpf_source} |
{event} {
BEGIN(INITIAL);
REWIND(1);
return PE_EVENT_NAME;
}
<<EOF>> {
BEGIN(INITIAL);
REWIND(0);
}
, {
return ',';
}
}
<array>{
"]" { BEGIN(config); return ']'; }
{num_dec} { return value(yyscanner, 10); }
{num_hex} { return value(yyscanner, 16); }
, { return ','; }
"\.\.\." { return PE_ARRAY_RANGE; }
}
<config>{
/*
* Please update config_term_names when new static term is added.
*/
config { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG); }
config1 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG1); }
config2 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG2); }
config3 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG3); }
name { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_NAME); }
period { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD); }
freq { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_SAMPLE_FREQ); }
branch_type { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE); }
time { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_TIME); }
call-graph { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CALLGRAPH); }
stack-size { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_STACKSIZE); }
max-stack { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_MAX_STACK); }
nr { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_MAX_EVENTS); }
inherit { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_INHERIT); }
no-inherit { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_NOINHERIT); }
overwrite { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_OVERWRITE); }
no-overwrite { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_NOOVERWRITE); }
percore { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_PERCORE); }
aux-output { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT); }
aux-sample-size { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_AUX_SAMPLE_SIZE); }
metric-id { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_METRIC_ID); }
cpu-cycles|cycles { return hw_term(yyscanner, PERF_COUNT_HW_CPU_CYCLES); }
stalled-cycles-frontend|idle-cycles-frontend { return hw_term(yyscanner, PERF_COUNT_HW_STALLED_CYCLES_FRONTEND); }
stalled-cycles-backend|idle-cycles-backend { return hw_term(yyscanner, PERF_COUNT_HW_STALLED_CYCLES_BACKEND); }
instructions { return hw_term(yyscanner, PERF_COUNT_HW_INSTRUCTIONS); }
cache-references { return hw_term(yyscanner, PERF_COUNT_HW_CACHE_REFERENCES); }
cache-misses { return hw_term(yyscanner, PERF_COUNT_HW_CACHE_MISSES); }
branch-instructions|branches { return hw_term(yyscanner, PERF_COUNT_HW_BRANCH_INSTRUCTIONS); }
branch-misses { return hw_term(yyscanner, PERF_COUNT_HW_BRANCH_MISSES); }
bus-cycles { return hw_term(yyscanner, PERF_COUNT_HW_BUS_CYCLES); }
ref-cycles { return hw_term(yyscanner, PERF_COUNT_HW_REF_CPU_CYCLES); }
r{num_raw_hex} { return str(yyscanner, PE_RAW); }
r0x{num_raw_hex} { return str(yyscanner, PE_RAW); }
, { return ','; }
"/" { BEGIN(INITIAL); return '/'; }
{lc_type} { return lc_str(yyscanner, _parse_state); }
{lc_type}-{lc_op_result} { return lc_str(yyscanner, _parse_state); }
{lc_type}-{lc_op_result}-{lc_op_result} { return lc_str(yyscanner, _parse_state); }
{name_minus} { return str(yyscanner, PE_NAME); }
\[all\] { return PE_ARRAY_ALL; }
"[" { BEGIN(array); return '['; }
@{drv_cfg_term} { return drv_str(yyscanner, PE_DRV_CFG_TERM); }
}
<mem>{
{modifier_bp} { return str(yyscanner, PE_MODIFIER_BP); }
/*
* The colon before memory access modifiers can get mixed up with the
* colon before event modifiers. Fortunately none of the option letters
* are the same, so trailing context can be used disambiguate the two
* cases.
*/
":"/{modifier_bp} { return str(yyscanner, PE_BP_COLON); }
/*
* The slash before memory length can get mixed up with the slash before
* config terms. Fortunately config terms do not start with a numeric
* digit, so trailing context can be used disambiguate the two cases.
*/
"/"/{digit} { return str(yyscanner, PE_BP_SLASH); }
"/"/{non_digit} { BEGIN(config); return '/'; }
{num_dec} { return value(yyscanner, 10); }
{num_hex} { return value(yyscanner, 16); }
/*
* We need to separate 'mem:' scanner part, in order to get specific
* modifier bits parsed out. Otherwise we would need to handle PE_NAME
* and we'd need to parse it manually. During the escape from <mem>
* state we need to put the escaping char back, so we dont miss it.
*/
. { unput(*yytext); BEGIN(INITIAL); }
/*
* We destroy the scanner after reaching EOF,
* but anyway just to be sure get back to INIT state.
*/
<<EOF>> { BEGIN(INITIAL); }
}
cpu-cycles|cycles { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES); }
stalled-cycles-frontend|idle-cycles-frontend { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_FRONTEND); }
stalled-cycles-backend|idle-cycles-backend { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_BACKEND); }
instructions { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_INSTRUCTIONS); }
cache-references { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_REFERENCES); }
cache-misses { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_MISSES); }
branch-instructions|branches { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_BRANCH_INSTRUCTIONS); }
branch-misses { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_BRANCH_MISSES); }
bus-cycles { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_BUS_CYCLES); }
ref-cycles { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_REF_CPU_CYCLES); }
cpu-clock { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_CLOCK); }
task-clock { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_TASK_CLOCK); }
page-faults|faults { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_PAGE_FAULTS); }
minor-faults { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_PAGE_FAULTS_MIN); }
major-faults { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_PAGE_FAULTS_MAJ); }
context-switches|cs { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CONTEXT_SWITCHES); }
cpu-migrations|migrations { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_MIGRATIONS); }
alignment-faults { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_ALIGNMENT_FAULTS); }
emulation-faults { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_EMULATION_FAULTS); }
dummy { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_DUMMY); }
duration_time { return tool(yyscanner, PERF_TOOL_DURATION_TIME); }
user_time { return tool(yyscanner, PERF_TOOL_USER_TIME); }
system_time { return tool(yyscanner, PERF_TOOL_SYSTEM_TIME); }
bpf-output { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_BPF_OUTPUT); }
cgroup-switches { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CGROUP_SWITCHES); }
{lc_type} { return str(yyscanner, PE_LEGACY_CACHE); }
{lc_type}-{lc_op_result} { return str(yyscanner, PE_LEGACY_CACHE); }
{lc_type}-{lc_op_result}-{lc_op_result} { return str(yyscanner, PE_LEGACY_CACHE); }
mem: { BEGIN(mem); return PE_PREFIX_MEM; }
r{num_raw_hex} { return str(yyscanner, PE_RAW); }
{num_dec} { return value(yyscanner, 10); }
{num_hex} { return value(yyscanner, 16); }
{modifier_event} { return str(yyscanner, PE_MODIFIER_EVENT); }
{bpf_object} { if (!isbpf(yyscanner)) { USER_REJECT }; return str(yyscanner, PE_BPF_OBJECT); }
{bpf_source} { if (!isbpf(yyscanner)) { USER_REJECT }; return str(yyscanner, PE_BPF_SOURCE); }
{name} { return str(yyscanner, PE_NAME); }
{name_tag} { return str(yyscanner, PE_NAME); }
"/" { BEGIN(config); return '/'; }
, { BEGIN(event); return ','; }
: { return ':'; }
"{" { BEGIN(event); return '{'; }
"}" { return '}'; }
= { return '='; }
\n { }
. { }
%%
int parse_events_wrap(void *scanner __maybe_unused)
{
return 1;
}