summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYabin Cui <yabinc@google.com>2023-12-07 00:53:17 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2023-12-07 00:53:17 +0000
commitc2ad5509aa878653e66589b3391c9b7a174e2d66 (patch)
tree5487f456601770f906327ee02af71c90b65945cd
parent54320640c7e8e4fc7ad5bee9e063c1bcf0ff2b59 (diff)
parentddcd44108ce38faf92ca4ded91041dfb2b5af705 (diff)
downloadextras-c2ad5509aa878653e66589b3391c9b7a174e2d66.tar.gz
Merge changes I58ae6a07,Icae497f9,I914c46b7 into main am: 9707ab6918 am: ddcd44108c
Original change: https://android-review.googlesource.com/c/platform/system/extras/+/2858330 Change-Id: I4580d29ea79c0e91ac932f114291273a1a383a8e Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r--simpleperf/Android.bp5
-rw-r--r--simpleperf/BranchListFile.cpp (renamed from simpleperf/ETMBranchListFile.cpp)64
-rw-r--r--simpleperf/BranchListFile.h (renamed from simpleperf/ETMBranchListFile.h)106
-rw-r--r--simpleperf/BranchListFile_test.cpp (renamed from simpleperf/ETMBranchListFile_test.cpp)8
-rw-r--r--simpleperf/ETMDecoder.cpp4
-rw-r--r--simpleperf/ETMDecoder.h6
-rw-r--r--simpleperf/branch_list.proto67
-rw-r--r--simpleperf/cmd_dumprecord.cpp31
-rw-r--r--simpleperf/cmd_inject.cpp574
-rw-r--r--simpleperf/cmd_inject_test.cpp11
-rw-r--r--simpleperf/cmd_record.cpp6
-rw-r--r--simpleperf/etm_branch_list.proto66
-rw-r--r--simpleperf/record_file.h1
-rw-r--r--simpleperf/testdata/lbr/inject_lbr.data11
-rw-r--r--simpleperf/testdata/lbr/perf_lbr.databin0 -> 23855 bytes
15 files changed, 603 insertions, 357 deletions
diff --git a/simpleperf/Android.bp b/simpleperf/Android.bp
index f1cbb1ea..bf33df45 100644
--- a/simpleperf/Android.bp
+++ b/simpleperf/Android.bp
@@ -220,8 +220,8 @@ cc_defaults {
"cmd_report_sample.proto",
"command.cpp",
"dso.cpp",
- "etm_branch_list.proto",
- "ETMBranchListFile.cpp",
+ "branch_list.proto",
+ "BranchListFile.cpp",
"event_attr.cpp",
"event_type.cpp",
"kallsyms.cpp",
@@ -561,6 +561,7 @@ cc_defaults {
"cmd_report_test.cpp",
],
srcs: [
+ "BranchListFile_test.cpp",
"cmd_inject_test.cpp",
"cmd_kmem_test.cpp",
"cmd_merge_test.cpp",
diff --git a/simpleperf/ETMBranchListFile.cpp b/simpleperf/BranchListFile.cpp
index c92cf8cc..e8f6b22c 100644
--- a/simpleperf/ETMBranchListFile.cpp
+++ b/simpleperf/BranchListFile.cpp
@@ -14,16 +14,16 @@
* limitations under the License.
*/
-#include "ETMBranchListFile.h"
+#include "BranchListFile.h"
#include "ETMDecoder.h"
-#include "system/extras/simpleperf/etm_branch_list.pb.h"
+#include "system/extras/simpleperf/branch_list.pb.h"
namespace simpleperf {
static constexpr const char* ETM_BRANCH_LIST_PROTO_MAGIC = "simpleperf:EtmBranchList";
-std::string BranchToProtoString(const std::vector<bool>& branch) {
+std::string ETMBranchToProtoString(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++) {
@@ -34,7 +34,7 @@ std::string BranchToProtoString(const std::vector<bool>& branch) {
return res;
}
-std::vector<bool> ProtoStringToBranch(const std::string& s, size_t bit_size) {
+std::vector<bool> ProtoStringToETMBranch(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))) {
@@ -44,28 +44,28 @@ std::vector<bool> ProtoStringToBranch(const std::string& s, size_t bit_size) {
return branch;
}
-static std::optional<proto::ETMBranchList_Binary::BinaryType> ToProtoBinaryType(DsoType dso_type) {
+static std::optional<proto::ETMBinary::BinaryType> ToProtoBinaryType(DsoType dso_type) {
switch (dso_type) {
case DSO_ELF_FILE:
- return proto::ETMBranchList_Binary::ELF_FILE;
+ return proto::ETMBinary::ELF_FILE;
case DSO_KERNEL:
- return proto::ETMBranchList_Binary::KERNEL;
+ return proto::ETMBinary::KERNEL;
case DSO_KERNEL_MODULE:
- return proto::ETMBranchList_Binary::KERNEL_MODULE;
+ return proto::ETMBinary::KERNEL_MODULE;
default:
LOG(ERROR) << "unexpected dso type " << dso_type;
return std::nullopt;
}
}
-bool BranchListBinaryMapToString(const BranchListBinaryMap& binary_map, std::string& s) {
- proto::ETMBranchList branch_list_proto;
+bool ETMBinaryMapToString(const ETMBinaryMap& binary_map, std::string& s) {
+ proto::BranchList 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();
+ const ETMBinary& binary = p.second;
+ auto binary_proto = branch_list_proto.add_etm_data();
binary_proto->set_path(key.path);
if (!key.build_id.IsEmpty()) {
@@ -85,7 +85,7 @@ bool BranchListBinaryMapToString(const BranchListBinaryMap& binary_map, std::str
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(ETMBranchToProtoString(branch));
branch_proto->set_branch_size(branch.size());
branch_proto->set_count(branch_p.second);
}
@@ -102,13 +102,13 @@ bool BranchListBinaryMapToString(const BranchListBinaryMap& binary_map, std::str
return true;
}
-static std::optional<DsoType> ToDsoType(proto::ETMBranchList_Binary::BinaryType binary_type) {
+static std::optional<DsoType> ToDsoType(proto::ETMBinary::BinaryType binary_type) {
switch (binary_type) {
- case proto::ETMBranchList_Binary::ELF_FILE:
+ case proto::ETMBinary::ELF_FILE:
return DSO_ELF_FILE;
- case proto::ETMBranchList_Binary::KERNEL:
+ case proto::ETMBinary::KERNEL:
return DSO_KERNEL;
- case proto::ETMBranchList_Binary::KERNEL_MODULE:
+ case proto::ETMBinary::KERNEL_MODULE:
return DSO_KERNEL_MODULE;
default:
LOG(ERROR) << "unexpected binary type " << binary_type;
@@ -116,46 +116,46 @@ static std::optional<DsoType> ToDsoType(proto::ETMBranchList_Binary::BinaryType
}
}
-static UnorderedBranchMap BuildUnorderedBranchMap(const proto::ETMBranchList_Binary& binary_proto) {
- UnorderedBranchMap branch_map;
+static UnorderedETMBranchMap BuildUnorderedETMBranchMap(const proto::ETMBinary& binary_proto) {
+ UnorderedETMBranchMap 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());
+ ProtoStringToETMBranch(branch_proto.branch(), branch_proto.branch_size());
b_map[branch] = branch_proto.count();
}
}
return branch_map;
}
-bool StringToBranchListBinaryMap(const std::string& s, BranchListBinaryMap& binary_map) {
- proto::ETMBranchList branch_list_proto;
+bool StringToETMBinaryMap(const std::string& s, ETMBinaryMap& binary_map) {
+ proto::BranchList branch_list_proto;
if (!branch_list_proto.ParseFromString(s)) {
PLOG(ERROR) << "failed to read ETMBranchList msg";
return false;
}
if (branch_list_proto.magic() != ETM_BRANCH_LIST_PROTO_MAGIC) {
- PLOG(ERROR) << "not in format etm_branch_list.proto";
+ PLOG(ERROR) << "not in etm branch list format in branch_list.proto";
return false;
}
- for (size_t i = 0; i < branch_list_proto.binaries_size(); i++) {
- const auto& binary_proto = branch_list_proto.binaries(i);
+ for (size_t i = 0; i < branch_list_proto.etm_data_size(); i++) {
+ const auto& binary_proto = branch_list_proto.etm_data(i);
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 = binary_map[key];
+ ETMBinary& binary = binary_map[key];
auto dso_type = ToDsoType(binary_proto.type());
if (!dso_type) {
LOG(ERROR) << "invalid binary type " << binary_proto.type();
return false;
}
binary.dso_type = dso_type.value();
- binary.branch_map = BuildUnorderedBranchMap(binary_proto);
+ binary.branch_map = BuildUnorderedETMBranchMap(binary_proto);
}
return true;
}
@@ -231,7 +231,7 @@ class ETMBranchListGeneratorImpl : public ETMBranchListGenerator {
}
bool ProcessRecord(const Record& r, bool& consumed) override;
- BranchListBinaryMap GetBranchListBinaryMap() override;
+ ETMBinaryMap GetETMBinaryMap() override;
private:
struct AuxRecordData {
@@ -257,7 +257,7 @@ class ETMBranchListGeneratorImpl : public ETMBranchListGenerator {
BinaryFilter binary_filter_;
std::map<uint32_t, PerCpuData> cpu_map_;
std::unique_ptr<ETMDecoder> etm_decoder_;
- std::unordered_map<Dso*, BranchListBinaryInfo> branch_list_binary_map_;
+ std::unordered_map<Dso*, ETMBinary> branch_list_binary_map_;
};
bool ETMBranchListGeneratorImpl::ProcessRecord(const Record& r, bool& consumed) {
@@ -353,11 +353,11 @@ void ETMBranchListGeneratorImpl::ProcessBranchList(const ETMBranchList& branch_l
++branch_map[branch_list.addr][branch_list.branch];
}
-BranchListBinaryMap ETMBranchListGeneratorImpl::GetBranchListBinaryMap() {
- BranchListBinaryMap binary_map;
+ETMBinaryMap ETMBranchListGeneratorImpl::GetETMBinaryMap() {
+ ETMBinaryMap binary_map;
for (auto& p : branch_list_binary_map_) {
Dso* dso = p.first;
- BranchListBinaryInfo& binary = p.second;
+ ETMBinary& binary = p.second;
binary.dso_type = dso->type();
BuildId build_id;
GetBuildId(*dso, build_id);
diff --git a/simpleperf/ETMBranchListFile.h b/simpleperf/BranchListFile.h
index fd595c56..031a026f 100644
--- a/simpleperf/ETMBranchListFile.h
+++ b/simpleperf/BranchListFile.h
@@ -27,7 +27,7 @@ namespace simpleperf {
// 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
+// kernel_start_addr (for vmlinux). kernel_start_addr affects how addresses in ETMBinary
// are interpreted for vmlinux.
struct BinaryKey {
std::string path;
@@ -38,7 +38,7 @@ struct 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()) {
+ BinaryKey(const 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;
@@ -63,14 +63,42 @@ struct BinaryKeyHash {
}
};
-using UnorderedBranchMap =
+class BinaryFilter {
+ public:
+ BinaryFilter(const RegEx* binary_name_regex) : binary_name_regex_(binary_name_regex) {}
+
+ void SetRegex(const RegEx* binary_name_regex) {
+ binary_name_regex_ = binary_name_regex;
+ dso_filter_cache_.clear();
+ }
+
+ bool Filter(const 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<const Dso*, bool> dso_filter_cache_;
+};
+
+using UnorderedETMBranchMap =
std::unordered_map<uint64_t, std::unordered_map<std::vector<bool>, uint64_t>>;
-struct BranchListBinaryInfo {
+struct ETMBinary {
DsoType dso_type;
- UnorderedBranchMap branch_map;
+ UnorderedETMBranchMap branch_map;
- void Merge(const BranchListBinaryInfo& other) {
+ void Merge(const ETMBinary& other) {
for (auto& other_p : other.branch_map) {
auto it = branch_map.find(other_p.first);
if (it == branch_map.end()) {
@@ -89,8 +117,8 @@ struct BranchListBinaryInfo {
}
}
- BranchMap GetOrderedBranchMap() const {
- BranchMap result;
+ ETMBranchMap GetOrderedBranchMap() const {
+ ETMBranchMap result;
for (const auto& p : branch_map) {
uint64_t addr = p.first;
const auto& b_map = p.second;
@@ -100,38 +128,10 @@ struct BranchListBinaryInfo {
}
};
-using BranchListBinaryMap = std::unordered_map<BinaryKey, BranchListBinaryInfo, BinaryKeyHash>;
-
-bool BranchListBinaryMapToString(const BranchListBinaryMap& binary_map, std::string& s);
-bool StringToBranchListBinaryMap(const std::string& s, BranchListBinaryMap& binary_map);
-
-class BinaryFilter {
- public:
- BinaryFilter(const RegEx* binary_name_regex) : binary_name_regex_(binary_name_regex) {}
-
- void SetRegex(const RegEx* binary_name_regex) {
- binary_name_regex_ = binary_name_regex;
- dso_filter_cache_.clear();
- }
+using ETMBinaryMap = std::unordered_map<BinaryKey, ETMBinary, BinaryKeyHash>;
- 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_;
-};
+bool ETMBinaryMapToString(const ETMBinaryMap& binary_map, std::string& s);
+bool StringToETMBinaryMap(const std::string& s, ETMBinaryMap& binary_map);
// Convert ETM data into branch lists while recording.
class ETMBranchListGenerator {
@@ -142,11 +142,33 @@ class ETMBranchListGenerator {
virtual void SetExcludePid(pid_t pid) = 0;
virtual void SetBinaryFilter(const RegEx* binary_name_regex) = 0;
virtual bool ProcessRecord(const Record& r, bool& consumed) = 0;
- virtual BranchListBinaryMap GetBranchListBinaryMap() = 0;
+ virtual ETMBinaryMap GetETMBinaryMap() = 0;
+};
+
+struct LBRBranch {
+ // If from_binary_id >= 1, it refers to LBRData.binaries[from_binary_id - 1]. Otherwise, it's
+ // invalid.
+ uint32_t from_binary_id = 0;
+ // If to_binary_id >= 1, it refers to LBRData.binaries[to_binary_id - 1]. Otherwise, it's invalid.
+ uint32_t to_binary_id = 0;
+ uint64_t from_vaddr_in_file = 0;
+ uint64_t to_vaddr_in_file = 0;
+};
+
+struct LBRSample {
+ // If binary_id >= 1, it refers to LBRData.binaries[binary_id - 1]. Otherwise, it's invalid.
+ uint32_t binary_id = 0;
+ uint64_t vaddr_in_file = 0;
+ std::vector<LBRBranch> branches;
+};
+
+struct LBRData {
+ std::vector<LBRSample> samples;
+ std::vector<BinaryKey> binaries;
};
// for testing
-std::string BranchToProtoString(const std::vector<bool>& branch);
-std::vector<bool> ProtoStringToBranch(const std::string& s, size_t bit_size);
+std::string ETMBranchToProtoString(const std::vector<bool>& branch);
+std::vector<bool> ProtoStringToETMBranch(const std::string& s, size_t bit_size);
} // namespace simpleperf
diff --git a/simpleperf/ETMBranchListFile_test.cpp b/simpleperf/BranchListFile_test.cpp
index 227c68b3..759b0218 100644
--- a/simpleperf/ETMBranchListFile_test.cpp
+++ b/simpleperf/BranchListFile_test.cpp
@@ -16,20 +16,20 @@
#include <gtest/gtest.h>
-#include "ETMBranchListFile.h"
+#include "BranchListFile.h"
using namespace simpleperf;
-TEST(ETMBranchListFile, branch_to_proto_string) {
+TEST(BranchListFile, etm_branch_to_proto_string) {
std::vector<bool> branch;
for (size_t i = 0; i < 100; i++) {
branch.push_back(i % 2 == 0);
- std::string s = BranchToProtoString(branch);
+ std::string s = ETMBranchToProtoString(branch);
for (size_t j = 0; j <= i; j++) {
bool b = s[j >> 3] & (1 << (j & 7));
ASSERT_EQ(b, branch[j]);
}
- std::vector<bool> branch2 = ProtoStringToBranch(s, branch.size());
+ std::vector<bool> branch2 = ProtoStringToETMBranch(s, branch.size());
ASSERT_EQ(branch, branch2);
}
}
diff --git a/simpleperf/ETMDecoder.cpp b/simpleperf/ETMDecoder.cpp
index 9cf43851..822cc6cd 100644
--- a/simpleperf/ETMDecoder.cpp
+++ b/simpleperf/ETMDecoder.cpp
@@ -930,8 +930,8 @@ class BranchDecoder {
InstructionDecoder instruction_decoder_;
};
-android::base::expected<void, std::string> ConvertBranchMapToInstrRanges(
- Dso* dso, const BranchMap& branch_map, const ETMDecoder::InstrRangeCallbackFn& callback) {
+android::base::expected<void, std::string> ConvertETMBranchMapToInstrRanges(
+ Dso* dso, const ETMBranchMap& branch_map, const ETMDecoder::InstrRangeCallbackFn& callback) {
ETMInstrRange instr_range;
instr_range.dso = dso;
diff --git a/simpleperf/ETMDecoder.h b/simpleperf/ETMDecoder.h
index e8ae7f7d..cb5c8dc8 100644
--- a/simpleperf/ETMDecoder.h
+++ b/simpleperf/ETMDecoder.h
@@ -90,9 +90,9 @@ class ETMDecoder {
// Map from addrs to a map of (branch_list, count).
// Use maps instead of unordered_maps. Because it helps locality by decoding instructions for sorted
// addresses.
-using BranchMap = std::map<uint64_t, std::map<std::vector<bool>, uint64_t>>;
+using ETMBranchMap = std::map<uint64_t, std::map<std::vector<bool>, uint64_t>>;
-android::base::expected<void, std::string> ConvertBranchMapToInstrRanges(
- Dso* dso, const BranchMap& branch_map, const ETMDecoder::InstrRangeCallbackFn& callback);
+android::base::expected<void, std::string> ConvertETMBranchMapToInstrRanges(
+ Dso* dso, const ETMBranchMap& branch_map, const ETMDecoder::InstrRangeCallbackFn& callback);
} // namespace simpleperf \ No newline at end of file
diff --git a/simpleperf/branch_list.proto b/simpleperf/branch_list.proto
new file mode 100644
index 00000000..29247933
--- /dev/null
+++ b/simpleperf/branch_list.proto
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// The branch list file format is generated by the inject command. It contains
+// a single BranchList message.
+
+syntax = "proto3";
+
+package simpleperf.proto;
+
+message BranchList {
+ // Used to identify format in generated proto files.
+ // Should always be "simpleperf:EtmBranchList".
+ string magic = 1;
+ repeated ETMBinary etm_data = 2;
+}
+
+message ETMBinary {
+ string path = 1;
+ string build_id = 2;
+
+ message Address {
+ // vaddr in binary, instr addr before the first branch
+ uint64 addr = 1;
+
+ message Branch {
+ // Each bit represents a branch: 0 for not branch, 1 for branch.
+ // Bit 0 comes first, bit 7 comes last.
+ bytes branch = 1;
+ uint32 branch_size = 2;
+ uint64 count = 3;
+ }
+
+ repeated Branch branches = 2;
+ }
+
+ repeated Address addrs = 3;
+
+ enum BinaryType {
+ ELF_FILE = 0;
+ KERNEL = 1;
+ KERNEL_MODULE = 2;
+ }
+ BinaryType type = 4;
+
+ message KernelBinaryInfo {
+ // kernel_start_addr is used to convert kernel ip address to vaddr in vmlinux.
+ // If it is zero, the Address in KERNEL binary has been converted to vaddr. Otherwise,
+ // the Address in KERNEL binary is still ip address, and need to be converted later.
+ uint64 kernel_start_addr = 1;
+ }
+
+ KernelBinaryInfo kernel_info = 5;
+}
diff --git a/simpleperf/cmd_dumprecord.cpp b/simpleperf/cmd_dumprecord.cpp
index 0bbbb10b..a9c4635b 100644
--- a/simpleperf/cmd_dumprecord.cpp
+++ b/simpleperf/cmd_dumprecord.cpp
@@ -26,7 +26,7 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
-#include "ETMBranchListFile.h"
+#include "BranchListFile.h"
#include "ETMDecoder.h"
#include "command.h"
#include "dso.h"
@@ -214,7 +214,8 @@ class DumpRecordCommand : public Command {
bool ProcessRecord(Record* r);
void ProcessSampleRecord(const SampleRecord& r);
void ProcessCallChainRecord(const CallChainRecord& r);
- SymbolInfo GetSymbolInfo(uint32_t pid, uint32_t tid, uint64_t ip, bool in_kernel);
+ SymbolInfo GetSymbolInfo(uint32_t pid, uint32_t tid, uint64_t ip,
+ std::optional<bool> in_kernel = std::nullopt);
bool ProcessTracingData(const TracingDataRecord& r);
bool DumpAuxData(const AuxRecord& aux);
bool DumpFeatureSection();
@@ -397,6 +398,19 @@ void DumpRecordCommand::ProcessSampleRecord(const SampleRecord& sr) {
s.vaddr_in_file);
}
}
+ if (sr.sample_type & PERF_SAMPLE_BRANCH_STACK) {
+ PrintIndented(1, "branch_stack:\n");
+ for (size_t i = 0; i < sr.branch_stack_data.stack_nr; ++i) {
+ uint64_t from_ip = sr.branch_stack_data.stack[i].from;
+ uint64_t to_ip = sr.branch_stack_data.stack[i].to;
+ SymbolInfo from_symbol = GetSymbolInfo(sr.tid_data.pid, sr.tid_data.tid, from_ip);
+ SymbolInfo to_symbol = GetSymbolInfo(sr.tid_data.pid, sr.tid_data.tid, to_ip);
+ PrintIndented(2, "%s (%s[+%" PRIx64 "]) -> %s (%s[+%" PRIx64 "])\n",
+ from_symbol.symbol->DemangledName(), from_symbol.dso->Path().c_str(),
+ from_symbol.vaddr_in_file, to_symbol.symbol->DemangledName(),
+ to_symbol.dso->Path().c_str(), to_symbol.vaddr_in_file);
+ }
+ }
// Dump tracepoint fields.
if (!events_.empty()) {
size_t attr_index = record_file_reader_->GetAttrIndexOfRecord(&sr);
@@ -422,9 +436,14 @@ void DumpRecordCommand::ProcessCallChainRecord(const CallChainRecord& cr) {
}
SymbolInfo DumpRecordCommand::GetSymbolInfo(uint32_t pid, uint32_t tid, uint64_t ip,
- bool in_kernel) {
+ std::optional<bool> in_kernel) {
ThreadEntry* thread = thread_tree_.FindThreadOrNew(pid, tid);
- const MapEntry* map = thread_tree_.FindMap(thread, ip, in_kernel);
+ const MapEntry* map;
+ if (in_kernel.has_value()) {
+ map = thread_tree_.FindMap(thread, ip, in_kernel.value());
+ } else {
+ map = thread_tree_.FindMap(thread, ip);
+ }
SymbolInfo info;
info.symbol = thread_tree_.FindSymbol(map, ip, &info.vaddr_in_file, &info.dso);
return info;
@@ -550,8 +569,8 @@ bool DumpRecordCommand::DumpFeatureSection() {
if (!record_file_reader_->ReadFeatureSection(FEAT_ETM_BRANCH_LIST, &data)) {
return false;
}
- BranchListBinaryMap binary_map;
- if (!StringToBranchListBinaryMap(data, binary_map)) {
+ ETMBinaryMap binary_map;
+ if (!StringToETMBinaryMap(data, binary_map)) {
return false;
}
PrintIndented(1, "etm_branch_list:\n");
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);
}
diff --git a/simpleperf/cmd_inject_test.cpp b/simpleperf/cmd_inject_test.cpp
index 23347b90..3b25553b 100644
--- a/simpleperf/cmd_inject_test.cpp
+++ b/simpleperf/cmd_inject_test.cpp
@@ -224,3 +224,14 @@ TEST(cmd_inject, accept_missing_aux_data) {
close(tmpfile.release());
ASSERT_TRUE(RunInjectCmd({"--output", "branch-list", "-i", perf_data, "-o", tmpfile.path}));
}
+
+TEST(cmd_inject, read_lbr_data) {
+ std::string data;
+ ASSERT_TRUE(RunInjectCmd({"-i", GetTestData("lbr/perf_lbr.data")}, &data));
+ data.erase(std::remove(data.begin(), data.end(), '\r'), data.end());
+
+ std::string expected_data;
+ ASSERT_TRUE(android::base::ReadFileToString(
+ GetTestData(std::string("lbr") + OS_PATH_SEPARATOR + "inject_lbr.data"), &expected_data));
+ ASSERT_NE(data.find(expected_data), data.npos);
+}
diff --git a/simpleperf/cmd_record.cpp b/simpleperf/cmd_record.cpp
index ad1133ce..da1a31bf 100644
--- a/simpleperf/cmd_record.cpp
+++ b/simpleperf/cmd_record.cpp
@@ -47,8 +47,8 @@
#endif
#include <unwindstack/Error.h>
+#include "BranchListFile.h"
#include "CallChainJoiner.h"
-#include "ETMBranchListFile.h"
#include "ETMRecorder.h"
#include "IOEventLoop.h"
#include "JITDebugReader.h"
@@ -2214,9 +2214,9 @@ void RecordCommand::CollectHitFileInfo(const SampleRecord& r, std::unordered_set
}
bool RecordCommand::DumpETMBranchListFeature() {
- BranchListBinaryMap binary_map = etm_branch_list_generator_->GetBranchListBinaryMap();
+ ETMBinaryMap binary_map = etm_branch_list_generator_->GetETMBinaryMap();
std::string s;
- if (!BranchListBinaryMapToString(binary_map, s)) {
+ if (!ETMBinaryMapToString(binary_map, s)) {
return false;
}
return record_file_writer_->WriteFeature(PerfFileFormat::FEAT_ETM_BRANCH_LIST, s.data(),
diff --git a/simpleperf/etm_branch_list.proto b/simpleperf/etm_branch_list.proto
deleted file mode 100644
index c66b0d5e..00000000
--- a/simpleperf/etm_branch_list.proto
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-syntax = "proto3";
-
-package simpleperf.proto;
-
-message ETMBranchList {
-
- message Binary {
- string path = 1;
- string build_id = 2;
-
- message Address {
- // vaddr in binary, instr addr before the first branch
- uint64 addr = 1;
-
- message Branch {
- // Each bit represents a branch: 0 for not branch, 1 for branch.
- // Bit 0 comes first, bit 7 comes last.
- bytes branch = 1;
- uint32 branch_size = 2;
- uint64 count = 3;
- }
-
- repeated Branch branches = 2;
- }
-
- repeated Address addrs = 3;
-
- enum BinaryType {
- ELF_FILE = 0;
- KERNEL = 1;
- KERNEL_MODULE = 2;
- }
- BinaryType type = 4;
-
- message KernelBinaryInfo {
- // kernel_start_addr is used to convert kernel ip address to vaddr in vmlinux.
- // If it is zero, the Address in KERNEL binary has been converted to vaddr. Otherwise,
- // the Address in KERNEL binary is still ip address, and need to be converted later.
- uint64 kernel_start_addr = 1;
- }
-
- KernelBinaryInfo kernel_info = 5;
-
- }
-
- // Used to identify format in generated proto files.
- // Should always be "simpleperf:EtmBranchList".
- string magic = 1;
- repeated Binary binaries = 2;
-}
diff --git a/simpleperf/record_file.h b/simpleperf/record_file.h
index fa696269..76d982d3 100644
--- a/simpleperf/record_file.h
+++ b/simpleperf/record_file.h
@@ -139,6 +139,7 @@ class RecordFileReader {
~RecordFileReader();
+ const std::string FileName() const { return filename_; }
const PerfFileFormat::FileHeader& FileHeader() const { return header_; }
const EventAttrIds& AttrSection() const { return event_attrs_; }
diff --git a/simpleperf/testdata/lbr/inject_lbr.data b/simpleperf/testdata/lbr/inject_lbr.data
new file mode 100644
index 00000000..ffc9b5b5
--- /dev/null
+++ b/simpleperf/testdata/lbr/inject_lbr.data
@@ -0,0 +1,11 @@
+2
+1910-191b:31
+1940-194d:341
+4
+1914:1
+1940:3
+1944:4
+1948:11
+2
+191b->1910:32
+194d->1940:353
diff --git a/simpleperf/testdata/lbr/perf_lbr.data b/simpleperf/testdata/lbr/perf_lbr.data
new file mode 100644
index 00000000..81fafe62
--- /dev/null
+++ b/simpleperf/testdata/lbr/perf_lbr.data
Binary files differ