FreeRTOS 小技巧(二):如何診斷內存泄漏

這篇博客用來說明如何在 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) 打印 “泄漏” 字節的總數(已分配但未運行跟蹤時釋放的字節),並以此表示分配的總數。

如果跟蹤緩衝區的大小不足以容納所有發生的分配,則會打印警告。如果看到此警告,請考慮縮短跟蹤時間或增加跟蹤緩衝區中的記錄數。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章