1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "ETMRecorder.h"
18 
19 #include <stdio.h>
20 #include <sys/sysinfo.h>
21 
22 #include <limits>
23 #include <memory>
24 #include <string>
25 
26 #include <android-base/expected.h>
27 #include <android-base/file.h>
28 #include <android-base/logging.h>
29 #include <android-base/parseint.h>
30 #include <android-base/properties.h>
31 #include <android-base/strings.h>
32 
33 #include "ETMConstants.h"
34 #include "environment.h"
35 #include "utils.h"
36 
37 namespace simpleperf {
38 
39 using android::base::expected;
40 using android::base::unexpected;
41 
42 static const std::string ETM_DIR = "/sys/bus/event_source/devices/cs_etm/";
43 
44 // from coresight_get_trace_id(int cpu) in include/linux/coresight-pmu.h
GetTraceId(int cpu)45 static int GetTraceId(int cpu) {
46   return 0x10 + cpu * 2;
47 }
48 
49 template <typename T>
ReadValueInEtmDir(const std::string & file,T * value,bool report_error=true,const std::string & prefix="")50 static bool ReadValueInEtmDir(const std::string& file, T* value, bool report_error = true,
51                               const std::string& prefix = "") {
52   std::string s;
53   uint64_t v;
54   if (!android::base::ReadFileToString(ETM_DIR + file, &s) ||
55       !android::base::StartsWith(s, prefix) ||
56       !android::base::ParseUint(&android::base::Trim(s)[prefix.size()], &v)) {
57     if (report_error) {
58       LOG(ERROR) << "failed to read " << ETM_DIR << file;
59     }
60     return false;
61   }
62   *value = static_cast<T>(v);
63   return true;
64 }
65 
GetBits(uint32_t value,int start,int end)66 static uint32_t GetBits(uint32_t value, int start, int end) {
67   return (value >> start) & ((1U << (end - start + 1)) - 1);
68 }
69 
GetMajorVersion() const70 int ETMPerCpu::GetMajorVersion() const {
71   return GetBits(trcidr1, 8, 11);
72 }
73 
IsContextIDSupported() const74 bool ETMPerCpu::IsContextIDSupported() const {
75   return GetBits(trcidr2, 5, 9) >= 4;
76 }
77 
IsTimestampSupported() const78 bool ETMPerCpu::IsTimestampSupported() const {
79   return GetBits(trcidr0, 24, 28) > 0;
80 }
81 
IsCycAccSupported() const82 bool ETMPerCpu::IsCycAccSupported() const {
83   return GetBits(trcidr0, 7, 7);
84 }
85 
IsEnabled() const86 bool ETMPerCpu::IsEnabled() const {
87   return GetBits(trcauthstatus, 0, 3) == 0xc;
88 }
89 
GetInstance()90 ETMRecorder& ETMRecorder::GetInstance() {
91   static ETMRecorder etm;
92   return etm;
93 }
94 
GetEtmEventType()95 int ETMRecorder::GetEtmEventType() {
96   if (event_type_ == 0) {
97     if (!IsDir(ETM_DIR) || !ReadValueInEtmDir("type", &event_type_, false)) {
98       event_type_ = -1;
99     }
100   }
101   return event_type_;
102 }
103 
BuildEventType()104 std::unique_ptr<EventType> ETMRecorder::BuildEventType() {
105   int etm_event_type = GetEtmEventType();
106   if (etm_event_type == -1) {
107     return nullptr;
108   }
109   return std::make_unique<EventType>("cs-etm", etm_event_type, 0,
110                                      "CoreSight ETM instruction tracing", "arm");
111 }
112 
IsETMDriverAvailable()113 bool ETMRecorder::IsETMDriverAvailable() {
114   return IsDir(ETM_DIR);
115 }
116 
CheckEtmSupport()117 expected<bool, std::string> ETMRecorder::CheckEtmSupport() {
118   if (GetEtmEventType() == -1) {
119     return unexpected("etm event type isn't supported on device");
120   }
121   if (!ReadEtmInfo()) {
122     return unexpected("etm devices are not available");
123   }
124   for (const auto& p : etm_info_) {
125     if (p.second.GetMajorVersion() < 4) {
126       return unexpected("etm device version is less than 4.0");
127     }
128     if (!p.second.IsContextIDSupported()) {
129       return unexpected("etm device doesn't support contextID");
130     }
131     if (!p.second.IsEnabled()) {
132       return unexpected("etm device isn't enabled by the bootloader");
133     }
134   }
135   if (!FindSinkConfig()) {
136     // Trigger a manual probe of etr. Then wait and recheck.
137     std::string prop_name = "profcollectd.etr.probe";
138     bool res = android::base::SetProperty(prop_name, "1");
139     if (!res) {
140       LOG(ERROR) << "fails to setprop" << prop_name;
141     }
142     usleep(200000);  // Wait for 200ms.
143     if (!FindSinkConfig()) {
144       return unexpected("can't find etr device, which moves etm data to memory");
145     }
146   }
147   etm_supported_ = true;
148   return true;
149 }
150 
ReadEtmInfo()151 bool ETMRecorder::ReadEtmInfo() {
152   int contextid_value;
153   use_contextid2_ = ReadValueInEtmDir("/format/contextid", &contextid_value, false, "config:") &&
154                     contextid_value == ETM_OPT_CTXTID2;
155 
156   std::vector<int> online_cpus = GetOnlineCpus();
157   for (const auto& name : GetEntriesInDir(ETM_DIR)) {
158     int cpu;
159     if (sscanf(name.c_str(), "cpu%d", &cpu) == 1) {
160       // We can't read ETM registers for offline cpus. So skip them.
161       if (std::find(online_cpus.begin(), online_cpus.end(), cpu) == online_cpus.end()) {
162         continue;
163       }
164       ETMPerCpu& cpu_info = etm_info_[cpu];
165       bool success = ReadValueInEtmDir(name + "/trcidr/trcidr0", &cpu_info.trcidr0) &&
166                      ReadValueInEtmDir(name + "/trcidr/trcidr1", &cpu_info.trcidr1) &&
167                      ReadValueInEtmDir(name + "/trcidr/trcidr2", &cpu_info.trcidr2) &&
168                      ReadValueInEtmDir(name + "/trcidr/trcidr4", &cpu_info.trcidr4) &&
169                      ReadValueInEtmDir(name + "/trcidr/trcidr8", &cpu_info.trcidr8) &&
170                      ReadValueInEtmDir(name + "/mgmt/trcauthstatus", &cpu_info.trcauthstatus);
171 
172       if (!ReadValueInEtmDir(name + "/mgmt/trcdevarch", &cpu_info.trcdevarch, false)) {
173         cpu_info.trcdevarch = 0;
174       }
175       if (!success) {
176         return false;
177       }
178     }
179   }
180   return (etm_info_.size() == online_cpus.size());
181 }
182 
FindSinkConfig()183 bool ETMRecorder::FindSinkConfig() {
184   bool has_etr = false;
185   bool has_trbe = false;
186   for (const auto& name : GetEntriesInDir(ETM_DIR + "sinks")) {
187     if (!has_etr && name.find("etr") != -1) {
188       if (ReadValueInEtmDir("sinks/" + name, &sink_config_)) {
189         has_etr = true;
190       }
191     }
192     if (name.find("trbe") != -1) {
193       has_trbe = true;
194       break;
195     }
196   }
197   if (has_trbe) {
198     // When TRBE is present, let the driver choose the most suitable
199     // configuration.
200     sink_config_ = 0;
201   }
202   return has_trbe || has_etr;
203 }
204 
SetEtmPerfEventAttr(perf_event_attr * attr)205 void ETMRecorder::SetEtmPerfEventAttr(perf_event_attr* attr) {
206   CHECK(etm_supported_);
207   BuildEtmConfig();
208   attr->config = etm_event_config_;
209   attr->config2 = sink_config_;
210   attr->config3 = cc_threshold_config_;
211 }
212 
BuildEtmConfig()213 void ETMRecorder::BuildEtmConfig() {
214   if (etm_event_config_ == 0) {
215     if (use_contextid2_) {
216       etm_event_config_ |= 1ULL << ETM_OPT_CTXTID2;
217       etm_config_reg_ |= 1U << ETM4_CFG_BIT_VMID;
218       etm_config_reg_ |= 1U << ETM4_CFG_BIT_VMID_OPT;
219     } else {
220       etm_event_config_ |= 1ULL << ETM_OPT_CTXTID;
221       etm_config_reg_ |= 1U << ETM4_CFG_BIT_CTXTID;
222     }
223 
224     if (record_timestamp_) {
225       bool ts_supported = true;
226       for (auto& p : etm_info_) {
227         ts_supported &= p.second.IsTimestampSupported();
228       }
229       if (ts_supported) {
230         etm_event_config_ |= 1ULL << ETM_OPT_TS;
231         etm_config_reg_ |= 1U << ETM4_CFG_BIT_TS;
232       }
233     }
234 
235     if (record_cycles_) {
236       bool cycles_supported = true;
237       for (auto& p : etm_info_) {
238         cycles_supported &= p.second.IsCycAccSupported();
239       }
240       if (cycles_supported) {
241         etm_event_config_ |= 1ULL << ETM_OPT_CYCACC;
242         etm_config_reg_ |= 1U << ETM4_CFG_BIT_CCI;
243 
244         if (cycle_threshold_) {
245           cc_threshold_config_ |= cycle_threshold_;
246         }
247       }
248     }
249   }
250 }
251 
CreateAuxTraceInfoRecord()252 AuxTraceInfoRecord ETMRecorder::CreateAuxTraceInfoRecord() {
253   AuxTraceInfoRecord::DataType data;
254   memset(&data, 0, sizeof(data));
255   data.aux_type = AuxTraceInfoRecord::AUX_TYPE_ETM;
256   data.version = 1;
257   data.nr_cpu = etm_info_.size();
258   data.pmu_type = GetEtmEventType();
259   std::vector<AuxTraceInfoRecord::ETEInfo> ete(etm_info_.size());
260   size_t pos = 0;
261   for (auto& p : etm_info_) {
262     auto& e = ete[pos++];
263     if (p.second.trcdevarch == 0) {
264       e.magic = AuxTraceInfoRecord::MAGIC_ETM4;
265       e.nrtrcparams = sizeof(AuxTraceInfoRecord::ETM4Info) / sizeof(uint64_t) - 3;
266     } else {
267       e.magic = AuxTraceInfoRecord::MAGIC_ETE;
268       e.nrtrcparams = sizeof(AuxTraceInfoRecord::ETEInfo) / sizeof(uint64_t) - 3;
269     }
270     e.cpu = p.first;
271     e.trcconfigr = etm_config_reg_;
272     e.trctraceidr = GetTraceId(p.first);
273     e.trcidr0 = p.second.trcidr0;
274     e.trcidr1 = p.second.trcidr1;
275     e.trcidr2 = p.second.trcidr2;
276     e.trcidr8 = p.second.trcidr8;
277     e.trcauthstatus = p.second.trcauthstatus;
278     e.trcdevarch = p.second.trcdevarch;
279   }
280   return AuxTraceInfoRecord(data, ete);
281 }
282 
GetAddrFilterPairs()283 size_t ETMRecorder::GetAddrFilterPairs() {
284   CHECK(etm_supported_);
285   size_t min_pairs = std::numeric_limits<size_t>::max();
286   for (auto& p : etm_info_) {
287     min_pairs = std::min<size_t>(min_pairs, GetBits(p.second.trcidr4, 0, 3));
288   }
289   if (min_pairs > 0) {
290     --min_pairs;  // One pair is used by the kernel to set default addr filter.
291   }
292   return min_pairs;
293 }
294 
SetRecordTimestamp(bool record)295 void ETMRecorder::SetRecordTimestamp(bool record) {
296   record_timestamp_ = record;
297 }
298 
SetRecordCycles(bool record)299 void ETMRecorder::SetRecordCycles(bool record) {
300   record_cycles_ = record;
301 }
302 
SetCycleThreshold(size_t threshold)303 void ETMRecorder::SetCycleThreshold(size_t threshold) {
304   cycle_threshold_ = threshold;
305 }
306 
307 }  // namespace simpleperf
308