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.cpp355
1 files changed, 99 insertions, 256 deletions
diff --git a/simpleperf/cmd_inject.cpp b/simpleperf/cmd_inject.cpp
index b2c573d4..d3f66cd7 100644
--- a/simpleperf/cmd_inject.cpp
+++ b/simpleperf/cmd_inject.cpp
@@ -14,19 +14,20 @@
* limitations under the License.
*/
+#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
#include <memory>
#include <optional>
-#include <regex>
#include <string>
#include <android-base/parseint.h>
#include <android-base/strings.h>
+#include "ETMBranchListFile.h"
#include "ETMDecoder.h"
-#include "cmd_inject_impl.h"
+#include "RegEx.h"
#include "command.h"
#include "record_file.h"
#include "system/extras/simpleperf/etm_branch_list.pb.h"
@@ -35,31 +36,8 @@
namespace simpleperf {
-std::string BranchToProtoString(const std::vector<bool>& branch) {
- size_t bytes = (branch.size() + 7) / 8;
- std::string res(bytes, '\0');
- for (size_t i = 0; i < branch.size(); i++) {
- if (branch[i]) {
- res[i >> 3] |= 1 << (i & 7);
- }
- }
- return res;
-}
-
-std::vector<bool> ProtoStringToBranch(const std::string& s, size_t bit_size) {
- std::vector<bool> branch(bit_size, false);
- for (size_t i = 0; i < bit_size; i++) {
- if (s[i >> 3] & (1 << (i & 7))) {
- branch[i] = true;
- }
- }
- return branch;
-}
-
namespace {
-constexpr const char* ETM_BRANCH_LIST_PROTO_MAGIC = "simpleperf:EtmBranchList";
-
using AddrPair = std::pair<uint64_t, uint64_t>;
struct AddrPairHash {
@@ -76,53 +54,6 @@ enum class OutputFormat {
BranchList,
};
-// When processing binary info in an input file, the binaries are identified by their path.
-// But this isn't sufficient when merging binary info from multiple input files. Because
-// binaries for the same path may be changed between generating input files. So after processing
-// each input file, we create BinaryKeys to identify binaries, which consider path, build_id and
-// kernel_start_addr (for vmlinux). kernel_start_addr affects how addresses in BranchListBinaryInfo
-// are interpreted for vmlinux.
-struct BinaryKey {
- std::string path;
- BuildId build_id;
- uint64_t kernel_start_addr = 0;
-
- BinaryKey() {}
-
- BinaryKey(const std::string& path, BuildId build_id) : path(path), build_id(build_id) {}
-
- BinaryKey(Dso* dso, uint64_t kernel_start_addr) : path(dso->Path()) {
- build_id = Dso::FindExpectedBuildIdForPath(dso->Path());
- if (dso->type() == DSO_KERNEL) {
- this->kernel_start_addr = kernel_start_addr;
- }
- }
-
- bool operator==(const BinaryKey& other) const {
- return path == other.path && build_id == other.build_id &&
- kernel_start_addr == other.kernel_start_addr;
- }
-};
-
-struct BinaryKeyHash {
- size_t operator()(const BinaryKey& key) const noexcept {
- size_t seed = 0;
- HashCombine(seed, key.path);
- HashCombine(seed, key.build_id);
- if (key.kernel_start_addr != 0) {
- HashCombine(seed, key.kernel_start_addr);
- }
- return seed;
- }
-};
-
-static void OverflowSafeAdd(uint64_t& dest, uint64_t add) {
- if (__builtin_add_overflow(dest, add, &dest)) {
- LOG(WARNING) << "Branch count overflow happened.";
- dest = UINT64_MAX;
- }
-}
-
struct AutoFDOBinaryInfo {
uint64_t first_load_segment_addr = 0;
std::unordered_map<AddrPair, uint64_t, AddrPairHash> range_count_map;
@@ -155,78 +86,50 @@ struct AutoFDOBinaryInfo {
}
};
-using UnorderedBranchMap =
- std::unordered_map<uint64_t, std::unordered_map<std::vector<bool>, uint64_t>>;
-
-struct BranchListBinaryInfo {
- DsoType dso_type;
- UnorderedBranchMap branch_map;
-
- void Merge(const BranchListBinaryInfo& other) {
- for (auto& other_p : other.branch_map) {
- auto it = branch_map.find(other_p.first);
- if (it == branch_map.end()) {
- branch_map[other_p.first] = std::move(other_p.second);
- } else {
- auto& map2 = it->second;
- for (auto& other_p2 : other_p.second) {
- auto it2 = map2.find(other_p2.first);
- if (it2 == map2.end()) {
- map2[other_p2.first] = other_p2.second;
- } else {
- OverflowSafeAdd(it2->second, other_p2.second);
- }
- }
- }
- }
- }
-
- BranchMap GetOrderedBranchMap() const {
- BranchMap result;
- for (const auto& p : branch_map) {
- uint64_t addr = p.first;
- const auto& b_map = p.second;
- result[addr] = std::map<std::vector<bool>, uint64_t>(b_map.begin(), b_map.end());
- }
- return result;
- }
-};
-
using AutoFDOBinaryCallback = std::function<void(const BinaryKey&, AutoFDOBinaryInfo&)>;
using BranchListBinaryCallback = std::function<void(const BinaryKey&, BranchListBinaryInfo&)>;
-class ThreadTreeWithFilter : public ThreadTree {
+class ETMThreadTreeWithFilter : public ETMThreadTree {
public:
void ExcludePid(pid_t pid) { exclude_pid_ = pid; }
+ ThreadTree& GetThreadTree() { return thread_tree_; }
+ void DisableThreadExitRecords() override { thread_tree_.DisableThreadExitRecords(); }
- ThreadEntry* FindThread(int tid) const override {
- ThreadEntry* thread = ThreadTree::FindThread(tid);
+ 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 DsoFilter {
+class BinaryFilter {
public:
- DsoFilter(const std::regex& binary_name_regex) : binary_name_regex_(binary_name_regex) {}
+ BinaryFilter(const RegEx* binary_name_regex) : binary_name_regex_(binary_name_regex) {}
- bool FilterDso(Dso* dso) {
+ bool Filter(Dso* dso) {
auto lookup = dso_filter_cache_.find(dso);
if (lookup != dso_filter_cache_.end()) {
return lookup->second;
}
- bool match = std::regex_search(dso->Path(), binary_name_regex_);
+ 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:
- std::regex binary_name_regex_;
+ const RegEx* binary_name_regex_;
std::unordered_map<Dso*, bool> dso_filter_cache_;
};
@@ -247,11 +150,11 @@ static uint64_t GetFirstLoadSegmentVaddr(Dso* dso) {
class PerfDataReader {
public:
PerfDataReader(const std::string& filename, bool exclude_perf, ETMDumpOption etm_dump_option,
- const std::regex& binary_name_regex)
+ const RegEx* binary_name_regex)
: filename_(filename),
exclude_perf_(exclude_perf),
etm_dump_option_(etm_dump_option),
- dso_filter_(binary_name_regex) {}
+ binary_filter_(binary_name_regex) {}
void SetCallback(const AutoFDOBinaryCallback& callback) { autofdo_callback_ = callback; }
void SetCallback(const BranchListBinaryCallback& callback) { branch_list_callback_ = callback; }
@@ -261,6 +164,9 @@ class PerfDataReader {
if (!record_file_reader_) {
return false;
}
+ if (record_file_reader_->HasFeature(PerfFileFormat::FEAT_ETM_BRANCH_LIST)) {
+ return ProcessETMBranchListFeature();
+ }
if (exclude_perf_) {
const auto& info_map = record_file_reader_->GetMetaInfoFeature();
if (auto it = info_map.find("recording_process"); it == info_map.end()) {
@@ -275,7 +181,9 @@ class PerfDataReader {
thread_tree_.ExcludePid(pid);
}
}
- record_file_reader_->LoadBuildIdAndFileFeatures(thread_tree_);
+ if (!record_file_reader_->LoadBuildIdAndFileFeatures(thread_tree_.GetThreadTree())) {
+ return false;
+ }
if (!record_file_reader_->ReadDataSection([this](auto r) { return ProcessRecord(r.get()); })) {
return false;
}
@@ -291,8 +199,34 @@ class PerfDataReader {
}
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);
+ }
+ return true;
+ }
+
bool ProcessRecord(Record* r) {
- thread_tree_.Update(*r);
+ thread_tree_.GetThreadTree().Update(*r);
if (r->type() == PERF_RECORD_AUXTRACE_INFO) {
etm_decoder_ = ETMDecoder::Create(*static_cast<AuxTraceInfoRecord*>(r), thread_tree_);
if (!etm_decoder_) {
@@ -308,14 +242,19 @@ class PerfDataReader {
}
} else if (r->type() == PERF_RECORD_AUX) {
AuxRecord* aux = static_cast<AuxRecord*>(r);
- uint64_t aux_size = aux->data->aux_size;
+ if (aux->data->aux_size > SIZE_MAX) {
+ LOG(ERROR) << "invalid aux size";
+ return false;
+ }
+ size_t aux_size = aux->data->aux_size;
if (aux_size > 0) {
- if (aux_data_buffer_.size() < aux_size) {
- aux_data_buffer_.resize(aux_size);
+ bool error = false;
+ if (!record_file_reader_->ReadAuxData(aux->Cpu(), aux->data->aux_offset, aux_size,
+ aux_data_buffer_, error)) {
+ return !error;
}
- if (!record_file_reader_->ReadAuxData(aux->Cpu(), aux->data->aux_offset,
- aux_data_buffer_.data(), aux_size)) {
- LOG(ERROR) << "failed to read aux data in " << filename_;
+ if (!etm_decoder_) {
+ LOG(ERROR) << "ETMDecoder isn't created";
return false;
}
return etm_decoder_->ProcessData(aux_data_buffer_.data(), aux_size, !aux->Unformatted(),
@@ -331,7 +270,7 @@ class PerfDataReader {
}
void ProcessInstrRange(const ETMInstrRange& instr_range) {
- if (!dso_filter_.FilterDso(instr_range.dso)) {
+ if (!binary_filter_.Filter(instr_range.dso)) {
return;
}
@@ -339,7 +278,7 @@ class PerfDataReader {
}
void ProcessBranchList(const ETMBranchList& branch_list) {
- if (!dso_filter_.FilterDso(branch_list.dso)) {
+ if (!binary_filter_.Filter(branch_list.dso)) {
return;
}
@@ -381,14 +320,14 @@ class PerfDataReader {
const std::string filename_;
bool exclude_perf_;
ETMDumpOption etm_dump_option_;
- DsoFilter dso_filter_;
+ BinaryFilter binary_filter_;
AutoFDOBinaryCallback autofdo_callback_;
BranchListBinaryCallback branch_list_callback_;
std::vector<uint8_t> aux_data_buffer_;
std::unique_ptr<ETMDecoder> etm_decoder_;
std::unique_ptr<RecordFileReader> record_file_reader_;
- ThreadTreeWithFilter thread_tree_;
+ ETMThreadTreeWithFilter thread_tree_;
uint64_t kernel_map_start_addr_ = 0;
// Store results for AutoFDO.
std::unordered_map<Dso*, AutoFDOBinaryInfo> autofdo_binary_map_;
@@ -399,82 +338,34 @@ class PerfDataReader {
// Read a protobuf file specified by etm_branch_list.proto, and generate BranchListBinaryInfo.
class BranchListReader {
public:
- BranchListReader(const std::string& filename, const std::regex binary_name_regex)
- : filename_(filename), binary_name_regex_(binary_name_regex) {}
+ BranchListReader(const std::string& filename, const RegEx* binary_name_regex)
+ : filename_(filename), binary_filter_(binary_name_regex) {}
void SetCallback(const BranchListBinaryCallback& callback) { callback_ = callback; }
bool Read() {
- auto fd = FileHelper::OpenReadOnly(filename_);
- if (!fd.ok()) {
- PLOG(ERROR) << "failed to open " << filename_;
- return false;
- }
-
- proto::ETMBranchList branch_list_proto;
- if (!branch_list_proto.ParseFromFileDescriptor(fd)) {
- PLOG(ERROR) << "failed to read msg from " << filename_;
+ std::string s;
+ if (!android::base::ReadFileToString(filename_, &s)) {
+ PLOG(ERROR) << "failed to read " << filename_;
return false;
}
- if (branch_list_proto.magic() != ETM_BRANCH_LIST_PROTO_MAGIC) {
- PLOG(ERROR) << "file not in format etm_branch_list.proto: " << filename_;
+ BranchListBinaryMap binary_map;
+ if (!StringToBranchListBinaryMap(s, binary_map)) {
+ PLOG(ERROR) << "file is in wrong format: " << filename_;
return false;
}
-
- for (size_t i = 0; i < branch_list_proto.binaries_size(); i++) {
- const auto& binary_proto = branch_list_proto.binaries(i);
- if (!std::regex_search(binary_proto.path(), binary_name_regex_)) {
+ for (auto& [key, binary] : binary_map) {
+ if (!binary_filter_.Filter(key.path)) {
continue;
}
- BinaryKey key(binary_proto.path(), BuildId(binary_proto.build_id()));
- if (binary_proto.has_kernel_info()) {
- key.kernel_start_addr = binary_proto.kernel_info().kernel_start_addr();
- }
- BranchListBinaryInfo binary;
- auto dso_type = ToDsoType(binary_proto.type());
- if (!dso_type) {
- LOG(ERROR) << "invalid binary type in " << filename_;
- return false;
- }
- binary.dso_type = dso_type.value();
- binary.branch_map = BuildUnorderedBranchMap(binary_proto);
callback_(key, binary);
}
return true;
}
private:
- std::optional<DsoType> ToDsoType(proto::ETMBranchList_Binary::BinaryType binary_type) {
- switch (binary_type) {
- case proto::ETMBranchList_Binary::ELF_FILE:
- return DSO_ELF_FILE;
- case proto::ETMBranchList_Binary::KERNEL:
- return DSO_KERNEL;
- case proto::ETMBranchList_Binary::KERNEL_MODULE:
- return DSO_KERNEL_MODULE;
- default:
- LOG(ERROR) << "unexpected binary type " << binary_type;
- return std::nullopt;
- }
- }
-
- UnorderedBranchMap BuildUnorderedBranchMap(const proto::ETMBranchList_Binary& binary_proto) {
- UnorderedBranchMap branch_map;
- for (size_t i = 0; i < binary_proto.addrs_size(); i++) {
- const auto& addr_proto = binary_proto.addrs(i);
- auto& b_map = branch_map[addr_proto.addr()];
- for (size_t j = 0; j < addr_proto.branches_size(); j++) {
- const auto& branch_proto = addr_proto.branches(j);
- std::vector<bool> branch =
- ProtoStringToBranch(branch_proto.branch(), branch_proto.branch_size());
- b_map[branch] = branch_proto.count();
- }
- }
- return branch_map;
- }
-
const std::string filename_;
- const std::regex binary_name_regex_;
+ BinaryFilter binary_filter_;
BranchListBinaryCallback callback_;
};
@@ -610,6 +501,7 @@ class AutoFDOWriter {
}
// Write the binary path in comment.
+ fprintf(output_fp.get(), "// build_id: %s\n", key.build_id.ToString().c_str());
fprintf(output_fp.get(), "// %s\n\n", key.path.c_str());
}
return true;
@@ -630,84 +522,30 @@ struct BranchListMerger {
}
}
- std::unordered_map<BinaryKey, BranchListBinaryInfo, BinaryKeyHash> binary_map;
+ BranchListBinaryMap binary_map;
};
// Write branch lists to a protobuf file specified by etm_branch_list.proto.
class BranchListWriter {
public:
- bool Write(const std::string& output_filename,
- const std::unordered_map<BinaryKey, BranchListBinaryInfo, BinaryKeyHash>& binary_map) {
+ bool Write(const std::string& output_filename, const BranchListBinaryMap& binary_map) {
// Don't produce empty output file.
if (binary_map.empty()) {
LOG(INFO) << "Skip empty output file.";
unlink(output_filename.c_str());
return true;
}
- std::unique_ptr<FILE, decltype(&fclose)> output_fp(fopen(output_filename.c_str(), "wb"),
- fclose);
- if (!output_fp) {
- PLOG(ERROR) << "failed to write to " << output_filename;
+ std::string s;
+ if (!BranchListBinaryMapToString(binary_map, s)) {
+ LOG(ERROR) << "invalid BranchListBinaryMap";
return false;
}
-
- proto::ETMBranchList branch_list_proto;
- branch_list_proto.set_magic(ETM_BRANCH_LIST_PROTO_MAGIC);
- std::vector<char> branch_buf;
- for (const auto& p : binary_map) {
- const BinaryKey& key = p.first;
- const BranchListBinaryInfo& binary = p.second;
- auto binary_proto = branch_list_proto.add_binaries();
-
- binary_proto->set_path(key.path);
- if (!key.build_id.IsEmpty()) {
- binary_proto->set_build_id(key.build_id.ToString().substr(2));
- }
- auto opt_binary_type = ToProtoBinaryType(binary.dso_type);
- if (!opt_binary_type.has_value()) {
- return false;
- }
- binary_proto->set_type(opt_binary_type.value());
-
- for (const auto& addr_p : binary.branch_map) {
- auto addr_proto = binary_proto->add_addrs();
- addr_proto->set_addr(addr_p.first);
-
- for (const auto& branch_p : addr_p.second) {
- const std::vector<bool>& branch = branch_p.first;
- auto branch_proto = addr_proto->add_branches();
-
- branch_proto->set_branch(BranchToProtoString(branch));
- branch_proto->set_branch_size(branch.size());
- branch_proto->set_count(branch_p.second);
- }
- }
-
- if (binary.dso_type == DSO_KERNEL) {
- binary_proto->mutable_kernel_info()->set_kernel_start_addr(key.kernel_start_addr);
- }
- }
- if (!branch_list_proto.SerializeToFileDescriptor(fileno(output_fp.get()))) {
+ if (!android::base::WriteStringToFile(s, output_filename)) {
PLOG(ERROR) << "failed to write to " << output_filename;
return false;
}
return true;
}
-
- private:
- std::optional<proto::ETMBranchList_Binary::BinaryType> ToProtoBinaryType(DsoType dso_type) {
- switch (dso_type) {
- case DSO_ELF_FILE:
- return proto::ETMBranchList_Binary::ELF_FILE;
- case DSO_KERNEL:
- return proto::ETMBranchList_Binary::KERNEL;
- case DSO_KERNEL_MODULE:
- return proto::ETMBranchList_Binary::KERNEL_MODULE;
- default:
- LOG(ERROR) << "unexpected dso type " << dso_type;
- return std::nullopt;
- }
- }
};
class InjectCommand : public Command {
@@ -783,7 +621,10 @@ class InjectCommand : public Command {
}
if (auto value = options.PullValue("--binary"); value) {
- binary_name_regex_ = *value->str_value;
+ binary_name_regex_ = RegEx::Create(*value->str_value);
+ if (binary_name_regex_ == nullptr) {
+ return false;
+ }
}
if (auto value = options.PullValue("--dump-etm"); value) {
if (!ParseEtmDumpOption(*value->str_value, &etm_dump_option_)) {
@@ -849,7 +690,8 @@ class InjectCommand : public Command {
autofdo_writer.AddAutoFDOBinary(key, binary);
};
for (const auto& input_filename : input_filenames_) {
- PerfDataReader reader(input_filename, exclude_perf_, etm_dump_option_, binary_name_regex_);
+ PerfDataReader reader(input_filename, exclude_perf_, etm_dump_option_,
+ binary_name_regex_.get());
reader.SetCallback(callback);
if (!reader.Read()) {
return false;
@@ -864,7 +706,8 @@ class InjectCommand : public Command {
branch_list_merger.AddBranchListBinary(key, binary);
};
for (const auto& input_filename : input_filenames_) {
- PerfDataReader reader(input_filename, exclude_perf_, etm_dump_option_, binary_name_regex_);
+ PerfDataReader reader(input_filename, exclude_perf_, etm_dump_option_,
+ binary_name_regex_.get());
reader.SetCallback(callback);
if (!reader.Read()) {
return false;
@@ -881,7 +724,7 @@ class InjectCommand : public Command {
branch_list_merger.AddBranchListBinary(key, binary);
};
for (const auto& input_filename : input_filenames_) {
- BranchListReader reader(input_filename, binary_name_regex_);
+ BranchListReader reader(input_filename, binary_name_regex_.get());
reader.SetCallback(callback);
if (!reader.Read()) {
return false;
@@ -913,7 +756,7 @@ class InjectCommand : public Command {
branch_list_merger.AddBranchListBinary(key, binary);
};
for (const auto& input_filename : input_filenames_) {
- BranchListReader reader(input_filename, binary_name_regex_);
+ BranchListReader reader(input_filename, binary_name_regex_.get());
reader.SetCallback(callback);
if (!reader.Read()) {
return false;
@@ -924,7 +767,7 @@ class InjectCommand : public Command {
return branch_list_writer.Write(output_filename_, branch_list_merger.binary_map);
}
- std::regex binary_name_regex_{""}; // Default to match everything.
+ std::unique_ptr<RegEx> binary_name_regex_;
bool exclude_perf_ = false;
std::vector<std::string> input_filenames_;
std::string output_filename_ = "perf_inject.data";