Reference
Computing flame graphs
FlameGraphs.flamegraph
— Functiong = flamegraph(data=Profile.fetch(); lidict=nothing, C=false, combine=true, recur=:off, norepl=true, pruned=[], filter=nothing)
Compute a graph representing profiling data. To compute it for the currently-collected profiling information, omit both data
and lidict
; if you are computing it for saved profiling data, supply both. (data
and lidict
must be a matched pair from Profile.retrieve()
.)
You can control the strategy with the following keywords:
C
: iftrue
, include stackframes collected fromccall
ed code.recur
(supported on Julia 1.4+): represent recursive calls as if they corresponded to iteration.norepl
: if true, the portions of stacktraces deeper thanREPL.eval_user_input
are discarded.pruned
: a list of(funcname, filename)
pairs that trigger the termination of this branch of the flame graph. You can use this to prevent very "tall" graphs from deeply-recursive calls, e.g.,pruned = [("sort!", "sort.jl")]
would omit nodes corresponding to Julia'ssort!
function and anything called by it. See alsorecur
for an alternative strategy.combine
: if true, instruction pointers that correspond to the same line of code are combined into a single stackframefilter
: drop all branches that do not satisfy the filter condition.Condition
can bestring
,regex
or any function, that can be applied toNodeData
. For example,filter = "mapslices"
or equivalentlyfilter = x -> (x.sf.func == :mapslices)
removes all branches that do not containmapslices
Node as a child or ancestor.threads::Union{Int,AbstractVector{Int},Nothing}
: specify which threads to include samples from.nothing
returns all.tasks::Union{Int,AbstractVector{Int},Nothing}
: specify which tasks to include samples from.nothing
returns all.
!!! compat 1.8 The threads
and tasks
kwargs require julia 1.8
g
can be inspected using AbstractTrees.jl
's print_tree
.
FlameGraphs.NodeData
— Typedata = NodeData(sf::StackFrame, status::UInt8, span::UnitRange{Int})
Data associated with a single node in a flamegraph. sf
is the stack frame (see ?StackTraces.StackFrame
). status
is a bitfield with information about this node or any "suppressed" nodes immediately called by this one:
status & 0x01
is nonzero for runtime dispatchstatus & 0x02
is nonzero for garbage collection
By default, C-language stackframes are omitted, but information about their identity is accumulated into their caller's status
.
length(span)
is the number of times this stackframe was captured at this depth and location in the flame graph. The starting index begins with the caller's starting span
but increments to ensure each child's span
occupies a distinct subset of the caller's span
. Concretely, span
is the range of indexes that will be occupied by this stackframe when the flame graph is rendered.
Rendering flame graphs
FlameGraphs.flamepixels
— Functionimg = flamepixels(g; kwargs...)
Return a flamegraph as a matrix of RGB colors. The first dimension corresponds to cost, the second dimension to depth in the call stack.
See also flametags
.
img = flamepixels(fcolor, g; costscale=nothing)
Return a flamegraph as a matrix of RGB colors, customizing the color choices.
fcolor
fcolor
is a function that returns the color used for the current item in the call stack. See FlameColors
for the default implementation of fcolor
.
If you provide a custom fcolor
, it must support the following API:
colorbg = fcolor(:bg)
colorfont = fcolor(:font)
must return the background and font colors.
colornode = fcolor(nextidx::Vector{Int}, j, data::NodeData)
chooses the color for the node represented by data
(see NodeData
). j
corresponds to depth in the call stack and nextidx[j]
holds the state for the next color choice. In general, if you have a list of colors, fcolor
should cycle nextidx[j]
to ensure that the next call to fcolor
with this j
moves on to the next color. (However, you may not want to increment nextidx[j]
if you are choosing the color by some means other than cycling through a list.)
By accessing data.sf
, you can choose to color individual nodes based on the identity of the stackframe.
costscale
costscale
can be used to limit the size of img
when profiling collected a large number of stacktraces. The size of the first dimension of img
is proportional to the total number of stacktraces collected during profiling. costscale
is the constant of proportionality; for example, setting costscale=0.2
would mean that size(img, 1)
would be approximately 1/5 the number of stacktraces collected by the profiler. The default value of nothing
imposes an upper bound of approximately 1000 pixels along the first dimension, with costscale=1
chosen if the number of samples is less than 1000.
FlameGraphs.flametags
— Functiontagimg = flametags(g, img)
From a flame graph g
, generate an array tagimg
with the same axes as img
, encoding the stackframe represented by each pixel of img
.
See flamepixels
to generate img
.
FlameGraphs.FlameColors
— Typefcolor = FlameColors(n::Integer=2;
colorbg=colorant"white", colorfont=colorant"black",
colorsrt=colorant"crimson", colorsgc=colorant"orange")
Choose a set of colors for rendering a flame graph. There are several special colors:
colorbg
is the background colorcolorfont
is used when annotating stackframes with textcolorsrt
highlights runtime dispatch, typically a costly processcolorsgc
highlights garbage-collection events
n
specifies the number of "other" colors to choose when one of the above is not relevant. FlameColors
chooses 2n
colors: the first n
colors for odd depths in the stacktrace and the last n
colors for even depths in the stacktrace. Consequently, different stackframes will typically be distinguishable from one another by color.
The highlighting can be disabled by passing nothing
or a zero-element vector for colorsrt
or colorsgc
. When a single color is passed for colorsrt
or colorsgc
, this method generates four variant colors slightly different from the specified color. colorsrt
or colorsgc
can also be specified as multiple colors with a vector. The first half of the vector is for odd depths and the second half is for even depths. By using a one-element vector instead of a single color, the specified color is always used.
While the return value is a struct
, it is callable and can be used as the fcolor
input for flamepixels
.
FlameGraphs.StackFrameCategory
— TypeStackFrameCategory(modcat=FlameGraphs.default_modcat,
loccat=FlameGraphs.default_loccat,
colorbg=colorant"white",
colorfont=colorant"black")
Colorize stackframes based on their category.
modcat(mod::Module)
should return a color based on the stackframe's module, or nothing
if it cannot categorize the stack frame based on the module.
loccat(sf::StackFrame)
must return a color. It can use any of the fields of the stackframe, but func
, file
, line
, and from_c
might be common choices.
colorbg
is the background color, and colorfont
stores the choice of font color.
Examples
using Plots, Profile, FlameGraphs
@profile plot(rand(5)) # "time to first plot"
g = flamegraph(C=true)
img = flamepixels(StackFrameCategory(), g)
Or you can tweak the coloration yourself:
function modcat(mod)
mod == Plots && return colorant"purple"
return nothing
end
img = flamepixels(StackFrameCategory(modcat), g)
FlameGraphs.default_modcat
— Functiondefault_modcat(mod::Module)
Returns dark gray for Core.Compiler
, light gray for Core
, light blue for Base
, and otherwise returns nothing
.
FlameGraphs.default_loccat
— Functiondefault_loccat(sf::StackFrame)
Returns yellow for LLVM, orange for any other ccall
, dark gray for anything in ./compiler
, light blue for Base code, and red for anything else.
I/O
FlameGraphs.save
— Functionsave(f::FileIO.File)
save(f::FileIO.File, data, lidict)
save(filename::AbstractString, data, ldict)
Save profiling data to a file. If data
and lidict
are not supplied, they are obtained from
data, lidict = Profile.retrieve()
Note that the data saved to the file discard some system-specific information to allow portability. Some visualization modes, like StackFrameCategory
, are not available for data loaded from such files.
These files conventionally have the extension ".jlprof". If you just supply a string filename ending with this extension, you must pass data
and lidict
explicitly, because FileIO has its own interpretation of the meaning of save
with no arguments.
Example
For this to work, you need to pkg> add FileIO FlameGraphs
.
julia> using Profile, FileIO # you don't even need to explicitly use `FlameGraphs`
julia> @profile mapslices(sum, rand(3,3,3,3), dims=[1,2]);
julia> save("/tmp/myprof.jlprof", Profile.retrieve()...)
FlameGraphs.load
— Functiondata, lidict = load(f::FileIO.File)
data, lidict = load(filename::AbstractString)
g = load(...)
Load profiling data. You can reconstruct the flame graph from flamegraph(data; lidict=lidict)
. Some files may already store the data in graph format, and return a single argument g
.