private int InnerProcessTraceFileMain()

in src/coreclr/tools/dotnet-pgo/Program.cs [988:1881]


        private int InnerProcessTraceFileMain()
        {
            string outputPath = Get(_command.OutputFilePath);
            if (outputPath == null)
            {
                PrintError("Output filename must be specified");
                return -8;
            }

            if ((_command.FileType.Value != PgoFileType.jittrace) && (_command.FileType != PgoFileType.mibc))
            {
                PrintError($"Invalid output pgo type {_command.FileType} specified.");
                return -9;
            }
            if (_command.FileType == PgoFileType.jittrace)
            {
                if (!outputPath.EndsWith(".jittrace"))
                {
                    PrintError($"jittrace output file name must end with .jittrace");
                    return -9;
                }
            }
            if (_command.FileType == PgoFileType.mibc)
            {
                if (!outputPath.EndsWith(".mibc"))
                {
                    PrintError($"mibc output file name must end with .mibc");
                    return -9;
                }
            }

            string etlFileName = Get(_command.TraceFilePath);
            foreach (string nettraceExtension in new string[] { ".netperf", ".netperf.zip", ".nettrace" })
            {
                if (etlFileName.EndsWith(nettraceExtension))
                {
                    string etlxFileName = Path.ChangeExtension(etlFileName, ".etlx");
                    PrintMessage($"Creating ETLX file {etlxFileName} from {etlFileName}");
                    TraceLog.CreateFromEventPipeDataFile(etlFileName, etlxFileName);
                    etlFileName = etlxFileName;
                }
            }

            string lttngExtension = ".trace.zip";
            if (etlFileName.EndsWith(lttngExtension))
            {
                string etlxFileName = Path.ChangeExtension(etlFileName, ".etlx");
                PrintMessage($"Creating ETLX file {etlxFileName} from {etlFileName}");
                TraceLog.CreateFromLttngTextDataFile(etlFileName, etlxFileName);
                etlFileName = etlxFileName;
            }

            UnZipIfNecessary(ref etlFileName, _command.BasicProgressMessages ? Console.Out : new StringWriter());

            // For SPGO we need to be able to map raw IPs back to IL offsets in methods.
            // Normally TraceEvent facilitates this remapping automatically and discards the IL<->IP mapping table events.
            // However, we have found TraceEvent's remapping to be imprecise (see https://github.com/microsoft/perfview/issues/1410).
            // Thus, when SPGO is requested, we need to keep these events.
            // Note that we always request these events to be kept because if one switches back and forth between SPGO and non-SPGO,
            // the cached .etlx file will not update.
            using (var traceLog = TraceLog.OpenOrConvert(etlFileName, new TraceLogOptions { KeepAllEvents = true }))
            {
                bool hasPid = IsSet(_command.Pid);
                string processName = Get(_command.ProcessName);
                if (hasPid && 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.OrderByDescending(proc => proc.CPUMSec))
                    {
                        PrintOutput($"Procname = {proc.Name} Pid = {proc.ProcessID} CPUMsec = {proc.CPUMSec:F0}");
                    }
                    return 1;
                }

                if (hasPid && processName != null)
                {
                    PrintError("--pid and --process-name cannot be specified together");
                    return -1;
                }

                // For a particular process
                TraceProcess p;
                if (hasPid)
                {
                    p = traceLog.Processes.LastProcessWithID(Get(_command.Pid));
                }
                else if (processName != null)
                {
                    List<TraceProcess> matchingProcesses = new List<TraceProcess>();
                    foreach (TraceProcess proc in traceLog.Processes)
                    {
                        if (String.Compare(proc.Name, 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() &&
                    !p.EventsInProcess.ByEventType<R2RGetEntryPointTraceData>().Any() &&
                    !p.EventsInProcess.ByEventType< SampledProfileTraceData>().Any())
                {
                    PrintError($"No data in trace for process\nWas the trace collected with provider at least \"Microsoft-Windows-DotNETRuntime:0x4000080018:5\"?");
                    return -5;
                }

                PgoTraceProcess pgoProcess = new PgoTraceProcess(p);
                int clrInstanceId = Get(_command.ClrInstanceId);
                if (!IsSet(_command.ClrInstanceId))
                {
                    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, s_logger, Get(_command.AutomaticReferences));

                if (_command.VerboseWarnings)
                    PrintWarning($"{traceLog.EventsLost} Lost events");

                bool filePathError = false;
                HashSet<ModuleDesc> modulesLoadedViaReference = new HashSet<ModuleDesc>();
                foreach (string file in Get(_command.Reference))
                {
                    try
                    {
                        if (!File.Exists(file))
                        {
                            PrintError($"Unable to find reference '{file}'");
                            filePathError = true;
                        }
                        else
                        {
                            var module = tsc.GetModuleFromPath(file, throwIfNotLoadable: false);
                            if (module != null)
                                modulesLoadedViaReference.Add(module);
                        }
                    }
                    catch (Internal.TypeSystem.TypeSystemException.BadImageFormatException)
                    {
                        // Ignore BadImageFormat in order to allow users to use '-r *.dll'
                        // in a folder with native dynamic libraries (which have the same extension on Windows).

                        // We don't need to log a warning here - it's already logged in GetModuleFromPath
                    }
                }

                if (filePathError)
                    return -6;

                if (!tsc.Initialize())
                    return -12;

                Dictionary<string, HashSet<string>> duplicateModuleAnalysis = new Dictionary<string, HashSet<string>>(StringComparer.OrdinalIgnoreCase);
                foreach (var module in pgoProcess.EnumerateLoadedManagedModules())
                {
                    var managedModule = module.ManagedModule;

                    if (module.ClrInstanceID != clrInstanceId)
                        continue;

                    if (managedModule.ModuleFile != null)
                    {
                        string simpleName = managedModule.ModuleFile.Name;
                        if (simpleName.EndsWith(".il"))
                            simpleName = simpleName.Substring(0, simpleName.Length - 3);

                        string filePathTemp = PgoTraceProcess.ComputeFilePathOnDiskForModule(managedModule);
                        string candidateFilePath;

                        // This path may be normalized
                        if (File.Exists(filePathTemp) || !tsc._normalizedFilePathToFilePath.TryGetValue(filePathTemp, out candidateFilePath))
                            candidateFilePath = filePathTemp;

                        if (!duplicateModuleAnalysis.TryGetValue(simpleName, out HashSet<string> candidatePaths))
                        {
                            duplicateModuleAnalysis[simpleName] = candidatePaths = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
                        }
                        candidatePaths.Add(candidateFilePath);
                    }
                }

                bool duplicateError = false;
                foreach (var assembliesWithDuplicates in duplicateModuleAnalysis)
                {
                    if (assembliesWithDuplicates.Value.Count == 1)
                        continue;

                    ModuleDesc loadedViaReference = null;
                    foreach (var module in modulesLoadedViaReference)
                    {
                        if (string.Equals(module.Assembly.GetName().Name, assembliesWithDuplicates.Key, StringComparison.OrdinalIgnoreCase))
                        {
                            loadedViaReference = module;
                            break;
                        }
                    }

                    // AutomaticReferences set to false disables this error, as no more references can actually be loaded past this point and cause a problem.
                    if (loadedViaReference == null && Get(_command.AutomaticReferences))
                    {
                        duplicateError = true;
                        PrintError($"Multiple assemblies with the same simple name loaded into the process. Specify the preferred module via the -reference parameter.");
                        foreach (string path in assembliesWithDuplicates.Value)
                        {
                            PrintMessage(path);
                        }
                    }
                }
                if (duplicateError)
                    return -13;

                TraceRuntimeDescToTypeSystemDesc idParser = new TraceRuntimeDescToTypeSystemDesc(p, tsc, clrInstanceId);

                int mismatchErrors = 0;
                foreach (var e in p.EventsInProcess.ByEventType<ModuleLoadUnloadTraceData>())
                {
                    ModuleDesc loadedModule = idParser.ResolveModuleID(e.ModuleID, false);
                    if (loadedModule == null)
                    {
                        if (!idParser.IsDynamicModuleID(e.ModuleID))
                            PrintWarning($"Unable to find loaded module {e.ModuleILFileName} to verify match");
                        continue;
                    }

                    EcmaModule ecmaModule = loadedModule as EcmaModule;
                    if (ecmaModule == null)
                    {
                        continue;
                    }

                    bool matched = false;
                    bool mismatch = false;
                    bool mismatchHandled = false;
                    foreach (DebugDirectoryEntry debugEntry in ecmaModule.PEReader.SafeReadDebugDirectory())
                    {
                        if (debugEntry.Type == DebugDirectoryEntryType.CodeView)
                        {
                            var codeViewData = ecmaModule.PEReader.ReadCodeViewDebugDirectoryData(debugEntry);
                            if (codeViewData.Path.EndsWith("ni.pdb"))
                                continue;
                            if (codeViewData.Guid != e.ManagedPdbSignature)
                            {
                                if (modulesLoadedViaReference.Contains(ecmaModule) && duplicateModuleAnalysis[ecmaModule.Assembly.GetName().Name].Count > 1)
                                {
                                    // This is the case where a duplicate dll mismatch was avoided by specifying a -reference parameter
                                    PrintMessage($"Disabling load of assembly data from assembly located at \"{e.ModuleILPath}\" during trace collection as module \"{tsc.PEReaderToFilePath(ecmaModule.PEReader)}\" is preferred, and does not match");
                                    idParser.RemoveModuleIDFromLoader(e.ModuleID);
                                    mismatchHandled = true;
                                }
                                else
                                {
                                    PrintError($"Dll mismatch between assembly located at \"{e.ModuleILPath}\" during trace collection and module \"{tsc.PEReaderToFilePath(ecmaModule.PEReader)}\"");
                                    mismatchErrors++;
                                    mismatch = true;
                                }
                                continue;
                            }
                            else
                            {
                                matched = true;
                            }
                        }
                    }

                    if (!matched && !mismatch && !mismatchHandled)
                    {
                        PrintMessage($"Unable to validate match between assembly located at \"{e.ModuleILPath}\" during trace collection and module \"{tsc.PEReaderToFilePath(ecmaModule.PEReader)}\"");
                    }

                    // TODO find some way to match on MVID as only some dlls have managed pdbs, and this won't find issues with embedded pdbs
                }

                if (mismatchErrors != 0)
                {
                    PrintError($"{mismatchErrors} mismatch error(s) found");
                    return -1;
                }

                // Now that the modules are validated run Init to prepare for the rest of execution
                idParser.Init();

                SortedDictionary<long, ProcessedMethodData> methodsToAttemptToPrepare = new SortedDictionary<long, ProcessedMethodData>();

                double excludeEventsBefore = Get(_command.ExcludeEventsBefore);
                double excludeEventsAfter = Get(_command.ExcludeEventsAfter);
                Regex excludeEventsBeforeJittingMethod = !string.IsNullOrEmpty(Get(_command.ExcludeEventsBeforeJittingMethod)) ? new Regex(Get(_command.ExcludeEventsBeforeJittingMethod)) : null;
                Regex excludeEventsAfterJittingMethod = !string.IsNullOrEmpty(Get(_command.ExcludeEventsAfterJittingMethod)) ? new Regex(Get(_command.ExcludeEventsAfterJittingMethod)) : null;
                Regex includeMethods = !string.IsNullOrEmpty(Get(_command.IncludeMethods)) ? new Regex(Get(_command.IncludeMethods)) : null;
                Regex excludeMethods = !string.IsNullOrEmpty(Get(_command.ExcludeMethods)) ? new Regex(Get(_command.ExcludeMethods)) : null;

                // Find all the R2RLoad events.
                if (_command.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)
                        {
                            if (!_command.Warnings)
                                continue;

                            PrintWarning($"Skipped R2REntryPoint {methodNameFromEventDirectly} due to ClrInstanceID of {e.ClrInstanceID}");
                            continue;
                        }

                        MethodDesc method = null;
                        string extraWarningText = null;
                        bool failedDueToNonloadableModule = false;
                        try
                        {
                            method = idParser.ResolveMethodID(e.MethodID, out failedDueToNonloadableModule, _command.VerboseWarnings);
                        }
                        catch (Exception exception)
                        {
                            extraWarningText = exception.ToString();
                        }

                        if (method == null)
                        {
                            if ((e.MethodNamespace == "dynamicClass") || failedDueToNonloadableModule || !_command.Warnings)
                                continue;

                            PrintWarning($"Unable to parse {methodNameFromEventDirectly} when looking up R2R methods");
                            if (extraWarningText != null)
                                PrintWarning(extraWarningText);
                            continue;
                        }

                        if (e.TimeStampRelativeMSec < excludeEventsBefore)
                        {
                            continue;
                        }

                        if (e.TimeStampRelativeMSec > excludeEventsAfter)
                        {
                            break;
                        }

                        string perfviewMethodName = e.MethodNamespace + "." + e.MethodName + paramsArgs;
                        if (PassesMethodFilter(includeMethods, excludeMethods, perfviewMethodName))
                        {
                            methodsToAttemptToPrepare.Add((int)e.EventIndex, new ProcessedMethodData(e.TimeStampRelativeMSec, method, "R2RLoad"));
                        }
                    }
                }

                // In case requesting events before/after jitting a method, discover the
                // corresponding excludeEventsBefore/excludeEventsAfter in event stream based
                // on filter criterias.
                if (_command.ProcessJitEvents && (excludeEventsBeforeJittingMethod != null || excludeEventsAfterJittingMethod != null))
                {
                    double firstMatchEventsBeforeJittingMethod = double.PositiveInfinity;
                    double lastMatchEventsAfterJittingMethod = double.NegativeInfinity;
                    foreach (var e in p.EventsInProcess.ByEventType<MethodJittingStartedTraceData>())
                    {
                        if (e.ClrInstanceID != clrInstanceId)
                        {
                            continue;
                        }

                        MethodDesc method = null;
                        bool failedDueToNonloadableModule = false;
                        try
                        {
                            method = idParser.ResolveMethodID(e.MethodID, out failedDueToNonloadableModule, false);
                        }
                        catch { }

                        if (method == null)
                        {
                            continue;
                        }

                        int parenIndex = e.MethodSignature.IndexOf('(');
                        string paramsArgs = e.MethodSignature.Substring(parenIndex);
                        string perfviewMethodName = e.MethodNamespace + "." + e.MethodName + paramsArgs;

                        if (e.TimeStampRelativeMSec > excludeEventsBefore && e.TimeStampRelativeMSec < firstMatchEventsBeforeJittingMethod && excludeEventsBeforeJittingMethod != null && excludeEventsBeforeJittingMethod.IsMatch(perfviewMethodName))
                        {
                            firstMatchEventsBeforeJittingMethod = e.TimeStampRelativeMSec;
                        }

                        if (e.TimeStampRelativeMSec < excludeEventsAfter && e.TimeStampRelativeMSec > lastMatchEventsAfterJittingMethod && excludeEventsAfterJittingMethod != null && excludeEventsAfterJittingMethod.IsMatch(perfviewMethodName))
                        {
                            lastMatchEventsAfterJittingMethod = e.TimeStampRelativeMSec;
                        }
                    }

                    if (firstMatchEventsBeforeJittingMethod < double.PositiveInfinity)
                    {
                        excludeEventsBefore = firstMatchEventsBeforeJittingMethod;
                    }

                    if (lastMatchEventsAfterJittingMethod > double.NegativeInfinity)
                    {
                        excludeEventsAfter = lastMatchEventsAfterJittingMethod;
                    }

                    if (excludeEventsBefore > excludeEventsAfter)
                    {
                        PrintError($"Exclude events before timestamp: \"{excludeEventsBefore}\" can't be later than exclude events after timestamp: \"{excludeEventsAfter}\"");
                        return -1;
                    }
                }

                // Find all the jitStart events.
                if (_command.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)
                        {
                            if (!_command.Warnings)
                                continue;

                            PrintWarning($"Skipped {methodNameFromEventDirectly} due to ClrInstanceID of {e.ClrInstanceID}");
                            continue;
                        }

                        MethodDesc method = null;
                        string extraWarningText = null;
                        bool failedDueToNonloadableModule = false;
                        try
                        {
                            method = idParser.ResolveMethodID(e.MethodID, out failedDueToNonloadableModule, _command.VerboseWarnings);
                        }
                        catch (Exception exception)
                        {
                            extraWarningText = exception.ToString();
                        }

                        if (method == null)
                        {
                            if ((e.MethodNamespace == "dynamicClass") || failedDueToNonloadableModule || !_command.Warnings)
                                continue;

                            PrintWarning($"Unable to parse {methodNameFromEventDirectly}");
                            if (extraWarningText != null)
                                PrintWarning(extraWarningText);
                            continue;
                        }

                        if (e.TimeStampRelativeMSec < excludeEventsBefore)
                        {
                            continue;
                        }

                        if (e.TimeStampRelativeMSec > excludeEventsAfter)
                        {
                            break;
                        }

                        string perfviewMethodName = e.MethodNamespace + "." + e.MethodName + paramsArgs;
                        if (PassesMethodFilter(includeMethods, excludeMethods, perfviewMethodName))
                        {
                            methodsToAttemptToPrepare.Add((int)e.EventIndex, new ProcessedMethodData(e.TimeStampRelativeMSec, method, "JitStart"));
                        }
                    }
                }

                MethodMemoryMap methodMemMap = null;
                MethodMemoryMap GetMethodMemMap()
                {
                    if (methodMemMap == null)
                    {
                        methodMemMap = new MethodMemoryMap(
                            p,
                            tsc,
                            idParser,
                            clrInstanceId,
                            Get(_command.PreciseDebugInfoFile),
                            s_logger);
                    }

                    return methodMemMap;
                }

                Dictionary<MethodDesc, Dictionary<MethodDesc, int>> callGraph = null;
                Dictionary<MethodDesc, int> exclusiveSamples = null;
                if (_command.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>();

                    MethodMemoryMap mmap = GetMethodMemMap();
                    foreach (var e in p.EventsInProcess.ByEventType<SampledProfileTraceData>())
                    {
                        if ((e.TimeStampRelativeMSec < excludeEventsBefore) && (e.TimeStampRelativeMSec > excludeEventsAfter))
                            continue;

                        var callstack = e.CallStack();
                        if (callstack == null)
                            continue;

                        ulong address1 = callstack.CodeAddress.Address;
                        MethodDesc topOfStackMethod = mmap.GetMethod(address1);
                        MethodDesc nextMethod = null;
                        if (callstack.Caller != null)
                        {
                            ulong address2 = callstack.Caller.CodeAddress.Address;
                            nextMethod = mmap.GetMethod(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(0x100000000 + (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;
                            }
                        }
                    }
                }

                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)
                    {
                        return;
                    }

                    MethodDesc method = null;
                    try
                    {
                        method = idParser.ResolveMethodID(methodID, out _, _command.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;
                    }
                }

                SampleCorrelator correlator = null;
                if (Get(_command.Spgo))
                {
                    correlator = new SampleCorrelator(GetMethodMemMap());

                    Guid lbrGuid = Guid.Parse("99134383-5248-43fc-834b-529454e75df3");
                    bool hasLbr = traceLog.Events.Any(e => e.TaskGuid == lbrGuid);

                    if (!hasLbr)
                    {
                        foreach (SampledProfileTraceData e in p.EventsInProcess.ByEventType<SampledProfileTraceData>())
                        {
                            correlator.AttributeSamplesToIP(e.InstructionPointer, 1);
                        }

                        PrintOutput($"Samples outside managed code: {correlator.SamplesOutsideManagedCode}");
                        PrintOutput($"Samples in managed code that does not have native<->IL mappings: {correlator.SamplesInManagedCodeWithoutAnyMappings}");
                        PrintOutput($"Samples in managed code with mappings that could not be correlated: {correlator.SamplesInManagedCodeOutsideMappings}");
                        PrintOutput($"Samples in inlinees that were not present in ETW events: {correlator.SamplesInUnknownInlinees}");
                        PrintOutput($"Samples in managed code for which we could not get the IL: {correlator.SamplesInManagedCodeWithoutIL}");
                        PrintOutput($"Samples in managed code that could not be attributed to the method's flow graph: {correlator.SamplesInManagedCodeOutsideFlowGraph}");
                        PrintOutput($"Samples successfully attributed: {correlator.TotalAttributedSamples}");
                    }
                    else
                    {
                        long numLbrRecords = 0;
                        foreach (var e in traceLog.Events)
                        {
                            if (e.TaskGuid != lbrGuid)
                                continue;

                            // Opcode is always 32 for the LBR event.
                            if (e.Opcode != (TraceEventOpcode)32)
                                continue;

                            numLbrRecords++;

                            unsafe
                            {
                                if (traceLog.PointerSize == 4)
                                {
                                    // For 32-bit machines we convert the data into a 64-bit format first.
                                    LbrTraceEventData32* data = (LbrTraceEventData32*)e.DataStart;
                                    if (data->ProcessId != p.ProcessID)
                                        continue;

                                    Span<LbrEntry32> lbr32 = LbrTraceEventData32.Entries(ref *data, e.EventDataLength);
                                    correlator.AttributeSampleToLbrRuns(lbr32);
                                }
                                else
                                {
                                    Trace.Assert(traceLog.PointerSize == 8, $"Unexpected PointerSize {traceLog.PointerSize}");

                                    LbrTraceEventData64* data = (LbrTraceEventData64*)e.DataStart;
                                    // TODO: The process ID check is not sufficient as PIDs can be reused, so we need to use timestamps too,
                                    // but we do not have access to PerfView functions to convert it. Hopefully TraceEvent will handle this
                                    // for us in the future.
                                    if (data->ProcessId != p.ProcessID)
                                        continue;

                                    Span<LbrEntry64> lbr64 = LbrTraceEventData64.Entries(ref *data, e.EventDataLength);
                                    correlator.AttributeSampleToLbrRuns(lbr64);
                                }
                            }
                        }

                        PrintOutput($"Profile is based on {numLbrRecords} LBR records");
                    }

                    correlator.SmoothAllProfiles();
                }

                if (_command.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");

                // 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 (_command.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, TypeSystemEntityOrUnknown>(pgoDataLoader, intDecompressor, true).ToArray();
                        }
                        else
                        {
                            SampleProfile sp = correlator?.GetProfile(methodData.Method);
                            if (sp != null && sp.AttributedSamples >= Get(_command.SpgoMinSamples))
                            {
                                IEnumerable<PgoSchemaElem> schema =
                                    sp.SmoothedSamples
                                    .Select(
                                        kvp =>
                                        new PgoSchemaElem
                                        {
                                            InstrumentationKind = kvp.Value > uint.MaxValue ? PgoInstrumentationKind.BasicBlockLongCount : PgoInstrumentationKind.BasicBlockIntCount,
                                            ILOffset = kvp.Key.Start,
                                            Count = 1,
                                            DataLong = kvp.Value,
                                        });

                                bool includeFullGraphs = Get(_command.IncludeFullGraphs);
                                if (includeFullGraphs)
                                {
                                    schema = schema.Concat(
                                        sp.SmoothedEdgeSamples
                                        .Select(kvp =>
                                            new PgoSchemaElem
                                            {
                                                InstrumentationKind = kvp.Value > uint.MaxValue ? PgoInstrumentationKind.EdgeLongCount : PgoInstrumentationKind.EdgeIntCount,
                                                ILOffset = kvp.Key.Item1.Start,
                                                Other = kvp.Key.Item2.Start,
                                                Count = 1,
                                                DataLong = kvp.Value
                                            }));
                                }

                                methodData.InstrumentationData = schema.ToArray();

#if DEBUG
                                if (includeFullGraphs)
                                {
                                    var writtenBlocks =
                                        new HashSet<int>(
                                            methodData.InstrumentationData
                                            .Where(elem => elem.InstrumentationKind == PgoInstrumentationKind.BasicBlockIntCount || elem.InstrumentationKind == PgoInstrumentationKind.BasicBlockLongCount)
                                            .Select(elem => elem.ILOffset));

                                    var writtenEdges =
                                        new HashSet<(int, int)>(
                                            methodData.InstrumentationData
                                            .Where(elem => elem.InstrumentationKind == PgoInstrumentationKind.EdgeIntCount || elem.InstrumentationKind == PgoInstrumentationKind.EdgeLongCount)
                                            .Select(elem => (elem.ILOffset, elem.Other)));

                                    Debug.Assert(writtenBlocks.SetEquals(sp.FlowGraph.BasicBlocks.Select(bb => bb.Start)));
                                    Debug.Assert(writtenEdges.SetEquals(sp.FlowGraph.BasicBlocks.SelectMany(bb => bb.Targets.Select(bbTar => (bb.Start, bbTar.Start)))));
                                }
#endif
                            }
                        }

                        methodsUsedInProcess.Add(methodData);
                    }
                }

                FileInfo outputFileInfo = new(outputPath);
                if (_command.FileType.Value == PgoFileType.jittrace)
                    GenerateJittraceFile(outputFileInfo, methodsUsedInProcess, _command.JitTraceOptions);
                else if (_command.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();
                    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(config, tsc, outputFileInfo, methodProfileData, _command.ValidateOutputFile, !Get(_command.Compressed));
                }
            }
            return 0;
        }