Пример #1
0
    internal void SetupCallbacks(MemoryGraph memoryGraph, TraceEventDispatcher source, int processID = 0, double startTimeRelativeMSec = 0)
    {
        m_graph           = memoryGraph;
        m_types           = new Dictionary <string, NodeTypeIndex>(10000);
        m_nodeBlocks      = new Queue <BulkNodeTraceData>();
        m_attributeBlocks = new Queue <BulkAttributeTraceData>();
        m_edgeBlocks      = new Queue <BulkEdgeTraceData>();
        m_ignoreEvents    = true;
        m_ignoreUntilMSec = startTimeRelativeMSec;
        m_processId       = processID;

        var jsDump = new JSDumpHeapTraceEventParser(source);

        jsDump.JSDumpHeapEnvelopeStart += delegate(SettingsTraceData data)
        {
            if (data.TimeStampRelativeMSec < m_ignoreUntilMSec)
            {
                return;
            }
            if (m_processId == 0)
            {
                m_processId = data.ProcessID;
            }
            if (data.ProcessID != m_processId)
            {
                return;
            }

            if (!m_seenStart)
            {
                m_ignoreEvents = false;
            }
            m_seenStart = true;
        };
        jsDump.JSDumpHeapEnvelopeStop += delegate(SummaryTraceData data)
        {
            if (m_ignoreEvents || data.ProcessID != m_processId)
            {
                return;
            }
            m_ignoreEvents = true;
            source.StopProcessing();
        };
        jsDump.JSDumpHeapBulkNode += delegate(BulkNodeTraceData data)
        {
            if (m_ignoreEvents || data.ProcessID != m_processId)
            {
                return;
            }
            m_nodeBlocks.Enqueue((BulkNodeTraceData)data.Clone());
        };
        jsDump.JSDumpHeapBulkAttribute += delegate(BulkAttributeTraceData data)
        {
            if (m_ignoreEvents || data.ProcessID != m_processId)
            {
                return;
            }
            m_attributeBlocks.Enqueue((BulkAttributeTraceData)data.Clone());
        };
        jsDump.JSDumpHeapBulkEdge += delegate(BulkEdgeTraceData data)
        {
            if (m_ignoreEvents || data.ProcessID != m_processId)
            {
                return;
            }
            m_edgeBlocks.Enqueue((BulkEdgeTraceData)data.Clone());
        };
        jsDump.JSDumpHeapStringTable += delegate(StringTableTraceData data)
        {
            if (m_ignoreEvents || data.ProcessID != m_processId)
            {
                return;
            }
            for (int i = 0; i < data.Count; i++)
            {
                m_stringTable.Add(data.Strings(i));
            }
        };
        jsDump.JSDumpHeapDoubleTable += delegate(DoubleTableTraceData data)
        {
            if (m_ignoreEvents || data.ProcessID != m_processId)
            {
                return;
            }
            for (int i = 0; i < data.Count; i++)
            {
                m_doubleTable.Add(data.Doubles(i));
            }
        };
    }
