public static void WriteStacks(StackSource source, XmlWriter writer) { writer.WriteStartElement("StackSource"); writer.WriteStartElement("Frames"); writer.WriteAttributeString("Count", source.CallFrameIndexLimit.ToString()); for (int i = 0; i < source.CallFrameIndexLimit; i++) { writer.WriteStartElement("Frame"); writer.WriteAttributeString("ID", i.ToString()); var frameName = source.GetFrameName((StackSourceFrameIndex)i, true); writer.WriteString(frameName); writer.WriteEndElement(); // Frame } writer.WriteEndElement(); // Frames writer.WriteStartElement("Stacks"); writer.WriteAttributeString("Count", source.CallStackIndexLimit.ToString()); for (int i = 0; i < source.CallStackIndexLimit; i++) { writer.WriteStartElement("Stack"); writer.WriteAttributeString("ID", i.ToString()); var FrameID = source.GetFrameIndex((StackSourceCallStackIndex)i); var callerID = source.GetCallerIndex((StackSourceCallStackIndex)i); writer.WriteAttributeString("CallerID", ((int)callerID).ToString()); writer.WriteAttributeString("FrameID", ((int)FrameID).ToString()); writer.WriteEndElement(); // Stack } writer.WriteEndElement(); // Stacks writer.WriteStartElement("Samples"); writer.WriteAttributeString("Count", source.SampleIndexLimit.ToString()); // We use the invariant culture, otherwise if we encode in France and decode // in English we get parse errors (this happened!); var invariantCulture = CultureInfo.InvariantCulture; source.ForEach(delegate(StackSourceSample sample) { // <Sample ID="1" Time="3432.23" StackID="2" Metric="1" EventKind="CPUSample" /> writer.WriteStartElement("Sample"); writer.WriteAttributeString("ID", ((int)sample.SampleIndex).ToString()); writer.WriteAttributeString("Time", sample.TimeRelativeMSec.ToString("f3", invariantCulture)); writer.WriteAttributeString("StackID", ((int)sample.StackIndex).ToString()); if (sample.Metric != 1) { var asInt = (int)sample.Metric; if (sample.Metric == asInt) { writer.WriteAttributeString("Metric", asInt.ToString()); } else { writer.WriteAttributeString("Metric", sample.Metric.ToString("f3", invariantCulture)); } } writer.WriteEndElement(); }); writer.WriteEndElement(); // Samples writer.WriteEndElement(); // StackSource }
public static void WriteStacks(StackSource source, XmlWriter writer) { writer.WriteStartElement("StackSource"); writer.WriteStartElement("Frames"); writer.WriteAttributeString("Count", source.CallFrameIndexLimit.ToString()); for (int i = 0; i < source.CallFrameIndexLimit; i++) { writer.WriteStartElement("Frame"); writer.WriteAttributeString("ID", i.ToString()); var frameName = source.GetFrameName((StackSourceFrameIndex)i, true); // Check for the optimization tier. The frame name would contain the optimization tier in the form: // Module![OptimizationTier]Symbol // Extract the optimization tier into an attribute and convert the frame name to this form for storage: // Module!Symbol if (frameName != null && frameName.Length >= 4) { int openBracketIndex = frameName.IndexOf("![") + 1; if (openBracketIndex > 0) { int closeBracketIndex = frameName.IndexOf(']', openBracketIndex + 1); if (closeBracketIndex - openBracketIndex > 1) { var optimizationTierStr = frameName.Substring(openBracketIndex + 1, closeBracketIndex - openBracketIndex - 1); if (Enum.TryParse <OptimizationTier>(optimizationTierStr, out var optimizationTier)) { if (optimizationTier != OptimizationTier.Unknown) { writer.WriteAttributeString("OptimizationTier", optimizationTierStr); } frameName = frameName.Substring(0, openBracketIndex) + frameName.Substring(closeBracketIndex + 1); } } } } writer.WriteString(frameName); writer.WriteEndElement(); // Frame } writer.WriteEndElement(); // Frames writer.WriteStartElement("Stacks"); writer.WriteAttributeString("Count", source.CallStackIndexLimit.ToString()); for (int i = 0; i < source.CallStackIndexLimit; i++) { writer.WriteStartElement("Stack"); writer.WriteAttributeString("ID", i.ToString()); var FrameID = source.GetFrameIndex((StackSourceCallStackIndex)i); var callerID = source.GetCallerIndex((StackSourceCallStackIndex)i); writer.WriteAttributeString("CallerID", ((int)callerID).ToString()); writer.WriteAttributeString("FrameID", ((int)FrameID).ToString()); writer.WriteEndElement(); // Stack } writer.WriteEndElement(); // Stacks writer.WriteStartElement("Samples"); writer.WriteAttributeString("Count", source.SampleIndexLimit.ToString()); // We use the invariant culture, otherwise if we encode in France and decode // in English we get parse errors (this happened!); var invariantCulture = CultureInfo.InvariantCulture; source.ForEach(delegate(StackSourceSample sample) { // <Sample ID="1" Time="3432.23" StackID="2" Metric="1" EventKind="CPUSample" /> writer.WriteStartElement("Sample"); writer.WriteAttributeString("ID", ((int)sample.SampleIndex).ToString()); writer.WriteAttributeString("Time", sample.TimeRelativeMSec.ToString("f3", invariantCulture)); writer.WriteAttributeString("StackID", ((int)sample.StackIndex).ToString()); if (sample.Metric != 1) { var asInt = (int)sample.Metric; if (sample.Metric == asInt) { writer.WriteAttributeString("Metric", asInt.ToString()); } else { writer.WriteAttributeString("Metric", sample.Metric.ToString("f3", invariantCulture)); } } writer.WriteEndElement(); }); writer.WriteEndElement(); // Samples writer.WriteEndElement(); // StackSource }
private static bool BuildInternalTempRepresentation(StackSource stackSource, string fileToWrite, string rootFunction) { StringBuilder flameChartStringBuilder = new StringBuilder(); bool enableRootingOnFunction = !string.IsNullOrWhiteSpace(rootFunction); // Write out the flame chart format, one line per stack // eg: corerun;foo;bar;baz 1 stackSource.ForEach(sample => { Stack <StackSourceCallStackIndex> callStackIndices = new Stack <StackSourceCallStackIndex>(); callStackIndices.Push(sample.StackIndex); StackSourceCallStackIndex callerIdx = stackSource.GetCallerIndex(sample.StackIndex); while (callerIdx != StackSourceCallStackIndex.Invalid) { callStackIndices.Push(callerIdx); callerIdx = stackSource.GetCallerIndex(callerIdx); } bool firstOne = true; bool foundRootFunction = false; while (callStackIndices.Count > 0) { var currFrame = callStackIndices.Pop(); var frameIdx = stackSource.GetFrameIndex(currFrame); var frameName = stackSource.GetFrameName(frameIdx, false); // If we're rooting on a function, skip the frames above it if (enableRootingOnFunction && !foundRootFunction) { if (frameName.Contains(rootFunction)) { foundRootFunction = true; } else { continue; } } if (!firstOne) { flameChartStringBuilder.Append(";"); } flameChartStringBuilder.Append(frameName); firstOne = false; } flameChartStringBuilder.Append(" 1"); flameChartStringBuilder.AppendLine(); }); using (TextWriter writer = File.CreateText(fileToWrite)) { try { writer.Write(flameChartStringBuilder.ToString()); } catch (IOException) { return(false); } } return(true); }
/// <summary> /// Looks up symbols for all modules that have an inclusive count >= minCount. /// stackSource, if given, can be used to be the filter. If null, 'this' is used. /// If stackSource is given, it needs to use the same indexes for frames as 'this'. /// shouldLoadSymbols, if given, can be used to filter the modules. /// </summary> public void LookupWarmSymbols(int minCount, SymbolReader reader, StackSource stackSource = null, Predicate <TraceModuleFile> shouldLoadSymbols = null) { if (stackSource == null) { stackSource = this; } Debug.Assert(stackSource.CallFrameIndexLimit == CallFrameIndexLimit); Debug.Assert(stackSource.CallStackIndexLimit == CallStackIndexLimit); reader.Log.WriteLine("Resolving all symbols for modules with inclusive times > {0}", minCount); if ((reader.Options & SymbolReaderOptions.CacheOnly) != 0) { reader.Log.WriteLine("Cache-Only set: will only look on the local machine."); } // Get a list of all the unique frames. We also keep track of unique stacks for efficiency var stackModuleLists = new ModuleList[stackSource.CallStackIndexLimit]; var stackCounts = new int[stackSource.CallStackIndexLimit]; var totalCount = 0; // Compute for each stack, the set of inclusive modules for that stack stackSource.ForEach(delegate(StackSourceSample sample) { stackCounts[(int)sample.StackIndex]++; totalCount++; }); reader.Log.WriteLine("Got a total of {0} samples", totalCount); // for each stack in the trace, find the list of modules for that stack var moduleCounts = new int[TraceLog.ModuleFiles.Count]; for (int i = 0; i < stackCounts.Length; i++) { var count = stackCounts[i]; if (count > 0) { var modules = GetModulesForStack(stackModuleLists, (StackSourceCallStackIndex)i); // Update the counts for each module in that stack. while (modules != null) { moduleCounts[(int)modules.Module.ModuleFileIndex] += count; modules = modules.Next; } } } // Now that we have an list of the inclusive counts of all frames. Find all stacks that meet the threshold for (int i = 0; i < moduleCounts.Length; i++) { if (moduleCounts[i] >= minCount) { var moduleFile = TraceLog.ModuleFiles[(ModuleFileIndex)i]; if (shouldLoadSymbols == null || shouldLoadSymbols(moduleFile)) { reader.Log.WriteLine("Resolving symbols (count={0}) for module {1} ", moduleCounts[i], moduleFile.FilePath); TraceLog.CallStacks.CodeAddresses.LookupSymbolsForModule(reader, moduleFile); } } } reader.Log.WriteLine("Done Resolving all symbols for modules with inclusive times > {0}", minCount); }
public static Dictionary <string, CallTreeItem> GetCallTree(StackSource stacks, out float timePerStack) { var callTree = new Dictionary <string, CallTreeItem>(StringComparer.OrdinalIgnoreCase); var currentStack = new HashSet <string>(); void AddRecursively(StackSourceCallStackIndex index, int depth = 0, CallTreeItem parent = null) { var name = stacks.GetFrameName(stacks.GetFrameIndex(index), false); if (name == "BROKEN") { return; } var isRecursion = !currentStack.Add(name); var caller = stacks.GetCallerIndex(index); if (!callTree.TryGetValue(name, out var item)) { callTree.Add(name, item = new CallTreeItem(name)); } if (!isRecursion) { item.IncSamples++; } if (depth == 0) { item.Samples++; } else { item.AddCallee(parent); } parent?.AddCaller(item); if (caller != StackSourceCallStackIndex.Invalid) { AddRecursively(caller, depth + 1, item); } if (!isRecursion) { currentStack.Remove(name); } } var metric = float.NaN; stacks.ForEach(stack => { if (float.IsNaN(metric)) { metric = stack.Metric; } if (metric != stack.Metric) { throw new Exception(); } if (stack.Count != 1) { throw new Exception(); } AddRecursively(stack.StackIndex); AddRecursively(stack.StackIndex); }); timePerStack = metric; return(callTree); }