From 6a7b9c574a9c8bebe81e6b2b904fedd597f3de89 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Thu, 11 Sep 2025 16:28:57 -0400
Subject: [PATCH 72/98] FIXME (unfinished) WIP on adding tests for crashes
 whilst buffering

---
 .../plugin/crash-test-buffered-ice-html.py    | 27 +++++++++++
 .../plugin/crash-test-buffered-ice-sarif.py   | 47 +++++++++++++++++++
 .../gcc.dg/plugin/crash-test-buffered-ice.c   | 24 ++++++++++
 ...h-test-buffered-write-through-null-html.py | 27 +++++++++++
 ...-test-buffered-write-through-null-sarif.py | 47 +++++++++++++++++++
 .../crash-test-buffered-write-through-null.c  | 24 ++++++++++
 .../gcc.dg/plugin/crash_test_plugin.cc        | 24 ++++++++--
 gcc/testsuite/gcc.dg/plugin/plugin.exp        |  2 +
 8 files changed, 218 insertions(+), 4 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/plugin/crash-test-buffered-ice-html.py
 create mode 100644 gcc/testsuite/gcc.dg/plugin/crash-test-buffered-ice-sarif.py
 create mode 100644 gcc/testsuite/gcc.dg/plugin/crash-test-buffered-ice.c
 create mode 100644 gcc/testsuite/gcc.dg/plugin/crash-test-buffered-write-through-null-html.py
 create mode 100644 gcc/testsuite/gcc.dg/plugin/crash-test-buffered-write-through-null-sarif.py
 create mode 100644 gcc/testsuite/gcc.dg/plugin/crash-test-buffered-write-through-null.c

