• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

JuliaLang / julia / #38193

22 Aug 2025 06:29AM UTC coverage: 77.913% (+0.06%) from 77.852%
#38193

push

local

web-flow
test: add missing closewrite call (#59356)

This test would previously just hang if there was any error, instead of
printing the error as intended.

This might be better as an IOBuffer so closewrite is implicit, but
that's behavior is not well-specified right now with multiple writers,
as is done here.

48533 of 62291 relevant lines covered (77.91%)

9669197.75 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

78.05
/base/stacktraces.jl
1
# This file is a part of Julia. License is MIT: https://julialang.org/license
2

3
"""
4
Tools for collecting and manipulating stack traces. Mainly used for building errors.
5
"""
6
module StackTraces
7

8

9
import Base: hash, ==, show
10

11
using Core: CodeInfo, MethodInstance, CodeInstance
12
using Base.IRShow
13

14
export StackTrace, StackFrame, stacktrace
15

16
"""
17
    StackFrame
18

19
Stack information representing execution context, with the following fields:
20

21
- `func::Symbol`
22

23
  The name of the function containing the execution context.
24

25
- `linfo::Union{Method, Core.MethodInstance, Core.CodeInstance, Core.CodeInfo, Nothing}`
26

27
  The Method, MethodInstance, CodeInstance, or CodeInfo containing the execution context (if it could be found),
28
     or nothing (for example, if the inlining was a result of macro expansion).
29

30
- `file::Symbol`
31

32
  The path to the file containing the execution context.
33

34
- `line::Int`
35

36
  The line number in the file containing the execution context.
37

38
- `from_c::Bool`
39

40
  True if the code is from C.
41

42
- `inlined::Bool`
43

44
  True if the code is from an inlined frame.
45

46
- `pointer::UInt64`
47

48
  Representation of the pointer to the execution context as returned by `backtrace`.
49

50
"""
51
struct StackFrame # this type should be kept platform-agnostic so that profiles can be dumped on one machine and read on another
52
    "the name of the function containing the execution context"
6,178✔
53
    func::Symbol
54
    "the path to the file containing the execution context"
55
    file::Symbol
56
    "the line number in the file containing the execution context"
57
    line::Int
58
    "the CodeInstance or CodeInfo containing the execution context (if it could be found), \
59
     or nothing (for example, if the inlining was a result of macro expansion)."
60
    linfo::Union{Core.MethodInstance, Core.CodeInstance, Method, CodeInfo, Nothing}
61
    "true if the code is from C"
62
    from_c::Bool
63
    "true if the code is from an inlined frame"
64
    inlined::Bool
65
    "representation of the pointer to the execution context as returned by `backtrace`"
66
    pointer::UInt64  # Large enough to be read losslessly on 32- and 64-bit machines.
67
end
68

69
StackFrame(func, file, line) = StackFrame(Symbol(func), Symbol(file), line,
12✔
70
                                          nothing, false, false, 0)
71

72
"""
73
    StackTrace
74

75
An alias for `Vector{StackFrame}` provided for convenience; returned by calls to
76
`stacktrace`.
77
"""
78
const StackTrace = Vector{StackFrame}
79

80
const empty_sym = Symbol("")
81
const UNKNOWN = StackFrame(empty_sym, empty_sym, -1, nothing, true, false, 0) # === lookup(C_NULL)
82

83

84
#=
85
If the StackFrame has function and line information, we consider two of them the same if
86
they share the same function/line information.
87
=#
88
function ==(a::StackFrame, b::StackFrame)
89
    return a.line == b.line && a.from_c == b.from_c && a.func == b.func && a.file == b.file && a.inlined == b.inlined # excluding linfo and pointer
57,059✔
90
end
91

92
function hash(frame::StackFrame, h::UInt)
93
    h ⊻= 0xf4fbda67fe20ce88 % UInt
134✔
94
    h = hash(frame.line, h)
275,282✔
95
    h = hash(frame.file, h)
275,282✔
96
    h = hash(frame.func, h)
275,282✔
97
    h = hash(frame.from_c, h)
275,282✔
98
    h = hash(frame.inlined, h)
275,282✔
99
    return h
59✔
100
end
101

102
"""
103
    lookup(pointer::Ptr{Cvoid})::Vector{StackFrame}
104

105
Given a pointer to an execution context (usually generated by a call to `backtrace`), looks
106
up stack frame context information. Returns an array of frame information for all functions
107
inlined at that point, innermost function first.
108
"""
109
Base.@constprop :none function lookup(pointer::Ptr{Cvoid})
2,005✔
110
    infos = ccall(:jl_lookup_code_address, Any, (Ptr{Cvoid}, Cint), pointer, false)::Core.SimpleVector
2,005✔
111
    pointer = convert(UInt64, pointer)
2,005✔
112
    isempty(infos) && return [StackFrame(empty_sym, empty_sym, -1, nothing, true, false, pointer)] # this is equal to UNKNOWN
2,005✔
113
    res = Vector{StackFrame}(undef, length(infos))
2,005✔
114
    for i in 1:length(infos)
3,941✔
115
        info = infos[i]::Core.SimpleVector
2,651✔
116
        @assert length(info) == 6 "corrupt return from jl_lookup_code_address"
2,651✔
117
        func = info[1]::Symbol
2,651✔
118
        file = info[2]::Symbol
2,651✔
119
        linenum = info[3]::Int
2,651✔
120
        linfo = info[4]
2,651✔
121
        res[i] = StackFrame(func, file, linenum, linfo, info[5]::Bool, info[6]::Bool, pointer)
2,651✔
122
    end
3,297✔
123
    return res
2,005✔
124
end
125

126
const top_level_scope_sym = Symbol("top-level scope")
127

128
function lookup(ip::Base.InterpreterIP)
137✔
129
    code = ip.code
137✔
130
    if code === nothing
137✔
131
        # interpreted top-level expression with no CodeInfo
132
        return [StackFrame(top_level_scope_sym, empty_sym, 0, nothing, false, false, 0)]
×
133
    end
134
    # prepare approximate code info
135
    if code isa MethodInstance && (meth = code.def; meth isa Method)
137✔
136
        func = meth.name
×
137
        file = meth.file
×
138
        line = meth.line
×
139
        codeinfo = meth.source
×
140
    else
141
        func = top_level_scope_sym
137✔
142
        file = empty_sym
137✔
143
        line = Int32(0)
137✔
144
        if code isa Core.CodeInstance
137✔
145
            codeinfo = code.inferred::CodeInfo
×
146
            def = code.def
×
147
            if isa(def, Core.ABIOverride)
×
148
                def = def.def
×
149
            end
150
            if isa(def, MethodInstance)
×
151
                let meth = def.def
×
152
                    if isa(meth, Method)
×
153
                        func = meth.name
×
154
                        file = meth.file
×
155
                        line = meth.line
×
156
                    end
157
                end
158
            end
159
        else
160
            codeinfo = code::CodeInfo
137✔
161
        end
162
    end
163
    def = (code isa CodeInfo ? StackTraces : code) # Module just used as a token for top-level code
137✔
164
    pc::Int = max(ip.stmt + 1, 0) # n.b. ip.stmt is 0-indexed
137✔
165
    scopes = IRShow.LineInfoNode[]
137✔
166
    IRShow.append_scopes!(scopes, pc, codeinfo.debuginfo, def)
274✔
167
    if isempty(scopes)
137✔
168
        return [StackFrame(func, file, line, code, false, false, 0)]
×
169
    end
170
    closure = let inlined::Bool = false, def = def
137✔
171
        function closure_inner(lno)
144✔
172
            if inlined
479✔
173
                def = lno.method
342✔
174
                def isa Union{Method,Core.CodeInstance,MethodInstance} || (def = nothing)
684✔
175
            else
176
                def = codeinfo
137✔
177
            end
178
            sf = StackFrame(IRShow.normalize_method_name(lno.method), lno.file, lno.line, def, false, inlined, 0)
479✔
179
            inlined = true
479✔
180
            return sf
479✔
181
        end
182
    end
183
    return map(closure, scopes)
137✔
184
end
185

186
"""
187
    stacktrace([trace::Vector{Ptr{Cvoid}},] [c_funcs::Bool=false])::StackTrace
188

189
Return a stack trace in the form of a vector of `StackFrame`s. (By default stacktrace
190
doesn't return C functions, but this can be enabled.) When called without specifying a
191
trace, `stacktrace` first calls `backtrace`.
192
"""
193
Base.@constprop :none function stacktrace(trace::Vector{<:Union{Base.InterpreterIP,Ptr{Cvoid}}}, c_funcs::Bool=false)
113✔
194
    stack = StackTrace()
685✔
195
    for ip in trace
86✔
196
        for frame in lookup(ip)
1,042✔
197
            # Skip frames that come from C calls.
198
            if c_funcs || !frame.from_c
2,763✔
199
                push!(stack, frame)
699✔
200
            end
201
        end
1,432✔
202
    end
1,042✔
203
    return stack
86✔
204
end
205

206
Base.@constprop :none function stacktrace(c_funcs::Bool=false)
207
    stack = stacktrace(backtrace(), c_funcs)
6✔
208
    # Remove frame for this function (and any functions called by this function).
209
    remove_frames!(stack, :stacktrace)
210
    # also remove all of the non-Julia functions that led up to this point (if that list is non-empty)
211
    c_funcs && deleteat!(stack, 1:(something(findfirst(frame -> !frame.from_c, stack), 1) - 1))
212
    return stack
213
end
214

215
"""
216
    remove_frames!(stack::StackTrace, name::Symbol)
217

218
Takes a `StackTrace` (a vector of `StackFrames`) and a function name (a `Symbol`) and
219
removes the `StackFrame` specified by the function name from the `StackTrace` (also removing
220
all frames above the specified function). Primarily used to remove `StackTraces` functions
221
from the `StackTrace` prior to returning it.
222
"""
223
function remove_frames!(stack::StackTrace, name::Symbol)
×
224
    deleteat!(stack, 1:something(findlast(frame -> frame.func == name, stack), 0))
×
225
    return stack
×
226
end
227

228
function remove_frames!(stack::StackTrace, names::Vector{Symbol})
×
229
    deleteat!(stack, 1:something(findlast(frame -> frame.func in names, stack), 0))
×
230
    return stack
×
231
end
232

233
"""
234
    remove_frames!(stack::StackTrace, m::Module)
235

236
Return the `StackTrace` with all `StackFrame`s from the provided `Module` removed.
237
"""
238
function remove_frames!(stack::StackTrace, m::Module)
239
    filter!(f -> !from(f, m), stack)
1✔
240
    return stack
×
241
end
242

243
is_top_level_frame(f::StackFrame) = f.linfo isa CodeInfo || (f.linfo === nothing && f.func === top_level_scope_sym)
×
244

245
function frame_method_or_module(lkup::StackFrame)
246
    code = lkup.linfo
1,168✔
247
    code isa Method && return code
1,168✔
248
    code isa Module && return code
221✔
249
    mi = frame_mi(lkup)
1,172✔
250
    mi isa MethodInstance || return nothing
1,172✔
251
    return mi.def
1,164✔
252
end
253

254
function frame_mi(lkup::StackFrame)
255
    code = lkup.linfo
2,331✔
256
    code isa Core.CodeInstance && (code = code.def)
2,331✔
257
    code isa Core.ABIOverride && (code = code.def)
2,331✔
258
    code isa MethodInstance || return nothing
2,335✔
259
    return code
2,327✔
260
end
261

262
function show_spec_linfo(io::IO, frame::StackFrame)
2,226✔
263
    linfo = frame.linfo
2,226✔
264
    if linfo === nothing
2,226✔
265
        if frame.func === empty_sym
930✔
266
            print(io, "ip:0x", string(frame.pointer, base=16))
×
267
        elseif frame.func === top_level_scope_sym
930✔
268
            print(io, "top-level scope")
23✔
269
        else
270
            Base.print_within_stacktrace(io, Base.demangle_function_name(string(frame.func)), bold=true)
907✔
271
        end
272
    elseif linfo isa CodeInfo
1,296✔
273
        print(io, "top-level scope")
132✔
274
    elseif linfo isa Module
1,164✔
275
        Base.print_within_stacktrace(io, Base.demangle_function_name(string(frame.func)), bold=true)
×
276
    else
277
        if linfo isa Union{MethodInstance, CodeInstance}
1,164✔
278
            def = frame_method_or_module(frame)
2,328✔
279
            if def isa Module
1,164✔
280
                Base.show_mi(io, linfo::MethodInstance, #=from_stackframe=#true)
×
281
            elseif linfo isa CodeInstance && linfo.owner !== nothing
1,164✔
282
                show_custom_spec_sig(io, linfo.owner, linfo, frame)
1✔
283
            else
284
                # Equivalent to the default implementation of `show_custom_spec_sig`
285
                # for `linfo isa CodeInstance`, but saves an extra dynamic dispatch.
286
                show_spec_sig(io, def, frame_mi(frame).specTypes)
1,163✔
287
            end
288
        else
289
            m = linfo::Method
×
290
            show_spec_sig(io, m, m.sig)
×
291
        end
292
    end
293
end
294

295
# Can be extended by compiler packages to customize backtrace display of custom code instance frames
296
function show_custom_spec_sig(io::IO, @nospecialize(owner), linfo::CodeInstance, frame::StackFrame)
297
    mi = Base.get_ci_mi(linfo)
2✔
298
    return show_spec_sig(io, mi.def, mi.specTypes)
1✔
299
end
300

301
function show_spec_sig(io::IO, m::Method, @nospecialize(sig::Type))
1,164✔
302
    if get(io, :limit, :false)::Bool
3,551✔
303
        if !haskey(io, :displaysize)
978✔
304
            io = IOContext(io, :displaysize => displaysize(io))
129✔
305
        end
306
    end
307
    argnames = Base.method_argnames(m)
2,328✔
308
    argnames = replace(argnames, :var"#unused#" => :var"")
1,164✔
309
    if m.nkw > 0
1,164✔
310
        # rearrange call kw_impl(kw_args..., func, pos_args...) to func(pos_args...; kw_args)
311
        kwarg_types = Any[ fieldtype(sig, i) for i = 2:(1+m.nkw) ]
168✔
312
        uw = Base.unwrap_unionall(sig)::DataType
146✔
313
        pos_sig = Base.rewrap_unionall(Tuple{uw.parameters[(m.nkw+2):end]...}, sig)
146✔
314
        kwnames = argnames[2:(m.nkw+1)]
292✔
315
        for i = 1:length(kwnames)
160✔
316
            str = string(kwnames[i])::String
168✔
317
            if endswith(str, "...")
168✔
318
                kwnames[i] = Symbol(str[1:end-3])
×
319
            end
320
        end
190✔
321
        Base.show_tuple_as_call(io, m.name, pos_sig;
146✔
322
                                demangle=true,
323
                                kwargs=zip(kwnames, kwarg_types),
324
                                argnames=argnames[m.nkw+2:end])
325
    else
326
        Base.show_tuple_as_call(io, m.name, sig; demangle=true, argnames)
1,018✔
327
    end
328
end
329

330
function show(io::IO, frame::StackFrame)
140✔
331
    show_spec_linfo(io, frame)
140✔
332
    if frame.file !== empty_sym
140✔
333
        file_info = basename(string(frame.file))
136✔
334
        print(io, " at ")
136✔
335
        print(io, file_info, ":")
136✔
336
        if frame.line >= 0
136✔
337
            print(io, frame.line)
133✔
338
        else
339
            print(io, "?")
3✔
340
        end
341
    end
342
    if frame.inlined
140✔
343
        print(io, " [inlined]")
41✔
344
    end
345
end
346

347
function Base.parentmodule(frame::StackFrame)
348
    linfo = frame.linfo
4,633✔
349
    if linfo isa CodeInstance
4,633✔
350
        linfo = linfo.def
1,861✔
351
        if isa(linfo, Core.ABIOverride)
1,861✔
352
            linfo = linfo.def
×
353
        end
354
    end
355
    if linfo isa MethodInstance
4,633✔
356
        def = linfo.def
2,409✔
357
        if def isa Module
2,409✔
358
            return def
×
359
        else
360
            return (def::Method).module
2,409✔
361
        end
362
    elseif linfo isa Method
2,224✔
363
        return linfo.module
×
364
    elseif linfo isa Module
2,224✔
365
        return linfo
×
366
    else
367
        # The module is not always available (common reasons include
368
        # frames arising from the interpreter)
369
        nothing
766✔
370
    end
371
end
372

373
"""
374
    from(frame::StackFrame, filter_mod::Module)::Bool
375

376
Return whether the `frame` is from the provided `Module`
377
"""
378
function from(frame::StackFrame, m::Module)
×
379
    return parentmodule(frame) === m
×
380
end
381

382
end  # module StackTraces
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc