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 48 static int GetTraceId(int cpu) { 49 return 0x10 + cpu * 2; 50 } 51 52 template <typename T> 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 67 static uint32_t GetBits(uint32_t value, int start, int end) { 68 return (value >> start) & ((1U << (end - start + 1)) - 1); 69 } 70 71 int ETMPerCpu::GetMajorVersion() const { 72 return GetBits(trcidr1, 8, 11); 73 } 74 75 bool ETMPerCpu::IsContextIDSupported() const { 76 return GetBits(trcidr2, 5, 9) >= 4; 77 } 78 79 bool ETMPerCpu::IsTimestampSupported() const { 80 return GetBits(trcidr0, 24, 28) > 0; 81 } 82 83 ETMRecorder& ETMRecorder::GetInstance() { 84 static ETMRecorder etm; 85 return etm; 86 } 87 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 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 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 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 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 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 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 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 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