2020-02-28 12:36:12 +03:00
// SPDX-License-Identifier: GPL-2.0
2020-02-28 12:36:13 +03:00
# include <stdbool.h>
2020-02-28 12:36:12 +03:00
# include <assert.h>
2020-06-23 16:09:03 +03:00
# include <errno.h>
# include <stdlib.h>
# include <string.h>
2020-07-19 21:13:11 +03:00
# include "metricgroup.h"
2021-11-11 03:21:07 +03:00
# include "cpumap.h"
# include "cputopo.h"
2020-07-19 21:13:11 +03:00
# include "debug.h"
2020-02-28 12:36:12 +03:00
# include "expr.h"
2020-02-28 12:36:13 +03:00
# include "expr-bison.h"
# include "expr-flex.h"
2021-11-11 03:21:06 +03:00
# include "smt.h"
2022-07-18 19:43:10 +03:00
# include "tsc.h"
2021-12-12 09:25:02 +03:00
# include <linux/err.h>
2020-05-16 01:17:32 +03:00
# include <linux/kernel.h>
2020-07-19 21:13:11 +03:00
# include <linux/zalloc.h>
# include <ctype.h>
2021-11-11 03:21:06 +03:00
# include <math.h>
2020-02-28 12:36:13 +03:00
# ifdef PARSER_DEBUG
extern int expr_debug ;
# endif
2020-02-28 12:36:12 +03:00
2020-08-26 18:30:55 +03:00
struct expr_id_data {
union {
2021-11-11 03:21:09 +03:00
struct {
double val ;
int source_count ;
} val ;
2020-08-26 18:30:55 +03:00
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-16 01:17:32 +03: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 10:46:11 +03:00
struct hashmap * ids__new ( void )
{
2021-12-14 04:10:27 +03:00
struct hashmap * hash ;
hash = hashmap__new ( key_hash , key_equal , NULL ) ;
if ( IS_ERR ( hash ) )
return NULL ;
return hash ;
2021-09-23 10:46:11 +03:00
}
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 20:21:20 +03:00
int ids__insert ( struct hashmap * ids , const char * id )
2020-07-19 21:13:03 +03:00
{
struct expr_id_data * data_ptr = NULL , * old_data = NULL ;
char * old_key = NULL ;
int ret ;
2021-09-23 10:46:11 +03:00
ret = hashmap__set ( ids , id , data_ptr ,
2020-07-19 21:13:03 +03:00
( const void * * ) & old_key , ( void * * ) & old_data ) ;
if ( ret )
free ( data_ptr ) ;
free ( old_key ) ;
free ( old_data ) ;
return ret ;
}
2021-09-23 10:46:11 +03: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 20:21:20 +03:00
return ids__insert ( ctx - > ids , id ) ;
2021-09-23 10:46:11 +03:00
}
2020-02-28 12:36:12 +03:00
/* Caller must make sure id is allocated */
2020-07-12 16:26:18 +03:00
int expr__add_id_val ( struct expr_parse_ctx * ctx , const char * id , double val )
2021-11-11 03:21:09 +03:00
{
return expr__add_id_val_source_count ( ctx , id , val , /*source_count=*/ 1 ) ;
}
/* Caller must make sure id is allocated */
int expr__add_id_val_source_count ( struct expr_parse_ctx * ctx , const char * id ,
double val , int source_count )
2020-02-28 12:36:12 +03:00
{
2020-07-12 16:26:18 +03:00
struct expr_id_data * data_ptr = NULL , * old_data = NULL ;
2020-05-16 01:17:32 +03:00
char * old_key = NULL ;
int ret ;
2020-07-19 21:13:03 +03:00
data_ptr = malloc ( sizeof ( * data_ptr ) ) ;
if ( ! data_ptr )
return - ENOMEM ;
2021-11-11 03:21:09 +03:00
data_ptr - > val . val = val ;
data_ptr - > val . source_count = source_count ;
2020-08-26 18:30:55 +03:00
data_ptr - > kind = EXPR_ID_DATA__VALUE ;
2020-07-19 21:13:03 +03:00
2021-09-23 10:46:04 +03:00
ret = hashmap__set ( ctx - > ids , id , data_ptr ,
2020-07-12 16:26:18 +03:00
( const void * * ) & old_key , ( void * * ) & old_data ) ;
2020-07-19 21:13:02 +03:00
if ( ret )
free ( data_ptr ) ;
2020-05-16 01:17:32 +03:00
free ( old_key ) ;
2020-07-12 16:26:18 +03:00
free ( old_data ) ;
2020-05-16 01:17:32 +03:00
return ret ;
}
2020-07-19 21:13:11 +03: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 ;
2022-10-04 05:15:52 +03:00
char * name ;
2020-07-19 21:13:11 +03:00
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 ;
}
/*
* 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 18:30:55 +03:00
data_ptr - > kind = EXPR_ID_DATA__REF ;
2020-07-19 21:13:11 +03:00
2021-09-23 10:46:04 +03:00
ret = hashmap__set ( ctx - > ids , name , data_ptr ,
2020-07-19 21:13:11 +03: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 21:13:04 +03:00
int expr__get_id ( struct expr_parse_ctx * ctx , const char * id ,
struct expr_id_data * * data )
2020-05-16 01:17:32 +03:00
{
2021-09-23 10:46:04 +03:00
return hashmap__find ( ctx - > ids , id , ( void * * ) data ) ? 0 : - 1 ;
2020-02-28 12:36:12 +03:00
}
2021-10-15 20:21:28 +03: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 21:13:12 +03: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 18:30:55 +03:00
switch ( data - > kind ) {
case EXPR_ID_DATA__VALUE :
2021-11-11 03:21:09 +03:00
pr_debug2 ( " lookup(%s): val %f \n " , id , data - > val . val ) ;
2020-08-26 18:30:55 +03:00
break ;
case EXPR_ID_DATA__REF :
pr_debug2 ( " lookup(%s): ref metric name %s \n " , id ,
data - > ref . metric_name ) ;
2020-07-19 21:13:12 +03:00
pr_debug ( " processing metric: %s ENTRY \n " , id ) ;
2020-08-26 18:30:55 +03:00
data - > kind = EXPR_ID_DATA__REF_VALUE ;
2021-10-15 20:21:16 +03:00
if ( expr__parse ( & data - > ref . val , ctx , data - > ref . metric_expr ) ) {
2020-07-19 21:13:12 +03:00
pr_debug ( " %s failed to count \n " , id ) ;
return - 1 ;
}
2021-11-11 03:21:09 +03:00
pr_debug ( " processing metric: %s EXIT: %f \n " , id , data - > ref . val ) ;
2020-08-26 18:30:55 +03: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 21:13:12 +03:00
}
return 0 ;
}
2020-07-19 21:13:05 +03: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 10:46:04 +03:00
hashmap__delete ( ctx - > ids , id ,
2020-07-19 21:13:05 +03:00
( const void * * ) & old_key , ( void * * ) & old_val ) ;
free ( old_key ) ;
free ( old_val ) ;
}
2021-09-23 10:46:04 +03:00
struct expr_parse_ctx * expr__ctx_new ( void )
2020-02-28 12:36:12 +03:00
{
2021-09-23 10:46:04 +03: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-12-12 09:25:02 +03:00
if ( IS_ERR ( ctx - > ids ) ) {
free ( ctx ) ;
return NULL ;
}
2022-08-31 20:49:25 +03:00
ctx - > sctx . user_requested_cpu_list = NULL ;
2022-08-31 20:49:21 +03:00
ctx - > sctx . runtime = 0 ;
2022-08-31 20:49:25 +03:00
ctx - > sctx . system_wide = false ;
2021-10-15 20:21:20 +03:00
2021-09-23 10:46:04 +03:00
return ctx ;
2020-05-16 01:17:32 +03:00
}
void expr__ctx_clear ( struct expr_parse_ctx * ctx )
{
struct hashmap_entry * cur ;
size_t bkt ;
2021-09-23 10:46:04 +03: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 ;
2022-08-31 20:49:25 +03:00
if ( ! ctx )
return ;
free ( ctx - > sctx . user_requested_cpu_list ) ;
2021-09-23 10:46:04 +03:00
hashmap__for_each_entry ( ctx - > ids , cur , bkt ) {
2020-05-16 01:17:32 +03:00
free ( ( char * ) cur - > key ) ;
free ( cur - > value ) ;
}
2021-09-23 10:46:04 +03:00
hashmap__free ( ctx - > ids ) ;
free ( ctx ) ;
2020-02-28 12:36:12 +03:00
}
2020-02-28 12:36:13 +03:00
static int
2020-04-01 23:33:34 +03:00
__expr__parse ( double * val , struct expr_parse_ctx * ctx , const char * expr ,
2021-10-15 20:21:16 +03:00
bool compute_ids )
2020-02-28 12:36:13 +03:00
{
YY_BUFFER_STATE buffer ;
void * scanner ;
int ret ;
2020-07-19 21:13:12 +03:00
pr_debug2 ( " parsing metric: %s \n " , expr ) ;
2022-08-31 20:49:21 +03:00
ret = expr_lex_init_extra ( & ctx - > sctx , & scanner ) ;
2020-02-28 12:36:13 +03:00
if ( ret )
return ret ;
buffer = expr__scan_string ( expr , scanner ) ;
# ifdef PARSER_DEBUG
expr_debug = 1 ;
2020-05-01 20:33:28 +03:00
expr_set_debug ( 1 , scanner ) ;
2020-02-28 12:36:13 +03:00
# endif
2021-09-23 10:46:13 +03:00
ret = expr_parse ( val , ctx , compute_ids , scanner ) ;
2020-02-28 12:36:13 +03:00
expr__flush_buffer ( buffer , scanner ) ;
expr__delete_buffer ( buffer , scanner ) ;
expr_lex_destroy ( scanner ) ;
return ret ;
}
2020-05-16 01:17:32 +03:00
int expr__parse ( double * final_val , struct expr_parse_ctx * ctx ,
2021-10-15 20:21:16 +03:00
const char * expr )
2020-02-28 12:36:13 +03:00
{
2021-10-15 20:21:16 +03:00
return __expr__parse ( final_val , ctx , expr , /*compute_ids=*/ false ) ? - 1 : 0 ;
2020-02-28 12:36:13 +03:00
}
2021-09-23 10:46:10 +03:00
int expr__find_ids ( const char * expr , const char * one ,
2021-10-15 20:21:16 +03:00
struct expr_parse_ctx * ctx )
2020-02-28 12:36:13 +03:00
{
2021-10-15 20:21:16 +03:00
int ret = __expr__parse ( NULL , ctx , expr , /*compute_ids=*/ true ) ;
2020-05-16 01:17:32 +03:00
2020-07-19 21:13:05 +03:00
if ( one )
expr__del_id ( ctx , one ) ;
2020-02-28 12:36:13 +03:00
2020-05-16 01:17:32 +03:00
return ret ;
2020-02-28 12:36:13 +03:00
}
2020-08-26 18:30:55 +03:00
double expr_id_data__value ( const struct expr_id_data * data )
{
if ( data - > kind = = EXPR_ID_DATA__VALUE )
2021-11-11 03:21:09 +03:00
return data - > val . val ;
2020-08-26 18:30:55 +03:00
assert ( data - > kind = = EXPR_ID_DATA__REF_VALUE ) ;
return data - > ref . val ;
}
2021-11-11 03:21:06 +03:00
2021-11-11 03:21:09 +03:00
double expr_id_data__source_count ( const struct expr_id_data * data )
{
assert ( data - > kind = = EXPR_ID_DATA__VALUE ) ;
return data - > val . source_count ;
}
2022-07-18 19:43:10 +03:00
# if !defined(__i386__) && !defined(__x86_64__)
double arch_get_tsc_freq ( void )
{
return 0.0 ;
}
# endif
2022-08-31 20:49:25 +03:00
double expr__get_literal ( const char * literal , const struct expr_scanner_ctx * ctx )
2021-11-11 03:21:06 +03:00
{
2021-11-11 03:21:07 +03:00
static struct cpu_topology * topology ;
2021-11-24 03:12:28 +03:00
double result = NAN ;
2021-11-11 03:21:07 +03:00
2021-11-24 03:12:28 +03:00
if ( ! strcmp ( " #num_cpus " , literal ) ) {
result = cpu__max_present_cpu ( ) . cpu ;
goto out ;
}
2021-11-11 03:21:07 +03:00
2022-07-18 19:43:10 +03:00
if ( ! strcasecmp ( " #system_tsc_freq " , literal ) ) {
result = arch_get_tsc_freq ( ) ;
goto out ;
}
2021-11-11 03:21:07 +03:00
/*
* Assume that topology strings are consistent , such as CPUs " 0-1 "
* wouldn ' t be listed as " 0,1 " , and so after deduplication the number of
* these strings gives an indication of the number of packages , dies ,
* etc .
*/
if ( ! topology ) {
topology = cpu_topology__new ( ) ;
if ( ! topology ) {
pr_err ( " Error creating CPU topology " ) ;
2021-11-24 03:12:28 +03:00
goto out ;
2021-11-11 03:21:07 +03:00
}
}
2022-08-31 20:49:22 +03:00
if ( ! strcasecmp ( " #smt_on " , literal ) ) {
result = smt_on ( topology ) ? 1.0 : 0.0 ;
goto out ;
}
2022-08-31 20:49:25 +03:00
if ( ! strcmp ( " #core_wide " , literal ) ) {
result = core_wide ( ctx - > system_wide , ctx - > user_requested_cpu_list , topology )
? 1.0 : 0.0 ;
goto out ;
}
2021-11-24 03:12:28 +03:00
if ( ! strcmp ( " #num_packages " , literal ) ) {
result = topology - > package_cpus_lists ;
goto out ;
}
if ( ! strcmp ( " #num_dies " , literal ) ) {
result = topology - > die_cpus_lists ;
goto out ;
}
if ( ! strcmp ( " #num_cores " , literal ) ) {
result = topology - > core_cpus_lists ;
goto out ;
}
2021-11-11 03:21:07 +03:00
2021-11-11 03:21:06 +03:00
pr_err ( " Unrecognized literal '%s' " , literal ) ;
2021-11-24 03:12:28 +03:00
out :
pr_debug2 ( " literal: %s = %f \n " , literal , result ) ;
return result ;
2021-11-11 03:21:06 +03:00
}