public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { if (relocsOnly) { return(new ObjectData(Array.Empty <byte>(), Array.Empty <Relocation>(), 1, Array.Empty <ISymbolDefinitionNode>())); } PgoValueEmitter pgoEmitter = new PgoValueEmitter(_factory.CompilationModuleGroup, _symbolNodeFactory, true); NativeWriter hashtableWriter = new NativeWriter(); Section hashtableSection = hashtableWriter.NewSection(); VertexHashtable vertexHashtable = new VertexHashtable(); hashtableSection.Place(vertexHashtable); Dictionary <byte[], BlobVertex> uniqueInstrumentationData = new Dictionary <byte[], BlobVertex>(ByteArrayComparer.Instance); foreach (MethodDesc method in _instrumentationDataMethods) { pgoEmitter.Clear(); PgoProcessor.EncodePgoData(_profileDataManager[method].SchemaData, pgoEmitter, false); // In composite R2R format, always enforce owning type to let us share generic instantiations among modules EcmaMethod typicalMethod = (EcmaMethod)method.GetTypicalMethodDefinition(); ModuleToken moduleToken = new ModuleToken(typicalMethod.Module, typicalMethod.Handle); ArraySignatureBuilder signatureBuilder = new ArraySignatureBuilder(); signatureBuilder.EmitMethodSignature( new MethodWithToken(method, moduleToken, constrainedType: null, unboxing: false, context: null), enforceDefEncoding: true, enforceOwningType: _factory.CompilationModuleGroup.EnforceOwningType(moduleToken.Module), factory.SignatureContext, isInstantiatingStub: false); byte[] signature = signatureBuilder.ToArray(); BlobVertex signatureBlob = new BlobVertex(signature); byte[] encodedInstrumentationData = pgoEmitter.ToByteArray(); BlobVertex instrumentationDataBlob = null; if (!uniqueInstrumentationData.TryGetValue(encodedInstrumentationData, out instrumentationDataBlob)) { instrumentationDataBlob = new BlobVertex(encodedInstrumentationData); hashtableSection.Place(instrumentationDataBlob); uniqueInstrumentationData.Add(encodedInstrumentationData, instrumentationDataBlob); } PgoInstrumentedDataWithSignatureBlobVertex pgoDataVertex = new PgoInstrumentedDataWithSignatureBlobVertex(signatureBlob, 0, instrumentationDataBlob); hashtableSection.Place(pgoDataVertex); vertexHashtable.Append(unchecked ((uint)ReadyToRunHashCode.MethodHashCode(method)), pgoDataVertex); } MemoryStream hashtableContent = new MemoryStream(); hashtableWriter.Save(hashtableContent); return(new ObjectData( data: hashtableContent.ToArray(), relocs: null, alignment: 8, definedSymbols: new ISymbolDefinitionNode[] { this })); }
public static void MergeProfileData(ref bool partialNgen, Dictionary <MethodDesc, MethodProfileData> mergedProfileData, ProfileData profileData) { if (profileData.PartialNGen) { partialNgen = true; } PgoSchemaElem[][] schemaElemMergerArray = new PgoSchemaElem[2][]; foreach (MethodProfileData data in profileData.GetAllMethodProfileData()) { MethodProfileData dataToMerge; if (mergedProfileData.TryGetValue(data.Method, out dataToMerge)) { var mergedCallWeights = data.CallWeights; if (mergedCallWeights == null) { mergedCallWeights = dataToMerge.CallWeights; } else if (dataToMerge.CallWeights != null) { mergedCallWeights = new Dictionary <MethodDesc, int>(data.CallWeights); foreach (var entry in dataToMerge.CallWeights) { if (mergedCallWeights.TryGetValue(entry.Key, out var initialWeight)) { mergedCallWeights[entry.Key] = initialWeight + entry.Value; } else { mergedCallWeights[entry.Key] = entry.Value; } } } PgoSchemaElem[] mergedSchemaData; if (data.SchemaData == null) { mergedSchemaData = dataToMerge.SchemaData; } else if (dataToMerge.SchemaData == null) { mergedSchemaData = data.SchemaData; } else { // Actually merge schemaElemMergerArray[0] = dataToMerge.SchemaData; schemaElemMergerArray[1] = data.SchemaData; mergedSchemaData = PgoProcessor.Merge <TypeSystemEntityOrUnknown>(schemaElemMergerArray); } mergedProfileData[data.Method] = new MethodProfileData(data.Method, dataToMerge.Flags | data.Flags, data.ExclusiveWeight + dataToMerge.ExclusiveWeight, mergedCallWeights, dataToMerge.ScenarioMask | data.ScenarioMask, mergedSchemaData); } else { mergedProfileData.Add(data.Method, data); } } }
void EnsurePgoData() { if (_pgoData == null) { var compressedIntParser = new PgoProcessor.PgoEncodedCompressedIntParser(Image, Offset); SignatureFormattingOptions formattingOptions = new SignatureFormattingOptions(); _pgoData = PgoProcessor.ParsePgoData <string>(new PgoDataLoader(_r2rReader, formattingOptions), compressedIntParser, true).ToArray(); _size = compressedIntParser.Offset - Offset; } }
public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { if (relocsOnly) { return(new ObjectData(Array.Empty <byte>(), Array.Empty <Relocation>(), 1, Array.Empty <ISymbolDefinitionNode>())); } PgoValueEmitter pgoEmitter = new PgoValueEmitter(_factory.CompilationModuleGroup, _symbolNodeFactory, true); NativeWriter hashtableWriter = new NativeWriter(); Section hashtableSection = hashtableWriter.NewSection(); VertexHashtable vertexHashtable = new VertexHashtable(); hashtableSection.Place(vertexHashtable); Dictionary <byte[], BlobVertex> uniqueInstrumentationData = new Dictionary <byte[], BlobVertex>(ByteArrayComparer.Instance); foreach (MethodDesc method in _instrumentationDataMethods) { pgoEmitter.Clear(); PgoProcessor.EncodePgoData(CorInfoImpl.ConvertTypeHandleHistogramsToCompactTypeHistogramFormat(_profileDataManager[method].SchemaData, factory.CompilationModuleGroup), pgoEmitter, false); byte[] signature = InstanceEntryPointTableNode.BuildSignatureForMethodDefinedInModule(method, factory); BlobVertex signatureBlob = new BlobVertex(signature); byte[] encodedInstrumentationData = pgoEmitter.ToByteArray(); BlobVertex instrumentationDataBlob = null; if (!uniqueInstrumentationData.TryGetValue(encodedInstrumentationData, out instrumentationDataBlob)) { instrumentationDataBlob = new BlobVertex(encodedInstrumentationData); hashtableSection.Place(instrumentationDataBlob); uniqueInstrumentationData.Add(encodedInstrumentationData, instrumentationDataBlob); } PgoInstrumentedDataWithSignatureBlobVertex pgoDataVertex = new PgoInstrumentedDataWithSignatureBlobVertex(signatureBlob, 0, instrumentationDataBlob); hashtableSection.Place(pgoDataVertex); vertexHashtable.Append(unchecked ((uint)ReadyToRunHashCode.MethodHashCode(method)), pgoDataVertex); } MemoryStream hashtableContent = new MemoryStream(); hashtableWriter.Save(hashtableContent); return(new ObjectData( data: hashtableContent.ToArray(), relocs: null, alignment: 8, definedSymbols: new ISymbolDefinitionNode[] { this })); }
protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) { PgoValueEmitter pgoEmitter = new PgoValueEmitter(_factory.CompilationModuleGroup, _symbolNodeFactory, false); foreach (MethodDesc method in _instrumentationDataMethods) { PgoProcessor.EncodePgoData(_profileDataManager[method].SchemaData, pgoEmitter, false); } DependencyListEntry[] symbols = new DependencyListEntry[pgoEmitter.ReferencedImports.Count]; for (int i = 0; i < symbols.Length; i++) { symbols[i] = new DependencyListEntry(pgoEmitter.ReferencedImports[i], "Pgo Instrumentation Data"); } return(new DependencyList(symbols)); }
/// <summary> /// Parse MIbcGroup method and return enumerable of MethodProfileData /// /// Like the AssemblyDictionary method, data is encoded via IL instructions. The format is /// /// ldtoken methodInProfileData /// Any series of instructions that does not include pop. Expansion data is encoded via ldstr "id" /// followed by a expansion specific sequence of il opcodes. /// pop /// {Repeat N times for N methods described} /// /// Extensions supported with current parser: /// /// ldstr "ExclusiveWeight" /// Any ldc.i4 or ldc.r4 or ldc.r8 instruction to indicate the exclusive weight /// /// ldstr "WeightedCallData" /// ldc.i4 <Count of methods called> /// Repeat <Count of methods called times> /// ldtoken <Method called from this method> /// ldc.i4 <Weight associated with calling the <Method called from this method>> /// /// This format is designed to be extensible to hold more data as we add new per method profile data without breaking existing parsers. /// </summary> static IEnumerable <MethodProfileData> ReadMIbcGroup(TypeSystemContext tsc, EcmaMethod method) { EcmaMethodIL ilBody = EcmaMethodIL.Create(method); MetadataLoaderForPgoData metadataLoader = new MetadataLoaderForPgoData(ilBody); byte[] ilBytes = ilBody.GetILBytes(); int currentOffset = 0; object methodInProgress = null; object metadataNotResolvable = new object(); object metadataObject = null; MibcGroupParseState state = MibcGroupParseState.LookingForNextMethod; int intValue = 0; int weightedCallGraphSize = 0; int profileEntryFound = 0; double exclusiveWeight = 0; Dictionary <MethodDesc, int> weights = null; bool processIntValue = false; List <long> instrumentationDataLongs = null; PgoSchemaElem[] pgoSchemaData = null; while (currentOffset < ilBytes.Length) { ILOpcode opcode = (ILOpcode)ilBytes[currentOffset]; if (opcode == ILOpcode.prefix1) { opcode = 0x100 + (ILOpcode)ilBytes[currentOffset + 1]; } processIntValue = false; switch (opcode) { case ILOpcode.ldtoken: { uint token = BitConverter.ToUInt32(ilBytes.AsSpan(currentOffset + 1, 4)); if (state == MibcGroupParseState.ProcessingInstrumentationData) { instrumentationDataLongs.Add(token); } else { metadataObject = null; try { metadataObject = ilBody.GetObject((int)token); } catch (TypeSystemException) { // The method being referred to may be missing. In that situation, // use the metadataNotResolvable sentinel to indicate that this record should be ignored metadataObject = metadataNotResolvable; } switch (state) { case MibcGroupParseState.ProcessingCallgraphToken: state = MibcGroupParseState.ProcessingCallgraphWeight; break; case MibcGroupParseState.LookingForNextMethod: methodInProgress = metadataObject; state = MibcGroupParseState.LookingForOptionalData; break; default: state = MibcGroupParseState.LookingForOptionalData; break; } } } break; case ILOpcode.ldc_r4: { float fltValue = BitConverter.ToSingle(ilBytes.AsSpan(currentOffset + 1, 4)); switch (state) { case MibcGroupParseState.ProcessingExclusiveWeight: exclusiveWeight = fltValue; state = MibcGroupParseState.LookingForOptionalData; break; default: state = MibcGroupParseState.LookingForOptionalData; break; } break; } case ILOpcode.ldc_r8: { double dblValue = BitConverter.ToDouble(ilBytes.AsSpan(currentOffset + 1, 8)); switch (state) { case MibcGroupParseState.ProcessingExclusiveWeight: exclusiveWeight = dblValue; state = MibcGroupParseState.LookingForOptionalData; break; default: state = MibcGroupParseState.LookingForOptionalData; break; } break; } case ILOpcode.ldc_i4_0: intValue = 0; processIntValue = true; break; case ILOpcode.ldc_i4_1: intValue = 1; processIntValue = true; break; case ILOpcode.ldc_i4_2: intValue = 2; processIntValue = true; break; case ILOpcode.ldc_i4_3: intValue = 3; processIntValue = true; break; case ILOpcode.ldc_i4_4: intValue = 4; processIntValue = true; break; case ILOpcode.ldc_i4_5: intValue = 5; processIntValue = true; break; case ILOpcode.ldc_i4_6: intValue = 6; processIntValue = true; break; case ILOpcode.ldc_i4_7: intValue = 7; processIntValue = true; break; case ILOpcode.ldc_i4_8: intValue = 8; processIntValue = true; break; case ILOpcode.ldc_i4_m1: intValue = -1; processIntValue = true; break; case ILOpcode.ldc_i4_s: intValue = (sbyte)ilBytes[currentOffset + 1]; processIntValue = true; break; case ILOpcode.ldc_i4: intValue = BitConverter.ToInt32(ilBytes.AsSpan(currentOffset + 1, 4)); processIntValue = true; break; case ILOpcode.ldc_i8: if (state == MibcGroupParseState.ProcessingInstrumentationData) { instrumentationDataLongs.Add(BitConverter.ToInt64(ilBytes.AsSpan(currentOffset + 1, 8))); } break; case ILOpcode.ldstr: { UInt32 userStringToken = BitConverter.ToUInt32(ilBytes.AsSpan(currentOffset + 1, 4)); string optionalDataName = (string)ilBody.GetObject((int)userStringToken); switch (optionalDataName) { case "ExclusiveWeight": state = MibcGroupParseState.ProcessingExclusiveWeight; break; case "WeightedCallData": state = MibcGroupParseState.ProcessingCallgraphCount; break; case "InstrumentationDataStart": state = MibcGroupParseState.ProcessingInstrumentationData; instrumentationDataLongs = new List <long>(); break; case "InstrumentationDataEnd": if (instrumentationDataLongs != null) { instrumentationDataLongs.Add(2); // MarshalMask 2 (Type) instrumentationDataLongs.Add(0); // PgoInstrumentationKind.Done (0) pgoSchemaData = PgoProcessor.ParsePgoData <TypeSystemEntityOrUnknown>(metadataLoader, instrumentationDataLongs, false).ToArray(); } state = MibcGroupParseState.LookingForOptionalData; break; default: state = MibcGroupParseState.LookingForOptionalData; break; } } break; case ILOpcode.pop: if (methodInProgress != metadataNotResolvable) { profileEntryFound++; if (exclusiveWeight == 0) { // If no exclusive weight is found assign a non zero value that assumes the order in the pgo file is significant. exclusiveWeight = Math.Min(1000000.0 - profileEntryFound, 0.0) / 1000000.0; } MethodProfileData mibcData = new MethodProfileData((MethodDesc)methodInProgress, MethodProfilingDataFlags.ReadMethodCode, exclusiveWeight, weights, 0xFFFFFFFF, pgoSchemaData); state = MibcGroupParseState.LookingForNextMethod; exclusiveWeight = 0; weights = null; instrumentationDataLongs = null; pgoSchemaData = null; yield return(mibcData); } methodInProgress = null; break; default: state = MibcGroupParseState.LookingForOptionalData; break; } if (processIntValue) { switch (state) { case MibcGroupParseState.ProcessingExclusiveWeight: exclusiveWeight = intValue; state = MibcGroupParseState.LookingForOptionalData; break; case MibcGroupParseState.ProcessingCallgraphCount: weightedCallGraphSize = intValue; weights = new Dictionary <MethodDesc, int>(); if (weightedCallGraphSize > 0) { state = MibcGroupParseState.ProcessingCallgraphToken; } else { state = MibcGroupParseState.LookingForOptionalData; } break; case MibcGroupParseState.ProcessingCallgraphWeight: if (metadataObject != metadataNotResolvable) { weights.Add((MethodDesc)metadataObject, intValue); } weightedCallGraphSize--; if (weightedCallGraphSize > 0) { state = MibcGroupParseState.ProcessingCallgraphToken; } else { state = MibcGroupParseState.LookingForOptionalData; } break; case MibcGroupParseState.ProcessingInstrumentationData: instrumentationDataLongs.Add(intValue); break; default: state = MibcGroupParseState.LookingForOptionalData; instrumentationDataLongs = null; break; } } // This isn't correct if there is a switch opcode, but since we won't do that, its ok currentOffset += opcode.GetSize(); } }
public byte[] ToByteArray() { return(PgoProcessor.PgoEncodedCompressedLongGenerator(_longs).ToArray()); }
public void AddProcessedMethodData(MethodProfileData processedMethodData) { MethodDesc method = processedMethodData.Method; // Format is // ldtoken method // variable amount of extra metadata about the method, Extension data is encoded via ldstr "id" // pop // Extensions generated by this emitter: // // ldstr "ExclusiveWeight" // Any ldc.i4 or ldc.r4 or ldc.r8 instruction to indicate the exclusive weight // // ldstr "WeightedCallData" // ldc.i4 <Count of methods called> // Repeat <Count of methods called times> // ldtoken <Method called from this method> // ldc.i4 <Weight associated with calling the <Method called from this method>> // // ldstr "InstrumentationDataStart" // Encoded ints and longs, using ldc.i4, and ldc.i8 instructions as well as ldtoken <type> instructions // ldstr "InstrumentationDataEnd" as a terminator try { EntityHandle methodHandle = _emitter.GetMethodRef(method); _il.OpCode(ILOpCode.Ldtoken); _il.Token(methodHandle); if (processedMethodData.ExclusiveWeight != 0) { _il.LoadString(_emitter.GetUserStringHandle("ExclusiveWeight")); if (((double)(int)processedMethodData.ExclusiveWeight) == processedMethodData.ExclusiveWeight) { _il.LoadConstantI4((int)processedMethodData.ExclusiveWeight); } else { _il.LoadConstantR8(processedMethodData.ExclusiveWeight); } } if ((processedMethodData.CallWeights != null) && processedMethodData.CallWeights.Count > 0) { _il.LoadString(_emitter.GetUserStringHandle("WeightedCallData")); _il.LoadConstantI4(processedMethodData.CallWeights.Count); foreach (var entry in processedMethodData.CallWeights) { EntityHandle calledMethod = _emitter.GetMethodRef(entry.Key); _il.OpCode(ILOpCode.Ldtoken); _il.Token(calledMethod); _il.LoadConstantI4(entry.Value); } } if (processedMethodData.SchemaData != null) { _il.LoadString(_emitter.GetUserStringHandle("InstrumentationDataStart")); PgoProcessor.EncodePgoData <TypeSystemEntityOrUnknown, TypeSystemEntityOrUnknown>(processedMethodData.SchemaData, this, true); } _il.OpCode(ILOpCode.Pop); } catch (Exception ex) { Program.PrintWarning($"Exception {ex} while attempting to generate method lists"); } }
static int InnerProcessTraceFileMain(CommandLineOptions commandLineOptions) { if (commandLineOptions.TraceFile == null) { PrintUsage(commandLineOptions, "--trace must be specified"); return(-8); } if (commandLineOptions.OutputFileName == null) { PrintUsage(commandLineOptions, "--output must be specified"); return(-8); } if (commandLineOptions.OutputFileName != null) { if (!commandLineOptions.FileType.HasValue) { PrintUsage(commandLineOptions, $"--pgo-file-type must be specified"); return(-9); } if ((commandLineOptions.FileType.Value != PgoFileType.jittrace) && (commandLineOptions.FileType != PgoFileType.mibc)) { PrintUsage(commandLineOptions, $"Invalid output pgo type {commandLineOptions.FileType} specified."); return(-9); } if (commandLineOptions.FileType == PgoFileType.jittrace) { if (!commandLineOptions.OutputFileName.Name.EndsWith(".jittrace")) { PrintUsage(commandLineOptions, $"jittrace output file name must end with .jittrace"); return(-9); } } if (commandLineOptions.FileType == PgoFileType.mibc) { if (!commandLineOptions.OutputFileName.Name.EndsWith(".mibc")) { PrintUsage(commandLineOptions, $"mibc output file name must end with .mibc"); return(-9); } } } string etlFileName = commandLineOptions.TraceFile.FullName; foreach (string nettraceExtension in new string[] { ".netperf", ".netperf.zip", ".nettrace" }) { if (commandLineOptions.TraceFile.FullName.EndsWith(nettraceExtension)) { etlFileName = commandLineOptions.TraceFile.FullName.Substring(0, commandLineOptions.TraceFile.FullName.Length - nettraceExtension.Length) + ".etlx"; PrintMessage($"Creating ETLX file {etlFileName} from {commandLineOptions.TraceFile.FullName}"); TraceLog.CreateFromEventPipeDataFile(commandLineOptions.TraceFile.FullName, etlFileName); } } string lttngExtension = ".trace.zip"; if (commandLineOptions.TraceFile.FullName.EndsWith(lttngExtension)) { etlFileName = commandLineOptions.TraceFile.FullName.Substring(0, commandLineOptions.TraceFile.FullName.Length - lttngExtension.Length) + ".etlx"; PrintMessage($"Creating ETLX file {etlFileName} from {commandLineOptions.TraceFile.FullName}"); TraceLog.CreateFromLttngTextDataFile(commandLineOptions.TraceFile.FullName, etlFileName); } UnZipIfNecessary(ref etlFileName, commandLineOptions.BasicProgressMessages ? Console.Out : new StringWriter()); using (var traceLog = TraceLog.OpenOrConvert(etlFileName)) { if ((!commandLineOptions.Pid.HasValue && commandLineOptions.ProcessName == null) && traceLog.Processes.Count != 1) { PrintError("Trace file contains multiple processes to distinguish between"); PrintOutput("Either a pid or process name from the following list must be specified"); foreach (TraceProcess proc in traceLog.Processes) { PrintOutput($"Procname = {proc.Name} Pid = {proc.ProcessID}"); } return(1); } if (commandLineOptions.Pid.HasValue && (commandLineOptions.ProcessName != null)) { PrintError("--pid and --process-name cannot be specified together"); return(-1); } // For a particular process TraceProcess p; if (commandLineOptions.Pid.HasValue) { p = traceLog.Processes.LastProcessWithID(commandLineOptions.Pid.Value); } else if (commandLineOptions.ProcessName != null) { List <TraceProcess> matchingProcesses = new List <TraceProcess>(); foreach (TraceProcess proc in traceLog.Processes) { if (String.Compare(proc.Name, commandLineOptions.ProcessName, StringComparison.OrdinalIgnoreCase) == 0) { matchingProcesses.Add(proc); } } if (matchingProcesses.Count == 0) { PrintError("Unable to find matching process in trace"); return(-1); } if (matchingProcesses.Count > 1) { StringBuilder errorMessage = new StringBuilder(); errorMessage.AppendLine("Found multiple matching processes in trace"); foreach (TraceProcess proc in matchingProcesses) { errorMessage.AppendLine($"{proc.Name}\tpid={proc.ProcessID}\tCPUMSec={proc.CPUMSec}"); } PrintError(errorMessage.ToString()); return(-2); } p = matchingProcesses[0]; } else { p = traceLog.Processes.First(); } if (!p.EventsInProcess.ByEventType <MethodDetailsTraceData>().Any()) { PrintError($"No MethodDetails\nWas the trace collected with provider at least \"Microsoft-Windows-DotNETRuntime:0x4000080018:5\"?"); return(-3); } if (!p.EventsInProcess.ByEventType <GCBulkTypeTraceData>().Any()) { PrintError($"No BulkType data\nWas the trace collected with provider at least \"Microsoft-Windows-DotNETRuntime:0x4000080018:5\"?"); return(-4); } if (!p.EventsInProcess.ByEventType <ModuleLoadUnloadTraceData>().Any()) { PrintError($"No managed module load data\nWas the trace collected with provider at least \"Microsoft-Windows-DotNETRuntime:0x4000080018:5\"?"); return(-5); } if (!p.EventsInProcess.ByEventType <MethodJittingStartedTraceData>().Any()) { PrintError($"No managed jit starting data\nWas the trace collected with provider at least \"Microsoft-Windows-DotNETRuntime:0x4000080018:5\"?"); return(-5); } PgoTraceProcess pgoProcess = new PgoTraceProcess(p); int? clrInstanceId = commandLineOptions.ClrInstanceId; if (!clrInstanceId.HasValue) { HashSet <int> clrInstanceIds = new HashSet <int>(); HashSet <int> examinedClrInstanceIds = new HashSet <int>(); foreach (var assemblyLoadTrace in p.EventsInProcess.ByEventType <AssemblyLoadUnloadTraceData>()) { if (examinedClrInstanceIds.Add(assemblyLoadTrace.ClrInstanceID)) { if (pgoProcess.ClrInstanceIsCoreCLRInstance(assemblyLoadTrace.ClrInstanceID)) { clrInstanceIds.Add(assemblyLoadTrace.ClrInstanceID); } } } if (clrInstanceIds.Count != 1) { if (clrInstanceIds.Count == 0) { PrintError($"No managed CLR in target process, or per module information could not be loaded from the trace."); } else { // There are multiple clr processes... search for the one that implements int[] clrInstanceIdsArray = clrInstanceIds.ToArray(); Array.Sort(clrInstanceIdsArray); StringBuilder errorMessage = new StringBuilder(); errorMessage.AppendLine("Multiple CLR instances used in process. Choose one to examine with -clrInstanceID:<id> Valid ids:"); foreach (int instanceID in clrInstanceIds) { errorMessage.AppendLine(instanceID.ToString()); } PrintError(errorMessage.ToString()); } return(-10); } else { clrInstanceId = clrInstanceIds.First(); } } var tsc = new TraceTypeSystemContext(pgoProcess, clrInstanceId.Value, s_logger); if (commandLineOptions.VerboseWarnings) { PrintWarning($"{traceLog.EventsLost} Lost events"); } bool filePathError = false; if (commandLineOptions.Reference != null) { foreach (FileInfo fileReference in commandLineOptions.Reference) { if (!File.Exists(fileReference.FullName)) { PrintError($"Unable to find reference '{fileReference.FullName}'"); filePathError = true; } else { tsc.GetModuleFromPath(fileReference.FullName); } } } if (filePathError) { return(-6); } if (!tsc.Initialize()) { return(-12); } TraceRuntimeDescToTypeSystemDesc idParser = new TraceRuntimeDescToTypeSystemDesc(p, tsc, clrInstanceId.Value); SortedDictionary <int, ProcessedMethodData> methodsToAttemptToPrepare = new SortedDictionary <int, ProcessedMethodData>(); if (commandLineOptions.ProcessR2REvents) { foreach (var e in p.EventsInProcess.ByEventType <R2RGetEntryPointTraceData>()) { int parenIndex = e.MethodSignature.IndexOf('('); string retArg = e.MethodSignature.Substring(0, parenIndex); string paramsArgs = e.MethodSignature.Substring(parenIndex); string methodNameFromEventDirectly = retArg + e.MethodNamespace + "." + e.MethodName + paramsArgs; if (e.ClrInstanceID != clrInstanceId.Value) { if (!commandLineOptions.Warnings) { continue; } PrintWarning($"Skipped R2REntryPoint {methodNameFromEventDirectly} due to ClrInstanceID of {e.ClrInstanceID}"); continue; } MethodDesc method = null; string extraWarningText = null; try { method = idParser.ResolveMethodID(e.MethodID, commandLineOptions.VerboseWarnings); } catch (Exception exception) { extraWarningText = exception.ToString(); } if (method == null) { if ((e.MethodNamespace == "dynamicClass") || !commandLineOptions.Warnings) { continue; } PrintWarning($"Unable to parse {methodNameFromEventDirectly} when looking up R2R methods"); if (extraWarningText != null) { PrintWarning(extraWarningText); } continue; } if ((e.TimeStampRelativeMSec >= commandLineOptions.ExcludeEventsBefore) && (e.TimeStampRelativeMSec <= commandLineOptions.ExcludeEventsAfter)) { methodsToAttemptToPrepare.Add((int)e.EventIndex, new ProcessedMethodData(e.TimeStampRelativeMSec, method, "R2RLoad")); } } } // Find all the jitStart events. if (commandLineOptions.ProcessJitEvents) { foreach (var e in p.EventsInProcess.ByEventType <MethodJittingStartedTraceData>()) { int parenIndex = e.MethodSignature.IndexOf('('); string retArg = e.MethodSignature.Substring(0, parenIndex); string paramsArgs = e.MethodSignature.Substring(parenIndex); string methodNameFromEventDirectly = retArg + e.MethodNamespace + "." + e.MethodName + paramsArgs; if (e.ClrInstanceID != clrInstanceId.Value) { if (!commandLineOptions.Warnings) { continue; } PrintWarning($"Skipped {methodNameFromEventDirectly} due to ClrInstanceID of {e.ClrInstanceID}"); continue; } MethodDesc method = null; string extraWarningText = null; try { method = idParser.ResolveMethodID(e.MethodID, commandLineOptions.VerboseWarnings); } catch (Exception exception) { extraWarningText = exception.ToString(); } if (method == null) { if ((e.MethodNamespace == "dynamicClass") || !commandLineOptions.Warnings) { continue; } PrintWarning($"Unable to parse {methodNameFromEventDirectly}"); if (extraWarningText != null) { PrintWarning(extraWarningText); } continue; } if ((e.TimeStampRelativeMSec >= commandLineOptions.ExcludeEventsBefore) && (e.TimeStampRelativeMSec <= commandLineOptions.ExcludeEventsAfter)) { methodsToAttemptToPrepare.Add((int)e.EventIndex, new ProcessedMethodData(e.TimeStampRelativeMSec, method, "JitStart")); } } } Dictionary <MethodDesc, Dictionary <MethodDesc, int> > callGraph = null; Dictionary <MethodDesc, int> exclusiveSamples = null; if (commandLineOptions.GenerateCallGraph) { HashSet <MethodDesc> methodsListedToPrepare = new HashSet <MethodDesc>(); foreach (var entry in methodsToAttemptToPrepare) { methodsListedToPrepare.Add(entry.Value.Method); } callGraph = new Dictionary <MethodDesc, Dictionary <MethodDesc, int> >(); exclusiveSamples = new Dictionary <MethodDesc, int>(); // Capture the addresses of jitted code List <ValueTuple <InstructionPointerRange, MethodDesc> > codeLocations = new List <(InstructionPointerRange, MethodDesc)>(); foreach (var e in p.EventsInProcess.ByEventType <MethodLoadUnloadTraceData>()) { if (e.ClrInstanceID != clrInstanceId.Value) { continue; } MethodDesc method = null; try { method = idParser.ResolveMethodID(e.MethodID, commandLineOptions.VerboseWarnings); } catch (Exception) { } if (method != null) { codeLocations.Add((new InstructionPointerRange(e.MethodStartAddress, e.MethodSize), method)); } } foreach (var e in p.EventsInProcess.ByEventType <MethodLoadUnloadVerboseTraceData>()) { if (e.ClrInstanceID != clrInstanceId.Value) { continue; } MethodDesc method = null; try { method = idParser.ResolveMethodID(e.MethodID, commandLineOptions.VerboseWarnings); } catch (Exception) { } if (method != null) { codeLocations.Add((new InstructionPointerRange(e.MethodStartAddress, e.MethodSize), method)); } } var sigProvider = new R2RSignatureTypeProvider(tsc); foreach (var module in p.LoadedModules) { if (module.FilePath == "") { continue; } if (!File.Exists(module.FilePath)) { continue; } try { byte[] image = File.ReadAllBytes(module.FilePath); using (FileStream fstream = new FileStream(module.FilePath, FileMode.Open, FileAccess.Read, FileShare.Read)) { var r2rCheckPEReader = new System.Reflection.PortableExecutable.PEReader(fstream, System.Reflection.PortableExecutable.PEStreamOptions.LeaveOpen); if (!ILCompiler.Reflection.ReadyToRun.ReadyToRunReader.IsReadyToRunImage(r2rCheckPEReader)) { continue; } } var reader = new ILCompiler.Reflection.ReadyToRun.ReadyToRunReader(tsc, module.FilePath); foreach (var methodEntry in reader.GetCustomMethodToRuntimeFunctionMapping <TypeDesc, MethodDesc, R2RSigProviderContext>(sigProvider)) { foreach (var runtimeFunction in methodEntry.Value.RuntimeFunctions) { codeLocations.Add((new InstructionPointerRange(module.ImageBase + (ulong)runtimeFunction.StartAddress, runtimeFunction.Size), methodEntry.Key)); } } } catch { } } InstructionPointerRange[] instructionPointerRanges = new InstructionPointerRange[codeLocations.Count]; MethodDesc[] methods = new MethodDesc[codeLocations.Count]; for (int i = 0; i < codeLocations.Count; i++) { instructionPointerRanges[i] = codeLocations[i].Item1; methods[i] = codeLocations[i].Item2; } Array.Sort(instructionPointerRanges, methods); foreach (var e in p.EventsInProcess.ByEventType <SampledProfileTraceData>()) { var callstack = e.CallStack(); if (callstack == null) { continue; } ulong address1 = callstack.CodeAddress.Address; MethodDesc topOfStackMethod = LookupMethodByAddress(address1); MethodDesc nextMethod = null; if (callstack.Caller != null) { ulong address2 = callstack.Caller.CodeAddress.Address; nextMethod = LookupMethodByAddress(address2); } if (topOfStackMethod != null) { if (!methodsListedToPrepare.Contains(topOfStackMethod)) { methodsListedToPrepare.Add(topOfStackMethod); methodsToAttemptToPrepare.Add((int)e.EventIndex, new ProcessedMethodData(e.TimeStampRelativeMSec, topOfStackMethod, "SampleMethod")); } if (exclusiveSamples.TryGetValue(topOfStackMethod, out int count)) { exclusiveSamples[topOfStackMethod] = count + 1; } else { exclusiveSamples[topOfStackMethod] = 1; } } if (topOfStackMethod != null && nextMethod != null) { if (!methodsListedToPrepare.Contains(nextMethod)) { methodsListedToPrepare.Add(nextMethod); methodsToAttemptToPrepare.Add((int)e.EventIndex, new ProcessedMethodData(e.TimeStampRelativeMSec, nextMethod, "SampleMethodCaller")); } if (!callGraph.TryGetValue(nextMethod, out var innerDictionary)) { innerDictionary = new Dictionary <MethodDesc, int>(); callGraph[nextMethod] = innerDictionary; } if (innerDictionary.TryGetValue(topOfStackMethod, out int count)) { innerDictionary[topOfStackMethod] = count + 1; } else { innerDictionary[topOfStackMethod] = 1; } } } MethodDesc LookupMethodByAddress(ulong address) { int index = Array.BinarySearch(instructionPointerRanges, new InstructionPointerRange(address, 1)); if (index >= 0) { return(methods[index]); } else { index = ~index; if (index >= instructionPointerRanges.Length) { return(null); } if (instructionPointerRanges[index].StartAddress < address) { if (instructionPointerRanges[index].EndAddress > address) { return(methods[index]); } } if (index == 0) { return(null); } index--; if (instructionPointerRanges[index].StartAddress < address) { if (instructionPointerRanges[index].EndAddress > address) { return(methods[index]); } } return(null); } } } Dictionary <MethodDesc, MethodChunks> instrumentationDataByMethod = new Dictionary <MethodDesc, MethodChunks>(); foreach (var e in p.EventsInProcess.ByEventType <JitInstrumentationDataVerboseTraceData>()) { AddToInstrumentationData(e.ClrInstanceID, e.MethodID, e.MethodFlags, e.Data); } foreach (var e in p.EventsInProcess.ByEventType <JitInstrumentationDataTraceData>()) { AddToInstrumentationData(e.ClrInstanceID, e.MethodID, e.MethodFlags, e.Data); } // Local function used with the above two loops as the behavior is supposed to be identical void AddToInstrumentationData(int eventClrInstanceId, long methodID, int methodFlags, byte[] data) { if (eventClrInstanceId != clrInstanceId.Value) { return; } MethodDesc method = null; try { method = idParser.ResolveMethodID(methodID, commandLineOptions.VerboseWarnings); } catch (Exception) { } if (method != null) { if (!instrumentationDataByMethod.TryGetValue(method, out MethodChunks perMethodChunks)) { perMethodChunks = new MethodChunks(); instrumentationDataByMethod.Add(method, perMethodChunks); } const int FinalChunkFlag = unchecked ((int)0x80000000); int chunkIndex = methodFlags & ~FinalChunkFlag; if ((chunkIndex != (perMethodChunks.LastChunk + 1)) || perMethodChunks.Done) { instrumentationDataByMethod.Remove(method); return; } perMethodChunks.LastChunk = perMethodChunks.InstrumentationData.Count; perMethodChunks.InstrumentationData.Add(data); if ((methodFlags & FinalChunkFlag) == FinalChunkFlag) { perMethodChunks.Done = true; } } } if (commandLineOptions.DisplayProcessedEvents) { foreach (var entry in methodsToAttemptToPrepare) { MethodDesc method = entry.Value.Method; string reason = entry.Value.Reason; PrintOutput($"{entry.Value.Millisecond.ToString("F4")} {reason} {method}"); } } PrintMessage($"Done processing input file"); if (commandLineOptions.OutputFileName == null) { return(0); } // Deduplicate entries HashSet <MethodDesc> methodsInListAlready = new HashSet <MethodDesc>(); List <ProcessedMethodData> methodsUsedInProcess = new List <ProcessedMethodData>(); PgoDataLoader pgoDataLoader = new PgoDataLoader(idParser); foreach (var entry in methodsToAttemptToPrepare) { if (methodsInListAlready.Add(entry.Value.Method)) { var methodData = entry.Value; if (commandLineOptions.GenerateCallGraph) { exclusiveSamples.TryGetValue(methodData.Method, out methodData.ExclusiveWeight); callGraph.TryGetValue(methodData.Method, out methodData.WeightedCallData); } if (instrumentationDataByMethod.TryGetValue(methodData.Method, out MethodChunks chunks)) { int size = 0; foreach (byte[] arr in chunks.InstrumentationData) { size += arr.Length; } byte[] instrumentationData = new byte[size]; int offset = 0; foreach (byte[] arr in chunks.InstrumentationData) { arr.CopyTo(instrumentationData, offset); offset += arr.Length; } var intDecompressor = new PgoProcessor.PgoEncodedCompressedIntParser(instrumentationData, 0); methodData.InstrumentationData = PgoProcessor.ParsePgoData <TypeSystemEntityOrUnknown>(pgoDataLoader, intDecompressor, true).ToArray(); } methodsUsedInProcess.Add(methodData); } } if (commandLineOptions.FileType.Value == PgoFileType.jittrace) { GenerateJittraceFile(commandLineOptions.OutputFileName, methodsUsedInProcess, commandLineOptions.JitTraceOptions); } else if (commandLineOptions.FileType.Value == PgoFileType.mibc) { 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(0); }