Skip to content

NVStore tests: Tune memory consumption; stop threads greafully #7127

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
Jul 11, 2018
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
120 changes: 83 additions & 37 deletions features/nvstore/TESTS/nvstore/functionality/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include <stdlib.h>
#include <stdio.h>
#include <algorithm>
#include "mbed_stats.h"

#if !NVSTORE_ENABLED
#error [NOT_SUPPORTED] NVSTORE needs to be enabled for this test
Expand All @@ -42,26 +43,24 @@ static const int thr_test_num_secs = 5;
static const int thr_test_max_data_size = 32;
static const int thr_test_num_threads = 3;

#ifdef TARGET_NRF52
static const int thr_test_stack_size = 1024;
#else
static const int thr_test_stack_size = 768;
#endif
static const int thr_test_min_stack_size = 768;
static const int thr_test_max_stack_size = 1024;


typedef struct {
uint8_t *buffs[max_test_keys][thr_test_num_buffs];
uint16_t sizes[max_test_keys][thr_test_num_buffs];
int inds[max_test_keys];
uint16_t max_keys;
uint16_t last_key;
int last_ind;
bool stop_threads;
} thread_test_data_t;

static thread_test_data_t *thr_test_data;

static const int race_test_num_threads = 4;
static const int race_test_key = 1;
static const int race_test_data_size = 128;
static const int race_test_min_stack_size = 768;
static const int race_test_max_stack_size = 1024;

static void gen_random(uint8_t *s, int len)
{
Expand Down Expand Up @@ -378,6 +377,41 @@ static void nvstore_basic_functionality_test()
delete[] nvstore_testing_buf_get;
}

// This function calculates the stack size that needs to be allocated per thread in
// the multi-thread tests. Given minimal and maximal stack sizes, and based on the heap
// stats (automatically enabled in CI), the function checks whether each thread has at least
// the minimal stack size, otherwise it reduces the number of threads (something that may happen
// on low memory boards).
static void calc_thread_stack_size(int &num_threads, uint32_t min_size, uint32_t max_size,
uint32_t &stack_size)
{
mbed_stats_heap_t heap_stats;
mbed_stats_heap_get(&heap_stats);

// reserved size (along with all other fields in heap stats) will be zero if
// app is compiled without heap stats (typically local builds)
if (!heap_stats.reserved_size) {
stack_size = max_size;
printf("Heap stats disabled in this build, so test may fail due to insufficient heap size\n");
printf("If this happens, please build the test with heap stats enabled (-DMBED_HEAP_STATS_ENABLED=1)\n");
return;
}

NVStore &nvstore = NVStore::get_instance();
int page_size = nvstore.size() / nvstore.get_max_possible_keys();
// Check if we can allocate enough stack size (per thread) for the current number of threads
while (num_threads) {
stack_size = (heap_stats.reserved_size - heap_stats.current_size) / num_threads - sizeof(rtos::Thread) - page_size;

stack_size = std::min(stack_size, max_size);
if (stack_size >= min_size) {
return;
}

// Got here - stack not sufficient per thread. Reduce number of threads
num_threads--;
}
}

static void thread_test_check_key(uint16_t key)
{
Expand All @@ -386,7 +420,7 @@ static void thread_test_check_key(uint16_t key)
uint16_t actual_len_bytes;
NVStore &nvstore = NVStore::get_instance();

ret = nvstore.get(key, basic_func_max_data_size, get_buff, actual_len_bytes);
ret = nvstore.get(key, thr_test_max_data_size, get_buff, actual_len_bytes);
TEST_ASSERT_EQUAL(NVSTORE_SUCCESS, ret);
TEST_ASSERT_NOT_EQUAL(0, actual_len_bytes);

Expand All @@ -400,13 +434,6 @@ static void thread_test_check_key(uint16_t key)
}
}

if (key == thr_test_data->last_key) {
if ((thr_test_data->sizes[key][thr_test_data->last_ind] == actual_len_bytes) &&
(!memcmp(thr_test_data->buffs[key][thr_test_data->last_ind], get_buff, actual_len_bytes))) {
return;
}
}

// Got here - always assert
TEST_ASSERT(0);

Expand All @@ -420,17 +447,13 @@ static void thread_test_worker()
uint16_t key;
NVStore &nvstore = NVStore::get_instance();

