-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Profile.jl improvements #33190
Profile.jl improvements #33190
Changes from all commits
4c27b1b
83fd034
868a15d
21a1465
fbf25ae
89ddf9a
efc625e
30b7d35
19e5c55
22c96ca
0002f0a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,8 +7,6 @@ module StackTraces | |
|
||
|
||
import Base: hash, ==, show | ||
using Base.Printf: @printf | ||
using Base: something | ||
|
||
export StackTrace, StackFrame, stacktrace | ||
|
||
|
@@ -98,31 +96,28 @@ end | |
|
||
|
||
""" | ||
lookup(pointer::Union{Ptr{Cvoid}, UInt}) -> Vector{StackFrame} | ||
lookupat(pointer::Union{Ptr{Cvoid}, UInt}) -> Vector{StackFrame} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is the name change worth it? I could imagine packages calling There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed, this change makes me uncomfortable. |
||
|
||
Given a pointer to an execution context (usually generated by a call to `backtrace`), looks | ||
up stack frame context information. Returns an array of frame information for all functions | ||
inlined at that point, innermost function first. | ||
""" | ||
function lookup(pointer::Ptr{Cvoid}) | ||
infos = ccall(:jl_lookup_code_address, Any, (Ptr{Cvoid}, Cint), pointer - 1, false) | ||
isempty(infos) && return [StackFrame(empty_sym, empty_sym, -1, nothing, true, false, convert(UInt64, pointer))] | ||
function lookupat(pointer::Ptr{Cvoid}) | ||
infos = ccall(:jl_lookup_code_address, Any, (Ptr{Cvoid}, Cint), pointer, false) | ||
pointer = convert(UInt64, pointer) | ||
isempty(infos) && return [StackFrame(empty_sym, empty_sym, -1, nothing, true, false, pointer)] # this is equal to UNKNOWN | ||
res = Vector{StackFrame}(undef, length(infos)) | ||
for i in 1:length(infos) | ||
info = infos[i] | ||
@assert(length(info) == 7) | ||
res[i] = StackFrame(info[1], info[2], info[3], info[4], info[5], info[6], info[7]) | ||
@assert(length(info) == 6) | ||
res[i] = StackFrame(info[1], info[2], info[3], info[4], info[5], info[6], pointer) | ||
end | ||
return res | ||
end | ||
|
||
lookup(pointer::UInt) = lookup(convert(Ptr{Cvoid}, pointer)) | ||
|
||
const top_level_scope_sym = Symbol("top-level scope") | ||
|
||
using Base.Meta | ||
is_loc_meta(expr, kind) = isexpr(expr, :meta) && length(expr.args) >= 1 && expr.args[1] === kind | ||
function lookup(ip::Base.InterpreterIP) | ||
function lookupat(ip::Base.InterpreterIP) | ||
if ip.code isa Core.MethodInstance && ip.code.def isa Method | ||
codeinfo = ip.code.uninferred | ||
func = ip.code.def.name | ||
|
@@ -154,10 +149,6 @@ function lookup(ip::Base.InterpreterIP) | |
return scopes | ||
end | ||
|
||
# allow lookup on already-looked-up data for easier handling of pre-processed frames | ||
lookup(s::StackFrame) = StackFrame[s] | ||
lookup(s::Tuple{StackFrame,Int}) = StackFrame[s[1]] | ||
|
||
""" | ||
backtrace() | ||
|
||
|
@@ -195,26 +186,28 @@ doesn't return C functions, but this can be enabled.) When called without specif | |
trace, `stacktrace` first calls `backtrace`. | ||
""" | ||
function stacktrace(trace::Vector{<:Union{Base.InterpreterIP,Ptr{Cvoid}}}, c_funcs::Bool=false) | ||
stack = vcat(StackTrace(), map(lookup, trace)...)::StackTrace | ||
|
||
# Remove frames that come from C calls. | ||
if !c_funcs | ||
filter!(frame -> !frame.from_c, stack) | ||
stack = StackTrace() | ||
for ip in trace | ||
ip isa Base.InterpreterIP || (ip -= 1) # convert from return stack to call stack (for inlining info) | ||
for frame in lookupat(ip) | ||
# Skip frames that come from C calls. | ||
if c_funcs || !frame.from_c | ||
push!(stack, frame) | ||
end | ||
end | ||
end | ||
return stack | ||
end | ||
|
||
function stacktrace(c_funcs::Bool=false) | ||
stack = stacktrace(backtrace(), c_funcs) | ||
# Remove frame for this function (and any functions called by this function). | ||
remove_frames!(stack, :stacktrace) | ||
|
||
# is there a better way? the func symbol has a number suffix which changes. | ||
# it's possible that no test is needed and we could just popfirst! all the time. | ||
# this line was added to PR #16213 because otherwise stacktrace() != stacktrace(false). | ||
# not sure why. possibly b/c of re-ordering of base/sysimg.jl | ||
!isempty(stack) && startswith(string(stack[1].func),"jlcall_stacktrace") && popfirst!(stack) | ||
stack | ||
# also remove all of the non-Julia functions that led up to this point (if that list is non-empty) | ||
c_funcs && deleteat!(stack, 1:(something(findfirst(frame -> !frame.from_c, stack), 1) - 1)) | ||
return stack | ||
end | ||
|
||
stacktrace(c_funcs::Bool=false) = stacktrace(backtrace(), c_funcs) | ||
|
||
""" | ||
remove_frames!(stack::StackTrace, name::Symbol) | ||
|
||
|
@@ -224,12 +217,12 @@ all frames above the specified function). Primarily used to remove `StackTraces` | |
from the `StackTrace` prior to returning it. | ||
""" | ||
function remove_frames!(stack::StackTrace, name::Symbol) | ||
splice!(stack, 1:something(findlast(frame -> frame.func == name, stack), 0)) | ||
deleteat!(stack, 1:something(findlast(frame -> frame.func == name, stack), 0)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wow. Suggests this needs a test, no? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. They have the same side-effect, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, right. I was confusing this with our renaming of |
||
return stack | ||
end | ||
|
||
function remove_frames!(stack::StackTrace, names::Vector{Symbol}) | ||
splice!(stack, 1:something(findlast(frame -> frame.func in names, stack), 0)) | ||
deleteat!(stack, 1:something(findlast(frame -> frame.func in names, stack), 0)) | ||
return stack | ||
end | ||
|
||
|
@@ -248,7 +241,7 @@ is_top_level_frame(f::StackFrame) = f.linfo isa Core.CodeInfo || (f.linfo === no | |
function show_spec_linfo(io::IO, frame::StackFrame) | ||
if frame.linfo === nothing | ||
if frame.func === empty_sym | ||
@printf(io, "ip:%#x", frame.pointer) | ||
print(io, "ip:0x", string(frame.pointer, base=16)) | ||
elseif frame.func === top_level_scope_sym | ||
print(io, "top-level scope") | ||
else | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -626,9 +626,9 @@ typedef int bt_cursor_t; | |
// Special marker in backtrace data for encoding interpreter frames | ||
#define JL_BT_INTERP_FRAME (((uintptr_t)0)-1) | ||
size_t rec_backtrace(uintptr_t *data, size_t maxsize) JL_NOTSAFEPOINT; | ||
size_t rec_backtrace_ctx(uintptr_t *data, size_t maxsize, bt_context_t *ctx) JL_NOTSAFEPOINT; | ||
size_t rec_backtrace_ctx(uintptr_t *data, size_t maxsize, bt_context_t *ctx, int add_interp_frames) JL_NOTSAFEPOINT; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Excellent change. I was thinking about this exact problem (Profile getting confused by- / having overhead from- interpreter frames) while making #33277. |
||
#ifdef LIBOSXUNWIND | ||
size_t rec_backtrace_ctx_dwarf(uintptr_t *data, size_t maxsize, bt_context_t *ctx); | ||
size_t rec_backtrace_ctx_dwarf(uintptr_t *data, size_t maxsize, bt_context_t *ctx, int add_interp_frames) JL_NOTSAFEPOINT; | ||
#endif | ||
JL_DLLEXPORT void jl_get_backtrace(jl_array_t **bt, jl_array_t **bt2); | ||
void jl_critical_error(int sig, bt_context_t *context, uintptr_t *bt_data, size_t *bt_size); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great, I think this answers my question at #33277 (comment)