| /* |
| * Copyright (c) 2021 Nordic Semiconductor ASA |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| #ifndef TESTSUITE_ZTEST_INCLUDE_ZTRESS_H__ |
| #define TESTSUITE_ZTEST_INCLUDE_ZTRESS_H__ |
| |
| #include <zephyr/sys/util.h> |
| #include <zephyr/kernel.h> |
| |
| #ifdef __cplusplus |
| extern "C" { |
| #endif |
| |
| /** @internal Internal ID's to distinguish context type. */ |
| #define ZTRESS_ID_THREAD 0 |
| #define ZTRESS_ID_K_TIMER 1 |
| |
| /** @brief Descriptor of a k_timer handler execution context. |
| * |
| * The handler is executed in the k_timer handler context which typically means |
| * interrupt context. This context will preempt any other used in the set. |
| * |
| * @note There can only be up to one k_timer context in the set and it must be the |
| * first argument of @ref ZTRESS_EXECUTE. |
| * |
| * @param handler User handler of type @ref ztress_handler. |
| * |
| * @param user_data User data passed to the @p handler. |
| * |
| * @param exec_cnt Number of handler executions to complete the test. If 0 then |
| * this is not included in completion criteria. |
| * |
| * @param init_timeout Initial backoff time base (given in @ref k_timeout_t). It |
| * is adjusted during the test to optimize CPU load. The actual timeout used for |
| * the timer is randomized. |
| */ |
| #define ZTRESS_TIMER(handler, user_data, exec_cnt, init_timeout) \ |
| (ZTRESS_ID_K_TIMER, handler, user_data, exec_cnt, 0, init_timeout) |
| |
| /** @brief Descriptor of a thread execution context. |
| * |
| * The handler is executed in the thread context. The priority of the thread is |
| * determined based on the order in which contexts are listed in @ref ZTRESS_EXECUTE. |
| * |
| * @note thread sleeps for random amount of time. Additionally, the thread busy-waits |
| * for a random length of time to further increase randomization in the test. |
| * |
| * @param handler User handler of type @ref ztress_handler. |
| * |
| * @param user_data User data passed to the @p handler. |
| * |
| * @param exec_cnt Number of handler executions to complete the test. If 0 then |
| * this is not included in completion criteria. |
| * |
| * @param preempt_cnt Number of preemptions of that context to complete the test. |
| * If 0 then this is not included in completion criteria. |
| * |
| * @param init_timeout Initial backoff time base (given in @ref k_timeout_t). It |
| * is adjusted during the test to optimize CPU load. The actual timeout used for |
| * sleeping is randomized. |
| */ |
| #define ZTRESS_THREAD(handler, user_data, exec_cnt, preempt_cnt, init_timeout) \ |
| (ZTRESS_ID_THREAD, handler, user_data, exec_cnt, preempt_cnt, init_timeout) |
| |
| /** @brief User handler called in one of the configured contexts. |
| * |
| * @param user_data User data provided in the context descriptor. |
| * |
| * @param cnt Current execution counter. Counted from 0. |
| * |
| * @param last Flag set to true indicates that it is the last execution because |
| * completion criteria are met, test timed out or was aborted. |
| * |
| * @param prio Context priority counting from 0 which indicates the highest priority. |
| * |
| * @retval true continue test. |
| * @retval false stop executing the current context. |
| */ |
| typedef bool (*ztress_handler)(void *user_data, uint32_t cnt, bool last, int prio); |
| |
| /** @internal Context structure. */ |
| struct ztress_context_data { |
| /* Handler. */ |
| ztress_handler handler; |
| |
| /* User data */ |
| void *user_data; |
| |
| /* Minimum number of executions to complete the test. */ |
| uint32_t exec_cnt; |
| |
| /* Minimum number of preemptions to complete the test. Valid only for |
| * thread context. |
| */ |
| uint32_t preempt_cnt; |
| |
| /* Initial timeout. */ |
| k_timeout_t t; |
| }; |
| |
| /** @brief Initialize context structure. |
| * |
| * For argument types see @ref ztress_context_data. For more details see |
| * @ref ZTRESS_THREAD. |
| * |
| * @param _handler Handler. |
| * @param _user_data User data passed to the handler. |
| * @param _exec_cnt Execution count limit. |
| * @param _preempt_cnt Preemption count limit. |
| * @param _t Initial timeout. |
| */ |
| #define ZTRESS_CONTEXT_INITIALIZER(_handler, _user_data, _exec_cnt, _preempt_cnt, _t) \ |
| { \ |
| .handler = (_handler), \ |
| .user_data = (_user_data), \ |
| .exec_cnt = (_exec_cnt), \ |
| .preempt_cnt = (_preempt_cnt), \ |
| .t = (_t) \ |
| } |
| |
| /** @internal Strip first argument (context type) and call struct initializer. */ |
| #define Z_ZTRESS_GET_HANDLER_DATA2(_, ...) \ |
| ZTRESS_CONTEXT_INITIALIZER(__VA_ARGS__) |
| |
| /** @internal Macro for initializing context data. */ |
| #define Z_ZTRESS_GET_HANDLER_DATA(data) \ |
| Z_ZTRESS_GET_HANDLER_DATA2 data |
| |
| /** @internal Macro for checking if provided context is a timer context. */ |
| #define Z_ZTRESS_HAS_TIMER(data, ...) \ |
| GET_ARG_N(1, __DEBRACKET data) |
| |
| /** @internal If context descriptor is @ref ZTRESS_TIMER, it returns index of that |
| * descriptor in the list of arguments. |
| */ |
| #define Z_ZTRESS_TIMER_IDX(idx, data) \ |
| ((GET_ARG_N(1, __DEBRACKET data)) == ZTRESS_ID_K_TIMER ? idx : 0) |
| |
| /** @intenal Macro validates that @ref ZTRESS_TIMER context is not used except for |
| * the first item in the list of contexts. |
| */ |
| #define Z_ZTRESS_TIMER_CONTEXT_VALIDATE(...) \ |
| BUILD_ASSERT((FOR_EACH_IDX(Z_ZTRESS_TIMER_IDX, (+), __VA_ARGS__)) == 0, \ |
| "There can only be up to one ZTRESS_TIMER context and it must " \ |
| "be the first in the list") |
| |
| /** @brief Setup and run stress test. |
| * |
| * It initialises all contexts and calls @ref ztress_execute. |
| * |
| * @param ... List of contexts. Contexts are configured using @ref ZTRESS_TIMER |
| * and @ref ZTRESS_THREAD macros. @ref ZTRESS_TIMER must be the first argument if |
| * used. Each thread context has an assigned priority. The priority is assigned in |
| * a descending order (first listed thread context has the highest priority). The |
| * number of supported thread contexts is configurable in Kconfig. |
| */ |
| #define ZTRESS_EXECUTE(...) do { \ |
| Z_ZTRESS_TIMER_CONTEXT_VALIDATE(__VA_ARGS__); \ |
| int has_timer = Z_ZTRESS_HAS_TIMER(__VA_ARGS__); \ |
| struct ztress_context_data data1[] = { \ |
| FOR_EACH(Z_ZTRESS_GET_HANDLER_DATA, (,), __VA_ARGS__) \ |
| }; \ |
| size_t cnt = ARRAY_SIZE(data1) - has_timer; \ |
| static struct ztress_context_data data[ARRAY_SIZE(data1)]; \ |
| for (size_t i = 0; i < ARRAY_SIZE(data1); i++) { \ |
| data[i] = data1[i]; \ |
| } \ |
| int err = ztress_execute(has_timer ? &data[0] : NULL, &data[has_timer], cnt); \ |
| \ |
| zassert_equal(err, 0, "ztress_execute failed (err: %d)", err); \ |
| } while (0) |
| |
| /** Execute contexts. |
| * |
| * The test runs until all completion requirements are met or until the test times out |
| * (use @ref ztress_set_timeout to configure timeout) or until the test is aborted |
| * (@ref ztress_abort). |
| * |
| * on test completion a report is printed (@ref ztress_report is called internally). |
| * |
| * @param timer_data Timer context. NULL if timer context is not used. |
| * @param thread_data List of thread contexts descriptors in priority descending order. |
| * @param cnt Number of thread contexts. |
| * |
| * @retval -EINVAL If configuration is invalid. |
| * @retval 0 if test is successfully performed. |
| */ |
| int ztress_execute(struct ztress_context_data *timer_data, |
| struct ztress_context_data *thread_data, |
| size_t cnt); |
| |
| /** @brief Abort ongoing stress test. */ |
| void ztress_abort(void); |
| |
| /** @brief Set test timeout. |
| * |
| * Test is terminated after timeout disregarding completion criteria. Setting |
| * is persistent between executions. |
| * |
| * @param t Timeout. |
| */ |
| void ztress_set_timeout(k_timeout_t t); |
| |
| /** @brief Print last test report. |
| * |
| * Report contains number of executions and preemptions for each context, initial |
| * and adjusted timeouts and CPU load during the test. |
| */ |
| void ztress_report(void); |
| |
| /** @brief Get number of executions of a given context in the last test. |
| * |
| * @param id Context id. 0 means the highest priority. |
| * |
| * @return Number of executions. |
| */ |
| int ztress_exec_count(uint32_t id); |
| |
| /** @brief Get number of preemptions of a given context in the last test. |
| * |
| * @param id Context id. 0 means the highest priority. |
| * |
| * @return Number of preemptions. |
| */ |
| int ztress_preempt_count(uint32_t id); |
| |
| /** @brief Get optimized timeout base of a given context in the last test. |
| * |
| * Optimized value can be used to update initial value. It will improve the test |
| * since optimal CPU load will be reach immediately. |
| * |
| * @param id Context id. 0 means the highest priority. |
| * |
| * @return Optimized timeout base. |
| */ |
| uint32_t ztress_optimized_ticks(uint32_t id); |
| |
| #ifdef __cplusplus |
| } |
| #endif |
| |
| #endif /* TESTSUITE_ZTEST_INCLUDE_ZTRESS_H__ */ |