blob: 8a1019cb99c9e41e4c75cbdd43a3aefa28b37bec [file]
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/*
* bench functions can be defined with the macro
* BENCH(suite_name,bench_name,n [, params])
* {
* ... bench function body ...
* }
*
* - This body will be executed n times for each params, if 4 arguments are
* given.
* - This body will be executed n times, if 3 arguments are given.
*
* For a suite, one is expected to also define BENCH_SETUP, BENCH_TEARDOWN.
* For a 2-tuple (suite_name, bench_name) one is expected to also define at
* least one BENCH_RESULT.
*
* BENCH_SETUP(suite_name)
* {
* ... bench setup body ...
* return int_error_code;
* }
*
* BENCH_SETUP(suite_name):
* - Will return 0 or NO_ERROR when it succeed.
* - Will be run before every execution of the BENCH body
* - Will cancel execution of the next BENCH body if returns non-zero.
* Test will be considered failed.
* - Will cancel execution of the next BENCH body if any ASSERT_<op> fails.
* Test will be considered failed.
* - All ASSERT_<op> macros from trusty_unittest can be used
* - GTEST_SKIP() maybe be called to skip the benchmark run.
*
* BENCH_TEARDOWN(suite_name)
* {
* ... bench teardown body ...
* }
*
* BENCH_TEARDOWN(suite_name):
* - Is executed even if BENCH_SETUP failed
* - Does not return any value
* - All ASSERT_<op> macros from trusty_unittest can be used
*
* BENCH_RESULT(suite_name,bench_name,res_name)
* {
* ... bench result body ...
* return int64_t_value_of_res_name_for_last_bench_body_run;
* }
*
*
* BENCH_RESULT(suite_name,bench_name,res_name):
* - At least one must be defined. Can define multiple times.
* - Must return an int64_t
* - Results will be aggregated for n runs of the BENCH( ) body.
* Aggregation is grouped by params to min/max/avg of the n runs
* - res_name will be used as column title for the metric summary
*
* Example:
* BENCH_RESULT(hwcrypto, hwrng, time_ns) {
* return bench_get_duration_ns();
* }
*
* - The execution sequence is roughly:
*
* for each param if any:
* BENCH_SETUP(suite_name,bench_name)
* repeat n times:
* BENCH_CONTENT
* for each BENCH_RESULT(suite_name,bench_name,res_name)
* update the accumulators for res_name [min,max,avg]
* BENCH_TEARDOWN(suite_name,bench_name)
* Print Result Table
*
* NOTE:
* When using a parameter array:
* - params must be an array of any type T any_name_is_fine[NB_PARAMS] = {...};
* The number of params is deduced from the sizeof(params)/sizeof(params[0]).
* So please do not dynamically allocate T* params.
* - params array name is up to the test writer
*
* The default column name for a parameter in the summary table is its index in
* the param array. To customize it, one can define a function with the
* following signature:
* static void trusty_bench_get_param_name_cb(char* buf, size_t buf_size,
* size_t param_idx);
*
* then assign it during BENCH_SETUP to the trusty_bench_get_param_name_cb
* global:
*
* BENCH_SETUP(suite_name) {
* trusty_bench_get_param_name_cb = &get_param_name_cb;
* …
* }
*
* trusty_bench_get_param_name_cb will be reset to NULL after teardown.
*
* See "trusty/user/app/sample/hwrng-bench/main.c" for a working and thoroughly
* commented example
*/
#pragma once
#include <errno.h>
#include <inttypes.h>
#include <stdarg.h>
#include <stdlib.h>
#include <lib/pmu/pmu_arch.h>
#include <lib/unittest/unittest.h>
#include <trusty_log.h>
#include "trusty_bench_common.h"
#include "trusty_bench_json_print.h"
#include "trusty_bench_option_cb.h"
#include "trusty_bench_print_tables.h"
#include "trusty_unittest.h"
#ifdef TRUSTY_USERSPACE
#ifdef WITH_PTHREAD
#include <lib/thread/pthread.h>
#endif
#elif WITH_SMP
#include <kernel/mp.h>
#endif
#include <uapi/err.h>
#ifdef WITH_TEST_PMU
#include <lib/pmu/pmu.h>
#endif
/*
* A few helper macros for static dispatch
*/
#define NB_ARGS_HELPER(_1, _2, _3, _4, _5, _6, _7, _8, N, ...) N
#define NB_ARGS(...) NB_ARGS_HELPER(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define CAT(a, ...) PRIMITIVE_CAT(a, __VA_ARGS__)
#define PRIMITIVE_CAT(a, ...) a##__VA_ARGS__
#define EVAL(...) __VA_ARGS__
__BEGIN_CDECLS
/**
* struct benchmark_internal_state - Store internals for current bench.
* @last_bench_body_duration: nanoseconds duration of the last execution of
* the bench body.
* @cur_param_idx: index of current parameter in param_array.
* @pmu: state of pmu counters
*/
static struct benchmark_internal_state {
int64_t last_bench_body_duration;
size_t cur_param_idx;
#ifdef WITH_TEST_PMU
struct trusty_pmu_state pmu;
#endif
} bench_state;
/**
* bench_get_duration_ns - convenience function to use in BENCH_RESULT to get
* the duration of last bench body execution.
*
* Return: The duration of the last completed BENCH body in nanoseconds.
*/
static inline int64_t bench_get_duration_ns(void) {
return bench_state.last_bench_body_duration;
}
/**
* bench_get_param_idx - convenience function to use to get the
* index of the current parameter BENCH_XXX is running for.
* Return: The index of the parameter BENCH_XXX is running for.
*/
static inline size_t bench_get_param_idx(void) {
return bench_state.cur_param_idx % trusty_cur_bench_nb_params;
}
/**
* bench_get_cpu_idx - convenience function to use to get the
* index of the current cpu BENCH_XXX is running for.
* Return: The index of the cpu BENCH_XXX is running for.
*/
static inline size_t bench_get_cpu_idx(void) {
return bench_state.cur_param_idx / trusty_cur_bench_nb_params;
}
/*
* Helper macros to run on tests on all CPUs
*/
#if defined(TRUSTY_USERSPACE) && defined(WITH_PTHREAD)
static int trusty_bench_multi_cpus_setup(void) {
if (trusty_bench_nb_cpu > 1) {
cpu_set_t cpu_set;
CPU_ZERO(&cpu_set);
CPU_SET(bench_state.cur_param_idx / trusty_cur_bench_nb_params,
&cpu_set);
return pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t),
&cpu_set);
}
return NO_ERROR;
}
static int trusty_bench_multi_cpus_teardown(void) {
if (trusty_bench_nb_cpu > 1) {
cpu_set_t cpu_set;
CPU_ZERO(&cpu_set);
for (int i = 0; i < SMP_MAX_CPUS; i++) {
CPU_SET(i, &cpu_set);
}
return pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t),
&cpu_set);
}
return NO_ERROR;
}
#elif !defined(TRUSTY_USERSPACE) && WITH_SMP
static int trusty_bench_multi_cpus_setup(void) {
if (trusty_bench_nb_cpu > 1) {
const int cpu = bench_state.cur_param_idx / trusty_cur_bench_nb_params;
if (cpu < SMP_MAX_CPUS && mp_is_cpu_active(cpu)) {
thread_set_pinned_cpu(get_current_thread(), cpu);
} else {
return EINVAL;
}
}
return NO_ERROR;
}
static int trusty_bench_multi_cpus_teardown(void) {
if (trusty_bench_nb_cpu > 1) {
thread_set_pinned_cpu(get_current_thread(), -1);
}
return NO_ERROR;
}
#else
static int trusty_bench_multi_cpus_setup(void) {
return NO_ERROR;
}
static int trusty_bench_multi_cpus_teardown(void) {
return NO_ERROR;
}
#endif
/**
* trusty_bench_update_metric - Update the appropriate metric with the value
* returned by BENCH_RESULT
* @m: The metric whose aggregate needs to be updated.
* @val: The value returned by BENCH_RESULT.
*/
static inline void trusty_bench_update_metric(struct bench_metric_node* m,
int64_t val) {
m->cnt += 1;
m->tot += val;
m->aggregates[BENCH_AGGREGATE_AVG] = m->tot / m->cnt;
m->aggregates[BENCH_AGGREGATE_MIN] =
MIN(m->aggregates[BENCH_AGGREGATE_MIN], val);
m->aggregates[BENCH_AGGREGATE_MAX] =
MAX(m->aggregates[BENCH_AGGREGATE_MAX], val);
}
/**
* trusty_bench_run_metrics - Run All Metric Updaters after one iteration
* of bench function for all param/metric in the last BENCH.
* @metric_list: List of metrics aggregated during all BENCH runs.
* @param_idx: Index of the current parameter in the param_array of
* BENCH.
* @cold_run: Are we updating metrics for the initial cold run?
*/
static inline void trusty_bench_run_metrics(struct list_node* metric_list,
size_t param_idx,
bool cold_run) {
struct bench_metric_list_node* entry;
list_for_every_entry(metric_list, entry, struct bench_metric_list_node,
node) {
if (param_idx == entry->param_idx) {
if (cold_run) {
entry->metric.cold = entry->bench_result();
} else {
trusty_bench_update_metric(&entry->metric,
entry->bench_result());
}
}
}
}
/**
* trusty_bench_check_metrics - Check if All Metric are within range
* after one iteration of bench function for all param/metric in the last BENCH.
* @metric_list: List of metrics aggregated during all BENCH runs.
* @param_idx: Index of the current parameter in the param_array of
* BENCH.
*/
static inline bool trusty_bench_check_metrics(struct list_node* metric_list,
size_t param_idx) {
struct bench_metric_list_node* entry;
list_for_every_entry(metric_list, entry, struct bench_metric_list_node,
node) {
if (param_idx == entry->param_idx) {
if (entry->check_results_cb != NULL &&
!entry->check_results_cb(entry)) {
return false;
}
}
}
return true;
}
/**
* trusty_bench_reset_metrics - Run All Metric Updaters after one
* iteration of bench function for all param/metric in the last BENCH.
* @metric_list: List of metrics aggregated during all BENCH runs.
* @param_idx: Index of the current parameter in the param_array of
* BENCH.
*/
static inline void trusty_bench_reset_metrics(struct list_node* metric_list,
size_t param_idx) {
struct bench_metric_list_node* entry;
list_for_every_entry(metric_list, entry, struct bench_metric_list_node,
node) {
if (param_idx == entry->param_idx) {
trusty_bench_update_metric(&entry->metric, entry->bench_result());
}
}
}
/**
* BENCH_SETUP - Runs before every execution of the body of the BENCH
* macro. Can be used to allocate memory, setup 'states',
* initialize 'sessions'...
* @suite_name: Identifier of the current suite.
*/
#define BENCH_SETUP(suite_name) \
static int suite_name##_setup(void); \
static int suite_name##_setup(void)
/**
* BENCH_TEARDOWN - Runs after every execution of the body of the BENCH
* macro. Can be used to free memory, clear 'states',
* close 'sessions'...
* @suite_name: Identifier of the current suite.
*/
#define BENCH_TEARDOWN(suite_name) \
static void suite_name##_teardown(void); \
static void suite_name##_teardown(void)
/**
* BENCH_RESULT_INNER - Declare a metric name for the corresponding BENCH
* and declare the functions to update it after every iteration
* @suite_name: Identifier of the current suite.
* @bench_name: Unique identifier of the Bench in the suite.
* @metric_name: Name of the metric to print in the result table.
* @formatted_value_cb: [optional] A callback of
* trusty_bench_get_formatted_value_callback_t type
* for formatting the result
* value to a string
* @param_name_cb: [optional] A callback of
* trusty_bench_get_param_name_callback_t type
* for formatting the param name
* @check_results_cb: [optional] A callback of
* trusty_bench_check_results_callback_t
* type for formatting the param name
*/
#define BENCH_RESULT_INNER(suite_name, bench_name, metric_name, \
formatted_value_cb_, param_name_cb_, \
check_results_cb_) \
static int64_t update_##suite_name##_##bench_name##_##metric_name(void); \
static struct bench_metric_list_node \
suite_name##_##bench_name##_##metric_name##_node = { \
.node = LIST_INITIAL_CLEARED_VALUE, \
.metric = {0, 0, 0, {INT32_MAX, 0, 0}}, \
.name = STRINGIFY(metric_name), \
.param_idx = 0, \
.nb_params = 0, \
.bench_result = \
update_##suite_name##_##bench_name##_##metric_name, \
.formatted_value_cb = formatted_value_cb_, \
.param_name_cb = param_name_cb_, \
.check_results_cb = check_results_cb_}; \
__attribute__((constructor)) void \
suite_name##_##bench_name##_##metric_name##_add(void) { \
list_add_tail(&suite_name##_##bench_name##_metric_list, \
&suite_name##_##bench_name##_##metric_name##_node.node); \
} \
\
static int64_t update_##suite_name##_##bench_name##_##metric_name(void)
/* Dispatch Mechanics for BENCH_RESULT */
#define BENCH_RESULT_3(suite_name, bench_name, metric_name) \
BENCH_RESULT_INNER(suite_name, bench_name, metric_name, 0, 0, 0)
#define BENCH_RESULT_4(suite_name, bench_name, metric_name, \
formatted_value_cb) \
BENCH_RESULT_INNER(suite_name, bench_name, metric_name, \
formatted_value_cb, 0, 0)
#define BENCH_RESULT_5(suite_name, bench_name, metric_name, \
formatted_value_cb, param_name_cb) \
BENCH_RESULT_INNER(suite_name, bench_name, metric_name, \
formatted_value_cb, param_name_cb, 0)
#define BENCH_RESULT_6(suite_name, bench_name, metric_name, \
formatted_value_cb, param_name_cb, check_results_cb) \
BENCH_RESULT_INNER(suite_name, bench_name, metric_name, \
formatted_value_cb, param_name_cb, check_results_cb)
#ifdef WITH_TEST_PMU
/**
* bench_get_pmu_cnt - convenience function to use in BENCH_RESULT to get
* the value of a pmu counter for the last bench body execution.
*
* Return: The value of a pmu counter of the last completed BENCH body.
*/
static inline int64_t bench_get_pmu_cnt(size_t idx) {
return bench_state.pmu.vals[idx];
}
#else
static inline int64_t bench_get_pmu_cnt(size_t idx) {
return 0;
}
#endif
/**
* BENCH_RESULT Dispatch BENCH_RESULT Called 3, 4 or 5 parameters.
* @suite_name: Identifier of the current suite.
* @bench_name: Unique identifier of the Bench in the suite.
* @metric_name: Name of the metric to print in the result table.
* @formatted_value_cb: [optional] A callback of
* trusty_bench_get_formatted_value_callback_t type for formatting the result
* value to a string
* @param_name_cb: [optional] A callback of
* trusty_bench_get_param_name_callback_t type for formatting the param name
*/
#define BENCH_RESULT(...) \
CAT(BENCH_RESULT_, EVAL(NB_ARGS(__VA_ARGS__)))(__VA_ARGS__)
/**
* PARAM_TEST_NODES_SIMPLE - Create the unparameterized test node lists for
* BENCH
* @suite_name: Identifier of the current suite.
* @bench_name: Unique identifier of the Bench in the suite.
*/
#define PARAM_TEST_NODES_SIMPLE(suite_name, bench_name) \
static struct test_list_node suite_name##_##bench_name##_bench_##_node = { \
.node = LIST_INITIAL_CLEARED_VALUE, \
.suite = STRINGIFY(suite_name_), \
.name = STRINGIFY(bench_name_), \
.func = suite_name##_##bench_name##_bench_, \
.needs_param = 0, \
}; \
\
__attribute__((constructor)) void \
suite_name##_##bench_name##_bench_##_add(void) { \
list_add_tail(&_test_list, \
&suite_name##_##bench_name##_bench_##_node.node); \
}
/**
* PARAM_TEST_NODES_PARAMETRIC - Create the unparameterized test node lists
* for BENCH
* @suite_name: Identifier of the current suite.
* @bench_name: Unique identifier of the Bench in the suite.
* @params: identifier of the param Array for parametric
* benches
*/
#define PARAM_TEST_NODES_PARAMETRIC(suite_name, bench_name, params) \
static struct test_list_node \
suite_name##_##bench_name##_bench_##params##_node = { \
.node = LIST_INITIAL_CLEARED_VALUE, \
.suite = STRINGIFY(suite_name_##params), \
.name = STRINGIFY(bench_name_##params), \
.func = suite_name##_##bench_name##_bench_##params, \
.needs_param = 0, \
}; \
\
__attribute__((constructor)) void \
suite_name##_##bench_name##_bench_##params##_add(void) { \
list_add_tail( \
&_test_list, \
&suite_name##_##bench_name##_bench_##params##_node.node); \
}
/**
* set_param_metric - Create a list of parameterized metrics out of the
* existing list of non-parameterized metric.
* @unparameterized_list: List of metrics aggregated during all BENCH
* runs.
* @parameterized_list: Will be filled with nb_params *
* length_of(unparameterized_list) metrics with
* appropriate param_idx value.
* @nb_params: Number of parameters of the BENCH macro.
* Return: The list of parameterized metrics.
*/
static inline struct bench_metric_list_node* set_param_metric(
struct list_node* unparameterized_list,
struct list_node* parameterized_list,
size_t nb_params) {
size_t idx = 0;
struct bench_metric_list_node* entry;
struct bench_metric_list_node* list_pool =
calloc(nb_params * list_length(unparameterized_list),
sizeof(struct bench_metric_list_node));
if (list_pool == NULL) {
TLOGE("Failed to Allocate memory for bench_metric_list_node!");
return NULL;
}
// clear parameterized_list from previous runs
struct list_node* node = NULL;
do {
node = list_remove_head(parameterized_list);
free(node);
} while (node != NULL);
list_for_every_entry(unparameterized_list, entry,
struct bench_metric_list_node, node) {
for (size_t idx_param = 0; idx_param < nb_params; ++idx_param) {
struct bench_metric_node tmp_metric = {0, 0, 0, {INT32_MAX, 0, 0}};
list_pool[idx].metric = tmp_metric;
list_pool[idx].name = entry->name;
list_pool[idx].param_idx = idx_param;
list_pool[idx].nb_params = nb_params;
list_pool[idx].bench_result = entry->bench_result;
list_pool[idx].formatted_value_cb = entry->formatted_value_cb;
list_pool[idx].param_name_cb = entry->param_name_cb;
list_pool[idx].check_results_cb = entry->check_results_cb;
list_add_tail(parameterized_list, &(list_pool[idx].node));
++idx;
}
}
return list_pool;
}
/**
* trusty_bench_get_overhead - Get Minimal overhead of the benchmark around
* benched function
*
* Return: The Value of the overhead in nanoseconds.
*/
static int64_t trusty_bench_get_overhead(void) {
const size_t nb_runs = 100;
int64_t start_time;
int64_t end_time;
int64_t res = INT64_MAX;
for (size_t i = 0; i < nb_runs; ++i) {
start_time = get_current_time_ns();
end_time = get_current_time_ns();
res = MIN(end_time - start_time, res);
}
return res;
}
/**
* get_extended_bench_name - Print Status of Currently Running Bench.
*
* @test_name_in: Name of the Current Unparameterized Test.
* @test_name_out: Name of the Current Unparameterized Test.
* + "_[param_idx]"
*
* Return: When successful, returns 0
* If test_name_out allocation/print failed returns asprintf
* return code
*/
static inline int get_extended_bench_name(const char* test_name_in,
char** test_name_out) {
int res = snprintf(NULL, 0, "%s_%zu", test_name_in,
bench_state.cur_param_idx);
*test_name_out = NULL;
if (res >= 0) {
*test_name_out = malloc(res + 1);
res = snprintf(*test_name_out, res + 1, "%s_%zu", test_name_in,
bench_state.cur_param_idx);
}
if (res < 0) {
return res;
}
if (!test_name_out) {
TLOGE("Cannot Allocate memory for test name\n");
return -1;
}
return 0;
}
#ifdef WITH_TEST_PMU
#define BENCH_INIT_PMU(evt_arr) \
init_pmu_state(evt_arr, countof(evt_arr), &bench_state.pmu)
#define PMU_START() pmu_start(&bench_state.pmu);
#define PMU_STOP() pmu_stop(&bench_state.pmu);
#define RESET_PMU() reset_pmu_cnts(&bench_state.pmu)
#define CLEAN_PMU() clean_pmu(&bench_state.pmu)
#else
#define BENCH_INIT_PMU(evt_arr) (void)(evt_arr)
#define PMU_START()
#define PMU_STOP()
#define RESET_PMU()
#define CLEAN_PMU()
#endif
/**
* BENCH_CORE - Called by both parametrized and unparameterized
* BENCH for their common part
* @suite_name: Identifier of the current suite.
* @bench_name: Unique identifier of the Bench in the suite.
* @nb_runs: The number of execution of its body for each param
* @nb_params: Number of params in params array
* @params: An array T array_name[nb_params] of parameter
* @metric_list: List of metric nodes to update
*/
#define BENCH_CORE(suite_name, bench_name, nb_runs, nb_params, params, \
metric_list) \
reset_vertical_print_widths(); \
trusty_bench_print_title(STRINGIFY(suite_name), STRINGIFY(bench_name), \
STRINGIFY(params)); \
static trusty_bench_print_callback_t trusty_bench_print_cb = \
&BENCHMARK_PRINT_CB; \
trusty_cur_bench_nb_params = nb_params; \
for (size_t idx_param = 0; idx_param < (nb_params * trusty_bench_nb_cpu); \
++idx_param) { \
bench_state.cur_param_idx = idx_param; \
char* extended_test_name = NULL; \
int res_alloc = get_extended_bench_name( \
STRINGIFY(bench_name##_##params), &extended_test_name); \
if (res_alloc < 0) { \
TLOGE("ERROR %d expanding test name\n", res_alloc); \
_test_context.all_ok = false; \
_test_context.tests_failed++; \
continue; \
} \
TEST_BEGIN_FUNC(STRINGIFY(suite_name), extended_test_name); \
\
int rc = trusty_bench_multi_cpus_setup(); \
if (rc != NO_ERROR) { \
_test_context.skipped = true; \
_test_context.tests_skipped++; \
} else { \
rc = suite_name##_setup(); \
} \
\
if (_test_context.skipped) { \
trusty_unittest_print_status(" SKIPPED"); \
continue; \
} else if (rc != NO_ERROR) { \
TLOGE("ERROR %d during benchmark setup\n", rc); \
_test_context.all_ok = false; \
_test_context.tests_failed++; \
continue; \
} \
int64_t overhead = trusty_bench_get_overhead(); \
\
PMU_START(); \
/* Cold Run */ \
int64_t start_time; \
int64_t end_time; \
start_time = get_current_time_ns(); \
int64_t res = suite_name##_##bench_name##_inner_##params(); \
end_time = get_current_time_ns(); \
\
PMU_STOP(); \
\
if (res != NO_ERROR) { \
TLOGE("ERROR During Cold Run%" PRId64 "\n", res); \
_test_context.all_ok = false; \
_test_context.tests_failed++; \
continue; \
} \
\
bench_state.last_bench_body_duration = end_time - start_time; \
if (5 * overhead >= bench_state.last_bench_body_duration) { \
trusty_unittest_printf( \
"WARNING: Benchmark internal function is too fast %" PRId64 \
"ns, while the benchmark overhead is %" PRId64 "ns.", \
overhead, bench_state.last_bench_body_duration); \
} \
\
bench_state.last_bench_body_duration -= overhead; \
\
if (!_test_context.hard_fail && _test_context.all_ok) { \
trusty_bench_run_metrics(&metric_list, idx_param, true); \
RESET_PMU(); \
} \
\
for (size_t idx_run = 0; idx_run < nb_runs; ++idx_run) { \
if (!_test_context.hard_fail && _test_context.all_ok) { \
PMU_START(); \
start_time = get_current_time_ns(); \
res = suite_name##_##bench_name##_inner_##params(); \
end_time = get_current_time_ns(); \
PMU_STOP(); \
\
bench_state.last_bench_body_duration = end_time - start_time; \
if (overhead >= bench_state.last_bench_body_duration) { \
TLOGE("Benchmark internal function is too fast %" PRId64 \
"ns, while the benchmark overhead is %" PRId64 \
"ns.", \
overhead, bench_state.last_bench_body_duration); \
} \
\
bench_state.last_bench_body_duration -= overhead; \
if (res != NO_ERROR) { \
TLOGE("ERROR %" PRId64 "\n", res); \
} \
} \
if (!_test_context.hard_fail && _test_context.all_ok) { \
trusty_bench_run_metrics(&metric_list, idx_param, false); \
RESET_PMU(); \
} \
} \
suite_name##_teardown(); \
rc = trusty_bench_multi_cpus_teardown(); \
if (rc != NO_ERROR) { \
TLOGW("failed to reset CPU affinity: %d\n", rc); \
} \
\
if (!trusty_bench_check_metrics(&metric_list, idx_param)) { \
_test_context.all_ok = false; \
_test_context.tests_failed++; \
} \
TEST_END_FUNC(); \
free(extended_test_name); \
extended_test_name = NULL; \
} \
trusty_bench_print_cb(&metric_list, (nb_params * trusty_bench_nb_cpu), \
STRINGIFY(suite_name), \
STRINGIFY(bench_name##_##params)); \
trusty_bench_get_param_name_cb = NULL; \
trusty_bench_get_formatted_value_cb = NULL; \
CLEAN_PMU();
/**
* BENCH_PARAMETERIZED_PTR -Called when BENCH has 5 parameters. This allows
* to reuse Other macros for different bench by
* aliasing an array to a pointer
* @suite_name: Identifier of the current suite.
* @bench_name: Unique identifier of the Bench in the suite.
* @nb_runs: The number of execution of its body for each param
* @params: An array T array_name[nb_params] of parameter
* @nb_params: Number of parameters in the parameter Array
*/
#define BENCH_PARAMETERIZED_PTR(nb_cpu, suite_name, bench_name, nb_runs, \
params, nb_params) \
static int suite_name##_##bench_name##_inner_##params(void); \
static void suite_name##_##bench_name##_bench_##params(void); \
static struct list_node suite_name##_##bench_name##_metric_list = \
LIST_INITIAL_VALUE(suite_name##_##bench_name##_metric_list); \
static struct list_node suite_name##_##bench_name##_metric_##params##_list = \
LIST_INITIAL_VALUE( \
suite_name##_##bench_name##_metric_##params##_list); \
\
static void suite_name##_##bench_name##_bench_##params(void) { \
trusty_bench_nb_cpu = nb_cpu; \
struct bench_metric_list_node* metric_pool = set_param_metric( \
&suite_name##_##bench_name##_metric_list, \
&suite_name##_##bench_name##_metric_##params##_list, \
(nb_params * trusty_bench_nb_cpu)); \
if (metric_pool == NULL) { \
_test_context.hard_fail = true; \
return; \
} \
BENCH_CORE(suite_name, bench_name, nb_runs, nb_params, params, \
suite_name##_##bench_name##_metric_##params##_list); \
free(metric_pool); \
} \
PARAM_TEST_NODES(suite_name, bench_name, params) \
\
static int suite_name##_##bench_name##_inner_##params(void)
/**
* BENCH_PARAMETERIZED - Called when BENCH has 4 parameters
* @suite_name: Identifier of the current suite.
* @bench_name: Unique identifier of the Bench in the suite.
* @nb_runs: The number of execution of its body for each param
* @params: An array T array_name[nb_params] of parameter
*/
#define BENCH_PARAMETERIZED(nb_cpu, suite_name, bench_name, nb_runs, params) \
BENCH_PARAMETERIZED_PTR(nb_cpu, suite_name, bench_name, nb_runs, params, \
countof(params))
/**
* BENCH_SIMPLE - Called when BENCH has only 3 parameters.
* @suite_name: Identifier of the current suite.
* @bench_name: Unique identifier of the Bench in the suite.
* @nb_runs: The number of execution of its body.
*/
#define BENCH_SIMPLE(nb_cpu, suite_name, bench_name, nb_runs) \
static int suite_name##_##bench_name##_inner_(void); \
static void suite_name##_##bench_name##_bench_(void); \
static struct list_node suite_name##_##bench_name##_metric_list = \
LIST_INITIAL_VALUE(suite_name##_##bench_name##_metric_list); \
static struct list_node suite_name##_##bench_name##_metric_cpu##_list = \
LIST_INITIAL_VALUE(suite_name##_##bench_name##_metric_cpu##_list); \
static void suite_name##_##bench_name##_bench_(void) { \
bench_state.cur_param_idx = 0; \
trusty_bench_nb_cpu = nb_cpu; \
struct bench_metric_list_node* metric_pool = set_param_metric( \
&suite_name##_##bench_name##_metric_list, \
&suite_name##_##bench_name##_metric_cpu##_list, \
trusty_bench_nb_cpu); \
if (metric_pool == NULL) { \
_test_context.hard_fail = true; \
return; \
} \
BENCH_CORE(suite_name, bench_name, nb_runs, 1, , \
suite_name##_##bench_name##_metric_cpu##_list); \
} \
\
PARAM_TEST_NODES(suite_name, bench_name) \
static int suite_name##_##bench_name##_inner_(void)
/*
* BENCH - Routing the BENCH macros depending on its number of parameters.
*/
#define BENCH_3 BENCH_SIMPLE
#define BENCH_4 BENCH_PARAMETERIZED
#define BENCH_5 BENCH_PARAMETERIZED_PTR
/**
* BENCH - Called 3, 4 or 5 parameters. This allows
* to reuse Other macros for different bench by
* aliasing an array to a pointer
* @suite_name: Identifier of the current suite.
* @bench_name: Unique identifier of the Bench in the suite.
* @nb_runs: The number of execution of its body for each param
* @params: [optional] An array T array_name[nb_params] of
* parameter, or a pointer T*, in the latter case a 5th
* parameter is needed
* @nb_params: [optional] if 4th parameter is a pointer, Number of
* parameters in the parameter Array
*/
#define BENCH(...) CAT(BENCH_, EVAL(NB_ARGS(__VA_ARGS__)))(1, __VA_ARGS__)
/**
* BENCH_ALL_CPU - Called 3, 4 or 5 parameters. This allows
* to reuse Other macros for different bench by
* aliasing an array to a pointer
* @suite_name: Identifier of the current suite.
* @bench_name: Unique identifier of the Bench in the suite.
* @nb_runs: The number of execution of its body for each param
* @params: [optional] An array T array_name[nb_params] of
* parameter, or a pointer T*, in the latter case a 5th
* parameter is needed
* @nb_params: [optional] if 4th parameter is a pointer, Number of
* parameters in the parameter Array
*/
#define BENCH_ALL_CPU(...) \
CAT(BENCH_, EVAL(NB_ARGS(__VA_ARGS__)))(SMP_MAX_CPUS, __VA_ARGS__)
/*
* PARAM_TEST_NODES - Routing the PARAM_TEST_NODES macros depending on its
* number of parameters.
*/
#define PARAM_TEST_NODES_2 PARAM_TEST_NODES_SIMPLE
#define PARAM_TEST_NODES_3 PARAM_TEST_NODES_PARAMETRIC
#define PARAM_TEST_NODES(...) \
CAT(PARAM_TEST_NODES_, EVAL(NB_ARGS(__VA_ARGS__)))(__VA_ARGS__)
__END_CDECLS