Пример #2
0
    /// <summary>
    /// Add the nodes of the JS heap for the process 'processID' to 'memoryGraph' sending diagnostic
    /// messages to 'log'.  If 'memoryGraph' is null, the ETW providers are triggered by we obviously
    /// don't update the memoryGraph.  Thus it is only done for the side effect of triggering a JS heap
    /// dump. returns true if successful.
    /// </summary>
    static public bool Dump(int processID, MemoryGraph memoryGraph, TextWriter log)
    {
        var ver    = Environment.OSVersion.Version;
        var intVer = ver.Major * 10 + ver.Minor;

        if (intVer < 62)
        {
            log.WriteLine("JavaScript Heap Dumping only supported on Win8 or above.");
            return(false);
        }

        var  sw                   = Stopwatch.StartNew();
        var  dumper               = new JavaScriptDumpGraphReader(log);
        bool dumpComplete         = false;
        bool listening            = false;
        int  edgeRecords          = 0;
        TraceEventSession session = null;
        Task readerTask           = null;

        try
        {
            bool     jsDataPresent = false;
            TimeSpan lastJSUpdate  = sw.Elapsed;
            // Set up a separate thread that will listen for ETW events coming back telling us we succeeded.
            readerTask = Task.Factory.StartNew(delegate
            {
                string sessionName = "PerfViewJSHeapSession";
                session            = new TraceEventSession(sessionName, null);
                // Set up the JScript heap listener
                var etwJSParser = new JSDumpHeapTraceEventParser(session.Source);
                etwJSParser.JSDumpHeapEnvelopeStop += delegate(SummaryTraceData data)
                {
                    if (data.ProcessID == processID)
                    {
                        log.WriteLine("{0,5:n1}s: JavaScript GC Complete.", sw.Elapsed.TotalSeconds);
                        dumpComplete        = true;
                        memoryGraph.Is64Bit = (data.PointerSize == 8);
                    }
                };

                etwJSParser.JSDumpHeapEnvelopeStart += delegate(SettingsTraceData data)
                {
                    log.WriteLine("{0,5:n1}s: JS Heap Dump Started...", sw.Elapsed.TotalSeconds);
                    jsDataPresent = true;
                };

                etwJSParser.JSDumpHeapBulkEdge += delegate(BulkEdgeTraceData data)
                {
                    if (data.ProcessID == processID)
                    {
                        edgeRecords++;
                        if ((sw.Elapsed - lastJSUpdate).TotalMilliseconds > 500)
                        {
                            log.WriteLine("{0,5:n1}s: Making JS GC Heap Progress...", sw.Elapsed.TotalSeconds);
                        }
                        lastJSUpdate = sw.Elapsed;
                    }
                };

                log.WriteLine("{0,5:n1}s: Enabling JScript Heap Provider", sw.Elapsed.TotalSeconds);
                session.EnableProvider(JSDumpHeapTraceEventParser.ProviderGuid, TraceEventLevel.Informational,
                                       (ulong)JSDumpHeapTraceEventParser.Keywords.jsdumpheap);

                listening = true;
                if (memoryGraph != null)
                {
                    dumper.SetupCallbacks(memoryGraph, session.Source, processID);
                }
                session.Source.Process();
                log.WriteLine("{0,5:n1}s: ETW Listener dieing", sw.Elapsed.TotalSeconds);
            });

            // Wait for thread above to start listening (should be very fast)
            while (!listening)
            {
                readerTask.Wait(1);
            }
            Debug.Assert(session != null);

            // Start the providers and trigger the GCs.
            log.WriteLine("{0,5:n1}s: Requesting a JScript Heap Dump", sw.Elapsed.TotalSeconds);

            // WinBlue (V6.3) does not support the passing of the process ID we used on Win8.
            // Thus we have to drop that so it works everywhere.
            // !TODO captures state for all processes!   This is not so bad because the others are probably
            // suspended and thus do not get dumped.   You could fix this on  WinBlue by using the new ETW
            // process filtering.
            session.CaptureState(JSDumpHeapTraceEventParser.ProviderGuid, (ulong)JSDumpHeapTraceEventParser.Keywords.jsdumpheap);

            for (; ;)
            {
                if (readerTask.Wait(100))
                {
                    break;
                }

                if (!jsDataPresent && sw.Elapsed.TotalSeconds > 5)
                {
                    log.WriteLine("{0,5:n1}s: Assume no JSHeap", sw.Elapsed.TotalSeconds);
                    break;
                }

                if (sw.Elapsed.TotalSeconds > 60)
                {
                    log.WriteLine("{0,5:n1}s: Timed out after 60 seconds", sw.Elapsed.TotalSeconds);
                    break;
                }
                // TODO FIX NOW, time out faster if we seek to be stuck
                if (dumpComplete)
                {
                    break;
                }
            }
            if (jsDataPresent)
            {
                dumper.ConvertHeapDataToGraph();        // Finish the conversion.
            }
        }
        finally
        {
            // Stop the ETW providers
            log.WriteLine("{0,5:n1}s: Shutting down ETW session", sw.Elapsed.TotalSeconds);
            if (session != null)
            {
                session.Dispose();
            }
        }
        if (readerTask != null)
        {
            log.WriteLine("{0,5:n1}s: Waiting for shutdown to complete.", sw.Elapsed.TotalSeconds);
            if (!readerTask.Wait(2000))
            {
                log.WriteLine("{0,5:n1}s: Shutdown wait timed out after 2 seconds.", sw.Elapsed.TotalSeconds);
            }
        }
        log.WriteLine("Collected {0} JSHeap Bulk Edge Events.", edgeRecords);
        log.WriteLine("[{0,5:n1}s: Done Dumping JScript heap success={1}]", sw.Elapsed.TotalSeconds, dumpComplete);

        return(dumpComplete);
    }