Merge pull request #54 from alliedmodders/profiling
Add a general profiling abstraction layer.
This commit is contained in:
		
						commit
						2ae04ee1df
					
				@ -184,6 +184,11 @@ class SMConfig(object):
 | 
			
		||||
          cxx.cflags += ['-Wno-narrowing']
 | 
			
		||||
        if (have_gcc and cxx.version >= '4.7') or (have_clang and cxx.version >= '3'):
 | 
			
		||||
          cxx.cxxflags += ['-Wno-delete-non-virtual-dtor']
 | 
			
		||||
      if have_clang:
 | 
			
		||||
        cxx.cxxflags += [
 | 
			
		||||
          '-Wno-implicit-exception-spec-mismatch',
 | 
			
		||||
          '-Wno-deprecated-register',
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
      cxx.linkflags += ['-m32']
 | 
			
		||||
      cxx.cxxflags += [
 | 
			
		||||
@ -191,8 +196,6 @@ class SMConfig(object):
 | 
			
		||||
        '-fno-threadsafe-statics',
 | 
			
		||||
        '-Wno-non-virtual-dtor',
 | 
			
		||||
        '-Wno-overloaded-virtual',
 | 
			
		||||
        '-Wno-implicit-exception-spec-mismatch',
 | 
			
		||||
        '-Wno-deprecated-register',
 | 
			
		||||
      ]
 | 
			
		||||
 | 
			
		||||
      if have_gcc:
 | 
			
		||||
 | 
			
		||||
@ -41,7 +41,8 @@ project.sources += [
 | 
			
		||||
  'MenuStyle_Radio.cpp',
 | 
			
		||||
  'sm_autonatives.cpp',
 | 
			
		||||
  'sm_srvcmds.cpp',
 | 
			
		||||
  'ConsoleDetours.cpp'
 | 
			
		||||
  'ConsoleDetours.cpp',
 | 
			
		||||
  'vprof_tool.cpp',
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
for sdk_name in SM.sdks:
 | 
			
		||||
 | 
			
		||||
@ -33,7 +33,6 @@ binary.sources += [
 | 
			
		||||
  'TextParsers.cpp',
 | 
			
		||||
  'smn_textparse.cpp',
 | 
			
		||||
  'smn_adt_trie.cpp',
 | 
			
		||||
  'Profiler.cpp',
 | 
			
		||||
  'smn_functions.cpp',
 | 
			
		||||
  'smn_timers.cpp',
 | 
			
		||||
  'smn_players.cpp',
 | 
			
		||||
@ -66,6 +65,7 @@ binary.sources += [
 | 
			
		||||
  'AdminCache.cpp',
 | 
			
		||||
  'sm_trie.cpp',
 | 
			
		||||
  'smn_console.cpp',
 | 
			
		||||
  'ProfileTools.cpp',
 | 
			
		||||
]
 | 
			
		||||
if builder.target_platform == 'windows':
 | 
			
		||||
  binary.sources += ['thread/WinThreads.cpp']
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										173
									
								
								core/logic/ProfileTools.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										173
									
								
								core/logic/ProfileTools.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,173 @@
 | 
			
		||||
// vim: set ts=4 sw=4 tw=99 noet :
 | 
			
		||||
// =============================================================================
 | 
			
		||||
// SourceMod
 | 
			
		||||
// Copyright (C) 2004-2014 AlliedModders LLC.  All rights reserved.
 | 
			
		||||
// =============================================================================
 | 
			
		||||
//
 | 
			
		||||
// This program is free software; you can redistribute it and/or modify it under
 | 
			
		||||
// the terms of the GNU General Public License, version 3.0, as published by the
 | 
			
		||||
// Free Software Foundation.
 | 
			
		||||
// 
 | 
			
		||||
// This program is distributed in the hope that it will be useful, but WITHOUT
 | 
			
		||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 | 
			
		||||
// FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 | 
			
		||||
// details.
 | 
			
		||||
//
 | 
			
		||||
// You should have received a copy of the GNU General Public License along with
 | 
			
		||||
// this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
//
 | 
			
		||||
// As a special exception, AlliedModders LLC gives you permission to link the
 | 
			
		||||
// code of this program (as well as its derivative works) to "Half-Life 2," the
 | 
			
		||||
// "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
 | 
			
		||||
// by the Valve Corporation.  You must obey the GNU General Public License in
 | 
			
		||||
// all respects for all other code used.  Additionally, AlliedModders LLC grants
 | 
			
		||||
// this exception to all derivative works.  AlliedModders LLC defines further
 | 
			
		||||
// exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
 | 
			
		||||
// or <http://www.sourcemod.net/license.php>.
 | 
			
		||||
 | 
			
		||||
#include "ProfileTools.h"
 | 
			
		||||
#include <stdarg.h>
 | 
			
		||||
 | 
			
		||||
ProfileToolManager g_ProfileToolManager;
 | 
			
		||||
 | 
			
		||||
ProfileToolManager::ProfileToolManager()
 | 
			
		||||
	: active_(nullptr),
 | 
			
		||||
	  default_(nullptr)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
ProfileToolManager::OnSourceModAllInitialized()
 | 
			
		||||
{
 | 
			
		||||
	rootmenu->AddRootConsoleCommand2("prof", "Profiling", this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
ProfileToolManager::OnSourceModShutdown()
 | 
			
		||||
{
 | 
			
		||||
	rootmenu->RemoveRootConsoleCommand("prof", this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
IProfilingTool *
 | 
			
		||||
ProfileToolManager::FindToolByName(const char *name)
 | 
			
		||||
{
 | 
			
		||||
	for (size_t i = 0; i < tools_.length(); i++) {
 | 
			
		||||
		if (strcmp(tools_[i]->Name(), name) == 0)
 | 
			
		||||
			return tools_[i];
 | 
			
		||||
	}
 | 
			
		||||
	return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
render_help(const char *fmt, ...)
 | 
			
		||||
{
 | 
			
		||||
	char buffer[2048];
 | 
			
		||||
 | 
			
		||||
	va_list ap;
 | 
			
		||||
	va_start(ap, fmt);
 | 
			
		||||
	smcore.FormatArgs(buffer, sizeof(buffer), fmt, ap);
 | 
			
		||||
	va_end(ap);
 | 
			
		||||
 | 
			
		||||
	rootmenu->ConsolePrint("%s", buffer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
ProfileToolManager::StartFromConsole(IProfilingTool *tool)
 | 
			
		||||
{
 | 
			
		||||
	if (active_) {
 | 
			
		||||
		rootmenu->ConsolePrint("A profile is already active using %s.", active_->Name());
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	active_ = tool;
 | 
			
		||||
	if (!active_->Start()) {
 | 
			
		||||
		rootmenu->ConsolePrint("Failed to attach to or start %s.", active_->Name());
 | 
			
		||||
		active_ = nullptr;
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	g_pSourcePawn2->SetProfilingTool(active_);
 | 
			
		||||
	g_pSourcePawn2->EnableProfiling();
 | 
			
		||||
	rootmenu->ConsolePrint("Started profiling with %s.", active_->Name());
 | 
			
		||||
 | 
			
		||||
	default_ = active_;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
ProfileToolManager::OnRootConsoleCommand2(const char *cmdname, const ICommandArgs *args)
 | 
			
		||||
{
 | 
			
		||||
	if (tools_.length() == 0) {
 | 
			
		||||
		rootmenu->ConsolePrint("No profiling tools are enabled.");
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (args->ArgC() >= 3) {
 | 
			
		||||
		cmdname = args->Arg(2);
 | 
			
		||||
 | 
			
		||||
		if (strcmp(cmdname, "list") == 0) {
 | 
			
		||||
			rootmenu->ConsolePrint("Profiling tools:");
 | 
			
		||||
			for (size_t i = 0; i < tools_.length(); i++) {
 | 
			
		||||
				rootmenu->DrawGenericOption(tools_[i]->Name(), tools_[i]->Description());
 | 
			
		||||
			}
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		if (strcmp(cmdname, "stop") == 0) {
 | 
			
		||||
			if (!active_) {
 | 
			
		||||
				rootmenu->ConsolePrint("No profiler is active.");
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
			g_pSourcePawn2->DisableProfiling();
 | 
			
		||||
			g_pSourcePawn2->SetProfilingTool(nullptr);
 | 
			
		||||
			active_->Stop(render_help);
 | 
			
		||||
			active_ = nullptr;
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (args->ArgC() < 4) {
 | 
			
		||||
			if (strcmp(cmdname, "start") == 0) {
 | 
			
		||||
				if (!default_) {
 | 
			
		||||
					default_ = FindToolByName("vprof");
 | 
			
		||||
					if (!default_ && tools_.length() > 0)
 | 
			
		||||
						default_ = tools_[0];
 | 
			
		||||
					if (!default_) {
 | 
			
		||||
						rootmenu->ConsolePrint("Could not find any profiler to use.");
 | 
			
		||||
						return;
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				StartFromConsole(default_);
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (args->ArgC() < 4) {
 | 
			
		||||
			rootmenu->ConsolePrint("You must specify a profiling tool name.");
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		const char *toolname = args->Arg(3);
 | 
			
		||||
		if (strcmp(cmdname, "start") == 0) {
 | 
			
		||||
			IProfilingTool *tool = FindToolByName(toolname);
 | 
			
		||||
			if (!tool) {
 | 
			
		||||
				rootmenu->ConsolePrint("No tool with the name \"%s\" was found.", toolname);
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
			StartFromConsole(tool);
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		if (strcmp(cmdname, "help") == 0) {
 | 
			
		||||
			IProfilingTool *tool = FindToolByName(toolname);
 | 
			
		||||
			if (!tool) {
 | 
			
		||||
				rootmenu->ConsolePrint("No tool with the name \"%s\" was found.", toolname);
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
			tool->RenderHelp(render_help);
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rootmenu->ConsolePrint("Profiling commands:");
 | 
			
		||||
	rootmenu->DrawGenericOption("list", "List all available profiling tools.");
 | 
			
		||||
	rootmenu->DrawGenericOption("start", "Start a profile with a given tool.");
 | 
			
		||||
	rootmenu->DrawGenericOption("stop", "Stop the current profile session.");
 | 
			
		||||
	rootmenu->DrawGenericOption("help", "Display help text for a profiler.");
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										87
									
								
								core/logic/ProfileTools.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								core/logic/ProfileTools.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,87 @@
 | 
			
		||||
// vim: set ts=4 sw=4 tw=99 noet :
 | 
			
		||||
// =============================================================================
 | 
			
		||||
// SourceMod
 | 
			
		||||
// Copyright (C) 2004-2014 AlliedModders LLC.  All rights reserved.
 | 
			
		||||
// =============================================================================
 | 
			
		||||
//
 | 
			
		||||
// This program is free software; you can redistribute it and/or modify it under
 | 
			
		||||
// the terms of the GNU General Public License, version 3.0, as published by the
 | 
			
		||||
// Free Software Foundation.
 | 
			
		||||
// 
 | 
			
		||||
// This program is distributed in the hope that it will be useful, but WITHOUT
 | 
			
		||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 | 
			
		||||
// FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 | 
			
		||||
// details.
 | 
			
		||||
//
 | 
			
		||||
// You should have received a copy of the GNU General Public License along with
 | 
			
		||||
// this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
//
 | 
			
		||||
// As a special exception, AlliedModders LLC gives you permission to link the
 | 
			
		||||
// code of this program (as well as its derivative works) to "Half-Life 2," the
 | 
			
		||||
// "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
 | 
			
		||||
// by the Valve Corporation.  You must obey the GNU General Public License in
 | 
			
		||||
// all respects for all other code used.  Additionally, AlliedModders LLC grants
 | 
			
		||||
// this exception to all derivative works.  AlliedModders LLC defines further
 | 
			
		||||
// exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
 | 
			
		||||
// or <http://www.sourcemod.net/license.php>.
 | 
			
		||||
 | 
			
		||||
#ifndef _include_sourcemod_logic_profile_tool_manager_h_
 | 
			
		||||
#define _include_sourcemod_logic_profile_tool_manager_h_
 | 
			
		||||
 | 
			
		||||
#include <sp_vm_api.h>
 | 
			
		||||
#include <am-vector.h>
 | 
			
		||||
#include <IShareSys.h>
 | 
			
		||||
#include <IRootConsoleMenu.h>
 | 
			
		||||
#include "common_logic.h"
 | 
			
		||||
 | 
			
		||||
using namespace SourcePawn;
 | 
			
		||||
 | 
			
		||||
class ProfileToolManager
 | 
			
		||||
	: public SMGlobalClass,
 | 
			
		||||
	  public IRootConsoleCommand
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	ProfileToolManager();
 | 
			
		||||
 | 
			
		||||
	// SMGlobalClass
 | 
			
		||||
	void OnSourceModAllInitialized() KE_OVERRIDE;
 | 
			
		||||
	void OnSourceModShutdown() KE_OVERRIDE;
 | 
			
		||||
 | 
			
		||||
	// IRootConsoleCommand
 | 
			
		||||
	void OnRootConsoleCommand2(const char *cmdname, const ICommandArgs *args) KE_OVERRIDE;
 | 
			
		||||
 | 
			
		||||
	void RegisterTool(IProfilingTool *tool) {
 | 
			
		||||
		tools_.append(tool);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool IsActive() const {
 | 
			
		||||
		return !!active_;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If we get problems with plugins not being able to balance the profile
 | 
			
		||||
	// scopes, we should add a safety net that automatically clears any pending
 | 
			
		||||
	// scopes.
 | 
			
		||||
	void EnterScope(const char *group, const char *name) {
 | 
			
		||||
		if (active_)
 | 
			
		||||
			active_->EnterScope(group, name);
 | 
			
		||||
	}
 | 
			
		||||
	void LeaveScope() {
 | 
			
		||||
		if (active_)
 | 
			
		||||
			active_->LeaveScope();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	IProfilingTool *FindToolByName(const char *name);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	void StartFromConsole(IProfilingTool *tool);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	ke::Vector<IProfilingTool *> tools_;
 | 
			
		||||
	IProfilingTool *active_;
 | 
			
		||||
	IProfilingTool *default_;
 | 
			
		||||
	bool enabled_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern ProfileToolManager g_ProfileToolManager;
 | 
			
		||||
 | 
			
		||||
#endif // _include_sourcemod_logic_profile_tool_manager_h_
 | 
			
		||||
@ -1,516 +0,0 @@
 | 
			
		||||
/**
 | 
			
		||||
 * vim: set ts=4 sw=4 tw=99 noet :
 | 
			
		||||
 * =============================================================================
 | 
			
		||||
 * SourceMod
 | 
			
		||||
 * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
 | 
			
		||||
 * =============================================================================
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify it under
 | 
			
		||||
 * the terms of the GNU General Public License, version 3.0, as published by the
 | 
			
		||||
 * Free Software Foundation.
 | 
			
		||||
 * 
 | 
			
		||||
 * This program is distributed in the hope that it will be useful, but WITHOUT
 | 
			
		||||
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 | 
			
		||||
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 | 
			
		||||
 * details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License along with
 | 
			
		||||
 * this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 * As a special exception, AlliedModders LLC gives you permission to link the
 | 
			
		||||
 * code of this program (as well as its derivative works) to "Half-Life 2," the
 | 
			
		||||
 * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
 | 
			
		||||
 * by the Valve Corporation.  You must obey the GNU General Public License in
 | 
			
		||||
 * all respects for all other code used.  Additionally, AlliedModders LLC grants
 | 
			
		||||
 * this exception to all derivative works.  AlliedModders LLC defines further
 | 
			
		||||
 * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
 | 
			
		||||
 * or <http://www.sourcemod.net/license.php>.
 | 
			
		||||
 *
 | 
			
		||||
 * Version: $Id$
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "Profiler.h"
 | 
			
		||||
#include <ISourceMod.h>
 | 
			
		||||
#if defined PLATFORM_POSIX
 | 
			
		||||
#include <sys/time.h>
 | 
			
		||||
#include <time.h>
 | 
			
		||||
#endif
 | 
			
		||||
#include <IPluginSys.h>
 | 
			
		||||
#include "stringutil.h"
 | 
			
		||||
 | 
			
		||||
ProfileEngine g_Profiler;
 | 
			
		||||
IProfiler *sm_profiler = &g_Profiler;
 | 
			
		||||
 | 
			
		||||
#if defined PLATFORM_WINDOWS
 | 
			
		||||
double WINDOWS_PERFORMANCE_FREQUENCY;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
class EmptyProfiler : public IProfiler
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	void OnNativeBegin(IPluginContext *pContext, sp_native_t *native)
 | 
			
		||||
	{
 | 
			
		||||
	}
 | 
			
		||||
	void OnNativeEnd()
 | 
			
		||||
	{
 | 
			
		||||
	}
 | 
			
		||||
	void OnFunctionBegin(IPluginContext *pContext, const char *name)
 | 
			
		||||
	{
 | 
			
		||||
	}
 | 
			
		||||
	void OnFunctionEnd()
 | 
			
		||||
	{
 | 
			
		||||
	}
 | 
			
		||||
	int OnCallbackBegin(IPluginContext *pContext, sp_public_t *pubfunc)
 | 
			
		||||
	{
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
	void OnCallbackEnd(int serial)
 | 
			
		||||
	{
 | 
			
		||||
	}
 | 
			
		||||
} s_EmptyProfiler;
 | 
			
		||||
 | 
			
		||||
inline void InitProfPoint(prof_point_t &pt)
 | 
			
		||||
{
 | 
			
		||||
#if defined PLATFORM_WINDOWS
 | 
			
		||||
	QueryPerformanceCounter(&pt.value);
 | 
			
		||||
#elif defined PLATFORM_POSIX
 | 
			
		||||
	gettimeofday(&pt.value, NULL);
 | 
			
		||||
#endif
 | 
			
		||||
	pt.is_set = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ProfileEngine::ProfileEngine()
 | 
			
		||||
{
 | 
			
		||||
	m_serial = 0;
 | 
			
		||||
 | 
			
		||||
#if defined PLATFORM_WINDOWS
 | 
			
		||||
	LARGE_INTEGER pf;
 | 
			
		||||
 | 
			
		||||
	if (QueryPerformanceFrequency(&pf))
 | 
			
		||||
	{
 | 
			
		||||
		WINDOWS_PERFORMANCE_FREQUENCY = 1.0 / (double)(pf.QuadPart);
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		WINDOWS_PERFORMANCE_FREQUENCY = -1.0;
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	if (IsEnabled())
 | 
			
		||||
	{
 | 
			
		||||
		InitProfPoint(m_ProfStart);
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		sm_profiler = &s_EmptyProfiler;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ProfileEngine::IsEnabled()
 | 
			
		||||
{
 | 
			
		||||
#if defined PLATFORM_WINDOWS
 | 
			
		||||
	return (WINDOWS_PERFORMANCE_FREQUENCY > 0.0);
 | 
			
		||||
#elif defined PLATFORM_POSIX
 | 
			
		||||
	return true;
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline double DiffProfPoints(const prof_point_t &start, const prof_point_t &end)
 | 
			
		||||
{
 | 
			
		||||
	double seconds;
 | 
			
		||||
 | 
			
		||||
#if defined PLATFORM_WINDOWS
 | 
			
		||||
	LONGLONG diff;
 | 
			
		||||
	
 | 
			
		||||
	diff = end.value.QuadPart - start.value.QuadPart;
 | 
			
		||||
	seconds = diff * WINDOWS_PERFORMANCE_FREQUENCY;
 | 
			
		||||
#elif defined PLATFORM_POSIX
 | 
			
		||||
	seconds = (double)(end.value.tv_sec - start.value.tv_sec);
 | 
			
		||||
 | 
			
		||||
	if (start.value.tv_usec > end.value.tv_usec)
 | 
			
		||||
	{
 | 
			
		||||
		seconds -= 1.0;
 | 
			
		||||
		seconds += (double)(1000000 - (start.value.tv_usec - end.value.tv_usec)) / 1000000.0;
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		seconds += (double)(end.value.tv_usec - start.value.tv_usec) / 1000000.0;
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	return seconds;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline double CalcAtomTime(const prof_atom_t &atom)
 | 
			
		||||
{
 | 
			
		||||
	if (!atom.end.is_set)
 | 
			
		||||
	{
 | 
			
		||||
		return atom.base_time;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return atom.base_time + DiffProfPoints(atom.start, atom.end);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ProfileEngine::OnNativeBegin(IPluginContext *pContext, sp_native_t *native)
 | 
			
		||||
{
 | 
			
		||||
	PushProfileStack(pContext, SP_PROF_NATIVES, native->name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ProfileEngine::OnNativeEnd()
 | 
			
		||||
{
 | 
			
		||||
	assert(!m_AtomStack.empty());
 | 
			
		||||
	assert(m_AtomStack.front().atom_type == SP_PROF_NATIVES);
 | 
			
		||||
 | 
			
		||||
	PopProfileStack(&m_Natives);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ProfileEngine::OnFunctionBegin(IPluginContext *pContext, const char *name)
 | 
			
		||||
{
 | 
			
		||||
	PushProfileStack(pContext, SP_PROF_FUNCTIONS, name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ProfileEngine::OnFunctionEnd()
 | 
			
		||||
{
 | 
			
		||||
	assert(!m_AtomStack.empty());
 | 
			
		||||
	assert(m_AtomStack.front().atom_type == SP_PROF_FUNCTIONS);
 | 
			
		||||
 | 
			
		||||
	PopProfileStack(&m_Functions);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int ProfileEngine::OnCallbackBegin(IPluginContext *pContext, sp_public_t *pubfunc)
 | 
			
		||||
{
 | 
			
		||||
	PushProfileStack(pContext, SP_PROF_CALLBACKS, pubfunc->name);
 | 
			
		||||
 | 
			
		||||
	return m_serial;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ProfileEngine::OnCallbackEnd(int serial)
 | 
			
		||||
{
 | 
			
		||||
	assert(!m_AtomStack.empty());
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Account for the situation where the JIT discards the 
 | 
			
		||||
	 * stack because there was an RTE of sorts.
 | 
			
		||||
	 */
 | 
			
		||||
	if (m_AtomStack.front().atom_type != SP_PROF_CALLBACKS
 | 
			
		||||
		&& m_AtomStack.front().atom_serial != serial)
 | 
			
		||||
	{
 | 
			
		||||
		prof_atom_t atom;
 | 
			
		||||
		double total_time;
 | 
			
		||||
 | 
			
		||||
		/* There was an error, and we need to discard things. */
 | 
			
		||||
		total_time = 0.0;
 | 
			
		||||
		while (!m_AtomStack.empty() 
 | 
			
		||||
			&& m_AtomStack.front().atom_type != SP_PROF_CALLBACKS 
 | 
			
		||||
			&& m_AtomStack.front().atom_serial != serial)
 | 
			
		||||
		{
 | 
			
		||||
			total_time += CalcAtomTime(m_AtomStack.front());
 | 
			
		||||
			m_AtomStack.pop();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * Now we can end and discard ourselves, without saving the data.
 | 
			
		||||
		 * Since this data is all erroneous anyway, we don't care if it's 
 | 
			
		||||
		 * not totally accurate. 
 | 
			
		||||
		 */
 | 
			
		||||
 | 
			
		||||
		assert(!m_AtomStack.empty());
 | 
			
		||||
		atom = m_AtomStack.front();
 | 
			
		||||
		m_AtomStack.pop();
 | 
			
		||||
 | 
			
		||||
		/* Note: We don't need to resume ourselves because end is set by Pause(). */
 | 
			
		||||
		total_time += CalcAtomTime(atom);
 | 
			
		||||
 | 
			
		||||
		ResumeParent(total_time);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	PopProfileStack(&m_Callbacks);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ProfileEngine::PushProfileStack(IPluginContext *ctx, int type, const char *name)
 | 
			
		||||
{
 | 
			
		||||
	prof_atom_t atom;
 | 
			
		||||
 | 
			
		||||
	PauseParent();
 | 
			
		||||
 | 
			
		||||
	atom.atom_type = type;
 | 
			
		||||
	atom.base_time = 0.0;
 | 
			
		||||
	atom.ctx = ctx->GetContext();
 | 
			
		||||
	atom.name = name;
 | 
			
		||||
	atom.end.is_set = false;
 | 
			
		||||
 | 
			
		||||
	if (type == SP_PROF_CALLBACKS)
 | 
			
		||||
	{
 | 
			
		||||
		atom.atom_serial = ++m_serial;
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		atom.atom_serial = 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	m_AtomStack.push(atom);
 | 
			
		||||
 | 
			
		||||
	/* Note: We do this after because the stack could grow and skew results */
 | 
			
		||||
	InitProfPoint(m_AtomStack.front().start);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ProfileEngine::PopProfileStack(ProfileReport *reporter)
 | 
			
		||||
{
 | 
			
		||||
	double total_time;
 | 
			
		||||
 | 
			
		||||
	prof_atom_t &atom = m_AtomStack.front();
 | 
			
		||||
 | 
			
		||||
	/* We're okay to cache our used time. */
 | 
			
		||||
	InitProfPoint(atom.end);
 | 
			
		||||
	total_time = CalcAtomTime(atom);
 | 
			
		||||
 | 
			
		||||
	/* Now it's time to save this! This may do a lot of computations which 
 | 
			
		||||
	 * is why we've cached the time beforehand.
 | 
			
		||||
	 */
 | 
			
		||||
	reporter->SaveAtom(atom);
 | 
			
		||||
	m_AtomStack.pop();
 | 
			
		||||
 | 
			
		||||
	/* Finally, tell our parent how much time we used. */
 | 
			
		||||
	ResumeParent(total_time);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ProfileEngine::PauseParent()
 | 
			
		||||
{
 | 
			
		||||
	if (m_AtomStack.empty())
 | 
			
		||||
	{
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	InitProfPoint(m_AtomStack.front().end);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ProfileEngine::ResumeParent(double addTime)
 | 
			
		||||
{
 | 
			
		||||
	if (m_AtomStack.empty())
 | 
			
		||||
	{
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	prof_atom_t &atom = m_AtomStack.front();
 | 
			
		||||
 | 
			
		||||
	/* Move its "paused time" to its base (known) time, 
 | 
			
		||||
	 * then reset the start/end.  Note that since CalcAtomTime() 
 | 
			
		||||
	 * reads the base time, we SHOULD NOT use += to add.
 | 
			
		||||
	 */
 | 
			
		||||
	atom.base_time = CalcAtomTime(atom);
 | 
			
		||||
	atom.base_time += addTime;
 | 
			
		||||
	InitProfPoint(atom.start);
 | 
			
		||||
	atom.end.is_set = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ProfileEngine::Clear()
 | 
			
		||||
{
 | 
			
		||||
	m_Natives.Clear();
 | 
			
		||||
	m_Callbacks.Clear();
 | 
			
		||||
	m_Functions.Clear();
 | 
			
		||||
	InitProfPoint(m_ProfStart);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ProfileEngine::OnSourceModAllInitialized()
 | 
			
		||||
{
 | 
			
		||||
	rootmenu->AddRootConsoleCommand2("profiler", "Profiler commands", this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ProfileEngine::OnSourceModShutdown()
 | 
			
		||||
{
 | 
			
		||||
	rootmenu->RemoveRootConsoleCommand("profiler", this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ProfileEngine::OnRootConsoleCommand2(const char *cmdname, const ICommandArgs *command)
 | 
			
		||||
{
 | 
			
		||||
	if (command->ArgC() >= 3)
 | 
			
		||||
	{
 | 
			
		||||
		if (strcmp(command->Arg(2), "flush") == 0)
 | 
			
		||||
		{
 | 
			
		||||
			FILE *fp;
 | 
			
		||||
			char path[256];
 | 
			
		||||
 | 
			
		||||
			g_pSM->BuildPath(Path_SM, path, sizeof(path), "logs/profile_%d.xml", (int)time(NULL));
 | 
			
		||||
 | 
			
		||||
			if ((fp = fopen(path, "wt")) == NULL)
 | 
			
		||||
			{
 | 
			
		||||
				rootmenu->ConsolePrint("Failed, could not open file for writing: %s", path);
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			GenerateReport(fp);
 | 
			
		||||
 | 
			
		||||
			fclose(fp);
 | 
			
		||||
			
 | 
			
		||||
			Clear();
 | 
			
		||||
 | 
			
		||||
			rootmenu->ConsolePrint("Profiler report generated as: %s\n", path);
 | 
			
		||||
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		else if (strcmp(command->Arg(2), "report") == 0)
 | 
			
		||||
		{
 | 
			
		||||
			FILE *fp;
 | 
			
		||||
			char path[256];
 | 
			
		||||
 | 
			
		||||
			g_pSM->BuildPath(Path_SM, path, sizeof(path), "logs/profile_%d.xml", (int)time(NULL));
 | 
			
		||||
 | 
			
		||||
			if ((fp = fopen(path, "wt")) == NULL)
 | 
			
		||||
			{
 | 
			
		||||
				rootmenu->ConsolePrint("Failed, could not open file for writing: %s", path);
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			GenerateReport(fp);
 | 
			
		||||
 | 
			
		||||
			fclose(fp);
 | 
			
		||||
			
 | 
			
		||||
			rootmenu->ConsolePrint("Profiler report generated as: %s\n", path);
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		else if (strcmp(command->Arg(2), "clear") == 0)
 | 
			
		||||
		{
 | 
			
		||||
			Clear();
 | 
			
		||||
 | 
			
		||||
			rootmenu->ConsolePrint("Profiler statistics cleared.\n");
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rootmenu->ConsolePrint("Profiler commands:");
 | 
			
		||||
	rootmenu->DrawGenericOption("flush", "Flushes statistics to disk and starts over");
 | 
			
		||||
	rootmenu->DrawGenericOption("report", "Flushes statistics to disk");
 | 
			
		||||
	rootmenu->DrawGenericOption("clear", "Clears statistics");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ProfileEngine::GenerateReport(FILE *fp)
 | 
			
		||||
{
 | 
			
		||||
	time_t t;
 | 
			
		||||
	double total_time;
 | 
			
		||||
	prof_point_t end_time;
 | 
			
		||||
 | 
			
		||||
	InitProfPoint(end_time);
 | 
			
		||||
	total_time = DiffProfPoints(m_ProfStart, end_time);
 | 
			
		||||
 | 
			
		||||
	t = time(NULL);
 | 
			
		||||
 | 
			
		||||
	fprintf(fp, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n");
 | 
			
		||||
	fprintf(fp, "<profile time=\"%d\" uptime=\"%f\">\n", (int)t, total_time);
 | 
			
		||||
	WriteReport(fp, &m_Natives, "natives");
 | 
			
		||||
	WriteReport(fp, &m_Callbacks, "callbacks");
 | 
			
		||||
	WriteReport(fp, &m_Functions, "functions");
 | 
			
		||||
	fprintf(fp, "</profile>\n");
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ProfileEngine::WriteReport(FILE *fp, ProfileReport *report, const char *name)
 | 
			
		||||
{
 | 
			
		||||
	size_t i, num;
 | 
			
		||||
	prof_atom_report_t *ar;
 | 
			
		||||
	char new_name[512];
 | 
			
		||||
 | 
			
		||||
	fprintf(fp, " <report name=\"%s\">\n", name);
 | 
			
		||||
	
 | 
			
		||||
	num = report->GetNumReports();
 | 
			
		||||
	for (i = 0; i < num; i++)
 | 
			
		||||
	{
 | 
			
		||||
		ar = report->GetReport(i);
 | 
			
		||||
 | 
			
		||||
		strncopy(new_name, ar->atom_name, sizeof(new_name));
 | 
			
		||||
		UTIL_ReplaceAll(new_name, sizeof(new_name), "<", "<", true);
 | 
			
		||||
		UTIL_ReplaceAll(new_name, sizeof(new_name), ">", ">", true);
 | 
			
		||||
 | 
			
		||||
		fprintf(fp, "  <item name=\"%s\" numcalls=\"%d\" mintime=\"%f\" maxtime=\"%f\" totaltime=\"%f\"/>\n", 
 | 
			
		||||
			new_name,
 | 
			
		||||
			ar->num_calls,
 | 
			
		||||
			ar->min_time,
 | 
			
		||||
			ar->max_time,
 | 
			
		||||
			ar->total_time);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fprintf(fp, " </report>\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ProfileReport::~ProfileReport()
 | 
			
		||||
{
 | 
			
		||||
	for (size_t i = 0; i < m_Reports.size(); i++)
 | 
			
		||||
	{
 | 
			
		||||
		delete m_Reports[i];
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ProfileReport::Clear()
 | 
			
		||||
{
 | 
			
		||||
	m_ReportLookup.clear();
 | 
			
		||||
	for (size_t i = 0; i < m_Reports.size(); i++)
 | 
			
		||||
	{
 | 
			
		||||
		delete m_Reports[i];
 | 
			
		||||
	}
 | 
			
		||||
	m_Reports.clear();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t ProfileReport::GetNumReports()
 | 
			
		||||
{
 | 
			
		||||
	return m_Reports.size();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
prof_atom_report_t *ProfileReport::GetReport(size_t i)
 | 
			
		||||
{
 | 
			
		||||
	return m_Reports[i];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ProfileReport::SaveAtom(const prof_atom_t &atom)
 | 
			
		||||
{
 | 
			
		||||
	double atom_time;
 | 
			
		||||
	char full_name[256];
 | 
			
		||||
	prof_atom_report_t *report;
 | 
			
		||||
 | 
			
		||||
	if (atom.atom_type == SP_PROF_NATIVES)
 | 
			
		||||
	{
 | 
			
		||||
		smcore.strncopy(full_name, atom.name, sizeof(full_name));
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		IPlugin *pl;
 | 
			
		||||
		const char *file;
 | 
			
		||||
 | 
			
		||||
		file = "unknown";
 | 
			
		||||
		if ((pl = pluginsys->FindPluginByContext(atom.ctx)) != NULL)
 | 
			
		||||
		{
 | 
			
		||||
			file = pl->GetFilename();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		smcore.Format(full_name, sizeof(full_name), "%s!%s", file, atom.name);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	atom_time = CalcAtomTime(atom);
 | 
			
		||||
 | 
			
		||||
	if (!m_ReportLookup.retrieve(full_name, &report))
 | 
			
		||||
	{
 | 
			
		||||
		report = new prof_atom_report_t;
 | 
			
		||||
 | 
			
		||||
		smcore.strncopy(report->atom_name, full_name, sizeof(report->atom_name));
 | 
			
		||||
		report->max_time = atom_time;
 | 
			
		||||
		report->min_time = atom_time;
 | 
			
		||||
		report->num_calls = 1;
 | 
			
		||||
		report->total_time = atom_time;
 | 
			
		||||
		
 | 
			
		||||
		m_ReportLookup.insert(full_name, report);
 | 
			
		||||
		m_Reports.push_back(report);
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		if (atom_time > report->max_time)
 | 
			
		||||
		{
 | 
			
		||||
			report->max_time = atom_time;
 | 
			
		||||
		}
 | 
			
		||||
		if (atom_time < report->min_time)
 | 
			
		||||
		{
 | 
			
		||||
			report->min_time = atom_time;
 | 
			
		||||
		}
 | 
			
		||||
		report->num_calls++;
 | 
			
		||||
		report->total_time += atom_time;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -1,136 +0,0 @@
 | 
			
		||||
/**
 | 
			
		||||
 * vim: set ts=4 sw=4 tw=99 noet :
 | 
			
		||||
 * =============================================================================
 | 
			
		||||
 * SourceMod
 | 
			
		||||
 * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
 | 
			
		||||
 * =============================================================================
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify it under
 | 
			
		||||
 * the terms of the GNU General Public License, version 3.0, as published by the
 | 
			
		||||
 * Free Software Foundation.
 | 
			
		||||
 * 
 | 
			
		||||
 * This program is distributed in the hope that it will be useful, but WITHOUT
 | 
			
		||||
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 | 
			
		||||
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 | 
			
		||||
 * details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License along with
 | 
			
		||||
 * this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 * As a special exception, AlliedModders LLC gives you permission to link the
 | 
			
		||||
 * code of this program (as well as its derivative works) to "Half-Life 2," the
 | 
			
		||||
 * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
 | 
			
		||||
 * by the Valve Corporation.  You must obey the GNU General Public License in
 | 
			
		||||
 * all respects for all other code used.  Additionally, AlliedModders LLC grants
 | 
			
		||||
 * this exception to all derivative works.  AlliedModders LLC defines further
 | 
			
		||||
 * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
 | 
			
		||||
 * or <http://www.sourcemod.net/license.php>.
 | 
			
		||||
 *
 | 
			
		||||
 * Version: $Id$
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef _INCLUDE_SOURCEMOD_PLUGIN_PROFILER_H_
 | 
			
		||||
#define _INCLUDE_SOURCEMOD_PLUGIN_PROFILER_H_
 | 
			
		||||
 | 
			
		||||
#include <sp_vm_api.h>
 | 
			
		||||
#include <sm_platform.h>
 | 
			
		||||
#include <sh_vector.h>
 | 
			
		||||
#include <sh_stack.h>
 | 
			
		||||
#include <sm_namehashset.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include "common_logic.h"
 | 
			
		||||
#include <IRootConsoleMenu.h>
 | 
			
		||||
 | 
			
		||||
using namespace SourcePawn;
 | 
			
		||||
using namespace SourceHook;
 | 
			
		||||
 | 
			
		||||
struct prof_point_t
 | 
			
		||||
{
 | 
			
		||||
#if defined PLATFORM_WINDOWS
 | 
			
		||||
	LARGE_INTEGER value;
 | 
			
		||||
#elif defined PLATFORM_POSIX
 | 
			
		||||
	struct timeval value;
 | 
			
		||||
#endif
 | 
			
		||||
	bool is_set;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct prof_atom_t
 | 
			
		||||
{
 | 
			
		||||
	int atom_type;			/* Type of object we're profiling */
 | 
			
		||||
	int atom_serial;		/* Serial number, if appropriate */
 | 
			
		||||
	sp_context_t *ctx;		/* Plugin context. */
 | 
			
		||||
	const char *name;		/* Name of the function */
 | 
			
		||||
	prof_point_t start;		/* Start time */
 | 
			
		||||
	prof_point_t end;		/* End time */
 | 
			
		||||
	double base_time;		/* Known time from children or pausing. */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct prof_atom_report_t
 | 
			
		||||
{
 | 
			
		||||
	char atom_name[256];	/* Full name to shove to logs */
 | 
			
		||||
	double total_time;		/* Total time spent executing, in s */
 | 
			
		||||
	unsigned int num_calls;	/* Number of invocations */
 | 
			
		||||
	double min_time;		/* Min time spent in one call, in s */
 | 
			
		||||
	double max_time;		/* Max time spent in one call, in s */
 | 
			
		||||
 | 
			
		||||
	static inline bool matches(const char *name, const prof_atom_report_t *report)
 | 
			
		||||
	{
 | 
			
		||||
		return strcmp(report->atom_name, name) == 0;
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class ProfileReport
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	~ProfileReport();
 | 
			
		||||
public:
 | 
			
		||||
	void SaveAtom(const prof_atom_t &atom);
 | 
			
		||||
	size_t GetNumReports();
 | 
			
		||||
	prof_atom_report_t *GetReport(size_t i);
 | 
			
		||||
	void Clear();
 | 
			
		||||
private:
 | 
			
		||||
	NameHashSet<prof_atom_report_t *> m_ReportLookup;
 | 
			
		||||
	CVector<prof_atom_report_t *> m_Reports;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class ProfileEngine :
 | 
			
		||||
	public SMGlobalClass,
 | 
			
		||||
	public IRootConsoleCommand,
 | 
			
		||||
	public IProfiler
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	ProfileEngine();
 | 
			
		||||
public:
 | 
			
		||||
	bool IsEnabled();
 | 
			
		||||
	bool GenerateReport(FILE *fp);
 | 
			
		||||
	void Clear();
 | 
			
		||||
public: //SMGlobalClass
 | 
			
		||||
	void OnSourceModAllInitialized();
 | 
			
		||||
	void OnSourceModShutdown();
 | 
			
		||||
public: //IRootConsoleCommand
 | 
			
		||||
	void OnRootConsoleCommand2(const char *cmdname, const ICommandArgs *command);
 | 
			
		||||
public: //IProfiler
 | 
			
		||||
	void OnNativeBegin(IPluginContext *pContext, sp_native_t *native);
 | 
			
		||||
	void OnNativeEnd() ;
 | 
			
		||||
	void OnFunctionBegin(IPluginContext *pContext, const char *name);
 | 
			
		||||
	void OnFunctionEnd();
 | 
			
		||||
	int OnCallbackBegin(IPluginContext *pContext, sp_public_t *pubfunc);
 | 
			
		||||
	void OnCallbackEnd(int serial);
 | 
			
		||||
private:
 | 
			
		||||
	void PushProfileStack(IPluginContext *ctx, int type, const char *name);
 | 
			
		||||
	void PopProfileStack(ProfileReport *reporter);
 | 
			
		||||
	void PauseParent();
 | 
			
		||||
	void ResumeParent(double addTime);
 | 
			
		||||
	void WriteReport(FILE *fp, ProfileReport *report, const char *name);
 | 
			
		||||
private:
 | 
			
		||||
	CStack<prof_atom_t> m_AtomStack;
 | 
			
		||||
	ProfileReport m_Callbacks;
 | 
			
		||||
	ProfileReport m_Functions;
 | 
			
		||||
	ProfileReport m_Natives;
 | 
			
		||||
	int m_serial;
 | 
			
		||||
	prof_point_t m_ProfStart;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern IProfiler *sm_profiler;
 | 
			
		||||
 | 
			
		||||
#endif //_INCLUDE_SOURCEMOD_PLUGIN_PROFILER_H_
 | 
			
		||||
@ -35,7 +35,6 @@
 | 
			
		||||
#include <stdarg.h>
 | 
			
		||||
#include "common_logic.h"
 | 
			
		||||
#include "TextParsers.h"
 | 
			
		||||
#include "Profiler.h"
 | 
			
		||||
#include "sm_crc32.h"
 | 
			
		||||
#include "MemoryUtils.h"
 | 
			
		||||
#include "stringutil.h"
 | 
			
		||||
@ -50,6 +49,7 @@
 | 
			
		||||
#include "ExtensionSys.h"
 | 
			
		||||
#include "ForwardSys.h"
 | 
			
		||||
#include "AdminCache.h"
 | 
			
		||||
#include "ProfileTools.h"
 | 
			
		||||
 | 
			
		||||
sm_core_t smcore;
 | 
			
		||||
IHandleSys *handlesys = &g_HandleSys;
 | 
			
		||||
@ -109,11 +109,15 @@ static void DumpAdminCache(FILE *f)
 | 
			
		||||
	g_Admins.DumpCache(f);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void RegisterProfiler(IProfilingTool *tool)
 | 
			
		||||
{
 | 
			
		||||
	g_ProfileToolManager.RegisterTool(tool);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static sm_logic_t logic =
 | 
			
		||||
{
 | 
			
		||||
	NULL,
 | 
			
		||||
	g_pThreader,
 | 
			
		||||
	sm_profiler,
 | 
			
		||||
	&g_Translator,
 | 
			
		||||
	stristr,
 | 
			
		||||
	CoreTranslate,
 | 
			
		||||
@ -128,6 +132,7 @@ static sm_logic_t logic =
 | 
			
		||||
	AddNatives,
 | 
			
		||||
	DumpHandles,
 | 
			
		||||
	DumpAdminCache,
 | 
			
		||||
	RegisterProfiler,
 | 
			
		||||
	&g_PluginSys,
 | 
			
		||||
	&g_ShareSys,
 | 
			
		||||
	&g_Extensions,
 | 
			
		||||
 | 
			
		||||
@ -317,7 +317,6 @@ struct sm_logic_t
 | 
			
		||||
{
 | 
			
		||||
	SMGlobalClass	*head;
 | 
			
		||||
	IThreader		*threader;
 | 
			
		||||
	IProfiler		*profiler;
 | 
			
		||||
	ITranslator		*translator;
 | 
			
		||||
	const char      *(*stristr)(const char *, const char *);
 | 
			
		||||
	bool			(*CoreTranslate)(char *,  size_t, const char *, unsigned int, size_t *, ...);
 | 
			
		||||
@ -332,6 +331,7 @@ struct sm_logic_t
 | 
			
		||||
	void			(*AddNatives)(sp_nativeinfo_t *natives);
 | 
			
		||||
	void			(*DumpHandles)(void (*dumpfn)(const char *fmt, ...));
 | 
			
		||||
	void			(*DumpAdminCache)(FILE *);
 | 
			
		||||
	void            (*RegisterProfiler)(IProfilingTool *tool);
 | 
			
		||||
	IScriptManager	*scripts;
 | 
			
		||||
	IShareSys		*sharesys;
 | 
			
		||||
	IExtensionSys	*extsys;
 | 
			
		||||
 | 
			
		||||
@ -36,6 +36,7 @@
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <sys/time.h>
 | 
			
		||||
#endif
 | 
			
		||||
#include "ProfileTools.h"
 | 
			
		||||
 | 
			
		||||
struct Profiler
 | 
			
		||||
{
 | 
			
		||||
@ -185,12 +186,42 @@ static cell_t GetProfilerTime(IPluginContext *pContext, const cell_t *params)
 | 
			
		||||
	return sp_ftoc(fTime);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static cell_t EnterProfilingEvent(IPluginContext *pContext, const cell_t *params)
 | 
			
		||||
{
 | 
			
		||||
	char *group;
 | 
			
		||||
	pContext->LocalToString(params[1], &group);
 | 
			
		||||
 | 
			
		||||
	char *name;
 | 
			
		||||
	pContext->LocalToString(params[2], &name);
 | 
			
		||||
 | 
			
		||||
	const char *groupname = NULL;
 | 
			
		||||
	if (strcmp(group, "all") != 0)
 | 
			
		||||
		groupname = group;
 | 
			
		||||
 | 
			
		||||
	g_ProfileToolManager.EnterScope(groupname, name);
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static cell_t LeaveProfilingEvent(IPluginContext *pContext, const cell_t *params)
 | 
			
		||||
{
 | 
			
		||||
	g_ProfileToolManager.LeaveScope();
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static cell_t IsProfilingActive(IPluginContext *pContext, const cell_t *params)
 | 
			
		||||
{
 | 
			
		||||
	return g_ProfileToolManager.IsActive() ? 1 : 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
REGISTER_NATIVES(profilerNatives)
 | 
			
		||||
{
 | 
			
		||||
	{"CreateProfiler",			CreateProfiler},
 | 
			
		||||
	{"GetProfilerTime",			GetProfilerTime},
 | 
			
		||||
	{"StartProfiling",			StartProfiling},
 | 
			
		||||
	{"StopProfiling",			StopProfiling},
 | 
			
		||||
	{"EnterProfilingEvent",     EnterProfilingEvent},
 | 
			
		||||
	{"LeaveProfilingEvent",     LeaveProfilingEvent},
 | 
			
		||||
	{"IsProfilingActive",       IsProfilingActive},
 | 
			
		||||
	{NULL,						NULL},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -628,7 +628,6 @@ void InitLogicBridge()
 | 
			
		||||
	glob->m_pGlobalClassNext = logicore.head;
 | 
			
		||||
 | 
			
		||||
	g_pThreader = logicore.threader;
 | 
			
		||||
	g_pSourcePawn2->SetProfiler(logicore.profiler);
 | 
			
		||||
	translator = logicore.translator;
 | 
			
		||||
	scripts = logicore.scripts;
 | 
			
		||||
	sharesys = logicore.sharesys;
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										108
									
								
								core/vprof_tool.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								core/vprof_tool.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,108 @@
 | 
			
		||||
// vim: set ts=4 sw=4 tw=99 noet :
 | 
			
		||||
// =============================================================================
 | 
			
		||||
// SourceMod
 | 
			
		||||
// Copyright (C) 2004-2014 AlliedModders LLC.  All rights reserved.
 | 
			
		||||
// =============================================================================
 | 
			
		||||
//
 | 
			
		||||
// This program is free software; you can redistribute it and/or modify it under
 | 
			
		||||
// the terms of the GNU General Public License, version 3.0, as published by the
 | 
			
		||||
// Free Software Foundation.
 | 
			
		||||
// 
 | 
			
		||||
// This program is distributed in the hope that it will be useful, but WITHOUT
 | 
			
		||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 | 
			
		||||
// FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 | 
			
		||||
// details.
 | 
			
		||||
//
 | 
			
		||||
// You should have received a copy of the GNU General Public License along with
 | 
			
		||||
// this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
//
 | 
			
		||||
// As a special exception, AlliedModders LLC gives you permission to link the
 | 
			
		||||
// code of this program (as well as its derivative works) to "Half-Life 2," the
 | 
			
		||||
// "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
 | 
			
		||||
// by the Valve Corporation.  You must obey the GNU General Public License in
 | 
			
		||||
// all respects for all other code used.  Additionally, AlliedModders LLC grants
 | 
			
		||||
// this exception to all derivative works.  AlliedModders LLC defines further
 | 
			
		||||
// exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
 | 
			
		||||
// or <http://www.sourcemod.net/license.php>.
 | 
			
		||||
 | 
			
		||||
#include "vprof_tool.h"
 | 
			
		||||
#include "logic_bridge.h"
 | 
			
		||||
#include "sourcemod.h"
 | 
			
		||||
#include "sourcemm_api.h"
 | 
			
		||||
 | 
			
		||||
#define VPROF_ENABLED
 | 
			
		||||
#include <tier0/vprof.h>
 | 
			
		||||
 | 
			
		||||
VProfTool sVProfTool;
 | 
			
		||||
 | 
			
		||||
VProfTool::VProfTool()
 | 
			
		||||
	: active_(false)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
VProfTool::OnSourceModAllInitialized()
 | 
			
		||||
{
 | 
			
		||||
	logicore.RegisterProfiler(this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *
 | 
			
		||||
VProfTool::Name()
 | 
			
		||||
{
 | 
			
		||||
	return "vprof";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *
 | 
			
		||||
VProfTool::Description()
 | 
			
		||||
{
 | 
			
		||||
	return "Valve built-in profiler";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool
 | 
			
		||||
VProfTool::Start()
 | 
			
		||||
{
 | 
			
		||||
	g_VProfCurrentProfile.Start();
 | 
			
		||||
	return IsActive();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
VProfTool::Stop(void (*render)(const char *fmt, ...))
 | 
			
		||||
{
 | 
			
		||||
	g_VProfCurrentProfile.Stop();
 | 
			
		||||
	RenderHelp(render);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool
 | 
			
		||||
VProfTool::IsActive()
 | 
			
		||||
{
 | 
			
		||||
	return g_VProfCurrentProfile.IsEnabled();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool
 | 
			
		||||
VProfTool::IsAttached()
 | 
			
		||||
{
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
VProfTool::EnterScope(const char *group, const char *name)
 | 
			
		||||
{
 | 
			
		||||
	if (IsActive()) {
 | 
			
		||||
		if (!group)
 | 
			
		||||
			group = VPROF_BUDGETGROUP_OTHER_UNACCOUNTED;
 | 
			
		||||
		g_VProfCurrentProfile.EnterScope(name, 1, group, false, 0);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void 
 | 
			
		||||
VProfTool::LeaveScope()
 | 
			
		||||
{
 | 
			
		||||
	if (IsActive())
 | 
			
		||||
		g_VProfCurrentProfile.ExitScope();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
VProfTool::RenderHelp(void (*render)(const char *fmt, ...))
 | 
			
		||||
{
 | 
			
		||||
	render("Use vprof_generate_report in your console to analyze a profile session.");
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										60
									
								
								core/vprof_tool.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								core/vprof_tool.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,60 @@
 | 
			
		||||
// vim: set ts=4 sw=4 tw=99 noet :
 | 
			
		||||
// =============================================================================
 | 
			
		||||
// SourceMod
 | 
			
		||||
// Copyright (C) 2004-2014 AlliedModders LLC.  All rights reserved.
 | 
			
		||||
// =============================================================================
 | 
			
		||||
//
 | 
			
		||||
// This program is free software; you can redistribute it and/or modify it under
 | 
			
		||||
// the terms of the GNU General Public License, version 3.0, as published by the
 | 
			
		||||
// Free Software Foundation.
 | 
			
		||||
// 
 | 
			
		||||
// This program is distributed in the hope that it will be useful, but WITHOUT
 | 
			
		||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 | 
			
		||||
// FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 | 
			
		||||
// details.
 | 
			
		||||
//
 | 
			
		||||
// You should have received a copy of the GNU General Public License along with
 | 
			
		||||
// this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
//
 | 
			
		||||
// As a special exception, AlliedModders LLC gives you permission to link the
 | 
			
		||||
// code of this program (as well as its derivative works) to "Half-Life 2," the
 | 
			
		||||
// "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
 | 
			
		||||
// by the Valve Corporation.  You must obey the GNU General Public License in
 | 
			
		||||
// all respects for all other code used.  Additionally, AlliedModders LLC grants
 | 
			
		||||
// this exception to all derivative works.  AlliedModders LLC defines further
 | 
			
		||||
// exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
 | 
			
		||||
// or <http://www.sourcemod.net/license.php>.
 | 
			
		||||
 | 
			
		||||
#ifndef _include_sourcemod_core_vprof_bridge_h_
 | 
			
		||||
#define _include_sourcemod_core_vprof_bridge_h_
 | 
			
		||||
 | 
			
		||||
#include <sp_vm_api.h>
 | 
			
		||||
#include "sm_globals.h"
 | 
			
		||||
#include <am-utility.h>
 | 
			
		||||
 | 
			
		||||
class VProfTool
 | 
			
		||||
	: public IProfilingTool,
 | 
			
		||||
	  public SMGlobalClass
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	VProfTool();
 | 
			
		||||
 | 
			
		||||
	// IProfilingTool
 | 
			
		||||
	const char *Name() KE_OVERRIDE;
 | 
			
		||||
	const char *Description() KE_OVERRIDE;
 | 
			
		||||
	bool Start() KE_OVERRIDE;
 | 
			
		||||
	void Stop(void (*render)(const char *fmt, ...)) KE_OVERRIDE;
 | 
			
		||||
	bool IsActive() KE_OVERRIDE;
 | 
			
		||||
	bool IsAttached() KE_OVERRIDE;
 | 
			
		||||
	void EnterScope(const char *group, const char *name) KE_OVERRIDE;
 | 
			
		||||
	void LeaveScope() KE_OVERRIDE;
 | 
			
		||||
	void RenderHelp(void (*render)(const char *fmt, ...)) KE_OVERRIDE;
 | 
			
		||||
 | 
			
		||||
	// SMGlobalClass
 | 
			
		||||
	void OnSourceModAllInitialized() KE_OVERRIDE;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	bool active_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif // _include_sourcemod_core_vprof_bridge_h_
 | 
			
		||||
@ -75,3 +75,26 @@ native StopProfiling(Handle:prof);
 | 
			
		||||
 * @error			Invalid Handle.
 | 
			
		||||
 */
 | 
			
		||||
native Float:GetProfilerTime(Handle:prof);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Mark the start of a profiling event.
 | 
			
		||||
 *
 | 
			
		||||
 * @param group     Budget group. This can be "all" for a default, or a short
 | 
			
		||||
 *                  description like "Timers" or "Events".
 | 
			
		||||
 * @param name      A name to attribute to this profiling event.
 | 
			
		||||
 * @noreturn
 | 
			
		||||
 */
 | 
			
		||||
native EnterProfilingEvent(const String:group[], const String:name[]);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Mark the end of the last profiling event. This must be called in the same
 | 
			
		||||
 * stack frame as StartProfilingEvent(). Not doing so, or throwing errors,
 | 
			
		||||
 * will make the resulting profile very wrong.
 | 
			
		||||
 */
 | 
			
		||||
native LeaveProfilingEvent();
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Returns true if the global profiler is enabled; false otherwise. It is
 | 
			
		||||
 * not necessary to call this before Enter/LeaveProfilingEvent.
 | 
			
		||||
 */
 | 
			
		||||
native bool:IsProfilingActive();
 | 
			
		||||
 | 
			
		||||
@ -998,61 +998,82 @@ namespace SourcePawn
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @brief Represents a code profiler for plugins.
 | 
			
		||||
	 * @brief Removed.
 | 
			
		||||
	 */
 | 
			
		||||
	class IProfiler
 | 
			
		||||
	class IProfiler;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @brief Encapsulates a profiling tool that may be attached to SourcePawn.
 | 
			
		||||
	 */
 | 
			
		||||
	class IProfilingTool
 | 
			
		||||
	{
 | 
			
		||||
	public:
 | 
			
		||||
		/**
 | 
			
		||||
		 * @brief Invoked by the JIT to notify that a native is being started.
 | 
			
		||||
		 * @brief Return the name of the profiling tool.
 | 
			
		||||
		 *
 | 
			
		||||
		 * @param pContext			Plugin context.
 | 
			
		||||
		 * @param native			Native information.
 | 
			
		||||
		 * @return                  Profiling tool name.
 | 
			
		||||
		 */
 | 
			
		||||
		virtual void OnNativeBegin(IPluginContext *pContext, sp_native_t *native) =0;
 | 
			
		||||
		virtual const char *Name() = 0;
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * @brief Invoked by the JIT to notify that the last native on the stack 
 | 
			
		||||
		 * is no longer being executed.
 | 
			
		||||
		 * @brief Description of the profiler.
 | 
			
		||||
		 *
 | 
			
		||||
		 * @return                  Description.
 | 
			
		||||
		 */
 | 
			
		||||
		virtual void OnNativeEnd() =0;
 | 
			
		||||
		virtual const char *Description() = 0;
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * @brief Invoked by the JIT to notify that a function call is starting.
 | 
			
		||||
		 * @brief Called to render help text.
 | 
			
		||||
		 *
 | 
			
		||||
		 * @param pContext			Plugin context.
 | 
			
		||||
		 * @param name				Function name, or NULL if not known.
 | 
			
		||||
		 * @param code_addr			P-Code address.
 | 
			
		||||
		 * @param  render           Function to render one line of text.
 | 
			
		||||
		 */
 | 
			
		||||
		virtual void OnFunctionBegin(IPluginContext *pContext, const char *name) =0;
 | 
			
		||||
		virtual void RenderHelp(void (*render)(const char *fmt, ...)) = 0;
 | 
			
		||||
	
 | 
			
		||||
		/**
 | 
			
		||||
		 * @brief Invoked by the JIT to notify that the last function call has 
 | 
			
		||||
		 * concluded.  In the case of an error inside a function, this will not 
 | 
			
		||||
		 * be called.  Instead, the VM will call OnCallbackEnd() and the profiler 
 | 
			
		||||
		 * stack must be unwound.
 | 
			
		||||
		 * @brief Initiate a start command.
 | 
			
		||||
		 *
 | 
			
		||||
		 * Initiate start commands through a profiling tool, returning whether
 | 
			
		||||
		 * or not the command is supported. If starting, SourceMod will generate
 | 
			
		||||
		 * events even if it cannot signal the external profiler.
 | 
			
		||||
		 */
 | 
			
		||||
		virtual void OnFunctionEnd() =0;
 | 
			
		||||
		virtual bool Start() = 0;
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * @brief Invoked by the VM to notify that a forward/callback is starting.
 | 
			
		||||
		 * @brief Initiate a stop command.
 | 
			
		||||
		 *
 | 
			
		||||
		 * @param pContext			Plugin context.
 | 
			
		||||
		 * @param pubfunc			Public function information.
 | 
			
		||||
		 * @return					Unique number to pass to OnFunctionEnd().
 | 
			
		||||
		 * @param render            Function to render any help messages.
 | 
			
		||||
		 */
 | 
			
		||||
		virtual int OnCallbackBegin(IPluginContext *pContext, sp_public_t *pubfunc) =0;
 | 
			
		||||
		virtual void Stop(void (*render)(const char *fmt, ...)) = 0;
 | 
			
		||||
	
 | 
			
		||||
		/**
 | 
			
		||||
		 * @brief Invoked by the JIT to notify that a callback has ended.  
 | 
			
		||||
		 * @brief Returns whether or not the profiler is currently profiling.
 | 
			
		||||
		 *
 | 
			
		||||
		 * As noted in OnFunctionEnd(), this my be called with a misaligned 
 | 
			
		||||
		 * profiler stack.  To correct this, the stack should be unwound 
 | 
			
		||||
		 * (discarding data as appropriate) to a matching serial number.
 | 
			
		||||
		 *
 | 
			
		||||
		 * @param serial			Unique number from OnCallbackBegin().
 | 
			
		||||
		 * @return                  True if active, false otherwise.
 | 
			
		||||
		 */
 | 
			
		||||
		virtual void OnCallbackEnd(int serial) =0;
 | 
			
		||||
		virtual bool IsActive() = 0;
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * @brief Returns whether the profiler is attached.
 | 
			
		||||
		 *
 | 
			
		||||
		 * @return                  True if attached, false otherwise.
 | 
			
		||||
		 */
 | 
			
		||||
		virtual bool IsAttached() = 0;
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * @brief Enters the scope of an event.
 | 
			
		||||
		 *
 | 
			
		||||
		 * LeaveScope() mus be called exactly once for each call to EnterScope().
 | 
			
		||||
		 *
 | 
			
		||||
		 * @param group             A named budget group, or NULL for the default.
 | 
			
		||||
		 * @param name              Event name.
 | 
			
		||||
		 */
 | 
			
		||||
		virtual void EnterScope(const char *group, const char *name) = 0;
 | 
			
		||||
		
 | 
			
		||||
		/**
 | 
			
		||||
		 * @brief Leave a profiling scope. This must be called exactly once for
 | 
			
		||||
		 * each call to EnterScope().
 | 
			
		||||
		 */
 | 
			
		||||
		virtual void LeaveScope() = 0;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	struct sp_plugin_s;
 | 
			
		||||
@ -1251,9 +1272,9 @@ namespace SourcePawn
 | 
			
		||||
		virtual IDebugListener *SetDebugListener(IDebugListener *listener) =0;
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * @brief Sets the global profiler.
 | 
			
		||||
		 * @brief Deprecated.
 | 
			
		||||
		 *
 | 
			
		||||
		 * @param profiler	Profiler pointer.
 | 
			
		||||
		 * @param profiler	Deprecated.
 | 
			
		||||
		 */
 | 
			
		||||
		virtual void SetProfiler(IProfiler *profiler) =0;
 | 
			
		||||
 | 
			
		||||
@ -1310,6 +1331,27 @@ namespace SourcePawn
 | 
			
		||||
		 * @return			True if the JIT is enabled, false otherwise.
 | 
			
		||||
		 */
 | 
			
		||||
		virtual bool IsJitEnabled() =0;
 | 
			
		||||
		
 | 
			
		||||
		/**
 | 
			
		||||
		 * @brief Enables profiling. SetProfilingTool() must have been called.
 | 
			
		||||
		 *
 | 
			
		||||
		 * Note that this does not activate the profiling tool. It only enables
 | 
			
		||||
		 * notifications to the profiling tool. SourcePawn will send events to
 | 
			
		||||
		 * the profiling tool even if the tool itself is reported as inactive.
 | 
			
		||||
		 */
 | 
			
		||||
		virtual void EnableProfiling() = 0;
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * @brief Disables profiling.
 | 
			
		||||
		 */
 | 
			
		||||
		virtual void DisableProfiling() = 0;
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * @brief Sets the profiling tool.
 | 
			
		||||
		 *
 | 
			
		||||
		 * @param tool      Profiling tool.
 | 
			
		||||
		 */
 | 
			
		||||
		virtual void SetProfilingTool(IProfilingTool *tool) =0;
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -292,7 +292,6 @@ int BaseRuntime::CreateFromMemory(sp_file_hdr_t *hdr, uint8_t *base)
 | 
			
		||||
  md5_data.finalize();
 | 
			
		||||
  md5_data.raw_digest(m_DataHash);
 | 
			
		||||
 | 
			
		||||
  m_plugin.profiler = g_engine2.GetProfiler();
 | 
			
		||||
  m_pCtx = new BaseContext(this);
 | 
			
		||||
  co_ = g_Jit.StartCompilation(this);
 | 
			
		||||
 | 
			
		||||
@ -491,9 +490,7 @@ BaseRuntime::GetFunctionById(funcid_t func_id)
 | 
			
		||||
      return NULL;
 | 
			
		||||
    pFunc = m_PubFuncs[func_id];
 | 
			
		||||
    if (!pFunc) {
 | 
			
		||||
      m_PubFuncs[func_id] = new CFunction(this, 
 | 
			
		||||
        (func_id << 1) | 1,
 | 
			
		||||
        func_id);
 | 
			
		||||
      m_PubFuncs[func_id] = new CFunction(this, (func_id << 1) | 1, func_id);
 | 
			
		||||
      pFunc = m_PubFuncs[func_id];
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
@ -501,6 +498,21 @@ BaseRuntime::GetFunctionById(funcid_t func_id)
 | 
			
		||||
  return pFunc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CFunction *
 | 
			
		||||
BaseRuntime::GetPublicFunction(size_t index)
 | 
			
		||||
{
 | 
			
		||||
  CFunction *pFunc = m_PubFuncs[index];
 | 
			
		||||
  if (!pFunc) {
 | 
			
		||||
    sp_public_t *pub = NULL;
 | 
			
		||||
    GetPublicByIndex(index, &pub);
 | 
			
		||||
    if (pub)
 | 
			
		||||
      m_PubFuncs[index] = new CFunction(this, (index << 1) | 1, index);
 | 
			
		||||
    pFunc = m_PubFuncs[index];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return pFunc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
IPluginFunction *
 | 
			
		||||
BaseRuntime::GetFunctionByName(const char *public_name)
 | 
			
		||||
{
 | 
			
		||||
@ -509,16 +521,7 @@ BaseRuntime::GetFunctionByName(const char *public_name)
 | 
			
		||||
  if (FindPublicByName(public_name, &index) != SP_ERROR_NONE)
 | 
			
		||||
    return NULL;
 | 
			
		||||
 | 
			
		||||
  CFunction *pFunc = m_PubFuncs[index];
 | 
			
		||||
  if (!pFunc) {
 | 
			
		||||
    sp_public_t *pub = NULL;
 | 
			
		||||
    GetPublicByIndex(index, &pub);
 | 
			
		||||
    if (pub)
 | 
			
		||||
      m_PubFuncs[index] = new CFunction(this, (index << 1) | 1, index);
 | 
			
		||||
    pFunc = m_PubFuncs[index];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return pFunc;
 | 
			
		||||
  return GetPublicFunction(index);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool BaseRuntime::IsDebugging()
 | 
			
		||||
@ -594,7 +597,6 @@ BaseRuntime::CreateBlank(uint32_t heastk)
 | 
			
		||||
  m_plugin.mem_size = heastk;
 | 
			
		||||
  m_plugin.memory = new uint8_t[heastk];
 | 
			
		||||
 | 
			
		||||
  m_plugin.profiler = g_engine2.GetProfiler();
 | 
			
		||||
  m_pCtx = new BaseContext(this);
 | 
			
		||||
  co_ = g_Jit.StartCompilation(this);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -71,6 +71,7 @@ class BaseRuntime
 | 
			
		||||
  void AddJittedFunction(JitFunction *fn);
 | 
			
		||||
  void SetName(const char *name);
 | 
			
		||||
  unsigned GetNativeReplacement(size_t index);
 | 
			
		||||
  CFunction *GetPublicFunction(size_t index);
 | 
			
		||||
 | 
			
		||||
  BaseContext *GetBaseContext();
 | 
			
		||||
  const sp_plugin_t *plugin() const {
 | 
			
		||||
 | 
			
		||||
@ -14,7 +14,7 @@ using namespace SourcePawn;
 | 
			
		||||
 | 
			
		||||
SourcePawnEngine2::SourcePawnEngine2()
 | 
			
		||||
{
 | 
			
		||||
	m_Profiler = NULL;
 | 
			
		||||
	profiler_ = NULL;
 | 
			
		||||
	jit_enabled_ = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -147,7 +147,7 @@ void SourcePawnEngine2::DestroyFakeNative(SPVM_NATIVE_FUNC func)
 | 
			
		||||
 | 
			
		||||
const char *SourcePawnEngine2::GetEngineName()
 | 
			
		||||
{
 | 
			
		||||
	return "SourcePawn 1.2, jit-x86";
 | 
			
		||||
	return "SourcePawn 1.3, jit-x86";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *SourcePawnEngine2::GetVersionString()
 | 
			
		||||
@ -155,16 +155,6 @@ const char *SourcePawnEngine2::GetVersionString()
 | 
			
		||||
	return SOURCEMOD_VERSION;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
IProfiler *SourcePawnEngine2::GetProfiler()
 | 
			
		||||
{
 | 
			
		||||
	return m_Profiler;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SourcePawnEngine2::SetProfiler(IProfiler *profiler)
 | 
			
		||||
{
 | 
			
		||||
	m_Profiler = profiler;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
IDebugListener *SourcePawnEngine2::SetDebugListener(IDebugListener *listener)
 | 
			
		||||
{
 | 
			
		||||
	return g_engine1.SetDebugListener(listener);
 | 
			
		||||
 | 
			
		||||
@ -21,7 +21,6 @@ namespace SourcePawn
 | 
			
		||||
		SPVM_NATIVE_FUNC CreateFakeNative(SPVM_FAKENATIVE_FUNC callback, void *pData);
 | 
			
		||||
		void DestroyFakeNative(SPVM_NATIVE_FUNC func);
 | 
			
		||||
		IDebugListener *SetDebugListener(IDebugListener *listener);
 | 
			
		||||
		void SetProfiler(IProfiler *profiler);
 | 
			
		||||
		ICompilation *StartCompilation();
 | 
			
		||||
		const char *GetErrorString(int err);
 | 
			
		||||
		bool Initialize();
 | 
			
		||||
@ -37,14 +36,51 @@ namespace SourcePawn
 | 
			
		||||
		bool IsJitEnabled() {
 | 
			
		||||
			return jit_enabled_;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void SetProfiler(IProfiler *profiler) {
 | 
			
		||||
			// Deprecated.
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		void EnableProfiling() {
 | 
			
		||||
			profiling_enabled_ = !!profiler_;
 | 
			
		||||
		}
 | 
			
		||||
		void DisableProfiling() {
 | 
			
		||||
			profiling_enabled_ = false;
 | 
			
		||||
		}
 | 
			
		||||
		bool IsProfilingEnabled() {
 | 
			
		||||
			return profiling_enabled_;
 | 
			
		||||
		}
 | 
			
		||||
		void SetProfilingTool(IProfilingTool *tool) {
 | 
			
		||||
			profiler_ = tool;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	public:
 | 
			
		||||
		IProfiler *GetProfiler();
 | 
			
		||||
		IProfilingTool *GetProfiler() {
 | 
			
		||||
			return profiler_;
 | 
			
		||||
		}
 | 
			
		||||
	private:
 | 
			
		||||
		IProfiler *m_Profiler;
 | 
			
		||||
		IProfilingTool *profiler_;
 | 
			
		||||
		bool jit_enabled_;
 | 
			
		||||
		bool profiling_enabled_;
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extern SourcePawn::SourcePawnEngine2 g_engine2;
 | 
			
		||||
 | 
			
		||||
class EnterProfileScope
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	EnterProfileScope(const char *group, const char *name)
 | 
			
		||||
	{
 | 
			
		||||
		if (g_engine2.IsProfilingEnabled())
 | 
			
		||||
			g_engine2.GetProfiler()->EnterScope(group, name);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	~EnterProfileScope()
 | 
			
		||||
	{
 | 
			
		||||
		if (g_engine2.IsProfilingEnabled())
 | 
			
		||||
			g_engine2.GetProfiler()->LeaveScope();
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif //_INCLUDE_SOURCEPAWN_ENGINE_2_H_
 | 
			
		||||
 | 
			
		||||
@ -59,7 +59,6 @@ namespace SourcePawn
 | 
			
		||||
		uint32_t	num_pubvars;	/**< Number of public variables */
 | 
			
		||||
		sp_native_t	*natives;		/**< Natives table */
 | 
			
		||||
		uint32_t	num_natives;	/**< Number of natives */
 | 
			
		||||
		IProfiler *profiler;		/**< Pointer to IProfiler */
 | 
			
		||||
		uint32_t	prof_flags;		/**< Profiling flags */
 | 
			
		||||
		uint32_t	run_flags;		/**< Runtime flags */
 | 
			
		||||
		uint32_t	pcode_version;	/**< P-Code version number */
 | 
			
		||||
 | 
			
		||||
@ -549,65 +549,47 @@ int BaseContext::Execute2(IPluginFunction *function, const cell_t *params, unsig
 | 
			
		||||
	int ir;
 | 
			
		||||
	int serial;
 | 
			
		||||
	cell_t *sp;
 | 
			
		||||
	funcid_t fnid;
 | 
			
		||||
	JitFunction *fn;
 | 
			
		||||
	sp_public_t *pubfunc;
 | 
			
		||||
	cell_t _ignore_result;
 | 
			
		||||
	unsigned int public_id;
 | 
			
		||||
 | 
			
		||||
	fnid = function->GetFunctionID();
 | 
			
		||||
	EnterProfileScope profileScope("SourcePawn", "EnterJIT");
 | 
			
		||||
 | 
			
		||||
	if (!g_WatchdogTimer.HandleInterrupt())
 | 
			
		||||
		return SP_ERROR_TIMEOUT;
 | 
			
		||||
 | 
			
		||||
	if (fnid & 1)
 | 
			
		||||
	{	
 | 
			
		||||
		public_id = fnid >> 1;
 | 
			
		||||
 | 
			
		||||
		if (m_pRuntime->GetPublicByIndex(public_id, &pubfunc) != SP_ERROR_NONE)
 | 
			
		||||
		{
 | 
			
		||||
			return SP_ERROR_NOT_FOUND;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
	funcid_t fnid = function->GetFunctionID();
 | 
			
		||||
	if (!(fnid & 1))
 | 
			
		||||
		return SP_ERROR_INVALID_ADDRESS;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	unsigned public_id = fnid >> 1;
 | 
			
		||||
	CFunction *cfun = m_pRuntime->GetPublicFunction(public_id);
 | 
			
		||||
	if (!cfun)
 | 
			
		||||
		return SP_ERROR_NOT_FOUND;
 | 
			
		||||
 | 
			
		||||
	if (m_pRuntime->IsPaused())
 | 
			
		||||
	{
 | 
			
		||||
		return SP_ERROR_NOT_RUNNABLE;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ((cell_t)(m_ctx.hp + 16*sizeof(cell_t)) > (cell_t)(m_ctx.sp - (sizeof(cell_t) * (num_params + 1))))
 | 
			
		||||
	{
 | 
			
		||||
		return SP_ERROR_STACKLOW;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (result == NULL)
 | 
			
		||||
	{
 | 
			
		||||
		result = &_ignore_result;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* We got this far.  It's time to start profiling. */
 | 
			
		||||
	
 | 
			
		||||
	if ((m_pRuntime->plugin()->prof_flags & SP_PROF_CALLBACKS) == SP_PROF_CALLBACKS)
 | 
			
		||||
	{
 | 
			
		||||
		serial = m_pRuntime->plugin()->profiler->OnCallbackBegin(this, pubfunc);
 | 
			
		||||
	}
 | 
			
		||||
	EnterProfileScope scriptScope("SourcePawn", cfun->FullName());
 | 
			
		||||
 | 
			
		||||
	/* See if we have to compile the callee. */
 | 
			
		||||
	if (g_engine2.IsJitEnabled() && (fn = m_pRuntime->m_PubJitFuncs[public_id]) == NULL)
 | 
			
		||||
	{
 | 
			
		||||
		/* We might not have to - check pcode offset. */
 | 
			
		||||
		fn = m_pRuntime->GetJittedFunctionByOffset(pubfunc->code_offs);
 | 
			
		||||
		fn = m_pRuntime->GetJittedFunctionByOffset(cfun->Public()->code_offs);
 | 
			
		||||
		if (fn)
 | 
			
		||||
		{
 | 
			
		||||
			m_pRuntime->m_PubJitFuncs[public_id] = fn;
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			if ((fn = g_Jit.CompileFunction(m_pRuntime, pubfunc->code_offs, &ir)) == NULL)
 | 
			
		||||
			if ((fn = g_Jit.CompileFunction(m_pRuntime, cfun->Public()->code_offs, &ir)) == NULL)
 | 
			
		||||
			{
 | 
			
		||||
				return ir;
 | 
			
		||||
			}
 | 
			
		||||
@ -651,7 +633,7 @@ int BaseContext::Execute2(IPluginFunction *function, const cell_t *params, unsig
 | 
			
		||||
	if (g_engine2.IsJitEnabled())
 | 
			
		||||
		ir = g_Jit.InvokeFunction(m_pRuntime, fn, result);
 | 
			
		||||
	else
 | 
			
		||||
		ir = Interpret(m_pRuntime, pubfunc->code_offs, result);
 | 
			
		||||
		ir = Interpret(m_pRuntime, cfun->Public()->code_offs, result);
 | 
			
		||||
 | 
			
		||||
	/* Restore some states, stop the frame tracer */
 | 
			
		||||
 | 
			
		||||
@ -695,11 +677,6 @@ int BaseContext::Execute2(IPluginFunction *function, const cell_t *params, unsig
 | 
			
		||||
	m_ctx.hp = save_hp;
 | 
			
		||||
	m_ctx.rp = save_rp;
 | 
			
		||||
	
 | 
			
		||||
	if ((m_pRuntime->plugin()->prof_flags & SP_PROF_CALLBACKS) == SP_PROF_CALLBACKS)
 | 
			
		||||
	{
 | 
			
		||||
		m_pRuntime->plugin()->profiler->OnCallbackEnd(serial);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	m_ctx.cip = save_cip;
 | 
			
		||||
	m_ctx.n_idx = save_n_idx;
 | 
			
		||||
	m_ctx.n_err = SP_ERROR_NONE;
 | 
			
		||||
 | 
			
		||||
@ -36,12 +36,9 @@
 | 
			
		||||
* FUNCTION CALLING *
 | 
			
		||||
********************/
 | 
			
		||||
 | 
			
		||||
void CFunction::Set(BaseRuntime *runtime, funcid_t fnid, uint32_t pub_id)
 | 
			
		||||
CFunction::~CFunction()
 | 
			
		||||
{
 | 
			
		||||
	m_pRuntime = runtime;
 | 
			
		||||
	m_curparam = 0;
 | 
			
		||||
	m_errorstate = SP_ERROR_NONE;
 | 
			
		||||
	m_FnId = fnid;
 | 
			
		||||
	delete [] full_name_;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool CFunction::IsRunnable()
 | 
			
		||||
@ -68,6 +65,16 @@ CFunction::CFunction(BaseRuntime *runtime, funcid_t id, uint32_t pub_id) :
 | 
			
		||||
	m_curparam(0), m_errorstate(SP_ERROR_NONE), m_FnId(id)
 | 
			
		||||
{
 | 
			
		||||
	m_pRuntime = runtime;
 | 
			
		||||
 | 
			
		||||
	runtime->GetPublicByIndex(pub_id, &public_);
 | 
			
		||||
 | 
			
		||||
	size_t rt_len = strlen(runtime->plugin()->name);
 | 
			
		||||
	size_t len = rt_len + strlen("::") + strlen(public_->name);
 | 
			
		||||
 | 
			
		||||
	full_name_ = new char[len + 1];
 | 
			
		||||
	strcpy(full_name_, runtime->plugin()->name);
 | 
			
		||||
	strcpy(&full_name_[rt_len], "::");
 | 
			
		||||
	strcpy(&full_name_[rt_len + 2], public_->name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int CFunction::PushCell(cell_t cell)
 | 
			
		||||
 | 
			
		||||
@ -61,6 +61,7 @@ public:
 | 
			
		||||
	CFunction(BaseRuntime *pRuntime, 
 | 
			
		||||
			  funcid_t fnid,
 | 
			
		||||
			  uint32_t pub_id);
 | 
			
		||||
	~CFunction();
 | 
			
		||||
public:
 | 
			
		||||
	virtual int PushCell(cell_t cell);
 | 
			
		||||
	virtual int PushCellByRef(cell_t *cell, int flags);
 | 
			
		||||
@ -82,7 +83,12 @@ public:
 | 
			
		||||
		cell_t *result);
 | 
			
		||||
	IPluginRuntime *GetParentRuntime();
 | 
			
		||||
public:
 | 
			
		||||
	void Set(BaseRuntime *runtime, funcid_t fnid, uint32_t pub_id);
 | 
			
		||||
	const char *FullName() const {
 | 
			
		||||
		return full_name_;
 | 
			
		||||
	}
 | 
			
		||||
	sp_public_t *Public() const {
 | 
			
		||||
		return public_;
 | 
			
		||||
	}
 | 
			
		||||
private:
 | 
			
		||||
	int _PushString(const char *string, int sz_flags, int cp_flags, size_t len);
 | 
			
		||||
	int SetError(int err);
 | 
			
		||||
@ -93,6 +99,8 @@ private:
 | 
			
		||||
	unsigned int m_curparam;
 | 
			
		||||
	int m_errorstate;
 | 
			
		||||
	funcid_t m_FnId;
 | 
			
		||||
	char *full_name_;
 | 
			
		||||
	sp_public_t *public_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif //_INCLUDE_SOURCEMOD_BASEFUNCTION_H_
 | 
			
		||||
 | 
			
		||||
@ -1950,7 +1950,6 @@ JITX86::SetupContextVars(BaseRuntime *runtime, BaseContext *pCtx, sp_context_t *
 | 
			
		||||
  ctx->tracker->pCur = ctx->tracker->pBase;
 | 
			
		||||
  ctx->tracker->size = 1024 / sizeof(cell_t);
 | 
			
		||||
  ctx->basecx = pCtx;
 | 
			
		||||
  ctx->vm[JITVARS_PROFILER] = g_engine2.GetProfiler();
 | 
			
		||||
  ctx->plugin = const_cast<sp_plugin_t *>(runtime->plugin());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user