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