diff options
Diffstat (limited to 'simpleperf/cmd_inject.cpp')
-rw-r--r-- | simpleperf/cmd_inject.cpp | 574 |
1 files changed, 377 insertions, 197 deletions
diff --git a/simpleperf/cmd_inject.cpp b/simpleperf/cmd_inject.cpp index d3f66cd7..be8f4173 100644 --- a/simpleperf/cmd_inject.cpp +++ b/simpleperf/cmd_inject.cpp @@ -25,12 +25,12 @@ #include <android-base/parseint.h> #include <android-base/strings.h> -#include "ETMBranchListFile.h" +#include "BranchListFile.h" #include "ETMDecoder.h" #include "RegEx.h" #include "command.h" #include "record_file.h" -#include "system/extras/simpleperf/etm_branch_list.pb.h" +#include "system/extras/simpleperf/branch_list.pb.h" #include "thread_tree.h" #include "utils.h" @@ -56,9 +56,20 @@ enum class OutputFormat { struct AutoFDOBinaryInfo { uint64_t first_load_segment_addr = 0; + std::unordered_map<uint64_t, uint64_t> address_count_map; std::unordered_map<AddrPair, uint64_t, AddrPairHash> range_count_map; std::unordered_map<AddrPair, uint64_t, AddrPairHash> branch_count_map; + void AddAddress(uint64_t addr) { OverflowSafeAdd(address_count_map[addr], 1); } + + void AddRange(uint64_t begin, uint64_t end) { + OverflowSafeAdd(range_count_map[std::make_pair(begin, end)], 1); + } + + void AddBranch(uint64_t from, uint64_t to) { + OverflowSafeAdd(branch_count_map[std::make_pair(from, to)], 1); + } + void AddInstrRange(const ETMInstrRange& instr_range) { uint64_t total_count = instr_range.branch_taken_count; OverflowSafeAdd(total_count, instr_range.branch_not_taken_count); @@ -71,6 +82,12 @@ struct AutoFDOBinaryInfo { } void Merge(const AutoFDOBinaryInfo& other) { + for (const auto& p : other.address_count_map) { + auto res = address_count_map.emplace(p.first, p.second); + if (!res.second) { + OverflowSafeAdd(res.first->second, p.second); + } + } for (const auto& p : other.range_count_map) { auto res = range_count_map.emplace(p.first, p.second); if (!res.second) { @@ -87,53 +104,10 @@ struct AutoFDOBinaryInfo { }; using AutoFDOBinaryCallback = std::function<void(const BinaryKey&, AutoFDOBinaryInfo&)>; -using BranchListBinaryCallback = std::function<void(const BinaryKey&, BranchListBinaryInfo&)>; - -class ETMThreadTreeWithFilter : public ETMThreadTree { - public: - void ExcludePid(pid_t pid) { exclude_pid_ = pid; } - ThreadTree& GetThreadTree() { return thread_tree_; } - void DisableThreadExitRecords() override { thread_tree_.DisableThreadExitRecords(); } - - const ThreadEntry* FindThread(int tid) override { - const ThreadEntry* thread = thread_tree_.FindThread(tid); - if (thread != nullptr && exclude_pid_ && thread->pid == exclude_pid_) { - return nullptr; - } - return thread; - } - - const MapSet& GetKernelMaps() override { return thread_tree_.GetKernelMaps(); } - - private: - ThreadTree thread_tree_; - std::optional<pid_t> exclude_pid_; -}; - -class BinaryFilter { - public: - BinaryFilter(const RegEx* binary_name_regex) : binary_name_regex_(binary_name_regex) {} - - bool Filter(Dso* dso) { - auto lookup = dso_filter_cache_.find(dso); - if (lookup != dso_filter_cache_.end()) { - return lookup->second; - } - bool match = Filter(dso->Path()); - dso_filter_cache_.insert({dso, match}); - return match; - } - - bool Filter(const std::string& path) { - return binary_name_regex_ == nullptr || binary_name_regex_->Search(path); - } - - private: - const RegEx* binary_name_regex_; - std::unordered_map<Dso*, bool> dso_filter_cache_; -}; +using ETMBinaryCallback = std::function<void(const BinaryKey&, ETMBinary&)>; +using LBRDataCallback = std::function<void(LBRData&)>; -static uint64_t GetFirstLoadSegmentVaddr(Dso* dso) { +static uint64_t GetFirstLoadSegmentVaddr(const Dso* dso) { ElfStatus status; if (auto elf = ElfFile::Open(dso->GetDebugFilePath(), &status); elf) { for (const auto& segment : elf->GetProgramHeader()) { @@ -145,90 +119,132 @@ static uint64_t GetFirstLoadSegmentVaddr(Dso* dso) { return 0; } -// Read perf.data, and generate AutoFDOBinaryInfo or BranchListBinaryInfo. -// To avoid resetting data, it only processes one input file per instance. +// Base class for reading perf.data and generating AutoFDO or branch list data. class PerfDataReader { public: - PerfDataReader(const std::string& filename, bool exclude_perf, ETMDumpOption etm_dump_option, + static std::string GetDataType(RecordFileReader& reader) { + const EventAttrIds& attrs = reader.AttrSection(); + if (attrs.size() != 1) { + return "unknown"; + } + const perf_event_attr& attr = attrs[0].attr; + if (IsEtmEventType(attr.type)) { + return "etm"; + } + if (attr.sample_type & PERF_SAMPLE_BRANCH_STACK) { + return "lbr"; + } + return "unknown"; + } + + PerfDataReader(std::unique_ptr<RecordFileReader> reader, bool exclude_perf, const RegEx* binary_name_regex) - : filename_(filename), + : reader_(std::move(reader)), exclude_perf_(exclude_perf), - etm_dump_option_(etm_dump_option), binary_filter_(binary_name_regex) {} + virtual ~PerfDataReader() {} + + std::string GetDataType() const { return GetDataType(*reader_); } void SetCallback(const AutoFDOBinaryCallback& callback) { autofdo_callback_ = callback; } - void SetCallback(const BranchListBinaryCallback& callback) { branch_list_callback_ = callback; } + virtual void SetCallback(const ETMBinaryCallback&) {} + virtual void SetCallback(const LBRDataCallback&) {} - bool Read() { - record_file_reader_ = RecordFileReader::CreateInstance(filename_); - if (!record_file_reader_) { - return false; - } - if (record_file_reader_->HasFeature(PerfFileFormat::FEAT_ETM_BRANCH_LIST)) { - return ProcessETMBranchListFeature(); - } + virtual bool Read() { if (exclude_perf_) { - const auto& info_map = record_file_reader_->GetMetaInfoFeature(); + const auto& info_map = reader_->GetMetaInfoFeature(); if (auto it = info_map.find("recording_process"); it == info_map.end()) { - LOG(ERROR) << filename_ << " doesn't support --exclude-perf"; + LOG(ERROR) << reader_->FileName() << " doesn't support --exclude-perf"; return false; } else { int pid; if (!android::base::ParseInt(it->second, &pid, 0)) { - LOG(ERROR) << "invalid recording_process " << it->second << " in " << filename_; + LOG(ERROR) << "invalid recording_process " << it->second << " in " << reader_->FileName(); return false; } - thread_tree_.ExcludePid(pid); + exclude_pid_ = pid; } } - if (!record_file_reader_->LoadBuildIdAndFileFeatures(thread_tree_.GetThreadTree())) { + + if (!reader_->LoadBuildIdAndFileFeatures(thread_tree_)) { return false; } - if (!record_file_reader_->ReadDataSection([this](auto r) { return ProcessRecord(r.get()); })) { + if (!reader_->ReadDataSection([this](auto r) { return ProcessRecord(*r); })) { return false; } - if (etm_decoder_ && !etm_decoder_->FinishData()) { - return false; + return PostProcess(); + } + + protected: + virtual bool ProcessRecord(Record& r) = 0; + virtual bool PostProcess() = 0; + + void ProcessAutoFDOBinaryInfo() { + for (auto& p : autofdo_binary_map_) { + const Dso* dso = p.first; + AutoFDOBinaryInfo& binary = p.second; + binary.first_load_segment_addr = GetFirstLoadSegmentVaddr(dso); + autofdo_callback_(BinaryKey(dso, 0), binary); } - if (autofdo_callback_) { - ProcessAutoFDOBinaryInfo(); - } else if (branch_list_callback_) { - ProcessBranchListBinaryInfo(); + } + + const std::string data_type_; + std::unique_ptr<RecordFileReader> reader_; + bool exclude_perf_; + BinaryFilter binary_filter_; + + std::optional<int> exclude_pid_; + ThreadTree thread_tree_; + AutoFDOBinaryCallback autofdo_callback_; + // Store results for AutoFDO. + std::unordered_map<const Dso*, AutoFDOBinaryInfo> autofdo_binary_map_; +}; + +class ETMThreadTreeWithFilter : public ETMThreadTree { + public: + ETMThreadTreeWithFilter(ThreadTree& thread_tree, std::optional<int>& exclude_pid) + : thread_tree_(thread_tree), exclude_pid_(exclude_pid) {} + + void DisableThreadExitRecords() override { thread_tree_.DisableThreadExitRecords(); } + + const ThreadEntry* FindThread(int tid) override { + const ThreadEntry* thread = thread_tree_.FindThread(tid); + if (thread != nullptr && exclude_pid_ && thread->pid == exclude_pid_) { + return nullptr; } - return true; + return thread; } + const MapSet& GetKernelMaps() override { return thread_tree_.GetKernelMaps(); } + private: - bool ProcessETMBranchListFeature() { - if (exclude_perf_) { - LOG(WARNING) << "--exclude-perf has no effect on perf.data with etm branch list"; - } - if (autofdo_callback_) { - LOG(ERROR) << "convert to autofdo format isn't support on perf.data with etm branch list"; - return false; - } - CHECK(branch_list_callback_); - std::string s; - if (!record_file_reader_->ReadFeatureSection(PerfFileFormat::FEAT_ETM_BRANCH_LIST, &s)) { - return false; - } - BranchListBinaryMap binary_map; - if (!StringToBranchListBinaryMap(s, binary_map)) { - return false; - } - for (auto& [key, binary] : binary_map) { - if (!binary_filter_.Filter(key.path)) { - continue; - } - branch_list_callback_(key, binary); + ThreadTree& thread_tree_; + std::optional<int>& exclude_pid_; +}; + +// Read perf.data with ETM data and generate AutoFDO or branch list data. +class ETMPerfDataReader : public PerfDataReader { + public: + ETMPerfDataReader(std::unique_ptr<RecordFileReader> reader, bool exclude_perf, + const RegEx* binary_name_regex, ETMDumpOption etm_dump_option) + : PerfDataReader(std::move(reader), exclude_perf, binary_name_regex), + etm_dump_option_(etm_dump_option), + etm_thread_tree_(thread_tree_, exclude_pid_) {} + + void SetCallback(const ETMBinaryCallback& callback) override { etm_binary_callback_ = callback; } + + bool Read() override { + if (reader_->HasFeature(PerfFileFormat::FEAT_ETM_BRANCH_LIST)) { + return ProcessETMBranchListFeature(); } - return true; + return PerfDataReader::Read(); } - bool ProcessRecord(Record* r) { - thread_tree_.GetThreadTree().Update(*r); - if (r->type() == PERF_RECORD_AUXTRACE_INFO) { - etm_decoder_ = ETMDecoder::Create(*static_cast<AuxTraceInfoRecord*>(r), thread_tree_); + private: + bool ProcessRecord(Record& r) override { + thread_tree_.Update(r); + if (r.type() == PERF_RECORD_AUXTRACE_INFO) { + etm_decoder_ = ETMDecoder::Create(static_cast<AuxTraceInfoRecord&>(r), etm_thread_tree_); if (!etm_decoder_) { return false; } @@ -236,32 +252,32 @@ class PerfDataReader { if (autofdo_callback_) { etm_decoder_->RegisterCallback( [this](const ETMInstrRange& range) { ProcessInstrRange(range); }); - } else if (branch_list_callback_) { + } else if (etm_binary_callback_) { etm_decoder_->RegisterCallback( - [this](const ETMBranchList& branch) { ProcessBranchList(branch); }); + [this](const ETMBranchList& branch) { ProcessETMBranchList(branch); }); } - } else if (r->type() == PERF_RECORD_AUX) { - AuxRecord* aux = static_cast<AuxRecord*>(r); - if (aux->data->aux_size > SIZE_MAX) { + } else if (r.type() == PERF_RECORD_AUX) { + AuxRecord& aux = static_cast<AuxRecord&>(r); + if (aux.data->aux_size > SIZE_MAX) { LOG(ERROR) << "invalid aux size"; return false; } - size_t aux_size = aux->data->aux_size; + size_t aux_size = aux.data->aux_size; if (aux_size > 0) { bool error = false; - if (!record_file_reader_->ReadAuxData(aux->Cpu(), aux->data->aux_offset, aux_size, - aux_data_buffer_, error)) { + if (!reader_->ReadAuxData(aux.Cpu(), aux.data->aux_offset, aux_size, aux_data_buffer_, + error)) { return !error; } if (!etm_decoder_) { LOG(ERROR) << "ETMDecoder isn't created"; return false; } - return etm_decoder_->ProcessData(aux_data_buffer_.data(), aux_size, !aux->Unformatted(), - aux->Cpu()); + return etm_decoder_->ProcessData(aux_data_buffer_.data(), aux_size, !aux.Unformatted(), + aux.Cpu()); } - } else if (r->type() == PERF_RECORD_MMAP && r->InKernel()) { - auto& mmap_r = *static_cast<MmapRecord*>(r); + } else if (r.type() == PERF_RECORD_MMAP && r.InKernel()) { + auto& mmap_r = static_cast<MmapRecord&>(r); if (android::base::StartsWith(mmap_r.filename, DEFAULT_KERNEL_MMAP_NAME)) { kernel_map_start_addr_ = mmap_r.data->addr; } @@ -269,6 +285,44 @@ class PerfDataReader { return true; } + bool PostProcess() override { + if (etm_decoder_ && !etm_decoder_->FinishData()) { + return false; + } + if (autofdo_callback_) { + ProcessAutoFDOBinaryInfo(); + } else if (etm_binary_callback_) { + ProcessETMBinary(); + } + return true; + } + + bool ProcessETMBranchListFeature() { + if (exclude_perf_) { + LOG(WARNING) << "--exclude-perf has no effect on perf.data with etm branch list"; + } + if (autofdo_callback_) { + LOG(ERROR) << "convert to autofdo format isn't support on perf.data with etm branch list"; + return false; + } + CHECK(etm_binary_callback_); + std::string s; + if (!reader_->ReadFeatureSection(PerfFileFormat::FEAT_ETM_BRANCH_LIST, &s)) { + return false; + } + ETMBinaryMap binary_map; + if (!StringToETMBinaryMap(s, binary_map)) { + return false; + } + for (auto& [key, binary] : binary_map) { + if (!binary_filter_.Filter(key.path)) { + continue; + } + etm_binary_callback_(key, binary); + } + return true; + } + void ProcessInstrRange(const ETMInstrRange& instr_range) { if (!binary_filter_.Filter(instr_range.dso)) { return; @@ -277,28 +331,19 @@ class PerfDataReader { autofdo_binary_map_[instr_range.dso].AddInstrRange(instr_range); } - void ProcessBranchList(const ETMBranchList& branch_list) { + void ProcessETMBranchList(const ETMBranchList& branch_list) { if (!binary_filter_.Filter(branch_list.dso)) { return; } - auto& branch_map = branch_list_binary_map_[branch_list.dso].branch_map; + auto& branch_map = etm_binary_map_[branch_list.dso].branch_map; ++branch_map[branch_list.addr][branch_list.branch]; } - void ProcessAutoFDOBinaryInfo() { - for (auto& p : autofdo_binary_map_) { - Dso* dso = p.first; - AutoFDOBinaryInfo& binary = p.second; - binary.first_load_segment_addr = GetFirstLoadSegmentVaddr(dso); - autofdo_callback_(BinaryKey(dso, 0), binary); - } - } - - void ProcessBranchListBinaryInfo() { - for (auto& p : branch_list_binary_map_) { + void ProcessETMBinary() { + for (auto& p : etm_binary_map_) { Dso* dso = p.first; - BranchListBinaryInfo& binary = p.second; + ETMBinary& binary = p.second; binary.dso_type = dso->type(); BinaryKey key(dso, 0); if (binary.dso_type == DSO_KERNEL) { @@ -313,35 +358,137 @@ class PerfDataReader { key.kernel_start_addr = kernel_map_start_addr_; } } - branch_list_callback_(key, binary); + etm_binary_callback_(key, binary); } } - const std::string filename_; - bool exclude_perf_; ETMDumpOption etm_dump_option_; - BinaryFilter binary_filter_; - AutoFDOBinaryCallback autofdo_callback_; - BranchListBinaryCallback branch_list_callback_; - + ETMBinaryCallback etm_binary_callback_; + ETMThreadTreeWithFilter etm_thread_tree_; std::vector<uint8_t> aux_data_buffer_; std::unique_ptr<ETMDecoder> etm_decoder_; - std::unique_ptr<RecordFileReader> record_file_reader_; - ETMThreadTreeWithFilter thread_tree_; uint64_t kernel_map_start_addr_ = 0; - // Store results for AutoFDO. - std::unordered_map<Dso*, AutoFDOBinaryInfo> autofdo_binary_map_; - // Store results for BranchList. - std::unordered_map<Dso*, BranchListBinaryInfo> branch_list_binary_map_; + // Store etm branch list data. + std::unordered_map<Dso*, ETMBinary> etm_binary_map_; }; -// Read a protobuf file specified by etm_branch_list.proto, and generate BranchListBinaryInfo. -class BranchListReader { +class LBRPerfDataReader : public PerfDataReader { public: - BranchListReader(const std::string& filename, const RegEx* binary_name_regex) + LBRPerfDataReader(std::unique_ptr<RecordFileReader> reader, bool exclude_perf, + const RegEx* binary_name_regex) + : PerfDataReader(std::move(reader), exclude_perf, binary_name_regex) {} + void SetCallback(const LBRDataCallback& callback) override { lbr_data_callback_ = callback; } + + private: + bool ProcessRecord(Record& r) override { + thread_tree_.Update(r); + if (r.type() == PERF_RECORD_SAMPLE) { + auto& sr = static_cast<SampleRecord&>(r); + ThreadEntry* thread = thread_tree_.FindThread(sr.tid_data.tid); + if (thread == nullptr) { + return true; + } + auto& stack = sr.branch_stack_data; + lbr_data_.samples.resize(lbr_data_.samples.size() + 1); + LBRSample& sample = lbr_data_.samples.back(); + std::pair<uint32_t, uint64_t> binary_addr = IpToBinaryAddr(*thread, sr.ip_data.ip); + sample.binary_id = binary_addr.first; + bool has_valid_binary_id = sample.binary_id != 0; + sample.vaddr_in_file = binary_addr.second; + sample.branches.resize(stack.stack_nr); + for (size_t i = 0; i < stack.stack_nr; ++i) { + uint64_t from_ip = stack.stack[i].from; + uint64_t to_ip = stack.stack[i].to; + LBRBranch& branch = sample.branches[i]; + binary_addr = IpToBinaryAddr(*thread, from_ip); + branch.from_binary_id = binary_addr.first; + branch.from_vaddr_in_file = binary_addr.second; + binary_addr = IpToBinaryAddr(*thread, to_ip); + branch.to_binary_id = binary_addr.first; + branch.to_vaddr_in_file = binary_addr.second; + if (branch.from_binary_id != 0 || branch.to_binary_id != 0) { + has_valid_binary_id = true; + } + } + if (!has_valid_binary_id) { + lbr_data_.samples.pop_back(); + } + } + return true; + } + + bool PostProcess() override { + if (autofdo_callback_) { + ConvertLBRDataToAutoFDO(); + ProcessAutoFDOBinaryInfo(); + } + return true; + } + + std::pair<uint32_t, uint64_t> IpToBinaryAddr(ThreadEntry& thread, uint64_t ip) { + const MapEntry* map = thread_tree_.FindMap(&thread, ip); + Dso* dso = map->dso; + if (thread_tree_.IsUnknownDso(dso) || !binary_filter_.Filter(dso)) { + return std::make_pair(0, 0); + } + uint32_t binary_id = GetBinaryId(dso); + uint64_t vaddr_in_file = dso->IpToVaddrInFile(ip, map->start_addr, map->pgoff); + return std::make_pair(binary_id, vaddr_in_file); + } + + uint32_t GetBinaryId(const Dso* dso) { + if (auto it = dso_map_.find(dso); it != dso_map_.end()) { + return it->second; + } + uint32_t binary_id = static_cast<uint32_t>(lbr_data_.binaries.size() + 1); + dso_map_[dso] = binary_id; + return binary_id; + } + + void ConvertLBRDataToAutoFDO() { + std::vector<AutoFDOBinaryInfo> binaries(dso_map_.size()); + for (const LBRSample& sample : lbr_data_.samples) { + if (sample.binary_id != 0) { + binaries[sample.binary_id - 1].AddAddress(sample.vaddr_in_file); + } + for (size_t i = 0; i < sample.branches.size(); ++i) { + const LBRBranch& branch = sample.branches[i]; + if (branch.from_binary_id == 0) { + continue; + } + if (branch.from_binary_id == branch.to_binary_id) { + binaries[branch.from_binary_id - 1].AddBranch(branch.from_vaddr_in_file, + branch.to_vaddr_in_file); + } + if (i > 0 && branch.from_binary_id == sample.branches[i - 1].to_binary_id) { + uint64_t begin = sample.branches[i - 1].to_vaddr_in_file; + uint64_t end = branch.from_vaddr_in_file; + // Use the same logic to skip bogus LBR data as AutoFDO. + if (end < begin || end - begin > (1 << 20)) { + continue; + } + binaries[branch.from_binary_id - 1].AddRange(begin, end); + } + } + } + for (const auto& [dso, binary_id] : dso_map_) { + autofdo_binary_map_[dso] = std::move(binaries[binary_id - 1]); + } + } + + LBRDataCallback lbr_data_callback_; + LBRData lbr_data_; + // Map from dso to binary_id in lbr_data_. + std::unordered_map<const Dso*, uint32_t> dso_map_; +}; + +// Read a protobuf file specified by etm_branch_list.proto, and generate ETMBinary. +class ETMBranchListReader { + public: + ETMBranchListReader(const std::string& filename, const RegEx* binary_name_regex) : filename_(filename), binary_filter_(binary_name_regex) {} - void SetCallback(const BranchListBinaryCallback& callback) { callback_ = callback; } + void SetCallback(const ETMBinaryCallback& callback) { callback_ = callback; } bool Read() { std::string s; @@ -349,8 +496,8 @@ class BranchListReader { PLOG(ERROR) << "failed to read " << filename_; return false; } - BranchListBinaryMap binary_map; - if (!StringToBranchListBinaryMap(s, binary_map)) { + ETMBinaryMap binary_map; + if (!StringToETMBinaryMap(s, binary_map)) { PLOG(ERROR) << "file is in wrong format: " << filename_; return false; } @@ -366,13 +513,13 @@ class BranchListReader { private: const std::string filename_; BinaryFilter binary_filter_; - BranchListBinaryCallback callback_; + ETMBinaryCallback callback_; }; -// Convert BranchListBinaryInfo into AutoFDOBinaryInfo. -class BranchListToAutoFDOConverter { +// Convert ETMBinary into AutoFDOBinaryInfo. +class ETMBranchListToAutoFDOConverter { public: - std::unique_ptr<AutoFDOBinaryInfo> Convert(const BinaryKey& key, BranchListBinaryInfo& binary) { + std::unique_ptr<AutoFDOBinaryInfo> Convert(const BinaryKey& key, ETMBinary& binary) { BuildId build_id = key.build_id; std::unique_ptr<Dso> dso = Dso::CreateDsoWithBuildId(binary.dso_type, key.path, build_id); if (!dso || !CheckBuildId(dso.get(), key.build_id)) { @@ -390,8 +537,8 @@ class BranchListToAutoFDOConverter { autofdo_binary->AddInstrRange(range); }; - auto result = - ConvertBranchMapToInstrRanges(dso.get(), binary.GetOrderedBranchMap(), process_instr_range); + auto result = ConvertETMBranchMapToInstrRanges(dso.get(), binary.GetOrderedBranchMap(), + process_instr_range); if (!result.ok()) { LOG(WARNING) << "failed to build instr ranges for binary " << dso->Path() << ": " << result.error(); @@ -410,15 +557,14 @@ class BranchListToAutoFDOConverter { build_id == expected_build_id; } - void ModifyBranchMapForKernel(Dso* dso, uint64_t kernel_start_addr, - BranchListBinaryInfo& binary) { + void ModifyBranchMapForKernel(Dso* dso, uint64_t kernel_start_addr, ETMBinary& binary) { if (kernel_start_addr == 0) { // vmlinux has been provided when generating branch lists. Addresses in branch lists are // already vaddrs in vmlinux. return; } // Addresses are still kernel ip addrs in memory. Need to convert them to vaddrs in vmlinux. - UnorderedBranchMap new_branch_map; + UnorderedETMBranchMap new_branch_map; for (auto& p : binary.branch_map) { uint64_t vaddr_in_file = dso->IpToVaddrInFile(p.first, kernel_start_addr, 0); new_branch_map[vaddr_in_file] = std::move(p.second); @@ -486,7 +632,12 @@ class AutoFDOWriter { } // Write addr_count_map. - fprintf(output_fp.get(), "0\n"); + std::map<uint64_t, uint64_t> address_count_map(binary.address_count_map.begin(), + binary.address_count_map.end()); + fprintf(output_fp.get(), "%zu\n", address_count_map.size()); + for (const auto& [addr, count] : address_count_map) { + fprintf(output_fp.get(), "%" PRIx64 ":%" PRIu64 "\n", to_offset(addr), count); + } // Write branch_count_map. std::map<AddrPair, uint64_t> branch_count_map(binary.branch_count_map.begin(), @@ -511,9 +662,9 @@ class AutoFDOWriter { std::unordered_map<BinaryKey, AutoFDOBinaryInfo, BinaryKeyHash> binary_map_; }; -// Merge BranchListBinaryInfo. -struct BranchListMerger { - void AddBranchListBinary(const BinaryKey& key, BranchListBinaryInfo& binary) { +// Merge ETMBinary. +struct ETMBranchListMerger { + void AddETMBinary(const BinaryKey& key, ETMBinary& binary) { auto it = binary_map.find(key); if (it == binary_map.end()) { binary_map[key] = std::move(binary); @@ -522,13 +673,13 @@ struct BranchListMerger { } } - BranchListBinaryMap binary_map; + ETMBinaryMap binary_map; }; // Write branch lists to a protobuf file specified by etm_branch_list.proto. -class BranchListWriter { +class ETMBranchListWriter { public: - bool Write(const std::string& output_filename, const BranchListBinaryMap& binary_map) { + bool Write(const std::string& output_filename, const ETMBinaryMap& binary_map) { // Don't produce empty output file. if (binary_map.empty()) { LOG(INFO) << "Skip empty output file."; @@ -536,8 +687,8 @@ class BranchListWriter { return true; } std::string s; - if (!BranchListBinaryMapToString(binary_map, s)) { - LOG(ERROR) << "invalid BranchListBinaryMap"; + if (!ETMBinaryMapToString(binary_map, s)) { + LOG(ERROR) << "invalid ETMBinaryMap"; return false; } if (!android::base::WriteStringToFile(s, output_filename)) { @@ -684,59 +835,88 @@ class InjectCommand : public Command { return true; } + bool ReadPerfDataFiles(const std::function<void(PerfDataReader&)> reader_callback) { + if (input_filenames_.empty()) { + return true; + } + + std::string expected_data_type; + for (const auto& filename : input_filenames_) { + std::unique_ptr<RecordFileReader> file_reader = RecordFileReader::CreateInstance(filename); + if (!file_reader) { + return false; + } + std::string data_type = PerfDataReader::GetDataType(*file_reader); + if (expected_data_type.empty()) { + expected_data_type = data_type; + } else if (expected_data_type != data_type) { + LOG(ERROR) << "files have different data type: " << input_filenames_[0] << ", " << filename; + return false; + } + std::unique_ptr<PerfDataReader> reader; + if (data_type == "etm") { + reader.reset(new ETMPerfDataReader(std::move(file_reader), exclude_perf_, + binary_name_regex_.get(), etm_dump_option_)); + } else if (data_type == "lbr") { + reader.reset( + new LBRPerfDataReader(std::move(file_reader), exclude_perf_, binary_name_regex_.get())); + } else { + LOG(ERROR) << "unsupported data type " << data_type << " in " << filename; + return false; + } + reader_callback(*reader); + if (!reader->Read()) { + return false; + } + } + return true; + } + bool ConvertPerfDataToAutoFDO() { AutoFDOWriter autofdo_writer; - auto callback = [&](const BinaryKey& key, AutoFDOBinaryInfo& binary) { + auto afdo_callback = [&](const BinaryKey& key, AutoFDOBinaryInfo& binary) { autofdo_writer.AddAutoFDOBinary(key, binary); }; - for (const auto& input_filename : input_filenames_) { - PerfDataReader reader(input_filename, exclude_perf_, etm_dump_option_, - binary_name_regex_.get()); - reader.SetCallback(callback); - if (!reader.Read()) { - return false; - } + auto reader_callback = [&](PerfDataReader& reader) { reader.SetCallback(afdo_callback); }; + if (!ReadPerfDataFiles(reader_callback)) { + return false; } return autofdo_writer.Write(output_filename_); } bool ConvertPerfDataToBranchList() { - BranchListMerger branch_list_merger; - auto callback = [&](const BinaryKey& key, BranchListBinaryInfo& binary) { - branch_list_merger.AddBranchListBinary(key, binary); + ETMBranchListMerger branch_list_merger; + auto etm_callback = [&](const BinaryKey& key, ETMBinary& binary) { + branch_list_merger.AddETMBinary(key, binary); }; - for (const auto& input_filename : input_filenames_) { - PerfDataReader reader(input_filename, exclude_perf_, etm_dump_option_, - binary_name_regex_.get()); - reader.SetCallback(callback); - if (!reader.Read()) { - return false; - } + auto reader_callback = [&](PerfDataReader& reader) { reader.SetCallback(etm_callback); }; + if (!ReadPerfDataFiles(reader_callback)) { + return false; } - BranchListWriter branch_list_writer; + ETMBranchListWriter branch_list_writer; return branch_list_writer.Write(output_filename_, branch_list_merger.binary_map); } bool ConvertBranchListToAutoFDO() { // Step1 : Merge branch lists from all input files. - BranchListMerger branch_list_merger; - auto callback = [&](const BinaryKey& key, BranchListBinaryInfo& binary) { - branch_list_merger.AddBranchListBinary(key, binary); + ETMBranchListMerger branch_list_merger; + auto callback = [&](const BinaryKey& key, ETMBinary& binary) { + branch_list_merger.AddETMBinary(key, binary); }; for (const auto& input_filename : input_filenames_) { - BranchListReader reader(input_filename, binary_name_regex_.get()); + ETMBranchListReader reader(input_filename, binary_name_regex_.get()); reader.SetCallback(callback); if (!reader.Read()) { return false; } } - // Step2: Convert BranchListBinaryInfo to AutoFDOBinaryInfo. + // Step2: Convert ETMBinary to AutoFDOBinaryInfo. AutoFDOWriter autofdo_writer; - BranchListToAutoFDOConverter converter; + ETMBranchListToAutoFDOConverter converter; for (auto& p : branch_list_merger.binary_map) { const BinaryKey& key = p.first; - BranchListBinaryInfo& binary = p.second; + ETMBinary& binary = p.second; std::unique_ptr<AutoFDOBinaryInfo> autofdo_binary = converter.Convert(key, binary); if (autofdo_binary) { // Create new BinaryKey with kernel_start_addr = 0. Because AutoFDO output doesn't care @@ -751,19 +931,19 @@ class InjectCommand : public Command { bool ConvertBranchListToBranchList() { // Step1 : Merge branch lists from all input files. - BranchListMerger branch_list_merger; - auto callback = [&](const BinaryKey& key, BranchListBinaryInfo& binary) { - branch_list_merger.AddBranchListBinary(key, binary); + ETMBranchListMerger branch_list_merger; + auto callback = [&](const BinaryKey& key, ETMBinary& binary) { + branch_list_merger.AddETMBinary(key, binary); }; for (const auto& input_filename : input_filenames_) { - BranchListReader reader(input_filename, binary_name_regex_.get()); + ETMBranchListReader reader(input_filename, binary_name_regex_.get()); reader.SetCallback(callback); if (!reader.Read()) { return false; } } - // Step2: Write BranchListBinaryInfo. - BranchListWriter branch_list_writer; + // Step2: Write ETMBinary. + ETMBranchListWriter branch_list_writer; return branch_list_writer.Write(output_filename_, branch_list_merger.binary_map); } |