Lua

LuaJIT has an integrated statistical profiler with very low overhead. It allows sampling the currently executing stack and other parameters in regular intervals.

The integrated profiler can be accessed from three levels:

High-Level Profiler

The bundled high-level profiler offers basic profiling functionality. It generates simple textual summaries or source code annotations. It can be accessed with the -jp command line option or from Lua code by loading the underlying jit.p module.

To cut to the chase — run this to get a CPU usage profile by function name:

luajit -jp myapp.lua

It's not a stated goal of the bundled profiler to add every possible option or to cater for special profiling needs. The low-level profiler APIs are documented below. They may be used by third-party authors to implement advanced functionality, e.g. IDE integration or graphical profilers.

Note: Sampling works for both interpreted and JIT-compiled code. The results for JIT-compiled code may sometimes be surprising. LuaJIT heavily optimizes and inlines Lua code — there's no simple one-to-one correspondence between source code lines and the sampled machine code.

-jp=[options[,output]]

The -jp command line option starts the high-level profiler. When the application run by the command line terminates, the profiler stops and writes the results to stdout or to the specified output file.

The options argument specifies how the profiling is to be performed:

The default output for -jp is a list of the most CPU consuming spots in the application. Increasing the stack dump depth with (say) -jp=2 may help to point out the main callers or callees of hotspots. But sample aggregation is still flat per unique stack dump.

To get a two-level view (split view) of callers/callees, use -jp=s or -jp=-s. The percentages shown for the second level are relative to the first level.

To see how much time is spent in each line relative to a function, use -jp=fl.

To see how much time is spent in different VM states or zones, use -jp=v or -jp=z.

Combinations of v/z with f/F/l produce two-level views, e.g. -jp=vf or -jp=fv. This shows the time spent in a VM state or zone vs. hotspots. This can be used to answer questions like "Which time-consuming functions are only interpreted?" or "What's the garbage collector overhead for a specific function?".

Multiple options can be combined — but not all combinations make sense, see above. E.g. -jp=3si4m1 samples three stack levels deep in 4ms intervals and shows a split view of the CPU consuming functions and their callers with a 1% threshold.

Source code annotations produced by -jp=a or -jp=A are always flat and at the line level. Obviously, the source code files need to be readable by the profiler script.

The high-level profiler can also be started and stopped from Lua code with:

require("jit.p").start(options, output)
...
require("jit.p").stop()

jit.zone — Zones

Zones can be used to provide information about different parts of an application to the high-level profiler. E.g. a game could make use of an "AI" zone, a "PHYS" zone, etc. Zones are hierarchical, organized as a stack.

The jit.zone module needs to be loaded explicitly:

local zone = require("jit.zone")

To show the time spent in each zone use -jp=z. To show the time spent relative to hotspots use e.g. -jp=zf or -jp=fz.

Low-level Lua API

The jit.profile module gives access to the low-level API of the profiler from Lua code. This module needs to be loaded explicitly:

local profile = require("jit.profile")

This module can be used to implement your own higher-level profiler. A typical profiling run starts the profiler, captures stack dumps in the profiler callback, adds them to a hash table to aggregate the number of samples, stops the profiler and then analyzes all captured stack dumps. Other parameters can be sampled in the profiler callback, too. But it's important not to spend too much time in the callback, since this may skew the statistics.

profile.start(mode, cb) — Start profiler

This function starts the profiler. The mode argument is a string holding options:

The cb argument is a callback function which is called with three arguments: (thread, samples, vmstate). The callback is called on a separate coroutine, the thread argument is the state that holds the stack to sample for profiling. Note: do not modify the stack of that state or call functions on it.

samples gives the number of accumulated samples since the last callback (usually 1).

vmstate holds the VM state at the time the profiling timer triggered. This may or may not correspond to the state of the VM when the profiling callback is called. The state is either 'N' native (compiled) code, 'I' interpreted code, 'C' C code, 'G' the garbage collector, or 'J' the JIT compiler.

profile.stop() — Stop profiler

This function stops the profiler.

dump = profile.dumpstack([thread,] fmt, depth) — Dump stack

This function allows taking stack dumps in an efficient manner. It returns a string with a stack dump for the thread (coroutine), formatted according to the fmt argument:

The depth argument gives the number of frames to dump, starting at the topmost frame of the thread. A negative number dumps the frames in inverse order.

The first example prints a list of the current module names and line numbers of up to 10 frames in separate lines. The second example prints semicolon-separated function names for all frames (up to 100) in inverse order:

print(profile.dumpstack(thread, "l\n", 10))
print(profile.dumpstack(thread, "lZ;", -100))

Low-level C API

The profiler can be controlled directly from C code, e.g. for use by IDEs. The declarations are in "luajit.h" (see Lua/C API extensions).

luaJIT_profile_start(L, mode, cb, data) — Start profiler

This function starts the profiler. See above for a description of the mode argument.

The cb argument is a callback function with the following declaration:

typedef void (*luaJIT_profile_callback)(void *data, lua_State *L,
                                        int samples, int vmstate);

data is available for use by the callback. L is the state that holds the stack to sample for profiling. Note: do not modify this stack or call functions on this stack — use a separate coroutine for this purpose. See above for a description of samples and vmstate.

luaJIT_profile_stop(L) — Stop profiler

This function stops the profiler.

p = luaJIT_profile_dumpstack(L, fmt, depth, len) — Dump stack

This function allows taking stack dumps in an efficient manner. See above for a description of fmt and depth.

This function returns a const char * pointing to a private string buffer of the profiler. The int *len argument returns the length of the output string. The buffer is overwritten on the next call and deallocated when the profiler stops. You either need to consume the content immediately or copy it for later use.