diff --git a/propeller/BUILD b/propeller/BUILD index 08d052b1bd3..870f8d23c5d 100644 --- a/propeller/BUILD +++ b/propeller/BUILD @@ -595,6 +595,26 @@ cc_library( ], ) +cc_library( + name = "perf_branch_frequencies_aggregator", + srcs = ["perf_branch_frequencies_aggregator.cc"], + hdrs = ["perf_branch_frequencies_aggregator.h"], + deps = [ + ":binary_content", + ":branch_frequencies", + ":branch_frequencies_aggregator", + ":perf_data_provider", + ":perfdata_reader", + ":propeller_options_cc_proto", + ":propeller_statistics", + ":resolve_mmap_name", + ":status_macros", + "@abseil-cpp//absl/log", + "@abseil-cpp//absl/status", + "@abseil-cpp//absl/status:statusor", + ], +) + ######################## # Tests & Test Utils # ######################## @@ -1048,3 +1068,26 @@ cc_test( "@com_google_googletest//:gtest_main", ], ) + +cc_test( + name = "perf_branch_frequencies_aggregator_test", + srcs = ["perf_branch_frequencies_aggregator_test.cc"], + data = [ + "//propeller/testdata:sample.arm.bin", + ], + deps = [ + ":binary_content", + ":branch_frequencies", + ":file_perf_data_provider", + ":perf_branch_frequencies_aggregator", + ":perf_data_provider", + ":propeller_options_cc_proto", + ":propeller_statistics", + ":status_testing_macros", + "@abseil-cpp//absl/status", + "@abseil-cpp//absl/status:status_matchers", + "@abseil-cpp//absl/status:statusor", + "@abseil-cpp//absl/strings", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/propeller/CMakeLists.txt b/propeller/CMakeLists.txt index 796ba4dfa29..f211ee94936 100644 --- a/propeller/CMakeLists.txt +++ b/propeller/CMakeLists.txt @@ -43,6 +43,7 @@ add_library(propeller_lib OBJECT node_chain_builder.cc node_chain.cc perfdata_reader.cc + perf_branch_frequencies_aggregator.cc program_cfg_builder.cc program_cfg.cc propeller_statistics.cc @@ -83,6 +84,7 @@ propeller_generate_tests( lazy_evaluator_test.cc lbr_branch_aggregator_test.cc perfdata_reader_test.cc + perf_branch_frequencies_aggregator_test.cc propeller_statistics_test.cc proto_branch_frequencies_aggregator_test.cc spe_tid_pid_provider_test.cc diff --git a/propeller/perf_branch_frequencies_aggregator.cc b/propeller/perf_branch_frequencies_aggregator.cc new file mode 100644 index 00000000000..533b7c053fe --- /dev/null +++ b/propeller/perf_branch_frequencies_aggregator.cc @@ -0,0 +1,58 @@ +#include "propeller/perf_branch_frequencies_aggregator.h" + +#include +#include +#include + +#include "absl/log/log.h" +#include "absl/status/status.h" +#include "absl/status/statusor.h" +#include "propeller/binary_content.h" +#include "propeller/branch_frequencies.h" +#include "propeller/perf_data_provider.h" +#include "propeller/perfdata_reader.h" +#include "propeller/propeller_options.pb.h" +#include "propeller/propeller_statistics.h" +#include "propeller/resolve_mmap_name.h" +#include "propeller/status_macros.h" + +namespace propeller { + +absl::StatusOr +PerfBranchFrequenciesAggregator::AggregateBranchFrequencies( + const PropellerOptions &options, const BinaryContent &binary_content, + PropellerStats &stats) { + PropellerStats::ProfileStats &profile_stats = stats.profile_stats; + BranchFrequencies frequencies; + + while (true) { + ASSIGN_OR_RETURN(std::optional perf_data, + perf_data_provider_->GetNext()); + if (!perf_data.has_value()) break; + + const std::string description = perf_data->description; + LOG(INFO) << "Parsing " << description << " ..."; + absl::StatusOr perf_data_reader = BuildPerfDataReader( + std::move(*perf_data), &binary_content, ResolveMmapName(options)); + if (!perf_data_reader.ok()) { + LOG(WARNING) << "Skipped profile " << description << ": " + << perf_data_reader.status(); + continue; + } + + profile_stats.binary_mmap_num += perf_data_reader->binary_mmaps().size(); + ++profile_stats.perf_file_parsed; + RETURN_IF_ERROR(perf_data_reader->AggregateSpe(frequencies)); + } + profile_stats.br_counters_accumulated += + frequencies.GetNumberOfTakenBranchCounters(); + if (profile_stats.br_counters_accumulated <= 100) + LOG(WARNING) << "Too few branch records in perf data."; + if (profile_stats.perf_file_parsed == 0) { + return absl::FailedPreconditionError( + "No perf file is parsed, cannot proceed."); + } + return frequencies; +} + +} // namespace propeller diff --git a/propeller/perf_branch_frequencies_aggregator.h b/propeller/perf_branch_frequencies_aggregator.h new file mode 100644 index 00000000000..c51b4d7b038 --- /dev/null +++ b/propeller/perf_branch_frequencies_aggregator.h @@ -0,0 +1,48 @@ +#ifndef PROPELLER_PERF_BRANCH_FREQUENCIES_AGGREGATOR_H_ +#define PROPELLER_PERF_BRANCH_FREQUENCIES_AGGREGATOR_H_ + +#include +#include + +#include "absl/status/statusor.h" +#include "propeller/binary_content.h" +#include "propeller/branch_frequencies.h" +#include "propeller/branch_frequencies_aggregator.h" +#include "propeller/perf_data_provider.h" +#include "propeller/propeller_options.pb.h" +#include "propeller/propeller_statistics.h" + +namespace propeller { +// `PerfBranchFrequenciesAggregator` is an implementation of +// `BranchFrequenciesAggregator` that builds `BranchFrequencies` from perf data +// containing SPE entries. The perf data can come from any `PerfDataProvider`, +// such as from a file or mock. +class PerfBranchFrequenciesAggregator : public BranchFrequenciesAggregator { + public: + explicit PerfBranchFrequenciesAggregator( + std::unique_ptr perf_data_provider) + : perf_data_provider_(std::move(perf_data_provider)) {} + + // PerfBranchFrequenciesAggregator is move-only. + PerfBranchFrequenciesAggregator(PerfBranchFrequenciesAggregator&&) = default; + PerfBranchFrequenciesAggregator& operator=( + PerfBranchFrequenciesAggregator&&) = default; + PerfBranchFrequenciesAggregator(const PerfBranchFrequenciesAggregator&) = + delete; + PerfBranchFrequenciesAggregator& operator=( + const PerfBranchFrequenciesAggregator&) = delete; + + // Aggregates branch frequencies from perf data, may return an `absl::Status` + // if the perf data can't be successfully parsed and aggregated (it doesn't + // exist, is malformed, etc.). + absl::StatusOr AggregateBranchFrequencies( + const PropellerOptions& options, const BinaryContent& binary_content, + PropellerStats& stats) override; + + private: + std::unique_ptr perf_data_provider_; +}; + +} // namespace propeller + +#endif // PROPELLER_PERF_BRANCH_FREQUENCIES_AGGREGATOR_H_ diff --git a/propeller/perf_branch_frequencies_aggregator_test.cc b/propeller/perf_branch_frequencies_aggregator_test.cc new file mode 100644 index 00000000000..6834c68b011 --- /dev/null +++ b/propeller/perf_branch_frequencies_aggregator_test.cc @@ -0,0 +1,64 @@ +#include "propeller/perf_branch_frequencies_aggregator.h" + +#include +#include +#include +#include +#include +#include + +#include "propeller/status_testing_macros.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/status/status.h" +#include "absl/status/statusor.h" +#include "absl/status/status_matchers.h" +#include "absl/strings/str_cat.h" +#include "propeller/binary_content.h" +#include "propeller/branch_frequencies.h" +#include "propeller/file_perf_data_provider.h" +#include "propeller/perf_data_provider.h" +#include "propeller/propeller_options.pb.h" +#include "propeller/propeller_statistics.h" + +namespace propeller { +namespace { +using ::testing::AllOf; +using ::testing::Field; +using ::testing::SizeIs; +using ::absl_testing::IsOkAndHolds; +using ::absl_testing::StatusIs; + +class MockPerfDataProvider : public PerfDataProvider { + public: + MOCK_METHOD(absl::StatusOr>, + GetNext, ()); + MOCK_METHOD(absl::StatusOr>, + GetAllAvailableOrNext, ()); +}; + +static constexpr std::string_view kTestDataDir = + "propeller/testdata/"; + +TEST(PerfBranchFrequenciesAggregator, FailsIfNoPerfData) { + auto mock_perf_data_provider = std::make_unique(); + + // PerfDataProvider::BufferHandle is not copyable, so we can't use + // `testing::Return` here. + EXPECT_CALL(*mock_perf_data_provider, GetNext()).WillRepeatedly([]() { + return absl::InvalidArgumentError("No perf data"); + }); + EXPECT_CALL(*mock_perf_data_provider, GetAllAvailableOrNext()) + .WillRepeatedly( + []() { return absl::InvalidArgumentError("No perf data"); }); + + PropellerStats stats; + EXPECT_THAT( + PerfBranchFrequenciesAggregator(std::move(mock_perf_data_provider)) + .AggregateBranchFrequencies(PropellerOptions(), BinaryContent(), + stats), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +} // namespace +} // namespace propeller