// Method copied from https://github.com/dotnet/diagnostics/blob/2c23d3265dd8f642a8d6cf4bb8a135a5ff8b00c2/src/Tools/dotnet-trace/TraceFileFormatConverter.cs#L64 private static void ConvertToSpeedscope(string fileToConvert, string outputFilename, bool continueOnError = false) { var etlxFilePath = TraceLog.CreateFromEventPipeDataFile(fileToConvert, null, new TraceLogOptions() { ContinueOnError = continueOnError }); using (var symbolReader = new SymbolReader(System.IO.TextWriter.Null) { SymbolPath = SymbolPath.MicrosoftSymbolServerPath }) using (var eventLog = new TraceLog(etlxFilePath)) { var stackSource = new MutableTraceEventStackSource(eventLog) { OnlyManagedCodeStacks = true // EventPipe currently only has managed code stacks. }; var computer = new SampleProfilerThreadTimeComputer(eventLog, symbolReader) { IncludeEventSourceEvents = false // SpeedScope handles only CPU samples, events are not supported }; computer.GenerateThreadTimeStacks(stackSource); SpeedScopeStackSourceWriter.WriteStackViewAsJson(stackSource, outputFilename); } if (File.Exists(etlxFilePath)) { File.Delete(etlxFilePath); } }
private static void ConvertToSpeedscope(string fileToConvert, string outputFilename) { var etlxFilePath = TraceLog.CreateFromEventPipeDataFile(fileToConvert); using (var symbolReader = new SymbolReader(System.IO.TextWriter.Null) { SymbolPath = SymbolPath.MicrosoftSymbolServerPath }) using (var eventLog = new TraceLog(etlxFilePath)) { var stackSource = new MutableTraceEventStackSource(eventLog) { OnlyManagedCodeStacks = true // EventPipe currently only has managed code stacks. }; var computer = new SampleProfilerThreadTimeComputer(eventLog, symbolReader); computer.GenerateThreadTimeStacks(stackSource); SpeedScopeStackSourceWriter.WriteStackViewAsJson(stackSource, outputFilename); } if (File.Exists(etlxFilePath)) { File.Delete(etlxFilePath); } }
private static void ConvertToSpeedscope(string fileToConvert) { var symbolReader = new SymbolReader(System.IO.TextWriter.Null) { SymbolPath = SymbolPath.MicrosoftSymbolServerPath }; var etlxFilePath = TraceLog.CreateFromEventPipeDataFile(fileToConvert); var eventLog = new TraceLog(etlxFilePath); try { var stackSource = new MutableTraceEventStackSource(eventLog) { OnlyManagedCodeStacks = true // EventPipe currently only has managed code stacks. }; var computer = new SampleProfilerThreadTimeComputer(eventLog, symbolReader); computer.GenerateThreadTimeStacks(stackSource); var speedScopeFilePath = Path.ChangeExtension(fileToConvert, "speedscope.json"); SpeedScopeStackSourceWriter.WriteStackViewAsJson(stackSource, speedScopeFilePath); } finally { eventLog.Dispose(); if (File.Exists(etlxFilePath)) { File.Delete(etlxFilePath); } } }
public void RunTests() { // Get the configuration and start tracing. TraceConfiguration traceConfig = GenerateConfiguration(); TraceControl.Enable(traceConfig); // Run the tests. foreach (EventSourceTest test in m_tests) { test.LogEvent(); } // Stop tracing. TraceControl.Disable(); // Open the trace file. string traceLogPath = TraceLog.CreateFromEventPipeDataFile(m_file.Path); using (TraceLog traceLog = new TraceLog(traceLogPath)) { TraceEventDispatcher dispatcher = traceLog.Events.GetSource(); dispatcher.Dynamic.All += delegate(TraceEvent data) { if (data.ProviderName.EndsWith("Rundown")) { return; } if (data.ProviderName.Equals("Microsoft-DotNETCore-EventPipe")) { return; } Assert.True($"m_nextTestVerificationIndex({m_nextTestVerificationIndex}) < m_tests.Count({m_tests.Count})", m_nextTestVerificationIndex < m_tests.Count); try { Console.WriteLine($"Verifying Event: {data.ToString()}"); m_tests[m_nextTestVerificationIndex].VerifyEvent(data); } catch { Console.WriteLine($"Failure during test '{m_tests[m_nextTestVerificationIndex].Name}'."); throw; } m_nextTestVerificationIndex++; }; dispatcher.Process(); Assert.Equal("Test Count", m_tests.Count, m_nextTestVerificationIndex); } }
private static void DumpNetTrace(string filePath) { using (var trace = new TraceLog(TraceLog.CreateFromEventPipeDataFile(filePath)).Events.GetSource()) { trace.Dynamic.All += (TraceEvent e) => { if (!string.IsNullOrWhiteSpace(e.ProviderName) && !string.IsNullOrWhiteSpace(e.EventName)) { Debug.WriteLine($"Event Provider: {e.ProviderName}"); Debug.WriteLine($" Event Name: {e.EventName}"); } }; trace.Process(); } }
private static void ValidateNetTrace(string filePath) { var nEventPipeResults = 0; using (var trace = new TraceLog(TraceLog.CreateFromEventPipeDataFile(filePath)).Events.GetSource()) { trace.Dynamic.All += (TraceEvent data) => { ++nEventPipeResults; }; trace.Process(); } // Assert there were events in the file. Assert.NotEqual("Found events in trace file", nEventPipeResults, 0); }
private static void Convert(TraceFileFormat format, string fileToConvert, string outputFilename, bool continueOnError = false) { var etlxFilePath = TraceLog.CreateFromEventPipeDataFile(fileToConvert, null, new TraceLogOptions() { ContinueOnError = continueOnError }); using (var symbolReader = new SymbolReader(TextWriter.Null) { SymbolPath = SymbolPath.MicrosoftSymbolServerPath }) using (var eventLog = new TraceLog(etlxFilePath)) { var stackSource = new MutableTraceEventStackSource(eventLog) { OnlyManagedCodeStacks = true // EventPipe currently only has managed code stacks. }; var computer = new SampleProfilerThreadTimeComputer(eventLog, symbolReader) { IncludeEventSourceEvents = false // SpeedScope handles only CPU samples, events are not supported }; computer.GenerateThreadTimeStacks(stackSource); switch (format) { case TraceFileFormat.Speedscope: SpeedScopeStackSourceWriter.WriteStackViewAsJson(stackSource, outputFilename); break; case TraceFileFormat.Chromium: ChromiumStackSourceWriter.WriteStackViewAsJson(stackSource, outputFilename, compress: false); break; default: // we should never get here throw new ArgumentException($"Invalid TraceFileFormat \"{format}\""); } } if (File.Exists(etlxFilePath)) { File.Delete(etlxFilePath); } }
public void GuidsInMetadataAreSupported() { PrepareTestData(); const string eventPipeFileName = "eventpipe-dotnetcore2.1-linux-x64-objver3.netperf"; Guid ExpectedActivityId = new Guid("10000000-0000-0000-0000-000000000001"); string eventPipeFilePath = Path.Combine(UnZippedDataDir, eventPipeFileName); bool activityIdHasBeenSet = false; using (var traceSource = new TraceLog(TraceLog.CreateFromEventPipeDataFile(eventPipeFilePath)).Events.GetSource()) { traceSource.AllEvents += (TraceEvent @event) => { // before the activity Id is set, it's empty // after the activity Id is set, all the events should have it (here we had only 1 thread) Assert.Equal(activityIdHasBeenSet ? ExpectedActivityId : Guid.Empty, @event.ActivityID); // the EVENTID for SetActivityId is 25 https://github.com/dotnet/coreclr/blob/c67c29d6e226e4cca1f1efb4d57b7f498d58b534/src/mscorlib/src/System/Threading/Tasks/TPLETWProvider.cs#L524 if (@event.ProviderName != TplEtwProviderTraceEventParser.ProviderName || @event.ID != (TraceEventID)25) { return; } Assert.False(activityIdHasBeenSet); // make sure the event comes only once // Sneak in a test of the DLR support here (casting to 'dynamic' // instead of using PayloadByName("NewId")): if (((dynamic)@event).NewId.Equals(ExpectedActivityId)) { activityIdHasBeenSet = true; } }; traceSource.Process(); Assert.True(activityIdHasBeenSet); } }
public void Basic(string eventPipeFileName) { // Initialize PrepareTestData(); string eventPipeFilePath = Path.Combine(UnZippedDataDir, eventPipeFileName); Output.WriteLine(string.Format("Processing the file {0}, Making ETLX and scanning.", Path.GetFullPath(eventPipeFilePath))); var eventStatistics = new SortedDictionary <string, EventRecord>(StringComparer.Ordinal); using (var traceLog = new TraceLog(TraceLog.CreateFromEventPipeDataFile(eventPipeFilePath))) { var traceSource = traceLog.Events.GetSource(); traceSource.AllEvents += delegate(TraceEvent data) { string eventName = data.ProviderName + "/" + data.EventName; if (eventStatistics.ContainsKey(eventName)) { eventStatistics[eventName].TotalCount++; } else { eventStatistics[eventName] = new EventRecord() { TotalCount = 1, FirstSeriazliedSample = new String(data.ToString().Replace("\n", "\\n").Replace("\r", "\\r").Take(1000).ToArray()) }; } }; // Process traceSource.Process(); } // Validate ValidateEventStatistics(eventStatistics, eventPipeFileName); }
public void AllEventsLeavesNoUnhandledEvents() { PrepareTestData(); const string eventPipeFileName = "eventpipe-dotnetcore2.1-win-x86-objver3.netperf"; string eventPipeFilePath = Path.Combine(UnZippedDataDir, eventPipeFileName); using (var traceSource = new TraceLog(TraceLog.CreateFromEventPipeDataFile(eventPipeFilePath)).Events.GetSource()) { int dynamicAllInvocationCount = 0; int unhandledEvents = 0; traceSource.AllEvents += _ => dynamicAllInvocationCount++; traceSource.UnhandledEvents += _ => unhandledEvents++; traceSource.Process(); Assert.NotEqual(0, dynamicAllInvocationCount); Assert.Equal(0, unhandledEvents); } }
private static void RoundTwo(string[] args) { using (var netPerfFile = NetPerfFile.Create(args)) { using (var etlFile = EtlFile.Create(args)) { Console.WriteLine("\tStart: Enable EventPipe."); TraceControl.Enable(EventPipeGetConfig(EventPipeAndEtwEventSource.Log, EventPipeAndEtwEventSource.Keywords.EventPipeKeyword, netPerfFile.Path)); Console.WriteLine("\tEnd: Enable EventPipe.\n"); Console.WriteLine("\tStart: Enable ETW."); TraceEventSession etwSession = EnableETW(EventPipeAndEtwEventSource.Log, EventPipeAndEtwEventSource.Keywords.EtwKeyword, etlFile.Path); Console.WriteLine("\tEnd: Enable ETW.\n"); WriteAllEvents(EventPipeAndEtwEventSource.Log); Console.WriteLine("\tStart: Disable EventPipe."); TraceControl.Disable(); Console.WriteLine("\tEnd: Disable EventPipe.\n"); WriteAllEvents(EventPipeAndEtwEventSource.Log); Console.WriteLine("\tStart: Disable ETW."); DisableETW(etwSession); Console.WriteLine("\tEnd: Disable ETW.\n"); Console.WriteLine("\tStart: Processing events from EventPipe file."); EventResults eventPipeResults = new EventResults(); EventResults etwResults = new EventResults(); using (var trace = new TraceLog(TraceLog.CreateFromEventPipeDataFile(netPerfFile.Path)).Events.GetSource()) { trace.Dynamic.All += delegate(TraceEvent data) { eventPipeResults.AddEvent(data); }; trace.Process(); } // Validate EventPipe results. eventPipeResults.Print("EventPipe Results:"); Assert.Equal("EventPipeEvent1Count", eventPipeResults.Event1Count, 1); Assert.Equal("EventPipeEvent2Count", eventPipeResults.Event2Count, 0); Assert.Equal("EventPipeEvent3Count", eventPipeResults.Event3Count, 1); Console.WriteLine("\tEnd: Processing events from EventPipe file.\n"); Console.WriteLine("\tStart: Processing events from ETW file."); using (var trace = new ETWTraceEventSource(etlFile.Path)) { trace.Dynamic.All += delegate(TraceEvent data) { etwResults.AddEvent(data); }; trace.Process(); } // Validate ETW results. etwResults.Print("ETW Results:"); Assert.Equal("EventPipeEvent1Count", etwResults.Event1Count, 0); Assert.Equal("EventPipeEvent2Count", etwResults.Event2Count, 2); Assert.Equal("EventPipeEvent3Count", etwResults.Event3Count, 2); Console.WriteLine("\tEnd: Processing events from ETW file."); } } }
static int Main(string[] args) { bool pass = true; using (SimpleEventSource eventSource = new SimpleEventSource()) { using (var netPerfFile = NetPerfFile.Create(args)) { Console.WriteLine("\tStart: Enable tracing."); TraceControl.Enable(GetConfig(eventSource, netPerfFile.Path)); Console.WriteLine("\tEnd: Enable tracing.\n"); Console.WriteLine("\tStart: Messaging."); // Send messages // Use random numbers and addition as a simple, human readble checksum Random generator = new Random(); for (int i = 0; i < messageIterations; i++) { int x = generator.Next(1, 1000); int y = generator.Next(1, 1000); string formula = String.Format("{0} + {1} = {2}", x, y, x + y); eventSource.MathResult(x, y, x + y, formula); } Console.WriteLine("\tEnd: Messaging.\n"); Console.WriteLine("\tStart: Disable tracing."); TraceControl.Disable(); Console.WriteLine("\tEnd: Disable tracing.\n"); Console.WriteLine("\tStart: Processing events from file."); int msgCount = 0; using (var trace = new TraceLog(TraceLog.CreateFromEventPipeDataFile(netPerfFile.Path)).Events.GetSource()) { var names = new HashSet <string>(); trace.Dynamic.All += delegate(TraceEvent data) { if (!names.Contains(data.ProviderName)) { Console.WriteLine("\t{0}", data.ProviderName); names.Add(data.ProviderName); } if (data.ProviderName == "SimpleEventSource") { msgCount += 1; } }; trace.Process(); } Console.WriteLine("\tEnd: Processing events from file.\n"); Console.WriteLine("\tProcessed {0} events from EventSource", msgCount); pass &= msgCount == messageIterations; } } return(pass ? 100 : 0); }
public void CanConvertProvidedTraceFiles(string zippedTraceFileName) { var debugListenersCopy = new TraceListener[Debug.Listeners.Count]; Debug.Listeners.CopyTo(debugListenersCopy, index: 0); Debug.Listeners.Clear(); string fileToUnzip = Path.Combine("inputs", "speedscope", zippedTraceFileName); string unzippedFile = Path.ChangeExtension(fileToUnzip, string.Empty); if (File.Exists(unzippedFile)) { File.Delete(unzippedFile); } ZipFile.ExtractToDirectory(fileToUnzip, Path.GetDirectoryName(fileToUnzip)); var etlxFilePath = TraceLog.CreateFromEventPipeDataFile(unzippedFile, null, new TraceLogOptions() { ContinueOnError = true }); try { using (var symbolReader = new SymbolReader(TextWriter.Null) { SymbolPath = SymbolPath.MicrosoftSymbolServerPath }) using (var eventLog = new TraceLog(etlxFilePath)) { var stackSource = new MutableTraceEventStackSource(eventLog) { OnlyManagedCodeStacks = true // EventPipe currently only has managed code stacks. }; var computer = new SampleProfilerThreadTimeComputer(eventLog, symbolReader) { IncludeEventSourceEvents = false // SpeedScope handles only CPU samples, events are not supported }; computer.GenerateThreadTimeStacks(stackSource); var samplesPerThread = GetSortedSamplesPerThread(stackSource); var exportedFrameNameToExportedFrameId = new Dictionary <string, int>(); var exportedFrameIdToFrameTuple = new Dictionary <int, FrameInfo>(); var profileEventsPerThread = new Dictionary <string, IReadOnlyList <ProfileEvent> >(); foreach (var pair in samplesPerThread) { var sortedProfileEvents = GetProfileEvents(stackSource, pair.Value, exportedFrameNameToExportedFrameId, exportedFrameIdToFrameTuple); Assert.True(Validate(sortedProfileEvents), "The output should be always valid"); profileEventsPerThread.Add(pair.Key.Name, sortedProfileEvents); } ; } } finally { if (File.Exists(etlxFilePath)) { File.Delete(etlxFilePath); } if (File.Exists(unzippedFile)) { File.Delete(unzippedFile); } if (debugListenersCopy.Length > 0) { Debug.Listeners.AddRange(debugListenersCopy); } } }
static async Task <int> Main(string[] args) { if (args.Length == 0 || args.Length > 1 || (args.Length == 1 && _helpFlags.Contains(args[0]))) { PrintUsage(); return(-1); } var pid = int.Parse(args[0]); var tempNetTraceFilename = Path.GetRandomFileName() + ".nettrace"; var tempEtlxFilename = ""; try { var client = new DiagnosticsClient(pid); var providers = new List <EventPipeProvider>() { new EventPipeProvider("Microsoft-DotNETCore-SampleProfiler", EventLevel.Informational) }; using (EventPipeSession session = client.StartEventPipeSession(providers)) using (FileStream fs = File.OpenWrite(tempNetTraceFilename)) { Task copyTask = session.EventStream.CopyToAsync(fs); await Task.Delay(10); session.Stop(); await copyTask; } tempEtlxFilename = TraceLog.CreateFromEventPipeDataFile(tempNetTraceFilename); using (var symbolReader = new SymbolReader(System.IO.TextWriter.Null) { SymbolPath = SymbolPath.MicrosoftSymbolServerPath }) using (var eventLog = new TraceLog(tempEtlxFilename)) { var stackSource = new MutableTraceEventStackSource(eventLog) { OnlyManagedCodeStacks = true }; var computer = new SampleProfilerThreadTimeComputer(eventLog, symbolReader); computer.GenerateThreadTimeStacks(stackSource); var samplesForThread = new Dictionary <string, List <StackSourceSample> >(); stackSource.ForEach((sample) => { var stackIndex = sample.StackIndex; while (!stackSource.GetFrameName(stackSource.GetFrameIndex(stackIndex), false).StartsWith("Thread (")) { stackIndex = stackSource.GetCallerIndex(stackIndex); } var threadName = stackSource.GetFrameName(stackSource.GetFrameIndex(stackIndex), false); if (samplesForThread.TryGetValue(threadName, out var samples)) { samples.Add(sample); } else { samplesForThread[threadName] = new List <StackSourceSample>() { sample }; } }); foreach (var(threadName, samples) in samplesForThread) { #if DEBUG Console.WriteLine($"Found {samples.Count} stacks for thread {threadName}"); #endif PrintStack(threadName, samples[0], stackSource); } } return(0); } catch (System.Exception e) { Console.WriteLine(e); return(-1); } finally { if (File.Exists(tempNetTraceFilename)) { File.Delete(tempNetTraceFilename); } if (File.Exists(tempEtlxFilename)) { File.Delete(tempEtlxFilename); } } }
static int InnerMain(FileInfo traceFile, FileInfo outputFileName, int?pid, string processName, PgoFileType?pgoFileType, IEnumerable <FileInfo> reference, int?clrInstanceId = null, bool processJitEvents = true, bool processR2REvents = true, bool displayProcessedEvents = false, bool validateOutputFile = true, bool verboseWarnings = false, jittraceoptions jitTraceOptions = jittraceoptions.sorted, double excludeEventsBefore = 0, double excludeEventsAfter = Double.MaxValue, bool warnings = true) { s_reachedInnerMain = true; if (traceFile == null) { PrintUsage("--trace-file must be specified"); return(-8); } if (outputFileName != null) { if (!pgoFileType.HasValue) { PrintUsage($"--pgo-file-type must be specified"); return(-9); } if ((pgoFileType.Value != PgoFileType.jittrace) && (pgoFileType != PgoFileType.mibc)) { PrintUsage($"Invalid output pgo type {pgoFileType} specified."); return(-9); } if (pgoFileType == PgoFileType.jittrace) { if (!outputFileName.Name.EndsWith(".jittrace")) { PrintUsage($"jittrace output file name must end with .jittrace"); return(-9); } } if (pgoFileType == PgoFileType.mibc) { if (!outputFileName.Name.EndsWith(".mibc")) { PrintUsage($"jittrace output file name must end with .mibc"); return(-9); } } } string etlFileName = traceFile.FullName; foreach (string nettraceExtension in new string[] { ".netperf", ".netperf.zip", ".nettrace" }) { if (traceFile.FullName.EndsWith(nettraceExtension)) { etlFileName = traceFile.FullName.Substring(0, traceFile.FullName.Length - nettraceExtension.Length) + ".etlx"; Console.WriteLine($"Creating ETLX file {etlFileName} from {traceFile.FullName}"); TraceLog.CreateFromEventPipeDataFile(traceFile.FullName, etlFileName); } } string lttngExtension = ".trace.zip"; if (traceFile.FullName.EndsWith(lttngExtension)) { etlFileName = traceFile.FullName.Substring(0, traceFile.FullName.Length - lttngExtension.Length) + ".etlx"; Console.WriteLine($"Creating ETLX file {etlFileName} from {traceFile.FullName}"); TraceLog.CreateFromLttngTextDataFile(traceFile.FullName, etlFileName); } using (var traceLog = TraceLog.OpenOrConvert(etlFileName)) { if ((!pid.HasValue && processName == null) && traceLog.Processes.Count != 1) { Console.WriteLine("Either a pid or process name from the following list must be specified"); foreach (TraceProcess proc in traceLog.Processes) { Console.WriteLine($"Procname = {proc.Name} Pid = {proc.ProcessID}"); } return(0); } if (pid.HasValue && (processName != null)) { PrintError("--pid and --process-name cannot be specified together"); return(-1); } // For a particular process TraceProcess p; if (pid.HasValue) { p = traceLog.Processes.LastProcessWithID(pid.Value); } 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()) { PrintError($"No managed jit starting data\nWas the trace collected with provider at least \"Microsoft-Windows-DotNETRuntime:0x4000080018:5\"?"); return(-5); } if (processR2REvents) { if (!p.EventsInProcess.ByEventType <R2RGetEntryPointTraceData>().Any()) { PrintError($"No r2r entrypoint data. This is not an error as in this case we can examine the jitted methods only\nWas the trace collected with provider at least \"Microsoft-Windows-DotNETRuntime:0x6000080018:5\"?"); } } PgoTraceProcess pgoProcess = new PgoTraceProcess(p); 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 (verboseWarnings) { Console.WriteLine($"{traceLog.EventsLost} Lost events"); } bool filePathError = false; if (reference != null) { foreach (FileInfo fileReference in 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 (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 (!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, verboseWarnings); } catch (Exception exception) { extraWarningText = exception.ToString(); } if (method == null) { if ((e.MethodNamespace == "dynamicClass") || !warnings) { continue; } PrintWarning($"Unable to parse {methodNameFromEventDirectly} when looking up R2R methods"); if (extraWarningText != null) { PrintWarning(extraWarningText); } continue; } if ((e.TimeStampRelativeMSec >= excludeEventsBefore) && (e.TimeStampRelativeMSec <= excludeEventsAfter)) { methodsToAttemptToPrepare.Add((int)e.EventIndex, new ProcessedMethodData(e.TimeStampRelativeMSec, method, "R2RLoad")); } } } // Find all the jitStart events. if (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 (!warnings) { continue; } PrintWarning($"Skipped {methodNameFromEventDirectly} due to ClrInstanceID of {e.ClrInstanceID}"); continue; } MethodDesc method = null; string extraWarningText = null; try { method = idParser.ResolveMethodID(e.MethodID, verboseWarnings); } catch (Exception exception) { extraWarningText = exception.ToString(); } if (method == null) { if (!warnings) { continue; } PrintWarning($"Unable to parse {methodNameFromEventDirectly}"); if (extraWarningText != null) { PrintWarning(extraWarningText); } continue; } if ((e.TimeStampRelativeMSec >= excludeEventsBefore) && (e.TimeStampRelativeMSec <= excludeEventsAfter)) { methodsToAttemptToPrepare.Add((int)e.EventIndex, new ProcessedMethodData(e.TimeStampRelativeMSec, method, "JitStart")); } } } if (displayProcessedEvents) { foreach (var entry in methodsToAttemptToPrepare) { MethodDesc method = entry.Value.Method; string reason = entry.Value.Reason; Console.WriteLine($"{entry.Value.Millisecond.ToString("F4")} {reason} {method}"); } } Console.WriteLine($"Done processing input file"); if (outputFileName == null) { return(0); } // Deduplicate entries HashSet <MethodDesc> methodsInListAlready = new HashSet <MethodDesc>(); List <ProcessedMethodData> methodsUsedInProcess = new List <ProcessedMethodData>(); foreach (var entry in methodsToAttemptToPrepare) { if (methodsInListAlready.Add(entry.Value.Method)) { methodsUsedInProcess.Add(entry.Value); } } if (pgoFileType.Value == PgoFileType.jittrace) { GenerateJittraceFile(outputFileName, methodsUsedInProcess, jitTraceOptions); } else if (pgoFileType.Value == PgoFileType.mibc) { return(GenerateMibcFile(tsc, outputFileName, methodsUsedInProcess, validateOutputFile)); } } return(0); }
public async Task <int> OnExecuteAsync(IConsole console, CommandLineApplication app) { var cleanupFiles = new List <string>(); MemoryDump dump = null; TraceLog trace = null; if (Files == null || Files.Count == 0) { console.Error.WriteLine("No files were provided!"); return(1); } try { foreach (var file in Files) { if (file.EndsWith(".netperf")) { console.WriteLine($"Loading trace: {file} ..."); var etlx = TraceLog.CreateFromEventPipeDataFile(file); console.WriteLine($"Convert trace to: {etlx}."); cleanupFiles.Add(etlx); trace = TraceLog.OpenOrConvert(etlx); } else { console.WriteLine($"Loading crash dump: {file} ..."); var target = DataTarget.LoadCrashDump(file); // Assume there's only one if (target.ClrVersions.Count > 1) { console.Error.WriteLine("Multiple CLR versions are present!"); return(1); } var runtime = target.ClrVersions[0].CreateRuntime(); dump = new MemoryDump(target, runtime); } } if (dump == null && trace == null) { console.Error.WriteLine("A dump or trace could not be loaded from the provided files"); return(1); } var session = new AnalysisSession(dump, trace); await(new CommandProcessor()).RunAsync(console, session, console.GetCtrlCToken()); return(0); } finally { dump?.Dispose(); trace?.Dispose(); foreach (var file in cleanupFiles) { if (File.Exists(file)) { File.Delete(file); } } } }
private static async Task <int> TopNReport(CancellationToken ct, IConsole console, string traceFile, int number, bool inclusive, bool verbose) { try { string tempEtlxFilename = TraceLog.CreateFromEventPipeDataFile(traceFile); int count = 0; int index = 0; List <CallTreeNodeBase> nodesToReport = new List <CallTreeNodeBase>(); using (var symbolReader = new SymbolReader(System.IO.TextWriter.Null) { SymbolPath = SymbolPath.MicrosoftSymbolServerPath }) using (var eventLog = new TraceLog(tempEtlxFilename)) { var stackSource = new MutableTraceEventStackSource(eventLog) { OnlyManagedCodeStacks = true }; var computer = new SampleProfilerThreadTimeComputer(eventLog, symbolReader); computer.GenerateThreadTimeStacks(stackSource); FilterParams filterParams = new FilterParams() { FoldRegExs = "CPU_TIME;UNMANAGED_CODE_TIME;{Thread (}", }; FilterStackSource filterStack = new FilterStackSource(filterParams, stackSource, ScalingPolicyKind.ScaleToData); CallTree callTree = new(ScalingPolicyKind.ScaleToData); callTree.StackSource = filterStack; List <CallTreeNodeBase> callTreeNodes = null; if (!inclusive) { callTreeNodes = callTree.ByIDSortedExclusiveMetric(); } else { callTreeNodes = callTree.ByIDSortedInclusiveMetric(); } int totalElements = callTreeNodes.Count; while (count < number && index < totalElements) { CallTreeNodeBase node = callTreeNodes[index]; index++; if (!unwantedMethodNames.Any(node.Name.Contains)) { nodesToReport.Add(node); count++; } } PrintReportHelper.TopNWriteToStdOut(nodesToReport, inclusive, verbose); } return(await Task.FromResult(0)); } catch (Exception ex) { Console.Error.WriteLine($"[ERROR] {ex.ToString()}"); } return(await Task.FromResult(0)); }
/// <summary> /// Reports a stack trace /// </summary> /// <param name="ct">The cancellation token</param> /// <param name="console"></param> /// <param name="processId">The process to report the stack from.</param> /// <param name="name">The name of process to report the stack from.</param> /// <param name="output">The output path for the collected trace data.</param> /// <param name="duration">The duration of to trace the target for. </param> /// <returns></returns> private static async Task <int> Report(CancellationToken ct, IConsole console, int processId, string name, TimeSpan duration) { string tempNetTraceFilename = Path.GetRandomFileName() + ".nettrace"; string tempEtlxFilename = ""; try { // Either processName or processId has to be specified. if (!string.IsNullOrEmpty(name)) { if (processId != 0) { Console.WriteLine("Can only specify either --name or --process-id option."); return(-1); } processId = CommandUtils.FindProcessIdWithName(name); if (processId < 0) { return(-1); } } if (processId < 0) { console.Error.WriteLine("Process ID should not be negative."); return(-1); } else if (processId == 0) { console.Error.WriteLine("--process-id is required"); return(-1); } var client = new DiagnosticsClient(processId); var providers = new List <EventPipeProvider>() { new EventPipeProvider("Microsoft-DotNETCore-SampleProfiler", EventLevel.Informational) }; // collect a *short* trace with stack samples // the hidden '--duration' flag can increase the time of this trace in case 10ms // is too short in a given environment, e.g., resource constrained systems // N.B. - This trace INCLUDES rundown. For sufficiently large applications, it may take non-trivial time to collect // the symbol data in rundown. using (EventPipeSession session = client.StartEventPipeSession(providers)) using (FileStream fs = File.OpenWrite(tempNetTraceFilename)) { Task copyTask = session.EventStream.CopyToAsync(fs); await Task.Delay(duration); session.Stop(); // check if rundown is taking more than 5 seconds and add comment to report Task timeoutTask = Task.Delay(TimeSpan.FromSeconds(5)); Task completedTask = await Task.WhenAny(copyTask, timeoutTask); if (completedTask == timeoutTask) { console.Out.WriteLine($"# Sufficiently large applications can cause this command to take non-trivial amounts of time"); } await copyTask; } // using the generated trace file, symbolocate and compute stacks. tempEtlxFilename = TraceLog.CreateFromEventPipeDataFile(tempNetTraceFilename); using (var symbolReader = new SymbolReader(System.IO.TextWriter.Null) { SymbolPath = SymbolPath.MicrosoftSymbolServerPath }) using (var eventLog = new TraceLog(tempEtlxFilename)) { var stackSource = new MutableTraceEventStackSource(eventLog) { OnlyManagedCodeStacks = true }; var computer = new SampleProfilerThreadTimeComputer(eventLog, symbolReader); computer.GenerateThreadTimeStacks(stackSource); var samplesForThread = new Dictionary <int, List <StackSourceSample> >(); stackSource.ForEach((sample) => { var stackIndex = sample.StackIndex; while (!stackSource.GetFrameName(stackSource.GetFrameIndex(stackIndex), false).StartsWith("Thread (")) { stackIndex = stackSource.GetCallerIndex(stackIndex); } // long form for: int.Parse(threadFrame["Thread (".Length..^1)]) // Thread id is in the frame name as "Thread (<ID>)" string template = "Thread ("; string threadFrame = stackSource.GetFrameName(stackSource.GetFrameIndex(stackIndex), false); int threadId = int.Parse(threadFrame.Substring(template.Length, threadFrame.Length - (template.Length + 1))); if (samplesForThread.TryGetValue(threadId, out var samples)) { samples.Add(sample); } else { samplesForThread[threadId] = new List <StackSourceSample>() { sample }; } }); // For every thread recorded in our trace, print the first stack foreach (var(threadId, samples) in samplesForThread) { #if DEBUG console.Out.WriteLine($"Found {samples.Count} stacks for thread 0x{threadId:X}"); #endif PrintStack(console, threadId, samples[0], stackSource); } } } catch (Exception ex) { Console.Error.WriteLine($"[ERROR] {ex.ToString()}"); return(-1); } finally { if (File.Exists(tempNetTraceFilename)) { File.Delete(tempNetTraceFilename); } if (File.Exists(tempEtlxFilename)) { File.Delete(tempEtlxFilename); } } return(0); }
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); }