Skip to content

Commit c3d2533

Browse files
committed
Infrastructure for hiding backtrace frames
This implements a hide_in_backtrace meta which may be attached to individual methods (via a flag on the CodeInfo) to mark them for hiding in backtraces. Use this infrastructure to hide several julia methods by default (these can still be seen using the internal_funcs flag to backtrace()) * error(), backtrace(), stacktrace() * keyword sorters
1 parent e25b357 commit c3d2533

File tree

15 files changed

+108
-36
lines changed

15 files changed

+108
-36
lines changed

base/error.jl

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,11 @@ throw
3030
3131
Raise an `ErrorException` with the given message.
3232
"""
33-
error(s::AbstractString) = throw(ErrorException(s))
33+
function error(s::AbstractString)
34+
@_noinline_meta
35+
@_hide_in_stacktrace_meta
36+
throw(ErrorException(s))
37+
end
3438

3539
"""
3640
error(msg...)
@@ -39,6 +43,7 @@ Raise an `ErrorException` with the given message.
3943
"""
4044
function error(s::Vararg{Any,N}) where {N}
4145
@_noinline_meta
46+
@_hide_in_stacktrace_meta
4247
throw(ErrorException(Main.Base.string(s...)))
4348
end
4449

@@ -106,8 +111,10 @@ Get a backtrace object for the current program point.
106111
"""
107112
function backtrace()
108113
@_noinline_meta
109-
# skip frame for backtrace(). Note that for this to work properly,
110-
# backtrace() itself must not be interpreted nor inlined.
114+
@_hide_in_stacktrace_meta
115+
# skip frame for backtrace(). Note that with `skip > 0` backtrace() must
116+
# not be inlined to avoid loosing parent frames.
117+
# We also tag it as hidden in case it gets interpreted.
111118
skip = 1
112119
bt1, bt2 = ccall(:jl_backtrace_from_here, Ref{SimpleVector}, (Cint, Cint), false, skip)
113120
return _reformat_bt(bt1::Vector{Ptr{Cvoid}}, bt2::Vector{Any})

base/errorshow.jl

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -745,11 +745,6 @@ function show_backtrace(io::IO, t::Vector{Any})
745745
end
746746
end
747747

748-
function is_kw_sorter_name(name::Symbol)
749-
sn = string(name)
750-
return !startswith(sn, '#') && endswith(sn, "##kw")
751-
end
752-
753748
function process_backtrace(t::Vector, limit::Int=typemax(Int); skipC = true)
754749
n = 0
755750
last_frame = StackTraces.UNKNOWN
@@ -762,25 +757,26 @@ function process_backtrace(t::Vector, limit::Int=typemax(Int); skipC = true)
762757
else
763758
lkups = StackTraces.lookup(lkups)
764759
end
765-
for lkup in lkups
766-
if lkup === StackTraces.UNKNOWN
760+
for frame in lkups
761+
if frame === StackTraces.UNKNOWN
767762
continue
768763
end
769764

770-
if (lkup.from_c && skipC) || is_kw_sorter_name(lkup.func)
765+
if StackTraces.is_hidden(frame) && skipC
771766
continue
772767
end
773768
count += 1
774769
if count > limit
775770
break
776771
end
777772

778-
if lkup.file != last_frame.file || lkup.line != last_frame.line || lkup.func != last_frame.func || lkup.linfo !== lkup.linfo
773+
if frame.file != last_frame.file || frame.line != last_frame.line ||
774+
frame.func != last_frame.func || frame.linfo !== last_frame.linfo
779775
if n > 0
780776
push!(ret, (last_frame, n))
781777
end
782778
n = 1
783-
last_frame = lkup
779+
last_frame = frame
784780
else
785781
n += 1
786782
end

base/essentials.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ end
113113
macro _propagate_inbounds_meta()
114114
return Expr(:meta, :inline, :propagate_inbounds)
115115
end
116+
macro _hide_in_stacktrace_meta()
117+
return Expr(:meta, :hide_in_stacktrace)
118+
end
116119

117120
function iterate end
118121

base/expr.jl

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,18 @@ macro pure(ex)
237237
esc(isa(ex, Expr) ? pushmeta!(ex, :pure) : ex)
238238
end
239239

240+
"""
241+
@hide_in_stacktrace
242+
243+
Mark a function to be hidden from the user in stacktraces. `@hide_in_stacktrace`
244+
currently implies `@noinline`, though this may be relaxed in the future.
245+
246+
See also [`stacktrace`](@ref).
247+
"""
248+
macro hide_in_stacktrace(ex)
249+
esc(ex isa Expr ? pushmeta!(pushmeta!(ex, :hide_in_stacktrace), :noinline) : ex)
250+
end
251+
240252
"""
241253
@propagate_inbounds
242254

base/stacktraces.jl

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,20 @@ function hash(frame::StackFrame, h::UInt)
9595
return h
9696
end
9797

98+
# Determine whether frame should be hidden from the user by default
99+
function is_hidden(frame::StackFrame)
100+
if frame.from_c
101+
return true
102+
end
103+
if frame.linfo isa Core.MethodInstance
104+
# NB: MethodInstance currently only available for non-inlined frames.
105+
def = frame.linfo.def
106+
if def isa Method
107+
return ccall(:jl_ir_flag_hide_in_stacktrace, Bool, (Any,), def.source)
108+
end
109+
end
110+
return false
111+
end
98112

99113
"""
100114
lookup(pointer::Ptr{Cvoid}) -> Vector{StackFrame}
@@ -151,32 +165,30 @@ function lookup(ip::Base.InterpreterIP)
151165
end
152166

153167
"""
154-
stacktrace([trace::Vector{Ptr{Cvoid}},] [c_funcs::Bool=false]) -> StackTrace
168+
stacktrace([rawtrace,] internal_funcs=false)
169+
170+
Returns a stack trace in the form of a vector of `StackFrame`s from the current
171+
call stack, or from a raw call stack `rawtrace` which must have been previously
172+
collected using `backtrace()`.
155173
156-
Returns a stack trace in the form of a vector of `StackFrame`s. (By default stacktrace
157-
doesn't return C functions, but this can be enabled.) When called without specifying a
158-
trace, `stacktrace` first calls `backtrace`.
174+
By default `stacktrace` filters out internal functions, including C functions
175+
and any Julia functions marked with `Base.@hide_in_stacktrace`. These can be
176+
included by setting `internal_funcs` to `true`.
159177
"""
160-
function stacktrace(trace::Vector{<:Union{Base.InterpreterIP,Ptr{Cvoid}}}, c_funcs::Bool=false)
178+
function stacktrace(rawtrace::AbstractVector, internal_funcs::Bool=false)
161179
stack = StackTrace()
162-
for ip in trace
180+
for ip in rawtrace
163181
for frame in lookup(ip)
164-
# Skip frames that come from C calls.
165-
if c_funcs || !frame.from_c
182+
if internal_funcs || !is_hidden(frame)
166183
push!(stack, frame)
167184
end
168185
end
169186
end
170187
return stack
171188
end
172189

173-
function stacktrace(c_funcs::Bool=false)
174-
stack = stacktrace(backtrace(), c_funcs)
175-
# Remove frame for this function (and any functions called by this function).
176-
remove_frames!(stack, :stacktrace)
177-
# also remove all of the non-Julia functions that led up to this point (if that list is non-empty)
178-
c_funcs && deleteat!(stack, 1:(something(findfirst(frame -> !frame.from_c, stack), 1) - 1))
179-
return stack
190+
@noinline Base.@hide_in_stacktrace function stacktrace(internal_funcs::Bool=false)
191+
stacktrace(backtrace(), internal_funcs)
180192
end
181193

