Add optional dumps of ingested perf maps and resolved address-to-source
mappings.  These diagnostics are isolated from the correlation algorithm so
they do not affect normal profile output.

afdo_tools/ChangeLog:

        * src/autofdo_debug_dump.cc: New file.
        * src/autofdo_debug_dump.h: 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/autofdo_debug_dump.cc | 249 +++++++++++++++++++++++++++
 afdo_tools/src/autofdo_debug_dump.h  |  41 +++++
 2 files changed, 290 insertions(+)
 create mode 100644 afdo_tools/src/autofdo_debug_dump.cc
 create mode 100644 afdo_tools/src/autofdo_debug_dump.h

diff --git a/afdo_tools/src/autofdo_debug_dump.cc 
b/afdo_tools/src/autofdo_debug_dump.cc
new file mode 100644
index 00000000000..ad2edf2457b
--- /dev/null
+++ b/afdo_tools/src/autofdo_debug_dump.cc
@@ -0,0 +1,249 @@
+/* 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 "autofdo_debug_dump.h"
+
+#include "dwarf_reader.h"
+#include "profile_reader.h"
+
+#include <algorithm>
+#include <cinttypes>
+#include <cstdio>
+#include <vector>
+
+namespace autofdo {
+namespace {
+
+struct count_row
+{
+  uint64_t a = 0;
+  uint64_t b = 0;
+  uint64_t count = 0;
+};
+
+bool
+count_descending (const count_row &left, const count_row &right)
+{
+  return left.count > right.count;
+}
+
+void
+print_address (FILE *out, const count_row &row)
+{
+  fprintf (out, "  0x%" PRIx64 " count=%" PRIu64 "\n", row.a, row.count);
+}
+
+void
+print_range (FILE *out, const count_row &row)
+{
+  const uint64_t span = row.b >= row.a ? row.b - row.a + 1 : 0;
+  fprintf (out,
+          "  0x%" PRIx64 "-0x%" PRIx64 " (span=%" PRIu64 ") count=%" PRIu64
+          "\n",
+          row.a,
+          row.b,
+          span,
+          row.count);
+}
+
+void
+print_branch (FILE *out, const count_row &row)
+{
+  fprintf (out,
+          "  0x%" PRIx64 "->0x%" PRIx64 " count=%" PRIu64 "\n",
+          row.a,
+          row.b,
+          row.count);
+}
+
+/* Print TITLE then every row in ROWS through PRINT_ROW.  */
+void
+dump_rows (FILE *out, const char *title, const std::vector<count_row> &rows,
+          void (*print_row) (FILE *, const count_row &))
+{
+  fprintf (out, "%s", title);
+  for (const count_row &row : rows)
+    print_row (out, row);
+}
+
+/* Write LOC as file:line:discriminator, or ??:? when it is unknown, followed
+   by one tab-separated caller entry per frame of INLINE_STACK.  */
+void
+format_source_location (
+  FILE *out, const dwarf_debug::source_location &loc,
+  const std::vector<dwarf_debug::inline_frame> *inline_stack)
+{
+  if (dwarf_debug::string_empty (loc.file_path) && loc.line_number == 0)
+    fprintf (out, "??:?");
+  else
+    {
+      const std::string &file = dwarf_debug::string_value (loc.file_path);
+      fprintf (out,
+              "%.*s:%u:%u",
+              static_cast<int> (file.size ()),
+              file.data (),
+              static_cast<unsigned> (loc.line_number),
+              static_cast<unsigned> (loc.discriminator));
+    }
+  if (inline_stack == nullptr)
+    return;
+  for (const dwarf_debug::inline_frame &frame : *inline_stack)
+    {
+      const std::string &name = dwarf_debug::string_value 
(frame.function_name);
+      fprintf (out,
+              "\t%.*s:%u",
+              static_cast<int> (name.size ()),
+              name.data (),
+              static_cast<unsigned> (frame.call_line));
+      if (frame.discriminator != 0)
+       fprintf (out, ".%u", static_cast<unsigned> (frame.discriminator));
+    }
+}
+
+/* True when two symbolizations agree on function, locations and, once they
+   have a location at all, inline stack.  */
+bool
+symbolize_entries_equal (const dwarf_debug::correlate_symbolize_entry &a,
+                        const dwarf_debug::correlate_symbolize_entry &b)
+{
+  if (a.function_name != b.function_name
+      || !dwarf_debug::source_locations_equal (a.locations, b.locations))
+    return false;
+  if (a.locations.empty ())
+    return true;
+  return dwarf_debug::inline_stacks_equal (
+    dwarf_debug::inline_stack_or_empty (a.inline_stack),
+    dwarf_debug::inline_stack_or_empty (b.inline_stack));
+}
+
+} /* anonymous namespace.  */
+
+void
+dump_ingested_perf_maps (const perf_data::profile_reader &source,
+                        const std::string &path)
+{
+  FILE *out = fopen (path.c_str (), "w");
+  if (out == nullptr)
+    {
+      fprintf (stderr, _("Error: Cannot open perf maps dump: %s\n"),
+              path.c_str ());
+      return;
+    }
+
+  std::vector<count_row> addresses;
+  const auto &address_frequencies = source.get_address_frequencies ();
+  addresses.reserve (address_frequencies.size ());
+  for (const auto &entry : address_frequencies)
+    addresses.push_back ({entry.first, 0, entry.second});
+  std::sort (addresses.begin (), addresses.end (), count_descending);
+  dump_rows (
+    out, "All addresses (file offset, count):\n", addresses, print_address);
+
+  std::vector<count_row> ranges;
+  if (const auto *profile_ranges = source.range_counts ())
+    {
+      ranges.reserve (profile_ranges->size ());
+      for (const perf_data::profile_range &row : *profile_ranges)
+       ranges.push_back ({row.beg, row.end, row.count});
+    }
+  std::sort (ranges.begin (), ranges.end (), count_descending);
+  dump_rows (out, "\nAll ranges (begin-end, count):\n", ranges, print_range);
+
+  std::vector<count_row> branches;
+  if (const auto *profile_branches = source.branch_counts ())
+    {
+      branches.reserve (profile_branches->size ());
+      for (const perf_data::profile_branch &row : *profile_branches)
+       branches.push_back ({row.from, row.to, row.count});
+    }
+  std::sort (branches.begin (), branches.end (), count_descending);
+  dump_rows (
+    out, "\nAll branches (from->to, count):\n", branches, print_branch);
+
+  fclose (out);
+  fprintf (stderr, _("Wrote perf ingest maps to %s\n"), path.c_str ());
+}
+
+/* Write SYMBOLIZE to PATH as one line per maximal run of PCs that share a
+   mapping.  True when there was nothing to write or the file was written.  */
+bool
+write_address_mapping_dump (
+  const std::string &path,
+  const dwarf_debug::correlate_symbolize_map &symbolize)
+{
+  if (path.empty ())
+    return true;
+  if (symbolize.empty ())
+    return false;
+
+  FILE *out = fopen (path.c_str (), "w");
+  if (out == nullptr)
+    return false;
+  fprintf (out,
+          "# addr\tfunc\tfile:line:dw_disc\tinline1:call_line\t..."
+          "addr is start of maximal run of identical consecutive "
+          "mappings\n"
+          "dw_disc = last DWARF LNE row at this PC (same as GNU addr2line); "
+          "not the is_stmt deposit row, not llvm-symbolizer :column\n");
+
+  std::vector<uint64_t> pcs;
+  pcs.reserve (symbolize.size ());
+  for (const auto &entry : symbolize)
+    pcs.push_back (entry.first);
+  std::sort (pcs.begin (), pcs.end ());
+
+  size_t i = 0;
+  while (i < pcs.size ())
+    {
+      const uint64_t run_start = pcs[i];
+      const dwarf_debug::correlate_symbolize_entry &entry
+       = symbolize.at (run_start);
+      if (dwarf_debug::string_empty (entry.function_name)
+         || entry.locations.empty ())
+       {
+         ++i;
+         continue;
+       }
+      uint64_t next_pc = run_start + 1;
+      size_t j = i + 1;
+      while (j < pcs.size () && pcs[j] == next_pc
+            && symbolize_entries_equal (entry, symbolize.at (pcs[j])))
+       {
+         ++next_pc;
+         ++j;
+       }
+
+      const std::string &name
+       = dwarf_debug::string_value (entry.function_name);
+      fprintf (out,
+              "0x%" PRIx64 "\t%.*s\t",
+              run_start,
+              static_cast<int> (name.size ()),
+              name.data ());
+      const size_t line_idx
+       = std::min (entry.dwarf_line_index, entry.locations.size () - 1);
+      format_source_location (
+       out, entry.locations[line_idx], entry.inline_stack);
+      fprintf (out, "\n");
+      i = j;
+    }
+  fclose (out);
+  return true;
+}
+
+} /* namespace autofdo.  */
diff --git a/afdo_tools/src/autofdo_debug_dump.h 
b/afdo_tools/src/autofdo_debug_dump.h
new file mode 100644
index 00000000000..119f19e56f5
--- /dev/null
+++ b/afdo_tools/src/autofdo_debug_dump.h
@@ -0,0 +1,41 @@
+/* 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/>.  */
+
+#ifndef GCC_AUTOFDO_DEBUG_DUMP_H
+#define GCC_AUTOFDO_DEBUG_DUMP_H
+
+#include "dwarf_reader.h"
+
+#include <string>
+
+namespace perf_data {
+class profile_reader;
+}
+
+namespace autofdo {
+
+void dump_ingested_perf_maps (const perf_data::profile_reader &source,
+                             const std::string &path);
+
+bool write_address_mapping_dump (
+  const std::string &path,
+  const dwarf_debug::correlate_symbolize_map &symbolize);
+
+} /* namespace autofdo.  */
+
+#endif /* GCC_AUTOFDO_DEBUG_DUMP_H.  */
-- 
2.34.1

Reply via email to