VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/alloc-ef.cpp@ 2981

Last change on this file since 2981 was 2981, checked in by vboxsync, 17 years ago

InnoTek -> innotek: all the headers and comments.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 16.9 KB
Line 
1/* $Id: alloc-ef.cpp 2981 2007-06-01 16:01:28Z vboxsync $ */
2/** @file
3 * innotek Portable Runtime - Memory Allocation, electric fence.
4 */
5
6/*
7 * Copyright (C) 2006-2007 innotek GmbH
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License as published by the Free Software Foundation,
13 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
14 * distribution. VirtualBox OSE is distributed in the hope that it will
15 * be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * If you received this file as part of a commercial VirtualBox
18 * distribution, then only the terms of your commercial VirtualBox
19 * license agreement apply instead of the previous paragraph.
20 */
21
22
23/*******************************************************************************
24* Header Files *
25*******************************************************************************/
26#include "alloc-ef.h"
27#include <iprt/log.h>
28#include <iprt/asm.h>
29#include <iprt/thread.h>
30#include <VBox/sup.h>
31#include <iprt/err.h>
32#include <errno.h>
33#include <stdio.h>
34#include <stdlib.h>
35
36#include <iprt/alloc.h>
37#include <iprt/assert.h>
38#include <iprt/param.h>
39#include <iprt/string.h>
40
41
42/*******************************************************************************
43* Global Variables *
44*******************************************************************************/
45#ifdef RTALLOC_EFENCE_TRACE
46/** Spinlock protecting the allthe blocks globals. */
47static volatile uint32_t g_BlocksLock;
48/** Tree tracking the allocations. */
49static AVLPVTREE g_BlocksTree;
50#ifdef RTALLOC_EFENCE_FREE_DELAYED
51/** Tail of the delayed blocks. */
52static volatile PRTMEMBLOCK g_pBlocksDelayHead;
53/** Tail of the delayed blocks. */
54static volatile PRTMEMBLOCK g_pBlocksDelayTail;
55/** Number of bytes in the delay list (includes fences). */
56static volatile size_t g_cbBlocksDelay;
57#endif
58#endif
59/** Array of pointers free watches for. */
60void *gapvRTMemFreeWatch[4] = {0};
61/** Enable logging of all freed memory. */
62bool gfRTMemFreeLog = false;
63
64
65/*******************************************************************************
66* Internal Functions *
67*******************************************************************************/
68/**
69 * Complains about something.
70 */
71static void rtmemComplain(const char *pszOp, const char *pszFormat, ...)
72{
73 va_list args;
74 fprintf(stderr, "RTMem error: %s: ", pszOp);
75 va_start(args, pszFormat);
76 vfprintf(stderr, pszFormat, args);
77 va_end(args);
78 AssertReleaseBreakpoint();
79}
80
81/**
82 * Log an event.
83 */
84static inline void rtmemLog(const char *pszOp, const char *pszFormat, ...)
85{
86#if 0
87 va_list args;
88 fprintf(stderr, "RTMem info: %s: ", pszOp);
89 va_start(args, pszFormat);
90 vfprintf(stderr, pszFormat, args);
91 va_end(args);
92#endif
93}
94
95
96#ifdef RTALLOC_EFENCE_TRACE
97
98/**
99 * Aquires the lock.
100 */
101static inline void rtmemBlockLock(void)
102{
103 unsigned c = 0;
104 while (!ASMAtomicCmpXchgU32(&g_BlocksLock, 1, 0))
105 RTThreadSleep(((++c) >> 2) & 31);
106}
107
108
109/**
110 * Releases the lock.
111 */
112static inline void rtmemBlockUnlock(void)
113{
114 Assert(g_BlocksLock == 1);
115 ASMAtomicXchgU32(&g_BlocksLock, 0);
116}
117
118
119/**
120 * Creates a block.
121 */
122static inline PRTMEMBLOCK rtmemBlockCreate(RTMEMTYPE enmType, size_t cb, void *pvCaller, unsigned iLine, const char *pszFile, const char *pszFunction)
123{
124 PRTMEMBLOCK pBlock = (PRTMEMBLOCK)malloc(sizeof(*pBlock));
125 if (pBlock)
126 {
127 pBlock->enmType = enmType;
128 pBlock->cb = cb;
129 pBlock->pvCaller = pvCaller;
130 pBlock->iLine = iLine;
131 pBlock->pszFile = pszFile;
132 pBlock->pszFunction = pszFunction;
133 }
134 return pBlock;
135}
136
137
138/**
139 * Frees a block.
140 */
141static inline void rtmemBlockFree(PRTMEMBLOCK pBlock)
142{
143 free(pBlock);
144}
145
146
147/**
148 * Insert a block from the tree.
149 */
150static inline void rtmemBlockInsert(PRTMEMBLOCK pBlock, void *pv)
151{
152 pBlock->Core.Key = pv;
153 rtmemBlockLock();
154 bool fRc = RTAvlPVInsert(&g_BlocksTree, &pBlock->Core);
155 rtmemBlockUnlock();
156 AssertRelease(fRc);
157}
158
159
160/**
161 * Remove a block from the tree and returns it to the caller.
162 */
163static inline PRTMEMBLOCK rtmemBlockRemove(void *pv)
164{
165 rtmemBlockLock();
166 PRTMEMBLOCK pBlock = (PRTMEMBLOCK)RTAvlPVRemove(&g_BlocksTree, pv);
167 rtmemBlockUnlock();
168 return pBlock;
169}
170
171/**
172 * Gets a block.
173 */
174static inline PRTMEMBLOCK rtmemBlockGet(void *pv)
175{
176 rtmemBlockLock();
177 PRTMEMBLOCK pBlock = (PRTMEMBLOCK)RTAvlPVGet(&g_BlocksTree, pv);
178 rtmemBlockUnlock();
179 return pBlock;
180}
181
182/**
183 * Dumps one allocation.
184 */
185static DECLCALLBACK(int) RTMemDumpOne(PAVLPVNODECORE pNode, void *pvUser)
186{
187 PRTMEMBLOCK pBlock = (PRTMEMBLOCK)pNode;
188 fprintf(stderr, "%p %08lx %p\n",
189 pBlock->Core.Key,
190 (long)pBlock->cb,
191 pBlock->pvCaller);
192 return 0;
193}
194
195/**
196 * Dumps the allocated blocks.
197 * This is something which you should call from gdb.
198 */
199extern "C" void RTMemDump(void);
200void RTMemDump(void)
201{
202 fprintf(stderr, "address size caller\n");
203 RTAvlPVDoWithAll(&g_BlocksTree, true, RTMemDumpOne, NULL);
204}
205
206
207#ifdef RTALLOC_EFENCE_FREE_DELAYED
208/**
209 * Insert a delayed block.
210 */
211static inline void rtmemBlockDelayInsert(PRTMEMBLOCK pBlock)
212{
213 size_t cbBlock = RT_ALIGN_Z(pBlock->cb, PAGE_SIZE) + RTALLOC_EFENCE_SIZE;
214 pBlock->Core.pRight = NULL;
215 pBlock->Core.pLeft = NULL;
216 rtmemBlockLock();
217 if (g_pBlocksDelayHead)
218 {
219 g_pBlocksDelayHead->Core.pLeft = (PAVLPVNODECORE)pBlock;
220 pBlock->Core.pRight = (PAVLPVNODECORE)g_pBlocksDelayHead;
221 g_pBlocksDelayHead = pBlock;
222 }
223 else
224 {
225 g_pBlocksDelayTail = pBlock;
226 g_pBlocksDelayHead = pBlock;
227 }
228 g_cbBlocksDelay += cbBlock;
229 rtmemBlockUnlock();
230}
231
232/**
233 * Removes a delayed block.
234 */
235static inline PRTMEMBLOCK rtmemBlockDelayRemove(void)
236{
237 PRTMEMBLOCK pBlock = NULL;
238 rtmemBlockLock();
239 if (g_cbBlocksDelay > RTALLOC_EFENCE_FREE_DELAYED)
240 {
241 pBlock = g_pBlocksDelayTail;
242 if (pBlock)
243 {
244 g_pBlocksDelayTail = (PRTMEMBLOCK)pBlock->Core.pLeft;
245 if (pBlock->Core.pLeft)
246 pBlock->Core.pLeft->pRight = NULL;
247 else
248 g_pBlocksDelayHead = NULL;
249 g_cbBlocksDelay -= RT_ALIGN_Z(pBlock->cb, PAGE_SIZE) + RTALLOC_EFENCE_SIZE;
250 }
251 }
252 rtmemBlockUnlock();
253 return pBlock;
254}
255
256
257#endif /* DELAY */
258
259#endif /* RTALLOC_EFENCE_TRACE */
260
261
262/**
263 * Internal allocator.
264 */
265RTDECL(void *) rtMemAlloc(const char *pszOp, RTMEMTYPE enmType, size_t cb, void *pvCaller, unsigned iLine, const char *pszFile, const char *pszFunction)
266{
267 /*
268 * Sanity.
269 */
270 if ( RT_ALIGN_Z(RTALLOC_EFENCE_SIZE, PAGE_SIZE) != RTALLOC_EFENCE_SIZE
271 && RTALLOC_EFENCE_SIZE <= 0)
272 {
273 rtmemComplain(pszOp, "Invalid E-fence size! %#x\n", RTALLOC_EFENCE_SIZE);
274 return NULL;
275 }
276 if (!cb)
277 {
278#if 0
279 rtmemComplain(pszOp, "Request of ZERO bytes allocation!\n");
280 return NULL;
281#else
282 cb = 1;
283#endif
284 }
285
286#ifdef RTALLOC_EFENCE_TRACE
287 /*
288 * Allocate the trace block.
289 */
290 PRTMEMBLOCK pBlock = rtmemBlockCreate(enmType, cb, pvCaller, iLine, pszFile, pszFunction);
291 if (!pBlock)
292 {
293 rtmemComplain(pszOp, "Failed to allocate trace block!\n");
294 return NULL;
295 }
296#endif
297
298 /*
299 * Allocate a block with page alignment space + the size of the E-fence.
300 */
301 size_t cbBlock = RT_ALIGN_Z(cb, PAGE_SIZE) + RTALLOC_EFENCE_SIZE;
302 void *pvBlock = RTMemPageAlloc(cbBlock);
303 if (pvBlock)
304 {
305 /*
306 * Calc the start of the fence and the user block
307 * and then change the page protection of the fence.
308 */
309 #ifdef RTALLOC_EFENCE_IN_FRONT
310 void *pvEFence = pvBlock;
311 void *pv = (char *)pvEFence + RTALLOC_EFENCE_SIZE;
312 #else
313 void *pvEFence = (char *)pvBlock + (cbBlock - RTALLOC_EFENCE_SIZE);
314 void *pv = (char *)pvEFence - cb;
315 #endif
316 int rc = RTMemProtect(pvEFence, RTALLOC_EFENCE_SIZE, RTMEM_PROT_NONE);
317 if (!rc)
318 {
319 #ifdef RTALLOC_EFENCE_TRACE
320 rtmemBlockInsert(pBlock, pv);
321 #endif
322 if (enmType == RTMEMTYPE_RTMEMALLOCZ)
323 memset(pv, 0, cb);
324#ifdef RTALLOC_EFENCE_FILLER
325 else
326 memset(pv, RTALLOC_EFENCE_FILLER, cb);
327#endif
328
329 rtmemLog(pszOp, "returns %p (pvBlock=%p cbBlock=%#x pvEFence=%p cb=%#x)\n", pv, pvBlock, cbBlock, pvEFence, cb);
330 return pv;
331 }
332 rtmemComplain(pszOp, "RTMemProtect failed, pvEFence=%p size %d, rc=%d\n", pvEFence, RTALLOC_EFENCE_SIZE, rc);
333 RTMemPageFree(pvBlock);
334 }
335 else
336 rtmemComplain(pszOp, "Failed to allocated %d bytes.\n", cb);
337
338#ifdef RTALLOC_EFENCE_TRACE
339 rtmemBlockFree(pBlock);
340#endif
341 return NULL;
342}
343
344
345/**
346 * Internal free.
347 */
348RTDECL(void) rtMemFree(const char *pszOp, RTMEMTYPE enmType, void *pv, void *pvCaller, unsigned iLine, const char *pszFile, const char *pszFunction)
349{
350 /*
351 * Simple case.
352 */
353 if (!pv)
354 return;
355
356 /*
357 * Check watch points.
358 */
359 for (unsigned i = 0; i < ELEMENTS(gapvRTMemFreeWatch); i++)
360 if (gapvRTMemFreeWatch[i] == pv)
361 AssertReleaseBreakpoint();
362
363#ifdef RTALLOC_EFENCE_TRACE
364 /*
365 * Find the block.
366 */
367 PRTMEMBLOCK pBlock = rtmemBlockRemove(pv);
368 if (pBlock)
369 {
370 if (gfRTMemFreeLog)
371 RTLogPrintf("RTMem %s: pv=%p pvCaller=%p cb=%#x\n", pszOp, pv, pvCaller, pBlock->cb);
372
373 #ifdef RTALLOC_EFENCE_FREE_FILL
374 /*
375 * Fill the user part of the block.
376 */
377 memset(pv, RTALLOC_EFENCE_FREE_FILL, pBlock->cb);
378 #endif
379
380 #if defined(RTALLOC_EFENCE_FREE_DELAYED) && RTALLOC_EFENCE_FREE_DELAYED > 0
381 /*
382 * We're doing delayed freeing.
383 * That means we'll expand the E-fence to cover the entire block.
384 */
385 int rc = RTMemProtect(pv, pBlock->cb, RTMEM_PROT_NONE);
386 if (RT_SUCCESS(rc))
387 {
388 /*
389 * Insert it into the free list and process pending frees.
390 */
391 rtmemBlockDelayInsert(pBlock);
392 while ((pBlock = rtmemBlockDelayRemove()) != NULL)
393 {
394 pv = pBlock->Core.Key;
395 #ifdef RTALLOC_EFENCE_IN_FRONT
396 void *pvBlock = (char *)pv - RTALLOC_EFENCE_SIZE;
397 #else
398 void *pvBlock = (void *)((uintptr_t)pv & ~PAGE_OFFSET_MASK);
399 #endif
400 size_t cbBlock = RT_ALIGN_Z(pBlock->cb, PAGE_SIZE) + RTALLOC_EFENCE_SIZE;
401 rc = RTMemProtect(pvBlock, cbBlock, RTMEM_PROT_READ | RTMEM_PROT_WRITE);
402 if (RT_SUCCESS(rc))
403 RTMemPageFree(pvBlock);
404 else
405 rtmemComplain(pszOp, "RTMemProtect(%p, %#x, RTMEM_PROT_READ | RTMEM_PROT_WRITE) -> %d\n", pvBlock, cbBlock, rc);
406 rtmemBlockFree(pBlock);
407 }
408 }
409 else
410 rtmemComplain(pszOp, "Failed to expand the efence of pv=%p cb=%d, rc=%d.\n", pv, pBlock, rc);
411
412 #else /* !RTALLOC_EFENCE_FREE_DELAYED */
413
414 /*
415 * Turn of the E-fence and free it.
416 */
417 #ifdef RTALLOC_EFENCE_IN_FRONT
418 void *pvBlock = (char *)pv - RTALLOC_EFENCE_SIZE;
419 void *pvEFence = pvBlock;
420 #else
421 void *pvBlock = (void *)((uintptr_t)pv & ~PAGE_OFFSET_MASK);
422 void *pvEFence = (char *)pv + pBlock->cb;
423 #endif
424 int rc = RTMemProtect(pvEFence, RTALLOC_EFENCE_SIZE, RTMEM_PROT_READ | RTMEM_PROT_WRITE);
425 if (RT_SUCCESS(rc))
426 RTMemPageFree(pvBlock);
427 else
428 rtmemComplain(pszOp, "RTMemProtect(%p, %#x, RTMEM_PROT_READ | RTMEM_PROT_WRITE) -> %d\n", pvEFence, RTALLOC_EFENCE_SIZE, rc);
429 rtmemBlockFree(pBlock);
430
431 #endif /* !RTALLOC_EFENCE_FREE_DELAYED */
432 }
433 else
434 rtmemComplain(pszOp, "pv=%p not found! Incorrect free!\n", pv);
435
436#else /* !RTALLOC_EFENCE_TRACE */
437
438 /*
439 * We have no size tracking, so we're not doing any freeing because
440 * we cannot if the E-fence is after the block.
441 * Let's just expand the E-fence to the first page of the user bit
442 * since we know that it's around.
443 */
444 int rc = RTMemProtect((void *)((uintptr_t)pv & ~PAGE_OFFSET_MASK), PAGE_SIZE, RTMEM_PROT_NONE);
445 if (RT_FAILURE(rc))
446 rtmemComplain(pszOp, "RTMemProtect(%p, PAGE_SIZE, RTMEM_PROT_NONE) -> %d\n", (void *)((uintptr_t)pv & ~PAGE_OFFSET_MASK), rc);
447#endif /* !RTALLOC_EFENCE_TRACE */
448}
449
450/**
451 * Internal realloc.
452 */
453RTDECL(void *) rtMemRealloc(const char *pszOp, RTMEMTYPE enmType, void *pvOld, size_t cbNew, void *pvCaller, unsigned iLine, const char *pszFile, const char *pszFunction)
454{
455 /*
456 * Allocate new and copy.
457 */
458 if (!pvOld)
459 return rtMemAlloc(pszOp, enmType, cbNew, pvCaller, iLine, pszFile, pszFunction);
460 if (!cbNew)
461 {
462 rtMemFree(pszOp, RTMEMTYPE_RTMEMREALLOC, pvOld, pvCaller, iLine, pszFile, pszFunction);
463 return NULL;
464 }
465
466#ifdef RTALLOC_EFENCE_TRACE
467
468 /*
469 * Get the block, allocate the new, copy the data, free the old one.
470 */
471 PRTMEMBLOCK pBlock = rtmemBlockGet(pvOld);
472 if (pBlock)
473 {
474 void *pvRet = rtMemAlloc(pszOp, enmType, cbNew, pvCaller, iLine, pszFile, pszFunction);
475 if (pvRet)
476 {
477 memcpy(pvRet, pvOld, RT_MIN(cbNew, pBlock->cb));
478 rtMemFree(pszOp, RTMEMTYPE_RTMEMREALLOC, pvOld, pvCaller, iLine, pszFile, pszFunction);
479 }
480 return pvRet;
481 }
482 else
483 rtmemComplain(pszOp, "pvOld=%p was not found!\n", pvOld);
484 return NULL;
485
486#else /* !RTALLOC_EFENCE_TRACE */
487
488 rtmemComplain(pszOp, "Not supported if RTALLOC_EFENCE_TRACE isn't defined!\n");
489 return NULL;
490
491#endif /* !RTALLOC_EFENCE_TRACE */
492}
493
494
495
496
497/**
498 * Same as RTMemTmpAlloc() except that it's fenced.
499 *
500 * @returns Pointer to the allocated memory.
501 * @returns NULL on failure.
502 * @param cb Size in bytes of the memory block to allocate.
503 */
504RTDECL(void *) RTMemEfTmpAlloc(size_t cb)
505{
506 return RTMemEfAlloc(cb);
507}
508
509
510/**
511 * Same as RTMemTmpAllocZ() except that it's fenced.
512 *
513 * @returns Pointer to the allocated memory.
514 * @returns NULL on failure.
515 * @param cb Size in bytes of the memory block to allocate.
516 */
517RTDECL(void *) RTMemEfTmpAllocZ(size_t cb)
518{
519 return RTMemEfAllocZ(cb);
520}
521
522
523/**
524 * Same as RTMemTmpFree() except that it's for fenced memory.
525 *
526 * @param pv Pointer to memory block.
527 */
528RTDECL(void) RTMemEfTmpFree(void *pv)
529{
530 RTMemEfFree(pv);
531}
532
533
534/**
535 * Same as RTMemAlloc() except that it's fenced.
536 *
537 * @returns Pointer to the allocated memory. Free with RTMemEfFree().
538 * @returns NULL on failure.
539 * @param cb Size in bytes of the memory block to allocate.
540 */
541RTDECL(void *) RTMemEfAlloc(size_t cb)
542{
543 return rtMemAlloc("Alloc", RTMEMTYPE_RTMEMALLOC, cb, ((void **)&cb)[-1], 0, NULL, NULL);
544}
545
546
547/**
548 * Same as RTMemAllocZ() except that it's fenced.
549 *
550 * @returns Pointer to the allocated memory.
551 * @returns NULL on failure.
552 * @param cb Size in bytes of the memory block to allocate.
553 */
554RTDECL(void *) RTMemEfAllocZ(size_t cb)
555{
556 return rtMemAlloc("AllocZ", RTMEMTYPE_RTMEMALLOCZ, cb, ((void **)&cb)[-1], 0, NULL, NULL);
557}
558
559
560/**
561 * Same as RTMemRealloc() except that it's fenced.
562 *
563 * @returns Pointer to the allocated memory.
564 * @returns NULL on failure.
565 * @param pvOld The memory block to reallocate.
566 * @param cbNew The new block size (in bytes).
567 */
568RTDECL(void *) RTMemEfRealloc(void *pvOld, size_t cbNew)
569{
570 return rtMemRealloc("Realloc", RTMEMTYPE_RTMEMREALLOC, pvOld, cbNew, ((void **)&pvOld)[-1], 0, NULL, NULL);
571}
572
573
574/**
575 * Free memory allocated by any of the RTMemEf* allocators.
576 *
577 * @param pv Pointer to memory block.
578 */
579RTDECL(void) RTMemEfFree(void *pv)
580{
581 if (pv)
582 rtMemFree("Free", RTMEMTYPE_RTMEMFREE, pv, ((void **)&pv)[-1], 0, NULL, NULL);
583}
584
585
586/**
587 * Same as RTMemDup() except that it's fenced.
588 *
589 * @returns New heap block with the duplicate data.
590 * @returns NULL if we're out of memory.
591 * @param pvSrc The memory to duplicate.
592 * @param cb The amount of memory to duplicate.
593 */
594RTDECL(void *) RTMemEfDup(const void *pvSrc, size_t cb)
595{
596 void *pvDst = RTMemEfAlloc(cb);
597 if (pvDst)
598 memcpy(pvDst, pvSrc, cb);
599 return pvDst;
600}
601
602
603/**
604 * Same as RTMemDupEx except that it's fenced.
605 *
606 * @returns New heap block with the duplicate data.
607 * @returns NULL if we're out of memory.
608 * @param pvSrc The memory to duplicate.
609 * @param cbSrc The amount of memory to duplicate.
610 * @param cbExtra The amount of extra memory to allocate and zero.
611 */
612RTDECL(void *) RTMemEfDupEx(const void *pvSrc, size_t cbSrc, size_t cbExtra)
613{
614 void *pvDst = RTMemEfAlloc(cbSrc + cbExtra);
615 if (pvDst)
616 {
617 memcpy(pvDst, pvSrc, cbSrc);
618 memset((uint8_t *)pvDst + cbSrc, 0, cbExtra);
619 }
620 return pvDst;
621}
622
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette