protected override StackSourceCallStackIndex InternCallerStack(StackSourceFrameIndex frameIndex, StackSourceCallStackIndex stackIndex)
 {
     lock (internCallStackLock)
     {
         return(Interner.CallStackIntern(frameIndex, stackIndex));
     }
 }
Пример #2
0
        public void Read(TextReader reader)
        {
            var sample = new StackSourceSample(this);

            sample.Metric = 1;
            for (; ;)
            {
                var line = reader.ReadLine();
                if (line == null)
                {
                    break;
                }

                if (StackForLine != null)
                {
                    sample.StackIndex = StackForLine(Interner, line);
                }
                else
                {
                    // Form the stack for this entry (trivial one element stack)
                    var frameIndex = Interner.FrameIntern(line);
                    sample.StackIndex = Interner.CallStackIntern(frameIndex, StackSourceCallStackIndex.Invalid);
                }
                if (sample.StackIndex != StackSourceCallStackIndex.Invalid)
                {
                    AddSample(sample);
                }
            }
            Interner.DoneInterning();
        }
Пример #3
0
        public StackSourceCallStackIndex GetCallStackForThread(TraceThread thread)
        {
            var processStack        = GetCallStackForProcess(thread.Process);
            var threadName          = "Thread (" + thread.ThreadID + ")";
            var internedThreadFrame = Interner.FrameIntern(threadName, m_emptyModuleIdx);
            var threadStack         = Interner.CallStackIntern(internedThreadFrame, processStack);

            return(threadStack);
        }
Пример #4
0
        // Advanced usage
        public StackSourceCallStackIndex GetCallStackForProcess(TraceProcess process)
        {
            string ptrSize              = process.Is64Bit ? "64" : "32";
            var    processName          = "Process" + ptrSize + " " + process.Name + " (" + process.ProcessID + ")";
            var    internedProcessFrame = Interner.FrameIntern(processName, m_emptyModuleIdx);
            var    processStack         = Interner.CallStackIntern(internedProcessFrame, StackSourceCallStackIndex.Invalid);

            return(processStack);
        }
Пример #5
0
        /// <summary>
        /// Note that on windows, lastAccessTime does not really work (acts like lastWriteTime).  They turned it off for efficiency reasons.
        /// </summary>
        public FileSizeStackSource(string directoryPath, TextWriter log, bool useWriteTime = true)
        {
            m_useWriteTime = useWriteTime;
            m_nowUtc       = DateTime.UtcNow;
            m_log          = log;
            // Make the full path the root node.
            var stackBase = Interner.CallStackIntern(Interner.FrameIntern("DIR: " + Path.GetFullPath(directoryPath)), StackSourceCallStackIndex.Invalid);

            AddSamplesForDirectory(directoryPath, stackBase);
            Interner.DoneInterning();
        }
Пример #6
0
        /// <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);
        }
Пример #7
0
        public MyStackSource()
        {
            StackSourceModuleIndex emptyModuleIdx = Interner.ModuleIntern("");

            // Make up a stack source with 10 samples in it, all with the same stack.
            var mySample = new StackSourceSample(this);

            for (int i = 0; i < 10; i++)
            {
                mySample.TimeRelativeMSec = i;
                mySample.Metric           = 10 + i; // Just to make things interesting.
                mySample.StackIndex       = StackSourceCallStackIndex.Invalid;

                // Add a frame 'Frame 1'
                mySample.StackIndex = Interner.CallStackIntern(Interner.FrameIntern("Frame 1", emptyModuleIdx), mySample.StackIndex);

                // Add a frame 'Frame 2'
                mySample.StackIndex = Interner.CallStackIntern(Interner.FrameIntern("Frame 2", emptyModuleIdx), mySample.StackIndex);

                // This copies mySample, so you can keep reusing mySample for the next sample
                AddSample(mySample);
            }
        }
Пример #8
0
        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();
            }
        }
