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/file.h>
27 #include <android-base/logging.h>
28 #include <android-base/parseint.h>
29 #include <android-base/strings.h>
30 
31 #include "environment.h"
32 #include "utils.h"
33 
34 namespace simpleperf {
35 
36 static constexpr bool ETM_RECORD_TIMESTAMP = false;
37 
38 // Config bits from include/linux/coresight-pmu.h in the kernel
39 // For etm_event_config:
40 static constexpr int ETM_OPT_CTXTID = 14;
41 static constexpr int ETM_OPT_TS = 28;
42 // For etm_config_reg:
43 static constexpr int ETM4_CFG_BIT_CTXTID = 6;
44 static constexpr int ETM4_CFG_BIT_TS = 11;
45 
46 static const std::string ETM_DIR = "/sys/bus/event_source/devices/cs_etm/";
47 
48 // from coresight_get_trace_id(int cpu) in include/linux/coresight-pmu.h
GetTraceId(int cpu)49 static int GetTraceId(int cpu) {
50   return 0x10 + cpu * 2;
51 }
52 
53 template <typename T>
ReadValueInEtmDir(const std::string & file,T * value,bool report_error=true)54 static bool ReadValueInEtmDir(const std::string& file, T* value, bool report_error = true) {
55   std::string s;
56   uint64_t v;
57   if (!android::base::ReadFileToString(ETM_DIR + file, &s) ||
58       !android::base::ParseUint(android::base::Trim(s), &v)) {
59     if (report_error) {
60       LOG(ERROR) << "failed to read " << ETM_DIR << file;
61     }
62     return false;
63   }
64   *value = static_cast<T>(v);
65   return true;
66 }
67 
GetBits(uint32_t value,int start,int end)68 static uint32_t GetBits(uint32_t value, int start, int end) {
69   return (value >> start) & ((1U << (end - start + 1)) - 1);
70 }
71 
GetMajorVersion() const72 int ETMPerCpu::GetMajorVersion() const {
73   return GetBits(trcidr1, 8, 11);
74 }
75 
IsContextIDSupported() const76 bool ETMPerCpu::IsContextIDSupported() const {
77   return GetBits(trcidr2, 5, 9) >= 4;
78 }
79 
IsTimestampSupported() const80 bool ETMPerCpu::IsTimestampSupported() const {
81   return GetBits(trcidr0, 24, 28) > 0;
82 }
83 
IsEnabled() const84 bool ETMPerCpu::IsEnabled() const {
85   return GetBits(trcauthstatus, 0, 3) == 0xc;
86 }
87 
GetInstance()88 ETMRecorder& ETMRecorder::GetInstance() {
89   static ETMRecorder etm;
90   return etm;
91 }
92 
GetEtmEventType()93 int ETMRecorder::GetEtmEventType() {
94   if (event_type_ == 0) {
95     if (!IsDir(ETM_DIR) || !ReadValueInEtmDir("type", &event_type_, false)) {
96       event_type_ = -1;
97     }
98   }
99   return event_type_;
100 }
101 
BuildEventType()102 std::unique_ptr<EventType> ETMRecorder::BuildEventType() {
103   int etm_event_type = GetEtmEventType();
104   if (etm_event_type == -1) {
105     return nullptr;
106   }
107   return std::make_unique<EventType>("cs-etm", etm_event_type, 0,
108                                      "CoreSight ETM instruction tracing", "arm");
109 }
110 
CheckEtmSupport()111 bool ETMRecorder::CheckEtmSupport() {
112   if (GetEtmEventType() == -1) {
113     LOG(ERROR) << "etm event type isn't supported on device";
114     return false;
115   }
116   if (!ReadEtmInfo()) {
117     LOG(ERROR) << "etm devices are not available";
118     return false;
119   }
120   for (const auto& p : etm_info_) {
121     if (p.second.GetMajorVersion() < 4) {
122       LOG(ERROR) << "etm device version is less than 4.0";
123       return false;
124     }
125     if (!p.second.IsContextIDSupported()) {
126       LOG(ERROR) << "etm device doesn't support contextID";
127       return false;
128     }
129     if (!p.second.IsEnabled()) {
130       LOG(ERROR) << "etm device isn't enabled by the bootloader";
131       return false;
132     }
133   }
134   if (!FindSinkConfig()) {
135     LOG(ERROR) << "can't find etr device, which moves etm data to memory";
136     return false;
137   }
138   etm_supported_ = true;
139   return true;
140 }
141 
ReadEtmInfo()142 bool ETMRecorder::ReadEtmInfo() {
143   std::vector<int> online_cpus = GetOnlineCpus();
144   for (const auto& name : GetEntriesInDir(ETM_DIR)) {
145     int cpu;
146     if (sscanf(name.c_str(), "cpu%d", &cpu) == 1) {
147       // We can't read ETM registers for offline cpus. So skip them.
148       if (std::find(online_cpus.begin(), online_cpus.end(), cpu) == online_cpus.end()) {
149         continue;
150       }
151       ETMPerCpu& cpu_info = etm_info_[cpu];
152       bool success = ReadValueInEtmDir(name + "/trcidr/trcidr0", &cpu_info.trcidr0) &&
153                      ReadValueInEtmDir(name + "/trcidr/trcidr1", &cpu_info.trcidr1) &&
154                      ReadValueInEtmDir(name + "/trcidr/trcidr2", &cpu_info.trcidr2) &&
155                      ReadValueInEtmDir(name + "/trcidr/trcidr4", &cpu_info.trcidr4) &&
156                      ReadValueInEtmDir(name + "/trcidr/trcidr8", &cpu_info.trcidr8) &&
157                      ReadValueInEtmDir(name + "/mgmt/trcauthstatus", &cpu_info.trcauthstatus);
158       if (!success) {
159         return false;
160       }
161     }
162   }
163   return (etm_info_.size() == online_cpus.size());
164 }
165 
FindSinkConfig()166 bool ETMRecorder::FindSinkConfig() {
167   bool has_etr = false;
168   bool has_trbe = false;
169   for (const auto& name : GetEntriesInDir(ETM_DIR + "sinks")) {
170     if (!has_etr && name.find("etr") != -1) {
171       if (ReadValueInEtmDir("sinks/" + name, &sink_config_)) {
172         has_etr = true;
173       }
174     }
175     if (name.find("trbe") != -1) {
176       has_trbe = true;
177       break;
178     }
179   }
180   if (has_trbe) {
181     // When TRBE is present, let the driver choose the most suitable
182     // configuration.
183     sink_config_ = 0;
184   }
185   return has_trbe || has_etr;
186 }
187 
SetEtmPerfEventAttr(perf_event_attr * attr)188 void ETMRecorder::SetEtmPerfEventAttr(perf_event_attr* attr) {
189   CHECK(etm_supported_);
190   BuildEtmConfig();
191   attr->config = etm_event_config_;
192   attr->config2 = sink_config_;
193 }
194 
BuildEtmConfig()195 void ETMRecorder::BuildEtmConfig() {
196   if (etm_event_config_ == 0) {
197     etm_event_config_ |= 1ULL << ETM_OPT_CTXTID;
198     etm_config_reg_ |= 1U << ETM4_CFG_BIT_CTXTID;
199 
200     if (ETM_RECORD_TIMESTAMP) {
201       bool ts_supported = true;
202       for (auto& p : etm_info_) {
203         ts_supported &= p.second.IsTimestampSupported();
204       }
205       if (ts_supported) {
206         etm_event_config_ |= 1ULL << ETM_OPT_TS;
207         etm_config_reg_ |= 1U << ETM4_CFG_BIT_TS;
208       }
209     }
210   }
211 }
212 
CreateAuxTraceInfoRecord()213 AuxTraceInfoRecord ETMRecorder::CreateAuxTraceInfoRecord() {
214   AuxTraceInfoRecord::DataType data;
215   memset(&data, 0, sizeof(data));
216   data.aux_type = AuxTraceInfoRecord::AUX_TYPE_ETM;
217   data.nr_cpu = etm_info_.size();
218   data.pmu_type = GetEtmEventType();
219   std::vector<AuxTraceInfoRecord::ETM4Info> etm4_v(etm_info_.size());
220   size_t pos = 0;
221   for (auto& p : etm_info_) {
222     auto& e = etm4_v[pos++];
223     e.magic = AuxTraceInfoRecord::MAGIC_ETM4;
224     e.cpu = p.first;
225     e.trcconfigr = etm_config_reg_;
226     e.trctraceidr = GetTraceId(p.first);
227     e.trcidr0 = p.second.trcidr0;
228     e.trcidr1 = p.second.trcidr1;
229     e.trcidr2 = p.second.trcidr2;
230     e.trcidr8 = p.second.trcidr8;
231     e.trcauthstatus = p.second.trcauthstatus;
232   }
233   return AuxTraceInfoRecord(data, etm4_v);
234 }
235 
GetAddrFilterPairs()236 size_t ETMRecorder::GetAddrFilterPairs() {
237   CHECK(etm_supported_);
238   size_t min_pairs = std::numeric_limits<size_t>::max();
239   for (auto& p : etm_info_) {
240     min_pairs = std::min<size_t>(min_pairs, GetBits(p.second.trcidr4, 0, 3));
241   }
242   if (min_pairs > 0) {
243     --min_pairs;  // One pair is used by the kernel to set default addr filter.
244   }
245   return min_pairs;
246 }
247 
248 }  // namespace simpleperf