Reference

Computing flame graphs

FlameGraphs.flamegraphFunction
g = 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: if true, include stackframes collected from ccalled code.
  • recur (supported on Julia 1.4+): represent recursive calls as if they corresponded to iteration.
  • norepl: if true, the portions of stacktraces deeper than REPL.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's sort! function and anything called by it. See also recur for an alternative strategy.
  • combine: if true, instruction pointers that correspond to the same line of code are combined into a single stackframe
  • filter: drop all branches that do not satisfy the filter condition. Condition can be string, regex or any function, that can be applied to NodeData. For example, filter = "mapslices" or equivalently filter = x -> (x.sf.func == :mapslices) removes all branches that do not contain mapslices 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.

source
FlameGraphs.NodeDataType
data = 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 dispatch
  • status & 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.

source

Rendering flame graphs

FlameGraphs.flamepixelsFunction
img = 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.

source
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.

source
FlameGraphs.flametagsFunction
tagimg = 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.

source
FlameGraphs.FlameColorsType
fcolor = 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 color
  • colorfont is used when annotating stackframes with text
  • colorsrt highlights runtime dispatch, typically a costly process
  • colorsgc 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.

source
FlameGraphs.StackFrameCategoryType
StackFrameCategory(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)
source
FlameGraphs.default_modcatFunction
default_modcat(mod::Module)

Returns dark gray for Core.Compiler, light gray for Core, light blue for Base, and otherwise returns nothing.

source
FlameGraphs.default_loccatFunction
default_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.

source

I/O

FlameGraphs.saveFunction
save(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()...)
source
FlameGraphs.loadFunction
data, 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.

source