Signed-off-by: Nikolay Sivov nsivov@codeweavers.com ---
I didn't realize UNICODE_STRING lengths are 2-byte.
dlls/kernel32/tests/thread.c | 28 ++++++++++++++-------------- dlls/kernelbase/thread.c | 14 ++++++-------- dlls/ntdll/thread.c | 12 +++++------- include/winternl.h | 3 +-- 4 files changed, 26 insertions(+), 31 deletions(-)
diff --git a/dlls/kernel32/tests/thread.c b/dlls/kernel32/tests/thread.c index 8ff34ba5f3..9a8ad9151c 100644 --- a/dlls/kernel32/tests/thread.c +++ b/dlls/kernel32/tests/thread.c @@ -2145,14 +2145,15 @@ static void test_thread_description(void) ok(len == sizeof(*thread_desc), "Unexpected structure length %u.\n", len);
len2 = 0; - thread_desc->Length = 1; - thread_desc->Description = (WCHAR *)thread_desc; + thread_desc->Description.Length = 1; + thread_desc->Description.MaximumLength = 0; + thread_desc->Description.Buffer = (WCHAR *)thread_desc; status = pNtQueryInformationThread(GetCurrentThread(), ThreadDescription, thread_desc, len, &len2); ok(!status, "Failed to get thread info, status %#x.\n", status); ok(len2 == sizeof(*thread_desc), "Unexpected structure length %u.\n", len); - ok(!thread_desc->Length, "Unexpected description length %#x.\n", thread_desc->Length); - ok(thread_desc->Description == (WCHAR *)(thread_desc + 1), "Unexpected description string pointer %p, %p.\n", - thread_desc->Description, thread_desc); + ok(!thread_desc->Description.Length, "Unexpected description length %#x.\n", thread_desc->Description.Length); + ok(thread_desc->Description.Buffer == (WCHAR *)(thread_desc + 1), + "Unexpected description string pointer %p, %p.\n", thread_desc->Description.Buffer, thread_desc);
hr = pSetThreadDescription(GetCurrentThread(), NULL); ok(hr == HRESULT_FROM_NT(STATUS_SUCCESS), "Failed to set thread description, hr %#x.\n", hr); @@ -2176,11 +2177,11 @@ static void test_thread_description(void) ok(!status, "Failed to get thread info.\n"); ok(len == sizeof(*thread_desc) + desc_len, "Unexpected structure length %u.\n", len);
- ok(thread_desc->Length == (desc_len << 16 | desc_len), "Unexpected description length %#x.\n", - thread_desc->Length); - ok(thread_desc->Description == (WCHAR *)(thread_desc + 1), "Unexpected description string pointer %p, %p.\n", - thread_desc->Description, thread_desc); - ok(!memcmp(thread_desc->Description, desc, desc_len), "Unexpected description string.\n"); + ok(thread_desc->Description.Length == desc_len && thread_desc->Description.MaximumLength == desc_len, + "Unexpected description length %u.\n", thread_desc->Description.Length); + ok(thread_desc->Description.Buffer == (WCHAR *)(thread_desc + 1), + "Unexpected description string pointer %p, %p.\n", thread_desc->Description.Buffer, thread_desc); + ok(!memcmp(thread_desc->Description.Buffer, desc, desc_len), "Unexpected description string.\n");
/* Partial results. */ len = 0; @@ -2193,7 +2194,7 @@ static void test_thread_description(void) ok(len == sizeof(*thread_desc) + desc_len, "Unexpected structure length %u.\n", len);
/* Change description. */ - thread_desc->Length = 8 << 16 | 8; + thread_desc->Description.Length = thread_desc->Description.MaximumLength = 8; lstrcpyW((WCHAR *)(thread_desc + 1), L"desc");
status = pNtSetInformationThread(GetCurrentThread(), ThreadDescription, thread_desc, sizeof(*thread_desc)); @@ -2211,7 +2212,7 @@ static void test_thread_description(void) status = NtSetInformationThread(GetCurrentThread(), ThreadDescription, NULL, sizeof(*thread_desc)); ok(status == STATUS_ACCESS_VIOLATION, "Unexpected status %#x.\n", status);
- thread_desc->Description = NULL; + thread_desc->Description.Buffer = NULL; status = pNtSetInformationThread(GetCurrentThread(), ThreadDescription, thread_desc, sizeof(*thread_desc)); ok(status == STATUS_ACCESS_VIOLATION, "Unexpected status %#x.\n", status);
@@ -2228,8 +2229,7 @@ static void test_thread_description(void) hr = pSetThreadDescription(GetCurrentThread(), L"123"); ok(hr == HRESULT_FROM_NT(STATUS_SUCCESS), "Failed to set thread description, hr %#x.\n", hr);
- thread_desc->Length = 0; - thread_desc->Description = NULL; + memset(thread_desc, 0, sizeof(*thread_desc)); status = pNtSetInformationThread(GetCurrentThread(), ThreadDescription, thread_desc, sizeof(*thread_desc)); ok(!status, "Failed to set thread description, status %#x.\n", status);
diff --git a/dlls/kernelbase/thread.c b/dlls/kernelbase/thread.c index 521d3be079..2f0bcb7dde 100644 --- a/dlls/kernelbase/thread.c +++ b/dlls/kernelbase/thread.c @@ -404,8 +404,8 @@ HRESULT WINAPI DECLSPEC_HOTPATCH SetThreadDescription( HANDLE thread, PCWSTR des if (length > USHRT_MAX) return HRESULT_FROM_NT(STATUS_INVALID_PARAMETER);
- info.Length = length << 16 | length; - info.Description = (WCHAR *)description; + info.Description.Length = info.Description.MaximumLength = length; + info.Description.Buffer = (WCHAR *)description;
return HRESULT_FROM_NT(NtSetInformationThread( thread, ThreadDescription, &info, sizeof(info) )); } @@ -434,15 +434,13 @@ HRESULT WINAPI DECLSPEC_HOTPATCH GetThreadDescription( HANDLE thread, WCHAR **de status = NtQueryInformationThread( thread, ThreadDescription, info, length, &length ); if (!status) { - length = info->Length & 0xffff; - - if (!(*description = LocalAlloc( 0, length + sizeof(WCHAR)))) + if (!(*description = LocalAlloc( 0, info->Description.Length + sizeof(WCHAR)))) status = STATUS_NO_MEMORY; else { - if (length) - memcpy(*description, info->Description, length); - (*description)[length / sizeof(WCHAR)] = 0; + if (info->Description.Length) + memcpy(*description, info->Description.Buffer, info->Description.Length); + (*description)[info->Description.Length / sizeof(WCHAR)] = 0; } }
diff --git a/dlls/ntdll/thread.c b/dlls/ntdll/thread.c index 6079a6c195..fd8d388403 100644 --- a/dlls/ntdll/thread.c +++ b/dlls/ntdll/thread.c @@ -1137,8 +1137,8 @@ NTSTATUS WINAPI NtQueryInformationThread( HANDLE handle, THREADINFOCLASS class, status = STATUS_BUFFER_TOO_SMALL; else if (status == STATUS_SUCCESS) { - info->Length = desc_len << 16 | desc_len; - info->Description = ptr; + info->Description.Length = info->Description.MaximumLength = desc_len; + info->Description.Buffer = ptr; }
if (ret_len && (status == STATUS_SUCCESS || status == STATUS_BUFFER_TOO_SMALL)) @@ -1303,20 +1303,18 @@ NTSTATUS WINAPI NtSetInformationThread( HANDLE handle, THREADINFOCLASS class, case ThreadDescription: { const THREAD_DESCRIPTION_INFORMATION *info = data; - data_size_t desc_len;
if (length != sizeof(*info)) return STATUS_INFO_LENGTH_MISMATCH; if (!info) return STATUS_ACCESS_VIOLATION;
- desc_len = info->Length & 0xffff; - if (info->Length >> 16 != desc_len) return STATUS_INVALID_PARAMETER; - if (info->Length && !info->Description) return STATUS_ACCESS_VIOLATION; + if (info->Description.Length != info->Description.MaximumLength) return STATUS_INVALID_PARAMETER; + if (info->Description.Length && !info->Description.Buffer) return STATUS_ACCESS_VIOLATION;
SERVER_START_REQ( set_thread_info ) { req->handle = wine_server_obj_handle( handle ); req->mask = SET_THREAD_INFO_DESCRIPTION; - wine_server_add_data( req, info->Description, desc_len ); + wine_server_add_data( req, info->Description.Buffer, info->Description.Length ); status = wine_server_call( req ); } SERVER_END_REQ; diff --git a/include/winternl.h b/include/winternl.h index 1477932d10..311fba3ae9 100644 --- a/include/winternl.h +++ b/include/winternl.h @@ -1030,8 +1030,7 @@ typedef struct _THREAD_DESCRIPTOR_INFORMATION
typedef struct _THREAD_DESCRIPTION_INFORMATION { - DWORD Length; - WCHAR *Description; + UNICODE_STRING Description; } THREAD_DESCRIPTION_INFORMATION, *PTHREAD_DESCRIPTION_INFORMATION;
typedef struct _KERNEL_USER_TIMES {
Signed-off-by: Nikolay Sivov nsivov@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 ); }