Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/dbgeng/dbgeng.c | 39 ++++++- dlls/dbgeng/tests/dbgeng.c | 231 +++++++++++++++++++++++++++++++++++++ include/dbgeng.h | 89 ++++++++++++++ 3 files changed, 357 insertions(+), 2 deletions(-)
diff --git a/dlls/dbgeng/dbgeng.c b/dlls/dbgeng/dbgeng.c index 75ae4505db..9aed1b0506 100644 --- a/dlls/dbgeng/dbgeng.c +++ b/dlls/dbgeng/dbgeng.c @@ -31,9 +31,17 @@
#include "wine/debug.h" #include "wine/heap.h" +#include "wine/list.h"
WINE_DEFAULT_DEBUG_CHANNEL(dbgeng);
+struct target_process +{ + struct list entry; + unsigned int pid; + unsigned int attach_flags; +}; + struct debug_client { IDebugClient IDebugClient_iface; @@ -42,6 +50,7 @@ struct debug_client IDebugControl2 IDebugControl2_iface; LONG refcount; ULONG engine_options; + struct list targets; };
static struct debug_client *impl_from_IDebugClient(IDebugClient *iface) @@ -113,11 +122,19 @@ static ULONG STDMETHODCALLTYPE debugclient_Release(IDebugClient *iface) { struct debug_client *debug_client = impl_from_IDebugClient(iface); ULONG refcount = InterlockedDecrement(&debug_client->refcount); + struct target_process *cur, *cur2;
TRACE("%p, %d.\n", debug_client, refcount);
if (!refcount) + { + LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &debug_client->targets, struct target_process, entry) + { + list_remove(&cur->entry); + heap_free(cur); + } heap_free(debug_client); + }
return refcount; } @@ -195,9 +212,26 @@ static HRESULT STDMETHODCALLTYPE debugclient_GetRunningProcessDescription(IDebug
static HRESULT STDMETHODCALLTYPE debugclient_AttachProcess(IDebugClient *iface, ULONG64 server, ULONG pid, ULONG flags) { - FIXME("%p, %s, %u, %#x stub.\n", iface, wine_dbgstr_longlong(server), pid, flags); + struct debug_client *debug_client = impl_from_IDebugClient(iface); + struct target_process *process;
- return E_NOTIMPL; + TRACE("%p, %s, %u, %#x.\n", iface, wine_dbgstr_longlong(server), pid, flags); + + if (server) + { + FIXME("Remote debugging is not supported.\n"); + return E_NOTIMPL; + } + + if (!(process = heap_alloc(sizeof(*process)))) + return E_OUTOFMEMORY; + + process->pid = pid; + process->attach_flags = flags; + + list_add_head(&debug_client->targets, &process->entry); + + return S_OK; }
static HRESULT STDMETHODCALLTYPE debugclient_CreateProcess(IDebugClient *iface, ULONG64 server, char *cmdline, @@ -2142,6 +2176,7 @@ HRESULT WINAPI DebugCreate(REFIID riid, void **obj) debug_client->IDebugSymbols_iface.lpVtbl = &debugsymbolsvtbl; debug_client->IDebugControl2_iface.lpVtbl = &debugcontrolvtbl; debug_client->refcount = 1; + list_init(&debug_client->targets);
unk = (IUnknown *)&debug_client->IDebugClient_iface;
diff --git a/dlls/dbgeng/tests/dbgeng.c b/dlls/dbgeng/tests/dbgeng.c index a76b25a128..f5d50c899a 100644 --- a/dlls/dbgeng/tests/dbgeng.c +++ b/dlls/dbgeng/tests/dbgeng.c @@ -17,6 +17,7 @@ */
#include <stdarg.h> +#include <stdio.h> #include <string.h>
#include "windef.h" @@ -99,7 +100,237 @@ static void test_engine_options(void) control->lpVtbl->Release(control); }
+static HRESULT WINAPI event_callbacks_QueryInterface(IDebugEventCallbacks *iface, REFIID riid, void **out) +{ + if (IsEqualIID(riid, &IID_IDebugEventCallbacks) || + IsEqualIID(riid, &IID_IUnknown)) + { + *out = iface; + iface->lpVtbl->AddRef(iface); + return S_OK; + } + + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI event_callbacks_AddRef(IDebugEventCallbacks *iface) +{ + return 2; +} + +static ULONG WINAPI event_callbacks_Release(IDebugEventCallbacks *iface) +{ + return 1; +} + +static HRESULT WINAPI event_callbacks_GetInterestMask(IDebugEventCallbacks *iface, ULONG *mask) +{ + *mask = ~0u; + return S_OK; +} + +static HRESULT WINAPI event_callbacks_Breakpoint(IDebugEventCallbacks *iface, PDEBUG_BREAKPOINT breakpoint) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI event_callbacks_Exception(IDebugEventCallbacks *iface, EXCEPTION_RECORD64 *exception, + ULONG first_chance) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI event_callbacks_CreateThread(IDebugEventCallbacks *iface, ULONG64 handle, ULONG64 data_offset, + ULONG64 start_offset) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI event_callbacks_ExitThread(IDebugEventCallbacks *iface, ULONG exit_code) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI event_callbacks_CreateProcess(IDebugEventCallbacks *iface, ULONG64 image_handle, ULONG64 handle, + ULONG64 base_offset, ULONG module_size, const char *module_name, const char *image_name, ULONG checksum, + ULONG timedatestamp, ULONG64 initial_thread_handle, ULONG64 thread_data_offset, ULONG64 start_offset) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI event_callbacks_ExitProcess(IDebugEventCallbacks *iface, ULONG exit_code) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI event_callbacks_LoadModule(IDebugEventCallbacks *iface, ULONG64 image_handle, + ULONG64 base_offset, ULONG module_size, const char *module_name, const char *image_name, ULONG checksum, + ULONG timedatestamp) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI event_callbacks_UnloadModule(IDebugEventCallbacks *iface, const char *image_basename, + ULONG64 base_offset) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI event_callbacks_SystemError(IDebugEventCallbacks *iface, ULONG error, ULONG level) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI event_callbacks_SessionStatus(IDebugEventCallbacks *iface, ULONG status) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI event_callbacks_ChangeDebuggeeState(IDebugEventCallbacks *iface, ULONG flags, ULONG64 argument) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI event_callbacks_ChangeEngineState(IDebugEventCallbacks *iface, ULONG flags, ULONG64 argument) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI event_callbacks_ChangeSymbolState(IDebugEventCallbacks *iface, ULONG flags, ULONG64 argument) + +{ + return E_NOTIMPL; +} + +static const IDebugEventCallbacksVtbl event_callbacks_vtbl = +{ + event_callbacks_QueryInterface, + event_callbacks_AddRef, + event_callbacks_Release, + event_callbacks_GetInterestMask, + event_callbacks_Breakpoint, + event_callbacks_Exception, + event_callbacks_CreateThread, + event_callbacks_ExitThread, + event_callbacks_CreateProcess, + event_callbacks_ExitProcess, + event_callbacks_LoadModule, + event_callbacks_UnloadModule, + event_callbacks_SystemError, + event_callbacks_SessionStatus, + event_callbacks_ChangeDebuggeeState, + event_callbacks_ChangeEngineState, + event_callbacks_ChangeSymbolState, +}; + +static const char *event_name = "dbgeng_test_event"; + +static void test_attach(void) +{ + IDebugEventCallbacks event_callbacks = { &event_callbacks_vtbl }; + PROCESS_INFORMATION info; + char path_name[MAX_PATH]; + IDebugControl *control; + IDebugClient *client; + STARTUPINFOA startup; + BOOL is_debugged; + HANDLE event; + char **argv; + HRESULT hr; + BOOL ret; + + hr = DebugCreate(&IID_IDebugClient, (void **)&client); + ok(hr == S_OK, "Failed to create engine object, hr %#x.\n", hr); + + hr = client->lpVtbl->QueryInterface(client, &IID_IDebugControl, (void **)&control); + ok(hr == S_OK, "Failed to get interface pointer, hr %#x.\n", hr); + + hr = client->lpVtbl->SetEventCallbacks(client, &event_callbacks); +todo_wine + ok(hr == S_OK, "Failed to set event callbacks, hr %#x.\n", hr); + + event = CreateEventA(NULL, FALSE, FALSE, event_name); + ok(event != NULL, "Failed to create event.\n"); + + winetest_get_mainargs(&argv); + memset(&startup, 0, sizeof(startup)); + startup.cb = sizeof(startup); + sprintf(path_name, "%s dbgeng target", argv[0]); + ret = CreateProcessA(NULL, path_name, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info), + ok(ret, "Failed to create target process.\n"); + + is_debugged = TRUE; + CheckRemoteDebuggerPresent(info.hProcess, &is_debugged); + ok(!is_debugged, "Unexpected mode.\n"); + + /* Non-invasive mode. */ + hr = client->lpVtbl->AttachProcess(client, 0, info.dwProcessId, DEBUG_ATTACH_NONINVASIVE); + ok(hr == S_OK, "Failed to attach to process, hr %#x.\n", hr); + + is_debugged = TRUE; + ret = CheckRemoteDebuggerPresent(info.hProcess, &is_debugged); + ok(ret, "Failed to check target status.\n"); + ok(!is_debugged, "Unexpected mode.\n"); + + hr = control->lpVtbl->WaitForEvent(control, 0, INFINITE); +todo_wine + ok(hr == S_OK, "Waiting for event failed, hr %#x.\n", hr); + + is_debugged = TRUE; + ret = CheckRemoteDebuggerPresent(info.hProcess, &is_debugged); + ok(ret, "Failed to check target status.\n"); + ok(!is_debugged, "Unexpected mode.\n"); + + hr = client->lpVtbl->DetachProcesses(client); +todo_wine + ok(hr == S_OK, "Failed to detach, hr %#x.\n", hr); + + hr = client->lpVtbl->EndSession(client, DEBUG_END_ACTIVE_DETACH); +todo_wine + ok(hr == S_OK, "Failed to end session, hr %#x.\n", hr); + + SetEvent(event); + + winetest_wait_child_process(info.hProcess); + + CloseHandle(info.hProcess); + CloseHandle(info.hThread); + + CloseHandle(event); + + client->lpVtbl->Release(client); + control->lpVtbl->Release(control); +} + +static void target_proc(void) +{ + HANDLE event = OpenEventA(SYNCHRONIZE, FALSE, event_name); + + ok(event != NULL, "Failed to open event handle.\n"); + + for (;;) + { + if (WaitForSingleObject(event, 100) == WAIT_OBJECT_0) + break; + } + + CloseHandle(event); +} + START_TEST(dbgeng) { + char **argv; + int argc; + + argc = winetest_get_mainargs(&argv); + + if (argc >= 3 && !strcmp(argv[2], "target")) + { + target_proc(); + return; + } + test_engine_options(); + test_attach(); } diff --git a/include/dbgeng.h b/include/dbgeng.h index 8c5c78c07f..3b7ff2be4f 100644 --- a/include/dbgeng.h +++ b/include/dbgeng.h @@ -73,6 +73,95 @@ DEFINE_GUID(IID_IDebugSystemObjects3, 0xe9676e2f, 0xe286, 0x4ea3, 0xb0, 0xf9 #define DEBUG_ATTACH_INVASIVE_RESUME_PROCESS 0x00000010 #define DEBUG_ATTACH_NONINVASIVE_ALLOW_PARTIAL 0x00000020
+/* EndSession() flags */ +#define DEBUG_END_PASSIVE 0x00000000 +#define DEBUG_END_ACTIVE_TERMINATE 0x00000001 +#define DEBUG_END_ACTIVE_DETACH 0x00000002 +#define DEBUG_END_REENTRANT 0x00000003 +#define DEBUG_END_DISCONNECT 0x00000004 + +/* ChangeEngineState() flags */ +#define DEBUG_CES_CURRENT_THREAD 0x00000001 +#define DEBUG_CES_EFFECTIVE_PROCESSOR 0x00000002 +#define DEBUG_CES_BREAKPOINTS 0x00000004 +#define DEBUG_CES_CODE_LEVEL 0x00000008 +#define DEBUG_CES_EXECUTION_STATUS 0x00000010 +#define DEBUG_CES_ENGINE_OPTIONS 0x00000020 +#define DEBUG_CES_LOG_FILE 0x00000040 +#define DEBUG_CES_RADIX 0x00000080 +#define DEBUG_CES_EVENT_FILTERS 0x00000100 +#define DEBUG_CES_PROCESS_OPTIONS 0x00000200 +#define DEBUG_CES_EXTENSIONS 0x00000400 +#define DEBUG_CES_SYSTEMS 0x00000800 +#define DEBUG_CES_ASSEMBLY_OPTIONS 0x00001000 +#define DEBUG_CES_EXPRESSION_SYNTAX 0x00002000 +#define DEBUG_CES_TEXT_REPLACEMENTS 0x00004000 +#define DEBUG_CES_ALL 0xffffffff + +#define DEBUG_STATUS_NO_CHANGE 0 +#define DEBUG_STATUS_GO 1 +#define DEBUG_STATUS_GO_HANDLED 2 +#define DEBUG_STATUS_GO_NOT_HANDLED 3 +#define DEBUG_STATUS_STEP_OVER 4 +#define DEBUG_STATUS_STEP_INTO 5 +#define DEBUG_STATUS_BREAK 6 +#define DEBUG_STATUS_NO_DEBUGGEE 7 +#define DEBUG_STATUS_STEP_BRANCH 8 +#define DEBUG_STATUS_IGNORE_EVENT 9 +#define DEBUG_STATUS_RESTART_REQUESTED 10 +#define DEBUG_STATUS_REVERSE_GO 11 +#define DEBUG_STATUS_REVERSE_STEP_BRANCH 12 +#define DEBUG_STATUS_REVERSE_STEP_OVER 13 +#define DEBUG_STATUS_REVERSE_STEP_INTO 14 +#define DEBUG_STATUS_OUT_OF_SYNC 15 +#define DEBUG_STATUS_WAIT_INPUT 16 +#define DEBUG_STATUS_TIMEOUT 17 +#define DEBUG_STATUS_MASK 0x1f + +/* ChangeSymbolState() flags */ +#define DEBUG_CSS_LOADS 0x00000001 +#define DEBUG_CSS_UNLOADS 0x00000002 +#define DEBUG_CSS_SCOPE 0x00000004 +#define DEBUG_CSS_PATHS 0x00000008 +#define DEBUG_CSS_SYMBOL_OPTIONS 0x00000010 +#define DEBUG_CSS_TYPE_OPTIONS 0x00000020 +#define DEBUG_CSS_COLLAPSE_CHILDREN 0x00000040 +#define DEBUG_CSS_ALL 0xffffffff + +/* SessionStatus() flags */ +#define DEBUG_SESSION_ACTIVE 0x00000000 +#define DEBUG_SESSION_END_SESSION_ACTIVE_TERMINATE 0x00000001 +#define DEBUG_SESSION_END_SESSION_ACTIVE_DETACH 0x00000002 +#define DEBUG_SESSION_END_SESSION_PASSIVE 0x00000003 +#define DEBUG_SESSION_END 0x00000004 +#define DEBUG_SESSION_REBOOT 0x00000005 +#define DEBUG_SESSION_HIBERNATE 0x00000006 +#define DEBUG_SESSION_FAILURE 0x00000007 + +/* ChangeDebuggeeState() flags */ +#define DEBUG_CDS_REGISTERS 0x00000001 +#define DEBUG_CDS_DATA 0x00000002 +#define DEBUG_CDS_REFRESH 0x00000004 +#define DEBUG_CDS_ALL 0xffffffff + +#define DEBUG_CDS_REFRESH_EVALUATE 1 +#define DEBUG_CDS_REFRESH_EXECUTE 2 +#define DEBUG_CDS_REFRESH_EXECUTECOMMANDFILE 3 +#define DEBUG_CDS_REFRESH_ADDBREAKPOINT 4 +#define DEBUG_CDS_REFRESH_REMOVEBREAKPOINT 5 +#define DEBUG_CDS_REFRESH_WRITEVIRTUAL 6 +#define DEBUG_CDS_REFRESH_WRITEVIRTUALUNCACHED 7 +#define DEBUG_CDS_REFRESH_WRITEPHYSICAL 8 +#define DEBUG_CDS_REFRESH_WRITEPHYSICAL2 9 +#define DEBUG_CDS_REFRESH_SETVALUE 10 +#define DEBUG_CDS_REFRESH_SETVALUE2 11 +#define DEBUG_CDS_REFRESH_SETSCOPE 12 +#define DEBUG_CDS_REFRESH_SETSCOPEFRAMEBYINDEX 13 +#define DEBUG_CDS_REFRESH_SETSCOPEFROMJITDEBUGINFO 14 +#define DEBUG_CDS_REFRESH_SETSCOPEFROMSTOREDEVENT 15 +#define DEBUG_CDS_REFRESH_INLINESTEP 16 +#define DEBUG_CDS_REFRESH_INLINESTEP_PSEUDO 17 + typedef struct _DEBUG_MODULE_PARAMETERS { ULONG64 Base;
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/dbgeng/dbgeng.c | 26 ++++++++++++++++++++++---- dlls/dbgeng/tests/dbgeng.c | 1 - 2 files changed, 22 insertions(+), 5 deletions(-)
diff --git a/dlls/dbgeng/dbgeng.c b/dlls/dbgeng/dbgeng.c index 9aed1b0506..f9faef06dd 100644 --- a/dlls/dbgeng/dbgeng.c +++ b/dlls/dbgeng/dbgeng.c @@ -51,6 +51,7 @@ struct debug_client LONG refcount; ULONG engine_options; struct list targets; + IDebugEventCallbacks *event_callbacks; };
static struct debug_client *impl_from_IDebugClient(IDebugClient *iface) @@ -133,6 +134,8 @@ static ULONG STDMETHODCALLTYPE debugclient_Release(IDebugClient *iface) list_remove(&cur->entry); heap_free(cur); } + if (debug_client->event_callbacks) + debug_client->event_callbacks->lpVtbl->Release(debug_client->event_callbacks); heap_free(debug_client); }
@@ -467,16 +470,31 @@ static HRESULT STDMETHODCALLTYPE debugclient_OutputIdentity(IDebugClient *iface,
static HRESULT STDMETHODCALLTYPE debugclient_GetEventCallbacks(IDebugClient *iface, IDebugEventCallbacks **callbacks) { - FIXME("%p, %p stub.\n", iface, callbacks); + struct debug_client *debug_client = impl_from_IDebugClient(iface);
- return E_NOTIMPL; + TRACE("%p, %p.\n", iface, callbacks); + + if (debug_client->event_callbacks) + { + *callbacks = debug_client->event_callbacks; + (*callbacks)->lpVtbl->AddRef(*callbacks); + } + + return S_OK; }
static HRESULT STDMETHODCALLTYPE debugclient_SetEventCallbacks(IDebugClient *iface, IDebugEventCallbacks *callbacks) { - FIXME("%p, %p stub.\n", iface, callbacks); + struct debug_client *debug_client = impl_from_IDebugClient(iface);
- return E_NOTIMPL; + TRACE("%p, %p.\n", iface, callbacks); + + if (debug_client->event_callbacks) + debug_client->event_callbacks->lpVtbl->Release(debug_client->event_callbacks); + if ((debug_client->event_callbacks = callbacks)) + debug_client->event_callbacks->lpVtbl->AddRef(debug_client->event_callbacks); + + return S_OK; }
static HRESULT STDMETHODCALLTYPE debugclient_FlushCallbacks(IDebugClient *iface) diff --git a/dlls/dbgeng/tests/dbgeng.c b/dlls/dbgeng/tests/dbgeng.c index f5d50c899a..baec60a9c9 100644 --- a/dlls/dbgeng/tests/dbgeng.c +++ b/dlls/dbgeng/tests/dbgeng.c @@ -247,7 +247,6 @@ static void test_attach(void) ok(hr == S_OK, "Failed to get interface pointer, hr %#x.\n", hr);
hr = client->lpVtbl->SetEventCallbacks(client, &event_callbacks); -todo_wine ok(hr == S_OK, "Failed to set event callbacks, hr %#x.\n", hr);
event = CreateEventA(NULL, FALSE, FALSE, event_name);