diff --git a/core/logic/AMBuilder b/core/logic/AMBuilder index 08d086b3..3e90dc55 100644 --- a/core/logic/AMBuilder +++ b/core/logic/AMBuilder @@ -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'] diff --git a/core/logic/FrameIterator.cpp b/core/logic/FrameIterator.cpp new file mode 100644 index 00000000..f2a939c0 --- /dev/null +++ b/core/logic/FrameIterator.cpp @@ -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 . + * + * 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 . + * + * 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(); +} diff --git a/core/logic/FrameIterator.h b/core/logic/FrameIterator.h new file mode 100644 index 00000000..b0c2ce17 --- /dev/null +++ b/core/logic/FrameIterator.h @@ -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 . + * + * 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 . + * + * Version: $Id$ + */ + +#include "sp_vm_api.h" +#include +#include + +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 frames; +}; \ No newline at end of file diff --git a/core/logic/smn_core.cpp b/core/logic/smn_core.cpp index d12e0fca..06527d7e 100644 --- a/core/logic/smn_core.cpp +++ b/core/logic/smn_core.cpp @@ -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 #include +#include +#include #include #include @@ -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, @@ -91,13 +95,21 @@ public: } void OnHandleDestroy(HandleType_t type, void *object) { - IPluginIterator *iter = (IPluginIterator *)object; - iter->Release(); + 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(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(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}, }; diff --git a/plugins/include/sourcemod.inc b/plugins/include/sourcemod.inc index 023e3af5..3b188630 100644 --- a/plugins/include/sourcemod.inc +++ b/plugins/include/sourcemod.inc @@ -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 #include #include