Exemplo n.º 1
0
 private bool ValidateAndSetProcessId(int processId, string name)
 {
     if (!ProcessLauncher.Launcher.HasChildProc)
     {
         if (name != null)
         {
             if (processId != 0)
             {
                 Console.WriteLine("Can only specify either --name or --process-id option.");
                 return(false);
             }
             processId = CommandUtils.FindProcessIdWithName(name);
             if (processId < 0)
             {
                 return(false);
             }
         }
         else if (processId == 0)
         {
             Console.WriteLine("Must specify either --name or --process-id option to start collecting.");
             return(false);
         }
     }
     _processId = processId;
     return(true);
 }
Exemplo n.º 2
0
        public async Task <int> Collect(CancellationToken ct, List <string> counter_list, IConsole console, int processId, int refreshInterval, CountersExportFormat format, string output, string name)
        {
            if (name != null)
            {
                if (processId != 0)
                {
                    Console.WriteLine("Can only specify either --name or --process-id option.");
                    return(0);
                }
                processId = CommandUtils.FindProcessIdWithName(name);
                if (processId < 0)
                {
                    return(0);
                }
            }

            try
            {
                _ct                = ct;
                _counterList       = counter_list; // NOTE: This variable name has an underscore because that's the "name" that the CLI displays. System.CommandLine doesn't like it if we change the variable to camelcase.
                _console           = console;
                _processId         = processId;
                _interval          = refreshInterval;
                _output            = output;
                _diagnosticsClient = new DiagnosticsClient(processId);

                if (_output.Length == 0)
                {
                    _console.Error.WriteLine("Output cannot be an empty string");
                    return(0);
                }

                if (format == CountersExportFormat.csv)
                {
                    _renderer = new CSVExporter(output);
                }
                else if (format == CountersExportFormat.json)
                {
                    // Try getting the process name.
                    string processName = "";
                    try
                    {
                        processName = Process.GetProcessById(_processId).ProcessName;
                    }
                    catch (Exception) { }
                    _renderer = new JSONExporter(output, processName);;
                }
                else
                {
                    _console.Error.WriteLine($"The output format {format} is not a valid output format.");
                    return(0);
                }
                return(await Start());
            }
            catch (OperationCanceledException)
            {
            }

            return(1);
        }
Exemplo n.º 3
0
        public async Task <int> Monitor(CancellationToken ct, List <string> counter_list, IConsole console, int processId, int refreshInterval, string name)
        {
            if (name != null)
            {
                if (processId != 0)
                {
                    Console.WriteLine("Can only specify either --name or --process-id option.");
                    return(0);
                }
                processId = CommandUtils.FindProcessIdWithName(name);
                if (processId < 0)
                {
                    return(0);
                }
            }

            try
            {
                _ct                = ct;
                _counterList       = counter_list; // NOTE: This variable name has an underscore because that's the "name" that the CLI displays. System.CommandLine doesn't like it if we change the variable to camelcase.
                _console           = console;
                _processId         = processId;
                _interval          = refreshInterval;
                _renderer          = new ConsoleWriter();
                _diagnosticsClient = new DiagnosticsClient(processId);

                return(await Start());
            }

            catch (OperationCanceledException)
            {
                try
                {
                    _session.Stop();
                }
                catch (Exception) {} // Swallow all exceptions for now.

                console.Out.WriteLine($"Complete");
                return(1);
            }
        }
