Runtime memory tracing
Running out of memory is a common problem with resource constrained systems such as the MCUs on which Arm Mbed OS runs. When faced with an out of memory error, you often need to understand how your software uses dynamic memory. The runtime memory tracer in Mbed OS is the tool that shows the runtime memory allocation patterns of your software: which parts of the code allocate and free memory and how much memory they need.
Using the memory tracer
The memory tracer is not enabled by default. To enable it, you need to enable the memory-tracing-enabled setting in the Mbed OS platform configuration options. We recommend doing this by adding it to your mbed_app.json:
{
"target_overrides": {
"*": {
"platform.memory-tracing-enabled": true
}
}
}
Tip: See the documentation of the Arm Mbed configuration system for more details about mbed_app.json.
After it is enabled, the memory tracer intercepts the calls to the standard allocation functions (malloc, realloc, calloc and free). It invokes a user supplied callback each time one of these functions is called. To let the tracer know which callback it needs to invoke, call mbed_mem_trace_set_callback(callback_function_name) as early as possible (preferably at the beginning of your main function). You can find the full documentation of the callback function in the memory tracer header file. The tracer supplies a default callback function (mbed_mem_trace_default_callback) that outputs trace data on the Mbed console (using printf). For each memory operation, the callback outputs a line that begins with #<op>:<0xresult>;<0xcaller>-:
- op identifies the memory operation (
mformalloc,rforrealloc,cforcallocandfforfree). - result (base 16) is the result returned by the memory operation. This is always 0 for
freebecausefreedoesn't return anything. - caller (base 16) is the address in the code where the memory operation was called.
The rest of the output depends on the operation being traced:
- For
malloc:size, wheresizeis the original argument tomalloc. - For
realloc:0xptr;size, whereptr(base 16) andsizeare the original arguments torealloc. - For
calloc:nmemb;size, wherenmembandsizeare the original arguments tocalloc. - For
free:0xptr, whereptr(base 16) is the original argument tofree.
Examples:
#m:0x20003240;0x600d-50encodes amallocthat returned 0x20003240. It was called by the instruction at 0x600D with thesizeargument equal to 50.#f:0x0;0x602f-0x20003240encodes afreethat was called by the instruction at 0x602f with theptrargument equal to 0x20003240.
Find the source of the default callback here. Besides being useful in itself, it can also serve as a template for user defined callback functions.
Tip: Find the full documentation of the callback function in the memory tracer header file.
Example
A simple code example that uses the memory tracer on a K64F board:
/*
* Copyright (c) 2020 Arm Limited and affiliates.
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdlib.h>
#include "mbed.h"
#include "mbed_mem_trace.h"
int main()
{
mbed_mem_trace_set_callback(mbed_mem_trace_default_callback);
while (true) {
void *p = malloc(50);
printf("50B allocated at %p\n", p);
ThisThread::sleep_for(500);
free(p);
printf("50B freed at %p\n\n", p);
}
}
It outputs the following trace:
#m:0x20003080;0x182f-50
#f:0x0;0x183f-0x20003080
#m:0x20003080;0x182f-50
#f:0x0;0x183f-0x20003080
#m:0x20003080;0x182f-50
#f:0x0;0x183f-0x20003080
...
Limitations
- The tracer doesn't handle nested calls of the memory functions. For example, if you call
reallocand the implementation ofrealloccallsmallocinternally, the call tomallocis not traced. - The caller argument of the callback function isn't always reliable. It doesn't work at all on some toolchains, and it might output erroneous data on others.