public void WalkTheStackAndExpandSamplesHandlesBrokenStacks(StackSourceFrameIndex kind) { // Main() calls WRONG const double relativeTime = 0.1; var main = new FakeStackSourceSample( relativeTime: relativeTime, name: "Main", frameIndex: (StackSourceFrameIndex)5, // 5 is first non-taken enum value stackIndex: (StackSourceCallStackIndex)1, // 1 is first non-taken enum value callerIndex: StackSourceCallStackIndex.Invalid); var wrong = new FakeStackSourceSample( relativeTime: relativeTime, name: "WRONG", frameIndex: kind, stackIndex: (StackSourceCallStackIndex)2, callerIndex: main.StackIndex); var allSamples = new[] { main, wrong }; var leafs = new[] { new Sample(wrong.StackIndex, -1, wrong.RelativeTime, wrong.Metric, -1) }; var stackSource = new StackSourceStub(allSamples); var frameNameToId = new Dictionary <string, int>(); var frameIdToSamples = WalkTheStackAndExpandSamples(stackSource, leafs, frameNameToId, new Dictionary <int, FrameInfo>()); Assert.Equal(0, frameNameToId[main.Name]); Assert.False(frameNameToId.ContainsKey(wrong.Name)); var theOnlySample = frameIdToSamples.Single().Value.Single(); Assert.Equal(relativeTime, theOnlySample.RelativeTime); Assert.Equal(0, theOnlySample.Depth); }
internal bool InUse; // can't be reused. Someone is pointing at it. internal void CloneValue(StackInfo other) { // Note that I intentionally don't clone the StackIndex or IncPathsMatchedSoFarStorage as these are not the 'value' of this node. FrameIndex = other.FrameIndex; CallerIndex = other.CallerIndex; IsOSMethod = other.IsOSMethod; }
protected override StackSourceCallStackIndex InternCallerStack(StackSourceFrameIndex frameIndex, StackSourceCallStackIndex stackIndex) { lock (internCallStackLock) { return(this.Interner.CallStackIntern(frameIndex, stackIndex)); } }
internal void StopScan(StreamscanrequestStartArgs_V1TraceData data) { // Get the requesting user process based on the PID logged inside the engine. TraceProcess process = _traceLog.Processes.GetProcess(data.PID, data.TimeStampRelativeMSec); ProcessIndex processIndex = process.ProcessIndex; if (processIndex == ProcessIndex.Invalid) { return; } // Get the file scan operation. Dictionary <ThreadIndex, FileScanOperation> processContainer = GetOrCreateProcessContainer(processIndex); FileScanOperation operation = processContainer.Values.Where(s => s.File.Equals(data.Path, System.StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); if (operation != null) { operation.StopTimeRelativeMSec = data.TimeStampRelativeMSec; // Create the stack. StackSourceFrameIndex fileNodeIndex = _stackSource.Interner.FrameIntern($"File ({operation.File})"); StackSourceFrameIndex reasonNodeIndex = _stackSource.Interner.FrameIntern($"Reason ({(operation.Reason != null ? operation.Reason : "Unknown")})"); StackSourceFrameIndex resultNodeIndex = _stackSource.Interner.FrameIntern($"Scan Result ({operation.Result})"); _sample.StackIndex = _stackSource.Interner.CallStackIntern(resultNodeIndex, operation.RequestorStack); _sample.StackIndex = _stackSource.Interner.CallStackIntern(reasonNodeIndex, _sample.StackIndex); _sample.StackIndex = _stackSource.Interner.CallStackIntern(fileNodeIndex, _sample.StackIndex); _sample.Metric = (float)(operation.StopTimeRelativeMSec - operation.StartTimeRelativeMSec); _sample.TimeRelativeMSec = operation.StartTimeRelativeMSec; _stackSource.AddSample(_sample); } }
public override string GetFrameName(StackSourceFrameIndex frameIndex, bool fullModulePath) { if (frameIndex >= (StackSourceFrameIndex)base.CallFrameIndexLimit) { return(m_Interner.GetFrameName(frameIndex, fullModulePath)); } return(base.GetFrameName(frameIndex, fullModulePath)); }
public override string GetFrameName(StackSourceFrameIndex frameIndex, bool verboseName) { if (frameIndex >= StackSourceFrameIndex.Start) { return((frameIndex - StackSourceFrameIndex.Start).ToString()); } return(frameIndex.ToString()); }
/// <summary> /// Implementation of StackSource protocol. /// </summary> public StackSourceModuleIndex GetModuleIndex(StackSourceFrameIndex frameIndex) { if (m_Interner.FrameStartIndex <= frameIndex) { return(m_Interner.GetModuleIndex(frameIndex)); } return(StackSourceModuleIndex.Invalid); // TODO FIX NOW this is a poor approximation }
public FakeStackSourceSample(double relativeTime, string name, StackSourceFrameIndex frameIndex, StackSourceCallStackIndex stackIndex, StackSourceCallStackIndex callerIndex) { RelativeTime = relativeTime; Name = name; FrameIndex = frameIndex; StackIndex = stackIndex; CallerIndex = callerIndex; }
public StackSourceModuleIndex GetModuleIndex(StackSourceFrameIndex frameIndex) { if (frameIndex >= (StackSourceFrameIndex)base.CallFrameIndexLimit) { return(m_Interner.GetModuleIndex(frameIndex)); } return(StackSourceModuleIndex.Invalid); // TODO FIX NOW this is a poor approximation }
public SourceLocation GetSourceLine(StackSourceFrameIndex frameIndex, SymbolReader reader) { uint codeAddressIndex = (uint)frameIndex - (uint)StackSourceFrameIndex.Start; if (codeAddressIndex >= m_log.CodeAddresses.MaxCodeAddressIndex) { return(null); } return(m_log.CodeAddresses.GetSourceLine(reader, (CodeAddressIndex)codeAddressIndex)); }
/// <summary> /// Given a frame index, return the cooresponding code address for it. /// </summary> public CodeAddressIndex GetFrameCodeAddress(StackSourceFrameIndex frameIndex) { uint codeAddressIndex = (uint)frameIndex - (uint)StackSourceFrameIndex.Start; if (codeAddressIndex >= m_log.CodeAddresses.MaxCodeAddressIndex) { return(CodeAddressIndex.Invalid); } return((CodeAddressIndex)codeAddressIndex); }
/// <summary> /// Get the 'logcal' call stack from PROCESS ROOT (the root of all stacks) to (but not including) the frame for the /// thread. By default (if you can't attribute it to anything else) it will just be attributed to the process, however /// it is likley that you want to insert pseudo-frames for the request and other logical groupings here. /// /// The actual method frames within a thread, as well as any resource specific pseduo-frames (e.g. BLOCKING, ...) /// are added by the ComputingResourceMachine itself. ///</summary> public virtual StackSourceCallStackIndex GetCallStackIndex(MutableTraceEventStackSource stackSource, TraceThread thread, TraceEvent data) { var callStackIndex = stackSource.GetCallStackForProcess(thread.Process); // There is no request, so add this stack as an unattributed sample. string frameName = "Unattributed"; StackSourceFrameIndex requestsFrameIndex = stackSource.Interner.FrameIntern(frameName); callStackIndex = stackSource.Interner.CallStackIntern(requestsFrameIndex, callStackIndex); return(callStackIndex); }
public override string GetFrameName(StackSourceFrameIndex frameIndex, bool verboseName) { var frame = this.frames[(int)frameIndex]; if ((int)frameIndex < this.pseudoFramesStartOffset) { return(this.eipToNameProvider.GetSymbolicName(frame.ProcessId, frame.InstructionPointer)); } return(this.pseudoFrames[(int)frame.ProcessId]); }
/// <summary> /// Gets the name of a frame. /// </summary> /// <param name="frameIndex">The frame to look up.</param> /// <param name="verboseName">Whether to include full module paths.</param> /// <returns>The name of the frame.</returns> public override string GetFrameName(StackSourceFrameIndex frameIndex, bool verboseName) { Debug.Assert((int)frameIndex >= 0 && (int)frameIndex < CallFrameIndexLimit); int source = m_frameMap.SourceOf(frameIndex); Debug.Assert(source >= 0); var offset = m_frameMap.OffsetOf(source, frameIndex); return(m_sources[source].GetFrameName(offset, verboseName)); }
private void OnGCAllocationTick(GCAllocationTickTraceData data) { TraceThread thread = data.Thread(); Debug.Assert(thread != null); if (null == thread) { return; } // Attempt to charge the allocation to a request. ScenarioThreadState scenarioThreadState = m_Configuration.ScenarioThreadState[(int)thread.ThreadIndex]; CallStackIndex traceLogCallStackIndex = data.CallStackIndex(); StackSourceCallStackIndex callStackIndex = scenarioThreadState.GetCallStackIndex(m_OutputStackSource, thread, data); // Add the thread. StackSourceFrameIndex threadFrameIndex = m_OutputStackSource.Interner.FrameIntern(thread.VerboseThreadName); callStackIndex = m_OutputStackSource.Interner.CallStackIntern(threadFrameIndex, callStackIndex); // Get the allocation call stack index. callStackIndex = m_OutputStackSource.GetCallStack(traceLogCallStackIndex, callStackIndex, null); // Add the type. string typeName = data.TypeName; if (typeName.Length > 0) { StackSourceFrameIndex nodeIndex = m_OutputStackSource.Interner.FrameIntern("Type " + data.TypeName); callStackIndex = m_OutputStackSource.Interner.CallStackIntern(nodeIndex, callStackIndex); } // Add a notification for large objects. if (data.AllocationKind == GCAllocationKind.Large) { StackSourceFrameIndex nodeIndex = m_OutputStackSource.Interner.FrameIntern("LargeObject"); callStackIndex = m_OutputStackSource.Interner.CallStackIntern(nodeIndex, callStackIndex); } // Set the time. m_Sample.TimeRelativeMSec = data.TimeStampRelativeMSec; // Set the metric. bool seenBadAllocTick = false; m_Sample.Metric = data.GetAllocAmount(ref seenBadAllocTick); // Set the stack index. m_Sample.StackIndex = callStackIndex; // Add the sample. m_OutputStackSource.AddSample(m_Sample); }
public FakeStackSourceSample(bool isLastOnCallStack, double relativeTime, float metric, string name, StackSourceFrameIndex frameIndex, StackSourceCallStackIndex stackIndex, StackSourceCallStackIndex callerIndex) { IsLastOnCallStack = isLastOnCallStack; RelativeTime = relativeTime; Metric = metric; Name = name; FrameIndex = frameIndex; StackIndex = stackIndex; CallerIndex = callerIndex; }
/// <summary> /// Add a sample representing the pinned object allocation. /// </summary> private void WriteAllocationStack( Address objectAddress, PinningRoot pinnedRoot, PinningStackAnalysisObject liveObjectInfo) { // Get the pinned object from the pinned root. PinnedObject pinnedObject = null; foreach (PinnedObject o in pinnedRoot.PinnedObjects) { if (o.ObjectAddress == objectAddress) { pinnedObject = o; break; } } // This should not happen, but we put this here to ensure that we don't crash. if (null == pinnedObject) { System.Diagnostics.Debug.Assert(false, "Pinned object could not be found, but was found in the _RootTable."); return; } // Get the allocation call stack. StackSourceCallStackIndex rootCallStackIndex = liveObjectInfo.AllocStack; // Add the generation pseudo-node. string generationString = "GENERATION " + pinnedObject.Generation; StackSourceFrameIndex generationFrameIndex = _StackSource.Interner.FrameIntern(generationString); StackSourceCallStackIndex callStackIndex = _StackSource.Interner.CallStackIntern(generationFrameIndex, rootCallStackIndex); // Add the type of the object. string objectTypeString = "OBJECT_TYPE " + pinnedObject.ObjectType; StackSourceFrameIndex objectTypeFrameIndex = _StackSource.Interner.FrameIntern(objectTypeString); callStackIndex = _StackSource.Interner.CallStackIntern(objectTypeFrameIndex, callStackIndex); // Set the object instance. string objectInstanceString = "OBJECT_INSTANCE " + pinnedObject.ObjectAddress; StackSourceFrameIndex objectInstanceFrameIndex = _StackSource.Interner.FrameIntern(objectInstanceString); callStackIndex = _StackSource.Interner.CallStackIntern(objectInstanceFrameIndex, callStackIndex); // Setup the sample. _Sample.TimeRelativeMSec = liveObjectInfo.AllocationTimeRelativeMSec; _Sample.Metric = pinnedObject.ObjectSize; _Sample.StackIndex = callStackIndex; _StackSource.AddSample(_Sample); }
/// <summary> /// Mark the thread as unblocked. /// </summary> public void LogBlockingStop( ComputingResourceStateMachine stateMachine, TraceThread thread, TraceEvent data) { if ((null == stateMachine) || (null == thread) || (null == data)) { return; } // Only add a sample if the thread was blocked. if (ThreadBlocked) { StackSourceSample sample = stateMachine.Sample; MutableTraceEventStackSource stackSource = stateMachine.StackSource; // Set the time and metric. sample.TimeRelativeMSec = this.BlockTimeStartRelativeMSec; sample.Metric = (float)(data.TimeStampRelativeMSec - this.BlockTimeStartRelativeMSec); /* Generate the stack trace. */ CallStackIndex traceLogCallStackIndex = data.CallStackIndex(); ScenarioThreadState scenarioThreadState = stateMachine.Configuration.ScenarioThreadState[ThreadIndex]; StackSourceCallStackIndex callStackIndex = scenarioThreadState.GetCallStackIndex(stateMachine.StackSource, thread, data); // Add the thread. StackSourceFrameIndex threadFrameIndex = stackSource.Interner.FrameIntern(thread.VerboseThreadName); callStackIndex = stackSource.Interner.CallStackIntern(threadFrameIndex, callStackIndex); // Add the full call stack. callStackIndex = stackSource.GetCallStack(traceLogCallStackIndex, callStackIndex, null); // Add Pseud-frames representing the kind of resource. StackSourceFrameIndex frameIndex = stackSource.Interner.FrameIntern("BLOCKED TIME"); callStackIndex = stackSource.Interner.CallStackIntern(frameIndex, callStackIndex); // Add the call stack to the sample. sample.StackIndex = callStackIndex; // Add the sample. stackSource.AddSample(sample); // Mark the thread as executing. BlockTimeStartRelativeMSec = -1; } }
public override string GetFrameName(StackSourceFrameIndex frameIndex, bool verboseName) { NodeTypeIndex typeIndex = (NodeTypeIndex)frameIndex; // Orphan node support if (typeIndex == m_graph.NodeTypeIndexLimit) { return("[not reachable from roots]"); } var type = m_graph.GetType(typeIndex, m_typeStorage); var moduleName = type.ModuleName; var ret = type.Name; if (moduleName != null) { if (verboseName) { int length = moduleName.Length - 4; if ((length >= 0) && (moduleName[length] == '.')) { moduleName = moduleName.Substring(0, length); } } else { moduleName = System.IO.Path.GetFileNameWithoutExtension(moduleName); } if (moduleName.Length == 0) { moduleName = "?"; } ret = moduleName + "!" + ShortenNameSpaces(type); } // TODO FIX NOW remove priority // ret += " " + m_typePriorities[(int)type.Index].ToString("f1"); // TODO FIX NOW hack for CLRProfiler comparison // ret = Regex.Replace(ret, @" *\[\]", "[]"); // ret = Regex.Replace(ret, @"`\d+", ""); return(ret); }
/// <summary> /// Write out a sample for each pin operation of the input object. /// </summary> private void WritePinningStacks( Address objectAddress, PinningRoot pinnedRoot, PinningStackAnalysisObject liveObjectInfo) { if (liveObjectInfo.PinInfo != null) { foreach (PinningStackAnalysisPinInfo pinInfo in liveObjectInfo.PinInfo) { // Get the pinning stack and the time. foreach (PinnedObject pinnedObject in pinnedRoot.PinnedObjects) { // Add the generation pseudo-node. string generationString = "GENERATION " + pinnedObject.Generation; StackSourceFrameIndex generationFrameIndex = _StackSource.Interner.FrameIntern(generationString); StackSourceCallStackIndex callStackIndex = _StackSource.Interner.CallStackIntern(generationFrameIndex, pinInfo.PinStack); // Add the root type. string rootTypeString = "ROOT_TYPE " + pinnedRoot.RootType.ToString(); StackSourceFrameIndex rootTypeFrameIndex = _StackSource.Interner.FrameIntern(rootTypeString); callStackIndex = _StackSource.Interner.CallStackIntern(rootTypeFrameIndex, callStackIndex); // Add the type of the object. string objectTypeString = "OBJECT_TYPE " + pinnedObject.ObjectType; StackSourceFrameIndex objectTypeFrameIndex = _StackSource.Interner.FrameIntern(objectTypeString); callStackIndex = _StackSource.Interner.CallStackIntern(objectTypeFrameIndex, callStackIndex); // Set the object instance. string objectInstanceString = "OBJECT_INSTANCE " + pinnedObject.ObjectAddress; StackSourceFrameIndex objectInstanceFrameIndex = _StackSource.Interner.FrameIntern(objectInstanceString); callStackIndex = _StackSource.Interner.CallStackIntern(objectInstanceFrameIndex, callStackIndex); // Set the metric to 1 since the metric represents the number of pin operations. _Sample.Metric = 1; // Set the call stack. _Sample.TimeRelativeMSec = pinInfo.PinTimeRelativeMSec; _Sample.StackIndex = callStackIndex; _StackSource.AddSample(_Sample); } } } }
public override string GetFrameName(StackSourceFrameIndex frameIndex, bool verboseName) { if (frameIndex < StackSourceFrameIndex.Start) { return(System.Enum.GetName(typeof(StackSourceFrameIndex), frameIndex)); // TODO can be more efficient } frameIndex = (StackSourceFrameIndex)((int)frameIndex - StackSourceFrameIndex.Start); // a frame index might be a CLRProfiler method index, or it might be CLRProfiler nodeId index if ((uint)frameIndex < (uint)m_clrProfiler.MethodIdLimit) { return("!" + m_clrProfiler.GetMethodById((ProfilerMethodID)((int)frameIndex)).FullName); } else { var typeId = (ProfilerTypeID)((int)frameIndex - (int)m_clrProfiler.MethodIdLimit); return("ALLOC " + m_clrProfiler.GetTypeById(typeId).name); } }
private void AddUnkownAsyncDurationIfNeeded(StartStopActivity startStopActivity, double unknownStartTimeMSec, TraceEvent data) { Debug.Assert(0 < unknownStartTimeMSec); Debug.Assert(unknownStartTimeMSec <= data.TimeStampRelativeMSec); if (startStopActivity.IsStopped) { return; } // We dont bother with times that are too small, we consider 1msec the threshold double delta = data.TimeStampRelativeMSec - unknownStartTimeMSec; if (delta < 1) { return; } // Add a sample with the amount of unknown duration. var sample = new StackSourceSample(m_outputStackSource); sample.Metric = (float)delta; sample.TimeRelativeMSec = unknownStartTimeMSec; StackSourceCallStackIndex stackIndex = m_startStopActivities.GetStartStopActivityStack(m_outputStackSource, startStopActivity, data.Process()); StackSourceFrameIndex unknownAsyncFrame = m_outputStackSource.Interner.FrameIntern("UNKNOWN_ASYNC"); stackIndex = m_outputStackSource.Interner.CallStackIntern(unknownAsyncFrame, stackIndex); sample.StackIndex = stackIndex; // We can't add the samples right now because AWAIT nodes might overlap and we have to take these back. // The add the to this list so that they can be trimmed at that time if needed. List <StackSourceSample> list = m_startStopActivityToAsyncUnknownSamples.Get((int)startStopActivity.Index); if (list == null) { list = new List <StackSourceSample>(); m_startStopActivityToAsyncUnknownSamples.Set((int)startStopActivity.Index, list); } list.Add(sample); }
/// <summary> /// Log a CPU sample on this thread. /// </summary> public void LogCPUSample( ComputingResourceStateMachine stateMachine, TraceThread thread, TraceEvent data) { if ((null == stateMachine) || (null == thread) || (null == data)) { return; } StackSourceSample sample = stateMachine.Sample; sample.Metric = 1; sample.TimeRelativeMSec = data.TimeStampRelativeMSec; MutableTraceEventStackSource stackSource = stateMachine.StackSource; // Attempt to charge the CPU to a request. CallStackIndex traceLogCallStackIndex = data.CallStackIndex(); ScenarioThreadState scenarioThreadState = stateMachine.Configuration.ScenarioThreadState[ThreadIndex]; StackSourceCallStackIndex callStackIndex = scenarioThreadState.GetCallStackIndex(stackSource, thread, data); // Add the thread. StackSourceFrameIndex threadFrameIndex = stackSource.Interner.FrameIntern(thread.VerboseThreadName); callStackIndex = stackSource.Interner.CallStackIntern(threadFrameIndex, callStackIndex); // Rest of the stack. // NOTE: Do not pass a call stack map into this method, as it will skew results. callStackIndex = stackSource.GetCallStack(traceLogCallStackIndex, callStackIndex, null); // Add the CPU frame. StackSourceFrameIndex cpuFrameIndex = stackSource.Interner.FrameIntern("CPU"); callStackIndex = stackSource.Interner.CallStackIntern(cpuFrameIndex, callStackIndex); // Add the sample. sample.StackIndex = callStackIndex; stackSource.AddSample(sample); }
public override string GetFrameName(StackSourceFrameIndex frameIndex, bool verboseName) { string ret = m_frames.Get((int)frameIndex); if (!verboseName) { var shortName = m_shortFrameNames.Get((int)frameIndex); if (shortName == null) { shortName = ret; // Strip off anything before the last \\ before the ! var exclaimIdx = ret.IndexOf('!'); if (0 < exclaimIdx) { // Becomes 0 if it fails, which is what we want. var startIdx = ret.LastIndexOfAny(s_directorySeparators, exclaimIdx - 1, exclaimIdx - 1) + 1; shortName = ret.Substring(startIdx); } m_shortFrameNames.Set((int)frameIndex, shortName); } ret = shortName; } return(ret); }
/// <summary> /// Gets the name of a frame. /// </summary> /// <param name="frameIndex">The frame to look up.</param> /// <param name="verboseName">Whether to include full module paths.</param> /// <returns>The name of the frame.</returns> public override string GetFrameName(StackSourceFrameIndex frameIndex, bool verboseName) { if (frameIndex < StackSourceFrameIndex.Start) { switch (frameIndex) { case StackSourceFrameIndex.Broken: return("BROKEN"); case StackSourceFrameIndex.Overhead: return("OVERHEAD"); case StackSourceFrameIndex.Root: return("ROOT"); default: return("?!?"); } } else { return(names[frameIndex - StackSourceFrameIndex.Start]); } }
/// <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. } } }
protected virtual StackSourceCallStackIndex InternCallerStack(StackSourceFrameIndex frameIndex, StackSourceCallStackIndex stackIndex) { return(this.Interner.CallStackIntern(frameIndex, stackIndex)); }
/// <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, IgnoreApplicationInsightsRequestsWithRelatedActivityId); // 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; }
public abstract ValueTask <SourceLocation> GetSourceLocation(StackSourceFrameIndex frameIndex);
/// <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); }