summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYabin Cui <yabinc@google.com>2019-09-18 16:05:51 -0700
committerYabin Cui <yabinc@google.com>2019-09-19 13:04:55 -0700
commitfad7bbea296a376139bddf8d3991a41b9d763409 (patch)
treefe4c4e85af51ea3445c505e29a32342be5325d46
parent939a46d25c6e64f2acb9790c942a2c026557f912 (diff)
downloadextras-fad7bbea296a376139bddf8d3991a41b9d763409.tar.gz
simpleperf: output text format for autofdo in inject cmd.
Also add --binary option to selected binaries for generating data. Because AutoFDO only accepts one binary input. Test it manually with a simple example as below: 1. on device, run `./simpleperf record -e cs-etm:u ./etm_test_multi_thread`. 2. on device, run `./simpleperf inject --binary test_loop_b`. 3. on host, run `adb pull /data/local/tmp/perf_inject.data`. 4. on host, run `./create_llvm_prof -binary=libetm_test_loop_b.so -profile=perf_inject.data -profiler=text -out=a`. And create_llvm_prof can generate output successfully. Bug: 141013987 Test: run simpleperf_unit_test. Change-Id: Iacc0e3ed4cc47c91d5764e7b0394718436211514
-rw-r--r--simpleperf/cmd_inject.cpp80
-rw-r--r--simpleperf/cmd_inject_test.cpp18
-rw-r--r--simpleperf/utils.h10
3 files changed, 101 insertions, 7 deletions
diff --git a/simpleperf/cmd_inject.cpp b/simpleperf/cmd_inject.cpp
index e14e32f0..9cf40b93 100644
--- a/simpleperf/cmd_inject.cpp
+++ b/simpleperf/cmd_inject.cpp
@@ -23,20 +23,39 @@
#include "command.h"
#include "record_file.h"
#include "thread_tree.h"
+#include "utils.h"
using namespace simpleperf;
namespace {
+using AddrPair = std::pair<uint64_t, uint64_t>;
+
+struct AddrPairHash {
+ size_t operator()(const AddrPair& ap) const noexcept {
+ size_t seed = 0;
+ HashCombine(seed, ap.first);
+ HashCombine(seed, ap.second);
+ return seed;
+ }
+};
+
+struct BinaryInfo {
+ std::unordered_map<AddrPair, uint64_t, AddrPairHash> range_count_map;
+ std::unordered_map<AddrPair, uint64_t, AddrPairHash> branch_count_map;
+};
+
class InjectCommand : public Command {
public:
InjectCommand()
: Command("inject", "convert etm instruction tracing data into instr ranges",
// clang-format off
"Usage: simpleperf inject [options]\n"
+"--binary binary_name Generate data only for binaries containing binary_name.\n"
"-i <file> input perf.data, generated by recording cs-etm event type.\n"
" Default is perf.data.\n"
"-o <file> output file. Default is perf_inject.data.\n"
+" The output is in text format accepted by AutoFDO.\n"
"--dump-etm type1,type2,... Dump etm data. A type is one of raw, packet and element.\n"
"--symdir <dir> Look for binaries in a directory recursively.\n"
// clang-format on
@@ -60,6 +79,7 @@ class InjectCommand : public Command {
if (!record_file_reader_->ReadDataSection([this](auto r) { return ProcessRecord(r.get()); })) {
return false;
}
+ PostProcess();
output_fp_.reset(nullptr);
return true;
}
@@ -67,7 +87,12 @@ class InjectCommand : public Command {
private:
bool ParseOptions(const std::vector<std::string>& args) {
for (size_t i = 0; i < args.size(); i++) {
- if (args[i] == "-i") {
+ if (args[i] == "--binary") {
+ if (!NextArgumentOrError(args, &i)) {
+ return false;
+ }
+ binary_name_filter_ = args[i];
+ } else if (args[i] == "-i") {
if (!NextArgumentOrError(args, &i)) {
return false;
}
@@ -122,14 +147,52 @@ class InjectCommand : public Command {
}
void ProcessInstrRange(const ETMInstrRange& instr_range) {
- fprintf(output_fp_.get(),
- "dso %s, [0x%" PRIx64 "-0x%" PRIx64 "], branch_to 0x%" PRIx64 ", taken %" PRIu64
- ", not_taken %" PRIu64 "\n",
- instr_range.dso->GetDebugFilePath().c_str(), instr_range.start_addr,
- instr_range.end_addr, instr_range.branch_to_addr, instr_range.branch_taken_count,
- instr_range.branch_not_taken_count);
+ if (instr_range.dso->GetDebugFilePath().find(binary_name_filter_) == std::string::npos) {
+ return;
+ }
+ auto& binary = binary_map_[instr_range.dso->GetDebugFilePath()];
+ binary.range_count_map[AddrPair(instr_range.start_addr, instr_range.end_addr)] +=
+ instr_range.branch_taken_count + instr_range.branch_not_taken_count;
+ if (instr_range.branch_taken_count > 0) {
+ binary.branch_count_map[AddrPair(instr_range.end_addr, instr_range.branch_to_addr)] +=
+ instr_range.branch_taken_count;
+ }
}
+ void PostProcess() {
+ for (const auto& pair : binary_map_) {
+ const std::string& binary_path = pair.first;
+ const BinaryInfo& binary = pair.second;
+
+ // Write range_count_map.
+ fprintf(output_fp_.get(), "%zu\n", binary.range_count_map.size());
+ for (const auto& pair2 : binary.range_count_map) {
+ const AddrPair& addr_range = pair2.first;
+ uint64_t count = pair2.second;
+
+ fprintf(output_fp_.get(), "%" PRIx64 "-%" PRIx64 ":%" PRIu64 "\n", addr_range.first,
+ addr_range.second, count);
+ }
+
+ // Write addr_count_map.
+ fprintf(output_fp_.get(), "0\n");
+
+ // Write branch_count_map.
+ fprintf(output_fp_.get(), "%zu\n", binary.branch_count_map.size());
+ for (const auto& pair2 : binary.branch_count_map) {
+ const AddrPair& branch = pair2.first;
+ uint64_t count = pair2.second;
+
+ fprintf(output_fp_.get(), "%" PRIx64 "->%" PRIx64 ":%" PRIu64 "\n", branch.first,
+ branch.second, count);
+ }
+
+ // Write the binary path in comment.
+ fprintf(output_fp_.get(), "// %s\n\n", binary_path.c_str());
+ }
+ }
+
+ std::string binary_name_filter_;
std::string input_filename_ = "perf.data";
std::string output_filename_ = "perf_inject.data";
ThreadTree thread_tree_;
@@ -138,6 +201,9 @@ class InjectCommand : public Command {
std::unique_ptr<ETMDecoder> etm_decoder_;
std::vector<uint8_t> aux_data_buffer_;
std::unique_ptr<FILE, decltype(&fclose)> output_fp_;
+
+ // Store results for AutoFDO.
+ std::unordered_map<std::string, BinaryInfo> binary_map_;
};
} // namespace
diff --git a/simpleperf/cmd_inject_test.cpp b/simpleperf/cmd_inject_test.cpp
index 473f04f7..3401360d 100644
--- a/simpleperf/cmd_inject_test.cpp
+++ b/simpleperf/cmd_inject_test.cpp
@@ -32,3 +32,21 @@ TEST(cmd_inject, smoke) {
// Test that we can find instr range in etm_test_loop binary.
ASSERT_NE(data.find("etm_test_loop"), std::string::npos);
}
+
+TEST(cmd_inject, binary_option) {
+ // Test that data for etm_test_loop is generated when selected by --binary.
+ TemporaryFile tmpfile;
+ ASSERT_TRUE(InjectCmd()->Run({"--symdir", GetTestDataDir() + "etm", "-i",
+ GetTestData(PERF_DATA_ETM_TEST_LOOP), "--binary", "etm_test_loop",
+ "-o", tmpfile.path}));
+ std::string data;
+ ASSERT_TRUE(android::base::ReadFileToString(tmpfile.path, &data));
+ ASSERT_NE(data.find("etm_test_loop"), std::string::npos);
+
+ // Test that data for etm_test_loop isn't generated when not selected by --binary.
+ ASSERT_TRUE(InjectCmd()->Run({"--symdir", GetTestDataDir() + "etm", "-i",
+ GetTestData(PERF_DATA_ETM_TEST_LOOP), "--binary",
+ "no_etm_test_loop", "-o", tmpfile.path}));
+ ASSERT_TRUE(android::base::ReadFileToString(tmpfile.path, &data));
+ ASSERT_EQ(data.find("etm_test_loop"), std::string::npos);
+}
diff --git a/simpleperf/utils.h b/simpleperf/utils.h
index 3ca8f5cf..872e191d 100644
--- a/simpleperf/utils.h
+++ b/simpleperf/utils.h
@@ -163,4 +163,14 @@ timeval SecondToTimeval(double time_in_sec);
std::string GetSimpleperfVersion();
+namespace {
+
+// from boost::hash_combine
+template <typename T>
+void HashCombine(size_t& seed, const T& val) {
+ seed ^= std::hash<T>()(val) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
+}
+
+} // namespace
+
#endif // SIMPLE_PERF_UTILS_H_