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