Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a config to MIBC format #71359

Merged
merged 10 commits into from
Jul 1, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions src/coreclr/tools/dotnet-pgo/MibcConfig.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Reflection;

namespace Microsoft.Diagnostics.Tools.Pgo;

public class MibcConfig
{
public string FormatVersion = "1.0";
public string Os;
public string Arch;
public string Runtime;

public override string ToString()
{
string str = "";
foreach (FieldInfo field in GetType().GetFields())
{
string paddedName = (field.Name + ":").PadRight(18, ' ');
str += $"{paddedName} {field.GetValue(this)}\n";
}
EgorBo marked this conversation as resolved.
Show resolved Hide resolved
return str;
}
}
34 changes: 33 additions & 1 deletion src/coreclr/tools/dotnet-pgo/MibcEmitter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -212,12 +212,44 @@ private static void AddAssembliesAssociatedWithMethod(MethodDesc method, HashSet
}
}

public static int GenerateMibcFile(TypeSystemContext tsc, FileInfo outputFileName, IEnumerable<MethodProfileData> methodsToAttemptToPlaceIntoProfileData, bool validate, bool uncompressed)
// ...
/// <summary>
/// Emit a global method "MibcConfig" that will contain key-value settings in the following format:
/// ldstr "key1"
/// ldstr "value1"
/// pop
/// pop
/// ldstr "key2"
/// ldstr "value2"
/// pop
/// pop
/// ...
/// </summary>
public static void GenerateConfigData(MibcConfig config, TypeSystemMetadataEmitter emitter)
{
var buffer = new BlobBuilder();
var il = new InstructionEncoder(buffer);

foreach (FieldInfo mibcCfgField in typeof(MibcConfig).GetFields())
{
Debug.Assert(!mibcCfgField.IsStatic && mibcCfgField.FieldType == typeof(string));
il.LoadString(emitter.GetUserStringHandle(mibcCfgField.Name));
il.LoadString(emitter.GetUserStringHandle((string)mibcCfgField.GetValue(config) ?? ""));
il.OpCode(ILOpCode.Pop);
il.OpCode(ILOpCode.Pop);
}
il.OpCode(ILOpCode.Ret);
emitter.AddGlobalMethod(nameof(MibcConfig), il, 8);
}

