Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 #include <linux/compiler.h>
0003 #include <string.h>
0004 #include <perf/cpumap.h>
0005 #include <perf/evlist.h>
0006 #include "metricgroup.h"
0007 #include "tests.h"
0008 #include "pmu-events/pmu-events.h"
0009 #include "evlist.h"
0010 #include "rblist.h"
0011 #include "debug.h"
0012 #include "expr.h"
0013 #include "stat.h"
0014 #include "pmu.h"
0015 
0016 struct value {
0017     const char  *event;
0018     u64      val;
0019 };
0020 
0021 static u64 find_value(const char *name, struct value *values)
0022 {
0023     struct value *v = values;
0024 
0025     while (v->event) {
0026         if (!strcmp(name, v->event))
0027             return v->val;
0028         v++;
0029     }
0030     return 0;
0031 }
0032 
0033 static void load_runtime_stat(struct runtime_stat *st, struct evlist *evlist,
0034                   struct value *vals)
0035 {
0036     struct evsel *evsel;
0037     u64 count;
0038 
0039     perf_stat__reset_shadow_stats();
0040     evlist__for_each_entry(evlist, evsel) {
0041         count = find_value(evsel->name, vals);
0042         perf_stat__update_shadow_stats(evsel, count, 0, st);
0043         if (!strcmp(evsel->name, "duration_time"))
0044             update_stats(&walltime_nsecs_stats, count);
0045     }
0046 }
0047 
0048 static double compute_single(struct rblist *metric_events, struct evlist *evlist,
0049                  struct runtime_stat *st, const char *name)
0050 {
0051     struct metric_expr *mexp;
0052     struct metric_event *me;
0053     struct evsel *evsel;
0054 
0055     evlist__for_each_entry(evlist, evsel) {
0056         me = metricgroup__lookup(metric_events, evsel, false);
0057         if (me != NULL) {
0058             list_for_each_entry (mexp, &me->head, nd) {
0059                 if (strcmp(mexp->metric_name, name))
0060                     continue;
0061                 return test_generic_metric(mexp, 0, st);
0062             }
0063         }
0064     }
0065     return 0.;
0066 }
0067 
0068 static int __compute_metric(const char *name, struct value *vals,
0069                 const char *name1, double *ratio1,
0070                 const char *name2, double *ratio2)
0071 {
0072     struct rblist metric_events = {
0073         .nr_entries = 0,
0074     };
0075     const struct pmu_events_table *pme_test;
0076     struct perf_cpu_map *cpus;
0077     struct runtime_stat st;
0078     struct evlist *evlist;
0079     int err;
0080 
0081     /*
0082      * We need to prepare evlist for stat mode running on CPU 0
0083      * because that's where all the stats are going to be created.
0084      */
0085     evlist = evlist__new();
0086     if (!evlist)
0087         return -ENOMEM;
0088 
0089     cpus = perf_cpu_map__new("0");
0090     if (!cpus) {
0091         evlist__delete(evlist);
0092         return -ENOMEM;
0093     }
0094 
0095     perf_evlist__set_maps(&evlist->core, cpus, NULL);
0096     runtime_stat__init(&st);
0097 
0098     /* Parse the metric into metric_events list. */
0099     pme_test = find_core_events_table("testarch", "testcpu");
0100     err = metricgroup__parse_groups_test(evlist, pme_test, name,
0101                          false, false,
0102                          &metric_events);
0103     if (err)
0104         goto out;
0105 
0106     err = evlist__alloc_stats(evlist, false);
0107     if (err)
0108         goto out;
0109 
0110     /* Load the runtime stats with given numbers for events. */
0111     load_runtime_stat(&st, evlist, vals);
0112 
0113     /* And execute the metric */
0114     if (name1 && ratio1)
0115         *ratio1 = compute_single(&metric_events, evlist, &st, name1);
0116     if (name2 && ratio2)
0117         *ratio2 = compute_single(&metric_events, evlist, &st, name2);
0118 
0119 out:
0120     /* ... cleanup. */
0121     metricgroup__rblist_exit(&metric_events);
0122     runtime_stat__exit(&st);
0123     evlist__free_stats(evlist);
0124     perf_cpu_map__put(cpus);
0125     evlist__delete(evlist);
0126     return err;
0127 }
0128 
0129 static int compute_metric(const char *name, struct value *vals, double *ratio)
0130 {
0131     return __compute_metric(name, vals, name, ratio, NULL, NULL);
0132 }
0133 
0134 static int compute_metric_group(const char *name, struct value *vals,
0135                 const char *name1, double *ratio1,
0136                 const char *name2, double *ratio2)
0137 {
0138     return __compute_metric(name, vals, name1, ratio1, name2, ratio2);
0139 }
0140 
0141 static int test_ipc(void)
0142 {
0143     double ratio;
0144     struct value vals[] = {
0145         { .event = "inst_retired.any",        .val = 300 },
0146         { .event = "cpu_clk_unhalted.thread", .val = 200 },
0147         { .event = NULL, },
0148     };
0149 
0150     TEST_ASSERT_VAL("failed to compute metric",
0151             compute_metric("IPC", vals, &ratio) == 0);
0152 
0153     TEST_ASSERT_VAL("IPC failed, wrong ratio",
0154             ratio == 1.5);
0155     return 0;
0156 }
0157 
0158 static int test_frontend(void)
0159 {
0160     double ratio;
0161     struct value vals[] = {
0162         { .event = "idq_uops_not_delivered.core",        .val = 300 },
0163         { .event = "cpu_clk_unhalted.thread",            .val = 200 },
0164         { .event = "cpu_clk_unhalted.one_thread_active", .val = 400 },
0165         { .event = "cpu_clk_unhalted.ref_xclk",          .val = 600 },
0166         { .event = NULL, },
0167     };
0168 
0169     TEST_ASSERT_VAL("failed to compute metric",
0170             compute_metric("Frontend_Bound_SMT", vals, &ratio) == 0);
0171 
0172     TEST_ASSERT_VAL("Frontend_Bound_SMT failed, wrong ratio",
0173             ratio == 0.45);
0174     return 0;
0175 }
0176 
0177 static int test_cache_miss_cycles(void)
0178 {
0179     double ratio;
0180     struct value vals[] = {
0181         { .event = "l1d-loads-misses",  .val = 300 },
0182         { .event = "l1i-loads-misses",  .val = 200 },
0183         { .event = "inst_retired.any",  .val = 400 },
0184         { .event = NULL, },
0185     };
0186 
0187     TEST_ASSERT_VAL("failed to compute metric",
0188             compute_metric("cache_miss_cycles", vals, &ratio) == 0);
0189 
0190     TEST_ASSERT_VAL("cache_miss_cycles failed, wrong ratio",
0191             ratio == 1.25);
0192     return 0;
0193 }
0194 
0195 
0196 /*
0197  * DCache_L2_All_Hits = l2_rqsts.demand_data_rd_hit + l2_rqsts.pf_hit + l2_rqsts.rfo_hi
0198  * DCache_L2_All_Miss = max(l2_rqsts.all_demand_data_rd - l2_rqsts.demand_data_rd_hit, 0) +
0199  *                      l2_rqsts.pf_miss + l2_rqsts.rfo_miss
0200  * DCache_L2_All      = dcache_l2_all_hits + dcache_l2_all_miss
0201  * DCache_L2_Hits     = d_ratio(dcache_l2_all_hits, dcache_l2_all)
0202  * DCache_L2_Misses   = d_ratio(dcache_l2_all_miss, dcache_l2_all)
0203  *
0204  * l2_rqsts.demand_data_rd_hit = 100
0205  * l2_rqsts.pf_hit             = 200
0206  * l2_rqsts.rfo_hi             = 300
0207  * l2_rqsts.all_demand_data_rd = 400
0208  * l2_rqsts.pf_miss            = 500
0209  * l2_rqsts.rfo_miss           = 600
0210  *
0211  * DCache_L2_All_Hits = 600
0212  * DCache_L2_All_Miss = MAX(400 - 100, 0) + 500 + 600 = 1400
0213  * DCache_L2_All      = 600 + 1400  = 2000
0214  * DCache_L2_Hits     = 600 / 2000  = 0.3
0215  * DCache_L2_Misses   = 1400 / 2000 = 0.7
0216  */
0217 static int test_dcache_l2(void)
0218 {
0219     double ratio;
0220     struct value vals[] = {
0221         { .event = "l2_rqsts.demand_data_rd_hit", .val = 100 },
0222         { .event = "l2_rqsts.pf_hit",             .val = 200 },
0223         { .event = "l2_rqsts.rfo_hit",            .val = 300 },
0224         { .event = "l2_rqsts.all_demand_data_rd", .val = 400 },
0225         { .event = "l2_rqsts.pf_miss",            .val = 500 },
0226         { .event = "l2_rqsts.rfo_miss",           .val = 600 },
0227         { .event = NULL, },
0228     };
0229 
0230     TEST_ASSERT_VAL("failed to compute metric",
0231             compute_metric("DCache_L2_Hits", vals, &ratio) == 0);
0232 
0233     TEST_ASSERT_VAL("DCache_L2_Hits failed, wrong ratio",
0234             ratio == 0.3);
0235 
0236     TEST_ASSERT_VAL("failed to compute metric",
0237             compute_metric("DCache_L2_Misses", vals, &ratio) == 0);
0238 
0239     TEST_ASSERT_VAL("DCache_L2_Misses failed, wrong ratio",
0240             ratio == 0.7);
0241     return 0;
0242 }
0243 
0244 static int test_recursion_fail(void)
0245 {
0246     double ratio;
0247     struct value vals[] = {
0248         { .event = "inst_retired.any",        .val = 300 },
0249         { .event = "cpu_clk_unhalted.thread", .val = 200 },
0250         { .event = NULL, },
0251     };
0252 
0253     TEST_ASSERT_VAL("failed to find recursion",
0254             compute_metric("M1", vals, &ratio) == -1);
0255 
0256     TEST_ASSERT_VAL("failed to find recursion",
0257             compute_metric("M3", vals, &ratio) == -1);
0258     return 0;
0259 }
0260 
0261 static int test_memory_bandwidth(void)
0262 {
0263     double ratio;
0264     struct value vals[] = {
0265         { .event = "l1d.replacement", .val = 4000000 },
0266         { .event = "duration_time",  .val = 200000000 },
0267         { .event = NULL, },
0268     };
0269 
0270     TEST_ASSERT_VAL("failed to compute metric",
0271             compute_metric("L1D_Cache_Fill_BW", vals, &ratio) == 0);
0272     TEST_ASSERT_VAL("L1D_Cache_Fill_BW, wrong ratio",
0273             1.28 == ratio);
0274 
0275     return 0;
0276 }
0277 
0278 static int test_metric_group(void)
0279 {
0280     double ratio1, ratio2;
0281     struct value vals[] = {
0282         { .event = "cpu_clk_unhalted.thread", .val = 200 },
0283         { .event = "l1d-loads-misses",        .val = 300 },
0284         { .event = "l1i-loads-misses",        .val = 200 },
0285         { .event = "inst_retired.any",        .val = 400 },
0286         { .event = NULL, },
0287     };
0288 
0289     TEST_ASSERT_VAL("failed to find recursion",
0290             compute_metric_group("group1", vals,
0291                          "IPC", &ratio1,
0292                          "cache_miss_cycles", &ratio2) == 0);
0293 
0294     TEST_ASSERT_VAL("group IPC failed, wrong ratio",
0295             ratio1 == 2.0);
0296 
0297     TEST_ASSERT_VAL("group cache_miss_cycles failed, wrong ratio",
0298             ratio2 == 1.25);
0299     return 0;
0300 }
0301 
0302 static int test__parse_metric(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
0303 {
0304     TEST_ASSERT_VAL("IPC failed", test_ipc() == 0);
0305     TEST_ASSERT_VAL("frontend failed", test_frontend() == 0);
0306     TEST_ASSERT_VAL("DCache_L2 failed", test_dcache_l2() == 0);
0307     TEST_ASSERT_VAL("recursion fail failed", test_recursion_fail() == 0);
0308     TEST_ASSERT_VAL("Memory bandwidth", test_memory_bandwidth() == 0);
0309 
0310     if (!perf_pmu__has_hybrid()) {
0311         TEST_ASSERT_VAL("cache_miss_cycles failed", test_cache_miss_cycles() == 0);
0312         TEST_ASSERT_VAL("test metric group", test_metric_group() == 0);
0313     }
0314     return 0;
0315 }
0316 
0317 DEFINE_SUITE("Parse and process metrics", parse_metric);