using System; using System.IO; using System.Text.Json; using System.Threading; using System.Collections.Concurrent; public class ProfileResult { public string Name { get; set; } public long Start { get; set; } public long End { get; set; } public ProfileResult(string name, long start, long end) { Name = name; Start = start; End = end; } } public class InstrumentationSession { public string Name { get; } public InstrumentationSession(string name) { Name = name; } } public class Instrumentor : IDisposable { private static Instrumentor _instance; private InstrumentationSession _currentSession; private ConcurrentQueue _profileQueue; private bool _disposed; private Instrumentor() { _profileQueue = new ConcurrentQueue(); } public static Instrumentor Get() { _instance ??= new Instrumentor(); return _instance; } public void BeginSession(string name, string filepath = "results.json") { if (_currentSession != null) { EndSession(); } _profileQueue = new ConcurrentQueue(); _currentSession = new InstrumentationSession(name); } public void EndSession() { if (_currentSession == null) return; try { using (var writer = new StreamWriter("results.json", false, System.Text.Encoding.UTF8)) { writer.Write("{\"otherData\": {},\"traceEvents\":["); bool isFirst = true; while (_profileQueue.TryDequeue(out var result)) { if (!isFirst) writer.Write(","); isFirst = false; string name = result.Name.Replace("\"", "'"); var profileJson = new { cat = "function", dur = result.End - result.Start, name = name, ph = "X", pid = Environment.ProcessId, tid = Thread.CurrentThread.ManagedThreadId, ts = result.Start }; string jsonString = JsonSerializer.Serialize(profileJson); writer.Write(jsonString); } writer.Write("]}"); } } finally { _currentSession = null; _profileQueue = new ConcurrentQueue(); } } public void WriteProfile(ProfileResult result) { if (_currentSession == null || _disposed) return; _profileQueue.Enqueue(result); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (!_disposed) { if (disposing) { EndSession(); } _disposed = true; } } ~Instrumentor() { Dispose(false); } } public class InstrumentationTimer : IDisposable { private readonly string _name; private bool _stopped; private readonly long _startTime; private bool _disposed; public InstrumentationTimer(string name) { _name = name; _stopped = false; _startTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() * 1000; // Convert to microseconds } public void Stop() { if (_stopped || _disposed) return; long endTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() * 1000; // Convert to microseconds long start = _startTime; long end = endTime; Instrumentor.Get().WriteProfile(new ProfileResult(_name, start, end)); _stopped = true; } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (!_disposed) { if (disposing) { Stop(); } _disposed = true; } } ~InstrumentationTimer() { Dispose(false); } } public static class Profiling { public static bool Enabled { get; set; } = true; public static IDisposable ProfileScope(string name) { return new InstrumentationTimer(name); } } // Extension method for profiling functions public static class ProfilingExtensions { public static T ProfileFunction(this Func func, string name) { using (Profiling.ProfileScope(name)) { return func(); } } public static void ProfileAction(this Action action, string name) { using (Profiling.ProfileScope(name)) { action(); } } } // Usage example: /* // Method 1: Use as context manager if (Profiling.Enabled) { using (Profiling.ProfileScope("ScopeName")) { // code to profile } } // Method 2: Use as extension method void FunctionToProfile() { // code here } // Call it like this: FunctionToProfile.ProfileAction("FunctionToProfile"); */