예제 #1
0
        private static void ConvertToSpeedscope(string fileToConvert, string outputFilename)
        {
            var etlxFilePath = TraceLog.CreateFromEventPipeDataFile(fileToConvert);

            using (var symbolReader = new SymbolReader(System.IO.TextWriter.Null)
            {
                SymbolPath = SymbolPath.MicrosoftSymbolServerPath
            })
                using (var eventLog = new TraceLog(etlxFilePath))
                {
                    var stackSource = new MutableTraceEventStackSource(eventLog)
                    {
                        OnlyManagedCodeStacks = true // EventPipe currently only has managed code stacks.
                    };

                    var computer = new SampleProfilerThreadTimeComputer(eventLog, symbolReader);
                    computer.GenerateThreadTimeStacks(stackSource);

                    SpeedScopeStackSourceWriter.WriteStackViewAsJson(stackSource, outputFilename);
                }

            if (File.Exists(etlxFilePath))
            {
                File.Delete(etlxFilePath);
            }
        }
예제 #2
0
        private static void ConvertToSpeedscope(string fileToConvert)
        {
            var symbolReader = new SymbolReader(System.IO.TextWriter.Null)
            {
                SymbolPath = SymbolPath.MicrosoftSymbolServerPath
            };
            var etlxFilePath = TraceLog.CreateFromEventPipeDataFile(fileToConvert);

            var eventLog = new TraceLog(etlxFilePath);

            try
            {
                var stackSource = new MutableTraceEventStackSource(eventLog)
                {
                    OnlyManagedCodeStacks = true // EventPipe currently only has managed code stacks.
                };

                var computer = new SampleProfilerThreadTimeComputer(eventLog, symbolReader);
                computer.GenerateThreadTimeStacks(stackSource);

                var speedScopeFilePath = Path.ChangeExtension(fileToConvert, "speedscope.json");

                SpeedScopeStackSourceWriter.WriteStackViewAsJson(stackSource, speedScopeFilePath);
            }
            finally
            {
                eventLog.Dispose();

                if (File.Exists(etlxFilePath))
                {
                    File.Delete(etlxFilePath);
                }
            }
        }
