1 /*
2 * Copyright (C) 2023 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 "ETMBranchListFile.h"
18
19 #include "ETMDecoder.h"
20 #include "system/extras/simpleperf/etm_branch_list.pb.h"
21
22 namespace simpleperf {
23
24 static constexpr const char* ETM_BRANCH_LIST_PROTO_MAGIC = "simpleperf:EtmBranchList";
25
BranchToProtoString(const std::vector<bool> & branch)26 std::string BranchToProtoString(const std::vector<bool>& branch) {
27 size_t bytes = (branch.size() + 7) / 8;
28 std::string res(bytes, '\0');
29 for (size_t i = 0; i < branch.size(); i++) {
30 if (branch[i]) {
31 res[i >> 3] |= 1 << (i & 7);
32 }
33 }
34 return res;
35 }
36
ProtoStringToBranch(const std::string & s,size_t bit_size)37 std::vector<bool> ProtoStringToBranch(const std::string& s, size_t bit_size) {
38 std::vector<bool> branch(bit_size, false);
39 for (size_t i = 0; i < bit_size; i++) {
40 if (s[i >> 3] & (1 << (i & 7))) {
41 branch[i] = true;
42 }
43 }
44 return branch;
45 }
46
ToProtoBinaryType(DsoType dso_type)47 static std::optional<proto::ETMBranchList_Binary::BinaryType> ToProtoBinaryType(DsoType dso_type) {
48 switch (dso_type) {
49 case DSO_ELF_FILE:
50 return proto::ETMBranchList_Binary::ELF_FILE;
51 case DSO_KERNEL:
52 return proto::ETMBranchList_Binary::KERNEL;
53 case DSO_KERNEL_MODULE:
54 return proto::ETMBranchList_Binary::KERNEL_MODULE;
55 default:
56 LOG(ERROR) << "unexpected dso type " << dso_type;
57 return std::nullopt;
58 }
59 }
60
BranchListBinaryMapToString(const BranchListBinaryMap & binary_map,std::string & s)61 bool BranchListBinaryMapToString(const BranchListBinaryMap& binary_map, std::string& s) {
62 proto::ETMBranchList branch_list_proto;
63 branch_list_proto.set_magic(ETM_BRANCH_LIST_PROTO_MAGIC);
64 std::vector<char> branch_buf;
65 for (const auto& p : binary_map) {
66 const BinaryKey& key = p.first;
67 const BranchListBinaryInfo& binary = p.second;
68 auto binary_proto = branch_list_proto.add_binaries();
69
70 binary_proto->set_path(key.path);
71 if (!key.build_id.IsEmpty()) {
72 binary_proto->set_build_id(key.build_id.ToString().substr(2));
73 }
74 auto opt_binary_type = ToProtoBinaryType(binary.dso_type);
75 if (!opt_binary_type.has_value()) {
76 return false;
77 }
78 binary_proto->set_type(opt_binary_type.value());
79
80 for (const auto& addr_p : binary.branch_map) {
81 auto addr_proto = binary_proto->add_addrs();
82 addr_proto->set_addr(addr_p.first);
83
84 for (const auto& branch_p : addr_p.second) {
85 const std::vector<bool>& branch = branch_p.first;
86 auto branch_proto = addr_proto->add_branches();
87
88 branch_proto->set_branch(BranchToProtoString(branch));
89 branch_proto->set_branch_size(branch.size());
90 branch_proto->set_count(branch_p.second);
91 }
92 }
93
94 if (binary.dso_type == DSO_KERNEL) {
95 binary_proto->mutable_kernel_info()->set_kernel_start_addr(key.kernel_start_addr);
96 }
97 }
98 if (!branch_list_proto.SerializeToString(&s)) {
99 LOG(ERROR) << "failed to serialize branch list binary map";
100 return false;
101 }
102 return true;
103 }
104
ToDsoType(proto::ETMBranchList_Binary::BinaryType binary_type)105 static std::optional<DsoType> ToDsoType(proto::ETMBranchList_Binary::BinaryType binary_type) {
106 switch (binary_type) {
107 case proto::ETMBranchList_Binary::ELF_FILE:
108 return DSO_ELF_FILE;
109 case proto::ETMBranchList_Binary::KERNEL:
110 return DSO_KERNEL;
111 case proto::ETMBranchList_Binary::KERNEL_MODULE:
112 return DSO_KERNEL_MODULE;
113 default:
114 LOG(ERROR) << "unexpected binary type " << binary_type;
115 return std::nullopt;
116 }
117 }
118
BuildUnorderedBranchMap(const proto::ETMBranchList_Binary & binary_proto)119 static UnorderedBranchMap BuildUnorderedBranchMap(const proto::ETMBranchList_Binary& binary_proto) {
120 UnorderedBranchMap branch_map;
121 for (size_t i = 0; i < binary_proto.addrs_size(); i++) {
122 const auto& addr_proto = binary_proto.addrs(i);
123 auto& b_map = branch_map[addr_proto.addr()];
124 for (size_t j = 0; j < addr_proto.branches_size(); j++) {
125 const auto& branch_proto = addr_proto.branches(j);
126 std::vector<bool> branch =
127 ProtoStringToBranch(branch_proto.branch(), branch_proto.branch_size());
128 b_map[branch] = branch_proto.count();
129 }
130 }
131 return branch_map;
132 }
133
StringToBranchListBinaryMap(const std::string & s,BranchListBinaryMap & binary_map)134 bool StringToBranchListBinaryMap(const std::string& s, BranchListBinaryMap& binary_map) {
135 proto::ETMBranchList branch_list_proto;
136 if (!branch_list_proto.ParseFromString(s)) {
137 PLOG(ERROR) << "failed to read ETMBranchList msg";
138 return false;
139 }
140 if (branch_list_proto.magic() != ETM_BRANCH_LIST_PROTO_MAGIC) {
141 PLOG(ERROR) << "not in format etm_branch_list.proto";
142 return false;
143 }
144
145 for (size_t i = 0; i < branch_list_proto.binaries_size(); i++) {
146 const auto& binary_proto = branch_list_proto.binaries(i);
147 BinaryKey key(binary_proto.path(), BuildId(binary_proto.build_id()));
148 if (binary_proto.has_kernel_info()) {
149 key.kernel_start_addr = binary_proto.kernel_info().kernel_start_addr();
150 }
151 BranchListBinaryInfo& binary = binary_map[key];
152 auto dso_type = ToDsoType(binary_proto.type());
153 if (!dso_type) {
154 LOG(ERROR) << "invalid binary type " << binary_proto.type();
155 return false;
156 }
157 binary.dso_type = dso_type.value();
158 binary.branch_map = BuildUnorderedBranchMap(binary_proto);
159 }
160 return true;
161 }
162
163 class ETMThreadTreeWhenRecording : public ETMThreadTree {
164 public:
ETMThreadTreeWhenRecording(bool dump_maps_from_proc)165 ETMThreadTreeWhenRecording(bool dump_maps_from_proc)
166 : dump_maps_from_proc_(dump_maps_from_proc) {}
167
GetThreadTree()168 ThreadTree& GetThreadTree() { return thread_tree_; }
ExcludePid(pid_t pid)169 void ExcludePid(pid_t pid) { exclude_pid_ = pid; }
170
FindThread(int tid)171 const ThreadEntry* FindThread(int tid) override {
172 const ThreadEntry* thread = thread_tree_.FindThread(tid);
173 if (thread == nullptr) {
174 if (dump_maps_from_proc_) {
175 thread = FindThreadFromProc(tid);
176 }
177 if (thread == nullptr) {
178 return nullptr;
179 }
180 }
181 if (exclude_pid_ && exclude_pid_ == thread->pid) {
182 return nullptr;
183 }
184
185 if (dump_maps_from_proc_) {
186 DumpMapsFromProc(thread->pid);
187 }
188 return thread;
189 }
190
DisableThreadExitRecords()191 void DisableThreadExitRecords() override { thread_tree_.DisableThreadExitRecords(); }
GetKernelMaps()192 const MapSet& GetKernelMaps() override { return thread_tree_.GetKernelMaps(); }
193
194 private:
FindThreadFromProc(int tid)195 const ThreadEntry* FindThreadFromProc(int tid) {
196 std::string comm;
197 pid_t pid;
198 if (ReadThreadNameAndPid(tid, &comm, &pid)) {
199 thread_tree_.SetThreadName(pid, tid, comm);
200 return thread_tree_.FindThread(tid);
201 }
202 return nullptr;
203 }
204
DumpMapsFromProc(int pid)205 void DumpMapsFromProc(int pid) {
206 if (dumped_processes_.count(pid) == 0) {
207 dumped_processes_.insert(pid);
208 std::vector<ThreadMmap> maps;
209 if (GetThreadMmapsInProcess(pid, &maps)) {
210 for (const auto& map : maps) {
211 thread_tree_.AddThreadMap(pid, pid, map.start_addr, map.len, map.pgoff, map.name);
212 }
213 }
214 }
215 }
216
217 ThreadTree thread_tree_;
218 bool dump_maps_from_proc_;
219 std::unordered_set<int> dumped_processes_;
220 std::optional<pid_t> exclude_pid_;
221 };
222
223 class ETMBranchListGeneratorImpl : public ETMBranchListGenerator {
224 public:
ETMBranchListGeneratorImpl(bool dump_maps_from_proc)225 ETMBranchListGeneratorImpl(bool dump_maps_from_proc)
226 : thread_tree_(dump_maps_from_proc), binary_filter_(nullptr) {}
227
SetExcludePid(pid_t pid)228 void SetExcludePid(pid_t pid) override { thread_tree_.ExcludePid(pid); }
SetBinaryFilter(const RegEx * binary_name_regex)229 void SetBinaryFilter(const RegEx* binary_name_regex) override {
230 binary_filter_.SetRegex(binary_name_regex);
231 }
232
233 bool ProcessRecord(const Record& r, bool& consumed) override;
234 BranchListBinaryMap GetBranchListBinaryMap() override;
235
236 private:
237 struct AuxRecordData {
238 uint64_t start;
239 uint64_t end;
240 bool formatted;
AuxRecordDatasimpleperf::ETMBranchListGeneratorImpl::AuxRecordData241 AuxRecordData(uint64_t start, uint64_t end, bool formatted)
242 : start(start), end(end), formatted(formatted) {}
243 };
244
245 struct PerCpuData {
246 std::vector<uint8_t> aux_data;
247 uint64_t data_offset = 0;
248 std::queue<AuxRecordData> aux_records;
249 };
250
251 bool ProcessAuxRecord(const AuxRecord& r);
252 bool ProcessAuxTraceRecord(const AuxTraceRecord& r);
253 void ProcessBranchList(const ETMBranchList& branch_list);
254
255 ETMThreadTreeWhenRecording thread_tree_;
256 uint64_t kernel_map_start_addr_ = 0;
257 BinaryFilter binary_filter_;
258 std::map<uint32_t, PerCpuData> cpu_map_;
259 std::unique_ptr<ETMDecoder> etm_decoder_;
260 std::unordered_map<Dso*, BranchListBinaryInfo> branch_list_binary_map_;
261 };
262
ProcessRecord(const Record & r,bool & consumed)263 bool ETMBranchListGeneratorImpl::ProcessRecord(const Record& r, bool& consumed) {
264 consumed = true; // No need to store any records.
265 uint32_t type = r.type();
266 if (type == PERF_RECORD_AUXTRACE_INFO) {
267 etm_decoder_ = ETMDecoder::Create(*static_cast<const AuxTraceInfoRecord*>(&r), thread_tree_);
268 if (!etm_decoder_) {
269 return false;
270 }
271 etm_decoder_->RegisterCallback(
272 [this](const ETMBranchList& branch) { ProcessBranchList(branch); });
273 return true;
274 }
275 if (type == PERF_RECORD_AUX) {
276 return ProcessAuxRecord(*static_cast<const AuxRecord*>(&r));
277 }
278 if (type == PERF_RECORD_AUXTRACE) {
279 return ProcessAuxTraceRecord(*static_cast<const AuxTraceRecord*>(&r));
280 }
281 if (type == PERF_RECORD_MMAP && r.InKernel()) {
282 auto& mmap_r = *static_cast<const MmapRecord*>(&r);
283 if (android::base::StartsWith(mmap_r.filename, DEFAULT_KERNEL_MMAP_NAME)) {
284 kernel_map_start_addr_ = mmap_r.data->addr;
285 }
286 }
287 thread_tree_.GetThreadTree().Update(r);
288 return true;
289 }
290
ProcessAuxRecord(const AuxRecord & r)291 bool ETMBranchListGeneratorImpl::ProcessAuxRecord(const AuxRecord& r) {
292 OverflowResult result = SafeAdd(r.data->aux_offset, r.data->aux_size);
293 if (result.overflow || r.data->aux_size > SIZE_MAX) {
294 LOG(ERROR) << "invalid aux record";
295 return false;
296 }
297 size_t size = r.data->aux_size;
298 uint64_t start = r.data->aux_offset;
299 uint64_t end = result.value;
300 PerCpuData& data = cpu_map_[r.Cpu()];
301 if (start >= data.data_offset && end <= data.data_offset + data.aux_data.size()) {
302 // The ETM data is available. Process it now.
303 uint8_t* p = data.aux_data.data() + (start - data.data_offset);
304 if (!etm_decoder_) {
305 LOG(ERROR) << "ETMDecoder isn't created";
306 return false;
307 }
308 return etm_decoder_->ProcessData(p, size, !r.Unformatted(), r.Cpu());
309 }
310 // The ETM data isn't available. Put the aux record into queue.
311 data.aux_records.emplace(start, end, !r.Unformatted());
312 return true;
313 }
314
ProcessAuxTraceRecord(const AuxTraceRecord & r)315 bool ETMBranchListGeneratorImpl::ProcessAuxTraceRecord(const AuxTraceRecord& r) {
316 OverflowResult result = SafeAdd(r.data->offset, r.data->aux_size);
317 if (result.overflow || r.data->aux_size > SIZE_MAX) {
318 LOG(ERROR) << "invalid auxtrace record";
319 return false;
320 }
321 size_t size = r.data->aux_size;
322 uint64_t start = r.data->offset;
323 uint64_t end = result.value;
324 PerCpuData& data = cpu_map_[r.Cpu()];
325 data.data_offset = start;
326 CHECK(r.location.addr != nullptr);
327 data.aux_data.resize(size);
328 memcpy(data.aux_data.data(), r.location.addr, size);
329
330 // Process cached aux records.
331 while (!data.aux_records.empty() && data.aux_records.front().start < end) {
332 const AuxRecordData& aux = data.aux_records.front();
333 if (aux.start >= start && aux.end <= end) {
334 uint8_t* p = data.aux_data.data() + (aux.start - start);
335 if (!etm_decoder_) {
336 LOG(ERROR) << "ETMDecoder isn't created";
337 return false;
338 }
339 if (!etm_decoder_->ProcessData(p, aux.end - aux.start, aux.formatted, r.Cpu())) {
340 return false;
341 }
342 }
343 data.aux_records.pop();
344 }
345 return true;
346 }
347
ProcessBranchList(const ETMBranchList & branch_list)348 void ETMBranchListGeneratorImpl::ProcessBranchList(const ETMBranchList& branch_list) {
349 if (!binary_filter_.Filter(branch_list.dso)) {
350 return;
351 }
352 auto& branch_map = branch_list_binary_map_[branch_list.dso].branch_map;
353 ++branch_map[branch_list.addr][branch_list.branch];
354 }
355
GetBranchListBinaryMap()356 BranchListBinaryMap ETMBranchListGeneratorImpl::GetBranchListBinaryMap() {
357 BranchListBinaryMap binary_map;
358 for (auto& p : branch_list_binary_map_) {
359 Dso* dso = p.first;
360 BranchListBinaryInfo& binary = p.second;
361 binary.dso_type = dso->type();
362 BuildId build_id;
363 GetBuildId(*dso, build_id);
364 BinaryKey key(dso->Path(), build_id);
365 if (binary.dso_type == DSO_KERNEL) {
366 if (kernel_map_start_addr_ == 0) {
367 LOG(WARNING) << "Can't convert kernel ip addresses without kernel start addr. So remove "
368 "branches for the kernel.";
369 continue;
370 }
371 key.kernel_start_addr = kernel_map_start_addr_;
372 }
373 binary_map[key] = std::move(binary);
374 }
375 return binary_map;
376 }
377
Create(bool dump_maps_from_proc)378 std::unique_ptr<ETMBranchListGenerator> ETMBranchListGenerator::Create(bool dump_maps_from_proc) {
379 return std::unique_ptr<ETMBranchListGenerator>(
380 new ETMBranchListGeneratorImpl(dump_maps_from_proc));
381 }
382
~ETMBranchListGenerator()383 ETMBranchListGenerator::~ETMBranchListGenerator() {}
384
385 } // namespace simpleperf
386