mirror of
https://github.com/roflmuffin/CounterStrikeSharp.git
synced 2025-12-05 15:40:24 -08:00
Improve FunctionReference trace logging with real user stack origin (#895)
Co-authored-by: Michael Wilson <roflmuffin@users.noreply.github.com> Co-authored-by: root <root@ns3203586.ip-146-59-53.eu>
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
"name": "SteamRT Sniper SDK",
|
||||
"image": "registry.gitlab.steamos.cloud/steamrt/sniper/sdk:latest",
|
||||
"updateContentCommand": "git submodule update --init --recursive",
|
||||
"postCreateCommand": "cmake -S . -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo && cmake --build build -j$(nproc)",
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"extensions": [
|
||||
@@ -16,4 +17,4 @@
|
||||
"features": {
|
||||
"ghcr.io/devcontainers/features/dotnet": "8.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
17
Dockerfile
Normal file
17
Dockerfile
Normal file
@@ -0,0 +1,17 @@
|
||||
FROM registry.gitlab.steamos.cloud/steamrt/sniper/sdk:latest
|
||||
|
||||
WORKDIR /workspace
|
||||
|
||||
RUN apt update && apt install -y \
|
||||
clang-16 \
|
||||
cmake \
|
||||
ninja-build \
|
||||
git \
|
||||
zlib1g-dev \
|
||||
libssl-dev \
|
||||
libprotobuf-dev \
|
||||
protobuf-compiler \
|
||||
pkg-config \
|
||||
curl && \
|
||||
ln -sf /usr/bin/clang-16 /usr/bin/clang && \
|
||||
ln -sf /usr/bin/clang++-16 /usr/bin/clang++
|
||||
@@ -267,10 +267,17 @@
|
||||
"linux": 0
|
||||
}
|
||||
},
|
||||
"CheckTransmit": {
|
||||
"signatures": {
|
||||
"library": "server",
|
||||
"windows": "48 8B C4 4C 89 48 ? 44 89 40 ? 48 89 50 ? 48 89 48 ? 55",
|
||||
"linux": "55 48 89 E5 41 57 49 89 CF 41 56 41 55 41 54 53 48 81 EC"
|
||||
}
|
||||
},
|
||||
"CheckTransmitPlayerSlot": {
|
||||
"offsets": {
|
||||
"windows": 584,
|
||||
"linux": 584
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
@@ -26,7 +27,7 @@ namespace CounterStrikeSharp.API.Core
|
||||
/// </summary>
|
||||
public enum FunctionLifetime
|
||||
{
|
||||
/// <summary>Delegate will be removed after the first invocation.</summary>
|
||||
/// <summary>Delegate will be removed after the first invocation.</summary>
|
||||
SingleUse,
|
||||
|
||||
/// <summary>Delegate will remain in memory for the lifetime of the application (or until <see cref="FunctionReference.Remove"/> is called).</summary>
|
||||
@@ -57,7 +58,7 @@ namespace CounterStrikeSharp.API.Core
|
||||
_targetMethod = method;
|
||||
_nativeCallback = CreateWrappedCallback();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="FunctionLifetime"/>
|
||||
/// </summary>
|
||||
@@ -73,6 +74,25 @@ namespace CounterStrikeSharp.API.Core
|
||||
|
||||
private unsafe CallbackDelegate CreateWrappedCallback()
|
||||
{
|
||||
var methodName = _targetMethod.Method.DeclaringType?.FullName + "." + _targetMethod.Method.Name;
|
||||
var profileName = "ScriptCallback::Execute::" + _targetMethod.Method.Name;
|
||||
|
||||
var stackTrace = new StackTrace(2, true);
|
||||
var firstUserFrame = stackTrace.GetFrames()?.FirstOrDefault(frame =>
|
||||
{
|
||||
var declaring = frame.GetMethod()?.DeclaringType?.FullName;
|
||||
return declaring != null &&
|
||||
!declaring.StartsWith("CounterStrikeSharp") &&
|
||||
!declaring.Contains("SafeExecutor") &&
|
||||
!declaring.Contains("FunctionReference");
|
||||
});
|
||||
|
||||
string caller = firstUserFrame != null
|
||||
? $"{firstUserFrame.GetMethod()?.DeclaringType?.FullName}.{firstUserFrame.GetMethod()?.Name} @ {firstUserFrame.GetFileName()}:{firstUserFrame.GetFileLineNumber()}"
|
||||
: "Unknown (no user frame)";
|
||||
|
||||
Helpers.RegisterCallbackTrace(methodName, 1, profileName, caller);
|
||||
|
||||
return context =>
|
||||
{
|
||||
try
|
||||
@@ -176,4 +196,4 @@ namespace CounterStrikeSharp.API.Core
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,5 +29,8 @@ namespace CounterStrikeSharp.API.Core
|
||||
[SecurityCritical]
|
||||
[DllImport(dllPath, EntryPoint = "InvokeNative")]
|
||||
public static extern void InvokeNative(IntPtr ptr);
|
||||
|
||||
[DllImport(dllPath, EntryPoint = "RegisterCallbackTrace")]
|
||||
public static extern void RegisterCallbackTrace(string name, int count, string profile, string callerStack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace CounterStrikeSharp.API.Core;
|
||||
|
||||
public enum CSWeaponType : uint
|
||||
{
|
||||
WEAPONTYPE_KNIFE = 0x0,
|
||||
WEAPONTYPE_KNIFE = 0x0,
|
||||
WEAPONTYPE_PISTOL = 0x1,
|
||||
WEAPONTYPE_SUBMACHINEGUN = 0x2,
|
||||
WEAPONTYPE_RIFLE = 0x3,
|
||||
@@ -20,5 +20,12 @@ public enum CSWeaponType : uint
|
||||
WEAPONTYPE_GRENADE = 0x9,
|
||||
WEAPONTYPE_EQUIPMENT = 0xA,
|
||||
WEAPONTYPE_STACKABLEITEM = 0xB,
|
||||
WEAPONTYPE_UNKNOWN = 0xC,
|
||||
WEAPONTYPE_FISTS = 0xC,
|
||||
WEAPONTYPE_BREACHCHARGE = 0xD,
|
||||
WEAPONTYPE_BUMPMINE = 0xE,
|
||||
WEAPONTYPE_TABLET = 0xF,
|
||||
WEAPONTYPE_MELEE = 0x10,
|
||||
WEAPONTYPE_SHIELD = 0x11,
|
||||
WEAPONTYPE_ZONE_REPULSOR = 0x12,
|
||||
WEAPONTYPE_UNKNOWN = 0x13,
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security;
|
||||
using System.Text;
|
||||
|
||||
@@ -124,6 +124,9 @@ public abstract class BaseMenuInstance : IMenuInstance
|
||||
|
||||
var menuItemIndex = CurrentOffset + desiredValue - 1;
|
||||
|
||||
if (Menu?.MenuOptions == null)
|
||||
return;
|
||||
|
||||
if (menuItemIndex >= 0 && menuItemIndex < Menu.MenuOptions.Count)
|
||||
{
|
||||
var menuOption = Menu.MenuOptions[menuItemIndex];
|
||||
|
||||
@@ -100452,8 +100452,36 @@
|
||||
"value": 11
|
||||
},
|
||||
{
|
||||
"name": "WEAPONTYPE_UNKNOWN",
|
||||
"name": "WEAPONTYPE_FISTS",
|
||||
"value": 12
|
||||
},
|
||||
{
|
||||
"name": "WEAPONTYPE_BREACHCHARGE",
|
||||
"value": 13
|
||||
},
|
||||
{
|
||||
"name": "WEAPONTYPE_BUMPMINE",
|
||||
"value": 14
|
||||
},
|
||||
{
|
||||
"name": "WEAPONTYPE_TABLET",
|
||||
"value": 15
|
||||
},
|
||||
{
|
||||
"name": "WEAPONTYPE_MELEE",
|
||||
"value": 16
|
||||
},
|
||||
{
|
||||
"name": "WEAPONTYPE_SHIELD",
|
||||
"value": 17
|
||||
},
|
||||
{
|
||||
"name": "WEAPONTYPE_ZONE_REPULSOR",
|
||||
"value": 18
|
||||
},
|
||||
{
|
||||
"name": "WEAPONTYPE_UNKNOWN",
|
||||
"value": 19
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -270,8 +270,15 @@ bool EventManager::OnFireEventPost(IGameEvent* pEvent, bool bDontBroadcast)
|
||||
pCallback->ScriptContext().Push(&override);
|
||||
pCallback->Execute();
|
||||
|
||||
globals::gameEventManager->FreeEvent(pEventCopy);
|
||||
m_EventCopies.pop();
|
||||
if (pEventCopy)
|
||||
{
|
||||
globals::gameEventManager->FreeEvent(pEventCopy);
|
||||
m_EventCopies.pop();
|
||||
}
|
||||
else
|
||||
{
|
||||
CSSHARP_CORE_WARN("OnFireEventPost: pEventCopy is nullptr, cannot free event");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,33 @@
|
||||
#include "core/log.h"
|
||||
#include "vprof.h"
|
||||
|
||||
DLL_EXPORT void RegisterCallbackTrace(const char* name, size_t count, const char* profile, const char* callerStack)
|
||||
{
|
||||
// Dummy logic to prevent compiler from optimizing this function away
|
||||
volatile size_t hash = 5381;
|
||||
|
||||
if (name)
|
||||
{
|
||||
for (const char* c = name; *c; ++c)
|
||||
hash = ((hash << 5) + hash) + *c;
|
||||
}
|
||||
|
||||
if (profile)
|
||||
{
|
||||
for (const char* c = profile; *c; ++c)
|
||||
hash = ((hash << 5) + hash) + *c;
|
||||
}
|
||||
|
||||
if (callerStack)
|
||||
{
|
||||
for (int i = 0; callerStack[i] && i < 128; ++i)
|
||||
hash ^= callerStack[i];
|
||||
}
|
||||
|
||||
hash ^= count;
|
||||
(void)hash;
|
||||
}
|
||||
|
||||
namespace counterstrikesharp {
|
||||
|
||||
ScriptCallback::ScriptCallback(const char* szName) : m_root_context(fxNativeContext{})
|
||||
@@ -36,22 +63,55 @@ void ScriptCallback::AddListener(CallbackT fnPluginFunction) { m_functions.push_
|
||||
|
||||
bool ScriptCallback::RemoveListener(CallbackT fnPluginFunction)
|
||||
{
|
||||
bool bSuccess = true;
|
||||
size_t nOriginalSize = m_functions.size();
|
||||
m_functions.erase(std::ranges::remove(m_functions, fnPluginFunction).begin(), m_functions.end());
|
||||
return m_functions.size() != nOriginalSize;
|
||||
}
|
||||
|
||||
m_functions.erase(std::remove(m_functions.begin(), m_functions.end(), fnPluginFunction), m_functions.end());
|
||||
|
||||
return bSuccess;
|
||||
bool ScriptCallback::IsContextSafe()
|
||||
{
|
||||
try
|
||||
{
|
||||
auto& Ctx = ScriptContext();
|
||||
Ctx.GetResult<void*>();
|
||||
return true;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
CSSHARP_CORE_WARN("Context is invalid (exception during access)");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptCallback::Execute(bool bResetContext)
|
||||
{
|
||||
if (!IsContextSafe())
|
||||
{
|
||||
ScriptContext().ThrowNativeError("ScriptCallback::Execute aborted due to invalid context");
|
||||
CSSHARP_CORE_WARN("ScriptCallback::Execute aborted due to invalid context (callback: '{}')", m_name);
|
||||
return;
|
||||
}
|
||||
|
||||
VPROF_BUDGET(m_profile_name.c_str(), "CS# Script Callbacks");
|
||||
|
||||
for (auto fnMethodToCall : m_functions)
|
||||
for (size_t nI = 0; nI < m_functions.size(); ++nI)
|
||||
{
|
||||
if (fnMethodToCall)
|
||||
if (auto fnMethodToCall = m_functions[nI])
|
||||
{
|
||||
fnMethodToCall(&ScriptContextStruct());
|
||||
try
|
||||
{
|
||||
fnMethodToCall(&ScriptContextStruct());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
ScriptContext().ThrowNativeError("Exception in callback execution");
|
||||
CSSHARP_CORE_ERROR("Exception thrown inside callback '{}', index {}", m_name, nI);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ScriptContext().ThrowNativeError("Null listener in callback");
|
||||
CSSHARP_CORE_ERROR("Null function pointer in callback '{}', index {}", m_name, nI);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,9 +149,9 @@ ScriptCallback* CallbackManager::FindCallback(const char* szName)
|
||||
|
||||
void CallbackManager::ReleaseCallback(ScriptCallback* pCallback)
|
||||
{
|
||||
auto I = std::remove_if(m_managed.begin(), m_managed.end(), [pCallback](ScriptCallback* pI) {
|
||||
auto I = std::ranges::remove_if(m_managed, [pCallback](const ScriptCallback* pI) {
|
||||
return pCallback == pI;
|
||||
});
|
||||
}).begin();
|
||||
|
||||
if (I != m_managed.end()) m_managed.erase(I, m_managed.end());
|
||||
delete pCallback;
|
||||
@@ -99,8 +159,7 @@ void CallbackManager::ReleaseCallback(ScriptCallback* pCallback)
|
||||
|
||||
bool CallbackManager::TryAddFunction(const char* szName, CallbackT fnCallable)
|
||||
{
|
||||
auto* pCallback = FindCallback(szName);
|
||||
if (pCallback)
|
||||
if (auto* pCallback = FindCallback(szName))
|
||||
{
|
||||
pCallback->AddListener(fnCallable);
|
||||
return true;
|
||||
@@ -111,8 +170,7 @@ bool CallbackManager::TryAddFunction(const char* szName, CallbackT fnCallable)
|
||||
|
||||
bool CallbackManager::TryRemoveFunction(const char* szName, CallbackT fnCallable)
|
||||
{
|
||||
auto* pCallback = FindCallback(szName);
|
||||
if (pCallback)
|
||||
if (auto* pCallback = FindCallback(szName))
|
||||
{
|
||||
return pCallback->RemoveListener(fnCallable);
|
||||
}
|
||||
|
||||
@@ -31,8 +31,9 @@ class ScriptCallback
|
||||
~ScriptCallback();
|
||||
void AddListener(CallbackT fnPluginFunction);
|
||||
bool RemoveListener(CallbackT fnPluginFunction);
|
||||
bool IsContextSafe();
|
||||
std::string GetName() { return m_name; }
|
||||
unsigned int GetFunctionCount() { return m_functions.size(); }
|
||||
unsigned int GetFunctionCount() const { return m_functions.size(); }
|
||||
std::vector<CallbackT> GetFunctions() { return m_functions; }
|
||||
|
||||
void Execute(bool bResetContext = true);
|
||||
@@ -72,7 +73,6 @@ class CallbackPair
|
||||
~CallbackPair();
|
||||
bool HasCallbacks() const { return pre->GetFunctionCount() > 0 || post->GetFunctionCount() > 0; }
|
||||
|
||||
public:
|
||||
ScriptCallback* pre;
|
||||
ScriptCallback* post;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user