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