gc: Fix for GC missing stack frames across segment boundaries.
This commit is contained in:
parent
578b036f9b
commit
961bd48724
4 changed files with 113 additions and 42 deletions
|
@ -1,19 +1,38 @@
|
|||
import stackwalk::Word;
|
||||
import libc::size_t;
|
||||
import libc::uintptr_t;
|
||||
import send_map::linear::LinearMap;
|
||||
|
||||
export Word;
|
||||
export gc;
|
||||
export cleanup_stack_for_failure;
|
||||
|
||||
// Mirrors rust_stack.h stk_seg
|
||||
struct StackSegment {
|
||||
let prev: *StackSegment;
|
||||
let next: *StackSegment;
|
||||
let end: uintptr_t;
|
||||
// And other fields which we don't care about...
|
||||
}
|
||||
|
||||
extern mod rustrt {
|
||||
fn rust_annihilate_box(ptr: *Word);
|
||||
|
||||
#[rust_stack]
|
||||
fn rust_gc_metadata() -> *Word;
|
||||
fn rust_call_tydesc_glue(root: *Word, tydesc: *Word, field: size_t);
|
||||
|
||||
#[rust_stack]
|
||||
fn rust_call_tydesc_glue(root: *Word, tydesc: *Word, field: size_t);
|
||||
fn rust_gc_metadata() -> *Word;
|
||||
|
||||
fn rust_get_stack_segment() -> *StackSegment;
|
||||
}
|
||||
|
||||
unsafe fn is_frame_in_segment(fp: *Word, segment: *StackSegment) -> bool {
|
||||
let begin: Word = unsafe::reinterpret_cast(&segment);
|
||||
let end: Word = unsafe::reinterpret_cast(&(*segment).end);
|
||||
let frame: Word = unsafe::reinterpret_cast(&fp);
|
||||
|
||||
return begin <= frame && frame <= end;
|
||||
}
|
||||
|
||||
type SafePoint = { sp_meta: *Word, fn_meta: *Word };
|
||||
|
@ -44,6 +63,17 @@ unsafe fn is_safe_point(pc: *Word) -> Option<SafePoint> {
|
|||
|
||||
type Visitor = fn(root: **Word, tydesc: *Word) -> bool;
|
||||
|
||||
unsafe fn bump<T, U>(ptr: *T, count: uint) -> *U {
|
||||
return unsafe::reinterpret_cast(&ptr::offset(ptr, count));
|
||||
}
|
||||
|
||||
unsafe fn align_to_pointer<T>(ptr: *T) -> *T {
|
||||
let align = sys::min_align_of::<*T>();
|
||||
let ptr: uint = unsafe::reinterpret_cast(&ptr);
|
||||
let ptr = (ptr + (align - 1)) & -align;
|
||||
return unsafe::reinterpret_cast(&ptr);
|
||||
}
|
||||
|
||||
unsafe fn walk_safe_point(fp: *Word, sp: SafePoint, visitor: Visitor) {
|
||||
let fp_bytes: *u8 = unsafe::reinterpret_cast(&fp);
|
||||
let sp_meta_u32s: *u32 = unsafe::reinterpret_cast(&sp.sp_meta);
|
||||
|
@ -97,7 +127,35 @@ const stack: Memory = 4;
|
|||
|
||||
const need_cleanup: Memory = exchange_heap | stack;
|
||||
|
||||
unsafe fn find_segment_for_frame(fp: *Word, segment: *StackSegment)
|
||||
-> {segment: *StackSegment, boundary: bool} {
|
||||
// Check if frame is in either current frame or previous frame.
|
||||
let in_segment = is_frame_in_segment(fp, segment);
|
||||
let in_prev_segment = ptr::is_not_null((*segment).prev) &&
|
||||
is_frame_in_segment(fp, (*segment).prev);
|
||||
|
||||
// If frame is not in either segment, walk down segment list until
|
||||
// we find the segment containing this frame.
|
||||
if !in_segment && !in_prev_segment {
|
||||
let mut segment = segment;
|
||||
while ptr::is_not_null((*segment).next) &&
|
||||
is_frame_in_segment(fp, (*segment).next) {
|
||||
segment = (*segment).next;
|
||||
}
|
||||
return {segment: segment, boundary: false};
|
||||
}
|
||||
|
||||
// If frame is in previous frame, then we're at a boundary.
|
||||
if !in_segment && in_prev_segment {
|
||||
return {segment: (*segment).prev, boundary: true};
|
||||
}
|
||||
|
||||
// Otherwise, we're somewhere on the inside of the frame.
|
||||
return {segment: segment, boundary: false};
|
||||
}
|
||||
|
||||
unsafe fn walk_gc_roots(mem: Memory, sentinel: **Word, visitor: Visitor) {
|
||||
let mut segment = rustrt::rust_get_stack_segment();
|
||||
let mut last_ret: *Word = ptr::null();
|
||||
// To avoid collecting memory used by the GC itself, skip stack
|
||||
// frames until past the root GC stack frame. The root GC stack
|
||||
|
@ -106,48 +164,55 @@ unsafe fn walk_gc_roots(mem: Memory, sentinel: **Word, visitor: Visitor) {
|
|||
let mut reached_sentinel = ptr::is_null(sentinel);
|
||||
for stackwalk::walk_stack |frame| {
|
||||
unsafe {
|
||||
let pc = last_ret;
|
||||
let {segment: next_segment, boundary: boundary} =
|
||||
find_segment_for_frame(frame.fp, segment);
|
||||
segment = next_segment;
|
||||
let ret_offset = if boundary { 4 } else { 1 };
|
||||
last_ret = *ptr::offset(frame.fp, ret_offset) as *Word;
|
||||
|
||||
if ptr::is_null(pc) {
|
||||
again;
|
||||
}
|
||||
|
||||
let mut delay_reached_sentinel = reached_sentinel;
|
||||
if ptr::is_not_null(last_ret) {
|
||||
let sp = is_safe_point(last_ret);
|
||||
match sp {
|
||||
Some(sp_info) => {
|
||||
for walk_safe_point(frame.fp, sp_info) |root, tydesc| {
|
||||
// Skip roots until we see the sentinel.
|
||||
if !reached_sentinel {
|
||||
if root == sentinel {
|
||||
delay_reached_sentinel = true;
|
||||
}
|
||||
again;
|
||||
let sp = is_safe_point(pc);
|
||||
match sp {
|
||||
Some(sp_info) => {
|
||||
for walk_safe_point(frame.fp, sp_info) |root, tydesc| {
|
||||
// Skip roots until we see the sentinel.
|
||||
if !reached_sentinel {
|
||||
if root == sentinel {
|
||||
delay_reached_sentinel = true;
|
||||
}
|
||||
again;
|
||||
}
|
||||
|
||||
// Skip null pointers, which can occur when a
|
||||
// unique pointer has already been freed.
|
||||
if ptr::is_null(*root) {
|
||||
again;
|
||||
// Skip null pointers, which can occur when a
|
||||
// unique pointer has already been freed.
|
||||
if ptr::is_null(*root) {
|
||||
again;
|
||||
}
|
||||
|
||||
if ptr::is_null(tydesc) {
|
||||
// Root is a generic box.
|
||||
let refcount = **root;
|
||||
if mem | task_local_heap != 0 && refcount != -1 {
|
||||
if !visitor(root, tydesc) { return; }
|
||||
} else if mem | exchange_heap != 0 && refcount == -1 {
|
||||
if !visitor(root, tydesc) { return; }
|
||||
}
|
||||
|
||||
if ptr::is_null(tydesc) {
|
||||
// Root is a generic box.
|
||||
let refcount = **root;
|
||||
if mem | task_local_heap != 0 && refcount != -1 {
|
||||
if !visitor(root, tydesc) { return; }
|
||||
} else if mem | exchange_heap != 0
|
||||
&& refcount == -1 {
|
||||
if !visitor(root, tydesc) { return; }
|
||||
}
|
||||
} else {
|
||||
// Root is a non-immediate.
|
||||
if mem | stack != 0 {
|
||||
if !visitor(root, tydesc) { return; }
|
||||
}
|
||||
} else {
|
||||
// Root is a non-immediate.
|
||||
if mem | stack != 0 {
|
||||
if !visitor(root, tydesc) { return; }
|
||||
}
|
||||
}
|
||||
}
|
||||
None => ()
|
||||
}
|
||||
}
|
||||
None => ()
|
||||
}
|
||||
reached_sentinel = delay_reached_sentinel;
|
||||
last_ret = *ptr::offset(frame.fp, 1) as *Word;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -610,6 +610,11 @@ rust_get_task() {
|
|||
return rust_get_current_task();
|
||||
}
|
||||
|
||||
extern "C" CDECL stk_seg *
|
||||
rust_get_stack_segment() {
|
||||
return rust_get_current_task()->stk;
|
||||
}
|
||||
|
||||
extern "C" CDECL void
|
||||
start_task(rust_task *target, fn_env_pair *f) {
|
||||
target->start(f->f, f->env, NULL);
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
#include <vector>
|
||||
|
||||
struct safe_point {
|
||||
size_t safe_point_loc;
|
||||
size_t safe_point_meta;
|
||||
size_t function_meta;
|
||||
uintptr_t safe_point_loc;
|
||||
uintptr_t safe_point_meta;
|
||||
uintptr_t function_meta;
|
||||
};
|
||||
|
||||
struct update_gc_entry_args {
|
||||
|
@ -19,7 +19,7 @@ static void
|
|||
update_gc_entry(const mod_entry* entry, void *cookie) {
|
||||
update_gc_entry_args *args = (update_gc_entry_args *)cookie;
|
||||
if (!strcmp(entry->name, "_gc_module_metadata")) {
|
||||
size_t *next = entry->state;
|
||||
uintptr_t *next = entry->state;
|
||||
uint32_t num_safe_points = *(uint32_t *)next;
|
||||
next++;
|
||||
|
||||
|
@ -37,7 +37,7 @@ cmp_safe_point(safe_point a, safe_point b) {
|
|||
return a.safe_point_loc < b.safe_point_loc;
|
||||
}
|
||||
|
||||
size_t *global_safe_points = 0;
|
||||
uintptr_t *global_safe_points = 0;
|
||||
|
||||
void
|
||||
update_gc_metadata(const void* map) {
|
||||
|
@ -50,10 +50,10 @@ update_gc_metadata(const void* map) {
|
|||
|
||||
// Serialize safe point list into format expected by runtime.
|
||||
global_safe_points =
|
||||
(size_t *)malloc((safe_points.size()*3 + 1)*sizeof(size_t));
|
||||
(uintptr_t *)malloc((safe_points.size()*3 + 1)*sizeof(uintptr_t));
|
||||
if (!global_safe_points) return;
|
||||
|
||||
size_t *next = global_safe_points;
|
||||
uintptr_t *next = global_safe_points;
|
||||
*(uint32_t *)next = safe_points.size();
|
||||
next++;
|
||||
for (uint32_t i = 0; i < safe_points.size(); i++) {
|
||||
|
|
|
@ -54,6 +54,7 @@ rust_env_pairs
|
|||
rust_task_yield
|
||||
rust_task_is_unwinding
|
||||
rust_get_task
|
||||
rust_get_stack_segment
|
||||
rust_task_weaken
|
||||
rust_task_unweaken
|
||||
sched_threads
|
||||
|
|
Loading…
Add table
Reference in a new issue