exception.rs 7.1 KB
Newer Older
Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// Copyright 2017 The Australian National University
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
//     http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

15 16 17 18 19
use compiler::backend::*;
use utils::Address;
use utils::POINTER_SIZE;
use runtime::*;

qinsoon's avatar
qinsoon committed
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
/// runtime function to deal with exception (unwind stack, find catch block, and restore)
/// This function is called by muentry_throw_exception() which gets emitted for THROW instruction
/// With the first argument being the address of the exception object,
/// And the second argument should be point to the base of the call frame of muentry_throw_exception,
/// which saves every callee saved register (note this frame will be modified by this function).
/// e.g. on aarch64 (where the values are the value of the registers immediately before the first
/// instruction in muentry_throw_exception is executed):
///                  Return Address              (value of X30)
/// frame_cursor --> Frame Pointer               (value of X29)
///                  First Callee Saved Register (value of X19)
///                  .........
///                  Last Callee Saved Register  (value of D15)
/// The actual offsets of the callee saved registers is determined by get_callee_saved_offset
/// (relative to frame_cursor)
/// The location of Frame Pointer and Return address is architecture dependent
/// (and are accessed by get/set_return_address and get/set_previous_frame and may be passed
/// real frame pointers or the frame cursor)
37 38 39 40 41 42 43 44 45 46
#[no_mangle]
pub extern fn throw_exception_internal(exception_obj: Address, frame_cursor: Address) -> !  {
    trace!("throwing exception: {}", exception_obj);

    if cfg!(debug_assertions) {
        trace!("Initial Frame: ");
        print_frame(frame_cursor);
    }

    let ref mut cur_thread = thread::MuThread::current_mut();
qinsoon's avatar
qinsoon committed
47 48

    // set exception object (the catch block will have a landing pad to fetch this object)
49 50
    cur_thread.exception_obj = exception_obj;

qinsoon's avatar
qinsoon committed
51 52 53
    let ref vm = cur_thread.vm;
    // this will be 16 bytes bellow the bottom of the previous frame
    let mut current_frame_pointer = frame_cursor;
54
    let mut callsite = get_return_address(current_frame_pointer);
qinsoon's avatar
qinsoon committed
55 56
    // thrower's fp, the starting point of the previous frame
    let mut previous_frame_pointer = get_previous_frame_pointer(current_frame_pointer);
57

58 59 60
    // acquire lock for exception table
    let compiled_exception_table = vm.compiled_exception_table.read().unwrap();

61 62 63 64 65 66
    loop {
        // Lookup the table for the callsite
        trace!("Callsite: 0x{:x}", callsite);
        trace!("\tprevious_frame_pointer: 0x{:x}", previous_frame_pointer);
        trace!("\tcurrent_frame_pointer: 0x{:x}", current_frame_pointer);

67 68
        let &(catch_address, compiled_func) = {
            let table_entry = compiled_exception_table.get(&callsite);
69

70
            if table_entry.is_none() {
qinsoon's avatar
qinsoon committed
71 72 73 74 75
                // we are not dealing with native frames for unwinding stack
                // See Issue #42
                error!("Cannot find Mu callsite (i.e. we have reached a native frame), \
                    either there isn't a catch block to catch the exception or \
                    your catch block is above a native function call");
76
                print_backtrace(frame_cursor);
qinsoon's avatar
qinsoon committed
77
                // The above function will not return
78
            }
79

80 81
            table_entry.unwrap()
        };
82 83 84 85 86 87 88 89 90 91 92 93 94

        // Check for a catch block at this callsite (there won't be one on the first iteration of this loop)
        if !catch_address.is_zero() {
            trace!("Found catch block: 0x{:x}", catch_address);
            let sp = get_previous_stack_pointer(current_frame_pointer);
            trace!("\tRestoring SP to: 0x{:x}", sp);

            if cfg!(debug_assertions) {
                trace!("Restoring frame: ");
                print_frame(frame_cursor);
            }

            // Found a catch block, branch to it
95
            drop(compiled_exception_table);    // drop the lock first
96 97 98 99 100 101 102 103 104
            unsafe { thread::exception_restore(catch_address, frame_cursor.to_ptr(), sp); }
        }

        // Restore callee saved registers
        unsafe {
            let ref cf = *compiled_func;
            let ref callee_saved = cf.frame.callee_saved;
            for (target_offset, source_offset) in callee_saved {
                // *(frame_cursor + target_offset) = *(frame_pointer + source_offset)
105 106
                let val = (previous_frame_pointer + *source_offset).load::<Address>();
                (frame_cursor + *target_offset).store::<Address>(val);
107 108 109 110 111 112 113 114 115 116 117 118 119 120
            }
        }

        // Move up to the previous frame
        current_frame_pointer = previous_frame_pointer;
        previous_frame_pointer = get_previous_frame_pointer(current_frame_pointer);

        // Restore the callsite
        callsite = get_return_address(current_frame_pointer);
        set_return_address(frame_cursor, callsite);
        set_previous_frame_pointer(frame_cursor, previous_frame_pointer);
    }
}

qinsoon's avatar
qinsoon committed
121 122
/// prints current frame cursor
fn print_frame(cursor: Address) {
123 124 125 126
    let top = 2;
    let bottom = -(CALLEE_SAVED_COUNT as isize);
    for i in (bottom .. top).rev() {
        unsafe {
127
            let addr = cursor + (i * POINTER_SIZE as isize);
128
            let val  = addr.load::<Word>();
qinsoon's avatar
qinsoon committed
129
            trace!("\taddr: 0x{:x} | val: 0x{:x} {}", addr, val, {if addr == cursor {"<- cursor"} else {""}});
130 131 132 133 134
        }

    }
}

qinsoon's avatar
qinsoon committed
135 136
/// This function may segfault or panic when it reaches the bottom of the stack
//  TODO: Determine where the bottom is without segfaulting
137 138 139 140 141 142 143 144 145
fn print_backtrace(base: Address) -> !{
    error!("BACKTRACE: ");

    let cur_thread = thread::MuThread::current();
    let ref vm = cur_thread.vm;

    let mut frame_pointer = base;
    let mut frame_count = 0;

146 147
    let compiled_exception_table = vm.compiled_exception_table.read().unwrap();

148 149 150
    loop {
        let callsite = get_return_address(frame_pointer);

151 152
        if compiled_exception_table.contains_key(&callsite) {
            let &(_, compiled_func_ptr) = compiled_exception_table.get(&callsite).unwrap();
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172

            unsafe {
                let ref compiled_func = *compiled_func_ptr;

                error!("\tframe {:2}: 0x{:x} - {} (fid: #{}, fvid: #{}) at 0x{:x}", frame_count,
                compiled_func.start.to_address(), vm.name_of(compiled_func.func_id),
                compiled_func.func_id, compiled_func.func_ver_id, callsite);
            }
        } else {
            let (func_name, func_start) = get_function_info(callsite);
            error!("\tframe {:2}: 0x{:x} - {} at 0x{:x}", frame_count, func_start, func_name, callsite);
        }

        frame_pointer = get_previous_frame_pointer(frame_pointer);
        if frame_pointer.is_zero() {
            panic!("Uncaught Mu Exception");
        }
        frame_count += 1;
    }
}