https://gcc.gnu.org/g:9e98b37f32f8bff72885904fc66ea7ec8fefec58

commit r16-3465-g9e98b37f32f8bff72885904fc66ea7ec8fefec58
Author: David Malcolm <dmalc...@redhat.com>
Date:   Fri Aug 29 14:39:37 2025 -0400

    diagnostics: add GCC_DIAGNOSTICS_LOG
    
    Whilst experimenting with PR diagnostics/121039 (potentially capturing
    suppressed diagnostics in SARIF output), I found it very useful to have
    a text log from the diagnostic subsystem to track what it's doing and
    the decisions it's making (e.g. exactly when and why a diagnostic is
    being rejected).
    
    This patch adds a simple logging mechanism to the diagnostics subsystem,
    enabled by setting GCC_DIAGNOSTICS_LOG in the environment, which emits
    nested text like this to stderr (or a named file):
    
    warning (option_id: 668, gmsgid: "%<-Wformat-security%> ignored without 
%<-Wformat%>")
      diagnostics::context::diagnostic_impl (option_id: 668, kind: warning, 
gmsgid: "%<-Wformat-security%> ignored without %<-Wformat%>")
        diagnostics::context::report_diagnostic
        rejecting: diagnostic not enabled
        false <- diagnostics::context::diagnostic_impl
      false <- warning
    
    This logging mechanism doesn't use pretty_printer because it can be
    helpful to use it to debug pretty_printer itself.
    
    gcc/ChangeLog:
            * Makefile.in (OBJS-libcommon): Add diagnostics/logging.o.
            * diagnostic-global-context.cc: Include "diagnostics/logging.h".
            (log_function_params, auto_inc_log_depth): New "using" decls.
            (verbatim): Add logging.
            (emit_diagnostic): Likewise.
            (emit_diagnostic_valist): Likewise.
            (emit_diagnostic_valist_meta): Likewise.
            (inform): Likewise.
            (inform_n): Likewise.
            (warning): Likewise.
            (warning_at): Likewise.
            (warning_meta): Likewise.
            (warning_n): Likewise.
            (pedwarn): Likewise.
            (permerror): Likewise.
            (permerror_opt): Likewise.
            * diagnostics/context.cc: Include "diagnostics/logging.h".
            (context::initialize): Initialize m_logger.  Add logging.
            (context::finish): Add logging.  Clean up m_logger.
            (context::dump): Add indent param.
            (context::set_sink): Add logging.
            (context::add_sink): Add logging.
            (diagnostic_kind_debug_text): New.
            (get_debug_string_for_kind): New.
            (context::report_diagnostic): Add logging.
            (context::diagnostic_impl): Likewise.
            (context::diagnostic_n_impl): Likewise.
            (context::end_group): Likewise.
            * diagnostics/context.h: Include "diagnostics/logging.h".
            (context::dump): Add indent param.
            (context::get_logger): New accessor.
            (context::classify_diagnostics): Add logging.
            (context::push_diagnostics): Likewise.
            (context::pop_diagnostics): Likewise.
            (context::m_logger): New field.
            * diagnostics/html-sink.cc: Include "diagnostics/logging.h".
            (html_builder::flush_to_file): Add logging.
            (html_sink::on_report_diagnostic): Likewise.
            * diagnostics/kinds.h (get_debug_string_for_kind): New decl.
            * diagnostics/logging.cc: New file.
            * diagnostics/logging.h: New file.
            * diagnostics/output-file.h: Include "label-text.h".
            * diagnostics/sarif-sink.cc: Include "diagnostics/logging.h".
            (sarif_builder::flush_to_object): Add logging.
            (sarif_builder::flush_to_file): Likewise.
            (sarif_sink::on_report_diagnostic): Likewise.
            * diagnostics/sink.h (sink::get_logger): New.
            * diagnostics/text-sink.cc: Include "diagnostics/logging.h".
            (text_sink::on_report_diagnostic): Add logging.
            * doc/invoke.texi (Environment Variables): Document
            GCC_DIAGNOSTICS_LOG.
            * opts-diagnostic.cc: Include "diagnostics/logging.h".
            (handle_OPT_fdiagnostics_add_output_): Add loggging.
            (handle_OPT_fdiagnostics_set_output_): Likewise.
    
    gcc/analyzer/ChangeLog:
            * pending-diagnostic.cc: Include "diagnostics/logging.h".
            (diagnostic_emission_context::warn): Add logging.
            (diagnostic_emission_context::inform): Likewise.
    
    Signed-off-by: David Malcolm <dmalc...@redhat.com>

Diff:
---
 gcc/Makefile.in                    |   1 +
 gcc/analyzer/pending-diagnostic.cc |  18 +++
 gcc/diagnostic-global-context.cc   | 267 ++++++++++++++++++++++++++++++++++++-
 gcc/diagnostics/context.cc         | 161 ++++++++++++++++++----
 gcc/diagnostics/context.h          |  31 ++++-
 gcc/diagnostics/html-sink.cc       |   6 +
 gcc/diagnostics/kinds.h            |   1 +
 gcc/diagnostics/logging.cc         |  72 ++++++++++
 gcc/diagnostics/logging.h          | 230 ++++++++++++++++++++++++++++++++
 gcc/diagnostics/output-file.h      |   2 +
 gcc/diagnostics/sarif-sink.cc      |   8 ++
 gcc/diagnostics/sink.h             |   2 +
 gcc/diagnostics/text-sink.cc       |   6 +
 gcc/doc/invoke.texi                |  10 ++
 gcc/opts-diagnostic.cc             |   5 +
 15 files changed, 784 insertions(+), 36 deletions(-)

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index d2744db843d7..d35fced2f2a2 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1864,6 +1864,7 @@ OBJS-libcommon = \
        diagnostics/sarif-sink.o \
        diagnostics/text-sink.o \
        diagnostics/lazy-paths.o \
+       diagnostics/logging.o \
        diagnostics/macro-unwinding.o \
        diagnostics/option-classifier.o \
        diagnostics/paths.o \
diff --git a/gcc/analyzer/pending-diagnostic.cc 
b/gcc/analyzer/pending-diagnostic.cc
index cc2d7959c4cc..14d8f9f803fd 100644
--- a/gcc/analyzer/pending-diagnostic.cc
+++ b/gcc/analyzer/pending-diagnostic.cc
@@ -21,6 +21,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "analyzer/common.h"
 
 #include "diagnostics/event-id.h"
+#include "diagnostics/logging.h"
 #include "cpplib.h"
 #include "digraph.h"
 #include "ordered-hash-map.h"
@@ -88,6 +89,12 @@ diagnostic_emission_context::get_pending_diagnostic () const
 bool
 diagnostic_emission_context::warn (const char *gmsgid, ...)
 {
+  auto dc_logger = global_dc->get_logger ();
+  diagnostics::logging::log_function_params
+    (dc_logger, "ana::diagnostic_emission_context::warn")
+    .log_param_string ("gmsgid", gmsgid);
+  diagnostics::logging::auto_inc_depth depth_sentinel (dc_logger);
+
   const pending_diagnostic &pd = get_pending_diagnostic ();
   auto_diagnostic_group d;
   va_list ap;
@@ -97,6 +104,11 @@ diagnostic_emission_context::warn (const char *gmsgid, ...)
                                                   pd.get_controlling_option (),
                                                   gmsgid, &ap);
   va_end (ap);
+
+  if (dc_logger)
+    dc_logger->log_bool_return ("ana::diagnostic_emission_context::warn",
+                               result);
+
   return result;
 }
 
