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.
550 lines
16 KiB
550 lines
16 KiB
/*++
|
|
|
|
Copyright (c) 2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
event.cxx
|
|
|
|
Abstract:
|
|
|
|
This file contains the routines to track and handle
|
|
debugger events.
|
|
|
|
Author:
|
|
|
|
Jason Hartman (JasonHa) 2000-11-20
|
|
|
|
Environment:
|
|
|
|
User Mode
|
|
|
|
--*/
|
|
|
|
#include "precomp.hxx"
|
|
|
|
BOOL gbSymbolsNotLoaded = TRUE;
|
|
|
|
ULONG UniqueTargetState = INVALID_UNIQUE_STATE;
|
|
|
|
#if DBG && 0
|
|
ULONG
|
|
DbgEventPrint(
|
|
IN PCHAR Format,
|
|
...
|
|
)
|
|
{
|
|
va_list arglist;
|
|
|
|
va_start(arglist, Format);
|
|
return vDbgPrintExWithPrefix("Event: ", -1, 0, Format, arglist);
|
|
}
|
|
|
|
#else
|
|
#define DbgEventPrint
|
|
#endif
|
|
|
|
typedef struct {
|
|
PDEBUG_CLIENT Client;
|
|
BOOL ParamsRead;
|
|
} MonitorThreadParams;
|
|
|
|
|
|
DWORD WINAPI EventMonitorThread(MonitorThreadParams *);
|
|
|
|
|
|
class EventMonitorCallbacks : public DebugBaseEventCallbacks
|
|
{
|
|
private:
|
|
ULONG RefCount;
|
|
|
|
public:
|
|
|
|
EventMonitorCallbacks()
|
|
{
|
|
RefCount = 1;
|
|
}
|
|
|
|
// IUnknown
|
|
STDMETHOD_(ULONG, AddRef)(
|
|
THIS
|
|
)
|
|
{
|
|
RefCount++;
|
|
|
|
return RefCount;
|
|
}
|
|
|
|
STDMETHOD_(ULONG, Release)(
|
|
THIS
|
|
)
|
|
{
|
|
RefCount--;
|
|
|
|
if (RefCount == 0)
|
|
{
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
return RefCount;
|
|
}
|
|
|
|
|
|
// IDebugEventCallbacks.
|
|
|
|
STDMETHOD(GetInterestMask)(
|
|
THIS_
|
|
OUT PULONG Mask
|
|
)
|
|
{
|
|
DbgEventPrint("GetInterestMask\n");
|
|
|
|
if (Mask != NULL)
|
|
{
|
|
*Mask = DEBUG_EVENT_SESSION_STATUS |
|
|
DEBUG_EVENT_CHANGE_DEBUGGEE_STATE |
|
|
DEBUG_EVENT_CHANGE_ENGINE_STATE |
|
|
DEBUG_EVENT_CHANGE_SYMBOL_STATE |
|
|
DEBUG_EVENT_UNLOAD_MODULE;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHOD(Breakpoint)(
|
|
THIS_
|
|
IN PDEBUG_BREAKPOINT Bp
|
|
)
|
|
{
|
|
DbgEventPrint("BP\n");
|
|
return DEBUG_STATUS_NO_CHANGE;
|
|
}
|
|
|
|
STDMETHOD(Exception)(
|
|
THIS_
|
|
IN PEXCEPTION_RECORD64 Exception,
|
|
IN ULONG FirstChance
|
|
)
|
|
{
|
|
DbgEventPrint("Exception\n");
|
|
return DEBUG_STATUS_NO_CHANGE;
|
|
}
|
|
|
|
STDMETHOD(CreateThread)(
|
|
THIS_
|
|
IN ULONG64 Handle,
|
|
IN ULONG64 DataOffset,
|
|
IN ULONG64 StartOffset
|
|
)
|
|
{
|
|
DbgEventPrint("CreateThread\n");
|
|
return DEBUG_STATUS_NO_CHANGE;
|
|
}
|
|
|
|
STDMETHOD(ExitThread)(
|
|
THIS_
|
|
IN ULONG ExitCode
|
|
)
|
|
{
|
|
DbgEventPrint("ExitThread\n");
|
|
return DEBUG_STATUS_NO_CHANGE;
|
|
}
|
|
|
|
STDMETHOD(CreateProcess)(
|
|
THIS_
|
|
IN ULONG64 ImageFileHandle,
|
|
IN ULONG64 Handle,
|
|
IN ULONG64 BaseOffset,
|
|
IN ULONG ModuleSize,
|
|
IN PCSTR ModuleName,
|
|
IN PCSTR ImageName,
|
|
IN ULONG CheckSum,
|
|
IN ULONG TimeDateStamp,
|
|
IN ULONG64 InitialThreadHandle,
|
|
IN ULONG64 ThreadDataOffset,
|
|
IN ULONG64 StartOffset
|
|
)
|
|
{
|
|
DbgEventPrint("CreateProcess\n");
|
|
return DEBUG_STATUS_NO_CHANGE;
|
|
}
|
|
|
|
STDMETHOD(ExitProcess)(
|
|
THIS_
|
|
IN ULONG ExitCode
|
|
)
|
|
{
|
|
DbgEventPrint("ExitProcess\n");
|
|
return DEBUG_STATUS_NO_CHANGE;
|
|
}
|
|
|
|
STDMETHOD(LoadModule)(
|
|
THIS_
|
|
IN ULONG64 ImageFileHandle,
|
|
IN ULONG64 BaseOffset,
|
|
IN ULONG ModuleSize,
|
|
IN PCSTR ModuleName,
|
|
IN PCSTR ImageName,
|
|
IN ULONG CheckSum,
|
|
IN ULONG TimeDateStamp
|
|
)
|
|
{
|
|
DbgEventPrint("LoadModule:\n"
|
|
" ModuleName: %s\n"
|
|
" ImageName: %s\n"
|
|
" BaseOffset: %I64x\n",
|
|
ModuleName, ImageName, BaseOffset);
|
|
return DEBUG_STATUS_NO_CHANGE;
|
|
}
|
|
|
|
STDMETHOD(UnloadModule)(
|
|
THIS_
|
|
IN PCSTR ImageBaseName,
|
|
IN ULONG64 BaseOffset
|
|
)
|
|
{
|
|
// Don't use Image base name for now - Debugger bug
|
|
//DbgEventPrint("UnloadModule %s @ %I64x\n", ImageBaseName, BaseOffset);
|
|
DbgEventPrint("UnloadModule ? @ %I64x\n", BaseOffset);
|
|
return DEBUG_STATUS_NO_CHANGE;
|
|
}
|
|
|
|
STDMETHOD(SystemError)(
|
|
THIS_
|
|
IN ULONG Error,
|
|
IN ULONG Level
|
|
)
|
|
{
|
|
DbgEventPrint("SystemError(%lu, %lu)\n", Error, Level);
|
|
return DEBUG_STATUS_NO_CHANGE;
|
|
}
|
|
|
|
STDMETHOD(SessionStatus)(
|
|
THIS_
|
|
IN ULONG Status
|
|
)
|
|
{
|
|
DbgEventPrint("SessionStatus(%lu)\n", Status);
|
|
if (Status == DEBUG_SESSION_ACTIVE) DbgEventPrint("DEBUG_SESSION_ACTIVE\n");
|
|
if (Status == DEBUG_SESSION_END_SESSION_ACTIVE_TERMINATE) DbgEventPrint("DEBUG_SESSION_END_SESSION_ACTIVE_TERMINATE\n");
|
|
if (Status == DEBUG_SESSION_END_SESSION_ACTIVE_DETACH) DbgEventPrint("DEBUG_SESSION_END_SESSION_ACTIVE_DETACH\n");
|
|
if (Status == DEBUG_SESSION_END_SESSION_PASSIVE) DbgEventPrint("DEBUG_SESSION_END_SESSION_PASSIVE\n");
|
|
if (Status == DEBUG_SESSION_END) DbgEventPrint("DEBUG_SESSION_END\n");
|
|
if (Status == DEBUG_SESSION_REBOOT) DbgEventPrint("DEBUG_SESSION_REBOOT\n");
|
|
if (Status == DEBUG_SESSION_HIBERNATE) DbgEventPrint("DEBUG_SESSION_HIBERNATE\n");
|
|
if (Status == DEBUG_SESSION_FAILURE) DbgEventPrint("DEBUG_SESSION_FAILURE\n");
|
|
return DEBUG_STATUS_NO_CHANGE;
|
|
}
|
|
|
|
STDMETHOD(ChangeDebuggeeState)(
|
|
THIS_
|
|
IN ULONG Flags,
|
|
IN ULONG64 Argument
|
|
)
|
|
{
|
|
DbgEventPrint("ChangeDebuggeeState(0x%lx, 0x%I64x)\n", Flags, Argument);
|
|
if (Flags == DEBUG_CDS_ALL)
|
|
{
|
|
DbgEventPrint("DEBUG_CDS_ALL\n");
|
|
UniqueTargetState++;
|
|
}
|
|
else
|
|
{
|
|
if (Flags & DEBUG_CDS_REGISTERS) DbgEventPrint("DEBUG_CDS_REGISTERS\n");
|
|
if (Flags & DEBUG_CDS_DATA)
|
|
{
|
|
DbgEventPrint("DEBUG_CDS_DATA\n");
|
|
UniqueTargetState++;
|
|
}
|
|
}
|
|
if (UniqueTargetState==INVALID_UNIQUE_STATE) UniqueTargetState++;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHOD(ChangeEngineState)(
|
|
THIS_
|
|
IN ULONG Flags,
|
|
IN ULONG64 Argument
|
|
)
|
|
{
|
|
//DbgEventPrint("ChangeEngineState(0x%lx, 0x%I64x)\n", Flags, Argument);
|
|
if (Flags == DEBUG_CES_ALL)
|
|
{
|
|
DbgEventPrint("DEBUG_CES_ALL\n");
|
|
UniqueTargetState++;
|
|
if (UniqueTargetState==INVALID_UNIQUE_STATE) UniqueTargetState++;
|
|
}
|
|
else
|
|
{
|
|
if (Flags & DEBUG_CES_CURRENT_THREAD) DbgEventPrint("DEBUG_CES_CURRENT_THREAD\n");
|
|
if (Flags & DEBUG_CES_EFFECTIVE_PROCESSOR) DbgEventPrint("DEBUG_CES_EFFECTIVE_PROCESSOR\n");
|
|
if (Flags & DEBUG_CES_BREAKPOINTS) DbgEventPrint("DEBUG_CES_BREAKPOINTS\n");
|
|
if (Flags & DEBUG_CES_CODE_LEVEL) DbgEventPrint("DEBUG_CES_CODE_LEVEL\n");
|
|
if (Flags & DEBUG_CES_EXECUTION_STATUS)
|
|
{
|
|
DbgEventPrint("DEBUG_CES_EXECUTION_STATUS\n");
|
|
switch (Argument & DEBUG_STATUS_MASK)
|
|
{
|
|
case DEBUG_STATUS_NO_CHANGE: DbgPrint("Exec Status: DEBUG_STATUS_NO_CHANGE\n"); break;
|
|
case DEBUG_STATUS_GO: DbgPrint("Exec Status: DEBUG_STATUS_GO\n"); break;
|
|
case DEBUG_STATUS_GO_HANDLED: DbgPrint("Exec Status: DEBUG_STATUS_GO_HANDLED\n"); break;
|
|
case DEBUG_STATUS_GO_NOT_HANDLED: DbgPrint("Exec Status: DEBUG_STATUS_GO_NOT_HANDLED\n"); break;
|
|
case DEBUG_STATUS_STEP_OVER: DbgPrint("Exec Status: DEBUG_STATUS_STEP_OVER\n"); break;
|
|
case DEBUG_STATUS_STEP_INTO: DbgPrint("Exec Status: DEBUG_STATUS_STEP_INTO\n"); break;
|
|
case DEBUG_STATUS_BREAK: DbgPrint("Exec Status: DEBUG_STATUS_BREAK\n"); break;
|
|
case DEBUG_STATUS_NO_DEBUGGEE: DbgPrint("Exec Status: DEBUG_STATUS_NO_DEBUGGEE\n"); break;
|
|
case DEBUG_STATUS_STEP_BRANCH: DbgPrint("Exec Status: DEBUG_STATUS_STEP_BRANCH\n"); break;
|
|
case DEBUG_STATUS_IGNORE_EVENT: DbgPrint("Exec Status: DEBUG_STATUS_IGNORE_EVENT\n"); break;
|
|
default: DbgPrint("Exec Status: Unknown\n"); break;
|
|
}
|
|
if (Argument & DEBUG_STATUS_INSIDE_WAIT) DbgPrint("Exec Status: DEBUG_STATUS_INSIDE_WAIT\n");
|
|
if ((Argument & DEBUG_STATUS_MASK) != DEBUG_STATUS_NO_CHANGE)
|
|
{
|
|
UniqueTargetState++;
|
|
if (UniqueTargetState==INVALID_UNIQUE_STATE) UniqueTargetState++;
|
|
}
|
|
}
|
|
if (Flags & DEBUG_CES_ENGINE_OPTIONS) DbgEventPrint("DEBUG_CES_ENGINE_OPTIONS\n");
|
|
if (Flags & DEBUG_CES_LOG_FILE) DbgEventPrint("DEBUG_CES_LOG_FILE\n");
|
|
//if (Flags & DEBUG_CES_RADIX) DbgEventPrint("DEBUG_CES_RADIX\n");
|
|
if (Flags & DEBUG_CES_EVENT_FILTERS) DbgEventPrint("DEBUG_CES_EVENT_FILTERS\n");
|
|
if (Flags & DEBUG_CES_PROCESS_OPTIONS) DbgEventPrint("DEBUG_CES_PROCESS_OPTIONS\n");
|
|
if (Flags & DEBUG_CES_EXTENSIONS) DbgEventPrint("DEBUG_CES_EXTENSIONS\n");
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHOD(ChangeSymbolState)(
|
|
THIS_
|
|
IN ULONG Flags,
|
|
IN ULONG64 Argument
|
|
)
|
|
{
|
|
DbgEventPrint("ChangeSymbolState(0x%lx, 0x%I64x)\n", Flags, Argument);
|
|
gbSymbolsNotLoaded = gbSymbolsNotLoaded || (Flags & DEBUG_CSS_UNLOADS);
|
|
UniqueTargetState++;
|
|
if (UniqueTargetState==INVALID_UNIQUE_STATE) UniqueTargetState++;
|
|
return S_OK;
|
|
}
|
|
};
|
|
|
|
typedef enum {
|
|
NO_DISPATCHING,
|
|
NEED_DISPATCH,
|
|
DISPATCHED
|
|
} MonitorState;
|
|
|
|
LONG g_MonitorState = NO_DISPATCHING;
|
|
PDEBUG_CLIENT g_pMonitorClient = NULL;
|
|
BOOL g_MonitorThreadSet = FALSE;
|
|
|
|
DWORD
|
|
WINAPI
|
|
EventMonitorThread(
|
|
MonitorThreadParams *Params
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
PDEBUG_CLIENT Client;
|
|
MonitorThreadParams ParamCopy;
|
|
HMODULE hModule = NULL;
|
|
TCHAR ModulePath[256];
|
|
|
|
if (Params != NULL && Params->Client != NULL)
|
|
{
|
|
ASSERTMSG("EventMonitorThread not started with NEED_DISPATCH.\n", g_MonitorState == NEED_DISPATCH);
|
|
|
|
if (GetModuleFileName(ghDllInst, ModulePath, sizeof(ModulePath)/sizeof(TCHAR)) == 0)
|
|
{
|
|
DbgPrint("EventMonitorThread failed to get Module path.\n");
|
|
hr = S_FALSE;
|
|
}
|
|
else
|
|
{
|
|
// LoadLibrary so we have a reference while this thread lives
|
|
hModule = LoadLibrary(ModulePath);
|
|
|
|
if (hModule != ghDllInst)
|
|
{
|
|
DbgPrint("EventMonitorThread retrieving an hModule different from ghDllInst.\n");
|
|
hr = S_FALSE;
|
|
}
|
|
}
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
Params->Client->AddRef();
|
|
|
|
ParamCopy = *Params;
|
|
Params->ParamsRead = TRUE;
|
|
Params = &ParamCopy;
|
|
|
|
hr = Params->Client->CreateClient(&Client);
|
|
DbgPrint("EventMonitorThread created client %p.\n", Client);
|
|
|
|
Params->Client->Release();
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
EventMonitorCallbacks *EventMonitor = new EventMonitorCallbacks;
|
|
|
|
if (EventMonitor != NULL)
|
|
{
|
|
hr = Client->SetEventCallbacks(EventMonitor);
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
// Pass monitoring client back to caller.
|
|
Client->AddRef();
|
|
if (InterlockedCompareExchangePointer((PVOID*)&g_pMonitorClient, Client, NULL) == NULL &&
|
|
InterlockedCompareExchange(&g_MonitorState, DISPATCHED, NEED_DISPATCH) == NEED_DISPATCH)
|
|
{
|
|
DbgPrint("EventMonitorThread dispatching for client %p.\n", Client);
|
|
UniqueTargetState++;
|
|
hr = Client->DispatchCallbacks(INFINITE);
|
|
}
|
|
else
|
|
{
|
|
// Another EventMonitorThread has already started or
|
|
// ReleaseEventCallbacks has already been called; so,
|
|
// release this client and
|
|
// NULL global monitor client if we set it
|
|
DbgPrint("EventMonitorThread exiting instead of dispatching for client %p.\n", Client);
|
|
InterlockedCompareExchangePointer((PVOID*)&g_pMonitorClient, NULL, Client);
|
|
Client->Release();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
OutputControl OutCtl(Client);
|
|
OutCtl.OutErr("EventMonitorThread callbacks setup failed, %s.\n", pszHRESULT(hr));
|
|
}
|
|
|
|
EventMonitor->Release();
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
Client->Release();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
|
|
DbgPrint("EventMonitorThread calling ExitThread().\n");
|
|
|
|
FreeLibraryAndExitThread(hModule, (DWORD)hr);
|
|
}
|
|
|
|
|
|
void
|
|
ReleaseEventCallbacks(
|
|
PDEBUG_CLIENT Client
|
|
)
|
|
{
|
|
if (g_MonitorThreadSet)
|
|
{
|
|
if (InterlockedExchange(&g_MonitorState, NO_DISPATCHING) == DISPATCHED)
|
|
{
|
|
PDEBUG_CLIENT pMonitorClient;
|
|
|
|
pMonitorClient = (PDEBUG_CLIENT)InterlockedExchangePointer((PVOID *)&g_pMonitorClient, NULL);
|
|
|
|
ASSERTMSG("g_MonitorState shows g_pMonitorClient should be set.\n", pMonitorClient != NULL);
|
|
|
|
if (Client == NULL)
|
|
{
|
|
if (GetDebugClient(&Client) != S_OK)
|
|
{
|
|
Client = pMonitorClient;
|
|
Client->AddRef();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Client->AddRef();
|
|
}
|
|
|
|
Client->ExitDispatch(pMonitorClient);
|
|
pMonitorClient->Release();
|
|
Client->Release();
|
|
}
|
|
|
|
g_MonitorThreadSet = FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
HRESULT
|
|
SetEventCallbacks(
|
|
PDEBUG_CLIENT Client
|
|
)
|
|
{
|
|
HRESULT hr = S_FALSE;
|
|
|
|
if (!g_MonitorThreadSet)
|
|
{
|
|
MonitorThreadParams NewThreadParams = { Client, FALSE };
|
|
HANDLE hThread;
|
|
DWORD ThreadID;
|
|
LONG PrevMonitorState;
|
|
|
|
PrevMonitorState = InterlockedExchange(&g_MonitorState, NEED_DISPATCH);
|
|
ASSERTMSG("Previous EventMonitor thread was never shutdown properly.\n", PrevMonitorState != DISPATCHED);
|
|
ASSERTMSG("Previous EventMonitor thread never completed setup.\n", PrevMonitorState != NEED_DISPATCH);
|
|
|
|
g_pMonitorClient = NULL;
|
|
|
|
hThread = CreateThread(NULL,
|
|
0,
|
|
(LPTHREAD_START_ROUTINE)EventMonitorThread,
|
|
&NewThreadParams,
|
|
0,
|
|
&ThreadID);
|
|
|
|
if (hThread)
|
|
{
|
|
// Default ExitCode to STILL_ACTIVE since it doesn't matter
|
|
// if the Params were read before we started checking.
|
|
DWORD ExitCode = STILL_ACTIVE;
|
|
|
|
while (!NewThreadParams.ParamsRead)
|
|
{
|
|
ExitCode = 0;
|
|
if (!GetExitCodeThread(hThread, &ExitCode))
|
|
DbgPrint("GetExitCodeThread returned error %lx.\n", GetLastError());
|
|
if (ExitCode != STILL_ACTIVE)
|
|
{
|
|
break;
|
|
}
|
|
|
|
SleepEx(10, TRUE);
|
|
}
|
|
|
|
if (ExitCode == STILL_ACTIVE)
|
|
{
|
|
hr = S_OK;
|
|
g_MonitorThreadSet = TRUE;
|
|
}
|
|
|
|
CloseHandle(hThread);
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
EventCallbacksReady(
|
|
PDEBUG_CLIENT Client
|
|
)
|
|
{
|
|
return (g_MonitorThreadSet && g_MonitorState == DISPATCHED) ? S_OK : S_FALSE;
|
|
}
|