# 堆内存调试

[English]

## 概述

ESP-IDF 集成了用于请求 堆内存信息、堆内存损坏检测、内核地址消毒器 (KASAN) 和 堆内存跟踪 的工具，有助于跟踪内存相关错误。

有关堆内存分配器的基本信息，请参阅 堆内存分配。

## 堆内存信息

要获取堆内存状态的相关信息，请调用以下函数：

- `heap_caps_get_free_size()` 返回不同属性内存的当前空闲内存。

- `heap_caps_get_largest_free_block()` 返回堆中最大的空闲块，也是当前可分配的最大内存块。跟踪此值并将其与总空闲堆对比，可以检测堆碎片化情况。

- `heap_caps_get_minimum_free_size()` 可以跟踪堆启动以来的“低水位”。

- `heap_caps_get_info()` 返回一个 `multi_heap_info_t` 结构体，包含上述函数的信息，以及一些额外的特定堆内存数据（分配数量等）。

- `heap_caps_print_heap_info()` 将 `heap_caps_get_info()` 返回的信息摘要打印到标准输出。

- `heap_caps_dump()` 和 `heap_caps_dump_all()` 输出堆中每个内存块结构的详细信息。注意，这可能会产生大量输出。

## 堆内存分配与释放钩子函数

你可以使用堆内存分配和释放检测钩子，接收每次成功分配或释放堆内存操作的通知：

- 定义 `esp_heap_trace_alloc_hook()` 获取堆内存分配操作成功的提示。

- 定义 `esp_heap_trace_free_hook()` 获取堆内存释放操作成功的提示。

要启用此功能，请设置 CONFIG_HEAP_USE_HOOKS 选项。`esp_heap_trace_alloc_hook()` 和 `esp_heap_trace_free_hook()` 具有弱声明（即 `__attribute__((weak))`），因此无需为这两个钩子提供声明。鉴于从 ISR 中分配和释放堆内存在技术上是可行的（但强烈不建议），`esp_heap_trace_alloc_hook()` 和 `esp_heap_trace_free_hook()` 可能会从 ISR 中调用。

不建议在钩子函数中执行（或调用 API 函数执行）阻塞操作或堆内存分配与释放。一般而言，最好保持代码简洁，避免在钩子函数中进行复杂计算。

要定义堆内存分配及释放钩子，请参阅如下示例:

    #include "esp_heap_caps.h"

    void esp_heap_trace_alloc_hook(void* ptr, size_t size, uint32_t caps)
    {
      ...
    }
    void esp_heap_trace_free_hook(void* ptr)
    {
      ...
    }

    void app_main()
    {
      ...
    }

## 内存分配失败钩子

用户可以使用 `heap_caps_register_failed_alloc_callback()` 注册回调函数，每次内存分配操作失败时都会调用该函数。

此外，若启用 CONFIG_HEAP_ABORT_WHEN_ALLOCATION_FAILS 选项，可以在任何分配操作失败时，自动中止系统。

要注册内存分配失败的回调函数，请参阅如下示例：

    #include "esp_heap_caps.h"

    void heap_caps_alloc_failed_hook(size_t requested_size, uint32_t caps, const char *function_name)
    {
      printf("%s was called but failed to allocate %d bytes with 0x%X capabilities. \n",function_name, requested_size, caps);
    }

    void app_main()
    {
        ...
        esp_err_t error = heap_caps_register_failed_alloc_callback(heap_caps_alloc_failed_hook);
        ...
        void *ptr = heap_caps_malloc(allocation_size, MALLOC_CAP_DEFAULT);
        ...
    }

## 检测堆内存损坏

堆内存损坏检测可检测到各类堆内存错误，包括：

- 越界写入和缓冲区溢出

- 对已释放内存的写操作

- 对已释放或未初始化内存的读操作

堆内存损坏检测共有三个级别，在下方列表中，后一级别比前一级别提供更精细的检测：

- 基本模式（无 canary 标记）

- 轻量级模式

- 全面检测模式

### 断言

如 heap/multi_heap.c 等堆的实现方式包含许多断言，堆内存损坏则断言失败。为高效检测堆内存损坏，请确保在项目配置中通过 CONFIG_COMPILER_OPTIMIZATION_ASSERTION_LEVEL 选项启用断言。

如果堆完整性断言失败，将打印一行类似 `CORRUPT HEAP: multi_heap.c:225 detected at 0x3ffbb71c` 的内容，打印的内存地址即内容损坏的堆结构地址。

调用 `heap_caps_check_integrity_all()` 或相关函数可手动检测堆的完整性。该函数可以检测所有指定的堆内存是否完整，在禁用断言时仍可生效。若此完整性检测发现错误，将打印相应错误及损坏的堆结构地址。

### 定位堆内存损坏

内存损坏可能是最难定位和修复的错误类型之一，因为导致内存损坏的原因可能与问题的表现毫不相干。以下是有关定位堆内存损坏的一些提示：

- 如果系统崩溃并提示 `CORRUPT HEAP:`，打印信息中通常包含栈跟踪，但此栈跟踪往往无效，因为系统会在检测到堆内存损坏后崩溃，但实际的损坏通常发生在其他位置，且损坏时间早于系统发现的时间。

- 将堆内存调试 配置项 级别增加到“轻度影响”或“全面”可以得到更准确的信息，定位首个内存损坏的地址。

- 在代码中定期调用 `heap_caps_check_integrity_all()` 或 `heap_caps_check_integrity_addr()` 可以定位内存损坏发生的确切时间。可以反复调整检测函数位置，以定位导致堆内存损坏的代码块。

- 根据损坏的内存地址，按照 JTAG 调试 在此地址上设置监视点，并在写入时使 CPU 暂停。

- 如果没有 JTAG，但大致了解损坏发生的时间，则可以通过 `esp_cpu_set_watchpoint()` 在软件中提前设置监视点，触发监视点将导致致命错误。函数使用示例为 `esp_cpu_set_watchpoint(0, (void *)addr, 4, ESP_WATCHPOINT_STORE)`。注意，监视点在各个 CPU 独立存在，并且仅设置在当前运行的 CPU 上，因此，若无法确定哪个 CPU 破坏了内存，则需要在两个 CPU 上分别调用此函数。

- 对于缓冲区溢出，以 `HEAP_TRACE_ALL` 模式进行 堆内存跟踪 可以看到哪些调用函数正在从堆中分配哪些地址，详情请参阅 堆内存跟踪定位堆内存损坏。如果可以找到在已损坏地址的前一地址分配内存的函数，这些函数很可能就是使缓冲区溢出的函数。

- 调用 `heap_caps_dump()` 或 `heap_caps_dump_all()` 提示损坏区域周围堆块的情况，并了解哪些堆块可能已经溢出或下溢等。

### 配置项

暂时提高堆内存损坏检测级别，可以进一步获取有关堆内存损坏错误的详细信息。

在项目配置菜单中，可以在 `Component config` 下找到 `Heap memory debugging` 菜单，其中的 CONFIG_HEAP_CORRUPTION_DETECTION 选项可以设置为以下三种级别：

#### 基本模式（无 canary 标记）

此为默认级别，默认情况下，不会启用任何特殊的堆内存损坏检测功能。但会启用提供的断言。如果堆的任何内部数据结构出现覆盖或损坏，就会打印出一个堆内存损坏错误。这通常表示缓冲区溢出或越界写入。

启用断言时，如果出现重复释放相同内存的情况（即双重释放），则会触发断言。

在基本模式调用 `heap_caps_check_integrity()`，可以检查所有堆结构的完整性，并在出错时打印错误信息。

#### 轻量级模式

此级别包含“基本模式”中的所有检测功能。此外，每个分配的内存块都会在头部和尾部添加 canary 字节进行“污染”标记。如果应用程序写入了这些 canary 字节，则会被视为已损坏，导致完整性检查失败。

头 canary 字的值为 `0xABBA1234` （按字节顺序为 `3412BAAB`），尾 canary 字的值为 `0xBAAD5678` （按字节顺序为 `7856ADBA`）。

