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]
This commit is contained in:
Aaron Turon 2014-11-23 19:21:17 -08:00
parent 840de07208
commit 2b3477d373
54 changed files with 2111 additions and 1775 deletions

View File

@ -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 \

View File

@ -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.
*/
}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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(); }
}

View File

@ -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<F>(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) {

View File

@ -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();

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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: F) where
});
result
} else {
let mut io = rustrt::Stdout;
let mut io = rt::util::Stdout;
f(&mut io as &mut Writer)
};
match result {

View File

@ -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;

View File

@ -1042,9 +1042,9 @@ fn real_args_as_bytes() -> Vec<Vec<u8>> {
target_os = "freebsd",
target_os = "dragonfly"))]
fn real_args_as_bytes() -> Vec<Vec<u8>> {
use rustrt;
use rt;
match rustrt::args::clone() {
match rt::args::clone() {
Some(args) => args,
None => panic!("process arguments not initialized")
}

View File

@ -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<Vec<Vec<u8>>> { 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) {
}

View File

@ -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<Vec<Thunk>>;

View File

@ -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<IoError>,
}
// 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, " ... <frames omitted>\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<CString>) -> 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, "<unknown>")),
}
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::<SYMBOL_INFO>() - 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);
}) }

View File

@ -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;

View File

@ -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<T> 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() {

View File

@ -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<Borrowed> {
@ -52,10 +52,10 @@ impl Local<local_ptr::Borrowed<Task>> 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() {

View File

@ -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<T>() -> Borrowed<T> {
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<tls::Key> {
use realrustrt;
use rt;
unsafe {
mem::transmute(realrustrt::shouldnt_be_public::maybe_tls_key())
mem::transmute(::realstd::rt::shouldnt_be_public::maybe_tls_key())
}
}
}

View File

@ -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)*))
}

View File

@ -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("<main>".into_cow());
Some(rt::thread::main_guard_page()));
task.name = Some(str::Slice("<main>"));
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;
}

View File

@ -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<pthread_mutex_t>,
cond: UnsafeCell<pthread_cond_t>,
}
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() {

View File

@ -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!()))
}

171
src/libstd/rt/thread.rs Normal file
View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<T> {
native: imp::rust_thread,
joined: bool,
packet: Box<Option<T>>,
}
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<T: Send>(main: proc():Send -> T) -> Thread<T> {
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<T: Send>(stack: uint, main: proc():Send -> T) -> Thread<T> {
// 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<T> = unsafe {
*mem::transmute::<&Box<Option<T>>, *const *mut Option<T>>(&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<T: Send> Thread<T> {
/// 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<T: Send> Drop for Thread<T> {
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());
}
}

View File

@ -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;

View File

@ -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

View File

@ -7,10 +7,18 @@
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//
// ignore-lexer-test FIXME #15677
use libc::uintptr_t;
use option::Option;
use option::Option::{Some, None};
use core::prelude::*;
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("<unknown>"));
}

View File

@ -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")]

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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(())
}

View File

@ -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<M: Send> Helper<M> {
self.cond.notify_one()
});
rustrt::at_exit(move|:| { self.shutdown() });
rt::at_exit(move|:| { self.shutdown() });
*self.initialized.get() = true;
}
}

View File

@ -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

View File

@ -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();
}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<proc()> = mem::transmute(main);
(*f)();
drop(handler);
mem::transmute(0 as thread::rust_thread_return)
}
}

View File

@ -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 {
}
}
}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<IoError>,
}
// 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, " ... <frames omitted>\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<CString>) -> 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, "<unknown>")),
}
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
}
}

View File

@ -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;

View File

@ -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;
use core::prelude::*;
use self::imp::{make_handler, drop_handler};
pub unsafe fn init() {
imp::init();
}
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);
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("<unknown>"));
}
// 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<uint> {
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");
}
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<uint> {
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;

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<proc():Send>) -> 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<proc():Send> = 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;
}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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::<SYMBOL_INFO>() - 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(())
}

View File

@ -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;

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<uint> {
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;
}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<proc():Send>) -> 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<proc():Send> = 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;
}

View File

@ -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<Exclusive<Vec<(Key, Dtor)>>>>(DTORS);
DTORS = 0 as *mut _;
});

View File

@ -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<T,F>(f: F) -> Future<Result<T, Box<Any + Send>>>
/// Read the name of the current task.
#[stable]
pub fn name() -> Option<String> {
use rustrt::task::Task;
use rt::task::Task;
let task = Local::borrow(None::<Task>);
match task.name {
@ -264,7 +264,7 @@ pub fn name() -> Option<String> {
/// 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::<Task>).unwinder.unwinding()
}

View File

@ -446,7 +446,7 @@ mod tests {
use prelude::*;
use cell::UnsafeCell;
use rustrt::thread::Thread;
use rt::thread::Thread;
struct Foo(Sender<()>);

View File

@ -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) {

View File

@ -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 {

View File

@ -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());

View File

@ -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 {

View File

@ -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());