예제 #3
0
        // Method copied from https://github.com/dotnet/diagnostics/blob/2c23d3265dd8f642a8d6cf4bb8a135a5ff8b00c2/src/Tools/dotnet-trace/TraceFileFormatConverter.cs#L64
        private static void ConvertToSpeedscope(string fileToConvert, string outputFilename, bool continueOnError = false)
        {
            var etlxFilePath = TraceLog.CreateFromEventPipeDataFile(fileToConvert, null, new TraceLogOptions()
            {
                ContinueOnError = continueOnError
            });

            using (var symbolReader = new SymbolReader(System.IO.TextWriter.Null)
            {
                SymbolPath = SymbolPath.MicrosoftSymbolServerPath
            })
                using (var eventLog = new TraceLog(etlxFilePath))
                {
                    var stackSource = new MutableTraceEventStackSource(eventLog)
                    {
                        OnlyManagedCodeStacks = true // EventPipe currently only has managed code stacks.
                    };

                    var computer = new SampleProfilerThreadTimeComputer(eventLog, symbolReader)
                    {
                        IncludeEventSourceEvents = false // SpeedScope handles only CPU samples, events are not supported
                    };
                    computer.GenerateThreadTimeStacks(stackSource);

                    SpeedScopeStackSourceWriter.WriteStackViewAsJson(stackSource, outputFilename);
                }

            if (File.Exists(etlxFilePath))
            {
                File.Delete(etlxFilePath);
            }
        }
        private static void Convert(TraceFileFormat format, string fileToConvert, string outputFilename, bool continueOnError = false)
        {
            var etlxFilePath = TraceLog.CreateFromEventPipeDataFile(fileToConvert, null, new TraceLogOptions()
            {
                ContinueOnError = continueOnError
            });

            using (var symbolReader = new SymbolReader(TextWriter.Null)
            {
                SymbolPath = SymbolPath.MicrosoftSymbolServerPath
            })
                using (var eventLog = new TraceLog(etlxFilePath))
                {
                    var stackSource = new MutableTraceEventStackSource(eventLog)
                    {
                        OnlyManagedCodeStacks = true // EventPipe currently only has managed code stacks.
                    };

                    var computer = new SampleProfilerThreadTimeComputer(eventLog, symbolReader)
                    {
                        IncludeEventSourceEvents = false // SpeedScope handles only CPU samples, events are not supported
                    };
                    computer.GenerateThreadTimeStacks(stackSource);

                    switch (format)
                    {
                    case TraceFileFormat.Speedscope:
                        SpeedScopeStackSourceWriter.WriteStackViewAsJson(stackSource, outputFilename);
                        break;

                    case TraceFileFormat.Chromium:
                        ChromiumStackSourceWriter.WriteStackViewAsJson(stackSource, outputFilename, compress: false);
                        break;

                    default:
                        // we should never get here
                        throw new ArgumentException($"Invalid TraceFileFormat \"{format}\"");
                    }
                }

            if (File.Exists(etlxFilePath))
            {
                File.Delete(etlxFilePath);
            }
        }
        /// <summary>
        /// Reports a stack trace
        /// </summary>
        /// <param name="ct">The cancellation token</param>
        /// <param name="console"></param>
        /// <param name="processId">The process to report the stack from.</param>
        /// <param name="name">The name of process to report the stack from.</param>
        /// <param name="output">The output path for the collected trace data.</param>
        /// <param name="duration">The duration of to trace the target for. </param>
        /// <returns></returns>
        private static async Task <int> Report(CancellationToken ct, IConsole console, int processId, string name, TimeSpan duration)
        {
            string tempNetTraceFilename = Path.GetRandomFileName() + ".nettrace";
            string tempEtlxFilename     = "";

            try
            {
                // Either processName or processId has to be specified.
                if (!string.IsNullOrEmpty(name))
                {
                    if (processId != 0)
                    {
                        Console.WriteLine("Can only specify either --name or --process-id option.");
                        return(-1);
                    }
                    processId = CommandUtils.FindProcessIdWithName(name);
                    if (processId < 0)
                    {
                        return(-1);
                    }
                }

                if (processId < 0)
                {
                    console.Error.WriteLine("Process ID should not be negative.");
                    return(-1);
                }
                else if (processId == 0)
                {
                    console.Error.WriteLine("--process-id is required");
                    return(-1);
                }


                var client    = new DiagnosticsClient(processId);
                var providers = new List <EventPipeProvider>()
                {
                    new EventPipeProvider("Microsoft-DotNETCore-SampleProfiler", EventLevel.Informational)
                };

                // collect a *short* trace with stack samples
                // the hidden '--duration' flag can increase the time of this trace in case 10ms
                // is too short in a given environment, e.g., resource constrained systems
                // N.B. - This trace INCLUDES rundown.  For sufficiently large applications, it may take non-trivial time to collect
                //        the symbol data in rundown.
                using (EventPipeSession session = client.StartEventPipeSession(providers))
                    using (FileStream fs = File.OpenWrite(tempNetTraceFilename))
                    {
                        Task copyTask = session.EventStream.CopyToAsync(fs);
                        await Task.Delay(duration);

                        session.Stop();

                        // check if rundown is taking more than 5 seconds and add comment to report
                        Task timeoutTask   = Task.Delay(TimeSpan.FromSeconds(5));
                        Task completedTask = await Task.WhenAny(copyTask, timeoutTask);

                        if (completedTask == timeoutTask)
                        {
                            console.Out.WriteLine($"# Sufficiently large applications can cause this command to take non-trivial amounts of time");
                        }
                        await copyTask;
                    }

                // using the generated trace file, symbolocate and compute stacks.
                tempEtlxFilename = TraceLog.CreateFromEventPipeDataFile(tempNetTraceFilename);
                using (var symbolReader = new SymbolReader(System.IO.TextWriter.Null)
                {
                    SymbolPath = SymbolPath.MicrosoftSymbolServerPath
                })
                    using (var eventLog = new TraceLog(tempEtlxFilename))
                    {
                        var stackSource = new MutableTraceEventStackSource(eventLog)
                        {
                            OnlyManagedCodeStacks = true
                        };

                        var computer = new SampleProfilerThreadTimeComputer(eventLog, symbolReader);
                        computer.GenerateThreadTimeStacks(stackSource);

                        var samplesForThread = new Dictionary <int, List <StackSourceSample> >();

                        stackSource.ForEach((sample) =>
                        {
                            var stackIndex = sample.StackIndex;
                            while (!stackSource.GetFrameName(stackSource.GetFrameIndex(stackIndex), false).StartsWith("Thread ("))
                            {
                                stackIndex = stackSource.GetCallerIndex(stackIndex);
                            }

                            // long form for: int.Parse(threadFrame["Thread (".Length..^1)])
                            // Thread id is in the frame name as "Thread (<ID>)"
                            string template    = "Thread (";
                            string threadFrame = stackSource.GetFrameName(stackSource.GetFrameIndex(stackIndex), false);
                            int threadId       = int.Parse(threadFrame.Substring(template.Length, threadFrame.Length - (template.Length + 1)));

                            if (samplesForThread.TryGetValue(threadId, out var samples))
                            {
                                samples.Add(sample);
                            }
                            else
                            {
                                samplesForThread[threadId] = new List <StackSourceSample>()
                                {
                                    sample
                                };
                            }
                        });

                        // For every thread recorded in our trace, print the first stack
                        foreach (var(threadId, samples) in samplesForThread)
                        {
#if DEBUG
                            console.Out.WriteLine($"Found {samples.Count} stacks for thread 0x{threadId:X}");
#endif
                            PrintStack(console, threadId, samples[0], stackSource);
                        }
                    }
            }
            catch (Exception ex)
            {
                Console.Error.WriteLine($"[ERROR] {ex.ToString()}");
                return(-1);
            }
            finally
            {
                if (File.Exists(tempNetTraceFilename))
                {
                    File.Delete(tempNetTraceFilename);
                }
                if (File.Exists(tempEtlxFilename))
                {
                    File.Delete(tempEtlxFilename);
                }
            }

            return(0);
        }
