mirror of
https://github.com/arehnman/yttrium-virtio-gpu.git
synced 2026-10-31 02:05:04 +00:00
Also in case there is no tagged data in the dump. Signed-off-by: Yuri Benditovich <[email protected]>
1168 lines
42 KiB
C++
1168 lines
42 KiB
C++
/*
|
|
* 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;
|
|
|
|
typedef CString (*TimeStampConversionFunc)(LONGLONG BaseTime, LARGE_INTEGER TimeStamp);
|
|
|
|
static CString PresentKernelSystemTime(LONGLONG BaseTime, LARGE_INTEGER TimeStamp)
|
|
{
|
|
FILETIME ft;
|
|
SYSTEMTIME st;
|
|
ft.dwLowDateTime = TimeStamp.LowPart;
|
|
ft.dwHighDateTime = TimeStamp.HighPart;
|
|
FileTimeToSystemTime(&ft, &st);
|
|
CString s;
|
|
s.Format("%02d.%02d.%02d.%03d", st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
|
|
return s;
|
|
}
|
|
|
|
static CString PresentTimeDiffInMicros(LONGLONG BaseTime, LARGE_INTEGER TimeStamp)
|
|
{
|
|
LONGLONG diff = (BaseTime - TimeStamp.QuadPart) / 10;
|
|
CString s;
|
|
s.Format("%06d.%06d", (LONG)(diff / 1000000), (LONG)(diff % 1000000));
|
|
return s;
|
|
}
|
|
|
|
TimeStampConversionFunc func = PresentTimeDiffInMicros;
|
|
const char *conv = "diff in micros";
|
|
|
|
// #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;
|
|
}
|
|
|
|
// clang-format off
|
|
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 ",
|
|
"NBLDestructor ",
|
|
"SendCompleteChain ",
|
|
"SendDone ",
|
|
};
|
|
// clang-format on
|
|
|
|
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(".");
|
|
DebugSymbols->AppendSymbolPath("C:\\ProgramData\\dbg\\sym");
|
|
DebugSymbols->AppendSymbolPath("C:\\Symbols");
|
|
}
|
|
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();
|
|
bool 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;
|
|
}
|
|
|
|
bool ResolveSymbol(LPCSTR Name, ULONG &Size, CString &Type, ULONG64 &Offset)
|
|
{
|
|
CString sFullName;
|
|
ULONG64 moduleBase;
|
|
ULONG typeId;
|
|
HRESULT result;
|
|
LPCSTR moduleName = "netkvm";
|
|
char buffer[1024];
|
|
if (strchr(Name, '!'))
|
|
{
|
|
sFullName = Name;
|
|
}
|
|
else
|
|
{
|
|
sFullName.Format("%s!%s", moduleName, Name);
|
|
}
|
|
result = DebugSymbols->GetOffsetByName(sFullName, &Offset);
|
|
if (FAILED(result))
|
|
{
|
|
PRINT("Can't find %s, error %X\n", sFullName.GetString(), result);
|
|
return false;
|
|
}
|
|
result = DebugSymbols->GetSymbolTypeId(sFullName, &typeId, &moduleBase);
|
|
if (FAILED(result))
|
|
{
|
|
Type = "<unknown>";
|
|
return true;
|
|
}
|
|
result = DebugSymbols->GetTypeName(moduleBase, typeId, buffer, sizeof(buffer), nullptr);
|
|
if (FAILED(result))
|
|
{
|
|
Type = "<hard to say>";
|
|
}
|
|
else
|
|
{
|
|
Type = buffer;
|
|
}
|
|
result = DebugSymbols->GetTypeSize(moduleBase, typeId, &Size);
|
|
if (FAILED(result))
|
|
{
|
|
PRINT("Can't get size of %s, error %X\n", Name, result);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
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)
|
|
{
|
|
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 [%s] x%08X x%08X x%08X %I64X",
|
|
phist[Index].uProcessor,
|
|
phist[Index].uIRQL,
|
|
sOp.GetBuffer(),
|
|
phist[Index].Context,
|
|
func(basetime, phist[Index].TimeStamp).GetString(),
|
|
phist[Index].lParam2,
|
|
phist[Index].lParam3,
|
|
phist[Index].lParam4,
|
|
phist[Index].pParam1);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// historySize - number of entries in history
|
|
// when parsinf dump:
|
|
// basetime - time of crash
|
|
// Index - index in the middle of the table (currentIndex)
|
|
// when parsing file
|
|
// basetime = 0
|
|
// Index = -1
|
|
static void ParseHistoryData(LONGLONG basetime, tBugCheckHistoryDataEntry *phist, ULONG historySize, LONG Index)
|
|
{
|
|
if (Index < 0)
|
|
{
|
|
Index = 0;
|
|
// find start index (where we have a break of timestamp) &
|
|
// basetime (maximum timestamp in the table)
|
|
ULONGLONG prev = phist->TimeStamp.QuadPart;
|
|
for (int n = 0; n < (LONG)historySize; n++)
|
|
{
|
|
ULONGLONG cur = phist[n].TimeStamp.QuadPart;
|
|
LONGLONG diff = LONGLONG(cur - prev);
|
|
if (diff < 0)
|
|
{
|
|
Index = n;
|
|
basetime = prev;
|
|
PRINT("Found current history entry at %d", n);
|
|
break;
|
|
}
|
|
prev = cur;
|
|
}
|
|
}
|
|
|
|
ParseHistoryEntry(NULL, NULL, 0);
|
|
LONG n;
|
|
for (n = Index; n < (LONG)historySize; n++)
|
|
{
|
|
ParseHistoryEntry(basetime, phist, n);
|
|
}
|
|
for (n = 0; n < Index; n++)
|
|
{
|
|
ParseHistoryEntry(basetime, phist, n);
|
|
}
|
|
}
|
|
|
|
void tDumpParser::ParseCrashData(tBugCheckStaticDataHeader *ph, ULONG64 databuffer, ULONG bytesRead, BOOL bWithSymbols)
|
|
{
|
|
UINT i;
|
|
|
|
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);
|
|
|
|
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;
|
|
ParseHistoryData(ph->qCrashTime.QuadPart, phist, pd->SizeOfHistory, 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)
|
|
{
|
|
PRINT(PRINT_SEPARATOR);
|
|
auto pd0 = &pd->StaticDataV0;
|
|
if (pd0->SizeOfHistory > 2)
|
|
{
|
|
PRINT("History: version %d, %d entries of %d",
|
|
pd0->HistoryDataVersion,
|
|
pd0->SizeOfHistory,
|
|
pd0->SizeOfHistoryEntry);
|
|
tBugCheckHistoryDataEntry_V1 *phist = (tBugCheckHistoryDataEntry *)(pd0->HistoryData - databuffer +
|
|
(PUCHAR)ph);
|
|
ParseHistoryData(ph->qCrashTime.QuadPart, phist, pd0->SizeOfHistory, -1);
|
|
}
|
|
else
|
|
{
|
|
PRINT("History records are not available");
|
|
}
|
|
PRINT(PRINT_SEPARATOR);
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool tDumpParser::FindOurTaggedCrashData(BOOL bWithSymbols)
|
|
{
|
|
bool bFound = false;
|
|
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");
|
|
bFound = true;
|
|
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))
|
|
{
|
|
ParseCrashData(ph, bcdl->Address, bytesRead, bWithSymbols);
|
|
}
|
|
}
|
|
free(databuffer);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
DataSpaces->EndEnumTagged(h);
|
|
}
|
|
return bFound;
|
|
}
|
|
|
|
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);
|
|
if (!FindOurTaggedCrashData(bSymbols) && bSymbols)
|
|
{
|
|
// retrieve entire netkvm!BugCheckData
|
|
ULONG size, bytesRead;
|
|
ULONG64 addr;
|
|
CString type;
|
|
if (ResolveSymbol("BugCheckData", size, type, addr) && size)
|
|
{
|
|
PVOID databuffer = malloc(size);
|
|
if (databuffer)
|
|
{
|
|
if (S_OK == DataSpaces->ReadVirtual(addr, databuffer, size, &bytesRead))
|
|
{
|
|
tBugCheckStaticDataHeader *ph = (tBugCheckStaticDataHeader *)databuffer;
|
|
PRINT("Retrieved %d bytes of data", bytesRead);
|
|
if (bytesRead >= sizeof(tBugCheckStaticDataHeader))
|
|
{
|
|
ParseCrashData(ph, addr, bytesRead, true);
|
|
}
|
|
}
|
|
free(databuffer);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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
|
|
|
|
static bool IsDumpFile(const CString &Name)
|
|
{
|
|
auto dot = Name.ReverseFind('.');
|
|
if (dot < 0)
|
|
{
|
|
return false;
|
|
}
|
|
CString s = Name.GetString() + dot + 1;
|
|
return !s.CompareNoCase(TEXT("dmp"));
|
|
}
|
|
|
|
int ParseDumpFile(int argc, TCHAR *argv[])
|
|
{
|
|
func = PresentTimeDiffInMicros;
|
|
conv = "diff in micros";
|
|
bool bDoHistory = false;
|
|
if (argc >= 2)
|
|
{
|
|
CString s = argv[1];
|
|
bDoHistory = !IsDumpFile(s);
|
|
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 (argc > 2)
|
|
{
|
|
switch (argv[2][0])
|
|
{
|
|
case 't':
|
|
func = PresentKernelSystemTime;
|
|
conv = "system time";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
PRINT("Converting time as time as %s", conv);
|
|
}
|
|
if (bDoHistory)
|
|
{
|
|
PRINT("Parsing data file %s", argv[1]);
|
|
FILE *fdata = fopen(argv[1], "rb");
|
|
if (!fdata)
|
|
{
|
|
PRINT("Failed to open file %s", argv[1]);
|
|
return ERROR_FILE_NOT_FOUND;
|
|
}
|
|
// get file size and load the file into memory
|
|
ULONG size = 0;
|
|
PVOID buffer = NULL;
|
|
fseek(fdata, 0, SEEK_END);
|
|
long offset = ftell(fdata);
|
|
PRINT("Got offset %d", offset);
|
|
fseek(fdata, 0, SEEK_SET);
|
|
if (offset > 0)
|
|
{
|
|
size = offset;
|
|
}
|
|
if (!size)
|
|
{
|
|
PRINT("File %s is empty", argv[1]);
|
|
return ERROR_FILE_CORRUPT;
|
|
}
|
|
if (size % sizeof(tBugCheckHistoryDataEntry))
|
|
{
|
|
PRINT("Size of %s is not valid", argv[1]);
|
|
return ERROR_FILE_CORRUPT;
|
|
}
|
|
buffer = malloc(size);
|
|
if (!buffer)
|
|
{
|
|
PRINT("Failed to allocate memory");
|
|
return ERROR_FILE_CORRUPT;
|
|
}
|
|
fread(buffer, 1, size, fdata);
|
|
size = size / sizeof(tBugCheckHistoryDataEntry);
|
|
PRINT("%d entries in the table", size);
|
|
ParseHistoryData(0, (tBugCheckHistoryDataEntry *)buffer, size, -1);
|
|
free(buffer);
|
|
fclose(fdata);
|
|
}
|
|
else if (!Parser.LoadFile(argv[1]))
|
|
{
|
|
PRINT("Failed to load dump file %s", argv[1]);
|
|
}
|
|
if (f)
|
|
{
|
|
fclose(f);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
puts("Arguments:");
|
|
puts(" <dump file>");
|
|
puts(" <history file> t|d for time conversion");
|
|
}
|
|
return 0;
|
|
}
|