projects-jenz/vprofiler/scripting/sm_vprofiler.sp

173 lines
4.2 KiB
SourcePawn
Raw Normal View History

#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;
}