228 lines
6.8 KiB
Lua
228 lines
6.8 KiB
Lua
local get, update, onUpdate = FProfiler.UI.getModelValue, FProfiler.UI.updateModel, FProfiler.UI.onModelUpdate
|
|
|
|
|
|
--[[-------------------------------------------------------------------------
|
|
Update the current selected focus object when data is entered
|
|
---------------------------------------------------------------------------]]
|
|
onUpdate({"server", "focusStr"}, function(new)
|
|
if not new or get({"server", "fromServer"}) then return end
|
|
|
|
net.Start("FProfile_focusObj")
|
|
net.WriteString(new)
|
|
net.SendToServer()
|
|
end)
|
|
|
|
net.Receive("FProfile_focusObj", function()
|
|
update({"server", "focusObj"}, net.ReadBool() and get({"server", "focusStr"}) or nil)
|
|
end)
|
|
|
|
-- A focus update occurs when someone else changes the focus
|
|
net.Receive("FProfile_focusUpdate", function()
|
|
update({"server", "fromServer"}, true)
|
|
|
|
local focusStr = net.ReadString()
|
|
update({"server", "focusStr"}, focusStr)
|
|
update({"server", "focusObj"}, net.ReadBool() and focusStr or nil)
|
|
|
|
update({"server", "fromServer"}, false)
|
|
end)
|
|
|
|
--[[-------------------------------------------------------------------------
|
|
(Re)start profiling
|
|
---------------------------------------------------------------------------]]
|
|
local function restartProfiling()
|
|
local shouldReset = get({"server", "shouldReset"})
|
|
|
|
net.Start("FProfile_startProfiling")
|
|
net.WriteBool(shouldReset)
|
|
net.SendToServer()
|
|
end
|
|
|
|
net.Receive("FProfile_startProfiling", function()
|
|
update({"server", "fromServer"}, true)
|
|
update({"server", "status"}, "Started")
|
|
update({"server", "recordTime"}, net.ReadDouble())
|
|
update({"server", "sessionStart"}, net.ReadDouble())
|
|
update({"server", "fromServer"}, false)
|
|
end)
|
|
|
|
|
|
--[[-------------------------------------------------------------------------
|
|
Stop profiling
|
|
---------------------------------------------------------------------------]]
|
|
local function stopProfiling()
|
|
net.Start("FProfile_stopProfiling")
|
|
net.SendToServer()
|
|
end
|
|
|
|
-- Read a row from a net message
|
|
local function readDataRow(countSize, readSpecific)
|
|
local res = {}
|
|
|
|
local count = net.ReadUInt(countSize)
|
|
|
|
for i = 1, count do
|
|
local row = {}
|
|
row.info = {}
|
|
|
|
row.func = net.ReadString()
|
|
row.info.short_src = net.ReadString()
|
|
row.info.linedefined = net.ReadUInt(16)
|
|
row.info.lastlinedefined = net.ReadUInt(16)
|
|
|
|
readSpecific(row)
|
|
|
|
table.insert(res, row)
|
|
end
|
|
|
|
return res
|
|
end
|
|
|
|
-- Read a bottleneck row
|
|
local function readBottleneckRow(row)
|
|
local nameCount = net.ReadUInt(8)
|
|
|
|
row.names = {}
|
|
for i = 1, nameCount do
|
|
table.insert(row.names, {
|
|
name = net.ReadString(),
|
|
namewhat = net.ReadString()
|
|
})
|
|
end
|
|
|
|
row.total_called = net.ReadUInt(32)
|
|
row.total_time = net.ReadDouble()
|
|
row.average_time = net.ReadDouble()
|
|
end
|
|
|
|
-- Read the top n row
|
|
local function readTopNRow(row)
|
|
row.info.name = net.ReadString()
|
|
row.info.namewhat = net.ReadString()
|
|
row.runtime = net.ReadDouble()
|
|
end
|
|
|
|
net.Receive("FProfile_stopProfiling", function()
|
|
update({"server", "fromServer"}, true)
|
|
update({"server", "status"}, "Stopped")
|
|
update({"server", "sessionStart"}, nil)
|
|
update({"server", "recordTime"}, net.ReadDouble())
|
|
|
|
update({"server", "bottlenecks"}, readDataRow(16, readBottleneckRow))
|
|
update({"server", "topLagSpikes"}, readDataRow(8, readTopNRow))
|
|
update({"server", "fromServer"}, false)
|
|
end)
|
|
|
|
|
|
--[[-------------------------------------------------------------------------
|
|
Start/stop recording when the recording status is changed
|
|
---------------------------------------------------------------------------]]
|
|
onUpdate({"server", "status"}, function(new, old)
|
|
if new == old or get({"server", "fromServer"}) then return end
|
|
(new == "Started" and restartProfiling or stopProfiling)()
|
|
end)
|
|
|
|
|
|
--[[-------------------------------------------------------------------------
|
|
Update info when a different line is selected
|
|
---------------------------------------------------------------------------]]
|
|
onUpdate({"server", "currentSelected"}, function(new)
|
|
if not new or not new.info or not new.info.linedefined or not new.info.lastlinedefined or not new.info.short_src then return end
|
|
|
|
-- net.Start("FProfile_getSource")
|
|
-- net.WriteString(tostring(new.func))
|
|
-- net.SendToServer()
|
|
end)
|
|
|
|
net.Receive("FProfile_getSource", function()
|
|
update({"server", "sourceText"}, net.ReadString())
|
|
end)
|
|
|
|
|
|
--[[-------------------------------------------------------------------------
|
|
When a function is to be printed to console
|
|
---------------------------------------------------------------------------]]
|
|
onUpdate({"server", "toConsole"}, function(data)
|
|
if not data then return end
|
|
|
|
update({"server", "toConsole"}, nil)
|
|
|
|
-- net.Start("FProfile_printFunction")
|
|
-- net.WriteBool(data.total_called and true or false) -- true for bottleneck function, false for top-n function
|
|
-- net.WriteString(tostring(data.func))
|
|
-- net.SendToServer()
|
|
end)
|
|
|
|
net.Receive("FProfile_printFunction", function(len)
|
|
local data = net.ReadData(len)
|
|
local decompressed = util.Decompress(data)
|
|
|
|
-- Print the text line by line, otherwise big parts of big data will not be printed
|
|
local split = string.Explode("\n", decompressed, false)
|
|
for _, line in ipairs(split) do
|
|
MsgN(line)
|
|
end
|
|
|
|
-- Write the thing to a file
|
|
file.CreateDir("fprofiler")
|
|
file.Write("fprofiler/profiledata.txt", showStr(data))
|
|
MsgC(Color(200, 200, 200), "-----", Color(120, 120, 255), "NOTE", Color(200, 200, 200), "---------------\n")
|
|
MsgC(Color(200, 200, 200), "In the server's console you can find a colour coded version of the above output.\nIf the above function does not fit in console, you can find it in data/fprofiler/profiledata.txt\n\n")
|
|
end)
|
|
|
|
|
|
--[[-------------------------------------------------------------------------
|
|
Check access when the frame opens
|
|
Also request a full serverside model update
|
|
---------------------------------------------------------------------------]]
|
|
onUpdate("frameVisible", function(isOpen)
|
|
-- Don't network if the server doesn't have FProfiler installed
|
|
if util.NetworkStringToID("FProfile_fullModelUpdate") == 0 then
|
|
update("serverAccess", false)
|
|
return
|
|
end
|
|
|
|
-- Update access
|
|
CAMI.PlayerHasAccess(LocalPlayer(), "FProfiler", function(b, _)
|
|
update("serverAccess", b)
|
|
end)
|
|
|
|
if not isOpen then
|
|
net.Start("FProfile_unsubscribe")
|
|
net.SendToServer()
|
|
|
|
return
|
|
end
|
|
|
|
net.Start("FProfile_fullModelUpdate")
|
|
net.SendToServer()
|
|
end)
|
|
|
|
|
|
net.Receive("FProfile_fullModelUpdate", function()
|
|
update({"server", "fromServer"}, true)
|
|
|
|
local focusExists = net.ReadBool()
|
|
if focusExists then
|
|
local focus = net.ReadString()
|
|
update({"server", "focusObj"}, focus)
|
|
update({"server", "focusStr"}, focus)
|
|
end
|
|
|
|
local startingTimeExists = net.ReadBool()
|
|
|
|
if startingTimeExists then
|
|
update({"server", "status"}, "Started")
|
|
update({"server", "sessionStart"}, net.ReadDouble())
|
|
else
|
|
update({"server", "status"}, "Stopped")
|
|
end
|
|
|
|
update({"server", "recordTime"}, net.ReadDouble())
|
|
|
|
update({"server", "bottlenecks"}, readDataRow(16, readBottleneckRow))
|
|
update({"server", "topLagSpikes"}, readDataRow(8, readTopNRow))
|
|
|
|
update({"server", "fromServer"}, false)
|
|
end)
|
|
|