451 lines
11 KiB
C
451 lines
11 KiB
C
#define __DEBUG_C__
|
|
|
|
#ifndef DEBUG
|
|
#define DEBUG
|
|
#endif
|
|
|
|
/*****************************************************************************
|
|
/* Debug include files
|
|
/*****************************************************************************/
|
|
#include <stdio.h>
|
|
#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);
|
|
}
|