Quantcast

Improve Profiler API.

Johnny C. Lam [06-04-14 - 20:23]
Improve Profiler API.

* Add method Profiler:Wrap() to wrap Blizzard API functions and use it in
  various modules to profile usage of the Blizzard functions.

- Add method Profiler:GetProfilingGroup() to get the methods collection
  associated with a particular profiling group.

- Extend Profiler:RegisterProfilingGroup() to take two callbacks for
  group-specific enabling and disabling of profiling.

git-svn-id: svn://svn.curseforge.net/wow/ovale/mainline/trunk@1511 d5049fe3-3747-40f7-a4b5-f36d6801af5f
Filename
OvaleActionBar.lua
OvaleAura.lua
OvaleBestAction.lua
OvaleComboPoints.lua
OvaleCompile.lua
OvaleCooldown.lua
OvaleDamageTaken.lua
OvaleEclipse.lua
OvaleEnemies.lua
OvaleEquipement.lua
OvaleFuture.lua
OvalePaperDoll.lua
OvalePool.lua
OvalePoolRefCount.lua
OvalePower.lua
OvaleRunes.lua
OvaleSpellDamage.lua
OvaleStance.lua
Profiler.lua
diff --git a/OvaleActionBar.lua b/OvaleActionBar.lua
index 1e21cea..4f17ca5 100644
--- a/OvaleActionBar.lua
+++ b/OvaleActionBar.lua
@@ -18,8 +18,9 @@ Ovale.OvaleActionBar = OvaleActionBar
 local Profiler = Ovale.Profiler
 local profiler = nil
 do
-	Profiler:RegisterProfilingGroup("OvaleActionBar")
-	profiler = Profiler.group["OvaleActionBar"]
+	local group = OvaleActionBar:GetName()
+	Profiler:RegisterProfilingGroup(group)
+	profiler = Profiler:GetProfilingGroup(group)
 end

 local gsub = string.gsub
diff --git a/OvaleAura.lua b/OvaleAura.lua
index 8111a8b..88d04c2 100644
--- a/OvaleAura.lua
+++ b/OvaleAura.lua
@@ -20,8 +20,9 @@ Ovale.OvaleAura = OvaleAura
 local Profiler = Ovale.Profiler
 local profiler = nil
 do
-	Profiler:RegisterProfilingGroup("OvaleAura")
-	profiler = Profiler.group["OvaleAura"]
+	local group = OvaleAura:GetName()
+	Profiler:RegisterProfilingGroup(group)
+	profiler = Profiler:GetProfilingGroup(group)
 end

 local OvalePool = Ovale.OvalePool
@@ -372,9 +373,9 @@ function OvaleAura:RemoveAurasOnInactiveUnits()
 end

 function OvaleAura:IsActiveAura(aura, now)
-	now = now or API_GetTime()
 	local boolean = false
 	if aura then
+		now = now or API_GetTime()
 		if aura.serial == self.serial[aura.guid] and aura.stacks > 0 and aura.gain <= now and now <= aura.ending then
 			boolean = true
 		elseif aura.consumed and IsWithinAuraLag(aura.ending, now) then
@@ -538,11 +539,11 @@ end

 -- Scan auras on the given GUID and update the aura database.
 function OvaleAura:ScanAurasOnGUID(guid)
-	profiler.Start("OvaleAura_ScanAurasOnGUID")
 	if not guid then return end
 	local unitId = OvaleGUID:GetUnitId(guid)
 	if not unitId then return end

+	profiler.Start("OvaleAura_ScanAurasOnGUID")
 	local now = API_GetTime()
 	Ovale:DebugPrintf(OVALE_AURA_DEBUG, "Scanning auras on %s (%s) at %f", guid, unitId, now)

diff --git a/OvaleBestAction.lua b/OvaleBestAction.lua
index 1e46c8d..3fcfa05 100644
--- a/OvaleBestAction.lua
+++ b/OvaleBestAction.lua
@@ -42,6 +42,7 @@ local Intersect = OvaleTimeSpan.Intersect
 local IntersectInterval = OvaleTimeSpan.IntersectInterval
 local Measure = OvaleTimeSpan.Measure
 local Union = OvaleTimeSpan.Union
+
 local API_GetTime = GetTime
 local API_GetActionCooldown = GetActionCooldown
 local API_GetActionTexture = GetActionTexture
@@ -57,6 +58,49 @@ local API_IsSpellInRange = IsSpellInRange
 local API_IsUsableAction = IsUsableAction
 local API_IsUsableItem = IsUsableItem

+-- Profiling set-up.
+local Profiler = Ovale.Profiler
+local profiler = nil
+do
+	local group = OvaleBestAction:GetName()
+
+	local function EnableProfiling()
+		API_GetActionCooldown = Profiler:Wrap(group, "OvaleBestAction_API_GetActionCooldown", GetActionCooldown)
+		API_GetActionTexture = Profiler:Wrap(group, "OvaleBestAction_API_GetActionTexture", GetActionTexture)
+		API_GetItemIcon = Profiler:Wrap(group, "OvaleBestAction_API_GetItemIcon", GetItemIcon)
+		API_GetItemCooldown = Profiler:Wrap(group, "OvaleBestAction_API_GetItemCooldown", GetItemCooldown)
+		API_GetItemSpell = Profiler:Wrap(group, "OvaleBestAction_API_GetItemSpell", GetItemSpell)
+		API_GetSpellInfo = Profiler:Wrap(group, "OvaleBestAction_API_GetSpellInfo", GetSpellTexture)
+		API_GetSpellTexture = Profiler:Wrap(group, "OvaleBestAction_API_GetSpellTexture", GetSpellTexture)
+		API_IsActionInRange = Profiler:Wrap(group, "OvaleBestAction_API_IsActionInRange", IsActionInRange)
+		API_IsCurrentAction = Profiler:Wrap(group, "OvaleBestAction_API_IsCurrentAction", IsCurrentAction)
+		API_IsItemInRange = Profiler:Wrap(group, "OvaleBestAction_API_IsItemInRange", IsItemInRange)
+		API_IsSpellInRange = Profiler:Wrap(group, "OvaleBestAction_API_IsSpellInRange", IsSpellInRange)
+		API_IsUsableAction = Profiler:Wrap(group, "OvaleBestAction_API_IsUsableAction", IsUsableAction)
+		API_IsUsableItem = Profiler:Wrap(group, "OvaleBestAction_API_IsUsableItem", IsUsableItem)
+	end
+
+	local function DisableProfiling()
+		API_GetTime = GetTime
+		API_GetActionCooldown = GetActionCooldown
+		API_GetActionTexture = GetActionTexture
+		API_GetItemIcon = GetItemIcon
+		API_GetItemCooldown = GetItemCooldown
+		API_GetItemSpell = GetItemSpell
+		API_GetSpellInfo = GetSpellInfo
+		API_GetSpellTexture = GetSpellTexture
+		API_IsActionInRange = IsActionInRange
+		API_IsCurrentAction = IsCurrentAction
+		API_IsItemInRange = IsItemInRange
+		API_IsSpellInRange = IsSpellInRange
+		API_IsUsableAction = IsUsableAction
+		API_IsUsableItem = IsUsableItem
+	end
+
+	Profiler:RegisterProfilingGroup(group, EnableProfiling, DisableProfiling)
+	profiler = Profiler:GetProfilingGroup(group)
+end
+
 local OVALE_DEFAULT_PRIORITY = 3

 -- Age of the current computation.
