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