[2/4] gprofng: mark non-precise HW counter columns

Message ID 20260626113840.44715-3-claziss@gmail.com
State New
Headers
Series gprofng: Improve HW counter markings and documentation |

Commit Message

Claudiu Zissulescu June 26, 2026, 11:33 a.m. UTC
  Mark the column header of any non-precise HW counter metric with "(*)"
and emit a single explanatory footnote below the column header block
when at least one displayed metric is non-precise.  Precise PEBS/IBS
events (ABST_EXACT, ABST_EXACT_PEBS_PLUS1) are left unmarked. Also
point the user at the "Precise events" section of -h output.

gprofng/

	* src/Metric.cc (Metric::set_name): Append "*" marker.
	* src/Print.cc (print_label): Emit a footnote explaining the "*"
	marker.
	* src/Hist_data.cc (Hist_data::print_label): Likewise.
---
 gprofng/src/Hist_data.cc | 27 +++++++++++++++++++++++++++
 gprofng/src/Metric.cc    | 16 +++++++++++++---
 gprofng/src/Print.cc     | 26 ++++++++++++++++++++++++++
 3 files changed, 66 insertions(+), 3 deletions(-)
  

Patch

diff --git a/gprofng/src/Hist_data.cc b/gprofng/src/Hist_data.cc
index 41e3edf3ae4..8818e84f215 100644
--- a/gprofng/src/Hist_data.cc
+++ b/gprofng/src/Hist_data.cc
@@ -863,6 +863,33 @@  Hist_data::print_label (FILE *out_file, Metric::HistMetric *hist_metric,
   sb1.toFileLn (out_file);
   sb2.toFileLn (out_file);
   sb3.toFileLn (out_file);
+
+  /* Footnote for non-precise HW counters (marked with " (*)" by
+     Metric::set_name).  Emit once if any visible metric uses such a
+     counter, so users understand source-line attribution may skid.
+     Mirrors the equivalent block in the free print_label() in Print.cc.  */
+  bool any_non_precise = false;
+  for (int i = 0; i < nmetrics && !any_non_precise; i++)
+    {
+      Metric *m = metrics->get (i);
+      if (!m->is_any_visible ())
+	continue;
+      if (m->get_type () != Metric::HWCNTR)
+	continue;
+      Hwcentry *hwc = m->get_hw_ctr ();
+      if (hwc != NULL
+	  && !HWCENTRY_USES_SAMPLING (hwc)
+	  && hwc->memop != ABST_EXACT)
+	any_non_precise = true;
+    }
+  if (any_non_precise)
+    fprintf (out_file, GTXT (
+      "%*s(*) Non-precise HW event: sample location may skid forward by\n"
+      "%*s    several instructions, so cache/branch misses are often\n"
+      "%*s    attributed to the next basic block (e.g. a loop header).\n"
+      "%*s    For exact line attribution, prefer precise events\n"
+      "%*s    (see the \"Precise events\" section of `gprofng collect app -h`).\n"),
+      space, "", space, "", space, "", space, "", space, "");
   return name_offset;
 }
 
diff --git a/gprofng/src/Metric.cc b/gprofng/src/Metric.cc
index a72bdaef6a3..f4264277bf9 100644
--- a/gprofng/src/Metric.cc
+++ b/gprofng/src/Metric.cc
@@ -306,7 +306,8 @@  Metric::set_subtype (SubType st)
     case HWCNTR:
       {
 	char *sstr, *estr1, *estr2;
-	if (get_hw_ctr () == NULL)
+	Hwcentry *hwc = get_hw_ctr ();
+	if (hwc == NULL)
 	  abort ();
 	sstr = get_username ();
 	if (st == EXCLUSIVE)
@@ -335,8 +336,17 @@  Metric::set_subtype (SubType st)
 				 get_aux (), st);
 	    estr2 = dbe_strdup (NTXT ("??"));
 	  }
-	name = dbe_sprintf (NTXT ("%s%s"), estr1, sstr);
-	abbr = dbe_sprintf (NTXT ("%s%s"), estr2, sstr);
+	/* Mark non-precise counters so users don't over-trust source-line
+	   attribution.  Only PEBS/EXACT events tag the retiring instruction;
+	   everything else may skid forward by several instructions.  The (*)
+	   should be explained by a footnote at report-print time.  */
+	const char *mark = "";
+	if ((st == EXCLUSIVE || st == INCLUSIVE || st == DATASPACE)
+	    && !HWCENTRY_USES_SAMPLING (hwc)
+	    && hwc->memop != ABST_EXACT)
+	  mark = " (*)";
+	name = dbe_sprintf (NTXT ("%s%s%s"), estr1, sstr, mark);
+	abbr = dbe_sprintf (NTXT ("%s%s%s"), estr2, sstr, mark);
 	break;
       }
 
diff --git a/gprofng/src/Print.cc b/gprofng/src/Print.cc
index 564e3a638bc..8d3cef4ede4 100644
--- a/gprofng/src/Print.cc
+++ b/gprofng/src/Print.cc
@@ -306,6 +306,32 @@  print_label (FILE *out_file, MetricList *metrics_list,
   fprintf (out_file, NTXT ("%*s%s\n"), space, NTXT (""), delTrailingBlanks (line1));
   fprintf (out_file, NTXT ("%*s%s\n"), space, NTXT (""), delTrailingBlanks (line2));
   fprintf (out_file, NTXT ("%*s%s\n"), space, NTXT (""), delTrailingBlanks (line3));
+
+  /* Footnote for non-precise HW counters (marked with " (*)" by
+     Metric::set_name).  Emit once if any visible metric uses such a
+     counter, so users understand source-line attribution may skid.  */
+  bool any_non_precise = false;
+  for (int i = 0, n = mlist->size (); i < n && !any_non_precise; i++)
+    {
+      Metric *m = mlist->fetch (i);
+      if (!(m->is_visible () || m->is_tvisible () || m->is_pvisible ()))
+	continue;
+      if (m->get_type () != Metric::HWCNTR)
+	continue;
+      Hwcentry *hwc = m->get_hw_ctr ();
+      if (hwc != NULL
+	  && !HWCENTRY_USES_SAMPLING (hwc)
+	  && hwc->memop != ABST_EXACT)
+	any_non_precise = true;
+    }
+  if (any_non_precise)
+    fprintf (out_file, GTXT (
+      "%*s(*) Non-precise HW event: sample location may skid forward by\n"
+      "%*s    several instructions, so cache/branch misses are often\n"
+      "%*s    attributed to the next basic block (e.g. a loop header).\n"
+      "%*s    For exact line attribution, prefer precise events\n"
+      "%*s    (see the \"Precise events\" section of `gprofng collect app -h`).\n"),
+      space, "", space, "", space, "", space, "", space, "");
   return name_offset;
 }