-- Require luasocket only when needed. local socket local profiler = {} local metrics = { {name = "Wall", get = function() return socket.gettime() end}, {name = "CPU", get = os.clock}, {name = "Memory", get = function() return collectgarbage("count") end} } local functions = { {name = "load", module = "cache"}, {name = "update", module = "cache"}, {name = "decode", module = "decoder"}, {name = "parse", module = "parser"}, {name = "run", module = "stages.unwrap_parens"}, {name = "run", module = "stages.parse_inline_options"}, {name = "run", module = "stages.linearize"}, {name = "run", module = "stages.name_functions"}, {name = "run", module = "stages.resolve_locals"}, {name = "run", module = "stages.detect_bad_whitespace"}, {name = "run", module = "stages.detect_cyclomatic_complexity"}, {name = "run", module = "stages.detect_empty_blocks"}, {name = "run", module = "stages.detect_empty_statements"}, {name = "run", module = "stages.detect_globals"}, {name = "run", module = "stages.detect_reversed_fornum_loops"}, {name = "run", module = "stages.detect_unbalanced_assignments"}, {name = "run", module = "stages.detect_uninit_accesses"}, {name = "run", module = "stages.detect_unreachable_code"}, {name = "run", module = "stages.detect_unused_fields"}, {name = "run", module = "stages.detect_unused_locals"}, {name = "filter", module = "filter"}, {name = "normalize", module = "options"} } local stats = {} local start_values = {} local function start_phase(name) for _, metric in ipairs(metrics) do start_values[metric][name] = metric.get() end end local function stop_phase(name) for _, metric in ipairs(metrics) do local increment = metric.get() - start_values[metric][name] stats[metric][name] = (stats[metric][name] or 0) + increment end end local phase_stack = {} local function push_phase(name) local prev_name = phase_stack[#phase_stack] if prev_name then stop_phase(prev_name) end table.insert(phase_stack, name) start_phase(name) end local function pop_phase(name) assert(name == table.remove(phase_stack)) stop_phase(name) local prev_name = phase_stack[#phase_stack] if prev_name then start_phase(prev_name) end end local function continue_wrapper(name, ...) pop_phase(name) return ... end local function wrap(fn, name) return function(...) push_phase(name) return continue_wrapper(name, fn(...)) end end local function patch(fn) local mod = require("luacheck." .. fn.module) local orig = mod[fn.name] local new = wrap(orig, fn.module .. "." .. fn.name) mod[fn.name] = new end function profiler.init() socket = require "socket" collectgarbage("stop") for _, metric in ipairs(metrics) do stats[metric] = {} start_values[metric] = {} end for _, fn in ipairs(functions) do patch(fn) end push_phase("other") end function profiler.report() pop_phase("other") for _, metric in ipairs(metrics) do local names = {} local total = 0 for name, value in pairs(stats[metric]) do table.insert(names, name) total = total + value end table.sort(names, function(name1, name2) local stats1 = stats[metric][name1] local stats2 = stats[metric][name2] if stats1 ~= stats2 then return stats1 > stats2 else return name1 < name2 end end) print(metric.name) print() for _, name in ipairs(names) do print(("%s - %f (%f%%)"):format(name, stats[metric][name], stats[metric][name] / total * 100)) end print(("Total - %f"):format(total)) print() end end return profiler