| File: PerfMapWriter.cs | Web Access |
| Project: src\runtime\src\coreclr\tools\aot\ILCompiler.Diagnostics\ILCompiler.Diagnostics.csproj (ILCompiler.Diagnostics) |
// 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; using System.Linq; using System.Security.Cryptography; using Internal.ReadyToRunDiagnosticsConstants; using Internal.TypeSystem; namespace ILCompiler.Diagnostics { public class PerfMapWriter { public const int LegacyCrossgen1FormatVersion = 0; public const int CurrentFormatVersion = 1; const int HeaderEntriesPseudoLength = 0; private TextWriter _writer; private PerfMapWriter(TextWriter writer) { _writer = writer; } public static void Write(string perfMapFileName, int perfMapFormatVersion, IEnumerable<MethodInfo> methods, IEnumerable<AssemblyInfo> inputAssemblies, TargetDetails details) { if (perfMapFormatVersion > CurrentFormatVersion) { throw new NotSupportedException(perfMapFormatVersion.ToString()); } using (TextWriter writer = new StreamWriter(perfMapFileName)) { PerfMapWriter perfMapWriter = new PerfMapWriter(writer); byte[] signature = PerfMapV1SignatureHelper(inputAssemblies, details); WritePerfMapV1Header(inputAssemblies, details, perfMapWriter); foreach (MethodInfo methodInfo in methods) { if (methodInfo.HotRVA != 0 && methodInfo.HotLength != 0) { perfMapWriter.WriteLine(methodInfo.Name, methodInfo.HotRVA, methodInfo.HotLength); } if (methodInfo.ColdRVA != 0 && methodInfo.ColdLength != 0) { perfMapWriter.WriteLine(methodInfo.Name, methodInfo.ColdRVA, methodInfo.ColdLength); } } } } private static void WritePerfMapV1Header(IEnumerable<AssemblyInfo> inputAssemblies, TargetDetails details, PerfMapWriter perfMapWriter) { byte[] signature = PerfMapV1SignatureHelper(inputAssemblies, details); // Make sure these get emitted in this order, other tools in the ecosystem like the symbol uploader and PerfView rely on this. // In particular, the order of it. Append only. string signatureFormatted = Convert.ToHexString(signature); PerfmapTokensForTarget targetTokens = TranslateTargetDetailsToPerfmapConstants(details); perfMapWriter.WriteLine(signatureFormatted, (uint)PerfMapPseudoRVAToken.OutputSignature, HeaderEntriesPseudoLength); perfMapWriter.WriteLine(CurrentFormatVersion.ToString(), (uint)PerfMapPseudoRVAToken.FormatVersion, HeaderEntriesPseudoLength); perfMapWriter.WriteLine(((uint)targetTokens.OperatingSystem).ToString(), (uint)PerfMapPseudoRVAToken.TargetOS, HeaderEntriesPseudoLength); perfMapWriter.WriteLine(((uint)targetTokens.Architecture).ToString(), (uint)PerfMapPseudoRVAToken.TargetArchitecture, HeaderEntriesPseudoLength); perfMapWriter.WriteLine(((uint)targetTokens.Abi).ToString(), (uint)PerfMapPseudoRVAToken.TargetABI, HeaderEntriesPseudoLength); } public static byte[] PerfMapV1SignatureHelper(IEnumerable<AssemblyInfo> inputAssemblies, TargetDetails details) { IEnumerable<AssemblyInfo> orderedInputs = inputAssemblies.OrderBy(asm => asm.Name, StringComparer.OrdinalIgnoreCase); List<byte> inputHash = new List<byte>(); foreach (AssemblyInfo inputAssembly in orderedInputs) { inputHash.AddRange(inputAssembly.Mvid.ToByteArray()); } PerfmapTokensForTarget targetTokens = TranslateTargetDetailsToPerfmapConstants(details); byte[] buffer = new byte[12]; if (!BitConverter.TryWriteBytes(buffer.AsSpan(0, sizeof(uint)), (uint)targetTokens.OperatingSystem) || !BitConverter.TryWriteBytes(buffer.AsSpan(4, sizeof(uint)), (uint)targetTokens.Architecture) || !BitConverter.TryWriteBytes(buffer.AsSpan(8, sizeof(uint)), (uint)targetTokens.Abi)) { throw new InvalidOperationException(); } if (!BitConverter.IsLittleEndian) { buffer.AsSpan(0, sizeof(uint)).Reverse(); buffer.AsSpan(4, sizeof(uint)).Reverse(); buffer.AsSpan(8, sizeof(uint)).Reverse(); } inputHash.AddRange(buffer); byte[] hash = MD5.HashData(inputHash.ToArray()); return hash; } internal record struct PerfmapTokensForTarget(PerfMapOSToken OperatingSystem, PerfMapArchitectureToken Architecture, PerfMapAbiToken Abi); private static PerfmapTokensForTarget TranslateTargetDetailsToPerfmapConstants(TargetDetails details) { PerfMapOSToken osToken = details.OperatingSystem switch { TargetOS.Unknown => PerfMapOSToken.Unknown, TargetOS.Windows => PerfMapOSToken.Windows, TargetOS.Linux => PerfMapOSToken.Linux, TargetOS.OSX => PerfMapOSToken.OSX, TargetOS.FreeBSD => PerfMapOSToken.FreeBSD, TargetOS.NetBSD => PerfMapOSToken.NetBSD, TargetOS.OpenBSD => PerfMapOSToken.OpenBSD, TargetOS.SunOS => PerfMapOSToken.SunOS, _ => throw new NotImplementedException(details.OperatingSystem.ToString()) }; PerfMapAbiToken abiToken = details.Abi switch { TargetAbi.Unknown => PerfMapAbiToken.Unknown, TargetAbi.NativeAot => PerfMapAbiToken.Default, TargetAbi.NativeAotArmel => PerfMapAbiToken.Armel, _ => throw new NotImplementedException(details.Abi.ToString()) }; PerfMapArchitectureToken archToken = details.Architecture switch { TargetArchitecture.Unknown => PerfMapArchitectureToken.Unknown, TargetArchitecture.ARM => PerfMapArchitectureToken.ARM, TargetArchitecture.ARM64 => PerfMapArchitectureToken.ARM64, TargetArchitecture.X64 => PerfMapArchitectureToken.X64, TargetArchitecture.X86 => PerfMapArchitectureToken.X86, TargetArchitecture.RiscV64 => PerfMapArchitectureToken.RiscV64, TargetArchitecture.LoongArch64 => PerfMapArchitectureToken.LoongArch64, _ => throw new NotImplementedException(details.Architecture.ToString()) }; return new PerfmapTokensForTarget(osToken, archToken, abiToken); } private void WriteLine(string methodName, uint rva, uint length) { _writer.WriteLine($@"{rva:X8} {length:X2} {methodName}"); } } }