Skip to content

Commit

Permalink
Add a library for aggregating branch frequencies from perf data
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 700388882
  • Loading branch information
dhoekwater authored and copybara-github committed Nov 26, 2024
1 parent 592c6d8 commit c23b031
Show file tree
Hide file tree
Showing 5 changed files with 215 additions and 0 deletions.
43 changes: 43 additions & 0 deletions propeller/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -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 #
########################
Expand Down Expand Up @@ -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",
],
)
2 changes: 2 additions & 0 deletions propeller/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
58 changes: 58 additions & 0 deletions propeller/perf_branch_frequencies_aggregator.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#include "propeller/perf_branch_frequencies_aggregator.h"

#include <optional>
#include <string>
#include <utility>

#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<BranchFrequencies>
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<PerfDataProvider::BufferHandle> 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<PerfDataReader> 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
48 changes: 48 additions & 0 deletions propeller/perf_branch_frequencies_aggregator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#ifndef PROPELLER_PERF_BRANCH_FREQUENCIES_AGGREGATOR_H_
#define PROPELLER_PERF_BRANCH_FREQUENCIES_AGGREGATOR_H_

#include <memory>
#include <utility>

#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<PerfDataProvider> 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<BranchFrequencies> AggregateBranchFrequencies(
const PropellerOptions& options, const BinaryContent& binary_content,
PropellerStats& stats) override;

private:
std::unique_ptr<PerfDataProvider> perf_data_provider_;
};

} // namespace propeller

#endif // PROPELLER_PERF_BRANCH_FREQUENCIES_AGGREGATOR_H_
64 changes: 64 additions & 0 deletions propeller/perf_branch_frequencies_aggregator_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#include "propeller/perf_branch_frequencies_aggregator.h"

#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <utility>
#include <vector>

#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<std::optional<PerfDataProvider::BufferHandle>>,
GetNext, ());
MOCK_METHOD(absl::StatusOr<std::vector<PerfDataProvider::BufferHandle>>,
GetAllAvailableOrNext, ());
};

static constexpr std::string_view kTestDataDir =
"propeller/testdata/";

TEST(PerfBranchFrequenciesAggregator, FailsIfNoPerfData) {
auto mock_perf_data_provider = std::make_unique<MockPerfDataProvider>();

// 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

0 comments on commit c23b031

Please sign in to comment.