Exemplo n.º 4
0
        /// <summary>
        /// Collects a diagnostic trace from a currently running process.
        /// </summary>
        /// <param name="ct">The cancellation token</param>
        /// <param name="console"></param>
        /// <param name="processId">The process to collect the trace from.</param>
        /// <param name="name">The name of process to collect the trace from.</param>
        /// <param name="output">The output path for the collected trace data.</param>
        /// <param name="buffersize">Sets the size of the in-memory circular buffer in megabytes.</param>
        /// <param name="providers">A list of EventPipe providers to be enabled. This is in the form 'Provider[,Provider]', where Provider is in the form: 'KnownProviderName[:Flags[:Level][:KeyValueArgs]]', and KeyValueArgs is in the form: '[key1=value1][;key2=value2]'</param>
        /// <param name="profile">A named pre-defined set of provider configurations that allows common tracing scenarios to be specified succinctly.</param>
        /// <param name="format">The desired format of the created trace file.</param>
        /// <param name="duration">The duration of trace to be taken. </param>
        /// <param name="clrevents">A list of CLR events to be emitted.</param>
        /// <param name="clreventlevel">The verbosity level of CLR events</param>
        /// <returns></returns>
        private static async Task <int> Collect(CancellationToken ct, IConsole console, int processId, FileInfo output, uint buffersize, string providers, string profile, TraceFileFormat format, TimeSpan duration, string clrevents, string clreventlevel, string name)
        {
            try
            {
                Debug.Assert(output != null);
                Debug.Assert(profile != null);

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

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

                if (profile.Length == 0 && providers.Length == 0 && clrevents.Length == 0)
                {
                    Console.Out.WriteLine("No profile or providers specified, defaulting to trace profile 'cpu-sampling'");
                    profile = "cpu-sampling";
                }

                Dictionary <string, string> enabledBy = new Dictionary <string, string>();

                var providerCollection = Extensions.ToProviders(providers);
                foreach (EventPipeProvider providerCollectionProvider in providerCollection)
                {
                    enabledBy[providerCollectionProvider.Name] = "--providers ";
                }

                if (profile.Length != 0)
                {
                    var selectedProfile = ListProfilesCommandHandler.DotNETRuntimeProfiles
                                          .FirstOrDefault(p => p.Name.Equals(profile, StringComparison.OrdinalIgnoreCase));
                    if (selectedProfile == null)
                    {
                        Console.Error.WriteLine($"Invalid profile name: {profile}");
                        return(ErrorCodes.ArgumentError);
                    }

                    Profile.MergeProfileAndProviders(selectedProfile, providerCollection, enabledBy);
                }

                // Parse --clrevents parameter
                if (clrevents.Length != 0)
                {
                    // Ignore --clrevents if CLR event provider was already specified via --profile or --providers command.
                    if (enabledBy.ContainsKey(Extensions.CLREventProviderName))
                    {
                        Console.WriteLine($"The argument --clrevents {clrevents} will be ignored because the CLR provider was configured via either --profile or --providers command.");
                    }
                    else
                    {
                        var clrProvider = Extensions.ToCLREventPipeProvider(clrevents, clreventlevel);
                        providerCollection.Add(clrProvider);
                        enabledBy[Extensions.CLREventProviderName] = "--clrevents";
                    }
                }


                if (providerCollection.Count <= 0)
                {
                    Console.Error.WriteLine("No providers were specified to start a trace.");
                    return(ErrorCodes.ArgumentError);
                }

                PrintProviders(providerCollection, enabledBy);

                var process    = Process.GetProcessById(processId);
                var shouldExit = new ManualResetEvent(false);
                var shouldStopAfterDuration       = duration != default(TimeSpan);
                var rundownRequested              = false;
                System.Timers.Timer durationTimer = null;

                ct.Register(() => shouldExit.Set());

                var diagnosticsClient = new DiagnosticsClient(processId);
                using (VirtualTerminalMode vTermMode = VirtualTerminalMode.TryEnable())
                {
                    EventPipeSession session = null;
                    try
                    {
                        session = diagnosticsClient.StartEventPipeSession(providerCollection, true, (int)buffersize);
                    }
                    catch (DiagnosticsClientException e)
                    {
                        Console.Error.WriteLine($"Unable to start a tracing session: {e.ToString()}");
                    }

                    if (session == null)
                    {
                        Console.Error.WriteLine("Unable to create session.");
                        return(ErrorCodes.SessionCreationError);
                    }

                    if (shouldStopAfterDuration)
                    {
                        durationTimer           = new System.Timers.Timer(duration.TotalMilliseconds);
                        durationTimer.Elapsed  += (s, e) => shouldExit.Set();
                        durationTimer.AutoReset = false;
                    }

                    var stopwatch = new Stopwatch();
                    durationTimer?.Start();
                    stopwatch.Start();

                    LineRewriter rewriter = null;

                    using (var fs = new FileStream(output.FullName, FileMode.Create, FileAccess.Write))
                    {
                        Console.Out.WriteLine($"Process        : {process.MainModule.FileName}");
                        Console.Out.WriteLine($"Output File    : {fs.Name}");
                        if (shouldStopAfterDuration)
                        {
                            Console.Out.WriteLine($"Trace Duration : {duration.ToString(@"dd\:hh\:mm\:ss")}");
                        }
                        Console.Out.WriteLine("\n\n");

                        var  fileInfo = new FileInfo(output.FullName);
                        Task copyTask = session.EventStream.CopyToAsync(fs);

                        if (!Console.IsOutputRedirected)
                        {
                            rewriter = new LineRewriter {
                                LineToClear = Console.CursorTop - 1
                            };
                            Console.CursorVisible = false;
                        }

                        Action printStatus = () =>
                        {
                            if (!Console.IsOutputRedirected)
                            {
                                rewriter?.RewriteConsoleLine();
                            }

                            fileInfo.Refresh();
                            Console.Out.WriteLine($"[{stopwatch.Elapsed.ToString(@"dd\:hh\:mm\:ss")}]\tRecording trace {GetSize(fileInfo.Length)}");
                            Console.Out.WriteLine("Press <Enter> or <Ctrl+C> to exit...");
                            if (rundownRequested)
                            {
                                Console.Out.WriteLine("Stopping the trace. This may take up to minutes depending on the application being traced.");
                            }
                        };

                        while (!shouldExit.WaitOne(100) && !(!Console.IsInputRedirected && Console.KeyAvailable && Console.ReadKey(true).Key == ConsoleKey.Enter))
                        {
                            printStatus();
                        }

                        // Behavior concerning Enter moving text in the terminal buffer when at the bottom of the buffer
                        // is different between Console/Terminals on Windows and Mac/Linux
                        if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows) &&
                            !Console.IsOutputRedirected &&
                            rewriter != null &&
                            Math.Abs(Console.CursorTop - Console.BufferHeight) == 1)
                        {
                            rewriter.LineToClear--;
                        }
                        durationTimer?.Stop();
                        rundownRequested = true;
                        session.Stop();

                        do
                        {
                            printStatus();
                        } while (!copyTask.Wait(100));
                    }

                    Console.Out.WriteLine("\nTrace completed.");

                    if (format != TraceFileFormat.NetTrace)
                    {
                        TraceFileFormatConverter.ConvertToFormat(format, output.FullName);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.Error.WriteLine($"[ERROR] {ex.ToString()}");
                return(ErrorCodes.TracingError);
            }
            finally
            {
                if (console.GetTerminal() != null)
                {
                    Console.CursorVisible = true;
                }
            }

            return(await Task.FromResult(0));
        }
        /// <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);
        }
