Web Inspector: provide basic information about DOM character data size
authoryurys@chromium.org <yurys@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 17 Jan 2012 15:37:06 +0000 (15:37 +0000)
committeryurys@chromium.org <yurys@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 17 Jan 2012 15:37:06 +0000 (15:37 +0000)
https://bugs.webkit.org/show_bug.cgi?id=76059

Memory agent allows to estimate size of DOM character data and size of WebCore
strings held by JavaScript objects.

Reviewed by Pavel Feldman.

* bindings/js/ScriptProfiler.h:
(WebCore::ScriptProfiler::visitExternalJSStrings):
* bindings/v8/ScriptProfiler.cpp:
(WebCore::ScriptProfiler::visitExternalJSStrings):
* bindings/v8/ScriptProfiler.h:
* bindings/v8/V8Binding.cpp:
(WebCore::WebCoreStringResource::visitStrings):
(WebCore::V8BindingPerIsolateData::visitJSExternalStrings):
* bindings/v8/V8Binding.h:
* inspector/DOMWrapperVisitor.h:
* inspector/Inspector.json:
* inspector/InspectorMemoryAgent.cpp:
(WebCore::CharacterDataStatistics::DOMTreeStatistics::DOMTreeStatistics):
(WebCore::CharacterDataStatistics::DOMTreeStatistics::collectNodeStatistics):
(WebCore::CharacterDataStatistics::CounterVisitor::CounterVisitor):
(WebCore::CharacterDataStatistics::CounterVisitor::domGroups):
(WebCore::CharacterDataStatistics::CounterVisitor::strings):
(WebCore::CharacterDataStatistics::CounterVisitor::visitNode):
(WebCore::CharacterDataStatistics::CounterVisitor::visitJSExternalString):
(WebCore::InspectorMemoryAgent::getDOMNodeCount):
* inspector/InspectorMemoryAgent.h:

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@105157 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebCore/ChangeLog
Source/WebCore/bindings/js/ScriptProfiler.h
Source/WebCore/bindings/v8/ScriptProfiler.cpp
Source/WebCore/bindings/v8/ScriptProfiler.h
Source/WebCore/bindings/v8/V8Binding.cpp
Source/WebCore/bindings/v8/V8Binding.h
Source/WebCore/inspector/DOMWrapperVisitor.h
Source/WebCore/inspector/Inspector.json
Source/WebCore/inspector/InspectorMemoryAgent.cpp
Source/WebCore/inspector/InspectorMemoryAgent.h

index f10af43..1b9ac25 100644 (file)
@@ -1,3 +1,35 @@
+2012-01-11  Yury Semikhatsky  <yurys@chromium.org>
+
+        Web Inspector: provide basic information about DOM character data size
+        https://bugs.webkit.org/show_bug.cgi?id=76059
+
+        Memory agent allows to estimate size of DOM character data and size of WebCore
+        strings held by JavaScript objects.
+
+        Reviewed by Pavel Feldman.
+
+        * bindings/js/ScriptProfiler.h:
+        (WebCore::ScriptProfiler::visitExternalJSStrings):
+        * bindings/v8/ScriptProfiler.cpp:
+        (WebCore::ScriptProfiler::visitExternalJSStrings):
+        * bindings/v8/ScriptProfiler.h:
+        * bindings/v8/V8Binding.cpp:
+        (WebCore::WebCoreStringResource::visitStrings):
+        (WebCore::V8BindingPerIsolateData::visitJSExternalStrings):
+        * bindings/v8/V8Binding.h:
+        * inspector/DOMWrapperVisitor.h:
+        * inspector/Inspector.json:
+        * inspector/InspectorMemoryAgent.cpp:
+        (WebCore::CharacterDataStatistics::DOMTreeStatistics::DOMTreeStatistics):
+        (WebCore::CharacterDataStatistics::DOMTreeStatistics::collectNodeStatistics):
+        (WebCore::CharacterDataStatistics::CounterVisitor::CounterVisitor):
+        (WebCore::CharacterDataStatistics::CounterVisitor::domGroups):
+        (WebCore::CharacterDataStatistics::CounterVisitor::strings):
+        (WebCore::CharacterDataStatistics::CounterVisitor::visitNode):
+        (WebCore::CharacterDataStatistics::CounterVisitor::visitJSExternalString):
+        (WebCore::InspectorMemoryAgent::getDOMNodeCount):
+        * inspector/InspectorMemoryAgent.h:
+
 2012-01-17  Vsevolod Vlasov  <vsevik@chromium.org>
 
         Web Inspector: Dialogs style and DialogDelegate interface fixes.
