Signed-off-by: Nikolay Sivov <nsivov(a)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;
--
2.20.1