Exemplo n.º 6
0
        /// <summary>
        /// Collects a gcdump from a currently running process.
        /// </summary>
        /// <param name="ct">The cancellation token</param>
        /// <param name="console"></param>
        /// <param name="processId">The process to collect the gcdump from.</param>
        /// <param name="output">The output path for the collected gcdump.</param>
        /// <returns></returns>
        private static async Task <int> Collect(CancellationToken ct, IConsole console, int processId, string output, int timeout, bool verbose, string name)
        {
            if (name != null)
            {
                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);
                }
            }

            try
            {
                if (processId < 0)
                {
                    Console.Out.WriteLine($"The PID cannot be negative: {processId}");
                    return(-1);
                }

                if (processId == 0)
                {
                    Console.Out.WriteLine("-p|--process-id is required");
                    return(-1);
                }

                output = string.IsNullOrEmpty(output)
                    ? $"{DateTime.Now:yyyyMMdd\\_HHmmss}_{processId}.gcdump"
                    : output;

                FileInfo outputFileInfo = new FileInfo(output);

                if (outputFileInfo.Exists)
                {
                    outputFileInfo.Delete();
                }

                if (string.IsNullOrEmpty(outputFileInfo.Extension) || outputFileInfo.Extension != ".gcdump")
                {
                    outputFileInfo = new FileInfo(outputFileInfo.FullName + ".gcdump");
                }

                Console.Out.WriteLine($"Writing gcdump to '{outputFileInfo.FullName}'...");

                var dumpTask = Task.Run(() =>
                {
                    if (TryCollectMemoryGraph(ct, processId, timeout, verbose, out var memoryGraph))
                    {
                        GCHeapDump.WriteMemoryGraph(memoryGraph, outputFileInfo.FullName, "dotnet-gcdump");
                        return(true);
                    }

                    return(false);
                });

                var fDumpSuccess = await dumpTask;

                if (fDumpSuccess)
                {
                    outputFileInfo.Refresh();
                    Console.Out.WriteLine($"\tFinished writing {outputFileInfo.Length} bytes.");
                    return(0);
                }
                else if (ct.IsCancellationRequested)
                {
                    Console.Out.WriteLine("\tCancelled.");
                    return(-1);
                }
                else
                {
                    Console.Out.WriteLine("\tFailed to collect gcdump. Try running with '-v' for more information.");
                    return(-1);
                }
            }
            catch (Exception ex)
            {
                Console.Error.WriteLine($"[ERROR] {ex.ToString()}");
                return(-1);
            }
        }
