From 958507f3d8ce41c7c6f37006747771a3edc80e8d Mon Sep 17 00:00:00 2001 From: Chet Husk Date: Sat, 12 Jul 2025 09:33:25 -0500 Subject: [PATCH 01/22] Minimal work to make System.Diagnostics.Activity usable by the dotnet CLI application and codebase --- .../Microsoft.DotNet.Cli.Utils/Microsoft.DotNet.Cli.Utils.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/Microsoft.DotNet.Cli.Utils.csproj b/src/Cli/Microsoft.DotNet.Cli.Utils/Microsoft.DotNet.Cli.Utils.csproj index b64200efdad3..211e5532250f 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/Microsoft.DotNet.Cli.Utils.csproj +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/Microsoft.DotNet.Cli.Utils.csproj @@ -54,6 +54,7 @@ + From e32eb6427f6b59bf253efd0aeeb075a89bad31a9 Mon Sep 17 00:00:00 2001 From: Chet Husk Date: Thu, 12 Jun 2025 13:26:17 -0500 Subject: [PATCH 02/22] WIP: rip and tear --- .../InternalReportInstallSuccessCommand.cs | 2 +- .../dotnet/Commands/MSBuild/MSBuildLogger.cs | 8 - src/Cli/dotnet/CommonOptionsFactory.cs | 24 ++ .../Extensions/ParseResultExtensions.cs | 16 +- src/Cli/dotnet/Parser.cs | 33 ++- src/Cli/dotnet/Program.cs | 266 +++++++----------- src/Cli/dotnet/Telemetry/Telemetry.cs | 3 +- .../MSBuild/DotnetMsbuildInProcTests.cs | 2 +- ...netFirstTimeUseConfigurerWIthStateSetup.cs | 3 +- test/dotnet.Tests/TelemetryCommandTest.cs | 8 +- 10 files changed, 174 insertions(+), 191 deletions(-) diff --git a/src/Cli/dotnet/Commands/Hidden/InternalReportInstallSuccess/InternalReportInstallSuccessCommand.cs b/src/Cli/dotnet/Commands/Hidden/InternalReportInstallSuccess/InternalReportInstallSuccessCommand.cs index 744289023948..a6d461872596 100644 --- a/src/Cli/dotnet/Commands/Hidden/InternalReportInstallSuccess/InternalReportInstallSuccessCommand.cs +++ b/src/Cli/dotnet/Commands/Hidden/InternalReportInstallSuccess/InternalReportInstallSuccessCommand.cs @@ -48,7 +48,7 @@ internal ThreadBlockingTelemetry() { var sessionId = Environment.GetEnvironmentVariable(TelemetrySessionIdEnvironmentVariableName); - telemetry = new Telemetry.Telemetry(new NoOpFirstTimeUseNoticeSentinel(), sessionId, blockThreadInitialization: true); + telemetry = new Telemetry.Telemetry(sessionId, blockThreadInitialization: true); } public bool Enabled => telemetry.Enabled; diff --git a/src/Cli/dotnet/Commands/MSBuild/MSBuildLogger.cs b/src/Cli/dotnet/Commands/MSBuild/MSBuildLogger.cs index 2656f9798a9e..03bb6957609b 100644 --- a/src/Cli/dotnet/Commands/MSBuild/MSBuildLogger.cs +++ b/src/Cli/dotnet/Commands/MSBuild/MSBuildLogger.cs @@ -298,14 +298,6 @@ private void OnTelemetryLogged(object sender, TelemetryEventArgs args) public void Shutdown() { - try - { - _sentinel?.Dispose(); - } - catch (Exception) - { - // Exceptions during telemetry shouldn't cause anything else to fail - } } public LoggerVerbosity Verbosity { get; set; } diff --git a/src/Cli/dotnet/CommonOptionsFactory.cs b/src/Cli/dotnet/CommonOptionsFactory.cs index f776cd47712a..848255d4b72d 100644 --- a/src/Cli/dotnet/CommonOptionsFactory.cs +++ b/src/Cli/dotnet/CommonOptionsFactory.cs @@ -4,6 +4,9 @@ #nullable disable using System.CommandLine; +using System.CommandLine.Parsing; +using Microsoft.DotNet.Cli.Extensions; +using Microsoft.DotNet.Cli.Utils; namespace Microsoft.DotNet.Cli; @@ -21,4 +24,25 @@ internal static class CommonOptionsFactory Recursive = recursive, Arity = ArgumentArity.Zero }; + + internal class SetDiagnosticModeAction(Option diagnosticOption) : System.CommandLine.Invocation.SynchronousCommandLineAction + { + public override int Invoke(ParseResult parseResult) + { + if (parseResult.IsDotnetBuiltInCommand()) + { + var diagIsChildOfRoot = parseResult.RootCommandResult.Children.FirstOrDefault((s) => s is OptionResult opt && opt.Option == diagnosticOption) is not null; + + // We found --diagnostic or -d, but we still need to determine whether the option should + // be attached to the dotnet command or the subcommand. + if (diagIsChildOfRoot) + { + Environment.SetEnvironmentVariable(CommandLoggingContext.Variables.Verbose, bool.TrueString); + CommandLoggingContext.SetVerbose(true); + Reporter.Reset(); + } + } + return 0; + } + } } diff --git a/src/Cli/dotnet/Extensions/ParseResultExtensions.cs b/src/Cli/dotnet/Extensions/ParseResultExtensions.cs index 6bcf5f84ff8c..2895d9a3feea 100644 --- a/src/Cli/dotnet/Extensions/ParseResultExtensions.cs +++ b/src/Cli/dotnet/Extensions/ParseResultExtensions.cs @@ -85,9 +85,12 @@ static bool ErrorContainsAllParts(ReadOnlySpan error, string[] parts) public static string RootSubCommandResult(this ParseResult parseResult) { - return parseResult.RootCommandResult.Children? - .Select(child => GetSymbolResultValue(parseResult, child)) - .FirstOrDefault(subcommand => !string.IsNullOrEmpty(subcommand)) ?? string.Empty; + CommandResult commandResult = parseResult.CommandResult; + while (commandResult != parseResult.RootCommandResult && commandResult.Parent is CommandResult parentCommand) + { + commandResult = parentCommand; + } + return commandResult.Command.Name; } public static bool IsDotnetBuiltInCommand(this ParseResult parseResult) @@ -101,12 +104,7 @@ public static bool IsTopLevelDotnetCommand(this ParseResult parseResult) return parseResult.CommandResult.Command.Equals(Parser.RootCommand) && string.IsNullOrEmpty(parseResult.RootSubCommandResult()); } - public static bool CanBeInvoked(this ParseResult parseResult) - { - return Parser.GetBuiltInCommand(parseResult.RootSubCommandResult()) != null || - parseResult.Tokens.Any(token => token.Type == TokenType.Directive) || - (parseResult.IsTopLevelDotnetCommand() && string.IsNullOrEmpty(parseResult.GetValue(Parser.DotnetSubCommand))); - } + public static bool CanBeInvoked(this ParseResult parseResult) => parseResult.Action is not null; public static int HandleMissingCommand(this ParseResult parseResult) { diff --git a/src/Cli/dotnet/Parser.cs b/src/Cli/dotnet/Parser.cs index 23dd3d6ebef3..580587aa8a32 100644 --- a/src/Cli/dotnet/Parser.cs +++ b/src/Cli/dotnet/Parser.cs @@ -96,14 +96,43 @@ public static class Parser public static readonly Option VersionOption = new("--version") { - Arity = ArgumentArity.Zero + Arity = ArgumentArity.Zero, + Action = new PrintVersionAction() }; + internal class PrintVersionAction : System.CommandLine.Invocation.SynchronousCommandLineAction + { + public PrintVersionAction() + { + Terminating = true; + } + public override int Invoke(ParseResult parseResult) + { + CommandLineInfo.PrintVersion(); + return 0; + } + } + public static readonly Option InfoOption = new("--info") { - Arity = ArgumentArity.Zero + Arity = ArgumentArity.Zero, + Action = new PrintInfoAction() }; + internal class PrintInfoAction : System.CommandLine.Invocation.SynchronousCommandLineAction + { + public PrintInfoAction() + { + Terminating = true; + } + + public override int Invoke(ParseResult parseResult) + { + CommandLineInfo.PrintInfo(); + return 0; + } + } + public static readonly Option ListSdksOption = new("--list-sdks") { Arity = ArgumentArity.Zero diff --git a/src/Cli/dotnet/Program.cs b/src/Cli/dotnet/Program.cs index 71feefd8e7f9..7578b4c884e5 100644 --- a/src/Cli/dotnet/Program.cs +++ b/src/Cli/dotnet/Program.cs @@ -24,6 +24,11 @@ namespace Microsoft.DotNet.Cli; +public static class Activities +{ + public static ActivitySource s_source = new("dotnet-cli", Product.Version); +} + public class Program { private static readonly string ToolPathSentinelFileName = $"{Product.Version}.toolpath.sentinel"; @@ -34,8 +39,13 @@ public static int Main(string[] args) // Register a handler for SIGTERM to allow graceful shutdown of the application on Unix. // See https://github.com/dotnet/docs/issues/46226. using var termSignalRegistration = PosixSignalRegistration.Create(PosixSignal.SIGTERM, _ => Environment.Exit(0)); - - using AutomaticEncodingRestorer _ = new(); + + // capture the time to we can compute muxer/host startup overhead + DateTime mainTimeStamp = DateTime.Now; + using var _mainActivity = Activities.s_source.StartActivity("main"); + _mainActivity.AddTag("process.pid", Process.GetCurrentProcess().Id); + _mainActivity.AddTag("process.executable.name", "dotnet"); + using AutomaticEncodingRestorer _encodingRestorer = new(); // Setting output encoding is not available on those platforms if (UILanguageOverride.OperatingSystemSupportsUtf8()) @@ -45,46 +55,20 @@ public static int Main(string[] args) DebugHelper.HandleDebugSwitch(ref args); - // Capture the current timestamp to calculate the host overhead. - DateTime mainTimeStamp = DateTime.Now; - TimeSpan startupTime = mainTimeStamp - Process.GetCurrentProcess().StartTime; + TrackHostStartup(mainTimeStamp); - bool perfLogEnabled = Env.GetEnvironmentVariableAsBool("DOTNET_CLI_PERF_LOG", false); + SetupMSBuildEnvironmentInvariants(); - if (string.IsNullOrEmpty(Env.GetEnvironmentVariable("MSBUILDFAILONDRIVEENUMERATINGWILDCARD"))) - { - Environment.SetEnvironmentVariable("MSBUILDFAILONDRIVEENUMERATINGWILDCARD", "1"); - } - - // Avoid create temp directory with root permission and later prevent access in non sudo - if (SudoEnvironmentDirectoryOverride.IsRunningUnderSudo()) - { - perfLogEnabled = false; - } - - PerformanceLogStartupInformation startupInfo = null; - if (perfLogEnabled) - { - startupInfo = new PerformanceLogStartupInformation(mainTimeStamp); - PerformanceLogManager.InitializeAndStartCleanup(FileSystemWrapper.Default); - } - - PerformanceLogEventListener perLogEventListener = null; try { - if (perfLogEnabled) - { - perLogEventListener = PerformanceLogEventListener.Create(FileSystemWrapper.Default, PerformanceLogManager.Instance.CurrentLogDirectory); - } - - PerformanceLogEventSource.Log.LogStartUpInformation(startupInfo); - PerformanceLogEventSource.Log.CLIStart(); - InitializeProcess(); try { - return ProcessArgs(args, startupTime); + var exitCode = ProcessArgs(args); + _mainActivity.AddTag("process.exit.code", exitCode); + _mainActivity.SetStatus(ActivityStatusCode.Ok); + return exitCode; } catch (Exception e) when (e.ShouldBeDisplayedAsError()) { @@ -97,7 +81,8 @@ public static int Main(string[] args) { commandParsingException.ParseResult.ShowHelp(); } - + _mainActivity.AddTag("process.exit.code", exitCode); + _mainActivity.SetStatus(ActivityStatusCode.Error); return 1; } catch (Exception e) when (!e.ShouldBeDisplayedAsError()) @@ -105,7 +90,8 @@ public static int Main(string[] args) // If telemetry object has not been initialized yet. It cannot be collected TelemetryEventEntry.SendFiltered(e); Reporter.Error.WriteLine(e.ToString().Red().Bold()); - + _mainActivity.AddTag("process.exit.code", exitCode); + _mainActivity.SetStatus(ActivityStatusCode.Error); return 1; } finally @@ -115,25 +101,41 @@ public static int Main(string[] args) } finally { - if (perLogEventListener != null) + Activities.s_source.Dispose(); + } + } + + private static void TrackHostStartup(DateTime mainTimeStamp) + { + using var hostStartupActivity = Activities.s_source.CreateActivity("host-startup", ActivityKind.Server); + hostStartupActivity?.SetStartTime(Process.GetCurrentProcess().StartTime); + if (TelemetryClient.Enabled) + { + // Get the global.json state to report in telemetry along with this command invocation. + if (NativeWrapper.NETCoreSdkResolverNativeWrapper.GetGlobalJsonState(Environment.CurrentDirectory) is string globalJsonState) { - perLogEventListener.Dispose(); + hostStartupActivity?.AddTag("dotnet.globalJson", globalJsonState); } } + hostStartupActivity?.SetEndTime(mainTimeStamp); } - internal static int ProcessArgs(string[] args) + /// + /// We have some behaviors in MSBuild that we want to enforce (either when using MSBuild API or by shelling out to it), + /// so we set those ASAP as globally as possible. + /// + private static void SetupMSBuildEnvironmentInvariants() { - return ProcessArgs(args, new TimeSpan(0)); + if (string.IsNullOrEmpty(Env.GetEnvironmentVariable("MSBUILDFAILONDRIVEENUMERATINGWILDCARD"))) + { + Environment.SetEnvironmentVariable("MSBUILDFAILONDRIVEENUMERATINGWILDCARD", "1"); + } } - internal static int ProcessArgs(string[] args, TimeSpan startupTime) + internal static int ProcessArgs(string[] args) { - Dictionary performanceData = []; - - PerformanceLogEventSource.Log.BuiltInCommandParserStart(); ParseResult parseResult; - using (new PerformanceMeasurement(performanceData, "Parse Time")) + using (var _parseActivity = Activities.s_source.StartActivity("parse")) { parseResult = Parser.Parse(args); @@ -142,110 +144,70 @@ internal static int ProcessArgs(string[] args, TimeSpan startupTime) // https://github.com/dotnet/sdk/issues/20195 SudoEnvironmentDirectoryOverride.OverrideEnvironmentVariableToTmp(parseResult); } - PerformanceLogEventSource.Log.BuiltInCommandParserStop(); - using (IFirstTimeUseNoticeSentinel disposableFirstTimeUseNoticeSentinel = new FirstTimeUseNoticeSentinel()) + using (var _firstTimeUseActivity = Activities.s_source.StartActivity("first-time-use")) { - IFirstTimeUseNoticeSentinel firstTimeUseNoticeSentinel = disposableFirstTimeUseNoticeSentinel; - IAspNetCertificateSentinel aspNetCertificateSentinel = new AspNetCertificateSentinel(); - IFileSentinel toolPathSentinel = new FileSentinel(new FilePath(Path.Combine(CliFolderPathCalculator.DotnetUserProfileFolderPath, ToolPathSentinelFileName))); - - PerformanceLogEventSource.Log.TelemetryRegistrationStart(); - - TelemetryClient ??= new Telemetry.Telemetry(firstTimeUseNoticeSentinel); - TelemetryEventEntry.Subscribe(TelemetryClient.TrackEvent); - TelemetryEventEntry.TelemetryFilter = new TelemetryFilter(Sha256Hasher.HashWithNormalizedCasing); - - PerformanceLogEventSource.Log.TelemetryRegistrationStop(); + IFirstTimeUseNoticeSentinel firstTimeUseNoticeSentinel = new FirstTimeUseNoticeSentinel(); - if (parseResult.GetValue(Parser.DiagOption) && parseResult.IsDotnetBuiltInCommand()) - { - // We found --diagnostic or -d, but we still need to determine whether the option should - // be attached to the dotnet command or the subcommand. - if (args.DiagOptionPrecedesSubcommand(parseResult.RootSubCommandResult())) - { - Environment.SetEnvironmentVariable(CommandLoggingContext.Variables.Verbose, bool.TrueString); - CommandLoggingContext.SetVerbose(true); - Reporter.Reset(); - } - } - if (parseResult.HasOption(Parser.VersionOption) && parseResult.IsTopLevelDotnetCommand()) - { - CommandLineInfo.PrintVersion(); - return 0; - } - else if (parseResult.HasOption(Parser.InfoOption) && parseResult.IsTopLevelDotnetCommand()) + IAspNetCertificateSentinel aspNetCertificateSentinel = new AspNetCertificateSentinel(); + IFileSentinel toolPathSentinel = new FileSentinel( + new FilePath( + Path.Combine( + CliFolderPathCalculator.DotnetUserProfileFolderPath, + ToolPathSentinelFileName))); + + var environmentProvider = new EnvironmentProvider(); + + bool generateAspNetCertificate = environmentProvider.GetEnvironmentVariableAsBool(EnvironmentVariableNames.DOTNET_GENERATE_ASPNET_CERTIFICATE, defaultValue: true); + bool telemetryOptout = environmentProvider.GetEnvironmentVariableAsBool(EnvironmentVariableNames.TELEMETRY_OPTOUT, defaultValue: CompileOptions.TelemetryOptOutDefault); + bool addGlobalToolsToPath = environmentProvider.GetEnvironmentVariableAsBool(EnvironmentVariableNames.DOTNET_ADD_GLOBAL_TOOLS_TO_PATH, defaultValue: true); + bool nologo = environmentProvider.GetEnvironmentVariableAsBool(EnvironmentVariableNames.DOTNET_NOLOGO, defaultValue: false); + bool skipWorkloadIntegrityCheck = environmentProvider.GetEnvironmentVariableAsBool(EnvironmentVariableNames.DOTNET_SKIP_WORKLOAD_INTEGRITY_CHECK, + // Default the workload integrity check skip to true if the command is being ran in CI. Otherwise, false. + defaultValue: new CIEnvironmentDetectorForTelemetry().IsCIEnvironment()); + + ReportDotnetHomeUsage(environmentProvider); + + var isDotnetBeingInvokedFromNativeInstaller = false; + if (parseResult.CommandResult.Command.Name.Equals(Parser.InstallSuccessCommand.Name)) { - CommandLineInfo.PrintInfo(); - return 0; + aspNetCertificateSentinel = new NoOpAspNetCertificateSentinel(); + firstTimeUseNoticeSentinel = new NoOpFirstTimeUseNoticeSentinel(); + toolPathSentinel = new NoOpFileSentinel(exists: false); + isDotnetBeingInvokedFromNativeInstaller = true; } - else - { - PerformanceLogEventSource.Log.FirstTimeConfigurationStart(); - - var environmentProvider = new EnvironmentProvider(); - - bool generateAspNetCertificate = environmentProvider.GetEnvironmentVariableAsBool(EnvironmentVariableNames.DOTNET_GENERATE_ASPNET_CERTIFICATE, defaultValue: true); - bool telemetryOptout = environmentProvider.GetEnvironmentVariableAsBool(EnvironmentVariableNames.TELEMETRY_OPTOUT, defaultValue: CompileOptions.TelemetryOptOutDefault); - bool addGlobalToolsToPath = environmentProvider.GetEnvironmentVariableAsBool(EnvironmentVariableNames.DOTNET_ADD_GLOBAL_TOOLS_TO_PATH, defaultValue: true); - bool nologo = environmentProvider.GetEnvironmentVariableAsBool(EnvironmentVariableNames.DOTNET_NOLOGO, defaultValue: false); - bool skipWorkloadIntegrityCheck = environmentProvider.GetEnvironmentVariableAsBool(EnvironmentVariableNames.DOTNET_SKIP_WORKLOAD_INTEGRITY_CHECK, - // Default the workload integrity check skip to true if the command is being ran in CI. Otherwise, false. - defaultValue: new CIEnvironmentDetectorForTelemetry().IsCIEnvironment()); - - ReportDotnetHomeUsage(environmentProvider); - - var isDotnetBeingInvokedFromNativeInstaller = false; - if (parseResult.CommandResult.Command.Name.Equals(Parser.InstallSuccessCommand.Name)) - { - aspNetCertificateSentinel = new NoOpAspNetCertificateSentinel(); - firstTimeUseNoticeSentinel = new NoOpFirstTimeUseNoticeSentinel(); - toolPathSentinel = new NoOpFileSentinel(exists: false); - isDotnetBeingInvokedFromNativeInstaller = true; - } - var dotnetFirstRunConfiguration = new DotnetFirstRunConfiguration( - generateAspNetCertificate: generateAspNetCertificate, - telemetryOptout: telemetryOptout, - addGlobalToolsToPath: addGlobalToolsToPath, - nologo: nologo, - skipWorkloadIntegrityCheck: skipWorkloadIntegrityCheck); - - string[] getStarOperators = ["getProperty", "getItem", "getTargetResult"]; - char[] switchIndicators = ['-', '/']; - var getStarOptionPassed = parseResult.CommandResult.Tokens.Any(t => - getStarOperators.Any(o => - switchIndicators.Any(i => t.Value.StartsWith(i + o, StringComparison.OrdinalIgnoreCase)))); - - ConfigureDotNetForFirstTimeUse( - firstTimeUseNoticeSentinel, - aspNetCertificateSentinel, - toolPathSentinel, - isDotnetBeingInvokedFromNativeInstaller, - dotnetFirstRunConfiguration, - environmentProvider, - performanceData, - skipFirstTimeUseCheck: getStarOptionPassed); - PerformanceLogEventSource.Log.FirstTimeConfigurationStop(); - } + var dotnetFirstRunConfiguration = new DotnetFirstRunConfiguration( + generateAspNetCertificate: generateAspNetCertificate, + telemetryOptout: telemetryOptout, + addGlobalToolsToPath: addGlobalToolsToPath, + nologo: nologo, + skipWorkloadIntegrityCheck: skipWorkloadIntegrityCheck); + + string[] getStarOperators = ["getProperty", "getItem", "getTargetResult"]; + char[] switchIndicators = ['-', '/']; + var getStarOptionPassed = parseResult.CommandResult.Tokens.Any(t => + getStarOperators.Any(o => + switchIndicators.Any(i => t.Value.StartsWith(i + o, StringComparison.OrdinalIgnoreCase)))); + + ConfigureDotNetForFirstTimeUse( + firstTimeUseNoticeSentinel, + aspNetCertificateSentinel, + toolPathSentinel, + isDotnetBeingInvokedFromNativeInstaller, + dotnetFirstRunConfiguration, + environmentProvider, + skipFirstTimeUseCheck: getStarOptionPassed); } + var telemetryClient = new Telemetry.Telemetry(); + TelemetryEventEntry.Subscribe(telemetryClient.TrackEvent); + TelemetryEventEntry.TelemetryFilter = new TelemetryFilter(Sha256Hasher.HashWithNormalizedCasing); + if (CommandLoggingContext.IsVerbose) { Console.WriteLine($"Telemetry is: {(TelemetryClient.Enabled ? "Enabled" : "Disabled")}"); } - PerformanceLogEventSource.Log.TelemetrySaveIfEnabledStart(); - performanceData.Add("Startup Time", startupTime.TotalMilliseconds); - - string globalJsonState = string.Empty; - if (TelemetryClient.Enabled) - { - // Get the global.json state to report in telemetry along with this command invocation. - globalJsonState = NativeWrapper.NETCoreSdkResolverNativeWrapper.GetGlobalJsonState(Environment.CurrentDirectory); - } - - TelemetryEventEntry.SendFiltered(Tuple.Create(parseResult, performanceData, globalJsonState)); - PerformanceLogEventSource.Log.TelemetrySaveIfEnabledStop(); int exitCode; if (parseResult.CanBeInvoked()) @@ -254,15 +216,16 @@ internal static int ProcessArgs(string[] args, TimeSpan startupTime) } else { - PerformanceLogEventSource.Log.ExtensibleCommandResolverStart(); try { + var _lookupExternalCommandActivity = Activities.s_source.StartActivity("lookup-external-command"); string commandName = "dotnet-" + parseResult.GetValue(Parser.DotnetSubCommand); var resolvedCommandSpec = CommandResolver.TryResolveCommandSpec( new DefaultCommandResolverPolicy(), commandName, args.GetSubArguments(), FrameworkConstants.CommonFrameworks.NetStandardApp15); + _lookupExternalCommandActivity?.Dispose(); if (resolvedCommandSpec is null && TryRunFileBasedApp(parseResult) is { } fileBasedAppExitCode) { @@ -270,13 +233,10 @@ internal static int ProcessArgs(string[] args, TimeSpan startupTime) } else { + var _executionActivity = Activities.s_source.StartActivity("execute-extensible-command"); var resolvedCommand = CommandFactoryUsingResolver.CreateOrThrow(commandName, resolvedCommandSpec); - PerformanceLogEventSource.Log.ExtensibleCommandResolverStop(); - - PerformanceLogEventSource.Log.ExtensibleCommandStart(); var result = resolvedCommand.Execute(); - PerformanceLogEventSource.Log.ExtensibleCommandStop(); - + _executionActivity?.Dispose(); exitCode = result.ExitCode; } } @@ -288,18 +248,6 @@ internal static int ProcessArgs(string[] args, TimeSpan startupTime) } } - TelemetryClient.TrackEvent("command/finish", properties: new Dictionary - { - { "exitCode", exitCode.ToString() } - }, - measurements: new Dictionary()); - - PerformanceLogEventSource.Log.TelemetryClientFlushStart(); - TelemetryClient.Flush(); - PerformanceLogEventSource.Log.TelemetryClientFlushStop(); - - TelemetryClient.Dispose(); - return exitCode; static int? TryRunFileBasedApp(ParseResult parseResult) @@ -330,9 +278,7 @@ internal static int ProcessArgs(string[] args, TimeSpan startupTime) static void InvokeBuiltInCommand(ParseResult parseResult, out int exitCode) { Debug.Assert(parseResult.CanBeInvoked()); - - PerformanceLogEventSource.Log.BuiltInCommandStart(); - + using var _invocationActivity = Activities.s_source.StartActivity("invocation"); try { exitCode = Parser.Invoke(parseResult); @@ -342,8 +288,6 @@ static void InvokeBuiltInCommand(ParseResult parseResult, out int exitCode) { exitCode = Parser.ExceptionHandler(exception, parseResult); } - - PerformanceLogEventSource.Log.BuiltInCommandStop(); } } @@ -391,7 +335,6 @@ private static void ConfigureDotNetForFirstTimeUse( bool isDotnetBeingInvokedFromNativeInstaller, DotnetFirstRunConfiguration dotnetFirstRunConfiguration, IEnvironmentProvider environmentProvider, - Dictionary performanceMeasurements, bool skipFirstTimeUseCheck) { var isFirstTimeUse = !firstTimeUseNoticeSentinel.Exists() && !skipFirstTimeUseCheck; @@ -407,7 +350,6 @@ private static void ConfigureDotNetForFirstTimeUse( dotnetFirstRunConfiguration, reporter, environmentPath, - performanceMeasurements, skipFirstTimeUseCheck: skipFirstTimeUseCheck); dotnetConfigurer.Configure(); diff --git a/src/Cli/dotnet/Telemetry/Telemetry.cs b/src/Cli/dotnet/Telemetry/Telemetry.cs index d9c3a59bd8a1..e3b8421e0302 100644 --- a/src/Cli/dotnet/Telemetry/Telemetry.cs +++ b/src/Cli/dotnet/Telemetry/Telemetry.cs @@ -44,8 +44,7 @@ public Telemetry( environmentProvider ??= new EnvironmentProvider(); - Enabled = !environmentProvider.GetEnvironmentVariableAsBool(EnvironmentVariableNames.TELEMETRY_OPTOUT, defaultValue: CompileOptions.TelemetryOptOutDefault) - && PermissionExists(sentinel); + Enabled = !environmentProvider.GetEnvironmentVariableAsBool(EnvironmentVariableNames.TELEMETRY_OPTOUT, defaultValue: CompileOptions.TelemetryOptOutDefault); if (!Enabled) { diff --git a/test/dotnet.Tests/CommandTests/MSBuild/DotnetMsbuildInProcTests.cs b/test/dotnet.Tests/CommandTests/MSBuild/DotnetMsbuildInProcTests.cs index a2a26316e3d8..1ea353b3443b 100644 --- a/test/dotnet.Tests/CommandTests/MSBuild/DotnetMsbuildInProcTests.cs +++ b/test/dotnet.Tests/CommandTests/MSBuild/DotnetMsbuildInProcTests.cs @@ -54,7 +54,7 @@ private string[] GetArgsForMSBuild(Func sentinelExists, out Telemetry.Tele { Telemetry.Telemetry.DisableForTests(); // reset static session id modified by telemetry constructor - telemetry = new Telemetry.Telemetry(new MockFirstTimeUseNoticeSentinel(sentinelExists)); + telemetry = new Telemetry.Telemetry(); MSBuildForwardingApp msBuildForwardingApp = new(Enumerable.Empty()); diff --git a/test/dotnet.Tests/ConfigurerTests/GivenADotnetFirstTimeUseConfigurerWIthStateSetup.cs b/test/dotnet.Tests/ConfigurerTests/GivenADotnetFirstTimeUseConfigurerWIthStateSetup.cs index d694240d865b..6d3075efaa01 100644 --- a/test/dotnet.Tests/ConfigurerTests/GivenADotnetFirstTimeUseConfigurerWIthStateSetup.cs +++ b/test/dotnet.Tests/ConfigurerTests/GivenADotnetFirstTimeUseConfigurerWIthStateSetup.cs @@ -252,8 +252,7 @@ private Telemetry RunConfigUsingMocks(bool isInstallerRun) configurer.Configure(); - return new Telemetry(firstTimeUseNoticeSentinel, - "test", + return new Telemetry("test", environmentProvider: _environmentProviderObject, senderCount: 0); } diff --git a/test/dotnet.Tests/TelemetryCommandTest.cs b/test/dotnet.Tests/TelemetryCommandTest.cs index 8d31a917db13..3e7a27e090e8 100644 --- a/test/dotnet.Tests/TelemetryCommandTest.cs +++ b/test/dotnet.Tests/TelemetryCommandTest.cs @@ -56,7 +56,7 @@ public void TopLevelCommandNameShouldBeSentToTelemetry() public void TopLevelCommandNameShouldBeSentToTelemetryWithPerformanceData() { string[] args = { "help" }; - Cli.Program.ProcessArgs(args, new TimeSpan(12345)); + Cli.Program.ProcessArgs(args); _fakeTelemetry.LogEntries.Should().Contain(e => e.EventName == "toplevelparser/command" && e.Properties.ContainsKey("verb") && @@ -85,7 +85,7 @@ public void TopLevelCommandNameShouldBeSentToTelemetryWithoutStartupTime() public void TopLevelCommandNameShouldBeSentToTelemetryZeroStartupTime() { string[] args = { "help" }; - Cli.Program.ProcessArgs(args, new TimeSpan(0)); + Cli.Program.ProcessArgs(args); _fakeTelemetry.LogEntries.Should().Contain(e => e.EventName == "toplevelparser/command" && e.Properties.ContainsKey("verb") && @@ -115,7 +115,7 @@ public void DotnetNewCommandFirstArgumentShouldBeSentToTelemetryWithPerformanceD { const string argumentToSend = "console"; string[] args = { "new", argumentToSend }; - Cli.Program.ProcessArgs(args, new TimeSpan(23456)); + Cli.Program.ProcessArgs(args); _fakeTelemetry .LogEntries.Should() .Contain(e => e.EventName == "sublevelparser/command" && @@ -272,7 +272,7 @@ public void AnyDotnetCommandVerbosityOpinionShouldBeSentToTelemetryWithPerforman const string optionKey = "verbosity"; const string optionValueToSend = "minimal"; string[] args = { "restore", "--" + optionKey, optionValueToSend }; - Cli.Program.ProcessArgs(args, new TimeSpan(34567)); + Cli.Program.ProcessArgs(args); _fakeTelemetry .LogEntries.Should() .Contain(e => e.EventName == "sublevelparser/command" && From 2276196fe6ed8554f2b1a6d1b57a14d8708b2b73 Mon Sep 17 00:00:00 2001 From: Chet Husk Date: Thu, 12 Jun 2025 17:09:05 -0500 Subject: [PATCH 03/22] WIP: tool install --- Directory.Packages.props | 6 +- src/Cli/dotnet/Activities.cs | 10 +++ .../ToolInstallGlobalOrToolPathCommand.cs | 1 + src/Cli/dotnet/Program.cs | 74 ++++++++++++++----- .../ToolPackage/ToolPackageDownloader.cs | 12 ++- src/Cli/dotnet/dotnet.csproj | 3 + 6 files changed, 82 insertions(+), 24 deletions(-) create mode 100644 src/Cli/dotnet/Activities.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index 568b021b857d..dfa098189be4 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -112,6 +112,9 @@ + + + @@ -146,7 +149,6 @@ -