/*++ Copyright (c) 1990-2001 Microsoft Corporation Module Name: dumpwrite.cpp Abstract: This module implements crashdump writing code. --*/ #include "ntsdp.hpp" #include #include // XXX drewb - Should go in machine.hpp. #define PAGE_ALIGN(Machine, Addr) \ ((Addr) & ~((ULONG64)((Machine)->m_PageSize - 1))) // Internal format flag for testing of microdumps. This // will not be made into a public flag and must not conflict // with them. #define FORMAT_USER_MICRO 0x80000000 //---------------------------------------------------------------------------- // // UserFullDumpTargetInfo::Write. // //---------------------------------------------------------------------------- #define USER_DUMP_MEMORY_BUFFER 65536 struct CREATE_USER_DUMP_STATE { PTHREAD_INFO Thread; PDEBUG_IMAGE_INFO Image; ULONG64 MemHandle; HANDLE DumpFileHandle; MEMORY_BASIC_INFORMATION64 MemInfo; MEMORY_BASIC_INFORMATION32 MemInfo32; ULONG64 MemBufDone; UCHAR MemBuf[USER_DUMP_MEMORY_BUFFER]; }; BOOL WINAPI CreateUserDumpCallback( ULONG DataType, PVOID* Data, PULONG DataLength, PVOID UserData ) { CREATE_USER_DUMP_STATE* State = (CREATE_USER_DUMP_STATE*)UserData; PTHREAD_INFO Thread; switch(DataType) { case DMP_DUMP_FILE_HANDLE: *Data = State->DumpFileHandle; *DataLength = sizeof(HANDLE); break; case DMP_DEBUG_EVENT: static DEBUG_EVENT Event; ADDR PcAddr; // // Fake up an exception event for the current thread. // ZeroMemory(&Event, sizeof(Event)); g_Machine->GetPC(&PcAddr); Event.dwDebugEventCode = EXCEPTION_DEBUG_EVENT; Event.dwProcessId = g_CurrentProcess->SystemId; Event.dwThreadId = g_CurrentProcess->CurrentThread->SystemId; if (g_LastEventType == DEBUG_EVENT_EXCEPTION) { // Use the exception record from the last exception. ExceptionRecord64To(&g_LastEventInfo.Exception.ExceptionRecord, &Event.u.Exception.ExceptionRecord); Event.u.Exception.dwFirstChance = g_LastEventInfo.Exception.FirstChance; } else { // Fake a breakpoint exception. Event.u.Exception.ExceptionRecord.ExceptionCode = STATUS_BREAKPOINT; Event.u.Exception.ExceptionRecord.ExceptionAddress = (PVOID)(ULONG_PTR)Flat(PcAddr); Event.u.Exception.dwFirstChance = TRUE; } *Data = &Event; *DataLength = sizeof(DEBUG_EVENT); break; case DMP_THREAD_STATE: static CRASH_THREAD CrashThread; ULONG64 Teb64; if (State->Thread == NULL) { Thread = g_CurrentProcess->ThreadHead; } else { Thread = State->Thread->Next; } State->Thread = Thread; if (Thread == NULL) { return FALSE; } ZeroMemory(&CrashThread, sizeof(CrashThread)); CrashThread.ThreadId = Thread->SystemId; CrashThread.SuspendCount = Thread->SuspendCount; if (IS_LIVE_USER_TARGET()) { if (g_TargetClassQualifier == DEBUG_USER_WINDOWS_PROCESS_SERVER) { // The priority information isn't important // enough to warrant remoting. CrashThread.PriorityClass = NORMAL_PRIORITY_CLASS; CrashThread.Priority = THREAD_PRIORITY_NORMAL; } else { CrashThread.PriorityClass = GetPriorityClass(g_CurrentProcess->Handle); CrashThread.Priority = GetThreadPriority(OS_HANDLE(Thread->Handle)); } } else { CrashThread.PriorityClass = NORMAL_PRIORITY_CLASS; CrashThread.Priority = THREAD_PRIORITY_NORMAL; } if (g_Target->GetThreadInfoDataOffset(Thread, NULL, &Teb64) != S_OK) { Teb64 = 0; } CrashThread.Teb = (DWORD_PTR)Teb64; *Data = &CrashThread; *DataLength = sizeof(CrashThread); break; case DMP_MEMORY_BASIC_INFORMATION: if (g_Target->QueryMemoryRegion(&State->MemHandle, FALSE, &State->MemInfo) != S_OK) { State->MemHandle = 0; State->MemInfo.RegionSize = 0; return FALSE; } #ifdef _WIN64 *Data = &State->MemInfo; *DataLength = sizeof(State->MemInfo); #else State->MemInfo32.BaseAddress = (ULONG)State->MemInfo.BaseAddress; State->MemInfo32.AllocationBase = (ULONG)State->MemInfo.AllocationBase; State->MemInfo32.AllocationProtect = State->MemInfo.AllocationProtect; State->MemInfo32.RegionSize = (ULONG)State->MemInfo.RegionSize; State->MemInfo32.State = State->MemInfo.State; State->MemInfo32.Protect = State->MemInfo.Protect; State->MemInfo32.Type = State->MemInfo.Type; *Data = &State->MemInfo32; *DataLength = sizeof(State->MemInfo32); #endif break; case DMP_THREAD_CONTEXT: if (State->Thread == NULL) { Thread = g_CurrentProcess->ThreadHead; } else { Thread = State->Thread->Next; } State->Thread = Thread; if (Thread == NULL) { ChangeRegContext(g_CurrentProcess->CurrentThread); return FALSE; } ChangeRegContext(Thread); if (g_Machine->GetContextState(MCTX_CONTEXT) != S_OK) { ErrOut("Unable to retrieve context for thread %d. " "Dump may be corrupt", Thread->UserId); return FALSE; } *Data = &g_Machine->m_Context; *DataLength = g_Machine->m_SizeTargetContext; break; case DMP_MODULE: static ULONG64 ModuleBuffer[(sizeof(CRASH_MODULE) + MAX_MODULE + sizeof(ULONG64) - 1) / sizeof(ULONG64)]; PCRASH_MODULE Module; PDEBUG_IMAGE_INFO Image; if (State->Image == NULL) { Image = g_CurrentProcess->ImageHead; } else { Image = State->Image->Next; } State->Image = Image; if (Image == NULL) { return FALSE; } Module = (PCRASH_MODULE)ModuleBuffer; Module->BaseOfImage = (DWORD_PTR)Image->BaseOfImage; Module->SizeOfImage = Image->SizeOfImage; Module->ImageNameLength = strlen(Image->ModuleName) + 1; strcpy(Module->ImageName, Image->ModuleName); *Data = Module; *DataLength = sizeof(*Module) + Module->ImageNameLength; break; case DMP_MEMORY_DATA: ULONG64 Left; Left = State->MemInfo.RegionSize - State->MemBufDone; if (Left == 0) { State->MemBufDone = 0; if (g_Target->QueryMemoryRegion(&State->MemHandle, FALSE, &State->MemInfo) != S_OK) { State->MemHandle = 0; State->MemInfo.RegionSize = 0; return FALSE; } Left = State->MemInfo.RegionSize; } ULONG Read; if (Left > USER_DUMP_MEMORY_BUFFER) { Left = USER_DUMP_MEMORY_BUFFER; } if (g_Target->ReadVirtual(State->MemInfo.BaseAddress + State->MemBufDone, State->MemBuf, (ULONG)Left, &Read) != S_OK || Read < Left) { ErrOut("ReadVirtual failed. Dump may be corrupt\n"); return FALSE; } State->MemBufDone += Read; *Data = State->MemBuf; *DataLength = Read; break; } return TRUE; } HRESULT UserFullDumpTargetInfo::Write(HANDLE hFile, ULONG FormatFlags, PCSTR Comment) { dprintf("user full dump\n"); FlushCallbacks(); if (!IS_LIVE_USER_TARGET()) { ErrOut("User full dumps can only be written in " "live user-mode sessions\n"); return E_UNEXPECTED; } if (Comment != NULL) { ErrOut("User full dumps do not support comments\n"); return E_INVALIDARG; } CREATE_USER_DUMP_STATE* State; State = (CREATE_USER_DUMP_STATE*)calloc(1, sizeof(*State)); if (State == NULL) { ErrOut("Unable to allocate memory for dump state\n"); return E_OUTOFMEMORY; } State->DumpFileHandle = hFile; HRESULT Status; if (!DbgHelpCreateUserDump(NULL, CreateUserDumpCallback, State)) { Status = WIN32_LAST_STATUS(); ErrOut("Dump creation failed, %s\n \"%s\"\n", FormatStatusCode(Status), FormatStatus(Status)); } else { Status = S_OK; } free(State); return Status; } //---------------------------------------------------------------------------- // // UserMiniDumpTargetInfo::Write. // //---------------------------------------------------------------------------- PMINIDUMP_EXCEPTION_INFORMATION CreateMiniExceptionInformation(PMINIDUMP_EXCEPTION_INFORMATION ExInfo, PEXCEPTION_POINTERS ExPointers, PEXCEPTION_RECORD ExRecord) { // If the last event was an exception put together // exception information in minidump format. if (g_LastEventType != DEBUG_EVENT_EXCEPTION || g_CurrentProcess != g_EventProcess) { return NULL; } // Get the full context for the event thread. ChangeRegContext(g_EventThread); if (g_Machine->GetContextState(MCTX_CONTEXT) != S_OK) { return NULL; } ExInfo->ThreadId = g_EventThreadSysId; ExInfo->ExceptionPointers = ExPointers; ExInfo->ClientPointers = FALSE; ExPointers->ExceptionRecord = ExRecord; ExceptionRecord64To(&g_LastEventInfo.Exception.ExceptionRecord, ExRecord); ExPointers->ContextRecord = (PCONTEXT)&g_Machine->m_Context; return ExInfo; } BOOL WINAPI MicroDumpCallback( IN PVOID CallbackParam, IN CONST PMINIDUMP_CALLBACK_INPUT CallbackInput, IN OUT PMINIDUMP_CALLBACK_OUTPUT CallbackOutput ) { switch(CallbackInput->CallbackType) { case IncludeModuleCallback: // Mask off all flags other than the basic write flag. CallbackOutput->ModuleWriteFlags &= ModuleWriteModule; break; case ModuleCallback: // Eliminate all unreferenced modules. if (!(CallbackOutput->ModuleWriteFlags & ModuleReferencedByMemory)) { CallbackOutput->ModuleWriteFlags = 0; } break; case IncludeThreadCallback: if (CallbackInput->IncludeThread.ThreadId != g_EventThreadSysId) { return FALSE; } // Reduce write to the minimum of information // necessary for a stack walk. CallbackOutput->ThreadWriteFlags &= ~ThreadWriteInstructionWindow; break; } return TRUE; } HRESULT UserMiniDumpTargetInfo::Write(HANDLE hFile, ULONG FormatFlags, PCSTR Comment) { if (!IS_USER_TARGET()) { ErrOut("User minidumps can only be written in user-mode sessions\n"); return E_UNEXPECTED; } dprintf("mini user dump\n"); FlushCallbacks(); HRESULT Status; if (IS_LIVE_USER_TARGET() && IS_LOCAL_USER_TARGET()) { MINIDUMP_EXCEPTION_INFORMATION ExInfoBuf, *ExInfo; EXCEPTION_POINTERS ExPointers; EXCEPTION_RECORD ExRecord; MINIDUMP_TYPE MiniType; MINIDUMP_USER_STREAM UserStreams[1]; MINIDUMP_USER_STREAM_INFORMATION UserStreamInfo; MINIDUMP_CALLBACK_INFORMATION CallbackBuffer; PMINIDUMP_CALLBACK_INFORMATION Callback; // // If we're live we can let the official minidump // code do all the work. // MiniType = MiniDumpNormal; if (FormatFlags & DEBUG_FORMAT_USER_SMALL_FULL_MEMORY) { MiniType = (MINIDUMP_TYPE)(MiniType | MiniDumpWithFullMemory); } if (FormatFlags & DEBUG_FORMAT_USER_SMALL_HANDLE_DATA) { MiniType = (MINIDUMP_TYPE)(MiniType | MiniDumpWithHandleData); } UserStreamInfo.UserStreamCount = 0; UserStreamInfo.UserStreamArray = UserStreams; if (Comment != NULL) { UserStreams[UserStreamInfo.UserStreamCount].Type = CommentStreamA; UserStreams[UserStreamInfo.UserStreamCount].BufferSize = strlen(Comment) + 1; UserStreams[UserStreamInfo.UserStreamCount].Buffer = (PVOID)Comment; UserStreamInfo.UserStreamCount++; } ExInfo = CreateMiniExceptionInformation(&ExInfoBuf, &ExPointers, &ExRecord); if (FormatFlags & FORMAT_USER_MICRO) { // This case isn't expected to be used by users, // it's for testing of the microdump support. Callback = &CallbackBuffer; Callback->CallbackRoutine = MicroDumpCallback; Callback->CallbackParam = NULL; ExInfo = NULL; MiniType = (MINIDUMP_TYPE)(MiniType | MiniDumpFilterMemory); } else { Callback = NULL; } if (!MiniDumpWriteDump(g_CurrentProcess->Handle, g_CurrentProcess->SystemId, hFile, MiniType, ExInfo, &UserStreamInfo, Callback)) { Status = WIN32_LAST_STATUS(); ErrOut("Dump creation failed, %s\n \"%s\"\n", FormatStatusCode(Status), FormatStatus(Status)); } else { Status = S_OK; } // Reset the current register context in case // it was changed at some point. ChangeRegContext(g_CurrentProcess->CurrentThread); } else { // The normal minidump code assumes access to a process, // so in the dump case we need to do everything ourself. Status = WriteNonProcess(hFile, FormatFlags, Comment); if (Status != S_OK) { ErrOut("Unable to write minidump, %s\n \"%s\"\n", FormatStatusCode(Status), FormatStatus(Status)); } } return Status; } #define NP_DUMP_STREAMS 6 #define CHECK_WRITE(Buf, Size) \ if (!WriteFile(File, Buf, Size, &Write, NULL)) \ { \ Status = WIN32_LAST_STATUS(); \ ErrOut("Failed writing to crashdump file - %s\n \"%s\"\n", \ FormatStatusCode(Status), \ FormatStatusArgs(Status, NULL)); \ goto Exit; \ } HRESULT MiniWriteVirtMem(HANDLE File, MINIDUMP_MEMORY_DESCRIPTOR* Mem) { HRESULT Status; UCHAR Buf[4096]; ULONG Write; ULONG64 Offset = Mem->StartOfMemoryRange; ULONG Left = Mem->Memory.DataSize; ULONG Chunk; while (Left > 0) { if (Left > sizeof(Buf)) { Chunk = sizeof(Buf); } else { Chunk = Left; } if ((Status = g_Target->ReadAllVirtual(Offset, Buf, Chunk)) != S_OK) { goto Exit; } CHECK_WRITE(Buf, Chunk); Left -= Chunk; Offset += Chunk; } Status = S_OK; Exit: return Status; } HRESULT MiniWriteInstructionWindow(HANDLE File, PMINIDUMP_MEMORY_DESCRIPTOR Desc) { ADDR Pc; UCHAR Instr[768]; switch(g_EffMachine) { case IMAGE_FILE_MACHINE_I386: case IMAGE_FILE_MACHINE_AMD64: Desc->Memory.DataSize = 256; break; case IMAGE_FILE_MACHINE_ALPHA: case IMAGE_FILE_MACHINE_AXP64: Desc->Memory.DataSize = 512; break; case IMAGE_FILE_MACHINE_IA64: Desc->Memory.DataSize = 768; break; } g_Machine->GetPC(&Pc); Desc->StartOfMemoryRange = Flat(Pc) - Desc->Memory.DataSize / 2; // Figure out how much of the desired memory // is actually accessible. for (;;) { ULONG Read; // If we can read the instructions through the // current program counter we'll say that's // good enough. if (g_Target->ReadVirtual(Desc->StartOfMemoryRange, Instr, Desc->Memory.DataSize, &Read) == S_OK && Desc->StartOfMemoryRange + Read > Flat(Pc)) { break; } // We couldn't read up to the program counter. // If the start address is on the previous page // move it up to the same page. if (PAGE_ALIGN(g_Machine, Desc->StartOfMemoryRange) != PAGE_ALIGN(g_Machine, Flat(Pc))) { ULONG Fraction = g_Machine->m_PageSize - (ULONG)Desc->StartOfMemoryRange & (g_Machine->m_PageSize - 1); Desc->StartOfMemoryRange += Fraction; Desc->Memory.DataSize -= Fraction; } else { // The start and PC were on the same page so // we just can't read memory. Desc->Memory.DataSize = 0; break; } } if (Desc->Memory.DataSize > 0) { ULONG Write; if (!WriteFile(File, Instr, Desc->Memory.DataSize, &Write, NULL)) { return WIN32_LAST_STATUS(); } } return S_OK; } HRESULT UserMiniDumpTargetInfo::WriteNonProcess(HANDLE File, ULONG FormatFlags, PCSTR Comment) { HRESULT Status; ULONG i; ULONG Read, Write; ULONG32 Data32; RVA DirRva, SectionRva, Rva; MINIDUMP_THREAD_EX* MiniThreads = NULL; MINIDUMP_MEMORY_DESCRIPTOR* MiniMemory = NULL; MINIDUMP_MODULE* MiniModules = NULL; ULONG NumStreams; BOOL BackingStore = g_TargetMachineType == IMAGE_FILE_MACHINE_IA64; ULONG ThreadStructSize = BackingStore ? sizeof(MINIDUMP_THREAD_EX) : sizeof(MINIDUMP_THREAD); if (FormatFlags != DEBUG_FORMAT_DEFAULT) { ErrOut("Full memory and handle information minidumps\n" "can only be written from a live session\n"); return E_INVALIDARG; } if (IS_DUMP_TARGET() && g_TargetClassQualifier != DEBUG_USER_WINDOWS_DUMP) { ErrOut("Minidumps can only be converted from user full dumps\n"); return E_INVALIDARG; } // // We're writing a minidump from a debug session without // a real process that the minidump code can scan. A lot // of information that's present in the minidump, such // as file version information, CPU characteristics, etc. // are not available, so we produce a pretty spare dump. // // The dump ends up with thread, module and memory lists // plus a system info stream. There may also be // an exception stream if the last event was an exception. // MiniThreads = new MINIDUMP_THREAD_EX[g_CurrentProcess->NumberThreads]; MiniMemory = new MINIDUMP_MEMORY_DESCRIPTOR [g_CurrentProcess->NumberThreads * (BackingStore ? 3 : 2)]; MiniModules = new MINIDUMP_MODULE[g_CurrentProcess->NumberImages]; if (MiniThreads == NULL || MiniMemory == NULL || MiniModules == NULL) { Status = E_OUTOFMEMORY; goto Exit; } // // Write header. // MINIDUMP_HEADER Hdr; NumStreams = NP_DUMP_STREAMS; if (g_LastEventType != DEBUG_EVENT_EXCEPTION) { NumStreams--; } if (Comment == NULL) { NumStreams--; } ZeroMemory(&Hdr, sizeof(Hdr)); Hdr.Signature = MINIDUMP_SIGNATURE; // Encode an implementation-specific version into the high word // of the version to make it clear what version of the code // was used to generate a dump. // In order to distinguish minidump.lib generated dumps from // dumps written by this code, force on the high bit of the // build number. Hdr.Version = (MINIDUMP_VERSION & 0xffff) | ((VER_PRODUCTMAJORVERSION & 0xf) << 28) | ((VER_PRODUCTMINORVERSION & 0xf) << 24) | (((VER_PRODUCTBUILD & 0xff) | 0x80) << 16); Hdr.NumberOfStreams = NumStreams; Hdr.TimeDateStamp = FileTimeToTimeDateStamp(g_Target->GetCurrentTimeDateN()); Rva = sizeof(Hdr); Hdr.StreamDirectoryRva = Rva; CHECK_WRITE(&Hdr, sizeof(Hdr)); // // Write placeholder directory. // MINIDUMP_DIRECTORY Dir[NP_DUMP_STREAMS]; PMINIDUMP_DIRECTORY CurDir; ZeroMemory(Dir, sizeof(Dir)); CHECK_WRITE(Dir, NumStreams * sizeof(Dir[0])); DirRva = Rva; Rva += NumStreams * sizeof(Dir[0]); CurDir = Dir; // // Write comment stream if necessary. // if (Comment != NULL) { Data32 = strlen(Comment) + 1; CurDir->StreamType = CommentStreamA; CurDir->Location.DataSize = Data32; CurDir->Location.Rva = Rva; Rva += CurDir->Location.DataSize; CurDir++; CHECK_WRITE(Comment, Data32); } // // Fill out thread descriptions while writing // out thread stacks and contexts and accumulating // memory records for memory written. // PTHREAD_INFO Thread; MINIDUMP_THREAD_EX* MiniThread; MINIDUMP_MEMORY_DESCRIPTOR* MiniMem; Thread = g_CurrentProcess->ThreadHead; MiniThread = MiniThreads; MiniMem = MiniMemory; for (i = 0; i < g_CurrentProcess->NumberThreads; i++) { ChangeRegContext(Thread); if ((Status = g_Machine->GetContextState(MCTX_CONTEXT)) != S_OK) { goto Exit; } MiniThread->ThreadId = Thread->SystemId; MiniThread->SuspendCount = Thread->SuspendCount; MiniThread->PriorityClass = NORMAL_PRIORITY_CLASS; MiniThread->Priority = THREAD_PRIORITY_NORMAL; if ((Status = g_Target-> GetThreadInfoDataOffset(Thread, NULL, &MiniThread->Teb)) != S_OK) { goto Exit; } ULONG64 StackBase; if ((Status = g_Target-> ReadPointer(g_TargetMachine, MiniThread->Teb + (g_TargetMachine->m_Ptr64 ? STACK_BASE_FROM_TEB64 : STACK_BASE_FROM_TEB32), &StackBase)) != S_OK) { goto Exit; } ADDR StackBottom; g_Machine->GetSP(&StackBottom); if (g_TargetMachineType == IMAGE_FILE_MACHINE_I386) { // // Note: for FPO frames on x86 we access bytes outside of the // ESP - StackBase range. Add a couple of bytes extra here so we // don't fail these cases. // AddrSub(&StackBottom, 4); } MiniThread->Stack.StartOfMemoryRange = Flat(StackBottom); MiniThread->Stack.Memory.DataSize = (ULONG32)(StackBase - Flat(StackBottom)); MiniThread->Stack.Memory.Rva = Rva; Rva += MiniThread->Stack.Memory.DataSize; // Accumulate stack memory descriptors. *MiniMem++ = MiniThread->Stack; // Write stack memory to file. if ((Status = MiniWriteVirtMem(File, &MiniThread->Stack)) != S_OK) { return Status; } if (BackingStore) { ULONG64 StoreBase, StoreTop; #if 1 // XXX drewb - The TEB bstore values don't seem to point to // the actual base of the backing store. Just // assume it's contiguous with the stack. StoreBase = StackBase; #else if ((Status = g_Target->ReadPointer(MiniThread->Teb + IA64_TEB_BSTORE_BASE, &StoreBase)) != S_OK) { goto Exit; } #endif // The BSP points to the bottom of the current frame's // storage area. We need to add on the size of the // current frame to get the amount of memory that // really needs to be stored. When computing the // size of the current frame space for NAT bits // must be figured in properly based on the number // of entries in the frame. The NAT collection // is spilled on every 63'rd spilled register to // make each block an every 64 ULONG64s long. // On NT the backing store base is always 9-bit aligned // so we can tell when exactly the next NAT spill // will occur by looking for when the 9-bit spill // region will overflow. ULONG FrameSize = g_Ia64Machine.GetReg32(STIFS) & IA64_PFS_SIZE_MASK; StoreTop = g_Ia64Machine.GetReg64(RSBSP); // Add in a ULONG64 for every register in the // current frame. While doing so, check for // spill entries. while (FrameSize-- > 0) { StoreTop += sizeof(ULONG64); if ((StoreTop & 0x1f8) == 0x1f8) { // Spill will be placed at this address so // account for it. StoreTop += sizeof(ULONG64); } } MiniThread->BackingStore.StartOfMemoryRange = StoreBase; MiniThread->BackingStore.Memory.DataSize = (ULONG32)(StoreTop - StoreBase); MiniThread->BackingStore.Memory.Rva = Rva; Rva += MiniThread->BackingStore.Memory.DataSize; // Accumulate stack memory descriptors. *MiniMem++ = MiniThread->BackingStore; // Write stack memory to file. if ((Status = MiniWriteVirtMem(File, &MiniThread->BackingStore)) != S_OK) { return Status; } } // // Try and save a window of instructions around // the current PC. // MINIDUMP_MEMORY_DESCRIPTOR InstrMem; if ((Status = MiniWriteInstructionWindow(File, &InstrMem)) != S_OK) { goto Exit; } if (InstrMem.Memory.DataSize > 0) { InstrMem.Memory.Rva = Rva; Rva += InstrMem.Memory.DataSize; *MiniMem++ = InstrMem; } // // Fill out context information and write. // MiniThread->ThreadContext.DataSize = g_Machine->m_SizeCanonicalContext; MiniThread->ThreadContext.Rva = Rva; Rva += MiniThread->ThreadContext.DataSize; CHECK_WRITE(&g_Machine->m_Context, g_Machine->m_SizeCanonicalContext); MiniThread++; Thread = Thread->Next; } // // Write thread list. // Data32 = g_CurrentProcess->NumberThreads; CurDir->StreamType = ThreadStructSize == sizeof(MINIDUMP_THREAD_EX) ? ThreadExListStream : ThreadListStream; CurDir->Location.DataSize = sizeof(MINIDUMP_THREAD_LIST) + ThreadStructSize * Data32; CurDir->Location.Rva = Rva; Rva += CurDir->Location.DataSize; CurDir++; CHECK_WRITE(&Data32, sizeof(Data32)); MiniThread = MiniThreads; for (i = 0; i < Data32; i++) { CHECK_WRITE(MiniThread, ThreadStructSize); MiniThread++; } // // Fill out module information and write supporting data. // PDEBUG_IMAGE_INFO Image; MINIDUMP_MODULE* MiniMod; MiniMod = MiniModules; for (Image = g_CurrentProcess->ImageHead; Image != NULL; Image = Image->Next) { ZeroMemory(MiniMod, sizeof(*MiniMod)); MiniMod->BaseOfImage = Image->BaseOfImage; MiniMod->SizeOfImage = Image->SizeOfImage; MiniMod->CheckSum = Image->CheckSum; MiniMod->TimeDateStamp = Image->TimeDateStamp; MiniMod->ModuleNameRva = Rva; // // Write name. // WCHAR WideName[MAX_IMAGE_PATH]; Data32 = strlen(Image->ImagePath); if (!MultiByteToWideChar(CP_ACP, 0, Image->ImagePath, Data32 + 1, WideName, sizeof(WideName) / sizeof(WCHAR))) { Status = WIN32_LAST_STATUS(); goto Exit; } Data32 *= sizeof(WCHAR); // Written size does not include terminator. CHECK_WRITE(&Data32, sizeof(Data32)); // Data does include terminator. Data32 += sizeof(WCHAR); CHECK_WRITE(WideName, Data32); Rva += sizeof(MINIDUMP_STRING) + Data32; MiniMod++; } DBG_ASSERT((ULONG)(MiniMod - MiniModules) == g_CurrentProcess->NumberImages); // // Write module list. // Data32 = g_CurrentProcess->NumberImages; CurDir->StreamType = ModuleListStream; CurDir->Location.DataSize = sizeof(MINIDUMP_MODULE_LIST) + sizeof(MINIDUMP_MODULE) * Data32; CurDir->Location.Rva = Rva; Rva += CurDir->Location.DataSize; CurDir++; CHECK_WRITE(&Data32, sizeof(Data32)); CHECK_WRITE(MiniModules, sizeof(*MiniModules) * Data32); // // Write memory list. // Data32 = (ULONG32)(MiniMem - MiniMemory); DBG_ASSERT(Data32 <= g_CurrentProcess->NumberThreads * (BackingStore ? 3 : 2)); CurDir->StreamType = MemoryListStream; CurDir->Location.DataSize = sizeof(MINIDUMP_MEMORY_LIST) + sizeof(MINIDUMP_MEMORY_DESCRIPTOR) * Data32; CurDir->Location.Rva = Rva; Rva += CurDir->Location.DataSize; CurDir++; CHECK_WRITE(&Data32, sizeof(Data32)); CHECK_WRITE(MiniMemory, sizeof(*MiniMemory) * Data32); // // Fill out what we can of the system information and // write it out. // MINIDUMP_SYSTEM_INFO SysInfo; ZeroMemory(&SysInfo, sizeof(SysInfo)); switch(g_TargetMachineType) { case IMAGE_FILE_MACHINE_I386: SysInfo.ProcessorArchitecture = PROCESSOR_ARCHITECTURE_INTEL; break; case IMAGE_FILE_MACHINE_ALPHA: SysInfo.ProcessorArchitecture = PROCESSOR_ARCHITECTURE_ALPHA; break; case IMAGE_FILE_MACHINE_IA64: SysInfo.ProcessorArchitecture = PROCESSOR_ARCHITECTURE_IA64; break; case IMAGE_FILE_MACHINE_AXP64: SysInfo.ProcessorArchitecture = PROCESSOR_ARCHITECTURE_ALPHA64; break; case IMAGE_FILE_MACHINE_AMD64: SysInfo.ProcessorArchitecture = PROCESSOR_ARCHITECTURE_AMD64; break; } SysInfo.MajorVersion = g_KdVersion.MajorVersion; SysInfo.MinorVersion = g_KdVersion.MinorVersion; SysInfo.BuildNumber = g_TargetBuildNumber; SysInfo.PlatformId = g_TargetPlatformId; CurDir->StreamType = SystemInfoStream; CurDir->Location.DataSize = sizeof(MINIDUMP_SYSTEM_INFO); CurDir->Location.Rva = Rva; Rva += CurDir->Location.DataSize; CurDir++; CHECK_WRITE(&SysInfo, sizeof(SysInfo)); if (g_LastEventType == DEBUG_EVENT_EXCEPTION) { // Get the full context for the event thread. ChangeRegContext(g_EventThread); if ((Status = g_Machine->GetContextState(MCTX_CONTEXT)) != S_OK) { goto Exit; } // // Write out the exception context and then // the exception stream. // MINIDUMP_EXCEPTION_STREAM ExStream; ExStream.ThreadId = g_EventThreadSysId; ExStream.__alignment = 0; DBG_ASSERT(sizeof(MINIDUMP_EXCEPTION) == sizeof(g_LastEventInfo.Exception.ExceptionRecord)); memcpy(&ExStream.ExceptionRecord, &g_LastEventInfo.Exception.ExceptionRecord, sizeof(g_LastEventInfo.Exception.ExceptionRecord)); ExStream.ThreadContext.DataSize = g_Machine->m_SizeCanonicalContext; ExStream.ThreadContext.Rva = Rva; Rva += ExStream.ThreadContext.DataSize; CHECK_WRITE(&g_Machine->m_Context, g_Machine->m_SizeCanonicalContext); CurDir->StreamType = ExceptionStream; CurDir->Location.DataSize = sizeof(MINIDUMP_EXCEPTION_STREAM); CurDir->Location.Rva = Rva; Rva += CurDir->Location.DataSize; CurDir++; CHECK_WRITE(&ExStream, sizeof(ExStream)); } // // Go back to the directory offset and rewrite the // fully initialized directory. // DBG_ASSERT((ULONG)(CurDir - Dir) == NumStreams); if (SetFilePointer(File, DirRva, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) { Status = WIN32_LAST_STATUS(); goto Exit; } CHECK_WRITE(Dir, NumStreams * sizeof(Dir[0])); Exit: // Register context may have changed so reassert the // current thread. ChangeRegContext(g_CurrentProcess->CurrentThread); delete MiniThreads; delete MiniMemory; delete MiniModules; return Status; } //------------------------------------------------------------------- // initialize the dump headers // #define MINIDUMP_BUGCHECK 0x10000000 void KernelDumpTargetInfo::InitDumpHeader32( PDUMP_HEADER32 pdh, PCSTR Comment, ULONG BugCheckCodeModifier ) { ULONG64 Data[4]; PULONG FillPtr = (PULONG)pdh; ADDR PcAddr; while (FillPtr < (PULONG)(pdh + 1)) { *FillPtr++ = DUMP_SIGNATURE32; } pdh->Signature = DUMP_SIGNATURE32; pdh->ValidDump = DUMP_VALID_DUMP32; pdh->MajorVersion = g_KdVersion.MajorVersion; pdh->MinorVersion = g_KdVersion.MinorVersion; g_Target->ReadDirectoryTableBase(Data); pdh->DirectoryTableBase = (ULONG)Data[0]; pdh->PfnDataBase = (ULONG)KdDebuggerData.MmPfnDatabase; pdh->PsLoadedModuleList = (ULONG)KdDebuggerData.PsLoadedModuleList; pdh->PsActiveProcessHead = (ULONG)KdDebuggerData.PsActiveProcessHead; pdh->MachineImageType = g_KdVersion.MachineType; pdh->NumberProcessors = g_TargetNumberProcessors; g_Target->ReadBugCheckData(&(pdh->BugCheckCode), Data); pdh->BugCheckCode |= BugCheckCodeModifier; pdh->BugCheckParameter1 = (ULONG)Data[0]; pdh->BugCheckParameter2 = (ULONG)Data[1]; pdh->BugCheckParameter3 = (ULONG)Data[2]; pdh->BugCheckParameter4 = (ULONG)Data[3]; //pdh->VersionUser = 0; pdh->PaeEnabled = KdDebuggerData.PaeEnabled; pdh->KdDebuggerDataBlock = (ULONG)g_KdDebuggerDataBlock; // pdh->PhysicalMemoryBlock =; g_Machine->GetContextState(MCTX_CONTEXT); g_Machine->ConvertContextTo(&g_Machine->m_Context, g_SystemVersion, sizeof(pdh->ContextRecord), pdh->ContextRecord); if (g_LastEventType == DEBUG_EVENT_EXCEPTION) { // Use the exception record from the last event. ExceptionRecord64To32(&g_LastEventInfo.Exception.ExceptionRecord, &pdh->Exception); } else { ADDR PcAddr; // Fake a breakpoint exception. ZeroMemory(&pdh->Exception, sizeof(pdh->Exception)); pdh->Exception.ExceptionCode = STATUS_BREAKPOINT; g_Machine->GetPC(&PcAddr); pdh->Exception.ExceptionAddress = (ULONG)Flat(PcAddr); } pdh->RequiredDumpSpace.QuadPart = TRIAGE_DUMP_SIZE32; pdh->SystemTime.QuadPart = g_Target->GetCurrentTimeDateN(); pdh->SystemUpTime.QuadPart = g_Target->GetCurrentSystemUpTimeN(); if (Comment != NULL && Comment[0]) { pdh->Comment[0] = 0; strncat(pdh->Comment, Comment, sizeof(pdh->Comment) - 1); } } void KernelDumpTargetInfo::InitDumpHeader64( PDUMP_HEADER64 pdh, PCSTR Comment, ULONG BugCheckCodeModifier ) { ULONG64 Data[4]; PULONG FillPtr = (PULONG)pdh; ADDR PcAddr; while (FillPtr < (PULONG)(pdh + 1)) { *FillPtr++ = DUMP_SIGNATURE32; } pdh->Signature = DUMP_SIGNATURE64; pdh->ValidDump = DUMP_VALID_DUMP64; pdh->MajorVersion = g_KdVersion.MajorVersion; pdh->MinorVersion = g_KdVersion.MinorVersion; // IA64 has several page directories. The defined // behavior is to put the kernel page directory // in the dump header as that's the one that can // be most useful when first initializing the dump. if (g_EffMachine == IMAGE_FILE_MACHINE_IA64) { ULONG Next; if (g_Machine->SetPageDirectory(PAGE_DIR_KERNEL, 0, &Next) != S_OK) { ErrOut("Unable to update the kernel dirbase\n"); Data[0] = 0; } else { Data[0] = g_Machine->m_PageDirectories[PAGE_DIR_KERNEL]; } } else { g_Target->ReadDirectoryTableBase(Data); } pdh->DirectoryTableBase = Data[0]; pdh->PfnDataBase = KdDebuggerData.MmPfnDatabase; pdh->PsLoadedModuleList = KdDebuggerData.PsLoadedModuleList; pdh->PsActiveProcessHead = KdDebuggerData.PsActiveProcessHead; pdh->MachineImageType = g_KdVersion.MachineType; pdh->NumberProcessors = g_TargetNumberProcessors; g_Target->ReadBugCheckData(&(pdh->BugCheckCode), Data); pdh->BugCheckCode |= BugCheckCodeModifier; pdh->BugCheckParameter1 = Data[0]; pdh->BugCheckParameter2 = Data[1]; pdh->BugCheckParameter3 = Data[2]; pdh->BugCheckParameter4 = Data[3]; //pdh->VersionUser = 0; // PaeEnabled Does not exist in the 64 bit header // pdh->PaeEnabled = KdDebuggerData.PaeEnabled; pdh->KdDebuggerDataBlock = g_KdDebuggerDataBlock; // pdh->PhysicalMemoryBlock =; g_Machine->GetContextState(MCTX_CONTEXT); g_Machine->ConvertContextTo(&g_Machine->m_Context, g_SystemVersion, sizeof(pdh->ContextRecord), pdh->ContextRecord); if (g_LastEventType == DEBUG_EVENT_EXCEPTION) { // Use the exception record from the last event. pdh->Exception = g_LastEventInfo.Exception.ExceptionRecord; } else { ADDR PcAddr; // Fake a breakpoint exception. ZeroMemory(&pdh->Exception, sizeof(pdh->Exception)); pdh->Exception.ExceptionCode = STATUS_BREAKPOINT; g_Machine->GetPC(&PcAddr); pdh->Exception.ExceptionAddress = Flat(PcAddr); } pdh->RequiredDumpSpace.QuadPart = TRIAGE_DUMP_SIZE64; pdh->SystemTime.QuadPart = g_Target->GetCurrentTimeDateN(); pdh->SystemUpTime.QuadPart = g_Target->GetCurrentSystemUpTimeN(); if (Comment != NULL && Comment[0]) { pdh->Comment[0] = 0; strncat(pdh->Comment, Comment, sizeof(pdh->Comment) - 1); } } //---------------------------------------------------------------------------- // // KernelFull64DumpTargetInfo::Write. // //---------------------------------------------------------------------------- HRESULT KernelFull64DumpTargetInfo::Write(HANDLE hFile, ULONG FormatFlags, PCSTR Comment) { PDUMP_HEADER64 pDumpHeader64; HRESULT Status; ULONG64 offset; ULONG Read; PPHYSICAL_MEMORY_DESCRIPTOR64 pmb64 = NULL; DWORD i,j; PUCHAR pPageBuffer = NULL; DWORD bytesWritten; DWORD percent; ULONG64 currentPagesWritten; pDumpHeader64 = (PDUMP_HEADER64) LocalAlloc(LPTR, sizeof(DUMP_HEADER64)); if (pDumpHeader64 == NULL) { ErrOut("Failed to allocate dump header buffer\n"); return E_OUTOFMEMORY; } if (!IS_REMOTE_KERNEL_TARGET() && !IS_KERNEL_FULL_DUMP()) { ErrOut("\nkernel full dumps can only be written when all of physical " "memory is accessible - aborting now\n"); return E_INVALIDARG; } dprintf("Full kernel dump\n"); FlushCallbacks(); KernelDumpTargetInfo::InitDumpHeader64(pDumpHeader64, Comment, 0); pDumpHeader64->DumpType = DUMP_TYPE_FULL; // // now copy the memory descriptor list to our header.. // first get the pointer va // Status = g_Target->ReadPointer(g_TargetMachine, KdDebuggerData.MmPhysicalMemoryBlock, &offset); if (Status != S_OK || (offset == 0)) { ErrOut("Unable to read MmPhysicalMemoryBlock\n"); } else { // // first read the memory descriptor size... // Status = g_Target->ReadVirtual(offset, pDumpHeader64->PhysicalMemoryBlockBuffer, DMP_PHYSICAL_MEMORY_BLOCK_SIZE_64, &Read); if (Status != S_OK || Read != DMP_PHYSICAL_MEMORY_BLOCK_SIZE_64) { ErrOut("Unable to read MmPhysicalMemoryBlock\n"); } else { pmb64 = &pDumpHeader64->PhysicalMemoryBlock; // // calculate total dump file size // pDumpHeader64->RequiredDumpSpace.QuadPart = pDumpHeader64->PhysicalMemoryBlock.NumberOfPages * g_Machine->m_PageSize; // // write dump header to crash dump file // if (!WriteFile(hFile, pDumpHeader64, sizeof(DUMP_HEADER64), &bytesWritten, NULL)) { Status = WIN32_LAST_STATUS(); ErrOut("Failed writing to crashdump file - %s\n \"%s\"\n", FormatStatusCode(Status), FormatStatusArgs(Status, NULL)); } } } if (Status == S_OK) { pPageBuffer = (PUCHAR) LocalAlloc(LPTR, g_Machine->m_PageSize); if (pPageBuffer == NULL) { ErrOut("Failed to allocate double buffer\n"); } else { // // now write the physical memory out to disk. // we use the dump header to retrieve the physical memory base and // run count then ask the transport to gecth these pages form the // target. On 1394, the virtual debugger driver will do physical // address reads on the remote host since there is a onoe-to-one // relationships between physical 1394 and physical mem addresses. // currentPagesWritten = 0; percent = 0; for (i = 0; i < pmb64->NumberOfRuns; i++) { offset = 0; offset = pmb64->Run[i].BasePage*g_Machine->m_PageSize; if (CheckUserInterrupt()) { ErrOut("Creation of crashdump file interrupted\n"); break; } for (j = 0; j< pmb64->Run[i].PageCount; j++) { // // printout the percent done every 5% increments // if ((currentPagesWritten*100)/pmb64->NumberOfPages == percent) { dprintf("Percent written %d \n", percent); FlushCallbacks(); if (g_DbgKdTransport && g_DbgKdTransport->m_DirectPhysicalMemory) { percent += 5; } else { percent += 1; } } if (g_DbgKdTransport && g_DbgKdTransport->m_DirectPhysicalMemory) { Status = g_DbgKdTransport->ReadTargetPhysicalMemory( offset, pPageBuffer, g_Machine->m_PageSize, &bytesWritten); } else { Status = g_Target->ReadPhysical(offset, pPageBuffer, g_Machine->m_PageSize, &bytesWritten); } if (g_EngStatus & ENG_STATUS_USER_INTERRUPT) { break; } else if (Status != S_OK) { ErrOut("Failed Reading page for crashdump file\n"); break; } else { // // now write the page to the local crashdump file // if (!WriteFile(hFile, pPageBuffer, g_Machine->m_PageSize, &bytesWritten, NULL)) { Status = WIN32_LAST_STATUS(); ErrOut("Failed writing header to crashdump file - %s\n \"%s\"\n", FormatStatusCode(Status), FormatStatusArgs(Status, NULL)); break; } offset += g_Machine->m_PageSize; currentPagesWritten++; } } } LocalFree(pPageBuffer); } } LocalFree(pDumpHeader64); return Status; } //---------------------------------------------------------------------------- // // KernelFull32DumpTargetInfo::Write. // //---------------------------------------------------------------------------- HRESULT KernelFull32DumpTargetInfo::Write(HANDLE hFile, ULONG FormatFlags, PCSTR Comment) { PDUMP_HEADER32 pDumpHeader32 = NULL; HRESULT Status; ULONG64 offset; ULONG Read; PPHYSICAL_MEMORY_DESCRIPTOR32 pmb = NULL; DWORD i,j; PUCHAR pPageBuffer = NULL; DWORD bytesWritten; DWORD percent; ULONG currentPagesWritten; pDumpHeader32 = (PDUMP_HEADER32) LocalAlloc(LPTR, sizeof(DUMP_HEADER32)); if (pDumpHeader32 == NULL) { ErrOut("Failed to allocate dump header buffer\n"); return E_OUTOFMEMORY; } if (!IS_REMOTE_KERNEL_TARGET() && !IS_KERNEL_FULL_DUMP()) { ErrOut("\nkernel full dumps can only be written when all of physical " "memory is accessible - aborting now\n"); return E_INVALIDARG; } dprintf("Full kernel dump\n"); FlushCallbacks(); // // Build the header // KernelDumpTargetInfo::InitDumpHeader32(pDumpHeader32, Comment, 0); pDumpHeader32->DumpType = DUMP_TYPE_FULL; // // now copy the memory descriptor list to our header.. // first get the pointer va // Status = g_Target->ReadPointer(g_TargetMachine, KdDebuggerData.MmPhysicalMemoryBlock, &offset); if (Status != S_OK || (offset == 0)) { ErrOut("Unable to read MmPhysicalMemoryBlock\n"); } else { // // first read the memory descriptor size... // Status = g_Target->ReadVirtual(offset, pDumpHeader32->PhysicalMemoryBlockBuffer, DMP_PHYSICAL_MEMORY_BLOCK_SIZE_32, &Read); if (Status != S_OK || Read != DMP_PHYSICAL_MEMORY_BLOCK_SIZE_32) { ErrOut("Unable to read MmPhysicalMemoryBlock\n"); } else { pmb = &pDumpHeader32->PhysicalMemoryBlock; // // calculate total dump file size // pDumpHeader32->RequiredDumpSpace.QuadPart = pDumpHeader32->PhysicalMemoryBlock.NumberOfPages * g_Machine->m_PageSize; // // write dump header to crash dump file // if (!WriteFile(hFile, pDumpHeader32, sizeof(DUMP_HEADER32), &bytesWritten, NULL)) { Status = WIN32_LAST_STATUS(); ErrOut("Failed writing to crashdump file - %s\n \"%s\"\n", FormatStatusCode(Status), FormatStatusArgs(Status, NULL)); } } } if (Status == S_OK) { pPageBuffer = (PUCHAR) LocalAlloc(LPTR, g_Machine->m_PageSize); if (pPageBuffer == NULL) { ErrOut("Failed to allocate double buffer\n"); } else { // // now write the physical memory out to disk. // we use the dump header to retrieve the physical memory base and // run count then ask the transport to gecth these pages form the // target. On 1394, the virtual debugger driver will do physical // address reads on the remote host since there is a onoe-to-one // relationships between physical 1394 and physical mem addresses. // currentPagesWritten = 0; percent = 0; for (i = 0; i < pmb->NumberOfRuns; i++) { offset = 0; offset = pmb->Run[i].BasePage*g_Machine->m_PageSize; if (CheckUserInterrupt()) { ErrOut("Creation of crashdump file interrupted\n"); break; } for (j = 0; j< pmb->Run[i].PageCount; j++) { // // printout the percent done every 5% increments // if ((currentPagesWritten*100)/pmb->NumberOfPages == percent) { dprintf("Percent written %d \n", percent); FlushCallbacks(); if (g_DbgKdTransport && g_DbgKdTransport->m_DirectPhysicalMemory) { percent += 5; } else { percent += 1; } } if (g_DbgKdTransport && g_DbgKdTransport->m_DirectPhysicalMemory) { Status = g_DbgKdTransport->ReadTargetPhysicalMemory( offset, pPageBuffer, g_Machine->m_PageSize, &bytesWritten); } else { Status = g_Target->ReadPhysical(offset, pPageBuffer, g_Machine->m_PageSize, &bytesWritten); } if (g_EngStatus & ENG_STATUS_USER_INTERRUPT) { break; } else if (Status != S_OK) { ErrOut("Failed Reading page for crashdump file\n"); break; } else { // // now write the page to the local crashdump file // if (!WriteFile(hFile, pPageBuffer, g_Machine->m_PageSize, &bytesWritten, NULL)) { Status = WIN32_LAST_STATUS(); ErrOut("Failed writing header to crashdump file - %s\n \"%s\"\n", FormatStatusCode(Status), FormatStatusArgs(Status, NULL)); break; } offset += g_Machine->m_PageSize; currentPagesWritten++; } } } LocalFree(pPageBuffer); } } LocalFree(pDumpHeader32); return Status; } DWORD GetNextModuleEntry( ModuleInfo *ModIter, MODULE_INFO_ENTRY *ModEntry ) { ZeroMemory(ModEntry, sizeof(MODULE_INFO_ENTRY)); // XXX we need to handle errors getting the modules if (ModIter->GetEntry(ModEntry) != S_OK) { return 1; } if (ModEntry->NameLength > (MAX_IMAGE_PATH - 1) * (ModEntry->UnicodeNamePtr ? sizeof(WCHAR) : sizeof(CHAR))) { ErrOut("Module list is corrupt."); if (IS_KERNEL_TARGET()) { ErrOut(" Check your kernel symbols.\n"); } else { ErrOut(" Loader list may be invalid\n"); } return 1; } // If this entry has no name just skip it. if ((ModEntry->NamePtr == NULL) || (ModEntry->NameLength == NULL)) { ErrOut(" Module List has empty entry in it - skipping\n"); return 2; } // If the image header information couldn't be read // we end up with placeholder values for certain entries. // The kernel writes out zeroes in this case so copy // its behavior so that there's one consistent value // for unknown. if (ModEntry->CheckSum == UNKNOWN_CHECKSUM) { ModEntry->CheckSum = 0; } if (ModEntry->TimeDateStamp == UNKNOWN_TIMESTAMP) { ModEntry->TimeDateStamp = 0; } return 0; } //---------------------------------------------------------------------------- // // Shared triage writing things. // //---------------------------------------------------------------------------- #define ExtractValue(NAME, val) { \ if (!KdDebuggerData.NAME) { \ val = 0; \ ErrOut("KdDebuggerData." #NAME " is NULL\n"); \ } else { \ ULONG lsize; \ g_Target->ReadVirtual(KdDebuggerData.NAME, &(val), \ sizeof(val), &lsize); \ } \ } inline ALIGN_8(unsigned offset) { return (offset + 7) & 0xfffffff8; } const unsigned MAX_TRIAGE_STACK_SIZE = 16 * 1024; const unsigned MAX_TRIAGE_BSTORE_SIZE = 16 * 4096; // as defined in ntia64.h const ULONG TRIAGE_DRIVER_NAME_SIZE_GUESS = 0x40; // // XXX drewb - This should all be cleaned up and properly // placed in the dump target and machine. // typedef struct _TRIAGE_PTR_DATA_BLOCK { ULONG64 MinAddress; ULONG64 MaxAddress; } TRIAGE_PTR_DATA_BLOCK, *PTRIAGE_PTR_DATA_BLOCK; // A triage dump is sixteen pages long. Some of that is // header information and at least a few other pages will // be used for basic dump information so limit the number // of extra data blocks to something less than sixteen // to save array space. #define IO_MAX_TRIAGE_DUMP_DATA_BLOCKS 8 ULONG IopNumTriageDumpDataBlocks; TRIAGE_PTR_DATA_BLOCK IopTriageDumpDataBlocks[IO_MAX_TRIAGE_DUMP_DATA_BLOCKS]; // // If space is available in a triage dump it's possible // to add "interesting" data pages referenced by runtime // information such as context registers. The following // lists are offsets into the CONTEXT structure of pointers // which usually point to interesting data. They are // in priority order. // #define IOP_LAST_CONTEXT_OFFSET 0xffff USHORT IopRunTimeContextOffsetsX86[] = { FIELD_OFFSET(X86_NT5_CONTEXT, Ebx), FIELD_OFFSET(X86_NT5_CONTEXT, Esi), FIELD_OFFSET(X86_NT5_CONTEXT, Edi), FIELD_OFFSET(X86_NT5_CONTEXT, Ecx), FIELD_OFFSET(X86_NT5_CONTEXT, Edx), FIELD_OFFSET(X86_NT5_CONTEXT, Eax), IOP_LAST_CONTEXT_OFFSET }; USHORT IopRunTimeContextOffsetsIa64[] = { FIELD_OFFSET(IA64_CONTEXT, IntS0), FIELD_OFFSET(IA64_CONTEXT, IntS1), FIELD_OFFSET(IA64_CONTEXT, IntS2), FIELD_OFFSET(IA64_CONTEXT, IntS3), IOP_LAST_CONTEXT_OFFSET }; USHORT IopRunTimeContextOffsetsAmd64[] = { FIELD_OFFSET(AMD64_CONTEXT, Rbx), FIELD_OFFSET(AMD64_CONTEXT, Rsi), FIELD_OFFSET(AMD64_CONTEXT, Rdi), FIELD_OFFSET(AMD64_CONTEXT, Rcx), FIELD_OFFSET(AMD64_CONTEXT, Rdx), FIELD_OFFSET(AMD64_CONTEXT, Rax), IOP_LAST_CONTEXT_OFFSET }; USHORT IopRunTimeContextOffsetsEmpty[] = { IOP_LAST_CONTEXT_OFFSET }; BOOLEAN IopIsAddressRangeValid( IN ULONG64 VirtualAddress, IN ULONG Length ) { VirtualAddress = PAGE_ALIGN(g_Machine, VirtualAddress); Length = (Length + g_Machine->m_PageSize - 1) >> g_Machine->m_PageShift; while (Length > 0) { UCHAR Data; if (g_Target-> ReadAllVirtual(VirtualAddress, &Data, sizeof(Data)) != S_OK) { return FALSE; } VirtualAddress += g_Machine->m_PageSize; Length--; } return TRUE; } BOOLEAN IoAddTriageDumpDataBlock( IN ULONG64 Address, IN ULONG Length ) /*++ Routine Description: Add an entry to the list of data blocks that should be saved in any triage dump generated. The entire block must be valid for any of it to be saved. Arguments: Address - Beginning of data block. Length - Length of data block. This must be less than the triage dump size. Return Value: TRUE - Block was added. FALSE - Block was not added. --*/ { ULONG i; PTRIAGE_PTR_DATA_BLOCK Block; ULONG64 MinAddress, MaxAddress; // Check against SIZE32 for both 32 and 64-bit dumps // as no data block needs to be larger than that. if (Length >= TRIAGE_DUMP_SIZE32 || !IopIsAddressRangeValid(Address, Length)) { return FALSE; } MinAddress = Address; MaxAddress = MinAddress + Length; // // Minimize overlap between the new block and existing blocks. // Blocks cannot simply be merged as blocks are inserted in // priority order for storage in the dump. Combining a low-priority // block with a high-priority block could lead to a medium- // priority block being bumped improperly from the dump. // Block = IopTriageDumpDataBlocks; for (i = 0; i < IopNumTriageDumpDataBlocks; i++, Block++) { if (MinAddress >= Block->MaxAddress || MaxAddress <= Block->MinAddress) { // No overlap. continue; } // // Trim overlap out of the new block. If this // would split the new block into pieces don't // trim to keep things simple. Content may then // be duplicated in the dump. // if (MinAddress >= Block->MinAddress) { if (MaxAddress <= Block->MaxAddress) { // New block is completely contained. return TRUE; } // New block extends above the current block // so trim off the low-range overlap. MinAddress = Block->MaxAddress; } else if (MaxAddress <= Block->MaxAddress) { // New block extends below the current block // so trim off the high-range overlap. MaxAddress = Block->MinAddress; } } if (IopNumTriageDumpDataBlocks >= IO_MAX_TRIAGE_DUMP_DATA_BLOCKS) { return FALSE; } Block = IopTriageDumpDataBlocks + IopNumTriageDumpDataBlocks++; Block->MinAddress = MinAddress; Block->MaxAddress = MaxAddress; return TRUE; } VOID IopAddRunTimeTriageDataBlocks( IN PCROSS_PLATFORM_CONTEXT Context, IN ULONG64 StackMin, IN ULONG64 StackMax, IN ULONG64 StoreMin, IN ULONG64 StoreMax ) /*++ Routine Description: Add data blocks referenced by the context or other runtime state. Arguments: Context - Context record at the time the dump is being generated for. StackMin, StackMax - Stack memory boundaries. Stack memory is stored elsewhere in the dump. StoreMin, StoreMax - Backing store memory boundaries. Store memory is stored elsewhere in the dump. Return Value: None. --*/ { PUSHORT ContextOffset; switch(g_TargetMachineType) { case IMAGE_FILE_MACHINE_I386: ContextOffset = IopRunTimeContextOffsetsX86; break; case IMAGE_FILE_MACHINE_IA64: ContextOffset = IopRunTimeContextOffsetsIa64; break; case IMAGE_FILE_MACHINE_AMD64: ContextOffset = IopRunTimeContextOffsetsAmd64; break; default: ContextOffset = IopRunTimeContextOffsetsEmpty; break; } while (*ContextOffset < IOP_LAST_CONTEXT_OFFSET) { ULONG64 Ptr; // // Retrieve possible pointers from the context // registers. // if (g_Machine->m_Ptr64) { Ptr = *(PULONG64)((PUCHAR)Context + *ContextOffset); } else { Ptr = EXTEND64(*(PULONG)((PUCHAR)Context + *ContextOffset)); } // Stack and backing store memory is already saved // so ignore any pointers that fall into those ranges. if ((Ptr < StackMin || Ptr >= StackMax) && (Ptr < StoreMin || Ptr >= StoreMax)) { IoAddTriageDumpDataBlock(PAGE_ALIGN(g_Machine, Ptr), g_Machine->m_PageSize); } ContextOffset++; } } ULONG IopSizeTriageDumpDataBlocks( ULONG Offset, ULONG BufferSize, PULONG StartOffset, PULONG Count ) { ULONG i; ULONG Size; PTRIAGE_PTR_DATA_BLOCK Block; *Count = 0; Block = IopTriageDumpDataBlocks; for (i = 0; i < IopNumTriageDumpDataBlocks; i++, Block++) { Size = ALIGN_8(sizeof(TRIAGE_DATA_BLOCK)) + ALIGN_8((ULONG)(Block->MaxAddress - Block->MinAddress)); if (Offset + Size >= BufferSize) { break; } if (i == 0) { *StartOffset = Offset; } Offset += Size; (*Count)++; } return Offset; } VOID IopWriteTriageDumpDataBlocks( ULONG StartOffset, ULONG Count, PUCHAR BufferAddress ) { ULONG i; PTRIAGE_PTR_DATA_BLOCK Block; PUCHAR DataBuffer; PTRIAGE_DATA_BLOCK DumpBlock; DumpBlock = (PTRIAGE_DATA_BLOCK) (BufferAddress + StartOffset); DataBuffer = (PUCHAR)(DumpBlock + Count); Block = IopTriageDumpDataBlocks; for (i = 0; i < Count; i++, Block++) { DumpBlock->Address = Block->MinAddress; DumpBlock->Offset = (ULONG)(DataBuffer - BufferAddress); DumpBlock->Size = (ULONG)(Block->MaxAddress - Block->MinAddress); g_Target->ReadAllVirtual(Block->MinAddress, DataBuffer, DumpBlock->Size); DataBuffer += DumpBlock->Size; DumpBlock++; } } //---------------------------------------------------------------------------- // // KernelTriage32DumpTargetInfo::Write. // //---------------------------------------------------------------------------- HRESULT KernelTriage32DumpTargetInfo::Write(HANDLE hFile, ULONG FormatFlags, PCSTR Comment) { HRESULT Status; PMEMORY_DUMP32 NewHeader; ULONG64 Addr; ULONG64 ThreadAddr; ULONG Size; BOOL PushedContext; ULONG CodeMod; ULONG BugCheckCode; ULONG64 BugCheckData[4]; ULONG64 SaveDataPage = 0; if (!IS_KERNEL_TARGET()) { ErrOut("kernel minidumps can only be written in kernel-mode sessions\n"); return E_UNEXPECTED; } dprintf("mini kernel dump\n"); FlushCallbacks(); NewHeader = (PMEMORY_DUMP32) malloc(TRIAGE_DUMP_SIZE32); if (NewHeader == NULL) { return E_OUTOFMEMORY; } // // Get the current thread address, used to extract various blocks of data. // For some bugchecks the interesting thread is a different // thread than the current thread, so make the following code // generic so it handles any thread. // if ((Status = g_Target-> ReadBugCheckData(&BugCheckCode, BugCheckData)) != S_OK) { goto NewHeader; } if (BugCheckCode == THREAD_STUCK_IN_DEVICE_DRIVER) { DEBUG_STACK_FRAME StkFrame; CROSS_PLATFORM_CONTEXT Context = {0}; // Modify the bugcheck code to indicate this // minidump represents a special state. CodeMod = MINIDUMP_BUGCHECK; // The interesting thread is the first bugcheck parameter. ThreadAddr = BugCheckData[0]; // We need to make the thread's context the current // machine context for the duration of dump generation. if ((Status = GetContextFromThreadStack(ThreadAddr, &Context, &StkFrame, FALSE)) != S_OK) { goto NewHeader; } g_Machine->PushContext(&Context); PushedContext = TRUE; } else if (BugCheckCode == SYSTEM_THREAD_EXCEPTION_NOT_HANDLED) { // // System thread stores a context record as the 4th parameter. // use that. // Also save the context record in case someone needs to look // at it. // if (BugCheckData[3]) { CROSS_PLATFORM_CONTEXT TargetContext, Context; if (g_Target-> ReadAllVirtual(BugCheckData[3], &TargetContext, g_Machine->m_SizeTargetContext) == S_OK && g_Machine->ConvertContextFrom(&Context, g_SystemVersion, g_Machine->m_SizeTargetContext, &TargetContext) == S_OK) { CodeMod = MINIDUMP_BUGCHECK; g_Machine->PushContext(&Context); PushedContext = TRUE; SaveDataPage = BugCheckData[3]; } } } else if (BugCheckCode == KERNEL_MODE_EXCEPTION_NOT_HANDLED) { X86_KTRAP_FRAME Trap; CROSS_PLATFORM_CONTEXT Context = {0}; // // 3rd parameter is a trap frame. // // Build a context record out of that only if it's a kernel mode // failure because esp may be wrong in that case ???. // // XXX drewb - This should be a machine method but // in order to localize code change to this file // for now I'm just making a check. if (g_TargetMachineType == IMAGE_FILE_MACHINE_I386 && BugCheckData[2] && g_Target-> ReadAllVirtual(BugCheckData[2], &Trap, sizeof(Trap)) == S_OK) { if ((Trap.SegCs & 1) || X86_IS_VM86(Trap.EFlags)) { Context.X86Context.Esp = Trap.HardwareEsp; } else { Context.X86Context.Esp = (ULONG)BugCheckData[2] + FIELD_OFFSET(X86_KTRAP_FRAME, HardwareEsp); } if (X86_IS_VM86(Trap.EFlags)) { Context.X86Context.SegSs = (USHORT)(Trap.HardwareSegSs & 0xffff); } else if ((Trap.SegCs & X86_MODE_MASK) != 0 /*KernelMode*/) { // // It's user mode. The HardwareSegSs contains R3 data selector. // Context.X86Context.SegSs = (USHORT)(Trap.HardwareSegSs | 3) & 0xffff; } else { Context.X86Context.SegSs = X86_KGDT_R0_DATA; } Context.X86Context.SegGs = Trap.SegGs & 0xffff; Context.X86Context.SegFs = Trap.SegFs & 0xffff; Context.X86Context.SegEs = Trap.SegEs & 0xffff; Context.X86Context.SegDs = Trap.SegDs & 0xffff; Context.X86Context.SegCs = Trap.SegCs & 0xffff; Context.X86Context.Eip = Trap.Eip; Context.X86Context.Ebp = Trap.Ebp; Context.X86Context.Eax = Trap.Eax; Context.X86Context.Ebx = Trap.Ebx; Context.X86Context.Ecx = Trap.Ecx; Context.X86Context.Edx = Trap.Edx; Context.X86Context.Edi = Trap.Edi; Context.X86Context.Esi = Trap.Esi; Context.X86Context.EFlags = Trap.EFlags; CodeMod = MINIDUMP_BUGCHECK; g_Machine->PushContext(&Context); PushedContext = TRUE; SaveDataPage = BugCheckData[2]; } } else if (BugCheckCode == UNEXPECTED_KERNEL_MODE_TRAP) { // XXX drewb - Put this in ntdbg.h and remove it // from i386_reg.cpp. #define MAX_RING 3 struct X86_KTSS { // Intel's TSS format ULONG Previous; struct { ULONG Esp; ULONG Ss; } Ring[MAX_RING]; ULONG Cr3; ULONG Eip; ULONG EFlags; ULONG Eax; ULONG Ecx; ULONG Edx; ULONG Ebx; ULONG Esp; ULONG Ebp; ULONG Esi; ULONG Edi; ULONG Es; ULONG Cs; ULONG Ss; ULONG Ds; ULONG Fs; ULONG Gs; ULONG Ldt; USHORT T; USHORT IoMapBase; }; X86_KTSS Tss; CROSS_PLATFORM_CONTEXT Context = {0}; // // Double fault // // XXX drewb - This should be a machine method but // in order to localize code change to this file // for now I'm just making a check. if (g_TargetMachineType == IMAGE_FILE_MACHINE_I386 && BugCheckData[0] == 8 && BugCheckData[1] && g_Target-> ReadAllVirtual(BugCheckData[1], &Tss, sizeof(Tss)) == S_OK) { // The thread is correct in this case. // Second parameter is the TSS. If we have a TSS, convert // the context and mark the bugcheck as converted. if (Tss.EFlags & X86_EFLAGS_V86_MASK) { Context.X86Context.SegSs = (USHORT)(Tss.Ss & 0xffff); } else if ((Tss.Cs & X86_MODE_MASK) != 0) { // // It's user mode. // The HardwareSegSs contains R3 data selector. // Context.X86Context.SegSs = (USHORT)(Tss.Ss | 3) & 0xffff; } else { Context.X86Context.SegSs = X86_KGDT_R0_DATA; } Context.X86Context.SegGs = Tss.Gs & 0xffff; Context.X86Context.SegFs = Tss.Fs & 0xffff; Context.X86Context.SegEs = Tss.Es & 0xffff; Context.X86Context.SegDs = Tss.Ds & 0xffff; Context.X86Context.SegCs = Tss.Cs & 0xffff; Context.X86Context.Esp = Tss.Esp; Context.X86Context.Eip = Tss.Eip; Context.X86Context.Ebp = Tss.Ebp; Context.X86Context.Eax = Tss.Eax; Context.X86Context.Ebx = Tss.Ebx; Context.X86Context.Ecx = Tss.Ecx; Context.X86Context.Edx = Tss.Edx; Context.X86Context.Edi = Tss.Edi; Context.X86Context.Esi = Tss.Esi; Context.X86Context.EFlags = Tss.EFlags; CodeMod = MINIDUMP_BUGCHECK; g_Machine->PushContext(&Context); PushedContext = TRUE; } } else { CodeMod = 0; Status = g_Target-> GetThreadInfoDataOffset(NULL, VIRTUAL_THREAD_HANDLE(CURRENT_PROC), &ThreadAddr); if (Status != S_OK) { goto NewHeader; } PushedContext = FALSE; } CCrashDumpWrapper32 Wrapper; // // setup the main header // KernelDumpTargetInfo::InitDumpHeader32(&NewHeader->Header, Comment, CodeMod); NewHeader->Header.DumpType = DUMP_TYPE_TRIAGE; NewHeader->Header.MiniDumpFields = TRIAGE_DUMP_BASIC_INFO; // // triage dump header begins on second page // TRIAGE_DUMP32 *ptdh = &NewHeader->Triage; ULONG i; ptdh->ServicePackBuild = g_TargetServicePackNumber; ptdh->SizeOfDump = TRIAGE_DUMP_SIZE32; ptdh->ContextOffset = FIELD_OFFSET (DUMP_HEADER32, ContextRecord); ptdh->ExceptionOffset = FIELD_OFFSET (DUMP_HEADER32, Exception); // // starting offset in triage dump follows the triage dump header // unsigned offset = ALIGN_8(g_TargetMachine->m_PageSize + sizeof(TRIAGE_DUMP32)); // // write mm information for Win2K and above only // if (g_SystemVersion >= NT_SVER_W2K) { ptdh->MmOffset = offset; Wrapper.WriteMmTriageInformation((PBYTE)NewHeader + ptdh->MmOffset); offset += ALIGN_8(sizeof(DUMP_MM_STORAGE32)); } // // write unloaded drivers // ptdh->UnloadedDriversOffset = offset; Wrapper.WriteUnloadedDrivers((PBYTE)NewHeader + ptdh->UnloadedDriversOffset); offset += ALIGN_8(sizeof(ULONG) + MI_UNLOADED_DRIVERS * sizeof(DUMP_UNLOADED_DRIVERS32)); // // write processor control block (KPRCB) // if (S_OK == g_Target->GetProcessorSystemDataOffset(CURRENT_PROC, DEBUG_DATA_KPRCB_OFFSET, &Addr)) { ptdh->PrcbOffset = offset; g_Target->ReadVirtual(Addr, ((PBYTE)NewHeader) + ptdh->PrcbOffset, g_TargetMachine->m_SizePrcb, &Size); offset += ALIGN_8(g_TargetMachine->m_SizePrcb); } // // Write the thread and process data structures. // ptdh->ProcessOffset = offset; offset += ALIGN_8(g_TargetMachine->m_SizeEProcess); ptdh->ThreadOffset = offset; offset += ALIGN_8(g_TargetMachine->m_SizeEThread); g_Target->ReadVirtual(ThreadAddr + g_TargetMachine->m_OffsetKThreadApcProcess, (PBYTE)NewHeader + ptdh->ProcessOffset, g_TargetMachine->m_SizeEProcess, &Size); g_Target->ReadVirtual(ThreadAddr, (PBYTE)NewHeader + ptdh->ThreadOffset, g_TargetMachine->m_SizeEThread, &Size); // // write the call stack // ADDR StackPtr; ULONG64 StackBase = 0; g_Machine->GetSP(&StackPtr); ptdh->TopOfStack = (ULONG)(ULONG_PTR)Flat(StackPtr); g_Target->ReadPointer(g_TargetMachine, g_TargetMachine->m_OffsetKThreadInitialStack + ThreadAddr, &StackBase); // Take the Min in case something goes wrong getting the stack base. ptdh->SizeOfCallStack = min((ULONG)(ULONG_PTR)(StackBase - Flat(StackPtr)), MAX_TRIAGE_STACK_SIZE); ptdh->CallStackOffset = offset; if (ptdh->SizeOfCallStack) { g_Target->ReadVirtual(ptdh->TopOfStack, ((PBYTE)NewHeader) + ptdh->CallStackOffset, ptdh->SizeOfCallStack, &Size); } offset += ALIGN_8(ptdh->SizeOfCallStack); // // write debugger data // if (g_SystemVersion >= NT_SVER_W2K_WHISTLER && g_KdDebuggerDataBlock && (!IS_KERNEL_TRIAGE_DUMP() || g_TriageDumpHasDebuggerData) && offset + ALIGN_8(sizeof(KdDebuggerData)) < TRIAGE_DUMP_SIZE32) { NewHeader->Header.MiniDumpFields |= TRIAGE_DUMP_DEBUGGER_DATA; ptdh->DebuggerDataOffset = offset; offset += ALIGN_8(sizeof(KdDebuggerData)); ptdh->DebuggerDataSize = sizeof(KdDebuggerData); memcpy((PBYTE)NewHeader + ptdh->DebuggerDataOffset, &KdDebuggerData, sizeof(KdDebuggerData)); } // // write loaded driver list // ModuleInfo* ModIter; ULONG MaxEntries; // Use a heuristic to guess how many entries we // can pack into the remaining space. MaxEntries = (TRIAGE_DUMP_SIZE32 - offset) / (sizeof(DUMP_DRIVER_ENTRY32) + TRIAGE_DRIVER_NAME_SIZE_GUESS); ptdh->DriverCount = 0; if ((ModIter = g_Target->GetModuleInfo(FALSE)) && ((ModIter->Initialize()) == S_OK)) { while (ptdh->DriverCount < MaxEntries) { MODULE_INFO_ENTRY ModEntry; ULONG retval = GetNextModuleEntry(ModIter, &ModEntry); if (retval == 1) { break; } else if (retval == 2) { continue; } ptdh->DriverCount++; } } ptdh->DriverListOffset = offset; offset += ALIGN_8(ptdh->DriverCount * sizeof(DUMP_DRIVER_ENTRY32)); ptdh->StringPoolOffset = offset; ptdh->BrokenDriverOffset = 0; Wrapper.WriteDriverList((PBYTE)NewHeader, ptdh); offset = ptdh->StringPoolOffset + ptdh->StringPoolSize; offset += ALIGN_8(offset); // // For XP and above add in any additional data pages and write out // whatever fits. // if (g_SystemVersion >= NT_SVER_W2K_WHISTLER) { if (SaveDataPage) { IoAddTriageDumpDataBlock(PAGE_ALIGN(g_Machine, SaveDataPage), g_Machine->m_PageSize); } // If the DPC stack is active, save that data page as well. // XXX drewb - Put into ntdbg.h and virtualize through machine. #define X86_KPRCB_DPC_ROUTINE_ACTIVE 0x874 if (g_TargetMachineType == IMAGE_FILE_MACHINE_I386 && ptdh->PrcbOffset && (*(PULONG)((PUCHAR)NewHeader + ptdh->PrcbOffset + X86_KPRCB_DPC_ROUTINE_ACTIVE))) { IoAddTriageDumpDataBlock (PAGE_ALIGN(g_Machine, EXTEND64(*(PULONG)((PUCHAR)NewHeader + ptdh->PrcbOffset + X86_KPRCB_DPC_ROUTINE_ACTIVE))), g_Machine->m_PageSize); } // Add data blocks which might be referred to by // the context or other runtime state. IopAddRunTimeTriageDataBlocks(&g_Machine->m_Context, EXTEND64(ptdh->TopOfStack), EXTEND64(ptdh->TopOfStack + ptdh->SizeOfCallStack), 0, 0); // Check which data blocks fit and write them. offset = IopSizeTriageDumpDataBlocks(offset, TRIAGE_DUMP_SIZE32, &ptdh->DataBlocksOffset, &ptdh->DataBlocksCount); offset += ALIGN_8(offset); if (ptdh->DataBlocksCount) { NewHeader->Header.MiniDumpFields |= TRIAGE_DUMP_DATA_BLOCKS; IopWriteTriageDumpDataBlocks(ptdh->DataBlocksOffset, ptdh->DataBlocksCount, (PUCHAR)NewHeader); } } // // all options are enabled // ptdh->TriageOptions = 0xffffffff; // // end of triage dump validated // ptdh->ValidOffset = TRIAGE_DUMP_SIZE32 - sizeof(ULONG); *(PULONG)(((PBYTE) NewHeader) + ptdh->ValidOffset) = TRIAGE_DUMP_VALID; // // Write it out to the file. // ULONG cbWritten; if (!WriteFile(hFile, NewHeader, TRIAGE_DUMP_SIZE32, &cbWritten, NULL)) { Status = WIN32_LAST_STATUS(); ErrOut("Write to minidump file failed for reason %s\n \"%s\"\n", FormatStatusCode(Status), FormatStatusArgs(Status, NULL)); } if (cbWritten != TRIAGE_DUMP_SIZE32) { ErrOut("Write to minidump failed because disk is full.\n"); Status = E_FAIL; } if (PushedContext) { g_Machine->PopContext(); } NewHeader: free(NewHeader); return Status; } //---------------------------------------------------------------------------- // // KernelTriage64DumpTargetInfo::Write. // //---------------------------------------------------------------------------- HRESULT KernelTriage64DumpTargetInfo::Write(HANDLE hFile, ULONG FormatFlags, PCSTR Comment) { HRESULT Status; PMEMORY_DUMP64 NewHeader; ULONG64 Addr; ULONG64 ThreadAddr; ULONG Size; BOOL PushedContext; ULONG CodeMod; ULONG BugCheckCode; ULONG64 BugCheckData[4]; ULONG64 SaveDataPage = 0; ULONG64 BStoreBase = 0; ULONG BStoreSize = 0; if (!IS_KERNEL_TARGET()) { ErrOut("kernel minidumps can only be written in kernel-mode sessions\n"); return E_UNEXPECTED; } dprintf("mini kernel dump\n"); FlushCallbacks(); NewHeader = (PMEMORY_DUMP64) malloc(TRIAGE_DUMP_SIZE64); if (NewHeader == NULL) { return E_OUTOFMEMORY; } // // Get the current thread address, used to extract various blocks of data. // For some bugchecks the interesting thread is a different // thread than the current thread, so make the following code // generic so it handles any thread. // if ((Status = g_Target-> ReadBugCheckData(&BugCheckCode, BugCheckData)) != S_OK) { goto NewHeader; } if (BugCheckCode == THREAD_STUCK_IN_DEVICE_DRIVER) { DEBUG_STACK_FRAME StkFrame; CROSS_PLATFORM_CONTEXT Context = {0}; // Modify the bugcheck code to indicate this // minidump represents a special state. CodeMod = MINIDUMP_BUGCHECK; // The interesting thread is the first bugcheck parameter. ThreadAddr = BugCheckData[0]; // We need to make the thread's context the current // machine context for the duration of dump generation. if ((Status = GetContextFromThreadStack(ThreadAddr, &Context, &StkFrame, FALSE)) != S_OK) { goto NewHeader; } g_Machine->PushContext(&Context); PushedContext = TRUE; } else if (BugCheckCode == SYSTEM_THREAD_EXCEPTION_NOT_HANDLED) { // // System thread stores a context record as the 4th parameter. // use that. // Also save the context record in case someone needs to look // at it. // if (BugCheckData[3]) { CROSS_PLATFORM_CONTEXT TargetContext, Context; if (ReadAllVirtual(BugCheckData[3], &TargetContext, g_Machine->m_SizeTargetContext) == S_OK && g_Machine->ConvertContextFrom(&Context, g_SystemVersion, g_Machine->m_SizeTargetContext, &TargetContext) == S_OK) { CodeMod = MINIDUMP_BUGCHECK; g_Machine->PushContext(&Context); PushedContext = TRUE; SaveDataPage = BugCheckData[3]; } } } else { CodeMod = 0; Status = g_Target-> GetThreadInfoDataOffset(NULL, VIRTUAL_THREAD_HANDLE(CURRENT_PROC), &ThreadAddr); if (Status != S_OK) { goto NewHeader; } PushedContext = FALSE; } CCrashDumpWrapper64 Wrapper; // // setup the main header // KernelDumpTargetInfo::InitDumpHeader64(&NewHeader->Header, Comment, CodeMod); NewHeader->Header.DumpType = DUMP_TYPE_TRIAGE; NewHeader->Header.MiniDumpFields = TRIAGE_DUMP_BASIC_INFO; // // triage dump header begins on second page // TRIAGE_DUMP64 *ptdh = &NewHeader->Triage; ULONG i; ptdh->ServicePackBuild = g_TargetServicePackNumber; ptdh->SizeOfDump = TRIAGE_DUMP_SIZE64; ptdh->ContextOffset = FIELD_OFFSET (DUMP_HEADER64, ContextRecord); ptdh->ExceptionOffset = FIELD_OFFSET (DUMP_HEADER64, Exception); // // starting offset in triage dump follows the triage dump header // unsigned offset = ALIGN_8(g_TargetMachine->m_PageSize + sizeof(TRIAGE_DUMP64)); // // write mm information // ptdh->MmOffset = offset; Wrapper.WriteMmTriageInformation((PBYTE)NewHeader + ptdh->MmOffset); offset += ALIGN_8(sizeof(DUMP_MM_STORAGE64)); // // write unloaded drivers // ptdh->UnloadedDriversOffset = offset; Wrapper.WriteUnloadedDrivers((PBYTE)NewHeader + ptdh->UnloadedDriversOffset); offset += ALIGN_8(sizeof(ULONG64) + MI_UNLOADED_DRIVERS * sizeof(DUMP_UNLOADED_DRIVERS64)); // // write processor control block (KPRCB) // if (S_OK == g_Target->GetProcessorSystemDataOffset(CURRENT_PROC, DEBUG_DATA_KPRCB_OFFSET, &Addr)) { ptdh->PrcbOffset = offset; g_Target->ReadVirtual(Addr, ((PBYTE)NewHeader) + ptdh->PrcbOffset, g_TargetMachine->m_SizePrcb, &Size); offset += ALIGN_8(g_TargetMachine->m_SizePrcb); } // // Write the thread and process data structures. // ptdh->ProcessOffset = offset; offset += ALIGN_8(g_TargetMachine->m_SizeEProcess); ptdh->ThreadOffset = offset; offset += ALIGN_8(g_TargetMachine->m_SizeEThread); g_Target->ReadVirtual(ThreadAddr + g_TargetMachine->m_OffsetKThreadApcProcess, (PBYTE)NewHeader + ptdh->ProcessOffset, g_TargetMachine->m_SizeEProcess, &Size); g_Target->ReadVirtual(ThreadAddr, (PBYTE)NewHeader + ptdh->ThreadOffset, g_TargetMachine->m_SizeEThread, &Size); // // write the call stack // ADDR StackPtr; ULONG64 StackBase = 0; g_Machine->GetSP(&StackPtr); ptdh->TopOfStack = Flat(StackPtr); g_Target->ReadPointer(g_TargetMachine, g_TargetMachine->m_OffsetKThreadInitialStack + ThreadAddr, &StackBase); // Take the Min in case something goes wrong getting the stack base. ptdh->SizeOfCallStack = min((ULONG)(ULONG_PTR)(StackBase - Flat(StackPtr)), MAX_TRIAGE_STACK_SIZE); ptdh->CallStackOffset = offset; if (ptdh->SizeOfCallStack) { g_Target->ReadVirtual(ptdh->TopOfStack, ((PBYTE)NewHeader) + ptdh->CallStackOffset, ptdh->SizeOfCallStack, &Size); } offset += ALIGN_8(ptdh->SizeOfCallStack); // // The IA64 contains two callstacks. The first is the normal // callstack, and the second is a scratch region where // the processor can spill registers. It is this latter stack, // the backing-store, that we now save. // if (g_TargetMachineType == IMAGE_FILE_MACHINE_IA64) { ULONG64 BStoreLimit; g_Target->ReadPointer(g_TargetMachine, ThreadAddr + FIELD_OFFSET(IA64_THREAD, InitialBStore), &BStoreBase); g_Target->ReadPointer(g_TargetMachine, ThreadAddr + FIELD_OFFSET(IA64_THREAD, BStoreLimit), &BStoreLimit); ptdh->ArchitectureSpecific.Ia64.BStoreOffset = offset; ptdh->ArchitectureSpecific.Ia64.LimitOfBStore = BStoreLimit; ptdh->ArchitectureSpecific.Ia64.SizeOfBStore = min((ULONG)(BStoreLimit - BStoreBase), MAX_TRIAGE_BSTORE_SIZE); BStoreSize = ptdh->ArchitectureSpecific.Ia64.SizeOfBStore; if (ptdh->ArchitectureSpecific.Ia64.SizeOfBStore) { g_Target->ReadVirtual(BStoreBase, ((PBYTE)NewHeader) + ptdh->ArchitectureSpecific.Ia64.BStoreOffset, ptdh->ArchitectureSpecific.Ia64.SizeOfBStore, &Size); offset += ALIGN_8(ptdh->ArchitectureSpecific.Ia64.SizeOfBStore); } } // // write debugger data // if (g_SystemVersion >= NT_SVER_W2K_WHISTLER && g_KdDebuggerDataBlock && (!IS_KERNEL_TRIAGE_DUMP() || g_TriageDumpHasDebuggerData) && offset + ALIGN_8(sizeof(KdDebuggerData)) < TRIAGE_DUMP_SIZE64) { NewHeader->Header.MiniDumpFields |= TRIAGE_DUMP_DEBUGGER_DATA; ptdh->DebuggerDataOffset = offset; offset += ALIGN_8(sizeof(KdDebuggerData)); ptdh->DebuggerDataSize = sizeof(KdDebuggerData); memcpy((PBYTE)NewHeader + ptdh->DebuggerDataOffset, &KdDebuggerData, sizeof(KdDebuggerData)); } // // write loaded driver list // ModuleInfo* ModIter; ULONG MaxEntries; // Use a heuristic to guess how many entries we // can pack into the remaining space. MaxEntries = (TRIAGE_DUMP_SIZE64 - offset) / (sizeof(DUMP_DRIVER_ENTRY64) + TRIAGE_DRIVER_NAME_SIZE_GUESS); ptdh->DriverCount = 0; if ((ModIter = g_Target->GetModuleInfo(FALSE)) && ((ModIter->Initialize()) == S_OK)) { while (ptdh->DriverCount < MaxEntries) { MODULE_INFO_ENTRY ModEntry; ULONG retval = GetNextModuleEntry(ModIter, &ModEntry); if (retval == 1) { break; } else if (retval == 2) { continue; } ptdh->DriverCount++; } } ptdh->DriverListOffset = offset; offset += ALIGN_8(ptdh->DriverCount * sizeof(DUMP_DRIVER_ENTRY64)); ptdh->StringPoolOffset = offset; ptdh->BrokenDriverOffset = 0; Wrapper.WriteDriverList((PBYTE)NewHeader, ptdh); offset = ptdh->StringPoolOffset + ptdh->StringPoolSize; offset += ALIGN_8(offset); // // For XP and above add in any additional data pages and write out // whatever fits. // if (g_SystemVersion >= NT_SVER_W2K_WHISTLER) { if (SaveDataPage) { IoAddTriageDumpDataBlock(PAGE_ALIGN(g_Machine, SaveDataPage), g_Machine->m_PageSize); } // Add data blocks which might be referred to by // the context or other runtime state. IopAddRunTimeTriageDataBlocks(&g_Machine->m_Context, ptdh->TopOfStack, ptdh->TopOfStack + ptdh->SizeOfCallStack, BStoreBase, BStoreSize); // Check which data blocks fit and write them. offset = IopSizeTriageDumpDataBlocks(offset, TRIAGE_DUMP_SIZE64, &ptdh->DataBlocksOffset, &ptdh->DataBlocksCount); offset += ALIGN_8(offset); if (ptdh->DataBlocksCount) { NewHeader->Header.MiniDumpFields |= TRIAGE_DUMP_DATA_BLOCKS; IopWriteTriageDumpDataBlocks(ptdh->DataBlocksOffset, ptdh->DataBlocksCount, (PUCHAR)NewHeader); } } // // all options are enabled // ptdh->TriageOptions = 0xffffffff; // // end of triage dump validated // ptdh->ValidOffset = TRIAGE_DUMP_SIZE64 - sizeof(ULONG); *(PULONG)(((PBYTE) NewHeader) + ptdh->ValidOffset) = TRIAGE_DUMP_VALID; // // Write it out to the file. // ULONG cbWritten; if (!WriteFile(hFile, NewHeader, TRIAGE_DUMP_SIZE64, &cbWritten, NULL)) { Status = WIN32_LAST_STATUS(); ErrOut("Write to minidump file failed for reason %s\n \"%s\"\n", FormatStatusCode(Status), FormatStatusArgs(Status, NULL)); } if (cbWritten != TRIAGE_DUMP_SIZE64) { ErrOut("Write to minidump failed because disk is full.\n"); Status = E_FAIL; } if (PushedContext) { g_Machine->PopContext(); } NewHeader: free(NewHeader); return Status; } //---------------------------------------------------------------------------- // // Functions. // //---------------------------------------------------------------------------- HRESULT WriteDumpFile(PCSTR DumpFile, ULONG Qualifier, ULONG FormatFlags, PCSTR Comment) { ULONG DumpType = DTYPE_COUNT; DumpTargetInfo* DumpTarget; HRESULT Status; ULONG OldMachine; HANDLE FileHandle; if (!IS_MACHINE_ACCESSIBLE()) { return E_UNEXPECTED; } if (IS_KERNEL_TARGET()) { if (FormatFlags != DEBUG_FORMAT_DEFAULT) { return E_INVALIDARG; } // // not much we can do without the processor block // or at least the PRCB for the current process in a minidump. // if (!KdDebuggerData.KiProcessorBlock && !g_DumpKiProcessors[CURRENT_PROC]) { ErrOut("Cannot find KiProcessorBlock - " "can not create dump file\n"); return E_FAIL; } switch(Qualifier) { case DEBUG_KERNEL_SMALL_DUMP: DumpType = g_TargetMachine->m_Ptr64 ? DTYPE_KERNEL_TRIAGE64 : DTYPE_KERNEL_TRIAGE32; break; case DEBUG_KERNEL_FULL_DUMP: if (g_DbgKdTransport != NULL && g_DbgKdTransport->m_DirectPhysicalMemory == FALSE) { WarnOut("Creating a full kernel dump over the COM port is a " "VERY VERY slow operation.\n" "This command may take many HOURS to complete. " "Ctrl-C if you want to terminate the command.\n"); } DumpType = g_TargetMachine->m_Ptr64 ? DTYPE_KERNEL_FULL64 : DTYPE_KERNEL_FULL32; break; default: // Other formats are not supported. return E_INVALIDARG; } } else { DBG_ASSERT(IS_USER_TARGET()); switch(Qualifier) { case DEBUG_USER_WINDOWS_SMALL_DUMP: if (FormatFlags & ~(DEBUG_FORMAT_USER_SMALL_FULL_MEMORY | DEBUG_FORMAT_USER_SMALL_HANDLE_DATA | FORMAT_USER_MICRO)) { return E_INVALIDARG; } DumpType = (FormatFlags & DEBUG_FORMAT_USER_SMALL_FULL_MEMORY) ? DTYPE_USER_MINI_FULL : DTYPE_USER_MINI_PARTIAL; break; case DEBUG_USER_WINDOWS_DUMP: if (FormatFlags != DEBUG_FORMAT_DEFAULT) { return E_INVALIDARG; } DumpType = g_TargetMachine->m_Ptr64 ? DTYPE_USER_FULL64 : DTYPE_USER_FULL32; break; default: // Other formats are not supported. return E_INVALIDARG; } } DBG_ASSERT(DumpType < DTYPE_COUNT); // Ensure that the dump is always written according to the // target machine type and not any emulated machine. OldMachine = g_EffMachine; SetEffMachine(g_TargetMachineType, FALSE); // Flush context first so that the minidump reads the // same register values the debugger has. FlushRegContext(); FileHandle = CreateFile(DumpFile, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (FileHandle == INVALID_HANDLE_VALUE) { Status = WIN32_LAST_STATUS(); ErrOut("Unable to create file '%s' - %s\n \"%s\"\n", DumpFile, FormatStatusCode(Status), FormatStatusArgs(Status, NULL)); } else { dprintf("Creating %s - ", DumpFile); Status = g_DumpTargets[DumpType]->Write(FileHandle, FormatFlags, Comment); CloseHandle(FileHandle); if (Status != S_OK) { DeleteFile(DumpFile); } } SetEffMachine(OldMachine, FALSE); return Status; } void ParseDumpFileCommand(void) { BOOL Usage = FALSE; ULONG Qual; ULONG FormatFlags; // // Default to minidumps // if (IS_KERNEL_TARGET()) { Qual = DEBUG_KERNEL_SMALL_DUMP; if (IS_LOCAL_KERNEL_TARGET()) { error(SESSIONNOTSUP); } } else { Qual = DEBUG_USER_WINDOWS_SMALL_DUMP; } FormatFlags = DEBUG_FORMAT_DEFAULT; // // Scan for options. // CHAR Save; PSTR FileName; BOOL SubLoop; PCSTR Comment = NULL; PSTR CommentEnd = NULL; BOOL Unique = FALSE; PPROCESS_INFO DumpProcess = g_CurrentProcess; for (;;) { if (PeekChar() == '-' || *g_CurCmd == '/') { SubLoop = TRUE; g_CurCmd++; switch(*g_CurCmd) { case 'a': DumpProcess = NULL; break; case 'c': g_CurCmd++; Comment = StringValue(STRV_SPACE_IS_SEPARATOR | STRV_TRIM_TRAILING_SPACE, &Save); *g_CurCmd = Save; CommentEnd = g_CurCmd; break; case 'f': if (IS_KERNEL_TARGET()) { Qual = DEBUG_KERNEL_FULL_DUMP; } else { Qual = DEBUG_USER_WINDOWS_DUMP; } break; case 'm': if (IS_KERNEL_TARGET()) { Qual = DEBUG_KERNEL_SMALL_DUMP; } else { Qual = DEBUG_USER_WINDOWS_SMALL_DUMP; for (;;) { switch(*(g_CurCmd + 1)) { case 'C': // Flag to test microdump code. FormatFlags |= FORMAT_USER_MICRO; break; case 'f': FormatFlags |= DEBUG_FORMAT_USER_SMALL_FULL_MEMORY; break; case 'h': FormatFlags |= DEBUG_FORMAT_USER_SMALL_HANDLE_DATA; break; default: SubLoop = FALSE; break; } if (SubLoop) { g_CurCmd++; } else { break; } } } break; case 'u': Unique = TRUE; break; default: ErrOut("Unknown option '%c'\n", *g_CurCmd); Usage = TRUE; break; } g_CurCmd++; } else { FileName = StringValue(STRV_TRIM_TRAILING_SPACE, &Save); if (*FileName) { break; } else { *g_CurCmd = Save; Usage = TRUE; } } if (Usage) { break; } } if (DumpProcess == NULL && !Unique) { Usage = TRUE; } if (Usage) { ErrOut("Usage: .dump [options] filename\n"); ErrOut("Options are:\n"); ErrOut(" /a - Create dumps for all processes (requires -u)\n"); ErrOut(" /c - Add a comment " "(not supported in all formats)\n"); ErrOut(" /f - Create a full dump\n"); if (IS_KERNEL_TARGET()) { ErrOut(" /m - Create a minidump (default)\n"); } else { ErrOut(" /m[f][h] - Create a minidump (default)\n"); } ErrOut(" /u - Append unique identifier to dump name\n"); return; } if (CommentEnd != NULL) { *CommentEnd = 0; } PTHREAD_INFO OldThread = g_CurrentProcess ? g_CurrentProcess->CurrentThread : NULL; PPROCESS_INFO Process; for (Process = g_ProcessHead; Process; Process = Process->Next) { PSTR DumpFileName; char UniqueName[2 * MAX_PATH]; if (DumpProcess != NULL && Process != DumpProcess) { continue; } if (Process != g_CurrentProcess) { SetCurrentThread(Process->ThreadHead, TRUE); } if (Unique) { SYSTEMTIME Time; char Ext[8], *Dot; strcpy(UniqueName, FileName); Dot = strrchr(UniqueName, '.'); if (Dot && strlen(Dot) < sizeof(Ext) - 1) { strcpy(Ext, Dot); *Dot = 0; } else { Dot = NULL; } GetLocalTime(&Time); sprintf(UniqueName + strlen(UniqueName), "_%04d-%02d-%02d_%02d-%02d-%02d-%03d", Time.wYear, Time.wMonth, Time.wDay, Time.wHour, Time.wMinute, Time.wSecond, Time.wMilliseconds); sprintf(UniqueName + strlen(UniqueName), "_%04X", g_CurrentProcess->SystemId); if (Dot) { strcpy(UniqueName + strlen(UniqueName), Ext); } DumpFileName = UniqueName; } else { DumpFileName = FileName; } WriteDumpFile(DumpFileName, Qual, FormatFlags, Comment); } if (!OldThread || OldThread->Process != g_CurrentProcess) { SetCurrentThread(OldThread, TRUE); } *g_CurCmd = Save; } // extern PKDDEBUGGER_DATA64 blocks[]; #define ALIGN_DOWN_POINTER(address, type) \ ((PVOID)((ULONG_PTR)(address) & ~((ULONG_PTR)sizeof(type) - 1))) #define ALIGN_UP_POINTER(address, type) \ (ALIGN_DOWN_POINTER(((ULONG_PTR)(address) + sizeof(type) - 1), type)) //---------------------------------------------------------------------------- // // CCrashDumpWrapper32. // //---------------------------------------------------------------------------- void CCrashDumpWrapper32::WriteDriverList( BYTE *pb, TRIAGE_DUMP32 *ptdh ) { ULONG Size; PDUMP_DRIVER_ENTRY32 pdde; PDUMP_STRING pds; ModuleInfo* ModIter; ULONG MaxEntries = ptdh->DriverCount; ptdh->DriverCount = 0; if (((ModIter = g_Target->GetModuleInfo(FALSE)) == NULL) || ((ModIter->Initialize()) != S_OK)) { return; } // pointer to first driver entry to write out pdde = (PDUMP_DRIVER_ENTRY32) (pb + ptdh->DriverListOffset); // pointer to first module name to write out pds = (PDUMP_STRING) (pb + ptdh->StringPoolOffset); while ((PBYTE)(pds + 1) < pb + TRIAGE_DUMP_SIZE32 && ptdh->DriverCount < MaxEntries) { MODULE_INFO_ENTRY ModEntry; ULONG retval = GetNextModuleEntry(ModIter, &ModEntry); if (retval == 1) { break; } else if (retval == 2) { continue; } pdde->LdrEntry.DllBase = (ULONG)(ULONG_PTR)ModEntry.Base; pdde->LdrEntry.SizeOfImage = ModEntry.Size; pdde->LdrEntry.CheckSum = ModEntry.CheckSum; pdde->LdrEntry.TimeDateStamp = ModEntry.TimeDateStamp; if (ModEntry.UnicodeNamePtr) { // convert length from bytes to characters pds->Length = ModEntry.NameLength / sizeof(WCHAR); if ((PBYTE)pds->Buffer + pds->Length + sizeof(WCHAR) > pb + TRIAGE_DUMP_SIZE32) { break; } CopyMemory(pds->Buffer, ModEntry.NamePtr, ModEntry.NameLength); } else { pds->Length = ModEntry.NameLength; if ((PBYTE)pds->Buffer + pds->Length + sizeof(WCHAR) > pb + TRIAGE_DUMP_SIZE32) { break; } MultiByteToWideChar(CP_ACP, 0, ModEntry.NamePtr, ModEntry.NameLength, pds->Buffer, ModEntry.NameLength); } // null terminate string pds->Buffer[pds->Length] = '\0'; pdde->DriverNameOffset = (ULONG)((ULONG_PTR) pds - (ULONG_PTR) pb); // get pointer to next string pds = (PDUMP_STRING) ALIGN_UP_POINTER(((LPBYTE) pds) + sizeof(DUMP_STRING) + sizeof(WCHAR) * (pds->Length + 1), ULONGLONG); pdde = (PDUMP_DRIVER_ENTRY32)(((PUCHAR) pdde) + sizeof(*pdde)); ptdh->DriverCount++; } ptdh->StringPoolSize = (ULONG) ((ULONG_PTR)pds - (ULONG_PTR)(pb + ptdh->StringPoolOffset)); } void CCrashDumpWrapper32::WriteUnloadedDrivers(BYTE *pb) { ULONG Size; ULONG i; ULONG Index; UNLOADED_DRIVERS32 *pud; UNLOADED_DRIVERS32 ud; PDUMP_UNLOADED_DRIVERS32 pdud; ULONG64 pvMiUnloadedDrivers; ULONG ulMiLastUnloadedDriver; *((PULONG) pb) = 0; // // find location of unloaded drivers // if (!KdDebuggerData.MmUnloadedDrivers || !KdDebuggerData.MmLastUnloadedDriver) { return; } g_Target->ReadPointer(g_TargetMachine, KdDebuggerData.MmUnloadedDrivers, &pvMiUnloadedDrivers); g_Target->ReadVirtual(KdDebuggerData.MmLastUnloadedDriver, &ulMiLastUnloadedDriver, sizeof(ULONG), &Size); if (pvMiUnloadedDrivers == NULL || ulMiLastUnloadedDriver == 0) { return; } // point to last unloaded drivers pdud = (PDUMP_UNLOADED_DRIVERS32)(((PULONG) pb) + 1); // // Write the list with the most recently unloaded driver first to the // least recently unloaded driver last. // Index = ulMiLastUnloadedDriver - 1; for (i = 0; i < MI_UNLOADED_DRIVERS; i += 1) { if (Index >= MI_UNLOADED_DRIVERS) { Index = MI_UNLOADED_DRIVERS - 1; } // read in unloaded driver if (g_Target->ReadVirtual(pvMiUnloadedDrivers + Index * sizeof(UNLOADED_DRIVERS32), &ud, sizeof(ud), &Size) != S_OK) { ErrOut("can't read memory from %s", FormatAddr64(pvMiUnloadedDrivers + Index * sizeof(UNLOADED_DRIVERS32))); } // copy name lengths pdud->Name.MaximumLength = ud.Name.MaximumLength; pdud->Name.Length = ud.Name.Length; if (ud.Name.Buffer == NULL) { break; } // copy start and end address pdud->StartAddress = ud.StartAddress; pdud->EndAddress = ud.EndAddress; // restrict name length and maximum name length to 12 characters if (pdud->Name.Length > MAX_UNLOADED_NAME_LENGTH) { pdud->Name.Length = MAX_UNLOADED_NAME_LENGTH; } if (pdud->Name.MaximumLength > MAX_UNLOADED_NAME_LENGTH) { pdud->Name.MaximumLength = MAX_UNLOADED_NAME_LENGTH; } // Can't store pointers in the dump so just zero it. pdud->Name.Buffer = 0; // Read in name. if (g_Target->ReadVirtual((ULONG64) ud.Name.Buffer, pdud->DriverName, pdud->Name.MaximumLength, &Size) != S_OK) { ErrOut("cannot read memory at address %08x", (ULONG)(ULONG64)(ud.Name.Buffer)); } // move to previous driver pdud += 1; Index -= 1; } // number of drivers in the list *((PULONG) pb) = i; } void CCrashDumpWrapper32::WriteMmTriageInformation(BYTE *pb) { DUMP_MM_STORAGE32 TriageInformation; ULONG64 pMmVerifierData; ULONG64 pvMmPagedPoolInfo; ULONG cbNonPagedPool; ULONG cbPagedPool; // version information TriageInformation.Version = 1; // size information TriageInformation.Size = sizeof(TriageInformation); // get special pool tag ExtractValue(MmSpecialPoolTag, TriageInformation.MmSpecialPoolTag); // get triage action taken ExtractValue(MmTriageActionTaken, TriageInformation.MiTriageActionTaken); pMmVerifierData = KdDebuggerData.MmVerifierData; // read in verifier level // BUGBUG - should not read internal data structures in MM //if (pMmVerifierData) // DmpReadMemory( // (ULONG64) &((MM_DRIVER_VERIFIER_DATA *) pMmVerifierData)->Level, // &TriageInformation.MmVerifyDriverLevel, // sizeof(TriageInformation.MmVerifyDriverLevel)); //else TriageInformation.MmVerifyDriverLevel = 0; // read in verifier ExtractValue(KernelVerifier, TriageInformation.KernelVerifier); // read non paged pool info ExtractValue(MmMaximumNonPagedPoolInBytes, cbNonPagedPool); TriageInformation.MmMaximumNonPagedPool = cbNonPagedPool / g_TargetMachine->m_PageSize; ExtractValue(MmAllocatedNonPagedPool, TriageInformation.MmAllocatedNonPagedPool); // read paged pool info ExtractValue(MmSizeOfPagedPoolInBytes, cbPagedPool); TriageInformation.PagedPoolMaximum = cbPagedPool / g_TargetMachine->m_PageSize; pvMmPagedPoolInfo = KdDebuggerData.MmPagedPoolInformation; // BUGBUG - should not read internal data structures in MM //if (pvMmPagedPoolInfo) // DmpReadMemory( // (ULONG64) &((MM_PAGED_POOL_INFO *) pvMmPagedPoolInfo)->AllocatedPagedPool, // &TriageInformation.PagedPoolAllocated, // sizeof(TriageInformation.PagedPoolAllocated)); //else TriageInformation.PagedPoolAllocated = 0; // read committed pages info ExtractValue(MmTotalCommittedPages, TriageInformation.CommittedPages); ExtractValue(MmPeakCommitment, TriageInformation.CommittedPagesPeak); ExtractValue(MmTotalCommitLimitMaximum, TriageInformation.CommitLimitMaximum); memcpy(pb, &TriageInformation, sizeof(TriageInformation)); } //---------------------------------------------------------------------------- // // CCrashDumpWrapper64. // //---------------------------------------------------------------------------- void CCrashDumpWrapper64::WriteDriverList( BYTE *pb, TRIAGE_DUMP64 *ptdh ) { ULONG Size; PDUMP_DRIVER_ENTRY64 pdde; PDUMP_STRING pds; ModuleInfo* ModIter; ULONG MaxEntries = ptdh->DriverCount; ptdh->DriverCount = 0; if (((ModIter = g_Target->GetModuleInfo(FALSE)) == NULL) || ((ModIter->Initialize()) != S_OK)) { return; } // pointer to first driver entry to write out pdde = (PDUMP_DRIVER_ENTRY64) (pb + ptdh->DriverListOffset); // pointer to first module name to write out pds = (PDUMP_STRING) (pb + ptdh->StringPoolOffset); while ((PBYTE)(pds + 1) < pb + TRIAGE_DUMP_SIZE64 && ptdh->DriverCount < MaxEntries) { MODULE_INFO_ENTRY ModEntry; ULONG retval = GetNextModuleEntry(ModIter, &ModEntry); if (retval == 1) { break; } else if (retval == 2) { continue; } pdde->LdrEntry.DllBase = ModEntry.Base; pdde->LdrEntry.SizeOfImage = ModEntry.Size; pdde->LdrEntry.CheckSum = ModEntry.CheckSum; pdde->LdrEntry.TimeDateStamp = ModEntry.TimeDateStamp; if (ModEntry.UnicodeNamePtr) { // convert length from bytes to characters pds->Length = ModEntry.NameLength / sizeof(WCHAR); if ((PBYTE)pds->Buffer + pds->Length + sizeof(WCHAR) > pb + TRIAGE_DUMP_SIZE64) { break; } CopyMemory(pds->Buffer, ModEntry.NamePtr, ModEntry.NameLength); } else { pds->Length = ModEntry.NameLength; if ((PBYTE)pds->Buffer + pds->Length + sizeof(WCHAR) > pb + TRIAGE_DUMP_SIZE64) { break; } MultiByteToWideChar(CP_ACP, 0, ModEntry.NamePtr, ModEntry.NameLength, pds->Buffer, ModEntry.NameLength); } // null terminate string pds->Buffer[pds->Length] = '\0'; pdde->DriverNameOffset = (ULONG)((ULONG_PTR) pds - (ULONG_PTR) pb); // get pointer to next string pds = (PDUMP_STRING) ALIGN_UP_POINTER(((LPBYTE) pds) + sizeof(DUMP_STRING) + sizeof(WCHAR) * (pds->Length + 1), ULONGLONG); pdde = (PDUMP_DRIVER_ENTRY64)(((PUCHAR) pdde) + sizeof(*pdde)); ptdh->DriverCount++; } ptdh->StringPoolSize = (ULONG) ((ULONG_PTR)pds - (ULONG_PTR)(pb + ptdh->StringPoolOffset)); } void CCrashDumpWrapper64::WriteUnloadedDrivers(BYTE *pb) { ULONG Size; ULONG i; ULONG Index; UNLOADED_DRIVERS64 *pud; UNLOADED_DRIVERS64 ud; PDUMP_UNLOADED_DRIVERS64 pdud; ULONG64 pvMiUnloadedDrivers; ULONG ulMiLastUnloadedDriver; *((PULONG) pb) = 0; // // find location of unloaded drivers // if (!KdDebuggerData.MmUnloadedDrivers || !KdDebuggerData.MmLastUnloadedDriver) { return; } g_Target->ReadPointer(g_TargetMachine, KdDebuggerData.MmUnloadedDrivers, &pvMiUnloadedDrivers); g_Target->ReadVirtual(KdDebuggerData.MmLastUnloadedDriver, &ulMiLastUnloadedDriver, sizeof(ULONG), &Size); if (pvMiUnloadedDrivers == NULL || ulMiLastUnloadedDriver == 0) { return; } // point to last unloaded drivers pdud = (PDUMP_UNLOADED_DRIVERS64)(((PULONG64) pb) + 1); // // Write the list with the most recently unloaded driver first to the // least recently unloaded driver last. // Index = ulMiLastUnloadedDriver - 1; for (i = 0; i < MI_UNLOADED_DRIVERS; i += 1) { if (Index >= MI_UNLOADED_DRIVERS) { Index = MI_UNLOADED_DRIVERS - 1; } // read in unloaded driver if (g_Target->ReadVirtual(pvMiUnloadedDrivers + Index * sizeof(UNLOADED_DRIVERS64), &ud, sizeof(ud), &Size) != S_OK) { ErrOut("can't read memory from %s", FormatAddr64(pvMiUnloadedDrivers + Index * sizeof(UNLOADED_DRIVERS64))); } // copy name lengths pdud->Name.MaximumLength = ud.Name.MaximumLength; pdud->Name.Length = ud.Name.Length; if (ud.Name.Buffer == NULL) { break; } // copy start and end address pdud->StartAddress = ud.StartAddress; pdud->EndAddress = ud.EndAddress; // restrict name length and maximum name length to 12 characters if (pdud->Name.Length > MAX_UNLOADED_NAME_LENGTH) { pdud->Name.Length = MAX_UNLOADED_NAME_LENGTH; } if (pdud->Name.MaximumLength > MAX_UNLOADED_NAME_LENGTH) { pdud->Name.MaximumLength = MAX_UNLOADED_NAME_LENGTH; } // Can't store pointers in the dump so just zero it. pdud->Name.Buffer = 0; // Read in name. if (g_Target->ReadVirtual(ud.Name.Buffer, pdud->DriverName, pdud->Name.MaximumLength, &Size) != S_OK) { ErrOut("cannot read memory at address %s", FormatAddr64(ud.Name.Buffer)); } // move to previous driver pdud += 1; Index -= 1; } // number of drivers in the list *((PULONG) pb) = i; } void CCrashDumpWrapper64::WriteMmTriageInformation(BYTE *pb) { DUMP_MM_STORAGE64 TriageInformation; ULONG64 pMmVerifierData; ULONG64 pvMmPagedPoolInfo; ULONG64 cbNonPagedPool; ULONG64 cbPagedPool; // version information TriageInformation.Version = 1; // size information TriageInformation.Size = sizeof(TriageInformation); // get special pool tag ExtractValue(MmSpecialPoolTag, TriageInformation.MmSpecialPoolTag); // get triage action taken ExtractValue(MmTriageActionTaken, TriageInformation.MiTriageActionTaken); pMmVerifierData = KdDebuggerData.MmVerifierData; // read in verifier level // BUGBUG - should not read internal data structures in MM //if (pMmVerifierData) // DmpReadMemory( // (ULONG64) &((MM_DRIVER_VERIFIER_DATA *) pMmVerifierData)->Level, // &TriageInformation.MmVerifyDriverLevel, // sizeof(TriageInformation.MmVerifyDriverLevel)); //else TriageInformation.MmVerifyDriverLevel = 0; // read in verifier ExtractValue(KernelVerifier, TriageInformation.KernelVerifier); // read non paged pool info ExtractValue(MmMaximumNonPagedPoolInBytes, cbNonPagedPool); TriageInformation.MmMaximumNonPagedPool = cbNonPagedPool / g_TargetMachine->m_PageSize; ExtractValue(MmAllocatedNonPagedPool, TriageInformation.MmAllocatedNonPagedPool); // read paged pool info ExtractValue(MmSizeOfPagedPoolInBytes, cbPagedPool); TriageInformation.PagedPoolMaximum = cbPagedPool / g_TargetMachine->m_PageSize; pvMmPagedPoolInfo = KdDebuggerData.MmPagedPoolInformation; // BUGBUG - should not read internal data structures in MM //if (pvMmPagedPoolInfo) // DmpReadMemory( // (ULONG64) &((MM_PAGED_POOL_INFO *) pvMmPagedPoolInfo)->AllocatedPagedPool, // &TriageInformation.PagedPoolAllocated, // sizeof(TriageInformation.PagedPoolAllocated)); //else TriageInformation.PagedPoolAllocated = 0; // read committed pages info ExtractValue(MmTotalCommittedPages, TriageInformation.CommittedPages); ExtractValue(MmPeakCommitment, TriageInformation.CommittedPagesPeak); ExtractValue(MmTotalCommitLimitMaximum, TriageInformation.CommitLimitMaximum); memcpy(pb, &TriageInformation, sizeof(TriageInformation)); }