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:
parent
840de07208
commit
2b3477d373
@ -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 \
|
||||
|
@ -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.
|
||||
*/
|
||||
}
|
||||
|
||||
|
@ -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(); }
|
||||
}
|
@ -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) {
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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")
|
||||
}
|
||||
|
@ -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) {
|
||||
}
|
@ -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>>;
|
||||
|
@ -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);
|
||||
}) }
|
||||
|
||||
|
@ -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;
|
@ -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() {
|
@ -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() {
|
@ -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())
|
||||
}
|
||||
}
|
||||
}
|
@ -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)*))
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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() {
|
@ -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
171
src/libstd/rt/thread.rs
Normal 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());
|
||||
}
|
||||
}
|
@ -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;
|
@ -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
|
@ -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>"));
|
||||
}
|
||||
|
@ -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")]
|
||||
|
131
src/libstd/sys/common/backtrace.rs
Normal file
131
src/libstd/sys/common/backtrace.rs
Normal 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(())
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
34
src/libstd/sys/common/thread.rs
Normal file
34
src/libstd/sys/common/thread.rs
Normal 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)
|
||||
}
|
||||
}
|
@ -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 {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
493
src/libstd/sys/unix/backtrace.rs
Normal file
493
src/libstd/sys/unix/backtrace.rs
Normal 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
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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;
|
||||
|
270
src/libstd/sys/unix/thread.rs
Normal file
270
src/libstd/sys/unix/thread.rs
Normal 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;
|
||||
}
|
371
src/libstd/sys/windows/backtrace.rs
Normal file
371
src/libstd/sys/windows/backtrace.rs
Normal 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(())
|
||||
}
|
@ -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;
|
||||
|
120
src/libstd/sys/windows/stack_overflow.rs
Normal file
120
src/libstd/sys/windows/stack_overflow.rs
Normal 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;
|
||||
}
|
95
src/libstd/sys/windows/thread.rs
Normal file
95
src/libstd/sys/windows/thread.rs
Normal 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;
|
||||
}
|
@ -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 _;
|
||||
});
|
||||
|
@ -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()
|
||||
}
|
||||
|
||||
|
@ -446,7 +446,7 @@ mod tests {
|
||||
use prelude::*;
|
||||
|
||||
use cell::UnsafeCell;
|
||||
use rustrt::thread::Thread;
|
||||
use rt::thread::Thread;
|
||||
|
||||
struct Foo(Sender<()>);
|
||||
|
||||
|
@ -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) {
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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());
|
||||
|
@ -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 {
|
||||
|
@ -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());
|
||||
|
Loading…
Reference in New Issue
Block a user