public StackResult ComputeStacks()

in src/CSVReader/ETLStackBrowse/Stack.cs [130:545]


            public StackResult ComputeStacks(ETLTrace trace, IStackParameters parms)
            {
                this.parms = parms;
                this.trace = trace;

                t0 = trace.Parameters.T0;
                t1 = trace.Parameters.T1;

                if (t0 == t1)
                {
                    t1 = t0 + 1; // just add 1ms to get a non-zero window
                }

                atomsRecords = trace.atomsRecords;
                atomsFields = trace.atomsFields;
                recordInfo = trace.CommonFieldIds;
                listEventFields = trace.EventFields;
                threads = trace.Threads;
                stackIgnoreEvents = trace.StackIgnoreEvents;
                fElideGenerics = trace.UIParameters.ElideGenerics;
                fUnmangleBartok = trace.UIParameters.UnmangleBartokSymbols;

                frameFilters = parms.FrameFilters;
                butterflyPivot = parms.ButterflyPivot;
                fUseExeFrame = parms.UseExeFrame;
                fUsePid = parms.UsePid;
                fUseTid = parms.UseTid;
                fFoldModules = parms.FoldModules;
                fUseRootAI = parms.UseRootAI;
                fUseIODuration = parms.UseIODuration;
                fReserved = parms.AnalyzeReservedMemory;

                backpatchRecordType = new int[atomsRecords.Count];

                MemProcessor memProcessor = new MemProcessor(trace);
                MemEffect memEffect = new MemEffect();

                ParseFrameFilters();

                PrepareNewTreeRoot();

                idDPC = atomsRecords.Lookup("DPC");
                idStack = atomsRecords.Lookup("Stack");
                idVAlloc = atomsRecords.Lookup("VirtualAlloc");
                idVFree = atomsRecords.Lookup("VirtualFree");
                idCSwitch = atomsRecords.Lookup("CSwitch");

                idFileIoOpEnd = atomsRecords.Lookup("FileIoOpEnd");
                idDiskRead = atomsRecords.Lookup("DiskRead");
                idDiskReadInit = atomsRecords.Lookup("DiskReadInit");
                idDiskWrite = atomsRecords.Lookup("DiskWrite");
                idDiskWriteInit = atomsRecords.Lookup("DiskWriteInit");

                idIStart = atomsRecords.Lookup("I-Start");
                idIDCStart = atomsRecords.Lookup("I-DCStart");
                idIEnd = atomsRecords.Lookup("I-End");

                // we're tracking previous context switch events for each thread 
                // so we can use the delay as a cost metric
                ThreadStat[] stats = trace.NewThreadStats();

                byThreadDesc = new byte[threads.Count][];

                for (int i = 0; i < threads.Count; i++)
                {
                    ThreadInfo ti = threads[i];
                    byThreadDesc[i] = ByteWindow.MakeBytes(String.Format("tid ({0,5})", ti.threadid));
                }

                for (int i = 0; i < stats.Length; i++)
                {
                    stats[i].time = t0;
                }

                // we keep previous events in the event stream so that
                // we can associate a stack with one of those events
                // the stacks usually come right after the event
                // but there could me intervening events
                // and those might have stacks as well
                // the stack has to match the thread and the timestamp
                // of the previous event for it to be associated with 
                // that event

                const int maxEvent = 16;
                PreviousEvent[] prev = new PreviousEvent[maxEvent];
                int iNextEvent = 0;

                // we keep a goodly number of previous stacks so that we can resolve pending I/O's from the past
                // there is no great magic number here but we want enough for the number of CPUs and plenty
                // for pending IOs.  In the event that we have not figured out the number of CPU's 
                // the IO suggested size is probably enough anyway

                int maxPendingStacks = Math.Max(trace.MaxCPU, 32); // should be enough for a large number of outstanding IOs

                frameStates = new FrameState[maxPendingStacks];
                int victim = 0;

                for (int i = 0; i < maxPendingStacks; i++)
                {
                    frameStates[i].threadId = -1;
                }

                bool[] threadFilters = trace.Parameters.GetThreadFilters();
                bool[] filters = ComputeStackEventFilters(trace);

                bool fIsMemAnalysis = ((idVAlloc >= 0 && filters[idVAlloc]) || (idVFree >= 0 && filters[idVFree]));

                IdentifyRecordsToBackpatch(filters);

                ETWLineReader l = trace.StandardLineReader();
                foreach (ByteWindow b in l.Lines())
                {
                    // if this is an event that we don't recognized then skip it
                    if (l.idType < 0)
                    {
                        continue;
                    }

                    // if it's not a stack event then this might be an event that is introducing a stack
                    // so record the event and the time in the circular buffer in that case
                    if (l.idType != idStack)
                    {
                        if (stackIgnoreEvents[l.idType])
                        {
                            continue;
                        }

                        if (l.idType == idIStart || l.idType == idIDCStart)
                        {
                            // TODO This is a hack.  We try to determine the full path of a module name by
                            // looking what modules are loaded.  Currently machine wide, could make process wide
                            // pretty easily. 
                            var filePathBytes = new ByteWindow(b, fldImageFileName);
                            filePathBytes.Trim();
                            filePathBytes.ib++;
                            filePathBytes.len -= 2;
                            if (filePathBytes.len > 0)
                            {
                                var filePath = filePathBytes.GetString();
                                var index = filePath.LastIndexOf('\\');
                                if (index >= 0)
                                {
                                    var fileName = filePath.Substring(index + 1);
                                    fullModuleNames[fileName] = filePath;
                                }
                            }
                        }

                        // this is where we use DiskRead, DiskWrite, and FileIoOpEnd events to patch the past
                        if (TryChangeWeightOfPastEvent(l, b))
                        {
                            continue;
                        }

                        prev[iNextEvent].time = l.t;

                        if (!l.MatchingTextFilter() || !l.MatchingMemory())
                        {
                            prev[iNextEvent].eventId = -1;
                            prev[iNextEvent].tid = -1;
                            continue;
                        }
                        else
                        {
                            prev[iNextEvent].eventId = l.idType;

                            int iThreadField = recordInfo[l.idType].threadField;
                            if (iThreadField >= 0)
                            {
                                // we know a specific thread that generated this stack, require the match
                                prev[iNextEvent].tid = b.GetInt(iThreadField);
                            }
                            else
                            {
                                // -1 matches anything, unknown thread triggered the stack event
                                prev[iNextEvent].tid = -1;
                            }

                            prev[iNextEvent].weight = 0;

                            if (l.idType == idCSwitch)
                            {
                                int oldTid = b.GetInt(fldCSwitchOldTID);
                                int idxOld = trace.FindThreadInfoIndex(l.t, oldTid);
                                stats[idxOld].time = l.t;

                                int newTid = b.GetInt(fldCSwitchNewTID);
                                int idxNew = trace.FindThreadInfoIndex(l.t, newTid);

                                ulong waitTime = (ulong)(l.t - stats[idxNew].time);

                                prev[iNextEvent].weight = waitTime;
                                prev[iNextEvent].tid = newTid;
                            }
                            else if (fIsMemAnalysis && (l.idType == idVAlloc || l.idType == idVFree))
                            {
                                memProcessor.ProcessMemRecord(l, b, memEffect);

                                ulong size = 0;

                                if (l.idType == idVAlloc)
                                {
                                    if (fReserved)
                                    {
                                        size = memEffect.reserved;
                                    }
                                    else
                                    {
                                        size = memEffect.committed;
                                    }
                                }
                                else
                                {
                                    if (fReserved)
                                    {
                                        size = memEffect.released;
                                    }
                                    else
                                    {
                                        size = memEffect.decommitted;
                                    }
                                }

                                // this allocation didn't affect the statistic of interest... ignore it
                                if (size == 0)
                                {
                                    prev[iNextEvent].eventId = -1;
                                    prev[iNextEvent].tid = -1;
                                    continue;
                                }
                                else
                                {
                                    prev[iNextEvent].weight = size;
                                }
                            }
                            else if (l.idType == idIStart || l.idType == idIEnd)
                            {
                                ulong addrBase = b.GetHex(fldImageBaseAddr);
                                ulong addrEnd = b.GetHex(fldImageEndAddr);
                                ulong size = addrEnd - addrBase;

                                prev[iNextEvent].weight = size;
                            }
                            else if (recordInfo[l.idType].sizeField > 0)
                            {
                                // get IO Size if appropriate
                                if (!fUseIODuration)
                                {
                                    prev[iNextEvent].weight = (ulong)b.GetLong(recordInfo[l.idType].sizeField);
                                }
                            }

                            // add the synthetic filename field if there is one in the leaf record type
                            if (filters[l.idType] && recordInfo[l.idType].goodNameField >= 0)
                            {
                                bsym.Assign(b, recordInfo[l.idType].goodNameField).Trim();
                                prev[iNextEvent].filenameId = atomsNodeNames.EnsureContains(bsym);
                            }
                            else
                            {
                                prev[iNextEvent].filenameId = -1;
                            }
                        }

                        iNextEvent = (iNextEvent + 1) % maxEvent;
                        continue;
                    }

                    // ok at this point we definitely have a stack event

                    // make sure this stack is for a thread we are measuring

                    int threadId = b.GetInt(fldStackThreadId);

                    int idx = trace.FindThreadInfoIndex(l.t, threadId);

                    if (!threadFilters[idx])
                    {
                        continue;
                    }

                    // we care about this thread, look up the state this thread is in
                    // do we have a pending stack already

                    int iStack = 0;
                    for (iStack = 0; iStack < maxPendingStacks; iStack++)
                    {
                        if (frameStates[iStack].threadId == threadId && frameStates[iStack].time == l.t)
                        {
                            break;
                        }
                    }

                    // this is a new stack, not a continuation, so we may have to flush
                    if (iStack == maxPendingStacks)
                    {
                        // check to see if this new stack is of the correct type before we flush one

                        // first we look backwards to see if we can find the event that introduced this stack
                        bool fFound = true;

                        int i = iNextEvent;
                        for (; ; )
                        {
                            if (--i < 0)
                            {
                                i = maxEvent - 1;
                            }

                            // if the time matches and this is a desired event type then many we can use it
                            if (prev[i].time == l.t && prev[i].eventId >= 0 && filters[prev[i].eventId])
                            {
                                // the previous event has to be non-thread-specific (like VirtualAlloc)
                                // or else the thread has to match (for context switches)
                                if (prev[i].tid == -1)
                                {
                                    break;
                                }

                                if (prev[i].tid == threadId)
                                {
                                    break;
                                }
                            }

                            if (i == iNextEvent)
                            {
                                fFound = false;
                                break;
                            }
                        }

                        // if we don't have a previous record that corresonds to this stack
                        // or if we have a record but it is not one that we are collecting stacks for
                        // then we skip this stack entirely

                        if (!fFound)
                        {
                            continue;
                        }

                        victim++;
                        if (victim == maxPendingStacks)
                        {
                            victim = 0;
                        }

                        // ok we're going with this stack, so we have to flush a stack we've been building up (it's done by now)
                        // because only one stack can be pending for any given CPU at any time index

                        if (frameStates[victim].root != null)
                        {
                            ProcessFrames(treeRoot, victim);
                        }

                        frameStates[victim].root = null;
                        frameStates[victim].threadId = threadId;
                        frameStates[victim].time = l.t;
                        frameStates[victim].weight = prev[i].weight;
                        frameStates[victim].filenameId = prev[i].filenameId;
                        frameStates[victim].eventId = prev[i].eventId;

                        // this event is consumed, we can't use it twice even if another stack otherwise might seem to match
                        prev[i].eventId = -1;

                        iStack = victim;
                    }

                    bsym.Assign(b, fldStackSymbol).Trim();

                    if (fElideGenerics || fUnmangleBartok)
                    {
                        PostProcessSymbol(bsym);
                    }

                    if (fFoldModules)
                    {
                        bsym.Truncate((byte)'!');
                    }

                    int id = atomsNodeNames.EnsureContains(bsym);

                    if (!fFoldModules || frameStates[iStack].root == null || frameStates[iStack].root.id != id)
                    {
                        Frame f = new Frame();
                        f.next = frameStates[iStack].root;
                        f.id = id;
                        frameStates[iStack].root = f;
                    }
                }

                // flush pending frames
                for (int i = 0; i < maxPendingStacks; i++)
                {
                    if (frameStates[i].root != null)
                    {
                        ProcessFrames(treeRoot, i);
                    }
                }

                frameStates = null;

                StackResult result = new StackResult();

                result.parms = parms;
                result.t0 = t0;
                result.t1 = t1;
                result.cStitchedStacks = cStitchedStacks;
                result.treeRoot = treeRoot;
                result.atomsNodeNames = atomsNodeNames;
                result.rollupStats = rollupStats;
                result.idPivot = idPivot;
                result.stackFilters = filters;

                return result;
            }