// Copyright (c) 1996-1999 Microsoft Corporation //+------------------------------------------------------------------------- // // Microsoft Windows // // File: debug.cxx // // Contents: Debug support. // // Classes: // // Functions: // // // // History: 18-Nov-96 BillMo Created. // // Notes: // // Codework: // //-------------------------------------------------------------------------- #include "pch.cxx" #pragma hdrstop #include "trklib.hxx" #include // vsprintf #if DBG == 1 //CFailPoint * CFailPoint::g_pList = NULL; #define TRKSVC_LOG_FILE TEXT("%SystemRoot%\\debug\\trksvcs.log") CHAR TrkGlobalDebugBuffer[ 1024]; // arbitrary DWORD TrkGlobalDebug; HANDLE g_LogFile = INVALID_HANDLE_VALUE; // This critical section is used to serialize simultaneous dbgout calls. CRITICAL_SECTION g_csDebugOut; LONG g_cCritSecInit = 0; CHAR g_szDebugBuffer[ 1024]; // arbitrary TCHAR g_tszDebugBuffer[ 1024 ]; ULONG g_grfDebugFlags = 0; ULONG g_grfLogFlags = 0; CHAR g_szModuleName[ MAX_PATH ] = { "" }; LONG g_cInitializations = 0; VOID TrkDebugDelete( VOID) { // This isn't thread safe, so we won't ever delete it. // It just means there's a one-time leak in the chk build // when the service gets stopped. //if( 0 == InterlockedDecrement( &g_cCritSecInit )) // DeleteCriticalSection( &g_csDebugOut); InterlockedDecrement(&g_cInitializations); if( INVALID_HANDLE_VALUE != g_LogFile ) { CloseHandle( g_LogFile ); g_LogFile = INVALID_HANDLE_VALUE; } } VOID TrkDebugCreate( ULONG grfLogFlags, CHAR *pszModuleName ) { TCHAR Buffer[ MAX_PATH]; DWORD Length; if( 1 < InterlockedIncrement(&g_cInitializations) ) return; strncpy( g_szModuleName, pszModuleName, sizeof(g_szModuleName) ); g_szModuleName[ sizeof(g_szModuleName) - 1 ] = TEXT('\0'); if( 1 == InterlockedIncrement( &g_cCritSecInit )) InitializeCriticalSection( &g_csDebugOut ); if( (TRK_DBG_FLAGS_WRITE_TO_FILE | TRK_DBG_FLAGS_APPEND_TO_FILE) & grfLogFlags ) { // // Length returned by ExpandEnvironmentalStrings includes terminating // NULL byte. // Length = ExpandEnvironmentStrings( TRKSVC_LOG_FILE, Buffer, sizeof( Buffer)); if ( Length == 0) { TrkLog(( TRKDBG_ERROR, TEXT("Error=%d"), GetLastError())); return; } if ( Length > sizeof( Buffer) || Length != _tcslen(Buffer) + 1) { Beep(2000,2000); TrkLog(( TRKDBG_ERROR, TEXT("Buffer=%x, Length = %d"), Buffer, Length)); return; } g_LogFile = CreateFile( Buffer, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL, (TRK_DBG_FLAGS_APPEND_TO_FILE & grfLogFlags) ? OPEN_ALWAYS : CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); if ( g_LogFile == INVALID_HANDLE_VALUE ) { TCHAR tsz[ 2 * MAX_PATH ]; _stprintf( tsz, TEXT("Cannot open %s (%lu)\n"), Buffer, GetLastError() ); OutputDebugString( tsz ); return; } if( TRK_DBG_FLAGS_APPEND_TO_FILE & grfLogFlags ) { // // Position the log file at the end // (VOID) SetFilePointer( g_LogFile, 0, NULL, FILE_END ); } else { // // Truncate the file // SetFilePointer( g_LogFile, 0, NULL, FILE_BEGIN ); SetEndOfFile( g_LogFile ); } } g_grfLogFlags = grfLogFlags; } VOID TrkLogRoutine( IN DWORD DebugFlag, IN LPTSTR Format, ... ) { LONG l = GetLastError(); va_list Arguments; va_start( Arguments, Format ); TrkLogErrorRoutineInternal( DebugFlag, NULL, Format, Arguments ); SetLastError(l); } VOID TrkLogErrorRoutine( IN DWORD DebugFlag, IN HRESULT hr, IN LPTSTR Format, ... ) { CHAR szHR[8]; va_list Arguments; va_start( Arguments, Format ); sprintf( szHR, "%08X", hr ); TrkLogErrorRoutineInternal( DebugFlag, szHR, Format, Arguments ); } VOID TrkLogErrorRoutineInternal( IN DWORD DebugFlag, IN LPSTR pszHR, IN LPTSTR Format, IN va_list Arguments ) { // va_list arglist; ULONG length = 0; DWORD BytesWritten; ULONG iFormatStart = 0; // Skip if TrkDebugCreate hasn't been called yet. if( 0 == g_grfLogFlags ) return; // // If we aren't debugging this type of message and it's not an // error, then we're done. // if( !( (g_grfDebugFlags | TRKDBG_ERROR ) & DebugFlag ) ) return; // // vsprintf isn't multithreaded + we don't want to intermingle output // from different threads. Therefore we can use just a single output // debug buffer. // EnterCriticalSection( &g_csDebugOut ); // // Prefix the line with any newlines // for( iFormatStart = 0; TEXT('\n') == Format[iFormatStart]; iFormatStart++ ) g_szDebugBuffer[length++] = '\n'; // // Put our name/time at the beginning of the line. // CFILETIME cftLocal(0); cftLocal.SetToLocal(); SYSTEMTIME st = static_cast( cftLocal ); length += (ULONG) sprintf( &g_szDebugBuffer[length], "[%s/%02d%02d%02d.%03d:%03x] ", g_szModuleName, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds, GetCurrentThreadId() ); // // Put the information requested by the caller onto the line // _vstprintf( g_tszDebugBuffer, &Format[iFormatStart], Arguments ); tcstombs( &g_szDebugBuffer[length], g_tszDebugBuffer ); length = strlen( g_szDebugBuffer ); if( NULL != pszHR ) length += (ULONG) sprintf( &g_szDebugBuffer[length], " %s", pszHR ); length += (ULONG) sprintf( &g_szDebugBuffer[length], "\n" ); TrkAssert(length <= sizeof(g_szDebugBuffer)); if( TRK_DBG_FLAGS_WRITE_TO_DBG & g_grfLogFlags ) (void) OutputDebugStringA( (PCH) g_szDebugBuffer); if( TRK_DBG_FLAGS_WRITE_TO_STDOUT & g_grfLogFlags ) printf( (PCH) g_szDebugBuffer ); if( (TRK_DBG_FLAGS_WRITE_TO_FILE | TRK_DBG_FLAGS_APPEND_TO_FILE) & g_grfLogFlags ) { if ( INVALID_HANDLE_VALUE == g_LogFile || !WriteFile( g_LogFile, g_szDebugBuffer, length, &BytesWritten, NULL ) ) { (void) OutputDebugStringA( (PCH) g_szDebugBuffer); } } LeaveCriticalSection( &g_csDebugOut ); } VOID TrkAssertFailed( IN PVOID FailedAssertion, IN PVOID FileName, IN ULONG LineNumber, IN PCHAR Message OPTIONAL ) /*++ Have my own version of RtlAssert so debug versions of netlogon really assert on free builds. --*/ { char Response[ 2 ]; for ( ; ; ) { DbgPrint( "\n*** Assertion failed: %s%s\n*** Source File: %s, line %ld\n\n", Message ? Message : "", FailedAssertion, FileName, LineNumber ); DbgPrompt( "Break, Ignore, terminate Process, Sleep 30 seconds, or terminate Thread (bipst)? ", Response, sizeof( Response)); switch ( toupper(Response[0])) { case 'B': DbgBreakPoint(); break; case 'I': return; break; case 'P': NtTerminateProcess( NtCurrentProcess(), STATUS_UNSUCCESSFUL ); break; case 'S': Sleep( 30000L); break; case 'T': NtTerminateThread( NtCurrentThread(), STATUS_UNSUCCESSFUL ); break; } } DbgBreakPoint(); NtTerminateProcess( NtCurrentProcess(), STATUS_UNSUCCESSFUL ); } typedef void (*PFNWin4AssertEx)( char const *pszFile, int iLine, char const *pszMsg); VOID TrkAssertFailedDlg( IN PVOID FailedAssertion, IN PVOID FileName, IN ULONG LineNumber, IN PCHAR Message OPTIONAL ) { static HINSTANCE hinstOLE32 = NULL; static PFNWin4AssertEx pfnWin4AssertEx = NULL; if( NULL == hinstOLE32 ) { hinstOLE32 = LoadLibraryEx( TEXT("ole32.dll"), NULL, 0 ); if( NULL == hinstOLE32 ) { TrkLog(( TRKDBG_ERROR, TEXT("Couldn't load ole32.dll for Win4AssertEx (%#08x)"), GetLastError() )); return; } } if( NULL == pfnWin4AssertEx ) { pfnWin4AssertEx = (PFNWin4AssertEx) GetProcAddress( hinstOLE32, "Win4AssertEx" ); if( NULL == pfnWin4AssertEx ) { TrkLog(( TRKDBG_ERROR, TEXT("Couldn't get Win4AssertEx from ole32.dll (%#08x)"), GetLastError() )); return; } } pfnWin4AssertEx( (char*) FileName, (int) LineNumber, (char*) FailedAssertion ); return; } VOID TrkLogRuntimeList( IN PCHAR Comment) { PLIST_ENTRY pListEntry; TrkLog(( TRKDBG_ERROR, TEXT("%s\n"), Comment)); } HANDLE hTestThread = NULL; /* DWORD WINAPI _TestWorkManagerThread(LPVOID pParam) { __try { ((CWorkManager*) pParam)->WorkManagerThread(); } __except (EXCEPTION_EXECUTE_HANDLER) { TrkAssert(GetExceptionCode() != STATUS_ACCESS_VIOLATION); } return(0); } void StartTestWorkerThread(CWorkManager * pwm) { DWORD dwThreadId; hTestThread = CreateThread( NULL, 0, _TestWorkManagerThread, pwm, 0, &dwThreadId ); TrkAssert(hTestThread != NULL); // Hack: make sure the work manager has a chance to init Sleep( 500 ); } void WaitTestThreadExit() { if (hTestThread != NULL) { WaitForSingleObject(hTestThread, INFINITE); CloseHandle(hTestThread); } } */ #endif // #if DBG == 1