diff mbox series

[pushed:,r15-6285] libgdiagnostics: consolidate logical locations

Message ID 20241216182025.31929-1-dmalcolm@redhat.com
State New
Headers show
Series [pushed:,r15-6285] libgdiagnostics: consolidate logical locations | expand

Commit Message

David Malcolm Dec. 16, 2024, 6:20 p.m. UTC
This patch updates diagnostic_manager_new_logical_location so
that repeated calls with the same input values yield the same
instance of diagnostic_logical_location.

Doing so allows the path-printing logic to properly consolidate runs of
events, whereas previously it could treat each event as having a
distinct logical location, and thus require them to be printed
separately; this greatly improves the output of sarif-replay when
displaying execution paths.

Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
Pushed to trunk as r15-6285-g2af541920787e3.

gcc/ChangeLog:
	* doc/libgdiagnostics/topics/logical-locations.rst
	(diagnostic_manager_new_logical_location): Add note about repeated
	calls.
	* libgdiagnostics.cc: Define INCLUDE_MAP.
	(class owned_nullable_string): Add copy ctor and move ctor.
	(owned_nullable_string::operator<): New.
	(diagnostic_logical_location::operator<): New.
	(diagnostic_manager::new_logical_location): Use m_logical_locs to
	"uniquify" instances, converting it to a std::map.
	(diagnostic_manager::logical_locs_map_t): New typedef.
	(diagnostic_manager::t m_logical_locs): Convert from a std::vector
	to a std::map.
	(diagnostic_execution_path::same_function_p): Update comment.

gcc/testsuite/ChangeLog:
	* libgdiagnostics.dg/test-logical-location.c: Include <assert.h>.
	Verify that creating a diagnostic_logical_location with equal
	values yields the same instance.
	* sarif-replay.dg/2.1.0-valid/malloc-vs-local-4.c.sarif: New test.
	* sarif-replay.dg/2.1.0-valid/signal-1.c.moved.sarif: Update
	expected output to show logical location and for consolidation of
	events into runs.
	* sarif-replay.dg/2.1.0-valid/signal-1.c.sarif: Likewise.
	* sarif-replay.dg/2.1.0-valid/spec-example-4.sarif: Likewise.

Signed-off-by: David Malcolm <dmalcolm@redhat.com>
---
 .../topics/logical-locations.rst              |   6 +
 gcc/libgdiagnostics.cc                        |  58 ++-
 .../test-logical-location.c                   |  13 +
 .../2.1.0-valid/malloc-vs-local-4.c.sarif     | 402 ++++++++++++++++++
 .../2.1.0-valid/signal-1.c.moved.sarif        |  25 +-
 .../2.1.0-valid/signal-1.c.sarif              |  25 +-
 .../2.1.0-valid/spec-example-4.sarif          |  11 +-
 7 files changed, 497 insertions(+), 43 deletions(-)
 create mode 100644 gcc/testsuite/sarif-replay.dg/2.1.0-valid/malloc-vs-local-4.c.sarif
diff mbox series

Patch

diff --git a/gcc/doc/libgdiagnostics/topics/logical-locations.rst b/gcc/doc/libgdiagnostics/topics/logical-locations.rst
index 85900b6344f2..70bbb00c486d 100644
--- a/gcc/doc/libgdiagnostics/topics/logical-locations.rst
+++ b/gcc/doc/libgdiagnostics/topics/logical-locations.rst
@@ -88,6 +88,12 @@  source location
    the SARIF logicalLocation ``decoratedName`` property
    (SARIF v2.1.0 `§3.33.6 <https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/sarif-v2.1.0-errata01-os-complete.html#_Toc141790975>`_).
 
