[PATCH v2 1/3] ntdll: Implement NtQueryInformationThread(ThreadTimes) using procfs.

Rémi Bernon rbernon at codeweavers.com
Mon Jun 29 01:32:57 CDT 2020


On 2020-06-29 03:43, Zebediah Figura wrote:
> From: Sebastian Lackner <sebastian at fds-team.de>
> 
> Based on a patch by Ray Hinchliffe <ray at pobox.co.uk>.
> 
> Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=20230
> Signed-off-by: Zebediah Figura <z.figura12 at gmail.com>
> ---
> v2: Rebase onto current git (i.e. onto ntdll.so).
> 
> This patch lets System Information Viewer, Ollydbg 1.x/2.x, and x64dbg display
> accurate values.
> 
> While these values are available through host utilities such as ps(1), it is
> sometimes more useful to access them from within debuggers and utilities running
> under Wine.
> 
> I have omitted NtQueryInformationProcess(ProcessTimes), since the obvious
> solution needs the server to pass back more data than it currently can, and I
> don't know what the preferred way to handle that is.
> 
>   dlls/ntdll/unix/thread.c | 82 +++++++++++++++++++++++++++++++---------
>   server/protocol.def      |  2 +
>   server/thread.c          |  2 +
>   3 files changed, 69 insertions(+), 17 deletions(-)
> 
> diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c
> index f3dddd2b02..54483e1f99 100644
> --- a/dlls/ntdll/unix/thread.c
> +++ b/dlls/ntdll/unix/thread.c
> @@ -29,6 +29,8 @@
>   #include <errno.h>
>   #include <limits.h>
>   #include <stdarg.h>
> +#include <stdio.h>
> +#include <string.h>
>   #include <pthread.h>
>   #include <signal.h>
>   #include <sys/types.h>
> @@ -821,6 +823,59 @@ static void wow64_context_to_server( context_t *to, const WOW64_CONTEXT *from )
>   
>   #endif /* __x86_64__ */
>   
> +#ifdef linux
> +static BOOL get_thread_times(int unix_pid, int unix_tid, LARGE_INTEGER *kernel_time, LARGE_INTEGER *user_time)
> +{
> +    unsigned long clocks_per_sec = sysconf( _SC_CLK_TCK );
> +    unsigned long usr, sys;
> +    const char *pos;
> +    char buf[512];
> +    FILE *f;
> +    int i;
> +
> +    sprintf( buf, "/proc/%u/task/%u/stat", unix_pid, unix_tid );
> +    if (!(f = fopen( buf, "r" )))
> +    {
> +        ERR("Failed to open %s: %s\n", buf, strerror(errno));
> +        return FALSE;
> +    }
> +
> +    pos = fgets( buf, sizeof(buf), f );
> +    fclose( f );
> +
> +    /* the process name is printed unescaped, so we have to skip to the last ')'
> +     * to avoid misinterpreting the string */
> +    if (pos) pos = strrchr( pos, ')' );
> +    if (pos) pos = strchr( pos + 1, ' ' );
> +    if (pos) pos++;
> +
> +    /* skip over the following fields: state, ppid, pgid, sid, tty_nr, tty_pgrp,
> +     * task->flags, min_flt, cmin_flt, maj_flt, cmaj_flt */
> +    for (i = 0; i < 11 && pos; i++)
> +    {
> +        pos = strchr( pos + 1, ' ' );
> +        if (pos) pos++;
> +    }
> +
> +    /* the next two values are user and system time */
> +    if (pos && (sscanf( pos, "%lu %lu", &usr, &sys ) == 2))
> +    {
> +        kernel_time->QuadPart = (ULONGLONG)sys * 10000000 / clocks_per_sec;
> +        user_time->QuadPart = (ULONGLONG)usr * 10000000 / clocks_per_sec;
> +        return TRUE;
> +    }
> +
> +    ERR("Failed to parse %s\n", debugstr_a(buf));
> +    return FALSE;
> +}
> +#else
> +static BOOL get_thread_times(int unix_pid, int unix_tid, LARGE_INTEGER *kernel_time, LARGE_INTEGER *user_time)
> +{
> +    static int once;
> +    if (!once++) FIXME("not implemented on this platform\n");
> +    return FALSE;
> +}
> +#endif
>   
>   /******************************************************************************
>    *              NtQueryInformationThread  (NTDLL.@)
> @@ -886,6 +941,7 @@ NTSTATUS WINAPI NtQueryInformationThread( HANDLE handle, THREADINFOCLASS class,
>       case ThreadTimes:
>       {
>           KERNEL_USER_TIMES kusrt;
> +        int unix_pid, unix_tid;
>   
>           SERVER_START_REQ( get_thread_times )
>           {
> @@ -895,15 +951,21 @@ NTSTATUS WINAPI NtQueryInformationThread( HANDLE handle, THREADINFOCLASS class,
>               {
>                   kusrt.CreateTime.QuadPart = reply->creation_time;
>                   kusrt.ExitTime.QuadPart = reply->exit_time;
> +                unix_pid = reply->unix_pid;
> +                unix_tid = reply->unix_tid;
>               }
>           }
>           SERVER_END_REQ;
>           if (status == STATUS_SUCCESS)
>           {
> -            /* We call times(2) for kernel time or user time */
> -            /* We can only (portably) do this for the current thread */
> -            if (handle == GetCurrentThread())
> +            BOOL ret = FALSE;
> +
> +            kusrt.KernelTime.QuadPart = kusrt.UserTime.QuadPart = 0;
> +            if (unix_pid != -1 && unix_tid != -1)
> +                ret = get_thread_times( unix_pid, unix_tid, &kusrt.KernelTime, &kusrt.UserTime );
> +            if (!ret && handle == GetCurrentThread())
>               {
> +                /* fall back to process times */
>                   struct tms time_buf;
>                   long clocks_per_sec = sysconf(_SC_CLK_TCK);
>   
> @@ -911,20 +973,6 @@ NTSTATUS WINAPI NtQueryInformationThread( HANDLE handle, THREADINFOCLASS class,
>                   kusrt.KernelTime.QuadPart = (ULONGLONG)time_buf.tms_stime * 10000000 / clocks_per_sec;
>                   kusrt.UserTime.QuadPart = (ULONGLONG)time_buf.tms_utime * 10000000 / clocks_per_sec;
>               }
> -            else
> -            {
> -                static BOOL reported = FALSE;
> -
> -                kusrt.KernelTime.QuadPart = 0;
> -                kusrt.UserTime.QuadPart = 0;
> -                if (reported)
> -                    TRACE("Cannot get kerneltime or usertime of other threads\n");
> -                else
> -                {
> -                    FIXME("Cannot get kerneltime or usertime of other threads\n");
> -                    reported = TRUE;
> -                }
> -            }
>               if (data) memcpy( data, &kusrt, min( length, sizeof(kusrt) ));
>               if (ret_len) *ret_len = min( length, sizeof(kusrt) );
>           }
> diff --git a/server/protocol.def b/server/protocol.def
> index c3442c06e9..54668a8cdc 100644
> --- a/server/protocol.def
> +++ b/server/protocol.def
> @@ -967,6 +967,8 @@ struct rawinput_device
>   @REPLY
>       timeout_t    creation_time; /* thread creation time */
>       timeout_t    exit_time;     /* thread exit time */
> +    int          unix_pid;      /* thread native pid */
> +    int          unix_tid;      /* thread native pid */
>   @END
>   
>   
> diff --git a/server/thread.c b/server/thread.c
> index e2bfa50c7b..3cf447b1a0 100644
> --- a/server/thread.c
> +++ b/server/thread.c
> @@ -1554,6 +1554,8 @@ DECL_HANDLER(get_thread_times)
>       {
>           reply->creation_time  = thread->creation_time;
>           reply->exit_time      = thread->exit_time;
> +        reply->unix_pid       = thread->unix_pid;
> +        reply->unix_tid       = thread->unix_tid;
>   
>           release_object( thread );
>       }
> 

Wouldn't it be possible (and portable) to track these times in wineserver?

Also FWIW these requests are used a lot by anti-debug checks, and 
although they are apparently happy with the times not being reported, 
I'm a little bit that reporting times could break some of them (although 
I haven't checked yet if they did, I'll try a few ones).
-- 
Rémi Bernon <rbernon at codeweavers.com>



More information about the wine-devel mailing list