Skip to content

Commit e7cd6ae

Browse files
authored
Merge pull request #7746 from davidsaada/david_nvstore_test_low_mem
Handle NVStore tests in a low memory environment
2 parents 47d1acf + 999042d commit e7cd6ae

File tree

1 file changed

+117
-91
lines changed
  • features/storage/nvstore/TESTS/nvstore/functionality

1 file changed

+117
-91
lines changed

features/storage/nvstore/TESTS/nvstore/functionality/main.cpp

Lines changed: 117 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
#include <stdlib.h>
2727
#include <stdio.h>
2828
#include <algorithm>
29-
#include "mbed_stats.h"
3029

3130
#if !NVSTORE_ENABLED
3231
#error [NOT_SUPPORTED] NVSTORE needs to be enabled for this test
@@ -35,6 +34,7 @@
3534
using namespace utest::v1;
3635

3736
static const uint16_t max_test_keys = 20;
37+
static const uint16_t max_possible_keys_threshold = 64;
3838

3939
static const size_t basic_func_max_data_size = 128;
4040

@@ -44,11 +44,9 @@ static const int thr_test_max_data_size = 32;
4444
static const int thr_test_num_threads = 3;
4545

4646
#if defined(__CORTEX_M23) || defined(__CORTEX_M33)
47-
static const int thr_test_min_stack_size = 1280;
48-
static const int thr_test_max_stack_size = 1280;
47+
static const int thr_test_stack_size = 1280;
4948
#else
50-
static const int thr_test_min_stack_size = 768;
51-
static const int thr_test_max_stack_size = 1024;
49+
static const int thr_test_stack_size = 1024;
5250
#endif
5351

5452
typedef struct {
@@ -63,8 +61,7 @@ static thread_test_data_t *thr_test_data;
6361
static const int race_test_num_threads = 4;
6462
static const int race_test_key = 1;
6563
static const int race_test_data_size = 128;
66-
static const int race_test_min_stack_size = 768;
67-
static const int race_test_max_stack_size = 1024;
64+
static const int race_test_stack_size = 768;
6865

6966
static bool nvstore_overlaps_code = false;
7067

@@ -81,9 +78,7 @@ static void nvstore_basic_functionality_test()
8178
uint16_t actual_len_bytes = 0;
8279
NVStore &nvstore = NVStore::get_instance();
8380
uint16_t key;
84-
85-
uint8_t *nvstore_testing_buf_set = new uint8_t[basic_func_max_data_size];
86-
uint8_t *nvstore_testing_buf_get = new uint8_t[basic_func_max_data_size];
81+
uint16_t max_possible_keys;
8782

8883
int result;
8984

@@ -99,11 +94,20 @@ static void nvstore_basic_functionality_test()
9994
TEST_SKIP_UNLESS_MESSAGE(!nvstore_overlaps_code, "Test skipped. NVStore region overlaps code.");
10095
}
10196

97+
uint8_t *nvstore_testing_buf_set = new (std::nothrow) uint8_t[basic_func_max_data_size];
98+
uint8_t *nvstore_testing_buf_get = new (std::nothrow) uint8_t[basic_func_max_data_size];
99+
if (!nvstore_testing_buf_set || !nvstore_testing_buf_get) {
100+
printf("Not enough heap space to run test. Test skipped\n");
101+
goto clean;
102+
}
103+
102104
gen_random(nvstore_testing_buf_set, basic_func_max_data_size);
103105

104-
uint16_t max_possible_keys = nvstore.get_max_possible_keys();
106+
max_possible_keys = nvstore.get_max_possible_keys();
105107
TEST_SKIP_UNLESS_MESSAGE(max_test_keys < max_possible_keys,
106108
"Not enough possible keys for test. Test skipped.");
109+
TEST_SKIP_UNLESS_MESSAGE(max_possible_keys >= max_possible_keys_threshold,
110+
"Max possible keys below threshold. Test skipped.");
107111

108112
nvstore.set_max_keys(max_test_keys);
109113
TEST_ASSERT_EQUAL(max_test_keys, nvstore.get_max_keys());
@@ -387,46 +391,11 @@ static void nvstore_basic_functionality_test()
387391
result = nvstore.get(NVSTORE_NUM_PREDEFINED_KEYS + 1, 64, nvstore_testing_buf_get, actual_len_bytes);
388392
TEST_ASSERT_EQUAL(NVSTORE_NOT_FOUND, result);
389393