@@ -106,6 +118,12 @@ diagnostic_emission_context::warn (const char *gmsgid, ...)
 void
 diagnostic_emission_context::inform (const char *gmsgid, ...)
 {
+  auto dc_logger = global_dc->get_logger ();
+  diagnostics::logging::log_function_params
+    (dc_logger, "ana::diagnostic_emission_context::inform")
+    .log_param_string ("gmsgid", gmsgid);
+  diagnostics::logging::auto_inc_depth depth_sentinel (dc_logger);
+
   const pending_diagnostic &pd = get_pending_diagnostic ();
   auto_diagnostic_group d;
   va_list ap;
diff --git a/gcc/diagnostic-global-context.cc b/gcc/diagnostic-global-context.cc
index 500f19cfb4f9..30fc1906790c 100644
--- a/gcc/diagnostic-global-context.cc
+++ b/gcc/diagnostic-global-context.cc
@@ -28,11 +28,15 @@ along with GCC; see the file COPYING3.  If not see
 #include "intl.h"
 #include "diagnostic.h"
 #include "diagnostics/sink.h"
+#include "diagnostics/logging.h"
 
 /* A diagnostics::context surrogate for stderr.  */
 static diagnostics::context global_diagnostic_context;
 diagnostics::context *global_dc = &global_diagnostic_context;
 
+using log_function_params = diagnostics::logging::log_function_params;
+using auto_inc_log_depth = diagnostics::logging::auto_inc_depth;
+
 /* Standard error reporting routines in increasing order of severity.  */
 
 /* Text to be emitted verbatim to the error message stream; this
@@ -41,8 +45,12 @@ diagnostics::context *global_dc = &global_diagnostic_context;
 void
 verbatim (const char *gmsgid, ...)
 {
-  va_list ap;
+  auto logger = global_dc->get_logger ();
+  log_function_params (logger, __func__)
+    .log_param_string ("gmsgid", gmsgid);
+  auto_inc_log_depth depth_sentinel (logger);
 
+  va_list ap;
   va_start (ap, gmsgid);
   text_info text (_(gmsgid), &ap, errno);
   global_dc->report_verbatim (text);
@@ -58,6 +66,13 @@ emit_diagnostic (enum diagnostics::kind kind,
                 diagnostics::option_id option_id,
                 const char *gmsgid, ...)
 {
+  auto logger = global_dc->get_logger ();
+  log_function_params (logger, __func__)
+    .log_param_location_t ("location", location)
+    .log_param_option_id ("option_id", option_id)
+    .log_param_string ("gmsgid", gmsgid);
+  auto_inc_log_depth depth_sentinel (logger);
+
   auto_diagnostic_group d;
   va_list ap;
   va_start (ap, gmsgid);
@@ -65,6 +80,10 @@ emit_diagnostic (enum diagnostics::kind kind,
   bool ret = global_dc->diagnostic_impl (&richloc, nullptr, option_id,
                                         gmsgid, &ap, kind);
   va_end (ap);
+
+  if (logger)
+    logger->log_bool_return ("emit_diagnostic", ret);
+
   return ret;
 }
 
@@ -76,12 +95,23 @@ emit_diagnostic (enum diagnostics::kind kind,
                 diagnostics::option_id option_id,
                 const char *gmsgid, ...)
 {
+  auto logger = global_dc->get_logger ();
+  log_function_params (logger, __func__)
+    .log_param_rich_location ("richloc", richloc)
+    .log_param_option_id ("option_id", option_id)
+    .log_param_string ("gmsgid", gmsgid);
+  auto_inc_log_depth depth_sentinel (logger);
+
   auto_diagnostic_group d;
   va_list ap;
   va_start (ap, gmsgid);
   bool ret = global_dc->diagnostic_impl (richloc, nullptr, option_id,
                                         gmsgid, &ap, kind);
   va_end (ap);
+
+  if (logger)
+    logger->log_bool_return ("emit_diagnostic", ret);
+
   return ret;
 }
 
@@ -93,9 +123,21 @@ emit_diagnostic_valist (enum diagnostics::kind kind,
                        diagnostics::option_id option_id,
                        const char *gmsgid, va_list *ap)
 {
+  auto logger = global_dc->get_logger ();
+  log_function_params (logger, __func__)
+    .log_param_location_t ("location", location)
+    .log_param_option_id ("option_id", option_id)
+    .log_param_string ("gmsgid", gmsgid);
+  auto_inc_log_depth depth_sentinel (logger);
+
   rich_location richloc (line_table, location);
-  return global_dc->diagnostic_impl (&richloc, nullptr, option_id,
-                                    gmsgid, ap, kind);
+  bool ret = global_dc->diagnostic_impl (&richloc, nullptr, option_id,
+                                        gmsgid, ap, kind);
+
+  if (logger)
+    logger->log_bool_return ("emit_diagnostic_valist", ret);
+
+  return ret;
 }
 
 /* As above, but with rich_location and metadata.  */
@@ -107,8 +149,20 @@ emit_diagnostic_valist_meta (enum diagnostics::kind kind,
                             diagnostics::option_id option_id,
                             const char *gmsgid, va_list *ap)
 {
-  return global_dc->diagnostic_impl (richloc, metadata, option_id,
-                                    gmsgid, ap, kind);
+  auto logger = global_dc->get_logger ();
+  log_function_params (logger, __func__)
+    .log_param_rich_location ("richloc", richloc)
+    .log_param_option_id ("option_id", option_id)
+    .log_param_string ("gmsgid", gmsgid);
+  auto_inc_log_depth depth_sentinel (logger);
+
+  bool ret = global_dc->diagnostic_impl (richloc, metadata, option_id,
+                                        gmsgid, ap, kind);
+
+  if (logger)
+    logger->log_bool_return ("emit_diagnostic_valist_meta", ret);
+
+  return ret;
 }
 
 /* An informative note at LOCATION.  Use this for additional details on an 
error
@@ -116,6 +170,12 @@ emit_diagnostic_valist_meta (enum diagnostics::kind kind,
 void
 inform (location_t location, const char *gmsgid, ...)
 {
+  auto logger = global_dc->get_logger ();
+  log_function_params (logger, __func__)
+    .log_param_location_t ("location", location)
+    .log_param_string ("gmsgid", gmsgid);
+  auto_inc_log_depth depth_sentinel (logger);
+
   auto_diagnostic_group d;
   va_list ap;
   va_start (ap, gmsgid);
@@ -131,6 +191,12 @@ inform (rich_location *richloc, const char *gmsgid, ...)
 {
   gcc_assert (richloc);
 
+  auto logger = global_dc->get_logger ();
+  log_function_params (logger, __func__)
+    .log_param_rich_location ("richloc", richloc)
+    .log_param_string ("gmsgid", gmsgid);
+  auto_inc_log_depth depth_sentinel (logger);
+
   auto_diagnostic_group d;
   va_list ap;
   va_start (ap, gmsgid);
@@ -145,6 +211,12 @@ void
 inform_n (location_t location, unsigned HOST_WIDE_INT n,
          const char *singular_gmsgid, const char *plural_gmsgid, ...)
 {
+  auto logger = global_dc->get_logger ();
+  log_function_params (logger, __func__)
+    .log_param_location_t ("location", location)
+    .log_params_n_gmsgids (n, singular_gmsgid, plural_gmsgid);
+  auto_inc_log_depth depth_sentinel (logger);
+
   va_list ap;
   va_start (ap, plural_gmsgid);
   auto_diagnostic_group d;
@@ -161,6 +233,12 @@ inform_n (location_t location, unsigned HOST_WIDE_INT n,
 bool
 warning (diagnostics::option_id option_id, const char *gmsgid, ...)
 {
+  auto logger = global_dc->get_logger ();
+  log_function_params (logger, __func__)
+    .log_param_option_id ("option_id", option_id)
+    .log_param_string ("gmsgid", gmsgid);
+  auto_inc_log_depth depth_sentinel (logger);
+
   auto_diagnostic_group d;
   va_list ap;
   va_start (ap, gmsgid);
@@ -169,6 +247,10 @@ warning (diagnostics::option_id option_id, const char 
*gmsgid, ...)
                                         gmsgid, &ap,
                                         diagnostics::kind::warning);
   va_end (ap);
+
+  if (logger)
+    logger->log_bool_return ("warning", ret);
+
   return ret;
 }
 
@@ -181,6 +263,13 @@ warning_at (location_t location,
            diagnostics::option_id option_id,
            const char *gmsgid, ...)
 {
+  auto logger = global_dc->get_logger ();
+  log_function_params (logger, __func__)
+    .log_param_location_t ("location", location)
+    .log_param_option_id ("option_id", option_id)
+    .log_param_string ("gmsgid", gmsgid);
+  auto_inc_log_depth depth_sentinel (logger);
+
   auto_diagnostic_group d;
   va_list ap;
   va_start (ap, gmsgid);
@@ -189,6 +278,10 @@ warning_at (location_t location,
                                         gmsgid, &ap,
                                         diagnostics::kind::warning);
   va_end (ap);
+
+  if (logger)
+    logger->log_bool_return ("warning_at", ret);
+
   return ret;
 }
 
@@ -201,6 +294,13 @@ warning_at (rich_location *richloc,
 {
   gcc_assert (richloc);
 
+  auto logger = global_dc->get_logger ();
+  log_function_params (logger, __func__)
+    .log_param_rich_location ("richloc", richloc)
+    .log_param_option_id ("option_id", option_id)
+    .log_param_string ("gmsgid", gmsgid);
+  auto_inc_log_depth depth_sentinel (logger);
+
   auto_diagnostic_group d;
   va_list ap;
   va_start (ap, gmsgid);
@@ -208,6 +308,10 @@ warning_at (rich_location *richloc,
                                         gmsgid, &ap,
                                         diagnostics::kind::warning);
   va_end (ap);
+
+  if (logger)
+    logger->log_bool_return ("warning_at", ret);
+
   return ret;
 }
 
@@ -221,6 +325,13 @@ warning_meta (rich_location *richloc,
 {
   gcc_assert (richloc);
 
+  auto logger = global_dc->get_logger ();
+  log_function_params (logger, __func__)
+    .log_param_rich_location ("richloc", richloc)
+    .log_param_option_id ("option_id", option_id)
+    .log_param_string ("gmsgid", gmsgid);
+  auto_inc_log_depth depth_sentinel (logger);
+
   auto_diagnostic_group d;
   va_list ap;
   va_start (ap, gmsgid);
@@ -228,6 +339,10 @@ warning_meta (rich_location *richloc,
                                         gmsgid, &ap,
                                         diagnostics::kind::warning);
   va_end (ap);
+
+  if (logger)
+    logger->log_bool_return ("warning_meta", ret);
+
   return ret;
 }
 
@@ -241,6 +356,13 @@ warning_n (rich_location *richloc,
 {
   gcc_assert (richloc);
 
+  auto logger = global_dc->get_logger ();
+  log_function_params (logger, __func__)
+    .log_param_rich_location ("richloc", richloc)
+    .log_param_option_id ("option_id", option_id)
+    .log_params_n_gmsgids (n, singular_gmsgid, plural_gmsgid);
+  auto_inc_log_depth depth_sentinel (logger);
+
   auto_diagnostic_group d;
   va_list ap;
   va_start (ap, plural_gmsgid);
@@ -248,6 +370,10 @@ warning_n (rich_location *richloc,
                                           singular_gmsgid, plural_gmsgid,
                                           &ap, diagnostics::kind::warning);
   va_end (ap);
+
+  if (logger)
+    logger->log_bool_return ("warning_n", ret);
+
   return ret;
 }
 
@@ -261,6 +387,13 @@ warning_n (location_t location,
           unsigned HOST_WIDE_INT n,
           const char *singular_gmsgid, const char *plural_gmsgid, ...)
 {
+  auto logger = global_dc->get_logger ();
+  log_function_params (logger, __func__)
+    .log_param_location_t ("location", location)
+    .log_param_option_id ("option_id", option_id)
+    .log_params_n_gmsgids (n, singular_gmsgid, plural_gmsgid);
+  auto_inc_log_depth depth_sentinel (logger);
+
   auto_diagnostic_group d;
   va_list ap;
   va_start (ap, plural_gmsgid);
@@ -269,6 +402,10 @@ warning_n (location_t location,
                                           singular_gmsgid, plural_gmsgid,
                                           &ap, diagnostics::kind::warning);
   va_end (ap);
+
+  if (logger)
+    logger->log_bool_return ("warning_n", ret);
+
   return ret;
 }
 
@@ -290,6 +427,13 @@ pedwarn (location_t location,
         diagnostics::option_id option_id,
         const char *gmsgid, ...)
 {
+  auto logger = global_dc->get_logger ();
+  log_function_params (logger, __func__)
+    .log_param_location_t ("location", location)
+    .log_param_option_id ("option_id", option_id)
+    .log_param_string ("gmsgid", gmsgid);
+  auto_inc_log_depth depth_sentinel (logger);
+
   auto_diagnostic_group d;
   va_list ap;
   va_start (ap, gmsgid);
@@ -298,6 +442,10 @@ pedwarn (location_t location,
                                         gmsgid, &ap,
                                         diagnostics::kind::pedwarn);
   va_end (ap);
+
+  if (logger)
+    logger->log_bool_return ("pedwarn", ret);
+
   return ret;
 }
 
@@ -310,6 +458,13 @@ pedwarn (rich_location *richloc,
 {
   gcc_assert (richloc);
 
+  auto logger = global_dc->get_logger ();
+  log_function_params (logger, __func__)
+    .log_param_rich_location ("richloc", richloc)
+    .log_param_option_id ("option_id", option_id)
+    .log_param_string ("gmsgid", gmsgid);
+  auto_inc_log_depth depth_sentinel (logger);
+
   auto_diagnostic_group d;
   va_list ap;
   va_start (ap, gmsgid);
@@ -317,6 +472,10 @@ pedwarn (rich_location *richloc,
                                         gmsgid, &ap,
                                         diagnostics::kind::pedwarn);
   va_end (ap);
+
+  if (logger)
+    logger->log_bool_return ("pedwarn", ret);
+
   return ret;
 }
 
@@ -330,6 +489,12 @@ pedwarn (rich_location *richloc,
 bool
 permerror (location_t location, const char *gmsgid, ...)
 {
+  auto logger = global_dc->get_logger ();
+  log_function_params (logger, __func__)
+    .log_param_location_t ("location", location)
+    .log_param_string ("gmsgid", gmsgid);
+  auto_inc_log_depth depth_sentinel (logger);
+
   auto_diagnostic_group d;
   va_list ap;
   va_start (ap, gmsgid);
@@ -337,6 +502,10 @@ permerror (location_t location, const char *gmsgid, ...)
   bool ret = global_dc->diagnostic_impl (&richloc, nullptr, -1, gmsgid, &ap,
                                         diagnostics::kind::permerror);
   va_end (ap);
+
+  if (logger)
+    logger->log_bool_return ("permerror", ret);
+
   return ret;
 }
 
@@ -347,12 +516,22 @@ permerror (rich_location *richloc, const char *gmsgid, 
...)
 {
   gcc_assert (richloc);
 
+  auto logger = global_dc->get_logger ();
+  log_function_params (logger, __func__)
+    .log_param_rich_location ("richloc", richloc)
+    .log_param_string ("gmsgid", gmsgid);
+  auto_inc_log_depth depth_sentinel (logger);
+
   auto_diagnostic_group d;
   va_list ap;
   va_start (ap, gmsgid);
   bool ret = global_dc->diagnostic_impl (richloc, nullptr, -1, gmsgid, &ap,
                                         diagnostics::kind::permerror);
   va_end (ap);
+
+  if (logger)
+    logger->log_bool_return ("permerror", ret);
+
   return ret;
 }
 
@@ -365,6 +544,13 @@ permerror_opt (location_t location,
               diagnostics::option_id option_id,
               const char *gmsgid, ...)
 {
+  auto logger = global_dc->get_logger ();
+  log_function_params (logger, __func__)
+    .log_param_location_t ("location", location)
+    .log_param_option_id ("option_id", option_id)
+    .log_param_string ("gmsgid", gmsgid);
+  auto_inc_log_depth depth_sentinel (logger);
+
   auto_diagnostic_group d;
   va_list ap;
   va_start (ap, gmsgid);
@@ -373,6 +559,10 @@ permerror_opt (location_t location,
                                         gmsgid, &ap,
                                         diagnostics::kind::permerror);
   va_end (ap);
+
+  if (logger)
+    logger->log_bool_return ("permerror_opt", ret);
+
   return ret;
 }
 
@@ -385,6 +575,13 @@ permerror_opt (rich_location *richloc,
 {
   gcc_assert (richloc);
 
+  auto logger = global_dc->get_logger ();
+  log_function_params (logger, __func__)
+    .log_param_rich_location ("richloc", richloc)
+    .log_param_option_id ("option_id", option_id)
+    .log_param_string ("gmsgid", gmsgid);
+  auto_inc_log_depth depth_sentinel (logger);
+
   auto_diagnostic_group d;
   va_list ap;
   va_start (ap, gmsgid);
@@ -392,6 +589,10 @@ permerror_opt (rich_location *richloc,
                                         gmsgid, &ap,
                                         diagnostics::kind::permerror);
   va_end (ap);
+
+  if (logger)
+    logger->log_bool_return ("permerror_opt", ret);
+
   return ret;
 }
 
@@ -400,6 +601,11 @@ permerror_opt (rich_location *richloc,
 void
 error (const char *gmsgid, ...)
 {
+  auto logger = global_dc->get_logger ();
+  log_function_params (logger, __func__)
+    .log_param_string ("gmsgid", gmsgid);
+  auto_inc_log_depth depth_sentinel (logger);
+
   auto_diagnostic_group d;
   va_list ap;
   va_start (ap, gmsgid);
@@ -415,6 +621,12 @@ void
 error_n (location_t location, unsigned HOST_WIDE_INT n,
         const char *singular_gmsgid, const char *plural_gmsgid, ...)
 {
+  auto logger = global_dc->get_logger ();
+  log_function_params (logger, __func__)
+    .log_param_location_t ("location", location)
+    .log_params_n_gmsgids (n, singular_gmsgid, plural_gmsgid);
+  auto_inc_log_depth depth_sentinel (logger);
+
   auto_diagnostic_group d;
   va_list ap;
   va_start (ap, plural_gmsgid);
@@ -429,6 +641,12 @@ error_n (location_t location, unsigned HOST_WIDE_INT n,
 void
 error_at (location_t loc, const char *gmsgid, ...)
 {
+  auto logger = global_dc->get_logger ();
+  log_function_params (logger, __func__)
+    .log_param_location_t ("loc", loc)
+    .log_param_string ("gmsgid", gmsgid);
+  auto_inc_log_depth depth_sentinel (logger);
+
   auto_diagnostic_group d;
   va_list ap;
   va_start (ap, gmsgid);
@@ -445,6 +663,12 @@ error_at (rich_location *richloc, const char *gmsgid, ...)
 {
   gcc_assert (richloc);
 
+  auto logger = global_dc->get_logger ();
+  log_function_params (logger, __func__)
+    .log_param_rich_location ("richloc", richloc)
+    .log_param_string ("gmsgid", gmsgid);
+  auto_inc_log_depth depth_sentinel (logger);
+
   auto_diagnostic_group d;
   va_list ap;
   va_start (ap, gmsgid);
@@ -461,6 +685,12 @@ error_meta (rich_location *richloc, const 
diagnostics::metadata &metadata,
 {
   gcc_assert (richloc);
 
+  auto logger = global_dc->get_logger ();
+  log_function_params (logger, __func__)
+    .log_param_rich_location ("richloc", richloc)
+    .log_param_string ("gmsgid", gmsgid);
+  auto_inc_log_depth depth_sentinel (logger);
+
   auto_diagnostic_group d;
   va_list ap;
   va_start (ap, gmsgid);
@@ -475,6 +705,11 @@ error_meta (rich_location *richloc, const 
diagnostics::metadata &metadata,
 void
 sorry (const char *gmsgid, ...)
 {
+  auto logger = global_dc->get_logger ();
+  log_function_params (logger, __func__)
+    .log_param_string ("gmsgid", gmsgid);
+  auto_inc_log_depth depth_sentinel (logger);
+
   auto_diagnostic_group d;
   va_list ap;
   va_start (ap, gmsgid);
@@ -488,6 +723,12 @@ sorry (const char *gmsgid, ...)
 void
 sorry_at (location_t loc, const char *gmsgid, ...)
 {
+  auto logger = global_dc->get_logger ();
+  log_function_params (logger, __func__)
+    .log_param_location_t ("loc", loc)
+    .log_param_string ("gmsgid", gmsgid);
+  auto_inc_log_depth depth_sentinel (logger);
+
   auto_diagnostic_group d;
   va_list ap;
   va_start (ap, gmsgid);
@@ -511,6 +752,12 @@ seen_error (void)
 void
 fatal_error (location_t loc, const char *gmsgid, ...)
 {
+  auto logger = global_dc->get_logger ();
+  log_function_params (logger, __func__)
+    .log_param_location_t ("loc", loc)
+    .log_param_string ("gmsgid", gmsgid);
+  auto_inc_log_depth depth_sentinel (logger);
+
   auto_diagnostic_group d;
   va_list ap;
   va_start (ap, gmsgid);
@@ -527,6 +774,11 @@ fatal_error (location_t loc, const char *gmsgid, ...)
 void
 internal_error (const char *gmsgid, ...)
 {
+  auto logger = global_dc->get_logger ();
+  log_function_params (logger, __func__)
+    .log_param_string ("gmsgid", gmsgid);
+  auto_inc_log_depth depth_sentinel (logger);
+
   auto_diagnostic_group d;
   va_list ap;
   va_start (ap, gmsgid);
@@ -544,6 +796,11 @@ internal_error (const char *gmsgid, ...)
 void
 internal_error_no_backtrace (const char *gmsgid, ...)
 {
+  auto logger = global_dc->get_logger ();
+  log_function_params (logger, __func__)
+    .log_param_string ("gmsgid", gmsgid);
+  auto_inc_log_depth depth_sentinel (logger);
+
   auto_diagnostic_group d;
   va_list ap;
   va_start (ap, gmsgid);
diff --git a/gcc/diagnostics/context.cc b/gcc/diagnostics/context.cc
index 3668958355f4..0f8670ba806e 100644
--- a/gcc/diagnostics/context.cc
+++ b/gcc/diagnostics/context.cc
@@ -51,6 +51,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "diagnostics/buffering.h"
 #include "diagnostics/file-cache.h"
 #include "diagnostics/dumping.h"
+#include "diagnostics/logging.h"
 
 #ifdef HAVE_TERMIOS_H
 # include <termios.h>
@@ -211,6 +212,7 @@ context::initialize (int n_opts)
   m_diagrams.m_theme = nullptr;
   m_original_argv = nullptr;
   m_diagnostic_buffer = nullptr;
+  m_logger = nullptr;
 
   enum diagnostic_text_art_charset text_art_charset
     = DIAGNOSTICS_TEXT_ART_CHARSET_EMOJI;
@@ -222,6 +224,27 @@ context::initialize (int n_opts)
        text_art_charset = DIAGNOSTICS_TEXT_ART_CHARSET_ASCII;
     }
   set_text_art_charset (text_art_charset);
+
+  if (const char *name = getenv ("GCC_DIAGNOSTICS_LOG"))
+    {
+      if (name[0] != '\0')
+       {
+         /* Try to write a log to the named path.  */
+         if (FILE *outfile = fopen (name, "w"))
+           m_logger = new logging::logger
+             (output_file (outfile, true,
+                           label_text::take (xstrdup (name))));
+       }
+      else
+       /* Write a log to stderr.  */
+       m_logger = new logging::logger
+         (output_file
+          (stderr, false,
+           label_text::borrow ("stderr")));
+    }
+
+  if (m_logger)
+    m_logger->log_printf ("diagnostics::context::initialize");
 }
 
 /* Maybe initialize the color support. We require clients to do this
@@ -329,6 +352,15 @@ context::initialize_input_context 
(diagnostic_input_charset_callback ccb,
 void
 context::finish ()
 {
+  if (m_logger)
+    {
+      m_logger->log_printf ("diagnostics::context::finish");
+      /* We're cleaning up the logger before this function exits,
+        so we can't use auto_inc_depth here.  */
+      m_logger->inc_depth ();
+      dump (m_logger->get_stream (), m_logger->get_indent ());
+    }
+
   /* We might be handling a fatal error.
      Close any active diagnostic groups, which may trigger flushing
      sinks.  */
