Add --make-repro-path option to crossgen2 (#57543)
authorDavid Wrighton <davidwr@microsoft.com>
Tue, 17 Aug 2021 19:42:07 +0000 (12:42 -0700)
committerGitHub <noreply@github.com>
Tue, 17 Aug 2021 19:42:07 +0000 (12:42 -0700)
- This is used to create repro packages for customer discovered issues with CrossGen2
- Customers will use this by setting the PublishCrossGen2ExtraArgs property to something like `--make-repro-path:c:\repro\crossgen2repro`
- Then the build will run, and produce zip files in the specified directory, one for each crossgen2 run that occurs during that build.
- Hopefully, then it is straightforward to package the failure up and send it in.

In addition, this tech can be used to transfer a full set of repro details from a Unix to Windows machine for easier debugging.

src/coreclr/tools/aot/crossgen2/CommandLineOptions.cs
src/coreclr/tools/aot/crossgen2/Properties/Resources.resx

index 8f832d1..c34c7ad 100644 (file)
@@ -2,8 +2,10 @@
 // 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;
 using System.Reflection;
 using System.Text;
 
@@ -75,6 +77,8 @@ namespace ILCompiler
 
         public IReadOnlyList<string> CodegenOptions;
 
+        public string MakeReproPath;
+
         public bool CompositeOrInputBubble => Composite || InputBubble;
 
         public CommandLineOptions(string[] args)
@@ -169,6 +173,8 @@ namespace ILCompiler
                 syntax.DefineOption("verify-type-and-field-layout", ref VerifyTypeAndFieldLayout, SR.VerifyTypeAndFieldLayoutOption);
                 syntax.DefineOption("callchain-profile", ref CallChainProfileFile, SR.CallChainProfileFile);
 
+                syntax.DefineOption("make-repro-path", ref MakeReproPath, SR.MakeReproPathHelp);
+
                 syntax.DefineOption("h|help", ref Help, SR.HelpOption);
 
                 syntax.DefineParameterList("in", ref InputFilePaths, SR.InputFilesToCompile);
@@ -230,6 +236,162 @@ namespace ILCompiler
 
                 HelpText = argSyntax.GetHelpText();
             }
+
+            if (MakeReproPath != null)
+            {
+                // Create a repro package in the specified path
+                // This package will have the set of input files needed for compilation
+                // + the original command line arguments
+                // + a rsp file that should work to directly run out of the zip file
+
+                string makeReproPath = MakeReproPath;
+                Directory.CreateDirectory(makeReproPath);
+
+                List<string> crossgenDetails = new List<string>();
+                crossgenDetails.Add("CrossGen2 version");
+                try
+                {
+                    crossgenDetails.Add(Environment.GetCommandLineArgs()[0]);
+                } catch  {}
+                try
+                {
+                    crossgenDetails.Add(System.Diagnostics.FileVersionInfo.GetVersionInfo(Environment.GetCommandLineArgs()[0]).ToString());
+                } catch  {}
+
+                crossgenDetails.Add("------------------------");
+                crossgenDetails.Add("Actual Command Line Args");
+                crossgenDetails.Add("------------------------");
+                crossgenDetails.AddRange(args);
+                foreach (string arg in args)
+                {
+                    if (arg.StartsWith('@'))
+                    {
+                        string rspFileName = arg.Substring(1);
+                        crossgenDetails.Add("------------------------");
+                        crossgenDetails.Add(rspFileName);
+                        crossgenDetails.Add("------------------------");
+                        try
+                        {
+                            crossgenDetails.AddRange(File.ReadAllLines(rspFileName));
+                        } catch  {}
+                    }
+                }
+
+                HashCode hashCodeOfArgs = new HashCode();
+                foreach (string s in crossgenDetails)
+                    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("crossgen2command.txt");
+                    using (StreamWriter writer = new StreamWriter(commandEntry.Open()))
+                    {
+                        foreach (string s in crossgenDetails)
+                            writer.WriteLine(s);
+                    }
+
+                    HashSet<string> inputOptionNames = new HashSet<string>();
+                    inputOptionNames.Add("-r");
+                    inputOptionNames.Add("-u");
+                    inputOptionNames.Add("-m");
+                    inputOptionNames.Add("--inputbubbleref");
+                    Dictionary<string, string> inputToReproPackageFileName = new Dictionary<string, string>();
+
+                    List<string> rspFile = new List<string>();
+                    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<string, string> dictionary = new Dictionary<string, string>();
+                                    foreach (string optInList in (IEnumerable)option.Value)
+                                    {
+                                        Helpers.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("crossgen2repro.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;
+                        }
+                    }
+                }
+            }
         }
     }
 }
index 0eb8411..c122cd6 100644 (file)
   <data name="InstructionSetHelp" xml:space="preserve">
     <value>The allowable values for the --instruction-set option are described in the table below. Each architecture has a different set of valid instruction sets, and multiple instruction sets may be specified by separating the instructions sets by a ','. For example 'avx2,bmi,lzcnt'</value>
   </data>
+  <data name="MakeReproPathHelp" xml:space="preserve">
+    <value>Make a repro zip file in the specified path. This is used for sending error reports to the .NET team.</value>
+  </data>
 </root>