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
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>(
107 "cs-etm", etm_event_type, 0, "CoreSight ETM instruction tracing", "arm");
108 }
109
CheckEtmSupport()110 bool ETMRecorder::CheckEtmSupport() {
111 if (GetEtmEventType() == -1) {
112 LOG(ERROR) << "etm event type isn't supported on device";
113 return false;
114 }
115 if (!ReadEtmInfo()) {
116 LOG(ERROR) << "etm devices are not available";
117 return false;
118 }
119 for (const auto& p : etm_info_) {
120 if (p.second.GetMajorVersion() < 4) {
121 LOG(ERROR) << "etm device version is less than 4.0";
122 return false;
123 }
124 if (!p.second.IsContextIDSupported()) {
125 LOG(ERROR) << "etm device doesn't support contextID";
126 return false;
127 }
128 if (!p.second.IsEnabled()) {
129 LOG(ERROR) << "etm device isn't enabled by the bootloader";
130 return false;
131 }
132 }
133 if (!FindSinkConfig()) {
134 LOG(ERROR) << "can't find etr device, which moves etm data to memory";
135 return false;
136 }
137 etm_supported_ = true;
138 return true;
139 }
140
ReadEtmInfo()141 bool ETMRecorder::ReadEtmInfo() {
142 int cpu_count = get_nprocs_conf();
143 for (const auto &name : GetEntriesInDir(ETM_DIR)) {
144 int cpu;
145 if (sscanf(name.c_str(), "cpu%d", &cpu) == 1) {
146 ETMPerCpu &cpu_info = etm_info_[cpu];
147 bool success =
148 ReadValueInEtmDir(name + "/trcidr/trcidr0", &cpu_info.trcidr0) &&
149 ReadValueInEtmDir(name + "/trcidr/trcidr1", &cpu_info.trcidr1) &&
150 ReadValueInEtmDir(name + "/trcidr/trcidr2", &cpu_info.trcidr2) &&
151 ReadValueInEtmDir(name + "/trcidr/trcidr4", &cpu_info.trcidr4) &&
152 ReadValueInEtmDir(name + "/trcidr/trcidr8", &cpu_info.trcidr8) &&
153 ReadValueInEtmDir(name + "/mgmt/trcauthstatus", &cpu_info.trcauthstatus);
154 if (!success) {
155 return false;
156 }
157 }
158 }
159 return (etm_info_.size() == cpu_count);
160 }
161
FindSinkConfig()162 bool ETMRecorder::FindSinkConfig() {
163 for (const auto &name : GetEntriesInDir(ETM_DIR + "sinks")) {
164 if (name.find("etr") != -1) {
165 if (ReadValueInEtmDir("sinks/" + name, &sink_config_)) {
166 return true;
167 }
168 }
169 }
170 return false;
171 }
172
SetEtmPerfEventAttr(perf_event_attr * attr)173 void ETMRecorder::SetEtmPerfEventAttr(perf_event_attr* attr) {
174 CHECK(etm_supported_);
175 BuildEtmConfig();
176 attr->config = etm_event_config_;
177 attr->config2 = sink_config_;
178 }
179
BuildEtmConfig()180 void ETMRecorder::BuildEtmConfig() {
181 if (etm_event_config_ == 0) {
182 etm_event_config_ |= 1ULL << ETM_OPT_CTXTID;
183 etm_config_reg_ |= 1U << ETM4_CFG_BIT_CTXTID;
184
185 if (ETM_RECORD_TIMESTAMP) {
186 bool ts_supported = true;
187 for (auto& p : etm_info_) {
188 ts_supported &= p.second.IsTimestampSupported();
189 }
190 if (ts_supported) {
191 etm_event_config_ |= 1ULL << ETM_OPT_TS;
192 etm_config_reg_ |= 1U << ETM4_CFG_BIT_TS;
193 }
194 }
195 }
196 }
197
CreateAuxTraceInfoRecord()198 AuxTraceInfoRecord ETMRecorder::CreateAuxTraceInfoRecord() {
199 AuxTraceInfoRecord::DataType data;
200 memset(&data, 0, sizeof(data));
201 data.aux_type = AuxTraceInfoRecord::AUX_TYPE_ETM;
202 data.nr_cpu = etm_info_.size();
203 data.pmu_type = GetEtmEventType();
204 std::vector<AuxTraceInfoRecord::ETM4Info> etm4_v(etm_info_.size());
205 size_t pos = 0;
206 for (auto& p : etm_info_) {
207 auto& e = etm4_v[pos++];
208 e.magic = AuxTraceInfoRecord::MAGIC_ETM4;
209 e.cpu = p.first;
210 e.trcconfigr = etm_config_reg_;
211 e.trctraceidr = GetTraceId(p.first);
212 e.trcidr0 = p.second.trcidr0;
213 e.trcidr1 = p.second.trcidr1;
214 e.trcidr2 = p.second.trcidr2;
215 e.trcidr8 = p.second.trcidr8;
216 e.trcauthstatus = p.second.trcauthstatus;
217 }
218 return AuxTraceInfoRecord(data, etm4_v);
219 }
220
GetAddrFilterPairs()221 size_t ETMRecorder::GetAddrFilterPairs() {
222 CHECK(etm_supported_);
223 size_t min_pairs = std::numeric_limits<size_t>::max();
224 for (auto& p : etm_info_) {
225 min_pairs = std::min<size_t>(min_pairs, GetBits(p.second.trcidr4, 0, 3));
226 }
227 if (min_pairs > 0) {
228 --min_pairs; // One pair is used by the kernel to set default addr filter.
229 }
230 return min_pairs;
231 }
232
233 } // namespace simpleperf