2020-02-28 10:36:12 +01:00
// SPDX-License-Identifier: GPL-2.0
2020-02-28 10:36:13 +01:00
# include <stdbool.h>
2020-02-28 10:36:12 +01:00
# include <assert.h>
2020-06-23 10:09:03 -03:00
# include <errno.h>
# include <stdlib.h>
# include <string.h>
2020-07-19 20:13:11 +02:00
# include "metricgroup.h"
# include "debug.h"
2020-02-28 10:36:12 +01:00
# include "expr.h"
2020-02-28 10:36:13 +01:00
# include "expr-bison.h"
# include "expr-flex.h"
2020-05-15 15:17:32 -07:00
# include <linux/kernel.h>
2020-07-19 20:13:11 +02:00
# include <linux/zalloc.h>
# include <ctype.h>
2020-02-28 10:36:13 +01:00
# ifdef PARSER_DEBUG
extern int expr_debug ;
# endif
2020-02-28 10:36:12 +01:00
2020-08-26 08:30:55 -07:00
struct expr_id_data {
union {
double val ;
struct {
double val ;
const char * metric_name ;
const char * metric_expr ;
} ref ;
} ;
enum {
/* Holding a double value. */
EXPR_ID_DATA__VALUE ,
/* Reference to another metric. */
EXPR_ID_DATA__REF ,
/* A reference but the value has been computed. */
EXPR_ID_DATA__REF_VALUE ,
} kind ;
} ;
2020-05-15 15:17:32 -07:00
static size_t key_hash ( const void * key , void * ctx __maybe_unused )
{
const char * str = ( const char * ) key ;
size_t hash = 0 ;
while ( * str ! = ' \0 ' ) {
hash * = 31 ;
hash + = * str ;
str + + ;
}
return hash ;
}
static bool key_equal ( const void * key1 , const void * key2 ,
void * ctx __maybe_unused )
{
return ! strcmp ( ( const char * ) key1 , ( const char * ) key2 ) ;
}
2021-09-23 00:46:11 -07:00
struct hashmap * ids__new ( void )
{
return hashmap__new ( key_hash , key_equal , NULL ) ;
}
void ids__free ( struct hashmap * ids )
{
struct hashmap_entry * cur ;
size_t bkt ;
if ( ids = = NULL )
return ;
hashmap__for_each_entry ( ids , cur , bkt ) {
free ( ( char * ) cur - > key ) ;
free ( cur - > value ) ;
}
hashmap__free ( ids ) ;
}
2021-10-15 10:21:20 -07:00
int ids__insert ( struct hashmap * ids , const char * id )
2020-07-19 20:13:03 +02:00
{
struct expr_id_data * data_ptr = NULL , * old_data = NULL ;
char * old_key = NULL ;
int ret ;
2021-09-23 00:46:11 -07:00
ret = hashmap__set ( ids , id , data_ptr ,
2020-07-19 20:13:03 +02:00
( const void * * ) & old_key , ( void * * ) & old_data ) ;
if ( ret )
free ( data_ptr ) ;
free ( old_key ) ;
free ( old_data ) ;
return ret ;
}
2021-09-23 00:46:11 -07:00
struct hashmap * ids__union ( struct hashmap * ids1 , struct hashmap * ids2 )
{
size_t bkt ;
struct hashmap_entry * cur ;
int ret ;
struct expr_id_data * old_data = NULL ;
char * old_key = NULL ;
if ( ! ids1 )
return ids2 ;
if ( ! ids2 )
return ids1 ;
if ( hashmap__size ( ids1 ) < hashmap__size ( ids2 ) ) {
struct hashmap * tmp = ids1 ;
ids1 = ids2 ;
ids2 = tmp ;
}
hashmap__for_each_entry ( ids2 , cur , bkt ) {
ret = hashmap__set ( ids1 , cur - > key , cur - > value ,
( const void * * ) & old_key , ( void * * ) & old_data ) ;
free ( old_key ) ;
free ( old_data ) ;
if ( ret ) {
hashmap__free ( ids1 ) ;
hashmap__free ( ids2 ) ;
return NULL ;
}
}
hashmap__free ( ids2 ) ;
return ids1 ;
}
/* Caller must make sure id is allocated */
int expr__add_id ( struct expr_parse_ctx * ctx , const char * id )
{
2021-10-15 10:21:20 -07:00
return ids__insert ( ctx - > ids , id ) ;
2021-09-23 00:46:11 -07:00
}
2020-02-28 10:36:12 +01:00
/* Caller must make sure id is allocated */
2020-07-12 15:26:18 +02:00
int expr__add_id_val ( struct expr_parse_ctx * ctx , const char * id , double val )
2020-02-28 10:36:12 +01:00
{
2020-07-12 15:26:18 +02:00
struct expr_id_data * data_ptr = NULL , * old_data = NULL ;
2020-05-15 15:17:32 -07:00
char * old_key = NULL ;
int ret ;
2020-07-19 20:13:03 +02:00
data_ptr = malloc ( sizeof ( * data_ptr ) ) ;
if ( ! data_ptr )
return - ENOMEM ;
data_ptr - > val = val ;
2020-08-26 08:30:55 -07:00
data_ptr - > kind = EXPR_ID_DATA__VALUE ;
2020-07-19 20:13:03 +02:00
2021-09-23 00:46:04 -07:00
ret = hashmap__set ( ctx - > ids , id , data_ptr ,
2020-07-12 15:26:18 +02:00
( const void * * ) & old_key , ( void * * ) & old_data ) ;
2020-07-19 20:13:02 +02:00
if ( ret )
free ( data_ptr ) ;
2020-05-15 15:17:32 -07:00
free ( old_key ) ;
2020-07-12 15:26:18 +02:00
free ( old_data ) ;
2020-05-15 15:17:32 -07:00
return ret ;
}
2020-07-19 20:13:11 +02:00
int expr__add_ref ( struct expr_parse_ctx * ctx , struct metric_ref * ref )
{
struct expr_id_data * data_ptr = NULL , * old_data = NULL ;
char * old_key = NULL ;
char * name , * p ;
int ret ;
data_ptr = zalloc ( sizeof ( * data_ptr ) ) ;
if ( ! data_ptr )
return - ENOMEM ;
name = strdup ( ref - > metric_name ) ;
if ( ! name ) {
free ( data_ptr ) ;
return - ENOMEM ;
}
/*
* The jevents tool converts all metric expressions
* to lowercase , including metric references , hence
* we need to add lowercase name for metric , so it ' s
* properly found .
*/
for ( p = name ; * p ; p + + )
* p = tolower ( * p ) ;
/*
* Intentionally passing just const char pointers ,
* originally from ' struct pmu_event ' object .
* We don ' t need to change them , so there ' s no
* need to create our own copy .
*/
data_ptr - > ref . metric_name = ref - > metric_name ;
data_ptr - > ref . metric_expr = ref - > metric_expr ;
2020-08-26 08:30:55 -07:00
data_ptr - > kind = EXPR_ID_DATA__REF ;
2020-07-19 20:13:11 +02:00
2021-09-23 00:46:04 -07:00
ret = hashmap__set ( ctx - > ids , name , data_ptr ,
2020-07-19 20:13:11 +02:00
( const void * * ) & old_key , ( void * * ) & old_data ) ;
if ( ret )
free ( data_ptr ) ;
pr_debug2 ( " adding ref metric %s: %s \n " ,
ref - > metric_name , ref - > metric_expr ) ;
free ( old_key ) ;
free ( old_data ) ;
return ret ;
}
2020-07-19 20:13:04 +02:00
int expr__get_id ( struct expr_parse_ctx * ctx , const char * id ,
struct expr_id_data * * data )
2020-05-15 15:17:32 -07:00
{
2021-09-23 00:46:04 -07:00
return hashmap__find ( ctx - > ids , id , ( void * * ) data ) ? 0 : - 1 ;
2020-02-28 10:36:12 +01:00
}
2021-10-15 10:21:28 -07:00
bool expr__subset_of_ids ( struct expr_parse_ctx * haystack ,
struct expr_parse_ctx * needles )
{
struct hashmap_entry * cur ;
size_t bkt ;
struct expr_id_data * data ;
hashmap__for_each_entry ( needles - > ids , cur , bkt ) {
if ( expr__get_id ( haystack , cur - > key , & data ) )
return false ;
}
return true ;
}
2020-07-19 20:13:12 +02:00
int expr__resolve_id ( struct expr_parse_ctx * ctx , const char * id ,
struct expr_id_data * * datap )
{
struct expr_id_data * data ;
if ( expr__get_id ( ctx , id , datap ) | | ! * datap ) {
pr_debug ( " %s not found \n " , id ) ;
return - 1 ;
}
data = * datap ;
2020-08-26 08:30:55 -07:00
switch ( data - > kind ) {
case EXPR_ID_DATA__VALUE :
pr_debug2 ( " lookup(%s): val %f \n " , id , data - > val ) ;
break ;
case EXPR_ID_DATA__REF :
pr_debug2 ( " lookup(%s): ref metric name %s \n " , id ,
data - > ref . metric_name ) ;
2020-07-19 20:13:12 +02:00
pr_debug ( " processing metric: %s ENTRY \n " , id ) ;
2020-08-26 08:30:55 -07:00
data - > kind = EXPR_ID_DATA__REF_VALUE ;
2021-10-15 10:21:16 -07:00
if ( expr__parse ( & data - > ref . val , ctx , data - > ref . metric_expr ) ) {
2020-07-19 20:13:12 +02:00
pr_debug ( " %s failed to count \n " , id ) ;
return - 1 ;
}
pr_debug ( " processing metric: %s EXIT: %f \n " , id , data - > val ) ;
2020-08-26 08:30:55 -07:00
break ;
case EXPR_ID_DATA__REF_VALUE :
pr_debug2 ( " lookup(%s): ref val %f metric name %s \n " , id ,
data - > ref . val , data - > ref . metric_name ) ;
break ;
default :
assert ( 0 ) ; /* Unreachable. */
2020-07-19 20:13:12 +02:00
}
return 0 ;
}
2020-07-19 20:13:05 +02:00
void expr__del_id ( struct expr_parse_ctx * ctx , const char * id )
{
struct expr_id_data * old_val = NULL ;
char * old_key = NULL ;
2021-09-23 00:46:04 -07:00
hashmap__delete ( ctx - > ids , id ,
2020-07-19 20:13:05 +02:00
( const void * * ) & old_key , ( void * * ) & old_val ) ;
free ( old_key ) ;
free ( old_val ) ;
}
2021-09-23 00:46:04 -07:00
struct expr_parse_ctx * expr__ctx_new ( void )
2020-02-28 10:36:12 +01:00
{
2021-09-23 00:46:04 -07:00
struct expr_parse_ctx * ctx ;
ctx = malloc ( sizeof ( struct expr_parse_ctx ) ) ;
if ( ! ctx )
return NULL ;
ctx - > ids = hashmap__new ( key_hash , key_equal , NULL ) ;
2021-10-15 10:21:16 -07:00
ctx - > runtime = 0 ;
2021-10-15 10:21:20 -07:00
2021-09-23 00:46:04 -07:00
return ctx ;
2020-05-15 15:17:32 -07:00
}
void expr__ctx_clear ( struct expr_parse_ctx * ctx )
{
struct hashmap_entry * cur ;
size_t bkt ;
2021-09-23 00:46:04 -07:00
hashmap__for_each_entry ( ctx - > ids , cur , bkt ) {
free ( ( char * ) cur - > key ) ;
free ( cur - > value ) ;
}
hashmap__clear ( ctx - > ids ) ;
}
void expr__ctx_free ( struct expr_parse_ctx * ctx )
{
struct hashmap_entry * cur ;
size_t bkt ;
hashmap__for_each_entry ( ctx - > ids , cur , bkt ) {
2020-05-15 15:17:32 -07:00
free ( ( char * ) cur - > key ) ;
free ( cur - > value ) ;
}
2021-09-23 00:46:04 -07:00
hashmap__free ( ctx - > ids ) ;
free ( ctx ) ;
2020-02-28 10:36:12 +01:00
}
2020-02-28 10:36:13 +01:00
static int
2020-04-02 02:03:34 +05:30
__expr__parse ( double * val , struct expr_parse_ctx * ctx , const char * expr ,
2021-10-15 10:21:16 -07:00
bool compute_ids )
2020-02-28 10:36:13 +01:00
{
2020-04-02 02:03:35 +05:30
struct expr_scanner_ctx scanner_ctx = {
2021-10-15 10:21:16 -07:00
. runtime = ctx - > runtime ,
2020-04-02 02:03:35 +05:30
} ;
2020-02-28 10:36:13 +01:00
YY_BUFFER_STATE buffer ;
void * scanner ;
int ret ;
2020-07-19 20:13:12 +02:00
pr_debug2 ( " parsing metric: %s \n " , expr ) ;
2020-04-02 02:03:35 +05:30
ret = expr_lex_init_extra ( & scanner_ctx , & scanner ) ;
2020-02-28 10:36:13 +01:00
if ( ret )
return ret ;
buffer = expr__scan_string ( expr , scanner ) ;
# ifdef PARSER_DEBUG
expr_debug = 1 ;
2020-05-01 10:33:28 -07:00
expr_set_debug ( 1 , scanner ) ;
2020-02-28 10:36:13 +01:00
# endif
2021-09-23 00:46:13 -07:00
ret = expr_parse ( val , ctx , compute_ids , scanner ) ;
2020-02-28 10:36:13 +01:00
expr__flush_buffer ( buffer , scanner ) ;
expr__delete_buffer ( buffer , scanner ) ;
expr_lex_destroy ( scanner ) ;
return ret ;
}
2020-05-15 15:17:32 -07:00
int expr__parse ( double * final_val , struct expr_parse_ctx * ctx ,
2021-10-15 10:21:16 -07:00
const char * expr )
2020-02-28 10:36:13 +01:00
{
2021-10-15 10:21:16 -07:00
return __expr__parse ( final_val , ctx , expr , /*compute_ids=*/ false ) ? - 1 : 0 ;
2020-02-28 10:36:13 +01:00
}
2021-09-23 00:46:10 -07:00
int expr__find_ids ( const char * expr , const char * one ,
2021-10-15 10:21:16 -07:00
struct expr_parse_ctx * ctx )
2020-02-28 10:36:13 +01:00
{
2021-10-15 10:21:16 -07:00
int ret = __expr__parse ( NULL , ctx , expr , /*compute_ids=*/ true ) ;
2020-05-15 15:17:32 -07:00
2020-07-19 20:13:05 +02:00
if ( one )
expr__del_id ( ctx , one ) ;
2020-02-28 10:36:13 +01:00
2020-05-15 15:17:32 -07:00
return ret ;
2020-02-28 10:36:13 +01:00
}
2020-08-26 08:30:55 -07:00
double expr_id_data__value ( const struct expr_id_data * data )
{
if ( data - > kind = = EXPR_ID_DATA__VALUE )
return data - > val ;
assert ( data - > kind = = EXPR_ID_DATA__REF_VALUE ) ;
return data - > ref . val ;
}