init
Embed:
(wiki syntax)
Show/hide line numbers
main.cpp
00001 /* 00002 * Copyright (c) 2013-2017, ARM Limited, All Rights Reserved 00003 * SPDX-License-Identifier: Apache-2.0 00004 * 00005 * Licensed under the Apache License, Version 2.0 (the "License"); you may 00006 * not use this file except in compliance with the License. 00007 * You may obtain a copy of the License at 00008 * 00009 * http://www.apache.org/licenses/LICENSE-2.0 00010 * 00011 * Unless required by applicable law or agreed to in writing, software 00012 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 00013 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00014 * See the License for the specific language governing permissions and 00015 * limitations under the License. 00016 */ 00017 00018 #include "mbed.h" 00019 #include "greentea-client/test_env.h" 00020 #include "unity/unity.h" 00021 #include "utest/utest.h" 00022 #include "mbed_mem_trace.h" 00023 #include <stdlib.h> 00024 #include <stdio.h> 00025 #include <stdarg.h> 00026 00027 #ifndef MBED_MEM_TRACING_ENABLED 00028 #error [NOT_SUPPORTED] test not supported 00029 #endif 00030 00031 using utest::v1::Case; 00032 00033 00034 /******************************************************************************/ 00035 /* Helper functions and data structures */ 00036 /******************************************************************************/ 00037 00038 #define THREAD_STACK_SIZE 384 00039 #define NUM_TEST_THREADS 3 00040 00041 template<osPriority PRIORITY, uint32_t STACK_SIZE> 00042 class TestThread : public Thread { 00043 uint8_t stack[STACK_SIZE]; 00044 public: 00045 TestThread() : Thread(PRIORITY, STACK_SIZE, stack) { } 00046 }; 00047 00048 // This structure keeps data about the various memory allocation operations, 00049 // as traced by 'test_trace_cb' below. 00050 #define TEST_MAX_MEMORY_OPS 10 00051 00052 // Trace results for all possible operations 00053 typedef struct { 00054 uint8_t op; 00055 void *res; 00056 union { 00057 struct { 00058 size_t arg_size; 00059 } malloc_info; 00060 struct { 00061 void *arg_ptr; 00062 size_t arg_size; 00063 } realloc_info; 00064 struct { 00065 size_t arg_nmemb; 00066 size_t arg_size; 00067 } calloc_info; 00068 struct { 00069 void *arg_ptr; 00070 } free_info; 00071 }; 00072 } mem_trace_data_t; 00073 00074 // Memory operation statistics 00075 typedef struct { 00076 mem_trace_data_t op_data[TEST_MAX_MEMORY_OPS]; 00077 uint32_t total_ops; 00078 bool invalid_op, overflow; 00079 } stats_t; 00080 00081 static stats_t stats; 00082 00083 00084 // Clear all the memory statistics 00085 static void test_clear_stats() 00086 { 00087 memset(&stats, 0, sizeof(stats)); 00088 } 00089 00090 // Memory tracer callback that records each operation in "stats" (above) 00091 extern "C" void test_trace_cb(uint8_t op, void *res, void *caller, ...) 00092 { 00093 va_list va; 00094 mem_trace_data_t *pmem = stats.op_data + stats.total_ops; 00095 if (stats.total_ops >= TEST_MAX_MEMORY_OPS) { 00096 stats.overflow = true; 00097 return; 00098 } 00099 va_start(va, caller); 00100 pmem->op = op; 00101 pmem->res = res; 00102 switch(op) { 00103 case MBED_MEM_TRACE_MALLOC: 00104 pmem->malloc_info.arg_size = va_arg(va, size_t); 00105 break; 00106 00107 case MBED_MEM_TRACE_REALLOC: 00108 pmem->realloc_info.arg_ptr = va_arg(va, void *); 00109 pmem->realloc_info.arg_size = va_arg(va, size_t); 00110 break; 00111 00112 case MBED_MEM_TRACE_CALLOC: 00113 pmem->calloc_info.arg_nmemb = va_arg(va, size_t); 00114 pmem->calloc_info.arg_size = va_arg(va, size_t); 00115 break; 00116 00117 case MBED_MEM_TRACE_FREE: 00118 pmem->free_info.arg_ptr = va_arg(va, void *); 00119 break; 00120 00121 default: 00122 stats.invalid_op = true; 00123 } 00124 stats.total_ops ++; 00125 va_end(va); 00126 } 00127 00128 // Generic sanity checks for the tracer 00129 static void check_sanity(uint32_t expected_ops) 00130 { 00131 TEST_ASSERT_FALSE(stats.overflow); 00132 TEST_ASSERT_FALSE(stats.invalid_op); 00133 TEST_ASSERT_EQUAL_UINT32(expected_ops, stats.total_ops); 00134 } 00135 00136 // Check a "malloc" operation 00137 static void check_malloc_op(const mem_trace_data_t *p, void *expected_res, size_t expected_arg_size) 00138 { 00139 TEST_ASSERT_EQUAL_UINT8(MBED_MEM_TRACE_MALLOC, p->op); 00140 TEST_ASSERT_EQUAL_PTR(expected_res, p->res); 00141 TEST_ASSERT_EQUAL_UINT32(expected_arg_size, p->malloc_info.arg_size); 00142 } 00143 00144 // Check a "free" operation 00145 static void check_free_op(const mem_trace_data_t *p, void *expected_arg_ptr) 00146 { 00147 TEST_ASSERT_EQUAL_UINT8(MBED_MEM_TRACE_FREE, p->op); 00148 TEST_ASSERT_EQUAL_PTR(expected_arg_ptr, p->free_info.arg_ptr); 00149 } 00150 00151 // Check a "realloc" operation 00152 static void check_realloc_op(const mem_trace_data_t *p, void *expected_res, void *expected_arg_ptr, size_t expected_arg_size) 00153 { 00154 TEST_ASSERT_EQUAL_UINT8(MBED_MEM_TRACE_REALLOC, p->op); 00155 TEST_ASSERT_EQUAL_PTR(expected_res, p->res); 00156 TEST_ASSERT_EQUAL_UINT32(expected_arg_ptr, p->realloc_info.arg_ptr); 00157 TEST_ASSERT_EQUAL_UINT32(expected_arg_size, p->realloc_info.arg_size); 00158 } 00159 00160 // Check a "calloc" operation 00161 static void check_calloc_op(const mem_trace_data_t *p, void *expected_res, size_t expected_arg_nmemb, size_t expected_arg_size) 00162 { 00163 TEST_ASSERT_EQUAL_UINT8(MBED_MEM_TRACE_CALLOC, p->op); 00164 TEST_ASSERT_EQUAL_PTR(expected_res, p->res); 00165 TEST_ASSERT_EQUAL_UINT32(expected_arg_nmemb, p->calloc_info.arg_nmemb); 00166 TEST_ASSERT_EQUAL_UINT32(expected_arg_size, p->calloc_info.arg_size); 00167 } 00168 00169 // Memory tracer callback to test thread safety 00170 extern "C" void test_trace_cb_multithread(uint8_t op, void *res, void *caller, ...) 00171 { 00172 volatile static int trace_guard = 0; 00173 trace_guard++; 00174 TEST_ASSERT_TRUE_MESSAGE(trace_guard == 1, "Race condition occurred !!!!"); 00175 trace_guard--; 00176 } 00177 00178 // Thread function 00179 void malloc_free(volatile bool *thread_continue) 00180 { 00181 const size_t block_size = 126; 00182 00183 while(*thread_continue) { 00184 void *p = malloc(block_size); 00185 TEST_ASSERT_NOT_EQUAL(p, NULL); 00186 free(p); 00187 } 00188 } 00189 00190 00191 /** Test single malloc/free tracing 00192 * 00193 * Given a memory trace mechanism 00194 * When perform single memory allocation/deallocation using malloc/free 00195 * Then tracing matches the operations 00196 * 00197 */ 00198 static void test_case_single_malloc_free() 00199 { 00200 const uint32_t num_op = 2; 00201 const size_t block_size = 126; 00202 const mem_trace_data_t *pmem = stats.op_data; 00203 00204 test_clear_stats(); 00205 mbed_mem_trace_set_callback(test_trace_cb); 00206 00207 // Allocate a single memory block 00208 void *p = malloc(block_size); 00209 TEST_ASSERT_NOT_EQUAL(p, NULL); 00210 00211 // Free the memory block 00212 free(p); 00213 00214 // Stop tracing 00215 mbed_mem_trace_set_callback(NULL); 00216 00217 // Check tracer result 00218 check_sanity(num_op); 00219 check_malloc_op(pmem++, p, block_size); 00220 check_free_op(pmem, p); 00221 } 00222 00223 00224 /** Test all memory operations (malloc, realloc, free, calloc) tracing 00225 * 00226 * Given a memory trace mechanism 00227 * When perform all memory operations 00228 * Then tracing matches the operations 00229 * 00230 */ 00231 static void test_case_all_memory_ops() 00232 { 00233 const uint32_t num_op = 5; 00234 const size_t malloc_size = 40, realloc_size = 80, nmemb = 25, size = 10; 00235 const mem_trace_data_t *pmem = stats.op_data; 00236 00237 test_clear_stats(); 00238 mbed_mem_trace_set_callback(test_trace_cb); 00239 00240 // Allocate a single memory block, the realloc it 00241 void *p_malloc = malloc(malloc_size); 00242 TEST_ASSERT_NOT_EQUAL(p_malloc, NULL); 00243 void *p_realloc = realloc(p_malloc, realloc_size); 00244 TEST_ASSERT_NOT_EQUAL(p_realloc, NULL); 00245 00246 // Use calloc() now 00247 void *p_calloc = calloc(nmemb, size); 00248 TEST_ASSERT_NOT_EQUAL(p_calloc, NULL); 00249 00250 // Free the realloc() pointer first, then the calloc() one 00251 free(p_realloc); 00252 free(p_calloc); 00253 00254 // Stop tracing 00255 mbed_mem_trace_set_callback(NULL); 00256 00257 // Check tracer result 00258 check_sanity(num_op); 00259 check_malloc_op(pmem++, p_malloc, malloc_size); 00260 check_realloc_op(pmem++, p_realloc, p_malloc, realloc_size); 00261 check_calloc_op(pmem++, p_calloc, nmemb, size); 00262 check_free_op(pmem++, p_realloc); 00263 check_free_op(pmem, p_calloc); 00264 } 00265 00266 00267 /** Test that tracing is off when using a NULL callback 00268 * 00269 * Given a memory trace mechanism 00270 * When tracing is turned off 00271 * Then performed memory operations doesn't report any tracing 00272 * 00273 */ 00274 static void test_case_trace_off() 00275 { 00276 const uint32_t num_op = 0; 00277 const size_t malloc_size = 10; 00278 00279 test_clear_stats(); 00280 // We don't want any tracing 00281 mbed_mem_trace_set_callback(NULL); 00282 00283 // Allocate a buffer and free it 00284 void *p_malloc = malloc(malloc_size); 00285 TEST_ASSERT_NOT_EQUAL(p_malloc, NULL); 00286 free(p_malloc); 00287 00288 // Check that we didn't trace anything 00289 check_sanity(num_op); 00290 } 00291 00292 00293 /** Test partial tracing 00294 * 00295 * Given a memory trace mechanism 00296 * When perform memory operations while tracing is on then off and on again 00297 * Then tracing report only part of operations 00298 * 00299 */ 00300 static void test_case_partial_trace() 00301 { 00302 const uint32_t num_op = 2; 00303 const size_t malloc_size_1 = 20, malloc_size_2 = 30; 00304 const mem_trace_data_t *pmem = stats.op_data; 00305 00306 test_clear_stats(); 00307 00308 // Start tracing 00309 mbed_mem_trace_set_callback(test_trace_cb); 00310 00311 // Allocate a buffer 00312 void *p_malloc_1 = malloc(malloc_size_1); 00313 TEST_ASSERT_NOT_EQUAL(p_malloc_1, NULL); 00314 00315 // Disable tracing before freeing the first buffer 00316 mbed_mem_trace_set_callback(NULL); 00317 free(p_malloc_1); 00318 00319 // Allocate another buffer (still not traced) 00320 void *p_malloc_2 = malloc(malloc_size_2); 00321 TEST_ASSERT_NOT_EQUAL(p_malloc_2, NULL); 00322 00323 // Re-enable tracing 00324 mbed_mem_trace_set_callback(test_trace_cb); 00325 00326 // And free the second buffer (this operation should be tracer) 00327 free(p_malloc_2); 00328 00329 // Stop tracing 00330 mbed_mem_trace_set_callback(NULL); 00331 00332 // Check tracer result 00333 check_sanity(num_op); 00334 check_malloc_op(pmem++, p_malloc_1, malloc_size_1); 00335 check_free_op(pmem, p_malloc_2); 00336 } 00337 00338 00339 /** Test new/delete tracing 00340 * 00341 * Given a memory trace mechanism 00342 * When memory allocation/deallocation is performed using new/delete 00343 * Then tracing matches the operations 00344 * 00345 */ 00346 static void test_case_new_delete() 00347 { 00348 const uint32_t num_op = 4; 00349 const mem_trace_data_t *pmem = stats.op_data; 00350 00351 test_clear_stats(); 00352 00353 // Start tracing 00354 mbed_mem_trace_set_callback(test_trace_cb); 00355 00356 // Test new, new[], delete and delete[] 00357 int *p_int = new int; 00358 int *p_int_array = new int[10]; 00359 delete p_int; 00360 delete[] p_int_array; 00361 00362 // Stop tracing 00363 mbed_mem_trace_set_callback(NULL); 00364 00365 // Check tracer result 00366 check_sanity(num_op); 00367 check_malloc_op(pmem++, p_int, sizeof(int)); 00368 check_malloc_op(pmem++, p_int_array, 10 * sizeof(int)); 00369 check_free_op(pmem++, p_int); 00370 check_free_op(pmem, p_int_array); 00371 } 00372 00373 00374 /** Test tracing thread safety 00375 * 00376 * Given a memory trace mechanism and multiple threads are started in parallel 00377 * When each of the threads perform memory allocation/deallocation (thus uses memory trace mechanisms) 00378 * Then tracing is protected against simultaneous multithreaded access 00379 * 00380 */ 00381 static void test_case_multithread_malloc_free() 00382 { 00383 const uint32_t wait_time_us = 10000; 00384 volatile bool threads_continue; 00385 TestThread<osPriorityNormal, THREAD_STACK_SIZE> threads[NUM_TEST_THREADS]; 00386 00387 mbed_mem_trace_set_callback(test_trace_cb_multithread); 00388 00389 threads_continue = true; 00390 for (int i = 0; i < NUM_TEST_THREADS; i++) { 00391 threads[i].start(callback(malloc_free, &threads_continue)); 00392 } 00393 00394 Thread::wait(wait_time_us); 00395 threads_continue = false; 00396 00397 for (int i = 0; i < NUM_TEST_THREADS; i++) { 00398 threads[i].join(); 00399 } 00400 00401 mbed_mem_trace_set_callback(NULL); 00402 } 00403 00404 00405 00406 static Case cases[] = 00407 { 00408 Case("Test single malloc/free trace", test_case_single_malloc_free), 00409 Case("Test all memory operations trace", test_case_all_memory_ops), 00410 Case("Test trace off", test_case_trace_off), 00411 Case("Test partial trace", test_case_partial_trace), 00412 Case("Test new/delete trace", test_case_new_delete), 00413 Case("Test multithreaded trace", test_case_multithread_malloc_free) 00414 }; 00415 00416 static utest::v1::status_t greentea_test_setup(const size_t number_of_cases) 00417 { 00418 GREENTEA_SETUP(15, "default_auto"); 00419 return utest::v1::greentea_test_setup_handler(number_of_cases); 00420 } 00421 00422 static utest::v1::Specification specification(greentea_test_setup, cases, utest::v1::greentea_test_teardown_handler); 00423 00424 int main() 00425 { 00426 // Disable stdout buffering to prevent any unwanted allocations 00427 setvbuf(stdout, NULL, _IONBF, 0); 00428 00429 return !utest::v1::Harness::run(specification); 00430 }
Generated on Tue Jul 12 2022 13:24:55 by
1.7.2