summaryrefslogtreecommitdiff
path: root/simpleperf/cmd_inject.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'simpleperf/cmd_inject.cpp')
-rw-r--r--simpleperf/cmd_inject.cpp574
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);
}