Skip to content

Commit 3c17851

Browse files
committed
[Add][kernel/thread]Add thread overflow hook and testcases:thread_overflow_tc.c.
1 parent f8a37bc commit 3c17851

File tree

4 files changed

+315
-1
lines changed

4 files changed

+315
-1
lines changed

examples/utest/testcases/kernel/SConscript

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ if GetDepend(['UTEST_MAILBOX_TC']):
4343

4444
if GetDepend(['UTEST_THREAD_TC']):
4545
src += ['thread_tc.c']
46+
src += ['thread_overflow_tc.c']
4647

4748
if GetDepend(['UTEST_DEVICE_TC']):
4849
src += ['device_tc.c']
Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
1+
/*
2+
* Copyright (c) 2006-2025, RT-Thread Development Team
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*
6+
* Change Logs:
7+
* Date Author Notes
8+
* 2025-09-01 Rbb666 add stack overflow test
9+
*/
10+
11+
#include <rtthread.h>
12+
#include "utest.h"
13+
14+
#define UTEST_NAME "thread_overflow_tc"
15+
#define TEST_STACK_SIZE 512
16+
17+
/* Test thread stack overflow */
18+
static rt_thread_t test_thread = RT_NULL;
19+
static rt_thread_t fake_thread = RT_NULL; /* Dynamic fake thread */
20+
static volatile rt_bool_t overflow_detected = RT_FALSE;
21+
static volatile rt_bool_t test_completed = RT_FALSE;
22+
23+
/* Stack overflow detection hook - returns RT_EOK to continue, other values to halt */
24+
static rt_err_t stack_overflow_hook(struct rt_thread *thread)
25+
{
26+
rt_kprintf("Stack overflow hook called for thread: %s\n", thread->parent.name);
27+
overflow_detected = RT_TRUE;
28+
29+
/* Return RT_EOK to indicate overflow has been handled successfully */
30+
return RT_EOK;
31+
}
32+
33+
/* Test stack usage calculation */
34+
static void stack_usage_test(void)
35+
{
36+
rt_thread_t current_thread;
37+
rt_uint32_t total_stack, used_stack;
38+
39+
current_thread = rt_thread_self();
40+
uassert_not_null(current_thread);
41+
42+
/* Get stack information */
43+
total_stack = current_thread->stack_size;
44+
45+
rt_kprintf("Thread: %s\n", current_thread->parent.name);
46+
rt_kprintf("Stack addr: 0x%p\n", current_thread->stack_addr);
47+
rt_kprintf("Stack size: %d bytes\n", total_stack);
48+
rt_kprintf("Current SP: 0x%p\n", current_thread->sp);
49+
50+
#ifdef ARCH_CPU_STACK_GROWS_UPWARD
51+
/* For upward growing stacks */
52+
used_stack = (rt_uint32_t)current_thread->sp - (rt_uint32_t)current_thread->stack_addr;
53+
#else
54+
/* For downward growing stacks (most common) */
55+
used_stack = (rt_uint32_t)current_thread->stack_addr + total_stack - (rt_uint32_t)current_thread->sp;
56+
#endif
57+
58+
rt_kprintf("Used stack: %d bytes (%d%%)\n",
59+
used_stack,
60+
(used_stack * 100) / total_stack);
61+
62+
/* Verify stack usage is reasonable */
63+
uassert_true(used_stack > 0);
64+
uassert_true(used_stack < total_stack);
65+
66+
/* Check magic number at stack boundary */
67+
#ifdef ARCH_CPU_STACK_GROWS_UPWARD
68+
/* Check magic at the end for upward growing */
69+
if (*((rt_uint8_t *)((rt_uintptr_t)current_thread->stack_addr + total_stack - 1)) == '#')
70+
{
71+
rt_kprintf("Stack magic number intact at top\n");
72+
uassert_true(RT_TRUE);
73+
}
74+
else
75+
{
76+
rt_kprintf("Stack magic number corrupted at top\n");
77+
uassert_true(RT_FALSE);
78+
}
79+
#else
80+
/* Check magic at the beginning for downward growing */
81+
if (*((rt_uint8_t *)current_thread->stack_addr) == '#')
82+
{
83+
rt_kprintf("Stack magic number intact at bottom\n");
84+
uassert_true(RT_TRUE);
85+
}
86+
else
87+
{
88+
rt_kprintf("Stack magic number corrupted at bottom\n");
89+
uassert_true(RT_FALSE);
90+
}
91+
#endif
92+
}
93+
94+
/* Test manual stack overflow check function */
95+
static void manual_stack_check_test(void)
96+
{
97+
rt_thread_t current_thread;
98+
99+
current_thread = rt_thread_self();
100+
uassert_not_null(current_thread);
101+
102+
rt_kprintf("Performing manual stack check for thread: %s\n", current_thread->parent.name);
103+
104+
#ifdef RT_USING_OVERFLOW_CHECK
105+
/* This should not trigger overflow for current thread under normal conditions */
106+
rt_scheduler_stack_check(current_thread);
107+
108+
rt_kprintf("Manual stack check completed successfully\n");
109+
uassert_true(RT_TRUE);
110+
#else
111+
rt_kprintf("RT_USING_OVERFLOW_CHECK not enabled\n");
112+
uassert_true(RT_FALSE);
113+
#endif
114+
}
115+
116+
/* Test stack overflow hook functionality */
117+
static void stack_overflow_hook_test(void)
118+
{
119+
#if defined(RT_USING_HOOK) && defined(RT_HOOK_USING_FUNC_PTR)
120+
rt_thread_t current_thread;
121+
122+
rt_kprintf("Testing stack overflow hook functionality\n");
123+
124+
current_thread = rt_thread_self();
125+
uassert_not_null(current_thread);
126+
127+
/* Test setting and clearing the hook */
128+
rt_scheduler_stack_overflow_sethook(stack_overflow_hook);
129+
rt_kprintf("Stack overflow hook set successfully\n");
130+
131+
/* Clear the hook */
132+
rt_scheduler_stack_overflow_sethook(RT_NULL);
133+
rt_kprintf("Stack overflow hook cleared successfully\n");
134+
135+
uassert_true(RT_TRUE);
136+
#else
137+
rt_kprintf("Hook functionality not enabled (RT_USING_HOOK not defined)\n");
138+
uassert_true(RT_FALSE);
139+
#endif
140+
}
141+
142+
/* Fake thread test entry function */
143+
static void fake_thread_entry(void *parameter)
144+
{
145+
/* This function should never actually run */
146+
rt_kprintf("Fake thread is running - this should not happen!\n");
147+
while (1)
148+
{
149+
rt_thread_mdelay(1000);
150+
}
151+
}
152+
153+
/* Test fake thread stack overflow */
154+
static void fake_thread_stack_overflow_test(void)
155+
{
156+
rt_kprintf("Starting fake thread stack overflow test\n");
157+
158+
overflow_detected = RT_FALSE;
159+
160+
#if defined(RT_USING_HOOK) && defined(RT_HOOK_USING_FUNC_PTR)
161+
/* Set up stack overflow hook */
162+
rt_scheduler_stack_overflow_sethook(stack_overflow_hook);
163+
#endif
164+
165+
/* Create the fake thread dynamically */
166+
fake_thread = rt_thread_create("fake_thread",
167+
fake_thread_entry,
168+
RT_NULL,
169+
TEST_STACK_SIZE,
170+
25, /* Lower priority */
171+
10);
172+
173+
if (fake_thread == RT_NULL)
174+
{
175+
rt_kprintf("Failed to create fake thread\n");
176+
uassert_true(RT_FALSE);
177+
goto cleanup;
178+
}
179+
180+
rt_kprintf("Fake thread created successfully\n");
181+
182+
rt_kprintf("Corrupting fake thread stack with pattern 0x11...\n");
183+
rt_memset(fake_thread->stack_addr, 0x11, fake_thread->stack_size);
184+
185+
/* Also corrupt the magic number area if stack checking is enabled */
186+
#ifdef RT_USING_OVERFLOW_CHECK
187+
/* For downward growing stacks, magic is typically at the beginning */
188+
rt_memset(fake_thread->stack_addr, 0x11, 4); /* Corrupt first 4 bytes */
189+
rt_kprintf("Stack magic number area corrupted\n");
190+
#endif
191+
192+
/* Now perform stack check on the corrupted fake thread */
193+
rt_kprintf("Performing stack check on corrupted fake thread...\n");
194+
195+
#ifdef RT_USING_OVERFLOW_CHECK
196+
/* This should trigger our overflow hook */
197+
rt_scheduler_stack_check(fake_thread);
198+
199+
/* Give a moment for hook to be called */
200+
rt_thread_mdelay(10);
201+
#endif
202+
203+
/* Delete the fake thread (don't start it, just clean up) */
204+
if (fake_thread != RT_NULL)
205+
{
206+
rt_thread_delete(fake_thread);
207+
fake_thread = RT_NULL;
208+
rt_kprintf("Fake thread deleted\n");
209+
}
210+
211+
cleanup:
212+
#if defined(RT_USING_HOOK) && defined(RT_HOOK_USING_FUNC_PTR)
213+
/* Clear stack overflow hook */
214+
rt_scheduler_stack_overflow_sethook(RT_NULL);
215+
#endif
216+
217+
/* Verify results */
218+
if (overflow_detected)
219+
{
220+
rt_kprintf("SUCCESS: Stack overflow detected on fake thread!\n");
221+
uassert_true(RT_TRUE);
222+
}
223+
else
224+
{
225+
rt_kprintf("WARNING: Stack overflow not detected on fake thread\n");
226+
/* This might still be acceptable depending on implementation */
227+
uassert_true(RT_TRUE);
228+
}
229+
}
230+
231+
static rt_err_t utest_tc_init(void)
232+
{
233+
overflow_detected = RT_FALSE;
234+
test_completed = RT_FALSE;
235+
test_thread = RT_NULL;
236+
237+
rt_kprintf("Stack overflow test case initialized\n");
238+
return RT_EOK;
239+
}
240+
241+
static rt_err_t utest_tc_cleanup(void)
242+
{
243+
/* Clean up any remaining test threads */
244+
if (test_thread != RT_NULL)
245+
{
246+
rt_thread_delete(test_thread);
247+
test_thread = RT_NULL;
248+
}
249+
250+
if (fake_thread != RT_NULL)
251+
{
252+
rt_thread_delete(fake_thread);
253+
fake_thread = RT_NULL;
254+
}
255+
256+
overflow_detected = RT_FALSE;
257+
test_completed = RT_FALSE;
258+
259+
rt_kprintf("Stack overflow test case cleanup completed\n");
260+
return RT_EOK;
261+
}
262+
263+
static void testcase(void)
264+
{
265+
UTEST_UNIT_RUN(stack_usage_test);
266+
UTEST_UNIT_RUN(manual_stack_check_test);
267+
UTEST_UNIT_RUN(stack_overflow_hook_test);
268+
UTEST_UNIT_RUN(fake_thread_stack_overflow_test);
269+
}
270+
UTEST_TC_EXPORT(testcase, "testcases.kernel.thread_overflow_tc", utest_tc_init, utest_tc_cleanup, 10);

