VirtualBox

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

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

Biggest check-in ever. New source code headers for all (C) innotek files.

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