From: Bernard Ladenthin <bernard.ladenthin@gmail.com>
Subject: kernel32/time: Implement GetSystemTimes using /proc/stat.
Message-Id: <52F9075B.4000601@gmail.com>
Date: Mon, 10 Feb 2014 18:07:39 +0100

---
 dlls/kernel32/time.c |  149
+++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 147 insertions(+), 2 deletions(-)

diff --git a/dlls/kernel32/time.c b/dlls/kernel32/time.c
index 77c71cb..1e2468d 100644
--- a/dlls/kernel32/time.c
+++ b/dlls/kernel32/time.c
@@ -20,6 +20,7 @@
 
 #include "config.h"
 
+#include <stdio.h>
 #include <string.h>
 #ifdef HAVE_UNISTD_H
 # include <unistd.h>
@@ -1077,9 +1078,153 @@ BOOL WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
  */
 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;
+
+    /* 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;
+
+    procfile = fopen(proc_stat, "r");
+    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;
+    }
+
+    if (strncmp(line, "cpu ", 4) != 0)
+    {
+        fclose(procfile);
+        TRACE("Cpu summary line starts invalid in %s\n", proc_stat);
+        return FALSE;
+    }
+
+    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;
+    }
+
+    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;
+        }
+
+        if (strncmp(line, "cpu", 3) != 0) break;
+        else ++numCPUs;
+    }
+
+    fclose(procfile);
+
+    /* Times are multiplied by the number of processors. */
+    divisor = 10000 * numCPUs;
+
+    idleTime    = cpu_idle / divisor;
 
-    return FALSE;
+    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;
+
+    lpIdleTime->dwHighDateTime = idleTime;
+    lpIdleTime->dwLowDateTime = MAXDWORD * (idleTime-lpIdleTime->dwHighDateTime);
+
+    lpKernelTime->dwHighDateTime = kernelTime;
+    lpKernelTime->dwLowDateTime = MAXDWORD * (kernelTime-lpKernelTime->dwHighDateTime);
+
+    lpUserTime->dwHighDateTime = userTime;
+    lpUserTime->dwLowDateTime = MAXDWORD * (userTime-lpUserTime->dwHighDateTime);
+
+    return TRUE;
 }
 
 /***********************************************************************