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