diff --git a/src/coreclr/tools/Common/CommandLine/Argument.cs b/src/coreclr/tools/Common/CommandLine/Argument.cs deleted file mode 100644 index f3b5af2c3152c8..00000000000000 --- a/src/coreclr/tools/Common/CommandLine/Argument.cs +++ /dev/null @@ -1,109 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.Generic; -using System.Collections.ObjectModel; - -namespace Internal.CommandLine -{ - public abstract class Argument - { - internal Argument(ArgumentCommand command, IEnumerable names, bool isOption, bool isRequired) - { - var nameArray = names.ToArray(); - Command = command; - Name = nameArray.First(); - Names = new ReadOnlyCollection(nameArray); - IsOption = isOption; - IsRequired = isRequired; - } - - public ArgumentCommand Command { get; private set; } - - public string Name { get; private set; } - - public ReadOnlyCollection Names { get; private set; } - - public string Help { get; set; } - - public bool IsOption { get; private set; } - - public bool IsParameter - { - get { return !IsOption; } - } - - public bool IsSpecified { get; private set; } - - public bool IsHidden { get; set; } - - public bool IsRequired { get; private set; } - - public virtual bool IsList - { - get { return false; } - } - - public object Value - { - get { return GetValue(); } - } - - public object DefaultValue - { - get { return GetDefaultValue(); } - } - - public bool IsActive - { - get { return Command == null || Command.IsActive; } - } - - public abstract bool IsFlag { get; } - - internal abstract object GetValue(); - - internal abstract object GetDefaultValue(); - - internal void MarkSpecified() - { - IsSpecified = true; - } - - public string GetDisplayName() - { - return GetDisplayName(Name); - } - - public IEnumerable GetDisplayNames() - { - return Names.Select(GetDisplayName); - } - - private string GetDisplayName(string name) - { - return IsOption ? GetOptionDisplayName(name) : GetParameterDisplayName(name); - } - - private static string GetOptionDisplayName(string name) - { - var modifier = name.Length == 1 ? @"-" : @"--"; - return modifier + name; - } - - private static string GetParameterDisplayName(string name) - { - return @"<" + name + @">"; - } - - public virtual string GetDisplayValue() - { - return Value == null ? string.Empty : Value.ToString(); - } - - public override string ToString() - { - return GetDisplayName(); - } - } -} diff --git a/src/coreclr/tools/Common/CommandLine/ArgumentCommand.cs b/src/coreclr/tools/Common/CommandLine/ArgumentCommand.cs deleted file mode 100644 index fe2f9c1bc9f219..00000000000000 --- a/src/coreclr/tools/Common/CommandLine/ArgumentCommand.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Internal.CommandLine -{ - public abstract class ArgumentCommand - { - internal ArgumentCommand(string name) - { - Name = name; - } - - public string Name { get; private set; } - - public string Help { get; set; } - - public object Value - { - get { return GetValue(); } - } - - public bool IsHidden { get; set; } - - public bool IsActive { get; private set; } - - internal abstract object GetValue(); - - internal void MarkActive() - { - IsActive = true; - } - - public override string ToString() - { - return Name; - } - } -} diff --git a/src/coreclr/tools/Common/CommandLine/ArgumentCommand_1.cs b/src/coreclr/tools/Common/CommandLine/ArgumentCommand_1.cs deleted file mode 100644 index d3dae3ecaa9c9a..00000000000000 --- a/src/coreclr/tools/Common/CommandLine/ArgumentCommand_1.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Internal.CommandLine -{ - public sealed class ArgumentCommand : ArgumentCommand - { - internal ArgumentCommand(string name, T value) - : base(name) - { - Value = value; - } - - public new T Value { get; private set; } - - internal override object GetValue() - { - return Value; - } - } -} diff --git a/src/coreclr/tools/Common/CommandLine/ArgumentLexer.cs b/src/coreclr/tools/Common/CommandLine/ArgumentLexer.cs deleted file mode 100644 index 387f320758652a..00000000000000 --- a/src/coreclr/tools/Common/CommandLine/ArgumentLexer.cs +++ /dev/null @@ -1,200 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Collections.Generic; - -namespace Internal.CommandLine -{ - internal static class ArgumentLexer - { - public static IReadOnlyList Lex(IEnumerable args, Func> responseFileReader = null) - { - var result = new List(); - - // We'll split the arguments into tokens. - // - // A token combines the modifier (/, -, --), the option name, and the option - // value. - // - // Please note that this code doesn't combine arguments. It only provides - // some pre-processing over the arguments to split out the modifier, - // option, and value: - // - // { "--out", "out.exe" } ==> { new ArgumentToken("--", "out", null), - // new ArgumentToken(null, null, "out.exe") } - // - // {"--out:out.exe" } ==> { new ArgumentToken("--", "out", "out.exe") } - // - // The reason it doesn't combine arguments is because it depends on the actual - // definition. For example, if --out is a flag (meaning it's of type bool) then - // out.exe in the first example wouldn't be considered its value. - // - // The code also handles the special -- token which indicates that the following - // arguments shouldn't be considered options. - // - // Finally, this code will also expand any reponse file entries, assuming the caller - // gave us a non-null reader. - - var hasSeenDashDash = false; - - foreach (var arg in ExpandResponseFiles(args, responseFileReader)) - { - // If we've seen a -- already, then we'll treat one as a plain name, that is - // without a modifier or value. - - if (!hasSeenDashDash && arg == @"--") - { - hasSeenDashDash = true; - continue; - } - - string modifier; - string name; - string value; - - if (hasSeenDashDash) - { - modifier = null; - name = arg; - value = null; - } - else - { - // If we haven't seen the -- separator, we're looking for options. - // Options have leading modifiers, i.e. /, -, or --. - // - // Options can also have values, such as: - // - // -f:false - // --name=hello - - - if (!TryExtractOption(arg, out modifier, out string nameAndValue)) - { - name = arg; - value = null; - } - else if (!TrySplitNameValue(nameAndValue, out name, out value)) - { - name = nameAndValue; - value = null; - } - else - { - value = value.Trim('"'); - } - } - - var token = new ArgumentToken(modifier, name, value); - result.Add(token); - } - - // Single letter options can be combined, for example the following two - // forms are considered equivalent: - // - // (1) -xdf - // (2) -x -d -f - // - // In order to free later phases from handling this case, we simply expand - // single letter options to the second form. - - for (var i = result.Count - 1; i >= 0; i--) - { - if (IsOptionBundle(result[i])) - ExpandOptionBundle(result, i); - } - - return result.ToArray(); - } - - private static IEnumerable ExpandResponseFiles(IEnumerable args, Func> responseFileReader) - { - foreach (var arg in args) - { - if (responseFileReader == null || !arg.StartsWith(@"@")) - { - yield return arg; - } - else - { - var fileName = arg.Substring(1); - - var responseFileArguments = responseFileReader(fileName); - - // The reader can suppress expanding this response file by - // returning null. In that case, we'll treat the response - // file token as a regular argument. - - if (responseFileArguments == null) - { - yield return arg; - } - else - { - foreach (var responseFileArgument in responseFileArguments) - yield return responseFileArgument.Trim(); - } - } - } - } - - private static bool IsOptionBundle(ArgumentToken token) - { - return token.IsOption && - token.Modifier == @"-" && - token.Name.Length > 1; - } - - private static void ExpandOptionBundle(IList receiver, int index) - { - var options = receiver[index].Name; - receiver.RemoveAt(index); - - foreach (var c in options) - { - var name = char.ToString(c); - var expandedToken = new ArgumentToken(@"-", name, null); - receiver.Insert(index, expandedToken); - index++; - } - } - - private static bool TryExtractOption(string text, out string modifier, out string remainder) - { - return TryExtractOption(text, @"--", out modifier, out remainder) || - TryExtractOption(text, @"-", out modifier, out remainder); - } - - private static bool TryExtractOption(string text, string prefix, out string modifier, out string remainder) - { - if (text.StartsWith(prefix)) - { - remainder = text.Substring(prefix.Length); - modifier = prefix; - return true; - } - - remainder = null; - modifier = null; - return false; - } - - private static bool TrySplitNameValue(string text, out string name, out string value) - { - for (int idx = 0; idx < text.Length; idx++) - { - char ch = text[idx]; - if (ch == ':' || ch == '=') - { - name = text.Substring(0, idx); - value = text.Substring(idx + 1); - return true; - } - } - name = null; - value = null; - return false; - } - } -} diff --git a/src/coreclr/tools/Common/CommandLine/ArgumentList_1.cs b/src/coreclr/tools/Common/CommandLine/ArgumentList_1.cs deleted file mode 100644 index a0072a0abaa7ef..00000000000000 --- a/src/coreclr/tools/Common/CommandLine/ArgumentList_1.cs +++ /dev/null @@ -1,59 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.Generic; - -namespace Internal.CommandLine -{ - public sealed class ArgumentList : Argument - { - internal ArgumentList(ArgumentCommand command, IEnumerable names, IReadOnlyList defaultValue, bool isRequired) - : base(command, names, true, isRequired) - { - Value = defaultValue; - DefaultValue = defaultValue; - } - - internal ArgumentList(ArgumentCommand command, string name, IReadOnlyList defaultValue) - : base(command, new[] { name }, false, true) - { - Value = defaultValue; - DefaultValue = defaultValue; - } - - public override bool IsList - { - get { return true; } - } - - public new IReadOnlyList Value { get; private set; } - - public new IReadOnlyList DefaultValue { get; private set; } - - public override bool IsFlag - { - get { return typeof(T) == typeof(bool); } - } - - internal override object GetValue() - { - return Value; - } - - internal override object GetDefaultValue() - { - return DefaultValue; - } - - internal void SetValue(IReadOnlyList value) - { - Value = value; - MarkSpecified(); - } - - public override string GetDisplayValue() - { - return string.Join(@", ", Value); - } - } -} diff --git a/src/coreclr/tools/Common/CommandLine/ArgumentParser.cs b/src/coreclr/tools/Common/CommandLine/ArgumentParser.cs deleted file mode 100644 index b538a669b93e77..00000000000000 --- a/src/coreclr/tools/Common/CommandLine/ArgumentParser.cs +++ /dev/null @@ -1,265 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Collections.Generic; - -namespace Internal.CommandLine -{ - internal sealed class ArgumentParser - { - private readonly IReadOnlyList _tokens; - - public ArgumentParser(IEnumerable arguments) - : this(arguments, null) - { - } - - public ArgumentParser(IEnumerable arguments, Func> responseFileReader) - { - if (arguments == null) - throw new ArgumentNullException(nameof(arguments)); - - _tokens = ArgumentLexer.Lex(arguments, responseFileReader); - } - - public bool TryParseCommand(string name) - { - var token = _tokens.FirstOrDefault(); - - if (token == null || token.IsOption || token.IsSeparator) - return false; - - if (!string.Equals(token.Name, name, StringComparison.Ordinal)) - return false; - - token.MarkMatched(); - return true; - } - - public bool TryParseOption(string diagnosticName, IReadOnlyCollection names, Func valueConverter, bool isRequired, out T value, out bool specified) - { - if (!TryParseOptionList(diagnosticName, names, valueConverter, isRequired, out IReadOnlyList values, out specified)) - { - value = default; - return false; - } - - // Please note that we don't verify that the option is only specified once. - // It's tradition on Unix to allow single options to occur more than once, - // with 'last one wins' semantics. This simplifies scripting because you - // can easily combine arguments. - - value = values.Last(); - return true; - } - - public bool TryParseOptionList(string diagnosticName, IReadOnlyCollection names, Func valueConverter, bool isRequired, out IReadOnlyList values, out bool specified) - { - var result = new List(); - var tokenIndex = 0; - var isFlag = typeof(T) == typeof(bool); - specified = false; - - while (tokenIndex < _tokens.Count) - { - if (TryParseOption(ref tokenIndex, names)) - { - specified = true; - if (TryParseOptionArgument(ref tokenIndex, isFlag, out string valueText)) - { - var value = ParseValue(diagnosticName, valueConverter, valueText); - result.Add(value); - } - else if (isFlag) - { - var value = (T)(object)true; - result.Add(value); - } - else if (isRequired) - { - var message = string.Format(Strings.OptionRequiresValueFmt, diagnosticName); - throw new ArgumentSyntaxException(message); - } - } - - tokenIndex++; - } - - if (!result.Any()) - { - values = null; - return false; - } - - values = result.ToArray(); - return true; - } - - public bool TryParseParameter(string diagnosticName, Func valueConverter, out T value) - { - foreach (var token in _tokens) - { - if (token.IsMatched || token.IsOption || token.IsSeparator) - continue; - - token.MarkMatched(); - - var valueText = token.Name; - value = ParseValue(diagnosticName, valueConverter, valueText); - return true; - } - - value = default; - return false; - } - - public bool TryParseParameterList(string diagnosticName, Func valueConverter, out IReadOnlyList values) - { - var result = new List(); - - while (TryParseParameter(diagnosticName, valueConverter, out T value)) - { - result.Add(value); - } - - if (!result.Any()) - { - values = null; - return false; - } - - values = result.ToArray(); - return true; - } - - private bool TryParseOption(ref int tokenIndex, IReadOnlyCollection names) - { - while (tokenIndex < _tokens.Count) - { - var a = _tokens[tokenIndex]; - - if (a.IsOption) - { - if (names.Any(n => string.Equals(a.Name, n, StringComparison.Ordinal))) - { - a.MarkMatched(); - return true; - } - } - - tokenIndex++; - } - - return false; - } - - private bool TryParseOptionArgument(ref int tokenIndex, bool isFlag, out string argument) - { - argument = null; - - // Let's see whether the current token already has value, like "-o:value" - - var a = _tokens[tokenIndex]; - if (a.HasValue) - { - a.MarkMatched(); - argument = a.Value; - return true; - } - - // OK, we may need to have to advance one or two tokens. Since we don't know - // up front, we'll take a look ahead. - - - // So, do we have a token? - - if (!TryGetNextToken(tokenIndex, out ArgumentToken lookahead)) - return false; - - // If it's an option, then it's not an argument and we're done. - - if (lookahead.IsOption) - return false; - - // OK, the lookahead is either a separator or it's an argument. - - if (!lookahead.IsSeparator) - { - // If this is a flag, we need an explicit separator. - // Since there is none, we don't consume this token. - - if (isFlag) - return false; - - lookahead.MarkMatched(); - argument = lookahead.Name; - tokenIndex++; - return true; - } - - // Skip separator - - lookahead.MarkMatched(); - tokenIndex++; - - // See whether the next token is an argument. - - if (!TryGetNextToken(tokenIndex, out lookahead)) - return false; - - if (lookahead.IsOption || lookahead.IsSeparator) - return false; - - lookahead.MarkMatched(); - argument = lookahead.Name; - tokenIndex++; - return true; - } - - private bool TryGetNextToken(int tokenIndex, out ArgumentToken token) - { - if (++tokenIndex >= _tokens.Count) - { - token = null; - return false; - } - - token = _tokens[tokenIndex]; - return true; - } - - private static T ParseValue(string diagnosticName, Func valueConverter, string valueText) - { - try - { - return valueConverter(valueText); - } - catch (FormatException ex) - { - var message = string.Format(Strings.CannotParseValueFmt, valueText, diagnosticName, ex.Message); - throw new ArgumentSyntaxException(message); - } - } - - public string GetUnreadCommand() - { - return _tokens.Where(t => !t.IsOption && !t.IsSeparator).Select(t => t.Name).FirstOrDefault(); - } - - public IReadOnlyList GetUnreadOptionNames() - { - return _tokens.Where(t => !t.IsMatched && t.IsOption).Select(t => t.Modifier + t.Name).ToArray(); - } - - public IReadOnlyList GetUnreadParameters() - { - return _tokens.Where(t => !t.IsMatched && !t.IsOption).Select(t => t.ToString()).ToArray(); - } - - public IReadOnlyList GetUnreadArguments() - { - return _tokens.Where(t => !t.IsMatched).Select(t => t.ToString()).ToArray(); - } - } -} diff --git a/src/coreclr/tools/Common/CommandLine/ArgumentSyntax.cs b/src/coreclr/tools/Common/CommandLine/ArgumentSyntax.cs deleted file mode 100644 index 976f2a559b1c5b..00000000000000 --- a/src/coreclr/tools/Common/CommandLine/ArgumentSyntax.cs +++ /dev/null @@ -1,470 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Collections.Generic; -using System.IO; - -namespace Internal.CommandLine -{ - public sealed partial class ArgumentSyntax - { - private readonly IEnumerable _arguments; - private readonly List _commands = new List(); - private readonly List _options = new List(); - private readonly List _parameters = new List(); - - private readonly List _extraHelpParagraphs = new List(); - - private ArgumentParser _parser; - private ArgumentCommand _definedCommand; - private ArgumentCommand _activeCommand; - - internal ArgumentSyntax(IEnumerable arguments) - { - _arguments = arguments; - - ApplicationName = GetApplicationName(); - HandleErrors = true; - HandleHelp = true; - HandleResponseFiles = true; - ErrorOnUnexpectedArguments = true; - } - - public static ArgumentSyntax Parse(IEnumerable arguments, Action defineAction) - { - if (arguments == null) - throw new ArgumentNullException(nameof(arguments)); - - if (defineAction == null) - throw new ArgumentNullException(nameof(defineAction)); - - var syntax = new ArgumentSyntax(arguments); - defineAction(syntax); - syntax.Validate(); - return syntax; - } - - private void Validate() - { - // Check whether help is requested - - if (HandleHelp && IsHelpRequested()) - { - var helpText = GetHelpText(); - Console.Error.Write(helpText); - Environment.Exit(0); - } - - // Check for invalid or missing command - - if (_activeCommand == null && _commands.Any()) - { - var unreadCommand = Parser.GetUnreadCommand(); - var message = unreadCommand == null - ? Strings.MissingCommand - : string.Format(Strings.UnknownCommandFmt, unreadCommand); - ReportError(message); - } - - if (ErrorOnUnexpectedArguments) - { - // Check for invalid options and extra parameters - - foreach (var option in Parser.GetUnreadOptionNames()) - { - var message = string.Format(Strings.InvalidOptionFmt, option); - ReportError(message); - } - - foreach (var parameter in Parser.GetUnreadParameters()) - { - var message = string.Format(Strings.ExtraParameterFmt, parameter); - ReportError(message); - } - } - } - - private bool IsHelpRequested() - { - return Parser.GetUnreadOptionNames() - .Any(a => string.Equals(a, @"-?", StringComparison.Ordinal) || - string.Equals(a, @"-h", StringComparison.Ordinal) || - string.Equals(a, @"--help", StringComparison.Ordinal)); - } - - public void ReportError(string message) - { - if (HandleErrors) - { - Console.Error.WriteLine(Strings.ErrorWithMessageFmt, message); - Environment.Exit(1); - } - - throw new ArgumentSyntaxException(message); - } - - public ArgumentCommand DefineCommand(string name, T value) - { - if (string.IsNullOrEmpty(name)) - throw new ArgumentException(Strings.NameMissing, nameof(name)); - - if (!IsValidName(name)) - { - var message = string.Format(Strings.CommandNameIsNotValidFmt, name); - throw new ArgumentException(message, nameof(name)); - } - - if (_commands.Any(c => string.Equals(c.Name, name, StringComparison.Ordinal))) - { - var message = string.Format(Strings.CommandAlreadyDefinedFmt, name); - throw new InvalidOperationException(message); - } - - if (_options.Concat(_parameters).Any(c => c.Command == null)) - throw new InvalidOperationException(Strings.CannotDefineCommandsIfArgumentsExist); - - var definedCommand = new ArgumentCommand(name, value); - _commands.Add(definedCommand); - _definedCommand = definedCommand; - - if (_activeCommand != null) - return definedCommand; - - if (!Parser.TryParseCommand(name)) - return definedCommand; - - _activeCommand = _definedCommand; - _activeCommand.MarkActive(); - - return definedCommand; - } - - public Argument DefineOption(string name, T defaultValue, Func valueConverter, bool isRequired) - { - if (string.IsNullOrEmpty(name)) - throw new ArgumentException(Strings.NameMissing, nameof(name)); - - if (DefinedParameters.Any()) - throw new InvalidOperationException(Strings.OptionsMustBeDefinedBeforeParameters); - - var names = ParseOptionNameList(name); - var option = new Argument(_definedCommand, names, defaultValue, isRequired); - _options.Add(option); - - if (_activeCommand != _definedCommand) - return option; - - try - { - if (Parser.TryParseOption(option.GetDisplayName(), option.Names, valueConverter, isRequired, out T value, out bool specified)) - { - option.SetValue(value); - } - else if (specified) - { - // No value was provided, but the option was specified and a value wasn't required - option.MarkSpecified(); - } - } - catch (ArgumentSyntaxException ex) - { - ReportError(ex.Message); - } - - return option; - } - - public ArgumentList DefineOptionList(string name, IReadOnlyList defaultValue, Func valueConverter, bool isRequired) - { - if (string.IsNullOrEmpty(name)) - throw new ArgumentException(Strings.NameMissing, nameof(name)); - - if (DefinedParameters.Any()) - throw new InvalidOperationException(Strings.OptionsMustBeDefinedBeforeParameters); - - var names = ParseOptionNameList(name); - var optionList = new ArgumentList(_definedCommand, names, defaultValue, isRequired); - _options.Add(optionList); - - if (_activeCommand != _definedCommand) - return optionList; - - try - { - if (Parser.TryParseOptionList(optionList.GetDisplayName(), optionList.Names, valueConverter, isRequired, out IReadOnlyList value, out bool specified)) - { - optionList.SetValue(value); - } - else if (specified) - { - // No value was provided, but the option was specified and a value wasn't required - optionList.MarkSpecified(); - } - } - catch (ArgumentSyntaxException ex) - { - ReportError(ex.Message); - } - - return optionList; - } - - public Argument DefineParameter(string name, T defaultValue, Func valueConverter) - { - if (string.IsNullOrEmpty(name)) - throw new ArgumentException(Strings.NameMissing, nameof(name)); - - if (!IsValidName(name)) - { - var message = string.Format(Strings.ParameterNameIsNotValidFmt, name); - throw new ArgumentException(message, nameof(name)); - } - - if (DefinedParameters.Any(p => p.IsList)) - throw new InvalidOperationException(Strings.ParametersCannotBeDefinedAfterLists); - - if (DefinedParameters.Any(p => string.Equals(name, p.Name, StringComparison.OrdinalIgnoreCase))) - { - var message = string.Format(Strings.ParameterAlreadyDefinedFmt, name); - throw new InvalidOperationException(message); - } - - var parameter = new Argument(_definedCommand, name, defaultValue); - _parameters.Add(parameter); - - if (_activeCommand != _definedCommand) - return parameter; - - try - { - if (Parser.TryParseParameter(parameter.GetDisplayName(), valueConverter, out T value)) - parameter.SetValue(value); - } - catch (ArgumentSyntaxException ex) - { - ReportError(ex.Message); - } - - return parameter; - } - - public ArgumentList DefineParameterList(string name, IReadOnlyList defaultValue, Func valueConverter) - { - if (string.IsNullOrEmpty(name)) - throw new ArgumentException(Strings.NameMissing, nameof(name)); - - if (!IsValidName(name)) - { - var message = string.Format(Strings.ParameterNameIsNotValidFmt, name); - throw new ArgumentException(message, nameof(name)); - } - - if (DefinedParameters.Any(p => p.IsList)) - throw new InvalidOperationException(Strings.CannotDefineMultipleParameterLists); - - var parameterList = new ArgumentList(_definedCommand, name, defaultValue); - _parameters.Add(parameterList); - - if (_activeCommand != _definedCommand) - return parameterList; - - try - { - if (Parser.TryParseParameterList(parameterList.GetDisplayName(), valueConverter, out IReadOnlyList values)) - parameterList.SetValue(values); - } - catch (ArgumentSyntaxException ex) - { - ReportError(ex.Message); - } - - return parameterList; - } - - private static bool IsValidName(string name) - { - if (string.IsNullOrEmpty(name)) - return false; - - if (name[0] == '-') - return false; - - return name.All(c => char.IsLetterOrDigit(c) || - c == '-' || - c == '_'); - } - - private IEnumerable ParseOptionNameList(string name) - { - var names = name.Split('|').Select(n => n.Trim()).ToArray(); - - foreach (var alias in names) - { - if (!IsValidName(alias)) - { - var message = string.Format(Strings.OptionNameIsNotValidFmt, alias); - throw new ArgumentException(message, nameof(name)); - } - - foreach (var option in DefinedOptions) - { - if (option.Names.Any(n => string.Equals(n, alias, StringComparison.Ordinal))) - { - var message = string.Format(Strings.OptionAlreadyDefinedFmt, alias); - throw new InvalidOperationException(message); - } - } - } - - return names; - } - - private IEnumerable ParseResponseFile(string fileName) - { - if (!HandleResponseFiles) - return null; - - if (!File.Exists(fileName)) - { - var message = string.Format(Strings.ResponseFileDoesNotExistFmt, fileName); - ReportError(message); - } - - return File.ReadLines(fileName); - } - - private static string GetApplicationName() - { - var processPath = Environment.GetCommandLineArgs()[0]; - var processName = Path.GetFileNameWithoutExtension(processPath); - return processName; - } - - public string ApplicationName { get; set; } - - public bool HandleErrors { get; set; } - - public bool HandleHelp { get; set; } - - public bool HandleResponseFiles { get; set; } - - public bool ErrorOnUnexpectedArguments { get; set; } - - public IEnumerable RemainingArguments - => Parser.GetUnreadArguments(); - - private ArgumentParser Parser - { - get - { - _parser ??= new ArgumentParser(_arguments, ParseResponseFile); - - return _parser; - } - } - - private IEnumerable DefinedOptions - { - get { return _options.Where(o => o.Command == null || o.Command == _definedCommand); } - } - - private IEnumerable DefinedParameters - { - get { return _parameters.Where(p => p.Command == null || p.Command == _definedCommand); } - } - - public ArgumentCommand ActiveCommand - { - get { return _activeCommand; } - } - - public IReadOnlyList Commands - { - get { return _commands; } - } - - public IEnumerable GetArguments() - { - return _options.Concat(_parameters); - } - - public IEnumerable GetArguments(ArgumentCommand command) - { - return GetArguments().Where(c => c.Command == null || c.Command == command); - } - - public IEnumerable GetOptions() - { - return _options; - } - - public IEnumerable GetOptions(ArgumentCommand command) - { - return _options.Where(c => c.Command == null || c.Command == command); - } - - public IEnumerable GetParameters() - { - return _parameters; - } - - public IEnumerable GetParameters(ArgumentCommand command) - { - return _parameters.Where(c => c.Command == null || c.Command == command); - } - - public IEnumerable GetActiveArguments() - { - return GetArguments(ActiveCommand); - } - - public IEnumerable GetActiveOptions() - { - return GetOptions(ActiveCommand); - } - - public IEnumerable GetActiveParameters() - { - return GetParameters(ActiveCommand); - } - - private static int ConsoleWindowWidth() - { - // Console.WindowWidth will throw an exception if the output is redirected in some cases - // This try/catch routine is probably excessive, but it will definitely cover all the cases - try - { - if (!Console.IsOutputRedirected) - return Console.WindowWidth; - } - catch - { - } - return 100; - } - - public string GetHelpText() - { - return GetHelpText(ConsoleWindowWidth() - 2); - } - - public string GetHelpText(int maxWidth) - { - return HelpTextGenerator.Generate(this, maxWidth); - } - - public IReadOnlyList ExtraHelpParagraphs - { - get - { - return _extraHelpParagraphs.ToArray(); - } - set - { - _extraHelpParagraphs.Clear(); - _extraHelpParagraphs.AddRange(value); - } - } - } -} diff --git a/src/coreclr/tools/Common/CommandLine/ArgumentSyntaxException.cs b/src/coreclr/tools/Common/CommandLine/ArgumentSyntaxException.cs deleted file mode 100644 index ddc12b8a0a5810..00000000000000 --- a/src/coreclr/tools/Common/CommandLine/ArgumentSyntaxException.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; - -namespace Internal.CommandLine -{ - public sealed class ArgumentSyntaxException : Exception - { - public ArgumentSyntaxException() - { - } - - public ArgumentSyntaxException(string message) - : base(message) - { - } - - public ArgumentSyntaxException(string message, Exception innerException) - : base(message, innerException) - { - } - } -} diff --git a/src/coreclr/tools/Common/CommandLine/ArgumentSyntax_Definers.cs b/src/coreclr/tools/Common/CommandLine/ArgumentSyntax_Definers.cs deleted file mode 100644 index 83d9f675b42180..00000000000000 --- a/src/coreclr/tools/Common/CommandLine/ArgumentSyntax_Definers.cs +++ /dev/null @@ -1,253 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Collections.Generic; -using System.Globalization; - -namespace Internal.CommandLine -{ - public partial class ArgumentSyntax - { - private static readonly Func s_stringParser = v => v; - private static readonly Func s_booleanParser = bool.Parse; - private static readonly Func s_int32Parser = v => int.Parse(v, CultureInfo.InvariantCulture); - - // Commands - - public ArgumentCommand DefineCommand(string name) - { - return DefineCommand(name, name); - } - - public ArgumentCommand DefineCommand(string name, ref T command, T value, string help) - { - var result = DefineCommand(name, value); - result.Help = help; - - if (_activeCommand == result) - command = value; - - return result; - } - - public ArgumentCommand DefineCommand(string name, ref string value, string help) - { - return DefineCommand(name, ref value, name, help); - } - - // Options - - public Argument DefineOption(string name, string defaultValue) - { - return DefineOption(name, defaultValue, s_stringParser); - } - - public Argument DefineOption(string name, bool defaultValue) - { - return DefineOption(name, defaultValue, s_booleanParser); - } - - public Argument DefineOption(string name, int defaultValue) - { - return DefineOption(name, defaultValue, s_int32Parser); - } - - public Argument DefineOption(string name, string defaultValue, bool requireValue) - { - return DefineOption(name, defaultValue, s_stringParser, requireValue); - } - - public Argument DefineOption(string name, bool defaultValue, bool requireValue) - { - return DefineOption(name, defaultValue, s_booleanParser, requireValue); - } - - public Argument DefineOption(string name, int defaultValue, bool requireValue) - { - return DefineOption(name, defaultValue, s_int32Parser, requireValue); - } - - public Argument DefineOption(string name, T defaultValue, Func valueConverter) - { - return DefineOption(name, defaultValue, valueConverter, true); - } - - public Argument DefineOption(string name, ref T value, Func valueConverter, string help) - { - return DefineOption(name, ref value, valueConverter, true, help); - } - - public Argument DefineOption(string name, ref T value, Func valueConverter, bool requireValue, string help) - { - var option = DefineOption(name, value, valueConverter, requireValue); - option.Help = help; - - value = option.Value; - return option; - } - - public Argument DefineOption(string name, ref string value, string help) - { - return DefineOption(name, ref value, s_stringParser, help); - } - - public Argument DefineOption(string name, ref bool value, string help) - { - return DefineOption(name, ref value, s_booleanParser, help); - } - - public Argument DefineOption(string name, ref int value, string help) - { - return DefineOption(name, ref value, s_int32Parser, help); - } - - public Argument DefineOption(string name, ref string value, bool requireValue, string help) - { - return DefineOption(name, ref value, s_stringParser, requireValue, help); - } - - public Argument DefineOption(string name, ref bool value, bool requireValue, string help) - { - return DefineOption(name, ref value, s_booleanParser, requireValue, help); - } - - public Argument DefineOption(string name, ref int value, bool requireValue, string help) - { - return DefineOption(name, ref value, s_int32Parser, requireValue, help); - } - - // Option lists - - public ArgumentList DefineOptionList(string name, IReadOnlyList defaultValue) - { - return DefineOptionList(name, defaultValue, s_stringParser); - } - - public ArgumentList DefineOptionList(string name, IReadOnlyList defaultValue) - { - return DefineOptionList(name, defaultValue, s_int32Parser); - } - - public ArgumentList DefineOptionList(string name, IReadOnlyList defaultValue, bool requireValue) - { - return DefineOptionList(name, defaultValue, s_stringParser, requireValue); - } - - public ArgumentList DefineOptionList(string name, IReadOnlyList defaultValue, bool requireValue) - { - return DefineOptionList(name, defaultValue, s_int32Parser, requireValue); - } - - public ArgumentList DefineOptionList(string name, IReadOnlyList defaultValue, Func valueConverter) - { - return DefineOptionList(name, defaultValue, valueConverter, true); - } - - public ArgumentList DefineOptionList(string name, ref IReadOnlyList value, Func valueConverter, string help) - { - return DefineOptionList(name, ref value, valueConverter, true, help); - } - - public ArgumentList DefineOptionList(string name, ref IReadOnlyList value, Func valueConverter, bool requireValue, string help) - { - var optionList = DefineOptionList(name, value, valueConverter, requireValue); - optionList.Help = help; - - value = optionList.Value; - return optionList; - } - - public ArgumentList DefineOptionList(string name, ref IReadOnlyList value, string help) - { - return DefineOptionList(name, ref value, s_stringParser, help); - } - - public ArgumentList DefineOptionList(string name, ref IReadOnlyList value, string help) - { - return DefineOptionList(name, ref value, s_int32Parser, help); - } - - public ArgumentList DefineOptionList(string name, ref IReadOnlyList value, bool requireValue, string help) - { - return DefineOptionList(name, ref value, s_stringParser, requireValue, help); - } - - public ArgumentList DefineOptionList(string name, ref IReadOnlyList value, bool requireValue, string help) - { - return DefineOptionList(name, ref value, s_int32Parser, requireValue, help); - } - - // Parameters - - public Argument DefineParameter(string name, string defaultValue) - { - return DefineParameter(name, defaultValue, s_stringParser); - } - - public Argument DefineParameter(string name, bool defaultValue) - { - return DefineParameter(name, defaultValue, s_booleanParser); - } - - public Argument DefineParameter(string name, int defaultValue) - { - return DefineParameter(name, defaultValue, s_int32Parser); - } - - public Argument DefineParameter(string name, ref T value, Func valueConverter, string help) - { - var parameter = DefineParameter(name, value, valueConverter); - parameter.Help = help; - - value = parameter.Value; - return parameter; - } - - public Argument DefineParameter(string name, ref string value, string help) - { - return DefineParameter(name, ref value, s_stringParser, help); - } - - public Argument DefineParameter(string name, ref bool value, string help) - { - return DefineParameter(name, ref value, s_booleanParser, help); - } - - public Argument DefineParameter(string name, ref int value, string help) - { - return DefineParameter(name, ref value, s_int32Parser, help); - } - - // Parameter list - - public ArgumentList DefineParameterList(string name, IReadOnlyList defaultValue) - { - return DefineParameterList(name, defaultValue, s_stringParser); - } - - public ArgumentList DefineParameterList(string name, IReadOnlyList defaultValue) - { - return DefineParameterList(name, defaultValue, s_int32Parser); - } - - public ArgumentList DefineParameterList(string name, ref IReadOnlyList value, Func valueConverter, string help) - { - var parameterList = DefineParameterList(name, value, valueConverter); - parameterList.Help = help; - - value = parameterList.Value; - return parameterList; - } - - public ArgumentList DefineParameterList(string name, ref IReadOnlyList value, string help) - { - return DefineParameterList(name, ref value, s_stringParser, help); - } - - public ArgumentList DefineParameterList(string name, ref IReadOnlyList value, string help) - { - return DefineParameterList(name, ref value, s_int32Parser, help); - } - } -} diff --git a/src/coreclr/tools/Common/CommandLine/ArgumentToken.cs b/src/coreclr/tools/Common/CommandLine/ArgumentToken.cs deleted file mode 100644 index 83ef63435d84b6..00000000000000 --- a/src/coreclr/tools/Common/CommandLine/ArgumentToken.cs +++ /dev/null @@ -1,80 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Internal.CommandLine -{ - internal sealed class ArgumentToken - { - internal ArgumentToken(string modifier, string name, string value) - { - Modifier = modifier; - Name = name; - Value = value; - } - - public string Modifier { get; private set; } - - public string Name { get; private set; } - - public string Value { get; private set; } - - public bool IsOption - { - get { return !string.IsNullOrEmpty(Modifier); } - } - - public bool IsSeparator - { - get { return Name == @":" || Name == @"="; } - } - - public bool HasValue - { - get { return !string.IsNullOrEmpty(Value); } - } - - public bool IsMatched { get; private set; } - - public void MarkMatched() - { - IsMatched = true; - } - - private bool Equals(ArgumentToken other) - { - return string.Equals(Modifier, other.Modifier) && - string.Equals(Name, other.Name) && - string.Equals(Value, other.Value); - } - - public override bool Equals(object obj) - { - if (obj is null) - return false; - - if (ReferenceEquals(obj, this)) - return true; - - var other = obj as ArgumentToken; - return other is not null && Equals(other); - } - - public override int GetHashCode() - { - unchecked - { - var hashCode = (Modifier != null ? Modifier.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ (Name != null ? Name.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ (Value != null ? Value.GetHashCode() : 0); - return hashCode; - } - } - - public override string ToString() - { - return HasValue - ? string.Format(@"{0}{1}:{2}", Modifier, Name, Value) - : string.Format(@"{0}{1}", Modifier, Name); - } - } -} diff --git a/src/coreclr/tools/Common/CommandLine/Argument_1.cs b/src/coreclr/tools/Common/CommandLine/Argument_1.cs deleted file mode 100644 index 8928ab3872a1eb..00000000000000 --- a/src/coreclr/tools/Common/CommandLine/Argument_1.cs +++ /dev/null @@ -1,49 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.Generic; - -namespace Internal.CommandLine -{ - public sealed class Argument : Argument - { - internal Argument(ArgumentCommand command, IEnumerable names, T defaultValue, bool isRequired) - : base(command, names, true, isRequired) - { - Value = defaultValue; - Value = DefaultValue = defaultValue; - } - - internal Argument(ArgumentCommand command, string name, T defaultValue) - : base(command, new[] { name }, false, true) - { - Value = defaultValue; - DefaultValue = defaultValue; - } - - public new T Value { get; private set; } - - public new T DefaultValue { get; private set; } - - public override bool IsFlag - { - get { return typeof(T) == typeof(bool); } - } - - internal override object GetValue() - { - return Value; - } - - internal override object GetDefaultValue() - { - return DefaultValue; - } - - internal void SetValue(T value) - { - Value = value; - MarkSpecified(); - } - } -} diff --git a/src/coreclr/tools/Common/CommandLine/CommandLineException.cs b/src/coreclr/tools/Common/CommandLine/CommandLineException.cs deleted file mode 100644 index 3785047b6a5a51..00000000000000 --- a/src/coreclr/tools/Common/CommandLine/CommandLineException.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; - -namespace Internal.CommandLine -{ - internal sealed class CommandLineException : Exception - { - public CommandLineException(string message) - : base(message) - { - } - } -} diff --git a/src/coreclr/tools/Common/CommandLine/CommandLineHelpers.cs b/src/coreclr/tools/Common/CommandLine/CommandLineHelpers.cs deleted file mode 100644 index d7fa472f748acd..00000000000000 --- a/src/coreclr/tools/Common/CommandLine/CommandLineHelpers.cs +++ /dev/null @@ -1,218 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Collections; -using System.Collections.Generic; -using System.IO; -using System.IO.Compression; - -namespace Internal.CommandLine -{ - // - // Helpers for command line processing - // - internal static class Helpers - { - // Helper to create a collection of paths unique in their simple names. - public static void AppendExpandedPaths(Dictionary dictionary, string pattern, bool strict) - { - bool empty = true; - - string directoryName = Path.GetDirectoryName(pattern); - string searchPattern = Path.GetFileName(pattern); - - if (directoryName == "") - directoryName = "."; - - if (Directory.Exists(directoryName)) - { - foreach (string fileName in Directory.EnumerateFiles(directoryName, searchPattern)) - { - string fullFileName = Path.GetFullPath(fileName); - - string simpleName = Path.GetFileNameWithoutExtension(fileName); - - if (dictionary.ContainsKey(simpleName)) - { - if (strict) - { - throw new CommandLineException("Multiple input files matching same simple name " + - fullFileName + " " + dictionary[simpleName]); - } - } - else - { - dictionary.Add(simpleName, fullFileName); - } - - empty = false; - } - } - - if (empty) - { - if (strict) - { - throw new CommandLineException("No files matching " + pattern); - } - else - { - Console.WriteLine("Warning: No files matching " + pattern); - } - } - } - -// ILVerify needs to switch command line processing to Internal.CommandLine and then it can take advantage of this too -#if !ILVERIFY - public static void MakeReproPackage(string makeReproPath, string outputFilePath, string[] args, ArgumentSyntax argSyntax, IEnumerable inputOptions) - { - Directory.CreateDirectory(makeReproPath); - - List details = new List(); - details.Add("Tool version"); - try - { - details.Add(Environment.GetCommandLineArgs()[0]); - } - catch { } - try - { - details.Add(System.Diagnostics.FileVersionInfo.GetVersionInfo(Environment.GetCommandLineArgs()[0]).ToString()); - } - catch { } - - details.Add("------------------------"); - details.Add("Actual Command Line Args"); - details.Add("------------------------"); - details.AddRange(args); - foreach (string arg in args) - { - if (arg.StartsWith('@')) - { - string rspFileName = arg.Substring(1); - details.Add("------------------------"); - details.Add(rspFileName); - details.Add("------------------------"); - try - { - details.AddRange(File.ReadAllLines(rspFileName)); - } - catch { } - } - } - - HashCode hashCodeOfArgs = default(HashCode); - foreach (string s in details) - hashCodeOfArgs.Add(s); - - string zipFileName = ((uint)hashCodeOfArgs.ToHashCode()).ToString(); - - if (outputFilePath != null) - zipFileName = zipFileName + "_" + Path.GetFileName(outputFilePath); - - zipFileName = Path.Combine(makeReproPath, Path.ChangeExtension(zipFileName, ".zip")); - - Console.WriteLine($"Creating {zipFileName}"); - using (var archive = ZipFile.Open(zipFileName, ZipArchiveMode.Create)) - { - ZipArchiveEntry commandEntry = archive.CreateEntry("command.txt"); - using (StreamWriter writer = new StreamWriter(commandEntry.Open())) - { - foreach (string s in details) - writer.WriteLine(s); - } - - HashSet inputOptionNames = new HashSet(inputOptions); - Dictionary inputToReproPackageFileName = new Dictionary(); - - List rspFile = new List(); - foreach (var option in argSyntax.GetOptions()) - { - if (option.GetDisplayName() == "--make-repro-path") - { - continue; - } - - if (option.Value != null && !option.Value.Equals(option.DefaultValue)) - { - if (option.IsList) - { - if (inputOptionNames.Contains(option.GetDisplayName())) - { - Dictionary dictionary = new Dictionary(); - foreach (string optInList in (IEnumerable)option.Value) - { - AppendExpandedPaths(dictionary, optInList, false); - } - foreach (string inputFile in dictionary.Values) - { - rspFile.Add($"{option.GetDisplayName()}:{ConvertFromInputPathToReproPackagePath(inputFile)}"); - } - } - else - { - foreach (object optInList in (IEnumerable)option.Value) - { - rspFile.Add($"{option.GetDisplayName()}:{optInList}"); - } - } - } - else - { - rspFile.Add($"{option.GetDisplayName()}:{option.Value}"); - } - } - } - - foreach (var parameter in argSyntax.GetParameters()) - { - if (parameter.Value != null) - { - if (parameter.IsList) - { - foreach (object optInList in (IEnumerable)parameter.Value) - { - rspFile.Add($"{ConvertFromInputPathToReproPackagePath((string)optInList)}"); - } - } - else - { - rspFile.Add($"{ConvertFromInputPathToReproPackagePath((string)parameter.Value.ToString())}"); - } - } - } - - ZipArchiveEntry rspEntry = archive.CreateEntry("repro.rsp"); - using (StreamWriter writer = new StreamWriter(rspEntry.Open())) - { - foreach (string s in rspFile) - writer.WriteLine(s); - } - - string ConvertFromInputPathToReproPackagePath(string inputPath) - { - if (inputToReproPackageFileName.TryGetValue(inputPath, out string reproPackagePath)) - { - return reproPackagePath; - } - - try - { - string inputFileDir = inputToReproPackageFileName.Count.ToString(); - reproPackagePath = Path.Combine(inputFileDir, Path.GetFileName(inputPath)); - archive.CreateEntryFromFile(inputPath, reproPackagePath); - inputToReproPackageFileName.Add(inputPath, reproPackagePath); - - return reproPackagePath; - } - catch - { - return inputPath; - } - } - } - } -#endif // !ILVERIFY - } -} diff --git a/src/coreclr/tools/Common/CommandLine/Enumerable.cs b/src/coreclr/tools/Common/CommandLine/Enumerable.cs deleted file mode 100644 index a29dbbf30f2559..00000000000000 --- a/src/coreclr/tools/Common/CommandLine/Enumerable.cs +++ /dev/null @@ -1,87 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Collections.Generic; -using System.Diagnostics; - -using Linq = System.Linq; - -namespace Internal.CommandLine -{ - // Low-level replacement of LINQ Enumerable class. In particular, this implementation - // doesn't use generic virtual methods. - internal static class Enumerable - { - public static IEnumerable Empty() - { - return Linq.Enumerable.Empty(); - } - - public static IEnumerable Select(this IEnumerable values, Func func) - { - Debug.Assert(values != null); - - foreach (T value in values) - { - yield return func(value); - } - } - - public static IEnumerable Where(this IEnumerable values, Func func) - { - Debug.Assert(values != null); - - foreach (T value in values) - { - if (func(value)) - yield return value; - } - } - - public static IEnumerable Concat(this IEnumerable first, IEnumerable second) - { - return Linq.Enumerable.Concat(first, second); - } - - public static bool All(this IEnumerable source, Func predicate) - { - return Linq.Enumerable.All(source, predicate); - } - - public static bool Any(this IEnumerable source, Func predicate) - { - return Linq.Enumerable.Any(source, predicate); - } - - public static bool Any(this IEnumerable source) - { - return Linq.Enumerable.Any(source); - } - - public static T[] ToArray(this IEnumerable source) - { - return Linq.Enumerable.ToArray(source); - } - - public static T Last(this IEnumerable source) - { - return Linq.Enumerable.Last(source); - } - - public static T FirstOrDefault(this IEnumerable source) - { - return Linq.Enumerable.FirstOrDefault(source); - } - - public static T First(this IEnumerable source) - { - return Linq.Enumerable.First(source); - } - - public static int Max(this IEnumerable source) - { - return Linq.Enumerable.Max(source); - } - } -} diff --git a/src/coreclr/tools/Common/CommandLine/HelpTextGenerator.cs b/src/coreclr/tools/Common/CommandLine/HelpTextGenerator.cs deleted file mode 100644 index 3c2f621901321d..00000000000000 --- a/src/coreclr/tools/Common/CommandLine/HelpTextGenerator.cs +++ /dev/null @@ -1,271 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.Generic; -using System.Text; - -namespace Internal.CommandLine -{ - internal static class HelpTextGenerator - { - public static string Generate(ArgumentSyntax argumentSyntax, int maxWidth) - { - var forCommandList = argumentSyntax.ActiveCommand == null && - argumentSyntax.Commands.Any(); - - var page = forCommandList - ? GetCommandListHelp(argumentSyntax) - : GetCommandHelp(argumentSyntax, argumentSyntax.ActiveCommand); - - var sb = new StringBuilder(); - sb.WriteHelpPage(page, maxWidth); - return sb.ToString(); - } - - private struct HelpPage - { - public string ApplicationName; - public IEnumerable SyntaxElements; - public IReadOnlyList Rows; - public IReadOnlyList ExtraParagraphs; - } - - private struct HelpRow - { - public string Header; - public string Text; - } - - private static void WriteHelpPage(this StringBuilder sb, HelpPage page, int maxWidth) - { - sb.WriteUsage(page.ApplicationName, page.SyntaxElements, maxWidth); - - if (!page.Rows.Any()) - return; - - sb.AppendLine(); - - sb.WriteRows(page.Rows, maxWidth); - - sb.AppendLine(); - - if (page.ExtraParagraphs != null) - { - foreach (string text in page.ExtraParagraphs) - { - var words = SplitWords(text); - sb.WriteWordWrapped(words, 0, maxWidth); - } - } - } - - private static void WriteUsage(this StringBuilder sb, string applicationName, IEnumerable syntaxElements, int maxWidth) - { - var usageHeader = string.Format(Strings.HelpUsageOfApplicationFmt, applicationName); - sb.Append(usageHeader); - - if (syntaxElements.Any()) - sb.Append(' '); - - var syntaxIndent = usageHeader.Length + 1; - var syntaxMaxWidth = maxWidth - syntaxIndent; - - sb.WriteWordWrapped(syntaxElements, syntaxIndent, syntaxMaxWidth); - } - - private static void WriteRows(this StringBuilder sb, IReadOnlyList rows, int maxWidth) - { - const int indent = 4; - var maxColumnWidth = rows.Select(r => r.Header.Length).Max(); - var helpStartColumn = maxColumnWidth + 2 * indent; - - var maxHelpWidth = maxWidth - helpStartColumn; - if (maxHelpWidth < 0) - maxHelpWidth = maxWidth; - - foreach (var row in rows) - { - var headerStart = sb.Length; - - sb.Append(' ', indent); - sb.Append(row.Header); - - var headerLength = sb.Length - headerStart; - var requiredSpaces = helpStartColumn - headerLength; - - sb.Append(' ', requiredSpaces); - - var words = SplitWords(row.Text); - sb.WriteWordWrapped(words, helpStartColumn, maxHelpWidth); - } - } - - private static void WriteWordWrapped(this StringBuilder sb, IEnumerable words, int indent, int maxidth) - { - var helpLines = WordWrapLines(words, maxidth); - var isFirstHelpLine = true; - - foreach (var helpLine in helpLines) - { - if (isFirstHelpLine) - isFirstHelpLine = false; - else - sb.Append(' ', indent); - - sb.AppendLine(helpLine); - } - - if (isFirstHelpLine) - sb.AppendLine(); - } - - private static HelpPage GetCommandListHelp(ArgumentSyntax argumentSyntax) - { - return new HelpPage - { - ApplicationName = argumentSyntax.ApplicationName, - SyntaxElements = GetGlobalSyntax(), - Rows = GetCommandRows(argumentSyntax).ToArray(), - ExtraParagraphs = argumentSyntax.ExtraHelpParagraphs - }; - } - - private static HelpPage GetCommandHelp(ArgumentSyntax argumentSyntax, ArgumentCommand command) - { - return new HelpPage - { - ApplicationName = argumentSyntax.ApplicationName, - SyntaxElements = GetCommandSyntax(argumentSyntax, command), - Rows = GetArgumentRows(argumentSyntax, command).ToArray(), - ExtraParagraphs = argumentSyntax.ExtraHelpParagraphs - }; - } - - private static IEnumerable GetGlobalSyntax() - { - yield return @""; - yield return @"[]"; - } - - private static IEnumerable GetCommandSyntax(ArgumentSyntax argumentSyntax, ArgumentCommand command) - { - if (command != null) - yield return command.Name; - - foreach (var option in argumentSyntax.GetOptions(command).Where(o => !o.IsHidden)) - yield return GetOptionSyntax(option); - - if (argumentSyntax.GetParameters(command).All(p => p.IsHidden)) - yield break; - - if (argumentSyntax.GetOptions(command).Any(o => !o.IsHidden)) - yield return @"[--]"; - - foreach (var parameter in argumentSyntax.GetParameters(command).Where(o => !o.IsHidden)) - yield return GetParameterSyntax(parameter); - } - - private static string GetOptionSyntax(Argument option) - { - var sb = new StringBuilder(); - - sb.Append('['); - sb.Append(option.GetDisplayName()); - - if (!option.IsFlag) - sb.Append(option.IsRequired ? @" " : @" [arg]"); - - if (option.IsList) - sb.Append(@"..."); - - sb.Append(']'); - - return sb.ToString(); - } - - private static string GetParameterSyntax(Argument parameter) - { - var sb = new StringBuilder(); - - sb.Append(parameter.GetDisplayName()); - if (parameter.IsList) - sb.Append(@"..."); - - return sb.ToString(); - } - - private static IEnumerable GetCommandRows(ArgumentSyntax argumentSyntax) - { - return argumentSyntax.Commands - .Where(c => !c.IsHidden) - .Select(c => new HelpRow { Header = c.Name, Text = c.Help }); - } - - private static IEnumerable GetArgumentRows(ArgumentSyntax argumentSyntax, ArgumentCommand command) - { - return argumentSyntax.GetArguments(command) - .Where(a => !a.IsHidden) - .Select(a => new HelpRow { Header = GetArgumentRowHeader(a), Text = a.Help }); - } - - private static string GetArgumentRowHeader(Argument argument) - { - var sb = new StringBuilder(); - - foreach (var displayName in argument.GetDisplayNames()) - { - if (sb.Length > 0) - sb.Append(@", "); - - sb.Append(displayName); - } - - if (argument.IsOption && !argument.IsFlag) - sb.Append(argument.IsRequired ? @" " : @" [arg]"); - - if (argument.IsList) - sb.Append(@"..."); - - return sb.ToString(); - } - - private static IEnumerable WordWrapLines(IEnumerable tokens, int maxWidth) - { - var sb = new StringBuilder(); - - foreach (var token in tokens) - { - var newLength = sb.Length == 0 - ? token.Length - : sb.Length + 1 + token.Length; - - if (newLength > maxWidth) - { - if (sb.Length == 0) - { - yield return token; - continue; - } - - yield return sb.ToString(); - sb.Clear(); - } - - if (sb.Length > 0) - sb.Append(' '); - - sb.Append(token); - } - - if (sb.Length > 0) - yield return sb.ToString(); - } - - private static IEnumerable SplitWords(string text) - { - return string.IsNullOrEmpty(text) - ? Enumerable.Empty() - : text.Split(' '); - } - } -} diff --git a/src/coreclr/tools/Common/CommandLine/Resources/Strings.resx b/src/coreclr/tools/Common/CommandLine/Resources/Strings.resx deleted file mode 100644 index 0949d6fa044876..00000000000000 --- a/src/coreclr/tools/Common/CommandLine/Resources/Strings.resx +++ /dev/null @@ -1,183 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Cannot define commands if global options or parameters exist. - - - value '{0}' isn't valid for {1}: {2} - - - Command '{0}' is already defined. - - - error: {0} - - - extra parameter '{0}' - - - usage: {0} - - - invalid option {0} - - - missing command - - - You must specify a name. - - - Option '{0}' is already defined. - - - Options must be defined before any parameters. - - - Response file '{0}' doesn't exist. - - - unknown command '{0}' - - - Unmatched quote at position {0}. - - - option {0} requires a value - - - Parameter '{0}' is already defined. - - - Cannot define multiple parameter lists. - - - Parameters cannot be defined after parameter lists. - - - Name '{0}' cannot be used for a command. - - - Name '{0}' cannot be used for an option. - - - Name '{0}' cannot be used for a parameter. - - \ No newline at end of file diff --git a/src/coreclr/tools/Common/CommandLineHelpers.cs b/src/coreclr/tools/Common/CommandLineHelpers.cs index f7d4a46ebe7a1d..18dd5bba0fe26f 100644 --- a/src/coreclr/tools/Common/CommandLineHelpers.cs +++ b/src/coreclr/tools/Common/CommandLineHelpers.cs @@ -24,7 +24,7 @@ internal static class Helpers { public const string DefaultSystemModule = "System.Private.CoreLib"; - public static Dictionary BuildPathDictionay(IReadOnlyList tokens, bool strict) + public static Dictionary BuildPathDictionary(IReadOnlyList tokens, bool strict) { Dictionary dictionary = new(StringComparer.OrdinalIgnoreCase); @@ -36,6 +36,22 @@ public static Dictionary BuildPathDictionay(IReadOnlyList return dictionary; } + public static List BuildPathList(IReadOnlyList tokens) + { + List paths = new(); + foreach (Token token in tokens) + { + Dictionary dictionary = new(StringComparer.OrdinalIgnoreCase); + AppendExpandedPaths(dictionary, token.Value, false); + foreach (string file in dictionary.Values) + { + paths.Add(file); + } + } + + return paths; + } + public static TargetOS GetTargetOS(string token) { if(string.IsNullOrEmpty(token)) @@ -171,7 +187,7 @@ public static void MakeReproPackage(string makeReproPath, string outputFilePath, Dictionary dictionary = new(); foreach (string optInList in values) { - Helpers.AppendExpandedPaths(dictionary, optInList, false); + AppendExpandedPaths(dictionary, optInList, false); } foreach (string inputFile in dictionary.Values) { @@ -241,7 +257,7 @@ string ConvertFromInputPathToReproPackagePath(string inputPath) } // Helper to create a collection of paths unique in their simple names. - private static void AppendExpandedPaths(Dictionary dictionary, string pattern, bool strict) + internal static void AppendExpandedPaths(Dictionary dictionary, string pattern, bool strict) { bool empty = true; string directoryName = Path.GetDirectoryName(pattern); diff --git a/src/coreclr/tools/ILVerify/ILVerify.csproj b/src/coreclr/tools/ILVerify/ILVerify.csproj index 8c341dcde4dc64..c4f768ab4d81e3 100644 --- a/src/coreclr/tools/ILVerify/ILVerify.csproj +++ b/src/coreclr/tools/ILVerify/ILVerify.csproj @@ -12,12 +12,7 @@ - - CommandLine\CommandLineException.cs - - - CommandLine\CommandLineHelpers.cs - + diff --git a/src/coreclr/tools/ILVerify/Program.cs b/src/coreclr/tools/ILVerify/Program.cs index 02480ca9592502..6ae6dcdcccc04a 100644 --- a/src/coreclr/tools/ILVerify/Program.cs +++ b/src/coreclr/tools/ILVerify/Program.cs @@ -13,7 +13,6 @@ using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; -using Internal.CommandLine; using Internal.TypeSystem.Ecma; using static System.Console; diff --git a/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs b/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs index 9f9346089b2ff3..dd8cf2239dff2a 100644 --- a/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs +++ b/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs @@ -16,9 +16,9 @@ namespace ILCompiler internal sealed class ILCompilerRootCommand : RootCommand { public Argument> InputFilePaths { get; } = - new("input-file-path", result => Helpers.BuildPathDictionay(result.Tokens, true), false, "Input file(s)") { Arity = ArgumentArity.OneOrMore }; + new("input-file-path", result => Helpers.BuildPathDictionary(result.Tokens, true), false, "Input file(s)") { Arity = ArgumentArity.OneOrMore }; public Option> ReferenceFiles { get; } = - new(new[] { "--reference", "-r" }, result => Helpers.BuildPathDictionay(result.Tokens, false), true, "Reference file(s) for compilation"); + new(new[] { "--reference", "-r" }, result => Helpers.BuildPathDictionary(result.Tokens, false), true, "Reference file(s) for compilation"); public Option OutputFilePath { get; } = new(new[] { "--out", "-o" }, "Output file path"); public Option Optimize { get; } = diff --git a/src/coreclr/tools/aot/ILCompiler/Program.cs b/src/coreclr/tools/aot/ILCompiler/Program.cs index a3b268fa418826..23335e08f44ed2 100644 --- a/src/coreclr/tools/aot/ILCompiler/Program.cs +++ b/src/coreclr/tools/aot/ILCompiler/Program.cs @@ -32,7 +32,7 @@ public Program(ILCompilerRootCommand command) { _command = command; - if (command.Result.GetValueForOption(command.WaitForDebugger)) + if (Get(command.WaitForDebugger)) { Console.WriteLine("Waiting for debugger to attach. Press ENTER to continue"); Console.ReadLine(); diff --git a/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs b/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs index 8569f9fbf1cb79..6d9a0b80caab1c 100644 --- a/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs +++ b/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs @@ -16,11 +16,11 @@ namespace ILCompiler internal class Crossgen2RootCommand : RootCommand { public Argument> InputFilePaths { get; } = - new("input-file-path", result => Helpers.BuildPathDictionay(result.Tokens, true), false, "Input file(s)") { Arity = ArgumentArity.OneOrMore }; + new("input-file-path", result => Helpers.BuildPathDictionary(result.Tokens, true), false, "Input file(s)") { Arity = ArgumentArity.OneOrMore }; public Option> UnrootedInputFilePaths { get; } = - new(new[] { "--unrooted-input-file-paths", "-u" }, result => Helpers.BuildPathDictionay(result.Tokens, true), true, SR.UnrootedInputFilesToCompile); + new(new[] { "--unrooted-input-file-paths", "-u" }, result => Helpers.BuildPathDictionary(result.Tokens, true), true, SR.UnrootedInputFilesToCompile); public Option> ReferenceFilePaths { get; } = - new(new[] { "--reference", "-r" }, result => Helpers.BuildPathDictionay(result.Tokens, false), true, SR.ReferenceFiles); + new(new[] { "--reference", "-r" }, result => Helpers.BuildPathDictionary(result.Tokens, false), true, SR.ReferenceFiles); public Option InstructionSet { get; } = new(new[] { "--instruction-set" }, SR.InstructionSets); public Option MibcFilePaths { get; } = @@ -40,7 +40,7 @@ internal class Crossgen2RootCommand : RootCommand public Option InputBubble { get; } = new(new[] { "--inputbubble" }, SR.InputBubbleOption); public Option> InputBubbleReferenceFilePaths { get; } = - new(new[] { "--inputbubbleref" }, result => Helpers.BuildPathDictionay(result.Tokens, false), true, SR.InputBubbleReferenceFiles); + new(new[] { "--inputbubbleref" }, result => Helpers.BuildPathDictionary(result.Tokens, false), true, SR.InputBubbleReferenceFiles); public Option Composite { get; } = new(new[] { "--composite" }, SR.CompositeBuildMode); public Option CompositeKeyFile { get; } = diff --git a/src/coreclr/tools/aot/crossgen2/Program.cs b/src/coreclr/tools/aot/crossgen2/Program.cs index 72ef597a41d535..acfe0430ec00cc 100644 --- a/src/coreclr/tools/aot/crossgen2/Program.cs +++ b/src/coreclr/tools/aot/crossgen2/Program.cs @@ -52,7 +52,7 @@ public Program(Crossgen2RootCommand command) _targetOS = Get(command.TargetOS); _targetArchitecture = Get(command.TargetArchitecture); - if (command.Result.GetValueForOption(command.WaitForDebugger)) + if (Get(command.WaitForDebugger)) { Console.WriteLine("Waiting for debugger to attach. Press ENTER to continue"); Console.ReadLine(); diff --git a/src/coreclr/tools/aot/crossgen2/crossgen2.props b/src/coreclr/tools/aot/crossgen2/crossgen2.props index dee32ed5ba401d..d3684e1bc4f277 100644 --- a/src/coreclr/tools/aot/crossgen2/crossgen2.props +++ b/src/coreclr/tools/aot/crossgen2/crossgen2.props @@ -21,11 +21,6 @@ true System.SR - - - true - Internal.CommandLine.Strings - diff --git a/src/coreclr/tools/dotnet-pgo/CommandLineOptions.cs b/src/coreclr/tools/dotnet-pgo/CommandLineOptions.cs deleted file mode 100644 index 2725165a55a662..00000000000000 --- a/src/coreclr/tools/dotnet-pgo/CommandLineOptions.cs +++ /dev/null @@ -1,415 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Reflection; - -using Internal.CommandLine; - -namespace Microsoft.Diagnostics.Tools.Pgo -{ - internal class CommandLineOptions - { - public bool Help; - public string HelpText; - - public FileInfo TraceFile; - public FileInfo OutputFileName; - public FileInfo PreciseDebugInfoFile; - public int? Pid; - public string ProcessName; - public PgoFileType? FileType; - public IEnumerable Reference; - public int? ClrInstanceId; - public bool ProcessJitEvents; - public bool ProcessR2REvents; - public bool DisplayProcessedEvents; - public bool ValidateOutputFile; - public bool GenerateCallGraph; - public bool Spgo; - public bool IncludeFullGraphs; - public int SpgoMinSamples = 50; - public bool VerboseWarnings; - public JitTraceOptions JitTraceOptions; - public double ExcludeEventsBefore; - public double ExcludeEventsAfter; - public bool Warnings; - public bool Uncompressed; - public bool BasicProgressMessages; - public bool DetailedProgressMessages; - public List InputFilesToMerge; - public List IncludedAssemblies = new List(); - public bool DumpMibc = false; - public FileInfo InputFileToDump; - public List CompareMibc; - public DirectoryInfo DumpWorstOverlapGraphsTo; - public int DumpWorstOverlapGraphs = -1; - public bool InheritTimestamp; - public bool AutomaticReferences; - - public string[] HelpArgs = Array.Empty(); - - private enum Verbosity - { - minimal, - normal, - detailed, - diagnostic - } - - private Verbosity VerbosityConverter(string s) - { - try - { - return Enum.Parse(s); - } - catch - { - throw new FormatException("Must be one of minimal, normal, detailed, or diagnostic"); - } - } - - private void FailIfUnspecified(Argument arg) - { - if (arg.Command.IsActive && !arg.IsSpecified) - throw new ArgumentSyntaxException($"--{arg.Name} must be specified."); - } - - void DefineArgumentSyntax(ArgumentSyntax syntax) - { - bool activeCommandIsCommandAssociatedWithTraceProcessing = false; - syntax.ApplicationName = typeof(Program).Assembly.GetName().Name.ToString(); - - // HandleHelp writes to error, fails fast with crash dialog and lacks custom formatting. - syntax.HandleHelp = false; - syntax.HandleErrors = false; - - string command = ""; - - void CommonOptions() - { - string traceFile = null; - syntax.DefineOption( - name: "t|trace", - value: ref traceFile, - help: "Specify the trace file to be parsed.", - requireValue: true); - if (traceFile != null) - TraceFile = new FileInfo(traceFile); - - OutputOption(); - - int pidLocal = 0; - if (syntax.DefineOption( - name: "pid", - value: ref pidLocal, - help: "The pid within the trace of the process to examine. If this is a multi-process trace, at least one of --pid or --process-name must be specified", - requireValue: true).IsSpecified) - Pid = pidLocal; - - syntax.DefineOption( - name: "process-name", - value: ref ProcessName, - help: "The process name within the trace of the process to examine. If this is a multi-process trace, at least one of --pid or --process-name must be specified.", - requireValue: false); - - int clrInstanceIdLocal = 0; - if (syntax.DefineOption( - name: "clr-instance-id", - value: ref clrInstanceIdLocal, - help: "If the process contains multiple .NET runtimes, the instance ID must be specified.", - requireValue: true).IsSpecified) - { - ClrInstanceId = clrInstanceIdLocal; - } - - Reference = DefineFileOptionList(name: "r|reference", help: "If a reference is not located on disk at the same location as used in the process, it may be specified with a --reference parameter. Multiple --reference parameters may be specified. The wild cards * and ? are supported by this option."); - - AutomaticReferences = true; - syntax.DefineOption( - name: "automatic-references", - value: ref AutomaticReferences, - help: "Attempt to find references by using paths embedded in the trace file. Defaults to true.", - requireValue: true); - - ExcludeEventsBefore = Double.MinValue; - syntax.DefineOption( - name: "exclude-events-before", - value: ref ExcludeEventsBefore, - help: "Exclude data from events before specified time. Time is specified as milliseconds from the start of the trace.", - valueConverter: Convert.ToDouble, - requireValue: true); - - ExcludeEventsAfter = Double.MaxValue; - syntax.DefineOption( - name: "exclude-events-after", - value: ref ExcludeEventsAfter, - help: "Exclude data from events after specified time. Time is specified as milliseconds from the start of the trace.", - valueConverter: Convert.ToDouble, - requireValue: true); - - VerbosityOption(); - } - - void OutputOption() - { - string outputFile = null; - syntax.DefineOption( - name: "o|output", - value: ref outputFile, - help: "Specify the output filename to be created.", - requireValue: true); - if (outputFile != null) - OutputFileName = new FileInfo(outputFile); - } - - void VerbosityOption() - { - Verbosity verbosity = Verbosity.normal; - syntax.DefineOption(name: "v|verbosity", value: ref verbosity, help: "Adjust verbosity level. Supported levels are minimal, normal, detailed, and diagnostic.", valueConverter: VerbosityConverter, requireValue: true); - BasicProgressMessages = (int)verbosity >= (int)Verbosity.normal; - Warnings = (int)verbosity >= (int)Verbosity.normal; - VerboseWarnings = (int)verbosity >= (int)Verbosity.detailed; - DetailedProgressMessages = (int)verbosity >= (int)Verbosity.detailed; - DisplayProcessedEvents = (int)verbosity >= (int)Verbosity.diagnostic; - } - - void CompressedOption() - { - bool compressed = false; - syntax.DefineOption(name: "compressed", value: ref compressed, help: "Generate compressed mibc", requireValue: false); - Uncompressed = !compressed; - } - - void HelpOption() - { - syntax.DefineOption("h|help", ref Help, "Display this usage message."); - } - - var mibcCommand = syntax.DefineCommand(name: "create-mibc", value: ref command, help: "Transform a trace file into a Mibc profile data file."); - if (mibcCommand.IsActive) - { - activeCommandIsCommandAssociatedWithTraceProcessing = true; - HelpArgs = new string[] { "create-mibc", "--help", "--trace", "trace", "--output", "output" }; - FileType = PgoFileType.mibc; - GenerateCallGraph = true; - ProcessJitEvents = true; - ProcessR2REvents = true; -#if DEBUG - ValidateOutputFile = true; -#else - ValidateOutputFile = false; -#endif - CommonOptions(); - CompressedOption(); - - string preciseDebugInfoFile = null; - syntax.DefineOption(name: "precise-debug-info-file", ref preciseDebugInfoFile, "Name of file of newline separated JSON objects containing precise debug info"); - if (preciseDebugInfoFile != null) - PreciseDebugInfoFile = new FileInfo(preciseDebugInfoFile); - - syntax.DefineOption(name: "spgo", value: ref Spgo, help: "Base profile on samples in the input. Uses last branch records if available and otherwise raw IP samples.", requireValue: false); - syntax.DefineOption(name: "spgo-min-samples", value: ref SpgoMinSamples, help: $"The minimum number of total samples a function must have before generating profile data for it with SPGO. Default: {SpgoMinSamples}", requireValue: false); - - syntax.DefineOption(name: "include-full-graphs", value: ref IncludeFullGraphs, help: "Include all blocks and edges in the written .mibc file, regardless of profile counts", requireValue: false); - - HelpOption(); - } - - JitTraceOptions = JitTraceOptions.none; -#if DEBUG - // Usage of the jittrace format requires using logic embedded in the runtime repository and isn't suitable for general consumer use at this time - // Build it in debug and check builds to ensure that it doesn't bitrot, and remains available for use by developers willing to build the repo - var jittraceCommand = syntax.DefineCommand(name: "create-jittrace", value: ref command, help: "Transform a trace file into a jittrace runtime file."); - if (jittraceCommand.IsActive) - { - activeCommandIsCommandAssociatedWithTraceProcessing = true; - HelpArgs = new string[] { "create-jittrace", "--help", "--trace", "trace", "--output", "output" }; - FileType = PgoFileType.jittrace; - ProcessJitEvents = true; - ProcessR2REvents = false; - ValidateOutputFile = false; - CommonOptions(); - - bool sorted = false; - syntax.DefineOption(name: "sorted", value: ref sorted, help: "Generate sorted output.", requireValue: false); - if (sorted) - { - JitTraceOptions |= JitTraceOptions.sorted; - } - - bool showtimestamp = false; - syntax.DefineOption(name: "showtimestamp", value: ref showtimestamp, help: "Show timestamps in output.", requireValue: false); - if (showtimestamp) - { - JitTraceOptions |= JitTraceOptions.showtimestamp; - } - - syntax.DefineOption(name: "includeReadyToRun", value: ref ProcessR2REvents, help: "Include ReadyToRun methods in the trace file.", requireValue: false); - HelpOption(); - } -#endif - - var mergeCommand = syntax.DefineCommand(name: "merge", value: ref command, help: "Merge multiple Mibc profile data files into one file."); - if (mergeCommand.IsActive) - { - HelpArgs = new string[] { "merge", "--help", "--output", "output", "--input", "input"}; - - InputFilesToMerge = DefineFileOptionList(name: "i|input", help: "Input .mibc files to be merged. Multiple input arguments are specified as --input file1.mibc --input file2.mibc"); - OutputOption(); - - IReadOnlyList assemblyNamesAsStrings = null; - syntax.DefineOptionList(name: "include-reference", value: ref assemblyNamesAsStrings, help: "If specified, include in Mibc file only references to the specified assemblies. Assemblies are specified as assembly names, not filenames. For instance, `System.Private.CoreLib` not `System.Private.CoreLib.dll`. Multiple --include-reference options may be specified.", requireValue: true); - if (assemblyNamesAsStrings != null) - { - foreach (string asmName in assemblyNamesAsStrings) - { - try - { - IncludedAssemblies.Add(new AssemblyName(asmName)); - } - catch - { - throw new FormatException($"Unable to parse '{asmName}' as an Assembly Name."); - } - } - } - - syntax.DefineOption(name: "inherit-timestamp", value: ref InheritTimestamp, help: "If specified, set the output's timestamp to the max timestamp of the input files"); - - VerbosityOption(); - CompressedOption(); - HelpOption(); -#if DEBUG - ValidateOutputFile = true; -#else - ValidateOutputFile = false; -#endif - } - - var dumpCommand = syntax.DefineCommand(name: "dump", value: ref command, help: "Dump the contents of a Mibc file."); - if (dumpCommand.IsActive) - { - DumpMibc = true; - HelpArgs = new string[] { "dump", "--help", "input", "output" }; - - VerbosityOption(); - HelpOption(); - - string inputFileToDump = null; - syntax.DefineParameter(name: "input", ref inputFileToDump, "Name of the input mibc file to dump."); - if (inputFileToDump != null) - InputFileToDump = new FileInfo(inputFileToDump); - - string outputFile = null; - syntax.DefineParameter(name: "output", ref outputFile, "Name of the output dump file."); - if (outputFile != null) - OutputFileName = new FileInfo(outputFile); - } - - var compareMibcCommand = syntax.DefineCommand(name: "compare-mibc", value: ref command, help: "Compare two .mibc files"); - if (compareMibcCommand.IsActive) - { - HelpArgs = new[] { "compare-mibc", "--input", "first.mibc", "--input", "second.mibc" }; - CompareMibc = DefineFileOptionList(name: "i|input", help: "The input .mibc files to be compared. Specify as --input file1.mibc --input file2.mibc"); - if (CompareMibc.Count != 2) - Help = true; - - syntax.DefineOption(name: "dump-worst-overlap-graphs", value: ref DumpWorstOverlapGraphs, help: "Number of graphs to dump to .dot format in dump-worst-overlap-graphs-to directory"); - string dumpWorstOverlapGraphsTo = null; - syntax.DefineOption(name: "dump-worst-overlap-graphs-to", value: ref dumpWorstOverlapGraphsTo, help: "Number of graphs to dump to .dot format in dump-worst-overlap-graphs-to directory"); - if (dumpWorstOverlapGraphsTo != null) - DumpWorstOverlapGraphsTo = new DirectoryInfo(dumpWorstOverlapGraphsTo); - } - - if (syntax.ActiveCommand == null) - { - // No command specified - Help = true; - } - - if (activeCommandIsCommandAssociatedWithTraceProcessing) - { - HelpText = -@$"{syntax.GetHelpText()} -Example tracing commands used to generate the input to this tool: -""dotnet-trace collect -p 73060 --providers Microsoft-Windows-DotNETRuntime:0x1E000080018:4"" - - Capture events from process 73060 where we capture both JIT and R2R events using EventPipe tracing - -""dotnet-trace collect -p 73060 --providers Microsoft-Windows-DotNETRuntime:0x1C000080018:4"" - - Capture events from process 73060 where we capture only JIT events using EventPipe tracing - -""perfview collect -LogFile:logOfCollection.txt -DataFile:jittrace.etl -Zip:false -merge:false -providers:Microsoft-Windows-DotNETRuntime:0x1E000080018:4"" - - Capture Jit and R2R events via perfview of all processes running using ETW tracing -"; - } - else - { - HelpText = syntax.GetHelpText(); - } - - List DefineFileOptionList(string name, string help) - { - IReadOnlyList filesAsStrings = null; - syntax.DefineOptionList(name: name, value: ref filesAsStrings, help: help, requireValue: true); - List referenceList = new List(); - if (filesAsStrings != null) - { - foreach (string pattern in filesAsStrings) - { - Dictionary paths = new Dictionary(StringComparer.OrdinalIgnoreCase); - Helpers.AppendExpandedPaths(paths, pattern, false); - foreach (string file in paths.Values) - referenceList.Add(new FileInfo(file)); - } - } - return referenceList; - } - } - - public static CommandLineOptions ParseCommandLine(string[] args) - { - CommandLineOptions realCommandLineParse = new CommandLineOptions(); - try - { - realCommandLineParse.ParseCommmandLineHelper(args); - return realCommandLineParse; - } - catch (ArgumentSyntaxException e) - { - ConsoleColor oldColor = Console.ForegroundColor; - Console.ForegroundColor = ConsoleColor.Red; - Console.Error.WriteLine(Internal.CommandLine.Strings.ErrorWithMessageFmt, e.Message); - Console.ForegroundColor = oldColor; - - CommandLineOptions helpParse = new CommandLineOptions(); - try - { - helpParse.ParseCommmandLineHelper(realCommandLineParse.HelpArgs); - } - catch (ArgumentSyntaxException) - { - // This exists to allow the command list help to work - } - Debug.Assert(helpParse.Help == true); - return helpParse; - } - } - - private CommandLineOptions() - { - } - - private void ParseCommmandLineHelper(string[] args) - { - ArgumentSyntax argSyntax = ArgumentSyntax.Parse(args, DefineArgumentSyntax); - if (Help || (!FileType.HasValue && (InputFilesToMerge == null) && !DumpMibc && CompareMibc == null)) - { - Help = true; - } - } - } -} diff --git a/src/coreclr/tools/dotnet-pgo/PgoRootCommand.cs b/src/coreclr/tools/dotnet-pgo/PgoRootCommand.cs new file mode 100644 index 00000000000000..e7d5ec6eb7594c --- /dev/null +++ b/src/coreclr/tools/dotnet-pgo/PgoRootCommand.cs @@ -0,0 +1,297 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.CommandLine; +using System.CommandLine.Help; +using System.CommandLine.Invocation; +using System.CommandLine.Parsing; +using System.Diagnostics; +using System.IO; +using System.Reflection; + +namespace Microsoft.Diagnostics.Tools.Pgo +{ + internal sealed class PgoRootCommand : RootCommand + { + public Option> InputFilesToMerge { get; } = + new(new[] { "--input", "-i" }, result => Helpers.BuildPathList(result.Tokens), false, "Input .mibc files to be merged. Multiple input arguments are specified as --input file1.mibc --input file2.mibc") { IsRequired = true, Arity = ArgumentArity.OneOrMore }; + public Option InputFilesToCompare { get; } = + new(new[] { "--input", "-i" }, "The input .mibc files to be compared. Specify as --input file1.mibc --input file2.mibc") { IsRequired = true, Arity = new ArgumentArity(2, 2) /* exactly two */ }; + public Option InputFileToDump { get; } = + new(new[] { "--input", "-i" }, "Name of the input mibc file to dump") { IsRequired = true, Arity = ArgumentArity.ExactlyOne }; + public Option TraceFilePath { get; } = + new(new[] { "--trace", "-t" }, "Specify the trace file to be parsed"); + public Option OutputFilePath { get; } = + new(new[] { "--output", "-o" }, "Specify the output filename to be created"); + public Option PreciseDebugInfoFile { get; } = + new(new[] { "--precise-debug-info-file" }, "Name of file of newline separated JSON objects containing precise debug info"); + public Option Pid { get; } = + new(new[] { "--pid" }, "The pid within the trace of the process to examine. If this is a multi-process trace, at least one of --pid or --process-name must be specified"); + public Option ProcessName { get; } = + new(new[] { "--process-name" }, "The process name within the trace of the process to examine. If this is a multi-process trace, at least one of --pid or --process-name must be specified"); + public Option> Reference = + new(new[] { "--reference", "-r" }, result => Helpers.BuildPathList(result.Tokens), true, "If a reference is not located on disk at the same location as used in the process, it may be specified with a --reference parameter. Multiple --reference parameters may be specified. The wild cards * and ? are supported by this option"); + public Option ClrInstanceId { get; } = + new("--clr-instance-id", "If the process contains multiple .NET runtimes, the instance ID must be specified"); + public Option Spgo { get; } = + new("--spgo", "Base profile on samples in the input. Uses last branch records if available and otherwise raw IP samples"); + public Option SpgoMinSamples { get; } = + new("--spgo-min-samples", () => 50, "The minimum number of total samples a function must have before generating profile data for it with SPGO. Default: 50"); + public Option IncludeFullGraphs { get; } = + new("--include-full-graphs", "Include all blocks and edges in the written .mibc file, regardless of profile counts"); + public Option ExcludeEventsBefore { get; } = + new("--exclude-events-before", () => Double.MinValue, "Exclude data from events before specified time. Time is specified as milliseconds from the start of the trace"); + public Option ExcludeEventsAfter { get; } = + new("--exclude-events-after", () => Double.MaxValue, "Exclude data from events after specified time. Time is specified as milliseconds from the start of the trace"); + public Option Compressed { get; } = + new("--compressed", () => true, "Generate compressed mibc"); + public Option DumpWorstOverlapGraphs { get; } = + new("--dump-worst-overlap-graphs", () => -1, "Number of graphs to dump to .dot format in dump-worst-overlap-graphs-to directory"); + public Option DumpWorstOverlapGraphsTo { get; } = + new("--dump-worst-overlap-graphs-to", "Number of graphs to dump to .dot format in dump-worst-overlap-graphs-to directory"); + public Option InheritTimestamp { get; } = + new("--inherit-timestamp", "If specified, set the output's timestamp to the max timestamp of the input files"); + public Option AutomaticReferences { get; } = + new("--automatic-references", () => true, "Attempt to find references by using paths embedded in the trace file. Defaults to true"); + public Option IncludedAssemblies { get; } = + new("--include-reference", result => + { + if (result.Tokens.Count > 0) + { + var includedAssemblies = new List(); + foreach (Token token in result.Tokens) + { + try + { + includedAssemblies.Add(new AssemblyName(token.Value)); + } + catch + { + throw new FormatException($"Unable to parse '{token.Value}' as an Assembly Name."); + } + } + } + + return Array.Empty(); + }, true, "If specified, include in Mibc file only references to the specified assemblies. Assemblies are specified as assembly names, not filenames. For instance, `System.Private.CoreLib` not `System.Private.CoreLib.dll`. Multiple --include-reference options may be specified."); + + private Option _includeReadyToRun { get; } = + new("--includeReadyToRun", "Include ReadyToRun methods in the trace file"); + private Option _verbosity { get; } = + new(new[] { "--verbose", "-v" }, () => Verbosity.normal, "Adjust verbosity level. Supported levels are minimal, normal, detailed, and diagnostic"); + private Option _isSorted { get; } = + new("--sorted", "Generate sorted output."); + private Option _showTimestamp { get; } = + new("--showtimestamp", "Show timestamps in output"); + + public PgoFileType? FileType; + public bool ProcessJitEvents; + public bool DisplayProcessedEvents; + public bool ValidateOutputFile; + public bool GenerateCallGraph; + public bool VerboseWarnings; + public JitTraceOptions JitTraceOptions; + public bool Warnings; + public bool BasicProgressMessages; + public bool DetailedProgressMessages; + public bool DumpMibc; + public ParseResult Result; + public bool ProcessR2REvents; + + private enum Verbosity + { + minimal, + normal, + detailed, + diagnostic + } + + public PgoRootCommand(string[] args) : base(".NET PGO Tool") + { + Command createMbicCommand = new("create-mibc", "Transform a trace file into a Mibc profile data file") + { + TraceFilePath, + OutputFilePath, + Pid, + ProcessName, + Reference, + ClrInstanceId, + ExcludeEventsBefore, + ExcludeEventsAfter, + AutomaticReferences, + _verbosity, + Compressed, + PreciseDebugInfoFile, + Spgo, + SpgoMinSamples, + IncludeFullGraphs + }; + + createMbicCommand.SetHandler(context => + { + FileType = PgoFileType.mibc; + GenerateCallGraph = true; + ProcessJitEvents = true; + ProcessR2REvents = true; +#if DEBUG + ValidateOutputFile = true; +#else + ValidateOutputFile = false; +#endif + + TryExecuteWithContext(context, true); + }); + + AddCommand(createMbicCommand); + + JitTraceOptions = JitTraceOptions.none; +#if DEBUG + Command createJitTraceCommand = new("create-jittrace","Transform a trace file into a jittrace runtime file") + { + TraceFilePath, + OutputFilePath, + Pid, + ProcessName, + Reference, + ClrInstanceId, + ExcludeEventsBefore, + ExcludeEventsAfter, + AutomaticReferences, + _verbosity, + _isSorted, + _showTimestamp, + _includeReadyToRun + }; + + createJitTraceCommand.SetHandler(context => + { + FileType = PgoFileType.jittrace; + ProcessJitEvents = true; + ValidateOutputFile = false; + ProcessR2REvents = context.ParseResult.GetValueForOption(_includeReadyToRun); + + if (context.ParseResult.GetValueForOption(_isSorted)) + { + JitTraceOptions |= JitTraceOptions.sorted; + } + + if (context.ParseResult.GetValueForOption(_showTimestamp)) + { + JitTraceOptions |= JitTraceOptions.showtimestamp; + } + + TryExecuteWithContext(context, true); + }); + + AddCommand(createJitTraceCommand); +#endif + + Command mergeCommand = new("merge", "Merge multiple Mibc profile data files into one file") + { + InputFilesToMerge, + OutputFilePath, + IncludedAssemblies, + InheritTimestamp, + _verbosity, + Compressed + }; + + mergeCommand.SetHandler(context => + { +#if DEBUG + ValidateOutputFile = true; +#else + ValidateOutputFile = false; +#endif + + TryExecuteWithContext(context, true); + }); + + AddCommand(mergeCommand); + + Command dumpCommand = new("dump", "Dump the contents of a Mibc file") + { + _verbosity, + InputFileToDump, + OutputFilePath, + }; + + dumpCommand.SetHandler(context => TryExecuteWithContext(context, true)); + + AddCommand(dumpCommand); + + Command compareMbicCommand = new Command("compare-mibc", "Compare two .mibc files") + { + InputFilesToCompare, + DumpWorstOverlapGraphs, + DumpWorstOverlapGraphsTo + }; + + compareMbicCommand.SetHandler(context => + { + DumpMibc = true; + TryExecuteWithContext(context, false); + }); + + AddCommand(compareMbicCommand); + + void TryExecuteWithContext(InvocationContext context, bool setVerbosity) + { + Result = context.ParseResult; + + if (setVerbosity) + { + Verbosity verbosity = context.ParseResult.GetValueForOption(_verbosity); + BasicProgressMessages = (int)verbosity >= (int)Verbosity.normal; + Warnings = (int)verbosity >= (int)Verbosity.normal; + VerboseWarnings = (int)verbosity >= (int)Verbosity.detailed; + DetailedProgressMessages = (int)verbosity >= (int)Verbosity.detailed; + DisplayProcessedEvents = (int)verbosity >= (int)Verbosity.diagnostic; + } + + try + { + context.ExitCode = new Program(this).Run(); + } + catch (Exception e) + { + Console.ResetColor(); + Console.ForegroundColor = ConsoleColor.Red; + + Console.Error.WriteLine("Error: " + e.Message); + Console.Error.WriteLine(e.ToString()); + + Console.ResetColor(); + + context.ExitCode = 1; + } + } + } + + public static IEnumerable GetExtendedHelp(HelpContext context) + { + foreach (HelpSectionDelegate sectionDelegate in HelpBuilder.Default.GetLayout()) + yield return sectionDelegate; + + if (context.Command.Name == "create-mibc" || context.Command.Name == "create-jittrace") + { + yield return _ => + { + Console.WriteLine( +@"Example tracing commands used to generate the input to this tool: +""dotnet-trace collect -p 73060 --providers Microsoft-Windows-DotNETRuntime:0x1E000080018:4"" +- Capture events from process 73060 where we capture both JIT and R2R events using EventPipe tracing + +""dotnet-trace collect -p 73060 --providers Microsoft-Windows-DotNETRuntime:0x1C000080018:4"" +- Capture events from process 73060 where we capture only JIT events using EventPipe tracing + +""perfview collect -LogFile:logOfCollection.txt -DataFile:jittrace.etl -Zip:false -merge:false -providers:Microsoft-Windows-DotNETRuntime:0x1E000080018:4"" +- Capture Jit and R2R events via perfview of all processes running using ETW tracing +"); + }; + } + } + } +} diff --git a/src/coreclr/tools/dotnet-pgo/Program.cs b/src/coreclr/tools/dotnet-pgo/Program.cs index 711931f28ff168..b8d76cad03ae86 100644 --- a/src/coreclr/tools/dotnet-pgo/Program.cs +++ b/src/coreclr/tools/dotnet-pgo/Program.cs @@ -1,36 +1,42 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Internal.TypeSystem; -using Internal.TypeSystem.Ecma; -using Internal.IL; -using Microsoft.Diagnostics.Tracing; -using Microsoft.Diagnostics.Tracing.Etlx; -using Microsoft.Diagnostics.Tracing.Parsers.Clr; using System; using System.Buffers.Binary; using System.Collections.Generic; -using System.IO; -using System.Reflection; -using System.Text; -using System.Linq; +using System.CommandLine; +using System.CommandLine.Help; +using System.CommandLine.Parsing; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Globalization; -using System.Threading.Tasks; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Reflection; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; -using System.IO.Compression; -using Microsoft.Diagnostics.Tracing.Parsers.Kernel; -using System.Diagnostics.CodeAnalysis; -using ILCompiler.Reflection.ReadyToRun; -using Microsoft.Diagnostics.Tools.Pgo; -using Internal.Pgo; using System.Reflection.PortableExecutable; -using ILCompiler.IBC; -using ILCompiler; using System.Runtime.Serialization.Json; +using System.Text; using System.Text.Json; using System.Text.Encodings.Web; +using System.Threading.Tasks; + +using Microsoft.Diagnostics.Tools.Pgo; +using Microsoft.Diagnostics.Tracing; +using Microsoft.Diagnostics.Tracing.Etlx; +using Microsoft.Diagnostics.Tracing.Parsers.Clr; +using Microsoft.Diagnostics.Tracing.Parsers.Kernel; + +using ILCompiler; +using ILCompiler.IBC; +using ILCompiler.Reflection.ReadyToRun; + +using Internal.IL; +using Internal.Pgo; +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; namespace Microsoft.Diagnostics.Tools.Pgo { @@ -133,34 +139,35 @@ public override string ToString() } } - - class Program + internal sealed class Program { - static Logger s_logger = new Logger(); - static int Main(string[] args) - { - var options = CommandLineOptions.ParseCommandLine(args); + private static Logger s_logger = new Logger(); - if (options.Help) - { - PrintOutput(options.HelpText); - return 1; - } - else - { - return InnerMain(options); - } - } + private readonly PgoRootCommand _command; + private List _inputFilesToMerge; + private string[] _inputFilesToCompare; - static void PrintUsage(CommandLineOptions commandLineOptions, string argValidationIssue) + public Program(PgoRootCommand command) { - if (argValidationIssue != null) - { - PrintError(argValidationIssue); - } - Main(commandLineOptions.HelpArgs); + _command = command; + + _inputFilesToMerge = Get(command.InputFilesToMerge); + _inputFilesToCompare = Get(command.InputFilesToCompare); } + private T Get(Option option) => _command.Result.GetValueForOption(option); + private T Get(Argument option) => _command.Result.GetValueForArgument(option); + private bool IsSet(Option option) => _command.Result.FindResultFor(option) != null; + + private static int Main(string[] args) => + new CommandLineBuilder(new PgoRootCommand(args)) + .UseTokenReplacer(Helpers.TryReadResponseFile) + .UseVersionOption("-v") + .UseHelp(context => context.HelpBuilder.CustomizeLayout(PgoRootCommand.GetExtendedHelp)) + .UseParseErrorReporting() + .Build() + .Invoke(args); + public static void PrintWarning(string warning) { s_logger.PrintWarning(warning); @@ -233,53 +240,54 @@ internal static void UnZipIfNecessary(ref string inputFileName, TextWriter log) } } - static int InnerMain(CommandLineOptions commandLineOptions) + public int Run() { - if (!commandLineOptions.BasicProgressMessages) + if (!_command.BasicProgressMessages) + { s_logger.HideMessages(); + } - if (!commandLineOptions.DetailedProgressMessages) + if (!_command.DetailedProgressMessages) + { s_logger.HideDetailedMessages(); + } - if (commandLineOptions.DumpMibc) + if (_command.DumpMibc) { - return InnerDumpMain(commandLineOptions); + return InnerDumpMain(); } - if (commandLineOptions.InputFilesToMerge != null) + if (_inputFilesToMerge != null) { - return InnerMergeMain(commandLineOptions); + return InnerMergeMain(); } - if (commandLineOptions.CompareMibc != null) + if (_inputFilesToCompare != null) { - return InnerCompareMibcMain(commandLineOptions); + return InnerCompareMibcMain(); } - return InnerProcessTraceFileMain(commandLineOptions); + return InnerProcessTraceFileMain(); } - static int InnerDumpMain(CommandLineOptions commandLineOptions) + private int InnerDumpMain() { - if ((commandLineOptions.InputFileToDump == null) || (!commandLineOptions.InputFileToDump.Exists)) + string outputPath = Get(_command.OutputFilePath); + if (outputPath == null) { - PrintUsage(commandLineOptions, "Valid input file must be specified"); + PrintError("Output filename must be specified"); return -8; } - if (commandLineOptions.OutputFileName == null) - { - PrintUsage(commandLineOptions, "Output filename must be specified"); - return -8; - } + string path = Get(_command.InputFileToDump); - PrintDetailedMessage($"Opening {commandLineOptions.InputFileToDump}"); - var mibcPeReader = MIbcProfileParser.OpenMibcAsPEReader(commandLineOptions.InputFileToDump.FullName); + PrintDetailedMessage($"Opening {path}"); + var mibcPeReader = MIbcProfileParser.OpenMibcAsPEReader(path); var tsc = new TypeRefTypeSystem.TypeRefTypeSystemContext(new PEReader[] { mibcPeReader }); - PrintDetailedMessage($"Parsing {commandLineOptions.InputFileToDump}"); + PrintDetailedMessage($"Parsing {path}"); var profileData = MIbcProfileParser.ParseMIbcFile(tsc, mibcPeReader, null, onlyDefinedInAssembly: null); PrintMibcStats(profileData); - using (FileStream outputFile = new FileStream(commandLineOptions.OutputFileName.FullName, FileMode.Create, FileAccess.Write)) + using (FileStream outputFile = new FileStream(outputPath, FileMode.Create, FileAccess.Write)) { JsonWriterOptions options = new JsonWriterOptions(); options.Indented = true; @@ -350,12 +358,12 @@ static int InnerDumpMain(CommandLineOptions commandLineOptions) jsonWriter.WriteEndArray(); jsonWriter.WriteEndObject(); } - PrintMessage($"Generated {commandLineOptions.OutputFileName}"); + PrintMessage($"Generated {outputPath}"); return 0; } - static MibcConfig ParseMibcConfigsAndMerge(TypeSystemContext tsc, params PEReader[] pEReader) + private static MibcConfig ParseMibcConfigsAndMerge(TypeSystemContext tsc, params PEReader[] pEReader) { MibcConfig firstCfg = null; foreach (PEReader peReader in pEReader) @@ -388,32 +396,29 @@ static MibcConfig ParseMibcConfigsAndMerge(TypeSystemContext tsc, params PEReade return firstCfg; } - static int InnerMergeMain(CommandLineOptions commandLineOptions) + private int InnerMergeMain() { - if (commandLineOptions.InputFilesToMerge.Count == 0) + string outputPath = Get(_command.OutputFilePath); + if (outputPath == null) { - PrintUsage(commandLineOptions, "--input must be specified"); + PrintError("Output filename must be specified"); return -8; } - if (commandLineOptions.OutputFileName == null) - { - PrintUsage(commandLineOptions, "--output must be specified"); - return -8; - } - - PEReader[] mibcReaders = new PEReader[commandLineOptions.InputFilesToMerge.Count]; + var paths = _inputFilesToMerge; + PEReader[] mibcReaders = new PEReader[paths.Count]; for (int i = 0; i < mibcReaders.Length; i++) { - PrintMessage($"Opening {commandLineOptions.InputFilesToMerge[i].FullName}"); - mibcReaders[i] = MIbcProfileParser.OpenMibcAsPEReader(commandLineOptions.InputFilesToMerge[i].FullName); + PrintMessage($"Opening {paths[i]}"); + mibcReaders[i] = MIbcProfileParser.OpenMibcAsPEReader(paths[i]); } HashSet assemblyNamesInBubble = null; - if (commandLineOptions.IncludedAssemblies.Count > 0) + AssemblyName[] assemblies = Get(_command.IncludedAssemblies); + if (assemblies.Length > 0) { assemblyNamesInBubble = new HashSet(); - foreach (var asmName in commandLineOptions.IncludedAssemblies) + foreach (var asmName in assemblies) { assemblyNamesInBubble.Add(asmName.Name); } @@ -428,16 +433,17 @@ static int InnerMergeMain(CommandLineOptions commandLineOptions) for (int i = 0; i < mibcReaders.Length; i++) { var peReader = mibcReaders[i]; - PrintDetailedMessage($"Merging {commandLineOptions.InputFilesToMerge[i].FullName}"); + PrintDetailedMessage($"Merging {paths[i]}"); ProfileData.MergeProfileData(ref partialNgen, mergedProfileData, MIbcProfileParser.ParseMIbcFile(tsc, peReader, assemblyNamesInBubble, onlyDefinedInAssembly: null)); } MibcConfig mergedConfig = ParseMibcConfigsAndMerge(tsc, mibcReaders); - int result = MibcEmitter.GenerateMibcFile(mergedConfig, tsc, commandLineOptions.OutputFileName, mergedProfileData.Values, commandLineOptions.ValidateOutputFile, commandLineOptions.Uncompressed); - if (result == 0 && commandLineOptions.InheritTimestamp) + var outputFileInfo = new FileInfo(outputPath); + int result = MibcEmitter.GenerateMibcFile(mergedConfig, tsc, outputFileInfo, mergedProfileData.Values, _command.ValidateOutputFile, !Get(_command.Compressed)); + if (result == 0 && Get(_command.InheritTimestamp)) { - commandLineOptions.OutputFileName.CreationTimeUtc = commandLineOptions.InputFilesToMerge.Max(fi => fi.CreationTimeUtc); - commandLineOptions.OutputFileName.LastWriteTimeUtc = commandLineOptions.InputFilesToMerge.Max(fi => fi.LastWriteTimeUtc); + outputFileInfo.CreationTimeUtc = paths.Max(f => new FileInfo(f).CreationTimeUtc); + outputFileInfo.LastWriteTimeUtc = paths.Max(f => new FileInfo(f).LastWriteTimeUtc); } return result; @@ -451,18 +457,18 @@ static int InnerMergeMain(CommandLineOptions commandLineOptions) } } - static int InnerCompareMibcMain(CommandLineOptions options) + private int InnerCompareMibcMain() { // Command line parser should require exactly 2 files - Trace.Assert(options.CompareMibc?.Count == 2); - FileInfo file1 = options.CompareMibc[0]; - FileInfo file2 = options.CompareMibc[1]; + Trace.Assert(_inputFilesToCompare.Length == 2); + string file1 = _inputFilesToCompare[0]; + string file2 = _inputFilesToCompare[1]; // Look for the shortest unique names for the input files. - string name1 = file1.Name; - string name2 = file2.Name; - string path1 = Path.GetDirectoryName(file1.FullName); - string path2 = Path.GetDirectoryName(file2.FullName); + string name1 = Path.GetFileName(file1); + string name2 = Path.GetFileName(file2); + string path1 = Path.GetDirectoryName(file1); + string path2 = Path.GetDirectoryName(file2); while (name1 == name2) { name1 = Path.Combine(Path.GetFileName(path1), name1); @@ -471,8 +477,8 @@ static int InnerCompareMibcMain(CommandLineOptions options) path2 = Path.GetDirectoryName(path2); } - PEReader mibc1 = MIbcProfileParser.OpenMibcAsPEReader(file1.FullName); - PEReader mibc2 = MIbcProfileParser.OpenMibcAsPEReader(file2.FullName); + PEReader mibc1 = MIbcProfileParser.OpenMibcAsPEReader(file1); + PEReader mibc2 = MIbcProfileParser.OpenMibcAsPEReader(file2); var tsc = new TypeRefTypeSystem.TypeRefTypeSystemContext(new PEReader[] { mibc1, mibc2 }); ProfileData profile1 = MIbcProfileParser.ParseMIbcFile(tsc, mibc1, null, onlyDefinedInAssembly: null); @@ -676,10 +682,12 @@ string FormatDevirt(GetLikelyClassResult result) } } - if (options.DumpWorstOverlapGraphsTo != null) + string dumpWorstOverlapGraphsTo = Get(_command.DumpWorstOverlapGraphsTo); + if (dumpWorstOverlapGraphsTo != null) { + int dumpWorstOverlapGraphs = Get(_command.DumpWorstOverlapGraphs); IEnumerable toDump; - if (options.DumpWorstOverlapGraphs == -1) + if (dumpWorstOverlapGraphs == -1) { // Take all with less than 0.5 overlap in order. toDump = @@ -700,7 +708,7 @@ string FormatDevirt(GetLikelyClassResult result) .Select(g => (Method: g.Key, Overlap: g.Select(t => t.Overlap).Min())) .OrderBy(t => t.Overlap) .Select(t => t.Method) - .Take(options.DumpWorstOverlapGraphs); + .Take(dumpWorstOverlapGraphs); } foreach (MethodDesc method in toDump) @@ -725,7 +733,7 @@ string FormatDevirt(GetLikelyClassResult result) foreach (char c in Path.GetInvalidFileNameChars()) fileName = fileName.Replace(c, '_'); - File.WriteAllText(Path.Combine(options.DumpWorstOverlapGraphsTo.FullName, fileName + ".dot"), dot); + File.WriteAllText(Path.Combine(dumpWorstOverlapGraphsTo, fileName + ".dot"), dot); } } } @@ -738,7 +746,7 @@ string FormatDevirt(GetLikelyClassResult result) /// /// Each array item represents a unique call-site, and the actual value is /// the number of unique classes seen at that call-site - static void PrintCallsitesByLikelyClassesChart(int[] callSites) + private static void PrintCallsitesByLikelyClassesChart(int[] callSites) { const int maxLikelyClasses = 10; const int tableWidth = 20; @@ -790,7 +798,7 @@ static void PrintCallsitesByLikelyClassesChart(int[] callSites) /// Prints a histogram for "likelihoods" distribution for a specific likely class (e.g. most popular one) /// /// Array of likelihoods 0-100.0 - static void PrintLikelihoodHistogram(double[] likelihoods) + private static void PrintLikelihoodHistogram(double[] likelihoods) { const int columns = 10; const int tableWidth = 20; @@ -826,7 +834,7 @@ static void PrintLikelihoodHistogram(double[] likelihoods) Console.WriteLine(); } - static void PrintMibcStats(ProfileData data) + private static void PrintMibcStats(ProfileData data) { PrintOutput(data.Config?.ToString()); List methods = data.GetAllMethodProfileData().ToList(); @@ -982,67 +990,57 @@ static bool IsUnknownTypeHandle(int handle) return new GetLikelyClassResult { IsNull = true }; } - static int InnerProcessTraceFileMain(CommandLineOptions commandLineOptions) + private int InnerProcessTraceFileMain() { - if (commandLineOptions.TraceFile == null) + string outputPath = Get(_command.OutputFilePath); + if (outputPath == null) { - PrintUsage(commandLineOptions, "--trace must be specified"); + PrintError("Output filename must be specified"); return -8; } - if (commandLineOptions.OutputFileName == null) + if ((_command.FileType.Value != PgoFileType.jittrace) && (_command.FileType != PgoFileType.mibc)) { - PrintUsage(commandLineOptions, "--output must be specified"); - return -8; - } - - if (!commandLineOptions.FileType.HasValue) - { - PrintUsage(commandLineOptions, $"--pgo-file-type must be specified"); - return -9; - } - if ((commandLineOptions.FileType.Value != PgoFileType.jittrace) && (commandLineOptions.FileType != PgoFileType.mibc)) - { - PrintUsage(commandLineOptions, $"Invalid output pgo type {commandLineOptions.FileType} specified."); + PrintError($"Invalid output pgo type {_command.FileType} specified."); return -9; } - if (commandLineOptions.FileType == PgoFileType.jittrace) + if (_command.FileType == PgoFileType.jittrace) { - if (!commandLineOptions.OutputFileName.Name.EndsWith(".jittrace")) + if (!outputPath.EndsWith(".jittrace")) { - PrintUsage(commandLineOptions, $"jittrace output file name must end with .jittrace"); + PrintError($"jittrace output file name must end with .jittrace"); return -9; } } - if (commandLineOptions.FileType == PgoFileType.mibc) + if (_command.FileType == PgoFileType.mibc) { - if (!commandLineOptions.OutputFileName.Name.EndsWith(".mibc")) + if (!outputPath.EndsWith(".mibc")) { - PrintUsage(commandLineOptions, $"mibc output file name must end with .mibc"); + PrintError($"mibc output file name must end with .mibc"); return -9; } } - string etlFileName = commandLineOptions.TraceFile.FullName; + string etlFileName = Get(_command.TraceFilePath); foreach (string nettraceExtension in new string[] { ".netperf", ".netperf.zip", ".nettrace" }) { - if (commandLineOptions.TraceFile.FullName.EndsWith(nettraceExtension)) + if (etlFileName.EndsWith(nettraceExtension)) { - etlFileName = Path.ChangeExtension(commandLineOptions.TraceFile.FullName, ".etlx"); - PrintMessage($"Creating ETLX file {etlFileName} from {commandLineOptions.TraceFile.FullName}"); - TraceLog.CreateFromEventPipeDataFile(commandLineOptions.TraceFile.FullName, etlFileName); + etlFileName = Path.ChangeExtension(etlFileName, ".etlx"); + PrintError($"Creating ETLX file {etlFileName} from {etlFileName}"); + TraceLog.CreateFromEventPipeDataFile(etlFileName, etlFileName); } } string lttngExtension = ".trace.zip"; - if (commandLineOptions.TraceFile.FullName.EndsWith(lttngExtension)) + if (etlFileName.EndsWith(lttngExtension)) { - etlFileName = Path.ChangeExtension(commandLineOptions.TraceFile.FullName, ".etlx"); - PrintMessage($"Creating ETLX file {etlFileName} from {commandLineOptions.TraceFile.FullName}"); - TraceLog.CreateFromLttngTextDataFile(commandLineOptions.TraceFile.FullName, etlFileName); + etlFileName = Path.ChangeExtension(etlFileName, ".etlx"); + PrintError($"Creating ETLX file {etlFileName} from {etlFileName}"); + TraceLog.CreateFromLttngTextDataFile(etlFileName, etlFileName); } - UnZipIfNecessary(ref etlFileName, commandLineOptions.BasicProgressMessages ? Console.Out : new StringWriter()); + UnZipIfNecessary(ref etlFileName, _command.BasicProgressMessages ? Console.Out : new StringWriter()); // For SPGO we need to be able to map raw IPs back to IL offsets in methods. // Normally TraceEvent facilitates this remapping automatically and discards the IL<->IP mapping table events. @@ -1052,7 +1050,9 @@ static int InnerProcessTraceFileMain(CommandLineOptions commandLineOptions) // the cached .etlx file will not update. using (var traceLog = TraceLog.OpenOrConvert(etlFileName, new TraceLogOptions { KeepAllEvents = true })) { - if ((!commandLineOptions.Pid.HasValue && commandLineOptions.ProcessName == null) && traceLog.Processes.Count != 1) + bool hasPid = IsSet(_command.Pid); + string processName = Get(_command.ProcessName); + if (hasPid && processName == null && traceLog.Processes.Count != 1) { PrintError("Trace file contains multiple processes to distinguish between"); PrintOutput("Either a pid or process name from the following list must be specified"); @@ -1063,7 +1063,7 @@ static int InnerProcessTraceFileMain(CommandLineOptions commandLineOptions) return 1; } - if (commandLineOptions.Pid.HasValue && (commandLineOptions.ProcessName != null)) + if (hasPid && processName != null) { PrintError("--pid and --process-name cannot be specified together"); return -1; @@ -1071,16 +1071,16 @@ static int InnerProcessTraceFileMain(CommandLineOptions commandLineOptions) // For a particular process TraceProcess p; - if (commandLineOptions.Pid.HasValue) + if (hasPid) { - p = traceLog.Processes.LastProcessWithID(commandLineOptions.Pid.Value); + p = traceLog.Processes.LastProcessWithID(Get(_command.Pid)); } - else if (commandLineOptions.ProcessName != null) + else if (processName != null) { List matchingProcesses = new List(); foreach (TraceProcess proc in traceLog.Processes) { - if (String.Compare(proc.Name, commandLineOptions.ProcessName, StringComparison.OrdinalIgnoreCase) == 0) + if (String.Compare(proc.Name, processName, StringComparison.OrdinalIgnoreCase) == 0) { matchingProcesses.Add(proc); } @@ -1137,8 +1137,8 @@ static int InnerProcessTraceFileMain(CommandLineOptions commandLineOptions) } PgoTraceProcess pgoProcess = new PgoTraceProcess(p); - int? clrInstanceId = commandLineOptions.ClrInstanceId; - if (!clrInstanceId.HasValue) + int clrInstanceId = Get(_command.ClrInstanceId); + if (!IsSet(_command.ClrInstanceId)) { HashSet clrInstanceIds = new HashSet(); HashSet examinedClrInstanceIds = new HashSet(); @@ -1178,39 +1178,36 @@ static int InnerProcessTraceFileMain(CommandLineOptions commandLineOptions) } } - var tsc = new TraceTypeSystemContext(pgoProcess, clrInstanceId.Value, s_logger, commandLineOptions.AutomaticReferences); + var tsc = new TraceTypeSystemContext(pgoProcess, clrInstanceId, s_logger, Get(_command.AutomaticReferences)); - if (commandLineOptions.VerboseWarnings) + if (_command.VerboseWarnings) PrintWarning($"{traceLog.EventsLost} Lost events"); bool filePathError = false; HashSet modulesLoadedViaReference = new HashSet(); - if (commandLineOptions.Reference != null) + foreach (string file in Get(_command.Reference)) { - foreach (FileInfo fileReference in commandLineOptions.Reference) + try { - try + if (!File.Exists(file)) { - if (!File.Exists(fileReference.FullName)) - { - PrintError($"Unable to find reference '{fileReference.FullName}'"); - filePathError = true; - } - else - { - var module = tsc.GetModuleFromPath(fileReference.FullName, throwIfNotLoadable: false); - if (module != null) - modulesLoadedViaReference.Add(module); - } + PrintError($"Unable to find reference '{file}'"); + filePathError = true; } - catch (Internal.TypeSystem.TypeSystemException.BadImageFormatException) + else { - // Ignore BadImageFormat in order to allow users to use '-r *.dll' - // in a folder with native dynamic libraries (which have the same extension on Windows). - - // We don't need to log a warning here - it's already logged in GetModuleFromPath + var module = tsc.GetModuleFromPath(file, throwIfNotLoadable: false); + if (module != null) + modulesLoadedViaReference.Add(module); } } + catch (Internal.TypeSystem.TypeSystemException.BadImageFormatException) + { + // Ignore BadImageFormat in order to allow users to use '-r *.dll' + // in a folder with native dynamic libraries (which have the same extension on Windows). + + // We don't need to log a warning here - it's already logged in GetModuleFromPath + } } if (filePathError) @@ -1224,7 +1221,7 @@ static int InnerProcessTraceFileMain(CommandLineOptions commandLineOptions) { var managedModule = module.ManagedModule; - if (module.ClrInstanceID != clrInstanceId.Value) + if (module.ClrInstanceID != clrInstanceId) continue; if (managedModule.ModuleFile != null) @@ -1263,8 +1260,9 @@ static int InnerProcessTraceFileMain(CommandLineOptions commandLineOptions) break; } } - if ((loadedViaReference == null) - && commandLineOptions.AutomaticReferences) // AutomaticReferences set to false disables this error, as no more references can actually be loaded past this point and cause a problem. + + // AutomaticReferences set to false disables this error, as no more references can actually be loaded past this point and cause a problem. + if (loadedViaReference == null && Get(_command.AutomaticReferences)) { duplicateError = true; PrintError($"Multiple assemblies with the same simple name loaded into the process. Specify the preferred module via the -reference parameter."); @@ -1277,7 +1275,7 @@ static int InnerProcessTraceFileMain(CommandLineOptions commandLineOptions) if (duplicateError) return -13; - TraceRuntimeDescToTypeSystemDesc idParser = new TraceRuntimeDescToTypeSystemDesc(p, tsc, clrInstanceId.Value); + TraceRuntimeDescToTypeSystemDesc idParser = new TraceRuntimeDescToTypeSystemDesc(p, tsc, clrInstanceId); int mismatchErrors = 0; foreach (var e in p.EventsInProcess.ByEventType()) @@ -1349,7 +1347,9 @@ static int InnerProcessTraceFileMain(CommandLineOptions commandLineOptions) SortedDictionary methodsToAttemptToPrepare = new SortedDictionary(); - if (commandLineOptions.ProcessR2REvents) + double excludeEventsBefore = Get(_command.ExcludeEventsBefore); + double excludeEventsAfter = Get(_command.ExcludeEventsAfter); + if (_command.ProcessR2REvents) { foreach (var e in p.EventsInProcess.ByEventType()) { @@ -1357,9 +1357,9 @@ static int InnerProcessTraceFileMain(CommandLineOptions commandLineOptions) string retArg = e.MethodSignature.Substring(0, parenIndex); string paramsArgs = e.MethodSignature.Substring(parenIndex); string methodNameFromEventDirectly = retArg + e.MethodNamespace + "." + e.MethodName + paramsArgs; - if (e.ClrInstanceID != clrInstanceId.Value) + if (e.ClrInstanceID != clrInstanceId) { - if (!commandLineOptions.Warnings) + if (!_command.Warnings) continue; PrintWarning($"Skipped R2REntryPoint {methodNameFromEventDirectly} due to ClrInstanceID of {e.ClrInstanceID}"); @@ -1370,7 +1370,7 @@ static int InnerProcessTraceFileMain(CommandLineOptions commandLineOptions) bool failedDueToNonloadableModule = false; try { - method = idParser.ResolveMethodID(e.MethodID, out failedDueToNonloadableModule, commandLineOptions.VerboseWarnings); + method = idParser.ResolveMethodID(e.MethodID, out failedDueToNonloadableModule, _command.VerboseWarnings); } catch (Exception exception) { @@ -1379,7 +1379,7 @@ static int InnerProcessTraceFileMain(CommandLineOptions commandLineOptions) if (method == null) { - if ((e.MethodNamespace == "dynamicClass") || failedDueToNonloadableModule || !commandLineOptions.Warnings) + if ((e.MethodNamespace == "dynamicClass") || failedDueToNonloadableModule || !_command.Warnings) continue; PrintWarning($"Unable to parse {methodNameFromEventDirectly} when looking up R2R methods"); @@ -1388,13 +1388,13 @@ static int InnerProcessTraceFileMain(CommandLineOptions commandLineOptions) continue; } - if ((e.TimeStampRelativeMSec >= commandLineOptions.ExcludeEventsBefore) && (e.TimeStampRelativeMSec <= commandLineOptions.ExcludeEventsAfter)) + if ((e.TimeStampRelativeMSec >= excludeEventsBefore) && (e.TimeStampRelativeMSec <= excludeEventsAfter)) methodsToAttemptToPrepare.Add((int)e.EventIndex, new ProcessedMethodData(e.TimeStampRelativeMSec, method, "R2RLoad")); } } // Find all the jitStart events. - if (commandLineOptions.ProcessJitEvents) + if (_command.ProcessJitEvents) { foreach (var e in p.EventsInProcess.ByEventType()) { @@ -1402,9 +1402,9 @@ static int InnerProcessTraceFileMain(CommandLineOptions commandLineOptions) string retArg = e.MethodSignature.Substring(0, parenIndex); string paramsArgs = e.MethodSignature.Substring(parenIndex); string methodNameFromEventDirectly = retArg + e.MethodNamespace + "." + e.MethodName + paramsArgs; - if (e.ClrInstanceID != clrInstanceId.Value) + if (e.ClrInstanceID != clrInstanceId) { - if (!commandLineOptions.Warnings) + if (!_command.Warnings) continue; PrintWarning($"Skipped {methodNameFromEventDirectly} due to ClrInstanceID of {e.ClrInstanceID}"); @@ -1416,7 +1416,7 @@ static int InnerProcessTraceFileMain(CommandLineOptions commandLineOptions) bool failedDueToNonloadableModule = false; try { - method = idParser.ResolveMethodID(e.MethodID, out failedDueToNonloadableModule, commandLineOptions.VerboseWarnings); + method = idParser.ResolveMethodID(e.MethodID, out failedDueToNonloadableModule, _command.VerboseWarnings); } catch (Exception exception) { @@ -1425,7 +1425,7 @@ static int InnerProcessTraceFileMain(CommandLineOptions commandLineOptions) if (method == null) { - if ((e.MethodNamespace == "dynamicClass") || failedDueToNonloadableModule || !commandLineOptions.Warnings) + if ((e.MethodNamespace == "dynamicClass") || failedDueToNonloadableModule || !_command.Warnings) continue; PrintWarning($"Unable to parse {methodNameFromEventDirectly}"); @@ -1434,7 +1434,7 @@ static int InnerProcessTraceFileMain(CommandLineOptions commandLineOptions) continue; } - if ((e.TimeStampRelativeMSec >= commandLineOptions.ExcludeEventsBefore) && (e.TimeStampRelativeMSec <= commandLineOptions.ExcludeEventsAfter)) + if ((e.TimeStampRelativeMSec >= excludeEventsBefore) && (e.TimeStampRelativeMSec <= excludeEventsAfter)) methodsToAttemptToPrepare.Add((int)e.EventIndex, new ProcessedMethodData(e.TimeStampRelativeMSec, method, "JitStart")); } } @@ -1448,8 +1448,8 @@ MethodMemoryMap GetMethodMemMap() p, tsc, idParser, - clrInstanceId.Value, - commandLineOptions.PreciseDebugInfoFile, + clrInstanceId, + new FileInfo(Get(_command.PreciseDebugInfoFile)), s_logger); } @@ -1458,7 +1458,7 @@ MethodMemoryMap GetMethodMemMap() Dictionary> callGraph = null; Dictionary exclusiveSamples = null; - if (commandLineOptions.GenerateCallGraph) + if (_command.GenerateCallGraph) { HashSet methodsListedToPrepare = new HashSet(); foreach (var entry in methodsToAttemptToPrepare) @@ -1472,7 +1472,7 @@ MethodMemoryMap GetMethodMemMap() MethodMemoryMap mmap = GetMethodMemMap(); foreach (var e in p.EventsInProcess.ByEventType()) { - if ((e.TimeStampRelativeMSec < commandLineOptions.ExcludeEventsBefore) && (e.TimeStampRelativeMSec > commandLineOptions.ExcludeEventsAfter)) + if ((e.TimeStampRelativeMSec < excludeEventsBefore) && (e.TimeStampRelativeMSec > excludeEventsAfter)) continue; var callstack = e.CallStack(); @@ -1545,7 +1545,7 @@ MethodMemoryMap GetMethodMemMap() // Local function used with the above two loops as the behavior is supposed to be identical void AddToInstrumentationData(int eventClrInstanceId, long methodID, int methodFlags, byte[] data) { - if (eventClrInstanceId != clrInstanceId.Value) + if (eventClrInstanceId != clrInstanceId) { return; } @@ -1553,7 +1553,7 @@ void AddToInstrumentationData(int eventClrInstanceId, long methodID, int methodF MethodDesc method = null; try { - method = idParser.ResolveMethodID(methodID, out _, commandLineOptions.VerboseWarnings); + method = idParser.ResolveMethodID(methodID, out _, _command.VerboseWarnings); } catch (Exception) { @@ -1581,7 +1581,7 @@ void AddToInstrumentationData(int eventClrInstanceId, long methodID, int methodF } SampleCorrelator correlator = null; - if (commandLineOptions.Spgo) + if (Get(_command.Spgo)) { correlator = new SampleCorrelator(GetMethodMemMap()); @@ -1652,7 +1652,7 @@ void AddToInstrumentationData(int eventClrInstanceId, long methodID, int methodF correlator.SmoothAllProfiles(); } - if (commandLineOptions.DisplayProcessedEvents) + if (_command.DisplayProcessedEvents) { foreach (var entry in methodsToAttemptToPrepare) { @@ -1662,12 +1662,7 @@ void AddToInstrumentationData(int eventClrInstanceId, long methodID, int methodF } } - PrintMessage($"Done processing input file"); - - if (commandLineOptions.OutputFileName == null) - { - return 0; - } + PrintMessage("Done processing input file"); // Deduplicate entries HashSet methodsInListAlready = new HashSet(); @@ -1680,7 +1675,7 @@ void AddToInstrumentationData(int eventClrInstanceId, long methodID, int methodF if (methodsInListAlready.Add(entry.Value.Method)) { var methodData = entry.Value; - if (commandLineOptions.GenerateCallGraph) + if (_command.GenerateCallGraph) { exclusiveSamples.TryGetValue(methodData.Method, out methodData.ExclusiveWeight); callGraph.TryGetValue(methodData.Method, out methodData.WeightedCallData); @@ -1708,7 +1703,7 @@ void AddToInstrumentationData(int eventClrInstanceId, long methodID, int methodF else { SampleProfile sp = correlator?.GetProfile(methodData.Method); - if (sp != null && sp.AttributedSamples >= commandLineOptions.SpgoMinSamples) + if (sp != null && sp.AttributedSamples >= Get(_command.SpgoMinSamples)) { IEnumerable schema = sp.SmoothedSamples @@ -1722,7 +1717,8 @@ void AddToInstrumentationData(int eventClrInstanceId, long methodID, int methodF DataLong = kvp.Value, }); - if (commandLineOptions.IncludeFullGraphs) + bool includeFullGraphs = Get(_command.IncludeFullGraphs); + if (includeFullGraphs) { schema = schema.Concat( sp.SmoothedEdgeSamples @@ -1740,7 +1736,7 @@ void AddToInstrumentationData(int eventClrInstanceId, long methodID, int methodF methodData.InstrumentationData = schema.ToArray(); #if DEBUG - if (commandLineOptions.IncludeFullGraphs) + if (includeFullGraphs) { var writtenBlocks = new HashSet( @@ -1765,9 +1761,10 @@ void AddToInstrumentationData(int eventClrInstanceId, long methodID, int methodF } } - if (commandLineOptions.FileType.Value == PgoFileType.jittrace) - GenerateJittraceFile(commandLineOptions.OutputFileName, methodsUsedInProcess, commandLineOptions.JitTraceOptions); - else if (commandLineOptions.FileType.Value == PgoFileType.mibc) + FileInfo outputFileInfo = new(outputPath); + if (_command.FileType.Value == PgoFileType.jittrace) + GenerateJittraceFile(outputFileInfo, methodsUsedInProcess, _command.JitTraceOptions); + else if (_command.FileType.Value == PgoFileType.mibc) { var config = new MibcConfig(); @@ -1786,13 +1783,13 @@ void AddToInstrumentationData(int eventClrInstanceId, long methodID, int methodF ProcessedMethodData processedData = methodsUsedInProcess[i]; methodProfileData[i] = new ILCompiler.MethodProfileData(processedData.Method, ILCompiler.MethodProfilingDataFlags.ReadMethodCode, processedData.ExclusiveWeight, processedData.WeightedCallData, 0xFFFFFFFF, processedData.InstrumentationData); } - return MibcEmitter.GenerateMibcFile(config, tsc, commandLineOptions.OutputFileName, methodProfileData, commandLineOptions.ValidateOutputFile, commandLineOptions.Uncompressed); + return MibcEmitter.GenerateMibcFile(config, tsc, outputFileInfo, methodProfileData, _command.ValidateOutputFile, !Get(_command.Compressed)); } } return 0; } - static void GenerateJittraceFile(FileInfo outputFileName, IEnumerable methodsToAttemptToPrepare, JitTraceOptions jittraceOptions) + private static void GenerateJittraceFile(FileInfo outputFileName, IEnumerable methodsToAttemptToPrepare, JitTraceOptions jittraceOptions) { PrintMessage($"JitTrace options {jittraceOptions}"); @@ -1870,13 +1867,13 @@ static void GenerateJittraceFile(FileInfo outputFileName, IEnumerable typeStringCache) + private static string GetStringForType(TypeDesc type, Dictionary typeStringCache) { string str; if (typeStringCache.TryGetValue(type, out str)) diff --git a/src/coreclr/tools/dotnet-pgo/dotnet-pgo.csproj b/src/coreclr/tools/dotnet-pgo/dotnet-pgo.csproj index 14a99e98aad289..b959b09a521790 100644 --- a/src/coreclr/tools/dotnet-pgo/dotnet-pgo.csproj +++ b/src/coreclr/tools/dotnet-pgo/dotnet-pgo.csproj @@ -27,6 +27,7 @@ + @@ -34,32 +35,12 @@ - - true - Internal.CommandLine.Strings - - - - - - - - - - - - - - - - + + - - - - +