simpleperf: loosen unwinding arch check for system wide collection.

When doing system wide collection, it is possible that there are
32-bit compat processes running on 64-bit devices. It is not proper
to abort in this situation. So loosen the check to allow it. Also
add corresponding test.

Bug: 27927427

Change-Id: I5c9253eb6e474497e4f37e234e0e523e141fab20
diff --git a/simpleperf/cmd_record.cpp b/simpleperf/cmd_record.cpp
index f4bd64c..1cea173 100644
--- a/simpleperf/cmd_record.cpp
+++ b/simpleperf/cmd_record.cpp
@@ -689,7 +689,11 @@
       RegSet regs = CreateRegSet(r.regs_user_data.reg_mask, r.regs_user_data.regs);
       std::vector<char>& stack = r.stack_user_data.data;
       ArchType arch = GetArchForAbi(GetBuildArch(), r.regs_user_data.abi);
-      std::vector<uint64_t> unwind_ips = UnwindCallChain(arch, *thread, regs, stack);
+      // Normally do strict arch check when unwinding stack. But allow unwinding 32-bit processes
+      // on 64-bit devices for system wide profiling.
+      bool strict_arch_check = !system_wide_collection_;
+      std::vector<uint64_t> unwind_ips = UnwindCallChain(arch, *thread, regs, stack,
+                                                         strict_arch_check);
       r.callchain_data.ips.push_back(PERF_CONTEXT_USER);
       r.callchain_data.ips.insert(r.callchain_data.ips.end(), unwind_ips.begin(), unwind_ips.end());
       r.regs_user_data.abi = 0;
diff --git a/simpleperf/cmd_record_test.cpp b/simpleperf/cmd_record_test.cpp
index a89febf..41a675b 100644
--- a/simpleperf/cmd_record_test.cpp
+++ b/simpleperf/cmd_record_test.cpp
@@ -53,9 +53,7 @@
 }
 
 TEST(record_cmd, system_wide_option) {
-  if (IsRoot()) {
-    ASSERT_TRUE(RunRecordCmd({"-a"}));
-  }
+  TEST_IN_ROOT(ASSERT_TRUE(RunRecordCmd({"-a"})));
 }
 
 TEST(record_cmd, sample_period_option) {
@@ -107,9 +105,7 @@
 }
 
 TEST(record_cmd, tracepoint_event) {
-  if (IsRoot()) {
-    ASSERT_TRUE(RunRecordCmd({"-a", "-e", "sched:sched_switch"}));
-  }
+  TEST_IN_ROOT(ASSERT_TRUE(RunRecordCmd({"-a", "-e", "sched:sched_switch"})));
 }
 
 TEST(record_cmd, branch_sampling) {
@@ -133,6 +129,10 @@
   ASSERT_TRUE(RunRecordCmd({"--call-graph", "fp"}));
 }
 
+TEST(record_cmd, system_wide_fp_callchain_sampling) {
+  TEST_IN_ROOT(ASSERT_TRUE(RunRecordCmd({"-a", "--call-graph", "fp"})));
+}
+
 TEST(record_cmd, dwarf_callchain_sampling) {
   if (IsDwarfCallChainSamplingSupported()) {
     ASSERT_TRUE(RunRecordCmd({"--call-graph", "dwarf"}));
@@ -144,6 +144,15 @@
   }
 }
 