@@ -381,45 +413,48 @@ context::finish ()
 
   freeargv (m_original_argv);
   m_original_argv = nullptr;
+
+  delete m_logger;
+  m_logger = nullptr;
 }
 
 /* Dump state of this diagnostics::context to OUT, for debugging.  */
 
 void
-context::dump (FILE *outfile) const
+context::dump (FILE *outfile, int indent) const
 {
-  dumping::emit_heading (outfile, 0, "diagnostics::context");
-  m_diagnostic_counters.dump (outfile, 2);
-  dumping::emit_heading (outfile, 2, "reference printer");
+  dumping::emit_heading (outfile, indent, "diagnostics::context");
+  m_diagnostic_counters.dump (outfile, indent + 2);
+  dumping::emit_heading (outfile, indent + 2, "reference printer");
   if (m_reference_printer)
-    m_reference_printer->dump (outfile, 4);
+    m_reference_printer->dump (outfile, indent + 4);
   else
-    dumping::emit_none (outfile, 4);
-  dumping::emit_heading (outfile, 2, "output sinks");
+    dumping::emit_none (outfile, indent + 4);
+  dumping::emit_heading (outfile, indent + 2, "output sinks");
   if (m_sinks.length () > 0)
     {
       for (unsigned i = 0; i < m_sinks.length (); ++i)
        {
-         dumping::emit_indent (outfile, 4);
+         dumping::emit_indent (outfile, indent + 4);
          const sink *s = m_sinks[i];
          fprintf (outfile, "sink %i (", i);
          s->dump_kind (outfile);
          fprintf (outfile, "):\n");
-         s->dump (outfile, 6);
+         s->dump (outfile, indent + 6);
        }
     }
   else
-    dumping::emit_none (outfile, 4);
-  dumping::emit_heading (outfile, 2, "diagnostic buffer");
+    dumping::emit_none (outfile, indent + 4);
+  dumping::emit_heading (outfile, indent + 2, "diagnostic buffer");
   if (m_diagnostic_buffer)
-    m_diagnostic_buffer->dump (outfile, 4);
+    m_diagnostic_buffer->dump (outfile, indent + 4);
   else
-    dumping::emit_none (outfile, 4);
-  dumping::emit_heading (outfile, 2, "file cache");
+    dumping::emit_none (outfile, indent + 4);
+  dumping::emit_heading (outfile, indent + 2, "file cache");
   if (m_file_cache)
-    m_file_cache->dump (outfile, 4);
+    m_file_cache->dump (outfile, indent + 4);
   else
-    dumping::emit_none (outfile, 4);
+    dumping::emit_none (outfile, indent + 4);
 }
 
 /* Return true if sufficiently severe diagnostics have been seen that
@@ -445,6 +480,9 @@ context::remove_all_output_sinks ()
 void
 context::set_sink (std::unique_ptr<sink> sink_)
 {
+  DIAGNOSTICS_LOG_SCOPE_PRINTF0 (m_logger, "diagnostics::context::set_sink");
+  if (m_logger)
+    sink_->dump (m_logger->get_stream (), m_logger->get_indent ());
   remove_all_output_sinks ();
   m_sinks.safe_push (sink_.release ());
 }
@@ -460,6 +498,9 @@ context::get_sink (size_t idx) const
 void
 context::add_sink (std::unique_ptr<sink> sink_)
 {
+  DIAGNOSTICS_LOG_SCOPE_PRINTF0 (m_logger, "diagnostics::context::add_sink");
+  if (m_logger)
+    sink_->dump (m_logger->get_stream (), m_logger->get_indent ());
   m_sinks.safe_push (sink_.release ());
 }
 
@@ -664,6 +705,19 @@ get_text_for_kind (enum kind kind)
   return diagnostic_kind_text[static_cast<int> (kind)];
 }
 
+static const char *const diagnostic_kind_debug_text[] = {
+#define DEFINE_DIAGNOSTIC_KIND(K, T, C) (#K),
+#include "diagnostics/kinds.def"
+#undef DEFINE_DIAGNOSTIC_KIND
+  "must-not-happen"
+};
+
+const char *
+get_debug_string_for_kind (enum kind kind)
+{
+  return diagnostic_kind_debug_text[static_cast<int> (kind)];
+}
+
 static const char *const diagnostic_kind_color[] = {
 #define DEFINE_DIAGNOSTIC_KIND(K, T, C) (C),
 #include "diagnostics/kinds.def"
@@ -1276,6 +1330,9 @@ context::emit_diagnostic_with_group_va (enum kind kind,
 bool
 context::report_diagnostic (diagnostic_info *diagnostic)
 {
+  auto logger = get_logger ();
+  DIAGNOSTICS_LOG_SCOPE_PRINTF0 (logger, 
"diagnostics::context::report_diagnostic");
+
   enum kind orig_diag_kind = diagnostic->m_kind;
 
   /* Every call to report_diagnostic should be within a
@@ -1290,6 +1347,8 @@ context::report_diagnostic (diagnostic_info *diagnostic)
   if (was_warning && m_inhibit_warnings)
     {
       inhibit_notes_in_group ();
+      if (m_logger)
+       m_logger->log_printf ("rejecting: inhibiting warnings");
       return false;
     }
 
@@ -1305,7 +1364,11 @@ context::report_diagnostic (diagnostic_info *diagnostic)
     }
 
   if (diagnostic->m_kind == kind::note && m_inhibit_notes_p)
-    return false;
+    {
+      if (m_logger)
+       m_logger->log_printf ("rejecting: inhibiting notes");
+      return false;
+    }
 
   /* If the user requested that warnings be treated as errors, so be
      it.  Note that we do this before the next block so that
@@ -1322,6 +1385,8 @@ context::report_diagnostic (diagnostic_info *diagnostic)
      stack.  .  */
   if (!diagnostic_enabled (diagnostic))
     {
+      if (m_logger)
+       m_logger->log_printf ("rejecting: diagnostic not enabled");
       inhibit_notes_in_group ();
       return false;
     }
