Skip to content

Commit 8d34c6d

Browse files
committed
test: add scheduler trace validation app
1 parent fbd651d commit 8d34c6d

1 file changed

Lines changed: 250 additions & 0 deletions

File tree

app/trace_sched.c

Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
#include <linmo.h>
2+
#include <sys/debug_trace.h>
3+
#include <sys/logger.h>
4+
5+
#if CONFIG_DEBUG_TRACE
6+
typedef struct {
7+
uint16_t worker_a;
8+
uint16_t worker_b;
9+
uint16_t verifier;
10+
} trace_sched_ids_t;
11+
12+
static int tests_passed;
13+
static int tests_failed;
14+
static volatile bool scenario_done;
15+
static volatile bool worker_a_ready;
16+
static volatile bool worker_b_ready;
17+
static volatile bool worker_b_suspended;
18+
static volatile bool worker_b_resumed;
19+
static volatile bool worker_b_destroyed;
20+
static trace_sched_ids_t ids;
21+
22+
#define TEST_ASSERT(condition, description) \
23+
do { \
24+
if (condition) { \
25+
printf("PASS: %s\n", description); \
26+
tests_passed++; \
27+
} else { \
28+
printf("FAIL: %s\n", description); \
29+
tests_failed++; \
30+
} \
31+
} while (0)
32+
33+
static int find_event_by_type(uint8_t type, uint32_t p1, uint32_t p2,
34+
uint32_t start_index)
35+
{
36+
uint32_t count = debug_trace_count();
37+
38+
for (uint32_t i = start_index; i < count; i++) {
39+
debug_event_t event;
40+
41+
if (debug_trace_get(i, &event) < 0)
42+
break;
43+
44+
if (event.event_type == type && event.param1 == p1 &&
45+
event.param2 == p2)
46+
return (int) i;
47+
}
48+
49+
return -1;
50+
}
51+
52+
static int find_event_by_type_any(uint8_t type, uint32_t p1)
53+
{
54+
uint32_t count = debug_trace_count();
55+
56+
for (uint32_t i = 0; i < count; i++) {
57+
debug_event_t event;
58+
59+
if (debug_trace_get(i, &event) < 0)
60+
break;
61+
62+
if (event.event_type == type && event.param1 == p1)
63+
return (int) i;
64+
}
65+
66+
return -1;
67+
}
68+
69+
static bool assert_event(const char *name, bool ok)
70+
{
71+
TEST_ASSERT(ok, name);
72+
return ok;
73+
}
74+
75+
static void flood_yield_events(int iterations)
76+
{
77+
for (int i = 0; i < iterations; i++)
78+
mo_task_yield();
79+
}
80+
81+
static void assert_monotonic_timestamps(void)
82+
{
83+
debug_event_t prev;
84+
debug_event_t curr;
85+
uint32_t count = debug_trace_count();
86+
87+
if (count < 2) {
88+
TEST_ASSERT(true, "trace timestamps are monotonic");
89+
return;
90+
}
91+
92+
for (uint32_t i = 1; i < count; i++) {
93+
if (debug_trace_get(i - 1, &prev) < 0 || debug_trace_get(i, &curr) < 0) {
94+
TEST_ASSERT(false, "trace timestamps are monotonic");
95+
return;
96+
}
97+
98+
if (prev.timestamp > curr.timestamp) {
99+
TEST_ASSERT(false, "trace timestamps are monotonic");
100+
return;
101+
}
102+
}
103+
104+
TEST_ASSERT(true, "trace timestamps are monotonic");
105+
}
106+
107+
static void worker_a(void)
108+
{
109+
worker_a_ready = true;
110+
while (!worker_b_ready)
111+
mo_task_wfi();
112+
mo_task_yield();
113+
mo_task_delay(2);
114+
mo_task_suspend(ids.worker_b);
115+
worker_b_suspended = true;
116+
scenario_done = true;
117+
118+
while (!worker_b_destroyed)
119+
mo_task_wfi();
120+
121+
for (;;)
122+
mo_task_wfi();
123+
}
124+
125+
static void worker_b(void)
126+
{
127+
worker_b_ready = true;
128+
while (!worker_a_ready)
129+
mo_task_wfi();
130+
131+
while (!worker_b_suspended)
132+
mo_task_wfi();
133+
134+
while (!worker_b_resumed)
135+
mo_task_wfi();
136+
137+
while (!worker_b_destroyed)
138+
mo_task_wfi();
139+
140+
for (;;)
141+
mo_task_wfi();
142+
}
143+
144+
static void verifier(void)
145+
{
146+
while (!scenario_done)
147+
mo_task_wfi();
148+
149+
if (mo_task_resume(ids.worker_b) == 0)
150+
worker_b_resumed = true;
151+
152+
if (mo_task_cancel(ids.worker_b) == 0)
153+
worker_b_destroyed = true;
154+
155+
int create_a = find_event_by_type(EVENT_TASK_CREATE, ids.worker_a, 4U, 0);
156+
int create_b = find_event_by_type(EVENT_TASK_CREATE, ids.worker_b, 4U, 0);
157+
int yield_a = find_event_by_type(EVENT_TASK_YIELD, ids.worker_a, 0U, 0);
158+
int delay_a = find_event_by_type(EVENT_TASK_DELAY, 2U, 0U, 0);
159+
int suspend_b = find_event_by_type(EVENT_TASK_SUSPEND, ids.worker_b, 0U,
160+
0U);
161+
int resume_b = find_event_by_type(EVENT_TASK_RESUME, ids.worker_b, 0U, 0);
162+
int destroy_b = find_event_by_type_any(EVENT_TASK_DESTROY, ids.worker_b);
163+
164+
assert_event("CREATE worker_a prio 4", create_a >= 0);
165+
assert_event("CREATE worker_b prio 4", create_b >= 0);
166+
assert_event("YIELD worker_a", yield_a >= 0);
167+
assert_event("DELAY worker_a ticks 2", delay_a >= 0);
168+
assert_event("SUSPEND worker_b", suspend_b >= 0);
169+
assert_event("RESUME worker_b", resume_b >= 0);
170+
assert_event("DESTROY worker_b", destroy_b >= 0);
171+
assert_event("CREATE events precede worker_a yield",
172+
create_a >= 0 && create_b >= 0 && yield_a >= 0 &&
173+
create_a < yield_a && create_b < yield_a);
174+
assert_event("worker_a yield precedes delay",
175+
yield_a >= 0 && delay_a >= 0 && yield_a < delay_a);
176+
assert_event("worker_a delay precedes worker_b suspend",
177+
delay_a >= 0 && suspend_b >= 0 && delay_a < suspend_b);
178+
assert_event("worker_b suspend precedes resume",
179+
suspend_b >= 0 && resume_b >= 0 && suspend_b < resume_b);
180+
assert_event("worker_b resume precedes destroy",
181+
resume_b >= 0 && destroy_b >= 0 && resume_b < destroy_b);
182+
183+
bool found_switch = false;
184+
for (uint32_t i = 0; i < debug_trace_count(); i++) {
185+
debug_event_t event;
186+
187+
if (debug_trace_get(i, &event) < 0)
188+
break;
189+
190+
if (event.event_type == EVENT_TASK_SWITCH &&
191+
((event.param1 == ids.worker_a && event.param2 == ids.worker_b) ||
192+
(event.param1 == ids.worker_b &&
193+
event.param2 == ids.worker_a))) {
194+
assert_event("SWITCH between worker_a and worker_b", true);
195+
found_switch = true;
196+
break;
197+
}
198+
}
199+
if (!found_switch)
200+
assert_event("SWITCH between worker_a and worker_b", false);
201+
202+
flood_yield_events(DEBUG_EVENT_BUFFER_SIZE + 8);
203+
TEST_ASSERT(debug_trace_overwrites() > 0,
204+
"trace reports overwrite after wraparound flood");
205+
assert_monotonic_timestamps();
206+
207+
mo_logger_flush();
208+
printf("Tests passed: %d\n", tests_passed);
209+
printf("Tests failed: %d\n", tests_failed);
210+
printf("Overall: %s\n", tests_failed == 0 ? "PASS" : "FAIL");
211+
if (tests_failed != 0)
212+
debug_dump_events();
213+
214+
*(volatile uint32_t *) 0x100000U = 0x5555U;
215+
for (;;)
216+
mo_task_wfi();
217+
}
218+
219+
int32_t app_main(void)
220+
{
221+
debug_clear_events();
222+
tests_passed = 0;
223+
tests_failed = 0;
224+
scenario_done = false;
225+
worker_a_ready = false;
226+
worker_b_ready = false;
227+
worker_b_suspended = false;
228+
worker_b_resumed = false;
229+
worker_b_destroyed = false;
230+
231+
ids.worker_a = mo_task_spawn(worker_a, DEFAULT_STACK_SIZE);
232+
ids.worker_b = mo_task_spawn(worker_b, DEFAULT_STACK_SIZE);
233+
ids.verifier = mo_task_spawn(verifier, DEFAULT_STACK_SIZE);
234+
235+
mo_task_priority(ids.worker_a, TASK_PRIO_NORMAL);
236+
mo_task_priority(ids.worker_b, TASK_PRIO_NORMAL);
237+
mo_task_priority(ids.verifier, TASK_PRIO_IDLE);
238+
239+
return 1;
240+
}
241+
#else
242+
int32_t app_main(void)
243+
{
244+
debug_clear_events();
245+
mo_logger_flush();
246+
printf("Tracing disabled build: PASS\n");
247+
*(volatile uint32_t *) 0x100000U = 0x5555U;
248+
return 0;
249+
}
250+
#endif

0 commit comments

Comments
 (0)