public static int GenerateMibcFile(MibcConfig config, TypeSystemContext tsc, FileInfo outputFileName, IEnumerable<MethodProfileData> methodsToAttemptToPlaceIntoProfileData, bool validate, bool uncompressed)
{
TypeSystemMetadataEmitter emitter = new TypeSystemMetadataEmitter(new AssemblyName(outputFileName.Name), tsc);
emitter.InjectSystemPrivateCanon();
emitter.AllowUseOfAddGlobalMethod();

GenerateConfigData(config, emitter);

SortedDictionary<string, MIbcGroup> groups = new SortedDictionary<string, MIbcGroup>();
StringBuilder mibcGroupNameBuilder = new StringBuilder();
HashSet<string> assembliesAssociatedWithMethod = new HashSet<string>();
Expand Down
88 changes: 81 additions & 7 deletions src/coreclr/tools/dotnet-pgo/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ static int InnerDumpMain(CommandLineOptions commandLineOptions)

PrintDetailedMessage($"Parsing {commandLineOptions.InputFileToDump}");
var profileData = MIbcProfileParser.ParseMIbcFile(tsc, mibcPeReader, null, onlyDefinedInAssembly: null);
PrintMibcStats(profileData);
PrintMibcStats(ParseMibcConfig(tsc, mibcPeReader), profileData);
EgorBo marked this conversation as resolved.
Show resolved Hide resolved

using (FileStream outputFile = new FileStream(commandLineOptions.OutputFileName.FullName, FileMode.Create, FileAccess.Write))
{
Expand Down Expand Up @@ -354,6 +354,67 @@ static int InnerDumpMain(CommandLineOptions commandLineOptions)
return 0;
}

static MibcConfig ParseMibcConfig(TypeRefTypeSystem.TypeRefTypeSystemContext tsc, params PEReader[] pEReaders)
{
MibcConfig mergedConfig = new();
// When we merge multiple mibc let's just unconditionally pick the first valid MibcConfig
EgorBo marked this conversation as resolved.
Show resolved Hide resolved
foreach (PEReader peReader in pEReaders)
{
EcmaModule mibcModule = EcmaModule.Create(tsc, peReader, null);
EcmaMethod mibcConfigMth = (EcmaMethod)mibcModule.GetGlobalModuleType().GetMethod(nameof(MibcConfig), null);
if (mibcConfigMth != null)
{
var ilBody = EcmaMethodIL.Create(mibcConfigMth);
var ilReader = new ILReader(ilBody.GetILBytes());

// Parse:
//
// ldstr "key1"
// ldstr "value1"
// pop
// pop
// ldstr "key2"
// ldstr "value2"
// pop
// pop
// ...
// ret
string fieldName = null;
while (ilReader.HasNext)
{
ILOpcode opcode = ilReader.ReadILOpcode();
switch (opcode)
{
case ILOpcode.ldstr:
var ldStrValue = (string)ilBody.GetObject(ilReader.ReadILToken());
if (fieldName != null)
{
var field = mergedConfig.GetType().GetField(fieldName);
if (field != null)
{
field.SetValue(mergedConfig, ldStrValue);
}
}
else
{
fieldName = ldStrValue;
}
break;

case ILOpcode.ret:
case ILOpcode.pop:
fieldName = null;
break;

default:
throw new InvalidOperationException($"Unexpected opcode: {opcode}");
}
}
break;
}
}
return mergedConfig;
}

static int InnerMergeMain(CommandLineOptions commandLineOptions)
{
Expand Down Expand Up @@ -399,7 +460,8 @@ static int InnerMergeMain(CommandLineOptions commandLineOptions)
ProfileData.MergeProfileData(ref partialNgen, mergedProfileData, MIbcProfileParser.ParseMIbcFile(tsc, peReader, assemblyNamesInBubble, onlyDefinedInAssembly: null));
}

int result = MibcEmitter.GenerateMibcFile(tsc, commandLineOptions.OutputFileName, mergedProfileData.Values, commandLineOptions.ValidateOutputFile, commandLineOptions.Uncompressed);
MibcConfig mergedConfig = ParseMibcConfig(tsc, mibcReaders);
int result = MibcEmitter.GenerateMibcFile(mergedConfig, tsc, commandLineOptions.OutputFileName, mergedProfileData.Values, commandLineOptions.ValidateOutputFile, commandLineOptions.Uncompressed);
if (result == 0 && commandLineOptions.InheritTimestamp)
{
commandLineOptions.OutputFileName.CreationTimeUtc = commandLineOptions.InputFilesToMerge.Max(fi => fi.CreationTimeUtc);
Expand Down Expand Up @@ -445,10 +507,10 @@ static int InnerCompareMibcMain(CommandLineOptions options)
ProfileData profile2 = MIbcProfileParser.ParseMIbcFile(tsc, mibc2, null, onlyDefinedInAssembly: null);
PrintOutput($"Comparing {name1} to {name2}");
PrintOutput($"Statistics for {name1}");
PrintMibcStats(profile1);
PrintMibcStats(ParseMibcConfig(tsc, mibc1), profile1);
PrintOutput("");
PrintOutput($"Statistics for {name2}");
PrintMibcStats(profile2);
PrintMibcStats(ParseMibcConfig(tsc, mibc2), profile2);

PrintOutput("");
PrintOutput("Comparison");
Expand Down Expand Up @@ -792,9 +854,10 @@ static void PrintLikelihoodHistogram(double[] likelihoods)
Console.WriteLine();
}

static void PrintMibcStats(ProfileData data)
static void PrintMibcStats(MibcConfig config, ProfileData data)
{
List<MethodProfileData> methods = data.GetAllMethodProfileData().ToList();
PrintOutput(config.ToString());
List <MethodProfileData> methods = data.GetAllMethodProfileData().ToList();
List<MethodProfileData> profiledMethods = methods.Where(spd => spd.SchemaData != null).ToList();
PrintOutput($"# Methods: {methods.Count}");
PrintOutput($"# Methods with any profile data: {profiledMethods.Count(spd => spd.SchemaData.Length > 0)}");
Expand Down Expand Up @@ -1659,13 +1722,24 @@ void AddToInstrumentationData(int eventClrInstanceId, long methodID, int methodF
GenerateJittraceFile(commandLineOptions.OutputFileName, methodsUsedInProcess, commandLineOptions.JitTraceOptions);
else if (commandLineOptions.FileType.Value == PgoFileType.mibc)
{
var config = new MibcConfig();

// Look for OS and Arch, e.g. "Windows" and "x64"
TraceEvent processInfo = p.EventsInProcess.Filter(t => t.EventName == "ProcessInfo").FirstOrDefault();
config.Os = processInfo?.PayloadByName("OSInformation")?.ToString();
config.Arch = processInfo?.PayloadByName("ArchInformation")?.ToString();

// Look for Sku, e.g. "CoreClr"
TraceEvent runtimeStart = p.EventsInProcess.Filter(t => t.EventName == "Runtime/Start").FirstOrDefault();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does the Sku work (what does it stand for)? It would be nice to have the mono runtime be emitted, so if we use the Sku for emitting runtime, would it be unproblematic to extend the Sku enum to also contain Mono (not sure if there is a trickle effect)? What is the Runtime/Start event/where do i look to find it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't sure what to pick for runtime so probably it would be easier if you tell me which event to analyze cc @lateralusX
I just opened a random nettrace via PerfView and searched for CoreCLR - this was the first thing to pop up - I assume mono runtime is expected to send this exact event on start with a different value for Sku

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If that eventname is the same as FireEtwRuntimeInformationDCStart, it seems to be the right event. I think we can try adding mono to the Sku Enum, right now we are emitting CoreCLR as well on Mono

RUNTIME_SKU_CORECLR,
since it wasn't clear whether a non 0x1, 0x2 Sku value will cause problems anywhere that its used.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see FireEtwRuntimeInformationDCStart in my sample nettrace

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is the events we collect - Microsoft-Windows-DotNETRuntime:0x1F000080018:5

Copy link
Member Author

@EgorBo EgorBo Jun 30, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I assume the current impl is correct as is, right? it does poll "Runtime/Start" event and extract Sku so once Mono is added we're all set

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lateralusX @EgorBo I'm thinking the value should be 0x4 as 0x3 will show up as DesktopClr, CoreClr since it matches those bits (0x1 || 0x2). I obtained a trace with --providers Microsoft-Windows-DotNETRuntime:0x1F000080018:5 on mono and it indeed shows the sku value within the mibc file. https://gist.github.com/mdh1418/2e9ad2d937dbdd0fb0be063c15eff61a

I also verified that we should be able to obtain the actual Sku value from the Mono AOT Compiler side as well. I'll put up a separate PR to integrate the .mibc file validation.

I think we should extend the relevant Sku structs and Mono additions I mentioned earlier

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is anything treating the sku as a bit set? Seems like it shouldn't be.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jakobbotsch I'm not sure what the main rationale is between making something a bitmap vs a valuemap, but this might be why its behaving as such

<bitMap name="RuntimeSkuMap">

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, seems reasonable then.

config.Runtime = runtimeStart?.PayloadByName("Sku")?.ToString();

ILCompiler.MethodProfileData[] methodProfileData = new ILCompiler.MethodProfileData[methodsUsedInProcess.Count];
for (int i = 0; i < methodProfileData.Length; i++)
{
ProcessedMethodData processedData = methodsUsedInProcess[i];
methodProfileData[i] = new ILCompiler.MethodProfileData(processedData.Method, ILCompiler.MethodProfilingDataFlags.ReadMethodCode, processedData.ExclusiveWeight, processedData.WeightedCallData, 0xFFFFFFFF, processedData.InstrumentationData);
}
return MibcEmitter.GenerateMibcFile(tsc, commandLineOptions.OutputFileName, methodProfileData, commandLineOptions.ValidateOutputFile, commandLineOptions.Uncompressed);
return MibcEmitter.GenerateMibcFile(config, tsc, commandLineOptions.OutputFileName, methodProfileData, commandLineOptions.ValidateOutputFile, commandLineOptions.Uncompressed);
}
}
return 0;
Expand Down