ntdll: Implement NtQueryInformationThread(ThreadTimes) using procfs.

Based on a patch by Ray Hinchliffe <ray@pobox.co.uk>.

Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=20230
Signed-off-by: Zebediah Figura <z.figura12@gmail.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Sebastian Lackner 2020-06-28 20:43:17 -05:00 committed by Alexandre Julliard
parent 045455bfe4
commit 847b93c740
6 changed files with 77 additions and 19 deletions

View File

@ -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) );
}

View File

@ -1037,6 +1037,8 @@ struct get_thread_times_reply
struct reply_header __header;
timeout_t creation_time;
timeout_t exit_time;
int unix_pid;
int unix_tid;
};
@ -6700,7 +6702,7 @@ union generic_reply
/* ### protocol_version begin ### */
#define SERVER_PROTOCOL_VERSION 609
#define SERVER_PROTOCOL_VERSION 610
/* ### protocol_version end ### */

View File

@ -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

View File

@ -862,7 +862,9 @@ C_ASSERT( FIELD_OFFSET(struct get_thread_times_request, handle) == 12 );
C_ASSERT( sizeof(struct get_thread_times_request) == 16 );
C_ASSERT( FIELD_OFFSET(struct get_thread_times_reply, creation_time) == 8 );
C_ASSERT( FIELD_OFFSET(struct get_thread_times_reply, exit_time) == 16 );
C_ASSERT( sizeof(struct get_thread_times_reply) == 24 );
C_ASSERT( FIELD_OFFSET(struct get_thread_times_reply, unix_pid) == 24 );
C_ASSERT( FIELD_OFFSET(struct get_thread_times_reply, unix_tid) == 28 );
C_ASSERT( sizeof(struct get_thread_times_reply) == 32 );
C_ASSERT( FIELD_OFFSET(struct set_thread_info_request, handle) == 12 );
C_ASSERT( FIELD_OFFSET(struct set_thread_info_request, mask) == 16 );
C_ASSERT( FIELD_OFFSET(struct set_thread_info_request, priority) == 20 );

View File

@ -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 );
}

View File

@ -1442,6 +1442,8 @@ static void dump_get_thread_times_reply( const struct get_thread_times_reply *re
{
dump_timeout( " creation_time=", &req->creation_time );
dump_timeout( ", exit_time=", &req->exit_time );
fprintf( stderr, ", unix_pid=%d", req->unix_pid );
fprintf( stderr, ", unix_tid=%d", req->unix_tid );
}
static void dump_set_thread_info_request( const struct set_thread_info_request *req )