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

JuliaLang / julia / #37552

pending completion
#37552

push

local

web-flow
Abbreviate varinfo signature and re-order for consistency (#48860)

1 of 1 new or added line in 1 file covered. (100.0%)

72746 of 83846 relevant lines covered (86.76%)

34617131.18 hits per line

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

5.45
/stdlib/Profile/src/Allocs.jl
1
module Allocs
2

3
using Base.StackTraces: StackTrace, StackFrame, lookup
4
using Base: InterpreterIP
5

6
# --- Raw results structs, originally defined in C ---
7

8
# The C jl_bt_element_t object contains either an IP pointer (size_t) or a void*.
9
const BTElement = Csize_t;
10

11
# matches jl_raw_backtrace_t on the C side
12
struct RawBacktrace
13
    data::Ptr{BTElement} # in C: *jl_bt_element_t
14
    size::Csize_t
15
end
16

17
# matches jl_raw_alloc_t on the C side
18
struct RawAlloc
19
    type::Ptr{Type}
20
    backtrace::RawBacktrace
21
    size::Csize_t
22
    task::Ptr{Cvoid}
23
    timestamp::UInt64
24
end
25

26
# matches jl_profile_allocs_raw_results_t on the C side
27
struct RawResults
28
    allocs::Ptr{RawAlloc}
29
    num_allocs::Csize_t
30
end
31

32
"""
33
    Profile.Allocs.@profile [sample_rate=0.1] expr
34

35
Profile allocations that happen during `expr`, returning
36
both the result and and AllocResults struct.
37

38
A sample rate of 1.0 will record everything; 0.0 will record nothing.
39

40
```julia
41
julia> Profile.Allocs.@profile sample_rate=0.01 peakflops()
42
1.03733270279065e11
43

44
julia> results = Profile.Allocs.fetch()
45

46
julia> last(sort(results.allocs, by=x->x.size))
47
Profile.Allocs.Alloc(Vector{Any}, Base.StackTraces.StackFrame[_new_array_ at array.c:127, ...], 5576)
48
```
49

50
The best way to visualize these is currently with the
51
[PProf.jl](https://github.com/JuliaPerf/PProf.jl) package,
52
by invoking `PProf.Allocs.pprof`.
53

54
!!! note
55
    The current implementation of the Allocations Profiler does not
56
    capture types for all allocations. Allocations for which the profiler
57
    could not capture the type are represented as having type
58
    `Profile.Allocs.UnknownType`.
59

60
    You can read more about the missing types and the plan to improve this, here:
61
    <https://github.com/JuliaLang/julia/issues/43688>.
62

63
!!! compat "Julia 1.8"
64
    The allocation profiler was added in Julia 1.8.
65
"""
66
macro profile(opts, ex)
67
    _prof_expr(ex, opts)
68
end
69
macro profile(ex)
70
    _prof_expr(ex, :(sample_rate=0.1))
71
end
72

73
function _prof_expr(expr, opts)
×
74
    quote
×
75
        $start(; $(esc(opts)))
×
76
        try
×
77
            $(esc(expr))
×
78
        finally
79
            $stop()
×
80
        end
81
    end
82
end
83

84
"""
85
    Profile.Allocs.start(sample_rate::Real)
86

87
Begin recording allocations with the given sample rate
88
A sample rate of 1.0 will record everything; 0.0 will record nothing.
89
"""
90
function start(; sample_rate::Real)
×
91
    ccall(:jl_start_alloc_profile, Cvoid, (Cdouble,), Float64(sample_rate))
×
92
end
93

94
"""
95
    Profile.Allocs.stop()
96

97
Stop recording allocations.
98
"""
99
function stop()
×
100
    ccall(:jl_stop_alloc_profile, Cvoid, ())
×
101
end
102

103
"""
104
    Profile.Allocs.clear()
105

106
Clear all previously profiled allocation information from memory.
107
"""
108
function clear()
×
109
    ccall(:jl_free_alloc_profile, Cvoid, ())
×
110
    return nothing
×
111
end
112

113
"""
114
    Profile.Allocs.fetch()
115

116
Retrieve the recorded allocations, and decode them into Julia
117
objects which can be analyzed.
118
"""
119
function fetch()
×
120
    raw_results = ccall(:jl_fetch_alloc_profile, RawResults, ())
×
121
    return decode(raw_results)
×
122
end
123

124
# decoded results
125

126
struct Alloc
127
    type::Any
128
    stacktrace::StackTrace
129
    size::Int
130
    task::Ptr{Cvoid} # N.B. unrooted, may not be valid
131
    timestamp::UInt64
132
end
133

134
struct AllocResults
135
    allocs::Vector{Alloc}
136
end
137

138
# Without this, the Alloc's stacktrace prints for lines and lines and lines...
139
function Base.show(io::IO, a::Alloc)
×
140
    stacktrace_sample = length(a.stacktrace) >= 1 ? "$(a.stacktrace[1]), ..." : ""
×
141
    print(io, "$Alloc($(a.type), $StackFrame[$stacktrace_sample], $(a.size))")
×
142
end
143

144
const BacktraceCache = Dict{BTElement,Vector{StackFrame}}
145

146
# copied from julia_internal.h
147
JL_BUFF_TAG::UInt = ccall(:jl_get_buff_tag, UInt, ())
2✔
148
const JL_GC_UNKNOWN_TYPE_TAG = UInt(0xdeadaa03)
149

150
function __init__()
4✔
151
    global JL_BUFF_TAG = ccall(:jl_get_buff_tag, UInt, ())
4✔
152
end
153

154
struct CorruptType end
155
struct BufferType end
156
struct UnknownType end
157

158
function load_type(ptr::Ptr{Type})
×
159
    if UInt(ptr) < UInt(4096)
×
160
        return CorruptType
×
161
    elseif UInt(ptr) == JL_BUFF_TAG
×
162
        return BufferType
×
163
    elseif UInt(ptr) == JL_GC_UNKNOWN_TYPE_TAG
×
164
        return UnknownType
×
165
    end
166
    return unsafe_pointer_to_objref(ptr)
×
167
end
168

169
function decode_alloc(cache::BacktraceCache, raw_alloc::RawAlloc)::Alloc
×
170
    Alloc(
×
171
        load_type(raw_alloc.type),
172
        stacktrace_memoized(cache, load_backtrace(raw_alloc.backtrace)),
173
        UInt(raw_alloc.size),
174
        raw_alloc.task,
175
        raw_alloc.timestamp
176
    )
177
end
178

179
function decode(raw_results::RawResults)::AllocResults
×
180
    cache = BacktraceCache()
×
181
    allocs = [
×
182
        decode_alloc(cache, unsafe_load(raw_results.allocs, i))
183
        for i in 1:raw_results.num_allocs
184
    ]
185
    return AllocResults(allocs)
×
186
end
187

188
function load_backtrace(trace::RawBacktrace)::Vector{BTElement}
×
189
    out = Vector{BTElement}()
×
190
    for i in 1:trace.size
×
191
        push!(out, unsafe_load(trace.data, i))
×
192
    end
×
193

194
    return out
×
195
end
196

197
function stacktrace_memoized(
×
198
    cache::BacktraceCache,
199
    trace::Vector{BTElement},
200
    c_funcs::Bool=true
201
)::StackTrace
202
    stack = StackTrace()
×
203
    for ip in trace
×
204
        frames = get(cache, ip) do
×
205
            res = lookup(ip)
×
206
            cache[ip] = res
×
207
            return res
×
208
        end
209
        for frame in frames
×
210
            # Skip frames that come from C calls.
211
            if c_funcs || !frame.from_c
×
212
                push!(stack, frame)
×
213
            end
214
        end
×
215
    end
×
216
    return stack
×
217
end
218

219
end
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