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