這篇博客用來說明如何在 FreeRTOS 中診斷在哪裏發生了內存泄漏。
1. 診斷內存泄露的步驟
如果你懷疑內存泄漏,則第一步是弄清楚程序的哪一部分正在泄漏內存。使用 xPortGetFreeHeapSize()
或 heap_caps_get_free_size()
來跟蹤在應用程序生命週期裏的內存使用。嘗試將泄漏範圍縮小到單個功能或一系列功能,因爲在這些功能或功能序列中,可用內存總是會不斷的減少。一旦通過上述 API 確定了您認爲正在內存泄漏的代碼段後,你需要進行以下操作來診斷內存泄漏:
-
通過
make menuconifg
項目打開配置菜單,進入Component settings
->Heap Memory Debugging
->Heap tracing
並選擇Standalone
選項(請參閱 CONFIG_HEAP_TRACING_DEST)。 -
在程序開頭調用
heap_trace_init_standalone()
函數,以註冊一個緩衝區,該緩衝區可用於記錄內存跟蹤。 -
調用函數
heap_trace_start()
以開始記錄系統中的所有malloc / free
。你可以在你懷疑內存泄漏的代碼段之前調用此函數。 -
一旦可疑代碼執行完畢,你需要調用
heap_trace_stop()
函數以停止跟蹤。 -
調用該函數
heap_trace_dump()
以轉儲堆跟蹤的結果。
2. 使用示例
以下是相關使用示例(假設你懷疑 do_something_you_suspect_is_leaking()
這段代碼內存泄漏):
#include "esp_heap_trace.h"
#define NUM_RECORDS 100
static heap_trace_record_t trace_record[NUM_RECORDS]; // This buffer must be in internal RAM
...
void app_main()
{
...
ESP_ERROR_CHECK( heap_trace_init_standalone(trace_record, NUM_RECORDS) );
...
}
void some_function()
{
ESP_ERROR_CHECK( heap_trace_start(HEAP_TRACE_LEAKS) );
do_something_you_suspect_is_leaking();
ESP_ERROR_CHECK( heap_trace_stop() );
heap_trace_dump();
...
}
堆跟蹤的輸出 log 如下所示:
2 allocations trace (100 entry buffer)
32 bytes (@ 0x3ffaf214) allocated CPU 0 ccount 0x2e9b7384 caller 0x400d276d:0x400d27c1
0x400d276d: leak_some_memory at /path/to/idf/examples/get-started/blink/main/./blink.c:27
0x400d27c1: blink_task at /path/to/idf/examples/get-started/blink/main/./blink.c:52
8 bytes (@ 0x3ffaf804) allocated CPU 0 ccount 0x2e9b79c0 caller 0x400d2776:0x400d27c1
0x400d2776: leak_some_memory at /path/to/idf/examples/get-started/blink/main/./blink.c:29
0x400d27c1: blink_task at /path/to/idf/examples/get-started/blink/main/./blink.c:52
40 bytes 'leaked' in trace (2 allocations)
total allocations 2 total frees 0
注:以上示例輸出使用IDF Monitor自動將PC地址解碼爲其源文件和行號。)
然後你可以分析堆跟蹤的輸出 log,第一行 2 allocations trace (100 entry buffer)
指示緩衝區中有多少個分配條目(與其總大小相比)。
此外,在HEAP_TRACE_LEAKS
模式下,對於尚未釋放的每個跟蹤的內存分配,打印一行:
-
XX bytes
是分配的字節數 -
@ 0x...
是從malloc / calloc
返回的堆地址。 -
CPU x
是分配時運行的 CPU(0 或 1)。 -
ccount 0x...
是分配爲 mode 時的 CCOUNT(CPU 週期計數)寄存器值。CPU 0 與CPU 1 有所不同。 -
caller 0x...
給出對malloc()/ free()
的調用的調用堆棧,作爲 PC 地址的列表。可以將它們解碼爲源文件和行號,如上所示。
可以在 make menuconfig
打開的項目配置菜單的 Heap Memory Debugging
-> Enable heap tracing
-> Heap tracing stack depth
下配置爲每個跟蹤條目記錄的調用堆棧的深度。每次分配最多可以記錄 10 個堆棧幀(默認爲 2)。每個附加的堆棧幀將每個 heap_trace_record_t
記錄的內存使用量增加 8 個字節。
最後,40 bytes leaked in trace (2 allocations)
打印 “泄漏” 字節的總數(已分配但未運行跟蹤時釋放的字節),並以此表示分配的總數。
如果跟蹤緩衝區的大小不足以容納所有發生的分配,則會打印警告。如果看到此警告,請考慮縮短跟蹤時間或增加跟蹤緩衝區中的記錄數。