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.
2327 lines
56 KiB
2327 lines
56 KiB
/*++
|
|
|
|
Copyright (c) 2000-2002 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
ext.cpp
|
|
|
|
Abstract:
|
|
|
|
Generic cross-platform and cross-processor extensions.
|
|
|
|
Environment:
|
|
|
|
User Mode
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
#include <ntverp.h>
|
|
#include <time.h>
|
|
#include <lm.h>
|
|
|
|
#include <strsafe.h>
|
|
|
|
extern CTriager *g_pTriager;
|
|
|
|
//
|
|
// Valid for the lifetime of the debug session.
|
|
//
|
|
|
|
WINDBG_EXTENSION_APIS ExtensionApis;
|
|
ULONG g_TargetMachine;
|
|
BOOL Connected;
|
|
ULONG g_TargetClass;
|
|
ULONG g_TargetQualifier;
|
|
ULONG g_TargetBuild;
|
|
ULONG g_TargetPlatform;
|
|
|
|
//
|
|
// Valid only during an extension API call
|
|
//
|
|
|
|
PDEBUG_ADVANCED g_ExtAdvanced;
|
|
PDEBUG_CLIENT g_ExtClient;
|
|
PDEBUG_DATA_SPACES3 g_ExtData;
|
|
PDEBUG_REGISTERS g_ExtRegisters;
|
|
PDEBUG_SYMBOLS2 g_ExtSymbols;
|
|
PDEBUG_SYSTEM_OBJECTS3 g_ExtSystem;
|
|
// Version 3 Interfaces
|
|
PDEBUG_CONTROL3 g_ExtControl;
|
|
|
|
// Queries for all debugger interfaces.
|
|
extern "C" HRESULT
|
|
ExtQuery(PDEBUG_CLIENT Client)
|
|
{
|
|
HRESULT Status;
|
|
|
|
if ((Status = Client->QueryInterface(__uuidof(IDebugAdvanced),
|
|
(void **)&g_ExtAdvanced)) != S_OK)
|
|
{
|
|
goto Fail;
|
|
}
|
|
if ((Status = Client->QueryInterface(__uuidof(IDebugDataSpaces3),
|
|
(void **)&g_ExtData)) != S_OK)
|
|
{
|
|
goto Fail;
|
|
}
|
|
if ((Status = Client->QueryInterface(__uuidof(IDebugRegisters),
|
|
(void **)&g_ExtRegisters)) != S_OK)
|
|
{
|
|
goto Fail;
|
|
}
|
|
if ((Status = Client->QueryInterface(__uuidof(IDebugSymbols),
|
|
(void **)&g_ExtSymbols)) != S_OK)
|
|
{
|
|
goto Fail;
|
|
}
|
|
if ((Status = Client->QueryInterface(__uuidof(IDebugSystemObjects3),
|
|
(void **)&g_ExtSystem)) != S_OK)
|
|
{
|
|
goto Fail;
|
|
}
|
|
if ((Status = Client->QueryInterface(__uuidof(IDebugControl3),
|
|
(void **)&g_ExtControl)) != S_OK)
|
|
{
|
|
goto Fail;
|
|
}
|
|
|
|
g_ExtClient = Client;
|
|
|
|
return S_OK;
|
|
|
|
Fail:
|
|
ExtRelease();
|
|
return Status;
|
|
}
|
|
|
|
// Cleans up all debugger interfaces.
|
|
void
|
|
ExtRelease(void)
|
|
{
|
|
g_ExtClient = NULL;
|
|
EXT_RELEASE(g_ExtAdvanced);
|
|
EXT_RELEASE(g_ExtData);
|
|
EXT_RELEASE(g_ExtRegisters);
|
|
EXT_RELEASE(g_ExtSymbols);
|
|
EXT_RELEASE(g_ExtSystem);
|
|
EXT_RELEASE(g_ExtControl);
|
|
}
|
|
|
|
// Normal output.
|
|
void __cdecl
|
|
ExtOut(PCSTR Format, ...)
|
|
{
|
|
va_list Args;
|
|
|
|
va_start(Args, Format);
|
|
g_ExtControl->OutputVaList(DEBUG_OUTPUT_NORMAL, Format, Args);
|
|
va_end(Args);
|
|
}
|
|
|
|
// Error output.
|
|
void __cdecl
|
|
ExtErr(PCSTR Format, ...)
|
|
{
|
|
va_list Args;
|
|
|
|
va_start(Args, Format);
|
|
g_ExtControl->OutputVaList(DEBUG_OUTPUT_ERROR, Format, Args);
|
|
va_end(Args);
|
|
}
|
|
|
|
// Warning output.
|
|
void __cdecl
|
|
ExtWarn(PCSTR Format, ...)
|
|
{
|
|
va_list Args;
|
|
|
|
va_start(Args, Format);
|
|
g_ExtControl->OutputVaList(DEBUG_OUTPUT_WARNING, Format, Args);
|
|
va_end(Args);
|
|
}
|
|
|
|
// Verbose output.
|
|
void __cdecl
|
|
ExtVerb(PCSTR Format, ...)
|
|
{
|
|
va_list Args;
|
|
|
|
va_start(Args, Format);
|
|
g_ExtControl->OutputVaList(DEBUG_OUTPUT_VERBOSE, Format, Args);
|
|
va_end(Args);
|
|
}
|
|
|
|
|
|
extern "C"
|
|
HRESULT
|
|
CALLBACK
|
|
DebugExtensionInitialize(PULONG Version, PULONG Flags)
|
|
{
|
|
IDebugClient *DebugClient;
|
|
PDEBUG_CONTROL DebugControl;
|
|
HRESULT Hr;
|
|
|
|
*Version = DEBUG_EXTENSION_VERSION(1, 0);
|
|
*Flags = 0;
|
|
|
|
// Ignore errors as there are no critical routines required.
|
|
InitDynamicCalls(&g_NtDllCallsDesc);
|
|
|
|
if ((Hr = DebugCreate(__uuidof(IDebugClient),
|
|
(void **)&DebugClient)) != S_OK)
|
|
{
|
|
return Hr;
|
|
}
|
|
if ((Hr = DebugClient->QueryInterface(__uuidof(IDebugControl),
|
|
(void **)&DebugControl)) != S_OK)
|
|
{
|
|
return Hr;
|
|
}
|
|
|
|
ExtensionApis.nSize = sizeof (ExtensionApis);
|
|
if ((Hr = DebugControl->GetWindbgExtensionApis64(&ExtensionApis)) != S_OK) {
|
|
return Hr;
|
|
}
|
|
|
|
DebugControl->Release();
|
|
DebugClient->Release();
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
extern "C"
|
|
void
|
|
CALLBACK
|
|
DebugExtensionNotify(ULONG Notify, ULONG64 Argument)
|
|
{
|
|
//
|
|
// The first time we actually connect to a target, get the page size
|
|
//
|
|
|
|
if ((Notify == DEBUG_NOTIFY_SESSION_ACCESSIBLE) && (!Connected))
|
|
{
|
|
IDebugClient *DebugClient;
|
|
PDEBUG_DATA_SPACES DebugDataSpaces;
|
|
PDEBUG_CONTROL DebugControl;
|
|
HRESULT Hr;
|
|
ULONG64 Page;
|
|
|
|
if ((Hr = DebugCreate(__uuidof(IDebugClient),
|
|
(void **)&DebugClient)) == S_OK)
|
|
{
|
|
//
|
|
// Get the architecture type.
|
|
//
|
|
|
|
if ((Hr = DebugClient->QueryInterface(__uuidof(IDebugControl),
|
|
(void **)&DebugControl)) == S_OK)
|
|
{
|
|
if ((Hr = DebugControl->GetActualProcessorType(
|
|
&g_TargetMachine)) == S_OK)
|
|
{
|
|
Connected = TRUE;
|
|
}
|
|
ULONG MajorVer, SrvPack;
|
|
if ((Hr = DebugControl->GetSystemVersion(
|
|
&g_TargetPlatform, &MajorVer,
|
|
&g_TargetBuild, NULL,
|
|
0, NULL,
|
|
&SrvPack, NULL,
|
|
0, NULL)) == S_OK) {
|
|
}
|
|
|
|
ULONG Qualifier;
|
|
if ((Hr = DebugControl->GetDebuggeeType(&g_TargetClass, &g_TargetQualifier)) == S_OK)
|
|
{
|
|
}
|
|
|
|
ULONG EventType, EventProcess, EventThread;
|
|
if (DebugControl->GetLastEventInformation(&EventType, &EventProcess, &EventThread,
|
|
NULL, 0, NULL, NULL, 0, NULL) == S_OK)
|
|
{
|
|
|
|
}
|
|
DebugControl->Release();
|
|
}
|
|
|
|
if (g_pTriager == NULL)
|
|
{
|
|
g_pTriager = new CTriager();
|
|
}
|
|
|
|
DebugClient->Release();
|
|
}
|
|
}
|
|
|
|
|
|
if (Notify == DEBUG_NOTIFY_SESSION_INACTIVE)
|
|
{
|
|
Connected = FALSE;
|
|
g_TargetMachine = 0;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
extern "C"
|
|
void
|
|
CALLBACK
|
|
DebugExtensionUninitialize(void)
|
|
{
|
|
if (g_pTriager)
|
|
{
|
|
delete g_pTriager;
|
|
g_pTriager = NULL;
|
|
}
|
|
UnInitializeDatabaseHandlers(TRUE);
|
|
return;
|
|
}
|
|
|
|
|
|
DllInit(
|
|
HANDLE hModule,
|
|
DWORD dwReason,
|
|
DWORD dwReserved
|
|
)
|
|
{
|
|
switch (dwReason) {
|
|
case DLL_THREAD_ATTACH:
|
|
break;
|
|
|
|
case DLL_THREAD_DETACH:
|
|
break;
|
|
|
|
case DLL_PROCESS_DETACH:
|
|
DebugExtensionUninitialize();
|
|
break;
|
|
|
|
case DLL_PROCESS_ATTACH:
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
wchr2ansi(
|
|
PWCHAR wstr,
|
|
PCHAR astr
|
|
)
|
|
{
|
|
do
|
|
{
|
|
*astr++ = (CHAR)*wstr++;
|
|
} while (*wstr);
|
|
}
|
|
|
|
void
|
|
ansi2wchr(
|
|
const PCHAR astr,
|
|
PWCHAR wstr
|
|
)
|
|
// both could point to same buffer
|
|
{
|
|
ULONG i = strlen(astr);
|
|
do
|
|
{
|
|
wstr[i] = astr[i];
|
|
} while (i--);
|
|
}
|
|
|
|
LegacyCommands()
|
|
{
|
|
dprintf("\n");
|
|
dprintf(" !cxr !exr, and !trap have been replaced with the new built-in debugger \n");
|
|
dprintf(" commands .cxr, .exr, .trap and .tss. There is also a new \".thread\" command. \n");
|
|
dprintf("\n");
|
|
dprintf(" These new commands no longer require symbols to work correctly.\n");
|
|
dprintf("\n");
|
|
dprintf(" Another change that comes with these new commands is that they actually\n");
|
|
dprintf(" change the internal state of the debugger engine \"permanently\" (until\n");
|
|
dprintf(" reverted). Any other debugger or extension command issued after the \n");
|
|
dprintf(" \".cxr\", \".trap\" or \".thread\" command will be executed with the new context.\n");
|
|
dprintf("\n");
|
|
dprintf(" For example, commands such as stack walk (\"k\", \"kb\", \"kv\" ), \"r\" and \"dv\"\n");
|
|
dprintf(" (show local variables) will all work based off the new context that was\n");
|
|
dprintf(" supplied by \".cxr\", \".trap\" or \".thread\".\n");
|
|
dprintf("\n");
|
|
dprintf(" \".cxr\", \".trap\" and \".thread\" also apply to WinDBG:\n");
|
|
dprintf(" using \".cxr\" , \".trap\" and \".thread\" will automatically show you the\n");
|
|
dprintf(" new stack in the WinDBG stack window and allow you to click on a frame and\n");
|
|
dprintf(" see local variables and source code (if source is available).\n");
|
|
dprintf("\n");
|
|
dprintf(" \".cxr\", \".trap\" or \".thread\" with no parameters will give you back the\n");
|
|
dprintf(" default context that was in effect before the command was executed.\n");
|
|
dprintf("\n");
|
|
dprintf(" For example, to exactly duplicate \n");
|
|
dprintf("\n");
|
|
dprintf(" !cxr <foo> !trap <foo>\n");
|
|
dprintf(" !kb !kb\n");
|
|
dprintf("\n");
|
|
dprintf(" you would now use\n");
|
|
dprintf("\n");
|
|
dprintf(" .cxr <foo> .trap <foo>\n");
|
|
dprintf(" kb kb\n");
|
|
dprintf(" .cxr .trap\n");
|
|
dprintf("\n");
|
|
return S_OK;
|
|
}
|
|
|
|
DECLARE_API ( cxr )
|
|
{
|
|
LegacyCommands();
|
|
return S_OK;
|
|
}
|
|
|
|
DECLARE_API ( exr )
|
|
{
|
|
LegacyCommands();
|
|
return S_OK;
|
|
}
|
|
|
|
DECLARE_API ( trap )
|
|
{
|
|
LegacyCommands();
|
|
return S_OK;
|
|
}
|
|
|
|
DECLARE_API ( tss )
|
|
{
|
|
dprintf("\n");
|
|
dprintf(" !tss has been replaced with the new built-in debugger command .tss.\n");
|
|
dprintf("\n");
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
DECLARE_API ( sel )
|
|
{
|
|
dprintf("\n");
|
|
dprintf(" !sel has been replaced with the built-in debugger command dg.\n");
|
|
dprintf("\n");
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
DECLARE_API( cpuid )
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Print out the version number for all CPUs, if available.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG64 Val;
|
|
BOOL First = TRUE;
|
|
ULONG Processor;
|
|
ULONG NumProcessors;
|
|
DEBUG_PROCESSOR_IDENTIFICATION_ALL IdAll;
|
|
ULONG Mhz;
|
|
|
|
INIT_API();
|
|
|
|
if (g_ExtControl->GetNumberProcessors(&NumProcessors) != S_OK)
|
|
{
|
|
NumProcessors = 0;
|
|
}
|
|
|
|
if (GetExpressionEx(args, &Val, &args))
|
|
{
|
|
//
|
|
// The user specified a procesor number.
|
|
//
|
|
|
|
Processor = (ULONG)Val;
|
|
if (Processor >= NumProcessors)
|
|
{
|
|
dprintf("Invalid processor number specified\n");
|
|
}
|
|
else
|
|
{
|
|
NumProcessors = Processor + 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Enumerate all the processors
|
|
//
|
|
|
|
Processor = 0;
|
|
}
|
|
|
|
while (Processor < NumProcessors)
|
|
{
|
|
if (g_ExtData->
|
|
ReadProcessorSystemData(Processor,
|
|
DEBUG_DATA_PROCESSOR_IDENTIFICATION,
|
|
&IdAll, sizeof(IdAll), NULL) != S_OK)
|
|
{
|
|
dprintf("Unable to get information for processor %d\n",
|
|
Processor);
|
|
Processor++;
|
|
continue;
|
|
}
|
|
|
|
if (g_ExtData->
|
|
ReadProcessorSystemData(Processor,
|
|
DEBUG_DATA_PROCESSOR_SPEED,
|
|
&Mhz, sizeof(Mhz), NULL) != S_OK)
|
|
{
|
|
Mhz = 0;
|
|
}
|
|
|
|
switch( g_TargetMachine )
|
|
{
|
|
case IMAGE_FILE_MACHINE_I386:
|
|
if (First)
|
|
{
|
|
dprintf("CP F/M/S Manufacturer");
|
|
if (Mhz)
|
|
{
|
|
dprintf(" MHz");
|
|
}
|
|
dprintf("\n");
|
|
}
|
|
|
|
dprintf("%2d %2d,%d,%-2d %-16.16s",
|
|
Processor,
|
|
IdAll.X86.Family,
|
|
IdAll.X86.Model,
|
|
IdAll.X86.Stepping,
|
|
IdAll.X86.VendorString);
|
|
if (Mhz)
|
|
{
|
|
dprintf("%4d", Mhz);
|
|
}
|
|
dprintf("\n");
|
|
|
|
break;
|
|
|
|
case IMAGE_FILE_MACHINE_AMD64:
|
|
|
|
if (First)
|
|
{
|
|
dprintf("CP F/M/S Manufacturer");
|
|
if (Mhz)
|
|
{
|
|
dprintf(" MHz");
|
|
}
|
|
dprintf("\n");
|
|
}
|
|
|
|
dprintf("%2d %2d,%d,%-2d %-16.16s",
|
|
Processor,
|
|
IdAll.Amd64.Family,
|
|
IdAll.Amd64.Model,
|
|
IdAll.Amd64.Stepping,
|
|
IdAll.Amd64.VendorString);
|
|
if (Mhz)
|
|
{
|
|
dprintf("%4d", Mhz);
|
|
}
|
|
dprintf("\n");
|
|
|
|
|
|
break;
|
|
|
|
case IMAGE_FILE_MACHINE_IA64:
|
|
if (First)
|
|
{
|
|
dprintf("CP M/R/F/A Manufacturer");
|
|
if (Mhz)
|
|
{
|
|
dprintf(" MHz");
|
|
}
|
|
dprintf("\n");
|
|
}
|
|
|
|
dprintf("%2d %d,%d,%d,%d %-16.16s",
|
|
Processor,
|
|
IdAll.Ia64.Model,
|
|
IdAll.Ia64.Revision,
|
|
IdAll.Ia64.Family,
|
|
IdAll.Ia64.ArchRev,
|
|
IdAll.Ia64.VendorString);
|
|
if (Mhz)
|
|
{
|
|
dprintf("%4d", Mhz);
|
|
}
|
|
dprintf("\n");
|
|
|
|
break;
|
|
|
|
default:
|
|
dprintf("Not supported for this target machine: %ld\n",
|
|
g_TargetMachine);
|
|
Processor = NumProcessors;
|
|
break;
|
|
}
|
|
|
|
Processor++;
|
|
First = FALSE;
|
|
}
|
|
|
|
EXIT_API();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
PrintTargetString(
|
|
BOOL Unicode,
|
|
PDEBUG_CLIENT Client,
|
|
LPCSTR args
|
|
)
|
|
{
|
|
ULONG64 AddrString;
|
|
ULONG64 Displacement;
|
|
STRING32 String;
|
|
UNICODE_STRING UnicodeString;
|
|
ULONG64 AddrBuffer;
|
|
CHAR Symbol[1024];
|
|
LPSTR StringData;
|
|
HRESULT hResult;
|
|
BOOL b;
|
|
|
|
|
|
AddrString = GetExpression(args);
|
|
if (!AddrString)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
//
|
|
// Get the symbolic name of the string
|
|
//
|
|
|
|
GetSymbol(AddrString, Symbol, &Displacement);
|
|
|
|
//
|
|
// Read the string from the debuggees address space into our
|
|
// own.
|
|
|
|
b = ReadMemory(AddrString, &String, sizeof(String), NULL);
|
|
|
|
if ( !b )
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
INIT_API();
|
|
|
|
if (IsPtr64())
|
|
{
|
|
hResult = g_ExtData->ReadPointersVirtual(1,
|
|
AddrString + FIELD_OFFSET(STRING64, Buffer),
|
|
&AddrBuffer);
|
|
}
|
|
else
|
|
{
|
|
hResult = g_ExtData->ReadPointersVirtual(1,
|
|
AddrString + FIELD_OFFSET(STRING32, Buffer),
|
|
&AddrBuffer);
|
|
}
|
|
|
|
EXIT_API();
|
|
|
|
if (hResult != S_OK)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
StringData = (LPSTR) LocalAlloc(LMEM_ZEROINIT,
|
|
String.Length + sizeof(UNICODE_NULL));
|
|
|
|
if (!StringData)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
dprintf("String(%d,%d)", String.Length, String.MaximumLength);
|
|
if (Symbol[0])
|
|
{
|
|
dprintf(" %s+%p", Symbol, Displacement);
|
|
}
|
|
|
|
b = ReadMemory(AddrBuffer, StringData, String.Length, NULL);
|
|
|
|
if ( b )
|
|
{
|
|
if (Unicode)
|
|
{
|
|
ANSI_STRING AnsiString;
|
|
|
|
UnicodeString.Buffer = (PWSTR)StringData;
|
|
UnicodeString.Length = String.Length;
|
|
UnicodeString.MaximumLength = String.Length+sizeof(UNICODE_NULL);
|
|
|
|
RtlUnicodeStringToAnsiString(&AnsiString, &UnicodeString,TRUE);
|
|
|
|
dprintf(" at %p: %s\n", AddrString, AnsiString.Buffer);
|
|
|
|
RtlFreeAnsiString(&AnsiString);
|
|
}
|
|
else
|
|
{
|
|
dprintf(" at %p: %s\n", AddrString, StringData);
|
|
}
|
|
|
|
LocalFree(StringData);
|
|
return S_OK;
|
|
}
|
|
else
|
|
{
|
|
LocalFree(StringData);
|
|
return E_FAIL;
|
|
}
|
|
}
|
|
|
|
DECLARE_API( str )
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called to format and dump counted (ansi) string.
|
|
|
|
Arguments:
|
|
|
|
args - Address
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
return PrintTargetString(FALSE, Client, args);
|
|
}
|
|
|
|
DECLARE_API( ustr )
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called to format and dump counted (unicode) string.
|
|
|
|
Arguments:
|
|
|
|
args - Address
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
return PrintTargetString(TRUE, Client, args);
|
|
}
|
|
|
|
DECLARE_API( obja )
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called to format and dump an object attributes structure.
|
|
|
|
Arguments:
|
|
|
|
args - Address
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG64 AddrObja;
|
|
ULONG64 Displacement;
|
|
ULONG64 AddrString;
|
|
STRING32 String;
|
|
ULONG64 StrAddr = NULL;
|
|
CHAR Symbol[1024];
|
|
LPSTR StringData;
|
|
BOOL b;
|
|
ULONG Attr;
|
|
HRESULT hResult;
|
|
ULONG ObjectNameOffset;
|
|
ULONG AttrOffset;
|
|
ULONG StringOffset;
|
|
|
|
if (IsPtr64())
|
|
{
|
|
ObjectNameOffset = FIELD_OFFSET(OBJECT_ATTRIBUTES64, ObjectName);
|
|
AttrOffset = FIELD_OFFSET(OBJECT_ATTRIBUTES64, Attributes);
|
|
StringOffset = FIELD_OFFSET(STRING64, Buffer);
|
|
}
|
|
else
|
|
{
|
|
ObjectNameOffset = FIELD_OFFSET(OBJECT_ATTRIBUTES32, ObjectName);
|
|
AttrOffset = FIELD_OFFSET(OBJECT_ATTRIBUTES32, Attributes);
|
|
StringOffset = FIELD_OFFSET(STRING32, Buffer);
|
|
}
|
|
|
|
|
|
AddrObja = GetExpression(args);
|
|
if (!AddrObja)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
//
|
|
// Get the symbolic name of the Obja
|
|
//
|
|
|
|
GetSymbol(AddrObja, Symbol, &Displacement);
|
|
|
|
dprintf("Obja %s+%p at %p:\n", Symbol, Displacement, AddrObja);
|
|
|
|
|
|
INIT_API();
|
|
|
|
hResult = g_ExtData->ReadPointersVirtual(1,
|
|
AddrObja + ObjectNameOffset,
|
|
&AddrString);
|
|
|
|
if ((hResult == S_OK) && AddrString)
|
|
{
|
|
b = ReadMemory(AddrString, &String, sizeof(String), NULL);
|
|
|
|
hResult = g_ExtData->ReadPointersVirtual(1,
|
|
AddrString + StringOffset,
|
|
&StrAddr);
|
|
}
|
|
|
|
EXIT_API();
|
|
|
|
|
|
if (!StrAddr)
|
|
{
|
|
dprintf("Could not read address of Object Name\n");
|
|
return E_FAIL;
|
|
}
|
|
|
|
StringData = (LPSTR)LocalAlloc(LMEM_ZEROINIT,
|
|
String.Length+sizeof(UNICODE_NULL));
|
|
|
|
if (StringData)
|
|
{
|
|
|
|
b = ReadMemory(StrAddr, StringData, String.Length, NULL);
|
|
|
|
if (b)
|
|
{
|
|
dprintf("\tName is %ws\n", StringData);
|
|
}
|
|
|
|
LocalFree(StringData);
|
|
}
|
|
|
|
b = ReadMemory(AddrObja + AttrOffset, &Attr, sizeof(Attr), NULL);
|
|
|
|
if (!b)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
if (Attr & OBJ_INHERIT )
|
|
{
|
|
dprintf("\tOBJ_INHERIT\n");
|
|
}
|
|
if (Attr & OBJ_PERMANENT )
|
|
{
|
|
dprintf("\tOBJ_PERMANENT\n");
|
|
}
|
|
if (Attr & OBJ_EXCLUSIVE )
|
|
{
|
|
dprintf("\tOBJ_EXCLUSIVE\n");
|
|
}
|
|
if (Attr & OBJ_CASE_INSENSITIVE )
|
|
{
|
|
dprintf("\tOBJ_CASE_INSENSITIVE\n");
|
|
}
|
|
if (Attr & OBJ_OPENIF )
|
|
{
|
|
dprintf("\tOBJ_OPENIF\n");
|
|
}
|
|
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
VOID
|
|
DecodeErrorForMessage(
|
|
PDEBUG_DECODE_ERROR pDecodeError
|
|
)
|
|
{
|
|
PSTR Text;
|
|
PSTR Source;
|
|
BOOL TreatAsStatus = pDecodeError->TreatAsStatus;
|
|
|
|
Text = FormatAnyStatus(pDecodeError->Code, NULL,
|
|
&TreatAsStatus, &Source);
|
|
pDecodeError->TreatAsStatus = TreatAsStatus;
|
|
CopyString(pDecodeError->Source, Source, sizeof(pDecodeError->Source));
|
|
CopyString(pDecodeError->Message, Text, sizeof(pDecodeError->Message));
|
|
}
|
|
|
|
VOID
|
|
DecodeError(
|
|
PSTR Banner,
|
|
ULONG Code,
|
|
BOOL TreatAsStatus
|
|
)
|
|
{
|
|
DEBUG_DECODE_ERROR Err;
|
|
|
|
Err.Code = Code;
|
|
Err.TreatAsStatus = TreatAsStatus;
|
|
DecodeErrorForMessage(&Err);
|
|
if (!TreatAsStatus)
|
|
{
|
|
dprintf("%s: (%s) %#x (%u) - %s\n",
|
|
Banner, Err.Source, Code, Code, Err.Message);
|
|
}
|
|
else
|
|
{
|
|
dprintf("%s: (%s) %#x - %s\n",
|
|
Banner, Err.Source, Code, Err.Message);
|
|
}
|
|
}
|
|
|
|
DECLARE_API( error )
|
|
{
|
|
BOOL TreatAsStatus = FALSE ;
|
|
ULONG64 err = 0;
|
|
|
|
if (GetExpressionEx( args, &err, &args ))
|
|
{
|
|
TreatAsStatus = (BOOL) GetExpression(args);
|
|
}
|
|
DecodeError( "Error code", (ULONG) err, TreatAsStatus );
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
typedef BOOL
|
|
(CALLBACK *PENUMERATE_UMODE_THREADS_CALLBACK)(
|
|
ULONG ThreadUserId,
|
|
PVOID UserContext
|
|
);
|
|
|
|
ULONG GetCurrentThreadUserID(void)
|
|
{
|
|
ULONG Id;
|
|
if (!g_ExtSystem) {
|
|
return 0;
|
|
}
|
|
|
|
if (g_ExtSystem->GetCurrentThreadId(&Id) != S_OK) {
|
|
return 0;
|
|
}
|
|
return Id;
|
|
}
|
|
|
|
BOOL
|
|
EnumerateUModeThreads(
|
|
PENUMERATE_UMODE_THREADS_CALLBACK Callback,
|
|
PVOID UserContext
|
|
)
|
|
{
|
|
ULONG CurrentThreadId;
|
|
ULONG ThreadId;
|
|
|
|
if (!g_ExtSystem) {
|
|
return FALSE;
|
|
}
|
|
|
|
// Remember thread we started with
|
|
if (g_ExtSystem->GetCurrentThreadId(&CurrentThreadId) != S_OK) {
|
|
return FALSE;
|
|
}
|
|
|
|
// Loop through all threads
|
|
for (ThreadId=0;;ThreadId++) {
|
|
|
|
// set ThreadId as current thread
|
|
if (g_ExtSystem->SetCurrentThreadId(ThreadId) != S_OK) {
|
|
// finished enumerateing threads
|
|
break;
|
|
}
|
|
|
|
// call the callback routine
|
|
if (!((*Callback)(ThreadId, UserContext))) {
|
|
// callback failed, break out
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
// Set current thread back to original value
|
|
g_ExtSystem->SetCurrentThreadId(CurrentThreadId);
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
DumpLastErrorForTeb(
|
|
ULONG64 Address
|
|
)
|
|
{
|
|
|
|
TEB Teb;
|
|
|
|
if (ReadMemory( (ULONG_PTR)Address,
|
|
&Teb,
|
|
sizeof(Teb),
|
|
NULL
|
|
)
|
|
) {
|
|
|
|
DecodeError( "LastErrorValue", Teb.LastErrorValue, FALSE );
|
|
|
|
DecodeError( "LastStatusValue", Teb.LastStatusValue, TRUE );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
dprintf("Unable to read TEB at %p\n", Address );
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
DumpCurrentThreadLastError(
|
|
ULONG CurrThreadID,
|
|
PVOID Context
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
THREAD_BASIC_INFORMATION ThreadInformation;
|
|
ULONGLONG Address = 0;
|
|
|
|
if (Context) {
|
|
dprintf("Last error for thread %lx:\n", CurrThreadID);
|
|
}
|
|
Address = GetExpression("@$teb");
|
|
|
|
if (Address) {
|
|
DumpLastErrorForTeb(Address);
|
|
} else {
|
|
dprintf("Unable to read thread %lx's TEB\n", CurrThreadID );
|
|
}
|
|
if (Context) {
|
|
dprintf("\n");
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
DECLARE_API( gle )
|
|
{
|
|
INIT_API();
|
|
|
|
if (!strcmp(args, "-all")) {
|
|
EnumerateUModeThreads(&DumpCurrentThreadLastError, Client);
|
|
} else {
|
|
DumpCurrentThreadLastError(GetCurrentThreadUserID(), NULL);
|
|
}
|
|
EXIT_API();
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
void
|
|
DispalyTime(
|
|
ULONG64 Time,
|
|
PCHAR TimeString
|
|
)
|
|
{
|
|
if (Time) {
|
|
ULONG seconds = (ULONG) Time;
|
|
ULONG minutes = seconds / 60;
|
|
ULONG hours = minutes / 60;
|
|
ULONG days = hours / 24;
|
|
|
|
dprintf("%s %d days %d:%02d:%02d \n",
|
|
TimeString,
|
|
days, hours%24, minutes%60, seconds%60);
|
|
}
|
|
}
|
|
|
|
extern PCHAR gTargetMode[], gAllOsTypes[];
|
|
#if 1
|
|
|
|
DECLARE_API( targetinfo )
|
|
{
|
|
TARGET_DEBUG_INFO TargetInfo;
|
|
EXT_TARGET_INFO GetTargetInfo;
|
|
INIT_API();
|
|
|
|
|
|
if (g_ExtControl->GetExtensionFunction(0, "GetTargetInfo", (FARPROC *)&GetTargetInfo) == S_OK) {
|
|
TargetInfo.SizeOfStruct = sizeof(TargetInfo);
|
|
if ((*GetTargetInfo)(Client, &TargetInfo) != S_OK) {
|
|
dprintf("GetTargetInfo failed\n");
|
|
} else {
|
|
const char * time;
|
|
|
|
dprintf("TargetInfo: ");
|
|
dprintf("%s\n", gTargetMode[ TargetInfo.Mode ]);
|
|
if ((time = ctime((time_t *) &TargetInfo.CrashTime)) != NULL) {
|
|
dprintf("Crash Time: %s", time);
|
|
}
|
|
if (TargetInfo.SysUpTime) {
|
|
DispalyTime(TargetInfo.SysUpTime,
|
|
"System Uptime:");
|
|
}
|
|
else
|
|
{
|
|
dprintf("System Uptime: not available\n");
|
|
}
|
|
if (TargetInfo.Mode == UserModeTarget) {
|
|
DispalyTime(TargetInfo.AppUpTime, "Process Uptime:");
|
|
}
|
|
if ((time = ctime((time_t *) &TargetInfo.EntryDate)) != NULL) {
|
|
dprintf("Entry Date: %s", time);
|
|
}
|
|
if (TargetInfo.OsInfo.Type) {
|
|
dprintf(gAllOsTypes[TargetInfo.OsInfo.Type]);
|
|
dprintf(" ");
|
|
}
|
|
// dprintf("OS Type %lx, Product %lx, Suite %lx\n",
|
|
// TargetInfo.OsInfo.Type, TargetInfo.OsInfo.ProductType,
|
|
// TargetInfo.OsInfo.Suite);
|
|
dprintf("%s, %s ",
|
|
TargetInfo.OsInfo.OsString,
|
|
TargetInfo.OsInfo.ServicePackString);
|
|
dprintf("Version %ld.%ld\n",
|
|
TargetInfo.OsInfo.Version.Major, TargetInfo.OsInfo.Version.Minor);
|
|
dprintf("%d procs, %d current processor, type %lx\n",
|
|
TargetInfo.Cpu.NumCPUs,
|
|
TargetInfo.Cpu.CurrentProc,
|
|
TargetInfo.Cpu.Type);
|
|
for (ULONG i =0; i<TargetInfo.Cpu.NumCPUs; i++) {
|
|
if (TargetInfo.Cpu.Type == IMAGE_FILE_MACHINE_I386) {
|
|
dprintf("CPU %lx Family %lx Model %lx Stepping %lx Vendor %-12.12s\n",
|
|
i,
|
|
TargetInfo.Cpu.ProcInfo[i].X86.Family,
|
|
TargetInfo.Cpu.ProcInfo[i].X86.Model,
|
|
TargetInfo.Cpu.ProcInfo[i].X86.Stepping,
|
|
TargetInfo.Cpu.ProcInfo[i].X86.VendorString);
|
|
} else if (TargetInfo.Cpu.Type == IMAGE_FILE_MACHINE_IA64) {
|
|
dprintf("CPU %lx Family %lx Model %lx Revision %lx Vendor %-12.12s\n",
|
|
i,
|
|
TargetInfo.Cpu.ProcInfo[i].Ia64.Family,
|
|
TargetInfo.Cpu.ProcInfo[i].Ia64.Model,
|
|
TargetInfo.Cpu.ProcInfo[i].Ia64.Revision,
|
|
TargetInfo.Cpu.ProcInfo[i].Ia64.VendorString);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
EXIT_API();
|
|
return S_OK;
|
|
}
|
|
#endif
|
|
|
|
DWORD
|
|
_EFN_GetTriageFollowupFromSymbol(
|
|
IN PDEBUG_CLIENT Client,
|
|
IN PSTR SymbolName,
|
|
OUT PDEBUG_TRIAGE_FOLLOWUP_INFO OwnerInfo
|
|
)
|
|
{
|
|
DWORD ret;
|
|
BOOL Enter = (g_ExtClient != Client);
|
|
|
|
|
|
if (!Client && Enter)
|
|
{
|
|
return FALSE;
|
|
}
|
|
if (OwnerInfo->SizeOfStruct != sizeof(DEBUG_TRIAGE_FOLLOWUP_INFO))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (Enter)
|
|
{
|
|
INIT_API()
|
|
}
|
|
if (g_pTriager == NULL)
|
|
{
|
|
g_pTriager = new CTriager();
|
|
}
|
|
if (g_pTriager != NULL)
|
|
{
|
|
ret = g_pTriager->GetFollowup(OwnerInfo->OwnerName,
|
|
OwnerInfo->OwnerNameSize,
|
|
SymbolName);
|
|
} else
|
|
{
|
|
ret = TRIAGE_FOLLOWUP_FAIL;
|
|
}
|
|
if (Enter)
|
|
{
|
|
EXIT_API();
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
DECLARE_API( owner )
|
|
{
|
|
CHAR Input[2000];
|
|
CHAR Owner[200];
|
|
ULONG status = TRIAGE_FOLLOWUP_DEFAULT;
|
|
PDEBUG_FAILURE_ANALYSIS pAnalysis = NULL;
|
|
FA_ENTRY* Entry;
|
|
|
|
INIT_API();
|
|
|
|
Input[0] = 0;
|
|
if (!sscanf(args, "%s", Input))
|
|
{
|
|
Input[0] = 0;
|
|
}
|
|
|
|
//
|
|
// If we have a string, look for that - otherwise, do an analysis and
|
|
// get the followup string from that.
|
|
//
|
|
|
|
if (*Input)
|
|
{
|
|
status = g_pTriager->GetFollowup(Owner, sizeof(Owner), Input);
|
|
}
|
|
else
|
|
{
|
|
_EFN_GetFailureAnalysis(Client, 0, &pAnalysis);
|
|
|
|
if (pAnalysis)
|
|
{
|
|
Entry = pAnalysis->Get(DEBUG_FLR_FOLLOWUP_NAME);
|
|
CopyString(Owner, FA_ENTRY_DATA(PCHAR, Entry), sizeof(Owner));
|
|
pAnalysis->Release();
|
|
status = TRIAGE_FOLLOWUP_SUCCESS;
|
|
}
|
|
}
|
|
|
|
if (status == TRIAGE_FOLLOWUP_FAIL)
|
|
{
|
|
dprintf("Internal error getting followup - contact Debugger team\n");
|
|
}
|
|
else
|
|
{
|
|
dprintf("Followup: %s\n", Owner);
|
|
}
|
|
|
|
EXIT_API();
|
|
return S_OK;
|
|
}
|
|
|
|
void
|
|
_EFN_DecodeError(
|
|
PDEBUG_DECODE_ERROR pDecodeError
|
|
)
|
|
{
|
|
if (pDecodeError->SizeOfStruct != sizeof(DEBUG_DECODE_ERROR))
|
|
{
|
|
return;
|
|
}
|
|
return DecodeErrorForMessage(pDecodeError);
|
|
}
|
|
|
|
DECLARE_API( elog_str )
|
|
{
|
|
HANDLE EventSource = NULL;
|
|
|
|
INIT_API();
|
|
|
|
if (args)
|
|
{
|
|
while (isspace(*args))
|
|
{
|
|
args++;
|
|
}
|
|
}
|
|
|
|
if (!args || !args[0])
|
|
{
|
|
Status = E_INVALIDARG;
|
|
ExtErr("Usage: elog_str string\n");
|
|
goto Exit;
|
|
}
|
|
|
|
// Get a handle to the NT application log.
|
|
EventSource = OpenEventLog(NULL, "Application");
|
|
if (!EventSource)
|
|
{
|
|
Status = HRESULT_FROM_WIN32(GetLastError());
|
|
ExtErr("Unable to open event log, 0x%08X\n", Status);
|
|
goto Exit;
|
|
}
|
|
|
|
if (!ReportEvent(EventSource, EVENTLOG_ERROR_TYPE, 0, 0, NULL,
|
|
1, 0, &args, NULL))
|
|
{
|
|
Status = HRESULT_FROM_WIN32(GetLastError());
|
|
ExtErr("Unable to report event, 0x%08X\n", Status);
|
|
goto Exit;
|
|
}
|
|
|
|
Status = S_OK;
|
|
|
|
Exit:
|
|
if (EventSource)
|
|
{
|
|
CloseEventLog(EventSource);
|
|
}
|
|
EXIT_API();
|
|
return Status;
|
|
}
|
|
|
|
HRESULT
|
|
AnsiToUnicode(PCSTR StrA, PWSTR* StrW)
|
|
{
|
|
ULONG Len;
|
|
|
|
// No input is an error.
|
|
if (NULL == StrA)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
Len = strlen(StrA) + 1;
|
|
*StrW = (PWSTR)malloc(Len * sizeof(WCHAR));
|
|
if (*StrW == NULL)
|
|
{
|
|
ExtErr("Unable to allocate memory\n");
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
if (0 == MultiByteToWideChar(CP_ACP, 0, StrA, Len, *StrW, Len))
|
|
{
|
|
HRESULT Status = HRESULT_FROM_WIN32(GetLastError());
|
|
free(*StrW);
|
|
ExtErr("Unable to convert string, 0x%08X\n", Status);
|
|
return Status;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
typedef NET_API_STATUS (NET_API_FUNCTION* PFN_NetMessageBufferSend)
|
|
(
|
|
IN LPCWSTR servername,
|
|
IN LPCWSTR msgname,
|
|
IN LPCWSTR fromname,
|
|
IN LPBYTE buf,
|
|
IN DWORD buflen
|
|
);
|
|
|
|
DECLARE_API( net_send )
|
|
{
|
|
PWSTR ArgsW = NULL;
|
|
PWSTR Tokens[4];
|
|
ULONG i;
|
|
HMODULE NetLib = NULL;
|
|
PFN_NetMessageBufferSend Send;
|
|
ULONG Result;
|
|
PWSTR ArgsEnd;
|
|
|
|
INIT_API();
|
|
|
|
NetLib = LoadLibrary("netapi32.dll");
|
|
if (!NetLib)
|
|
{
|
|
Status = HRESULT_FROM_WIN32(GetLastError());
|
|
ExtErr("Platform does not support net send\n");
|
|
goto Exit;
|
|
}
|
|
Send = (PFN_NetMessageBufferSend)
|
|
GetProcAddress(NetLib, "NetMessageBufferSend");
|
|
if (!Send)
|
|
{
|
|
Status = E_NOTIMPL;
|
|
ExtErr("Platform does not support net send\n");
|
|
goto Exit;
|
|
}
|
|
|
|
Status = AnsiToUnicode(args, &ArgsW);
|
|
if (Status != S_OK)
|
|
{
|
|
goto Exit;
|
|
}
|
|
ArgsEnd = ArgsW + wcslen(ArgsW);
|
|
|
|
// The message text is the entire remainder of the argument
|
|
// string after parsing the first separate tokens, so
|
|
// only wcstok up to the next-to-last token.
|
|
for (i = 0; i < sizeof(Tokens) / sizeof(Tokens[0]) - 1; i++)
|
|
{
|
|
Tokens[i] = wcstok(i == 0 ? ArgsW : NULL, L" \t");
|
|
if (Tokens[i] == NULL)
|
|
{
|
|
Status = E_INVALIDARG;
|
|
ExtErr("USAGE: net_send <targetserver> <targetuser> "
|
|
"<fromuser> <msg>\n");
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
Tokens[i] = Tokens[i - 1] + wcslen(Tokens[i - 1]) + 1;
|
|
while (Tokens[i] < ArgsEnd &&
|
|
(*Tokens[i] == ' ' || *Tokens[i] == '\t'))
|
|
{
|
|
Tokens[i]++;
|
|
}
|
|
if (Tokens[i] >= ArgsEnd)
|
|
{
|
|
Status = E_INVALIDARG;
|
|
ExtErr("USAGE: net_send <execmachine> <targetmachine> "
|
|
"<sender> <msg>\n");
|
|
goto Exit;
|
|
}
|
|
|
|
Result = Send(Tokens[0], Tokens[1], Tokens[2], (PBYTE)Tokens[3],
|
|
(wcslen(Tokens[3]) + 1) * sizeof(WCHAR));
|
|
if (Result != NERR_Success)
|
|
{
|
|
Status = HRESULT_FROM_WIN32(Result);;
|
|
ExtErr("Unable to send message, 0x%08X\n", Status);
|
|
goto Exit;
|
|
}
|
|
|
|
Status = S_OK;
|
|
|
|
Exit:
|
|
if (ArgsW)
|
|
{
|
|
free(ArgsW);
|
|
}
|
|
if (NetLib)
|
|
{
|
|
FreeLibrary(NetLib);
|
|
}
|
|
EXIT_API();
|
|
return Status;
|
|
}
|
|
|
|
// XXX drewb - This function just starts a mail message; the
|
|
// UI comes up and the user must finish and send the message.
|
|
// Therefore it doesn't have much value over the
|
|
// user just deciding to send a message.
|
|
#if 0
|
|
|
|
typedef ULONG (FAR PASCAL *PFN_MapiSendMail)
|
|
(
|
|
LHANDLE lhSession,
|
|
ULONG ulUIParam,
|
|
lpMapiMessage lpMessage,
|
|
FLAGS flFlags,
|
|
ULONG ulReserved
|
|
);
|
|
|
|
DECLARE_API( mapi_send )
|
|
{
|
|
HMODULE MapiLib = NULL;
|
|
PFN_MapiSendMail Send;
|
|
MapiMessage Mail;
|
|
|
|
INIT_API();
|
|
|
|
MapiLib = LoadLibrary("mapi.dll");
|
|
if (!MapiLib)
|
|
{
|
|
Status = HRESULT_FROM_WIN32(GetLastError());
|
|
ExtErr("Platform does not support MAPI\n");
|
|
goto Exit;
|
|
}
|
|
Send = (PFN_MapiSendMail)
|
|
GetProcAddress(MapiLib, "MAPISendMail");
|
|
if (!Send)
|
|
{
|
|
Status = E_NOTIMPL;
|
|
ExtErr("Platform does not support MAPI\n");
|
|
goto Exit;
|
|
}
|
|
|
|
ZeroMemory(&Mail, sizeof(Mail));
|
|
|
|
if (!Send(0, // use implicit session.
|
|
0, // ulUIParam; 0 is always valid
|
|
&Mail, // the message being sent
|
|
MAPI_DIALOG, // allow the user to edit the message
|
|
0 // reserved; must be 0
|
|
))
|
|
{
|
|
Status = E_FAIL;
|
|
ExtErr("Unable to send mail\n");
|
|
goto Exit;
|
|
}
|
|
|
|
Status = S_OK;
|
|
|
|
Exit:
|
|
if (MapiLib)
|
|
{
|
|
FreeLibrary(MapiLib);
|
|
}
|
|
EXIT_API();
|
|
return Status;
|
|
}
|
|
|
|
#endif
|
|
|
|
DECLARE_API( imggp )
|
|
{
|
|
ULONG64 ImageBase;
|
|
IMAGE_NT_HEADERS64 NtHdr;
|
|
|
|
INIT_API();
|
|
|
|
ImageBase = GetExpression(args);
|
|
|
|
if (g_ExtData->ReadImageNtHeaders(ImageBase, &NtHdr) != S_OK)
|
|
{
|
|
ExtErr("Unable to read image header at %p\n", ImageBase);
|
|
goto Exit;
|
|
}
|
|
|
|
if (NtHdr.OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC)
|
|
{
|
|
ExtErr("Image is not 64-bit\n");
|
|
goto Exit;
|
|
}
|
|
if (NtHdr.OptionalHeader.NumberOfRvaAndSizes <=
|
|
IMAGE_DIRECTORY_ENTRY_GLOBALPTR)
|
|
{
|
|
ExtErr("Image does not have a GP directory entry\n");
|
|
goto Exit;
|
|
}
|
|
|
|
ExtOut("Image at %p has a GP value of %p\n",
|
|
ImageBase, ImageBase +
|
|
NtHdr.OptionalHeader.DataDirectory
|
|
[IMAGE_DIRECTORY_ENTRY_GLOBALPTR].VirtualAddress);
|
|
|
|
Exit:
|
|
EXIT_API();
|
|
return S_OK;
|
|
}
|
|
|
|
DECLARE_API( imgreloc )
|
|
{
|
|
ULONG64 ImageBase;
|
|
IMAGE_NT_HEADERS64 NtHdr;
|
|
ULONG NumMod;
|
|
ULONG i;
|
|
PDEBUG_MODULE_PARAMETERS Mod = NULL;
|
|
|
|
INIT_API();
|
|
|
|
ImageBase = GetExpression(args);
|
|
|
|
if (g_ExtSymbols->GetNumberModules(&NumMod, &i) != S_OK ||
|
|
!(Mod = (PDEBUG_MODULE_PARAMETERS)malloc(NumMod * sizeof(*Mod))) ||
|
|
g_ExtSymbols->GetModuleParameters(NumMod, NULL, 0, Mod) != S_OK)
|
|
{
|
|
ExtErr("Unable to get module information\n");
|
|
goto Exit;
|
|
}
|
|
|
|
for (i = 0; i < NumMod; i++)
|
|
{
|
|
char Name[MAX_PATH];
|
|
|
|
if (g_ExtData->ReadImageNtHeaders(Mod[i].Base, &NtHdr) != S_OK)
|
|
{
|
|
ExtErr("Unable to read image header at %p\n", Mod[i].Base);
|
|
continue;
|
|
}
|
|
|
|
if (FAILED(g_ExtSymbols->
|
|
GetModuleNames(DEBUG_ANY_ID, Mod[i].Base,
|
|
NULL, 0, NULL,
|
|
Name, sizeof(Name), NULL,
|
|
NULL, 0, NULL)))
|
|
{
|
|
StringCchCopy(Name, sizeof(Name), "<Error>");
|
|
}
|
|
|
|
ExtOut("%p %s - ", Mod[i].Base, Name);
|
|
|
|
if (NtHdr.OptionalHeader.ImageBase != Mod[i].Base)
|
|
{
|
|
ExtOut("RELOCATED from %p\n",
|
|
NtHdr.OptionalHeader.ImageBase);
|
|
}
|
|
else
|
|
{
|
|
ExtOut("at preferred address\n");
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
free(Mod);
|
|
EXIT_API();
|
|
return S_OK;
|
|
}
|
|
|
|
VOID
|
|
GetNtTimeStamp(
|
|
PULONG TimeStamp
|
|
)
|
|
{
|
|
ULONG64 ModBase;
|
|
IMAGE_NT_HEADERS64 NtHdr;
|
|
if (!TimeStamp)
|
|
{
|
|
return;
|
|
}
|
|
if (g_ExtSymbols->GetModuleByModuleName("nt", 0, NULL, &ModBase) != S_OK)
|
|
{
|
|
return;
|
|
}
|
|
NtHdr.FileHeader.TimeDateStamp = 0;
|
|
if (g_ExtData->ReadImageNtHeaders(ModBase, &NtHdr) != S_OK)
|
|
{
|
|
return;
|
|
}
|
|
|
|
*TimeStamp = NtHdr.FileHeader.TimeDateStamp;
|
|
}
|
|
|
|
VOID
|
|
GetSkuFromProductType(
|
|
ULONG ProductType,
|
|
ULONG Suite,
|
|
PULONG pSku
|
|
)
|
|
{
|
|
ULONG Sku = 0;
|
|
if (!pSku)
|
|
{
|
|
return;
|
|
}
|
|
*pSku = 0;
|
|
|
|
if (ProductType == VER_NT_WORKSTATION)
|
|
{
|
|
if (Suite & VER_SUITE_PERSONAL)
|
|
{
|
|
Sku = CiOsHomeEdition;
|
|
}
|
|
else
|
|
{
|
|
Sku = CiOsProfessional;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (Suite & VER_SUITE_DATACENTER)
|
|
{
|
|
Sku = CiOsDataCenter;
|
|
}
|
|
else if (Suite & VER_SUITE_ENTERPRISE)
|
|
{
|
|
Sku = CiOsAdvancedServer;
|
|
}
|
|
else if (Suite & VER_SUITE_BLADE)
|
|
{
|
|
Sku = CiOsWebServer;
|
|
}
|
|
else
|
|
{
|
|
Sku = CiOsServer;
|
|
}
|
|
}
|
|
|
|
*pSku = Sku;
|
|
}
|
|
|
|
HRESULT
|
|
InitializeCrashInstance(
|
|
PCRASH_INSTANCE pCrash
|
|
)
|
|
{
|
|
|
|
return 0;
|
|
}
|
|
|
|
PCHAR
|
|
GetLogFileName(
|
|
void
|
|
)
|
|
{
|
|
static CHAR szLogFileName[MAX_PATH+50];
|
|
PCHAR ExeDir;
|
|
|
|
ExeDir = &szLogFileName[0];
|
|
|
|
*ExeDir = 0;
|
|
// Get the directory the debugger executable is in.
|
|
if (!GetModuleFileName(NULL, ExeDir, MAX_PATH))
|
|
{
|
|
// Error. Use the current directory.
|
|
StringCchCopy(ExeDir, sizeof(szLogFileName), ".");
|
|
} else
|
|
{
|
|
// Remove the executable name.
|
|
PCHAR pszTmp = strrchr(ExeDir, '\\');
|
|
if (pszTmp)
|
|
{
|
|
*pszTmp = 0;
|
|
}
|
|
}
|
|
StringCchCat(ExeDir, sizeof(szLogFileName), "\\FailedAddCrash.log");
|
|
return &szLogFileName[0];
|
|
|
|
}
|
|
|
|
|
|
#define DB_LOOKUP_CRASH 1
|
|
#define DB_ADD_CRASH 2
|
|
#define DB_RETRIAGE_CRASH 4
|
|
#define DB_PRINT_CRASH 8
|
|
#define DB_SEND_MAIL 0x10
|
|
#define DB_NO_CUSTOMER 0x20
|
|
|
|
HRESULT
|
|
AddCrashToDB(
|
|
ULONG Flag,
|
|
PCRASH_INSTANCE pCrash
|
|
)
|
|
{
|
|
HRESULT Hr;
|
|
CrashDatabaseHandler *CrDb;
|
|
PDEBUG_FAILURE_ANALYSIS Analysis;
|
|
CHAR Bucket[100], Followup[50], DefaultBucket[100], Driver[100];
|
|
TARGET_DEBUG_INFO TargetInfo;
|
|
BOOL AddCrash = Flag & DB_ADD_CRASH;
|
|
BOOL LookupCrash = Flag & DB_LOOKUP_CRASH;
|
|
|
|
if ((Hr = _EFN_GetFailureAnalysis(g_ExtClient,
|
|
0,
|
|
&Analysis)) != S_OK)
|
|
{
|
|
return Hr;
|
|
}
|
|
|
|
// Construct CRASH_INSTANCE
|
|
|
|
Followup[0] = Bucket[0] = Driver[0] = 0;
|
|
Analysis->GetString(DEBUG_FLR_BUCKET_ID,
|
|
Bucket,
|
|
sizeof(Bucket));
|
|
Analysis->GetString(DEBUG_FLR_DEFAULT_BUCKET_ID,
|
|
DefaultBucket,
|
|
sizeof(DefaultBucket));
|
|
Analysis->GetString(DEBUG_FLR_FOLLOWUP_NAME,
|
|
Followup,
|
|
sizeof(Followup));
|
|
Analysis->GetString(DEBUG_FLR_IMAGE_NAME,
|
|
Driver,
|
|
sizeof(Driver));
|
|
if (!isprint(Driver[0]))
|
|
{
|
|
Driver[0] = 0;
|
|
}
|
|
if (Analysis->Get(DEBUG_FLR_CPU_OVERCLOCKED))
|
|
{
|
|
pCrash->OverClocked = TRUE;
|
|
}
|
|
else
|
|
{
|
|
pCrash->OverClocked = FALSE;
|
|
}
|
|
|
|
pCrash->StopCode = Analysis->GetFailureCode();
|
|
pCrash->FailureType = Analysis->GetFailureType();
|
|
pCrash->SolutionType = CiSolUnsolved;
|
|
Analysis->GetUlong(DEBUG_FLR_SOLUTION_ID, &pCrash->SolutionId);
|
|
Analysis->GetUlong(DEBUG_FLR_SOLUTION_TYPE, (PULONG) &pCrash->SolutionType);
|
|
Analysis->GetUlong(DEBUG_FLR_DEFAULT_SOLUTION_ID, &pCrash->GenericSolId);
|
|
Analysis->Release();
|
|
|
|
if (!Followup[0] || !Bucket[0])
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
pCrash->Build = g_TargetBuild; // make room for service pack
|
|
if (pCrash->Bucket)
|
|
{
|
|
StringCchCopy(pCrash->Bucket, pCrash->BucketSize, Bucket);
|
|
} else
|
|
{
|
|
pCrash->Bucket = Bucket;
|
|
pCrash->BucketSize = sizeof(Bucket);
|
|
}
|
|
if (pCrash->DefaultBucket)
|
|
{
|
|
StringCchCopy(pCrash->DefaultBucket, pCrash->DefaultBucketSize, DefaultBucket);
|
|
} else
|
|
{
|
|
pCrash->DefaultBucket = DefaultBucket;
|
|
pCrash->DefaultBucketSize = sizeof(DefaultBucket);
|
|
}
|
|
pCrash->Followup = Followup;
|
|
pCrash->FaultyDriver = Driver;
|
|
pCrash->DumpClass = g_TargetQualifier;
|
|
// GetNtTimeStamp(&pCrash->NtTimeStamp);
|
|
|
|
//
|
|
// extract the incident ID from the dump name
|
|
// there are 2 types of filenames we could have to support.
|
|
// The old version is id@*.*
|
|
// The new version is id.*
|
|
//
|
|
|
|
CHAR FileName[MAX_PATH];
|
|
DWORD ID;
|
|
|
|
_splitpath(pCrash->Path, NULL, NULL, FileName, NULL);
|
|
|
|
//
|
|
// Extract the name of the original dump file in the cab.
|
|
//
|
|
|
|
pCrash->OriginalDumpFileName = NULL;
|
|
|
|
if (g_ExtSystem->GetCurrentSystemServerName(FileName, sizeof(FileName),
|
|
NULL) == S_OK)
|
|
{
|
|
pCrash->OriginalDumpFileName = strrchr(FileName, '_');
|
|
|
|
if (pCrash->OriginalDumpFileName)
|
|
{
|
|
pCrash->OriginalDumpFileName +=1;
|
|
}
|
|
}
|
|
|
|
TargetInfo.SizeOfStruct = sizeof(TARGET_DEBUG_INFO);
|
|
|
|
if (FillTargetDebugInfo(g_ExtClient, &TargetInfo) == S_OK)
|
|
{
|
|
switch (TargetInfo.Cpu.Type)
|
|
{
|
|
case IMAGE_FILE_MACHINE_I386:
|
|
pCrash->CpuId =
|
|
(TargetInfo.Cpu.ProcInfo[0].X86.Family << 16) |
|
|
(TargetInfo.Cpu.ProcInfo[0].X86.Model << 8) |
|
|
(TargetInfo.Cpu.ProcInfo[0].X86.Stepping);
|
|
break;
|
|
case IMAGE_FILE_MACHINE_IA64:
|
|
pCrash->CpuId =
|
|
(TargetInfo.Cpu.ProcInfo[0].Ia64.Family << 16) |
|
|
(TargetInfo.Cpu.ProcInfo[0].Ia64.Model << 8) |
|
|
(TargetInfo.Cpu.ProcInfo[0].Ia64.Revision);
|
|
break;
|
|
case IMAGE_FILE_MACHINE_AMD64:
|
|
pCrash->CpuId =
|
|
(TargetInfo.Cpu.ProcInfo[0].Amd64.Family << 16) |
|
|
(TargetInfo.Cpu.ProcInfo[0].Amd64.Model << 8) |
|
|
(TargetInfo.Cpu.ProcInfo[0].Amd64.Stepping);
|
|
break;
|
|
}
|
|
pCrash->CpuType = TargetInfo.Cpu.Type;
|
|
pCrash->NumProc = TargetInfo.Cpu.NumCPUs;
|
|
pCrash->UpTime = (ULONG) TargetInfo.SysUpTime;
|
|
pCrash->CrashTime = (ULONG) TargetInfo.CrashTime;
|
|
|
|
pCrash->ServicePack =
|
|
(TargetInfo.OsInfo.SrvPackNumber & 0xFFFF0000) ?
|
|
(TargetInfo.OsInfo.SrvPackNumber & 0xFFFF0000) >> 16 :
|
|
((TargetInfo.OsInfo.SrvPackNumber & 0xFFFF) >> 8) * 1000;
|
|
|
|
GetSkuFromProductType(TargetInfo.OsInfo.ProductType,
|
|
TargetInfo.OsInfo.Suite,
|
|
(PULONG) &pCrash->Sku);
|
|
|
|
if (Flag & DB_PRINT_CRASH)
|
|
{
|
|
dprintf("CRASH DATA FOR DB\n----------------------\n");
|
|
dprintf("CrashId : %ld - %ld\n", pCrash->CrashTime , pCrash->UpTime);
|
|
dprintf("BucketId : %s\n", pCrash->Bucket);
|
|
dprintf("FollowUp : %s\n", pCrash->Followup);
|
|
dprintf("Build : %u\n", pCrash->Build);
|
|
dprintf("CpuId : %ld - %ld\n", pCrash->uCpu >> 32, (ULONG) pCrash->uCpu);
|
|
dprintf("Overclocked : %s\n", pCrash->OverClocked ? "TRUE" : "FALSE");
|
|
}
|
|
}
|
|
|
|
if (!(Flag & ~DB_PRINT_CRASH))
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
pCrash->bSendMail = Flag & DB_SEND_MAIL;
|
|
pCrash->bUpdateCustomer = FALSE; // Disable customer DB
|
|
|
|
if (AddCrash && !(Flag & DB_RETRIAGE_CRASH))
|
|
{
|
|
Hr = _EFN_DbAddCrashDirect(pCrash, g_ExtControl);
|
|
}
|
|
else if (Flag & DB_RETRIAGE_CRASH)
|
|
{
|
|
// reset crash bucket mapping
|
|
pCrash->bResetBucket = TRUE;
|
|
pCrash->bUpdateCustomer = !(Flag & DB_NO_CUSTOMER);
|
|
Hr = _EFN_DbAddCrashDirect(pCrash, g_ExtControl);
|
|
}
|
|
else if (LookupCrash)
|
|
{
|
|
CHAR SolText[ SOLUTION_TEXT_SIZE ], OSVer[ OS_VER_SIZE ];
|
|
ULONG Count=0;
|
|
CCrashInstance CrashInstance;
|
|
|
|
if ((Hr = InitializeDatabaseHandlers(g_ExtControl, 7)) != S_OK)
|
|
{
|
|
return Hr;
|
|
}
|
|
|
|
g_CrDb->BuildQueryForCrashInstance(pCrash);
|
|
g_CrDb->m_pADOResult = &CrashInstance;
|
|
g_CrDb->m_fPrintIt = TRUE;
|
|
g_CrDb->GetRecords(&Count, TRUE);
|
|
if (Count)
|
|
{
|
|
dprintf("This crash has already been added to database\n");
|
|
CrashInstance.OutPut();
|
|
}
|
|
g_CrDb->m_fPrintIt = FALSE;
|
|
if (g_SolDb->GetSolution(pCrash) == S_OK)
|
|
{
|
|
dprintf("Solution found for bucket:\n%s\n", SolText);
|
|
}
|
|
}
|
|
|
|
return Hr;
|
|
}
|
|
|
|
DECLARE_API( dbaddcrash )
|
|
{
|
|
CHAR Path[MAX_PATH]={0};
|
|
CHAR CrashGUID[50] = {0};
|
|
CHAR MQConnectStr[100] = {0};
|
|
CHAR SrNumber[100] = {0};
|
|
CRASH_INSTANCE Crash = {0};
|
|
CHAR Buffer[50];
|
|
BOOL Retriage = FALSE;
|
|
BOOL Print = FALSE;
|
|
BOOL bParseError = FALSE;
|
|
ULONG Flag = 0;
|
|
HRESULT Hr;
|
|
PCSTR argssave = args;
|
|
|
|
|
|
INIT_API();
|
|
|
|
while (*args)
|
|
{
|
|
if (*args == ' ' || *args == '\t')
|
|
{
|
|
++args;
|
|
continue;
|
|
}
|
|
else if (*args == '-' || *args == '/')
|
|
{
|
|
++args;
|
|
switch (*args)
|
|
{
|
|
case 'g': // GUID identifying this crash, return bucket along with this
|
|
++args;
|
|
while (*args == ' ') ++args;
|
|
if (!sscanf(args,"%50s", CrashGUID))
|
|
{
|
|
CrashGUID[0] = 0;
|
|
}
|
|
args+=strlen(CrashGUID);
|
|
Crash.MesgGuid = CrashGUID;
|
|
break;
|
|
|
|
case 'm':
|
|
if (!strncmp(args, "mail", 4))
|
|
{
|
|
Flag |= DB_SEND_MAIL ;
|
|
args+=4;
|
|
}
|
|
break;
|
|
case 'n':
|
|
if (!strncmp(args, "nocust", 6))
|
|
{
|
|
Flag |= DB_NO_CUSTOMER ;
|
|
args+=6;
|
|
}
|
|
break;
|
|
case 'o':
|
|
Print = TRUE;
|
|
break;
|
|
case 'p':
|
|
++args;
|
|
while (*args == ' ') ++args;
|
|
if (!sscanf(args,"%240s", Path))
|
|
{
|
|
Path[0] = 0;
|
|
}
|
|
args+=strlen(Path);
|
|
Crash.Path = Path;
|
|
break;
|
|
case 'r':
|
|
if (!strncmp(args, "retriage", 8))
|
|
{
|
|
Retriage = TRUE;
|
|
args+=8;
|
|
}
|
|
break;
|
|
case 's': // queue connection string to send bucketid back
|
|
if (!strncmp(args, "source", 6))
|
|
{
|
|
ULONG Source;
|
|
|
|
args+=6;
|
|
while (*args == ' ') ++args;
|
|
|
|
if (isdigit(*args))
|
|
{
|
|
Source = atoi(args);
|
|
if (Source < (ULONG) CiSrcMax)
|
|
{
|
|
Crash.SourceId = (CI_SOURCE) Source;
|
|
}
|
|
while (isdigit(*args)) ++args;
|
|
}
|
|
} else if (!strncmp(args, "sr", 2))
|
|
{
|
|
args+=2;
|
|
while (*args == ' ') ++args;
|
|
if (!sscanf(args,"%100s", SrNumber))
|
|
{
|
|
SrNumber[0] = 0;
|
|
}
|
|
args+= strlen(SrNumber);
|
|
Crash.PssSr = SrNumber;
|
|
} else
|
|
{
|
|
++args;
|
|
while (*args == ' ') ++args;
|
|
if (!sscanf(args,"%100s", MQConnectStr))
|
|
{
|
|
MQConnectStr[0] = 0;
|
|
}
|
|
args+=strlen(MQConnectStr);
|
|
Crash.MqConnectStr = MQConnectStr;
|
|
}
|
|
break;
|
|
default:
|
|
++args;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dprintf("Error in '%s'\n", args);
|
|
bParseError = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (*Path && !bParseError)
|
|
{
|
|
if (Retriage)
|
|
{
|
|
dprintf("Retriag crash\n");
|
|
Hr = AddCrashToDB(DB_RETRIAGE_CRASH | Flag, &Crash);
|
|
}
|
|
else
|
|
{
|
|
Hr = AddCrashToDB(DB_ADD_CRASH | Flag, &Crash);
|
|
}
|
|
}
|
|
else if (Print && !bParseError)
|
|
{
|
|
Hr = AddCrashToDB(DB_PRINT_CRASH, NULL);
|
|
}
|
|
else
|
|
{
|
|
dprintf("Bad argument: %s\n", argssave);
|
|
dprintf("Usage: !dbaddcrash -o -mail -p <dumppath> -retriage\n");
|
|
Hr = E_FAIL;
|
|
}
|
|
|
|
if (FAILED (Hr))
|
|
{
|
|
int g_LogFile;
|
|
|
|
g_LogFile = _open(GetLogFileName(), O_APPEND | O_CREAT | O_RDWR,
|
|
S_IREAD | S_IWRITE);
|
|
|
|
if (g_LogFile != -1)
|
|
{
|
|
_write(g_LogFile, Path, strlen(Path));
|
|
_write(g_LogFile, "\n", strlen("\n"));
|
|
_close(g_LogFile);
|
|
}
|
|
}
|
|
|
|
EXIT_API();
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
DECLARE_API( dblookupcrash )
|
|
{
|
|
CHAR Bucket[MAX_PATH]={0};
|
|
|
|
INIT_API();
|
|
while (*args)
|
|
{
|
|
if (*args == ' ' || *args == '\t')
|
|
{
|
|
++args;
|
|
continue;
|
|
} else if (*args == '-' || *args == '/')
|
|
{
|
|
++args;
|
|
switch (*args)
|
|
{
|
|
case 'b':
|
|
++args;
|
|
while (*args == ' ') ++args;
|
|
if (sscanf(args,"%s", Bucket))
|
|
{
|
|
args+=strlen(Bucket);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
++args;
|
|
}
|
|
|
|
AddCrashToDB(DB_LOOKUP_CRASH, NULL);
|
|
|
|
EXIT_API();
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
DECLARE_API( help )
|
|
{
|
|
dprintf("diskspace <DriveLetter>[:] - Displays free disk space for specified volume\n");
|
|
dprintf("analyze [-v] - Analyzes current exception or bugcheck\n");
|
|
dprintf("cpuid [processor] - Displays CPU version info for all CPUs\n");
|
|
dprintf("elog_str <message> - Logs simple message to host event log\n");
|
|
dprintf("error [errorcode] - Displays Win32 & NTSTATUS error string\n");
|
|
dprintf("exchain - Displays exception chain for current thread\n");
|
|
dprintf("gle [-all] - Displays last error & status for current thread\n");
|
|
dprintf("imggp <imagebase> - Displays GP directory entry for 64-bit image\n");
|
|
dprintf("imgreloc <imagebase> - Relocates modules for an image\n");
|
|
dprintf("obja <address> - Displays OBJECT_ATTRIBUTES[32|64]\n");
|
|
dprintf("owner [symbol!module] - Detects owner for current exception or\n");
|
|
dprintf(" bugcheck from triage.ini\n");
|
|
dprintf("str <address> - Displays ANSI_STRING or OEM_STRING\n");
|
|
dprintf("ustr <address> - Displays UNICODE_STRING\n");
|
|
|
|
dprintf("\nType \".hh [command]\" for more detailed help\n");
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
void
|
|
DumpCrtEhX86(ULONG64 RecAddr)
|
|
{
|
|
// struct _EH3_EXCEPTION_REGISTRATION *Next;
|
|
// PVOID ExceptionHandler;
|
|
// PSCOPETABLE_ENTRY ScopeTable;
|
|
// DWORD TryLevel;
|
|
ULONG64 Record[4];
|
|
|
|
if (g_ExtData->ReadPointersVirtual(4, RecAddr, Record) != S_OK)
|
|
{
|
|
return;
|
|
}
|
|
|
|
ULONG64 ScopeBase = Record[2];
|
|
LONG Level = (LONG)Record[3];
|
|
|
|
while (Level > -1)
|
|
{
|
|
// int enclosing_level;
|
|
// int (*filter)(PEXCEPTION_RECORD);
|
|
// void (*specific_handler)(void);
|
|
ULONG64 ScopeRec[3];
|
|
|
|
if (g_ExtData->ReadPointersVirtual(3, ScopeBase + Level * 12,
|
|
ScopeRec) != S_OK)
|
|
{
|
|
return;
|
|
}
|
|
|
|
ExtOut(" CRT scope %2d, ", Level);
|
|
|
|
char Sym[256];
|
|
ULONG64 Disp;
|
|
|
|
if (ScopeRec[1])
|
|
{
|
|
ExtOut("filter: ");
|
|
if (FAILED(g_ExtSymbols->
|
|
GetNameByOffset(ScopeRec[1], Sym, sizeof(Sym),
|
|
NULL, &Disp)) ||
|
|
!Sym[0])
|
|
{
|
|
ExtOut("%p", ScopeRec[1]);
|
|
}
|
|
else
|
|
{
|
|
ExtOut("%s+%I64x (%p)", Sym, Disp, ScopeRec[1]);
|
|
}
|
|
dprintf("\n ");
|
|
}
|
|
|
|
ExtOut("func: ");
|
|
if (FAILED(g_ExtSymbols->
|
|
GetNameByOffset(ScopeRec[2], Sym, sizeof(Sym),
|
|
NULL, &Disp)) ||
|
|
!Sym[0])
|
|
{
|
|
ExtOut("%p", ScopeRec[2]);
|
|
}
|
|
else
|
|
{
|
|
ExtOut("%s+%I64x (%p)", Sym, Disp, ScopeRec[2]);
|
|
}
|
|
|
|
ExtOut("\n");
|
|
|
|
Level = (LONG)ScopeRec[0];
|
|
}
|
|
}
|
|
|
|
void
|
|
DumpExceptionChainX86(ULONG64 Teb)
|
|
{
|
|
// struct _EXCEPTION_REGISTRATION_RECORD *ExceptionList;
|
|
// PVOID StackBase;
|
|
// PVOID StackLimit;
|
|
ULONG64 TibInfo[3];
|
|
|
|
if (g_ExtData->ReadPointersVirtual(3, Teb, TibInfo) != S_OK)
|
|
{
|
|
ExtErr("Unable to read TIB\n");
|
|
return;
|
|
}
|
|
|
|
ULONG64 RecAddr = TibInfo[0];
|
|
|
|
while (RecAddr != (ULONG64)-1)
|
|
{
|
|
ULONG64 Record[2];
|
|
|
|
if (RecAddr < TibInfo[2] ||
|
|
RecAddr + 8 > TibInfo[1] ||
|
|
(RecAddr & 3))
|
|
{
|
|
ExtErr("Invalid exception stack at %p\n", RecAddr);
|
|
return;
|
|
}
|
|
|
|
if (g_ExtData->ReadPointersVirtual(2, RecAddr, Record) != S_OK)
|
|
{
|
|
ExtErr("Unable to read exception record at %p\n", RecAddr);
|
|
return;
|
|
}
|
|
|
|
char Sym[256];
|
|
ULONG64 Disp;
|
|
|
|
ExtOut("%p: ", RecAddr);
|
|
if (FAILED(g_ExtSymbols->
|
|
GetNameByOffset(Record[1], Sym, sizeof(Sym),
|
|
NULL, &Disp)) ||
|
|
!Sym[0])
|
|
{
|
|
ExtOut("%p\n", Record[1]);
|
|
}
|
|
else
|
|
{
|
|
ExtOut("%s+%I64x (%p)\n", Sym, Disp, Record[1]);
|
|
|
|
// Check and see if this is the CRT exception
|
|
// handling function because in that case we
|
|
// have to go through the CRT tables to
|
|
// find the real exception handler.
|
|
PSTR Scan = strchr(Sym, '!');
|
|
if (Scan)
|
|
{
|
|
while (*++Scan == '_')
|
|
{
|
|
// Empty.
|
|
}
|
|
if (!strcmp(Scan, "except_handler3"))
|
|
{
|
|
DumpCrtEhX86(RecAddr);
|
|
}
|
|
}
|
|
}
|
|
|
|
RecAddr = Record[0];
|
|
}
|
|
}
|
|
|
|
DECLARE_API( exchain )
|
|
{
|
|
INIT_API();
|
|
|
|
ULONG64 Teb;
|
|
|
|
if (g_ExtSystem->GetCurrentThreadTeb(&Teb) != S_OK)
|
|
{
|
|
ExtErr("Unable to get TEB address\n");
|
|
goto Exit;
|
|
}
|
|
|
|
switch(g_TargetMachine)
|
|
{
|
|
case IMAGE_FILE_MACHINE_I386:
|
|
DumpExceptionChainX86(Teb);
|
|
break;
|
|
default:
|
|
ExtErr("exchain is x86 only\n");
|
|
break;
|
|
}
|
|
|
|
Exit:
|
|
EXIT_API();
|
|
return S_OK;
|
|
}
|