Web Inspector: partially instrument DOM Tree native memory.
[WebKit-https.git] / Source / WebCore / inspector / InspectorMemoryAgent.cpp
index 0cad49a..e396542 100644 (file)
 #include "InspectorState.h"
 #include "InspectorValues.h"
 #include "InstrumentingAgents.h"
+#include "MemoryCache.h"
+#include "MemoryInstrumentation.h"
+#include "MemoryUsageSupport.h"
 #include "Node.h"
 #include "Page.h"
+#include "ScriptGCEvent.h"
 #include "ScriptProfiler.h"
 #include "StyledElement.h"
 #include <wtf/HashSet.h>
@@ -55,8 +59,32 @@ using WebCore::TypeBuilder::Memory::ListenerCount;
 using WebCore::TypeBuilder::Memory::NodeCount;
 using WebCore::TypeBuilder::Memory::StringStatistics;
 
+// Use a type alias instead of 'using' here which would cause a conflict on Mac.
+typedef WebCore::TypeBuilder::Memory::MemoryBlock InspectorMemoryBlock;
+
 namespace WebCore {
 
+namespace MemoryBlockName {
+static const char jsHeapAllocated[] = "JSHeapAllocated";
+static const char jsHeapUsed[] = "JSHeapUsed";
+static const char inspectorData[] = "InspectorData";
+static const char memoryCache[] = "MemoryCache";
+static const char processPrivateMemory[] = "ProcessPrivateMemory";
+
+static const char cachedImages[] = "CachedImages";
+static const char cachedCssStyleSheets[] = "CachedCssStyleSheets";
+static const char cachedScripts[] = "CachedScripts";
+static const char cachedXslStyleSheets[] = "CachedXslStyleSheets";
+static const char cachedFonts[] = "CachedFonts";
+static const char renderTreeUsed[] = "RenderTreeUsed";
+static const char renderTreeAllocated[] = "RenderTreeAllocated";
+
+static const char dom[] = "DOM";
+static const char domTreeOther[] = "DOMTreeOther";
+static const char domTreeDOM[] = "DOMTreeDOM";
+static const char domTreeCSS[] = "DOMTreeCSS";
+}
+
 namespace {
 
 String nodeName(Node* node)
@@ -155,10 +183,6 @@ private:
         collectListenersInfo(node);
     }
 
-    void collectCharacterData(Node*)
-    {
-    }
-
     void collectNodeNameInfo(Node* node)
     {
         String name = nodeName(node);
@@ -307,10 +331,177 @@ void InspectorMemoryAgent::getDOMNodeCount(ErrorString*, RefPtr<TypeBuilder::Arr
     strings = counterVisitor.strings();
 }
 
-InspectorMemoryAgent::InspectorMemoryAgent(InstrumentingAgents* instrumentingAgents, InspectorState* state, Page* page, InspectorDOMAgent* domAgent)
+static PassRefPtr<InspectorMemoryBlock> jsHeapInfo()
+{
+    size_t usedJSHeapSize;
+    size_t totalJSHeapSize;
+    size_t jsHeapSizeLimit;
+    ScriptGCEvent::getHeapSize(usedJSHeapSize, totalJSHeapSize, jsHeapSizeLimit);
+
+    RefPtr<InspectorMemoryBlock> jsHeapAllocated = InspectorMemoryBlock::create().setName(MemoryBlockName::jsHeapAllocated);
+    jsHeapAllocated->setSize(totalJSHeapSize);
+
+    RefPtr<TypeBuilder::Array<InspectorMemoryBlock> > children = TypeBuilder::Array<InspectorMemoryBlock>::create();
+    RefPtr<InspectorMemoryBlock> jsHeapUsed = InspectorMemoryBlock::create().setName(MemoryBlockName::jsHeapUsed);
+    jsHeapUsed->setSize(usedJSHeapSize);
+    children->addItem(jsHeapUsed);
+
+    jsHeapAllocated->setChildren(children);
+    return jsHeapAllocated.release();
+}
+
+static PassRefPtr<InspectorMemoryBlock> inspectorData()
+{
+    size_t dataSize = ScriptProfiler::profilerSnapshotsSize();
+    RefPtr<InspectorMemoryBlock> inspectorData = InspectorMemoryBlock::create().setName(MemoryBlockName::inspectorData);
+    inspectorData->setSize(dataSize);
+    return inspectorData.release();
+}
+
+static PassRefPtr<InspectorMemoryBlock> renderTreeInfo(Page* page)
+{
+    ArenaSize arenaSize = page->renderTreeSize();
+
+    RefPtr<InspectorMemoryBlock> renderTreeAllocated = InspectorMemoryBlock::create().setName(MemoryBlockName::renderTreeAllocated);
+    renderTreeAllocated->setSize(arenaSize.allocated);
+
+    RefPtr<TypeBuilder::Array<InspectorMemoryBlock> > children = TypeBuilder::Array<InspectorMemoryBlock>::create();
+    RefPtr<InspectorMemoryBlock> renderTreeUsed = InspectorMemoryBlock::create().setName(MemoryBlockName::renderTreeUsed);
+    renderTreeUsed->setSize(arenaSize.treeSize);
+    children->addItem(renderTreeUsed);
+
+    renderTreeAllocated->setChildren(children);
+    return renderTreeAllocated.release();
+}
+
+static void addMemoryBlockFor(TypeBuilder::Array<InspectorMemoryBlock>* array, size_t size, const char* name)
+{
+    RefPtr<InspectorMemoryBlock> result = InspectorMemoryBlock::create().setName(name);
+    result->setSize(size);
+    array->addItem(result);
+}
+
+namespace {
+
+class MemoryInstrumentationImpl : public MemoryInstrumentation {
+public:
+    MemoryInstrumentationImpl()
+    {
+        for (int i = 0; i < LastTypeEntry; ++i)
+            m_totalSizes[i] = 0;
+    }
+
+    PassRefPtr<InspectorMemoryBlock> dumpStatistics()
+    {
+        size_t totalSize = 0;
+        for (int i = Other; i < LastTypeEntry; ++i)
+            totalSize += m_totalSizes[i];
+
+        RefPtr<TypeBuilder::Array<InspectorMemoryBlock> > domChildren = TypeBuilder::Array<InspectorMemoryBlock>::create();
+        addMemoryBlockFor(domChildren.get(), m_totalSizes[Other], MemoryBlockName::domTreeOther);
+        addMemoryBlockFor(domChildren.get(), m_totalSizes[DOM], MemoryBlockName::domTreeDOM);
+        addMemoryBlockFor(domChildren.get(), m_totalSizes[CSS], MemoryBlockName::domTreeCSS);
+
+        RefPtr<InspectorMemoryBlock> dom = InspectorMemoryBlock::create().setName(MemoryBlockName::dom);
+        dom->setSize(totalSize);
+        dom->setChildren(domChildren.release());
+        return dom.release();
+    }
+
+private:
+    virtual void countObjectSize(ObjectType objectType, size_t size)
+    {
+        ASSERT(objectType >= 0 && objectType < LastTypeEntry);
+        m_totalSizes[objectType] += size;
+    }
+
+    virtual bool visited(const void* object)
+    {
+        return !m_visitedObjects.add(object).isNewEntry;
+    }
+    size_t m_totalSizes[LastTypeEntry];
+    typedef HashSet<const void*> VisitedObjects;
+    VisitedObjects m_visitedObjects;
+};
+
+class DOMTreesIterator : public DOMWrapperVisitor {
+public:
+    explicit DOMTreesIterator(Page* page) : m_page(page) { }
+
+    virtual void visitNode(Node* node)
+    {
+        if (node->document() && node->document()->frame() && m_page != node->document()->frame()->page())
+            return;
+
+        m_domMemoryUsage.reportInstrumentedPointer(node);
+    }
+
+    virtual void visitJSExternalString(StringImpl*) { }
+
+    PassRefPtr<InspectorMemoryBlock> dumpStatistics() { return m_domMemoryUsage.dumpStatistics(); }
+
+private:
+    Page* m_page;
+    MemoryInstrumentationImpl m_domMemoryUsage;
+};
+
+}
+
+static PassRefPtr<InspectorMemoryBlock> domTreeInfo(Page* page)
+{
+    DOMTreesIterator domTreesIterator(page);
+    ScriptProfiler::visitJSDOMWrappers(&domTreesIterator);
+
+    // Make sure all documents reachable from the main frame are accounted.
+    for (Frame* frame = page->mainFrame(); frame; frame = frame->tree()->traverseNext()) {
+        if (Document* doc = frame->document())
+            domTreesIterator.visitNode(doc);
+    }
+
+    return domTreesIterator.dumpStatistics();
+}
+
+static PassRefPtr<InspectorMemoryBlock> memoryCacheInfo()
+{
+    MemoryCache::Statistics stats = memoryCache()->getStatistics();
+    int totalSize = stats.images.size +
+                    stats.cssStyleSheets.size +
+                    stats.scripts.size +
+                    stats.xslStyleSheets.size +
+                    stats.fonts.size;
+    RefPtr<InspectorMemoryBlock> memoryCacheStats = InspectorMemoryBlock::create().setName(MemoryBlockName::memoryCache);
+    memoryCacheStats->setSize(totalSize);
+
+    RefPtr<TypeBuilder::Array<InspectorMemoryBlock> > children = TypeBuilder::Array<InspectorMemoryBlock>::create();
+    addMemoryBlockFor(children.get(), stats.images.size, MemoryBlockName::cachedImages);
+    addMemoryBlockFor(children.get(), stats.cssStyleSheets.size, MemoryBlockName::cachedCssStyleSheets);
+    addMemoryBlockFor(children.get(), stats.scripts.size, MemoryBlockName::cachedScripts);
+    addMemoryBlockFor(children.get(), stats.xslStyleSheets.size, MemoryBlockName::cachedXslStyleSheets);
+    addMemoryBlockFor(children.get(), stats.fonts.size, MemoryBlockName::cachedFonts);
+    memoryCacheStats->setChildren(children.get());
+    return memoryCacheStats.release();
+}
+
+void InspectorMemoryAgent::getProcessMemoryDistribution(ErrorString*, RefPtr<InspectorMemoryBlock>& processMemory)
+{
+    size_t privateBytes = 0;
+    size_t sharedBytes = 0;
+    MemoryUsageSupport::processMemorySizesInBytes(&privateBytes, &sharedBytes);
+    processMemory = InspectorMemoryBlock::create().setName(MemoryBlockName::processPrivateMemory);
+    processMemory->setSize(privateBytes);
+
+    RefPtr<TypeBuilder::Array<InspectorMemoryBlock> > children = TypeBuilder::Array<InspectorMemoryBlock>::create();
+    children->addItem(jsHeapInfo());
+    children->addItem(inspectorData());
+    children->addItem(memoryCacheInfo());
+    children->addItem(renderTreeInfo(m_page)); // TODO: collect for all pages?
+    children->addItem(domTreeInfo(m_page)); // TODO: collect for all pages?
+    processMemory->setChildren(children);
+}
+
+InspectorMemoryAgent::InspectorMemoryAgent(InstrumentingAgents* instrumentingAgents, InspectorState* state, Page* page, InspectorDOMAgent*)
     : InspectorBaseAgent<InspectorMemoryAgent>("Memory", instrumentingAgents, state)
     , m_page(page)
-    , m_domAgent(domAgent)
 {
 }