dect
/
linux-2.6
Archived
13
0
Fork 0

perf tools: Add parser generator for events parsing

Changing event parsing to use flex/bison parse generator.
The event syntax stays as it was.

grammar description:

events: events ',' event | event

event:  event_def PE_MODIFIER_EVENT | event_def

event_def: event_legacy_symbol sep_dc     |
           event_legacy_cache sep_dc      |
           event_legacy_breakpoint sep_dc |
           event_legacy_tracepoint sep_dc |
           event_legacy_numeric sep_dc    |
           event_legacy_raw sep_dc

event_legacy_symbol:      PE_NAME_SYM

event_legacy_cache:       PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT '-' PE_NAME_CACHE_OP_RESULT |
                          PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT  |
                          PE_NAME_CACHE_TYPE

event_legacy_raw:         PE_SEP_RAW PE_VALUE

event_legacy_numeric:     PE_VALUE ':' PE_VALUE

event_legacy_breakpoint:  PE_SEP_BP ':' PE_VALUE ':' PE_MODIFIER_BP

event_breakpoint_type:    PE_MODIFIER_BPTYPE | empty

PE_NAME_SYM:              cpu-cycles|cycles                              |
                          stalled-cycles-frontend|idle-cycles-frontend   |
                          stalled-cycles-backend|idle-cycles-backend     |
                          instructions                                   |
                          cache-references                               |
                          cache-misses                                   |
                          branch-instructions|branches                   |
                          branch-misses                                  |
                          bus-cycles                                     |
                          cpu-clock                                      |
                          task-clock                                     |
                          page-faults|faults                             |
                          minor-faults                                   |
                          major-faults                                   |
                          context-switches|cs                            |
                          cpu-migrations|migrations                      |
                          alignment-faults                               |
                          emulation-faults

PE_NAME_CACHE_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

PE_NAME_CACHE_OP_RESULT:  load|loads|read                        |
                          store|stores|write                     |
                          prefetch|prefetches                    |
                          speculative-read|speculative-load      |
                          refs|Reference|ops|access              |
                          misses|miss

PE_MODIFIER_EVENT:        [ukhp]{0,5}

PE_MODIFIER_BP:           [rwx]

PE_SEP_BP:                'mem'

PE_SEP_RAW:               'r'

sep_dc:                   ':' |

Added flex/bison files for event grammar parsing. The generated
parser is part of the patch. Added makefile rule 'event-parser'
to generate the parser code out of the bison/flex sources.

Acked-by: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: Jiri Olsa <jolsa@redhat.com>
Link: http://lkml.kernel.org/n/tip-u4pfig5waq3ll2bfcdex8fgi@git.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
Jiri Olsa 2012-03-15 20:09:15 +01:00 committed by Arnaldo Carvalho de Melo
parent 641cc93881
commit 89812fc81f
10 changed files with 4752 additions and 341 deletions

View File

