EVENT_TRACE_EVENT_HEIGHT = 16; EVENT_TRACE_MAX_ENTRIES = 1000; DEBUGLOCALS_LEVEL = 4; local _normalFontColor = { 1, .82, 0, 1 }; EVENT_TRACE_SYSTEM_TIMES = {}; EVENT_TRACE_SYSTEM_TIMES["System"] = true; EVENT_TRACE_SYSTEM_TIMES["Elapsed"] = true; EVENT_TRACE_EVENT_COLORS = {}; EVENT_TRACE_EVENT_COLORS["System"] = _normalFontColor; EVENT_TRACE_EVENT_COLORS["Elapsed"] = { .6, .6, .6, 1 }; local _EventTraceFrame; _framesSinceLast = 0; _timeSinceLast = 0; local _timer = CreateFrame("FRAME"); _timer:SetScript("OnUpdate", function (self, elapsed) _framesSinceLast = _framesSinceLast + 1; _timeSinceLast = _timeSinceLast + elapsed; end); function EventTraceFrame_OnLoad (self) self.buttons = {}; self.events = {}; self.times = {}; self.rawtimes = {}; self.args = { {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {} }; self.ignoredEvents = {}; self.lastIndex = 0; self.visibleButtons = 0; _EventTraceFrame = self; self:SetScript("OnSizeChanged", EventTraceFrame_OnSizeChanged); EventTraceFrame_OnSizeChanged(self, self:GetWidth(), self:GetHeight()); self:EnableMouse(true); self:EnableMouseWheel(true); self:SetScript("OnMouseWheel", EventTraceFrame_OnMouseWheel); end local _workTable = {}; function EventTraceFrame_OnEvent (self, event, ...) if ( not self.ignoredEvents[event] ) then if ( not self.ignoreElapsed and _framesSinceLast ~= 0 ) then self.ignoreElapsed = true; EventTraceFrame_OnEvent(self, "On Update"); self.ignoreElapsed = nil; end local nextIndex = self.lastIndex + 1; if ( nextIndex > EVENT_TRACE_MAX_ENTRIES ) then local staleIndex = nextIndex - EVENT_TRACE_MAX_ENTRIES; self.events[staleIndex] = nil; self.times[staleIndex] = nil; self.rawtimes[staleIndex] = nil; for k, v in next, self.args do self.args[k][staleIndex] = nil; end end if ( string.match(event, "Begin Capture") or string.match(event, "End Capture") ) then self.times[nextIndex] = "System"; if ( self.eventsToCapture ) then self.events[nextIndex] = string.format("%s (%s events)", event, tostring(self.eventsToCapture)); else self.events[nextIndex] = event; end elseif ( event == "On Update" ) then self.times[nextIndex] = "Elapsed"; self.events[nextIndex] = string.format(string.format("%.3f sec", _timeSinceLast) .. " - %d frame(s)", _framesSinceLast); _timeSinceLast = 0; _framesSinceLast = 0; else self.events[nextIndex] = event; local seconds = GetTime(); local minutes = math.floor(math.floor(seconds) / 60); local hours = math.floor(minutes / 60); seconds = seconds - 60 * minutes; minutes = minutes - 60 * hours; hours = hours % 1000; self.times[nextIndex] = string.format("%.2d:%.2d:%06.3f", hours, minutes, seconds); local numArgs = select("#", ...); for i=1, numArgs do if ( not self.args[i] ) then self.args[i] = {}; end self.args[i][nextIndex] = select(i, ...); end if ( self.eventsToCapture ) then self.eventsToCapture = self.eventsToCapture - 1; end end self.rawtimes[nextIndex] = GetTime(); self.lastIndex = nextIndex; EventTraceFrame_Update (); if ( self.eventsToCapture and self.eventsToCapture <= 0 ) then self.eventsToCapture = nil; EventTraceFrame_StopEventCapture(); end end end function EventTraceFrame_OnUpdate (self, elapsed) EventTraceFrame_Update(); end function EventTraceFrame_OnSizeChanged (self, width, height) local numButtonsToDisplay = math.floor((height - 36)/EVENT_TRACE_EVENT_HEIGHT); local numButtonsCreated = #self.buttons; if ( numButtonsCreated < numButtonsToDisplay ) then for i = numButtonsCreated + 1, numButtonsToDisplay do local button = CreateFrame("BUTTON", "EventTraceFrameButton" .. i, self, "EventTraceEventTemplate"); button:SetPoint("BOTTOMLEFT", 12, (16 * (i - 1)) + 12); button:SetPoint("RIGHT", -28, 0); tinsert(self.buttons, button); end for i = self.visibleButtons + 1, numButtonsToDisplay do self.buttons[i]:Show(); end self.visibleButtons = numButtonsToDisplay; EventTraceFrame_Update(); elseif ( self.visibleButtons < numButtonsToDisplay ) then for i = self.visibleButtons + 1, numButtonsToDisplay do self.buttons[i]:Show(); end self.visibleButtons = numButtonsToDisplay; EventTraceFrame_Update(); elseif ( numButtonsToDisplay < self.visibleButtons ) then for i = numButtonsToDisplay + 1, self.visibleButtons do self.buttons[i]:Hide(); end self.visibleButtons = numButtonsToDisplay; end end function EventTraceFrame_Update () local offset = 0; local scrollBar = _G["EventTraceFrameScroll"]; local scrollBarValue = scrollBar:GetValue(); local minValue, maxValue = scrollBar:GetMinMaxValues(); local firstID = max(1, _EventTraceFrame.lastIndex - EVENT_TRACE_MAX_ENTRIES + 1); local lastID = _EventTraceFrame.lastIndex or 1; if ( firstID >= lastID ) then scrollBar:SetMinMaxValues(firstID-1, lastID); else scrollBar:SetMinMaxValues(firstID, lastID); end if ( scrollBarValue < firstID ) then scrollBar:SetValue(firstID); scrollBarValue = firstID; end if ( scrollBarValue < 1 ) then scrollBarValue = 1; elseif ( not _EventTraceFrame.selectedEvent ) then if ( scrollBarValue == maxValue ) then scrollBar:SetValue(_EventTraceFrame.lastIndex); end end for i = 1, _EventTraceFrame.visibleButtons do local button = _EventTraceFrame.buttons[i]; if ( button ) then local index = scrollBarValue - (i - 1); local event = _EventTraceFrame.events[index]; if ( event ) then local timeString = _EventTraceFrame.times[index] button.index = index; button.time:SetText(timeString); button.event:SetText(event); local color = EVENT_TRACE_EVENT_COLORS[event] or EVENT_TRACE_EVENT_COLORS[timeString]; if ( color ) then button.time:SetTextColor(unpack(color)); button.event:SetTextColor(unpack(color)); else button.time:SetTextColor(1, 1, 1, 1); button.event:SetTextColor(1, 1, 1, 1); end button:Show(); if ( _EventTraceFrame.selectedEvent ) then if ( index == _EventTraceFrame.selectedEvent ) then EventTraceFrameEvent_DisplayTooltip(button); button:GetHighlightTexture():SetVertexColor(.15, .25, 1, .35); button:LockHighlight(true); button.wasSelected = true; elseif ( button.wasSelected ) then button.wasSelected = nil; button:GetHighlightTexture():SetVertexColor(.8, .8, 1, .15); button:UnlockHighlight(); end else if ( button.wasSelected ) then button.wasSelected = nil; button:GetHighlightTexture():SetVertexColor(.8, .8, 1, .15); button:UnlockHighlight(); end if ( button:IsMouseOver() ) then EventTraceFrameEvent_OnEnter(button); end end else button.index = index; button:Hide(); end end end EventTraceFrame_UpdateKeyboardStatus(); end function EventTraceFrame_StartEventCapture () if ( _EventTraceFrame.started ) then -- Nothing to do? return; end _EventTraceFrame.started = true; _framesSinceLast = 0; _timeSinceLast = 0; _EventTraceFrame:RegisterAllEvents(); EventTraceFrame_OnEvent(_EventTraceFrame, "Begin Capture"); end function EventTraceFrame_StopEventCapture () if ( not _EventTraceFrame.started ) then -- Nothing to do! return; end _EventTraceFrame.started = false; _framesSinceLast = 0; _timeSinceLast = 0; _EventTraceFrame:UnregisterAllEvents(); EventTraceFrame_OnEvent(_EventTraceFrame, "End Capture"); end function EventTraceFrame_HandleSlashCmd (msg) msg = strlower(msg); if ( msg == "start" ) then EventTraceFrame_StartEventCapture(); elseif ( msg == "stop" ) then EventTraceFrame_StopEventCapture(); elseif ( tonumber(msg) and tonumber(msg) > 0 ) then if ( not _EventTraceFrame.started ) then _EventTraceFrame.eventsToCapture = tonumber(msg); EventTraceFrame_StartEventCapture(); end elseif ( msg == "" ) then if ( not _EventTraceFrame:IsShown() ) then _EventTraceFrame:Show(); if ( _EventTraceFrame.started == nil ) then EventTraceFrame_StartEventCapture(); -- If this is the first time we're showing the window, start capturing events immediately. end else _EventTraceFrame:Hide(); end end end function EventTraceFrame_OnMouseWheel (self, delta) local scrollBar = _G["EventTraceFrameScroll"]; local minVal, maxVal = scrollBar:GetMinMaxValues(); local currentValue = scrollBar:GetValue(); local newValue = currentValue - ( delta * 3 ); newValue = max(newValue, minVal); newValue = min(newValue, maxVal); if ( newValue ~= currentValue ) then scrollBar:SetValue(newValue); end end function EventTraceFrame_UpdateKeyboardStatus () if ( _EventTraceFrame.selectedEvent ) then local focus = GetMouseFocus(); if ( focus == _EventTraceFrame or (focus and focus:GetParent() == _EventTraceFrame) ) then _EventTraceFrame:EnableKeyboard(true); return; end end _EventTraceFrame:EnableKeyboard(false); end function EventTraceFrame_OnKeyUp (self, key) if ( key == "ESCAPE" ) then self.selectedEvent = nil; EventTraceTooltip:Hide(); EventTraceFrame_Update(); end end local TIME_ENTRY_FORMAT = "Time:"; local DETAILS_ENTRY_FORMAT = "Details:"; local ARGUMENT_ENTRY_FORMAT = "arg %d:"; local function EventTrace_FormatArgValue (val) if ( type(val) == "string" ) then return string.format('"%s"', val); elseif ( type(val) == "number" ) then return tostring(val); elseif ( type(val) == "bool" ) then return string.format('|cffaaaaff%s|r', tostring(val)); elseif ( type(val) == "table" or type(val) == "bool" ) then return string.format('|cffffaaaa%s|r', tostring(val)); end end function EventTraceFrameEvent_DisplayTooltip (eventButton) local index = eventButton.index; if ( not index ) then return; end local tooltip = _G["EventTraceTooltip"]; tooltip:SetOwner(eventButton, "ANCHOR_NONE"); tooltip:SetPoint("TOPLEFT", eventButton, "TOPRIGHT", 24, 2); local timeString = _EventTraceFrame.times[index] if ( EVENT_TRACE_SYSTEM_TIMES[timeString] ) then tooltip:AddLine(timeString, 1, 1, 1); tooltip:AddDoubleLine(string.format(TIME_ENTRY_FORMAT), _EventTraceFrame.rawtimes[index], 1, .82, 0, 1, 1, 1); tooltip:AddDoubleLine(string.format(DETAILS_ENTRY_FORMAT), _EventTraceFrame.events[index], 1, .82, 0, 1, 1, 1); else tooltip:AddLine(_EventTraceFrame.events[index], 1, 1, 1); tooltip:AddDoubleLine(string.format(TIME_ENTRY_FORMAT), _EventTraceFrame.rawtimes[index], 1, .82, 0, 1, 1, 1); for k, v in ipairs(EventTraceFrame.args) do if ( v[index] ) then tooltip:AddDoubleLine(string.format(ARGUMENT_ENTRY_FORMAT, k), EventTrace_FormatArgValue(v[index]), 1, .82, 0, 1, 1, 1); end end end tooltip:Show(); end function EventTraceFrameEvent_OnEnter (self) if ( _EventTraceFrame.selectedEvent ) then return; else EventTraceFrameEvent_DisplayTooltip(self); end end function EventTraceFrameEvent_OnLeave (self) if ( not _EventTraceFrame.selectedEvent ) then EventTraceTooltip:Hide(); end end function EventTraceFrameEvent_OnClick (self) if ( _EventTraceFrame.selectedEvent == self.index ) then _EventTraceFrame.selectedEvent = nil; else _EventTraceFrame.selectedEvent = self.index; end EventTraceFrame_Update(); end local ERROR_FORMAT = [[|cffffd200Message:|cffffffff %s |cffffd200Time:|cffffffff %s |cffffd200Count:|cffffffff %s |cffffd200Stack:|cffffffff %s |cffffd200Locals:|cffffffff %s]]; local INDEX_ORDER_FORMAT = "%d / %d" local _ScriptErrorsFrame; function ScriptErrorsFrame_OnLoad (self) self.title:SetText(LUA_ERROR); self:RegisterForDrag("LeftButton"); self.seen = {}; self.order = {}; self.count = {}; self.messages = {}; self.times = {}; self.locals = {}; _ScriptErrorsFrame = self; end function ScriptErrorsFrame_OnShow (self) ScriptErrorsFrame_Update(); end function ScriptErrorsFrame_OnError (message, keepHidden) local stack = debugstack(DEBUGLOCALS_LEVEL); local messageStack = message..stack; -- Fix me later if ( _ScriptErrorsFrame ) then local index = _ScriptErrorsFrame.seen[messageStack]; if ( index ) then _ScriptErrorsFrame.count[index] = _ScriptErrorsFrame.count[index] + 1; _ScriptErrorsFrame.messages[index] = message; _ScriptErrorsFrame.times[index] = date(); _ScriptErrorsFrame.locals[index] = debuglocals(DEBUGLOCALS_LEVEL); else tinsert(_ScriptErrorsFrame.order, stack); index = #_ScriptErrorsFrame.order; _ScriptErrorsFrame.count[index] = 1; _ScriptErrorsFrame.messages[index] = message; _ScriptErrorsFrame.times[index] = date(); _ScriptErrorsFrame.seen[messageStack] = index; _ScriptErrorsFrame.locals[index] = debuglocals(DEBUGLOCALS_LEVEL); end if ( not _ScriptErrorsFrame:IsShown() and not keepHidden ) then _ScriptErrorsFrame.index = index; _ScriptErrorsFrame:Show(); else ScriptErrorsFrame_Update(); end end end function ScriptErrorsFrame_Update () local editBox = ScriptErrorsFrameScrollFrameText; local index = _ScriptErrorsFrame.index; if ( not index or not _ScriptErrorsFrame.order[index] ) then index = #_ScriptErrorsFrame.order; _ScriptErrorsFrame.index = index; end if ( index == 0 ) then editBox:SetText(""); ScriptErrorsFrame_UpdateButtons(); return; end local text = string.format( ERROR_FORMAT, _ScriptErrorsFrame.messages[index], _ScriptErrorsFrame.times[index], _ScriptErrorsFrame.count[index], _ScriptErrorsFrame.order[index], _ScriptErrorsFrame.locals[index] ); local parent = editBox:GetParent(); local prevText = editBox.text; editBox.text = text; if ( prevText ~= text ) then editBox:SetText(text); editBox:HighlightText(0); editBox:SetCursorPosition(0); else ScrollingEdit_OnTextChanged(editBox, parent); end parent:SetVerticalScroll(0); ScriptErrorsFrame_UpdateButtons(); end function ScriptErrorsFrame_UpdateButtons () local index = _ScriptErrorsFrame.index; local numErrors = #_ScriptErrorsFrame.order; if ( index == 0 ) then _ScriptErrorsFrame.next:Disable(); _ScriptErrorsFrame.previous:Disable(); else if ( numErrors == 1 ) then _ScriptErrorsFrame.next:Disable(); _ScriptErrorsFrame.previous:Disable(); elseif ( index == 1 ) then _ScriptErrorsFrame.next:Enable(); _ScriptErrorsFrame.previous:Disable(); elseif ( index == numErrors ) then _ScriptErrorsFrame.next:Disable(); _ScriptErrorsFrame.previous:Enable(); else _ScriptErrorsFrame.next:Enable(); _ScriptErrorsFrame.previous:Enable(); end end _ScriptErrorsFrame.indexLabel:SetText(string.format(INDEX_ORDER_FORMAT, index, numErrors)); end function ScriptErrorsFrame_DeleteError (index) if ( _ScriptErrorsFrame.order[index] ) then _ScriptErrorsFrame.seen[_ScriptErrorsFrame.messages[index] .. _ScriptErrorsFrame.order[index]] = nil; tremove(_ScriptErrorsFrame.order, index); tremove(_ScriptErrorsFrame.messages, index); tremove(_ScriptErrorsFrame.times, index); tremove(_ScriptErrorsFrame.count, index); end end function ScriptErrorsFrameButton_OnClick (self) local id = self:GetID(); if ( id == 1 ) then _ScriptErrorsFrame.index = _ScriptErrorsFrame.index + 1; else _ScriptErrorsFrame.index = _ScriptErrorsFrame.index - 1; end ScriptErrorsFrame_Update(); end --[[ function ScriptErrorsFrameDelete_OnClick (self); local index = _ScriptErrorsFrame.index; ScriptErrorsFrame_DeleteError(index); local numErrors = #_ScriptErrorsFrame.order; if ( numErrors == 0 ) then _ScriptErrorsFrame.index = 0; elseif ( index > numErrors ) then _ScriptErrorsFrame.index = numErrors; end ScriptErrorsFrame_Update(); end ]] function DebugTooltip_OnLoad(self) self:SetFrameLevel(self:GetFrameLevel() + 2); self:SetBackdropBorderColor(TOOLTIP_DEFAULT_COLOR.r, TOOLTIP_DEFAULT_COLOR.g, TOOLTIP_DEFAULT_COLOR.b); self:SetBackdropColor(TOOLTIP_DEFAULT_BACKGROUND_COLOR.r, TOOLTIP_DEFAULT_BACKGROUND_COLOR.g, TOOLTIP_DEFAULT_BACKGROUND_COLOR.b); self.statusBar2 = getglobal(self:GetName().."StatusBar2"); self.statusBar2Text = getglobal(self:GetName().."StatusBar2Text"); end function FrameStackTooltip_Toggle (showHidden) local tooltip = _G["FrameStackTooltip"]; if ( tooltip:IsVisible() ) then tooltip:Hide(); else tooltip:SetOwner(UIParent, "ANCHOR_NONE"); tooltip:SetPoint("BOTTOMRIGHT", UIParent, "BOTTOMRIGHT", -CONTAINER_OFFSET_X - 13, CONTAINER_OFFSET_Y); tooltip.default = 1; tooltip.showHidden = showHidden; tooltip:SetFrameStack(showHidden); end end FRAMESTACK_UPDATE_TIME = .1 local _timeSinceLast = 0 function FrameStackTooltip_OnUpdate (self, elapsed) _timeSinceLast = _timeSinceLast - elapsed; if ( _timeSinceLast <= 0 ) then _timeSinceLast = FRAMESTACK_UPDATE_TIME; self:SetFrameStack(self.showHidden); end end function FrameStackTooltip_OnShow (self) local parent = self:GetParent() or UIParent; local ps = parent:GetEffectiveScale(); local px, py = parent:GetCenter(); px, py = px * ps, py * ps; local x, y = GetCursorPosition(); self:ClearAllPoints(); if (x > px) then if (y > py) then self:SetPoint("BOTTOMLEFT", parent, "BOTTOMLEFT", 20, 20); else self:SetPoint("TOPLEFT", parent, "TOPLEFT", 20, -20); end else if (y > py) then self:SetPoint("BOTTOMRIGHT", parent, "BOTTOMRIGHT", -20, 20); else self:SetPoint("TOPRIGHT", parent, "TOPRIGHT", -20, -20); end end end FrameStackTooltip_OnEnter = FrameStackTooltip_OnShow;