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 <memory>
23 #include <limits>
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 "utils.h"
32 
33 namespace simpleperf {
34 
35 static constexpr bool ETM_RECORD_TIMESTAMP = false;
36 
37 // Config bits from include/linux/coresight-pmu.h in the kernel
38 // For etm_event_config:
39 static constexpr int ETM_OPT_CTXTID = 14;
40 static constexpr int ETM_OPT_TS = 28;
41 // For etm_config_reg:
42 static constexpr int ETM4_CFG_BIT_CTXTID = 6;
43 static constexpr int ETM4_CFG_BIT_TS = 11;
44 
45 static const std::string ETM_DIR = "/sys/bus/event_source/devices/cs_etm/";
46 
47 // from coresight_get_trace_id(int cpu) in include/linux/coresight-pmu.h
GetTraceId(int cpu)48 static int GetTraceId(int cpu) {
49   return 0x10 + cpu * 2;
50 }
51 
52 template <typename T>
ReadValueInEtmDir(const std::string & file,T * value,bool report_error=true)53 static bool ReadValueInEtmDir(const std::string& file, T* value, bool report_error = true) {
54   std::string s;
55   uint64_t v;
56   if (!android::base::ReadFileToString(ETM_DIR + file, &s) ||
57       !android::base::ParseUint(android::base::Trim(s), &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 
GetInstance()83 ETMRecorder& ETMRecorder::GetInstance() {
84   static ETMRecorder etm;
85   return etm;
86 }
87 
GetEtmEventType()88 int ETMRecorder::GetEtmEventType() {
89   if (event_type_ == 0) {
90     if (!IsDir(ETM_DIR) || !ReadValueInEtmDir("type", &event_type_, false)) {
91       event_type_ = -1;
92     }
93   }
94   return event_type_;
95 }
96 
BuildEventType()97 std::unique_ptr<EventType> ETMRecorder::BuildEventType() {
98   int etm_event_type = GetEtmEventType();
99   if (etm_event_type == -1) {
100     return nullptr;
101   }
102   return std::make_unique<EventType>(
103       "cs-etm", etm_event_type, 0, "CoreSight ETM instruction tracing", "arm");
104 }
105 
CheckEtmSupport()106 bool ETMRecorder::CheckEtmSupport() {
107   if (GetEtmEventType() == -1) {
108     LOG(ERROR) << "etm event type isn't supported on device";
109     return false;
110   }
111   if (!ReadEtmInfo()) {
112     LOG(ERROR) << "etm devices are not available";
113     return false;
114   }
115   for (const auto& p : etm_info_) {
116     if (p.second.GetMajorVersion() < 4) {
117       LOG(ERROR) << "etm device version is less than 4.0";
118       return false;
119     }
120     if (!p.second.IsContextIDSupported()) {
121       LOG(ERROR) << "etm device doesn't support contextID";
122       return false;
123     }
124   }
125   if (!FindSinkConfig()) {
126     LOG(ERROR) << "can't find etr device, which moves etm data to memory";
127     return false;
128   }
129   etm_supported_ = true;
130   return true;
131 }
132 
ReadEtmInfo()133 bool ETMRecorder::ReadEtmInfo() {
134   int cpu_count = get_nprocs_conf();
135   for (const auto &name : GetEntriesInDir(ETM_DIR)) {
136     int cpu;
137     if (sscanf(name.c_str(), "cpu%d", &cpu) == 1) {
138       ETMPerCpu &cpu_info = etm_info_[cpu];
139       bool success =
140           ReadValueInEtmDir(name + "/trcidr/trcidr0", &cpu_info.trcidr0) &&
141           ReadValueInEtmDir(name + "/trcidr/trcidr1", &cpu_info.trcidr1) &&
142           ReadValueInEtmDir(name + "/trcidr/trcidr2", &cpu_info.trcidr2) &&
143           ReadValueInEtmDir(name + "/trcidr/trcidr4", &cpu_info.trcidr4) &&
144           ReadValueInEtmDir(name + "/trcidr/trcidr8", &cpu_info.trcidr8) &&
145           ReadValueInEtmDir(name + "/mgmt/trcauthstatus", &cpu_info.trcauthstatus);
146       if (!success) {
147         return false;
148       }
149     }
150   }
151   return (etm_info_.size() == cpu_count);
152 }
153 
FindSinkConfig()154 bool ETMRecorder::FindSinkConfig() {
155   for (const auto &name : GetEntriesInDir(ETM_DIR + "sinks")) {
156     if (name.find("etr") != -1) {
157       if (ReadValueInEtmDir("sinks/" + name, &sink_config_)) {
158         return true;
159       }
160     }
161   }
162   return false;
163 }
164 
SetEtmPerfEventAttr(perf_event_attr * attr)165 void ETMRecorder::SetEtmPerfEventAttr(perf_event_attr* attr) {
166   CHECK(etm_supported_);
167   BuildEtmConfig();
168   attr->config = etm_event_config_;
169   attr->config2 = sink_config_;
170 }
171 
BuildEtmConfig()172 void ETMRecorder::BuildEtmConfig() {
173   if (etm_event_config_ == 0) {
174     etm_event_config_ |= 1ULL << ETM_OPT_CTXTID;
175     etm_config_reg_ |= 1U << ETM4_CFG_BIT_CTXTID;
176 
177     if (ETM_RECORD_TIMESTAMP) {
178       bool ts_supported = true;
179       for (auto& p : etm_info_) {
180         ts_supported &= p.second.IsTimestampSupported();
181       }
182       if (ts_supported) {
183         etm_event_config_ |= 1ULL << ETM_OPT_TS;
184         etm_config_reg_ |= 1U << ETM4_CFG_BIT_TS;
185       }
186     }
187   }
188 }
189 
CreateAuxTraceInfoRecord()190 AuxTraceInfoRecord ETMRecorder::CreateAuxTraceInfoRecord() {
191   AuxTraceInfoRecord::DataType data;
192   memset(&data, 0, sizeof(data));
193   data.aux_type = AuxTraceInfoRecord::AUX_TYPE_ETM;
194   data.nr_cpu = etm_info_.size();
195   data.pmu_type = GetEtmEventType();
196   std::vector<AuxTraceInfoRecord::ETM4Info> etm4_v(etm_info_.size());
197   size_t pos = 0;
198   for (auto& p : etm_info_) {
199     auto& e = etm4_v[pos++];
200     e.magic = AuxTraceInfoRecord::MAGIC_ETM4;
201     e.cpu = p.first;
202     e.trcconfigr = etm_config_reg_;
203     e.trctraceidr = GetTraceId(p.first);
204     e.trcidr0 = p.second.trcidr0;
205     e.trcidr1 = p.second.trcidr1;
206     e.trcidr2 = p.second.trcidr2;
207     e.trcidr8 = p.second.trcidr8;
208     e.trcauthstatus = p.second.trcauthstatus;
209   }
210   return AuxTraceInfoRecord(data, etm4_v);
211 }
212 
GetAddrFilterPairs()213 size_t ETMRecorder::GetAddrFilterPairs() {
214   CHECK(etm_supported_);
215   size_t min_pairs = std::numeric_limits<size_t>::max();
216   for (auto& p : etm_info_) {
217     min_pairs = std::min<size_t>(min_pairs, GetBits(p.second.trcidr4, 0, 3));
218   }
219   if (min_pairs > 0) {
220     --min_pairs;  // One pair is used by the kernel to set default addr filter.
221   }
222   return min_pairs;
223 }
224 
225 }  // namespace simpleperf