add an option to debug borrows (RUST_DEBUG_BORROW) so you can
find out where the offending borrow occurred. This ... still needs some work.
This commit is contained in:
@@ -31,8 +31,10 @@ pub struct Environment {
|
|||||||
argc: c_int,
|
argc: c_int,
|
||||||
/// The argv value passed to main
|
/// The argv value passed to main
|
||||||
argv: **c_char,
|
argv: **c_char,
|
||||||
/// Print GC debugging info
|
/// Print GC debugging info (true if env var RUST_DEBUG_MEM is set)
|
||||||
debug_mem: bool
|
debug_mem: bool,
|
||||||
|
/// Track origin of `@mut` borrows (true if env var RUST_DEBUG_BORROWS is set)
|
||||||
|
debug_borrows: bool
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the global environment settings
|
/// Get the global environment settings
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ use str;
|
|||||||
use sys;
|
use sys;
|
||||||
use unstable::exchange_alloc;
|
use unstable::exchange_alloc;
|
||||||
use cast::transmute;
|
use cast::transmute;
|
||||||
|
use task::rt::rust_get_task;
|
||||||
|
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
pub type rust_task = c_void;
|
pub type rust_task = c_void;
|
||||||
@@ -27,7 +28,8 @@ pub static FROZEN_BIT: uint = 0x80000000;
|
|||||||
pub static FROZEN_BIT: uint = 0x8000000000000000;
|
pub static FROZEN_BIT: uint = 0x8000000000000000;
|
||||||
|
|
||||||
pub mod rustrt {
|
pub mod rustrt {
|
||||||
use libc::{c_char, uintptr_t};
|
use unstable::lang::rust_task;
|
||||||
|
use libc::{c_void, c_char, uintptr_t};
|
||||||
|
|
||||||
pub extern {
|
pub extern {
|
||||||
#[rust_stack]
|
#[rust_stack]
|
||||||
@@ -43,6 +45,12 @@ pub mod rustrt {
|
|||||||
|
|
||||||
#[fast_ffi]
|
#[fast_ffi]
|
||||||
unsafe fn rust_upcall_free_noswitch(ptr: *c_char);
|
unsafe fn rust_upcall_free_noswitch(ptr: *c_char);
|
||||||
|
|
||||||
|
#[rust_stack]
|
||||||
|
fn rust_take_task_borrow_list(task: *rust_task) -> *c_void;
|
||||||
|
|
||||||
|
#[rust_stack]
|
||||||
|
fn rust_set_task_borrow_list(task: *rust_task, map: *c_void);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,10 +69,50 @@ pub fn fail_bounds_check(file: *c_char, line: size_t,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fail_borrowed(file: *c_char, line: size_t) {
|
struct BorrowRecord {
|
||||||
let msg = "borrowed";
|
box: *mut BoxRepr,
|
||||||
do str::as_buf(msg) |msg_p, _| {
|
file: *c_char,
|
||||||
fail_(msg_p as *c_char, file, line);
|
line: size_t
|
||||||
|
}
|
||||||
|
|
||||||
|
fn swap_task_borrow_list(f: &fn(~[BorrowRecord]) -> ~[BorrowRecord]) {
|
||||||
|
unsafe {
|
||||||
|
let cur_task = rust_get_task();
|
||||||
|
let mut borrow_list: ~[BorrowRecord] = {
|
||||||
|
let ptr = rustrt::rust_take_task_borrow_list(cur_task);
|
||||||
|
if ptr.is_null() { ~[] } else { transmute(ptr) }
|
||||||
|
};
|
||||||
|
borrow_list = f(borrow_list);
|
||||||
|
rustrt::rust_set_task_borrow_list(cur_task, transmute(borrow_list));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fail_borrowed(box: *mut BoxRepr, file: *c_char, line: size_t) {
|
||||||
|
if !::rt::env::get().debug_borrows {
|
||||||
|
let msg = "borrowed";
|
||||||
|
do str::as_buf(msg) |msg_p, _| {
|
||||||
|
fail_(msg_p as *c_char, file, line);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
do swap_task_borrow_list |borrow_list| {
|
||||||
|
let mut msg = ~"borrowed";
|
||||||
|
let mut sep = " at ";
|
||||||
|
for borrow_list.each_reverse |entry| {
|
||||||
|
if entry.box == box {
|
||||||
|
str::push_str(&mut msg, sep);
|
||||||
|
let filename = unsafe {
|
||||||
|
str::raw::from_c_str(entry.file)
|
||||||
|
};
|
||||||
|
str::push_str(&mut msg, filename);
|
||||||
|
str::push_str(&mut msg, fmt!(":%u", line as uint));
|
||||||
|
sep = " and at ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
do str::as_buf(msg) |msg_p, _| {
|
||||||
|
fail_(msg_p as *c_char, file, line)
|
||||||
|
}
|
||||||
|
borrow_list
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,6 +188,7 @@ pub unsafe fn local_free(ptr: *c_char) {
|
|||||||
rustrt::rust_upcall_free_noswitch(ptr);
|
rustrt::rust_upcall_free_noswitch(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(stage0)]
|
||||||
#[lang="borrow_as_imm"]
|
#[lang="borrow_as_imm"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub unsafe fn borrow_as_imm(a: *u8) {
|
pub unsafe fn borrow_as_imm(a: *u8) {
|
||||||
@@ -147,6 +196,21 @@ pub unsafe fn borrow_as_imm(a: *u8) {
|
|||||||
(*a).header.ref_count |= FROZEN_BIT;
|
(*a).header.ref_count |= FROZEN_BIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(stage0))]
|
||||||
|
#[lang="borrow_as_imm"]
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn borrow_as_imm(a: *u8, file: *c_char, line: size_t) {
|
||||||
|
let a: *mut BoxRepr = transmute(a);
|
||||||
|
(*a).header.ref_count |= FROZEN_BIT;
|
||||||
|
if ::rt::env::get().debug_borrows {
|
||||||
|
do swap_task_borrow_list |borrow_list| {
|
||||||
|
let mut borrow_list = borrow_list;
|
||||||
|
borrow_list.push(BorrowRecord {box: a, file: file, line: line});
|
||||||
|
borrow_list
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[lang="return_to_mut"]
|
#[lang="return_to_mut"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub unsafe fn return_to_mut(a: *u8) {
|
pub unsafe fn return_to_mut(a: *u8) {
|
||||||
@@ -165,7 +229,7 @@ pub unsafe fn check_not_borrowed(a: *u8) {
|
|||||||
let a: *mut BoxRepr = transmute(a);
|
let a: *mut BoxRepr = transmute(a);
|
||||||
if ((*a).header.ref_count & FROZEN_BIT) != 0 {
|
if ((*a).header.ref_count & FROZEN_BIT) != 0 {
|
||||||
do str::as_buf("XXX") |file_p, _| {
|
do str::as_buf("XXX") |file_p, _| {
|
||||||
fail_borrowed(file_p as *c_char, 0);
|
fail_borrowed(a, file_p as *c_char, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -178,7 +242,7 @@ pub unsafe fn check_not_borrowed(a: *u8,
|
|||||||
line: size_t) {
|
line: size_t) {
|
||||||
let a: *mut BoxRepr = transmute(a);
|
let a: *mut BoxRepr = transmute(a);
|
||||||
if ((*a).header.ref_count & FROZEN_BIT) != 0 {
|
if ((*a).header.ref_count & FROZEN_BIT) != 0 {
|
||||||
fail_borrowed(file, line);
|
fail_borrowed(a, file, line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -966,7 +966,7 @@ pub fn root_pats_as_necessary(bcx: block,
|
|||||||
|
|
||||||
let datum = Datum {val: val, ty: node_id_type(bcx, pat_id),
|
let datum = Datum {val: val, ty: node_id_type(bcx, pat_id),
|
||||||
mode: ByRef, source: ZeroMem};
|
mode: ByRef, source: ZeroMem};
|
||||||
bcx = datum.root(bcx, root_info);
|
bcx = datum.root(bcx, br.pats[col].span, root_info);
|
||||||
// If we kept going, we'd only re-root the same value, so
|
// If we kept going, we'd only re-root the same value, so
|
||||||
// return now.
|
// return now.
|
||||||
return bcx;
|
return bcx;
|
||||||
|
|||||||
@@ -517,7 +517,7 @@ pub impl Datum {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn root(&self, bcx: block, root_info: RootInfo) -> block {
|
fn root(&self, bcx: block, span: span, root_info: RootInfo) -> block {
|
||||||
/*!
|
/*!
|
||||||
*
|
*
|
||||||
* In some cases, borrowck will decide that an @T/@[]/@str
|
* In some cases, borrowck will decide that an @T/@[]/@str
|
||||||
@@ -542,6 +542,12 @@ pub impl Datum {
|
|||||||
// If we need to freeze the box, do that now.
|
// If we need to freeze the box, do that now.
|
||||||
if root_info.freeze.is_some() {
|
if root_info.freeze.is_some() {
|
||||||
// NOTE distinguish the two kinds of freezing here
|
// NOTE distinguish the two kinds of freezing here
|
||||||
|
|
||||||
|
let loc = bcx.sess().parse_sess.cm.lookup_char_pos(span.lo);
|
||||||
|
let line = C_int(bcx.ccx(), loc.line as int);
|
||||||
|
let filename_cstr = C_cstr(bcx.ccx(), @/*bad*/copy loc.file.name);
|
||||||
|
let filename = PointerCast(bcx, filename_cstr, T_ptr(T_i8()));
|
||||||
|
|
||||||
callee::trans_lang_call(
|
callee::trans_lang_call(
|
||||||
bcx,
|
bcx,
|
||||||
bcx.tcx().lang_items.borrow_as_imm_fn(),
|
bcx.tcx().lang_items.borrow_as_imm_fn(),
|
||||||
@@ -549,7 +555,9 @@ pub impl Datum {
|
|||||||
Load(bcx,
|
Load(bcx,
|
||||||
PointerCast(bcx,
|
PointerCast(bcx,
|
||||||
scratch.val,
|
scratch.val,
|
||||||
T_ptr(T_ptr(T_i8()))))
|
T_ptr(T_ptr(T_i8())))),
|
||||||
|
filename,
|
||||||
|
line
|
||||||
],
|
],
|
||||||
expr::Ignore)
|
expr::Ignore)
|
||||||
} else {
|
} else {
|
||||||
@@ -647,7 +655,7 @@ pub impl Datum {
|
|||||||
let key = root_map_key { id: expr_id, derefs: derefs };
|
let key = root_map_key { id: expr_id, derefs: derefs };
|
||||||
let bcx = match ccx.maps.root_map.find(&key) {
|
let bcx = match ccx.maps.root_map.find(&key) {
|
||||||
None => bcx,
|
None => bcx,
|
||||||
Some(&root_info) => self.root(bcx, root_info)
|
Some(&root_info) => self.root(bcx, span, root_info)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Perform the write guard, if necessary.
|
// Perform the write guard, if necessary.
|
||||||
|
|||||||
@@ -828,7 +828,7 @@ fn trans_lvalue_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock {
|
|||||||
// at the end of the scope with id `scope_id`:
|
// at the end of the scope with id `scope_id`:
|
||||||
let root_key = root_map_key { id: expr.id, derefs: 0u };
|
let root_key = root_map_key { id: expr.id, derefs: 0u };
|
||||||
for bcx.ccx().maps.root_map.find(&root_key).each |&root_info| {
|
for bcx.ccx().maps.root_map.find(&root_key).each |&root_info| {
|
||||||
bcx = unrooted_datum.root(bcx, *root_info);
|
bcx = unrooted_datum.root(bcx, expr.span, *root_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
return DatumBlock {bcx: bcx, datum: unrooted_datum};
|
return DatumBlock {bcx: bcx, datum: unrooted_datum};
|
||||||
|
|||||||
@@ -683,6 +683,20 @@ rust_task_local_data_atexit(rust_task *task, void (*cleanup_fn)(void *data)) {
|
|||||||
task->task_local_data_cleanup = cleanup_fn;
|
task->task_local_data_cleanup = cleanup_fn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set/get/atexit task_borrow_list can run on the rust stack for speed.
|
||||||
|
extern "C" void *
|
||||||
|
rust_take_task_borrow_list(rust_task *task) {
|
||||||
|
void *r = task->borrow_list;
|
||||||
|
task->borrow_list = NULL;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
extern "C" void
|
||||||
|
rust_set_task_borrow_list(rust_task *task, void *data) {
|
||||||
|
assert(task->borrow_list == NULL);
|
||||||
|
assert(data != NULL);
|
||||||
|
task->borrow_list = data;
|
||||||
|
}
|
||||||
|
|
||||||
extern "C" void
|
extern "C" void
|
||||||
task_clear_event_reject(rust_task *task) {
|
task_clear_event_reject(rust_task *task) {
|
||||||
task->clear_event_reject();
|
task->clear_event_reject();
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
#define RUST_SEED "RUST_SEED"
|
#define RUST_SEED "RUST_SEED"
|
||||||
#define RUST_POISON_ON_FREE "RUST_POISON_ON_FREE"
|
#define RUST_POISON_ON_FREE "RUST_POISON_ON_FREE"
|
||||||
#define RUST_DEBUG_MEM "RUST_DEBUG_MEM"
|
#define RUST_DEBUG_MEM "RUST_DEBUG_MEM"
|
||||||
|
#define RUST_DEBUG_BORROWS "RUST_DEBUG_BORROWS"
|
||||||
|
|
||||||
#if defined(__WIN32__)
|
#if defined(__WIN32__)
|
||||||
static int
|
static int
|
||||||
@@ -130,6 +131,7 @@ load_env(int argc, char **argv) {
|
|||||||
env->argc = argc;
|
env->argc = argc;
|
||||||
env->argv = argv;
|
env->argv = argv;
|
||||||
env->debug_mem = getenv(RUST_DEBUG_MEM) != NULL;
|
env->debug_mem = getenv(RUST_DEBUG_MEM) != NULL;
|
||||||
|
env->debug_borrows = getenv(RUST_DEBUG_BORROWS) != NULL;
|
||||||
return env;
|
return env;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ struct rust_env {
|
|||||||
int argc;
|
int argc;
|
||||||
char **argv;
|
char **argv;
|
||||||
rust_bool debug_mem;
|
rust_bool debug_mem;
|
||||||
|
rust_bool debug_borrows;
|
||||||
};
|
};
|
||||||
|
|
||||||
rust_env* load_env(int argc, char **argv);
|
rust_env* load_env(int argc, char **argv);
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ rust_task::rust_task(rust_sched_loop *sched_loop, rust_task_state state,
|
|||||||
total_stack_sz(0),
|
total_stack_sz(0),
|
||||||
task_local_data(NULL),
|
task_local_data(NULL),
|
||||||
task_local_data_cleanup(NULL),
|
task_local_data_cleanup(NULL),
|
||||||
|
borrow_list(NULL),
|
||||||
state(state),
|
state(state),
|
||||||
cond(NULL),
|
cond(NULL),
|
||||||
cond_name("none"),
|
cond_name("none"),
|
||||||
@@ -75,6 +76,16 @@ rust_task::delete_this()
|
|||||||
assert(ref_count == 0); // ||
|
assert(ref_count == 0); // ||
|
||||||
// (ref_count == 1 && this == sched->root_task));
|
// (ref_count == 1 && this == sched->root_task));
|
||||||
|
|
||||||
|
if (borrow_list) {
|
||||||
|
// NOTE should free borrow_list from within rust code!
|
||||||
|
// If there is a pointer in there, it is a ~[BorrowRecord] pointer,
|
||||||
|
// which are currently allocated with LIBC malloc/free. But this is
|
||||||
|
// not really the right way to do this, we should be freeing this
|
||||||
|
// pointer from Rust code.
|
||||||
|
free(borrow_list);
|
||||||
|
borrow_list = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
sched_loop->release_task(this);
|
sched_loop->release_task(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -241,6 +241,11 @@ rust_task : public kernel_owned<rust_task>
|
|||||||
void *task_local_data;
|
void *task_local_data;
|
||||||
void (*task_local_data_cleanup)(void *data);
|
void (*task_local_data_cleanup)(void *data);
|
||||||
|
|
||||||
|
// Contains a ~[BorrowRecord] pointer, or NULL.
|
||||||
|
//
|
||||||
|
// Used by borrow management code in libcore/unstable/lang.rs.
|
||||||
|
void *borrow_list;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
// Protects state, cond, cond_name
|
// Protects state, cond, cond_name
|
||||||
|
|||||||
Reference in New Issue
Block a user