/*++ Copyright (c) 1989-2000 Microsoft Corporation Module Name: UserDump.cpp Abstract: This module implements the code for generating a user mode dump. Author: clupu created 01/31/2001 Revision History: --*/ #include "stdafx.h" #include #include "Debugger.h" #include "UserDumpp.h" #define MEM_SIZE (64*1024) // // Private data structure used for communcating // crash dump data to the callback function. // typedef struct _CRASH_DUMP_INFO { PPROCESS_INFO pProcess; EXCEPTION_DEBUG_INFO* ExceptionInfo; DWORD MemoryCount; DWORD_PTR Address; PUCHAR MemoryData; MEMORY_BASIC_INFORMATION mbi; SIZE_T MbiOffset; SIZE_T MbiRemaining; PTHREAD_INFO pCurrentThread; IMAGEHLP_MODULE mi; PCRASH_MODULE CrashModule; } CRASH_DUMP_INFO, *PCRASH_DUMP_INFO; // // Local function prototypes // DWORD_PTR GetTeb( HANDLE hThread ) { NTSTATUS Status; THREAD_BASIC_INFORMATION ThreadBasicInfo; DWORD_PTR Address = 0; Status = NtQueryInformationThread( hThread, ThreadBasicInformation, &ThreadBasicInfo, sizeof( ThreadBasicInfo ), NULL ); if ( NT_SUCCESS(Status) ) { Address = (DWORD_PTR)ThreadBasicInfo.TebBaseAddress; } return Address; } BOOL CrashDumpCallback( IN DWORD DataType, // requested data type OUT PVOID* DumpData, // pointer to a pointer to the data OUT LPDWORD DumpDataLength, // pointer to the data length IN OUT PVOID cdi // private data ) /*++ Return: TRUE on success, FALSE otherwise. Desc: This function is the callback used by crashlib. Its purpose is to provide data to CreateUserDump() for writting to the crashdump file. --*/ { PCRASH_DUMP_INFO CrashdumpInfo = (PCRASH_DUMP_INFO)cdi; switch ( DataType ) { case DMP_DEBUG_EVENT: *DumpData = &CrashdumpInfo->pProcess->DebugEvent; *DumpDataLength = sizeof(DEBUG_EVENT); break; case DMP_THREAD_STATE: { static CRASH_THREAD CrashThread; PTHREAD_INFO pCurrentThread; *DumpData = &CrashThread; if ( CrashdumpInfo->pCurrentThread == NULL ) { pCurrentThread = CrashdumpInfo->pProcess->pFirstThreadInfo; } else { pCurrentThread = CrashdumpInfo->pCurrentThread->pNext; } CrashdumpInfo->pCurrentThread = pCurrentThread; if ( pCurrentThread == NULL ) { return FALSE; } ZeroMemory(&CrashThread, sizeof(CrashThread)); CrashThread.ThreadId = pCurrentThread->dwThreadId; CrashThread.SuspendCount = SuspendThread(pCurrentThread->hThread); if ( CrashThread.SuspendCount != (DWORD)-1 ) { ResumeThread(pCurrentThread->hThread); } CrashThread.PriorityClass = GetPriorityClass(CrashdumpInfo->pProcess->hProcess); CrashThread.Priority = GetThreadPriority(pCurrentThread->hThread); CrashThread.Teb = GetTeb(pCurrentThread->hThread); *DumpDataLength = sizeof(CRASH_THREAD); break; } case DMP_MEMORY_BASIC_INFORMATION: while ( TRUE ) { CrashdumpInfo->Address += CrashdumpInfo->mbi.RegionSize; if ( !VirtualQueryEx(CrashdumpInfo->pProcess->hProcess, (LPVOID)CrashdumpInfo->Address, &CrashdumpInfo->mbi, sizeof(MEMORY_BASIC_INFORMATION)) ) { return FALSE; } if ( (CrashdumpInfo->mbi.Protect & PAGE_GUARD) || (CrashdumpInfo->mbi.Protect & PAGE_NOACCESS) ) { continue; } if ( (CrashdumpInfo->mbi.State & MEM_FREE) || (CrashdumpInfo->mbi.State & MEM_RESERVE) ) { continue; } break; } *DumpData = &CrashdumpInfo->mbi; *DumpDataLength = sizeof(MEMORY_BASIC_INFORMATION); break; case DMP_THREAD_CONTEXT: { PTHREAD_INFO pCurrentThread; if ( CrashdumpInfo->pCurrentThread == NULL ) { pCurrentThread = CrashdumpInfo->pProcess->pFirstThreadInfo; } else { pCurrentThread = CrashdumpInfo->pCurrentThread->pNext; } CrashdumpInfo->pCurrentThread = pCurrentThread; if ( pCurrentThread == NULL ) { return FALSE; } *DumpData = &CrashdumpInfo->pCurrentThread->Context; *DumpDataLength = sizeof(CONTEXT); break; } case DMP_MODULE: if ( CrashdumpInfo->mi.BaseOfImage == 0 ) { return FALSE; } CrashdumpInfo->CrashModule->BaseOfImage = CrashdumpInfo->mi.BaseOfImage; CrashdumpInfo->CrashModule->SizeOfImage = CrashdumpInfo->mi.ImageSize; CrashdumpInfo->CrashModule->ImageNameLength = strlen(CrashdumpInfo->mi.ImageName) + 1; strcpy( CrashdumpInfo->CrashModule->ImageName, CrashdumpInfo->mi.ImageName ); *DumpData = CrashdumpInfo->CrashModule; *DumpDataLength = sizeof(CRASH_MODULE) + CrashdumpInfo->CrashModule->ImageNameLength; if ( !SymGetModuleInfo(CrashdumpInfo->pProcess->hProcess, (DWORD_PTR)-1, &CrashdumpInfo->mi) ) { CrashdumpInfo->mi.BaseOfImage = 0; } break; case DMP_MEMORY_DATA: if ( !CrashdumpInfo->MemoryCount ) { CrashdumpInfo->Address = 0; CrashdumpInfo->MbiOffset = 0; CrashdumpInfo->MbiRemaining = 0; ZeroMemory( &CrashdumpInfo->mbi, sizeof(MEMORY_BASIC_INFORMATION) ); CrashdumpInfo->MemoryData = (PUCHAR)VirtualAlloc(NULL, MEM_SIZE, MEM_COMMIT, PAGE_READWRITE); } if ( !CrashdumpInfo->MbiRemaining ) { while ( TRUE ) { CrashdumpInfo->Address += CrashdumpInfo->mbi.RegionSize; if ( !VirtualQueryEx(CrashdumpInfo->pProcess->hProcess, (LPVOID)CrashdumpInfo->Address, &CrashdumpInfo->mbi, sizeof(MEMORY_BASIC_INFORMATION)) ) { if ( CrashdumpInfo->MemoryData ) { VirtualFree(CrashdumpInfo->MemoryData, MEM_SIZE, MEM_RELEASE); } return FALSE; } if ( (CrashdumpInfo->mbi.Protect & PAGE_GUARD) || (CrashdumpInfo->mbi.Protect & PAGE_NOACCESS) ) { continue; } if ( (CrashdumpInfo->mbi.State & MEM_FREE) || (CrashdumpInfo->mbi.State & MEM_RESERVE) ) { continue; } CrashdumpInfo->MbiOffset = 0; CrashdumpInfo->MbiRemaining = CrashdumpInfo->mbi.RegionSize; CrashdumpInfo->MemoryCount += 1; break; } } *DumpDataLength = (DWORD)__min( CrashdumpInfo->MbiRemaining, MEM_SIZE ); CrashdumpInfo->MbiRemaining -= *DumpDataLength; ReadProcessMemory(CrashdumpInfo->pProcess->hProcess, (PUCHAR)((DWORD_PTR)CrashdumpInfo->mbi.BaseAddress + CrashdumpInfo->MbiOffset), CrashdumpInfo->MemoryData, *DumpDataLength, NULL); *DumpData = CrashdumpInfo->MemoryData; CrashdumpInfo->MbiOffset += *DumpDataLength; break; } return TRUE; } BOOL CreateUserDump( IN LPTSTR pszFileName, IN PDBGHELP_CREATE_USER_DUMP_CALLBACK DmpCallback, IN PVOID lpv ) /*++ Return: TRUE on success, FALSE otherwise. Desc: Creates the dump file. --*/ { OSVERSIONINFOW OsVersion = {0}; USERMODE_CRASHDUMP_HEADER DumpHeader = {0}; DWORD cb; HANDLE hFile = INVALID_HANDLE_VALUE; BOOL rval; PVOID DumpData; DWORD DumpDataLength; SECURITY_ATTRIBUTES SecAttrib; SECURITY_DESCRIPTOR SecDescript; // // Create a DACL that allows all access to the directory. // SecAttrib.nLength = sizeof(SECURITY_ATTRIBUTES); SecAttrib.lpSecurityDescriptor = &SecDescript; SecAttrib.bInheritHandle = FALSE; InitializeSecurityDescriptor(&SecDescript, SECURITY_DESCRIPTOR_REVISION); SetSecurityDescriptorDacl(&SecDescript, TRUE, NULL, FALSE); hFile = CreateFile(pszFileName, GENERIC_READ | GENERIC_WRITE, 0, &SecAttrib, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if ( hFile == NULL || hFile == INVALID_HANDLE_VALUE ) { return FALSE; } // // Write out an empty header. // if ( !WriteFile(hFile, &DumpHeader, sizeof(DumpHeader), &cb, NULL) ) { goto bad_file; } // // Write the debug event. // DumpHeader.DebugEventOffset = SetFilePointer(hFile, 0, NULL, FILE_CURRENT); DmpCallback(DMP_DEBUG_EVENT, &DumpData, &DumpDataLength, lpv); if ( !WriteFile(hFile, DumpData, sizeof(DEBUG_EVENT), &cb, NULL) ) { goto bad_file; } // // Write the memory map. // DumpHeader.MemoryRegionOffset = SetFilePointer(hFile, 0, NULL, FILE_CURRENT); do { __try { rval = DmpCallback(DMP_MEMORY_BASIC_INFORMATION, &DumpData, &DumpDataLength, lpv); } __except (EXCEPTION_EXECUTE_HANDLER) { rval = FALSE; } if ( rval ) { DumpHeader.MemoryRegionCount += 1; if ( !WriteFile(hFile, DumpData, sizeof(MEMORY_BASIC_INFORMATION), &cb, NULL) ) { goto bad_file; } } } while ( rval ); // // Write the thread contexts. // DumpHeader.ThreadOffset = SetFilePointer(hFile, 0, NULL, FILE_CURRENT); do { __try { rval = DmpCallback(DMP_THREAD_CONTEXT, &DumpData, &DumpDataLength, lpv); } __except (EXCEPTION_EXECUTE_HANDLER) { rval = FALSE; } if ( rval ) { if ( !WriteFile(hFile, DumpData, DumpDataLength, &cb, NULL) ) { goto bad_file; } DumpHeader.ThreadCount += 1; } } while ( rval ); // // Write the thread states. // DumpHeader.ThreadStateOffset = SetFilePointer(hFile, 0, NULL, FILE_CURRENT); do { __try { rval = DmpCallback(DMP_THREAD_STATE, &DumpData, &DumpDataLength, lpv); } __except (EXCEPTION_EXECUTE_HANDLER) { rval = FALSE; } if ( rval ) { if ( !WriteFile(hFile, DumpData, sizeof(CRASH_THREAD), &cb, NULL) ) { goto bad_file; } } } while ( rval ); // // Write the module table. // DumpHeader.ModuleOffset = SetFilePointer(hFile, 0, NULL, FILE_CURRENT); do { __try { rval = DmpCallback(DMP_MODULE, &DumpData, &DumpDataLength, lpv); } __except (EXCEPTION_EXECUTE_HANDLER) { rval = FALSE; } if ( rval ) { if ( !WriteFile(hFile, DumpData, sizeof(CRASH_MODULE) + ((PCRASH_MODULE)DumpData)->ImageNameLength, &cb, NULL) ) { goto bad_file; } DumpHeader.ModuleCount += 1; } } while ( rval ); // // Write the virtual memory // DumpHeader.DataOffset = SetFilePointer(hFile, 0, NULL, FILE_CURRENT); do { __try { rval = DmpCallback(DMP_MEMORY_DATA, &DumpData, &DumpDataLength, lpv); } __except (EXCEPTION_EXECUTE_HANDLER) { rval = FALSE; } if ( rval ) { if ( !WriteFile(hFile, DumpData, DumpDataLength, &cb, NULL) ) { goto bad_file; } } } while ( rval ); // // The VersionInfo is optional. // DumpHeader.VersionInfoOffset = SetFilePointer(hFile, 0, NULL, FILE_CURRENT); // // re-write the dump header with some valid data. // GetVersionEx(&OsVersion); DumpHeader.Signature = USERMODE_CRASHDUMP_SIGNATURE; DumpHeader.MajorVersion = OsVersion.dwMajorVersion; DumpHeader.MinorVersion = OsVersion.dwMinorVersion; #ifdef _X86_ DumpHeader.MachineImageType = IMAGE_FILE_MACHINE_I386; DumpHeader.ValidDump = USERMODE_CRASHDUMP_VALID_DUMP32; #else DumpHeader.MachineImageType = IMAGE_FILE_MACHINE_IA64; DumpHeader.ValidDump = USERMODE_CRASHDUMP_VALID_DUMP64; #endif SetFilePointer(hFile, 0, 0, FILE_BEGIN); if ( !WriteFile(hFile, &DumpHeader, sizeof(DumpHeader), &cb, NULL) ) { goto bad_file; } CloseHandle(hFile); return TRUE; bad_file: CloseHandle(hFile); DeleteFile(pszFileName); return FALSE; } BOOL GenerateUserModeDump( LPTSTR pszFileName, PPROCESS_INFO pProcess, LPEXCEPTION_DEBUG_INFO ed ) { CRASH_DUMP_INFO CrashdumpInfo = {0}; BOOL bRet; PTHREAD_INFO pThread; CrashdumpInfo.mi.SizeOfStruct = sizeof(CrashdumpInfo.mi); CrashdumpInfo.pProcess = pProcess; CrashdumpInfo.ExceptionInfo = ed; // // Get the thread context for all the threads. // pThread = pProcess->pFirstThreadInfo; while ( pThread != NULL ) { pThread->Context.ContextFlags = CONTEXT_FULL; GetThreadContext(pThread->hThread, &pThread->Context); pThread = pThread->pNext; } // // Get first entry in the module list. // if ( !SymInitialize(pProcess->hProcess, NULL, FALSE) ) { return FALSE; } if ( !SymGetModuleInfo(pProcess->hProcess, 0, &CrashdumpInfo.mi) ) { return FALSE; } CrashdumpInfo.CrashModule = (PCRASH_MODULE)LocalAlloc(LPTR, 4096); bRet = CreateUserDump(pszFileName, CrashDumpCallback, &CrashdumpInfo); LocalFree(CrashdumpInfo.CrashModule); return bRet; }