xref: /aosp_15_r20/system/extras/simpleperf/cmd_inject.cpp (revision 288bf5226967eb3dac5cce6c939ccc2a7f2b4fe5)
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 <stdint.h>
18 #include <stdio.h>
19 #include <unistd.h>
20 
21 #include <memory>
22 #include <optional>
23 #include <string>
24 #include <thread>
25 
26 #include <android-base/parseint.h>
27 #include <android-base/strings.h>
28 
29 #include "BranchListFile.h"
30 #include "ETMDecoder.h"
31 #include "RegEx.h"
32 #include "ZstdUtil.h"
33 #include "command.h"
34 #include "record_file.h"
35 #include "system/extras/simpleperf/branch_list.pb.h"
36 #include "thread_tree.h"
37 #include "utils.h"
38 
39 namespace simpleperf {
40 
41 namespace {
42 
43 using AddrPair = std::pair<uint64_t, uint64_t>;
44 
45 struct AddrPairHash {
operator ()simpleperf::__anon245dd9b60111::AddrPairHash46   size_t operator()(const AddrPair& ap) const noexcept {
47     size_t seed = 0;
48     HashCombine(seed, ap.first);
49     HashCombine(seed, ap.second);
50     return seed;
51   }
52 };
53 
54 enum class OutputFormat {
55   AutoFDO,
56   BOLT,
57   BranchList,
58 };
59 
60 struct AutoFDOBinaryInfo {
61   std::vector<ElfSegment> executable_segments;
62   std::unordered_map<uint64_t, uint64_t> address_count_map;
63   std::unordered_map<AddrPair, uint64_t, AddrPairHash> range_count_map;
64   std::unordered_map<AddrPair, uint64_t, AddrPairHash> branch_count_map;
65 
AddAddresssimpleperf::__anon245dd9b60111::AutoFDOBinaryInfo66   void AddAddress(uint64_t addr) { OverflowSafeAdd(address_count_map[addr], 1); }
67 
AddRangesimpleperf::__anon245dd9b60111::AutoFDOBinaryInfo68   void AddRange(uint64_t begin, uint64_t end) {
69     OverflowSafeAdd(range_count_map[std::make_pair(begin, end)], 1);
70   }
71 
AddBranchsimpleperf::__anon245dd9b60111::AutoFDOBinaryInfo72   void AddBranch(uint64_t from, uint64_t to) {
73     OverflowSafeAdd(branch_count_map[std::make_pair(from, to)], 1);
74   }
75 
AddInstrRangesimpleperf::__anon245dd9b60111::AutoFDOBinaryInfo76   void AddInstrRange(const ETMInstrRange& instr_range) {
77     uint64_t total_count = instr_range.branch_taken_count;
78     OverflowSafeAdd(total_count, instr_range.branch_not_taken_count);
79     OverflowSafeAdd(range_count_map[AddrPair(instr_range.start_addr, instr_range.end_addr)],
80                     total_count);
81     if (instr_range.branch_taken_count > 0) {
82       OverflowSafeAdd(branch_count_map[AddrPair(instr_range.end_addr, instr_range.branch_to_addr)],
83                       instr_range.branch_taken_count);
84     }
85   }
86 
Mergesimpleperf::__anon245dd9b60111::AutoFDOBinaryInfo87   void Merge(const AutoFDOBinaryInfo& other) {
88     for (const auto& p : other.address_count_map) {
89       auto res = address_count_map.emplace(p.first, p.second);
90       if (!res.second) {
91         OverflowSafeAdd(res.first->second, p.second);
92       }
93     }
94     for (const auto& p : other.range_count_map) {
95       auto res = range_count_map.emplace(p.first, p.second);
96       if (!res.second) {
97         OverflowSafeAdd(res.first->second, p.second);
98       }
99     }
100     for (const auto& p : other.branch_count_map) {
101       auto res = branch_count_map.emplace(p.first, p.second);
102       if (!res.second) {
103         OverflowSafeAdd(res.first->second, p.second);
104       }
105     }
106   }
107 
VaddrToOffsetsimpleperf::__anon245dd9b60111::AutoFDOBinaryInfo108   std::optional<uint64_t> VaddrToOffset(uint64_t vaddr) const {
109     for (const auto& segment : executable_segments) {
110       if (segment.vaddr <= vaddr && vaddr < segment.vaddr + segment.file_size) {
111         return vaddr - segment.vaddr + segment.file_offset;
112       }
113     }
114     return std::nullopt;
115   }
116 };
117 
118 using AutoFDOBinaryCallback = std::function<void(const BinaryKey&, AutoFDOBinaryInfo&)>;
119 using ETMBinaryCallback = std::function<void(const BinaryKey&, ETMBinary&)>;
120 using LBRDataCallback = std::function<void(LBRData&)>;
121 
GetExecutableSegments(const Dso * dso)122 static std::vector<ElfSegment> GetExecutableSegments(const Dso* dso) {
123   std::vector<ElfSegment> segments;
124   ElfStatus status;
125   if (auto elf = ElfFile::Open(dso->GetDebugFilePath(), &status); elf) {
126     segments = elf->GetProgramHeader();
127     auto not_executable = [](const ElfSegment& s) { return !s.is_executable; };
128     segments.erase(std::remove_if(segments.begin(), segments.end(), not_executable),
129                    segments.end());
130   }
131   return segments;
132 }
133 
134 // Base class for reading perf.data and generating AutoFDO or branch list data.
135 class PerfDataReader {
136  public:
GetDataType(RecordFileReader & reader)137   static std::string GetDataType(RecordFileReader& reader) {
138     const EventAttrIds& attrs = reader.AttrSection();
139     if (attrs.size() != 1) {
140       return "unknown";
141     }
142     const perf_event_attr& attr = attrs[0].attr;
143     if (IsEtmEventType(attr.type)) {
144       return "etm";
145     }
146     if (attr.sample_type & PERF_SAMPLE_BRANCH_STACK) {
147       return "lbr";
148     }
149     return "unknown";
150   }
151 
PerfDataReader(std::unique_ptr<RecordFileReader> reader,bool exclude_perf,const RegEx * binary_name_regex)152   PerfDataReader(std::unique_ptr<RecordFileReader> reader, bool exclude_perf,
153                  const RegEx* binary_name_regex)
154       : reader_(std::move(reader)),
155         exclude_perf_(exclude_perf),
156         binary_filter_(binary_name_regex) {}
~PerfDataReader()157   virtual ~PerfDataReader() {}
158 
GetDataType() const159   std::string GetDataType() const { return GetDataType(*reader_); }
160 
AddCallback(const AutoFDOBinaryCallback & callback)161   void AddCallback(const AutoFDOBinaryCallback& callback) { autofdo_callback_ = callback; }
AddCallback(const ETMBinaryCallback & callback)162   void AddCallback(const ETMBinaryCallback& callback) { etm_binary_callback_ = callback; }
AddCallback(const LBRDataCallback & callback)163   void AddCallback(const LBRDataCallback& callback) { lbr_data_callback_ = callback; }
164 
Read()165   virtual bool Read() {
166     if (exclude_perf_) {
167       const auto& info_map = reader_->GetMetaInfoFeature();
168       if (auto it = info_map.find("recording_process"); it == info_map.end()) {
169         LOG(ERROR) << reader_->FileName() << " doesn't support --exclude-perf";
170         return false;
171       } else {
172         int pid;
173         if (!android::base::ParseInt(it->second, &pid, 0)) {
174           LOG(ERROR) << "invalid recording_process " << it->second << " in " << reader_->FileName();
175           return false;
176         }
177         exclude_pid_ = pid;
178       }
179     }
180 
181     if (!reader_->LoadBuildIdAndFileFeatures(thread_tree_)) {
182       return false;
183     }
184     if (reader_->HasFeature(PerfFileFormat::FEAT_INIT_MAP)) {
185       if (!reader_->ReadInitMapFeature([this](auto r) { return ProcessRecord(*r); })) {
186         return false;
187       }
188     }
189     if (!reader_->ReadDataSection([this](auto r) { return ProcessRecord(*r); })) {
190       return false;
191     }
192     return PostProcess();
193   }
194 
195  protected:
196   virtual bool ProcessRecord(Record& r) = 0;
197   virtual bool PostProcess() = 0;
198 
ProcessAutoFDOBinaryInfo()199   void ProcessAutoFDOBinaryInfo() {
200     for (auto& p : autofdo_binary_map_) {
201       const Dso* dso = p.first;
202       AutoFDOBinaryInfo& binary = p.second;
203       binary.executable_segments = GetExecutableSegments(dso);
204       autofdo_callback_(BinaryKey(dso, 0), binary);
205     }
206   }
207 
208   const std::string data_type_;
209   std::unique_ptr<RecordFileReader> reader_;
210   bool exclude_perf_;
211   BinaryFilter binary_filter_;
212 
213   std::optional<int> exclude_pid_;
214   ThreadTree thread_tree_;
215   AutoFDOBinaryCallback autofdo_callback_;
216   ETMBinaryCallback etm_binary_callback_;
217   LBRDataCallback lbr_data_callback_;
218   // Store results for AutoFDO.
219   std::unordered_map<const Dso*, AutoFDOBinaryInfo> autofdo_binary_map_;
220 };
221 
222 class ETMThreadTreeWithFilter : public ETMThreadTree {
223  public:
ETMThreadTreeWithFilter(ThreadTree & thread_tree,std::optional<int> & exclude_pid,const std::vector<std::unique_ptr<RegEx>> & exclude_process_names)224   ETMThreadTreeWithFilter(ThreadTree& thread_tree, std::optional<int>& exclude_pid,
225                           const std::vector<std::unique_ptr<RegEx>>& exclude_process_names)
226       : thread_tree_(thread_tree),
227         exclude_pid_(exclude_pid),
228         exclude_process_names_(exclude_process_names) {}
229 
DisableThreadExitRecords()230   void DisableThreadExitRecords() override { thread_tree_.DisableThreadExitRecords(); }
231 
FindThread(int tid)232   const ThreadEntry* FindThread(int tid) override {
233     const ThreadEntry* thread = thread_tree_.FindThread(tid);
234     if (thread != nullptr && ShouldExcludePid(thread->pid)) {
235       return nullptr;
236     }
237     return thread;
238   }
239 
GetKernelMaps()240   const MapSet& GetKernelMaps() override { return thread_tree_.GetKernelMaps(); }
241 
242  private:
ShouldExcludePid(int pid)243   bool ShouldExcludePid(int pid) {
244     if (exclude_pid_ && pid == exclude_pid_) {
245       return true;
246     }
247     if (!exclude_process_names_.empty()) {
248       const ThreadEntry* process = thread_tree_.FindThread(pid);
249       if (process != nullptr) {
250         for (const auto& regex : exclude_process_names_) {
251           if (regex->Search(process->comm)) {
252             return true;
253           }
254         }
255       }
256     }
257     return false;
258   }
259 
260   ThreadTree& thread_tree_;
261   std::optional<int>& exclude_pid_;
262   const std::vector<std::unique_ptr<RegEx>>& exclude_process_names_;
263 };
264 
265 // Read perf.data with ETM data and generate AutoFDO or branch list data.
266 class ETMPerfDataReader : public PerfDataReader {
267  public:
ETMPerfDataReader(std::unique_ptr<RecordFileReader> reader,bool exclude_perf,const std::vector<std::unique_ptr<RegEx>> & exclude_process_names,const RegEx * binary_name_regex,ETMDumpOption etm_dump_option)268   ETMPerfDataReader(std::unique_ptr<RecordFileReader> reader, bool exclude_perf,
269                     const std::vector<std::unique_ptr<RegEx>>& exclude_process_names,
270                     const RegEx* binary_name_regex, ETMDumpOption etm_dump_option)
271       : PerfDataReader(std::move(reader), exclude_perf, binary_name_regex),
272         etm_dump_option_(etm_dump_option),
273         etm_thread_tree_(thread_tree_, exclude_pid_, exclude_process_names) {}
274 
Read()275   bool Read() override {
276     if (reader_->HasFeature(PerfFileFormat::FEAT_ETM_BRANCH_LIST)) {
277       return ProcessETMBranchListFeature();
278     }
279     return PerfDataReader::Read();
280   }
281 
282  private:
ProcessRecord(Record & r)283   bool ProcessRecord(Record& r) override {
284     thread_tree_.Update(r);
285     if (r.type() == PERF_RECORD_AUXTRACE_INFO) {
286       etm_decoder_ = ETMDecoder::Create(static_cast<AuxTraceInfoRecord&>(r), etm_thread_tree_);
287       if (!etm_decoder_) {
288         return false;
289       }
290       etm_decoder_->EnableDump(etm_dump_option_);
291       if (autofdo_callback_) {
292         etm_decoder_->RegisterCallback(
293             [this](const ETMInstrRange& range) { ProcessInstrRange(range); });
294       } else if (etm_binary_callback_) {
295         etm_decoder_->RegisterCallback(
296             [this](const ETMBranchList& branch) { ProcessETMBranchList(branch); });
297       }
298     } else if (r.type() == PERF_RECORD_AUX) {
299       AuxRecord& aux = static_cast<AuxRecord&>(r);
300       if (aux.data->aux_size > SIZE_MAX) {
301         LOG(ERROR) << "invalid aux size";
302         return false;
303       }
304       size_t aux_size = aux.data->aux_size;
305       if (aux_size > 0) {
306         bool error = false;
307         if (!reader_->ReadAuxData(aux.Cpu(), aux.data->aux_offset, aux_size, aux_data_buffer_,
308                                   error)) {
309           return !error;
310         }
311         if (!etm_decoder_) {
312           LOG(ERROR) << "ETMDecoder isn't created";
313           return false;
314         }
315         return etm_decoder_->ProcessData(aux_data_buffer_.data(), aux_size, !aux.Unformatted(),
316                                          aux.Cpu());
317       }
318     } else if (r.type() == PERF_RECORD_MMAP && r.InKernel()) {
319       auto& mmap_r = static_cast<MmapRecord&>(r);
320       if (android::base::StartsWith(mmap_r.filename, DEFAULT_KERNEL_MMAP_NAME)) {
321         kernel_map_start_addr_ = mmap_r.data->addr;
322       }
323     }
324     return true;
325   }
326 
PostProcess()327   bool PostProcess() override {
328     if (etm_decoder_ && !etm_decoder_->FinishData()) {
329       return false;
330     }
331     if (autofdo_callback_) {
332       ProcessAutoFDOBinaryInfo();
333     } else if (etm_binary_callback_) {
334       ProcessETMBinary();
335     }
336     return true;
337   }
338 
ProcessETMBranchListFeature()339   bool ProcessETMBranchListFeature() {
340     if (exclude_perf_) {
341       LOG(WARNING) << "--exclude-perf has no effect on perf.data with etm branch list";
342     }
343     if (autofdo_callback_) {
344       LOG(ERROR) << "convert to autofdo format isn't support on perf.data with etm branch list";
345       return false;
346     }
347     CHECK(etm_binary_callback_);
348     std::string s;
349     if (!reader_->ReadFeatureSection(PerfFileFormat::FEAT_ETM_BRANCH_LIST, &s)) {
350       return false;
351     }
352     ETMBinaryMap binary_map;
353     if (!StringToETMBinaryMap(s, binary_map)) {
354       return false;
355     }
356     for (auto& [key, binary] : binary_map) {
357       if (!binary_filter_.Filter(key.path)) {
358         continue;
359       }
360       etm_binary_callback_(key, binary);
361     }
362     return true;
363   }
364 
ProcessInstrRange(const ETMInstrRange & instr_range)365   void ProcessInstrRange(const ETMInstrRange& instr_range) {
366     if (!binary_filter_.Filter(instr_range.dso)) {
367       return;
368     }
369 
370     autofdo_binary_map_[instr_range.dso].AddInstrRange(instr_range);
371   }
372 
ProcessETMBranchList(const ETMBranchList & branch_list)373   void ProcessETMBranchList(const ETMBranchList& branch_list) {
374     if (!binary_filter_.Filter(branch_list.dso)) {
375       return;
376     }
377 
378     auto& branch_map = etm_binary_map_[branch_list.dso].branch_map;
379     ++branch_map[branch_list.addr][branch_list.branch];
380   }
381 
ProcessETMBinary()382   void ProcessETMBinary() {
383     for (auto& p : etm_binary_map_) {
384       Dso* dso = p.first;
385       ETMBinary& binary = p.second;
386       binary.dso_type = dso->type();
387       BinaryKey key(dso, 0);
388       if (binary.dso_type == DSO_KERNEL) {
389         if (kernel_map_start_addr_ == 0) {
390           LOG(WARNING) << "Can't convert kernel ip addresses without kernel start addr. So remove "
391                           "branches for the kernel.";
392           continue;
393         }
394         if (dso->GetDebugFilePath() == dso->Path()) {
395           // vmlinux isn't available. We still use kernel ip addr. Put kernel start addr in proto
396           // for address conversion later.
397           key.kernel_start_addr = kernel_map_start_addr_;
398         }
399       }
400       etm_binary_callback_(key, binary);
401     }
402   }
403 
404   ETMDumpOption etm_dump_option_;
405   ETMThreadTreeWithFilter etm_thread_tree_;
406   std::vector<uint8_t> aux_data_buffer_;
407   std::unique_ptr<ETMDecoder> etm_decoder_;
408   uint64_t kernel_map_start_addr_ = 0;
409   // Store etm branch list data.
410   std::unordered_map<Dso*, ETMBinary> etm_binary_map_;
411 };
412 
ConvertLBRDataToAutoFDO(const LBRData & lbr_data)413 static std::optional<std::vector<AutoFDOBinaryInfo>> ConvertLBRDataToAutoFDO(
414     const LBRData& lbr_data) {
415   std::vector<AutoFDOBinaryInfo> binaries(lbr_data.binaries.size());
416   for (const LBRSample& sample : lbr_data.samples) {
417     if (sample.binary_id != 0) {
418       if (sample.binary_id > binaries.size()) {
419         LOG(ERROR) << "binary_id out of range";
420         return std::nullopt;
421       }
422       binaries[sample.binary_id - 1].AddAddress(sample.vaddr_in_file);
423     }
424     for (size_t i = 0; i < sample.branches.size(); ++i) {
425       const LBRBranch& branch = sample.branches[i];
426       if (branch.from_binary_id == 0) {
427         continue;
428       }
429       if (branch.from_binary_id > binaries.size()) {
430         LOG(ERROR) << "binary_id out of range";
431         return std::nullopt;
432       }
433       if (branch.from_binary_id == branch.to_binary_id) {
434         binaries[branch.from_binary_id - 1].AddBranch(branch.from_vaddr_in_file,
435                                                       branch.to_vaddr_in_file);
436       }
437       if (i > 0 && branch.from_binary_id == sample.branches[i - 1].to_binary_id) {
438         uint64_t begin = sample.branches[i - 1].to_vaddr_in_file;
439         uint64_t end = branch.from_vaddr_in_file;
440         // Use the same logic to skip bogus LBR data as AutoFDO.
441         if (end < begin || end - begin > (1 << 20)) {
442           continue;
443         }
444         binaries[branch.from_binary_id - 1].AddRange(begin, end);
445       }
446     }
447   }
448   return binaries;
449 }
450 
451 class LBRPerfDataReader : public PerfDataReader {
452  public:
LBRPerfDataReader(std::unique_ptr<RecordFileReader> reader,bool exclude_perf,const RegEx * binary_name_regex)453   LBRPerfDataReader(std::unique_ptr<RecordFileReader> reader, bool exclude_perf,
454                     const RegEx* binary_name_regex)
455       : PerfDataReader(std::move(reader), exclude_perf, binary_name_regex) {}
456 
457  private:
ProcessRecord(Record & r)458   bool ProcessRecord(Record& r) override {
459     thread_tree_.Update(r);
460     if (r.type() == PERF_RECORD_SAMPLE) {
461       auto& sr = static_cast<SampleRecord&>(r);
462       ThreadEntry* thread = thread_tree_.FindThread(sr.tid_data.tid);
463       if (thread == nullptr) {
464         return true;
465       }
466       auto& stack = sr.branch_stack_data;
467       lbr_data_.samples.resize(lbr_data_.samples.size() + 1);
468       LBRSample& sample = lbr_data_.samples.back();
469       std::pair<uint32_t, uint64_t> binary_addr = IpToBinaryAddr(*thread, sr.ip_data.ip);
470       sample.binary_id = binary_addr.first;
471       bool has_valid_binary_id = sample.binary_id != 0;
472       sample.vaddr_in_file = binary_addr.second;
473       sample.branches.resize(stack.stack_nr);
474       for (size_t i = 0; i < stack.stack_nr; ++i) {
475         uint64_t from_ip = stack.stack[i].from;
476         uint64_t to_ip = stack.stack[i].to;
477         LBRBranch& branch = sample.branches[i];
478         binary_addr = IpToBinaryAddr(*thread, from_ip);
479         branch.from_binary_id = binary_addr.first;
480         branch.from_vaddr_in_file = binary_addr.second;
481         binary_addr = IpToBinaryAddr(*thread, to_ip);
482         branch.to_binary_id = binary_addr.first;
483         branch.to_vaddr_in_file = binary_addr.second;
484         if (branch.from_binary_id != 0 || branch.to_binary_id != 0) {
485           has_valid_binary_id = true;
486         }
487       }
488       if (!has_valid_binary_id) {
489         lbr_data_.samples.pop_back();
490       }
491     }
492     return true;
493   }
494 
PostProcess()495   bool PostProcess() override {
496     if (autofdo_callback_) {
497       std::optional<std::vector<AutoFDOBinaryInfo>> binaries = ConvertLBRDataToAutoFDO(lbr_data_);
498       if (!binaries) {
499         return false;
500       }
501       for (const auto& [dso, binary_id] : dso_map_) {
502         autofdo_binary_map_[dso] = std::move(binaries.value()[binary_id - 1]);
503       }
504       ProcessAutoFDOBinaryInfo();
505     } else if (lbr_data_callback_) {
506       lbr_data_callback_(lbr_data_);
507     }
508     return true;
509   }
510 
IpToBinaryAddr(ThreadEntry & thread,uint64_t ip)511   std::pair<uint32_t, uint64_t> IpToBinaryAddr(ThreadEntry& thread, uint64_t ip) {
512     const MapEntry* map = thread_tree_.FindMap(&thread, ip);
513     Dso* dso = map->dso;
514     if (thread_tree_.IsUnknownDso(dso) || !binary_filter_.Filter(dso)) {
515       return std::make_pair(0, 0);
516     }
517     uint32_t binary_id = GetBinaryId(dso);
518     uint64_t vaddr_in_file = dso->IpToVaddrInFile(ip, map->start_addr, map->pgoff);
519     return std::make_pair(binary_id, vaddr_in_file);
520   }
521 
GetBinaryId(const Dso * dso)522   uint32_t GetBinaryId(const Dso* dso) {
523     if (auto it = dso_map_.find(dso); it != dso_map_.end()) {
524       return it->second;
525     }
526     lbr_data_.binaries.emplace_back(dso, 0);
527     uint32_t binary_id = static_cast<uint32_t>(lbr_data_.binaries.size());
528     dso_map_[dso] = binary_id;
529     return binary_id;
530   }
531 
532   LBRData lbr_data_;
533   // Map from dso to binary_id in lbr_data_.
534   std::unordered_map<const Dso*, uint32_t> dso_map_;
535 };
536 
537 // Read a protobuf file specified by branch_list.proto.
538 class BranchListReader {
539  public:
BranchListReader(std::string_view filename,const RegEx * binary_name_regex)540   BranchListReader(std::string_view filename, const RegEx* binary_name_regex)
541       : filename_(filename), binary_filter_(binary_name_regex) {}
542 
AddCallback(const ETMBinaryCallback & callback)543   void AddCallback(const ETMBinaryCallback& callback) { etm_binary_callback_ = callback; }
AddCallback(const LBRDataCallback & callback)544   void AddCallback(const LBRDataCallback& callback) { lbr_data_callback_ = callback; }
545 
Read()546   bool Read() {
547     auto reader = BranchListProtoReader::CreateForFile(filename_);
548     if (!reader) {
549       return false;
550     }
551     ETMBinaryMap etm_data;
552     LBRData lbr_data;
553     if (!reader->Read(etm_data, lbr_data)) {
554       return false;
555     }
556     if (etm_binary_callback_ && !etm_data.empty()) {
557       ProcessETMData(etm_data);
558     }
559     if (lbr_data_callback_ && !lbr_data.samples.empty()) {
560       ProcessLBRData(lbr_data);
561     }
562     return true;
563   }
564 
565  private:
ProcessETMData(ETMBinaryMap & etm_data)566   void ProcessETMData(ETMBinaryMap& etm_data) {
567     for (auto& [key, binary] : etm_data) {
568       if (!binary_filter_.Filter(key.path)) {
569         continue;
570       }
571       etm_binary_callback_(key, binary);
572     }
573   }
574 
ProcessLBRData(LBRData & lbr_data)575   void ProcessLBRData(LBRData& lbr_data) {
576     // 1. Check if we need to remove binaries.
577     std::vector<uint32_t> new_ids(lbr_data.binaries.size());
578     uint32_t next_id = 1;
579 
580     for (size_t i = 0; i < lbr_data.binaries.size(); ++i) {
581       if (!binary_filter_.Filter(lbr_data.binaries[i].path)) {
582         new_ids[i] = 0;
583       } else {
584         new_ids[i] = next_id++;
585       }
586     }
587 
588     if (next_id <= lbr_data.binaries.size()) {
589       // 2. Modify lbr_data.binaries.
590       for (size_t i = 0; i < lbr_data.binaries.size(); ++i) {
591         if (new_ids[i] != 0) {
592           size_t new_pos = new_ids[i] - 1;
593           lbr_data.binaries[new_pos] = lbr_data.binaries[i];
594         }
595       }
596       lbr_data.binaries.resize(next_id - 1);
597 
598       // 3. Modify lbr_data.samples.
599       auto convert_id = [&](uint32_t& binary_id) {
600         if (binary_id != 0) {
601           binary_id = (binary_id <= new_ids.size()) ? new_ids[binary_id - 1] : 0;
602         }
603       };
604       std::vector<LBRSample> new_samples;
605       for (LBRSample& sample : lbr_data.samples) {
606         convert_id(sample.binary_id);
607         bool has_valid_binary_id = sample.binary_id != 0;
608         for (LBRBranch& branch : sample.branches) {
609           convert_id(branch.from_binary_id);
610           convert_id(branch.to_binary_id);
611           if (branch.from_binary_id != 0 || branch.to_binary_id != 0) {
612             has_valid_binary_id = true;
613           }
614         }
615         if (has_valid_binary_id) {
616           new_samples.emplace_back(std::move(sample));
617         }
618       }
619       lbr_data.samples = std::move(new_samples);
620     }
621     lbr_data_callback_(lbr_data);
622   }
623 
624   const std::string filename_;
625   BinaryFilter binary_filter_;
626   ETMBinaryCallback etm_binary_callback_;
627   LBRDataCallback lbr_data_callback_;
628 };
629 
630 // Convert ETMBinary into AutoFDOBinaryInfo.
631 class ETMBranchListToAutoFDOConverter {
632  public:
Convert(const BinaryKey & key,ETMBinary & binary)633   std::unique_ptr<AutoFDOBinaryInfo> Convert(const BinaryKey& key, ETMBinary& binary) {
634     BuildId build_id = key.build_id;
635     std::unique_ptr<Dso> dso = Dso::CreateDsoWithBuildId(binary.dso_type, key.path, build_id);
636     if (!dso || !CheckBuildId(dso.get(), key.build_id)) {
637       return nullptr;
638     }
639     std::unique_ptr<AutoFDOBinaryInfo> autofdo_binary(new AutoFDOBinaryInfo);
640     autofdo_binary->executable_segments = GetExecutableSegments(dso.get());
641 
642     if (dso->type() == DSO_KERNEL) {
643       CHECK_EQ(key.kernel_start_addr, 0);
644     }
645 
646     auto process_instr_range = [&](const ETMInstrRange& range) {
647       autofdo_binary->AddInstrRange(range);
648     };
649 
650     auto result = ConvertETMBranchMapToInstrRanges(dso.get(), binary.GetOrderedBranchMap(),
651                                                    process_instr_range);
652     if (!result.ok()) {
653       LOG(WARNING) << "failed to build instr ranges for binary " << dso->Path() << ": "
654                    << result.error();
655       return nullptr;
656     }
657     return autofdo_binary;
658   }
659 
660  private:
CheckBuildId(Dso * dso,const BuildId & expected_build_id)661   bool CheckBuildId(Dso* dso, const BuildId& expected_build_id) {
662     if (expected_build_id.IsEmpty()) {
663       return true;
664     }
665     BuildId build_id;
666     return GetBuildIdFromDsoPath(dso->GetDebugFilePath(), &build_id) &&
667            build_id == expected_build_id;
668   }
669 };
670 
671 // Write instruction ranges to a file in AutoFDO text format.
672 class AutoFDOWriter {
673  public:
AddAutoFDOBinary(const BinaryKey & key,AutoFDOBinaryInfo & binary)674   void AddAutoFDOBinary(const BinaryKey& key, AutoFDOBinaryInfo& binary) {
675     auto it = binary_map_.find(key);
676     if (it == binary_map_.end()) {
677       binary_map_[key] = std::move(binary);
678     } else {
679       it->second.Merge(binary);
680     }
681   }
682 
WriteAutoFDO(const std::string & output_filename)683   bool WriteAutoFDO(const std::string& output_filename) {
684     std::unique_ptr<FILE, decltype(&fclose)> output_fp(fopen(output_filename.c_str(), "w"), fclose);
685     if (!output_fp) {
686       PLOG(ERROR) << "failed to write to " << output_filename;
687       return false;
688     }
689     // autofdo_binary_map is used to store instruction ranges, which can have a large amount. And
690     // it has a larger access time (instruction ranges * executed time). So it's better to use
691     // unorder_maps to speed up access time. But we also want a stable output here, to compare
692     // output changes result from code changes. So generate a sorted output here.
693     std::vector<BinaryKey> keys;
694     for (auto& p : binary_map_) {
695       keys.emplace_back(p.first);
696     }
697     std::sort(keys.begin(), keys.end(),
698               [](const BinaryKey& key1, const BinaryKey& key2) { return key1.path < key2.path; });
699     if (keys.size() > 1) {
700       fprintf(output_fp.get(),
701               "// Please split this file. AutoFDO only accepts profile for one binary.\n");
702     }
703     for (const auto& key : keys) {
704       const AutoFDOBinaryInfo& binary = binary_map_[key];
705       // AutoFDO text format needs file_offsets instead of virtual addrs in a binary. So convert
706       // vaddrs to file offsets.
707 
708       // Write range_count_map. Sort the output by addrs.
709       std::vector<std::pair<AddrPair, uint64_t>> range_counts;
710       for (std::pair<AddrPair, uint64_t> p : binary.range_count_map) {
711         std::optional<uint64_t> start_offset = binary.VaddrToOffset(p.first.first);
712         std::optional<uint64_t> end_offset = binary.VaddrToOffset(p.first.second);
713         if (start_offset && end_offset) {
714           p.first.first = start_offset.value();
715           p.first.second = end_offset.value();
716           range_counts.emplace_back(p);
717         }
718       }
719       std::sort(range_counts.begin(), range_counts.end());
720       fprintf(output_fp.get(), "%zu\n", range_counts.size());
721       for (const auto& p : range_counts) {
722         fprintf(output_fp.get(), "%" PRIx64 "-%" PRIx64 ":%" PRIu64 "\n", p.first.first,
723                 p.first.second, p.second);
724       }
725 
726       // Write addr_count_map. Sort the output by addrs.
727       std::vector<std::pair<uint64_t, uint64_t>> address_counts;
728       for (std::pair<uint64_t, uint64_t> p : binary.address_count_map) {
729         std::optional<uint64_t> offset = binary.VaddrToOffset(p.first);
730         if (offset) {
731           p.first = offset.value();
732           address_counts.emplace_back(p);
733         }
734       }
735       std::sort(address_counts.begin(), address_counts.end());
736       fprintf(output_fp.get(), "%zu\n", address_counts.size());
737       for (const auto& p : address_counts) {
738         fprintf(output_fp.get(), "%" PRIx64 ":%" PRIu64 "\n", p.first, p.second);
739       }
740 
741       // Write branch_count_map. Sort the output by addrs.
742       std::vector<std::pair<AddrPair, uint64_t>> branch_counts;
743       for (std::pair<AddrPair, uint64_t> p : binary.branch_count_map) {
744         std::optional<uint64_t> from_offset = binary.VaddrToOffset(p.first.first);
745         std::optional<uint64_t> to_offset = binary.VaddrToOffset(p.first.second);
746         if (from_offset) {
747           p.first.first = from_offset.value();
748           p.first.second = to_offset ? to_offset.value() : 0;
749           branch_counts.emplace_back(p);
750         }
751       }
752       std::sort(branch_counts.begin(), branch_counts.end());
753       fprintf(output_fp.get(), "%zu\n", branch_counts.size());
754       for (const auto& p : branch_counts) {
755         fprintf(output_fp.get(), "%" PRIx64 "->%" PRIx64 ":%" PRIu64 "\n", p.first.first,
756                 p.first.second, p.second);
757       }
758 
759       // Write the binary path in comment.
760       fprintf(output_fp.get(), "// build_id: %s\n", key.build_id.ToString().c_str());
761       fprintf(output_fp.get(), "// %s\n\n", key.path.c_str());
762     }
763     return true;
764   }
765 
766   // Write bolt profile in format documented in
767   // https://github.com/llvm/llvm-project/blob/main/bolt/include/bolt/Profile/DataAggregator.h#L372.
WriteBolt(const std::string & output_filename)768   bool WriteBolt(const std::string& output_filename) {
769     std::unique_ptr<FILE, decltype(&fclose)> output_fp(fopen(output_filename.c_str(), "w"), fclose);
770     if (!output_fp) {
771       PLOG(ERROR) << "failed to write to " << output_filename;
772       return false;
773     }
774     // autofdo_binary_map is used to store instruction ranges, which can have a large amount. And
775     // it has a larger access time (instruction ranges * executed time). So it's better to use
776     // unorder_maps to speed up access time. But we also want a stable output here, to compare
777     // output changes result from code changes. So generate a sorted output here.
778     std::vector<BinaryKey> keys;
779     for (auto& p : binary_map_) {
780       keys.emplace_back(p.first);
781     }
782     std::sort(keys.begin(), keys.end(),
783               [](const BinaryKey& key1, const BinaryKey& key2) { return key1.path < key2.path; });
784     if (keys.size() > 1) {
785       fprintf(output_fp.get(),
786               "// Please split this file. BOLT only accepts profile for one binary.\n");
787     }
788 
789     for (const auto& key : keys) {
790       const AutoFDOBinaryInfo& binary = binary_map_[key];
791       // Write range_count_map. Sort the output by addrs.
792       std::vector<std::pair<AddrPair, uint64_t>> range_counts;
793       for (const auto& p : binary.range_count_map) {
794         range_counts.emplace_back(p);
795       }
796       std::sort(range_counts.begin(), range_counts.end());
797       for (const auto& p : range_counts) {
798         fprintf(output_fp.get(), "F %" PRIx64 " %" PRIx64 " %" PRIu64 "\n", p.first.first,
799                 p.first.second, p.second);
800       }
801 
802       // Write branch_count_map. Sort the output by addrs.
803       std::vector<std::pair<AddrPair, uint64_t>> branch_counts;
804       for (const auto& p : binary.branch_count_map) {
805         branch_counts.emplace_back(p);
806       }
807       std::sort(branch_counts.begin(), branch_counts.end());
808       for (const auto& p : branch_counts) {
809         fprintf(output_fp.get(), "B %" PRIx64 " %" PRIx64 " %" PRIu64 " 0\n", p.first.first,
810                 p.first.second, p.second);
811       }
812 
813       // Write the binary path in comment.
814       fprintf(output_fp.get(), "// build_id: %s\n", key.build_id.ToString().c_str());
815       fprintf(output_fp.get(), "// %s\n", key.path.c_str());
816     }
817     return true;
818   }
819 
820  private:
821   std::unordered_map<BinaryKey, AutoFDOBinaryInfo, BinaryKeyHash> binary_map_;
822 };
823 
824 // Merge branch list data.
825 struct BranchListMerger {
AddETMBinarysimpleperf::__anon245dd9b60111::BranchListMerger826   void AddETMBinary(const BinaryKey& key, ETMBinary& binary) {
827     if (auto it = etm_data_.find(key); it != etm_data_.end()) {
828       it->second.Merge(binary);
829     } else {
830       etm_data_[key] = std::move(binary);
831     }
832   }
833 
AddLBRDatasimpleperf::__anon245dd9b60111::BranchListMerger834   void AddLBRData(LBRData& lbr_data) {
835     // 1. Merge binaries.
836     std::vector<uint32_t> new_ids(lbr_data.binaries.size());
837     for (size_t i = 0; i < lbr_data.binaries.size(); i++) {
838       const BinaryKey& key = lbr_data.binaries[i];
839       if (auto it = lbr_binary_id_map_.find(key); it != lbr_binary_id_map_.end()) {
840         new_ids[i] = it->second;
841       } else {
842         uint32_t next_id = static_cast<uint32_t>(lbr_binary_id_map_.size()) + 1;
843         new_ids[i] = next_id;
844         lbr_binary_id_map_[key] = next_id;
845         lbr_data_.binaries.emplace_back(key);
846       }
847     }
848 
849     // 2. Merge samples.
850     auto convert_id = [&](uint32_t& binary_id) {
851       if (binary_id != 0) {
852         binary_id = (binary_id <= new_ids.size()) ? new_ids[binary_id - 1] : 0;
853       }
854     };
855 
856     for (LBRSample& sample : lbr_data.samples) {
857       convert_id(sample.binary_id);
858       for (LBRBranch& branch : sample.branches) {
859         convert_id(branch.from_binary_id);
860         convert_id(branch.to_binary_id);
861       }
862       lbr_data_.samples.emplace_back(std::move(sample));
863     }
864   }
865 
Mergesimpleperf::__anon245dd9b60111::BranchListMerger866   void Merge(BranchListMerger& other) {
867     for (auto& p : other.GetETMData()) {
868       AddETMBinary(p.first, p.second);
869     }
870     AddLBRData(other.GetLBRData());
871   }
872 
GetETMDatasimpleperf::__anon245dd9b60111::BranchListMerger873   ETMBinaryMap& GetETMData() { return etm_data_; }
874 
GetLBRDatasimpleperf::__anon245dd9b60111::BranchListMerger875   LBRData& GetLBRData() { return lbr_data_; }
876 
877  private:
878   ETMBinaryMap etm_data_;
879   LBRData lbr_data_;
880   std::unordered_map<BinaryKey, uint32_t, BinaryKeyHash> lbr_binary_id_map_;
881 };
882 
883 // Read multiple branch list files and merge them using BranchListMerger.
884 class BranchListMergedReader {
885  public:
BranchListMergedReader(bool allow_mismatched_build_id,const RegEx * binary_name_regex,size_t jobs)886   BranchListMergedReader(bool allow_mismatched_build_id, const RegEx* binary_name_regex,
887                          size_t jobs)
888       : allow_mismatched_build_id_(allow_mismatched_build_id),
889         binary_name_regex_(binary_name_regex),
890         jobs_(jobs) {}
891 
Read(const std::vector<std::string> & input_filenames)892   std::unique_ptr<BranchListMerger> Read(const std::vector<std::string>& input_filenames) {
893     std::mutex input_file_mutex;
894     size_t input_file_index = 0;
895     auto get_input_file = [&]() -> std::string_view {
896       std::lock_guard<std::mutex> guard(input_file_mutex);
897       if (input_file_index == input_filenames.size()) {
898         return "";
899       }
900       if ((input_file_index + 1) % 100 == 0) {
901         LOG(VERBOSE) << "Read input file " << (input_file_index + 1) << "/"
902                      << input_filenames.size();
903       }
904       return input_filenames[input_file_index++];
905     };
906 
907     std::atomic_size_t failed_to_read_count = 0;
908     size_t thread_count = std::min(jobs_, input_filenames.size()) - 1;
909     std::vector<BranchListMerger> thread_mergers(thread_count);
910     std::vector<std::unique_ptr<std::thread>> threads;
911 
912     for (size_t i = 0; i < thread_count; i++) {
913       threads.emplace_back(new std::thread([&, i]() {
914         ReadInThreadFunction(get_input_file, thread_mergers[i], failed_to_read_count);
915       }));
916     }
917     auto merger = std::make_unique<BranchListMerger>();
918     ReadInThreadFunction(get_input_file, *merger, failed_to_read_count);
919     for (size_t i = 0; i < thread_count; i++) {
920       threads[i]->join();
921       merger->Merge(thread_mergers[i]);
922     }
923     if (failed_to_read_count == input_filenames.size()) {
924       LOG(ERROR) << "No valid input file";
925       return nullptr;
926     }
927     return merger;
928   }
929 
930  private:
ReadInThreadFunction(const std::function<std::string_view ()> & get_input_file,BranchListMerger & merger,std::atomic_size_t & failed_to_read_count)931   void ReadInThreadFunction(const std::function<std::string_view()>& get_input_file,
932                             BranchListMerger& merger, std::atomic_size_t& failed_to_read_count) {
933     auto etm_callback = [&](const BinaryKey& key, ETMBinary& binary) {
934       BinaryKey new_key = key;
935       if (allow_mismatched_build_id_) {
936         new_key.build_id = BuildId();
937       }
938       if (binary.dso_type == DsoType::DSO_KERNEL) {
939         ModifyBranchMapForKernel(new_key, binary);
940       }
941       merger.AddETMBinary(new_key, binary);
942     };
943     auto lbr_callback = [&](LBRData& lbr_data) {
944       if (allow_mismatched_build_id_) {
945         for (BinaryKey& key : lbr_data.binaries) {
946           key.build_id = BuildId();
947         }
948       }
949       merger.AddLBRData(lbr_data);
950     };
951     while (true) {
952       std::string_view input_file = get_input_file();
953       if (input_file.empty()) {
954         break;
955       }
956       BranchListReader reader(input_file, binary_name_regex_);
957       reader.AddCallback(etm_callback);
958       reader.AddCallback(lbr_callback);
959       if (!reader.Read()) {
960         failed_to_read_count++;
961       }
962     }
963   }
964 
ModifyBranchMapForKernel(BinaryKey & key,ETMBinary & binary)965   void ModifyBranchMapForKernel(BinaryKey& key, ETMBinary& binary) {
966     if (key.kernel_start_addr == 0) {
967       // vmlinux has been provided when generating branch lists. Addresses in branch lists are
968       // already vaddrs in vmlinux.
969       return;
970     }
971     {
972       std::lock_guard<std::mutex> guard(kernel_dso_mutex_);
973       if (!kernel_dso_) {
974         BuildId build_id = key.build_id;
975         kernel_dso_ = Dso::CreateDsoWithBuildId(binary.dso_type, key.path, build_id);
976         if (!kernel_dso_) {
977           return;
978         }
979         // Call IpToVaddrInFile once to initialize kernel start addr from vmlinux.
980         kernel_dso_->IpToVaddrInFile(0, key.kernel_start_addr, 0);
981       }
982     }
983     // Addresses are still kernel ip addrs in memory. Need to convert them to vaddrs in vmlinux.
984     UnorderedETMBranchMap new_branch_map;
985     for (auto& p : binary.branch_map) {
986       uint64_t vaddr_in_file = kernel_dso_->IpToVaddrInFile(p.first, key.kernel_start_addr, 0);
987       new_branch_map[vaddr_in_file] = std::move(p.second);
988     }
989     binary.branch_map = std::move(new_branch_map);
990     key.kernel_start_addr = 0;
991   }
992 
993   const bool allow_mismatched_build_id_;
994   const RegEx* binary_name_regex_;
995   size_t jobs_;
996   std::unique_ptr<Dso> kernel_dso_;
997   std::mutex kernel_dso_mutex_;
998 };
999 
1000 // Write branch lists to a protobuf file specified by branch_list.proto.
WriteBranchListFile(const std::string & output_filename,const ETMBinaryMap & etm_data,const LBRData & lbr_data,bool compress)1001 static bool WriteBranchListFile(const std::string& output_filename, const ETMBinaryMap& etm_data,
1002                                 const LBRData& lbr_data, bool compress) {
1003   auto writer = BranchListProtoWriter::CreateForFile(output_filename, compress);
1004   if (!writer) {
1005     return false;
1006   }
1007   if (!etm_data.empty()) {
1008     return writer->Write(etm_data);
1009   }
1010   if (!lbr_data.samples.empty()) {
1011     return writer->Write(lbr_data);
1012   }
1013   // Don't produce empty output file.
1014   LOG(INFO) << "Skip empty output file.";
1015   unlink(output_filename.c_str());
1016   return true;
1017 }
1018 
1019 class InjectCommand : public Command {
1020  public:
InjectCommand()1021   InjectCommand()
1022       : Command("inject", "parse etm instruction tracing data",
1023                 // clang-format off
1024 "Usage: simpleperf inject [options]\n"
1025 "--binary binary_name         Generate data only for binaries matching binary_name regex.\n"
1026 "-i file1,file2,...           Input files. Default is perf.data. Support below formats:\n"
1027 "                               1. perf.data generated by recording cs-etm event type.\n"
1028 "                               2. branch_list file generated by `inject --output branch-list`.\n"
1029 "                             If a file name starts with @, it contains a list of input files.\n"
1030 "-o <file>                    output file. Default is perf_inject.data.\n"
1031 "--output <format>            Select output file format:\n"
1032 "                               autofdo      -- text format accepted by TextSampleReader\n"
1033 "                                               of AutoFDO\n"
1034 "                               bolt         -- text format accepted by `perf2bolt --pa`\n"
1035 "                               branch-list  -- protobuf file in etm_branch_list.proto\n"
1036 "                             Default is autofdo.\n"
1037 "--dump-etm type1,type2,...   Dump etm data. A type is one of raw, packet and element.\n"
1038 "--exclude-perf               Exclude trace data for the recording process.\n"
1039 "--exclude-process-name process_name_regex      Exclude data for processes with name containing\n"
1040 "                                               the regular expression.\n"
1041 "--symdir <dir>               Look for binaries in a directory recursively.\n"
1042 "--allow-mismatched-build-id  Allow mismatched build ids when searching for debug binaries.\n"
1043 "-j <jobs>                    Use multiple threads to process branch list files.\n"
1044 "-z                           Compress branch-list output\n"
1045 "--dump <file>                Dump a branch list file.\n"
1046 "\n"
1047 "Examples:\n"
1048 "1. Generate autofdo text output.\n"
1049 "$ simpleperf inject -i perf.data -o autofdo.txt --output autofdo\n"
1050 "\n"
1051 "2. Generate branch list proto, then convert to autofdo text.\n"
1052 "$ simpleperf inject -i perf.data -o branch_list.data --output branch-list\n"
1053 "$ simpleperf inject -i branch_list.data -o autofdo.txt --output autofdo\n"
1054                 // clang-format on
1055         ) {}
1056 
Run(const std::vector<std::string> & args)1057   bool Run(const std::vector<std::string>& args) override {
1058     GOOGLE_PROTOBUF_VERIFY_VERSION;
1059     if (!ParseOptions(args)) {
1060       return false;
1061     }
1062     if (!dump_branch_list_file_.empty()) {
1063       return DumpBranchListFile(dump_branch_list_file_);
1064     }
1065 
1066     CHECK(!input_filenames_.empty());
1067     if (IsPerfDataFile(input_filenames_[0])) {
1068       switch (output_format_) {
1069         case OutputFormat::AutoFDO:
1070           [[fallthrough]];
1071         case OutputFormat::BOLT:
1072           return ConvertPerfDataToAutoFDO();
1073         case OutputFormat::BranchList:
1074           return ConvertPerfDataToBranchList();
1075       }
1076     } else {
1077       switch (output_format_) {
1078         case OutputFormat::AutoFDO:
1079           [[fallthrough]];
1080         case OutputFormat::BOLT:
1081           return ConvertBranchListToAutoFDO();
1082         case OutputFormat::BranchList:
1083           return ConvertBranchListToBranchList();
1084       }
1085     }
1086   }
1087 
1088  private:
ParseOptions(const std::vector<std::string> & args)1089   bool ParseOptions(const std::vector<std::string>& args) {
1090     const OptionFormatMap option_formats = {
1091         {"--allow-mismatched-build-id", {OptionValueType::NONE, OptionType::SINGLE}},
1092         {"--binary", {OptionValueType::STRING, OptionType::SINGLE}},
1093         {"--dump", {OptionValueType::STRING, OptionType::SINGLE}},
1094         {"--dump-etm", {OptionValueType::STRING, OptionType::SINGLE}},
1095         {"--exclude-perf", {OptionValueType::NONE, OptionType::SINGLE}},
1096         {"--exclude-process-name", {OptionValueType::STRING, OptionType::MULTIPLE}},
1097         {"-i", {OptionValueType::STRING, OptionType::MULTIPLE}},
1098         {"-j", {OptionValueType::UINT, OptionType::SINGLE}},
1099         {"-o", {OptionValueType::STRING, OptionType::SINGLE}},
1100         {"--output", {OptionValueType::STRING, OptionType::SINGLE}},
1101         {"--symdir", {OptionValueType::STRING, OptionType::MULTIPLE}},
1102         {"-z", {OptionValueType::NONE, OptionType::SINGLE}},
1103     };
1104     OptionValueMap options;
1105     std::vector<std::pair<OptionName, OptionValue>> ordered_options;
1106     if (!PreprocessOptions(args, option_formats, &options, &ordered_options, nullptr)) {
1107       return false;
1108     }
1109 
1110     if (options.PullBoolValue("--allow-mismatched-build-id")) {
1111       allow_mismatched_build_id_ = true;
1112       Dso::AllowMismatchedBuildId();
1113     }
1114     if (auto value = options.PullValue("--binary"); value) {
1115       binary_name_regex_ = RegEx::Create(value->str_value);
1116       if (binary_name_regex_ == nullptr) {
1117         return false;
1118       }
1119     }
1120     options.PullStringValue("--dump", &dump_branch_list_file_);
1121     if (auto value = options.PullValue("--dump-etm"); value) {
1122       if (!ParseEtmDumpOption(value->str_value, &etm_dump_option_)) {
1123         return false;
1124       }
1125     }
1126     exclude_perf_ = options.PullBoolValue("--exclude-perf");
1127     for (const std::string& value : options.PullStringValues("--exclude-process-name")) {
1128       std::unique_ptr<RegEx> regex = RegEx::Create(value);
1129       if (regex == nullptr) {
1130         return false;
1131       }
1132       exclude_process_names_.emplace_back(std::move(regex));
1133     }
1134 
1135     for (const OptionValue& value : options.PullValues("-i")) {
1136       std::vector<std::string> files = android::base::Split(value.str_value, ",");
1137       for (std::string& file : files) {
1138         if (android::base::StartsWith(file, "@")) {
1139           if (!ReadFileList(file.substr(1), &input_filenames_)) {
1140             return false;
1141           }
1142         } else {
1143           input_filenames_.emplace_back(file);
1144         }
1145       }
1146     }
1147     if (input_filenames_.empty()) {
1148       input_filenames_.emplace_back("perf.data");
1149     }
1150     if (!options.PullUintValue("-j", &jobs_, 1)) {
1151       return false;
1152     }
1153     options.PullStringValue("-o", &output_filename_);
1154     if (auto value = options.PullValue("--output"); value) {
1155       const std::string& output = value->str_value;
1156       if (output == "autofdo") {
1157         output_format_ = OutputFormat::AutoFDO;
1158       } else if (output == "bolt") {
1159         output_format_ = OutputFormat::BOLT;
1160       } else if (output == "branch-list") {
1161         output_format_ = OutputFormat::BranchList;
1162       } else {
1163         LOG(ERROR) << "unknown format in --output option: " << output;
1164         return false;
1165       }
1166     }
1167     if (std::vector<OptionValue> values = options.PullValues("--symdir"); !values.empty()) {
1168       for (const OptionValue& value : values) {
1169         if (!Dso::AddSymbolDir(value.str_value)) {
1170           return false;
1171         }
1172       }
1173       // Symbol dirs are cleaned when Dso count is decreased to zero, which can happen between
1174       // processing input files. To make symbol dirs always available, create a placeholder dso to
1175       // prevent cleaning from happening.
1176       placeholder_dso_ = Dso::CreateDso(DSO_UNKNOWN_FILE, "unknown");
1177     }
1178     compress_ = options.PullBoolValue("-z");
1179     CHECK(options.values.empty());
1180     return true;
1181   }
1182 
ReadFileList(const std::string & path,std::vector<std::string> * file_list)1183   bool ReadFileList(const std::string& path, std::vector<std::string>* file_list) {
1184     std::string data;
1185     if (!android::base::ReadFileToString(path, &data)) {
1186       PLOG(ERROR) << "failed to read " << path;
1187       return false;
1188     }
1189     std::vector<std::string> tokens = android::base::Tokenize(data, " \t\n\r");
1190     file_list->insert(file_list->end(), tokens.begin(), tokens.end());
1191     return true;
1192   }
1193 
ReadPerfDataFiles(const std::function<void (PerfDataReader &)> reader_callback)1194   bool ReadPerfDataFiles(const std::function<void(PerfDataReader&)> reader_callback) {
1195     if (input_filenames_.empty()) {
1196       return true;
1197     }
1198 
1199     std::string expected_data_type;
1200     for (const auto& filename : input_filenames_) {
1201       std::unique_ptr<RecordFileReader> file_reader = RecordFileReader::CreateInstance(filename);
1202       if (!file_reader) {
1203         return false;
1204       }
1205       std::string data_type = PerfDataReader::GetDataType(*file_reader);
1206       if (expected_data_type.empty()) {
1207         expected_data_type = data_type;
1208       } else if (expected_data_type != data_type) {
1209         LOG(ERROR) << "files have different data type: " << input_filenames_[0] << ", " << filename;
1210         return false;
1211       }
1212       std::unique_ptr<PerfDataReader> reader;
1213       if (data_type == "etm") {
1214         reader.reset(new ETMPerfDataReader(std::move(file_reader), exclude_perf_,
1215                                            exclude_process_names_, binary_name_regex_.get(),
1216                                            etm_dump_option_));
1217       } else if (data_type == "lbr") {
1218         reader.reset(
1219             new LBRPerfDataReader(std::move(file_reader), exclude_perf_, binary_name_regex_.get()));
1220       } else {
1221         LOG(ERROR) << "unsupported data type " << data_type << " in " << filename;
1222         return false;
1223       }
1224       reader_callback(*reader);
1225       if (!reader->Read()) {
1226         return false;
1227       }
1228     }
1229     return true;
1230   }
1231 
ConvertPerfDataToAutoFDO()1232   bool ConvertPerfDataToAutoFDO() {
1233     AutoFDOWriter autofdo_writer;
1234     auto afdo_callback = [&](const BinaryKey& key, AutoFDOBinaryInfo& binary) {
1235       autofdo_writer.AddAutoFDOBinary(key, binary);
1236     };
1237     auto reader_callback = [&](PerfDataReader& reader) { reader.AddCallback(afdo_callback); };
1238     if (!ReadPerfDataFiles(reader_callback)) {
1239       return false;
1240     }
1241     if (output_format_ == OutputFormat::AutoFDO) {
1242       return autofdo_writer.WriteAutoFDO(output_filename_);
1243     }
1244     CHECK(output_format_ == OutputFormat::BOLT);
1245     return autofdo_writer.WriteBolt(output_filename_);
1246   }
1247 
ConvertPerfDataToBranchList()1248   bool ConvertPerfDataToBranchList() {
1249     BranchListMerger merger;
1250     auto etm_callback = [&](const BinaryKey& key, ETMBinary& binary) {
1251       merger.AddETMBinary(key, binary);
1252     };
1253     auto lbr_callback = [&](LBRData& lbr_data) { merger.AddLBRData(lbr_data); };
1254 
1255     auto reader_callback = [&](PerfDataReader& reader) {
1256       reader.AddCallback(etm_callback);
1257       reader.AddCallback(lbr_callback);
1258     };
1259     if (!ReadPerfDataFiles(reader_callback)) {
1260       return false;
1261     }
1262     return WriteBranchListFile(output_filename_, merger.GetETMData(), merger.GetLBRData(),
1263                                compress_);
1264   }
1265 
ConvertBranchListToAutoFDO()1266   bool ConvertBranchListToAutoFDO() {
1267     // Step1 : Merge branch lists from all input files.
1268     BranchListMergedReader reader(allow_mismatched_build_id_, binary_name_regex_.get(), jobs_);
1269     std::unique_ptr<BranchListMerger> merger = reader.Read(input_filenames_);
1270     if (!merger) {
1271       return false;
1272     }
1273 
1274     // Step2: Convert ETMBinary and LBRData to AutoFDOBinaryInfo.
1275     AutoFDOWriter autofdo_writer;
1276     ETMBranchListToAutoFDOConverter converter;
1277     for (auto& p : merger->GetETMData()) {
1278       const BinaryKey& key = p.first;
1279       ETMBinary& binary = p.second;
1280       std::unique_ptr<AutoFDOBinaryInfo> autofdo_binary = converter.Convert(key, binary);
1281       if (autofdo_binary) {
1282         // Create new BinaryKey with kernel_start_addr = 0. Because AutoFDO output doesn't care
1283         // kernel_start_addr.
1284         autofdo_writer.AddAutoFDOBinary(BinaryKey(key.path, key.build_id), *autofdo_binary);
1285       }
1286     }
1287     if (!merger->GetLBRData().samples.empty()) {
1288       LBRData& lbr_data = merger->GetLBRData();
1289       std::optional<std::vector<AutoFDOBinaryInfo>> binaries = ConvertLBRDataToAutoFDO(lbr_data);
1290       if (!binaries) {
1291         return false;
1292       }
1293       for (size_t i = 0; i < binaries.value().size(); ++i) {
1294         BinaryKey& key = lbr_data.binaries[i];
1295         AutoFDOBinaryInfo& binary = binaries.value()[i];
1296         std::unique_ptr<Dso> dso = Dso::CreateDsoWithBuildId(DSO_ELF_FILE, key.path, key.build_id);
1297         if (!dso) {
1298           continue;
1299         }
1300         binary.executable_segments = GetExecutableSegments(dso.get());
1301         autofdo_writer.AddAutoFDOBinary(key, binary);
1302       }
1303     }
1304 
1305     // Step3: Write AutoFDOBinaryInfo.
1306     if (output_format_ == OutputFormat::AutoFDO) {
1307       return autofdo_writer.WriteAutoFDO(output_filename_);
1308     }
1309     CHECK(output_format_ == OutputFormat::BOLT);
1310     return autofdo_writer.WriteBolt(output_filename_);
1311   }
1312 
ConvertBranchListToBranchList()1313   bool ConvertBranchListToBranchList() {
1314     // Step1 : Merge branch lists from all input files.
1315     BranchListMergedReader reader(allow_mismatched_build_id_, binary_name_regex_.get(), jobs_);
1316     std::unique_ptr<BranchListMerger> merger = reader.Read(input_filenames_);
1317     if (!merger) {
1318       return false;
1319     }
1320     // Step2: Write ETMBinary.
1321     return WriteBranchListFile(output_filename_, merger->GetETMData(), merger->GetLBRData(),
1322                                compress_);
1323   }
1324 
1325   std::unique_ptr<RegEx> binary_name_regex_;
1326   bool exclude_perf_ = false;
1327   std::vector<std::unique_ptr<RegEx>> exclude_process_names_;
1328   std::vector<std::string> input_filenames_;
1329   std::string output_filename_ = "perf_inject.data";
1330   OutputFormat output_format_ = OutputFormat::AutoFDO;
1331   ETMDumpOption etm_dump_option_;
1332   bool compress_ = false;
1333   bool allow_mismatched_build_id_ = false;
1334   size_t jobs_ = 1;
1335   std::string dump_branch_list_file_;
1336 
1337   std::unique_ptr<Dso> placeholder_dso_;
1338 };
1339 
1340 }  // namespace
1341 
RegisterInjectCommand()1342 void RegisterInjectCommand() {
1343   return RegisterCommand("inject", [] { return std::unique_ptr<Command>(new InjectCommand); });
1344 }
1345 
1346 }  // namespace simpleperf
1347