Implement the facade that selects an offline perf reader and exposes
normalized sample mass to the profiler, separating input selection
from DWARF correlation and profile construction.
afdo_tools/ChangeLog:
* src/profile_reader.cc: New file.
Signed-off-by: Kugan Vivekanandarajah <[email protected]>
Co-authored-by: Dhruv Chawla <[email protected]>
Co-authored-by: Rahul Shinde <[email protected]>
---
afdo_tools/src/profile_reader.cc | 230 +++++++++++++++++++++++++++++++
1 file changed, 230 insertions(+)
create mode 100644 afdo_tools/src/profile_reader.cc
diff --git a/afdo_tools/src/profile_reader.cc b/afdo_tools/src/profile_reader.cc
new file mode 100644
index 00000000000..9e1ec06e811
--- /dev/null
+++ b/afdo_tools/src/profile_reader.cc
@@ -0,0 +1,230 @@
+/* Copyright The GNU Toolchain Authors
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#include "profile_reader.h"
+
+#include "branch_stack_reader_base.h"
+#include "dwarf_reader.h"
+#include "perf_branch_stack_reader.h"
+#include "perf_file_reader.h"
+#include "perf_spe_reader.h"
+
+#include <algorithm>
+#include <cstdio>
+#include <memory>
+#include <utility>
+
+namespace perf_data {
+
+/* Drop everything derived from a previous load, so it can be reused. */
+void
+profile_reader::reset_profile_state ()
+{
+ sorted_touch_pcs_.clear ();
+ first_sample_time_by_pc_.clear ();
+ function_timestamps_.clear ();
+}
+
+/* Remember the earliest TIME seen at PC, a DWARF lookup address. */
+void
+profile_reader::note_sample_timestamp (uint64_t pc, uint64_t time)
+{
+ if (time == 0 || pc == 0)
+ return;
+ auto inserted = first_sample_time_by_pc_.emplace (pc, time);
+ if (!inserted.second && time < inserted.first->second)
+ inserted.first->second = time;
+}
+
+/* Reduce the per-PC first-sample times to one earliest time per function,
+ which is what the profile records. */
+void
+profile_reader::fold_sample_timestamps_by_function (
+ const dwarf_debug::dwarf_reader &dwarf)
+{
+ if (first_sample_time_by_pc_.empty ())
+ {
+ function_timestamps_.clear ();
+ return;
+ }
+
+ function_timestamp_map folded;
+ std::unordered_map<dwarf_debug::function_instance_key,
+ uint64_t *,
+ dwarf_debug::function_instance_key_hash>
+ folded_cache;
+ for (const auto &entry : first_sample_time_by_pc_)
+ {
+ const uint64_t dwarf_addr = entry.first;
+ const uint64_t time = entry.second;
+ const dwarf_debug::function_metadata *meta
+ = dwarf.containing_function_at_pc (dwarf_addr);
+ if (meta == nullptr || dwarf_debug::string_empty (meta->name))
+ continue;
+
+ const dwarf_debug::function_instance_key view_key{
+ meta->name, meta->start_addr};
+ auto cached = folded_cache.find (view_key);
+ uint64_t *first_time;
+ if (cached != folded_cache.end ())
+ first_time = cached->second;
+ else
+ {
+ auto inserted = folded.emplace (
+ std::make_pair (
+ dwarf_debug::string_value (meta->name), meta->start_addr),
+ time);
+ first_time = &inserted.first->second;
+ folded_cache.emplace (
+ dwarf_debug::function_instance_key{meta->name, meta->start_addr},
+ first_time);
+ }
+ if (time < *first_time)
+ *first_time = time;
+ }
+
+ first_sample_time_by_pc_.clear ();
+ first_sample_time_by_pc_.rehash (0);
+ function_timestamps_.swap (folded);
+}
+
+/* Branch-stack range mass, or null when this reader has none to give. A
+ per-PC run has none: its mass is the sampled IPs, not the branch stack. */
+const std::vector<profile_range> *
+profile_reader::range_counts () const
+{
+ const branch_stack_reader_base *bs = as_branch_stack_reader ();
+ if (bs == nullptr || !bs->has_finalized_profile ()
+ || !use_branch_stack_for_correlation_)
+ return nullptr;
+ const std::vector<profile_range> &ranges = bs->sorted_ranges ();
+ return ranges.empty () ? nullptr : &ranges;
+}
+
+/* Branch-stack edge counts, or null when this reader has none to give. */
+const std::vector<profile_branch> *
+profile_reader::branch_counts () const
+{
+ const branch_stack_reader_base *bs = as_branch_stack_reader ();
+ if (bs == nullptr || !bs->has_finalized_profile ()
+ || !use_branch_stack_for_correlation_)
+ return nullptr;
+ const std::vector<profile_branch> &branches = bs->sorted_branches ();
+ return branches.empty () ? nullptr : &branches;
+}
+
+/* True when the profile carries usable mass in any form. */
+bool
+profile_reader::has_sample_mass () const
+{
+ if (range_counts () != nullptr || branch_counts () != nullptr)
+ return true;
+ return !get_address_frequencies ().empty ();
+}
+
+/* Read the profile and get it ready for correlation. USE_BRANCH_STACK picks
+ range and edge mass over per-sample-IP mass. */
+bool
+profile_reader::load_and_prepare (bool use_branch_stack)
+{
+ use_branch_stack_for_correlation_ = use_branch_stack;
+ if (branch_stack_reader_base *bs = as_branch_stack_reader ())
+ {
+ /* Branch-stack correlate uses ranges/edges, not sample-IP mass. */
+ if (use_branch_stack)
+ bs->disable_sample_ip_accumulation ();
+ }
+
+ if (!load ())
+ {
+ fprintf (stderr, _("Error: Failed to load profile data\n"));
+ return false;
+ }
+
+ if (use_branch_stack && range_counts () == nullptr)
+ fprintf (stderr,
+ _("Warning: branch-stack mode is enabled but the range "
+ "profile is empty\n"));
+
+ if (!has_sample_mass ())
+ {
+ fprintf (stderr,
+ _("Error: profile has no sample mass (ranges, branches, "
+ "or per-PC counts)\n"));
+ return false;
+ }
+ return true;
+}
+
+/* The address ranges the profile touched, so DWARF loading can skip CUs no
+ sample fell in. */
+dwarf_debug::pc_range_list
+profile_reader::sample_ranges_for_dwarf () const
+{
+ dwarf_debug::pc_range_list ranges;
+ if (const auto *profile_ranges = range_counts ())
+ {
+ ranges.reserve (profile_ranges->size ());
+ for (const profile_range &row : *profile_ranges)
+ if (row.end >= row.beg)
+ {
+ if (row.end != UINT64_MAX)
+ ranges.emplace_back (row.beg, row.end + 1);
+ else if (row.beg != UINT64_MAX)
+ ranges.emplace_back (row.beg, UINT64_MAX);
+ }
+ }
+ if (const auto *profile_branches = branch_counts ())
+ {
+ ranges.reserve (ranges.size () + profile_branches->size ());
+ for (const profile_branch &row : *profile_branches)
+ if (row.from != UINT64_MAX)
+ ranges.emplace_back (row.from, row.from + 1);
+ }
+ /* A per-PC run has no ranges; scope DWARF to the sampled IPs instead. */
+ if (ranges.empty ())
+ {
+ const auto &counts = get_address_frequencies ();
+ ranges.reserve (counts.size ());
+ for (const auto &entry : counts)
+ if (entry.first != UINT64_MAX)
+ ranges.emplace_back (entry.first, entry.first + 1);
+ }
+ return ranges;
+}
+
+std::unique_ptr<profile_reader>
+create_perf_reader (const std::string &perf_data_path,
+ const std::string &binary_path, sampling_mode mode)
+{
+ switch (mode)
+ {
+ case sampling_mode::BRANCH_STACK:
+ return std::make_unique<perf_branch_stack_reader> (
+ perf_data_path, binary_path);
+
+ case sampling_mode::SPE:
+ return std::make_unique<perf_spe_reader> (perf_data_path, binary_path);
+
+ default:
+ fprintf (stderr, _("Error: Unknown sampling mode\n"));
+ return nullptr;
+ }
+}
+
+} /* namespace perf_data. */
--
2.34.1