blob: fbd7ba09fc2493b9a09b1c281d1823344d0d7336 [file] [log] [blame]
/*
*
* 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 <zephyr/init.h>
#include <zephyr/sys/math_extras.h>
#include <zephyr/sys/mutex.h>
#include <zephyr/sys/sys_heap.h>
}
#include <cstdint>
#include <cstring>
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 InitSysHeapMalloc()
{
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(InitSysHeapMalloc, 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_memory_stats sysHeapStats;
ReturnErrorOnFailure(System::MapErrorZephyr(sys_heap_runtime_stats_get(&sHeap, &sysHeapStats)));
stats.free = sysHeapStats.free_bytes;
stats.used = sysHeapStats.allocated_bytes;
stats.maxUsed = sysHeapStats.max_allocated_bytes;
return CHIP_NO_ERROR;
}
void ResetMaxStats()
{
(void) sys_heap_runtime_stats_reset_max(&sHeap);
}
#endif // CONFIG_SYS_HEAP_RUNTIME_STATS
} // namespace Malloc
} // namespace DeviceLayer
} // namespace chip
#ifdef CONFIG_CHIP_MALLOC_SYS_HEAP_OVERRIDE
extern "C" {
// Construct the name of a function wrapped with the `--wrap=symbol` GCC option.
#define WRAP(f) __wrap_##f
// Define a function as an alias of another function.
#define ALIAS(f) __attribute((alias(f)))
// Mark a function as externally visible so that it is not optimized-away even
// if LTO or whole-program optimization is enabled.
#define EXTERNALLY_VISIBLE __attribute((externally_visible))
EXTERNALLY_VISIBLE void * WRAP(malloc)(size_t size) ALIAS("_ZN4chip11DeviceLayer6Malloc6MallocEj");
EXTERNALLY_VISIBLE void * WRAP(calloc)(size_t num, size_t size) ALIAS("_ZN4chip11DeviceLayer6Malloc6CallocEjj");
EXTERNALLY_VISIBLE void * WRAP(realloc)(void * mem, size_t size) ALIAS("_ZN4chip11DeviceLayer6Malloc7ReallocEPvj");
EXTERNALLY_VISIBLE void WRAP(free)(void * mem) ALIAS("_ZN4chip11DeviceLayer6Malloc4FreeEPv");
EXTERNALLY_VISIBLE void * WRAP(_malloc_r)(_reent *, size_t size)
{
return WRAP(malloc)(size);
}
EXTERNALLY_VISIBLE void * WRAP(_calloc_r)(_reent *, size_t num, size_t size)
{
return WRAP(calloc)(num, size);
}
EXTERNALLY_VISIBLE void * WRAP(_realloc_r)(_reent *, void * mem, size_t size)
{
return WRAP(realloc)(mem, size);
}
EXTERNALLY_VISIBLE void WRAP(_free_r)(_reent *, void * mem)
{
WRAP(free)(mem);
}
} // extern "C"
#endif // CONFIG_CHIP_MALLOC_SYS_HEAP_OVERRIDE