From 2b3477d373603527d23cc578f3737857b7b253d7 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Sun, 23 Nov 2014 19:21:17 -0800 Subject: [PATCH] libs: merge librustrt into libstd This commit merges the `rustrt` crate into `std`, undoing part of the facade. This merger continues the paring down of the runtime system. Code relying on the public API of `rustrt` will break; some of this API is now available through `std::rt`, but is likely to change and/or be removed very soon. [breaking-change] --- mk/crates.mk | 7 +- src/libcollections/slice.rs | 12 +- src/librustrt/util.rs | 136 --- src/{librustrt => libstd}/c_str.rs | 30 +- src/libstd/comm/mod.rs | 11 +- src/libstd/comm/oneshot.rs | 4 +- src/libstd/comm/select.rs | 4 +- src/libstd/comm/shared.rs | 6 +- src/libstd/comm/stream.rs | 6 +- src/libstd/comm/sync.rs | 6 +- src/libstd/failure.rs | 6 +- src/libstd/io/stdio.rs | 7 +- src/libstd/lib.rs | 9 +- src/libstd/os.rs | 4 +- src/{librustrt => libstd/rt}/args.rs | 14 +- src/{librustrt => libstd/rt}/at_exit_imp.rs | 12 +- src/libstd/rt/backtrace.rs | 978 +----------------- src/{librustrt => libstd/rt}/bookkeeping.rs | 6 +- src/{librustrt => libstd/rt}/exclusive.rs | 10 +- src/{librustrt => libstd/rt}/libunwind.rs | 0 src/{librustrt => libstd/rt}/local.rs | 12 +- src/{librustrt => libstd/rt}/local_ptr.rs | 22 +- src/{librustrt => libstd/rt}/macros.rs | 7 +- src/libstd/rt/mod.rs | 88 +- src/{librustrt => libstd/rt}/mutex.rs | 337 +----- src/{librustrt => libstd/rt}/task.rs | 48 +- src/libstd/rt/thread.rs | 171 +++ .../rt}/thread_local_storage.rs | 6 +- src/{librustrt => libstd/rt}/unwind.rs | 39 +- src/libstd/rt/util.rs | 147 ++- src/libstd/rtdeps.rs | 2 +- src/libstd/sys/common/backtrace.rs | 131 +++ src/libstd/sys/common/helper_thread.rs | 5 +- src/libstd/sys/common/mod.rs | 3 + src/{librustrt => libstd/sys/common}/stack.rs | 4 +- src/libstd/sys/common/thread.rs | 34 + src/libstd/sys/common/thread_local.rs | 4 +- src/libstd/sys/unix/backtrace.rs | 493 +++++++++ src/libstd/sys/unix/mod.rs | 3 + .../sys/unix}/stack_overflow.rs | 166 +-- src/libstd/sys/unix/thread.rs | 270 +++++ src/libstd/sys/windows/backtrace.rs | 371 +++++++ src/libstd/sys/windows/mod.rs | 3 + src/libstd/sys/windows/stack_overflow.rs | 120 +++ src/libstd/sys/windows/thread.rs | 95 ++ src/libstd/sys/windows/thread_local.rs | 6 +- src/libstd/task.rs | 12 +- src/libstd/thread_local/mod.rs | 2 +- src/{librustrt => libstd}/thunk.rs | 0 src/rt/rust_try.ll | 2 +- src/test/run-pass/foreign-call-no-runtime.rs | 3 +- .../match-ref-binding-in-guard-3256.rs | 4 +- src/test/run-pass/running-with-no-runtime.rs | 4 +- src/test/run-pass/writealias.rs | 4 +- 54 files changed, 2111 insertions(+), 1775 deletions(-) delete mode 100644 src/librustrt/util.rs rename src/{librustrt => libstd}/c_str.rs (98%) rename src/{librustrt => libstd/rt}/args.rs (95%) rename src/{librustrt => libstd/rt}/at_exit_imp.rs (93%) rename src/{librustrt => libstd/rt}/bookkeeping.rs (95%) rename src/{librustrt => libstd/rt}/exclusive.rs (96%) rename src/{librustrt => libstd/rt}/libunwind.rs (100%) rename src/{librustrt => libstd/rt}/local.rs (96%) rename src/{librustrt => libstd/rt}/local_ptr.rs (97%) rename src/{librustrt => libstd/rt}/macros.rs (86%) rename src/{librustrt => libstd/rt}/mutex.rs (52%) rename src/{librustrt => libstd/rt}/task.rs (96%) create mode 100644 src/libstd/rt/thread.rs rename src/{librustrt => libstd/rt}/thread_local_storage.rs (96%) rename src/{librustrt => libstd/rt}/unwind.rs (98%) create mode 100644 src/libstd/sys/common/backtrace.rs rename src/{librustrt => libstd/sys/common}/stack.rs (99%) create mode 100644 src/libstd/sys/common/thread.rs create mode 100644 src/libstd/sys/unix/backtrace.rs rename src/{librustrt => libstd/sys/unix}/stack_overflow.rs (64%) create mode 100644 src/libstd/sys/unix/thread.rs create mode 100644 src/libstd/sys/windows/backtrace.rs create mode 100644 src/libstd/sys/windows/stack_overflow.rs create mode 100644 src/libstd/sys/windows/thread.rs rename src/{librustrt => libstd}/thunk.rs (100%) diff --git a/mk/crates.mk b/mk/crates.mk index a47d4871326df..fafe77c78da4e 100644 --- a/mk/crates.mk +++ b/mk/crates.mk @@ -51,7 +51,7 @@ TARGET_CRATES := libc std flate arena term \ serialize getopts collections test time rand \ - log regex graphviz core rbml alloc rustrt \ + log regex graphviz core rbml alloc \ unicode RUSTC_CRATES := rustc rustc_typeck rustc_borrowck rustc_driver rustc_trans rustc_back rustc_llvm HOST_CRATES := syntax $(RUSTC_CRATES) rustdoc regex_macros fmt_macros @@ -62,9 +62,8 @@ DEPS_core := DEPS_libc := core DEPS_unicode := core DEPS_alloc := core libc native:jemalloc -DEPS_rustrt := alloc core libc collections native:rustrt_native -DEPS_std := core libc rand alloc collections rustrt unicode \ - native:rust_builtin native:backtrace +DEPS_std := core libc rand alloc collections unicode \ + native:rust_builtin native:backtrace native:rustrt_native DEPS_graphviz := std DEPS_syntax := std term serialize log fmt_macros arena libc DEPS_rustc_driver := arena flate getopts graphviz libc rustc rustc_back rustc_borrowck \ diff --git a/src/libcollections/slice.rs b/src/libcollections/slice.rs index bba00a80f6854..c2ed28d81df72 100644 --- a/src/libcollections/slice.rs +++ b/src/libcollections/slice.rs @@ -1344,8 +1344,6 @@ pub mod raw { #[cfg(test)] mod tests { - extern crate rustrt; - use std::cell::Cell; use std::default::Default; use std::mem; @@ -1629,9 +1627,9 @@ mod tests { #[test] fn test_swap_remove_noncopyable() { // Tests that we don't accidentally run destructors twice. - let mut v = vec![rustrt::exclusive::Exclusive::new(()), - rustrt::exclusive::Exclusive::new(()), - rustrt::exclusive::Exclusive::new(())]; + let mut v = vec![rt::exclusive::Exclusive::new(()), + rt::exclusive::Exclusive::new(()), + rt::exclusive::Exclusive::new(())]; let mut _e = v.swap_remove(0); assert_eq!(v.len(), 2); _e = v.swap_remove(1); @@ -1736,7 +1734,7 @@ mod tests { v2.dedup(); /* * If the boxed pointers were leaked or otherwise misused, valgrind - * and/or rustrt should raise errors. + * and/or rt should raise errors. */ } @@ -1750,7 +1748,7 @@ mod tests { v2.dedup(); /* * If the pointers were leaked or otherwise misused, valgrind and/or - * rustrt should raise errors. + * rt should raise errors. */ } diff --git a/src/librustrt/util.rs b/src/librustrt/util.rs deleted file mode 100644 index 74f918b8ee3b4..0000000000000 --- a/src/librustrt/util.rs +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. -// -// ignore-lexer-test FIXME #15677 - -use core::prelude::*; - -use core::cmp; -use core::fmt; -use core::intrinsics; -use core::slice; -use core::str; -use libc; - -// Indicates whether we should perform expensive sanity checks, including rtassert! -// -// FIXME: Once the runtime matures remove the `true` below to turn off rtassert, -// etc. -pub const ENFORCE_SANITY: bool = true || !cfg!(rtopt) || cfg!(rtdebug) || - cfg!(rtassert); - -pub struct Stdio(libc::c_int); - -#[allow(non_upper_case_globals)] -impl Copy for Stdio {} - -#[allow(non_upper_case_globals)] -pub const Stdout: Stdio = Stdio(libc::STDOUT_FILENO); -#[allow(non_upper_case_globals)] -pub const Stderr: Stdio = Stdio(libc::STDERR_FILENO); - -impl fmt::FormatWriter for Stdio { - fn write(&mut self, data: &[u8]) -> fmt::Result { - #[cfg(unix)] - type WriteLen = libc::size_t; - #[cfg(windows)] - type WriteLen = libc::c_uint; - unsafe { - let Stdio(fd) = *self; - libc::write(fd, - data.as_ptr() as *const libc::c_void, - data.len() as WriteLen); - } - Ok(()) // yes, we're lying - } -} - -pub fn dumb_print(args: &fmt::Arguments) { - use core::fmt::FormatWriter; - let mut w = Stderr; - let _ = w.write_fmt(args); -} - -pub fn abort(args: &fmt::Arguments) -> ! { - use core::fmt::FormatWriter; - - struct BufWriter<'a> { - buf: &'a mut [u8], - pos: uint, - } - impl<'a> FormatWriter for BufWriter<'a> { - fn write(&mut self, bytes: &[u8]) -> fmt::Result { - let left = self.buf[mut self.pos..]; - let to_write = bytes[..cmp::min(bytes.len(), left.len())]; - slice::bytes::copy_memory(left, to_write); - self.pos += to_write.len(); - Ok(()) - } - } - - // Convert the arguments into a stack-allocated string - let mut msg = [0u8, ..512]; - let mut w = BufWriter { buf: &mut msg, pos: 0 }; - let _ = write!(&mut w, "{}", args); - let msg = str::from_utf8(w.buf[mut ..w.pos]).unwrap_or("aborted"); - let msg = if msg.is_empty() {"aborted"} else {msg}; - - // Give some context to the message - let hash = msg.bytes().fold(0, |accum, val| accum + (val as uint) ); - let quote = match hash % 10 { - 0 => " -It was from the artists and poets that the pertinent answers came, and I -know that panic would have broken loose had they been able to compare notes. -As it was, lacking their original letters, I half suspected the compiler of -having asked leading questions, or of having edited the correspondence in -corroboration of what he had latently resolved to see.", - 1 => " -There are not many persons who know what wonders are opened to them in the -stories and visions of their youth; for when as children we listen and dream, -we think but half-formed thoughts, and when as men we try to remember, we are -dulled and prosaic with the poison of life. But some of us awake in the night -with strange phantasms of enchanted hills and gardens, of fountains that sing -in the sun, of golden cliffs overhanging murmuring seas, of plains that stretch -down to sleeping cities of bronze and stone, and of shadowy companies of heroes -that ride caparisoned white horses along the edges of thick forests; and then -we know that we have looked back through the ivory gates into that world of -wonder which was ours before we were wise and unhappy.", - 2 => " -Instead of the poems I had hoped for, there came only a shuddering blackness -and ineffable loneliness; and I saw at last a fearful truth which no one had -ever dared to breathe before — the unwhisperable secret of secrets — The fact -that this city of stone and stridor is not a sentient perpetuation of Old New -York as London is of Old London and Paris of Old Paris, but that it is in fact -quite dead, its sprawling body imperfectly embalmed and infested with queer -animate things which have nothing to do with it as it was in life.", - 3 => " -The ocean ate the last of the land and poured into the smoking gulf, thereby -giving up all it had ever conquered. From the new-flooded lands it flowed -again, uncovering death and decay; and from its ancient and immemorial bed it -trickled loathsomely, uncovering nighted secrets of the years when Time was -young and the gods unborn. Above the waves rose weedy remembered spires. The -moon laid pale lilies of light on dead London, and Paris stood up from its damp -grave to be sanctified with star-dust. Then rose spires and monoliths that were -weedy but not remembered; terrible spires and monoliths of lands that men never -knew were lands...", - 4 => " -There was a night when winds from unknown spaces whirled us irresistibly into -limitless vacuum beyond all thought and entity. Perceptions of the most -maddeningly untransmissible sort thronged upon us; perceptions of infinity -which at the time convulsed us with joy, yet which are now partly lost to my -memory and partly incapable of presentation to others.", - _ => "You've met with a terrible fate, haven't you?" - }; - rterrln!("{}", ""); - rterrln!("{}", quote); - rterrln!("{}", ""); - rterrln!("fatal runtime error: {}", msg); - unsafe { intrinsics::abort(); } -} diff --git a/src/librustrt/c_str.rs b/src/libstd/c_str.rs similarity index 98% rename from src/librustrt/c_str.rs rename to src/libstd/c_str.rs index 6649b93e4b057..27a139835c980 100644 --- a/src/librustrt/c_str.rs +++ b/src/libstd/c_str.rs @@ -67,17 +67,17 @@ //! } //! ``` +use string::String; +use hash; +use fmt; +use kinds::marker; +use mem; use core::prelude::*; -use collections::string::String; -use core::hash; -use core::fmt; -use core::kinds::{Sized, marker}; -use core::mem; -use core::ptr; -use core::raw::Slice; -use core::slice; -use core::str; +use ptr; +use raw::Slice; +use slice; +use str; use libc; /// The representation of a C String. @@ -534,9 +534,9 @@ pub unsafe fn from_c_multistring(buf: *const libc::c_char, #[cfg(test)] mod tests { - use std::prelude::*; - use std::ptr; - use std::task; + use prelude::*; + use ptr; + use task; use libc; use super::*; @@ -726,9 +726,11 @@ mod tests { #[cfg(test)] mod bench { - use test::Bencher; + extern crate test; + + use self::test::Bencher; use libc; - use std::prelude::*; + use prelude::*; #[inline] fn check(s: &str, c_str: *const libc::c_char) { diff --git a/src/libstd/comm/mod.rs b/src/libstd/comm/mod.rs index 29a7b0dd0cc54..dfbb09d26b5b4 100644 --- a/src/libstd/comm/mod.rs +++ b/src/libstd/comm/mod.rs @@ -327,7 +327,7 @@ use alloc::arc::Arc; use core::kinds::marker; use core::mem; use core::cell::UnsafeCell; -use rustrt::task::BlockedTask; +use rt::task::BlockedTask; pub use comm::select::{Select, Handle}; @@ -336,9 +336,8 @@ macro_rules! test { mod $name { #![allow(unused_imports)] - extern crate rustrt; - use prelude::*; + use rt; use comm::*; use super::*; @@ -1519,7 +1518,7 @@ mod test { } } test! { fn sends_off_the_runtime() { - use rustrt::thread::Thread; + use rt::thread::Thread; let (tx, rx) = channel(); let t = Thread::start(move|| { @@ -1534,7 +1533,7 @@ mod test { } } test! { fn try_recvs_off_the_runtime() { - use rustrt::thread::Thread; + use rt::thread::Thread; let (tx, rx) = channel(); let (cdone, pdone) = channel(); @@ -1984,7 +1983,7 @@ mod sync_tests { } } test! { fn try_recvs_off_the_runtime() { - use rustrt::thread::Thread; + use rt::thread::Thread; let (tx, rx) = sync_channel::<()>(0); let (cdone, pdone) = channel(); diff --git a/src/libstd/comm/oneshot.rs b/src/libstd/comm/oneshot.rs index bc34c3e8c5295..2c5248c0897b2 100644 --- a/src/libstd/comm/oneshot.rs +++ b/src/libstd/comm/oneshot.rs @@ -41,8 +41,8 @@ use core::prelude::*; use alloc::boxed::Box; use core::mem; -use rustrt::local::Local; -use rustrt::task::{Task, BlockedTask}; +use rt::local::Local; +use rt::task::{Task, BlockedTask}; use sync::atomic; use comm::Receiver; diff --git a/src/libstd/comm/select.rs b/src/libstd/comm/select.rs index de2b84b083c23..4da9b4cfa369f 100644 --- a/src/libstd/comm/select.rs +++ b/src/libstd/comm/select.rs @@ -59,8 +59,8 @@ use core::cell::Cell; use core::kinds::marker; use core::mem; use core::uint; -use rustrt::local::Local; -use rustrt::task::{Task, BlockedTask}; +use rt::local::Local; +use rt::task::{Task, BlockedTask}; use comm::Receiver; diff --git a/src/libstd/comm/shared.rs b/src/libstd/comm/shared.rs index 13b5e10fcd3dc..b3856e588e233 100644 --- a/src/libstd/comm/shared.rs +++ b/src/libstd/comm/shared.rs @@ -25,9 +25,9 @@ use core::prelude::*; use alloc::boxed::Box; use core::cmp; use core::int; -use rustrt::local::Local; -use rustrt::task::{Task, BlockedTask}; -use rustrt::thread::Thread; +use rt::local::Local; +use rt::task::{Task, BlockedTask}; +use rt::thread::Thread; use sync::{atomic, Mutex, MutexGuard}; use comm::mpsc_queue as mpsc; diff --git a/src/libstd/comm/stream.rs b/src/libstd/comm/stream.rs index 06ab4f4427aa6..827b1d51ac48d 100644 --- a/src/libstd/comm/stream.rs +++ b/src/libstd/comm/stream.rs @@ -27,9 +27,9 @@ use core::prelude::*; use alloc::boxed::Box; use core::cmp; use core::int; -use rustrt::local::Local; -use rustrt::task::{Task, BlockedTask}; -use rustrt::thread::Thread; +use rt::local::Local; +use rt::task::{Task, BlockedTask}; +use rt::thread::Thread; use sync::atomic; use comm::spsc_queue as spsc; diff --git a/src/libstd/comm/sync.rs b/src/libstd/comm/sync.rs index a2e839e134c7d..933cd43c662aa 100644 --- a/src/libstd/comm/sync.rs +++ b/src/libstd/comm/sync.rs @@ -42,9 +42,9 @@ use alloc::boxed::Box; use vec::Vec; use core::mem; use core::cell::UnsafeCell; -use rustrt::local::Local; -use rustrt::mutex::{NativeMutex, LockGuard}; -use rustrt::task::{Task, BlockedTask}; +use rt::local::Local; +use rt::mutex::{NativeMutex, LockGuard}; +use rt::task::{Task, BlockedTask}; use sync::atomic; diff --git a/src/libstd/failure.rs b/src/libstd/failure.rs index 89bccb8b99fe8..5438f1920d6f9 100644 --- a/src/libstd/failure.rs +++ b/src/libstd/failure.rs @@ -20,9 +20,9 @@ use option::Option; use option::Option::{Some, None}; use result::Result::Ok; use rt::backtrace; -use rustrt::{Stderr, Stdio}; -use rustrt::local::Local; -use rustrt::task::Task; +use rt::util::{Stderr, Stdio}; +use rt::local::Local; +use rt::task::Task; use str::Str; use string::String; diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs index 73be389bb914e..7b5cbf7d58f55 100644 --- a/src/libstd/io/stdio.rs +++ b/src/libstd/io/stdio.rs @@ -42,9 +42,8 @@ use option::Option::{Some, None}; use ops::{Deref, DerefMut, FnOnce}; use result::Result::{Ok, Err}; use rt; -use rustrt; -use rustrt::local::Local; -use rustrt::task::Task; +use rt::local::Local; +use rt::task::Task; use slice::SliceExt; use str::StrPrelude; use string::String; @@ -345,7 +344,7 @@ fn with_task_stdout(f: F) where }); result } else { - let mut io = rustrt::Stdout; + let mut io = rt::util::Stdout; f(&mut io as &mut Writer) }; match result { diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index e99aba9b67380..d7f331b6c230e 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -104,7 +104,7 @@ html_playground_url = "http://play.rust-lang.org/")] #![allow(unknown_features)] -#![feature(macro_rules, globs, linkage)] +#![feature(macro_rules, globs, linkage, thread_local, asm)] #![feature(default_type_params, phase, lang_items, unsafe_destructor)] #![feature(import_shadowing, slicing_syntax)] #![feature(unboxed_closures)] @@ -124,7 +124,6 @@ extern crate core; extern crate "collections" as core_collections; extern crate "rand" as core_rand; extern crate libc; -extern crate rustrt; // Make std testable by not duplicating lang items. See #2912 #[cfg(test)] extern crate "std" as realstd; @@ -167,12 +166,8 @@ pub use core_collections::str; pub use core_collections::string; pub use core_collections::vec; -pub use rustrt::c_str; - pub use unicode::char; -pub use rustrt::thunk; - /* Exported macros */ pub mod macros; @@ -207,6 +202,7 @@ pub mod prelude; #[path = "num/f64.rs"] pub mod f64; pub mod ascii; +pub mod thunk; /* Common traits */ @@ -216,6 +212,7 @@ pub mod num; /* Runtime and platform support */ pub mod thread_local; +pub mod c_str; pub mod c_vec; pub mod dynamic_lib; pub mod fmt; diff --git a/src/libstd/os.rs b/src/libstd/os.rs index 860f9d2670a07..6e02c03602f1c 100644 --- a/src/libstd/os.rs +++ b/src/libstd/os.rs @@ -1042,9 +1042,9 @@ fn real_args_as_bytes() -> Vec> { target_os = "freebsd", target_os = "dragonfly"))] fn real_args_as_bytes() -> Vec> { - use rustrt; + use rt; - match rustrt::args::clone() { + match rt::args::clone() { Some(args) => args, None => panic!("process arguments not initialized") } diff --git a/src/librustrt/args.rs b/src/libstd/rt/args.rs similarity index 95% rename from src/librustrt/args.rs rename to src/libstd/rt/args.rs index c1b48e989a1c1..8b9dbf73c53b9 100644 --- a/src/librustrt/args.rs +++ b/src/libstd/rt/args.rs @@ -20,7 +20,7 @@ //! FIXME #7756: Would be nice for this to not exist. use core::prelude::*; -use collections::vec::Vec; +use vec::Vec; /// One-time global initialization. pub unsafe fn init(argc: int, argv: *const *const u8) { imp::init(argc, argv) } @@ -46,12 +46,12 @@ pub fn clone() -> Option>> { imp::clone() } mod imp { use core::prelude::*; - use alloc::boxed::Box; - use collections::vec::Vec; - use collections::string::String; - use core::mem; + use boxed::Box; + use vec::Vec; + use string::String; + use mem; - use mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT}; + use rt::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT}; static mut GLOBAL_ARGS_PTR: uint = 0; static LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT; @@ -145,7 +145,7 @@ mod imp { target_os = "windows"))] mod imp { use core::prelude::*; - use collections::vec::Vec; + use vec::Vec; pub unsafe fn init(_argc: int, _argv: *const *const u8) { } diff --git a/src/librustrt/at_exit_imp.rs b/src/libstd/rt/at_exit_imp.rs similarity index 93% rename from src/librustrt/at_exit_imp.rs rename to src/libstd/rt/at_exit_imp.rs index 8be77d9b34ddd..086079c312ad4 100644 --- a/src/librustrt/at_exit_imp.rs +++ b/src/libstd/rt/at_exit_imp.rs @@ -14,13 +14,13 @@ use core::prelude::*; -use alloc::boxed::Box; -use collections::vec::Vec; -use core::atomic; -use core::mem; -use thunk::{Thunk}; +use boxed::Box; +use vec::Vec; +use sync::atomic; +use mem; +use thunk::Thunk; -use exclusive::Exclusive; +use rt::exclusive::Exclusive; type Queue = Exclusive>; diff --git a/src/libstd/rt/backtrace.rs b/src/libstd/rt/backtrace.rs index d815a5ea4f702..40885823a0596 100644 --- a/src/libstd/rt/backtrace.rs +++ b/src/libstd/rt/backtrace.rs @@ -19,9 +19,8 @@ use os; use result::Result::{Ok, Err}; use str::{StrPrelude, from_str}; use sync::atomic; -use unicode::char::UnicodeChar; -pub use self::imp::write; +pub use sys::backtrace::write; // For now logging is turned off by default, and this function checks to see // whether the magical environment variable is present to see if it's turned on. @@ -41,984 +40,13 @@ pub fn log_enabled() -> bool { val == 2 } -#[cfg(target_word_size = "64")] const HEX_WIDTH: uint = 18; -#[cfg(target_word_size = "32")] const HEX_WIDTH: uint = 10; - -// All rust symbols are in theory lists of "::"-separated identifiers. Some -// assemblers, however, can't handle these characters in symbol names. To get -// around this, we use C++-style mangling. The mangling method is: -// -// 1. Prefix the symbol with "_ZN" -// 2. For each element of the path, emit the length plus the element -// 3. End the path with "E" -// -// For example, "_ZN4testE" => "test" and "_ZN3foo3bar" => "foo::bar". -// -// We're the ones printing our backtraces, so we can't rely on anything else to -// demangle our symbols. It's *much* nicer to look at demangled symbols, so -// this function is implemented to give us nice pretty output. -// -// Note that this demangler isn't quite as fancy as it could be. We have lots -// of other information in our symbols like hashes, version, type information, -// etc. Additionally, this doesn't handle glue symbols at all. -fn demangle(writer: &mut Writer, s: &str) -> IoResult<()> { - // First validate the symbol. If it doesn't look like anything we're - // expecting, we just print it literally. Note that we must handle non-rust - // symbols because we could have any function in the backtrace. - let mut valid = true; - let mut inner = s; - if s.len() > 4 && s.starts_with("_ZN") && s.ends_with("E") { - inner = s.slice(3, s.len() - 1); - // On Windows, dbghelp strips leading underscores, so we accept "ZN...E" form too. - } else if s.len() > 3 && s.starts_with("ZN") && s.ends_with("E") { - inner = s.slice(2, s.len() - 1); - } else { - valid = false; - } - - if valid { - let mut chars = inner.chars(); - while valid { - let mut i = 0; - for c in chars { - if c.is_numeric() { - i = i * 10 + c as uint - '0' as uint; - } else { - break - } - } - if i == 0 { - valid = chars.next().is_none(); - break - } else if chars.by_ref().take(i - 1).count() != i - 1 { - valid = false; - } - } - } - - // Alright, let's do this. - if !valid { - try!(writer.write_str(s)); - } else { - let mut first = true; - while inner.len() > 0 { - if !first { - try!(writer.write_str("::")); - } else { - first = false; - } - let mut rest = inner; - while rest.char_at(0).is_numeric() { - rest = rest.slice_from(1); - } - let i: uint = from_str(inner.slice_to(inner.len() - rest.len())).unwrap(); - inner = rest.slice_from(i); - rest = rest.slice_to(i); - while rest.len() > 0 { - if rest.starts_with("$") { - macro_rules! demangle { - ($($pat:expr => $demangled:expr),*) => ({ - $(if rest.starts_with($pat) { - try!(writer.write_str($demangled)); - rest = rest.slice_from($pat.len()); - } else)* - { - try!(writer.write_str(rest)); - break; - } - - }) - } - - // see src/librustc/back/link.rs for these mappings - demangle! ( - "$SP$" => "@", - "$UP$" => "Box", - "$RP$" => "*", - "$BP$" => "&", - "$LT$" => "<", - "$GT$" => ">", - "$LP$" => "(", - "$RP$" => ")", - "$C$" => ",", - - // in theory we can demangle any Unicode code point, but - // for simplicity we just catch the common ones. - "$x20" => " ", - "$x27" => "'", - "$x5b" => "[", - "$x5d" => "]" - ) - } else { - let idx = match rest.find('$') { - None => rest.len(), - Some(i) => i, - }; - try!(writer.write_str(rest.slice_to(idx))); - rest = rest.slice_from(idx); - } - } - } - } - - Ok(()) -} - -/// Backtrace support built on libgcc with some extra OS-specific support -/// -/// Some methods of getting a backtrace: -/// -/// * The backtrace() functions on unix. It turns out this doesn't work very -/// well for green threads on OSX, and the address to symbol portion of it -/// suffers problems that are described below. -/// -/// * Using libunwind. This is more difficult than it sounds because libunwind -/// isn't installed everywhere by default. It's also a bit of a hefty library, -/// so possibly not the best option. When testing, libunwind was excellent at -/// getting both accurate backtraces and accurate symbols across platforms. -/// This route was not chosen in favor of the next option, however. -/// -/// * We're already using libgcc_s for exceptions in rust (triggering task -/// unwinding and running destructors on the stack), and it turns out that it -/// conveniently comes with a function that also gives us a backtrace. All of -/// these functions look like _Unwind_*, but it's not quite the full -/// repertoire of the libunwind API. Due to it already being in use, this was -/// the chosen route of getting a backtrace. -/// -/// After choosing libgcc_s for backtraces, the sad part is that it will only -/// give us a stack trace of instruction pointers. Thankfully these instruction -/// pointers are accurate (they work for green and native threads), but it's -/// then up to us again to figure out how to translate these addresses to -/// symbols. As with before, we have a few options. Before, that, a little bit -/// of an interlude about symbols. This is my very limited knowledge about -/// symbol tables, and this information is likely slightly wrong, but the -/// general idea should be correct. -/// -/// When talking about symbols, it's helpful to know a few things about where -/// symbols are located. Some symbols are located in the dynamic symbol table -/// of the executable which in theory means that they're available for dynamic -/// linking and lookup. Other symbols end up only in the local symbol table of -/// the file. This loosely corresponds to pub and priv functions in Rust. -/// -/// Armed with this knowledge, we know that our solution for address to symbol -/// translation will need to consult both the local and dynamic symbol tables. -/// With that in mind, here's our options of translating an address to -/// a symbol. -/// -/// * Use dladdr(). The original backtrace()-based idea actually uses dladdr() -/// behind the scenes to translate, and this is why backtrace() was not used. -/// Conveniently, this method works fantastically on OSX. It appears dladdr() -/// uses magic to consult the local symbol table, or we're putting everything -/// in the dynamic symbol table anyway. Regardless, for OSX, this is the -/// method used for translation. It's provided by the system and easy to do.o -/// -/// Sadly, all other systems have a dladdr() implementation that does not -/// consult the local symbol table. This means that most functions are blank -/// because they don't have symbols. This means that we need another solution. -/// -/// * Use unw_get_proc_name(). This is part of the libunwind api (not the -/// libgcc_s version of the libunwind api), but involves taking a dependency -/// to libunwind. We may pursue this route in the future if we bundle -/// libunwind, but libunwind was unwieldy enough that it was not chosen at -/// this time to provide this functionality. -/// -/// * Shell out to a utility like `readelf`. Crazy though it may sound, it's a -/// semi-reasonable solution. The stdlib already knows how to spawn processes, -/// so in theory it could invoke readelf, parse the output, and consult the -/// local/dynamic symbol tables from there. This ended up not getting chosen -/// due to the craziness of the idea plus the advent of the next option. -/// -/// * Use `libbacktrace`. It turns out that this is a small library bundled in -/// the gcc repository which provides backtrace and symbol translation -/// functionality. All we really need from it is the backtrace functionality, -/// and we only really need this on everything that's not OSX, so this is the -/// chosen route for now. -/// -/// In summary, the current situation uses libgcc_s to get a trace of stack -/// pointers, and we use dladdr() or libbacktrace to translate these addresses -/// to symbols. This is a bit of a hokey implementation as-is, but it works for -/// all unix platforms we support right now, so it at least gets the job done. -#[cfg(unix)] -mod imp { - use prelude::*; - - use c_str::CString; - use io::IoResult; - use libc; - use mem; - use sync::{StaticMutex, MUTEX_INIT}; - - /// As always - iOS on arm uses SjLj exceptions and - /// _Unwind_Backtrace is even not available there. Still, - /// backtraces could be extracted using a backtrace function, - /// which thanks god is public - /// - /// As mentioned in a huge comment block above, backtrace doesn't - /// play well with green threads, so while it is extremely nice - /// and simple to use it should be used only on iOS devices as the - /// only viable option. - #[cfg(all(target_os = "ios", target_arch = "arm"))] - #[inline(never)] - pub fn write(w: &mut Writer) -> IoResult<()> { - use result; - - extern { - fn backtrace(buf: *mut *mut libc::c_void, - sz: libc::c_int) -> libc::c_int; - } - - // while it doesn't requires lock for work as everything is - // local, it still displays much nicer backtraces when a - // couple of tasks panic simultaneously - static LOCK: StaticMutex = MUTEX_INIT; - let _g = LOCK.lock(); - - try!(writeln!(w, "stack backtrace:")); - // 100 lines should be enough - const SIZE: uint = 100; - let mut buf: [*mut libc::c_void, ..SIZE] = unsafe {mem::zeroed()}; - let cnt = unsafe { backtrace(buf.as_mut_ptr(), SIZE as libc::c_int) as uint}; - - // skipping the first one as it is write itself - let iter = range(1, cnt).map(|i| { - print(w, i as int, buf[i]) - }); - result::fold(iter, (), |_, _| ()) - } - - #[cfg(not(all(target_os = "ios", target_arch = "arm")))] - #[inline(never)] // if we know this is a function call, we can skip it when - // tracing - pub fn write(w: &mut Writer) -> IoResult<()> { - use io::IoError; - - struct Context<'a> { - idx: int, - writer: &'a mut (Writer+'a), - last_error: Option, - } - - // When using libbacktrace, we use some necessary global state, so we - // need to prevent more than one thread from entering this block. This - // is semi-reasonable in terms of printing anyway, and we know that all - // I/O done here is blocking I/O, not green I/O, so we don't have to - // worry about this being a native vs green mutex. - static LOCK: StaticMutex = MUTEX_INIT; - let _g = LOCK.lock(); - - try!(writeln!(w, "stack backtrace:")); - - let mut cx = Context { writer: w, last_error: None, idx: 0 }; - return match unsafe { - uw::_Unwind_Backtrace(trace_fn, - &mut cx as *mut Context as *mut libc::c_void) - } { - uw::_URC_NO_REASON => { - match cx.last_error { - Some(err) => Err(err), - None => Ok(()) - } - } - _ => Ok(()), - }; - - extern fn trace_fn(ctx: *mut uw::_Unwind_Context, - arg: *mut libc::c_void) -> uw::_Unwind_Reason_Code { - let cx: &mut Context = unsafe { mem::transmute(arg) }; - let ip = unsafe { uw::_Unwind_GetIP(ctx) as *mut libc::c_void }; - // dladdr() on osx gets whiny when we use FindEnclosingFunction, and - // it appears to work fine without it, so we only use - // FindEnclosingFunction on non-osx platforms. In doing so, we get a - // slightly more accurate stack trace in the process. - // - // This is often because panic involves the last instruction of a - // function being "call std::rt::begin_unwind", with no ret - // instructions after it. This means that the return instruction - // pointer points *outside* of the calling function, and by - // unwinding it we go back to the original function. - let ip = if cfg!(target_os = "macos") || cfg!(target_os = "ios") { - ip - } else { - unsafe { uw::_Unwind_FindEnclosingFunction(ip) } - }; - - // Don't print out the first few frames (they're not user frames) - cx.idx += 1; - if cx.idx <= 0 { return uw::_URC_NO_REASON } - // Don't print ginormous backtraces - if cx.idx > 100 { - match write!(cx.writer, " ... \n") { - Ok(()) => {} - Err(e) => { cx.last_error = Some(e); } - } - return uw::_URC_FAILURE - } - - // Once we hit an error, stop trying to print more frames - if cx.last_error.is_some() { return uw::_URC_FAILURE } - - match print(cx.writer, cx.idx, ip) { - Ok(()) => {} - Err(e) => { cx.last_error = Some(e); } - } - - // keep going - return uw::_URC_NO_REASON - } - } - - #[cfg(any(target_os = "macos", target_os = "ios"))] - fn print(w: &mut Writer, idx: int, addr: *mut libc::c_void) -> IoResult<()> { - use intrinsics; - #[repr(C)] - struct Dl_info { - dli_fname: *const libc::c_char, - dli_fbase: *mut libc::c_void, - dli_sname: *const libc::c_char, - dli_saddr: *mut libc::c_void, - } - extern { - fn dladdr(addr: *const libc::c_void, - info: *mut Dl_info) -> libc::c_int; - } - - let mut info: Dl_info = unsafe { intrinsics::init() }; - if unsafe { dladdr(addr as *const libc::c_void, &mut info) == 0 } { - output(w, idx,addr, None) - } else { - output(w, idx, addr, Some(unsafe { - CString::new(info.dli_sname, false) - })) - } - } - - #[cfg(not(any(target_os = "macos", target_os = "ios")))] - fn print(w: &mut Writer, idx: int, addr: *mut libc::c_void) -> IoResult<()> { - use os; - use ptr; - - //////////////////////////////////////////////////////////////////////// - // libbacktrace.h API - //////////////////////////////////////////////////////////////////////// - type backtrace_syminfo_callback = - extern "C" fn(data: *mut libc::c_void, - pc: libc::uintptr_t, - symname: *const libc::c_char, - symval: libc::uintptr_t, - symsize: libc::uintptr_t); - type backtrace_error_callback = - extern "C" fn(data: *mut libc::c_void, - msg: *const libc::c_char, - errnum: libc::c_int); - enum backtrace_state {} - #[link(name = "backtrace", kind = "static")] - #[cfg(not(test))] - extern {} - - extern { - fn backtrace_create_state(filename: *const libc::c_char, - threaded: libc::c_int, - error: backtrace_error_callback, - data: *mut libc::c_void) - -> *mut backtrace_state; - fn backtrace_syminfo(state: *mut backtrace_state, - addr: libc::uintptr_t, - cb: backtrace_syminfo_callback, - error: backtrace_error_callback, - data: *mut libc::c_void) -> libc::c_int; - } - - //////////////////////////////////////////////////////////////////////// - // helper callbacks - //////////////////////////////////////////////////////////////////////// - - extern fn error_cb(_data: *mut libc::c_void, _msg: *const libc::c_char, - _errnum: libc::c_int) { - // do nothing for now - } - extern fn syminfo_cb(data: *mut libc::c_void, - _pc: libc::uintptr_t, - symname: *const libc::c_char, - _symval: libc::uintptr_t, - _symsize: libc::uintptr_t) { - let slot = data as *mut *const libc::c_char; - unsafe { *slot = symname; } - } - - // The libbacktrace API supports creating a state, but it does not - // support destroying a state. I personally take this to mean that a - // state is meant to be created and then live forever. - // - // I would love to register an at_exit() handler which cleans up this - // state, but libbacktrace provides no way to do so. - // - // With these constraints, this function has a statically cached state - // that is calculated the first time this is requested. Remember that - // backtracing all happens serially (one global lock). - // - // An additionally oddity in this function is that we initialize the - // filename via self_exe_name() to pass to libbacktrace. It turns out - // that on Linux libbacktrace seamlessly gets the filename of the - // current executable, but this fails on freebsd. by always providing - // it, we make sure that libbacktrace never has a reason to not look up - // the symbols. The libbacktrace API also states that the filename must - // be in "permanent memory", so we copy it to a static and then use the - // static as the pointer. - // - // FIXME: We also call self_exe_name() on DragonFly BSD. I haven't - // tested if this is required or not. - unsafe fn init_state() -> *mut backtrace_state { - static mut STATE: *mut backtrace_state = 0 as *mut backtrace_state; - static mut LAST_FILENAME: [libc::c_char, ..256] = [0, ..256]; - if !STATE.is_null() { return STATE } - let selfname = if cfg!(target_os = "freebsd") || - cfg!(target_os = "dragonfly") { - os::self_exe_name() - } else { - None - }; - let filename = match selfname { - Some(path) => { - let bytes = path.as_vec(); - if bytes.len() < LAST_FILENAME.len() { - let i = bytes.iter(); - for (slot, val) in LAST_FILENAME.iter_mut().zip(i) { - *slot = *val as libc::c_char; - } - LAST_FILENAME.as_ptr() - } else { - ptr::null() - } - } - None => ptr::null(), - }; - STATE = backtrace_create_state(filename, 0, error_cb, - ptr::null_mut()); - return STATE - } - - //////////////////////////////////////////////////////////////////////// - // translation - //////////////////////////////////////////////////////////////////////// - - // backtrace errors are currently swept under the rug, only I/O - // errors are reported - let state = unsafe { init_state() }; - if state.is_null() { - return output(w, idx, addr, None) - } - let mut data = 0 as *const libc::c_char; - let data_addr = &mut data as *mut *const libc::c_char; - let ret = unsafe { - backtrace_syminfo(state, addr as libc::uintptr_t, - syminfo_cb, error_cb, - data_addr as *mut libc::c_void) - }; - if ret == 0 || data.is_null() { - output(w, idx, addr, None) - } else { - output(w, idx, addr, Some(unsafe { CString::new(data, false) })) - } - } - - // Finally, after all that work above, we can emit a symbol. - fn output(w: &mut Writer, idx: int, addr: *mut libc::c_void, - s: Option) -> IoResult<()> { - try!(write!(w, " {:2}: {:2$} - ", idx, addr, super::HEX_WIDTH)); - match s.as_ref().and_then(|c| c.as_str()) { - Some(string) => try!(super::demangle(w, string)), - None => try!(write!(w, "")), - } - w.write(&['\n' as u8]) - } - - /// Unwind library interface used for backtraces - /// - /// Note that the native libraries come from librustrt, not this - /// module. - /// Note that dead code is allowed as here are just bindings - /// iOS doesn't use all of them it but adding more - /// platform-specific configs pollutes the code too much - #[allow(non_camel_case_types)] - #[allow(non_snake_case)] - #[allow(dead_code)] - mod uw { - pub use self::_Unwind_Reason_Code::*; - - use libc; - - #[repr(C)] - pub enum _Unwind_Reason_Code { - _URC_NO_REASON = 0, - _URC_FOREIGN_EXCEPTION_CAUGHT = 1, - _URC_FATAL_PHASE2_ERROR = 2, - _URC_FATAL_PHASE1_ERROR = 3, - _URC_NORMAL_STOP = 4, - _URC_END_OF_STACK = 5, - _URC_HANDLER_FOUND = 6, - _URC_INSTALL_CONTEXT = 7, - _URC_CONTINUE_UNWIND = 8, - _URC_FAILURE = 9, // used only by ARM EABI - } - - pub enum _Unwind_Context {} - - pub type _Unwind_Trace_Fn = - extern fn(ctx: *mut _Unwind_Context, - arg: *mut libc::c_void) -> _Unwind_Reason_Code; - - extern { - // No native _Unwind_Backtrace on iOS - #[cfg(not(all(target_os = "ios", target_arch = "arm")))] - pub fn _Unwind_Backtrace(trace: _Unwind_Trace_Fn, - trace_argument: *mut libc::c_void) - -> _Unwind_Reason_Code; - - #[cfg(all(not(target_os = "android"), - not(all(target_os = "linux", target_arch = "arm"))))] - pub fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t; - - #[cfg(all(not(target_os = "android"), - not(all(target_os = "linux", target_arch = "arm"))))] - pub fn _Unwind_FindEnclosingFunction(pc: *mut libc::c_void) - -> *mut libc::c_void; - } - - // On android, the function _Unwind_GetIP is a macro, and this is the - // expansion of the macro. This is all copy/pasted directly from the - // header file with the definition of _Unwind_GetIP. - #[cfg(any(target_os = "android", - all(target_os = "linux", target_arch = "arm")))] - pub unsafe fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t { - #[repr(C)] - enum _Unwind_VRS_Result { - _UVRSR_OK = 0, - _UVRSR_NOT_IMPLEMENTED = 1, - _UVRSR_FAILED = 2, - } - #[repr(C)] - enum _Unwind_VRS_RegClass { - _UVRSC_CORE = 0, - _UVRSC_VFP = 1, - _UVRSC_FPA = 2, - _UVRSC_WMMXD = 3, - _UVRSC_WMMXC = 4, - } - #[repr(C)] - enum _Unwind_VRS_DataRepresentation { - _UVRSD_UINT32 = 0, - _UVRSD_VFPX = 1, - _UVRSD_FPAX = 2, - _UVRSD_UINT64 = 3, - _UVRSD_FLOAT = 4, - _UVRSD_DOUBLE = 5, - } - - type _Unwind_Word = libc::c_uint; - extern { - fn _Unwind_VRS_Get(ctx: *mut _Unwind_Context, - klass: _Unwind_VRS_RegClass, - word: _Unwind_Word, - repr: _Unwind_VRS_DataRepresentation, - data: *mut libc::c_void) - -> _Unwind_VRS_Result; - } - - let mut val: _Unwind_Word = 0; - let ptr = &mut val as *mut _Unwind_Word; - let _ = _Unwind_VRS_Get(ctx, _Unwind_VRS_RegClass::_UVRSC_CORE, 15, - _Unwind_VRS_DataRepresentation::_UVRSD_UINT32, - ptr as *mut libc::c_void); - (val & !1) as libc::uintptr_t - } - - // This function also doesn't exist on Android or ARM/Linux, so make it - // a no-op - #[cfg(any(target_os = "android", - all(target_os = "linux", target_arch = "arm")))] - pub unsafe fn _Unwind_FindEnclosingFunction(pc: *mut libc::c_void) - -> *mut libc::c_void - { - pc - } - } -} - -/// As always, windows has something very different than unix, we mainly want -/// to avoid having to depend too much on libunwind for windows. -/// -/// If you google around, you'll find a fair bit of references to built-in -/// functions to get backtraces on windows. It turns out that most of these are -/// in an external library called dbghelp. I was unable to find this library -/// via `-ldbghelp`, but it is apparently normal to do the `dlopen` equivalent -/// of it. -/// -/// You'll also find that there's a function called CaptureStackBackTrace -/// mentioned frequently (which is also easy to use), but sadly I didn't have a -/// copy of that function in my mingw install (maybe it was broken?). Instead, -/// this takes the route of using StackWalk64 in order to walk the stack. -#[cfg(windows)] -#[allow(dead_code, non_snake_case)] -mod imp { - use c_str::CString; - use intrinsics; - use io::{IoResult, Writer}; - use libc; - use mem; - use ops::Drop; - use option::Option::{Some, None}; - use path::Path; - use result::Result::{Ok, Err}; - use sync::{StaticMutex, MUTEX_INIT}; - use slice::SliceExt; - use str::StrPrelude; - use dynamic_lib::DynamicLibrary; - - #[allow(non_snake_case)] - extern "system" { - fn GetCurrentProcess() -> libc::HANDLE; - fn GetCurrentThread() -> libc::HANDLE; - fn RtlCaptureContext(ctx: *mut arch::CONTEXT); - } - - type SymFromAddrFn = - extern "system" fn(libc::HANDLE, u64, *mut u64, - *mut SYMBOL_INFO) -> libc::BOOL; - type SymInitializeFn = - extern "system" fn(libc::HANDLE, *mut libc::c_void, - libc::BOOL) -> libc::BOOL; - type SymCleanupFn = - extern "system" fn(libc::HANDLE) -> libc::BOOL; - - type StackWalk64Fn = - extern "system" fn(libc::DWORD, libc::HANDLE, libc::HANDLE, - *mut STACKFRAME64, *mut arch::CONTEXT, - *mut libc::c_void, *mut libc::c_void, - *mut libc::c_void, *mut libc::c_void) -> libc::BOOL; - - const MAX_SYM_NAME: uint = 2000; - const IMAGE_FILE_MACHINE_I386: libc::DWORD = 0x014c; - const IMAGE_FILE_MACHINE_IA64: libc::DWORD = 0x0200; - const IMAGE_FILE_MACHINE_AMD64: libc::DWORD = 0x8664; - - #[repr(C)] - struct SYMBOL_INFO { - SizeOfStruct: libc::c_ulong, - TypeIndex: libc::c_ulong, - Reserved: [u64, ..2], - Index: libc::c_ulong, - Size: libc::c_ulong, - ModBase: u64, - Flags: libc::c_ulong, - Value: u64, - Address: u64, - Register: libc::c_ulong, - Scope: libc::c_ulong, - Tag: libc::c_ulong, - NameLen: libc::c_ulong, - MaxNameLen: libc::c_ulong, - // note that windows has this as 1, but it basically just means that - // the name is inline at the end of the struct. For us, we just bump - // the struct size up to MAX_SYM_NAME. - Name: [libc::c_char, ..MAX_SYM_NAME], - } - - - #[repr(C)] - enum ADDRESS_MODE { - AddrMode1616, - AddrMode1632, - AddrModeReal, - AddrModeFlat, - } - - struct ADDRESS64 { - Offset: u64, - Segment: u16, - Mode: ADDRESS_MODE, - } - - struct STACKFRAME64 { - AddrPC: ADDRESS64, - AddrReturn: ADDRESS64, - AddrFrame: ADDRESS64, - AddrStack: ADDRESS64, - AddrBStore: ADDRESS64, - FuncTableEntry: *mut libc::c_void, - Params: [u64, ..4], - Far: libc::BOOL, - Virtual: libc::BOOL, - Reserved: [u64, ..3], - KdHelp: KDHELP64, - } - - struct KDHELP64 { - Thread: u64, - ThCallbackStack: libc::DWORD, - ThCallbackBStore: libc::DWORD, - NextCallback: libc::DWORD, - FramePointer: libc::DWORD, - KiCallUserMode: u64, - KeUserCallbackDispatcher: u64, - SystemRangeStart: u64, - KiUserExceptionDispatcher: u64, - StackBase: u64, - StackLimit: u64, - Reserved: [u64, ..5], - } - - #[cfg(target_arch = "x86")] - mod arch { - use libc; - - const MAXIMUM_SUPPORTED_EXTENSION: uint = 512; - - #[repr(C)] - pub struct CONTEXT { - ContextFlags: libc::DWORD, - Dr0: libc::DWORD, - Dr1: libc::DWORD, - Dr2: libc::DWORD, - Dr3: libc::DWORD, - Dr6: libc::DWORD, - Dr7: libc::DWORD, - FloatSave: FLOATING_SAVE_AREA, - SegGs: libc::DWORD, - SegFs: libc::DWORD, - SegEs: libc::DWORD, - SegDs: libc::DWORD, - Edi: libc::DWORD, - Esi: libc::DWORD, - Ebx: libc::DWORD, - Edx: libc::DWORD, - Ecx: libc::DWORD, - Eax: libc::DWORD, - Ebp: libc::DWORD, - Eip: libc::DWORD, - SegCs: libc::DWORD, - EFlags: libc::DWORD, - Esp: libc::DWORD, - SegSs: libc::DWORD, - ExtendedRegisters: [u8, ..MAXIMUM_SUPPORTED_EXTENSION], - } - - #[repr(C)] - pub struct FLOATING_SAVE_AREA { - ControlWord: libc::DWORD, - StatusWord: libc::DWORD, - TagWord: libc::DWORD, - ErrorOffset: libc::DWORD, - ErrorSelector: libc::DWORD, - DataOffset: libc::DWORD, - DataSelector: libc::DWORD, - RegisterArea: [u8, ..80], - Cr0NpxState: libc::DWORD, - } - - pub fn init_frame(frame: &mut super::STACKFRAME64, - ctx: &CONTEXT) -> libc::DWORD { - frame.AddrPC.Offset = ctx.Eip as u64; - frame.AddrPC.Mode = super::ADDRESS_MODE::AddrModeFlat; - frame.AddrStack.Offset = ctx.Esp as u64; - frame.AddrStack.Mode = super::ADDRESS_MODE::AddrModeFlat; - frame.AddrFrame.Offset = ctx.Ebp as u64; - frame.AddrFrame.Mode = super::ADDRESS_MODE::AddrModeFlat; - super::IMAGE_FILE_MACHINE_I386 - } - } - - #[cfg(target_arch = "x86_64")] - mod arch { - use libc::{c_longlong, c_ulonglong}; - use libc::types::os::arch::extra::{WORD, DWORD, DWORDLONG}; - use simd; - - #[repr(C)] - pub struct CONTEXT { - _align_hack: [simd::u64x2, ..0], // FIXME align on 16-byte - P1Home: DWORDLONG, - P2Home: DWORDLONG, - P3Home: DWORDLONG, - P4Home: DWORDLONG, - P5Home: DWORDLONG, - P6Home: DWORDLONG, - - ContextFlags: DWORD, - MxCsr: DWORD, - - SegCs: WORD, - SegDs: WORD, - SegEs: WORD, - SegFs: WORD, - SegGs: WORD, - SegSs: WORD, - EFlags: DWORD, - - Dr0: DWORDLONG, - Dr1: DWORDLONG, - Dr2: DWORDLONG, - Dr3: DWORDLONG, - Dr6: DWORDLONG, - Dr7: DWORDLONG, - - Rax: DWORDLONG, - Rcx: DWORDLONG, - Rdx: DWORDLONG, - Rbx: DWORDLONG, - Rsp: DWORDLONG, - Rbp: DWORDLONG, - Rsi: DWORDLONG, - Rdi: DWORDLONG, - R8: DWORDLONG, - R9: DWORDLONG, - R10: DWORDLONG, - R11: DWORDLONG, - R12: DWORDLONG, - R13: DWORDLONG, - R14: DWORDLONG, - R15: DWORDLONG, - - Rip: DWORDLONG, - - FltSave: FLOATING_SAVE_AREA, - - VectorRegister: [M128A, .. 26], - VectorControl: DWORDLONG, - - DebugControl: DWORDLONG, - LastBranchToRip: DWORDLONG, - LastBranchFromRip: DWORDLONG, - LastExceptionToRip: DWORDLONG, - LastExceptionFromRip: DWORDLONG, - } - - #[repr(C)] - pub struct M128A { - _align_hack: [simd::u64x2, ..0], // FIXME align on 16-byte - Low: c_ulonglong, - High: c_longlong - } - - #[repr(C)] - pub struct FLOATING_SAVE_AREA { - _align_hack: [simd::u64x2, ..0], // FIXME align on 16-byte - _Dummy: [u8, ..512] // FIXME: Fill this out - } - - pub fn init_frame(frame: &mut super::STACKFRAME64, - ctx: &CONTEXT) -> DWORD { - frame.AddrPC.Offset = ctx.Rip as u64; - frame.AddrPC.Mode = super::ADDRESS_MODE::AddrModeFlat; - frame.AddrStack.Offset = ctx.Rsp as u64; - frame.AddrStack.Mode = super::ADDRESS_MODE::AddrModeFlat; - frame.AddrFrame.Offset = ctx.Rbp as u64; - frame.AddrFrame.Mode = super::ADDRESS_MODE::AddrModeFlat; - super::IMAGE_FILE_MACHINE_AMD64 - } - } - - #[repr(C)] - struct Cleanup { - handle: libc::HANDLE, - SymCleanup: SymCleanupFn, - } - - impl Drop for Cleanup { - fn drop(&mut self) { (self.SymCleanup)(self.handle); } - } - - pub fn write(w: &mut Writer) -> IoResult<()> { - // According to windows documentation, all dbghelp functions are - // single-threaded. - static LOCK: StaticMutex = MUTEX_INIT; - let _g = LOCK.lock(); - - // Open up dbghelp.dll, we don't link to it explicitly because it can't - // always be found. Additionally, it's nice having fewer dependencies. - let path = Path::new("dbghelp.dll"); - let lib = match DynamicLibrary::open(Some(&path)) { - Ok(lib) => lib, - Err(..) => return Ok(()), - }; - - macro_rules! sym { ($e:expr, $t:ident) => (unsafe { - match lib.symbol($e) { - Ok(f) => mem::transmute::<*mut u8, $t>(f), - Err(..) => return Ok(()) - } - }) } - - // Fetch the symbols necessary from dbghelp.dll - let SymFromAddr = sym!("SymFromAddr", SymFromAddrFn); - let SymInitialize = sym!("SymInitialize", SymInitializeFn); - let SymCleanup = sym!("SymCleanup", SymCleanupFn); - let StackWalk64 = sym!("StackWalk64", StackWalk64Fn); - - // Allocate necessary structures for doing the stack walk - let process = unsafe { GetCurrentProcess() }; - let thread = unsafe { GetCurrentThread() }; - let mut context: arch::CONTEXT = unsafe { intrinsics::init() }; - unsafe { RtlCaptureContext(&mut context); } - let mut frame: STACKFRAME64 = unsafe { intrinsics::init() }; - let image = arch::init_frame(&mut frame, &context); - - // Initialize this process's symbols - let ret = SymInitialize(process, 0 as *mut libc::c_void, libc::TRUE); - if ret != libc::TRUE { return Ok(()) } - let _c = Cleanup { handle: process, SymCleanup: SymCleanup }; - - // And now that we're done with all the setup, do the stack walking! - let mut i = 0i; - try!(write!(w, "stack backtrace:\n")); - while StackWalk64(image, process, thread, &mut frame, &mut context, - 0 as *mut libc::c_void, - 0 as *mut libc::c_void, - 0 as *mut libc::c_void, - 0 as *mut libc::c_void) == libc::TRUE{ - let addr = frame.AddrPC.Offset; - if addr == frame.AddrReturn.Offset || addr == 0 || - frame.AddrReturn.Offset == 0 { break } - - i += 1; - try!(write!(w, " {:2}: {:#2$x}", i, addr, super::HEX_WIDTH)); - let mut info: SYMBOL_INFO = unsafe { intrinsics::init() }; - info.MaxNameLen = MAX_SYM_NAME as libc::c_ulong; - // the struct size in C. the value is different to - // `size_of::() - MAX_SYM_NAME + 1` (== 81) - // due to struct alignment. - info.SizeOfStruct = 88; - - let mut displacement = 0u64; - let ret = SymFromAddr(process, addr as u64, &mut displacement, - &mut info); - - if ret == libc::TRUE { - try!(write!(w, " - ")); - let cstr = unsafe { CString::new(info.Name.as_ptr(), false) }; - let bytes = cstr.as_bytes(); - match cstr.as_str() { - Some(s) => try!(super::demangle(w, s)), - None => try!(w.write(bytes[..bytes.len()-1])), - } - if displacement != 0 { - try!(write!(w, "+{:#x}", displacement)); - } - } - try!(w.write(&['\n' as u8])); - } - - Ok(()) - } -} - #[cfg(test)] mod test { use prelude::*; - use io::MemWriter; - + use sys_common; macro_rules! t { ($a:expr, $b:expr) => ({ let mut m = Vec::new(); - super::demangle(&mut m, $a).unwrap(); + sys_common::backtrace::demangle(&mut m, $a).unwrap(); assert_eq!(String::from_utf8(m).unwrap(), $b); }) } diff --git a/src/librustrt/bookkeeping.rs b/src/libstd/rt/bookkeeping.rs similarity index 95% rename from src/librustrt/bookkeeping.rs rename to src/libstd/rt/bookkeeping.rs index e918a496d5531..aca520fc088f5 100644 --- a/src/librustrt/bookkeeping.rs +++ b/src/libstd/rt/bookkeeping.rs @@ -18,10 +18,10 @@ //! each respective runtime to make sure that they call increment() and //! decrement() manually. -use core::atomic; -use core::ops::Drop; +use sync::atomic; +use ops::Drop; -use mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT}; +use rt::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT}; static TASK_COUNT: atomic::AtomicUint = atomic::INIT_ATOMIC_UINT; static TASK_LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT; diff --git a/src/librustrt/exclusive.rs b/src/libstd/rt/exclusive.rs similarity index 96% rename from src/librustrt/exclusive.rs rename to src/libstd/rt/exclusive.rs index 1d8ea2202bf0d..1d3082d1b4cc4 100644 --- a/src/librustrt/exclusive.rs +++ b/src/libstd/rt/exclusive.rs @@ -10,8 +10,8 @@ use core::prelude::*; -use core::cell::UnsafeCell; -use mutex; +use cell::UnsafeCell; +use rt::mutex; /// An OS mutex over some data. /// @@ -79,10 +79,10 @@ impl<'a, T: Send> DerefMut for ExclusiveGuard<'a, T> { #[cfg(test)] mod tests { - use std::prelude::*; - use alloc::arc::Arc; + use prelude::*; + use sync::Arc; use super::Exclusive; - use std::task; + use task; #[test] fn exclusive_new_arc() { diff --git a/src/librustrt/libunwind.rs b/src/libstd/rt/libunwind.rs similarity index 100% rename from src/librustrt/libunwind.rs rename to src/libstd/rt/libunwind.rs diff --git a/src/librustrt/local.rs b/src/libstd/rt/local.rs similarity index 96% rename from src/librustrt/local.rs rename to src/libstd/rt/local.rs index df733d7ee225b..089960a6bc80b 100644 --- a/src/librustrt/local.rs +++ b/src/libstd/rt/local.rs @@ -10,9 +10,9 @@ use core::prelude::*; -use alloc::boxed::Box; -use local_ptr; -use task::Task; +use boxed::Box; +use rt::local_ptr; +use rt::task::Task; /// Encapsulates some task-local data. pub trait Local { @@ -52,10 +52,10 @@ impl Local> for Task { #[cfg(test)] mod test { - use std::prelude::*; - use thread::Thread; + use prelude::*; use super::*; - use task::Task; + use super::super::thread::Thread; + use super::super::task::Task; #[test] fn thread_local_task_smoke_test() { diff --git a/src/librustrt/local_ptr.rs b/src/libstd/rt/local_ptr.rs similarity index 97% rename from src/librustrt/local_ptr.rs rename to src/libstd/rt/local_ptr.rs index 58f8f8b310f55..a87bc3d27662e 100644 --- a/src/librustrt/local_ptr.rs +++ b/src/libstd/rt/local_ptr.rs @@ -19,8 +19,8 @@ use core::prelude::*; -use core::mem; -use alloc::boxed::Box; +use mem; +use boxed::Box; #[cfg(any(windows, // mingw-w32 doesn't like thread_local things target_os = "android", // see #10686 @@ -86,11 +86,11 @@ pub unsafe fn borrow() -> Borrowed { pub mod compiled { use core::prelude::*; - use alloc::boxed::Box; - use core::mem; + use boxed::Box; + use mem; #[cfg(test)] - pub use realrustrt::shouldnt_be_public::RT_TLS_PTR; + pub use realstd::rt::shouldnt_be_public::RT_TLS_PTR; #[cfg(not(test))] #[thread_local] @@ -237,10 +237,10 @@ pub mod compiled { pub mod native { use core::prelude::*; - use alloc::boxed::Box; - use core::mem; - use core::ptr; - use thread_local_storage as tls; + use boxed::Box; + use mem; + use ptr; + use rt::thread_local_storage as tls; static mut RT_TLS_KEY: tls::Key = -1; @@ -396,9 +396,9 @@ pub mod native { #[inline] #[cfg(test)] pub fn maybe_tls_key() -> Option { - use realrustrt; + use rt; unsafe { - mem::transmute(realrustrt::shouldnt_be_public::maybe_tls_key()) + mem::transmute(::realstd::rt::shouldnt_be_public::maybe_tls_key()) } } } diff --git a/src/librustrt/macros.rs b/src/libstd/rt/macros.rs similarity index 86% rename from src/librustrt/macros.rs rename to src/libstd/rt/macros.rs index 4514430853572..bee8b5b82f48e 100644 --- a/src/librustrt/macros.rs +++ b/src/libstd/rt/macros.rs @@ -17,7 +17,7 @@ macro_rules! rterrln { ($fmt:expr $($arg:tt)*) => ( { - format_args!(::util::dumb_print, concat!($fmt, "\n") $($arg)*) + format_args!(::rt::util::dumb_print, concat!($fmt, "\n") $($arg)*) } ) } @@ -32,7 +32,7 @@ macro_rules! rtdebug { macro_rules! rtassert { ( $arg:expr ) => ( { - if ::util::ENFORCE_SANITY { + if ::rt::util::ENFORCE_SANITY { if !$arg { rtabort!(" assertion failed: {}", stringify!($arg)); } @@ -40,7 +40,6 @@ macro_rules! rtassert { } ) } - macro_rules! rtabort { - ($($arg:tt)*) => (format_args!(::util::abort, $($arg)*)) + ($($arg:tt)*) => (format_args!(::rt::util::abort, $($arg)*)) } diff --git a/src/libstd/rt/mod.rs b/src/libstd/rt/mod.rs index eb517047ddc0b..21c8197ef0558 100644 --- a/src/libstd/rt/mod.rs +++ b/src/libstd/rt/mod.rs @@ -50,23 +50,43 @@ use borrow::IntoCow; use failure; -use rustrt; use os; use thunk::Thunk; +use kinds::Send; +use sys_common; // Reexport some of our utilities which are expected by other crates. pub use self::util::{default_sched_threads, min_stack, running_on_valgrind}; +pub use self::unwind::{begin_unwind, begin_unwind_fmt}; -// Reexport functionality from librustrt and other crates underneath the -// standard library which work together to create the entire runtime. +// Reexport some functionality from liballoc. pub use alloc::heap; -pub use rustrt::{begin_unwind, begin_unwind_fmt, at_exit}; // Simple backtrace functionality (to print on panic) pub mod backtrace; -// Just stuff -mod util; +// Internals +mod macros; + +// These should be refactored/moved/made private over time +pub mod mutex; +pub mod thread; +pub mod exclusive; +pub mod util; +pub mod bookkeeping; +pub mod local; +pub mod task; +pub mod unwind; + +mod args; +mod at_exit_imp; +mod libunwind; +mod local_ptr; +mod thread_local_storage; + +/// The default error code of the rust runtime if the main task panics instead +/// of exiting cleanly. +pub const DEFAULT_ERROR_CODE: int = 101; /// One-time runtime initialization. /// @@ -75,8 +95,15 @@ mod util; /// metadata, and storing the process arguments. #[allow(experimental)] pub fn init(argc: int, argv: *const *const u8) { - rustrt::init(argc, argv); - unsafe { rustrt::unwind::register(failure::on_fail); } + // FIXME: Derefing these pointers is not safe. + // Need to propagate the unsafety to `start`. + unsafe { + args::init(argc, argv); + local_ptr::init(); + at_exit_imp::init(); + thread::init(); + unwind::register(failure::on_fail); + } } #[cfg(any(windows, android))] @@ -106,7 +133,8 @@ fn lang_start(main: *const u8, argc: int, argv: *const *const u8) -> int { pub fn start(argc: int, argv: *const *const u8, main: Thunk) -> int { use prelude::*; use rt; - use rustrt::task::Task; + use rt::task::Task; + use str; let something_around_the_top_of_the_stack = 1; let addr = &something_around_the_top_of_the_stack as *const int; @@ -139,18 +167,35 @@ pub fn start(argc: int, argv: *const *const u8, main: Thunk) -> int { let mut exit_code = None; let mut main = Some(main); let mut task = box Task::new(Some((my_stack_bottom, my_stack_top)), - Some(rustrt::thread::main_guard_page())); - task.name = Some("
".into_cow()); + Some(rt::thread::main_guard_page())); + task.name = Some(str::Slice("
")); drop(task.run(|| { unsafe { - rustrt::stack::record_os_managed_stack_bounds(my_stack_bottom, my_stack_top); + sys_common::stack::record_os_managed_stack_bounds(my_stack_bottom, my_stack_top); } (main.take().unwrap()).invoke(()); exit_code = Some(os::get_exit_status()); }).destroy()); - unsafe { rt::cleanup(); } + unsafe { cleanup(); } // If the exit code wasn't set, then the task block must have panicked. - return exit_code.unwrap_or(rustrt::DEFAULT_ERROR_CODE); + return exit_code.unwrap_or(rt::DEFAULT_ERROR_CODE); +} + +/// Enqueues a procedure to run when the runtime is cleaned up +/// +/// The procedure passed to this function will be executed as part of the +/// runtime cleanup phase. For normal rust programs, this means that it will run +/// after all other tasks have exited. +/// +/// The procedure is *not* executed with a local `Task` available to it, so +/// primitives like logging, I/O, channels, spawning, etc, are *not* available. +/// This is meant for "bare bones" usage to clean up runtime details, this is +/// not meant as a general-purpose "let's clean everything up" function. +/// +/// It is forbidden for procedures to register more `at_exit` handlers when they +/// are running, and doing so will lead to a process abort. +pub fn at_exit(f: proc():Send) { + at_exit_imp::push(f); } /// One-time runtime cleanup. @@ -163,5 +208,18 @@ pub fn start(argc: int, argv: *const *const u8, main: Thunk) -> int { /// Invoking cleanup while portions of the runtime are still in use may cause /// undefined behavior. pub unsafe fn cleanup() { - rustrt::cleanup(); + bookkeeping::wait_for_other_tasks(); + args::cleanup(); + thread::cleanup(); + local_ptr::cleanup(); + at_exit_imp::run(); +} + +// FIXME: these probably shouldn't be public... +#[doc(hidden)] +pub mod shouldnt_be_public { + #[cfg(not(test))] + pub use super::local_ptr::native::maybe_tls_key; + #[cfg(all(not(windows), not(target_os = "android"), not(target_os = "ios")))] + pub use super::local_ptr::compiled::RT_TLS_PTR; } diff --git a/src/librustrt/mutex.rs b/src/libstd/rt/mutex.rs similarity index 52% rename from src/librustrt/mutex.rs rename to src/libstd/rt/mutex.rs index cbfb86a6ac7b2..381f14570dfbc 100644 --- a/src/librustrt/mutex.rs +++ b/src/libstd/rt/mutex.rs @@ -33,7 +33,7 @@ //! # Example //! //! ```rust -//! use rustrt::mutex::{NativeMutex, StaticNativeMutex, NATIVE_MUTEX_INIT}; +//! use rt::mutex::{NativeMutex, StaticNativeMutex, NATIVE_MUTEX_INIT}; //! //! // Use a statically initialized mutex //! static LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT; @@ -60,6 +60,8 @@ use core::prelude::*; +use sys::mutex as imp; + /// A native mutex suitable for storing in statics (that is, it has /// the `destroy` method rather than a destructor). /// @@ -108,7 +110,7 @@ impl StaticNativeMutex { /// # Example /// /// ```rust - /// use rustrt::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT}; + /// use rt::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT}; /// static LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT; /// unsafe { /// let _guard = LOCK.lock(); @@ -225,7 +227,7 @@ impl NativeMutex { /// # Example /// /// ```rust - /// use rustrt::mutex::NativeMutex; + /// use rt::mutex::NativeMutex; /// unsafe { /// let mut lock = NativeMutex::new(); /// @@ -336,336 +338,13 @@ impl<'a> Drop for LockGuard<'a> { } } -#[cfg(unix)] -mod imp { - use libc; - use self::os::{PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, - pthread_mutex_t, pthread_cond_t}; - use core::cell::UnsafeCell; - - type pthread_mutexattr_t = libc::c_void; - type pthread_condattr_t = libc::c_void; - - #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] - mod os { - use libc; - - pub type pthread_mutex_t = *mut libc::c_void; - pub type pthread_cond_t = *mut libc::c_void; - - pub const PTHREAD_MUTEX_INITIALIZER: pthread_mutex_t = - 0 as pthread_mutex_t; - pub const PTHREAD_COND_INITIALIZER: pthread_cond_t = - 0 as pthread_cond_t; - } - - #[cfg(any(target_os = "macos", target_os = "ios"))] - mod os { - use core::kinds::Copy; - use libc; - - #[cfg(target_arch = "x86_64")] - const __PTHREAD_MUTEX_SIZE__: uint = 56; - #[cfg(target_arch = "x86_64")] - const __PTHREAD_COND_SIZE__: uint = 40; - #[cfg(target_arch = "x86")] - const __PTHREAD_MUTEX_SIZE__: uint = 40; - #[cfg(target_arch = "x86")] - const __PTHREAD_COND_SIZE__: uint = 24; - #[cfg(target_arch = "arm")] - const __PTHREAD_MUTEX_SIZE__: uint = 40; - #[cfg(target_arch = "arm")] - const __PTHREAD_COND_SIZE__: uint = 24; - - const _PTHREAD_MUTEX_SIG_INIT: libc::c_long = 0x32AAABA7; - const _PTHREAD_COND_SIG_INIT: libc::c_long = 0x3CB0B1BB; - - #[repr(C)] - pub struct pthread_mutex_t { - __sig: libc::c_long, - __opaque: [u8, ..__PTHREAD_MUTEX_SIZE__], - } - - impl Copy for pthread_mutex_t {} - - #[repr(C)] - pub struct pthread_cond_t { - __sig: libc::c_long, - __opaque: [u8, ..__PTHREAD_COND_SIZE__], - } - - impl Copy for pthread_cond_t {} - - pub const PTHREAD_MUTEX_INITIALIZER: pthread_mutex_t = pthread_mutex_t { - __sig: _PTHREAD_MUTEX_SIG_INIT, - __opaque: [0, ..__PTHREAD_MUTEX_SIZE__], - }; - pub const PTHREAD_COND_INITIALIZER: pthread_cond_t = pthread_cond_t { - __sig: _PTHREAD_COND_SIG_INIT, - __opaque: [0, ..__PTHREAD_COND_SIZE__], - }; - } - - #[cfg(target_os = "linux")] - mod os { - use core::kinds::Copy; - use libc; - - // minus 8 because we have an 'align' field - #[cfg(target_arch = "x86_64")] - const __SIZEOF_PTHREAD_MUTEX_T: uint = 40 - 8; - #[cfg(target_arch = "x86")] - const __SIZEOF_PTHREAD_MUTEX_T: uint = 24 - 8; - #[cfg(target_arch = "arm")] - const __SIZEOF_PTHREAD_MUTEX_T: uint = 24 - 8; - #[cfg(target_arch = "mips")] - const __SIZEOF_PTHREAD_MUTEX_T: uint = 24 - 8; - #[cfg(target_arch = "mipsel")] - const __SIZEOF_PTHREAD_MUTEX_T: uint = 24 - 8; - #[cfg(target_arch = "x86_64")] - const __SIZEOF_PTHREAD_COND_T: uint = 48 - 8; - #[cfg(target_arch = "x86")] - const __SIZEOF_PTHREAD_COND_T: uint = 48 - 8; - #[cfg(target_arch = "arm")] - const __SIZEOF_PTHREAD_COND_T: uint = 48 - 8; - #[cfg(target_arch = "mips")] - const __SIZEOF_PTHREAD_COND_T: uint = 48 - 8; - #[cfg(target_arch = "mipsel")] - const __SIZEOF_PTHREAD_COND_T: uint = 48 - 8; - - #[repr(C)] - pub struct pthread_mutex_t { - __align: libc::c_longlong, - size: [u8, ..__SIZEOF_PTHREAD_MUTEX_T], - } - - impl Copy for pthread_mutex_t {} - - #[repr(C)] - pub struct pthread_cond_t { - __align: libc::c_longlong, - size: [u8, ..__SIZEOF_PTHREAD_COND_T], - } - - impl Copy for pthread_cond_t {} - - pub const PTHREAD_MUTEX_INITIALIZER: pthread_mutex_t = pthread_mutex_t { - __align: 0, - size: [0, ..__SIZEOF_PTHREAD_MUTEX_T], - }; - pub const PTHREAD_COND_INITIALIZER: pthread_cond_t = pthread_cond_t { - __align: 0, - size: [0, ..__SIZEOF_PTHREAD_COND_T], - }; - } - #[cfg(target_os = "android")] - mod os { - use libc; - - #[repr(C)] - pub struct pthread_mutex_t { value: libc::c_int } - #[repr(C)] - pub struct pthread_cond_t { value: libc::c_int } - - pub const PTHREAD_MUTEX_INITIALIZER: pthread_mutex_t = pthread_mutex_t { - value: 0, - }; - pub const PTHREAD_COND_INITIALIZER: pthread_cond_t = pthread_cond_t { - value: 0, - }; - } - - pub struct Mutex { - lock: UnsafeCell, - cond: UnsafeCell, - } - - pub const MUTEX_INIT: Mutex = Mutex { - lock: UnsafeCell { value: PTHREAD_MUTEX_INITIALIZER }, - cond: UnsafeCell { value: PTHREAD_COND_INITIALIZER }, - }; - - impl Mutex { - pub unsafe fn new() -> Mutex { - // As mutex might be moved and address is changing it - // is better to avoid initialization of potentially - // opaque OS data before it landed - let m = Mutex { - lock: UnsafeCell::new(PTHREAD_MUTEX_INITIALIZER), - cond: UnsafeCell::new(PTHREAD_COND_INITIALIZER), - }; - - return m; - } - - pub unsafe fn lock(&self) { pthread_mutex_lock(self.lock.get()); } - pub unsafe fn unlock(&self) { pthread_mutex_unlock(self.lock.get()); } - pub unsafe fn signal(&self) { pthread_cond_signal(self.cond.get()); } - pub unsafe fn wait(&self) { - pthread_cond_wait(self.cond.get(), self.lock.get()); - } - pub unsafe fn trylock(&self) -> bool { - pthread_mutex_trylock(self.lock.get()) == 0 - } - pub unsafe fn destroy(&self) { - pthread_mutex_destroy(self.lock.get()); - pthread_cond_destroy(self.cond.get()); - } - } - - extern { - fn pthread_mutex_destroy(lock: *mut pthread_mutex_t) -> libc::c_int; - fn pthread_cond_destroy(cond: *mut pthread_cond_t) -> libc::c_int; - fn pthread_mutex_lock(lock: *mut pthread_mutex_t) -> libc::c_int; - fn pthread_mutex_trylock(lock: *mut pthread_mutex_t) -> libc::c_int; - fn pthread_mutex_unlock(lock: *mut pthread_mutex_t) -> libc::c_int; - - fn pthread_cond_wait(cond: *mut pthread_cond_t, - lock: *mut pthread_mutex_t) -> libc::c_int; - fn pthread_cond_signal(cond: *mut pthread_cond_t) -> libc::c_int; - } -} - -#[cfg(windows)] -mod imp { - use alloc::heap; - use core::atomic; - use core::ptr; - use core::ptr::RawPtr; - use libc::{HANDLE, BOOL, LPSECURITY_ATTRIBUTES, c_void, DWORD, LPCSTR}; - use libc; - - type LPCRITICAL_SECTION = *mut c_void; - const SPIN_COUNT: DWORD = 4000; - #[cfg(target_arch = "x86")] - const CRIT_SECTION_SIZE: uint = 24; - #[cfg(target_arch = "x86_64")] - const CRIT_SECTION_SIZE: uint = 40; - - pub struct Mutex { - // pointers for the lock/cond handles, atomically updated - lock: atomic::AtomicUint, - cond: atomic::AtomicUint, - } - - pub const MUTEX_INIT: Mutex = Mutex { - lock: atomic::INIT_ATOMIC_UINT, - cond: atomic::INIT_ATOMIC_UINT, - }; - - impl Mutex { - pub unsafe fn new() -> Mutex { - Mutex { - lock: atomic::AtomicUint::new(init_lock()), - cond: atomic::AtomicUint::new(init_cond()), - } - } - pub unsafe fn lock(&self) { - EnterCriticalSection(self.getlock() as LPCRITICAL_SECTION) - } - pub unsafe fn trylock(&self) -> bool { - TryEnterCriticalSection(self.getlock() as LPCRITICAL_SECTION) != 0 - } - pub unsafe fn unlock(&self) { - LeaveCriticalSection(self.getlock() as LPCRITICAL_SECTION) - } - - pub unsafe fn wait(&self) { - self.unlock(); - WaitForSingleObject(self.getcond() as HANDLE, libc::INFINITE); - self.lock(); - } - - pub unsafe fn signal(&self) { - assert!(SetEvent(self.getcond() as HANDLE) != 0); - } - - /// This function is especially unsafe because there are no guarantees made - /// that no other thread is currently holding the lock or waiting on the - /// condition variable contained inside. - pub unsafe fn destroy(&self) { - let lock = self.lock.swap(0, atomic::SeqCst); - let cond = self.cond.swap(0, atomic::SeqCst); - if lock != 0 { free_lock(lock) } - if cond != 0 { free_cond(cond) } - } - - unsafe fn getlock(&self) -> *mut c_void { - match self.lock.load(atomic::SeqCst) { - 0 => {} - n => return n as *mut c_void - } - let lock = init_lock(); - match self.lock.compare_and_swap(0, lock, atomic::SeqCst) { - 0 => return lock as *mut c_void, - _ => {} - } - free_lock(lock); - return self.lock.load(atomic::SeqCst) as *mut c_void; - } - - unsafe fn getcond(&self) -> *mut c_void { - match self.cond.load(atomic::SeqCst) { - 0 => {} - n => return n as *mut c_void - } - let cond = init_cond(); - match self.cond.compare_and_swap(0, cond, atomic::SeqCst) { - 0 => return cond as *mut c_void, - _ => {} - } - free_cond(cond); - return self.cond.load(atomic::SeqCst) as *mut c_void; - } - } - - pub unsafe fn init_lock() -> uint { - let block = heap::allocate(CRIT_SECTION_SIZE, 8) as *mut c_void; - if block.is_null() { ::alloc::oom() } - InitializeCriticalSectionAndSpinCount(block, SPIN_COUNT); - return block as uint; - } - - pub unsafe fn init_cond() -> uint { - return CreateEventA(ptr::null_mut(), libc::FALSE, libc::FALSE, - ptr::null()) as uint; - } - - pub unsafe fn free_lock(h: uint) { - DeleteCriticalSection(h as LPCRITICAL_SECTION); - heap::deallocate(h as *mut u8, CRIT_SECTION_SIZE, 8); - } - - pub unsafe fn free_cond(h: uint) { - let block = h as HANDLE; - libc::CloseHandle(block); - } - - #[allow(non_snake_case)] - extern "system" { - fn CreateEventA(lpSecurityAttributes: LPSECURITY_ATTRIBUTES, - bManualReset: BOOL, - bInitialState: BOOL, - lpName: LPCSTR) -> HANDLE; - fn InitializeCriticalSectionAndSpinCount( - lpCriticalSection: LPCRITICAL_SECTION, - dwSpinCount: DWORD) -> BOOL; - fn DeleteCriticalSection(lpCriticalSection: LPCRITICAL_SECTION); - fn EnterCriticalSection(lpCriticalSection: LPCRITICAL_SECTION); - fn LeaveCriticalSection(lpCriticalSection: LPCRITICAL_SECTION); - fn TryEnterCriticalSection(lpCriticalSection: LPCRITICAL_SECTION) -> BOOL; - fn SetEvent(hEvent: HANDLE) -> BOOL; - fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) -> DWORD; - } -} - #[cfg(test)] mod test { - use std::prelude::*; + use prelude::*; - use std::mem::drop; + use mem::drop; use super::{StaticNativeMutex, NATIVE_MUTEX_INIT}; - use thread::Thread; + use rt::thread::Thread; #[test] fn smoke_lock() { diff --git a/src/librustrt/task.rs b/src/libstd/rt/task.rs similarity index 96% rename from src/librustrt/task.rs rename to src/libstd/rt/task.rs index b942a3819cc41..babd111b3c221 100644 --- a/src/librustrt/task.rs +++ b/src/libstd/rt/task.rs @@ -15,27 +15,28 @@ pub use self::BlockedTask::*; use self::TaskState::*; -use alloc::arc::Arc; -use alloc::boxed::Box; -use core::any::Any; -use core::atomic::{AtomicUint, SeqCst}; -use core::iter::{IteratorExt, Take}; -use core::ops::FnOnce; -use core::mem; -use core::ops::FnMut; +use any::Any; +use boxed::Box; +use sync::Arc; +use sync::atomic::{AtomicUint, SeqCst}; +use iter::{IteratorExt, Take}; +use kinds::marker; +use mem; +use ops::FnMut; use core::prelude::{Clone, Drop, Err, Iterator, None, Ok, Option, Send, Some}; use core::prelude::{drop}; - -use bookkeeping; -use mutex::NativeMutex; -use local::Local; -use thread::{mod, Thread}; -use stack; -use unwind; -use unwind::Unwinder; -use collections::str::SendStr; +use str::SendStr; use thunk::Thunk; +use rt; +use rt::bookkeeping; +use rt::mutex::NativeMutex; +use rt::local::Local; +use rt::thread::{mod, Thread}; +use sys_common::stack; +use rt::unwind; +use rt::unwind::Unwinder; + /// State associated with Rust tasks. /// /// This structure is currently undergoing major changes, and is @@ -129,9 +130,7 @@ impl Task { task.name = name; task.death.on_exit = on_exit; - // FIXME: change this back after moving rustrt into std - // let stack = stack_size.unwrap_or(rt::min_stack()); - let stack = stack_size.unwrap_or(2 * 1024 * 1024); + let stack = stack_size.unwrap_or(rt::min_stack()); // Note that this increment must happen *before* the spawn in order to // guarantee that if this task exits it will always end up waiting for @@ -504,8 +503,9 @@ impl Death { #[cfg(test)] mod test { use super::*; - use std::prelude::*; - use std::task; + use prelude::*; + use task; + use rt::unwind; #[test] fn unwind() { @@ -519,7 +519,7 @@ mod test { #[test] fn rng() { - use std::rand::{StdRng, Rng}; + use rand::{StdRng, Rng}; let mut r = StdRng::new().ok().unwrap(); let _ = r.next_u32(); } @@ -541,7 +541,7 @@ mod test { #[test] #[should_fail] fn test_begin_unwind() { - use unwind::begin_unwind; + use rt::unwind::begin_unwind; begin_unwind("cause", &(file!(), line!())) } diff --git a/src/libstd/rt/thread.rs b/src/libstd/rt/thread.rs new file mode 100644 index 0000000000000..c10338b1bceb2 --- /dev/null +++ b/src/libstd/rt/thread.rs @@ -0,0 +1,171 @@ +// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Native os-thread management +//! +//! This modules contains bindings necessary for managing OS-level threads. +//! These functions operate outside of the rust runtime, creating threads +//! which are not used for scheduling in any way. + +#![allow(non_camel_case_types)] + +use core::prelude::*; + +use boxed::Box; +use mem; +use sys::stack_overflow; +use sys::thread as imp; + +pub unsafe fn init() { + imp::guard::init(); + stack_overflow::init(); +} + +pub unsafe fn cleanup() { + stack_overflow::cleanup(); +} + +/// This struct represents a native thread's state. This is used to join on an +/// existing thread created in the join-able state. +pub struct Thread { + native: imp::rust_thread, + joined: bool, + packet: Box>, +} + +static DEFAULT_STACK_SIZE: uint = 1024 * 1024; + +/// Returns the last writable byte of the main thread's stack next to the guard +/// page. Must be called from the main thread. +pub fn main_guard_page() -> uint { + unsafe { + imp::guard::main() + } +} + +/// Returns the last writable byte of the current thread's stack next to the +/// guard page. Must not be called from the main thread. +pub fn current_guard_page() -> uint { + unsafe { + imp::guard::current() + } +} + +// There are two impl blocks b/c if T were specified at the top then it's just a +// pain to specify a type parameter on Thread::spawn (which doesn't need the +// type parameter). +impl Thread<()> { + + /// Starts execution of a new OS thread. + /// + /// This function will not wait for the thread to join, but a handle to the + /// thread will be returned. + /// + /// Note that the handle returned is used to acquire the return value of the + /// procedure `main`. The `join` function will wait for the thread to finish + /// and return the value that `main` generated. + /// + /// Also note that the `Thread` returned will *always* wait for the thread + /// to finish executing. This means that even if `join` is not explicitly + /// called, when the `Thread` falls out of scope its destructor will block + /// waiting for the OS thread. + pub fn start(main: proc():Send -> T) -> Thread { + Thread::start_stack(DEFAULT_STACK_SIZE, main) + } + + /// Performs the same functionality as `start`, but specifies an explicit + /// stack size for the new thread. + pub fn start_stack(stack: uint, main: proc():Send -> T) -> Thread { + + // We need the address of the packet to fill in to be stable so when + // `main` fills it in it's still valid, so allocate an extra box to do + // so. + let packet = box None; + let packet2: *mut Option = unsafe { + *mem::transmute::<&Box>, *const *mut Option>(&packet) + }; + let main = proc() unsafe { *packet2 = Some(main()); }; + let native = unsafe { imp::create(stack, box main) }; + + Thread { + native: native, + joined: false, + packet: packet, + } + } + + /// This will spawn a new thread, but it will not wait for the thread to + /// finish, nor is it possible to wait for the thread to finish. + /// + /// This corresponds to creating threads in the 'detached' state on unix + /// systems. Note that platforms may not keep the main program alive even if + /// there are detached thread still running around. + pub fn spawn(main: proc():Send) { + Thread::spawn_stack(DEFAULT_STACK_SIZE, main) + } + + /// Performs the same functionality as `spawn`, but explicitly specifies a + /// stack size for the new thread. + pub fn spawn_stack(stack: uint, main: proc():Send) { + unsafe { + let handle = imp::create(stack, box main); + imp::detach(handle); + } + } + + /// Relinquishes the CPU slot that this OS-thread is currently using, + /// allowing another thread to run for awhile. + pub fn yield_now() { + unsafe { imp::yield_now(); } + } +} + +impl Thread { + /// Wait for this thread to finish, returning the result of the thread's + /// calculation. + pub fn join(mut self) -> T { + assert!(!self.joined); + unsafe { imp::join(self.native) }; + self.joined = true; + assert!(self.packet.is_some()); + self.packet.take().unwrap() + } +} + +#[unsafe_destructor] +impl Drop for Thread { + fn drop(&mut self) { + // This is required for correctness. If this is not done then the thread + // would fill in a return box which no longer exists. + if !self.joined { + unsafe { imp::join(self.native) }; + } + } +} + +#[cfg(test)] +mod tests { + use super::Thread; + + #[test] + fn smoke() { Thread::start(proc (){}).join(); } + + #[test] + fn data() { assert_eq!(Thread::start(proc () { 1i }).join(), 1); } + + #[test] + fn detached() { Thread::spawn(proc () {}) } + + #[test] + fn small_stacks() { + assert_eq!(42i, Thread::start_stack(0, proc () 42i).join()); + assert_eq!(42i, Thread::start_stack(1, proc () 42i).join()); + } +} diff --git a/src/librustrt/thread_local_storage.rs b/src/libstd/rt/thread_local_storage.rs similarity index 96% rename from src/librustrt/thread_local_storage.rs rename to src/libstd/rt/thread_local_storage.rs index aee70980bbaf7..ee6ad8a4e08e8 100644 --- a/src/librustrt/thread_local_storage.rs +++ b/src/libstd/rt/thread_local_storage.rs @@ -11,7 +11,7 @@ #![allow(dead_code)] #[cfg(unix)] use libc::c_int; -#[cfg(unix)] use core::ptr::null; +#[cfg(unix)] use ptr::null; #[cfg(windows)] use libc::types::os::arch::extra::{DWORD, LPVOID, BOOL}; #[cfg(unix)] @@ -93,12 +93,12 @@ extern "system" { #[cfg(test)] mod test { - use std::prelude::*; + use prelude::*; use super::*; #[test] fn tls_smoke_test() { - use std::mem::transmute; + use mem::transmute; unsafe { let mut key = 0; let value = box 20i; diff --git a/src/librustrt/unwind.rs b/src/libstd/rt/unwind.rs similarity index 98% rename from src/librustrt/unwind.rs rename to src/libstd/rt/unwind.rs index 5bc542a84e6e8..1ac06270851c5 100644 --- a/src/librustrt/unwind.rs +++ b/src/libstd/rt/unwind.rs @@ -59,23 +59,23 @@ use core::prelude::*; -use alloc::boxed::Box; -use collections::string::String; -use collections::str::StrAllocating; -use collections::vec::Vec; -use core::any::Any; -use core::atomic; -use core::cmp; -use core::fmt; -use core::intrinsics; -use core::mem; -use core::raw::Closure; +use boxed::Box; +use string::String; +use str::StrAllocating; +use vec::Vec; +use any::Any; +use sync::atomic; +use cmp; +use fmt; +use intrinsics; +use mem; +use raw::Closure; use libc::c_void; -use local::Local; -use task::Task; +use rt::local::Local; +use rt::task::Task; -use libunwind as uw; +use rt::libunwind as uw; #[allow(missing_copy_implementations)] pub struct Unwinder { @@ -241,7 +241,7 @@ fn rust_exception_class() -> uw::_Unwind_Exception_Class { not(test)))] #[doc(hidden)] pub mod eabi { - use libunwind as uw; + use rt::libunwind as uw; use libc::c_int; extern "C" { @@ -294,7 +294,7 @@ pub mod eabi { #[cfg(all(target_os = "ios", target_arch = "arm", not(test)))] #[doc(hidden)] pub mod eabi { - use libunwind as uw; + use rt::libunwind as uw; use libc::c_int; extern "C" { @@ -349,7 +349,7 @@ pub mod eabi { #[cfg(all(target_arch = "arm", not(target_os = "ios"), not(test)))] #[doc(hidden)] pub mod eabi { - use libunwind as uw; + use rt::libunwind as uw; use libc::c_int; extern "C" { @@ -400,8 +400,7 @@ pub mod eabi { #[allow(non_camel_case_types, non_snake_case)] pub mod eabi { pub use self::EXCEPTION_DISPOSITION::*; - use core::prelude::*; - use libunwind as uw; + use rt::libunwind as uw; use libc::{c_void, c_int}; #[repr(C)] @@ -513,7 +512,7 @@ pub extern fn rust_begin_unwind(msg: &fmt::Arguments, /// the actual formatting into this shared place. #[inline(never)] #[cold] pub fn begin_unwind_fmt(msg: &fmt::Arguments, file_line: &(&'static str, uint)) -> ! { - use core::fmt::FormatWriter; + use fmt::FormatWriter; // We do two allocations here, unfortunately. But (a) they're // required with the current scheme, and (b) we don't handle diff --git a/src/libstd/rt/util.rs b/src/libstd/rt/util.rs index ce359c7b0e006..d3cfccab9d096 100644 --- a/src/libstd/rt/util.rs +++ b/src/libstd/rt/util.rs @@ -7,10 +7,18 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. +// +// ignore-lexer-test FIXME #15677 + +use core::prelude::*; -use libc::uintptr_t; -use option::Option; -use option::Option::{Some, None}; +use core::cmp; +use core::fmt; +use core::intrinsics; +use core::slice; +use core::str; + +use libc::{mod, uintptr_t}; use os; use str::{FromStr, from_str, Str}; use sync::atomic; @@ -73,3 +81,136 @@ pub fn default_sched_threads() -> uint { } } } + +// Indicates whether we should perform expensive sanity checks, including rtassert! +// +// FIXME: Once the runtime matures remove the `true` below to turn off rtassert, +// etc. +pub const ENFORCE_SANITY: bool = true || !cfg!(rtopt) || cfg!(rtdebug) || + cfg!(rtassert); + +pub struct Stdio(libc::c_int); + +#[allow(non_upper_case_globals)] +pub const Stdout: Stdio = Stdio(libc::STDOUT_FILENO); +#[allow(non_upper_case_globals)] +pub const Stderr: Stdio = Stdio(libc::STDERR_FILENO); + +impl fmt::FormatWriter for Stdio { + fn write(&mut self, data: &[u8]) -> fmt::Result { + #[cfg(unix)] + type WriteLen = libc::size_t; + #[cfg(windows)] + type WriteLen = libc::c_uint; + unsafe { + let Stdio(fd) = *self; + libc::write(fd, + data.as_ptr() as *const libc::c_void, + data.len() as WriteLen); + } + Ok(()) // yes, we're lying + } +} + +pub fn dumb_print(args: &fmt::Arguments) { + use fmt::FormatWriter; + let mut w = Stderr; + let _ = w.write_fmt(args); +} + +pub fn abort(args: &fmt::Arguments) -> ! { + use fmt::FormatWriter; + + struct BufWriter<'a> { + buf: &'a mut [u8], + pos: uint, + } + impl<'a> FormatWriter for BufWriter<'a> { + fn write(&mut self, bytes: &[u8]) -> fmt::Result { + let left = self.buf[mut self.pos..]; + let to_write = bytes[..cmp::min(bytes.len(), left.len())]; + slice::bytes::copy_memory(left, to_write); + self.pos += to_write.len(); + Ok(()) + } + } + + // Convert the arguments into a stack-allocated string + let mut msg = [0u8, ..512]; + let mut w = BufWriter { buf: &mut msg, pos: 0 }; + let _ = write!(&mut w, "{}", args); + let msg = str::from_utf8(w.buf[mut ..w.pos]).unwrap_or("aborted"); + let msg = if msg.is_empty() {"aborted"} else {msg}; + + // Give some context to the message + let hash = msg.bytes().fold(0, |accum, val| accum + (val as uint) ); + let quote = match hash % 10 { + 0 => " +It was from the artists and poets that the pertinent answers came, and I +know that panic would have broken loose had they been able to compare notes. +As it was, lacking their original letters, I half suspected the compiler of +having asked leading questions, or of having edited the correspondence in +corroboration of what he had latently resolved to see.", + 1 => " +There are not many persons who know what wonders are opened to them in the +stories and visions of their youth; for when as children we listen and dream, +we think but half-formed thoughts, and when as men we try to remember, we are +dulled and prosaic with the poison of life. But some of us awake in the night +with strange phantasms of enchanted hills and gardens, of fountains that sing +in the sun, of golden cliffs overhanging murmuring seas, of plains that stretch +down to sleeping cities of bronze and stone, and of shadowy companies of heroes +that ride caparisoned white horses along the edges of thick forests; and then +we know that we have looked back through the ivory gates into that world of +wonder which was ours before we were wise and unhappy.", + 2 => " +Instead of the poems I had hoped for, there came only a shuddering blackness +and ineffable loneliness; and I saw at last a fearful truth which no one had +ever dared to breathe before — the unwhisperable secret of secrets — The fact +that this city of stone and stridor is not a sentient perpetuation of Old New +York as London is of Old London and Paris of Old Paris, but that it is in fact +quite dead, its sprawling body imperfectly embalmed and infested with queer +animate things which have nothing to do with it as it was in life.", + 3 => " +The ocean ate the last of the land and poured into the smoking gulf, thereby +giving up all it had ever conquered. From the new-flooded lands it flowed +again, uncovering death and decay; and from its ancient and immemorial bed it +trickled loathsomely, uncovering nighted secrets of the years when Time was +young and the gods unborn. Above the waves rose weedy remembered spires. The +moon laid pale lilies of light on dead London, and Paris stood up from its damp +grave to be sanctified with star-dust. Then rose spires and monoliths that were +weedy but not remembered; terrible spires and monoliths of lands that men never +knew were lands...", + 4 => " +There was a night when winds from unknown spaces whirled us irresistibly into +limitless vacuum beyond all thought and entity. Perceptions of the most +maddeningly untransmissible sort thronged upon us; perceptions of infinity +which at the time convulsed us with joy, yet which are now partly lost to my +memory and partly incapable of presentation to others.", + _ => "You've met with a terrible fate, haven't you?" + }; + rterrln!("{}", ""); + rterrln!("{}", quote); + rterrln!("{}", ""); + rterrln!("fatal runtime error: {}", msg); + unsafe { intrinsics::abort(); } +} + +pub unsafe fn report_overflow() { + use rt::task::Task; + use rt::local::Local; + + // See the message below for why this is not emitted to the + // ^ Where did the message below go? + // task's logger. This has the additional conundrum of the + // logger may not be initialized just yet, meaning that an FFI + // call would happen to initialized it (calling out to libuv), + // and the FFI call needs 2MB of stack when we just ran out. + + let task: Option<*mut Task> = Local::try_unsafe_borrow(); + + let name = task.and_then(|task| { + (*task).name.as_ref().map(|n| n.as_slice()) + }); + + rterrln!("\ntask '{}' has overflowed its stack", name.unwrap_or("")); +} diff --git a/src/libstd/rtdeps.rs b/src/libstd/rtdeps.rs index 35a8713711591..862808a9e3d90 100644 --- a/src/libstd/rtdeps.rs +++ b/src/libstd/rtdeps.rs @@ -22,7 +22,7 @@ extern {} // LLVM implements the `frem` instruction as a call to `fmod`, which lives in // libm. Hence, we must explicitly link to it. // -// On Linux, librt and libdl are indirect dependencies via rustrt, +// On Linux, librt and libdl are indirect dependencies via std, // and binutils 2.22+ won't add them automatically #[cfg(target_os = "linux")] #[link(name = "dl")] diff --git a/src/libstd/sys/common/backtrace.rs b/src/libstd/sys/common/backtrace.rs new file mode 100644 index 0000000000000..0c03060b3142d --- /dev/null +++ b/src/libstd/sys/common/backtrace.rs @@ -0,0 +1,131 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use io::{IoResult, Writer}; +use iter::Iterator; +use option::{Some, None}; +use result::{Ok, Err}; +use str::{StrPrelude, from_str}; +use unicode::char::UnicodeChar; + +#[cfg(target_word_size = "64")] pub const HEX_WIDTH: uint = 18; +#[cfg(target_word_size = "32")] pub const HEX_WIDTH: uint = 10; + +// All rust symbols are in theory lists of "::"-separated identifiers. Some +// assemblers, however, can't handle these characters in symbol names. To get +// around this, we use C++-style mangling. The mangling method is: +// +// 1. Prefix the symbol with "_ZN" +// 2. For each element of the path, emit the length plus the element +// 3. End the path with "E" +// +// For example, "_ZN4testE" => "test" and "_ZN3foo3bar" => "foo::bar". +// +// We're the ones printing our backtraces, so we can't rely on anything else to +// demangle our symbols. It's *much* nicer to look at demangled symbols, so +// this function is implemented to give us nice pretty output. +// +// Note that this demangler isn't quite as fancy as it could be. We have lots +// of other information in our symbols like hashes, version, type information, +// etc. Additionally, this doesn't handle glue symbols at all. +pub fn demangle(writer: &mut Writer, s: &str) -> IoResult<()> { + // First validate the symbol. If it doesn't look like anything we're + // expecting, we just print it literally. Note that we must handle non-rust + // symbols because we could have any function in the backtrace. + let mut valid = true; + if s.len() > 4 && s.starts_with("_ZN") && s.ends_with("E") { + let mut chars = s.slice(3, s.len() - 1).chars(); + while valid { + let mut i = 0; + for c in chars { + if c.is_numeric() { + i = i * 10 + c as uint - '0' as uint; + } else { + break + } + } + if i == 0 { + valid = chars.next().is_none(); + break + } else if chars.by_ref().take(i - 1).count() != i - 1 { + valid = false; + } + } + } else { + valid = false; + } + + // Alright, let's do this. + if !valid { + try!(writer.write_str(s)); + } else { + let mut s = s.slice_from(3); + let mut first = true; + while s.len() > 1 { + if !first { + try!(writer.write_str("::")); + } else { + first = false; + } + let mut rest = s; + while rest.char_at(0).is_numeric() { + rest = rest.slice_from(1); + } + let i: uint = from_str(s.slice_to(s.len() - rest.len())).unwrap(); + s = rest.slice_from(i); + rest = rest.slice_to(i); + while rest.len() > 0 { + if rest.starts_with("$") { + macro_rules! demangle( + ($($pat:expr => $demangled:expr),*) => ({ + $(if rest.starts_with($pat) { + try!(writer.write_str($demangled)); + rest = rest.slice_from($pat.len()); + } else)* + { + try!(writer.write_str(rest)); + break; + } + + }) + ) + // see src/librustc/back/link.rs for these mappings + demangle! ( + "$SP$" => "@", + "$UP$" => "Box", + "$RP$" => "*", + "$BP$" => "&", + "$LT$" => "<", + "$GT$" => ">", + "$LP$" => "(", + "$RP$" => ")", + "$C$" => ",", + + // in theory we can demangle any Unicode code point, but + // for simplicity we just catch the common ones. + "$x20" => " ", + "$x27" => "'", + "$x5b" => "[", + "$x5d" => "]" + ) + } else { + let idx = match rest.find('$') { + None => rest.len(), + Some(i) => i, + }; + try!(writer.write_str(rest.slice_to(idx))); + rest = rest.slice_from(idx); + } + } + } + } + + Ok(()) +} diff --git a/src/libstd/sys/common/helper_thread.rs b/src/libstd/sys/common/helper_thread.rs index 96b4accd4bd03..ffb053e852efc 100644 --- a/src/libstd/sys/common/helper_thread.rs +++ b/src/libstd/sys/common/helper_thread.rs @@ -24,9 +24,8 @@ use prelude::*; use cell::UnsafeCell; use mem; -use rustrt::bookkeeping; -use rustrt; use sync::{StaticMutex, StaticCondvar}; +use rt::{mod, bookkeeping}; use sys::helper_signal; use task; @@ -91,7 +90,7 @@ impl Helper { self.cond.notify_one() }); - rustrt::at_exit(move|:| { self.shutdown() }); + rt::at_exit(move|:| { self.shutdown() }); *self.initialized.get() = true; } } diff --git a/src/libstd/sys/common/mod.rs b/src/libstd/sys/common/mod.rs index 73e1c7bd9e5e0..aeee4cf01cdd0 100644 --- a/src/libstd/sys/common/mod.rs +++ b/src/libstd/sys/common/mod.rs @@ -19,11 +19,14 @@ use num::Int; use path::BytesContainer; use collections; +pub mod backtrace; pub mod condvar; pub mod helper_thread; pub mod mutex; pub mod net; pub mod rwlock; +pub mod stack; +pub mod thread; pub mod thread_local; // common error constructors diff --git a/src/librustrt/stack.rs b/src/libstd/sys/common/stack.rs similarity index 99% rename from src/librustrt/stack.rs rename to src/libstd/sys/common/stack.rs index 4874f642a93af..2a88e20c8fa1f 100644 --- a/src/librustrt/stack.rs +++ b/src/libstd/sys/common/stack.rs @@ -55,7 +55,7 @@ pub const RED_ZONE: uint = 20 * 1024; #[cfg(not(test))] // in testing, use the original libstd's version #[lang = "stack_exhausted"] extern fn stack_exhausted() { - use core::intrinsics; + use intrinsics; unsafe { // We're calling this function because the stack just ran out. We need @@ -100,7 +100,7 @@ extern fn stack_exhausted() { // #9854 - unwinding on windows through __morestack has never worked // #2361 - possible implementation of not using landing pads - ::stack_overflow::report(); + ::rt::util::report_overflow(); intrinsics::abort(); } diff --git a/src/libstd/sys/common/thread.rs b/src/libstd/sys/common/thread.rs new file mode 100644 index 0000000000000..5e1adfb87149f --- /dev/null +++ b/src/libstd/sys/common/thread.rs @@ -0,0 +1,34 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use core::prelude::*; + +use boxed::Box; +use mem; +use uint; +use libc; +use sys_common::stack; +use sys::{thread, stack_overflow}; + +// This is the starting point of rust os threads. The first thing we do +// is make sure that we don't trigger __morestack (also why this has a +// no_stack_check annotation), and then we extract the main function +// and invoke it. +#[no_stack_check] +pub fn start_thread(main: *mut libc::c_void) -> thread::rust_thread_return { + unsafe { + stack::record_os_managed_stack_bounds(0, uint::MAX); + let handler = stack_overflow::Handler::new(); + let f: Box = mem::transmute(main); + (*f)(); + drop(handler); + mem::transmute(0 as thread::rust_thread_return) + } +} diff --git a/src/libstd/sys/common/thread_local.rs b/src/libstd/sys/common/thread_local.rs index cf56a71d67acd..a8bc6bf9d0d6a 100644 --- a/src/libstd/sys/common/thread_local.rs +++ b/src/libstd/sys/common/thread_local.rs @@ -58,7 +58,8 @@ use prelude::*; -use rustrt::exclusive::Exclusive; +use rt::exclusive::Exclusive; +use rt; use sync::atomic::{mod, AtomicUint}; use sync::{Once, ONCE_INIT}; @@ -283,4 +284,3 @@ mod tests { } } } - diff --git a/src/libstd/sys/unix/backtrace.rs b/src/libstd/sys/unix/backtrace.rs new file mode 100644 index 0000000000000..c139dba2c4608 --- /dev/null +++ b/src/libstd/sys/unix/backtrace.rs @@ -0,0 +1,493 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/// Backtrace support built on libgcc with some extra OS-specific support +/// +/// Some methods of getting a backtrace: +/// +/// * The backtrace() functions on unix. It turns out this doesn't work very +/// well for green threads on OSX, and the address to symbol portion of it +/// suffers problems that are described below. +/// +/// * Using libunwind. This is more difficult than it sounds because libunwind +/// isn't installed everywhere by default. It's also a bit of a hefty library, +/// so possibly not the best option. When testing, libunwind was excellent at +/// getting both accurate backtraces and accurate symbols across platforms. +/// This route was not chosen in favor of the next option, however. +/// +/// * We're already using libgcc_s for exceptions in rust (triggering task +/// unwinding and running destructors on the stack), and it turns out that it +/// conveniently comes with a function that also gives us a backtrace. All of +/// these functions look like _Unwind_*, but it's not quite the full +/// repertoire of the libunwind API. Due to it already being in use, this was +/// the chosen route of getting a backtrace. +/// +/// After choosing libgcc_s for backtraces, the sad part is that it will only +/// give us a stack trace of instruction pointers. Thankfully these instruction +/// pointers are accurate (they work for green and native threads), but it's +/// then up to us again to figure out how to translate these addresses to +/// symbols. As with before, we have a few options. Before, that, a little bit +/// of an interlude about symbols. This is my very limited knowledge about +/// symbol tables, and this information is likely slightly wrong, but the +/// general idea should be correct. +/// +/// When talking about symbols, it's helpful to know a few things about where +/// symbols are located. Some symbols are located in the dynamic symbol table +/// of the executable which in theory means that they're available for dynamic +/// linking and lookup. Other symbols end up only in the local symbol table of +/// the file. This loosely corresponds to pub and priv functions in Rust. +/// +/// Armed with this knowledge, we know that our solution for address to symbol +/// translation will need to consult both the local and dynamic symbol tables. +/// With that in mind, here's our options of translating an address to +/// a symbol. +/// +/// * Use dladdr(). The original backtrace()-based idea actually uses dladdr() +/// behind the scenes to translate, and this is why backtrace() was not used. +/// Conveniently, this method works fantastically on OSX. It appears dladdr() +/// uses magic to consult the local symbol table, or we're putting everything +/// in the dynamic symbol table anyway. Regardless, for OSX, this is the +/// method used for translation. It's provided by the system and easy to do.o +/// +/// Sadly, all other systems have a dladdr() implementation that does not +/// consult the local symbol table. This means that most functions are blank +/// because they don't have symbols. This means that we need another solution. +/// +/// * Use unw_get_proc_name(). This is part of the libunwind api (not the +/// libgcc_s version of the libunwind api), but involves taking a dependency +/// to libunwind. We may pursue this route in the future if we bundle +/// libunwind, but libunwind was unwieldy enough that it was not chosen at +/// this time to provide this functionality. +/// +/// * Shell out to a utility like `readelf`. Crazy though it may sound, it's a +/// semi-reasonable solution. The stdlib already knows how to spawn processes, +/// so in theory it could invoke readelf, parse the output, and consult the +/// local/dynamic symbol tables from there. This ended up not getting chosen +/// due to the craziness of the idea plus the advent of the next option. +/// +/// * Use `libbacktrace`. It turns out that this is a small library bundled in +/// the gcc repository which provides backtrace and symbol translation +/// functionality. All we really need from it is the backtrace functionality, +/// and we only really need this on everything that's not OSX, so this is the +/// chosen route for now. +/// +/// In summary, the current situation uses libgcc_s to get a trace of stack +/// pointers, and we use dladdr() or libbacktrace to translate these addresses +/// to symbols. This is a bit of a hokey implementation as-is, but it works for +/// all unix platforms we support right now, so it at least gets the job done. + +use c_str::CString; +use io::{IoResult, Writer}; +use libc; +use mem; +use option::{Some, None, Option}; +use result::{Ok, Err}; +use rt::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT}; + +use sys_common::backtrace::*; + +/// As always - iOS on arm uses SjLj exceptions and +/// _Unwind_Backtrace is even not available there. Still, +/// backtraces could be extracted using a backtrace function, +/// which thanks god is public +/// +/// As mentioned in a huge comment block above, backtrace doesn't +/// play well with green threads, so while it is extremely nice +/// and simple to use it should be used only on iOS devices as the +/// only viable option. +#[cfg(all(target_os = "ios", target_arch = "arm"))] +#[inline(never)] +pub fn write(w: &mut Writer) -> IoResult<()> { + use iter::{Iterator, range}; + use result; + use slice::SliceExt; + + extern { + fn backtrace(buf: *mut *mut libc::c_void, + sz: libc::c_int) -> libc::c_int; + } + + // while it doesn't requires lock for work as everything is + // local, it still displays much nicer backtraces when a + // couple of tasks panic simultaneously + static LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT; + let _g = unsafe { LOCK.lock() }; + + try!(writeln!(w, "stack backtrace:")); + // 100 lines should be enough + const SIZE: uint = 100; + let mut buf: [*mut libc::c_void, ..SIZE] = unsafe {mem::zeroed()}; + let cnt = unsafe { backtrace(buf.as_mut_ptr(), SIZE as libc::c_int) as uint}; + + // skipping the first one as it is write itself + let iter = range(1, cnt).map(|i| { + print(w, i as int, buf[i]) + }); + result::fold(iter, (), |_, _| ()) +} + +#[cfg(not(all(target_os = "ios", target_arch = "arm")))] +#[inline(never)] // if we know this is a function call, we can skip it when + // tracing +pub fn write(w: &mut Writer) -> IoResult<()> { + use io::IoError; + + struct Context<'a> { + idx: int, + writer: &'a mut Writer+'a, + last_error: Option, + } + + // When using libbacktrace, we use some necessary global state, so we + // need to prevent more than one thread from entering this block. This + // is semi-reasonable in terms of printing anyway, and we know that all + // I/O done here is blocking I/O, not green I/O, so we don't have to + // worry about this being a native vs green mutex. + static LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT; + let _g = unsafe { LOCK.lock() }; + + try!(writeln!(w, "stack backtrace:")); + + let mut cx = Context { writer: w, last_error: None, idx: 0 }; + return match unsafe { + uw::_Unwind_Backtrace(trace_fn, + &mut cx as *mut Context as *mut libc::c_void) + } { + uw::_URC_NO_REASON => { + match cx.last_error { + Some(err) => Err(err), + None => Ok(()) + } + } + _ => Ok(()), + }; + + extern fn trace_fn(ctx: *mut uw::_Unwind_Context, + arg: *mut libc::c_void) -> uw::_Unwind_Reason_Code { + let cx: &mut Context = unsafe { mem::transmute(arg) }; + let ip = unsafe { uw::_Unwind_GetIP(ctx) as *mut libc::c_void }; + // dladdr() on osx gets whiny when we use FindEnclosingFunction, and + // it appears to work fine without it, so we only use + // FindEnclosingFunction on non-osx platforms. In doing so, we get a + // slightly more accurate stack trace in the process. + // + // This is often because panic involves the last instruction of a + // function being "call std::rt::begin_unwind", with no ret + // instructions after it. This means that the return instruction + // pointer points *outside* of the calling function, and by + // unwinding it we go back to the original function. + let ip = if cfg!(target_os = "macos") || cfg!(target_os = "ios") { + ip + } else { + unsafe { uw::_Unwind_FindEnclosingFunction(ip) } + }; + + // Don't print out the first few frames (they're not user frames) + cx.idx += 1; + if cx.idx <= 0 { return uw::_URC_NO_REASON } + // Don't print ginormous backtraces + if cx.idx > 100 { + match write!(cx.writer, " ... \n") { + Ok(()) => {} + Err(e) => { cx.last_error = Some(e); } + } + return uw::_URC_FAILURE + } + + // Once we hit an error, stop trying to print more frames + if cx.last_error.is_some() { return uw::_URC_FAILURE } + + match print(cx.writer, cx.idx, ip) { + Ok(()) => {} + Err(e) => { cx.last_error = Some(e); } + } + + // keep going + return uw::_URC_NO_REASON + } +} + +#[cfg(any(target_os = "macos", target_os = "ios"))] +fn print(w: &mut Writer, idx: int, addr: *mut libc::c_void) -> IoResult<()> { + use intrinsics; + #[repr(C)] + struct Dl_info { + dli_fname: *const libc::c_char, + dli_fbase: *mut libc::c_void, + dli_sname: *const libc::c_char, + dli_saddr: *mut libc::c_void, + } + extern { + fn dladdr(addr: *const libc::c_void, + info: *mut Dl_info) -> libc::c_int; + } + + let mut info: Dl_info = unsafe { intrinsics::init() }; + if unsafe { dladdr(addr as *const libc::c_void, &mut info) == 0 } { + output(w, idx,addr, None) + } else { + output(w, idx, addr, Some(unsafe { + CString::new(info.dli_sname, false) + })) + } +} + +#[cfg(not(any(target_os = "macos", target_os = "ios")))] +fn print(w: &mut Writer, idx: int, addr: *mut libc::c_void) -> IoResult<()> { + use iter::Iterator; + use os; + use path::GenericPath; + use ptr::RawPtr; + use ptr; + use slice::SliceExt; + + //////////////////////////////////////////////////////////////////////// + // libbacktrace.h API + //////////////////////////////////////////////////////////////////////// + type backtrace_syminfo_callback = + extern "C" fn(data: *mut libc::c_void, + pc: libc::uintptr_t, + symname: *const libc::c_char, + symval: libc::uintptr_t, + symsize: libc::uintptr_t); + type backtrace_error_callback = + extern "C" fn(data: *mut libc::c_void, + msg: *const libc::c_char, + errnum: libc::c_int); + enum backtrace_state {} + #[link(name = "backtrace", kind = "static")] + #[cfg(not(test))] + extern {} + + extern { + fn backtrace_create_state(filename: *const libc::c_char, + threaded: libc::c_int, + error: backtrace_error_callback, + data: *mut libc::c_void) + -> *mut backtrace_state; + fn backtrace_syminfo(state: *mut backtrace_state, + addr: libc::uintptr_t, + cb: backtrace_syminfo_callback, + error: backtrace_error_callback, + data: *mut libc::c_void) -> libc::c_int; + } + + //////////////////////////////////////////////////////////////////////// + // helper callbacks + //////////////////////////////////////////////////////////////////////// + + extern fn error_cb(_data: *mut libc::c_void, _msg: *const libc::c_char, + _errnum: libc::c_int) { + // do nothing for now + } + extern fn syminfo_cb(data: *mut libc::c_void, + _pc: libc::uintptr_t, + symname: *const libc::c_char, + _symval: libc::uintptr_t, + _symsize: libc::uintptr_t) { + let slot = data as *mut *const libc::c_char; + unsafe { *slot = symname; } + } + + // The libbacktrace API supports creating a state, but it does not + // support destroying a state. I personally take this to mean that a + // state is meant to be created and then live forever. + // + // I would love to register an at_exit() handler which cleans up this + // state, but libbacktrace provides no way to do so. + // + // With these constraints, this function has a statically cached state + // that is calculated the first time this is requested. Remember that + // backtracing all happens serially (one global lock). + // + // An additionally oddity in this function is that we initialize the + // filename via self_exe_name() to pass to libbacktrace. It turns out + // that on Linux libbacktrace seamlessly gets the filename of the + // current executable, but this fails on freebsd. by always providing + // it, we make sure that libbacktrace never has a reason to not look up + // the symbols. The libbacktrace API also states that the filename must + // be in "permanent memory", so we copy it to a static and then use the + // static as the pointer. + // + // FIXME: We also call self_exe_name() on DragonFly BSD. I haven't + // tested if this is required or not. + unsafe fn init_state() -> *mut backtrace_state { + static mut STATE: *mut backtrace_state = 0 as *mut backtrace_state; + static mut LAST_FILENAME: [libc::c_char, ..256] = [0, ..256]; + if !STATE.is_null() { return STATE } + let selfname = if cfg!(target_os = "freebsd") || + cfg!(target_os = "dragonfly") { + os::self_exe_name() + } else { + None + }; + let filename = match selfname { + Some(path) => { + let bytes = path.as_vec(); + if bytes.len() < LAST_FILENAME.len() { + let i = bytes.iter(); + for (slot, val) in LAST_FILENAME.iter_mut().zip(i) { + *slot = *val as libc::c_char; + } + LAST_FILENAME.as_ptr() + } else { + ptr::null() + } + } + None => ptr::null(), + }; + STATE = backtrace_create_state(filename, 0, error_cb, + ptr::null_mut()); + return STATE + } + + //////////////////////////////////////////////////////////////////////// + // translation + //////////////////////////////////////////////////////////////////////// + + // backtrace errors are currently swept under the rug, only I/O + // errors are reported + let state = unsafe { init_state() }; + if state.is_null() { + return output(w, idx, addr, None) + } + let mut data = 0 as *const libc::c_char; + let data_addr = &mut data as *mut *const libc::c_char; + let ret = unsafe { + backtrace_syminfo(state, addr as libc::uintptr_t, + syminfo_cb, error_cb, + data_addr as *mut libc::c_void) + }; + if ret == 0 || data.is_null() { + output(w, idx, addr, None) + } else { + output(w, idx, addr, Some(unsafe { CString::new(data, false) })) + } +} + +// Finally, after all that work above, we can emit a symbol. +fn output(w: &mut Writer, idx: int, addr: *mut libc::c_void, + s: Option) -> IoResult<()> { + try!(write!(w, " {:2}: {:2$} - ", idx, addr, HEX_WIDTH)); + match s.as_ref().and_then(|c| c.as_str()) { + Some(string) => try!(demangle(w, string)), + None => try!(write!(w, "")), + } + w.write(&['\n' as u8]) +} + +/// Unwind library interface used for backtraces +/// +/// Note that dead code is allowed as here are just bindings +/// iOS doesn't use all of them it but adding more +/// platform-specific configs pollutes the code too much +#[allow(non_camel_case_types)] +#[allow(non_snake_case)] +#[allow(dead_code)] +mod uw { + pub use self::_Unwind_Reason_Code::*; + + use libc; + + #[repr(C)] + pub enum _Unwind_Reason_Code { + _URC_NO_REASON = 0, + _URC_FOREIGN_EXCEPTION_CAUGHT = 1, + _URC_FATAL_PHASE2_ERROR = 2, + _URC_FATAL_PHASE1_ERROR = 3, + _URC_NORMAL_STOP = 4, + _URC_END_OF_STACK = 5, + _URC_HANDLER_FOUND = 6, + _URC_INSTALL_CONTEXT = 7, + _URC_CONTINUE_UNWIND = 8, + _URC_FAILURE = 9, // used only by ARM EABI + } + + pub enum _Unwind_Context {} + + pub type _Unwind_Trace_Fn = + extern fn(ctx: *mut _Unwind_Context, + arg: *mut libc::c_void) -> _Unwind_Reason_Code; + + extern { + // No native _Unwind_Backtrace on iOS + #[cfg(not(all(target_os = "ios", target_arch = "arm")))] + pub fn _Unwind_Backtrace(trace: _Unwind_Trace_Fn, + trace_argument: *mut libc::c_void) + -> _Unwind_Reason_Code; + + #[cfg(all(not(target_os = "android"), + not(all(target_os = "linux", target_arch = "arm"))))] + pub fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t; + + #[cfg(all(not(target_os = "android"), + not(all(target_os = "linux", target_arch = "arm"))))] + pub fn _Unwind_FindEnclosingFunction(pc: *mut libc::c_void) + -> *mut libc::c_void; + } + + // On android, the function _Unwind_GetIP is a macro, and this is the + // expansion of the macro. This is all copy/pasted directly from the + // header file with the definition of _Unwind_GetIP. + #[cfg(any(target_os = "android", + all(target_os = "linux", target_arch = "arm")))] + pub unsafe fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t { + #[repr(C)] + enum _Unwind_VRS_Result { + _UVRSR_OK = 0, + _UVRSR_NOT_IMPLEMENTED = 1, + _UVRSR_FAILED = 2, + } + #[repr(C)] + enum _Unwind_VRS_RegClass { + _UVRSC_CORE = 0, + _UVRSC_VFP = 1, + _UVRSC_FPA = 2, + _UVRSC_WMMXD = 3, + _UVRSC_WMMXC = 4, + } + #[repr(C)] + enum _Unwind_VRS_DataRepresentation { + _UVRSD_UINT32 = 0, + _UVRSD_VFPX = 1, + _UVRSD_FPAX = 2, + _UVRSD_UINT64 = 3, + _UVRSD_FLOAT = 4, + _UVRSD_DOUBLE = 5, + } + + type _Unwind_Word = libc::c_uint; + extern { + fn _Unwind_VRS_Get(ctx: *mut _Unwind_Context, + klass: _Unwind_VRS_RegClass, + word: _Unwind_Word, + repr: _Unwind_VRS_DataRepresentation, + data: *mut libc::c_void) + -> _Unwind_VRS_Result; + } + + let mut val: _Unwind_Word = 0; + let ptr = &mut val as *mut _Unwind_Word; + let _ = _Unwind_VRS_Get(ctx, _Unwind_VRS_RegClass::_UVRSC_CORE, 15, + _Unwind_VRS_DataRepresentation::_UVRSD_UINT32, + ptr as *mut libc::c_void); + (val & !1) as libc::uintptr_t + } + + // This function also doesn't exist on Android or ARM/Linux, so make it + // a no-op + #[cfg(any(target_os = "android", + all(target_os = "linux", target_arch = "arm")))] + pub unsafe fn _Unwind_FindEnclosingFunction(pc: *mut libc::c_void) + -> *mut libc::c_void + { + pc + } +} diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs index acbf209632630..f3babca32871a 100644 --- a/src/libstd/sys/unix/mod.rs +++ b/src/libstd/sys/unix/mod.rs @@ -34,6 +34,7 @@ macro_rules! helper_init { (static $name:ident: Helper<$m:ty>) => ( }; ) } +pub mod backtrace; pub mod c; pub mod ext; pub mod condvar; @@ -44,8 +45,10 @@ pub mod os; pub mod pipe; pub mod process; pub mod rwlock; +pub mod stack_overflow; pub mod sync; pub mod tcp; +pub mod thread; pub mod thread_local; pub mod timer; pub mod tty; diff --git a/src/librustrt/stack_overflow.rs b/src/libstd/sys/unix/stack_overflow.rs similarity index 64% rename from src/librustrt/stack_overflow.rs rename to src/libstd/sys/unix/stack_overflow.rs index 05f9f6287c404..73b98f762b479 100644 --- a/src/librustrt/stack_overflow.rs +++ b/src/libstd/sys/unix/stack_overflow.rs @@ -8,20 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![allow(non_camel_case_types)] - -use core::prelude::*; use libc; -use local::Local; -use task::Task; - -pub unsafe fn init() { - imp::init(); -} +use core::prelude::*; +use self::imp::{make_handler, drop_handler}; -pub unsafe fn cleanup() { - imp::cleanup(); -} +pub use self::imp::{init, cleanup}; pub struct Handler { _data: *mut libc::c_void @@ -29,149 +20,33 @@ pub struct Handler { impl Handler { pub unsafe fn new() -> Handler { - imp::make_handler() + make_handler() } } impl Drop for Handler { fn drop(&mut self) { unsafe { - imp::drop_handler(self); - } - } -} - -pub unsafe fn report() { - // See the message below for why this is not emitted to the - // ^ Where did the message below go? - // task's logger. This has the additional conundrum of the - // logger may not be initialized just yet, meaning that an FFI - // call would happen to initialized it (calling out to libuv), - // and the FFI call needs 2MB of stack when we just ran out. - - let task: Option<*mut Task> = Local::try_unsafe_borrow(); - - let name = task.and_then(|task| { - (*task).name.as_ref().map(|n| n.as_slice()) - }); - - rterrln!("\ntask '{}' has overflowed its stack", name.unwrap_or("")); -} - -// get_task_info is called from an exception / signal handler. -// It returns the guard page of the current task or 0 if that -// guard page doesn't exist. None is returned if there's currently -// no local task. -#[cfg(any(windows, target_os = "linux", target_os = "macos"))] -unsafe fn get_task_guard_page() -> Option { - let task: Option<*mut Task> = Local::try_unsafe_borrow(); - task.map(|task| (&*task).stack_guard().unwrap_or(0)) -} - -#[cfg(windows)] -#[allow(non_snake_case)] -mod imp { - use core::ptr; - use core::mem; - use libc; - use libc::types::os::arch::extra::{LPVOID, DWORD, LONG, BOOL}; - use stack; - use super::{Handler, get_task_guard_page, report}; - - // This is initialized in init() and only read from after - static mut PAGE_SIZE: uint = 0; - - #[no_stack_check] - extern "system" fn vectored_handler(ExceptionInfo: *mut EXCEPTION_POINTERS) -> LONG { - unsafe { - let rec = &(*(*ExceptionInfo).ExceptionRecord); - let code = rec.ExceptionCode; - - if code != EXCEPTION_STACK_OVERFLOW { - return EXCEPTION_CONTINUE_SEARCH; - } - - // We're calling into functions with stack checks, - // however stack checks by limit should be disabled on Windows - stack::record_sp_limit(0); - - if get_task_guard_page().is_some() { - report(); - } - - EXCEPTION_CONTINUE_SEARCH - } - } - - pub unsafe fn init() { - let mut info = mem::zeroed(); - libc::GetSystemInfo(&mut info); - PAGE_SIZE = info.dwPageSize as uint; - - if AddVectoredExceptionHandler(0, vectored_handler) == ptr::null_mut() { - panic!("failed to install exception handler"); + drop_handler(self); } - - mem::forget(make_handler()); - } - - pub unsafe fn cleanup() { - } - - pub unsafe fn make_handler() -> Handler { - if SetThreadStackGuarantee(&mut 0x5000) == 0 { - panic!("failed to reserve stack space for exception handling"); - } - - super::Handler { _data: 0i as *mut libc::c_void } - } - - pub unsafe fn drop_handler(_handler: &mut Handler) { - } - - pub struct EXCEPTION_RECORD { - pub ExceptionCode: DWORD, - pub ExceptionFlags: DWORD, - pub ExceptionRecord: *mut EXCEPTION_RECORD, - pub ExceptionAddress: LPVOID, - pub NumberParameters: DWORD, - pub ExceptionInformation: [LPVOID, ..EXCEPTION_MAXIMUM_PARAMETERS] - } - - pub struct EXCEPTION_POINTERS { - pub ExceptionRecord: *mut EXCEPTION_RECORD, - pub ContextRecord: LPVOID - } - - pub type PVECTORED_EXCEPTION_HANDLER = extern "system" - fn(ExceptionInfo: *mut EXCEPTION_POINTERS) -> LONG; - - pub type ULONG = libc::c_ulong; - - const EXCEPTION_CONTINUE_SEARCH: LONG = 0; - const EXCEPTION_MAXIMUM_PARAMETERS: uint = 15; - const EXCEPTION_STACK_OVERFLOW: DWORD = 0xc00000fd; - - extern "system" { - fn AddVectoredExceptionHandler(FirstHandler: ULONG, - VectoredHandler: PVECTORED_EXCEPTION_HANDLER) - -> LPVOID; - fn SetThreadStackGuarantee(StackSizeInBytes: *mut ULONG) -> BOOL; } } #[cfg(any(target_os = "linux", target_os = "macos"))] mod imp { use core::prelude::*; - use stack; + use sys_common::stack; - use super::{Handler, get_task_guard_page, report}; - use core::mem; - use core::ptr; - use core::intrinsics; + use super::Handler; + use rt::util::report_overflow; + use mem; + use ptr; + use intrinsics; use self::signal::{siginfo, sigaction, SIGBUS, SIG_DFL, SA_SIGINFO, SA_ONSTACK, sigaltstack, SIGSTKSZ}; + use rt::local::Local; + use rt::task::Task; use libc; use libc::funcs::posix88::mman::{mmap, munmap}; use libc::consts::os::posix88::{SIGSEGV, @@ -185,6 +60,16 @@ mod imp { // This is initialized in init() and only read from after static mut PAGE_SIZE: uint = 0; + // get_task_info is called from an exception / signal handler. + // It returns the guard page of the current task or 0 if that + // guard page doesn't exist. None is returned if there's currently + // no local task. + unsafe fn get_task_guard_page() -> Option { + let task: Option<*mut Task> = Local::try_unsafe_borrow(); + task.map(|task| (&*task).stack_guard().unwrap_or(0)) + } + + #[no_stack_check] unsafe extern fn signal_handler(signum: libc::c_int, info: *mut siginfo, @@ -212,7 +97,7 @@ mod imp { term(signum); } - report(); + report_overflow(); intrinsics::abort() } @@ -387,8 +272,7 @@ mod imp { } #[cfg(not(any(target_os = "linux", - target_os = "macos", - windows)))] + target_os = "macos")))] mod imp { use libc; diff --git a/src/libstd/sys/unix/thread.rs b/src/libstd/sys/unix/thread.rs new file mode 100644 index 0000000000000..02da3a1981837 --- /dev/null +++ b/src/libstd/sys/unix/thread.rs @@ -0,0 +1,270 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use core::prelude::*; + +use boxed::Box; +use cmp; +use mem; +use ptr; +use libc::consts::os::posix01::{PTHREAD_CREATE_JOINABLE, PTHREAD_STACK_MIN}; +use libc; + +use sys_common::stack::RED_ZONE; +use sys_common::thread::*; + +pub type rust_thread = libc::pthread_t; +pub type rust_thread_return = *mut u8; +pub type StartFn = extern "C" fn(*mut libc::c_void) -> rust_thread_return; + +#[no_stack_check] +pub extern fn thread_start(main: *mut libc::c_void) -> rust_thread_return { + return start_thread(main); +} + +#[cfg(all(not(target_os = "linux"), not(target_os = "macos")))] +pub mod guard { + pub unsafe fn current() -> uint { + 0 + } + + pub unsafe fn main() -> uint { + 0 + } + + pub unsafe fn init() { + } +} + +#[cfg(any(target_os = "linux", target_os = "macos"))] +pub mod guard { + use super::*; + #[cfg(any(target_os = "linux", target_os = "android"))] + use mem; + #[cfg(any(target_os = "linux", target_os = "android"))] + use ptr; + use libc; + use libc::funcs::posix88::mman::{mmap}; + use libc::consts::os::posix88::{PROT_NONE, + MAP_PRIVATE, + MAP_ANON, + MAP_FAILED, + MAP_FIXED}; + + // These are initialized in init() and only read from after + static mut PAGE_SIZE: uint = 0; + static mut GUARD_PAGE: uint = 0; + + #[cfg(target_os = "macos")] + unsafe fn get_stack_start() -> *mut libc::c_void { + current() as *mut libc::c_void + } + + #[cfg(any(target_os = "linux", target_os = "android"))] + unsafe fn get_stack_start() -> *mut libc::c_void { + let mut attr: libc::pthread_attr_t = mem::zeroed(); + if pthread_getattr_np(pthread_self(), &mut attr) != 0 { + panic!("failed to get thread attributes"); + } + let mut stackaddr = ptr::null_mut(); + let mut stacksize = 0; + if pthread_attr_getstack(&attr, &mut stackaddr, &mut stacksize) != 0 { + panic!("failed to get stack information"); + } + if pthread_attr_destroy(&mut attr) != 0 { + panic!("failed to destroy thread attributes"); + } + stackaddr + } + + pub unsafe fn init() { + let psize = libc::sysconf(libc::consts::os::sysconf::_SC_PAGESIZE); + if psize == -1 { + panic!("failed to get page size"); + } + + PAGE_SIZE = psize as uint; + + let stackaddr = get_stack_start(); + + // Rellocate the last page of the stack. + // This ensures SIGBUS will be raised on + // stack overflow. + let result = mmap(stackaddr, + PAGE_SIZE as libc::size_t, + PROT_NONE, + MAP_PRIVATE | MAP_ANON | MAP_FIXED, + -1, + 0); + + if result != stackaddr || result == MAP_FAILED { + panic!("failed to allocate a guard page"); + } + + let offset = if cfg!(target_os = "linux") { + 2 + } else { + 1 + }; + + GUARD_PAGE = stackaddr as uint + offset * PAGE_SIZE; + } + + pub unsafe fn main() -> uint { + GUARD_PAGE + } + + #[cfg(target_os = "macos")] + pub unsafe fn current() -> uint { + (pthread_get_stackaddr_np(pthread_self()) as libc::size_t - + pthread_get_stacksize_np(pthread_self())) as uint + } + + #[cfg(any(target_os = "linux", target_os = "android"))] + pub unsafe fn current() -> uint { + let mut attr: libc::pthread_attr_t = mem::zeroed(); + if pthread_getattr_np(pthread_self(), &mut attr) != 0 { + panic!("failed to get thread attributes"); + } + let mut guardsize = 0; + if pthread_attr_getguardsize(&attr, &mut guardsize) != 0 { + panic!("failed to get stack guard page"); + } + if guardsize == 0 { + panic!("there is no guard page"); + } + let mut stackaddr = ptr::null_mut(); + let mut stacksize = 0; + if pthread_attr_getstack(&attr, &mut stackaddr, &mut stacksize) != 0 { + panic!("failed to get stack information"); + } + if pthread_attr_destroy(&mut attr) != 0 { + panic!("failed to destroy thread attributes"); + } + + stackaddr as uint + guardsize as uint + } +} + +pub unsafe fn create(stack: uint, p: Box) -> rust_thread { + let mut native: libc::pthread_t = mem::zeroed(); + let mut attr: libc::pthread_attr_t = mem::zeroed(); + assert_eq!(pthread_attr_init(&mut attr), 0); + assert_eq!(pthread_attr_setdetachstate(&mut attr, + PTHREAD_CREATE_JOINABLE), 0); + + // Reserve room for the red zone, the runtime's stack of last resort. + let stack_size = cmp::max(stack, RED_ZONE + min_stack_size(&attr) as uint); + match pthread_attr_setstacksize(&mut attr, stack_size as libc::size_t) { + 0 => { + }, + libc::EINVAL => { + // EINVAL means |stack_size| is either too small or not a + // multiple of the system page size. Because it's definitely + // >= PTHREAD_STACK_MIN, it must be an alignment issue. + // Round up to the nearest page and try again. + let page_size = libc::sysconf(libc::_SC_PAGESIZE) as uint; + let stack_size = (stack_size + page_size - 1) & + (-(page_size as int - 1) as uint - 1); + assert_eq!(pthread_attr_setstacksize(&mut attr, stack_size as libc::size_t), 0); + }, + errno => { + // This cannot really happen. + panic!("pthread_attr_setstacksize() error: {}", errno); + }, + }; + + let arg: *mut libc::c_void = mem::transmute(p); + let ret = pthread_create(&mut native, &attr, thread_start, arg); + assert_eq!(pthread_attr_destroy(&mut attr), 0); + + if ret != 0 { + // be sure to not leak the closure + let _p: Box = mem::transmute(arg); + panic!("failed to spawn native thread: {}", ret); + } + native +} + +pub unsafe fn join(native: rust_thread) { + assert_eq!(pthread_join(native, ptr::null_mut()), 0); +} + +pub unsafe fn detach(native: rust_thread) { + assert_eq!(pthread_detach(native), 0); +} + +pub unsafe fn yield_now() { assert_eq!(sched_yield(), 0); } +// glibc >= 2.15 has a __pthread_get_minstack() function that returns +// PTHREAD_STACK_MIN plus however many bytes are needed for thread-local +// storage. We need that information to avoid blowing up when a small stack +// is created in an application with big thread-local storage requirements. +// See #6233 for rationale and details. +// +// Link weakly to the symbol for compatibility with older versions of glibc. +// Assumes that we've been dynamically linked to libpthread but that is +// currently always the case. Note that you need to check that the symbol +// is non-null before calling it! +#[cfg(target_os = "linux")] +fn min_stack_size(attr: *const libc::pthread_attr_t) -> libc::size_t { + type F = unsafe extern "C" fn(*const libc::pthread_attr_t) -> libc::size_t; + extern { + #[linkage = "extern_weak"] + static __pthread_get_minstack: *const (); + } + if __pthread_get_minstack.is_null() { + PTHREAD_STACK_MIN + } else { + unsafe { mem::transmute::<*const (), F>(__pthread_get_minstack)(attr) } + } +} + +// __pthread_get_minstack() is marked as weak but extern_weak linkage is +// not supported on OS X, hence this kludge... +#[cfg(not(target_os = "linux"))] +fn min_stack_size(_: *const libc::pthread_attr_t) -> libc::size_t { + PTHREAD_STACK_MIN +} + +#[cfg(any(target_os = "linux"))] +extern { + pub fn pthread_self() -> libc::pthread_t; + pub fn pthread_getattr_np(native: libc::pthread_t, + attr: *mut libc::pthread_attr_t) -> libc::c_int; + pub fn pthread_attr_getguardsize(attr: *const libc::pthread_attr_t, + guardsize: *mut libc::size_t) -> libc::c_int; + pub fn pthread_attr_getstack(attr: *const libc::pthread_attr_t, + stackaddr: *mut *mut libc::c_void, + stacksize: *mut libc::size_t) -> libc::c_int; +} + +#[cfg(target_os = "macos")] +extern { + pub fn pthread_self() -> libc::pthread_t; + pub fn pthread_get_stackaddr_np(thread: libc::pthread_t) -> *mut libc::c_void; + pub fn pthread_get_stacksize_np(thread: libc::pthread_t) -> libc::size_t; +} + +extern { + fn pthread_create(native: *mut libc::pthread_t, + attr: *const libc::pthread_attr_t, + f: StartFn, + value: *mut libc::c_void) -> libc::c_int; + fn pthread_join(native: libc::pthread_t, + value: *mut *mut libc::c_void) -> libc::c_int; + fn pthread_attr_init(attr: *mut libc::pthread_attr_t) -> libc::c_int; + pub fn pthread_attr_destroy(attr: *mut libc::pthread_attr_t) -> libc::c_int; + fn pthread_attr_setstacksize(attr: *mut libc::pthread_attr_t, + stack_size: libc::size_t) -> libc::c_int; + fn pthread_attr_setdetachstate(attr: *mut libc::pthread_attr_t, + state: libc::c_int) -> libc::c_int; + fn pthread_detach(thread: libc::pthread_t) -> libc::c_int; + fn sched_yield() -> libc::c_int; +} diff --git a/src/libstd/sys/windows/backtrace.rs b/src/libstd/sys/windows/backtrace.rs new file mode 100644 index 0000000000000..833b69d6cbebb --- /dev/null +++ b/src/libstd/sys/windows/backtrace.rs @@ -0,0 +1,371 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +/// As always, windows has something very different than unix, we mainly want +/// to avoid having to depend too much on libunwind for windows. +/// +/// If you google around, you'll find a fair bit of references to built-in +/// functions to get backtraces on windows. It turns out that most of these are +/// in an external library called dbghelp. I was unable to find this library +/// via `-ldbghelp`, but it is apparently normal to do the `dlopen` equivalent +/// of it. +/// +/// You'll also find that there's a function called CaptureStackBackTrace +/// mentioned frequently (which is also easy to use), but sadly I didn't have a +/// copy of that function in my mingw install (maybe it was broken?). Instead, +/// this takes the route of using StackWalk64 in order to walk the stack. + +use c_str::CString; +use intrinsics; +use io::{IoResult, Writer}; +use libc; +use mem; +use ops::Drop; +use option::{Some, None}; +use path::Path; +use result::{Ok, Err}; +use rt::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT}; +use slice::SliceExt; +use str::StrPrelude; +use dynamic_lib::DynamicLibrary; + +use sys_common::backtrace::*; + +#[allow(non_snake_case)] +extern "system" { + fn GetCurrentProcess() -> libc::HANDLE; + fn GetCurrentThread() -> libc::HANDLE; + fn RtlCaptureContext(ctx: *mut arch::CONTEXT); +} + +type SymFromAddrFn = + extern "system" fn(libc::HANDLE, u64, *mut u64, + *mut SYMBOL_INFO) -> libc::BOOL; +type SymInitializeFn = + extern "system" fn(libc::HANDLE, *mut libc::c_void, + libc::BOOL) -> libc::BOOL; +type SymCleanupFn = + extern "system" fn(libc::HANDLE) -> libc::BOOL; + +type StackWalk64Fn = + extern "system" fn(libc::DWORD, libc::HANDLE, libc::HANDLE, + *mut STACKFRAME64, *mut arch::CONTEXT, + *mut libc::c_void, *mut libc::c_void, + *mut libc::c_void, *mut libc::c_void) -> libc::BOOL; + +const MAX_SYM_NAME: uint = 2000; +const IMAGE_FILE_MACHINE_I386: libc::DWORD = 0x014c; +const IMAGE_FILE_MACHINE_IA64: libc::DWORD = 0x0200; +const IMAGE_FILE_MACHINE_AMD64: libc::DWORD = 0x8664; + +#[repr(C)] +struct SYMBOL_INFO { + SizeOfStruct: libc::c_ulong, + TypeIndex: libc::c_ulong, + Reserved: [u64, ..2], + Index: libc::c_ulong, + Size: libc::c_ulong, + ModBase: u64, + Flags: libc::c_ulong, + Value: u64, + Address: u64, + Register: libc::c_ulong, + Scope: libc::c_ulong, + Tag: libc::c_ulong, + NameLen: libc::c_ulong, + MaxNameLen: libc::c_ulong, + // note that windows has this as 1, but it basically just means that + // the name is inline at the end of the struct. For us, we just bump + // the struct size up to MAX_SYM_NAME. + Name: [libc::c_char, ..MAX_SYM_NAME], +} + + +#[repr(C)] +enum ADDRESS_MODE { + AddrMode1616, + AddrMode1632, + AddrModeReal, + AddrModeFlat, +} + +struct ADDRESS64 { + Offset: u64, + Segment: u16, + Mode: ADDRESS_MODE, +} + +struct STACKFRAME64 { + AddrPC: ADDRESS64, + AddrReturn: ADDRESS64, + AddrFrame: ADDRESS64, + AddrStack: ADDRESS64, + AddrBStore: ADDRESS64, + FuncTableEntry: *mut libc::c_void, + Params: [u64, ..4], + Far: libc::BOOL, + Virtual: libc::BOOL, + Reserved: [u64, ..3], + KdHelp: KDHELP64, +} + +struct KDHELP64 { + Thread: u64, + ThCallbackStack: libc::DWORD, + ThCallbackBStore: libc::DWORD, + NextCallback: libc::DWORD, + FramePointer: libc::DWORD, + KiCallUserMode: u64, + KeUserCallbackDispatcher: u64, + SystemRangeStart: u64, + KiUserExceptionDispatcher: u64, + StackBase: u64, + StackLimit: u64, + Reserved: [u64, ..5], +} + +#[cfg(target_arch = "x86")] +mod arch { + use libc; + + const MAXIMUM_SUPPORTED_EXTENSION: uint = 512; + + #[repr(C)] + pub struct CONTEXT { + ContextFlags: libc::DWORD, + Dr0: libc::DWORD, + Dr1: libc::DWORD, + Dr2: libc::DWORD, + Dr3: libc::DWORD, + Dr6: libc::DWORD, + Dr7: libc::DWORD, + FloatSave: FLOATING_SAVE_AREA, + SegGs: libc::DWORD, + SegFs: libc::DWORD, + SegEs: libc::DWORD, + SegDs: libc::DWORD, + Edi: libc::DWORD, + Esi: libc::DWORD, + Ebx: libc::DWORD, + Edx: libc::DWORD, + Ecx: libc::DWORD, + Eax: libc::DWORD, + Ebp: libc::DWORD, + Eip: libc::DWORD, + SegCs: libc::DWORD, + EFlags: libc::DWORD, + Esp: libc::DWORD, + SegSs: libc::DWORD, + ExtendedRegisters: [u8, ..MAXIMUM_SUPPORTED_EXTENSION], + } + + #[repr(C)] + pub struct FLOATING_SAVE_AREA { + ControlWord: libc::DWORD, + StatusWord: libc::DWORD, + TagWord: libc::DWORD, + ErrorOffset: libc::DWORD, + ErrorSelector: libc::DWORD, + DataOffset: libc::DWORD, + DataSelector: libc::DWORD, + RegisterArea: [u8, ..80], + Cr0NpxState: libc::DWORD, + } + + pub fn init_frame(frame: &mut super::STACKFRAME64, + ctx: &CONTEXT) -> libc::DWORD { + frame.AddrPC.Offset = ctx.Eip as u64; + frame.AddrPC.Mode = super::ADDRESS_MODE::AddrModeFlat; + frame.AddrStack.Offset = ctx.Esp as u64; + frame.AddrStack.Mode = super::ADDRESS_MODE::AddrModeFlat; + frame.AddrFrame.Offset = ctx.Ebp as u64; + frame.AddrFrame.Mode = super::ADDRESS_MODE::AddrModeFlat; + super::IMAGE_FILE_MACHINE_I386 + } +} + +#[cfg(target_arch = "x86_64")] +mod arch { + use libc::{c_longlong, c_ulonglong}; + use libc::types::os::arch::extra::{WORD, DWORD, DWORDLONG}; + use simd; + + #[repr(C)] + pub struct CONTEXT { + _align_hack: [simd::u64x2, ..0], // FIXME align on 16-byte + P1Home: DWORDLONG, + P2Home: DWORDLONG, + P3Home: DWORDLONG, + P4Home: DWORDLONG, + P5Home: DWORDLONG, + P6Home: DWORDLONG, + + ContextFlags: DWORD, + MxCsr: DWORD, + + SegCs: WORD, + SegDs: WORD, + SegEs: WORD, + SegFs: WORD, + SegGs: WORD, + SegSs: WORD, + EFlags: DWORD, + + Dr0: DWORDLONG, + Dr1: DWORDLONG, + Dr2: DWORDLONG, + Dr3: DWORDLONG, + Dr6: DWORDLONG, + Dr7: DWORDLONG, + + Rax: DWORDLONG, + Rcx: DWORDLONG, + Rdx: DWORDLONG, + Rbx: DWORDLONG, + Rsp: DWORDLONG, + Rbp: DWORDLONG, + Rsi: DWORDLONG, + Rdi: DWORDLONG, + R8: DWORDLONG, + R9: DWORDLONG, + R10: DWORDLONG, + R11: DWORDLONG, + R12: DWORDLONG, + R13: DWORDLONG, + R14: DWORDLONG, + R15: DWORDLONG, + + Rip: DWORDLONG, + + FltSave: FLOATING_SAVE_AREA, + + VectorRegister: [M128A, .. 26], + VectorControl: DWORDLONG, + + DebugControl: DWORDLONG, + LastBranchToRip: DWORDLONG, + LastBranchFromRip: DWORDLONG, + LastExceptionToRip: DWORDLONG, + LastExceptionFromRip: DWORDLONG, + } + + #[repr(C)] + pub struct M128A { + _align_hack: [simd::u64x2, ..0], // FIXME align on 16-byte + Low: c_ulonglong, + High: c_longlong + } + + #[repr(C)] + pub struct FLOATING_SAVE_AREA { + _align_hack: [simd::u64x2, ..0], // FIXME align on 16-byte + _Dummy: [u8, ..512] // FIXME: Fill this out + } + + pub fn init_frame(frame: &mut super::STACKFRAME64, + ctx: &CONTEXT) -> DWORD { + frame.AddrPC.Offset = ctx.Rip as u64; + frame.AddrPC.Mode = super::ADDRESS_MODE::AddrModeFlat; + frame.AddrStack.Offset = ctx.Rsp as u64; + frame.AddrStack.Mode = super::ADDRESS_MODE::AddrModeFlat; + frame.AddrFrame.Offset = ctx.Rbp as u64; + frame.AddrFrame.Mode = super::ADDRESS_MODE::AddrModeFlat; + super::IMAGE_FILE_MACHINE_AMD64 + } +} + +#[repr(C)] +struct Cleanup { + handle: libc::HANDLE, + SymCleanup: SymCleanupFn, +} + +impl Drop for Cleanup { + fn drop(&mut self) { (self.SymCleanup)(self.handle); } +} + +pub fn write(w: &mut Writer) -> IoResult<()> { + // According to windows documentation, all dbghelp functions are + // single-threaded. + static LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT; + let _g = unsafe { LOCK.lock() }; + + // Open up dbghelp.dll, we don't link to it explicitly because it can't + // always be found. Additionally, it's nice having fewer dependencies. + let path = Path::new("dbghelp.dll"); + let lib = match DynamicLibrary::open(Some(&path)) { + Ok(lib) => lib, + Err(..) => return Ok(()), + }; + + macro_rules! sym( ($e:expr, $t:ident) => (unsafe { + match lib.symbol($e) { + Ok(f) => mem::transmute::<*mut u8, $t>(f), + Err(..) => return Ok(()) + } + }) ) + + // Fetch the symbols necessary from dbghelp.dll + let SymFromAddr = sym!("SymFromAddr", SymFromAddrFn); + let SymInitialize = sym!("SymInitialize", SymInitializeFn); + let SymCleanup = sym!("SymCleanup", SymCleanupFn); + let StackWalk64 = sym!("StackWalk64", StackWalk64Fn); + + // Allocate necessary structures for doing the stack walk + let process = unsafe { GetCurrentProcess() }; + let thread = unsafe { GetCurrentThread() }; + let mut context: arch::CONTEXT = unsafe { intrinsics::init() }; + unsafe { RtlCaptureContext(&mut context); } + let mut frame: STACKFRAME64 = unsafe { intrinsics::init() }; + let image = arch::init_frame(&mut frame, &context); + + // Initialize this process's symbols + let ret = SymInitialize(process, 0 as *mut libc::c_void, libc::TRUE); + if ret != libc::TRUE { return Ok(()) } + let _c = Cleanup { handle: process, SymCleanup: SymCleanup }; + + // And now that we're done with all the setup, do the stack walking! + let mut i = 0i; + try!(write!(w, "stack backtrace:\n")); + while StackWalk64(image, process, thread, &mut frame, &mut context, + 0 as *mut libc::c_void, + 0 as *mut libc::c_void, + 0 as *mut libc::c_void, + 0 as *mut libc::c_void) == libc::TRUE{ + let addr = frame.AddrPC.Offset; + if addr == frame.AddrReturn.Offset || addr == 0 || + frame.AddrReturn.Offset == 0 { break } + + i += 1; + try!(write!(w, " {:2}: {:#2$x}", i, addr, HEX_WIDTH)); + let mut info: SYMBOL_INFO = unsafe { intrinsics::init() }; + info.MaxNameLen = MAX_SYM_NAME as libc::c_ulong; + // the struct size in C. the value is different to + // `size_of::() - MAX_SYM_NAME + 1` (== 81) + // due to struct alignment. + info.SizeOfStruct = 88; + + let mut displacement = 0u64; + let ret = SymFromAddr(process, addr as u64, &mut displacement, + &mut info); + + if ret == libc::TRUE { + try!(write!(w, " - ")); + let cstr = unsafe { CString::new(info.Name.as_ptr(), false) }; + let bytes = cstr.as_bytes(); + match cstr.as_str() { + Some(s) => try!(demangle(w, s)), + None => try!(w.write(bytes[..bytes.len()-1])), + } + } + try!(w.write(&['\n' as u8])); + } + + Ok(()) +} diff --git a/src/libstd/sys/windows/mod.rs b/src/libstd/sys/windows/mod.rs index d22d4e0f534db..6924687d8c470 100644 --- a/src/libstd/sys/windows/mod.rs +++ b/src/libstd/sys/windows/mod.rs @@ -35,6 +35,7 @@ macro_rules! helper_init { (static $name:ident: Helper<$m:ty>) => ( }; ) } +pub mod backtrace; pub mod c; pub mod ext; pub mod condvar; @@ -46,7 +47,9 @@ pub mod pipe; pub mod process; pub mod rwlock; pub mod sync; +pub mod stack_overflow; pub mod tcp; +pub mod thread; pub mod thread_local; pub mod timer; pub mod tty; diff --git a/src/libstd/sys/windows/stack_overflow.rs b/src/libstd/sys/windows/stack_overflow.rs new file mode 100644 index 0000000000000..e3d96a054f4c6 --- /dev/null +++ b/src/libstd/sys/windows/stack_overflow.rs @@ -0,0 +1,120 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rt::local::Local; +use rt::task::Task; +use rt::util::report_overflow; +use core::prelude::*; +use ptr; +use mem; +use libc; +use libc::types::os::arch::extra::{LPVOID, DWORD, LONG, BOOL}; +use sys_common::stack; + +pub struct Handler { + _data: *mut libc::c_void +} + +impl Handler { + pub unsafe fn new() -> Handler { + make_handler() + } +} + +impl Drop for Handler { + fn drop(&mut self) {} +} + +// get_task_info is called from an exception / signal handler. +// It returns the guard page of the current task or 0 if that +// guard page doesn't exist. None is returned if there's currently +// no local task. +unsafe fn get_task_guard_page() -> Option { + let task: Option<*mut Task> = Local::try_unsafe_borrow(); + task.map(|task| (&*task).stack_guard().unwrap_or(0)) +} + +// This is initialized in init() and only read from after +static mut PAGE_SIZE: uint = 0; + +#[no_stack_check] +extern "system" fn vectored_handler(ExceptionInfo: *mut EXCEPTION_POINTERS) -> LONG { + unsafe { + let rec = &(*(*ExceptionInfo).ExceptionRecord); + let code = rec.ExceptionCode; + + if code != EXCEPTION_STACK_OVERFLOW { + return EXCEPTION_CONTINUE_SEARCH; + } + + // We're calling into functions with stack checks, + // however stack checks by limit should be disabled on Windows + stack::record_sp_limit(0); + + if get_task_guard_page().is_some() { + report_overflow(); + } + + EXCEPTION_CONTINUE_SEARCH + } +} + +pub unsafe fn init() { + let mut info = mem::zeroed(); + libc::GetSystemInfo(&mut info); + PAGE_SIZE = info.dwPageSize as uint; + + if AddVectoredExceptionHandler(0, vectored_handler) == ptr::null_mut() { + panic!("failed to install exception handler"); + } + + mem::forget(make_handler()); +} + +pub unsafe fn cleanup() { +} + +pub unsafe fn make_handler() -> Handler { + if SetThreadStackGuarantee(&mut 0x5000) == 0 { + panic!("failed to reserve stack space for exception handling"); + } + + Handler { _data: 0i as *mut libc::c_void } +} + +pub struct EXCEPTION_RECORD { + pub ExceptionCode: DWORD, + pub ExceptionFlags: DWORD, + pub ExceptionRecord: *mut EXCEPTION_RECORD, + pub ExceptionAddress: LPVOID, + pub NumberParameters: DWORD, + pub ExceptionInformation: [LPVOID, ..EXCEPTION_MAXIMUM_PARAMETERS] +} + +pub struct EXCEPTION_POINTERS { + pub ExceptionRecord: *mut EXCEPTION_RECORD, + pub ContextRecord: LPVOID +} + +pub type PVECTORED_EXCEPTION_HANDLER = extern "system" + fn(ExceptionInfo: *mut EXCEPTION_POINTERS) -> LONG; + +pub type ULONG = libc::c_ulong; + +const EXCEPTION_CONTINUE_SEARCH: LONG = 0; +const EXCEPTION_MAXIMUM_PARAMETERS: uint = 15; +const EXCEPTION_STACK_OVERFLOW: DWORD = 0xc00000fd; + +extern "system" { + fn AddVectoredExceptionHandler(FirstHandler: ULONG, + VectoredHandler: PVECTORED_EXCEPTION_HANDLER) + -> LPVOID; + fn SetThreadStackGuarantee(StackSizeInBytes: *mut ULONG) -> BOOL; +} diff --git a/src/libstd/sys/windows/thread.rs b/src/libstd/sys/windows/thread.rs new file mode 100644 index 0000000000000..00f1e9767f5fa --- /dev/null +++ b/src/libstd/sys/windows/thread.rs @@ -0,0 +1,95 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use core::prelude::*; + +use boxed::Box; +use cmp; +use mem; +use ptr; +use libc; +use libc::types::os::arch::extra::{LPSECURITY_ATTRIBUTES, SIZE_T, BOOL, + LPVOID, DWORD, LPDWORD, HANDLE}; +use sys_common::stack::RED_ZONE; +use sys_common::thread::*; + +pub type rust_thread = HANDLE; +pub type rust_thread_return = DWORD; + +pub type StartFn = extern "system" fn(*mut libc::c_void) -> rust_thread_return; + +#[no_stack_check] +pub extern "system" fn thread_start(main: *mut libc::c_void) -> rust_thread_return { + return start_thread(main); +} + +pub mod guard { + pub unsafe fn main() -> uint { + 0 + } + + pub unsafe fn current() -> uint { + 0 + } + + pub unsafe fn init() { + } +} + +pub unsafe fn create(stack: uint, p: Box) -> rust_thread { + let arg: *mut libc::c_void = mem::transmute(p); + // FIXME On UNIX, we guard against stack sizes that are too small but + // that's because pthreads enforces that stacks are at least + // PTHREAD_STACK_MIN bytes big. Windows has no such lower limit, it's + // just that below a certain threshold you can't do anything useful. + // That threshold is application and architecture-specific, however. + // For now, the only requirement is that it's big enough to hold the + // red zone. Round up to the next 64 kB because that's what the NT + // kernel does, might as well make it explicit. With the current + // 20 kB red zone, that makes for a 64 kB minimum stack. + let stack_size = (cmp::max(stack, RED_ZONE) + 0xfffe) & (-0xfffe - 1); + let ret = CreateThread(ptr::null_mut(), stack_size as libc::size_t, + thread_start, arg, 0, ptr::null_mut()); + + if ret as uint == 0 { + // be sure to not leak the closure + let _p: Box = mem::transmute(arg); + panic!("failed to spawn native thread: {}", ret); + } + return ret; +} + +pub unsafe fn join(native: rust_thread) { + use libc::consts::os::extra::INFINITE; + WaitForSingleObject(native, INFINITE); +} + +pub unsafe fn detach(native: rust_thread) { + assert!(libc::CloseHandle(native) != 0); +} + +pub unsafe fn yield_now() { + // This function will return 0 if there are no other threads to execute, + // but this also means that the yield was useless so this isn't really a + // case that needs to be worried about. + SwitchToThread(); +} + +#[allow(non_snake_case)] +extern "system" { + fn CreateThread(lpThreadAttributes: LPSECURITY_ATTRIBUTES, + dwStackSize: SIZE_T, + lpStartAddress: StartFn, + lpParameter: LPVOID, + dwCreationFlags: DWORD, + lpThreadId: LPDWORD) -> HANDLE; + fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) -> DWORD; + fn SwitchToThread() -> BOOL; +} diff --git a/src/libstd/sys/windows/thread_local.rs b/src/libstd/sys/windows/thread_local.rs index 969b322af991f..6c8d9639d5c4c 100644 --- a/src/libstd/sys/windows/thread_local.rs +++ b/src/libstd/sys/windows/thread_local.rs @@ -13,8 +13,8 @@ use prelude::*; use libc::types::os::arch::extra::{DWORD, LPVOID, BOOL}; use mem; -use rustrt; -use rustrt::exclusive::Exclusive; +use rt; +use rt::exclusive::Exclusive; use sync::{ONCE_INIT, Once}; pub type Key = DWORD; @@ -131,7 +131,7 @@ fn init_dtors() { DTORS = mem::transmute(dtors); } - rustrt::at_exit(move|| unsafe { + rt::at_exit(move|| unsafe { mem::transmute::<_, Box>>>(DTORS); DTORS = 0 as *mut _; }); diff --git a/src/libstd/task.rs b/src/libstd/task.rs index 324b594209a1e..127cad186f693 100644 --- a/src/libstd/task.rs +++ b/src/libstd/task.rs @@ -53,9 +53,9 @@ use kinds::Send; use option::Option; use option::Option::{None, Some}; use result::Result; -use rustrt::local::Local; -use rustrt::task::Task; -use rustrt::task; +use rt::local::Local; +use rt::task; +use rt::task::Task; use str::SendStr; use string::{String, ToString}; use thunk::{Thunk}; @@ -252,7 +252,7 @@ pub fn try_future(f: F) -> Future>> /// Read the name of the current task. #[stable] pub fn name() -> Option { - use rustrt::task::Task; + use rt::task::Task; let task = Local::borrow(None::); match task.name { @@ -264,7 +264,7 @@ pub fn name() -> Option { /// Yield control to the task scheduler. #[unstable = "Name will change."] pub fn deschedule() { - use rustrt::task::Task; + use rt::task::Task; Task::yield_now(); } @@ -272,7 +272,7 @@ pub fn deschedule() { /// destructor that is run while unwinding the stack after a call to `panic!()`). #[unstable = "May move to a different module."] pub fn failing() -> bool { - use rustrt::task::Task; + use rt::task::Task; Local::borrow(None::).unwinder.unwinding() } diff --git a/src/libstd/thread_local/mod.rs b/src/libstd/thread_local/mod.rs index 1268ab8e0cfa6..b78428d69de21 100644 --- a/src/libstd/thread_local/mod.rs +++ b/src/libstd/thread_local/mod.rs @@ -446,7 +446,7 @@ mod tests { use prelude::*; use cell::UnsafeCell; - use rustrt::thread::Thread; + use rt::thread::Thread; struct Foo(Sender<()>); diff --git a/src/librustrt/thunk.rs b/src/libstd/thunk.rs similarity index 100% rename from src/librustrt/thunk.rs rename to src/libstd/thunk.rs diff --git a/src/rt/rust_try.ll b/src/rt/rust_try.ll index 33d2d31a2e0c1..18b23b4143a58 100644 --- a/src/rt/rust_try.ll +++ b/src/rt/rust_try.ll @@ -12,7 +12,7 @@ ; When f(...) returns normally, the return value is null. ; When f(...) throws, the return value is a pointer to the caught exception object. -; See also: librustrt/unwind.rs +; See also: libstd/rt/unwind.rs define i8* @rust_try(void (i8*,i8*)* %f, i8* %fptr, i8* %env) { diff --git a/src/test/run-pass/foreign-call-no-runtime.rs b/src/test/run-pass/foreign-call-no-runtime.rs index 3f659f1dbfbf7..0bfae8498e2a6 100644 --- a/src/test/run-pass/foreign-call-no-runtime.rs +++ b/src/test/run-pass/foreign-call-no-runtime.rs @@ -9,10 +9,9 @@ // except according to those terms. extern crate libc; -extern crate rustrt; use std::mem; -use rustrt::thread::Thread; +use std::rt::thread::Thread; #[link(name = "rust_test_helpers")] extern { diff --git a/src/test/run-pass/match-ref-binding-in-guard-3256.rs b/src/test/run-pass/match-ref-binding-in-guard-3256.rs index ac783961b508e..9a822ddbbcc51 100644 --- a/src/test/run-pass/match-ref-binding-in-guard-3256.rs +++ b/src/test/run-pass/match-ref-binding-in-guard-3256.rs @@ -8,11 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -extern crate rustrt; +use std::rt::exclusive; pub fn main() { unsafe { - let x = Some(::rustrt::exclusive::Exclusive::new(true)); + let x = Some(exclusive::Exclusive::new(true)); match x { Some(ref z) if *z.lock() => { assert!(*z.lock()); diff --git a/src/test/run-pass/running-with-no-runtime.rs b/src/test/run-pass/running-with-no-runtime.rs index 36b90a9168f71..067b0e8dfb4a2 100644 --- a/src/test/run-pass/running-with-no-runtime.rs +++ b/src/test/run-pass/running-with-no-runtime.rs @@ -8,15 +8,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -extern crate rustrt; - use std::io::process::{Command, ProcessOutput}; use std::os; use std::str; use std::rt; use std::thunk::Thunk; -use rustrt::unwind::try; +use std::rt::unwind::try; #[start] fn start(argc: int, argv: *const *const u8) -> int { diff --git a/src/test/run-pass/writealias.rs b/src/test/run-pass/writealias.rs index c8d281a791c90..a86aedd757a30 100644 --- a/src/test/run-pass/writealias.rs +++ b/src/test/run-pass/writealias.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -extern crate rustrt; +use std::rt::exclusive; struct Point {x: int, y: int, z: int} @@ -16,7 +16,7 @@ fn f(p: &mut Point) { p.z = 13; } pub fn main() { unsafe { - let x = Some(::rustrt::exclusive::Exclusive::new(true)); + let x = Some(exclusive::Exclusive::new(true)); match x { Some(ref z) if *z.lock() => { assert!(*z.lock());