Created
September 30, 2024 20:44
-
-
Save waldnercharles/3b62d57db9342f184b91f60505b3cd4d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
---@class Profiler | |
local profiler = { | |
internal_fns = {}, | |
call_stack = {}, | |
call_tree = {}, | |
clock = function() | |
return get_ticks() / get_tick_frequency() | |
end | |
} | |
local function get_filename_without_extension(path) | |
local filename = string.match(path, "^.+[\\/](.+)$") or path or "?" | |
return string.match(filename, "(.+)%..+$") or filename | |
end | |
local function find_child(parent, id) | |
for _, child in ipairs(parent.children) do if child.id == id then return child end end | |
end | |
local function on_function_call(info) | |
local parent = (#profiler.call_stack > 0 and profiler.call_stack[#profiler.call_stack]) or { children = profiler.call_tree } | |
local node_id = (parent.id or "[ROOT]")..string.format(":%s:%d", info.name or "?", info.currentline) | |
local node = find_child(parent, node_id) | |
if not node then | |
node = { | |
id = node_id, | |
name = info.name or "?", | |
src = get_filename_without_extension(info.short_src or "?"), | |
defined_line = info.linedefined, | |
line = info.currentline, | |
calls = 0, | |
duration = 0, | |
parent = parent, | |
children = {} | |
} | |
if parent then | |
table.insert(parent.children, node) | |
end | |
end | |
node.called_at = profiler.clock() | |
node.calls = node.calls + 1 | |
table.insert(profiler.call_stack, node) | |
end | |
local function on_function_return() | |
if (#profiler.call_stack == 0) then return end | |
local node = table.remove(profiler.call_stack) | |
local dt = profiler.clock() - node.called_at | |
node.duration = node.duration + dt | |
end | |
local function hook(event, info) | |
info = info or debug.getinfo(2, "flnS") | |
local f = info.func | |
-- ignore functions from the profiler | |
if profiler.internal_fns[f] then | |
return | |
end | |
if event == "call" then | |
on_function_call(info) | |
elseif event == "tail call" then | |
local prev = debug.getinfo(3, "fnS") | |
hook("return", prev) | |
hook("call", info) | |
elseif event == "return" then | |
on_function_return() | |
end | |
end | |
function profiler.reset() | |
profiler.call_stack = {} | |
profiler.call_tree = {} | |
end | |
function profiler.start() | |
debug.sethook(hook, "cr") | |
end | |
function profiler.stop() | |
debug.sethook() | |
end | |
local function imgui_call_tree_node(tree) | |
if (not tree) then return end | |
for _, node in ipairs(tree) do | |
local line_number = (node.parent.line or node.line_defined or -1) | |
local label = string.format("%s.%s%s - %.6f seconds (called %d times)", node.src, node.name, (line_number > -1 and ":"..line_number) or "", node.duration, node.calls) | |
if imgui_tree_node(node.id, #node.children == 0 and ImGuiTreeNodeFlags_Leaf or ImGuiTreeNodeFlags_None, label) then | |
if #node.children > 0 then | |
imgui_call_tree_node(node.children) | |
end | |
imgui_tree_pop() | |
end | |
end | |
end | |
function profiler.imgui_call_tree() | |
imgui_text("Call Tree") | |
imgui_call_tree_node(profiler.call_tree) | |
end | |
for _, v in pairs(profiler) do | |
if type(v) == "function" then | |
profiler.internal_fns[v] = true | |
end | |
end | |
return profiler |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment