From: Bernard Ladenthin Subject: kernel32/time: Implement GetSystemTimes using /proc/stat. Message-Id: <53A86AE6.5000102@gmail.com> Date: Mon, 23 Jun 2014 19:59:02 +0200 --- dlls/kernel32/time.c | 175 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 170 insertions(+), 5 deletions(-) diff --git a/dlls/kernel32/time.c b/dlls/kernel32/time.c index 21ac120..e5c864f 100644 --- a/dlls/kernel32/time.c +++ b/dlls/kernel32/time.c @@ -20,6 +20,7 @@ #include "config.h" +#include #include #ifdef HAVE_UNISTD_H # include @@ -1081,18 +1082,182 @@ BOOL WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate, * Retrieves system timing information * * PARAMS - * lpIdleTime [O] Destination for idle time. - * lpKernelTime [O] Destination for kernel time. - * lpUserTime [O] Destination for user time. + * lpIdleTime [out] Destination for idle time. + * lpKernelTime [out] Destination for kernel time. + * lpUserTime [out] Destination for user time. * * RETURNS * TRUE if success, FALSE otherwise. */ BOOL WINAPI GetSystemTimes(LPFILETIME lpIdleTime, LPFILETIME lpKernelTime, LPFILETIME lpUserTime) { - FIXME("(%p,%p,%p): Stub!\n", lpIdleTime, lpKernelTime, lpUserTime); + FILE *procfile; + int successfulValues; + unsigned int numCPUs = 0; + const char proc_stat[] = "/proc/stat"; + float idleTime, kernelTime, userTime; + float divisor; + long clocksPerSec; + ULONGLONG secs; + + /* Enough for each of the /proc/stat cpu lines. */ + char line[512]; + + /* Description from PROC(5): proc - process information pseudo-filesystem + * All the fields don't necessarily exist, depending on the kernel version used. + * Therefore, all values are initialized. + */ + + /* Time spent in user mode. */ + unsigned long long cpu_user = 0; + + /* Time spent in user mode with low priority (nice). */ + unsigned long long cpu_nice = 0; + + /* Time spent in system mode. */ + unsigned long long cpu_sys = 0; + + /* Time spent in the idle task. This value should be USER_HZ times the second entry in the + * /proc/uptime pseudo-file. + */ + unsigned long long cpu_idle = 0; + + /* Time waiting for I/O to complete. (since Linux 2.5.41) */ + unsigned long long cpu_iowait = 0; + + /* Time servicing interrupts. (since Linux 2.6.0-test4) */ + unsigned long long cpu_hardirq = 0; + + /* Time servicing softirqs. (since Linux 2.6.0-test4) */ + unsigned long long cpu_softirq = 0; + + /* Stolen time, which is the time spent in other operating systems when running in a + * virtualized environment. (since Linux 2.6.11) + */ + unsigned long long cpu_steal = 0; + + /* Time spent running a virtual CPU for guest operating systems under the control of the + * Linux kernel. (since Linux 2.6.24) + */ + unsigned long long cpu_guest = 0; + + /* Time spent running a niced guest (virtual CPU for guest operating systems under the + * control of the Linux kernel). (since Linux 2.6.33) + */ + unsigned long long cpu_guest_nice = 0; + + /* Get the clocks per second. */ + clocksPerSec = sysconf(_SC_CLK_TCK); + + /* The clocks in 100's of nanoseconds. */ + secs = 10000000 / clocksPerSec; + + /* Try to open the /proc/stat file. */ + procfile = fopen(proc_stat, "r"); + + /* Couldn't open the /proc/stat file. */ + if (!procfile) + { + FIXME("(%p,%p,%p): Not implemented for this platform!\n", lpIdleTime, lpKernelTime, + lpUserTime); + return FALSE; + } + + /* Read the cpu summary line. */ + if (!fgets(line, sizeof(line), procfile)) + { + fclose(procfile); + TRACE("Could not read the cpu summary line from %s\n", proc_stat); + SetLastError(ERROR_BAD_FORMAT); + return FALSE; + } + + /* Check whether the line starts with cpu. */ + if (strncmp(line, "cpu ", 4) != 0) + { + fclose(procfile); + TRACE("Cpu summary line starts invalid in %s\n", proc_stat); + return FALSE; + } + + /* Scan the cpu summary line. */ + successfulValues = sscanf( + line + 5, + "%llu %llu %llu %llu %llu %llu %llu %llu %llu %llu", + &cpu_user, + &cpu_nice, + &cpu_sys, + &cpu_idle, + &cpu_iowait, + &cpu_hardirq, + &cpu_softirq, + &cpu_steal, + &cpu_guest, + &cpu_guest_nice + ); + + /* cpu_user, cpu_nice, cpu_sys, cpu_idle, cpu_iowait, cpu_hardirq, cpu_softirq and cpu_steal + * must be at least read correctly. cpu_guest and cpu_guest_nice is insignificant. + * cpu_user/cpu_nice already include cpu_guest/cpu_guest_nice. + */ + if (successfulValues < 8) + { + fclose(procfile); + TRACE("Could not read the cpu summary line properly from %s\n", proc_stat); + SetLastError(ERROR_BAD_FORMAT); + return FALSE; + } - return FALSE; + /* Reading each line to determine the sum of all the processors. */ + for (;;) + { + if (!fgets(line, sizeof(line), procfile)) + { + fclose(procfile); + TRACE("Could not read the cpu line from %s\n", proc_stat); + SetLastError(ERROR_BAD_FORMAT); + return FALSE; + } + + /* Check whether the line starts with cpu. */ + if (strncmp(line, "cpu", 3) != 0) break; + else ++numCPUs; + } + + fclose(procfile); + + /* Times are multiplied by the number of processors. */ + divisor = secs * numCPUs; + + idleTime = cpu_idle / divisor; + + kernelTime = idleTime; + kernelTime += cpu_sys / divisor; + kernelTime += cpu_iowait / divisor; + kernelTime += cpu_hardirq / divisor; + kernelTime += cpu_softirq / divisor; + kernelTime += cpu_steal / divisor; + + /* cpu_user/cpu_nice already include cpu_guest/cpu_guest_nice. */ + userTime = cpu_user / divisor; + userTime += cpu_nice / divisor; + + if (lpIdleTime) { + lpIdleTime->dwHighDateTime = idleTime; + lpIdleTime->dwLowDateTime = MAXDWORD * (idleTime-lpIdleTime->dwHighDateTime); + } + + if (lpKernelTime) { + lpKernelTime->dwHighDateTime = kernelTime; + lpKernelTime->dwLowDateTime = MAXDWORD * (kernelTime-lpKernelTime->dwHighDateTime); + } + + if (lpUserTime) { + lpUserTime->dwHighDateTime = userTime; + lpUserTime->dwLowDateTime = MAXDWORD * (userTime-lpUserTime->dwHighDateTime); + } + + return TRUE; } /***********************************************************************