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.
3405 lines
87 KiB
3405 lines
87 KiB
//----------------------------------------------------------------------------
|
|
//
|
|
// Dot command parsing.
|
|
//
|
|
// Copyright (C) Microsoft Corporation, 1990-2002.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#include "ntsdp.hpp"
|
|
#include <time.h>
|
|
#include <locale.h>
|
|
#include <dbgver.h>
|
|
|
|
#define CRASH_BUGCHECK_CODE 0xDEADDEAD
|
|
|
|
void
|
|
ParseSeparateCurrentProcess(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
HRESULT Status;
|
|
ULONG Mode;
|
|
char Desc[128];
|
|
|
|
if (g_SymOptions & SYMOPT_SECURE)
|
|
{
|
|
error(NOTSECURE);
|
|
}
|
|
|
|
if (!strcmp(Cmd->Name, "abandon"))
|
|
{
|
|
Mode = SEP_ABANDON;
|
|
}
|
|
else if (!strcmp(Cmd->Name, "detach"))
|
|
{
|
|
Mode = SEP_DETACH;
|
|
}
|
|
else if (!strcmp(Cmd->Name, "kill"))
|
|
{
|
|
Mode = SEP_TERMINATE;
|
|
}
|
|
else
|
|
{
|
|
error(SYNTAX);
|
|
}
|
|
|
|
if (IS_DUMP_TARGET(g_Target))
|
|
{
|
|
// This will also cause the target to get cleaned
|
|
// up as the system will release the only reference.
|
|
dprintf("Closing dump file\n");
|
|
g_Target->DebuggeeReset(DEBUG_SESSION_END, FALSE);
|
|
delete g_Target;
|
|
SetToAnyLayers(TRUE);
|
|
}
|
|
else if ((Status = SeparateCurrentProcess(Mode, Desc)) == S_OK)
|
|
{
|
|
dprintf("%s\n", Desc);
|
|
}
|
|
else if (Status == E_NOTIMPL)
|
|
{
|
|
dprintf("The system doesn't support %s\n", Cmd->Name);
|
|
}
|
|
else
|
|
{
|
|
dprintf("Unable to %s, %s\n",
|
|
Cmd->Name, FormatStatusCode(Status));
|
|
}
|
|
}
|
|
|
|
ULONG64
|
|
ParseConnectProcessServer(void)
|
|
{
|
|
HRESULT Status;
|
|
PSTR Srv;
|
|
PUSER_DEBUG_SERVICES Services;
|
|
CHAR Save;
|
|
|
|
Srv = StringValue(STRV_SPACE_IS_SEPARATOR |
|
|
STRV_TRIM_TRAILING_SPACE, &Save);
|
|
Status = DbgRpcConnectServer(Srv, &IID_IUserDebugServices,
|
|
(IUnknown**)&Services);
|
|
*g_CurCmd = Save;
|
|
|
|
if (Status != S_OK)
|
|
{
|
|
ErrOut("Unable to connect to '%s', %s\n \"%s\"\n",
|
|
Srv, FormatStatusCode(Status),
|
|
FormatStatus(Status));
|
|
error(NOTFOUND);
|
|
}
|
|
|
|
return (ULONG_PTR)Services;
|
|
}
|
|
|
|
void
|
|
DotAttach(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
HRESULT Status;
|
|
ULONG Pid;
|
|
ULONG Flags = DEBUG_ATTACH_DEFAULT;
|
|
ULONG64 Server = 0;
|
|
|
|
if (g_SessionThread != GetCurrentThreadId())
|
|
{
|
|
error(BADTHREAD);
|
|
}
|
|
if (g_SymOptions & SYMOPT_SECURE)
|
|
{
|
|
error(NOTSECURE);
|
|
}
|
|
|
|
while (PeekChar() == '-' || *g_CurCmd == '/')
|
|
{
|
|
switch(*++g_CurCmd)
|
|
{
|
|
case 'b':
|
|
Flags |= DEBUG_ATTACH_INVASIVE_NO_INITIAL_BREAK;
|
|
break;
|
|
case 'e':
|
|
Flags = DEBUG_ATTACH_EXISTING;
|
|
break;
|
|
case 'p':
|
|
if (!strncmp(g_CurCmd, "premote", 7) && isspace(g_CurCmd[7]))
|
|
{
|
|
g_CurCmd += 7;
|
|
Server = ParseConnectProcessServer();
|
|
}
|
|
else
|
|
{
|
|
error(SYNTAX);
|
|
}
|
|
break;
|
|
case 'r':
|
|
Flags |= DEBUG_ATTACH_INVASIVE_RESUME_PROCESS;
|
|
break;
|
|
case 'v':
|
|
Flags = DEBUG_ATTACH_NONINVASIVE;
|
|
if (g_CurCmd[1] == 'r')
|
|
{
|
|
g_CurCmd++;
|
|
Flags |= DEBUG_ATTACH_NONINVASIVE_NO_SUSPEND;
|
|
}
|
|
break;
|
|
default:
|
|
ErrOut("Unknown option '%c'\n", *g_CurCmd);
|
|
break;
|
|
}
|
|
|
|
g_CurCmd++;
|
|
}
|
|
|
|
Pid = (ULONG)GetExpression();
|
|
|
|
PPENDING_PROCESS Pending;
|
|
TargetInfo* Target;
|
|
BOOL CreatedTarget;
|
|
|
|
if ((Status = UserInitialize(Client, Server,
|
|
&Target, &CreatedTarget)) == S_OK)
|
|
{
|
|
Status = Target->StartAttachProcess(Pid, Flags, &Pending);
|
|
if (Status == S_OK)
|
|
{
|
|
dprintf("Attach will occur on next execution\n");
|
|
}
|
|
else if (CreatedTarget)
|
|
{
|
|
delete Target;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ErrOut("Unable to initialize target, %s\n",
|
|
FormatStatusCode(Status));
|
|
}
|
|
|
|
if (Server)
|
|
{
|
|
((PUSER_DEBUG_SERVICES)(ULONG_PTR)Server)->Release();
|
|
}
|
|
}
|
|
|
|
void
|
|
DotBpCmds(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
ULONG Flags = 0;
|
|
ProcessInfo* Process = g_Process;
|
|
ULONG Id;
|
|
|
|
while (PeekChar() == '-')
|
|
{
|
|
switch(*(++g_CurCmd))
|
|
{
|
|
case '1':
|
|
Flags |= BPCMDS_ONE_LINE;
|
|
break;
|
|
case 'd':
|
|
Flags |= BPCMDS_FORCE_DISABLE;
|
|
break;
|
|
case 'e':
|
|
Flags |= BPCMDS_EXPR_ONLY;
|
|
break;
|
|
case 'm':
|
|
Flags |= BPCMDS_MODULE_HINT;
|
|
break;
|
|
case 'p':
|
|
g_CurCmd++;
|
|
Id = (ULONG)GetTermExpression("Process ID missing from");
|
|
Process = FindAnyProcessByUserId(Id);
|
|
g_CurCmd--;
|
|
break;
|
|
default:
|
|
dprintf("Unknown option '%c'\n", *g_CurCmd);
|
|
break;
|
|
}
|
|
|
|
g_CurCmd++;
|
|
}
|
|
|
|
if (!Process)
|
|
{
|
|
error(BADPROCESS);
|
|
}
|
|
|
|
ListBreakpointsAsCommands(Client, Process, Flags);
|
|
}
|
|
|
|
void
|
|
DotBpSync(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
if (PeekChar() && *g_CurCmd != ';')
|
|
{
|
|
if (GetExpression())
|
|
{
|
|
g_EngOptions |= DEBUG_ENGOPT_SYNCHRONIZE_BREAKPOINTS;
|
|
}
|
|
else
|
|
{
|
|
g_EngOptions &= ~DEBUG_ENGOPT_SYNCHRONIZE_BREAKPOINTS;
|
|
}
|
|
}
|
|
|
|
dprintf("Breakpoint synchronization %s\n",
|
|
(g_EngOptions & DEBUG_ENGOPT_SYNCHRONIZE_BREAKPOINTS) ?
|
|
"enabled" : "disabled");
|
|
}
|
|
|
|
void
|
|
DotBreakin(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
if (g_DebuggerPlatformId != VER_PLATFORM_WIN32_NT)
|
|
{
|
|
ErrOut(".breakin not supported on this platform\n");
|
|
return;
|
|
}
|
|
|
|
if (g_Target && g_Target->m_SystemVersion <= NT_SVER_NT4)
|
|
{
|
|
// SysDbgBreakPoint isn't supported, so
|
|
// try to use user32's PrivateKDBreakPoint.
|
|
if (InitDynamicCalls(&g_User32CallsDesc) == S_OK &&
|
|
g_User32Calls.PrivateKDBreakPoint != NULL)
|
|
{
|
|
g_User32Calls.PrivateKDBreakPoint();
|
|
}
|
|
else
|
|
{
|
|
ErrOut(".breakin is not supported on this system\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NTSTATUS NtStatus;
|
|
|
|
NtStatus = g_NtDllCalls.NtSystemDebugControl
|
|
(SysDbgBreakPoint, NULL, 0, NULL, 0, NULL);
|
|
if (NtStatus == STATUS_ACCESS_DENIED)
|
|
{
|
|
ErrOut(".breakin requires debug privilege\n");
|
|
}
|
|
else if (NtStatus == STATUS_INVALID_INFO_CLASS)
|
|
{
|
|
ErrOut(".breakin is not supported on this system\n");
|
|
}
|
|
else if (!NT_SUCCESS(NtStatus))
|
|
{
|
|
ErrOut(".breakin failed, 0x%X\n", NtStatus);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
DotBugCheck(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
ULONG Code;
|
|
ULONG64 Args[4];
|
|
|
|
if (g_Target->ReadBugCheckData(&Code, Args) == S_OK)
|
|
{
|
|
dprintf("Bugcheck code %08X\n", Code);
|
|
dprintf("Arguments %s %s %s %s\n",
|
|
FormatAddr64(Args[0]), FormatAddr64(Args[1]),
|
|
FormatAddr64(Args[2]), FormatAddr64(Args[3]));
|
|
}
|
|
else
|
|
{
|
|
dprintf("Unable to read bugcheck data\n");
|
|
}
|
|
}
|
|
|
|
void
|
|
DotCache(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
if (IS_REMOTE_KERNEL_TARGET(g_Target) ||
|
|
(IS_LIVE_USER_TARGET(g_Target) &&
|
|
!((LiveUserTargetInfo*)g_Target)->m_Local))
|
|
{
|
|
if (!g_Process)
|
|
{
|
|
error(BADPROCESS);
|
|
}
|
|
|
|
g_Process->m_VirtualCache.ParseCommands();
|
|
}
|
|
else
|
|
{
|
|
error(SESSIONNOTSUP);
|
|
}
|
|
}
|
|
|
|
void
|
|
DotChain(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
OutputExtensions(Client, FALSE);
|
|
}
|
|
|
|
void
|
|
DotChildDbg(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
if (!IS_LIVE_USER_TARGET(g_Target))
|
|
{
|
|
error(SESSIONNOTSUP);
|
|
}
|
|
if (g_Process == NULL)
|
|
{
|
|
error(BADTHREAD);
|
|
}
|
|
|
|
HRESULT Status;
|
|
PUSER_DEBUG_SERVICES Services =
|
|
((LiveUserTargetInfo*)g_Target)->m_Services;
|
|
ULONG Opts;
|
|
|
|
Opts = g_Process->m_Options;
|
|
|
|
if (PeekChar() && *g_CurCmd != ';')
|
|
{
|
|
ULONG64 Val = GetExpression();
|
|
if (Val)
|
|
{
|
|
Opts &= ~DEBUG_PROCESS_ONLY_THIS_PROCESS;
|
|
}
|
|
else
|
|
{
|
|
Opts |= DEBUG_PROCESS_ONLY_THIS_PROCESS;
|
|
}
|
|
|
|
if ((Status = Services->SetProcessOptions(g_Process->m_SysHandle,
|
|
Opts)) != S_OK)
|
|
{
|
|
if (Status == E_NOTIMPL)
|
|
{
|
|
ErrOut("The system doesn't support changing the flag\n");
|
|
}
|
|
else
|
|
{
|
|
ErrOut("Unable to set process options, %s\n",
|
|
FormatStatusCode(Status));
|
|
}
|
|
return;
|
|
}
|
|
|
|
g_Process->m_Options = Opts;
|
|
}
|
|
|
|
dprintf("Processes created by the current process will%s be debugged\n",
|
|
(Opts & DEBUG_PROCESS_ONLY_THIS_PROCESS) ? " not" : "");
|
|
}
|
|
|
|
void
|
|
DotClients(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
DebugClient* Cur;
|
|
|
|
for (Cur = g_Clients; Cur != NULL; Cur = Cur->m_Next)
|
|
{
|
|
if (Cur->m_Flags & CLIENT_PRIMARY)
|
|
{
|
|
dprintf("%s, last active %s",
|
|
Cur->m_Identity, ctime(&Cur->m_LastActivity));
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
DotCloseHandle(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
HRESULT Status;
|
|
BOOL All = FALSE;
|
|
ULONG64 Handle;
|
|
ULONG64 Dup;
|
|
ULONG HandleCount;
|
|
|
|
if (!g_Process)
|
|
{
|
|
error(BADPROCESS);
|
|
}
|
|
if (!IS_LIVE_USER_TARGET(g_Target))
|
|
{
|
|
error(SESSIONNOTSUP);
|
|
}
|
|
|
|
PUSER_DEBUG_SERVICES Services =
|
|
((LiveUserTargetInfo*)g_Target)->m_Services;
|
|
|
|
while (PeekChar() == '-' || *g_CurCmd == '/')
|
|
{
|
|
switch(*++g_CurCmd)
|
|
{
|
|
case 'a':
|
|
g_CurCmd++;
|
|
All = TRUE;
|
|
break;
|
|
default:
|
|
ErrOut("Unknown option '%c'\n", *g_CurCmd);
|
|
g_CurCmd++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!All)
|
|
{
|
|
Handle = GetExpression();
|
|
|
|
if ((Status = Services->
|
|
DuplicateHandle(g_Process->m_SysHandle, Handle,
|
|
SERVICE_HANDLE(GetCurrentProcess()), 0, FALSE,
|
|
DUPLICATE_CLOSE_SOURCE |
|
|
DUPLICATE_SAME_ACCESS, &Dup)) == S_OK)
|
|
{
|
|
dprintf("Closed %x\n", (ULONG)Handle);
|
|
}
|
|
else
|
|
{
|
|
ErrOut("Possibly closed %x, %s\n",
|
|
(ULONG)Handle, FormatStatusCode(Status));
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (Services->
|
|
ReadHandleData(g_Process->m_SysHandle, 0,
|
|
DEBUG_HANDLE_DATA_TYPE_HANDLE_COUNT,
|
|
&HandleCount, sizeof(HandleCount), NULL) != S_OK)
|
|
{
|
|
ErrOut("Unable to get handle count\n");
|
|
return;
|
|
}
|
|
|
|
dprintf("0x%x handles to scan for...\n", HandleCount);
|
|
|
|
Handle = 4;
|
|
while (HandleCount)
|
|
{
|
|
if (CheckUserInterrupt())
|
|
{
|
|
WarnOut("-- Interrupted\n");
|
|
break;
|
|
}
|
|
|
|
if ((Status = Services->
|
|
DuplicateHandle(g_Process->m_SysHandle, Handle,
|
|
SERVICE_HANDLE(GetCurrentProcess()), 0, FALSE,
|
|
DUPLICATE_CLOSE_SOURCE |
|
|
DUPLICATE_SAME_ACCESS, &Dup)) == S_OK)
|
|
{
|
|
dprintf(" closed %x, 0x%x remaining\n",
|
|
(ULONG)Handle, --HandleCount);
|
|
}
|
|
|
|
Handle += 4;
|
|
}
|
|
}
|
|
|
|
void
|
|
DotContext(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
if (!g_Machine)
|
|
{
|
|
error(BADTHREAD);
|
|
}
|
|
|
|
if (PeekChar() && *g_CurCmd != ';')
|
|
{
|
|
ULONG64 Base = GetExpression();
|
|
ULONG NextIdx;
|
|
|
|
if (g_Machine->SetPageDirectory(g_Thread, PAGE_DIR_USER, Base,
|
|
&NextIdx) != S_OK)
|
|
{
|
|
WarnOut("WARNING: Unable to reset page directory base\n");
|
|
}
|
|
|
|
// Flush the cache as anything we read from user mode is
|
|
// no longer valid
|
|
g_Process->m_VirtualCache.Empty();
|
|
|
|
if (Base && !g_Process->m_VirtualCache.m_ForceDecodePTEs &&
|
|
IS_REMOTE_KERNEL_TARGET(g_Target))
|
|
{
|
|
WarnOut("WARNING: "
|
|
".cache forcedecodeptes is not enabled\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dprintf("User-mode page directory base is %I64x\n",
|
|
g_Machine->m_PageDirectories[PAGE_DIR_USER]);
|
|
}
|
|
}
|
|
|
|
void
|
|
DotCorStack(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
if (PeekChar() && *g_CurCmd != ';')
|
|
{
|
|
while (PeekChar() == '-' || *g_CurCmd == '/')
|
|
{
|
|
switch(*++g_CurCmd)
|
|
{
|
|
case 'd':
|
|
g_DebugCorStack = !g_DebugCorStack;
|
|
g_CurCmd++;
|
|
break;
|
|
default:
|
|
ErrOut("Unknown option '%c'\n", *g_CurCmd);
|
|
g_CurCmd++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
g_AllowCorStack = GetExpression() != 0;
|
|
}
|
|
|
|
dprintf("COR-assisted stack walking %s\n",
|
|
g_AllowCorStack ? "enabled" : "disabled");
|
|
}
|
|
|
|
void
|
|
DotCrash(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
if (PeekChar())
|
|
{
|
|
error(SYNTAX);
|
|
}
|
|
|
|
g_LastCommand[0] = '\0';
|
|
g_Target->Crash(CRASH_BUGCHECK_CODE);
|
|
// Go back to waiting for a state change to
|
|
// receive the bugcheck exception.
|
|
g_CmdState = 'e';
|
|
NotifyChangeEngineState(DEBUG_CES_EXECUTION_STATUS,
|
|
DEBUG_STATUS_GO, TRUE);
|
|
}
|
|
|
|
void
|
|
DotCreate(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
HRESULT Status;
|
|
PPENDING_PROCESS Pending;
|
|
PSTR CmdLine;
|
|
PWSTR CmdLineW;
|
|
CHAR Save;
|
|
ULONG64 Server = 0;
|
|
|
|
if (g_SessionThread != GetCurrentThreadId())
|
|
{
|
|
error(BADTHREAD);
|
|
}
|
|
if (g_SymOptions & SYMOPT_SECURE)
|
|
{
|
|
error(NOTSECURE);
|
|
}
|
|
|
|
while (PeekChar() == '-' || *g_CurCmd == '/')
|
|
{
|
|
switch(*++g_CurCmd)
|
|
{
|
|
case 'p':
|
|
if (!strncmp(g_CurCmd, "premote", 7) && isspace(g_CurCmd[7]))
|
|
{
|
|
g_CurCmd += 7;
|
|
Server = ParseConnectProcessServer();
|
|
}
|
|
else
|
|
{
|
|
error(SYNTAX);
|
|
}
|
|
break;
|
|
default:
|
|
ErrOut("Unknown option '%c'\n", *g_CurCmd);
|
|
break;
|
|
}
|
|
|
|
g_CurCmd++;
|
|
}
|
|
|
|
CmdLine = StringValue(STRV_TRIM_TRAILING_SPACE, &Save);
|
|
if (AnsiToWide(CmdLine, &CmdLineW) != S_OK)
|
|
{
|
|
ErrOut("Out of memory\n");
|
|
}
|
|
else
|
|
{
|
|
TargetInfo* Target;
|
|
BOOL CreatedTarget;
|
|
|
|
if ((Status = UserInitialize(Client, Server,
|
|
&Target, &CreatedTarget)) == S_OK)
|
|
{
|
|
Status = Target->
|
|
StartCreateProcess(CmdLineW, DEBUG_ONLY_THIS_PROCESS,
|
|
NULL, NULL, &Pending);
|
|
if (Status == S_OK)
|
|
{
|
|
dprintf("Create will proceed with next execution\n");
|
|
}
|
|
else if (CreatedTarget)
|
|
{
|
|
delete Target;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ErrOut("Unable to initialize target, %s\n",
|
|
FormatStatusCode(Status));
|
|
}
|
|
|
|
FreeWide(CmdLineW);
|
|
}
|
|
|
|
if (Server)
|
|
{
|
|
((PUSER_DEBUG_SERVICES)(ULONG_PTR)Server)->Release();
|
|
}
|
|
|
|
*g_CurCmd = Save;
|
|
}
|
|
|
|
void
|
|
DotCreateDir(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
PSTR Str;
|
|
char Save;
|
|
|
|
if (PeekChar() && *g_CurCmd != ';')
|
|
{
|
|
PWSTR NewStr;
|
|
|
|
Str = StringValue(STRV_TRIM_TRAILING_SPACE |
|
|
STRV_ALLOW_EMPTY_STRING, &Save);
|
|
if (!*Str)
|
|
{
|
|
if (g_StartProcessDir)
|
|
{
|
|
FreeWide(g_StartProcessDir);
|
|
}
|
|
g_StartProcessDir = NULL;
|
|
}
|
|
else if (AnsiToWide(Str, &NewStr) != S_OK)
|
|
{
|
|
ErrOut("Out of memory\n");
|
|
}
|
|
else
|
|
{
|
|
if (g_StartProcessDir)
|
|
{
|
|
FreeWide(g_StartProcessDir);
|
|
}
|
|
g_StartProcessDir = NewStr;
|
|
}
|
|
}
|
|
|
|
dprintf("Process creation dir: %ws\n",
|
|
g_StartProcessDir ? g_StartProcessDir : L"<default>");
|
|
}
|
|
|
|
void
|
|
DotCxr(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
ULONG Flags = 0;
|
|
ULONG64 ContextBase = 0;
|
|
|
|
if (!g_Machine)
|
|
{
|
|
error(BADTHREAD);
|
|
}
|
|
|
|
if (PeekChar() && *g_CurCmd != ';')
|
|
{
|
|
ContextBase = GetExpression();
|
|
}
|
|
|
|
if (ContextBase)
|
|
{
|
|
SetAndOutputVirtualContext(ContextBase,
|
|
REGALL_INT32 | REGALL_INT64 |
|
|
(g_Machine->m_ExecTypes[0] ==
|
|
IMAGE_FILE_MACHINE_I386 ?
|
|
REGALL_EXTRA0 : 0));
|
|
}
|
|
else if (GetCurrentScopeContext())
|
|
{
|
|
dprintf("Resetting default context\n");
|
|
ResetCurrentScope();
|
|
}
|
|
}
|
|
|
|
void
|
|
DotDrivers(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
PCSTR ArgsRet;
|
|
|
|
g_Target->Reload(g_Thread, "-l", &ArgsRet);
|
|
}
|
|
|
|
void
|
|
DotDumpCab(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
char Save;
|
|
PCSTR CabName;
|
|
ULONG Flags = DEBUG_FORMAT_WRITE_CAB;
|
|
|
|
if (!IS_DUMP_TARGET(g_Target))
|
|
{
|
|
error(TARGETNOTSUP);
|
|
}
|
|
|
|
while (PeekChar() == '-' || *g_CurCmd == '/')
|
|
{
|
|
switch(*++g_CurCmd)
|
|
{
|
|
case 'a':
|
|
Flags |= DEBUG_FORMAT_CAB_SECONDARY_FILES;
|
|
g_CurCmd++;
|
|
break;
|
|
default:
|
|
ErrOut("Unknown option '%c'\n", *g_CurCmd);
|
|
g_CurCmd++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
CabName = StringValue(STRV_TRIM_TRAILING_SPACE |
|
|
STRV_ESCAPED_CHARACTERS, &Save);
|
|
CreateCabFromDump(NULL, CabName, Flags);
|
|
}
|
|
|
|
void
|
|
DotDumpDebug(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
((DumpTargetInfo *)g_Target)->DumpDebug();
|
|
}
|
|
|
|
void
|
|
DotDumpOff(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
//
|
|
// Show the file offset for a VA.
|
|
//
|
|
|
|
ULONG64 Addr = GetExpression();
|
|
ULONG64 Offs;
|
|
ULONG File;
|
|
ULONG Avail;
|
|
|
|
Offs = ((DumpTargetInfo*)g_Target)->
|
|
VirtualToOffset(Addr, &File, &Avail);
|
|
dprintf("Virtual %s maps to file %d offset %I64x\n",
|
|
FormatAddr64(Addr), File, Offs);
|
|
}
|
|
|
|
void
|
|
DotDumpPOff(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
if (IS_KERNEL_SUMMARY_DUMP(g_Target) || IS_KERNEL_FULL_DUMP(g_Target))
|
|
{
|
|
//
|
|
// Show the file offset for a physical address.
|
|
//
|
|
|
|
ULONG64 Addr = GetExpression();
|
|
ULONG Avail;
|
|
|
|
dprintf("Physical %I64x maps to file offset %I64x\n",
|
|
Addr, ((KernelFullSumDumpTargetInfo *)g_Target)->
|
|
PhysicalToOffset(Addr, TRUE, &Avail));
|
|
}
|
|
else
|
|
{
|
|
error(SESSIONNOTSUP);
|
|
}
|
|
}
|
|
|
|
void
|
|
DotEcho(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
CHAR Save;
|
|
PSTR Str;
|
|
|
|
Str = StringValue(STRV_TRIM_TRAILING_SPACE |
|
|
STRV_ALLOW_EMPTY_STRING, &Save);
|
|
dprintf("%s\n", Str);
|
|
*g_CurCmd = Save;
|
|
}
|
|
|
|
void
|
|
DotEchoTimestamps(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
g_EchoEventTimestamps = !g_EchoEventTimestamps;
|
|
dprintf("Event timestamps are now %s\n",
|
|
g_EchoEventTimestamps ? "enabled" : "disabled");
|
|
}
|
|
|
|
void
|
|
DotEcxr(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
CROSS_PLATFORM_CONTEXT Context;
|
|
HRESULT Status;
|
|
|
|
Status = E_UNEXPECTED;
|
|
if (!g_Target || !g_Machine ||
|
|
(Status = g_Target->GetExceptionContext(&Context)) != S_OK)
|
|
{
|
|
ErrOut("Unable to get exception context, 0x%X\n", Status);
|
|
}
|
|
else
|
|
{
|
|
SetAndOutputContext(&Context, FALSE, REGALL_INT32 | REGALL_INT64 |
|
|
(g_Machine->
|
|
m_ExecTypes[0] == IMAGE_FILE_MACHINE_I386 ?
|
|
REGALL_EXTRA0 : 0));
|
|
}
|
|
}
|
|
|
|
void
|
|
DotEffMach(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
if (PeekChar() != ';' && *g_CurCmd)
|
|
{
|
|
PSTR Name = g_CurCmd;
|
|
|
|
while (*g_CurCmd && *g_CurCmd != ';' && !isspace(*g_CurCmd))
|
|
{
|
|
g_CurCmd++;
|
|
}
|
|
if (*g_CurCmd)
|
|
{
|
|
*g_CurCmd++ = 0;
|
|
}
|
|
|
|
ULONG Machine;
|
|
|
|
if (Name[0] == '.' && Name[1] == 0)
|
|
{
|
|
// Reset to target machine.
|
|
Machine = g_Target->m_MachineType;
|
|
}
|
|
else if (Name[0] == '#' && Name[1] == 0)
|
|
{
|
|
// Reset to executing machine.
|
|
Machine = g_EventMachine->m_ExecTypes[0];
|
|
}
|
|
else
|
|
{
|
|
for (Machine = 0; Machine < MACHIDX_COUNT; Machine++)
|
|
{
|
|
if (!_strcmpi(Name,
|
|
g_Target->m_Machines[Machine]->m_AbbrevName))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Machine >= MACHIDX_COUNT)
|
|
{
|
|
ErrOut("Unknown machine '%s'\n", Name);
|
|
return;
|
|
}
|
|
|
|
Machine = g_Target->m_Machines[Machine]->m_ExecTypes[0];
|
|
}
|
|
|
|
g_Target->SetEffMachine(Machine, TRUE);
|
|
g_Machine = g_Target->m_EffMachine;
|
|
}
|
|
|
|
if (g_Machine != NULL)
|
|
{
|
|
dprintf("Effective machine: %s (%s)\n",
|
|
g_Machine->m_FullName, g_Machine->m_AbbrevName);
|
|
}
|
|
else
|
|
{
|
|
dprintf("No effective machine\n");
|
|
}
|
|
}
|
|
|
|
void
|
|
DotEnableLongStatus(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
g_EnableLongStatus = (BOOL)GetExpression();
|
|
if (g_EnableLongStatus)
|
|
{
|
|
g_TypeOptions |= DEBUG_TYPEOPTS_LONGSTATUS_DISPLAY;
|
|
}
|
|
else
|
|
{
|
|
g_TypeOptions &= ~DEBUG_TYPEOPTS_LONGSTATUS_DISPLAY;
|
|
}
|
|
// Callback to update locals and watch window
|
|
NotifyChangeSymbolState(DEBUG_CSS_TYPE_OPTIONS, 0, NULL);
|
|
}
|
|
|
|
void
|
|
DotEnableUnicode(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
g_EnableUnicode = (BOOL)GetExpression();
|
|
if (g_EnableUnicode)
|
|
{
|
|
g_TypeOptions |= DEBUG_TYPEOPTS_UNICODE_DISPLAY;
|
|
}
|
|
else
|
|
{
|
|
g_TypeOptions &= ~DEBUG_TYPEOPTS_UNICODE_DISPLAY;
|
|
}
|
|
// Callback to update locals and watch window
|
|
NotifyChangeSymbolState(DEBUG_CSS_TYPE_OPTIONS, 0, NULL);
|
|
}
|
|
|
|
|
|
void
|
|
DotForceRadixOutput(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
g_PrintDefaultRadix = (BOOL) GetExpression();
|
|
if (g_PrintDefaultRadix)
|
|
{
|
|
g_TypeOptions |= DEBUG_TYPEOPTS_FORCERADIX_OUTPUT;
|
|
}
|
|
else
|
|
{
|
|
g_TypeOptions &= ~DEBUG_TYPEOPTS_FORCERADIX_OUTPUT;
|
|
}
|
|
// Callback to update locals and watch window
|
|
NotifyChangeSymbolState(DEBUG_CSS_TYPE_OPTIONS, 0, NULL);
|
|
}
|
|
|
|
void
|
|
DotEndSrv(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
if (DbgRpcDisableServer((ULONG)GetExpression()) == S_OK)
|
|
{
|
|
dprintf("Server told to exit. Actual exit may be delayed until\n"
|
|
"the next connection attempt.\n");
|
|
}
|
|
else
|
|
{
|
|
ErrOut("No such server\n");
|
|
}
|
|
}
|
|
|
|
void
|
|
DotEndPSrv(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
if (IS_LIVE_USER_TARGET(g_Target) &&
|
|
!((LiveUserTargetInfo*)g_Target)->m_Local)
|
|
{
|
|
((LiveUserTargetInfo*)g_Target)->m_Services->Uninitialize(TRUE);
|
|
dprintf("Server told to exit\n");
|
|
}
|
|
else
|
|
{
|
|
error(SESSIONNOTSUP);
|
|
}
|
|
}
|
|
|
|
void
|
|
DotEnumTag(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
HRESULT Status;
|
|
ULONG64 Handle;
|
|
|
|
if ((Status = Client->StartEnumTagged(&Handle)) != S_OK)
|
|
{
|
|
ErrOut("Unable to start enumeration, %s\n",
|
|
FormatStatusCode(Status));
|
|
return;
|
|
}
|
|
|
|
GUID Tag;
|
|
ULONG Size;
|
|
UCHAR Buffer[16];
|
|
ULONG Total;
|
|
ULONG Left;
|
|
ULONG Offs;
|
|
|
|
while (Client->GetNextTagged(Handle, &Tag, &Size) == S_OK)
|
|
{
|
|
if (CheckUserInterrupt())
|
|
{
|
|
dprintf("-- Interrupted\n");
|
|
break;
|
|
}
|
|
|
|
dprintf("{%08X-%04X-%04X-%02X%02X%02X%02X%02X%02X%02X%02X} "
|
|
"- 0x%x bytes\n",
|
|
Tag.Data1, Tag.Data2, Tag.Data3,
|
|
Tag.Data4[0], Tag.Data4[1], Tag.Data4[2],
|
|
Tag.Data4[3], Tag.Data4[4], Tag.Data4[5],
|
|
Tag.Data4[6], Tag.Data4[7], Size);
|
|
|
|
Offs = 0;
|
|
Left = Size;
|
|
|
|
while (Left > 0)
|
|
{
|
|
ULONG Req;
|
|
|
|
if (Left > sizeof(Buffer))
|
|
{
|
|
Req = sizeof(Buffer);
|
|
}
|
|
else
|
|
{
|
|
Req = Left;
|
|
}
|
|
|
|
if (Client->ReadTagged(&Tag, Offs, Buffer, Req, &Total) != S_OK)
|
|
{
|
|
ErrOut(" Unable to read data\n");
|
|
break;
|
|
}
|
|
|
|
ULONG i;
|
|
|
|
dprintf(" ");
|
|
|
|
for (i = 0; i < Req; i++)
|
|
{
|
|
dprintf(" %02X", Buffer[i]);
|
|
}
|
|
while (i < sizeof(Buffer))
|
|
{
|
|
dprintf(" ");
|
|
i++;
|
|
}
|
|
|
|
dprintf(" ");
|
|
|
|
for (i = 0; i < Req; i++)
|
|
{
|
|
dprintf("%c", isprint(Buffer[i]) ? Buffer[i] : '.');
|
|
}
|
|
|
|
dprintf("\n");
|
|
|
|
Offs += Req;
|
|
Left -= Req;
|
|
}
|
|
}
|
|
|
|
Client->EndEnumTagged(Handle);
|
|
}
|
|
|
|
void
|
|
DotEvents(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
HRESULT Status;
|
|
|
|
if (PeekChar() && *g_CurCmd != ';')
|
|
{
|
|
ULONG Relation = DEBUG_EINDEX_FROM_CURRENT;
|
|
ULONG Index = 1;
|
|
|
|
while (PeekChar() == '-' || *g_CurCmd == '/')
|
|
{
|
|
switch(*++g_CurCmd)
|
|
{
|
|
case '-':
|
|
g_CurCmd++;
|
|
Relation = DEBUG_EINDEX_FROM_CURRENT;
|
|
Index = -(LONG)GetExpression();
|
|
break;
|
|
case '+':
|
|
g_CurCmd++;
|
|
Relation = DEBUG_EINDEX_FROM_CURRENT;
|
|
Index = (ULONG)GetExpression();
|
|
break;
|
|
case 'e':
|
|
g_CurCmd++;
|
|
Relation = DEBUG_EINDEX_FROM_END;
|
|
Index = (ULONG)GetExpression();
|
|
break;
|
|
case 's':
|
|
g_CurCmd++;
|
|
Relation = DEBUG_EINDEX_FROM_START;
|
|
Index = (ULONG)GetExpression();
|
|
break;
|
|
default:
|
|
ErrOut("Unknown option '%c'\n", *g_CurCmd);
|
|
g_CurCmd++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((Status = Client->
|
|
SetNextEventIndex(Relation, Index, &Index)) != S_OK)
|
|
{
|
|
ErrOut("Unable to set next event index, %s\n",
|
|
FormatStatusCode(Status));
|
|
}
|
|
else
|
|
{
|
|
dprintf("Next event index will be %d\n", Index);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ULONG Num, Cur;
|
|
|
|
if (FAILED(Status = Client->GetNumberEvents(&Num)) ||
|
|
(Status = Client->GetCurrentEventIndex(&Cur)) != S_OK)
|
|
{
|
|
dprintf("Unable to get event information, %s\n",
|
|
FormatStatusCode(Status));
|
|
}
|
|
else
|
|
{
|
|
for (ULONG i = 0; i < Num; i++)
|
|
{
|
|
char Name[64];
|
|
|
|
if (FAILED(Client->
|
|
GetEventIndexDescription(i, DEBUG_EINDEX_NAME,
|
|
Name, sizeof(Name), NULL)))
|
|
{
|
|
strcpy(Name, "<Error>");
|
|
}
|
|
|
|
dprintf("%c%2d - %s\n",
|
|
i == Cur ? '.' : ' ', i, Name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
DotEventStr(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
if (!g_EventThread ||
|
|
!g_EventThread->m_EventStrings)
|
|
{
|
|
ErrOut("No event strings\n");
|
|
}
|
|
else
|
|
{
|
|
g_EventThread->OutputEventStrings();
|
|
}
|
|
}
|
|
|
|
void
|
|
DotExePath(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
if (PeekChar())
|
|
{
|
|
if (ChangePath(&g_ExecutableImageSearchPath,
|
|
g_CurCmd, Cmd->Name[7] == '+',
|
|
DEBUG_CSS_PATHS) != S_OK)
|
|
{
|
|
// This command uses the whole string.
|
|
*g_CurCmd = 0;
|
|
return;
|
|
}
|
|
|
|
*g_CurCmd = 0;
|
|
}
|
|
|
|
if (g_ExecutableImageSearchPath == NULL)
|
|
{
|
|
dprintf("No exectutable image search path\n");
|
|
}
|
|
else
|
|
{
|
|
dprintf("Executable image search path is: %s\n",
|
|
g_ExecutableImageSearchPath);
|
|
CheckPath(g_ExecutableImageSearchPath);
|
|
}
|
|
}
|
|
|
|
void
|
|
DotExpr(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
if (PeekChar() && *g_CurCmd != ';')
|
|
{
|
|
ULONG i;
|
|
char Save;
|
|
PCSTR Name;
|
|
|
|
while (PeekChar() == '-' || *g_CurCmd == '/')
|
|
{
|
|
switch(*++g_CurCmd)
|
|
{
|
|
case 'q':
|
|
g_CurCmd++;
|
|
dprintf("Available expression evaluators:\n");
|
|
for (i = 0; i < EVAL_COUNT; i++)
|
|
{
|
|
EvalExpression* Eval = GetEvaluator(i, FALSE);
|
|
dprintf("%s - %s\n",
|
|
Eval->m_AbbrevName,
|
|
Eval->m_FullName);
|
|
ReleaseEvaluator(Eval);
|
|
}
|
|
dprintf("\n");
|
|
break;
|
|
case 's':
|
|
g_CurCmd++;
|
|
Name = StringValue(STRV_SPACE_IS_SEPARATOR, &Save);
|
|
if (SetExprSyntaxByName(Name) != S_OK)
|
|
{
|
|
dprintf("No evaluator named '%s'\n", Name);
|
|
}
|
|
*g_CurCmd = Save;
|
|
break;
|
|
default:
|
|
ErrOut("Unknown option '%c'\n", *g_CurCmd);
|
|
g_CurCmd++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
EvalExpression* Eval = GetCurEvaluator();
|
|
dprintf("Current expression evaluator: %s - %s\n",
|
|
Eval->m_AbbrevName, Eval->m_FullName);
|
|
ReleaseEvaluator(Eval);
|
|
}
|
|
|
|
void
|
|
DotExr(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
ULONG64 ExrAddress = GetExpression();
|
|
ULONG i;
|
|
char Buffer[256];
|
|
ULONG64 Displacement;
|
|
EXCEPTION_RECORD64 Exr64;
|
|
EXCEPTION_RECORD32 Exr32;
|
|
EXCEPTION_RECORD64 *Exr = &Exr64;
|
|
HRESULT Status = S_OK;
|
|
EVENT_FILTER* Filter;
|
|
|
|
if (ExrAddress == (ULONG64) -1)
|
|
{
|
|
if (g_LastEventType == DEBUG_EVENT_EXCEPTION)
|
|
{
|
|
Exr64 = g_LastEventInfo.Exception.ExceptionRecord;
|
|
}
|
|
else
|
|
{
|
|
ErrOut("Last event was not an exception\n");
|
|
return;
|
|
}
|
|
}
|
|
else if (g_Target->m_Machine->m_Ptr64)
|
|
{
|
|
Status = CurReadAllVirtual(ExrAddress, &Exr64, sizeof(Exr64));
|
|
}
|
|
else
|
|
{
|
|
Status = CurReadAllVirtual(ExrAddress, &Exr32, sizeof(Exr32));
|
|
ExceptionRecord32To64(&Exr32, &Exr64);
|
|
}
|
|
if (Status != S_OK)
|
|
{
|
|
dprintf64("Cannot read Exception record @ %p\n", ExrAddress);
|
|
return;
|
|
}
|
|
|
|
GetSymbol(Exr->ExceptionAddress, &Buffer[0], DIMA(Buffer),
|
|
&Displacement);
|
|
|
|
if (*Buffer)
|
|
{
|
|
dprintf64("ExceptionAddress: %p (%s",
|
|
Exr->ExceptionAddress,
|
|
Buffer);
|
|
if (Displacement)
|
|
{
|
|
dprintf64("+0x%p)\n", Displacement);
|
|
}
|
|
else
|
|
{
|
|
dprintf64(")\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dprintf64("ExceptionAddress: %p\n", Exr->ExceptionAddress);
|
|
}
|
|
dprintf(" ExceptionCode: %08lx", Exr->ExceptionCode);
|
|
Filter = GetSpecificExceptionFilter(Exr->ExceptionCode);
|
|
if (Filter)
|
|
{
|
|
dprintf(" (%s)\n", Filter->Name);
|
|
}
|
|
else
|
|
{
|
|
dprintf("\n");
|
|
}
|
|
dprintf(" ExceptionFlags: %08lx\n", Exr->ExceptionFlags);
|
|
|
|
dprintf("NumberParameters: %d\n", Exr->NumberParameters);
|
|
if (Exr->NumberParameters > EXCEPTION_MAXIMUM_PARAMETERS)
|
|
{
|
|
Exr->NumberParameters = EXCEPTION_MAXIMUM_PARAMETERS;
|
|
}
|
|
for (i = 0; i < Exr->NumberParameters; i++)
|
|
{
|
|
dprintf64(" Parameter[%d]: %p\n", i, Exr->ExceptionInformation[i]);
|
|
}
|
|
|
|
//
|
|
// Known Exception processing:
|
|
//
|
|
|
|
switch(Exr->ExceptionCode)
|
|
{
|
|
case STATUS_ACCESS_VIOLATION:
|
|
if (Exr->NumberParameters == 2)
|
|
{
|
|
dprintf64("Attempt to %s address %p\n",
|
|
(Exr->ExceptionInformation[0] ?
|
|
"write to" : "read from"),
|
|
Exr->ExceptionInformation[1]);
|
|
}
|
|
break;
|
|
|
|
case STATUS_IN_PAGE_ERROR:
|
|
if (Exr->NumberParameters == 3)
|
|
{
|
|
dprintf64("Inpage operation failed at %p, due to I/O error %p\n",
|
|
Exr->ExceptionInformation[1],
|
|
Exr->ExceptionInformation[2]);
|
|
}
|
|
break;
|
|
|
|
case STATUS_INVALID_HANDLE:
|
|
case STATUS_HANDLE_NOT_CLOSABLE:
|
|
dprintf64("Thread tried to close a handle that was "
|
|
"invalid or illegal to close\n");
|
|
break;
|
|
|
|
case STATUS_POSSIBLE_DEADLOCK:
|
|
if (Exr->NumberParameters == 1)
|
|
{
|
|
RTL_CRITICAL_SECTION64 CritSec64;
|
|
RTL_CRITICAL_SECTION32 CritSec32;
|
|
|
|
GetSymbol(Exr->ExceptionInformation[0],
|
|
Buffer, DIMA(Buffer), &Displacement);
|
|
if (*Buffer)
|
|
{
|
|
dprintf64("Critical section at %p (%s+%p)",
|
|
Exr->ExceptionInformation[0],
|
|
Buffer,
|
|
Displacement);
|
|
}
|
|
else
|
|
{
|
|
dprintf64("Critical section at %p",
|
|
Exr->ExceptionInformation[0]);
|
|
}
|
|
|
|
if (g_Target->m_Machine->m_Ptr64)
|
|
{
|
|
Status = CurReadAllVirtual(Exr->ExceptionInformation[0],
|
|
&CritSec64, sizeof(CritSec64));
|
|
}
|
|
else
|
|
{
|
|
Status = CurReadAllVirtual(Exr->ExceptionInformation[0],
|
|
&CritSec32, sizeof(CritSec32));
|
|
if (Status == S_OK)
|
|
{
|
|
CritSec64.OwningThread = CritSec32.OwningThread;
|
|
}
|
|
}
|
|
if (Status == S_OK)
|
|
{
|
|
dprintf64("is owned by thread %p,\n"
|
|
"causing this thread to raise an exception",
|
|
CritSec64.OwningThread);
|
|
}
|
|
|
|
dprintf("\n");
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
DotFnEnt(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
if (!IS_CUR_MACHINE_ACCESSIBLE())
|
|
{
|
|
error(BADTHREAD);
|
|
}
|
|
|
|
BOOL SymDirect = FALSE;
|
|
|
|
while (PeekChar() == '-' || *g_CurCmd == '/')
|
|
{
|
|
switch(*++g_CurCmd)
|
|
{
|
|
case 's':
|
|
SymDirect = TRUE;
|
|
break;
|
|
default:
|
|
ErrOut("Unknown option '%c'\n", *g_CurCmd);
|
|
break;
|
|
}
|
|
|
|
g_CurCmd++;
|
|
}
|
|
|
|
ULONG64 Addr = GetExpression();
|
|
PVOID FnEnt;
|
|
|
|
if (SymDirect)
|
|
{
|
|
FnEnt = SymFunctionTableAccess64(g_Process->m_SymHandle, Addr);
|
|
}
|
|
else
|
|
{
|
|
FnEnt = SwFunctionTableAccess(g_Process->m_SymHandle, Addr);
|
|
}
|
|
if (FnEnt == NULL)
|
|
{
|
|
ErrOut("No function entry for %s\n", FormatAddr64(Addr));
|
|
return;
|
|
}
|
|
|
|
dprintf("%s function entry %s for:\n",
|
|
SymDirect ? "Symbol" : "Debugger", FormatAddr64((ULONG_PTR)FnEnt));
|
|
ListNearSymbols(Addr);
|
|
dprintf("\n");
|
|
g_Machine->OutputFunctionEntry(FnEnt);
|
|
}
|
|
|
|
void
|
|
DotFormats(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
LONG64 Value;
|
|
LONG Val32;
|
|
BOOL Use64;
|
|
// If this command is really .formats then display
|
|
// everything, otherwise just display the basic
|
|
// expression result for ?.
|
|
BOOL Verbose = Cmd != NULL;
|
|
|
|
Value = GetExpression();
|
|
|
|
// Allow 64-bit expressions to be evaluated even on
|
|
// 32-bit platforms since they can also use 64-bit numbers.
|
|
Use64 = NeedUpper(Value) || !g_Machine || g_Machine->m_Ptr64;
|
|
Val32 = (LONG)Value;
|
|
|
|
if (!Verbose)
|
|
{
|
|
if (Use64)
|
|
{
|
|
dprintf("Evaluate expression: %I64d = %08x`%08x\n",
|
|
Value, (ULONG)(Value >> 32), Val32);
|
|
}
|
|
else
|
|
{
|
|
dprintf("Evaluate expression: %d = %08x\n",
|
|
Val32, Val32);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dprintf("Evaluate expression:\n");
|
|
if (Use64)
|
|
{
|
|
dprintf(" Hex: %08x`%08x\n", (ULONG)(Value >> 32), Val32);
|
|
dprintf(" Decimal: %I64d\n", Value);
|
|
}
|
|
else
|
|
{
|
|
dprintf(" Hex: %08x\n", Val32);
|
|
dprintf(" Decimal: %d\n", Val32);
|
|
}
|
|
|
|
ULONG Shift = Use64 ? 63 : 30;
|
|
dprintf(" Octal: ");
|
|
for (;;)
|
|
{
|
|
dprintf("%c", ((Value >> Shift) & 7) + '0');
|
|
if (Shift == 0)
|
|
{
|
|
break;
|
|
}
|
|
Shift -= 3;
|
|
}
|
|
dprintf("\n");
|
|
|
|
Shift = Use64 ? 63 : 31;
|
|
dprintf(" Binary: ");
|
|
for (;;)
|
|
{
|
|
if ((Shift & 7) == 7)
|
|
{
|
|
dprintf(" ");
|
|
}
|
|
|
|
dprintf("%c", ((Value >> Shift) & 1) + '0');
|
|
if (Shift == 0)
|
|
{
|
|
break;
|
|
}
|
|
Shift--;
|
|
}
|
|
dprintf("\n");
|
|
|
|
Shift = Use64 ? 56 : 24;
|
|
dprintf(" Chars: ");
|
|
for (;;)
|
|
{
|
|
Val32 = (LONG)((Value >> Shift) & 0xff);
|
|
if (Val32 >= ' ' && Val32 <= 127)
|
|
{
|
|
dprintf("%c", Val32);
|
|
}
|
|
else
|
|
{
|
|
dprintf(".");
|
|
}
|
|
if (Shift == 0)
|
|
{
|
|
break;
|
|
}
|
|
Shift -= 8;
|
|
}
|
|
Val32 = (LONG)Value;
|
|
dprintf("\n");
|
|
|
|
dprintf(" Time: %s\n", Use64 ? LONG64FileTimeToStr(Value) : TimeToStr(Val32) );
|
|
|
|
dprintf(" Float: low %hg high %hg\n",
|
|
*(float*)&Value, *((float*)&Value + 1));
|
|
dprintf(" Double: %g\n", *(double*)&Value);
|
|
}
|
|
}
|
|
|
|
void
|
|
DotFrame(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
PDEBUG_STACK_FRAME StackFrame;
|
|
ULONG Traced = 0, Frame = 0;
|
|
|
|
if (PeekChar())
|
|
{
|
|
Frame = (ULONG) GetExpression();
|
|
StackFrame = (PDEBUG_STACK_FRAME)
|
|
malloc(sizeof(DEBUG_STACK_FRAME) * (Frame + 1));
|
|
if (!StackFrame)
|
|
{
|
|
error(NOMEMORY);
|
|
}
|
|
|
|
Traced = StackTrace(Client, 0, 0, 0, STACK_ALL_DEFAULT,
|
|
StackFrame, Frame + 1, 0, 0, FALSE);
|
|
if (Traced <= Frame)
|
|
{
|
|
ErrOut("Cannot find frame 0x%lx, previous scope unchanged\n",
|
|
Frame);
|
|
return;
|
|
}
|
|
|
|
// Do not change the previous context
|
|
SetCurrentScope(&StackFrame[Frame], NULL, 0);
|
|
}
|
|
|
|
PrintStackFrame(&GetCurrentScope()->Frame, NULL,
|
|
DEBUG_STACK_FRAME_ADDRESSES |
|
|
DEBUG_STACK_FRAME_NUMBERS |
|
|
DEBUG_STACK_SOURCE_LINE);
|
|
}
|
|
|
|
BOOL
|
|
CALLBACK
|
|
GetFirstLocalOffset(PSYMBOL_INFO SymInfo,
|
|
ULONG Size,
|
|
PVOID Context)
|
|
{
|
|
PULONG Offset = (PULONG) Context;
|
|
|
|
if (SymInfo->Flags & SYMFLAG_REGREL)
|
|
{
|
|
*Offset = (ULONG)SymInfo->Address;
|
|
}
|
|
else
|
|
{
|
|
*Offset = 0;
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void
|
|
DotFrameEbpFix(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
//
|
|
// This adjusts the SAVED_EBP value in stackframe by
|
|
// looking at parameter's offset and actual address. Using
|
|
// these it back-caclulates what ebp value could be right for the frame.
|
|
//
|
|
|
|
if (g_Machine->m_ExecTypes[0] != IMAGE_FILE_MACHINE_I386)
|
|
{
|
|
error(SESSIONNOTSUP);
|
|
}
|
|
|
|
if (!g_ScopeBuffer.Frame.FuncTableEntry)
|
|
{
|
|
// No need to do this for NON-FPO calls.
|
|
goto Show;
|
|
}
|
|
if (((PFPO_DATA)g_ScopeBuffer.Frame.FuncTableEntry)->cdwParams == 0)
|
|
{
|
|
// We cannot do this for calls without any parameters.
|
|
ErrOut("Parameters required\n");
|
|
return;
|
|
}
|
|
|
|
IMAGEHLP_SYMBOL64 ImgSym;
|
|
ULONG Param1Offset;
|
|
ULONG64 Displacement;
|
|
|
|
ImgSym.MaxNameLength = 1;
|
|
|
|
if (g_ScopeBuffer.Frame.InstructionOffset &&
|
|
SymGetSymFromAddr64(g_Process->m_SymHandle,
|
|
g_ScopeBuffer.Frame.InstructionOffset,
|
|
&Displacement,
|
|
&ImgSym))
|
|
{
|
|
IMAGEHLP_STACK_FRAME StackFrame;
|
|
|
|
// SetCurrentScope to this function address, not the return address
|
|
// since we only want to enumerate the parameters
|
|
g_EngNotify++;
|
|
StackFrame.InstructionOffset = ImgSym.Address;
|
|
SymSetContext(g_Process->m_SymHandle,&StackFrame, NULL);
|
|
|
|
// Enumerate locals and save the offset for first one
|
|
EnumerateLocals(GetFirstLocalOffset, &Param1Offset);
|
|
|
|
if (Param1Offset)
|
|
{
|
|
// Params start after 2 dwords from farame offset
|
|
SAVE_EBP(&g_ScopeBuffer.Frame) =
|
|
((ULONG) g_ScopeBuffer.Frame.FrameOffset + 2*sizeof(DWORD) -
|
|
Param1Offset) + 0xEB00000000; // EBP tag
|
|
}
|
|
|
|
// Reset the scope back to original
|
|
SymSetContext(g_Process->m_SymHandle,
|
|
(PIMAGEHLP_STACK_FRAME)&g_ScopeBuffer.Frame, NULL);
|
|
g_EngNotify--;
|
|
}
|
|
|
|
Show:
|
|
PrintStackFrame(&GetCurrentScope()->Frame, NULL,
|
|
DEBUG_STACK_FRAME_ADDRESSES |
|
|
DEBUG_STACK_FRAME_NUMBERS |
|
|
DEBUG_STACK_SOURCE_LINE);
|
|
}
|
|
|
|
void
|
|
DotImgScan(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
ULONG64 Handle = 0;
|
|
MEMORY_BASIC_INFORMATION64 Info;
|
|
BOOL Verbose = FALSE;
|
|
BOOL AllRegions = TRUE;
|
|
BOOL ReloadIfPossible = FALSE;
|
|
ADDR Start;
|
|
ULONG64 Length;
|
|
|
|
//
|
|
// Scan virtual memory looking for image headers.
|
|
//
|
|
|
|
while (PeekChar() == '-' || *g_CurCmd == '/')
|
|
{
|
|
switch(*++g_CurCmd)
|
|
{
|
|
case 'l':
|
|
g_CurCmd++;
|
|
ReloadIfPossible = TRUE;
|
|
break;
|
|
case 'r':
|
|
g_CurCmd++;
|
|
Length = 0x10000;
|
|
GetRange(&Start, &Length, 1, SEGREG_DATA,
|
|
0x7fffffff);
|
|
AllRegions = FALSE;
|
|
Info.BaseAddress = Flat(Start);
|
|
Info.RegionSize = Length;
|
|
break;
|
|
case 'v':
|
|
g_CurCmd++;
|
|
Verbose = TRUE;
|
|
break;
|
|
default:
|
|
ErrOut("Unknown option '%c'\n", *g_CurCmd);
|
|
g_CurCmd++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
while ((AllRegions && g_Target->
|
|
QueryMemoryRegion(g_Process, &Handle, FALSE, &Info) == S_OK) ||
|
|
(!AllRegions && Info.RegionSize))
|
|
{
|
|
ULONG64 Addr;
|
|
|
|
if (Verbose)
|
|
{
|
|
dprintf("*** Checking %s - %s\n",
|
|
FormatAddr64(Info.BaseAddress),
|
|
FormatAddr64(Info.BaseAddress + Info.RegionSize - 1));
|
|
FlushCallbacks();
|
|
}
|
|
|
|
//
|
|
// Check for MZ at the beginning of every page.
|
|
//
|
|
|
|
Addr = Info.BaseAddress;
|
|
while (Addr < Info.BaseAddress + Info.RegionSize)
|
|
{
|
|
USHORT ShortSig;
|
|
ULONG Done;
|
|
|
|
if (g_Target->
|
|
ReadVirtual(g_Process, Addr, &ShortSig, sizeof(ShortSig),
|
|
&Done) == S_OK &&
|
|
Done == sizeof(ShortSig) &&
|
|
ShortSig == IMAGE_DOS_SIGNATURE)
|
|
{
|
|
IMAGE_NT_HEADERS64 NtHdr;
|
|
char Name[MAX_IMAGE_PATH];
|
|
|
|
if (AllRegions)
|
|
{
|
|
dprintf("MZ at %s, prot %08X, type %08X",
|
|
FormatAddr64(Addr), Info.Protect, Info.Type);
|
|
}
|
|
else
|
|
{
|
|
dprintf("MZ at %s", FormatAddr64(Addr));
|
|
}
|
|
|
|
if (g_Target->ReadImageNtHeaders(g_Process, Addr,
|
|
&NtHdr) == S_OK)
|
|
{
|
|
dprintf(" - size %x\n", NtHdr.OptionalHeader.SizeOfImage);
|
|
if (GetModnameFromImage(g_Process, Addr, NULL,
|
|
Name, DIMA(Name), TRUE))
|
|
{
|
|
dprintf(" Name: %s\n", Name);
|
|
|
|
if (ReloadIfPossible)
|
|
{
|
|
char ReloadCmd[MAX_IMAGE_PATH];
|
|
PCSTR ArgsRet;
|
|
|
|
PrintString(ReloadCmd, DIMA(ReloadCmd),
|
|
"%s=0x%s,0x%x",
|
|
Name, FormatAddr64(Addr),
|
|
NtHdr.OptionalHeader.SizeOfImage);
|
|
if (g_Target->Reload(g_Thread, ReloadCmd,
|
|
&ArgsRet) == S_OK)
|
|
{
|
|
dprintf(" Loaded %s module\n", Name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dprintf("\n");
|
|
}
|
|
|
|
FlushCallbacks();
|
|
}
|
|
|
|
if (CheckUserInterrupt())
|
|
{
|
|
WarnOut(" Aborted\n");
|
|
return;
|
|
}
|
|
|
|
Addr += g_Machine->m_PageSize;
|
|
}
|
|
|
|
Info.RegionSize = 0;
|
|
}
|
|
}
|
|
|
|
void
|
|
DotKdFiles(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
((ConnLiveKernelTargetInfo*)g_Target)->m_Transport->ParseKdFileAssoc();
|
|
}
|
|
|
|
void
|
|
DotKdTrans(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
((ConnLiveKernelTargetInfo*)g_Target)->m_Transport->OutputInfo();
|
|
}
|
|
|
|
void
|
|
DotKFrames(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
g_DefaultStackTraceDepth = (ULONG)GetExpression();
|
|
dprintf("Default stack trace depth is 0n%d frames\n",
|
|
g_DefaultStackTraceDepth);
|
|
}
|
|
|
|
PCSTR
|
|
EventProcThreadString(void)
|
|
{
|
|
if (IS_USER_TARGET(g_EventTarget))
|
|
{
|
|
static char s_Buf[64];
|
|
|
|
PrintString(s_Buf, DIMA(s_Buf), "%x.%x: ",
|
|
g_EventProcessSysId, g_EventThreadSysId);
|
|
return s_Buf;
|
|
}
|
|
else
|
|
{
|
|
return "";
|
|
}
|
|
}
|
|
|
|
void
|
|
DotLastEvent(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
dprintf("Last event: %s%s\n",
|
|
EventProcThreadString(), g_LastEventDesc);
|
|
}
|
|
|
|
void
|
|
DotLocale(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
PSTR LocaleStr;
|
|
CHAR Save;
|
|
|
|
if (PeekChar() && *g_CurCmd != ';')
|
|
{
|
|
LocaleStr = StringValue(STRV_ESCAPED_CHARACTERS |
|
|
STRV_TRIM_TRAILING_SPACE,
|
|
&Save);
|
|
}
|
|
else
|
|
{
|
|
LocaleStr = NULL;
|
|
}
|
|
|
|
dprintf("Locale: %s\n", setlocale(LC_ALL, LocaleStr));
|
|
|
|
if (LocaleStr != NULL)
|
|
{
|
|
*g_CurCmd = Save;
|
|
}
|
|
}
|
|
|
|
void
|
|
DotLogAppend(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
ParseLogOpen(TRUE);
|
|
}
|
|
|
|
void
|
|
DotLogClose(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
CloseLogFile();
|
|
}
|
|
|
|
void
|
|
DotLogFile(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
if (g_LogFile >= 0)
|
|
{
|
|
dprintf("Log '%s' open%s\n",
|
|
g_OpenLogFileName,
|
|
g_OpenLogFileAppended ? " for append" : "");
|
|
}
|
|
else
|
|
{
|
|
dprintf("No log file open\n");
|
|
}
|
|
}
|
|
|
|
void
|
|
DotLogOpen(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
ParseLogOpen(FALSE);
|
|
}
|
|
|
|
void
|
|
DotNetSyms(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
char Save;
|
|
PSTR Str;
|
|
|
|
Str = StringValue(0, &Save);
|
|
|
|
if (_stricmp(Str, "1") == 0 ||
|
|
_stricmp(Str, "true") == 0 ||
|
|
_stricmp(Str, "yes") == 0)
|
|
{
|
|
g_EngOptions |= DEBUG_ENGOPT_ALLOW_NETWORK_PATHS;
|
|
g_EngOptions &= ~DEBUG_ENGOPT_DISALLOW_NETWORK_PATHS;
|
|
}
|
|
else if (_stricmp(Str, "0") == 0 ||
|
|
_stricmp(Str, "false") == 0 ||
|
|
_stricmp(Str, "no") == 0)
|
|
{
|
|
g_EngOptions |= DEBUG_ENGOPT_DISALLOW_NETWORK_PATHS;
|
|
g_EngOptions &= ~DEBUG_ENGOPT_ALLOW_NETWORK_PATHS;
|
|
}
|
|
|
|
*g_CurCmd = Save;
|
|
|
|
if (g_EngOptions & DEBUG_ENGOPT_ALLOW_NETWORK_PATHS)
|
|
{
|
|
dprintf("netsyms = yes\n");
|
|
}
|
|
else if (g_EngOptions & DEBUG_ENGOPT_DISALLOW_NETWORK_PATHS)
|
|
{
|
|
dprintf("netsyms = no\n");
|
|
}
|
|
else
|
|
{
|
|
dprintf("netsyms = don't care\n");
|
|
}
|
|
}
|
|
|
|
void
|
|
DotNoEngErr(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
// Internal command to clear out the error suppression
|
|
// flags in case we want to rerun operations and check
|
|
// for errors that may be in suppression mode.
|
|
g_EngErr = 0;
|
|
}
|
|
|
|
void
|
|
DotNoShell(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
g_EngOptions |= DEBUG_ENGOPT_DISALLOW_SHELL_COMMANDS;
|
|
dprintf("Shell commands disabled\n");
|
|
}
|
|
|
|
void
|
|
DotNoVersion(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
dprintf("Extension DLL system version checking is disabled\n");
|
|
g_EnvDbgOptions |= OPTION_NOVERSIONCHECK;
|
|
}
|
|
|
|
void
|
|
DotOCommand(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
if (PeekChar() != ';' && *g_CurCmd)
|
|
{
|
|
ULONG BufLen;
|
|
|
|
BufLen = MAX_PATH;
|
|
|
|
while (PeekChar() == '-' || *g_CurCmd == '/')
|
|
{
|
|
switch(*++g_CurCmd)
|
|
{
|
|
case 'd':
|
|
BufLen = 0;
|
|
break;
|
|
default:
|
|
ErrOut("Unknown option '%c'\n", *g_CurCmd);
|
|
break;
|
|
}
|
|
|
|
g_CurCmd++;
|
|
}
|
|
|
|
if (BufLen)
|
|
{
|
|
ULONG Len;
|
|
CHAR Save;
|
|
PSTR Pat = StringValue(STRV_ESCAPED_CHARACTERS, &Save);
|
|
Len = strlen(Pat);
|
|
if (Len >= BufLen)
|
|
{
|
|
error(OVERFLOW);
|
|
}
|
|
|
|
memcpy(g_OutputCommandRedirectPrefix, Pat, Len + 1);
|
|
g_OutputCommandRedirectPrefixLen = Len;
|
|
|
|
*g_CurCmd = Save;
|
|
}
|
|
else
|
|
{
|
|
g_OutputCommandRedirectPrefixLen = 0;
|
|
}
|
|
}
|
|
|
|
if (g_OutputCommandRedirectPrefixLen)
|
|
{
|
|
dprintf("Treat output prefixed with '%s' as a command\n",
|
|
g_OutputCommandRedirectPrefix);
|
|
}
|
|
else
|
|
{
|
|
dprintf("Output command redirection inactive\n");
|
|
}
|
|
}
|
|
|
|
void
|
|
DotOFilter(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
if (PeekChar() != ';' && *g_CurCmd)
|
|
{
|
|
g_OutFilterResult = TRUE;
|
|
|
|
while (PeekChar() == '-' || *g_CurCmd == '/')
|
|
{
|
|
switch(*++g_CurCmd)
|
|
{
|
|
case '!':
|
|
g_OutFilterResult = FALSE;
|
|
break;
|
|
default:
|
|
ErrOut("Unknown option '%c'\n", *g_CurCmd);
|
|
break;
|
|
}
|
|
|
|
g_CurCmd++;
|
|
}
|
|
|
|
CHAR Save;
|
|
PSTR Pat = StringValue(STRV_TRIM_TRAILING_SPACE |
|
|
STRV_ESCAPED_CHARACTERS |\
|
|
STRV_ALLOW_EMPTY_STRING, &Save);
|
|
|
|
if (strlen(Pat) + 1 > sizeof(g_OutFilterPattern))
|
|
{
|
|
error(OVERFLOW);
|
|
}
|
|
|
|
CopyString(g_OutFilterPattern, Pat, DIMA(g_OutFilterPattern));
|
|
*g_CurCmd = Save;
|
|
_strupr(g_OutFilterPattern);
|
|
}
|
|
|
|
if (g_OutFilterPattern[0])
|
|
{
|
|
dprintf("Only display debuggee output that %s '%s'\n",
|
|
g_OutFilterResult ? "matches" : "doesn't match",
|
|
g_OutFilterPattern);
|
|
}
|
|
else
|
|
{
|
|
dprintf("No debuggee output filter set\n");
|
|
}
|
|
}
|
|
|
|
void
|
|
DotOpenDump(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
PSTR FileName;
|
|
PWSTR FileWide;
|
|
char Save;
|
|
TargetInfo* Target;
|
|
|
|
if (g_SymOptions & SYMOPT_SECURE)
|
|
{
|
|
error(NOTSECURE);
|
|
}
|
|
|
|
FileName = StringValue(STRV_TRIM_TRAILING_SPACE |
|
|
STRV_ESCAPED_CHARACTERS, &Save);
|
|
if (AnsiToWide(FileName, &FileWide) != S_OK)
|
|
{
|
|
ErrOut("Unable to convert filename\n");
|
|
}
|
|
else
|
|
{
|
|
// Error messages are displayed by DumpInitialize.
|
|
if (DumpInitialize(Client, FileWide, 0, &Target) == S_OK)
|
|
{
|
|
dprintf("Opened '%s'\n", FileName);
|
|
}
|
|
|
|
FreeWide(FileWide);
|
|
}
|
|
*g_CurCmd = Save;
|
|
}
|
|
|
|
void
|
|
DotOutMask(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
// Private internal command for debugging the debugger.
|
|
ULONG Expr = (ULONG)GetExpression();
|
|
if (Cmd->Name[7] == '-')
|
|
{
|
|
Client->m_OutMask &= ~Expr;
|
|
}
|
|
else
|
|
{
|
|
Client->m_OutMask |= Expr;
|
|
}
|
|
dprintf("Client %p mask is %X\n", Client, Client->m_OutMask);
|
|
CollectOutMasks();
|
|
|
|
// Also update the log mask as people usually
|
|
// want to see the same things in the log.
|
|
g_LogMask = Client->m_OutMask;
|
|
}
|
|
|
|
void
|
|
DotPCache(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
g_Target->m_PhysicalCache.ParseCommands();
|
|
}
|
|
|
|
void
|
|
DotReboot(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
if (PeekChar())
|
|
{
|
|
error(SYNTAX);
|
|
}
|
|
|
|
// Null out .reboot command.
|
|
g_LastCommand[0] = '\0';
|
|
g_Target->Reboot();
|
|
// The target is no longer accepting commands
|
|
// so reset the command state.
|
|
g_CmdState = 'i';
|
|
}
|
|
|
|
void
|
|
DotReCxr(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
while (PeekChar() == '-')
|
|
{
|
|
switch(*++g_CurCmd)
|
|
{
|
|
case 'f':
|
|
g_Target->FlushRegContext();
|
|
break;
|
|
default:
|
|
ErrOut("Unknown option '%c'\n", *g_CurCmd);
|
|
break;
|
|
}
|
|
|
|
g_CurCmd++;
|
|
}
|
|
|
|
if (g_Machine)
|
|
{
|
|
g_Machine->InvalidateContext();
|
|
}
|
|
}
|
|
|
|
void
|
|
DotReload(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
g_Target->Reload(g_Thread, g_CurCmd, (PCSTR*)&g_CurCmd);
|
|
ClearStoredTypes(0);
|
|
}
|
|
|
|
void
|
|
DotSecure(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
if (PeekChar() && *g_CurCmd != ';')
|
|
{
|
|
// Ignore argument.
|
|
if (GetExpression())
|
|
{
|
|
if (SetSymOptions(g_SymOptions | SYMOPT_SECURE) != S_OK)
|
|
{
|
|
ErrOut("ERROR: Unable to enter secure mode.\n"
|
|
"Secure mode requires no sessions and "
|
|
"no remote clients.\n");
|
|
}
|
|
else
|
|
{
|
|
dprintf("Entered secure mode\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
dprintf("Secure mode %sabled\n",
|
|
(g_SymOptions & SYMOPT_SECURE) ? "en" : "dis");
|
|
}
|
|
|
|
void
|
|
EnumServers(BOOL ShowComputerName)
|
|
{
|
|
ULONG Id;
|
|
char Desc[4 * MAX_PARAM_VALUE];
|
|
PVOID Cookie = NULL;
|
|
BOOL Any = FALSE;
|
|
char CompName[MAX_COMPUTERNAME_LENGTH + 1];
|
|
|
|
if (ShowComputerName)
|
|
{
|
|
ULONG CompSize = sizeof(CompName);
|
|
|
|
if (!GetComputerName(CompName, &CompSize))
|
|
{
|
|
PrintString(CompName, DIMA(CompName),
|
|
"<Win32 Error %d>", GetLastError());
|
|
}
|
|
}
|
|
|
|
while (Cookie =
|
|
DbgRpcEnumActiveServers(Cookie, &Id, Desc, sizeof(Desc)))
|
|
{
|
|
dprintf("%d - %s", Id, Desc);
|
|
|
|
if (ShowComputerName)
|
|
{
|
|
dprintf(",Server=%s", CompName);
|
|
}
|
|
|
|
dprintf("\n");
|
|
|
|
Any = TRUE;
|
|
}
|
|
if (!Any)
|
|
{
|
|
dprintf("No active servers\n");
|
|
}
|
|
}
|
|
|
|
void
|
|
DotServer(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
HRESULT Status;
|
|
|
|
// Skip whitespace.
|
|
if (PeekChar() == 0)
|
|
{
|
|
ErrOut("Usage: .server tcp:port=<Socket> OR "
|
|
".server npipe:pipe=<PipeName>\n");
|
|
}
|
|
else
|
|
{
|
|
Status = ClientStartServer(g_CurCmd, TRUE);
|
|
if (Status != S_OK)
|
|
{
|
|
ErrOut("Unable to start server, %s\n \"%s\"\n",
|
|
FormatStatusCode(Status), FormatStatus(Status));
|
|
}
|
|
else
|
|
{
|
|
dprintf("Server started. Client can connect with any of:\n");
|
|
EnumServers(TRUE);
|
|
}
|
|
*g_CurCmd = 0;
|
|
}
|
|
}
|
|
|
|
void
|
|
DotServers(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
EnumServers(FALSE);
|
|
}
|
|
|
|
void
|
|
DotShell(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
ShellProcess Shell;
|
|
PSTR InFile = NULL;
|
|
PSTR OutFile = NULL;
|
|
PSTR ErrFile = NULL;
|
|
PSTR* File;
|
|
|
|
if ((g_EngOptions & DEBUG_ENGOPT_DISALLOW_SHELL_COMMANDS) ||
|
|
(g_SymOptions & SYMOPT_SECURE))
|
|
{
|
|
ErrOut(".shell has been disabled\n");
|
|
return;
|
|
}
|
|
|
|
if (AnySystemProcesses(TRUE))
|
|
{
|
|
ErrOut(".shell: can't create a process while debugging CSRSS.\n");
|
|
return;
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
if (PeekChar() == '-')
|
|
{
|
|
g_CurCmd++;
|
|
switch(*g_CurCmd)
|
|
{
|
|
case 'e':
|
|
File = &ErrFile;
|
|
goto ParseFile;
|
|
case 'i':
|
|
File = &InFile;
|
|
goto ParseFile;
|
|
case 'o':
|
|
File = &OutFile;
|
|
ParseFile:
|
|
g_CurCmd++;
|
|
if (*g_CurCmd == '-')
|
|
{
|
|
*File = "nul";
|
|
g_CurCmd++;
|
|
}
|
|
else
|
|
{
|
|
char LastSave;
|
|
|
|
*File = StringValue(STRV_SPACE_IS_SEPARATOR |
|
|
STRV_ESCAPED_CHARACTERS,
|
|
&LastSave);
|
|
if (LastSave)
|
|
{
|
|
g_CurCmd++;
|
|
}
|
|
else
|
|
{
|
|
*g_CurCmd = LastSave;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 't':
|
|
Shell.m_DefaultTimeout = (ULONG)GetExpression() * 1000;
|
|
break;
|
|
|
|
default:
|
|
ErrOut("Unknown option '%c'\n", *g_CurCmd);
|
|
g_CurCmd++;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If either output file was specified singly give
|
|
// the output file the name and let the error file
|
|
// get set to a duplicate of it.
|
|
if (ErrFile && !OutFile)
|
|
{
|
|
OutFile = ErrFile;
|
|
ErrFile = NULL;
|
|
}
|
|
|
|
Shell.Start(g_CurCmd, InFile, OutFile, ErrFile);
|
|
|
|
// Command uses the whole string so we're done.
|
|
*g_CurCmd = 0;
|
|
}
|
|
|
|
void
|
|
DotSleep(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
ULONG WaitStatus;
|
|
ULONG Millis = (ULONG)
|
|
GetExpressionDesc("Number of milliseconds missing from");
|
|
|
|
// This command is intended for use with ntsd/cdb -d
|
|
// when being at the prompt locks up the target machine.
|
|
// If you want to use the target machine for something,
|
|
// such as copy symbols, there's no easy way to get it
|
|
// running again without resuming the program. By
|
|
// sleeping you can return control to the target machine
|
|
// without changing the session state.
|
|
// The sleep is done with a wait on a named event so
|
|
// that it can be interrupted from a different process.
|
|
ResetEvent(g_SleepPidEvent);
|
|
WaitStatus = WaitForSingleObject(g_SleepPidEvent, Millis);
|
|
if (WaitStatus == WAIT_OBJECT_0)
|
|
{
|
|
dprintf("Sleep interrupted\n");
|
|
}
|
|
else if (WaitStatus != WAIT_TIMEOUT)
|
|
{
|
|
ErrOut("Sleep failed, %s\n",
|
|
FormatStatusCode(WIN32_LAST_STATUS()));
|
|
}
|
|
}
|
|
|
|
void
|
|
DotSrcPath(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
if (PeekChar())
|
|
{
|
|
if (ChangePath(&g_SrcPath, g_CurCmd, Cmd->Name[7] == '+',
|
|
DEBUG_CSS_PATHS) != S_OK)
|
|
{
|
|
// This command uses the whole string.
|
|
*g_CurCmd = 0;
|
|
return;
|
|
}
|
|
|
|
*g_CurCmd = 0;
|
|
}
|
|
|
|
if (g_SrcPath == NULL)
|
|
{
|
|
dprintf("No source search path\n");
|
|
}
|
|
else
|
|
{
|
|
dprintf("Source search path is: %s\n", g_SrcPath);
|
|
CheckPath(g_SrcPath);
|
|
}
|
|
}
|
|
|
|
void
|
|
DotSymPath(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
ChangeSymPath(g_CurCmd, Cmd->Name[7] == '+', NULL, 0);
|
|
// Command uses the whole string.
|
|
*g_CurCmd = 0;
|
|
}
|
|
|
|
void
|
|
DotSxCmds(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
ULONG Flags = 0;
|
|
|
|
while (PeekChar() == '-')
|
|
{
|
|
switch(*(++g_CurCmd))
|
|
{
|
|
case '1':
|
|
Flags |= SXCMDS_ONE_LINE;
|
|
break;
|
|
default:
|
|
dprintf("Unknown option '%c'\n", *g_CurCmd);
|
|
break;
|
|
}
|
|
|
|
g_CurCmd++;
|
|
}
|
|
|
|
ListFiltersAsCommands(Client, Flags);
|
|
}
|
|
|
|
void
|
|
DotSymFix(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
char Path[MAX_PATH * 2];
|
|
PSTR SrvName;
|
|
char LocalStore[MAX_PATH + 1];
|
|
|
|
strcpy(Path, "SRV*");
|
|
|
|
if (IsInternalPackage())
|
|
{
|
|
// Use the internal symbol server.
|
|
// A local cache is not required.
|
|
SrvName = "\\\\symbols\\symbols";
|
|
}
|
|
else
|
|
{
|
|
// Use the public Microsoft symbol server.
|
|
// This requires a local cache, so default
|
|
// if one isn't given.
|
|
SrvName = "http://msdl.microsoft.com/download/symbols";
|
|
|
|
if (!PeekChar() || *g_CurCmd == ';')
|
|
{
|
|
if (!CatString(Path, "*", DIMA(Path)))
|
|
{
|
|
error(OVERFLOW);
|
|
}
|
|
if (SymGetHomeDirectory(hdSym, LocalStore, DIMA(LocalStore)))
|
|
{
|
|
WarnOut("No local cache given, using %s\n", LocalStore);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (PeekChar() && *g_CurCmd != ';')
|
|
{
|
|
char Save;
|
|
PSTR Str = StringValue(0, &Save);
|
|
|
|
if (!CatString(Path, Str, DIMA(Path)) ||
|
|
!CatString(Path, "*", DIMA(Path)))
|
|
{
|
|
error(OVERFLOW);
|
|
}
|
|
|
|
*g_CurCmd = Save;
|
|
}
|
|
|
|
if (!CatString(Path, SrvName, DIMA(Path)))
|
|
{
|
|
error(OVERFLOW);
|
|
}
|
|
|
|
ChangeSymPath(Path, Cmd->Name[6] == '+', NULL, 0);
|
|
}
|
|
|
|
void
|
|
DotSymOpt(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
ULONG Flags = (ULONG)GetExpression();
|
|
|
|
if (Cmd->Name[6] == '+')
|
|
{
|
|
Flags |= g_SymOptions;
|
|
}
|
|
else
|
|
{
|
|
Flags = g_SymOptions & ~Flags;
|
|
}
|
|
|
|
if (SetSymOptions(Flags) != S_OK)
|
|
{
|
|
ErrOut("Invalid symbol options\n");
|
|
}
|
|
else
|
|
{
|
|
dprintf("Symbol options are %X\n", Flags);
|
|
}
|
|
}
|
|
|
|
void
|
|
DotTList(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
BOOL ListCurrent = FALSE;
|
|
BOOL Verbose = FALSE;
|
|
|
|
if (!IS_LIVE_USER_TARGET(g_Target))
|
|
{
|
|
error(SESSIONNOTSUP);
|
|
}
|
|
if (g_SymOptions & SYMOPT_SECURE)
|
|
{
|
|
error(NOTSECURE);
|
|
}
|
|
|
|
while (PeekChar() == '-' || *g_CurCmd == '/')
|
|
{
|
|
switch(*++g_CurCmd)
|
|
{
|
|
case 'c':
|
|
if (g_Process == NULL)
|
|
{
|
|
error(BADPROCESS);
|
|
}
|
|
ListCurrent = TRUE;
|
|
break;
|
|
case 'v':
|
|
Verbose = TRUE;
|
|
break;
|
|
default:
|
|
ErrOut("Unknown option '%c'\n", *g_CurCmd);
|
|
break;
|
|
}
|
|
|
|
g_CurCmd++;
|
|
}
|
|
|
|
PUSER_DEBUG_SERVICES Services =
|
|
((LiveUserTargetInfo*)g_Target)->m_Services;
|
|
|
|
#define MAX_IDS 1024
|
|
ULONG Ids[MAX_IDS];
|
|
HRESULT Status;
|
|
ULONG IdCount;
|
|
ULONG i;
|
|
WCHAR DescBuf[2 * MAX_PATH];
|
|
PWSTR Desc;
|
|
ULONG DescLen;
|
|
|
|
if (ListCurrent)
|
|
{
|
|
Ids[0] = g_Process->m_SystemId;
|
|
IdCount = 1;
|
|
}
|
|
else
|
|
{
|
|
if ((Status = Services->GetProcessIds(Ids, MAX_IDS, &IdCount)) != S_OK)
|
|
{
|
|
ErrOut("Unable to get process list, %s\n",
|
|
FormatStatusCode(Status));
|
|
return;
|
|
}
|
|
|
|
if (IdCount > MAX_IDS)
|
|
{
|
|
WarnOut("Process list missing %d processes\n",
|
|
IdCount - MAX_IDS);
|
|
IdCount = MAX_IDS;
|
|
}
|
|
}
|
|
|
|
if (Verbose)
|
|
{
|
|
Desc = DescBuf;
|
|
DescLen = sizeof(DescBuf);
|
|
}
|
|
else
|
|
{
|
|
Desc = NULL;
|
|
DescLen = 0;
|
|
}
|
|
|
|
for (i = 0; i < IdCount; i++)
|
|
{
|
|
WCHAR ExeName[MAX_PATH];
|
|
int Space;
|
|
|
|
if (Ids[i] < 10)
|
|
{
|
|
Space = 3;
|
|
}
|
|
else if (Ids[i] < 100)
|
|
{
|
|
Space = 2;
|
|
}
|
|
else if (Ids[i] < 1000)
|
|
{
|
|
Space = 1;
|
|
}
|
|
else
|
|
{
|
|
Space = 0;
|
|
}
|
|
dprintf("%.*s", Space, " ");
|
|
|
|
if (FAILED(Status = Services->
|
|
GetProcessDescriptionW(Ids[i], DEBUG_PROC_DESC_DEFAULT,
|
|
ExeName, sizeof(ExeName), NULL,
|
|
Desc, DescLen, NULL)))
|
|
{
|
|
ErrOut("0n%d Error %s\n", Ids[i], FormatStatusCode(Status));
|
|
}
|
|
else if (Ids[i] >= 0x80000000)
|
|
{
|
|
dprintf("0x%x %ws\n", Ids[i], ExeName);
|
|
}
|
|
else
|
|
{
|
|
dprintf("0n%d %ws\n", Ids[i], ExeName);
|
|
}
|
|
|
|
if (Desc && Desc[0])
|
|
{
|
|
dprintf(" %ws\n", Desc);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
DotTime(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
if (!g_Target)
|
|
{
|
|
error(BADSYSTEM);
|
|
}
|
|
|
|
g_Target->OutputTime();
|
|
}
|
|
|
|
void
|
|
DotTrap(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
ULONG64 Addr = 0;
|
|
|
|
if (PeekChar() && *g_CurCmd != ';')
|
|
{
|
|
Addr = GetExpression();
|
|
}
|
|
|
|
if (Addr)
|
|
{
|
|
CROSS_PLATFORM_CONTEXT Context;
|
|
|
|
if (g_Target->m_Machine->GetContextFromTrapFrame(Addr, &Context,
|
|
TRUE) == S_OK)
|
|
{
|
|
g_Target->m_Machine->SetAndOutputTrapFrame(Addr, &Context);
|
|
}
|
|
}
|
|
else if (GetCurrentScopeContext())
|
|
{
|
|
dprintf("Resetting default context\n");
|
|
ResetCurrentScope();
|
|
}
|
|
}
|
|
|
|
void
|
|
DotTss(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
ULONG Sel = 0;
|
|
BOOL Extended = FALSE;
|
|
|
|
if (PeekChar() && *g_CurCmd != ';')
|
|
{
|
|
Sel = (ULONG)GetExpression();
|
|
|
|
// If user specified a 2nd parameter, doesn't matter
|
|
// what it is, dump the portions of the TSS not covered
|
|
// by the trap frame dump.
|
|
if (PeekChar() && *g_CurCmd != ';')
|
|
{
|
|
Extended = TRUE;
|
|
g_CurCmd += strlen(g_CurCmd);
|
|
}
|
|
}
|
|
|
|
if (Sel)
|
|
{
|
|
CROSS_PLATFORM_CONTEXT Context;
|
|
DESCRIPTOR64 SelDesc;
|
|
|
|
//
|
|
// Lookup task selector.
|
|
//
|
|
|
|
if (g_Target->GetSelDescriptor
|
|
(g_Thread, g_Machine, Sel, &SelDesc) != S_OK)
|
|
{
|
|
ErrOut("Unable to get descriptor for selector %x\n",
|
|
Sel);
|
|
return;
|
|
}
|
|
|
|
if (X86_DESC_TYPE(SelDesc.Flags) != 9 &&
|
|
X86_DESC_TYPE(SelDesc.Flags) != 0xb)
|
|
{
|
|
ErrOut("%x is not a 32-bit TSS selector\n", Sel);
|
|
return;
|
|
}
|
|
|
|
if (g_Target->m_Machine->
|
|
GetContextFromTaskSegment(SelDesc.Base, &Context,
|
|
TRUE) == S_OK)
|
|
{
|
|
g_Target->m_Machine->
|
|
SetAndOutputTaskSegment(SelDesc.Base, &Context,
|
|
Extended);
|
|
}
|
|
}
|
|
else if (GetCurrentScopeContext())
|
|
{
|
|
dprintf("Resetting default context\n");
|
|
ResetCurrentScope();
|
|
}
|
|
}
|
|
|
|
void
|
|
DotTTime(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
if (!g_Thread)
|
|
{
|
|
error(BADSYSTEM);
|
|
}
|
|
|
|
g_Thread->OutputTimes();
|
|
}
|
|
|
|
void
|
|
LoadTxtSym(FILE* File, PSTR FileName, ULONG64 Base, ULONG Size)
|
|
{
|
|
char Line[MAX_SYMBOL_LEN];
|
|
ImageInfo* Image;
|
|
BOOL Absolute = FALSE;
|
|
|
|
if (!fgets(Line, DIMA(Line), File) ||
|
|
strcmp(Line, "TEXTSYM format | V1.0\n") != 0)
|
|
{
|
|
ErrOut("Not a TEXTSYM 1.0 file\n");
|
|
return;
|
|
}
|
|
|
|
Image = new ImageInfo(g_Process, FileName, Base, TRUE);
|
|
if (!Image)
|
|
{
|
|
ErrOut("Unable to create module\n");
|
|
return;
|
|
}
|
|
|
|
Image->m_SizeOfImage = Size;
|
|
CreateModuleNameFromPath(FileName, Image->m_ModuleName);
|
|
strcpy(Image->m_OriginalModuleName, Image->m_ModuleName);
|
|
|
|
if (!SymLoadModuleEx(g_Process->m_SymHandle, NULL,
|
|
Image->m_ImagePath,
|
|
Image->m_ModuleName, Image->m_BaseOfImage,
|
|
Image->m_SizeOfImage, NULL, SLMFLAG_VIRTUAL))
|
|
{
|
|
ErrOut("Unable to add module\n");
|
|
delete Image;
|
|
return;
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
PSTR Scan;
|
|
ULONG64 Addr;
|
|
|
|
if (!fgets(Line, DIMA(Line), File))
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (strncmp(Line, "GLOBAL | ", 9) != 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Remove newline.
|
|
Scan = Line + strlen(Line) - 1;
|
|
if (*Scan == '\n')
|
|
{
|
|
*Scan = 0;
|
|
}
|
|
|
|
Scan = Line + 9;
|
|
if (!sscanf(Scan, "%I64x", &Addr))
|
|
{
|
|
ErrOut("Unable to read address at %.16s\n", Scan);
|
|
break;
|
|
}
|
|
if (Absolute && (Addr < Base || Addr >= Base + Size))
|
|
{
|
|
ErrOut("Address %I64x out of image bounds\n", Addr);
|
|
continue;
|
|
}
|
|
|
|
Scan += 26;
|
|
|
|
if (!SymAddSymbol(g_Process->m_SymHandle,
|
|
Image->m_BaseOfImage,
|
|
Scan, Addr + Base, 0, 0))
|
|
{
|
|
ErrOut("Unable to add '%s' at %I64x\n", Scan, Addr);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
DotTxtSym(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
PSTR FileName;
|
|
char Save;
|
|
ULONG64 Base;
|
|
ULONG Size;
|
|
FILE* File;
|
|
|
|
//
|
|
// XXX drewb.
|
|
// At the moment the IA64 firmware symbols come in
|
|
// a couple of trivial text formats. In order to
|
|
// help them out we added this simple command to
|
|
// create a fake module and populate it from the
|
|
// text files. This should be removed when
|
|
// no longer necessary.
|
|
//
|
|
|
|
FileName = StringValue(STRV_SPACE_IS_SEPARATOR |
|
|
STRV_ESCAPED_CHARACTERS, &Save);
|
|
|
|
File = fopen(FileName, "r");
|
|
if (!File)
|
|
{
|
|
ErrOut("Unable to open '%s', Win32 error %d\n",
|
|
FileName, GetLastError());
|
|
*g_CurCmd = 0;
|
|
return;
|
|
}
|
|
|
|
if (Save == '"')
|
|
{
|
|
Save = *++g_CurCmd;
|
|
}
|
|
if (isspace(Save))
|
|
{
|
|
g_CurCmd++;
|
|
Save = PeekChar();
|
|
}
|
|
|
|
__try
|
|
{
|
|
if (Save != '=')
|
|
{
|
|
error(SYNTAX);
|
|
}
|
|
g_CurCmd++;
|
|
|
|
Base = GetExpression();
|
|
|
|
if (PeekChar() != ',')
|
|
{
|
|
error(SYNTAX);
|
|
}
|
|
g_CurCmd++;
|
|
|
|
Size = (ULONG)GetExpression();
|
|
|
|
LoadTxtSym(File, FileName, Base, Size);
|
|
}
|
|
__except(CommandExceptionFilter(GetExceptionInformation()))
|
|
{
|
|
*g_CurCmd = 0;
|
|
}
|
|
|
|
fclose(File);
|
|
}
|
|
|
|
void
|
|
DotWake(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
ULONG Pid = (ULONG)GetExpression();
|
|
if (!SetPidEvent(Pid, OPEN_EXISTING))
|
|
{
|
|
ErrOut("Process %d is not a sleeping debugger\n", Pid);
|
|
}
|
|
}
|
|
|
|
void
|
|
DotWriteMem(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
if (!g_Process)
|
|
{
|
|
error(BADPROCESS);
|
|
}
|
|
|
|
PSTR FileName, FileNameEnd;
|
|
char Save;
|
|
HANDLE File;
|
|
ADDR Addr;
|
|
ULONG64 Len;
|
|
char Buf[2048];
|
|
BOOL Err = FALSE;
|
|
|
|
FileName = StringValue(STRV_SPACE_IS_SEPARATOR |
|
|
STRV_ESCAPED_CHARACTERS, &Save);
|
|
FileNameEnd = g_CurCmd;
|
|
*g_CurCmd = Save;
|
|
|
|
Len = 0x10000;
|
|
GetRange(&Addr, &Len, 1, SEGREG_DATA, DEFAULT_RANGE_LIMIT);
|
|
|
|
*FileNameEnd = 0;
|
|
|
|
File = CreateFile(FileName, GENERIC_WRITE, 0, NULL,
|
|
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (File == INVALID_HANDLE_VALUE)
|
|
{
|
|
ErrOut("Unable to create file\n");
|
|
return;
|
|
}
|
|
|
|
dprintf("Writing %s bytes", FormatDisp64(Len));
|
|
FlushCallbacks();
|
|
|
|
while (Len > 0)
|
|
{
|
|
ULONG Req, Done;
|
|
|
|
Req = sizeof(Buf);
|
|
if (Req > Len)
|
|
{
|
|
Req = (ULONG)Len;
|
|
}
|
|
|
|
if (CurReadVirtual(Flat(Addr), Buf, Req, &Done) != S_OK ||
|
|
Done != Req)
|
|
{
|
|
dprintf("\n");
|
|
ErrOut("Unable to read memory at %s, file is incomplete\n",
|
|
FormatAddr64(Flat(Addr)));
|
|
Err = TRUE;
|
|
break;
|
|
}
|
|
|
|
if (!WriteFile(File, Buf, Req, &Done, NULL) ||
|
|
Done != Req)
|
|
{
|
|
dprintf("\n");
|
|
ErrOut("Unable to write data from %s, file is incomplete\n",
|
|
FormatAddr64(Flat(Addr)));
|
|
Err = TRUE;
|
|
break;
|
|
}
|
|
|
|
AddrAdd(&Addr, Done);
|
|
Len -= Done;
|
|
|
|
dprintf(".");
|
|
FlushCallbacks();
|
|
}
|
|
|
|
CloseHandle(File);
|
|
if (!Err)
|
|
{
|
|
dprintf("\n");
|
|
}
|
|
}
|
|
|
|
#define MODE_USER 0x00000001
|
|
#define MODE_KERNEL 0x00000002
|
|
#define _MODE_UK (MODE_USER | MODE_KERNEL)
|
|
|
|
#define MODE_LIVE 0x00000004
|
|
#define MODE_DUMP 0x00000008
|
|
#define _MODE_LD (MODE_LIVE | MODE_DUMP)
|
|
|
|
#define MODE_LOCAL 0x00000010
|
|
#define MODE_REMOTE 0x00000020
|
|
#define MODE_REMOTE_CONN 0x00000040
|
|
#define _MODE_LR (MODE_LOCAL | MODE_REMOTE | MODE_REMOTE_CONN)
|
|
|
|
#define MODE_BANG 0x20000000
|
|
#define MODE_INTERNAL 0x40000000
|
|
#define MODE_UNINIT 0x80000000
|
|
|
|
#define ANY (0xffff | MODE_UNINIT)
|
|
#define AIT (_MODE_UK | _MODE_LD | _MODE_LR)
|
|
|
|
#define AUT (MODE_USER | _MODE_LD | _MODE_LR)
|
|
#define LLU (MODE_USER | MODE_LIVE | MODE_LOCAL)
|
|
#define LUT (MODE_USER | MODE_LIVE | _MODE_LR)
|
|
|
|
#define AKT (MODE_KERNEL | _MODE_LD | _MODE_LR)
|
|
#define LKT (MODE_KERNEL | MODE_LIVE | _MODE_LR)
|
|
#define RKT (MODE_KERNEL | MODE_LIVE | MODE_REMOTE | MODE_REMOTE_CONN)
|
|
#define CKT (MODE_KERNEL | MODE_LIVE | MODE_REMOTE_CONN)
|
|
|
|
#define ADT (_MODE_UK | MODE_DUMP | _MODE_LR)
|
|
|
|
ULONG
|
|
CurrentMode(void)
|
|
{
|
|
ULONG Mode = 0;
|
|
|
|
if (IS_USER_TARGET(g_Target))
|
|
{
|
|
Mode |= MODE_USER;
|
|
|
|
if (IS_DUMP_TARGET(g_Target) ||
|
|
((LiveUserTargetInfo*)g_Target)->m_Local)
|
|
{
|
|
Mode |= MODE_LOCAL;
|
|
}
|
|
else
|
|
{
|
|
Mode |= MODE_REMOTE;
|
|
}
|
|
}
|
|
else if (IS_KERNEL_TARGET(g_Target))
|
|
{
|
|
Mode |= MODE_KERNEL;
|
|
|
|
if (IS_CONN_KERNEL_TARGET(g_Target))
|
|
{
|
|
Mode |= MODE_REMOTE_CONN;
|
|
}
|
|
else if (IS_REMOTE_KERNEL_TARGET(g_Target))
|
|
{
|
|
Mode |= MODE_REMOTE;
|
|
}
|
|
else
|
|
{
|
|
Mode |= MODE_LOCAL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return MODE_UNINIT;
|
|
}
|
|
|
|
if (IS_DUMP_TARGET(g_Target))
|
|
{
|
|
Mode |= MODE_DUMP;
|
|
}
|
|
else
|
|
{
|
|
Mode |= MODE_LIVE;
|
|
}
|
|
|
|
return Mode;
|
|
}
|
|
|
|
void DotHelp(PDOT_COMMAND Cmd, DebugClient* Client);
|
|
|
|
// These must be in alphabetical order.
|
|
DOT_COMMAND g_DotCommands[] =
|
|
{
|
|
LUT, "abandon", "", "abandon the current process",
|
|
ParseSeparateCurrentProcess,
|
|
ANY | MODE_INTERNAL, "aliascmds", "", "", DotAliasCmds,
|
|
ANY, "asm", "[<options>]", "set disassembly options", DotAsm,
|
|
ANY, "asm-", "[<options>]", "clear disassembly options", DotAsm,
|
|
ANY, "attach", "<proc>", "attach to <proc> at next execution", DotAttach,
|
|
ANY | MODE_INTERNAL, "bpcmds", "", "", DotBpCmds,
|
|
LUT, "bpsync", "[0|1]", "special breakpoint behavior for "
|
|
"multithreaded debuggees", DotBpSync,
|
|
LUT, "breakin", "", "break into KD", DotBreakin,
|
|
AKT, "bugcheck", "", "display the bugcheck code and parameters "
|
|
"for a crashed system", DotBugCheck,
|
|
AIT, "cache", "[<options>]", "virtual memory cache control", DotCache,
|
|
ANY | MODE_BANG, "chain", "", "list current extensions", DotChain,
|
|
LUT, "childdbg", "<0|1>", "turn child process debugging on or off",
|
|
DotChildDbg,
|
|
ANY, "clients", "", "list currently active clients", DotClients,
|
|
LUT, "closehandle", "[<options>] [<handle>]", "close the given handle",
|
|
DotCloseHandle,
|
|
AKT, "context", "[<address>]", "set page directory base", DotContext,
|
|
ANY | MODE_INTERNAL, "corstack", "[0|1]", "toggle COR stack assistance",
|
|
DotCorStack,
|
|
LKT, "crash", "", "cause target to bugcheck", DotCrash,
|
|
ANY, "create", "<command line>", "create a new process", DotCreate,
|
|
ANY, "createdir", "[<path>]", "current directory for process create",
|
|
DotCreateDir,
|
|
AIT, "cxr", "<address>", "dump context record at specified address\n"
|
|
"k* after this gives cxr stack", DotCxr,
|
|
AIT, "detach", "", "detach from the current process/dump",
|
|
ParseSeparateCurrentProcess,
|
|
AIT, "dump", "[<options>] <filename>", "create a dump file on the "
|
|
"host system", DotDump,
|
|
ADT, "dumpcab", "[<options>] <filename>", "create a CAB for an open dump",
|
|
DotDumpCab,
|
|
ADT, "dumpdebug", "", "display detailed information about the dump file",
|
|
DotDumpDebug,
|
|
ADT | MODE_INTERNAL, "dumpoff", "", "", DotDumpOff,
|
|
ADT | MODE_INTERNAL, "dumppoff", "", "", DotDumpPOff,
|
|
AIT | MODE_BANG, "drivers", "", "display the list of loaded modules "
|
|
"(same as .reload -l)", DotDrivers,
|
|
ANY, "echo", "[\"<string>\"|<string>]", "echo string", DotEcho,
|
|
ANY, "echotimestamps", "[0|1]", "toggle timestamp output on events",
|
|
DotEchoTimestamps,
|
|
AIT, "ecxr", "", "dump context record for current exception", DotEcxr,
|
|
AIT, "effmach", "[<machine>]", "change current machine type", DotEffMach,
|
|
ANY, "enable_long_status", "[0|1]", "dump LONG types in default base",
|
|
DotEnableLongStatus,
|
|
ANY, "enable_unicode", "[0|1]", "dump USHORT array/pointers "
|
|
"and unicode strings", DotEnableUnicode,
|
|
ANY, "endsrv", "<id>", "disable the given engine server", DotEndSrv,
|
|
LLU, "endpsrv", "", "cause the current session's "
|
|
"process server to exit", DotEndPSrv,
|
|
AIT, "enumtag", "", "enumerate available tagged data", DotEnumTag,
|
|
ANY, "eventlog", "", "display log of recent events", DotEventLog,
|
|
AIT, "events", "", "display and select available events", DotEvents,
|
|
AIT, "eventstr", "", "display any event strings registered by debuggee",
|
|
DotEventStr,
|
|
ANY | MODE_BANG, "exepath", "[<dir>[;...]]", "set executable search path",
|
|
DotExePath,
|
|
ANY | MODE_BANG, "exepath+", "[<dir>[;...]]",
|
|
"append executable search path", DotExePath,
|
|
ANY, "expr", "", "control expression evaluator", DotExpr,
|
|
AIT, "exr", "<address>", "dump exception record at specified address",
|
|
DotExr,
|
|
AIT, "fiber", "<address>", "sets context of fiber at address\n"
|
|
"resets context if no address specified", DotFiber,
|
|
AIT, "fnent", "<address>", "dump function entry for the "
|
|
"given code address", DotFnEnt,
|
|
AIT, "frame", "[<frame>]", "set current stack frame for locals", DotFrame,
|
|
AIT | MODE_INTERNAL, "frame_ebpfix", "", "", DotFrameEbpFix,
|
|
ANY, "formats", "<expr>", "displays expression result in many formats",
|
|
DotFormats,
|
|
ANY, "force_radix_output", "[0|1]", "dump integer types in default base",
|
|
DotForceRadixOutput,
|
|
ANY, "help", "", "display this help", DotHelp,
|
|
AIT, "imgscan", "<options>", "scan memory for PE images", DotImgScan,
|
|
CKT, "kdfiles", "<file>", "control debuggee remote file loading",
|
|
DotKdFiles,
|
|
CKT, "kdtrans", "", "display current KD transport information", DotKdTrans,
|
|
ANY, "kframes", "<count>", "set default stack trace depth", DotKFrames,
|
|
LUT, "kill", "", "kill the current process",
|
|
ParseSeparateCurrentProcess,
|
|
RKT, "kill", "", "kill a process on the target", DotKernelKill,
|
|
ANY, "lastevent", "", "display the last event that occurred", DotLastEvent,
|
|
ANY | MODE_BANG, "lines", "", "toggle line symbol loading", DotLines,
|
|
ANY | MODE_BANG, "load", "<name>",
|
|
"add this extension DLL to the list of known DLLs", NULL,
|
|
ANY, "locale", "[<locale>]", "set the current locale", DotLocale,
|
|
ANY, "logfile", "", "display log status", DotLogFile,
|
|
ANY, "logopen", "[<file>]", "open new log file", DotLogOpen,
|
|
ANY, "logappend", "[<file>]", "append to log file", DotLogAppend,
|
|
ANY, "logclose", "", "close log file", DotLogClose,
|
|
ANY, "netsyms", "[0|1]", "allow/disallow net symbol paths", DotNetSyms,
|
|
ANY | MODE_INTERNAL, "noengerr", "", "", DotNoEngErr,
|
|
ANY, "noshell", "", "disable shell commands", DotNoShell,
|
|
ANY | MODE_BANG, "noversion", "", "disable extension version checking",
|
|
DotNoVersion,
|
|
ANY, "ofilter", "<pattern>", "filter debuggee output against "
|
|
"the given pattern", DotOFilter,
|
|
LUT, "ocommand", "<prefix>", "treat output with the given prefix "
|
|
"as a command", DotOCommand,
|
|
ANY, "opendump", "<file>", "open a dump file", DotOpenDump,
|
|
ANY, "outmask", "<mask>", "set bits in the current output mask",
|
|
DotOutMask,
|
|
ANY, "outmask-", "<mask>", "clear bits in the current output mask",
|
|
DotOutMask,
|
|
LKT, "pagein", "[<options>] <addr>", "page in user-mode data", DotPageIn,
|
|
RKT, "pcache", "[<options>]", "physical memory cache control", DotPCache,
|
|
AIT, "process", "[<address>]", "sets implicit process\n"
|
|
"resets default if no address specified", DotProcess,
|
|
RKT, "reboot", "", "reboot target", DotReboot,
|
|
AIT | MODE_INTERNAL, "recxr", "", "", DotReCxr,
|
|
AIT | MODE_BANG, "reload", "[<image.ext>[=<address>,<size>]]",
|
|
"reload symbols", DotReload,
|
|
ANY, "remote", "<pipename>", "start remote.exe server", NULL,
|
|
ANY, "secure", "[0|1]", "disallow operations dangerous for the host",
|
|
DotSecure,
|
|
ANY, "server", "<options>", "start engine server", DotServer,
|
|
ANY, "servers", "", "list active remoting servers", DotServers,
|
|
ANY | MODE_BANG, "setdll", "<name>", "debugger will search for extensions "
|
|
"in this DLL first", NULL,
|
|
ANY, "shell", "[<command>]", "execute shell command", DotShell,
|
|
ANY, "sleep", "<milliseconds>", "debugger sleeps for given duration\n"
|
|
"useful for allowing access to a machine that's\n"
|
|
"broken in on an ntsd -d", DotSleep,
|
|
ANY | MODE_BANG, "srcpath", "[<dir>[;...]]", "set source search path",
|
|
DotSrcPath,
|
|
ANY | MODE_BANG, "srcpath+", "[<dir>[;...]]", "append source search path",
|
|
DotSrcPath,
|
|
ANY | MODE_INTERNAL, "sxcmds", "", "", DotSxCmds,
|
|
ANY | MODE_BANG, "symfix", "[<localsym>]", "fix symbol search path",
|
|
DotSymFix,
|
|
ANY | MODE_BANG, "symfix+", "[<localsym>]",
|
|
"append fixed symbol search path", DotSymFix,
|
|
ANY, "symopt+", "<flags>", "set symbol options", DotSymOpt,
|
|
ANY, "symopt-", "<flags>", "clear symbol options", DotSymOpt,
|
|
ANY | MODE_BANG, "sympath", "[<dir>[;...]]", "set symbol search path",
|
|
DotSymPath,
|
|
ANY | MODE_BANG, "sympath+", "[<dir>[;...]]", "append symbol search path",
|
|
DotSymPath,
|
|
AIT, "thread", "[<address>]", "sets context of thread at address\n"
|
|
"resets default context if no address specified", DotThread,
|
|
AIT, "time", "", "displays session time information", DotTime,
|
|
AIT, "ttime", "", "displays thread time information", DotTTime,
|
|
LUT, "tlist", "", "list running processes", DotTList,
|
|
AKT, "trap", "<address>", "dump a trap frame", DotTrap,
|
|
AKT, "tss", "<selector>", "dump a Task State Segment", DotTss,
|
|
ANY | MODE_INTERNAL, "txtsym", "", "", DotTxtSym,
|
|
ANY | MODE_BANG, "unload", "<name>", "remove this extension DLL from the "
|
|
"list of extension DLLs", NULL,
|
|
ANY | MODE_BANG, "unloadall", "", "remove all extension DLLs from the "
|
|
"list of extensions DLLs", NULL,
|
|
ANY, "wake", "", "wake up a .sleep'ing debugger", DotWake,
|
|
AIT, "writemem", "<file> <range>", "write raw memory to a file",
|
|
DotWriteMem,
|
|
};
|
|
|
|
void
|
|
DotHelp(PDOT_COMMAND Cmd, DebugClient* Client)
|
|
{
|
|
ULONG Mode = CurrentMode();
|
|
|
|
dprintf(". commands:\n");
|
|
for (Cmd = g_DotCommands;
|
|
Cmd < g_DotCommands + DIMA(g_DotCommands);
|
|
Cmd++)
|
|
{
|
|
if (!(Cmd->Mode & MODE_INTERNAL) &&
|
|
(Cmd->Mode & Mode) == Mode)
|
|
{
|
|
PSTR Desc;
|
|
int StartBlanks;
|
|
|
|
dprintf(" .%s", Cmd->Name);
|
|
StartBlanks = 4 + strlen(Cmd->Name);
|
|
if (*Cmd->Args)
|
|
{
|
|
dprintf(" %s", Cmd->Args);
|
|
StartBlanks += 1 + strlen(Cmd->Args);
|
|
}
|
|
dprintf(" - ");
|
|
StartBlanks += 3;
|
|
|
|
Desc = Cmd->Desc;
|
|
while (*Desc)
|
|
{
|
|
PSTR Nl;
|
|
|
|
Nl = strchr(Desc, '\n');
|
|
if (Nl)
|
|
{
|
|
dprintf("%.*s%.*s",
|
|
(int)(Nl - Desc) + 1, Desc,
|
|
StartBlanks, g_Blanks);
|
|
Desc = Nl + 1;
|
|
}
|
|
else
|
|
{
|
|
dprintf("%s", Desc);
|
|
break;
|
|
}
|
|
}
|
|
|
|
dprintf("\n");
|
|
}
|
|
}
|
|
|
|
dprintf("\nUse \".hh <command>\" or open debugger.chm in the debuggers "
|
|
"directory to get\n"
|
|
"detailed documentation on a command.\n");
|
|
dprintf("\n");
|
|
}
|
|
|
|
#define MAX_DOT_COMMAND 32
|
|
|
|
BOOL
|
|
DotCommand(DebugClient* Client, BOOL Bang)
|
|
{
|
|
ULONG Index = 0;
|
|
char Cmd[MAX_DOT_COMMAND];
|
|
char Ch;
|
|
|
|
// Read in up to the first few alpha characters into
|
|
// Cmd, converting to lower case.
|
|
|
|
while (Index < MAX_DOT_COMMAND)
|
|
{
|
|
Ch = (char)tolower(*g_CurCmd);
|
|
if ((Ch >= 'a' && Ch <= 'z') || Ch == '-' || Ch == '+' || Ch == '_')
|
|
{
|
|
Cmd[Index++] = Ch;
|
|
g_CurCmd++;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If all characters read, then too big, else terminate.
|
|
if (Index == MAX_DOT_COMMAND)
|
|
{
|
|
return FALSE;
|
|
}
|
|
Cmd[Index] = '\0';
|
|
|
|
DOT_COMMAND* DotCmd;
|
|
ULONG Mode = CurrentMode();
|
|
|
|
if (Bang)
|
|
{
|
|
Mode |= MODE_BANG;
|
|
}
|
|
|
|
for (DotCmd = g_DotCommands;
|
|
DotCmd < g_DotCommands + DIMA(g_DotCommands);
|
|
DotCmd++)
|
|
{
|
|
if (DotCmd->Func &&
|
|
(DotCmd->Mode & Mode) == Mode &&
|
|
!strcmp(Cmd, DotCmd->Name))
|
|
{
|
|
DotCmd->Func(DotCmd, Client);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|