-
Notifications
You must be signed in to change notification settings - Fork 383
Enable dotnet-trace and dotnet-counters collection from startup #1635
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 25 commits
cc40349
f9d96f7
51dca9b
907ada3
4eaba69
7a873d0
e4e463e
c5e542d
643cc1c
8df4435
2229e45
583fcef
8529324
cd96473
0878632
c1b6003
8c01c55
c451374
02451f6
f381430
154bb17
70ebf8d
8e80aa4
7a16985
326cdd3
bcdae64
b32f366
27a2565
2500d03
e25dd08
ae4eec3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,112 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
| // See the LICENSE file in the project root for more information. | ||
|
|
||
| using Microsoft.Diagnostics.NETCore.Client; | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Diagnostics; | ||
| using System.IO; | ||
| using System.Threading; | ||
| using System.Threading.Tasks; | ||
|
|
||
| namespace Microsoft.Internal.Common.Utils | ||
| { | ||
| // <summary> | ||
| // ProcessLauncher is a child-process launcher for "diagnostics tools at startup" scenarios | ||
| // It launches the target process at startup and passes its processId to the corresponding Command handler. | ||
| // </summary> | ||
| internal class ProcessLauncher | ||
| { | ||
| private Process _childProc; | ||
| public ManualResetEvent HasExited; | ||
sywhang marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| internal static ProcessLauncher Launcher = new ProcessLauncher(); | ||
|
|
||
| public void PrepareChildProcess(List<string> args) | ||
| { | ||
| _childProc = new Process(); | ||
| _childProc.StartInfo.FileName = args[0]; | ||
| _childProc.StartInfo.Arguments = String.Join(" ", args.GetRange(1, args.Count - 1)); | ||
sywhang marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| public bool HasChildProc | ||
sywhang marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| { | ||
| get | ||
| { | ||
| return _childProc != null; | ||
| } | ||
| } | ||
|
|
||
| public Process ChildProc | ||
| { | ||
| get | ||
| { | ||
| return _childProc; | ||
| } | ||
| } | ||
| public bool Start(string diagnosticTransportName) | ||
| { | ||
| HasExited = new ManualResetEvent(false); | ||
| _childProc.StartInfo.UseShellExecute = false; | ||
| _childProc.StartInfo.RedirectStandardOutput = true; | ||
| _childProc.StartInfo.RedirectStandardError = true; | ||
| _childProc.StartInfo.RedirectStandardInput = true; | ||
| _childProc.StartInfo.Environment.Add("DOTNET_DiagnosticPorts", $"{diagnosticTransportName},suspend"); | ||
| _childProc.Exited += new EventHandler(OnExited); | ||
sywhang marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
sywhang marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| try | ||
| { | ||
| _childProc.Start(); | ||
| } | ||
| catch (Exception e) | ||
| { | ||
| Console.WriteLine($"Cannot start target process: {_childProc.StartInfo.FileName}"); | ||
sywhang marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| Console.WriteLine(e.ToString()); | ||
| return false; | ||
| } | ||
| return true; | ||
| } | ||
|
|
||
| private static void OnExited(object sender, EventArgs args) | ||
| { | ||
| ProcessLauncher.Launcher.HasExited.Set(); | ||
| } | ||
| } | ||
|
|
||
| // <summary> | ||
| // This class acts a helper class for building a DiagnosticsClient instance | ||
| // </summary> | ||
| internal class ReversedDiagnosticsClientBuilder | ||
| { | ||
| private static string GetRandomTransportName() => "DOTNET_TOOL_REVERSE_TRANSPORT_" + Path.GetRandomFileName(); | ||
sywhang marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| private string diagnosticTransportName; | ||
| private ReversedDiagnosticsServer server; | ||
| private ProcessLauncher _childProcLauncher; | ||
|
|
||
| public ReversedDiagnosticsClientBuilder(ProcessLauncher childProcLauncher) | ||
| { | ||
| diagnosticTransportName = GetRandomTransportName(); | ||
| _childProcLauncher = childProcLauncher; | ||
| server = new ReversedDiagnosticsServer(diagnosticTransportName); | ||
| server.Start(); | ||
| } | ||
|
|
||
| // <summary> | ||
| // Starts the child process and returns the diagnostics client once the child proc connects to the reversed diagnostics pipe. | ||
| // The callee needs to resume the diagnostics client at appropriate time. | ||
| // </summary> | ||
| public DiagnosticsClient Build(int timeoutInSec) | ||
sywhang marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| { | ||
| if (!_childProcLauncher.HasChildProc) | ||
| { | ||
| throw new InvalidOperationException("Must have a valid child process to launch."); | ||
| } | ||
| if (!_childProcLauncher.Start(diagnosticTransportName)) | ||
| { | ||
| throw new InvalidOperationException("Failed to start dotnet-counters."); | ||
| } | ||
| IpcEndpointInfo endpointInfo = server.Accept(TimeSpan.FromSeconds(timeoutInSec)); | ||
| return new DiagnosticsClient(endpointInfo.Endpoint); | ||
sywhang marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,6 +3,7 @@ | |
| // See the LICENSE file in the project root for more information. | ||
|
|
||
| using Microsoft.Internal.Common.Commands; | ||
| using Microsoft.Internal.Common.Utils; | ||
| using Microsoft.Tools.Common; | ||
| using System; | ||
| using System.Collections.Generic; | ||
|
|
@@ -23,6 +24,15 @@ internal class Program | |
| { | ||
| delegate Task<int> ExportDelegate(CancellationToken ct, List<string> counter_list, IConsole console, int processId, int refreshInterval, CountersExportFormat format, string output, string processName); | ||
|
|
||
| private IEnumerable<Command> StartupCommands | ||
| { | ||
| get | ||
| { | ||
| yield return MonitorCommand(); | ||
| yield return CollectCommand(); | ||
| } | ||
| } | ||
|
|
||
| private static Command MonitorCommand() => | ||
| new Command( | ||
| name: "monitor", | ||
|
|
@@ -33,7 +43,7 @@ private static Command MonitorCommand() => | |
| // Arguments and Options | ||
| CounterList(), ProcessIdOption(), RefreshIntervalOption(), NameOption() | ||
| }; | ||
|
|
||
| private static Command CollectCommand() => | ||
| new Command( | ||
| name: "collect", | ||
|
|
@@ -145,6 +155,19 @@ private static Task<int> Main(string[] args) | |
| .AddCommand(ProcessStatusCommandHandler.ProcessStatusCommand("Lists the dotnet processes that can be monitored")) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This comment should go on the CounterList() above but github doesn't let me mark it there. The "--" argument has a standardized meaning that it terminates option processing. Some examples: I thought I had commented on this before but it turned out I never hit submit so you never saw those comments
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks, I added the --counters argument and made it work only when we're attaching at startup so that we're backwards compatible. i.e. The old syntax
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I'd suggest making the --counters argument work all the time and document it as the intended way people should use the tool. Specifying counters as positional arguments becomes the thing that we allow when --process-id is specified to preserve back-compat for people running it via the previous command-line syntax.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done :) Example output below: |
||
| .UseDefaults() | ||
| .Build(); | ||
|
|
||
| ParseResult parseResult = parser.Parse(args); | ||
| string parsedCommandName = parseResult.CommandResult.Command.Name; | ||
| if (parsedCommandName == "monitor" || parsedCommandName == "collect") | ||
| { | ||
| IReadOnlyCollection<string> unparsedTokens = parseResult.UnparsedTokens; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. System.CommandLine parses the "--" token but not the content that follows it?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It does, but we had an issue where it ate things like spaces in the arguments and parsed it as separate args, so now I'm using this as a way to check if the command has unparsed arguments, and parse the args manually in ProcessLauncher.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Have you talked with @jonsequitur at all about the "--" handling? I am fine if we need workarounds but IMO "--" is a common enough convention that I would hope System.CommandLine gains some support for it in the future.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I haven't yet. I'll post an issue over in the command-line-api repo.
sywhang marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| // If we notice there are unparsed tokens, user might want to attach on startup. | ||
| if (unparsedTokens.Count > 0) | ||
| { | ||
| ProcessLauncher.Launcher.PrepareChildProcess(unparsedTokens.ToList()); | ||
| } | ||
| } | ||
|
|
||
| return parser.InvokeAsync(args); | ||
| } | ||
| } | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.