Exemplo n.º 7
0
        /// <summary>
        /// Collects a diagnostic trace from a currently running process.
        /// </summary>
        /// <param name="ct">The cancellation token</param>
        /// <param name="console"></param>
        /// <param name="processId">The process to collect the trace from.</param>
        /// <param name="name">The name of process to collect the trace from.</param>
        /// <param name="output">The output path for the collected trace data.</param>
        /// <param name="buffersize">Sets the size of the in-memory circular buffer in megabytes.</param>
        /// <param name="providers">A list of EventPipe providers to be enabled. This is in the form 'Provider[,Provider]', where Provider is in the form: 'KnownProviderName[:Flags[:Level][:KeyValueArgs]]', and KeyValueArgs is in the form: '[key1=value1][;key2=value2]'</param>
        /// <param name="profile">A named pre-defined set of provider configurations that allows common tracing scenarios to be specified succinctly.</param>
        /// <param name="format">The desired format of the created trace file.</param>
        /// <param name="duration">The duration of trace to be taken. </param>
        /// <param name="clrevents">A list of CLR events to be emitted.</param>
        /// <param name="clreventlevel">The verbosity level of CLR events</param>
        /// <returns></returns>
        private static async Task <int> Collect(CancellationToken ct, IConsole console, int processId, FileInfo output, uint buffersize, string providers, string profile, TraceFileFormat format, TimeSpan duration, string clrevents, string clreventlevel, string name)
        {
            try
            {
                Debug.Assert(output != null);
                Debug.Assert(profile != null);

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

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

                bool hasConsole = console.GetTerminal() != null;

                if (hasConsole)
                {
                    Console.Clear();
                }

                if (profile.Length == 0 && providers.Length == 0 && clrevents.Length == 0)
                {
                    Console.Out.WriteLine("No profile or providers specified, defaulting to trace profile 'cpu-sampling'");
                    profile = "cpu-sampling";
                }

                Dictionary <string, string> enabledBy = new Dictionary <string, string>();

                var providerCollection = Extensions.ToProviders(providers);
                foreach (EventPipeProvider providerCollectionProvider in providerCollection)
                {
                    enabledBy[providerCollectionProvider.Name] = "--providers ";
                }

                if (profile.Length != 0)
                {
                    var selectedProfile = ListProfilesCommandHandler.DotNETRuntimeProfiles
                                          .FirstOrDefault(p => p.Name.Equals(profile, StringComparison.OrdinalIgnoreCase));
                    if (selectedProfile == null)
                    {
                        Console.Error.WriteLine($"Invalid profile name: {profile}");
                        return(ErrorCodes.ArgumentError);
                    }

                    Profile.MergeProfileAndProviders(selectedProfile, providerCollection, enabledBy);
                }

                // Parse --clrevents parameter
                if (clrevents.Length != 0)
                {
                    // Ignore --clrevents if CLR event provider was already specified via --profile or --providers command.
                    if (enabledBy.ContainsKey(Extensions.CLREventProviderName))
                    {
                        Console.WriteLine($"The argument --clrevents {clrevents} will be ignored because the CLR provider was configured via either --profile or --providers command.");
                    }
                    else
                    {
                        var clrProvider = Extensions.ToCLREventPipeProvider(clrevents, clreventlevel);
                        providerCollection.Add(clrProvider);
                        enabledBy[Extensions.CLREventProviderName] = "--clrevents";
                    }
                }


                if (providerCollection.Count <= 0)
                {
                    Console.Error.WriteLine("No providers were specified to start a trace.");
                    return(ErrorCodes.ArgumentError);
                }

                PrintProviders(providerCollection, enabledBy);

                var process    = Process.GetProcessById(processId);
                var shouldExit = new ManualResetEvent(false);
                var shouldStopAfterDuration = duration != default(TimeSpan);
                var failed           = false;
                var terminated       = false;
                var rundownRequested = false;
                System.Timers.Timer durationTimer = null;

                ct.Register(() => shouldExit.Set());

                var diagnosticsClient = new DiagnosticsClient(processId);
                using (VirtualTerminalMode vTermMode = VirtualTerminalMode.TryEnable())
                {
                    EventPipeSession session = null;
                    try
                    {
                        session = diagnosticsClient.StartEventPipeSession(providerCollection, true);
                    }
                    catch (DiagnosticsClientException e)
                    {
                        Console.Error.WriteLine($"Unable to start a tracing session: {e.ToString()}");
                    }

                    if (session == null)
                    {
                        Console.Error.WriteLine("Unable to create session.");
                        return(ErrorCodes.SessionCreationError);
                    }

                    if (shouldStopAfterDuration)
                    {
                        durationTimer           = new System.Timers.Timer(duration.TotalMilliseconds);
                        durationTimer.Elapsed  += (s, e) => shouldExit.Set();
                        durationTimer.AutoReset = false;
                    }

                    var collectingTask = new Task(() =>
                    {
                        try
                        {
                            var stopwatch = new Stopwatch();
                            durationTimer?.Start();
                            stopwatch.Start();

                            using (var fs = new FileStream(output.FullName, FileMode.Create, FileAccess.Write))
                            {
                                Console.Out.WriteLine($"Process        : {process.MainModule.FileName}");
                                Console.Out.WriteLine($"Output File    : {fs.Name}");
                                if (shouldStopAfterDuration)
                                {
                                    Console.Out.WriteLine($"Trace Duration : {duration.ToString(@"dd\:hh\:mm\:ss")}");
                                }

                                Console.Out.WriteLine("\n\n");
                                var buffer = new byte[16 * 1024];

                                while (true)
                                {
                                    int nBytesRead = session.EventStream.Read(buffer, 0, buffer.Length);
                                    if (nBytesRead <= 0)
                                    {
                                        break;
                                    }
                                    fs.Write(buffer, 0, nBytesRead);

                                    if (!rundownRequested)
                                    {
                                        if (hasConsole)
                                        {
                                            lineToClear = Console.CursorTop - 1;
                                            ResetCurrentConsoleLine(vTermMode.IsEnabled);
                                        }

                                        Console.Out.WriteLine($"[{stopwatch.Elapsed.ToString(@"dd\:hh\:mm\:ss")}]\tRecording trace {GetSize(fs.Length)}");
                                        Console.Out.WriteLine("Press <Enter> or <Ctrl+C> to exit...");
                                        Debug.WriteLine($"PACKET: {Convert.ToBase64String(buffer, 0, nBytesRead)} (bytes {nBytesRead})");
                                    }
                                }
                            }
                        }
                        catch (Exception ex)
                        {
                            failed = true;
                            Console.Error.WriteLine($"[ERROR] {ex.ToString()}");
                        }
                        finally
                        {
                            terminated = true;
                            shouldExit.Set();
                        }
                    });
                    collectingTask.Start();

                    do
                    {
                        while (!Console.KeyAvailable && !shouldExit.WaitOne(250))
                        {
                        }
                    } while (!shouldExit.WaitOne(0) && Console.ReadKey(true).Key != ConsoleKey.Enter);

                    if (!terminated)
                    {
                        durationTimer?.Stop();
                        if (hasConsole)
                        {
                            lineToClear = Console.CursorTop;
                            ResetCurrentConsoleLine(vTermMode.IsEnabled);
                        }
                        Console.Out.WriteLine("Stopping the trace. This may take up to minutes depending on the application being traced.");
                        rundownRequested = true;
                        session.Stop();
                    }
                    await collectingTask;
                }

                Console.Out.WriteLine();
                Console.Out.WriteLine("Trace completed.");

                if (format != TraceFileFormat.NetTrace)
                {
                    TraceFileFormatConverter.ConvertToFormat(format, output.FullName);
                }

                return(failed ? ErrorCodes.TracingError : 0);
            }
            catch (Exception ex)
            {
                Console.Error.WriteLine($"[ERROR] {ex.ToString()}");
                return(ErrorCodes.UnknownError);
            }
        }
