Simpleperf: remove dependency on global current_arch.

When running unit tests on arm64 devices:
[OK] ReportCommandTest.dwarf_callgraph
[OK] record_cmd.dwarf_callchain_sampling.
ERROR: can't unwind data recorded on a different architecture.

It is because ReportCommandtest.dwarf_callgraph opens a perf.data
recorded on x86_64, and changes current_arch. It causes a problem when
the test record_cmd.dwarf_callchain_sampling calls libbacktrace built
on aarch64. Athough it doesn't make the test fail, we should fix this.

Change-Id: I2cd70369a769ef2199cab2302b8b824369be0907
diff --git a/simpleperf/cmd_dumprecord.cpp b/simpleperf/cmd_dumprecord.cpp
index 13f40d4..f19cfd3 100644
--- a/simpleperf/cmd_dumprecord.cpp
+++ b/simpleperf/cmd_dumprecord.cpp
@@ -39,7 +39,7 @@
       : Command("dump", "dump perf record file",
                 "Usage: simpleperf dumprecord [options] [perf_record_file]\n"
                 "    Dump different parts of a perf record file. Default file is perf.data.\n"),
-        record_filename_("perf.data") {
+        record_filename_("perf.data"), record_file_arch_(GetBuildArch()) {
   }
 
   bool Run(const std::vector<std::string>& args);
@@ -53,6 +53,7 @@
 
   std::string record_filename_;
   std::unique_ptr<RecordFileReader> record_file_reader_;
+  ArchType record_file_arch_;
 };
 
 bool DumpRecordCommand::Run(const std::vector<std::string>& args) {
@@ -65,10 +66,12 @@
   }
   std::string arch = record_file_reader_->ReadFeatureString(FEAT_ARCH);
   if (!arch.empty()) {
-    if (!SetCurrentArch(arch)) {
+    record_file_arch_ = GetArchType(arch);
+    if (record_file_arch_ == ARCH_UNSUPPORTED) {
       return false;
     }
   }
+  ScopedCurrentArch scoped_arch(record_file_arch_);
   DumpFileHeader();
   DumpAttrSection();
   DumpDataSection();
diff --git a/simpleperf/cmd_record.cpp b/simpleperf/cmd_record.cpp
index f0a7cea..9166ae6 100644
--- a/simpleperf/cmd_record.cpp
+++ b/simpleperf/cmd_record.cpp
@@ -693,7 +693,7 @@
       ThreadEntry* thread = thread_tree_.FindThreadOrNew(r.tid_data.pid, r.tid_data.tid);
       RegSet regs = CreateRegSet(r.regs_user_data.reg_mask, r.regs_user_data.regs);
       std::vector<char>& stack = r.stack_user_data.data;
-      std::vector<uint64_t> unwind_ips = UnwindCallChain(*thread, regs, stack);
+      std::vector<uint64_t> unwind_ips = UnwindCallChain(GetBuildArch(), *thread, regs, stack);
       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_report.cpp b/simpleperf/cmd_report.cpp
index a89be67..9f3e736 100644
--- a/simpleperf/cmd_report.cpp
+++ b/simpleperf/cmd_report.cpp
@@ -270,6 +270,7 @@
             "    --vmlinux <file>\n"
             "                  Parse kernel symbols from <file>.\n"),
         record_filename_("perf.data"),
+        record_file_arch_(GetBuildArch()),
         use_branch_address_(false),
         accumulate_callchain_(false),
         print_callgraph_(false),
@@ -302,6 +303,7 @@
                            uint64_t parent_period, bool last);
 
   std::string record_filename_;
+  ArchType record_file_arch_;
   std::unique_ptr<RecordFileReader> record_file_reader_;
   perf_event_attr event_attr_;
   std::vector<std::unique_ptr<Displayable>> displayable_items_;
@@ -336,6 +338,7 @@
   if (!ReadFeaturesFromRecordFile()) {
     return false;
   }
+  ScopedCurrentArch scoped_arch(record_file_arch_);
   ReadSampleTreeFromRecordFile();
 
   // 3. Show collected information.
@@ -565,7 +568,8 @@
         RegSet regs = CreateRegSet(r.regs_user_data.reg_mask, r.regs_user_data.regs);
         std::vector<char> stack(r.stack_user_data.data.begin(),
                                 r.stack_user_data.data.begin() + r.stack_user_data.data.size());
