Signed-off-by: Paul Gofman pgofman@codeweavers.com --- v2: - implement on top of RtlCaptureStackBackTrace() and don't make implementation arch specific; - test skip count and requested frame count in more details; - fix some corner cases around returned frame count weirdness.
dlls/ntdll/exception.c | 27 ++++++++++ dlls/ntdll/ntdll.spec | 2 +- dlls/ntdll/tests/exception.c | 83 +++++++++++++++++++++++++++++ dlls/ntoskrnl.exe/ntoskrnl.exe.spec | 2 +- include/ddk/ntddk.h | 3 ++ include/winnt.h | 2 + 6 files changed, 117 insertions(+), 2 deletions(-)
diff --git a/dlls/ntdll/exception.c b/dlls/ntdll/exception.c index c3714e8369b..9d174ecf36d 100644 --- a/dlls/ntdll/exception.c +++ b/dlls/ntdll/exception.c @@ -30,6 +30,7 @@ #define WIN32_NO_STATUS #include "windef.h" #include "winternl.h" +#include "ddk/ntddk.h" #include "ddk/wdm.h" #include "wine/exception.h" #include "wine/list.h" @@ -1065,3 +1066,29 @@ NTSTATUS WINAPI RtlCopyExtendedContext( CONTEXT_EX *dst, ULONG context_flags, CO memcpy( &dst_xs->YmmContext, &src_xs->YmmContext, sizeof(dst_xs->YmmContext) ); return STATUS_SUCCESS; } + + +/********************************************************************** + * RtlWalkFrameChain (NTDLL.@) + */ +ULONG WINAPI RtlWalkFrameChain( void **callers, ULONG count, ULONG skip ) +{ + void *buffer[256]; + ULONG frame_count; + + TRACE( "callers %p, count %u, skip %#x.\n", callers, count, skip ); + + if (skip & ~(0xff << RTL_STACK_WALKING_MODE_FRAMES_TO_SKIP_SHIFT)) + { + WARN( "Invalid flags %#x.\n", skip ); + return 0; + } + skip >>= RTL_STACK_WALKING_MODE_FRAMES_TO_SKIP_SHIFT; + + frame_count = RtlCaptureStackBackTrace( 1, min( count, ARRAY_SIZE(buffer) ), buffer, NULL ); + if (frame_count == ARRAY_SIZE(buffer)) + FIXME( "Frame count exceeds buffer size.\n" ); + if (skip < frame_count) + memcpy( callers, buffer + skip, sizeof(*callers) * (frame_count - skip) ); + return frame_count; +} diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec index 13e65f65139..c54b2e205aa 100644 --- a/dlls/ntdll/ntdll.spec +++ b/dlls/ntdll/ntdll.spec @@ -1073,7 +1073,7 @@ @ stdcall RtlWakeAddressSingle(ptr) @ stdcall RtlWakeAllConditionVariable(ptr) @ stdcall RtlWakeConditionVariable(ptr) -@ stub RtlWalkFrameChain +@ stdcall RtlWalkFrameChain(ptr long long) @ stdcall RtlWalkHeap(long ptr) @ stdcall RtlWow64EnableFsRedirection(long) @ stdcall RtlWow64EnableFsRedirectionEx(long ptr) diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index 6af68317732..f7f05dc5799 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -28,6 +28,7 @@ #include "winnt.h" #include "winreg.h" #include "winternl.h" +#include "ddk/ntddk.h" #include "ddk/wdm.h" #include "excpt.h" #include "wine/test.h" @@ -9113,6 +9114,87 @@ static void test_copy_context(void) } #endif
+static void test_walk_stack(void) +{ + ULONG count, expected, frame_count, requested_count, skip_count; + void *addrs[256], *addrs2[256]; + void *start, *end; + unsigned int i, j; + + memset(addrs, 0xcc, sizeof(addrs)); + memset(addrs2, 0xcc, sizeof(addrs2)); + + frame_count = RtlCaptureStackBackTrace(0, ARRAY_SIZE(addrs), addrs, NULL); + count = RtlWalkFrameChain(addrs2, ARRAY_SIZE(addrs2), 0); + + trace("frame_count %u.\n", frame_count); + + ok(frame_count > 1, "Got zero frame_count.\n"); + ok(count == frame_count, "Got unexpected frame_count %u, count %u.\n", frame_count, count); + + start = test_walk_stack; + end = (BYTE *)start + 0x1000; + todo_wine_if(sizeof(void *) == 4) + ok(addrs[0] >= start && addrs[0] < end, "Address is not inside test function, start %p, end %p, addr %p.\n", + start, end, addrs[0]); + todo_wine_if(sizeof(void *) == 4) + ok(addrs2[0] >= start && addrs2[0] < end, "Address is not inside test function, start %p, end %p, addr %p.\n", + start, end, addrs2[0]); + + for (i = 1; i < frame_count; ++i) + { + ok(addrs[i] == addrs2[i], "i %u, addresses do not match, %p vs %p.\n", i, addrs[i], addrs2[i]); + } + todo_wine ok(!!addrs[frame_count - 1], "Expected non-NULL last address.\n"); + + for (requested_count = frame_count - 1; requested_count != ARRAY_SIZE(addrs); requested_count = ARRAY_SIZE(addrs)) + { + for (i = 0; i < 32; ++i) + { + winetest_push_context("requested_count %u, i %u", requested_count, i); + skip_count = (1 << i) >> 8; + + if (i < RTL_STACK_WALKING_MODE_FRAMES_TO_SKIP_SHIFT + || i >= RTL_STACK_WALKING_MODE_FRAMES_TO_SKIP_SHIFT + 8) + expected = 0; + else + expected = min(frame_count, requested_count); + + memset(addrs2, 0xcc, sizeof(addrs2)); + count = RtlWalkFrameChain(addrs2, requested_count, 1 << i); + ok(count == expected, "Got unexpected frame_count %u, expected %u.\n", count, expected); + + if (skip_count < count) + count -= skip_count; + else + count = 0; + + for (j = 0; j < count; ++j) + ok( addrs2[j] != (void *)(ULONG_PTR)0xcccccccccccccccc, "Address is not set, j %u.\n", j ); + for (; j < ARRAY_SIZE(addrs2); ++j) + ok( addrs2[j] == (void *)(ULONG_PTR)0xcccccccccccccccc, "Address is set, j %u.\n", j ); + + if (!count) + { + winetest_pop_context(); + continue; + } + + memset(addrs, 0xcc, sizeof(addrs)); + expected = skip_count > frame_count ? 0 : min(frame_count - skip_count, requested_count); + count = RtlCaptureStackBackTrace(skip_count, requested_count, addrs, NULL); + ok(count == expected, "Got unexpected frame_count %u, expected %u. i %u.\n", count, expected, i); + + count = min(frame_count, requested_count) - skip_count; + for (j = 0; j < count; ++j) + { + ok(addrs[j] == addrs2[j], "Addresses do not match, j %u, %p, %p.\n", j, addrs[j], addrs2[j]); + } + winetest_pop_context(); + } + } +} + START_TEST(exception) { HMODULE hntdll = GetModuleHandleA("ntdll.dll"); @@ -9339,5 +9421,6 @@ START_TEST(exception) test_suspend_thread(); test_suspend_process(); test_unload_trace(); + test_walk_stack(); VirtualFree(code_mem, 0, MEM_RELEASE); } diff --git a/dlls/ntoskrnl.exe/ntoskrnl.exe.spec b/dlls/ntoskrnl.exe/ntoskrnl.exe.spec index 0208e2f633f..b94e58ccb26 100644 --- a/dlls/ntoskrnl.exe/ntoskrnl.exe.spec +++ b/dlls/ntoskrnl.exe/ntoskrnl.exe.spec @@ -1300,7 +1300,7 @@ @ stdcall RtlVerifyVersionInfo(ptr long int64) @ stdcall -arch=arm64,x86_64 RtlVirtualUnwind(long long long ptr ptr ptr ptr ptr) @ stub RtlVolumeDeviceToDosName -@ stub RtlWalkFrameChain +@ stdcall RtlWalkFrameChain(ptr long long) @ stdcall RtlWriteRegistryValue(long ptr ptr long ptr long) @ stub RtlZeroHeap @ stdcall RtlZeroMemory(ptr long) diff --git a/include/ddk/ntddk.h b/include/ddk/ntddk.h index 41ad3d721bd..ce558139aa6 100644 --- a/include/ddk/ntddk.h +++ b/include/ddk/ntddk.h @@ -245,6 +245,8 @@ typedef EXPAND_STACK_CALLOUT *PEXPAND_STACK_CALLOUT; typedef GUID UUID; #endif
+#define RTL_STACK_WALKING_MODE_FRAMES_TO_SKIP_SHIFT 8 + NTSTATUS WINAPI ExUuidCreate(UUID*); NTSTATUS WINAPI IoQueryDeviceDescription(PINTERFACE_TYPE,PULONG,PCONFIGURATION_TYPE,PULONG, PCONFIGURATION_TYPE,PULONG,PIO_QUERY_DEVICE_ROUTINE,PVOID); @@ -267,5 +269,6 @@ NTSTATUS WINAPI PsSetCreateThreadNotifyRoutine(PCREATE_THREAD_NOTIFY_ROUTINE); NTSTATUS WINAPI PsSetLoadImageNotifyRoutine(PLOAD_IMAGE_NOTIFY_ROUTINE); void WINAPI RtlInitializeGenericTableAvl(PRTL_AVL_TABLE,PRTL_AVL_COMPARE_ROUTINE,PRTL_AVL_ALLOCATE_ROUTINE, PRTL_AVL_FREE_ROUTINE,void *); void WINAPI RtlInsertElementGenericTableAvl(PRTL_AVL_TABLE,void *,ULONG,BOOL*); +ULONG WINAPI RtlWalkFrameChain(void **,ULONG,ULONG);
#endif diff --git a/include/winnt.h b/include/winnt.h index ef731e29c52..eb1bd165720 100644 --- a/include/winnt.h +++ b/include/winnt.h @@ -1840,6 +1840,8 @@ NTSYSAPI PVOID WINAPI RtlVirtualUnwind(DWORD,ULONG_PTR,ULONG_PTR,RUNTIME_FUNCT
#endif
+NTSYSAPI USHORT WINAPI RtlCaptureStackBackTrace(ULONG,ULONG,void **,ULONG *); + /* * Product types */
Signed-off-by: Paul Gofman pgofman@codeweavers.com --- dlls/ntdll/signal_x86_64.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dlls/ntdll/signal_x86_64.c b/dlls/ntdll/signal_x86_64.c index f401bc8b477..57c7f81f9ea 100644 --- a/dlls/ntdll/signal_x86_64.c +++ b/dlls/ntdll/signal_x86_64.c @@ -1538,7 +1538,7 @@ USHORT WINAPI RtlCaptureStackBackTrace( ULONG skip, ULONG count, PVOID *buffer, for (i = 0; i < skip + count; i++) { status = virtual_unwind( UNW_FLAG_NHANDLER, &dispatch, &context ); - if (status != STATUS_SUCCESS) return i; + if (status != STATUS_SUCCESS) return num_entries;
if (!dispatch.EstablisherFrame) break;
Signed-off-by: Paul Gofman pgofman@codeweavers.com --- v2: - get rid of parameter intermediate load to %eax.
dlls/ntdll/signal_i386.c | 20 +++++++++++++++++++- dlls/ntdll/tests/exception.c | 2 -- 2 files changed, 19 insertions(+), 3 deletions(-)
diff --git a/dlls/ntdll/signal_i386.c b/dlls/ntdll/signal_i386.c index 14971032ce6..69ce62c4168 100644 --- a/dlls/ntdll/signal_i386.c +++ b/dlls/ntdll/signal_i386.c @@ -480,6 +480,24 @@ __ASM_STDCALL_FUNC( RtlRaiseException, 4, "ret $4" ) /* actually never returns */
+/*********************************************************************** + * capture_context_wrapper (NTDLL.@) + */ +void WINAPI capture_context_wrapper(CONTEXT *); +__ASM_STDCALL_FUNC( capture_context_wrapper, 4, + "pushl %ebp\n\t" + __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t") + __ASM_CFI(".cfi_rel_offset %ebp,0\n\t") + "movl %esp,%ebp\n\t" + __ASM_CFI(".cfi_def_cfa_register %ebp\n\t") + "pushl 8(%ebp)\n\t" /* context */ + "call " __ASM_STDCALL("RtlCaptureContext",4) "\n\t" + "leave\n\t" + __ASM_CFI(".cfi_def_cfa %esp,4\n\t") + __ASM_CFI(".cfi_same_value %ebp\n\t") + "ret $4" ) + + /************************************************************************* * RtlCaptureStackBackTrace (NTDLL.@) */ @@ -489,7 +507,7 @@ USHORT WINAPI RtlCaptureStackBackTrace( ULONG skip, ULONG count, PVOID *buffer, ULONG i; ULONG *frame;
- RtlCaptureContext( &context ); + capture_context_wrapper( &context ); if (hash) *hash = 0; frame = (ULONG *)context.Ebp;
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index f7f05dc5799..336702766ce 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -9134,10 +9134,8 @@ static void test_walk_stack(void)
start = test_walk_stack; end = (BYTE *)start + 0x1000; - todo_wine_if(sizeof(void *) == 4) ok(addrs[0] >= start && addrs[0] < end, "Address is not inside test function, start %p, end %p, addr %p.\n", start, end, addrs[0]); - todo_wine_if(sizeof(void *) == 4) ok(addrs2[0] >= start && addrs2[0] < end, "Address is not inside test function, start %p, end %p, addr %p.\n", start, end, addrs2[0]);
Signed-off-by: Paul Gofman pgofman@codeweavers.com --- dlls/ntdll/signal_i386.c | 2 +- dlls/ntdll/signal_x86_64.c | 2 +- dlls/ntdll/tests/exception.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/dlls/ntdll/signal_i386.c b/dlls/ntdll/signal_i386.c index 69ce62c4168..15e53b6059e 100644 --- a/dlls/ntdll/signal_i386.c +++ b/dlls/ntdll/signal_i386.c @@ -519,7 +519,7 @@ USHORT WINAPI RtlCaptureStackBackTrace( ULONG skip, ULONG count, PVOID *buffer,
for (i = 0; i < count; i++) { - if (!is_valid_frame( frame )) break; + if (!is_valid_frame( frame ) || !frame[1]) break; buffer[i] = (void *)frame[1]; if (hash) *hash += frame[1]; frame = (ULONG *)*frame; diff --git a/dlls/ntdll/signal_x86_64.c b/dlls/ntdll/signal_x86_64.c index 57c7f81f9ea..d277a72436f 100644 --- a/dlls/ntdll/signal_x86_64.c +++ b/dlls/ntdll/signal_x86_64.c @@ -1540,7 +1540,7 @@ USHORT WINAPI RtlCaptureStackBackTrace( ULONG skip, ULONG count, PVOID *buffer, status = virtual_unwind( UNW_FLAG_NHANDLER, &dispatch, &context ); if (status != STATUS_SUCCESS) return num_entries;
- if (!dispatch.EstablisherFrame) break; + if (!dispatch.EstablisherFrame || !context.Rip) break;
if ((dispatch.EstablisherFrame & 7) || dispatch.EstablisherFrame < (ULONG64)NtCurrentTeb()->Tib.StackLimit || diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index 336702766ce..60dac376b39 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -9143,7 +9143,7 @@ static void test_walk_stack(void) { ok(addrs[i] == addrs2[i], "i %u, addresses do not match, %p vs %p.\n", i, addrs[i], addrs2[i]); } - todo_wine ok(!!addrs[frame_count - 1], "Expected non-NULL last address.\n"); + ok(!!addrs[frame_count - 1], "Expected non-NULL last address.\n");
for (requested_count = frame_count - 1; requested_count != ARRAY_SIZE(addrs); requested_count = ARRAY_SIZE(addrs)) {