exception.rs 8.42 KB
Newer Older
Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
1
// Copyright 2017 The Australian National University
qinsoon's avatar
qinsoon committed
2
//
Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
3 4 5
// 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
qinsoon's avatar
qinsoon committed
6
//
Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
7
//     http://www.apache.org/licenses/LICENSE-2.0
qinsoon's avatar
qinsoon committed
8
//
Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
9 10 11 12 13 14
// 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
use compiler::backend::*;
use utils::Address;
use utils::POINTER_SIZE;
18 19 20
use std::collections::HashMap;
use std::ops::Deref;
use compiler::machine_code::CompiledCallsite;
21
use runtime::*;
22
use log;
23

qinsoon's avatar
qinsoon committed
24 25
/// 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
qinsoon's avatar
qinsoon committed
26 27
/// 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,
qinsoon's avatar
qinsoon committed
28 29 30 31 32 33 34 35 36 37 38 39 40
/// 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)
41
#[no_mangle]
42
pub extern "C" fn throw_exception_internal(exception_obj: Address, frame_cursor: Address) -> ! {
43
    debug!("throwing exception: {}", exception_obj);
44 45 46 47 48 49 50

    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
51 52

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

qinsoon's avatar
qinsoon committed
55 56 57
    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;
58
    let mut callsite = get_return_address(current_frame_pointer);
qinsoon's avatar
qinsoon committed
59
    // thrower's fp, the starting point of the previous frame
qinsoon's avatar
qinsoon committed
60
    let mut previous_frame_pointer = get_previous_frame_pointer(current_frame_pointer);
61 62 63 64
    // the address of the catch block
    let catch_address;
    // the stack pointer to restore to
    let sp;
65 66
    {
        // acquire lock for exception table
67
        let compiled_callsite_table = vm.compiled_callsite_table().read().unwrap();
68

69
        print_backtrace(frame_cursor, compiled_callsite_table.deref());
70
        loop {
71

72 73 74 75 76 77 78 79 80
            // 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);

            let callsite_info = {
                let table_entry = compiled_callsite_table.get(&callsite);

                if table_entry.is_none() {
81 82
                    // we are not dealing with native frames for unwinding stack
                    // See Issue #42
qinsoon's avatar
qinsoon committed
83 84 85 86 87
                    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"
                    );
88 89 90 91 92
                    panic!("Uncaught Mu Exception");
                }
                table_entry.unwrap()
            };

qinsoon's avatar
qinsoon committed
93
            // Check for a catch block at this callsite
94 95
            if callsite_info.exceptional_destination.is_some() {
                catch_address = callsite_info.exceptional_destination.unwrap();
Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
96 97 98 99 100
                debug!(
                    "Found catch block: 0x{:x} - {}",
                    catch_address,
                    get_symbol_name(catch_address)
                );
qinsoon's avatar
qinsoon committed
101 102
                sp = get_previous_stack_pointer(
                    current_frame_pointer,
103
                    callsite_info.stack_args_size
qinsoon's avatar
qinsoon committed
104
                );
105 106 107 108 109 110 111 112
                trace!("\tRestoring SP to: 0x{:x}", sp);

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

                break; // Found a catch block
113
            }
114

115 116 117 118
            // Restore callee saved registers
            unsafe {
                for (target_offset, source_offset) in callsite_info.callee_saved_registers.iter() {
                    // *(frame_cursor + target_offset) = *(frame_pointer + source_offset)
119 120
                    let val = (previous_frame_pointer + *source_offset).load::<Address>();
                    (frame_cursor + *target_offset).store::<Address>(val);
121
                }
122 123
            }

124 125 126
            // Move up to the previous frame
            current_frame_pointer = previous_frame_pointer;
            previous_frame_pointer = get_previous_frame_pointer(current_frame_pointer);
127

128 129 130 131
            // 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);
132 133
        }
    }
134
    // The above loop will only exit when a catch block is found, so restore to it
qinsoon's avatar
qinsoon committed
135 136 137
    unsafe {
        thread::exception_restore(catch_address, frame_cursor.to_ptr(), sp);
    }
138 139
}

qinsoon's avatar
qinsoon committed
140 141
/// prints current frame cursor
fn print_frame(cursor: Address) {
142 143
    let top = 2;
    let bottom = -(CALLEE_SAVED_COUNT as isize);
qinsoon's avatar
qinsoon committed
144
    for i in (bottom..top).rev() {
145
        unsafe {
146
            let addr = cursor + (i * POINTER_SIZE as isize);
qinsoon's avatar
qinsoon committed
147 148 149 150 151 152 153 154
            let val = addr.load::<Word>();
            trace!("\taddr: 0x{:x} | val: 0x{:x} {}", addr, val, {
                if addr == cursor {
                    "<- cursor"
                } else {
                    ""
                }
            });
155 156 157 158 159
        }

    }
}

qinsoon's avatar
qinsoon committed
160 161
/// This function may segfault or panic when it reaches the bottom of the stack
//  TODO: Determine where the bottom is without segfaulting
162
fn print_backtrace(base: Address, compiled_callsite_table: &HashMap<Address, CompiledCallsite>) {
163 164 165
    if log::max_log_level() < log::LogLevelFilter::Debug {
        return;
    }
Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
166

167
    debug!("BACKTRACE: ");
168 169
    let cur_thread = thread::MuThread::current();
    let ref vm = cur_thread.vm;
170 171
    // compiled_funcs: RwLock<HashMap<MuID, RwLock<CompiledFunction>>>;
    let compiled_funcs = vm.compiled_funcs().read().unwrap();
172 173 174 175 176
    let mut frame_pointer = base;
    let mut frame_count = 0;

    loop {
        let callsite = get_return_address(frame_pointer);
177 178 179 180
        frame_pointer = get_previous_frame_pointer(frame_pointer);
        if frame_pointer.is_zero() {
            return;
        }
181

182
        if compiled_callsite_table.contains_key(&callsite) {
qinsoon's avatar
qinsoon committed
183 184 185 186 187 188 189 190 191 192
            let function_version = compiled_callsite_table
                .get(&callsite)
                .unwrap()
                .function_version;
            let compiled_func = compiled_funcs
                .get(&function_version)
                .unwrap()
                .read()
                .unwrap();

193 194
            debug!(
                "\tframe {:2}: 0x{:x} - {} (fid: #{}, fvid: #{}) at 0x{:x} - {}",
qinsoon's avatar
qinsoon committed
195 196
                frame_count,
                compiled_func.start.to_address(),
197
                vm.get_name_for_func(compiled_func.func_id),
qinsoon's avatar
qinsoon committed
198 199
                compiled_func.func_id,
                compiled_func.func_ver_id,
200 201
                callsite,
                get_symbol_name(callsite)
qinsoon's avatar
qinsoon committed
202
            );
203 204
        } else {
            let (func_name, func_start) = get_function_info(callsite);
205
            debug!(
qinsoon's avatar
qinsoon committed
206 207 208 209 210 211
                "\tframe {:2}: 0x{:x} - {} at 0x{:x}",
                frame_count,
                func_start,
                func_name,
                callsite
            );
Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
212
            debug!("\tother native frames...");
213
            break;
214 215 216 217
        }

        frame_count += 1;
    }
qinsoon's avatar
qinsoon committed
218
}