538 lines
20 KiB
Lua
538 lines
20 KiB
Lua
local QuestieProfiler = QuestieLoader:CreateModule("Profiler")
|
|
|
|
--- COMPATIBILITY ---
|
|
local C_Timer = QuestieCompat.C_Timer
|
|
local BackdropTemplateMixin = not QuestieCompat.Is335 and BackdropTemplateMixin
|
|
|
|
QuestieProfiler.hooks = {}
|
|
QuestieProfiler.alreadyHooked = {}
|
|
QuestieProfiler.needsHook = {}
|
|
QuestieProfiler.hookCallCount = {}
|
|
QuestieProfiler.hookTimeCount = {}
|
|
QuestieProfiler.lowerCaseLookup = {}
|
|
QuestieProfiler.shortestName = {}
|
|
QuestieProfiler.lookupToHook = {}
|
|
QuestieProfiler.hooks = {}
|
|
QuestieProfiler.needsHookCount = 0
|
|
QuestieProfiler.finishedHookCount = 0
|
|
QuestieProfiler.hookedFunctionCount = 0
|
|
QuestieProfiler.highestMS = 0
|
|
QuestieProfiler.highestCalls = 0
|
|
QuestieProfiler.searchFilter = nil
|
|
|
|
QuestieProfiler.condensedMode = false
|
|
|
|
QuestieProfiler.scriptsToWatch = {
|
|
"OnUpdate",
|
|
"OnShow",
|
|
"OnHide"
|
|
}
|
|
|
|
function QuestieProfiler:HookFunction(key, val, table, name)
|
|
local lookupKey = name .. "." .. tostring(key)
|
|
QuestieProfiler.shortestName[val] = lookupKey
|
|
QuestieProfiler.alreadyHooked[val] = true
|
|
QuestieProfiler.lookupToHook[lookupKey] = val
|
|
local hook = {}
|
|
hook.original = val
|
|
hook.originalKey = key
|
|
hook.originalParent = table
|
|
|
|
QuestieProfiler.hookCallCount[lookupKey] = 0
|
|
QuestieProfiler.hookTimeCount[lookupKey] = 0
|
|
hook.override = function(...)
|
|
--local _, _, name = strsplit("\\", debugstack(2))
|
|
--print(name)
|
|
--print("Hookboy run " .. name .. "->"..key)
|
|
local htc = QuestieProfiler.hookCallCount[lookupKey] + 1
|
|
QuestieProfiler.hookCallCount[lookupKey] = htc
|
|
if htc > QuestieProfiler.highestCalls then
|
|
QuestieProfiler.highestCalls = htc
|
|
end
|
|
local start = debugprofilestop()
|
|
local ret = { hook.original(...) }
|
|
start = debugprofilestop() - start
|
|
htc = QuestieProfiler.hookTimeCount[lookupKey] + start
|
|
QuestieProfiler.hookTimeCount[lookupKey] = htc
|
|
if htc > QuestieProfiler.highestMS then
|
|
QuestieProfiler.highestMS = htc
|
|
end
|
|
return unpack(ret)
|
|
end
|
|
QuestieProfiler.lowerCaseLookup[lookupKey] = string.lower(lookupKey)
|
|
tinsert(QuestieProfiler.hooks, hook)
|
|
table[key] = hook.override
|
|
QuestieProfiler.hookedFunctionCount = QuestieProfiler.hookedFunctionCount + 1
|
|
end
|
|
|
|
function QuestieProfiler:HookTable(table, name)
|
|
QuestieProfiler.alreadyHooked[table] = true
|
|
for key, val in pairs(table) do
|
|
if QuestieProfiler.alreadyHooked[val] then
|
|
if QuestieProfiler.shortestName[val] then
|
|
local lookupKey
|
|
if type(key) == "table" then
|
|
lookupKey = name .. ".(key)" .. tostring(key)
|
|
else
|
|
lookupKey = name .. "." .. tostring(key)
|
|
end
|
|
if string.len(lookupKey) < string.len(QuestieProfiler.shortestName[val]) then
|
|
QuestieProfiler.shortestName[val] = lookupKey
|
|
QuestieProfiler.lowerCaseLookup[lookupKey] = string.lower(lookupKey)
|
|
--print("Shorter name: " .. lookupKey)
|
|
end
|
|
end
|
|
else
|
|
--QuestieProfiler.alreadyHooked[val] = true
|
|
local typ = type(val)
|
|
if type(key) == "table" then
|
|
tinsert(QuestieProfiler.needsHook, { key, name .. ".(key)" .. tostring(key) })
|
|
QuestieProfiler.needsHookCount = QuestieProfiler.needsHookCount + 1
|
|
end
|
|
if typ == "function" then
|
|
if key == "GetScript" then -- get all scripts
|
|
for _, script in pairs(QuestieProfiler.scriptsToWatch) do
|
|
local res, ret = pcall(val, table, script)
|
|
if res then
|
|
QuestieProfiler:HookScript(script, ret, table, name)
|
|
end
|
|
end
|
|
end
|
|
--print("["..QuestieProfiler.finishedHookCount.."/"..QuestieProfiler.needsHookCount.."]Hooking function " .. name .. "->" .. key)
|
|
QuestieProfiler:HookFunction(key, val, table, name)
|
|
elseif typ == "table" then
|
|
--QuestieProfiler:HookTable(val, name .. "->"..key)
|
|
|
|
tinsert(QuestieProfiler.needsHook, { val, name .. "." .. tostring(key) })
|
|
QuestieProfiler.needsHookCount = QuestieProfiler.needsHookCount + 1
|
|
end
|
|
end
|
|
end
|
|
QuestieProfiler.finishedHookCount = QuestieProfiler.finishedHookCount + 1
|
|
end
|
|
|
|
function QuestieProfiler:HookModules()
|
|
for moduleName, module in pairs(QuestieLoader._modules) do
|
|
print("Hooking module " .. moduleName)
|
|
QuestieProfiler:HookTable(module, moduleName)
|
|
end
|
|
end
|
|
|
|
function QuestieProfiler:HookFrames()
|
|
-- bit of a hack but I want to keep questie's code 100% unaffected, this should be a stand-alone module.
|
|
-- that also makes sure the profiler doesnt mess with the performance of the addon when its not running (which is almost always)
|
|
--C_Timer.NewTicker(2, function()
|
|
-- QuestieProfiler:HookTable(QuestieFramePool, "QuestieFramePool") -- update framepool functions
|
|
--end)
|
|
end
|
|
|
|
function QuestieProfiler:CreateUI()
|
|
local base = CreateFrame("Frame", nil, UIParent, BackdropTemplateMixin and "BackdropTemplate" or nil)
|
|
base:SetFrameStrata("TOOLTIP")
|
|
base:SetSize(840, 580)
|
|
base:SetPoint("Center", UIParent, 0, 50)
|
|
|
|
base:SetMovable(true)
|
|
base:EnableMouse(true)
|
|
base:RegisterForDrag("LeftButton")
|
|
|
|
base:SetBackdrop({
|
|
bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background-Dark",
|
|
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
|
|
tile = true,
|
|
edgeSize = 16,
|
|
insets = { left = 4, right = 4, top = 4, bottom = 4 },
|
|
})
|
|
|
|
local scrollFrameTemplete
|
|
if Questie.IsWotlk and (not QuestieCompat.Is335) then
|
|
scrollFrameTemplete = "ScrollFrameTemplate"
|
|
else
|
|
scrollFrameTemplete = "UIPanelScrollFrameTemplate"
|
|
end
|
|
|
|
base.scrollFrame = CreateFrame("ScrollFrame", "QuestieProfilerScrollFrame", base, scrollFrameTemplete)
|
|
base.scrollFrame:SetFrameStrata("TOOLTIP")
|
|
base.scrollFrame:SetPoint("TOPLEFT", base, 0, -40)
|
|
base.scrollFrame:SetPoint("BOTTOMRIGHT", base, 0, 30)
|
|
|
|
if QuestieCompat.Is335 then
|
|
base.scrollFrame.ScrollBar = _G[base.scrollFrame:GetName() .. "ScrollBar"]
|
|
base.scrollFrame.ScrollBar:ClearAllPoints()
|
|
base.scrollFrame.ScrollBar:SetPoint("TOPRIGHT", -6, -16)
|
|
base.scrollFrame.ScrollBar:SetPoint("BOTTOMRIGHT", -6, 16)
|
|
base.scrollFrame.ScrollBar.Show = function() end
|
|
end
|
|
base.scrollFrame.ScrollBar:Hide()
|
|
|
|
base.scrollContainer = CreateFrame("Frame")
|
|
base.scrollContainer:SetFrameStrata("TOOLTIP")
|
|
|
|
local lineCounter = 0
|
|
base.scrollContainer.ResetLines = function()
|
|
lineCounter = 0
|
|
end
|
|
|
|
local lines = {}
|
|
base.scrollContainer.CreateLineFrame = function()
|
|
local label = base.scrollContainer:CreateFontString(nil, "ARTWORK", "GameFontNormal")
|
|
label:SetText("")
|
|
label:SetPoint("TOPLEFT", base.scrollContainer, 0, -lineCounter * 10)
|
|
|
|
label:SetWidth(base.scrollFrame:GetWidth() - 270)
|
|
label:SetJustifyH("RIGHT")
|
|
|
|
local label2 = base.scrollContainer:CreateFontString(nil, "ARTWORK", "GameFontNormal")
|
|
label2:SetText("")
|
|
label2:SetPoint("TOPLEFT", base.scrollContainer, base.scrollFrame:GetWidth() - 260, -lineCounter * 10)
|
|
|
|
local label3 = base.scrollContainer:CreateFontString(nil, "ARTWORK", "GameFontNormal")
|
|
label3:SetText("")
|
|
label3:SetPoint("TOPLEFT", base.scrollContainer, base.scrollFrame:GetWidth() - 150, -lineCounter * 10)
|
|
|
|
base.scrollContainer:SetSize(base.scrollFrame:GetWidth(), lineCounter * 10)
|
|
|
|
return { label, label2, label3 }
|
|
end
|
|
|
|
base.scrollContainer.GetNextLine = function()
|
|
lineCounter = lineCounter + 1
|
|
if not lines[lineCounter] then
|
|
lines[lineCounter] = base.scrollContainer.CreateLineFrame()
|
|
end
|
|
return lines[lineCounter]
|
|
end
|
|
|
|
base.scrollContainer:SetSize(base.scrollFrame:GetWidth(), (base.scrollFrame:GetHeight() * 2))
|
|
|
|
base.scrollFrame:SetScrollChild(base.scrollContainer)
|
|
base.scrollFrame:Show()
|
|
|
|
QuestieProfiler.baseUI = base
|
|
|
|
-- should maybe be exposed in QuestieLib
|
|
local function RGBToHex(r, g, b)
|
|
if r > 255 then r = 255 end
|
|
if g > 255 then g = 255 end
|
|
if b > 255 then b = 255 end
|
|
if r < 0 then r = 0 end
|
|
if g < 0 then g = 0 end
|
|
if b < 0 then b = 0 end
|
|
return string.format("\124cFF%02x%02x%02x", r, g, b)
|
|
end
|
|
|
|
local function FloatRGBToHex(r, g, b) return RGBToHex(r * 254, g * 254, b * 254) end
|
|
|
|
local function valToHex(float)
|
|
if float < .49 then return FloatRGBToHex(1, 0 + float / .5, 0) end
|
|
if float == .50 then return FloatRGBToHex(1, 1, 0) end
|
|
if float > .50 then return FloatRGBToHex(1 - float / 2, 1, 0) end
|
|
end
|
|
|
|
local statusLabel = base:CreateFontString(nil, "ARTWORK", "GameFontNormal")
|
|
statusLabel:SetText("This is a test.")
|
|
statusLabel:SetPoint("BOTTOMLEFT", base, 10, 10)
|
|
statusLabel:Show()
|
|
base.lastIndex = 0
|
|
base.update = function()
|
|
base.scrollContainer.ResetLines()
|
|
local sorted = {}
|
|
local index = 0
|
|
local timeCount = QuestieProfiler.hookTimeCount
|
|
local callCount = QuestieProfiler.hookCallCount
|
|
local highestMS = QuestieProfiler.highestMS
|
|
local highestCalls = QuestieProfiler.highestCalls
|
|
|
|
if QuestieProfiler.condensedMode then
|
|
timeCount = {}
|
|
callCount = {}
|
|
highestMS = 0
|
|
highestCalls = 0
|
|
for call, _ in pairs(QuestieProfiler.hookTimeCount) do
|
|
local callStack = {}
|
|
index = 0
|
|
for token in string.gmatch(call, "[^.]+") do
|
|
--print(token)
|
|
tinsert(callStack, token)
|
|
index = index + 1
|
|
end
|
|
--local ncall = (callStack[index-2] or "?") .. "." .. (callStack[index-1] or "?") .. ":" .. callStack[index].."()"
|
|
--local ncall = (callStack[index-1] or "?") .. ":" .. callStack[index].."()"
|
|
local ncall = callStack[index] .. "()"
|
|
if callStack[index - 1] and not tonumber(callStack[index - 1]) then
|
|
ncall = callStack[index - 1] .. "." .. ncall
|
|
end
|
|
if callStack[index - 2] and not tonumber(callStack[index - 2]) then
|
|
ncall = callStack[index - 2] .. "." .. ncall
|
|
end
|
|
|
|
if not timeCount[ncall] then
|
|
timeCount[ncall] = 0
|
|
callCount[ncall] = 0
|
|
end
|
|
local time = timeCount[ncall] + QuestieProfiler.hookTimeCount[call]
|
|
local calls = callCount[ncall] + QuestieProfiler.hookCallCount[call]
|
|
timeCount[ncall] = time
|
|
callCount[ncall] = calls
|
|
QuestieProfiler.lowerCaseLookup[ncall] = string.lower(ncall)
|
|
if time > highestMS then
|
|
highestMS = time
|
|
end
|
|
if calls > highestCalls then
|
|
highestCalls = calls
|
|
end
|
|
end
|
|
end
|
|
for call, count in pairs(timeCount) do
|
|
if ((count > 0 and not QuestieProfiler.searchFilter) or (callCount[call] > 0 and QuestieProfiler.searchFilter and string.match(QuestieProfiler.lowerCaseLookup[call], QuestieProfiler.searchFilter))) then
|
|
tinsert(sorted, { call, count })
|
|
index = index + 1
|
|
end
|
|
end
|
|
table.sort(sorted, function(a, b) return a[2] > b[2] end)
|
|
|
|
statusLabel:SetText(string.format("Total functions hooked: %d Highest call count: %d Highest time: %.2fms", QuestieProfiler.hookedFunctionCount, highestCalls, highestMS))
|
|
|
|
index = 0
|
|
for call in pairs(sorted) do
|
|
call = sorted[call][1]
|
|
local count = timeCount[call]
|
|
|
|
local line = base.scrollContainer.GetNextLine()
|
|
local color
|
|
|
|
if mod(index, 2) == 1 then
|
|
color = "\124cFFFFFFFF"
|
|
--color2 = "\124cFF88FFAA"
|
|
else
|
|
color = "\124cFFEEEEEE"
|
|
--color2 = "\124cFF77EE99"
|
|
end
|
|
local color2 = valToHex(1 - (count / (highestMS / 2))) or "\124cFFFF0000"
|
|
local color3 = valToHex(1 - (callCount[call] / (highestCalls / 2))) or "\124cFFFF0000"
|
|
--print("Highest ms: " .. QuestieProfiler.highestMS)
|
|
--print("Highest calls: " .. QuestieProfiler.highestCalls)
|
|
local callstr = QuestieProfiler.shortestName[QuestieProfiler.lookupToHook[call]] or call
|
|
|
|
if (string.len(callstr) > 64) then
|
|
callstr = "..." .. string.sub(callstr, string.len(callstr) - 64)
|
|
end
|
|
|
|
line[1]:SetText(color .. callstr)
|
|
line[2]:SetText(color2 .. string.format("time=%.2fms", count))
|
|
line[3]:SetText(color3 .. "call count=" .. callCount[call])
|
|
index = index + 1
|
|
if index > 150 then
|
|
break
|
|
end
|
|
end
|
|
if base.lastIndex > index then
|
|
for _ = index, base.lastIndex do
|
|
local line = base.scrollContainer.GetNextLine()
|
|
line[1]:SetText("")
|
|
line[2]:SetText("")
|
|
line[3]:SetText("")
|
|
end
|
|
end
|
|
base.lastIndex = index
|
|
end
|
|
base.update()
|
|
|
|
|
|
local button = CreateFrame("Button", nil, base)
|
|
button:SetPoint("TOPLEFT", base, 10, -10)
|
|
button:SetWidth(80)
|
|
button:SetHeight(24)
|
|
|
|
button:SetText("Reset")
|
|
|
|
button:SetNormalFontObject("GameFontNormal")
|
|
|
|
local function buildTexture(str)
|
|
local tex = button:CreateTexture()
|
|
tex:SetTexture(str)
|
|
tex:SetTexCoord(0, 0.625, 0, 0.6875)
|
|
tex:SetAllPoints()
|
|
return tex
|
|
end
|
|
|
|
button:SetNormalTexture(buildTexture("Interface/Buttons/UI-Panel-Button-Up"))
|
|
button:SetHighlightTexture(buildTexture("Interface/Buttons/UI-Panel-Button-Highlight"))
|
|
button:SetPushedTexture(buildTexture("Interface/Buttons/UI-Panel-Button-Down"))
|
|
button:SetScript("OnClick", function(self, ...)
|
|
self:Disable()
|
|
QuestieProfiler:Unhook()
|
|
QuestieProfiler:DoHooks(function()
|
|
for key in pairs(QuestieProfiler.hookCallCount) do
|
|
QuestieProfiler.hookCallCount[key] = 0
|
|
QuestieProfiler.hookTimeCount[key] = 0
|
|
end
|
|
QuestieProfiler.highestMS = 0
|
|
QuestieProfiler.highestCalls = 0
|
|
for _, line in pairs(lines) do
|
|
line[1]:SetText("")
|
|
line[2]:SetText("")
|
|
line[3]:SetText("")
|
|
end
|
|
self:Enable()
|
|
end)
|
|
end)
|
|
|
|
button = CreateFrame("Button", nil, base)
|
|
button:SetPoint("TOPLEFT", base, base:GetWidth() - 70, -10)
|
|
button:SetWidth(60)
|
|
button:SetHeight(24)
|
|
|
|
button:SetText("Close")
|
|
|
|
button:SetNormalFontObject("GameFontNormal")
|
|
button:SetNormalTexture(buildTexture("Interface/Buttons/UI-Panel-Button-Up"))
|
|
button:SetHighlightTexture(buildTexture("Interface/Buttons/UI-Panel-Button-Highlight"))
|
|
button:SetPushedTexture(buildTexture("Interface/Buttons/UI-Panel-Button-Down"))
|
|
button:SetScript("OnClick", function()
|
|
QuestieProfiler:Unhook()
|
|
base:Hide()
|
|
end)
|
|
|
|
button = CreateFrame("Button", nil, base)
|
|
button:SetPoint("TOPLEFT", base, 90, -10)
|
|
button:SetWidth(140)
|
|
button:SetHeight(24)
|
|
|
|
button:SetText("Mode: Full")
|
|
button:SetNormalFontObject("GameFontNormal")
|
|
button:SetNormalTexture(buildTexture("Interface/Buttons/UI-Panel-Button-Up"))
|
|
button:SetHighlightTexture(buildTexture("Interface/Buttons/UI-Panel-Button-Highlight"))
|
|
button:SetPushedTexture(buildTexture("Interface/Buttons/UI-Panel-Button-Down"))
|
|
|
|
button:SetScript("OnClick", function(...)
|
|
QuestieProfiler.condensedMode = not QuestieProfiler.condensedMode
|
|
if QuestieProfiler.condensedMode then
|
|
button:SetText("Mode: Condensed")
|
|
else
|
|
button:SetText("Mode: Full")
|
|
end
|
|
for key in pairs(QuestieProfiler.hookCallCount) do
|
|
QuestieProfiler.hookCallCount[key] = 0
|
|
QuestieProfiler.hookTimeCount[key] = 0
|
|
end
|
|
QuestieProfiler.highestMS = 0
|
|
QuestieProfiler.highestCalls = 0
|
|
for _, line in pairs(lines) do
|
|
line[1]:SetText("")
|
|
line[2]:SetText("")
|
|
line[3]:SetText("")
|
|
end
|
|
end)
|
|
|
|
local searchBox = CreateFrame("Frame", nil, base, BackdropTemplateMixin and "BackdropTemplate")
|
|
searchBox:SetPoint("TOPLEFT", base, 260, -10)
|
|
searchBox:SetWidth(192)
|
|
searchBox:SetHeight(24)
|
|
searchBox:Show()
|
|
|
|
searchBox:SetBackdrop({
|
|
bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background",
|
|
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
|
|
tile = true,
|
|
tileSize = 0,
|
|
edgeSize = 16,
|
|
insets = { left = 0, right = 0, top = 0, bottom = 0 }
|
|
})
|
|
|
|
local search = CreateFrame("EditBox", nil, searchBox, BackdropTemplateMixin and "BackdropTemplate" or nil)
|
|
search:SetMultiLine(false)
|
|
search:SetFontObject(ChatFontNormal)
|
|
search:SetWidth(180)
|
|
search:SetHeight(20)
|
|
search:SetFrameStrata("TOOLTIP")
|
|
search:SetPoint("TOPLEFT", searchBox, 6, -1)
|
|
search:SetText("\124cFF888888Filter...")
|
|
|
|
search:HookScript("OnMouseDown", function()
|
|
if search:GetText() == "\124cFF888888Filter..." then
|
|
search:SetText("")
|
|
end
|
|
end)
|
|
local function clearFocus()
|
|
search:ClearFocus()
|
|
if string.len(search:GetText()) == 0 then
|
|
search:SetText("\124cFF888888Filter...")
|
|
end
|
|
end
|
|
search:SetAutoFocus(false)
|
|
search:SetScript("OnEscapePressed", clearFocus)
|
|
search:HookScript(QuestieCompat.Is335 and "OnTextChanged" or "OnKeyUp", function(self, userInput)
|
|
local txt = string.lower(search:GetText())
|
|
if string.len(txt) == 0 or userInput == false then
|
|
txt = nil
|
|
end
|
|
QuestieProfiler.searchFilter = txt
|
|
end)
|
|
base:SetScript("OnMouseDown", clearFocus)
|
|
base:SetScript("OnDragStart", function()
|
|
base:StartMoving()
|
|
clearFocus()
|
|
end)
|
|
base:SetScript("OnDragStop", function()
|
|
base:StopMovingOrSizing()
|
|
end)
|
|
search:Show()
|
|
|
|
base:Show()
|
|
C_Timer.NewTicker(0.5, base.update)
|
|
end
|
|
|
|
function QuestieProfiler:DoHooks(after)
|
|
local timer
|
|
|
|
-- libstub modules. TODO: check debugstack() to only include calls made by questie
|
|
--QuestieProfiler.needsHookCount = QuestieProfiler.needsHookCount + 1
|
|
--tinsert(QuestieProfiler.needsHook, {LibStub, "LibStub"})
|
|
|
|
for moduleName, module in pairs(QuestieLoader._modules) do
|
|
if module ~= QuestieProfiler then
|
|
QuestieProfiler.needsHookCount = QuestieProfiler.needsHookCount + 1
|
|
tinsert(QuestieProfiler.needsHook, { module, moduleName })
|
|
end
|
|
end
|
|
|
|
timer = C_Timer.NewTicker(0.01, function()
|
|
for _ = 0, 512 do
|
|
local toHook = tremove(QuestieProfiler.needsHook)
|
|
if toHook then
|
|
QuestieProfiler:HookTable(toHook[1], toHook[2])
|
|
else
|
|
timer:Cancel()
|
|
if after then after() end
|
|
return
|
|
end
|
|
end
|
|
end)
|
|
end
|
|
|
|
function QuestieProfiler:Unhook()
|
|
for _, hook in pairs(QuestieProfiler.hooks) do
|
|
hook.originalParent[hook.originalKey] = hook.original
|
|
end
|
|
QuestieProfiler.hooks = {}
|
|
QuestieProfiler.alreadyHooked = {}
|
|
QuestieProfiler.needsHookCount = 0
|
|
QuestieProfiler.finishedHookCount = 0
|
|
QuestieProfiler.hookedFunctionCount = 0
|
|
QuestieProfiler.highestMS = 0
|
|
QuestieProfiler.highestCalls = 0
|
|
end
|
|
|
|
function QuestieProfiler:Start() -- call ingame when developing /run QuestieLoader:ImportModule("Profiler"):Start()
|
|
print("Starting profiler...")
|
|
QuestieProfiler:DoHooks(function()
|
|
QuestieProfiler:HookFrames()
|
|
if QuestieProfiler.baseUI then
|
|
QuestieProfiler.baseUI:Show()
|
|
else
|
|
QuestieProfiler:CreateUI()
|
|
end
|
|
end)
|
|
end
|