xref: /aosp_15_r20/trusty/kernel/include/shared/lk/trusty_benchmark.h (revision 344aa361028b423587d4ef3fa52a23d194628137)
1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining
5  * a copy of this software and associated documentation files
6  * (the "Software"), to deal in the Software without restriction,
7  * including without limitation the rights to use, copy, modify, merge,
8  * publish, distribute, sublicense, and/or sell copies of the Software,
9  * and to permit persons to whom the Software is furnished to do so,
10  * subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be
13  * included in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  */
23 
24 /*
25  * bench functions can be defined with the macro
26  * BENCH(suite_name,bench_name,n [, params])
27  * {
28  *     ... bench function body ...
29  * }
30  *
31  *  - This body will be executed n times for each params, if 4 arguments are
32  *    given.
33  *  - This body will be executed n times, if 3 arguments are given.
34  *
35  * For a suite, one is expected to also define BENCH_SETUP, BENCH_TEARDOWN.
36  * For a 2-tuple (suite_name, bench_name) one is expected to also define at
37  * least one BENCH_RESULT.
38  *
39  * BENCH_SETUP(suite_name)
40  * {
41  *     ... bench setup body ...
42  *     return int_error_code;
43  * }
44  *
45  * BENCH_SETUP(suite_name):
46  *  - Will return 0 or NO_ERROR when it succeed.
47  *  - Will be run before every execution of the BENCH body
48  *  - Will cancel execution of the next BENCH body if returns non-zero.
49  *    Test will be considered failed.
50  *  - Will cancel execution of the next BENCH body if any ASSERT_<op> fails.
51  *    Test will be considered failed.
52  *  - All ASSERT_<op> macros from trusty_unittest can be used
53  *  - GTEST_SKIP() maybe be called to skip the benchmark run.
54  *
55  * BENCH_TEARDOWN(suite_name)
56  * {
57  *     ... bench teardown body ...
58  * }
59  *
60  * BENCH_TEARDOWN(suite_name):
61  *  - Is executed even if BENCH_SETUP failed
62  *  - Does not return any value
63  *  - All ASSERT_<op> macros from trusty_unittest can be used
64  *
65  * BENCH_RESULT(suite_name,bench_name,res_name)
66  * {
67  *     ... bench result body ...
68  *     return int64_t_value_of_res_name_for_last_bench_body_run;
69  * }
70  *
71  *
72  * BENCH_RESULT(suite_name,bench_name,res_name):
73  *  - At least one must be defined. Can define multiple times.
74  *  - Must return an int64_t
75  *  - Results will be aggregated for n runs of the BENCH( ) body.
76  *    Aggregation is grouped by params to min/max/avg of the n runs
77  *  - res_name will be used as column title for the metric summary
78  *
79  * Example:
80  *      BENCH_RESULT(hwcrypto, hwrng, time_ns) {
81  *          return bench_get_duration_ns();
82  *      }
83  *
84  * - The execution sequence is roughly:
85  *
86  *       for each param if any:
87  *          BENCH_SETUP(suite_name,bench_name)
88  *           repeat n times:
89  *               BENCH_CONTENT
90  *               for each BENCH_RESULT(suite_name,bench_name,res_name)
91  *                   update the accumulators for res_name [min,max,avg]
92  *           BENCH_TEARDOWN(suite_name,bench_name)
93  *       Print Result Table
94  *
95  * NOTE:
96  * When using a parameter array:
97  *  - params must be an array of any type T any_name_is_fine[NB_PARAMS] = {...};
98  *    The number of params is deduced from the sizeof(params)/sizeof(params[0]).
99  *    So please do not dynamically allocate T* params.
100  *  - params array name is up to the test writer
101  *
102  * The default column name for a parameter in the summary table is its index in
103  * the param array. To customize it, one can define a function with the
104  * following signature:
105  * static void trusty_bench_get_param_name_cb(char* buf, size_t buf_size,
106  * size_t param_idx);
107  *
108  * then assign it during BENCH_SETUP to the trusty_bench_get_param_name_cb
109  * global:
110  *
111  * BENCH_SETUP(suite_name) {
112  *   trusty_bench_get_param_name_cb = &get_param_name_cb;
113  *   …
114  * }
115  *
116  * trusty_bench_get_param_name_cb will be reset to NULL after teardown.
117  *
118  * See "trusty/user/app/sample/hwrng-bench/main.c" for a working and thoroughly
119  * commented example
120  */
121 
122 #pragma once
123 #include <errno.h>
124 #include <inttypes.h>
125 #include <stdarg.h>
126 #include <stdlib.h>
127 
128 #include <lib/pmu/pmu_arch.h>
129 #include <lib/unittest/unittest.h>
130 #include <trusty_log.h>
131 #include "trusty_bench_common.h"
132 #include "trusty_bench_json_print.h"
133 #include "trusty_bench_option_cb.h"
134 #include "trusty_bench_print_tables.h"
135 #include "trusty_unittest.h"
136 #ifdef TRUSTY_USERSPACE
137 #ifdef WITH_PTHREAD
138 #include <lib/thread/pthread.h>
139 #endif
140 #elif WITH_SMP
141 #include <kernel/mp.h>
142 #endif
143 #include <uapi/err.h>
144 
145 #ifdef WITH_TEST_PMU
146 #include <lib/pmu/pmu.h>
147 #endif
148 
149 /*
150  * A few helper macros for static dispatch
151  */
152 #define NB_ARGS_HELPER(_1, _2, _3, _4, _5, _6, _7, _8, N, ...) N
153 #define NB_ARGS(...) NB_ARGS_HELPER(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1, 0)
154 
155 #define CAT(a, ...) PRIMITIVE_CAT(a, __VA_ARGS__)
156 #define PRIMITIVE_CAT(a, ...) a##__VA_ARGS__
157 
158 #define EVAL(...) __VA_ARGS__
159 
160 __BEGIN_CDECLS
161 
162 /**
163  * struct benchmark_internal_state - Store internals for current bench.
164  * @last_bench_body_duration:   nanoseconds duration of the last execution of
165  *                              the bench body.
166  * @cur_param_idx:              index of current parameter in param_array.
167  * @pmu:                        state of pmu counters
168  */
169 static struct benchmark_internal_state {
170     int64_t last_bench_body_duration;
171     size_t cur_param_idx;
172 #ifdef WITH_TEST_PMU
173     struct trusty_pmu_state pmu;
174 #endif
175 } bench_state;
176 
177 /**
178  * bench_get_duration_ns - convenience function to use in BENCH_RESULT to get
179  * the duration of last bench body execution.
180  *
181  * Return: The duration of the last completed BENCH body in nanoseconds.
182  */
bench_get_duration_ns(void)183 static inline int64_t bench_get_duration_ns(void) {
184     return bench_state.last_bench_body_duration;
185 }
186 
187 /**
188  * bench_get_param_idx - convenience function to use to get the
189  * index of the current parameter BENCH_XXX is running for.
190  * Return: The index of the parameter BENCH_XXX is running for.
191  */
bench_get_param_idx(void)192 static inline size_t bench_get_param_idx(void) {
193     return bench_state.cur_param_idx % trusty_cur_bench_nb_params;
194 }
195 
196 /**
197  * bench_get_cpu_idx - convenience function to use to get the
198  * index of the current cpu BENCH_XXX is running for.
199  * Return: The index of the cpu BENCH_XXX is running for.
200  */
bench_get_cpu_idx(void)201 static inline size_t bench_get_cpu_idx(void) {
202     return bench_state.cur_param_idx / trusty_cur_bench_nb_params;
203 }
204 
205 /*
206  * Helper macros to run on tests on all CPUs
207  */
208 #if defined(TRUSTY_USERSPACE) && defined(WITH_PTHREAD)
trusty_bench_multi_cpus_setup(void)209 static int trusty_bench_multi_cpus_setup(void) {
210     if (trusty_bench_nb_cpu > 1) {
211         cpu_set_t cpu_set;
212 
213         CPU_ZERO(&cpu_set);
214         CPU_SET(bench_state.cur_param_idx / trusty_cur_bench_nb_params,
215                 &cpu_set);
216 
217         return pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t),
218                                       &cpu_set);
219     }
220     return NO_ERROR;
221 }
222 
trusty_bench_multi_cpus_teardown(void)223 static int trusty_bench_multi_cpus_teardown(void) {
224     if (trusty_bench_nb_cpu > 1) {
225         cpu_set_t cpu_set;
226 
227         CPU_ZERO(&cpu_set);
228         for (int i = 0; i < SMP_MAX_CPUS; i++) {
229             CPU_SET(i, &cpu_set);
230         }
231 
232         return pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t),
233                                       &cpu_set);
234     }
235     return NO_ERROR;
236 }
237 #elif !defined(TRUSTY_USERSPACE) && WITH_SMP
trusty_bench_multi_cpus_setup(void)238 static int trusty_bench_multi_cpus_setup(void) {
239     if (trusty_bench_nb_cpu > 1) {
240         const int cpu = bench_state.cur_param_idx / trusty_cur_bench_nb_params;
241 
242         if (cpu < SMP_MAX_CPUS && mp_is_cpu_active(cpu)) {
243             thread_set_pinned_cpu(get_current_thread(), cpu);
244         } else {
245             return EINVAL;
246         }
247     }
248 
249     return NO_ERROR;
250 }
251 
trusty_bench_multi_cpus_teardown(void)252 static int trusty_bench_multi_cpus_teardown(void) {
253     if (trusty_bench_nb_cpu > 1) {
254         thread_set_pinned_cpu(get_current_thread(), -1);
255     }
256     return NO_ERROR;
257 }
258 #else
trusty_bench_multi_cpus_setup(void)259 static int trusty_bench_multi_cpus_setup(void) {
260     return NO_ERROR;
261 }
262 
trusty_bench_multi_cpus_teardown(void)263 static int trusty_bench_multi_cpus_teardown(void) {
264     return NO_ERROR;
265 }
266 #endif
267 
268 /**
269  * trusty_bench_update_metric -  Update the appropriate metric with the value
270  * returned by BENCH_RESULT
271  * @m:              The metric whose aggregate needs to be updated.
272  * @val:            The value returned by BENCH_RESULT.
273  */
trusty_bench_update_metric(struct bench_metric_node * m,int64_t val)274 static inline void trusty_bench_update_metric(struct bench_metric_node* m,
275                                               int64_t val) {
276     m->cnt += 1;
277     m->tot += val;
278     m->aggregates[BENCH_AGGREGATE_AVG] = m->tot / m->cnt;
279     m->aggregates[BENCH_AGGREGATE_MIN] =
280             MIN(m->aggregates[BENCH_AGGREGATE_MIN], val);
281     m->aggregates[BENCH_AGGREGATE_MAX] =
282             MAX(m->aggregates[BENCH_AGGREGATE_MAX], val);
283 }
284 
285 /**
286  * trusty_bench_run_metrics -        Run All Metric Updaters after one iteration
287  * of bench function for all param/metric in the last BENCH.
288  * @metric_list:        List of metrics aggregated during all BENCH runs.
289  * @param_idx:          Index of the current parameter in the param_array of
290  *                      BENCH.
291  * @cold_run:           Are we updating metrics for the initial cold run?
292  */
trusty_bench_run_metrics(struct list_node * metric_list,size_t param_idx,bool cold_run)293 static inline void trusty_bench_run_metrics(struct list_node* metric_list,
294                                             size_t param_idx,
295                                             bool cold_run) {
296     struct bench_metric_list_node* entry;
297 
298     list_for_every_entry(metric_list, entry, struct bench_metric_list_node,
299                          node) {
300         if (param_idx == entry->param_idx) {
301             if (cold_run) {
302                 entry->metric.cold = entry->bench_result();
303             } else {
304                 trusty_bench_update_metric(&entry->metric,
305                                            entry->bench_result());
306             }
307         }
308     }
309 }
310 
311 /**
312  * trusty_bench_check_metrics -        Check if All Metric are within range
313  * after one iteration of bench function for all param/metric in the last BENCH.
314  * @metric_list:        List of metrics aggregated during all BENCH runs.
315  * @param_idx:          Index of the current parameter in the param_array of
316  *                      BENCH.
317  */
trusty_bench_check_metrics(struct list_node * metric_list,size_t param_idx)318 static inline bool trusty_bench_check_metrics(struct list_node* metric_list,
319                                               size_t param_idx) {
320     struct bench_metric_list_node* entry;
321 
322     list_for_every_entry(metric_list, entry, struct bench_metric_list_node,
323                          node) {
324         if (param_idx == entry->param_idx) {
325             if (entry->check_results_cb != NULL &&
326                 !entry->check_results_cb(entry)) {
327                 return false;
328             }
329         }
330     }
331     return true;
332 }
333 
334 /**
335  * trusty_bench_reset_metrics -        Run All Metric Updaters after one
336  * iteration of bench function for all param/metric in the last BENCH.
337  * @metric_list:        List of metrics aggregated during all BENCH runs.
338  * @param_idx:          Index of the current parameter in the param_array of
339  *                      BENCH.
340  */
trusty_bench_reset_metrics(struct list_node * metric_list,size_t param_idx)341 static inline void trusty_bench_reset_metrics(struct list_node* metric_list,
342                                               size_t param_idx) {
343     struct bench_metric_list_node* entry;
344 
345     list_for_every_entry(metric_list, entry, struct bench_metric_list_node,
346                          node) {
347         if (param_idx == entry->param_idx) {
348             trusty_bench_update_metric(&entry->metric, entry->bench_result());
349         }
350     }
351 }
352 
353 /**
354  * BENCH_SETUP -        Runs before every execution of the body of the BENCH
355  *                      macro. Can be used to allocate memory, setup 'states',
356  *                      initialize 'sessions'...
357  * @suite_name:         Identifier of the current suite.
358  */
359 #define BENCH_SETUP(suite_name)          \
360     static int suite_name##_setup(void); \
361     static int suite_name##_setup(void)
362 
363 /**
364  * BENCH_TEARDOWN -     Runs after every execution of the body of the BENCH
365  *                      macro. Can be used to free memory, clear 'states',
366  *                      close 'sessions'...
367  * @suite_name:         Identifier of the current suite.
368  */
369 #define BENCH_TEARDOWN(suite_name)           \
370     static void suite_name##_teardown(void); \
371     static void suite_name##_teardown(void)
372 
373 /**
374  * BENCH_RESULT_INNER -       Declare a metric name for the corresponding BENCH
375  * and declare the functions to update it after every iteration
376  * @suite_name:         Identifier of the current suite.
377  * @bench_name:         Unique identifier of the Bench in the suite.
378  * @metric_name:        Name of the metric to print in the result table.
379  * @formatted_value_cb: [optional] A callback of
380  *                      trusty_bench_get_formatted_value_callback_t type
381  *                      for formatting the result
382  *                      value to a string
383  * @param_name_cb:      [optional] A callback of
384  *                      trusty_bench_get_param_name_callback_t type
385  *                      for formatting the param name
386  * @check_results_cb:   [optional] A callback of
387  *                      trusty_bench_check_results_callback_t
388  *                      type for formatting the param name
389  */
390 #define BENCH_RESULT_INNER(suite_name, bench_name, metric_name,                 \
391                            formatted_value_cb_, param_name_cb_,                 \
392                            check_results_cb_)                                   \
393     static int64_t update_##suite_name##_##bench_name##_##metric_name(void);    \
394     static struct bench_metric_list_node                                        \
395             suite_name##_##bench_name##_##metric_name##_node = {                \
396                     .node = LIST_INITIAL_CLEARED_VALUE,                         \
397                     .metric = {0, 0, 0, {INT32_MAX, 0, 0}},                     \
398                     .name = STRINGIFY(metric_name),                             \
399                     .param_idx = 0,                                             \
400                     .nb_params = 0,                                             \
401                     .bench_result =                                             \
402                             update_##suite_name##_##bench_name##_##metric_name, \
403                     .formatted_value_cb = formatted_value_cb_,                  \
404                     .param_name_cb = param_name_cb_,                            \
405                     .check_results_cb = check_results_cb_};                     \
406     __attribute__((constructor)) void                                           \
407             suite_name##_##bench_name##_##metric_name##_add(void) {             \
408         list_add_tail(&suite_name##_##bench_name##_metric_list,                 \
409                       &suite_name##_##bench_name##_##metric_name##_node.node);  \
410     }                                                                           \
411                                                                                 \
412     static int64_t update_##suite_name##_##bench_name##_##metric_name(void)
413 
414 /* Dispatch Mechanics for BENCH_RESULT */
415 #define BENCH_RESULT_3(suite_name, bench_name, metric_name) \
416     BENCH_RESULT_INNER(suite_name, bench_name, metric_name, 0, 0, 0)
417 
418 #define BENCH_RESULT_4(suite_name, bench_name, metric_name, \
419                        formatted_value_cb)                  \
420     BENCH_RESULT_INNER(suite_name, bench_name, metric_name, \
421                        formatted_value_cb, 0, 0)
422 
423 #define BENCH_RESULT_5(suite_name, bench_name, metric_name, \
424                        formatted_value_cb, param_name_cb)   \
425     BENCH_RESULT_INNER(suite_name, bench_name, metric_name, \
426                        formatted_value_cb, param_name_cb, 0)
427 
428 #define BENCH_RESULT_6(suite_name, bench_name, metric_name,                 \
429                        formatted_value_cb, param_name_cb, check_results_cb) \
430     BENCH_RESULT_INNER(suite_name, bench_name, metric_name,                 \
431                        formatted_value_cb, param_name_cb, check_results_cb)
432 
433 #ifdef WITH_TEST_PMU
434 /**
435  * bench_get_pmu_cnt - convenience function to use in BENCH_RESULT to get
436  * the value of a pmu counter for the last bench body execution.
437  *
438  * Return: The value of a pmu counter of the last completed BENCH body.
439  */
bench_get_pmu_cnt(size_t idx)440 static inline int64_t bench_get_pmu_cnt(size_t idx) {
441     return bench_state.pmu.vals[idx];
442 }
443 #else
bench_get_pmu_cnt(size_t idx)444 static inline int64_t bench_get_pmu_cnt(size_t idx) {
445     return 0;
446 }
447 #endif
448 /**
449  * BENCH_RESULT             Dispatch BENCH_RESULT Called 3, 4 or 5 parameters.
450  * @suite_name:             Identifier of the current suite.
451  * @bench_name:             Unique identifier of the Bench in the suite.
452  * @metric_name:        Name of the metric to print in the result table.
453  * @formatted_value_cb:        [optional] A callback of
454  * trusty_bench_get_formatted_value_callback_t type for formatting the result
455  * value to a string
456  * @param_name_cb:              [optional] A callback of
457  * trusty_bench_get_param_name_callback_t type for formatting the param name
458  */
459 #define BENCH_RESULT(...) \
460     CAT(BENCH_RESULT_, EVAL(NB_ARGS(__VA_ARGS__)))(__VA_ARGS__)
461 
462 /**
463  * PARAM_TEST_NODES_SIMPLE -    Create the unparameterized test node lists for
464  *                              BENCH
465  * @suite_name:                 Identifier of the current suite.
466  * @bench_name:                 Unique identifier of the Bench in the suite.
467  */
468 #define PARAM_TEST_NODES_SIMPLE(suite_name, bench_name)                        \
469     static struct test_list_node suite_name##_##bench_name##_bench_##_node = { \
470             .node = LIST_INITIAL_CLEARED_VALUE,                                \
471             .suite = STRINGIFY(suite_name_),                                   \
472             .name = STRINGIFY(bench_name_),                                    \
473             .func = suite_name##_##bench_name##_bench_,                        \
474             .needs_param = 0,                                                  \
475     };                                                                         \
476                                                                                \
477     __attribute__((constructor)) void                                          \
478             suite_name##_##bench_name##_bench_##_add(void) {                   \
479         list_add_tail(&_test_list,                                             \
480                       &suite_name##_##bench_name##_bench_##_node.node);        \
481     }
482 
483 /**
484  * PARAM_TEST_NODES_PARAMETRIC -    Create the unparameterized test node lists
485  *                                  for BENCH
486  * @suite_name:                     Identifier of the current suite.
487  * @bench_name:                     Unique identifier of the Bench in the suite.
488  * @params:                         identifier of the param Array for parametric
489  * benches
490  */
491 #define PARAM_TEST_NODES_PARAMETRIC(suite_name, bench_name, params)       \
492     static struct test_list_node                                          \
493             suite_name##_##bench_name##_bench_##params##_node = {         \
494                     .node = LIST_INITIAL_CLEARED_VALUE,                   \
495                     .suite = STRINGIFY(suite_name_##params),              \
496                     .name = STRINGIFY(bench_name_##params),               \
497                     .func = suite_name##_##bench_name##_bench_##params,   \
498                     .needs_param = 0,                                     \
499     };                                                                    \
500                                                                           \
501     __attribute__((constructor)) void                                     \
502             suite_name##_##bench_name##_bench_##params##_add(void) {      \
503         list_add_tail(                                                    \
504                 &_test_list,                                              \
505                 &suite_name##_##bench_name##_bench_##params##_node.node); \
506     }
507 
508 /**
509  * set_param_metric -       Create a list of parameterized metrics out of the
510  *                          existing list of non-parameterized metric.
511  * @unparameterized_list:   List of metrics aggregated during all BENCH
512  *                          runs.
513  * @parameterized_list:     Will be filled with nb_params *
514  *                          length_of(unparameterized_list) metrics with
515  *                          appropriate param_idx value.
516  * @nb_params:              Number of parameters of the BENCH macro.
517  * Return:                  The list of parameterized metrics.
518  */
set_param_metric(struct list_node * unparameterized_list,struct list_node * parameterized_list,size_t nb_params)519 static inline struct bench_metric_list_node* set_param_metric(
520         struct list_node* unparameterized_list,
521         struct list_node* parameterized_list,
522         size_t nb_params) {
523     size_t idx = 0;
524     struct bench_metric_list_node* entry;
525     struct bench_metric_list_node* list_pool =
526             calloc(nb_params * list_length(unparameterized_list),
527                    sizeof(struct bench_metric_list_node));
528     if (list_pool == NULL) {
529         TLOGE("Failed to Allocate memory for bench_metric_list_node!");
530         return NULL;
531     }
532 
533     // clear parameterized_list from previous runs
534     struct list_node* node = NULL;
535     do {
536         node = list_remove_head(parameterized_list);
537         free(node);
538     } while (node != NULL);
539 
540     list_for_every_entry(unparameterized_list, entry,
541                          struct bench_metric_list_node, node) {
542         for (size_t idx_param = 0; idx_param < nb_params; ++idx_param) {
543             struct bench_metric_node tmp_metric = {0, 0, 0, {INT32_MAX, 0, 0}};
544 
545             list_pool[idx].metric = tmp_metric;
546             list_pool[idx].name = entry->name;
547             list_pool[idx].param_idx = idx_param;
548             list_pool[idx].nb_params = nb_params;
549             list_pool[idx].bench_result = entry->bench_result;
550             list_pool[idx].formatted_value_cb = entry->formatted_value_cb;
551             list_pool[idx].param_name_cb = entry->param_name_cb;
552             list_pool[idx].check_results_cb = entry->check_results_cb;
553             list_add_tail(parameterized_list, &(list_pool[idx].node));
554             ++idx;
555         }
556     }
557     return list_pool;
558 }
559 
560 /**
561  * trusty_bench_get_overhead - Get Minimal overhead of the benchmark around
562  * benched function
563  *
564  * Return:        The Value of the overhead in nanoseconds.
565  */
trusty_bench_get_overhead(void)566 static int64_t trusty_bench_get_overhead(void) {
567     const size_t nb_runs = 100;
568     int64_t start_time;
569     int64_t end_time;
570     int64_t res = INT64_MAX;
571 
572     for (size_t i = 0; i < nb_runs; ++i) {
573         start_time = get_current_time_ns();
574         end_time = get_current_time_ns();
575         res = MIN(end_time - start_time, res);
576     }
577     return res;
578 }
579 
580 /**
581  * get_extended_bench_name - Print Status of Currently Running Bench.
582  *
583  * @test_name_in:   Name of the Current Unparameterized Test.
584  * @test_name_out:  Name of the Current Unparameterized Test.
585  *                  + "_[param_idx]"
586  *
587  * Return:          When successful, returns 0
588  *                  If test_name_out allocation/print failed returns asprintf
589  *                  return code
590  */
get_extended_bench_name(const char * test_name_in,char ** test_name_out)591 static inline int get_extended_bench_name(const char* test_name_in,
592                                           char** test_name_out) {
593     int res = snprintf(NULL, 0, "%s_%zu", test_name_in,
594                        bench_state.cur_param_idx);
595     *test_name_out = NULL;
596     if (res >= 0) {
597         *test_name_out = malloc(res + 1);
598         res = snprintf(*test_name_out, res + 1, "%s_%zu", test_name_in,
599                        bench_state.cur_param_idx);
600     }
601     if (res < 0) {
602         return res;
603     }
604     if (!test_name_out) {
605         TLOGE("Cannot Allocate memory for test name\n");
606         return -1;
607     }
608     return 0;
609 }
610 
611 #ifdef WITH_TEST_PMU
612 #define BENCH_INIT_PMU(evt_arr) \
613     init_pmu_state(evt_arr, countof(evt_arr), &bench_state.pmu)
614 #define PMU_START() pmu_start(&bench_state.pmu);
615 #define PMU_STOP() pmu_stop(&bench_state.pmu);
616 #define RESET_PMU() reset_pmu_cnts(&bench_state.pmu)
617 #define CLEAN_PMU() clean_pmu(&bench_state.pmu)
618 #else
619 #define BENCH_INIT_PMU(evt_arr) (void)(evt_arr)
620 #define PMU_START()
621 #define PMU_STOP()
622 #define RESET_PMU()
623 #define CLEAN_PMU()
624 #endif
625 
626 /**
627  * BENCH_CORE -             Called by both parametrized and unparameterized
628  * BENCH for their common part
629  * @suite_name:             Identifier of the current suite.
630  * @bench_name:             Unique identifier of the Bench in the suite.
631  * @nb_runs:                The number of execution of its body for each param
632  * @nb_params:              Number of params in params array
633  * @params:                 An array T array_name[nb_params] of parameter
634  * @metric_list:            List of metric nodes to update
635  */
636 #define BENCH_CORE(suite_name, bench_name, nb_runs, nb_params, params,          \
637                    metric_list)                                                 \
638     reset_vertical_print_widths();                                              \
639     trusty_bench_print_title(STRINGIFY(suite_name), STRINGIFY(bench_name),      \
640                              STRINGIFY(params));                                \
641     static trusty_bench_print_callback_t trusty_bench_print_cb =                \
642             &BENCHMARK_PRINT_CB;                                                \
643     trusty_cur_bench_nb_params = nb_params;                                     \
644     for (size_t idx_param = 0; idx_param < (nb_params * trusty_bench_nb_cpu);   \
645          ++idx_param) {                                                         \
646         bench_state.cur_param_idx = idx_param;                                  \
647         char* extended_test_name = NULL;                                        \
648         int res_alloc = get_extended_bench_name(                                \
649                 STRINGIFY(bench_name##_##params), &extended_test_name);         \
650         if (res_alloc < 0) {                                                    \
651             TLOGE("ERROR %d expanding test name\n", res_alloc);                 \
652             _test_context.all_ok = false;                                       \
653             _test_context.tests_failed++;                                       \
654             continue;                                                           \
655         }                                                                       \
656         TEST_BEGIN_FUNC(STRINGIFY(suite_name), extended_test_name);             \
657                                                                                 \
658         int rc = trusty_bench_multi_cpus_setup();                               \
659         if (rc != NO_ERROR) {                                                   \
660             _test_context.skipped = true;                                       \
661             _test_context.tests_skipped++;                                      \
662         } else {                                                                \
663             rc = suite_name##_setup();                                          \
664         }                                                                       \
665                                                                                 \
666         if (_test_context.skipped) {                                            \
667             trusty_unittest_print_status(" SKIPPED");                           \
668             continue;                                                           \
669         } else if (rc != NO_ERROR) {                                            \
670             TLOGE("ERROR %d during benchmark setup\n", rc);                     \
671             _test_context.all_ok = false;                                       \
672             _test_context.tests_failed++;                                       \
673             continue;                                                           \
674         }                                                                       \
675         int64_t overhead = trusty_bench_get_overhead();                         \
676                                                                                 \
677         PMU_START();                                                            \
678         /* Cold Run */                                                          \
679         int64_t start_time;                                                     \
680         int64_t end_time;                                                       \
681         start_time = get_current_time_ns();                                     \
682         int64_t res = suite_name##_##bench_name##_inner_##params();             \
683         end_time = get_current_time_ns();                                       \
684                                                                                 \
685         PMU_STOP();                                                             \
686                                                                                 \
687         if (res != NO_ERROR) {                                                  \
688             TLOGE("ERROR During Cold Run%" PRId64 "\n", res);                   \
689             _test_context.all_ok = false;                                       \
690             _test_context.tests_failed++;                                       \
691             continue;                                                           \
692         }                                                                       \
693                                                                                 \
694         bench_state.last_bench_body_duration = end_time - start_time;           \
695         if (5 * overhead >= bench_state.last_bench_body_duration) {             \
696             trusty_unittest_printf(                                             \
697                     "WARNING: Benchmark internal function is too fast %" PRId64 \
698                     "ns, while the benchmark overhead is %" PRId64 "ns.",       \
699                     overhead, bench_state.last_bench_body_duration);            \
700         }                                                                       \
701                                                                                 \
702         bench_state.last_bench_body_duration -= overhead;                       \
703                                                                                 \
704         if (!_test_context.hard_fail && _test_context.all_ok) {                 \
705             trusty_bench_run_metrics(&metric_list, idx_param, true);            \
706             RESET_PMU();                                                        \
707         }                                                                       \
708                                                                                 \
709         for (size_t idx_run = 0; idx_run < nb_runs; ++idx_run) {                \
710             if (!_test_context.hard_fail && _test_context.all_ok) {             \
711                 PMU_START();                                                    \
712                 start_time = get_current_time_ns();                             \
713                 res = suite_name##_##bench_name##_inner_##params();             \
714                 end_time = get_current_time_ns();                               \
715                 PMU_STOP();                                                     \
716                                                                                 \
717                 bench_state.last_bench_body_duration = end_time - start_time;   \
718                 if (overhead >= bench_state.last_bench_body_duration) {         \
719                     TLOGE("Benchmark internal function is too fast %" PRId64    \
720                           "ns, while the benchmark overhead is %" PRId64        \
721                           "ns.",                                                \
722                           overhead, bench_state.last_bench_body_duration);      \
723                 }                                                               \
724                                                                                 \
725                 bench_state.last_bench_body_duration -= overhead;               \
726                 if (res != NO_ERROR) {                                          \
727                     TLOGE("ERROR %" PRId64 "\n", res);                          \
728                 }                                                               \
729             }                                                                   \
730             if (!_test_context.hard_fail && _test_context.all_ok) {             \
731                 trusty_bench_run_metrics(&metric_list, idx_param, false);       \
732                 RESET_PMU();                                                    \
733             }                                                                   \
734         }                                                                       \
735         suite_name##_teardown();                                                \
736         rc = trusty_bench_multi_cpus_teardown();                                \
737         if (rc != NO_ERROR) {                                                   \
738             TLOGW("failed to reset CPU affinity: %d\n", rc);                    \
739         }                                                                       \
740                                                                                 \
741         if (!trusty_bench_check_metrics(&metric_list, idx_param)) {             \
742             _test_context.all_ok = false;                                       \
743             _test_context.tests_failed++;                                       \
744         }                                                                       \
745         TEST_END_FUNC();                                                        \
746         free(extended_test_name);                                               \
747         extended_test_name = NULL;                                              \
748     }                                                                           \
749     trusty_bench_print_cb(&metric_list, (nb_params * trusty_bench_nb_cpu),      \
750                           STRINGIFY(suite_name),                                \
751                           STRINGIFY(bench_name##_##params));                    \
752     trusty_bench_get_param_name_cb = NULL;                                      \
753     trusty_bench_get_formatted_value_cb = NULL;                                 \
754     CLEAN_PMU();
755 
756 /**
757  * BENCH_PARAMETERIZED_PTR -Called when BENCH has 5 parameters. This allows
758  *                          to reuse Other macros for different bench by
759  * aliasing an array to a pointer
760  * @suite_name:             Identifier of the current suite.
761  * @bench_name:             Unique identifier of the Bench in the suite.
762  * @nb_runs:                The number of execution of its body for each param
763  * @params:                 An array T array_name[nb_params] of parameter
764  * @nb_params:              Number of parameters in the parameter Array
765  */
766 #define BENCH_PARAMETERIZED_PTR(nb_cpu, suite_name, bench_name, nb_runs,         \
767                                 params, nb_params)                               \
768     static int suite_name##_##bench_name##_inner_##params(void);                 \
769     static void suite_name##_##bench_name##_bench_##params(void);                \
770     static struct list_node suite_name##_##bench_name##_metric_list =            \
771             LIST_INITIAL_VALUE(suite_name##_##bench_name##_metric_list);         \
772     static struct list_node suite_name##_##bench_name##_metric_##params##_list = \
773             LIST_INITIAL_VALUE(                                                  \
774                     suite_name##_##bench_name##_metric_##params##_list);         \
775                                                                                  \
776     static void suite_name##_##bench_name##_bench_##params(void) {               \
777         trusty_bench_nb_cpu = nb_cpu;                                            \
778         struct bench_metric_list_node* metric_pool = set_param_metric(           \
779                 &suite_name##_##bench_name##_metric_list,                        \
780                 &suite_name##_##bench_name##_metric_##params##_list,             \
781                 (nb_params * trusty_bench_nb_cpu));                              \
782         if (metric_pool == NULL) {                                               \
783             _test_context.hard_fail = true;                                      \
784             return;                                                              \
785         }                                                                        \
786         BENCH_CORE(suite_name, bench_name, nb_runs, nb_params, params,           \
787                    suite_name##_##bench_name##_metric_##params##_list);          \
788         free(metric_pool);                                                       \
789     }                                                                            \
790     PARAM_TEST_NODES(suite_name, bench_name, params)                             \
791                                                                                  \
792     static int suite_name##_##bench_name##_inner_##params(void)
793 
794 /**
795  * BENCH_PARAMETERIZED -    Called when BENCH has 4 parameters
796  * @suite_name:             Identifier of the current suite.
797  * @bench_name:             Unique identifier of the Bench in the suite.
798  * @nb_runs:                The number of execution of its body for each param
799  * @params:                 An array T array_name[nb_params] of parameter
800  */
801 #define BENCH_PARAMETERIZED(nb_cpu, suite_name, bench_name, nb_runs, params) \
802     BENCH_PARAMETERIZED_PTR(nb_cpu, suite_name, bench_name, nb_runs, params, \
803                             countof(params))
804 
805 /**
806  * BENCH_SIMPLE -       Called when BENCH has only 3 parameters.
807  * @suite_name:         Identifier of the current suite.
808  * @bench_name:         Unique identifier of the Bench in the suite.
809  * @nb_runs:            The number of execution of its body.
810  */
811 #define BENCH_SIMPLE(nb_cpu, suite_name, bench_name, nb_runs)                  \
812     static int suite_name##_##bench_name##_inner_(void);                       \
813     static void suite_name##_##bench_name##_bench_(void);                      \
814     static struct list_node suite_name##_##bench_name##_metric_list =          \
815             LIST_INITIAL_VALUE(suite_name##_##bench_name##_metric_list);       \
816     static struct list_node suite_name##_##bench_name##_metric_cpu##_list =    \
817             LIST_INITIAL_VALUE(suite_name##_##bench_name##_metric_cpu##_list); \
818     static void suite_name##_##bench_name##_bench_(void) {                     \
819         bench_state.cur_param_idx = 0;                                         \
820         trusty_bench_nb_cpu = nb_cpu;                                          \
821         struct bench_metric_list_node* metric_pool = set_param_metric(         \
822                 &suite_name##_##bench_name##_metric_list,                      \
823                 &suite_name##_##bench_name##_metric_cpu##_list,                \
824                 trusty_bench_nb_cpu);                                          \
825         if (metric_pool == NULL) {                                             \
826             _test_context.hard_fail = true;                                    \
827             return;                                                            \
828         }                                                                      \
829         BENCH_CORE(suite_name, bench_name, nb_runs, 1, ,                       \
830                    suite_name##_##bench_name##_metric_cpu##_list);             \
831     }                                                                          \
832                                                                                \
833     PARAM_TEST_NODES(suite_name, bench_name)                                   \
834     static int suite_name##_##bench_name##_inner_(void)
835 
836 /*
837  * BENCH - Routing the BENCH macros depending on its number of parameters.
838  */
839 #define BENCH_3 BENCH_SIMPLE
840 #define BENCH_4 BENCH_PARAMETERIZED
841 #define BENCH_5 BENCH_PARAMETERIZED_PTR
842 
843 /**
844  * BENCH - Called 3, 4 or 5 parameters. This allows
845  *                          to reuse Other macros for different bench by
846  * aliasing an array to a pointer
847  * @suite_name:             Identifier of the current suite.
848  * @bench_name:             Unique identifier of the Bench in the suite.
849  * @nb_runs:                The number of execution of its body for each param
850  * @params:                 [optional] An array T array_name[nb_params] of
851  *                          parameter, or a pointer T*, in the latter case a 5th
852  *                          parameter is needed
853  * @nb_params:              [optional] if 4th parameter is a pointer, Number of
854  *                          parameters in the parameter Array
855  */
856 #define BENCH(...) CAT(BENCH_, EVAL(NB_ARGS(__VA_ARGS__)))(1, __VA_ARGS__)
857 
858 /**
859  * BENCH_ALL_CPU - Called 3, 4 or 5 parameters. This allows
860  *                          to reuse Other macros for different bench by
861  * aliasing an array to a pointer
862  * @suite_name:             Identifier of the current suite.
863  * @bench_name:             Unique identifier of the Bench in the suite.
864  * @nb_runs:                The number of execution of its body for each param
865  * @params:                 [optional] An array T array_name[nb_params] of
866  *                          parameter, or a pointer T*, in the latter case a 5th
867  *                          parameter is needed
868  * @nb_params:              [optional] if 4th parameter is a pointer, Number of
869  *                          parameters in the parameter Array
870  */
871 #define BENCH_ALL_CPU(...) \
872     CAT(BENCH_, EVAL(NB_ARGS(__VA_ARGS__)))(SMP_MAX_CPUS, __VA_ARGS__)
873 
874 /*
875  * PARAM_TEST_NODES - Routing the PARAM_TEST_NODES macros depending on its
876  * number of parameters.
877  */
878 #define PARAM_TEST_NODES_2 PARAM_TEST_NODES_SIMPLE
879 #define PARAM_TEST_NODES_3 PARAM_TEST_NODES_PARAMETRIC
880 #define PARAM_TEST_NODES(...) \
881     CAT(PARAM_TEST_NODES_, EVAL(NB_ARGS(__VA_ARGS__)))(__VA_ARGS__)
882 
883 __END_CDECLS
884