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 8 commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ public MethodProfileData(MethodDesc method, MethodProfilingDataFlags flags, doub

public abstract class ProfileData
{
public abstract MibcConfig Config { get; }
public abstract bool PartialNGen { get; }
public abstract MethodProfileData GetMethodProfileData(MethodDesc m);
public abstract IEnumerable<MethodProfileData> GetAllMethodProfileData();
Expand Down Expand Up @@ -131,6 +132,8 @@ private EmptyProfileData()
{
}

public override MibcConfig Config { get; } = new ();

public override bool PartialNGen => false;

public static EmptyProfileData Singleton => s_singleton;
Expand Down
43 changes: 41 additions & 2 deletions src/coreclr/tools/aot/ILCompiler.ReadyToRun/IBC/IBCProfileData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,45 @@

namespace ILCompiler.IBC
{
public class MibcConfig
{
public string FormatVersion = "1.0";
public string Os;
public string Arch;
public string Runtime;

public override string ToString()
{
return
$"""
FormatVersion: {FormatVersion}
Runtime: {Runtime}
Os: {Os}
Arch: {Arch}

""";
}

public static MibcConfig FromKeyValueMap(Dictionary<string, string> kvMap)
{
MibcConfig config = new();
foreach (var kvPair in kvMap)
{
switch (kvPair.Key)
{
case nameof(FormatVersion): config.FormatVersion = kvPair.Value; break;
case nameof(Os): config.Os = kvPair.Value; break;
case nameof(Arch): config.Arch = kvPair.Value; break;
case nameof(Runtime): config.Runtime = kvPair.Value; break;
}
}
return config;
}
}

public class IBCProfileData : ProfileData
{
public IBCProfileData(bool partialNGen, IEnumerable<MethodProfileData> methodData)
public IBCProfileData(MibcConfig config, bool partialNGen, IEnumerable<MethodProfileData> methodData)
{
MethodProfileData[] dataArray = methodData.ToArray();
foreach (MethodProfileData data in dataArray)
Expand All @@ -22,12 +57,16 @@ public IBCProfileData(bool partialNGen, IEnumerable<MethodProfileData> methodDat
}
}
_partialNGen = partialNGen;
_config = config;
}

private readonly Dictionary<MethodDesc, MethodProfileData> _methodData = new Dictionary<MethodDesc, MethodProfileData>();
private readonly bool _partialNGen;
private readonly MibcConfig _config;

public override MibcConfig Config => _config;

public override bool PartialNGen { get { return _partialNGen; } }
public override bool PartialNGen => _partialNGen;

public override MethodProfileData GetMethodProfileData(MethodDesc m)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ public ProfileData ParseIBCDataFromModule(EcmaModule ecmaModule)
}
}

return new IBCProfileData(parsedData.PartialNGen, methodProfileData);
return new IBCProfileData(null, parsedData.PartialNGen, methodProfileData);
}

public struct IBCBlobKey : IEquatable<IBCBlobKey>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,65 @@ public static ProfileData ParseMIbcFile(TypeSystemContext tsc, PEReader peReader
}
}

return new IBCProfileData(false, loadedMethodProfileData);
return new IBCProfileData(ParseMibcConfig(tsc, peReader), false, loadedMethodProfileData);

EgorBo marked this conversation as resolved.
Show resolved Hide resolved

}

public static MibcConfig ParseMibcConfig(TypeSystemContext tsc, PEReader pEReader)
{
// When we merge multiple mibc let's just unconditionally pick the first valid MibcConfig
EgorBo marked this conversation as resolved.
Show resolved Hide resolved
EcmaModule mibcModule = EcmaModule.Create(tsc, pEReader, null);
EcmaMethod mibcConfigMth = (EcmaMethod)mibcModule.GetGlobalModuleType().GetMethod(nameof(MibcConfig), null);

if (mibcConfigMth == null)
return 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;
Dictionary<string, string> keyValue = new();
while (ilReader.HasNext)
{
ILOpcode opcode = ilReader.ReadILOpcode();
switch (opcode)
{
case ILOpcode.ldstr:
var ldStrValue = (string)ilBody.GetObject(ilReader.ReadILToken());
if (fieldName != null)
{
keyValue[fieldName] = ldStrValue;
}
else
{
fieldName = ldStrValue;
}
break;

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

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

return MibcConfig.FromKeyValueMap(keyValue);
}

enum MibcGroupParseState
Expand Down
35 changes: 34 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,45 @@ 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();

if (config != null)
GenerateConfigData(config, emitter);

SortedDictionary<string, MIbcGroup> groups = new SortedDictionary<string, MIbcGroup>();
StringBuilder mibcGroupNameBuilder = new StringBuilder();
HashSet<string> assembliesAssociatedWithMethod = new HashSet<string>();
Expand Down
51 changes: 48 additions & 3 deletions src/coreclr/tools/dotnet-pgo/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,38 @@ static int InnerDumpMain(CommandLineOptions commandLineOptions)
return 0;
}

static MibcConfig ParseMibcConfigsAndMerge(TypeSystemContext tsc, params PEReader[] pEReader)
{
MibcConfig firstCfg = null;
foreach (PEReader peReader in pEReader)
{
MibcConfig config = MIbcProfileParser.ParseMibcConfig(tsc, peReader);
if (firstCfg == null)
{
firstCfg = config;
}
else
{
if (firstCfg.Runtime != config.Runtime)
{
PrintMessage(
$"Warning: Attempting to merge MIBCs collected on different runtimes: {firstCfg.Runtime} != {config.Runtime}");
}
if (firstCfg.FormatVersion != config.FormatVersion)
{
PrintMessage(
$"Warning: Attempting to merge MIBCs with different format versions: {firstCfg.FormatVersion} != {config.FormatVersion}");
}
if (firstCfg.Os != config.Os ||
firstCfg.Arch != config.Arch)
{
PrintMessage(
$"Warning: Attempting to merge MIBCs collected on different RIDs: {firstCfg.Os}-{firstCfg.Arch} != {config.Os}-{config.Arch}");
}
}
}
return firstCfg;
}

static int InnerMergeMain(CommandLineOptions commandLineOptions)
{
Expand Down Expand Up @@ -399,7 +431,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 = ParseMibcConfigsAndMerge(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 @@ -794,7 +827,8 @@ static void PrintLikelihoodHistogram(double[] likelihoods)

static void PrintMibcStats(ProfileData data)
{
List<MethodProfileData> methods = data.GetAllMethodProfileData().ToList();
PrintOutput(data.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 +1693,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