feat(heap): Add new heap component

This commit is contained in:
Dong Heng
2018-09-10 13:34:07 +08:00
parent 0982011e37
commit 260da4a6f3
11 changed files with 1041 additions and 0 deletions

View File

@ -0,0 +1,197 @@
// Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD
//
// 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.
#pragma once
#include "sdkconfig.h"
#include <stdint.h>
#include <stddef.h>
#include "esp_err.h"
#include "esp_heap_config.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Get "HEAP_ALIGN_SIZE" bytes aligned data(HEAP_ALIGN(ptr) >= ptr).
*/
#define HEAP_ALIGN(ptr) (((size_t)ptr + (HEAP_ALIGN_SIZE - 1)) & ~(HEAP_ALIGN_SIZE - 1))
#define MALLOC_CAP_32BIT (1 << 1) ///< Memory must allow for aligned 32-bit data accesses
#define MALLOC_CAP_8BIT (1 << 2) ///< Memory must allow for 8-bit data accesses
#define MALLOC_CAP_DMA (1 << 3) ///< Memory must be able to accessed by DMA
#define MEM_BLK_TAG 0x80000000 ///< Mark the memory block used
#define MEM_BLK_TRACE 0x80000000 ///< Mark the memory block traced
#define MEM_HEAD_SIZE sizeof(mem_blk_t) ///< Size of first type memory block
#define MEM2_HEAD_SIZE sizeof(mem2_blk_t) ///< Size of second type memory block
/**
* First type memory block.
*/
typedef struct mem_blk {
struct mem_blk *prev; ///< Point to previous memory block
struct mem_blk *next; ///< Point to next memory block
} mem_blk_t;
/**
* Second type memory block.
*/
typedef struct mem_blk2 {
struct mem_blk2 *prev; ///< Point to previous memory block
struct mem_blk2 *next; ///< Point to next memory block
const char *file; ///< Which "file" allocate the memory block
size_t line; ///< Which "line" allocate the memory block
} mem2_blk_t;
/**
* User region information.
*/
typedef struct heap_region {
void *start_addr; ///< Heap region start address
size_t total_size; ///< Heap region total size by byte
uint32_t caps; ///< Heap capacity
void *free_blk; ///< First free memory block
size_t free_bytes; ///< Current free heap size by byte
size_t min_free_bytes; ///< Minimum free heap size by byte ever
} heap_region_t;
/**
* @brief Get the total free size of all the regions that have the given capabilities
*
* This function takes all regions capable of having the given capabilities allocated in them
* and adds up the free space they have.
*
* @param caps Bitwise OR of MALLOC_CAP_* flags indicating the type of memory
*
* @return Amount of free bytes in the regions
*/
size_t heap_caps_get_free_size(uint32_t caps);
/**
* @brief Get the total minimum free memory of all regions with the given capabilities
*
* This adds all the low water marks of the regions capable of delivering the memory
* with the given capabilities.
*
* @param caps Bitwise OR of MALLOC_CAP_* flags indicating the type of memory
*
* @return Amount of free bytes in the regions
*/
size_t heap_caps_get_minimum_free_size(uint32_t caps);
/**
* @brief Initialize regions of memory to the collection of heaps at runtime.
*
* @param region region table head point
* @param max_num region table size
*/
void esp_heap_caps_init_region(heap_region_t *region, size_t max_num);
/**
* @brief Allocate a chunk of memory which has the given capabilities
*
* Equivalent semantics to libc malloc(), for capability-aware memory.
*
* In SDK, ``malloc(s)`` is equivalent to ``heap_caps_malloc(s, MALLOC_CAP_32BIT)``.
*
* @param size Size, in bytes, of the amount of memory to allocate
* @param caps Bitwise OR of MALLOC_CAP_* flags indicating the type of memory to be returned
*
* @return A pointer to the memory allocated on success, NULL on failure
*/
#define heap_caps_malloc(size, caps) _heap_caps_malloc(size, caps, __FILE__, __LINE__)
void *_heap_caps_malloc(size_t size, uint32_t caps, const char *file, size_t line);
/**
* @brief Free memory previously allocated via heap_caps_(m/c/re/z)alloc().
*
* Equivalent semantics to libc free(), for capability-aware memory.
*
* In SDK, ``free(p)`` is equivalent to ``heap_caps_free(p)``.
*
* @param ptr Pointer to memory previously returned from heap_caps_(m/c/re/z)alloc(). Can be NULL.
*/
#define heap_caps_free(p) _heap_caps_free(p, __FILE__, __LINE__)
void _heap_caps_free(void *ptr, const char *file, size_t line);
/**
* @brief Allocate a chunk of memory which has the given capabilities. The initialized value in the memory is set to zero.
*
* Equivalent semantics to libc calloc(), for capability-aware memory.
*
* In IDF, ``calloc(c, s)`` is equivalent to ``heap_caps_calloc(c, s, MALLOC_CAP_32BIT)``.
*
* @param n Number of continuing chunks of memory to allocate
* @param size Size, in bytes, of a chunk of memory to allocate
* @param caps Bitwise OR of MALLOC_CAP_* flags indicating the type of memory to be returned
*
* @return A pointer to the memory allocated on success, NULL on failure
*/
#define heap_caps_calloc(n, size, caps) _heap_caps_calloc(n, size, caps, __FILE__, __LINE__)
void *_heap_caps_calloc(size_t count, size_t size, uint32_t caps, const char *file, size_t line);
/**
* @brief Reallocate memory previously allocated via heap_caps_(m/c/re/z)alloc().
*
* Equivalent semantics to libc realloc(), for capability-aware memory.
*
* In SDK, ``realloc(p, s)`` is equivalent to ``heap_caps_realloc(p, s, MALLOC_CAP_32BIT)``.
*
* 'caps' parameter can be different to the capabilities that any original 'ptr' was allocated with. In this way,
* realloc can be used to "move" a buffer if necessary to ensure it meets a new set of capabilities.
*
* @param ptr Pointer to previously allocated memory, or NULL for a new allocation.
* @param size Size of the new buffer requested, or 0 to free the buffer.
* @param caps Bitwise OR of MALLOC_CAP_* flags indicating the type of memory desired for the new allocation.
*
* @return Pointer to a new buffer of size 'size' with capabilities 'caps', or NULL if allocation failed.
*/
#define heap_caps_realloc(ptr, size, caps) _heap_caps_realloc(ptr, size, caps, __FILE__, __LINE__)
void *_heap_caps_realloc(void *mem, size_t newsize, uint32_t caps, const char *file, size_t line);
/**
* @brief Allocate a chunk of memory which has the given capabilities. The initialized value in the memory is set to zero.
*
* Equivalent semantics to libc calloc(), for capability-aware memory.
*
* In IDF, ``calloc(c, s)`` is equivalent to ``heap_caps_calloc(c, s, MALLOC_CAP_32BIT)``.
*
* @param n Number of continuing chunks of memory to allocate
* @param size Size, in bytes, of a chunk of memory to allocate
* @param caps Bitwise OR of MALLOC_CAP_* flags indicating the type of memory to be returned
*
* @return A pointer to the memory allocated on success, NULL on failure
*/
#define heap_caps_zalloc(size, caps) _heap_caps_zalloc(size, caps, __FILE__, __LINE__)
void *_heap_caps_zalloc(size_t size, uint32_t caps, const char *file, size_t line);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,31 @@
// Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD
//
// 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.
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Initialize the capability-aware heap allocator.
*
* This is called once in the ESP8266 startup code. Do not call it
* at other times.
*/
void heap_caps_init();
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,82 @@
// Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD
//
// 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.
#pragma once
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
HEAP_TRACE_NONE = 0,
HEAP_TRACE_LEAKS,
} heap_trace_mode_t;
typedef struct {
char buf[1];
} heap_trace_record_t;
/**
* @brief Check if heap trace is on
*
* @return true if on or false
*/
int heap_trace_is_on(void);
/**
* @brief Empty function just for passing compiling some place.
*/
esp_err_t heap_trace_init_standalone(heap_trace_record_t *record_buffer, size_t num_records);
/**
* @brief Start heap tracing. All heap allocations will be traced, until heap_trace_stop() is called.
*
* @param mode Mode for tracing.
* - HEAP_TRACE_LEAKS means only suspected memory leaks are traced. (When memory is freed, the record is removed from the trace buffer.)
* @return
* - ESP_OK Tracing is started.
*/
esp_err_t heap_trace_start(heap_trace_mode_t mode);
/**
* @brief Stop heap tracing.
*
* @return
* - ESP_OK Heap tracing stopped..
*/
esp_err_t heap_trace_stop(void);
/**
* @brief Resume heap tracing which was previously stopped.
*
* @return
* - ESP_ERR_NOT_SUPPORTED Project was compiled without heap tracing enabled in menuconfig.
* - ESP_OK Heap tracing resumed.
*/
esp_err_t heap_trace_resume(void);
/**
* @brief Dump heap trace record data to stdout
*
* @note It is safe to call this function while heap tracing is running, however in HEAP_TRACE_LEAK mode the dump may skip
* entries unless heap tracing is stopped first.
*/
void heap_trace_dump(void);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,154 @@
// Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD
//
// 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.
#pragma once
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
#define _mem_blk_get_ptr(_mem_blk, _offset, _mask) \
((mem_blk_t *)((((uint32_t *)(_mem_blk))[_offset]) & (~_mask)))
#define _mem_blk_set_ptr(_mem_blk, _val, _offset, _mask) \
{ \
uint32_t *__p = (uint32_t *)(_mem_blk); \
uint32_t __bits = __p[_offset] & (_mask); \
\
__p[_offset] = (uint32_t)(_val) | __bits; \
}
#define mem_blk_prev(_mem_blk) _mem_blk_get_ptr(_mem_blk, 0, MEM_BLK_TAG)
#define mem_blk_next(_mem_blk) _mem_blk_get_ptr(_mem_blk, 1, MEM_BLK_TRACE)
#define mem_blk_set_prev(_mem_blk, _prev) _mem_blk_set_ptr(_mem_blk, _prev, 0, MEM_BLK_TAG)
#define mem_blk_set_next(_mem_blk, _next) _mem_blk_set_ptr(_mem_blk, _next, 1, MEM_BLK_TRACE)
static inline size_t mem_blk_head_size(bool trace)
{
return trace ? MEM2_HEAD_SIZE : MEM_HEAD_SIZE;
}
static inline void mem_blk_set_traced(mem2_blk_t *mem_blk, const char *file, size_t line)
{
uint32_t *val = (uint32_t *)mem_blk + 1;
*val |= MEM_BLK_TRACE;
mem_blk->file = file;
mem_blk->line = line;
}
static inline void mem_blk_set_untraced(mem2_blk_t *mem_blk)
{
uint32_t *val = (uint32_t *)mem_blk + 1;
*val &= ~MEM_BLK_TRACE;
}
static inline int mem_blk_is_traced(mem_blk_t *mem_blk)
{
uint32_t *val = (uint32_t *)mem_blk + 1;
return *val & MEM_BLK_TRACE;
}
static inline void mem_blk_set_used(mem_blk_t *mem_blk)
{
uint32_t *val = (uint32_t *)mem_blk;
*val |= MEM_BLK_TAG;
}
static inline void mem_blk_set_unused(mem_blk_t *mem_blk)
{
uint32_t *val = (uint32_t *)mem_blk;
*val &= ~MEM_BLK_TAG;
}
static inline int mem_blk_is_used(mem_blk_t *mem_blk)
{
uint32_t *val = (uint32_t *)mem_blk;
return *val & MEM_BLK_TAG;
}
static inline int mem_blk_is_end(mem_blk_t *mem_blk)
{
return mem_blk_next(mem_blk) == NULL;
}
static inline size_t blk_link_size(mem_blk_t *blk)
{
return (size_t)mem_blk_next(blk) - (size_t)blk;
}
static inline size_t get_blk_region(void *ptr)
{
size_t num;
extern heap_region_t g_heap_region[HEAP_REGIONS_MAX];
for (num = 0; num < HEAP_REGIONS_MAX; num++) {
if ((uint8_t *)ptr > (uint8_t *)g_heap_region[num].start_addr
&& (uint8_t *)ptr < ((uint8_t *)g_heap_region[num].start_addr + g_heap_region[num].total_size)) {
break;
}
}
return num;
}
static inline size_t ptr2memblk_size(size_t size, bool trace)
{
size_t head_size = trace ? MEM2_HEAD_SIZE : MEM_HEAD_SIZE;
return HEAP_ALIGN(size + head_size);
}
static inline bool ptr_is_traced(void *ptr)
{
uint32_t *p = (uint32_t *)ptr - 1;
return p[0] & 0xf0000000 ? (p[0] == 0xffffffff ? true : false) : true;
}
static inline mem_blk_t *ptr2blk(void *ptr, bool trace)
{
size_t head_size = trace ? MEM2_HEAD_SIZE : MEM_HEAD_SIZE;
return (mem_blk_t *)((uint8_t *)ptr - head_size);
}
static inline void *blk2ptr(mem_blk_t *mem_blk, bool trace)
{
size_t head_size = trace ? MEM2_HEAD_SIZE : MEM_HEAD_SIZE;
return (void *)((uint8_t *)mem_blk + head_size);
}
static inline size_t ptr_size(void *ptr)
{
bool trancd = ptr_is_traced(ptr);
mem_blk_t *blk_mem = ptr2blk(ptr, trancd);
size_t size = blk_link_size(blk_mem) - mem_blk_head_size(trancd);
return size;
}
#ifdef __cplusplus
}
#endif