Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[zephyr] Allow to override default malloc with sys_heap #16926

Merged
merged 7 commits into from
Apr 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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