ml: use clock() to measure the CPU time.

clock() can give higher accuracy compared to
GetPlatformIndependentCPUUsage(). For example, on "eve" and
"nocturne", its resolution is 1e-6 second whereas
GetPlatformIndependentCPUUsage()'s is 1e-3 second.

BUG=chromium:1108264
TEST=on device, it can measure CPU usage less than 0.001.

Change-Id: I86dbf3312ca1648582c9d4bd3c18c99566a2824f
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform2/+/2315717
Reviewed-by: Andrew Moylan <amoylan@chromium.org>
Tested-by: Honglin Yu <honglinyu@chromium.org>
Commit-Queue: Honglin Yu <honglinyu@chromium.org>
diff --git a/ml/request_metrics.cc b/ml/request_metrics.cc
index 8678142..c66b06a 100644
--- a/ml/request_metrics.cc
+++ b/ml/request_metrics.cc
@@ -12,19 +12,19 @@
 
 using chromeos::machine_learning::mojom::LoadModelResult;
 
-RequestMetrics::RequestMetrics(
-    const std::string& model_name, const std::string& request_name)
+RequestMetrics::RequestMetrics(const std::string& model_name,
+                               const std::string& request_name)
     : name_base_(std::string(kGlobalMetricsPrefix) + model_name + "." +
                  request_name),
-      process_metrics_(nullptr) {}
+      initial_cpu_clock_(0),
+      initial_memory_(0), status_(Status::kNotStarted) {}
 
 void RequestMetrics::StartRecordingPerformanceMetrics() {
-  DCHECK(process_metrics_ == nullptr);
-  process_metrics_ = base::ProcessMetrics::CreateCurrentProcessMetrics();
-  // Call GetPlatformIndependentCPUUsage in order to set the "zero" point of the
-  // CPU usage counter of process_metrics_.
-  process_metrics_->GetPlatformIndependentCPUUsage();
-  timer_.Start();
+  DCHECK(status_ == Status::kNotStarted);
+  // Get initial CPU clock in order to set the "zero" point of the CPU usage
+  // counter.
+  initial_cpu_clock_ = std::clock();
+  DCHECK(initial_cpu_clock_ != static_cast<std::clock_t>(-1));
   // Query memory usage.
   size_t usage = 0;
   if (!GetTotalProcessMemoryUsage(&usage)) {
@@ -32,27 +32,16 @@
     return;
   }
   initial_memory_ = static_cast<int64_t>(usage);
+
+  status_ = Status::kRecording;
 }
 
 void RequestMetrics::FinishRecordingPerformanceMetrics() {
-  DCHECK(process_metrics_ != nullptr);
-  // To get CPU time, we multiply elapsed (wall) time by CPU usage percentage.
-  timer_.Stop();
-  base::TimeDelta elapsed_time;
-  DCHECK(timer_.GetElapsedTime(&elapsed_time));
-  const int64_t elapsed_time_microsec = elapsed_time.InMicroseconds();
-
-  // CPU usage, 12.34 means 12.34%, and the range is 0 to 100 * numCPUCores.
-  // That's to say it can exceed 100 when there're multi CPUs.
-  // For example, if the device has 4 CPUs and the process fully uses 2 of
-  // them, the percent will be 200%.
-  const double cpu_usage_percent =
-      process_metrics_->GetPlatformIndependentCPUUsage();
-
-  // CPU time, as mentioned above, "100 microseconds" means "1 CPU core fully
-  // utilized for 100 microseconds".
-  const int64_t cpu_time_microsec =
-      static_cast<int64_t>(cpu_usage_percent * elapsed_time_microsec / 100.);
+  DCHECK(status_ == Status::kRecording);
+  status_ = Status::kFinished;
+  // Get CPU time by `clock()`.
+  const int64_t cpu_time_microsec = static_cast<int64_t>(
+      (std::clock() - initial_cpu_clock_) * 1000000.0 / CLOCKS_PER_SEC);
 
   // Memory usage
   size_t usage = 0;
diff --git a/ml/request_metrics.h b/ml/request_metrics.h
index 622602e..42223e7 100644
--- a/ml/request_metrics.h
+++ b/ml/request_metrics.h
@@ -5,12 +5,11 @@
 #ifndef ML_REQUEST_METRICS_H_
 #define ML_REQUEST_METRICS_H_
 
+#include <ctime>
 #include <memory>
 #include <string>
 
 #include <base/macros.h>
-#include <base/process/process_metrics.h>
-#include <base/time/time.h>
 #include <metrics/metrics_library.h>
 
 #include "metrics/timer.h"
@@ -46,13 +45,30 @@
   void FinishRecordingPerformanceMetrics();
 
  private:
+  enum class Status {
+    kNotStarted = 0,
+    kRecording = 1,
+    kEventSent = 2,
+    kFinished = 3
+  };
+
   MetricsLibrary metrics_library_;
 
   const std::string name_base_;
-  std::unique_ptr<base::ProcessMetrics> process_metrics_;
-  chromeos_metrics::Timer timer_;
+  std::clock_t initial_cpu_clock_;
   int64_t initial_memory_;
 
+  // The `status_` serves as a safe guard to ensure the correct usage of the
+  // objects of this class, specifically,
+  // 1. `status_` is initialized to `kNotStarted` at construction.
+  // 2. `StartRecordingPerformanceMetrics()` must be called when
+  // `status_=kNotStarted`. And it sets `status_` to `kRecording`.
+  // 3. `RecordRequestEvent()` must be called when `status_=kRecording` or
+  // `status_=kFinished`, and it sets `status_` to `kEventSent`.
+  // 4. `FinishRecordingPerformanceMetrics()` must be called when
+  // `status_=kRecording` and it sets `status_` to `kEventFinished`.
+  Status status_;
+
   DISALLOW_COPY_AND_ASSIGN(RequestMetrics);
 };
 
@@ -72,10 +88,11 @@
 
 template <class RequestEventEnum>
 void RequestMetrics::RecordRequestEvent(RequestEventEnum event) {
+  DCHECK(status_ == Status::kRecording || status_ == Status::kFinished);
   metrics_library_.SendEnumToUMA(
       name_base_ + kEventSuffix, static_cast<int>(event),
       static_cast<int>(RequestEventEnum::kMaxValue) + 1);
-  process_metrics_.reset(nullptr);
+  status_ = Status::kEventSent;
 }
 
 // Records a generic model specification error event during a model loading