From 2e5b0e119bc6f87218156fb76254705c8528346b Mon Sep 17 00:00:00 2001 From: amy Date: Sat, 7 Dec 2024 22:09:12 +0000 Subject: [PATCH 01/24] fucked up somewhere --- Directory.Packages.props | 11 +- OpenDreamRuntime/DreamManager.cs | 22 ++- OpenDreamRuntime/DreamThread.cs | 26 ++- OpenDreamRuntime/OpenDreamRuntime.csproj | 1 + OpenDreamRuntime/Procs/AsyncNativeProc.cs | 2 + OpenDreamRuntime/Procs/DMProc.cs | 7 + OpenDreamRuntime/Procs/InitDreamObject.cs | 4 +- OpenDreamRuntime/Profile.cs | 222 ++++++++++++++++++++++ 8 files changed, 281 insertions(+), 14 deletions(-) create mode 100644 OpenDreamRuntime/Profile.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index 9e2d5a099a..5a7d08bce0 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -1,7 +1,8 @@ - - - - - + + + + + + \ No newline at end of file diff --git a/OpenDreamRuntime/DreamManager.cs b/OpenDreamRuntime/DreamManager.cs index 20aacd3428..2c57d382a1 100644 --- a/OpenDreamRuntime/DreamManager.cs +++ b/OpenDreamRuntime/DreamManager.cs @@ -102,13 +102,21 @@ public void Shutdown() { public void Update() { if (!Initialized) return; - - _procScheduler.Process(); - UpdateStat(); - _dreamMapManager.UpdateTiles(); - DreamObjectSavefile.FlushAllUpdates(); - WorldInstance.SetVariableValue("cpu", WorldInstance.GetVariable("tick_usage")); - ProcessDelQueue(); + using (Profiler.BeginZone("Tick")) + { + using (Profiler.BeginZone("DM Execution")) + _procScheduler.Process(); + using (Profiler.BeginZone("Map Update")){ + UpdateStat(); + _dreamMapManager.UpdateTiles(); + } + using (Profiler.BeginZone("Disk IO")) + DreamObjectSavefile.FlushAllUpdates(); + WorldInstance.SetVariableValue("cpu", WorldInstance.GetVariable("tick_usage")); + using (Profiler.BeginZone("Deletion Queue")) + ProcessDelQueue(); + } + Profiler.EmitFrameMark(); } public void ProcessDelQueue() { diff --git a/OpenDreamRuntime/DreamThread.cs b/OpenDreamRuntime/DreamThread.cs index c9c9212084..4b9c2e66e1 100644 --- a/OpenDreamRuntime/DreamThread.cs +++ b/OpenDreamRuntime/DreamThread.cs @@ -137,7 +137,9 @@ public DMError(string message) public abstract class ProcState : IDisposable { private static int _idCounter = 0; public int Id { get; } = ++_idCounter; - + public abstract (string SourceFile, int Line) TracyLocationId { get; } + public ProfilerZone TracyZoneId { get; set; } + public bool TracyZoned { get; set; } public DreamThread Thread { get; set; } [Access(typeof(ProcScheduler))] @@ -253,6 +255,10 @@ public DreamValue ReentrantResume(ProcState? untilState, out ProcStatus resultSt ProcStatus status; try { // _current.Resume may mutate our state!!! + if (!_current.TracyZoned && _current.Proc != null) { + _current.TracyZoneId = Profiler.BeginZone(_current.Proc.Name, filePath:_current.TracyLocationId.SourceFile, lineNumber:(uint)_current.TracyLocationId.Line);//ProfilerInternal.StartScopedZone(_current.TracyLocationId); + _current.TracyZoned = true; + } status = _current.Resume(); } catch (DMError dmError) { if (_current == null) { @@ -274,8 +280,18 @@ public DreamValue ReentrantResume(ProcState? untilState, out ProcStatus resultSt switch (status) { // The entire Thread is stopping case ProcStatus.Cancelled: + if (_current.TracyZoned) { + _current.TracyZoneId.Dispose(); + _current.TracyZoned = false; + } var current = _current; _current = null; + foreach (var s in _stack) { + if (!s.TracyZoned) + continue; + s.TracyZoneId.Dispose(); + s.TracyZoned = false; + } _stack.Clear(); resultStatus = status; return current.Result; @@ -283,6 +299,10 @@ public DreamValue ReentrantResume(ProcState? untilState, out ProcStatus resultSt // Our top-most proc just returned a value case ProcStatus.Returned: var returned = _current.Result; + if (_current.TracyZoned) { + _current.TracyZoneId.Dispose(); + _current.TracyZoned = false; + } PopProcState(); // If our stack is empty, the context has finished execution @@ -298,6 +318,10 @@ public DreamValue ReentrantResume(ProcState? untilState, out ProcStatus resultSt // The context is done executing for now case ProcStatus.Deferred: + if (_current.TracyZoned) { + _current.TracyZoneId.Dispose(); + _current.TracyZoned = false; + } // We return the current return value here even though it may not be the final result resultStatus = status; return _current.Result; diff --git a/OpenDreamRuntime/OpenDreamRuntime.csproj b/OpenDreamRuntime/OpenDreamRuntime.csproj index e6858df7e9..bf74ac73dd 100644 --- a/OpenDreamRuntime/OpenDreamRuntime.csproj +++ b/OpenDreamRuntime/OpenDreamRuntime.csproj @@ -14,6 +14,7 @@ + diff --git a/OpenDreamRuntime/Procs/AsyncNativeProc.cs b/OpenDreamRuntime/Procs/AsyncNativeProc.cs index 772921408f..02a4e43070 100644 --- a/OpenDreamRuntime/Procs/AsyncNativeProc.cs +++ b/OpenDreamRuntime/Procs/AsyncNativeProc.cs @@ -26,6 +26,8 @@ public sealed class State : ProcState { private AsyncNativeProc? _proc; public override DreamProc? Proc => _proc; + public override (string SourceFile, int Line) TracyLocationId => ("Native Proc", 0); + private Func> _taskFunc; private Task? _task; diff --git a/OpenDreamRuntime/Procs/DMProc.cs b/OpenDreamRuntime/Procs/DMProc.cs index 1ab4d5c0dd..8b8f093b56 100644 --- a/OpenDreamRuntime/Procs/DMProc.cs +++ b/OpenDreamRuntime/Procs/DMProc.cs @@ -50,6 +50,8 @@ public DMProc(int id, TreeEntry owningType, ProcDefinitionJson json, string? nam } public (string Source, int Line) GetSourceAtOffset(int offset) { + if(SourceInfo.Count == 0) + return ("whatthefuck",0); SourceInfoJson current = SourceInfo[0]; string source = ObjectTree.Strings[current.File!.Value]; @@ -151,6 +153,8 @@ public sealed class NullProcState : ProcState { public override DreamProc? Proc => _proc; + public override (string SourceFile, int Line) TracyLocationId => ("",0); + private DreamProc? _proc; public override ProcStatus Resume() { @@ -354,6 +358,9 @@ public sealed class DMProcState : ProcState { private DMProc _proc; public override DMProc Proc => _proc; + public override (string SourceFile, int Line) TracyLocationId => _proc.GetSourceAtOffset(_pc+1); + + /// Static initializer for maintainer friendly OpcodeHandlers to performance friendly _opcodeHandlers static unsafe DMProcState() { int maxOpcode = (int)_opcodeHandlers.Keys.Max(); diff --git a/OpenDreamRuntime/Procs/InitDreamObject.cs b/OpenDreamRuntime/Procs/InitDreamObject.cs index e80c67c5b4..ad205de909 100644 --- a/OpenDreamRuntime/Procs/InitDreamObject.cs +++ b/OpenDreamRuntime/Procs/InitDreamObject.cs @@ -9,7 +9,7 @@ internal sealed class InitDreamObjectState : ProcState { private readonly DreamObjectTree _objectTree; private enum Stage { - // Need to call the object's (init) proc + // Need to call the object's (init) proc Init, // Need to call IDreamMetaObject.OnObjectCreated & New @@ -42,6 +42,8 @@ public void Initialize(DreamThread thread, DreamObject dreamObject, DreamObject? public override DreamProc? Proc => null; + public override (string SourceFile, int Line) TracyLocationId => ($"new {_dreamObject.ObjectDefinition.Type}",0); + public override void AppendStackFrame(StringBuilder builder) { builder.AppendLine($"new {_dreamObject.ObjectDefinition.Type}"); } diff --git a/OpenDreamRuntime/Profile.cs b/OpenDreamRuntime/Profile.cs new file mode 100644 index 0000000000..f3ee30267e --- /dev/null +++ b/OpenDreamRuntime/Profile.cs @@ -0,0 +1,222 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using bottlenoselabs.C2CS.Runtime; +using static Tracy.PInvoke; + +public static class Profiler +{ + // Plot names need to be cached for the lifetime of the program + // seealso Tracy docs section 3.1 + private static readonly Dictionary PlotNameCache = new Dictionary(); + + /// + /// Begins a new and returns the handle to that zone. Time + /// spent inside a zone is calculated by Tracy and shown in the profiler. A zone is + /// ended when is called either automatically via + /// disposal scope rules or by calling it manually. + /// + /// A custom name for this zone. + /// Is the zone active. An inactive zone wont be shown in the profiler. + /// An RRGGBB color code that Tracy will use to color the zone in the profiler. + /// Arbitrary text associated with this zone. + /// + /// The source code line number that this zone begins at. + /// If this param is not explicitly assigned the value will provided by . + /// + /// + /// The source code file path that this zone begins at. + /// If this param is not explicitly assigned the value will provided by . + /// + /// + /// The source code member name that this zone begins at. + /// If this param is not explicitly assigned the value will provided by . + /// + /// + public static ProfilerZone BeginZone( + string? zoneName = null, + bool active = true, + uint color = 0, + string? text = null, + [CallerLineNumber] uint lineNumber = 0, + [CallerFilePath] string? filePath = null, + [CallerMemberName] string? memberName = null) + { + using var filestr = GetCString(filePath, out var fileln); + using var memberstr = GetCString(memberName, out var memberln); + using var namestr = GetCString(zoneName, out var nameln); + var srcLocId = TracyAllocSrclocName(lineNumber, filestr, fileln, memberstr, memberln, namestr, nameln, color); + var context = TracyEmitZoneBeginAlloc(srcLocId, active ? 1 : 0); + + if (text != null) + { + using var textstr = GetCString(text, out var textln); + TracyEmitZoneText(context, textstr, textln); + } + + return new ProfilerZone(context); + } + + /// + /// Configure how Tracy will display plotted values. + /// + /// + /// Name of the plot to configure. Each represents a unique plot. + /// + /// + /// Changes how the values in the plot are presented by the profiler. + /// + /// + /// Determines whether the plot will be displayed as a staircase or will smoothly change between plot points + /// + /// + /// If the the area below the plot will not be filled with a solid color. + /// + /// + /// An RRGGBB color code that Tracy will use to color the plot in the profiler. + /// + public static void PlotConfig(string name, PlotType type = PlotType.Number, bool step = false, bool fill = true, uint color = 0) + { + var namestr = GetPlotCString(name); + TracyEmitPlotConfig(namestr, (int)type, step ? 1 : 0, fill ? 1 : 0, color); + } + + /// + /// Add a value to a plot. + /// + public static void Plot(string name, double val) + { + var namestr = GetPlotCString(name); + TracyEmitPlot(namestr, val); + } + + /// + /// Add a value to a plot. + /// + public static void Plot(string name, int val) + { + var namestr = GetPlotCString(name); + TracyEmitPlotInt(namestr, val); + } + + /// + /// Add a value to a plot. + /// + public static void Plot(string name, float val) + { + var namestr = GetPlotCString(name); + TracyEmitPlotFloat(namestr, val); + } + + private static CString GetPlotCString(string name) + { + if(!PlotNameCache.TryGetValue(name, out var plotCString)) + { + plotCString = CString.FromString(name); + PlotNameCache.Add(name, plotCString); + } + return plotCString; + } + + /// + /// Emit a string that will be included along with the trace description. + /// + /// + /// Viewable in the Info tab in the profiler. + /// + public static void AppInfo(string appInfo) + { + using var infostr = GetCString(appInfo, out var infoln); + TracyEmitMessageAppinfo(infostr, infoln); + } + + + /// + /// Emit the top-level frame marker. + /// + /// + /// Tracy Cpp API and docs refer to this as the FrameMark macro. + /// + public static void EmitFrameMark() + { + TracyEmitFrameMark(null); + } + + /// + /// Is the app connected to the external profiler? + /// + /// + public static bool IsConnected() + { + return TracyConnected() != 0; + } + + /// + /// Creates a for use by Tracy. Also returns the + /// length of the string for interop convenience. + /// + public static CString GetCString(string? fromString, out ulong clength) + { + if (fromString == null) + { + clength = 0; + return new CString(0); + } + clength = (ulong)fromString.Length; + return CString.FromString(fromString); + } + + public enum PlotType + { + /// + /// Values will be displayed as plain numbers. + /// + Number = 0, + + /// + /// Treats the values as memory sizes. Will display kilobytes, megabytes, etc. + /// + Memory = 1, + + /// + /// Values will be displayed as percentage (with value 100 being equal to 100%). + /// + Percentage = 2, + } +} + +public readonly struct ProfilerZone : IDisposable +{ + public readonly TracyCZoneCtx Context; + + public uint Id => Context.Data.Id; + + public int Active => Context.Data.Active; + + internal ProfilerZone(TracyCZoneCtx context) + { + Context = context; + } + + public void EmitName(string name) + { + using var namestr = Profiler.GetCString(name, out var nameln); + TracyEmitZoneName(Context, namestr, nameln); + } + + public void EmitColor(uint color) + { + TracyEmitZoneColor(Context, color); + } + + public void EmitText(string text) + { + using var textstr = Profiler.GetCString(text, out var textln); + TracyEmitZoneText(Context, textstr, textln); + } + + public void Dispose() + { + TracyEmitZoneEnd(Context); + } +} From 8cb97aa845ad1aab2a5dda3a5b77ae7990047cf7 Mon Sep 17 00:00:00 2001 From: amy Date: Sat, 7 Dec 2024 22:51:00 +0000 Subject: [PATCH 02/24] cool now it's crashing for some reason --- OpenDreamRuntime/DreamManager.cs | 27 ++++++++++++++------------- OpenDreamRuntime/DreamThread.cs | 8 +++++++- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/OpenDreamRuntime/DreamManager.cs b/OpenDreamRuntime/DreamManager.cs index 2c57d382a1..82a1f1a12b 100644 --- a/OpenDreamRuntime/DreamManager.cs +++ b/OpenDreamRuntime/DreamManager.cs @@ -80,16 +80,17 @@ public void StartWorld() { // It is now OK to call user code, like /New procs. Initialized = true; InitializedTick = _gameTiming.CurTick; + using (Profiler.BeginZone("StartWorld", color:(uint)Color.OrangeRed.ToArgb())) { + // Call global with waitfor=FALSE + _objectTree.GlobalInitProc?.Spawn(WorldInstance, new()); - // Call global with waitfor=FALSE - _objectTree.GlobalInitProc?.Spawn(WorldInstance, new()); + // Call New() on all /area and /turf that exist, each with waitfor=FALSE separately. If created any /area, call New a SECOND TIME + // new() up /objs and /mobs from compiled-in maps [order: (1,1) then (2,1) then (1,2) then (2,2)] + _dreamMapManager.InitializeAtoms(_compiledJson.Maps); - // Call New() on all /area and /turf that exist, each with waitfor=FALSE separately. If created any /area, call New a SECOND TIME - // new() up /objs and /mobs from compiled-in maps [order: (1,1) then (2,1) then (1,2) then (2,2)] - _dreamMapManager.InitializeAtoms(_compiledJson.Maps); - - // Call world.New() - WorldInstance.SpawnProc("New"); + // Call world.New() + WorldInstance.SpawnProc("New"); + } } public void Shutdown() { @@ -102,18 +103,18 @@ public void Shutdown() { public void Update() { if (!Initialized) return; - using (Profiler.BeginZone("Tick")) + using (Profiler.BeginZone("Tick", color:(uint)Color.OrangeRed.ToArgb())) { - using (Profiler.BeginZone("DM Execution")) + using (Profiler.BeginZone("DM Execution", color:(uint)Color.LightPink.ToArgb())) _procScheduler.Process(); - using (Profiler.BeginZone("Map Update")){ + using (Profiler.BeginZone("Map Update", color:(uint)Color.LightPink.ToArgb())){ UpdateStat(); _dreamMapManager.UpdateTiles(); } - using (Profiler.BeginZone("Disk IO")) + using (Profiler.BeginZone("Disk IO", color:(uint)Color.LightPink.ToArgb())) DreamObjectSavefile.FlushAllUpdates(); WorldInstance.SetVariableValue("cpu", WorldInstance.GetVariable("tick_usage")); - using (Profiler.BeginZone("Deletion Queue")) + using (Profiler.BeginZone("Deletion Queue", color:(uint)Color.LightPink.ToArgb())) ProcessDelQueue(); } Profiler.EmitFrameMark(); diff --git a/OpenDreamRuntime/DreamThread.cs b/OpenDreamRuntime/DreamThread.cs index 4b9c2e66e1..defb787b5b 100644 --- a/OpenDreamRuntime/DreamThread.cs +++ b/OpenDreamRuntime/DreamThread.cs @@ -256,7 +256,7 @@ public DreamValue ReentrantResume(ProcState? untilState, out ProcStatus resultSt try { // _current.Resume may mutate our state!!! if (!_current.TracyZoned && _current.Proc != null) { - _current.TracyZoneId = Profiler.BeginZone(_current.Proc.Name, filePath:_current.TracyLocationId.SourceFile, lineNumber:(uint)_current.TracyLocationId.Line);//ProfilerInternal.StartScopedZone(_current.TracyLocationId); + _current.TracyZoneId = Profiler.BeginZone(_current.Proc.OwningType.Path+"/"+_current.Proc.Name, filePath:_current.TracyLocationId.SourceFile, lineNumber:(uint)_current.TracyLocationId.Line);//ProfilerInternal.StartScopedZone(_current.TracyLocationId); _current.TracyZoned = true; } status = _current.Resume(); @@ -322,6 +322,12 @@ public DreamValue ReentrantResume(ProcState? untilState, out ProcStatus resultSt _current.TracyZoneId.Dispose(); _current.TracyZoned = false; } + foreach (var s in _stack) { + if (!s.TracyZoned) + continue; + s.TracyZoneId.Dispose(); + s.TracyZoned = false; + } // We return the current return value here even though it may not be the final result resultStatus = status; return _current.Result; From 1193b29a7f40ec3533103fe7ab7701071dccea05 Mon Sep 17 00:00:00 2001 From: amy Date: Sat, 7 Dec 2024 23:53:46 +0000 Subject: [PATCH 03/24] lint and procnames --- OpenDreamRuntime/DreamManager.cs | 5 +++++ OpenDreamRuntime/DreamThread.cs | 6 ++++-- OpenDreamRuntime/Profile.cs | 11 ++++++----- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/OpenDreamRuntime/DreamManager.cs b/OpenDreamRuntime/DreamManager.cs index 82a1f1a12b..42f15a4006 100644 --- a/OpenDreamRuntime/DreamManager.cs +++ b/OpenDreamRuntime/DreamManager.cs @@ -107,16 +107,21 @@ public void Update() { { using (Profiler.BeginZone("DM Execution", color:(uint)Color.LightPink.ToArgb())) _procScheduler.Process(); + using (Profiler.BeginZone("Map Update", color:(uint)Color.LightPink.ToArgb())){ UpdateStat(); _dreamMapManager.UpdateTiles(); } + using (Profiler.BeginZone("Disk IO", color:(uint)Color.LightPink.ToArgb())) DreamObjectSavefile.FlushAllUpdates(); + WorldInstance.SetVariableValue("cpu", WorldInstance.GetVariable("tick_usage")); + using (Profiler.BeginZone("Deletion Queue", color:(uint)Color.LightPink.ToArgb())) ProcessDelQueue(); } + Profiler.EmitFrameMark(); } diff --git a/OpenDreamRuntime/DreamThread.cs b/OpenDreamRuntime/DreamThread.cs index defb787b5b..d93c2465ef 100644 --- a/OpenDreamRuntime/DreamThread.cs +++ b/OpenDreamRuntime/DreamThread.cs @@ -254,11 +254,12 @@ public DreamValue ReentrantResume(ProcState? untilState, out ProcStatus resultSt while (_current != null) { ProcStatus status; try { - // _current.Resume may mutate our state!!! if (!_current.TracyZoned && _current.Proc != null) { - _current.TracyZoneId = Profiler.BeginZone(_current.Proc.OwningType.Path+"/"+_current.Proc.Name, filePath:_current.TracyLocationId.SourceFile, lineNumber:(uint)_current.TracyLocationId.Line);//ProfilerInternal.StartScopedZone(_current.TracyLocationId); + var location =_current.TracyLocationId; + _current.TracyZoneId = Profiler.BeginZone((_current.Proc.OwningType.Path.Equals("/") ? "/proc/" : _current.Proc.OwningType.Path+"/") +_current.Proc.Name, filePath: location.SourceFile, lineNumber:(uint)location.Line); _current.TracyZoned = true; } + // _current.Resume may mutate our state!!! status = _current.Resume(); } catch (DMError dmError) { if (_current == null) { @@ -322,6 +323,7 @@ public DreamValue ReentrantResume(ProcState? untilState, out ProcStatus resultSt _current.TracyZoneId.Dispose(); _current.TracyZoned = false; } + foreach (var s in _stack) { if (!s.TracyZoned) continue; diff --git a/OpenDreamRuntime/Profile.cs b/OpenDreamRuntime/Profile.cs index f3ee30267e..062c844733 100644 --- a/OpenDreamRuntime/Profile.cs +++ b/OpenDreamRuntime/Profile.cs @@ -1,14 +1,14 @@ -using System; -using System.Collections.Generic; -using System.Runtime.CompilerServices; +using System.Runtime.CompilerServices; using bottlenoselabs.C2CS.Runtime; using static Tracy.PInvoke; +namespace OpenDreamRuntime; + public static class Profiler { // Plot names need to be cached for the lifetime of the program // seealso Tracy docs section 3.1 - private static readonly Dictionary PlotNameCache = new Dictionary(); + private static readonly Dictionary PlotNameCache = new(); /// /// Begins a new and returns the handle to that zone. Time @@ -115,6 +115,7 @@ private static CString GetPlotCString(string name) plotCString = CString.FromString(name); PlotNameCache.Add(name, plotCString); } + return plotCString; } @@ -130,7 +131,6 @@ public static void AppInfo(string appInfo) TracyEmitMessageAppinfo(infostr, infoln); } - /// /// Emit the top-level frame marker. /// @@ -162,6 +162,7 @@ public static CString GetCString(string? fromString, out ulong clength) clength = 0; return new CString(0); } + clength = (ulong)fromString.Length; return CString.FromString(fromString); } From c7db578e31208bda51a1457915eaa1d36c204c27 Mon Sep 17 00:00:00 2001 From: amy Date: Sun, 8 Dec 2024 12:38:11 +0000 Subject: [PATCH 04/24] nearly there --- OpenDreamRuntime/DreamThread.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/OpenDreamRuntime/DreamThread.cs b/OpenDreamRuntime/DreamThread.cs index d93c2465ef..09d1e4d909 100644 --- a/OpenDreamRuntime/DreamThread.cs +++ b/OpenDreamRuntime/DreamThread.cs @@ -490,6 +490,10 @@ private bool TryCatchException(Exception exception) { if (!InspectStack().Any(x => x.IsCatching())) return false; while (!_current.IsCatching()) { + if(_current.TracyZoned) { + _current.TracyZoneId.Dispose(); + _current.TracyZoned = false; + } PopProcState(); } From 27d77f703e8c689d076badb5f219124d7b07c4f2 Mon Sep 17 00:00:00 2001 From: amy Date: Sun, 8 Dec 2024 13:31:14 +0000 Subject: [PATCH 05/24] well fuck me I fixed it --- OpenDreamRuntime/DreamThread.cs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/OpenDreamRuntime/DreamThread.cs b/OpenDreamRuntime/DreamThread.cs index 09d1e4d909..a75e536b53 100644 --- a/OpenDreamRuntime/DreamThread.cs +++ b/OpenDreamRuntime/DreamThread.cs @@ -300,10 +300,6 @@ public DreamValue ReentrantResume(ProcState? untilState, out ProcStatus resultSt // Our top-most proc just returned a value case ProcStatus.Returned: var returned = _current.Result; - if (_current.TracyZoned) { - _current.TracyZoneId.Dispose(); - _current.TracyZoned = false; - } PopProcState(); // If our stack is empty, the context has finished execution @@ -366,6 +362,11 @@ public void PushProcState(ProcState state) { } public void PopProcState(bool dispose = true) { + if (_current?.TracyZoned == true) { + _current.TracyZoneId.Dispose(); + _current.TracyZoned = false; + } + if (_current?.WaitFor == false) { _syncCount--; } @@ -490,10 +491,6 @@ private bool TryCatchException(Exception exception) { if (!InspectStack().Any(x => x.IsCatching())) return false; while (!_current.IsCatching()) { - if(_current.TracyZoned) { - _current.TracyZoneId.Dispose(); - _current.TracyZoned = false; - } PopProcState(); } From 4a6633334c55629213a14476217516cd524320a2 Mon Sep 17 00:00:00 2001 From: amy Date: Sun, 8 Dec 2024 16:05:06 +0000 Subject: [PATCH 06/24] make it toggleable --- OpenDreamRuntime/DreamManager.cs | 1 + OpenDreamRuntime/DreamThread.cs | 36 +++++---- OpenDreamRuntime/EntryPoint.cs | 2 + OpenDreamRuntime/Profile.cs | 13 +++- OpenDreamShared/OpenDreamCVars.cs | 119 +++++++++++++++--------------- 5 files changed, 93 insertions(+), 78 deletions(-) diff --git a/OpenDreamRuntime/DreamManager.cs b/OpenDreamRuntime/DreamManager.cs index 42f15a4006..88f8dfb49a 100644 --- a/OpenDreamRuntime/DreamManager.cs +++ b/OpenDreamRuntime/DreamManager.cs @@ -80,6 +80,7 @@ public void StartWorld() { // It is now OK to call user code, like /New procs. Initialized = true; InitializedTick = _gameTiming.CurTick; + using (Profiler.BeginZone("StartWorld", color:(uint)Color.OrangeRed.ToArgb())) { // Call global with waitfor=FALSE _objectTree.GlobalInitProc?.Spawn(WorldInstance, new()); diff --git a/OpenDreamRuntime/DreamThread.cs b/OpenDreamRuntime/DreamThread.cs index a75e536b53..3bcda92ed9 100644 --- a/OpenDreamRuntime/DreamThread.cs +++ b/OpenDreamRuntime/DreamThread.cs @@ -138,8 +138,7 @@ public abstract class ProcState : IDisposable { private static int _idCounter = 0; public int Id { get; } = ++_idCounter; public abstract (string SourceFile, int Line) TracyLocationId { get; } - public ProfilerZone TracyZoneId { get; set; } - public bool TracyZoned { get; set; } + public ProfilerZone? TracyZoneId { get; set; } public DreamThread Thread { get; set; } [Access(typeof(ProcScheduler))] @@ -254,10 +253,9 @@ public DreamValue ReentrantResume(ProcState? untilState, out ProcStatus resultSt while (_current != null) { ProcStatus status; try { - if (!_current.TracyZoned && _current.Proc != null) { + if (_current.TracyZoneId is null && _current.Proc != null) { var location =_current.TracyLocationId; _current.TracyZoneId = Profiler.BeginZone((_current.Proc.OwningType.Path.Equals("/") ? "/proc/" : _current.Proc.OwningType.Path+"/") +_current.Proc.Name, filePath: location.SourceFile, lineNumber:(uint)location.Line); - _current.TracyZoned = true; } // _current.Resume may mutate our state!!! status = _current.Resume(); @@ -281,17 +279,17 @@ public DreamValue ReentrantResume(ProcState? untilState, out ProcStatus resultSt switch (status) { // The entire Thread is stopping case ProcStatus.Cancelled: - if (_current.TracyZoned) { - _current.TracyZoneId.Dispose(); - _current.TracyZoned = false; + if (_current.TracyZoneId is not null) { + _current.TracyZoneId.Value.Dispose(); + _current.TracyZoneId = null; } var current = _current; _current = null; foreach (var s in _stack) { - if (!s.TracyZoned) + if (s.TracyZoneId is null) continue; - s.TracyZoneId.Dispose(); - s.TracyZoned = false; + s.TracyZoneId.Value.Dispose(); + s.TracyZoneId = null; } _stack.Clear(); resultStatus = status; @@ -315,16 +313,16 @@ public DreamValue ReentrantResume(ProcState? untilState, out ProcStatus resultSt // The context is done executing for now case ProcStatus.Deferred: - if (_current.TracyZoned) { - _current.TracyZoneId.Dispose(); - _current.TracyZoned = false; + if (_current.TracyZoneId is not null) { + _current.TracyZoneId.Value.Dispose(); + _current.TracyZoneId = null; } foreach (var s in _stack) { - if (!s.TracyZoned) + if (s.TracyZoneId is null) continue; - s.TracyZoneId.Dispose(); - s.TracyZoned = false; + s.TracyZoneId.Value.Dispose(); + s.TracyZoneId = null; } // We return the current return value here even though it may not be the final result resultStatus = status; @@ -362,9 +360,9 @@ public void PushProcState(ProcState state) { } public void PopProcState(bool dispose = true) { - if (_current?.TracyZoned == true) { - _current.TracyZoneId.Dispose(); - _current.TracyZoned = false; + if (_current?.TracyZoneId is not null) { + _current.TracyZoneId.Value.Dispose(); + _current.TracyZoneId = null; } if (_current?.WaitFor == false) { diff --git a/OpenDreamRuntime/EntryPoint.cs b/OpenDreamRuntime/EntryPoint.cs index e797e5151d..6f9759a7ca 100644 --- a/OpenDreamRuntime/EntryPoint.cs +++ b/OpenDreamRuntime/EntryPoint.cs @@ -51,6 +51,8 @@ public override void Init() { _configManager.SetCVar(OpenDreamCVars.JsonPath, arg); break; } + if(_configManager.GetCVar(OpenDreamCVars.TracyEnable)) + Profiler.ActivateTracy(); _prototypeManager.LoadDirectory(new ResPath("/Resources/Prototypes")); diff --git a/OpenDreamRuntime/Profile.cs b/OpenDreamRuntime/Profile.cs index 062c844733..dab9c4c018 100644 --- a/OpenDreamRuntime/Profile.cs +++ b/OpenDreamRuntime/Profile.cs @@ -6,10 +6,16 @@ namespace OpenDreamRuntime; public static class Profiler { + //whether these procs are NOPs or not + private static bool _tracyActvated = false; // Plot names need to be cached for the lifetime of the program // seealso Tracy docs section 3.1 private static readonly Dictionary PlotNameCache = new(); + public static void ActivateTracy() { + _tracyActvated = true; + } + /// /// Begins a new and returns the handle to that zone. Time /// spent inside a zone is calculated by Tracy and shown in the profiler. A zone is @@ -33,7 +39,7 @@ public static class Profiler /// If this param is not explicitly assigned the value will provided by . /// /// - public static ProfilerZone BeginZone( + public static ProfilerZone? BeginZone( string? zoneName = null, bool active = true, uint color = 0, @@ -42,6 +48,9 @@ public static ProfilerZone BeginZone( [CallerFilePath] string? filePath = null, [CallerMemberName] string? memberName = null) { + if(!_tracyActvated) + return null; + using var filestr = GetCString(filePath, out var fileln); using var memberstr = GetCString(memberName, out var memberln); using var namestr = GetCString(zoneName, out var nameln); @@ -139,6 +148,8 @@ public static void AppInfo(string appInfo) /// public static void EmitFrameMark() { + if(!_tracyActvated) + return; TracyEmitFrameMark(null); } diff --git a/OpenDreamShared/OpenDreamCVars.cs b/OpenDreamShared/OpenDreamCVars.cs index cc87102218..f862be64e4 100644 --- a/OpenDreamShared/OpenDreamCVars.cs +++ b/OpenDreamShared/OpenDreamCVars.cs @@ -1,62 +1,65 @@ using System; using Robust.Shared.Configuration; -namespace OpenDreamShared { - [CVarDefs] - public abstract class OpenDreamCVars { - public static readonly CVarDef JsonPath = - CVarDef.Create("opendream.json_path", String.Empty, CVar.SERVERONLY); - - public static readonly CVarDef DownloadTimeout = - CVarDef.Create("opendream.download_timeout", 30, CVar.CLIENTONLY); - - public static readonly CVarDef AlwaysShowExceptions = - CVarDef.Create("opendream.always_show_exceptions", false, CVar.SERVERONLY); - - public static readonly CVarDef DebugAdapterLaunched = - CVarDef.Create("opendream.debug_adapter_launched", 0, CVar.SERVERONLY); - - public static readonly CVarDef SpoofIEUserAgent = - CVarDef.Create("opendream.spoof_ie_user_agent", true, CVar.CLIENTONLY); - - public static readonly CVarDef WorldParams = - CVarDef.Create("opendream.world_params", string.Empty, CVar.SERVERONLY); - - public static readonly CVarDef TopicPort = - CVarDef.Create("opendream.topic_port", 25567, CVar.SERVERONLY); - - /* - * INFOLINKS - */ - - /// - /// Link to Discord server to show in the launcher. - /// - public static readonly CVarDef InfoLinksDiscord = - CVarDef.Create("infolinks.discord", "", CVar.SERVER | CVar.REPLICATED); - - /// - /// Link to forum to show in the launcher. - /// - public static readonly CVarDef InfoLinksForum = - CVarDef.Create("infolinks.forum", "", CVar.SERVER | CVar.REPLICATED); - - /// - /// Link to GitHub page to show in the launcher. - /// - public static readonly CVarDef InfoLinksGithub = - CVarDef.Create("infolinks.github", "", CVar.SERVER | CVar.REPLICATED); - - /// - /// Link to website to show in the launcher. - /// - public static readonly CVarDef InfoLinksWebsite = - CVarDef.Create("infolinks.website", "", CVar.SERVER | CVar.REPLICATED); - - /// - /// Link to wiki to show in the launcher. - /// - public static readonly CVarDef InfoLinksWiki = - CVarDef.Create("infolinks.wiki", "", CVar.SERVER | CVar.REPLICATED); - } +namespace OpenDreamShared; +[CVarDefs] +public abstract class OpenDreamCVars { + public static readonly CVarDef JsonPath = + CVarDef.Create("opendream.json_path", String.Empty, CVar.SERVERONLY); + + public static readonly CVarDef DownloadTimeout = + CVarDef.Create("opendream.download_timeout", 30, CVar.CLIENTONLY); + + public static readonly CVarDef AlwaysShowExceptions = + CVarDef.Create("opendream.always_show_exceptions", false, CVar.SERVERONLY); + + public static readonly CVarDef DebugAdapterLaunched = + CVarDef.Create("opendream.debug_adapter_launched", 0, CVar.SERVERONLY); + + public static readonly CVarDef SpoofIEUserAgent = + CVarDef.Create("opendream.spoof_ie_user_agent", true, CVar.CLIENTONLY); + + public static readonly CVarDef WorldParams = + CVarDef.Create("opendream.world_params", string.Empty, CVar.SERVERONLY); + + public static readonly CVarDef TopicPort = + CVarDef.Create("opendream.topic_port", 25567, CVar.SERVERONLY); + + public static readonly CVarDef TracyEnable = + CVarDef.Create("opendream.enable_tracy", false, CVar.SERVERONLY); + + /* + * INFOLINKS + */ + + /// + /// Link to Discord server to show in the launcher. + /// + public static readonly CVarDef InfoLinksDiscord = + CVarDef.Create("infolinks.discord", "", CVar.SERVER | CVar.REPLICATED); + + /// + /// Link to forum to show in the launcher. + /// + public static readonly CVarDef InfoLinksForum = + CVarDef.Create("infolinks.forum", "", CVar.SERVER | CVar.REPLICATED); + + /// + /// Link to GitHub page to show in the launcher. + /// + public static readonly CVarDef InfoLinksGithub = + CVarDef.Create("infolinks.github", "", CVar.SERVER | CVar.REPLICATED); + + /// + /// Link to website to show in the launcher. + /// + public static readonly CVarDef InfoLinksWebsite = + CVarDef.Create("infolinks.website", "", CVar.SERVER | CVar.REPLICATED); + + /// + /// Link to wiki to show in the launcher. + /// + public static readonly CVarDef InfoLinksWiki = + CVarDef.Create("infolinks.wiki", "", CVar.SERVER | CVar.REPLICATED); + } From 604d7d6192ca0943e3de00bda06fdbffc6f00af3 Mon Sep 17 00:00:00 2001 From: amy Date: Sun, 8 Dec 2024 16:28:08 +0000 Subject: [PATCH 07/24] lint --- OpenDreamRuntime/DreamThread.cs | 2 +- OpenDreamRuntime/EntryPoint.cs | 1 + OpenDreamRuntime/Profile.cs | 78 +++++++++++++++---------------- OpenDreamShared/OpenDreamCVars.cs | 3 +- 4 files changed, 41 insertions(+), 43 deletions(-) diff --git a/OpenDreamRuntime/DreamThread.cs b/OpenDreamRuntime/DreamThread.cs index 3bcda92ed9..7cd7107161 100644 --- a/OpenDreamRuntime/DreamThread.cs +++ b/OpenDreamRuntime/DreamThread.cs @@ -255,7 +255,7 @@ public DreamValue ReentrantResume(ProcState? untilState, out ProcStatus resultSt try { if (_current.TracyZoneId is null && _current.Proc != null) { var location =_current.TracyLocationId; - _current.TracyZoneId = Profiler.BeginZone((_current.Proc.OwningType.Path.Equals("/") ? "/proc/" : _current.Proc.OwningType.Path+"/") +_current.Proc.Name, filePath: location.SourceFile, lineNumber:(uint)location.Line); + _current.TracyZoneId = Profiler.BeginZone((_current.Proc.OwningType.Path.Equals("/") ? "/proc/" : _current.Proc.OwningType.Path+"/") +_current.Proc.Name, filePath: location.SourceFile, lineNumber: location.Line); } // _current.Resume may mutate our state!!! status = _current.Resume(); diff --git a/OpenDreamRuntime/EntryPoint.cs b/OpenDreamRuntime/EntryPoint.cs index 6f9759a7ca..77ad71324f 100644 --- a/OpenDreamRuntime/EntryPoint.cs +++ b/OpenDreamRuntime/EntryPoint.cs @@ -51,6 +51,7 @@ public override void Init() { _configManager.SetCVar(OpenDreamCVars.JsonPath, arg); break; } + if(_configManager.GetCVar(OpenDreamCVars.TracyEnable)) Profiler.ActivateTracy(); diff --git a/OpenDreamRuntime/Profile.cs b/OpenDreamRuntime/Profile.cs index dab9c4c018..3a8c1228cd 100644 --- a/OpenDreamRuntime/Profile.cs +++ b/OpenDreamRuntime/Profile.cs @@ -4,16 +4,16 @@ namespace OpenDreamRuntime; -public static class Profiler -{ - //whether these procs are NOPs or not - private static bool _tracyActvated = false; +public static class Profiler{ + //whether these procs are NOPs or not. Defaults to false. Use ActivateTracy() to set true + private static bool _tracyActivated; + // Plot names need to be cached for the lifetime of the program // seealso Tracy docs section 3.1 private static readonly Dictionary PlotNameCache = new(); public static void ActivateTracy() { - _tracyActvated = true; + _tracyActivated = true; } /// @@ -44,17 +44,17 @@ public static void ActivateTracy() { bool active = true, uint color = 0, string? text = null, - [CallerLineNumber] uint lineNumber = 0, + [CallerLineNumber] int lineNumber = 0, [CallerFilePath] string? filePath = null, [CallerMemberName] string? memberName = null) { - if(!_tracyActvated) + if(!_tracyActivated) return null; using var filestr = GetCString(filePath, out var fileln); using var memberstr = GetCString(memberName, out var memberln); using var namestr = GetCString(zoneName, out var nameln); - var srcLocId = TracyAllocSrclocName(lineNumber, filestr, fileln, memberstr, memberln, namestr, nameln, color); + var srcLocId = TracyAllocSrclocName((uint)lineNumber, filestr, fileln, memberstr, memberln, namestr, nameln, color); var context = TracyEmitZoneBeginAlloc(srcLocId, active ? 1 : 0); if (text != null) @@ -84,8 +84,9 @@ public static void ActivateTracy() { /// /// An RRGGBB color code that Tracy will use to color the plot in the profiler. /// - public static void PlotConfig(string name, PlotType type = PlotType.Number, bool step = false, bool fill = true, uint color = 0) - { + public static void PlotConfig(string name, PlotType type = PlotType.Number, bool step = false, bool fill = true, uint color = 0){ + if(!_tracyActivated) + return; var namestr = GetPlotCString(name); TracyEmitPlotConfig(namestr, (int)type, step ? 1 : 0, fill ? 1 : 0, color); } @@ -93,8 +94,9 @@ public static void PlotConfig(string name, PlotType type = PlotType.Number, bool /// /// Add a value to a plot. /// - public static void Plot(string name, double val) - { + public static void Plot(string name, double val){ + if(!_tracyActivated) + return; var namestr = GetPlotCString(name); TracyEmitPlot(namestr, val); } @@ -102,8 +104,9 @@ public static void Plot(string name, double val) /// /// Add a value to a plot. /// - public static void Plot(string name, int val) - { + public static void Plot(string name, int val){ + if(!_tracyActivated) + return; var namestr = GetPlotCString(name); TracyEmitPlotInt(namestr, val); } @@ -111,14 +114,14 @@ public static void Plot(string name, int val) /// /// Add a value to a plot. /// - public static void Plot(string name, float val) - { + public static void Plot(string name, float val){ + if(!_tracyActivated) + return; var namestr = GetPlotCString(name); TracyEmitPlotFloat(namestr, val); } - private static CString GetPlotCString(string name) - { + private static CString GetPlotCString(string name){ if(!PlotNameCache.TryGetValue(name, out var plotCString)) { plotCString = CString.FromString(name); @@ -134,8 +137,9 @@ private static CString GetPlotCString(string name) /// /// Viewable in the Info tab in the profiler. /// - public static void AppInfo(string appInfo) - { + public static void AppInfo(string appInfo){ + if(!_tracyActivated) + return; using var infostr = GetCString(appInfo, out var infoln); TracyEmitMessageAppinfo(infostr, infoln); } @@ -146,9 +150,8 @@ public static void AppInfo(string appInfo) /// /// Tracy Cpp API and docs refer to this as the FrameMark macro. /// - public static void EmitFrameMark() - { - if(!_tracyActvated) + public static void EmitFrameMark(){ + if(!_tracyActivated) return; TracyEmitFrameMark(null); } @@ -157,8 +160,9 @@ public static void EmitFrameMark() /// Is the app connected to the external profiler? /// /// - public static bool IsConnected() - { + public static bool IsConnected(){ + if(!_tracyActivated) + return false; return TracyConnected() != 0; } @@ -166,8 +170,7 @@ public static bool IsConnected() /// Creates a for use by Tracy. Also returns the /// length of the string for interop convenience. /// - public static CString GetCString(string? fromString, out ulong clength) - { + public static CString GetCString(string? fromString, out ulong clength){ if (fromString == null) { clength = 0; @@ -178,8 +181,7 @@ public static CString GetCString(string? fromString, out ulong clength) return CString.FromString(fromString); } - public enum PlotType - { + public enum PlotType{ /// /// Values will be displayed as plain numbers. /// @@ -197,38 +199,32 @@ public enum PlotType } } -public readonly struct ProfilerZone : IDisposable -{ +public readonly struct ProfilerZone : IDisposable{ public readonly TracyCZoneCtx Context; public uint Id => Context.Data.Id; public int Active => Context.Data.Active; - internal ProfilerZone(TracyCZoneCtx context) - { + internal ProfilerZone(TracyCZoneCtx context){ Context = context; } - public void EmitName(string name) - { + public void EmitName(string name){ using var namestr = Profiler.GetCString(name, out var nameln); TracyEmitZoneName(Context, namestr, nameln); } - public void EmitColor(uint color) - { + public void EmitColor(uint color){ TracyEmitZoneColor(Context, color); } - public void EmitText(string text) - { + public void EmitText(string text){ using var textstr = Profiler.GetCString(text, out var textln); TracyEmitZoneText(Context, textstr, textln); } - public void Dispose() - { + public void Dispose(){ TracyEmitZoneEnd(Context); } } diff --git a/OpenDreamShared/OpenDreamCVars.cs b/OpenDreamShared/OpenDreamCVars.cs index f862be64e4..92e4ee6e66 100644 --- a/OpenDreamShared/OpenDreamCVars.cs +++ b/OpenDreamShared/OpenDreamCVars.cs @@ -2,6 +2,7 @@ using Robust.Shared.Configuration; namespace OpenDreamShared; + [CVarDefs] public abstract class OpenDreamCVars { public static readonly CVarDef JsonPath = @@ -26,7 +27,7 @@ public abstract class OpenDreamCVars { CVarDef.Create("opendream.topic_port", 25567, CVar.SERVERONLY); public static readonly CVarDef TracyEnable = - CVarDef.Create("opendream.enable_tracy", false, CVar.SERVERONLY); + CVarDef.Create("opendream.enable_tracy", false, CVar.SERVERONLY); /* * INFOLINKS From f32646f3b229161370d49737599ffc49c84f64c7 Mon Sep 17 00:00:00 2001 From: amy Date: Sun, 8 Dec 2024 16:33:26 +0000 Subject: [PATCH 08/24] more lint --- OpenDreamRuntime/DreamThread.cs | 3 ++- OpenDreamRuntime/Profile.cs | 8 +++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/OpenDreamRuntime/DreamThread.cs b/OpenDreamRuntime/DreamThread.cs index 7cd7107161..aa0558018e 100644 --- a/OpenDreamRuntime/DreamThread.cs +++ b/OpenDreamRuntime/DreamThread.cs @@ -253,8 +253,9 @@ public DreamValue ReentrantResume(ProcState? untilState, out ProcStatus resultSt while (_current != null) { ProcStatus status; try { - if (_current.TracyZoneId is null && _current.Proc != null) { + if (Profiler.IsActivated() && _current.TracyZoneId is null && _current.Proc != null) { //IsActivated() call just for optimisation reasons var location =_current.TracyLocationId; + // ReSharper disable once ExplicitCallerInfoArgument _current.TracyZoneId = Profiler.BeginZone((_current.Proc.OwningType.Path.Equals("/") ? "/proc/" : _current.Proc.OwningType.Path+"/") +_current.Proc.Name, filePath: location.SourceFile, lineNumber: location.Line); } // _current.Resume may mutate our state!!! diff --git a/OpenDreamRuntime/Profile.cs b/OpenDreamRuntime/Profile.cs index 3a8c1228cd..50034028d8 100644 --- a/OpenDreamRuntime/Profile.cs +++ b/OpenDreamRuntime/Profile.cs @@ -1,4 +1,5 @@ -using System.Runtime.CompilerServices; +using System.Diagnostics.Contracts; +using System.Runtime.CompilerServices; using bottlenoselabs.C2CS.Runtime; using static Tracy.PInvoke; @@ -16,6 +17,11 @@ public static void ActivateTracy() { _tracyActivated = true; } + [Pure] + public static bool IsActivated() { + return _tracyActivated; + } + /// /// Begins a new and returns the handle to that zone. Time /// spent inside a zone is calculated by Tracy and shown in the profiler. A zone is From 82658f9533241c6129ccab3d44e7045b561f911e Mon Sep 17 00:00:00 2001 From: amy Date: Sun, 8 Dec 2024 16:40:02 +0000 Subject: [PATCH 09/24] once more --- OpenDreamRuntime/DreamThread.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/OpenDreamRuntime/DreamThread.cs b/OpenDreamRuntime/DreamThread.cs index aa0558018e..66ba5f5454 100644 --- a/OpenDreamRuntime/DreamThread.cs +++ b/OpenDreamRuntime/DreamThread.cs @@ -255,8 +255,9 @@ public DreamValue ReentrantResume(ProcState? untilState, out ProcStatus resultSt try { if (Profiler.IsActivated() && _current.TracyZoneId is null && _current.Proc != null) { //IsActivated() call just for optimisation reasons var location =_current.TracyLocationId; - // ReSharper disable once ExplicitCallerInfoArgument + // ReSharper disable ExplicitCallerInfoArgument _current.TracyZoneId = Profiler.BeginZone((_current.Proc.OwningType.Path.Equals("/") ? "/proc/" : _current.Proc.OwningType.Path+"/") +_current.Proc.Name, filePath: location.SourceFile, lineNumber: location.Line); + // ReSharper restore ExplicitCallerInfoArgument } // _current.Resume may mutate our state!!! status = _current.Resume(); From ca18c192976d84f40d87843bbd69a9869d24ab38 Mon Sep 17 00:00:00 2001 From: amy Date: Sun, 8 Dec 2024 17:35:48 +0000 Subject: [PATCH 10/24] prettify output --- OpenDreamRuntime/DreamThread.cs | 3 ++- OpenDreamRuntime/Procs/DMProc.cs | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/OpenDreamRuntime/DreamThread.cs b/OpenDreamRuntime/DreamThread.cs index 66ba5f5454..a16020ba8e 100644 --- a/OpenDreamRuntime/DreamThread.cs +++ b/OpenDreamRuntime/DreamThread.cs @@ -255,8 +255,9 @@ public DreamValue ReentrantResume(ProcState? untilState, out ProcStatus resultSt try { if (Profiler.IsActivated() && _current.TracyZoneId is null && _current.Proc != null) { //IsActivated() call just for optimisation reasons var location =_current.TracyLocationId; + var procpath = (_current.Proc.OwningType.Path.Equals("/") ? "/proc/" : _current.Proc.OwningType.Path+"/") +_current.Proc.Name; // ReSharper disable ExplicitCallerInfoArgument - _current.TracyZoneId = Profiler.BeginZone((_current.Proc.OwningType.Path.Equals("/") ? "/proc/" : _current.Proc.OwningType.Path+"/") +_current.Proc.Name, filePath: location.SourceFile, lineNumber: location.Line); + _current.TracyZoneId = Profiler.BeginZone(filePath: location.SourceFile, lineNumber: location.Line, memberName: procpath); // ReSharper restore ExplicitCallerInfoArgument } // _current.Resume may mutate our state!!! diff --git a/OpenDreamRuntime/Procs/DMProc.cs b/OpenDreamRuntime/Procs/DMProc.cs index 8b8f093b56..51477eb49b 100644 --- a/OpenDreamRuntime/Procs/DMProc.cs +++ b/OpenDreamRuntime/Procs/DMProc.cs @@ -51,7 +51,7 @@ public DMProc(int id, TreeEntry owningType, ProcDefinitionJson json, string? nam public (string Source, int Line) GetSourceAtOffset(int offset) { if(SourceInfo.Count == 0) - return ("whatthefuck",0); + return ("",0); SourceInfoJson current = SourceInfo[0]; string source = ObjectTree.Strings[current.File!.Value]; @@ -153,7 +153,7 @@ public sealed class NullProcState : ProcState { public override DreamProc? Proc => _proc; - public override (string SourceFile, int Line) TracyLocationId => ("",0); + public override (string SourceFile, int Line) TracyLocationId => ("",0); private DreamProc? _proc; From c9f8c518cacb68371464ba4f7613eab2f04ab324 Mon Sep 17 00:00:00 2001 From: amy Date: Sun, 8 Dec 2024 18:01:28 +0000 Subject: [PATCH 11/24] Add native proc profiling --- OpenDreamRuntime/DreamThread.cs | 5 ++++- OpenDreamRuntime/Procs/AsyncNativeProc.cs | 2 +- OpenDreamRuntime/Procs/DMProc.cs | 2 ++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/OpenDreamRuntime/DreamThread.cs b/OpenDreamRuntime/DreamThread.cs index a16020ba8e..6f23064695 100644 --- a/OpenDreamRuntime/DreamThread.cs +++ b/OpenDreamRuntime/DreamThread.cs @@ -211,7 +211,10 @@ public static DreamValue Run(DreamProc proc, DreamObject src, DreamObject? usr, var context = new DreamThread(proc.ToString()); if (proc is NativeProc nativeProc) { - return nativeProc.Call(context, src, usr, new(arguments)); + var zone = Profiler.BeginZone(filePath:"Native Proc", lineNumber:0, memberName:nativeProc.Name); + var result = nativeProc.Call(context, src, usr, new(arguments)); + zone?.Dispose(); + return result; } var state = proc.CreateState(context, src, usr, new DreamProcArguments(arguments)); diff --git a/OpenDreamRuntime/Procs/AsyncNativeProc.cs b/OpenDreamRuntime/Procs/AsyncNativeProc.cs index 02a4e43070..81da8138f8 100644 --- a/OpenDreamRuntime/Procs/AsyncNativeProc.cs +++ b/OpenDreamRuntime/Procs/AsyncNativeProc.cs @@ -26,7 +26,7 @@ public sealed class State : ProcState { private AsyncNativeProc? _proc; public override DreamProc? Proc => _proc; - public override (string SourceFile, int Line) TracyLocationId => ("Native Proc", 0); + public override (string SourceFile, int Line) TracyLocationId => ("Async Native Proc", 0); private Func> _taskFunc; private Task? _task; diff --git a/OpenDreamRuntime/Procs/DMProc.cs b/OpenDreamRuntime/Procs/DMProc.cs index 51477eb49b..189068a4d1 100644 --- a/OpenDreamRuntime/Procs/DMProc.cs +++ b/OpenDreamRuntime/Procs/DMProc.cs @@ -488,7 +488,9 @@ public void SetReturn(DreamValue value) { public ProcStatus Call(DreamProc proc, DreamObject? src, DreamProcArguments arguments) { if (proc is NativeProc p) { // Skip a whole song and dance. + var zone = Profiler.BeginZone(filePath:"Native Proc", lineNumber:0, memberName:p.Name); Push(p.Call(Thread, src, Usr, arguments)); + zone?.Dispose(); return ProcStatus.Continue; } From 58fc1678d8fe8dd03370c03d708f741fd661c35e Mon Sep 17 00:00:00 2001 From: amy Date: Sun, 8 Dec 2024 20:43:59 +0000 Subject: [PATCH 12/24] fix crash native proc --- OpenDreamRuntime/DreamThread.cs | 6 ++---- OpenDreamRuntime/Procs/DMProc.cs | 5 ++--- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/OpenDreamRuntime/DreamThread.cs b/OpenDreamRuntime/DreamThread.cs index 6f23064695..011c233ee1 100644 --- a/OpenDreamRuntime/DreamThread.cs +++ b/OpenDreamRuntime/DreamThread.cs @@ -211,10 +211,8 @@ public static DreamValue Run(DreamProc proc, DreamObject src, DreamObject? usr, var context = new DreamThread(proc.ToString()); if (proc is NativeProc nativeProc) { - var zone = Profiler.BeginZone(filePath:"Native Proc", lineNumber:0, memberName:nativeProc.Name); - var result = nativeProc.Call(context, src, usr, new(arguments)); - zone?.Dispose(); - return result; + using(Profiler.BeginZone(filePath:"Native Proc", lineNumber:0, memberName:nativeProc.Name)) + return nativeProc.Call(context, src, usr, new(arguments)); } var state = proc.CreateState(context, src, usr, new DreamProcArguments(arguments)); diff --git a/OpenDreamRuntime/Procs/DMProc.cs b/OpenDreamRuntime/Procs/DMProc.cs index 189068a4d1..db8be3b231 100644 --- a/OpenDreamRuntime/Procs/DMProc.cs +++ b/OpenDreamRuntime/Procs/DMProc.cs @@ -488,9 +488,8 @@ public void SetReturn(DreamValue value) { public ProcStatus Call(DreamProc proc, DreamObject? src, DreamProcArguments arguments) { if (proc is NativeProc p) { // Skip a whole song and dance. - var zone = Profiler.BeginZone(filePath:"Native Proc", lineNumber:0, memberName:p.Name); - Push(p.Call(Thread, src, Usr, arguments)); - zone?.Dispose(); + using(Profiler.BeginZone(filePath:"Native Proc", lineNumber:0, memberName:p.Name)) + Push(p.Call(Thread, src, Usr, arguments)); return ProcStatus.Continue; } From 696e6a0111cfe9760f308fd2fca96dab9bf4e2f6 Mon Sep 17 00:00:00 2001 From: amy Date: Sun, 8 Dec 2024 22:17:31 +0000 Subject: [PATCH 13/24] shut up linter --- OpenDreamRuntime/DreamThread.cs | 2 ++ OpenDreamRuntime/Procs/DMProc.cs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/OpenDreamRuntime/DreamThread.cs b/OpenDreamRuntime/DreamThread.cs index 011c233ee1..7c409c6ec0 100644 --- a/OpenDreamRuntime/DreamThread.cs +++ b/OpenDreamRuntime/DreamThread.cs @@ -211,8 +211,10 @@ public static DreamValue Run(DreamProc proc, DreamObject src, DreamObject? usr, var context = new DreamThread(proc.ToString()); if (proc is NativeProc nativeProc) { + // ReSharper disable ExplicitCallerInfoArgument using(Profiler.BeginZone(filePath:"Native Proc", lineNumber:0, memberName:nativeProc.Name)) return nativeProc.Call(context, src, usr, new(arguments)); + // ReSharper restore ExplicitCallerInfoArgument } var state = proc.CreateState(context, src, usr, new DreamProcArguments(arguments)); diff --git a/OpenDreamRuntime/Procs/DMProc.cs b/OpenDreamRuntime/Procs/DMProc.cs index db8be3b231..3e743b7aab 100644 --- a/OpenDreamRuntime/Procs/DMProc.cs +++ b/OpenDreamRuntime/Procs/DMProc.cs @@ -488,8 +488,10 @@ public void SetReturn(DreamValue value) { public ProcStatus Call(DreamProc proc, DreamObject? src, DreamProcArguments arguments) { if (proc is NativeProc p) { // Skip a whole song and dance. + // ReSharper disable ExplicitCallerInfoArgument using(Profiler.BeginZone(filePath:"Native Proc", lineNumber:0, memberName:p.Name)) Push(p.Call(Thread, src, Usr, arguments)); + // ReSharper restore ExplicitCallerInfoArgument return ProcStatus.Continue; } From 4e62bae1da48865742de52d7e0f2a14e2e6fff0b Mon Sep 17 00:00:00 2001 From: amy Date: Mon, 9 Dec 2024 23:56:19 +0000 Subject: [PATCH 14/24] memory tracking --- OpenDreamRuntime/Objects/DreamObject.cs | 4 +++ OpenDreamRuntime/Profile.cs | 29 +++++++++++++++++++++ OpenDreamRuntime/Resources/DreamResource.cs | 3 +++ 3 files changed, 36 insertions(+) diff --git a/OpenDreamRuntime/Objects/DreamObject.cs b/OpenDreamRuntime/Objects/DreamObject.cs index cfa73be66b..8148662b42 100644 --- a/OpenDreamRuntime/Objects/DreamObject.cs +++ b/OpenDreamRuntime/Objects/DreamObject.cs @@ -17,6 +17,7 @@ namespace OpenDreamRuntime.Objects { [Virtual] public class DreamObject { + private ProfilerMemory? _tracyMemoryId; public DreamObjectDefinition ObjectDefinition; [Access(typeof(DreamObject))] @@ -87,6 +88,8 @@ public DreamObject(DreamObjectDefinition objectDefinition) { if (this is not DreamObjectAtom && IsSubtypeOf(ObjectTree.Datum)) { ObjectDefinition.DreamManager.Datums.AddLast(new WeakDreamRef(this)); } + + _tracyMemoryId = Profiler.BeginMemoryZone((ulong)(Unsafe.SizeOf() + ObjectDefinition.Variables.Count * Unsafe.SizeOf() ), "object"); } public virtual void Initialize(DreamProcArguments args) { @@ -145,6 +148,7 @@ public void Delete(bool possiblyThreaded = false) { } public bool IsSubtypeOf(TreeEntry ancestor) { + if(Deleted) return false; return ObjectDefinition.IsSubtypeOf(ancestor); } diff --git a/OpenDreamRuntime/Profile.cs b/OpenDreamRuntime/Profile.cs index 50034028d8..9926e27c4f 100644 --- a/OpenDreamRuntime/Profile.cs +++ b/OpenDreamRuntime/Profile.cs @@ -1,5 +1,6 @@ using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using bottlenoselabs.C2CS.Runtime; using static Tracy.PInvoke; @@ -8,6 +9,7 @@ namespace OpenDreamRuntime; public static class Profiler{ //whether these procs are NOPs or not. Defaults to false. Use ActivateTracy() to set true private static bool _tracyActivated; + private static UInt64 _memoryUID = 0; // Plot names need to be cached for the lifetime of the program // seealso Tracy docs section 3.1 @@ -72,6 +74,17 @@ public static bool IsActivated() { return new ProfilerZone(context); } + public static ProfilerMemory? BeginMemoryZone(ulong size, string? name) + { + if(!_tracyActivated) + return null; + + var namestr = name is null ? GetPlotCString("null") : GetPlotCString(name); + unsafe { + return new ProfilerMemory((void*)_memoryUID++, size, namestr); + } + } + /// /// Configure how Tracy will display plotted values. /// @@ -234,3 +247,19 @@ public void Dispose(){ TracyEmitZoneEnd(Context); } } + +public sealed unsafe class ProfilerMemory { + + private readonly void* _ptr; + private CString _name; + + internal ProfilerMemory(void* pointer, ulong size, CString name){ + _ptr = pointer; + _name = name; + TracyEmitMemoryAllocNamed(_ptr, size, 0, _name); + } + + ~ProfilerMemory(){ + TracyEmitMemoryFreeNamed(_ptr, 0, _name); + } +} diff --git a/OpenDreamRuntime/Resources/DreamResource.cs b/OpenDreamRuntime/Resources/DreamResource.cs index 44ad6a5a94..09374ee8e0 100644 --- a/OpenDreamRuntime/Resources/DreamResource.cs +++ b/OpenDreamRuntime/Resources/DreamResource.cs @@ -19,16 +19,19 @@ public byte[]? ResourceData { private readonly string? _filePath; private byte[]? _resourceData; + private ProfilerMemory? _tracyMemoryId; public DreamResource(int id, string? filePath, string? resourcePath) { Id = id; ResourcePath = resourcePath; _filePath = filePath; + _tracyMemoryId = Profiler.BeginMemoryZone(ResourceData is null? 0 : (ulong)ResourceData.Length, "resource"); } public DreamResource(int id, byte[] data) { Id = id; _resourceData = data; + _tracyMemoryId = Profiler.BeginMemoryZone(ResourceData is null? 0 : (ulong)ResourceData.Length, "resource"); } public virtual string? ReadAsString() { From 724c2bae1065890bb82759b9e8c92ca955e48a57 Mon Sep 17 00:00:00 2001 From: amy Date: Tue, 10 Dec 2024 22:55:01 +0000 Subject: [PATCH 15/24] more precise, more detailed --- OpenDreamRuntime/DreamValue.cs | 2 + OpenDreamRuntime/Objects/DreamObject.cs | 3 +- OpenDreamRuntime/Objects/DreamObjectTree.cs | 74 +++++++++++---------- OpenDreamRuntime/Profile.cs | 14 +++- OpenDreamRuntime/Resources/DreamResource.cs | 5 +- 5 files changed, 57 insertions(+), 41 deletions(-) diff --git a/OpenDreamRuntime/DreamValue.cs b/OpenDreamRuntime/DreamValue.cs index 99102d0b6c..8d39726416 100644 --- a/OpenDreamRuntime/DreamValue.cs +++ b/OpenDreamRuntime/DreamValue.cs @@ -65,10 +65,12 @@ public static DreamValue False { private object? _refValue; private readonly float _floatValue; + private readonly ProfilerMemory? _tracyMemoryId; //only used for strings, since everything else is a value type or handled in DreamObject public DreamValue(string value) { DebugTools.Assert(value != null); Type = DreamValueType.String; + _tracyMemoryId = Profiler.BeginMemoryZone((ulong) (1+value.Length*sizeof(char)), "string"); _refValue = value; } diff --git a/OpenDreamRuntime/Objects/DreamObject.cs b/OpenDreamRuntime/Objects/DreamObject.cs index 8148662b42..654a16ae76 100644 --- a/OpenDreamRuntime/Objects/DreamObject.cs +++ b/OpenDreamRuntime/Objects/DreamObject.cs @@ -89,7 +89,7 @@ public DreamObject(DreamObjectDefinition objectDefinition) { ObjectDefinition.DreamManager.Datums.AddLast(new WeakDreamRef(this)); } - _tracyMemoryId = Profiler.BeginMemoryZone((ulong)(Unsafe.SizeOf() + ObjectDefinition.Variables.Count * Unsafe.SizeOf() ), "object"); + _tracyMemoryId = Profiler.BeginMemoryZone((ulong)(Unsafe.SizeOf() + ObjectDefinition.Variables.Count * Unsafe.SizeOf() ), "/datum"); } public virtual void Initialize(DreamProcArguments args) { @@ -109,6 +109,7 @@ protected virtual void HandleDeletion(bool possiblyThreaded) { Variables = null; ObjectDefinition = null!; + _tracyMemoryId?.ReleaseMemory(); } /// diff --git a/OpenDreamRuntime/Objects/DreamObjectTree.cs b/OpenDreamRuntime/Objects/DreamObjectTree.cs index f2492129f4..9f8345d200 100644 --- a/OpenDreamRuntime/Objects/DreamObjectTree.cs +++ b/OpenDreamRuntime/Objects/DreamObjectTree.cs @@ -139,42 +139,44 @@ public IEnumerable GetAllDescendants(TreeEntry treeEntry) { /// (by calling the result of or ) /// public DreamObject CreateObject(TreeEntry type) { - if (type == List) - return CreateList(); - if (type == Savefile) - return new DreamObjectSavefile(Savefile.ObjectDefinition); - if (type.ObjectDefinition.IsSubtypeOf(DatabaseQuery)) - return new DreamObjectDatabaseQuery(type.ObjectDefinition); - if (type.ObjectDefinition.IsSubtypeOf(Database)) - return new DreamObjectDatabase(type.ObjectDefinition); - if (type.ObjectDefinition.IsSubtypeOf(Matrix)) - return new DreamObjectMatrix(type.ObjectDefinition); - if (type.ObjectDefinition.IsSubtypeOf(Sound)) - return new DreamObjectSound(type.ObjectDefinition); - if (type.ObjectDefinition.IsSubtypeOf(Regex)) - return new DreamObjectRegex(type.ObjectDefinition); - if (type.ObjectDefinition.IsSubtypeOf(Image)) - return new DreamObjectImage(type.ObjectDefinition); - if (type.ObjectDefinition.IsSubtypeOf(Icon)) - return new DreamObjectIcon(type.ObjectDefinition); - if (type.ObjectDefinition.IsSubtypeOf(Filter)) - return new DreamObjectFilter(type.ObjectDefinition); - if (type.ObjectDefinition.IsSubtypeOf(Mob)) - return new DreamObjectMob(type.ObjectDefinition); - if (type.ObjectDefinition.IsSubtypeOf(Movable)) - return new DreamObjectMovable(type.ObjectDefinition); - if (type.ObjectDefinition.IsSubtypeOf(Area)) - return new DreamObjectArea(type.ObjectDefinition); - if (type.ObjectDefinition.IsSubtypeOf(Atom)) - return new DreamObjectAtom(type.ObjectDefinition); - if (type.ObjectDefinition.IsSubtypeOf(Client)) - throw new Exception("Cannot create objects of type /client"); - if (type.ObjectDefinition.IsSubtypeOf(Turf)) - throw new Exception("New turfs must be created by the map manager"); - if (type.ObjectDefinition.IsSubtypeOf(Exception)) - return new DreamObjectException(type.ObjectDefinition); - - return new DreamObject(type.ObjectDefinition); + using(Profiler.BeginZone($"new {type}")){ + if (type == List) + return CreateList(); + if (type == Savefile) + return new DreamObjectSavefile(Savefile.ObjectDefinition); + if (type.ObjectDefinition.IsSubtypeOf(DatabaseQuery)) + return new DreamObjectDatabaseQuery(type.ObjectDefinition); + if (type.ObjectDefinition.IsSubtypeOf(Database)) + return new DreamObjectDatabase(type.ObjectDefinition); + if (type.ObjectDefinition.IsSubtypeOf(Matrix)) + return new DreamObjectMatrix(type.ObjectDefinition); + if (type.ObjectDefinition.IsSubtypeOf(Sound)) + return new DreamObjectSound(type.ObjectDefinition); + if (type.ObjectDefinition.IsSubtypeOf(Regex)) + return new DreamObjectRegex(type.ObjectDefinition); + if (type.ObjectDefinition.IsSubtypeOf(Image)) + return new DreamObjectImage(type.ObjectDefinition); + if (type.ObjectDefinition.IsSubtypeOf(Icon)) + return new DreamObjectIcon(type.ObjectDefinition); + if (type.ObjectDefinition.IsSubtypeOf(Filter)) + return new DreamObjectFilter(type.ObjectDefinition); + if (type.ObjectDefinition.IsSubtypeOf(Mob)) + return new DreamObjectMob(type.ObjectDefinition); + if (type.ObjectDefinition.IsSubtypeOf(Movable)) + return new DreamObjectMovable(type.ObjectDefinition); + if (type.ObjectDefinition.IsSubtypeOf(Area)) + return new DreamObjectArea(type.ObjectDefinition); + if (type.ObjectDefinition.IsSubtypeOf(Atom)) + return new DreamObjectAtom(type.ObjectDefinition); + if (type.ObjectDefinition.IsSubtypeOf(Client)) + throw new Exception("Cannot create objects of type /client"); + if (type.ObjectDefinition.IsSubtypeOf(Turf)) + throw new Exception("New turfs must be created by the map manager"); + if (type.ObjectDefinition.IsSubtypeOf(Exception)) + return new DreamObjectException(type.ObjectDefinition); + + return new DreamObject(type.ObjectDefinition); + } } public T CreateObject(TreeEntry type) where T : DreamObject { diff --git a/OpenDreamRuntime/Profile.cs b/OpenDreamRuntime/Profile.cs index 9926e27c4f..e5903fbe67 100644 --- a/OpenDreamRuntime/Profile.cs +++ b/OpenDreamRuntime/Profile.cs @@ -1,6 +1,7 @@ using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Threading; using bottlenoselabs.C2CS.Runtime; using static Tracy.PInvoke; @@ -9,6 +10,8 @@ namespace OpenDreamRuntime; public static class Profiler{ //whether these procs are NOPs or not. Defaults to false. Use ActivateTracy() to set true private static bool _tracyActivated; + + //internal tracking for unique IDs for memory zones, because we can't use actual object pointers sadly as they are unstable outside of `unsafe` private static UInt64 _memoryUID = 0; // Plot names need to be cached for the lifetime of the program @@ -81,7 +84,7 @@ public static bool IsActivated() { var namestr = name is null ? GetPlotCString("null") : GetPlotCString(name); unsafe { - return new ProfilerMemory((void*)_memoryUID++, size, namestr); + return new ProfilerMemory((void*)(Interlocked.Add(ref _memoryUID, size)-size), size, namestr); } } @@ -252,6 +255,7 @@ public sealed unsafe class ProfilerMemory { private readonly void* _ptr; private CString _name; + private int hasRun = 0; internal ProfilerMemory(void* pointer, ulong size, CString name){ _ptr = pointer; @@ -259,7 +263,13 @@ internal ProfilerMemory(void* pointer, ulong size, CString name){ TracyEmitMemoryAllocNamed(_ptr, size, 0, _name); } + public void ReleaseMemory(){ + if (System.Threading.Interlocked.Exchange(ref hasRun, 1) == 0){ // only run once + TracyEmitMemoryFreeNamed(_ptr, 0, _name); + } + } + ~ProfilerMemory(){ - TracyEmitMemoryFreeNamed(_ptr, 0, _name); + ReleaseMemory(); } } diff --git a/OpenDreamRuntime/Resources/DreamResource.cs b/OpenDreamRuntime/Resources/DreamResource.cs index 09374ee8e0..8c06d8cbec 100644 --- a/OpenDreamRuntime/Resources/DreamResource.cs +++ b/OpenDreamRuntime/Resources/DreamResource.cs @@ -1,4 +1,5 @@ using System.IO; +using System.Runtime.CompilerServices; using System.Text; namespace OpenDreamRuntime.Resources; @@ -25,13 +26,13 @@ public DreamResource(int id, string? filePath, string? resourcePath) { Id = id; ResourcePath = resourcePath; _filePath = filePath; - _tracyMemoryId = Profiler.BeginMemoryZone(ResourceData is null? 0 : (ulong)ResourceData.Length, "resource"); + _tracyMemoryId = Profiler.BeginMemoryZone((ulong)(Unsafe.SizeOf() + (ResourceData is null? 0 : ResourceData.Length)), "resource"); } public DreamResource(int id, byte[] data) { Id = id; _resourceData = data; - _tracyMemoryId = Profiler.BeginMemoryZone(ResourceData is null? 0 : (ulong)ResourceData.Length, "resource"); + _tracyMemoryId = Profiler.BeginMemoryZone((ulong)(Unsafe.SizeOf() + (ResourceData is null? 0 : ResourceData.Length)), "resource"); } public virtual string? ReadAsString() { From ae6f74b70fa4c6ed4718acd1e1a02a75af379a96 Mon Sep 17 00:00:00 2001 From: amy Date: Wed, 11 Dec 2024 00:03:32 +0000 Subject: [PATCH 16/24] lint --- OpenDreamRuntime/DreamValue.cs | 1 + OpenDreamRuntime/Objects/DreamObject.cs | 2 +- OpenDreamRuntime/Profile.cs | 8 +++----- OpenDreamRuntime/Resources/DreamResource.cs | 2 +- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/OpenDreamRuntime/DreamValue.cs b/OpenDreamRuntime/DreamValue.cs index 8d39726416..458a2b7d84 100644 --- a/OpenDreamRuntime/DreamValue.cs +++ b/OpenDreamRuntime/DreamValue.cs @@ -65,6 +65,7 @@ public static DreamValue False { private object? _refValue; private readonly float _floatValue; + //ReSharper disable once NotAccessedField.Local private readonly ProfilerMemory? _tracyMemoryId; //only used for strings, since everything else is a value type or handled in DreamObject public DreamValue(string value) { diff --git a/OpenDreamRuntime/Objects/DreamObject.cs b/OpenDreamRuntime/Objects/DreamObject.cs index 654a16ae76..6f1bbeb418 100644 --- a/OpenDreamRuntime/Objects/DreamObject.cs +++ b/OpenDreamRuntime/Objects/DreamObject.cs @@ -17,7 +17,7 @@ namespace OpenDreamRuntime.Objects { [Virtual] public class DreamObject { - private ProfilerMemory? _tracyMemoryId; + private readonly ProfilerMemory? _tracyMemoryId; public DreamObjectDefinition ObjectDefinition; [Access(typeof(DreamObject))] diff --git a/OpenDreamRuntime/Profile.cs b/OpenDreamRuntime/Profile.cs index e5903fbe67..fc79a90840 100644 --- a/OpenDreamRuntime/Profile.cs +++ b/OpenDreamRuntime/Profile.cs @@ -1,6 +1,5 @@ using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; using System.Threading; using bottlenoselabs.C2CS.Runtime; using static Tracy.PInvoke; @@ -252,10 +251,9 @@ public void Dispose(){ } public sealed unsafe class ProfilerMemory { - private readonly void* _ptr; - private CString _name; - private int hasRun = 0; + private readonly CString _name; + private int _hasRun; internal ProfilerMemory(void* pointer, ulong size, CString name){ _ptr = pointer; @@ -264,7 +262,7 @@ internal ProfilerMemory(void* pointer, ulong size, CString name){ } public void ReleaseMemory(){ - if (System.Threading.Interlocked.Exchange(ref hasRun, 1) == 0){ // only run once + if (Interlocked.Exchange(ref _hasRun, 1) == 0){ // only run once TracyEmitMemoryFreeNamed(_ptr, 0, _name); } } diff --git a/OpenDreamRuntime/Resources/DreamResource.cs b/OpenDreamRuntime/Resources/DreamResource.cs index 8c06d8cbec..0a8fe1c14a 100644 --- a/OpenDreamRuntime/Resources/DreamResource.cs +++ b/OpenDreamRuntime/Resources/DreamResource.cs @@ -26,7 +26,7 @@ public DreamResource(int id, string? filePath, string? resourcePath) { Id = id; ResourcePath = resourcePath; _filePath = filePath; - _tracyMemoryId = Profiler.BeginMemoryZone((ulong)(Unsafe.SizeOf() + (ResourceData is null? 0 : ResourceData.Length)), "resource"); + _tracyMemoryId = Profiler.BeginMemoryZone((ulong)(Unsafe.SizeOf() + (ResourceData?.Length ?? 0)), "resource"); } public DreamResource(int id, byte[] data) { From 4cbdfb72c6cffcb29844e5bc8e472f66b8d357ba Mon Sep 17 00:00:00 2001 From: amy Date: Wed, 11 Dec 2024 00:13:34 +0000 Subject: [PATCH 17/24] fuck off --- OpenDreamRuntime/DreamValue.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/OpenDreamRuntime/DreamValue.cs b/OpenDreamRuntime/DreamValue.cs index 458a2b7d84..3e91f5cd4f 100644 --- a/OpenDreamRuntime/DreamValue.cs +++ b/OpenDreamRuntime/DreamValue.cs @@ -65,6 +65,7 @@ public static DreamValue False { private object? _refValue; private readonly float _floatValue; + //ReSharper disable once NotAccessedField.Local private readonly ProfilerMemory? _tracyMemoryId; //only used for strings, since everything else is a value type or handled in DreamObject From 7462ab9df162a9b20b13119bd652d8dfcfd0fb69 Mon Sep 17 00:00:00 2001 From: amy Date: Wed, 11 Dec 2024 17:40:10 +0000 Subject: [PATCH 18/24] list and #if tools --- OpenDreamRuntime/DreamThread.cs | 13 ++++++++++++- OpenDreamRuntime/Objects/DreamObject.cs | 8 +++++--- OpenDreamRuntime/Objects/Types/DreamList.cs | 3 +++ OpenDreamRuntime/Profile.cs | 7 +++++-- 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/OpenDreamRuntime/DreamThread.cs b/OpenDreamRuntime/DreamThread.cs index 7c409c6ec0..4c8371d075 100644 --- a/OpenDreamRuntime/DreamThread.cs +++ b/OpenDreamRuntime/DreamThread.cs @@ -256,13 +256,15 @@ public DreamValue ReentrantResume(ProcState? untilState, out ProcStatus resultSt while (_current != null) { ProcStatus status; try { - if (Profiler.IsActivated() && _current.TracyZoneId is null && _current.Proc != null) { //IsActivated() call just for optimisation reasons + #if TOOLS + if (_current.TracyZoneId is null && _current.Proc != null) { var location =_current.TracyLocationId; var procpath = (_current.Proc.OwningType.Path.Equals("/") ? "/proc/" : _current.Proc.OwningType.Path+"/") +_current.Proc.Name; // ReSharper disable ExplicitCallerInfoArgument _current.TracyZoneId = Profiler.BeginZone(filePath: location.SourceFile, lineNumber: location.Line, memberName: procpath); // ReSharper restore ExplicitCallerInfoArgument } + #endif // _current.Resume may mutate our state!!! status = _current.Resume(); } catch (DMError dmError) { @@ -285,18 +287,25 @@ public DreamValue ReentrantResume(ProcState? untilState, out ProcStatus resultSt switch (status) { // The entire Thread is stopping case ProcStatus.Cancelled: + #if TOOLS if (_current.TracyZoneId is not null) { _current.TracyZoneId.Value.Dispose(); _current.TracyZoneId = null; } + #endif + var current = _current; _current = null; + + #if TOOLS foreach (var s in _stack) { if (s.TracyZoneId is null) continue; s.TracyZoneId.Value.Dispose(); s.TracyZoneId = null; } + #endif + _stack.Clear(); resultStatus = status; return current.Result; @@ -319,6 +328,7 @@ public DreamValue ReentrantResume(ProcState? untilState, out ProcStatus resultSt // The context is done executing for now case ProcStatus.Deferred: + #if TOOLS if (_current.TracyZoneId is not null) { _current.TracyZoneId.Value.Dispose(); _current.TracyZoneId = null; @@ -330,6 +340,7 @@ public DreamValue ReentrantResume(ProcState? untilState, out ProcStatus resultSt s.TracyZoneId.Value.Dispose(); s.TracyZoneId = null; } + #endif // We return the current return value here even though it may not be the final result resultStatus = status; return _current.Result; diff --git a/OpenDreamRuntime/Objects/DreamObject.cs b/OpenDreamRuntime/Objects/DreamObject.cs index 6f1bbeb418..b489355511 100644 --- a/OpenDreamRuntime/Objects/DreamObject.cs +++ b/OpenDreamRuntime/Objects/DreamObject.cs @@ -17,7 +17,7 @@ namespace OpenDreamRuntime.Objects { [Virtual] public class DreamObject { - private readonly ProfilerMemory? _tracyMemoryId; + protected ProfilerMemory? _tracyMemoryId; public DreamObjectDefinition ObjectDefinition; [Access(typeof(DreamObject))] @@ -88,8 +88,10 @@ public DreamObject(DreamObjectDefinition objectDefinition) { if (this is not DreamObjectAtom && IsSubtypeOf(ObjectTree.Datum)) { ObjectDefinition.DreamManager.Datums.AddLast(new WeakDreamRef(this)); } - - _tracyMemoryId = Profiler.BeginMemoryZone((ulong)(Unsafe.SizeOf() + ObjectDefinition.Variables.Count * Unsafe.SizeOf() ), "/datum"); + #if TOOLS + if(_tracyMemoryId is null) //if it's not null, subclasses have done their own allocation + _tracyMemoryId = Profiler.BeginMemoryZone((ulong)(Unsafe.SizeOf() + ObjectDefinition.Variables.Count * Unsafe.SizeOf() ), "/datum"); + #endif } public virtual void Initialize(DreamProcArguments args) { diff --git a/OpenDreamRuntime/Objects/Types/DreamList.cs b/OpenDreamRuntime/Objects/Types/DreamList.cs index 793886360b..03b82dc0ef 100644 --- a/OpenDreamRuntime/Objects/Types/DreamList.cs +++ b/OpenDreamRuntime/Objects/Types/DreamList.cs @@ -28,6 +28,9 @@ public DreamList(DreamObjectDefinition listDef, int size) : base(listDef) { public DreamList(DreamObjectDefinition listDef, List values, Dictionary? associativeValues) : base(listDef) { _values = values; _associativeValues = associativeValues; + #if TOOLS + _tracyMemoryId = Profiler.BeginMemoryZone((ulong)(Unsafe.SizeOf() + _values.Count * Unsafe.SizeOf() + _associativeValues?.Count * Unsafe.SizeOf() ?? 0), "/list"); + #endif } public override void Initialize(DreamProcArguments args) { diff --git a/OpenDreamRuntime/Profile.cs b/OpenDreamRuntime/Profile.cs index fc79a90840..52e52f579c 100644 --- a/OpenDreamRuntime/Profile.cs +++ b/OpenDreamRuntime/Profile.cs @@ -22,6 +22,7 @@ public static void ActivateTracy() { } [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsActivated() { return _tracyActivated; } @@ -58,8 +59,9 @@ public static bool IsActivated() { [CallerFilePath] string? filePath = null, [CallerMemberName] string? memberName = null) { - if(!_tracyActivated) - return null; + #if !TOOLS + return null; + #else using var filestr = GetCString(filePath, out var fileln); using var memberstr = GetCString(memberName, out var memberln); @@ -74,6 +76,7 @@ public static bool IsActivated() { } return new ProfilerZone(context); + #endif } public static ProfilerMemory? BeginMemoryZone(ulong size, string? name) From 4986175664f6b28c2677e930d49985a39cf039e3 Mon Sep 17 00:00:00 2001 From: amy Date: Wed, 11 Dec 2024 18:18:05 +0000 Subject: [PATCH 19/24] fuck it, tools build means we can trace all list ops right? --- OpenDreamRuntime/DreamValue.cs | 5 ++- OpenDreamRuntime/EntryPoint.cs | 4 --- OpenDreamRuntime/Objects/Types/DreamList.cs | 26 +++++++++++++++- OpenDreamRuntime/Profile.cs | 34 +++------------------ OpenDreamShared/OpenDreamCVars.cs | 3 -- 5 files changed, 33 insertions(+), 39 deletions(-) diff --git a/OpenDreamRuntime/DreamValue.cs b/OpenDreamRuntime/DreamValue.cs index 3e91f5cd4f..fd109f7dee 100644 --- a/OpenDreamRuntime/DreamValue.cs +++ b/OpenDreamRuntime/DreamValue.cs @@ -66,13 +66,16 @@ public static DreamValue False { private object? _refValue; private readonly float _floatValue; + #if TOOLS //ReSharper disable once NotAccessedField.Local private readonly ProfilerMemory? _tracyMemoryId; //only used for strings, since everything else is a value type or handled in DreamObject - + #endif public DreamValue(string value) { DebugTools.Assert(value != null); Type = DreamValueType.String; + #if TOOLS _tracyMemoryId = Profiler.BeginMemoryZone((ulong) (1+value.Length*sizeof(char)), "string"); + #endif _refValue = value; } diff --git a/OpenDreamRuntime/EntryPoint.cs b/OpenDreamRuntime/EntryPoint.cs index 77ad71324f..967f2c3b49 100644 --- a/OpenDreamRuntime/EntryPoint.cs +++ b/OpenDreamRuntime/EntryPoint.cs @@ -2,7 +2,6 @@ using OpenDreamRuntime.Objects.Types; using OpenDreamRuntime.Procs.DebugAdapter; using OpenDreamShared; -using Robust.Server.ServerStatus; using Robust.Shared; using Robust.Shared.Configuration; using Robust.Shared.ContentPack; @@ -52,9 +51,6 @@ public override void Init() { break; } - if(_configManager.GetCVar(OpenDreamCVars.TracyEnable)) - Profiler.ActivateTracy(); - _prototypeManager.LoadDirectory(new ResPath("/Resources/Prototypes")); _serverInfoManager.Initialize(); diff --git a/OpenDreamRuntime/Objects/Types/DreamList.cs b/OpenDreamRuntime/Objects/Types/DreamList.cs index 03b82dc0ef..cf76bfaa92 100644 --- a/OpenDreamRuntime/Objects/Types/DreamList.cs +++ b/OpenDreamRuntime/Objects/Types/DreamList.cs @@ -29,7 +29,7 @@ public DreamList(DreamObjectDefinition listDef, List values, Diction _values = values; _associativeValues = associativeValues; #if TOOLS - _tracyMemoryId = Profiler.BeginMemoryZone((ulong)(Unsafe.SizeOf() + _values.Count * Unsafe.SizeOf() + _associativeValues?.Count * Unsafe.SizeOf() ?? 0), "/list"); + _tracyMemoryId = Profiler.BeginMemoryZone((ulong)(Unsafe.SizeOf() + _values.Count * Unsafe.SizeOf() + (_associativeValues?.Count ?? 0) * Unsafe.SizeOf()), "/list"); #endif } @@ -130,6 +130,10 @@ public virtual void SetValue(DreamValue key, DreamValue value, bool allowGrowth _associativeValues ??= new Dictionary(1); _associativeValues[key] = value; } + #if TOOLS + _tracyMemoryId?.ReleaseMemory(); + _tracyMemoryId = Profiler.BeginMemoryZone((ulong)(Unsafe.SizeOf() + _values.Count * Unsafe.SizeOf() + (_associativeValues?.Count ?? 0) * Unsafe.SizeOf()), "/list"); + #endif } public virtual void RemoveValue(DreamValue value) { @@ -139,10 +143,18 @@ public virtual void RemoveValue(DreamValue value) { _associativeValues?.Remove(value); _values.RemoveAt(valueIndex); } + #if TOOLS + _tracyMemoryId?.ReleaseMemory(); + _tracyMemoryId = Profiler.BeginMemoryZone((ulong)(Unsafe.SizeOf() + _values.Count * Unsafe.SizeOf() + (_associativeValues?.Count ?? 0) * Unsafe.SizeOf()), "/list"); + #endif } public virtual void AddValue(DreamValue value) { _values.Add(value); + #if TOOLS + _tracyMemoryId?.ReleaseMemory(); + _tracyMemoryId = Profiler.BeginMemoryZone((ulong)(Unsafe.SizeOf() + _values.Count * Unsafe.SizeOf() + (_associativeValues?.Count ?? 0) * Unsafe.SizeOf()), "/list"); + #endif } //Does not include associations @@ -179,10 +191,18 @@ public virtual void Cut(int start = 1, int end = 0) { if (end > start) _values.RemoveRange(start - 1, end - start); + #if TOOLS + _tracyMemoryId?.ReleaseMemory(); + _tracyMemoryId = Profiler.BeginMemoryZone((ulong)(Unsafe.SizeOf() + _values.Count * Unsafe.SizeOf() + (_associativeValues?.Count ?? 0) * Unsafe.SizeOf()), "/list"); + #endif } public void Insert(int index, DreamValue value) { _values.Insert(index - 1, value); + #if TOOLS + _tracyMemoryId?.ReleaseMemory(); + _tracyMemoryId = Profiler.BeginMemoryZone((ulong)(Unsafe.SizeOf() + _values.Count * Unsafe.SizeOf() + (_associativeValues?.Count ?? 0) * Unsafe.SizeOf()), "/list"); + #endif } public void Swap(int index1, int index2) { @@ -202,6 +222,10 @@ public void Resize(int size) { } else { Cut(size + 1); } + #if TOOLS + _tracyMemoryId?.ReleaseMemory(); + _tracyMemoryId = Profiler.BeginMemoryZone((ulong)(Unsafe.SizeOf() + _values.Count * Unsafe.SizeOf() + (_associativeValues?.Count ?? 0) * Unsafe.SizeOf()), "/list"); + #endif } public virtual int GetLength() { diff --git a/OpenDreamRuntime/Profile.cs b/OpenDreamRuntime/Profile.cs index 52e52f579c..e6e57bf6cd 100644 --- a/OpenDreamRuntime/Profile.cs +++ b/OpenDreamRuntime/Profile.cs @@ -7,9 +7,6 @@ namespace OpenDreamRuntime; public static class Profiler{ - //whether these procs are NOPs or not. Defaults to false. Use ActivateTracy() to set true - private static bool _tracyActivated; - //internal tracking for unique IDs for memory zones, because we can't use actual object pointers sadly as they are unstable outside of `unsafe` private static UInt64 _memoryUID = 0; @@ -17,16 +14,6 @@ public static class Profiler{ // seealso Tracy docs section 3.1 private static readonly Dictionary PlotNameCache = new(); - public static void ActivateTracy() { - _tracyActivated = true; - } - - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsActivated() { - return _tracyActivated; - } - /// /// Begins a new and returns the handle to that zone. Time /// spent inside a zone is calculated by Tracy and shown in the profiler. A zone is @@ -81,13 +68,14 @@ public static bool IsActivated() { public static ProfilerMemory? BeginMemoryZone(ulong size, string? name) { - if(!_tracyActivated) - return null; - + #if !TOOLS + return null + #else var namestr = name is null ? GetPlotCString("null") : GetPlotCString(name); unsafe { return new ProfilerMemory((void*)(Interlocked.Add(ref _memoryUID, size)-size), size, namestr); } + #endif } /// @@ -109,8 +97,6 @@ public static bool IsActivated() { /// An RRGGBB color code that Tracy will use to color the plot in the profiler. /// public static void PlotConfig(string name, PlotType type = PlotType.Number, bool step = false, bool fill = true, uint color = 0){ - if(!_tracyActivated) - return; var namestr = GetPlotCString(name); TracyEmitPlotConfig(namestr, (int)type, step ? 1 : 0, fill ? 1 : 0, color); } @@ -119,8 +105,6 @@ public static void PlotConfig(string name, PlotType type = PlotType.Number, bool /// Add a value to a plot. /// public static void Plot(string name, double val){ - if(!_tracyActivated) - return; var namestr = GetPlotCString(name); TracyEmitPlot(namestr, val); } @@ -129,8 +113,6 @@ public static void Plot(string name, double val){ /// Add a value to a plot. /// public static void Plot(string name, int val){ - if(!_tracyActivated) - return; var namestr = GetPlotCString(name); TracyEmitPlotInt(namestr, val); } @@ -139,8 +121,6 @@ public static void Plot(string name, int val){ /// Add a value to a plot. /// public static void Plot(string name, float val){ - if(!_tracyActivated) - return; var namestr = GetPlotCString(name); TracyEmitPlotFloat(namestr, val); } @@ -162,8 +142,6 @@ private static CString GetPlotCString(string name){ /// Viewable in the Info tab in the profiler. /// public static void AppInfo(string appInfo){ - if(!_tracyActivated) - return; using var infostr = GetCString(appInfo, out var infoln); TracyEmitMessageAppinfo(infostr, infoln); } @@ -175,8 +153,6 @@ public static void AppInfo(string appInfo){ /// Tracy Cpp API and docs refer to this as the FrameMark macro. /// public static void EmitFrameMark(){ - if(!_tracyActivated) - return; TracyEmitFrameMark(null); } @@ -185,8 +161,6 @@ public static void EmitFrameMark(){ /// /// public static bool IsConnected(){ - if(!_tracyActivated) - return false; return TracyConnected() != 0; } diff --git a/OpenDreamShared/OpenDreamCVars.cs b/OpenDreamShared/OpenDreamCVars.cs index 92e4ee6e66..99acef2fc3 100644 --- a/OpenDreamShared/OpenDreamCVars.cs +++ b/OpenDreamShared/OpenDreamCVars.cs @@ -26,9 +26,6 @@ public abstract class OpenDreamCVars { public static readonly CVarDef TopicPort = CVarDef.Create("opendream.topic_port", 25567, CVar.SERVERONLY); - public static readonly CVarDef TracyEnable = - CVarDef.Create("opendream.enable_tracy", false, CVar.SERVERONLY); - /* * INFOLINKS */ From cb2dabc61eee243e288cbd54efb7babda0ed75e3 Mon Sep 17 00:00:00 2001 From: amy Date: Wed, 11 Dec 2024 18:30:34 +0000 Subject: [PATCH 20/24] whoops --- OpenDreamRuntime/Profile.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenDreamRuntime/Profile.cs b/OpenDreamRuntime/Profile.cs index e6e57bf6cd..2696d1dae3 100644 --- a/OpenDreamRuntime/Profile.cs +++ b/OpenDreamRuntime/Profile.cs @@ -69,7 +69,7 @@ public static class Profiler{ public static ProfilerMemory? BeginMemoryZone(ulong size, string? name) { #if !TOOLS - return null + return null; #else var namestr = name is null ? GetPlotCString("null") : GetPlotCString(name); unsafe { From 27db5dea7e69050220ad04759fd695a9630cff2d Mon Sep 17 00:00:00 2001 From: amy Date: Wed, 11 Dec 2024 19:38:25 +0000 Subject: [PATCH 21/24] split instance and content tracking for lists --- OpenDreamRuntime/Objects/Types/DreamList.cs | 29 +++++++++++---------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/OpenDreamRuntime/Objects/Types/DreamList.cs b/OpenDreamRuntime/Objects/Types/DreamList.cs index cf76bfaa92..7744a5fc04 100644 --- a/OpenDreamRuntime/Objects/Types/DreamList.cs +++ b/OpenDreamRuntime/Objects/Types/DreamList.cs @@ -13,7 +13,7 @@ namespace OpenDreamRuntime.Objects.Types; public class DreamList : DreamObject { private readonly List _values; private Dictionary? _associativeValues; - + private ProfilerMemory? _tracyContentsMemoryId; public override bool ShouldCallNew => false; public virtual bool IsAssociative => (_associativeValues != null && _associativeValues.Count > 0); @@ -29,7 +29,8 @@ public DreamList(DreamObjectDefinition listDef, List values, Diction _values = values; _associativeValues = associativeValues; #if TOOLS - _tracyMemoryId = Profiler.BeginMemoryZone((ulong)(Unsafe.SizeOf() + _values.Count * Unsafe.SizeOf() + (_associativeValues?.Count ?? 0) * Unsafe.SizeOf()), "/list"); + _tracyMemoryId = Profiler.BeginMemoryZone(1, "/list instance"); + _tracyContentsMemoryId = Profiler.BeginMemoryZone((ulong)(Unsafe.SizeOf() + _values.Count * Unsafe.SizeOf() + (_associativeValues?.Count ?? 0) * Unsafe.SizeOf()), "/list contents"); #endif } @@ -131,8 +132,8 @@ public virtual void SetValue(DreamValue key, DreamValue value, bool allowGrowth _associativeValues[key] = value; } #if TOOLS - _tracyMemoryId?.ReleaseMemory(); - _tracyMemoryId = Profiler.BeginMemoryZone((ulong)(Unsafe.SizeOf() + _values.Count * Unsafe.SizeOf() + (_associativeValues?.Count ?? 0) * Unsafe.SizeOf()), "/list"); + _tracyContentsMemoryId?.ReleaseMemory(); + _tracyContentsMemoryId = Profiler.BeginMemoryZone((ulong)(Unsafe.SizeOf() + _values.Count * Unsafe.SizeOf() + (_associativeValues?.Count ?? 0) * Unsafe.SizeOf()), "/list contents"); #endif } @@ -144,16 +145,16 @@ public virtual void RemoveValue(DreamValue value) { _values.RemoveAt(valueIndex); } #if TOOLS - _tracyMemoryId?.ReleaseMemory(); - _tracyMemoryId = Profiler.BeginMemoryZone((ulong)(Unsafe.SizeOf() + _values.Count * Unsafe.SizeOf() + (_associativeValues?.Count ?? 0) * Unsafe.SizeOf()), "/list"); + _tracyContentsMemoryId?.ReleaseMemory(); + _tracyContentsMemoryId = Profiler.BeginMemoryZone((ulong)(Unsafe.SizeOf() + _values.Count * Unsafe.SizeOf() + (_associativeValues?.Count ?? 0) * Unsafe.SizeOf()), "/list contents"); #endif } public virtual void AddValue(DreamValue value) { _values.Add(value); #if TOOLS - _tracyMemoryId?.ReleaseMemory(); - _tracyMemoryId = Profiler.BeginMemoryZone((ulong)(Unsafe.SizeOf() + _values.Count * Unsafe.SizeOf() + (_associativeValues?.Count ?? 0) * Unsafe.SizeOf()), "/list"); + _tracyContentsMemoryId?.ReleaseMemory(); + _tracyContentsMemoryId = Profiler.BeginMemoryZone((ulong)(Unsafe.SizeOf() + _values.Count * Unsafe.SizeOf() + (_associativeValues?.Count ?? 0) * Unsafe.SizeOf()), "/list contents"); #endif } @@ -192,16 +193,16 @@ public virtual void Cut(int start = 1, int end = 0) { if (end > start) _values.RemoveRange(start - 1, end - start); #if TOOLS - _tracyMemoryId?.ReleaseMemory(); - _tracyMemoryId = Profiler.BeginMemoryZone((ulong)(Unsafe.SizeOf() + _values.Count * Unsafe.SizeOf() + (_associativeValues?.Count ?? 0) * Unsafe.SizeOf()), "/list"); + _tracyContentsMemoryId?.ReleaseMemory(); + _tracyContentsMemoryId = Profiler.BeginMemoryZone((ulong)(Unsafe.SizeOf() + _values.Count * Unsafe.SizeOf() + (_associativeValues?.Count ?? 0) * Unsafe.SizeOf()), "/list contents"); #endif } public void Insert(int index, DreamValue value) { _values.Insert(index - 1, value); #if TOOLS - _tracyMemoryId?.ReleaseMemory(); - _tracyMemoryId = Profiler.BeginMemoryZone((ulong)(Unsafe.SizeOf() + _values.Count * Unsafe.SizeOf() + (_associativeValues?.Count ?? 0) * Unsafe.SizeOf()), "/list"); + _tracyContentsMemoryId?.ReleaseMemory(); + _tracyContentsMemoryId = Profiler.BeginMemoryZone((ulong)(Unsafe.SizeOf() + _values.Count * Unsafe.SizeOf() + (_associativeValues?.Count ?? 0) * Unsafe.SizeOf()), "/list contents"); #endif } @@ -223,8 +224,8 @@ public void Resize(int size) { Cut(size + 1); } #if TOOLS - _tracyMemoryId?.ReleaseMemory(); - _tracyMemoryId = Profiler.BeginMemoryZone((ulong)(Unsafe.SizeOf() + _values.Count * Unsafe.SizeOf() + (_associativeValues?.Count ?? 0) * Unsafe.SizeOf()), "/list"); + _tracyContentsMemoryId?.ReleaseMemory(); + _tracyContentsMemoryId = Profiler.BeginMemoryZone((ulong)(Unsafe.SizeOf() + _values.Count * Unsafe.SizeOf() + (_associativeValues?.Count ?? 0) * Unsafe.SizeOf()), "/list contents"); #endif } From 51b46fea0df7f503f657e565bc82b556645c235c Mon Sep 17 00:00:00 2001 From: amy Date: Wed, 11 Dec 2024 19:53:54 +0000 Subject: [PATCH 22/24] more ifdef --- OpenDreamRuntime/DreamThread.cs | 2 ++ OpenDreamRuntime/Objects/DreamObject.cs | 4 ++++ OpenDreamRuntime/Objects/Types/DreamList.cs | 2 ++ OpenDreamRuntime/Procs/AsyncNativeProc.cs | 4 ++-- OpenDreamRuntime/Procs/DMProc.cs | 8 ++++---- OpenDreamRuntime/Procs/InitDreamObject.cs | 4 ++-- OpenDreamRuntime/Resources/DreamResource.cs | 6 ++++++ 7 files changed, 22 insertions(+), 8 deletions(-) diff --git a/OpenDreamRuntime/DreamThread.cs b/OpenDreamRuntime/DreamThread.cs index 4c8371d075..61c82b70b4 100644 --- a/OpenDreamRuntime/DreamThread.cs +++ b/OpenDreamRuntime/DreamThread.cs @@ -137,8 +137,10 @@ public DMError(string message) public abstract class ProcState : IDisposable { private static int _idCounter = 0; public int Id { get; } = ++_idCounter; + #if TOOLS public abstract (string SourceFile, int Line) TracyLocationId { get; } public ProfilerZone? TracyZoneId { get; set; } + #endif public DreamThread Thread { get; set; } [Access(typeof(ProcScheduler))] diff --git a/OpenDreamRuntime/Objects/DreamObject.cs b/OpenDreamRuntime/Objects/DreamObject.cs index b489355511..4dbf96807f 100644 --- a/OpenDreamRuntime/Objects/DreamObject.cs +++ b/OpenDreamRuntime/Objects/DreamObject.cs @@ -17,7 +17,9 @@ namespace OpenDreamRuntime.Objects { [Virtual] public class DreamObject { + #if TOOLS protected ProfilerMemory? _tracyMemoryId; + #endif public DreamObjectDefinition ObjectDefinition; [Access(typeof(DreamObject))] @@ -111,7 +113,9 @@ protected virtual void HandleDeletion(bool possiblyThreaded) { Variables = null; ObjectDefinition = null!; + #if TOOLS _tracyMemoryId?.ReleaseMemory(); + #endif } /// diff --git a/OpenDreamRuntime/Objects/Types/DreamList.cs b/OpenDreamRuntime/Objects/Types/DreamList.cs index 7744a5fc04..898bafe9ca 100644 --- a/OpenDreamRuntime/Objects/Types/DreamList.cs +++ b/OpenDreamRuntime/Objects/Types/DreamList.cs @@ -13,7 +13,9 @@ namespace OpenDreamRuntime.Objects.Types; public class DreamList : DreamObject { private readonly List _values; private Dictionary? _associativeValues; + #if TOOLS private ProfilerMemory? _tracyContentsMemoryId; + #endif public override bool ShouldCallNew => false; public virtual bool IsAssociative => (_associativeValues != null && _associativeValues.Count > 0); diff --git a/OpenDreamRuntime/Procs/AsyncNativeProc.cs b/OpenDreamRuntime/Procs/AsyncNativeProc.cs index 81da8138f8..ef0daf21d4 100644 --- a/OpenDreamRuntime/Procs/AsyncNativeProc.cs +++ b/OpenDreamRuntime/Procs/AsyncNativeProc.cs @@ -25,9 +25,9 @@ public sealed class State : ProcState { private AsyncNativeProc? _proc; public override DreamProc? Proc => _proc; - + #if TOOLS public override (string SourceFile, int Line) TracyLocationId => ("Async Native Proc", 0); - + #endif private Func> _taskFunc; private Task? _task; diff --git a/OpenDreamRuntime/Procs/DMProc.cs b/OpenDreamRuntime/Procs/DMProc.cs index 3e743b7aab..e55d67ad6a 100644 --- a/OpenDreamRuntime/Procs/DMProc.cs +++ b/OpenDreamRuntime/Procs/DMProc.cs @@ -152,9 +152,9 @@ public sealed class NullProcState : ProcState { public static readonly Stack Pool = new(); public override DreamProc? Proc => _proc; - + #if TOOLS public override (string SourceFile, int Line) TracyLocationId => ("",0); - + #endif private DreamProc? _proc; public override ProcStatus Resume() { @@ -357,9 +357,9 @@ public sealed class DMProcState : ProcState { private DMProc _proc; public override DMProc Proc => _proc; - + #if TOOLS public override (string SourceFile, int Line) TracyLocationId => _proc.GetSourceAtOffset(_pc+1); - + #endif /// Static initializer for maintainer friendly OpcodeHandlers to performance friendly _opcodeHandlers static unsafe DMProcState() { diff --git a/OpenDreamRuntime/Procs/InitDreamObject.cs b/OpenDreamRuntime/Procs/InitDreamObject.cs index ad205de909..3a419780c9 100644 --- a/OpenDreamRuntime/Procs/InitDreamObject.cs +++ b/OpenDreamRuntime/Procs/InitDreamObject.cs @@ -41,9 +41,9 @@ public void Initialize(DreamThread thread, DreamObject dreamObject, DreamObject? private Stage _stage = Stage.Init; public override DreamProc? Proc => null; - + #if TOOLS public override (string SourceFile, int Line) TracyLocationId => ($"new {_dreamObject.ObjectDefinition.Type}",0); - + #endif public override void AppendStackFrame(StringBuilder builder) { builder.AppendLine($"new {_dreamObject.ObjectDefinition.Type}"); } diff --git a/OpenDreamRuntime/Resources/DreamResource.cs b/OpenDreamRuntime/Resources/DreamResource.cs index 0a8fe1c14a..df3d85aed5 100644 --- a/OpenDreamRuntime/Resources/DreamResource.cs +++ b/OpenDreamRuntime/Resources/DreamResource.cs @@ -20,19 +20,25 @@ public byte[]? ResourceData { private readonly string? _filePath; private byte[]? _resourceData; + #if TOOLS private ProfilerMemory? _tracyMemoryId; + #endif public DreamResource(int id, string? filePath, string? resourcePath) { Id = id; ResourcePath = resourcePath; _filePath = filePath; + #if TOOLS _tracyMemoryId = Profiler.BeginMemoryZone((ulong)(Unsafe.SizeOf() + (ResourceData?.Length ?? 0)), "resource"); + #endif } public DreamResource(int id, byte[] data) { Id = id; _resourceData = data; + #if TOOLS _tracyMemoryId = Profiler.BeginMemoryZone((ulong)(Unsafe.SizeOf() + (ResourceData is null? 0 : ResourceData.Length)), "resource"); + #endif } public virtual string? ReadAsString() { From fba2601bb63870b81bf793ad1b3118123f759125 Mon Sep 17 00:00:00 2001 From: amy Date: Wed, 11 Dec 2024 19:59:38 +0000 Subject: [PATCH 23/24] missed one --- OpenDreamRuntime/DreamThread.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/OpenDreamRuntime/DreamThread.cs b/OpenDreamRuntime/DreamThread.cs index 61c82b70b4..b0cd8906b4 100644 --- a/OpenDreamRuntime/DreamThread.cs +++ b/OpenDreamRuntime/DreamThread.cs @@ -379,10 +379,12 @@ public void PushProcState(ProcState state) { } public void PopProcState(bool dispose = true) { + #if TOOLS if (_current?.TracyZoneId is not null) { _current.TracyZoneId.Value.Dispose(); _current.TracyZoneId = null; } + #endif if (_current?.WaitFor == false) { _syncCount--; From f03a935a6c5d857e4eac6a94f11f89e8aed9fd93 Mon Sep 17 00:00:00 2001 From: amy Date: Wed, 11 Dec 2024 21:06:18 +0000 Subject: [PATCH 24/24] so much lint --- OpenDreamRuntime/Objects/DreamObject.cs | 4 ++-- OpenDreamRuntime/Profile.cs | 7 +++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/OpenDreamRuntime/Objects/DreamObject.cs b/OpenDreamRuntime/Objects/DreamObject.cs index 4dbf96807f..48da289f0c 100644 --- a/OpenDreamRuntime/Objects/DreamObject.cs +++ b/OpenDreamRuntime/Objects/DreamObject.cs @@ -91,8 +91,8 @@ public DreamObject(DreamObjectDefinition objectDefinition) { ObjectDefinition.DreamManager.Datums.AddLast(new WeakDreamRef(this)); } #if TOOLS - if(_tracyMemoryId is null) //if it's not null, subclasses have done their own allocation - _tracyMemoryId = Profiler.BeginMemoryZone((ulong)(Unsafe.SizeOf() + ObjectDefinition.Variables.Count * Unsafe.SizeOf() ), "/datum"); + //if it's not null, subclasses have done their own allocation + _tracyMemoryId ??= Profiler.BeginMemoryZone((ulong)(Unsafe.SizeOf() + ObjectDefinition.Variables.Count * Unsafe.SizeOf() ), "/datum"); #endif } diff --git a/OpenDreamRuntime/Profile.cs b/OpenDreamRuntime/Profile.cs index 2696d1dae3..d41292b365 100644 --- a/OpenDreamRuntime/Profile.cs +++ b/OpenDreamRuntime/Profile.cs @@ -1,5 +1,4 @@ -using System.Diagnostics.Contracts; -using System.Runtime.CompilerServices; +using System.Runtime.CompilerServices; using System.Threading; using bottlenoselabs.C2CS.Runtime; using static Tracy.PInvoke; @@ -8,7 +7,7 @@ namespace OpenDreamRuntime; public static class Profiler{ //internal tracking for unique IDs for memory zones, because we can't use actual object pointers sadly as they are unstable outside of `unsafe` - private static UInt64 _memoryUID = 0; + private static ulong _memoryUid; // Plot names need to be cached for the lifetime of the program // seealso Tracy docs section 3.1 @@ -73,7 +72,7 @@ public static class Profiler{ #else var namestr = name is null ? GetPlotCString("null") : GetPlotCString(name); unsafe { - return new ProfilerMemory((void*)(Interlocked.Add(ref _memoryUID, size)-size), size, namestr); + return new ProfilerMemory((void*)(Interlocked.Add(ref _memoryUid, size)-size), size, namestr); } #endif }