xref: /aosp_15_r20/system/extras/simpleperf/kallsyms.cpp (revision 288bf5226967eb3dac5cce6c939ccc2a7f2b4fe5)
1*288bf522SAndroid Build Coastguard Worker /*
2*288bf522SAndroid Build Coastguard Worker  * Copyright (C) 2020 The Android Open Source Project
3*288bf522SAndroid Build Coastguard Worker  *
4*288bf522SAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*288bf522SAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*288bf522SAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*288bf522SAndroid Build Coastguard Worker  *
8*288bf522SAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*288bf522SAndroid Build Coastguard Worker  *
10*288bf522SAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*288bf522SAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*288bf522SAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*288bf522SAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*288bf522SAndroid Build Coastguard Worker  * limitations under the License.
15*288bf522SAndroid Build Coastguard Worker  */
16*288bf522SAndroid Build Coastguard Worker #include "kallsyms.h"
17*288bf522SAndroid Build Coastguard Worker 
18*288bf522SAndroid Build Coastguard Worker #include <inttypes.h>
19*288bf522SAndroid Build Coastguard Worker 
20*288bf522SAndroid Build Coastguard Worker #include <string>
21*288bf522SAndroid Build Coastguard Worker 
22*288bf522SAndroid Build Coastguard Worker #include <android-base/file.h>
23*288bf522SAndroid Build Coastguard Worker #include <android-base/logging.h>
24*288bf522SAndroid Build Coastguard Worker #include <android-base/properties.h>
25*288bf522SAndroid Build Coastguard Worker 
26*288bf522SAndroid Build Coastguard Worker #include "environment.h"
27*288bf522SAndroid Build Coastguard Worker #include "read_elf.h"
28*288bf522SAndroid Build Coastguard Worker #include "utils.h"
29*288bf522SAndroid Build Coastguard Worker 
30*288bf522SAndroid Build Coastguard Worker namespace simpleperf {
31*288bf522SAndroid Build Coastguard Worker 
32*288bf522SAndroid Build Coastguard Worker #if defined(__linux__)
33*288bf522SAndroid Build Coastguard Worker 
34*288bf522SAndroid Build Coastguard Worker namespace {
35*288bf522SAndroid Build Coastguard Worker 
36*288bf522SAndroid Build Coastguard Worker const char kKallsymsPath[] = "/proc/kallsyms";
37*288bf522SAndroid Build Coastguard Worker const char kProcModulesPath[] = "/proc/modules";
38*288bf522SAndroid Build Coastguard Worker const char kPtrRestrictPath[] = "/proc/sys/kernel/kptr_restrict";
39*288bf522SAndroid Build Coastguard Worker const char kLowerPtrRestrictAndroidProp[] = "security.lower_kptr_restrict";
40*288bf522SAndroid Build Coastguard Worker const unsigned int kMinLineTestNonNullSymbols = 10;
41*288bf522SAndroid Build Coastguard Worker 
42*288bf522SAndroid Build Coastguard Worker // Tries to read the kernel symbol file and ensure that at least some symbol
43*288bf522SAndroid Build Coastguard Worker // addresses are non-null.
CanReadKernelSymbolAddresses()44*288bf522SAndroid Build Coastguard Worker bool CanReadKernelSymbolAddresses() {
45*288bf522SAndroid Build Coastguard Worker   LineReader reader(kKallsymsPath);
46*288bf522SAndroid Build Coastguard Worker   if (!reader.Ok()) {
47*288bf522SAndroid Build Coastguard Worker     LOG(DEBUG) << "Failed to read " << kKallsymsPath;
48*288bf522SAndroid Build Coastguard Worker     return false;
49*288bf522SAndroid Build Coastguard Worker   }
50*288bf522SAndroid Build Coastguard Worker   auto symbol_callback = [&](const KernelSymbol& symbol) { return (symbol.addr != 0u); };
51*288bf522SAndroid Build Coastguard Worker   for (unsigned int i = 0; i < kMinLineTestNonNullSymbols; i++) {
52*288bf522SAndroid Build Coastguard Worker     std::string* line = reader.ReadLine();
53*288bf522SAndroid Build Coastguard Worker     if (line == nullptr) {
54*288bf522SAndroid Build Coastguard Worker       return false;
55*288bf522SAndroid Build Coastguard Worker     }
56*288bf522SAndroid Build Coastguard Worker     if (ProcessKernelSymbols(*line, symbol_callback)) {
57*288bf522SAndroid Build Coastguard Worker       return true;
58*288bf522SAndroid Build Coastguard Worker     }
59*288bf522SAndroid Build Coastguard Worker   }
60*288bf522SAndroid Build Coastguard Worker   return false;
61*288bf522SAndroid Build Coastguard Worker }
62*288bf522SAndroid Build Coastguard Worker 
63*288bf522SAndroid Build Coastguard Worker // Define a scope in which access to kallsyms is possible.
64*288bf522SAndroid Build Coastguard Worker // This is based on the Perfetto implementation.
65*288bf522SAndroid Build Coastguard Worker class ScopedKptrUnrestrict {
66*288bf522SAndroid Build Coastguard Worker  public:
67*288bf522SAndroid Build Coastguard Worker   ScopedKptrUnrestrict();   // Lowers kptr_restrict if necessary.
68*288bf522SAndroid Build Coastguard Worker   ~ScopedKptrUnrestrict();  // Restores the initial kptr_restrict.
69*288bf522SAndroid Build Coastguard Worker 
70*288bf522SAndroid Build Coastguard Worker   // Indicates if access to kallsyms should be successful.
KallsymsAvailable()71*288bf522SAndroid Build Coastguard Worker   bool KallsymsAvailable() { return kallsyms_available_; }
72*288bf522SAndroid Build Coastguard Worker 
ResetWarning()73*288bf522SAndroid Build Coastguard Worker   static void ResetWarning() { kernel_address_warning_printed_ = false; }
74*288bf522SAndroid Build Coastguard Worker 
75*288bf522SAndroid Build Coastguard Worker  private:
76*288bf522SAndroid Build Coastguard Worker   bool WriteKptrRestrict(const std::string& value);
77*288bf522SAndroid Build Coastguard Worker   void PrintWarning();
78*288bf522SAndroid Build Coastguard Worker 
79*288bf522SAndroid Build Coastguard Worker   bool restore_property_ = false;
80*288bf522SAndroid Build Coastguard Worker   bool restore_restrict_value_ = false;
81*288bf522SAndroid Build Coastguard Worker   std::string saved_restrict_value_;
82*288bf522SAndroid Build Coastguard Worker   bool kallsyms_available_ = false;
83*288bf522SAndroid Build Coastguard Worker 
84*288bf522SAndroid Build Coastguard Worker   static bool kernel_address_warning_printed_;
85*288bf522SAndroid Build Coastguard Worker };
86*288bf522SAndroid Build Coastguard Worker 
87*288bf522SAndroid Build Coastguard Worker bool ScopedKptrUnrestrict::kernel_address_warning_printed_ = false;
88*288bf522SAndroid Build Coastguard Worker 
ScopedKptrUnrestrict()89*288bf522SAndroid Build Coastguard Worker ScopedKptrUnrestrict::ScopedKptrUnrestrict() {
90*288bf522SAndroid Build Coastguard Worker   if (CanReadKernelSymbolAddresses()) {
91*288bf522SAndroid Build Coastguard Worker     // Everything seems to work (e.g., we are running as root and kptr_restrict
92*288bf522SAndroid Build Coastguard Worker     // is < 2). Don't touching anything.
93*288bf522SAndroid Build Coastguard Worker     kallsyms_available_ = true;
94*288bf522SAndroid Build Coastguard Worker     return;
95*288bf522SAndroid Build Coastguard Worker   }
96*288bf522SAndroid Build Coastguard Worker 
97*288bf522SAndroid Build Coastguard Worker   if (GetAndroidVersion() >= 12 && IsRoot()) {
98*288bf522SAndroid Build Coastguard Worker     // Enable kernel addresses by setting property.
99*288bf522SAndroid Build Coastguard Worker     if (!android::base::SetProperty(kLowerPtrRestrictAndroidProp, "1")) {
100*288bf522SAndroid Build Coastguard Worker       LOG(DEBUG) << "Unable to set " << kLowerPtrRestrictAndroidProp << " to 1.";
101*288bf522SAndroid Build Coastguard Worker       PrintWarning();
102*288bf522SAndroid Build Coastguard Worker       return;
103*288bf522SAndroid Build Coastguard Worker     }
104*288bf522SAndroid Build Coastguard Worker     restore_property_ = true;
105*288bf522SAndroid Build Coastguard Worker     // Init takes some time to react to the property change.
106*288bf522SAndroid Build Coastguard Worker     // Unfortunately, we cannot read kptr_restrict because of SELinux. Instead,
107*288bf522SAndroid Build Coastguard Worker     // we detect this by reading the initial lines of kallsyms and checking
108*288bf522SAndroid Build Coastguard Worker     // that they are non-zero. This loop waits for at most 250ms (50 * 5ms).
109*288bf522SAndroid Build Coastguard Worker     for (int attempt = 1; attempt <= 50; ++attempt) {
110*288bf522SAndroid Build Coastguard Worker       usleep(5000);
111*288bf522SAndroid Build Coastguard Worker       if (CanReadKernelSymbolAddresses()) {
112*288bf522SAndroid Build Coastguard Worker         kallsyms_available_ = true;
113*288bf522SAndroid Build Coastguard Worker         return;
114*288bf522SAndroid Build Coastguard Worker       }
115*288bf522SAndroid Build Coastguard Worker     }
116*288bf522SAndroid Build Coastguard Worker     LOG(DEBUG) << "kallsyms addresses are still masked after setting "
117*288bf522SAndroid Build Coastguard Worker                << kLowerPtrRestrictAndroidProp;
118*288bf522SAndroid Build Coastguard Worker     PrintWarning();
119*288bf522SAndroid Build Coastguard Worker     return;
120*288bf522SAndroid Build Coastguard Worker   }
121*288bf522SAndroid Build Coastguard Worker 
122*288bf522SAndroid Build Coastguard Worker   // Otherwise, read the kptr_restrict value and lower it if needed.
123*288bf522SAndroid Build Coastguard Worker   if (!android::base::ReadFileToString(kPtrRestrictPath, &saved_restrict_value_)) {
124*288bf522SAndroid Build Coastguard Worker     LOG(DEBUG) << "Failed to read " << kPtrRestrictPath;
125*288bf522SAndroid Build Coastguard Worker     PrintWarning();
126*288bf522SAndroid Build Coastguard Worker     return;
127*288bf522SAndroid Build Coastguard Worker   }
128*288bf522SAndroid Build Coastguard Worker 
129*288bf522SAndroid Build Coastguard Worker   // Progressively lower kptr_restrict until we can read kallsyms.
130*288bf522SAndroid Build Coastguard Worker   for (int value = atoi(saved_restrict_value_.c_str()); value > 0; --value) {
131*288bf522SAndroid Build Coastguard Worker     if (!WriteKptrRestrict(std::to_string(value))) {
132*288bf522SAndroid Build Coastguard Worker       break;
133*288bf522SAndroid Build Coastguard Worker     }
134*288bf522SAndroid Build Coastguard Worker     restore_restrict_value_ = true;
135*288bf522SAndroid Build Coastguard Worker     if (CanReadKernelSymbolAddresses()) {
136*288bf522SAndroid Build Coastguard Worker       kallsyms_available_ = true;
137*288bf522SAndroid Build Coastguard Worker       return;
138*288bf522SAndroid Build Coastguard Worker     }
139*288bf522SAndroid Build Coastguard Worker   }
140*288bf522SAndroid Build Coastguard Worker   PrintWarning();
141*288bf522SAndroid Build Coastguard Worker }
142*288bf522SAndroid Build Coastguard Worker 
~ScopedKptrUnrestrict()143*288bf522SAndroid Build Coastguard Worker ScopedKptrUnrestrict::~ScopedKptrUnrestrict() {
144*288bf522SAndroid Build Coastguard Worker   if (restore_property_) {
145*288bf522SAndroid Build Coastguard Worker     android::base::SetProperty(kLowerPtrRestrictAndroidProp, "0");
146*288bf522SAndroid Build Coastguard Worker   }
147*288bf522SAndroid Build Coastguard Worker   if (restore_restrict_value_) {
148*288bf522SAndroid Build Coastguard Worker     WriteKptrRestrict(saved_restrict_value_);
149*288bf522SAndroid Build Coastguard Worker   }
150*288bf522SAndroid Build Coastguard Worker }
151*288bf522SAndroid Build Coastguard Worker 
WriteKptrRestrict(const std::string & value)152*288bf522SAndroid Build Coastguard Worker bool ScopedKptrUnrestrict::WriteKptrRestrict(const std::string& value) {
153*288bf522SAndroid Build Coastguard Worker   if (!android::base::WriteStringToFile(value, kPtrRestrictPath)) {
154*288bf522SAndroid Build Coastguard Worker     LOG(DEBUG) << "Failed to set " << kPtrRestrictPath << " to " << value;
155*288bf522SAndroid Build Coastguard Worker     return false;
156*288bf522SAndroid Build Coastguard Worker   }
157*288bf522SAndroid Build Coastguard Worker   return true;
158*288bf522SAndroid Build Coastguard Worker }
159*288bf522SAndroid Build Coastguard Worker 
PrintWarning()160*288bf522SAndroid Build Coastguard Worker void ScopedKptrUnrestrict::PrintWarning() {
161*288bf522SAndroid Build Coastguard Worker   if (!kernel_address_warning_printed_) {
162*288bf522SAndroid Build Coastguard Worker     kernel_address_warning_printed_ = true;
163*288bf522SAndroid Build Coastguard Worker     LOG(WARNING) << "Access to kernel symbol addresses is restricted. If "
164*288bf522SAndroid Build Coastguard Worker                  << "possible, please do `echo 0 >/proc/sys/kernel/kptr_restrict` "
165*288bf522SAndroid Build Coastguard Worker                  << "to fix this.";
166*288bf522SAndroid Build Coastguard Worker   }
167*288bf522SAndroid Build Coastguard Worker }
168*288bf522SAndroid Build Coastguard Worker 
169*288bf522SAndroid Build Coastguard Worker }  // namespace
170*288bf522SAndroid Build Coastguard Worker 
GetLoadedModules()171*288bf522SAndroid Build Coastguard Worker std::vector<KernelMmap> GetLoadedModules() {
172*288bf522SAndroid Build Coastguard Worker   ScopedKptrUnrestrict kptr_unrestrict;
173*288bf522SAndroid Build Coastguard Worker   if (!kptr_unrestrict.KallsymsAvailable()) return {};
174*288bf522SAndroid Build Coastguard Worker   std::vector<KernelMmap> result;
175*288bf522SAndroid Build Coastguard Worker   LineReader reader(kProcModulesPath);
176*288bf522SAndroid Build Coastguard Worker   if (!reader.Ok()) {
177*288bf522SAndroid Build Coastguard Worker     // There is no /proc/modules on Android devices, so we don't print error if failed to open it.
178*288bf522SAndroid Build Coastguard Worker     PLOG(DEBUG) << "failed to open file /proc/modules";
179*288bf522SAndroid Build Coastguard Worker     return result;
180*288bf522SAndroid Build Coastguard Worker   }
181*288bf522SAndroid Build Coastguard Worker   std::string* line;
182*288bf522SAndroid Build Coastguard Worker   std::string name_buf;
183*288bf522SAndroid Build Coastguard Worker   while ((line = reader.ReadLine()) != nullptr) {
184*288bf522SAndroid Build Coastguard Worker     // Parse line like: nf_defrag_ipv6 34768 1 nf_conntrack_ipv6, Live 0xffffffffa0fe5000
185*288bf522SAndroid Build Coastguard Worker     name_buf.resize(line->size());
186*288bf522SAndroid Build Coastguard Worker     char* name = name_buf.data();
187*288bf522SAndroid Build Coastguard Worker     uint64_t addr;
188*288bf522SAndroid Build Coastguard Worker     uint64_t len;
189*288bf522SAndroid Build Coastguard Worker     if (sscanf(line->data(), "%s%" PRIu64 "%*u%*s%*s 0x%" PRIx64, name, &len, &addr) == 3) {
190*288bf522SAndroid Build Coastguard Worker       KernelMmap map;
191*288bf522SAndroid Build Coastguard Worker       map.name = name;
192*288bf522SAndroid Build Coastguard Worker       map.start_addr = addr;
193*288bf522SAndroid Build Coastguard Worker       map.len = len;
194*288bf522SAndroid Build Coastguard Worker       result.push_back(map);
195*288bf522SAndroid Build Coastguard Worker     }
196*288bf522SAndroid Build Coastguard Worker   }
197*288bf522SAndroid Build Coastguard Worker   bool all_zero = true;
198*288bf522SAndroid Build Coastguard Worker   for (const auto& map : result) {
199*288bf522SAndroid Build Coastguard Worker     if (map.start_addr != 0) {
200*288bf522SAndroid Build Coastguard Worker       all_zero = false;
201*288bf522SAndroid Build Coastguard Worker     }
202*288bf522SAndroid Build Coastguard Worker   }
203*288bf522SAndroid Build Coastguard Worker   if (all_zero) {
204*288bf522SAndroid Build Coastguard Worker     LOG(DEBUG) << "addresses in /proc/modules are all zero, so ignore kernel modules";
205*288bf522SAndroid Build Coastguard Worker     return std::vector<KernelMmap>();
206*288bf522SAndroid Build Coastguard Worker   }
207*288bf522SAndroid Build Coastguard Worker   return result;
208*288bf522SAndroid Build Coastguard Worker }
209*288bf522SAndroid Build Coastguard Worker 
GetKernelStartAddress()210*288bf522SAndroid Build Coastguard Worker uint64_t GetKernelStartAddress() {
211*288bf522SAndroid Build Coastguard Worker   ScopedKptrUnrestrict kptr_unrestrict;
212*288bf522SAndroid Build Coastguard Worker   if (!kptr_unrestrict.KallsymsAvailable()) return 0;
213*288bf522SAndroid Build Coastguard Worker   LineReader reader(kKallsymsPath);
214*288bf522SAndroid Build Coastguard Worker   if (!reader.Ok()) {
215*288bf522SAndroid Build Coastguard Worker     return 0;
216*288bf522SAndroid Build Coastguard Worker   }
217*288bf522SAndroid Build Coastguard Worker   std::string* line;
218*288bf522SAndroid Build Coastguard Worker   while ((line = reader.ReadLine()) != nullptr) {
219*288bf522SAndroid Build Coastguard Worker     if (strstr(line->data(), "_stext") != nullptr) {
220*288bf522SAndroid Build Coastguard Worker       uint64_t addr;
221*288bf522SAndroid Build Coastguard Worker       if (sscanf(line->data(), "%" PRIx64, &addr) == 1) {
222*288bf522SAndroid Build Coastguard Worker         return addr;
223*288bf522SAndroid Build Coastguard Worker       }
224*288bf522SAndroid Build Coastguard Worker     }
225*288bf522SAndroid Build Coastguard Worker   }
226*288bf522SAndroid Build Coastguard Worker   return 0;
227*288bf522SAndroid Build Coastguard Worker }
228*288bf522SAndroid Build Coastguard Worker 
LoadKernelSymbols(std::string * kallsyms)229*288bf522SAndroid Build Coastguard Worker bool LoadKernelSymbols(std::string* kallsyms) {
230*288bf522SAndroid Build Coastguard Worker   ScopedKptrUnrestrict kptr_unrestrict;
231*288bf522SAndroid Build Coastguard Worker   if (kptr_unrestrict.KallsymsAvailable()) {
232*288bf522SAndroid Build Coastguard Worker     return android::base::ReadFileToString(kKallsymsPath, kallsyms);
233*288bf522SAndroid Build Coastguard Worker   }
234*288bf522SAndroid Build Coastguard Worker   return false;
235*288bf522SAndroid Build Coastguard Worker }
236*288bf522SAndroid Build Coastguard Worker 
ResetKernelAddressWarning()237*288bf522SAndroid Build Coastguard Worker void ResetKernelAddressWarning() {
238*288bf522SAndroid Build Coastguard Worker   ScopedKptrUnrestrict::ResetWarning();
239*288bf522SAndroid Build Coastguard Worker }
240*288bf522SAndroid Build Coastguard Worker 
241*288bf522SAndroid Build Coastguard Worker #endif  // defined(__linux__)
242*288bf522SAndroid Build Coastguard Worker 
ProcessKernelSymbols(std::string & symbol_data,const std::function<bool (const KernelSymbol &)> & callback)243*288bf522SAndroid Build Coastguard Worker bool ProcessKernelSymbols(std::string& symbol_data,
244*288bf522SAndroid Build Coastguard Worker                           const std::function<bool(const KernelSymbol&)>& callback) {
245*288bf522SAndroid Build Coastguard Worker   char* p = &symbol_data[0];
246*288bf522SAndroid Build Coastguard Worker   char* data_end = p + symbol_data.size();
247*288bf522SAndroid Build Coastguard Worker   while (p < data_end) {
248*288bf522SAndroid Build Coastguard Worker     char* line_end = strchr(p, '\n');
249*288bf522SAndroid Build Coastguard Worker     if (line_end != nullptr) {
250*288bf522SAndroid Build Coastguard Worker       *line_end = '\0';
251*288bf522SAndroid Build Coastguard Worker     }
252*288bf522SAndroid Build Coastguard Worker     size_t line_size = (line_end != nullptr) ? (line_end - p) : (data_end - p);
253*288bf522SAndroid Build Coastguard Worker     // Parse line like: ffffffffa005c4e4 d __warned.41698       [libsas]
254*288bf522SAndroid Build Coastguard Worker     char name[line_size];
255*288bf522SAndroid Build Coastguard Worker     char module[line_size];
256*288bf522SAndroid Build Coastguard Worker     strcpy(module, "");
257*288bf522SAndroid Build Coastguard Worker 
258*288bf522SAndroid Build Coastguard Worker     KernelSymbol symbol;
259*288bf522SAndroid Build Coastguard Worker     int ret = sscanf(p, "%" PRIx64 " %c %s%s", &symbol.addr, &symbol.type, name, module);
260*288bf522SAndroid Build Coastguard Worker     if (line_end != nullptr) {
261*288bf522SAndroid Build Coastguard Worker       *line_end = '\n';
262*288bf522SAndroid Build Coastguard Worker       p = line_end + 1;
263*288bf522SAndroid Build Coastguard Worker     } else {
264*288bf522SAndroid Build Coastguard Worker       p = data_end;
265*288bf522SAndroid Build Coastguard Worker     }
266*288bf522SAndroid Build Coastguard Worker     if (ret >= 3) {
267*288bf522SAndroid Build Coastguard Worker       if (IsArmMappingSymbol(name)) {
268*288bf522SAndroid Build Coastguard Worker         continue;
269*288bf522SAndroid Build Coastguard Worker       }
270*288bf522SAndroid Build Coastguard Worker 
271*288bf522SAndroid Build Coastguard Worker       symbol.name = name;
272*288bf522SAndroid Build Coastguard Worker       size_t module_len = strlen(module);
273*288bf522SAndroid Build Coastguard Worker       if (module_len > 2 && module[0] == '[' && module[module_len - 1] == ']') {
274*288bf522SAndroid Build Coastguard Worker         module[module_len - 1] = '\0';
275*288bf522SAndroid Build Coastguard Worker         symbol.module = &module[1];
276*288bf522SAndroid Build Coastguard Worker       } else {
277*288bf522SAndroid Build Coastguard Worker         symbol.module = nullptr;
278*288bf522SAndroid Build Coastguard Worker       }
279*288bf522SAndroid Build Coastguard Worker 
280*288bf522SAndroid Build Coastguard Worker       if (callback(symbol)) {
281*288bf522SAndroid Build Coastguard Worker         return true;
282*288bf522SAndroid Build Coastguard Worker       }
283*288bf522SAndroid Build Coastguard Worker     }
284*288bf522SAndroid Build Coastguard Worker   }
285*288bf522SAndroid Build Coastguard Worker   return false;
286*288bf522SAndroid Build Coastguard Worker }
287*288bf522SAndroid Build Coastguard Worker 
288*288bf522SAndroid Build Coastguard Worker }  // namespace simpleperf
289