基本模式下的堆内存损坏检测可以检测到大多数越界写入，在检测到错误前的越界字节数取决于堆属性。但轻量级模式更精确，可以检测到单个字节的越界写入。

启用轻量级模式检测会增加内存使用量，因为每次内存分配都会使用额外的元数据字节。

在轻量级模式下，每次调用 `heap_caps_free()` 时，都会检查要释放的缓冲区头尾 canary 字节是否匹配预期值。

调用 `heap_caps_check_integrity()` 或 `heap_caps_check_integrity_all()` 时，会检查所有已分配的堆内存块的 canary 字节是否匹配预期值。

以上两种情况检查的是，在缓冲区返回给用户之前，已分配块的前 4 个字节是否为 `0xABBA1234`，以及在缓冲区返回给用户之后，最后 4 个字节是否为 `0xBAAD5678`。

如果检查到字节与上述值不同，通常表示缓冲区越界或下溢。其中越界表示在写入内存时，写入的数据超过了所分配内存的大小，导致写入到了未分配的内存区域；下溢表示在读取内存时，读取的数据超出了所分配内存的范围，读取了未分配的内存区域的数据。

#### 全面检测模式

此级别包含了轻量级模式的检测功能，此外还会检查未初始化访问和使用已释放内存产生的错误。此模式会将所有新分配的内存填充为 `0xCE`，将所有已释放的内存填充为 `0xFE`。

启用全面检测模式会对运行性能产生实质影响，因为每次 `heap_caps_malloc()` 或 `heap_caps_free()` 操作完成时，都需要将所有内存设置为分配模式，并检查内存。但是，此模式更容易检测到其他方式难以发现的内存损坏错误。建议只在调试时启用此模式，请勿在生产环境中启用。

在调用 `heap_caps_check_integrity()` 或 `heap_caps_check_integrity_all()` 时，还会对已分配和释放模式（分别为 `0xCE` 和 `0xFE`）进行检查。

##### 全面检测模式下程序崩溃

全面检测模式下，如果应用程序在读取或写入与 `0xCECECECE` 相关地址时崩溃，表示它读取了未初始化内存。此时，应修改应用程序，使用 `heap_caps_calloc()` 将内存清零，或在使用前初始化内存。在栈分配的自动变量中也可能存在 `0xCECECECE` 的值，因为 ESP-IDF 中的大多数任务栈最初由堆分配，而在 C 中，栈内存默认未初始化。

如果应用程序崩溃，且异常寄存器转储指示某些地址或值为 `0xFEFEFEFE`，表示它读取了已释放的堆内存。此时，应修改应用程序，避免访问已释放的堆内存。

调用 `heap_caps_malloc()` 或 `heap_caps_realloc()` 时，如果在已释放的内存中找到了不同于 `0xFEFEFEFE` 的值，将导致应用程序崩溃。这表示应用程序写入了已经释放的内存，从而产生错误。

##### 全面检测模式下手动堆内存检测

调用 `heap_caps_check_integrity()` 或 `heap_caps_check_integrity_all()` 会打印与 `0xFEFEFEFE`、`0xABBA1234`、或 `0xBAAD5678` 相关的错误。在不同情况下，检测器均会检测给定模式，若未找到，则输出相应错误：

- 对于已释放的堆内存块，检测器会检查是否所有字节都设置为 `0xFE`，检测到任何其他值都表示错误写入了已释放内存。

- 对于已分配的堆内存块，检测器的检查模式与轻量级模式相同，即在每个分配的缓冲区头部和尾部检查 canary 字节 `0xABBA1234` 和 `0xBAAD5678`，检测到任何其他字节都表示缓冲区越界或下溢。

## 内核地址消毒器 (KASAN)

KASAN 是一种由编译器辅助的堆内存和 DRAM 内存安全检查工具。启用后，GCC 会为内存加载和存储操作插入运行时检查，对照影子内存区域进行校验。一旦发生违规访问（缓冲区溢出、下溢、释放后使用等），会在访问发生处立即报告。

启用 CONFIG_COMPILER_KASAN 即可使用该功能。该选项目前标记为实验性功能，需先启用 CONFIG_IDF_EXPERIMENTAL_FEATURES。

KASAN 在开发和调试阶段最为有用：

- 通过可配置的分配红区检测堆内存越界访问（参见 CONFIG_KASAN_HEAP_REDZONE_SIZE）

- 启用已释放内存块隔离队列后，可捕获释放后使用问题（参见 CONFIG_KASAN_QUARANTINE_SIZE）

- 会为大多数应用程序和组件代码插桩；底层 HAL/ROM/bootloader 代码会被自动排除

需要权衡的方面：

- 插桩组件的代码大小通常会增加 1.5–3 倍

- 影子内存会占用约 42–64 KiB 的内部 DRAM（取决于目标芯片）

- 运行时开销较大，请勿在量产固件中启用

KASAN 使用自己的堆内存钩子和红区方案。请勿同时启用堆内存毒化功能，应将 CONFIG_HEAP_CORRUPTION_DETECTION 保持为 `Basic (no poisoning)`（默认值）。

如需进行人为故障注入和回归测试，请参阅 `tools/test_apps/system/kasan_test` 下的 `kasan_test` 应用程序。

## 堆任务跟踪

可以通过 CONFIG_HEAP_TASK_TRACKING 启用堆任务跟踪功能。

该功能允许用户跟踪自启动以来每个任务的堆内存使用情况，并提供一系列统计信息，这些信息可以通过 getter 函数获取，或直接输出到用户指定的流中。此功能有助于识别内存使用模式和潜在的内存泄漏。

用户还可以启用 CONFIG_HEAP_TRACK_DELETED_TASKS，以便在任务被删除后仍然保留其统计信息。

备注

请注意，堆任务跟踪无法检测静态分配的任务是否被删除。因此，在阅读下文时需注意，静态分配的任务在堆任务跟踪功能范围内始终被视为存活状态。

需要特别说明的是，除调试目的外，强烈不建议将该功能用于其他用途，原因如下：

- 跟踪每个任务的分配并存储统计信息会占用较大 RAM。

- 每次内存分配和释放操作所需的额外数据处理会大幅降低堆分配器的整体性能。

备注

在统计信息输出或访问时不会显示堆任务跟踪功能自身分配的内存。

### 统计信息的结构

堆任务跟踪功能将每个任务的统计信息分为三个层级：

- 任务级统计信息

- 堆级统计信息

- 分配级统计信息

任务级统计信息包括以下内容：

- 任务名称

- 任务句柄

- 任务状态（运行中或已删除）

- 任务的峰值内存使用量（任务生命周期内使用的最大内存）

- 任务的当前内存使用量

- 该任务分配过内存的堆数量

堆级统计信息针对每个被该任务使用的堆，包含以下内容：

- 堆名称

- 堆的内存分配能力（不含优先级）

- 堆的总大小

- 该任务在该堆上的当前使用量

- 该任务在该堆上的峰值使用量

- 该任务在该堆上的分配次数

分配级统计信息针对该任务在该堆上的每次分配，包含以下内容：

- 分配的地址

- 分配的大小

### 输出统计信息

`heap_caps_print_single_task_stat_overview()` API 可将指定任务的堆使用情况概览输出到指定流。

    ┌────────────────────┬─────────┬──────────────────────┬───────────────────┬─────────────────┐
    │ TASK               │ STATUS  │ CURRENT MEMORY USAGE │ PEAK MEMORY USAGE │ TOTAL HEAP USED │
    ├────────────────────┼─────────┼──────────────────────┼───────────────────┼─────────────────┤
    │          task_name │ ALIVE   │                    0 │              7152 │               1 │
    └────────────────────┴─────────┴──────────────────────┴───────────────────┴─────────────────┘

