Embeddable Lua Profiler is a pure Lua statistical/sampling profiler. It depends on a clock function and the debug hooks.
💡
|
LuaJIT 2.1 has a built-in profiler of much better quality than this library. |
|
With PUC Lua, unlike LuaJIT, the debug hook is per Lua thread instead of being global. Under those circumstances the profiler will not be able to sample other threads (coroutines) by default; watch() can be called on each created coroutine for complete coverage, but the result may not be as good when there is a lot of thread switching.
|
The profiler sets the debug hook (for the current and watched Lua threads) and tries to keep the hook event synced with the sampling period by adjusting the instruction period, then samples are taken periodically within an error margin.
Each sample is assigned to a stack dump identifier string formatted as a newline (\n
) separated list of stack entries, starting with the top of the stack.
Each stack entry is formatted as what:short_src:name:currentline
(see debug fields).
The identifier ?
represents missed samples (theoric samples that couldn’t be recorded). They can indicate a clock function of poor quality or execution not covered by debug hooks (JIT compiled code, C function, something hanging, etc.).
💡
|
The stack dump depth can be increased to locate more precisely the hotspots. |
|
The quality of the profile depends on the quality of the clock function and the number of samples. Enough samples must be recorded to get meaningful measures (at least thousands ?). |
-- Set clock function.
-- (the default function is os.clock)
--
-- f_clock(): should return the current execution time reference in seconds
ELProfiler.setClock(f_clock)
-- Watch a Lua thread/coroutine for profiling.
-- LuaJIT has a global debug hook, thus this function should only be used with PUC Lua.
-- Once the thread is referenced, it will be recorded by all subsequent uses of the library.
ELProfiler.watch(thread)
-- Start profiling.
-- With PUC Lua, start()/stop() functions must be called in the same thread,
-- main or another one (in this case the main thread will not be recorded,
-- unless watched).
--
-- period: (optional) sampling period in seconds (default: 0.01 => 10ms/100Hz)
-- stack_depth: (optional) stack dump depth (default: 1)
ELProfiler.start(period, stack_depth)
-- Stop profiling.
-- return profile data {} or nil if not running
--- duration: profile duration in seconds
--- samples_count: total number of samples
--- samples: map of stack dump identifier string => number of samples
ELProfiler.stop()
-- Create text report from profile data.
-- threshold: (optional) minimum samples fraction required (0.01 => 1%, default: 0)
-- return formatted string
ELProfiler.format(profile_data, threshold)