@@ -1330,13 +1395,21 @@ context::report_diagnostic (diagnostic_info *diagnostic)
       && ((!m_warn_system_headers
           && diagnostic->m_iinfo.m_allsyslocs)
          || m_inhibit_warnings))
-    /* Bail if the warning is not to be reported because all locations in the
-       inlining stack (if there is one) are in system headers.  */
-    return false;
+    {
+      /* Bail if the warning is not to be reported because all locations in the
+        inlining stack (if there is one) are in system headers.  */
+      if (m_logger)
+       m_logger->log_printf ("rejecting: warning in system header");
+      return false;
+    }
 
   if (diagnostic->m_kind == kind::note && notes_inhibited_in_group ())
-    /* Bail for all the notes in the diagnostic_group that started to inhibit 
notes.  */
-    return false;
+    {
+      /* Bail for all the notes in the diagnostic_group that started to 
inhibit notes.  */
+      if (m_logger)
+       m_logger->log_printf ("rejecting: notes inhibited within group");
+      return false;
+    }
 
   if (diagnostic->m_kind != kind::note && diagnostic->m_kind != kind::ice)
     check_max_errors (false);
@@ -1397,8 +1470,13 @@ context::report_diagnostic (diagnostic_info *diagnostic)
 
   /* Is this the initial diagnostic within the stack of groups?  */
   if (m_diagnostic_groups.m_emission_count == 0)
