From: Sebastian Lackner sebastian@fds-team.de
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 --- 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 ); }
From: Michael Müller michael@fds-team.de
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- This patch lets Process Hacker display accurate values.
dlls/ntdll/unix/system.c | 40 +++++++++++++++++++++------------- dlls/ntdll/unix/thread.c | 4 ++-- dlls/ntdll/unix/unix_private.h | 2 ++ server/protocol.def | 1 + server/snapshot.c | 1 + 5 files changed, 31 insertions(+), 17 deletions(-)
diff --git a/dlls/ntdll/unix/system.c b/dlls/ntdll/unix/system.c index 5e2e763445..fd86ab874a 100644 --- a/dlls/ntdll/unix/system.c +++ b/dlls/ntdll/unix/system.c @@ -2101,6 +2101,7 @@ NTSTATUS WINAPI NtQuerySystemInformation( SYSTEM_INFORMATION_CLASS class, len = 0; while (ret == STATUS_SUCCESS) { + int unix_pid = -1; SERVER_START_REQ( next_process ) { req->handle = wine_server_obj_handle( handle ); @@ -2108,6 +2109,8 @@ NTSTATUS WINAPI NtQuerySystemInformation( SYSTEM_INFORMATION_CLASS class, 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;
@@ -2156,31 +2159,38 @@ NTSTATUS WINAPI NtQuerySystemInformation( SYSTEM_INFORMATION_CLASS class, i = j = 0; while (ret == STATUS_SUCCESS) { + 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++; - if (UlongToHandle(reply->pid) == spi->UniqueProcessId) - { - /* ftKernelTime, ftUserTime, ftCreateTime; - * dwTickCount, dwStartAddress - */ - - memset(&spi->ti[i], 0, sizeof(spi->ti)); - - spi->ti[i].CreateTime.QuadPart = 0xdeadbeef; - spi->ti[i].ClientId.UniqueProcess = UlongToHandle(reply->pid); - spi->ti[i].ClientId.UniqueThread = UlongToHandle(reply->tid); - spi->ti[i].dwCurrentPriority = reply->base_pri + reply->delta_pri; - spi->ti[i].dwBasePriority = reply->base_pri; - i++; - } } } 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++; + } + } } if (ret == STATUS_NO_MORE_FILES) ret = STATUS_SUCCESS;
diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c index 54483e1f99..3655adf2db 100644 --- a/dlls/ntdll/unix/thread.c +++ b/dlls/ntdll/unix/thread.c @@ -824,7 +824,7 @@ 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) +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; @@ -869,7 +869,7 @@ static BOOL get_thread_times(int unix_pid, int unix_tid, LARGE_INTEGER *kernel_t return FALSE; } #else -static BOOL get_thread_times(int unix_pid, int unix_tid, LARGE_INTEGER *kernel_time, LARGE_INTEGER *user_time) +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"); diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h index ee6caaec8f..fe63606ed8 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h @@ -209,6 +209,8 @@ extern void virtual_fill_image_information( const pe_image_info_t *pe_info, SECTION_IMAGE_INFORMATION *info ) DECLSPEC_HIDDEN;
extern NTSTATUS get_thread_ldt_entry( HANDLE handle, void *data, ULONG len, ULONG *ret_len ) DECLSPEC_HIDDEN; +extern BOOL get_thread_times( int unix_pid, int unix_tid, LARGE_INTEGER *kernel_time, + LARGE_INTEGER *user_time ) DECLSPEC_HIDDEN; extern void signal_init_threading(void) DECLSPEC_HIDDEN; extern NTSTATUS signal_alloc_thread( TEB *teb ) DECLSPEC_HIDDEN; extern void signal_free_thread( TEB *teb ) DECLSPEC_HIDDEN; diff --git a/server/protocol.def b/server/protocol.def index 54668a8cdc..a625384e36 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1914,6 +1914,7 @@ enum char_info_mode thread_id_t tid; /* thread id */ int base_pri; /* base priority */ int delta_pri; /* delta priority */ + int unix_tid; /* thread native pid */ @END
diff --git a/server/snapshot.c b/server/snapshot.c index a0f2ea17a3..bdceaef530 100644 --- a/server/snapshot.c +++ b/server/snapshot.c @@ -150,6 +150,7 @@ static int snapshot_next_thread( struct snapshot *snapshot, struct next_thread_r reply->tid = get_thread_id( ptr->thread ); reply->base_pri = ptr->priority; reply->delta_pri = 0; /* FIXME */ + reply->unix_tid = ptr->thread->unix_tid; return 1; }
From: Michael Müller michael@fds-team.de
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- This patch lets Process Hacker display accurate values.
dlls/ntdll/unix/system.c | 2 ++ dlls/ntdll/unix/thread.c | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/dlls/ntdll/unix/system.c b/dlls/ntdll/unix/system.c index fd86ab874a..7045bc1550 100644 --- a/dlls/ntdll/unix/system.c +++ b/dlls/ntdll/unix/system.c @@ -2155,6 +2155,8 @@ NTSTATUS WINAPI NtQuerySystemInformation( SYSTEM_INFORMATION_CLASS class, { int i, j;
+ get_thread_times(unix_pid, -1, &spi->KernelTime, &spi->UserTime); + /* set thread info */ i = j = 0; while (ret == STATUS_SUCCESS) diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c index 3655adf2db..c583a08c05 100644 --- a/dlls/ntdll/unix/thread.c +++ b/dlls/ntdll/unix/thread.c @@ -833,7 +833,10 @@ BOOL get_thread_times(int unix_pid, int unix_tid, LARGE_INTEGER *kernel_time, LA FILE *f; int i;
- sprintf( buf, "/proc/%u/task/%u/stat", unix_pid, unix_tid ); + if (unix_tid == -1) + sprintf( buf, "/proc/%u/stat", unix_pid ); + else + 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));
On 2020-06-29 03:43, Zebediah Figura wrote:
From: Sebastian Lackner sebastian@fds-team.de
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
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).
On 6/29/20 1:32 AM, Rémi Bernon wrote:
On 2020-06-29 03:43, Zebediah Figura wrote:
From: Sebastian Lackner sebastian@fds-team.de
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
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).
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@fds-team.de
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
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@codeweavers.com
On 6/29/20 9:45 AM, Rémi Bernon wrote:
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@fds-team.de
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
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?
That could arguably make sense, though in that case I think we'd want to track the time spent in unix libraries in general, not just in the server.
I'll leave it to the maintainer to decide which implementation is more correct; I have no idea myself.
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@codeweavers.com