+   Repeated calls to :func:`diagnostic_manager_new_logical_location` with
+   "equal" input values on the same :type:`diagnostic_manager` will return
+   the same instance of :type:`diagnostic_logical_location`.  "Equal" here
+   includes different string buffers that compare as equal with
+   :func:``strcmp`.
+
 .. function:: void diagnostic_manager_debug_dump_logical_location (const diagnostic_manager *diag_mgr, \
                                                                    const diagnostic_logical_location *loc, \
                                                                    FILE *out)
diff --git a/gcc/libgdiagnostics.cc b/gcc/libgdiagnostics.cc
index 00f8fefe838e..126ba747f796 100644
--- a/gcc/libgdiagnostics.cc
+++ b/gcc/libgdiagnostics.cc
@@ -18,6 +18,7 @@  along with GCC; see the file COPYING3.  If not see
 <http://www.gnu.org/licenses/>.  */
 
 #include "config.h"
+#define INCLUDE_MAP
 #define INCLUDE_VECTOR
 #include "system.h"
 #include "coretypes.h"
@@ -43,6 +44,15 @@  public:
   : m_str (str ? ::xstrdup (str) : nullptr)
   {
   }
+  owned_nullable_string (const owned_nullable_string &other)
+  : m_str (other.xstrdup ())
+  {
+  }
+  owned_nullable_string (owned_nullable_string &&other)
+  {
+    m_str = other.m_str;
+    other.m_str = nullptr;
+  }
 
   ~owned_nullable_string ()
   {
@@ -62,6 +72,16 @@  public:
     return m_str ? ::xstrdup (m_str) : nullptr;
   }
 
+  bool
+  operator< (const owned_nullable_string &other) const
+  {
+    if (m_str && other.m_str)
+      return strcmp (m_str, other.m_str) < 0;
+    if (m_str == nullptr && other.m_str != nullptr)
+      return true;
+    return false;
+  }
+
 private:
   char *m_str;
 };
@@ -205,6 +225,23 @@  struct diagnostic_logical_location : public logical_location
     return label_text::borrow (m_short_name.get_str ());
   }
 
+  bool
+  operator< (const diagnostic_logical_location &other) const
+  {
+    if (m_kind < other.m_kind)
+      return true;
+    if (m_parent < other.m_parent)
+      return true;
+    if (m_short_name < other.m_short_name)
+      return true;
+    if (m_fully_qualified_name < other.m_fully_qualified_name)
+      return true;
+    if (m_decorated_name < other.m_decorated_name)
+      return true;
+
+    return false;
+  }
+
 private:
   enum diagnostic_logical_location_kind_t m_kind;
   const diagnostic_logical_location *m_parent;
@@ -445,6 +482,16 @@  public:
 			const char *fully_qualified_name,
 			const char *decorated_name)
   {
+    /* Use m_logical_locs to "uniquify" instances.  */
+    diagnostic_logical_location key (kind,
+				     parent,
+				     short_name,
+				     fully_qualified_name,
+				     decorated_name);
+    auto iter = m_logical_locs.find (key);
+    if (iter != m_logical_locs.end ())
+      return (*iter).second.get ();
+
     std::unique_ptr<diagnostic_logical_location> logical_loc
       = ::make_unique<diagnostic_logical_location> (kind,
 						    parent,
@@ -452,7 +499,9 @@  public:
 						    fully_qualified_name,
 						    decorated_name);
     const diagnostic_logical_location *result = logical_loc.get ();
-    m_logical_locs.push_back (std::move (logical_loc));
+    m_logical_locs.insert
+      (logical_locs_map_t::value_type (std::move (key),
+				       std::move (logical_loc)));
     return result;
   }
 
@@ -552,7 +601,9 @@  private:
   hash_map<nofree_string_hash, diagnostic_file *> m_str_to_file_map;
   hash_map<int_hash<location_t, UNKNOWN_LOCATION, location_t (-1)>,
 	   diagnostic_physical_location *> m_location_t_map;
-  std::vector<std::unique_ptr<diagnostic_logical_location>> m_logical_locs;
+  typedef std::map<diagnostic_logical_location,
+		   std::unique_ptr<diagnostic_logical_location>> logical_locs_map_t;
+  logical_locs_map_t m_logical_locs;
   const diagnostic *m_current_diag;
   const diagnostic_logical_location *m_prev_diag_logical_loc;
   std::unique_ptr<edit_context> m_edit_context;
@@ -758,8 +809,7 @@  struct diagnostic_execution_path : public diagnostic_path
     const logical_location *logical_loc_b
       = m_events[event_idx_b]->get_logical_location ();
 
-    // TODO:
-    /* Pointer equality, so we may want to uniqify logical loc ptrs.  */
+    /* Pointer equality, as we uniqify logical location instances.  */
     return logical_loc_a == logical_loc_b;
   }
 
diff --git a/gcc/testsuite/libgdiagnostics.dg/test-logical-location.c b/gcc/testsuite/libgdiagnostics.dg/test-logical-location.c
index d853983b2bcf..140891938970 100644
--- a/gcc/testsuite/libgdiagnostics.dg/test-logical-location.c
+++ b/gcc/testsuite/libgdiagnostics.dg/test-logical-location.c
@@ -19,6 +19,8 @@  PRINT "hello world!";
 */
 const int line_num = __LINE__ - 2;
 
+#include <assert.h>
+
 int
 main ()
 {
@@ -60,6 +62,17 @@  main ()
   diagnostic_finish (d, "can't find %qs", "foo");
   /* end quoted source */
 
+  /* Verify that creating a diagnostic_logical_location with equal values 
+     yields the same instance.  */
+  const diagnostic_logical_location *dup
+    = diagnostic_manager_new_logical_location (diag_mgr,
+					       DIAGNOSTIC_LOGICAL_LOCATION_KIND_FUNCTION,
+					       NULL, /* parent */
+					       "test_short_name",
+					       "test_qualified_name",
+					       "test_decorated_name");
+  assert (dup == logical_loc); 
+
   return end_test ();
 }
 
diff --git a/gcc/testsuite/sarif-replay.dg/2.1.0-valid/malloc-vs-local-4.c.sarif b/gcc/testsuite/sarif-replay.dg/2.1.0-valid/malloc-vs-local-4.c.sarif
new file mode 100644
index 000000000000..5fd8e628b47d
--- /dev/null
+++ b/gcc/testsuite/sarif-replay.dg/2.1.0-valid/malloc-vs-local-4.c.sarif
@@ -0,0 +1,402 @@ 
+/* Test a replay of a .sarif file generated from GCC testsuite.
+
+   The dg directives were stripped out from the generated .sarif
+   to avoid confusing DejaGnu for this test.   */
+
+{"$schema": "https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json",
+ "version": "2.1.0",
+ "runs": [{"tool": {"driver": {"name": "GNU C23",
+                               "fullName": "GNU C23 (GCC) version 15.0.0 20241211 (experimental) (x86_64-pc-linux-gnu)",
+                               "version": "15.0.0 20241211 (experimental)",
+                               "informationUri": "https://gcc.gnu.org/gcc-15/",
+                               "rules": [{"id": "-Wanalyzer-possible-null-dereference",
+                                          "helpUri": "https://gcc.gnu.org/onlinedocs/gcc/Static-Analyzer-Options.html#index-Wanalyzer-possible-null-dereference"},
+                                         {"id": "-Wanalyzer-double-free",
+                                          "helpUri": "https://gcc.gnu.org/onlinedocs/gcc/Static-Analyzer-Options.html#index-Wanalyzer-double-free"}]}},
+           "taxonomies": [{"name": "CWE",
+                           "version": "4.7",
+                           "organization": "MITRE",
+                           "shortDescription": {"text": "The MITRE Common Weakness Enumeration"},
+                           "taxa": [{"id": "690",
+                                     "helpUri": "https://cwe.mitre.org/data/definitions/690.html"},
+                                    {"id": "415",
+                                     "helpUri": "https://cwe.mitre.org/data/definitions/415.html"}]}],
+           "invocations": [{"arguments": ["./cc1",
+                                          "-quiet",
+                                          "-iprefix",
+                                          "/home/david/coding/gcc-newgit-more-taint/build-with-libgdiagnostics/gcc/../lib/gcc/x86_64-pc-linux-gnu/15.0.0/",
+                                          "-isystem",
+                                          "./include",
+                                          "-isystem",
+                                          "./include-fixed",
+                                          "/not/a/real/path/malloc-vs-local-4.c",
+                                          "-quiet",
+                                          "-dumpbase",
+                                          "malloc-vs-local-4.c",
+                                          "-dumpbase-ext",
+                                          ".c",
+                                          "-mtune=generic",
+                                          "-march=x86-64",
+                                          "-fanalyzer",
+                                          "-fdiagnostics-add-output=sarif",
+                                          "-o",
+                                          "malloc-vs-local-4.s"],
+                            "workingDirectory": {"uri": "/home/david/coding/gcc-newgit-more-taint/build-with-libgdiagnostics/gcc"},
+                            "startTimeUtc": "2024-12-12T22:09:12Z",
+                            "executionSuccessful": true,
+                            "toolExecutionNotifications": [],
+                            "endTimeUtc": "2024-12-12T22:09:12Z"}],
+           "originalUriBaseIds": {"PWD": {"uri": "file:///home/david/coding/gcc-newgit-more-taint/build-with-libgdiagnostics/gcc/"}},
+           "artifacts": [{"location": {"uri": "/not/a/real/path/malloc-vs-local-4.c",
+                                       "uriBaseId": "PWD"},
+                          "sourceLanguage": "c",
+                          "contents": {"text": "#include <stdlib.h>\n\nvoid __attribute__((noinline)) callee_1 (int *ptr)\n{\n  *ptr = 42; \n}\n\nint test_1 (int i, int flag)\n{\n  /* Double diamond CFG; either use &i, or a malloc-ed buffer.  */\n  int *ptr = &i;\n  if (flag)\n    ptr = (int *)malloc (sizeof (int));\n  callee_1 (ptr);\n  if (flag)\n    free (ptr);\n  return i;\n}\n\nvoid __attribute__((noinline)) callee_2 (int *ptr)\n{\n  *ptr = 42;\n}\n\nint test_2 (int flag)\n{\n  int i;\n\n  if (flag)\n    callee_2 (&i);\n\n  callee_2 (&i);\n\n  if (!flag)\n    {\n      void *ptr = malloc (16);\n      free (ptr);\n      free (ptr);\n    }\n}\n"},
+                          "roles": ["analysisTarget",
+                                    "tracedFile"]}],
+           "results": [{"ruleId": "-Wanalyzer-possible-null-dereference",
+                        "taxa": [{"id": "690",
+                                  "toolComponent": {"name": "cwe"}}],
+                        "properties": {"gcc/analyzer/saved_diagnostic/sm": "malloc",
+                                       "gcc/analyzer/saved_diagnostic/enode": 78,
+                                       "gcc/analyzer/saved_diagnostic/snode": 22,
+                                       "gcc/analyzer/saved_diagnostic/sval": "&HEAP_ALLOCATED_REGION(46)",
+                                       "gcc/analyzer/saved_diagnostic/state": "unchecked ({free})",
+                                       "gcc/analyzer/saved_diagnostic/idx": 1},
+                        "level": "warning",
+                        "message": {"text": "dereference of possibly-NULL ‘ptr’"},
+                        "locations": [{"physicalLocation": {"artifactLocation": {"uri": "/not/a/real/path/malloc-vs-local-4.c",
+                                                                                 "uriBaseId": "PWD"},
+                                                            "region": {"startLine": 5,
+                                                                       "startColumn": 3,
+                                                                       "endColumn": 12},
+                                                            "contextRegion": {"startLine": 5,
+                                                                              "snippet": {"text": "  *ptr = 42;\n"}}},
+                                       "logicalLocations": [{"name": "callee_1",
+                                                             "fullyQualifiedName": "callee_1",
+                                                             "decoratedName": "callee_1",
+                                                             "kind": "function"}]}],
+                        "codeFlows": [{"threadFlows": [{"id": "main",
+                                                        "locations": [{"properties": {"gcc/analyzer/checker_event/emission_id": "(1)",
+                                                                                      "gcc/analyzer/checker_event/kind": "EK_FUNCTION_ENTRY"},
+                                                                       "location": {"physicalLocation": {"artifactLocation": {"uri": "/not/a/real/path/malloc-vs-local-4.c",
+                                                                                                                              "uriBaseId": "PWD"},
+                                                                                                         "region": {"startLine": 8,
+                                                                                                                    "startColumn": 5,
+                                                                                                                    "endColumn": 11},
+                                                                                                         "contextRegion": {"startLine": 8,
+                                                                                                                           "snippet": {"text": "int test_1 (int i, int flag)\n"}}},
+                                                                                    "logicalLocations": [{"name": "test_1",
+                                                                                                          "fullyQualifiedName": "test_1",
+                                                                                                          "decoratedName": "test_1",
+                                                                                                          "kind": "function"}],
+                                                                                    "message": {"text": "entry to ‘test_1’"}},
+                                                                       "kinds": ["enter",
+                                                                                 "function"],
+                                                                       "nestingLevel": 1,
+                                                                       "executionOrder": 1},
+                                                                      {"properties": {"gcc/analyzer/checker_event/emission_id": "(2)",
+                                                                                      "gcc/analyzer/checker_event/kind": "EK_START_CFG_EDGE",
+                                                                                      "gcc/analyzer/superedge_event/superedge": {"kind": "SUPEREDGE_CFG_EDGE",
+                                                                                                                                 "src_idx": 13,
+                                                                                                                                 "dst_idx": 14,
+                                                                                                                                 "desc": "true (flags TRUE_VALUE) (has goto_locus)"}},
+                                                                       "location": {"physicalLocation": {"artifactLocation": {"uri": "/not/a/real/path/malloc-vs-local-4.c",
+                                                                                                                              "uriBaseId": "PWD"},
+                                                                                                         "region": {"startLine": 12,
+                                                                                                                    "startColumn": 6,
+                                                                                                                    "endColumn": 7},
+                                                                                                         "contextRegion": {"startLine": 12,
+                                                                                                                           "snippet": {"text": "  if (flag)\n"}}},
+                                                                                    "logicalLocations": [{"name": "test_1",
+                                                                                                          "fullyQualifiedName": "test_1",
+                                                                                                          "decoratedName": "test_1",
+                                                                                                          "kind": "function"}],
+                                                                                    "message": {"text": "following ‘true’ branch (when ‘flag != 0’)..."}},
+                                                                       "kinds": ["branch",
+                                                                                 "true"],
+                                                                       "nestingLevel": 1,
+                                                                       "executionOrder": 2},
+                                                                      {"properties": {"gcc/analyzer/checker_event/emission_id": "(3)",
+                                                                                      "gcc/analyzer/checker_event/kind": "EK_END_CFG_EDGE",
+                                                                                      "gcc/analyzer/superedge_event/superedge": {"kind": "SUPEREDGE_CFG_EDGE",
+                                                                                                                                 "src_idx": 13,
+                                                                                                                                 "dst_idx": 14,
+                                                                                                                                 "desc": "true (flags TRUE_VALUE) (has goto_locus)"}},
+                                                                       "location": {"physicalLocation": {"artifactLocation": {"uri": "/not/a/real/path/malloc-vs-local-4.c",
+                                                                                                                              "uriBaseId": "PWD"},
+                                                                                                         "region": {"startLine": 13,
+                                                                                                                    "startColumn": 18,
+                                                                                                                    "endColumn": 39},
+                                                                                                         "contextRegion": {"startLine": 13,
+                                                                                                                           "snippet": {"text": "    ptr = (int *)malloc (sizeof (int));\n"}}},
+                                                                                    "logicalLocations": [{"name": "test_1",
+                                                                                                          "fullyQualifiedName": "test_1",
+                                                                                                          "decoratedName": "test_1",
+                                                                                                          "kind": "function"}],
+                                                                                    "message": {"text": "...to here"}},
+                                                                       "kinds": ["branch",
+                                                                                 "true"],
+                                                                       "nestingLevel": 1,
+                                                                       "executionOrder": 3},
+                                                                      {"properties": {"gcc/analyzer/checker_event/emission_id": "(4)",
+                                                                                      "gcc/analyzer/checker_event/kind": "EK_STATE_CHANGE"},
+                                                                       "location": {"physicalLocation": {"artifactLocation": {"uri": "/not/a/real/path/malloc-vs-local-4.c",
+                                                                                                                              "uriBaseId": "PWD"},
+                                                                                                         "region": {"startLine": 13,
+                                                                                                                    "startColumn": 18,
+                                                                                                                    "endColumn": 39},
+                                                                                                         "contextRegion": {"startLine": 13,
+                                                                                                                           "snippet": {"text": "    ptr = (int *)malloc (sizeof (int));\n"}}},
+                                                                                    "logicalLocations": [{"name": "test_1",
+                                                                                                          "fullyQualifiedName": "test_1",
+                                                                                                          "decoratedName": "test_1",
+                                                                                                          "kind": "function"}],
+                                                                                    "message": {"text": "this call could return NULL"}},
+                                                                       "kinds": ["acquire",
+                                                                                 "memory"],
+                                                                       "nestingLevel": 1,
+                                                                       "executionOrder": 4},
+                                                                      {"properties": {"gcc/analyzer/checker_event/emission_id": "(5)",
+                                                                                      "gcc/analyzer/checker_event/kind": "EK_CALL_EDGE",
+                                                                                      "gcc/analyzer/superedge_event/superedge": {"kind": "SUPEREDGE_CALL",
+                                                                                                                                 "src_idx": 15,
+                                                                                                                                 "dst_idx": 21,
+                                                                                                                                 "desc": "call"}},
+                                                                       "location": {"physicalLocation": {"artifactLocation": {"uri": "/not/a/real/path/malloc-vs-local-4.c",
+                                                                                                                              "uriBaseId": "PWD"},
+                                                                                                         "region": {"startLine": 14,
+                                                                                                                    "startColumn": 3,
+                                                                                                                    "endColumn": 17},
+                                                                                                         "contextRegion": {"startLine": 14,
+                                                                                                                           "snippet": {"text": "  callee_1 (ptr);\n"}}},
+                                                                                    "logicalLocations": [{"name": "test_1",
+                                                                                                          "fullyQualifiedName": "test_1",
+                                                                                                          "decoratedName": "test_1",
+                                                                                                          "kind": "function"}],
+                                                                                    "message": {"text": "calling ‘callee_1’ from ‘test_1’"}},
+                                                                       "kinds": ["call",
+                                                                                 "function"],
+                                                                       "nestingLevel": 1,
+                                                                       "executionOrder": 5},
+                                                                      {"properties": {"gcc/analyzer/checker_event/emission_id": "(6)",
+                                                                                      "gcc/analyzer/checker_event/kind": "EK_FUNCTION_ENTRY"},
+                                                                       "location": {"physicalLocation": {"artifactLocation": {"uri": "/not/a/real/path/malloc-vs-local-4.c",
+                                                                                                                              "uriBaseId": "PWD"},
+                                                                                                         "region": {"startLine": 3,
+                                                                                                                    "startColumn": 32,
+                                                                                                                    "endColumn": 40},
+                                                                                                         "contextRegion": {"startLine": 3,
+                                                                                                                           "snippet": {"text": "void __attribute__((noinline)) callee_1 (int *ptr)\n"}}},
+                                                                                    "logicalLocations": [{"name": "callee_1",
+                                                                                                          "fullyQualifiedName": "callee_1",
+                                                                                                          "decoratedName": "callee_1",
+                                                                                                          "kind": "function"}],
+                                                                                    "message": {"text": "entry to ‘callee_1’"}},
+                                                                       "kinds": ["enter",
+                                                                                 "function"],
+                                                                       "nestingLevel": 2,
+                                                                       "executionOrder": 6},
+                                                                      {"properties": {"gcc/analyzer/checker_event/emission_id": "(7)",
+                                                                                      "gcc/analyzer/checker_event/kind": "EK_WARNING"},
+                                                                       "location": {"physicalLocation": {"artifactLocation": {"uri": "/not/a/real/path/malloc-vs-local-4.c",
+                                                                                                                              "uriBaseId": "PWD"},
+                                                                                                         "region": {"startLine": 5,
+                                                                                                                    "startColumn": 3,
+                                                                                                                    "endColumn": 12},
+                                                                                                         "contextRegion": {"startLine": 5,
+                                                                                                                           "snippet": {"text": "  *ptr = 42;\n"}}},
+                                                                                    "logicalLocations": [{"name": "callee_1",
+                                                                                                          "fullyQualifiedName": "callee_1",
+                                                                                                          "decoratedName": "callee_1",
+                                                                                                          "kind": "function"}],
+                                                                                    "message": {"text": "‘ptr’ could be NULL: unchecked value from [(4)](sarif:/runs/0/results/0/codeFlows/0/threadFlows/0/locations/3)"}},
+                                                                       "kinds": ["danger"],
+                                                                       "nestingLevel": 2,
+                                                                       "executionOrder": 7}]}]}]},
+                       {"ruleId": "-Wanalyzer-double-free",
+                        "taxa": [{"id": "415",
+                                  "toolComponent": {"name": "cwe"}}],
+                        "properties": {"gcc/analyzer/saved_diagnostic/sm": "malloc",
+                                       "gcc/analyzer/saved_diagnostic/enode": 39,
+                                       "gcc/analyzer/saved_diagnostic/snode": 6,
+                                       "gcc/analyzer/saved_diagnostic/sval": "&HEAP_ALLOCATED_REGION(46)",
+                                       "gcc/analyzer/saved_diagnostic/state": "freed",
+                                       "gcc/analyzer/saved_diagnostic/idx": 0},
+                        "level": "warning",
+                        "message": {"text": "double-‘free’ of ‘ptr’"},
+                        "locations": [{"physicalLocation": {"artifactLocation": {"uri": "/not/a/real/path/malloc-vs-local-4.c",
+                                                                                 "uriBaseId": "PWD"},
+                                                            "region": {"startLine": 38,
+                                                                       "startColumn": 7,
+                                                                       "endColumn": 17},
+                                                            "contextRegion": {"startLine": 38,
+                                                                              "snippet": {"text": "      free (ptr);\n"}}},
+                                       "logicalLocations": [{"name": "test_2",
+                                                             "fullyQualifiedName": "test_2",
+                                                             "decoratedName": "test_2",
+                                                             "kind": "function"}]}],
+                        "codeFlows": [{"threadFlows": [{"id": "main",
+                                                        "locations": [{"properties": {"gcc/analyzer/checker_event/emission_id": "(1)",
+                                                                                      "gcc/analyzer/checker_event/kind": "EK_START_CFG_EDGE",
+                                                                                      "gcc/analyzer/superedge_event/superedge": {"kind": "SUPEREDGE_CFG_EDGE",
+                                                                                                                                 "src_idx": 5,
+                                                                                                                                 "dst_idx": 6,
+                                                                                                                                 "desc": "true (flags TRUE_VALUE) (has goto_locus)"}},
+                                                                       "location": {"physicalLocation": {"artifactLocation": {"uri": "/not/a/real/path/malloc-vs-local-4.c",
+                                                                                                                              "uriBaseId": "PWD"},
+                                                                                                         "region": {"startLine": 34,
+                                                                                                                    "startColumn": 6,
+                                                                                                                    "endColumn": 7},
+                                                                                                         "contextRegion": {"startLine": 34,
+                                                                                                                           "snippet": {"text": "  if (!flag)\n"}}},
+                                                                                    "logicalLocations": [{"name": "test_2",
+                                                                                                          "fullyQualifiedName": "test_2",
+                                                                                                          "decoratedName": "test_2",
+                                                                                                          "kind": "function"}],
+                                                                                    "message": {"text": "following ‘true’ branch (when ‘flag == 0’)..."}},
+                                                                       "kinds": ["branch",
+                                                                                 "true"],
+                                                                       "nestingLevel": 1,
+                                                                       "executionOrder": 1},
+                                                                      {"properties": {"gcc/analyzer/checker_event/emission_id": "(2)",
+                                                                                      "gcc/analyzer/checker_event/kind": "EK_END_CFG_EDGE",
+                                                                                      "gcc/analyzer/superedge_event/superedge": {"kind": "SUPEREDGE_CFG_EDGE",
+                                                                                                                                 "src_idx": 5,
+                                                                                                                                 "dst_idx": 6,
+                                                                                                                                 "desc": "true (flags TRUE_VALUE) (has goto_locus)"}},
+                                                                       "location": {"physicalLocation": {"artifactLocation": {"uri": "/not/a/real/path/malloc-vs-local-4.c",
+                                                                                                                              "uriBaseId": "PWD"},
+                                                                                                         "region": {"startLine": 36,
+                                                                                                                    "startColumn": 19,
+                                                                                                                    "endColumn": 30},
+                                                                                                         "contextRegion": {"startLine": 36,
+                                                                                                                           "snippet": {"text": "      void *ptr = malloc (16);\n"}}},
+                                                                                    "logicalLocations": [{"name": "test_2",
+                                                                                                          "fullyQualifiedName": "test_2",
+                                                                                                          "decoratedName": "test_2",
+                                                                                                          "kind": "function"}],
+                                                                                    "message": {"text": "...to here"}},
+                                                                       "kinds": ["branch",
+                                                                                 "true"],
+                                                                       "nestingLevel": 1,
+                                                                       "executionOrder": 2},
+                                                                      {"properties": {"gcc/analyzer/checker_event/emission_id": "(3)",
+                                                                                      "gcc/analyzer/checker_event/kind": "EK_STATE_CHANGE"},
+                                                                       "location": {"physicalLocation": {"artifactLocation": {"uri": "/not/a/real/path/malloc-vs-local-4.c",
+                                                                                                                              "uriBaseId": "PWD"},
+                                                                                                         "region": {"startLine": 36,
+                                                                                                                    "startColumn": 19,
+                                                                                                                    "endColumn": 30},
+                                                                                                         "contextRegion": {"startLine": 36,
+                                                                                                                           "snippet": {"text": "      void *ptr = malloc (16);\n"}}},
+                                                                                    "logicalLocations": [{"name": "test_2",
+                                                                                                          "fullyQualifiedName": "test_2",
+                                                                                                          "decoratedName": "test_2",
+                                                                                                          "kind": "function"}],
+                                                                                    "message": {"text": "allocated here"}},
+                                                                       "kinds": ["acquire",
+                                                                                 "memory"],
+                                                                       "nestingLevel": 1,
+                                                                       "executionOrder": 3},
+                                                                      {"properties": {"gcc/analyzer/checker_event/emission_id": "(4)",
+                                                                                      "gcc/analyzer/checker_event/kind": "EK_STATE_CHANGE"},
+                                                                       "location": {"physicalLocation": {"artifactLocation": {"uri": "/not/a/real/path/malloc-vs-local-4.c",
+                                                                                                                              "uriBaseId": "PWD"},
+                                                                                                         "region": {"startLine": 37,
+                                                                                                                    "startColumn": 7,
+                                                                                                                    "endColumn": 17},
+                                                                                                         "contextRegion": {"startLine": 37,
+                                                                                                                           "snippet": {"text": "      free (ptr);\n"}}},
+                                                                                    "logicalLocations": [{"name": "test_2",
+                                                                                                          "fullyQualifiedName": "test_2",
+                                                                                                          "decoratedName": "test_2",
+                                                                                                          "kind": "function"}],
+                                                                                    "message": {"text": "first ‘free’ here"}},
+                                                                       "kinds": ["release",
+                                                                                 "memory"],
+                                                                       "nestingLevel": 1,
+                                                                       "executionOrder": 4},
+                                                                      {"properties": {"gcc/analyzer/checker_event/emission_id": "(5)",
+                                                                                      "gcc/analyzer/checker_event/kind": "EK_WARNING"},
+                                                                       "location": {"physicalLocation": {"artifactLocation": {"uri": "/not/a/real/path/malloc-vs-local-4.c",
+                                                                                                                              "uriBaseId": "PWD"},
+                                                                                                         "region": {"startLine": 38,
+                                                                                                                    "startColumn": 7,
+                                                                                                                    "endColumn": 17},
+                                                                                                         "contextRegion": {"startLine": 38,
+                                                                                                                           "snippet": {"text": "      free (ptr);\n"}}},
+                                                                                    "logicalLocations": [{"name": "test_2",
+                                                                                                          "fullyQualifiedName": "test_2",
+                                                                                                          "decoratedName": "test_2",
+                                                                                                          "kind": "function"}],
+                                                                                    "message": {"text": "second ‘free’ here; first ‘free’ was at [(4)](sarif:/runs/0/results/1/codeFlows/0/threadFlows/0/locations/3)"}},
+                                                                       "kinds": ["danger"],
+                                                                       "nestingLevel": 1,
+                                                                       "executionOrder": 5}]}]}]}]}]}
+// TODO: show the CWEs
+// TODO: fix URL in message
+
+/* { dg-begin-multiline-output "" }
+In function 'callee_1':
+/not/a/real/path/malloc-vs-local-4.c:5:3: warning: dereference of possibly-NULL ‘ptr’ [-Wanalyzer-possible-null-dereference]
+    5 |   *ptr = 42;
+      |   ^~~~~~~~~~
+  'test_1': events 1-5
+    |
+    |    8 | int test_1 (int i, int flag)
+    |      |     ^~~~~~~
+    |      |     |
+    |      |     (1) entry to ‘test_1’
+    |......
+    |   12 |   if (flag)
+    |      |      ~~
+    |      |      |
+    |      |      (2) following ‘true’ branch (when ‘flag != 0’)...
+    |   13 |     ptr = (int *)malloc (sizeof (int));
+    |      |                  ~~~~~~~~~~~~~~~~~~~~~~
+    |      |                  |
+    |      |                  (3) ...to here
+    |      |                  (4) this call could return NULL
+    |   14 |   callee_1 (ptr);
+    |      |   ~~~~~~~~~~~~~~~
+    |      |   |
+    |      |   (5) calling ‘callee_1’ from ‘test_1’
+    |
+    +--> 'callee_1': events 6-7
+           |
+           |    3 | void __attribute__((noinline)) callee_1 (int *ptr)
+           |      |                                ^~~~~~~~~
+           |      |                                |
+           |      |                                (6) entry to ‘callee_1’
+           |    4 | {
+           |    5 |   *ptr = 42;
+           |      |   ~~~~~~~~~~                    
+           |      |   |
+           |      |   (7) ‘ptr’ could be NULL: unchecked value from [(4)](sarif:/runs/0/results/0/codeFlows/0/threadFlows/0/locations/3)
+           |
+   { dg-end-multiline-output "" } */
+/* { dg-begin-multiline-output "" }
+In function 'test_2':
+/not/a/real/path/malloc-vs-local-4.c:38:7: warning: double-‘free’ of ‘ptr’ [-Wanalyzer-double-free]
+   38 |       free (ptr);
+      |       ^~~~~~~~~~~
+  'test_2': events 1-5
+   34 |   if (!flag)
+      |      ^~
+      |      |
+      |      (1) following ‘true’ branch (when ‘flag == 0’)...
+   35 |     {
+   36 |       void *ptr = malloc (16);
+      |                   ~~~~~~~~~~~~
+      |                   |
+      |                   (2) ...to here
+      |                   (3) allocated here
+   37 |       free (ptr);
+      |       ~~~~~~~~~~~
+      |       |
+      |       (4) first ‘free’ here
+   38 |       free (ptr);
+      |       ~~~~~~~~~~~
+      |       |
+      |       (5) second ‘free’ here; first ‘free’ was at [(4)](sarif:/runs/0/results/1/codeFlows/0/threadFlows/0/locations/3)
+   { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/sarif-replay.dg/2.1.0-valid/signal-1.c.moved.sarif b/gcc/testsuite/sarif-replay.dg/2.1.0-valid/signal-1.c.moved.sarif
index eabab5a68cd3..f0026de12da4 100644
--- a/gcc/testsuite/sarif-replay.dg/2.1.0-valid/signal-1.c.moved.sarif
+++ b/gcc/testsuite/sarif-replay.dg/2.1.0-valid/signal-1.c.moved.sarif
@@ -167,20 +167,19 @@ 
 
 // TODO: show the CWE
 /* { dg-begin-multiline-output "" }
+In function 'custom_logger':
 signal-1.c:13:3: warning: call to ‘fprintf’ from within signal handler [-Wanalyzer-unsafe-call-within-signal-handler]
    13 |   fprintf(stderr, "LOG: %s", msg);
       |   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-  'main': event 1
+  'main': events 1-2
     |
     |   21 | int main(int argc, const char *argv)
     |      |     ^~~~~
     |      |     |
     |      |     (1) entry to ‘main’
-    |
-  'main': event 2
-    |
+    |......
     |   25 |   signal(SIGINT, handler);
-    |      |   ^~~~~~~~~~~~~~~~~~~~~~~~
+    |      |   ~~~~~~~~~~~~~~~~~~~~~~~~
     |      |   |
     |      |   (2) registering ‘handler’ as signal handler
     |
@@ -189,31 +188,27 @@  signal-1.c:13:3: warning: call to ‘fprintf’ from within signal handler [-Wan
     |GNU C17:
     | (3): later on, when the signal is delivered to the process
     |
-    +--> 'handler': event 4
+    +--> 'handler': events 4-5
            |
            |   16 | static void handler(int signum)
            |      |             ^~~~~~~~
            |      |             |
            |      |             (4) entry to ‘handler’
-           |
-         'handler': event 5
-           |
+           |   17 | {
            |   18 |   custom_logger("got signal");
-           |      |   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
+           |      |   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            |      |   |
            |      |   (5) calling ‘custom_logger’ from ‘handler’
            |
-           +--> 'custom_logger': event 6
+           +--> 'custom_logger': events 6-7
                   |
                   |   11 | void custom_logger(const char *msg)
                   |      |      ^~~~~~~~~~~~~~
                   |      |      |
                   |      |      (6) entry to ‘custom_logger’
-                  |
-                'custom_logger': event 7
-                  |
+                  |   12 | {
                   |   13 |   fprintf(stderr, "LOG: %s", msg);
-                  |      |   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                  |      |   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                   |      |   |
                   |      |   (7) call to ‘fprintf’ from within signal handler
                   |
diff --git a/gcc/testsuite/sarif-replay.dg/2.1.0-valid/signal-1.c.sarif b/gcc/testsuite/sarif-replay.dg/2.1.0-valid/signal-1.c.sarif
index 81ac149e1253..e2f316b972e9 100644
--- a/gcc/testsuite/sarif-replay.dg/2.1.0-valid/signal-1.c.sarif
+++ b/gcc/testsuite/sarif-replay.dg/2.1.0-valid/signal-1.c.sarif
@@ -165,20 +165,19 @@ 
 
 // TODO: show the CWE
 /* { dg-begin-multiline-output "" }
+In function 'custom_logger':
 ../../src/gcc/testsuite/gcc.dg/analyzer/signal-1.c:13:3: warning: call to ‘fprintf’ from within signal handler [-Wanalyzer-unsafe-call-within-signal-handler]
    13 |   fprintf(stderr, "LOG: %s", msg);
       |   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-  'main': event 1
+  'main': events 1-2
     |
     |   21 | int main(int argc, const char *argv)
     |      |     ^~~~~
     |      |     |
     |      |     (1) entry to ‘main’
-    |
-  'main': event 2
-    |
+    |......
     |   25 |   signal(SIGINT, handler);
-    |      |   ^~~~~~~~~~~~~~~~~~~~~~~~
+    |      |   ~~~~~~~~~~~~~~~~~~~~~~~~
     |      |   |
     |      |   (2) registering ‘handler’ as signal handler
     |
@@ -187,31 +186,27 @@ 
     |GNU C17:
     | (3): later on, when the signal is delivered to the process
     |
-    +--> 'handler': event 4
+    +--> 'handler': events 4-5
            |
            |   16 | static void handler(int signum)
            |      |             ^~~~~~~~
            |      |             |
            |      |             (4) entry to ‘handler’
-           |
-         'handler': event 5
-           |
+           |   17 | {
            |   18 |   custom_logger("got signal");
-           |      |   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
+           |      |   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            |      |   |
            |      |   (5) calling ‘custom_logger’ from ‘handler’
            |
-           +--> 'custom_logger': event 6
+           +--> 'custom_logger': events 6-7
                   |
                   |   11 | void custom_logger(const char *msg)
                   |      |      ^~~~~~~~~~~~~~
                   |      |      |
                   |      |      (6) entry to ‘custom_logger’
-                  |
-                'custom_logger': event 7
-                  |
+                  |   12 | {
                   |   13 |   fprintf(stderr, "LOG: %s", msg);
-                  |      |   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                  |      |   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                   |      |   |
                   |      |   (7) call to ‘fprintf’ from within signal handler
                   |
diff --git a/gcc/testsuite/sarif-replay.dg/2.1.0-valid/spec-example-4.sarif b/gcc/testsuite/sarif-replay.dg/2.1.0-valid/spec-example-4.sarif
index 84d3f887cefd..60c87314e1c8 100644
--- a/gcc/testsuite/sarif-replay.dg/2.1.0-valid/spec-example-4.sarif
+++ b/gcc/testsuite/sarif-replay.dg/2.1.0-valid/spec-example-4.sarif
@@ -749,15 +749,8 @@ 
 /* { dg-begin-multiline-output "" }
 In function 'collections::list::add':
 collections/list.h:15:9: error: Variable "ptr" was used without being initialized. It was declared [here](0). [C2001]
-  event 1
-    |
-    |
-  event 2
-    |
-    |
-  event 3
-    |
-    |
+  events 1-3
+......
    { dg-end-multiline-output "" } */
 /* { dg-begin-multiline-output "" }
 collections/list.h:8:5: note: Variable "ptr" was declared here.