wine-wine/dlls/dbgeng/tests/dbgeng.c

527 lines
18 KiB
C

/*
* Copyright 2019 Nikolay Sivov for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include "windef.h"
#include "winbase.h"
#include "initguid.h"
#include "dbgeng.h"
#include "wine/test.h"
static void test_engine_options(void)
{
IDebugControl *control;
ULONG options;
HRESULT hr;
hr = DebugCreate(&IID_IDebugControl, (void **)&control);
ok(hr == S_OK, "Failed to create engine object, hr %#x.\n", hr);
options = 0xf;
hr = control->lpVtbl->GetEngineOptions(control, &options);
ok(hr == S_OK, "Failed to get engine options, hr %#x.\n", hr);
ok(options == 0, "Unexpected options %#x.\n", options);
hr = control->lpVtbl->AddEngineOptions(control, DEBUG_ENGOPT_INITIAL_BREAK);
ok(hr == S_OK, "Failed to add engine options, hr %#x.\n", hr);
options = 0;
hr = control->lpVtbl->GetEngineOptions(control, &options);
ok(hr == S_OK, "Failed to get engine options, hr %#x.\n", hr);
ok(options == DEBUG_ENGOPT_INITIAL_BREAK, "Unexpected options %#x.\n", options);
hr = control->lpVtbl->AddEngineOptions(control, 0x00800000);
ok(hr == E_INVALIDARG, "Unexpected hr %#x.\n", hr);
options = 0;
hr = control->lpVtbl->GetEngineOptions(control, &options);
ok(hr == S_OK, "Failed to get engine options, hr %#x.\n", hr);
ok(options == DEBUG_ENGOPT_INITIAL_BREAK, "Unexpected options %#x.\n", options);
hr = control->lpVtbl->RemoveEngineOptions(control, 0x00800000);
ok(hr == S_OK, "Failed to remove options, hr %#x.\n", hr);
hr = control->lpVtbl->AddEngineOptions(control, DEBUG_ENGOPT_IGNORE_DBGHELP_VERSION);
ok(hr == S_OK, "Failed to add engine options, hr %#x.\n", hr);
options = 0;
hr = control->lpVtbl->GetEngineOptions(control, &options);
ok(hr == S_OK, "Failed to get engine options, hr %#x.\n", hr);
ok(options == (DEBUG_ENGOPT_INITIAL_BREAK | DEBUG_ENGOPT_IGNORE_DBGHELP_VERSION),
"Unexpected options %#x.\n", options);
hr = control->lpVtbl->RemoveEngineOptions(control, DEBUG_ENGOPT_INITIAL_BREAK);
ok(hr == S_OK, "Failed to remove options, hr %#x.\n", hr);
options = 0;
hr = control->lpVtbl->GetEngineOptions(control, &options);
ok(hr == S_OK, "Failed to get engine options, hr %#x.\n", hr);
ok(options == DEBUG_ENGOPT_IGNORE_DBGHELP_VERSION, "Unexpected options %#x.\n", options);
hr = control->lpVtbl->SetEngineOptions(control, DEBUG_ENGOPT_INITIAL_BREAK);
ok(hr == S_OK, "Failed to set options, hr %#x.\n", hr);
options = 0;
hr = control->lpVtbl->GetEngineOptions(control, &options);
ok(hr == S_OK, "Failed to get engine options, hr %#x.\n", hr);
ok(options == DEBUG_ENGOPT_INITIAL_BREAK, "Unexpected options %#x.\n", options);
hr = control->lpVtbl->SetEngineOptions(control, 0x00800000);
ok(hr == E_INVALIDARG, "Unexpected hr %#x.\n", hr);
hr = control->lpVtbl->SetEngineOptions(control, 0x00800000 | DEBUG_ENGOPT_IGNORE_DBGHELP_VERSION);
ok(hr == E_INVALIDARG, "Unexpected hr %#x.\n", hr);
options = 0;
hr = control->lpVtbl->GetEngineOptions(control, &options);
ok(hr == S_OK, "Failed to get engine options, hr %#x.\n", hr);
ok(options == DEBUG_ENGOPT_INITIAL_BREAK, "Unexpected options %#x.\n", options);
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 BOOL create_target_process(const char *event_name, PROCESS_INFORMATION *info)
{
static const char *event_target_ready_name = "dbgeng_test_target_ready_event";
char path_name[MAX_PATH];
STARTUPINFOA startup;
HANDLE ready_event;
char **argv;
BOOL ret;
ready_event = CreateEventA(NULL, FALSE, FALSE, event_target_ready_name);
ok(ready_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 %s %s", argv[0], event_name, event_target_ready_name);
ret = CreateProcessA(NULL, path_name, NULL, NULL, FALSE, 0, NULL, NULL, &startup, info);
if (ret)
{
WaitForSingleObject(ready_event, INFINITE);
}
CloseHandle(ready_event);
return ret;
}
static void test_attach(void)
{
static const char *event_name = "dbgeng_test_event";
IDebugEventCallbacks event_callbacks = { &event_callbacks_vtbl };
PROCESS_INFORMATION info;
IDebugControl *control;
IDebugClient *client;
BOOL is_debugged;
HANDLE event;
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);
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");
ret = create_target_process(event_name, &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);
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);
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);
wait_child_process(info.hProcess);
CloseHandle(info.hProcess);
CloseHandle(info.hThread);
CloseHandle(event);
client->lpVtbl->Release(client);
control->lpVtbl->Release(control);
}
static void test_module_information(void)
{
static const char *event_name = "dbgeng_test_event";
unsigned int loaded, unloaded, index, length;
DEBUG_MODULE_PARAMETERS params[2];
IDebugDataSpaces *dataspaces;
PROCESS_INFORMATION info;
IDebugSymbols2 *symbols;
IDebugControl *control;
ULONG64 bases[2], base;
char buffer[MAX_PATH];
IDebugClient *client;
HANDLE event;
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->QueryInterface(client, &IID_IDebugSymbols2, (void **)&symbols);
ok(hr == S_OK, "Failed to get interface pointer, hr %#x.\n", hr);
hr = client->lpVtbl->QueryInterface(client, &IID_IDebugDataSpaces, (void **)&dataspaces);
ok(hr == S_OK, "Failed to get interface pointer, hr %#x.\n", hr);
hr = control->lpVtbl->IsPointer64Bit(control);
ok(hr == E_UNEXPECTED, "Unexpected hr %#x.\n", hr);
event = CreateEventA(NULL, FALSE, FALSE, event_name);
ok(event != NULL, "Failed to create event.\n");
ret = create_target_process(event_name, &info);
ok(ret, "Failed to create target process.\n");
hr = control->lpVtbl->SetEngineOptions(control, DEBUG_ENGOPT_INITIAL_BREAK);
ok(hr == S_OK, "Failed to set engine options, hr %#x.\n", hr);
hr = client->lpVtbl->AttachProcess(client, 0, info.dwProcessId, DEBUG_ATTACH_NONINVASIVE);
ok(hr == S_OK, "Failed to attach to process, hr %#x.\n", hr);
hr = control->lpVtbl->IsPointer64Bit(control);
ok(hr == E_UNEXPECTED, "Unexpected hr %#x.\n", hr);
hr = control->lpVtbl->WaitForEvent(control, 0, INFINITE);
ok(hr == S_OK, "Waiting for event failed, hr %#x.\n", hr);
hr = control->lpVtbl->IsPointer64Bit(control);
ok(SUCCEEDED(hr), "Failed to get pointer length, hr %#x.\n", hr);
/* Number of modules. */
hr = symbols->lpVtbl->GetNumberModules(symbols, &loaded, &unloaded);
ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
ok(loaded > 0, "Unexpected module count %u.\n", loaded);
/* Module base. */
hr = symbols->lpVtbl->GetModuleByIndex(symbols, loaded, &base);
ok(FAILED(hr), "Unexpected hr %#x.\n", hr);
base = 0;
hr = symbols->lpVtbl->GetModuleByIndex(symbols, 0, &base);
ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
ok(!!base, "Unexpected module base.\n");
hr = symbols->lpVtbl->GetModuleByOffset(symbols, 0, 0, &index, &base);
ok(FAILED(hr), "Unexpected hr %#x.\n", hr);
hr = symbols->lpVtbl->GetModuleByOffset(symbols, base, 0, &index, &base);
ok(hr == S_OK, "Failed to get module, hr %#x.\n", hr);
hr = symbols->lpVtbl->GetModuleByOffset(symbols, base, 0, NULL, NULL);
ok(hr == S_OK, "Failed to get module, hr %#x.\n", hr);
hr = symbols->lpVtbl->GetModuleByOffset(symbols, base + 1, 0, NULL, NULL);
ok(hr == S_OK, "Failed to get module, hr %#x.\n", hr);
hr = symbols->lpVtbl->GetModuleByOffset(symbols, base, loaded, NULL, NULL);
ok(FAILED(hr), "Unexpected hr %#x.\n", hr);
/* Parameters. */
base = 0;
hr = symbols->lpVtbl->GetModuleByIndex(symbols, 0, &base);
ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
ok(!!base, "Unexpected module base.\n");
hr = symbols->lpVtbl->GetModuleParameters(symbols, 1, NULL, 0, params);
ok(hr == S_OK, "Failed to get module parameters, hr %#x.\n", hr);
ok(params[0].Base == base, "Unexpected module base.\n");
hr = symbols->lpVtbl->GetModuleParameters(symbols, 1, &base, 100, params);
ok(hr == S_OK, "Failed to get module parameters, hr %#x.\n", hr);
ok(params[0].Base == base, "Unexpected module base.\n");
bases[0] = base + 1;
bases[1] = base;
hr = symbols->lpVtbl->GetModuleParameters(symbols, 2, bases, 0, params);
ok(hr == S_OK || broken(hr == E_NOINTERFACE) /* XP */, "Failed to get module parameters, hr %#x.\n", hr);
ok(params[0].Base == DEBUG_INVALID_OFFSET, "Unexpected module base.\n");
ok(params[0].Size == 0, "Unexpected module size.\n");
ok(params[1].Base == base, "Unexpected module base.\n");
ok(params[1].Size != 0, "Unexpected module size.\n");
hr = symbols->lpVtbl->GetModuleParameters(symbols, 1, bases, 0, params);
ok(hr == S_OK || broken(hr == E_NOINTERFACE) /* XP */, "Failed to get module parameters, hr %#x.\n", hr);
hr = symbols->lpVtbl->GetModuleParameters(symbols, 1, bases, loaded, params);
ok(hr == S_OK || broken(hr == E_NOINTERFACE) /* XP */, "Failed to get module parameters, hr %#x.\n", hr);
hr = symbols->lpVtbl->GetModuleParameters(symbols, 1, NULL, loaded, params);
ok(FAILED(hr), "Unexpected hr %#x.\n", hr);
/* Image name. */
hr = symbols->lpVtbl->GetModuleNameString(symbols, DEBUG_MODNAME_IMAGE, 0, 0, buffer, sizeof(buffer), &length);
ok(hr == S_OK, "Failed to get image name, hr %#x.\n", hr);
ok(strlen(buffer) + 1 == length, "Unexpected length.\n");
hr = symbols->lpVtbl->GetModuleNameString(symbols, DEBUG_MODNAME_IMAGE, 0, 0, NULL, sizeof(buffer), &length);
ok(hr == S_OK, "Failed to get image name, hr %#x.\n", hr);
ok(length > 0, "Unexpected length.\n");
hr = symbols->lpVtbl->GetModuleNameString(symbols, DEBUG_MODNAME_IMAGE, DEBUG_ANY_ID, base, buffer, sizeof(buffer),
&length);
ok(hr == S_OK, "Failed to get image name, hr %#x.\n", hr);
ok(strlen(buffer) + 1 == length, "Unexpected length.\n");
hr = symbols->lpVtbl->GetModuleNameString(symbols, DEBUG_MODNAME_IMAGE, 0, 0, buffer, length - 1, &length);
ok(hr == S_FALSE, "Failed to get image name, hr %#x.\n", hr);
ok(strlen(buffer) + 2 == length, "Unexpected length %u.\n", length);
hr = symbols->lpVtbl->GetModuleNameString(symbols, DEBUG_MODNAME_IMAGE, 0, 0, NULL, length - 1, NULL);
ok(hr == S_FALSE, "Failed to get image name, hr %#x.\n", hr);
/* Read memory. */
base = 0;
hr = symbols->lpVtbl->GetModuleByIndex(symbols, 0, &base);
ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
ok(!!base, "Unexpected module base.\n");
hr = dataspaces->lpVtbl->ReadVirtual(dataspaces, base, buffer, sizeof(buffer), &length);
ok(hr == S_OK, "Failed to read process memory, hr %#x.\n", hr);
ok(length == sizeof(buffer), "Unexpected length %u.\n", length);
ok(buffer[0] == 'M' && buffer[1] == 'Z', "Unexpected contents.\n");
memset(buffer, 0, sizeof(buffer));
hr = dataspaces->lpVtbl->ReadVirtual(dataspaces, base, buffer, sizeof(buffer), NULL);
ok(hr == S_OK, "Failed to read process memory, hr %#x.\n", hr);
ok(buffer[0] == 'M' && buffer[1] == 'Z', "Unexpected contents.\n");
hr = client->lpVtbl->DetachProcesses(client);
ok(hr == S_OK, "Failed to detach, hr %#x.\n", hr);
SetEvent(event);
wait_child_process(info.hProcess);
CloseHandle(info.hProcess);
CloseHandle(info.hThread);
CloseHandle(event);
client->lpVtbl->Release(client);
control->lpVtbl->Release(control);
symbols->lpVtbl->Release(symbols);
dataspaces->lpVtbl->Release(dataspaces);
}
static void target_proc(const char *event_name, const char *event_ready_name)
{
HANDLE terminate_event, ready_event;
terminate_event = OpenEventA(SYNCHRONIZE, FALSE, event_name);
ok(terminate_event != NULL, "Failed to open event handle.\n");
ready_event = OpenEventA(EVENT_MODIFY_STATE, FALSE, event_ready_name);
ok(ready_event != NULL, "Failed to open event handle.\n");
SetEvent(ready_event);
for (;;)
{
if (WaitForSingleObject(terminate_event, 100) == WAIT_OBJECT_0)
break;
}
CloseHandle(terminate_event);
CloseHandle(ready_event);
}
START_TEST(dbgeng)
{
char **argv;
int argc;
argc = winetest_get_mainargs(&argv);
if (argc > 4 && !strcmp(argv[2], "target"))
{
target_proc(argv[3], argv[4]);
return;
}
test_engine_options();
test_attach();
test_module_information();
}