commit
1fe6c3b880
@ -0,0 +1,650 @@
|
||||
--[[
|
||||
|
||||
== Introduction ==
|
||||
|
||||
Note that this requires os.clock(), debug.sethook(),
|
||||
and debug.getinfo() or your equivalent replacements to
|
||||
be available if this is an embedded application.
|
||||
|
||||
Example usage:
|
||||
|
||||
profiler = newProfiler()
|
||||
profiler:start()
|
||||
|
||||
< call some functions that take time >
|
||||
|
||||
profiler:stop()
|
||||
|
||||
local outfile = io.open( "profile.txt", "w+" )
|
||||
profiler:report( outfile )
|
||||
outfile:close()
|
||||
|
||||
== Optionally choosing profiling method ==
|
||||
|
||||
The rest of this comment can be ignored if you merely want a good profiler.
|
||||
|
||||
newProfiler(method, sampledelay):
|
||||
|
||||
If method is omitted or "time", will profile based on real performance.
|
||||
optionally, frequency can be provided to control the number of opcodes
|
||||
per profiling tick. By default this is 100000, which (on my system) provides
|
||||
one tick approximately every 2ms and reduces system performance by about 10%.
|
||||
This can be reduced to increase accuracy at the cost of performance, or
|
||||
increased for the opposite effect.
|
||||
|
||||
If method is "call", will profile based on function calls. Frequency is
|
||||
ignored.
|
||||
|
||||
|
||||
"time" may bias profiling somewhat towards large areas with "simple opcodes",
|
||||
as the profiling function (which introduces a certain amount of unavoidable
|
||||
overhead) will be called more often. This can be minimized by using a larger
|
||||
sample delay - the default should leave any error largely overshadowed by
|
||||
statistical noise. With a delay of 1000 I was able to achieve inaccuray of
|
||||
approximately 25%. Increasing the delay to 100000 left inaccuracy below my
|
||||
testing error.
|
||||
|
||||
"call" may bias profiling heavily towards areas with many function calls.
|
||||
Testing found a degenerate case giving a figure inaccurate by approximately
|
||||
20,000%. (Yes, a multiple of 200.) This is, however, more directly comparable
|
||||
to common profilers (such as gprof) and also gives accurate function call
|
||||
counts, which cannot be retrieved from "time".
|
||||
|
||||
I strongly recommend "time" mode, and it is now the default.
|
||||
|
||||
== History ==
|
||||
|
||||
2008-09-16 - Time-based profiling and conversion to Lua 5.1
|
||||
by Ben Wilhelm ( zorba-pepperfish@pavlovian.net ).
|
||||
Added the ability to optionally choose profiling methods, along with a new
|
||||
profiling method.
|
||||
|
||||
Converted to Lua 5, a few improvements, and
|
||||
additional documentation by Tom Spilman ( tom@sickheadgames.com )
|
||||
|
||||
Additional corrections and tidying by original author
|
||||
Daniel Silverstone ( dsilvers@pepperfish.net )
|
||||
|
||||
== Status ==
|
||||
|
||||
Daniel Silverstone is no longer using this code, and judging by how long it's
|
||||
been waiting for Lua 5.1 support, I don't think Tom Spilman is either. I'm
|
||||
perfectly willing to take on maintenance, so if you have problems or
|
||||
questions, go ahead and email me :)
|
||||
-- Ben Wilhelm ( zorba-pepperfish@pavlovian.net ) '
|
||||
|
||||
== Copyright ==
|
||||
|
||||
Lua profiler - Copyright Pepperfish 2002,2003,2004
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
deal in the Software without restriction, including without limitation the
|
||||
rights to use, copy, modify, merge, publish, distribute, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to
|
||||
do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
IN THE SOFTWARE.
|
||||
|
||||
--]]
|
||||
|
||||
local _ENV = mkmodule('profiler')
|
||||
|
||||
|
||||
--
|
||||
-- All profiler related stuff is stored in the top level table '_profiler'
|
||||
--
|
||||
local _profiler = {}
|
||||
local DEFAULT_FILTERED_FUNC = 1
|
||||
local DEFAULT_MISSING_FUNC = 3
|
||||
|
||||
|
||||
--
|
||||
-- newProfiler() creates a new profiler object for managing
|
||||
-- the profiler and storing state. Note that only one profiler
|
||||
-- object can be executing at one time.
|
||||
--
|
||||
function newProfiler(variant, sampledelay)
|
||||
if _profiler.running then
|
||||
print("Profiler already running.")
|
||||
return
|
||||
end
|
||||
|
||||
variant = variant or "time"
|
||||
|
||||
if variant ~= "time" and variant ~= "call" then
|
||||
print("Profiler method must be 'time' or 'call'.")
|
||||
return
|
||||
end
|
||||
|
||||
local newprof = {}
|
||||
for k,v in pairs(_profiler) do
|
||||
newprof[k] = v
|
||||
end
|
||||
newprof.variant = variant
|
||||
newprof.sampledelay = sampledelay or 10000
|
||||
return newprof
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- This function stops the profiler. It will do nothing
|
||||
-- if a profiler is not running, and nothing if it isn't
|
||||
-- the currently running profiler.
|
||||
--
|
||||
function _profiler.stop(self)
|
||||
if _profiler.running ~= self then
|
||||
return
|
||||
end
|
||||
-- Stop the profiler.
|
||||
debug.sethook()
|
||||
_profiler.running = nil
|
||||
end
|
||||
local function get_stats(rawstats, prevented_functions, func, depth)
|
||||
if prevented_functions[func] then
|
||||
return nil
|
||||
end
|
||||
local stats = rawstats[func]
|
||||
if not stats then
|
||||
depth = depth + 1
|
||||
ci = debug.getinfo(depth,"fnS")
|
||||
if ci.what == "C" and ci.name == "__index" then
|
||||
local value1
|
||||
_, value1 = debug.getlocal(depth, 1)
|
||||
value1 = tostring(value1)
|
||||
value1 = value1:match("^<([^:]*):") or value1
|
||||
ci.short_src = value1
|
||||
end
|
||||
stats = {
|
||||
func = ci.func,
|
||||
anon = ci.name == nil,
|
||||
count = 0,
|
||||
time = 0,
|
||||
profile_time = 0,
|
||||
anon_child_time = 0,
|
||||
name_child_time = 0,
|
||||
children = {},
|
||||
children_time = {},
|
||||
currentline = {},
|
||||
func_info = ci,
|
||||
}
|
||||
rawstats[func] = stats
|
||||
end
|
||||
return stats
|
||||
end
|
||||
|
||||
--
|
||||
-- Simple wrapper to handle the hook. You should not
|
||||
-- be calling this directly. Duplicated to reduce overhead.
|
||||
--
|
||||
local function _profiler_hook_wrapper_by_call(action)
|
||||
local entry = os.clock()
|
||||
local self = _profiler.running
|
||||
local depth, stack = self.depth, self.stack
|
||||
if action == "call" then
|
||||
local parent = stack[depth]
|
||||
if parent and parent.should_not_profile > 0 then
|
||||
parent.should_not_profile = parent.should_not_profile + 1
|
||||
parent.profile_time = parent.profile_time + (os.clock() - entry)
|
||||
return
|
||||
end
|
||||
local ci = debug.getinfo(2,"f")
|
||||
local func = ci.func
|
||||
local rawstats = self.rawstats
|
||||
local stats = get_stats(rawstats, self.prevented_functions, func, 2)
|
||||
depth = depth + 1
|
||||
self.depth = depth
|
||||
local this = stack[depth]
|
||||
if not this then
|
||||
this = {
|
||||
should_not_profile = stats and 0 or 1,
|
||||
stats = stats,
|
||||
clock_start = entry,
|
||||
profile_time = 0,
|
||||
}
|
||||
stack[depth] = this
|
||||
else
|
||||
this.should_not_profile = stats and 0 or 1
|
||||
this.stats = stats
|
||||
this.clock_start = entry
|
||||
end
|
||||
this.profile_time = (os.clock() - entry)
|
||||
else
|
||||
if depth == 0 then
|
||||
return
|
||||
end
|
||||
local this = stack[depth]
|
||||
if this.should_not_profile > 0 then
|
||||
this.should_not_profile = this.should_not_profile - 1
|
||||
if this.should_not_profile == 0 then
|
||||
self.depth = depth - 1
|
||||
local parent = stack[self.depth]
|
||||
if parent then
|
||||
local time = entry - this.start_time - this.profile_time
|
||||
parent.stats.anon_child_time = parent.stats.anon_child_time + time
|
||||
parent.profile_time = parent.profile_time + this.profile_time + (os.clock() - entry)
|
||||
end
|
||||
else
|
||||
this.profile_time = this.profile_time + (os.clock() - entry)
|
||||
end
|
||||
return
|
||||
end
|
||||
local time = entry - this.clock_start - this.profile_time
|
||||
depth = depth - 1
|
||||
self.depth = depth
|
||||
local parent = stack[depth]
|
||||
|
||||
local this_stats,parent_stats = this.stats,parent and parent.stats or nil
|
||||
local func = this_stats.func
|
||||
|
||||
this_stats.count = this_stats.count + 1
|
||||
this_stats.time = this_stats.time + time
|
||||
this_stats.profile_time = this_stats.profile_time + this.profile_time
|
||||
if not parent then return end
|
||||
if this_stats.anon then
|
||||
parent_stats.anon_child_time = parent_stats.anon_child_time + time
|
||||
else
|
||||
parent_stats.name_child_time = parent_stats.name_child_time + time
|
||||
end
|
||||
local ch = parent_stats.children[func]
|
||||
if not ch then
|
||||
parent_stats.children[func] = 1
|
||||
parent_stats.children_time[func] = time
|
||||
else
|
||||
parent_stats.children[func] = ch + 1
|
||||
parent_stats.children_time[func] = parent_stats.children_time[func] + time
|
||||
end
|
||||
parent.profile_time = parent.profile_time + this.profile_time + (os.clock() - entry)
|
||||
end
|
||||
end
|
||||
|
||||
local function _profiler_hook_wrapper_by_time()
|
||||
local self = _profiler.running
|
||||
local timetaken = os.clock() - self.lastclock
|
||||
local rawstats = self.rawstats
|
||||
local prevented = self.prevented_functions
|
||||
local depth = 2
|
||||
local caller = debug.getinfo(depth,'fl')
|
||||
local child
|
||||
if not caller then
|
||||
return
|
||||
end
|
||||
local cf = caller.func
|
||||
if cf then
|
||||
child = get_stats(rawstats, prevented, cf, depth)
|
||||
if child then
|
||||
child.count = child.count + 1
|
||||
child.time = child.time + timetaken
|
||||
local line = caller.currentline
|
||||
child.currentline[line] = (child.currentline[line] or 0) + 1
|
||||
else
|
||||
cf = DEFAULT_FILTERED_FUNC
|
||||
end
|
||||
else
|
||||
cf = DEFAULT_MISSING_FUNC
|
||||
end
|
||||
depth = 3
|
||||
local caller = debug.getinfo(depth,'f')
|
||||
while caller do
|
||||
if caller.func then
|
||||
local this = get_stats(rawstats, prevented, caller.func, depth)
|
||||
if this then
|
||||
this.time = this.time + timetaken
|
||||
if not child or child.anon then
|
||||
this.anon_child_time = this.anon_child_time + timetaken
|
||||
else
|
||||
this.name_child_time = this.name_child_time + timetaken
|
||||
end
|
||||
local ch = this.children[cf]
|
||||
if ch then
|
||||
this.children[cf] = ch + 1
|
||||
this.children_time[cf] = this.children_time[cf] + timetaken
|
||||
else
|
||||
this.children[cf] = 1
|
||||
this.children_time[cf] = timetaken
|
||||
end
|
||||
cf = caller.func
|
||||
else
|
||||
cf = DEFAULT_FILTERED_FUNC
|
||||
end
|
||||
child = this
|
||||
else
|
||||
cf = DEFAULT_MISSING_FUNC
|
||||
child = nil
|
||||
end
|
||||
depth = depth + 1
|
||||
caller = debug.getinfo(depth, 'f')
|
||||
end
|
||||
self.lastclock = os.clock()
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- This function starts the profiler. It will do nothing
|
||||
-- if this (or any other) profiler is already running.
|
||||
--
|
||||
function _profiler.start(self)
|
||||
if _profiler.running then
|
||||
return
|
||||
end
|
||||
-- Start the profiler. This begins by setting up internal profiler state
|
||||
_profiler.running = self
|
||||
assert(_profiler.running)
|
||||
self.rawstats = {}
|
||||
self.stack = {}
|
||||
self.depth = 0
|
||||
if self.variant == "time" then
|
||||
self.lastclock = os.clock()
|
||||
debug.sethook( _profiler_hook_wrapper_by_time, "", self.sampledelay )
|
||||
elseif self.variant == "call" then
|
||||
debug.sethook( _profiler_hook_wrapper_by_call, "cr" )
|
||||
else
|
||||
error("Profiler method must be 'time' or 'call'.")
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- This writes a profile report to the output file object. If
|
||||
-- sort_by_total_time is nil or false the output is sorted by
|
||||
-- the function time minus the time in it's children.
|
||||
--
|
||||
function _profiler.report( self, outfile, sort_by_total_time )
|
||||
|
||||
outfile:write
|
||||
[[Lua Profile output created by profiler.lua. Copyright Pepperfish 2002+
|
||||
|
||||
]]
|
||||
|
||||
-- This is pretty awful.
|
||||
local terms = {}
|
||||
if self.variant == "time" then
|
||||
terms.capitalized = "Sample"
|
||||
terms.single = "sample"
|
||||
terms.pastverb = "sampled"
|
||||
elseif self.variant == "call" then
|
||||
terms.capitalized = "Call"
|
||||
terms.single = "call"
|
||||
terms.pastverb = "called"
|
||||
else
|
||||
error("Profiler method must be 'time' or 'call'.")
|
||||
end
|
||||
|
||||
local total_time = 0
|
||||
local ordering = {}
|
||||
for func,record in pairs(self.rawstats) do
|
||||
table.insert(ordering, func)
|
||||
end
|
||||
|
||||
if sort_by_total_time then
|
||||
table.sort( ordering,
|
||||
function(a,b) return self.rawstats[a].time > self.rawstats[b].time end
|
||||
)
|
||||
else
|
||||
table.sort( ordering,
|
||||
function(a,b)
|
||||
local arec = self.rawstats[a]
|
||||
local brec = self.rawstats[b]
|
||||
local atime = arec.time - (arec.anon_child_time + arec.name_child_time)
|
||||
local btime = brec.time - (brec.anon_child_time + brec.name_child_time)
|
||||
return atime > btime
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
for i=1,#ordering do
|
||||
local func = ordering[i]
|
||||
local record = self.rawstats[func]
|
||||
local thisfuncname = " " .. self:_pretty_name(func) .. " "
|
||||
if string.len( thisfuncname ) < 42 then
|
||||
thisfuncname =
|
||||
string.rep( "-", math.floor((42 - string.len(thisfuncname))/2) ) .. thisfuncname
|
||||
thisfuncname =
|
||||
thisfuncname .. string.rep( "-", 42 - string.len(thisfuncname) )
|
||||
end
|
||||
|
||||
local child_count = 0
|
||||
for _,v in pairs(record.children) do
|
||||
child_count = child_count + v
|
||||
end
|
||||
total_time = total_time + ( record.time - ( record.anon_child_time +
|
||||
record.name_child_time ) )
|
||||
outfile:write( string.rep( "-", 19 ) .. thisfuncname ..
|
||||
string.rep( "-", 19 ) .. "\n" )
|
||||
outfile:write( terms.capitalized.." count: " ..
|
||||
string.format( "%5d", record.count ) .. "\n" )
|
||||
outfile:write( terms.capitalized.." count in children: " ..
|
||||
string.format( "%5d", child_count ) .. "\n" )
|
||||
outfile:write( "Time spend total: " ..
|
||||
string.format( "%4.3f", record.time ) .. "s\n" )
|
||||
outfile:write( "Time spent in children: " ..
|
||||
string.format("%4.3f",record.anon_child_time+record.name_child_time) ..
|
||||
"s\n" )
|
||||
outfile:write( "Time spent in profiler: " ..
|
||||
string.format("%4.3f",record.profile_time) ..
|
||||
"s\n" )
|
||||
local timeinself =
|
||||
record.time - (record.anon_child_time + record.name_child_time)
|
||||
outfile:write( "Time spent in self: " ..
|
||||
string.format("%4.3f", timeinself) .. "s\n" )
|
||||
outfile:write( "Time spent per " .. terms.single .. ": " ..
|
||||
string.format("%4.6f", record.time/(record.count+(self.variant == "time" and child_count or 0))) ..
|
||||
"s/" .. terms.single .. "\n" )
|
||||
outfile:write( "Time spent in self per "..terms.single..": " ..
|
||||
string.format( "%4.6f", record.count > 0 and timeinself/record.count or 0.0 ) .. "s/" ..
|
||||
terms.single.."\n" )
|
||||
|
||||
-- Report on each child in the form
|
||||
-- Child <funcname> called n times and took a.bs
|
||||
local added_blank = 0
|
||||
for k,v in pairs(record.children) do
|
||||
if self.prevented_functions[k] == nil or
|
||||
self.prevented_functions[k] == 0
|
||||
then
|
||||
if added_blank == 0 then
|
||||
outfile:write( "\n" ) -- extra separation line
|
||||
added_blank = 1
|
||||
end
|
||||
local pretty_name
|
||||
if k == DEFAULT_FILTERED_FUNC then
|
||||
pretty_name = "(Filtered function)"
|
||||
elseif k == DEFAULT_MISSING_FUNC then
|
||||
pretty_name = "(Function pointer missing)"
|
||||
else
|
||||
pretty_name = self:_pretty_name(k)
|
||||
end
|
||||
outfile:write( "Child " .. pretty_name ..
|
||||
string.rep( " ", 41-string.len(pretty_name) ) .. " " ..
|
||||
terms.pastverb.." " .. string.format("%6d", v) )
|
||||
outfile:write( " times. Took " ..
|
||||
string.format("%4.3f", record.children_time[k] ) .. "s\n" )
|
||||
end
|
||||
end
|
||||
|
||||
local lines = {}
|
||||
for line,v in pairs(record.currentline) do
|
||||
if line >= 0 then
|
||||
lines[#lines+1] = line
|
||||
end
|
||||
end
|
||||
table.sort(lines)
|
||||
for i=1,#lines do
|
||||
local line = lines[i]
|
||||
local v = record.currentline[line]
|
||||
-- @todo How about reading the source code from the file?
|
||||
outfile:write( ("%6d %s in line %d\n"):format(v, terms.pastverb, line))
|
||||
end
|
||||
|
||||
outfile:write( "\n" ) -- extra separation line
|
||||
outfile:flush()
|
||||
end
|
||||
outfile:write( "\n\n" )
|
||||
outfile:write( "Total time spent in profiled functions: " ..
|
||||
string.format("%5.3g",total_time) .. "s\n" )
|
||||
outfile:write( [[
|
||||
|
||||
END
|
||||
]] )
|
||||
outfile:flush()
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- This writes the profile to the output file object as
|
||||
-- loadable Lua source.
|
||||
--
|
||||
function _profiler.lua_report(self,outfile)
|
||||
-- Purpose: Write out the entire raw state in a cross-referenceable form.
|
||||
local ordering = {}
|
||||
local functonum = {}
|
||||
for func,record in pairs(self.rawstats) do
|
||||
table.insert(ordering, func)
|
||||
functonum[func] = #ordering
|
||||
end
|
||||
|
||||
outfile:write(
|
||||
"-- Profile generated by profiler.lua Copyright Pepperfish 2002+\n\n" )
|
||||
outfile:write( "-- Function names\nfuncnames = {}\n" )
|
||||
for i=1,#ordering do
|
||||
local thisfunc = ordering[i]
|
||||
outfile:write( "funcnames[" .. i .. "] = " ..
|
||||
string.format("%q", self:_pretty_name(thisfunc)) .. "\n" )
|
||||
end
|
||||
outfile:write( "\n" )
|
||||
outfile:write( "-- Function times\nfunctimes = {}\n" )
|
||||
for i=1,#ordering do
|
||||
local thisfunc = ordering[i]
|
||||
local record = self.rawstats[thisfunc]
|
||||
outfile:write( "functimes[" .. i .. "] = { " )
|
||||
outfile:write( "tot=" .. record.time .. ", " )
|
||||
outfile:write( "achild=" .. record.anon_child_time .. ", " )
|
||||
outfile:write( "nchild=" .. record.name_child_time .. ", " )
|
||||
outfile:write( "count=" .. record.count .. " }\n" )
|
||||
end
|
||||
outfile:write( "\n" )
|
||||
outfile:write( "-- Child links\nchildren = {}\n" )
|
||||
for i=1,#ordering do
|
||||
local thisfunc = ordering[i]
|
||||
local record = self.rawstats[thisfunc]
|
||||
outfile:write( "children[" .. i .. "] = { " )
|
||||
for k,v in pairs(record.children) do
|
||||
if functonum[k] then -- non-recorded functions will be ignored now
|
||||
outfile:write( functonum[k] .. ", " )
|
||||
end
|
||||
end
|
||||
outfile:write( "}\n" )
|
||||
end
|
||||
outfile:write( "\n" )
|
||||
outfile:write( "-- Child call counts\nchildcounts = {}\n" )
|
||||
for i=1,#ordering do
|
||||
local thisfunc = ordering[i]
|
||||
local record = self.rawstats[thisfunc]
|
||||
outfile:write( "childcounts[" .. i .. "] = { " )
|
||||
for k,v in pairs(record.children) do
|
||||
if functonum[k] then -- non-recorded functions will be ignored now
|
||||
outfile:write( v .. ", " )
|
||||
end
|
||||
end
|
||||
outfile:write( "}\n" )
|
||||
end
|
||||
outfile:write( "\n" )
|
||||
outfile:write( "-- Child call time\nchildtimes = {}\n" )
|
||||
for i=1,#ordering do
|
||||
local thisfunc = ordering[i]
|
||||
local record = self.rawstats[thisfunc];
|
||||
outfile:write( "childtimes[" .. i .. "] = { " )
|
||||
for k,v in pairs(record.children) do
|
||||
if functonum[k] then -- non-recorded functions will be ignored now
|
||||
outfile:write( record.children_time[k] .. ", " )
|
||||
end
|
||||
end
|
||||
outfile:write( "}\n" )
|
||||
end
|
||||
outfile:write( "\n\n-- That is all.\n\n" )
|
||||
outfile:flush()
|
||||
end
|
||||
|
||||
-- Internal function to calculate a pretty name for the profile output
|
||||
function _profiler._pretty_name(self,func)
|
||||
|
||||
-- Only the data collected during the actual
|
||||
-- run seems to be correct.... why?
|
||||
local info = self.rawstats[ func ].func_info
|
||||
-- local info = debug.getinfo( func )
|
||||
|
||||
local name = ""
|
||||
if info.what == "Lua" then
|
||||
name = "L:"
|
||||
end
|
||||
if info.what == "C" then
|
||||
name = "C:"
|
||||
end
|
||||
if info.what == "main" then
|
||||
name = " :"
|
||||
end
|
||||
|
||||
if info.namewhat ~= nil then
|
||||
name = name .. info.namewhat .. ":"
|
||||
end
|
||||
if info.name == nil then
|
||||
name = name .. "<"..tostring(func) .. ">"
|
||||
else
|
||||
name = name .. info.name
|
||||
end
|
||||
|
||||
if info.short_src then
|
||||
name = name .. "@" .. info.short_src
|
||||
else
|
||||
if info.what == "C" then
|
||||
name = name .. "@?"
|
||||
else
|
||||
name = name .. "@<string>"
|
||||
end
|
||||
end
|
||||
name = name .. ":"
|
||||
if info.what == "C" then
|
||||
name = name .. "?"
|
||||
else
|
||||
name = name .. info.linedefined
|
||||
end
|
||||
|
||||
return name
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- This allows you to specify functions which you do
|
||||
-- not want profiled.
|
||||
--
|
||||
-- BUG: 2 will probably act exactly like 1 in "time" mode.
|
||||
-- If anyone cares, let me (zorba) know and it can be fixed.
|
||||
--
|
||||
function _profiler.prevent(self, func, enable)
|
||||
if enable then
|
||||
self.prevented_functions[func] = true
|
||||
else
|
||||
self.prevented_functions[func] = nil
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
_profiler.prevented_functions = {
|
||||
[_profiler.start] = true,
|
||||
[_profiler.stop] = true,
|
||||
[_profiler_hook_wrapper_by_time] = true,
|
||||
[_profiler_hook_wrapper_by_call] = true,
|
||||
[_profiler.prevent] = true,
|
||||
[_profiler.report] = true,
|
||||
[_profiler.lua_report] = true,
|
||||
[_profiler._pretty_name] = true
|
||||
}
|
||||
|
||||
return _ENV
|
Loading…
Reference in New Issue