Module: wine Branch: master Commit: 982472d8953fab6356c43a322f794d3f29ae3971 URL: https://gitlab.winehq.org/wine/wine/-/commit/982472d8953fab6356c43a322f794d3...
Author: Eric Pouech eric.pouech@gmail.com Date: Wed Jan 18 16:24:00 2023 +0100
kernel32/tests: Merge two debug event loops in test_debugger().
Current test in debugger expects that all breakpoint debug events appear before all the exit thread events. On Windows, it happens that sometimes the breakpoint and exit thread events are intertwined (making the test fail).
So, this patch: - merges the exception events loop and the exit thread events loop into a single loop. - detects the unordered sequence (mark it broken as windows only) - extends the test so that we check that all exit thread events are received. - introduce next_event_filter helper to not return some unwanted events.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=47874 Signed-off-by: Eric Pouech eric.pouech@gmail.com
---
dlls/kernel32/tests/debugger.c | 58 +++++++++++++++++++++++++++++++----------- 1 file changed, 43 insertions(+), 15 deletions(-)
diff --git a/dlls/kernel32/tests/debugger.c b/dlls/kernel32/tests/debugger.c index a190fc6f3f3..cc6265fcd97 100644 --- a/dlls/kernel32/tests/debugger.c +++ b/dlls/kernel32/tests/debugger.c @@ -391,12 +391,25 @@ static void next_event_(unsigned line, struct debugger_context *ctx, unsigned ti ctx->current_thread = get_debuggee_thread(ctx, ctx->ev.dwThreadId); }
+static DWORD event_mask(DWORD ev) +{ + return (ev >= 1 && ev <= 7) ? (1LU << ev) : 0; +} + +#define next_event_filter(a, b,c) next_event_filter_(__LINE__, (a), (b), (c)) +static void next_event_filter_(unsigned line, struct debugger_context *ctx, DWORD timeout, DWORD mask) +{ + do + { + next_event_(line, ctx, timeout); + } while (event_mask(ctx->ev.dwDebugEventCode) & mask); +} + #define wait_for_breakpoint(a) wait_for_breakpoint_(__LINE__,a) static void wait_for_breakpoint_(unsigned line, struct debugger_context *ctx) { - do next_event_(line, ctx, WAIT_EVENT_TIMEOUT); - while (ctx->ev.dwDebugEventCode == LOAD_DLL_DEBUG_EVENT || ctx->ev.dwDebugEventCode == UNLOAD_DLL_DEBUG_EVENT - || ctx->ev.dwDebugEventCode == CREATE_THREAD_DEBUG_EVENT); + next_event_filter_(line, ctx, WAIT_EVENT_TIMEOUT, + event_mask(LOAD_DLL_DEBUG_EVENT) | event_mask(UNLOAD_DLL_DEBUG_EVENT) | event_mask(CREATE_THREAD_DEBUG_EVENT));
ok_(__FILE__,line)(ctx->ev.dwDebugEventCode == EXCEPTION_DEBUG_EVENT, "dwDebugEventCode = %ld\n", ctx->ev.dwDebugEventCode); ok_(__FILE__,line)(ctx->ev.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT, "ExceptionCode = %lx\n", @@ -1710,6 +1723,8 @@ static void test_debugger(const char *argv0)
if (sizeof(loop_code) > 1) { + unsigned event_order = 0; + memset(buf, OP_BP, sizeof(buf)); memcpy(proc_code, &loop_code, sizeof(loop_code)); ret = WriteProcessMemory(pi.hProcess, mem, buf, sizeof(buf), NULL); @@ -1720,11 +1735,14 @@ static void test_debugger(const char *argv0) worker_cnt = 20; for (i = 0; i < worker_cnt; i++) { - thread = CreateRemoteThread(pi.hProcess, NULL, 0, (void*)thread_proc, NULL, 0, NULL); + DWORD tid; + thread = CreateRemoteThread(pi.hProcess, NULL, 0, (void*)thread_proc, NULL, 0, &tid); ok(thread != NULL, "CreateRemoteThread failed: %lu\n", GetLastError());
next_event(&ctx, WAIT_EVENT_TIMEOUT); ok(ctx.ev.dwDebugEventCode == CREATE_THREAD_DEBUG_EVENT, "dwDebugEventCode = %ld\n", ctx.ev.dwDebugEventCode); + ok(ctx.ev.dwThreadId == tid, "Unexpected thread id\n"); + ok(ctx.ev.u.CreateThread.lpStartAddress == (void*)thread_proc, "Unexpected thread's start address\n");
ret = CloseHandle(thread); ok(ret, "CloseHandle failed, last error %ld.\n", GetLastError()); @@ -1741,6 +1759,11 @@ static void test_debugger(const char *argv0) ret = WriteProcessMemory(pi.hProcess, thread_proc + 1, &byte, 1, NULL); ok(ret, "WriteProcessMemory failed: %lu\n", GetLastError());
+ /* One would expect that we get all exception debug events (for the worker threads + * that hit the BP instruction), then the exit thread events for all created threads. + * It happens that on Windows, the exception & exit thread events can be intertwined. + * So detect this situation. + */ for (;;) { DEBUG_EVENT ev; @@ -1753,26 +1776,31 @@ static void test_debugger(const char *argv0) ret = WaitForDebugEvent(&ev, 10); ok(GetLastError() == ERROR_SEM_TIMEOUT, "WaitForDebugEvent returned %x(%lu)\n", ret, GetLastError());
- next_event(&ctx, POLL_EVENT_TIMEOUT); - if (ctx.ev.dwDebugEventCode != EXCEPTION_DEBUG_EVENT) break; + for (;;) + { + next_event_filter(&ctx, POLL_EVENT_TIMEOUT, event_mask(CREATE_THREAD_DEBUG_EVENT)); + if (ctx.ev.dwDebugEventCode == EXCEPTION_DEBUG_EVENT) break; + if (ctx.ev.dwDebugEventCode == EXIT_THREAD_DEBUG_EVENT) + { + if (event_order == 0) event_order = 1; /* first exit thread event */ + if (!--worker_cnt) break; + } + } + if (!worker_cnt) break; + + ok(ctx.ev.dwDebugEventCode == EXCEPTION_DEBUG_EVENT, "dwDebugEventCode = %ld\n", ctx.ev.dwDebugEventCode); trace("exception at %p in thread %04lx\n", ctx.ev.u.Exception.ExceptionRecord.ExceptionAddress, ctx.ev.dwThreadId); ok(ctx.ev.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT, "ExceptionCode = %lx\n", ctx.ev.u.Exception.ExceptionRecord.ExceptionCode); ok(ctx.ev.u.Exception.ExceptionRecord.ExceptionAddress == thread_proc + 1, "ExceptionAddress = %p\n", ctx.ev.u.Exception.ExceptionRecord.ExceptionAddress); exception_cnt++; + if (event_order == 1) event_order = 2; /* exception debug event after exit thread event */ }
trace("received %u exceptions\n", exception_cnt); - - for (;;) - { - ok(ctx.ev.dwDebugEventCode == EXIT_THREAD_DEBUG_EVENT - || broken(ctx.ev.dwDebugEventCode == CREATE_THREAD_DEBUG_EVENT), /* sometimes happens on vista */ - "dwDebugEventCode = %ld\n", ctx.ev.dwDebugEventCode); - if (ctx.ev.dwDebugEventCode == EXIT_THREAD_DEBUG_EVENT && !--worker_cnt) break; - next_event(&ctx, WAIT_EVENT_TIMEOUT); - } + ok(!worker_cnt, "Missing %u exit thread events\n", worker_cnt); + ok(event_order == 1 || broken(event_order == 2), "Intertwined exit thread & exception debug events\n"); }
if (OP_BP)