`heap_caps_print_all_task_stat_overview()` 可输出所有任务（若启用 CONFIG_HEAP_TRACK_DELETED_TASKS，则包括已删除任务）的堆使用概览。

    ┌────────────────────┬─────────┬──────────────────────┬───────────────────┬─────────────────┐
    │ TASK               │ STATUS  │ CURRENT MEMORY USAGE │ PEAK MEMORY USAGE │ TOTAL HEAP USED │
    ├────────────────────┼─────────┼──────────────────────┼───────────────────┼─────────────────┤
    │          task_name │ DELETED │                11392 │             11616 │               1 │
    │    other_task_name │ ALIVE   │                    0 │              9408 │               2 │
    │               main │ ALIVE   │                 3860 │              7412 │               2 │
    │               ipc1 │ ALIVE   │                   32 │                44 │               1 │
    │               ipc0 │ ALIVE   │                10080 │             10092 │               1 │
    │      Pre-scheduler │ ALIVE   │                 2236 │              2236 │               1 │
    └────────────────────┴─────────┴──────────────────────┴───────────────────┴─────────────────┘

备注

名为 "Pre-scheduler" 的任务表示调度器启动前发生的内存分配。这并非实际任务，因此 "status" 字段（显示为 "ALIVE"）没有实际意义，可忽略。

使用 `heap_caps_print_single_task_stat()` 可输出指定任务的完整统计信息，或使用 `heap_caps_print_all_task_stat()` 输出所有任务的统计信息：

    [...]
    ├ ALIVE: main, CURRENT MEMORY USAGE 308, PEAK MEMORY USAGE 7412, TOTAL HEAP USED 2:
    │    ├ HEAP: RAM, CAPS: 0x0010580e, SIZE: 344400, USAGE: CURRENT 220 (0%), PEAK 220 (0%), ALLOC COUNT: 2
    │    │    ├ ALLOC 0x3fc99024, SIZE 88
    │    │    ├ ALLOC 0x3fc99124, SIZE 132
    │    └ HEAP: RAM, CAPS: 0x0010580e, SIZE: 22308, USAGE: CURRENT 88 (0%), PEAK 7192 (32%), ALLOC COUNT: 5
    │         ├ ALLOC 0x3fce99f8, SIZE 20
    │         ├ ALLOC 0x3fce9a10, SIZE 12
    │         ├ ALLOC 0x3fce9a20, SIZE 16
    │         ├ ALLOC 0x3fce9a34, SIZE 20
    │         ├ ALLOC 0x3fce9a4c, SIZE 20
    [...]
    └ ALIVE: Pre-scheduler, CURRENT MEMORY USAGE 2236, PEAK MEMORY USAGE 2236, TOTAL HEAP USED 1:
        └ HEAP: RAM, CAPS: 0x0010580e, SIZE: 344400, USAGE: CURRENT 2236 (0%), PEAK 2236 (0%), ALLOC COUNT: 11
              ├ ALLOC 0x3fc95cb0, SIZE 164
              ├ ALLOC 0x3fc95dd8, SIZE 12
              ├ ALLOC 0x3fc95dfc, SIZE 12
              ├ ALLOC 0x3fc95e20, SIZE 16
              ├ ALLOC 0x3fc95e48, SIZE 24
              ├ ALLOC 0x3fc95e78, SIZE 88
              ├ ALLOC 0x3fc95ee8, SIZE 88
              ├ ALLOC 0x3fc95f58, SIZE 88
              ├ ALLOC 0x3fc95fc8, SIZE 88
              ├ ALLOC 0x3fc96038, SIZE 1312
              ├ ALLOC 0x3fc96570, SIZE 344

备注

为了便于阅读，上述输出内容有删减（见 "[...]"），仅展示了 main 任务和 Pre-scheduler 任务的统计信息。此处演示了示调用 `heap_caps_print_all_task_stat()` （或 `heap_caps_print_single_task_stat()`）API 时的输出内容。

备注

本节所述 API 的详细用法可参考 system/heap_task_tracking/basic。

### 获取统计信息

用户可使用 `heap_caps_get_single_task_stat()` 获取指定任务的信息。通过该 API 获取的信息与 `heap_caps_print_single_task_stat()` 的输出内容一致。

用户可使用 `heap_caps_get_all_task_stat()` 获取所有任务（若启用 CONFIG_HEAP_TRACK_DELETED_TASKS，则包括已删除任务）的统计信息概览。通过该 API 获取的信息与 `heap_caps_print_all_task_stat()` 的输出内容一致。

每个 getter 函数都需要一个指向数据结构的指针，该结构用于堆任务跟踪收集指定任务（或所有任务）的统计信息。该数据结构包含指向数组的指针，用户可以选择静态或动态分配这些数组。

由于难以预估用于存储信息的数组大小（如每个任务的分配次数、每个任务使用的堆数量、自启动以来创建的任务总数等），堆任务跟踪还提供了 `heap_caps_alloc_single_task_stat_arrays()` （或 `heap_caps_alloc_all_task_stat_arrays()`）来动态分配这些数组所需的内存。

同时，堆任务跟踪还提供 `heap_caps_free_single_task_stat_arrays()` （或 `heap_caps_free_all_task_stat_arrays()`）用于释放通过上述 API 动态分配的内存。

备注

本节所述 API 的详细用法可参考 system/heap_task_tracking/advanced。

## 堆内存跟踪

堆内存跟踪支持跟踪用于分配或释放内存的代码，且支持以下两种跟踪模式：

- 独立模式。此模式下，跟踪数据保存在设备上（因此收集的信息大小受指定缓冲区限制），并由设备上的代码完成分析。部分 API 可访问和转储收集的信息。

- 主机模式。此模式不受独立模式所受限制，其跟踪数据使用 app_trace 库通过 JTAG 连接发送到主机，随后使用特殊工具完成分析。

堆内存跟踪具有以下两种功能：

- 泄漏检测：检测已分配但未释放的内存。

- 堆内存使用分析：显示在跟踪运行期间所有分配或释放内存的函数。

### 如何判断内存泄漏

如果怀疑存在内存泄漏，首先要找出程序中存在泄漏的部分。调用 `heap_caps_get_free_size()` 或 堆内存信息 中的其他相关函数，跟踪应用程序的内存使用情况，尝试将泄漏范围缩小到某个或一系列空闲内存始终减少而没有恢复的函数。

### 独立模式

确定存在泄漏的代码后，请执行以下步骤：

- 启用 CONFIG_HEAP_TRACING_DEST 选项。

- 在程序早期调用函数 `heap_trace_init_standalone()` 注册一个可用于记录内存跟踪的缓冲区。

- 在有内存泄漏之嫌的代码块前，调用函数 `heap_trace_start()` 记录系统中的所有内存分配和释放操作。

- 在可疑代码执行完毕后调用 `heap_trace_stop()` 函数可停止跟踪内存的分配和释放。

- 调用 `heap_trace_alloc_pause()` 函数暂停跟踪新的内存分配，同时继续跟踪内存释放。在可能泄漏内存的代码后立即调用该函数，防止记录任何新的内存分配。

- 调用函数 `heap_trace_dump()` 导出内存跟踪结果。

应用程序代码初始化、启动和停止堆内存跟踪的一般过程，见以下代码片段示例：

    #include "esp_heap_trace.h"

    #define NUM_RECORDS 100
    static heap_trace_record_t trace_record[NUM_RECORDS]; // 该缓冲区必须在内部 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();
        ...
    }

堆内存跟踪会输出类似以下格式的内容：

    ====== Heap Trace: 8 records (8 capacity) ======
        3 bytes (@ 0x3fcb26f8, Internal) allocated CPU 0 ccount 0x1e7af728 freed
        6 bytes (@ 0x3fcb4ff0, Internal) allocated CPU 0 ccount 0x1e7afc38 freed
        9 bytes (@ 0x3fcb5000, Internal) allocated CPU 0 ccount 0x1e7b01d4 freed
        12 bytes (@ 0x3fcb5010, Internal) allocated CPU 0 ccount 0x1e7b0778 freed
        15 bytes (@ 0x3fcb5020, Internal) allocated CPU 0 ccount 0x1e7b0d18 freed
        18 bytes (@ 0x3fcb5034, Internal) allocated CPU 0 ccount 0x1e7b12b8 freed
        21 bytes (@ 0x3fcb504c, Internal) allocated CPU 0 ccount 0x1e7b1858 freed
        24 bytes (@ 0x3fcb5068, Internal) allocated CPU 0 ccount 0x1e7b1dfc freed
    ====== Heap Trace Summary ======
    Mode: Heap Trace All
    0 bytes alive in trace (0/8 allocations)
    records: 8 (8 capacity, 8 high water mark)
    total allocations: 8
    total frees: 8
    ================================

或者，启用了 `CONFIG_ESP_SYSTEM_USE_FRAME_POINTER` 选项并且正确配置了栈深度，会输出以下内容：

    ====== Heap Trace: 8 records (8 capacity) ======
        6 bytes (@ 0x3fc9f620, Internal) allocated CPU 0 ccount 0x1a31ac84 caller 0x40376321:0x40376379
    0x40376321: heap_caps_malloc at /path/to/idf/examples/components/heap/heap_caps.c:84
    0x40376379: heap_caps_malloc_default at /path/to/idf/examples/components/heap/heap_caps.c:110

    freed by 0x403839e4:0x42008096
    0x403839e4: free at /path/to/idf/examples/components/newlib/heap.c:40
    0x42008096: test_func_74 at /path/to/idf/examples/components/heap/test_apps/heap_tests/main/test_heap_trace.c:104 (discriminator 3)

        9 bytes (@ 0x3fc9f630, Internal) allocated CPU 0 ccount 0x1a31b618 caller 0x40376321:0x40376379
    0x40376321: heap_caps_malloc at /path/to/idf/examples/components/heap/heap_caps.c:84
    0x40376379: heap_caps_malloc_default at /path/to/idf/examples/components/heap/heap_caps.c:110

    freed by 0x403839e4:0x42008096
    0x403839e4: free at /path/to/idf/examples/components/newlib/heap.c:40
    0x42008096: test_func_74 at /path/to/idf/examples/components/heap/test_apps/heap_tests/main/test_heap_trace.c:104 (discriminator 3)

        12 bytes (@ 0x3fc9f640, Internal) allocated CPU 0 ccount 0x1a31bfac caller 0x40376321:0x40376379
    0x40376321: heap_caps_malloc at /path/to/idf/examples/components/heap/heap_caps.c:84
    0x40376379: heap_caps_malloc_default at /path/to/idf/examples/components/heap/heap_caps.c:110

    freed by 0x403839e4:0x42008096
    0x403839e4: free at /path/to/idf/examples/components/newlib/heap.c:40
    0x42008096: test_func_74 at /path/to/idf/examples/components/heap/test_apps/heap_tests/main/test_heap_trace.c:104 (discriminator 3)

        15 bytes (@ 0x3fc9f650, Internal) allocated CPU 0 ccount 0x1a31c940 caller 0x40376321:0x40376379
    0x40376321: heap_caps_malloc at /path/to/idf/examples/components/heap/heap_caps.c:84
    0x40376379: heap_caps_malloc_default at /path/to/idf/examples/components/heap/heap_caps.c:110

    freed by 0x403839e4:0x42008096
    0x403839e4: free at /path/to/idf/examples/components/newlib/heap.c:40
    0x42008096: test_func_74 at /path/to/idf/examples/components/heap/test_apps/heap_tests/main/test_heap_trace.c:104 (discriminator 3)

        18 bytes (@ 0x3fc9f664, Internal) allocated CPU 0 ccount 0x1a31d2d4 caller 0x40376321:0x40376379
    0x40376321: heap_caps_malloc at /path/to/idf/examples/components/heap/heap_caps.c:84
    0x40376379: heap_caps_malloc_default at /path/to/idf/examples/components/heap/heap_caps.c:110

    freed by 0x403839e4:0x42008096
    0x403839e4: free at /path/to/idf/examples/components/newlib/heap.c:40
    0x42008096: test_func_74 at /path/to/idf/examples/components/heap/test_apps/heap_tests/main/test_heap_trace.c:104 (discriminator 3)

        21 bytes (@ 0x3fc9f67c, Internal) allocated CPU 0 ccount 0x1a31dc68 caller 0x40376321:0x40376379
    0x40376321: heap_caps_malloc at /path/to/idf/examples/components/heap/heap_caps.c:84
    0x40376379: heap_caps_malloc_default at /path/to/idf/examples/components/heap/heap_caps.c:110

    freed by 0x403839e4:0x42008096
    0x403839e4: free at /path/to/idf/examples/components/newlib/heap.c:40
    0x42008096: test_func_74 at /path/to/idf/examples/components/heap/test_apps/heap_tests/main/test_heap_trace.c:104 (discriminator 3)

        24 bytes (@ 0x3fc9f698, Internal) allocated CPU 0 ccount 0x1a31e600 caller 0x40376321:0x40376379
    0x40376321: heap_caps_malloc at /path/to/idf/examples/components/heap/heap_caps.c:84
    0x40376379: heap_caps_malloc_default at /path/to/idf/examples/components/heap/heap_caps.c:110

    freed by 0x403839e4:0x42008096
    0x403839e4: free at /path/to/idf/examples/components/newlib/heap.c:40
    0x42008096: test_func_74 at /path/to/idf/examples/components/heap/test_apps/heap_tests/main/test_heap_trace.c:104 (discriminator 3)

        6 bytes (@ 0x3fc9f6b4, Internal) allocated CPU 0 ccount 0x1a320698 caller 0x40376321:0x40376379
    0x40376321: heap_caps_malloc at /path/to/idf/examples/components/heap/heap_caps.c:84
    0x40376379: heap_caps_malloc_default at /path/to/idf/examples/components/heap/heap_caps.c:110

    ====== Heap Trace Summary ======
    Mode: Heap Trace All
    6 bytes alive in trace (1/8 allocations)
    records: 8 (8 capacity, 8 high water mark)
    total allocations: 9
    total frees: 8
    ================================

备注

以上示例输出使用 IDF 监视器，自动将 PC 地址解码为其源文件和行号。

如果记录列表溢出，将输出 `(NB: Internal Buffer has overflowed, so trace data is incomplete.)`。如果看到此日志，请考虑缩短追踪周期或增加追踪缓冲区中的记录数量。

如果在调用 `heap_trace_dump()` 或 `heap_trace_dump_caps()` 时有新条目被追踪，将输出 `(NB: New entries were traced while dumping, so trace dump may have duplicate entries.)`。

在 `HEAP_TRACE_LEAKS` 或 `HEAP_TRACE_ALL` 模式下，对跟踪的每个未释放的已分配内存，打印的信息中都会包含以下内容：

- `XX bytes`：分配的字节数。

- `@ 0x...`：从 `heap_caps_malloc()` 或 `heap_caps_calloc()` 返回的堆地址。

- `Internal` 或 `PSRAM`：分配内存的一般位置。

- `CPU x`：分配过程中运行的 CPU（CPU 0 或 CPU 1）。

- `ccount 0x...`：分配时的 CCOUNT（CPU 循环计数器）寄存器值，CPU 0 与 CPU 1 中的这一值不同。

在 `HEAP_TRACE_LEAKS` 模式下，释放内存后，相关的记录会被丢弃。

在 `HEAP_TRACE_ALL` 模式下：

- 释放内存后，相关记录会保留在列表中，其字段 `freed` 设置为 true。

- 在达到最大记录数后，会丢弃旧记录，用新记录替换。

默认情况下，每个跟踪条目的调用栈深度为 0：不记录调用者 PC（仍会跟踪分配地址、大小等相关字段）。启用 CONFIG_ESP_SYSTEM_USE_FRAME_POINTER 后才能遍历调用栈，然后可通过 CONFIG_HEAP_TRACING_STACK_DEPTH 配置深度。每个内存分配最多可以记录 32 个栈帧（默认为 2），每增加一个栈帧，每个 `heap_trace_record_t` 记录的内存使用量将增加 8 个字节。

最后，将打印“泄漏”的总字节数（即在跟踪期间分配但未释放的总字节数），以及它所代表的总分配次数。

#### 使用哈希表提高性能

