942 lines
22 KiB
C++
942 lines
22 KiB
C++
/*++
|
|
|
|
Copyright (c) 1993-2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
exts.c
|
|
|
|
Abstract:
|
|
|
|
This file contains the generic routines and initialization code
|
|
for the kernel debugger extensions dll.
|
|
|
|
Environment:
|
|
|
|
User Mode
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
#include <ntverp.h>
|
|
|
|
//
|
|
// Valid for the lifetime of the debug session.
|
|
//
|
|
|
|
WINDBG_EXTENSION_APIS ExtensionApis;
|
|
ULONG TargetMachine;
|
|
BOOL Connected;
|
|
ULONG g_TargetClass;
|
|
|
|
//
|
|
// Valid only during an extension API call
|
|
//
|
|
|
|
PDEBUG_ADVANCED g_ExtAdvanced;
|
|
PDEBUG_CLIENT g_ExtClient;
|
|
PDEBUG_CONTROL g_ExtControl;
|
|
PDEBUG_DATA_SPACES g_ExtData;
|
|
PDEBUG_REGISTERS g_ExtRegisters;
|
|
PDEBUG_SYMBOLS2 g_ExtSymbols;
|
|
PDEBUG_SYSTEM_OBJECTS g_ExtSystem;
|
|
|
|
#define SKIP_WSPACE(s) while (*s && (*s == ' ' || *s == '\t')) {++s;}
|
|
|
|
// 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(IDebugControl),
|
|
(void **)&g_ExtControl)) != S_OK)
|
|
{
|
|
goto Fail;
|
|
}
|
|
if ((Status = Client->QueryInterface(__uuidof(IDebugDataSpaces),
|
|
(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(IDebugSymbols2),
|
|
(void **)&g_ExtSymbols)) != S_OK)
|
|
{
|
|
goto Fail;
|
|
}
|
|
if ((Status = Client->QueryInterface(__uuidof(IDebugSystemObjects),
|
|
(void **)&g_ExtSystem)) != 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_ExtControl);
|
|
EXT_RELEASE(g_ExtData);
|
|
EXT_RELEASE(g_ExtRegisters);
|
|
EXT_RELEASE(g_ExtSymbols);
|
|
EXT_RELEASE(g_ExtSystem);
|
|
}
|
|
|
|
// 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;
|
|
|
|
|
|
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 page size and PAE enable flag
|
|
//
|
|
|
|
if ((Hr = DebugClient->QueryInterface(__uuidof(IDebugDataSpaces),
|
|
(void **)&DebugDataSpaces)) == S_OK)
|
|
{
|
|
if ((Hr = DebugDataSpaces->ReadDebuggerData(
|
|
DEBUG_DATA_MmPageSize, &Page,
|
|
sizeof(Page), NULL)) == S_OK)
|
|
{
|
|
PageSize = (ULONG)(ULONG_PTR)Page;
|
|
}
|
|
|
|
DebugDataSpaces->Release();
|
|
}
|
|
//
|
|
// Get the architecture type.
|
|
//
|
|
|
|
if ((Hr = DebugClient->QueryInterface(__uuidof(IDebugControl),
|
|
(void **)&DebugControl)) == S_OK)
|
|
{
|
|
if ((Hr = DebugControl->GetActualProcessorType(
|
|
&TargetMachine)) == S_OK)
|
|
{
|
|
Connected = TRUE;
|
|
}
|
|
ULONG Qualifier;
|
|
if ((Hr = DebugControl->GetDebuggeeType(&g_TargetClass, &Qualifier)) == S_OK)
|
|
{
|
|
}
|
|
|
|
DebugControl->Release();
|
|
}
|
|
|
|
DebugClient->Release();
|
|
}
|
|
}
|
|
|
|
|
|
if (Notify == DEBUG_NOTIFY_SESSION_INACTIVE)
|
|
{
|
|
Connected = FALSE;
|
|
PageSize = 0;
|
|
TargetMachine = 0;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
extern "C"
|
|
void
|
|
CALLBACK
|
|
DebugExtensionUninitialize(void)
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
DllInit(
|
|
HANDLE hModule,
|
|
DWORD dwReason,
|
|
DWORD dwReserved
|
|
)
|
|
{
|
|
switch (dwReason) {
|
|
case DLL_THREAD_ATTACH:
|
|
break;
|
|
|
|
case DLL_THREAD_DETACH:
|
|
break;
|
|
|
|
case DLL_PROCESS_DETACH:
|
|
break;
|
|
|
|
case DLL_PROCESS_ATTACH:
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
CxrHelp()
|
|
{
|
|
dprintf("\n");
|
|
dprintf(" !cxr has been replaced with the new built-in debugger command .cxr\n");
|
|
dprintf(" !exr, !trap and !tss (both Kernelmode only) too are built-in . commands now\n");
|
|
dprintf(" 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");
|
|
}
|
|
|
|
DECLARE_API ( cxr )
|
|
{
|
|
CxrHelp();
|
|
return S_OK;
|
|
}
|
|
|
|
DECLARE_API ( exr )
|
|
{
|
|
CHAR Cmd[400];
|
|
|
|
dprintf("*** !exr obsolete: Use '.exr %s'\n", args);
|
|
|
|
sprintf(Cmd, ".exr %s", args);
|
|
if (Client &&
|
|
(ExtQuery(Client) == S_OK)) {
|
|
g_ExtControl->Execute(DEBUG_OUTCTL_ALL_CLIENTS |
|
|
DEBUG_OUTCTL_OVERRIDE_MASK |
|
|
DEBUG_OUTCTL_NOT_LOGGED,
|
|
Cmd, DEBUG_EXECUTE_DEFAULT );
|
|
|
|
ExtRelease();
|
|
return S_OK;
|
|
}
|
|
return E_FAIL;
|
|
|
|
}
|
|
|
|
|
|
HRESULT
|
|
PrintString(
|
|
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 PrintString(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 PrintString(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)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
if (AddrString)
|
|
{
|
|
b = ReadMemory(AddrString, &String, sizeof(String), NULL);
|
|
|
|
hResult = g_ExtData->ReadPointersVirtual(1,
|
|
AddrString + StringOffset,
|
|
&StrAddr);
|
|
}
|
|
|
|
EXIT_API();
|
|
|
|
|
|
if (StrAddr)
|
|
{
|
|
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;
|
|
}
|
|
|
|
typedef struct _LIST_TYPE_PARAMS {
|
|
PCHAR Command;
|
|
PCHAR CommandArgs;
|
|
ULONG FieldOffset;
|
|
ULONG nElement;
|
|
} LIST_TYPE_PARAMS, *PLIST_TYPE_PARAMS;
|
|
|
|
ULONG
|
|
ListCallback(
|
|
PFIELD_INFO Field,
|
|
PVOID Context
|
|
)
|
|
{
|
|
CHAR Execute[MAX_PATH];
|
|
PCHAR Buffer;
|
|
PLIST_TYPE_PARAMS pListParams = (PLIST_TYPE_PARAMS) Context;
|
|
|
|
|
|
// Execute command
|
|
sprintf(Execute,
|
|
"%s %I64lx %s",
|
|
pListParams->Command,
|
|
Field->address,// - pListParams->FieldOffset,
|
|
pListParams->CommandArgs);
|
|
g_ExtControl->Execute(DEBUG_OUTCTL_AMBIENT,Execute,DEBUG_EXECUTE_DEFAULT);
|
|
dprintf("\n");
|
|
|
|
if (CheckControlC()) {
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
!list [-type <Type>] [-x "<Command>"] <address>
|
|
|
|
This dumps out a list starting at ginve <address>
|
|
|
|
<Type> If types is specified, it'll list that particular type accoring to
|
|
the given field.
|
|
|
|
<Command> If specified it'll execute the command for every list element.
|
|
|
|
*/
|
|
DECLARE_API ( list )
|
|
{
|
|
CHAR Command[MAX_PATH], CmdArgs[MAX_PATH];
|
|
CHAR Type[MAX_PATH];
|
|
CHAR Field[MAX_PATH];
|
|
ULONG64 Start;
|
|
ULONG i, Offset, Commandlen;
|
|
ULONG64 Next, Current;
|
|
LIST_TYPE_PARAMS ListParams;
|
|
|
|
ZeroMemory(Type, sizeof(Type));
|
|
ZeroMemory(Field, sizeof(Field));
|
|
ZeroMemory(Command, sizeof(Command));
|
|
CmdArgs[0] = 0;
|
|
Start = 0;
|
|
while (args && *args) {
|
|
SKIP_WSPACE(args);
|
|
|
|
if (*args == '-' || *args == '/') {
|
|
++args;
|
|
if (*args == 't') {
|
|
PCHAR Dot;
|
|
|
|
args++;
|
|
SKIP_WSPACE(args);
|
|
|
|
Dot = strchr(args, '.');
|
|
if (Dot) {
|
|
strncpy(Type, args, (ULONG) (ULONG_PTR) (Dot-args));
|
|
|
|
Dot++;
|
|
i=0;
|
|
while (*Dot && (i < MAX_PATH-1) && (*Dot != ' ') && (*Dot != '\t'))
|
|
Field[i++] = *Dot++;
|
|
args = Dot;
|
|
}
|
|
} else if (*args == 'x') {
|
|
++args;
|
|
|
|
SKIP_WSPACE(args);
|
|
i=0;
|
|
if (*args == '"') {
|
|
++args;
|
|
while (*args && (i < MAX_PATH-1) && (*args != '"'))
|
|
Command[i++] = *args++;
|
|
++args;
|
|
} else {
|
|
dprintf("Invalid command specification. See !list -h\n");
|
|
return E_INVALIDARG;
|
|
}
|
|
} else if (*args == 'a') {
|
|
++args;
|
|
|
|
SKIP_WSPACE(args);
|
|
i=0;
|
|
if (*args == '"') {
|
|
++args;
|
|
while (*args && (i < MAX_PATH-1) && (*args != '"'))
|
|
CmdArgs[i++] = *args++;
|
|
++args;
|
|
CmdArgs[i] = 0;
|
|
} else {
|
|
dprintf("Invalid command argument specification. See !list -h\n");
|
|
return E_INVALIDARG;
|
|
}
|
|
} else if (*args == 'h' || *args == '?') {
|
|
dprintf("Usage: !list -t [mod!]TYPE.Field <Start-Address>\n"
|
|
" -x \"Command-for-each-element\"\n"
|
|
" -a \"Command-arguments\"\n"
|
|
" -h\n"
|
|
"Command after -x is executed for each list element. Its first argument is\n"
|
|
"list-head address and remaining arguments are specified after -a\n"
|
|
"eg. !list -t MYTYPE.l.Flink -x \"dd\" -a \"l2\" 0x6bc00\n"
|
|
" dumps first 2 dwords in list of MYTYPE at 0x6bc00\n\n"
|
|
);
|
|
return S_OK;
|
|
} else {
|
|
dprintf("Invalid flag -%c in !list\n", *args);
|
|
return E_INVALIDARG;
|
|
}
|
|
} else {
|
|
if (!GetExpressionEx(args, &Start, &args)) {
|
|
dprintf("Invalid expression in %s\n", args);
|
|
return E_FAIL;
|
|
}
|
|
}
|
|
}
|
|
|
|
Offset = 0;
|
|
if (!Command[0]) {
|
|
strcat(Command, "dp");
|
|
}
|
|
|
|
if (Type[0] && Field[0]) {
|
|
|
|
if (GetFieldOffset(Type, Field, &Offset)) {
|
|
dprintf("GetFieldOffset failed for %s.%s\n", Type, Field);
|
|
return E_FAIL;
|
|
}
|
|
|
|
|
|
ListParams.Command = Command;
|
|
ListParams.CommandArgs = CmdArgs;
|
|
ListParams.FieldOffset = Offset;
|
|
ListParams.nElement = 0;
|
|
|
|
INIT_API();
|
|
|
|
ListType(Type, Start, FALSE, Field, (PVOID) &ListParams, &ListCallback );
|
|
|
|
EXIT_API();
|
|
return S_OK;
|
|
|
|
}
|
|
Current = Start;
|
|
Next = 0;
|
|
INIT_API();
|
|
|
|
while (Next != Start) {
|
|
CHAR Execute[MAX_PATH];
|
|
PCHAR Buffer;
|
|
|
|
// Execute command
|
|
sprintf(Execute, "%s %I64lx %s", Command, Current, CmdArgs);
|
|
g_ExtControl->Execute(DEBUG_OUTCTL_AMBIENT,Execute,DEBUG_EXECUTE_DEFAULT);
|
|
dprintf("\n");
|
|
|
|
if (!ReadPointer(Current + Offset, &Next)) {
|
|
dprintf("Cannot read next element at %p\n",
|
|
Current + Offset);
|
|
break;
|
|
}
|
|
if (!Next) {
|
|
break;
|
|
}
|
|
Next -= Offset;
|
|
Current = Next;
|
|
if (CheckControlC()) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
EXIT_API();
|
|
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
BOOL
|
|
AnalyzeExceptionPointer(
|
|
ULONG64 ExcepPtr
|
|
)
|
|
{
|
|
ULONG64 Exr, Cxr;
|
|
ULONG PtrSize= IsPtr64() ? 8 : 4;
|
|
|
|
if (!ReadPointer(ExcepPtr, &Exr) ||
|
|
!ReadPointer(ExcepPtr + PtrSize, &Cxr)) {
|
|
dprintf("Unable to read exception poointers at %p\n", ExcepPtr);
|
|
return FALSE;
|
|
}
|
|
|
|
CHAR Cmd[100];
|
|
|
|
sprintf(Cmd, ".exr %I64lx", Exr);
|
|
g_ExtControl->Execute(DEBUG_OUTCTL_ALL_CLIENTS |
|
|
DEBUG_OUTCTL_OVERRIDE_MASK,
|
|
Cmd, DEBUG_EXECUTE_DEFAULT);
|
|
|
|
sprintf(Cmd, ".cxr %I64lx", Cxr);
|
|
g_ExtControl->Execute(DEBUG_OUTCTL_ALL_CLIENTS |
|
|
DEBUG_OUTCTL_OVERRIDE_MASK,
|
|
Cmd, DEBUG_EXECUTE_DEFAULT);
|
|
g_ExtControl->Execute(DEBUG_OUTCTL_ALL_CLIENTS |
|
|
DEBUG_OUTCTL_OVERRIDE_MASK,
|
|
"kb", DEBUG_EXECUTE_DEFAULT);
|
|
g_ExtControl->Execute(DEBUG_OUTCTL_ALL_CLIENTS |
|
|
DEBUG_OUTCTL_OVERRIDE_MASK,
|
|
".cxr", DEBUG_EXECUTE_DEFAULT);
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
IsFunctionAddr(
|
|
ULONG64 IP,
|
|
PSTR FuncName
|
|
)
|
|
// Check if IP is in the function FuncName
|
|
{
|
|
CHAR Buffer[MAX_PATH], *scan, *FnIP;
|
|
ULONG64 Disp;
|
|
|
|
GetSymbol(IP, Buffer, &Disp);
|
|
|
|
if (scan = strchr(Buffer, '!')) {
|
|
FnIP = scan+1;
|
|
while (*FnIP == '_') ++FnIP;
|
|
} else {
|
|
FnIP = &Buffer[0];
|
|
}
|
|
|
|
return !strncmp(FnIP, FuncName, strlen(FuncName));
|
|
}
|
|
|
|
BOOL
|
|
GetExceptionPointerFromStack(
|
|
PULONG64 pExcePtr
|
|
)
|
|
{
|
|
#define MAX_STACK_FRAMES 50
|
|
DEBUG_STACK_FRAME stk[MAX_STACK_FRAMES], *Frame;
|
|
ULONG nFrames;
|
|
|
|
if (g_ExtControl->GetStackTrace(0, 0, 0, stk, MAX_STACK_FRAMES, &nFrames ) == S_OK) {
|
|
for (unsigned int i=0; i<nFrames; ++i) {
|
|
if (IsFunctionAddr(stk[i].InstructionOffset, "UnhandledExceptionFilter")) {
|
|
*pExcePtr = stk[i].Params[0];
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/*****************************************************************************************
|
|
*
|
|
* Do analysis for a given exception pointer
|
|
*
|
|
******************************************************************************************/
|
|
|
|
DECLARE_API ( analyzeexcep )
|
|
{
|
|
ULONG64 ExcepPtr=0;
|
|
INIT_API();
|
|
|
|
GetExpressionEx(args, &ExcepPtr, &args);
|
|
|
|
if (!ExcepPtr) {
|
|
GetExceptionPointerFromStack(&ExcepPtr);
|
|
}
|
|
if (ExcepPtr) {
|
|
AnalyzeExceptionPointer(ExcepPtr);
|
|
} else {
|
|
dprintf("Cannot get exception pointer\n");
|
|
}
|
|
EXIT_API();
|
|
return S_OK;
|
|
}
|