diff --git a/src/stackwalk.c b/src/stackwalk.c index 2994e653cb462b..dd1e3f1b9c8dce 100644 --- a/src/stackwalk.c +++ b/src/stackwalk.c @@ -107,8 +107,6 @@ static int jl_unw_stepn(bt_cursor_t *cursor, jl_bt_element_t *bt_data, size_t *b from_signal_handler = 0; continue; } - if (sp) - sp[n] = thesp; // For the purposes of looking up debug info for functions, we want // to harvest addresses for the *call* instruction `call_ip` during // stack walking. However, this information isn't directly @@ -160,6 +158,8 @@ static int jl_unw_stepn(bt_cursor_t *cursor, jl_bt_element_t *bt_data, size_t *b } } bt_entry->uintptr = call_ip; + if (sp) + sp[n] = thesp; n++; } // NOTE: if we have some pgcstack entries remaining (because the @@ -251,8 +251,8 @@ JL_DLLEXPORT jl_value_t *jl_backtrace_from_here(int returnsp, int skip) jl_array_grow_end(ip, maxincr); uintptr_t *sp_ptr = NULL; if (returnsp) { - sp_ptr = (uintptr_t*)jl_array_data(sp) + offset; jl_array_grow_end(sp, maxincr); + sp_ptr = (uintptr_t*)jl_array_data(sp) + offset; } size_t size_incr = 0; have_more_frames = jl_unw_stepn(&cursor, (jl_bt_element_t*)jl_array_data(ip) + offset, diff --git a/test/backtrace.jl b/test/backtrace.jl index 8917c8698c9c49..3aebfec410f345 100644 --- a/test/backtrace.jl +++ b/test/backtrace.jl @@ -258,3 +258,88 @@ let code = """ @test occursin("InterpreterIP in top-level CodeInfo for Main.A", bt_str) end +""" + _reformat_sp(bt_data...) -> sp::Vector{Ptr{Cvoid}} + +Convert the output `bt_data` of `jl_backtrace_from_here` with `returnsp` flag set to a +vector of valid stack pointers `sp`; i.e., `sp` is a subset of `bt_data[3]`. + +See also `Base._reformat_bt`. +""" +function _reformat_sp( + bt_raw::Array{Ptr{Cvoid},1}, + bt2::Array{Any,1}, + sp_raw::Array{Ptr{Cvoid},1}, +) + bt = Base._reformat_bt(bt_raw, bt2) + sp = empty!(similar(sp_raw)) + i = j = 0 + while true + # Advance `i` such that `bt[i] isa Ptr{Cvoid}` (native pointer). + local ip + while true + if i == lastindex(bt) + return sp + end + i += 1 + x = bt[i] + if x isa Ptr{Cvoid} + ip = x + break + end + end + # Advance `j` such that `bt_raw[j] == bt[i]` to find a valid stack pointer. + while true + if j == lastindex(bt_raw) + return sp + end + j += 1 + if bt_raw[j] == ip + push!(sp, sp_raw[j]) + break + end + end + end +end + +""" + withframeaddress(f) + +Call function `f` with an address `ptr::Ptr{Cvoid}` of an independent frame +immediately outer to `f`. +""" +withframeaddress +@eval @noinline function withframeaddress(f) + sp = Core.Intrinsics.llvmcall( + ($""" + declare i8* @llvm.frameaddress(i32) + define private i$(Sys.WORD_SIZE) @frameaddr() { + %1 = call i8* @llvm.frameaddress(i32 0) + %2 = ptrtoint i8* %1 to i$(Sys.WORD_SIZE) + ret i$(Sys.WORD_SIZE) %2 + }""", "frameaddr"), + UInt, + Tuple{}, + ) + @noinline f(Ptr{Cvoid}(sp)) +end + +function sandwiched_backtrace() + local ptr1, ptr2, bt + withframeaddress() do p1 + ptr1 = p1 + bt = ccall(:jl_backtrace_from_here, Ref{Base.SimpleVector}, (Cint, Cint), true, 0) + withframeaddress() do p2 + ptr2 = p2 + end + end + return ptr1, ptr2, bt +end + +@testset "stack pointers" begin + ptr1, ptr2, bt_data = sandwiched_backtrace() + sp = _reformat_sp(bt_data...) + @test ptr2 < sp[2] + @test sp[1] < ptr1 + @test all(diff(Int128.(UInt.(sp))) .> 0) +end