szaszm commented on a change in pull request #1032:
URL: https://github.com/apache/nifi-minifi-cpp/pull/1032#discussion_r620348236



##########
File path: libminifi/src/utils/OsUtils.cpp
##########
@@ -164,43 +168,140 @@ std::string OsUtils::userIdToUsername(const std::string 
&uid) {
   return name;
 }
 
-uint64_t OsUtils::getMemoryUsage() {
-#ifdef __linux__
-  static const std::string linePrefix = "VmRSS:";
-  std::ifstream statusFile("/proc/self/status");
+int64_t OsUtils::getCurrentProcessPhysicalMemoryUsage() {
+#if defined(__linux__)
+  static const std::string resident_set_size_prefix = "VmRSS:";
+  std::ifstream status_file("/proc/self/status");
   std::string line;
 
-  while (std::getline(statusFile, line)) {
-    // if line begins with "VmRSS:"
-    if (line.rfind(linePrefix, 0) == 0) {
-      std::istringstream valuableLine(line.substr(linePrefix.length()));
-      uint64_t kByteValue;
-      valuableLine >> kByteValue;
-      return kByteValue * 1024;
+  while (std::getline(status_file, line)) {
+    if (line.rfind(resident_set_size_prefix, 0) == 0) {
+      std::istringstream 
resident_set_size_value(line.substr(resident_set_size_prefix.length()));
+      uint64_t memory_usage_in_kBytes;
+      resident_set_size_value >> memory_usage_in_kBytes;
+      return memory_usage_in_kBytes * 1024;
     }
   }
 
-  throw std::runtime_error("Could not get memory info for current process");
-#endif
-
-#ifdef __APPLE__
+  return -1;
+#elif defined(__APPLE__)
   task_basic_info tInfo;
   mach_msg_type_number_t tInfoCount = TASK_BASIC_INFO_COUNT;
   if (KERN_SUCCESS != task_info(mach_task_self(), TASK_BASIC_INFO, 
(task_info_t)&tInfo, &tInfoCount))
-    throw std::runtime_error("Could not get memory info for current process");
+    return -1;
   return tInfo.resident_size;
-#endif
-
-#ifdef _WIN32
+#elif defined(WIN32)
   PROCESS_MEMORY_COUNTERS pmc;
   if (!GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc)))
-    throw std::runtime_error("Could not get memory info for current process");
+    return -1;
   return pmc.WorkingSetSize;
+#else
+  static_assert(false, "Unsupported platform");

Review comment:
       I want to go back to using dummy values or non-fatal warnings on 
unsupported platforms, instead of intentionally making the compilation fail 
even if the agent would work otherwise.
   Aside from having to deal with manual cmake calls, there's not much stopping 
users to compile minifi c++ on non-linux POSIX platforms today, even though 
it's not tested.

##########
File path: libminifi/src/utils/SystemCPUUsageTracker.cpp
##########
@@ -0,0 +1,190 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "utils/SystemCPUUsageTracker.h"
+
+namespace org {
+namespace apache {
+namespace nifi {
+namespace minifi {
+namespace utils {
+#ifdef __linux__
+
+SystemCPUUsageTracker::SystemCPUUsageTracker() :
+    total_user_(0), previous_total_user_(0),
+    total_user_low_(0), previous_total_user_low_(0),
+    total_sys_(0), previous_total_sys_(0),
+    total_idle_(0), previous_total_idle_(0) {
+  queryCPUTimes();
+}
+
+double SystemCPUUsageTracker::getCPUUsageAndRestartCollection() {
+  queryCPUTimes();
+  if (isCurrentQuerySameAsPrevious() || isCurrentQueryOlderThanPrevious()) {
+    return -1.0;
+  } else {
+    return getCPUUsageBetweenLastTwoQueries();
+  }
+}
+
+void SystemCPUUsageTracker::queryCPUTimes() {
+  previous_total_user_ = total_user_;
+  previous_total_user_low_ = total_user_low_;
+  previous_total_sys_ = total_sys_;
+  previous_total_idle_ = total_idle_;
+  FILE* file = fopen("/proc/stat", "r");
+  if (fscanf(file, "cpu %lu %lu %lu %lu", &total_user_, &total_user_low_, 
&total_sys_, &total_idle_) != 4) {
+    total_user_ = previous_total_user_;
+    total_user_low_ = previous_total_user_low_;
+    total_idle_ = previous_total_idle_;
+    total_sys_ = previous_total_sys_;
+  }
+  fclose(file);
+}
+
+bool SystemCPUUsageTracker::isCurrentQueryOlderThanPrevious() const {
+  return (total_user_ < previous_total_user_ ||
+          total_user_low_ < previous_total_user_low_ ||
+          total_sys_ < previous_total_sys_ ||
+          total_idle_ < previous_total_idle_);
+}
+
+bool SystemCPUUsageTracker::isCurrentQuerySameAsPrevious() const {
+  return (total_user_ == previous_total_user_ &&
+          total_user_low_ == previous_total_user_low_ &&
+          total_sys_ == previous_total_sys_ &&
+          total_idle_ == previous_total_idle_);
+}
+
+double SystemCPUUsageTracker::getCPUUsageBetweenLastTwoQueries() const {
+  uint64_t total_user_diff = total_user_ - previous_total_user_;
+  uint64_t total_user_low_diff = total_user_low_ - previous_total_user_low_;
+  uint64_t total_system_diff = total_sys_ - previous_total_sys_;
+  uint64_t total_idle_diff = total_idle_ - previous_total_idle_;
+  uint64_t total_diff =  total_user_diff + total_user_low_diff + 
total_system_diff;
+  double percent = 
static_cast<double>(total_diff)/static_cast<double>(total_diff+total_idle_diff);

Review comment:
       If `queryCPUTimes` fails, then `total_* == previous_total_*` => 
`total_diff == 0 && total_idle_diff == 0` => division by zero.

##########
File path: libminifi/test/unit/MemoryUsageTest.cpp
##########
@@ -21,8 +21,29 @@
 #include "../TestBase.h"
 
 TEST_CASE("Test memory usage", "[testmemoryusage]") {
+  constexpr bool cout_enabled = true;
   std::vector<char> v(30000000);
-  const auto memoryUsage = utils::OsUtils::getMemoryUsage();
-  REQUIRE(memoryUsage > v.size());
-  REQUIRE(memoryUsage < 2 * v.size());
+  const auto RAMUsagebyProcess = 
utils::OsUtils::getCurrentProcessPhysicalMemoryUsage();
+  const auto RAMUsagebySystem = utils::OsUtils::getSystemPhysicalMemoryUsage();
+  const auto RAMTotal = utils::OsUtils::getSystemTotalPhysicalMemory();
+
+  if (cout_enabled) {
+    std::cout << "Physical Memory used by this process: " << RAMUsagebyProcess 
 << " bytes" << std::endl;
+    std::cout << "Physical Memory used by the system: " << RAMUsagebySystem << 
" bytes" << std::endl;
+    std::cout << "Total Physical Memory in the system: " << RAMTotal << " 
bytes" << std::endl;
+  }
+  REQUIRE(RAMUsagebyProcess >= v.size());
+  REQUIRE(v.size()*2 >= RAMUsagebyProcess);
+  REQUIRE(RAMUsagebySystem >= RAMUsagebyProcess);
+  REQUIRE(RAMTotal >= RAMUsagebySystem);
+}
+
+#ifndef WIN32
+size_t GetTotalMemoryLegacy() {
+  return (size_t) sysconf(_SC_PHYS_PAGES) * (size_t) sysconf(_SC_PAGESIZE);

Review comment:
       C-style cast, the linter should reject this. In any case, use 
`static_cast`. 
https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es49-if-you-must-use-a-cast-use-a-named-cast

##########
File path: libminifi/src/utils/OsUtils.cpp
##########
@@ -164,43 +168,140 @@ std::string OsUtils::userIdToUsername(const std::string 
&uid) {
   return name;
 }
 
-uint64_t OsUtils::getMemoryUsage() {
-#ifdef __linux__
-  static const std::string linePrefix = "VmRSS:";
-  std::ifstream statusFile("/proc/self/status");
+int64_t OsUtils::getCurrentProcessPhysicalMemoryUsage() {
+#if defined(__linux__)
+  static const std::string resident_set_size_prefix = "VmRSS:";
+  std::ifstream status_file("/proc/self/status");
   std::string line;
 
-  while (std::getline(statusFile, line)) {
-    // if line begins with "VmRSS:"
-    if (line.rfind(linePrefix, 0) == 0) {
-      std::istringstream valuableLine(line.substr(linePrefix.length()));
-      uint64_t kByteValue;
-      valuableLine >> kByteValue;
-      return kByteValue * 1024;
+  while (std::getline(status_file, line)) {
+    if (line.rfind(resident_set_size_prefix, 0) == 0) {
+      std::istringstream 
resident_set_size_value(line.substr(resident_set_size_prefix.length()));
+      uint64_t memory_usage_in_kBytes;
+      resident_set_size_value >> memory_usage_in_kBytes;
+      return memory_usage_in_kBytes * 1024;
     }
   }
 
-  throw std::runtime_error("Could not get memory info for current process");
-#endif
-
-#ifdef __APPLE__
+  return -1;
+#elif defined(__APPLE__)
   task_basic_info tInfo;
   mach_msg_type_number_t tInfoCount = TASK_BASIC_INFO_COUNT;
   if (KERN_SUCCESS != task_info(mach_task_self(), TASK_BASIC_INFO, 
(task_info_t)&tInfo, &tInfoCount))
-    throw std::runtime_error("Could not get memory info for current process");
+    return -1;
   return tInfo.resident_size;
-#endif
-
-#ifdef _WIN32
+#elif defined(WIN32)
   PROCESS_MEMORY_COUNTERS pmc;
   if (!GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc)))
-    throw std::runtime_error("Could not get memory info for current process");
+    return -1;
   return pmc.WorkingSetSize;
+#else
+  static_assert(false, "Unsupported platform");
 #endif
+}
+
+int64_t OsUtils::getSystemPhysicalMemoryUsage() {
+#if defined(__linux__)
+  const std::string available_memory_prefix = "MemAvailable:";
+  const std::string total_memory_prefix = "MemTotal:";
+  std::ifstream meminfo_file("/proc/meminfo");
+  std::string line;
 
-  throw std::runtime_error("getMemoryUsage() is not implemented for this 
platform");
+  minifi::utils::optional<uint64_t> total_memory_kByte;
+  minifi::utils::optional<uint64_t> available_memory_kByte;
+  while ((!total_memory_kByte.has_value() || 
!available_memory_kByte.has_value()) && std::getline(meminfo_file, line)) {
+    if (line.rfind(total_memory_prefix, 0) == 0) {
+      std::istringstream 
total_memory_line(line.substr(total_memory_prefix.length()));
+      total_memory_kByte.emplace(0);
+      total_memory_line >> total_memory_kByte.value();
+    } else if (line.rfind(available_memory_prefix, 0) == 0) {
+      std::istringstream 
available_memory_line(line.substr(available_memory_prefix.length()));
+      available_memory_kByte.emplace(0);
+      available_memory_line >> available_memory_kByte.value();
+    }
+  }
+  if (total_memory_kByte.has_value() && available_memory_kByte.has_value())
+    return (total_memory_kByte.value() - available_memory_kByte.value()) * 
1024;
+
+  return -1;
+#elif defined(__APPLE__)
+  vm_size_t page_size;
+  mach_port_t mach_port = mach_host_self();
+  vm_statistics64_data_t vm_stats;
+  mach_msg_type_number_t count = sizeof(vm_stats) / sizeof(natural_t);
+  if (KERN_SUCCESS == host_page_size(mach_port, &page_size) &&
+      KERN_SUCCESS == host_statistics64(mach_port, HOST_VM_INFO,
+                                      (host_info64_t)&vm_stats, &count)) {
+      uint64_t physical_memory_used = ((int64_t)vm_stats.active_count +
+                               (int64_t)vm_stats.wire_count) *  
(int64_t)page_size;
+      return physical_memory_used;
+  }
+  return -1;
+#elif defined(WIN32)
+  MEMORYSTATUSEX memory_info;
+  memory_info.dwLength = sizeof(MEMORYSTATUSEX);
+  GlobalMemoryStatusEx(&memory_info);
+  DWORDLONG physical_memory_used = memory_info.ullTotalPhys - 
memory_info.ullAvailPhys;
+  return physical_memory_used;
+#else
+  static_assert(false, "Unsupported platform");
+#endif
+}
+
+int64_t OsUtils::getSystemTotalPhysicalMemory() {
+#if defined(__linux__)
+  struct sysinfo memory_info;
+  sysinfo(&memory_info);
+  uint64_t total_physical_memory = memory_info.totalram;
+  total_physical_memory *= memory_info.mem_unit;
+  return total_physical_memory;
+#elif defined(__APPLE__)
+  int mib[2];
+  int64_t total_physical_memory = 0;
+  mib[0] = CTL_HW;
+  mib[1] = HW_MEMSIZE;
+  size_t length = sizeof(int64_t);
+  sysctl(mib, 2, &total_physical_memory, &length, NULL, 0);
+  return total_physical_memory;
+#elif defined(WIN32)
+  MEMORYSTATUSEX memory_info;
+  memory_info.dwLength = sizeof(MEMORYSTATUSEX);
+  GlobalMemoryStatusEx(&memory_info);
+  DWORDLONG total_physical_memory = memory_info.ullTotalPhys;
+  return total_physical_memory;
+#else
+  static_assert(false, "Unsupported platform");
+#endif
 }
 
+std::string OsUtils::getMachineArchitecture() {
+#if defined(__linux__) || defined(__APPLE__)

Review comment:
       This could be in the else branch of `#if defined(WIN32)`. BSDs have 
uname, too.

##########
File path: libminifi/test/unit/ResponseNodeValueTests.cpp
##########
@@ -0,0 +1,187 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <memory>
+
+#include "../../include/core/state/Value.h"
+#include "../TestBase.h"
+
+template <class T>
+bool canConvertToType(org::apache::nifi::minifi::state::response::ValueNode 
value_node) {
+  T conversion_target;
+  return value_node.getValue()->convertValue(conversion_target);
+}
+
+template <class T>
+bool canConvertToType(org::apache::nifi::minifi::state::response::ValueNode 
value_node, const T& expected_result) {
+  T conversion_target;
+  bool canConvert = value_node.getValue()->convertValue(conversion_target);
+  return canConvert && (expected_result == conversion_target);
+}
+
+TEST_CASE("IntValueNodeTests", "[responsenodevaluetests]") {
+  org::apache::nifi::minifi::state::response::ValueNode value_node;
+
+  int positive_int_value = 6;
+  value_node = positive_int_value;
+  REQUIRE(value_node.getValue()->getTypeIndex() == 
org::apache::nifi::minifi::state::response::Value::INT_TYPE);
+  REQUIRE(canConvertToType<uint32_t>(value_node));
+  REQUIRE(canConvertToType<uint64_t>(value_node));
+  REQUIRE(canConvertToType<int32_t>(value_node));
+  REQUIRE(canConvertToType<int64_t>(value_node));
+  REQUIRE(canConvertToType<int> (value_node));
+  REQUIRE_FALSE(canConvertToType<bool>(value_node));
+  REQUIRE(canConvertToType<double>(value_node));
+  REQUIRE(value_node.to_string() == "6");
+
+  int negative_int_value = -7;
+  value_node = negative_int_value;
+  REQUIRE(value_node.getValue()->getTypeIndex() == 
org::apache::nifi::minifi::state::response::Value::INT_TYPE);
+  REQUIRE_FALSE(canConvertToType<uint32_t>(value_node));
+  REQUIRE_FALSE(canConvertToType<uint64_t>(value_node));
+  REQUIRE(canConvertToType<int32_t>(value_node));
+  REQUIRE(canConvertToType<int64_t>(value_node));
+  REQUIRE(canConvertToType<int> (value_node));
+  REQUIRE_FALSE(canConvertToType<bool>(value_node));
+  REQUIRE(canConvertToType<double>(value_node));
+  REQUIRE(value_node.to_string() == "-7");
+
+  uint32_t max_uint32_value = (uint64_t{1} << 32) - 1;

Review comment:
       Implicit narrowing here. I would convert the initializer to either 
`std::numeric_limits<uint32_t>::max()` (preferred), `static_cast<uint32_t>(-1)` 
or `~uint32_t{0}` to avoid the conversion altogether.

##########
File path: libminifi/src/utils/ProcessCPUUsageTracker.cpp
##########
@@ -0,0 +1,140 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "utils/ProcessCPUUsageTracker.h"
+
+#if defined(__linux__) || defined(__APPLE__)
+#include <sys/times.h>
+#endif

Review comment:
       `times` is POSIX. Since we have no clear macro check for posix, either 
`!WIN32` or `__unix__` is fine for me.

##########
File path: libminifi/test/unit/ResponseNodeValueTests.cpp
##########
@@ -0,0 +1,187 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <memory>
+
+#include "../../include/core/state/Value.h"
+#include "../TestBase.h"
+
+template <class T>
+bool canConvertToType(org::apache::nifi::minifi::state::response::ValueNode 
value_node) {
+  T conversion_target;
+  return value_node.getValue()->convertValue(conversion_target);
+}
+
+template <class T>
+bool canConvertToType(org::apache::nifi::minifi::state::response::ValueNode 
value_node, const T& expected_result) {
+  T conversion_target;
+  bool canConvert = value_node.getValue()->convertValue(conversion_target);
+  return canConvert && (expected_result == conversion_target);
+}
+
+TEST_CASE("IntValueNodeTests", "[responsenodevaluetests]") {
+  org::apache::nifi::minifi::state::response::ValueNode value_node;

Review comment:
       This file could use a type alias and maybe a handful of namespace 
aliases.

##########
File path: libminifi/src/utils/SystemCPUUsageTracker.cpp
##########
@@ -0,0 +1,190 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "utils/SystemCPUUsageTracker.h"
+
+namespace org {
+namespace apache {
+namespace nifi {
+namespace minifi {
+namespace utils {
+#ifdef __linux__
+
+SystemCPUUsageTracker::SystemCPUUsageTracker() :
+    total_user_(0), previous_total_user_(0),
+    total_user_low_(0), previous_total_user_low_(0),
+    total_sys_(0), previous_total_sys_(0),
+    total_idle_(0), previous_total_idle_(0) {
+  queryCPUTimes();
+}
+
+double SystemCPUUsageTracker::getCPUUsageAndRestartCollection() {
+  queryCPUTimes();
+  if (isCurrentQuerySameAsPrevious() || isCurrentQueryOlderThanPrevious()) {
+    return -1.0;
+  } else {
+    return getCPUUsageBetweenLastTwoQueries();
+  }
+}
+
+void SystemCPUUsageTracker::queryCPUTimes() {

Review comment:
       Minor, but I prefer to capitalize abbreviations as single words, simply 
because this allows one to programmatically convert between casing styles, e.g. 
`XmlParser` could easily be converted to `xml_parser`, but `XMLParser` would 
likely end up being `x_m_l_parser`, and even with smarter heuristics, 
programmatic approaches would fail at `SOAPXMLRequest` or `XMLRPCRequest`. The 
google style guide happens to agree with me [1], but I leave it up to you 
whether you want to change it or not.
   
   [1] https://google.github.io/styleguide/cppguide.html#General_Naming_Rules
   > For the purposes of the naming rules below, a "word" is anything that you 
would write in English without internal spaces. This includes abbreviations, 
such as acronyms and initialisms. For names written in mixed case (also 
sometimes referred to as "camel case" or "Pascal case"), in which the first 
letter of each word is capitalized, prefer to capitalize abbreviations as 
single words, e.g., `StartRpc()` rather than `StartRPC()`.

##########
File path: libminifi/src/utils/SystemCPUUsageTracker.cpp
##########
@@ -0,0 +1,190 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "utils/SystemCPUUsageTracker.h"
+
+namespace org {
+namespace apache {
+namespace nifi {
+namespace minifi {
+namespace utils {
+#ifdef __linux__
+
+SystemCPUUsageTracker::SystemCPUUsageTracker() :
+    total_user_(0), previous_total_user_(0),
+    total_user_low_(0), previous_total_user_low_(0),
+    total_sys_(0), previous_total_sys_(0),
+    total_idle_(0), previous_total_idle_(0) {
+  queryCPUTimes();
+}
+
+double SystemCPUUsageTracker::getCPUUsageAndRestartCollection() {
+  queryCPUTimes();
+  if (isCurrentQuerySameAsPrevious() || isCurrentQueryOlderThanPrevious()) {
+    return -1.0;
+  } else {
+    return getCPUUsageBetweenLastTwoQueries();
+  }
+}
+
+void SystemCPUUsageTracker::queryCPUTimes() {
+  previous_total_user_ = total_user_;
+  previous_total_user_low_ = total_user_low_;
+  previous_total_sys_ = total_sys_;
+  previous_total_idle_ = total_idle_;
+  FILE* file = fopen("/proc/stat", "r");
+  if (fscanf(file, "cpu %lu %lu %lu %lu", &total_user_, &total_user_low_, 
&total_sys_, &total_idle_) != 4) {
+    total_user_ = previous_total_user_;
+    total_user_low_ = previous_total_user_low_;
+    total_idle_ = previous_total_idle_;
+    total_sys_ = previous_total_sys_;
+  }
+  fclose(file);
+}
+
+bool SystemCPUUsageTracker::isCurrentQueryOlderThanPrevious() const {
+  return (total_user_ < previous_total_user_ ||
+          total_user_low_ < previous_total_user_low_ ||
+          total_sys_ < previous_total_sys_ ||
+          total_idle_ < previous_total_idle_);
+}
+
+bool SystemCPUUsageTracker::isCurrentQuerySameAsPrevious() const {
+  return (total_user_ == previous_total_user_ &&
+          total_user_low_ == previous_total_user_low_ &&
+          total_sys_ == previous_total_sys_ &&
+          total_idle_ == previous_total_idle_);
+}
+
+double SystemCPUUsageTracker::getCPUUsageBetweenLastTwoQueries() const {
+  uint64_t total_user_diff = total_user_ - previous_total_user_;
+  uint64_t total_user_low_diff = total_user_low_ - previous_total_user_low_;
+  uint64_t total_system_diff = total_sys_ - previous_total_sys_;
+  uint64_t total_idle_diff = total_idle_ - previous_total_idle_;
+  uint64_t total_diff =  total_user_diff + total_user_low_diff + 
total_system_diff;
+  double percent = 
static_cast<double>(total_diff)/static_cast<double>(total_diff+total_idle_diff);
+
+  return percent;
+}
+#endif  // linux
+
+#ifdef WIN32
+SystemCPUUsageTracker::SystemCPUUsageTracker() :
+    total_idle_(0), total_sys_(0), total_user_(0),
+    previous_total_idle_(0), previous_total_sys_(0), previous_total_user_(0) {
+  queryCPUTimes();
+}
+
+double SystemCPUUsageTracker::getCPUUsageAndRestartCollection() {
+  queryCPUTimes();
+  if (isCurrentQuerySameAsPrevious() || isCurrentQueryOlderThanPrevious()) {
+    return -1.0;
+  } else {
+    return getCPUUsageBetweenLastTwoQueries();
+  }
+}
+
+void SystemCPUUsageTracker::queryCPUTimes() {
+  previous_total_user_ = total_user_;
+  previous_total_sys_ = total_sys_;
+  previous_total_idle_ = total_idle_;
+  FILETIME fidle, fsys, fuser;
+  GetSystemTimes(&fidle, &fsys, &fuser);
+  total_user_ = ULARGE_INTEGER{ fuser.dwLowDateTime, fuser.dwHighDateTime 
}.QuadPart;
+  total_sys_ = ULARGE_INTEGER{ fsys.dwLowDateTime, fsys.dwHighDateTime 
}.QuadPart;
+  total_idle_ = ULARGE_INTEGER{ fidle.dwLowDateTime, fidle.dwHighDateTime 
}.QuadPart;
+}
+
+bool SystemCPUUsageTracker::isCurrentQueryOlderThanPrevious() const {
+  return (total_user_ < previous_total_user_ ||
+    total_sys_ < previous_total_sys_ ||
+    total_idle_ < previous_total_idle_);
+}
+
+bool SystemCPUUsageTracker::isCurrentQuerySameAsPrevious() const {
+  return (total_user_ == previous_total_user_ &&
+    total_sys_ == previous_total_sys_ &&
+    total_idle_ == previous_total_idle_);
+}
+
+double SystemCPUUsageTracker::getCPUUsageBetweenLastTwoQueries() const {
+  uint64_t total_user_diff = total_user_ - previous_total_user_;
+  uint64_t total_sys_diff = total_sys_ - previous_total_sys_;
+  uint64_t total_idle_diff = total_idle_ - previous_total_idle_;
+  uint64_t total_diff = total_user_diff + total_sys_diff;
+  double percent = static_cast<double>(total_diff - total_idle_diff) / 
static_cast<double>(total_diff);
+
+  return percent;
+}
+#endif  // windows
+
+#ifdef __APPLE__
+SystemCPUUsageTracker::SystemCPUUsageTracker() :
+    total_ticks_(0), previous_total_ticks_(0),
+    idle_ticks_(0), previous_idle_ticks_(0) {
+  queryCPUTicks();
+}
+
+double SystemCPUUsageTracker::getCPUUsageAndRestartCollection() {
+  queryCPUTicks();
+  if (isCurrentQuerySameAsPrevious() || isCurrentQueryOlderThanPrevious()) {
+    return -1.0;
+  } else {
+    return getCPUUsageBetweenLastTwoQueries();
+  }
+}
+
+void SystemCPUUsageTracker::queryCPUTicks() {
+  host_cpu_load_info_data_t cpuinfo;
+  mach_msg_type_number_t count = HOST_CPU_LOAD_INFO_COUNT;
+  auto query_result = host_statistics(mach_host_self(), HOST_CPU_LOAD_INFO, 
(host_info_t)&cpuinfo, &count);
+  if (query_result == KERN_SUCCESS) {
+    previous_total_ticks_ = total_ticks_;
+    previous_idle_ticks_ = idle_ticks_;
+    total_ticks_ = 0;
+    for (int i = 0; i < CPU_STATE_MAX; i++) {
+      total_ticks_ += cpuinfo.cpu_ticks[i];
+    }
+    idle_ticks_ = cpuinfo.cpu_ticks[CPU_STATE_IDLE];
+  }

Review comment:
       I can't verify this logic because it's incredibly hard to find proper 
documentation for 
[`host_statistics`](https://developer.apple.com/documentation/kernel/1502546-host_statistics).
 Could you explain how you came up with the solution?




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
[email protected]


Reply via email to