예제 #6
0
        private static async Task <int> TopNReport(CancellationToken ct, IConsole console, string traceFile, int number, bool inclusive, bool verbose)
        {
            try
            {
                string tempEtlxFilename = TraceLog.CreateFromEventPipeDataFile(traceFile);
                int    count            = 0;
                int    index            = 0;
                List <CallTreeNodeBase> nodesToReport = new List <CallTreeNodeBase>();
                using (var symbolReader = new SymbolReader(System.IO.TextWriter.Null)
                {
                    SymbolPath = SymbolPath.MicrosoftSymbolServerPath
                })
                    using (var eventLog = new TraceLog(tempEtlxFilename))
                    {
                        var stackSource = new MutableTraceEventStackSource(eventLog)
                        {
                            OnlyManagedCodeStacks = true
                        };

                        var computer = new SampleProfilerThreadTimeComputer(eventLog, symbolReader);

                        computer.GenerateThreadTimeStacks(stackSource);

                        FilterParams filterParams = new FilterParams()
                        {
                            FoldRegExs = "CPU_TIME;UNMANAGED_CODE_TIME;{Thread (}",
                        };
                        FilterStackSource filterStack = new FilterStackSource(filterParams, stackSource, ScalingPolicyKind.ScaleToData);
                        CallTree          callTree    = new(ScalingPolicyKind.ScaleToData);
                        callTree.StackSource = filterStack;

                        List <CallTreeNodeBase> callTreeNodes = null;

                        if (!inclusive)
                        {
                            callTreeNodes = callTree.ByIDSortedExclusiveMetric();
                        }
                        else
                        {
                            callTreeNodes = callTree.ByIDSortedInclusiveMetric();
                        }

                        int totalElements = callTreeNodes.Count;
                        while (count < number && index < totalElements)
                        {
                            CallTreeNodeBase node = callTreeNodes[index];
                            index++;
                            if (!unwantedMethodNames.Any(node.Name.Contains))
                            {
                                nodesToReport.Add(node);
                                count++;
                            }
                        }

                        PrintReportHelper.TopNWriteToStdOut(nodesToReport, inclusive, verbose);
                    }
                return(await Task.FromResult(0));
            }
            catch (Exception ex)
            {
                Console.Error.WriteLine($"[ERROR] {ex.ToString()}");
            }

            return(await Task.FromResult(0));
        }
