simpleperf: Fix BR_INST_RETIRED.NEAR_TAKEN for Intel Atom CPUs

Intel Atom CPUs use a different umask for BR_INST_RETIRED.NEAR_TAKEN,
as shown in
https://github.com/intel/perfmon/blob/main/scripts/perf/alderlake/pipeline.json#L303.

This patch detects Intel Atom CPUs and changes config when opening
perf event files on those CPUs.

Bug: 345410289
Test: run simpleperf manually
Test: run simpleperf_unit_test
Change-Id: Ic17bf663fa77c3fbd2df446ba941755a1cd51958
diff --git a/simpleperf/cmd_list.cpp b/simpleperf/cmd_list.cpp
index f6a474d..bd864a6 100644
--- a/simpleperf/cmd_list.cpp
+++ b/simpleperf/cmd_list.cpp
@@ -25,6 +25,7 @@
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/strings.h>
 
 #include "ETMRecorder.h"
 #include "RegEx.h"
@@ -157,7 +158,11 @@
           got_status = true;
         }
       } else if (model.arch == "x86") {
-        if (event_type.limited_arch != model_name) {
+        std::string limited_arch = event_type.limited_arch;
+        if (auto pos = limited_arch.find(':'); pos != std::string::npos) {
+          limited_arch = limited_arch.substr(0, pos);
+        }
+        if (limited_arch != model_name) {
           supported = false;
           got_status = true;
         }
@@ -196,10 +201,10 @@
       return it->second;
     }
 #elif defined(__i386__) || defined(__x86_64__)
-    if (model.x86_data.vendor_id == "GenuineIntel") {
+    if (android::base::StartsWith(model.x86_data.vendor_id, "GenuineIntel")) {
       return "x86-intel";
     }
-    if (model.x86_data.vendor_id == "AuthenticAMD") {
+    if (android::base::StartsWith(model.x86_data.vendor_id, "AuthenticAMD")) {
       return "x86-amd";
     }
 #endif  // defined(__i386__) || defined(__x86_64__)
@@ -220,6 +225,12 @@
       std::this_thread::sleep_for(std::chrono::milliseconds(1));
     }
     perf_event_attr attr = CreateDefaultPerfEventAttr(event_type);
+#if defined(__i386__) || defined(__x86_64__)
+    std::set<int> atom_cpus = GetX86IntelAtomCpus();
+    if (atom_cpus.count(cpu) > 0) {
+      attr.config = event_type.GetIntelAtomCpuConfig();
+    }
+#endif  // defined(__i386__) || defined(__x86_64__)
     std::unique_ptr<EventFd> event_fd = EventFd::OpenEventFile(
         attr, test_thread_arg.tid, test_thread_arg.cpu, nullptr, event_type.name, false);
     test_thread_arg.start = true;
diff --git a/simpleperf/environment.cpp b/simpleperf/environment.cpp
index 316aee4..7921073 100644
--- a/simpleperf/environment.cpp
+++ b/simpleperf/environment.cpp
@@ -1101,6 +1101,7 @@
   }
 
   std::vector<CpuModel> ParseX86CpuModel(const std::vector<std::string>& lines) {
+    std::set<int> atom_cpus = GetX86IntelAtomCpus();
     std::vector<CpuModel> cpu_models;
     uint32_t processor = 0;
     CpuModel model;
@@ -1112,6 +1113,9 @@
         parsed |= 1;
       } else if (name == "vendor_id") {
         model.x86_data.vendor_id = value;
+        if (atom_cpus.count(static_cast<int>(processor)) > 0) {
+          model.x86_data.vendor_id += "-atom";
+        }
         AddCpuModel(processor, model, cpu_models);
         parsed = 0;
       }
@@ -1181,4 +1185,13 @@
 #endif
 }
 
+std::set<int> GetX86IntelAtomCpus() {
+  std::string data;
+  if (!android::base::ReadFileToString("/sys/devices/cpu_atom/cpus", &data)) {
+    return {};
+  }
+  std::optional<std::set<int>> atom_cpus = GetCpusFromString(data);
+  return atom_cpus.has_value() ? atom_cpus.value() : std::set<int>();
+}
+
 }  // namespace simpleperf
diff --git a/simpleperf/environment.h b/simpleperf/environment.h
index 7d0d7f0..279f2ce 100644
--- a/simpleperf/environment.h
+++ b/simpleperf/environment.h
@@ -173,6 +173,7 @@
 };
 
 std::vector<CpuModel> GetCpuModels();
