From: Zebediah Figura Subject: [PATCH 1/3] ntdll: Reimplement NtQuerySystemInformation(SystemProcessInformation) using a single server call. Message-Id: <20200702031814.1078606-1-zfigura@codeweavers.com> Date: Wed, 1 Jul 2020 22:18:12 -0500 Signed-off-by: Zebediah Figura --- This decreases CPU usage by wineserver from about 65% to about 45% for me in Street Fighter V, which for some reason calls this 15-20 times per second. dlls/ntdll/unix/system.c | 177 ++++++++++++++++----------------------- server/process.c | 56 +++++++++++++ server/protocol.def | 30 +++++++ server/trace.c | 34 ++++++++ 4 files changed, 191 insertions(+), 106 deletions(-) diff --git a/dlls/ntdll/unix/system.c b/dlls/ntdll/unix/system.c index 7045bc1550b..f89208997e2 100644 --- a/dlls/ntdll/unix/system.c +++ b/dlls/ntdll/unix/system.c @@ -2083,132 +2083,97 @@ NTSTATUS WINAPI NtQuerySystemInformation( SYSTEM_INFORMATION_CLASS class, case SystemProcessInformation: { - SYSTEM_PROCESS_INFORMATION *spi = info; - SYSTEM_PROCESS_INFORMATION *last = NULL; - HANDLE handle = 0; - WCHAR procname[1024]; - WCHAR* exename; - DWORD wlen = 0; - DWORD procstructlen = 0; + unsigned int process_count, i, j; + const struct process_info *server_process; + void *buffer = NULL; - SERVER_START_REQ( create_snapshot ) + if (size && !(buffer = RtlAllocateHeap( GetProcessHeap(), 0, size ))) { - req->flags = SNAP_PROCESS | SNAP_THREAD; - if (!(ret = wine_server_call( req ))) handle = wine_server_ptr_handle( reply->handle ); + ret = STATUS_NO_MEMORY; + break; } - SERVER_END_REQ; - len = 0; - while (ret == STATUS_SUCCESS) + SERVER_START_REQ( list_processes ) { - int unix_pid = -1; - SERVER_START_REQ( next_process ) - { - req->handle = wine_server_obj_handle( handle ); - req->reset = (len == 0); - wine_server_set_reply( req, procname, sizeof(procname) - sizeof(WCHAR) ); - if (!(ret = wine_server_call( req ))) - { - unix_pid = reply->unix_pid; - - /* Make sure procname is 0 terminated */ - procname[wine_server_reply_size(reply) / sizeof(WCHAR)] = 0; - - /* Get only the executable name, not the path */ - if ((exename = wcsrchr(procname, '\\')) != NULL) exename++; - else exename = procname; - - wlen = (wcslen(exename) + 1) * sizeof(WCHAR); - procstructlen = sizeof(*spi) + wlen + ((reply->threads - 1) * sizeof(SYSTEM_THREAD_INFORMATION)); + wine_server_set_reply( req, buffer, size ); + ret = wine_server_call( req ); + len = reply->user_len; + process_count = reply->process_count; + } + SERVER_END_REQ; - if (size >= len + procstructlen) - { - /* ftCreationTime, ftUserTime, ftKernelTime; - * vmCounters, ioCounters - */ - memset(spi, 0, sizeof(*spi)); + if (ret) + { + RtlFreeHeap( GetProcessHeap(), 0, buffer ); + break; + } - spi->NextEntryOffset = procstructlen - wlen; - spi->dwThreadCount = reply->threads; + server_process = buffer; + len = 0; - /* spi->pszProcessName will be set later on */ + for (i = 0; i < process_count; i++) + { + const WCHAR *server_name = (const WCHAR *)(server_process + 1); + SYSTEM_PROCESS_INFORMATION *nt_process = (SYSTEM_PROCESS_INFORMATION *)((char *)info + len); + const struct thread_info *server_thread; + const WCHAR *file_part; + ULONG proc_len; + ULONG name_len = 0; - spi->dwBasePriority = reply->priority; - spi->UniqueProcessId = UlongToHandle(reply->pid); - spi->ParentProcessId = UlongToHandle(reply->ppid); - spi->HandleCount = reply->handles; + file_part = server_name + (server_process->name_len / sizeof(WCHAR)); + while (file_part > server_name && file_part[-1] != '\\') + { + file_part--; + name_len++; + } - /* spi->ti will be set later on */ + proc_len = sizeof(*nt_process) + server_process->thread_count * sizeof(SYSTEM_THREAD_INFORMATION) + + (name_len + 1) * sizeof(WCHAR); + len += proc_len; - } - len += procstructlen; - } - } - SERVER_END_REQ; - if (ret != STATUS_SUCCESS) + if (len <= size) { - if (ret == STATUS_NO_MORE_FILES) ret = STATUS_SUCCESS; - break; + memset(nt_process, 0, sizeof(*nt_process)); + if (i < process_count - 1) + nt_process->NextEntryOffset = proc_len; + nt_process->dwThreadCount = server_process->thread_count; + nt_process->dwBasePriority = server_process->priority; + nt_process->UniqueProcessId = UlongToHandle(server_process->pid); + nt_process->ParentProcessId = UlongToHandle(server_process->parent_pid); + nt_process->HandleCount = server_process->handle_count; + get_thread_times( server_process->unix_pid, -1, &nt_process->KernelTime, &nt_process->UserTime ); } - if (size >= len) + server_thread = (const struct thread_info *)((const char *)server_name + server_process->name_len); + for (j = 0; j < server_process->thread_count; j++) { - int i, j; - - get_thread_times(unix_pid, -1, &spi->KernelTime, &spi->UserTime); - - /* set thread info */ - i = j = 0; - while (ret == STATUS_SUCCESS) + if (len <= size) { - int unix_tid, pid, tid, base_pri, delta_pri; - SERVER_START_REQ( next_thread ) - { - req->handle = wine_server_obj_handle( handle ); - req->reset = (j == 0); - if (!(ret = wine_server_call( req ))) - { - unix_tid = reply->unix_tid; - pid = reply->pid; - tid = reply->tid; - base_pri = reply->base_pri; - delta_pri = reply->delta_pri; - j++; - } - } - SERVER_END_REQ; - - if (!ret) - { - if (UlongToHandle(pid) == spi->UniqueProcessId) - { - memset(&spi->ti[i], 0, sizeof(spi->ti)); - - spi->ti[i].CreateTime.QuadPart = 0xdeadbeef; - spi->ti[i].ClientId.UniqueProcess = UlongToHandle(pid); - spi->ti[i].ClientId.UniqueThread = UlongToHandle(tid); - spi->ti[i].dwCurrentPriority = base_pri + delta_pri; - spi->ti[i].dwBasePriority = base_pri; - get_thread_times(unix_pid, unix_tid, &spi->ti[i].KernelTime, &spi->ti[i].UserTime); - i++; - } - } + nt_process->ti[j].CreateTime.QuadPart = 0xdeadbeef; + nt_process->ti[j].ClientId.UniqueProcess = UlongToHandle(server_process->pid); + nt_process->ti[j].ClientId.UniqueThread = UlongToHandle(server_thread->tid); + nt_process->ti[j].dwCurrentPriority = server_thread->current_priority; + nt_process->ti[j].dwBasePriority = server_thread->base_priority; + get_thread_times( server_process->unix_pid, server_thread->unix_tid, + &nt_process->ti[j].KernelTime, &nt_process->ti[j].UserTime ); } - if (ret == STATUS_NO_MORE_FILES) ret = STATUS_SUCCESS; - - /* now append process name */ - spi->ProcessName.Buffer = (WCHAR*)((char*)spi + spi->NextEntryOffset); - spi->ProcessName.Length = wlen - sizeof(WCHAR); - spi->ProcessName.MaximumLength = wlen; - memcpy( spi->ProcessName.Buffer, exename, wlen ); - spi->NextEntryOffset += wlen; - last = spi; - spi = (SYSTEM_PROCESS_INFORMATION*)((char*)spi + spi->NextEntryOffset); + + server_thread++; } + + if (len <= size) + { + nt_process->ProcessName.Buffer = (WCHAR *)&nt_process->ti[server_process->thread_count]; + nt_process->ProcessName.Length = name_len * sizeof(WCHAR); + nt_process->ProcessName.MaximumLength = (name_len + 1) * sizeof(WCHAR); + memcpy(nt_process->ProcessName.Buffer, file_part, name_len * sizeof(WCHAR)); + nt_process->ProcessName.Buffer[name_len] = 0; + } + + server_process = (const struct process_info *)server_thread; } - if (ret == STATUS_SUCCESS && last) last->NextEntryOffset = 0; + if (len > size) ret = STATUS_INFO_LENGTH_MISMATCH; - if (handle) NtClose( handle ); break; } diff --git a/server/process.c b/server/process.c index 283edc09c5e..452861491f8 100644 --- a/server/process.c +++ b/server/process.c @@ -22,6 +22,7 @@ #include "wine/port.h" #include +#include #include #include #include @@ -1819,3 +1820,58 @@ DECL_HANDLER(resume_process) release_object( process ); } } + +/* Get a list of processes and threads currently running */ +DECL_HANDLER(list_processes) +{ + struct process_info *process_info; + struct process *process; + struct thread *thread; + + reply->process_count = 0; + reply->user_len = 0; + + LIST_FOR_EACH_ENTRY( process, &process_list, struct process, entry ) + { + struct process_dll *exe = get_process_exe_module( process ); + reply->user_len += sizeof(struct process_info) + exe->namelen; + reply->user_len += process->running_threads * sizeof(struct thread_info); + reply->process_count++; + } + + if (reply->user_len > get_reply_max_size()) + { + set_error( STATUS_INFO_LENGTH_MISMATCH ); + return; + } + + process_info = set_reply_data_size( reply->user_len ); + if (!process_info) return; + + LIST_FOR_EACH_ENTRY( process, &process_list, struct process, entry ) + { + struct process_dll *exe = get_process_exe_module( process ); + WCHAR *name = (WCHAR *)(process_info + 1); + struct thread_info *thread_info = (struct thread_info *)((char *)name + exe->namelen); + + process_info->name_len = exe->namelen; + process_info->thread_count = process->running_threads; + process_info->priority = process->priority; + process_info->pid = process->id; + process_info->parent_pid = process->parent_id; + process_info->handle_count = get_handle_table_count(process); + process_info->unix_pid = process->unix_pid; + memcpy( name, exe->filename, exe->namelen ); + + LIST_FOR_EACH_ENTRY( thread, &process->thread_list, struct thread, proc_entry ) + { + thread_info->tid = thread->id; + thread_info->base_priority = thread->priority; + thread_info->current_priority = thread->priority; /* FIXME */ + thread_info->unix_tid = thread->unix_tid; + thread_info++; + } + + process_info = (struct process_info *)thread_info; + } +} diff --git a/server/protocol.def b/server/protocol.def index 6416306c0a1..bd531a1a727 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1919,6 +1919,36 @@ enum char_info_mode @END +struct thread_info +{ + thread_id_t tid; + int base_priority; + int current_priority; + int unix_tid; +}; + +struct process_info +{ + data_size_t name_len; + unsigned int thread_count; + int priority; + process_id_t pid; + process_id_t parent_pid; + unsigned int handle_count; + int unix_pid; + /* VARARG(name,unicode_str,name_len); */ + /* VARARG(threads,struct thread_info,thread_count); */ +}; + +/* Get a list of processes and threads currently running */ +@REQ(list_processes) +@REPLY + data_size_t user_len; /* length needed to store user */ + unsigned int process_count; + VARARG(data,process_info); +@END + + /* Wait for a debug event */ @REQ(wait_debug_event) int get_handle; /* should we alloc a handle for waiting? */ diff --git a/server/trace.c b/server/trace.c index 2fb5afd5ef1..f39c7402d41 100644 --- a/server/trace.c +++ b/server/trace.c @@ -1115,6 +1115,40 @@ static void dump_varargs_token_groups( const char *prefix, data_size_t size ) fputc( '}', stderr ); } +static void dump_varargs_process_info( const char *prefix, data_size_t size ) +{ + const struct process_info *process = cur_data; + const struct thread_info *thread; + data_size_t pos = 0; + unsigned int i; + + fprintf( stderr,"%s{", prefix ); + + while (size - pos >= sizeof(*process)) + { + fprintf( stderr, "[thread_count=%u,priority=%d,pid=%04x,parent_pid=%04x,handle_count=%08x,unix_pid=%d,", + process->thread_count, process->priority, process->pid, + process->parent_pid, process->handle_count, process->unix_pid ); + pos += sizeof(*process); + pos = dump_inline_unicode_string( "name=L\"", pos, process->name_len, size ); + fprintf( stderr, "\",threads={" ); + thread = (const struct thread_info *)((const char *)cur_data + pos); + for (i = 0; i < process->thread_count; i++) + { + if (size - pos < sizeof(*thread)) break; + fprintf( stderr, "[tid=%04x,base_priority=%d,current_priority=%d,unix_tid=%d],", + thread->tid, thread->base_priority, thread->current_priority, thread->unix_tid ); + pos += sizeof(*thread); + thread++; + } + fprintf( stderr, "}," ); + + process = (const struct process_info *)thread; + } + + fputc( '}', stderr ); +} + static void dump_varargs_object_attributes( const char *prefix, data_size_t size ) { const struct object_attributes *objattr = cur_data; -- 2.27.0