예제 #7
0
        public void CanConvertProvidedTraceFiles(string zippedTraceFileName)
        {
            var debugListenersCopy = new TraceListener[Debug.Listeners.Count];

            Debug.Listeners.CopyTo(debugListenersCopy, index: 0);
            Debug.Listeners.Clear();

            string fileToUnzip  = Path.Combine("inputs", "speedscope", zippedTraceFileName);
            string unzippedFile = Path.ChangeExtension(fileToUnzip, string.Empty);

            if (File.Exists(unzippedFile))
            {
                File.Delete(unzippedFile);
            }
            ZipFile.ExtractToDirectory(fileToUnzip, Path.GetDirectoryName(fileToUnzip));
            var etlxFilePath = TraceLog.CreateFromEventPipeDataFile(unzippedFile, null, new TraceLogOptions()
            {
                ContinueOnError = true
            });

            try
            {
                using (var symbolReader = new SymbolReader(TextWriter.Null)
                {
                    SymbolPath = SymbolPath.MicrosoftSymbolServerPath
                })
                    using (var eventLog = new TraceLog(etlxFilePath))
                    {
                        var stackSource = new MutableTraceEventStackSource(eventLog)
                        {
                            OnlyManagedCodeStacks = true // EventPipe currently only has managed code stacks.
                        };

                        var computer = new SampleProfilerThreadTimeComputer(eventLog, symbolReader)
                        {
                            IncludeEventSourceEvents = false // SpeedScope handles only CPU samples, events are not supported
                        };
                        computer.GenerateThreadTimeStacks(stackSource);

                        var samplesPerThread = GetSortedSamplesPerThread(stackSource);

                        var exportedFrameNameToExportedFrameId = new Dictionary <string, int>();
                        var exportedFrameIdToFrameTuple        = new Dictionary <int, FrameInfo>();
                        var profileEventsPerThread             = new Dictionary <string, IReadOnlyList <ProfileEvent> >();

                        foreach (var pair in samplesPerThread)
                        {
                            var sortedProfileEvents = GetProfileEvents(stackSource, pair.Value, exportedFrameNameToExportedFrameId, exportedFrameIdToFrameTuple);

                            Assert.True(Validate(sortedProfileEvents), "The output should be always valid");

                            profileEventsPerThread.Add(pair.Key.Name, sortedProfileEvents);
                        }
                        ;
                    }
            }
            finally
            {
                if (File.Exists(etlxFilePath))
                {
                    File.Delete(etlxFilePath);
                }
                if (File.Exists(unzippedFile))
                {
                    File.Delete(unzippedFile);
                }
                if (debugListenersCopy.Length > 0)
                {
                    Debug.Listeners.AddRange(debugListenersCopy);
                }
            }
        }
