NVStore tests: Tune memory consumption; stop threads greafully

- Tune thread stack size in nvstore test using heap stats
- Stop threads gracefully instead of killing them (in multi-thread test)
pull/7127/head
David Saada 2018-06-05 18:07:52 +03:00
parent e8005f6d72
commit 1b5b8390ea
1 changed files with 83 additions and 37 deletions

View File

@ -26,6 +26,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <algorithm> #include <algorithm>
#include "mbed_stats.h"
#if !NVSTORE_ENABLED #if !NVSTORE_ENABLED
#error [NOT_SUPPORTED] NVSTORE needs to be enabled for this test #error [NOT_SUPPORTED] NVSTORE needs to be enabled for this test
@ -42,19 +43,15 @@ static const int thr_test_num_secs = 5;
static const int thr_test_max_data_size = 32; static const int thr_test_max_data_size = 32;
static const int thr_test_num_threads = 3; static const int thr_test_num_threads = 3;
#ifdef TARGET_NRF52 static const int thr_test_min_stack_size = 768;
static const int thr_test_stack_size = 1024; static const int thr_test_max_stack_size = 1024;
#else
static const int thr_test_stack_size = 768;
#endif
typedef struct { typedef struct {
uint8_t *buffs[max_test_keys][thr_test_num_buffs]; uint8_t *buffs[max_test_keys][thr_test_num_buffs];
uint16_t sizes[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 max_keys;
uint16_t last_key; bool stop_threads;
int last_ind;
} thread_test_data_t; } thread_test_data_t;
static thread_test_data_t *thr_test_data; static thread_test_data_t *thr_test_data;
@ -62,6 +59,8 @@ static thread_test_data_t *thr_test_data;
static const int race_test_num_threads = 4; static const int race_test_num_threads = 4;
static const int race_test_key = 1; static const int race_test_key = 1;
static const int race_test_data_size = 128; 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) static void gen_random(uint8_t *s, int len)
{ {
@ -378,6 +377,41 @@ static void nvstore_basic_functionality_test()
delete[] nvstore_testing_buf_get; 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) static void thread_test_check_key(uint16_t key)
{ {
@ -386,7 +420,7 @@ static void thread_test_check_key(uint16_t key)
uint16_t actual_len_bytes; uint16_t actual_len_bytes;
NVStore &nvstore = NVStore::get_instance(); 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_EQUAL(NVSTORE_SUCCESS, ret);
TEST_ASSERT_NOT_EQUAL(0, actual_len_bytes); TEST_ASSERT_NOT_EQUAL(0, actual_len_bytes);
@ -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 // Got here - always assert
TEST_ASSERT(0); TEST_ASSERT(0);
@ -420,17 +447,13 @@ static void thread_test_worker()
uint16_t key; uint16_t key;
NVStore &nvstore = NVStore::get_instance(); NVStore &nvstore = NVStore::get_instance();
for (;;) { while (!thr_test_data->stop_threads) {
key = rand() % thr_test_data->max_keys; key = rand() % thr_test_data->max_keys;
is_set = rand() % 10; is_set = rand() % 10;
if (is_set) { if (is_set) {
buf_num = rand() % thr_test_num_buffs; 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]); ret = nvstore.set(key, thr_test_data->sizes[key][buf_num], thr_test_data->buffs[key][buf_num]);
TEST_ASSERT_EQUAL(NVSTORE_SUCCESS, ret); TEST_ASSERT_EQUAL(NVSTORE_SUCCESS, ret);
thr_test_data->inds[key] = buf_num;
} else { } else {
thread_test_check_key(key); thread_test_check_key(key);
} }
@ -445,6 +468,7 @@ static void nvstore_multi_thread_test()
#ifdef MBED_CONF_RTOS_PRESENT #ifdef MBED_CONF_RTOS_PRESENT
int i; int i;
int num_threads = thr_test_num_threads; int num_threads = thr_test_num_threads;
uint32_t stack_size;
uint16_t size; uint16_t size;
uint16_t key; uint16_t key;
int ret; int ret;
@ -458,43 +482,51 @@ static void nvstore_multi_thread_test()
thr_test_data = new thread_test_data_t; thr_test_data = new thread_test_data_t;
thr_test_data->max_keys = max_test_keys / 2; 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 (key = 0; key < thr_test_data->max_keys; key++) {
for (i = 0; i < thr_test_num_buffs; i++) { for (i = 0; i < thr_test_num_buffs; i++) {
size = 1 + rand() % thr_test_max_data_size; size = 1 + rand() % thr_test_max_data_size;
thr_test_data->sizes[key][i] = size; thr_test_data->sizes[key][i] = size;
thr_test_data->buffs[key][i] = new uint8_t[size + 1]; 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); 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]); ret = nvstore.set(key, thr_test_data->sizes[key][0], thr_test_data->buffs[key][0]);
TEST_ASSERT_EQUAL(NVSTORE_SUCCESS, ret); 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++) { 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)); threads[i]->start(callback(thread_test_worker));
} }
wait_ms(thr_test_num_secs * 1000); wait_ms(thr_test_num_secs * 1000);
thr_test_data->stop_threads = true;
wait_ms(1000);
for (i = 0; i < num_threads; i++) { for (i = 0; i < num_threads; i++) {
threads[i]->terminate();
delete threads[i]; delete threads[i];
} }
delete[] threads; delete[] threads;
wait_ms(1000); ret = nvstore.deinit();
TEST_ASSERT_EQUAL(NVSTORE_SUCCESS, ret);
nvstore.deinit(); ret = nvstore.init();
TEST_ASSERT_EQUAL(NVSTORE_SUCCESS, ret);
nvstore.init();
for (key = 0; key < thr_test_data->max_keys; key++) { for (key = 0; key < thr_test_data->max_keys; key++) {
thread_test_check_key(key); thread_test_check_key(key);
TEST_ASSERT_EQUAL(NVSTORE_SUCCESS, ret);
} }
end:
for (key = 0; key < thr_test_data->max_keys; key++) { for (key = 0; key < thr_test_data->max_keys; key++) {
for (i = 0; i < thr_test_num_buffs; i++) { for (i = 0; i < thr_test_num_buffs; i++) {
delete[] thr_test_data->buffs[key][i]; delete[] thr_test_data->buffs[key][i];
@ -503,9 +535,12 @@ static void nvstore_multi_thread_test()
delete thr_test_data; delete thr_test_data;
nvstore.reset();
#endif #endif
} }
static void race_test_worker(void *buf) static void race_test_worker(void *buf)
{ {
int ret; int ret;
@ -519,10 +554,12 @@ static void nvstore_race_test()
{ {
#ifdef MBED_CONF_RTOS_PRESENT #ifdef MBED_CONF_RTOS_PRESENT
int i; int i;
uint32_t stack_size;
uint16_t initial_buf_size; uint16_t initial_buf_size;
int ret; int ret;
rtos::Thread *threads[race_test_num_threads]; rtos::Thread *threads[race_test_num_threads];
uint8_t *get_buff, *buffs[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; uint16_t actual_len_bytes;
NVStore &nvstore = NVStore::get_instance(); NVStore &nvstore = NVStore::get_instance();
@ -530,7 +567,7 @@ static void nvstore_race_test()
ret = nvstore.reset(); ret = nvstore.reset();
TEST_ASSERT_EQUAL(NVSTORE_SUCCESS, ret); 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]; uint8_t *initial_buf = new uint8_t[initial_buf_size];
int num_sets = (nvstore.size() - race_test_data_size) / initial_buf_size; int num_sets = (nvstore.size() - race_test_data_size) / initial_buf_size;
for (i = 0; i < num_sets; i++) { for (i = 0; i < num_sets; i++) {
@ -539,33 +576,42 @@ static void nvstore_race_test()
} }
delete[] initial_buf; 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]; buffs[i] = new uint8_t[race_test_data_size];
gen_random(buffs[i], race_test_data_size); gen_random(buffs[i], race_test_data_size);
} }
for (i = 0; i < race_test_num_threads; i++) { for (i = 0; i < num_threads; i++) {
threads[i] = new rtos::Thread((osPriority_t)((int)osPriorityBelowNormal - race_test_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(race_test_worker, (void *) buffs[i])); threads[i]->start(callback(race_test_worker, (void *) buffs[i]));
threads[i]->join(); 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); ret = nvstore.get(race_test_key, race_test_data_size, get_buff, actual_len_bytes);
TEST_ASSERT_EQUAL(NVSTORE_SUCCESS, ret); TEST_ASSERT_EQUAL(NVSTORE_SUCCESS, ret);
TEST_ASSERT_EQUAL(race_test_data_size, actual_len_bytes); 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)) { if (!memcmp(buffs[i], get_buff, actual_len_bytes)) {
break; 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 threads[i];
delete[] buffs[i]; delete[] buffs[i];
} }
end:
delete[] get_buff; delete[] get_buff;
#endif #endif
} }