xref: /aosp_15_r20/system/extras/simpleperf/BranchListFile.cpp (revision 288bf5226967eb3dac5cce6c939ccc2a7f2b4fe5)
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