index 133b0e2..2211971 100644 (file)
@@ -62,6 +62,7 @@ public:
     static bool hasHeapProfiler() { return false; }
     // FIXME: Implement this counter for JSC. See bug 73936 for more details.
     static void visitJSDOMWrappers(DOMWrapperVisitor*) { }
+    static void visitExternalJSStrings(DOMWrapperVisitor*) { }
 };
 
 } // namespace WebCore
index 6d5de39..4007a8f 100644 (file)
@@ -159,4 +159,9 @@ void ScriptProfiler::visitJSDOMWrappers(DOMWrapperVisitor* visitor)
     visitDOMNodes(&adapter);
 }
 
+void ScriptProfiler::visitExternalJSStrings(DOMWrapperVisitor* visitor)
+{
+    V8BindingPerIsolateData::current()->visitJSExternalStrings(visitor);
+}
+
 } // namespace WebCore
index e987f69..9f02f4b 100644 (file)
@@ -66,6 +66,7 @@ public:
     static bool hasHeapProfiler() { return true; }
     static void initialize();
     static void visitJSDOMWrappers(DOMWrapperVisitor*);
+    static void visitExternalJSStrings(DOMWrapperVisitor*);
 };
 
 } // namespace WebCore
index a36dd62..4fd672f 100644 (file)
@@ -32,6 +32,7 @@
 #include "V8Binding.h"
 
 #include "DOMStringList.h"
+#include "DOMWrapperVisitor.h"
 #include "Element.h"
 #include "MathExtras.h"
 #include "PlatformString.h"
@@ -145,6 +146,13 @@ public:
         return m_atomicString;
     }
 
+    void visitStrings(DOMWrapperVisitor* visitor)
+    {
+        visitor->visitJSExternalString(m_plainString.impl());
+        if (m_plainString.impl() != m_atomicString.impl() && !m_atomicString.isNull())
+            visitor->visitJSExternalString(m_atomicString.impl());
+    }
+
     static WebCoreStringResource* toStringResource(v8::Handle<v8::String> v8String)
     {
         return static_cast<WebCoreStringResource*>(v8String->GetExternalStringResource());
@@ -165,6 +173,25 @@ private:
 #endif
 };
 
+void V8BindingPerIsolateData::visitJSExternalStrings(DOMWrapperVisitor* visitor)
+{
+    v8::HandleScope handleScope;
+    class VisitorImpl : public v8::ExternalResourceVisitor {
+    public:
+        VisitorImpl(DOMWrapperVisitor* visitor) : m_visitor(visitor) { }
+        virtual ~VisitorImpl() { }
+        virtual void VisitExternalString(v8::Handle<v8::String> string)
+        {
+            WebCoreStringResource* resource = static_cast<WebCoreStringResource*>(string->GetExternalStringResource());
+            if (resource)
+                resource->visitStrings(m_visitor);
+        }
+    private:
+        DOMWrapperVisitor* m_visitor;
+    } v8Visitor(visitor);
+    v8::V8::VisitExternalResources(&v8Visitor);
+}
+
 String v8ValueToWebCoreString(v8::Handle<v8::Value> value)
 {
     if (value->IsString())
@@ -502,7 +529,6 @@ void StringCache::remove(StringImpl* stringImpl)
     m_stringCache.remove(stringImpl);
 }
 