for (;;) {
while (!thr_test_data->stop_threads) {
key = rand() % thr_test_data->max_keys;
is_set = rand() % 10;

if (is_set) {
buf_num = rand() % thr_test_num_buffs;
thr_test_data->last_key = key;
thr_test_data->last_ind = buf_num;
ret = nvstore.set(key, thr_test_data->sizes[key][buf_num], thr_test_data->buffs[key][buf_num]);
TEST_ASSERT_EQUAL(NVSTORE_SUCCESS, ret);
thr_test_data->inds[key] = buf_num;
} else {
thread_test_check_key(key);
}
Expand All @@ -445,6 +468,7 @@ static void nvstore_multi_thread_test()
#ifdef MBED_CONF_RTOS_PRESENT
int i;
int num_threads = thr_test_num_threads;
uint32_t stack_size;
uint16_t size;
uint16_t key;
int ret;
Expand All @@ -458,43 +482,51 @@ static void nvstore_multi_thread_test()

thr_test_data = new thread_test_data_t;
thr_test_data->max_keys = max_test_keys / 2;
thr_test_data->stop_threads = false;
for (key = 0; key < thr_test_data->max_keys; key++) {
for (i = 0; i < thr_test_num_buffs; i++) {
size = 1 + rand() % thr_test_max_data_size;
thr_test_data->sizes[key][i] = size;
thr_test_data->buffs[key][i] = new uint8_t[size + 1];
thr_test_data->inds[key] = 0;
gen_random(thr_test_data->buffs[key][i], size);
}
ret = nvstore.set(key, thr_test_data->sizes[key][0], thr_test_data->buffs[key][0]);
TEST_ASSERT_EQUAL(NVSTORE_SUCCESS, ret);
}

calc_thread_stack_size(num_threads, thr_test_min_stack_size, thr_test_max_stack_size, stack_size);
if (!num_threads) {
printf("Not enough heap space to run test. Test skipped\n");
goto end;
}

for (i = 0; i < num_threads; i++) {
threads[i] = new rtos::Thread((osPriority_t)((int)osPriorityBelowNormal-num_threads+i), thr_test_stack_size);
threads[i] = new rtos::Thread((osPriority_t)((int)osPriorityBelowNormal - num_threads + i), stack_size);
threads[i]->start(callback(thread_test_worker));
}

wait_ms(thr_test_num_secs * 1000);
thr_test_data->stop_threads = true;

wait_ms(1000);

for (i = 0; i < num_threads; i++) {
threads[i]->terminate();
delete threads[i];
}

delete[] threads;

wait_ms(1000);

nvstore.deinit();
ret = nvstore.deinit();
TEST_ASSERT_EQUAL(NVSTORE_SUCCESS, ret);

nvstore.init();
ret = nvstore.init();
TEST_ASSERT_EQUAL(NVSTORE_SUCCESS, ret);

for (key = 0; key < thr_test_data->max_keys; key++) {
thread_test_check_key(key);
TEST_ASSERT_EQUAL(NVSTORE_SUCCESS, ret);
}

end:
for (key = 0; key < thr_test_data->max_keys; key++) {
for (i = 0; i < thr_test_num_buffs; i++) {
delete[] thr_test_data->buffs[key][i];
Expand All @@ -503,9 +535,12 @@ static void nvstore_multi_thread_test()

delete thr_test_data;

nvstore.reset();

#endif
}


static void race_test_worker(void *buf)
{
int ret;
Expand All @@ -519,18 +554,20 @@ static void nvstore_race_test()
{
#ifdef MBED_CONF_RTOS_PRESENT
int i;
uint32_t stack_size;
uint16_t initial_buf_size;
int ret;
rtos::Thread *threads[race_test_num_threads];
uint8_t *get_buff, *buffs[race_test_num_threads];
int num_threads = race_test_num_threads;
uint16_t actual_len_bytes;

NVStore &nvstore = NVStore::get_instance();

ret = nvstore.reset();
TEST_ASSERT_EQUAL(NVSTORE_SUCCESS, ret);

initial_buf_size = std::min((nvstore.size() - race_test_data_size) / 2, (size_t) 256);
initial_buf_size = std::min((nvstore.size() - race_test_data_size) / 2, (size_t) race_test_data_size);
uint8_t *initial_buf = new uint8_t[initial_buf_size];
int num_sets = (nvstore.size() - race_test_data_size) / initial_buf_size;
for (i = 0; i < num_sets; i++) {
Expand All @@ -539,33 +576,42 @@ static void nvstore_race_test()
}
delete[] initial_buf;

for (i = 0; i < race_test_num_threads; i++) {
get_buff = new uint8_t[race_test_data_size];

calc_thread_stack_size(num_threads, race_test_min_stack_size, race_test_max_stack_size, stack_size);
if (!num_threads) {
printf("Not enough heap space to run test. Test skipped\n");
goto end;
}

for (i = 0; i < num_threads; i++) {
buffs[i] = new uint8_t[race_test_data_size];
gen_random(buffs[i], race_test_data_size);
}

for (i = 0; i < race_test_num_threads; i++) {
threads[i] = new rtos::Thread((osPriority_t)((int)osPriorityBelowNormal - race_test_num_threads + i), thr_test_stack_size);
for (i = 0; i < num_threads; i++) {
threads[i] = new rtos::Thread((osPriority_t)((int)osPriorityBelowNormal - num_threads + i), stack_size);
threads[i]->start(callback(race_test_worker, (void *) buffs[i]));
threads[i]->join();
}

get_buff = new uint8_t[race_test_data_size];
ret = nvstore.get(race_test_key, race_test_data_size, get_buff, actual_len_bytes);
TEST_ASSERT_EQUAL(NVSTORE_SUCCESS, ret);
TEST_ASSERT_EQUAL(race_test_data_size, actual_len_bytes);

for (i = 0; i < race_test_num_threads; i++) {
for (i = 0; i < num_threads; i++) {
if (!memcmp(buffs[i], get_buff, actual_len_bytes)) {
break;
}
}
TEST_ASSERT_NOT_EQUAL(race_test_num_threads, i);
TEST_ASSERT_NOT_EQUAL(num_threads, i);

for (i = 0; i < race_test_num_threads; i++) {
for (i = 0; i < num_threads; i++) {
delete threads[i];
delete[] buffs[i];
}

end:
delete[] get_buff;
#endif
}
Expand Down