| File: src\ExtractNgenMethodList.cs | Web Access |
| Project: src\arcade\src\Microsoft.DotNet.Arcade.Sdk\Microsoft.DotNet.Arcade.Sdk.csproj (Microsoft.DotNet.Arcade.Sdk) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.Build.Framework; using Microsoft.Build.Utilities; using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Reflection.Metadata; using System.Reflection.PortableExecutable; using System.Xml.Linq; namespace Microsoft.DotNet.Arcade.Sdk { /// <summary> /// Used to convert a raw XML dump from IBCMerge into the set of methods which will be NGEN'd when /// partial NGEN is enabled /// </summary> public sealed class ExtractNgenMethodList : Microsoft.Build.Utilities.Task { /// <summary> /// This is the XML file produced by passing -dxml to ibcmerge. It will be transformed into the set of /// methods marked for NGEN in the assembly /// </summary> [Required] public string IbcXmlFilePath { get; set; } /// <summary> /// This is the name of the assembly for when IBC is being run. /// </summary> [Required] public string AssemblyFilePath { get; set; } /// <summary> /// The current target framework for the assembly being compiled. /// </summary> public string AssemblyTargetFramework { get; set; } /// <summary> /// This is the directory the NGEN list should be output to /// </summary> [Required] public string OutputDirectory { get; set; } /// <summary> /// From IBCMerge.MethodProfilingDataFlags /// </summary> private const int ReadMethodCode = 0x1; public override bool Execute() { var document = XDocument.Load(IbcXmlFilePath); var items = new List<string>(); var parentElement = document.Root.Element("MethodProfilingData"); if (parentElement != null) { foreach (var child in parentElement.Elements("Item")) { var flagsInt = child.Attribute("flagsInt"); var name = child.Attribute("name"); if (int.TryParse(flagsInt?.Value.Substring(2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out int parsedFlagsInt) && (parsedFlagsInt & ReadMethodCode) == 1) { items.Add(name.Value); } } } items.Sort(); Directory.CreateDirectory(OutputDirectory); // When AssemblyTargetFramework is set then this is an assembly that is being built by the current // build. Appending the target framework means we will avoid name clashes. When it's not set then // this is a binary that is included in the build but not actually built here. Possible, remotely, // that there will be multiple versions with the same target framework. Hence use the MVID as the // suffix here to avoid clashes. var outputFileNameSuffix = string.IsNullOrEmpty(AssemblyTargetFramework) ? GetAssemblyMvid().ToString() : AssemblyTargetFramework; var outputFileName = $"{Path.GetFileNameWithoutExtension(AssemblyFilePath)}-{outputFileNameSuffix}.ngen.txt"; var outputFilePath = Path.Combine(OutputDirectory, outputFileName); using (var outputFileStream = new StreamWriter(outputFilePath, append: false)) { foreach (var item in items) { outputFileStream.WriteLine(item); } } return true; } private Guid GetAssemblyMvid() { using (var stream = File.Open(AssemblyFilePath, FileMode.Open, FileAccess.Read, FileShare.Read)) { var peReader = new PEReader(stream); var metadataReader = peReader.GetMetadataReader(); return metadataReader.GetGuid(metadataReader.GetModuleDefinition().Mvid); } } } }