Пример #9
0
 public StackSourceCallStackIndex GetCallStack(StackSourceFrameIndex frameIndex, StackSourceCallStackIndex callerIndex)
 {
     return(Interner.CallStackIntern(frameIndex, callerIndex));
 }
Пример #10
0
        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 virtual StackSourceCallStackIndex InternCallerStack(StackSourceFrameIndex frameIndex, StackSourceCallStackIndex stackIndex)
 {
     return(Interner.CallStackIntern(frameIndex, stackIndex));
 }
Пример #12
0
        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);
            }
        }
Пример #13
0
        private void Read(Stream rawStream)
        {
            XmlReaderSettings settings = new XmlReaderSettings()
            {
                IgnoreWhitespace = true, IgnoreComments = true
            };
            XmlReader reader           = XmlTextReader.Create(rawStream, settings);
            var       stack            = new GrowableArray <StackSourceSample>();
            bool      metricsInclusive = false; // If true, we need to convert them to exclusive as part of processing

            while (reader.Read())
            {
                if (reader.NodeType == XmlNodeType.Element)
                {
                    if (reader.Name == "node")
                    {
                        var    sample   = new StackSourceSample(this);
                        string callTree = reader.GetAttribute("call_tree");

                        // Case for allocation stacks
                        string sizeStr = reader.GetAttribute("size");
                        if (sizeStr != null)
                        {
                            metricsInclusive = true;        // allocation numbers are inclusive
                            int size = 0;
                            int.TryParse(sizeStr, out size);
                            sample.Metric = size;

                            string recoredObectsStr = reader.GetAttribute("recorded_objects");
                            int    recoredObects    = 0;
                            if (recoredObectsStr != null)
                            {
                                int.TryParse(recoredObectsStr, out recoredObects);
                            }

                            sample.Count = recoredObects;
                        }
                        else
                        {
                            Debug.Assert(metricsInclusive == false);        // CPU time is exclusive.
                            // For CPU
                            string own_time_msStr = reader.GetAttribute("own_time_ms");
                            if (own_time_msStr != null)
                            {
                                int own_time_ms;
                                int.TryParse(own_time_msStr, out own_time_ms);
                                sample.Metric = own_time_ms;

                                string countStr = reader.GetAttribute("count");
                                int    count    = 0;
                                if (countStr != null)
                                {
                                    int.TryParse(countStr, out count);
                                }

                                sample.Count = count;
                            }
                        }

                        // Get the parent stack for this line
                        var parentStackIndex = StackSourceCallStackIndex.Invalid;
                        int depth            = stack.Count;
                        if (depth > 0)
                        {
                            StackSourceSample parent = stack[depth - 1];
                            parentStackIndex = parent.StackIndex;

                            if (metricsInclusive)
                            {
                                // The values are inclusive, but StackSoruceSamples are the exclusive amounts, so remove children.
                                parent.Count  -= sample.Count;
                                parent.Metric -= sample.Metric;
                            }
                        }
                        if (callTree != null)
                        {
                            var frameIndex = Interner.FrameIntern(callTree);
                            sample.StackIndex = Interner.CallStackIntern(frameIndex, parentStackIndex);
                        }
                        stack.Add(sample);
                    }
                }
                if (reader.NodeType == XmlNodeType.EndElement || reader.IsEmptyElement)
                {
                    if (reader.Name == "node")
                    {
                        StackSourceSample sample = stack.Pop();
                        if ((sample.Count > 0 || sample.Metric > 0) && sample.StackIndex != StackSourceCallStackIndex.Invalid)
                        {
                            AddSample(sample);
                        }
                    }
                }
            }

            Debug.Assert(stack.Count == 0);
            Interner.DoneInterning();
        }
        public ManagedExeSizeStackSource(string managedExePath)
        {
            // Make the full path the root node.
            var stackBase = Interner.CallStackIntern(Interner.FrameIntern("FILE: " + Path.GetFileName(managedExePath)), StackSourceCallStackIndex.Invalid);

            StackSourceSample sample = new StackSourceSample(this);;

            Assembly assembly = Assembly.ReflectionOnlyLoadFrom(managedExePath);

            AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += delegate(object sender, ResolveEventArgs args)
            {
                Trace.WriteLine("RequestingAssembly " + args.RequestingAssembly.FullName);
                Trace.WriteLine("Name Requested " + args.Name);
                Assembly ret = null;
                try
                {
                    if (args.Name.StartsWith("System") || args.Name.StartsWith("Microsoft"))
                    {
                        ret = Assembly.ReflectionOnlyLoad(args.Name);
                    }
                }
                catch (Exception)
                {
                }
                if (ret == null)
                {
                    Trace.WriteLine("Could not resolve assembly reference " + args.Name);
                }
                return(ret);
            };
            Type[] types = null;
            try
            {
                Trace.WriteLine("Calling GetTypes");
                types = assembly.GetTypes();
            }
            catch (ReflectionTypeLoadException e)
            {
                Trace.WriteLine("Got exception " + e);
                foreach (var loaderException in e.LoaderExceptions)
                {
                    Trace.WriteLine("Loader Exception " + loaderException);
                }
                types = e.Types;
            }
            Trace.WriteLine("Got " + types.Length + " types");

            foreach (Type type in types)
            {
                if (type == null)
                {
                    continue;
                }
                Trace.WriteLine("Looking at TYPE " + type.FullName);
                foreach (MethodInfo methodDef in type.GetMethods())
                {
                    Trace.WriteLine("Looking at METHOD " + methodDef.Name);
                    MethodBody methodBody = methodDef.GetMethodBody();
                    int        ilLen      = 0;
                    if (methodBody != null)
                    {
                        byte[] il = methodBody.GetILAsByteArray();
                        if (il != null)
                        {
                            ilLen = il.Length;
                        }
                    }
                    PerfView.App.CommandProcessor.LogFile.WriteLine(string.Format("Method {0} size {1}", methodDef.Name, ilLen));

                    sample.StackIndex = Interner.CallStackIntern(Interner.FrameIntern("METHOD " + methodDef.Name), stackBase);
                    sample.Metric     = ilLen;
                    AddSample(sample);
                }
            }
            Interner.DoneInterning();
            Trace.WriteLine("Done");
        }