394+
clean:
390395
delete[] nvstore_testing_buf_set;
391396
delete[] nvstore_testing_buf_get;
392397
}
393398

394-
// This function calculates the stack size that needs to be allocated per thread in
395-
// the multi-thread tests. Given minimal and maximal stack sizes, and based on the heap
396-
// stats (automatically enabled in CI), the function checks whether each thread has at least
397-
// the minimal stack size, otherwise it reduces the number of threads (something that may happen
398-
// on low memory boards).
399-
static void calc_thread_stack_size(int &num_threads, uint32_t min_size, uint32_t max_size,
400-
uint32_t &stack_size)
401-
{
402-
mbed_stats_heap_t heap_stats;
403-
mbed_stats_heap_get(&heap_stats);
404-
405-
// reserved size (along with all other fields in heap stats) will be zero if
406-
// app is compiled without heap stats (typically local builds)
407-
if (!heap_stats.reserved_size) {
408-
stack_size = max_size;
409-
printf("Heap stats disabled in this build, so test may fail due to insufficient heap size\n");
410-
printf("If this happens, please build the test with heap stats enabled (-DMBED_HEAP_STATS_ENABLED=1)\n");
411-
return;
412-
}
413-
414-
NVStore &nvstore = NVStore::get_instance();
415-
int page_size = nvstore.size() / nvstore.get_max_possible_keys();
416-
// Check if we can allocate enough stack size (per thread) for the current number of threads
417-
while (num_threads) {
418-
stack_size = (heap_stats.reserved_size - heap_stats.current_size) / num_threads - sizeof(rtos::Thread) - page_size;
419-
420-
stack_size = std::min(stack_size, max_size);
421-
if (stack_size >= min_size) {
422-
return;
423-
}
424-
425-
// Got here - stack not sufficient per thread. Reduce number of threads
426-
num_threads--;
427-
}
428-
}
429-
430399
static void thread_test_check_key(uint16_t key)
431400
{
432401
uint8_t get_buff[thr_test_max_data_size];
@@ -481,47 +450,66 @@ static void nvstore_multi_thread_test()
481450
{
482451
#ifdef MBED_CONF_RTOS_PRESENT
483452
int i;
484-
int num_threads = thr_test_num_threads;
485-
uint32_t stack_size;
486453
uint16_t size;
487454
uint16_t key;
488455
int ret;
489-
490-
rtos::Thread **threads = new rtos::Thread*[num_threads];
456+
char *dummy;
457+
uint16_t max_possible_keys;
491458

492459
NVStore &nvstore = NVStore::get_instance();
493460

494461
TEST_SKIP_UNLESS_MESSAGE(!nvstore_overlaps_code, "Test skipped. NVStore region overlaps code.");
495462

463+
thr_test_data = 0;
464+
rtos::Thread **threads = new (std::nothrow) rtos::Thread*[thr_test_num_threads];
465+
if (!threads) {
466+
goto mem_fail;
467+
}
468+
memset(threads, 0, thr_test_num_threads * sizeof(rtos::Thread*));
469+
496470
ret = nvstore.reset();
497471
TEST_ASSERT_EQUAL(NVSTORE_SUCCESS, ret);
498472

499-
thr_test_data = new thread_test_data_t;
473+
thr_test_data = new (std::nothrow) thread_test_data_t;
474+
if (!thr_test_data) {
475+
goto mem_fail;
476+
}
477+
478+
memset(thr_test_data, 0, sizeof(thread_test_data_t));
500479
thr_test_data->max_keys = max_test_keys / 2;
501-
uint16_t max_possible_keys = nvstore.get_max_possible_keys();
480+
max_possible_keys = nvstore.get_max_possible_keys();
502481
TEST_SKIP_UNLESS_MESSAGE(thr_test_data->max_keys < max_possible_keys,
503482
"Not enough possible keys for test. Test skipped.");
483+
TEST_SKIP_UNLESS_MESSAGE(max_possible_keys >= max_possible_keys_threshold,
484+
"Max possible keys below threshold. Test skipped.");
504485

505486
thr_test_data->stop_threads = false;
506487
for (key = 0; key < thr_test_data->max_keys; key++) {
507488
for (i = 0; i < thr_test_num_buffs; i++) {
508489
size = 1 + rand() % thr_test_max_data_size;
509490
thr_test_data->sizes[key][i] = size;
510-
thr_test_data->buffs[key][i] = new uint8_t[size + 1];
491+
thr_test_data->buffs[key][i] = new (std::nothrow) uint8_t[size + 1];
492+
if (!thr_test_data->buffs[key][i]) {
493+
goto mem_fail;
494+
}
511495
gen_random(thr_test_data->buffs[key][i], size);
512496
}
513497
ret = nvstore.set(key, thr_test_data->sizes[key][0], thr_test_data->buffs[key][0]);
514498
TEST_ASSERT_EQUAL(NVSTORE_SUCCESS, ret);
515499
}
516500

517-
calc_thread_stack_size(num_threads, thr_test_min_stack_size, thr_test_max_stack_size, stack_size);
518-
if (!num_threads) {
519-
printf("Not enough heap space to run test. Test skipped\n");
520-
goto end;
501+
dummy = new (std::nothrow) char[thr_test_num_threads * thr_test_stack_size];
502+
delete[] dummy;
503+
if (!dummy) {
504+
goto mem_fail;
521505
}
522506

523-
for (i = 0; i < num_threads; i++) {
524-
threads[i] = new rtos::Thread((osPriority_t)((int)osPriorityBelowNormal - num_threads + i), stack_size);
507+
for (i = 0; i < thr_test_num_threads; i++) {
508+
threads[i] = new (std::nothrow) rtos::Thread((osPriority_t)((int)osPriorityBelowNormal - thr_test_num_threads + i),
509+
thr_test_stack_size);
510+
if (!threads[i]) {
511+
goto mem_fail;
512+
}
525513
threads[i]->start(callback(thread_test_worker));
526514
}
527515

@@ -530,12 +518,6 @@ static void nvstore_multi_thread_test()
530518

531519
wait_ms(1000);
532520

533-
for (i = 0; i < num_threads; i++) {
534-
delete threads[i];
535-
}
536-
537-
delete[] threads;
538-
539521
ret = nvstore.deinit();
540522
TEST_ASSERT_EQUAL(NVSTORE_SUCCESS, ret);
541523

@@ -545,15 +527,32 @@ static void nvstore_multi_thread_test()
545527
for (key = 0; key < thr_test_data->max_keys; key++) {
546528
thread_test_check_key(key);
547529
}
530+
goto clean;
548531

549-
end:
550-
for (key = 0; key < thr_test_data->max_keys; key++) {
551-
for (i = 0; i < thr_test_num_buffs; i++) {
552-
delete[] thr_test_data->buffs[key][i];
532+
mem_fail:
533+
printf("Not enough heap space to run test. Test skipped\n");
534+
535+
clean:
536+
if (thr_test_data) {
537+
thr_test_data->stop_threads = true;
538+
wait_ms(1000);
539+
540+
for (key = 0; key < thr_test_data->max_keys; key++) {
541+
for (i = 0; i < thr_test_num_buffs; i++) {
542+
delete[] thr_test_data->buffs[key][i];
543+
}
553544
}
545+
546+
delete thr_test_data;
554547
}
555548

556-
delete thr_test_data;
549+
if (threads) {
550+
for (i = 0; i < thr_test_num_threads; i++) {
551+
delete threads[i];
552+
}
553+
554+
delete[] threads;
555+
}
557556

558557
nvstore.reset();
559558

@@ -574,45 +573,68 @@ static void nvstore_race_test()
574573
{
575574
#ifdef MBED_CONF_RTOS_PRESENT
576575
int i;
577-
uint32_t stack_size;
578576
uint16_t initial_buf_size;
579577
int ret;
578+
int num_sets;
580579
rtos::Thread *threads[race_test_num_threads];
581-
uint8_t *get_buff, *buffs[race_test_num_threads];
582-
int num_threads = race_test_num_threads;
580+
uint8_t *get_buff = 0, *buffs[race_test_num_threads];
583581
uint16_t actual_len_bytes;
582+
uint16_t max_possible_keys;
583+
char *dummy;
584584

585585
TEST_SKIP_UNLESS_MESSAGE(!nvstore_overlaps_code, "Test skipped. NVStore region overlaps code.");
586586

587587
NVStore &nvstore = NVStore::get_instance();
588588

589+
max_possible_keys = nvstore.get_max_possible_keys();
590+
TEST_SKIP_UNLESS_MESSAGE(max_possible_keys >= max_possible_keys_threshold,
591+
"Max possible keys below threshold. Test skipped.");
592+
589593
ret = nvstore.reset();
590594
TEST_ASSERT_EQUAL(NVSTORE_SUCCESS, ret);
591595

596+
memset(buffs, 0, sizeof(buffs));
597+
memset(threads, 0, sizeof(threads));
598+
592599
initial_buf_size = std::min((nvstore.size() - race_test_data_size) / 2, (size_t) race_test_data_size);
593-
uint8_t *initial_buf = new uint8_t[initial_buf_size];
594-
int num_sets = (nvstore.size() - race_test_data_size) / initial_buf_size;
600+
uint8_t *initial_buf = new (std::nothrow) uint8_t[initial_buf_size];
601+
if (!initial_buf) {
602+
goto mem_fail;
603+
}
604+
605+
num_sets = (nvstore.size() - race_test_data_size) / initial_buf_size;
595606
for (i = 0; i < num_sets; i++) {
596607
ret = nvstore.set(0, initial_buf_size, initial_buf);
597608
TEST_ASSERT_EQUAL(NVSTORE_SUCCESS, ret);
598609
}
599610
delete[] initial_buf;
600611

601-
get_buff = new uint8_t[race_test_data_size];
602-
603-
calc_thread_stack_size(num_threads, race_test_min_stack_size, race_test_max_stack_size, stack_size);
604-
if (!num_threads) {
605-
printf("Not enough heap space to run test. Test skipped\n");
606-
goto end;
612+
get_buff = new (std::nothrow) uint8_t[race_test_data_size];
613+
if (!get_buff) {
614+
goto mem_fail;
607615
}
608616

609-
for (i = 0; i < num_threads; i++) {
610-
buffs[i] = new uint8_t[race_test_data_size];
617+
for (i = 0; i < race_test_num_threads; i++) {
618+
buffs[i] = new (std::nothrow) uint8_t[race_test_data_size];
619+
if (!buffs[i]) {
620+
goto mem_fail;
621+
}
611622
gen_random(buffs[i], race_test_data_size);
612623
}
613624

614-
for (i = 0; i < num_threads; i++) {
615-
threads[i] = new rtos::Thread((osPriority_t)((int)osPriorityBelowNormal - num_threads + i), stack_size);
625+
for (i = 0; i < race_test_num_threads; i++) {
626+
threads[i] = new (std::nothrow) rtos::Thread((osPriority_t)((int)osPriorityBelowNormal - race_test_num_threads + i),
627+
race_test_stack_size);
628+
if (!threads[i]) {
629+
goto mem_fail;
630+
}
631+
632+
dummy = new (std::nothrow) char[race_test_stack_size];
633+
if (!dummy) {
634+
goto mem_fail;
635+
}
636+
delete[] dummy;
637+
616638
threads[i]->start(callback(race_test_worker, (void *) buffs[i]));
617639
threads[i]->join();
618640
}
@@ -621,19 +643,23 @@ static void nvstore_race_test()
621643
TEST_ASSERT_EQUAL(NVSTORE_SUCCESS, ret);
622644
TEST_ASSERT_EQUAL(race_test_data_size, actual_len_bytes);
623645

624-
for (i = 0; i < num_threads; i++) {
646+
for (i = 0; i < race_test_num_threads; i++) {
625647
if (!memcmp(buffs[i], get_buff, actual_len_bytes)) {
626648
break;
627649
}
628650
}
629-
TEST_ASSERT_NOT_EQUAL(num_threads, i);
651+
TEST_ASSERT_NOT_EQUAL(race_test_num_threads, i);
630652

631-
for (i = 0; i < num_threads; i++) {
653+
goto clean;
654+
655+
mem_fail:
656+
printf("Not enough heap space to run test. Test skipped\n");
657+
658+
clean:
659+
for (i = 0; i < race_test_num_threads; i++) {
632660
delete threads[i];
633661
delete[] buffs[i];
634662
}
635-
636-
end:
637663
delete[] get_buff;
638664
#endif
639665
}

0 commit comments

Comments
 (0)