perf tools: Add perf pmu object to access pmu format definition
Adding pmu object which provides interface to pmu's sysfs event format definition located at: ${sysfs_mount}/bus/event_source/devices/${pmu}/format Following interface is exported: struct perf_pmu* perf_pmu__find(char *name); - this function returns pmu object, which is then passed as a handle to other interface functions int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, struct list_head *head_terms); - this function configures perf_event_attr struct based on pmu's format definitions and config terms data, containined in head_terms list. Parser generator is used to retrive the pmu's format definition. The generated parser is part of the patch. Added makefile rule 'pmu-parser' to generate the parser code out of the bison/flex sources. Added builtin test 'Test perf pmu format parsing', which could be run like: perf test pmu Acked-by: Peter Zijlstra <peterz@infradead.org> Signed-off-by: Jiri Olsa <jolsa@redhat.com> Link: http://lkml.kernel.org/n/tip-errz96u1668gj9wlop1zhpht@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
parent
8f707d843c
commit
cd82a32e99
|
@ -276,6 +276,7 @@ LIB_H += util/build-id.h
|
|||
LIB_H += util/debug.h
|
||||
LIB_H += util/debugfs.h
|
||||
LIB_H += util/sysfs.h
|
||||
LIB_H += util/pmu.h
|
||||
LIB_H += util/event.h
|
||||
LIB_H += util/evsel.h
|
||||
LIB_H += util/evlist.h
|
||||
|
@ -323,6 +324,7 @@ LIB_OBJS += $(OUTPUT)util/config.o
|
|||
LIB_OBJS += $(OUTPUT)util/ctype.o
|
||||
LIB_OBJS += $(OUTPUT)util/debugfs.o
|
||||
LIB_OBJS += $(OUTPUT)util/sysfs.o
|
||||
LIB_OBJS += $(OUTPUT)util/pmu.o
|
||||
LIB_OBJS += $(OUTPUT)util/environment.o
|
||||
LIB_OBJS += $(OUTPUT)util/event.o
|
||||
LIB_OBJS += $(OUTPUT)util/evlist.o
|
||||
|
@ -361,6 +363,8 @@ 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/pmu-flex.o
|
||||
LIB_OBJS += $(OUTPUT)util/pmu-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
|
||||
|
@ -773,6 +777,9 @@ $(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS
|
|||
$(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/pmu-flex.o: util/pmu-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 $<
|
||||
|
||||
|
@ -810,6 +817,7 @@ help:
|
|||
@echo ' info - make GNU info documentation (access with info <foo>)'
|
||||
@echo ' pdf - make pdf documentation'
|
||||
@echo ' event-parser - make event parser code'
|
||||
@echo ' pmu-parser - make pmu format 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'
|
||||
|
@ -863,6 +871,10 @@ 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
|
||||
|
||||
pmu-parser:
|
||||
$(QUIET_BISON)$(BISON) -v util/pmu.y -d -o util/pmu-bison.c
|
||||
$(QUIET_FLEX)$(FLEX) --header-file=util/pmu-flex.h -t util/pmu.l > util/pmu-flex.c
|
||||
|
||||
### Detect prefix changes
|
||||
TRACK_CFLAGS = $(subst ','\'',$(ALL_CFLAGS)):\
|
||||
$(bindir_SQ):$(perfexecdir_SQ):$(template_dir_SQ):$(prefix_SQ)
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "util/parse-events.h"
|
||||
#include "util/symbol.h"
|
||||
#include "util/thread_map.h"
|
||||
#include "util/pmu.h"
|
||||
#include "../../include/linux/hw_breakpoint.h"
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
@ -1484,6 +1485,11 @@ static int test__rdpmc(void)
|
|||
|
||||
#endif
|
||||
|
||||
static int test__perf_pmu(void)
|
||||
{
|
||||
return perf_pmu__test();
|
||||
}
|
||||
|
||||
static struct test {
|
||||
const char *desc;
|
||||
int (*func)(void);
|
||||
|
@ -1518,6 +1524,10 @@ static struct test {
|
|||
.desc = "Validate PERF_RECORD_* events & perf_sample fields",
|
||||
.func = test__PERF_RECORD,
|
||||
},
|
||||
{
|
||||
.desc = "Test perf pmu format parsing",
|
||||
.func = test__perf_pmu,
|
||||
},
|
||||
{
|
||||
.func = NULL,
|
||||
},
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,73 @@
|
|||
/* 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 {
|
||||
PP_CONFIG = 258,
|
||||
PP_CONFIG1 = 259,
|
||||
PP_CONFIG2 = 260,
|
||||
PP_VALUE = 261,
|
||||
PP_ERROR = 262
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
|
||||
typedef union YYSTYPE
|
||||
{
|
||||
|
||||
/* Line 1685 of yacc.c */
|
||||
#line 31 "util/pmu.y"
|
||||
|
||||
unsigned long num;
|
||||
DECLARE_BITMAP(bits, PERF_PMU_FORMAT_BITS);
|
||||
|
||||
|
||||
|
||||
/* Line 1685 of yacc.c */
|
||||
#line 65 "util/pmu-bison.h"
|
||||
} YYSTYPE;
|
||||
# define YYSTYPE_IS_TRIVIAL 1
|
||||
# define yystype YYSTYPE /* obsolescent; will be withdrawn */
|
||||
# define YYSTYPE_IS_DECLARED 1
|
||||
#endif
|
||||
|
||||
extern YYSTYPE perf_pmu_lval;
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,316 @@
|
|||
#ifndef perf_pmu_HEADER_H
|
||||
#define perf_pmu_HEADER_H 1
|
||||
#define perf_pmu_IN_HEADER 1
|
||||
|
||||
#line 6 "util/pmu-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 perf_pmu_leng;
|
||||
|
||||
extern FILE *perf_pmu_in, *perf_pmu_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 perf_pmu_restart (FILE *input_file );
|
||||
void perf_pmu__switch_to_buffer (YY_BUFFER_STATE new_buffer );
|
||||
YY_BUFFER_STATE perf_pmu__create_buffer (FILE *file,int size );
|
||||
void perf_pmu__delete_buffer (YY_BUFFER_STATE b );
|
||||
void perf_pmu__flush_buffer (YY_BUFFER_STATE b );
|
||||
void perf_pmu_push_buffer_state (YY_BUFFER_STATE new_buffer );
|
||||
void perf_pmu_pop_buffer_state (void );
|
||||
|
||||
YY_BUFFER_STATE perf_pmu__scan_buffer (char *base,yy_size_t size );
|
||||
YY_BUFFER_STATE perf_pmu__scan_string (yyconst char *yy_str );
|
||||
YY_BUFFER_STATE perf_pmu__scan_bytes (yyconst char *bytes,int len );
|
||||
|
||||
void *perf_pmu_alloc (yy_size_t );
|
||||
void *perf_pmu_realloc (void *,yy_size_t );
|
||||
void perf_pmu_free (void * );
|
||||
|
||||
/* Begin user sect3 */
|
||||
|
||||
extern int perf_pmu_lineno;
|
||||
|
||||
extern char *perf_pmu_text;
|
||||
#define yytext_ptr perf_pmu_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 perf_pmu_lex_destroy (void );
|
||||
|
||||
int perf_pmu_get_debug (void );
|
||||
|
||||
void perf_pmu_set_debug (int debug_flag );
|
||||
|
||||
YY_EXTRA_TYPE perf_pmu_get_extra (void );
|
||||
|
||||
void perf_pmu_set_extra (YY_EXTRA_TYPE user_defined );
|
||||
|
||||
FILE *perf_pmu_get_in (void );
|
||||
|
||||
void perf_pmu_set_in (FILE * in_str );
|
||||
|
||||
FILE *perf_pmu_get_out (void );
|
||||
|
||||
void perf_pmu_set_out (FILE * out_str );
|
||||
|
||||
int perf_pmu_get_leng (void );
|
||||
|
||||
char *perf_pmu_get_text (void );
|
||||
|
||||
int perf_pmu_get_lineno (void );
|
||||
|
||||
void perf_pmu_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 perf_pmu_wrap (void );
|
||||
#else
|
||||
extern int perf_pmu_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 perf_pmu_lex (void);
|
||||
|
||||
#define YY_DECL int perf_pmu_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 38 "util/pmu.l"
|
||||
|
||||
|
||||
#line 315 "util/pmu-flex.h"
|
||||
#undef perf_pmu_IN_HEADER
|
||||
#endif /* perf_pmu_HEADER_H */
|
|
@ -0,0 +1,469 @@
|
|||
|
||||
#include <linux/list.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <dirent.h>
|
||||
#include "sysfs.h"
|
||||
#include "util.h"
|
||||
#include "pmu.h"
|
||||
#include "parse-events.h"
|
||||
|
||||
int perf_pmu_parse(struct list_head *list, char *name);
|
||||
extern FILE *perf_pmu_in;
|
||||
|
||||
static LIST_HEAD(pmus);
|
||||
|
||||
/*
|
||||
* Parse & process all the sysfs attributes located under
|
||||
* the directory specified in 'dir' parameter.
|
||||
*/
|
||||
static int pmu_format_parse(char *dir, struct list_head *head)
|
||||
{
|
||||
struct dirent *evt_ent;
|
||||
DIR *format_dir;
|
||||
int ret = 0;
|
||||
|
||||
format_dir = opendir(dir);
|
||||
if (!format_dir)
|
||||
return -EINVAL;
|
||||
|
||||
while (!ret && (evt_ent = readdir(format_dir))) {
|
||||
char path[PATH_MAX];
|
||||
char *name = evt_ent->d_name;
|
||||
FILE *file;
|
||||
|
||||
if (!strcmp(name, ".") || !strcmp(name, ".."))
|
||||
continue;
|
||||
|
||||
snprintf(path, PATH_MAX, "%s/%s", dir, name);
|
||||
|
||||
ret = -EINVAL;
|
||||
file = fopen(path, "r");
|
||||
if (!file)
|
||||
break;
|
||||
|
||||
perf_pmu_in = file;
|
||||
ret = perf_pmu_parse(head, name);
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
closedir(format_dir);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reading/parsing the default pmu format definition, which should be
|
||||
* located at:
|
||||
* /sys/bus/event_source/devices/<dev>/format as sysfs group attributes.
|
||||
*/
|
||||
static int pmu_format(char *name, struct list_head *format)
|
||||
{
|
||||
struct stat st;
|
||||
char path[PATH_MAX];
|
||||
const char *sysfs;
|
||||
|
||||
sysfs = sysfs_find_mountpoint();
|
||||
if (!sysfs)
|
||||
return -1;
|
||||
|
||||
snprintf(path, PATH_MAX,
|
||||
"%s/bus/event_source/devices/%s/format", sysfs, name);
|
||||
|
||||
if (stat(path, &st) < 0)
|
||||
return -1;
|
||||
|
||||
if (pmu_format_parse(path, format))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reading/parsing the default pmu type value, which should be
|
||||
* located at:
|
||||
* /sys/bus/event_source/devices/<dev>/type as sysfs attribute.
|
||||
*/
|
||||
static int pmu_type(char *name, __u32 *type)
|
||||
{
|
||||
struct stat st;
|
||||
char path[PATH_MAX];
|
||||
const char *sysfs;
|
||||
FILE *file;
|
||||
int ret = 0;
|
||||
|
||||
sysfs = sysfs_find_mountpoint();
|
||||
if (!sysfs)
|
||||
return -1;
|
||||
|
||||
snprintf(path, PATH_MAX,
|
||||
"%s/bus/event_source/devices/%s/type", sysfs, name);
|
||||
|
||||
if (stat(path, &st) < 0)
|
||||
return -1;
|
||||
|
||||
file = fopen(path, "r");
|
||||
if (!file)
|
||||
return -EINVAL;
|
||||
|
||||
if (1 != fscanf(file, "%u", type))
|
||||
ret = -1;
|
||||
|
||||
fclose(file);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct perf_pmu *pmu_lookup(char *name)
|
||||
{
|
||||
struct perf_pmu *pmu;
|
||||
LIST_HEAD(format);
|
||||
__u32 type;
|
||||
|
||||
/*
|
||||
* The pmu data we store & need consists of the pmu
|
||||
* type value and format definitions. Load both right
|
||||
* now.
|
||||
*/
|
||||
if (pmu_format(name, &format))
|
||||
return NULL;
|
||||
|
||||
if (pmu_type(name, &type))
|
||||
return NULL;
|
||||
|
||||
pmu = zalloc(sizeof(*pmu));
|
||||
if (!pmu)
|
||||
return NULL;
|
||||
|
||||
INIT_LIST_HEAD(&pmu->format);
|
||||
list_splice(&format, &pmu->format);
|
||||
pmu->name = strdup(name);
|
||||
pmu->type = type;
|
||||
return pmu;
|
||||
}
|
||||
|
||||
static struct perf_pmu *pmu_find(char *name)
|
||||
{
|
||||
struct perf_pmu *pmu;
|
||||
|
||||
list_for_each_entry(pmu, &pmus, list)
|
||||
if (!strcmp(pmu->name, name))
|
||||
return pmu;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct perf_pmu *perf_pmu__find(char *name)
|
||||
{
|
||||
struct perf_pmu *pmu;
|
||||
|
||||
/*
|
||||
* Once PMU is loaded it stays in the list,
|
||||
* so we keep us from multiple reading/parsing
|
||||
* the pmu format definitions.
|
||||
*/
|
||||
pmu = pmu_find(name);
|
||||
if (pmu)
|
||||
return pmu;
|
||||
|
||||
return pmu_lookup(name);
|
||||
}
|
||||
|
||||
static struct perf_pmu__format*
|
||||
pmu_find_format(struct list_head *formats, char *name)
|
||||
{
|
||||
struct perf_pmu__format *format;
|
||||
|
||||
list_for_each_entry(format, formats, list)
|
||||
if (!strcmp(format->name, name))
|
||||
return format;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns value based on the format definition (format parameter)
|
||||
* and unformated value (value parameter).
|
||||
*
|
||||
* TODO maybe optimize a little ;)
|
||||
*/
|
||||
static __u64 pmu_format_value(unsigned long *format, __u64 value)
|
||||
{
|
||||
unsigned long fbit, vbit;
|
||||
__u64 v = 0;
|
||||
|
||||
for (fbit = 0, vbit = 0; fbit < PERF_PMU_FORMAT_BITS; fbit++) {
|
||||
|
||||
if (!test_bit(fbit, format))
|
||||
continue;
|
||||
|
||||
if (!(value & (1llu << vbit++)))
|
||||
continue;
|
||||
|
||||
v |= (1llu << fbit);
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup one of config[12] attr members based on the
|
||||
* user input data - temr parameter.
|
||||
*/
|
||||
static int pmu_config_term(struct list_head *formats,
|
||||
struct perf_event_attr *attr,
|
||||
struct parse_events__term *term)
|
||||
{
|
||||
struct perf_pmu__format *format;
|
||||
__u64 *vp;
|
||||
|
||||
/*
|
||||
* Support only for hardcoded and numnerial terms.
|
||||
* Hardcoded terms should be already in, so nothing
|
||||
* to be done for them.
|
||||
*/
|
||||
if (parse_events__is_hardcoded_term(term))
|
||||
return 0;
|
||||
|
||||
if (term->type != PARSE_EVENTS__TERM_TYPE_NUM)
|
||||
return -EINVAL;
|
||||
|
||||
format = pmu_find_format(formats, term->config);
|
||||
if (!format)
|
||||
return -EINVAL;
|
||||
|
||||
switch (format->value) {
|
||||
case PERF_PMU_FORMAT_VALUE_CONFIG:
|
||||
vp = &attr->config;
|
||||
break;
|
||||
case PERF_PMU_FORMAT_VALUE_CONFIG1:
|
||||
vp = &attr->config1;
|
||||
break;
|
||||
case PERF_PMU_FORMAT_VALUE_CONFIG2:
|
||||
vp = &attr->config2;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*vp |= pmu_format_value(format->bits, term->val.num);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pmu_config(struct list_head *formats, struct perf_event_attr *attr,
|
||||
struct list_head *head_terms)
|
||||
{
|
||||
struct parse_events__term *term, *h;
|
||||
|
||||
list_for_each_entry_safe(term, h, head_terms, list)
|
||||
if (pmu_config_term(formats, attr, term))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Configures event's 'attr' parameter based on the:
|
||||
* 1) users input - specified in terms parameter
|
||||
* 2) pmu format definitions - specified by pmu parameter
|
||||
*/
|
||||
int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
|
||||
struct list_head *head_terms)
|
||||
{
|
||||
attr->type = pmu->type;
|
||||
return pmu_config(&pmu->format, attr, head_terms);
|
||||
}
|
||||
|
||||
int perf_pmu__new_format(struct list_head *list, char *name,
|
||||
int config, unsigned long *bits)
|
||||
{
|
||||
struct perf_pmu__format *format;
|
||||
|
||||
format = zalloc(sizeof(*format));
|
||||
if (!format)
|
||||
return -ENOMEM;
|
||||
|
||||
format->name = strdup(name);
|
||||
format->value = config;
|
||||
memcpy(format->bits, bits, sizeof(format->bits));
|
||||
|
||||
list_add_tail(&format->list, list);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void perf_pmu__set_format(unsigned long *bits, long from, long to)
|
||||
{
|
||||
long b;
|
||||
|
||||
if (!to)
|
||||
to = from;
|
||||
|
||||
memset(bits, 0, BITS_TO_LONGS(PERF_PMU_FORMAT_BITS));
|
||||
for (b = from; b <= to; b++)
|
||||
set_bit(b, bits);
|
||||
}
|
||||
|
||||
/* Simulated format definitions. */
|
||||
static struct test_format {
|
||||
const char *name;
|
||||
const char *value;
|
||||
} test_formats[] = {
|
||||
{ "krava01", "config:0-1,62-63\n", },
|
||||
{ "krava02", "config:10-17\n", },
|
||||
{ "krava03", "config:5\n", },
|
||||
{ "krava11", "config1:0,2,4,6,8,20-28\n", },
|
||||
{ "krava12", "config1:63\n", },
|
||||
{ "krava13", "config1:45-47\n", },
|
||||
{ "krava21", "config2:0-3,10-13,20-23,30-33,40-43,50-53,60-63\n", },
|
||||
{ "krava22", "config2:8,18,48,58\n", },
|
||||
{ "krava23", "config2:28-29,38\n", },
|
||||
};
|
||||
|
||||
#define TEST_FORMATS_CNT (sizeof(test_formats) / sizeof(struct test_format))
|
||||
|
||||
/* Simulated users input. */
|
||||
static struct parse_events__term test_terms[] = {
|
||||
{
|
||||
.config = (char *) "krava01",
|
||||
.val.num = 15,
|
||||
.type = PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
},
|
||||
{
|
||||
.config = (char *) "krava02",
|
||||
.val.num = 170,
|
||||
.type = PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
},
|
||||
{
|
||||
.config = (char *) "krava03",
|
||||
.val.num = 1,
|
||||
.type = PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
},
|
||||
{
|
||||
.config = (char *) "krava11",
|
||||
.val.num = 27,
|
||||
.type = PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
},
|
||||
{
|
||||
.config = (char *) "krava12",
|
||||
.val.num = 1,
|
||||
.type = PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
},
|
||||
{
|
||||
.config = (char *) "krava13",
|
||||
.val.num = 2,
|
||||
.type = PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
},
|
||||
{
|
||||
.config = (char *) "krava21",
|
||||
.val.num = 119,
|
||||
.type = PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
},
|
||||
{
|
||||
.config = (char *) "krava22",
|
||||
.val.num = 11,
|
||||
.type = PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
},
|
||||
{
|
||||
.config = (char *) "krava23",
|
||||
.val.num = 2,
|
||||
.type = PARSE_EVENTS__TERM_TYPE_NUM,
|
||||
},
|
||||
};
|
||||
#define TERMS_CNT (sizeof(test_terms) / sizeof(struct parse_events__term))
|
||||
|
||||
/*
|
||||
* Prepare format directory data, exported by kernel
|
||||
* at /sys/bus/event_source/devices/<dev>/format.
|
||||
*/
|
||||
static char *test_format_dir_get(void)
|
||||
{
|
||||
static char dir[PATH_MAX];
|
||||
unsigned int i;
|
||||
|
||||
snprintf(dir, PATH_MAX, "/tmp/perf-pmu-test-format-XXXXXX");
|
||||
if (!mkdtemp(dir))
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < TEST_FORMATS_CNT; i++) {
|
||||
static char name[PATH_MAX];
|
||||
struct test_format *format = &test_formats[i];
|
||||
FILE *file;
|
||||
|
||||
snprintf(name, PATH_MAX, "%s/%s", dir, format->name);
|
||||
|
||||
file = fopen(name, "w");
|
||||
if (!file)
|
||||
return NULL;
|
||||
|
||||
if (1 != fwrite(format->value, strlen(format->value), 1, file))
|
||||
break;
|
||||
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
return dir;
|
||||
}
|
||||
|
||||
/* Cleanup format directory. */
|
||||
static int test_format_dir_put(char *dir)
|
||||
{
|
||||
char buf[PATH_MAX];
|
||||
snprintf(buf, PATH_MAX, "rm -f %s/*\n", dir);
|
||||
if (system(buf))
|
||||
return -1;
|
||||
|
||||
snprintf(buf, PATH_MAX, "rmdir %s\n", dir);
|
||||
return system(buf);
|
||||
}
|
||||
|
||||
static struct list_head *test_terms_list(void)
|
||||
{
|
||||
static LIST_HEAD(terms);
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < TERMS_CNT; i++)
|
||||
list_add_tail(&test_terms[i].list, &terms);
|
||||
|
||||
return &terms;
|
||||
}
|
||||
|
||||
#undef TERMS_CNT
|
||||
|
||||
int perf_pmu__test(void)
|
||||
{
|
||||
char *format = test_format_dir_get();
|
||||
LIST_HEAD(formats);
|
||||
struct list_head *terms = test_terms_list();
|
||||
int ret;
|
||||
|
||||
if (!format)
|
||||
return -EINVAL;
|
||||
|
||||
do {
|
||||
struct perf_event_attr attr;
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
|
||||
ret = pmu_format_parse(format, &formats);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
ret = pmu_config(&formats, &attr, terms);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
ret = -EINVAL;
|
||||
|
||||
if (attr.config != 0xc00000000002a823)
|
||||
break;
|
||||
if (attr.config1 != 0x8000400000000145)
|
||||
break;
|
||||
if (attr.config2 != 0x0400000020041d07)
|
||||
break;
|
||||
|
||||
ret = 0;
|
||||
} while (0);
|
||||
|
||||
test_format_dir_put(format);
|
||||
return ret;
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
#ifndef __PMU_H
|
||||
#define __PMU_H
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include "../../../include/linux/perf_event.h"
|
||||
|
||||
enum {
|
||||
PERF_PMU_FORMAT_VALUE_CONFIG,
|
||||
PERF_PMU_FORMAT_VALUE_CONFIG1,
|
||||
PERF_PMU_FORMAT_VALUE_CONFIG2,
|
||||
};
|
||||
|
||||
#define PERF_PMU_FORMAT_BITS 64
|
||||
|
||||
struct perf_pmu__format {
|
||||
char *name;
|
||||
int value;
|
||||
DECLARE_BITMAP(bits, PERF_PMU_FORMAT_BITS);
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
struct perf_pmu {
|
||||
char *name;
|
||||
__u32 type;
|
||||
struct list_head format;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
struct perf_pmu *perf_pmu__find(char *name);
|
||||
int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
|
||||
struct list_head *head_terms);
|
||||
|
||||
int perf_pmu_wrap(void);
|
||||
void perf_pmu_error(struct list_head *list, char *name, char const *msg);
|
||||
|
||||
int perf_pmu__new_format(struct list_head *list, char *name,
|
||||
int config, unsigned long *bits);
|
||||
void perf_pmu__set_format(unsigned long *bits, long from, long to);
|
||||
|
||||
int perf_pmu__test(void);
|
||||
#endif /* __PMU_H */
|
|
@ -0,0 +1,43 @@
|
|||
%option prefix="perf_pmu_"
|
||||
|
||||
%{
|
||||
#include <stdlib.h>
|
||||
#include <linux/bitops.h>
|
||||
#include "pmu.h"
|
||||
#include "pmu-bison.h"
|
||||
|
||||
static int value(int base)
|
||||
{
|
||||
long num;
|
||||
|
||||
errno = 0;
|
||||
num = strtoul(perf_pmu_text, NULL, base);
|
||||
if (errno)
|
||||
return PP_ERROR;
|
||||
|
||||
perf_pmu_lval.num = num;
|
||||
return PP_VALUE;
|
||||
}
|
||||
|
||||
%}
|
||||
|
||||
num_dec [0-9]+
|
||||
|
||||
%%
|
||||
|
||||
{num_dec} { return value(10); }
|
||||
config { return PP_CONFIG; }
|
||||
config1 { return PP_CONFIG1; }
|
||||
config2 { return PP_CONFIG2; }
|
||||
- { return '-'; }
|
||||
: { return ':'; }
|
||||
, { return ','; }
|
||||
. { ; }
|
||||
\n { ; }
|
||||
|
||||
%%
|
||||
|
||||
int perf_pmu_wrap(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
|
||||
%name-prefix "perf_pmu_"
|
||||
%parse-param {struct list_head *format}
|
||||
%parse-param {char *name}
|
||||
|
||||
%{
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/bitmap.h>
|
||||
#include <string.h>
|
||||
#include "pmu.h"
|
||||
|
||||
extern int perf_pmu_lex (void);
|
||||
|
||||
#define ABORT_ON(val) \
|
||||
do { \
|
||||
if (val) \
|
||||
YYABORT; \
|
||||
} while (0)
|
||||
|
||||
%}
|
||||
|
||||
%token PP_CONFIG PP_CONFIG1 PP_CONFIG2
|
||||
%token PP_VALUE PP_ERROR
|
||||
%type <num> PP_VALUE
|
||||
%type <bits> bit_term
|
||||
%type <bits> bits
|
||||
|
||||
%union
|
||||
{
|
||||
unsigned long num;
|
||||
DECLARE_BITMAP(bits, PERF_PMU_FORMAT_BITS);
|
||||
}
|
||||
|
||||
%%
|
||||
|
||||
format:
|
||||
format format_term
|
||||
|
|
||||
format_term
|
||||
|
||||
format_term:
|
||||
PP_CONFIG ':' bits
|
||||
{
|
||||
ABORT_ON(perf_pmu__new_format(format, name,
|
||||
PERF_PMU_FORMAT_VALUE_CONFIG,
|
||||
$3));
|
||||
}
|
||||
|
|
||||
PP_CONFIG1 ':' bits
|
||||
{
|
||||
ABORT_ON(perf_pmu__new_format(format, name,
|
||||
PERF_PMU_FORMAT_VALUE_CONFIG1,
|
||||
$3));
|
||||
}
|
||||
|
|
||||
PP_CONFIG2 ':' bits
|
||||
{
|
||||
ABORT_ON(perf_pmu__new_format(format, name,
|
||||
PERF_PMU_FORMAT_VALUE_CONFIG2,
|
||||
$3));
|
||||
}
|
||||
|
||||
bits:
|
||||
bits ',' bit_term
|
||||
{
|
||||
bitmap_or($$, $1, $3, 64);
|
||||
}
|
||||
|
|
||||
bit_term
|
||||
{
|
||||
memcpy($$, $1, sizeof($1));
|
||||
}
|
||||
|
||||
bit_term:
|
||||
PP_VALUE '-' PP_VALUE
|
||||
{
|
||||
perf_pmu__set_format($$, $1, $3);
|
||||
}
|
||||
|
|
||||
PP_VALUE
|
||||
{
|
||||
perf_pmu__set_format($$, $1, 0);
|
||||
}
|
||||
|
||||
%%
|
||||
|
||||
void perf_pmu_error(struct list_head *list __used,
|
||||
char *name __used,
|
||||
char const *msg __used)
|
||||
{
|
||||
}
|
Loading…
Reference in New Issue