Пример #15
0
        void Read(TextReader reader)
        {
            // TODO this is relatively inefficient.
            var regEx = new Regex(@"^\s*(\d+)\s*(\d+)\s*\[\s*(\d+)\s*\]\s*(\S*?)!?(.*)");

            var         stack  = new GrowableArray <WTStackElem>();
            WTStackElem elem   = new WTStackElem();
            long        time   = 0;
            var         sample = new StackSourceSample(this);

            for (; ;)
            {
                var line = reader.ReadLine();
                if (line == null)
                {
                    break;
                }
                var match = regEx.Match(line);
                if (match.Success)
                {
                    // Parse the line.
                    int    excInstrSoFar = int.Parse(match.Groups[1].Value);
                    int    depth         = int.Parse(match.Groups[3].Value);
                    string module        = match.Groups[4].Value;
                    string method        = match.Groups[5].Value;

                    // Form the name for this line
                    var moduleIndex = Interner.ModuleIntern(module);
                    var frameIndex  = Interner.FrameIntern(method, moduleIndex);

                    // Get the parent stack for this line
                    var parent = StackSourceCallStackIndex.Invalid;
                    if (depth > 0)
                    {
                        parent = stack[depth - 1].FirstCallStackIndex;    // TODO handle out of range
                    }
                    // Form the stack for this entry
                    var callStackIndex = Interner.CallStackIntern(frameIndex, parent);

                    int exclInstr;                              // Number of instructions executed on this line
                    int extra = stack.Count - depth;            // The number of frames we need to pop off (including me)
                    if (extra > 0)
                    {
                        // We returned from one or more methods OR we have not left the current method
                        //
                        elem = stack[depth];

                        // We expect to return to the same method we were at at this depth.
                        if (callStackIndex == elem.CallStackIndex)
                        {
                            exclInstr = excInstrSoFar - elem.ExclInstrSoFar;    // We are continuing the function
                        }
                        else
                        {
                            // We are tail-calling to another routine.
                            exclInstr           = excInstrSoFar;
                            elem.CallStackIndex = callStackIndex;
                        }

                        // Pop off all the frames we returned from
                        Debug.Assert(exclInstr >= 0);
                        stack.RemoveRange(depth, extra);
                    }
                    else
                    {
                        // Means we are adding a new frame (we called someone)
                        Debug.Assert(extra == 0);       // We always add only one more frame (e.g. we never go from depth 2 to 4)
                        elem.CallStackIndex      = callStackIndex;
                        elem.FirstCallStackIndex = callStackIndex;
                        exclInstr = excInstrSoFar;
                    }
                    elem.ExclInstrSoFar = excInstrSoFar;
                    stack.Add(elem);

                    time += exclInstr;

                    sample.Metric           = exclInstr;
                    sample.TimeRelativeMSec = time - exclInstr;
                    sample.StackIndex       = elem.FirstCallStackIndex;
                    AddSample(sample);
                }
            }
            Interner.DoneInterning();
        }
