This repository has been archived by the owner on Nov 11, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 96
/
u_port_heap.c
391 lines (338 loc) · 12 KB
/
u_port_heap.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
/*
* Copyright 2019-2024 u-blox
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/** @file
* @brief Default implementation of pUPortMalloc() / uPortFree() and
* uPortHeapAllocCount().
*/
#ifdef U_CFG_OVERRIDE
# include "u_cfg_override.h" // For a customer's configuration override
#endif
/* ----------------------------------------------------------------
* INCLUDE FILES
* -------------------------------------------------------------- */
#include "stdlib.h" // malloc()/free().
#include "stddef.h" // NULL, size_t etc.
#include "stdint.h" // int32_t etc.
#include "stdbool.h"
#include "string.h" // memset(), memcpy()
#include "ctype.h" // isprint()
#include "u_cfg_sw.h"
#include "stdint.h" // int32_t etc.
#include "u_compiler.h" // U_WEAK
#include "u_error_common.h"
#include "u_assert.h"
#include "u_port.h"
#include "u_port_os.h"
#include "u_port_heap.h"
#include "u_port_debug.h"
/* ----------------------------------------------------------------
* COMPILE-TIME MACROS
* -------------------------------------------------------------- */
#ifndef U_PORT_HEAP_GUARD
/** The uint32_t guard to put before and after each heap block, allowing
* us to check for overruns ("DEADBEEF", readable in a hex dump on
* a little-endian MCU, which they pretty much all are these days).
*/
# define U_PORT_HEAP_GUARD 0xefbeaddeUL
#endif
/** The size of #U_PORT_HEAP_GUARD; must be 4.
*/
#define U_PORT_HEAP_GUARD_SIZE sizeof(uint32_t)
/** The size of uPortHeapBlock_t, _without_ any packing on the end.
*/
#define U_PORT_HEAP_STRUCTURE_SIZE_NO_END_PACKING ((sizeof(void *) * 2) + (sizeof(int32_t) * 3))
#ifndef U_PORT_HEAP_BUFFER_OVERRUN_MARKER
/** The string to prefix a buffer overrun with.
*/
# define U_PORT_HEAP_BUFFER_OVERRUN_MARKER " *** BUFFER OVERRUN *** "
#endif
#ifndef U_PORT_HEAP_BUFFER_UNDERRUN_MARKER
/** The string to prefix a buffer underrun with.
*/
# define U_PORT_HEAP_BUFFER_UNDERRUN_MARKER " *** BUFFER UNDERRUN *** "
#endif
/** Local version of the lock helper, since this can't necessarily
* use the normal one.
*/
#define U_PORT_HEAP_MUTEX_LOCK(x) { gpMutexLock ? gpMutexLock(x) : uPortMutexLock(x)
/** Local version of the unlock helper, since this can't necessarily
* use the normal one.
*/
#define U_PORT_HEAP_MUTEX_UNLOCK(x) } gpMutexUnlock ? gpMutexUnlock(x) : uPortMutexUnlock(x)
/* ----------------------------------------------------------------
* TYPES
* -------------------------------------------------------------- */
/** Structure to track a memory block on the heap. When
* U_CFG_HEAP_MONITOR is defined and a heap allocation is done
* the allocation will be increased in size to include this at the
* start, then a guard of length #U_PORT_HEAP_GUARD_SIZE, then
* the actual callers memory block and finally another guard of
* length #U_PORT_HEAP_GUARD_SIZE on the end.
*
* IMPORTANT: this structure must work out to be a multiple of
* the worst-case pointer size of any supported platform (currently
* 64-bit for LINUX64) minus #U_PORT_HEAP_GUARD_SIZE, otherwise the
* memory passed back to the user will not be properly aligned;
* the structure is ordered as it is, with the smallest members last,
* in order to ensure this is the case.
*
* IF you change this structure make sure that
* #U_PORT_HEAP_STRUCTURE_SIZE_NO_END_PACKING is updated to match
* and make sure to use #U_PORT_HEAP_STRUCTURE_SIZE_NO_END_PACKING
* and not sizeof(uPortHeapBlock_t).
*/
typedef struct uPortHeapBlock_t {
struct uPortHeapBlock_t *pNext;
const char *pFile;
int32_t line;
int32_t size;
int32_t timeMilliseconds;
} uPortHeapBlock_t;
/* ----------------------------------------------------------------
* VARIABLES
* -------------------------------------------------------------- */
/** Variable to keep track of the total number of heap allocations
* outstanding.
*/
static int32_t gHeapAllocCount = 0;
/** Variable to keep track of the total number of perpetual heap
* allocations.
*/
static int32_t gHeapPerpetualAllocCount = 0;
#ifdef U_CFG_HEAP_MONITOR
/** Root of linked list of blocks on the heap.
*/
static uPortHeapBlock_t *gpHeapBlockList = NULL;
/** Mutex to protect the linked list.
*/
static uPortMutexHandle_t gMutex = NULL;
/** Hook for platform-specific mutex lock function, if required
* (e.g. the Linux port needs this).
*/
static int32_t (*gpMutexLock) (const uPortMutexHandle_t) = NULL;
/** Hook for platform-specific mutex unlock function, if required
* (e.g. the Linux port needs this).
*/
static int32_t (*gpMutexUnlock) (const uPortMutexHandle_t) = NULL;
#endif
/* ----------------------------------------------------------------
* STATIC FUNCTIONS
* -------------------------------------------------------------- */
#ifdef U_CFG_HEAP_MONITOR
static void printBlock(const char *pPrefix, const uPortHeapBlock_t *pBlock)
{
if (pPrefix == NULL) {
pPrefix = "";
}
if (pBlock != NULL) {
uPortLog("%sBLOCK address %p %6d byte(s) allocated by %s:%d @ %d.\n",
pPrefix,
pBlock + sizeof(uPortHeapBlock_t) + U_PORT_HEAP_GUARD_SIZE,
pBlock->size, pBlock->pFile, pBlock->line, pBlock->timeMilliseconds);
}
}
static void printMemory(const char *pMemory, size_t size)
{
for (size_t x = 0; x < size; x++, pMemory++) {
if (!isprint((int32_t) *pMemory)) {
uPortLog("[%02x]", (const unsigned char) *pMemory);
} else {
uPortLog("%c", *pMemory);
}
}
}
#endif
/* ----------------------------------------------------------------
* PUBLIC FUNCTIONS
* -------------------------------------------------------------- */
#ifdef U_CFG_HEAP_MONITOR
// For heap monitoring, pUPortMalloc() becomes static _pUPortMalloc()
// which we call internally from pUPortMallocMonitor().
static void *_pUPortMalloc(size_t sizeBytes)
#else
U_WEAK void *pUPortMalloc(size_t sizeBytes)
#endif
{
void *pMalloc = malloc(sizeBytes);
if (pMalloc != NULL) {
gHeapAllocCount++;
}
return pMalloc;
}
#ifdef U_CFG_HEAP_MONITOR
// The malloc call that replaces pUPortMalloc() when U_CFG_HEAP_MONITOR
// is defined,
void *pUPortMallocMonitor(size_t sizeBytes, const char *pFile,
int32_t line)
{
void *pMemory = NULL;
uPortHeapBlock_t *pBlock;
uPortHeapBlock_t *pBlockTmp;
char *pTmp;
size_t blockSizeBytes;
uint32_t heapGuard = U_PORT_HEAP_GUARD;
if (gMutex != NULL) {
// Allocate enough memory for what the caller wanted,
// plus our monitoring structure, plus two guards
blockSizeBytes = sizeBytes + U_PORT_HEAP_STRUCTURE_SIZE_NO_END_PACKING +
(U_PORT_HEAP_GUARD_SIZE * 2);
pBlock = (uPortHeapBlock_t *) _pUPortMalloc(blockSizeBytes);
if (pBlock != NULL) {
// Populate the structure
memset(pBlock, 0, sizeof(*pBlock));
pBlock->pFile = pFile;
pBlock->line = line;
pBlock->size = sizeBytes;
pBlock->timeMilliseconds = uPortGetTickTimeMs();
pTmp = ((char *) pBlock) + U_PORT_HEAP_STRUCTURE_SIZE_NO_END_PACKING;
// Add the opening guard after the block
memcpy(pTmp, &heapGuard, U_PORT_HEAP_GUARD_SIZE);
pTmp += U_PORT_HEAP_GUARD_SIZE;
// This is what we'll return to the caller
pMemory = pTmp;
// Add the closing guard on the very end
pTmp += sizeBytes;
memcpy(pTmp, &heapGuard, U_PORT_HEAP_GUARD_SIZE);
U_PORT_HEAP_MUTEX_LOCK(gMutex);
// Add the block to the list
pBlockTmp = gpHeapBlockList;
gpHeapBlockList = pBlock;
pBlock->pNext = pBlockTmp;
U_PORT_HEAP_MUTEX_UNLOCK(gMutex);
}
}
return pMemory;
}
#endif
U_WEAK void uPortFree(void *pMemory)
{
#ifdef U_CFG_HEAP_MONITOR
uPortHeapBlock_t *pBlock;
uPortHeapBlock_t *pBlockTmp1;
uPortHeapBlock_t *pBlockTmp2 = NULL;
char *pTmp;
const char *pMarker = NULL;
uint32_t heapGuard = U_PORT_HEAP_GUARD;
if ((pMemory != NULL) && (gMutex != NULL)) {
// Wind back to the start of the block
pBlock = (uPortHeapBlock_t *) (((char *) pMemory) - (U_PORT_HEAP_STRUCTURE_SIZE_NO_END_PACKING +
U_PORT_HEAP_GUARD_SIZE));
// Check the guards
pTmp = ((char *) pMemory) - U_PORT_HEAP_GUARD_SIZE;
if (memcmp(pTmp, &heapGuard, U_PORT_HEAP_GUARD_SIZE) != 0) {
pMarker = U_PORT_HEAP_BUFFER_UNDERRUN_MARKER;
uPortLog("%sexpected: ", pMarker);
printMemory((char *) &heapGuard, U_PORT_HEAP_GUARD_SIZE);
uPortLog(", got: ");
printMemory(pTmp, U_PORT_HEAP_GUARD_SIZE);
uPortLog("\n");
}
pTmp += U_PORT_HEAP_GUARD_SIZE + pBlock->size;
if (memcmp(pTmp, &heapGuard, U_PORT_HEAP_GUARD_SIZE) != 0) {
pMarker = U_PORT_HEAP_BUFFER_OVERRUN_MARKER;
uPortLog("%sexpected: ", pMarker);
printMemory((char *) &heapGuard, U_PORT_HEAP_GUARD_SIZE);
uPortLog(", got: ");
printMemory(pTmp, U_PORT_HEAP_GUARD_SIZE);
uPortLog("\n");
}
if (pMarker != NULL) {
printBlock(pMarker, pBlock);
}
U_PORT_HEAP_MUTEX_LOCK(gMutex);
// Remove the block from the list
pBlockTmp1 = gpHeapBlockList;
while ((pBlockTmp1 != NULL) && (pBlockTmp1 != pBlock)) {
pBlockTmp2 = pBlockTmp1;
pBlockTmp1 = pBlockTmp1->pNext;
}
if (pBlockTmp1 != NULL) {
if (pBlockTmp2 == NULL) {
// Must be at head
gpHeapBlockList = pBlockTmp1->pNext;
} else {
pBlockTmp2->pNext = pBlockTmp1->pNext;
}
}
U_PORT_HEAP_MUTEX_UNLOCK(gMutex);
U_ASSERT(pMarker == NULL);
// pBlock is what we need to free
pMemory = pBlock;
}
#endif
if (pMemory != NULL) {
gHeapAllocCount--;
}
free(pMemory);
}
U_WEAK int32_t uPortHeapAllocCount()
{
return gHeapAllocCount;
}
U_WEAK void uPortHeapPerpetualAllocAdd()
{
gHeapPerpetualAllocCount++;
}
U_WEAK int32_t uPortHeapPerpetualAllocCount()
{
return gHeapPerpetualAllocCount;
}
// Print out the contents of the heap.
int32_t uPortHeapDump(const char *pPrefix)
{
int32_t x = 0;
#ifdef U_CFG_HEAP_MONITOR
uPortHeapBlock_t *pBlock = gpHeapBlockList;
while (pBlock != NULL) {
printBlock(pPrefix, pBlock);
pBlock = pBlock->pNext;
x++;
}
if (pPrefix == NULL) {
pPrefix = "";
}
uPortLog("%s%d block(s).\n", pPrefix, x);
#else
(void) pPrefix;
#endif
return x;
}
// Initialise heap monitoring.
int32_t uPortHeapMonitorInit(int32_t (*pMutexCreate) (uPortMutexHandle_t *),
int32_t (*pMutexLock) (const uPortMutexHandle_t),
int32_t (*pMutexUnlock) (const uPortMutexHandle_t))
{
int32_t errorCode = (int32_t) U_ERROR_COMMON_SUCCESS;
#ifdef U_CFG_HEAP_MONITOR
if (gMutex == NULL) {
if (pMutexCreate != NULL) {
errorCode = pMutexCreate(&gMutex);
} else {
errorCode = uPortMutexCreate(&gMutex);
}
if (errorCode == 0) {
if (pMutexCreate == NULL) {
// Mark the call to uPortMutexCreate() as perpetual for accounting purposes
uPortOsResourcePerpetualAdd(U_PORT_OS_RESOURCE_TYPE_MUTEX);
}
gpMutexLock = pMutexLock;
gpMutexUnlock = pMutexUnlock;
}
}
#endif
return errorCode;
}
// End of file