|
| 1 | +// SPDX-License-Identifier: GPL-2.0 |
| 2 | +#include <linux/compiler.h> |
| 3 | +#include <string.h> |
| 4 | +#include <perf/cpumap.h> |
| 5 | +#include <perf/evlist.h> |
| 6 | +#include "metricgroup.h" |
| 7 | +#include "tests.h" |
| 8 | +#include "pmu-events/pmu-events.h" |
| 9 | +#include "evlist.h" |
| 10 | +#include "rblist.h" |
| 11 | +#include "debug.h" |
| 12 | +#include "expr.h" |
| 13 | +#include "stat.h" |
| 14 | + |
| 15 | +static struct pmu_event pme_test[] = { |
| 16 | +{ |
| 17 | + .metric_expr = "inst_retired.any / cpu_clk_unhalted.thread", |
| 18 | + .metric_name = "IPC", |
| 19 | +}, |
| 20 | +}; |
| 21 | + |
| 22 | +static struct pmu_events_map map = { |
| 23 | + .cpuid = "test", |
| 24 | + .version = "1", |
| 25 | + .type = "core", |
| 26 | + .table = pme_test, |
| 27 | +}; |
| 28 | + |
| 29 | +struct value { |
| 30 | + const char *event; |
| 31 | + u64 val; |
| 32 | +}; |
| 33 | + |
| 34 | +static u64 find_value(const char *name, struct value *values) |
| 35 | +{ |
| 36 | + struct value *v = values; |
| 37 | + |
| 38 | + while (v->event) { |
| 39 | + if (!strcmp(name, v->event)) |
| 40 | + return v->val; |
| 41 | + v++; |
| 42 | + }; |
| 43 | + return 0; |
| 44 | +} |
| 45 | + |
| 46 | +static void load_runtime_stat(struct runtime_stat *st, struct evlist *evlist, |
| 47 | + struct value *vals) |
| 48 | +{ |
| 49 | + struct evsel *evsel; |
| 50 | + u64 count; |
| 51 | + |
| 52 | + evlist__for_each_entry(evlist, evsel) { |
| 53 | + count = find_value(evsel->name, vals); |
| 54 | + perf_stat__update_shadow_stats(evsel, count, 0, st); |
| 55 | + } |
| 56 | +} |
| 57 | + |
| 58 | +static double compute_single(struct rblist *metric_events, struct evlist *evlist, |
| 59 | + struct runtime_stat *st) |
| 60 | +{ |
| 61 | + struct evsel *evsel = evlist__first(evlist); |
| 62 | + struct metric_event *me; |
| 63 | + |
| 64 | + me = metricgroup__lookup(metric_events, evsel, false); |
| 65 | + if (me != NULL) { |
| 66 | + struct metric_expr *mexp; |
| 67 | + |
| 68 | + mexp = list_first_entry(&me->head, struct metric_expr, nd); |
| 69 | + return test_generic_metric(mexp, 0, st); |
| 70 | + } |
| 71 | + return 0.; |
| 72 | +} |
| 73 | + |
| 74 | +static int compute_metric(const char *name, struct value *vals, double *ratio) |
| 75 | +{ |
| 76 | + struct rblist metric_events = { |
| 77 | + .nr_entries = 0, |
| 78 | + }; |
| 79 | + struct perf_cpu_map *cpus; |
| 80 | + struct runtime_stat st; |
| 81 | + struct evlist *evlist; |
| 82 | + int err; |
| 83 | + |
| 84 | + /* |
| 85 | + * We need to prepare evlist for stat mode running on CPU 0 |
| 86 | + * because that's where all the stats are going to be created. |
| 87 | + */ |
| 88 | + evlist = evlist__new(); |
| 89 | + if (!evlist) |
| 90 | + return -ENOMEM; |
| 91 | + |
| 92 | + cpus = perf_cpu_map__new("0"); |
| 93 | + if (!cpus) |
| 94 | + return -ENOMEM; |
| 95 | + |
| 96 | + perf_evlist__set_maps(&evlist->core, cpus, NULL); |
| 97 | + |
| 98 | + /* Parse the metric into metric_events list. */ |
| 99 | + err = metricgroup__parse_groups_test(evlist, &map, name, |
| 100 | + false, false, |
| 101 | + &metric_events); |
| 102 | + |
| 103 | + TEST_ASSERT_VAL("failed to parse metric", err == 0); |
| 104 | + |
| 105 | + if (perf_evlist__alloc_stats(evlist, false)) |
| 106 | + return -1; |
| 107 | + |
| 108 | + /* Load the runtime stats with given numbers for events. */ |
| 109 | + runtime_stat__init(&st); |
| 110 | + load_runtime_stat(&st, evlist, vals); |
| 111 | + |
| 112 | + /* And execute the metric */ |
| 113 | + *ratio = compute_single(&metric_events, evlist, &st); |
| 114 | + |
| 115 | + /* ... clenup. */ |
| 116 | + metricgroup__rblist_exit(&metric_events); |
| 117 | + runtime_stat__exit(&st); |
| 118 | + perf_evlist__free_stats(evlist); |
| 119 | + perf_cpu_map__put(cpus); |
| 120 | + evlist__delete(evlist); |
| 121 | + return 0; |
| 122 | +} |
| 123 | + |
| 124 | +static int test_ipc(void) |
| 125 | +{ |
| 126 | + double ratio; |
| 127 | + struct value vals[] = { |
| 128 | + { .event = "inst_retired.any", .val = 300 }, |
| 129 | + { .event = "cpu_clk_unhalted.thread", .val = 200 }, |
| 130 | + { .event = NULL, }, |
| 131 | + }; |
| 132 | + |
| 133 | + TEST_ASSERT_VAL("failed to compute metric", |
| 134 | + compute_metric("IPC", vals, &ratio) == 0); |
| 135 | + |
| 136 | + TEST_ASSERT_VAL("IPC failed, wrong ratio", |
| 137 | + ratio == 1.5); |
| 138 | + return 0; |
| 139 | +} |
| 140 | + |
| 141 | +int test__parse_metric(struct test *test __maybe_unused, int subtest __maybe_unused) |
| 142 | +{ |
| 143 | + TEST_ASSERT_VAL("IPC failed", test_ipc() == 0); |
| 144 | + return 0; |
| 145 | +} |
0 commit comments