diff --git a/gcc/testsuite/gcc.dg/plugin/crash-test-buffered-ice-html.py b/gcc/testsuite/gcc.dg/plugin/crash-test-buffered-ice-html.py
new file mode 100644
index 000000000000..ae37341f4e93
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/plugin/crash-test-buffered-ice-html.py
@@ -0,0 +1,27 @@
+from htmltest import *
+
+import pytest
+
+@pytest.fixture(scope='function', autouse=True)
+def html_tree():
+    return html_tree_from_env()
+
+def test_results(html_tree):
+    root = html_tree.getroot ()
+    assert root.tag == make_tag('html')
+
+    head = root.find('xhtml:head', ns)
+    assert head is not None
+
+    body = root.find('xhtml:body', ns)
+    assert body is not None
+
+    diag_list = body.find("./xhtml:div[@class='gcc-diagnostic-list']", ns)
+    assert len(diag_list)
+
+    # The ICE
+    ice = diag_list.find('xhtml:div[@class="gcc-diagnostic"]', ns)
+    assert ice is not None
+    ice_msg = ice.find("./xhtml:div[@class='gcc-message']", ns)
+    assert ice_msg is not None
+    assert ice_msg.text == "Segmentation fault"
diff --git a/gcc/testsuite/gcc.dg/plugin/crash-test-buffered-ice-sarif.py b/gcc/testsuite/gcc.dg/plugin/crash-test-buffered-ice-sarif.py
new file mode 100644
index 000000000000..97f6606bd8e7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/plugin/crash-test-buffered-ice-sarif.py
@@ -0,0 +1,47 @@
+from sarif import *
+
+import pytest
+
+@pytest.fixture(scope='function', autouse=True)
+def sarif():
+    return sarif_from_env()
+
+def test_execution_failed(sarif):
+    runs = sarif['runs']
+    run = runs[0]
+
+    invocations = run['invocations']
+    assert len(invocations) == 1
+    invocation = invocations[0]
+
+    assert invocation['executionSuccessful'] == False
+
+def test_notification(sarif):
+    # We expect an execution notification for the ICE in the invocation
+    runs = sarif['runs']
+    run = runs[0]
+
+    invocations = run['invocations']
+    assert len(invocations) == 1
+    invocation = invocations[0]
+
+    assert invocation['executionSuccessful'] == False
+
+    notifications = invocation['toolExecutionNotifications']
+    assert len(notifications) == 1
+
+    notification = notifications[0]
+
+    assert notification['message']['text'] == "Segmentation fault"
+    assert notification['level'] == 'error'
+
+    loc0 = notification['locations'][0]
+    assert get_location_artifact_uri(loc0).endswith('crash-test-buffered-write-through-null.c')
+    assert 'inject_write_through_null ();' in get_location_snippet_text(loc0)
+
+    # We may have backtrace information
+    if 'properties' in notification:
+        backtrace = notification['properties']['gcc/backtrace']
+        assert 'frames' in backtrace
+        # Ideally we should have a frame for pass_crash_test::execute(function*)
+        # but we can't rely on this.
diff --git a/gcc/testsuite/gcc.dg/plugin/crash-test-buffered-ice.c b/gcc/testsuite/gcc.dg/plugin/crash-test-buffered-ice.c
new file mode 100644
index 000000000000..274e5f10a86e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/plugin/crash-test-buffered-ice.c
@@ -0,0 +1,24 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-fno-report-bug" } */
+/* { dg-additional-options "-fplugin-arg-crash_test_plugin-buffered" } */
+/* { dg-additional-options "-fdiagnostics-add-output=sarif" } */
+/* { dg-additional-options "-fdiagnostics-add-output=experimental-html:javascript=no" } */
+
+extern void inject_ice (void);
+
+void test_1 (void)
+{
+  inject_ice (); /* { dg-ice "I'm sorry Dave, I'm afraid I can't do that" } */
+  /* { dg-regexp "during GIMPLE pass: crash_test" } */
+}
+
+/* Verify that some JSON was written to a file with the expected name.  */
+/* { dg-final { verify-sarif-file } } */
+
+/* Use a Python script to verify various properties about the generated
+   .sarif file:
+   { dg-final { run-sarif-pytest crash-test-buffered-ice.c "crash-test-buffered-ice-sarif.py" } } */
+
+/* Use a Python script to verify various properties about the generated
+   .html file:
+   { dg-final { run-html-pytest crash-test-buffered-ice.c "crash-test-buffered-ice-html.py" } } */
diff --git a/gcc/testsuite/gcc.dg/plugin/crash-test-buffered-write-through-null-html.py b/gcc/testsuite/gcc.dg/plugin/crash-test-buffered-write-through-null-html.py
new file mode 100644
index 000000000000..ae37341f4e93
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/plugin/crash-test-buffered-write-through-null-html.py
@@ -0,0 +1,27 @@
+from htmltest import *
+
+import pytest
+
+@pytest.fixture(scope='function', autouse=True)
+def html_tree():
+    return html_tree_from_env()
+
+def test_results(html_tree):
+    root = html_tree.getroot ()
+    assert root.tag == make_tag('html')
+
+    head = root.find('xhtml:head', ns)
+    assert head is not None
+
+    body = root.find('xhtml:body', ns)
+    assert body is not None
+
+    diag_list = body.find("./xhtml:div[@class='gcc-diagnostic-list']", ns)
+    assert len(diag_list)
+
+    # The ICE
+    ice = diag_list.find('xhtml:div[@class="gcc-diagnostic"]', ns)
+    assert ice is not None
+    ice_msg = ice.find("./xhtml:div[@class='gcc-message']", ns)
+    assert ice_msg is not None
+    assert ice_msg.text == "Segmentation fault"
diff --git a/gcc/testsuite/gcc.dg/plugin/crash-test-buffered-write-through-null-sarif.py b/gcc/testsuite/gcc.dg/plugin/crash-test-buffered-write-through-null-sarif.py
new file mode 100644
index 000000000000..97f6606bd8e7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/plugin/crash-test-buffered-write-through-null-sarif.py
@@ -0,0 +1,47 @@
+from sarif import *
+
+import pytest
+
+@pytest.fixture(scope='function', autouse=True)
+def sarif():
+    return sarif_from_env()
+
+def test_execution_failed(sarif):
+    runs = sarif['runs']
+    run = runs[0]
+
+    invocations = run['invocations']
+    assert len(invocations) == 1
+    invocation = invocations[0]
+
+    assert invocation['executionSuccessful'] == False
+
+def test_notification(sarif):
+    # We expect an execution notification for the ICE in the invocation
+    runs = sarif['runs']
+    run = runs[0]
+
+    invocations = run['invocations']
+    assert len(invocations) == 1
+    invocation = invocations[0]
+
+    assert invocation['executionSuccessful'] == False
+
+    notifications = invocation['toolExecutionNotifications']
+    assert len(notifications) == 1
+
+    notification = notifications[0]
+
+    assert notification['message']['text'] == "Segmentation fault"
+    assert notification['level'] == 'error'
+
+    loc0 = notification['locations'][0]
+    assert get_location_artifact_uri(loc0).endswith('crash-test-buffered-write-through-null.c')
+    assert 'inject_write_through_null ();' in get_location_snippet_text(loc0)
+
+    # We may have backtrace information
+    if 'properties' in notification:
+        backtrace = notification['properties']['gcc/backtrace']
+        assert 'frames' in backtrace
+        # Ideally we should have a frame for pass_crash_test::execute(function*)
+        # but we can't rely on this.
diff --git a/gcc/testsuite/gcc.dg/plugin/crash-test-buffered-write-through-null.c b/gcc/testsuite/gcc.dg/plugin/crash-test-buffered-write-through-null.c
new file mode 100644
index 000000000000..926a87e763f4
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/plugin/crash-test-buffered-write-through-null.c
@@ -0,0 +1,24 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-fno-report-bug" } */
+/* { dg-additional-options "-fplugin-arg-crash_test_plugin-buffered" } */
+/* { dg-additional-options "-fdiagnostics-add-output=sarif" } */
+/* { dg-additional-options "-fdiagnostics-add-output=experimental-html:javascript=no" } */
+
+extern void inject_write_through_null (void);
+
+void test_inject_write_through_null (void)
+{
+  inject_write_through_null (); /* { dg-ice "Segmentation fault" } */ 
+  /* { dg-regexp "during GIMPLE pass: crash_test" } */
+}
+
+/* Verify that some JSON was written to a file with the expected name.  */
+/* { dg-final { verify-sarif-file } } */
+
+/* Use a Python script to verify various properties about the generated
+   .sarif file:
+   { dg-final { run-sarif-pytest crash-test-buffered-write-through-null.c "crash-test-buffered-write-through-null-sarif.py" } } */
+
+/* Use a Python script to verify various properties about the generated
+   .html file:
+   { dg-final { run-html-pytest crash-test-buffered-write-through-null.c "crash-test-buffered-write-through-null-html.py" } } */
diff --git a/gcc/testsuite/gcc.dg/plugin/crash_test_plugin.cc b/gcc/testsuite/gcc.dg/plugin/crash_test_plugin.cc
index 27c027da6a67..73a5b1052fb1 100644
--- a/gcc/testsuite/gcc.dg/plugin/crash_test_plugin.cc
+++ b/gcc/testsuite/gcc.dg/plugin/crash_test_plugin.cc
@@ -28,6 +28,7 @@
 #include "c-family/c-common.h"
 #include "diagnostic.h"
 #include "context.h"
