Skip to content

Commit 6d15ce6

Browse files
committed
test: add scheduler trace validation app
1 parent 63ce0ae commit 6d15ce6

1 file changed

Lines changed: 252 additions & 0 deletions

File tree

app/trace_sched.c

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

0 commit comments

Comments
 (0)