Add stack trace printing to test crashes and assertions

To debug rare or flaky issues that only reproduce on CI bots, install
Abseil's failure signal handler centrally to print a symbolized
stacktrace across Dawn and Tint test runners.

Fixed: 522635660
Change-Id: I583c5c5bd501317fababf0f228811255b1dd6980
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/315996
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Arthur Sonzogni <arthursonzogni@chromium.org>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
diff --git a/src/dawn/tests/BUILD.gn b/src/dawn/tests/BUILD.gn
index 89c39e4..4224231 100644
--- a/src/dawn/tests/BUILD.gn
+++ b/src/dawn/tests/BUILD.gn
@@ -493,6 +493,7 @@
   ]
   public_deps = [
     "${dawn_root}/src/dawn/partition_alloc:raw_ptr",
+    "${dawn_root}/src/utils:crash_handler",
     "${dawn_root}/src/utils:gmock_and_gtest",
   ]
 
diff --git a/src/dawn/tests/CMakeLists.txt b/src/dawn/tests/CMakeLists.txt
index 9b64d0b..cd81d38 100644
--- a/src/dawn/tests/CMakeLists.txt
+++ b/src/dawn/tests/CMakeLists.txt
@@ -43,6 +43,7 @@
 )
 common_compile_options(dawn_test_infra)
 target_link_libraries(dawn_test_infra PRIVATE
+    dawn_crash_handler
     dawn_native
     dawn_wgpu_utils
     dawncpp
diff --git a/src/dawn/tests/DawnTest.cpp b/src/dawn/tests/DawnTest.cpp
index f142b02..92350a1 100644
--- a/src/dawn/tests/DawnTest.cpp
+++ b/src/dawn/tests/DawnTest.cpp
@@ -77,6 +77,7 @@
 #include "src/dawn/utils/WGPUHelpers.h"
 #include "src/dawn/utils/WireHelper.h"
 #include "src/utils/assert.h"
+#include "src/utils/crash_handler.h"
 #include "src/utils/log.h"
 #include "src/utils/platform.h"
 
@@ -352,6 +353,7 @@
 }
 
 DawnTestEnvironment::DawnTestEnvironment(int argc, char** argv) {
+    InstallCrashHandler(argv[0]);
     InitializePartitionAllocForTesting();
     InitializeDanglingPointerDetectorForTesting();
 
diff --git a/src/dawn/tests/unittests/validation/ValidationTest.cpp b/src/dawn/tests/unittests/validation/ValidationTest.cpp
index 5a09ac2..b36aa9d 100644
--- a/src/dawn/tests/unittests/validation/ValidationTest.cpp
+++ b/src/dawn/tests/unittests/validation/ValidationTest.cpp
@@ -49,6 +49,7 @@
 #include "src/dawn/tests/ToggleParser.h"
 #include "src/dawn/utils/WireHelper.h"
 #include "src/utils/assert.h"
+#include "src/utils/crash_handler.h"
 
 namespace {
 
@@ -61,6 +62,7 @@
 }  // namespace
 
 void InitDawnValidationTestEnvironment(int argc, char** argv) {
+    dawn::InstallCrashHandler(argv[0]);
     dawn::InitializePartitionAllocForTesting();
     dawn::InitializeDanglingPointerDetectorForTesting();
 
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index 19ff862..27f27ee 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -609,6 +609,8 @@
       target_link_libraries(${TARGET} PRIVATE dawn_shared_utils)
     elseif(${DEPENDENCY} STREQUAL "src_utils_chromium_test_compat")
       target_link_libraries(${TARGET} PRIVATE dawn_shared_utils_chromium_test_compat)
+    elseif(${DEPENDENCY} STREQUAL "src_utils_crash_handler")
+      target_link_libraries(${TARGET} PRIVATE dawn_crash_handler)
     else()
       message(FATAL_ERROR "unhandled external dependency ${DEPENDENCY}")
     endif()
diff --git a/src/tint/cmd/test/BUILD.bazel b/src/tint/cmd/test/BUILD.bazel
index fdc33fa..106768d 100644
--- a/src/tint/cmd/test/BUILD.bazel
+++ b/src/tint/cmd/test/BUILD.bazel
@@ -105,6 +105,7 @@
     "@gtest",
     "//src/utils",
     "//src/utils/chromium_test_compat",
+    "//src/utils:crash_handler",
   ] + select({
     ":tint_build_glsl_writer": [
       "//src/tint/lang/glsl/writer/common:test",
diff --git a/src/tint/cmd/test/BUILD.cmake b/src/tint/cmd/test/BUILD.cmake
index 7262525..a2f7fe4 100644
--- a/src/tint/cmd/test/BUILD.cmake
+++ b/src/tint/cmd/test/BUILD.cmake
@@ -105,6 +105,7 @@
   "gtest"
   "src_utils"
   "src_utils_chromium_test_compat"
+  "src_utils_crash_handler"
 )
 
 if(TINT_BUILD_GLSL_WRITER)
diff --git a/src/tint/cmd/test/BUILD.gn b/src/tint/cmd/test/BUILD.gn
index eb62512..7dd570a 100644
--- a/src/tint/cmd/test/BUILD.gn
+++ b/src/tint/cmd/test/BUILD.gn
@@ -49,6 +49,7 @@
     sources = [ "main_test.cc" ]
     deps = [
       "${dawn_root}/src/utils",
+      "${dawn_root}/src/utils:crash_handler",
       "${dawn_root}/src/utils/chromium_test_compat",
       "${tint_src_dir}:gmock_and_gtest",
       "${tint_src_dir}/api",
diff --git a/src/tint/cmd/test/main_test.cc b/src/tint/cmd/test/main_test.cc
index 96cd3ec..d3e2b99 100644
--- a/src/tint/cmd/test/main_test.cc
+++ b/src/tint/cmd/test/main_test.cc
@@ -28,9 +28,11 @@
 #include "gmock/gmock.h"
 #include "src/tint/api/tint.h"
 #include "src/utils/chromium_test_compat/chromium_test_compat.h"
+#include "src/utils/crash_handler.h"
 
 // Entry point for tint unit tests
 int main(int argc, char** argv) {
+    dawn::InstallCrashHandler(argv[0]);
     dawn::SubstituteChromiumArgs(argc, argv);
     testing::InitGoogleMock(&argc, argv);
 
diff --git a/src/tint/externals.json b/src/tint/externals.json
index 32a95f8..a00b30e 100644
--- a/src/tint/externals.json
+++ b/src/tint/externals.json
@@ -113,6 +113,11 @@
           "libprotobuf-mutator/**"
       ]
     },
+    "src_utils_crash_handler": {
+        "IncludePatterns": [
+            "src/utils/crash_handler.*"
+        ]
+    },
     "src_utils": {
         "IncludePatterns": [
             "src/utils/*"
diff --git a/src/utils/BUILD.bazel b/src/utils/BUILD.bazel
index 2a1eb14..0b35a57 100644
--- a/src/utils/BUILD.bazel
+++ b/src/utils/BUILD.bazel
@@ -58,3 +58,15 @@
   copts = COPTS,
   visibility = ["//visibility:public"],
 )
+
+cc_library(
+  name = "crash_handler",
+  hdrs = ["crash_handler.h"],
+  srcs = ["crash_handler.cc"],
+  deps = [
+    "@abseil_cpp//absl/debugging:failure_signal_handler",
+    "@abseil_cpp//absl/debugging:symbolize",
+  ],
+  copts = COPTS,
+  visibility = ["//visibility:public"],
+)
diff --git a/src/utils/BUILD.gn b/src/utils/BUILD.gn
index f10af3e..7a0d39e 100644
--- a/src/utils/BUILD.gn
+++ b/src/utils/BUILD.gn
@@ -106,6 +106,15 @@
   public_configs = [ ":internal_config" ]
 }
 
+source_set("crash_handler") {
+  sources = [
+    "crash_handler.cc",
+    "crash_handler.h",
+  ]
+  public_configs = [ ":internal_config" ]
+  deps = [ "${dawn_root}:abseil" ]
+}
+
 dawn_nocompile_source_set("nocompile_sources") {
   deps = [ ":utils" ]
   sources = [
diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt
index 444d962..bc2cc65 100644
--- a/src/utils/CMakeLists.txt
+++ b/src/utils/CMakeLists.txt
@@ -63,3 +63,16 @@
     SOURCES
         ${sources}
 )
+
+dawn_add_library(
+    dawn_crash_handler
+    ENABLE_EMSCRIPTEN
+    UTILITY_TARGET dawn_internal_config
+    HEADERS
+        "crash_handler.h"
+    SOURCES
+        "crash_handler.cc"
+    DEPENDS
+        absl::failure_signal_handler
+        absl::symbolize
+)
diff --git a/src/utils/assert_test.cc b/src/utils/assert_test.cc
index 670bb61..6b64fce 100644
--- a/src/utils/assert_test.cc
+++ b/src/utils/assert_test.cc
@@ -107,7 +107,22 @@
     EXPECT_DEATH(DAWN_ASSERT(g_var2 != 124), "g_var2 != 124");
 #endif
 }
-#endif
+
+#ifndef _WIN32
+TEST_F(AssertDeathTest, StackTrace) {
+    EXPECT_DEATH(DAWN_UNREACHABLE(), "PC: @");
+}
+
+TEST_F(AssertDeathTest, CrashStackTrace) {
+    EXPECT_DEATH(
+        {
+            volatile int* ptr = nullptr;
+            *ptr = 1;
+        },
+        "PC: @");
+}
+#endif  // !defined(_WIN32)
+#endif  // GTEST_HAS_DEATH_TEST
 
 using AssertFunctionalityTest = ::testing::Test;
 
diff --git a/src/utils/crash_handler.cc b/src/utils/crash_handler.cc
new file mode 100644
index 0000000..4ab1ed9
--- /dev/null
+++ b/src/utils/crash_handler.cc
@@ -0,0 +1,45 @@
+// Copyright 2026 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "src/utils/crash_handler.h"
+
+#include "absl/debugging/failure_signal_handler.h"
+#include "absl/debugging/symbolize.h"
+
+namespace dawn {
+
+void InstallCrashHandler(const char* argv0) {
+    absl::InitializeSymbolizer(argv0);
+    absl::FailureSignalHandlerOptions options;
+    options.symbolize_stacktrace = true;
+    options.use_alternate_stack = true;
+    options.alarm_on_failure_secs = 3;
+    options.call_previous_handler = true;
+    absl::InstallFailureSignalHandler(options);
+}
+
+}  // namespace dawn
diff --git a/src/utils/crash_handler.h b/src/utils/crash_handler.h
new file mode 100644
index 0000000..d2b7f4e0
--- /dev/null
+++ b/src/utils/crash_handler.h
@@ -0,0 +1,38 @@
+// Copyright 2026 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef SRC_UTILS_CRASH_HANDLER_H_
+#define SRC_UTILS_CRASH_HANDLER_H_
+
+namespace dawn {
+
+// Install crash handler to print a demangled stack trace on unexpected crashes or assertions.
+void InstallCrashHandler(const char* argv0);
+
+}  // namespace dawn
+
+#endif  // SRC_UTILS_CRASH_HANDLER_H_
diff --git a/tools/src/cmd/gen/build/BUILD.bazel.tmpl b/tools/src/cmd/gen/build/BUILD.bazel.tmpl
index 52130c7..de3a1c2 100644
--- a/tools/src/cmd/gen/build/BUILD.bazel.tmpl
+++ b/tools/src/cmd/gen/build/BUILD.bazel.tmpl
@@ -206,6 +206,7 @@
 {{-   else if eq $.Name "spirv-tools"                    -}}"@spirv_tools",
 {{-   else if eq $.Name "thread"                         -}}{{/* unsupported */}}
 {{-   else if eq $.Name "winsock"                        -}}{{/* unsupported */}}
+{{-   else if eq $.Name "src_utils_crash_handler"        -}}"//src/utils:crash_handler",
 {{-   else if eq $.Name "src_utils"                      -}}"//src/utils",
 {{-   else if eq $.Name "src_utils_chromium_test_compat" -}}"//src/utils/chromium_test_compat",
 {{-   else                                      -}}{{Error (printf "unhandled external dependency '%v'" $.Name)}}
diff --git a/tools/src/cmd/gen/build/BUILD.gn.tmpl b/tools/src/cmd/gen/build/BUILD.gn.tmpl
index c138adc..4a8b3ca 100644
--- a/tools/src/cmd/gen/build/BUILD.gn.tmpl
+++ b/tools/src/cmd/gen/build/BUILD.gn.tmpl
@@ -193,6 +193,7 @@
 {{-   else if eq $.Name "vulkan-headers"                 -}}"${dawn_vulkan_headers_dir}:vulkan_headers",
 {{-   else if eq $.Name "libvulkan"                      -}}"${dawn_vulkan_loader_dir}:libvulkan",
 {{-   else if eq $.Name "winsock"                        -}}"${tint_src_dir}:winsock",
+{{-   else if eq $.Name "src_utils_crash_handler"        -}}"${dawn_root}/src/utils:crash_handler",
 {{-   else if eq $.Name "src_utils"                      -}}"${dawn_root}/src/utils:utils",
 {{-   else if eq $.Name "src_utils_chromium_test_compat" -}}"${dawn_root}/src/utils/chromium_test_compat:chromium_test_compat",
 {{-   else                                      -}}{{Error (printf "unhandled external dependency '%v'" $.Name)}}