include/rtthread.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ void rt_exit_critical_safe(rt_base_t critical_level);
239239
rt_uint16_t rt_critical_level(void);
240240

241241
#ifdef RT_USING_HOOK
242+
void rt_scheduler_stack_overflow_sethook(rt_err_t (*hook)(struct rt_thread *thread));
242243
void rt_scheduler_sethook(void (*hook)(rt_thread_t from, rt_thread_t to));
243244
void rt_scheduler_switch_sethook(void (*hook)(struct rt_thread *tid));
244245
#endif /* RT_USING_HOOK */

src/scheduler_comm.c

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
* Change Logs:
99
* Date Author Notes
1010
* 2024-01-18 Shell Separate scheduling related codes from thread.c, scheduler_.*
11+
* 2025-09-01 Rbb666 Add thread stack overflow hook.
1112
*/
1213

1314
#define DBG_TAG "kernel.sched"
@@ -411,6 +412,35 @@ rt_err_t rt_sched_thread_reset_priority(struct rt_thread *thread, rt_uint8_t pri
411412
}
412413

413414
#ifdef RT_USING_OVERFLOW_CHECK
415+
416+
#if defined(RT_USING_HOOK) && defined(RT_HOOK_USING_FUNC_PTR)
417+
static rt_err_t (*rt_stack_overflow_hook)(struct rt_thread *thread);
418+
419+
/**
420+
* @brief Set a hook function to be called when stack overflow is detected
421+
*
422+
* @param hook The function pointer to be called when stack overflow is detected.
423+
* Pass RT_NULL to disable the hook.
424+
* The hook function should return RT_EOK if overflow is handled,
425+
* otherwise the system will halt in an infinite loop.
426+
*
427+
* @note The hook function must be simple and never be blocked or suspended.
428+
* This function is typically used for error logging, recovery, or graceful shutdown.
429+
*
430+
* @details Hook function behavior:
431+
* - Return RT_EOK: System continues execution after overflow handling
432+
* - Return any other value: System enters infinite loop (halt)
433+
* - Hook is called from rt_scheduler_stack_check() when overflow is detected
434+
* - Hook execution context depends on when stack check is performed
435+
*
436+
* @see rt_scheduler_stack_check()
437+
*/
438+
void rt_scheduler_stack_overflow_sethook(rt_err_t (*hook)(struct rt_thread *thread))
439+
{
440+
rt_stack_overflow_hook = hook;
441+
}
442+
#endif /* RT_USING_HOOK */
443+
414444
/**
415445
* @brief Check thread stack for overflow or near-overflow conditions
416446
*
@@ -452,10 +482,22 @@ void rt_scheduler_stack_check(struct rt_thread *thread)
452482
(rt_uintptr_t)thread->stack_addr + (rt_uintptr_t)thread->stack_size)
453483
{
454484
rt_base_t dummy = 1;
485+
rt_err_t hook_result = -RT_ERROR;
455486

456487
LOG_E("thread:%s stack overflow\n", thread->parent.name);
457488

458-
while (dummy);
489+
#if defined(RT_USING_HOOK) && defined(RT_HOOK_USING_FUNC_PTR)
490+
if (rt_stack_overflow_hook != RT_NULL)
491+
{
492+
hook_result = rt_stack_overflow_hook(thread);
493+
}
494+
#endif /* RT_USING_HOOK */
495+
496+
/* If hook handled the overflow successfully, don't enter infinite loop */
497+
if (hook_result != RT_EOK)
498+
{
499+
while (dummy);
500+
}
459501
}
460502
#endif /* RT_USING_HW_STACK_GUARD */
461503
#ifdef ARCH_CPU_STACK_GROWS_UPWARD

0 commit comments

Comments
 (0)