public CSVStackSource(CSVReader reader, string eventName, double startRelativeMSec, double endRelativeMSec) { lock (reader) { reader.m_stackEventType = eventName; reader.T0 = (long)(startRelativeMSec * 1000); reader.T1 = long.MaxValue - 1000000; double endusec = endRelativeMSec * 1000; if (endusec < reader.T1) { reader.T1 = (long)endusec; } reader.m_trace.Parameters.T0 = reader.T0; reader.m_trace.Parameters.T1 = reader.T1; var result = reader.m_trace.StackStream(delegate(ETLTrace.Frame frame, ETLTrace.TreeComputer treeComputer, long timeUsec, ulong weight) { m_fullModulePaths = treeComputer.fullModuleNames; StackSourceSample sample = new StackSourceSample(this); sample.TimeRelativeMSec = timeUsec / 1000.0; sample.Metric = weight; if (reader.m_stackEventType == "CSwitch") { sample.Metric = sample.Metric / 1000.0F; } if (sample.Metric == 0) { sample.Metric = 1; } // Get rid of quotes. treeComputer.fullModuleNames["\"Unknown\""] = "UNKNOWN"; // We are traversing frames from the root (threadStart), to leaf (caller before callee). StackSourceCallStackIndex stackIndex = StackSourceCallStackIndex.Invalid; bool callerFrameIsThread = false; while (frame != null) { var fullFrameName = treeComputer.atomsNodeNames.MakeString(frame.id); string moduleName = ""; // Parse it into module and function name var frameName = fullFrameName; var index = fullFrameName.IndexOf('!'); if (index >= 0) { frameName = fullFrameName.Substring(index + 1); frameName = frameName.Replace(';', ','); // They use ';' for template separators for some reason, fix it. moduleName = fullFrameName.Substring(0, index); string fullModuleName; if (treeComputer.fullModuleNames.TryGetValue(moduleName, out fullModuleName)) { moduleName = fullModuleName; } if (moduleName.Length > 4 && moduleName[moduleName.Length - 4] == '.') { #if false // TODO decide if we want to ignore the .NI.DLL and if so do it uniformly. if (moduleName.Length > 7 && moduleName[moduleName.Length - 7] == '.' && moduleName[moduleName.Length - 6] == 'n' && moduleName[moduleName.Length - 5] == 'i') { moduleName = moduleName.Substring(0, moduleName.Length - 7); } else #endif moduleName = moduleName.Substring(0, moduleName.Length - 4); } // If the thread does not call into ntdll, we consider it broken if (callerFrameIsThread && !moduleName.EndsWith("ntdll", StringComparison.Ordinal)) { var brokenFrame = Interner.FrameIntern("BROKEN", Interner.ModuleIntern("")); stackIndex = Interner.CallStackIntern(brokenFrame, stackIndex); } } else { Match m = Regex.Match(frameName, @"^tid *\( *(\d+)\)"); if (m.Success) { frameName = "Thread (" + m.Groups[1].Value + ")"; } else { m = Regex.Match(frameName, @"^(.*?)(\.exe)? *\( *(\d+)\) *$"); if (m.Success) { frameName = "Process " + m.Groups[1].Value + " (" + m.Groups[3].Value + ")"; } } } var myModuleIndex = Interner.ModuleIntern(moduleName); var myFrameIndex = Interner.FrameIntern(frameName, myModuleIndex); stackIndex = Interner.CallStackIntern(myFrameIndex, stackIndex); callerFrameIsThread = frameName.StartsWith("tid "); frame = frame.next; } sample.StackIndex = stackIndex; AddSample(sample); }); Interner.DoneInterning(); } }
public static StackSourceCallStackIndex IndexOf(this IndexMap map, int source, StackSourceCallStackIndex offset) { return((StackSourceCallStackIndex)map.IndexOf(source, (int)offset)); }
private void Read(XmlReader reader) { Stack <string> frameStack = null; // We use the invarient culture, otherwise if we encode in france and decode // in english we get parse errors (this happened!); var invariantCulture = CultureInfo.InvariantCulture; var inputDepth = reader.Depth; var depthForSamples = 0; while (reader.Read()) { PROCESS_NODE: switch (reader.NodeType) { case XmlNodeType.Element: if (reader.Name == "Sample") { var sample = new StackSourceSample(this); sample.Metric = 1; if (reader.MoveToFirstAttribute()) { do { if (reader.Name == "Time") { sample.TimeRelativeMSec = double.Parse(reader.ReadContentAsString(), invariantCulture); } else if (reader.Name == "StackID") { sample.StackIndex = (StackSourceCallStackIndex)reader.ReadContentAsInt(); } else if (reader.Name == "Metric") { sample.Metric = float.Parse(reader.ReadContentAsString(), invariantCulture); } } while (reader.MoveToNextAttribute()); } sample.SampleIndex = (StackSourceSampleIndex)m_curSample; m_samples.Set(m_curSample++, sample); if (sample.TimeRelativeMSec > m_maxTime) { m_maxTime = sample.TimeRelativeMSec; } // See if there is a literal stack present as the body of if (!reader.Read()) { break; } if (reader.NodeType != XmlNodeType.Text) { goto PROCESS_NODE; } string rawStack = reader.Value.Trim(); if (0 < rawStack.Length) { InitInterner(); StackSourceCallStackIndex stackIdx = StackSourceCallStackIndex.Invalid; string[] frames = rawStack.Split('\n'); for (int i = frames.Length - 1; 0 <= i; --i) { var frameIdx = m_interner.FrameIntern(frames[i].Trim()); stackIdx = m_interner.CallStackIntern(frameIdx, stackIdx); } sample.StackIndex = stackIdx; } } else if (reader.Name == "Stack") { int stackID = -1; int callerID = -1; int frameID = -1; if (reader.MoveToFirstAttribute()) { do { if (reader.Name == "ID") { stackID = reader.ReadContentAsInt(); } else if (reader.Name == "FrameID") { frameID = reader.ReadContentAsInt(); } else if (reader.Name == "CallerID") { callerID = reader.ReadContentAsInt(); } } while (reader.MoveToNextAttribute()); if (0 <= stackID) { m_stacks.Set(stackID, new Frame(frameID, callerID)); } } } else if (reader.Name == "Frame") { var frameID = -1; if (reader.MoveToFirstAttribute()) { do { if (reader.Name == "ID") { frameID = reader.ReadContentAsInt(); } } while (reader.MoveToNextAttribute()); } reader.Read(); // Move on to body of the element var frameName = reader.ReadContentAsString(); m_frames.Set(frameID, frameName); } else if (reader.Name == "Frames") { var count = reader.GetAttribute("Count"); if (count != null && m_frames.Count == 0) { m_frames = new GrowableArray <string>(int.Parse(count)); } } else if (reader.Name == "Stacks") { var count = reader.GetAttribute("Count"); if (count != null && m_stacks.Count == 0) { m_stacks = new GrowableArray <Frame>(int.Parse(count)); } #if DEBUG for (int i = 0; i < m_stacks.Count; i++) { m_stacks[i] = new Frame(int.MinValue, int.MinValue); } #endif } else if (reader.Name == "Samples") { var count = reader.GetAttribute("Count"); if (count != null && m_samples.Count == 0) { m_samples = new GrowableArray <StackSourceSample>(int.Parse(count)); } depthForSamples = reader.Depth; } // This is the logic for the JSON case. These are the anonymous object representing a sample. else if (reader.Name == "item") { // THis is an item which is an element of the 'Samples' array. if (reader.Depth == depthForSamples + 1) { var sample = new StackSourceSample(this); sample.Metric = 1; InitInterner(); int depthForSample = reader.Depth; if (frameStack == null) { frameStack = new Stack <string>(); } frameStack.Clear(); while (reader.Read()) { PROCESS_NODE_SAMPLE: if (reader.Depth <= depthForSample) { break; } if (reader.NodeType == XmlNodeType.Element) { if (reader.Name == "Time") { sample.TimeRelativeMSec = reader.ReadElementContentAsDouble(); goto PROCESS_NODE_SAMPLE; } else if (reader.Name == "Metric") { sample.Metric = (float)reader.ReadElementContentAsDouble(); goto PROCESS_NODE_SAMPLE; } else if (reader.Name == "item") { // Item is a string under stack under the sample. if (reader.Depth == depthForSample + 2) { frameStack.Push(reader.ReadElementContentAsString()); goto PROCESS_NODE_SAMPLE; } } } } // Reverse the order of the frames in the stack. sample.StackIndex = StackSourceCallStackIndex.Invalid; while (0 < frameStack.Count) { var frameIdx = m_interner.FrameIntern(frameStack.Pop()); sample.StackIndex = m_interner.CallStackIntern(frameIdx, sample.StackIndex); } if (sample.TimeRelativeMSec > m_maxTime) { m_maxTime = sample.TimeRelativeMSec; } sample.SampleIndex = (StackSourceSampleIndex)m_curSample; m_samples.Set(m_curSample++, sample); } } break; case XmlNodeType.EndElement: if (reader.Depth <= inputDepth) { reader.Read(); goto Done; } break; case XmlNodeType.Text: default: break; } } Done :; #if DEBUG for (int i = 0; i < m_samples.Count; i++) { Debug.Assert(m_samples[i] != null); } for (int i = 0; i < m_frames.Count; i++) { Debug.Assert(m_frames[i] != null); } for (int i = 0; i < m_stacks.Count; i++) { Debug.Assert(m_stacks[i].frameID >= 0); Debug.Assert(m_stacks[i].callerID >= -1); } #endif }
/// <summary> /// Generate the thread time stacks, outputting to 'stackSource'. /// </summary> /// <param name="outputStackSource"></param> /// <param name="traceEvents">Optional filtered trace events.</param> public void GenerateThreadTimeStacks(MutableTraceEventStackSource outputStackSource, TraceEvents traceEvents = null) { m_outputStackSource = outputStackSource; m_sample = new StackSourceSample(outputStackSource); m_nodeNameInternTable = new Dictionary <double, StackSourceFrameIndex>(10); m_ExternalFrameIndex = outputStackSource.Interner.FrameIntern("UNMANAGED_CODE_TIME"); m_cpuFrameIndex = outputStackSource.Interner.FrameIntern("CPU_TIME"); TraceLogEventSource eventSource = traceEvents == null?m_eventLog.Events.GetSource() : traceEvents.GetSource(); if (GroupByStartStopActivity) { UseTasks = true; } if (UseTasks) { m_activityComputer = new ActivityComputer(eventSource, m_symbolReader); m_activityComputer.AwaitUnblocks += delegate(TraceActivity activity, TraceEvent data) { var sample = m_sample; sample.Metric = (float)(activity.StartTimeRelativeMSec - activity.CreationTimeRelativeMSec); sample.TimeRelativeMSec = activity.CreationTimeRelativeMSec; // The stack at the Unblock, is the stack at the time the task was created (when blocking started). sample.StackIndex = m_activityComputer.GetCallStackForActivity(m_outputStackSource, activity, GetTopFramesForActivityComputerCase(data, data.Thread(), true)); StackSourceFrameIndex awaitFrame = m_outputStackSource.Interner.FrameIntern("AWAIT_TIME"); sample.StackIndex = m_outputStackSource.Interner.CallStackIntern(awaitFrame, sample.StackIndex); m_outputStackSource.AddSample(sample); if (m_threadToStartStopActivity != null) { UpdateStartStopActivityOnAwaitComplete(activity, data); } }; // We can provide a bit of extra value (and it is useful for debugging) if we immediately log a CPU // sample when we schedule or start a task. That we we get the very instant it starts. var tplProvider = new TplEtwProviderTraceEventParser(eventSource); tplProvider.AwaitTaskContinuationScheduledSend += OnSampledProfile; tplProvider.TaskScheduledSend += OnSampledProfile; tplProvider.TaskExecuteStart += OnSampledProfile; tplProvider.TaskWaitSend += OnSampledProfile; tplProvider.TaskWaitStop += OnTaskUnblock; // Log the activity stack even if you don't have a stack. } if (GroupByStartStopActivity) { m_startStopActivities = new StartStopActivityComputer(eventSource, m_activityComputer); // Maps thread Indexes to the start-stop activity that they are executing. m_threadToStartStopActivity = new StartStopActivity[m_eventLog.Threads.Count]; /********* Start Unknown Async State machine for StartStop activities ******/ // The delegates below along with the AddUnkownAsyncDurationIfNeeded have one purpose: // To inject UNKNOWN_ASYNC stacks when there is an active start-stop activity that is // 'missing' time. It has the effect of insuring that Start-Stop tasks always have // a metric that is not unrealistically small. m_activityComputer.Start += delegate(TraceActivity activity, TraceEvent data) { StartStopActivity newStartStopActivityForThread = m_startStopActivities.GetCurrentStartStopActivity(activity.Thread, data); UpdateThreadToWorkOnStartStopActivity(activity.Thread, newStartStopActivityForThread, data); }; m_activityComputer.AfterStop += delegate(TraceActivity activity, TraceEvent data, TraceThread thread) { StartStopActivity newStartStopActivityForThread = m_startStopActivities.GetCurrentStartStopActivity(thread, data); UpdateThreadToWorkOnStartStopActivity(thread, newStartStopActivityForThread, data); }; m_startStopActivities.Start += delegate(StartStopActivity startStopActivity, TraceEvent data) { // We only care about the top-most activities since unknown async time is defined as time // where a top most activity is running but no thread (or await time) is associated with it // fast out otherwise (we just insure that we mark the thread as doing this activity) if (startStopActivity.Creator != null) { UpdateThreadToWorkOnStartStopActivity(data.Thread(), startStopActivity, data); return; } // Then we have a refcount of exactly one Debug.Assert(m_unknownTimeStartMsec.Get((int)startStopActivity.Index) >= 0); // There was nothing running before. m_unknownTimeStartMsec.Set((int)startStopActivity.Index, -1); // Set it so just we are running. m_threadToStartStopActivity[(int)data.Thread().ThreadIndex] = startStopActivity; }; m_startStopActivities.Stop += delegate(StartStopActivity startStopActivity, TraceEvent data) { // We only care about the top-most activities since unknown async time is defined as time // where a top most activity is running but no thread (or await time) is associated with it // fast out otherwise if (startStopActivity.Creator != null) { return; } double unknownStartTime = m_unknownTimeStartMsec.Get((int)startStopActivity.Index); if (0 < unknownStartTime) { AddUnkownAsyncDurationIfNeeded(startStopActivity, unknownStartTime, data); } // Actually emit all the async unknown events. List <StackSourceSample> samples = m_startStopActivityToAsyncUnknownSamples.Get((int)startStopActivity.Index); if (samples != null) { foreach (var sample in samples) { m_outputStackSource.AddSample(sample); // Adding Unknown ASync } m_startStopActivityToAsyncUnknownSamples.Set((int)startStopActivity.Index, null); } m_unknownTimeStartMsec.Set((int)startStopActivity.Index, 0); Debug.Assert(m_threadToStartStopActivity[(int)data.Thread().ThreadIndex] == startStopActivity || m_threadToStartStopActivity[(int)data.Thread().ThreadIndex] == null); m_threadToStartStopActivity[(int)data.Thread().ThreadIndex] = null; }; } eventSource.Clr.GCAllocationTick += OnSampledProfile; eventSource.Clr.GCSampledObjectAllocation += OnSampledProfile; var eventPipeTraceEventPraser = new SampleProfilerTraceEventParser(eventSource); eventPipeTraceEventPraser.ThreadSample += OnSampledProfile; if (IncludeEventSourceEvents) { eventSource.Dynamic.All += delegate(TraceEvent data) { // TODO decide what the correct heuristic is. // Currently I only do this for things that might be an EventSoruce (uses the name->Guid hashing) // Most importantly, it excludes the high volume CLR providers. if (!TraceEventProviders.MaybeAnEventSource(data.ProviderGuid)) { return; } // We don't want most of the FrameworkEventSource events either. if (data.ProviderGuid == FrameworkEventSourceTraceEventParser.ProviderGuid) { if (!((TraceEventID)140 <= data.ID && data.ID <= (TraceEventID)143)) // These are the GetResponce and GetResestStream events { return; } } // We don't care about EventPipe sample profiler events. if (data.ProviderGuid == SampleProfilerTraceEventParser.ProviderGuid) { return; } // We don't care about the TPL provider. Too many events. if (data.ProviderGuid == TplEtwProviderTraceEventParser.ProviderGuid) { return; } // We don't care about ManifestData events. if (data.ID == (TraceEventID)0xFFFE) { return; } TraceThread thread = data.Thread(); if (thread == null) { return; } StackSourceCallStackIndex stackIndex = GetCallStack(data, thread); // Tack on additional info about the event. var fieldNames = data.PayloadNames; for (int i = 0; i < fieldNames.Length; i++) { var fieldName = fieldNames[i]; var value = data.PayloadString(i); var fieldNodeName = "EventData: " + fieldName + "=" + value; var fieldNodeIndex = m_outputStackSource.Interner.FrameIntern(fieldNodeName); stackIndex = m_outputStackSource.Interner.CallStackIntern(fieldNodeIndex, stackIndex); } stackIndex = m_outputStackSource.Interner.CallStackIntern(m_outputStackSource.Interner.FrameIntern("EventName: " + data.ProviderName + "/" + data.EventName), stackIndex); m_threadState[(int)thread.ThreadIndex].LogThreadStack(data.TimeStampRelativeMSec, stackIndex, thread, this, false); }; } eventSource.Process(); m_outputStackSource.DoneAddingSamples(); m_threadState = null; }
/// <summary> /// Get the frame index of a given call stack. /// </summary> /// <param name="callStackIndex">The call stack to look up.</param> /// <returns>The frame index of the call stack, if it exists, <see cref="StackSourceFrameIndex.Invalid"/> otherwise.</returns> public override StackSourceFrameIndex GetFrameIndex(StackSourceCallStackIndex callStackIndex) { return((callStackIndex - StackSourceCallStackIndex.Start) + StackSourceFrameIndex.Start); }
private StackSourceCallStackIndex InternFrames(IEnumerator <Frame> frameIterator, StackSourceCallStackIndex stackIndex, int processID, int?threadid = null, BlockedTimeAnalyzer blockedTimeAnalyzer = null) { // We shouldn't advance the iterator if thread time is enabled because we need // to add an extra frame to the caller stack that is not in the frameIterator. // i.e. Short-circuiting prevents the frameIterator from doing MoveNext :) if (blockedTimeAnalyzer == null && !frameIterator.MoveNext()) { return(StackSourceCallStackIndex.Invalid); } StackSourceFrameIndex frameIndex; string frameDisplayName; if (blockedTimeAnalyzer != null) { // If doThreadTime is true, then we need to make sure that threadid is not null Contract.Requires(threadid != null, nameof(threadid)); if (blockedTimeAnalyzer.IsThreadBlocked((int)threadid)) { frameDisplayName = LinuxThreadState.BLOCKED_TIME.ToString(); } else { frameDisplayName = LinuxThreadState.CPU_TIME.ToString(); } } else { frameDisplayName = frameIterator.Current.DisplayName; } frameIndex = this.InternFrame(frameDisplayName); stackIndex = this.InternCallerStack(frameIndex, this.InternFrames(frameIterator, stackIndex, processID)); return(stackIndex); }
public override StackSourceFrameIndex GetFrameIndex(StackSourceCallStackIndex callStackIndex) => samples.First(sample => sample.StackIndex == callStackIndex).FrameIndex;
public override StackSourceCallStackIndex GetCallerIndex(StackSourceCallStackIndex callStackIndex) { NodeIndex nodeIndex = (NodeIndex)callStackIndex; return((StackSourceCallStackIndex)m_parent[(int)nodeIndex]); }
/// <summary> /// Implementation of StackSource protocol. /// </summary> public override StackSourceCallStackIndex GetCallerIndex(StackSourceCallStackIndex callStackIndex) { Debug.Assert(callStackIndex >= 0); Debug.Assert(StackSourceCallStackIndex.Start == 0); // If there are any cases before start, we need to handle them here. int curIndex = (int)callStackIndex - (int)StackSourceCallStackIndex.Start; int nextIndex = (int)StackSourceCallStackIndex.Start; if (curIndex < m_log.CallStacks.Count) { var nextCallStackIndex = m_log.CallStacks.Caller((CallStackIndex)curIndex); if (nextCallStackIndex == CallStackIndex.Invalid) { nextIndex += m_log.CallStacks.Count; // Now points at the threads region. var threadIndex = m_log.CallStacks.ThreadIndex((CallStackIndex)curIndex); nextIndex += (int)threadIndex; // Mark it as a broken stack, which come after all the indexes for normal threads and processes. if (!ReasonableTopFrame(callStackIndex, threadIndex)) { nextIndex += m_log.Threads.Count + m_log.Processes.Count; } } else { nextIndex += (int)nextCallStackIndex; } return((StackSourceCallStackIndex)nextIndex); } curIndex -= m_log.CallStacks.Count; // Now is a thread index nextIndex += m_log.CallStacks.Count; // Output index points to the thread region. if (curIndex < m_log.Threads.Count) { nextIndex += m_log.Threads.Count; // Output index point to process region. nextIndex += (int)m_log.Threads[(ThreadIndex)curIndex].Process.ProcessIndex; return((StackSourceCallStackIndex)nextIndex); } curIndex -= m_log.Threads.Count; // Now is a broken thread index if (curIndex < m_log.Processes.Count) { return(StackSourceCallStackIndex.Invalid); // Process has no parent } curIndex -= m_log.Processes.Count; // Now is a broken thread index if (curIndex < m_log.Threads.Count) // It is a broken stack { nextIndex += curIndex; // Indicate the real thread. return((StackSourceCallStackIndex)nextIndex); } curIndex -= m_log.Threads.Count; // Now it points at the one-element stacks. if (curIndex < m_pseudoStacks.Count) { // Now points beginning of the broken stacks indexes. nextIndex += m_log.Threads.Count + m_log.Processes.Count; // Pick the broken stack for this thread. nextIndex += (int)m_pseudoStacks[curIndex].ThreadIndex; return((StackSourceCallStackIndex)nextIndex); } Debug.Assert(false, "Invalid CallStackIndex"); return(StackSourceCallStackIndex.Invalid); }
private void AddSamplesForDirectory(string directoryPath, StackSourceCallStackIndex directoryStack) { StackSourceSample sample = null; try { var directory = new FastDirectory(directoryPath); foreach (var member in directory.Members) { if (member.IsDirectory) { var stack = Interner.CallStackIntern(Interner.FrameIntern("DIR: " + member.Name), directoryStack); AddSamplesForDirectory(Path.Combine(directoryPath, member.Name), stack); } else { var stack = directoryStack; // Allow easy grouping by extension. var ext = Path.GetExtension(member.Name).ToLower(); // And whether the DLL/EXE is managed or not. var suffix = ""; if (string.Compare(ext, ".dll", true) == 0 || string.Compare(ext, ".exe", true) == 0 || string.Compare(ext, ".winmd", true) == 0) { suffix = ""; string fileName = Path.Combine(directoryPath, member.Name); try { using (var peFile = new PEFile.PEFile(fileName)) { suffix = peFile.Header.IsManaged ? " (MANAGED)" : " (UNMANAGED)"; if (peFile.Header.IsPE64) { suffix += " (64Bit)"; } if (peFile.HasPrecompiledManagedCode) { if (peFile.IsManagedReadyToRun) { short major, minor; peFile.ReadyToRunVersion(out major, out minor); suffix += " (ReadyToRun(" + major + "." + minor + "))"; } else { suffix += " (NGEN)"; } } } } catch (Exception) { m_log.WriteLine("Error: exception looking at file " + fileName); m_log.Flush(); } } stack = Interner.CallStackIntern(Interner.FrameIntern("EXT: " + ext + suffix), stack); // Finally the file name itself. stack = Interner.CallStackIntern(Interner.FrameIntern("FILE: " + member.Name), stack); if (sample == null) { sample = new StackSourceSample(this); } sample.Metric = member.Size; sample.StackIndex = stack; if (m_useWriteTime) { sample.TimeRelativeMSec = (m_nowUtc - member.LastWriteTimeUtc).TotalDays; } else { sample.TimeRelativeMSec = (m_nowUtc - member.LastAccessTimeUtc).TotalDays; } AddSample(sample); m_totalSize += member.Size; int count = SampleIndexLimit; if ((count % 1000) == 0) { m_log.WriteLine("[Processed " + count + " files, size " + (m_totalSize / 1000000).ToString("n0") + " MB in directory scan at " + Path.Combine(directoryPath, member.Name) + " ]"); } } } } catch (Exception e) { m_log.WriteLine("Error processing directory " + directoryPath + ": " + e.Message); } }
public override StackSourceCallStackIndex GetCallerIndex(StackSourceCallStackIndex callStackIndex) { return(StackSourceCallStackIndex.Invalid); }
public override StackSourceFrameIndex GetFrameIndex(StackSourceCallStackIndex callStackIndex) { return(this.inner.GetFrameIndex(callStackIndex)); }
public override int GetNumberOfFoldedFrames(StackSourceCallStackIndex callStackIndex) { return(this.inner.GetNumberOfFoldedFrames(callStackIndex)); }
private void Read(TextReader reader) { var framePattern = new Regex(@"\b(\w+?)\!(\S\(?[\S\s]*\)?)"); var stackStart = new Regex(@"Call Site"); // the call stack from the debugger kc command looksl like this //Call Site //coreclr!JIT_MonEnterWorker_Portable //System_Windows_ni!MS.Internal.ManagedPeerTable.TryGetManagedPeer(IntPtr, Boolean, System.Object ByRef) //System_Windows_ni!MS.Internal.ManagedPeerTable.EnsureManagedPeer(IntPtr, Int32, System.Type, Boolean) //System_Windows_ni!MS.Internal.FrameworkCallbacks.CheckPeerType(IntPtr, System.String, Boolean) //System_Windows_ni!DomainBoundILStubClass.IL_STUB_ReversePInvoke(Int32, IntPtr, Int32) //coreclr!UM2MThunk_WrapperHelper //coreclr!UMThunkStubWorker //coreclr!UMThunkStub //agcore!CParser::StartObjectElement //agcore!CParser::Attribute //agcore!CParser::LoadXaml var stack = new GrowableArray <DebuggerCallStackFrame>(); bool newCallStackFound = false; var sample = new StackSourceSample(this); float time = 0; for (; ;) { var line = reader.ReadLine(); if (line == null) { break; } var match = framePattern.Match(line); if (match.Success && newCallStackFound) { var module = match.Groups[1].Value; var methodName = match.Groups[2].Value; // trim the methodName if it has file name info (if the trace is collected with kv instead of kc) int index = methodName.LastIndexOf(")+"); if (index != -1) { methodName = methodName.Substring(0, index + 1); } var moduleIndex = Interner.ModuleIntern(module); var frameIndex = Interner.FrameIntern(methodName, moduleIndex); DebuggerCallStackFrame frame = new DebuggerCallStackFrame(); frame.frame = frameIndex; stack.Add(frame); } else { var stackStartMatch = stackStart.Match(line); if (stackStartMatch.Success) { // start a new sample. // add the previous sample // clear the stack if (stack.Count != 0) { StackSourceCallStackIndex parent = StackSourceCallStackIndex.Invalid; for (int i = stack.Count - 1; i >= 0; --i) { parent = Interner.CallStackIntern(stack[i].frame, parent); } stack.Clear(); sample.StackIndex = parent; sample.TimeRelativeMSec = time; time++; AddSample(sample); } newCallStackFound = true; } } } Interner.DoneInterning(); }
protected override StackSourceCallStackIndex InternCallerStack(StackSourceFrameIndex frameIndex, StackSourceCallStackIndex stackIndex) { lock (internCallStackLock) { return(this.Interner.CallStackIntern(frameIndex, stackIndex)); } }
public override StackSourceCallStackIndex GetCallerIndex(StackSourceCallStackIndex callStackIndex) { Debug.Assert(callStackIndex >= 0); // subtract 1 so 0 (clrprofiler Scentinal) becomes CallStackIndex Sentinal return((StackSourceCallStackIndex)(m_clrProfiler.NextFrame((ProfilerStackTraceID)(callStackIndex + 1)) - 1)); }
protected virtual StackSourceCallStackIndex InternCallerStack(StackSourceFrameIndex frameIndex, StackSourceCallStackIndex stackIndex) { return(this.Interner.CallStackIntern(frameIndex, stackIndex)); }
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); }
public override StackSourceCallStackIndex GetCallerIndex(StackSourceCallStackIndex callStackIndex) => samples.First(sample => sample.StackIndex == callStackIndex).CallerIndex;
/// <summary> /// Generates a Stack Source from an XML file created with XmlStackSourceWriter. /// If 'readElement' is non-null Any XML Elements that are not recognised to it so /// that that information can be parsed by upper level logic. When that routine /// returns it must have skipped past that element (so reader points at whatever /// is after the End element tag). /// /// If the filename ends in .zip, the file is assumed to be a ZIPPed XML file and /// it is first Unziped and then processed. /// /// If the file ends in .json or .json.zip it can also read that (using JsonReaderWriterFactory.CreateJsonReader) /// see https://msdn.microsoft.com/en-us/library/bb412170.aspx?f=255&MSPPError=-2147217396 for /// more on this mapping. /// </summary> public XmlStackSource(string fileName, Action <XmlReader> readElement = null) { using (Stream dataStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete)) { var xmlStream = dataStream; string unzippedName = fileName; if (fileName.EndsWith(".zip", StringComparison.OrdinalIgnoreCase)) { var zipArchive = new ZipArchive(dataStream); var entries = zipArchive.Entries; if (entries.Count != 1) { throw new ApplicationException("The ZIP file does not have exactly 1 XML file in it,"); } xmlStream = entries[0].Open(); unzippedName = fileName.Substring(0, fileName.Length - 4); } XmlReader reader; if (unzippedName.EndsWith(".json", StringComparison.OrdinalIgnoreCase)) { reader = GetJsonReader(dataStream); } else { XmlReaderSettings settings = new XmlReaderSettings() { IgnoreWhitespace = true, IgnoreComments = true }; reader = XmlTextReader.Create(xmlStream, settings); } reader.Read(); // Skip the StackWindow element. bool readStackSource = false; for (;;) { if (reader.NodeType == XmlNodeType.Element) { if (reader.Name == "StackSource") { if (!readStackSource) { Read(reader); readStackSource = true; } } else if (readElement != null) { readElement(reader); } else { reader.Read(); } } else if (!reader.Read()) { break; } } if (m_interner != null) { // Transfer the interned names to the m_frames array. // Go from high to low so at most one reallocation happens. for (int i = m_interner.FrameCount - 1; 0 <= i; --i) { StackSourceFrameIndex frameIdx = m_interner.FrameStartIndex + i; m_frames.Set((int)frameIdx, m_interner.GetFrameName(frameIdx, true)); } for (int i = m_interner.CallStackCount - 1; 0 <= i; --i) { StackSourceCallStackIndex stackIdx = m_interner.CallStackStartIndex + i; m_stacks.Set((int)stackIdx, new Frame( (int)m_interner.GetFrameIndex(stackIdx), (int)m_interner.GetCallerIndex(stackIdx))); } m_interner = null; // we are done with it. } } }
/// <summary> /// If you place a file called PerfViewExtensions\PerfViewStartup next to the PerfView.exe it will /// run execute commands in that file. If you put /// /// DeclareFileView .etl "Demo View In Etl File" DemoDeclareFileView /// /// It will create a child node for all .etl files called 'Demo View In Etl File' If you click /// on this node it will execute this user command. It is passed the name of the file that was /// opened and the name of the view that was opened (in this case 'Demo View In Etl File'). /// </summary> public void DemoDeclareFileView(string fileName, string viewName) { // This demo creates a view that shows you all the START events in a stack view. LogFile.WriteLine("************ In DemoDeclareFileView file = {0} view = {1}", fileName, viewName); // This is an example of opening an ETL file. ETLDataFile etlFile = OpenETLFile(fileName); // An ETLData file is a high level construct that knows about high level 'views' of the data (CPU stacks, thread time Stacks ...) // However if you want to create a new view, you probably want a TraceLog which is the underlying ETW data. TraceLog traceLog = etlFile.TraceLog; // A tracelog represent the whole ETL file (which has process, images, threads etc), we want events, and we want callbacks // for each event which is what GetSource() does. THus we get a source (which we can add callbacks to) var eventSource = traceLog.Events.GetSource(); // At this point create the 'output' of our routine. Our goal is to produce stacks that we will view in the // stack viewer. Thus we create an 'empty' one fo these. var stackSource = new MutableTraceEventStackSource(traceLog); // A stack source is list of samples. We create a sample structure, mutate it and then call AddSample() repeatedly to add samples. var sample = new StackSourceSample(stackSource); // Setup the callbacks, In this case we are going to watch for stacks where GCs happen eventSource.Clr.GCStart += delegate(GCStartTraceData data) { // An TraceLog should have a callstack associated with this event; CallStackIndex callStackIdx = data.CallStackIndex(); if (callStackIdx != CallStackIndex.Invalid) { // Convert the TraceLog call stack to a MutableTraceEventStackSource call stack StackSourceCallStackIndex stackCallStackIndex = stackSource.GetCallStack(callStackIdx, data); // Add a pseudo frame on the bottom of the stack StackSourceFrameIndex frameIdxForName = stackSource.Interner.FrameIntern("GC Gen " + data.Depth + "Reason " + data.Reason); stackCallStackIndex = stackSource.Interner.CallStackIntern(frameIdxForName, stackCallStackIndex); // create a sample with that stack and add it to the stack source (list of samples) sample.Metric = 1; sample.TimeRelativeMSec = data.TimeStampRelativeMSec; sample.StackIndex = stackCallStackIndex; stackSource.AddSample(sample); } }; // You can set up other callback for other events. // This causes the TraceLog source to actually spin through all the events calling our callbacks as requested. eventSource.Process(); // We are done adding sample to our stack Source, so we tell the MutableTraceEventStackSource that. // after that is becomes viewable (and read-only). stackSource.DoneAddingSamples(); // Take the stack source (raw data) and make it into a 'Stacks' allows you to add filtering to and send to 'OpendStackViewer' Stacks stacksForViewer = new Stacks(stackSource, viewName, etlFile); // Set any filtering options here. // Now we can display the viewer. OpenStackViewer(stacksForViewer); }
public override StackSourceCallStackIndex GetCallerIndex(StackSourceCallStackIndex callStackIndex) { return((StackSourceCallStackIndex)m_stacks[(int)callStackIndex].callerID); }
/// <summary> /// Gets the index of the caller of a given call stack. /// </summary> /// <param name="callStackIndex">The call stack to look up.</param> /// <returns>The caller, if it exists, <see cref="StackSourceCallStackIndex.Invalid"/> otherwise.</returns> public override StackSourceCallStackIndex GetCallerIndex(StackSourceCallStackIndex callStackIndex) { // All pseudo-frames are top-level frames. Debug.Assert((int)callStackIndex >= 0 && (int)callStackIndex < names.Length); return(StackSourceCallStackIndex.Invalid); }
public override StackSourceFrameIndex GetFrameIndex(StackSourceCallStackIndex callStackIndex) { return((StackSourceFrameIndex)m_stacks[(int)callStackIndex].frameID); }
public static int SourceOf(this IndexMap map, StackSourceCallStackIndex aggregate) { return(map.SourceOf((int)aggregate)); }
public static StackSourceCallStackIndex OffsetOf(this IndexMap map, int source, StackSourceCallStackIndex aggregate) { return((StackSourceCallStackIndex)map.OffsetOf(source, (int)aggregate)); }