diff --git a/hal/api/mbed_mem_trace.h b/hal/api/mbed_mem_trace.h new file mode 100644 index 0000000000..2ca7ac76ad --- /dev/null +++ b/hal/api/mbed_mem_trace.h @@ -0,0 +1,138 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2016 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MBED_MEM_TRACE_H__ +#define __MBED_MEM_TRACE_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/* Operation types for tracer */ +enum { + MBED_MEM_TRACE_MALLOC, + MBED_MEM_TRACE_REALLOC, + MBED_MEM_TRACE_CALLOC, + MBED_MEM_TRACE_FREE +}; + +/* Prefix for the output of the default tracer */ +#define MBED_MEM_DEFAULT_TRACER_PREFIX "#" + +/** + * Type of the callback used by the memory tracer. This callback is called when a memory + * allocation operation (malloc, realloc, calloc, free) is called and tracing is enabled + * for that memory allocation function. + * + * @param op the ID of the operation (MBED_MEM_TRACE_MALLOC, MBED_MEM_TRACE_REALLOC, + * MBED_MEM_TRACE_CALLOC or MBED_MEM_TRACE_FREE). + * @param res the result that the memory operation returned (NULL for 'free'). + * @param caller the caller of the memory operation. Note that the value of 'caller' might be + * unreliable. + * + * The rest of the parameters passed 'mbed_mem_trace_cb_t' are the same as the memory operations + * that triggered its call (see 'man malloc' for details): + * + * - for malloc: cb(MBED_MEM_TRACE_MALLOC, res, caller, size). + * - for realloc: cb(MBED_MEM_TRACE_REALLOC, res, caller, ptr, size). + * - for calloc: cb(MBED_MEM_TRACE_CALLOC, res, caller, nmemb, size). + * - for free: cb(MBED_MEM_TRACE_FREE, NULL, caller, ptr). + */ +typedef void (*mbed_mem_trace_cb_t)(uint8_t op, void *res, void* caller, ...); + +/** + * Set the callback used by the memory tracer (use NULL for disable tracing). + * + * @param cb the callback to call on each memory operation. + */ +void mbed_mem_trace_set_callback(mbed_mem_trace_cb_t cb); + +/** + * Trace a call to 'malloc'. + * @param res the result of running 'malloc'. + * @param size the 'size' argument given to 'malloc'. + * @param caller the caller of the memory operation. + * @return 'res' (the first argument). + */ +void *mbed_mem_trace_malloc(void *res, size_t size, void *caller); + +/** + * Trace a call to 'realloc'. + * @param res the result of running 'realloc'. + * @param ptr the 'ptr' argument given to 'realloc'. + * @param size the 'size' argument given to 'realloc'. + * + * @return 'res' (the first argument). + */ +void *mbed_mem_trace_realloc(void *res, void *ptr, size_t size, void *caller); + +/** + * Trace a call to 'calloc'. + * @param res the result of running 'calloc'. + * @param nmemb the 'nmemb' argument given to 'calloc'. + * @param size the 'size' argument given to 'calloc'. + * @param caller the caller of the memory operation. + * @Return 'res' (the first argument). + */ +void *mbed_mem_trace_calloc(void *res, size_t num, size_t size, void *caller); + +/** + * Trace a call to 'free'. + * @param ptr the 'ptr' argument given to 'free'. + * @param caller the caller of the memory operation. + */ +void mbed_mem_trace_free(void *ptr, void *caller); + +/** + * Default memory trace callback. DO NOT CALL DIRECTLY. It is meant to be used + * as the second argument of 'mbed_mem_trace_setup'. + * + * The default callback outputs trace data using 'printf', in a format that's + * easily parsable by an external tool. For each memory operation, the callback + * outputs a line that begins with '#:<0xresult>;<0xcaller>-': + * + * - 'op' identifies the memory operation ('m' for 'malloc', 'r' for 'realloc', + * 'c' for 'calloc' and 'f' for 'free'). + * - 'result' (base 16) is the result of the memor operation. This is always NULL + * for 'free', since 'free' doesn't return anything. + * -'caller' (base 16) is the caller of the memory operation. Note that the value + * of 'caller' might be unreliable. + * + * The rest of the output depends on the operation being traced: + * + * - for 'malloc': 'size', where 'size' is the original argument to 'malloc'. + * - for 'realloc': '0xptr;size', where 'ptr' (base 16) and 'size' are the original arguments to 'realloc'. + * - for 'calloc': 'nmemb;size', where 'nmemb' and 'size' are the original arguments to 'calloc'. + * - for 'free': '0xptr', where 'ptr' (base 16) is the original argument to 'free'. + * + * Examples: + * + * - '#m:0x20003240;0x600d-50' encodes a 'malloc' that returned 0x20003240, was called + * by the instruction at 0x600D with a the 'size' argument equal to 50. + * - '#f:0x0;0x602f-0x20003240' encodes a 'free' that was called by the instruction at + * 0x602f with the 'ptr' argument equal to 0x20003240. + */ +void mbed_mem_trace_default_callback(uint8_t op, void *res, void *caller, ...); + +#ifdef __cplusplus +} +#endif + +#endif// #ifndef __MBED_MEM_TRACE_H__ + diff --git a/hal/api/toolchain.h b/hal/api/toolchain.h index e7bff5fd70..f02bae058d 100644 --- a/hal/api/toolchain.h +++ b/hal/api/toolchain.h @@ -240,6 +240,29 @@ */ #define MBED_DEPRECATED_SINCE(D, M) MBED_DEPRECATED(M " [since " D "]") +/** MBED_CALLER_ADDR() + * Returns the caller of the current function. + * + * @note + * This macro is only implemented for GCC and ARMCC. + * + * @code + * #include "toolchain.h" + * + * printf("This function was called from %p", MBED_CALLER_ADDR()); + * @endcode + * + * @return Address of the calling function + */ +#ifndef MBED_CALLER_ADDR +#if (defined(__GNUC__) || defined(__clang__)) && !defined(__CC_ARM) +#define MBED_CALLER_ADDR() __builtin_extract_return_addr(__builtin_return_address(0)) +#elif defined(__CC_ARM) +#define MBED_CALLER_ADDR() __builtin_return_address(0) +#else +#define MBED_CALLER_ADDR() (NULL) +#endif +#endif // FILEHANDLE declaration #if defined(TOOLCHAIN_ARM) diff --git a/hal/common/mbed_alloc_wrappers.cpp b/hal/common/mbed_alloc_wrappers.cpp new file mode 100644 index 0000000000..5e923f11df --- /dev/null +++ b/hal/common/mbed_alloc_wrappers.cpp @@ -0,0 +1,312 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2016 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mbed_mem_trace.h" +#include "mbed_stats.h" +#include "toolchain.h" +#include "SingletonPtr.h" +#include "PlatformMutex.h" +#include +#include +#include +#include + +/* There are two memory tracers in mbed OS: + +- the first can be used to detect the maximum heap usage at runtime. It is + activated by defining the MBED_HEAP_STATS_ENABLED macro. +- the second can be used to trace each memory call by automatically invoking + a callback on each memory operation (see hal/api/mbed_mem_trace.h). It is + activated by defining the MBED_MEM_TRACING_ENABLED macro. + +Both tracers can be activated and deactivated in any combination. If both tracers +are active, the second one (MBED_MEM_TRACING_ENABLED) will trace the first one's +(MBED_HEAP_STATS_ENABLED) memory calls.*/ + +/******************************************************************************/ +/* Implementation of the runtime max heap usage checker */ +/******************************************************************************/ + +/* Size must be a multiple of 8 to keep alignment */ +typedef struct { + uint32_t size; + uint32_t pad; +} alloc_info_t; + +static SingletonPtr malloc_stats_mutex; +static mbed_stats_heap_t heap_stats = {0, 0, 0, 0, 0}; + +void mbed_stats_heap_get(mbed_stats_heap_t *stats) +{ + malloc_stats_mutex->lock(); + memcpy(stats, &heap_stats, sizeof(mbed_stats_heap_t)); + malloc_stats_mutex->unlock(); +} + +/******************************************************************************/ +/* GCC memory allocation wrappers */ +/******************************************************************************/ + +#if defined(TOOLCHAIN_GCC) + +#ifdef FEATURE_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#endif/* FEATURE_UVISOR */ + +// TODO: memory tracing doesn't work with uVisor enabled. +#if !defined(FEATURE_UVISOR) + +extern "C" { + void * __real__malloc_r(struct _reent * r, size_t size); + void * __real__realloc_r(struct _reent * r, void * ptr, size_t size); + void __real__free_r(struct _reent * r, void * ptr); + void* __real__calloc_r(struct _reent * r, size_t nmemb, size_t size); +} + +extern "C" void * __wrap__malloc_r(struct _reent * r, size_t size) { + void *ptr = NULL; +#ifdef MBED_HEAP_STATS_ENABLED + malloc_stats_mutex->lock(); + alloc_info_t *alloc_info = (alloc_info_t*)__real__malloc_r(r, size + sizeof(alloc_info_t)); + if (alloc_info != NULL) { + alloc_info->size = size; + ptr = (void*)(alloc_info + 1); + heap_stats.current_size += size; + heap_stats.total_size += size; + heap_stats.alloc_cnt += 1; + if (heap_stats.current_size > heap_stats.max_size) { + heap_stats.max_size = heap_stats.current_size; + } + } else { + heap_stats.alloc_fail_cnt += 1; + } + malloc_stats_mutex->unlock(); +#else // #ifdef MBED_HEAP_STATS_ENABLED + ptr = __real__malloc_r(r, size); +#endif // #ifdef MBED_HEAP_STATS_ENABLED +#ifdef MBED_MEM_TRACING_ENABLED + mbed_mem_trace_malloc(ptr, size, MBED_CALLER_ADDR()); +#endif // #ifdef MBED_MEM_TRACING_ENABLED + return ptr; +} + +extern "C" void * __wrap__realloc_r(struct _reent * r, void * ptr, size_t size) { + void *new_ptr = NULL; +#ifdef MBED_HEAP_STATS_ENABLED + // Implement realloc_r with malloc and free. + // The function realloc_r can't be used here directly since + // it can call into __wrap__malloc_r (returns ptr + 4) or + // resize memory directly (returns ptr + 0). + + // Note - no lock needed since malloc and free are thread safe + + // Get old size + uint32_t old_size = 0; + if (ptr != NULL) { + alloc_info_t *alloc_info = ((alloc_info_t*)ptr) - 1; + old_size = alloc_info->size; + } + + // Allocate space + if (size != 0) { + new_ptr = malloc(size); + } + + // If the new buffer has been allocated copy the data to it + // and free the old buffer + if (new_ptr != NULL) { + uint32_t copy_size = (old_size < size) ? old_size : size; + memcpy(new_ptr, (void*)ptr, copy_size); + free(ptr); + } +#else // #ifdef MBED_HEAP_STATS_ENABLED + new_ptr = __real__realloc_r(r, ptr, size); +#endif // #ifdef MBED_HEAP_STATS_ENABLED +#ifdef MBED_MEM_TRACING_ENABLED + mbed_mem_trace_realloc(new_ptr, ptr, size, MBED_CALLER_ADDR()); +#endif // #ifdef MBED_MEM_TRACING_ENABLED + return new_ptr; +} + +extern "C" void __wrap__free_r(struct _reent * r, void * ptr) { +#ifdef MBED_HEAP_STATS_ENABLED + malloc_stats_mutex->lock(); + alloc_info_t *alloc_info = NULL; + if (ptr != NULL) { + alloc_info = ((alloc_info_t*)ptr) - 1; + heap_stats.current_size -= alloc_info->size; + heap_stats.alloc_cnt -= 1; + } + __real__free_r(r, (void*)alloc_info); + malloc_stats_mutex->unlock(); +#else // #ifdef MBED_HEAP_STATS_ENABLED + __real__free_r(r, ptr); +#endif // #ifdef MBED_HEAP_STATS_ENABLED +#ifdef MBED_MEM_TRACING_ENABLED + mbed_mem_trace_free(ptr, MBED_CALLER_ADDR()); +#endif // #ifdef MBED_MEM_TRACING_ENABLED +} + +extern "C" void * __wrap__calloc_r(struct _reent * r, size_t nmemb, size_t size) { + void *ptr = NULL; +#ifdef MBED_HEAP_STATS_ENABLED + // Note - no lock needed since malloc is thread safe + + ptr = malloc(nmemb * size); + if (ptr != NULL) { + memset(ptr, 0, nmemb * size); + } +#else // #ifdef MBED_HEAP_STATS_ENABLED + ptr = __real__calloc_r(r, nmemb, size); +#endif // #ifdef MBED_HEAP_STATS_ENABLED +#ifdef MBED_MEM_TRACING_ENABLED + mbed_mem_trace_calloc(ptr, nmemb, size, MBED_CALLER_ADDR()); +#endif // #ifdef MBED_MEM_TRACING_ENABLED + return ptr; +} + +#endif // if !defined(FEATURE_UVISOR) + +/******************************************************************************/ +/* ARMCC memory allocation wrappers */ +/******************************************************************************/ + +#elif defined(TOOLCHAIN_ARM) // #if defined(TOOLCHAIN_GCC) + +/* Enable hooking of memory function only if tracing is also enabled */ +#if defined(MBED_MEM_TRACING_ENABLED) || defined(MBED_HEAP_STATS_ENABLED) + +extern "C" { + void *$Super$$malloc(size_t size); + void *$Super$$realloc(void *ptr, size_t size); + void *$Super$$calloc(size_t nmemb, size_t size); + void $Super$$free(void *ptr); +} + +extern "C" void* $Sub$$malloc(size_t size) { + void *ptr = NULL; +#ifdef MBED_HEAP_STATS_ENABLED + malloc_stats_mutex->lock(); + alloc_info_t *alloc_info = (alloc_info_t*)$Super$$malloc(size + sizeof(alloc_info_t)); + if (alloc_info != NULL) { + alloc_info->size = size; + ptr = (void*)(alloc_info + 1); + heap_stats.current_size += size; + heap_stats.total_size += size; + heap_stats.alloc_cnt += 1; + if (heap_stats.current_size > heap_stats.max_size) { + heap_stats.max_size = heap_stats.current_size; + } + } else { + heap_stats.alloc_fail_cnt += 1; + } + malloc_stats_mutex->unlock(); +#else // #ifdef MBED_HEAP_STATS_ENABLED + ptr = $Super$$malloc(size); +#endif // #ifdef MBED_HEAP_STATS_ENABLED +#ifdef MBED_MEM_TRACING_ENABLED + mbed_mem_trace_malloc(ptr, size, MBED_CALLER_ADDR()); +#endif // #ifdef MBED_MEM_TRACING_ENABLED + return ptr; +} + +extern "C" void* $Sub$$realloc(void *ptr, size_t size) { + void *new_ptr = NULL; +#ifdef MBED_HEAP_STATS_ENABLED + // Note - no lock needed since malloc and free are thread safe + + // Get old size + uint32_t old_size = 0; + if (ptr != NULL) { + alloc_info_t *alloc_info = ((alloc_info_t*)ptr) - 1; + old_size = alloc_info->size; + } + + // Allocate space + if (size != 0) { + new_ptr = malloc(size); + } + + // If the new buffer has been allocated copy the data to it + // and free the old buffer + if (new_ptr != NULL) { + uint32_t copy_size = (old_size < size) ? old_size : size; + memcpy(new_ptr, (void*)ptr, copy_size); + free(ptr); + } +#else // #ifdef MBED_HEAP_STATS_ENABLED + new_ptr = $Super$$realloc(ptr, size); +#endif // #ifdef MBED_HEAP_STATS_ENABLED +#ifdef MBED_MEM_TRACING_ENABLED + mbed_mem_trace_realloc(new_ptr, ptr, size, MBED_CALLER_ADDR()); +#endif // #ifdef MBED_MEM_TRACING_ENABLED + return new_ptr; +} + +extern "C" void *$Sub$$calloc(size_t nmemb, size_t size) { + void *ptr = NULL; +#ifdef MBED_HEAP_STATS_ENABLED + // Note - no lock needed since malloc is thread safe + ptr = malloc(nmemb * size); + if (ptr != NULL) { + memset(ptr, 0, nmemb * size); + } +#else // #ifdef MBED_HEAP_STATS_ENABLED + ptr = $Super$$calloc(nmemb, size); +#endif // #ifdef MBED_HEAP_STATS_ENABLED +#ifdef MBED_MEM_TRACING_ENABLED + mbed_mem_trace_calloc(ptr, nmemb, size, MBED_CALLER_ADDR()); +#endif // #ifdef MBED_MEM_TRACING_ENABLED + return ptr; +} + +extern "C" void $Sub$$free(void *ptr) { +#ifdef MBED_HEAP_STATS_ENABLED + malloc_stats_mutex->lock(); + alloc_info_t *alloc_info = NULL; + if (ptr != NULL) { + alloc_info = ((alloc_info_t*)ptr) - 1; + heap_stats.current_size -= alloc_info->size; + heap_stats.alloc_cnt -= 1; + } + $Super$$free((void*)alloc_info); + malloc_stats_mutex->unlock(); +#else // #ifdef MBED_HEAP_STATS_ENABLED + $Super$$free(ptr); +#endif // #ifdef MBED_HEAP_STATS_ENABLED +#ifdef MBED_MEM_TRACING_ENABLED + mbed_mem_trace_free(ptr, MBED_CALLER_ADDR()); +#endif // #ifdef MBED_MEM_TRACING_ENABLED +} + +#endif // #if defined(MBED_MEM_TRACING_ENABLED) || defined(MBED_HEAP_STATS_ENABLED) + +/******************************************************************************/ +/* Allocation wrappers for other toolchains are not supported yet */ +/******************************************************************************/ + +#else // #if defined(TOOLCHAIN_GCC) + +#ifdef MBED_MEM_TRACING_ENABLED +#warning Memory tracing is not supported with the current toolchain. +#endif + +#ifdef MBED_HEAP_STATS_ENABLED +#warning Heap statistics are not supported with the current toolchain. +#endif + +#endif // #if defined(TOOLCHAIN_GCC) + diff --git a/hal/common/mbed_mem_trace.c b/hal/common/mbed_mem_trace.c new file mode 100644 index 0000000000..ab753fadc7 --- /dev/null +++ b/hal/common/mbed_mem_trace.c @@ -0,0 +1,115 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2016 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include "mbed_mem_trace.h" +#include "critical.h" + +/****************************************************************************** + * Internal variables, functions and helpers + *****************************************************************************/ + +/* The callback function that will be called after a traced memory operations finishes. */ +static mbed_mem_trace_cb_t mem_trace_cb; +/* 'trave_level' guards "trace inside trace" situations (for example, the implementation + * of realloc() might call malloc() internally, and since malloc() is also traced, this could + * result in two calls to the callback function instead of one. */ +static uint8_t trace_level; + +/****************************************************************************** + * Public interface + *****************************************************************************/ + +void mbed_mem_trace_set_callback(mbed_mem_trace_cb_t cb) { + mem_trace_cb = cb; +} + +void *mbed_mem_trace_malloc(void *res, size_t size, void *caller) { + if (mem_trace_cb) { + if (core_util_atomic_incr_u8(&trace_level, 1) == 1) { + mem_trace_cb(MBED_MEM_TRACE_MALLOC, res, caller, size); + } + core_util_atomic_decr_u8(&trace_level, 1); + } + return res; +} + +void *mbed_mem_trace_realloc(void *res, void *ptr, size_t size, void *caller) { + if (mem_trace_cb) { + if (core_util_atomic_incr_u8(&trace_level, 1) == 1) { + mem_trace_cb(MBED_MEM_TRACE_REALLOC, res, caller, ptr, size); + } + core_util_atomic_decr_u8(&trace_level, 1); + } + return res; +} + +void *mbed_mem_trace_calloc(void *res, size_t num, size_t size, void *caller) { + if (mem_trace_cb) { + if (core_util_atomic_incr_u8(&trace_level, 1) == 1) { + mem_trace_cb(MBED_MEM_TRACE_CALLOC, res, caller, num, size); + } + core_util_atomic_decr_u8(&trace_level, 1); + } + return res; +} + +void mbed_mem_trace_free(void *ptr, void *caller) { + if (mem_trace_cb) { + if (core_util_atomic_incr_u8(&trace_level, 1) == 1) { + mem_trace_cb(MBED_MEM_TRACE_FREE, NULL, caller, ptr); + } + core_util_atomic_decr_u8(&trace_level, 1); + } +} + +void mbed_mem_trace_default_callback(uint8_t op, void *res, void *caller, ...) { + va_list va; + size_t temp_s1, temp_s2; + void *temp_ptr; + + va_start(va, caller); + switch(op) { + case MBED_MEM_TRACE_MALLOC: + temp_s1 = va_arg(va, size_t); + printf(MBED_MEM_DEFAULT_TRACER_PREFIX "m:%p;%p-%u\n", res, caller, temp_s1); + break; + + case MBED_MEM_TRACE_REALLOC: + temp_ptr = va_arg(va, void*); + temp_s1 = va_arg(va, size_t); + printf(MBED_MEM_DEFAULT_TRACER_PREFIX "r:%p;%p-%p;%u\n", res, caller, temp_ptr, temp_s1); + break; + + case MBED_MEM_TRACE_CALLOC: + temp_s1 = va_arg(va, size_t); + temp_s2 = va_arg(va, size_t); + printf(MBED_MEM_DEFAULT_TRACER_PREFIX "c:%p;%p-%u;%u\n", res, caller, temp_s1, temp_s2); + break; + + case MBED_MEM_TRACE_FREE: + temp_ptr = va_arg(va, void*); + printf(MBED_MEM_DEFAULT_TRACER_PREFIX "f:%p;%p-%p\n", res, caller, temp_ptr); + break; + + default: + printf("?\n"); + } + va_end(va); +} + diff --git a/hal/common/retarget.cpp b/hal/common/retarget.cpp index abc1d82a6f..16c4c58221 100644 --- a/hal/common/retarget.cpp +++ b/hal/common/retarget.cpp @@ -479,135 +479,13 @@ extern "C" WEAK void __cxa_pure_virtual(void) { #endif -/* Size must be a multiple of 8 to keep alignment */ -typedef struct { - uint32_t size; - uint32_t pad; -} alloc_info_t; - -static SingletonPtr malloc_stats_mutex; -static mbed_stats_heap_t heap_stats = {0, 0, 0, 0, 0}; - -void mbed_stats_heap_get(mbed_stats_heap_t *stats) -{ - malloc_stats_mutex->lock(); - memcpy(stats, &heap_stats, sizeof(mbed_stats_heap_t)); - malloc_stats_mutex->unlock(); -} - #if defined(TOOLCHAIN_GCC) -#ifdef FEATURE_UVISOR -#include "uvisor-lib/uvisor-lib.h" -#endif/* FEATURE_UVISOR */ - -extern "C" { - /* uVisor wraps malloc_r, realloc_r and free_r, but not calloc_r! */ #ifndef FEATURE_UVISOR -extern "C" void __malloc_lock( struct _reent *_r ); -extern "C" void __malloc_unlock( struct _reent *_r ); - -void * __wrap__malloc_r(struct _reent * r, size_t size) { - extern void * __real__malloc_r(struct _reent * r, size_t size); -#if !defined(MBED_HEAP_STATS_ENABLED ) || !MBED_HEAP_STATS_ENABLED - return __real__malloc_r(r, size); -#else - - malloc_stats_mutex->lock(); - alloc_info_t *alloc_info = (alloc_info_t*)__real__malloc_r(r, size + sizeof(alloc_info_t)); - void *ptr = NULL; - if (alloc_info != NULL) { - alloc_info->size = size; - ptr = (void*)(alloc_info + 1); - heap_stats.current_size += size; - heap_stats.total_size += size; - heap_stats.alloc_cnt += 1; - if (heap_stats.current_size > heap_stats.max_size) { - heap_stats.max_size = heap_stats.current_size; - } - } else { - heap_stats.alloc_fail_cnt += 1; - } - malloc_stats_mutex->unlock(); - - return ptr; -#endif -} -void * __wrap__realloc_r(struct _reent * r, void * ptr, size_t size) { -#if !defined(MBED_HEAP_STATS_ENABLED ) || !MBED_HEAP_STATS_ENABLED - extern void * __real__realloc_r(struct _reent * r, void * ptr, size_t size); - return __real__realloc_r(r, ptr, size); -#else - - // Implement realloc_r with malloc and free. - // The function realloc_r can't be used here directly since - // it can call into __wrap__malloc_r (returns ptr + 4) or - // resize memory directly (returns ptr + 0). - - // Note - no lock needed since malloc and free are thread safe - - // Get old size - uint32_t old_size = 0; - if (ptr != NULL) { - alloc_info_t *alloc_info = ((alloc_info_t*)ptr) - 1; - old_size = alloc_info->size; - } - - // Allocate space - void *new_ptr = NULL; - if (size != 0) { - new_ptr = malloc(size); - } - - // If the new buffer has been allocated copy the data to it - // and free the old buffer - if (new_ptr != NULL) { - uint32_t copy_size = (old_size < size) ? old_size : size; - memcpy(new_ptr, (void*)ptr, copy_size); - free(ptr); - } - - return new_ptr; -#endif -} -void __wrap__free_r(struct _reent * r, void * ptr) { - extern void __real__free_r(struct _reent * r, void * ptr); -#if !defined(MBED_HEAP_STATS_ENABLED ) || !MBED_HEAP_STATS_ENABLED - __real__free_r(r, ptr); -#else - - malloc_stats_mutex->lock(); - alloc_info_t *alloc_info = NULL; - if (ptr != NULL) { - alloc_info = ((alloc_info_t*)ptr) - 1; - heap_stats.current_size -= alloc_info->size; - heap_stats.alloc_cnt -= 1; - } - __real__free_r(r, (void*)alloc_info); - malloc_stats_mutex->unlock(); -#endif -} #endif/* FEATURE_UVISOR */ -void* __wrap__calloc_r(struct _reent * r, size_t num, size_t size) { -#if !defined(MBED_HEAP_STATS_ENABLED ) || !MBED_HEAP_STATS_ENABLED - extern void* __real__calloc_r(struct _reent * r, size_t num, size_t size); - return __real__calloc_r(r, num, size); -#else - - // Note - no lock needed since malloc is thread safe - - void *ptr = malloc(num * size); - if (ptr != NULL) { - memset(ptr, 0, num * size); - } - - return ptr; -#endif -} -} extern "C" WEAK void software_init_hook_rtos(void) { @@ -629,85 +507,6 @@ extern "C" void software_init_hook(void) } #endif -#if defined(TOOLCHAIN_ARM) && (defined(MBED_HEAP_STATS_ENABLED ) && MBED_HEAP_STATS_ENABLED ) - -extern "C" void *$Super$$malloc(size_t size); -extern "C" void *$Super$$realloc(void * ptr, size_t size); -extern "C" void $Super$$free(void * ptr); - -extern "C" void *$Sub$$malloc(size_t size) -{ - malloc_stats_mutex->lock(); - alloc_info_t *alloc_info = (alloc_info_t*)$Super$$malloc(size + sizeof(alloc_info_t)); - void *ptr = NULL; - if (alloc_info != NULL) { - alloc_info->size = size; - ptr = (void*)(alloc_info + 1); - heap_stats.current_size += size; - heap_stats.total_size += size; - heap_stats.alloc_cnt += 1; - if (heap_stats.current_size > heap_stats.max_size) { - heap_stats.max_size = heap_stats.current_size; - } - } else { - heap_stats.alloc_fail_cnt += 1; - } - malloc_stats_mutex->unlock(); - - return ptr; -} - -extern "C" void *$Sub$$realloc(void * ptr, size_t size) -{ - // Note - no lock needed since malloc and free are thread safe - - // Get old size - uint32_t old_size = 0; - if (ptr != NULL) { - alloc_info_t *alloc_info = ((alloc_info_t*)ptr) - 1; - old_size = alloc_info->size; - } - - // Allocate space - void *new_ptr = NULL; - if (size != 0) { - new_ptr = malloc(size); - } - - // If the new buffer has been allocated copy the data to it - // and free the old buffer - if (new_ptr != NULL) { - uint32_t copy_size = (old_size < size) ? old_size : size; - memcpy(new_ptr, (void*)ptr, copy_size); - free(ptr); - } - - return new_ptr; -} -extern "C" void $Sub$$free(void * ptr) -{ - malloc_stats_mutex->lock(); - alloc_info_t *alloc_info = NULL; - if (ptr != NULL) { - alloc_info = ((alloc_info_t*)ptr) - 1; - heap_stats.current_size -= alloc_info->size; - heap_stats.alloc_cnt -= 1; - } - $Super$$free((void*)alloc_info); - malloc_stats_mutex->unlock(); -} -extern "C" void *$Sub$$calloc(size_t num, size_t size) -{ - // Note - no lock needed since malloc is thread safe - void *ptr = malloc(num * size); - if (ptr != NULL) { - memset(ptr, 0, num * size); - } - return ptr; -} - -#endif /* defined(TOOLCHAIN_ARM) && (defined(MBED_HEAP_STATS_ENABLED ) && MBED_HEAP_STATS_ENABLED ) */ - // **************************************************************************** // mbed_main is a function that is called before main() // mbed_sdk_init() is also a function that is called before main(), but unlike