mirror of https://github.com/ARMmbed/mbed-os.git
Heap statistics
Keep track of the current size allocated, maximum size allocated, number of allocations, failed allocations and total size allocated for both GCC and ARM. Report the maximum size allocated at the end of testing. Also, add a test to verify heap metrics are working as expected.pull/2442/head
parent
dda7f7d77a
commit
6a31ffbf3e
|
|
@ -0,0 +1,207 @@
|
|||
/*
|
||||
* Copyright (c) 2013-2016, ARM Limited, All Rights Reserved
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* 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.h"
|
||||
#include "greentea-client/test_env.h"
|
||||
#include "unity/unity.h"
|
||||
#include "utest/utest.h"
|
||||
#include "mbed_stats.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#if !defined(MBED_HEAP_STATS_ENABLED) || !MBED_HEAP_STATS_ENABLED || defined(__ICCARM__)
|
||||
#error [NOT_SUPPORTED] test not supported
|
||||
#endif
|
||||
|
||||
using namespace utest::v1;
|
||||
|
||||
#define ALLOCATION_SIZE_DEFAULT 564
|
||||
#define ALLOCATION_SIZE_SMALL 124
|
||||
#define ALLOCATION_SIZE_LARGE 790
|
||||
#define ALLOCATION_SIZE_FAIL (1024 * 1024 *1024)
|
||||
|
||||
typedef void* (*malloc_cb_t) (uint32_t size);
|
||||
|
||||
static void* thunk_malloc(uint32_t size);
|
||||
static void* thunk_calloc_1(uint32_t size);
|
||||
static void* thunk_calloc_4(uint32_t size);
|
||||
static void* thunk_realloc(uint32_t size);
|
||||
|
||||
malloc_cb_t malloc_thunk_array[] = {
|
||||
thunk_malloc,
|
||||
thunk_calloc_1,
|
||||
thunk_calloc_4,
|
||||
thunk_realloc,
|
||||
};
|
||||
|
||||
void test_case_malloc_free_size()
|
||||
{
|
||||
printf("Initial print to setup stdio buffers\n");
|
||||
mbed_stats_heap_t stats_start;
|
||||
mbed_stats_heap_t stats_current;
|
||||
void *data;
|
||||
|
||||
mbed_stats_heap_get(&stats_start);
|
||||
|
||||
for (uint32_t i = 0; i < sizeof(malloc_thunk_array) / sizeof(malloc_cb_t); i++) {
|
||||
|
||||
// Allocate memory and assert size change
|
||||
data = malloc_thunk_array[i](ALLOCATION_SIZE_DEFAULT);
|
||||
TEST_ASSERT(data != NULL);
|
||||
mbed_stats_heap_get(&stats_current);
|
||||
uint32_t increase = stats_current.current_size - stats_start.current_size;
|
||||
TEST_ASSERT_EQUAL_UINT32(ALLOCATION_SIZE_DEFAULT, increase);
|
||||
TEST_ASSERT_EQUAL_UINT32(stats_start.total_size + ALLOCATION_SIZE_DEFAULT * (i + 1), stats_current.total_size);
|
||||
TEST_ASSERT_EQUAL_UINT32(stats_start.alloc_cnt + 1, stats_current.alloc_cnt);
|
||||
TEST_ASSERT_EQUAL_UINT32(stats_start.alloc_fail_cnt, stats_current.alloc_fail_cnt);
|
||||
|
||||
// Free memory and assert back to starting size
|
||||
free(data);
|
||||
mbed_stats_heap_get(&stats_current);
|
||||
TEST_ASSERT_EQUAL_UINT32(stats_start.current_size, stats_current.current_size);
|
||||
TEST_ASSERT_EQUAL_UINT32(stats_start.alloc_cnt, stats_current.alloc_cnt);
|
||||
TEST_ASSERT_EQUAL_UINT32(stats_start.alloc_fail_cnt, stats_current.alloc_fail_cnt);
|
||||
}
|
||||
}
|
||||
|
||||
void test_case_allocate_zero()
|
||||
{
|
||||
mbed_stats_heap_t stats_start;
|
||||
mbed_stats_heap_t stats_current;
|
||||
void *data;
|
||||
|
||||
mbed_stats_heap_get(&stats_start);
|
||||
|
||||
for (uint32_t i = 0; i < sizeof(malloc_thunk_array) / sizeof(malloc_cb_t); i++) {
|
||||
|
||||
// Allocate memory and assert size change
|
||||
data = malloc_thunk_array[i](0);
|
||||
// Return can be NULL
|
||||
mbed_stats_heap_get(&stats_current);
|
||||
TEST_ASSERT_EQUAL_UINT32(stats_start.current_size, stats_current.current_size);
|
||||
TEST_ASSERT_EQUAL_UINT32(stats_start.total_size, stats_current.total_size);
|
||||
TEST_ASSERT_EQUAL_UINT32(stats_start.alloc_fail_cnt, stats_current.alloc_fail_cnt);
|
||||
|
||||
// Free memory and assert back to starting size
|
||||
free(data);
|
||||
mbed_stats_heap_get(&stats_current);
|
||||
TEST_ASSERT_EQUAL_UINT32(stats_start.current_size, stats_current.current_size);
|
||||
TEST_ASSERT_EQUAL_UINT32(stats_start.alloc_cnt, stats_current.alloc_cnt);
|
||||
TEST_ASSERT_EQUAL_UINT32(stats_start.alloc_fail_cnt, stats_current.alloc_fail_cnt);
|
||||
}
|
||||
}
|
||||
|
||||
void test_case_allocate_fail()
|
||||
{
|
||||
mbed_stats_heap_t stats_start;
|
||||
mbed_stats_heap_t stats_current;
|
||||
void *data;
|
||||
|
||||
mbed_stats_heap_get(&stats_start);
|
||||
|
||||
for (uint32_t i = 0; i < sizeof(malloc_thunk_array) / sizeof(malloc_cb_t); i++) {
|
||||
|
||||
// Trigger a failure by trying to allocate a buffer that won't fit
|
||||
data = malloc_thunk_array[i](ALLOCATION_SIZE_FAIL);
|
||||
TEST_ASSERT(data == NULL);
|
||||
mbed_stats_heap_get(&stats_current);
|
||||
TEST_ASSERT_EQUAL_UINT32(stats_start.current_size, stats_current.current_size);
|
||||
TEST_ASSERT_EQUAL_UINT32(stats_start.total_size, stats_current.total_size);
|
||||
TEST_ASSERT_EQUAL_UINT32(stats_start.alloc_cnt, stats_current.alloc_cnt);
|
||||
TEST_ASSERT_EQUAL_UINT32(stats_start.alloc_fail_cnt + i + 1, stats_current.alloc_fail_cnt);
|
||||
}
|
||||
}
|
||||
|
||||
static void* thunk_malloc(uint32_t size)
|
||||
{
|
||||
printf("Malloc thunk\n");
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
static void* thunk_calloc_1(uint32_t size)
|
||||
{
|
||||
printf("Calloc thunk 1 byte\n");
|
||||
return calloc(size / 1, 1);
|
||||
}
|
||||
|
||||
static void* thunk_calloc_4(uint32_t size)
|
||||
{
|
||||
printf("Calloc thunk 4 bytes\n");
|
||||
return calloc(size / 4, 4);
|
||||
}
|
||||
|
||||
|
||||
static void* thunk_realloc(uint32_t size)
|
||||
{
|
||||
printf("Realloc thunk\n");
|
||||
return realloc(NULL, size);
|
||||
}
|
||||
|
||||
void test_case_realloc_size()
|
||||
{
|
||||
mbed_stats_heap_t stats_start;
|
||||
mbed_stats_heap_t stats_current;
|
||||
uint32_t increase;
|
||||
void *data;
|
||||
|
||||
mbed_stats_heap_get(&stats_start);
|
||||
|
||||
// Allocate memory and assert size change
|
||||
data = realloc(NULL, ALLOCATION_SIZE_DEFAULT);
|
||||
TEST_ASSERT(data != NULL);
|
||||
mbed_stats_heap_get(&stats_current);
|
||||
increase = stats_current.current_size - stats_start.current_size;
|
||||
TEST_ASSERT_EQUAL_UINT32(increase, ALLOCATION_SIZE_DEFAULT);
|
||||
|
||||
// Decrease size and assert size change
|
||||
data = realloc(data, ALLOCATION_SIZE_SMALL);
|
||||
TEST_ASSERT(data != NULL);
|
||||
mbed_stats_heap_get(&stats_current);
|
||||
increase = stats_current.current_size - stats_start.current_size;
|
||||
TEST_ASSERT_EQUAL_UINT32(increase, ALLOCATION_SIZE_SMALL);
|
||||
|
||||
// Increase size and assert size change
|
||||
data = realloc(data, ALLOCATION_SIZE_LARGE);
|
||||
TEST_ASSERT(data != NULL);
|
||||
mbed_stats_heap_get(&stats_current);
|
||||
increase = stats_current.current_size - stats_start.current_size;
|
||||
TEST_ASSERT_EQUAL_UINT32(increase, ALLOCATION_SIZE_LARGE);
|
||||
|
||||
// Free memory and assert back to starting size
|
||||
free(data);
|
||||
mbed_stats_heap_get(&stats_current);
|
||||
TEST_ASSERT_EQUAL_UINT32(stats_start.current_size, stats_current.current_size);
|
||||
}
|
||||
|
||||
Case cases[] = {
|
||||
Case("malloc and free size", test_case_malloc_free_size),
|
||||
Case("allocate size zero", test_case_allocate_zero),
|
||||
Case("allocation failure", test_case_allocate_fail),
|
||||
Case("realloc size", test_case_realloc_size),
|
||||
};
|
||||
|
||||
utest::v1::status_t greentea_test_setup(const size_t number_of_cases)
|
||||
{
|
||||
GREENTEA_SETUP(20, "default_auto");
|
||||
return greentea_test_setup_handler(number_of_cases);
|
||||
}
|
||||
|
||||
Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler);
|
||||
|
||||
int main()
|
||||
{
|
||||
Harness::run(specification);
|
||||
}
|
||||
|
|
@ -21,6 +21,7 @@
|
|||
#include "greentea-client/test_env.h"
|
||||
#include "utest/utest_stack_trace.h"
|
||||
#include "utest/utest_serial.h"
|
||||
#include "mbed_stats.h"
|
||||
|
||||
using namespace utest::v1;
|
||||
|
||||
|
|
@ -105,7 +106,10 @@ utest::v1::status_t utest::v1::greentea_test_setup_handler(const size_t number_o
|
|||
void utest::v1::greentea_test_teardown_handler(const size_t passed, const size_t failed, const failure_t failure)
|
||||
{
|
||||
UTEST_LOG_FUNCTION();
|
||||
mbed_stats_heap_t heap_stats;
|
||||
verbose_test_teardown_handler(passed, failed, failure);
|
||||
mbed_stats_heap_get(&heap_stats);
|
||||
greentea_send_kv("max_heap_usage",heap_stats.max_size);
|
||||
greentea_send_kv(TEST_ENV_TESTCASE_SUMMARY, passed, failed);
|
||||
int result = !(failed || (failure.reason && !(failure.reason & REASON_IGNORE)));
|
||||
GREENTEA_TESTSUITE_RESULT(result);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2016-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_STATS_H
|
||||
#define MBED_STATS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
uint32_t current_size; /**< Bytes allocated currently. */
|
||||
uint32_t max_size; /**< Max bytes allocated at a given time. */
|
||||
uint32_t total_size; /**< Cumulative sum of bytes ever allocated. */
|
||||
uint32_t alloc_cnt; /**< Current number of allocations. */
|
||||
uint32_t alloc_fail_cnt; /**< Number of failed allocations. */
|
||||
} mbed_stats_heap_t;
|
||||
|
||||
/**
|
||||
* Fill the passed in structure with heap stats.
|
||||
*/
|
||||
void mbed_stats_heap_get(mbed_stats_heap_t *stats);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -24,7 +24,9 @@
|
|||
#include "SingletonPtr.h"
|
||||
#include "PlatformMutex.h"
|
||||
#include "mbed_error.h"
|
||||
#include "mbed_stats.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#if DEVICE_STDIO_MESSAGES
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
|
@ -477,6 +479,22 @@ 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<PlatformMutex> 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"
|
||||
|
|
@ -484,17 +502,105 @@ extern "C" WEAK void __cxa_pure_virtual(void) {
|
|||
|
||||
#ifndef FEATURE_UVISOR
|
||||
extern "C" {
|
||||
|
||||
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
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
#endif/* FEATURE_UVISOR */
|
||||
|
|
@ -519,6 +625,85 @@ 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
|
||||
|
|
@ -684,6 +869,8 @@ char* mbed_gets(char*s, int size, FILE *_file){
|
|||
#endif
|
||||
}
|
||||
|
||||
} // namespace mbed
|
||||
|
||||
#if defined (__ICCARM__)
|
||||
// Stub out locks when an rtos is not present
|
||||
extern "C" WEAK void __iar_system_Mtxinit(__iar_Rmtx *mutex) {}
|
||||
|
|
@ -725,8 +912,6 @@ extern "C" void __env_unlock( struct _reent *_r )
|
|||
}
|
||||
#endif
|
||||
|
||||
} // namespace mbed
|
||||
|
||||
void *operator new(std::size_t count)
|
||||
{
|
||||
void *buffer = malloc(count);
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ class GCC(mbedToolchain):
|
|||
'c': ["-std=gnu99"],
|
||||
'cxx': ["-std=gnu++98", "-fno-rtti", "-Wvla"],
|
||||
'ld': ["-Wl,--gc-sections", "-Wl,--wrap,main",
|
||||
"-Wl,--wrap,_malloc_r", "-Wl,--wrap,_free_r", "-Wl,--wrap,_realloc_r"],
|
||||
"-Wl,--wrap,_malloc_r", "-Wl,--wrap,_free_r", "-Wl,--wrap,_realloc_r", "-Wl,--wrap,_calloc_r"],
|
||||
}
|
||||
|
||||
def __init__(self, target, options=None, notify=None, macros=None, silent=False, tool_path="", extra_verbose=False):
|
||||
|
|
|
|||
Loading…
Reference in New Issue