[PATCH 2/2] ntdll: Add support for querying thread suspend count.
Nikolay Sivov
nsivov at codeweavers.com
Tue Dec 3 03:05:22 CST 2019
Signed-off-by: Nikolay Sivov <nsivov at codeweavers.com>
---
x64dbg debugger uses this info class, checking suspend status periodically.
When unavailable, it repeatedly suspends/resumes threads as a fallback to
get NtSuspendThread() returned counter, which is not ideal.
dlls/ntdll/tests/exception.c | 63 +++++++++++++++++++++++++++++++++-
dlls/ntdll/thread.c | 21 ++++++++++++
include/wine/server_protocol.h | 4 ++-
include/winternl.h | 1 +
server/protocol.def | 1 +
server/request.h | 5 +--
server/thread.c | 1 +
server/trace.c | 1 +
8 files changed, 93 insertions(+), 4 deletions(-)
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c
index fb58c0ee7a..44df47466b 100644
--- a/dlls/ntdll/tests/exception.c
+++ b/dlls/ntdll/tests/exception.c
@@ -50,6 +50,7 @@ static ULONG (WINAPI *pRtlRemoveVectoredContinueHandler)(PVOID handler);
static NTSTATUS (WINAPI *pNtReadVirtualMemory)(HANDLE, const void*, void*, SIZE_T, SIZE_T*);
static NTSTATUS (WINAPI *pNtTerminateProcess)(HANDLE handle, LONG exit_code);
static NTSTATUS (WINAPI *pNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);
+static NTSTATUS (WINAPI *pNtQueryInformationThread)(HANDLE, THREADINFOCLASS, PVOID, ULONG, PULONG);
static NTSTATUS (WINAPI *pNtSetInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG);
static BOOL (WINAPI *pIsWow64Process)(HANDLE, PBOOL);
static NTSTATUS (WINAPI *pNtClose)(HANDLE);
@@ -3163,11 +3164,34 @@ static DWORD WINAPI suspend_thread_test( void *arg )
return 0;
}
+static void test_suspend_count(HANDLE hthread, ULONG expected_count, int line)
+{
+ static BOOL supported = TRUE;
+ NTSTATUS status;
+ ULONG count;
+
+ if (!supported)
+ return;
+
+ count = ~0u;
+ status = pNtQueryInformationThread(hthread, ThreadSuspendCount, &count, sizeof(count), NULL);
+ if (status)
+ {
+ win_skip("ThreadSuspendCount is not supported.\n");
+ supported = FALSE;
+ return;
+ }
+
+ ok_(__FILE__, line)(!status, "Failed to get suspend count, status %#x.\n", status);
+ ok_(__FILE__, line)(count == expected_count, "Unexpected suspend count %u.\n", count);
+}
+
static void test_suspend_thread(void)
{
+#define TEST_SUSPEND_COUNT(thread, count) test_suspend_count((thread), (count), __LINE__)
HANDLE thread, event;
+ ULONG count, len;
NTSTATUS status;
- ULONG count;
DWORD ret;
status = NtSuspendThread(0, NULL);
@@ -3184,6 +3208,31 @@ static void test_suspend_thread(void)
ret = WaitForSingleObject(thread, 0);
ok(ret == WAIT_TIMEOUT, "Unexpected status %d.\n", ret);
+ status = pNtQueryInformationThread(thread, ThreadSuspendCount, &count, sizeof(count), NULL);
+ if (!status)
+ {
+ status = pNtQueryInformationThread(thread, ThreadSuspendCount, NULL, sizeof(count), NULL);
+ ok(status == STATUS_ACCESS_VIOLATION, "Unexpected status %#x.\n", status);
+
+ status = pNtQueryInformationThread(thread, ThreadSuspendCount, &count, sizeof(count) / 2, NULL);
+ ok(status == STATUS_INFO_LENGTH_MISMATCH, "Unexpected status %#x.\n", status);
+
+ len = 123;
+ status = pNtQueryInformationThread(thread, ThreadSuspendCount, &count, sizeof(count) / 2, &len);
+ ok(status == STATUS_INFO_LENGTH_MISMATCH, "Unexpected status %#x.\n", status);
+ ok(len == 123, "Unexpected info length %u.\n", len);
+
+ len = 123;
+ status = pNtQueryInformationThread(thread, ThreadSuspendCount, NULL, 0, &len);
+ ok(status == STATUS_INFO_LENGTH_MISMATCH, "Unexpected status %#x.\n", status);
+ ok(len == 123, "Unexpected info length %u.\n", len);
+
+ count = 10;
+ status = pNtQueryInformationThread(0, ThreadSuspendCount, &count, sizeof(count), NULL);
+ ok(status, "Unexpected status %#x.\n", status);
+ ok(count == 10, "Unexpected suspend count %u.\n", count);
+ }
+
status = NtResumeThread(thread, NULL);
ok(!status, "Unexpected status %#x.\n", status);
@@ -3191,24 +3240,35 @@ static void test_suspend_thread(void)
ok(!status, "Unexpected status %#x.\n", status);
ok(count == 0, "Unexpected suspended count %u.\n", count);
+ TEST_SUSPEND_COUNT(thread, 0);
+
status = NtSuspendThread(thread, NULL);
ok(!status, "Failed to suspend a thread, status %#x.\n", status);
+ TEST_SUSPEND_COUNT(thread, 1);
+
status = NtSuspendThread(thread, &count);
ok(!status, "Failed to suspend a thread, status %#x.\n", status);
ok(count == 1, "Unexpected suspended count %u.\n", count);
+ TEST_SUSPEND_COUNT(thread, 2);
+
status = NtResumeThread(thread, &count);
ok(!status, "Failed to resume a thread, status %#x.\n", status);
ok(count == 2, "Unexpected suspended count %u.\n", count);
+ TEST_SUSPEND_COUNT(thread, 1);
+
status = NtResumeThread(thread, NULL);
ok(!status, "Failed to resume a thread, status %#x.\n", status);
+ TEST_SUSPEND_COUNT(thread, 0);
+
SetEvent(event);
WaitForSingleObject(thread, INFINITE);
CloseHandle(thread);
+#undef TEST_SUSPEND_COUNT
}
static const char *suspend_process_event_name = "suspend_process_event";
@@ -3418,6 +3478,7 @@ START_TEST(exception)
X(RtlAddVectoredContinueHandler);
X(RtlRemoveVectoredContinueHandler);
X(NtQueryInformationProcess);
+ X(NtQueryInformationThread);
X(NtSetInformationProcess);
X(NtSuspendProcess);
X(NtResumeProcess);
diff --git a/dlls/ntdll/thread.c b/dlls/ntdll/thread.c
index fd8d388403..f993dd314c 100644
--- a/dlls/ntdll/thread.c
+++ b/dlls/ntdll/thread.c
@@ -1115,6 +1115,27 @@ NTSTATUS WINAPI NtQueryInformationThread( HANDLE handle, THREADINFOCLASS class,
*(BOOL*)data = FALSE;
if (ret_len) *ret_len = sizeof(BOOL);
return STATUS_SUCCESS;
+ case ThreadSuspendCount:
+ {
+ ULONG count = 0;
+
+ if (length != sizeof(ULONG)) return STATUS_INFO_LENGTH_MISMATCH;
+ if (!data) return STATUS_ACCESS_VIOLATION;
+
+ SERVER_START_REQ( get_thread_info )
+ {
+ req->handle = wine_server_obj_handle( handle );
+ req->tid_in = 0;
+ if (!(status = wine_server_call( req )))
+ count = reply->suspend_count;
+ }
+ SERVER_END_REQ;
+
+ if (!status)
+ *(ULONG *)data = count;
+
+ return status;
+ }
case ThreadDescription:
{
THREAD_DESCRIPTION_INFORMATION *info = data;
diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h
index 108701b2bc..aaa5fd2e33 100644
--- a/include/wine/server_protocol.h
+++ b/include/wine/server_protocol.h
@@ -1006,8 +1006,10 @@ struct get_thread_info_reply
int exit_code;
int priority;
int last;
+ int suspend_count;
data_size_t desc_len;
/* VARARG(desc,unicode_str); */
+ char __pad_60[4];
};
@@ -6700,6 +6702,6 @@ union generic_reply
struct resume_process_reply resume_process_reply;
};
-#define SERVER_PROTOCOL_VERSION 592
+#define SERVER_PROTOCOL_VERSION 593
#endif /* __WINE_WINE_SERVER_PROTOCOL_H */
diff --git a/include/winternl.h b/include/winternl.h
index 311fba3ae9..83449b80c7 100644
--- a/include/winternl.h
+++ b/include/winternl.h
@@ -1008,6 +1008,7 @@ typedef enum _THREADINFOCLASS {
ThreadUmsInformation,
ThreadCounterProfiling,
ThreadIdealProcessorEx,
+ ThreadSuspendCount = 35,
ThreadDescription = 38,
MaxThreadInfoClass
} THREADINFOCLASS;
diff --git a/server/protocol.def b/server/protocol.def
index 566bc83bb2..1cb1fea602 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -944,6 +944,7 @@ struct rawinput_device
int exit_code; /* thread exit code */
int priority; /* thread priority level */
int last; /* last thread in process */
+ int suspend_count; /* thread suspend count */
data_size_t desc_len; /* description length in bytes */
VARARG(desc,unicode_str); /* description string */
@END
diff --git a/server/request.h b/server/request.h
index de7720ae68..2930b31d72 100644
--- a/server/request.h
+++ b/server/request.h
@@ -849,8 +849,9 @@ C_ASSERT( FIELD_OFFSET(struct get_thread_info_reply, affinity) == 32 );
C_ASSERT( FIELD_OFFSET(struct get_thread_info_reply, exit_code) == 40 );
C_ASSERT( FIELD_OFFSET(struct get_thread_info_reply, priority) == 44 );
C_ASSERT( FIELD_OFFSET(struct get_thread_info_reply, last) == 48 );
-C_ASSERT( FIELD_OFFSET(struct get_thread_info_reply, desc_len) == 52 );
-C_ASSERT( sizeof(struct get_thread_info_reply) == 56 );
+C_ASSERT( FIELD_OFFSET(struct get_thread_info_reply, suspend_count) == 52 );
+C_ASSERT( FIELD_OFFSET(struct get_thread_info_reply, desc_len) == 56 );
+C_ASSERT( sizeof(struct get_thread_info_reply) == 64 );
C_ASSERT( FIELD_OFFSET(struct get_thread_times_request, handle) == 12 );
C_ASSERT( sizeof(struct get_thread_times_request) == 16 );
C_ASSERT( FIELD_OFFSET(struct get_thread_times_reply, creation_time) == 8 );
diff --git a/server/thread.c b/server/thread.c
index 4fc6abf0ef..80db41b48d 100644
--- a/server/thread.c
+++ b/server/thread.c
@@ -1463,6 +1463,7 @@ DECL_HANDLER(get_thread_info)
reply->priority = thread->priority;
reply->affinity = thread->affinity;
reply->last = thread->process->running_threads == 1;
+ reply->suspend_count = thread->suspend;
reply->desc_len = thread->desc_len;
if (thread->desc && get_reply_max_size())
diff --git a/server/trace.c b/server/trace.c
index d44f67a021..411369a4f6 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -1423,6 +1423,7 @@ static void dump_get_thread_info_reply( const struct get_thread_info_reply *req
fprintf( stderr, ", exit_code=%d", req->exit_code );
fprintf( stderr, ", priority=%d", req->priority );
fprintf( stderr, ", last=%d", req->last );
+ fprintf( stderr, ", suspend_count=%d", req->suspend_count );
fprintf( stderr, ", desc_len=%u", req->desc_len );
dump_varargs_unicode_str( ", desc=", cur_size );
}
--
2.24.0
More information about the wine-devel
mailing list