Skip to content

Commit bc2879a

Browse files
slcasnerprojectgus
authored andcommitted
heap: Add task tracking option for heap usage monitoring
Add back a feature that was available in the old heap implementation in release/v2.1 and earlier: keep track of which task allocates each block from the heap. The task handle is conditionally added as another word in the heap poisoning header under this configuration option CONFIG_HEAP_TASK_TRACKING. To allow custom monitoring and debugging code to be added, add helper functions in multi_heap.c and multi_heap_poisoning.c to provide access to information in the block headers. Add esp_heap_debug_dump_totals() to monitor heap usage esp_heap_debug_dump_totals() dumps into a user-provided data structure a summary of the amound of heap memory in region type that is used by each task. Optionally it will also dump into another data structure the metadata about each allocated block for a given list of tasks or for all tasks (limited by available space). Address change requests on PR #1498 This set of changes fixes the files in e3b702c to just add the CONFIG_HEAP_TASK_TRACKING option without adding the new function heap_caps_get_per_task_info() in case that is the only portion of the PR that will be accepted. Part of the change is to remove the new .c and .h files containing that function and to remove the line to compile it from components/heap/component.mk since it should not have been included in e3b702c. One or more additional commits to add the new function will follow. The other changes here: - uint32_t get_all_caps() moves to heap_private.h - replace "void* foo" with "void *foo" - add braces around single-line "if" blocks - replace tab characters with spaces Address change requests on PR #1498, part 2 This set of changes fixes the files in cdf32aa to add the new function heap_caps_get_per_task_info() with its new name and to add the line to compile it in components/heap/component.mk. This does not address all the suggested changes because there are some needing further discussion. This commit does not include the suggested change to move the declaration of the new function into esp_heap_caps.h because the new function references TaskHandle_t so esp_heap_caps.h would have to include freertos/FreeRTOS.h and freertos/task.h, but FreeRTOS.h includes esp_heap_caps.h through two other header files which results in compilation errors because not all of FreeRTOS.h has been read yet. Change heap_caps_get_per_task_info() to take struct of params In addition to moving the large number of function parameters into a struct as the single parameter, the following changes were made: - Clear out the totals for any prepopulated tasks so the app code doesn't have to do it. - Rather than partitioning the per-task totals into a predetermined set of heap capabilities, take a list of <caps,mask> pairs to compare the caps to the heap capabilities as masked. This lets the caller configure the desired partitioning, or none. - Allow the totals array pointer or the blocks array pointer to be NULL to indicate not to collect that part of the information. - In addition to returning the total space allocated by each task, return the number of blocks allocated by each task. - Don't need to return the heap capabilities as part of the details for each block since the heap region (and therefore its capabilities) can be determined from the block address. - Renamed heap_task_info.h to esp_heap_task_info.h to fit the naming convention, and renamed the structs for totals and block details to better fit the revised function name. - Provide full Doxygen commenting for the function and parameter structs. Add copyright header to new files Merges #1498
1 parent a76d5a3 commit bc2879a

File tree

10 files changed

+335
-13
lines changed

10 files changed

+335
-13
lines changed

components/heap/Kconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,13 @@ config HEAP_TRACING_STACK_DEPTH
3737
More stack frames uses more memory in the heap trace buffer (and slows down allocation), but
3838
can provide useful information.
3939

40+
config HEAP_TASK_TRACKING
41+
bool "Enable heap task tracking"
42+
depends on !HEAP_POISONING_DISABLED
43+
help
44+
Enables tracking the task responsible for each heap allocation.
45+
46+
This function depends on heap poisoning being enabled and adds four more bytes of overhead for each block
47+
allocated.
48+
4049
endmenu

components/heap/component.mk

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ COMPONENT_OBJS := heap_caps_init.o heap_caps.o multi_heap.o heap_trace.o
66

77
ifndef CONFIG_HEAP_POISONING_DISABLED
88
COMPONENT_OBJS += multi_heap_poisoning.o
9+
10+
ifdef CONFIG_HEAP_TASK_TRACKING
11+
COMPONENT_OBJS += heap_task_info.o
12+
endif
913
endif
1014

1115
ifdef CONFIG_HEAP_TRACING

components/heap/heap_caps.c

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -55,19 +55,6 @@ IRAM_ATTR static void *dram_alloc_to_iram_addr(void *addr, size_t len)
5555
return (void *)(iptr + 1);
5656
}
5757

