diff options
Diffstat (limited to 'tests/ui/runtime')
-rw-r--r-- | tests/ui/runtime/atomic-print.rs | 45 | ||||
-rw-r--r-- | tests/ui/runtime/backtrace-debuginfo-aux.rs | 14 | ||||
-rw-r--r-- | tests/ui/runtime/backtrace-debuginfo.rs | 190 | ||||
-rw-r--r-- | tests/ui/runtime/native-print-no-runtime.rs | 9 | ||||
-rw-r--r-- | tests/ui/runtime/out-of-stack.rs | 88 | ||||
-rw-r--r-- | tests/ui/runtime/rt-explody-panic-payloads.rs | 33 | ||||
-rw-r--r-- | tests/ui/runtime/running-with-no-runtime.rs | 52 | ||||
-rw-r--r-- | tests/ui/runtime/signal-alternate-stack-cleanup.rs | 37 | ||||
-rw-r--r-- | tests/ui/runtime/stdout-during-shutdown.rs | 19 | ||||
-rw-r--r-- | tests/ui/runtime/stdout-during-shutdown.run.stdout | 1 |
10 files changed, 488 insertions, 0 deletions
diff --git a/tests/ui/runtime/atomic-print.rs b/tests/ui/runtime/atomic-print.rs new file mode 100644 index 000000000..fe5791053 --- /dev/null +++ b/tests/ui/runtime/atomic-print.rs @@ -0,0 +1,45 @@ +// run-pass + +#![allow(unused_must_use)] +#![allow(deprecated)] +// ignore-emscripten no threads support +// ignore-sgx no processes + +use std::{env, fmt, process, sync, thread}; + +struct SlowFmt(u32); +impl fmt::Debug for SlowFmt { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + thread::sleep_ms(3); + self.0.fmt(f) + } +} + +fn do_print(x: u32) { + let x = SlowFmt(x); + println!("{:?}{:?}{:?}{:?}{:?}", x, x, x, x, x); +} + +fn main(){ + if env::args().count() == 2 { + let barrier = sync::Arc::new(sync::Barrier::new(2)); + let tbarrier = barrier.clone(); + let t = thread::spawn(move || { + tbarrier.wait(); + do_print(1); + }); + barrier.wait(); + do_print(2); + t.join(); + } else { + let this = env::args().next().unwrap(); + let output = process::Command::new(this).arg("-").output().unwrap(); + for line in String::from_utf8(output.stdout).unwrap().lines() { + match line.chars().next().unwrap() { + '1' => assert_eq!(line, "11111"), + '2' => assert_eq!(line, "22222"), + chr => panic!("unexpected character {:?}", chr) + } + } + } +} diff --git a/tests/ui/runtime/backtrace-debuginfo-aux.rs b/tests/ui/runtime/backtrace-debuginfo-aux.rs new file mode 100644 index 000000000..1411bcf89 --- /dev/null +++ b/tests/ui/runtime/backtrace-debuginfo-aux.rs @@ -0,0 +1,14 @@ +// run-pass +// ignore-test: not a test, used by backtrace-debuginfo.rs to test file!() + +#[inline(never)] +pub fn callback<F>(f: F) where F: FnOnce((&'static str, u32)) { + f((file!(), line!())) +} + +// We emit the wrong location for the caller here when inlined on MSVC +#[cfg_attr(not(target_env = "msvc"), inline(always))] +#[cfg_attr(target_env = "msvc", inline(never))] +pub fn callback_inlined<F>(f: F) where F: FnOnce((&'static str, u32)) { + f((file!(), line!())) +} diff --git a/tests/ui/runtime/backtrace-debuginfo.rs b/tests/ui/runtime/backtrace-debuginfo.rs new file mode 100644 index 000000000..8b5466b6c --- /dev/null +++ b/tests/ui/runtime/backtrace-debuginfo.rs @@ -0,0 +1,190 @@ +// run-pass +// We disable tail merging here because it can't preserve debuginfo and thus +// potentially breaks the backtraces. Also, subtle changes can decide whether +// tail merging succeeds, so the test might work today but fail tomorrow due to a +// seemingly completely unrelated change. +// Unfortunately, LLVM has no "disable" option for this, so we have to set +// "enable" to 0 instead. + +// compile-flags:-g -Copt-level=0 -Cllvm-args=-enable-tail-merge=0 +// compile-flags:-Cforce-frame-pointers=yes +// compile-flags:-Cstrip=none +// ignore-pretty issue #37195 +// ignore-emscripten spawning processes is not supported +// ignore-sgx no processes +// ignore-fuchsia Backtrace not symbolized, trace different line alignment + +use std::env; + +#[path = "backtrace-debuginfo-aux.rs"] mod aux; + +macro_rules! pos { + () => ((file!(), line!())) +} + +macro_rules! dump_and_die { + ($($pos:expr),*) => ({ + // FIXME(#18285): we cannot include the current position because + // the macro span takes over the last frame's file/line. + // + // You might also be wondering why a major platform, + // i686-pc-windows-msvc, is located in here. Some of the saga can be + // found on #62897, but the tl;dr; is that it appears that if the + // standard library doesn't have debug information or frame pointers, + // which it doesn't by default on the test builders, then the stack + // walking routines in dbghelp will randomly terminate the stack trace + // in libstd without going further. Presumably the addition of frame + // pointers and/or debuginfo fixes this since tests always work with + // nightly compilers (which have debuginfo). In general though this test + // is replicated in rust-lang/backtrace-rs and has extensive coverage + // there, even on i686-pc-windows-msvc. We do the best we can in + // rust-lang/rust to test it as well, but sometimes we just gotta keep + // landing PRs. + if cfg!(any(target_os = "android", + all(target_os = "linux", target_arch = "arm"), + all(target_env = "msvc", target_arch = "x86"), + target_os = "freebsd", + target_os = "dragonfly", + target_os = "openbsd")) { + // skip these platforms as this support isn't implemented yet. + } else { + dump_filelines(&[$($pos),*]); + panic!(); + } + }) +} + +// we can't use a function as it will alter the backtrace +macro_rules! check { + ($counter:expr; $($pos:expr),*) => ({ + if *$counter == 0 { + dump_and_die!($($pos),*) + } else { + *$counter -= 1; + } + }) +} + +type Pos = (&'static str, u32); + +// this goes to stdout and each line has to be occurred +// in the following backtrace to stderr with a correct order. +fn dump_filelines(filelines: &[Pos]) { + for &(file, line) in filelines.iter().rev() { + // extract a basename + let basename = file.split(&['/', '\\'][..]).last().unwrap(); + println!("{}:{}", basename, line); + } +} + +#[inline(never)] +fn inner(counter: &mut i32, main_pos: Pos, outer_pos: Pos) { + check!(counter; main_pos, outer_pos); + check!(counter; main_pos, outer_pos); + let inner_pos = pos!(); aux::callback(|aux_pos| { + check!(counter; main_pos, outer_pos, inner_pos, aux_pos); + }); + let inner_pos = pos!(); aux::callback_inlined(|aux_pos| { + check!(counter; main_pos, outer_pos, inner_pos, aux_pos); + }); +} + +// We emit the wrong location for the caller here when inlined on MSVC +#[cfg_attr(not(target_env = "msvc"), inline(always))] +#[cfg_attr(target_env = "msvc", inline(never))] +fn inner_inlined(counter: &mut i32, main_pos: Pos, outer_pos: Pos) { + check!(counter; main_pos, outer_pos); + check!(counter; main_pos, outer_pos); + + // Again, disable inlining for MSVC. + #[cfg_attr(not(target_env = "msvc"), inline(always))] + #[cfg_attr(target_env = "msvc", inline(never))] + fn inner_further_inlined(counter: &mut i32, main_pos: Pos, outer_pos: Pos, inner_pos: Pos) { + check!(counter; main_pos, outer_pos, inner_pos); + } + inner_further_inlined(counter, main_pos, outer_pos, pos!()); + + let inner_pos = pos!(); aux::callback(|aux_pos| { + check!(counter; main_pos, outer_pos, inner_pos, aux_pos); + }); + let inner_pos = pos!(); aux::callback_inlined(|aux_pos| { + check!(counter; main_pos, outer_pos, inner_pos, aux_pos); + }); + + // this tests a distinction between two independent calls to the inlined function. + // (un)fortunately, LLVM somehow merges two consecutive such calls into one node. + inner_further_inlined(counter, main_pos, outer_pos, pos!()); +} + +#[inline(never)] +fn outer(mut counter: i32, main_pos: Pos) { + inner(&mut counter, main_pos, pos!()); + inner_inlined(&mut counter, main_pos, pos!()); +} + +fn check_trace(output: &str, error: &str) -> Result<(), String> { + // reverse the position list so we can start with the last item (which was the first line) + let mut remaining: Vec<&str> = output.lines().map(|s| s.trim()).rev().collect(); + + if !error.contains("stack backtrace") { + return Err(format!("no backtrace found in stderr:\n{}", error)) + } + for line in error.lines() { + if !remaining.is_empty() && line.contains(remaining.last().unwrap()) { + remaining.pop(); + } + } + if !remaining.is_empty() { + return Err(format!("trace does not match position list\n\ + still need to find {:?}\n\n\ + --- stdout\n{}\n\ + --- stderr\n{}", + remaining, output, error)) + } + Ok(()) +} + +fn run_test(me: &str) { + use std::str; + use std::process::Command; + + let mut i = 0; + let mut errors = Vec::new(); + loop { + let out = Command::new(me) + .env("RUST_BACKTRACE", "full") + .arg(i.to_string()).output().unwrap(); + let output = str::from_utf8(&out.stdout).unwrap(); + let error = str::from_utf8(&out.stderr).unwrap(); + if out.status.success() { + assert!(output.contains("done."), "bad output for successful run: {}", output); + break; + } else { + if let Err(e) = check_trace(output, error) { + errors.push(e); + } + } + i += 1; + } + if errors.len() > 0 { + for error in errors { + println!("---------------------------------------"); + println!("{}", error); + } + + panic!("found some errors"); + } +} + +#[inline(never)] +fn main() { + let args: Vec<String> = env::args().collect(); + if args.len() >= 2 { + let case = args[1].parse().unwrap(); + eprintln!("test case {}", case); + outer(case, pos!()); + println!("done."); + } else { + run_test(&args[0]); + } +} diff --git a/tests/ui/runtime/native-print-no-runtime.rs b/tests/ui/runtime/native-print-no-runtime.rs new file mode 100644 index 000000000..f17c9fa6c --- /dev/null +++ b/tests/ui/runtime/native-print-no-runtime.rs @@ -0,0 +1,9 @@ +// run-pass + +#![feature(start)] + +#[start] +pub fn main(_: isize, _: *const *const u8) -> isize { + println!("hello"); + 0 +} diff --git a/tests/ui/runtime/out-of-stack.rs b/tests/ui/runtime/out-of-stack.rs new file mode 100644 index 000000000..6873abc49 --- /dev/null +++ b/tests/ui/runtime/out-of-stack.rs @@ -0,0 +1,88 @@ +// run-pass + +#![allow(unused_must_use)] +#![allow(unconditional_recursion)] +// ignore-android: FIXME (#20004) +// ignore-emscripten no processes +// ignore-sgx no processes +// ignore-fuchsia must translate zircon signal to SIGABRT, FIXME (#58590) + +#![feature(core_intrinsics)] +#![feature(rustc_private)] + +#[cfg(unix)] +extern crate libc; + +use std::env; +use std::process::Command; +use std::thread; + +// Inlining to avoid llvm turning the recursive functions into tail calls, +// which doesn't consume stack. +#[inline(always)] +pub fn black_box<T>(dummy: T) { std::intrinsics::black_box(dummy); } + +fn silent_recurse() { + let buf = [0u8; 1000]; + black_box(buf); + silent_recurse(); +} + +fn loud_recurse() { + println!("hello!"); + loud_recurse(); + black_box(()); // don't optimize this into a tail call. please. +} + +#[cfg(unix)] +fn check_status(status: std::process::ExitStatus) +{ + use std::os::unix::process::ExitStatusExt; + + assert!(!status.success()); + assert_eq!(status.signal(), Some(libc::SIGABRT)); +} + +#[cfg(not(unix))] +fn check_status(status: std::process::ExitStatus) +{ + assert!(!status.success()); +} + + +fn main() { + let args: Vec<String> = env::args().collect(); + if args.len() > 1 && args[1] == "silent" { + silent_recurse(); + } else if args.len() > 1 && args[1] == "loud" { + loud_recurse(); + } else if args.len() > 1 && args[1] == "silent-thread" { + thread::spawn(silent_recurse).join(); + } else if args.len() > 1 && args[1] == "loud-thread" { + thread::spawn(loud_recurse).join(); + } else { + let mut modes = vec![ + "silent-thread", + "loud-thread", + ]; + + // On linux it looks like the main thread can sometimes grow its stack + // basically without bounds, so we only test the child thread cases + // there. + if !cfg!(target_os = "linux") { + modes.push("silent"); + modes.push("loud"); + } + for mode in modes { + println!("testing: {}", mode); + + let silent = Command::new(&args[0]).arg(mode).output().unwrap(); + + check_status(silent.status); + + let error = String::from_utf8_lossy(&silent.stderr); + assert!(error.contains("has overflowed its stack"), + "missing overflow message: {}", error); + } + } +} diff --git a/tests/ui/runtime/rt-explody-panic-payloads.rs b/tests/ui/runtime/rt-explody-panic-payloads.rs new file mode 100644 index 000000000..755d3df42 --- /dev/null +++ b/tests/ui/runtime/rt-explody-panic-payloads.rs @@ -0,0 +1,33 @@ +// run-pass +// needs-unwind +// ignore-emscripten no processes +// ignore-sgx no processes + +use std::env; +use std::process::Command; + +struct Bomb; + +impl Drop for Bomb { + fn drop(&mut self) { + std::panic::panic_any(Bomb); + } +} + +fn main() { + let args = env::args().collect::<Vec<_>>(); + let output = match &args[..] { + [me] => Command::new(&me).arg("plant the").output(), + [..] => std::panic::panic_any(Bomb), + }.expect("running the command should have succeeded"); + println!("{:#?}", output); + let stderr = std::str::from_utf8(&output.stderr); + assert!(stderr + .map(|v| { + // When running inside QEMU user-mode emulation, there will be an extra message printed + // by QEMU in the stderr whenever a core dump happens. Remove it before the check. + v.strip_suffix("qemu: uncaught target signal 6 (Aborted) - core dumped\n").unwrap_or(v) + }) + .map(|v| { v.ends_with("fatal runtime error: drop of the panic payload panicked\n") }) + .unwrap_or(false)); +} diff --git a/tests/ui/runtime/running-with-no-runtime.rs b/tests/ui/runtime/running-with-no-runtime.rs new file mode 100644 index 000000000..c575a6bec --- /dev/null +++ b/tests/ui/runtime/running-with-no-runtime.rs @@ -0,0 +1,52 @@ +// run-pass +// ignore-emscripten spawning processes is not supported +// ignore-sgx no processes +// revisions: mir thir +// [thir]compile-flags: -Zthir-unsafeck + +#![feature(start)] + +use std::ffi::CStr; +use std::process::{Command, Output}; +use std::panic; +use std::str; + +#[start] +fn start(argc: isize, argv: *const *const u8) -> isize { + if argc > 1 { + unsafe { + match **argv.offset(1) as char { + '1' => {} + '2' => println!("foo"), + '3' => assert!(panic::catch_unwind(|| {}).is_ok()), + '4' => assert!(panic::catch_unwind(|| panic!()).is_err()), + '5' => assert!(Command::new("test").spawn().is_err()), + _ => panic!() + } + } + return 0 + } + + let args = unsafe { + (0..argc as usize).map(|i| { + let ptr = *argv.add(i) as *const _; + CStr::from_ptr(ptr).to_bytes().to_vec() + }).collect::<Vec<_>>() + }; + let me = String::from_utf8(args[0].to_vec()).unwrap(); + + pass(Command::new(&me).arg("1").output().unwrap()); + pass(Command::new(&me).arg("2").output().unwrap()); + pass(Command::new(&me).arg("3").output().unwrap()); + pass(Command::new(&me).arg("4").output().unwrap()); + pass(Command::new(&me).arg("5").output().unwrap()); + + 0 +} + +fn pass(output: Output) { + if !output.status.success() { + println!("{:?}", str::from_utf8(&output.stdout)); + println!("{:?}", str::from_utf8(&output.stderr)); + } +} diff --git a/tests/ui/runtime/signal-alternate-stack-cleanup.rs b/tests/ui/runtime/signal-alternate-stack-cleanup.rs new file mode 100644 index 000000000..8a6d73895 --- /dev/null +++ b/tests/ui/runtime/signal-alternate-stack-cleanup.rs @@ -0,0 +1,37 @@ +// run-pass +// Previously memory for alternate signal stack have been unmapped during +// main thread exit while still being in use by signal handlers. This test +// triggers this situation by sending signal from atexit handler. +// +// ignore-wasm32-bare no libc +// ignore-windows +// ignore-sgx no libc +// ignore-vxworks no SIGWINCH in user space + +#![feature(rustc_private)] +extern crate libc; + +use libc::*; + +unsafe extern "C" fn signal_handler(signum: c_int, _: *mut siginfo_t, _: *mut c_void) { + assert_eq!(signum, SIGWINCH); +} + +extern "C" fn send_signal() { + unsafe { + raise(SIGWINCH); + } +} + +fn main() { + unsafe { + // Install signal handler that runs on alternate signal stack. + let mut action: sigaction = std::mem::zeroed(); + action.sa_flags = (SA_ONSTACK | SA_SIGINFO) as _; + action.sa_sigaction = signal_handler as sighandler_t; + sigaction(SIGWINCH, &action, std::ptr::null_mut()); + + // Send SIGWINCH on exit. + atexit(send_signal); + } +} diff --git a/tests/ui/runtime/stdout-during-shutdown.rs b/tests/ui/runtime/stdout-during-shutdown.rs new file mode 100644 index 000000000..a6cf812ca --- /dev/null +++ b/tests/ui/runtime/stdout-during-shutdown.rs @@ -0,0 +1,19 @@ +// run-pass +// check-run-results +// ignore-emscripten + +// Emscripten doesn't flush its own stdout buffers on exit, which would fail +// this test. So this test is disabled on this platform. +// See https://emscripten.org/docs/getting_started/FAQ.html#what-does-exiting-the-runtime-mean-why-don-t-atexit-s-run + +#![feature(rustc_private)] + +extern crate libc; + +fn main() { + extern "C" fn bye() { + print!(", world!"); + } + unsafe { libc::atexit(bye) }; + print!("hello"); +} diff --git a/tests/ui/runtime/stdout-during-shutdown.run.stdout b/tests/ui/runtime/stdout-during-shutdown.run.stdout new file mode 100644 index 000000000..30f51a3fb --- /dev/null +++ b/tests/ui/runtime/stdout-during-shutdown.run.stdout @@ -0,0 +1 @@ +hello, world!
\ No newline at end of file |