From ddc8c2b14dfc50e53c63885bf8c4cb88c9574170 Mon Sep 17 00:00:00 2001 From: Carl Peto Date: Wed, 19 Nov 2025 15:05:37 +0000 Subject: [PATCH] [Backtrace] show the crashed thread first in standard crash logs It can be hard to find the crashed thread. This always shows it first, before any other threads. rdar://164566321 --- .../public/libexec/swift-backtrace/main.swift | 5 +- test/Backtracing/CrashWithThreads.swift | 85 +++++++++++++++++++ 2 files changed, 87 insertions(+), 3 deletions(-) create mode 100644 test/Backtracing/CrashWithThreads.swift diff --git a/stdlib/public/libexec/swift-backtrace/main.swift b/stdlib/public/libexec/swift-backtrace/main.swift index 4a9be488049..a5df54afba6 100644 --- a/stdlib/public/libexec/swift-backtrace/main.swift +++ b/stdlib/public/libexec/swift-backtrace/main.swift @@ -894,12 +894,11 @@ Generate a backtrace for the parent process. } } + dump(ndx: target.crashingThreadNdx, thread: crashingThread) if args.threads! { - for (ndx, thread) in target.threads.enumerated() { + for (ndx, thread) in target.threads.enumerated() where ndx != target.crashingThreadNdx { dump(ndx: ndx, thread: thread) } - } else { - dump(ndx: target.crashingThreadNdx, thread: crashingThread) } if args.registers! == .crashedOnly { diff --git a/test/Backtracing/CrashWithThreads.swift b/test/Backtracing/CrashWithThreads.swift new file mode 100644 index 00000000000..c4ed1dca230 --- /dev/null +++ b/test/Backtracing/CrashWithThreads.swift @@ -0,0 +1,85 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -Onone -g -o %t/CrashWithThreads +// RUN: %target-codesign %t/CrashWithThreads +// RUN: (env SWIFT_BACKTRACE=enable=yes,cache=no,swift-backtrace=%backtracer %target-run %t/CrashWithThreads 2>&1 || true) | %FileCheck -vv %s -dump-input-filter=all + +// UNSUPPORTED: use_os_stdlib +// UNSUPPORTED: back_deployment_runtime +// UNSUPPORTED: asan +// REQUIRES: executable_test +// REQUIRES: backtracing +// REQUIRES: OS=macosx || OS=linux-gnu + +#if canImport(Darwin) + import Darwin +#elseif canImport(Glibc) + import Glibc +#elseif canImport(Android) + import Android +#elseif os(Windows) + import CRT +#else +#error("Unsupported platform") +#endif + +func reallyCrashMe() { + print("I'm going to crash now") + let ptr = UnsafeMutablePointer(bitPattern: 4)! + ptr.pointee = 42 +} + +func crashMe() { + reallyCrashMe() +} + +func spawnThread(_ shouldCrash: Bool) { + #if os(Linux) + var thread: pthread_t = 0 + #elseif os(macOS) + var thread = pthread_t(nil) + #endif + if shouldCrash { + pthread_create(&thread, nil, { _ in + crashMe() + // this should not be run + while (true) { + sleep(10) + } + }, nil) + } else { + pthread_create(&thread, nil, { _ in + while (true) { + sleep(10) + } + }, nil) + } +} + +let crashingThreadIndex = (1..<10).randomElement() + +for threadIndex in 0..<10 { + spawnThread(threadIndex == crashingThreadIndex) +} + +while (true) { + sleep(10) +} + +// CHECK: *** Program crashed: Bad pointer dereference at 0x{{0+}}4 *** + +// make sure there are no threads before the crashing thread (rdar://164566321) + +// CHECK-NOT: Thread {{[0-9]+}}: + +// CHECK: Thread {{[1-9]+}} {{(".*" )?}}crashed: + +// CHECK: 0 0x{{[0-9a-f]+}} reallyCrashMe() + {{[0-9]+}} in CrashWithThreads at {{.*}}/CrashWithThreads +// CHECK-NEXT: 1 [ra] 0x{{[0-9a-f]+}} crashMe() + {{[0-9]+}} in CrashWithThreads at {{.*}}/CrashWithThreads +// CHECK-NEXT: 2 [ra] 0x{{[0-9a-f]+}} closure #{{[0-9]}} in spawnThread(_:) + {{[0-9]+}} in CrashWithThreads at {{.*}}/CrashWithThreads +// CHECK-NEXT: 3 [ra] [thunk] 0x{{[0-9a-f]+}} @objc closure #{{[0-9]}} in spawnThread(_:) + {{[0-9]+}} in CrashWithThreads at {{.*}} + +// CHECK: Registers: + +// CHECK: Images ({{[0-9]+}} omitted): + +// CHECK: {{0x[0-9a-f]+}}–{{0x[0-9a-f]+}}{{ +}}{{([0-9a-f]+|)}}{{ +}}CrashWithThreads{{ +}}{{.*}}/CrashWithThreads