You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3470 lines
99 KiB
3470 lines
99 KiB
//----------------------------------------------------------------------------
|
|
//
|
|
// Generic failure analysis framework.
|
|
//
|
|
// Copyright (C) Microsoft Corporation, 2001.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
#include <time.h>
|
|
|
|
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;
|
|
}
|