+std::set<int> GetX86IntelAtomCpus();
 
 #endif  // defined(__linux__)
 
diff --git a/simpleperf/event_selection_set.cpp b/simpleperf/event_selection_set.cpp
index 58f8250..5095739 100644
--- a/simpleperf/event_selection_set.cpp
+++ b/simpleperf/event_selection_set.cpp
@@ -666,8 +666,18 @@
   // successfully or all failed to open.
   EventFd* group_fd = nullptr;
   for (auto& selection : group.selections) {
+#if defined(__i386__) || defined(__x86_64__)
+    perf_event_attr attr = selection.event_attr;
+    std::set<int> atom_cpus = GetX86IntelAtomCpus();
+    if (atom_cpus.count(cpu) > 0) {
+      attr.config = selection.event_type_modifier.event_type.GetIntelAtomCpuConfig();
+    }
+    std::unique_ptr<EventFd> event_fd =
+        EventFd::OpenEventFile(attr, tid, cpu, group_fd, selection.event_type_modifier.name, false);
+#else   // defined(__i386__) || defined(__x86_64__)
     std::unique_ptr<EventFd> event_fd = EventFd::OpenEventFile(
         selection.event_attr, tid, cpu, group_fd, selection.event_type_modifier.name, false);
+#endif  // defined(__i386__) || defined(__x86_64__)
     if (!event_fd) {
       *failed_event_type = selection.event_type_modifier.name;
       return false;
diff --git a/simpleperf/event_table.json b/simpleperf/event_table.json
index ddcbfbf..df17063 100644
--- a/simpleperf/event_table.json
+++ b/simpleperf/event_table.json
@@ -1045,7 +1045,7 @@
   },
   "x86-intel": {
     "events": [
-      ["0x20c4", "BR_INST_RETIRED.NEAR_TAKEN", "Taken branch instructions retired"]
+      ["0x20c4", "BR_INST_RETIRED.NEAR_TAKEN", "Taken branch instructions retired", "atom=0xc0c4"]
     ]
   },
   "x86-amd": {
diff --git a/simpleperf/event_table_generator.py b/simpleperf/event_table_generator.py
index d44cbb6..31f678b 100755
--- a/simpleperf/event_table_generator.py
+++ b/simpleperf/event_table_generator.py
@@ -189,7 +189,10 @@
             number = int(event[0], 16)
             name = event[1]
             desc = event[2]
-            self.events.append(RawEvent(number, name, desc, self.arch))
+            limited_arch = self.arch
+            if len(event) > 3:
+                limited_arch += ":" + event[3]
+            self.events.append(RawEvent(number, name, desc, limited_arch))
 
 
 class RawEventGenerator:
@@ -210,7 +213,7 @@
             lines = []
             for event in events:
                 lines.append(gen_event_type_entry_str(event.name, 'PERF_TYPE_RAW', '0x%x' %
-                         event.number, event.desc, event.limited_arch))
+                                                      event.number, event.desc, event.limited_arch))
             return guard(''.join(lines))
 
         lines_arm64 = generate_event_entries(self.arm64_data.events, self.add_arm_guard)
diff --git a/simpleperf/event_type.cpp b/simpleperf/event_type.cpp
index fef1a9f..d54ac27 100644
--- a/simpleperf/event_type.cpp
+++ b/simpleperf/event_type.cpp
@@ -463,6 +463,16 @@
   return empty_result;
 }
 
+uint64_t EventType::GetIntelAtomCpuConfig() const {
+  if (auto pos = limited_arch.find("atom="); pos != std::string::npos) {
+    uint64_t atom_config;
+    if (android::base::ParseUint(limited_arch.substr(pos + 5), &atom_config)) {
+      return atom_config;
+    }
+  }
+  return config;
+}
+
 std::string ScopedEventTypes::BuildString(const std::vector<const EventType*>& event_types) {
   std::string result;
   for (auto type : event_types) {
diff --git a/simpleperf/event_type.h b/simpleperf/event_type.h
index d85f71d..8683dbb 100644
--- a/simpleperf/event_type.h
+++ b/simpleperf/event_type.h
@@ -58,6 +58,7 @@
   bool IsTracepointEvent() const { return type == PERF_TYPE_TRACEPOINT; }
 
   std::vector<int> GetPmuCpumask();
+  uint64_t GetIntelAtomCpuConfig() const;
 
   std::string name;
   uint32_t type;