+#include "diagnostics/buffering.h"
 
 int plugin_is_GPL_compatible;
 
@@ -50,9 +51,10 @@ const pass_data pass_data_crash_test =
 class pass_crash_test : public gimple_opt_pass
 {
 public:
-  pass_crash_test (gcc::context *ctxt, bool nested)
+  pass_crash_test (gcc::context *ctxt, bool buffered, bool nested)
   : gimple_opt_pass (pass_data_crash_test, ctxt),
-    m_nested (nested)
+    m_buffered (buffered),
+    m_nested (nested)      
   {}
 
   /* opt_pass methods: */
@@ -60,6 +62,9 @@ public:
   unsigned int execute (function *) final override;
 
 private:
+  // If true, then inject the crash whilst global_dc has an active buffer
+  bool m_buffered;
+
   /* If true, inject the crash whilst within a nested diagnostic
      (PR diagnostics/121876).  */
   bool m_nested;
@@ -94,6 +99,11 @@ pass_crash_test::execute (function *fun)
   gimple_stmt_iterator gsi;
   basic_block bb;
 
+  diagnostics::buffer buf (*global_dc);
+
+  if (m_buffered)
+    global_dc->set_diagnostic_buffer (&buf);
+
   FOR_EACH_BB_FN (bb, fun)
     for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
       {
@@ -136,6 +146,9 @@ pass_crash_test::execute (function *fun)
 	  }
       }
 
+  if (m_buffered)
+    global_dc->set_diagnostic_buffer (nullptr);
+
   return 0;
 }
 
@@ -154,15 +167,18 @@ plugin_init (struct plugin_name_args *plugin_info,
   if (!plugin_default_version_check (version, &gcc_version))
     return 1;
 
+  bool buffered = false;
   bool nested = false;
 
   for (int i = 0; i < argc; i++)
     {
-      if (!strcmp (argv[i].key, "nested"))
+      if (!strcmp (argv[i].key, "buffered"))
+	buffered = true;
+      else if (!strcmp (argv[i].key, "nested"))
 	nested = true;
     }
 
-  pass_info.pass = new pass_crash_test (g, nested);
+  pass_info.pass = new pass_crash_test (g, buffered, nested);
   pass_info.reference_pass_name = "*warn_function_noreturn";
   pass_info.ref_pass_instance_number = 1;
   pass_info.pos_op = PASS_POS_INSERT_AFTER;
diff --git a/gcc/testsuite/gcc.dg/plugin/plugin.exp b/gcc/testsuite/gcc.dg/plugin/plugin.exp
index 5cd089a2ed04..d1a650510443 100644
--- a/gcc/testsuite/gcc.dg/plugin/plugin.exp
+++ b/gcc/testsuite/gcc.dg/plugin/plugin.exp
@@ -68,6 +68,8 @@ set plugin_test_list [list \
     { poly-int-06_plugin.cc poly-int-test-1.c } \
     { poly-int-07_plugin.cc poly-int-test-1.c } \
     { crash_test_plugin.cc \
+	  crash-test-buffered-ice.c \
+	  crash-test-buffered-write-through-null.c \
 	  crash-test-ice-in-header-sarif-2.1.c \
 	  crash-test-ice-in-header-sarif-2.2.c \
 	  crash-test-ice-sarif.c \
-- 
2.49.0

