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 "BranchListFile.h"
18
19 #include "ETMDecoder.h"
20 #include "ZstdUtil.h"
21 #include "system/extras/simpleperf/branch_list.pb.h"
22
23 namespace simpleperf {
24
25 static constexpr const char* ETM_BRANCH_LIST_PROTO_MAGIC = "simpleperf:EtmBranchList";
26
ETMBranchToProtoString(const std::vector<bool> & branch)27 std::string ETMBranchToProtoString(const std::vector<bool>& branch) {
28 size_t bytes = (branch.size() + 7) / 8;
29 std::string res(bytes, '\0');
30 for (size_t i = 0; i < branch.size(); i++) {
31 if (branch[i]) {
32 res[i >> 3] |= 1 << (i & 7);
33 }
34 }
35 return res;
36 }
37
ProtoStringToETMBranch(const std::string & s,size_t bit_size)38 std::vector<bool> ProtoStringToETMBranch(const std::string& s, size_t bit_size) {
39 std::vector<bool> branch(bit_size, false);
40 for (size_t i = 0; i < bit_size; i++) {
41 if (s[i >> 3] & (1 << (i & 7))) {
42 branch[i] = true;
43 }
44 }
45 return branch;
46 }
47
ToProtoBinaryType(DsoType dso_type)48 static std::optional<proto::ETMBinary::BinaryType> ToProtoBinaryType(DsoType dso_type) {
49 switch (dso_type) {
50 case DSO_ELF_FILE:
51 return proto::ETMBinary::ELF_FILE;
52 case DSO_KERNEL:
53 return proto::ETMBinary::KERNEL;
54 case DSO_KERNEL_MODULE:
55 return proto::ETMBinary::KERNEL_MODULE;
56 default:
57 LOG(ERROR) << "unexpected dso type " << dso_type;
58 return std::nullopt;
59 }
60 }
61
ETMBinaryMapToString(const ETMBinaryMap & binary_map,std::string & s)62 bool ETMBinaryMapToString(const ETMBinaryMap& binary_map, std::string& s) {
63 auto writer = BranchListProtoWriter::CreateForString(&s, false);
64 if (!writer) {
65 return false;
66 }
67 if (!writer->Write(binary_map)) {
68 return false;
69 }
70 return true;
71 }
72
ToDsoType(proto::ETMBinary::BinaryType binary_type)73 static std::optional<DsoType> ToDsoType(proto::ETMBinary::BinaryType binary_type) {
74 switch (binary_type) {
75 case proto::ETMBinary::ELF_FILE:
76 return DSO_ELF_FILE;
77 case proto::ETMBinary::KERNEL:
78 return DSO_KERNEL;
79 case proto::ETMBinary::KERNEL_MODULE:
80 return DSO_KERNEL_MODULE;
81 default:
82 LOG(ERROR) << "unexpected binary type " << binary_type;
83 return std::nullopt;
84 }
85 }
86
StringToETMBinaryMap(const std::string & s,ETMBinaryMap & binary_map)87 bool StringToETMBinaryMap(const std::string& s, ETMBinaryMap& binary_map) {
88 LBRData lbr_data;
89 auto reader = BranchListProtoReader::CreateForString(s);
90 if (!reader) {
91 return false;
92 }
93 return reader->Read(binary_map, lbr_data);
94 }
95
96 class ETMThreadTreeWhenRecording : public ETMThreadTree {
97 public:
ETMThreadTreeWhenRecording(bool dump_maps_from_proc)98 ETMThreadTreeWhenRecording(bool dump_maps_from_proc)
99 : dump_maps_from_proc_(dump_maps_from_proc) {}
100
GetThreadTree()101 ThreadTree& GetThreadTree() { return thread_tree_; }
ExcludePid(pid_t pid)102 void ExcludePid(pid_t pid) { exclude_pid_ = pid; }
103
FindThread(int tid)104 const ThreadEntry* FindThread(int tid) override {
105 const ThreadEntry* thread = thread_tree_.FindThread(tid);
106 if (thread == nullptr) {
107 if (dump_maps_from_proc_) {
108 thread = FindThreadFromProc(tid);
109 }
110 if (thread == nullptr) {
111 return nullptr;
112 }
113 }
114 if (exclude_pid_ && exclude_pid_ == thread->pid) {
115 return nullptr;
116 }
117
118 if (dump_maps_from_proc_) {
119 DumpMapsFromProc(thread->pid);
120 }
121 return thread;
122 }
123
DisableThreadExitRecords()124 void DisableThreadExitRecords() override { thread_tree_.DisableThreadExitRecords(); }
GetKernelMaps()125 const MapSet& GetKernelMaps() override { return thread_tree_.GetKernelMaps(); }
126
127 private:
FindThreadFromProc(int tid)128 const ThreadEntry* FindThreadFromProc(int tid) {
129 std::string comm;
130 pid_t pid;
131 if (ReadThreadNameAndPid(tid, &comm, &pid)) {
132 thread_tree_.SetThreadName(pid, tid, comm);
133 return thread_tree_.FindThread(tid);
134 }
135 return nullptr;
136 }
137
DumpMapsFromProc(int pid)138 void DumpMapsFromProc(int pid) {
139 if (dumped_processes_.count(pid) == 0) {
140 dumped_processes_.insert(pid);
141 std::vector<ThreadMmap> maps;
142 if (GetThreadMmapsInProcess(pid, &maps)) {
143 for (const auto& map : maps) {
144 thread_tree_.AddThreadMap(pid, pid, map.start_addr, map.len, map.pgoff, map.name);
145 }
146 }
147 }
148 }
149
150 ThreadTree thread_tree_;
151 bool dump_maps_from_proc_;
152 std::unordered_set<int> dumped_processes_;
153 std::optional<pid_t> exclude_pid_;
154 };
155
156 class ETMBranchListGeneratorImpl : public ETMBranchListGenerator {
157 public:
ETMBranchListGeneratorImpl(bool dump_maps_from_proc)158 ETMBranchListGeneratorImpl(bool dump_maps_from_proc)
159 : thread_tree_(dump_maps_from_proc), binary_filter_(nullptr) {}
160
SetExcludePid(pid_t pid)161 void SetExcludePid(pid_t pid) override { thread_tree_.ExcludePid(pid); }
SetBinaryFilter(const RegEx * binary_name_regex)162 void SetBinaryFilter(const RegEx* binary_name_regex) override {
163 binary_filter_.SetRegex(binary_name_regex);
164 }
165
166 bool ProcessRecord(const Record& r, bool& consumed) override;
167 ETMBinaryMap GetETMBinaryMap() override;
168
169 private:
170 struct AuxRecordData {
171 uint64_t start;
172 uint64_t end;
173 bool formatted;
AuxRecordDatasimpleperf::ETMBranchListGeneratorImpl::AuxRecordData174 AuxRecordData(uint64_t start, uint64_t end, bool formatted)
175 : start(start), end(end), formatted(formatted) {}
176 };
177
178 struct PerCpuData {
179 std::vector<uint8_t> aux_data;
180 uint64_t data_offset = 0;
181 std::queue<AuxRecordData> aux_records;
182 };
183
184 bool ProcessAuxRecord(const AuxRecord& r);
185 bool ProcessAuxTraceRecord(const AuxTraceRecord& r);
186 void ProcessBranchList(const ETMBranchList& branch_list);
187
188 ETMThreadTreeWhenRecording thread_tree_;
189 uint64_t kernel_map_start_addr_ = 0;
190 BinaryFilter binary_filter_;
191 std::map<uint32_t, PerCpuData> cpu_map_;
192 std::unique_ptr<ETMDecoder> etm_decoder_;
193 std::unordered_map<Dso*, ETMBinary> branch_list_binary_map_;
194 };
195
ProcessRecord(const Record & r,bool & consumed)196 bool ETMBranchListGeneratorImpl::ProcessRecord(const Record& r, bool& consumed) {
197 consumed = true; // No need to store any records.
198 uint32_t type = r.type();
199 if (type == PERF_RECORD_AUXTRACE_INFO) {
200 etm_decoder_ = ETMDecoder::Create(*static_cast<const AuxTraceInfoRecord*>(&r), thread_tree_);
201 if (!etm_decoder_) {
202 return false;
203 }
204 etm_decoder_->RegisterCallback(
205 [this](const ETMBranchList& branch) { ProcessBranchList(branch); });
206 return true;
207 }
208 if (type == PERF_RECORD_AUX) {
209 return ProcessAuxRecord(*static_cast<const AuxRecord*>(&r));
210 }
211 if (type == PERF_RECORD_AUXTRACE) {
212 return ProcessAuxTraceRecord(*static_cast<const AuxTraceRecord*>(&r));
213 }
214 if (type == PERF_RECORD_MMAP && r.InKernel()) {
215 auto& mmap_r = *static_cast<const MmapRecord*>(&r);
216 if (android::base::StartsWith(mmap_r.filename, DEFAULT_KERNEL_MMAP_NAME)) {
217 kernel_map_start_addr_ = mmap_r.data->addr;
218 }
219 }
220 thread_tree_.GetThreadTree().Update(r);
221 return true;
222 }
223
ProcessAuxRecord(const AuxRecord & r)224 bool ETMBranchListGeneratorImpl::ProcessAuxRecord(const AuxRecord& r) {
225 OverflowResult result = SafeAdd(r.data->aux_offset, r.data->aux_size);
226 if (result.overflow || r.data->aux_size > SIZE_MAX) {
227 LOG(ERROR) << "invalid aux record";
228 return false;
229 }
230 size_t size = r.data->aux_size;
231 uint64_t start = r.data->aux_offset;
232 uint64_t end = result.value;
233 PerCpuData& data = cpu_map_[r.Cpu()];
234 if (start >= data.data_offset && end <= data.data_offset + data.aux_data.size()) {
235 // The ETM data is available. Process it now.
236 uint8_t* p = data.aux_data.data() + (start - data.data_offset);
237 if (!etm_decoder_) {
238 LOG(ERROR) << "ETMDecoder isn't created";
239 return false;
240 }
241 return etm_decoder_->ProcessData(p, size, !r.Unformatted(), r.Cpu());
242 }
243 // The ETM data isn't available. Put the aux record into queue.
244 data.aux_records.emplace(start, end, !r.Unformatted());
245 return true;
246 }
247
ProcessAuxTraceRecord(const AuxTraceRecord & r)248 bool ETMBranchListGeneratorImpl::ProcessAuxTraceRecord(const AuxTraceRecord& r) {
249 OverflowResult result = SafeAdd(r.data->offset, r.data->aux_size);
250 if (result.overflow || r.data->aux_size > SIZE_MAX) {
251 LOG(ERROR) << "invalid auxtrace record";
252 return false;
253 }
254 size_t size = r.data->aux_size;
255 uint64_t start = r.data->offset;
256 uint64_t end = result.value;
257 PerCpuData& data = cpu_map_[r.Cpu()];
258 data.data_offset = start;
259 CHECK(r.location.addr != nullptr);
260 data.aux_data.resize(size);
261 memcpy(data.aux_data.data(), r.location.addr, size);
262
263 // Process cached aux records.
264 while (!data.aux_records.empty() && data.aux_records.front().start < end) {
265 const AuxRecordData& aux = data.aux_records.front();
266 if (aux.start >= start && aux.end <= end) {
267 uint8_t* p = data.aux_data.data() + (aux.start - start);
268 if (!etm_decoder_) {
269 LOG(ERROR) << "ETMDecoder isn't created";
270 return false;
271 }
272 if (!etm_decoder_->ProcessData(p, aux.end - aux.start, aux.formatted, r.Cpu())) {
273 return false;
274 }
275 }
276 data.aux_records.pop();
277 }
278 return true;
279 }
280
ProcessBranchList(const ETMBranchList & branch_list)281 void ETMBranchListGeneratorImpl::ProcessBranchList(const ETMBranchList& branch_list) {
282 if (!binary_filter_.Filter(branch_list.dso)) {
283 return;
284 }
285 auto& branch_map = branch_list_binary_map_[branch_list.dso].branch_map;
286 ++branch_map[branch_list.addr][branch_list.branch];
287 }
288
GetETMBinaryMap()289 ETMBinaryMap ETMBranchListGeneratorImpl::GetETMBinaryMap() {
290 ETMBinaryMap binary_map;
291 for (auto& p : branch_list_binary_map_) {
292 Dso* dso = p.first;
293 ETMBinary& binary = p.second;
294 binary.dso_type = dso->type();
295 BuildId build_id;
296 GetBuildId(*dso, build_id);
297 BinaryKey key(dso->Path(), build_id);
298 if (binary.dso_type == DSO_KERNEL) {
299 if (kernel_map_start_addr_ == 0) {
300 LOG(WARNING) << "Can't convert kernel ip addresses without kernel start addr. So remove "
301 "branches for the kernel.";
302 continue;
303 }
304 key.kernel_start_addr = kernel_map_start_addr_;
305 }
306 binary_map[key] = std::move(binary);
307 }
308 return binary_map;
309 }
310
Create(bool dump_maps_from_proc)311 std::unique_ptr<ETMBranchListGenerator> ETMBranchListGenerator::Create(bool dump_maps_from_proc) {
312 return std::unique_ptr<ETMBranchListGenerator>(
313 new ETMBranchListGeneratorImpl(dump_maps_from_proc));
314 }
315
~ETMBranchListGenerator()316 ETMBranchListGenerator::~ETMBranchListGenerator() {}
317
LBRDataToString(const LBRData & data,std::string & s)318 bool LBRDataToString(const LBRData& data, std::string& s) {
319 auto writer = BranchListProtoWriter::CreateForString(&s, false);
320 if (!writer) {
321 return false;
322 }
323 if (!writer->Write(data)) {
324 return false;
325 }
326 return true;
327 }
328
CreateForFile(const std::string & output_filename,bool compress,size_t max_branches_per_message)329 std::unique_ptr<BranchListProtoWriter> BranchListProtoWriter::CreateForFile(
330 const std::string& output_filename, bool compress, size_t max_branches_per_message) {
331 auto writer = std::unique_ptr<BranchListProtoWriter>(
332 new BranchListProtoWriter(output_filename, nullptr, compress, max_branches_per_message));
333 if (!writer->WriteHeader()) {
334 return nullptr;
335 }
336 return writer;
337 }
338
CreateForString(std::string * output_str,bool compress,size_t max_branches_per_message)339 std::unique_ptr<BranchListProtoWriter> BranchListProtoWriter::CreateForString(
340 std::string* output_str, bool compress, size_t max_branches_per_message) {
341 auto writer = std::unique_ptr<BranchListProtoWriter>(
342 new BranchListProtoWriter("", output_str, compress, max_branches_per_message));
343 if (!writer->WriteHeader()) {
344 return nullptr;
345 }
346 return writer;
347 }
348
Write(const ETMBinaryMap & etm_data)349 bool BranchListProtoWriter::Write(const ETMBinaryMap& etm_data) {
350 if (!output_fp_ && !WriteHeader()) {
351 return false;
352 }
353 std::unique_ptr<proto::BranchList> proto_branch_list = std::make_unique<proto::BranchList>();
354 proto::ETMBinary* proto_binary = nullptr;
355 proto::ETMBinary_Address* proto_addr = nullptr;
356 size_t branch_count = 0;
357
358 auto add_proto_binary = [&](const BinaryKey& key, const ETMBinary& binary) {
359 proto_binary = proto_branch_list->add_etm_data();
360 proto_binary->set_path(key.path);
361 if (!key.build_id.IsEmpty()) {
362 proto_binary->set_build_id(key.build_id.ToString().substr(2));
363 }
364 auto opt_binary_type = ToProtoBinaryType(binary.dso_type);
365 if (!opt_binary_type.has_value()) {
366 return false;
367 }
368 proto_binary->set_type(opt_binary_type.value());
369 if (binary.dso_type == DSO_KERNEL) {
370 proto_binary->mutable_kernel_info()->set_kernel_start_addr(key.kernel_start_addr);
371 }
372 return true;
373 };
374
375 auto add_proto_addr = [&](uint64_t addr) {
376 proto_addr = proto_binary->add_addrs();
377 proto_addr->set_addr(addr);
378 };
379
380 for (const auto& [key, binary] : etm_data) {
381 if (!add_proto_binary(key, binary)) {
382 return false;
383 }
384 for (const auto& [addr, branch_map] : binary.branch_map) {
385 add_proto_addr(addr);
386 size_t new_branch_count = 0;
387 for (const auto& [branch, _] : branch_map) {
388 new_branch_count += branch.size();
389 }
390 if (branch_count + new_branch_count > max_branches_per_message_) {
391 if (!WriteProtoBranchList(*proto_branch_list)) {
392 return false;
393 }
394 proto_branch_list.reset(new proto::BranchList);
395 if (!add_proto_binary(key, binary)) {
396 return false;
397 }
398 add_proto_addr(addr);
399 branch_count = 0;
400 }
401 branch_count += new_branch_count;
402 for (const auto& [branch, count] : branch_map) {
403 proto::ETMBinary_Address_Branch* proto_branch = proto_addr->add_branches();
404 proto_branch->set_branch(ETMBranchToProtoString(branch));
405 proto_branch->set_branch_size(branch.size());
406 proto_branch->set_count(count);
407 }
408 }
409 }
410 return WriteProtoBranchList(*proto_branch_list);
411 }
412
Write(const LBRData & lbr_data)413 bool BranchListProtoWriter::Write(const LBRData& lbr_data) {
414 if (!output_fp_ && !WriteHeader()) {
415 return false;
416 }
417 proto::BranchList proto_branch_list;
418 proto_branch_list.set_magic(ETM_BRANCH_LIST_PROTO_MAGIC);
419 auto proto_lbr = proto_branch_list.mutable_lbr_data();
420 for (const LBRSample& sample : lbr_data.samples) {
421 auto proto_sample = proto_lbr->add_samples();
422 proto_sample->set_binary_id(sample.binary_id);
423 proto_sample->set_vaddr_in_file(sample.vaddr_in_file);
424 for (const LBRBranch& branch : sample.branches) {
425 auto proto_branch = proto_sample->add_branches();
426 proto_branch->set_from_binary_id(branch.from_binary_id);
427 proto_branch->set_to_binary_id(branch.to_binary_id);
428 proto_branch->set_from_vaddr_in_file(branch.from_vaddr_in_file);
429 proto_branch->set_to_vaddr_in_file(branch.to_vaddr_in_file);
430 }
431 }
432 for (const BinaryKey& binary : lbr_data.binaries) {
433 auto proto_binary = proto_lbr->add_binaries();
434 proto_binary->set_path(binary.path);
435 proto_binary->set_build_id(binary.build_id.ToString().substr(2));
436 }
437 return WriteProtoBranchList(proto_branch_list);
438 }
439
WriteHeader()440 bool BranchListProtoWriter::WriteHeader() {
441 if (!output_filename_.empty()) {
442 output_fp_.reset(fopen(output_filename_.c_str(), "wbe"));
443 if (!output_fp_) {
444 PLOG(ERROR) << "failed to open " << output_filename_;
445 return false;
446 }
447 } else {
448 output_str_->clear();
449 }
450 if (!WriteData(ETM_BRANCH_LIST_PROTO_MAGIC, strlen(ETM_BRANCH_LIST_PROTO_MAGIC))) {
451 return false;
452 }
453 uint32_t version = 1;
454 if (!WriteData(&version, sizeof(version))) {
455 return false;
456 }
457 uint8_t compress = compress_ ? 1 : 0;
458 if (!WriteData(&compress, sizeof(compress))) {
459 return false;
460 }
461 return true;
462 }
463
WriteProtoBranchList(proto::BranchList & branch_list)464 bool BranchListProtoWriter::WriteProtoBranchList(proto::BranchList& branch_list) {
465 std::string s;
466 if (!branch_list.SerializeToString(&s)) {
467 LOG(ERROR) << "failed to serialize branch list binary map";
468 return false;
469 }
470 if (compress_ && !ZstdCompress(s.data(), s.size(), s)) {
471 return false;
472 }
473 uint32_t msg_size = s.size();
474 return WriteData(&msg_size, sizeof(msg_size)) && WriteData(s.data(), s.size());
475 }
476
WriteData(const void * data,size_t size)477 bool BranchListProtoWriter::WriteData(const void* data, size_t size) {
478 if (output_fp_) {
479 if (fwrite(data, size, 1, output_fp_.get()) != 1) {
480 LOG(ERROR) << "failed to write to " << output_filename_;
481 return false;
482 }
483 } else {
484 output_str_->insert(output_str_->size(), static_cast<const char*>(data), size);
485 }
486 return true;
487 }
488
CreateForFile(const std::string & input_filename)489 std::unique_ptr<BranchListProtoReader> BranchListProtoReader::CreateForFile(
490 const std::string& input_filename) {
491 return std::unique_ptr<BranchListProtoReader>(new BranchListProtoReader(input_filename, ""));
492 }
493
CreateForString(const std::string & input_str)494 std::unique_ptr<BranchListProtoReader> BranchListProtoReader::CreateForString(
495 const std::string& input_str) {
496 return std::unique_ptr<BranchListProtoReader>(new BranchListProtoReader("", input_str));
497 }
498
Read(ETMBinaryMap & etm_data,LBRData & lbr_data)499 bool BranchListProtoReader::Read(ETMBinaryMap& etm_data, LBRData& lbr_data) {
500 if (!input_filename_.empty()) {
501 input_fp_.reset(fopen(input_filename_.c_str(), "rbe"));
502 if (!input_fp_) {
503 PLOG(ERROR) << "failed to open " << input_filename_;
504 return false;
505 }
506 }
507 char magic[24];
508 if (!ReadData(magic, sizeof(magic)) ||
509 memcmp(magic, ETM_BRANCH_LIST_PROTO_MAGIC, sizeof(magic)) != 0) {
510 return ReadOldFileFormat(etm_data, lbr_data);
511 }
512 uint32_t version;
513 if (!ReadData(&version, sizeof(version)) && version != 1) {
514 LOG(ERROR) << "unsupported version in " << input_filename_;
515 return false;
516 }
517 uint8_t compress;
518 if (!ReadData(&compress, sizeof(compress))) {
519 return false;
520 }
521 compress_ = compress == 1;
522 long file_offset = ftell(input_fp_.get());
523 if (file_offset == -1) {
524 PLOG(ERROR) << "failed to call ftell";
525 return false;
526 }
527 uint64_t file_size = GetFileSize(input_filename_);
528 while (file_offset < file_size) {
529 uint32_t msg_size;
530 if (!ReadData(&msg_size, sizeof(msg_size))) {
531 return false;
532 }
533 proto::BranchList proto_branch_list;
534 if (!ReadProtoBranchList(msg_size, proto_branch_list)) {
535 return false;
536 }
537 for (size_t i = 0; i < proto_branch_list.etm_data_size(); i++) {
538 const proto::ETMBinary& proto_binary = proto_branch_list.etm_data(i);
539 if (!AddETMBinary(proto_binary, etm_data)) {
540 return false;
541 }
542 }
543 if (proto_branch_list.has_lbr_data()) {
544 AddLBRData(proto_branch_list.lbr_data(), lbr_data);
545 }
546 file_offset += 4 + msg_size;
547 }
548 return true;
549 }
550
AddETMBinary(const proto::ETMBinary & proto_binary,ETMBinaryMap & etm_data)551 bool BranchListProtoReader::AddETMBinary(const proto::ETMBinary& proto_binary,
552 ETMBinaryMap& etm_data) {
553 BinaryKey key(proto_binary.path(), BuildId(proto_binary.build_id()));
554 if (proto_binary.has_kernel_info()) {
555 key.kernel_start_addr = proto_binary.kernel_info().kernel_start_addr();
556 }
557 ETMBinary& binary = etm_data[key];
558 auto dso_type = ToDsoType(proto_binary.type());
559 if (!dso_type) {
560 LOG(ERROR) << "invalid binary type " << proto_binary.type();
561 return false;
562 }
563 binary.dso_type = dso_type.value();
564 auto& branch_map = binary.branch_map;
565 for (size_t i = 0; i < proto_binary.addrs_size(); i++) {
566 const auto& proto_addr = proto_binary.addrs(i);
567 auto& b_map = branch_map[proto_addr.addr()];
568 for (size_t j = 0; j < proto_addr.branches_size(); j++) {
569 const auto& proto_branch = proto_addr.branches(j);
570 std::vector<bool> branch =
571 ProtoStringToETMBranch(proto_branch.branch(), proto_branch.branch_size());
572 b_map[branch] = proto_branch.count();
573 }
574 }
575 return true;
576 }
577
AddLBRData(const proto::LBRData & proto_lbr_data,LBRData & lbr_data)578 void BranchListProtoReader::AddLBRData(const proto::LBRData& proto_lbr_data, LBRData& lbr_data) {
579 for (size_t i = 0; i < proto_lbr_data.samples_size(); ++i) {
580 const auto& proto_sample = proto_lbr_data.samples(i);
581 lbr_data.samples.resize(lbr_data.samples.size() + 1);
582 LBRSample& sample = lbr_data.samples.back();
583 sample.binary_id = proto_sample.binary_id();
584 sample.vaddr_in_file = proto_sample.vaddr_in_file();
585 sample.branches.resize(proto_sample.branches_size());
586 for (size_t j = 0; j < proto_sample.branches_size(); ++j) {
587 const auto& proto_branch = proto_sample.branches(j);
588 LBRBranch& branch = sample.branches[j];
589 branch.from_binary_id = proto_branch.from_binary_id();
590 branch.to_binary_id = proto_branch.to_binary_id();
591 branch.from_vaddr_in_file = proto_branch.from_vaddr_in_file();
592 branch.to_vaddr_in_file = proto_branch.to_vaddr_in_file();
593 }
594 }
595 for (size_t i = 0; i < proto_lbr_data.binaries_size(); ++i) {
596 const auto& proto_binary = proto_lbr_data.binaries(i);
597 lbr_data.binaries.emplace_back(proto_binary.path(), BuildId(proto_binary.build_id()));
598 }
599 }
600
ReadProtoBranchList(uint32_t size,proto::BranchList & proto_branch_list)601 bool BranchListProtoReader::ReadProtoBranchList(uint32_t size,
602 proto::BranchList& proto_branch_list) {
603 std::string s;
604 s.resize(size);
605 if (!ReadData(s.data(), size)) {
606 return false;
607 }
608 if (compress_ && !ZstdDecompress(s.data(), s.size(), s)) {
609 return false;
610 }
611 if (!proto_branch_list.ParseFromString(s)) {
612 PLOG(ERROR) << "failed to read ETMBranchList msg";
613 return false;
614 }
615 return true;
616 }
617
Rewind()618 void BranchListProtoReader::Rewind() {
619 if (input_fp_) {
620 rewind(input_fp_.get());
621 } else {
622 input_str_pos_ = 0;
623 }
624 }
625
ReadData(void * data,size_t size)626 bool BranchListProtoReader::ReadData(void* data, size_t size) {
627 if (input_fp_) {
628 if (fread(data, size, 1, input_fp_.get()) != 1) {
629 PLOG(ERROR) << "failed to read " << input_filename_;
630 return false;
631 }
632 } else {
633 if (input_str_pos_ + size > input_str_.size()) {
634 LOG(ERROR) << "failed to read BranchList from string";
635 return false;
636 }
637 memcpy(data, &input_str_[input_str_pos_], size);
638 input_str_pos_ += size;
639 }
640 return true;
641 }
642
ReadOldFileFormat(ETMBinaryMap & etm_data,LBRData & lbr_data)643 bool BranchListProtoReader::ReadOldFileFormat(ETMBinaryMap& etm_data, LBRData& lbr_data) {
644 size_t size = 0;
645 if (!input_filename_.empty()) {
646 size = static_cast<size_t>(GetFileSize(input_filename_));
647 if (android::base::EndsWith(input_filename_, ".zst")) {
648 compress_ = true;
649 }
650 } else {
651 size = input_str_.size();
652 }
653 Rewind();
654 proto::BranchList proto_branch_list;
655 if (!ReadProtoBranchList(size, proto_branch_list)) {
656 return false;
657 }
658 if (proto_branch_list.magic() != ETM_BRANCH_LIST_PROTO_MAGIC) {
659 PLOG(ERROR) << "not in format of branch_list.proto";
660 }
661 for (size_t i = 0; i < proto_branch_list.etm_data_size(); i++) {
662 const proto::ETMBinary& proto_binary = proto_branch_list.etm_data(i);
663 if (!AddETMBinary(proto_binary, etm_data)) {
664 return false;
665 }
666 }
667 if (proto_branch_list.has_lbr_data()) {
668 AddLBRData(proto_branch_list.lbr_data(), lbr_data);
669 }
670 return true;
671 }
672
DumpBranchListFile(std::string filename)673 bool DumpBranchListFile(std::string filename) {
674 ETMBinaryMap etm_data;
675 LBRData lbr_data;
676 auto reader = BranchListProtoReader::CreateForFile(filename);
677 if (!reader || !reader->Read(etm_data, lbr_data)) {
678 return false;
679 }
680
681 if (!etm_data.empty()) {
682 std::vector<BinaryKey> sorted_keys;
683 for (const auto& [key, _] : etm_data) {
684 sorted_keys.emplace_back(key);
685 }
686 std::sort(sorted_keys.begin(), sorted_keys.end(),
687 [](const BinaryKey& key1, const BinaryKey& key2) { return key1.path < key2.path; });
688 PrintIndented(0, "etm_data:\n");
689 for (size_t i = 0; i < sorted_keys.size(); ++i) {
690 const auto& key = sorted_keys[i];
691 const auto& binary = etm_data[key];
692 PrintIndented(1, "binary[%zu].path: %s\n", i, key.path.c_str());
693 PrintIndented(1, "binary[%zu].build_id: %s\n", i, key.build_id.ToString().c_str());
694 PrintIndented(1, "binary[%zu].binary_type: %s\n", i, DsoTypeToString(binary.dso_type));
695 if (binary.dso_type == DSO_KERNEL) {
696 PrintIndented(1, "binary[%zu].kernel_start_addr: 0x%" PRIx64 "\n", i,
697 key.kernel_start_addr);
698 }
699 PrintIndented(1, "binary[%zu].addrs:\n", i);
700 size_t addr_id = 0;
701 for (const auto& [addr, branches] : binary.GetOrderedBranchMap()) {
702 PrintIndented(2, "addr[%zu]: 0x%" PRIx64 "\n", addr_id++, addr);
703 size_t branch_id = 0;
704 for (const auto& [branch, count] : branches) {
705 std::string s = "0b";
706 for (auto it = branch.rbegin(); it != branch.rend(); ++it) {
707 s.push_back(*it ? '1' : '0');
708 }
709 PrintIndented(3, "branch[%zu].branch: %s\n", branch_id, s.c_str());
710 PrintIndented(3, "branch[%zu].count: %" PRIu64 "\n", branch_id, count);
711 ++branch_id;
712 }
713 }
714 }
715 }
716 if (!lbr_data.samples.empty()) {
717 PrintIndented(0, "lbr_data:\n");
718 for (size_t i = 0; i < lbr_data.samples.size(); ++i) {
719 const auto& sample = lbr_data.samples[i];
720 PrintIndented(1, "sample[%zu].binary_id: %u\n", i, sample.binary_id);
721 PrintIndented(1, "sample[%zu].vaddr_in_file: 0x%" PRIx64 "\n", i, sample.vaddr_in_file);
722 PrintIndented(1, "sample[%zu].branches:\n", i);
723 for (size_t j = 0; j < sample.branches.size(); ++j) {
724 const auto& branch = sample.branches[j];
725 PrintIndented(2, "branch[%zu].from_binary_id: %u\n", j, branch.from_binary_id);
726 PrintIndented(2, "branch[%zu].from_vaddr_in_file: 0x%" PRIx64 "\n", j,
727 branch.from_vaddr_in_file);
728 PrintIndented(2, "branch[%zu].to_binary_id: %u\n", j, branch.to_binary_id);
729 PrintIndented(2, "branch[%zu].to_vaddr_in_file: 0x%" PRIx64 "\n", j,
730 branch.to_vaddr_in_file);
731 }
732 }
733 for (size_t i = 0; i < lbr_data.binaries.size(); ++i) {
734 const auto& binary = lbr_data.binaries[i];
735 PrintIndented(1, "binary[%zu].path: %s\n", i, binary.path.c_str());
736 PrintIndented(1, "binary[%zu].build_id: %s\n", i, binary.build_id.ToString().c_str());
737 }
738 }
739 return true;
740 }
741
742 } // namespace simpleperf
743