-    for (auto sink_ : m_sinks)
-      sink_->on_begin_group ();
+    {
+      DIAGNOSTICS_LOG_SCOPE_PRINTF0
+       (get_logger (),
+        "diagnostics::context: beginning group");
+      for (auto sink_ : m_sinks)
+       sink_->on_begin_group ();
+    }
   m_diagnostic_groups.m_emission_count++;
 
   va_list *orig_args = diagnostic->m_message.m_args_ptr;
@@ -1557,6 +1635,13 @@ context::diagnostic_impl (rich_location *richloc,
                          const char *gmsgid,
                          va_list *ap, enum kind kind)
 {
+  logging::log_function_params
+    (m_logger, "diagnostics::context::diagnostic_impl")
+    .log_param_option_id ("option_id", opt_id)
+    .log_param_kind ("kind", kind)
+    .log_param_string ("gmsgid", gmsgid);
+  logging::auto_inc_depth depth_sentinel (m_logger);
+
   diagnostic_info diagnostic;
   if (kind == diagnostics::kind::permerror)
     {
@@ -1574,7 +1659,11 @@ context::diagnostic_impl (rich_location *richloc,
        diagnostic.m_option_id = opt_id;
     }
   diagnostic.m_metadata = metadata;
-  return report_diagnostic (&diagnostic);
+
+  bool ret = report_diagnostic (&diagnostic);
+  if (m_logger)
+    m_logger->log_bool_return ("diagnostics::context::diagnostic_impl", ret);
+  return ret;
 }
 
 /* Implement inform_n, warning_n, and error_n, as documented and
@@ -1588,6 +1677,13 @@ context::diagnostic_n_impl (rich_location *richloc,
                            const char *plural_gmsgid,
                            va_list *ap, enum kind kind)
 {
+  logging::log_function_params
+    (m_logger, "diagnostics::context::diagnostic_n_impl")
+    .log_param_option_id ("option_id", opt_id)
+    .log_param_kind ("kind", kind)
+    .log_params_n_gmsgids (n, singular_gmsgid, plural_gmsgid);
+  logging::auto_inc_depth depth_sentinel (m_logger);
+
   diagnostic_info diagnostic;
   unsigned long gtn;
 
@@ -1604,7 +1700,11 @@ context::diagnostic_n_impl (rich_location *richloc,
   if (kind == diagnostics::kind::warning)
     diagnostic.m_option_id = opt_id;
   diagnostic.m_metadata = metadata;
-  return report_diagnostic (&diagnostic);
+
+  bool ret = report_diagnostic (&diagnostic);
+  if (m_logger)
+    m_logger->log_bool_return ("diagnostics::context::diagnostic_n_impl", ret);
+  return ret;
 }
 
 
@@ -1706,8 +1806,13 @@ context::end_group ()
         If any diagnostics were emitted, give the context a chance
         to do something.  */
       if (m_diagnostic_groups.m_emission_count > 0)
-       for (auto sink_ : m_sinks)
-         sink_->on_end_group ();
+       {
+         DIAGNOSTICS_LOG_SCOPE_PRINTF0
+           (get_logger (),
+            "diagnostics::context::end_group: ending group");
+         for (auto sink_ : m_sinks)
+           sink_->on_end_group ();
+       }
       m_diagnostic_groups.m_emission_count = 0;
     }
   /* We're popping one level, so might need to stop inhibiting notes.  */
