• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <stdio.h>
18 #include <unistd.h>
19 
20 #include <memory>
21 #include <optional>
22 #include <regex>
23 #include <string>
24 
25 #include <android-base/parseint.h>
26 
27 #include "ETMDecoder.h"
28 #include "cmd_inject_impl.h"
29 #include "command.h"
30 #include "record_file.h"
31 #include "system/extras/simpleperf/etm_branch_list.pb.h"
32 #include "thread_tree.h"
33 #include "utils.h"
34 
35 namespace simpleperf {
36 
BranchToProtoString(const std::vector<bool> & branch)37 std::string BranchToProtoString(const std::vector<bool>& branch) {
38   size_t bytes = (branch.size() + 7) / 8;
39   std::string res(bytes, '\0');
40   for (size_t i = 0; i < branch.size(); i++) {
41     if (branch[i]) {
42       res[i >> 3] |= 1 << (i & 7);
43     }
44   }
45   return res;
46 }
47 
ProtoStringToBranch(const std::string & s,size_t bit_size)48 std::vector<bool> ProtoStringToBranch(const std::string& s, size_t bit_size) {
49   std::vector<bool> branch(bit_size, false);
50   for (size_t i = 0; i < bit_size; i++) {
51     if (s[i >> 3] & (1 << (i & 7))) {
52       branch[i] = true;
53     }
54   }
55   return branch;
56 }
57 
58 namespace {
59 
60 using AddrPair = std::pair<uint64_t, uint64_t>;
61 
62 struct AddrPairHash {
operator ()simpleperf::__anon3ce946470111::AddrPairHash63   size_t operator()(const AddrPair& ap) const noexcept {
64     size_t seed = 0;
65     HashCombine(seed, ap.first);
66     HashCombine(seed, ap.second);
67     return seed;
68   }
69 };
70 
71 enum class OutputFormat {
72   AutoFDO,
73   BranchList,
74 };
75 
76 struct AutoFDOBinaryInfo {
77   std::unordered_map<AddrPair, uint64_t, AddrPairHash> range_count_map;
78   std::unordered_map<AddrPair, uint64_t, AddrPairHash> branch_count_map;
79 };
80 
81 using BranchListBinaryInfo =
82     std::unordered_map<uint64_t, std::unordered_map<std::vector<bool>, uint64_t>>;
83 
84 class ThreadTreeWithFilter : public ThreadTree {
85  public:
ExcludePid(pid_t pid)86   void ExcludePid(pid_t pid) { exclude_pid_ = pid; }
87 
FindThread(int tid) const88   ThreadEntry* FindThread(int tid) const override {
89     ThreadEntry* thread = ThreadTree::FindThread(tid);
90     if (thread != nullptr && exclude_pid_ && thread->pid == exclude_pid_) {
91       return nullptr;
92     }
93     return thread;
94   }
95 
96  private:
97   std::optional<pid_t> exclude_pid_;
98 };
99 
100 constexpr const char* ETM_BRANCH_LIST_PROTO_MAGIC = "simpleperf:EtmBranchList";
101 
102 class InjectCommand : public Command {
103  public:
InjectCommand()104   InjectCommand()
105       : Command("inject", "parse etm instruction tracing data",
106                 // clang-format off
107 "Usage: simpleperf inject [options]\n"
108 "--binary binary_name         Generate data only for binaries matching binary_name regex.\n"
109 "-i <file>                    Input file. Default is perf.data. Support below formats:\n"
110 "                               1. perf.data generated by recording cs-etm event type.\n"
111 "                               2. branch_list file generated by `inject --output branch-list`.\n"
112 "-o <file>                    output file. Default is perf_inject.data.\n"
113 "--output <format>            Select output file format:\n"
114 "                               autofdo      -- text format accepted by TextSampleReader\n"
115 "                                               of AutoFDO\n"
116 "                               branch-list  -- protobuf file in etm_branch_list.proto\n"
117 "                             Default is autofdo.\n"
118 "--dump-etm type1,type2,...   Dump etm data. A type is one of raw, packet and element.\n"
119 "--exclude-perf               Exclude trace data for the recording process.\n"
120 "--symdir <dir>               Look for binaries in a directory recursively.\n"
121 "\n"
122 "Examples:\n"
123 "1. Generate autofdo text output.\n"
124 "$ simpleperf inject -i perf.data -o autofdo.txt --output autofdo\n"
125 "\n"
126 "2. Generate branch list proto, then convert to autofdo text.\n"
127 "$ simpleperf inject -i perf.data -o branch_list.data --output branch-list\n"
128 "$ simpleperf inject -i branch_list.data -o autofdo.txt --output autofdo\n"
129                 // clang-format on
130                 ),
131         output_fp_(nullptr, fclose) {}
132 
Run(const std::vector<std::string> & args)133   bool Run(const std::vector<std::string>& args) override {
134     GOOGLE_PROTOBUF_VERIFY_VERSION;
135     // 1. Parse options.
136     if (!ParseOptions(args)) {
137       return false;
138     }
139 
140     // 2. Open output file.
141     const char* open_mode = (output_format_ == OutputFormat::AutoFDO) ? "w" : "wb";
142     output_fp_.reset(fopen(output_filename_.c_str(), open_mode));
143     if (!output_fp_) {
144       PLOG(ERROR) << "failed to write to " << output_filename_;
145       return false;
146     }
147 
148     // 3. Process input file.
149     if (!ProcessInputFile()) {
150       return false;
151     }
152 
153     // 4. Write output file.
154     if (!WriteOutput()) {
155       return false;
156     }
157     output_fp_.reset(nullptr);
158     return true;
159   }
160 
161  private:
ParseOptions(const std::vector<std::string> & args)162   bool ParseOptions(const std::vector<std::string>& args) {
163     const OptionFormatMap option_formats = {
164         {"--binary", {OptionValueType::STRING, OptionType::SINGLE}},
165         {"--dump-etm", {OptionValueType::STRING, OptionType::SINGLE}},
166         {"--exclude-perf", {OptionValueType::NONE, OptionType::SINGLE}},
167         {"-i", {OptionValueType::STRING, OptionType::SINGLE}},
168         {"-o", {OptionValueType::STRING, OptionType::SINGLE}},
169         {"--output", {OptionValueType::STRING, OptionType::SINGLE}},
170         {"--symdir", {OptionValueType::STRING, OptionType::MULTIPLE}},
171     };
172     OptionValueMap options;
173     std::vector<std::pair<OptionName, OptionValue>> ordered_options;
174     if (!PreprocessOptions(args, option_formats, &options, &ordered_options, nullptr)) {
175       return false;
176     }
177 
178     if (auto value = options.PullValue("--binary"); value) {
179       binary_name_regex_ = *value->str_value;
180     }
181     if (auto value = options.PullValue("--dump-etm"); value) {
182       if (!ParseEtmDumpOption(*value->str_value, &etm_dump_option_)) {
183         return false;
184       }
185     }
186     exclude_perf_ = options.PullBoolValue("--exclude-perf");
187     options.PullStringValue("-i", &input_filename_);
188     options.PullStringValue("-o", &output_filename_);
189     if (auto value = options.PullValue("--output"); value) {
190       const std::string& output = *value->str_value;
191       if (output == "autofdo") {
192         output_format_ = OutputFormat::AutoFDO;
193       } else if (output == "branch-list") {
194         output_format_ = OutputFormat::BranchList;
195       } else {
196         LOG(ERROR) << "unknown format in --output option: " << output;
197         return false;
198       }
199     }
200     if (auto value = options.PullValue("--symdir"); value) {
201       if (!Dso::AddSymbolDir(*value->str_value)) {
202         return false;
203       }
204     }
205     CHECK(options.values.empty());
206     return true;
207   }
208 
ProcessInputFile()209   bool ProcessInputFile() {
210     if (IsPerfDataFile(input_filename_)) {
211       record_file_reader_ = RecordFileReader::CreateInstance(input_filename_);
212       if (!record_file_reader_) {
213         return false;
214       }
215       if (exclude_perf_) {
216         const auto& info_map = record_file_reader_->GetMetaInfoFeature();
217         if (auto it = info_map.find("recording_process"); it == info_map.end()) {
218           LOG(ERROR) << input_filename_ << " doesn't support --exclude-perf";
219           return false;
220         } else {
221           int pid;
222           if (!android::base::ParseInt(it->second, &pid, 0)) {
223             LOG(ERROR) << "invalid recording_process " << it->second;
224             return false;
225           }
226           thread_tree_.ExcludePid(pid);
227         }
228       }
229       record_file_reader_->LoadBuildIdAndFileFeatures(thread_tree_);
230       if (!record_file_reader_->ReadDataSection(
231               [this](auto r) { return ProcessRecord(r.get()); })) {
232         return false;
233       }
234       if (etm_decoder_ && !etm_decoder_->FinishData()) {
235         return false;
236       }
237       return true;
238     }
239     return ProcessBranchListFile();
240   }
241 
ProcessRecord(Record * r)242   bool ProcessRecord(Record* r) {
243     thread_tree_.Update(*r);
244     if (r->type() == PERF_RECORD_AUXTRACE_INFO) {
245       etm_decoder_ = ETMDecoder::Create(*static_cast<AuxTraceInfoRecord*>(r), thread_tree_);
246       if (!etm_decoder_) {
247         return false;
248       }
249       etm_decoder_->EnableDump(etm_dump_option_);
250       if (output_format_ == OutputFormat::AutoFDO) {
251         etm_decoder_->RegisterCallback(
252             [this](const ETMInstrRange& range) { ProcessInstrRange(range); });
253       } else if (output_format_ == OutputFormat::BranchList) {
254         etm_decoder_->RegisterCallback(
255             [this](const ETMBranchList& branch) { ProcessBranchList(branch); });
256       }
257     } else if (r->type() == PERF_RECORD_AUX) {
258       AuxRecord* aux = static_cast<AuxRecord*>(r);
259       uint64_t aux_size = aux->data->aux_size;
260       if (aux_size > 0) {
261         if (aux_data_buffer_.size() < aux_size) {
262           aux_data_buffer_.resize(aux_size);
263         }
264         if (!record_file_reader_->ReadAuxData(aux->Cpu(), aux->data->aux_offset,
265                                               aux_data_buffer_.data(), aux_size)) {
266           LOG(ERROR) << "failed to read aux data";
267           return false;
268         }
269         return etm_decoder_->ProcessData(aux_data_buffer_.data(), aux_size);
270       }
271     } else if (r->type() == PERF_RECORD_MMAP && r->InKernel()) {
272       auto& mmap_r = *static_cast<MmapRecord*>(r);
273       if (android::base::StartsWith(mmap_r.filename, DEFAULT_KERNEL_MMAP_NAME)) {
274         kernel_map_start_addr_ = mmap_r.data->addr;
275       }
276     }
277     return true;
278   }
279 
280   std::unordered_map<Dso*, bool> dso_filter_cache;
FilterDso(Dso * dso)281   bool FilterDso(Dso* dso) {
282     auto lookup = dso_filter_cache.find(dso);
283     if (lookup != dso_filter_cache.end()) {
284       return lookup->second;
285     }
286     bool match = std::regex_search(dso->Path(), binary_name_regex_);
287     dso_filter_cache.insert({dso, match});
288     return match;
289   }
290 
ProcessInstrRange(const ETMInstrRange & instr_range)291   void ProcessInstrRange(const ETMInstrRange& instr_range) {
292     if (!FilterDso(instr_range.dso)) {
293       return;
294     }
295 
296     auto& binary = autofdo_binary_map_[instr_range.dso];
297     binary.range_count_map[AddrPair(instr_range.start_addr, instr_range.end_addr)] +=
298         instr_range.branch_taken_count + instr_range.branch_not_taken_count;
299     if (instr_range.branch_taken_count > 0) {
300       binary.branch_count_map[AddrPair(instr_range.end_addr, instr_range.branch_to_addr)] +=
301           instr_range.branch_taken_count;
302     }
303   }
304 
ProcessBranchList(const ETMBranchList & branch_list)305   void ProcessBranchList(const ETMBranchList& branch_list) {
306     if (!FilterDso(branch_list.dso)) {
307       return;
308     }
309 
310     ++branch_list_binary_map_[branch_list.dso][branch_list.addr][branch_list.branch];
311   }
312 
ProcessBranchListFile()313   bool ProcessBranchListFile() {
314     if (output_format_ != OutputFormat::AutoFDO) {
315       LOG(ERROR) << "Only support autofdo output when given a branch list file.";
316       return false;
317     }
318     // 1. Load EtmBranchList msg from proto file.
319     auto fd = FileHelper::OpenReadOnly(input_filename_);
320     if (!fd.ok()) {
321       PLOG(ERROR) << "failed to open " << input_filename_;
322       return false;
323     }
324     proto::ETMBranchList branch_list_proto;
325     if (!branch_list_proto.ParseFromFileDescriptor(fd)) {
326       PLOG(ERROR) << "failed to read msg from " << input_filename_;
327       return false;
328     }
329     if (branch_list_proto.magic() != ETM_BRANCH_LIST_PROTO_MAGIC) {
330       PLOG(ERROR) << "file not in format etm_branch_list.proto: " << input_filename_;
331       return false;
332     }
333 
334     // 2. Build branch map for each binary, convert them to instr ranges.
335     auto callback = [this](const ETMInstrRange& range) { ProcessInstrRange(range); };
336     auto check_build_id = [](Dso* dso, const BuildId& expected_build_id) {
337       if (expected_build_id.IsEmpty()) {
338         return true;
339       }
340       BuildId build_id;
341       return GetBuildIdFromDsoPath(dso->GetDebugFilePath(), &build_id) &&
342              build_id == expected_build_id;
343     };
344 
345     for (size_t i = 0; i < branch_list_proto.binaries_size(); i++) {
346       const auto& binary_proto = branch_list_proto.binaries(i);
347       BuildId build_id(binary_proto.build_id());
348       std::optional<DsoType> dso_type = ToDsoType(binary_proto.type());
349       if (!dso_type.has_value()) {
350         return false;
351       }
352       std::unique_ptr<Dso> dso =
353           Dso::CreateDsoWithBuildId(dso_type.value(), binary_proto.path(), build_id);
354       if (!dso || !FilterDso(dso.get()) || !check_build_id(dso.get(), build_id)) {
355         continue;
356       }
357       // Dso is used in ETMInstrRange in post process, so need to extend its lifetime.
358       Dso* dso_p = dso.get();
359       branch_list_dso_v_.emplace_back(dso.release());
360       auto branch_map = BuildBranchMap(binary_proto);
361 
362       if (dso_p->type() == DSO_KERNEL) {
363         if (!ModifyBranchMapForKernel(binary_proto, dso_p, branch_map)) {
364           return false;
365         }
366       }
367 
368       if (auto result = ConvertBranchMapToInstrRanges(dso_p, branch_map, callback); !result.ok()) {
369         LOG(WARNING) << "failed to build instr ranges for binary " << dso_p->Path() << ": "
370                      << result.error();
371       }
372     }
373     return true;
374   }
375 
BuildBranchMap(const proto::ETMBranchList_Binary & binary_proto)376   BranchMap BuildBranchMap(const proto::ETMBranchList_Binary& binary_proto) {
377     BranchMap branch_map;
378     for (size_t i = 0; i < binary_proto.addrs_size(); i++) {
379       const auto& addr_proto = binary_proto.addrs(i);
380       auto& b_map = branch_map[addr_proto.addr()];
381       for (size_t j = 0; j < addr_proto.branches_size(); j++) {
382         const auto& branch_proto = addr_proto.branches(j);
383         std::vector<bool> branch =
384             ProtoStringToBranch(branch_proto.branch(), branch_proto.branch_size());
385         b_map[branch] = branch_proto.count();
386       }
387     }
388     return branch_map;
389   }
390 
ModifyBranchMapForKernel(const proto::ETMBranchList_Binary & binary_proto,Dso * dso,BranchMap & branch_map)391   bool ModifyBranchMapForKernel(const proto::ETMBranchList_Binary& binary_proto, Dso* dso,
392                                 BranchMap& branch_map) {
393     if (!binary_proto.has_kernel_info()) {
394       LOG(ERROR) << "no kernel info";
395       return false;
396     }
397     uint64_t kernel_map_start_addr = binary_proto.kernel_info().kernel_start_addr();
398     if (kernel_map_start_addr == 0) {
399       return true;
400     }
401     // Addresses are still kernel ip addrs in memory. Need to convert them to vaddrs in vmlinux.
402     BranchMap new_branch_map;
403     for (auto& p : branch_map) {
404       uint64_t vaddr_in_file = dso->IpToVaddrInFile(p.first, kernel_map_start_addr, 0);
405       new_branch_map[vaddr_in_file] = std::move(p.second);
406     }
407     branch_map = std::move(new_branch_map);
408     return true;
409   }
410 
WriteOutput()411   bool WriteOutput() {
412     if (output_format_ == OutputFormat::AutoFDO) {
413       GenerateInstrRange();
414       return true;
415     }
416     CHECK(output_format_ == OutputFormat::BranchList);
417     return GenerateBranchList();
418   }
419 
GenerateInstrRange()420   void GenerateInstrRange() {
421     // autofdo_binary_map is used to store instruction ranges, which can have a large amount. And it
422     // has a larger access time (instruction ranges * executed time). So it's better to use
423     // unorder_maps to speed up access time. But we also want a stable output here, to compare
424     // output changes result from code changes. So generate a sorted output here.
425     std::vector<Dso*> dso_v;
426     for (auto& p : autofdo_binary_map_) {
427       dso_v.emplace_back(p.first);
428     }
429     std::sort(dso_v.begin(), dso_v.end(), [](Dso* d1, Dso* d2) { return d1->Path() < d2->Path(); });
430     if (dso_v.size() > 1) {
431       fprintf(output_fp_.get(),
432               "// Please split this file. AutoFDO only accepts profile for one binary.\n");
433     }
434     for (auto dso : dso_v) {
435       const AutoFDOBinaryInfo& binary = autofdo_binary_map_[dso];
436       // AutoFDO text format needs file_offsets instead of virtual addrs in a binary. And it uses
437       // below formula: vaddr = file_offset + GetFirstLoadSegmentVaddr().
438       uint64_t first_load_segment_addr = GetFirstLoadSegmentVaddr(dso);
439 
440       auto to_offset = [&](uint64_t vaddr) -> uint64_t {
441         if (vaddr == 0) {
442           return 0;
443         }
444         CHECK_GE(vaddr, first_load_segment_addr);
445         return vaddr - first_load_segment_addr;
446       };
447 
448       // Write range_count_map.
449       std::map<AddrPair, uint64_t> range_count_map(binary.range_count_map.begin(),
450                                                    binary.range_count_map.end());
451       fprintf(output_fp_.get(), "%zu\n", range_count_map.size());
452       for (const auto& pair2 : range_count_map) {
453         const AddrPair& addr_range = pair2.first;
454         uint64_t count = pair2.second;
455 
456         fprintf(output_fp_.get(), "%" PRIx64 "-%" PRIx64 ":%" PRIu64 "\n",
457                 to_offset(addr_range.first), to_offset(addr_range.second), count);
458       }
459 
460       // Write addr_count_map.
461       fprintf(output_fp_.get(), "0\n");
462 
463       // Write branch_count_map.
464       std::map<AddrPair, uint64_t> branch_count_map(binary.branch_count_map.begin(),
465                                                     binary.branch_count_map.end());
466       fprintf(output_fp_.get(), "%zu\n", branch_count_map.size());
467       for (const auto& pair2 : branch_count_map) {
468         const AddrPair& branch = pair2.first;
469         uint64_t count = pair2.second;
470 
471         fprintf(output_fp_.get(), "%" PRIx64 "->%" PRIx64 ":%" PRIu64 "\n", to_offset(branch.first),
472                 to_offset(branch.second), count);
473       }
474 
475       // Write the binary path in comment.
476       fprintf(output_fp_.get(), "// %s\n\n", dso->Path().c_str());
477     }
478   }
479 
GetFirstLoadSegmentVaddr(Dso * dso)480   uint64_t GetFirstLoadSegmentVaddr(Dso* dso) {
481     ElfStatus status;
482     if (auto elf = ElfFile::Open(dso->GetDebugFilePath(), &status); elf) {
483       for (const auto& segment : elf->GetProgramHeader()) {
484         if (segment.is_load) {
485           return segment.vaddr;
486         }
487       }
488     }
489     return 0;
490   }
491 
GenerateBranchList()492   bool GenerateBranchList() {
493     // Don't produce empty output file.
494     if (branch_list_binary_map_.empty()) {
495       LOG(INFO) << "Skip empty output file.";
496       output_fp_.reset(nullptr);
497       unlink(output_filename_.c_str());
498       return true;
499     }
500 
501     proto::ETMBranchList branch_list_proto;
502     branch_list_proto.set_magic(ETM_BRANCH_LIST_PROTO_MAGIC);
503     std::vector<char> branch_buf;
504     for (const auto& dso_p : branch_list_binary_map_) {
505       Dso* dso = dso_p.first;
506       auto& addr_map = dso_p.second;
507       auto binary_proto = branch_list_proto.add_binaries();
508 
509       binary_proto->set_path(dso->Path());
510       BuildId build_id = Dso::FindExpectedBuildIdForPath(dso->Path());
511       if (!build_id.IsEmpty()) {
512         binary_proto->set_build_id(build_id.ToString().substr(2));
513       }
514       auto opt_binary_type = ToProtoBinaryType(dso->type());
515       if (!opt_binary_type.has_value()) {
516         return false;
517       }
518       binary_proto->set_type(opt_binary_type.value());
519 
520       for (const auto& addr_p : addr_map) {
521         auto addr_proto = binary_proto->add_addrs();
522         addr_proto->set_addr(addr_p.first);
523 
524         for (const auto& branch_p : addr_p.second) {
525           const std::vector<bool>& branch = branch_p.first;
526           auto branch_proto = addr_proto->add_branches();
527 
528           branch_proto->set_branch(BranchToProtoString(branch));
529           branch_proto->set_branch_size(branch.size());
530           branch_proto->set_count(branch_p.second);
531         }
532       }
533 
534       if (dso->type() == DSO_KERNEL) {
535         if (kernel_map_start_addr_ == 0) {
536           LOG(WARNING) << "Can't convert kernel ip addresses without kernel start addr. So remove "
537                           "branches for the kernel.";
538           branch_list_proto.mutable_binaries()->RemoveLast();
539           continue;
540         }
541         if (dso->GetDebugFilePath() == dso->Path()) {
542           // vmlinux isn't available. We still use kernel ip addr. Put kernel start addr in proto
543           // for address conversion later.
544           binary_proto->mutable_kernel_info()->set_kernel_start_addr(kernel_map_start_addr_);
545         } else {
546           // vmlinux is available. We have converted kernel ip addr to vaddr in vmlinux. So no need
547           // to put kernel start addr in proto.
548           binary_proto->mutable_kernel_info()->set_kernel_start_addr(0);
549         }
550       }
551     }
552     if (!branch_list_proto.SerializeToFileDescriptor(fileno(output_fp_.get()))) {
553       PLOG(ERROR) << "failed to write to output file";
554       return false;
555     }
556     return true;
557   }
558 
ToProtoBinaryType(DsoType dso_type)559   std::optional<proto::ETMBranchList_Binary::BinaryType> ToProtoBinaryType(DsoType dso_type) {
560     switch (dso_type) {
561       case DSO_ELF_FILE:
562         return proto::ETMBranchList_Binary::ELF_FILE;
563       case DSO_KERNEL:
564         return proto::ETMBranchList_Binary::KERNEL;
565       case DSO_KERNEL_MODULE:
566         return proto::ETMBranchList_Binary::KERNEL_MODULE;
567       default:
568         LOG(ERROR) << "unexpected dso type " << dso_type;
569         return std::nullopt;
570     }
571   }
572 
ToDsoType(proto::ETMBranchList_Binary::BinaryType binary_type)573   std::optional<DsoType> ToDsoType(proto::ETMBranchList_Binary::BinaryType binary_type) {
574     switch (binary_type) {
575       case proto::ETMBranchList_Binary::ELF_FILE:
576         return DSO_ELF_FILE;
577       case proto::ETMBranchList_Binary::KERNEL:
578         return DSO_KERNEL;
579       case proto::ETMBranchList_Binary::KERNEL_MODULE:
580         return DSO_KERNEL_MODULE;
581       default:
582         LOG(ERROR) << "unexpected binary type " << binary_type;
583         return std::nullopt;
584     }
585   }
586 
587   std::regex binary_name_regex_{""};  // Default to match everything.
588   bool exclude_perf_ = false;
589   std::string input_filename_ = "perf.data";
590   std::string output_filename_ = "perf_inject.data";
591   OutputFormat output_format_ = OutputFormat::AutoFDO;
592   ThreadTreeWithFilter thread_tree_;
593   std::unique_ptr<RecordFileReader> record_file_reader_;
594   ETMDumpOption etm_dump_option_;
595   std::unique_ptr<ETMDecoder> etm_decoder_;
596   std::vector<uint8_t> aux_data_buffer_;
597   std::unique_ptr<FILE, decltype(&fclose)> output_fp_;
598 
599   // Store results for AutoFDO.
600   std::unordered_map<Dso*, AutoFDOBinaryInfo> autofdo_binary_map_;
601   // Store results for BranchList.
602   std::unordered_map<Dso*, BranchListBinaryInfo> branch_list_binary_map_;
603   std::vector<std::unique_ptr<Dso>> branch_list_dso_v_;
604   uint64_t kernel_map_start_addr_ = 0;
605 };
606 
607 }  // namespace
608 
RegisterInjectCommand()609 void RegisterInjectCommand() {
610   return RegisterCommand("inject", [] { return std::unique_ptr<Command>(new InjectCommand); });
611 }
612 
613 }  // namespace simpleperf
614