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

Reply via email to