CPUThieves
This addon makes an attempt at spotting CPU thieves live and reporting about them. You probably don't want to use this if you don't have WoW Lua developer experience.
CPUThieves: Detected 182ms FPS hickup DURING COMBAT. (174ms = Lua) CPUThieves: 170 0.2s WowLua CPUThieves: 170 0.2s F:WowLuaButton_Config CPUThieves: .. and 3 more frames with same handler function CPUThieves: 170 0.2s F:WowLuaButton_Previous CPUThieves: 170 0.2s F:WowLuaButton_Next CPUThieves: 170 0.2s F:WowLuaButton_Redo CPUThieves: .. and 5 more frames with same handler function
Important
It needs CPU profiling to do its work, which is enabled via the command line: /cputhieves enable
! Remember to /cputhieves disable
when you are done profiling!
It WILL try to report on excessive CPU usage during combat, but note that this is not a cheap operation, so it may actually trigger the 200ms execution limit set by Blizzard, leading to Lua errors, and probably more reports followed by more errors. If this happens to you, load fewer addons while doing these tests, or get a faster CPU :D
How to parse the output
Note that the (123 = Lua)
on the first line comes from Blizzard's GetScriptCPUUsage() API which seems a bit random in what it returns - probably it's timing related and I can't be arsed to figure it out right now. Sorry!
For AddOn CPU usage, you need to remember that it counts time spent in functions CREATED by a given addon, so often something like Ace3 will be showing high. Also, it will NOT count any time spent inside APIs - even those that use lots of time - so the time for an addon might show lower than it actually is.
For Frame CPU usage (items reported with an "F:" before the name), time spent inside APIs will be correctly counted. But note that frames with the same handler function will all show the same CPU time spent, which CPUThieves (half-heartedly) tries to hide some of. This becomes very visible if something gets blamed on the ChatFrames - there's 10 of them!
For Function CPU usage (items reported with "()"), it also includes the time of all subroutines called, so in e.g. a 150ms hickups you might see several functions taking almost 150ms each. This is expected.
Important Blizzard oddities
You should know:
GetFrameCPUTime() on its own only returns the total time used by its CURRENTLY set script functions, none of the past ones.
CPUThieves keeps track of all OnUpdate and OnEvent scripts that it has SEEN a frame use, and sums them all up. This should prevent the worst effects of addons switching their scripts live, but is not guaranteed to work 100% - we can still miss out on things between polling cycles!
C_Timer timers tend to not show up in either Frame or AddOn CPU (but sometimes they do, hrm..). This is why CPUThieves monitors timer functions explicitly.
More advanced
/cputhieves stats [N]
will show the top N/7 addons & frames by total CPU time used:
CPUThieves: Tracking total 234 addons, 16247 frames CPUThieves: Addons by total CPU: CPUThieves: 5.8s !CPUThieves CPUThieves: 1.3s Ace3 CPUThieves: 0.8s Overachiever CPUThieves: 0.6s QuestCompletist CPUThieves: 0.6s Pawn CPUThieves: 0.3s WeakAuras CPUThieves: 0.3s SavedInstances CPUThieves: Frames by total CPU: CPUThieves: 1.5s UnnamedFrame2C2EA960 CPUThieves: 1.3s F:AceEvent30Frame CPUThieves: 1.1s F:AceAddon30Frame CPUThieves: 1.1s F:PawnUIFrame CPUThieves: 0.7s F:qcQuestCompletistUI CPUThieves: 0.5s F:Atr_core CPUThieves: 0.4s F:UIParent CPUThieves: CPUThieves uses about 4.3ms per frame. Worst seen: 23.4ms
It is normal for !CPUThieves to use plenty of CPU time itself since it needs to keep looping through all addons and frames to stay up-to-date with their CPU usages (to know which addon used extra CPU when FPS hickups are detected!)
/cputhieves reset
will reset these timers.
/cputhieves output
lets you move the alerting to another ChatFrame (or anything with a :AddMessage function)
Identifying unnamed frames and functions
All unnamed frames that use high CPU are assigned a global name e.g. UnnamedFrame01234567 and reported as such. This allows you to prod it yourself with e.g. /dump
Unnamed functions (usually detected via C_Timer usage) are assigned a global name e.g. Function01234567 and reported as such.
Also try /cputhieves identify VARNAME
that does its best to find out just where the hell that variable came from.
- It loops through ALL global vars&tables, up to 6 deep, trying to find someone pointing at it
- For frames, it loops ALL Script handlers and does the same global scan
- For any table, it loops all members and does issecurevariable() on them, trying to find mention of addons It will take a second or two to complete with all the variable scanning. Don't worry, your WoW didn't hang.
Functions
By default, we monitor: - C_Timer callouts (and where they were registered the first time) - All GameTooltip APIs (you'd be surprised how much time they take!) - All functions in the global namespace at startup (the "!" in "!CPUThieves" means we load before pretty much everything else)
Logging
CPUThieves logs all its output into its savedvariables file, in a rotating 1000-line buffer, timestamped:
CPUThievesDB = { ["chatFrame"] = "ChatFrame1", ["log"] = { "06:21:06 CPUThieves: 2.7s F:UIParent", -- [1] "06:21:06 CPUThieves: 1.4s F:table: 000000001B190C60", -- [2] -- 8< -- "07:13:10 CPUThieves: 56 49.7s !CPUThieves", -- [485] "05:24:34 CPUThieves: 110 0.7s F:ChatFrame7EditBox", -- [486] --- 8< -- "06:21:06 CPUThieves: 3.6s F:AceEvent30Frame", -- [999] "06:21:06 CPUThieves: 3.5s F:AceAddon30Frame", -- [1000] ["n"] = 485, }, }
The ["n"] entry shows where it wrote last.
I've been using it live in raids a couple of times now.. it actually works. Yeah the FPS gets a bit choppy here and there but it's playable. (This on a farily overclocked 3570K CPU though so it probably isn't for everyone)