VirtualBox

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

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

The Giant CDDL Dual-License Header Change.

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