默认情况下，堆追踪使用一个静态分配的双向链表来存储追踪记录。这种方式的缺点是，当链表中的记录条目数量增加时，查找特定记录的耗时也会随之增加，从而导致运行性能下降。因此，在需要存储大量记录时，双向链表的使用效率很低（甚至可能导致功能无法使用，因为从列表中检索条目所需的时间会阻碍应用程序的正常运行）。

为了解决这个问题，可以启用 CONFIG_HEAP_TRACE_HASH_MAP 选项，使用哈希表机制来存储记录。这样就可以在不严重影响性能的情况下追踪大量记录。

每个哈希表条目是一个单向链表，用于存储具有相同哈希 ID 的记录。

每条记录的哈希 ID 是基于它们追踪的内存指针计算的。使用的哈希函数基于修改后的 Fowler-Noll-Vo 哈希函数，确保了所有记录在范围 [0, 哈希表大小) 内均匀分布。其中哈希表大小可以通过 CONFIG_HEAP_TRACE_HASH_MAP_SIZE 来定义。

备注

- 选项 CONFIG_HEAP_TRACE_HASH_MAP_SIZE 定义了哈希表中的条目数量。记录的总数量仍由用户在调用 `heap_trace_init_standalone()` 时定义。如果最大记录数为 `N`，而哈希表的条目数为 `H`，那么每个条目最多可包含 `N / H` 条记录。

- 哈希表是对双向链表的补充，而无法替代双向链表。因为使用哈希表可能会导致显著的内存开销。

- 存储哈希表所用的内存是动态分配的（默认分配在内部内存中），但用户可以通过 CONFIG_HEAP_TRACE_HASH_MAP_IN_EXT_RAM 选项，将哈希表强制存储在外部内存中（此选项仅在启用了 CONFIG_SPIRAM 的条件下可用）。

### 主机模式

确定存在泄漏的代码后，请执行以下步骤：

- 将 CONFIG_HEAP_TRACING_DEST 设置为 `Host-Based`。

- 将 CONFIG_APPTRACE_DESTINATION 设置为 `JTAG`。

- 将 `espressif/esp_sysview` 托管组件依赖添加到 `idf_component.yml`。

- 启用 CONFIG_ESP_TRACE_LIB_EXTERNAL。

- 在程序早期，调用函数 `heap_trace_init_tohost()`，初始化 JTAG 堆内存跟踪模块。

- 在有内存泄漏之嫌的代码块前，调用函数 `heap_trace_start()` 开始记录系统中的内存分配和释放操作。

  主机模式忽略该函数参数，堆内存跟踪模块以 `HEAP_TRACE_ALL` 传递后的方式运行，即所有的内存分配和释放都发送到主机。

- 在有内存泄露之嫌的代码块后，调用函数 `heap_trace_stop()` 停止跟踪。

应用程序代码初始化、启动和停止基于主机模式堆内存跟踪的一般过程，请参阅以下代码片段示例：

    #include "esp_heap_trace.h"

    ...

    void app_main()
    {
        ...
        ESP_ERROR_CHECK( heap_trace_init_tohost() );
        ...
    }

    void some_function()
    {
        ESP_ERROR_CHECK( heap_trace_start(HEAP_TRACE_LEAKS) );

        do_something_you_suspect_is_leaking();

        ESP_ERROR_CHECK( heap_trace_stop() );
        ...
    }

要收集并分析堆内存跟踪结果，请在主机上完成以下操作：

1. 构建程序并将其下载到目标设备，详情请参阅 第五步：开始使用 ESP-IDF 吧。

2. 运行 OpenOCD（请参阅 JTAG 调试）。

备注

使用此功能需要 `v0.10.0-esp32-20181105` 或更高版本的 OpenOCD。

3. 使用 GDB 可以自动启动和/或停止跟踪，为此应准备特殊的 `gdbinit` 文件：

    target remote :3333

    mon reset halt
    maintenance flush register-cache

    tb heap_trace_start
    commands
    mon esp sysview start file:///tmp/heap.svdat
    c
    end

    tb heap_trace_stop
    commands
    mon esp sysview stop
    end

    c

使用此文件，GDB 将连接到目标设备、重置该设备，在程序触发 `heap_trace_start()` 断点时开始跟踪，在程序触发 `heap_trace_stop()` 断点时停止跟踪。跟踪数据将保存至 `/tmp/heap_log.svdat`。

4. 使用命令 `riscv32-esp-elf-gdb -x gdbinit </path/to/program/elf>` 运行 GDB

5. 调用 `heap_trace_stop()` 函数使程序停止运行时，退出 GDB，跟踪数据将保存至 `/tmp/heap.svdat`

6. 运行处理脚本 `$IDF_PATH/tools/esp_app_trace/sysviewtrace_proc.py -p -b </path/to/program/elf> /tmp/heap_log.svdat`