@ -61,6 +61,8 @@ ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \
CC = $(CROSS_COMPILE)gcc
AR = $(CROSS_COMPILE)ar
FLEX = $(CROSS_COMPILE)flex
BISON= $(CROSS_COMPILE)bison
# Additional ARCH settings for x86
ifeq ($(ARCH),i386)
@ -357,6 +359,8 @@ LIB_OBJS += $(OUTPUT)util/session.o
LIB_OBJS += $(OUTPUT)util/thread.o
LIB_OBJS += $(OUTPUT)util/thread_map.o
LIB_OBJS += $(OUTPUT)util/trace-event-parse.o
LIB_OBJS += $(OUTPUT)util/parse-events-flex.o
LIB_OBJS += $(OUTPUT)util/parse-events-bison.o
LIB_OBJS += $(OUTPUT)util/trace-event-read.o
LIB_OBJS += $(OUTPUT)util/trace-event-info.o
LIB_OBJS += $(OUTPUT)util/trace-event-scripting.o
@ -645,6 +649,8 @@ ifndef V
QUIET_LINK = @echo ' ' LINK $@;
QUIET_MKDIR = @echo ' ' MKDIR $@;
QUIET_GEN = @echo ' ' GEN $@;
QUIET_FLEX = @echo ' ' FLEX $@;
QUIET_BISON = @echo ' ' BISON $@;
endif
endif
@ -725,6 +731,9 @@ $(OUTPUT)perf.o perf.spec \
$(SCRIPTS) \
: $(OUTPUT)PERF-VERSION-FILE
.SUFFIXES:
.SUFFIXES: .o .c .S .s
$(OUTPUT)%.o: %.c $(OUTPUT)PERF-CFLAGS
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $<
$(OUTPUT)%.i: %.c $(OUTPUT)PERF-CFLAGS
@ -761,6 +770,9 @@ $(OUTPUT)util/ui/browsers/map.o: util/ui/browsers/map.c $(OUTPUT)PERF-CFLAGS
$(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $<
$(OUTPUT)util/parse-events-flex.o: util/parse-events-flex.c $(OUTPUT)PERF-CFLAGS
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -Wno-redundant-decls -Wno-switch-default -Wno-unused-function $<
$(OUTPUT)util/scripting-engines/trace-event-perl.o: util/scripting-engines/trace-event-perl.c $(OUTPUT)PERF-CFLAGS
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow $<
@ -797,6 +809,7 @@ help:
@echo ' html - make html documentation'
@echo ' info - make GNU info documentation (access with info <foo>)'
@echo ' pdf - make pdf documentation'
@echo ' event-parser - make event parser code'
@echo ' TAGS - use etags to make tag information for source browsing'
@echo ' tags - use ctags to make tag information for source browsing'
@echo ' cscope - use cscope to make interactive browsing database'
@ -846,6 +859,10 @@ cscope:
$(RM) cscope*
$(FIND) . -name '*.[hcS]' -print | xargs cscope -b
event-parser:
$(QUIET_BISON)$(BISON) -v util/parse-events.y -d -o util/parse-events-bison.c
$(QUIET_FLEX)$(FLEX) --header-file=util/parse-events-flex.h -t util/parse-events.l > util/parse-events-flex.c
### Detect prefix changes
TRACK_CFLAGS = $(subst ','\'',$(ALL_CFLAGS)):\
$(bindir_SQ):$(perfexecdir_SQ):$(template_dir_SQ):$(prefix_SQ)

View File

@ -650,7 +650,7 @@ static int test__checkevent_raw(struct perf_evlist *evlist)
TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type);
TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config);
TEST_ASSERT_VAL("wrong config", 0x1a == evsel->attr.config);
return 0;
}
@ -872,7 +872,7 @@ static struct test__event_st {
.check = test__checkevent_tracepoint_multi,
},
{
.name = "r1",
.name = "r1a",
.check = test__checkevent_raw,
},
{
@ -916,7 +916,7 @@ static struct test__event_st {
.check = test__checkevent_tracepoint_multi_modifier,
},
{
.name = "r1:kp",
.name = "r1a:kp",
.check = test__checkevent_raw_modifier,
},
{

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,79 @@
/* A Bison parser, made by GNU Bison 2.4.3. */
/* Skeleton interface for Bison's Yacc-like parsers in C
Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
2009, 2010 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* As a special exception, you may create a larger work that contains
part or all of the Bison parser skeleton and distribute that work
under terms of your choice, so long as that work isn't itself a
parser generator using the skeleton or a modified version thereof
as a parser skeleton. Alternatively, if you modify or redistribute
the parser skeleton itself, you may (at your option) remove this
special exception, which will cause the skeleton and the resulting
Bison output files to be licensed under the GNU General Public
License without this special exception.
This special exception was added by the Free Software Foundation in
version 2.2 of Bison. */
/* Tokens. */
#ifndef YYTOKENTYPE
# define YYTOKENTYPE
/* Put the tokens into the symbol table, so that GDB and other debuggers
know about them. */
enum yytokentype {
PE_VALUE = 258,
PE_VALUE_SYM = 259,
PE_RAW = 260,
PE_NAME = 261,
PE_MODIFIER_EVENT = 262,
PE_MODIFIER_BP = 263,
PE_NAME_CACHE_TYPE = 264,
PE_NAME_CACHE_OP_RESULT = 265,
PE_PREFIX_MEM = 266,
PE_PREFIX_RAW = 267,
PE_ERROR = 268
};
#endif
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
typedef union YYSTYPE
{
/* Line 1685 of yacc.c */
#line 42 "util/parse-events.y"
char *str;
unsigned long num;
/* Line 1685 of yacc.c */
#line 71 "util/parse-events-bison.h"
} YYSTYPE;
# define YYSTYPE_IS_TRIVIAL 1
# define yystype YYSTYPE /* obsolescent; will be withdrawn */
# define YYSTYPE_IS_DECLARED 1
#endif
extern YYSTYPE parse_events_lval;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,316 @@
#ifndef parse_events_HEADER_H
#define parse_events_HEADER_H 1
#define parse_events_IN_HEADER 1
#line 6 "util/parse-events-flex.h"
#define YY_INT_ALIGNED short int
/* A lexical scanner generated by flex */
#define FLEX_SCANNER
#define YY_FLEX_MAJOR_VERSION 2
#define YY_FLEX_MINOR_VERSION 5
#define YY_FLEX_SUBMINOR_VERSION 35
#if YY_FLEX_SUBMINOR_VERSION > 0
#define FLEX_BETA
#endif
/* First, we deal with platform-specific or compiler-specific issues. */
/* begin standard C headers. */
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
/* end standard C headers. */
/* flex integer type definitions */
#ifndef FLEXINT_H
#define FLEXINT_H
/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
* if you want the limit (max/min) macros for int types.
*/
#ifndef __STDC_LIMIT_MACROS
#define __STDC_LIMIT_MACROS 1
#endif
#include <inttypes.h>
typedef int8_t flex_int8_t;
typedef uint8_t flex_uint8_t;
typedef int16_t flex_int16_t;
typedef uint16_t flex_uint16_t;
typedef int32_t flex_int32_t;
typedef uint32_t flex_uint32_t;
#else
typedef signed char flex_int8_t;
typedef short int flex_int16_t;
typedef int flex_int32_t;
typedef unsigned char flex_uint8_t;
typedef unsigned short int flex_uint16_t;
typedef unsigned int flex_uint32_t;
#endif /* ! C99 */
/* Limits of integral types. */
#ifndef INT8_MIN
#define INT8_MIN (-128)
#endif
#ifndef INT16_MIN
#define INT16_MIN (-32767-1)
#endif
#ifndef INT32_MIN
#define INT32_MIN (-2147483647-1)
#endif
#ifndef INT8_MAX
#define INT8_MAX (127)
#endif
#ifndef INT16_MAX
#define INT16_MAX (32767)
#endif
#ifndef INT32_MAX
#define INT32_MAX (2147483647)
#endif
#ifndef UINT8_MAX
#define UINT8_MAX (255U)
#endif
#ifndef UINT16_MAX
#define UINT16_MAX (65535U)
#endif
#ifndef UINT32_MAX
#define UINT32_MAX (4294967295U)
#endif
#endif /* ! FLEXINT_H */
#ifdef __cplusplus
/* The "const" storage-class-modifier is valid. */
#define YY_USE_CONST
#else /* ! __cplusplus */
/* C99 requires __STDC__ to be defined as 1. */
#if defined (__STDC__)
#define YY_USE_CONST
#endif /* defined (__STDC__) */
#endif /* ! __cplusplus */
#ifdef YY_USE_CONST
#define yyconst const
#else
#define yyconst
#endif
/* Size of default input buffer. */
#ifndef YY_BUF_SIZE
#define YY_BUF_SIZE 16384
#endif
#ifndef YY_TYPEDEF_YY_BUFFER_STATE
#define YY_TYPEDEF_YY_BUFFER_STATE
typedef struct yy_buffer_state *YY_BUFFER_STATE;
#endif
extern int parse_events_leng;
extern FILE *parse_events_in, *parse_events_out;
#ifndef YY_TYPEDEF_YY_SIZE_T
#define YY_TYPEDEF_YY_SIZE_T
typedef size_t yy_size_t;
#endif
#ifndef YY_STRUCT_YY_BUFFER_STATE
#define YY_STRUCT_YY_BUFFER_STATE
struct yy_buffer_state
{
FILE *yy_input_file;
char *yy_ch_buf; /* input buffer */
char *yy_buf_pos; /* current position in input buffer */
/* Size of input buffer in bytes, not including room for EOB
* characters.
*/
yy_size_t yy_buf_size;
/* Number of characters read into yy_ch_buf, not including EOB
* characters.
*/
int yy_n_chars;
/* Whether we "own" the buffer - i.e., we know we created it,
* and can realloc() it to grow it, and should free() it to
* delete it.
*/
int yy_is_our_buffer;
/* Whether this is an "interactive" input source; if so, and
* if we're using stdio for input, then we want to use getc()
* instead of fread(), to make sure we stop fetching input after
* each newline.
*/
int yy_is_interactive;
/* Whether we're considered to be at the beginning of a line.
* If so, '^' rules will be active on the next match, otherwise
* not.
*/
int yy_at_bol;
int yy_bs_lineno; /**< The line count. */
int yy_bs_column; /**< The column count. */
/* Whether to try to fill the input buffer when we reach the
* end of it.
*/
int yy_fill_buffer;
int yy_buffer_status;
};
#endif /* !YY_STRUCT_YY_BUFFER_STATE */
void parse_events_restart (FILE *input_file );
void parse_events__switch_to_buffer (YY_BUFFER_STATE new_buffer );
YY_BUFFER_STATE parse_events__create_buffer (FILE *file,int size );
void parse_events__delete_buffer (YY_BUFFER_STATE b );
void parse_events__flush_buffer (YY_BUFFER_STATE b );
void parse_events_push_buffer_state (YY_BUFFER_STATE new_buffer );
void parse_events_pop_buffer_state (void );
YY_BUFFER_STATE parse_events__scan_buffer (char *base,yy_size_t size );
YY_BUFFER_STATE parse_events__scan_string (yyconst char *yy_str );
YY_BUFFER_STATE parse_events__scan_bytes (yyconst char *bytes,int len );
void *parse_events_alloc (yy_size_t );
void *parse_events_realloc (void *,yy_size_t );
void parse_events_free (void * );
/* Begin user sect3 */
extern int parse_events_lineno;
extern char *parse_events_text;
#define yytext_ptr parse_events_text
#ifdef YY_HEADER_EXPORT_START_CONDITIONS
#define INITIAL 0
#endif
#ifndef YY_NO_UNISTD_H
/* Special case for "unistd.h", since it is non-ANSI. We include it way
* down here because we want the user's section 1 to have been scanned first.
* The user has a chance to override it with an option.
*/
#include <unistd.h>
#endif
#ifndef YY_EXTRA_TYPE
#define YY_EXTRA_TYPE void *
#endif
/* Accessor methods to globals.
These are made visible to non-reentrant scanners for convenience. */
int parse_events_lex_destroy (void );
int parse_events_get_debug (void );
void parse_events_set_debug (int debug_flag );
YY_EXTRA_TYPE parse_events_get_extra (void );
void parse_events_set_extra (YY_EXTRA_TYPE user_defined );
FILE *parse_events_get_in (void );
void parse_events_set_in (FILE * in_str );
FILE *parse_events_get_out (void );
void parse_events_set_out (FILE * out_str );
int parse_events_get_leng (void );
char *parse_events_get_text (void );
int parse_events_get_lineno (void );
void parse_events_set_lineno (int line_number );
/* Macros after this point can all be overridden by user definitions in
* section 1.
*/
#ifndef YY_SKIP_YYWRAP
#ifdef __cplusplus
extern "C" int parse_events_wrap (void );
#else
extern int parse_events_wrap (void );
#endif
#endif
#ifndef yytext_ptr
static void yy_flex_strncpy (char *,yyconst char *,int );
#endif
#ifdef YY_NEED_STRLEN
static int yy_flex_strlen (yyconst char * );
#endif
#ifndef YY_NO_INPUT
#endif
/* Amount of stuff to slurp up with each read. */
#ifndef YY_READ_BUF_SIZE
#define YY_READ_BUF_SIZE 8192
#endif
/* Number of entries by which start-condition stack grows. */
#ifndef YY_START_STACK_INCR
#define YY_START_STACK_INCR 25
#endif
/* Default declaration of generated scanner - a define so the user can
* easily add parameters.
*/
#ifndef YY_DECL
#define YY_DECL_IS_OURS 1
extern int parse_events_lex (void);
#define YY_DECL int parse_events_lex (void)
#endif /* !YY_DECL */
/* yy_get_previous_state - get the state just before the EOB char was reached */
#undef YY_NEW_FILE
#undef YY_FLUSH_BUFFER
#undef yy_set_bol
#undef yy_new_buffer
#undef yy_set_interactive
#undef YY_DO_BEFORE_ACTION
#ifdef YY_DECL_IS_OURS
#undef YY_DECL_IS_OURS
#undef YY_DECL
#endif
#line 102 "util/parse-events.l"
#line 315 "util/parse-events-flex.h"
#undef parse_events_IN_HEADER
#endif /* parse_events_HEADER_H */

View File

@ -11,6 +11,9 @@
#include "cache.h"
#include "header.h"
#include "debugfs.h"
#include "parse-events-flex.h"
#define MAX_NAME_LEN 100
struct event_symbol {
u8 type;
@ -19,11 +22,7 @@ struct event_symbol {
const char *alias;
};
enum event_result {
EVT_FAILED,
EVT_HANDLED,
EVT_HANDLED_ALL
};
int parse_events_parse(struct list_head *list, int *idx);
#define CHW(x) .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_##x
#define CSW(x) .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_##x
@ -354,7 +353,24 @@ const char *__event_name(int type, u64 config)
return "unknown";
}
static int parse_aliases(const char **str, const char *names[][MAX_ALIASES], int size)
static int add_event(struct list_head *list, int *idx,
struct perf_event_attr *attr, char *name)
{
struct perf_evsel *evsel;
event_attr_init(attr);
evsel = perf_evsel__new(attr, (*idx)++);
if (!evsel)
return -ENOMEM;
list_add_tail(&evsel->node, list);
evsel->name = strdup(name);
return 0;
}
static int parse_aliases(char *str, const char *names[][MAX_ALIASES], int size)
{
int i, j;
int n, longest = -1;
@ -362,58 +378,57 @@ static int parse_aliases(const char **str, const char *names[][MAX_ALIASES], int
for (i = 0; i < size; i++) {
for (j = 0; j < MAX_ALIASES && names[i][j]; j++) {
n = strlen(names[i][j]);
if (n > longest && !strncasecmp(*str, names[i][j], n))
if (n > longest && !strncasecmp(str, names[i][j], n))
longest = n;
}
if (longest > 0) {
*str += longest;
if (longest > 0)
return i;
}
}
return -1;
}
static enum event_result
parse_generic_hw_event(const char **str, struct perf_event_attr *attr)
int parse_events_add_cache(struct list_head *list, int *idx,
char *type, char *op_result1, char *op_result2)
{
const char *s = *str;
struct perf_event_attr attr;
char name[MAX_NAME_LEN];
int cache_type = -1, cache_op = -1, cache_result = -1;
char *op_result[2] = { op_result1, op_result2 };
int i, n;
cache_type = parse_aliases(&s, hw_cache, PERF_COUNT_HW_CACHE_MAX);
/*
* No fallback - if we cannot get a clear cache type
* then bail out:
*/
cache_type = parse_aliases(type, hw_cache,
PERF_COUNT_HW_CACHE_MAX);
if (cache_type == -1)
return EVT_FAILED;
return -EINVAL;
while ((cache_op == -1 || cache_result == -1) && *s == '-') {
++s;
n = snprintf(name, MAX_NAME_LEN, "%s", type);
for (i = 0; (i < 2) && (op_result[i]); i++) {
char *str = op_result[i];
snprintf(name + n, MAX_NAME_LEN - n, "-%s\n", str);
if (cache_op == -1) {
cache_op = parse_aliases(&s, hw_cache_op,
PERF_COUNT_HW_CACHE_OP_MAX);
cache_op = parse_aliases(str, hw_cache_op,
PERF_COUNT_HW_CACHE_OP_MAX);
if (cache_op >= 0) {
if (!is_cache_op_valid(cache_type, cache_op))
return EVT_FAILED;
return -EINVAL;
continue;
}
}
if (cache_result == -1) {
cache_result = parse_aliases(&s, hw_cache_result,
cache_result = parse_aliases(str, hw_cache_result,
PERF_COUNT_HW_CACHE_RESULT_MAX);
if (cache_result >= 0)
continue;
}
/*
* Can't parse this as a cache op or result, so back up
* to the '-'.
*/
--s;
break;
}
/*
@ -428,20 +443,17 @@ parse_generic_hw_event(const char **str, struct perf_event_attr *attr)
if (cache_result == -1)
cache_result = PERF_COUNT_HW_CACHE_RESULT_ACCESS;
attr->config = cache_type | (cache_op << 8) | (cache_result << 16);
attr->type = PERF_TYPE_HW_CACHE;
*str = s;
return EVT_HANDLED;
memset(&attr, 0, sizeof(attr));
attr.config = cache_type | (cache_op << 8) | (cache_result << 16);
attr.type = PERF_TYPE_HW_CACHE;
return add_event(list, idx, &attr, name);
}
static enum event_result
parse_single_tracepoint_event(char *sys_name,
const char *evt_name,
unsigned int evt_length,
struct perf_event_attr *attr,
const char **strp)
static int add_tracepoint(struct list_head *list, int *idx,
char *sys_name, char *evt_name)
{
struct perf_event_attr attr;
char name[MAX_NAME_LEN];
char evt_path[MAXPATHLEN];
char id_buf[4];
u64 id;
@ -452,130 +464,80 @@ parse_single_tracepoint_event(char *sys_name,
fd = open(evt_path, O_RDONLY);
if (fd < 0)
return EVT_FAILED;
return -1;
if (read(fd, id_buf, sizeof(id_buf)) < 0) {
close(fd);
return EVT_FAILED;
return -1;
}
close(fd);
id = atoll(id_buf);
attr->config = id;
attr->type = PERF_TYPE_TRACEPOINT;
*strp += strlen(sys_name) + evt_length + 1; /* + 1 for the ':' */
attr->sample_type |= PERF_SAMPLE_RAW;
attr->sample_type |= PERF_SAMPLE_TIME;
attr->sample_type |= PERF_SAMPLE_CPU;
memset(&attr, 0, sizeof(attr));
attr.config = id;
attr.type = PERF_TYPE_TRACEPOINT;
attr.sample_type |= PERF_SAMPLE_RAW;
attr.sample_type |= PERF_SAMPLE_TIME;
attr.sample_type |= PERF_SAMPLE_CPU;
attr.sample_period = 1;
attr->sample_period = 1;
return EVT_HANDLED;
snprintf(name, MAX_NAME_LEN, "%s:%s", sys_name, evt_name);
return add_event(list, idx, &attr, name);
}
/* sys + ':' + event + ':' + flags*/
#define MAX_EVOPT_LEN (MAX_EVENT_LENGTH * 2 + 2 + 128)
static enum event_result
parse_multiple_tracepoint_event(struct perf_evlist *evlist, char *sys_name,
const char *evt_exp, char *flags)
static int add_tracepoint_multi(struct list_head *list, int *idx,
char *sys_name, char *evt_name)
{
char evt_path[MAXPATHLEN];
struct dirent *evt_ent;
DIR *evt_dir;
int ret = 0;
snprintf(evt_path, MAXPATHLEN, "%s/%s", tracing_events_path, sys_name);
evt_dir = opendir(evt_path);
if (!evt_dir) {
perror("Can't open event dir");
return EVT_FAILED;
return -1;
}
while ((evt_ent = readdir(evt_dir))) {
char event_opt[MAX_EVOPT_LEN + 1];
int len;
while (!ret && (evt_ent = readdir(evt_dir))) {
if (!strcmp(evt_ent->d_name, ".")
|| !strcmp(evt_ent->d_name, "..")
|| !strcmp(evt_ent->d_name, "enable")
|| !strcmp(evt_ent->d_name, "filter"))
continue;
if (!strglobmatch(evt_ent->d_name, evt_exp))
if (!strglobmatch(evt_ent->d_name, evt_name))
continue;
len = snprintf(event_opt, MAX_EVOPT_LEN, "%s:%s%s%s", sys_name,
evt_ent->d_name, flags ? ":" : "",
flags ?: "");
if (len < 0)
return EVT_FAILED;
if (parse_events(evlist, event_opt, 0))
return EVT_FAILED;
ret = add_tracepoint(list, idx, sys_name, evt_ent->d_name);
}
return EVT_HANDLED_ALL;
return ret;
}
static enum event_result
parse_tracepoint_event(struct perf_evlist *evlist, const char **strp,
struct perf_event_attr *attr)
int parse_events_add_tracepoint(struct list_head *list, int *idx,
char *sys, char *event)
{
const char *evt_name;
char *flags = NULL, *comma_loc;
char sys_name[MAX_EVENT_LENGTH];
unsigned int sys_length, evt_length;
int ret;
if (debugfs_valid_mountpoint(tracing_events_path))
return 0;
ret = debugfs_valid_mountpoint(tracing_events_path);
if (ret)
return ret;
evt_name = strchr(*strp, ':');
if (!evt_name)
return EVT_FAILED;
sys_length = evt_name - *strp;
if (sys_length >= MAX_EVENT_LENGTH)
return 0;
strncpy(sys_name, *strp, sys_length);
sys_name[sys_length] = '\0';
evt_name = evt_name + 1;
comma_loc = strchr(evt_name, ',');
if (comma_loc) {
/* take the event name up to the comma */
evt_name = strndup(evt_name, comma_loc - evt_name);
}
flags = strchr(evt_name, ':');
if (flags) {
/* split it out: */
evt_name = strndup(evt_name, flags - evt_name);
flags++;
}
evt_length = strlen(evt_name);
if (evt_length >= MAX_EVENT_LENGTH)
return EVT_FAILED;
if (strpbrk(evt_name, "*?")) {
*strp += strlen(sys_name) + evt_length + 1; /* 1 == the ':' */
return parse_multiple_tracepoint_event(evlist, sys_name,
evt_name, flags);
} else {
return parse_single_tracepoint_event(sys_name, evt_name,
evt_length, attr, strp);
}
return strpbrk(event, "*?") ?
add_tracepoint_multi(list, idx, sys, event) :
add_tracepoint(list, idx, sys, event);
}
static enum event_result
parse_breakpoint_type(const char *type, const char **strp,
struct perf_event_attr *attr)
static int
parse_breakpoint_type(const char *type, struct perf_event_attr *attr)
{
int i;
for (i = 0; i < 3; i++) {
if (!type[i])
if (!type || !type[i])
break;
switch (type[i]) {
@ -589,164 +551,65 @@ parse_breakpoint_type(const char *type, const char **strp,
attr->bp_type |= HW_BREAKPOINT_X;
break;
default:
return EVT_FAILED;
return -EINVAL;
}
}
if (!attr->bp_type) /* Default */
attr->bp_type = HW_BREAKPOINT_R | HW_BREAKPOINT_W;
*strp = type + i;
return EVT_HANDLED;
return 0;
}
static enum event_result
parse_breakpoint_event(const char **strp, struct perf_event_attr *attr)
int parse_events_add_breakpoint(struct list_head *list, int *idx,
void *ptr, char *type)
{
const char *target;
const char *type;
char *endaddr;
u64 addr;
enum event_result err;
struct perf_event_attr attr;
char name[MAX_NAME_LEN];
target = strchr(*strp, ':');
if (!target)
return EVT_FAILED;
memset(&attr, 0, sizeof(attr));
attr.bp_addr = (u64) ptr;
if (strncmp(*strp, "mem", target - *strp) != 0)
return EVT_FAILED;
target++;
addr = strtoull(target, &endaddr, 0);
if (target == endaddr)
return EVT_FAILED;
attr->bp_addr = addr;
*strp = endaddr;
type = strchr(target, ':');
/* If no type is defined, just rw as default */
if (!type) {
attr->bp_type = HW_BREAKPOINT_R | HW_BREAKPOINT_W;
} else {
err = parse_breakpoint_type(++type, strp, attr);
if (err == EVT_FAILED)
return EVT_FAILED;
}
if (parse_breakpoint_type(type, &attr))
return -EINVAL;
/*
* We should find a nice way to override the access length
* Provide some defaults for now
*/
if (attr->bp_type == HW_BREAKPOINT_X)
attr->bp_len = sizeof(long);
if (attr.bp_type == HW_BREAKPOINT_X)
attr.bp_len = sizeof(long);
else
attr->bp_len = HW_BREAKPOINT_LEN_4;
attr.bp_len = HW_BREAKPOINT_LEN_4;
attr->type = PERF_TYPE_BREAKPOINT;
attr.type = PERF_TYPE_BREAKPOINT;
return EVT_HANDLED;
snprintf(name, MAX_NAME_LEN, "mem:%p:%s", ptr, type ? type : "rw");
return add_event(list, idx, &attr, name);
}
static int check_events(const char *str, unsigned int i)
int
parse_events_add_numeric(struct list_head *list, int *idx,
unsigned long type, unsigned long config)
{
int n;
struct perf_event_attr attr;
n = strlen(event_symbols[i].symbol);
if (!strncasecmp(str, event_symbols[i].symbol, n))
return n;
n = strlen(event_symbols[i].alias);
if (n) {
if (!strncasecmp(str, event_symbols[i].alias, n))
return n;
}
return 0;
memset(&attr, 0, sizeof(attr));
attr.type = type;
attr.config = config;
return add_event(list, idx, &attr,
(char *) __event_name(type, config));
}
static enum event_result
parse_symbolic_event(const char **strp, struct perf_event_attr *attr)
int parse_events_modifier(struct list_head *list, char *str)
{
const char *str = *strp;
unsigned int i;
int n;
for (i = 0; i < ARRAY_SIZE(event_symbols); i++) {
n = check_events(str, i);
if (n > 0) {
attr->type = event_symbols[i].type;
attr->config = event_symbols[i].config;
*strp = str + n;
return EVT_HANDLED;
}
}
return EVT_FAILED;
}
static enum event_result
parse_raw_event(const char **strp, struct perf_event_attr *attr)
{
const char *str = *strp;
u64 config;
int n;
if (*str != 'r')
return EVT_FAILED;
n = hex2u64(str + 1, &config);
if (n > 0) {
const char *end = str + n + 1;
if (*end != '\0' && *end != ',' && *end != ':')
return EVT_FAILED;
*strp = end;
attr->type = PERF_TYPE_RAW;
attr->config = config;
return EVT_HANDLED;
}
return EVT_FAILED;
}
static enum event_result
parse_numeric_event(const char **strp, struct perf_event_attr *attr)
{
const char *str = *strp;
char *endp;
unsigned long type;
u64 config;
type = strtoul(str, &endp, 0);
if (endp > str && type < PERF_TYPE_MAX && *endp == ':') {
str = endp + 1;
config = strtoul(str, &endp, 0);
if (endp > str) {
attr->type = type;
attr->config = config;
*strp = endp;
return EVT_HANDLED;
}
}
return EVT_FAILED;
}
static int
parse_event_modifier(const char **strp, struct perf_event_attr *attr)
{
const char *str = *strp;
struct perf_evsel *evsel;
int exclude = 0, exclude_GH = 0;
int eu = 0, ek = 0, eh = 0, eH = 0, eG = 0, precise = 0;
if (!*str)
if (str == NULL)
return 0;
if (*str == ',')
return 0;
if (*str++ != ':')
return -1;
while (*str) {
if (*str == 'u') {
if (!exclude)
@ -775,111 +638,60 @@ parse_event_modifier(const char **strp, struct perf_event_attr *attr)
++str;
}
if (str < *strp + 2)
return -1;
*strp = str;
/*
* precise ip:
*
* 0 - SAMPLE_IP can have arbitrary skid
* 1 - SAMPLE_IP must have constant skid
* 2 - SAMPLE_IP requested to have 0 skid
* 3 - SAMPLE_IP must have 0 skid
*
* See also PERF_RECORD_MISC_EXACT_IP
*/
if (precise > 3)
return -EINVAL;
attr->exclude_user = eu;
attr->exclude_kernel = ek;
attr->exclude_hv = eh;
attr->precise_ip = precise;
attr->exclude_host = eH;
attr->exclude_guest = eG;
list_for_each_entry(evsel, list, node) {
evsel->attr.exclude_user = eu;
evsel->attr.exclude_kernel = ek;
evsel->attr.exclude_hv = eh;
evsel->attr.precise_ip = precise;
evsel->attr.exclude_host = eH;
evsel->attr.exclude_guest = eG;
}
return 0;
}
/*
* Each event can have multiple symbolic names.
* Symbolic names are (almost) exactly matched.
*/
static enum event_result
parse_event_symbols(struct perf_evlist *evlist, const char **str,
struct perf_event_attr *attr)
int parse_events(struct perf_evlist *evlist, const char *str, int unset __used)
{
enum event_result ret;
struct perf_evsel *evsel, *h;
LIST_HEAD(list);
YY_BUFFER_STATE buffer;
int ret, idx = evlist->nr_entries;
ret = parse_tracepoint_event(evlist, str, attr);
if (ret != EVT_FAILED)
goto modifier;
buffer = parse_events__scan_string(str);
ret = parse_raw_event(str, attr);
if (ret != EVT_FAILED)
goto modifier;
ret = parse_events_parse(&list, &idx);
ret = parse_numeric_event(str, attr);
if (ret != EVT_FAILED)
goto modifier;
parse_events__flush_buffer(buffer);
parse_events__delete_buffer(buffer);
ret = parse_symbolic_event(str, attr);
if (ret != EVT_FAILED)
goto modifier;
if (!ret) {
int entries = idx - evlist->nr_entries;
perf_evlist__splice_list_tail(evlist, &list, entries);
return 0;
}
ret = parse_generic_hw_event(str, attr);
if (ret != EVT_FAILED)
goto modifier;
list_for_each_entry_safe(evsel, h, &list, node)
perf_evsel__delete(evsel);
ret = parse_breakpoint_event(str, attr);
if (ret != EVT_FAILED)
goto modifier;
fprintf(stderr, "invalid or unsupported event: '%s'\n", *str);
fprintf(stderr, "invalid or unsupported event: '%s'\n", str);
fprintf(stderr, "Run 'perf list' for a list of valid events\n");
return EVT_FAILED;
modifier:
if (parse_event_modifier(str, attr) < 0) {
fprintf(stderr, "invalid event modifier: '%s'\n", *str);
fprintf(stderr, "Run 'perf list' for a list of valid events and modifiers\n");
return EVT_FAILED;
}
return ret;
}
int parse_events(struct perf_evlist *evlist , const char *str, int unset __used)
{
struct perf_event_attr attr;
enum event_result ret;
const char *ostr;
for (;;) {
ostr = str;
memset(&attr, 0, sizeof(attr));
event_attr_init(&attr);
ret = parse_event_symbols(evlist, &str, &attr);
if (ret == EVT_FAILED)
return -1;
if (!(*str == 0 || *str == ',' || isspace(*str)))
return -1;
if (ret != EVT_HANDLED_ALL) {
struct perf_evsel *evsel;
evsel = perf_evsel__new(&attr, evlist->nr_entries);
if (evsel == NULL)
return -1;
perf_evlist__add(evlist, evsel);
evsel->name = calloc(str - ostr + 1, 1);
if (!evsel->name)
return -1;
strncpy(evsel->name, ostr, str - ostr);
}
if (*str == 0)
break;
if (*str == ',')
++str;
while (isspace(*str))
++str;
}
return 0;
}
int parse_events_option(const struct option *opt, const char *str,
int unset __used)
{
@ -1052,8 +864,6 @@ int print_hwcache_events(const char *event_glob)
return printed;
}
#define MAX_NAME_LEN 100
/*
* Print the help text for the event symbols:
*/

View File

@ -33,6 +33,21 @@ extern int parse_filter(const struct option *opt, const char *str, int unset);
#define EVENTS_HELP_MAX (128*1024)
int parse_events_modifier(struct list_head *list __used, char *str __used);
int parse_events_add_tracepoint(struct list_head *list, int *idx,
char *sys, char *event);
int parse_events_add_raw(struct perf_evlist *evlist, unsigned long config,
unsigned long config1, unsigned long config2,
char *mod);
int parse_events_add_numeric(struct list_head *list, int *idx,
unsigned long type, unsigned long config);
int parse_events_add_cache(struct list_head *list, int *idx,
char *type, char *op_result1, char *op_result2);
int parse_events_add_breakpoint(struct list_head *list, int *idx,
void *ptr, char *type);
void parse_events_error(struct list_head *list, int *idx,
char const *msg);
void print_events(const char *event_glob);
void print_events_type(u8 type);
void print_tracepoint_events(const char *subsys_glob, const char *event_glob);

View File

@ -0,0 +1,107 @@
%option prefix="parse_events_"
%{
#include <errno.h>
#include "../perf.h"
#include "parse-events-bison.h"
static int __value(char *str, int base, int token)
{
long num;
errno = 0;
num = strtoul(str, NULL, base);
if (errno)
return PE_ERROR;
parse_events_lval.num = num;
return token;
}
static int value(int base)
{
return __value(parse_events_text, base, PE_VALUE);
}
static int raw(void)
{
return __value(parse_events_text + 1, 16, PE_RAW);
}
static int str(int token)
{
parse_events_lval.str = strdup(parse_events_text);
return token;
}
static int sym(int type, int config)
{
parse_events_lval.num = (type << 16) + config;
return PE_VALUE_SYM;
}
%}
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_*?]*
modifier_event [ukhp]{1,5}
modifier_bp [rwx]
%%
cpu-cycles|cycles { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES); }
stalled-cycles-frontend|idle-cycles-frontend { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_FRONTEND); }
stalled-cycles-backend|idle-cycles-backend { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_BACKEND); }
instructions { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_INSTRUCTIONS); }
cache-references { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_REFERENCES); }
cache-misses { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_MISSES); }
branch-instructions|branches { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_BRANCH_INSTRUCTIONS); }
branch-misses { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_BRANCH_MISSES); }
bus-cycles { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_BUS_CYCLES); }
cpu-clock { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_CLOCK); }
task-clock { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_TASK_CLOCK); }
page-faults|faults { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_PAGE_FAULTS); }
minor-faults { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_PAGE_FAULTS_MIN); }
major-faults { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_PAGE_FAULTS_MAJ); }
context-switches|cs { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CONTEXT_SWITCHES); }
cpu-migrations|migrations { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_MIGRATIONS); }
alignment-faults { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_ALIGNMENT_FAULTS); }
emulation-faults { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_EMULATION_FAULTS); }
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 { return str(PE_NAME_CACHE_TYPE); }
load|loads|read |
store|stores|write |
prefetch|prefetches |
speculative-read|speculative-load |
refs|Reference|ops|access |
misses|miss { return str(PE_NAME_CACHE_OP_RESULT); }
mem: { return PE_PREFIX_MEM; }
r{num_raw_hex} { return raw(); }
{num_dec} { return value(10); }
{num_hex} { return value(16); }
{modifier_event} { return str(PE_MODIFIER_EVENT); }
{modifier_bp} { return str(PE_MODIFIER_BP); }
{name} { return str(PE_NAME); }
"/" { return '/'; }
- { return '-'; }
, { return ','; }
: { return ':'; }
= { return '='; }
%%
int parse_events_wrap(void)
{
return 1;
}

