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