Merge "simpleperf: fix calculating cpu frequency."
diff --git a/simpleperf/cmd_debug_unwind_test.cpp b/simpleperf/cmd_debug_unwind_test.cpp
index 153adb7..7b99afa 100644
--- a/simpleperf/cmd_debug_unwind_test.cpp
+++ b/simpleperf/cmd_debug_unwind_test.cpp
@@ -34,48 +34,6 @@
   return CreateCommandInstance("debug-unwind");
 }
 
-class CaptureStdout {
- public:
-  CaptureStdout() : started_(false) {}
-
-  ~CaptureStdout() {
-    if (started_) {
-      Finish();
-    }
-  }
-
-  bool Start() {
-    fflush(stdout);
-    old_stdout_ = dup(STDOUT_FILENO);
-    if (old_stdout_ == -1) {
-      return false;
-    }
-    started_ = true;
-    tmpfile_.reset(new TemporaryFile);
-    if (dup2(tmpfile_->fd, STDOUT_FILENO) == -1) {
-      return false;
-    }
-    return true;
-  }
-
-  std::string Finish() {
-    fflush(stdout);
-    started_ = false;
-    dup2(old_stdout_, STDOUT_FILENO);
-    close(old_stdout_);
-    std::string s;
-    if (!android::base::ReadFileToString(tmpfile_->path, &s)) {
-      return "";
-    }
-    return s;
-  }
-
- private:
-  bool started_;
-  int old_stdout_;
-  std::unique_ptr<TemporaryFile> tmpfile_;
-};
-
 TEST(cmd_debug_unwind, smoke) {
   std::string input_data = GetTestData(PERF_DATA_NO_UNWIND);
   CaptureStdout capture;
diff --git a/simpleperf/cmd_stat.cpp b/simpleperf/cmd_stat.cpp
index 34b4436..6872a89 100644
--- a/simpleperf/cmd_stat.cpp
+++ b/simpleperf/cmd_stat.cpp
@@ -210,7 +210,11 @@
       return "";
     }
     if (s.type_name == "cpu-cycles") {
-      double hz = s.count / (duration_in_sec / s.scale);
+      double running_time_in_sec;
+      if (!FindRunningTimeForSummary(s, &running_time_in_sec)) {
+        return "";
+      }
+      double hz = s.count / (running_time_in_sec / s.scale);
       return android::base::StringPrintf("%lf%cGHz", hz / 1e9, sap_mid);
     }
     if (s.type_name == "instructions" && s.count != 0) {
@@ -247,7 +251,11 @@
         return android::base::StringPrintf("%f%%%cmiss rate", miss_rate * 100, sap_mid);
       }
     }
-    double rate = s.count / (duration_in_sec / s.scale);
+    double running_time_in_sec;
+    if (!FindRunningTimeForSummary(s, &running_time_in_sec)) {
+      return "";
+    }
+    double rate = s.count / (running_time_in_sec / s.scale);
     if (rate > 1e9) {
       return android::base::StringPrintf("%.3lf%cG/sec", rate / 1e9, sap_mid);
     }
@@ -260,6 +268,17 @@
     return android::base::StringPrintf("%.3lf%c/sec", rate, sap_mid);
   }
 
+  bool FindRunningTimeForSummary(const CounterSummary& summary, double* running_time_in_sec) {
+    for (auto& s : summaries_) {
+      if ((s.type_name == "task-clock" || s.type_name == "cpu-clock") &&
+          s.IsMonitoredAtTheSameTime(summary) && s.count != 0u) {
+        *running_time_in_sec = s.count / 1e9;
+        return true;
+      }
+    }
+    return false;
+  }
+
  private:
   std::vector<CounterSummary> summaries_;
   bool csv_;
diff --git a/simpleperf/cmd_stat_test.cpp b/simpleperf/cmd_stat_test.cpp
index b6896e1..ae3b036 100644
--- a/simpleperf/cmd_stat_test.cpp
+++ b/simpleperf/cmd_stat_test.cpp
@@ -18,6 +18,7 @@
 
 #include <android-base/file.h>
 #include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 #include <android-base/test_utils.h>
 
 #include <thread>
@@ -194,3 +195,27 @@
     ASSERT_EQ(attr.attr->freq, 0u);
   }
 }
+
+TEST(stat_cmd, calculating_cpu_frequency) {
+  CaptureStdout capture;
+  ASSERT_TRUE(capture.Start());
+  ASSERT_TRUE(StatCmd()->Run({"--csv", "--group", "task-clock,cpu-cycles", "sleep", "1"}));
+  std::string output = capture.Finish();
+  double task_clock_in_ms = 0;
+  uint64_t cpu_cycle_count = 0;
+  double cpu_frequency = 0;
+  for (auto& line : android::base::Split(output, "\n")) {
+    if (line.find("task-clock") != std::string::npos) {
+      ASSERT_EQ(sscanf(line.c_str(), "%lf(ms)", &task_clock_in_ms), 1);
+    } else if (line.find("cpu-cycles") != std::string::npos) {
+      ASSERT_EQ(sscanf(line.c_str(), "%" SCNu64 ",cpu-cycles,%lf", &cpu_cycle_count,
+                       &cpu_frequency), 2);
+    }
+  }
+  ASSERT_NE(task_clock_in_ms, 0.0f);
+  ASSERT_NE(cpu_cycle_count, 0u);
+  ASSERT_NE(cpu_frequency, 0.0f);
+  double calculated_frequency = cpu_cycle_count / task_clock_in_ms / 1e6;
+  // Accept error up to 1e-3. Because the stat cmd print values with precision 1e-6.
+  ASSERT_NEAR(cpu_frequency, calculated_frequency, 1e-3);
+}
diff --git a/simpleperf/test_util.h b/simpleperf/test_util.h
index f5fb590..8768d66 100644
--- a/simpleperf/test_util.h
+++ b/simpleperf/test_util.h
@@ -19,6 +19,9 @@
 #include <string>
 #include <vector>
 
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+
 #include "read_elf.h"
 #include "workload.h"
 
@@ -56,3 +59,45 @@
       return; \
     } \
   } while (0)
+
+class CaptureStdout {
+ public:
+  CaptureStdout() : started_(false) {}
+
+  ~CaptureStdout() {
+    if (started_) {
+      Finish();
+    }
+  }
+
+  bool Start() {
+    fflush(stdout);
+    old_stdout_ = dup(STDOUT_FILENO);
+    if (old_stdout_ == -1) {
+      return false;
+    }
+    started_ = true;
+    tmpfile_.reset(new TemporaryFile);
+    if (dup2(tmpfile_->fd, STDOUT_FILENO) == -1) {
+      return false;
+    }
+    return true;
+  }
+
+  std::string Finish() {
+    fflush(stdout);
+    started_ = false;
+    dup2(old_stdout_, STDOUT_FILENO);
+    close(old_stdout_);
+    std::string s;
+    if (!android::base::ReadFileToString(tmpfile_->path, &s)) {
+      return "";
+    }
+    return s;
+  }
+
+ private:
+  bool started_;
+  int old_stdout_;
+  std::unique_ptr<TemporaryFile> tmpfile_;
+};