Exemplo n.º 8
0
        public int Collect(IConsole console, int processId, string output, bool diag, DumpTypeOption type, string name)
        {
            Console.WriteLine(name);
            if (name != null)
            {
                if (processId != 0)
                {
                    Console.WriteLine("Can only specify either --name or --process-id option.");
                    return(0);
                }
                processId = CommandUtils.FindProcessIdWithName(name);
                if (processId < 0)
                {
                    return(0);
                }
            }

            if (processId == 0)
            {
                console.Error.WriteLine("ProcessId is required.");
                return(1);
            }

            try
            {
                if (output == null)
                {
                    // Build timestamp based file path
                    string timestamp = $"{DateTime.Now:yyyyMMdd_HHmmss}";
                    output = Path.Combine(Directory.GetCurrentDirectory(), RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? $"dump_{timestamp}.dmp" : $"core_{timestamp}");
                }
                // Make sure the dump path is NOT relative. This path could be sent to the runtime
                // process on Linux which may have a different current directory.
                output = Path.GetFullPath(output);

                // Display the type of dump and dump path
                string dumpTypeMessage = null;
                switch (type)
                {
                case DumpTypeOption.Full:
                    dumpTypeMessage = "full";
                    break;

                case DumpTypeOption.Heap:
                    dumpTypeMessage = "dump with heap";
                    break;

                case DumpTypeOption.Mini:
                    dumpTypeMessage = "dump";
                    break;
                }
                console.Out.WriteLine($"Writing {dumpTypeMessage} to {output}");

                if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                {
                    // Get the process
                    Process process = Process.GetProcessById(processId);

                    Windows.CollectDump(process, output, type);
                }
                else
                {
                    var client = new DiagnosticsClient(processId);

                    DumpType dumpType = DumpType.Normal;
                    switch (type)
                    {
                    case DumpTypeOption.Full:
                        dumpType = DumpType.Full;
                        break;

                    case DumpTypeOption.Heap:
                        dumpType = DumpType.WithHeap;
                        break;

                    case DumpTypeOption.Mini:
                        dumpType = DumpType.Normal;
                        break;
                    }

                    // Send the command to the runtime to initiate the core dump
                    client.WriteDump(dumpType, output, diag);
                }
            }
            catch (Exception ex) when
                (ex is FileNotFoundException ||
                ex is DirectoryNotFoundException ||
                ex is UnauthorizedAccessException ||
                ex is PlatformNotSupportedException ||
                ex is InvalidDataException ||
                ex is InvalidOperationException ||
                ex is NotSupportedException ||
                ex is DiagnosticsClientException)
            {
                console.Error.WriteLine($"{ex.Message}");
                return(1);
            }

            console.Out.WriteLine($"Complete");
            return(0);
        }