diff --git a/gcc/diagnostics/context.h b/gcc/diagnostics/context.h
index dea4588f96e2..9464f6adb6c9 100644
--- a/gcc/diagnostics/context.h
+++ b/gcc/diagnostics/context.h
@@ -28,6 +28,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "diagnostics/source-printing-options.h"
 #include "diagnostics/column-options.h"
 #include "diagnostics/counters.h"
+#include "diagnostics/logging.h"
 
 namespace diagnostics {
 
@@ -275,8 +276,10 @@ public:
 
   void finish ();
 
-  void dump (FILE *out) const;
-  void DEBUG_FUNCTION dump () const { dump (stderr); }
+  void dump (FILE *out, int indent) const;
+  void DEBUG_FUNCTION dump () const { dump (stderr, 0); }
+
+  logging::logger *get_logger () { return m_logger; }
 
   bool execution_failed_p () const;
 
@@ -335,18 +338,35 @@ public:
                       enum kind new_kind,
                       location_t where)
   {
+    logging::log_function_params
+      (m_logger, "diagnostics::context::classify_diagnostics")
+      .log_param_option_id ("option_id", opt_id)
+      .log_param_kind ("new_kind", new_kind)
+      .log_param_location_t ("where", where);
+    logging::auto_inc_depth depth_sentinel (m_logger);
+
     return m_option_classifier.classify_diagnostic (this,
                                                    opt_id,
                                                    new_kind,
                                                    where);
   }
 
-  void push_diagnostics (location_t where ATTRIBUTE_UNUSED)
+  void push_diagnostics (location_t where)
   {
+    logging::log_function_params
+      (m_logger, "diagnostics::context::push_diagnostics")
+      .log_param_location_t ("where", where);
+    logging::auto_inc_depth depth_sentinel (m_logger);
+
     m_option_classifier.push ();
   }
   void pop_diagnostics (location_t where)
   {
+    logging::log_function_params
+      (m_logger, "diagnostics::context::pop_diagnostics")
+      .log_param_location_t ("where", where);
+    logging::auto_inc_depth depth_sentinel (m_logger);
+
     m_option_classifier.pop (where);
   }
 
@@ -826,6 +846,11 @@ private:
      later (if the buffer is flushed), moved to other buffers, or
      discarded (if the buffer is cleared).  */
   buffer *m_diagnostic_buffer;
