BazZziliuS

Lag Finder

Aug 9th, 2018
417
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 28.01 KB | None | 0 0
  1. -- Note: GM functions that have been modified during a test will be restored with an older version.
  2.  
  3. addon_hooks_lag_finder = addon_hooks_lag_finder or {}
  4.  
  5. local concommand_name
  6. if SERVER then
  7.     concommand_name = "find_laggy_hooks_sv"
  8.     util.AddNetworkString( "find_laggy_hooks" )
  9. else
  10.     concommand_name = "find_laggy_hooks_cl"
  11. end
  12.  
  13. local SendToSuperAdmins -- parameter set below
  14.  
  15. ---------- MESSAGE COLORS ----------
  16. local ServerColor = Color( 255, 128, 192 )
  17. local ResultColor
  18. if SERVER then
  19.     ResultColor = ServerColor
  20. else
  21.     ResultColor = Color( 255, 192, 0 )
  22. end
  23.  
  24. ---------- SEND MESSAGE ----------
  25. local function NiceMsgN( ply, ... )
  26.     if SERVER and IsValid( ply ) then
  27.         ply:PrintMessage( HUD_PRINTCONSOLE, string.Implode( "", { ... } ) )
  28.     else
  29.         MsgN( ... )
  30.     end
  31. end
  32.  
  33. ---------- FORMAT HOOK TIMINGS ----------
  34. local IntroMessage = "\n\nHere are the %u laggiest hooks found during the last %u frames:\n"..
  35.     ( SERVER and game.IsDedicated() and " Identifier ------------------------------- Impact  Execs  Mean t /f  Max time \n" or--79
  36.     " Identifier ------------------------------------------------------------ Impact  Executions  Mean t /f  Max time \n" )--113
  37. local FormatTimings
  38. do
  39.     local function FillStringSlot( Input, Slot )
  40.         Input = string.sub( Input, 1, Slot )
  41.         Input = Input..( string.rep( " ", Slot-utf8.len( Input ) ) )
  42.         return Input
  43.     end
  44.     local UnitFormats={ -- decimals+unit, multiplier
  45.         -- Greek characters are not used because UTF-8 sub-strings are not supported.
  46.         {"%01.3fus", 1000000},
  47.         {"%01.2fus", 1000000},
  48.         {"%01.1fus", 1000000},
  49.         {"%01.0fus", 1000000},
  50.         {"%01.2fms", 1000},
  51.         {"%01.1fms", 1000},
  52.         {"%01.0fms", 1000},
  53.         {"%01.2fs", 1},
  54.         {"%01.1fs", 1},
  55.         {"%01.0fs", 1},
  56.     }
  57.     local function FillTimeSlot( Input, Slot )
  58.         local NumString
  59.         for i,UnitFormat in ipairs( UnitFormats ) do
  60.             NumString=string.format( UnitFormat[1], Input*UnitFormat[2] )
  61.             if utf8.len( NumString )<=Slot then
  62.                 break
  63.             end
  64.         end
  65.         return FillStringSlot( NumString, Slot )
  66.     end
  67.     local function FillIntegerSlot( Input, Slot )
  68.         local NumString = tostring( Input )
  69.         if string.len( NumString )>Slot then
  70.             for decimals=Slot-5,0,-1 do
  71.                 NumString = string.format( "%."..decimals.."e", Input )
  72.                 if string.len( NumString )<=Slot then
  73.                     break
  74.                 end
  75.             end
  76.         end
  77.         return FillStringSlot( NumString, Slot )
  78.     end
  79.     local FormatBase = ' %s %s %s %s %s\n  in "%s" (lines %s to %s),\n  %s\n'
  80.     local Slots -- string lengths for virtual table in console
  81.     if CLIENT or !game.IsDedicated() then
  82.         Slots = {
  83.             identifier=71,
  84.             Impact=7,
  85.             count=11,
  86.             meantime=10,
  87.             maxtime=9,
  88.         }
  89.     else
  90.         Slots = {
  91.             identifier=42,
  92.             Impact=7,
  93.             count=6,
  94.             meantime=10,
  95.             maxtime=9,
  96.         }
  97.     end
  98.     function FormatTimings( identifier, Impact, count, meantime, maxtime, LuaFile, linedefined, lastlinedefined, foundstr )
  99.         return string.format( FormatBase,
  100.             FillStringSlot( identifier, Slots.identifier ),
  101.             FillStringSlot( string.format( Impact<10 and "%05.2f%%" or "%1.0f%%", Impact*100 ), Slots.Impact ),
  102.             FillIntegerSlot( count, Slots.count ),
  103.             FillTimeSlot( meantime, Slots.meantime ),
  104.             FillTimeSlot( maxtime, Slots.maxtime ),
  105.             -- FillStringSlot( string.format( "%.2e", meantime ), Slots.meantime ), -- debug
  106.             -- FillStringSlot( string.format( "%.2e", maxtime ), Slots.maxtime ), -- debug
  107.             LuaFile,
  108.             tostring( linedefined ),
  109.             tostring( lastlinedefined ),
  110.             foundstr
  111.         )
  112.     end
  113. end
  114.  
  115. ---------- NETWORK ----------
  116. if CLIENT then
  117.     local function net_ReceiveRemoteTimings()
  118.         local TopCount = net.ReadUInt( 32 )
  119.         local NumFrames = net.ReadUInt( 32 )
  120.         MsgC( ServerColor, string.format( IntroMessage, TopCount, NumFrames ) )
  121.         for k=1,TopCount do
  122.             local identifier = net.ReadString()
  123.             local Impact = net.ReadFloat()
  124.             local count = net.ReadUInt( 32 )
  125.             local meantime = net.ReadFloat()
  126.             local maxtime = net.ReadFloat()
  127.             local LuaFile = net.ReadString()
  128.             local linedefined = net.ReadUInt( 32 )
  129.             local lastlinedefined = net.ReadUInt( 32 )
  130.             local foundstr = net.ReadString()
  131.             MsgC( ServerColor, FormatTimings( identifier, Impact, count, meantime, maxtime, LuaFile, linedefined, lastlinedefined, foundstr ) )
  132.         end
  133.     end
  134.     net.Receive( "find_laggy_hooks", function()
  135.         local Type = net.ReadUInt( 8 )
  136.         if Type == 0 then
  137.             net_ReceiveRemoteTimings()
  138.         end
  139.     end )
  140. end
  141.  
  142. ---------- STORE HOOK EXECUTION TIME ----------
  143. addon_hooks_lag_finder.ReportedTimings = addon_hooks_lag_finder.ReportedTimings or {}
  144. function addon_hooks_lag_finder.ReportHookTiming( identifier, HookFunction, start, finish )
  145.     local Report = addon_hooks_lag_finder.ReportedTimings[identifier]
  146.     if !Report then
  147.         Report = {
  148.             identifier = identifier,
  149.             HookFunction = HookFunction,
  150.             count = 0,
  151.             time = 0.,
  152.             maxtime = 0.,
  153.         }
  154.         addon_hooks_lag_finder.ReportedTimings[identifier] = Report
  155.     end
  156.     local RunTime = finish-start
  157.     Report.count = Report.count+1
  158.     Report.time = Report.time+RunTime
  159.     Report.maxtime = math.max( RunTime, Report.maxtime )
  160. end
  161.  
  162. ---------- REPORT STATISTICS ----------
  163. local find_laggy_hooks -- function defined below
  164. local ReportStatistics
  165. do
  166.     local function SortReportedTimings( a, b )
  167.         return a.Impact > b.Impact
  168.     end
  169.     function ReportStatistics()
  170.         local NumFrames = addon_hooks_lag_finder.NumFrames
  171.         local NumFrames1 = NumFrames-1
  172.         local PeriodEnd = SysTime() -- true value
  173.         local PeriodDuration = PeriodEnd-addon_hooks_lag_finder.PeriodStart
  174.         -- Copy the ReportedTimings, with integer keys:
  175.         local ReportedTimings = {}
  176.         for _,Report in pairs( addon_hooks_lag_finder.ReportedTimings ) do
  177.             -- Good method but the maxtime often gets crazy values:
  178.             -- Report.Impact = (
  179.                 -- ( Report.maxtime*math.max( NumFrames, Report.count ) ) -- pessimistic: consider as always max time
  180.                 -- +Report.time -- optimistic: consider as always mean time
  181.             -- )/( 2*PeriodDuration )
  182.             -- Average method:
  183.             if Report.count>=NumFrames1 then -- considering only mean execution time
  184.                 Report.Impact = Report.time/PeriodDuration
  185.             else -- same, multiplied by NumFrames/Report.count
  186.                 Report.Impact = ( Report.time*NumFrames )/( PeriodDuration*Report.count )
  187.             end
  188.             table.insert( ReportedTimings, Report )
  189.         end
  190.         -- Sort the copied ReportedTimings:
  191.         table.sort( ReportedTimings, SortReportedTimings )
  192.         local TopCount=0 -- number of displayed values
  193.         if addon_hooks_lag_finder.TopCountOrRatioThreshold < 1 then -- use RatioThreshold
  194.             local RatioThreshold = addon_hooks_lag_finder.TopCountOrRatioThreshold
  195.             -- Only keep impacts higher than the RatioThreshold:
  196.             for k=#ReportedTimings,1,-1 do
  197.                 if ReportedTimings[k].Impact>=RatioThreshold then
  198.                     TopCount=k
  199.                     break
  200.                 end
  201.             end
  202.         else -- use TopCount
  203.             TopCount=math.min( #ReportedTimings, addon_hooks_lag_finder.TopCountOrRatioThreshold )
  204.         end
  205.         -- Clear the ReportedTimings for next capture period:
  206.         addon_hooks_lag_finder.ReportedTimings = {}
  207.         addon_hooks_lag_finder.RunningFunctions = {} -- safety precaution
  208.         addon_hooks_lag_finder.PeriodStart = PeriodEnd
  209.         addon_hooks_lag_finder.PeriodEnd = PeriodEnd+addon_hooks_lag_finder.ReportInterval
  210.         addon_hooks_lag_finder.NumFrames = 0
  211.         -- Process info:
  212.         for k=1,TopCount do
  213.             local Report = ReportedTimings[k]
  214.             Report.meantime = Report.time/NumFrames
  215.             local info = debug.getinfo( Report.HookFunction, 'S' )
  216.             Report.info = info
  217.             local LuaFile = tostring( info.short_src )
  218.             Report.LuaFile = LuaFile
  219.             local foundstr
  220.             if addon_hooks_conflict_finder and addon_hooks_conflict_finder.LocateLuaFile then
  221.                 local locations = addon_hooks_conflict_finder.LocateLuaFile( LuaFile )
  222.                 if !locations then
  223.                     foundstr = "not found in Workshop addons"
  224.                 elseif #locations == 1 then
  225.                     foundstr = "found in Workshop addon « "..locations[1].." »"
  226.                 else
  227.                     foundstr = "found in Workshop addons « "..string.Implode( " », « ", locations ).." »"
  228.                 end
  229.             else
  230.                 foundstr = "Addon hooks Conflict Finder not available"
  231.             end
  232.             Report.foundstr = foundstr
  233.         end
  234.         if CLIENT or !SendToSuperAdmins then
  235.             MsgC( ResultColor, string.format( IntroMessage, TopCount, NumFrames ) )
  236.             for k=1,TopCount do
  237.                 local Report = ReportedTimings[k]
  238.                 local info = Report.info
  239.                 MsgC( ResultColor, FormatTimings( Report.identifier, Report.Impact, Report.count, Report.meantime, Report.maxtime, Report.LuaFile, info.linedefined, info.lastlinedefined, Report.foundstr ) )
  240.             end
  241.         else
  242.             local superadmins = {}
  243.             for _,ply in pairs( player.GetAll() ) do
  244.                 if ply:IsSuperAdmin() then
  245.                     superadmins[#superadmins+1] = ply
  246.                 end
  247.             end
  248.             if #superadmins > 0 then
  249.                 net.Start( "find_laggy_hooks" )
  250.                     net.WriteUInt( 0, 8 )
  251.                     net.WriteUInt( TopCount, 32 )
  252.                     net.WriteUInt( NumFrames, 32 )
  253.                     for k=1,TopCount do
  254.                         local Report = ReportedTimings[k]
  255.                         local info = Report.info
  256.                         net.WriteString( Report.identifier )
  257.                         net.WriteFloat( Report.Impact )
  258.                         net.WriteUInt( Report.count, 32 )
  259.                         net.WriteFloat( Report.meantime )
  260.                         net.WriteFloat( Report.maxtime )
  261.                         net.WriteString( Report.LuaFile )
  262.                         net.WriteUInt( info.linedefined or 0, 32 )
  263.                         net.WriteUInt( info.lastlinedefined or 0, 32 )
  264.                         net.WriteString( Report.foundstr )
  265.                     end
  266.                 net.Send( superadmins )
  267.             else
  268.                 find_laggy_hooks( nil, concommand_name, { 0 }, concommand_name..' "0"' ) -- Stop sending to superadmins if none connected.
  269.             end
  270.         end
  271.         -- Set the period start & end again (accurate time ignoring find_laggy_hooks' process):
  272.         addon_hooks_lag_finder.PeriodStart = SysTime()
  273.         addon_hooks_lag_finder.PeriodEnd = addon_hooks_lag_finder.PeriodStart+addon_hooks_lag_finder.ReportInterval -- expected value
  274.     end
  275. end
  276. hook.Add( "Think", "find_laggy_hooks", function()
  277.     if addon_hooks_lag_finder.RunningTest then
  278.         addon_hooks_lag_finder.NumFrames = addon_hooks_lag_finder.NumFrames+1
  279.         if SysTime()>=addon_hooks_lag_finder.PeriodEnd then
  280.             ReportStatistics()
  281.         end
  282.     end
  283. end )
  284.  
  285. ---------- START TESTS ----------
  286. local helpstr = concommand_name.." [ReportInterval=30 or 0] [TopCount=16 or RatioThreshold]\n   Displays laggy hooks on every ReportInterval seconds.\n   The optional second argument can be a top TopCount of laggiest hooks or a threshold ratio of (execution_time/frame_time).\n   To stop the test, type 0 as the first argument.\n"
  287. local CancelTests
  288. if addon_hooks_lag_finder.CancelTests then
  289.     CancelTests = addon_hooks_lag_finder.CancelTests
  290. else
  291.     CancelTests = {
  292.         -- hook={},-- contains event names, hook names, functions
  293.         -- net={},-- contains message ids, functions
  294.         -- usermessage={},-- contains message ids, functions
  295.         -- ENT={},-- contains classes, event names, functions
  296.         -- SWEP={},-- contains classes, event names, functions
  297.         -- GM={},-- contains gamemodes, event names, functions
  298.     }
  299.     addon_hooks_lag_finder.CancelTests = CancelTests
  300. end
  301. local function GetModifiedHookFunction( identifier, HookFunction, ReportedFunction )
  302.     -- Do not make re-used local variables: danger in case of recursive calls.
  303.     local function new_HookFunction( ... )
  304.         if !addon_hooks_lag_finder.RunningFunctions[HookFunction] then
  305.             addon_hooks_lag_finder.RunningFunctions[HookFunction]=true
  306.             local start,ret,finish
  307.             start=SysTime()
  308.             ret={ pcall( HookFunction, ... ) } -- protected to prevent malfunction
  309.             finish=SysTime()
  310.             addon_hooks_lag_finder.RunningFunctions[HookFunction]=false
  311.             addon_hooks_lag_finder.ReportHookTiming( identifier, ReportedFunction or HookFunction, start, finish )
  312.             if ret[1] then -- ok
  313.                 table.remove( ret, 1 ) -- remove the returned status
  314.             else -- error
  315.                 error( ret[2] ) -- throw the returned error when safe
  316.             end
  317.             return unpack( ret )
  318.         else -- ignore recursive calls
  319.             return HookFunction( ... )
  320.         end
  321.     end
  322.     return new_HookFunction
  323. end
  324. function addon_hooks_lag_finder.AddModifiedHook( EventName, HookName, HookFunction, ... )
  325.     local new_HookFunction
  326.     if isfunction( HookFunction ) then
  327.         if HookName!="find_laggy_hooks" then
  328.             new_HookFunction = GetModifiedHookFunction(
  329.                 'Event "'..tostring( EventName )..'", hook "'..tostring( HookName )..'"',
  330.                 HookFunction
  331.             )
  332.             addon_hooks_lag_finder.CancelTests.hook[EventName] = addon_hooks_lag_finder.CancelTests.hook[EventName] or {}
  333.             addon_hooks_lag_finder.CancelTests.hook[EventName][HookName] = HookFunction
  334.         else
  335.             new_HookFunction = HookFunction
  336.         end
  337.     else
  338.         new_HookFunction = HookFunction
  339.     end
  340.     -- hook.Remove( EventName, HookName ) -- useless
  341.     return addon_hooks_lag_finder.hook_Add( EventName, HookName, new_HookFunction, ... )
  342. end
  343. function addon_hooks_lag_finder.AddModifiedNet( EventName, HookFunction, ... )
  344.     local new_HookFunction
  345.     local lower_EventName = tostring( EventName ):lower()
  346.     if isfunction( HookFunction ) then
  347.         if EventName!="find_laggy_hooks" then
  348.             new_HookFunction = GetModifiedHookFunction(
  349.                 'Net receiver "'..tostring( EventName )..'"',
  350.                 HookFunction
  351.             )
  352.             addon_hooks_lag_finder.CancelTests.net[lower_EventName] = HookFunction
  353.         else
  354.             new_HookFunction = HookFunction
  355.         end
  356.     else
  357.         new_HookFunction = HookFunction
  358.     end
  359.     return addon_hooks_lag_finder.net_Receive( lower_EventName, new_HookFunction, ... )
  360. end
  361. function addon_hooks_lag_finder.AddModifiedUsermessage( EventName, HookFunction, ... )
  362.     local new_HookFunction
  363.     if isfunction( HookFunction ) then
  364.         if EventName!="find_laggy_hooks" then
  365.             new_HookFunction = GetModifiedHookFunction(
  366.                 'Usermessage receiver "'..tostring( EventName )..'"',
  367.                 HookFunction
  368.             )
  369.             addon_hooks_lag_finder.CancelTests.usermessage[EventName] = {
  370.                 Function = HookFunction,
  371.                 PreArgs = {...},
  372.             }
  373.         else
  374.             new_HookFunction = HookFunction
  375.         end
  376.     else
  377.         new_HookFunction = HookFunction
  378.     end
  379.     return addon_hooks_lag_finder.usermessage_Hook( EventName, new_HookFunction, ... )
  380. end
  381. function addon_hooks_lag_finder.AdjustModifiedTimer( EventName, delay, repetitions, HookFunction, ... )
  382.     local new_HookFunction
  383.     if isfunction( HookFunction ) then
  384.         local new_HookFunction2 = GetModifiedHookFunction(
  385.             'Timer "'..tostring( EventName )..'"',
  386.             HookFunction
  387.         )
  388.         new_HookFunction = function( ... )
  389.             if addon_hooks_lag_finder.RunningTest then
  390.                 return new_HookFunction2( ... )
  391.             else
  392.                 return HookFunction( ... )
  393.             end
  394.         end
  395.     else
  396.         new_HookFunction = HookFunction
  397.     end
  398.     return addon_hooks_lag_finder.timer_Adjust( EventName, delay, repetitions, new_HookFunction, ... )
  399. end
  400. function addon_hooks_lag_finder.AddModifiedTimer( EventName, delay, repetitions, HookFunction, ... )
  401.     local new_HookFunction
  402.     if isfunction( HookFunction ) then
  403.         local new_HookFunction2 = GetModifiedHookFunction(
  404.             'Timer "'..tostring( EventName )..'"',
  405.             HookFunction
  406.         )
  407.         new_HookFunction = function( ... )
  408.             if addon_hooks_lag_finder.RunningTest then
  409.                 return new_HookFunction2( ... )
  410.             else
  411.                 return HookFunction( ... )
  412.             end
  413.         end
  414.     else
  415.         new_HookFunction = HookFunction
  416.     end
  417.     return addon_hooks_lag_finder.timer_Create( EventName, delay, repetitions, new_HookFunction, ... )
  418. end
  419. function addon_hooks_lag_finder.AdjustModifiedTimerSimple( delay, HookFunction, ... )
  420.     local new_HookFunction
  421.     if isfunction( HookFunction ) then
  422.         local new_HookFunction2 = GetModifiedHookFunction(
  423.             'Timer timer.Simple( '..tostring( delay )..', '..tostring( HookFunction )..' )',
  424.             HookFunction
  425.         )
  426.         new_HookFunction = function( ... )
  427.             if addon_hooks_lag_finder.RunningTest then
  428.                 return new_HookFunction2( ... )
  429.             else
  430.                 return HookFunction( ... )
  431.             end
  432.         end
  433.     else
  434.         new_HookFunction = HookFunction
  435.     end
  436.     return addon_hooks_lag_finder.timer_Simple( delay, new_HookFunction, ... )
  437. end
  438. -- Modify timer.Adjust():
  439. if !addon_hooks_lag_finder.timer_Adjust then
  440.     addon_hooks_lag_finder.timer_Adjust = timer.Adjust
  441.     timer.Adjust = function( EventName, delay, repetitions, HookFunction, ... )
  442.         return addon_hooks_lag_finder.AdjustModifiedTimer( EventName, delay, repetitions, HookFunction, ... )
  443.     end
  444. end
  445. -- Modify timer.Create():
  446. if !addon_hooks_lag_finder.timer_Create then
  447.     addon_hooks_lag_finder.timer_Create = timer.Create
  448.     timer.Create = function( EventName, delay, repetitions, HookFunction, ... )
  449.         return addon_hooks_lag_finder.AddModifiedTimer( EventName, delay, repetitions, HookFunction, ... )
  450.     end
  451. end
  452. -- Modify timer.Simple():
  453. if !addon_hooks_lag_finder.timer_Simple then
  454.     addon_hooks_lag_finder.timer_Simple = timer.Simple
  455.     timer.Simple = function( delay, HookFunction, ... )
  456.         return addon_hooks_lag_finder.AdjustModifiedTimerSimple( delay, HookFunction, ... )
  457.     end
  458. end
  459. -- Run or stop the test:
  460. function find_laggy_hooks( ply, cmd, args, fullstring )
  461.     if CLIENT or !IsValid( ply ) or ply:IsSuperAdmin() then
  462.         local ReportInterval = tonumber( args[1] ) or 30
  463.         if !ReportInterval then
  464.             NiceMsgN( ply, " - "..helpstr )
  465.             return
  466.         end
  467.         if addon_hooks_conflict_finder and addon_hooks_conflict_finder.FillAddonsList then
  468.             if !addon_hooks_conflict_finder.AddonsList then
  469.                 addon_hooks_conflict_finder.FillAddonsList()
  470.             end
  471.         end
  472.         local IsRunning = addon_hooks_lag_finder.RunningTest
  473.         local HookTable = hook.GetTable() -- This may be a copy!
  474.         local NetTable = net.Receivers
  475.         local UsermessageTable = usermessage.GetTable()
  476.         local ENTTable = scripted_ents.GetList()
  477.         local SWEPTable = weapons.GetList()
  478.         if ReportInterval>0 then -- begin operation
  479.             if !IsRunning then
  480.                 if IsValid( ply ) then
  481.                     SendToSuperAdmins = true
  482.                 else
  483.                     SendToSuperAdmins = false
  484.                 end
  485.                 -- Modify hook.Add():
  486.                 if !addon_hooks_lag_finder.hook_Add then
  487.                     addon_hooks_lag_finder.hook_Add = hook.Add
  488.                     hook.Add = function( EventName, HookName, HookFunction, ... )
  489.                         if addon_hooks_lag_finder.RunningTest and HookName!="find_laggy_hooks" then
  490.                             return addon_hooks_lag_finder.AddModifiedHook( EventName, HookName, HookFunction, ... )
  491.                         else
  492.                             return addon_hooks_lag_finder.hook_Add( EventName, HookName, HookFunction, ... )
  493.                         end
  494.                     end
  495.                 end
  496.                 -- Modify net.Receive():
  497.                 if !addon_hooks_lag_finder.net_Receive then
  498.                     addon_hooks_lag_finder.net_Receive = net.Receive
  499.                     net.Receive = function( EventName, HookFunction, ... )
  500.                         if addon_hooks_lag_finder.RunningTest and EventName!="find_laggy_hooks" then
  501.                             return addon_hooks_lag_finder.AddModifiedNet( EventName, HookFunction, ... )
  502.                         else
  503.                             return addon_hooks_lag_finder.net_Receive( EventName, HookFunction, ... )
  504.                         end
  505.                     end
  506.                 end
  507.                 -- Modify usermessage.Hook():
  508.                 if !addon_hooks_lag_finder.usermessage_Hook then
  509.                     addon_hooks_lag_finder.usermessage_Hook = usermessage.Hook
  510.                     usermessage.Hook = function( EventName, HookFunction, ... )
  511.                         if addon_hooks_lag_finder.RunningTest and EventName!="find_laggy_hooks" then
  512.                             return addon_hooks_lag_finder.AddModifiedUsermessage( EventName, HookFunction, ... )
  513.                         else
  514.                             return addon_hooks_lag_finder.usermessage_Hook( EventName, HookFunction, ... )
  515.                         end
  516.                     end
  517.                 end
  518.                 -- Mark test as running:
  519.                 addon_hooks_lag_finder.ReportedTimings = {}
  520.                 addon_hooks_lag_finder.RunningFunctions = {}
  521.                 addon_hooks_lag_finder.PeriodStart = SysTime()
  522.                 addon_hooks_lag_finder.PeriodEnd = addon_hooks_lag_finder.PeriodStart+ReportInterval
  523.                 addon_hooks_lag_finder.NumFrames = 0
  524.                 addon_hooks_lag_finder.TopCountOrRatioThreshold = tonumber( args[2] ) or 16
  525.                 addon_hooks_lag_finder.ReportInterval = ReportInterval
  526.                 addon_hooks_lag_finder.RunningTest = true
  527.                 -- Add hooks:
  528.                 CancelTests.hook={}
  529.                 do
  530.                     local TempHookTable = {} -- Copy: ensure that pairs() is safe while modifying table.
  531.                     for EventName,EventFunctions in pairs( HookTable ) do
  532.                         TempHookTable[EventName]={}
  533.                         for HookName,HookFunction in pairs( EventFunctions ) do
  534.                             TempHookTable[EventName][HookName]=HookFunction
  535.                         end
  536.                     end
  537.                     for EventName,EventFunctions in pairs( TempHookTable ) do
  538.                         for HookName,HookFunction in pairs( EventFunctions ) do
  539.                             pcall( addon_hooks_lag_finder.AddModifiedHook, EventName, HookName, HookFunction )
  540.                         end
  541.                     end
  542.                 end
  543.                 -- Add net receivers:
  544.                 CancelTests.net={}
  545.                 do
  546.                     local TempNetTable = {} -- Copy: ensure that pairs() is safe while modifying table.
  547.                     for EventName,HookFunction in pairs( NetTable ) do
  548.                         TempNetTable[EventName]=HookFunction
  549.                     end
  550.                     for EventName,HookFunction in pairs( TempNetTable ) do
  551.                         pcall( addon_hooks_lag_finder.AddModifiedNet, EventName, HookFunction )
  552.                     end
  553.                 end
  554.                 -- Add usermessage receivers:
  555.                 CancelTests.usermessage={}
  556.                 do
  557.                     local TempUsermessageTable = {} -- Copy: ensure that pairs() is safe while modifying table.
  558.                     for EventName,HookData in pairs( UsermessageTable ) do
  559.                         TempUsermessageTable[EventName]=HookData
  560.                     end
  561.                     for EventName,HookData in pairs( TempUsermessageTable ) do
  562.                         pcall( addon_hooks_lag_finder.AddModifiedUsermessage, EventName, HookData.Function, unpack( HookData.PreArgs ) )
  563.                     end
  564.                 end
  565.                 -- Add ENT functions:
  566.                 CancelTests.ENT={}
  567.                 do
  568.                     local TempENTTable = {} -- Copy: ensure that pairs() is safe while modifying table.
  569.                     for class,ClassTable in pairs( ENTTable ) do
  570.                         TempENTTable[class]={}
  571.                         for EventName,HookFunction in pairs( ClassTable.t ) do
  572.                             if isfunction( HookFunction ) then
  573.                                 TempENTTable[class][EventName]=HookFunction
  574.                             end
  575.                         end
  576.                     end
  577.                     for class,ClassFunctions in pairs( TempENTTable ) do
  578.                         CancelTests.ENT[class] = {}
  579.                         local ENT = scripted_ents.GetStored( class ).t
  580.                         for EventName,HookFunction in pairs( ClassFunctions ) do
  581.                             local new_HookFunction = GetModifiedHookFunction(
  582.                                 'Entity "'..tostring( class )..'", function ENT.'..tostring( EventName ),
  583.                                 HookFunction
  584.                             )
  585.                             CancelTests.ENT[class][EventName] = HookFunction
  586.                             ENT[EventName] = new_HookFunction
  587.                         end
  588.                         pcall( scripted_ents.Register, ENT, class ) -- should not be required
  589.                     end
  590.                 end
  591.                 -- Add SWEP functions:
  592.                 CancelTests.SWEP={}
  593.                 do
  594.                     local TempSWEPTable = {} -- Copy: ensure that pairs() is safe while modifying table.
  595.                     for _,SWEP in pairs( SWEPTable ) do
  596.                         local class = SWEP.ClassName
  597.                         if class then
  598.                             TempSWEPTable[class]={}
  599.                             for EventName,HookFunction in pairs( SWEP ) do
  600.                                 if isfunction( HookFunction ) then
  601.                                     TempSWEPTable[class][EventName]=HookFunction
  602.                                 end
  603.                             end
  604.                         end
  605.                     end
  606.                     for class,ClassFunctions in pairs( TempSWEPTable ) do
  607.                         CancelTests.SWEP[class] = {}
  608.                         local SWEP = weapons.GetStored( class )
  609.                         for EventName,HookFunction in pairs( ClassFunctions ) do
  610.                             local new_HookFunction = GetModifiedHookFunction(
  611.                                 'Weapon "'..tostring( class )..'", function SWEP.'..tostring( EventName ),
  612.                                 HookFunction
  613.                             )
  614.                             CancelTests.SWEP[class][EventName] = HookFunction
  615.                             SWEP[EventName] = new_HookFunction
  616.                         end
  617.                         pcall( weapons.Register, SWEP, class ) -- should not be required
  618.                     end
  619.                 end
  620.                 -- Add GM functions:
  621.                 CancelTests.GM={}
  622.                 do
  623.                     local Validated={} -- Prevent endless loops.
  624.                     local TempGamemodeTable = {} -- Copy: ensure that pairs() is safe while modifying table and prevent risk of multiply modified functions.
  625.                     local GM = GAMEMODE
  626.                     while GM and !Validated[GM] do
  627.                         TempGamemodeTable[GM]={}
  628.                         for EventName,HookFunction in pairs( GM ) do
  629.                             if isfunction( HookFunction ) then
  630.                                 TempGamemodeTable[GM][EventName]=HookFunction
  631.                             end
  632.                         end
  633.                         Validated[GM]=true
  634.                         GM = GM.BaseClass
  635.                     end
  636.                     for GM,GM_functions in pairs( TempGamemodeTable ) do
  637.                         CancelTests.GM[GM] = {}
  638.                         for EventName,HookFunction in pairs( GM_functions ) do
  639.                             local new_HookFunction
  640.                             if CLIENT and EventName=="RenderScene" then
  641.                                 addon_hooks_lag_finder.view = {
  642.                                     dopostprocess = true,
  643.                                     drawhud = true,
  644.                                     drawmonitors = true,
  645.                                     drawviewmodel = true,
  646.                                     bloomtone = true,
  647.                                 }
  648.                                 local function RenderScene( ... )
  649.                                     local result = HookFunction( ... )
  650.                                     if result then
  651.                                         return result
  652.                                     else
  653.                                         render.RenderView( addon_hooks_lag_finder.view )
  654.                                         return true
  655.                                     end
  656.                                 end
  657.                                 new_HookFunction = GetModifiedHookFunction(
  658.                                     'Default scene rendering / GM.RenderScene',
  659.                                     RenderScene,
  660.                                     HookFunction
  661.                                 )
  662.                             else
  663.                                 new_HookFunction = GetModifiedHookFunction(
  664.                                     'Function GM.'..tostring( EventName ),
  665.                                     HookFunction
  666.                                 )
  667.                             end
  668.                             CancelTests.GM[GM][EventName] = HookFunction -- to be restored first
  669.                             GM[EventName] = new_HookFunction
  670.                         end
  671.                     end
  672.                 end
  673.                 -- Re-adjust to accurate time reference:
  674.                 addon_hooks_lag_finder.PeriodStart = SysTime()
  675.                 addon_hooks_lag_finder.PeriodEnd = addon_hooks_lag_finder.PeriodStart+ReportInterval
  676.                 NiceMsgN( ply, 'Lag test started, next output in '..ReportInterval..' seconds!' )
  677.             else
  678.                 NiceMsgN( ply, 'Lag test is already running!' )
  679.             end
  680.         else -- finish operation
  681.             -- We only browse cancel tables because they are copies!
  682.             if IsRunning then
  683.                 -- Restore GM functions:
  684.                 if CancelTests.GM then
  685.                     local Validated={} -- Prevent endless loops.
  686.                     local GM = GAMEMODE
  687.                     while GM and !Validated[GM] do
  688.                         if CancelTests.GM[GM] then
  689.                             for EventName,HookFunction in pairs( CancelTests.GM[GM] ) do
  690.                                 if isfunction( GM[EventName] ) then
  691.                                     GM[EventName]=HookFunction
  692.                                 end
  693.                             end
  694.                         end
  695.                         CancelTests.GM[GM]=nil
  696.                         Validated[GM]=true
  697.                         GM = GM.BaseClass
  698.                     end
  699.                     CancelTests.GM=nil
  700.                 end
  701.                 -- Restore ENT functions:
  702.                 if CancelTests.ENT then
  703.                     for class,ClassFunctions in pairs( CancelTests.ENT ) do
  704.                         local ENT = scripted_ents.GetStored( class ).t
  705.                         if ENT then
  706.                             for EventName,HookFunction in pairs( ClassFunctions ) do
  707.                                 if isfunction( ENT[EventName] ) then
  708.                                     ENT[EventName]=HookFunction
  709.                                 end
  710.                             end
  711.                             pcall( scripted_ents.Register, ENT, class ) -- should not be required
  712.                         end
  713.                     end
  714.                     CancelTests.ENT=nil
  715.                 end
  716.                 -- Restore SWEP functions:
  717.                 if CancelTests.SWEP then
  718.                     for class,ClassFunctions in pairs( CancelTests.SWEP ) do
  719.                         local SWEP = weapons.GetStored( class )
  720.                         if SWEP then
  721.                             for EventName,HookFunction in pairs( ClassFunctions ) do
  722.                                 if isfunction( SWEP[EventName] ) then
  723.                                     SWEP[EventName]=HookFunction
  724.                                 end
  725.                             end
  726.                             pcall( scripted_ents.Register, SWEP, class ) -- should not be required
  727.                         end
  728.                     end
  729.                     CancelTests.SWEP=nil
  730.                 end
  731.                 -- Restore usermessage receivers:
  732.                 if CancelTests.usermessage then
  733.                     for EventName,HookData in pairs( CancelTests.usermessage ) do
  734.                         if istable( UsermessageTable[EventName] ) then
  735.                             addon_hooks_lag_finder.usermessage_Hook( EventName, HookData.Function, unpack( HookData.PreArgs ) )
  736.                         end
  737.                     end
  738.                     CancelTests.usermessage=nil
  739.                 end
  740.                 -- Restore net receivers:
  741.                 if CancelTests.net then
  742.                     for EventName,HookFunction in pairs( CancelTests.net ) do
  743.                         if isfunction( NetTable[EventName] ) then
  744.                             addon_hooks_lag_finder.net_Receive( EventName, HookFunction )
  745.                         end
  746.                     end
  747.                     CancelTests.net=nil
  748.                 end
  749.                 -- Restore hooks:
  750.                 if CancelTests.hook then
  751.                     for EventName,EventFunctions in pairs( CancelTests.hook ) do
  752.                         if istable( HookTable[EventName] ) then
  753.                             for HookName,HookFunction in pairs( EventFunctions ) do
  754.                                 if isfunction( HookTable[EventName][HookName] ) then
  755.                                     addon_hooks_lag_finder.hook_Add( EventName, HookName, HookFunction )
  756.                                 end
  757.                             end
  758.                         end
  759.                     end
  760.                     CancelTests.hook=nil
  761.                 end
  762.                 -- Mark test as stopped:
  763.                 addon_hooks_lag_finder.RunningTest = false
  764.                 -- Report pending statistics:
  765.                 ReportStatistics()
  766.                 addon_hooks_lag_finder.ReportedTimings = {}
  767.                 addon_hooks_lag_finder.RunningFunctions = {}
  768.                 -- Confirm:
  769.                 NiceMsgN( ply, 'Lag test is now stopped!' )
  770.             else
  771.                 NiceMsgN( ply, 'Lag test is not running!' )
  772.             end
  773.         end
  774.     end
  775. end
  776. concommand.Add( concommand_name, find_laggy_hooks, nil, helpstr, 0 )
Add Comment
Please, Sign In to add comment