堆内存跟踪堆输出将类似以下格式的内容:

    Parse trace from '/tmp/heap.svdat'...
    Stop parsing trace. (Timeout 0.000000 sec while reading 1 byte!)
    Process events from '['/tmp/heap.svdat']'...
    [0.002244575] HEAP: Allocated 1 byte @ 0x3ffaffd8 from task "alloc" on core 0 by:
    /home/user/projects/esp/esp-idf/examples/system/tracing/sysview_tracing_heap_log/main/sysview_heap_log.c:47
    /home/user/projects/esp/esp-idf/components/freertos/port.c:355 (discriminator 1)

    [0.002258425] HEAP: Allocated 2 bytes @ 0x3ffaffe0 from task "alloc" on core 0 by:
    /home/user/projects/esp/esp-idf/examples/system/tracing/sysview_tracing_heap_log/main/sysview_heap_log.c:48
    /home/user/projects/esp/esp-idf/components/freertos/port.c:355 (discriminator 1)

    [0.002563725] HEAP: Freed bytes @ 0x3ffaffe0 from the task "free" on core 0 by:
    /home/user/projects/esp/esp-idf/examples/system/tracing/sysview_tracing_heap_log/main/sysview_heap_log.c:31 (discriminator 9)
    /home/user/projects/esp/esp-idf/components/freertos/port.c:355 (discriminator 1)

    [0.002782950] HEAP: Freed bytes @ 0x3ffb40b8 from the task "main" on core 0 by:
    /home/user/projects/esp/esp-idf/components/freertos/tasks.c:4590
    /home/user/projects/esp/esp-idf/components/freertos/tasks.c:4590

    [0.002798700] HEAP: Freed bytes @ 0x3ffb50bc from the task "main" on core 0 by:
    /home/user/projects/esp/esp-idf/components/freertos/tasks.c:4590
    /home/user/projects/esp/esp-idf/components/freertos/tasks.c:4590

    [0.102436025] HEAP: Allocated 2 bytes @ 0x3ffaffe0 from the task "alloc" on core 0 by:
    /home/user/projects/esp/esp-idf/examples/system/tracing/sysview_tracing_heap_log/main/sysview_heap_log.c:47
    /home/user/projects/esp/esp-idf/components/freertos/port.c:355 (discriminator 1)

    [0.102449800] HEAP: Allocated 4 bytes @ 0x3ffaffe8 from the task "alloc" on core 0 by:
    /home/user/projects/esp/esp-idf/examples/system/tracing/sysview_tracing_heap_log/main/sysview_heap_log.c:48
    /home/user/projects/esp/esp-idf/components/freertos/port.c:355 (discriminator 1)

    [0.102666150] HEAP: Freed bytes @ 0x3ffaffe8 from the task "free" on core 0 by:
    /home/user/projects/esp/esp-idf/examples/system/tracing/sysview_tracing_heap_log/main/sysview_heap_log.c:31 (discriminator 9)
    /home/user/projects/esp/esp-idf/components/freertos/port.c:355 (discriminator 1)

    [0.202436200] HEAP: Allocated 3 bytes @ 0x3ffaffe8 from the task "alloc" on core 0 by:
    /home/user/projects/esp/esp-idf/examples/system/tracing/sysview_tracing_heap_log/main/sysview_heap_log.c:47
    /home/user/projects/esp/esp-idf/components/freertos/port.c:355 (discriminator 1)

    [0.202451725] HEAP: Allocated 6 bytes @ 0x3ffafff0 from the task "alloc" on core 0 by:
    /home/user/projects/esp/esp-idf/examples/system/tracing/sysview_tracing_heap_log/main/sysview_heap_log.c:48
    /home/user/projects/esp/esp-idf/components/freertos/port.c:355 (discriminator 1)

    [0.202667075] HEAP: Freed bytes @ 0x3ffafff0 from the task "free" on core 0 by:
    /home/user/projects/esp/esp-idf/examples/system/tracing/sysview_tracing_heap_log/main/sysview_heap_log.c:31 (discriminator 9)
    /home/user/projects/esp/esp-idf/components/freertos/port.c:355 (discriminator 1)

    [0.302436000] HEAP: Allocated 4 bytes @ 0x3ffafff0 from the task "alloc" on core 0 by:
    /home/user/projects/esp/esp-idf/examples/system/tracing/sysview_tracing_heap_log/main/sysview_heap_log.c:47
    /home/user/projects/esp/esp-idf/components/freertos/port.c:355 (discriminator 1)

    [0.302451475] HEAP: Allocated 8 bytes @ 0x3ffb40b8 from the task "alloc" on core 0 by:
    /home/user/projects/esp/esp-idf/examples/system/tracing/sysview_tracing_heap_log/main/sysview_heap_log.c:48
    /home/user/projects/esp/esp-idf/components/freertos/port.c:355 (discriminator 1)

    [0.302667500] HEAP: Freed bytes @ 0x3ffb40b8 from the task "free" on core 0 by:
    /home/user/projects/esp/esp-idf/examples/system/tracing/sysview_tracing_heap_log/main/sysview_heap_log.c:31 (discriminator 9)
    /home/user/projects/esp/esp-idf/components/freertos/port.c:355 (discriminator 1)

    Processing completed.

    Processed 1019 events

    =============== HEAP TRACE REPORT ===============

    Processed 14 heap events.

    [0.002244575] HEAP: Allocated 1 bytes @ 0x3ffaffd8 from the task "alloc" on core 0 by:
    /home/user/projects/esp/esp-idf/examples/system/tracing/sysview_tracing_heap_log/main/sysview_heap_log.c:47
    /home/user/projects/esp/esp-idf/components/freertos/port.c:355 (discriminator 1)

    [0.102436025] HEAP: Allocated 2 bytes @ 0x3ffaffe0 from the task "alloc" on core 0 by:
    /home/user/projects/esp/esp-idf/examples/system/tracing/sysview_tracing_heap_log/main/sysview_heap_log.c:47
    /home/user/projects/esp/esp-idf/components/freertos/port.c:355 (discriminator 1)

    [0.202436200] HEAP: Allocated 3 bytes @ 0x3ffaffe8 from the task "alloc" on core 0 by:
    /home/user/projects/esp/esp-idf/examples/system/tracing/sysview_tracing_heap_log/main/sysview_heap_log.c:47
    /home/user/projects/esp/esp-idf/components/freertos/port.c:355 (discriminator 1)

    [0.302436000] HEAP: Allocated 4 bytes @ 0x3ffafff0 from the task "alloc" on core 0 by:
    /home/user/projects/esp/esp-idf/examples/system/tracing/sysview_tracing_heap_log/main/sysview_heap_log.c:47
    /home/user/projects/esp/esp-idf/components/freertos/port.c:355 (discriminator 1)

    Found 10 leaked bytes in 4 blocks.

### 堆内存跟踪定位堆内存损坏

堆内存跟踪也是一种定位堆内存损坏位置的方法。当堆中的某个区域损坏时，可能是因为程序中的其他部分在相邻地址分配内存。

如果大致了解堆内存损坏发生的时间范围，启用 `HEAP_TRACE_ALL` 模式的堆内存跟踪，可以记录分配内存的所有函数及其相应地址。

以此方法使用堆内存跟踪与上文描述的内存泄漏检测类似，对已分配但未释放的内存输出结果相同，但还会显示已释放内存的记录。

### 性能影响

在 menuconfig 中启用堆内存跟踪，会增加程序代码的大小，即便未运行堆内存跟踪，也会对堆内存分配或释放操作的性能产生较小的负面影响。

运行堆内存跟踪时，堆内存分配或释放操作的速度明显变慢。增加为各内存分配的栈帧深度（见上文）也会造成这种性能影响。

为减轻堆内存跟踪运行时的性能损失，请启用 CONFIG_HEAP_TRACE_HASH_MAP。此时，将使用哈希映射机制处理堆内存跟踪记录，减少堆内存分配或释放操作的执行时长。设置 CONFIG_HEAP_TRACE_HASH_MAP_SIZE 的值可以调整哈希映射的大小。

默认情况下，哈希映射会放置在内部 RAM 中，启用 CONFIG_HEAP_TRACE_HASH_MAP_IN_EXT_RAM 时也可将其放置在外部 RAM 中。要启用此配置，请确保已启用 CONFIG_SPIRAM 和 CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY。

### 内存泄漏误报

并非所有由 `heap_trace_dump()` 打印的信息都是内存泄漏，以下情况也可能打印信息：

- 在调用 `heap_trace_start()` 后分配且在调用 `heap_trace_stop()` 后才释放的内存都会出现在泄漏信息中。

- 系统中的其他任务也可能进行内存分配。根据这些任务的时间安排，报错的这部分内存很可能在调用 `heap_trace_stop()` 后释放。

- 当任务第一次使用 stdio，如调用 `heap_caps_printf()` 时，libc 会分配一个锁，即 RTOS 互斥信号量，该分配将持续至任务删除。

- 进行打印浮点数等调用 `heap_caps_printf()` 的操作时，会根据需要，从堆中分配一些内存，这些分配将持续至任务删除。

- 蓝牙、Wi-Fi 和 TCP/IP 库会分配堆内存缓冲区，处理传入或传出的数据，这些内存缓冲区通常持续时间较短。但如果在运行堆内存泄漏跟踪期间，网络底层接收或发送了数据，一些缓冲区可能会出现在堆内存泄漏跟踪输出中。

- 由于存在 `TIME_WAIT` 状态，TCP 连接在关闭后仍会使用一些内存，`TIME_WAIT` 状态结束后将释放这些内存。

要区分“真实”和“误报”的内存泄漏，可以在堆内存跟踪运行时多次调用可疑代码，并在堆内存跟踪输出中查找重复出现的内存分配情况。

## 应用示例

- system/heap_task_tracking/basic 演示了堆任务跟踪的概览功能，用于导出每个任务在堆内存使用方面的统计信息概要。

- system/heap_task_tracking/advanced 演示了堆任务跟踪中统计信息函数的用法，用于访问每个任务在堆内存使用方面的完整统计数据。

## API 参考–堆任务跟踪

### Header File

- components/heap/include/esp_heap_task_info.h

- This header file can be included with:

  >     #include "esp_heap_task_info.h"

### Functions

