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 .metric_expr = "idq_uops_not_delivered.core / (4 * (( ( cpu_clk_unhalted.thread / 2 ) * " 22 "( 1 + cpu_clk_unhalted.one_thread_active / cpu_clk_unhalted.ref_xclk ) )))", 23 .metric_name = "Frontend_Bound_SMT", 24 }, 25 }; 26 27 static struct pmu_events_map map = { 28 .cpuid = "test", 29 .version = "1", 30 .type = "core", 31 .table = pme_test, 32 }; 33 34 struct value { 35 const char *event; 36 u64 val; 37 }; 38 39 static u64 find_value(const char *name, struct value *values) 40 { 41 struct value *v = values; 42 43 while (v->event) { 44 if (!strcmp(name, v->event)) 45 return v->val; 46 v++; 47 }; 48 return 0; 49 } 50 51 static void load_runtime_stat(struct runtime_stat *st, struct evlist *evlist, 52 struct value *vals) 53 { 54 struct evsel *evsel; 55 u64 count; 56 57 evlist__for_each_entry(evlist, evsel) { 58 count = find_value(evsel->name, vals); 59 perf_stat__update_shadow_stats(evsel, count, 0, st); 60 } 61 } 62 63 static double compute_single(struct rblist *metric_events, struct evlist *evlist, 64 struct runtime_stat *st) 65 { 66 struct evsel *evsel = evlist__first(evlist); 67 struct metric_event *me; 68 69 me = metricgroup__lookup(metric_events, evsel, false); 70 if (me != NULL) { 71 struct metric_expr *mexp; 72 73 mexp = list_first_entry(&me->head, struct metric_expr, nd); 74 return test_generic_metric(mexp, 0, st); 75 } 76 return 0.; 77 } 78 79 static int compute_metric(const char *name, struct value *vals, double *ratio) 80 { 81 struct rblist metric_events = { 82 .nr_entries = 0, 83 }; 84 struct perf_cpu_map *cpus; 85 struct runtime_stat st; 86 struct evlist *evlist; 87 int err; 88 89 /* 90 * We need to prepare evlist for stat mode running on CPU 0 91 * because that's where all the stats are going to be created. 92 */ 93 evlist = evlist__new(); 94 if (!evlist) 95 return -ENOMEM; 96 97 cpus = perf_cpu_map__new("0"); 98 if (!cpus) 99 return -ENOMEM; 100 101 perf_evlist__set_maps(&evlist->core, cpus, NULL); 102 103 /* Parse the metric into metric_events list. */ 104 err = metricgroup__parse_groups_test(evlist, &map, name, 105 false, false, 106 &metric_events); 107 108 TEST_ASSERT_VAL("failed to parse metric", err == 0); 109 110 if (perf_evlist__alloc_stats(evlist, false)) 111 return -1; 112 113 /* Load the runtime stats with given numbers for events. */ 114 runtime_stat__init(&st); 115 load_runtime_stat(&st, evlist, vals); 116 117 /* And execute the metric */ 118 *ratio = compute_single(&metric_events, evlist, &st); 119 120 /* ... clenup. */ 121 metricgroup__rblist_exit(&metric_events); 122 runtime_stat__exit(&st); 123 perf_evlist__free_stats(evlist); 124 perf_cpu_map__put(cpus); 125 evlist__delete(evlist); 126 return 0; 127 } 128 129 static int test_ipc(void) 130 { 131 double ratio; 132 struct value vals[] = { 133 { .event = "inst_retired.any", .val = 300 }, 134 { .event = "cpu_clk_unhalted.thread", .val = 200 }, 135 { .event = NULL, }, 136 }; 137 138 TEST_ASSERT_VAL("failed to compute metric", 139 compute_metric("IPC", vals, &ratio) == 0); 140 141 TEST_ASSERT_VAL("IPC failed, wrong ratio", 142 ratio == 1.5); 143 return 0; 144 } 145 146 static int test_frontend(void) 147 { 148 double ratio; 149 struct value vals[] = { 150 { .event = "idq_uops_not_delivered.core", .val = 300 }, 151 { .event = "cpu_clk_unhalted.thread", .val = 200 }, 152 { .event = "cpu_clk_unhalted.one_thread_active", .val = 400 }, 153 { .event = "cpu_clk_unhalted.ref_xclk", .val = 600 }, 154 { .event = NULL, }, 155 }; 156 157 TEST_ASSERT_VAL("failed to compute metric", 158 compute_metric("Frontend_Bound_SMT", vals, &ratio) == 0); 159 160 TEST_ASSERT_VAL("Frontend_Bound_SMT failed, wrong ratio", 161 ratio == 0.45); 162 return 0; 163 } 164 165 int test__parse_metric(struct test *test __maybe_unused, int subtest __maybe_unused) 166 { 167 TEST_ASSERT_VAL("IPC failed", test_ipc() == 0); 168 TEST_ASSERT_VAL("frontend failed", test_frontend() == 0); 169 return 0; 170 } 171