Skip to content

Commit 2abba13

Browse files
committed
Merge branch 'feature/heap_task_tracking_pr1498' into 'master'
heap: Add task tracking features (PR 1498) See merge request idf/esp-idf!1960
2 parents ab82ce3 + bc2879a commit 2abba13

File tree

10 files changed

+336
-14
lines changed

10 files changed

+336
-14
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: 35 additions & 1 deletion
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,12 +287,17 @@ 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);
285298

286299
assert_valid_block(heap, pb);
287-
MULTI_HEAP_ASSERT(!is_free(pb), pb); // block should be free
300+
MULTI_HEAP_ASSERT(!is_free(pb), pb); // block shouldn't be free
288301
return block_data_size(pb);
289302
}
290303

@@ -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)