+
+  /* Owned by the context.
+     Debugging option: if non-NULL, report information to the logger
+     on what the context is doing.  */
+  logging::logger *m_logger;
 };
 
 /* Client supplied function to announce a diagnostic
diff --git a/gcc/diagnostics/html-sink.cc b/gcc/diagnostics/html-sink.cc
index 1fd317a8b808..64dcefeedaad 100644
--- a/gcc/diagnostics/html-sink.cc
+++ b/gcc/diagnostics/html-sink.cc
@@ -34,6 +34,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "diagnostics/buffering.h"
 #include "diagnostics/paths.h"
 #include "diagnostics/dumping.h"
+#include "diagnostics/logging.h"
 #include "diagnostics/client-data-hooks.h"
 #include "selftest.h"
 #include "diagnostics/selftest-context.h"
@@ -1320,6 +1321,8 @@ html_builder::end_group ()
 void
 html_builder::flush_to_file (FILE *outf)
 {
+  DIAGNOSTICS_LOG_SCOPE_PRINTF0 (m_context.get_logger (),
+                                "diagnostics::html_builder::flush_to_file");
   if (m_html_gen_opts.m_javascript)
     {
       gcc_assert (m_head_element);
@@ -1389,6 +1392,9 @@ public:
   on_report_diagnostic (const diagnostic_info &diagnostic,
                        enum kind orig_diag_kind) final override
   {
+    DIAGNOSTICS_LOG_SCOPE_PRINTF0
+      (get_logger (),
+       "diagnostics::html_sink::on_report_diagnostic");
     m_builder.on_report_diagnostic (diagnostic, orig_diag_kind, m_buffer);
   }
   void on_diagram (const diagram &d) final override
diff --git a/gcc/diagnostics/kinds.h b/gcc/diagnostics/kinds.h
index 7b4a1687d0e2..1357be5ef5bf 100644
--- a/gcc/diagnostics/kinds.h
+++ b/gcc/diagnostics/kinds.h
@@ -38,6 +38,7 @@ enum class kind
 };
 
 extern const char *get_text_for_kind (enum diagnostics::kind);
+extern const char *get_debug_string_for_kind (enum diagnostics::kind);
 extern const char *get_color_for_kind (enum diagnostics::kind);
 
 } // namespace diagnostics
diff --git a/gcc/diagnostics/logging.cc b/gcc/diagnostics/logging.cc
new file mode 100644
index 000000000000..cfe23d56e3d8
--- /dev/null
+++ b/gcc/diagnostics/logging.cc
@@ -0,0 +1,72 @@
+/* Utilities for implementing "dump" functions for the diagnostics subsystem.
+   Copyright (C) 2025 Free Software Foundation, Inc.
+   Contributed by David Malcolm <dmalc...@redhat.com>.
+
+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 "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "diagnostics/logging.h"
+
+namespace diagnostics {
+namespace logging {
+
+logger::logger (output_file outfile)
+: m_outfile (std::move (outfile)),
+  m_log_depth (0)
+{
+}
+
+void
+logger::log_printf (const char *fmt, ...)
+{
+  emit_indent ();
+
+  va_list ap;
+  va_start (ap, fmt);
+  vfprintf (get_stream (), fmt, ap);
+  va_end (ap);
+
+  emit_newline ();
+}
+
+void
+logger::log_bool_return (const char *function_name, bool retval)
+{
+  log_printf ("%s <- %s",
+             retval ? "true" : "false",
+             function_name);
+}
+
+/* Emit indentation to OUTFILE for the start of a log line.  */
+
+void
+logger::emit_indent () const
+{
+  fprintf (get_stream (), "%*s", get_indent (), "");
+}
+
+void
+logger::emit_newline () const
+{
+  fputc ('\n', get_stream ());
+}
+
+} // namespace logging {
+} // namespace diagnostics
diff --git a/gcc/diagnostics/logging.h b/gcc/diagnostics/logging.h
new file mode 100644
index 000000000000..2ce0166aad53
--- /dev/null
+++ b/gcc/diagnostics/logging.h
@@ -0,0 +1,230 @@
+/* Debugging code for logging what the diagnostics subsystem is doing.
+   Copyright (C) 2025 Free Software Foundation, Inc.
+   Contributed by David Malcolm <dmalc...@redhat.com>.
+
+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_DIAGNOSTICS_LOGGING_H
+#define GCC_DIAGNOSTICS_LOGGING_H
+
+#include "diagnostics/output-file.h"
+#include "diagnostics/option-id.h"
+#include "diagnostics/kinds.h"
+
+namespace diagnostics {
+
+namespace logging {
+
+/* A class for emitting a temporal log of what the diagnostics subsystem
+   is doing, for debugging.
+   We can't use pretty_printer here as we could potentially be debugging
+   pretty-printing itself.  */
+
+class logger
+{
+public:
+  logger (output_file outfile);
+
+  /* High-level functions that emit a line of text.  */
+  void log_printf (const char *fmt, ...)
+    __attribute__ ((__format__ (printf, 2, 3)));
+  void log_bool_return (const char *function_name, bool retval);
+
+  /* Lower-level functions for building up a line of text.  */
+  void emit_indent () const;
+  void emit_newline () const;
+
+  FILE *get_stream () const
+  {
+    return m_outfile.get_open_file ();
+  }
+
+  int get_indent () const { return m_log_depth * 2; }
+
+  void inc_depth () { m_log_depth++; }
+  void dec_depth () { m_log_depth--; }
+
+private:
+  output_file m_outfile;
+  int m_log_depth;
+};
+
+/* RAII class for pushing/popping depth within a logger.  */
+
+class auto_inc_depth
+{
+public:
+  auto_inc_depth (logger *log)
+  : m_logger (log)
+  {
+    if (m_logger)
+      m_logger->inc_depth ();
+  }
+  ~auto_inc_depth ()
+  {
+    if (m_logger)
+      m_logger->dec_depth ();
+  }
+
+private:
+  logger *m_logger;
+};
+
+/* Class for debugging function call parameters.  */
+
+class log_function_params
+{
+public:
+  log_function_params (logger *logger_, const char *name)
+  : m_logger (logger_),
+    m_first_param (true)
+  {
+    if (m_logger)
+      {
+       m_logger->emit_indent ();
+       fprintf (m_logger->get_stream (), "%s (", name);
+      }
+  }
+  ~log_function_params ()
+  {
+    if (m_logger)
+      {
+       fprintf (m_logger->get_stream (), ")");
+       m_logger->emit_newline ();
+      }
+  }
+
+  log_function_params &
+  log_param_string (const char *name, const char *value)
+  {
+    if (m_logger)
+      {
+       add_any_comma ();
+       fprintf (m_logger->get_stream (), "%s: \"%s\"", name, value);
+      }
+    return *this;
+  }
+
+  log_function_params &
+  log_param_location_t (const char *name, location_t value)
+  {
+    if (m_logger)
+      {
+       add_any_comma ();
+       fprintf (m_logger->get_stream (),
+                "%s: " HOST_SIZE_T_PRINT_HEX,
+                name, (size_t)value);
+      }
+    return *this;
+  }
+
+  log_function_params &
+  log_param_rich_location (const char *name, const rich_location *richloc)
+  {
+    if (m_logger)
+      {
+       add_any_comma ();
+       fprintf (m_logger->get_stream (),
+                "%s: %p",
+                name, const_cast<void *> ((const void *)richloc));
+      }
+    return *this;
+  }
+
+  log_function_params &
+  log_param_option_id (const char *name, diagnostics::option_id value)
+  {
+    if (m_logger)
+      {
+       add_any_comma ();
+       fprintf (m_logger->get_stream (), "%s: %i", name, value.m_idx);
+      }
+    return *this;
+  }
+
+  log_function_params &
+  log_param_kind (const char *name, enum diagnostics::kind value)
+  {
+    if (m_logger)
+      {
+       add_any_comma ();
+       fprintf (m_logger->get_stream (), "%s: %s",
+                name, get_debug_string_for_kind (value));
+      }
+    return *this;
+  }
+
+  log_function_params &
+  log_param_uhwi (const char *name, unsigned HOST_WIDE_INT value)
+  {
+    if (m_logger)
+      {
+       add_any_comma ();
+       fprintf (m_logger->get_stream (),
+                "%s: " HOST_WIDE_INT_PRINT_DEC,
+                name, value);
+      }
+    return *this;
+  }
+
+  log_function_params &
+  log_params_n_gmsgids (unsigned HOST_WIDE_INT n,
+                       const char *singular_gmsgid,
+                       const char *plural_gmsgid)
+  {
+    return log_param_uhwi ("n", n)
+      .log_param_string ("singular_gmsgid", singular_gmsgid)
+      .log_param_string ("plural_gmsgid", plural_gmsgid);
+  }
+
+private:
+  void
+  add_any_comma ()
+  {
+    gcc_assert (m_logger);
+    if (m_first_param)
+      m_first_param = false;
+    else
+      fprintf (m_logger->get_stream (), ", ");
+  }
+
+  logger *m_logger;
+  bool m_first_param;
+};
+
+} // namespace logging
+} // namespace diagnostics
+
+/* Various macros for logging a formatted line, and indenting
+   further log messages within a scope.  */
+
+#define DIAGNOSTICS_LOG_SCOPE_PRINTF0(LOGGER, FMT) \
+  if (LOGGER)                                                  \
+    (LOGGER)->log_printf ((FMT));                              \
+  diagnostics::logging::auto_inc_depth depth_sentinel (LOGGER);
+
+#define DIAGNOSTICS_LOG_SCOPE_PRINTF1(LOGGER, FMT, ARG0)       \
+  if (LOGGER)                                                  \
+    (LOGGER)->log_printf ((FMT), (ARG0));                      \
+  diagnostics::logging::auto_inc_depth depth_sentinel (LOGGER);
+
+#define DIAGNOSTICS_LOG_SCOPE_PRINTF2(LOGGER, FMT, ARG0, ARG1) \
+  if (LOGGER)                                                  \
+    (LOGGER)->log_printf ((FMT), (ARG0), (ARG1));              \
+  diagnostics::logging::auto_inc_depth depth_sentinel (LOGGER);
+
+#endif /* ! GCC_DIAGNOSTICS_LOGGING_H */
diff --git a/gcc/diagnostics/output-file.h b/gcc/diagnostics/output-file.h
index f93638793341..827adcdc2b09 100644
--- a/gcc/diagnostics/output-file.h
+++ b/gcc/diagnostics/output-file.h
@@ -21,6 +21,8 @@ along with GCC; see the file COPYING3.  If not see
 #ifndef GCC_DIAGNOSTICS_OUTPUT_FILE_H
 #define GCC_DIAGNOSTICS_OUTPUT_FILE_H
 
