/*++ Copyright (c) 1994 Microsoft Corporation Module Name: debug.c Abstract: This file contains debugging macros for the BINL server. Author: Madan Appiah (madana) 10-Sep-1993 Environment: User Mode - Win32 Revision History: --*/ #include "binl.h" #pragma hdrstop const char g_szTrue[] = "True"; const char g_szFalse[] = "False"; VOID DebugInitialize ( VOID ) { DWORD dwErr; HKEY KeyHandle; InitializeCriticalSection(&BinlGlobalDebugFileCritSect); BinlGlobalDebugFileHandle = NULL; BinlGlobalDebugFileMaxSize = DEFAULT_MAXIMUM_DEBUGFILE_SIZE; BinlGlobalDebugSharePath = NULL; // Read DebugFlags value dwErr = RegOpenKeyEx( HKEY_LOCAL_MACHINE, BINL_PARAMETERS_KEY, 0, KEY_QUERY_VALUE, &KeyHandle ); if ( dwErr == ERROR_SUCCESS ) { BinlGlobalDebugFlag = ReadDWord( KeyHandle, BINL_DEBUG_KEY, 0 ); BinlPrintDbg(( DEBUG_OPTIONS, "Debug Flags = 0x%08x.\n", BinlGlobalDebugFlag )); RegCloseKey( KeyHandle ); } #if DBG // break in the debugger if we are asked to do so. if(BinlGlobalDebugFlag & DEBUG_STARTUP_BRK) { BinlPrintDbg(( 0, "Stopping at DebugInitialize()'s DebugBreak( ).\n" )); DebugBreak(); } #endif // // Open debug log file. // if ( BinlGlobalDebugFlag & DEBUG_LOG_IN_FILE ) { BinlOpenDebugFile( FALSE ); // not a reopen. } } // DebugInitialize VOID DebugUninitialize ( VOID ) { EnterCriticalSection( &BinlGlobalDebugFileCritSect ); if ( BinlGlobalDebugFileHandle != NULL ) { CloseHandle( BinlGlobalDebugFileHandle ); BinlGlobalDebugFileHandle = NULL; } if( BinlGlobalDebugSharePath != NULL ) { BinlFreeMemory( BinlGlobalDebugSharePath ); BinlGlobalDebugSharePath = NULL; } LeaveCriticalSection( &BinlGlobalDebugFileCritSect ); DeleteCriticalSection( &BinlGlobalDebugFileCritSect ); } // DebugUninitialize VOID BinlOpenDebugFile( IN BOOL ReopenFlag ) /*++ Routine Description: Opens or re-opens the debug file Arguments: ReopenFlag - TRUE to indicate the debug file is to be closed, renamed, and recreated. Return Value: None --*/ { WCHAR LogFileName[500]; WCHAR BakFileName[500]; DWORD FileAttributes; DWORD PathLength; DWORD WinError; // // Close the handle to the debug file, if it is currently open // EnterCriticalSection( &BinlGlobalDebugFileCritSect ); if ( BinlGlobalDebugFileHandle != NULL ) { CloseHandle( BinlGlobalDebugFileHandle ); BinlGlobalDebugFileHandle = NULL; } LeaveCriticalSection( &BinlGlobalDebugFileCritSect ); // // make debug directory path first, if it is not made before. // if( BinlGlobalDebugSharePath == NULL ) { if ( !GetWindowsDirectoryW( LogFileName, sizeof(LogFileName)/sizeof(WCHAR) ) ) { BinlPrintDbg((DEBUG_ERRORS, "Window Directory Path can't be " "retrieved, %lu.\n", GetLastError() )); return; } // // check debug path length. // PathLength = wcslen(LogFileName) * sizeof(WCHAR) + sizeof(DEBUG_DIR) + sizeof(WCHAR); if( (PathLength + sizeof(DEBUG_FILE) > sizeof(LogFileName) ) || (PathLength + sizeof(DEBUG_BAK_FILE) > sizeof(BakFileName) ) ) { BinlPrintDbg((DEBUG_ERRORS, "Debug directory path (%ws) length is too long.\n", LogFileName)); goto ErrorReturn; } wcscat(LogFileName, DEBUG_DIR); // // copy debug directory name to global var. // BinlGlobalDebugSharePath = BinlAllocateMemory( (wcslen(LogFileName) + 1) * sizeof(WCHAR) ); if( BinlGlobalDebugSharePath == NULL ) { BinlPrintDbg((DEBUG_ERRORS, "Can't allocate memory for debug share " "(%ws).\n", LogFileName)); goto ErrorReturn; } wcscpy(BinlGlobalDebugSharePath, LogFileName); } else { wcscpy(LogFileName, BinlGlobalDebugSharePath); } // // Check this path exists. // FileAttributes = GetFileAttributesW( LogFileName ); if( FileAttributes == 0xFFFFFFFF ) { WinError = GetLastError(); if( WinError == ERROR_FILE_NOT_FOUND ) { // // Create debug directory. // if( !CreateDirectoryW( LogFileName, NULL) ) { BinlPrintDbg((DEBUG_ERRORS, "Can't create Debug directory (%ws), " "%lu.\n", LogFileName, GetLastError() )); goto ErrorReturn; } } else { BinlPrintDbg((DEBUG_ERRORS, "Can't Get File attributes(%ws), " "%lu.\n", LogFileName, WinError )); goto ErrorReturn; } } else { // // if this is not a directory. // if(!(FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { BinlPrintDbg((DEBUG_ERRORS, "Debug directory path (%ws) exists " "as file.\n", LogFileName)); goto ErrorReturn; } } // // Create the name of the old and new log file names // (VOID) wcscpy( BakFileName, LogFileName ); (VOID) wcscat( LogFileName, DEBUG_FILE ); (VOID) wcscat( BakFileName, DEBUG_BAK_FILE ); // // If this is a re-open, // delete the backup file, // rename the current file to the backup file. // if ( ReopenFlag ) { if ( !DeleteFile( BakFileName ) ) { WinError = GetLastError(); if ( WinError != ERROR_FILE_NOT_FOUND ) { BinlPrintDbg((DEBUG_ERRORS, "Cannot delete %ws (%ld)\n", BakFileName, WinError )); BinlPrintDbg((DEBUG_ERRORS, " Try to re-open the file.\n")); ReopenFlag = FALSE; // Don't truncate the file } } } if ( ReopenFlag ) { if ( !MoveFile( LogFileName, BakFileName ) ) { BinlPrintDbg((DEBUG_ERRORS, "Cannot rename %ws to %ws (%ld)\n", LogFileName, BakFileName, GetLastError() )); BinlPrintDbg((DEBUG_ERRORS, " Try to re-open the file.\n")); ReopenFlag = FALSE; // Don't truncate the file } } // // Open the file. // EnterCriticalSection( &BinlGlobalDebugFileCritSect ); BinlGlobalDebugFileHandle = CreateFileW( LogFileName, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, ReopenFlag ? CREATE_ALWAYS : OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); if ( BinlGlobalDebugFileHandle == INVALID_HANDLE_VALUE ) { BinlPrintDbg((DEBUG_ERRORS, "cannot open %ws ,\n", LogFileName )); LeaveCriticalSection( &BinlGlobalDebugFileCritSect ); goto ErrorReturn; } else { // Position the log file at the end (VOID) SetFilePointer( BinlGlobalDebugFileHandle, 0, NULL, FILE_END ); } LeaveCriticalSection( &BinlGlobalDebugFileCritSect ); return; ErrorReturn: BinlPrintDbg((DEBUG_ERRORS, " Debug output will be written to debug terminal.\n")); return; } VOID BinlPrintRoutine( IN DWORD DebugFlag, IN LPSTR Format, ... ) { #define MAX_PRINTF_LEN 1024 // Arbitrary. va_list arglist; char OutputBuffer[MAX_PRINTF_LEN]; ULONG length; DWORD BytesWritten; static BeginningOfLine = TRUE; static LineCount = 0; static TruncateLogFileInProgress = FALSE; LPSTR Text; // // If we aren't debugging this functionality, just return. // if ( DebugFlag != 0 && (BinlGlobalDebugFlag & DebugFlag) == 0 ) { return; } // // vsprintf isn't multithreaded + we don't want to intermingle output // from different threads. // EnterCriticalSection( &BinlGlobalDebugFileCritSect ); length = 0; // // Handle the beginning of a new line. // // if ( BeginningOfLine ) { // // If the log file is getting huge, // truncate it. // if ( BinlGlobalDebugFileHandle != NULL && !TruncateLogFileInProgress ) { // // Only check every 50 lines, // LineCount++; if ( LineCount >= 50 ) { DWORD FileSize; LineCount = 0; // // Is the log file too big? // FileSize = GetFileSize( BinlGlobalDebugFileHandle, NULL ); if ( FileSize == 0xFFFFFFFF ) { (void) DbgPrint( "[BinlServer] Cannot GetFileSize %ld\n", GetLastError() ); } else if ( FileSize > BinlGlobalDebugFileMaxSize ) { TruncateLogFileInProgress = TRUE; LeaveCriticalSection( &BinlGlobalDebugFileCritSect ); BinlOpenDebugFile( TRUE ); BinlPrint(( DEBUG_MISC, "Logfile truncated because it was larger than %ld bytes\n", BinlGlobalDebugFileMaxSize )); EnterCriticalSection( &BinlGlobalDebugFileCritSect ); TruncateLogFileInProgress = FALSE; } } } // Indicate this is a BINL server's message. length += (ULONG) sprintf( &OutputBuffer[length], "[BinlServer] " ); // // Put the thread id at the begining of the line. // if (BinlGlobalDebugFlag & DEBUG_THREAD) { DWORD threadId = GetCurrentThreadId(); length += (ULONG) sprintf( &OutputBuffer[length], "%08x ", threadId ); } // // Put the timestamp at the begining of the line. // if (BinlGlobalDebugFlag & DEBUG_TIMESTAMP) { SYSTEMTIME SystemTime; GetLocalTime( &SystemTime ); length += (ULONG) sprintf( &OutputBuffer[length], "%02u/%02u %02u:%02u:%02u ", SystemTime.wMonth, SystemTime.wDay, SystemTime.wHour, SystemTime.wMinute, SystemTime.wSecond ); } // // Indicate the type of message on the line // switch (DebugFlag) { case DEBUG_OPTIONS: Text = "OPTIONS"; break; case DEBUG_ERRORS: Text = "ERRORS"; break; case DEBUG_STOC: Text = "STOC"; break; case DEBUG_INIT: Text = "INIT"; break; case DEBUG_SCAVENGER: Text = "SCAVENGER"; break; case DEBUG_REGISTRY: Text = "REGISTRY"; break; case DEBUG_NETINF: Text = "NETINF"; break; case DEBUG_MISC: Text = "MISC"; break; case DEBUG_MESSAGE: Text = "MESSAGE"; break; case DEBUG_LOG_IN_FILE: Text = "LOG_IN_FILE"; break; default: Text = NULL; break; } if ( Text != NULL ) { length += (ULONG) sprintf( &OutputBuffer[length], "[%s] ", Text ); } } // // Put a the information requested by the caller onto the line // va_start(arglist, Format); length += (ULONG) vsprintf(&OutputBuffer[length], Format, arglist); BeginningOfLine = (length > 0 && OutputBuffer[length-1] == '\n' ); va_end(arglist); // Add in the fix for notepad users and fixing the assert BinlAssert(length < MAX_PRINTF_LEN - 1); if(BeginningOfLine){ OutputBuffer[length-1] = '\r'; OutputBuffer[length] = '\n'; length++; OutputBuffer[length] = '\0'; } // // Output to the debug terminal, // if the log file isn't open or we are asked to do so. // if ( (BinlGlobalDebugFileHandle == NULL) || !(BinlGlobalDebugFlag & DEBUG_LOG_IN_FILE) ) { // // Don't use DbgPrint(OutputBuffer) here because the buffer // might contain strings that printf will try to interpret // (e.g., NewMachineNamingPolicy = %1Fist%Last%#). // (void) DbgPrint( "%s", (PCH)OutputBuffer); // // Write the debug info to the log file. // } else { if ( !WriteFile( BinlGlobalDebugFileHandle, OutputBuffer, lstrlenA( OutputBuffer ), &BytesWritten, NULL ) ) { (void) DbgPrint( "%s", (PCH) OutputBuffer); } } LeaveCriticalSection( &BinlGlobalDebugFileCritSect ); } #if DBG VOID BinlAssertFailed( LPSTR FailedAssertion, LPSTR FileName, DWORD LineNumber, LPSTR Message ) /*++ Routine Description: Assertion failed. Arguments: FailedAssertion : FileName : LineNumber : Message : Return Value: none. --*/ { RtlAssert( FailedAssertion, FileName, (ULONG) LineNumber, (PCHAR) Message); BinlPrintDbg(( 0, "Assert @ %s \n", FailedAssertion )); BinlPrintDbg(( 0, "Assert Filename, %s \n", FileName )); BinlPrintDbg(( 0, "Line Num. = %ld.\n", LineNumber )); BinlPrintDbg(( 0, "Message is %s\n", Message )); #if DBG DebugBreak( ); #endif } VOID BinlDumpMessage( DWORD BinlDebugFlag, LPDHCP_MESSAGE BinlMessage ) /*++ Routine Description: This function dumps a DHCP packet in human readable form. Arguments: BinlDebugFlag - debug flag that indicates what we are debugging. BinlMessage - A pointer to a DHCP message. Return Value: None. --*/ { LPOPTION option; BYTE i; BinlPrintDbg(( BinlDebugFlag, "Binl message: \n\n")); BinlPrintDbg(( BinlDebugFlag, "Operation :")); if ( BinlMessage->Operation == BOOT_REQUEST ) { BinlPrintDbg(( BinlDebugFlag, "BootRequest\n")); } else if ( BinlMessage->Operation == BOOT_REPLY ) { BinlPrintDbg(( BinlDebugFlag, "BootReply\n")); } else { BinlPrintDbg(( BinlDebugFlag, "Unknown %x\n", BinlMessage->Operation)); return; } BinlPrintDbg(( BinlDebugFlag, "Hardware Address type : %d\n", BinlMessage->HardwareAddressType)); BinlPrintDbg(( BinlDebugFlag, "Hardware Address Length: %d\n", BinlMessage->HardwareAddressLength)); BinlPrintDbg(( BinlDebugFlag, "Hop Count : %d\n", BinlMessage->HopCount )); BinlPrintDbg(( BinlDebugFlag, "Transaction ID : %lx\n", BinlMessage->TransactionID )); BinlPrintDbg(( BinlDebugFlag, "Seconds Since Boot : %d\n", BinlMessage->SecondsSinceBoot )); BinlPrintDbg(( BinlDebugFlag, "Client IP Address : " )); BinlPrintDbg(( BinlDebugFlag, "%s\n", inet_ntoa(*(struct in_addr *)&BinlMessage->ClientIpAddress ) )); BinlPrintDbg(( BinlDebugFlag, "Your IP Address : " )); BinlPrintDbg(( BinlDebugFlag, "%s\n", inet_ntoa(*(struct in_addr *)&BinlMessage->YourIpAddress ) )); BinlPrintDbg(( BinlDebugFlag, "Server IP Address : " )); BinlPrintDbg(( BinlDebugFlag, "%s\n", inet_ntoa(*(struct in_addr *)&BinlMessage->BootstrapServerAddress ) )); BinlPrintDbg(( BinlDebugFlag, "Relay Agent IP Address : " )); BinlPrintDbg(( BinlDebugFlag, "%s\n", inet_ntoa(*(struct in_addr *)&BinlMessage->RelayAgentIpAddress ) )); BinlPrintDbg(( BinlDebugFlag, "Hardware Address : ")); for ( i = 0; i < BinlMessage->HardwareAddressLength; i++ ) { BinlPrintDbg(( BinlDebugFlag, "%2.2x", BinlMessage->HardwareAddress[i] )); } option = &BinlMessage->Option; BinlPrintDbg(( BinlDebugFlag, "\n\n")); BinlPrintDbg(( BinlDebugFlag, "Magic Cookie: ")); for ( i = 0; i < 4; i++ ) { BinlPrintDbg(( BinlDebugFlag, "%d ", *((LPBYTE)option)++ )); } BinlPrintDbg(( BinlDebugFlag, "\n\n")); BinlPrintDbg(( BinlDebugFlag, "Options:\n")); while ( option->OptionType != 255 ) { BinlPrintDbg(( BinlDebugFlag, "\tType = %d ", option->OptionType )); for ( i = 0; i < option->OptionLength; i++ ) { BinlPrintDbg(( BinlDebugFlag, "%2.2x", option->OptionValue[i] )); } BinlPrintDbg(( BinlDebugFlag, "\n")); if ( option->OptionType == OPTION_PAD || option->OptionType == OPTION_END ) { option = (LPOPTION)( (LPBYTE)(option) + 1); } else { option = (LPOPTION)( (LPBYTE)(option) + option->OptionLength + 2); } if ( (LPBYTE)option - (LPBYTE)BinlMessage > DHCP_MESSAGE_SIZE ) { BinlPrintDbg(( BinlDebugFlag, "End of message, but no trailer found!\n")); break; } } } #endif // DBG DWORD BinlReportEventW( DWORD EventID, DWORD EventType, DWORD NumStrings, DWORD DataLength, LPWSTR *Strings, LPVOID Data ) /*++ Routine Description: This function writes the specified (EventID) log at the end of the eventlog. Arguments: EventID - The specific event identifier. This identifies the message that goes with this event. EventType - Specifies the type of event being logged. This parameter can have one of the following values: Value Meaning EVENTLOG_ERROR_TYPE Error event EVENTLOG_WARNING_TYPE Warning event EVENTLOG_INFORMATION_TYPE Information event NumStrings - Specifies the number of strings that are in the array at 'Strings'. A value of zero indicates no strings are present. DataLength - Specifies the number of bytes of event-specific raw (binary) data to write to the log. If cbData is zero, no event-specific data is present. Strings - Points to a buffer containing an array of null-terminated strings that are merged into the message before displaying to the user. This parameter must be a valid pointer (or NULL), even if cStrings is zero. Data - Buffer containing the raw data. This parameter must be a valid pointer (or NULL), even if cbData is zero. Return Value: Returns the WIN32 extended error obtained by GetLastError(). NOTE : This function works slow since it calls the open and close eventlog source everytime. --*/ { HANDLE EventlogHandle; DWORD ReturnCode; // // open eventlog section. // EventlogHandle = RegisterEventSourceW(NULL, BINL_SERVER); if (EventlogHandle == NULL) { ReturnCode = GetLastError(); goto Cleanup; } // // Log the error code specified // if( !ReportEventW( EventlogHandle, (WORD)EventType, 0, // event category EventID, NULL, (WORD)NumStrings, DataLength, Strings, Data ) ) { ReturnCode = GetLastError(); goto Cleanup; } ReturnCode = NO_ERROR; Cleanup: if( EventlogHandle != NULL ) { DeregisterEventSource(EventlogHandle); } return ReturnCode; } DWORD BinlReportEventA( DWORD EventID, DWORD EventType, DWORD NumStrings, DWORD DataLength, LPSTR *Strings, LPVOID Data ) /*++ Routine Description: This function writes the specified (EventID) log at the end of the eventlog. Arguments: Source - Points to a null-terminated string that specifies the name of the module referenced. The node must exist in the registration database, and the module name has the following format: \EventLog\System\Lanmanworkstation EventID - The specific event identifier. This identifies the message that goes with this event. EventType - Specifies the type of event being logged. This parameter can have one of the following values: Value Meaning EVENTLOG_ERROR_TYPE Error event EVENTLOG_WARNING_TYPE Warning event EVENTLOG_INFORMATION_TYPE Information event NumStrings - Specifies the number of strings that are in the array at 'Strings'. A value of zero indicates no strings are present. DataLength - Specifies the number of bytes of event-specific raw (binary) data to write to the log. If cbData is zero, no event-specific data is present. Strings - Points to a buffer containing an array of null-terminated strings that are merged into the message before displaying to the user. This parameter must be a valid pointer (or NULL), even if cStrings is zero. Data - Buffer containing the raw data. This parameter must be a valid pointer (or NULL), even if cbData is zero. Return Value: Returns the WIN32 extended error obtained by GetLastError(). NOTE : This function works slow since it calls the open and close eventlog source everytime. --*/ { HANDLE EventlogHandle; DWORD ReturnCode; // // open eventlog section. // EventlogHandle = RegisterEventSourceW( NULL, BINL_SERVER ); if (EventlogHandle == NULL) { ReturnCode = GetLastError(); goto Cleanup; } // // Log the error code specified // if( !ReportEventA( EventlogHandle, (WORD)EventType, 0, // event category EventID, NULL, (WORD)NumStrings, DataLength, Strings, Data ) ) { ReturnCode = GetLastError(); goto Cleanup; } ReturnCode = NO_ERROR; Cleanup: if( EventlogHandle != NULL ) { DeregisterEventSource(EventlogHandle); } return ReturnCode; } VOID BinlServerEventLog( DWORD EventID, DWORD EventType, DWORD ErrorCode ) /*++ Routine Description: Logs an event in EventLog. Arguments: EventID - The specific event identifier. This identifies the message that goes with this event. EventType - Specifies the type of event being logged. This parameter can have one of the following values: Value Meaning EVENTLOG_ERROR_TYPE Error event EVENTLOG_WARNING_TYPE Warning event EVENTLOG_INFORMATION_TYPE Information event ErrorCode - Error Code to be Logged. Return Value: None. --*/ { DWORD Error; LPSTR Strings[1]; CHAR ErrorCodeOemString[32 + 1]; wsprintfA( ErrorCodeOemString, "%lu", ErrorCode ); Strings[0] = ErrorCodeOemString; Error = BinlReportEventA( EventID, EventType, 1, sizeof(ErrorCode), Strings, &ErrorCode ); if( Error != ERROR_SUCCESS ) { BinlPrintDbg(( DEBUG_ERRORS, "BinlReportEventW failed, %ld.\n", Error )); } return; } #if DBG==1 // // Memory allocation and tracking // LPVOID g_TraceMemoryTable = NULL; CRITICAL_SECTION g_TraceMemoryCS; #define DEBUG_OUTPUT_BUFFER_SIZE 1024 typedef struct _MEMORYBLOCK { HGLOBAL hglobal; struct _MEMORYBLOCK *pNext; LPCSTR pszModule; LPCSTR pszComment; LPCSTR pszFile; DWORD dwBytes; UINT uFlags; UINT uLine; } MEMORYBLOCK, *LPMEMORYBLOCK; // // Takes the filename and line number and put them into a string buffer. // // NOTE: the buffer is assumed to be of size DEBUG_OUTPUT_BUFFER_SIZE. // LPSTR dbgmakefilelinestring( LPSTR pszBuf, LPCSTR pszFile, UINT uLine ) { LPVOID args[2]; args[0] = (LPVOID) pszFile; args[1] = (LPVOID) UintToPtr( uLine ); FormatMessageA( FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY, "%1(%2!u!):", 0, // error code 0, // default language (LPSTR) pszBuf, // output buffer DEBUG_OUTPUT_BUFFER_SIZE, // size of buffer (va_list*) &args ); // arguments return pszBuf; } // // Adds a MEMORYBLOCK to the memory tracking list. // HGLOBAL DebugMemoryAdd( HGLOBAL hglobal, LPCSTR pszFile, UINT uLine, LPCSTR pszModule, UINT uFlags, DWORD dwBytes, LPCSTR pszComment ) { if ( hglobal ) { LPMEMORYBLOCK pmb = (LPMEMORYBLOCK) GlobalAlloc( GMEM_FIXED, sizeof(MEMORYBLOCK) ); if ( !pmb ) { GlobalFree( hglobal ); return NULL; } pmb->hglobal = hglobal; pmb->dwBytes = dwBytes; pmb->uFlags = uFlags; pmb->pszFile = pszFile; pmb->uLine = uLine; pmb->pszModule = pszModule; pmb->pszComment = pszComment; EnterCriticalSection( &g_TraceMemoryCS ); pmb->pNext = g_TraceMemoryTable; g_TraceMemoryTable = pmb; BinlPrintDbg((DEBUG_MEMORY, "DebugAlloc: 0x%08x alloced (%s)\n", hglobal, pmb->pszComment )); LeaveCriticalSection( &g_TraceMemoryCS ); } return hglobal; } // // Removes a MEMORYBLOCK to the memory tracking list. // void DebugMemoryDelete( HGLOBAL hglobal ) { if ( hglobal ) { LPMEMORYBLOCK pmbHead; LPMEMORYBLOCK pmbLast = NULL; EnterCriticalSection( &g_TraceMemoryCS ); pmbHead = g_TraceMemoryTable; while ( pmbHead && pmbHead->hglobal != hglobal ) { pmbLast = pmbHead; pmbHead = pmbLast->pNext; } if ( pmbHead ) { HGLOBAL *p; if ( pmbLast ) { pmbLast->pNext = pmbHead->pNext; } else { g_TraceMemoryTable = pmbHead->pNext; } BinlPrintDbg((DEBUG_MEMORY, "DebugFree: 0x%08x freed (%s)\n", hglobal, pmbHead->pszComment )); p = (HGLOBAL)((LPBYTE)hglobal + pmbHead->dwBytes - sizeof(HGLOBAL)); if ( *p != hglobal ) { BinlPrintDbg(((DEBUG_ERRORS|DEBUG_MEMORY), "DebugFree: Heap check FAILED for %0x08x %u bytes (%s).\n", hglobal, pmbHead->dwBytes, pmbHead->pszComment)); BinlPrintDbg(((DEBUG_ERRORS|DEBUG_MEMORY), "DebugFree: %s, Line: %u\n", pmbHead->pszFile, pmbHead->uLine )); BinlAssert( *p == hglobal ); } memset( hglobal, 0xFE, pmbHead->dwBytes ); memset( pmbHead, 0xFD, sizeof(sizeof(MEMORYBLOCK)) ); LocalFree( pmbHead ); } else { HGLOBAL *p; BinlPrintDbg(((DEBUG_ERRORS|DEBUG_MEMORY), "DebugFree: 0x%08x not found in memory table\n", hglobal )); memset( hglobal, 0xFE, (int)LocalSize( hglobal )); } LeaveCriticalSection( &g_TraceMemoryCS ); } } // // Allocates memory and adds the MEMORYBLOCK to the memory tracking list. // HGLOBAL DebugAlloc( LPCSTR pszFile, UINT uLine, LPCSTR pszModule, UINT uFlags, DWORD dwBytes, LPCSTR pszComment ) { HGLOBAL hglobal; DWORD dwBytesToAlloc = ROUND_UP_COUNT( dwBytes + sizeof(HGLOBAL), ALIGN_WORST); HGLOBAL *p; hglobal = GlobalAlloc( uFlags, dwBytesToAlloc ); if (hglobal == NULL) { return NULL; } p = (HGLOBAL)((LPBYTE)hglobal + dwBytesToAlloc - sizeof(HGLOBAL)); *p = hglobal; return DebugMemoryAdd( hglobal, pszFile, uLine, pszModule, uFlags, dwBytesToAlloc, pszComment ); } // // Remove the MEMORYBLOCK to the memory tracking list, memsets the // memory to 0xFE and then frees the memory. // HGLOBAL DebugFree( HGLOBAL hglobal ) { DebugMemoryDelete( hglobal ); return GlobalFree( hglobal ); } // // Checks the memory tracking list. If it is not empty, it will dump the // list and break. // void DebugMemoryCheck( ) { BOOL fFoundLeak = FALSE; LPMEMORYBLOCK pmb; EnterCriticalSection( &g_TraceMemoryCS ); pmb = g_TraceMemoryTable; while ( pmb ) { LPMEMORYBLOCK pTemp; LPVOID args[ 5 ]; CHAR szOutput[ DEBUG_OUTPUT_BUFFER_SIZE ]; CHAR szFileLine[ DEBUG_OUTPUT_BUFFER_SIZE ]; if ( fFoundLeak == FALSE ) { BinlPrintRoutine( 0, "\n***************************** Memory leak detected *****************************\n\n"); //BinlPrintRoutine( 0, "1234567890123456789012345678901234567890 1234567890 X 0x12345678 12345 1..."); BinlPrintRoutine( 0, "Filename(Line Number): Module Addr/HGLOBAL Size String\n"); fFoundLeak = TRUE; } args[0] = (LPVOID) pmb->hglobal; args[1] = (LPVOID) &szFileLine; args[2] = (LPVOID) pmb->pszComment; args[3] = (LPVOID) ULongToPtr( pmb->dwBytes ); args[4] = (LPVOID) pmb->pszModule; dbgmakefilelinestring( szFileLine, pmb->pszFile, pmb->uLine ); if ( !!(pmb->uFlags & GMEM_MOVEABLE) ) { FormatMessageA( FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY, "%2!-40s! %5!-10s! H 0x%1!08x! %4!-5u! \"%3\"\n", 0, // error code 0, // default language (LPSTR) &szOutput, // output buffer DEBUG_OUTPUT_BUFFER_SIZE, // size of buffer (va_list*) &args ); // arguments } else { FormatMessageA( FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY, "%2!-40s! %5!-10s! A 0x%1!08x! %4!-5u! \"%3\"\n", 0, // error code 0, // default language (LPSTR) &szOutput, // output buffer DEBUG_OUTPUT_BUFFER_SIZE, // size of buffer (va_list*) &args ); // arguments } BinlPrintRoutine( 0, szOutput ); pTemp = pmb; pmb = pmb->pNext; memset( pTemp, 0xFD, sizeof(MEMORYBLOCK) ); LocalFree( pTemp ); } if ( fFoundLeak == TRUE ) { BinlPrintRoutine( 0, "\n***************************** Memory leak detected *****************************\n\n"); } LeaveCriticalSection( &g_TraceMemoryCS ); //BinlAssert( !fFoundLeak ); } VOID DumpBuffer( PVOID Buffer, ULONG BufferSize ) /*++ Routine Description: Dumps the buffer content on to the debugger output. Arguments: Buffer: buffer pointer. BufferSize: size of the buffer. Return Value: none --*/ { #define NUM_CHARS 16 ULONG i, limit; CHAR TextBuffer[NUM_CHARS + 1]; PUCHAR BufferPtr = Buffer; DbgPrint("------------------------------------\n"); // // Hex dump of the bytes // limit = ((BufferSize - 1) / NUM_CHARS + 1) * NUM_CHARS; for (i = 0; i < limit; i++) { if (i < BufferSize) { DbgPrint("%02x ", (UCHAR)BufferPtr[i]); if (BufferPtr[i] < 31 ) { TextBuffer[i % NUM_CHARS] = '.'; } else if (BufferPtr[i] == '\0') { TextBuffer[i % NUM_CHARS] = ' '; } else { TextBuffer[i % NUM_CHARS] = (CHAR) BufferPtr[i]; } } else { DbgPrint(" "); TextBuffer[i % NUM_CHARS] = ' '; } if ((i + 1) % NUM_CHARS == 0) { TextBuffer[NUM_CHARS] = 0; DbgPrint(" %s\n", TextBuffer); } } DbgPrint("------------------------------------\n"); } #endif // DBG==1