about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2013-05-01 10:29:47 -0400
committerNiko Matsakis <niko@alum.mit.edu>2013-05-01 10:30:54 -0400
commit4af2d90af59bb5e28e5d114d8a6004d68fad3bd5 (patch)
treec5be284688af69a2843258f71511686827bc21d1
parent84861101eca12942b42f36f8adb18cfc74515431 (diff)
downloadrust-4af2d90af59bb5e28e5d114d8a6004d68fad3bd5.tar.gz
rust-4af2d90af59bb5e28e5d114d8a6004d68fad3bd5.zip
add an option to debug borrows (RUST_DEBUG_BORROW) so you can
find out where the offending borrow occurred. This ... still needs
some work.
-rw-r--r--src/libcore/rt/env.rs6
-rw-r--r--src/libcore/unstable/lang.rs78
-rw-r--r--src/librustc/middle/trans/_match.rs2
-rw-r--r--src/librustc/middle/trans/datum.rs14
-rw-r--r--src/librustc/middle/trans/expr.rs2
-rw-r--r--src/rt/rust_builtin.cpp14
-rw-r--r--src/rt/rust_env.cpp2
-rw-r--r--src/rt/rust_env.h1
-rw-r--r--src/rt/rust_task.cpp11
-rw-r--r--src/rt/rust_task.h5
10 files changed, 121 insertions, 14 deletions
diff --git a/src/libcore/rt/env.rs b/src/libcore/rt/env.rs
index 92e2ec51306..1f52cf77868 100644
--- a/src/libcore/rt/env.rs
+++ b/src/libcore/rt/env.rs
@@ -31,8 +31,10 @@ pub struct Environment {
     argc: c_int,
     /// The argv value passed to main
     argv: **c_char,
-    /// Print GC debugging info
-    debug_mem: bool
+    /// Print GC debugging info (true if env var RUST_DEBUG_MEM is set)
+    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
diff --git a/src/libcore/unstable/lang.rs b/src/libcore/unstable/lang.rs
index c5062f25ea5..0705be7cdef 100644
--- a/src/libcore/unstable/lang.rs
+++ b/src/libcore/unstable/lang.rs
@@ -17,6 +17,7 @@ use str;
 use sys;
 use unstable::exchange_alloc;
 use cast::transmute;
+use task::rt::rust_get_task;
 
 #[allow(non_camel_case_types)]
 pub type rust_task = c_void;
@@ -27,7 +28,8 @@ pub static FROZEN_BIT: uint = 0x80000000;
 pub static FROZEN_BIT: uint = 0x8000000000000000;
 
 pub mod rustrt {
-    use libc::{c_char, uintptr_t};
+    use unstable::lang::rust_task;
+    use libc::{c_void, c_char, uintptr_t};
 
     pub extern {
         #[rust_stack]
@@ -43,6 +45,12 @@ pub mod rustrt {
 
         #[fast_ffi]
         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) {
-    let msg = "borrowed";
-    do str::as_buf(msg) |msg_p, _| {
-        fail_(msg_p as *c_char, file, line);
+struct BorrowRecord {
+    box: *mut BoxRepr,
+    file: *c_char,
+    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);
 }
 
+#[cfg(stage0)]
 #[lang="borrow_as_imm"]
 #[inline(always)]
 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;
 }
 
+#[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"]
 #[inline(always)]
 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);
     if ((*a).header.ref_count & FROZEN_BIT) != 0 {
         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) {
     let a: *mut BoxRepr = transmute(a);
     if ((*a).header.ref_count & FROZEN_BIT) != 0 {
-        fail_borrowed(file, line);
+        fail_borrowed(a, file, line);
     }
 }
 
diff --git a/src/librustc/middle/trans/_match.rs b/src/librustc/middle/trans/_match.rs
index 785bb3edc07..d7f9567c333 100644
--- a/src/librustc/middle/trans/_match.rs
+++ b/src/librustc/middle/trans/_match.rs
@@ -966,7 +966,7 @@ pub fn root_pats_as_necessary(bcx: block,
 
                 let datum = Datum {val: val, ty: node_id_type(bcx, pat_id),
                                    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
                 // return now.
                 return bcx;
diff --git a/src/librustc/middle/trans/datum.rs b/src/librustc/middle/trans/datum.rs
index 03c81cad50e..ae71a3e6c22 100644
--- a/src/librustc/middle/trans/datum.rs
+++ b/src/librustc/middle/trans/datum.rs
@@ -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
@@ -542,6 +542,12 @@ pub impl Datum {
         // If we need to freeze the box, do that now.
         if root_info.freeze.is_some() {
             // 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(
                 bcx,
                 bcx.tcx().lang_items.borrow_as_imm_fn(),
@@ -549,7 +555,9 @@ pub impl Datum {
                     Load(bcx,
                          PointerCast(bcx,
                                      scratch.val,
-                                     T_ptr(T_ptr(T_i8()))))
+                                     T_ptr(T_ptr(T_i8())))),
+                    filename,
+                    line
                 ],
                 expr::Ignore)
         } else {
@@ -647,7 +655,7 @@ pub impl Datum {
         let key = root_map_key { id: expr_id, derefs: derefs };
         let bcx = match ccx.maps.root_map.find(&key) {
             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.
diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs
index bc44d7de983..166b8bc01f8 100644
--- a/src/librustc/middle/trans/expr.rs
+++ b/src/librustc/middle/trans/expr.rs
@@ -828,7 +828,7 @@ fn trans_lvalue_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock {
     // at the end of the scope with id `scope_id`:
     let root_key = root_map_key { id: expr.id, derefs: 0u };
     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};
diff --git a/src/rt/rust_builtin.cpp b/src/rt/rust_builtin.cpp
index ee025a39ff4..197b8b36d2d 100644
--- a/src/rt/rust_builtin.cpp
+++ b/src/rt/rust_builtin.cpp
@@ -683,6 +683,20 @@ rust_task_local_data_atexit(rust_task *task, void (*cleanup_fn)(void *data)) {
     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
 task_clear_event_reject(rust_task *task) {
     task->clear_event_reject();
diff --git a/src/rt/rust_env.cpp b/src/rt/rust_env.cpp
index 041b4efac52..e6fe35609ec 100644
--- a/src/rt/rust_env.cpp
+++ b/src/rt/rust_env.cpp
@@ -24,6 +24,7 @@
 #define RUST_SEED "RUST_SEED"
 #define RUST_POISON_ON_FREE "RUST_POISON_ON_FREE"
 #define RUST_DEBUG_MEM "RUST_DEBUG_MEM"
+#define RUST_DEBUG_BORROWS "RUST_DEBUG_BORROWS"
 
 #if defined(__WIN32__)
 static int
@@ -130,6 +131,7 @@ load_env(int argc, char **argv) {
     env->argc = argc;
     env->argv = argv;
     env->debug_mem = getenv(RUST_DEBUG_MEM) != NULL;
+    env->debug_borrows = getenv(RUST_DEBUG_BORROWS) != NULL;
     return env;
 }
 
diff --git a/src/rt/rust_env.h b/src/rt/rust_env.h
index df27f7674f2..322198bb031 100644
--- a/src/rt/rust_env.h
+++ b/src/rt/rust_env.h
@@ -28,6 +28,7 @@ struct rust_env {
     int argc;
     char **argv;
     rust_bool debug_mem;
+    rust_bool debug_borrows;
 };
 
 rust_env* load_env(int argc, char **argv);
diff --git a/src/rt/rust_task.cpp b/src/rt/rust_task.cpp
index e6293aa5c1d..ea42936f2e5 100644
--- a/src/rt/rust_task.cpp
+++ b/src/rt/rust_task.cpp
@@ -42,6 +42,7 @@ rust_task::rust_task(rust_sched_loop *sched_loop, rust_task_state state,
     total_stack_sz(0),
     task_local_data(NULL),
     task_local_data_cleanup(NULL),
+    borrow_list(NULL),
     state(state),
     cond(NULL),
     cond_name("none"),
@@ -75,6 +76,16 @@ rust_task::delete_this()
     assert(ref_count == 0); // ||
     //   (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);
 }
 
diff --git a/src/rt/rust_task.h b/src/rt/rust_task.h
index 4aa1199cabc..dc45c0439ea 100644
--- a/src/rt/rust_task.h
+++ b/src/rt/rust_task.h
@@ -241,6 +241,11 @@ rust_task : public kernel_owned<rust_task>
     void *task_local_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:
 
     // Protects state, cond, cond_name