+#include "label-text.h"
+
 namespace diagnostics {
 
 /* RAII class for wrapping a FILE * that could be borrowed or owned,
diff --git a/gcc/diagnostics/sarif-sink.cc b/gcc/diagnostics/sarif-sink.cc
index 7526c16be08b..c85a35e4f631 100644
--- a/gcc/diagnostics/sarif-sink.cc
+++ b/gcc/diagnostics/sarif-sink.cc
@@ -34,6 +34,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "diagnostics/sink.h"
 #include "diagnostics/buffering.h"
 #include "diagnostics/dumping.h"
+#include "diagnostics/logging.h"
 #include "json.h"
 #include "cpplib.h"
 #include "diagnostics/logical-locations.h"
@@ -1944,6 +1945,8 @@ report_global_digraph (const 
lazily_created<digraphs::digraph> &ldg)
 std::unique_ptr<sarif_log>
 sarif_builder::flush_to_object ()
 {
+  DIAGNOSTICS_LOG_SCOPE_PRINTF0 (m_context.get_logger (),
+                                "diagnostics::sarif_builder::flush_to_object");
   m_invocation_obj->prepare_to_flush (*this);
   std::unique_ptr<sarif_log> top
     = make_top_level_object (std::move (m_invocation_obj),
@@ -1959,6 +1962,8 @@ sarif_builder::flush_to_object ()
 void
 sarif_builder::flush_to_file (FILE *outf)
 {
+  DIAGNOSTICS_LOG_SCOPE_PRINTF0 (m_context.get_logger (),
+                                "diagnostics::sarif_builder::flush_to_file");
   std::unique_ptr<sarif_log> top = flush_to_object ();
   m_serialization_format->write_to_file (outf, *top);
 }
@@ -3940,6 +3945,9 @@ public:
   on_report_diagnostic (const diagnostic_info &diagnostic,
                        enum kind orig_diag_kind) final override
   {
+    DIAGNOSTICS_LOG_SCOPE_PRINTF0
+      (get_logger (),
+       "diagnostics::sarif_sink::on_report_diagnostic");
     m_builder.on_report_diagnostic (diagnostic, orig_diag_kind, m_buffer);
   }
   void on_diagram (const diagram &d) final override
diff --git a/gcc/diagnostics/sink.h b/gcc/diagnostics/sink.h
index 24eb70761fd1..aaa6c50ab214 100644
--- a/gcc/diagnostics/sink.h
+++ b/gcc/diagnostics/sink.h
@@ -90,6 +90,8 @@ public:
 
   void DEBUG_FUNCTION dump () const { dump (stderr, 0); }
 
+  logging::logger *get_logger () { return m_context.get_logger (); }
+
 protected:
   sink (context &dc)
   : m_context (dc),
diff --git a/gcc/diagnostics/text-sink.cc b/gcc/diagnostics/text-sink.cc
index 48b369c8af63..d4cfb89347f4 100644
--- a/gcc/diagnostics/text-sink.cc
+++ b/gcc/diagnostics/text-sink.cc
@@ -34,6 +34,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "diagnostics/text-sink.h"
 #include "diagnostics/buffering.h"
 #include "diagnostics/dumping.h"
+#include "diagnostics/logging.h"
 #include "text-art/theme.h"
 
 /* Disable warnings about quoting issues in the pp_xxx calls below
@@ -205,6 +206,11 @@ void
 text_sink::on_report_diagnostic (const diagnostic_info &diagnostic,
                                 enum kind orig_diag_kind)
 {
+  auto logger = get_logger ();
+  DIAGNOSTICS_LOG_SCOPE_PRINTF0
+    (logger,
+     "diagnostics::text_sink::on_report_diagnostic");
+
   pretty_printer *pp = get_printer ();
 
   (*text_starter (&m_context)) (*this, &diagnostic);
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index fa051f2bc47d..5c0521744acd 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -37907,6 +37907,16 @@ compilation which is to be used as input to the next 
stage: for example,
 the output of the preprocessor, which is the input to the compiler
 proper.
 
+@vindex GCC_DIAGNOSTICS_LOG
+@item GCC_DIAGNOSTICS_LOG
+If @env{GCC_DIAGNOSTICS_LOG} is set, then additional information
+about the diagnostics subsystem will be emitted.  If it is set to an empty
+value, then the information will be written to stderr; otherwise, GCC will
+attempt to open that file and write the information there.
+
+The precise content and format of the information is subject to change;
+it is intended for use by GCC developers, rather than end-users.
+
 @vindex GCC_COMPARE_DEBUG
 @item GCC_COMPARE_DEBUG
 Setting @env{GCC_COMPARE_DEBUG} is nearly equivalent to passing
diff --git a/gcc/opts-diagnostic.cc b/gcc/opts-diagnostic.cc
index 70d7d74af611..0e0296ab5aa7 100644
--- a/gcc/opts-diagnostic.cc
+++ b/gcc/opts-diagnostic.cc
@@ -32,6 +32,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "intl.h"
 #include "diagnostic.h"
 #include "diagnostics/output-spec.h"
+#include "diagnostics/logging.h"
 #include "opts.h"
 #include "options.h"
 
@@ -78,6 +79,8 @@ handle_OPT_fdiagnostics_add_output_ (const gcc_options &opts,
   gcc_assert (line_table);
 
   const char *const option_name = "-fdiagnostics-add-output=";
+  DIAGNOSTICS_LOG_SCOPE_PRINTF2 (dc.get_logger (),
+                                "handling: %s%s", option_name, arg);
   opt_spec_context ctxt (opts, dc, line_table, loc, option_name);
   auto sink = ctxt.parse_and_make_sink (arg, dc);
   if (!sink)
@@ -97,6 +100,8 @@ handle_OPT_fdiagnostics_set_output_ (const gcc_options &opts,
   gcc_assert (line_table);
 
   const char *const option_name = "-fdiagnostics-set-output=";
+  DIAGNOSTICS_LOG_SCOPE_PRINTF2 (dc.get_logger (),
+                                "handling: %s%s", option_name, arg);
   opt_spec_context ctxt (opts, dc, line_table, loc, option_name);
   auto sink = ctxt.parse_and_make_sink (arg, dc);
   if (!sink)

Reply via email to