+TEST(record_cmd, system_wide_dwarf_callchain_sampling) {
+  if (IsDwarfCallChainSamplingSupported()) {
+    TEST_IN_ROOT(RunRecordCmd({"-a", "--call-graph", "dwarf"}));
+  } else {
+    GTEST_LOG_(INFO)
+        << "This test does nothing as dwarf callchain sampling is not supported on this device.";
+  }
+}
+
 TEST(record_cmd, no_unwind_option) {
   if (IsDwarfCallChainSamplingSupported()) {
     ASSERT_TRUE(RunRecordCmd({"--call-graph", "dwarf", "--no-unwind"}));
@@ -196,9 +205,7 @@
 
 TEST(record_cmd, cpu_option) {
   ASSERT_TRUE(RunRecordCmd({"--cpu", "0"}));
-  if (IsRoot()) {
-    ASSERT_TRUE(RunRecordCmd({"--cpu", "0", "-a"}));
-  }
+  TEST_IN_ROOT(ASSERT_TRUE(RunRecordCmd({"--cpu", "0", "-a"})));
 }
 
 TEST(record_cmd, mmap_page_option) {
diff --git a/simpleperf/cmd_report.cpp b/simpleperf/cmd_report.cpp
index d42bf69..b9c3d6f 100644
--- a/simpleperf/cmd_report.cpp
+++ b/simpleperf/cmd_report.cpp
@@ -272,6 +272,7 @@
         record_filename_("perf.data"),
         record_file_arch_(GetBuildArch()),
         use_branch_address_(false),
+        system_wide_collection_(false),
         accumulate_callchain_(false),
         print_callgraph_(false),
         callgraph_show_callee_(true),
@@ -312,6 +313,7 @@
   std::unique_ptr<SampleTree> sample_tree_;
   bool use_branch_address_;
   std::string record_cmdline_;
+  bool system_wide_collection_;
   bool accumulate_callchain_;
   bool print_callgraph_;
   bool callgraph_show_callee_;
@@ -569,8 +571,11 @@
         std::vector<char> stack(r.stack_user_data.data.begin(),
                                 r.stack_user_data.data.begin() + r.stack_user_data.data.size());
         ArchType arch = GetArchForAbi(ScopedCurrentArch::GetCurrentArch(), r.regs_user_data.abi);
+        // Normally do strict arch check when unwinding stack. But allow unwinding 32-bit processes
+        // on 64-bit devices for system wide profiling.
+        bool strict_arch_check = !system_wide_collection_;
         std::vector<uint64_t> unwind_ips =
-            UnwindCallChain(arch, *sample->thread, regs, stack);
+            UnwindCallChain(arch, *sample->thread, regs, stack, strict_arch_check);
         if (!unwind_ips.empty()) {
           ips.push_back(PERF_CONTEXT_USER);
           ips.insert(ips.end(), unwind_ips.begin(), unwind_ips.end());
@@ -647,6 +652,20 @@
   std::vector<std::string> cmdline = record_file_reader_->ReadCmdlineFeature();
   if (!cmdline.empty()) {
     record_cmdline_ = android::base::Join(cmdline, ' ');
+    // TODO: the code to detect system wide collection option is fragile, remove it once we can
+    // do cross unwinding.
+    for (size_t i = 0; i < cmdline.size(); i++) {
+      std::string& s = cmdline[i];
+      if (s == "-a") {
+        system_wide_collection_ = true;
+        break;
+      } else if (s == "--call-graph" || s == "--cpu" || s == "-e" || s == "-f" || s == "-F" ||
+                 s == "-j" || s == "-m" || s == "-o" || s == "-p" || s == "-t") {
+        i++;
+      } else if (!s.empty() && s[0] != '-') {
+        break;
+      }
+    }
   }
   return true;
 }
diff --git a/simpleperf/cmd_stat_test.cpp b/simpleperf/cmd_stat_test.cpp
index 27f1f09..1935b5f 100644
--- a/simpleperf/cmd_stat_test.cpp
+++ b/simpleperf/cmd_stat_test.cpp
@@ -35,9 +35,7 @@
 }
 
 TEST(stat_cmd, system_wide_option) {
-  if (IsRoot()) {
-    ASSERT_TRUE(StatCmd()->Run({"-a", "sleep", "1"}));
-  }
+  TEST_IN_ROOT(ASSERT_TRUE(StatCmd()->Run({"-a", "sleep", "1"})));
 }
 
 TEST(stat_cmd, verbose_option) {
@@ -45,9 +43,7 @@
 }
 
 TEST(stat_cmd, tracepoint_event) {
-  if (IsRoot()) {
-    ASSERT_TRUE(StatCmd()->Run({"-a", "-e", "sched:sched_switch", "sleep", "1"}));
-  }
+  TEST_IN_ROOT(ASSERT_TRUE(StatCmd()->Run({"-a", "-e", "sched:sched_switch", "sleep", "1"})));
 }
 
 TEST(stat_cmd, event_modifier) {
@@ -87,7 +83,5 @@
 
 TEST(stat_cmd, cpu_option) {
   ASSERT_TRUE(StatCmd()->Run({"--cpu", "0", "sleep", "1"}));
-  if (IsRoot()) {
-    ASSERT_TRUE(StatCmd()->Run({"--cpu", "0", "-a", "sleep", "1"}));
-  }
+  TEST_IN_ROOT(ASSERT_TRUE(StatCmd()->Run({"--cpu", "0", "-a", "sleep", "1"})));
 }
diff --git a/simpleperf/dwarf_unwind.cpp b/simpleperf/dwarf_unwind.cpp
index 39bbef1..10baeb6 100644
--- a/simpleperf/dwarf_unwind.cpp
+++ b/simpleperf/dwarf_unwind.cpp
@@ -95,11 +95,12 @@
 }
 
 std::vector<uint64_t> UnwindCallChain(ArchType arch, const ThreadEntry& thread,
-                                      const RegSet& regs, const std::vector<char>& stack) {
+                                      const RegSet& regs, const std::vector<char>& stack,
+                                      bool strict_arch_check) {
   std::vector<uint64_t> result;
-  if (arch != GetBuildArch()) {
+  if (!IsArchTheSame(arch, GetBuildArch(), strict_arch_check)) {
     LOG(FATAL) << "simpleperf is built in arch " << GetArchString(GetBuildArch())
-        << ", and can't do stack unwinding for arch " << GetArchString(arch);
+            << ", and can't do stack unwinding for arch " << GetArchString(arch);
     return result;
   }
   uint64_t sp_reg_value;
diff --git a/simpleperf/dwarf_unwind.h b/simpleperf/dwarf_unwind.h
index 4e3ffd1..c725723 100644
--- a/simpleperf/dwarf_unwind.h
+++ b/simpleperf/dwarf_unwind.h
@@ -24,6 +24,6 @@
 struct ThreadEntry;
 
 std::vector<uint64_t> UnwindCallChain(ArchType arch, const ThreadEntry& thread, const RegSet& regs,
-                                      const std::vector<char>& stack);
+                                      const std::vector<char>& stack, bool strict_arch_check);
 
 #endif  // SIMPLE_PERF_DWARF_UNWIND_H_
diff --git a/simpleperf/get_test_data.h b/simpleperf/get_test_data.h
index c380a7d..57a6e64 100644
--- a/simpleperf/get_test_data.h
+++ b/simpleperf/get_test_data.h
@@ -24,8 +24,6 @@
 std::string GetTestData(const std::string& filename);
 const std::string& GetTestDataDir();
 
-bool IsRoot();
-
 // The source code of elf and elf_with_mini_debug_info is testdata/elf_file_source.cpp.
 static const std::string ELF_FILE = "elf";
 static const std::string ELF_FILE_WITH_MINI_DEBUG_INFO = "elf_with_mini_debug_info";
diff --git a/simpleperf/nonlinux_support/nonlinux_support.cpp b/simpleperf/nonlinux_support/nonlinux_support.cpp
index 7551d36..37833ed 100644
--- a/simpleperf/nonlinux_support/nonlinux_support.cpp
+++ b/simpleperf/nonlinux_support/nonlinux_support.cpp
@@ -21,7 +21,7 @@
 #include "environment.h"
 
 std::vector<uint64_t> UnwindCallChain(ArchType, const ThreadEntry&, const RegSet&,
-                                      const std::vector<char>&) {
+                                      const std::vector<char>&, bool) {
   return std::vector<uint64_t>();
 }
 
diff --git a/simpleperf/perf_regs.cpp b/simpleperf/perf_regs.cpp
index 5d43db5..e96b83c 100644
--- a/simpleperf/perf_regs.cpp
+++ b/simpleperf/perf_regs.cpp
@@ -67,6 +67,25 @@
   return "unknown";
 }
 
+// If strict_check, must have arch1 == arch2.
+// Otherwise, allow X86_32 with X86_64, ARM with ARM64.
+bool IsArchTheSame(ArchType arch1, ArchType arch2, bool strict_check) {
+  if (strict_check) {
+    return arch1 == arch2;
+  }
+  switch (arch1) {
+    case ARCH_X86_32:
+    case ARCH_X86_64:
+      return arch2 == ARCH_X86_32 || arch2 == ARCH_X86_64;
+    case ARCH_ARM64:
+    case ARCH_ARM:
+      return arch2 == ARCH_ARM64 || arch2 == ARCH_ARM;
+    default:
+      break;
+  }
+  return arch1 == arch2;
+}
+
 uint64_t GetSupportedRegMask(ArchType arch) {
   switch (arch) {
     case ARCH_X86_32:
diff --git a/simpleperf/perf_regs.h b/simpleperf/perf_regs.h
index 98c925f..7705e50 100644
--- a/simpleperf/perf_regs.h
+++ b/simpleperf/perf_regs.h
@@ -58,6 +58,7 @@
 ArchType GetArchType(const std::string& arch);
 ArchType GetArchForAbi(ArchType machine_arch, int abi);
 std::string GetArchString(ArchType arch);
+bool IsArchTheSame(ArchType arch1, ArchType arch2, bool strict_check);
 uint64_t GetSupportedRegMask(ArchType arch);
 std::string GetRegName(size_t regno, ArchType arch);
 
diff --git a/simpleperf/test_util.h b/simpleperf/test_util.h
index 61a0ec7..15d11cb 100644
--- a/simpleperf/test_util.h
+++ b/simpleperf/test_util.h
@@ -28,3 +28,14 @@
 
 void ParseSymbol(const ElfFileSymbol& symbol, std::map<std::string, ElfFileSymbol>* symbols);
 void CheckElfFileSymbols(const std::map<std::string, ElfFileSymbol>& symbols);
+
+bool IsRoot();
+
+#define TEST_IN_ROOT(TestStatement)                                                                \
+  do {                                                                                             \
+    if (IsRoot()) {                                                                                \
+      TestStatement;                                                                               \
+    } else {                                                                                       \
+      GTEST_LOG_(INFO) << "Didn't test \"" << #TestStatement << "\" requires root privileges";     \
+    }                                                                                              \
+  } while (0)