mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
tests/malloc_thread_safety: improve test
- fix false test failure on picolibc - For picolibc, the result of mallinfo().uordblks doesn't go back to zero after the first allocation. By adding `free(malloc(4))` before recording the pre-test mallinfo() value, this issues is worked arround - increase test coverage to also cover realloc() - ease testing with picolibc by allowing `make PICOLIBC=1 flash test`
This commit is contained in:
parent
8f93e42e9c
commit
02e069a357
@ -1,5 +1,9 @@
|
|||||||
include ../Makefile.tests_common
|
include ../Makefile.tests_common
|
||||||
|
|
||||||
|
PICOLIBC ?= 0
|
||||||
|
ifneq (0,$(PICOLIBC))
|
||||||
|
FEATURES_REQUIRED += picolibc
|
||||||
|
endif
|
||||||
USEMODULE += xtimer
|
USEMODULE += xtimer
|
||||||
|
|
||||||
include $(RIOTBASE)/Makefile.include
|
include $(RIOTBASE)/Makefile.include
|
||||||
|
@ -39,7 +39,7 @@ static char WORD_ALIGNED t1_stack[THREAD_STACKSIZE_SMALL];
|
|||||||
static char WORD_ALIGNED t2_stack[THREAD_STACKSIZE_SMALL];
|
static char WORD_ALIGNED t2_stack[THREAD_STACKSIZE_SMALL];
|
||||||
static atomic_uint_least8_t is_running = ATOMIC_VAR_INIT(1);
|
static atomic_uint_least8_t is_running = ATOMIC_VAR_INIT(1);
|
||||||
|
|
||||||
void * t1_t2_func(void *arg)
|
void * t1_t2_malloc_func(void *arg)
|
||||||
{
|
{
|
||||||
(void)arg;
|
(void)arg;
|
||||||
while (atomic_load(&is_running)) {
|
while (atomic_load(&is_running)) {
|
||||||
@ -57,6 +57,30 @@ void * t1_t2_func(void *arg)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void * t1_t2_realloc_func(void *arg)
|
||||||
|
{
|
||||||
|
(void)arg;
|
||||||
|
while (atomic_load(&is_running)) {
|
||||||
|
int *chunk = realloc(NULL, sizeof(int) * 1);
|
||||||
|
expect(chunk);
|
||||||
|
/* cppcheck-suppress memleakOnRealloc
|
||||||
|
* no need to free data on allocation failure, as expect() terminates then anyway */
|
||||||
|
chunk = realloc(chunk, sizeof(int) * 2);
|
||||||
|
expect(chunk);
|
||||||
|
/* cppcheck-suppress memleakOnRealloc
|
||||||
|
* no need to free data on allocation failure, as expect() terminates then anyway */
|
||||||
|
chunk = realloc(chunk, sizeof(int) * 4);
|
||||||
|
expect(chunk);
|
||||||
|
/* cppcheck-suppress memleakOnRealloc
|
||||||
|
* no need to free data on allocation failure, as expect() terminates then anyway */
|
||||||
|
chunk = realloc(chunk, sizeof(int) * 8);
|
||||||
|
expect(chunk);
|
||||||
|
free(chunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static void evil_schedule_hack_dont_do_this_at_home(uint8_t prio)
|
static void evil_schedule_hack_dont_do_this_at_home(uint8_t prio)
|
||||||
{
|
{
|
||||||
extern clist_node_t sched_runqueues[SCHED_PRIO_LEVELS];
|
extern clist_node_t sched_runqueues[SCHED_PRIO_LEVELS];
|
||||||
@ -66,6 +90,7 @@ static void evil_schedule_hack_dont_do_this_at_home(uint8_t prio)
|
|||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
kernel_pid_t t1, t2;
|
kernel_pid_t t1, t2;
|
||||||
|
int failed = 0;
|
||||||
puts(
|
puts(
|
||||||
"Test Application for multithreaded use of malloc()\n"
|
"Test Application for multithreaded use of malloc()\n"
|
||||||
"==================================================\n"
|
"==================================================\n"
|
||||||
@ -78,41 +103,67 @@ int main(void)
|
|||||||
);
|
);
|
||||||
|
|
||||||
#ifndef NO_MALLINFO
|
#ifndef NO_MALLINFO
|
||||||
|
/* in case the malloc implementation dynamically allocates management structures,
|
||||||
|
* do one malloc() / free() to obtain the baseline for mallinfo()
|
||||||
|
*/
|
||||||
|
free(malloc(sizeof(int)));
|
||||||
struct mallinfo pre = mallinfo();
|
struct mallinfo pre = mallinfo();
|
||||||
#else
|
#else
|
||||||
puts("WARNING: Use of mallinfo() disabled.\n");
|
puts("WARNING: Use of mallinfo() disabled.\n");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
t1 = thread_create(t1_stack, sizeof(t1_stack), THREAD_PRIORITY_MAIN + 1,
|
void *(* const funcs[])(void *) = { t1_t2_malloc_func, t1_t2_realloc_func };
|
||||||
THREAD_CREATE_STACKTEST, t1_t2_func, NULL, "t1");
|
const char *tests[] = { "malloc()/free()", "realloc()/free()" };
|
||||||
t2 = thread_create(t2_stack, sizeof(t2_stack), THREAD_PRIORITY_MAIN + 1,
|
|
||||||
THREAD_CREATE_STACKTEST, t1_t2_func, NULL, "t2");
|
|
||||||
expect((t1 != KERNEL_PID_UNDEF) && (t2 != KERNEL_PID_UNDEF));
|
|
||||||
|
|
||||||
for (uint16_t i = 0; i < 2 * MS_PER_SEC; i++) {
|
for (size_t i = 0; i < ARRAY_SIZE(funcs); i++) {
|
||||||
xtimer_usleep(US_PER_MS);
|
printf("Testing: %s\n", tests[i]);
|
||||||
/* shuffle t1 and t2 in their run queue. This should eventually hit
|
t1 = thread_create(t1_stack, sizeof(t1_stack), THREAD_PRIORITY_MAIN + 1,
|
||||||
* during a call to malloc() or free() and disclose any missing
|
THREAD_CREATE_STACKTEST, funcs[i], NULL, "t1");
|
||||||
* guards */
|
t2 = thread_create(t2_stack, sizeof(t2_stack), THREAD_PRIORITY_MAIN + 1,
|
||||||
evil_schedule_hack_dont_do_this_at_home(THREAD_PRIORITY_MAIN + 1);
|
THREAD_CREATE_STACKTEST, funcs[i], NULL, "t2");
|
||||||
}
|
expect((t1 != KERNEL_PID_UNDEF) && (t2 != KERNEL_PID_UNDEF));
|
||||||
|
|
||||||
/* Don't keep threads spinning */
|
for (uint16_t i = 0; i < 2 * MS_PER_SEC; i++) {
|
||||||
atomic_store(&is_running, 0);
|
xtimer_usleep(US_PER_MS);
|
||||||
/* Give threads time to terminate */
|
/* shuffle t1 and t2 in their run queue. This should eventually hit
|
||||||
xtimer_usleep(10 * US_PER_MS);
|
* during a call to malloc() or free() and disclose any missing
|
||||||
|
* guards */
|
||||||
|
evil_schedule_hack_dont_do_this_at_home(THREAD_PRIORITY_MAIN + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Don't keep threads spinning */
|
||||||
|
atomic_store(&is_running, 0);
|
||||||
|
/* Give threads time to terminate */
|
||||||
|
xtimer_usleep(10 * US_PER_MS);
|
||||||
|
|
||||||
#ifndef NO_MALLINFO
|
#ifndef NO_MALLINFO
|
||||||
struct mallinfo post = mallinfo();
|
struct mallinfo post = mallinfo();
|
||||||
|
|
||||||
/* RIOT's board or arch support hopefully doesn't use malloc, so there
|
/* RIOT's board or arch support hopefully doesn't use malloc, so there
|
||||||
* should be zero bytes allocated prior to the first call to malloc() in
|
* should be zero bytes allocated prior to the first call to malloc() in
|
||||||
* this test. But let's be forgiving and just expect that the number of
|
* this test. But let's be forgiving and just expect that the number of
|
||||||
* allocated bytes before and after the test is equal.
|
* allocated bytes before and after the test is equal.
|
||||||
*/
|
*/
|
||||||
expect(pre.uordblks == post.uordblks);
|
if (pre.uordblks != post.uordblks) {
|
||||||
|
failed = 1;
|
||||||
|
puts("Not all blocks were correctly freed!");
|
||||||
|
printf("mallinfo().uordblks before test: %u, after test: %u\n",
|
||||||
|
(unsigned)pre.uordblks, (unsigned)post.uordblks);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
puts("TEST PASSED");
|
}
|
||||||
|
|
||||||
|
/* cppcheck-suppress knownConditionTrueFalse
|
||||||
|
* The actual test is that this application doesn't crash / hang due to memory corruptions.
|
||||||
|
* But if there is mallinfo() provided, we can also test for memory leaks in the malloc()
|
||||||
|
* implementation pretty much for free. Even if this implementation most likely comes from
|
||||||
|
* the standard C lib, it is still good to be at least aware of bugs in the used toolchain */
|
||||||
|
if (failed) {
|
||||||
|
puts("TEST FAILED");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
puts("TEST PASSED");
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user