58-
/* return all possible capabilities (across all priorities) for a given heap */
59-
inline static uint32_t get_all_caps(const heap_t *heap)
60-
{
61-
if (heap->heap == NULL) {
62-
return 0;
63-
}
64-
uint32_t all_caps = 0;
65-
for (int prio = 0; prio < SOC_MEMORY_TYPE_NO_PRIOS; prio++) {
66-
all_caps |= heap->caps[prio];
67-
}
68-
return all_caps;
69-
}
70-
7158
bool heap_caps_match(const heap_t *heap, uint32_t caps)
7259
{
7360
return heap->heap != NULL && ((get_all_caps(heap) & caps) == caps);

components/heap/heap_private.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,18 @@ extern SLIST_HEAD(registered_heap_ll, heap_t_) registered_heaps;
4848

4949
bool heap_caps_match(const heap_t *heap, uint32_t caps);
5050

51+
/* return all possible capabilities (across all priorities) for a given heap */
52+
inline static uint32_t get_all_caps(const heap_t *heap)
53+
{
54+
if (heap->heap == NULL) {
55+
return 0;
56+
}
57+
uint32_t all_caps = 0;
58+
for (int prio = 0; prio < SOC_MEMORY_TYPE_NO_PRIOS; prio++) {
59+
all_caps |= heap->caps[prio];
60+
}
61+
return all_caps;
62+
}
5163

5264
/*
5365
Because we don't want to add _another_ known allocation method to the stack of functions to trace wrt memory tracing,

components/heap/heap_task_info.c

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include <freertos/FreeRTOS.h>
16+
#include <freertos/task.h>
17+
#include <multi_heap.h>
18+
#include "multi_heap_internal.h"
19+
#include "heap_private.h"
20+
#include "esp_heap_task_info.h"
21+
22+
#ifdef CONFIG_HEAP_TASK_TRACKING
23+
24+
/*
25+
* Return per-task heap allocation totals and lists of blocks.
26+
*
27+
* For each task that has allocated memory from the heap, return totals for
28+
* allocations within regions matching one or more sets of capabilities.
29+
*
30+
* Optionally also return an array of structs providing details about each
31+
* block allocated by one or more requested tasks, or by all tasks.
32+
*
33+
* Returns the number of block detail structs returned.
34+
*/
35+
size_t heap_caps_get_per_task_info(heap_task_info_params_t *params)
36+
{
37+
heap_t *reg;
38+
heap_task_block_t *blocks = params->blocks;
39+
size_t count = *params->num_totals;
40+
size_t remaining = params->max_blocks;
41+
42+
// Clear out totals for any prepopulated tasks.
43+
if (params->totals) {
44+
for (size_t i = 0; i < count; ++i) {
45+
for (size_t type = 0; type < NUM_HEAP_TASK_CAPS; ++type) {
46+
params->totals[i].size[type] = 0;
47+
params->totals[i].count[type] = 0;
48+
}
49+
}
50+
}
51+
52+
SLIST_FOREACH(reg, &registered_heaps, next) {
53+
multi_heap_handle_t heap = reg->heap;
54+
if (heap == NULL) {
55+
continue;
56+
}
57+
58+
// Find if the capabilities of this heap region match on of the desired
59+
// sets of capabilities.
60+
uint32_t caps = get_all_caps(reg);
61+
uint32_t type;
62+
for (type = 0; type < NUM_HEAP_TASK_CAPS; ++type) {
63+
if ((caps & params->mask[type]) == params->caps[type]) {
64+
break;
65+
}
66+
}
67+
if (type == NUM_HEAP_TASK_CAPS) {
68+
continue;
69+
}
70+
71+
multi_heap_block_handle_t b = multi_heap_get_first_block(heap);
72+
multi_heap_internal_lock(heap);
73+
for ( ; b ; b = multi_heap_get_next_block(heap, b)) {
74+
if (multi_heap_is_free(b)) {
75+
continue;
76+
}
77+
void *p = multi_heap_get_block_address(b); // Safe, only arithmetic
78+
size_t bsize = multi_heap_get_allocated_size(heap, p); // Validates
79+
TaskHandle_t btask = (TaskHandle_t)multi_heap_get_block_owner(b);
80+
81+
// Accumulate per-task allocation totals.
82+
if (params->totals) {
83+
size_t i;
84+
for (i = 0; i < count; ++i) {
85+
if (params->totals[i].task == btask) {
86+
break;
87+
}
88+
}
89+
if (i < count) {
90+
params->totals[i].size[type] += bsize;
91+
params->totals[i].count[type] += 1;
92+
}
93+
else {
94+
if (count < params->max_totals) {
95+
params->totals[count].task = btask;
96+
params->totals[count].size[type] = bsize;
97+
params->totals[i].count[type] = 1;
98+
++count;
99+
}
100+
}
101+
}
102+
103+
// Return details about allocated blocks for selected tasks.
104+
if (blocks && remaining > 0) {
105+
if (params->tasks) {
106+
size_t i;
107+
for (i = 0; i < params->num_tasks; ++i) {
108+
if (btask == params->tasks[i]) {
109+
break;
110+
}
111+
}
112+
if (i == params->num_tasks) {
113+
continue;
114+
}
115+
}
116+
blocks->task = btask;
117+
blocks->address = p;
118+
blocks->size = bsize;
119+
++blocks;
120+
--remaining;
121+
}
122+
}
123+
multi_heap_internal_unlock(heap);
124+
}
125+
*params->num_totals = count;
126+
return params->max_blocks - remaining;
127+
}
128+
129+
#endif // CONFIG_HEAP_TASK_TRACKING
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
#pragma once
15+
16+
#ifdef CONFIG_HEAP_TASK_TRACKING
17+
18+
#ifdef __cplusplus
19+
extern "C" {
20+
#endif
21+
22+
// This macro controls how much space is provided for partitioning the per-task
23+
// heap allocation info according to one or more sets of heap capabilities.
24+
#define NUM_HEAP_TASK_CAPS 4
25+
26+
/** @brief Structure to collect per-task heap allocation totals partitioned by selected caps */
27+
typedef struct {
28+
TaskHandle_t task; ///< Task to which these totals belong
29+
size_t size[NUM_HEAP_TASK_CAPS]; ///< Total allocations partitioned by selected caps
30+
size_t count[NUM_HEAP_TASK_CAPS]; ///< Number of blocks partitioned by selected caps
31+
} heap_task_totals_t;
32+
33+
/** @brief Structure providing details about a block allocated by a task */
34+
typedef struct {
35+
TaskHandle_t task; ///< Task that allocated the block
36+
void *address; ///< User address of allocated block
37+
uint32_t size; ///< Size of the allocated block
38+
} heap_task_block_t;
39+
40+
/** @brief Structure to provide parameters to heap_caps_get_per_task_info
41+
*
42+
* The 'caps' and 'mask' arrays allow partitioning the per-task heap allocation
43+
* totals by selected sets of heap region capabilities so that totals for
44+
* multiple regions can be accumulated in one scan. The capabilities flags for
45+
* each region ANDed with mask[i] are compared to caps[i] in order; the
46+
* allocations in that region are added to totals->size[i] and totals->count[i]
47+
* for the first i that matches. To collect the totals without any
48+
* partitioning, set mask[0] and caps[0] both to zero. The allocation totals
49+
* are returned in the 'totals' array of heap_task_totals_t structs. To allow
50+
* easily comparing the totals array between consecutive calls, that array can
51+
* be left populated from one call to the next so the order of tasks is the
52+
* same even if some tasks have freed their blocks or have been deleted. The
53+
* number of blocks prepopulated is given by num_totals, which is updated upon
54+
* return. If there are more tasks with allocations than the capacity of the
55+
* totals array (given by max_totals), information for the excess tasks will be
56+
* not be collected. The totals array pointer can be NULL if the totals are
57+
* not desired.
58+
*
59+
* The 'tasks' array holds a list of handles for tasks whose block details are
60+
* to be returned in the 'blocks' array of heap_task_block_t structs. If the
61+
* tasks array pointer is NULL, block details for all tasks will be returned up
62+
* to the capacity of the buffer array, given by max_blocks. The function
63+
* return value tells the number of blocks filled into the array. The blocks
64+
* array pointer can be NULL if block details are not desired, or max_blocks
65+
* can be set to zero.
66+
*/
67+
typedef struct {
68+
int32_t caps[NUM_HEAP_TASK_CAPS]; ///< Array of caps for partitioning task totals
69+
int32_t mask[NUM_HEAP_TASK_CAPS]; ///< Array of masks under which caps must match
70+
TaskHandle_t *tasks; ///< Array of tasks whose block info is returned
71+
size_t num_tasks; ///< Length of tasks array
72+
heap_task_totals_t *totals; ///< Array of structs to collect task totals
73+
size_t *num_totals; ///< Number of task structs currently in array
74+
size_t max_totals; ///< Capacity of array of task totals structs
75+
heap_task_block_t *blocks; ///< Array of task block details structs
76+
size_t max_blocks; ///< Capacity of array of task block info structs
77+
} heap_task_info_params_t;
78+
79+
/**
80+
* @brief Return per-task heap allocation totals and lists of blocks.
81+
*
82+
* For each task that has allocated memory from the heap, return totals for
83+
* allocations within regions matching one or more sets of capabilities.
84+
*
85+
* Optionally also return an array of structs providing details about each
86+
* block allocated by one or more requested tasks, or by all tasks.
87+
*
88+
* @param params Structure to hold all the parameters for the function
89+
* (@see heap_task_info_params_t).
90+
* @return Number of block detail structs returned (@see heap_task_block_t).
91+
*/
92+
extern size_t heap_caps_get_per_task_info(heap_task_info_params_t *params);
93+
94+
#ifdef __cplusplus
95+
}
96+
#endif
97+
98+
#endif // CONFIG_HEAP_TASK_TRACKING

components/heap/multi_heap.c

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,14 @@ size_t multi_heap_free_size(multi_heap_handle_t heap)
5454
size_t multi_heap_minimum_free_size(multi_heap_handle_t heap)
5555
__attribute__((alias("multi_heap_minimum_free_size_impl")));
5656

57+
void *multi_heap_get_block_address(multi_heap_block_handle_t block)
58+
__attribute__((alias("multi_heap_get_block_address_impl")));
59+
60+
void *multi_heap_get_block_owner(multi_heap_block_handle_t block)
61+
{
62+
return NULL;
63+
}
64+
5765
#endif
5866

5967
#define ALIGN(X) ((X) & ~(sizeof(void *)-1))
@@ -279,6 +287,11 @@ static void split_if_necessary(heap_t *heap, heap_block_t *block, size_t size, h
279287
heap->free_bytes += block_data_size(new_block);
280288
}
281289

290+
void *multi_heap_get_block_address_impl(multi_heap_block_handle_t block)
291+
{
292+
return ((char *)block + offsetof(heap_block_t, data));
293+
}
294+
282295
size_t multi_heap_get_allocated_size_impl(multi_heap_handle_t heap, void *p)
283296
{
284297
heap_block_t *pb = get_block(p);
@@ -339,6 +352,27 @@ void inline multi_heap_internal_unlock(multi_heap_handle_t heap)
339352
MULTI_HEAP_UNLOCK(heap->lock);
340353
}
341354

355+
multi_heap_block_handle_t multi_heap_get_first_block(multi_heap_handle_t heap)
356+
{
357+
return &heap->first_block;
358+
}
359+
360+
multi_heap_block_handle_t multi_heap_get_next_block(multi_heap_handle_t heap, multi_heap_block_handle_t block)
361+
{
362+
heap_block_t *next = get_next_block(block);
363+
/* check for valid free last block to avoid assert in assert_valid_block */
364+
if (next == heap->last_block && is_last_block(next) && is_free(next)) {
365+
return NULL;
366+
}
367+
assert_valid_block(heap, next);
368+
return next;
369+
}
370+
371+
bool multi_heap_is_free(multi_heap_block_handle_t block)
372+
{
373+
return is_free(block);
374+
}
375+
342376
void *multi_heap_malloc_impl(multi_heap_handle_t heap, size_t size)
343377
{
344378
heap_block_t *best_block = NULL;

components/heap/multi_heap_internal.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
// limitations under the License.
1414
#pragma once
1515

16+
/* Opaque handle to a heap block */
17+
typedef const struct heap_block *multi_heap_block_handle_t;
18+
1619
/* Internal definitions for the "implementation" of the multi_heap API,
1720
as defined in multi_heap.c.
1821
@@ -28,6 +31,7 @@ void multi_heap_get_info_impl(multi_heap_handle_t heap, multi_heap_info_t *info)
2831
size_t multi_heap_free_size_impl(multi_heap_handle_t heap);
2932
size_t multi_heap_minimum_free_size_impl(multi_heap_handle_t heap);
3033
size_t multi_heap_get_allocated_size_impl(multi_heap_handle_t heap, void *p);
34+
void *multi_heap_get_block_address_impl(multi_heap_block_handle_t block);
3135

3236
/* Some internal functions for heap poisoning use */
3337

@@ -45,3 +49,20 @@ void multi_heap_internal_poison_fill_region(void *start, size_t size, bool is_fr
4549
void multi_heap_internal_lock(multi_heap_handle_t heap);
4650

4751
void multi_heap_internal_unlock(multi_heap_handle_t heap);
52+
53+
/* Some internal functions for heap debugging code to use */
54+
55+
/* Get the handle to the first (fixed free) block in a heap */
56+
multi_heap_block_handle_t multi_heap_get_first_block(multi_heap_handle_t heap);
57+
58+
/* Get the handle to the next block in a heap, with validation */
59+
multi_heap_block_handle_t multi_heap_get_next_block(multi_heap_handle_t heap, multi_heap_block_handle_t block);
60+
61+
/* Test if a heap block is free */
62+
bool multi_heap_is_free(const multi_heap_block_handle_t block);
63+
64+
/* Get the data address of a heap block */
65+
void *multi_heap_get_block_address(multi_heap_block_handle_t block);
66+
67+
/* Get the owner identification for a heap block */
68+
void *multi_heap_get_block_owner(multi_heap_block_handle_t block);

0 commit comments

Comments
 (0)