internal void Increment(TraceEvent data) { #if DEBUG // Debug.Assert((byte)data.opcode != unchecked((byte)-1)); // Means PrepForCallback not done. Debug.Assert(data.TaskName != "ERRORTASK"); Debug.Assert(data.OpcodeName != "ERROROPCODE"); #endif Count++; TaskStats task = this[data.TaskName]; if (task.ProviderName == null) { task.ProviderName = data.ProviderName; } CallStackIndex index = data.CallStackIndex(); bool hasStack = (index != CallStackIndex.Invalid); if (hasStack) { StackCount++; } task.Increment(data.OpcodeName, hasStack); StackWalkTraceData asStackWalk = data as StackWalkTraceData; if (asStackWalk != null) { StackWalkStats stackWalkStats = task.ExtraData as StackWalkStats; if (stackWalkStats == null) { stackWalkStats = new StackWalkStats(); task.ExtraData = stackWalkStats; } stackWalkStats.Log(asStackWalk); } }
/// <summary> /// Convert the TraceEvent callStack 'callStackIdx' into a StackSourceCallStack. 'callStackIdx is /// assumed to be related to the traceEvent 'data'. 'data' is used to determine the process and thread /// of the stack. (TODO data may not be needed anymore as callStackIndexes do encode their thread (and thus process)). /// </summary> public StackSourceCallStackIndex GetCallStack(CallStackIndex callStackIndex, TraceEvent data) { // This only happens if only have ONLY the thread and process (no addressses) // TODO do we care about this case? Should we remove? var thread = data.Thread(); StackSourceCallStackIndex topOfStack; if (thread == null) { var process = data.Process(); if (process != null) { topOfStack = GetCallStackForProcess(process); } else { topOfStack = StackSourceCallStackIndex.Invalid; } } else { topOfStack = GetCallStackForThread(thread); } return(GetCallStack(callStackIndex, topOfStack, m_callStackMap)); }
/// <summary> /// Find the StackSourceCallStackIndex for the TraceEvent call stack index 'callStackIndex' which has a top of its /// stack (above the stack, where the thread and process would normally go) as 'top'. If callStackMap is non-null /// it is used as an interning table for CallStackIndex -> StackSourceCallStackIndex. This can speed up the /// transformation dramatically. It will still work if it is null. /// </summary> /// public StackSourceCallStackIndex GetCallStack(CallStackIndex callStackIndex, StackSourceCallStackIndex top, CallStackMap callStackMap = null) { if (callStackIndex == CallStackIndex.Invalid) { return(top); } StackSourceCallStackIndex cachedValue; if (callStackMap != null) { cachedValue = callStackMap.Get(callStackIndex); if (cachedValue != StackSourceCallStackIndex.Invalid) { return(cachedValue); } } var frameIdx = GetFrameIndex(m_log.CallStacks.CodeAddressIndex(callStackIndex)); CallStackIndex nonInternedCallerIdx = m_log.CallStacks.Caller(callStackIndex); StackSourceCallStackIndex callerIdx; if (nonInternedCallerIdx == CallStackIndex.Invalid) { callerIdx = top; if (!OnlyManagedCodeStacks) { var frameName = GetFrameName(frameIdx, false); var bangIdx = frameName.IndexOf('!'); if (0 < bangIdx) { if (!(5 <= bangIdx && string.Compare(frameName, bangIdx - 5, "ntdll", 0, 5, StringComparison.OrdinalIgnoreCase) == 0)) { var brokenFrame = m_Interner.FrameIntern("BROKEN", m_emptyModuleIdx); callerIdx = m_Interner.CallStackIntern(brokenFrame, callerIdx); } } } } else { callerIdx = GetCallStack(nonInternedCallerIdx, top, callStackMap); } var ret = m_Interner.CallStackIntern(frameIdx, callerIdx); if (callStackMap != null) { callStackMap.Put(callStackIndex, ret); } return(ret); }
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); }
// Caller only need to provide TraceThread public StackSourceCallStackIndex GetCallStackThread(CallStackIndex callStackIndex, TraceThread thread) { if (callStackIndex == CallStackIndex.Invalid) { if (thread == null) { return(StackSourceCallStackIndex.Invalid); } return(GetCallStackForThread(thread)); } var idx = (int)StackSourceCallStackIndex.Start + (int)callStackIndex; return((StackSourceCallStackIndex)idx); }
/// <summary> /// Find the StackSourceCallStackIndex for the TraceEvent call stack index 'callStackIndex' which has a top of its /// stack as 'top'. If callStckMap is non-null it is used as an interning table for CallStackIndex -> StackSourceCallStackIndex. /// This can speed up the transformation dramatically. /// </summary> public StackSourceCallStackIndex GetCallStack(CallStackIndex callStackIndex, StackSourceCallStackIndex top, Dictionary <int, StackSourceCallStackIndex> callStackMap) { if (callStackIndex == CallStackIndex.Invalid) { return(top); } StackSourceCallStackIndex cachedValue; if (callStackMap != null && callStackMap.TryGetValue((int)callStackIndex, out cachedValue)) { return(cachedValue); } var frameIdx = GetFrameIndex(m_log.CallStacks.CodeAddressIndex(callStackIndex)); CallStackIndex nonInternedCallerIdx = m_log.CallStacks.Caller(callStackIndex); StackSourceCallStackIndex callerIdx; if (nonInternedCallerIdx == CallStackIndex.Invalid) { callerIdx = top; var frameName = GetFrameName(frameIdx, false); var bangIdx = frameName.IndexOf('!'); if (0 < bangIdx) { if (!(5 <= bangIdx && string.Compare(frameName, bangIdx - 5, "ntdll", 0, 5, StringComparison.OrdinalIgnoreCase) == 0)) { var brokenFrame = m_Interner.FrameIntern("BROKEN", m_emptyModuleIdx); callerIdx = m_Interner.CallStackIntern(brokenFrame, callerIdx); } } } else { callerIdx = GetCallStack(nonInternedCallerIdx, top, callStackMap); } var ret = m_Interner.CallStackIntern(frameIdx, callerIdx); if (callStackMap != null) { callStackMap[(int)callStackIndex] = ret; } return(ret); }
/// <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; } }
/// <summary> /// Find the StackSourceCallStackIndex for the TraceEvent call stack index 'callStackIndex' which has a top of its /// stack as 'top'. If callStckMap is non-null it is used as an interning table for CallStackIndex -> StackSourceCallStackIndex. /// This can speed up the transformation dramatically. /// </summary> /// <param name="callStackIndex"></param> /// <param name="top"></param> /// <param name="callStackMap"></param> /// <returns></returns> public StackSourceCallStackIndex GetCallStack(CallStackIndex callStackIndex, StackSourceCallStackIndex top, Dictionary <int, StackSourceCallStackIndex> callStackMap) { if (callStackIndex == CallStackIndex.Invalid) { return(top); } StackSourceCallStackIndex cachedValue; if (callStackMap != null && callStackMap.TryGetValue((int)callStackIndex, out cachedValue)) { return(cachedValue); } bool isReasonableTopStack; var frameIdx = GetFrameIndex(m_log.CallStacks.CodeAddressIndex(callStackIndex), out isReasonableTopStack); CallStackIndex nonInternedCallerIdx = m_log.CallStacks.Caller(callStackIndex); StackSourceCallStackIndex callerIdx; if (nonInternedCallerIdx == CallStackIndex.Invalid) { callerIdx = top; if (!isReasonableTopStack) { var brokenFrame = Interner.FrameIntern("BROKEN", m_emptyModuleIdx); callerIdx = Interner.CallStackIntern(brokenFrame, callerIdx); } } else { callerIdx = GetCallStack(nonInternedCallerIdx, top, callStackMap); } var ret = Interner.CallStackIntern(frameIdx, callerIdx); if (callStackMap != null) { callStackMap[(int)callStackIndex] = ret; } return(ret); }
// Note that this always returns a stack, since we know the thread and process. public StackSourceCallStackIndex GetCallStack(CallStackIndex callStackIndex, TraceEvent data) { if (callStackIndex == CallStackIndex.Invalid) { if (data == null) { return(StackSourceCallStackIndex.Invalid); } var thread = data.Thread(); if (thread == null) { return(StackSourceCallStackIndex.Invalid); } return(GetCallStackForThread(thread)); } var idx = (int)StackSourceCallStackIndex.Start + (int)callStackIndex; return((StackSourceCallStackIndex)idx); }
/// <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); }
internal void OnVirtualMem(VirtualAllocTraceData data) { VirtualAllocTraceData.VirtualAllocFlags flags = data.Flags; ModuleClass alloc = ModuleClass.Free; if ((flags & VirtualAllocTraceData.VirtualAllocFlags.MEM_COMMIT) != 0) { alloc = ModuleClass.Unknown; CallStackIndex s = m_stackDecoder.FirstFrame(data.EventIndex); while (s != CallStackIndex.Invalid) { ModuleClass old = alloc; alloc = m_stackDecoder.GetModuleClass(s); if ((old == ModuleClass.OSUser) && (alloc != old)) { break; } s = m_stackDecoder.GetCaller(s); } } else if ((flags & (VirtualAllocTraceData.VirtualAllocFlags.MEM_DECOMMIT | VirtualAllocTraceData.VirtualAllocFlags.MEM_RELEASE)) == 0) { return; } if (m_MemMap == null) { m_MemMap = new Dictionary <ulong, ModuleClass[]>(); m_ModuleVMSize = new ulong[(int)(ModuleClass.Max)]; } Debug.Assert((data.BaseAddr % PageSize) == 0); Debug.Assert((data.Length % PageSize) == 0); ulong addr = data.BaseAddr; // TODO because of the algorithm below, we can't handle very large blocks of memory // because it is linear in size of the alloc or free. It turns out that sometimes // we free very large chunks. Thus cap it. This is not accurate but at least we // complete. long len = data.Length / PageSize; if (len > 0x400000) // Cap it at 4M pages = 16GB chunks. { len = 0x400000; } while (len > 0) { ModuleClass[] bit; ulong region = addr / OneMB; if (!m_MemMap.TryGetValue(region, out bit)) { bit = new ModuleClass[OneMB / PageSize]; m_MemMap[region] = bit; } int offset = (int)(addr % OneMB) / PageSize; if (alloc != bit[offset]) { ModuleClass old = bit[offset]; bit[offset] = alloc; if (alloc != ModuleClass.Free) { if (old == ModuleClass.Free) { m_ModuleVMSize[(int)alloc] += PageSize; m_VMSize += PageSize; if (m_VMSize > m_MaxVMSize) { m_MaxVMSize = m_VMSize; } } } else { m_ModuleVMSize[(int)old] -= PageSize; m_VMSize -= PageSize; } } addr += PageSize; len--; } if (m_VMCurve == null) { m_VMCurve = new List <double>(); } double clrSize = m_ModuleVMSize[(int)ModuleClass.Clr] / OneMBD; double graphSize = m_ModuleVMSize[(int)ModuleClass.OSGraphics] / OneMBD; double win8StoreSize; if (m_stackDecoder.WwaHost) { win8StoreSize = m_ModuleVMSize[(int)ModuleClass.JScript] / OneMBD; } else { win8StoreSize = m_ModuleVMSize[(int)ModuleClass.Win8Store] / OneMBD; } m_VMCurve.Add(data.TimeStampRelativeMSec); m_VMCurve.Add(clrSize); m_VMCurve.Add(clrSize + graphSize); m_VMCurve.Add(clrSize + graphSize + win8StoreSize); m_VMCurve.Add(m_VMSize / OneMBD); }
/// <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); }