cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

expr.c (9515B)


      1// SPDX-License-Identifier: GPL-2.0
      2#include <stdbool.h>
      3#include <assert.h>
      4#include <errno.h>
      5#include <stdlib.h>
      6#include <string.h>
      7#include "metricgroup.h"
      8#include "cpumap.h"
      9#include "cputopo.h"
     10#include "debug.h"
     11#include "expr.h"
     12#include "expr-bison.h"
     13#include "expr-flex.h"
     14#include "smt.h"
     15#include <linux/err.h>
     16#include <linux/kernel.h>
     17#include <linux/zalloc.h>
     18#include <ctype.h>
     19#include <math.h>
     20
     21#ifdef PARSER_DEBUG
     22extern int expr_debug;
     23#endif
     24
     25struct expr_id_data {
     26	union {
     27		struct {
     28			double val;
     29			int source_count;
     30		} val;
     31		struct {
     32			double val;
     33			const char *metric_name;
     34			const char *metric_expr;
     35		} ref;
     36	};
     37
     38	enum {
     39		/* Holding a double value. */
     40		EXPR_ID_DATA__VALUE,
     41		/* Reference to another metric. */
     42		EXPR_ID_DATA__REF,
     43		/* A reference but the value has been computed. */
     44		EXPR_ID_DATA__REF_VALUE,
     45	} kind;
     46};
     47
     48static size_t key_hash(const void *key, void *ctx __maybe_unused)
     49{
     50	const char *str = (const char *)key;
     51	size_t hash = 0;
     52
     53	while (*str != '\0') {
     54		hash *= 31;
     55		hash += *str;
     56		str++;
     57	}
     58	return hash;
     59}
     60
     61static bool key_equal(const void *key1, const void *key2,
     62		    void *ctx __maybe_unused)
     63{
     64	return !strcmp((const char *)key1, (const char *)key2);
     65}
     66
     67struct hashmap *ids__new(void)
     68{
     69	struct hashmap *hash;
     70
     71	hash = hashmap__new(key_hash, key_equal, NULL);
     72	if (IS_ERR(hash))
     73		return NULL;
     74	return hash;
     75}
     76
     77void ids__free(struct hashmap *ids)
     78{
     79	struct hashmap_entry *cur;
     80	size_t bkt;
     81
     82	if (ids == NULL)
     83		return;
     84
     85	hashmap__for_each_entry(ids, cur, bkt) {
     86		free((char *)cur->key);
     87		free(cur->value);
     88	}
     89
     90	hashmap__free(ids);
     91}
     92
     93int ids__insert(struct hashmap *ids, const char *id)
     94{
     95	struct expr_id_data *data_ptr = NULL, *old_data = NULL;
     96	char *old_key = NULL;
     97	int ret;
     98
     99	ret = hashmap__set(ids, id, data_ptr,
    100			   (const void **)&old_key, (void **)&old_data);
    101	if (ret)
    102		free(data_ptr);
    103	free(old_key);
    104	free(old_data);
    105	return ret;
    106}
    107
    108struct hashmap *ids__union(struct hashmap *ids1, struct hashmap *ids2)
    109{
    110	size_t bkt;
    111	struct hashmap_entry *cur;
    112	int ret;
    113	struct expr_id_data *old_data = NULL;
    114	char *old_key = NULL;
    115
    116	if (!ids1)
    117		return ids2;
    118
    119	if (!ids2)
    120		return ids1;
    121
    122	if (hashmap__size(ids1) <  hashmap__size(ids2)) {
    123		struct hashmap *tmp = ids1;
    124
    125		ids1 = ids2;
    126		ids2 = tmp;
    127	}
    128	hashmap__for_each_entry(ids2, cur, bkt) {
    129		ret = hashmap__set(ids1, cur->key, cur->value,
    130				(const void **)&old_key, (void **)&old_data);
    131		free(old_key);
    132		free(old_data);
    133
    134		if (ret) {
    135			hashmap__free(ids1);
    136			hashmap__free(ids2);
    137			return NULL;
    138		}
    139	}
    140	hashmap__free(ids2);
    141	return ids1;
    142}
    143
    144/* Caller must make sure id is allocated */
    145int expr__add_id(struct expr_parse_ctx *ctx, const char *id)
    146{
    147	return ids__insert(ctx->ids, id);
    148}
    149
    150/* Caller must make sure id is allocated */
    151int expr__add_id_val(struct expr_parse_ctx *ctx, const char *id, double val)
    152{
    153	return expr__add_id_val_source_count(ctx, id, val, /*source_count=*/1);
    154}
    155
    156/* Caller must make sure id is allocated */
    157int expr__add_id_val_source_count(struct expr_parse_ctx *ctx, const char *id,
    158				  double val, int source_count)
    159{
    160	struct expr_id_data *data_ptr = NULL, *old_data = NULL;
    161	char *old_key = NULL;
    162	int ret;
    163
    164	data_ptr = malloc(sizeof(*data_ptr));
    165	if (!data_ptr)
    166		return -ENOMEM;
    167	data_ptr->val.val = val;
    168	data_ptr->val.source_count = source_count;
    169	data_ptr->kind = EXPR_ID_DATA__VALUE;
    170
    171	ret = hashmap__set(ctx->ids, id, data_ptr,
    172			   (const void **)&old_key, (void **)&old_data);
    173	if (ret)
    174		free(data_ptr);
    175	free(old_key);
    176	free(old_data);
    177	return ret;
    178}
    179
    180int expr__add_ref(struct expr_parse_ctx *ctx, struct metric_ref *ref)
    181{
    182	struct expr_id_data *data_ptr = NULL, *old_data = NULL;
    183	char *old_key = NULL;
    184	char *name, *p;
    185	int ret;
    186
    187	data_ptr = zalloc(sizeof(*data_ptr));
    188	if (!data_ptr)
    189		return -ENOMEM;
    190
    191	name = strdup(ref->metric_name);
    192	if (!name) {
    193		free(data_ptr);
    194		return -ENOMEM;
    195	}
    196
    197	/*
    198	 * The jevents tool converts all metric expressions
    199	 * to lowercase, including metric references, hence
    200	 * we need to add lowercase name for metric, so it's
    201	 * properly found.
    202	 */
    203	for (p = name; *p; p++)
    204		*p = tolower(*p);
    205
    206	/*
    207	 * Intentionally passing just const char pointers,
    208	 * originally from 'struct pmu_event' object.
    209	 * We don't need to change them, so there's no
    210	 * need to create our own copy.
    211	 */
    212	data_ptr->ref.metric_name = ref->metric_name;
    213	data_ptr->ref.metric_expr = ref->metric_expr;
    214	data_ptr->kind = EXPR_ID_DATA__REF;
    215
    216	ret = hashmap__set(ctx->ids, name, data_ptr,
    217			   (const void **)&old_key, (void **)&old_data);
    218	if (ret)
    219		free(data_ptr);
    220
    221	pr_debug2("adding ref metric %s: %s\n",
    222		  ref->metric_name, ref->metric_expr);
    223
    224	free(old_key);
    225	free(old_data);
    226	return ret;
    227}
    228
    229int expr__get_id(struct expr_parse_ctx *ctx, const char *id,
    230		 struct expr_id_data **data)
    231{
    232	return hashmap__find(ctx->ids, id, (void **)data) ? 0 : -1;
    233}
    234
    235bool expr__subset_of_ids(struct expr_parse_ctx *haystack,
    236			 struct expr_parse_ctx *needles)
    237{
    238	struct hashmap_entry *cur;
    239	size_t bkt;
    240	struct expr_id_data *data;
    241
    242	hashmap__for_each_entry(needles->ids, cur, bkt) {
    243		if (expr__get_id(haystack, cur->key, &data))
    244			return false;
    245	}
    246	return true;
    247}
    248
    249
    250int expr__resolve_id(struct expr_parse_ctx *ctx, const char *id,
    251		     struct expr_id_data **datap)
    252{
    253	struct expr_id_data *data;
    254
    255	if (expr__get_id(ctx, id, datap) || !*datap) {
    256		pr_debug("%s not found\n", id);
    257		return -1;
    258	}
    259
    260	data = *datap;
    261
    262	switch (data->kind) {
    263	case EXPR_ID_DATA__VALUE:
    264		pr_debug2("lookup(%s): val %f\n", id, data->val.val);
    265		break;
    266	case EXPR_ID_DATA__REF:
    267		pr_debug2("lookup(%s): ref metric name %s\n", id,
    268			data->ref.metric_name);
    269		pr_debug("processing metric: %s ENTRY\n", id);
    270		data->kind = EXPR_ID_DATA__REF_VALUE;
    271		if (expr__parse(&data->ref.val, ctx, data->ref.metric_expr)) {
    272			pr_debug("%s failed to count\n", id);
    273			return -1;
    274		}
    275		pr_debug("processing metric: %s EXIT: %f\n", id, data->ref.val);
    276		break;
    277	case EXPR_ID_DATA__REF_VALUE:
    278		pr_debug2("lookup(%s): ref val %f metric name %s\n", id,
    279			data->ref.val, data->ref.metric_name);
    280		break;
    281	default:
    282		assert(0);  /* Unreachable. */
    283	}
    284
    285	return 0;
    286}
    287
    288void expr__del_id(struct expr_parse_ctx *ctx, const char *id)
    289{
    290	struct expr_id_data *old_val = NULL;
    291	char *old_key = NULL;
    292
    293	hashmap__delete(ctx->ids, id,
    294			(const void **)&old_key, (void **)&old_val);
    295	free(old_key);
    296	free(old_val);
    297}
    298
    299struct expr_parse_ctx *expr__ctx_new(void)
    300{
    301	struct expr_parse_ctx *ctx;
    302
    303	ctx = malloc(sizeof(struct expr_parse_ctx));
    304	if (!ctx)
    305		return NULL;
    306
    307	ctx->ids = hashmap__new(key_hash, key_equal, NULL);
    308	if (IS_ERR(ctx->ids)) {
    309		free(ctx);
    310		return NULL;
    311	}
    312	ctx->runtime = 0;
    313
    314	return ctx;
    315}
    316
    317void expr__ctx_clear(struct expr_parse_ctx *ctx)
    318{
    319	struct hashmap_entry *cur;
    320	size_t bkt;
    321
    322	hashmap__for_each_entry(ctx->ids, cur, bkt) {
    323		free((char *)cur->key);
    324		free(cur->value);
    325	}
    326	hashmap__clear(ctx->ids);
    327}
    328
    329void expr__ctx_free(struct expr_parse_ctx *ctx)
    330{
    331	struct hashmap_entry *cur;
    332	size_t bkt;
    333
    334	hashmap__for_each_entry(ctx->ids, cur, bkt) {
    335		free((char *)cur->key);
    336		free(cur->value);
    337	}
    338	hashmap__free(ctx->ids);
    339	free(ctx);
    340}
    341
    342static int
    343__expr__parse(double *val, struct expr_parse_ctx *ctx, const char *expr,
    344	      bool compute_ids)
    345{
    346	struct expr_scanner_ctx scanner_ctx = {
    347		.runtime = ctx->runtime,
    348	};
    349	YY_BUFFER_STATE buffer;
    350	void *scanner;
    351	int ret;
    352
    353	pr_debug2("parsing metric: %s\n", expr);
    354
    355	ret = expr_lex_init_extra(&scanner_ctx, &scanner);
    356	if (ret)
    357		return ret;
    358
    359	buffer = expr__scan_string(expr, scanner);
    360
    361#ifdef PARSER_DEBUG
    362	expr_debug = 1;
    363	expr_set_debug(1, scanner);
    364#endif
    365
    366	ret = expr_parse(val, ctx, compute_ids, scanner);
    367
    368	expr__flush_buffer(buffer, scanner);
    369	expr__delete_buffer(buffer, scanner);
    370	expr_lex_destroy(scanner);
    371	return ret;
    372}
    373
    374int expr__parse(double *final_val, struct expr_parse_ctx *ctx,
    375		const char *expr)
    376{
    377	return __expr__parse(final_val, ctx, expr, /*compute_ids=*/false) ? -1 : 0;
    378}
    379
    380int expr__find_ids(const char *expr, const char *one,
    381		   struct expr_parse_ctx *ctx)
    382{
    383	int ret = __expr__parse(NULL, ctx, expr, /*compute_ids=*/true);
    384
    385	if (one)
    386		expr__del_id(ctx, one);
    387
    388	return ret;
    389}
    390
    391double expr_id_data__value(const struct expr_id_data *data)
    392{
    393	if (data->kind == EXPR_ID_DATA__VALUE)
    394		return data->val.val;
    395	assert(data->kind == EXPR_ID_DATA__REF_VALUE);
    396	return data->ref.val;
    397}
    398
    399double expr_id_data__source_count(const struct expr_id_data *data)
    400{
    401	assert(data->kind == EXPR_ID_DATA__VALUE);
    402	return data->val.source_count;
    403}
    404
    405double expr__get_literal(const char *literal)
    406{
    407	static struct cpu_topology *topology;
    408	double result = NAN;
    409
    410	if (!strcasecmp("#smt_on", literal)) {
    411		result = smt_on() > 0 ? 1.0 : 0.0;
    412		goto out;
    413	}
    414
    415	if (!strcmp("#num_cpus", literal)) {
    416		result = cpu__max_present_cpu().cpu;
    417		goto out;
    418	}
    419
    420	/*
    421	 * Assume that topology strings are consistent, such as CPUs "0-1"
    422	 * wouldn't be listed as "0,1", and so after deduplication the number of
    423	 * these strings gives an indication of the number of packages, dies,
    424	 * etc.
    425	 */
    426	if (!topology) {
    427		topology = cpu_topology__new();
    428		if (!topology) {
    429			pr_err("Error creating CPU topology");
    430			goto out;
    431		}
    432	}
    433	if (!strcmp("#num_packages", literal)) {
    434		result = topology->package_cpus_lists;
    435		goto out;
    436	}
    437	if (!strcmp("#num_dies", literal)) {
    438		result = topology->die_cpus_lists;
    439		goto out;
    440	}
    441	if (!strcmp("#num_cores", literal)) {
    442		result = topology->core_cpus_lists;
    443		goto out;
    444	}
    445
    446	pr_err("Unrecognized literal '%s'", literal);
    447out:
    448	pr_debug2("literal: %s = %f\n", literal, result);
    449	return result;
    450}