summaryrefslogtreecommitdiffstats
path: root/tests/ui/runtime
diff options
context:
space:
mode:
Diffstat (limited to 'tests/ui/runtime')
-rw-r--r--tests/ui/runtime/atomic-print.rs45
-rw-r--r--tests/ui/runtime/backtrace-debuginfo-aux.rs14
-rw-r--r--tests/ui/runtime/backtrace-debuginfo.rs190
-rw-r--r--tests/ui/runtime/native-print-no-runtime.rs9
-rw-r--r--tests/ui/runtime/out-of-stack.rs88
-rw-r--r--tests/ui/runtime/rt-explody-panic-payloads.rs33
-rw-r--r--tests/ui/runtime/running-with-no-runtime.rs52
-rw-r--r--tests/ui/runtime/signal-alternate-stack-cleanup.rs37
-rw-r--r--tests/ui/runtime/stdout-during-shutdown.rs19
-rw-r--r--tests/ui/runtime/stdout-during-shutdown.run.stdout1
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