Add FrameIterator to SourceMod (#716).

* Create FrameIterator type
This commit adds the FrameIterator type to core sm along with a few
methods around getting the information for each frame.

* Fix incorrect documentation
[skip ci]
* Implement KyleS's Changes
* A nit
This commit is contained in:
Michael Flaherty 2017-11-16 18:55:37 -08:00 committed by Kyle Sanderson
parent 7507672895
commit 43cdf20fd3
5 changed files with 364 additions and 3 deletions

View File

@ -82,6 +82,7 @@ binary.sources += [
'CDataPack.cpp',
'frame_tasks.cpp',
'smn_halflife.cpp',
'FrameIterator.cpp',
]
if builder.target.platform == 'windows':
binary.sources += ['thread/WinThreads.cpp']

View File

@ -0,0 +1,96 @@
/**
* vim: set ts=4 sw=4 tw=99 noet :
* =============================================================================
* SourceMod
* Copyright (C) 2004-2017 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 "FrameIterator.h"
SafeFrameIterator::SafeFrameIterator(IFrameIterator *it)
{
while (!it->Done())
{
FrameInfo info = FrameInfo(it);
frames.append(info);
it->Next();
}
it->Reset();
current = 0;
}
bool SafeFrameIterator::Done() const
{
return current == frames.length();
}
bool SafeFrameIterator::Next()
{
if (!this->Done())
{
current++;
return true;
}
return false;
}
void SafeFrameIterator::Reset()
{
current = 0;
}
int SafeFrameIterator::LineNumber() const
{
if (this->Done())
{
return -1;
}
return (int)frames[current].LineNumber;
}
const char *SafeFrameIterator::FunctionName() const
{
if (this->Done())
{
return NULL;
}
return frames[current].FunctionName.chars();
}
const char *SafeFrameIterator::FilePath() const
{
if (this->Done())
{
return NULL;
}
return frames[current].FilePath.chars();
}

View File

@ -0,0 +1,77 @@
/**
* vim: set ts=4 sw=4 tw=99 noet :
* =============================================================================
* SourceMod
* Copyright (C) 2004-2017 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 "sp_vm_api.h"
#include <am-vector.h>
#include <am-string.h>
using namespace SourcePawn;
/**
* Frame iterator cache which is safe for plugins to hold handles to,
* unlike what you'd recieve from IPluginContext::CreateFrameIterator.
*/
class SafeFrameIterator
{
public:
/**
* Struct which holds all of the cached values for each individual frame.
*/
struct FrameInfo
{
ke::AString FunctionName;
ke::AString FilePath;
unsigned LineNumber;
FrameInfo(IFrameIterator *it)
{
LineNumber = it->LineNumber();
FunctionName = it->FunctionName();
FilePath = it->FilePath();
}
};
SafeFrameIterator(IFrameIterator *);
bool Done() const;
bool Next();
void Reset();
int LineNumber() const;
const char *FunctionName() const;
const char *FilePath() const;
private:
size_t current;
ke::Vector<FrameInfo> frames;
};

View File

@ -2,7 +2,7 @@
* vim: set ts=4 sw=4 tw=99 noet :
* =============================================================================
* SourceMod
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
* Copyright (C) 2004-2017 AlliedModders LLC. All rights reserved.
* =============================================================================
*
* This program is free software; you can redistribute it and/or modify it under
@ -37,6 +37,8 @@
#include <ISourceMod.h>
#include <ITranslator.h>
#include <DebugReporter.h>
#include <FrameIterator.h>
#include <sourcehook.h>
#include <sh_memory.h>
@ -59,6 +61,7 @@ using namespace SourcePawn;
HandleType_t g_PlIter;
HandleType_t g_FrameIter;
IForward *g_OnLogAction = NULL;
@ -76,6 +79,7 @@ public:
hacc.access[HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY|HANDLE_RESTRICT_OWNER;
g_PlIter = handlesys->CreateType("PluginIterator", this, 0, NULL, NULL, g_pCoreIdent, NULL);
g_FrameIter = handlesys->CreateType("FrameIterator", this, 0, NULL, NULL, g_pCoreIdent, NULL);
g_OnLogAction = forwardsys->CreateForward("OnLogAction",
ET_Hook,
@ -90,14 +94,22 @@ public:
sm_datetime_format = bridge->FindConVar("sm_datetime_format");
}
void OnHandleDestroy(HandleType_t type, void *object)
{
if (type == g_FrameIter)
{
delete (SafeFrameIterator *) object;
}
else if (type == g_PlIter)
{
IPluginIterator *iter = (IPluginIterator *)object;
iter->Release();
}
}
void OnSourceModShutdown()
{
forwardsys->ReleaseForward(g_OnLogAction);
handlesys->RemoveType(g_PlIter, g_pCoreIdent);
handlesys->RemoveType(g_FrameIter, g_pCoreIdent);
}
} g_CoreNativeHelpers;
@ -780,6 +792,143 @@ static cell_t IsNullString(IPluginContext *pContext, const cell_t *params)
return str == nullptr;
}
static cell_t FrameIterator_Create(IPluginContext *pContext, const cell_t *params)
{
IFrameIterator *it = pContext->CreateFrameIterator();
SafeFrameIterator *iterator = new SafeFrameIterator(it);
pContext->DestroyFrameIterator(it);
Handle_t handle = handlesys->CreateHandle(g_FrameIter, iterator, pContext->GetIdentity(), g_pCoreIdent, NULL);
if (handle == BAD_HANDLE)
{
delete iterator;
return BAD_HANDLE;
}
return handle;
}
static cell_t FrameIterator_Next(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl = (Handle_t)params[1];
HandleError err;
SafeFrameIterator *iterator;
HandleSecurity sec;
sec.pIdentity = g_pCoreIdent;
sec.pOwner = pContext->GetIdentity();
if ((err=handlesys->ReadHandle(hndl, g_FrameIter, &sec, (void **)&iterator)) != HandleError_None)
{
return pContext->ThrowNativeError("Could not read Handle %x (error %d)", hndl, err);
}
return iterator->Next();
}
static cell_t FrameIterator_Reset(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl = (Handle_t)params[1];
HandleError err;
SafeFrameIterator *iterator;
HandleSecurity sec;
sec.pIdentity = g_pCoreIdent;
sec.pOwner = pContext->GetIdentity();
if ((err=handlesys->ReadHandle(hndl, g_FrameIter, &sec, (void **)&iterator)) != HandleError_None)
{
return pContext->ThrowNativeError("Could not read Handle %x (error %d)", hndl, err);
}
iterator->Reset();
return 0;
}
static cell_t FrameIterator_LineNumber(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl = (Handle_t)params[1];
HandleError err;
SafeFrameIterator *iterator;
HandleSecurity sec;
sec.pIdentity = g_pCoreIdent;
sec.pOwner = pContext->GetIdentity();
if ((err=handlesys->ReadHandle(hndl, g_FrameIter, &sec, (void **)&iterator)) != HandleError_None)
{
return pContext->ThrowNativeError("Could not read Handle %x (error %d)", hndl, err);
}
int lineNum = iterator->LineNumber();
if (lineNum < 0)
{
return pContext->ThrowNativeError("Iterator out of bounds. Check return value of FrameIterator.Next");
}
return lineNum;
}
static cell_t FrameIterator_GetFunctionName(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl = (Handle_t)params[1];
HandleError err;
SafeFrameIterator *iterator;
HandleSecurity sec;
sec.pIdentity = g_pCoreIdent;
sec.pOwner = pContext->GetIdentity();
if ((err=handlesys->ReadHandle(hndl, g_FrameIter, &sec, (void **)&iterator)) != HandleError_None)
{
return pContext->ThrowNativeError("Could not read Handle %x (error %d)", hndl, err);
}
const char* functionName = iterator->FunctionName();
if (!functionName)
{
return pContext->ThrowNativeError("Iterator out of bounds. Check return value of FrameIterator.Next");
}
char* buffer;
pContext->LocalToString(params[2], &buffer);
size_t size = static_cast<size_t>(params[3]);
ke::SafeStrcpy(buffer, size, functionName);
return 0;
}
static cell_t FrameIterator_GetFilePath(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl = (Handle_t)params[1];
HandleError err;
SafeFrameIterator *iterator;
HandleSecurity sec;
sec.pIdentity = g_pCoreIdent;
sec.pOwner = pContext->GetIdentity();
if ((err=handlesys->ReadHandle(hndl, g_FrameIter, &sec, (void **)&iterator)) != HandleError_None)
{
return pContext->ThrowNativeError("Could not read Handle %x (error %d)", hndl, err);
}
const char* filePath = iterator->FilePath();
if (!filePath)
{
return pContext->ThrowNativeError("Iterator out of bounds. Check return value of FrameIterator.Next");
}
char* buffer;
pContext->LocalToString(params[2], &buffer);
size_t size = static_cast<size_t>(params[3]);
ke::SafeStrcpy(buffer, size, filePath);
return 0;
}
REGISTER_NATIVES(coreNatives)
{
{"ThrowError", ThrowError},
@ -810,5 +959,12 @@ REGISTER_NATIVES(coreNatives)
{"StoreToAddress", StoreToAddress},
{"IsNullVector", IsNullVector},
{"IsNullString", IsNullString},
{"FrameIterator.FrameIterator", FrameIterator_Create},
{"FrameIterator.Next", FrameIterator_Next},
{"FrameIterator.Reset", FrameIterator_Reset},
{"FrameIterator.LineNumber.get", FrameIterator_LineNumber},
{"FrameIterator.GetFunctionName", FrameIterator_GetFunctionName},
{"FrameIterator.GetFilePath", FrameIterator_GetFilePath},
{NULL, NULL},
};

View File

@ -645,6 +645,37 @@ native int LoadFromAddress(Address addr, NumberType size);
*/
native void StoreToAddress(Address addr, int data, NumberType size);
methodmap FrameIterator < Handle {
// Creates a stack frame iterator to build your own stack traces.
// @return New handle to a FrameIterator.
public native FrameIterator();
// Advances the iterator to the next stack frame.
// @return True if another frame was fetched and data can be successfully read.
// @error No next element exception.
public native bool Next();
// Resets the iterator back to it's starting position.
public native void Reset();
// Returns the line number of the current function call.
property bool LineNumber {
public native get();
}
// Gets the name of the current function in the call stack.
//
// @param buffer Buffer to copy to.
// @param maxlen Max size of the buffer.
public native void GetFunctionName(char[] buffer, int maxlen);
// Gets the file path to the current call in the call stack.
//
// @param buffer Buffer to copy to.
// @param maxlen Max size of the buffer.
public native void GetFilePath(char[] buffer, int maxlen);
}
#include <helpers>
#include <entity>
#include <entity_prop_stocks>