From 643ec188c22f286025354010203f2ef7ea874410 Mon Sep 17 00:00:00 2001 From: Chris Foster Date: Tue, 15 Oct 2019 20:45:10 +1000 Subject: [PATCH] Make backtrace buffer handling more systematic (#33277) Increase expressibility of what can be stored in backtrace buffers, while ensuring that the GC can find roots without knowing about the detail. To do this, introduce a new "extended backtrace entry" format which carries along the number of roots and other data in a bitpacked format. This allows the backtrace buffer to be traversed and the roots collected in a general way, without the GC knowing about interpreter frames. Use this to add the module to InterperterIP so that the module of interpreted top level thunks can be known. In the future the extended entry format should allow us to be a lot more flexible with what can be stored in a backtrace. For example, we could * Compress the backtrace cycles of runaway recursive functions so that stack overflows are much more likely to fit in the fixed-size bt_data array. * Integrate external or other types of interpreter frames into the backtrace machinery. --- base/error.jl | 34 +++++--- base/errorshow.jl | 2 +- base/stacktraces.jl | 15 ++-- src/gc.c | 61 ++++++++------ src/gc.h | 7 +- src/interpreter-stacktrace.c | 26 ++++-- src/julia.h | 7 ++ src/julia_internal.h | 138 +++++++++++++++++++++++++++---- src/julia_threads.h | 3 +- src/rtutils.c | 13 +-- src/signal-handling.c | 16 ++-- src/signals-mach.c | 8 +- src/signals-unix.c | 8 +- src/signals-win.c | 6 +- src/stackwalk.c | 155 +++++++++++++++++++++++++---------- src/task.c | 4 +- src/threading.c | 7 +- test/backtrace.jl | 2 +- 18 files changed, 364 insertions(+), 148 deletions(-) diff --git a/base/error.jl b/base/error.jl index 0dc356723ab5e..d77fb650732ae 100644 --- a/base/error.jl +++ b/base/error.jl @@ -62,25 +62,39 @@ rethrow(e) = ccall(:jl_rethrow_other, Bottom, (Any,), e) struct InterpreterIP code::Union{CodeInfo,Core.MethodInstance,Nothing} stmt::Csize_t + mod::Union{Module,Nothing} end -# convert dual arrays (ips, interpreter_frames) to a single array of locations +# convert dual arrays (raw bt buffer, array of GC managed values) to a single +# array of locations function _reformat_bt(bt, bt2) ret = Vector{Union{InterpreterIP,Ptr{Cvoid}}}() i, j = 1, 1 while i <= length(bt) ip = bt[i]::Ptr{Cvoid} - if UInt(ip) == (-1 % UInt) - # The next one is really a CodeInfo - push!(ret, InterpreterIP( - bt2[j], - bt[i+2])) - j += 1 - i += 3 - else - push!(ret, Ptr{Cvoid}(ip)) + if UInt(ip) != (-1 % UInt) # See also jl_bt_is_native + # native frame + push!(ret, ip) i += 1 + continue + end + # Extended backtrace entry + entry_metadata = reinterpret(UInt, bt[i+1]) + njlvalues = entry_metadata & 0x7 + nuintvals = (entry_metadata >> 3) & 0x7 + tag = (entry_metadata >> 6) & 0xf + header = entry_metadata >> 10 + if tag == 1 # JL_BT_INTERP_FRAME_TAG + code = bt2[j] + mod = njlvalues == 2 ? bt2[j+1] : nothing + push!(ret, InterpreterIP(code, header, mod)) + else + # Tags we don't know about are an error + throw(ArgumentError("Unexpected extended backtrace entry tag $tag at bt[$i]")) end + # See jl_bt_entry_size + j += njlvalues + i += Int(2 + njlvalues + nuintvals) end ret end diff --git a/base/errorshow.jl b/base/errorshow.jl index 3b6495a2a3c9f..ce14fa16071c5 100644 --- a/base/errorshow.jl +++ b/base/errorshow.jl @@ -705,7 +705,7 @@ end function show(io::IO, ip::InterpreterIP) print(io, typeof(ip)) if ip.code isa Core.CodeInfo - print(io, " in top-level CodeInfo at statement $(Int(ip.stmt))") + print(io, " in top-level CodeInfo for $(ip.mod) at statement $(Int(ip.stmt))") else print(io, " in $(ip.code) at statement $(Int(ip.stmt))") end diff --git a/base/stacktraces.jl b/base/stacktraces.jl index e04512905bf13..e63449d1ec23a 100644 --- a/base/stacktraces.jl +++ b/base/stacktraces.jl @@ -7,6 +7,7 @@ module StackTraces import Base: hash, ==, show +import Core: CodeInfo, MethodInstance export StackTrace, StackFrame, stacktrace @@ -52,7 +53,7 @@ struct StackFrame # this type should be kept platform-agnostic so that profiles "the line number in the file containing the execution context" line::Int "the MethodInstance or CodeInfo containing the execution context (if it could be found)" - linfo::Union{Core.MethodInstance, Core.CodeInfo, Nothing} + linfo::Union{MethodInstance, CodeInfo, Nothing} "true if the code is from C" from_c::Bool "true if the code is from an inlined frame" @@ -118,7 +119,7 @@ end const top_level_scope_sym = Symbol("top-level scope") function lookup(ip::Base.InterpreterIP) - if ip.code isa Core.MethodInstance && ip.code.def isa Method + if ip.code isa MethodInstance && ip.code.def isa Method codeinfo = ip.code.uninferred func = ip.code.def.name file = ip.code.def.file @@ -127,7 +128,7 @@ function lookup(ip::Base.InterpreterIP) # interpreted top-level expression with no CodeInfo return [StackFrame(top_level_scope_sym, empty_sym, 0, nothing, false, false, 0)] else - @assert ip.code isa Core.CodeInfo + @assert ip.code isa CodeInfo codeinfo = ip.code func = top_level_scope_sym file = empty_sym @@ -206,7 +207,7 @@ function remove_frames!(stack::StackTrace, m::Module) return stack end -is_top_level_frame(f::StackFrame) = f.linfo isa Core.CodeInfo || (f.linfo === nothing && f.func === top_level_scope_sym) +is_top_level_frame(f::StackFrame) = f.linfo isa CodeInfo || (f.linfo === nothing && f.func === top_level_scope_sym) function show_spec_linfo(io::IO, frame::StackFrame) if frame.linfo === nothing @@ -220,7 +221,7 @@ function show_spec_linfo(io::IO, frame::StackFrame) :nothing printstyled(io, Base.demangle_function_name(string(frame.func)), color=color) end - elseif frame.linfo isa Core.MethodInstance + elseif frame.linfo isa MethodInstance def = frame.linfo.def if isa(def, Method) sig = frame.linfo.specTypes @@ -243,7 +244,7 @@ function show_spec_linfo(io::IO, frame::StackFrame) else Base.show(io, frame.linfo) end - elseif frame.linfo isa Core.CodeInfo + elseif frame.linfo isa CodeInfo print(io, "top-level scope") end end @@ -276,7 +277,7 @@ function from(frame::StackFrame, m::Module) finfo = frame.linfo result = false - if finfo isa Core.MethodInstance + if finfo isa MethodInstance frame_m = finfo.def isa(frame_m, Method) && (frame_m = frame_m.module) result = nameof(frame_m) === nameof(m) diff --git a/src/gc.c b/src/gc.c index 831dc14545348..5c5e3bd8d8de8 100644 --- a/src/gc.c +++ b/src/gc.c @@ -2059,34 +2059,41 @@ excstack: { gc_mark_excstack_t *stackitr = gc_pop_markdata(&sp, gc_mark_excstack_t); jl_excstack_t *excstack = stackitr->s; size_t itr = stackitr->itr; - size_t i = stackitr->i; + size_t bt_index = stackitr->bt_index; + size_t jlval_index = stackitr->jlval_index; while (itr > 0) { size_t bt_size = jl_excstack_bt_size(excstack, itr); - uintptr_t *bt_data = jl_excstack_bt_data(excstack, itr); - while (i+2 < bt_size) { - if (bt_data[i] != JL_BT_INTERP_FRAME) { - i++; + jl_bt_element_t *bt_data = jl_excstack_bt_data(excstack, itr); + for (; bt_index < bt_size; bt_index += jl_bt_entry_size(bt_data + bt_index)) { + jl_bt_element_t *bt_entry = bt_data + bt_index; + if (jl_bt_is_native(bt_entry)) continue; - } - // found an interpreter frame to mark - new_obj = (jl_value_t*)bt_data[i+1]; - uintptr_t nptr = 0; - i += 3; - if (gc_try_setmark(new_obj, &nptr, &tag, &bits)) { - stackitr->i = i; - stackitr->itr = itr; - gc_repush_markdata(&sp, gc_mark_excstack_t); - goto mark; + // Found an extended backtrace entry: iterate over any + // GC-managed values inside. + size_t njlvals = jl_bt_num_jlvals(bt_entry); + while (jlval_index < njlvals) { + new_obj = jl_bt_entry_jlvalue(bt_entry, jlval_index); + uintptr_t nptr = 0; + jlval_index += 1; + if (gc_try_setmark(new_obj, &nptr, &tag, &bits)) { + stackitr->itr = itr; + stackitr->bt_index = bt_index; + stackitr->jlval_index = jlval_index; + gc_repush_markdata(&sp, gc_mark_excstack_t); + goto mark; + } } } - // mark the exception + // The exception comes last - mark it new_obj = jl_excstack_exception(excstack, itr); itr = jl_excstack_next(excstack, itr); - i = 0; + bt_index = 0; + jlval_index = 0; uintptr_t nptr = 0; if (gc_try_setmark(new_obj, &nptr, &tag, &bits)) { - stackitr->i = i; stackitr->itr = itr; + stackitr->bt_index = bt_index; + stackitr->jlval_index = jlval_index; gc_repush_markdata(&sp, gc_mark_excstack_t); goto mark; } @@ -2359,7 +2366,7 @@ mark: { if (ta->excstack) { gc_setmark_buf_(ptls, ta->excstack, bits, sizeof(jl_excstack_t) + sizeof(uintptr_t)*ta->excstack->reserved_size); - gc_mark_excstack_t stackdata = {ta->excstack, ta->excstack->top, 0}; + gc_mark_excstack_t stackdata = {ta->excstack, ta->excstack->top, 0, 0}; gc_mark_stack_push(&ptls->gc_cache, &sp, gc_mark_laddr(excstack), &stackdata, sizeof(stackdata), 1); } @@ -2654,13 +2661,15 @@ static void jl_gc_queue_remset(jl_gc_mark_cache_t *gc_cache, jl_gc_mark_sp_t *sp static void jl_gc_queue_bt_buf(jl_gc_mark_cache_t *gc_cache, jl_gc_mark_sp_t *sp, jl_ptls_t ptls2) { - size_t n = 0; - while (n+2 < ptls2->bt_size) { - if (ptls2->bt_data[n] == JL_BT_INTERP_FRAME) { - gc_mark_queue_obj(gc_cache, sp, (jl_value_t*)ptls2->bt_data[n+1]); - n += 2; - } - n++; + jl_bt_element_t *bt_data = ptls2->bt_data; + size_t bt_size = ptls2->bt_size; + for (size_t i = 0; i < bt_size; i += jl_bt_entry_size(bt_data + i)) { + jl_bt_element_t *bt_entry = bt_data + i; + if (jl_bt_is_native(bt_entry)) + continue; + size_t njlvals = jl_bt_num_jlvals(bt_entry); + for (size_t j = 0; j < njlvals; j++) + gc_mark_queue_obj(gc_cache, sp, jl_bt_entry_jlvalue(bt_entry, j)); } } diff --git a/src/gc.h b/src/gc.h index a9c360bff3820..1dea38863a2fe 100644 --- a/src/gc.h +++ b/src/gc.h @@ -153,9 +153,10 @@ typedef struct { // Exception stack data typedef struct { - jl_excstack_t *s; // Stack of exceptions - size_t itr; // Iterator into exception stack - size_t i; // Iterator into backtrace data for exception + jl_excstack_t *s; // Stack of exceptions + size_t itr; // Iterator into exception stack + size_t bt_index; // Current backtrace buffer entry index + size_t jlval_index; // Index into GC managed values for current bt entry } gc_mark_excstack_t; // Module bindings. This is also the beginning of module scanning. diff --git a/src/interpreter-stacktrace.c b/src/interpreter-stacktrace.c index 18cd5f4243fc1..dacccb5e12e77 100644 --- a/src/interpreter-stacktrace.c +++ b/src/interpreter-stacktrace.c @@ -390,20 +390,29 @@ JL_DLLEXPORT int jl_is_enter_interpreter_frame(uintptr_t ip) return enter_interpreter_frame_start <= ip && ip <= enter_interpreter_frame_end; } -JL_DLLEXPORT size_t jl_capture_interp_frame(uintptr_t *data, uintptr_t sp, uintptr_t fp, size_t space_remaining) +JL_DLLEXPORT size_t jl_capture_interp_frame(jl_bt_element_t *bt_entry, uintptr_t sp, + uintptr_t fp, size_t space_remaining) { #ifdef FP_CAPTURE_OFFSET interpreter_state *s = (interpreter_state *)(fp-FP_CAPTURE_OFFSET); #else interpreter_state *s = (interpreter_state *)(sp+TOTAL_STACK_PADDING); #endif - int required_space = 3; + int need_module = !s->mi; + int required_space = need_module ? 4 : 3; if (space_remaining < required_space) - return 0; - // Sentinel value to indicate an interpreter frame - data[0] = JL_BT_INTERP_FRAME; - data[1] = s->mi ? (uintptr_t)s->mi : s->src ? (uintptr_t)s->src : (uintptr_t)jl_nothing; - data[2] = (uintptr_t)s->ip; + return 0; // Should not happen + size_t njlvalues = need_module ? 2 : 1; + uintptr_t entry_tags = jl_bt_entry_descriptor(njlvalues, 0, JL_BT_INTERP_FRAME_TAG, s->ip); + bt_entry[0].uintptr = JL_BT_NON_PTR_ENTRY; + bt_entry[1].uintptr = entry_tags; + bt_entry[2].jlvalue = s->mi ? (jl_value_t*)s->mi : + s->src ? (jl_value_t*)s->src : (jl_value_t*)jl_nothing; + if (need_module) { + // If we only have a CodeInfo (s->src), we are in a top level thunk and + // need to record the module separately. + bt_entry[3].jlvalue = (jl_value_t*)s->module; + } return required_space; } @@ -419,7 +428,8 @@ JL_DLLEXPORT int jl_is_enter_interpreter_frame(uintptr_t ip) return 0; } -JL_DLLEXPORT size_t jl_capture_interp_frame(uintptr_t *data, uintptr_t sp, uintptr_t fp, size_t space_remaining) +JL_DLLEXPORT size_t jl_capture_interp_frame(jl_bt_element_t *bt_entry, uintptr_t sp, + uintptr_t fp, size_t space_remaining) { // Leave bt_entry[0] as the native instruction ptr return 0; diff --git a/src/julia.h b/src/julia.h index 3ab36075d1299..64c2d5456a668 100644 --- a/src/julia.h +++ b/src/julia.h @@ -236,6 +236,13 @@ typedef struct _jl_llvm_functions_t { typedef struct _jl_method_instance_t jl_method_instance_t; +typedef struct _jl_line_info_node_t { + jl_value_t *method; + jl_sym_t *file; + intptr_t line; + intptr_t inlined_at; +} jl_line_info_node_t; + // This type describes a single function body typedef struct _jl_code_info_t { // ssavalue-indexed arrays of properties: diff --git a/src/julia_internal.h b/src/julia_internal.h index de2b060419d98..58f9b093f49c7 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -590,7 +590,109 @@ jl_tupletype_t *arg_type_tuple(jl_value_t *arg1, jl_value_t **args, size_t nargs int jl_has_meta(jl_array_t *body, jl_sym_t *sym); -// backtraces +//-------------------------------------------------- +// Backtraces + +// Backtrace buffers: +// +// A backtrace buffer conceptually contains a stack of instruction pointers +// ordered from the inner-most frame to the outermost. We store them in a +// special raw format for two reasons: +// +// * Efficiency: Every `throw()` must populate the trace so it must be as +// efficient as possible. +// * Signal safety: For signal-based exceptions such as StackOverflowError +// the trace buffer needs to be filled from a signal handler where most +// operations are not allowed (including malloc) so we choose a flat +// preallocated buffer. +// +// The raw buffer layout contains "frame entries" composed of one or several +// values of type `jl_bt_element_t`. From the point of view of the GC, an entry +// is either: +// +// 1. A single instruction pointer to native code, not GC-managed. +// 2. An "extended entry": a mixture of raw data and pointers to julia objects +// which must be treated as GC roots. +// +// A single extended entry is seralized using multiple elements from the raw +// buffer; if `e` is the pointer to the first slot we have: +// +// e[0] JL_BT_NON_PTR_ENTRY - Special marker to distinguish extended entries +// e[1] descriptor - A bit packed uintptr_t containing a tag and +// the number of GC- managed and non-managed values +// e[2+j] - GC managed data +// e[2+ngc+i] - Non-GC-managed data +// +// The format of `descriptor` is, from LSB to MSB: +// 0:2 ngc Number of GC-managed pointers for this frame entry +// 3:5 nptr Number of non-GC-managed buffer elements +// 6:9 tag Entry type +// 10:... header Entry-specific header data +typedef struct _jl_bt_element_t { + union { + uintptr_t uintptr; // Metadata or native instruction ptr + jl_value_t* jlvalue; // Pointer to GC-managed value + }; +} jl_bt_element_t; + +#define JL_BT_NON_PTR_ENTRY (((uintptr_t)0)-1) +// Maximum size for an extended backtrace entry (likely significantly larger +// than the actual size of 3-4 for an interpreter frame) +#define JL_BT_MAX_ENTRY_SIZE 16 + +STATIC_INLINE int jl_bt_is_native(jl_bt_element_t *bt_entry) JL_NOTSAFEPOINT +{ + return bt_entry[0].uintptr != JL_BT_NON_PTR_ENTRY; +} + +// Extended backtrace entry header packing; the bit packing is done manually +// for precise layout control for interop with julia side. +STATIC_INLINE uintptr_t jl_bt_entry_descriptor(int ngc, int nptr, + int tag, uintptr_t header) JL_NOTSAFEPOINT +{ + assert(((ngc & 0x7) == ngc) && ((nptr & 0x7) == nptr) && ((tag & 0xf) == tag)); + return (ngc & 0x7) | (nptr & 0x7) << 3 | (tag & 0xf) << 6 | header << 10; +} + +// Unpacking of extended backtrace entry data +STATIC_INLINE size_t jl_bt_num_jlvals(jl_bt_element_t *bt_entry) JL_NOTSAFEPOINT +{ + assert(!jl_bt_is_native(bt_entry)); + return bt_entry[1].uintptr & 0x7; +} +STATIC_INLINE size_t jl_bt_num_uintvals(jl_bt_element_t *bt_entry) JL_NOTSAFEPOINT +{ + assert(!jl_bt_is_native(bt_entry)); + return (bt_entry[1].uintptr >> 3) & 0x7; +} +STATIC_INLINE int jl_bt_entry_tag(jl_bt_element_t *bt_entry) JL_NOTSAFEPOINT +{ + assert(!jl_bt_is_native(bt_entry)); + return (bt_entry[1].uintptr >> 6) & 0xf; +} +STATIC_INLINE uintptr_t jl_bt_entry_header(jl_bt_element_t *bt_entry) JL_NOTSAFEPOINT +{ + assert(!jl_bt_is_native(bt_entry)); + return bt_entry[1].uintptr >> 10; +} + +// Return `i`th GC-managed pointer for extended backtrace entry +// The returned value is rooted for the lifetime of the parent exception stack. +STATIC_INLINE jl_value_t *jl_bt_entry_jlvalue(jl_bt_element_t *bt_entry, size_t i) JL_NOTSAFEPOINT +{ + return bt_entry[2 + i].jlvalue; +} + +#define JL_BT_INTERP_FRAME_TAG 1 // An interpreter frame + +// Number of bt elements in frame. +STATIC_INLINE size_t jl_bt_entry_size(jl_bt_element_t *bt_entry) JL_NOTSAFEPOINT +{ + return jl_bt_is_native(bt_entry) ? + 1 : 2 + jl_bt_num_jlvals(bt_entry) + jl_bt_num_uintvals(bt_entry); +} + +// Function metadata arising from debug info lookup of instruction pointer typedef struct { char *func_name; char *file_name; @@ -634,23 +736,21 @@ typedef unw_cursor_t bt_cursor_t; typedef int bt_context_t; typedef int bt_cursor_t; #endif -// Special marker in backtrace data for encoding interpreter frames -#define JL_BT_INTERP_FRAME (((uintptr_t)0)-1) -// Maximum number of elements of bt_data taken up by interpreter frame -#define JL_BT_MAX_ENTRY_SIZE 3 -size_t rec_backtrace(uintptr_t *bt_data, size_t maxsize, int skip) JL_NOTSAFEPOINT; +size_t rec_backtrace(jl_bt_element_t *bt_data, size_t maxsize, int skip) JL_NOTSAFEPOINT; // Record backtrace from a signal handler. `ctx` is the context of the code // which was asynchronously interrupted. -size_t rec_backtrace_ctx(uintptr_t *bt_data, size_t maxsize, bt_context_t *ctx, +size_t rec_backtrace_ctx(jl_bt_element_t *bt_data, size_t maxsize, bt_context_t *ctx, int add_interp_frames) JL_NOTSAFEPOINT; #ifdef LIBOSXUNWIND -size_t rec_backtrace_ctx_dwarf(uintptr_t *bt_data, size_t maxsize, bt_context_t *ctx, int add_interp_frames) JL_NOTSAFEPOINT; +size_t rec_backtrace_ctx_dwarf(jl_bt_element_t *bt_data, size_t maxsize, bt_context_t *ctx, int add_interp_frames) JL_NOTSAFEPOINT; #endif JL_DLLEXPORT jl_value_t *jl_get_backtrace(void); -void jl_critical_error(int sig, bt_context_t *context, uintptr_t *bt_data, size_t *bt_size); +void jl_critical_error(int sig, bt_context_t *context, jl_bt_element_t *bt_data, size_t *bt_size); JL_DLLEXPORT void jl_raise_debugger(void); int jl_getFunctionInfo(jl_frame_t **frames, uintptr_t pointer, int skipC, int noInline) JL_NOTSAFEPOINT; -JL_DLLEXPORT void jl_gdblookup(uintptr_t ip) JL_NOTSAFEPOINT; +JL_DLLEXPORT void jl_gdblookup(void* ip) JL_NOTSAFEPOINT; +void jl_print_native_codeloc(uintptr_t ip) JL_NOTSAFEPOINT; +void jl_print_bt_entry_codeloc(jl_bt_element_t *bt_data) JL_NOTSAFEPOINT; // *to is NULL or malloc'd pointer, from is allowed to be NULL STATIC_INLINE char *jl_copy_str(char **to, const char *from) { @@ -666,7 +766,8 @@ STATIC_INLINE char *jl_copy_str(char **to, const char *from) } JL_DLLEXPORT int jl_is_interpreter_frame(uintptr_t ip) JL_NOTSAFEPOINT; JL_DLLEXPORT int jl_is_enter_interpreter_frame(uintptr_t ip) JL_NOTSAFEPOINT; -JL_DLLEXPORT size_t jl_capture_interp_frame(uintptr_t *data, uintptr_t sp, uintptr_t fp, size_t space_remaining) JL_NOTSAFEPOINT; +JL_DLLEXPORT size_t jl_capture_interp_frame(jl_bt_element_t *bt_data, uintptr_t sp, + uintptr_t fp, size_t space_remaining) JL_NOTSAFEPOINT; // Exception stack: a stack of pairs of (exception,raw_backtrace). // The stack may be traversed and accessed with the functions below. @@ -676,25 +777,25 @@ typedef struct _jl_excstack_t { // Pack all stack entries into a growable buffer to amortize allocation // across repeated exception handling. // Layout: [bt_data1... bt_size1 exc1 bt_data2... bt_size2 exc2 ..] - // uintptr_t data[]; // Access with jl_excstack_raw + // jl_bt_element_t data[]; // Access with jl_excstack_raw } jl_excstack_t; -STATIC_INLINE uintptr_t *jl_excstack_raw(jl_excstack_t *stack) JL_NOTSAFEPOINT +STATIC_INLINE jl_bt_element_t *jl_excstack_raw(jl_excstack_t *stack) JL_NOTSAFEPOINT { - return (uintptr_t*)(stack + 1); + return (jl_bt_element_t*)(stack + 1); } // Exception stack access STATIC_INLINE jl_value_t *jl_excstack_exception(jl_excstack_t *stack JL_PROPAGATES_ROOT, size_t itr) JL_NOTSAFEPOINT { - return (jl_value_t*)(jl_excstack_raw(stack)[itr-1]); + return jl_excstack_raw(stack)[itr-1].jlvalue; } STATIC_INLINE size_t jl_excstack_bt_size(jl_excstack_t *stack, size_t itr) JL_NOTSAFEPOINT { - return jl_excstack_raw(stack)[itr-2]; + return jl_excstack_raw(stack)[itr-2].uintptr; } -STATIC_INLINE uintptr_t *jl_excstack_bt_data(jl_excstack_t *stack, size_t itr) JL_NOTSAFEPOINT +STATIC_INLINE jl_bt_element_t *jl_excstack_bt_data(jl_excstack_t *stack, size_t itr) JL_NOTSAFEPOINT { return jl_excstack_raw(stack) + itr-2 - jl_excstack_bt_size(stack, itr); } @@ -708,9 +809,10 @@ void jl_reserve_excstack(jl_excstack_t **stack JL_REQUIRE_ROOTED_SLOT, size_t reserved_size); void jl_push_excstack(jl_excstack_t **stack JL_REQUIRE_ROOTED_SLOT JL_ROOTING_ARGUMENT, jl_value_t *exception JL_ROOTED_ARGUMENT, - uintptr_t *bt_data, size_t bt_size); + jl_bt_element_t *bt_data, size_t bt_size); void jl_copy_excstack(jl_excstack_t *dest, jl_excstack_t *src) JL_NOTSAFEPOINT; +//-------------------------------------------------- // timers // Returns time in nanosec JL_DLLEXPORT uint64_t jl_hrtime(void); diff --git a/src/julia_threads.h b/src/julia_threads.h index 7dea19af4f7c6..7d47bd1ec716c 100644 --- a/src/julia_threads.h +++ b/src/julia_threads.h @@ -156,6 +156,7 @@ typedef struct { } jl_gc_mark_cache_t; typedef struct _jl_excstack_t jl_excstack_t; +struct _jl_bt_element_t; // This includes all the thread local states we care about for a thread. // Changes to TLS field types must be reflected in codegen. #define JL_MAX_BT_SIZE 80000 @@ -193,7 +194,7 @@ struct _jl_tls_states_t { // Temp storage for exception thrown in signal handler. Not rooted. struct _jl_value_t *sig_exception; // Temporary backtrace buffer. Scanned for gc roots when bt_size > 0. - uintptr_t *bt_data; // JL_MAX_BT_SIZE + 1 elements long + struct _jl_bt_element_t *bt_data; // JL_MAX_BT_SIZE + 1 elements long size_t bt_size; // Size for backtrace in transit in bt_data // Atomically set by the sender, reset by the handler. volatile sig_atomic_t signal_request; diff --git a/src/rtutils.c b/src/rtutils.c index c8670b942e172..d00aac0a38789 100644 --- a/src/rtutils.c +++ b/src/rtutils.c @@ -297,7 +297,7 @@ JL_DLLEXPORT void jl_restore_excstack(size_t state) void jl_copy_excstack(jl_excstack_t *dest, jl_excstack_t *src) JL_NOTSAFEPOINT { assert(dest->reserved_size >= src->top); - memcpy(jl_excstack_raw(dest), jl_excstack_raw(src), sizeof(uintptr_t)*src->top); + memcpy(jl_excstack_raw(dest), jl_excstack_raw(src), sizeof(jl_bt_element_t)*src->top); dest->top = src->top; } @@ -317,15 +317,16 @@ void jl_reserve_excstack(jl_excstack_t **stack JL_REQUIRE_ROOTED_SLOT, } void jl_push_excstack(jl_excstack_t **stack JL_REQUIRE_ROOTED_SLOT JL_ROOTING_ARGUMENT, - jl_value_t *exception JL_ROOTED_ARGUMENT, - uintptr_t *bt_data, size_t bt_size) + jl_value_t *exception JL_ROOTED_ARGUMENT, + jl_bt_element_t *bt_data, size_t bt_size) { jl_reserve_excstack(stack, (*stack ? (*stack)->top : 0) + bt_size + 2); jl_excstack_t *s = *stack; - memcpy(jl_excstack_raw(s) + s->top, bt_data, sizeof(uintptr_t)*bt_size); + jl_bt_element_t *rawstack = jl_excstack_raw(s); + memcpy(rawstack + s->top, bt_data, sizeof(jl_bt_element_t)*bt_size); s->top += bt_size + 2; - jl_excstack_raw(s)[s->top-2] = bt_size; - jl_excstack_raw(s)[s->top-1] = (uintptr_t)exception; + rawstack[s->top-2].uintptr = bt_size; + rawstack[s->top-1].jlvalue = exception; } // conversion ----------------------------------------------------------------- diff --git a/src/signal-handling.c b/src/signal-handling.c index 73ca73e22d708..43d133f6e394c 100644 --- a/src/signal-handling.c +++ b/src/signal-handling.c @@ -18,7 +18,7 @@ extern "C" { #include // Profiler control variables // -static volatile intptr_t *bt_data_prof = NULL; +static volatile jl_bt_element_t *bt_data_prof = NULL; static volatile size_t bt_size_max = 0; static volatile size_t bt_size_cur = 0; static volatile uint64_t nsecprof = 0; @@ -221,7 +221,7 @@ void jl_show_sigill(void *_ctx) } // what to do on a critical error -void jl_critical_error(int sig, bt_context_t *context, uintptr_t *bt_data, size_t *bt_size) +void jl_critical_error(int sig, bt_context_t *context, jl_bt_element_t *bt_data, size_t *bt_size) { // This function is not allowed to reference any TLS variables. // We need to explicitly pass in the TLS buffer pointer when @@ -230,10 +230,14 @@ void jl_critical_error(int sig, bt_context_t *context, uintptr_t *bt_data, size_ if (sig) jl_safe_printf("\nsignal (%d): %s\n", sig, strsignal(sig)); jl_safe_printf("in expression starting at %s:%d\n", jl_filename, jl_lineno); - if (context) + if (context) { + // Must avoid extended backtrace frames here unless we're sure bt_data + // is properly rooted. *bt_size = n = rec_backtrace_ctx(bt_data, JL_MAX_BT_SIZE, context, 0); - for (i = 0; i < n; i++) - jl_gdblookup(bt_data[i] - 1); + } + for (i = 0; i < n; i += jl_bt_entry_size(bt_data + i)) { + jl_print_bt_entry_codeloc(bt_data + i); + } gc_debug_print_status(); gc_debug_critical_error(); } @@ -247,7 +251,7 @@ JL_DLLEXPORT int jl_profile_init(size_t maxsize, uint64_t delay_nsec) nsecprof = delay_nsec; if (bt_data_prof != NULL) free((void*)bt_data_prof); - bt_data_prof = (intptr_t*) calloc(maxsize, sizeof(intptr_t)); + bt_data_prof = (jl_bt_element_t*) calloc(maxsize, sizeof(jl_bt_element_t)); if (bt_data_prof == NULL && maxsize > 0) return -1; bt_size_cur = 0; diff --git a/src/signals-mach.c b/src/signals-mach.c index 09caa0a874ec4..416c362dc76dd 100644 --- a/src/signals-mach.c +++ b/src/signals-mach.c @@ -448,10 +448,10 @@ void *mach_profile_listener(void *arg) if (forceDwarf == 0) { // Save the backtrace - bt_size_cur += rec_backtrace_ctx((uintptr_t*)bt_data_prof + bt_size_cur, bt_size_max - bt_size_cur - 1, uc, 0); + bt_size_cur += rec_backtrace_ctx((jl_bt_element_t*)bt_data_prof + bt_size_cur, bt_size_max - bt_size_cur - 1, uc, 0); } else if (forceDwarf == 1) { - bt_size_cur += rec_backtrace_ctx_dwarf((uintptr_t*)bt_data_prof + bt_size_cur, bt_size_max - bt_size_cur - 1, uc, 0); + bt_size_cur += rec_backtrace_ctx_dwarf((jl_bt_element_t*)bt_data_prof + bt_size_cur, bt_size_max - bt_size_cur - 1, uc, 0); } else if (forceDwarf == -1) { jl_safe_printf("WARNING: profiler attempt to access an invalid memory location\n"); @@ -459,11 +459,11 @@ void *mach_profile_listener(void *arg) forceDwarf = -2; #else - bt_size_cur += rec_backtrace_ctx((uintptr_t*)bt_data_prof + bt_size_cur, bt_size_max - bt_size_cur - 1, uc, 0); + bt_size_cur += rec_backtrace_ctx((jl_bt_element_t*)bt_data_prof + bt_size_cur, bt_size_max - bt_size_cur - 1, uc, 0); #endif // Mark the end of this block with 0 - bt_data_prof[bt_size_cur++] = 0; + bt_data_prof[bt_size_cur++].uintptr = 0; // Reset the alarm kern_return_t ret = clock_alarm(clk, TIME_RELATIVE, timerprof, profile_port); diff --git a/src/signals-unix.c b/src/signals-unix.c index fa234c337714c..5737d64248be9 100644 --- a/src/signals-unix.c +++ b/src/signals-unix.c @@ -551,7 +551,7 @@ static void kqueue_signal(int *sigqueue, struct kevent *ev, int sig) static void *signal_listener(void *arg) { - static uintptr_t bt_data[JL_MAX_BT_SIZE + 1]; + static jl_bt_element_t bt_data[JL_MAX_BT_SIZE + 1]; static size_t bt_size = 0; sigset_t sset; int sig, critical, profile; @@ -669,7 +669,7 @@ static void *signal_listener(void *arg) bt_size += rec_backtrace_ctx(bt_data + bt_size, JL_MAX_BT_SIZE / jl_n_threads - 1, signal_context, 0); - bt_data[bt_size++] = 0; + bt_data[bt_size++].uintptr = 0; } // do backtrace for profiler @@ -686,13 +686,13 @@ static void *signal_listener(void *arg) jl_safe_printf("WARNING: profiler attempt to access an invalid memory location\n"); } else { // Get backtrace data - bt_size_cur += rec_backtrace_ctx((uintptr_t*)bt_data_prof + bt_size_cur, + bt_size_cur += rec_backtrace_ctx((jl_bt_element_t*)bt_data_prof + bt_size_cur, bt_size_max - bt_size_cur - 1, signal_context, 0); } ptls->safe_restore = old_buf; // Mark the end of this block with 0 - bt_data_prof[bt_size_cur++] = 0; + bt_data_prof[bt_size_cur++].uintptr = 0; } if (bt_size_cur >= bt_size_max - 1) { // Buffer full: Delete the timer diff --git a/src/signals-win.c b/src/signals-win.c index 49ea19780c0b5..fefa3daf29622 100644 --- a/src/signals-win.c +++ b/src/signals-win.c @@ -297,7 +297,7 @@ LONG WINAPI jl_exception_handler(struct _EXCEPTION_POINTERS *ExceptionInfo) jl_safe_printf("UNKNOWN"); break; } jl_safe_printf(" at 0x%Ix -- ", (size_t)ExceptionInfo->ExceptionRecord->ExceptionAddress); - jl_gdblookup((uintptr_t)ExceptionInfo->ExceptionRecord->ExceptionAddress); + jl_print_native_codeloc(ExceptionInfo->ExceptionRecord->ExceptionAddress); jl_critical_error(0, ExceptionInfo->ContextRecord, ptls->bt_data, &ptls->bt_size); @@ -344,10 +344,10 @@ static DWORD WINAPI profile_bt( LPVOID lparam ) break; } // Get backtrace data - bt_size_cur += rec_backtrace_ctx((uintptr_t*)bt_data_prof + bt_size_cur, + bt_size_cur += rec_backtrace_ctx((jl_bt_element_t*)bt_data_prof + bt_size_cur, bt_size_max - bt_size_cur - 1, &ctxThread, 0); // Mark the end of this block with 0 - bt_data_prof[bt_size_cur] = 0; + bt_data_prof[bt_size_cur].uintptr = 0; bt_size_cur++; } if ((DWORD)-1 == ResumeThread(hMainThread)) { diff --git a/src/stackwalk.c b/src/stackwalk.c index d4615a19b2c54..4304757a5cb05 100644 --- a/src/stackwalk.c +++ b/src/stackwalk.c @@ -45,7 +45,7 @@ static int jl_unw_step(bt_cursor_t *cursor, uintptr_t *ip, uintptr_t *sp, uintpt // // jl_unw_stepn will return 1 if there are more frames to come. The number of // elements written to bt_data (and sp if non-NULL) are returned in bt_size. -int jl_unw_stepn(bt_cursor_t *cursor, uintptr_t *bt_data, size_t *bt_size, +int jl_unw_stepn(bt_cursor_t *cursor, jl_bt_element_t *bt_data, size_t *bt_size, uintptr_t *sp, size_t maxsize, int skip, int add_interp_frames, int from_signal_handler) JL_NOTSAFEPOINT { @@ -117,17 +117,17 @@ int jl_unw_stepn(bt_cursor_t *cursor, uintptr_t *bt_data, size_t *bt_size, // normal frame call_ip -= 1; } - if (call_ip == JL_BT_INTERP_FRAME) { + if (call_ip == JL_BT_NON_PTR_ENTRY) { // Never leave special marker in the bt data as it can corrupt the GC. call_ip = 0; } - uintptr_t *bt_entry = bt_data + n; + jl_bt_element_t *bt_entry = bt_data + n; size_t entry_sz = 0; if (add_interp_frames && jl_is_enter_interpreter_frame(call_ip) && (entry_sz = jl_capture_interp_frame(bt_entry, thesp, thefp, maxsize-n)) != 0) { n += entry_sz; } else { - *bt_entry = call_ip; + bt_entry->uintptr = call_ip; n++; } } @@ -149,7 +149,7 @@ int jl_unw_stepn(bt_cursor_t *cursor, uintptr_t *bt_data, size_t *bt_size, return need_more_space; } -NOINLINE size_t rec_backtrace_ctx(uintptr_t *bt_data, size_t maxsize, +NOINLINE size_t rec_backtrace_ctx(jl_bt_element_t *bt_data, size_t maxsize, bt_context_t *context, int add_interp_frames) JL_NOTSAFEPOINT { bt_cursor_t cursor; @@ -165,7 +165,7 @@ NOINLINE size_t rec_backtrace_ctx(uintptr_t *bt_data, size_t maxsize, // // The first `skip` frames are omitted, in addition to omitting the frame from // `rec_backtrace` itself. -NOINLINE size_t rec_backtrace(uintptr_t *bt_data, size_t maxsize, int skip) +NOINLINE size_t rec_backtrace(jl_bt_element_t *bt_data, size_t maxsize, int skip) { bt_context_t context; memset(&context, 0, sizeof(context)); @@ -217,7 +217,7 @@ JL_DLLEXPORT jl_value_t *jl_backtrace_from_here(int returnsp, int skip) jl_array_grow_end(sp, maxincr); } size_t size_incr = 0; - have_more_frames = jl_unw_stepn(&cursor, (uintptr_t*)jl_array_data(ip) + offset, + have_more_frames = jl_unw_stepn(&cursor, (jl_bt_element_t*)jl_array_data(ip) + offset, &size_incr, sp_ptr, maxincr, skip, 1, 0); skip = 0; offset += size_incr; @@ -227,12 +227,18 @@ JL_DLLEXPORT jl_value_t *jl_backtrace_from_here(int returnsp, int skip) jl_array_del_end(sp, jl_array_len(sp) - offset); size_t n = 0; + jl_bt_element_t *bt_data = (jl_bt_element_t*)jl_array_data(ip); while (n < jl_array_len(ip)) { - if ((uintptr_t)jl_array_ptr_ref(ip, n) == JL_BT_INTERP_FRAME) { - jl_array_ptr_1d_push(bt2, jl_array_ptr_ref(ip, n+1)); - n += 2; + jl_bt_element_t *bt_entry = bt_data + n; + if (!jl_bt_is_native(bt_entry)) { + size_t njlvals = jl_bt_num_jlvals(bt_entry); + for (size_t j = 0; j < njlvals; j++) { + jl_value_t *v = jl_bt_entry_jlvalue(bt_entry, j); + JL_GC_PROMISE_ROOTED(v); + jl_array_ptr_1d_push(bt2, v); + } } - n++; + n += jl_bt_entry_size(bt_entry); } } jl_value_t *bt = returnsp ? (jl_value_t*)jl_svec(3, ip, bt2, sp) : (jl_value_t*)jl_svec(2, ip, bt2); @@ -240,32 +246,37 @@ JL_DLLEXPORT jl_value_t *jl_backtrace_from_here(int returnsp, int skip) return bt; } -// note: btout and bt2out must be GC roots -void decode_backtrace(uintptr_t *bt_data, size_t bt_size, - jl_array_t **btout, jl_array_t **bt2out) +void decode_backtrace(jl_bt_element_t *bt_data, size_t bt_size, + jl_array_t **btout JL_REQUIRE_ROOTED_SLOT, + jl_array_t **bt2out JL_REQUIRE_ROOTED_SLOT) { jl_array_t *bt, *bt2; if (array_ptr_void_type == NULL) { array_ptr_void_type = jl_apply_type2((jl_value_t*)jl_array_type, (jl_value_t*)jl_voidpointer_type, jl_box_long(1)); } bt = *btout = jl_alloc_array_1d(array_ptr_void_type, bt_size); - memcpy(bt->data, bt_data, bt_size * sizeof(void*)); + static_assert(sizeof(jl_bt_element_t) == sizeof(void*), + "jl_bt_element_t is presented as Ptr{Cvoid} on julia side"); + memcpy(bt->data, bt_data, bt_size * sizeof(jl_bt_element_t)); bt2 = *bt2out = jl_alloc_array_1d(jl_array_any_type, 0); - // Scan the stack for any interpreter frames - size_t n = 0; - while (n < bt_size) { - if (bt_data[n] == JL_BT_INTERP_FRAME) { - jl_array_ptr_1d_push(bt2, (jl_value_t*)bt_data[n+1]); - n += 2; + // Scan the backtrace buffer for any gc-managed values + for (size_t i = 0; i < bt_size; i += jl_bt_entry_size(bt_data + i)) { + jl_bt_element_t* bt_entry = bt_data + i; + if (jl_bt_is_native(bt_entry)) + continue; + size_t njlvals = jl_bt_num_jlvals(bt_entry); + for (size_t j = 0; j < njlvals; j++) { + jl_value_t *v = jl_bt_entry_jlvalue(bt_entry, j); + JL_GC_PROMISE_ROOTED(v); + jl_array_ptr_1d_push(bt2, v); } - n++; } } JL_DLLEXPORT jl_value_t *jl_get_backtrace(void) { jl_excstack_t *s = jl_get_ptls_states()->current_task->excstack; - uintptr_t *bt_data = NULL; + jl_bt_element_t *bt_data = NULL; size_t bt_size = 0; if (s && s->top) { bt_data = jl_excstack_bt_data(s, s->top); @@ -519,7 +530,7 @@ static int jl_unw_step(bt_cursor_t *cursor, uintptr_t *ip, uintptr_t *sp, uintpt } #ifdef LIBOSXUNWIND -NOINLINE size_t rec_backtrace_ctx_dwarf(uintptr_t *bt_data, size_t maxsize, +NOINLINE size_t rec_backtrace_ctx_dwarf(jl_bt_element_t *bt_data, size_t maxsize, bt_context_t *context, int add_interp_frames) { size_t bt_size = 0; @@ -577,8 +588,22 @@ JL_DLLEXPORT jl_value_t *jl_lookup_code_address(void *ip, int skipC) return rs; } -//for looking up functions from gdb: -JL_DLLEXPORT void jl_gdblookup(uintptr_t ip) +void jl_safe_print_codeloc(const char* func_name, const char* file_name, + int line, int inlined) JL_NOTSAFEPOINT +{ + const char *inlined_str = inlined ? " [inlined]" : ""; + if (line != -1) { + jl_safe_printf("%s at %s:%d%s\n", func_name, file_name, line, inlined_str); + } + else { + jl_safe_printf("%s at %s (unknown line)%s\n", func_name, file_name, inlined_str); + } +} + +// Print function, file and line containing native instruction pointer `ip` by +// looking up debug info. Prints multiple such frames when `ip` points to +// inlined code. +void jl_print_native_codeloc(uintptr_t ip) JL_NOTSAFEPOINT { // This function is not allowed to reference any TLS variables since // it can be called from an unmanaged thread on OSX. @@ -593,15 +618,7 @@ JL_DLLEXPORT void jl_gdblookup(uintptr_t ip) jl_safe_printf("unknown function (ip: %p)\n", (void*)ip); } else { - const char *inlined = frame.inlined ? " [inlined]" : ""; - if (frame.line != -1) { - jl_safe_printf("%s at %s:%" PRIuPTR "%s\n", frame.func_name, - frame.file_name, (uintptr_t)frame.line, inlined); - } - else { - jl_safe_printf("%s at %s (unknown line)%s\n", frame.func_name, - frame.file_name, inlined); - } + jl_safe_print_codeloc(frame.func_name, frame.file_name, frame.line, frame.inlined); free(frame.func_name); free(frame.file_name); } @@ -609,22 +626,70 @@ JL_DLLEXPORT void jl_gdblookup(uintptr_t ip) free(frames); } +// Print code location for backtrace buffer entry at *bt_entry +void jl_print_bt_entry_codeloc(jl_bt_element_t *bt_entry) JL_NOTSAFEPOINT +{ + if (jl_bt_is_native(bt_entry)) { + jl_print_native_codeloc(bt_entry[0].uintptr); + } + else if (jl_bt_entry_tag(bt_entry) == JL_BT_INTERP_FRAME_TAG) { + size_t ip = jl_bt_entry_header(bt_entry); + jl_value_t *code = jl_bt_entry_jlvalue(bt_entry, 0); + if (jl_is_method_instance(code)) { + // When interpreting a method instance, need to unwrap to find the code info + code = ((jl_method_instance_t*)code)->uninferred; + } + if (jl_is_code_info(code)) { + jl_code_info_t *src = (jl_code_info_t*)code; + // See also the debug info handling in codegen.cpp. + // NB: debuginfoloc is 1-based! + intptr_t debuginfoloc = ((int32_t*)jl_array_data(src->codelocs))[ip]; + while (debuginfoloc != 0) { + jl_line_info_node_t *locinfo = (jl_line_info_node_t*) + jl_array_ptr_ref(src->linetable, debuginfoloc - 1); + assert(jl_typeis(locinfo, jl_lineinfonode_type)); + jl_value_t *method = locinfo->method; + if (jl_is_method_instance(method)) { + method = ((jl_method_instance_t*)method)->def.value; + if (jl_is_method(method)) + method = (jl_value_t*)((jl_method_t*)method)->name; + } + const char *func_name = jl_is_symbol(method) ? + jl_symbol_name((jl_sym_t*)method) : "Unknown"; + jl_safe_print_codeloc(func_name, jl_symbol_name(locinfo->file), + locinfo->line, locinfo->inlined_at); + debuginfoloc = locinfo->inlined_at; + } + } + else { + // If we're using this function something bad has already happened; + // be a bit defensive to avoid crashing while reporting the crash. + jl_safe_printf("No code info - unknown interpreter state!\n"); + } + } + else { + jl_safe_printf("Non-native bt entry with tag and header bits 0x%" PRIxPTR "\n", + bt_entry[1].uintptr); + } +} + +//-------------------------------------------------- +// Tools for interactive debugging in gdb +JL_DLLEXPORT void jl_gdblookup(void* ip) +{ + jl_print_native_codeloc((uintptr_t)ip); +} + +// Print backtrace for current exception in catch block JL_DLLEXPORT void jlbacktrace(void) JL_NOTSAFEPOINT { jl_excstack_t *s = jl_get_ptls_states()->current_task->excstack; if (!s) return; size_t bt_size = jl_excstack_bt_size(s, s->top); - uintptr_t *bt_data = jl_excstack_bt_data(s, s->top); - for (size_t i = 0; i < bt_size; ) { - if (bt_data[i] == JL_BT_INTERP_FRAME) { - jl_safe_printf("Interpreter frame (ip: %d)\n", (int)bt_data[i+2]); - jl_static_show(JL_STDERR, (jl_value_t*)bt_data[i+1]); - i += 3; - } else { - jl_gdblookup(bt_data[i] - 1); - i += 1; - } + jl_bt_element_t *bt_data = jl_excstack_bt_data(s, s->top); + for (size_t i = 0; i < bt_size; i += jl_bt_entry_size(bt_data + i)) { + jl_print_bt_entry_codeloc(bt_data + i); } } diff --git a/src/task.c b/src/task.c index aeaa8af71e8ed..e8a21e90884d7 100644 --- a/src/task.c +++ b/src/task.c @@ -518,7 +518,7 @@ JL_DLLEXPORT void jl_rethrow_other(jl_value_t *e JL_MAYBE_UNROOTED) if (!excstack || excstack->top == 0) jl_error("rethrow(exc) not allowed outside a catch block"); // overwrite exception on top of stack. see jl_excstack_exception - jl_excstack_raw(excstack)[excstack->top-1] = (uintptr_t)e; + jl_excstack_raw(excstack)[excstack->top-1].jlvalue = e; JL_GC_PROMISE_ROOTED(e); throw_internal(NULL); } @@ -673,7 +673,7 @@ STATIC_OR_JS void NOINLINE JL_NORETURN start_task(void) if (t->exception != jl_nothing) { record_backtrace(ptls, 0); jl_push_excstack(&t->excstack, t->exception, - ptls->bt_data, ptls->bt_size); + ptls->bt_data, ptls->bt_size); res = t->exception; } else { diff --git a/src/threading.c b/src/threading.c index b92ce7776be71..8bae4948ac421 100644 --- a/src/threading.c +++ b/src/threading.c @@ -262,14 +262,15 @@ void jl_init_threadtls(int16_t tid) sizeof(size_t)); } ptls->defer_signal = 0; - void *bt_data = malloc(sizeof(uintptr_t) * (JL_MAX_BT_SIZE + 1)); + jl_bt_element_t *bt_data = (jl_bt_element_t*) + malloc(sizeof(jl_bt_element_t) * (JL_MAX_BT_SIZE + 1)); if (bt_data == NULL) { jl_printf(JL_STDERR, "could not allocate backtrace buffer\n"); gc_debug_critical_error(); abort(); } - memset(bt_data, 0, sizeof(uintptr_t) * (JL_MAX_BT_SIZE + 1)); - ptls->bt_data = (uintptr_t*)bt_data; + memset(bt_data, 0, sizeof(jl_bt_element_t) * (JL_MAX_BT_SIZE + 1)); + ptls->bt_data = bt_data; ptls->sig_exception = NULL; ptls->previous_exception = NULL; #ifdef _OS_WINDOWS_ diff --git a/test/backtrace.jl b/test/backtrace.jl index 715f5d2913a9a..8b6ca94c77970 100644 --- a/test/backtrace.jl +++ b/test/backtrace.jl @@ -255,6 +255,6 @@ let code = """ bt_str = read(`$(Base.julia_cmd()) --startup-file=no --compile=min -e $code`, String) @test occursin("InterpreterIP in MethodInstance for foo", bt_str) - @test occursin("InterpreterIP in top-level CodeInfo", bt_str) + @test occursin("InterpreterIP in top-level CodeInfo for Main.A", bt_str) end