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