Skip to content

Commit

Permalink
[zephyr] Allow to override default malloc with sys_heap (project-chip…
Browse files Browse the repository at this point in the history
…#16926)

* [zephyr] Allow to override default malloc with sys_heap

Add a setting that overrides default malloc/calloc/realloc/
free functions with custom implementations based on sys_heap
from Zephyr RTOS.

Update DiagnosticDataProvider methods for obtaining the heap
statistics to use the sys_heap statistics when sys_heap-baed
malloc is used. Additionally, fix the DiagnosticDataProvider
implementation using mallinfo() by taking the maximum heap
size into account.

* Restyled by whitespace

* Restyled by clang-format

* Restyled by gn

* Handle mul overflow

* Add separate switch for wrapping malloc/calloc/realloc/free symbols

* Restyled by gn

Co-authored-by: Restyled.io <commits@restyled.io>
  • Loading branch information
2 people authored and chencheung committed Apr 6, 2022
1 parent 56e795f commit 7bf594e
Show file tree
Hide file tree
Showing 9 changed files with 323 additions and 10 deletions.
14 changes: 14 additions & 0 deletions config/nrfconnect/chip-module/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ chip_gn_arg_bool ("chip_error_logging" CONFIG_MATTER_LOG_LE
chip_gn_arg_bool ("chip_progress_logging" CONFIG_MATTER_LOG_LEVEL GREATER_EQUAL 3)
chip_gn_arg_bool ("chip_detail_logging" CONFIG_MATTER_LOG_LEVEL GREATER_EQUAL 4)
chip_gn_arg_bool ("chip_automation_logging" "false")
chip_gn_arg_bool ("chip_malloc_sys_heap" CONFIG_CHIP_MALLOC_SYS_HEAP)

if (CONFIG_CHIP_ROTATING_DEVICE_ID)
chip_gn_arg_bool("chip_enable_rotating_device_id" "true")
Expand Down Expand Up @@ -305,6 +306,19 @@ if (CONFIG_CHIP_LIB_SHELL)
target_link_options(chip INTERFACE -Wl,--whole-archive -lCHIPShell -Wl,--no-whole-archive)
endif()

if (CONFIG_CHIP_MALLOC_SYS_HEAP_OVERRIDE)
target_link_options(chip INTERFACE
-Wl,--wrap=malloc
-Wl,--wrap=calloc
-Wl,--wrap=realloc
-Wl,--wrap=free
-Wl,--wrap=_malloc_r
-Wl,--wrap=_calloc_r
-Wl,--wrap=_realloc_r
-Wl,--wrap=_free_r
)
endif()

target_link_libraries(chip INTERFACE -Wl,--start-group ${CHIP_LIBRARIES} -Wl,--end-group)

add_dependencies(chip chip-gn)
Expand Down
27 changes: 27 additions & 0 deletions config/zephyr/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,33 @@ config CHIP_OPERATIONAL_TIME_SAVE_INTERVAL
precisely operation time in case of device reboot and maximizing flash memory
lifetime.

config CHIP_MALLOC_SYS_HEAP
bool "Memory allocator based on Zephyr sys_heap"
imply SYS_HEAP_RUNTIME_STATS
help
Enable memory allocation functions, imitating with the default malloc,
calloc, realloc and free, based on sys_heap from Zephyr RTOS.

if CHIP_MALLOC_SYS_HEAP

config CHIP_MALLOC_SYS_HEAP_OVERRIDE
bool "Override default allocator with custom one based on Zephyr sys_heap"
default y
help
Replace the default memory allocation functions, such as malloc, calloc,
realloc, free and their reentrant versions, with their counterparts based
on sys_heap from Zephyr RTOS.

config CHIP_MALLOC_SYS_HEAP_SIZE
int "Heap size used by memory allocator based on Zephyr sys_heap"
default 16384 # 16kB
help
This value controls how much of the device RAM is reserved for the heap
used by the memory allocation functions based on sys_heap from Zephyr
RTOS.

endif

config APP_LINK_WITH_CHIP
bool "Link 'app' with Connected Home over IP"
default y
Expand Down
46 changes: 39 additions & 7 deletions src/platform/Zephyr/DiagnosticDataProviderImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,30 @@
#include <lib/support/logging/CHIPLogging.h>
#include <platform/DiagnosticDataProvider.h>
#include <platform/Zephyr/DiagnosticDataProviderImpl.h>
#include <platform/Zephyr/SysHeapMalloc.h>

#include <drivers/hwinfo.h>
#include <sys/util.h>

#ifdef CONFIG_MCUBOOT_IMG_MANAGER
#include <dfu/mcuboot.h>
#endif

#include <malloc.h>

#if CHIP_DEVICE_CONFIG_HEAP_STATISTICS_MALLINFO

#ifdef CONFIG_NEWLIB_LIBC_ALIGNED_HEAP_SIZE
const size_t kMaxHeapSize = CONFIG_NEWLIB_LIBC_ALIGNED_HEAP_SIZE;
#elif defined(CONFIG_NEWLIB_LIBC)
extern char _end[];
const size_t kMaxHeapSize = CONFIG_SRAM_BASE_ADDRESS + KB(CONFIG_SRAM_SIZE) - POINTER_TO_UINT(_end);
#else
#pragma error "Maximum heap size is required but unknown"
#endif

#endif

namespace chip {
namespace DeviceLayer {

Expand Down Expand Up @@ -101,11 +116,15 @@ inline DiagnosticDataProviderImpl::DiagnosticDataProviderImpl() : mBootReason(De

CHIP_ERROR DiagnosticDataProviderImpl::GetCurrentHeapFree(uint64_t & currentHeapFree)
{
#ifdef CONFIG_NEWLIB_LIBC
// This will return the amount of memory which has been allocated from the system, but is not
// used right now. Ideally, this value should be increased by the amount of memory which can
// be allocated from the system, but Zephyr does not expose that number.
currentHeapFree = mallinfo().fordblks;
#ifdef CONFIG_CHIP_MALLOC_SYS_HEAP
Malloc::Stats stats;
ReturnErrorOnFailure(Malloc::GetStats(stats));

currentHeapFree = stats.free;
return CHIP_NO_ERROR;
#elif CHIP_DEVICE_CONFIG_HEAP_STATISTICS_MALLINFO
const auto stats = mallinfo();
currentHeapFree = kMaxHeapSize - stats.arena + stats.fordblks;
return CHIP_NO_ERROR;
#else
return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE;
Expand All @@ -114,7 +133,13 @@ CHIP_ERROR DiagnosticDataProviderImpl::GetCurrentHeapFree(uint64_t & currentHeap

CHIP_ERROR DiagnosticDataProviderImpl::GetCurrentHeapUsed(uint64_t & currentHeapUsed)
{
#ifdef CONFIG_NEWLIB_LIBC
#ifdef CONFIG_CHIP_MALLOC_SYS_HEAP
Malloc::Stats stats;
ReturnErrorOnFailure(Malloc::GetStats(stats));

currentHeapUsed = stats.used;
return CHIP_NO_ERROR;
#elif CHIP_DEVICE_CONFIG_HEAP_STATISTICS_MALLINFO
currentHeapUsed = mallinfo().uordblks;
return CHIP_NO_ERROR;
#else
Expand All @@ -124,7 +149,14 @@ CHIP_ERROR DiagnosticDataProviderImpl::GetCurrentHeapUsed(uint64_t & currentHeap

CHIP_ERROR DiagnosticDataProviderImpl::GetCurrentHeapHighWatermark(uint64_t & currentHeapHighWatermark)
{
#ifdef CONFIG_NEWLIB_LIBC
#ifdef CONFIG_CHIP_MALLOC_SYS_HEAP
Malloc::Stats stats;
ReturnErrorOnFailure(Malloc::GetStats(stats));

// TODO: use the maximum usage once that is implemented in Zephyr
currentHeapHighWatermark = stats.used;
return CHIP_NO_ERROR;
#elif CHIP_DEVICE_CONFIG_HEAP_STATISTICS_MALLINFO
// ARM newlib does not provide a way to obtain the peak heap usage, so for now just return
// the amount of memory allocated from the system which should be an upper bound of the peak
// usage provided that the heap is not very fragmented.
Expand Down
180 changes: 180 additions & 0 deletions src/platform/Zephyr/SysHeapMalloc.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
/*
*
* Copyright (c) 2022 Project CHIP Authors
*
* 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 "SysHeapMalloc.h"

#include <lib/support/CodeUtils.h>
#include <system/SystemError.h>

extern "C" {
#include <init.h>
#include <sys/math_extras.h>
#include <sys/mutex.h>
#include <sys/sys_heap.h>
}

#include <cstdint>
#include <cstring>

// Construct name of the given function wrapped with the `--wrap=symbol` GCC option.
#define WRAP(f) __wrap_##f

using namespace chip;

namespace {

// Alignment of memory blocks returned by malloc.
// Choose the value that guarantees that the returned memory blocks are castable to all built-in types.
constexpr size_t kMallocAlignment = alignof(long long);

uint8_t sHeapMemory[CONFIG_CHIP_MALLOC_SYS_HEAP_SIZE] alignas(kMallocAlignment);
sys_heap sHeap;
SYS_MUTEX_DEFINE(sLock);

// RAII helper for synchronizing access to the common heap.
class LockGuard
{
public:
LockGuard() : mStatus(sys_mutex_lock(&sLock, K_FOREVER)) {}
~LockGuard();

bool Locked() const { return mStatus == 0; }
CHIP_ERROR Error() const { return System::MapErrorZephyr(mStatus); }

private:
const int mStatus;
};

LockGuard::~LockGuard()
{
if (mStatus == 0)
{
sys_mutex_unlock(&sLock);
}
}

int initHeap(const device *)
{
sys_heap_init(&sHeap, sHeapMemory, sizeof(sHeapMemory));
return 0;
}

} // namespace

// Initialize the heap in the POST_KERNEL phase to make sure that it is ready even before
// C++ static constructors are called (which happens prior to the APPLICATION initialization phase).
SYS_INIT(initHeap, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);

namespace chip {
namespace DeviceLayer {
namespace Malloc {

void * Malloc(size_t size)
{
LockGuard lockGuard;

return lockGuard.Locked() ? sys_heap_aligned_alloc(&sHeap, kMallocAlignment, size) : nullptr;
}

void * Calloc(size_t num, size_t size)
{
size_t totalSize;

if (size_mul_overflow(num, size, &totalSize))
{
return nullptr;
}

void * mem = malloc(totalSize);

if (mem)
{
memset(mem, 0, totalSize);
}

return mem;
}

void * Realloc(void * mem, size_t size)
{
LockGuard lockGuard;

return lockGuard.Locked() ? sys_heap_aligned_realloc(&sHeap, mem, kMallocAlignment, size) : nullptr;
}

void Free(void * mem)
{
LockGuard lockGuard;

VerifyOrReturn(lockGuard.Locked());
sys_heap_free(&sHeap, mem);
}

#ifdef CONFIG_SYS_HEAP_RUNTIME_STATS

CHIP_ERROR GetStats(Stats & stats)
{
LockGuard lockGuard;
ReturnErrorOnFailure(lockGuard.Error());

sys_heap_runtime_stats sysHeapStats;
ReturnErrorOnFailure(System::MapErrorZephyr(sys_heap_runtime_stats_get(&sHeap, &sysHeapStats)));

stats.free = sysHeapStats.free_bytes;
stats.used = sysHeapStats.allocated_bytes;

return CHIP_NO_ERROR;
}

#endif // CONFIG_SYS_HEAP_RUNTIME_STATS

} // namespace Malloc
} // namespace DeviceLayer
} // namespace chip

#ifdef CONFIG_CHIP_MALLOC_SYS_HEAP_OVERRIDE

extern "C" {

void * WRAP(malloc)(size_t size) __attribute((alias("_ZN4chip11DeviceLayer6Malloc6MallocEj")));
void * WRAP(calloc)(size_t num, size_t size) __attribute((alias("_ZN4chip11DeviceLayer6Malloc6CallocEjj")));
void * WRAP(realloc)(void * mem, size_t size) __attribute((alias("_ZN4chip11DeviceLayer6Malloc7ReallocEPvj")));
void WRAP(free)(void * mem) __attribute((alias("_ZN4chip11DeviceLayer6Malloc4FreeEPv")));

void * WRAP(_malloc_r)(_reent *, size_t size)
{
return WRAP(malloc)(size);
}

void * WRAP(_calloc_r)(_reent *, size_t num, size_t size)
{
return WRAP(calloc)(num, size);
}

void * WRAP(_realloc_r)(_reent *, void * mem, size_t size)
{
return WRAP(realloc)(mem, size);
}

void WRAP(_free_r)(_reent *, void * mem)
{
WRAP(free)(mem);
}

} // extern "C"

#endif // CONFIG_CHIP_MALLOC_SYS_HEAP_OVERRIDE
40 changes: 40 additions & 0 deletions src/platform/Zephyr/SysHeapMalloc.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
*
* Copyright (c) 2022 Project CHIP Authors
*
* 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 <cstddef>

#include <lib/core/CHIPError.h>

namespace chip {
namespace DeviceLayer {
namespace Malloc {

struct Stats
{
size_t free;
size_t used;
};

void * Malloc(size_t size);
void * Calloc(size_t num, size_t size);
void * Realloc(void * mem, size_t size);
void Free(void * mem);
CHIP_ERROR GetStats(Stats & stats);

} // namespace Malloc
} // namespace DeviceLayer
} // namespace chip
6 changes: 6 additions & 0 deletions src/platform/nrfconnect/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import("//build_overrides/chip.gni")

import("${chip_root}/src/platform/device.gni")
import("${chip_root}/src/platform/nrfconnect/args.gni")

assert(chip_device_platform == "nrfconnect")

Expand All @@ -28,6 +29,7 @@ static_library("nrfconnect") {
"../Zephyr/KeyValueStoreManagerImpl.cpp",
"../Zephyr/Logging.cpp",
"../Zephyr/PlatformManagerImpl.cpp",
"../Zephyr/SysHeapMalloc.h",
"../Zephyr/SystemTimeSupport.cpp",
"../Zephyr/ZephyrConfig.cpp",
"../Zephyr/ZephyrConfig.h",
Expand Down Expand Up @@ -77,4 +79,8 @@ static_library("nrfconnect") {
"OTAImageProcessorImpl.h",
]
}

if (chip_malloc_sys_heap) {
sources += [ "../Zephyr/SysHeapMalloc.cpp" ]
}
}
Loading

0 comments on commit 7bf594e

Please sign in to comment.