static volatile const char *BOINCrcsid="$Id$"; /*************************************************************************** * HeapCheck - a heap debugging library * Copyright (C) 2001 Thanassis Tsiodras (ttsiod@softlab.ntua.gr) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * ************************************************************************** * * HeapCheck 1.2 * ============= * * A simple debugging allocator, designed to help heap bug-hunting. * Like most debugging heap allocators, it helps you find memory leaks. * It also helps you where others can't: in catching invalid accesses. * It uses the paging system to create inaccessible pages by the side * of your normal allocations. This way, accesses outside the heap- * allocated space cause an exception (even READ accesses). This is much * better than heap debugging done with: * * 1. Visual C++ 6.0 Runtime Heap Debugging: * This only checks for heap corruption (i.e. only WRITINGS outside * the allocations) and even then, it only catches writings in the * NO_MANS_LAND_SIZE (default:4 bytes) on either side of the blocks. * The detection of these errors is done whenever the user (or the system) * calls _CrtCheckMemory(). HeapCheck catches READ accesses as well, * at the EXACT place they are made, and in a much larger * block (a page in i386 systems is 4096 bytes). * * 2. BoundsChecker: * This is a very good debugging tool, capable of catching almost * every bug. However, in order to perform all these checks, a lot * of CPU cycles are used, especially in instrumentation mode and * maximum memory checking (some programs won't work correctly when * they run so slowly, e.g. protocol stacks). HeapCheck catches only * heap-related errors, but it uses the paging hardware to do the * checking. The net result is that the HeapCheck debug versions of * programs run at almost the same speed as normal debug versions. * * I hope (but can't guarantee) that it will help you in your heap * bug hunting. It has definitely helped me with my projects. * Many thanks for the original idea go to the creator of UNIX's * Electric Fence, Bruce Perens. Oh, and make sure you use the * correct (Win2000) versions of the debugging libraries. I used: * * dbghelp.dll : 5.0.2195.1 * imagehlp.dll : 5.0.2195.1 * * Maybe SymGetLineFromAddr works with other combinations, but I used * these successfully under both NT 4.0sp6 and Win2000. * * Happy coding! * Thanassis Tsiodras * ttsiod@softlab.ntua.gr * ************************************************************************** */ /* * Remember, all of this code gets called only in _DEBUG builds! * Check swaps.h to see why... */ #ifdef _DEBUG #include #include #include /* Include prototypes for HeapCheck functions */ #include "HeapCheckPrototypes.h" /* * It is possible to write code that bypasses the allocation mechanisms of * the library, and allocates from the standard VC-heap. An example inside * the runtime library is the use of an enhanced 'new' operator in * iostrini.cpp (line 21) and cerrinit.cpp (line 21) where _new_crt is used. * _new_crt maps through a #define to a 'new' operator that takes extra * parameters. HeapCheck can't catch this call, so when the time comes for * the deletion of these blocks, the library's delete operator doesn't find * them in it's tables. It is capable though to understand that these are * VC-heap blocks, through the use of _CrtIsValidHeapPointer. * * If you #define NO_VC_HEAP_ERRS, the library won't complain for such * situations. This is the default, but if you use inside your code direct * calls to _malloc_dbg, _calloc_dbg, etc, you should disable this. */ #define NO_VC_HEAP_ERRS /* * Modify this for Alpha, PowerPC, etc. It is the page size used by * the virtual memory hardware. For Intel architectures, this is 4096 * bytes. For others, place a breakpoint in the call to HeapCheckStartup's * GetSystemInfo, and read the 'dwPageSize' field of the 'si' variable. */ #define PAGE_SIZE 4096 /* * Total concurrent allocations possible. If your program needs more, * you'll get an assertion telling you to increase this. */ #define MAX_ALLOCATIONS 16384 /* * Total heap available to the application. If your program needs more, * you'll get an assertion telling you to increase this. */ #define MAX_ALLOCATABLE_BLOCK 8*1048576 /* * Max depth of call stack looked up when our functions are called */ #define MAX_STACK_DEPTH 30 /* * Define PRINT_NUMERIC_EIP to get stack traces that boldly go * where no imagehlp.dll has gone before... Hope you have SoftIce... */ //#define PRINT_NUMERIC_EIP /* * Variables */ static DWORD dwAllocationGranularity = 0; static PCHAR pMemoryBase; static DWORD dwTotalPages; static DWORD dwFreePages; static struct tag_allocations { DWORD isFree; PCHAR pData; DWORD dwPages; DWORD dwLength; DWORD EIPs[MAX_STACK_DEPTH]; } allocations[MAX_ALLOCATIONS]; static BYTE pagesState[MAX_ALLOCATABLE_BLOCK/PAGE_SIZE]; static DWORD dwTotalPeakMemory = 0, dwTotalAllocated = 0; static CRITICAL_SECTION section; /* * Function prototypes. */ /* * Weird hack, in order to support 5 and 6 argument passing to _CrtDbgReport. * Unforunately, with VC 5.0/6.0, RPT macros go up until _RPT4! */ #ifndef _RPT5 #define _RPT5(rptno, msg, arg1, arg2, arg3, arg4, arg5) \ do { if ((1 == _CrtDbgReport(rptno, NULL, 0, NULL, msg, arg1, arg2, arg3, arg4, arg5))) \ _CrtDbgBreak(); } while (0) #endif #ifndef _RPT6 #define _RPT6(rptno, msg, arg1, arg2, arg3, arg4, arg5, arg6) \ do { if ((1 == _CrtDbgReport(rptno, NULL, 0, NULL, msg, arg1, arg2, arg3, arg4, arg5, arg6))) \ _CrtDbgBreak(); } while (0) #endif static void HeapCheckStartup(void); static void HeapCheckShutDown(void); /* * Simple allocator of pages that never frees. */ static void * FindConsecutiveFreePagesSimple(DWORD dwPages) { return pMemoryBase + PAGE_SIZE*(dwTotalPages - dwFreePages); } /* * Page allocator that searches for free pages. * Can be improved with better algorithms, but it's good enough for * most projects... */ static void * FindConsecutiveFreePages(DWORD dwPages) { DWORD i; PVOID pv; for(i=0; i= dwPages); } data = (char*)FindConsecutiveFreePages(dwPages); // OK, now make data-pages available... lpvResult = VirtualAlloc( (LPVOID) data, (dwPages-1)*PAGE_SIZE, MEM_COMMIT, PAGE_READWRITE); _ASSERTE(lpvResult != NULL ); VirtualProtect( (LPVOID) data, (dwPages-1)*PAGE_SIZE, PAGE_READWRITE, &dwOldProtection); // and fence-page untouchable! bResult = VirtualFree( (LPVOID) (data + (dwPages-1)*PAGE_SIZE), PAGE_SIZE, MEM_DECOMMIT); _ASSERTE(bResult == TRUE); data += (PAGE_SIZE - (blockSize % PAGE_SIZE)) % PAGE_SIZE; dwFreePages -= dwPages; allocations[dwSlot].pData = data; allocations[dwSlot].dwPages = dwPages; allocations[dwSlot].dwLength = blockSize; FillCallStack(allocations[dwSlot].EIPs); // Cause problems to users who think malloc zeroes block memory... FillMemory(data, blockSize, 0xCD); dwTotalAllocated += blockSize; if (dwTotalPeakMemory= dwPages); } data = (char*) FindConsecutiveFreePages(dwPages); // OK, now make fence-page untouchable... bResult = VirtualFree( (LPVOID) data, PAGE_SIZE, MEM_DECOMMIT); _ASSERTE(bResult == TRUE); // and data-pages available! lpvResult = VirtualAlloc( (LPVOID) (data+PAGE_SIZE), (dwPages-1)*PAGE_SIZE, MEM_COMMIT, PAGE_READWRITE); _ASSERTE(lpvResult != NULL ); VirtualProtect( (LPVOID) (data+PAGE_SIZE), (dwPages-1)*PAGE_SIZE, PAGE_READWRITE, &dwOldProtection); data += PAGE_SIZE; dwFreePages -= dwPages; allocations[dwSlot].pData = data; allocations[dwSlot].dwPages = dwPages; allocations[dwSlot].dwLength = blockSize; FillCallStack(allocations[dwSlot].EIPs); // Cause problems to users who think malloc zeroes block memory... FillMemory(data, blockSize, 0xCD); dwTotalAllocated += blockSize; if (dwTotalPeakMemory