1 /*
2  * Copyright (c) 2021 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /**
8  * @file
9  *
10  * @brief Zephyr testing framework ztress macros
11  */
12 
13 #ifndef TESTSUITE_ZTEST_INCLUDE_ZTRESS_H__
14 #define TESTSUITE_ZTEST_INCLUDE_ZTRESS_H__
15 
16 #include <zephyr/sys/util.h>
17 #include <zephyr/kernel.h>
18 
19 #ifdef __cplusplus
20 extern "C" {
21 #endif
22 
23 /** @internal Internal ID's to distinguish context type. */
24 #define ZTRESS_ID_THREAD 0
25 #define ZTRESS_ID_K_TIMER 1
26 
27 /**
28  * @defgroup ztest_ztress Ztest ztress macros
29  * @ingroup ztest
30  *
31  * This module provides test stress when using Ztest.
32  *
33  * @{
34  */
35 
36 /** @brief Descriptor of a k_timer handler execution context.
37  *
38  * The handler is executed in the k_timer handler context which typically means
39  * interrupt context. This context will preempt any other used in the set.
40  *
41  * @note There can only be up to one k_timer context in the set and it must be the
42  * first argument of @ref ZTRESS_EXECUTE.
43  *
44  * @param handler User handler of type @ref ztress_handler.
45  *
46  * @param user_data User data passed to the @p handler.
47  *
48  * @param exec_cnt Number of handler executions to complete the test. If 0 then
49  * this is not included in completion criteria.
50  *
51  * @param init_timeout Initial backoff time base (given in @ref k_timeout_t). It
52  * is adjusted during the test to optimize CPU load. The actual timeout used for
53  * the timer is randomized.
54  */
55 #define ZTRESS_TIMER(handler, user_data, exec_cnt, init_timeout) \
56 	(ZTRESS_ID_K_TIMER, handler, user_data, exec_cnt, 0, init_timeout)
57 
58 /** @brief Descriptor of a thread execution context.
59  *
60  * The handler is executed in the thread context. The priority of the thread is
61  * determined based on the order in which contexts are listed in @ref ZTRESS_EXECUTE.
62  *
63  * @note thread sleeps for random amount of time. Additionally, the thread busy-waits
64  * for a random length of time to further increase randomization in the test.
65  *
66  * @param handler User handler of type @ref ztress_handler.
67  *
68  * @param user_data User data passed to the @p handler.
69  *
70  * @param exec_cnt Number of handler executions to complete the test. If 0 then
71  * this is not included in completion criteria.
72  *
73  * @param preempt_cnt Number of preemptions of that context to complete the test.
74  * If 0 then this is not included in completion criteria.
75  *
76  * @param init_timeout Initial backoff time base (given in @ref k_timeout_t). It
77  * is adjusted during the test to optimize CPU load. The actual timeout used for
78  * sleeping is randomized.
79  */
80 #define ZTRESS_THREAD(handler, user_data, exec_cnt, preempt_cnt, init_timeout) \
81 	(ZTRESS_ID_THREAD, handler, user_data, exec_cnt, preempt_cnt, init_timeout)
82 
83 /** @brief User handler called in one of the configured contexts.
84  *
85  * @param user_data User data provided in the context descriptor.
86  *
87  * @param cnt Current execution counter. Counted from 0.
88  *
89  * @param last Flag set to true indicates that it is the last execution because
90  * completion criteria are met, test timed out or was aborted.
91  *
92  * @param prio Context priority counting from 0 which indicates the highest priority.
93  *
94  * @retval true continue test.
95  * @retval false stop executing the current context.
96  */
97 typedef bool (*ztress_handler)(void *user_data, uint32_t cnt, bool last, int prio);
98 
99 /** @internal Context structure. */
100 struct ztress_context_data {
101 	/* Handler. */
102 	ztress_handler handler;
103 
104 	/* User data */
105 	void *user_data;
106 
107 	/* Minimum number of executions to complete the test. */
108 	uint32_t exec_cnt;
109 
110 	/* Minimum number of preemptions to complete the test. Valid only for
111 	 * thread context.
112 	 */
113 	uint32_t preempt_cnt;
114 
115 	/* Initial timeout. */
116 	k_timeout_t t;
117 };
118 
119 /** @brief Initialize context structure.
120  *
121  * For argument types see @ref ztress_context_data. For more details see
122  * @ref ZTRESS_THREAD.
123  *
124  * @param _handler Handler.
125  * @param _user_data User data passed to the handler.
126  * @param _exec_cnt Execution count limit.
127  * @param _preempt_cnt Preemption count limit.
128  * @param _t Initial timeout.
129  */
130 #define ZTRESS_CONTEXT_INITIALIZER(_handler, _user_data, _exec_cnt, _preempt_cnt, _t) \
131 	{ \
132 		.handler = (_handler), \
133 		.user_data = (_user_data), \
134 		.exec_cnt = (_exec_cnt), \
135 		.preempt_cnt = (_preempt_cnt), \
136 		.t = (_t) \
137 	}
138 
139 /** @internal Strip first argument (context type) and call struct initializer. */
140 #define Z_ZTRESS_GET_HANDLER_DATA2(_, ...) \
141 	ZTRESS_CONTEXT_INITIALIZER(__VA_ARGS__)
142 
143 /** @internal Macro for initializing context data. */
144 #define Z_ZTRESS_GET_HANDLER_DATA(data) \
145 	Z_ZTRESS_GET_HANDLER_DATA2 data
146 
147 /** @internal Macro for checking if provided context is a timer context. */
148 #define Z_ZTRESS_HAS_TIMER(data, ...) \
149 	GET_ARG_N(1, __DEBRACKET data)
150 
151 /** @internal If context descriptor is @ref ZTRESS_TIMER, it returns index of that
152  * descriptor in the list of arguments.
153  */
154 #define Z_ZTRESS_TIMER_IDX(idx, data) \
155 	((GET_ARG_N(1, __DEBRACKET data)) == ZTRESS_ID_K_TIMER ? idx : 0)
156 
157 /** @intenal Macro validates that @ref ZTRESS_TIMER context is not used except for
158  * the first item in the list of contexts.
159  */
160 #define Z_ZTRESS_TIMER_CONTEXT_VALIDATE(...) \
161 	BUILD_ASSERT((FOR_EACH_IDX(Z_ZTRESS_TIMER_IDX, (+), __VA_ARGS__)) == 0, \
162 			"There can only be up to one ZTRESS_TIMER context and it must " \
163 			"be the first in the list")
164 
165 /** @brief Setup and run stress test.
166  *
167  * It initialises all contexts and calls @ref ztress_execute.
168  *
169  * @param ... List of contexts. Contexts are configured using @ref ZTRESS_TIMER
170  * and @ref ZTRESS_THREAD macros. @ref ZTRESS_TIMER must be the first argument if
171  * used. Each thread context has an assigned priority. The priority is assigned in
172  * a descending order (first listed thread context has the highest priority).
173  * The maximum number of supported thread contexts, including the timer context,
174  * is configurable in Kconfig (ZTRESS_MAX_THREADS).
175  */
176 #define ZTRESS_EXECUTE(...) do {							\
177 	Z_ZTRESS_TIMER_CONTEXT_VALIDATE(__VA_ARGS__);					\
178 	int has_timer = Z_ZTRESS_HAS_TIMER(__VA_ARGS__);				\
179 	struct ztress_context_data _ctx_data1[] = {					\
180 		FOR_EACH(Z_ZTRESS_GET_HANDLER_DATA, (,), __VA_ARGS__)			\
181 	};										\
182 	size_t cnt = ARRAY_SIZE(_ctx_data1) - has_timer;				\
183 	static struct ztress_context_data _ctx_data[ARRAY_SIZE(_ctx_data1)];		\
184 	for (size_t i = 0; i < ARRAY_SIZE(_ctx_data1); i++) {				\
185 		_ctx_data[i] = _ctx_data1[i];						\
186 	}	                                                                        \
187 	int exec_err = ztress_execute(has_timer ? &_ctx_data[0] : NULL,			\
188 				 &_ctx_data[has_timer], cnt);				\
189 											\
190 	zassert_equal(exec_err, 0, "ztress_execute failed (err: %d)", exec_err);	\
191 } while (0)
192 
193 /** Execute contexts.
194  *
195  * The test runs until all completion requirements are met or until the test times out
196  * (use @ref ztress_set_timeout to configure timeout) or until the test is aborted
197  * (@ref ztress_abort).
198  *
199  * on test completion a report is printed (@ref ztress_report is called internally).
200  *
201  * @param timer_data Timer context. NULL if timer context is not used.
202  * @param thread_data List of thread contexts descriptors in priority descending order.
203  * @param cnt Number of thread contexts.
204  *
205  * @retval -EINVAL If configuration is invalid.
206  * @retval 0 if test is successfully performed.
207  */
208 int ztress_execute(struct ztress_context_data *timer_data,
209 		     struct ztress_context_data *thread_data,
210 		     size_t cnt);
211 
212 /** @brief Abort ongoing stress test. */
213 void ztress_abort(void);
214 
215 /** @brief Set test timeout.
216  *
217  * Test is terminated after timeout disregarding completion criteria. Setting
218  * is persistent between executions.
219  *
220  * @param t Timeout.
221  */
222 void ztress_set_timeout(k_timeout_t t);
223 
224 /** @brief Print last test report.
225  *
226  * Report contains number of executions and preemptions for each context, initial
227  * and adjusted timeouts and CPU load during the test.
228  */
229 void ztress_report(void);
230 
231 /** @brief Get number of executions of a given context in the last test.
232  *
233  * @param id Context id. 0 means the highest priority.
234  *
235  * @return Number of executions.
236  */
237 int ztress_exec_count(uint32_t id);
238 
239 /** @brief Get number of preemptions of a given context in the last test.
240  *
241  * @param id Context id. 0 means the highest priority.
242  *
243  * @return Number of preemptions.
244  */
245 int ztress_preempt_count(uint32_t id);
246 
247 /** @brief Get optimized timeout base of a given context in the last test.
248  *
249  * Optimized value can be used to update initial value. It will improve the test
250  * since optimal CPU load will be reach immediately.
251  *
252  * @param id Context id. 0 means the highest priority.
253  *
254  * @return Optimized timeout base.
255  */
256 uint32_t ztress_optimized_ticks(uint32_t id);
257 
258 /**
259  * @}
260  */
261 
262 #ifdef __cplusplus
263 }
264 #endif
265 
266 #endif /* TESTSUITE_ZTEST_INCLUDE_ZTRESS_H__ */
267