311 lines · c
1// SPDX-License-Identifier: GPL-2.02#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#include "pmus.h"15 16struct value {17 const char *event;18 u64 val;19};20 21static u64 find_value(const char *name, struct value *values)22{23 struct value *v = values;24 25 while (v->event) {26 if (!strcmp(name, v->event))27 return v->val;28 v++;29 }30 return 0;31}32 33static void load_runtime_stat(struct evlist *evlist, struct value *vals)34{35 struct evsel *evsel;36 u64 count;37 38 evlist__alloc_aggr_stats(evlist, 1);39 evlist__for_each_entry(evlist, evsel) {40 count = find_value(evsel->name, vals);41 evsel->supported = true;42 evsel->stats->aggr->counts.val = count;43 if (evsel__name_is(evsel, "duration_time"))44 update_stats(&walltime_nsecs_stats, count);45 }46}47 48static double compute_single(struct rblist *metric_events, struct evlist *evlist,49 const char *name)50{51 struct metric_expr *mexp;52 struct metric_event *me;53 struct evsel *evsel;54 55 evlist__for_each_entry(evlist, evsel) {56 me = metricgroup__lookup(metric_events, evsel, false);57 if (me != NULL) {58 list_for_each_entry (mexp, &me->head, nd) {59 if (strcmp(mexp->metric_name, name))60 continue;61 return test_generic_metric(mexp, 0);62 }63 }64 }65 return 0.;66}67 68static int __compute_metric(const char *name, struct value *vals,69 const char *name1, double *ratio1,70 const char *name2, double *ratio2)71{72 struct rblist metric_events = {73 .nr_entries = 0,74 };75 const struct pmu_metrics_table *pme_test;76 struct perf_cpu_map *cpus;77 struct evlist *evlist;78 int err;79 80 /*81 * We need to prepare evlist for stat mode running on CPU 082 * because that's where all the stats are going to be created.83 */84 evlist = evlist__new();85 if (!evlist)86 return -ENOMEM;87 88 cpus = perf_cpu_map__new("0");89 if (!cpus) {90 evlist__delete(evlist);91 return -ENOMEM;92 }93 94 perf_evlist__set_maps(&evlist->core, cpus, NULL);95 96 /* Parse the metric into metric_events list. */97 pme_test = find_core_metrics_table("testarch", "testcpu");98 err = metricgroup__parse_groups_test(evlist, pme_test, name,99 &metric_events);100 if (err)101 goto out;102 103 err = evlist__alloc_stats(/*config=*/NULL, evlist, /*alloc_raw=*/false);104 if (err)105 goto out;106 107 /* Load the runtime stats with given numbers for events. */108 load_runtime_stat(evlist, vals);109 110 /* And execute the metric */111 if (name1 && ratio1)112 *ratio1 = compute_single(&metric_events, evlist, name1);113 if (name2 && ratio2)114 *ratio2 = compute_single(&metric_events, evlist, name2);115 116out:117 /* ... cleanup. */118 metricgroup__rblist_exit(&metric_events);119 evlist__free_stats(evlist);120 perf_cpu_map__put(cpus);121 evlist__delete(evlist);122 return err;123}124 125static int compute_metric(const char *name, struct value *vals, double *ratio)126{127 return __compute_metric(name, vals, name, ratio, NULL, NULL);128}129 130static int compute_metric_group(const char *name, struct value *vals,131 const char *name1, double *ratio1,132 const char *name2, double *ratio2)133{134 return __compute_metric(name, vals, name1, ratio1, name2, ratio2);135}136 137static int test_ipc(void)138{139 double ratio;140 struct value vals[] = {141 { .event = "inst_retired.any", .val = 300 },142 { .event = "cpu_clk_unhalted.thread", .val = 200 },143 { .event = NULL, },144 };145 146 TEST_ASSERT_VAL("failed to compute metric",147 compute_metric("IPC", vals, &ratio) == 0);148 149 TEST_ASSERT_VAL("IPC failed, wrong ratio",150 ratio == 1.5);151 return 0;152}153 154static int test_frontend(void)155{156 double ratio;157 struct value vals[] = {158 { .event = "idq_uops_not_delivered.core", .val = 300 },159 { .event = "cpu_clk_unhalted.thread", .val = 200 },160 { .event = "cpu_clk_unhalted.one_thread_active", .val = 400 },161 { .event = "cpu_clk_unhalted.ref_xclk", .val = 600 },162 { .event = NULL, },163 };164 165 TEST_ASSERT_VAL("failed to compute metric",166 compute_metric("Frontend_Bound_SMT", vals, &ratio) == 0);167 168 TEST_ASSERT_VAL("Frontend_Bound_SMT failed, wrong ratio",169 ratio == 0.45);170 return 0;171}172 173static int test_cache_miss_cycles(void)174{175 double ratio;176 struct value vals[] = {177 { .event = "l1d-loads-misses", .val = 300 },178 { .event = "l1i-loads-misses", .val = 200 },179 { .event = "inst_retired.any", .val = 400 },180 { .event = NULL, },181 };182 183 TEST_ASSERT_VAL("failed to compute metric",184 compute_metric("cache_miss_cycles", vals, &ratio) == 0);185 186 TEST_ASSERT_VAL("cache_miss_cycles failed, wrong ratio",187 ratio == 1.25);188 return 0;189}190 191 192/*193 * DCache_L2_All_Hits = l2_rqsts.demand_data_rd_hit + l2_rqsts.pf_hit + l2_rqsts.rfo_hi194 * DCache_L2_All_Miss = max(l2_rqsts.all_demand_data_rd - l2_rqsts.demand_data_rd_hit, 0) +195 * l2_rqsts.pf_miss + l2_rqsts.rfo_miss196 * DCache_L2_All = dcache_l2_all_hits + dcache_l2_all_miss197 * DCache_L2_Hits = d_ratio(dcache_l2_all_hits, dcache_l2_all)198 * DCache_L2_Misses = d_ratio(dcache_l2_all_miss, dcache_l2_all)199 *200 * l2_rqsts.demand_data_rd_hit = 100201 * l2_rqsts.pf_hit = 200202 * l2_rqsts.rfo_hi = 300203 * l2_rqsts.all_demand_data_rd = 400204 * l2_rqsts.pf_miss = 500205 * l2_rqsts.rfo_miss = 600206 *207 * DCache_L2_All_Hits = 600208 * DCache_L2_All_Miss = MAX(400 - 100, 0) + 500 + 600 = 1400209 * DCache_L2_All = 600 + 1400 = 2000210 * DCache_L2_Hits = 600 / 2000 = 0.3211 * DCache_L2_Misses = 1400 / 2000 = 0.7212 */213static int test_dcache_l2(void)214{215 double ratio;216 struct value vals[] = {217 { .event = "l2_rqsts.demand_data_rd_hit", .val = 100 },218 { .event = "l2_rqsts.pf_hit", .val = 200 },219 { .event = "l2_rqsts.rfo_hit", .val = 300 },220 { .event = "l2_rqsts.all_demand_data_rd", .val = 400 },221 { .event = "l2_rqsts.pf_miss", .val = 500 },222 { .event = "l2_rqsts.rfo_miss", .val = 600 },223 { .event = NULL, },224 };225 226 TEST_ASSERT_VAL("failed to compute metric",227 compute_metric("DCache_L2_Hits", vals, &ratio) == 0);228 229 TEST_ASSERT_VAL("DCache_L2_Hits failed, wrong ratio",230 ratio == 0.3);231 232 TEST_ASSERT_VAL("failed to compute metric",233 compute_metric("DCache_L2_Misses", vals, &ratio) == 0);234 235 TEST_ASSERT_VAL("DCache_L2_Misses failed, wrong ratio",236 ratio == 0.7);237 return 0;238}239 240static int test_recursion_fail(void)241{242 double ratio;243 struct value vals[] = {244 { .event = "inst_retired.any", .val = 300 },245 { .event = "cpu_clk_unhalted.thread", .val = 200 },246 { .event = NULL, },247 };248 249 TEST_ASSERT_VAL("failed to find recursion",250 compute_metric("M1", vals, &ratio) == -1);251 252 TEST_ASSERT_VAL("failed to find recursion",253 compute_metric("M3", vals, &ratio) == -1);254 return 0;255}256 257static int test_memory_bandwidth(void)258{259 double ratio;260 struct value vals[] = {261 { .event = "l1d.replacement", .val = 4000000 },262 { .event = "duration_time", .val = 200000000 },263 { .event = NULL, },264 };265 266 TEST_ASSERT_VAL("failed to compute metric",267 compute_metric("L1D_Cache_Fill_BW", vals, &ratio) == 0);268 TEST_ASSERT_VAL("L1D_Cache_Fill_BW, wrong ratio",269 1.28 == ratio);270 271 return 0;272}273 274static int test_metric_group(void)275{276 double ratio1, ratio2;277 struct value vals[] = {278 { .event = "cpu_clk_unhalted.thread", .val = 200 },279 { .event = "l1d-loads-misses", .val = 300 },280 { .event = "l1i-loads-misses", .val = 200 },281 { .event = "inst_retired.any", .val = 400 },282 { .event = NULL, },283 };284 285 TEST_ASSERT_VAL("failed to find recursion",286 compute_metric_group("group1", vals,287 "IPC", &ratio1,288 "cache_miss_cycles", &ratio2) == 0);289 290 TEST_ASSERT_VAL("group IPC failed, wrong ratio",291 ratio1 == 2.0);292 293 TEST_ASSERT_VAL("group cache_miss_cycles failed, wrong ratio",294 ratio2 == 1.25);295 return 0;296}297 298static int test__parse_metric(struct test_suite *test __maybe_unused, int subtest __maybe_unused)299{300 TEST_ASSERT_VAL("IPC failed", test_ipc() == 0);301 TEST_ASSERT_VAL("frontend failed", test_frontend() == 0);302 TEST_ASSERT_VAL("DCache_L2 failed", test_dcache_l2() == 0);303 TEST_ASSERT_VAL("recursion fail failed", test_recursion_fail() == 0);304 TEST_ASSERT_VAL("Memory bandwidth", test_memory_bandwidth() == 0);305 TEST_ASSERT_VAL("cache_miss_cycles failed", test_cache_miss_cycles() == 0);306 TEST_ASSERT_VAL("test metric group", test_metric_group() == 0);307 return 0;308}309 310DEFINE_SUITE("Parse and process metrics", parse_metric);311