exception.rs 8.23 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
22
use runtime::*;

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

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

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

        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);

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

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

qinsoon's avatar
qinsoon committed
92
93
            // Check for a catch block at this callsite
            // (there won't be one on the first iteration of this loop)
94
95
96
            if callsite_info.exceptional_destination.is_some() {
                catch_address = callsite_info.exceptional_destination.unwrap();
                trace!("Found catch block: 0x{:x}", catch_address);
qinsoon's avatar
qinsoon committed
97
98
                sp = get_previous_stack_pointer(
                    current_frame_pointer,
99
                    callsite_info.stack_args_size
qinsoon's avatar
qinsoon committed
100
                );
101
102
103
104
105
106
107
108
                trace!("\tRestoring SP to: 0x{:x}", sp);

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

                break; // Found a catch block
109
            }
110

111
112
113
114
            // 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)
115
116
                    let val = (previous_frame_pointer + *source_offset).load::<Address>();
                    (frame_cursor + *target_offset).store::<Address>(val);
117
                }
118
119
            }

120
121
122
            // Move up to the previous frame
            current_frame_pointer = previous_frame_pointer;
            previous_frame_pointer = get_previous_frame_pointer(current_frame_pointer);
123

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

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

    }
}

qinsoon's avatar
qinsoon committed
156
157
/// This function may segfault or panic when it reaches the bottom of the stack
//  TODO: Determine where the bottom is without segfaulting
158
fn print_backtrace(base: Address, compiled_callsite_table: &HashMap<Address, CompiledCallsite>) {
159
160
161
162
    error!("BACKTRACE: ");

    let cur_thread = thread::MuThread::current();
    let ref vm = cur_thread.vm;
163
164
    // compiled_funcs: RwLock<HashMap<MuID, RwLock<CompiledFunction>>>;
    let compiled_funcs = vm.compiled_funcs().read().unwrap();
165
166
167
168
169
170
    let mut frame_pointer = base;
    let mut frame_count = 0;

    loop {
        let callsite = get_return_address(frame_pointer);

171
        if compiled_callsite_table.contains_key(&callsite) {
qinsoon's avatar
qinsoon committed
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
            let function_version = compiled_callsite_table
                .get(&callsite)
                .unwrap()
                .function_version;
            let compiled_func = compiled_funcs
                .get(&function_version)
                .unwrap()
                .read()
                .unwrap();

            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
            );
191
192
        } else {
            let (func_name, func_start) = get_function_info(callsite);
qinsoon's avatar
qinsoon committed
193
194
195
196
197
198
199
            error!(
                "\tframe {:2}: 0x{:x} - {} at 0x{:x}",
                frame_count,
                func_start,
                func_name,
                callsite
            );
200
201
202
203
        }

        frame_pointer = get_previous_frame_pointer(frame_pointer);
        if frame_pointer.is_zero() {
204
            return;
205
206
207
        }
        frame_count += 1;
    }
qinsoon's avatar
qinsoon committed
208
}