-        std::vector<uint64_t> unwind_ips = UnwindCallChain(*sample->thread, regs, stack);
+        std::vector<uint64_t> unwind_ips =
+            UnwindCallChain(ScopedCurrentArch::GetCurrentArch(), *sample->thread, regs, stack);
         if (!unwind_ips.empty()) {
           ips.push_back(PERF_CONTEXT_USER);
           ips.insert(ips.end(), unwind_ips.begin(), unwind_ips.end());
@@ -633,7 +637,8 @@
 
   std::string arch = record_file_reader_->ReadFeatureString(PerfFileFormat::FEAT_ARCH);
   if (!arch.empty()) {
-    if (!SetCurrentArch(arch)) {
+    record_file_arch_ = GetArchType(arch);
+    if (record_file_arch_ == ARCH_UNSUPPORTED) {
       return false;
     }
   }
diff --git a/simpleperf/dwarf_unwind.cpp b/simpleperf/dwarf_unwind.cpp
index fd84feb..ae2e1a1 100644
--- a/simpleperf/dwarf_unwind.cpp
+++ b/simpleperf/dwarf_unwind.cpp
@@ -94,15 +94,15 @@
   return ucontext;
 }
 
-std::vector<uint64_t> UnwindCallChain(const ThreadEntry& thread, const RegSet& regs,
-                                      const std::vector<char>& stack) {
+std::vector<uint64_t> UnwindCallChain(ArchType arch, const ThreadEntry& thread,
+                                      const RegSet& regs, const std::vector<char>& stack) {
   std::vector<uint64_t> result;
-  if (GetCurrentArch() != GetBuildArch()) {
+  if (arch != GetBuildArch()) {
     LOG(ERROR) << "can't unwind data recorded on a different architecture";
     return result;
   }
   uint64_t sp_reg_value;
-  if (!GetSpRegValue(regs, &sp_reg_value)) {
+  if (!GetSpRegValue(regs, arch, &sp_reg_value)) {
     LOG(ERROR) << "can't get sp reg value";
     return result;
   }
diff --git a/simpleperf/dwarf_unwind.h b/simpleperf/dwarf_unwind.h
index 671013e..4e3ffd1 100644
--- a/simpleperf/dwarf_unwind.h
+++ b/simpleperf/dwarf_unwind.h
@@ -23,7 +23,7 @@
 
 struct ThreadEntry;
 
-std::vector<uint64_t> UnwindCallChain(const ThreadEntry& thread, const RegSet& regs,
+std::vector<uint64_t> UnwindCallChain(ArchType arch, const ThreadEntry& thread, const RegSet& regs,
                                       const std::vector<char>& stack);
 
 #endif  // SIMPLE_PERF_DWARF_UNWIND_H_
diff --git a/simpleperf/event_selection_set.cpp b/simpleperf/event_selection_set.cpp
index d99966a..fad8b1e 100644
--- a/simpleperf/event_selection_set.cpp
+++ b/simpleperf/event_selection_set.cpp
@@ -45,7 +45,7 @@
   perf_event_attr attr = CreateDefaultPerfEventAttr(*type);
   attr.sample_type |= PERF_SAMPLE_CALLCHAIN | PERF_SAMPLE_REGS_USER | PERF_SAMPLE_STACK_USER;
   attr.exclude_callchain_user = 1;
-  attr.sample_regs_user = GetSupportedRegMask();
+  attr.sample_regs_user = GetSupportedRegMask(GetBuildArch());
   attr.sample_stack_user = 8192;
   return IsEventAttrSupportedByKernel(attr);
 }
@@ -166,7 +166,7 @@
     selection.event_attr.sample_type |=
         PERF_SAMPLE_CALLCHAIN | PERF_SAMPLE_REGS_USER | PERF_SAMPLE_STACK_USER;
     selection.event_attr.exclude_callchain_user = 1;
-    selection.event_attr.sample_regs_user = GetSupportedRegMask();
+    selection.event_attr.sample_regs_user = GetSupportedRegMask(GetBuildArch());
     selection.event_attr.sample_stack_user = dump_stack_size;
   }
   return true;
diff --git a/simpleperf/nonlinux_support/nonlinux_support.cpp b/simpleperf/nonlinux_support/nonlinux_support.cpp
index a248995..7551d36 100644
--- a/simpleperf/nonlinux_support/nonlinux_support.cpp
+++ b/simpleperf/nonlinux_support/nonlinux_support.cpp
@@ -20,7 +20,8 @@
 #include "dwarf_unwind.h"
 #include "environment.h"
 
-std::vector<uint64_t> UnwindCallChain(const ThreadEntry&, const RegSet&, const std::vector<char>&) {
+std::vector<uint64_t> UnwindCallChain(ArchType, const ThreadEntry&, const RegSet&,
+                                      const std::vector<char>&) {
   return std::vector<uint64_t>();
 }
 
diff --git a/simpleperf/perf_regs.cpp b/simpleperf/perf_regs.cpp
index 1f29971..29d144e 100644
--- a/simpleperf/perf_regs.cpp
+++ b/simpleperf/perf_regs.cpp
@@ -21,30 +21,24 @@
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 
-static ArchType current_arch = GetBuildArch();
+ArchType ScopedCurrentArch::current_arch = GetBuildArch();
 
-ArchType GetCurrentArch() {
-  return current_arch;
-}
-
-bool SetCurrentArch(const std::string& arch) {
+ArchType GetArchType(const std::string& arch) {
   if (arch == "x86" || arch == "i686") {
-    current_arch = ARCH_X86_32;
+    return ARCH_X86_32;
   } else if (arch == "x86_64") {
-    current_arch = ARCH_X86_64;
+    return ARCH_X86_64;
   } else if (arch == "aarch64") {
-    current_arch = ARCH_ARM64;
+    return ARCH_ARM64;
   } else if (android::base::StartsWith(arch, "arm")) {
-    current_arch = ARCH_ARM;
-  } else {
-    LOG(ERROR) << "unsupported arch: " << arch;
-    return false;
+    return ARCH_ARM;
   }
-  return true;
+  LOG(ERROR) << "unsupported arch: " << arch;
+  return ARCH_UNSUPPORTED;
 }
 
-uint64_t GetSupportedRegMask() {
-  switch (GetCurrentArch()) {
+uint64_t GetSupportedRegMask(ArchType arch) {
+  switch (arch) {
     case ARCH_X86_32:
       return ((1ULL << PERF_REG_X86_32_MAX) - 1);
     case ARCH_X86_64:
@@ -78,10 +72,10 @@
     {PERF_REG_ARM64_LR, "lr"}, {PERF_REG_ARM64_SP, "sp"}, {PERF_REG_ARM64_PC, "pc"},
 };
 
-std::string GetRegName(size_t regno) {
+std::string GetRegName(size_t regno, ArchType arch) {
   // Cast regno to int type to avoid -Werror=type-limits.
   int reg = static_cast<int>(regno);
-  switch (GetCurrentArch()) {
+  switch (arch) {
     case ARCH_X86_64: {
       if (reg >= PERF_REG_X86_R8 && reg <= PERF_REG_X86_R15) {
         return android::base::StringPrintf("r%d", reg - PERF_REG_X86_R8 + 8);
@@ -133,18 +127,23 @@
   return false;
 }
 
-bool GetSpRegValue(const RegSet& regs, uint64_t* value) {
+bool GetSpRegValue(const RegSet& regs, ArchType arch, uint64_t* value) {
   size_t regno;
-#if defined(__i386__)
-  regno = PERF_REG_X86_SP;
-#elif defined(__x86_64__)
-  regno = PERF_REG_X86_SP;
-#elif defined(__aarch64__)
-  regno = PERF_REG_ARM64_SP;
-#elif defined(__arm__)
-  regno = PERF_REG_ARM_SP;
-#else
-  return false;
-#endif
+  switch (arch) {
+    case ARCH_X86_32:
+      regno = PERF_REG_X86_SP;
+      break;
+    case ARCH_X86_64:
+      regno = PERF_REG_X86_SP;
+      break;
+    case ARCH_ARM:
+      regno = PERF_REG_ARM_SP;
+      break;
+    case ARCH_ARM64:
+      regno = PERF_REG_ARM64_SP;
+      break;
+    default:
+      return false;
+  }
   return GetRegValue(regs, regno, value);
 }
diff --git a/simpleperf/perf_regs.h b/simpleperf/perf_regs.h
index 47451c0..9fc610f 100644
--- a/simpleperf/perf_regs.h
+++ b/simpleperf/perf_regs.h
@@ -55,12 +55,26 @@
 #endif
 }
 
-ArchType GetCurrentArch();
-bool SetCurrentArch(const std::string& arch);
+ArchType GetArchType(const std::string& arch);
+uint64_t GetSupportedRegMask(ArchType arch);
+std::string GetRegName(size_t regno, ArchType arch);
 
-uint64_t GetSupportedRegMask();
+class ScopedCurrentArch {
+ public:
+  ScopedCurrentArch(ArchType arch) : saved_arch(current_arch) {
+    current_arch = arch;
+  }
+  ~ScopedCurrentArch() {
+    current_arch = saved_arch;
+  }
+  static ArchType GetCurrentArch() {
+    return current_arch;
+  }
 
-std::string GetRegName(size_t regno);
+ private:
+  ArchType saved_arch;
+  static ArchType current_arch;
+};
 
 struct RegSet {
   uint64_t valid_mask;
@@ -70,6 +84,6 @@
 RegSet CreateRegSet(uint64_t valid_mask, const std::vector<uint64_t>& valid_regs);
 
 bool GetRegValue(const RegSet& regs, size_t regno, uint64_t* value);
-bool GetSpRegValue(const RegSet& regs, uint64_t* value);
+bool GetSpRegValue(const RegSet& regs, ArchType arch, uint64_t* value);
 
 #endif  // SIMPLE_PERF_PERF_REGS_H_
diff --git a/simpleperf/record.cpp b/simpleperf/record.cpp
index f9e220a..d97ba81 100644
--- a/simpleperf/record.cpp
+++ b/simpleperf/record.cpp
@@ -505,7 +505,8 @@
     PrintIndented(indent, "user regs: abi=%" PRId64 "\n", regs_user_data.abi);
     for (size_t i = 0, pos = 0; i < 64; ++i) {
       if ((regs_user_data.reg_mask >> i) & 1) {
-        PrintIndented(indent + 1, "reg (%s) 0x%016" PRIx64 "\n", GetRegName(i).c_str(),
+        PrintIndented(indent + 1, "reg (%s) 0x%016" PRIx64 "\n",
+                      GetRegName(i, ScopedCurrentArch::GetCurrentArch()).c_str(),
                       regs_user_data.regs[pos++]);
       }
     }