size_t heap_caps_get_per_task_info(heap_task_info_params_t *params)

  Return per-task heap allocation totals and lists of blocks.

  For each task that has allocated memory from the heap, return totals for allocations within regions matching one or more sets of capabilities.

  Optionally also return an array of structs providing details about each block allocated by one or more requested tasks, or by all tasks.

  参见

  heap_task_info_params_t).

  参见

  heap_task_block_t).

  参数:

    params -- Structure to hold all the parameters for the function (

  返回:

    Number of block detail structs returned (

esp_err_t heap_caps_get_all_task_stat(heap_all_tasks_stat_t *tasks_stat)

  Return per-task heap memory usage and associated allocation information on each heap for all tasks.

  For each task that has allocated memory from the heap, return information of memory usage and allocation information of the task on each heap the task has used.

  参见

  heap_all_tasks_stat_t).

  参数:

    tasks_stat -- Structure to hold the memory usage statistics of all tasks (

  返回:

    ESP_OK if the information were gathered successfully. ESP_ERR_INVALID_ARG if the user defined field in heap_all_tasks_stat_t are not set properly

esp_err_t heap_caps_get_single_task_stat(heap_single_task_stat_t *task_stat, TaskHandle_t task_handle)

  Return heap memory usage and associated allocation information on each heap for a given task.

  参数:

    - task_handle -- [in] handle of the task. If NULL, the function will get the current task handle and return the statistics of this task.

    - task_stat -- [out] Structure to hold the memory usage statistics of the task defined by task_handle

  返回:

    ESP_OK if the information were gathered successfully. ESP_ERR_INVALID_ARG if the user defined field in heap_single_task_stat_t are not set properly

void heap_caps_print_all_task_stat(FILE *stream)

  Print heap memory usage and associated allocation information on each heap for all created tasks since startup (running and deleted ones when CONFIG_HEAP_TRACK_DELETED_TASKS is enabled).

  备注

  This function is an alternative to heap_caps_get_all_task_stat if the goal is just to print information and not manipulate them.

  参数:

    stream -- The stream to dump to, if NULL then stdout is used

void heap_caps_print_all_task_stat_overview(FILE *stream)

  Print summary information of all tasks.

  备注

  The information printed by this function is an array formatted log of task_stat_t content for each running task (and deleted ones if HEAP_TRACK_DELETED_TASKS is enabled)

  参数:

    stream -- The stream to dump to, if NULL then stdout is used

void heap_caps_print_single_task_stat(FILE *stream, TaskHandle_t task_handle)

  Print heap memory usage and associated allocation information on each heap for a given task.

  备注

  This function is an alternative to heap_caps_get_single_task_stat if the goal is just to print information and not manipulate them.

  参数:

    - stream -- The stream to dump to, if NULL then stdout is used

    - task_handle -- The task handle of the task to get memory usage and associated allocation information from.

void heap_caps_print_single_task_stat_overview(FILE *stream, TaskHandle_t task_handle)

  Print summary information of a given task.

  备注

  The information printed by this function is an array formatted log of task_stat_t content for the given task. This function will not print the task summary information if the given task is deleted and HEAP_TRACK_DELETED_TASKS is disabled.

  参数:

    - stream -- The stream to dump to, if NULL then stdout is used

    - task_handle -- The task handle of the task to get memory usage and associated allocation information from.

esp_err_t heap_caps_alloc_single_task_stat_arrays(heap_single_task_stat_t *task_stat, TaskHandle_t task_handle)

  Allocate the memory used to store the heap and alloc statistics and fill task_stat with the pointer to those allocations and the number of heaps and allocs statistics available for the given task.

  备注

  If NULL is passed as parameter for the task_handle, the information on the currently running task will be returned. This function should be called prior to heap_caps_get_single_task_stat() if the user wishes to use dynamic allocation to store statistics.

  参数:

    - task_handle -- The task from which to get the information. If NULL, this function will return the number of heap used by the calling task.

    - task_stat -- Structure containing information filled by this function.

  返回:

    ESP_OK if the memory necessary to gather the statistics was allocated successfully. ESP_FAIL if not enough memory space is available to store all statistics.

void heap_caps_free_single_task_stat_arrays(heap_single_task_stat_t *task_stat)

  Free the memory allocated to store heap and alloc statistics by calling heap_caps_alloc_single_task_stat_arrays.

  参数:

    task_stat -- Structure from which to free the allocated memory used to store statistics

esp_err_t heap_caps_alloc_all_task_stat_arrays(heap_all_tasks_stat_t *tasks_stat)

  Allocate the memory used to store the tasks, heaps and allocs statistics and fill tasks_stat with the pointer to those allocations and the number of tasks, heaps and allocs statistics available.

  备注

  This function should be called prior to heap_caps_get_all_task_stat() if the user wishes to use dynamic allocation to store statistics.

  参数:

    tasks_stat -- Structure containing information filled by this function.

  返回:

    ESP_OK if the memory necessary to gather the statistics was allocated successfully. ESP_FAIL if not enough memory space is available to store all statistics.

void heap_caps_free_all_task_stat_arrays(heap_all_tasks_stat_t *tasks_stat)

  Free the memory allocated to store task, heap and alloc statistics by calling heap_caps_alloc_all_task_stat_arrays.

  参数:

    tasks_stat -- Structure from which to free the allocated memory used to store statistics

### Structures

struct heap_task_totals_t

  Structure to collect per-task heap allocation totals partitioned by selected caps.

  Public Members

  TaskHandle_t task

    Task to which these totals belong.

  size_t size[NUM_HEAP_TASK_CAPS]

    Total allocations partitioned by selected caps.

  size_t count[NUM_HEAP_TASK_CAPS]

    Number of blocks partitioned by selected caps.

struct heap_task_block_t

  Structure providing details about a block allocated by a task.

  Public Members

  TaskHandle_t task

    Task that allocated the block.

  void *address

    User address of allocated block.

  uint32_t size

    Size of the allocated block.

struct heap_task_info_params_t

  Structure to provide parameters to heap_caps_get_per_task_info.

  The 'caps' and 'mask' arrays allow partitioning the per-task heap allocation totals by selected sets of heap region capabilities so that totals for multiple regions can be accumulated in one scan. The capabilities flags for each region ANDed with mask[i] are compared to caps[i] in order; the allocations in that region are added to totals->size[i] and totals->count[i] for the first i that matches. To collect the totals without any partitioning, set mask[0] and caps[0] both to zero. The allocation totals are returned in the 'totals' array of heap_task_totals_t structs. To allow easily comparing the totals array between consecutive calls, that array can be left populated from one call to the next so the order of tasks is the same even if some tasks have freed their blocks or have been deleted. The number of blocks prepopulated is given by num_totals, which is updated upon return. If there are more tasks with allocations than the capacity of the totals array (given by max_totals), information for the excess tasks will be not be collected. The totals array pointer can be NULL if the totals are not desired.

  The 'tasks' array holds a list of handles for tasks whose block details are to be returned in the 'blocks' array of heap_task_block_t structs. If the tasks array pointer is NULL, block details for all tasks will be returned up to the capacity of the buffer array, given by max_blocks. The function return value tells the number of blocks filled into the array. The blocks array pointer can be NULL if block details are not desired, or max_blocks can be set to zero.

  Public Members

  int32_t caps[NUM_HEAP_TASK_CAPS]

    Array of caps for partitioning task totals.

  int32_t mask[NUM_HEAP_TASK_CAPS]

    Array of masks under which caps must match.

  TaskHandle_t *tasks

    Array of tasks whose block info is returned.

  size_t num_tasks

    Length of tasks array.

  heap_task_totals_t *totals

    Array of structs to collect task totals.

  size_t *num_totals

    Number of task structs currently in array.

  size_t max_totals

    Capacity of array of task totals structs.

  heap_task_block_t *blocks

    Array of task block details structs.

  size_t max_blocks

    Capacity of array of task block info structs.

struct heap_stat_t

  Structure providing details about memory usage of a given task on a heap.

  Public Members

  const char *name

    Pointer to the name of the heap defined in soc_memory_types[].

  uint32_t caps

    All caps supported by the heap (ORED)

  size_t size

    The available size of the heap.

  size_t current_usage

    The current usage of a given task on the heap.

  size_t peak_usage

    The peak usage since startup on a given task on the heap.

  size_t alloc_count

    The current number of allocation by a given task on the heap.

  heap_task_block_t *alloc_stat

    Pointer to an array of allocation stats for a given task on the heap.

struct task_stat_t

  Structure providing details about a task.

  Public Members

  char name[configMAX_TASK_NAME_LEN]

    Name of the task.

  TaskHandle_t handle

    Pointer to the task handle.

  bool is_alive

    Information whether the task is alive (true) or deleted (false)

  size_t overall_peak_usage

    Information about the memory peak usage across all heaps of a given task.

  size_t overall_current_usage

    Information about the memory current usage across all heaps of a given task.

  size_t heap_count

    Number of different heaps the task has used since its creation.

  heap_stat_t *heap_stat

    Pointer to an array containing statistics of the heaps used by the task.

struct heap_single_task_stat_t

  User interface containing the statistics of a given task and the associated memory usage of the task on each heap.

  Public Members

  task_stat_t stat

    Statistics of the task.

  size_t heap_count

    size of user defined heap_stat array

  heap_stat_t *heap_stat_start

    Pointer to the start to the user defined heap_stat array.

  size_t alloc_count

    size of user defined alloc_stat array

  heap_task_block_t *alloc_stat_start

    Pointer to the start to the user defined alloc_stat array.

struct heap_all_tasks_stat_t

  User interface containing the statistics of all tasks and the associated memory usage of those tasks on each heap they use.

  Public Members

  size_t task_count

    user defined size of heap_single_task_stat_t array

  task_stat_t *stat_arr

    Pointer to the user defined array of heap_single_task_stat_t.

  size_t heap_count

    size of user defined heap_stat array

  heap_stat_t *heap_stat_start

    Pointer to the start to the user defined heap_stat array.

  size_t alloc_count

    size of user defined alloc_stat array

  heap_task_block_t *alloc_stat_start

    Pointer to the start to the user defined alloc_stat array.

### Macros

NUM_HEAP_TASK_CAPS

## API 参考–堆内存跟踪

### Header File

- components/heap/include/esp_heap_trace.h

- This header file can be included with:

  >     #include "esp_heap_trace.h"

### Functions

esp_err_t heap_trace_init_standalone(heap_trace_record_t *record_buffer, size_t num_records)

  Initialise heap tracing in standalone mode.

  This function must be called before any other heap tracing functions.

  To disable heap tracing and allow the buffer to be freed, stop tracing and then call heap_trace_init_standalone(NULL, 0);

  参数:

    - record_buffer -- Provide a buffer to use for heap trace data. Note: External RAM is allowed, but it prevents recording allocations made from ISR's.

    - num_records -- Size of the heap trace buffer, as number of record structures.

  返回:

    - ESP_ERR_NOT_SUPPORTED Project was compiled without heap tracing enabled in menuconfig.

    - ESP_ERR_INVALID_STATE Heap tracing is currently in progress.

    - ESP_OK Heap tracing initialised successfully.

esp_err_t heap_trace_init_tohost(void)

  Initialise heap tracing in host-based mode.

  This function must be called before any other heap tracing functions.

  返回:

    - ESP_ERR_INVALID_STATE Heap tracing is currently in progress.

    - ESP_OK Heap tracing initialised successfully.

esp_err_t heap_trace_start(heap_trace_mode_t mode)

  Start heap tracing. All heap allocations & frees will be traced, until heap_trace_stop() is called.

  备注

  heap_trace_init_standalone() must be called to provide a valid buffer, before this function is called.

  备注

  Calling this function while heap tracing is running will reset the heap trace state and continue tracing.

  参数:

    mode -- Mode for tracing.

    - HEAP_TRACE_ALL means all heap allocations and frees are traced.

    - HEAP_TRACE_LEAKS means only suspected memory leaks are traced. (When memory is freed, the record is removed from the trace buffer.)

  返回:

    - ESP_ERR_NOT_SUPPORTED Project was compiled without heap tracing enabled in menuconfig.

    - ESP_ERR_INVALID_STATE A non-zero-length buffer has not been set via heap_trace_init_standalone().

    - ESP_OK Tracing is started.

esp_err_t heap_trace_stop(void)

  Stop heap tracing.

  返回:

    - ESP_ERR_NOT_SUPPORTED Project was compiled without heap tracing enabled in menuconfig.

    - ESP_ERR_INVALID_STATE Heap tracing was not in progress.

    - ESP_OK Heap tracing stopped.

esp_err_t heap_trace_alloc_pause(void)

  Pause heap tracing of allocations.

  备注

  This function puts the heap tracing in the state where the new allocations will no longer be traced but the free will still be. This can be used to e.g., strategically monitor a set of allocations to make sure each of them will get freed without polluting the list of records with unwanted allocations.

  返回:

    - ESP_ERR_NOT_SUPPORTED Project was compiled without heap tracing enabled in menuconfig.

    - ESP_ERR_INVALID_STATE Heap tracing was not in progress.

    - ESP_OK Heap tracing paused.

esp_err_t heap_trace_resume(void)

  Resume heap tracing which was previously stopped.

  Unlike heap_trace_start(), this function does not clear the buffer of any pre-existing trace records.

  The heap trace mode is the same as when heap_trace_start() was last called (or HEAP_TRACE_ALL if heap_trace_start() was never called).

  返回:

    - ESP_ERR_NOT_SUPPORTED Project was compiled without heap tracing enabled in menuconfig.

    - ESP_ERR_INVALID_STATE Heap tracing was already started.

    - ESP_OK Heap tracing resumed.

size_t heap_trace_get_count(void)

  Return number of records in the heap trace buffer.

  It is safe to call this function while heap tracing is running.

esp_err_t heap_trace_get(size_t index, heap_trace_record_t *record)

  Return a raw record from the heap trace buffer.

  备注

  It is safe to call this function while heap tracing is running, however in HEAP_TRACE_LEAK mode record indexing may skip entries unless heap tracing is stopped first.

  参数:

    - index -- Index (zero-based) of the record to return.

    - record -- [out] Record where the heap trace record will be copied.

  返回:

    - ESP_ERR_NOT_SUPPORTED Project was compiled without heap tracing enabled in menuconfig.

    - ESP_ERR_INVALID_STATE Heap tracing was not initialised.

    - ESP_ERR_INVALID_ARG Index is out of bounds for current heap trace record count.

    - ESP_OK Record returned successfully.

void heap_trace_dump(void)

  Dump heap trace record data to stdout.

  备注

  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_caps(const uint32_t caps)

  Dump heap trace from the memory of the capabilities passed as parameter.

  参数:

    caps -- Capability(ies) of the memory from which to dump the trace. Set MALLOC_CAP_INTERNAL to dump heap trace data from internal memory. Set MALLOC_CAP_SPIRAM to dump heap trace data from PSRAM. Set both to dump both heap trace data.

esp_err_t heap_trace_summary(heap_trace_summary_t *summary)

  Get summary information about the result of a heap trace.

  备注

  It is safe to call this function while heap tracing is running.

### Structures

struct heap_trace_record_t

  Trace record data type. Stores information about an allocated region of memory.

  Public Members

  uint32_t ccount

    CCOUNT of the CPU when the allocation was made. LSB (bit value 1) is the CPU number (0 or 1).

  void *address

    Address which was allocated. If NULL, then this record is empty.

  size_t size

    Size of the allocation.

  bool freed

    State of the allocation (false if not freed, true if freed)

  void *alloced_by[CONFIG_HEAP_TRACING_STACK_DEPTH]

    Call stack of the caller which allocated the memory.

  void *freed_by[CONFIG_HEAP_TRACING_STACK_DEPTH]

    Call stack of the caller which freed the memory (all zero if not freed.)

struct heap_trace_summary_t

  Stores information about the result of a heap trace.

  Public Members

  heap_trace_mode_t mode

    The heap trace mode we just completed / are running.

  size_t total_allocations

    The total number of allocations made during tracing.

  size_t total_frees

    The total number of frees made during tracing.

  size_t count

    The number of records in the internal buffer.

  size_t capacity

    The capacity of the internal buffer.

  size_t high_water_mark

    The maximum value that 'count' got to.

  size_t has_overflowed

    True if the internal buffer overflowed at some point.

### Macros

CONFIG_HEAP_TRACING_STACK_DEPTH

### Type Definitions

typedef struct heap_trace_record_t heap_trace_record_t

  Trace record data type. Stores information about an allocated region of memory.

### Enumerations

enum heap_trace_mode_t

  Values:

  enumerator HEAP_TRACE_ALL
  enumerator HEAP_TRACE_LEAKS

此文档对您有帮助吗？

- 反馈已收到，谢谢！
- 如果您有其他意见，欢迎填写乐鑫文档反馈表。

- 我们重视您的反馈。
- 您可以填写乐鑫文档反馈表告诉我们如何改进该文档。