[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