Add kernel and user CPU utilization

This adds two utilization sensors, "CPU_Kernel" and "CPU_User" that
are populated with the fraction of CPU processing spent in kernel space
and user space.

The intended use case is BMCWeb can read those sensors from
phosphor-health-monitor and report them in a RedFish response.

The RedFish resources are:
* ManagerDiagnosticData.ProcessorStatistics.KernelPercent
* ManagerDiagnosticData.ProcessorStatistics.UserPercent

Tested: Installed on a QEMU-emulated BMC
bmc# busctl tree xyz.openbmc_project.HealthMon
`-/xyz
  `-/xyz/openbmc_project
    `-/xyz/openbmc_project/sensors
      `-/xyz/openbmc_project/sensors/utilization
        |-/xyz/openbmc_project/sensors/utilization/CPU
        |-/xyz/openbmc_project/sensors/utilization/CPU_Kernel
        `-/xyz/openbmc_project/sensors/utilization/CPU_User

signed-off-by: Sui Chen <suichen@google.com>
Change-Id: Ic6792e4c7cd8eba144eb9adec9366c1bc15b1a44
diff --git a/bmc_health_config.json b/bmc_health_config.json
index b969676..99dde39 100644
--- a/bmc_health_config.json
+++ b/bmc_health_config.json
@@ -4,18 +4,54 @@
     "Window_size": 120,
     "Threshold":
     {
-        "Critical":
-        {
-            "Value": 90.0,
-            "Log": true,
-            "Target": ""
-        },
-        "Warning":
-        {
-            "Value": 80.0,
-            "Log": false,
-            "Target": ""
-        }
+      "Critical":
+      {
+          "Value": 90.0,
+          "Log": true,
+          "Target": ""
+      },
+      "Warning":
+      {
+        "Value": 80.0,
+        "Log": false,
+        "Target": ""
+      }
+    }
+  },
+  "CPU_User" : {
+    "Frequency" : 1,
+    "Window_size": 120,
+    "Threshold": {
+      "Critical":
+      {
+          "Value": 90.0,
+          "Log": true,
+          "Target": ""
+      },
+      "Warning":
+      {
+          "Value": 80.0,
+          "Log": false,
+          "Target": ""
+      }
+    }
+  },
+  "CPU_Kernel" : {
+    "Frequency" : 1,
+    "Window_size": 120,
+    "Threshold": {
+      "Critical":
+      {
+          "Value": 90.0,
+          "Log": true,
+          "Target": ""
+      },
+      "Warning":
+      {
+        "Value": 80.0,
+        "Log": false,
+        "Target": ""
+      }
     }
   },
   "Memory" : {
@@ -37,12 +73,12 @@
     "Window_size": 120,
     "Threshold":
     {
-        "Critical":
-        {
-            "Value": 85.0,
-            "Log": true,
-            "Target": ""
-        }
+      "Critical":
+      {
+          "Value": 85.0,
+          "Log": true,
+          "Target": ""
+      }
     }
   },
   "Storage_TMP" : {
diff --git a/healthMonitor.cpp b/healthMonitor.cpp
index 24cd067..0255f5f 100644
--- a/healthMonitor.cpp
+++ b/healthMonitor.cpp
@@ -103,7 +103,14 @@
     NUM_CPU_STATES_TIME
 };
 
-double readCPUUtilization([[maybe_unused]] std::string path)
+enum CPUUtilizationType
+{
+    USER = 0,
+    KERNEL,
+    TOTAL
+};
+
+double readCPUUtilization(enum CPUUtilizationType type)
 {
     auto proc_stat = "/proc/stat";
     std::ifstream fileStat(proc_stat);
@@ -142,22 +149,34 @@
         return -1;
     }
 
-    static double preActiveTime = 0, preIdleTime = 0;
+    static std::unordered_map<enum CPUUtilizationType, double> preActiveTime,
+        preIdleTime;
     double activeTime, activeTimeDiff, idleTime, idleTimeDiff, totalTime,
         activePercValue;
 
     idleTime = timeData[IDLE_IDX] + timeData[IOWAIT_IDX];
-    activeTime = timeData[USER_IDX] + timeData[NICE_IDX] +
-                 timeData[SYSTEM_IDX] + timeData[IRQ_IDX] +
-                 timeData[SOFTIRQ_IDX] + timeData[STEAL_IDX] +
-                 timeData[GUEST_USER_IDX] + timeData[GUEST_NICE_IDX];
+    if (type == TOTAL)
+    {
+        activeTime = timeData[USER_IDX] + timeData[NICE_IDX] +
+                     timeData[SYSTEM_IDX] + timeData[IRQ_IDX] +
+                     timeData[SOFTIRQ_IDX] + timeData[STEAL_IDX] +
+                     timeData[GUEST_USER_IDX] + timeData[GUEST_NICE_IDX];
+    }
+    else if (type == KERNEL)
+    {
+        activeTime = timeData[SYSTEM_IDX];
+    }
+    else if (type == USER)
+    {
+        activeTime = timeData[USER_IDX];
+    }
 
-    idleTimeDiff = idleTime - preIdleTime;
-    activeTimeDiff = activeTime - preActiveTime;
+    idleTimeDiff = idleTime - preIdleTime[type];
+    activeTimeDiff = activeTime - preActiveTime[type];
 
     /* Store current idle and active time for next calculation */
-    preIdleTime = idleTime;
-    preActiveTime = activeTime;
+    preIdleTime[type] = idleTime;
+    preActiveTime[type] = activeTime;
 
     totalTime = idleTimeDiff + activeTimeDiff;
 
@@ -169,6 +188,21 @@
     return activePercValue;
 }
 
+auto readCPUUtilizationTotal([[maybe_unused]] const std::string& path)
+{
+    return readCPUUtilization(CPUUtilizationType::TOTAL);
+}
+
+auto readCPUUtilizationKernel([[maybe_unused]] const std::string& path)
+{
+    return readCPUUtilization(CPUUtilizationType::KERNEL);
+}
+
+auto readCPUUtilizationUser([[maybe_unused]] const std::string& path)
+{
+    return readCPUUtilization(CPUUtilizationType::USER);
+}
+
 double readMemoryUtilization([[maybe_unused]] const std::string& path)
 {
     /* Unused var: path */
@@ -283,9 +317,18 @@
 
 constexpr auto storage = "Storage";
 constexpr auto inode = "Inode";
-/** Map of read function for each health sensors supported */
+
+/** Map of read function for each health sensors supported
+ *
+ * The following health sensors are read in the ManagerDiagnosticData
+ * Redfish resource:
+ *  - CPU_Kernel populates ProcessorStatistics.KernelPercent
+ *  - CPU_User populates ProcessorStatistics.UserPercent
+ */
 const std::map<std::string, std::function<double(const std::string& path)>>
-    readSensors = {{"CPU", readCPUUtilization},
+    readSensors = {{"CPU", readCPUUtilizationTotal},
+                   {"CPU_Kernel", readCPUUtilizationKernel},
+                   {"CPU_User", readCPUUtilizationUser},
                    {"Memory", readMemoryUtilization},
                    {storage, readStorageUtilization},
                    {inode, readInodeUtilization}};
@@ -582,7 +625,6 @@
 {
 
     std::vector<HealthConfig> cfgs;
-    HealthConfig cfg;
     auto data = parseConfigFile(HEALTH_CONFIG_FILE);
 
     // print values