mirror of
https://github.com/arehnman/yttrium-virtio-gpu.git
synced 2026-10-31 02:05:04 +00:00
The project is switching from GPL to BSD. Signed-off-by: Ladi Prosek <[email protected]>
900 lines
28 KiB
C++
Executable File
900 lines
28 KiB
C++
Executable File
/*
|
|
* This file contains dump parsing logic
|
|
*
|
|
* Copyright (c) 2008-2017 Red Hat, Inc.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met :
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and / or other materials provided with the distribution.
|
|
* 3. Neither the names of the copyright holders nor the names of their contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "stdafx.h"
|
|
#include "NetKVMDumpParser.h"
|
|
#include "..\..\Common\DebugData.h"
|
|
#include <sal.h>
|
|
|
|
#ifdef _DEBUG
|
|
#define new DEBUG_NEW
|
|
#endif
|
|
|
|
#define PRINT_SEPARATOR "-------------------------------------"
|
|
#define DEBUG_SYMBOLS_IF IDebugSymbols3
|
|
|
|
FILE *outf = stdout;
|
|
|
|
//#define UNDER_DEBUGGING
|
|
|
|
#ifndef UNDER_DEBUGGING
|
|
#define PRINT(fmt, ...) fprintf(outf, "[%s]: "##fmt##"\n", __FUNCTION__, __VA_ARGS__);
|
|
#else
|
|
#define PRINT(fmt, ...) { CString __s; __s.Format(TEXT("[%s]: ")##TEXT(fmt)##TEXT("\n"), TEXT(__FUNCTION__), __VA_ARGS__); OutputDebugString(__s.GetBuffer()); }
|
|
#endif
|
|
|
|
CString ErrorToString(HRESULT hr)
|
|
{
|
|
CString s;
|
|
LPTSTR lpMessageBuffer;
|
|
if (FormatMessage(
|
|
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
FORMAT_MESSAGE_FROM_SYSTEM,
|
|
NULL,
|
|
hr,
|
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), //The user default language
|
|
(LPTSTR) &lpMessageBuffer,
|
|
0,
|
|
NULL ))
|
|
{
|
|
s = lpMessageBuffer;
|
|
LocalFree(lpMessageBuffer);
|
|
}
|
|
else
|
|
{
|
|
s.Format("[Error %lu]", hr);
|
|
}
|
|
return s;
|
|
}
|
|
|
|
static const LPCSTR OpNames[] = {
|
|
"PowerOff ",
|
|
"PowerOn ",
|
|
"SysPause ",
|
|
"SysResume ",
|
|
"InternalSendPause ",
|
|
"InternalReceivePause ",
|
|
"InternalSendResume ",
|
|
"InternalReceiveResume",
|
|
"SysReset ",
|
|
"Halt ",
|
|
"ConnectIndication ",
|
|
"DPC ",
|
|
"Send ",
|
|
"SendNBLRequest ",
|
|
"SendPacketRequest ",
|
|
"SendPacketMapped ",
|
|
"SubmittedPacket ",
|
|
"BufferSent ",
|
|
"BufferReceivedStat ",
|
|
"BufferReturned ",
|
|
"SendComplete ",
|
|
"TxProcess ",
|
|
"PacketReceived ",
|
|
"OidRequest ",
|
|
"PnpEvent ",
|
|
};
|
|
|
|
static CString HistoryOperationName(ULONG op)
|
|
{
|
|
CString s;
|
|
if (op < sizeof(OpNames)/ sizeof(OpNames[0]))
|
|
s = OpNames[op];
|
|
else
|
|
s.Format("##%d", op);
|
|
return s;
|
|
}
|
|
|
|
typedef struct _tagNamedFlag
|
|
{
|
|
ULONG64 flag;
|
|
LPCSTR name;
|
|
}tNamedFlag;
|
|
|
|
typedef struct _tagNamedValue
|
|
{
|
|
ULONG64 value;
|
|
LPCSTR name;
|
|
}tNamedValue;
|
|
|
|
#define STRINGER(x) #x
|
|
#define VALUE(v) { v, STRINGER(##v) }
|
|
#define ENDTABLE { 0, NULL }
|
|
|
|
const tNamedValue SessionStatusValues[] =
|
|
{
|
|
VALUE(DEBUG_SESSION_ACTIVE),
|
|
VALUE(DEBUG_SESSION_END_SESSION_ACTIVE_TERMINATE),
|
|
VALUE(DEBUG_SESSION_END_SESSION_ACTIVE_DETACH),
|
|
VALUE(DEBUG_SESSION_END_SESSION_PASSIVE),
|
|
VALUE(DEBUG_SESSION_END),
|
|
VALUE(DEBUG_SESSION_REBOOT),
|
|
VALUE(DEBUG_SESSION_HIBERNATE),
|
|
VALUE(DEBUG_SESSION_FAILURE),
|
|
ENDTABLE
|
|
};
|
|
|
|
const tNamedValue DebuggeeStateValues[] =
|
|
{
|
|
VALUE(DEBUG_CDS_ALL),
|
|
VALUE(DEBUG_CDS_REGISTERS),
|
|
VALUE(DEBUG_CDS_DATA),
|
|
ENDTABLE
|
|
};
|
|
|
|
const tNamedValue DebuggeeStateArgValues[] =
|
|
{
|
|
VALUE(DEBUG_DATA_SPACE_VIRTUAL),
|
|
VALUE(DEBUG_DATA_SPACE_PHYSICAL),
|
|
VALUE(DEBUG_DATA_SPACE_CONTROL),
|
|
VALUE(DEBUG_DATA_SPACE_IO),
|
|
VALUE(DEBUG_DATA_SPACE_MSR),
|
|
VALUE(DEBUG_DATA_SPACE_BUS_DATA),
|
|
VALUE(DEBUG_DATA_SPACE_DEBUGGER_DATA),
|
|
VALUE(DEBUG_DATA_SPACE_COUNT),
|
|
ENDTABLE
|
|
};
|
|
|
|
const tNamedFlag EngineStateFlags[] =
|
|
{
|
|
VALUE(DEBUG_CES_ALL),
|
|
VALUE(DEBUG_CES_CURRENT_THREAD),
|
|
VALUE(DEBUG_CES_EFFECTIVE_PROCESSOR),
|
|
VALUE(DEBUG_CES_BREAKPOINTS),
|
|
VALUE(DEBUG_CES_CODE_LEVEL),
|
|
VALUE(DEBUG_CES_EXECUTION_STATUS),
|
|
VALUE(DEBUG_CES_ENGINE_OPTIONS),
|
|
VALUE(DEBUG_CES_LOG_FILE),
|
|
VALUE(DEBUG_CES_RADIX),
|
|
VALUE(DEBUG_CES_EVENT_FILTERS),
|
|
VALUE(DEBUG_CES_PROCESS_OPTIONS),
|
|
VALUE(DEBUG_CES_EXTENSIONS),
|
|
VALUE(DEBUG_CES_SYSTEMS),
|
|
VALUE(DEBUG_CES_ASSEMBLY_OPTIONS),
|
|
VALUE(DEBUG_CES_EXPRESSION_SYNTAX),
|
|
VALUE(DEBUG_CES_TEXT_REPLACEMENTS),
|
|
ENDTABLE
|
|
};
|
|
|
|
|
|
const tNamedFlag SymbolStateFlags[] =
|
|
{
|
|
VALUE(DEBUG_CSS_ALL),
|
|
VALUE(DEBUG_CSS_LOADS),
|
|
VALUE(DEBUG_CSS_UNLOADS),
|
|
VALUE(DEBUG_CSS_SCOPE),
|
|
VALUE(DEBUG_CSS_PATHS),
|
|
VALUE(DEBUG_CSS_SYMBOL_OPTIONS),
|
|
VALUE(DEBUG_CSS_TYPE_OPTIONS),
|
|
ENDTABLE
|
|
};
|
|
|
|
#if !defined(DEBUG_STATUS_RESTART_REQUESTED)
|
|
#error Path to debug SDK is not defined properly in Property Manager.
|
|
#endif
|
|
|
|
|
|
const tNamedValue EngineExecutionStatus[] =
|
|
{
|
|
VALUE(DEBUG_STATUS_NO_CHANGE),
|
|
VALUE(DEBUG_STATUS_GO),
|
|
VALUE(DEBUG_STATUS_GO_HANDLED),
|
|
VALUE(DEBUG_STATUS_GO_NOT_HANDLED),
|
|
VALUE(DEBUG_STATUS_STEP_OVER),
|
|
VALUE(DEBUG_STATUS_STEP_INTO),
|
|
VALUE(DEBUG_STATUS_BREAK),
|
|
VALUE(DEBUG_STATUS_NO_DEBUGGEE),
|
|
VALUE(DEBUG_STATUS_STEP_BRANCH),
|
|
VALUE(DEBUG_STATUS_IGNORE_EVENT),
|
|
VALUE(DEBUG_STATUS_RESTART_REQUESTED),
|
|
VALUE(DEBUG_STATUS_REVERSE_GO),
|
|
VALUE(DEBUG_STATUS_REVERSE_STEP_BRANCH),
|
|
VALUE(DEBUG_STATUS_REVERSE_STEP_OVER),
|
|
VALUE(DEBUG_STATUS_REVERSE_STEP_INTO),
|
|
ENDTABLE
|
|
};
|
|
|
|
const tNamedFlag EngineExecutionStatusFlags[] =
|
|
{
|
|
VALUE(DEBUG_STATUS_INSIDE_WAIT),
|
|
VALUE(DEBUG_STATUS_WAIT_TIMEOUT),
|
|
ENDTABLE
|
|
};
|
|
|
|
const tNamedFlag ModuleFlags[] =
|
|
{
|
|
VALUE(DEBUG_MODULE_LOADED),
|
|
VALUE(DEBUG_MODULE_UNLOADED),
|
|
VALUE(DEBUG_MODULE_USER_MODE),
|
|
VALUE(DEBUG_MODULE_EXPLICIT),
|
|
VALUE(DEBUG_MODULE_SECONDARY),
|
|
VALUE(DEBUG_MODULE_SYNTHETIC),
|
|
VALUE(DEBUG_MODULE_SYM_BAD_CHECKSUM),
|
|
ENDTABLE
|
|
};
|
|
|
|
const tNamedValue SymbolTypeValues[] =
|
|
{
|
|
VALUE(DEBUG_SYMTYPE_NONE),
|
|
VALUE(DEBUG_SYMTYPE_COFF),
|
|
VALUE(DEBUG_SYMTYPE_CODEVIEW),
|
|
VALUE(DEBUG_SYMTYPE_PDB),
|
|
VALUE(DEBUG_SYMTYPE_EXPORT),
|
|
VALUE(DEBUG_SYMTYPE_DEFERRED),
|
|
VALUE(DEBUG_SYMTYPE_SYM),
|
|
VALUE(DEBUG_SYMTYPE_DIA),
|
|
ENDTABLE
|
|
};
|
|
|
|
|
|
const tNamedFlag SymbolOptionsFlags[] =
|
|
{
|
|
VALUE(SYMOPT_CASE_INSENSITIVE),
|
|
VALUE(SYMOPT_UNDNAME),
|
|
VALUE(SYMOPT_DEFERRED_LOADS),
|
|
VALUE(SYMOPT_NO_CPP),
|
|
VALUE(SYMOPT_LOAD_LINES),
|
|
VALUE(SYMOPT_OMAP_FIND_NEAREST),
|
|
VALUE(SYMOPT_LOAD_ANYTHING),
|
|
VALUE(SYMOPT_IGNORE_CVREC),
|
|
VALUE(SYMOPT_NO_UNQUALIFIED_LOADS),
|
|
VALUE(SYMOPT_FAIL_CRITICAL_ERRORS),
|
|
VALUE(SYMOPT_EXACT_SYMBOLS),
|
|
VALUE(SYMOPT_ALLOW_ABSOLUTE_SYMBOLS),
|
|
VALUE(SYMOPT_IGNORE_NT_SYMPATH),
|
|
VALUE(SYMOPT_INCLUDE_32BIT_MODULES),
|
|
VALUE(SYMOPT_PUBLICS_ONLY),
|
|
VALUE(SYMOPT_NO_PUBLICS),
|
|
VALUE(SYMOPT_AUTO_PUBLICS),
|
|
VALUE(SYMOPT_NO_IMAGE_SEARCH),
|
|
VALUE(SYMOPT_SECURE),
|
|
VALUE(SYMOPT_NO_PROMPTS),
|
|
VALUE(SYMOPT_OVERWRITE),
|
|
VALUE(SYMOPT_IGNORE_IMAGEDIR),
|
|
VALUE(SYMOPT_FLAT_DIRECTORY),
|
|
VALUE(SYMOPT_FAVOR_COMPRESSED),
|
|
VALUE(SYMOPT_ALLOW_ZERO_ADDRESS),
|
|
VALUE(SYMOPT_DISABLE_SYMSRV_AUTODETECT),
|
|
VALUE(SYMOPT_DEBUG),
|
|
ENDTABLE
|
|
};
|
|
|
|
typedef struct _tagModule
|
|
{
|
|
LPCSTR name;
|
|
ULONG64 Base;
|
|
ULONG index;
|
|
}tModule;
|
|
|
|
class tDumpParser : public DebugBaseEventCallbacks, public IDebugOutputCallbacks
|
|
{
|
|
public:
|
|
tDumpParser() : refCount(0), Client(NULL), Control(NULL), DataSpaces(NULL), DebugSymbols(NULL)
|
|
{
|
|
HRESULT hr;
|
|
bEnableOutput = FALSE;
|
|
hr = DebugCreate(__uuidof(IDebugClient), (void **)&Client);
|
|
if (Client) hr = Client->QueryInterface(__uuidof(IDebugControl2), (void **)&Control);
|
|
if (Client) hr = Client->QueryInterface(__uuidof(IDebugDataSpaces3), (void **)&DataSpaces);
|
|
if (Client) hr = Client->QueryInterface(__uuidof(DEBUG_SYMBOLS_IF), (void **)&DebugSymbols);
|
|
}
|
|
~tDumpParser()
|
|
{
|
|
ULONG ul = 0;
|
|
if (DebugSymbols) DebugSymbols->Release();
|
|
if (Control) Control->Release();
|
|
if (DataSpaces) DataSpaces->Release();
|
|
if (Client) ul = Client->Release();
|
|
//PRINT("finished (%d)", ul);
|
|
}
|
|
BOOL LoadFile(TCHAR *filename)
|
|
{
|
|
HRESULT hr = S_FALSE;
|
|
if (Client && Control && DataSpaces)
|
|
{
|
|
hr = Client->SetEventCallbacks(this);
|
|
if (S_OK == hr) hr = Client->SetOutputCallbacks(this);
|
|
DebugSymbols->AddSymbolOptions(SYMOPT_DEBUG);
|
|
//DebugSymbols->RemoveSymbolOptions(SYMOPT_DEFERRED_LOADS);
|
|
DebugSymbols->AppendSymbolPath(".");
|
|
}
|
|
else
|
|
{
|
|
CString sMessage = TEXT("Error: Not all required interfaces are up\n");
|
|
if (!Client) sMessage += TEXT("Client interface is not initialized\n");
|
|
if (!Control) sMessage += TEXT("Control interface is not initialized\n");
|
|
if (!DataSpaces) sMessage += TEXT("Data interface is not initialized\n");
|
|
PRINT("%s", sMessage.GetBuffer());
|
|
}
|
|
if (hr == S_OK)
|
|
{
|
|
hr = Client->OpenDumpFile(filename);
|
|
}
|
|
if (hr == S_OK) Control->WaitForEvent(0, INFINITE);
|
|
return hr == S_OK;
|
|
}
|
|
void ProcessDumpFile();
|
|
void FindOurTaggedCrashData(BOOL bWithSymbols);
|
|
BOOL CheckLoadedSymbols(tModule *pModule);
|
|
void ProcessSymbols(tModule *pModule);
|
|
void ParseCrashData(tBugCheckStaticDataHeader *ph, ULONG64 databuffer, ULONG bytesRead, BOOL bWithSymbols);
|
|
typedef enum _tageSystemProperty {
|
|
espSymbolPath,
|
|
espSystemVersion,
|
|
espSystemTime,
|
|
} eSystemProperty;
|
|
CString GetProperty(eSystemProperty Prop);
|
|
protected:
|
|
CString Parse(ULONG64 val, const tNamedValue *pt)
|
|
{
|
|
CString s;
|
|
while (pt->name) { if (pt->value == val) { s = pt->name; break; } pt++; }
|
|
if (s.IsEmpty()) s.Format(TEXT("Unknown value 0x%I64X"), val);
|
|
return s;
|
|
}
|
|
|
|
CString Parse(ULONG64 val, const tNamedFlag *pt)
|
|
{
|
|
CString s;
|
|
while (pt->name && val)
|
|
{
|
|
if ((pt->flag & val) == pt->flag)
|
|
{
|
|
val &= ~pt->flag;
|
|
if (!s.IsEmpty()) s += ' ';
|
|
s += pt->name;
|
|
}
|
|
pt++;
|
|
}
|
|
if (val)
|
|
{
|
|
CString sv;
|
|
sv.Format(TEXT("0x%X"), val);
|
|
if (!s.IsEmpty()) s += ' ';
|
|
s += sv;
|
|
}
|
|
return s;
|
|
}
|
|
protected:
|
|
STDMETHOD_(ULONG, AddRef)(
|
|
THIS
|
|
) { return ++refCount; }
|
|
STDMETHOD_(ULONG, Release)(
|
|
THIS
|
|
) { return --refCount; }
|
|
STDMETHOD(QueryInterface)(
|
|
THIS_
|
|
__in REFIID InterfaceId,
|
|
__out PVOID* Interface
|
|
)
|
|
{
|
|
*Interface = NULL;
|
|
if (IsEqualIID(InterfaceId, __uuidof(IUnknown)) ||
|
|
IsEqualIID(InterfaceId, __uuidof(IDebugEventCallbacks)))
|
|
{
|
|
*Interface = (IDebugEventCallbacks *)this;
|
|
}
|
|
else if (IsEqualIID(InterfaceId, __uuidof(IDebugOutputCallbacks)))
|
|
{
|
|
*Interface = (IDebugOutputCallbacks *)this;
|
|
}
|
|
if (*Interface) AddRef();
|
|
return (*Interface) ? S_OK : E_NOINTERFACE;
|
|
}
|
|
STDMETHOD(GetInterestMask)(
|
|
THIS_
|
|
__out PULONG Mask
|
|
)
|
|
{
|
|
*Mask =
|
|
DEBUG_EVENT_BREAKPOINT |
|
|
DEBUG_EVENT_EXCEPTION |
|
|
DEBUG_EVENT_LOAD_MODULE |
|
|
DEBUG_EVENT_UNLOAD_MODULE |
|
|
DEBUG_EVENT_SYSTEM_ERROR |
|
|
DEBUG_EVENT_SESSION_STATUS |
|
|
DEBUG_EVENT_CHANGE_DEBUGGEE_STATE |
|
|
DEBUG_EVENT_CHANGE_ENGINE_STATE |
|
|
DEBUG_EVENT_CHANGE_SYMBOL_STATE ;
|
|
return S_OK;
|
|
}
|
|
STDMETHOD(Breakpoint)(
|
|
THIS_
|
|
__in PDEBUG_BREAKPOINT Bp
|
|
)
|
|
{
|
|
PRINT("");
|
|
return DEBUG_STATUS_BREAK;
|
|
}
|
|
STDMETHOD(Exception)(
|
|
THIS_
|
|
__in PEXCEPTION_RECORD64 Exception,
|
|
__in ULONG FirstChance
|
|
)
|
|
{
|
|
PRINT("");
|
|
return DEBUG_STATUS_NO_CHANGE;
|
|
}
|
|
|
|
STDMETHOD(LoadModule)(
|
|
THIS_
|
|
__in ULONG64 ImageFileHandle,
|
|
__in ULONG64 BaseOffset,
|
|
__in ULONG ModuleSize,
|
|
__in PCSTR ModuleName,
|
|
__in PCSTR ImageName,
|
|
__in ULONG CheckSum,
|
|
__in ULONG TimeDateStamp
|
|
)
|
|
{
|
|
PRINT("%s", ImageName);
|
|
return DEBUG_STATUS_NO_CHANGE;
|
|
}
|
|
STDMETHOD(UnloadModule)(
|
|
THIS_
|
|
__in PCSTR ImageBaseName,
|
|
__in ULONG64 BaseOffset
|
|
)
|
|
{
|
|
PRINT("%s", ImageBaseName);
|
|
return DEBUG_STATUS_NO_CHANGE;
|
|
}
|
|
STDMETHOD(SystemError)(
|
|
THIS_
|
|
__in ULONG Error,
|
|
__in ULONG Level
|
|
)
|
|
{
|
|
PRINT("error 0x%X, level 0x%X", Error, Level);
|
|
return DEBUG_STATUS_NO_CHANGE;
|
|
}
|
|
STDMETHOD(SessionStatus)(
|
|
THIS_
|
|
__in ULONG Status
|
|
)
|
|
{
|
|
CString s = Parse(Status, SessionStatusValues);
|
|
PRINT("%s", s.GetBuffer());
|
|
return DEBUG_STATUS_NO_CHANGE;
|
|
}
|
|
STDMETHOD(ChangeDebuggeeState)(
|
|
THIS_
|
|
__in ULONG Flags,
|
|
__in ULONG64 Argument
|
|
)
|
|
{
|
|
CString sf = Parse(Flags, DebuggeeStateValues);
|
|
CString sarg;
|
|
if (Flags == DEBUG_CDS_DATA) sarg = Parse(Argument, DebuggeeStateArgValues);
|
|
else sarg.Format(TEXT("0x%I64X"), Argument);
|
|
PRINT("%s(%s)", sf.GetBuffer(), sarg.GetBuffer());
|
|
return DEBUG_STATUS_NO_CHANGE;
|
|
return S_OK;
|
|
}
|
|
STDMETHOD(ChangeEngineState)(
|
|
THIS_
|
|
__in ULONG Flags,
|
|
__in ULONG64 Argument
|
|
)
|
|
{
|
|
CString s = Parse(Flags, EngineStateFlags);
|
|
CString sArg;
|
|
if (Flags == DEBUG_CES_EXECUTION_STATUS)
|
|
{
|
|
CString sWait = Parse(Argument & ~DEBUG_STATUS_MASK, EngineExecutionStatusFlags);
|
|
sArg = Parse(Argument & DEBUG_STATUS_MASK, EngineExecutionStatus);
|
|
sArg += ' ';
|
|
sArg += sWait;
|
|
}
|
|
else
|
|
sArg.Format(TEXT("arg 0x%I64X"), Argument);
|
|
PRINT("%s(%s)", s.GetBuffer(), sArg.GetBuffer());
|
|
if (Flags == DEBUG_CES_EXECUTION_STATUS && (Argument & DEBUG_STATUS_MASK) == DEBUG_STATUS_BREAK
|
|
&& !(Argument & ~DEBUG_STATUS_MASK))
|
|
{
|
|
ProcessDumpFile();
|
|
}
|
|
return S_OK;
|
|
}
|
|
STDMETHOD(ChangeSymbolState)(
|
|
THIS_
|
|
__in ULONG Flags,
|
|
__in ULONG64 Argument
|
|
)
|
|
{
|
|
CString s = Parse(Flags, SymbolStateFlags);
|
|
PRINT("%s(arg 0x%I64X)", s.GetBuffer(), Argument);
|
|
return S_OK;
|
|
}
|
|
// IDebugOutputCallbacks.
|
|
STDMETHOD(Output)(
|
|
THIS_
|
|
__in ULONG Mask,
|
|
__in PCSTR Text
|
|
)
|
|
{
|
|
if (bEnableOutput)
|
|
{
|
|
PRINT("%s", Text);
|
|
}
|
|
return S_OK;
|
|
}
|
|
ULONG refCount;
|
|
IDebugClient *Client;
|
|
IDebugControl2 *Control;
|
|
IDebugDataSpaces3 *DataSpaces;
|
|
DEBUG_SYMBOLS_IF *DebugSymbols;
|
|
BOOL bEnableOutput;
|
|
};
|
|
|
|
#define CHECK_INTERFACE(xf) { \
|
|
xf *p; HRESULT hr = Client->QueryInterface(__uuidof(xf), (void **)&p); \
|
|
PRINT("Interface " TEXT(STRINGER(xf)) TEXT(" %spresent"), p ? "" : "NOT "); \
|
|
if (p) p->Release(); }
|
|
|
|
|
|
static void ParseHistoryEntry(LONGLONG basetime, tBugCheckHistoryDataEntry *phist, LONG Index)
|
|
{
|
|
if (!phist)
|
|
{
|
|
PRINT("Op Ctx Time Params");
|
|
}
|
|
else if (phist[Index].Context)
|
|
{
|
|
LONGLONG diffInt = (basetime - phist[Index].TimeStamp.QuadPart) / 10;
|
|
CString sOp = HistoryOperationName(phist[Index].operation);
|
|
|
|
#if (PARANDIS_DEBUG_HISTORY_DATA_VERSION == 0)
|
|
PRINT("%s %I64X [-%09I64d] x%08X x%08X x%08X %I64X", sOp.GetBuffer(), phist[Index].Context, diffInt, phist[Index].lParam2, phist[Index].lParam3, phist[Index].lParam4, phist[Index].pParam1 );
|
|
#elif (PARANDIS_DEBUG_HISTORY_DATA_VERSION == 1)
|
|
PRINT("CPU[%d] IRQL[%d] %s %I64X [-%09I64d] x%08X x%08X x%08X %I64X", phist[Index].uProcessor, phist[Index].uIRQL, sOp.GetBuffer(), phist[Index].Context, diffInt, phist[Index].lParam2, phist[Index].lParam3, phist[Index].lParam4, phist[Index].pParam1 );
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void tDumpParser::ParseCrashData(tBugCheckStaticDataHeader *ph, ULONG64 databuffer, ULONG bytesRead, BOOL bWithSymbols)
|
|
{
|
|
UINT i;
|
|
for (i = 0; i < ph->ulMaxContexts; ++i)
|
|
{
|
|
if (ph->PerNicDataVersion == 0)
|
|
{
|
|
tBugCheckPerNicDataContent_V0 *pndc = (tBugCheckPerNicDataContent_V0 *)(ph->PerNicData - databuffer + (PUCHAR)ph);
|
|
pndc += i;
|
|
if (pndc->Context)
|
|
{
|
|
LONGLONG diffInt = (ph->qCrashTime.QuadPart - pndc->LastInterruptTimeStamp.QuadPart) / 10;
|
|
LONGLONG diffTx = (ph->qCrashTime.QuadPart - pndc->LastTxCompletionTimeStamp.QuadPart) / 10;
|
|
PRINT(PRINT_SEPARATOR);
|
|
PRINT("Context %I64X:", pndc->Context);
|
|
PRINT("\tLastInterrupt %I64d us before crash", diffInt);
|
|
PRINT("\tLast Tx complete %I64d us before crash", diffTx);
|
|
PRINT("\tWaiting <unknown> packets, %d free buffers", pndc->nofReadyTxBuffers);
|
|
PRINT(PRINT_SEPARATOR);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PRINT("Unsupported per-NIC data version %d", ph->PerNicDataVersion);
|
|
}
|
|
}
|
|
if (ph->StaticDataVersion == 0)
|
|
{
|
|
tBugCheckStaticDataContent_V0 *pd = (tBugCheckStaticDataContent_V0 *)(ph->DataArea - databuffer + (PUCHAR)ph);
|
|
tBugCheckHistoryDataEntry_V1 *phist = (tBugCheckHistoryDataEntry *)(pd->HistoryData - databuffer + (PUCHAR)ph);
|
|
PRINT(PRINT_SEPARATOR);
|
|
if (pd->SizeOfHistory > 2)
|
|
{
|
|
PRINT("History: version %d, %d entries of %d, current at %d", pd->HistoryDataVersion, pd->SizeOfHistory, pd->SizeOfHistoryEntry, pd->CurrentHistoryIndex);
|
|
LONG Index = pd->CurrentHistoryIndex % pd->SizeOfHistory;
|
|
LONG EndIndex = Index;
|
|
ParseHistoryEntry(NULL, NULL, 0);
|
|
for (; Index < (LONG)pd->SizeOfHistory; Index++)
|
|
{
|
|
ParseHistoryEntry(ph->qCrashTime.QuadPart, phist, Index);
|
|
}
|
|
for (Index = 0; Index < EndIndex; Index++)
|
|
{
|
|
ParseHistoryEntry(ph->qCrashTime.QuadPart, phist, Index);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PRINT("History records are not available");
|
|
}
|
|
PRINT(PRINT_SEPARATOR);
|
|
}
|
|
else if (ph->StaticDataVersion == 1)
|
|
{
|
|
tBugCheckStaticDataContent_V1 *pd = (tBugCheckStaticDataContent_V1 *)(ph->DataArea - databuffer + (PUCHAR)ph);
|
|
if (pd->PendingNblEntryVersion == 0)
|
|
{
|
|
tPendingNBlEntry_V0 *pNBL = (tPendingNBlEntry_V0 *)(pd->PendingNblData - databuffer + (PUCHAR)ph);
|
|
if (pd->MaxPendingNbl > 1)
|
|
{
|
|
PRINT(PRINT_SEPARATOR);
|
|
PRINT("Pending NBL%s, if any (time ago in us):", pd->fNBLOverflow ? "(was logging overflow)" : "");
|
|
for (ULONG i = 0; i < pd->MaxPendingNbl; ++i)
|
|
{
|
|
ULONGLONG diff = ph->qCrashTime.QuadPart - pNBL[i].TimeStamp.QuadPart;
|
|
if (pNBL[i].NBL == 0) continue;
|
|
PRINT("[%05d] NBL %I64x %I64d", i, pNBL[i].NBL, diff);
|
|
}
|
|
PRINT(PRINT_SEPARATOR);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void tDumpParser::FindOurTaggedCrashData(BOOL bWithSymbols)
|
|
{
|
|
ULONG64 h;
|
|
if (S_OK == DataSpaces->StartEnumTagged(&h))
|
|
{
|
|
UCHAR ourBuffer[16];
|
|
ULONG size;
|
|
GUID guid;
|
|
while (S_OK == DataSpaces->GetNextTagged(h, &guid, &size))
|
|
{
|
|
WCHAR string[64];
|
|
StringFromGUID2(guid, string, sizeof(string)/sizeof(string[0]));
|
|
//PRINT("Found %S(size %d)", string, size);
|
|
if (IsEqualGUID(ParaNdis_CrashGuid, guid))
|
|
{
|
|
PRINT("Found NetKVM GUID");
|
|
if (S_OK == DataSpaces->ReadTagged(&guid, 0, ourBuffer, size, &size))
|
|
{
|
|
if (size >= sizeof(tBugCheckDataLocation))
|
|
{
|
|
tBugCheckDataLocation *bcdl = (tBugCheckDataLocation *)ourBuffer;
|
|
PRINT("Found NetKVM data at %I64X, size %lld", bcdl->Address, bcdl->Size);
|
|
ULONG bufferSize= (ULONG)bcdl->Size;
|
|
ULONG bytesRead;
|
|
PVOID databuffer = malloc(bufferSize);
|
|
if (databuffer)
|
|
{
|
|
if (S_OK == DataSpaces->ReadVirtual(bcdl->Address, databuffer, bufferSize, &bytesRead))
|
|
{
|
|
tBugCheckStaticDataHeader *ph = (tBugCheckStaticDataHeader *)databuffer;
|
|
PRINT("Retrieved %d bytes of data", bytesRead);
|
|
if (bytesRead >= sizeof(tBugCheckStaticDataHeader))
|
|
{
|
|
PRINT("Versions: static data %d, per-NIC data %d, ptr size %d, %d contexts, crash time %I64X",
|
|
ph->StaticDataVersion, ph->PerNicDataVersion, ph->SizeOfPointer, ph->ulMaxContexts, ph->qCrashTime.QuadPart);
|
|
PRINT("Per-NIC data at %I64X, Static data at %I64X(%lld bytes)", ph->PerNicData, ph->DataArea, ph->DataAreaSize);
|
|
ParseCrashData(ph, bcdl->Address, bytesRead, bWithSymbols);
|
|
}
|
|
}
|
|
free(databuffer);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
DataSpaces->EndEnumTagged(h);
|
|
|
|
}
|
|
}
|
|
|
|
static BOOL GetModuleName(ULONG Which, ULONG64 Base, DEBUG_SYMBOLS_IF *DebugSymbols, CString& s)
|
|
{
|
|
HRESULT hr;
|
|
ULONG bufsize = 1024;
|
|
char *buf = (char *)malloc(bufsize);
|
|
*buf = 0;
|
|
hr = DebugSymbols->GetModuleNameString(
|
|
Which,
|
|
DEBUG_ANY_ID,
|
|
Base,
|
|
buf,
|
|
bufsize,
|
|
NULL);
|
|
s = buf;
|
|
free(buf);
|
|
return S_OK == hr;
|
|
}
|
|
|
|
static BOOL TryMatchingSymbols(DEBUG_SYMBOLS_IF *DebugSymbols, PCSTR name, BOOL bPrintAll = FALSE)
|
|
{
|
|
UINT n = 0;
|
|
CString s;
|
|
s.Format("%s!*", name);
|
|
ULONG64 matchHandle;
|
|
if (S_OK == DebugSymbols->StartSymbolMatch(s.GetBuffer(), &matchHandle))
|
|
{
|
|
ULONG size = 1024;
|
|
char *buf = (char *)malloc(size);
|
|
*buf = 0;
|
|
while (S_OK == DebugSymbols->GetNextSymbolMatch(
|
|
matchHandle, buf, size, NULL, NULL))
|
|
{
|
|
n++;
|
|
if (!bPrintAll) break;
|
|
PRINT("%s", buf);
|
|
}
|
|
DebugSymbols->EndSymbolMatch(matchHandle);
|
|
free(buf);
|
|
}
|
|
return n != 0;
|
|
}
|
|
|
|
BOOL tDumpParser::CheckLoadedSymbols(tModule *pModule)
|
|
{
|
|
BOOL bLoaded = FALSE;
|
|
ULONG bufferSize = 2048, bufferUsage;
|
|
UNREFERENCED_PARAMETER(bufferUsage);
|
|
char *buffer = (char *)malloc(bufferSize);
|
|
DEBUG_MODULE_PARAMETERS ModuleParams;
|
|
HRESULT hr = DebugSymbols->GetModuleByModuleName(pModule->name, 0, &pModule->index, &pModule->Base);
|
|
if (S_OK == hr)
|
|
{
|
|
CString s;
|
|
PRINT("Found %s at %I64X", pModule->name, pModule->Base);
|
|
if (GetModuleName(DEBUG_MODNAME_MODULE, pModule->Base, DebugSymbols, s))
|
|
PRINT("\tModule Name:%s", s.GetBuffer());
|
|
if (GetModuleName(DEBUG_MODNAME_IMAGE, pModule->Base, DebugSymbols, s))
|
|
PRINT("\tImage Name:%s", s.GetBuffer());
|
|
if (GetModuleName(DEBUG_MODNAME_SYMBOL_FILE, pModule->Base, DebugSymbols, s))
|
|
PRINT("\tSymbol file:%s", s.GetBuffer());
|
|
|
|
bLoaded = 0 != TryMatchingSymbols(DebugSymbols, pModule->name);
|
|
PRINT("Symbols for %s %sLOADED", pModule->name, bLoaded ? "" : "NOT ");
|
|
|
|
if (S_OK == DebugSymbols->GetModuleParameters(1, &pModule->Base, 0, &ModuleParams))
|
|
{
|
|
CString sSymbolType = Parse(ModuleParams.SymbolType, SymbolTypeValues);
|
|
CString sFlags = Parse(ModuleParams.Flags, ModuleFlags);
|
|
PRINT("Symbol Type %s, Flags %s", sSymbolType.GetBuffer(), sFlags.GetBuffer());
|
|
// timestamp from 1.1.1970
|
|
__time32_t timestamp = ModuleParams.TimeDateStamp;
|
|
tm *timer = _gmtime32(×tamp);
|
|
strftime(buffer, bufferSize, "%d %b %Y %H:%M:%S UTC", timer);
|
|
//PRINT("Checksum: %X", ModuleParams.Checksum);
|
|
PRINT("Time Stamp: %s(%X)", buffer, ModuleParams.TimeDateStamp);
|
|
}
|
|
}
|
|
free(buffer);
|
|
return bLoaded;
|
|
}
|
|
|
|
void tDumpParser::ProcessDumpFile()
|
|
{
|
|
ULONG SymbolOptions;
|
|
BOOL bSymbols;
|
|
CString s;
|
|
tModule module;
|
|
module.name = "netkvm";
|
|
if (S_OK == DebugSymbols->GetSymbolOptions(&SymbolOptions))
|
|
{
|
|
s = Parse(SymbolOptions, SymbolOptionsFlags);
|
|
PRINT("Symbol options %s", s.GetBuffer());
|
|
}
|
|
|
|
s = GetProperty(espSymbolPath);
|
|
PRINT("Symbol path:%s", s.GetBuffer());
|
|
s = GetProperty(espSystemVersion);
|
|
PRINT("System version:%s", s.GetBuffer());
|
|
s = GetProperty(espSystemTime);
|
|
PRINT("Crash time:%s", s.GetBuffer());
|
|
bSymbols = CheckLoadedSymbols(&module);
|
|
FindOurTaggedCrashData(bSymbols);
|
|
}
|
|
|
|
CString tDumpParser::GetProperty(eSystemProperty Prop)
|
|
{
|
|
CString s;
|
|
switch (Prop)
|
|
{
|
|
case espSymbolPath:
|
|
if (DebugSymbols)
|
|
{
|
|
ULONG maxlen = 1024, len;
|
|
char *buf = (char *)malloc(maxlen);
|
|
*buf = 0;
|
|
DebugSymbols->GetSymbolPath(buf, maxlen, &len);
|
|
s = buf;
|
|
free(buf);
|
|
}
|
|
break;
|
|
case espSystemVersion:
|
|
if (Control)
|
|
{
|
|
ULONG platform, major, minor, sp, buildsize = 128, buildused, procs = 0;
|
|
char *buf = (char *)malloc(buildsize);
|
|
*buf = 0;
|
|
BOOL Is64 = Control->IsPointer64Bit() == S_OK;
|
|
CString sSP;
|
|
Control->GetNumberProcessors(&procs);
|
|
Control->GetSystemVersion(&platform, &major, &minor, NULL, 0, NULL, &sp, buf, buildsize, &buildused);
|
|
if (sp) sSP.Format("(SP%d.%d)", sp >> 8, sp & 0xff);
|
|
s.Format("(%X)%d%s%s,%s,%d CPU",
|
|
major, minor, sSP.GetBuffer(), Is64 ? "(64-bit)" : "", buf, procs);
|
|
free(buf);
|
|
}
|
|
break;
|
|
case espSystemTime:
|
|
if (Control)
|
|
{
|
|
ULONG ulSecondSince1970 = 0, ulUpTime = 0;
|
|
Control->GetCurrentTimeDate(&ulSecondSince1970);
|
|
Control->GetCurrentSystemUpTime(&ulUpTime);
|
|
s = "Unknown";
|
|
if (ulSecondSince1970)
|
|
{
|
|
char buffer[256] = {0};
|
|
ULONG days, hours, min, sec, rem;
|
|
days = ulUpTime / (60*60*24);
|
|
rem = ulUpTime - days * 60*60*24;
|
|
hours = rem / (60*60);
|
|
rem = rem - hours * 60 * 60;
|
|
min = rem / 60;
|
|
rem = rem - min * 60;
|
|
sec = rem;
|
|
__time32_t timestamp = ulSecondSince1970;
|
|
tm *timer = _localtime32(×tamp);
|
|
strftime(buffer, sizeof(buffer), "%b %d %H:%M:%S %Y(Local)", timer);
|
|
s.Format("%s (Up time %d:%d:%d:%d)", buffer, days, hours, min, sec);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
#define UNDER_DEBUGGING
|
|
#endif
|
|
|
|
int ParseDumpFile(int argc, TCHAR* argv[])
|
|
{
|
|
if (argc == 2)
|
|
{
|
|
CString s = argv[1];
|
|
s += ".txt";
|
|
FILE *f = fopen(s.GetBuffer(), "w+t");
|
|
if (f) outf = f;
|
|
tDumpParser Parser;
|
|
#ifdef UNDER_DEBUGGING
|
|
fputs("UNDER_DEBUGGING, the output is redirected to debugger", f);
|
|
#endif
|
|
if (!Parser.LoadFile(argv[1])) PRINT("Failed to load dump file %s", argv[1]);
|
|
if (f) fclose(f);
|
|
}
|
|
else
|
|
{
|
|
PRINT("%s", SessionStatusValues[0].name);
|
|
}
|
|
return 0;
|
|
}
|