private static void OutputProfilerFrame(ProfilerFrame source, Dictionary <string, int> exportedFrameNameToExportedFrameId, Dictionary <string, IList <ProfileEvent> > profileEventsPerThread) { int frameId = 0; foreach (var sourceThreadMethod in source.threadMethods) { var list = new List <ProfileEvent>(sourceThreadMethod.Item2.Count); foreach (ProfilerMethod method in sourceThreadMethod.Item2) { if (!exportedFrameNameToExportedFrameId.TryGetValue(method.methodName, out int value)) { value = frameId; frameId++; exportedFrameNameToExportedFrameId.Add(method.methodName, value); } list.Add(new ProfileEvent(ProfileEventType.Open, value, method.startTime, method.depth)); list.Add(new ProfileEvent(ProfileEventType.Close, value, method.endTime, method.depth)); } profileEventsPerThread[sourceThreadMethod.Item1] = OrderForExport(list).ToList(); } }
private static IList <TraceEvent> GetEvents(ProfilerFrame frame) { var events = new List <TraceEvent>(); int processId = Process.GetCurrentProcess().Id; int threadId = 0; string categories = "PERF"; foreach (var frameThreadMethod in frame.threadMethods.OrderByDescending(x => x.Item1)) { foreach (ProfilerMethod profilerMethod in frameThreadMethod.Item2) { //Chrome tracing shows methods with same startTime as parallel, so we add depth to the startTime long startTime = (long)profilerMethod.startTime + profilerMethod.depth; long endTime = (long)profilerMethod.endTime < startTime ? startTime : (long)profilerMethod.endTime; events.Add(new TraceEvent() { name = profilerMethod.methodName, cat = categories, pid = processId, ph = EventType.B, tid = frameThreadMethod.Item1, ts = startTime }); events.Add(new TraceEvent() { name = profilerMethod.methodName, cat = categories, pid = processId, ph = EventType.E, tid = frameThreadMethod.Item1, ts = endTime }); } threadId++; } if (frame.markers != null) { foreach (ProfilerMarker marker in frame.markers) { events.Add(new TraceEvent() { name = marker.markName, cat = "EVENTS", pid = processId, ph = EventType.I, tid = "Events", ts = (long)(marker.time) }); } } return(events); }
private static void Export(ProfilerFrame source, TextWriter writer, string name) { var methodNameToId = new Dictionary <string, int>(); var eventsPerThread = new Dictionary <string, IList <ProfileEvent> >(); OutputProfilerFrame(source, methodNameToId, eventsPerThread); var orderedFrameNames = methodNameToId.OrderBy(pair => pair.Value).Select(pair => pair.Key).ToArray(); WriteToFile(eventsPerThread, orderedFrameNames, writer, name); }
private static void CollectFrame() { ProfilerFrame frame = new ProfilerFrame(); frame.frameNumber = currentFrame; frame.frameLength = (stopwatch.ElapsedTicks / (double)Stopwatch.Frequency) * 1000000.0; frame.threadMethods = PooledList <(string, PooledList <ProfilerMethod>)> .Create(); foreach (var dataList in collectedMethods.Values) { if (dataList.Count == 0) { continue; } if (profilingThreads.TryGetValue(dataList[0].threadId, out string threadName)) { var tuple = frame.threadMethods.SingleOrDefault(x => x.Item1 == threadName); PooledList <ProfilerMethod> outList = null; if (tuple.Item1 == null) { outList = PooledList <ProfilerMethod> .Create(); frame.threadMethods.Add((threadName, outList)); } else { outList = tuple.Item2; } outList.AddRange(dataList); } } foreach (var dataList in collectedMarkers.Values) { if (frame.markers == null) { frame.markers = PooledList <ProfilerMarker> .Create(); } frame.markers.AddRange(dataList); } if (collectedFrames.IsFull) { collectedFrames.Front().Dispose(); } collectedFrames.PushBack(frame); }
public static void WriteToFile(ProfilerFrame source, string filePath) { var file = new FileInfo(filePath); var dir = file.Directory; if (!dir.Exists) { dir.Create(); } if (file.Exists) { file.Delete(); } using var fs = new FileStream(filePath, FileMode.Create, FileAccess.ReadWrite); using var writeStream = new StreamWriter(fs, Encoding.UTF8); Export(source, writeStream, Path.GetFileNameWithoutExtension(filePath)); }
public static void WriteToFile(string filename, ProfilingFormat format, FrameSelection frameSelection = FrameSelection.Median) { if (!ProfilingEnabled) { throw new InvalidOperationException("Cannot write profile to file when profiling is not enabled."); } ProfilerFrame frame = default; if (collectedFrames.IsEmpty) { return; } bool needsDispose = false; switch (frameSelection) { case FrameSelection.Median: var ordered = collectedFrames.OrderBy(x => x.frameLength).ToArray(); frame = ordered[(int)Math.Floor(ordered.Length / 2.0)]; break; case FrameSelection.Longest: frame = collectedFrames.MaxBy(x => x.frameLength); break; case FrameSelection.Shortest: frame = collectedFrames.MinBy(x => x.frameLength); break; case FrameSelection.Latest: frame = collectedFrames.Back(); break; case FrameSelection.Percentile95: ordered = collectedFrames.OrderBy(x => x.frameLength).ToArray(); int index = ordered.Length - (int)(ordered.Length * 0.05f); if (index >= ordered.Length) { index = ordered.Length - 1; } frame = ordered[index]; break; case FrameSelection.All: frame = new ProfilerFrame(); frame.frameNumber = collectedFrames.Min(x => x.frameNumber); frame.frameLength = collectedFrames.Sum(x => x.frameLength); frame.markers = PooledList <ProfilerMarker> .Create(); double elapsedTime = 0; foreach (var collectedFrame in collectedFrames) { if (collectedFrame.markers != null) { frame.markers.AddRange(collectedFrame.markers.Select(x => new ProfilerMarker(x.markName, x.time + elapsedTime))); } elapsedTime += collectedFrame.frameLength; } frame.threadMethods = PooledList <(string, PooledList <ProfilerMethod>)> .Create(); elapsedTime = 0; foreach (var collectedFrame in collectedFrames) { foreach (var collectedFrameThreadMethod in collectedFrame.threadMethods) { var tuple = frame.threadMethods.SingleOrDefault(x => x.Item1 == collectedFrameThreadMethod.Item1); PooledList <ProfilerMethod> outList = null; if (tuple.Item1 == null) { outList = PooledList <ProfilerMethod> .Create(); frame.threadMethods.Add((collectedFrameThreadMethod.Item1, outList)); } else { outList = tuple.Item2; } outList.AddRange(collectedFrameThreadMethod.Item2.Select(x => new ProfilerMethod( x.methodName, x.depth, x.startTime + elapsedTime, x.endTime + elapsedTime, x.threadId))); } elapsedTime += collectedFrame.frameLength; } needsDispose = true; break; default: throw new ArgumentOutOfRangeException(nameof(frameSelection), frameSelection, null); } switch (format) { case ProfilingFormat.SpeedScope: SpeedScopeWriter.WriteToFile(frame, filename); break; case ProfilingFormat.ChromeTracing: ChromeTracingWriter.WriteToFile(frame, filename); break; default: throw new ArgumentOutOfRangeException(nameof(format), format, null); } if (needsDispose) { frame.Dispose(); } }
private static void Export(ProfilerFrame source, TextWriter writer, string name) { var events = GetEvents(source); WriteToFile(events, writer, name); }