Signed-off-by: Paul Gofman <pgofman(a)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.
v3:
- fix loop in test.
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..d73e75ee529 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 < frame_count + 1; ++requested_count)
+ {
+ 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
*/
--
2.31.1