diff options
| author | bors <bors@rust-lang.org> | 2025-08-07 02:32:55 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2025-08-07 02:32:55 +0000 |
| commit | 61cb1e97fcf954c37d0a457a8084ed9ad8b3cb82 (patch) | |
| tree | 107887d30f4041adae19af258f83ca2dc0fb5d93 /library/std/src | |
| parent | 6bcdcc73bd11568fd85f5a38b58e1eda054ad1cd (diff) | |
| parent | 289fe36d373c5a13fa7f1b93f39ff88425ab2351 (diff) | |
| download | rust-61cb1e97fcf954c37d0a457a8084ed9ad8b3cb82.tar.gz rust-61cb1e97fcf954c37d0a457a8084ed9ad8b3cb82.zip | |
Auto merge of #115746 - tgross35:unnamed-threads-panic-message, r=cuviper
Print thread ID in panic message
`panic!` does not print any identifying information for threads that are
unnamed. However, in many cases, the thread ID can be determined.
This changes the panic message from something like this:
thread '<unnamed>' panicked at src/main.rs:3:5:
explicit panic
To something like this:
thread '<unnamed>' (12345) panicked at src/main.rs:3:5:
explicit panic
Stack overflow messages are updated as well.
This change applies to both named and unnamed threads. The ID printed is
the OS integer thread ID rather than the Rust thread ID, which should
also be what debuggers print.
try-job: aarch64-apple
try-job: aarch64-gnu
try-job: dist-apple-various
try-job: dist-various-*
try-job: dist-x86_64-freebsd
try-job: dist-x86_64-illumos
try-job: dist-x86_64-netbsd
try-job: dist-x86_64-solaris
try-job: test-various
try-job: x86_64-gnu
try-job: x86_64-mingw-1
try-job: x86_64-msvc-1
Diffstat (limited to 'library/std/src')
| -rw-r--r-- | library/std/src/panicking.rs | 3 | ||||
| -rw-r--r-- | library/std/src/sys/pal/hermit/thread.rs | 4 | ||||
| -rw-r--r-- | library/std/src/sys/pal/itron/thread.rs | 4 | ||||
| -rw-r--r-- | library/std/src/sys/pal/sgx/thread.rs | 6 | ||||
| -rw-r--r-- | library/std/src/sys/pal/teeos/thread.rs | 4 | ||||
| -rw-r--r-- | library/std/src/sys/pal/uefi/thread.rs | 4 | ||||
| -rw-r--r-- | library/std/src/sys/pal/unix/stack_overflow.rs | 6 | ||||
| -rw-r--r-- | library/std/src/sys/pal/unix/thread.rs | 56 | ||||
| -rw-r--r-- | library/std/src/sys/pal/unsupported/thread.rs | 4 | ||||
| -rw-r--r-- | library/std/src/sys/pal/wasi/thread.rs | 4 | ||||
| -rw-r--r-- | library/std/src/sys/pal/windows/c/bindings.txt | 1 | ||||
| -rw-r--r-- | library/std/src/sys/pal/windows/c/windows_sys.rs | 1 | ||||
| -rw-r--r-- | library/std/src/sys/pal/windows/stack_overflow.rs | 3 | ||||
| -rw-r--r-- | library/std/src/sys/pal/windows/thread.rs | 8 | ||||
| -rw-r--r-- | library/std/src/sys/pal/xous/thread.rs | 4 | ||||
| -rw-r--r-- | library/std/src/thread/current.rs | 13 | ||||
| -rw-r--r-- | library/std/src/thread/mod.rs | 2 | ||||
| -rw-r--r-- | library/std/src/thread/tests.rs | 7 |
18 files changed, 127 insertions, 7 deletions
diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index 224cd39855a..547906ca7dc 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -269,6 +269,7 @@ fn default_hook(info: &PanicHookInfo<'_>) { thread::with_current_name(|name| { let name = name.unwrap_or("<unnamed>"); + let tid = thread::current_os_id(); // Try to write the panic message to a buffer first to prevent other concurrent outputs // interleaving with it. @@ -277,7 +278,7 @@ fn default_hook(info: &PanicHookInfo<'_>) { let write_msg = |dst: &mut dyn crate::io::Write| { // We add a newline to ensure the panic message appears at the start of a line. - writeln!(dst, "\nthread '{name}' panicked at {location}:\n{msg}") + writeln!(dst, "\nthread '{name}' ({tid}) panicked at {location}:\n{msg}") }; if write_msg(&mut cursor).is_ok() { diff --git a/library/std/src/sys/pal/hermit/thread.rs b/library/std/src/sys/pal/hermit/thread.rs index 95fe4f902d3..cc4734b6819 100644 --- a/library/std/src/sys/pal/hermit/thread.rs +++ b/library/std/src/sys/pal/hermit/thread.rs @@ -115,6 +115,10 @@ impl Thread { } } +pub(crate) fn current_os_id() -> Option<u64> { + None +} + pub fn available_parallelism() -> io::Result<NonZero<usize>> { unsafe { Ok(NonZero::new_unchecked(hermit_abi::available_parallelism())) } } diff --git a/library/std/src/sys/pal/itron/thread.rs b/library/std/src/sys/pal/itron/thread.rs index 0d28051fcc4..4e14cb3cbca 100644 --- a/library/std/src/sys/pal/itron/thread.rs +++ b/library/std/src/sys/pal/itron/thread.rs @@ -361,6 +361,10 @@ unsafe fn terminate_and_delete_current_task() -> ! { unsafe { crate::hint::unreachable_unchecked() }; } +pub(crate) fn current_os_id() -> Option<u64> { + None +} + pub fn available_parallelism() -> io::Result<NonZero<usize>> { super::unsupported() } diff --git a/library/std/src/sys/pal/sgx/thread.rs b/library/std/src/sys/pal/sgx/thread.rs index a236c362706..1f613badcd7 100644 --- a/library/std/src/sys/pal/sgx/thread.rs +++ b/library/std/src/sys/pal/sgx/thread.rs @@ -1,6 +1,6 @@ #![cfg_attr(test, allow(dead_code))] // why is this necessary? -use super::abi::usercalls; +use super::abi::{thread, usercalls}; use super::unsupported; use crate::ffi::CStr; use crate::io; @@ -149,6 +149,10 @@ impl Thread { } } +pub(crate) fn current_os_id() -> Option<u64> { + Some(thread::current().addr().get() as u64) +} + pub fn available_parallelism() -> io::Result<NonZero<usize>> { unsupported() } diff --git a/library/std/src/sys/pal/teeos/thread.rs b/library/std/src/sys/pal/teeos/thread.rs index a91d95626e7..1812d11e692 100644 --- a/library/std/src/sys/pal/teeos/thread.rs +++ b/library/std/src/sys/pal/teeos/thread.rs @@ -144,6 +144,10 @@ impl Drop for Thread { } } +pub(crate) fn current_os_id() -> Option<u64> { + None +} + // Note: Both `sched_getaffinity` and `sysconf` are available but not functional on // teeos, so this function always returns an Error! pub fn available_parallelism() -> io::Result<NonZero<usize>> { diff --git a/library/std/src/sys/pal/uefi/thread.rs b/library/std/src/sys/pal/uefi/thread.rs index 75c364362b2..47a48008c76 100644 --- a/library/std/src/sys/pal/uefi/thread.rs +++ b/library/std/src/sys/pal/uefi/thread.rs @@ -56,6 +56,10 @@ impl Thread { } } +pub(crate) fn current_os_id() -> Option<u64> { + None +} + pub fn available_parallelism() -> io::Result<NonZero<usize>> { // UEFI is single threaded Ok(NonZero::new(1).unwrap()) diff --git a/library/std/src/sys/pal/unix/stack_overflow.rs b/library/std/src/sys/pal/unix/stack_overflow.rs index d89100e6919..0d2100d66bc 100644 --- a/library/std/src/sys/pal/unix/stack_overflow.rs +++ b/library/std/src/sys/pal/unix/stack_overflow.rs @@ -119,7 +119,8 @@ mod imp { && thread_info.guard_page_range.contains(&fault_addr) { let name = thread_info.thread_name.as_deref().unwrap_or("<unknown>"); - rtprintpanic!("\nthread '{name}' has overflowed its stack\n"); + let tid = crate::thread::current_os_id(); + rtprintpanic!("\nthread '{name}' ({tid}) has overflowed its stack\n"); rtabort!("stack overflow"); } }) @@ -696,7 +697,8 @@ mod imp { if code == c::EXCEPTION_STACK_OVERFLOW { crate::thread::with_current_name(|name| { let name = name.unwrap_or("<unknown>"); - rtprintpanic!("\nthread '{name}' has overflowed its stack\n"); + let tid = crate::thread::current_os_id(); + rtprintpanic!("\nthread '{name}' ({tid}) has overflowed its stack\n"); }); } c::EXCEPTION_CONTINUE_SEARCH diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs index 7f6440152d4..36e53e7cadc 100644 --- a/library/std/src/sys/pal/unix/thread.rs +++ b/library/std/src/sys/pal/unix/thread.rs @@ -398,6 +398,62 @@ impl Drop for Thread { } } +pub(crate) fn current_os_id() -> Option<u64> { + // Most Unix platforms have a way to query an integer ID of the current thread, all with + // slightly different spellings. + // + // The OS thread ID is used rather than `pthread_self` so as to match what will be displayed + // for process inspection (debuggers, trace, `top`, etc.). + cfg_if::cfg_if! { + // Most platforms have a function returning a `pid_t` or int, which is an `i32`. + if #[cfg(any(target_os = "android", target_os = "linux"))] { + use crate::sys::weak::syscall; + + // `libc::gettid` is only available on glibc 2.30+, but the syscall is available + // since Linux 2.4.11. + syscall!(fn gettid() -> libc::pid_t;); + + // SAFETY: FFI call with no preconditions. + let id: libc::pid_t = unsafe { gettid() }; + Some(id as u64) + } else if #[cfg(target_os = "nto")] { + // SAFETY: FFI call with no preconditions. + let id: libc::pid_t = unsafe { libc::gettid() }; + Some(id as u64) + } else if #[cfg(target_os = "openbsd")] { + // SAFETY: FFI call with no preconditions. + let id: libc::pid_t = unsafe { libc::getthrid() }; + Some(id as u64) + } else if #[cfg(target_os = "freebsd")] { + // SAFETY: FFI call with no preconditions. + let id: libc::c_int = unsafe { libc::pthread_getthreadid_np() }; + Some(id as u64) + } else if #[cfg(target_os = "netbsd")] { + // SAFETY: FFI call with no preconditions. + let id: libc::lwpid_t = unsafe { libc::_lwp_self() }; + Some(id as u64) + } else if #[cfg(any(target_os = "illumos", target_os = "solaris"))] { + // On Illumos and Solaris, the `pthread_t` is the same as the OS thread ID. + // SAFETY: FFI call with no preconditions. + let id: libc::pthread_t = unsafe { libc::pthread_self() }; + Some(id as u64) + } else if #[cfg(target_vendor = "apple")] { + // Apple allows querying arbitrary thread IDs, `thread=NULL` queries the current thread. + let mut id = 0u64; + // SAFETY: `thread_id` is a valid pointer, no other preconditions. + let status: libc::c_int = unsafe { libc::pthread_threadid_np(0, &mut id) }; + if status == 0 { + Some(id) + } else { + None + } + } else { + // Other platforms don't have an OS thread ID or don't have a way to access it. + None + } + } +} + #[cfg(any( target_os = "linux", target_os = "nto", diff --git a/library/std/src/sys/pal/unsupported/thread.rs b/library/std/src/sys/pal/unsupported/thread.rs index 5a1e3fde986..34d9b5ec70c 100644 --- a/library/std/src/sys/pal/unsupported/thread.rs +++ b/library/std/src/sys/pal/unsupported/thread.rs @@ -39,6 +39,10 @@ impl Thread { } } +pub(crate) fn current_os_id() -> Option<u64> { + None +} + pub fn available_parallelism() -> io::Result<NonZero<usize>> { unsupported() } diff --git a/library/std/src/sys/pal/wasi/thread.rs b/library/std/src/sys/pal/wasi/thread.rs index a46c74630c9..4755e2ef5da 100644 --- a/library/std/src/sys/pal/wasi/thread.rs +++ b/library/std/src/sys/pal/wasi/thread.rs @@ -194,6 +194,10 @@ impl Thread { } } +pub(crate) fn current_os_id() -> Option<u64> { + None +} + pub fn available_parallelism() -> io::Result<NonZero<usize>> { cfg_if::cfg_if! { if #[cfg(target_feature = "atomics")] { diff --git a/library/std/src/sys/pal/windows/c/bindings.txt b/library/std/src/sys/pal/windows/c/bindings.txt index 827d96e73db..c8e4dca4781 100644 --- a/library/std/src/sys/pal/windows/c/bindings.txt +++ b/library/std/src/sys/pal/windows/c/bindings.txt @@ -2185,6 +2185,7 @@ GetSystemInfo GetSystemTimeAsFileTime GetSystemTimePreciseAsFileTime GetTempPathW +GetThreadId GetUserProfileDirectoryW GetWindowsDirectoryW HANDLE diff --git a/library/std/src/sys/pal/windows/c/windows_sys.rs b/library/std/src/sys/pal/windows/c/windows_sys.rs index b2e3aabc633..45a273d241a 100644 --- a/library/std/src/sys/pal/windows/c/windows_sys.rs +++ b/library/std/src/sys/pal/windows/c/windows_sys.rs @@ -61,6 +61,7 @@ windows_targets::link!("kernel32.dll" "system" fn GetSystemInfo(lpsysteminfo : * windows_targets::link!("kernel32.dll" "system" fn GetSystemTimeAsFileTime(lpsystemtimeasfiletime : *mut FILETIME)); windows_targets::link!("kernel32.dll" "system" fn GetSystemTimePreciseAsFileTime(lpsystemtimeasfiletime : *mut FILETIME)); windows_targets::link!("kernel32.dll" "system" fn GetTempPathW(nbufferlength : u32, lpbuffer : PWSTR) -> u32); +windows_targets::link!("kernel32.dll" "system" fn GetThreadId(thread : HANDLE) -> u32); windows_targets::link!("userenv.dll" "system" fn GetUserProfileDirectoryW(htoken : HANDLE, lpprofiledir : PWSTR, lpcchsize : *mut u32) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn GetWindowsDirectoryW(lpbuffer : PWSTR, usize : u32) -> u32); windows_targets::link!("kernel32.dll" "system" fn InitOnceBeginInitialize(lpinitonce : *mut INIT_ONCE, dwflags : u32, fpending : *mut BOOL, lpcontext : *mut *mut core::ffi::c_void) -> BOOL); diff --git a/library/std/src/sys/pal/windows/stack_overflow.rs b/library/std/src/sys/pal/windows/stack_overflow.rs index 734cd30bed0..9a40551b985 100644 --- a/library/std/src/sys/pal/windows/stack_overflow.rs +++ b/library/std/src/sys/pal/windows/stack_overflow.rs @@ -20,7 +20,8 @@ unsafe extern "system" fn vectored_handler(ExceptionInfo: *mut c::EXCEPTION_POIN if code == c::EXCEPTION_STACK_OVERFLOW { thread::with_current_name(|name| { let name = name.unwrap_or("<unknown>"); - rtprintpanic!("\nthread '{name}' has overflowed its stack\n"); + let tid = thread::current_os_id(); + rtprintpanic!("\nthread '{name}' ({tid}) has overflowed its stack\n"); }); } c::EXCEPTION_CONTINUE_SEARCH diff --git a/library/std/src/sys/pal/windows/thread.rs b/library/std/src/sys/pal/windows/thread.rs index b45f76fb546..c708da5af12 100644 --- a/library/std/src/sys/pal/windows/thread.rs +++ b/library/std/src/sys/pal/windows/thread.rs @@ -127,6 +127,14 @@ impl Thread { } } +pub(crate) fn current_os_id() -> Option<u64> { + // SAFETY: FFI call with no preconditions. + let id: u32 = unsafe { c::GetThreadId(c::GetCurrentThread()) }; + + // A return value of 0 indicates failed lookup. + if id == 0 { None } else { Some(id.into()) } +} + pub fn available_parallelism() -> io::Result<NonZero<usize>> { let res = unsafe { let mut sysinfo: c::SYSTEM_INFO = crate::mem::zeroed(); diff --git a/library/std/src/sys/pal/xous/thread.rs b/library/std/src/sys/pal/xous/thread.rs index f2404a62abf..92803c94c6e 100644 --- a/library/std/src/sys/pal/xous/thread.rs +++ b/library/std/src/sys/pal/xous/thread.rs @@ -145,6 +145,10 @@ impl Thread { } } +pub(crate) fn current_os_id() -> Option<u64> { + None +} + pub fn available_parallelism() -> io::Result<NonZero<usize>> { // We're unicore right now. Ok(unsafe { NonZero::new_unchecked(1) }) diff --git a/library/std/src/thread/current.rs b/library/std/src/thread/current.rs index 414711298f0..5c879903526 100644 --- a/library/std/src/thread/current.rs +++ b/library/std/src/thread/current.rs @@ -1,4 +1,4 @@ -use super::{Thread, ThreadId}; +use super::{Thread, ThreadId, imp}; use crate::mem::ManuallyDrop; use crate::ptr; use crate::sys::thread_local::local_pointer; @@ -148,6 +148,17 @@ pub(crate) fn current_id() -> ThreadId { id::get_or_init() } +/// Gets the OS thread ID of the thread that invokes it, if available. If not, return the Rust +/// thread ID. +/// +/// We use a `u64` to all possible platform IDs without excess `cfg`; most use `int`, some use a +/// pointer, and Apple uses `uint64_t`. This is a "best effort" approach for diagnostics and is +/// allowed to fall back to a non-OS ID (such as the Rust thread ID) or a non-unique ID (such as a +/// PID) if the thread ID cannot be retrieved. +pub(crate) fn current_os_id() -> u64 { + imp::current_os_id().unwrap_or_else(|| current_id().as_u64().get()) +} + /// Gets a reference to the handle of the thread that invokes it, if the handle /// has been initialized. pub(super) fn try_with_current<F, R>(f: F) -> R diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 8d3ab82ace1..292323d0118 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -183,7 +183,7 @@ mod current; #[stable(feature = "rust1", since = "1.0.0")] pub use current::current; -pub(crate) use current::{current_id, current_or_unnamed, drop_current}; +pub(crate) use current::{current_id, current_or_unnamed, current_os_id, drop_current}; use current::{set_current, try_with_current}; mod spawnhook; diff --git a/library/std/src/thread/tests.rs b/library/std/src/thread/tests.rs index 59ec48a57d1..ae889f1e778 100644 --- a/library/std/src/thread/tests.rs +++ b/library/std/src/thread/tests.rs @@ -347,6 +347,13 @@ fn test_thread_id_not_equal() { } #[test] +fn test_thread_os_id_not_equal() { + let spawned_id = thread::spawn(|| thread::current_os_id()).join().unwrap(); + let current_id = thread::current_os_id(); + assert!(current_id != spawned_id); +} + +#[test] fn test_scoped_threads_drop_result_before_join() { let actually_finished = &AtomicBool::new(false); struct X<'scope, 'env>(&'scope Scope<'scope, 'env>, &'env AtomicBool); |
