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

Rémi Bernon rbernon at codeweavers.com
Mon Jun 29 09:45:39 CDT 2020


On 2020-06-29 16:43, Zebediah Figura wrote:
> On 6/29/20 1:32 AM, Rémi Bernon wrote:
>> 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?
> 
> I'm not sure I understand; how would that be more portable?
> 

Of course it wouldn't track Linux kernel time, but instead time spent in 
wineserver as kernel time, but that's what wineserver is supposed to be 
right?

>>
>> 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).
> 
> For what it's worth, this patch has been in wine-staging for a long time
> (not that that necessarily means anything, of course).
> -- 
Rémi Bernon <rbernon at codeweavers.com>



More information about the wine-devel mailing list