View File

@ -0,0 +1,127 @@
%name-prefix "parse_events_"
%parse-param {struct list_head *list}
%parse-param {int *idx}
%{
#define YYDEBUG 1
#include <linux/compiler.h>
#include <linux/list.h>
#include "types.h"
#include "util.h"
#include "parse-events.h"
extern int parse_events_lex (void);
#define ABORT_ON(val) \
do { \
if (val) \
YYABORT; \
} while (0)
%}
%token PE_VALUE PE_VALUE_SYM PE_RAW
%token PE_NAME
%token PE_MODIFIER_EVENT PE_MODIFIER_BP
%token PE_NAME_CACHE_TYPE PE_NAME_CACHE_OP_RESULT
%token PE_PREFIX_MEM PE_PREFIX_RAW
%token PE_ERROR
%type <num> PE_VALUE
%type <num> PE_VALUE_SYM
%type <num> PE_RAW
%type <str> PE_NAME
%type <str> PE_NAME_CACHE_TYPE
%type <str> PE_NAME_CACHE_OP_RESULT
%type <str> PE_MODIFIER_EVENT
%type <str> PE_MODIFIER_BP
%union
{
char *str;
unsigned long num;
}
%%
events:
events ',' event | event
event:
event_def PE_MODIFIER_EVENT
{
ABORT_ON(parse_events_modifier(list, $2));
}
|
event_def
event_def: event_legacy_symbol sep_dc |
event_legacy_cache sep_dc |
event_legacy_mem |
event_legacy_tracepoint sep_dc |
event_legacy_numeric sep_dc |
event_legacy_raw sep_dc
event_legacy_symbol:
PE_VALUE_SYM
{
int type = $1 >> 16;
int config = $1 & 255;
ABORT_ON(parse_events_add_numeric(list, idx, type, config));
}
event_legacy_cache:
PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT '-' PE_NAME_CACHE_OP_RESULT
{
ABORT_ON(parse_events_add_cache(list, idx, $1, $3, $5));
}
|
PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT
{
ABORT_ON(parse_events_add_cache(list, idx, $1, $3, NULL));
}
|
PE_NAME_CACHE_TYPE
{
ABORT_ON(parse_events_add_cache(list, idx, $1, NULL, NULL));
}
event_legacy_mem:
PE_PREFIX_MEM PE_VALUE ':' PE_MODIFIER_BP sep_dc
{
ABORT_ON(parse_events_add_breakpoint(list, idx, (void *) $2, $4));
}
|
PE_PREFIX_MEM PE_VALUE sep_dc
{
ABORT_ON(parse_events_add_breakpoint(list, idx, (void *) $2, NULL));
}
event_legacy_tracepoint:
PE_NAME ':' PE_NAME
{
ABORT_ON(parse_events_add_tracepoint(list, idx, $1, $3));
}
event_legacy_numeric:
PE_VALUE ':' PE_VALUE
{
ABORT_ON(parse_events_add_numeric(list, idx, $1, $3));
}
event_legacy_raw:
PE_RAW
{
ABORT_ON(parse_events_add_numeric(list, idx, PERF_TYPE_RAW, $1));
}
sep_dc: ':' |
%%
void parse_events_error(struct list_head *list __used, int *idx __used,
char const *msg __used)
{
}