-
 v8::Local<v8::String> StringCache::v8ExternalStringSlow(StringImpl* stringImpl)
 {
     if (!stringImpl->length())
index 72529e0..528b4fb 100644 (file)
@@ -46,6 +46,7 @@
 namespace WebCore {
 
     class DOMStringList;
+    class DOMWrapperVisitor;
     class EventListener;
     class EventTarget;
 
@@ -123,6 +124,7 @@ namespace WebCore {
         }
 
         StringCache* stringCache() { return &m_stringCache; }
+        void visitJSExternalStrings(DOMWrapperVisitor*);
 
         DOMDataList& allStores() { return m_domDataList; }
 
index b822928..4bf246f 100644 (file)
@@ -36,6 +36,7 @@ class Node;
 class DOMWrapperVisitor {
 public:
     virtual void visitNode(Node*) = 0;
+    virtual void visitJSExternalString(StringImpl*) = 0;
 protected:
     virtual ~DOMWrapperVisitor() { }
 };
index 6b04732..1b41546 100644 (file)
                 "description": "Number of JS event listeners by event type."
             },
             {
+                "id": "StringStatistics",
+                "type": "object",
+                "properties": [
+                    { "name": "dom", "type": "integer" },
+                    { "name": "js", "type": "integer" },
+                    { "name": "shared", "type": "integer" }
+                ],
+                "description": "Character data statistics for the page."
+            },
+            {
                 "id": "DOMGroup",
                 "type": "object",
                 "properties": [
@@ -83,7 +93,8 @@
             {
                 "name": "getDOMNodeCount",
                 "returns": [
-                    { "name": "count", "type": "array", "items": { "$ref": "DOMGroup" }}
+                    { "name": "domGroups", "type": "array", "items": { "$ref": "DOMGroup" }},
+                    { "name": "strings", "$ref": "StringStatistics" }
                 ]
             }
         ]
index 58e50b6..7242d65 100644 (file)
@@ -33,6 +33,7 @@
 
 #if ENABLE(INSPECTOR)
 
+#include "CharacterData.h"
 #include "DOMWrapperVisitor.h"
 #include "Document.h"
 #include "EventListenerMap.h"
@@ -51,6 +52,7 @@
 using WebCore::TypeBuilder::Memory::DOMGroup;
 using WebCore::TypeBuilder::Memory::ListenerCount;
 using WebCore::TypeBuilder::Memory::NodeCount;
+using WebCore::TypeBuilder::Memory::StringStatistics;
 
 namespace WebCore {
 
@@ -63,11 +65,50 @@ String nodeName(Node* node)
     return node->nodeName().lower();
 }
 
+int stringSize(StringImpl* string)
+{
+    int size = string->length();
+    if (!string->is8Bit())
+        size *= 2;
+    return size + sizeof(*string);
+}
+
 typedef HashSet<StringImpl*, PtrHash<StringImpl*> > StringImplIdentitySet;
 
+class CharacterDataStatistics {
+    WTF_MAKE_NONCOPYABLE(CharacterDataStatistics);
+public:
+    CharacterDataStatistics() : m_characterDataSize(0) { }
+
+    void collectCharacterData(Node* node)
+    {
+        if (!node->isCharacterDataNode())
+            return;
+
+        CharacterData* characterData = static_cast<CharacterData*>(node);
+        StringImpl* dataImpl = characterData->dataImpl();
+        if (m_domStringImplSet.contains(dataImpl))
+            return;
+        m_domStringImplSet.add(dataImpl);
+
+        m_characterDataSize += stringSize(dataImpl);
+    }
+
+    bool contains(StringImpl* s) { return m_domStringImplSet.contains(s); }
+
+    int characterDataSize() { return m_characterDataSize; }
+
+private:
+    StringImplIdentitySet m_domStringImplSet;
+    int m_characterDataSize;
+};
+
 class DOMTreeStatistics {
+    WTF_MAKE_NONCOPYABLE(DOMTreeStatistics);
 public:
-    DOMTreeStatistics(Node* rootNode) : m_totalNodeCount(0)
+    DOMTreeStatistics(Node* rootNode, CharacterDataStatistics& characterDataStatistics)
+        : m_totalNodeCount(0)
+        , m_characterDataStatistics(characterDataStatistics)
     {
         collectTreeStatistics(rootNode);
     }
@@ -108,7 +149,7 @@ private:
     }
     void collectNodeStatistics(Node* node)
     {
-        collectCharacterData(node);
+        m_characterDataStatistics.collectCharacterData(node);
         collectNodeNameInfo(node);
         collectListenersInfo(node);
     }
@@ -149,14 +190,27 @@ private:
     int m_totalNodeCount;
     HashMap<AtomicString, int> m_eventTypeToCount;
     HashMap<String, int> m_nodeNameToCount;
-    StringImplIdentitySet m_domStringImplSet;
+    CharacterDataStatistics& m_characterDataStatistics;
 };
 
 class CounterVisitor : public DOMWrapperVisitor {
 public:
-    CounterVisitor(Page* page) : m_page(page), m_counters(InspectorArray::create()) { }
+    CounterVisitor(Page* page)
+        : m_page(page)
+        , m_domGroups(InspectorArray::create())
+        , m_jsExternalStringSize(0)
+        , m_sharedStringSize(0) { }
 
-    InspectorArray* counters() { return m_counters.get(); }
+    InspectorArray* domGroups() { return m_domGroups.get(); }
+
+    PassRefPtr<InspectorObject> strings()
+    {
+        RefPtr<StringStatistics> stringStatistics = StringStatistics::create()
+            .setDom(m_characterDataStatistics.characterDataSize())
+            .setJs(m_jsExternalStringSize)
+            .setShared(m_sharedStringSize);
+        return stringStatistics.release();
+    }
 
     virtual void visitNode(Node* node)
     {
@@ -171,7 +225,7 @@ public:
             return;
         m_roots.add(rootNode);
 
-        DOMTreeStatistics domTreeStats(rootNode);
+        DOMTreeStatistics domTreeStats(rootNode, m_characterDataStatistics);
 
         RefPtr<DOMGroup> domGroup = DOMGroup::create()
             .setSize(domTreeStats.totalNodeCount())
@@ -181,7 +235,15 @@ public:
         if (rootNode->nodeType() == Node::DOCUMENT_NODE)
             domGroup->setDocumentURI(static_cast<Document*>(rootNode)->documentURI());
 
-        m_counters->pushObject(domGroup);
+        m_domGroups->pushObject(domGroup);
+    }
+
+    virtual void visitJSExternalString(StringImpl* string)
+    {
+        int size = stringSize(string);
+        m_jsExternalStringSize += size;
+        if (m_characterDataStatistics.contains(string))
+            m_sharedStringSize += size;
     }
 
 private:
@@ -215,7 +277,10 @@ private:
 
     HashSet<Node*> m_roots;
     Page* m_page;
-    RefPtr<InspectorArray> m_counters;
+    RefPtr<InspectorArray> m_domGroups;
+    CharacterDataStatistics m_characterDataStatistics;
+    int m_jsExternalStringSize;
+    int m_sharedStringSize;
 };
 
 } // namespace
