Embedded SDK
Embedded SDK
Loading...
Searching...
No Matches
Dynamic memory allocator

Introduction

This page describes the Dynamic Memory Allocator available for Applications.

Basically, this allocator behaves like any memory allocator in usual OS. The main difference is that it can be instantiated, thanks to its initialization function: mem_init()

This function takes as parameters

  • a pointer to a buffer that will be used both as context for the Allocator and for dynamic chunks,
  • and the size of this buffer

It returns a memory context (mem_ctx_t) that will be used by other functions

Then, chunks can be allocated with mem_alloc() and released with mem_free()

Both of these function take the memory context as first parameter.

High-Level Memory Utilities

For application developers, a set of high-level wrapper functions is provided in app_mem_utils.h. These functions simplify memory management by hiding the memory context and providing additional safety features.

Initialization

Initialize the application heap with mem_utils_init():

static uint32_t heap_buffer[2048]; // 8KB heap
if (!mem_utils_init(heap_buffer, sizeof(heap_buffer))) {
// Initialization failed
}
bool mem_utils_init(void *heap_start, size_t heap_size)
Initializes the App heap buffer.

Basic Allocation

Use APP_MEM_ALLOC() and APP_MEM_FREE() for simple allocations:

void *ptr = APP_MEM_ALLOC(128);
if (ptr != NULL) {
// Use the allocated memory
}
#define APP_MEM_FREE(ptr)
#define APP_MEM_ALLOC(size)

Buffer Management

For buffers that need reallocation or safe cleanup, use APP_MEM_CALLOC() and mem_utils_buffer_cleanup(). The allocated buffers are always zero-initialized. The cleanup function frees the buffer and sets the pointer to NULL to avoid dangling pointers.

void *buffer = NULL;
// Allocate zero-initialized buffer
if (APP_MEM_CALLOC(&buffer, 256)) {
// Buffer is ready to use and zeroed
// Reallocate to different size
APP_MEM_CALLOC(&buffer, 512); // Old buffer freed automatically
// Safe cleanup (frees and sets buffer = NULL)
mem_utils_buffer_cleanup(&buffer);
}
#define APP_MEM_CALLOC(ptr, size)

String Operations

Duplicate strings with APP_MEM_STRDUP():

const char *original = "Hello, World!";
char *copy = APP_MEM_STRDUP(original);
if (copy != NULL) {
// Use the duplicated string
APP_MEM_FREE(copy);
}
#define APP_MEM_STRDUP(ptr)

Memory Profiling

The allocator supports memory profiling to detect leaks and track allocations during development.

Enabling Profiling

Compile your application with the flag HAVE_MEMORY_PROFILING.

When profiling is enabled, all allocation and deallocation operations are logged with file and line information.

Analyzing Profiling Data

Use the tools/valground.py script to analyze memory profiling logs from Speculos:

# Run tests and pipe output to valground.py
pytest --device nanosp -s -k test_name 2>&1 | ./tools/valground.py
# Quiet mode (only errors and summary)
pytest --device nanosp -s 2>&1 | ./tools/valground.py

The tool detects:

  • Memory leaks: Allocated blocks never freed
  • Double free errors: Freeing the same pointer twice
  • Invalid free: Freeing unallocated pointers
  • Persistent allocations: Blocks that persist across test boundaries
  • Memory statistics: Total/max allocation, heap utilization percentage

Example Output

[1/5] - test_basic_alloc PASSED
=== Summary ===
Total overtime = 512 bytes
Max overtime = 256 bytes (12.50% full)
Allocations = 8
No memory leak detected, congrats!
=== Global ===
Higher Max overtime = 256 bytes

Exit code is 0 if no errors detected, 1 if leaks or free errors found.

Persistent Allocations

Persistent allocations are those that are intentionally kept without being freed (mem_buffer_persistent()). Such buffers will not be reported as leaks.

void *persistent_buffer = NULL;
bool success;
success = mem_buffer_persistent(&persistent_buffer, 1024);
// This allocation will be flagged in profiling reports
// but won't be counted as a leak

This is useful for long-lived buffers that are intentionally kept across multiple operations.