173 lines
4.2 KiB
SourcePawn
173 lines
4.2 KiB
SourcePawn
|
#define PLUGIN_VERSION "1.1"
|
||
|
|
||
|
#pragma semicolon 1
|
||
|
#pragma newdecls required
|
||
|
|
||
|
#include <sourcemod>
|
||
|
|
||
|
public Plugin myinfo =
|
||
|
{
|
||
|
name = "[ANY] [Debugger] Valve Profiler",
|
||
|
description = "Measures per-plugin performance and provides a log with various counters",
|
||
|
author = "Alex Dragokas",
|
||
|
version = PLUGIN_VERSION,
|
||
|
url = "https://github.com/dragokas/"
|
||
|
};
|
||
|
|
||
|
/*
|
||
|
Commands:
|
||
|
|
||
|
- sm_debug - Start / stop vprof debug tracing
|
||
|
|
||
|
Logfile:
|
||
|
|
||
|
- addons/sourcemod/logs/profiler__<DATE>_<TIME>.log
|
||
|
|
||
|
For details of implementation see also:
|
||
|
https://github.com/alliedmodders/sourcemod/issues/1162
|
||
|
*/
|
||
|
|
||
|
const float LOG_MAX_WAITTIME = 60.0;
|
||
|
const float LOG_CHECK_INTERVAL = 5.0;
|
||
|
|
||
|
char g_PathPrefix[PLATFORM_MAX_PATH],
|
||
|
g_PathOrig[PLATFORM_MAX_PATH],
|
||
|
g_PathProfilerLog[PLATFORM_MAX_PATH],
|
||
|
g_PathCosole[] = "console.log";
|
||
|
ConVar g_CVarLogFile;
|
||
|
Handle g_hTimer;
|
||
|
bool g_bL4D2;
|
||
|
int g_ptrFile;
|
||
|
|
||
|
public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max)
|
||
|
{
|
||
|
g_bL4D2 = (GetEngineVersion() == Engine_Left4Dead2);
|
||
|
return APLRes_Success;
|
||
|
}
|
||
|
|
||
|
public void OnPluginStart()
|
||
|
{
|
||
|
CreateConVar("sm_prof_version", PLUGIN_VERSION, "Plugin Version", FCVAR_NOTIFY | FCVAR_DONTRECORD);
|
||
|
g_CVarLogFile = FindConVar("con_logfile");
|
||
|
|
||
|
RegAdminCmd("sm_debug", Cmd_Debug, ADMFLAG_ROOT, "Start / stop the valve profiler");
|
||
|
|
||
|
BuildPath(Path_SM, g_PathPrefix, sizeof(g_PathPrefix), "logs/profiler_");
|
||
|
}
|
||
|
|
||
|
public void OnConfigsExecuted()
|
||
|
{
|
||
|
g_CVarLogFile.GetString(g_PathOrig, sizeof(g_PathOrig));
|
||
|
}
|
||
|
|
||
|
public Action Cmd_Debug(int client, int args)
|
||
|
{
|
||
|
static bool start;
|
||
|
char sTime[32];
|
||
|
|
||
|
if( !start )
|
||
|
{
|
||
|
delete g_hTimer;
|
||
|
|
||
|
FormatTime(sTime, sizeof(sTime), "%F_%H-%M-%S", GetTime());
|
||
|
FormatEx(g_PathProfilerLog, sizeof(g_PathProfilerLog), "%s_%s.log", g_PathPrefix, sTime);
|
||
|
|
||
|
if( g_bL4D2 )
|
||
|
{
|
||
|
g_ptrFile = FileSize(g_PathCosole);
|
||
|
}
|
||
|
else {
|
||
|
SetCvarSilent(g_CVarLogFile, g_PathProfilerLog);
|
||
|
}
|
||
|
|
||
|
ReplyToCommand(client, "\x04[START]\x05 Profiler is started...");
|
||
|
ServerCommand("vprof_on");
|
||
|
ServerExecute();
|
||
|
RequestFrame(OnFrameDelay);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ServerCommand("sm prof stop vprof");
|
||
|
ServerCommand("sm prof dump vprof");
|
||
|
ServerCommand("vprof_off");
|
||
|
ReplyToCommand(client, "\x04[STOP]\x05 Saving profiler log to: %s", g_PathProfilerLog);
|
||
|
|
||
|
// Profiler needs some time for analysis
|
||
|
|
||
|
if( g_bL4D2 )
|
||
|
{
|
||
|
// L4D2 has bugged con_logfile: https://github.com/ValveSoftware/Source-1-Games/issues/3601
|
||
|
g_hTimer = CreateTimer(LOG_CHECK_INTERVAL, Timer_MirrorLog, 1);
|
||
|
}
|
||
|
else {
|
||
|
g_hTimer = CreateTimer(LOG_MAX_WAITTIME, Timer_RestoreCvar);
|
||
|
}
|
||
|
}
|
||
|
start = !start;
|
||
|
return Plugin_Handled;
|
||
|
}
|
||
|
|
||
|
public void OnFrameDelay()
|
||
|
{
|
||
|
ServerCommand("sm prof start vprof");
|
||
|
}
|
||
|
|
||
|
void SetCvarSilent(ConVar cvar, char[] value)
|
||
|
{
|
||
|
int flags = cvar.Flags;
|
||
|
cvar.Flags &= ~ FCVAR_NOTIFY;
|
||
|
cvar.SetString(value);
|
||
|
cvar.Flags = flags;
|
||
|
}
|
||
|
|
||
|
public Action Timer_RestoreCvar(Handle timer)
|
||
|
{
|
||
|
SetCvarSilent(g_CVarLogFile, g_PathOrig);
|
||
|
g_hTimer = null;
|
||
|
return Plugin_Handled;
|
||
|
}
|
||
|
|
||
|
public Action Timer_MirrorLog(Handle timer, int init)
|
||
|
{
|
||
|
static float sec;
|
||
|
|
||
|
if( init ) sec = 0.0;
|
||
|
sec += LOG_CHECK_INTERVAL;
|
||
|
|
||
|
if( sec > LOG_MAX_WAITTIME )
|
||
|
{
|
||
|
g_hTimer = null;
|
||
|
return Plugin_Handled;
|
||
|
}
|
||
|
if( FileSize(g_PathCosole) != g_ptrFile )
|
||
|
{
|
||
|
File hr = OpenFile(g_PathCosole, "rb");
|
||
|
if( !hr )
|
||
|
{
|
||
|
LogError("Cannot open file: %s", g_PathCosole);
|
||
|
g_hTimer = null;
|
||
|
return Plugin_Handled;
|
||
|
}
|
||
|
if( g_ptrFile != -1 )
|
||
|
{
|
||
|
hr.Seek(g_ptrFile, SEEK_SET);
|
||
|
}
|
||
|
File hw = OpenFile(g_PathProfilerLog, "ab");
|
||
|
if( hw )
|
||
|
{
|
||
|
static int bytesRead, buff[1024];
|
||
|
|
||
|
while( !hr.EndOfFile() )
|
||
|
{
|
||
|
bytesRead = hr.Read(buff, sizeof(buff), 1);
|
||
|
hw.Write(buff, bytesRead, 1);
|
||
|
}
|
||
|
delete hw;
|
||
|
}
|
||
|
g_ptrFile = hr.Position;
|
||
|
delete hr;
|
||
|
}
|
||
|
g_hTimer = CreateTimer(LOG_CHECK_INTERVAL, Timer_MirrorLog, 0);
|
||
|
return Plugin_Handled;
|
||
|
}
|