Пример #16
0
        void Read(TextReader reader)
        {
            var stack = new GrowableArray <StackSourceCallStackIndex>();


            var line   = reader.ReadLine(); // Skip the first line, which is column headers.
            var sample = new StackSourceSample(this);

            for (; ;)
            {
                line = reader.ReadLine();
                if (line == null)
                {
                    break;
                }
                //   0       1           2             3          4       5        6         7          8
                // Order, # of Calls, % Incl Time, % Excl Time, Depth, Function, Module, Incl Time, Excl Time,% Sw. Out, Incl Switched Out, Type, Comments	Min	Avg	Max	Excl Switched Out

                int    idx    = 0;
                int    depth  = 0;
                string method = null;
                string module = null;
                int    intVal;
                long   longVal;
                for (int col = 0; col <= 8; col++)
                {
                    var newIdx = line.IndexOf('\t', idx);
                    Debug.Assert(0 < newIdx);
                    if (newIdx < 0)
                    {
                        goto SKIP;
                    }

                    switch (col)
                    {
                    case 1:
                        int.TryParse(line.Substring(idx, newIdx - idx), System.Globalization.NumberStyles.Number, null, out intVal);
                        sample.Count = intVal;
                        break;

                    case 4:
                        int.TryParse(line.Substring(idx, newIdx - idx), System.Globalization.NumberStyles.Number, null, out depth);
                        break;

                    case 5:
                        while (idx < newIdx)
                        {
                            if (line[idx] != ' ')
                            {
                                break;
                            }
                            idx++;
                        }
                        method = line.Substring(idx, newIdx - idx);
                        method = method.Replace((char)0xFFFD, '@');          // They used this character to separate the method name from signature.
                        break;

                    case 6:
                        module = "";
                        if (depth != 0)
                        {
                            module = line.Substring(idx, newIdx - idx);
                        }
                        break;

                    case 8:
                        long.TryParse(line.Substring(idx, newIdx - idx), System.Globalization.NumberStyles.Number, null, out longVal);
                        sample.Metric = longVal / 1000000;     // TODO what is the metric?
                        break;
                    }
                    idx = newIdx + 1;
                }
                var moduleIdx = Interner.ModuleIntern(module);
                var frameIdx  = Interner.FrameIntern(method, moduleIdx);
                var prevFrame = StackSourceCallStackIndex.Invalid;
                if (0 < depth && depth <= stack.Count)
                {
                    prevFrame = stack[depth - 1];
                }
                var callStackIdx = Interner.CallStackIntern(frameIdx, prevFrame);

                if (depth < stack.Count)
                {
                    stack.Count = depth;
                }
                stack.Add(callStackIdx);

                sample.StackIndex = callStackIdx;
                AddSample(sample);
                SKIP :;
            }
            Interner.DoneInterning();
        }