@@ -78,6 +122,7 @@ local function PutValue(element, value, origin, rate)
 end

 local function ComputeAction(element, state)
+	profiler.Start("OvaleBestAction_ComputeAction")
 	local self = OvaleBestAction
 	local action = element.params[1]
 	local actionTexture, actionInRange, actionCooldownStart, actionCooldownDuration,
@@ -87,12 +132,15 @@ local function ComputeAction(element, state)

 	if not actionTexture then
 		Ovale:Logf("Action %s not found", action)
+		profiler.Stop("OvaleBestAction_ComputeAction")
 		return timeSpan
 	elseif not (actionEnable and actionEnable > 0) then
 		Ovale:Logf("Action %s not enabled", action)
+		profiler.Stop("OvaleBestAction_ComputeAction")
 		return timeSpan
 	elseif element.params.usable == 1 and not actionUsable then
 		Ovale:Logf("Action %s not usable", action)
+		profiler.Stop("OvaleBestAction_ComputeAction")
 		return timeSpan
 	end

@@ -181,13 +229,17 @@ local function ComputeAction(element, state)

 	local priority = element.params.priority or OVALE_DEFAULT_PRIORITY
 	if value then
-		return timeSpan, priority, PutValue(element, value, 0, 0)
+		local result = PutValue(element, value, 0, 0)
+		profiler.Stop("OvaleBestAction_ComputeAction")
+		return timeSpan, priority, result
 	else
+		profiler.Stop("OvaleBestAction_ComputeAction")
 		return timeSpan, priority, element
 	end
 end

 local function ComputeAnd(element, state)
+	profiler.Start("OvaleBestAction_Compute")
 	Ovale:Logf("%s [%d]", element.type, element.nodeId)
 	local self = OvaleBestAction
 	local timeSpanA = self:ComputeBool(element.a, state)
@@ -203,10 +255,12 @@ local function ComputeAnd(element, state)
 		Intersect(timeSpanA, timeSpanB, timeSpan)
 	end
 	Ovale:Logf("%s returns %s [%d]", element.type, tostring(timeSpan), element.nodeId)
+	profiler.Stop("OvaleBestAction_Compute")
 	return timeSpan
 end

 local function ComputeArithmetic(element, state)
+	profiler.Start("OvaleBestAction_Compute")
 	local self = OvaleBestAction
 	local timeSpanA, _, elementA = self:Compute(element.a, state)
 	local timeSpanB, _, elementB = self:Compute(element.b, state)
@@ -217,7 +271,9 @@ local function ComputeArithmetic(element, state)
 	Intersect(timeSpanA, timeSpanB, timeSpan)
 	if Measure(timeSpan) == 0 then
 		Ovale:Logf("%s return %s [%d]", element.type, tostring(timeSpan), element.nodeId)
