Skip to content

malloc test refactoring #5323

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 22, 2017
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 112 additions & 31 deletions TESTS/mbedmicro-rtos-mbed/malloc/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,80 +14,161 @@
* limitations under the License.
*/
#include "mbed.h"
#include "test_env.h"
#include "rtos.h"
#include "greentea-client/test_env.h"
#include "utest/utest.h"
#include "unity/unity.h"


#if defined(MBED_RTOS_SINGLE_THREAD)
#error [NOT_SUPPORTED] test not supported
#endif

#define NUM_THREADS 5
using utest::v1::Case;

extern uint32_t mbed_heap_size;
static const int test_timeout = 25;
volatile bool thread_should_continue = true;
#define NUM_THREADS 4
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did the number of threads need to be decreased to pass testing?

Copy link
Contributor Author

@maciejbocianski maciejbocianski Oct 19, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could restore back thread count to 5, but it will cause fail on GCC_ARM.
To run 5 threads on GCC_ARM we have to free additional ~300B of heap memory.

To achieve this we could reduce thread allocation to 20B or move part of Thread objects on stack (but can not move all of them because we reach main thread stack limit).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do 5 threads no longer fit?

Copy link
Contributor Author

@maciejbocianski maciejbocianski Oct 25, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it turns out that refactoring (new test cases, functions, ...) increased static RAM usage by 480B as a result less memory remains for heap and fifth thread stack allocation fails

python tools/memap.py BUILD/tests/nucleo_f070rb/GCC_ARM/TESTS/mbedmicro-rtos-mbed/malloc/malloc.map -t GCC_ARM

befor refactoring
Total Static RAM memory (data + bss): 10172 bytes

after refactoring
Total Static RAM memory (data + bss): 10652 bytes

diff 
Total Static RAM memory (data + bss): 480 bytes
extern uint32_t mbed_heap_size;
printf("mbed_heap_size: %lu  \r\n", mbed_heap_size);

befor refactoring
mbed_heap_size: 4996

after refactoring
mbed_heap_size: 4516

diff 
mbed_heap_size: 480

#define THREAD_MALLOC_SIZE 100

#if defined(__CORTEX_A9)
#define THREAD_STACK_SIZE DEFAULT_STACK_SIZE
#else
#define THREAD_STACK_SIZE 256
#endif

DigitalOut led1(LED1);
volatile bool should_exit = false;
volatile bool allocation_failure = false;

void task_using_malloc(void)
{
void* data;
while (1) {
void *data = NULL;

while (thread_should_continue) {
// Repeatedly allocate and free memory
data = malloc(100);
if (data != NULL) {
memset(data, 0, 100);
} else {
allocation_failure = true;
}
free(data);
data = malloc(THREAD_MALLOC_SIZE);
TEST_ASSERT_NOT_NULL(data);

if (should_exit) {
return;
}
// test whole allocated memory
memset(data, 0, THREAD_MALLOC_SIZE);

free(data);
}
}

int main()
/** Test for multithreaded heap allocations

Given multiple threads are started in parallel
When each of the threads allocate memory
Then the memory allocation succeed and @a malloc return valid memory
*/
void test_multithread_allocation(void)
{
// static stack for threads to reduce heap usage on devices with small RAM
// and eliminate run out of heap memory problem
uint8_t stack[NUM_THREADS][THREAD_STACK_SIZE];

bool thread_alloc_failure = false;
Thread *thread_list[NUM_THREADS];
int test_time = 15;
GREENTEA_SETUP(20, "default_auto");
int test_time = 20;

// Allocate threads for the test
for (int i = 0; i < NUM_THREADS; i++) {
thread_list[i] = new Thread(osPriorityNormal, THREAD_STACK_SIZE, stack[i]);
if (NULL == thread_list[i]) {
allocation_failure = true;
thread_alloc_failure = true;
} else {
thread_list[i]->start(task_using_malloc);
}
}

// Give the test time to run
while (test_time) {
led1 = !led1;
while (test_time--) {
Thread::wait(1000);
test_time--;
}

// Join and delete all threads
should_exit = 1;
thread_should_continue = false;
for (int i = 0; i < NUM_THREADS; i++) {
if (NULL == thread_list[i]) {
continue;
if (NULL != thread_list[i]) {
thread_list[i]->join();
delete thread_list[i];
thread_list[i] = NULL;
}
thread_list[i]->join();
delete thread_list[i];
}
TEST_ASSERT_FALSE(thread_alloc_failure);
}

/** Test for large heap allocation

Given a heap of size mbed_heap_size
When try to allocate memory of size mbed_heap_size/5 (20% of whole heap)
Then the memory is allocated and @a malloc return valid memory
*/
void test_big_allocation(void)
{
const uint32_t alloc_size = mbed_heap_size / 5;
void *data = NULL;

data = malloc(alloc_size);
TEST_ASSERT_NOT_NULL(data);

// test whole allocated memory
memset(data, 0, alloc_size);

free(data);
}

/** Test if allocation of zero size does not cause any undefined behaviour

Given a heap
When try to allocate memory of size 0
Then the return value of @a malloc depends on the particular library implementation
(NULL or smallest possible allocation) and no undefined behaviour happens

@note If allocation size is zero, the return value depends on the particular library implementation
(it may or may not be a null pointer), but the returned pointer shall not be dereferenced
*/
void test_zero_allocation(void)
{
void *data = NULL;

data = malloc(0);
if(data != NULL) {
free(data);
}
TEST_ASSERT_MESSAGE(true, "malloc(0) succeed - no undefined behaviour happens");
}

/** Test if free on NULL pointer does not cause any undefined behaviour

Given a NULL pointer
When try to free it
Then the function @a free does nothing and no undefined behaviour happens
*/
void test_null_free(void)
{
void *data = NULL;
free(data);

TEST_ASSERT_MESSAGE(true, "free(NULL) succeed - no undefined behaviour happens");
}

// Test cases
Case cases[] = {
Case("Test 0 size allocation", test_zero_allocation),
Case("Test NULL pointer free", test_null_free),
Case("Test multithreaded allocations", test_multithread_allocation),
Case("Test large allocation", test_big_allocation)
};

GREENTEA_TESTSUITE_RESULT(!allocation_failure);
utest::v1::status_t greentea_test_setup(const size_t number_of_cases)
{
GREENTEA_SETUP(test_timeout, "timing_drift_auto");
return utest::v1::greentea_test_setup_handler(number_of_cases);
}

utest::v1::Specification specification(greentea_test_setup, cases);

int main()
{
return !utest::v1::Harness::run(specification);
}