diff options
Diffstat (limited to 'erts/emulator/beam/beam_emu.c')
-rw-r--r-- | erts/emulator/beam/beam_emu.c | 275 |
1 files changed, 140 insertions, 135 deletions
diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 48824ef9da..5ff4549818 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -111,10 +111,9 @@ do { \ #define CHECK_ALIGNED(Dst) ASSERT((((Uint)&Dst) & (sizeof(Uint)-1)) == 0) -#define GET_BIF_MODULE(p) ((p)->info.mfa.module) -#define GET_BIF_FUNCTION(p) ((p)->info.mfa.function) -#define GET_BIF_ARITY(p) ((p)->info.mfa.arity) -#define GET_BIF_ADDRESS(p) ((BifFunction)((p)->trampoline.bif.func)) +#define GET_EXPORT_MODULE(p) ((p)->info.mfa.module) +#define GET_EXPORT_FUNCTION(p) ((p)->info.mfa.function) +#define GET_EXPORT_ARITY(p) ((p)->info.mfa.arity) #define TermWords(t) (((t) / (sizeof(BeamInstr)/sizeof(Eterm))) + !!((t) % (sizeof(BeamInstr)/sizeof(Eterm)))) @@ -296,6 +295,7 @@ do { \ */ static void init_emulator_finish(void) ERTS_NOINLINE; static ErtsCodeMFA *ubif2mfa(void* uf) ERTS_NOINLINE; +static BeamInstr *printable_return_address(Process* p, Eterm *E) ERTS_NOINLINE; static BeamInstr* handle_error(Process* c_p, BeamInstr* pc, Eterm* reg, ErtsCodeMFA* bif_mfa) ERTS_NOINLINE; static BeamInstr* call_error_handler(Process* p, ErtsCodeMFA* mfa, @@ -888,15 +888,52 @@ void process_main(Eterm * x_reg_array, FloatDef* f_reg_array) } /* + * Enter all BIFs into the export table. + * + * Note that they will all call the error_handler until their modules have been + * loaded, which may prevent the system from booting if BIFs from non-preloaded + * modules are apply/3'd while loading code. Ordinary BIF calls will work fine + * however since they won't go through export entries. + */ +static void install_bifs(void) { + int i; + + for (i = 0; i < BIF_SIZE; i++) { + BifEntry *entry; + Export *ep; + int j; + + entry = &bif_table[i]; + + ep = erts_export_put(entry->module, entry->name, entry->arity); + + ep->info.op = BeamOpCodeAddr(op_i_func_info_IaaI); + ep->info.mfa.module = entry->module; + ep->info.mfa.function = entry->name; + ep->info.mfa.arity = entry->arity; + ep->bif_table_index = i; + + memset(&ep->trampoline, 0, sizeof(ep->trampoline)); + ep->trampoline.op = BeamOpCodeAddr(op_call_error_handler); + + for (j = 0; j < ERTS_NUM_CODE_IX; j++) { + ep->addressv[j] = ep->trampoline.raw; + } + + bif_export[i] = ep; + } +} + +/* * One-time initialization of emulator. Does not need to be * in process_main(). */ static void init_emulator_finish(void) { +#if defined(ARCH_64) && defined(CODE_MODEL_SMALL) int i; -#if defined(ARCH_64) && defined(CODE_MODEL_SMALL) for (i = 0; i < NUMBER_OF_OPCODES; i++) { BeamInstr instr = BeamOpCodeAddr(i); if (instr >= (1ull << 32)) { @@ -917,24 +954,7 @@ init_emulator_finish(void) beam_exception_trace[0] = BeamOpCodeAddr(op_return_trace); /* UGLY */ beam_return_time_trace[0] = BeamOpCodeAddr(op_i_return_time_trace); - /* - * Enter all BIFs into the export table. - */ - for (i = 0; i < BIF_SIZE; i++) { - Export *ep = erts_export_put(bif_table[i].module, - bif_table[i].name, - bif_table[i].arity); - - ep->info.op = BeamOpCodeAddr(op_i_func_info_IaaI); - ep->info.mfa.module = bif_table[i].module; - ep->info.mfa.function = bif_table[i].name; - ep->info.mfa.arity = bif_table[i].arity; - - ep->trampoline.op = BeamOpCodeAddr(op_apply_bif); - ep->trampoline.bif.func = (BeamInstr) bif_table[i].f; - - bif_export[i] = ep; - } + install_bifs(); } /* @@ -1234,6 +1254,33 @@ Eterm error_atom[NUMBER_EXIT_CODES] = { am_badkey, /* 19 */ }; +/* Returns the return address at E[0] in printable form, skipping tracing in + * the same manner as gather_stacktrace. + * + * This is needed to generate correct stacktraces when throwing errors from + * instructions that return like an ordinary function, such as call_nif. */ +static BeamInstr *printable_return_address(Process* p, Eterm *E) { + Eterm *ptr = E; + + ASSERT(is_CP(*ptr)); + + while (ptr < STACK_START(p)) { + BeamInstr *cp = cp_val(*ptr); + + if (cp == beam_exception_trace || cp == beam_return_trace) { + ptr += 3; + } else if (cp == beam_return_time_trace) { + ptr += 2; + } else if (cp == beam_return_to_trace) { + ptr += 1; + } else { + return cp; + } + } + + ERTS_ASSERT(!"No continuation pointer on stack"); +} + /* * To fully understand the error handling, one must keep in mind that * when an exception is thrown, the search for a handler can jump back @@ -1527,19 +1574,24 @@ expand_error_value(Process* c_p, Uint freason, Eterm Value) { static void -gather_stacktrace(Process* p, Eterm *ptr, struct StackTrace* s, int depth) +gather_stacktrace(Process* p, struct StackTrace* s, int depth) { - BeamInstr *prev; - BeamInstr i_return_trace; + BeamInstr i_return_time_trace; BeamInstr i_return_to_trace; + BeamInstr i_return_trace; + BeamInstr *prev; + Eterm *ptr; if (depth == 0) { return; } - prev = s->depth ? s->trace[s->depth-1] : s->pc; - i_return_trace = beam_return_trace[0]; + i_return_time_trace = beam_return_time_trace[0]; i_return_to_trace = beam_return_to_trace[0]; + i_return_trace = beam_return_trace[0]; + + prev = s->depth ? s->trace[s->depth-1] : s->pc; + ptr = p->stop; /* * Traverse the stack backwards and add all unique continuation @@ -1552,16 +1604,15 @@ gather_stacktrace(Process* p, Eterm *ptr, struct StackTrace* s, int depth) while (ptr < STACK_START(p) && depth > 0) { if (is_CP(*ptr)) { - if (*cp_val(*ptr) == i_return_trace) { - /* Skip stack frame variables */ - do ++ptr; while (is_not_CP(*ptr)); - /* Skip return_trace parameters */ + BeamInstr *cp = cp_val(*ptr); + + if (*cp == i_return_time_trace) { ptr += 2; - } else if (*cp_val(*ptr) == i_return_to_trace) { - /* Skip stack frame variables */ - do ++ptr; while (is_not_CP(*ptr)); + } else if (*cp == i_return_to_trace) { + ptr += 1; + } else if (*cp == i_return_trace) { + ptr += 3; } else { - BeamInstr *cp = cp_val(*ptr); if (cp != prev) { /* Record non-duplicates only */ prev = cp; @@ -1614,7 +1665,6 @@ static void save_stacktrace(Process* c_p, BeamInstr* pc, Eterm* reg, ErtsCodeMFA *bif_mfa, Eterm args) { struct StackTrace* s; - Eterm *stack_start; int sz; int depth = erts_backtrace_depth; /* max depth (never negative) */ @@ -1633,33 +1683,6 @@ save_stacktrace(Process* c_p, BeamInstr* pc, Eterm* reg, s->depth = 0; /* - * If we crash on an instruction that returns to a return/exception trace - * instruction, we must set the stacktrace 'pc' to the actual return - * address or we'll lose the top stackframe when gathering the stack - * trace. - */ - stack_start = STACK_TOP(c_p); - if (stack_start < STACK_START(c_p) && is_CP(*stack_start)) { - BeamInstr *cp = cp_val(*stack_start); - - if (cp == pc) { - if (pc == beam_exception_trace || pc == beam_return_trace) { - ASSERT(&stack_start[3] <= STACK_START(c_p)); - /* Fake having failed on the first instruction in the function - * pointed to by the tag. */ - pc = cp_val(stack_start[1]); - stack_start += 3; - } else if (pc == beam_return_to_trace) { - ASSERT(&stack_start[2] <= STACK_START(c_p)); - pc = cp_val(stack_start[1]); - /* Skip both the trace tag and the new 'pc' to avoid - * duplicated entries. */ - stack_start += 2; - } - } - } - - /* * If the failure was in a BIF other than 'error/1', 'error/2', * 'exit/1' or 'throw/1', save BIF-MFA and save the argument * registers by consing up an arglist. @@ -1721,13 +1744,13 @@ save_stacktrace(Process* c_p, BeamInstr* pc, Eterm* reg, } /* Save the actual stack trace */ - gather_stacktrace(c_p, stack_start, s, depth); + gather_stacktrace(c_p, s, depth); } void erts_save_stacktrace(Process* p, struct StackTrace* s, int depth) { - gather_stacktrace(p, STACK_TOP(p), s, depth); + gather_stacktrace(p, s, depth); } /* @@ -1986,83 +2009,66 @@ apply_bif_error_adjustment(Process *p, Export *ep, Eterm *reg, Uint arity, BeamInstr *I, Uint stack_offset) { + int apply_only; + Uint need; + + need = stack_offset /* bytes */ / sizeof(Eterm); + apply_only = stack_offset == 0; + /* * I is only set when the apply is a tail call, i.e., * from the instructions i_apply_only, i_apply_last_P, * and apply_last_IP. */ - if (I - && BeamIsOpCode(ep->trampoline.op, op_apply_bif) - && (ep == bif_export[BIF_error_1] - || ep == bif_export[BIF_error_2] - || ep == bif_export[BIF_exit_1] - || ep == bif_export[BIF_throw_1])) { - /* - * We are about to tail apply one of the BIFs - * erlang:error/1, erlang:error/2, erlang:exit/1, - * or erlang:throw/1. Error handling of these BIFs is - * special! - * - * We need the topmost continuation pointer to point into the - * calling function when handling the error after the BIF has - * been applied. This in order to get the topmost stackframe - * correct. - * - * Note that these BIFs will unconditionally cause an - * exception to be raised. That is, our modifications of the - * stack will be corrected by the error handling code. - */ - int apply_only = stack_offset == 0; - BeamInstr *cpp; - Eterm *E; + if (!(I && (ep == bif_export[BIF_error_1] || + ep == bif_export[BIF_error_2] || + ep == bif_export[BIF_exit_1] || + ep == bif_export[BIF_throw_1]))) { + return; + } - E = p->stop; + /* + * We are about to tail apply one of the BIFs erlang:error/1, + * erlang:error/2, erlang:exit/1, or erlang:throw/1. Error handling of + * these BIFs is special! + * + * We need the topmost continuation pointer to point into the calling + * function when handling the error after the BIF has been applied. This in + * order to get the topmost stackframe correct. + * + * Note that these BIFs will unconditionally cause an exception to be + * raised. That is, our modifications of the stack will be corrected by the + * error handling code. + */ + if (need == 0) { + need = 1; /* i_apply_only */ + } - while (is_not_CP(*E)) { - E++; - } - cpp = cp_val(E[0]); + if (p->stop - p->htop < need) { + erts_garbage_collect(p, (int) need, reg, arity+1); + } + if (apply_only) { /* - * If we find an exception/return-to trace continuation - * pointer as the topmost continuation pointer, we do not - * need to do anything since the information will already - * be available for generation of the stacktrace. + * Called from the i_apply_only instruction. + * + * Push the continuation pointer for the current function to the stack. */ - - if (cpp != beam_exception_trace - && cpp != beam_return_trace - && cpp != beam_return_to_trace) { - Uint need = stack_offset /* bytes */ / sizeof(Eterm); - if (need == 0) - need = 1; /* i_apply_only */ - if (p->stop - p->htop < need) - erts_garbage_collect(p, (int) need, reg, arity+1); - if (apply_only) { - /* - * Called from the i_apply_only instruction. - * - * Push the continuation pointer for the current - * function to the stack. - */ - p->stop -= need; - p->stop[0] = make_cp(I); - } else { - /* - * Called from an i_apply_last_* instruction. - * - * The calling instruction will deallocate a stack - * frame of size 'stack_offset'. - * - * Push the continuation pointer for the current - * function to the stack, and then add a dummy - * stackframe for the i_apply_last* instruction - * to discard. - */ - p->stop[0] = make_cp(I); - p->stop -= need; - } - } + p->stop -= need; + p->stop[0] = make_cp(I); + } else { + /* + * Called from an i_apply_last_* instruction. + * + * The calling instruction will deallocate a stack frame of size + * 'stack_offset'. + * + * Push the continuation pointer for the current function to the stack, + * and then add a dummy stackframe for the i_apply_last* instruction + * to discard. + */ + p->stop[0] = make_cp(I); + p->stop -= need; } } @@ -3117,8 +3123,7 @@ erts_is_builtin(Eterm Mod, Eterm Name, int arity) return 0; } - return ep->addressv[erts_active_code_ix()] == ep->trampoline.raw && - BeamIsOpCode(ep->trampoline.op, op_apply_bif); + return ep->bif_table_index != -1; } |