-		return timeSpan, OVALE_DEFAULT_PRIORITY, PutValue(element, 0, 0, 0)
+		local result = PutValue(element, 0, 0, 0)
+		profiler.Stop("OvaleBestAction_Compute")
+		return timeSpan, OVALE_DEFAULT_PRIORITY, result
 	end

 	--[[
@@ -310,10 +366,13 @@ local function ComputeArithmetic(element, state)
 		end
 	end
 	Ovale:Logf("result = %f+(t-%f)*%f [%d]", l, m, n, element.nodeId)
-	return timeSpan, OVALE_DEFAULT_PRIORITY, PutValue(element, l, m, n)
+	local result = PutValue(element, l, m, n)
+	profiler.Stop("OvaleBestAction_Compute")
+	return timeSpan, OVALE_DEFAULT_PRIORITY, result
 end

 local function ComputeCompare(element, state)
+	profiler.Start("OvaleBestAction_Compute")
 	local self = OvaleBestAction
 	local timeSpanA, _, elementA = self:Compute(element.a, state)
 	local timeSpanB, _, elementB = self:Compute(element.b, state)
@@ -323,6 +382,7 @@ local function ComputeCompare(element, state)
 	-- Take intersection of A and B.
 	Intersect(timeSpanA, timeSpanB, timeSpan)
 	if Measure(timeSpan) == 0 then
+		profiler.Stop("OvaleBestAction_Compute")
 		return timeSpan
 	end

@@ -380,10 +440,12 @@ local function ComputeCompare(element, state)
 		self_pool:Release(scratch)
 	end
 	Ovale:Logf("compare %s returns %s [%d]", operator, tostring(timeSpan), element.nodeId)
+	profiler.Stop("OvaleBestAction_Compute")
 	return timeSpan
 end

 local function ComputeCustomFunction(element, state)
+	profiler.Start("OvaleBestAction_Compute")
 	Ovale:Logf("custom function %s", element.name)
 	local self = OvaleBestAction
 	if not element.serial or element.serial < self_serial then
@@ -424,19 +486,24 @@ local function ComputeCustomFunction(element, state)
 			end
 		end
 		timeSpan[1], timeSpan[2] = 0, math.huge
-		return timeSpan, priorityA, PutValue(element, value, 0, 0)
+		local result = PutValue(element, value, 0, 0)
+		profiler.Stop("OvaleBestAction_Compute")
+		return timeSpan, priorityA, result
 	else
 		CopyTimeSpan(timeSpanA, timeSpan)
+		profiler.Stop("OvaleBestAction_Compute")
 		return timeSpan, priorityA, elementA
 	end
 end

 local function ComputeFunction(element, state)
+	profiler.Start("OvaleBestAction_ComputeFunction")
 	local timeSpan = element.timeSpan
 	timeSpan:Reset()

 	if not OvaleCondition:IsCondition(element.func) then
 		Ovale:Errorf("Condition %s not found", element.func)
+		profiler.Stop("OvaleBestAction_ComputeFunction")
 		return timeSpan
 	end

@@ -481,13 +548,17 @@ local function ComputeFunction(element, state)
 	end

 	if value then
-		return timeSpan, OVALE_DEFAULT_PRIORITY, PutValue(element, value, origin, rate)
+		local result = PutValue(element, value, origin, rate)
+		profiler.Stop("OvaleBestAction_ComputeFunction")
+		return timeSpan, OVALE_DEFAULT_PRIORITY, result
 	else
+		profiler.Stop("OvaleBestAction_ComputeFunction")
 		return timeSpan
 	end
 end

 local function ComputeGroup(element, state)
+	profiler.Start("OvaleBestAction_Compute")
 	local self = OvaleBestAction
 	local bestTimeSpan, bestPriority, bestElement, bestCastTime
 	local timeSpan = element.timeSpan
@@ -496,6 +567,7 @@ local function ComputeGroup(element, state)
 	Ovale:Logf("%s [%d]", element.type, element.nodeId)

 	if #element.nodes == 1 then
+		profiler.Stop("OvaleBestAction_Compute")
 		return self:Compute(element.nodes[1], state)
 	end

@@ -564,6 +636,7 @@ local function ComputeGroup(element, state)

 	if not bestTimeSpan then
 		Ovale:Logf("group return %s [%d]", tostring(timeSpan), element.nodeId)
+		profiler.Stop("OvaleBestAction_Compute")
 		return timeSpan
 	else
 		CopyTimeSpan(bestTimeSpan, timeSpan)
@@ -576,11 +649,13 @@ local function ComputeGroup(element, state)
 		else
 			Ovale:Logf("group no best action returns %s [%d]", tostring(timeSpan), element.nodeId)
 		end
+		profiler.Stop("OvaleBestAction_Compute")
 		return timeSpan, bestPriority, bestElement
 	end
 end

 local function ComputeIf(element, state)
+	profiler.Start("OvaleBestAction_Compute")
 	Ovale:Logf("%s [%d]", element.type, element.nodeId)
 	local self = OvaleBestAction

@@ -600,7 +675,9 @@ local function ComputeIf(element, state)
 		timeSpan:Reset(conditionTimeSpan)
 		self_pool:Release(conditionTimeSpan)
 		Ovale:Logf("%s return %s [%d]", element.type, tostring(timeSpan), element.nodeId)
-		return timeSpan, OVALE_DEFAULT_PRIORITY, PutValue(element, 0, 0, 0)
+		local result = PutValue(element, 0, 0, 0)
+		profiler.Stop("OvaleBestAction_Compute")
+		return timeSpan, OVALE_DEFAULT_PRIORITY, result
 	end

 	local timeSpanB, priorityB, elementB = self:Compute(element.b, state)
@@ -613,10 +690,12 @@ local function ComputeIf(element, state)
 	self_pool:Release(conditionTimeSpan)

 	Ovale:Logf("%s return %s [%d]", element.type, tostring(timeSpan), element.nodeId)
+	profiler.Stop("OvaleBestAction_Compute")
 	return timeSpan, priorityB, elementB
 end

 local function ComputeLua(element, state)
+	profiler.Start("OvaleBestAction_ComputeLua")
 	local ret = loadstring(element.lua)()
 	Ovale:Logf("lua %s [%d]", ret, element.nodeId)

@@ -624,10 +703,13 @@ local function ComputeLua(element, state)
 	timeSpan:Reset()

 	timeSpan[1], timeSpan[2] = 0, math.huge
-	return timeSpan, OVALE_DEFAULT_PRIORITY, PutValue(element, ret, 0, 0)
+	local result = PutValue(element, ret, 0, 0)
+	profiler.Stop("OvaleBestAction_ComputeLua")
+	return timeSpan, OVALE_DEFAULT_PRIORITY, result
 end

 local function ComputeNot(element, state)
+	profiler.Start("OvaleBestAction_Compute")
 	Ovale:Logf("%s [%d]", element.type, element.nodeId)
 	local self = OvaleBestAction
 	local timeSpanA = self:ComputeBool(element.a, state)
@@ -636,10 +718,12 @@ local function ComputeNot(element, state)

 	Complement(timeSpanA, timeSpan)
 	Ovale:Logf("%s returns %s [%d]", element.type, tostring(timeSpan), element.nodeId)
+	profiler.Stop("OvaleBestAction_Compute")
 	return timeSpan
 end

 local function ComputeOr(element, state)
+	profiler.Start("OvaleBestAction_Compute")
 	Ovale:Logf("%s [%d]", element.type, element.nodeId)
 	local self = OvaleBestAction
 	local timeSpanA = self:ComputeBool(element.a, state)
@@ -650,19 +734,23 @@ local function ComputeOr(element, state)
 	-- Take union of A and B.
 	Union(timeSpanA, timeSpanB, timeSpan)
 	Ovale:Logf("%s returns %s [%d]", element.type, tostring(timeSpan), element.nodeId)
+	profiler.Stop("OvaleBestAction_Compute")
 	return timeSpan
 end

 local function ComputeValue(element, state)
+	profiler.Start("OvaleBestAction_ComputeValue")
 	Ovale:Logf("value %s", element.value)
 	local timeSpan = element.timeSpan
 	timeSpan:Reset()

 	timeSpan[1], timeSpan[2] = 0, math.huge
+	profiler.Stop("OvaleBestAction_ComputeValue")
 	return timeSpan, OVALE_DEFAULT_PRIORITY, element
 end

 local function ComputeWait(element, state)
+	profiler.Start("OvaleBestAction_Compute")
 	Ovale:Logf("%s [%d]", element.type, element.nodeId)
 	local self = OvaleBestAction
 	local timeSpanA, priorityA, elementA = self:Compute(element.a, state)
@@ -674,6 +762,7 @@ local function ComputeWait(element, state)
 		CopyTimeSpan(timeSpanA, timeSpan)
 		Ovale:Logf("%s return %s [%d]", element.type, tostring(timeSpan), element.nodeId)
 	end
+	profiler.Stop("OvaleBestAction_Compute")
 	return timeSpan, priorityA, elementA
 end
 --</private-static-methods>
@@ -723,6 +812,7 @@ function OvaleBestAction:GetActionInfo(element, state)
 		return nil
 	end

+	profiler.Start("OvaleBestAction_GetActionInfo")
 	local target = element.params.target or OvaleCondition.defaultTarget
 	local action
 	local actionTexture, actionInRange, actionCooldownStart, actionCooldownDuration,
@@ -734,6 +824,7 @@ function OvaleBestAction:GetActionInfo(element, state)
 		action = OvaleActionBar:GetForSpell(spellId)
 		if not OvaleSpellBook:IsKnownSpell(spellId) and not action then
 			Ovale:Logf("Spell %s not learnt", spellId)
+			profiler.Stop("OvaleBestAction_GetActionInfo")
 			return nil
 		end

@@ -748,12 +839,14 @@ function OvaleBestAction:GetActionInfo(element, state)
 		if si then
 			if si.stance and not OvaleStance:IsStance(si.stance) then
 				-- Spell requires a stance that player is not in.
+				profiler.Stop("OvaleBestAction_GetActionInfo")
 				return nil
 			end
 			if si.combo then
 				-- Spell requires combo points.
 				local cost = state:ComboPointCost(spellId)
 				if state.combo < cost then
+					profiler.Stop("OvaleBestAction_GetActionInfo")
 					return nil
 				end
 			end
@@ -762,6 +855,7 @@ function OvaleBestAction:GetActionInfo(element, state)
 					-- Spell requires "secondary" resources, e.g., chi, focus, rage, etc.,
 					local cost = state:PowerCost(spellId, powerType)
 					if state[powerType] < cost then
+						profiler.Stop("OvaleBestAction_GetActionInfo")
 						return nil
 					end
 				end
@@ -820,6 +914,7 @@ function OvaleBestAction:GetActionInfo(element, state)
 		action = OvaleActionBar:GetForMacro(macro)
 		if not action then
 			Ovale:Logf("Unknown macro %s", macro)
+			profiler.Stop("OvaleBestAction_GetActionInfo")
 			return nil
 		end
 		actionTexture = API_GetActionTexture(action)
@@ -836,6 +931,7 @@ function OvaleBestAction:GetActionInfo(element, state)
 		end
 		if not itemId then
 			Ovale:Logf("Unknown item %s", element.params[1])
+			profiler.Stop("OvaleBestAction_GetActionInfo")
 			return nil
 		end
 		Ovale:Logf("Item %s", itemId)
@@ -867,6 +963,7 @@ function OvaleBestAction:GetActionInfo(element, state)
 		actionIsCurrent = API_IsCurrentAction(action)
 	end

+	profiler.Stop("OvaleBestAction_GetActionInfo")
 	return actionTexture, actionInRange, actionCooldownStart, actionCooldownDuration,
 		actionUsable, actionShortcut, actionIsCurrent, actionEnable, actionType, actionId, target, element.params.nored
 end
diff --git a/OvaleComboPoints.lua b/OvaleComboPoints.lua
index 730ed79..39d167f 100644
--- a/OvaleComboPoints.lua
+++ b/OvaleComboPoints.lua
@@ -18,8 +18,9 @@ Ovale.OvaleComboPoints = OvaleComboPoints
 local Profiler = Ovale.Profiler
 local profiler = nil
 do
-	Profiler:RegisterProfilingGroup("OvaleComboPoints")
-	profiler = Profiler.group["OvaleComboPoints"]
+	local group = OvaleComboPoints:GetName()
+	Profiler:RegisterProfilingGroup(group)
+	profiler = Profiler:GetProfilingGroup(group)
 end

 -- Forward declarations for module dependencies.
@@ -150,7 +151,9 @@ function OvaleComboPoints:UNIT_COMBO_POINTS(event, ...)
 end

 function OvaleComboPoints:Refresh()
+	profiler.Start("OvaleComboPoints_Refresh")
 	self.combo = API_GetComboPoints("player") or 0
+	profiler.Stop("OvaleComboPoints_Refresh")
 end

 function OvaleComboPoints:Debug()
diff --git a/OvaleCompile.lua b/OvaleCompile.lua
index 395c023..5d1d362 100644
--- a/OvaleCompile.lua
+++ b/OvaleCompile.lua
@@ -13,14 +13,6 @@ local OvaleCompile = Ovale:NewModule("OvaleCompile", "AceEvent-3.0")
 Ovale.OvaleCompile = OvaleCompile

 --<private-static-properties>
--- Profiling set-up.
-local Profiler = Ovale.Profiler
-local profiler = nil
-do
-	Profiler:RegisterProfilingGroup("OvaleCompile")
-	profiler = Profiler.group["OvaleCompile"]
-end
-
 local L = Ovale.L
 local OvalePool = Ovale.OvalePool
 local OvaleTimeSpan = Ovale.OvaleTimeSpan
@@ -51,6 +43,26 @@ local wipe = table.wipe
 local API_GetItemInfo = GetItemInfo
 local API_GetSpellInfo = GetSpellInfo

+-- Profiling set-up.
+local Profiler = Ovale.Profiler
+local profiler = nil
+do
+	local group = OvaleCompile:GetName()
+
+	local function EnableProfiling()
+		API_GetItemInfo = Profiler:Wrap(group, "OvaleCompile_API_GetItemInfo", GetItemInfo)
+		API_GetSpellInfo = Profiler:Wrap(group, "OvaleCompile_API_GetSpellInfo", GetSpellInfo)
+	end
+
+	local function DisableProfiling()
+		API_GetItemInfo = GetItemInfo
+		API_GetSpellInfo = GetSpellInfo
+	end
+
+	Profiler:RegisterProfilingGroup(group, EnableProfiling, DisableProfiling)
+	profiler = Profiler:GetProfilingGroup(group)
+end
+
 local self_node = {}
 local self_pool = OvalePool("OvaleCompile_pool")
 local self_timeSpanPool = OvalePool("OvaleCompile_timeSpanPool")
@@ -780,7 +792,7 @@ end
 local function ParseItemName(text)
 	local itemId = tonumber(text)
 	if itemId then
-		local item = API_GetItemInfo(spellId) or "Item " .. itemId
+		local item = API_GetItemInfo(itemId) or "Item " .. itemId
 		return '"' .. item .. '"'
 	else
 		Ovale:FormatPrint("ItemName of %s unknown\n", text)
diff --git a/OvaleCooldown.lua b/OvaleCooldown.lua
index cb3b585..5c5d2ca 100644
--- a/OvaleCooldown.lua
+++ b/OvaleCooldown.lua
@@ -13,14 +13,6 @@ local OvaleCooldown = Ovale:NewModule("OvaleCooldown", "AceEvent-3.0")
 Ovale.OvaleCooldown = OvaleCooldown

 --<private-static-properties>
--- Profiling set-up.
-local Profiler = Ovale.Profiler
-local profiler = nil
-do
-	Profiler:RegisterProfilingGroup("OvaleCooldown")
-	profiler = Profiler.group["OvaleCooldown"]
-end
-
 -- Forward declarations for module dependencies.
 local OvaleData = nil
 local OvaleGUID = nil
@@ -34,6 +26,30 @@ local API_UnitHealth = UnitHealth
 local API_UnitHealthMax = UnitHealthMax
 local API_UnitClass = UnitClass

+-- Profiling set-up.
+local Profiler = Ovale.Profiler
+local profiler = nil
+do
+	local group = OvaleCooldown:GetName()
+
+	local function EnableProfiling()
+		API_GetSpellCharges = Profiler:Wrap(group, "OvaleCooldown_API_GetSpellCharges", GetSpellCharges)
+		API_GetSpellCooldown = Profiler:Wrap(group, "OvaleCooldown_API_GetSpellCooldown", GetSpellCooldown)
+		API_UnitHealth = Profiler:Wrap(group, "OvaleCooldown_API_UnitHealth", UnitHealth)
+		API_UnitHealthMax = Profiler:Wrap(group, "OvaleCooldown_API_UnitHealthMax", UnitHealthMax)
+	end
+
+	local function DisableProfiling()
+		API_GetSpellCharges = GetSpellCharges
+		API_GetSpellCooldown = GetSpellCooldown
+		API_UnitHealth = UnitHealth
+		API_UnitHealthMax = UnitHealthMax
+	end
+
+	Profiler:RegisterProfilingGroup(group, EnableProfiling, DisableProfiling)
+	profiler = Profiler:GetProfilingGroup(group)
+end
+
 -- Player's class.
 local _, self_class = API_UnitClass("player")
 -- Current age of cooldown state.
diff --git a/OvaleDamageTaken.lua b/OvaleDamageTaken.lua
index 9d0dd9e..d57d7ae 100644
--- a/OvaleDamageTaken.lua
+++ b/OvaleDamageTaken.lua
@@ -18,8 +18,9 @@ Ovale.OvaleDamageTaken = OvaleDamageTaken
 local Profiler = Ovale.Profiler
 local profiler = nil
 do
-	Profiler:RegisterProfilingGroup("OvaleDamageTaken")
-	profiler = Profiler.group["OvaleDamageTaken"]
+	local group = OvaleDamageTaken:GetName()
+	Profiler:RegisterProfilingGroup(group)
+	profiler = Profiler:GetProfilingGroup(group)
 end

 local OvalePool = Ovale.OvalePool
diff --git a/OvaleEclipse.lua b/OvaleEclipse.lua
index 2535804..f45fd36 100644
--- a/OvaleEclipse.lua
+++ b/OvaleEclipse.lua
@@ -20,8 +20,9 @@ Ovale.OvaleEclipse = OvaleEclipse
 local Profiler = Ovale.Profiler
 local profiler = nil
 do
-	Profiler:RegisterProfilingGroup("OvaleEclipse")
-	profiler = Profiler.group["OvaleEclipse"]
+	local group = OvaleEclipse:GetName()
+	Profiler:RegisterProfilingGroup(group)
+	profiler = Profiler:GetProfilingGroup(group)
 end

 -- Forward declarations for module dependencies.
@@ -169,10 +170,13 @@ function OvaleEclipse:Update()
 end

 function OvaleEclipse:UpdateEclipse()
+	profiler.Start("OvaleEclipse_UpdateEclipse")
 	self.eclipse = API_UnitPower("player", SPELL_POWER_ECLIPSE)
+	profiler.Stop("OvaleEclipse_UpdateEclipse")
 end

 function OvaleEclipse:UpdateEclipseDirection()
+	profiler.Start("OvaleEclipse_UpdateEclipseDirection")
 	local direction = API_GetEclipseDirection()
 	if direction == "moon" then
 		self.eclipseDirection = -1
@@ -187,6 +191,7 @@ function OvaleEclipse:UpdateEclipseDirection()
 			self.eclipseDirection = 0
 		end
 	end
+	profiler.Stop("OvaleEclipse_UpdateEclipseDirection")
 end
 --</public-static-methods>

diff --git a/OvaleEnemies.lua b/OvaleEnemies.lua
index d7a2942..04184cf 100644
--- a/OvaleEnemies.lua
+++ b/OvaleEnemies.lua
@@ -19,8 +19,9 @@ Ovale.OvaleEnemies = OvaleEnemies
 local Profiler = Ovale.Profiler
 local profiler = nil
 do
-	Profiler:RegisterProfilingGroup("OvaleEnemies")
-	profiler = Profiler.group["OvaleEnemies"]
+	local group = OvaleEnemies:GetName()
+	Profiler:RegisterProfilingGroup(group)
+	profiler = Profiler:GetProfilingGroup(group)
 end

 local bit_band = bit.band
@@ -67,16 +68,17 @@ function OvaleEnemies:OnDisable()
 end

 function OvaleEnemies:COMBAT_LOG_EVENT_UNFILTERED(event, timestamp, cleuEvent, hideCaster, sourceGUID, sourceName, sourceFlags, sourceRaidFlags, destGUID, destName, destFlags, destRaidFlags, ...)
-	local now = API_GetTime()
 	if cleuEvent == "UNIT_DIED" then
 		self:RemoveEnemy(destGUID, true)
 	elseif sourceFlags and bit_band(sourceFlags, COMBATLOG_OBJECT_REACTION_HOSTILE) > 0
 			and bit_band(sourceFlags, COMBATLOG_OBJECT_AFFILIATION_OUTSIDER) > 0
 			and destFlags and bit_band(destFlags, COMBATLOG_OBJECT_AFFILIATION_OUTSIDER) == 0 then
+		local now = API_GetTime()
 		self:AddEnemy(sourceGUID, sourceName, now)
 	elseif destGUID and bit_band(destFlags, COMBATLOG_OBJECT_REACTION_HOSTILE) > 0
 			and bit_band(destFlags, COMBATLOG_OBJECT_AFFILIATION_OUTSIDER) > 0
 			and sourceFlags and bit_band(sourceFlags, COMBATLOG_OBJECT_AFFILIATION_OUTSIDER) == 0 then
+		local now = API_GetTime()
 		self:AddEnemy(destGUID, destName, now)
 	end
 end
@@ -104,35 +106,37 @@ end

 function OvaleEnemies:AddEnemy(guid, name, timestamp)
 	profiler.Start("OvaleEnemies_AddEnemy")
-	if not guid then return end
-	local seen = self.enemyLastSeen[guid]
-	self.enemyLastSeen[guid] = timestamp
-	self.enemyName[guid] = name
-	if not seen then
-		self.activeEnemies = self.activeEnemies + 1
-		Ovale:DebugPrintf(OVALE_ENEMIES_DEBUG, "New enemy (%d total): %s (%s)", self.activeEnemies, guid, name)
-		Ovale.refreshNeeded["player"] = true
+	if guid then
+		local seen = self.enemyLastSeen[guid]
+		self.enemyLastSeen[guid] = timestamp
+		self.enemyName[guid] = name
+		if not seen then
+			self.activeEnemies = self.activeEnemies + 1
+			Ovale:DebugPrintf(OVALE_ENEMIES_DEBUG, "New enemy (%d total): %s (%s)", self.activeEnemies, guid, name)
+			Ovale.refreshNeeded["player"] = true
+		end
 	end
 	profiler.Stop("OvaleEnemies_AddEnemy")
 end

 function OvaleEnemies:RemoveEnemy(guid, isDead)
 	profiler.Start("OvaleEnemies_RemoveEnemy")
-	if not guid then return end
-	local seen = self.enemyLastSeen[guid]
-	local name = self.enemyName[guid]
-	self.enemyLastSeen[guid] = nil
-	if seen then
-		if self.activeEnemies > 0 then
-			self.activeEnemies = self.activeEnemies - 1
-		end
-		if isDead then
-			Ovale:DebugPrintf(OVALE_ENEMIES_DEBUG, "Enemy died (%d total): %s (%s)", self.activeEnemies, guid, name)
-		else
-			Ovale:DebugPrintf(OVALE_ENEMIES_DEBUG, "Enemy removed (%d total): %s (%s), last seen at %f", self.activeEnemies, guid, name, seen)
+	if guid then
+		local seen = self.enemyLastSeen[guid]
+		local name = self.enemyName[guid]
+		self.enemyLastSeen[guid] = nil
+		if seen then
+			if self.activeEnemies > 0 then
+				self.activeEnemies = self.activeEnemies - 1
+			end
+			if isDead then
+				Ovale:DebugPrintf(OVALE_ENEMIES_DEBUG, "Enemy died (%d total): %s (%s)", self.activeEnemies, guid, name)
+			else
+				Ovale:DebugPrintf(OVALE_ENEMIES_DEBUG, "Enemy removed (%d total): %s (%s), last seen at %f", self.activeEnemies, guid, name, seen)
+			end
+			self:SendMessage("Ovale_InactiveUnit", guid)
+			Ovale.refreshNeeded["player"] = true
 		end
-		self:SendMessage("Ovale_InactiveUnit", guid)
-		Ovale.refreshNeeded["player"] = true
 	end
 	profiler.Stop("OvaleEnemies_RemoveEnemy")
 end
diff --git a/OvaleEquipement.lua b/OvaleEquipement.lua
index 94b59c7..c764120 100644
--- a/OvaleEquipement.lua
+++ b/OvaleEquipement.lua
@@ -17,8 +17,9 @@ Ovale.OvaleEquipement = OvaleEquipement
 local Profiler = Ovale.Profiler
 local profiler = nil
 do
-	Profiler:RegisterProfilingGroup("OvaleEquipement")
-	profiler = Profiler.group["OvaleEquipement"]
+	local group = OvaleEquipement:GetName()
+	Profiler:RegisterProfilingGroup(group)
+	profiler = Profiler:GetProfilingGroup(group)
 end

 local pairs = pairs
@@ -375,14 +376,19 @@ OvaleEquipement.offHandWeaponSpeed = nil

 --<private-static-methods>
 local function GetEquippedItemType(slotId)
+	profiler.Start("OvaleEquipement_GetEquippedItemType")
 	local itemId = OvaleEquipement:GetEquippedItem(slotId)
+	local itemType
 	if itemId then
 		local _, _, _, _, _, _, _, _, inventoryType = API_GetItemInfo(itemId)
-		return inventoryType
+		itemType = inventoryType
 	end
+	profiler.Stop("OvaleEquipement_GetEquippedItemType")
+	return itemType
 end

-function GetItemLevel(slotId)
+local function GetItemLevel(slotId)
+	profiler.Start("OvaleEquipement_GetItemLevel")
 	self_tooltip:SetInventoryItem("player", slotId)
 	local itemLevel
 	for i = 2, self_tooltip:NumLines() do
@@ -390,20 +396,27 @@ function GetItemLevel(slotId)
 		if text then
 			itemLevel = strmatch(text, OVALE_ITEM_LEVEL_PATTERN)
 			if itemLevel then
-				return tonumber(itemLevel)
+				itemLevel = tonumber(itemLevel)
+				break
 			end
 		end
 	end
+	profiler.Stop("OvaleEquipement_GetItemLevel")
+	return itemLevel
 end

 local function GetNormalizedWeaponSpeed(slotId)
+	profiler.Start("OvaleEquipement_GetNormalizedWeaponSpeed")
+	local weaponSpeed
 	if slotId == INVSLOT_MAINHAND or slotId == INVSLOT_OFFHAND then
 		local itemId = OvaleEquipement:GetEquippedItem(slotId)
 		if itemId then
 			local _, _, _, _, _, _, weaponClass = API_GetItemInfo(itemId)
-			return OVALE_NORMALIZED_WEAPON_SPEED[weaponClass]
+			weaponSpeed = OVALE_NORMALIZED_WEAPON_SPEED[weaponClass]
 		end
 	end
+	profiler.Stop("OvaleEquipement_GetNormalizedWeaponSpeed")
+	return weaponSpeed
 end
 --</private-static-methods>

diff --git a/OvaleFuture.lua b/OvaleFuture.lua
index b055676..d8c1c35 100644
--- a/OvaleFuture.lua
+++ b/OvaleFuture.lua
@@ -15,14 +15,6 @@ local OvaleFuture = Ovale:NewModule("OvaleFuture", "AceEvent-3.0")
 Ovale.OvaleFuture = OvaleFuture

 --<private-static-properties>
--- Profiling set-up.
-local Profiler = Ovale.Profiler
-local profiler = nil
-do
-	Profiler:RegisterProfilingGroup("OvaleFuture")
-	profiler = Profiler.group["OvaleFuture"]
-end
-
 local OvalePool = Ovale.OvalePool

 -- Forward declarations for module dependencies.
@@ -46,6 +38,32 @@ local API_UnitGUID = UnitGUID
 local API_UnitName = UnitName
 local MAX_COMBO_POINTS = MAX_COMBO_POINTS

+-- Profiling set-up.
+local Profiler = Ovale.Profiler
+local profiler = nil
+do
+	local group = OvaleFuture:GetName()
+
+	local function EnableProfiling()
+		API_GetTime = Profiler:Wrap(group, "OvaleFuture_API_GetTime", GetTime)
+		API_UnitCastingInfo = Profiler:Wrap(group, "OvaleFuture_API_UnitCastingInfo", UnitCastingInfo)
+		API_UnitChannelInfo = Profiler:Wrap(group, "OvaleFuture_API_UnitChannelInfo", UnitChannelInfo)
+		API_UnitGUID = Profiler:Wrap(group, "OvaleFuture_API_UnitGUID", UnitGUID)
+		API_UnitName = Profiler:Wrap(group, "OvaleFuture_API_UnitName", UnitName)
+	end
+
+	local function DisableProfiling()
+		API_GetTime = GetTime
+		API_UnitCastingInfo = UnitCastingInfo
+		API_UnitChannelInfo = UnitChannelInfo
+		API_UnitGUID = UnitGUID
+		API_UnitName = UnitName
+	end
+
+	Profiler:RegisterProfilingGroup(group, EnableProfiling, DisableProfiling)
+	profiler = Profiler:GetProfilingGroup(group)
+end
+
 -- Player's GUID.
 local self_guid = nil

diff --git a/OvalePaperDoll.lua b/OvalePaperDoll.lua
index 834d7aa..7968eff 100644
--- a/OvalePaperDoll.lua
+++ b/OvalePaperDoll.lua
@@ -18,8 +18,9 @@ Ovale.OvalePaperDoll = OvalePaperDoll
 local Profiler = Ovale.Profiler
 local profiler = nil
 do
-	Profiler:RegisterProfilingGroup("OvalePaperDoll")
-	profiler = Profiler.group["OvalePaperDoll"]
+	local group = OvalePaperDoll:GetName()
+	Profiler:RegisterProfilingGroup(group)
+	profiler = Profiler:GetProfilingGroup(group)
 end

 local OvalePool = Ovale.OvalePoolRefCount
diff --git a/OvalePool.lua b/OvalePool.lua
index bb69786..2d767a5 100644
--- a/OvalePool.lua
+++ b/OvalePool.lua
@@ -17,8 +17,9 @@ Ovale.OvalePool = OvalePool
 local Profiler = Ovale.Profiler
 local profiler = nil
 do
-	Profiler:RegisterProfilingGroup("OvalePool")
-	profiler = Profiler.group["OvalePool"]
+	local group = "OvalePool"
+	Profiler:RegisterProfilingGroup(group)
+	profiler = Profiler:GetProfilingGroup(group)
 end

 local assert = assert
diff --git a/OvalePoolRefCount.lua b/OvalePoolRefCount.lua
index c9c917b..4432b3f 100644
--- a/OvalePoolRefCount.lua
+++ b/OvalePoolRefCount.lua
@@ -28,8 +28,9 @@ Ovale.OvalePoolRefCount = OvalePoolRefCount
 local Profiler = Ovale.Profiler
 local profiler = nil
 do
-	Profiler:RegisterProfilingGroup("OvalePoolRefCount")
-	profiler = Profiler.group["OvalePoolRefCount"]
+	local group = "OvalePoolRefCount"
+	Profiler:RegisterProfilingGroup(group)
+	profiler = Profiler:GetProfilingGroup(group)
 end

 local assert = assert
diff --git a/OvalePower.lua b/OvalePower.lua
index ee70d7f..fcd1d1c 100644
--- a/OvalePower.lua
+++ b/OvalePower.lua
@@ -13,14 +13,6 @@ local OvalePower = Ovale:NewModule("OvalePower", "AceEvent-3.0")
 Ovale.OvalePower = OvalePower

 --<private-static-properties>
--- Profiling set-up.
-local Profiler = Ovale.Profiler
-local profiler = nil
-do
-	Profiler:RegisterProfilingGroup("OvalePower")
-	profiler = Profiler.group["OvalePower"]
-end
-
 -- Forward declarations for module dependencies.
 local OvaleAura = nil
 local OvaleFuture = nil
@@ -49,6 +41,24 @@ local SPELL_POWER_RUNIC_POWER = SPELL_POWER_RUNIC_POWER
 local SPELL_POWER_SHADOW_ORBS = SPELL_POWER_SHADOW_ORBS
 local SPELL_POWER_SOUL_SHARDS = SPELL_POWER_SOUL_SHARDS

+-- Profiling set-up.
+local Profiler = Ovale.Profiler
+local profiler = nil
+do
+	local group = OvalePower:GetName()
+
+	local function EnableProfiling()
+		API_GetSpellInfo = Profiler:Wrap(group, "OvalePower_API_GetSpellInfo", GetSpellInfo)
+	end
+
+	local function DisableProfiling()
+		API_GetSpellInfo = GetSpellInfo
+	end
+
+	Profiler:RegisterProfilingGroup(group, EnableProfiling, DisableProfiling)
+	profiler = Profiler:GetProfilingGroup(group)
+end
+
 -- Table of functions to update spellcast information to register with OvaleFuture.
 local self_updateSpellcastInfo = {}
 --</private-static-properties>
diff --git a/OvaleRunes.lua b/OvaleRunes.lua
index 3e1cd3f..7b3c07c 100644
--- a/OvaleRunes.lua
+++ b/OvaleRunes.lua
@@ -19,14 +19,6 @@ local OvaleRunes = Ovale:NewModule("OvaleRunes", "AceEvent-3.0")
 Ovale.OvaleRunes = OvaleRunes

 --<private-static-properties>
--- Profiling set-up.
-local Profiler = Ovale.Profiler
-local profiler = nil
-do
-	Profiler:RegisterProfilingGroup("OvaleRunes")
-	profiler = Profiler.group["OvaleRunes"]
-end
-
 -- Forward declarations for module dependencies.
 local OvaleData = nil
 local OvalePower = nil
@@ -43,6 +35,26 @@ local API_GetSpellInfo = GetSpellInfo
 local API_GetTime = GetTime
 local API_UnitClass = UnitClass

+-- Profiling set-up.
+local Profiler = Ovale.Profiler
+local profiler = nil
+do
+	local group = OvaleRunes:GetName()
+
+	local function EnableProfiling()
+		API_GetRuneCooldown = Profiler:Wrap(group, "OvaleRunes_API_GetRuneCooldown", GetRuneCooldown)
+		API_GetRuneType = Profiler:Wrap(group, "OvaleRunes_API_GetRuneType", GetRuneType)
+	end
+
+	local function DisableProfiling()
+		API_GetRuneCooldown = GetRuneCooldown
+		API_GetRuneType = GetRuneType
+	end
+
+	Profiler:RegisterProfilingGroup(group, EnableProfiling, DisableProfiling)
+	profiler = Profiler:GetProfilingGroup(group)
+end
+
 -- Player's class.
 local _, self_class = API_UnitClass("player")

diff --git a/OvaleSpellDamage.lua b/OvaleSpellDamage.lua
index 4e11813..8b570de 100644
--- a/OvaleSpellDamage.lua
+++ b/OvaleSpellDamage.lua
@@ -19,8 +19,9 @@ Ovale.OvaleSpellDamage = OvaleSpellDamage
 local Profiler = Ovale.Profiler
 local profiler = nil
 do
-	Profiler:RegisterProfilingGroup("OvaleSpellDamage")
-	profiler = Profiler.group["OvaleSpellDamage"]
+	local group = OvaleSpellDamage:GetName()
+	Profiler:RegisterProfilingGroup(group)
+	profiler = Profiler:GetProfilingGroup(group)
 end

 local API_UnitGUID = UnitGUID
diff --git a/OvaleStance.lua b/OvaleStance.lua
index 7318ccb..f39386e 100644
--- a/OvaleStance.lua
+++ b/OvaleStance.lua
@@ -14,14 +14,6 @@ local OvaleStance = Ovale:NewModule("OvaleStance", "AceEvent-3.0")
 Ovale.OvaleStance = OvaleStance

 --<private-static-properties>
--- Profiling set-up.
-local Profiler = Ovale.Profiler
-local profiler = nil
-do
-	Profiler:RegisterProfilingGroup("OvaleStance")
-	profiler = Profiler.group["OvaleStance"]
-end
-
 local ipairs = ipairs
 local pairs = pairs
 local tinsert = table.insert
@@ -32,6 +24,28 @@ local API_GetShapeshiftForm = GetShapeshiftForm
 local API_GetShapeshiftFormInfo = GetShapeshiftFormInfo
 local API_GetSpellInfo = GetSpellInfo

+-- Profiling set-up.
+local Profiler = Ovale.Profiler
+local profiler = nil
+do
+	local group = OvaleStance:GetName()
+
+	local function EnableProfiling()
+		API_GetNumShapeshiftForms = Profiler:Wrap(group, "OvaleStance_API_GetNumShapeshiftForms", GetNumShapeshiftForms)
+		API_GetShapeshiftForm = Profiler:Wrap(group, "OvaleStance_API_GetShapeshiftForm", GetShapeshiftForm)
+		API_GetShapeshiftFormInfo = Profiler:Wrap(group, "OvaleStance_API_GetShapeshiftFormInfo", GetShapeshiftFormInfo)
+	end
+
+	local function DisableProfiling()
+		API_GetNumShapeshiftForms = GetNumShapeshiftForms
+		API_GetShapeshiftForm = GetShapeshiftForm
+		API_GetShapeshiftFormInfo = GetShapeshiftFormInfo
+	end
+
+	Profiler:RegisterProfilingGroup(group, EnableProfiling, DisableProfiling)
+	profiler = Profiler:GetProfilingGroup(group)
+end
+
 local OVALE_SPELLID_TO_STANCE = {
 	-- Death Knight
 	[API_GetSpellInfo(48263)] = "deathknight_blood_presence",
diff --git a/Profiler.lua b/Profiler.lua
index e0f7e32..b60c40b 100644
--- a/Profiler.lua
+++ b/Profiler.lua
@@ -28,12 +28,10 @@ local self_stack = {}
 local self_stackSize = 0
 local self_timeSpent = {}
 local self_timesInvoked = {}
---</private-static-properties>

---<public-static-properties>
--- Registered profiling groups.
-Profiler.group = {}
---</public-static-properties>
+-- Profiling methods collections, indexed by group.
+local self_profiler = {}
+--</private-static-properties>

 --<private-static-methods>
 local function DoNothing()
@@ -80,49 +78,68 @@ end
 --</private-static-methods>

 --<public-static-methods>
-function Profiler:RegisterProfilingGroup(group)
-	self.group[group] = self.group[group] or {}
+function Profiler:RegisterProfilingGroup(group, enableFunction, disableFunction)
+	local profiler = self_profiler[group] or {}
+	profiler.Enable = enableFunction
+	profiler.Disable = disableFunction
+	self_profiler[group] = profiler
 	self:Disable(group, false)
 end

+function Profiler:GetProfilingGroup(group)
+	return self_profiler[group]
+end
+
 function Profiler:Enable(group, isVerbose)
 	if group then
-		local methods = self.group[group]
-		if methods then
+		local profiler = self_profiler[group]
+		if profiler then
 			if isVerbose then
 				Ovale:FormatPrint("Profiling for %s is enabled.", group)
 			end
-			methods.Start = StartProfiler
-			methods.Stop = StopProfiler
+			if profiler.Enable then
+				profiler.Enable()
+			end
+			profiler.Start = StartProfiler
+			profiler.Stop = StopProfiler
 		end
 	else
-		for group, methods in pairs(self.group) do
+		for group, profiler in pairs(self_profiler) do
 			if isVerbose then
 				Ovale:FormatPrint("Profiling for %s is enabled.", group)
 			end
-			methods.Start = StartProfiler
-			methods.Stop = StopProfiler
+			if profiler.Enable then
+				profiler.Enable()
+			end
+			profiler.Start = StartProfiler
+			profiler.Stop = StopProfiler
 		end
 	end
 end

 function Profiler:Disable(group, isVerbose)
 	if group then
-		local methods = self.group[group]
-		if methods then
+		local profiler = self_profiler[group]
+		if profiler then
 			if isVerbose then
 				Ovale:FormatPrint("Profiling for %s is disabled.", group)
 			end
-			methods.Start = DoNothing
-			methods.Stop = DoNothing
+			if profiler.Disable then
+				profiler.Disable()
+			end
+			profiler.Start = DoNothing
+			profiler.Stop = DoNothing
 		end
 	else
-		for group, methods in pairs(self.group) do
+		for group, profiler in pairs(self_profiler) do
 			if isVerbose then
 				Ovale:FormatPrint("Profiling for %s is disabled.", group)
 			end
-			methods.Start = DoNothing
-			methods.Stop = DoNothing
+			if profiler.Disable then
+				profiler.Disable()
+			end
+			profiler.Start = DoNothing
+			profiler.Stop = DoNothing
 		end
 	end
 end
@@ -172,6 +189,19 @@ do
 	end
 end

+function Profiler:Wrap(group, tag, functionPtr)
+	local profiler = self_profiler[group]
+	local helper = function(...)
+		profiler.Stop(tag)
+		return ...
+	end
+	local wrapper = function(...)
+		profiler.Start(tag)
+		return helper(functionPtr(...))
+	end
+	return wrapper
+end
+
 function Profiler:Debug()
 	Ovale:FormatPrint("Profiler stack size = %d", self_stackSize)
 	local index = self_stackSize