//---------------------------------------------------------------------------- // // Generic failure analysis framework. // // Copyright (C) Microsoft Corporation, 2001. // //---------------------------------------------------------------------------- #include "precomp.h" #pragma hdrstop #include BOOL g_SymbolsReloaded; HRESULT ModuleParams::Update(void) { HRESULT Status; ULONG i; DEBUG_MODULE_PARAMETERS Params; if (m_Valid) { return S_OK; } if ((Status = g_ExtSymbols->GetModuleByModuleName(m_Name, 0, &i, &m_Base)) == S_OK && (Status = g_ExtSymbols->GetModuleParameters(1, &m_Base, i, &Params)) == S_OK) { m_Size = Params.Size; m_Valid = TRUE; } return Status; } // Read a null terminated string from the address specified. BOOL ReadAcsiiString(ULONG64 Address, PCHAR DestBuffer, ULONG BufferLen) { ULONG OneByteRead; ULONG BytesRead = 0; if (Address && DestBuffer) { while (BufferLen && ReadMemory(Address, DestBuffer, 1, &OneByteRead)) { BytesRead++; if ((*DestBuffer) == 0) { return BytesRead; } BufferLen--; DestBuffer++; Address++; } } return 0; } LONG FaExceptionFilter( struct _EXCEPTION_POINTERS *ExceptionInfo ) { ULONG Code = ExceptionInfo->ExceptionRecord->ExceptionCode; if (Code == E_OUTOFMEMORY || Code == E_INVALIDARG) { // Expected exceptions that the analysis code // can throw to terminate the analysis. Drop // into the handler. return EXCEPTION_EXECUTE_HANDLER; } // Otherwise this isn't an exception we expected. // Let it continue on so that it isn't hidden and // can be debugged. return EXCEPTION_CONTINUE_SEARCH; } BOOL FaGetSymbol( ULONG64 Address, PCHAR Name, PULONG64 Disp, ULONG NameSize ) { CHAR Buffer[MAX_PATH] = {0}; *Name = 0; GetSymbol(Address, Name, Disp); if (*Name == 0) { // // Get the actual Image name from debugger module list // ULONG Index; CHAR ModBuffer[100]; ULONG64 Base; if (S_OK == g_ExtSymbols-> GetModuleByOffset(Address, 0, &Index, &Base)) { if (g_ExtSymbols-> GetModuleNames(Index, Base, ModBuffer, sizeof(ModBuffer), NULL, NULL, 0, NULL, NULL, 0, NULL) == S_OK) { PCHAR Break = strrchr(ModBuffer, '\\'); if (Break) { CopyString(ModBuffer, Break + 1, sizeof(ModBuffer)); } CopyString(Name, ModBuffer, NameSize); if (Break = strchr(Name, '.')) { *Break = 0; } *Disp = Address - Base; } } } return (*Name != 0); } BOOL FaIsFunctionAddr( ULONG64 IP, PSTR FuncName ) // Check if IP is in the function FuncName { static ULONG64 s_LastIP = 0; static CHAR s_Buffer[MAX_PATH]; CHAR *Scan, *FnIP; ULONG64 Disp; if (s_LastIP != IP) { // This would make it faster for multiple IsFunctionAddr for same IP GetSymbol(IP, s_Buffer, &Disp); s_LastIP = IP; } if (Scan = strchr(s_Buffer, '!')) { FnIP = Scan + 1; while (*FnIP == '_') { ++FnIP; } } else { FnIP = &s_Buffer[0]; } return !strncmp(FnIP, FuncName, strlen(FuncName)); } BOOL FaGetFollowupInfo( IN OPTIONAL ULONG64 Addr, PCHAR SymbolName, PCHAR Owner, ULONG OwnerSize ) { EXT_TRIAGE_FOLLOWUP FollowUp = &_EFN_GetTriageFollowupFromSymbol; DEBUG_TRIAGE_FOLLOWUP_INFO Info; CHAR Buffer[MAX_PATH]; if (!*SymbolName) { ULONG64 Disp; FaGetSymbol(Addr, Buffer, &Disp, sizeof(Buffer)); SymbolName = Buffer; } if (*SymbolName) { Info.SizeOfStruct = sizeof(Info); Info.OwnerName = Owner; Info.OwnerNameSize = (USHORT)OwnerSize; if ((*FollowUp)(g_ExtClient, SymbolName, &Info) > TRIAGE_FOLLOWUP_IGNORE) { // This is an interesting routine to followup on return TRUE; } } if (Owner) { *Owner=0; } return FALSE; } HRESULT FaGetPoolTagFollowup( PCHAR szPoolTag, PSTR Followup, ULONG FollowupSize ) { ULONG PoolTag; DEBUG_POOLTAG_DESCRIPTION TagDesc = {0}; PGET_POOL_TAG_DESCRIPTION pGetTagDesc = NULL; TagDesc.SizeOfStruct = sizeof(TagDesc); if (g_ExtControl-> GetExtensionFunction(0, "GetPoolTagDescription", (FARPROC*)&pGetTagDesc) == S_OK && pGetTagDesc) { PoolTag = *((PULONG) szPoolTag); if ((*pGetTagDesc)(PoolTag, &TagDesc) == S_OK) { PCHAR Dot; if (Dot = strchr(TagDesc.Binary, '.')) { *Dot = 0; } if (TagDesc.Binary[0]) { if (FaGetFollowupInfo(0, TagDesc.Binary, Followup, FollowupSize)) { return S_OK; } } if (TagDesc.Owner[0]) { CopyString(Followup, TagDesc.Owner, FollowupSize); return S_OK; } } } return E_FAIL; } ULONG64 FaGetImplicitStackOffset( void ) { // IDebugRegisters::GetStackOffset not used since it // ignores implicit context ULONG64 Stk = 0; switch (g_TargetMachine) { case IMAGE_FILE_MACHINE_I386: Stk = GetExpression("@esp"); break; case IMAGE_FILE_MACHINE_IA64: Stk = GetExpression("@sp"); break; case IMAGE_FILE_MACHINE_AMD64: Stk = GetExpression("@rsp"); break; } return Stk; } DECLARE_API( analyze ) { ULONG EventType, ProcId, ThreadId; BOOL Force = FALSE; BOOL ForceUser = FALSE; INIT_API(); if (g_ExtControl->GetLastEventInformation(&EventType, &ProcId, &ThreadId, NULL, 0, NULL, NULL, 0, NULL) != S_OK) { ExtErr("Unable to get last event information\n"); goto Exit; } // // Check for -f in both cases // PCSTR tmpArgs = args; while (*args) { if (*args == '-') { ++args; if (*args == 'f') { Force = TRUE; break; } else if (!strncmp(args, "show",4)) { Force = TRUE; } else if (*args == 'u') { // could be use for user stack anlysis in k-mode // ForceUser = TRUE; } } ++args; } args = tmpArgs; // // Call the correct routine to process the event. // if ((EventType == DEBUG_EVENT_EXCEPTION) || (Force == TRUE)) { ULONG DebugType, DebugQual; if (g_ExtControl->GetDebuggeeType(&DebugType, &DebugQual) != S_OK) { ExtErr("Unable to determine debuggee type\n"); Status = E_FAIL; } else { if (ForceUser) { DebugType = DEBUG_CLASS_USER_WINDOWS; } switch(DebugType) { case DEBUG_CLASS_KERNEL: // // For live debug sessions force the symbols to get reloaded // the first time as we find many sessions where the // debugger got reconnected and no module list exists. // This also happens for user mode breaks in kd where the // module list is wrong. // if ((g_TargetQualifier == DEBUG_KERNEL_CONNECTION) && (!g_SymbolsReloaded++)) { g_ExtSymbols->Reload(""); } Status = AnalyzeBugCheck(args); break; case DEBUG_CLASS_USER_WINDOWS: Status = AnalyzeUserException(args); break; case DEBUG_CLASS_UNINITIALIZED: ExtErr("No debuggee\n"); Status = E_FAIL; default: ExtErr("Unknown debuggee type\n"); Status = E_INVALIDARG; } } } else if (EventType == 0) { dprintf("The debuggee is ready to run\n"); Status = S_OK; } else { Status = E_NOINTERFACE; } if (Status == E_NOINTERFACE) { g_ExtControl->Execute(DEBUG_OUTCTL_ALL_CLIENTS, ".lastevent", DEBUG_EXECUTE_DEFAULT); } Exit: EXIT_API(); return Status; } HRESULT _EFN_GetFailureAnalysis( IN PDEBUG_CLIENT Client, IN ULONG Flags, OUT PDEBUG_FAILURE_ANALYSIS* Analysis ) { BOOL Enter = (g_ExtClient != Client); HRESULT Hr; if (Enter) { INIT_API(); } ULONG DebugType, DebugQual; if ((Hr = g_ExtControl->GetDebuggeeType(&DebugType, &DebugQual)) != S_OK) { ExtErr("Unable to determine debuggee type\n"); } else if (DebugType == DEBUG_CLASS_KERNEL) { BUGCHECK_ANALYSIS Bc; *Analysis = (IDebugFailureAnalysis*)BcAnalyze(&Bc, Flags); Hr = *Analysis ? S_OK : E_OUTOFMEMORY; } else if (DebugType == DEBUG_CLASS_USER_WINDOWS) { EX_STATE ExState; *Analysis = (IDebugFailureAnalysis*)UeAnalyze(&ExState, Flags); Hr = *Analysis ? S_OK : E_OUTOFMEMORY; } else { Hr = E_INVALIDARG; } if (Enter) { EXIT_API(); } return Hr; } DECLARE_API( dumpfa ) { INIT_API(); ULONG64 Address = GetExpression(args); if (Address) { ULONG64 Data; ULONG64 DataUsed; ULONG EntrySize; EntrySize = GetTypeSize("ext!_FA_ENTRY"); InitTypeRead(Address, ext!DebugFailureAnalysis); Data = ReadField(m_Data); DataUsed = ReadField(m_DataUsed); g_ExtControl->Output(1, "DataUsed %x\n", (ULONG)DataUsed); while (DataUsed > EntrySize) { ULONG FullSize; InitTypeRead(Data, ext!_FA_ENTRY); g_ExtControl->Output(1, "Type = %08lx - Size = %x\n", (ULONG)ReadField(Tag), ReadField(DataSize)); FullSize = (ULONG)ReadField(FullSize); Data += FullSize; DataUsed -= FullSize; } } EXIT_API(); return S_OK; } //---------------------------------------------------------------------------- // // DebugFailureAnalysisImpl. // //---------------------------------------------------------------------------- #define FA_ALIGN(Size) (((Size) + 7) & ~7) #define FA_GROW_BY 4096 #if DBG #define SCORCH_ENTRY(Entry) \ memset((Entry) + 1, 0xdb, (Entry)->FullSize - sizeof(*(Entry))) #else #define SCORCH_ENTRY(Entry) #endif #define RAISE_ERROR(Code) RaiseException(Code, 0, 0, NULL) DebugFailureAnalysis::DebugFailureAnalysis(void) { m_Refs = 1; m_FailureClass = DEBUG_CLASS_UNINITIALIZED; m_FailureType = DEBUG_FLR_UNKNOWN; m_FailureCode = 0; m_Data = NULL; m_DataLen = 0; m_DataUsed = 0; ZeroMemory(PossibleFollowups, sizeof(PossibleFollowups)); BestClassFollowUp = (FlpClasses)0; } DebugFailureAnalysis::~DebugFailureAnalysis(void) { free(m_Data); } STDMETHODIMP DebugFailureAnalysis::QueryInterface( THIS_ IN REFIID InterfaceId, OUT PVOID* Interface ) { HRESULT Status; *Interface = NULL; Status = S_OK; if (IsEqualIID(InterfaceId, IID_IUnknown) || IsEqualIID(InterfaceId, __uuidof(IDebugFailureAnalysis))) { *Interface = (IDebugFailureAnalysis *)this; AddRef(); } else { Status = E_NOINTERFACE; } return Status; } STDMETHODIMP_(ULONG) DebugFailureAnalysis::AddRef( THIS ) { return InterlockedIncrement((PLONG)&m_Refs); } STDMETHODIMP_(ULONG) DebugFailureAnalysis::Release( THIS ) { LONG Refs = InterlockedDecrement((PLONG)&m_Refs); if (Refs == 0) { delete this; } return Refs; } STDMETHODIMP_(ULONG) DebugFailureAnalysis::GetFailureClass(void) { return m_FailureClass; } STDMETHODIMP_(DEBUG_FAILURE_TYPE) DebugFailureAnalysis::GetFailureType(void) { return m_FailureType; } STDMETHODIMP_(ULONG) DebugFailureAnalysis::GetFailureCode(void) { return m_FailureCode; } STDMETHODIMP_(FA_ENTRY*) DebugFailureAnalysis::Get(FA_TAG Tag) { FA_ENTRY* Entry = NULL; while ((Entry = NextEntry(Entry)) != NULL) { if (Entry->Tag == Tag) { return Entry; } } return NULL; } STDMETHODIMP_(FA_ENTRY*) DebugFailureAnalysis::GetNext(FA_ENTRY* Entry, FA_TAG Tag, FA_TAG TagMask) { while ((Entry = NextEntry(Entry)) != NULL) { if ((Entry->Tag & TagMask) == Tag) { return Entry; } } return NULL; } STDMETHODIMP_(FA_ENTRY*) DebugFailureAnalysis::GetString(FA_TAG Tag, PSTR Str, ULONG MaxSize) { FA_ENTRY* Entry = Get(Tag); if (Entry != NULL) { if (Entry->DataSize > MaxSize) { return NULL; } CopyString(Str, FA_ENTRY_DATA(PSTR, Entry),MaxSize); } return Entry; } STDMETHODIMP_(FA_ENTRY*) DebugFailureAnalysis::GetBuffer(FA_TAG Tag, PVOID Buf, ULONG Size) { FA_ENTRY* Entry = Get(Tag); if (Entry != NULL) { if (Entry->DataSize != Size) { return NULL; } memcpy(Buf, FA_ENTRY_DATA(PUCHAR, Entry), Size); } return Entry; } STDMETHODIMP_(FA_ENTRY*) DebugFailureAnalysis::GetUlong(FA_TAG Tag, PULONG Value) { return GetBuffer(Tag, Value, sizeof(*Value)); } STDMETHODIMP_(FA_ENTRY*) DebugFailureAnalysis::GetUlong64(FA_TAG Tag, PULONG64 Value) { return GetBuffer(Tag, Value, sizeof(*Value)); } STDMETHODIMP_(FA_ENTRY*) DebugFailureAnalysis::NextEntry(FA_ENTRY* Entry) { if (Entry == NULL) { Entry = (FA_ENTRY*)m_Data; } else { Entry = (FA_ENTRY*)((PUCHAR)Entry + Entry->FullSize); } if (ValidEntry(Entry)) { return Entry; } else { return NULL; } } FA_ENTRY* DebugFailureAnalysis::Set(FA_TAG Tag, ULONG Size) { FA_ENTRY* Entry; ULONG FullSize; // Compute full rounded size. FullSize = sizeof(FA_ENTRY) + FA_ALIGN(Size); // Check and see if there's already an entry. Entry = Get(Tag); if (Entry != NULL) { // If it's already large enough use it and // pack in remaining data. if (Entry->FullSize >= FullSize) { ULONG Pack = Entry->FullSize - FullSize; if (Pack > 0) { PackData((PUCHAR)Entry + FullSize, Pack); Entry->FullSize = (USHORT)FullSize; } Entry->DataSize = (USHORT)Size; SCORCH_ENTRY(Entry); return Entry; } // Entry is too small so remove it. PackData((PUCHAR)Entry, Entry->FullSize); } return Add(Tag, Size); } FA_ENTRY* DebugFailureAnalysis::SetString(FA_TAG Tag, PSTR Str) { ULONG Size = strlen(Str) + 1; FA_ENTRY* Entry = Set(Tag, Size); if (Entry != NULL) { memcpy(FA_ENTRY_DATA(PSTR, Entry), Str, Size); } return Entry; } FA_ENTRY* DebugFailureAnalysis::SetStrings(FA_TAG Tag, ULONG Count, PSTR* Strs) { ULONG i; ULONG Size = 0; for (i = 0; i < Count; i++) { Size += strlen(Strs[i]) + 1; } // Put a double terminator at the very end. Size++; FA_ENTRY* Entry = Set(Tag, Size); if (Entry != NULL) { PSTR Data = FA_ENTRY_DATA(PSTR, Entry); for (i = 0; i < Count; i++) { Size = strlen(Strs[i]) + 1; memcpy(Data, Strs[i], Size); Data += Size; } *Data = 0; } return Entry; } FA_ENTRY* DebugFailureAnalysis::SetBuffer(FA_TAG Tag, PVOID Buf, ULONG Size) { FA_ENTRY* Entry = Set(Tag, Size); if (Entry != NULL) { memcpy(FA_ENTRY_DATA(PUCHAR, Entry), Buf, Size); } return Entry; } FA_ENTRY* DebugFailureAnalysis::Add(FA_TAG Tag, ULONG Size) { // Compute full rounded size. ULONG FullSize = sizeof(FA_ENTRY) + FA_ALIGN(Size); FA_ENTRY* Entry = AllocateEntry(FullSize); if (Entry != NULL) { Entry->Tag = Tag; Entry->FullSize = (USHORT)FullSize; Entry->DataSize = (USHORT)Size; SCORCH_ENTRY(Entry); } return Entry; } ULONG DebugFailureAnalysis::Delete(FA_TAG Tag, FA_TAG TagMask) { ULONG Deleted = 0; FA_ENTRY* Entry = NextEntry(NULL); while (Entry != NULL) { if ((Entry->Tag & TagMask) == Tag) { PackData((PUCHAR)Entry, Entry->FullSize); Deleted++; // Check and see if we packed away the last entry. if (!ValidEntry(Entry)) { break; } } else { Entry = NextEntry(Entry); } } return Deleted; } void DebugFailureAnalysis::Empty(void) { // Reset used to just the header. m_DataUsed = 0; } FA_ENTRY* DebugFailureAnalysis::AllocateEntry(ULONG FullSize) { // Sizes must fit in USHORTs. This shouldn't be // a big problem since analyses shouldn't have // huge data items in them. if (FullSize > 0xffff) { RAISE_ERROR(E_INVALIDARG); return NULL; } if (m_DataUsed + FullSize > m_DataLen) { ULONG NewLen = m_DataLen; do { NewLen += FA_GROW_BY; } while (m_DataUsed + FullSize > NewLen); PUCHAR NewData = (PUCHAR)realloc(m_Data, NewLen); if (NewData == NULL) { RAISE_ERROR(E_OUTOFMEMORY); return NULL; } m_Data = NewData; m_DataLen = NewLen; } FA_ENTRY* Entry = (FA_ENTRY*)(m_Data + m_DataUsed); m_DataUsed += FullSize; return Entry; } void DebugFailureAnalysis::DbFindBucketInfo( void ) { SolutionDatabaseHandler *Db; CHAR Solution[SOLUTION_TEXT_SIZE]; CHAR SolOSVer[OS_VER_SIZE]; ULONG RaidBug; FA_ENTRY* BucketEntry; FA_ENTRY* GBucketEntry; FA_ENTRY* DriverNameEntry = NULL; FA_ENTRY* TimeStampEntry = NULL; static CHAR SolvedBucket[MAX_PATH] = {0}, SolvedgBucket[MAX_PATH] = {0}; static CHAR SolutionString[MAX_PATH] = {0}; static ULONG SolutionId = 0, SolutionType, SolutionIdgBucket = 0; if (GetProcessingFlags() & FAILURE_ANALYSIS_NO_DB_LOOKUP) { return; } if (!(BucketEntry = Get(DEBUG_FLR_BUCKET_ID))) { return; } if (!strcmp(SolvedBucket, FA_ENTRY_DATA(PCHAR, BucketEntry))) { if (SolutionType != CiSolUnsolved) { SetString(DEBUG_FLR_INTERNAL_SOLUTION_TEXT, SolutionString); } SetUlong(DEBUG_FLR_SOLUTION_ID, SolutionId); SetUlong(DEBUG_FLR_SOLUTION_TYPE, SolutionType); // Generic bucket if (BucketEntry = Get(DEBUG_FLR_DEFAULT_BUCKET_ID)) { if (!strcmp(SolvedgBucket, FA_ENTRY_DATA(PCHAR, BucketEntry))) { SetUlong(DEBUG_FLR_DEFAULT_SOLUTION_ID, SolutionIdgBucket); } } return; } // if (!(DriverNameEntry = Get(DEBUG_FLR_IMAGE_NAME)) || // !(TimeStampEntry = Get(DEBUG_FLR_IMAGE_TIMESTAMP))) // { // return; // } HRESULT Hr; BOOL SolDbInitialized = g_SolDb != NULL; if (!SolDbInitialized) { if (FAILED(Hr = InitializeDatabaseHandlers(g_ExtControl, 4))) { // dprintf("Database initialize failed %lx\n", Hr); return; } SolDbInitialized = TRUE; } if (g_SolDb->ConnectToDataBase()) { if (GBucketEntry = Get(DEBUG_FLR_DEFAULT_BUCKET_ID)) { CopyString(SolvedgBucket, FA_ENTRY_DATA(PCHAR, GBucketEntry), sizeof(SolvedgBucket)); } // // List crashes for the same bucket // CopyString(SolvedBucket, FA_ENTRY_DATA(PCHAR, BucketEntry), sizeof(SolvedBucket)); if (SUCCEEDED(Hr = g_SolDb->GetSolutionFromDB(FA_ENTRY_DATA(PCHAR, BucketEntry), SolvedgBucket, NULL, 0, // FA_ENTRY_DATA(PCHAR, DriverNameEntry), // (ULONG)(*FA_ENTRY_DATA(PULONG64, TimeStampEntry)), 0, Solution, SOLUTION_TEXT_SIZE, &SolutionId, &SolutionType, &SolutionIdgBucket))) { if (SolutionId != 0) { SetString(DEBUG_FLR_INTERNAL_SOLUTION_TEXT, Solution); CopyString(SolutionString, Solution, sizeof(SolutionString)); } else { SolutionId = -1; // unsolved SolutionType = 0; } if (SolutionIdgBucket == 0) { SolutionIdgBucket = -1; // unsolved } SetUlong(DEBUG_FLR_SOLUTION_ID, SolutionId); SetUlong(DEBUG_FLR_SOLUTION_TYPE, SolutionType); SetUlong(DEBUG_FLR_DEFAULT_SOLUTION_ID, SolutionIdgBucket); } else { // We did not succesfully look up in DB SolvedgBucket[0] = '\0'; SolvedBucket[0] = '\0'; } #if 0 if (SolOSVer[0] != '\0') { SetString(DEBUG_FLR_FIXED_IN_OSVERSION, SolOSVer); } } if (Db->FindRaidBug(FA_ENTRY_DATA(PCHAR, Entry), &RaidBug) == S_OK) { SetUlong64(DEBUG_FLR_INTERNAL_RAID_BUG, RaidBug); } #endif } ; if (SolDbInitialized) { UnInitializeDatabaseHandlers(FALSE); } return; } FLR_LOOKUP_TABLE FlrLookupTable[] = { DEBUG_FLR_RESERVED , "RESERVED" ,DEBUG_FLR_DRIVER_OBJECT , "DRIVER_OBJECT" ,DEBUG_FLR_DEVICE_OBJECT , "DEVICE_OBJECT" ,DEBUG_FLR_INVALID_PFN , "INVALID_PFN" ,DEBUG_FLR_WORKER_ROUTINE , "WORKER_ROUTINE" ,DEBUG_FLR_WORK_ITEM , "WORK_ITEM" ,DEBUG_FLR_INVALID_DPC_FOUND , "INVALID_DPC_FOUND" ,DEBUG_FLR_PROCESS_OBJECT , "PROCESS_OBJECT" ,DEBUG_FLR_FAILED_INSTRUCTION_ADDRESS , "FAILED_INSTRUCTION_ADDRESS" ,DEBUG_FLR_LAST_CONTROL_TRANSFER , "LAST_CONTROL_TRANSFER" ,DEBUG_FLR_ACPI_EXTENSION , "ACPI_EXTENSION" ,DEBUG_FLR_ACPI_OBJECT , "ACPI_OBJECT" ,DEBUG_FLR_PROCESS_NAME , "PROCESS_NAME" ,DEBUG_FLR_READ_ADDRESS , "READ_ADDRESS" ,DEBUG_FLR_WRITE_ADDRESS , "WRITE_ADDRESS" ,DEBUG_FLR_CRITICAL_SECTION , "CRITICAL_SECTION" ,DEBUG_FLR_BAD_HANDLE , "BAD_HANDLE" ,DEBUG_FLR_INVALID_HEAP_ADDRESS , "INVALID_HEAP_ADDRESS" ,DEBUG_FLR_IRP_ADDRESS , "IRP_ADDRESS" ,DEBUG_FLR_IRP_MAJOR_FN , "IRP_MAJOR_FN" ,DEBUG_FLR_IRP_MINOR_FN , "IRP_MINOR_FN" ,DEBUG_FLR_IRP_CANCEL_ROUTINE , "IRP_CANCEL_ROUTINE" ,DEBUG_FLR_IOSB_ADDRESS , "IOSB_ADDRESS" ,DEBUG_FLR_INVALID_USEREVENT , "INVALID_USEREVENT" ,DEBUG_FLR_PREVIOUS_MODE , "PREVIOUS_MODE" ,DEBUG_FLR_CURRENT_IRQL , "CURRENT_IRQL" ,DEBUG_FLR_PREVIOUS_IRQL , "PREVIOUS_IRQL" ,DEBUG_FLR_REQUESTED_IRQL , "REQUESTED_IRQL" ,DEBUG_FLR_ASSERT_DATA , "ASSERT_DATA" ,DEBUG_FLR_ASSERT_FILE , "ASSERT_FILE_LOCATION" ,DEBUG_FLR_EXCEPTION_PARAMETER1 , "EXCEPTION_PARAMETER1" ,DEBUG_FLR_EXCEPTION_PARAMETER2 , "EXCEPTION_PARAMETER2" ,DEBUG_FLR_EXCEPTION_PARAMETER3 , "EXCEPTION_PARAMETER3" ,DEBUG_FLR_EXCEPTION_PARAMETER4 , "EXCEPTION_PARAMETER4" ,DEBUG_FLR_EXCEPTION_RECORD , "EXCEPTION_RECORD" ,DEBUG_FLR_POOL_ADDRESS , "POOL_ADDRESS" ,DEBUG_FLR_CORRUPTING_POOL_ADDRESS , "CORRUPTING_POOL_ADDRESS" ,DEBUG_FLR_CORRUPTING_POOL_TAG , "CORRUPTING_POOL_TAG" ,DEBUG_FLR_FREED_POOL_TAG , "FREED_POOL_TAG" ,DEBUG_FLR_SPECIAL_POOL_CORRUPTION_TYPE , "SPECIAL_POOL_CORRUPTION_TYPE" ,DEBUG_FLR_FILE_ID , "FILE_ID" ,DEBUG_FLR_FILE_LINE , "FILE_LINE" ,DEBUG_FLR_BUGCHECK_STR , "BUGCHECK_STR" ,DEBUG_FLR_BUGCHECK_SPECIFIER , "BUGCHECK_SPECIFIER" ,DEBUG_FLR_DRIVER_VERIFIER_IO_VIOLATION_TYPE, "DRIVER_VERIFIER_IO_VIOLATION_TYPE" ,DEBUG_FLR_EXCEPTION_CODE , "EXCEPTION_CODE" ,DEBUG_FLR_STATUS_CODE , "STATUS_CODE" ,DEBUG_FLR_IOCONTROL_CODE , "IOCONTROL_CODE" ,DEBUG_FLR_MM_INTERNAL_CODE , "MM_INTERNAL_CODE" ,DEBUG_FLR_DRVPOWERSTATE_SUBCODE , "DRVPOWERSTATE_SUBCODE" ,DEBUG_FLR_CORRUPT_MODULE_LIST , "CORRUPT_MODULE_LIST" ,DEBUG_FLR_BAD_STACK , "BAD_STACK" ,DEBUG_FLR_ZEROED_STACK , "ZEROED_STACK" ,DEBUG_FLR_WRONG_SYMBOLS , "WRONG_SYMBOLS" ,DEBUG_FLR_FOLLOWUP_DRIVER_ONLY , "FOLLOWUP_DRIVER_ONLY" ,DEBUG_FLR_CPU_OVERCLOCKED , "CPU_OVERCLOCKED" ,DEBUG_FLR_MANUAL_BREAKIN , "MANUAL_BREAKIN" ,DEBUG_FLR_POSSIBLE_INVALID_CONTROL_TRANSFER, "POSSIBLE_INVALID_CONTROL_TRANSFER" ,DEBUG_FLR_POISONED_TB , "POISONED_TB" ,DEBUG_FLR_UNKNOWN_MODULE , "UNKNOWN_MODULE" ,DEBUG_FLR_ANALYZAABLE_POOL_CORRUPTION , "ANALYZAABLE_POOL_CORRUPTION" ,DEBUG_FLR_SINGLE_BIT_ERROR , "SINGLE_BIT_ERROR" ,DEBUG_FLR_TWO_BIT_ERROR , "TWO_BIT_ERROR" ,DEBUG_FLR_INVALID_KERNEL_CONTEXT , "INVALID_KERNEL_CONTEXT" ,DEBUG_FLR_DISK_HARDWARE_ERROR , "DISK_HARDWARE_ERROR" ,DEBUG_FLR_POOL_CORRUPTOR , "POOL_CORRUPTOR" ,DEBUG_FLR_MEMORY_CORRUPTOR , "MEMORY_CORRUPTOR" ,DEBUG_FLR_UNALIGNED_STACK_POINTER , "UNALIGNED_STACK_POINTER" ,DEBUG_FLR_OLD_OS_VERSION , "OLD_OS_VERSION" ,DEBUG_FLR_BUGCHECKING_DRIVER , "BUGCHECKING_DRIVER" ,DEBUG_FLR_BUCKET_ID , "BUCKET_ID" ,DEBUG_FLR_IMAGE_NAME , "IMAGE_NAME" ,DEBUG_FLR_SYMBOL_NAME , "SYMBOL_NAME" ,DEBUG_FLR_FOLLOWUP_NAME , "FOLLOWUP_NAME" ,DEBUG_FLR_STACK_COMMAND , "STACK_COMMAND" ,DEBUG_FLR_STACK_TEXT , "STACK_TEXT" ,DEBUG_FLR_INTERNAL_SOLUTION_TEXT , "INTERNAL_SOLUTION_TEXT" ,DEBUG_FLR_MODULE_NAME , "MODULE_NAME" ,DEBUG_FLR_INTERNAL_RAID_BUG , "INTERNAL_RAID_BUG" ,DEBUG_FLR_FIXED_IN_OSVERSION , "FIXED_IN_OSVERSION" ,DEBUG_FLR_DEFAULT_BUCKET_ID , "DEFAULT_BUCKET_ID" ,DEBUG_FLR_FAULTING_IP , "FAULTING_IP" ,DEBUG_FLR_FAULTING_MODULE , "FAULTING_MODULE" ,DEBUG_FLR_IMAGE_TIMESTAMP , "DEBUG_FLR_IMAGE_TIMESTAMP" ,DEBUG_FLR_FOLLOWUP_IP , "FOLLOWUP_IP" ,DEBUG_FLR_FAULTING_THREAD , "FAULTING_THREAD" ,DEBUG_FLR_CONTEXT , "CONTEXT" ,DEBUG_FLR_TRAP_FRAME , "TRAP_FRAME" ,DEBUG_FLR_TSS , "TSS" ,DEBUG_FLR_SHOW_ERRORLOG , "ERROR_LOG" ,DEBUG_FLR_MASK_ALL , "MASK_ALL" // Zero entry Must be last; ,DEBUG_FLR_INVALID , "INVALID" }; void DebugFailureAnalysis::OutputEntryParam(DEBUG_FLR_PARAM_TYPE Type) { FA_ENTRY *Entry = Get(Type); if (Entry) { OutputEntry(Entry); } } void DebugFailureAnalysis::OutputEntry(FA_ENTRY* Entry) { CHAR Buffer[MAX_PATH]; CHAR Module[MAX_PATH]; ULONG64 Base; ULONG64 Address; ULONG OutCtl; ULONG i = 0; OutCtl = DEBUG_OUTCTL_AMBIENT; switch(Entry->Tag) { // These are just tags - don't print out case DEBUG_FLR_CORRUPT_MODULE_LIST: case DEBUG_FLR_BAD_STACK: case DEBUG_FLR_ZEROED_STACK: case DEBUG_FLR_WRONG_SYMBOLS: case DEBUG_FLR_FOLLOWUP_DRIVER_ONLY: case DEBUG_FLR_UNKNOWN_MODULE: case DEBUG_FLR_ANALYZAABLE_POOL_CORRUPTION: case DEBUG_FLR_INVALID_KERNEL_CONTEXT: case DEBUG_FLR_SOLUTION_TYPE: case DEBUG_FLR_MANUAL_BREAKIN: // soluion ids from DB case DEBUG_FLR_SOLUTION_ID: case DEBUG_FLR_DEFAULT_SOLUTION_ID: // Field folded into others case DEBUG_FLR_BUGCHECK_SPECIFIER: return; // Marged with other output return; } // // Find the entry in the description table // while(FlrLookupTable[i].Data && Entry->Tag != FlrLookupTable[i].Data) { i++; } dprintf("\n%s: ", FlrLookupTable[i].String); switch(Entry->Tag) { // Notification to user case DEBUG_FLR_DISK_HARDWARE_ERROR: // FlrLookupTable value has already been printed dprintf("There was error with disk hardware\n"); break; // Strings: case DEBUG_FLR_ASSERT_DATA: case DEBUG_FLR_ASSERT_FILE: case DEBUG_FLR_BUCKET_ID: case DEBUG_FLR_DEFAULT_BUCKET_ID: case DEBUG_FLR_STACK_TEXT: case DEBUG_FLR_STACK_COMMAND: case DEBUG_FLR_INTERNAL_SOLUTION_TEXT: case DEBUG_FLR_FIXED_IN_OSVERSION: case DEBUG_FLR_BUGCHECK_STR: case DEBUG_FLR_IMAGE_NAME: case DEBUG_FLR_MODULE_NAME: case DEBUG_FLR_PROCESS_NAME: case DEBUG_FLR_FOLLOWUP_NAME: case DEBUG_FLR_POOL_CORRUPTOR: case DEBUG_FLR_MEMORY_CORRUPTOR: case DEBUG_FLR_BUGCHECKING_DRIVER: case DEBUG_FLR_SYMBOL_NAME: case DEBUG_FLR_CORRUPTING_POOL_TAG: case DEBUG_FLR_FREED_POOL_TAG: dprintf(" %s\n", FA_ENTRY_DATA(PCHAR, Entry)); break; // DWORDs: case DEBUG_FLR_PREVIOUS_IRQL: case DEBUG_FLR_CURRENT_IRQL: case DEBUG_FLR_MM_INTERNAL_CODE: case DEBUG_FLR_SPECIAL_POOL_CORRUPTION_TYPE: case DEBUG_FLR_PREVIOUS_MODE: case DEBUG_FLR_IMAGE_TIMESTAMP: case DEBUG_FLR_SINGLE_BIT_ERROR: case DEBUG_FLR_TWO_BIT_ERROR: dprintf(" %lx\n", *FA_ENTRY_DATA(PULONG64, Entry)); break; // DWORDs: case DEBUG_FLR_INTERNAL_RAID_BUG: case DEBUG_FLR_OLD_OS_VERSION: dprintf(" %d\n", *FA_ENTRY_DATA(PULONG64, Entry)); break; // Pointers case DEBUG_FLR_PROCESS_OBJECT: case DEBUG_FLR_DEVICE_OBJECT: case DEBUG_FLR_DRIVER_OBJECT: case DEBUG_FLR_ACPI_OBJECT: case DEBUG_FLR_IRP_ADDRESS: case DEBUG_FLR_EXCEPTION_PARAMETER1: case DEBUG_FLR_EXCEPTION_PARAMETER2: case DEBUG_FLR_FAULTING_THREAD: case DEBUG_FLR_WORK_ITEM: dprintf(" %p\n", *FA_ENTRY_DATA(PULONG64, Entry)); break; // Pointers to code case DEBUG_FLR_WORKER_ROUTINE: case DEBUG_FLR_IRP_CANCEL_ROUTINE: case DEBUG_FLR_FAILED_INSTRUCTION_ADDRESS: case DEBUG_FLR_FAULTING_IP: case DEBUG_FLR_FOLLOWUP_IP: case DEBUG_FLR_FAULTING_MODULE: FaGetSymbol(*FA_ENTRY_DATA(PULONG64, Entry), Buffer, &Address, sizeof(Buffer)); dprintf("\n%s+%I64lx\n", Buffer, Address); g_ExtControl->OutputDisassemblyLines(OutCtl, 0, 1, *FA_ENTRY_DATA(PULONG64, Entry), 0, NULL, NULL, NULL, NULL); break; // Address description case DEBUG_FLR_READ_ADDRESS: case DEBUG_FLR_WRITE_ADDRESS: case DEBUG_FLR_POOL_ADDRESS: case DEBUG_FLR_CORRUPTING_POOL_ADDRESS: { PCSTR Desc = DescribeAddress(*FA_ENTRY_DATA(PULONG64, Entry)); if (!Desc) { Desc = ""; } dprintf(" %p %s\n", *FA_ENTRY_DATA(PULONG64, Entry), Desc); break; } case DEBUG_FLR_EXCEPTION_CODE: case DEBUG_FLR_STATUS_CODE: { DEBUG_DECODE_ERROR Err; Err.Code = (ULONG) *FA_ENTRY_DATA(PULONG, Entry); Err.TreatAsStatus = (Entry->Tag == DEBUG_FLR_STATUS_CODE); // dprintf(" %lx", *FA_ENTRY_DATA(PULONG, Entry)); DecodeErrorForMessage( &Err ); if (!Err.TreatAsStatus) { dprintf("(%s) %#x (%u) - %s\n", Err.Source, Err.Code, Err.Code, Err.Message); } else { dprintf("(%s) %#x - %s\n", Err.Source, Err.Code, Err.Message); } break; } case DEBUG_FLR_CPU_OVERCLOCKED: dprintf(" *** Machine was determined to be overclocked !\n"); break; case DEBUG_FLR_ACPI_EXTENSION: dprintf(" %p -- (!acpikd.acpiext %p)\n", *FA_ENTRY_DATA(PULONG64, Entry), *FA_ENTRY_DATA(PULONG64, Entry)); sprintf(Buffer, "!acpikd.acpiext %I64lx", *FA_ENTRY_DATA(PULONG64, Entry)); g_ExtControl->Execute(OutCtl, Buffer, DEBUG_EXECUTE_DEFAULT); break; case DEBUG_FLR_TRAP_FRAME: dprintf(" %p -- (.trap %I64lx)\n", *FA_ENTRY_DATA(PULONG64, Entry), *FA_ENTRY_DATA(PULONG64, Entry)); sprintf(Buffer, ".trap %I64lx", *FA_ENTRY_DATA(PULONG64, Entry)); g_ExtControl->Execute(OutCtl, Buffer, DEBUG_EXECUTE_DEFAULT); g_ExtControl->Execute(OutCtl, ".trap", DEBUG_EXECUTE_DEFAULT); break; case DEBUG_FLR_CONTEXT: dprintf(" %p -- (.cxr %I64lx)\n", *FA_ENTRY_DATA(PULONG64, Entry), *FA_ENTRY_DATA(PULONG64, Entry)); sprintf(Buffer, ".cxr %I64lx", *FA_ENTRY_DATA(PULONG64, Entry)); g_ExtControl->Execute(OutCtl, Buffer, DEBUG_EXECUTE_DEFAULT); g_ExtControl->Execute(OutCtl, ".cxr", DEBUG_EXECUTE_DEFAULT); break; case DEBUG_FLR_EXCEPTION_RECORD: dprintf(" %p -- (.exr %I64lx)\n", *FA_ENTRY_DATA(PULONG64, Entry), *FA_ENTRY_DATA(PULONG64, Entry)); sprintf(Buffer, ".exr %I64lx", *FA_ENTRY_DATA(PULONG64, Entry)); g_ExtControl->Execute(OutCtl, Buffer, DEBUG_EXECUTE_DEFAULT); break; case DEBUG_FLR_TSS: dprintf(" %p -- (.tss %I64lx)\n", *FA_ENTRY_DATA(PULONG64, Entry), *FA_ENTRY_DATA(PULONG64, Entry)); sprintf(Buffer, ".tss %I64lx", *FA_ENTRY_DATA(PULONG64, Entry)); g_ExtControl->Execute(OutCtl, Buffer, DEBUG_EXECUTE_DEFAULT); g_ExtControl->Execute(OutCtl, ".trap", DEBUG_EXECUTE_DEFAULT); break; case DEBUG_FLR_LAST_CONTROL_TRANSFER: case DEBUG_FLR_POSSIBLE_INVALID_CONTROL_TRANSFER: dprintf(" from %p to %p\n", FA_ENTRY_DATA(PULONG64, Entry)[0], FA_ENTRY_DATA(PULONG64, Entry)[1]); break; case DEBUG_FLR_CRITICAL_SECTION: dprintf("%p (!cs -s %p)\n", *FA_ENTRY_DATA(PULONG64, Entry), *FA_ENTRY_DATA(PULONG64, Entry)); break; case DEBUG_FLR_BAD_HANDLE: dprintf("%p (!htrace %p)\n", *FA_ENTRY_DATA(PULONG64, Entry), *FA_ENTRY_DATA(PULONG64, Entry)); break; case DEBUG_FLR_INVALID_HEAP_ADDRESS: dprintf("%p (!heap -p -a %p)\n", *FA_ENTRY_DATA(PULONG64, Entry), *FA_ENTRY_DATA(PULONG64, Entry)); break; case DEBUG_FLR_SHOW_ERRORLOG: g_ExtControl->Execute(OutCtl, "!errlog", DEBUG_EXECUTE_DEFAULT); break; default: dprintf(" *** Unknown TAG in analysis list %lx\n\n", Entry->Tag); return; } return; } void DebugFailureAnalysis::Output() { ULONG RetVal; FA_ENTRY* Entry; BOOL Verbose = (GetProcessingFlags() & FAILURE_ANALYSIS_VERBOSE); // // In verbose mode, show everything that we figured out during analysis // if (Verbose) { Entry = NULL; while (Entry = NextEntry(Entry)) { OutputEntry(Entry); } } if (Get(DEBUG_FLR_POISONED_TB)) { dprintf("*** WARNING: nt!MmPoisonedTb is non-zero:\n" "*** The machine has been manipulated using the kernel debugger.\n" "*** MachineOwner should be contacted first\n\n\n"); } PCHAR SolInOS = NULL; if (Entry = Get(DEBUG_FLR_FIXED_IN_OSVERSION)) { SolInOS = FA_ENTRY_DATA(PCHAR, Entry); } PCHAR Solution = NULL; if (Entry = Get(DEBUG_FLR_INTERNAL_SOLUTION_TEXT)) { Solution = FA_ENTRY_DATA(PCHAR, Entry); } // // Print the bad driver if we are not in verbose mode - otherwise // is is printed out using the params // if (!Verbose && !Solution) { if (Entry = Get(DEBUG_FLR_CORRUPTING_POOL_TAG)) { dprintf("Probably pool corruption caused by Tag: %s\n", FA_ENTRY_DATA(PCHAR, Entry)); } else { PCHAR DriverName = NULL; PCHAR SymName = NULL; if (Entry = Get(DEBUG_FLR_IMAGE_NAME)) { DriverName = FA_ENTRY_DATA(PCHAR, Entry); } if (Entry = Get(DEBUG_FLR_SYMBOL_NAME)) { SymName = FA_ENTRY_DATA(PCHAR, Entry); } if (DriverName || SymName) { dprintf("Probably caused by : "); if (SymName && DriverName) { dprintf("%s ( %s )\n", DriverName, SymName); } else if (SymName) { dprintf("%s\n", SymName); } else { dprintf("%s\n", DriverName); } } } } if (Verbose || !Solution) { PCHAR FollowupAlias = NULL; // // Print what the user should do: // - Followup person // - Solution text if there is one. // if (Entry = Get(DEBUG_FLR_FOLLOWUP_NAME)) { dprintf("\nFollowup: %s\n", FA_ENTRY_DATA(PCHAR, Entry)); } else { dprintf(" *** Followup info cannot be found !!! Please contact \"Debugger Team\"\n"); } dprintf("---------\n"); if (Entry = Get(DEBUG_FLR_POSSIBLE_INVALID_CONTROL_TRANSFER)) { CHAR Buffer[MAX_PATH]; ULONG64 Address; FaGetSymbol(FA_ENTRY_DATA(PULONG64, Entry)[0], Buffer, &Address, sizeof(Buffer)); dprintf(" *** Possible invalid call from %p ( %s+0x%1p )\n", FA_ENTRY_DATA(PULONG64, Entry)[0], Buffer, Address); FaGetSymbol(FA_ENTRY_DATA(PULONG64, Entry)[1], Buffer, &Address, sizeof(Buffer)); dprintf(" *** Expected target %p ( %s+0x%1p )\n", FA_ENTRY_DATA(PULONG64, Entry)[1], Buffer, Address); } dprintf("\n"); } if (Entry = Get(DEBUG_FLR_INTERNAL_RAID_BUG)) { dprintf("Raid bug for this failure: %d\n\n", *FA_ENTRY_DATA(PULONG64, Entry)); } if (Solution) { dprintf(" This problem has a known fix.\n" " Please connect to the following URL for details:\n" " ------------------------------------------------\n" " %s\n\n", Solution); } if (SolInOS) { dprintf(" This has been fixed in : %s\n", SolInOS); } } LPSTR TimeToStr( ULONG TimeDateStamp, BOOL DateOnly ) { LPSTR TimeDateStr; // pointer to static cruntime buffer. static char datebuffer[100]; tm * pTime; time_t TDStamp = (time_t) (LONG) TimeDateStamp; // Handle invalid \ page out timestamps, since ctime blows up on // this number if ((TimeDateStamp == 0) || (TimeDateStamp == -1)) { return "unknown_date"; } else if (DateOnly) { pTime = localtime(&TDStamp); sprintf(datebuffer, "%d_%d_%d", pTime->tm_mon + 1, pTime->tm_mday, pTime->tm_year + 1900); return datebuffer; } else { // TimeDateStamp is always a 32 bit quantity on the target, // and we need to sign extend for 64 bit host since time_t // has been extended to 64 bits. TDStamp = (time_t) (LONG) TimeDateStamp; TimeDateStr = ctime((time_t *)&TDStamp); if (TimeDateStr) { TimeDateStr[strlen(TimeDateStr) - 1] = 0; } else { TimeDateStr = "***** Invalid"; } return TimeDateStr; } } void DebugFailureAnalysis::GenerateBucketId(void) { ULONG LengthUsed = 0; CHAR BucketId[MAX_PATH] = {0}; PSTR BucketPtr = BucketId; PSTR Str; FA_ENTRY* Entry; FA_ENTRY* NameEntry; FA_ENTRY* ModuleEntry; ULONG ModuleTimestamp = 0; CHAR Command[MAX_PATH] = {0}; CHAR followup[MAX_PATH]; // // Set the final command string // if (Entry = Get(DEBUG_FLR_STACK_COMMAND)) { CopyString(Command, FA_ENTRY_DATA(PSTR, Entry), sizeof(Command) - 5); strcat(Command, " ; "); } strcat(Command, "kb"); SetString(DEBUG_FLR_STACK_COMMAND, Command); if (Get(DEBUG_FLR_OLD_OS_VERSION)) { SetString(DEBUG_FLR_DEFAULT_BUCKET_ID, "OLD_OS"); } // // Don't change the bucket ID for these two things as the debugger code // to detect them is not 100% reliable. // //if (Get(DEBUG_FLR_CPU_OVERCLOCKED)) //{ // SetString(DEBUG_FLR_BUCKET_ID, "CPU_OVERCLOCKED"); // return; //} // // // If the faulting module exists: // Get the module timestamp of the faulting module. // Check if it is an old driver. // ModuleEntry = Get(DEBUG_FLR_MODULE_NAME); if (ModuleEntry) { ULONG Index; ULONG64 Base; DEBUG_MODULE_PARAMETERS Params; g_ExtSymbols->GetModuleByModuleName(FA_ENTRY_DATA(PCHAR, ModuleEntry), 0, &Index, &Base); if (Base && g_ExtSymbols->GetModuleParameters(1, &Base, Index, &Params) == S_OK) { ModuleTimestamp = Params.TimeDateStamp; } } NameEntry = Get(DEBUG_FLR_IMAGE_NAME); if (NameEntry) { if (!strcmp(FA_ENTRY_DATA(PCHAR, NameEntry), "Unknown_Image")) { NameEntry = NULL; } } if (ModuleTimestamp && NameEntry) { PCHAR String; ULONG LookupTimestamp; String = g_pTriager->GetFollowupStr("OldImages", FA_ENTRY_DATA(PCHAR, NameEntry)); if (String) { LookupTimestamp = strtol(String, NULL, 16); // // If the driver is known to be bad, just use driver name // if (LookupTimestamp > ModuleTimestamp) { if (FaGetFollowupInfo(NULL, FA_ENTRY_DATA(PCHAR, ModuleEntry), followup, sizeof(followup))) { SetString(DEBUG_FLR_FOLLOWUP_NAME, followup); } //sprintf(BucketPtr, "OLD_IMAGE_%s_TS_%lX", // FA_ENTRY_DATA(PCHAR, NameEntry), // ModuleTimestamp); PrintString(BucketPtr, sizeof(BucketId) - LengthUsed, "OLD_IMAGE_%s", FA_ENTRY_DATA(PCHAR, NameEntry)); SetString(DEBUG_FLR_BUCKET_ID, BucketId); return; } } } if (Entry = Get(DEBUG_FLR_POSSIBLE_INVALID_CONTROL_TRANSFER)) { if (Get(DEBUG_FLR_SINGLE_BIT_ERROR)) { SetString(DEBUG_FLR_BUCKET_ID, "SINGLE_BIT_CPU_CALL_ERROR"); } else if (Get(DEBUG_FLR_TWO_BIT_ERROR)) { SetString(DEBUG_FLR_BUCKET_ID, "TWO_BIT_CPU_CALL_ERROR"); } else { SetString(DEBUG_FLR_BUCKET_ID, "CPU_CALL_ERROR"); } return; } if (Entry = Get(DEBUG_FLR_MANUAL_BREAKIN)) { SetString(DEBUG_FLR_BUCKET_ID, "MANUAL_BREAKIN"); SetString(DEBUG_FLR_FOLLOWUP_NAME, "MachineOwner"); return; } if (!PossibleFollowups[FlpSpecific].Owner[0]) { BOOL bPoolTag = FALSE; if (Entry = Get(DEBUG_FLR_BUGCHECKING_DRIVER)) { PrintString(BucketPtr, sizeof(BucketId) - LengthUsed, "%s_BUGCHECKING_DRIVER_%s", FA_ENTRY_DATA(PCHAR, Get(DEBUG_FLR_BUGCHECK_STR)), FA_ENTRY_DATA(PCHAR, Entry)); } else if (Entry = Get(DEBUG_FLR_MEMORY_CORRUPTOR)) { PrintString(BucketPtr, sizeof(BucketId) - LengthUsed, "MEMORY_CORRUPTION_%s", FA_ENTRY_DATA(PCHAR, Entry)); } else if (Entry = Get(DEBUG_FLR_POOL_CORRUPTOR)) { PrintString(BucketPtr, sizeof(BucketId) - LengthUsed, "POOL_CORRUPTION_%s", FA_ENTRY_DATA(PCHAR, Entry)); } else if (Entry = Get(DEBUG_FLR_CORRUPTING_POOL_TAG)) { PrintString(BucketPtr, sizeof(BucketId) - LengthUsed, "CORRUPTING_POOLTAG_%s", FA_ENTRY_DATA(PCHAR, Entry)); bPoolTag = TRUE; } if (Entry) { if (bPoolTag && (FaGetPoolTagFollowup(FA_ENTRY_DATA(PCHAR, Entry), followup, sizeof(followup)) == S_OK)) { SetString(DEBUG_FLR_FOLLOWUP_NAME, followup); } else if (FaGetFollowupInfo(NULL, FA_ENTRY_DATA(PCHAR, Entry), followup, sizeof(followup))) { SetString(DEBUG_FLR_FOLLOWUP_NAME, followup); } SetString(DEBUG_FLR_BUCKET_ID, BucketId); return; } } // // Only check this after as we could still have found a bad driver // with a bad stack (like drivers that cause stack corruption ...) // Str = NULL; Entry = NULL; while (Entry = NextEntry(Entry)) { switch(Entry->Tag) { case DEBUG_FLR_WRONG_SYMBOLS: Str = "WRONG_SYMBOLS"; break; case DEBUG_FLR_BAD_STACK: Str = "BAD_STACK"; break; case DEBUG_FLR_ZEROED_STACK: Str = "ZEROED_STACK"; break; case DEBUG_FLR_INVALID_KERNEL_CONTEXT: Str = "INVALID_KERNEL_CONTEXT"; break; case DEBUG_FLR_CORRUPT_MODULE_LIST: Str = "CORRUPT_MODULELIST"; break; break; } if (Str) { if (FaGetFollowupInfo(NULL, "ProcessingError", followup, sizeof(followup))) { SetString(DEBUG_FLR_FOLLOWUP_NAME, followup); } SetString(DEBUG_FLR_BUCKET_ID, Str); return; } } // // Add failure code. // if (Entry = Get(DEBUG_FLR_BUGCHECK_STR)) { PrintString(BucketPtr, sizeof(BucketId) - LengthUsed, "%s", FA_ENTRY_DATA(PCHAR, Entry)); LengthUsed += strlen(BucketPtr); BucketPtr = BucketId + LengthUsed; } if (Entry = Get(DEBUG_FLR_BUGCHECK_SPECIFIER)) { PrintString(BucketPtr, sizeof(BucketId) - LengthUsed, "%s", FA_ENTRY_DATA(PCHAR, Entry)); LengthUsed += strlen(BucketPtr); BucketPtr = BucketId + LengthUsed; } // // If it's driver only, but the failure is not in a driver, then show the // full name of the failure. If we could not get the name, or we really // have a driver only, show the name of the image. // if ( (Entry = Get(DEBUG_FLR_SYMBOL_NAME)) && ( !Get(DEBUG_FLR_FOLLOWUP_DRIVER_ONLY) || (BestClassFollowUp < FlpUnknownDrv))) { // // If the faulting IP and the read address are the same, this is // an interesting scenario we want to catch. // if (Get(DEBUG_FLR_FAILED_INSTRUCTION_ADDRESS)) { PrintString(BucketPtr, sizeof(BucketId) - LengthUsed, "_BAD_IP"); LengthUsed += strlen(BucketPtr); BucketPtr = BucketId + LengthUsed; } PrintString(BucketPtr, sizeof(BucketId) - LengthUsed, "_%s", FA_ENTRY_DATA(PCHAR, Entry)); LengthUsed += strlen(BucketPtr); BucketPtr = BucketId + LengthUsed; } else if (NameEntry) { PrintString(BucketPtr, sizeof(BucketId) - LengthUsed, "_IMAGE_%s", FA_ENTRY_DATA(PCHAR, NameEntry)); LengthUsed += strlen(BucketPtr); BucketPtr = BucketId + LengthUsed; // Also add timestamp in this case. if (ModuleTimestamp) { PrintString(BucketPtr, sizeof(BucketId) - LengthUsed, "_DATE_%s", TimeToStr(ModuleTimestamp, TRUE)); LengthUsed += strlen(BucketPtr); BucketPtr = BucketId + LengthUsed; } } // // Store the bucket ID in the analysis structure // //BucketDone: for (PCHAR Scan = &BucketId[0]; *Scan; ++Scan) { // remove special chars that cause problems for IIS or SQL if (*Scan == '<' || *Scan == '>' || *Scan == '|' || *Scan == '`' || *Scan == '\''|| (!isprint(*Scan)) ) { *Scan = '_'; } } SetString(DEBUG_FLR_BUCKET_ID, BucketId); } void DebugFailureAnalysis::AnalyzeStack(void) { PDEBUG_OUTPUT_CALLBACKS PrevCB; ULONG i; BOOL BoostToSpecific = FALSE; ULONG64 TrapFrame = 0; ULONG64 Thread = 0; ULONG64 ImageNameAddr = 0; DEBUG_STACK_FRAME Stk[MAX_STACK_FRAMES]; ULONG Frames = 0; ULONG Trap0EFrameLimit = 2; ULONG64 OriginalFaultingAddress = 0; CHAR Command[50] = {0}; FA_ENTRY* Entry; BOOL BadContext = FALSE; ULONG PtrSize = IsPtr64() ? 8 : 4; BOOL IsVrfBugcheck = FALSE; // // If someone provided a best followup already, just return // if (PossibleFollowups[MaxFlpClass-1].Owner[0]) { return; } if (g_TargetClass == DEBUG_CLASS_KERNEL) { // Check if CPU is overclocked //if (BcIsCpuOverClocked()) //{ // SetUlong64(DEBUG_FLR_CPU_OVERCLOCKED, -1); // // BestClassFollowUp = FlpOSInternalRoutine; // strcpy(PossibleFollowups[FlpOSInternalRoutine].Owner, "MachineOwner"); // return; //} // // Check if this bugcheck has any specific followup that independant // of the failure stack // if (Entry = Get(DEBUG_FLR_BUGCHECK_STR)) { PCHAR String; String = g_pTriager->GetFollowupStr("bugcheck", FA_ENTRY_DATA(PCHAR, Entry)); if (String) { if (!strncmp(String, "maybe_", 6)) { BestClassFollowUp = FlpOSRoutine; CopyString(PossibleFollowups[FlpOSRoutine].Owner, String + 6, sizeof(PossibleFollowups[FlpOSRoutine].Owner)); } else if (!strncmp(String, "specific_", 9)) { BoostToSpecific = TRUE; } else { BestClassFollowUp = FlpSpecific; CopyString(PossibleFollowups[FlpSpecific].Owner, String, sizeof(PossibleFollowups[FlpSpecific].Owner)); return; } } } } // // Add trap frame, context info from the current stack // // Note(kksharma):We only need one of these to get to // faulting stack (and only one of them should // be available otherwise somethings wrong) // Entry = NULL; while (Entry = NextEntry(Entry)) { switch(Entry->Tag) { case DEBUG_FLR_CONTEXT: sprintf(Command, ".cxr %I64lx", *FA_ENTRY_DATA(PULONG64, Entry)); break; case DEBUG_FLR_TRAP_FRAME: sprintf(Command, ".trap %I64lx", *FA_ENTRY_DATA(PULONG64, Entry)); break; case DEBUG_FLR_TSS: sprintf(Command, ".tss %I64lx", *FA_ENTRY_DATA(PULONG64, Entry)); break; case DEBUG_FLR_FAULTING_THREAD: sprintf(Command, ".thread %I64lx", *FA_ENTRY_DATA(PULONG64, Entry)); break; case DEBUG_FLR_EXCEPTION_RECORD: if (*FA_ENTRY_DATA(PULONG64, Entry) == -1) { sprintf(Command, ".ecxr"); } break; case DEBUG_FLR_FAULTING_IP: case DEBUG_FLR_FAULTING_MODULE: // // We already have some info from the bugcheck // Use that address to start off with. // But if we could not use it to set any followup, then continue // lookinh for the others. // if (OriginalFaultingAddress = *FA_ENTRY_DATA(PULONG64, Entry)) { if (!GetTriageInfoFromStack(0, 1, OriginalFaultingAddress, PossibleFollowups, &BestClassFollowUp)) { OriginalFaultingAddress = 0; } } break; default: break; } if (Command[0] && OriginalFaultingAddress) { break; } } RepeatGetCommand: if (!Command[0]) { // // Get the current stack. // if (S_OK != g_ExtControl->GetStackTrace(0, 0, 0, Stk, MAX_STACK_FRAMES, &Frames)) { Frames = 0; } // // Make sure this is a valid stack to analyze. Such as for kernel mode // try to recognize stack after user breakin and send to machineowner // if (m_FailureType == DEBUG_FLR_KERNEL && m_FailureCode == 0 && Frames >= 3) { if (IsManualBreakin(Stk, Frames)) { // set machine owner as followup SetUlong(DEBUG_FLR_MANUAL_BREAKIN, TRUE); strcpy(PossibleFollowups[MaxFlpClass-1].Owner, "MachineOwner"); PossibleFollowups[MaxFlpClass-1].InstructionOffset = Stk[0].InstructionOffset; return; } } // // Get the current stack and check if we can get trap // frame/context from it // ULONG64 ExceptionPointers = 0; for (i = 0; i < Frames; ++i) { #if 0 // Stack walker taskes care of these when walking stack if (GetTrapFromStackFrameFPO(&stk[i], &TrapFrame)) { break; } #endif // // First argument of this function is the assert // Second argument of this function is the file name // Third argument of this function is the line number // if (FaIsFunctionAddr(Stk[i].InstructionOffset, "RtlAssert")) { ULONG Len; CHAR AssertData[MAX_PATH + 1] = {0}; if (Len = ReadAcsiiString(Stk[i].Params[0], AssertData, sizeof(AssertData))) { SetString(DEBUG_FLR_ASSERT_DATA, AssertData); } if (Len = ReadAcsiiString(Stk[i].Params[1], AssertData, sizeof(AssertData))) { strncat(AssertData, " at Line ", sizeof(AssertData) - Len); Len = strlen(AssertData); PrintString(AssertData + Len, sizeof(AssertData) - Len - 1, "%I64lx", Stk[i].Params[2]); SetString(DEBUG_FLR_ASSERT_FILE, AssertData); } } // If Trap 0E is the second or 3rd frame on the stack, we can just // switch to that trap frame. // Otherwise, we want to leave it as is because the failure is // most likely due to the frames between bugcheck and trap0E // // ebp of KiTrap0E is the trap frame // if ((i <= Trap0EFrameLimit) && FaIsFunctionAddr(Stk[i].InstructionOffset, "KiTrap0E")) { TrapFrame = Stk[i].Reserved[2]; break; } // // take first param - spin lock - and it contains the thread that // owns the spin lock. // Make sure to zero the bottom bit as it is always set ... // if ((i == 0) && FaIsFunctionAddr(Stk[i].InstructionOffset, "SpinLockSpinningForTooLong")) { if (ReadPointer(Stk[0].Params[0], &Thread) && Thread) { Thread &= ~0x1; } break; } // // First arg of KiMemoryFault is the trap frame // if (FaIsFunctionAddr(Stk[i].InstructionOffset, "KiMemoryFault") || FaIsFunctionAddr(Stk[i].InstructionOffset, "Ki386CheckDivideByZeroTrap")) { TrapFrame = Stk[i].Params[0]; break; } // // Third arg of KiMemoryFault is the trap frame // if (FaIsFunctionAddr(Stk[i].InstructionOffset, "KiDispatchException")) { TrapFrame = Stk[i].Params[2]; break; } // // First argument of this function is EXCEPTION_POINTERS // if (FaIsFunctionAddr(Stk[i].InstructionOffset, "PspUnhandledExceptionInSystemThread")) { ExceptionPointers = Stk[i].Params[0]; break; } // // First argument of this function is a BUGCHECK_DATA structure // The thread is the second parameter in that data structure // if (FaIsFunctionAddr(Stk[i].InstructionOffset, "WdBugCheckStuckDriver") || FaIsFunctionAddr(Stk[i].InstructionOffset, "WdpBugCheckStuckDriver")) { ReadPointer(Stk[i].Params[0] + PtrSize, &Thread); break; } // // First argument of these functions are EXCEPTION_POINTERS // if (FaIsFunctionAddr(Stk[i].InstructionOffset, "PopExceptionFilter") || FaIsFunctionAddr(Stk[i].InstructionOffset, "RtlUnhandledExceptionFilter2")) { ExceptionPointers = Stk[i].Params[0]; break; } // // THIRD argument has the name of Exe // nt!PspCatchCriticalBreak(char* Msg, void* Object,unsigned char* ImageFileName) // if (FaIsFunctionAddr(Stk[i].InstructionOffset, "PspCatchCriticalBreak")) { ImageNameAddr = Stk[i].Params[2]; break; } // // VERIFIER : Look for possible verifier failures // verifier!VerifierStopMessage means verifier caused the break // if (FaIsFunctionAddr(Stk[i].InstructionOffset, "VerifierStopMessage")) { IsVrfBugcheck = TRUE; break; } } if (ExceptionPointers) { ULONG64 Exr = 0, Cxr = 0; if (!ReadPointer(ExceptionPointers, &Exr) || !ReadPointer(ExceptionPointers + PtrSize, &Cxr)) { // dprintf("Unable to read exception pointers at %p\n", // ExcepPtr); } if (Exr) { SetUlong64(DEBUG_FLR_EXCEPTION_RECORD, Exr); } if (Cxr) { sprintf(Command, ".cxr %I64lx", Cxr); SetUlong64(DEBUG_FLR_CONTEXT, Cxr); } } if (TrapFrame) { sprintf(Command, ".trap %I64lx", TrapFrame); SetUlong64(DEBUG_FLR_TRAP_FRAME, TrapFrame); } if (Thread) { sprintf(Command, ".thread %I64lx", Thread); SetUlong64(DEBUG_FLR_FAULTING_THREAD, Thread); } if (ImageNameAddr) { CHAR Buffer[50]={0}, *pImgExt; ULONG cb; if (ReadMemory(ImageNameAddr, Buffer, sizeof(Buffer) - 1, &cb) && Buffer[0]) { if (pImgExt = strchr(Buffer, '.')) { // we do not want imageextension here *pImgExt = 0; } SetString(DEBUG_FLR_MODULE_NAME, Buffer); } } if (IsVrfBugcheck) { ULONG64 AvrfCxr = 0; // We hit this when app verifier breaks into kd and usermode // analysis isn't called if (DoVerifierAnalysis(NULL, this) == S_OK) { if (GetUlong64(DEBUG_FLR_CONTEXT, &AvrfCxr) != NULL) { sprintf(Command, ".cxr %I64lx", AvrfCxr); } } } } // // execute the command and get an updated stack // if (Command[0]) { g_ExtControl->Execute(DEBUG_OUTCTL_IGNORE, Command, DEBUG_EXECUTE_NOT_LOGGED); if (g_ExtControl->GetStackTrace(0, 0, 0, Stk, MAX_STACK_FRAMES, &Frames) != S_OK) { Frames = 0; } } // // Get relevant stack // // We can get stack with 1 frame because a .trap can bring us to the // faulting instruction, and if it's a 3rd party driver with no symbols // and image, the stack can be 1 frame - although a very valid one. // if (Frames) { ULONG64 Values[3]; Values[0] = Stk[0].ReturnOffset; Values[1] = Stk[0].InstructionOffset; Values[2] = Stk[0].StackOffset; SetUlong64s(DEBUG_FLR_LAST_CONTROL_TRANSFER, 3, Values); // If everything on the stack is user mode in the case of a kernel // mode failure, we got some bad context information. if (IsFollowupContext(Values[0],Values[1],Values[2]) != FollowYes) { SetUlong64(DEBUG_FLR_INVALID_KERNEL_CONTEXT, 0); BadContext = TRUE; } else { GetTriageInfoFromStack(&Stk[0], Frames, 0, PossibleFollowups, &BestClassFollowUp); } } ULONG64 StackBase = FaGetImplicitStackOffset(); // // If the stack pointer is not aligned, take a note of that. // if (StackBase & 0x3) { Set(DEBUG_FLR_UNALIGNED_STACK_POINTER, 0); } // // If we have an image name (possibly directly from the bugcheck // information) try to get the followup from that. // if ((BestClassFollowUp < FlpUnknownDrv) && (Entry = Get(DEBUG_FLR_MODULE_NAME))) { FaGetFollowupInfo(NULL, FA_ENTRY_DATA(PCHAR, Entry), PossibleFollowups[FlpUnknownDrv].Owner, sizeof(PossibleFollowups[FlpUnknownDrv].Owner)); if (PossibleFollowups[FlpUnknownDrv].Owner[0]) { BestClassFollowUp = FlpUnknownDrv; } } // // If we could not find anything at this point, look further up the stack // for a trap frame to catch failures of this kind: // nt!RtlpBreakWithStatusInstruction // nt!KiBugCheckDebugBreak+0x19 // nt!KeBugCheck2+0x499 // nt!KeBugCheckEx+0x19 // nt!_KiTrap0E+0x224 // if (!Command[0] && (BestClassFollowUp < FlpOSFilterDrv) && (Trap0EFrameLimit != 0xff)) { Trap0EFrameLimit = 0xff; goto RepeatGetCommand; } // // Last resort, manually read the stack and look for some symbol // to followup on. // if ((BadContext == FALSE) && ((BestClassFollowUp == FlpIgnore) || ((BestClassFollowUp < FlpOSRoutine) && (Frames <= 2)))) { FindFollowupOnRawStack(StackBase, PossibleFollowups, &BestClassFollowUp); } // // Get something ! // if (BestClassFollowUp < FlpOSRoutine) { if (!BestClassFollowUp) { PossibleFollowups[FlpOSInternalRoutine].InstructionOffset = GetExpression("@$ip"); } if (!PossibleFollowups[FlpOSInternalRoutine].Owner[0] || !_stricmp(PossibleFollowups[FlpOSInternalRoutine].Owner, "ignore")) { FaGetFollowupInfo(NULL, "default", PossibleFollowups[FlpOSInternalRoutine].Owner, sizeof(PossibleFollowups[FlpOSInternalRoutine].Owner)); BestClassFollowUp = FlpOSRoutine; } } // // Special handling so a bugcheck EA can always take predence over a pool // corruption. // if (BoostToSpecific) { for (i = MaxFlpClass-1; i ; i--) { if (PossibleFollowups[i].Owner[0]) { PossibleFollowups[FlpSpecific] = PossibleFollowups[i]; break; } } } // // Get the faulting stack // g_OutCapCb.Reset(); g_OutCapCb.Output(0, "\n"); g_ExtClient->GetOutputCallbacks(&PrevCB); g_ExtClient->SetOutputCallbacks(&g_OutCapCb); g_ExtControl->OutputStackTrace(DEBUG_OUTCTL_THIS_CLIENT | DEBUG_OUTCTL_NOT_LOGGED, Stk, Frames, DEBUG_STACK_ARGUMENTS | DEBUG_STACK_FRAME_ADDRESSES | DEBUG_STACK_SOURCE_LINE); g_ExtClient->SetOutputCallbacks(PrevCB); if (*g_OutCapCb.GetCapturedText()) { SetString(DEBUG_FLR_STACK_TEXT, g_OutCapCb.GetCapturedText()); } // // Reset the current state to normal so !analyze does not have any // side-effects // if (Command[0]) { SetString(DEBUG_FLR_STACK_COMMAND, Command); // // Clear the set context // g_ExtControl->Execute(DEBUG_OUTCTL_IGNORE, ".cxr 0; .thread", DEBUG_EXECUTE_NOT_LOGGED); } } VOID DebugFailureAnalysis::FindFollowupOnRawStack( ULONG64 StackBase, PFOLLOWUP_DESCS PossibleFollowups, FlpClasses *BestClassFollowUp ) { #define NUM_ADDRS 200 ULONG i; ULONG PtrSize = IsPtr64() ? 8 : 4; BOOL AddressFound = FALSE; BOOL ZeroedStack = TRUE; ULONG64 AddrToLookup; FlpClasses RawStkBestFollowup; if (*BestClassFollowUp >= FlpUnknownDrv) { // Any better fron raw stack won't be as accurate as what we have return; } else if (*BestClassFollowUp == FlpIgnore) { // We don't want to followup on os internal routine here RawStkBestFollowup = FlpOSInternalRoutine; } else { RawStkBestFollowup = *BestClassFollowUp; } // Align stack to natural pointer size. StackBase &= ~((ULONG64)PtrSize - 1); for (i = 0; i < NUM_ADDRS; i++) { if (!ReadPointer(StackBase, &AddrToLookup)) { break; } StackBase+= PtrSize; if (AddrToLookup) { FOLLOW_ADDRESS Follow; ZeroedStack = FALSE; Follow = IsPotentialFollowupAddress(AddrToLookup); if (Follow == FollowStop) { break; } else if (Follow == FollowSkip) { continue; } AddressFound = TRUE; GetTriageInfoFromStack(0, 1, AddrToLookup, PossibleFollowups, &RawStkBestFollowup); if (RawStkBestFollowup == FlpUnknownDrv) { break; } } } if (!AddressFound) { if (ZeroedStack) { SetUlong64(DEBUG_FLR_ZEROED_STACK, 0); } else { SetUlong64(DEBUG_FLR_BAD_STACK, 0); } } if (RawStkBestFollowup > FlpOSInternalRoutine) { *BestClassFollowUp = RawStkBestFollowup; } } BOOL DebugFailureAnalysis::GetTriageInfoFromStack( PDEBUG_STACK_FRAME Stack, ULONG Frames, ULONG64 SingleInstruction, PFOLLOWUP_DESCS PossibleFollowups, FlpClasses *BestClassFollowUp) { ULONG i; EXT_TRIAGE_FOLLOWUP FollowUp = &_EFN_GetTriageFollowupFromSymbol; BOOL bStat = FALSE; BOOL IgnorePoolCorruptionFlp = FALSE; FOLLOW_ADDRESS Follow; if (Get(DEBUG_FLR_ANALYZAABLE_POOL_CORRUPTION)) { IgnorePoolCorruptionFlp = TRUE; } for (i = 0; i < Frames; ++i) { ULONG64 Disp; ULONG64 Instruction; CHAR Module[20] = {0}; CHAR Buffer[MAX_PATH]; CHAR Owner[100]; DWORD dwOwner; DEBUG_TRIAGE_FOLLOWUP_INFO Info; FlpClasses ClassFollowUp = FlpIgnore; FlpClasses StoreClassFollowUp = FlpIgnore; Instruction = SingleInstruction; if (!SingleInstruction) { Instruction = Stack[i].InstructionOffset; } // // Determine how to process this address. // Follow = IsPotentialFollowupAddress(Instruction); if (Follow == FollowStop) { break; } else if (Follow == FollowSkip) { continue; } Buffer[0] = 0; FaGetSymbol(Instruction, Buffer, &Disp, sizeof(Buffer)); if (Buffer[0] == 0) { // // Either its a bad stack or someone jumped called to bad IP // continue; } // // Check if this routine has any special significance for getting // faulting module // PCHAR Routine = strchr(Buffer, '!'); if (Routine) { *Routine = 0; } CopyString(Module, Buffer, sizeof(Module)); if (Routine) { *Routine = '!'; Routine++; if (Stack && !strcmp(Routine, "IopCompleteRequest")) { // First argument is Irp Tail, get the driver from Irp ULONG TailOffset = 0; ULONG64 Irp; GetFieldOffset("nt!_IRP", "Tail", &TailOffset); if (TailOffset) { FA_ENTRY* Entry = NULL; PCHAR ModuleName = NULL; Irp = Stack[i].Params[0] - TailOffset; SetUlong64(DEBUG_FLR_IRP_ADDRESS, Irp); if (BcGetDriverNameFromIrp(this, Irp, NULL, NULL)) { if (Entry = Get(DEBUG_FLR_IMAGE_NAME)) { CopyString(Buffer, FA_ENTRY_DATA(PCHAR, Entry), sizeof(Buffer)); PCHAR Dot; if (Dot = strchr(Buffer, '.')) { *Dot = 0; CopyString(Module, Buffer, sizeof(Module)); } } } } } else if ((i == 0) && Stack && !strcmp(Routine, "ObpCloseHandleTableEntry")) { // // Check for possible memory corruption // 2nd parameter is HANDLE_TABLE_ENTRY // if (CheckForCorruptionInHTE(Stack[i].Params[1], Owner, sizeof(Owner))) { // We have pool corrupting PoolTag in analysis // continue with default analysis for now } } } ClassFollowUp = GetFollowupClass(Instruction, Module, Routine); if (ClassFollowUp == FlpIgnore) { continue; } Info.SizeOfStruct = sizeof(Info); Info.OwnerName = &Owner[0]; Info.OwnerNameSize = sizeof(Owner); if (dwOwner = (*FollowUp)(g_ExtClient, Buffer, &Info)) { PCHAR pOwner = Owner; if (dwOwner == TRIAGE_FOLLOWUP_IGNORE) { ClassFollowUp = FlpIgnore; } else if (!strncmp(Owner, "maybe_", 6)) { pOwner = Owner + 6; ClassFollowUp = (FlpClasses) ((ULONG) ClassFollowUp - 1); } else if (!strncmp(Owner, "last_", 5)) { pOwner = Owner + 5; ClassFollowUp = FlpOSInternalRoutine; } else if (!strncmp(Owner, "specific_", 9)) { pOwner = Owner + 9; ClassFollowUp = FlpSpecific; } else if (!_stricmp(Owner, "pool_corruption")) { if (IgnorePoolCorruptionFlp) { continue; } // // If we have non-kernel followups already on the stack // it could be them no correctly handling this stack. // If we only have kernel calls, then it must be pool // corruption. // // We later rely on a pool corruption always being marked // as a FlpUnknownDrv // StoreClassFollowUp = FlpUnknownDrv; ClassFollowUp = FlpOSFilterDrv; } if (StoreClassFollowUp == FlpIgnore) { StoreClassFollowUp = ClassFollowUp; } // // Save this entry if it's better than anything else we have. // if (ClassFollowUp > *BestClassFollowUp) { bStat = TRUE; *BestClassFollowUp = StoreClassFollowUp; CopyString(PossibleFollowups[StoreClassFollowUp].Owner, pOwner, sizeof(PossibleFollowups[StoreClassFollowUp].Owner)); PossibleFollowups[StoreClassFollowUp].InstructionOffset = Instruction; if (StoreClassFollowUp == FlpUnknownDrv) { // Best match possible return bStat; } } } } return bStat; } BOOL DebugFailureAnalysis::AddCorruptModules(void) { // // Check if we have an old build. Anything smaller than OSBuild // and not identified specifically by build number in the list is old. // PCHAR String; CHAR BuildString[7]; ULONG BuildNum = 0; BOOL FoundCorruptor = FALSE; BOOL PoolCorruption = FALSE; ULONG Loaded; ULONG Unloaded; CHAR Name[MAX_PATH]; CHAR ImageName[MAX_PATH]; CHAR CorruptModule[MAX_PATH]; FA_ENTRY *Entry; FA_ENTRY *BugCheckEntry; sprintf(BuildString, "%d", g_TargetBuild); if (!g_pTriager->GetFollowupStr("OSBuild", BuildString)) { if (String = g_pTriager->GetFollowupStr("OSBuild", "old")) { BuildNum = strtol(String, NULL, 10); if (BuildNum > g_TargetBuild) { SetUlong64(DEBUG_FLR_OLD_OS_VERSION, g_TargetBuild); } } } // // If we have a specific solution, return // if we can't get a module list, return // if (PossibleFollowups[FlpSpecific].Owner[0] || (g_ExtSymbols->GetNumberModules(&Loaded, &Unloaded) != S_OK)) { return FALSE; } // // Determine if the failure was likely caused by a pool corruption // if ((BestClassFollowUp < FlpUnknownDrv) || !_stricmp(PossibleFollowups[FlpUnknownDrv].Owner, "pool_corruption")) { PoolCorruption = TRUE; } BugCheckEntry = Get(DEBUG_FLR_BUGCHECK_STR); // // Loop three types to find the types of corruptors in order. // the order must match the order in which we generate the bucket name // for these types so the image name ends up correct. // for (ULONG TypeLoop = 0; TypeLoop < 3; TypeLoop++) { if ((TypeLoop == 0) && !BugCheckEntry) { continue; } if ((TypeLoop == 2) && !PoolCorruption) { continue; } for (ULONG Index = 0; Index < Loaded + Unloaded; Index++) { ULONG64 Base; DEBUG_FLR_PARAM_TYPE Type = (DEBUG_FLR_PARAM_TYPE)0; PCHAR Scan; PCHAR DriverName; DEBUG_MODULE_PARAMETERS Params; ULONG Start, End = 0; if (g_ExtSymbols->GetModuleByIndex(Index, &Base) != S_OK) { continue; } if (g_ExtSymbols->GetModuleNames(Index, Base, ImageName, MAX_PATH, NULL, Name, MAX_PATH, NULL, NULL, 0, NULL) != S_OK) { continue; } if (g_ExtSymbols->GetModuleParameters(1, &Base, Index, &Params) != S_OK) { continue; } // // Strip the path // DriverName = ImageName; if (Scan = strrchr(DriverName, '\\')) { DriverName = Scan+1; } // // Look for the module in both the various bad drivers list. // poolcorruptor and memorycorruptor lists in triage.ini // switch (TypeLoop) { case 0: Type = DEBUG_FLR_BUGCHECKING_DRIVER; PrintString(CorruptModule, sizeof(CorruptModule), "%s_%s", FA_ENTRY_DATA(PCHAR, BugCheckEntry), DriverName); g_pTriager->GetFollowupDate("bugcheckingDriver", CorruptModule, &Start, &End); break; case 1: Type = DEBUG_FLR_MEMORY_CORRUPTOR; g_pTriager->GetFollowupDate("memorycorruptors", DriverName, &Start, &End); break; case 2: // // Only look at kernel mode pool corruptors if the failure // is a kernel mode crash (and same for user mode), because // a kernel pool corruptor will almost never affect an app // (apps don't see data in pool blocks) // if ((BOOL)(GetFailureType() != DEBUG_FLR_KERNEL) == (BOOL)((Params.Flags & DEBUG_MODULE_USER_MODE) != 0)) { Type = DEBUG_FLR_POOL_CORRUPTOR; g_pTriager->GetFollowupDate("poolcorruptors", DriverName, &Start, &End); } break; } // // Add it to the list if it's really known to be a bad driver. // if (End) { // // Check to see if the timestamp is older than a fixed // driver. If the module is unloaded and no fix is know, // then also mark it as bad // if ( (Params.TimeDateStamp && (Params.TimeDateStamp < End) && (Params.TimeDateStamp >= Start)) || ((Params.Flags & DEBUG_MODULE_UNLOADED) && (End == 0xffffffff)) ) { // Don't store the timestamp on memory corrupting // modules to simplify bucketing annd allow for // name lookup. // //sprintf(CorruptModule, "%s_%08lx", // DriverName, Params.TimeDateStamp); // // Store the first driver we find as the cause, // bug accumulate the list of known memory corruptors. // if (!FoundCorruptor) { SetString(DEBUG_FLR_MODULE_NAME, Name); SetString(DEBUG_FLR_IMAGE_NAME, DriverName); SetUlong64(DEBUG_FLR_IMAGE_TIMESTAMP, Params.TimeDateStamp); FoundCorruptor = TRUE; } // // Remove the dot since we check the followup // based on that string. // if (Scan = strrchr(DriverName, '.')) { *Scan = 0; } if (strlen(DriverName) < sizeof(CorruptModule)) { CopyString(CorruptModule, DriverName, sizeof(CorruptModule)); } Entry = Add(Type, strlen(CorruptModule) + 1); if (Entry) { CopyString(FA_ENTRY_DATA(PCHAR, Entry), CorruptModule, Entry->FullSize); } } } } } return FoundCorruptor; } void DebugFailureAnalysis::SetSymbolNameAndModule() { ULONG64 Address = 0; CHAR Buffer[MAX_PATH]; ULONG64 Disp, Base = 0; ULONG Index; ULONG i; // // Store the best followup name in the analysis results // for (i = MaxFlpClass-1; i ; i--) { if (PossibleFollowups[i].Owner[0]) { if (PossibleFollowups[i].InstructionOffset) { Address = PossibleFollowups[i].InstructionOffset; SetUlong64(DEBUG_FLR_FOLLOWUP_IP, PossibleFollowups[i].InstructionOffset); } SetString(DEBUG_FLR_FOLLOWUP_NAME, PossibleFollowups[i].Owner); break; } } // // Now that we have the followip, get the module names. // // The address may not be set in the case where we just // had a driver name with no real address for it // if (Address) { // // Try to get the full symbol name. // leave space for Displacement // Buffer[0] = 0; if (FaGetSymbol(Address, Buffer, &Disp, sizeof(Buffer) - 20)) { sprintf(Buffer + strlen(Buffer), "+%I64lx", Disp); SetString(DEBUG_FLR_SYMBOL_NAME, Buffer); } // // Now get the Mod name // g_ExtSymbols->GetModuleByOffset(Address, 0, &Index, &Base); if (Base) { CHAR ModBuf[100]; CHAR ImageBuf[100]; PCHAR Scan; PCHAR ImageName; if (g_ExtSymbols->GetModuleNames(Index, Base, ImageBuf, sizeof(ImageBuf), NULL, ModBuf, sizeof(ModBuf), NULL, NULL, 0, NULL) == S_OK) { // // Check for unknown module. // If it's not, then we should have something valid. // if (!strstr(ModBuf, "Unknown")) { // // Strip the path - keep the extension // ImageName = ImageBuf; if (Scan = strrchr(ImageName, '\\')) { ImageName = Scan+1; } SetString(DEBUG_FLR_MODULE_NAME, ModBuf); SetString(DEBUG_FLR_IMAGE_NAME, ImageName); DEBUG_MODULE_PARAMETERS Params; ULONG TimeStamp = 0; if (g_ExtSymbols->GetModuleParameters(1, &Base, Index, &Params) == S_OK) { TimeStamp = Params.TimeDateStamp; } SetUlong64(DEBUG_FLR_IMAGE_TIMESTAMP, TimeStamp); return; } } } } // // If we make it here there was an error getting module name, // so set things to "unknown". // if (!Get(DEBUG_FLR_MODULE_NAME)) { SetUlong64(DEBUG_FLR_UNKNOWN_MODULE, 1); SetString(DEBUG_FLR_MODULE_NAME, "Unknown_Module"); SetString(DEBUG_FLR_IMAGE_NAME, "Unknown_Image"); SetUlong64(DEBUG_FLR_IMAGE_TIMESTAMP, 0); } } HRESULT DebugFailureAnalysis::CheckModuleSymbols(PSTR ModName, PSTR ShowName) { ULONG ModIndex; ULONG64 ModBase; DEBUG_MODULE_PARAMETERS ModParams; if (S_OK != g_ExtSymbols->GetModuleByModuleName(ModName, 0, &ModIndex, &ModBase)) { ExtErr("***** Debugger could not find %s in module list, " "dump might be corrupt.\n" "***** Followup with Debugger team\n\n", ModName); SetString(DEBUG_FLR_CORRUPT_MODULE_LIST, ModName); return E_FAILURE_CORRUPT_MODULE_LIST; } else if ((S_OK != g_ExtSymbols->GetModuleParameters(1, &ModBase, 0, &ModParams)) || (ModParams.SymbolType == DEBUG_SYMTYPE_NONE) || (ModParams.SymbolType == DEBUG_SYMTYPE_EXPORT)) // (ModParams.Flags & DEBUG_MODULE_SYM_BAD_CHECKSUM)) { ExtErr("***** %s symbols are WRONG. Please fix symbols to " "do analysis.\n\n", ShowName); SetUlong64(DEBUG_FLR_WRONG_SYMBOLS, ModBase); return E_FAILURE_WRONG_SYMBOLS; } return S_OK; } void DebugFailureAnalysis::ProcessInformation(void) { // // Analysis of abstracted information. // // Now that raw information has been gathered, // perform abstract analysis of the gathered // information to produce even higher-level // information. The process iterates until no // new information is produced. // AnalyzeStack(); while (ProcessInformationPass()) { // Iterate. } // // Only add corrupt modules if we did not find a specific solution. // // If we do find a memory corruptor, the followup and name will be set // from the module name as part of bucketing. if (!AddCorruptModules()) { SetSymbolNameAndModule(); } GenerateBucketId(); DbFindBucketInfo(); } ULONG64 GetControlTransferTargetX86(ULONG64 StackOffset, PULONG64 ReturnOffset) { ULONG Done; UCHAR InstrBuf[8]; ULONG StackReturn; ULONG64 Target; ULONG JumpCount; // // Check that we just performed a call, which implies // the first value on the stack is equal to the return address // computed during stack walk. // if (!ReadMemory(StackOffset, &StackReturn, 4, &Done) || (Done != 4) || StackReturn != (ULONG)*ReturnOffset) { return 0; } // // Check for call rel32 instruction. // if (!ReadMemory(*ReturnOffset - 5, InstrBuf, 5, &Done) || (Done != 5) || (InstrBuf[0] != 0xe8)) { return 0; } Target = (LONG64)(LONG) ((ULONG)*ReturnOffset + *(ULONG UNALIGNED *)&InstrBuf[1]); // Adjust the return offset to point to the start of the instruction. (*ReturnOffset) -= 5; // // We may have called an import thunk or something else which // immediately jumps somewhere else, so follow jumps. // JumpCount = 8; for (;;) { if (!ReadMemory(Target, InstrBuf, 6, &Done) || Done < 5) { // We expect to be able to read the target memory // as that's where we think IP is. If this fails // we need to flag it as a problem. return Target; } if (InstrBuf[0] == 0xe9) { Target = (LONG64)(LONG) ((ULONG)Target + 5 + *(ULONG UNALIGNED *)&InstrBuf[1]); } else if (InstrBuf[0] == 0xff && InstrBuf[1] == 0x25) { ULONG64 Ind; if (Done < 6) { // We see a jump but we don't have all the // memory. To avoid spurious errors we just // give up. return 0; } Ind = (LONG64)(LONG)*(ULONG UNALIGNED *)&InstrBuf[2]; if (!ReadMemory(Ind, &Target, 4, &Done) || Done != 4) { return 0; } Target = (LONG64)(LONG)Target; } else { break; } if (JumpCount-- == 0) { // We've been tracing jumps too long, just give up. return 0; } } return Target; } BOOL DebugFailureAnalysis::ProcessInformationPass(void) { ULONG Done; ULONG64 ExceptionCode; ULONG64 Arg1, Arg2; ULONG64 Values[2]; ULONG PtrSize = IsPtr64() ? 8 : 4; FA_ENTRY* Entry; // // Determine if the current fault is due to inability // to execute an instruction. The checks are: // 1. A read access violation at the current IP indicates // the current instruction memory is invalid. // 2. An illegal instruction fault indicates the current // instruction is invalid. // if (!Get(DEBUG_FLR_FAILED_INSTRUCTION_ADDRESS)) { if (GetUlong64(DEBUG_FLR_EXCEPTION_CODE, &ExceptionCode) && (ExceptionCode == STATUS_ILLEGAL_INSTRUCTION) && GetUlong64(DEBUG_FLR_FAULTING_IP, &Arg1)) { // Invalid instruction. SetUlong64(DEBUG_FLR_FAILED_INSTRUCTION_ADDRESS, Arg1); return TRUE; } if ( // ExceptionCode == STATUS_ACCESS_VIOLATION && GetUlong64(DEBUG_FLR_READ_ADDRESS, &Arg1) && GetUlong64(DEBUG_FLR_FAULTING_IP, &Arg2) && Arg1 == Arg2) { // Invalid instruction. SetUlong64(DEBUG_FLR_FAILED_INSTRUCTION_ADDRESS, Arg1); return TRUE; } } // // If we've determined that the current failure is // due to inability to execute an instruction, check // and see whether there's a call to the instruction. // If the instruction prior at the return address can be analyzed, // check for known instruction sequences to see if perhaps // the processor incorrectly handled a control transfer. // if (!Get(DEBUG_FLR_POSSIBLE_INVALID_CONTROL_TRANSFER) && (Entry = Get(DEBUG_FLR_LAST_CONTROL_TRANSFER))) { ULONG64 ReturnOffset = FA_ENTRY_DATA(PULONG64, Entry)[0]; Arg2 = FA_ENTRY_DATA(PULONG64, Entry)[1]; ULONG64 StackOffset = FA_ENTRY_DATA(PULONG64, Entry)[2]; ULONG64 Target = 0; switch(g_TargetMachine) { case IMAGE_FILE_MACHINE_I386: Target = GetControlTransferTargetX86(StackOffset, &ReturnOffset); break; } if (Target && Target != Arg2) { char Sym1[MAX_PATH], Sym2[MAX_PATH]; ULONG64 Disp; // // If both addresses are within the same function // we assume that there has been some execution // in the function and therefore this doesn't // actually indicate a problem. // NOTE - DbgBreakPointWithStatus has an internal label // which messes up symbols, so account for that too by // checking we are 10 bytes within the function. // FaGetSymbol(Target, Sym1, &Disp, sizeof(Sym1)); FaGetSymbol(Arg2, Sym2, &Disp, sizeof(Sym2)); if ((Arg2 - Target > 10) && (strcmp(Sym1, Sym2) != 0)) { PCHAR String; ULONG64 BitDiff; ULONG64 BitDiff2; Values[0] = ReturnOffset; Values[1] = Target; SetUlong64s(DEBUG_FLR_POSSIBLE_INVALID_CONTROL_TRANSFER, 2, Values); // // If the difference between the two address is a power of 2, // then it's a single bit error. // Also, to avoid sign extension issues due to a 1 bit error // in the top bit, check if the difference betweent the two is // only the sign extensions, and zero out the top 32 bits if // it's the case. // BitDiff = Arg2 ^ Target; if ((BitDiff >> 32) == 0xFFFFFFFF) { BitDiff &= 0xFFFFFFFF; } if (!(BitDiff2 = (BitDiff & (BitDiff - 1)))) { Set(DEBUG_FLR_SINGLE_BIT_ERROR, 1); } if (!(BitDiff2 & (BitDiff2 - 1))) { Set(DEBUG_FLR_TWO_BIT_ERROR, 1); } if (String = g_pTriager->GetFollowupStr("badcpu", "")) { BestClassFollowUp = FlpSpecific; CopyString(PossibleFollowups[FlpSpecific].Owner, String, sizeof(PossibleFollowups[FlpSpecific].Owner)); SetString(DEBUG_FLR_MODULE_NAME, "No_Module"); SetString(DEBUG_FLR_IMAGE_NAME, "No_Image"); SetUlong64(DEBUG_FLR_IMAGE_TIMESTAMP, 0); } return TRUE; } } } // // If the process is determine to be of importance to this failure, // also expose the process name. // This will overwrite the PROCESS_NAME of the default process. // if (GetUlong64(DEBUG_FLR_PROCESS_OBJECT, &Arg1) && !Get(DEBUG_FLR_PROCESS_NAME)) { ULONG NameOffset; CHAR Name[17]; if (!GetFieldOffset("nt!_EPROCESS", "ImageFileName", &NameOffset) && NameOffset) { if (ReadMemory(Arg1 + NameOffset, Name, sizeof(Name), &Done) && (Done == sizeof(Name))) { Name[16] = 0; SetString(DEBUG_FLR_PROCESS_NAME, Name); return TRUE; } } } return FALSE; }