182194
"""

src/ast.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ jl_sym_t *throw_undef_if_not_sym; jl_sym_t *getfield_undefref_sym;
6262
jl_sym_t *gc_preserve_begin_sym; jl_sym_t *gc_preserve_end_sym;
6363
jl_sym_t *coverageeffect_sym; jl_sym_t *escape_sym;
6464
jl_sym_t *aliasscope_sym; jl_sym_t *popaliasscope_sym;
65+
jl_sym_t *hide_in_stacktrace_sym;
6566

6667
static uint8_t flisp_system_image[] = {
6768
#include <julia_flisp.boot.inc>
@@ -368,6 +369,7 @@ void jl_init_frontend(void)
368369
copyast_sym = jl_symbol("copyast");
369370
loopinfo_sym = jl_symbol("loopinfo");
370371
pure_sym = jl_symbol("pure");
372+
hide_in_stacktrace_sym = jl_symbol("hide_in_stacktrace");
371373
meta_sym = jl_symbol("meta");
372374
list_sym = jl_symbol("list");
373375
unused_sym = jl_symbol("#unused#");

src/dump.c

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2565,7 +2565,8 @@ JL_DLLEXPORT jl_array_t *jl_compress_ir(jl_method_t *m, jl_code_info_t *code)
25652565
NULL
25662566
};
25672567

2568-
uint8_t flags = (code->inferred << 3)
2568+
uint8_t flags = (code->hide_in_stacktrace << 4)
2569+
| (code->inferred << 3)
25692570
| (code->inlineable << 2)
25702571
| (code->propagate_inbounds << 1)
25712572
| (code->pure << 0);
@@ -2651,6 +2652,7 @@ JL_DLLEXPORT jl_code_info_t *jl_uncompress_ir(jl_method_t *m, jl_code_instance_t
26512652

26522653
jl_code_info_t *code = jl_new_code_info_uninit();
26532654
uint8_t flags = read_uint8(s.s);
2655+
code->hide_in_stacktrace = !!(flags & (1 << 4));
26542656
code->inferred = !!(flags & (1 << 3));
26552657
code->inlineable = !!(flags & (1 << 2));
26562658
code->propagate_inbounds = !!(flags & (1 << 1));
@@ -2704,6 +2706,15 @@ JL_DLLEXPORT jl_code_info_t *jl_uncompress_ir(jl_method_t *m, jl_code_instance_t
27042706
return code;
27052707
}
27062708

2709+
JL_DLLEXPORT uint8_t jl_ir_flag_hide_in_stacktrace(jl_array_t *data)
2710+
{
2711+
if (jl_is_code_info(data))
2712+
return ((jl_code_info_t*)data)->hide_in_stacktrace;
2713+
assert(jl_typeis(data, jl_array_uint8_type));
2714+
uint8_t flags = ((uint8_t*)data->data)[0];
2715+
return !!(flags & (1 << 4));
2716+
}
2717+
27072718
JL_DLLEXPORT uint8_t jl_ir_flag_inferred(jl_array_t *data)
27082719
{
27092720
if (jl_is_code_info(data))

src/jltypes.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2080,7 +2080,7 @@ void jl_init_types(void) JL_GC_DISABLED
20802080
jl_code_info_type =
20812081
jl_new_datatype(jl_symbol("CodeInfo"), core,
20822082
jl_any_type, jl_emptysvec,
2083-
jl_perm_symsvec(18,
2083+
jl_perm_symsvec(19,
20842084
"code",
20852085
"codelocs",
20862086
"ssavaluetypes",
@@ -2095,11 +2095,12 @@ void jl_init_types(void) JL_GC_DISABLED
20952095
"edges",
20962096
"min_world",
20972097
"max_world",
2098+
"hide_in_stacktrace",
20982099
"inferred",
20992100
"inlineable",
21002101
"propagate_inbounds",
21012102
"pure"),
2102-
jl_svec(18,
2103+
jl_svec(19,
21032104
jl_array_any_type,
21042105
jl_any_type,
21052106
jl_any_type,
@@ -2117,8 +2118,9 @@ void jl_init_types(void) JL_GC_DISABLED
21172118
jl_bool_type,
21182119
jl_bool_type,
21192120
jl_bool_type,
2121+
jl_bool_type,
21202122
jl_bool_type),
2121-
0, 1, 18);
2123+
0, 1, 19);
21222124

21232125
jl_method_type =
21242126
jl_new_datatype(jl_symbol("Method"), core,

src/julia-syntax.scm

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,7 @@
499499
name positional-sparams pargl-all
500500
`(block
501501
,@(without-generated prologue)
502+
(meta hide_in_stacktrace)
502503
,(let (;; call mangled(vals..., [rest_kw,] pargs..., [vararg]...)
503504
(ret `(return (call ,mangled
504505
,@(if ordered-defaults keynames vals)
@@ -519,6 +520,7 @@
519520
(call (core kwftype) ,ftype)) ,kw ,@pargl ,@vararg)
520521
`(block
521522
,@(filter linenum? prologue)
523+
(meta hide_in_stacktrace)
522524
,(scopenest
523525
keynames
524526
(map (lambda (v dflt)

src/julia.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,7 @@ typedef struct _jl_code_info_t {
264264
size_t min_world;
265265
size_t max_world;
266266
// various boolean properties:
267+
uint8_t hide_in_stacktrace;
267268
uint8_t inferred;
268269
uint8_t inlineable;
269270
uint8_t propagate_inbounds;
@@ -1651,6 +1652,7 @@ JL_DLLEXPORT jl_value_t *jl_copy_ast(jl_value_t *expr JL_MAYBE_UNROOTED);
16511652
// IR representation
16521653
JL_DLLEXPORT jl_array_t *jl_compress_ir(jl_method_t *m, jl_code_info_t *code);
16531654
JL_DLLEXPORT jl_code_info_t *jl_uncompress_ir(jl_method_t *m, jl_code_instance_t *metadata, jl_array_t *data);
1655+
JL_DLLEXPORT uint8_t jl_ir_flag_hide_in_stacktrace(jl_array_t *data) JL_NOTSAFEPOINT;
16541656
JL_DLLEXPORT uint8_t jl_ir_flag_inferred(jl_array_t *data) JL_NOTSAFEPOINT;
16551657
JL_DLLEXPORT uint8_t jl_ir_flag_inlineable(jl_array_t *data) JL_NOTSAFEPOINT;
16561658
JL_DLLEXPORT uint8_t jl_ir_flag_pure(jl_array_t *data) JL_NOTSAFEPOINT;

0 commit comments

Comments
 (0)