You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
perf tools: Add config options support for event parsing
Adding a new rule to the event grammar to be able to specify
values of additional attributes of symbolic event.
The new syntax for event symbolic definition is:
event_legacy_symbol: PE_NAME_SYM '/' event_config '/' |
PE_NAME_SYM sep_slash_dc
event_config: event_config ',' event_term | event_term
event_term: PE_NAME '=' PE_NAME |
PE_NAME '=' PE_VALUE
PE_NAME
sep_slash_dc: '/' | ':' |
At the moment the config options are hardcoded to be used for legacy
symbol events to define several perf_event_attr fields. It is:
'config' to define perf_event_attr::config
'config1' to define perf_event_attr::config1
'config2' to define perf_event_attr::config2
'period' to define perf_event_attr::sample_period
Legacy events could be now specified as:
cycles/period=100000/
If term is specified without the value assignment, then 1 is
assigned by default.
Acked-by: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: Jiri Olsa <jolsa@redhat.com>
Link: http://lkml.kernel.org/n/tip-mgkavww9790jbt2jdkooyv4q@git.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
committed by
Arnaldo Carvalho de Melo
parent
89812fc81f
commit
8f707d843c
@@ -677,6 +677,24 @@ static int test__checkevent_symbolic_name(struct perf_evlist *evlist)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test__checkevent_symbolic_name_config(struct perf_evlist *evlist)
|
||||
{
|
||||
struct perf_evsel *evsel = list_entry(evlist->entries.next,
|
||||
struct perf_evsel, node);
|
||||
|
||||
TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
|
||||
TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
|
||||
TEST_ASSERT_VAL("wrong config",
|
||||
PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config);
|
||||
TEST_ASSERT_VAL("wrong period",
|
||||
100000 == evsel->attr.sample_period);
|
||||
TEST_ASSERT_VAL("wrong config1",
|
||||
0 == evsel->attr.config1);
|
||||
TEST_ASSERT_VAL("wrong config2",
|
||||
1 == evsel->attr.config2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test__checkevent_symbolic_alias(struct perf_evlist *evlist)
|
||||
{
|
||||
struct perf_evsel *evsel = list_entry(evlist->entries.next,
|
||||
@@ -883,6 +901,10 @@ static struct test__event_st {
|
||||
.name = "instructions",
|
||||
.check = test__checkevent_symbolic_name,
|
||||
},
|
||||
{
|
||||
.name = "cycles/period=100000,config2/",
|
||||
.check = test__checkevent_symbolic_name_config,
|
||||
},
|
||||
{
|
||||
.name = "faults",
|
||||
.check = test__checkevent_symbolic_alias,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -41,14 +41,15 @@
|
||||
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
|
||||
PE_TERM = 261,
|
||||
PE_NAME = 262,
|
||||
PE_MODIFIER_EVENT = 263,
|
||||
PE_MODIFIER_BP = 264,
|
||||
PE_NAME_CACHE_TYPE = 265,
|
||||
PE_NAME_CACHE_OP_RESULT = 266,
|
||||
PE_PREFIX_MEM = 267,
|
||||
PE_PREFIX_RAW = 268,
|
||||
PE_ERROR = 269
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -59,15 +60,17 @@ typedef union YYSTYPE
|
||||
{
|
||||
|
||||
/* Line 1685 of yacc.c */
|
||||
#line 42 "util/parse-events.y"
|
||||
#line 45 "util/parse-events.y"
|
||||
|
||||
char *str;
|
||||
unsigned long num;
|
||||
struct list_head *head;
|
||||
struct parse_events__term *term;
|
||||
|
||||
|
||||
|
||||
/* Line 1685 of yacc.c */
|
||||
#line 71 "util/parse-events-bison.h"
|
||||
#line 74 "util/parse-events-bison.h"
|
||||
} YYSTYPE;
|
||||
# define YYSTYPE_IS_TRIVIAL 1
|
||||
# define yystype YYSTYPE /* obsolescent; will be withdrawn */
|
||||
|
||||
+359
-312
File diff suppressed because it is too large
Load Diff
@@ -308,7 +308,7 @@ extern int parse_events_lex (void);
|
||||
#undef YY_DECL
|
||||
#endif
|
||||
|
||||
#line 102 "util/parse-events.l"
|
||||
#line 121 "util/parse-events.l"
|
||||
|
||||
|
||||
#line 315 "util/parse-events-flex.h"
|
||||
|
||||
@@ -588,15 +588,60 @@ int parse_events_add_breakpoint(struct list_head *list, int *idx,
|
||||
return add_event(list, idx, &attr, name);
|
||||
}
|
||||
|
||||
int
|
||||
parse_events_add_numeric(struct list_head *list, int *idx,
|
||||
unsigned long type, unsigned long config)
|
||||
static int config_term(struct perf_event_attr *attr,
|
||||
struct parse_events__term *term)
|
||||
{
|
||||
switch (term->type) {
|
||||
case PARSE_EVENTS__TERM_TYPE_CONFIG:
|
||||
attr->config = term->val.num;
|
||||
break;
|
||||
case PARSE_EVENTS__TERM_TYPE_CONFIG1:
|
||||
attr->config1 = term->val.num;
|
||||
break;
|
||||
case PARSE_EVENTS__TERM_TYPE_CONFIG2:
|
||||
attr->config2 = term->val.num;
|
||||
break;
|
||||
case PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD:
|
||||
attr->sample_period = term->val.num;
|
||||
break;
|
||||
case PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE:
|
||||
/*
|
||||
* TODO uncomment when the field is available
|
||||
* attr->branch_sample_type = term->val.num;
|
||||
*/
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int config_attr(struct perf_event_attr *attr,
|
||||
struct list_head *head, int fail)
|
||||
{
|
||||
struct parse_events__term *term;
|
||||
|
||||
list_for_each_entry(term, head, list)
|
||||
if (config_term(attr, term) && fail)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int parse_events_add_numeric(struct list_head *list, int *idx,
|
||||
unsigned long type, unsigned long config,
|
||||
struct list_head *head_config)
|
||||
{
|
||||
struct perf_event_attr attr;
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
attr.type = type;
|
||||
attr.config = config;
|
||||
|
||||
if (head_config &&
|
||||
config_attr(&attr, head_config, 1))
|
||||
return -EINVAL;
|
||||
|
||||
return add_event(list, idx, &attr,
|
||||
(char *) __event_name(type, config));
|
||||
}
|
||||
@@ -923,3 +968,51 @@ void print_events(const char *event_glob)
|
||||
|
||||
print_tracepoint_events(NULL, NULL);
|
||||
}
|
||||
|
||||
int parse_events__is_hardcoded_term(struct parse_events__term *term)
|
||||
{
|
||||
return term->type <= PARSE_EVENTS__TERM_TYPE_HARDCODED_MAX;
|
||||
}
|
||||
|
||||
int parse_events__new_term(struct parse_events__term **_term, int type,
|
||||
char *config, char *str, long num)
|
||||
{
|
||||
struct parse_events__term *term;
|
||||
|
||||
term = zalloc(sizeof(*term));
|
||||
if (!term)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_LIST_HEAD(&term->list);
|
||||
term->type = type;
|
||||
term->config = config;
|
||||
|
||||
switch (type) {
|
||||
case PARSE_EVENTS__TERM_TYPE_CONFIG:
|
||||
case PARSE_EVENTS__TERM_TYPE_CONFIG1:
|
||||
case PARSE_EVENTS__TERM_TYPE_CONFIG2:
|
||||
case PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD:
|
||||
case PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE:
|
||||
case PARSE_EVENTS__TERM_TYPE_NUM:
|
||||
term->val.num = num;
|
||||
break;
|
||||
case PARSE_EVENTS__TERM_TYPE_STR:
|
||||
term->val.str = str;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*_term = term;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void parse_events__free_terms(struct list_head *terms)
|
||||
{
|
||||
struct parse_events__term *term, *h;
|
||||
|
||||
list_for_each_entry_safe(term, h, terms, list)
|
||||
free(term);
|
||||
|
||||
free(terms);
|
||||
}
|
||||
|
||||
@@ -33,6 +33,34 @@ extern int parse_filter(const struct option *opt, const char *str, int unset);
|
||||
|
||||
#define EVENTS_HELP_MAX (128*1024)
|
||||
|
||||
enum {
|
||||
PARSE_EVENTS__TERM_TYPE_CONFIG,
|
||||
PARSE_EVENTS__TERM_TYPE_CONFIG1,
|
||||
PARSE_EVENTS__TERM_TYPE_CONFIG2,
|
||||
PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD,
|
||||
PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE,
|
||||
PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
PARSE_EVENTS__TERM_TYPE_STR,
|
||||
|
||||
PARSE_EVENTS__TERM_TYPE_HARDCODED_MAX =
|
||||
PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE,
|
||||
};
|
||||
|
||||
struct parse_events__term {
|
||||
char *config;
|
||||
union {
|
||||
char *str;
|
||||
long num;
|
||||
} val;
|
||||
int type;
|
||||
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
int parse_events__is_hardcoded_term(struct parse_events__term *term);
|
||||
int parse_events__new_term(struct parse_events__term **term, int type,
|
||||
char *config, char *str, long num);
|
||||
void parse_events__free_terms(struct list_head *terms);
|
||||
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);
|
||||
@@ -40,7 +68,8 @@ 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);
|
||||
unsigned long type, unsigned long config,
|
||||
struct list_head *head_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,
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <errno.h>
|
||||
#include "../perf.h"
|
||||
#include "parse-events-bison.h"
|
||||
#include "parse-events.h"
|
||||
|
||||
static int __value(char *str, int base, int token)
|
||||
{
|
||||
@@ -41,6 +42,12 @@ static int sym(int type, int config)
|
||||
return PE_VALUE_SYM;
|
||||
}
|
||||
|
||||
static int term(int type)
|
||||
{
|
||||
parse_events_lval.num = type;
|
||||
return PE_TERM;
|
||||
}
|
||||
|
||||
%}
|
||||
|
||||
num_dec [0-9]+
|
||||
@@ -85,6 +92,18 @@ speculative-read|speculative-load |
|
||||
refs|Reference|ops|access |
|
||||
misses|miss { return str(PE_NAME_CACHE_OP_RESULT); }
|
||||
|
||||
/*
|
||||
* These are event config hardcoded term names to be specified
|
||||
* within xxx/.../ syntax. So far we dont clash with other names,
|
||||
* so we can put them here directly. In case the we have a conflict
|
||||
* in future, this needs to go into '//' condition block.
|
||||
*/
|
||||
config { return term(PARSE_EVENTS__TERM_TYPE_CONFIG); }
|
||||
config1 { return term(PARSE_EVENTS__TERM_TYPE_CONFIG1); }
|
||||
config2 { return term(PARSE_EVENTS__TERM_TYPE_CONFIG2); }
|
||||
period { return term(PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD); }
|
||||
branch_type { return term(PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE); }
|
||||
|
||||
mem: { return PE_PREFIX_MEM; }
|
||||
r{num_raw_hex} { return raw(); }
|
||||
{num_dec} { return value(10); }
|
||||
|
||||
@@ -23,7 +23,7 @@ do { \
|
||||
|
||||
%}
|
||||
|
||||
%token PE_VALUE PE_VALUE_SYM PE_RAW
|
||||
%token PE_VALUE PE_VALUE_SYM PE_RAW PE_TERM
|
||||
%token PE_NAME
|
||||
%token PE_MODIFIER_EVENT PE_MODIFIER_BP
|
||||
%token PE_NAME_CACHE_TYPE PE_NAME_CACHE_OP_RESULT
|
||||
@@ -32,16 +32,21 @@ do { \
|
||||
%type <num> PE_VALUE
|
||||
%type <num> PE_VALUE_SYM
|
||||
%type <num> PE_RAW
|
||||
%type <num> PE_TERM
|
||||
%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
|
||||
%type <head> event_config
|
||||
%type <term> event_term
|
||||
|
||||
%union
|
||||
{
|
||||
char *str;
|
||||
unsigned long num;
|
||||
struct list_head *head;
|
||||
struct parse_events__term *term;
|
||||
}
|
||||
%%
|
||||
|
||||
@@ -56,7 +61,7 @@ event_def PE_MODIFIER_EVENT
|
||||
|
|
||||
event_def
|
||||
|
||||
event_def: event_legacy_symbol sep_dc |
|
||||
event_def: event_legacy_symbol |
|
||||
event_legacy_cache sep_dc |
|
||||
event_legacy_mem |
|
||||
event_legacy_tracepoint sep_dc |
|
||||
@@ -64,12 +69,21 @@ event_def: event_legacy_symbol sep_dc |
|
||||
event_legacy_raw sep_dc
|
||||
|
||||
event_legacy_symbol:
|
||||
PE_VALUE_SYM
|
||||
PE_VALUE_SYM '/' event_config '/'
|
||||
{
|
||||
int type = $1 >> 16;
|
||||
int config = $1 & 255;
|
||||
|
||||
ABORT_ON(parse_events_add_numeric(list, idx, type, config));
|
||||
ABORT_ON(parse_events_add_numeric(list, idx, type, config, $3));
|
||||
parse_events__free_terms($3);
|
||||
}
|
||||
|
|
||||
PE_VALUE_SYM sep_slash_dc
|
||||
{
|
||||
int type = $1 >> 16;
|
||||
int config = $1 & 255;
|
||||
|
||||
ABORT_ON(parse_events_add_numeric(list, idx, type, config, NULL));
|
||||
}
|
||||
|
||||
event_legacy_cache:
|
||||
@@ -108,17 +122,85 @@ PE_NAME ':' PE_NAME
|
||||
event_legacy_numeric:
|
||||
PE_VALUE ':' PE_VALUE
|
||||
{
|
||||
ABORT_ON(parse_events_add_numeric(list, idx, $1, $3));
|
||||
ABORT_ON(parse_events_add_numeric(list, idx, $1, $3, NULL));
|
||||
}
|
||||
|
||||
event_legacy_raw:
|
||||
PE_RAW
|
||||
{
|
||||
ABORT_ON(parse_events_add_numeric(list, idx, PERF_TYPE_RAW, $1));
|
||||
ABORT_ON(parse_events_add_numeric(list, idx, PERF_TYPE_RAW, $1, NULL));
|
||||
}
|
||||
|
||||
event_config:
|
||||
event_config ',' event_term
|
||||
{
|
||||
struct list_head *head = $1;
|
||||
struct parse_events__term *term = $3;
|
||||
|
||||
ABORT_ON(!head);
|
||||
list_add_tail(&term->list, head);
|
||||
$$ = $1;
|
||||
}
|
||||
|
|
||||
event_term
|
||||
{
|
||||
struct list_head *head = malloc(sizeof(*head));
|
||||
struct parse_events__term *term = $1;
|
||||
|
||||
ABORT_ON(!head);
|
||||
INIT_LIST_HEAD(head);
|
||||
list_add_tail(&term->list, head);
|
||||
$$ = head;
|
||||
}
|
||||
|
||||
event_term:
|
||||
PE_NAME '=' PE_NAME
|
||||
{
|
||||
struct parse_events__term *term;
|
||||
|
||||
ABORT_ON(parse_events__new_term(&term, PARSE_EVENTS__TERM_TYPE_STR,
|
||||
$1, $3, 0));
|
||||
$$ = term;
|
||||
}
|
||||
|
|
||||
PE_NAME '=' PE_VALUE
|
||||
{
|
||||
struct parse_events__term *term;
|
||||
|
||||
ABORT_ON(parse_events__new_term(&term, PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
$1, NULL, $3));
|
||||
$$ = term;
|
||||
}
|
||||
|
|
||||
PE_NAME
|
||||
{
|
||||
struct parse_events__term *term;
|
||||
|
||||
ABORT_ON(parse_events__new_term(&term, PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
$1, NULL, 1));
|
||||
$$ = term;
|
||||
}
|
||||
|
|
||||
PE_TERM '=' PE_VALUE
|
||||
{
|
||||
struct parse_events__term *term;
|
||||
|
||||
ABORT_ON(parse_events__new_term(&term, $1, NULL, NULL, $3));
|
||||
$$ = term;
|
||||
}
|
||||
|
|
||||
PE_TERM
|
||||
{
|
||||
struct parse_events__term *term;
|
||||
|
||||
ABORT_ON(parse_events__new_term(&term, $1, NULL, NULL, 1));
|
||||
$$ = term;
|
||||
}
|
||||
|
||||
sep_dc: ':' |
|
||||
|
||||
sep_slash_dc: '/' | ':' |
|
||||
|
||||
%%
|
||||
|
||||
void parse_events_error(struct list_head *list __used, int *idx __used,
|
||||
|
||||
Reference in New Issue
Block a user