1 /*
2  * Copyright (c) 2022 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/ztest.h>
8 
9 #define HELPER_STACK_SIZE 500
10 
11 /**
12  * @brief Verify @a va1 and @a val2 are within @a pcnt % of each other
13  */
14 #define TEST_WITHIN_X_PERCENT(val1, val2, pcnt)                       \
15 	((((val1) * 100) < ((val2) * (100 + (pcnt)))) &&              \
16 	 (((val1) * 100) > ((val2) * (100 - (pcnt))))) ? true : false
17 
18 #if defined(CONFIG_RISCV)
19 #define IDLE_EVENT_STATS_PRECISION 7
20 #elif defined(CONFIG_QEMU_TARGET)
21 #define IDLE_EVENT_STATS_PRECISION 3
22 #else
23 #define IDLE_EVENT_STATS_PRECISION 1
24 #endif
25 
26 static struct k_thread helper_thread;
27 static K_THREAD_STACK_DEFINE(helper_stack, HELPER_STACK_SIZE);
28 
29 static struct k_thread *main_thread;
30 
31 /**
32  * @brief Helper thread to test_thread_runtime_stats_get()
33  */
helper1(void * p1,void * p2,void * p3)34 void helper1(void *p1, void *p2, void *p3)
35 {
36 	/* Using volatile condition to prevent compilers from optimizing while(true) */
37 	volatile bool condition = true;
38 
39 	while (condition) {
40 	}
41 }
42 
43 /**
44  * @brief Busy wait for specified number of ticks
45  */
busy_loop(uint32_t ticks)46 void busy_loop(uint32_t ticks)
47 {
48 	uint32_t  tick = sys_clock_tick_get_32();
49 
50 	while (sys_clock_tick_get_32() < (tick + ticks)) {
51 	}
52 }
53 
54 /**
55  * @brief Test the k_threads_runtime_stats_all_get() API
56  *
57  * 1. Create a helper thread.
58  * 2. Busy loop for 2 ticks.
59  *    - Idle time should not increase.
60  * 3. Sleep for two ticks. Helper executes and busy loops.
61  *    - Idle time should not increase
62  * 4. Kill helper thread, and sleep for 2 ticks
63  *    - Idle time should increase
64  * 5. Busy loop for 3 ticks
65  *    - Idle time should not increase
66  *    - current, peak and average cycles should be different
67  */
ZTEST(usage_api,test_all_stats_usage)68 ZTEST(usage_api, test_all_stats_usage)
69 {
70 	int  priority;
71 	k_tid_t  tid;
72 	k_thread_runtime_stats_t  stats1;
73 	k_thread_runtime_stats_t  stats2;
74 	k_thread_runtime_stats_t  stats3;
75 	k_thread_runtime_stats_t  stats4;
76 	k_thread_runtime_stats_t  stats5;
77 
78 	priority = k_thread_priority_get(_current);
79 	tid = k_thread_create(&helper_thread, helper_stack,
80 			      K_THREAD_STACK_SIZEOF(helper_stack),
81 			      helper1, NULL, NULL, NULL,
82 			      priority + 2, 0, K_NO_WAIT);
83 
84 	k_thread_runtime_stats_all_get(&stats1);
85 
86 	busy_loop(2);   /* Busy wait 2 ticks */
87 
88 	k_thread_runtime_stats_all_get(&stats2);
89 
90 #if defined(CONFIG_RISCV)
91 	k_sleep(K_TICKS(3));  /* Helper runs for 3 ticks - on slower platforms */
92 #else
93 	k_sleep(K_TICKS(2));  /* Helper runs for 2 ticks */
94 #endif
95 
96 	k_thread_runtime_stats_all_get(&stats3);
97 
98 	k_thread_abort(tid);
99 
100 	k_sleep(K_TICKS(2));  /* Idle for 2 ticks */
101 
102 	k_thread_runtime_stats_all_get(&stats4);
103 
104 	busy_loop(3);   /* Busy wait for 3 ticks */
105 
106 	k_thread_runtime_stats_all_get(&stats5);
107 
108 	/*
109 	 * Verify that before the system idles for 2 ticks that
110 	 * [execution_cycles] is increasing, [total_cycles + idle_cycles] matches
111 	 * [execution_cycles] and [idle_cycles] is not changing (as the
112 	 * system is not going to idle during that test).
113 	 */
114 
115 	zassert_true(stats2.execution_cycles > stats1.execution_cycles);
116 	zassert_true(stats3.execution_cycles > stats2.execution_cycles);
117 	zassert_true(stats1.execution_cycles == (stats1.total_cycles + stats1.idle_cycles));
118 	zassert_true(stats2.execution_cycles == (stats2.total_cycles + stats2.idle_cycles));
119 	zassert_true(stats3.execution_cycles == (stats3.total_cycles + stats3.idle_cycles));
120 #ifdef CONFIG_SCHED_THREAD_USAGE_ALL
121 	zassert_true(stats1.idle_cycles == stats2.idle_cycles);
122 	zassert_true(stats1.idle_cycles == stats3.idle_cycles);
123 #endif
124 
125 #ifdef CONFIG_SCHED_THREAD_USAGE_ANALYSIS
126 
127 	/*
128 	 * The analysis fields should behave as follows prior to the system
129 	 * going idle.
130 	 * 1. [current_cycles] increases.
131 	 * 2. [peak_cycles] matches [current_cycles].
132 	 * 3. [average_cycles] is 0 if system has not gone idle yet
133 	 * 4. [current_cycles] matches [execution_cycles] if system has not gone idle yet
134 	 */
135 
136 	zassert_true(stats2.current_cycles > stats1.current_cycles);
137 	zassert_true(stats3.current_cycles > stats2.current_cycles);
138 
139 	zassert_true(stats1.peak_cycles == stats1.current_cycles);
140 	zassert_true(stats2.peak_cycles == stats2.current_cycles);
141 	zassert_true(stats3.peak_cycles == stats3.current_cycles);
142 
143 	if (stats1.idle_cycles == 0) {
144 		zassert_true(stats1.average_cycles == 0);
145 		zassert_true(stats2.average_cycles == 0);
146 		zassert_true(stats3.average_cycles == 0);
147 
148 		zassert_true(stats1.current_cycles == stats1.execution_cycles);
149 		zassert_true(stats2.current_cycles == stats2.execution_cycles);
150 		zassert_true(stats3.current_cycles == stats3.execution_cycles);
151 	} else {
152 		zassert_true(stats1.current_cycles < stats1.execution_cycles);
153 		zassert_true(stats2.current_cycles < stats2.execution_cycles);
154 		zassert_true(stats3.current_cycles < stats3.execution_cycles);
155 	}
156 #endif
157 
158 	/*
159 	 * Now process the statistics after the idle event.
160 	 *
161 	 * 1. [execution_cycles] continues to increase
162 	 * 2. [total_cycles] increases
163 	 * 3. [current_cycles] had a reset event but still increases
164 	 * 4. [peak_cycles] does not change
165 	 * 5. [average_cycles] increases
166 	 * 6. [idle_cycles] increased once.
167 	 */
168 
169 	zassert_true(stats4.execution_cycles > stats3.execution_cycles);
170 	zassert_true(stats5.execution_cycles > stats4.execution_cycles);
171 
172 	/*
173 	 * If the frequency is low enough, the [total_cycles] might not
174 	 * increase between sample points 3 and 4. Count this as acceptable.
175 	 */
176 
177 	zassert_true(stats4.total_cycles >= stats3.total_cycles);
178 	zassert_true(stats5.total_cycles > stats4.total_cycles);
179 
180 #ifdef CONFIG_SCHED_THREAD_USAGE_ANALYSIS
181 	zassert_true(stats4.current_cycles <= stats1.current_cycles);
182 	zassert_true(stats5.current_cycles > stats4.current_cycles);
183 
184 	zassert_true(TEST_WITHIN_X_PERCENT(stats4.peak_cycles,
185 					   stats3.peak_cycles, IDLE_EVENT_STATS_PRECISION), NULL);
186 	zassert_true(stats4.peak_cycles == stats5.peak_cycles);
187 
188 	zassert_true(stats4.average_cycles > 0);
189 	zassert_true(stats5.average_cycles > stats4.average_cycles);
190 #endif
191 
192 #ifdef CONFIG_SCHED_THREAD_USAGE_ALL
193 	zassert_true(stats4.idle_cycles > stats3.idle_cycles);
194 	zassert_true(stats4.idle_cycles == stats5.idle_cycles);
195 #endif
196 }
197 
198 #ifdef CONFIG_SCHED_THREAD_USAGE_ANALYSIS
199 /**
200  * @brief Test the k_thread_runtime_stats_enable/disable APIs
201  */
ZTEST(usage_api,test_thread_stats_enable_disable)202 ZTEST(usage_api, test_thread_stats_enable_disable)
203 {
204 	k_tid_t  tid;
205 	k_thread_runtime_stats_t  stats1;
206 	k_thread_runtime_stats_t  stats2;
207 	k_thread_runtime_stats_t  helper_stats1;
208 	k_thread_runtime_stats_t  helper_stats2;
209 	k_thread_runtime_stats_t  helper_stats3;
210 	int  priority;
211 
212 	priority = k_thread_priority_get(_current);
213 	tid = k_thread_create(&helper_thread, helper_stack,
214 			      K_THREAD_STACK_SIZEOF(helper_stack),
215 			      helper1, NULL, NULL, NULL,
216 			      priority + 2, 0, K_NO_WAIT);
217 
218 	/*
219 	 * Sleep to let the helper thread execute for some time before
220 	 * disabling the runtime stats on the helper thread.
221 	 */
222 
223 	k_sleep(K_TICKS(5));
224 
225 	k_thread_runtime_stats_get(_current, &stats1);
226 	k_thread_runtime_stats_get(tid, &helper_stats1);
227 	k_thread_runtime_stats_disable(tid);
228 
229 	/*
230 	 * Busy wait for the remaining tick before re-enabling the thread
231 	 * runtime stats on the helper thread.
232 	 */
233 
234 	busy_loop(1);
235 
236 	/* Sleep for two ticks to let the helper thread execute. */
237 
238 	k_sleep(K_TICKS(2));
239 
240 	k_thread_runtime_stats_enable(tid);
241 	k_thread_runtime_stats_get(_current, &stats2);
242 	k_thread_runtime_stats_get(tid, &helper_stats2);
243 
244 	/* Sleep for two ticks to let the helper thread execute again. */
245 
246 	k_sleep(K_TICKS(2));
247 
248 	k_thread_runtime_stats_get(tid, &helper_stats3);
249 
250 	/*
251 	 * Verify that the between sample sets 1 and 2 that additional stats
252 	 * were not gathered for the helper thread, but were gathered for the
253 	 * main current thread.
254 	 */
255 
256 	zassert_true(helper_stats1.execution_cycles ==
257 		     helper_stats2.execution_cycles, NULL);
258 	zassert_true(stats1.execution_cycles < stats2.execution_cycles);
259 
260 	/*
261 	 * Verify that between sample sets 2 and 3 that additional stats were
262 	 * gathered for the helper thread.
263 	 */
264 
265 	zassert_true(helper_stats2.execution_cycles <
266 		     helper_stats3.execution_cycles, NULL);
267 
268 	k_thread_abort(tid);
269 }
270 #else
ZTEST(usage_api,test_thread_stats_enable_disable)271 ZTEST(usage_api, test_thread_stats_enable_disable)
272 {
273 }
274 #endif
275 
276 #ifdef CONFIG_SCHED_THREAD_USAGE_ALL
277 /**
278  * @brief Test the k_sys_runtime_stats_enable/disable APIs
279  */
ZTEST(usage_api,test_sys_stats_enable_disable)280 ZTEST(usage_api, test_sys_stats_enable_disable)
281 {
282 	k_thread_runtime_stats_t  sys_stats1;
283 	k_thread_runtime_stats_t  sys_stats2;
284 	k_thread_runtime_stats_t  sys_stats3;
285 	k_thread_runtime_stats_t  thread_stats1;
286 	k_thread_runtime_stats_t  thread_stats2;
287 	k_thread_runtime_stats_t  thread_stats3;
288 
289 	/*
290 	 * Disable system runtime stats gathering.
291 	 * This should not impact thread runtime stats gathering.
292 	 */
293 
294 	k_sys_runtime_stats_disable();
295 
296 	k_thread_runtime_stats_get(_current, &thread_stats1);
297 	k_thread_runtime_stats_all_get(&sys_stats1);
298 
299 	busy_loop(2);
300 
301 	k_thread_runtime_stats_get(_current, &thread_stats2);
302 	k_thread_runtime_stats_all_get(&sys_stats2);
303 
304 	/*
305 	 * Enable system runtime stats gathering.
306 	 * This should not impact thread runtime stats gathering.
307 	 */
308 
309 	k_sys_runtime_stats_enable();
310 
311 	busy_loop(2);
312 
313 	k_thread_runtime_stats_get(_current, &thread_stats3);
314 	k_thread_runtime_stats_all_get(&sys_stats3);
315 
316 	/*
317 	 * There ought to be no differences between sys_stat1 and sys_stat2.
318 	 * Although a memory compare of the two structures would be sufficient,
319 	 * each individual field is being tested in case to more easily
320 	 * isolate the cause of any error.
321 	 */
322 
323 	zassert_true(sys_stats1.execution_cycles == sys_stats2.execution_cycles,
324 		     NULL);
325 	zassert_true(sys_stats1.total_cycles == sys_stats2.total_cycles);
326 
327 #ifdef CONFIG_SCHED_THREAD_USAGE_ANALYSIS
328 	zassert_true(sys_stats1.current_cycles == sys_stats2.current_cycles,
329 		     NULL);
330 	zassert_true(sys_stats1.peak_cycles == sys_stats2.peak_cycles);
331 	zassert_true(sys_stats1.average_cycles == sys_stats2.average_cycles,
332 		     NULL);
333 #endif
334 
335 #ifdef CONFIG_SCHED_THREAD_USAGE_ALL
336 	zassert_true(sys_stats1.idle_cycles == sys_stats2.idle_cycles);
337 #endif
338 
339 	/*
340 	 * As only system stats have been disabled, thread stats should be
341 	 * unaffected. To simplify things, just check [execution_cycles] and
342 	 * [current_cycles] (if enabled).
343 	 */
344 
345 	zassert_true(thread_stats1.execution_cycles <
346 		     thread_stats2.execution_cycles, NULL);
347 
348 #ifdef CONFIG_SCHED_THREAD_USAGE_ANALYSIS
349 	zassert_true(thread_stats1.current_cycles <
350 		     thread_stats2.current_cycles, NULL);
351 #endif
352 
353 	/*
354 	 * Now verify that the enabling of system runtime stats gathering
355 	 * has resulted in the gathering of system runtime stats. Again,
356 	 * thread runtime stats gathering should be unaffected.
357 	 */
358 
359 	zassert_true(sys_stats2.execution_cycles < sys_stats3.execution_cycles,
360 		     NULL);
361 	zassert_true(sys_stats2.total_cycles < sys_stats3.total_cycles);
362 
363 #ifdef CONFIG_SCHED_THREAD_USAGE_ANALYSIS
364 
365 	/*
366 	 * As enabling reset [current_cycles], it is not easy to predict
367 	 * what its value should be. For now, settle for ensuring that it
368 	 * is different and not zero.
369 	 */
370 
371 	zassert_true(sys_stats2.current_cycles != sys_stats3.current_cycles,
372 		     NULL);
373 	zassert_true(sys_stats2.current_cycles != 0);
374 	zassert_true(sys_stats2.peak_cycles == sys_stats3.peak_cycles);
375 	zassert_true(sys_stats2.average_cycles > sys_stats3.average_cycles,
376 		     NULL);
377 #endif
378 
379 #ifdef CONFIG_SCHED_THREAD_USAGE_ALL
380 	zassert_true(sys_stats2.idle_cycles == sys_stats3.idle_cycles);
381 #endif
382 }
383 #else
ZTEST(usage_api,test_sys_stats_enable_disable)384 ZTEST(usage_api, test_sys_stats_enable_disable)
385 {
386 }
387 #endif
388 
389 /**
390  * @brief Timer handler to resume the main thread
391  */
resume_main(struct k_timer * timer)392 void resume_main(struct k_timer *timer)
393 {
394 	k_thread_resume(main_thread);
395 }
396 
397 /**
398  * @brief Test the k_thread_runtime_stats_get() API
399  *
400  * This routine tests the k_thread_runtime_stats_get() routine. It verifies
401  * that the contents of the fields guarded by CONFIG_SCHED_THREAD_USAGE
402  * are correct.
403  */
ZTEST(usage_api,test_thread_stats_usage)404 ZTEST(usage_api, test_thread_stats_usage)
405 {
406 	k_tid_t  tid;
407 	int  priority;
408 	int  status;
409 	struct k_timer   timer;
410 	k_thread_runtime_stats_t  stats1;
411 	k_thread_runtime_stats_t  stats2;
412 	k_thread_runtime_stats_t  stats3;
413 
414 	priority = k_thread_priority_get(_current);
415 
416 	/*
417 	 * Verify that k_thread_runtime_stats_get() returns the expected
418 	 * values for error cases.
419 	 */
420 
421 	status = k_thread_runtime_stats_get(NULL, &stats1);
422 	zassert_true(status == -EINVAL);
423 
424 	status = k_thread_runtime_stats_get(_current, NULL);
425 	zassert_true(status == -EINVAL);
426 
427 	/* Align to the next tick */
428 
429 	k_sleep(K_TICKS(1));
430 
431 	/* Create a low priority helper thread to start in 1 tick. */
432 
433 	tid = k_thread_create(&helper_thread, helper_stack,
434 			      K_THREAD_STACK_SIZEOF(helper_stack),
435 			      helper1, NULL, NULL, NULL,
436 			      priority + 2, 0, K_TICKS(1));
437 
438 	main_thread = _current;
439 	k_timer_init(&timer, resume_main, NULL);
440 	k_timer_start(&timer, K_TICKS(1), K_TICKS(10));
441 
442 	/* Verify thread creation succeeded */
443 
444 	zassert_true(tid == &helper_thread);
445 
446 	/* Get a valid set of thread runtime stats */
447 
448 	status = k_thread_runtime_stats_get(tid, &stats1);
449 	zassert_true(status == 0);
450 
451 	/*
452 	 * Suspend main thread. Timer will wake it 1 tick to sample
453 	 * the helper threads runtime stats.
454 	 */
455 
456 	k_thread_suspend(_current);
457 
458 	/*
459 	 * T = 1.
460 	 * Timer woke the main thread. Sample runtime stats for helper thread
461 	 * before suspending.
462 	 */
463 
464 	k_thread_runtime_stats_get(tid, &stats1);
465 	k_thread_suspend(_current);
466 
467 	/*
468 	 * T = 11.
469 	 * Timer woke the main thread. Suspend main thread again.
470 	 */
471 
472 	k_thread_suspend(_current);
473 
474 	/*
475 	 * T = 21.
476 	 * Timer woke the main thread. Sample runtime stats for helper thread
477 	 * before suspending.
478 	 */
479 
480 	k_thread_runtime_stats_get(tid, &stats2);
481 	k_thread_suspend(_current);
482 
483 	/*
484 	 * T = 31.
485 	 * Timer woke the main thread. Sample runtime stats for helper thread
486 	 * and stop the timer.
487 	 */
488 
489 	k_thread_runtime_stats_get(tid, &stats3);
490 	k_timer_stop(&timer);
491 
492 #ifdef CONFIG_SCHED_THREAD_USAGE_ANALYSIS
493 	k_thread_runtime_stats_t  stats4;
494 	k_thread_runtime_stats_t  stats5;
495 
496 	/*
497 	 * Sleep for 20 ticks, and then 1 tick. This will allow the helper
498 	 * thread to have two different scheduled execution windows.
499 	 */
500 
501 	k_sleep(K_TICKS(20));
502 	k_thread_runtime_stats_get(tid, &stats4);
503 
504 	k_sleep(K_TICKS(1));
505 	k_thread_runtime_stats_get(tid, &stats5);
506 #endif
507 
508 	/* Verify execution_cycles are identical to total_cycles */
509 
510 	zassert_true(stats1.execution_cycles == stats1.total_cycles);
511 	zassert_true(stats2.execution_cycles == stats2.total_cycles);
512 
513 #ifdef CONFIG_SCHED_THREAD_USAGE_ALL
514 	zassert_true(stats3.idle_cycles == 0);
515 #endif
516 
517 	/*
518 	 * Verify that the time for which the helper thread executed between
519 	 * the first and second samplings is more than that between the
520 	 * second and third.
521 	 */
522 
523 	uint64_t  diff12;
524 	uint64_t  diff23;
525 
526 	diff12 = stats2.execution_cycles - stats1.execution_cycles;
527 	diff23 = stats3.execution_cycles - stats2.execution_cycles;
528 
529 	zassert_true(diff12 > diff23);
530 
531 #ifdef CONFIG_SCHED_THREAD_USAGE_ANALYSIS
532 
533 	/* Verify that [current_cycles] change as expected. */
534 
535 	zassert_true(stats4.current_cycles >= stats5.current_cycles);
536 	zassert_true(stats4.current_cycles > stats3.current_cycles);
537 	zassert_true(stats5.current_cycles < stats3.current_cycles);
538 
539 	/* Verify that [peak_cycles] change as expected */
540 
541 	zassert_true(stats4.peak_cycles > stats2.peak_cycles);
542 	zassert_true(stats4.peak_cycles == stats5.peak_cycles);
543 	zassert_true(stats4.peak_cycles == stats4.current_cycles);
544 
545 	/* Verify that [average_cycles] change as expected */
546 
547 	zassert_true(stats4.average_cycles > stats3.average_cycles);
548 	zassert_true(stats4.average_cycles > stats5.average_cycles);
549 	zassert_true(stats5.average_cycles >= stats3.average_cycles);
550 #endif
551 
552 	/* Abort the helper thread */
553 
554 	k_thread_abort(tid);
555 }
556 
557 ZTEST_SUITE(usage_api, NULL, NULL,
558 		ztest_simple_1cpu_before, ztest_simple_1cpu_after, NULL);
559