[PATCH v4 2/4] ntdll: Implement RtlWalkFrameChain() for x86 and x86_64.
Paul Gofman
pgofman at codeweavers.com
Mon Nov 1 06:13:58 CDT 2021
Signed-off-by: Paul Gofman <pgofman at codeweavers.com>
---
v4:
- use helper common with RtlCaptureStackBackTrace() and avoid using temporary buffer.
dlls/ntdll/exception.c | 24 ++++++++-
dlls/ntdll/ntdll.spec | 2 +-
dlls/ntdll/signal_i386.c | 17 +++---
dlls/ntdll/signal_x86_64.c | 7 +--
dlls/ntdll/tests/exception.c | 83 +++++++++++++++++++++++++++++
dlls/ntoskrnl.exe/ntoskrnl.exe.spec | 2 +-
include/ddk/ntddk.h | 3 ++
include/winnt.h | 2 +
8 files changed, 124 insertions(+), 16 deletions(-)
diff --git a/dlls/ntdll/exception.c b/dlls/ntdll/exception.c
index 02bf672f8fb..ea0d0df6694 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"
@@ -1074,6 +1075,25 @@ USHORT WINAPI RtlCaptureStackBackTrace( ULONG skip, ULONG count, PVOID *buffer,
{
ULONG ret;
- ret = capture_stack_back_trace( skip, count, buffer, hash );
- return min( ret, ~(USHORT)0 );
+ ret = capture_stack_back_trace( skip, skip + count, buffer, hash );
+ if (ret < skip) return 0;
+ return min( ret - skip, ~(USHORT)0 );
+}
+
+
+/**********************************************************************
+ * RtlWalkFrameChain (NTDLL.@)
+ */
+ULONG WINAPI RtlWalkFrameChain( void **callers, ULONG count, ULONG skip )
+{
+ 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;
+
+ return capture_stack_back_trace( skip, count, callers, NULL );
}
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/signal_i386.c b/dlls/ntdll/signal_i386.c
index 26150ce877b..ff3e837c784 100644
--- a/dlls/ntdll/signal_i386.c
+++ b/dlls/ntdll/signal_i386.c
@@ -488,27 +488,26 @@ ULONG WINAPI capture_stack_back_trace( ULONG skip, ULONG count, PVOID *buffer, U
CONTEXT context;
ULONG i;
ULONG *frame;
+ ULONG num_entries = 0;
++skip;
+ ++count;
RtlCaptureContext( &context );
if (hash) *hash = 0;
frame = (ULONG *)context.Ebp;
- while (skip--)
- {
- if (!is_valid_frame( frame )) return 0;
- frame = (ULONG *)*frame;
- }
-
for (i = 0; i < count; i++)
{
if (!is_valid_frame( frame )) break;
- buffer[i] = (void *)frame[1];
- if (hash) *hash += frame[1];
+ if (i >= skip)
+ {
+ buffer[num_entries++] = (void *)frame[1];
+ if (hash) *hash += frame[1];
+ }
frame = (ULONG *)*frame;
}
- return i;
+ return i ? i - 1 : 0;
}
diff --git a/dlls/ntdll/signal_x86_64.c b/dlls/ntdll/signal_x86_64.c
index 5f3c8f70208..4a9c7d4b9ff 100644
--- a/dlls/ntdll/signal_x86_64.c
+++ b/dlls/ntdll/signal_x86_64.c
@@ -1504,16 +1504,17 @@ ULONG WINAPI capture_stack_back_trace( ULONG skip, ULONG count, PVOID *buffer, U
TRACE( "(%u, %u, %p, %p)\n", skip, count, buffer, hash );
++skip;
+ ++count;
RtlCaptureContext( &context );
dispatch.TargetIp = 0;
dispatch.ContextRecord = &context;
dispatch.HistoryTable = &table;
if (hash) *hash = 0;
- for (i = 0; i < skip + count; i++)
+ for (i = 0; i < count; i++)
{
status = virtual_unwind( UNW_FLAG_NHANDLER, &dispatch, &context );
- if (status != STATUS_SUCCESS) return i;
+ if (status != STATUS_SUCCESS) break;
if (!dispatch.EstablisherFrame) break;
@@ -1532,7 +1533,7 @@ ULONG WINAPI capture_stack_back_trace( ULONG skip, ULONG count, PVOID *buffer, U
}
if (hash && num_entries > 0) *hash = hash_pointers( buffer, num_entries );
TRACE( "captured %u frames\n", num_entries );
- return num_entries;
+ return i ? i - 1 : 0;
}
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
More information about the wine-devel
mailing list