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

Zebediah Figura z.figura12 at gmail.com
Mon Jun 29 09:43:10 CDT 2020


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?

> 
> 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).

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 488 bytes
Desc: OpenPGP digital signature
URL: <http://www.winehq.org/pipermail/wine-devel/attachments/20200629/b7697c61/attachment.sig>


More information about the wine-devel mailing list