Merge "simpleperf: add whitelist to omit tests requiring hw counters."
diff --git a/simpleperf/cmd_record_test.cpp b/simpleperf/cmd_record_test.cpp
index 4ef591f..fa4bab9 100644
--- a/simpleperf/cmd_record_test.cpp
+++ b/simpleperf/cmd_record_test.cpp
@@ -57,9 +57,13 @@
   return RecordCmd()->Run(v);
 }
 
-TEST(record_cmd, no_options) { ASSERT_TRUE(RunRecordCmd({})); }
+TEST(record_cmd, no_options) {
+  TEST_REQUIRE_HW_COUNTER();
+  ASSERT_TRUE(RunRecordCmd({}));
+}
 
 TEST(record_cmd, system_wide_option) {
+  TEST_REQUIRE_HW_COUNTER();
   TEST_IN_ROOT(ASSERT_TRUE(RunRecordCmd({"-a"})));
 }
 
@@ -86,6 +90,7 @@
 }
 
 TEST(record_cmd, sample_period_option) {
+  TEST_REQUIRE_HW_COUNTER();
   TemporaryFile tmpfile;
   ASSERT_TRUE(RunRecordCmd({"-c", "100000"}, tmpfile.path));
   CheckEventType(tmpfile.path, "cpu-cycles", 100000u, 0);
@@ -96,6 +101,7 @@
 }
 
 TEST(record_cmd, freq_option) {
+  TEST_REQUIRE_HW_COUNTER();
   TemporaryFile tmpfile;
   ASSERT_TRUE(RunRecordCmd({"-f", "99"}, tmpfile.path));
   CheckEventType(tmpfile.path, "cpu-cycles", 0, 99u);
@@ -105,6 +111,7 @@
 }
 
 TEST(record_cmd, multiple_freq_or_sample_period_option) {
+  TEST_REQUIRE_HW_COUNTER();
   TemporaryFile tmpfile;
   ASSERT_TRUE(RunRecordCmd({"-f", "99", "-e", "cpu-cycles", "-c", "1000000", "-e",
                             "cpu-clock"}, tmpfile.path));
@@ -113,11 +120,13 @@
 }
 
 TEST(record_cmd, output_file_option) {
+  TEST_REQUIRE_HW_COUNTER();
   TemporaryFile tmpfile;
   ASSERT_TRUE(RecordCmd()->Run({"-o", tmpfile.path, "sleep", SLEEP_SEC}));
 }
 
 TEST(record_cmd, dump_kernel_mmap) {
+  TEST_REQUIRE_HW_COUNTER();
   TemporaryFile tmpfile;
   ASSERT_TRUE(RunRecordCmd({}, tmpfile.path));
   std::unique_ptr<RecordFileReader> reader =
@@ -141,6 +150,7 @@
 }
 
 TEST(record_cmd, dump_build_id_feature) {
+  TEST_REQUIRE_HW_COUNTER();
   TemporaryFile tmpfile;
   ASSERT_TRUE(RunRecordCmd({}, tmpfile.path));
   std::unique_ptr<RecordFileReader> reader =
@@ -157,6 +167,7 @@
 }
 
 TEST(record_cmd, rN_event) {
+  TEST_REQUIRE_HW_COUNTER();
   OMIT_TEST_ON_NON_NATIVE_ABIS();
   size_t event_number;
   if (GetBuildArch() == ARCH_ARM64 || GetBuildArch() == ARCH_ARM) {
@@ -183,6 +194,7 @@
 }
 
 TEST(record_cmd, branch_sampling) {
+  TEST_REQUIRE_HW_COUNTER();
   if (IsBranchSamplingSupported()) {
     ASSERT_TRUE(RunRecordCmd({"-b"}));
     ASSERT_TRUE(RunRecordCmd({"-j", "any,any_call,any_ret,ind_call"}));
@@ -196,14 +208,17 @@
 }
 
 TEST(record_cmd, event_modifier) {
+  TEST_REQUIRE_HW_COUNTER();
   ASSERT_TRUE(RunRecordCmd({"-e", "cpu-cycles:u"}));
 }
 
 TEST(record_cmd, fp_callchain_sampling) {
+  TEST_REQUIRE_HW_COUNTER();
   ASSERT_TRUE(RunRecordCmd({"--call-graph", "fp"}));
 }
 
 TEST(record_cmd, fp_callchain_sampling_warning_on_arm) {
+  TEST_REQUIRE_HW_COUNTER();
   if (GetBuildArch() != ARCH_ARM) {
     GTEST_LOG_(INFO) << "This test does nothing as it only tests on arm arch.";
     return;
@@ -216,6 +231,7 @@
 }
 
 TEST(record_cmd, system_wide_fp_callchain_sampling) {
+  TEST_REQUIRE_HW_COUNTER();
   TEST_IN_ROOT(ASSERT_TRUE(RunRecordCmd({"-a", "--call-graph", "fp"})));
 }
 
@@ -242,7 +258,25 @@
   return in_native_abi == 1;
 }
 
+bool HasHardwareCounter() {
+  static int has_hw_counter = -1;
+  if (has_hw_counter == -1) {
+    has_hw_counter = 1;
+#if defined(__arm__)
+    std::string cpu_info;
+    if (android::base::ReadFileToString("/proc/cpuinfo", &cpu_info)) {
+      std::string hardware = GetHardwareFromCpuInfo(cpu_info);
+      if (std::regex_search(hardware, std::regex(R"(i\.MX6.*Quad)"))) {
+        has_hw_counter = 0;
+      }
+    }
+#endif
+  }
+  return has_hw_counter == 1;
+}
+
 TEST(record_cmd, dwarf_callchain_sampling) {
+  TEST_REQUIRE_HW_COUNTER();
   OMIT_TEST_ON_NON_NATIVE_ABIS();
   ASSERT_TRUE(IsDwarfCallChainSamplingSupported());
   std::vector<std::unique_ptr<Workload>> workloads;
@@ -255,12 +289,14 @@
 }
 
 TEST(record_cmd, system_wide_dwarf_callchain_sampling) {
+  TEST_REQUIRE_HW_COUNTER();
   OMIT_TEST_ON_NON_NATIVE_ABIS();
   ASSERT_TRUE(IsDwarfCallChainSamplingSupported());
   TEST_IN_ROOT(RunRecordCmd({"-a", "--call-graph", "dwarf"}));
 }
 
 TEST(record_cmd, no_unwind_option) {
+  TEST_REQUIRE_HW_COUNTER();
   OMIT_TEST_ON_NON_NATIVE_ABIS();
   ASSERT_TRUE(IsDwarfCallChainSamplingSupported());
   ASSERT_TRUE(RunRecordCmd({"--call-graph", "dwarf", "--no-unwind"}));
@@ -268,6 +304,7 @@
 }
 
 TEST(record_cmd, post_unwind_option) {
+  TEST_REQUIRE_HW_COUNTER();
   OMIT_TEST_ON_NON_NATIVE_ABIS();
   ASSERT_TRUE(IsDwarfCallChainSamplingSupported());
   std::vector<std::unique_ptr<Workload>> workloads;
@@ -279,6 +316,7 @@
 }
 
 TEST(record_cmd, existing_processes) {
+  TEST_REQUIRE_HW_COUNTER();
   std::vector<std::unique_ptr<Workload>> workloads;
   CreateProcesses(2, &workloads);
   std::string pid_list = android::base::StringPrintf(
@@ -287,6 +325,7 @@
 }
 
 TEST(record_cmd, existing_threads) {
+  TEST_REQUIRE_HW_COUNTER();
   std::vector<std::unique_ptr<Workload>> workloads;
   CreateProcesses(2, &workloads);
   // Process id can also be used as thread id in linux.
@@ -296,6 +335,7 @@
 }
 
 TEST(record_cmd, no_monitored_threads) {
+  TEST_REQUIRE_HW_COUNTER();
   ScopedAppPackageName scoped_package_name("");
   TemporaryFile tmpfile;
   ASSERT_FALSE(RecordCmd()->Run({"-o", tmpfile.path}));
@@ -303,11 +343,13 @@
 }
 
 TEST(record_cmd, more_than_one_event_types) {
+  TEST_REQUIRE_HW_COUNTER();
   ASSERT_TRUE(RunRecordCmd({"-e", "cpu-cycles,cpu-clock"}));
   ASSERT_TRUE(RunRecordCmd({"-e", "cpu-cycles", "-e", "cpu-clock"}));
 }
 
 TEST(record_cmd, mmap_page_option) {
+  TEST_REQUIRE_HW_COUNTER();
   ASSERT_TRUE(RunRecordCmd({"-m", "1"}));
   ASSERT_FALSE(RunRecordCmd({"-m", "0"}));
   ASSERT_FALSE(RunRecordCmd({"-m", "7"}));
@@ -332,6 +374,7 @@
 }
 
 TEST(record_cmd, kernel_symbol) {
+  TEST_REQUIRE_HW_COUNTER();
   TemporaryFile tmpfile;
   ASSERT_TRUE(RunRecordCmd({"--no-dump-symbols"}, tmpfile.path));
   bool success;
@@ -383,6 +426,7 @@
 }
 
 TEST(record_cmd, no_dump_symbols) {
+  TEST_REQUIRE_HW_COUNTER();
   TemporaryFile tmpfile;
   ASSERT_TRUE(RunRecordCmd({}, tmpfile.path));
   bool success;
@@ -405,6 +449,7 @@
 }
 
 TEST(record_cmd, dump_kernel_symbols) {
+  TEST_REQUIRE_HW_COUNTER();
   if (!IsRoot()) {
     GTEST_LOG_(INFO) << "Test requires root privilege";
     return;
@@ -432,15 +477,20 @@
 }
 
 TEST(record_cmd, group_option) {
+  TEST_REQUIRE_HW_COUNTER();
   ASSERT_TRUE(RunRecordCmd({"--group", "cpu-cycles,cpu-clock", "-m", "16"}));
   ASSERT_TRUE(RunRecordCmd({"--group", "cpu-cycles,cpu-clock", "--group",
                             "cpu-cycles:u,cpu-clock:u", "--group",
                             "cpu-cycles:k,cpu-clock:k", "-m", "16"}));
 }
 
-TEST(record_cmd, symfs_option) { ASSERT_TRUE(RunRecordCmd({"--symfs", "/"})); }
+TEST(record_cmd, symfs_option) {
+  TEST_REQUIRE_HW_COUNTER();
+  ASSERT_TRUE(RunRecordCmd({"--symfs", "/"}));
+}
 
 TEST(record_cmd, duration_option) {
+  TEST_REQUIRE_HW_COUNTER();
   TemporaryFile tmpfile;
   ASSERT_TRUE(RecordCmd()->Run({"--duration", "1.2", "-p",
                                 std::to_string(getpid()), "-o", tmpfile.path, "--in-app"}));
@@ -458,6 +508,7 @@
 }
 
 TEST(record_cmd, handle_SIGHUP) {
+  TEST_REQUIRE_HW_COUNTER();
   TemporaryFile tmpfile;
   int pipefd[2];
   ASSERT_EQ(0, pipe(pipefd));
@@ -477,6 +528,7 @@
 }
 
 TEST(record_cmd, stop_when_no_more_targets) {
+  TEST_REQUIRE_HW_COUNTER();
   TemporaryFile tmpfile;
   std::atomic<int> tid(0);
   std::thread thread([&]() {
@@ -489,6 +541,7 @@
 }
 
 TEST(record_cmd, donot_stop_when_having_targets) {
+  TEST_REQUIRE_HW_COUNTER();
   std::vector<std::unique_ptr<Workload>> workloads;
   CreateProcesses(1, &workloads);
   std::string pid = std::to_string(workloads[0]->GetPid());
@@ -500,6 +553,7 @@
 }
 
 TEST(record_cmd, start_profiling_fd_option) {
+  TEST_REQUIRE_HW_COUNTER();
   int pipefd[2];
   ASSERT_EQ(0, pipe(pipefd));
   int read_fd = pipefd[0];
@@ -518,6 +572,7 @@
 }
 
 TEST(record_cmd, record_meta_info_feature) {
+  TEST_REQUIRE_HW_COUNTER();
   TemporaryFile tmpfile;
   ASSERT_TRUE(RunRecordCmd({}, tmpfile.path));
   std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(tmpfile.path);
@@ -543,6 +598,7 @@
 }
 
 TEST(record_cmd, dump_regs_for_tracepoint_events) {
+  TEST_REQUIRE_HW_COUNTER();
   TEST_REQUIRE_HOST_ROOT();
   OMIT_TEST_ON_NON_NATIVE_ABIS();
   // Check if the kernel can dump registers for tracepoint events.
@@ -552,6 +608,7 @@
 }
 
 TEST(record_cmd, trace_offcpu_option) {
+  TEST_REQUIRE_HW_COUNTER();
   // On linux host, we need root privilege to read tracepoint events.
   TEST_REQUIRE_HOST_ROOT();
   OMIT_TEST_ON_NON_NATIVE_ABIS();
@@ -566,10 +623,12 @@
 }
 
 TEST(record_cmd, exit_with_parent_option) {
+  TEST_REQUIRE_HW_COUNTER();
   ASSERT_TRUE(RunRecordCmd({"--exit-with-parent"}));
 }
 
 TEST(record_cmd, clockid_option) {
+  TEST_REQUIRE_HW_COUNTER();
   if (!IsSettingClockIdSupported()) {
     ASSERT_FALSE(RunRecordCmd({"--clockid", "monotonic"}));
   } else {
@@ -584,6 +643,7 @@
 }
 
 TEST(record_cmd, generate_samples_by_hw_counters) {
+  TEST_REQUIRE_HW_COUNTER();
   std::vector<std::string> events = {"cpu-cycles", "instructions"};
   for (auto& event : events) {
     TemporaryFile tmpfile;
@@ -602,16 +662,19 @@
 }
 
 TEST(record_cmd, callchain_joiner_options) {
+  TEST_REQUIRE_HW_COUNTER();
   ASSERT_TRUE(RunRecordCmd({"--no-callchain-joiner"}));
   ASSERT_TRUE(RunRecordCmd({"--callchain-joiner-min-matching-nodes", "2"}));
 }
 
 TEST(record_cmd, dashdash) {
+  TEST_REQUIRE_HW_COUNTER();
   TemporaryFile tmpfile;
   ASSERT_TRUE(RecordCmd()->Run({"-o", tmpfile.path, "--", "sleep", "1"}));
 }
 
 TEST(record_cmd, size_limit_option) {
+  TEST_REQUIRE_HW_COUNTER();
   std::vector<std::unique_ptr<Workload>> workloads;
   CreateProcesses(1, &workloads);
   std::string pid = std::to_string(workloads[0]->GetPid());
diff --git a/simpleperf/cmd_report_test.cpp b/simpleperf/cmd_report_test.cpp
index 3eb3c91..91c5413 100644
--- a/simpleperf/cmd_report_test.cpp
+++ b/simpleperf/cmd_report_test.cpp
@@ -499,6 +499,7 @@
 }
 
 TEST_F(ReportCommandTest, dwarf_callgraph) {
+  TEST_REQUIRE_HW_COUNTER();
   OMIT_TEST_ON_NON_NATIVE_ABIS();
   ASSERT_TRUE(IsDwarfCallChainSamplingSupported());
   std::vector<std::unique_ptr<Workload>> workloads;
@@ -521,6 +522,7 @@
 }
 
 TEST_F(ReportCommandTest, exclude_kernel_callchain) {
+  TEST_REQUIRE_HW_COUNTER();
   TEST_REQUIRE_HOST_ROOT();
   OMIT_TEST_ON_NON_NATIVE_ABIS();
   std::vector<std::unique_ptr<Workload>> workloads;
diff --git a/simpleperf/cmd_stat_test.cpp b/simpleperf/cmd_stat_test.cpp
index 7bb9e37..4a68b00 100644
--- a/simpleperf/cmd_stat_test.cpp
+++ b/simpleperf/cmd_stat_test.cpp
@@ -53,6 +53,7 @@
 }
 
 TEST(stat_cmd, rN_event) {
+  TEST_REQUIRE_HW_COUNTER();
   OMIT_TEST_ON_NON_NATIVE_ABIS();
   size_t event_number;
   if (GetBuildArch() == ARCH_ARM64 || GetBuildArch() == ARCH_ARM) {
@@ -72,6 +73,7 @@
 }
 
 TEST(stat_cmd, event_modifier) {
+  TEST_REQUIRE_HW_COUNTER();
   ASSERT_TRUE(
       StatCmd()->Run({"-e", "cpu-cycles:u,cpu-cycles:k", "sleep", "1"}));
 }
@@ -127,6 +129,7 @@
 }
 
 TEST(stat_cmd, group_option) {
+  TEST_REQUIRE_HW_COUNTER();
   ASSERT_TRUE(
       StatCmd()->Run({"--group", "cpu-clock,page-faults", "sleep", "1"}));
   ASSERT_TRUE(StatCmd()->Run({"--group", "cpu-cycles,instructions", "--group",
@@ -135,6 +138,7 @@
 }
 
 TEST(stat_cmd, auto_generated_summary) {
+  TEST_REQUIRE_HW_COUNTER();
   TemporaryFile tmp_file;
   ASSERT_TRUE(StatCmd()->Run({"--group", "instructions:u,instructions:k", "-o",
                               tmp_file.path, "sleep", "1"}));
@@ -214,6 +218,7 @@
 }
 
 TEST(stat_cmd, sample_speed_should_be_zero) {
+  TEST_REQUIRE_HW_COUNTER();
   EventSelectionSet set(true);
   ASSERT_TRUE(set.AddEventType("cpu-cycles"));
   set.AddMonitoredProcesses({getpid()});
@@ -228,6 +233,7 @@
 }
 
 TEST(stat_cmd, calculating_cpu_frequency) {
+  TEST_REQUIRE_HW_COUNTER();
   CaptureStdout capture;
   ASSERT_TRUE(capture.Start());
   ASSERT_TRUE(StatCmd()->Run({"--csv", "--group", "task-clock,cpu-cycles", "sleep", "1"}));
diff --git a/simpleperf/environment.cpp b/simpleperf/environment.cpp
index 0a0a72c..092e3ea 100644
--- a/simpleperf/environment.cpp
+++ b/simpleperf/environment.cpp
@@ -727,3 +727,16 @@
 #endif  // defined(__ANDROID__)
   return 0;
 }
+
+std::string GetHardwareFromCpuInfo(const std::string& cpu_info) {
+  for (auto& line : android::base::Split(cpu_info, "\n")) {
+    size_t pos = line.find(':');
+    if (pos != std::string::npos) {
+      std::string key = android::base::Trim(line.substr(0, pos));
+      if (key == "Hardware") {
+        return android::base::Trim(line.substr(pos + 1));
+      }
+    }
+  }
+  return "";
+}
diff --git a/simpleperf/environment.h b/simpleperf/environment.h
index 8d3945f..b8289cc 100644
--- a/simpleperf/environment.h
+++ b/simpleperf/environment.h
@@ -125,4 +125,6 @@
 
 constexpr int kAndroidVersionP = 9;
 
+std::string GetHardwareFromCpuInfo(const std::string& cpu_info);
+
 #endif  // SIMPLE_PERF_ENVIRONMENT_H_
diff --git a/simpleperf/environment_test.cpp b/simpleperf/environment_test.cpp
index 5b7c81e..e61108d 100644
--- a/simpleperf/environment_test.cpp
+++ b/simpleperf/environment_test.cpp
@@ -44,3 +44,10 @@
   ASSERT_TRUE(dso != nullptr);
   ASSERT_NE(dso->GetDebugFilePath(), "[vdso]");
 }
+
+TEST(environment, GetHardwareFromCpuInfo) {
+  std::string cpu_info = "CPU revision : 10\n\n"
+      "Hardware : Symbol i.MX6 Freeport_Plat Quad/DualLite (Device Tree)\n";
+  ASSERT_EQ("Symbol i.MX6 Freeport_Plat Quad/DualLite (Device Tree)",
+            GetHardwareFromCpuInfo(cpu_info));
+}
diff --git a/simpleperf/test_util.h b/simpleperf/test_util.h
index 9508d96..006ae2f 100644
--- a/simpleperf/test_util.h
+++ b/simpleperf/test_util.h
@@ -61,6 +61,15 @@
     } \
   } while (0)
 
+bool HasHardwareCounter();
+#define TEST_REQUIRE_HW_COUNTER() \
+  do { \
+    if (!HasHardwareCounter()) { \
+      GTEST_LOG_(INFO) << "Skip this test as the machine doesn't have hardware PMU counters."; \
+      return; \
+    } \
+  } while (0)
+
 class CaptureStdout {
  public:
   CaptureStdout() : started_(false) {}