@@ -224,7 +289,7 @@ InspectorMemoryAgent::~InspectorMemoryAgent()
 {
 }
 
-void InspectorMemoryAgent::getDOMNodeCount(ErrorString*, RefPtr<InspectorArray>& result)
+void InspectorMemoryAgent::getDOMNodeCount(ErrorString*, RefPtr<InspectorArray>& domGroups, RefPtr<InspectorObject>& strings)
 {
     CounterVisitor counterVisitor(m_page);
     ScriptProfiler::visitJSDOMWrappers(&counterVisitor);
@@ -235,7 +300,10 @@ void InspectorMemoryAgent::getDOMNodeCount(ErrorString*, RefPtr<InspectorArray>&
             counterVisitor.visitNode(doc);
     }
 
-    result = counterVisitor.counters();
+    ScriptProfiler::visitExternalJSStrings(&counterVisitor);
+
+    domGroups = counterVisitor.domGroups();
+    strings = counterVisitor.strings();
 }
 
 InspectorMemoryAgent::InspectorMemoryAgent(InstrumentingAgents* instrumentingAgents, InspectorState* state, Page* page, InspectorDOMAgent* domAgent)
index b1bc115..ab5305e 100644 (file)
@@ -57,7 +57,7 @@ public:
         return adoptPtr(new InspectorMemoryAgent(instrumentingAgents, state, page, domAgent));
     }
 
-    void getDOMNodeCount(ErrorString*, RefPtr<InspectorArray>& result);
+    void getDOMNodeCount(ErrorString*, RefPtr<InspectorArray>& domGroups, RefPtr<InspectorObject>& strings);
 
     ~InspectorMemoryAgent();