#define __DEBUG_C__ #ifndef DEBUG #define DEBUG #endif /***************************************************************************** /* Debug include files /*****************************************************************************/ #include #include "debug.h" /***************************************************************************** /* Local debug macro definitions /*****************************************************************************/ #define SIGNATURE_SIZE 2 #define HEADER_SIGNATURE 'rdHM' #define FOOTER_SIGNATURE 'rtFM' #define MEMFILL_VALUE 0xCC #define MEMORY_TRAP(msg) TRAP(TRAP_LEVEL_4, msg) /***************************************************************************** /* Data types local to debug /*****************************************************************************/ typedef struct { LIST_ENTRY ListEntry; PCHAR FileName; ULONG LineNumber; BOOL IsValid; ULONG BufferSize; ULONG Signature[SIGNATURE_SIZE]; } ALLOCHEADER, *PALLOCHEADER; typedef struct { ULONG Signature[SIGNATURE_SIZE]; } ALLOCFOOTER, *PALLOCFOOTER; typedef enum { MEM_ALLOC_NO_ERROR, MEM_ALLOC_HEADER_OVERFLOW, MEM_ALLOC_INVALID_HEADER, MEM_ALLOC_ALREADY_FREED, MEM_ALLOC_FOOTER_OVERFLOW } MEM_ALLOC_STATUS, *PMEM_ALLOC_STATUS; /***************************************************************************** /* Global debug data variables /*****************************************************************************/ INT Debug_TrapLevel = TRAP_LEVEL_1; BOOL Debug_TrapOn = FALSE; static INT Debug_ListCount = 0; /***************************************************************************** /* Local debug data variables /*****************************************************************************/ static LIST_ENTRY Debug_AllocList = { &Debug_AllocList, &Debug_AllocList }; /***************************************************************************** /* Local debug function declarations /*****************************************************************************/ BOOL Debug_IsEmptyList( IN PLIST_ENTRY ListHead ); VOID Debug_InsertIntoListAtTail( IN OUT PLIST_ENTRY ListHead, IN OUT PLIST_ENTRY NewEntry ); PLIST_ENTRY Debug_RemoveHeadOfList( IN PLIST_ENTRY ListHead ); VOID Debug_RemoveListEntry( IN PLIST_ENTRY OldEntry ); /***************************************************************************** /* Global debug function definitions /*****************************************************************************/ HGLOBAL __cdecl Debug_Alloc( IN PCHAR FileName, IN ULONG LineNumber, IN DWORD AllocSize ) { INT Index; DWORD BytesToAllocate; PALLOCHEADER NewBuffer; PALLOCFOOTER BufferFooter; BytesToAllocate = sizeof(ALLOCHEADER) + sizeof(ALLOCFOOTER) + AllocSize; NewBuffer = (PALLOCHEADER) GlobalAlloc(GPTR, BytesToAllocate); if (NULL != NewBuffer) { /* // Initialize the header structure */ NewBuffer -> FileName = FileName; NewBuffer -> LineNumber = LineNumber; NewBuffer -> IsValid = TRUE; NewBuffer -> BufferSize = AllocSize; for (Index = 0; Index < SIGNATURE_SIZE; Index++) NewBuffer -> Signature[Index] = HEADER_SIGNATURE; /* // Insert the new allocation block into the list of allocation blocks */ Debug_InsertIntoListAtTail(&Debug_AllocList, &(NewBuffer -> ListEntry) ); /* // Increment to the pointer that will get returned to the user and // initialize that to the fill value */ NewBuffer++; FillMemory(NewBuffer, AllocSize, MEMFILL_VALUE); /* // Initialize the footer on the memory block */ BufferFooter = (PALLOCFOOTER) (((PCHAR) NewBuffer) + AllocSize); for (Index = 0; Index < SIGNATURE_SIZE; Index++) BufferFooter -> Signature[Index] = FOOTER_SIGNATURE; } return (NewBuffer); } HGLOBAL __cdecl Debug_Realloc( IN PCHAR FileName, IN ULONG LineNumber, IN PALLOCHEADER MemoryBlock, IN DWORD AllocSize ) { MEM_ALLOC_STATUS AllocStatus; PALLOCHEADER OldBuffer; PALLOCHEADER NewBuffer; ASSERT(NULL != MemoryBlock); OldBuffer = MemoryBlock-1; Debug_ValidateMemoryAlloc(MemoryBlock, &AllocStatus ); NewBuffer = (PALLOCHEADER) Debug_Alloc(FileName, LineNumber, AllocSize ); if (NULL != NewBuffer) { CopyMemory(NewBuffer, MemoryBlock, OldBuffer -> BufferSize); Debug_Free(MemoryBlock); } return (NewBuffer); } HGLOBAL __cdecl Debug_Free( IN PALLOCHEADER Buffer ) { PALLOCHEADER Header; MEM_ALLOC_STATUS AllocStatus; Header = Buffer-1; Debug_ValidateMemoryAlloc(Buffer, &AllocStatus); /* // If the block has already been freed, we will simply return NULL. */ if (MEM_ALLOC_ALREADY_FREED == AllocStatus) return (NULL); /* // If we at least have an valid header, we can remove the header entry // from our list of allocated blocks */ if (MEM_ALLOC_INVALID_HEADER != AllocStatus) { Debug_RemoveListEntry(&(Header -> ListEntry)); Header -> IsValid = FALSE; } /* // Free the block of memory */ return (GlobalFree(Header)); } BOOL __cdecl Debug_ValidateMemoryAlloc( IN PALLOCHEADER Header, OUT PMEM_ALLOC_STATUS AllocStatus ) { INT Index; BOOL IsBadSignature; PALLOCFOOTER Footer; MEM_ALLOC_STATUS Status; /* // Begin by validating the header signature. If this is messed up there's // nothing else that can be done. Check each SIGNATURE entry // starting from the end to verify it is correct. If any of them are // not equal to HEADER_SIGNATURE then something went wrong. However, // if the first element in the array. Index 0 is valid, then we can // reasonably assume that the rest of the header is correct and we can // extract the appropriate info and display a more meaningful error // message. If that signature is not valid, however, there's no way // to insure that any of the other values are valid and therefore we // can't extract the valid info from the header. */ Status = MEM_ALLOC_NO_ERROR; Header--; IsBadSignature = FALSE; for (Index = SIGNATURE_SIZE-1; Index >= 0; Index--) { if (Header -> Signature[Index] != HEADER_SIGNATURE) { IsBadSignature = TRUE; break; } } if (IsBadSignature) { if (HEADER_SIGNATURE == Header -> Signature[0]) { static CHAR msg[1024]; sprintf(msg, "Header overflow in block: %p\nAllocated by %s on line %u\n", Header, Header -> FileName, Header -> LineNumber ); MEMORY_TRAP(msg); Status = MEM_ALLOC_HEADER_OVERFLOW; } else { static CHAR msg[1024]; sprintf(msg, "Corrupted allocation header in block: %p\nCannot extract allocation info", Header ); MEMORY_TRAP(msg); Status = MEM_ALLOC_INVALID_HEADER; } } /* // We passed the signature phase, let's validate the rest of the memory // allocation beginning with the header where we'll check the IsValid // flag to see if this chunk of memory has previously been freed. */ else if (!Header -> IsValid) { static CHAR msg[1024]; sprintf(msg, "Allocated block already been freed: %p\nAllocated by %s on line %u\n", Header, Header -> FileName, Header -> LineNumber ); MEMORY_TRAP(msg); Status = MEM_ALLOC_ALREADY_FREED; } else { /* // Next step is to verify that the footer is still correct and we did not // overflow our buffer on the other end. */ Footer = (PALLOCFOOTER) (((PCHAR) (Header+1)) + Header -> BufferSize); IsBadSignature = FALSE; for (Index = 0; Index < SIGNATURE_SIZE; Index++) { if (FOOTER_SIGNATURE != Footer -> Signature[Index]) { IsBadSignature = TRUE; break; } } if (IsBadSignature) { static CHAR msg[1024]; sprintf(msg, "Footer overflow in block: %p\nAllocated by %s on line %u\n", Header, Header -> FileName, Header -> LineNumber ); MEMORY_TRAP(msg); Status = MEM_ALLOC_FOOTER_OVERFLOW; return (FALSE); } } if (NULL != AllocStatus) *AllocStatus = Status; return (MEM_ALLOC_NO_ERROR == Status); } VOID __cdecl Debug_CheckForMemoryLeaks( ) { static CHAR msg[1024]; PALLOCHEADER Header; while (!Debug_IsEmptyList(&Debug_AllocList)) { Header = (PALLOCHEADER) Debug_RemoveHeadOfList(&Debug_AllocList); sprintf(msg, "Memory leak block: %p\nAllocated by %s on line %u\n", Header, Header -> FileName, Header -> LineNumber ); MEMORY_TRAP(msg); } return; } /***************************************************************************** /* Local debug function definitions /*****************************************************************************/ BOOL Debug_IsEmptyList( IN PLIST_ENTRY ListHead ) { return (ListHead -> Flink == ListHead); } VOID Debug_InsertIntoListAtTail( IN OUT PLIST_ENTRY ListHead, IN OUT PLIST_ENTRY NewEntry ) { PLIST_ENTRY OldTail; OldTail = ListHead -> Blink; NewEntry -> Flink = ListHead; NewEntry -> Blink = OldTail; OldTail -> Flink = NewEntry; ListHead -> Blink = NewEntry; Debug_ListCount++; return; } VOID Debug_RemoveListEntry( IN PLIST_ENTRY OldEntry ) { PLIST_ENTRY Flink; PLIST_ENTRY Blink; Flink = OldEntry -> Flink; Blink = OldEntry -> Blink; Blink -> Flink = OldEntry -> Flink; Flink -> Blink = OldEntry -> Blink; Debug_ListCount--; return; } PLIST_ENTRY Debug_RemoveHeadOfList( IN PLIST_ENTRY ListHead ) { PLIST_ENTRY OldHead; OldHead = ListHead -> Flink; ListHead -> Flink = OldHead -> Flink; OldHead -> Flink -> Blink = ListHead; Debug_ListCount--; return (OldHead); }