예제 #8
0
        static async Task <int> Main(string[] args)
        {
            if (args.Length == 0 || args.Length > 1 || (args.Length == 1 && _helpFlags.Contains(args[0])))
            {
                PrintUsage();
                return(-1);
            }

            var pid = int.Parse(args[0]);

            var tempNetTraceFilename = Path.GetRandomFileName() + ".nettrace";
            var tempEtlxFilename     = "";

            try
            {
                var client    = new DiagnosticsClient(pid);
                var providers = new List <EventPipeProvider>()
                {
                    new EventPipeProvider("Microsoft-DotNETCore-SampleProfiler", EventLevel.Informational)
                };

                using (EventPipeSession session = client.StartEventPipeSession(providers))
                    using (FileStream fs = File.OpenWrite(tempNetTraceFilename))
                    {
                        Task copyTask = session.EventStream.CopyToAsync(fs);
                        await Task.Delay(10);

                        session.Stop();
                        await copyTask;
                    }

                tempEtlxFilename = TraceLog.CreateFromEventPipeDataFile(tempNetTraceFilename);
                using (var symbolReader = new SymbolReader(System.IO.TextWriter.Null)
                {
                    SymbolPath = SymbolPath.MicrosoftSymbolServerPath
                })
                    using (var eventLog = new TraceLog(tempEtlxFilename))
                    {
                        var stackSource = new MutableTraceEventStackSource(eventLog)
                        {
                            OnlyManagedCodeStacks = true
                        };

                        var computer = new SampleProfilerThreadTimeComputer(eventLog, symbolReader);
                        computer.GenerateThreadTimeStacks(stackSource);

                        var samplesForThread = new Dictionary <string, List <StackSourceSample> >();

                        stackSource.ForEach((sample) =>
                        {
                            var stackIndex = sample.StackIndex;
                            while (!stackSource.GetFrameName(stackSource.GetFrameIndex(stackIndex), false).StartsWith("Thread ("))
                            {
                                stackIndex = stackSource.GetCallerIndex(stackIndex);
                            }

                            var threadName = stackSource.GetFrameName(stackSource.GetFrameIndex(stackIndex), false);
                            if (samplesForThread.TryGetValue(threadName, out var samples))
                            {
                                samples.Add(sample);
                            }
                            else
                            {
                                samplesForThread[threadName] = new List <StackSourceSample>()
                                {
                                    sample
                                };
                            }
                        });

                        foreach (var(threadName, samples) in samplesForThread)
                        {
#if DEBUG
                            Console.WriteLine($"Found {samples.Count} stacks for thread {threadName}");
#endif
                            PrintStack(threadName, samples[0], stackSource);
                        }
                    }
                return(0);
            }
            catch (System.Exception e)
            {
                Console.WriteLine(e);
                return(-1);
            }
            finally
            {
                if (File.Exists(tempNetTraceFilename))
                {
                    File.Delete(tempNetTraceFilename);
                }
                if (File.Exists(tempEtlxFilename))
                {
                    File.Delete(tempEtlxFilename);
                }
            }
        }
예제 #9
0
        /// <summary>
        /// Reads trace and prints managed stacks
        /// </summary>
        /// <param name="console"></param>
        /// <param name="traceFile"></param>
        /// <returns>Returns 0 for success and 1 for failure</returns>
        /// <remarks>This code is adopted from josalem code https://github.com/josalem/DotStack </remarks>
        public static int Analyze(IConsole console, FileInfo traceFile)
        {
            if (string.IsNullOrEmpty(traceFile.FullName))
            {
                Console.ForegroundColor = ConsoleColor.Red;
                console.Error.WriteLine("-f [process-id] Process ID is required.");
                Console.ResetColor();
                return(1);
            }

            Console.WriteLine("[Trace analysis started...]\n");

            // Both the namespaces have TraceLog, here we use Microsoft.Diagnostics.Tracing.Etlx.TraceLog;
            // The following line creates a etlx file and then does analysis using that.
            string tempEtlxFilename = TraceLog.CreateFromEventPipeDataFile(traceFile.FullName);

            using (var symbolReader = new SymbolReader(System.IO.TextWriter.Null)
            {
                SymbolPath = SymbolPath.MicrosoftSymbolServerPath
            })

                using (var eventLog = new TraceLog(tempEtlxFilename))
                {
                    var stackSource = new MutableTraceEventStackSource(eventLog)
                    {
                        OnlyManagedCodeStacks = true
                    };

                    var computer = new SampleProfilerThreadTimeComputer(eventLog, symbolReader);
                    computer.GenerateThreadTimeStacks(stackSource);

                    var samplesForThread = new Dictionary <string, List <StackSourceSample> >();

                    stackSource.ForEach((sample) =>
                    {
                        var stackIndex = sample.StackIndex;
                        while (!stackSource.GetFrameName(stackSource.GetFrameIndex(stackIndex), false).StartsWith("Thread ("))
                        {
                            stackIndex = stackSource.GetCallerIndex(stackIndex);
                        }

                        var threadName = stackSource.GetFrameName(stackSource.GetFrameIndex(stackIndex), false);
                        if (samplesForThread.TryGetValue(threadName, out var samples))
                        {
                            samples.Add(sample);
                        }
                        else
                        {
                            samplesForThread[threadName] = new List <StackSourceSample>()
                            {
                                sample
                            };
                        }
                    });

                    foreach (var(threadName, samples) in samplesForThread)
                    {
                        PrintStack(threadName, samples[0], stackSource);
                    }
                }

            Console.WriteLine("\n[Trace analysis completed...]");
            return(0);
        }