[PATCH 1/3] ntdll: Reimplement NtQuerySystemInformation(SystemProcessInformation) using a single server call.

Zebediah Figura z.figura12 at gmail.com
Wed Jul 1 22:18:12 CDT 2020


Signed-off-by: Zebediah Figura <zfigura at codeweavers.com>
---
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 <assert.h>
+#include <errno.h>
 #include <limits.h>
 #include <signal.h>
 #include <string.h>
@@ -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 */
+ at REQ(list_processes)
+ at REPLY
+    data_size_t     user_len;     /* length needed to store user */
+    unsigned int    process_count;
+    VARARG(data,process_info);
+ at 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




More information about the wine-devel mailing list