0fb2aac33aa5115c41bc510a2291880e610a3a34
[WebKit-https.git] / Source / WebCore / inspector / InspectorMemoryAgent.cpp
1 /*
2  * Copyright (C) 2011 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include "config.h"
32
33 #if ENABLE(INSPECTOR)
34
35 #include "InspectorMemoryAgent.h"
36
37 #include "CharacterData.h"
38 #include "DOMWrapperVisitor.h"
39 #include "Document.h"
40 #include "EventListenerMap.h"
41 #include "Frame.h"
42 #include "InspectorFrontend.h"
43 #include "InspectorState.h"
44 #include "InspectorValues.h"
45 #include "InstrumentingAgents.h"
46 #include "MemoryCache.h"
47 #include "MemoryUsageSupport.h"
48 #include "Node.h"
49 #include "Page.h"
50 #include "ScriptGCEvent.h"
51 #include "ScriptProfiler.h"
52 #include "StyledElement.h"
53 #include <wtf/HashSet.h>
54 #include <wtf/text/StringBuilder.h>
55
56 using WebCore::TypeBuilder::Memory::DOMGroup;
57 using WebCore::TypeBuilder::Memory::ListenerCount;
58 using WebCore::TypeBuilder::Memory::NodeCount;
59 using WebCore::TypeBuilder::Memory::StringStatistics;
60
61 // Use a type alias instead of 'using' here which would cause a conflict on Mac.
62 typedef WebCore::TypeBuilder::Memory::MemoryBlock InspectorMemoryBlock;
63
64 namespace WebCore {
65
66 namespace MemoryBlockName {
67 static const char jsHeapAllocated[] = "JSHeapAllocated";
68 static const char jsHeapUsed[] = "JSHeapUsed";
69 static const char inspectorData[] = "InspectorData";
70 static const char memoryCache[] = "MemoryCache";
71 static const char processPrivateMemory[] = "ProcessPrivateMemory";
72
73 static const char cachedImages[] = "CachedImages";
74 static const char cachedCssStyleSheets[] = "CachedCssStyleSheets";
75 static const char cachedScripts[] = "CachedScripts";
76 static const char cachedXslStyleSheets[] = "CachedXslStyleSheets";
77 static const char cachedFonts[] = "CachedFonts";
78 static const char renderTreeUsed[] = "RenderTreeUsed";
79 static const char renderTreeAllocated[] = "RenderTreeAllocated";
80 }
81
82 namespace {
83
84 String nodeName(Node* node)
85 {
86     if (node->document()->isXHTMLDocument())
87          return node->nodeName();
88     return node->nodeName().lower();
89 }
90
91 int stringSize(StringImpl* string)
92 {
93     int size = string->length();
94     if (!string->is8Bit())
95         size *= 2;
96     return size + sizeof(*string);
97 }
98
99 typedef HashSet<StringImpl*, PtrHash<StringImpl*> > StringImplIdentitySet;
100
101 class CharacterDataStatistics {
102     WTF_MAKE_NONCOPYABLE(CharacterDataStatistics);
103 public:
104     CharacterDataStatistics() : m_characterDataSize(0) { }
105
106     void collectCharacterData(Node* node)
107     {
108         if (!node->isCharacterDataNode())
109             return;
110
111         CharacterData* characterData = static_cast<CharacterData*>(node);
112         StringImpl* dataImpl = characterData->dataImpl();
113         if (m_domStringImplSet.contains(dataImpl))
114             return;
115         m_domStringImplSet.add(dataImpl);
116
117         m_characterDataSize += stringSize(dataImpl);
118     }
119
120     bool contains(StringImpl* s) { return m_domStringImplSet.contains(s); }
121
122     int characterDataSize() { return m_characterDataSize; }
123
124 private:
125     StringImplIdentitySet m_domStringImplSet;
126     int m_characterDataSize;
127 };
128
129 class DOMTreeStatistics {
130     WTF_MAKE_NONCOPYABLE(DOMTreeStatistics);
131 public:
132     DOMTreeStatistics(Node* rootNode, CharacterDataStatistics& characterDataStatistics)
133         : m_totalNodeCount(0)
134         , m_characterDataStatistics(characterDataStatistics)
135     {
136         collectTreeStatistics(rootNode);
137     }
138
139     int totalNodeCount() { return m_totalNodeCount; }
140
141     PassRefPtr<TypeBuilder::Array<TypeBuilder::Memory::NodeCount> > nodeCount()
142     {
143         RefPtr<TypeBuilder::Array<TypeBuilder::Memory::NodeCount> > childrenStats = TypeBuilder::Array<TypeBuilder::Memory::NodeCount>::create();
144         for (HashMap<String, int>::iterator it = m_nodeNameToCount.begin(); it != m_nodeNameToCount.end(); ++it) {
145             RefPtr<NodeCount> nodeCount = NodeCount::create().setNodeName(it->first)
146                                                              .setCount(it->second);
147             childrenStats->addItem(nodeCount);
148         }
149         return childrenStats.release();
150     }
151
152     PassRefPtr<TypeBuilder::Array<TypeBuilder::Memory::ListenerCount> > listenerCount()
153     {
154         RefPtr<TypeBuilder::Array<TypeBuilder::Memory::ListenerCount> > listenerStats = TypeBuilder::Array<TypeBuilder::Memory::ListenerCount>::create();
155         for (HashMap<AtomicString, int>::iterator it = m_eventTypeToCount.begin(); it != m_eventTypeToCount.end(); ++it) {
156             RefPtr<ListenerCount> listenerCount = ListenerCount::create().setType(it->first)
157                                                                          .setCount(it->second);
158             listenerStats->addItem(listenerCount);
159         }
160         return listenerStats.release();
161     }
162
163 private:
164     void collectTreeStatistics(Node* rootNode)
165     {
166         Node* currentNode = rootNode;
167         collectListenersInfo(rootNode);
168         while ((currentNode = currentNode->traverseNextNode(rootNode))) {
169             ++m_totalNodeCount;
170             collectNodeStatistics(currentNode);
171         }
172     }
173     void collectNodeStatistics(Node* node)
174     {
175         m_characterDataStatistics.collectCharacterData(node);
176         collectNodeNameInfo(node);
177         collectListenersInfo(node);
178     }
179
180     void collectCharacterData(Node*)
181     {
182     }
183
184     void collectNodeNameInfo(Node* node)
185     {
186         String name = nodeName(node);
187         int currentCount = m_nodeNameToCount.get(name);
188         m_nodeNameToCount.set(name, currentCount + 1);
189     }
190
191     void collectListenersInfo(Node* node)
192     {
193         EventTargetData* d = node->eventTargetData();
194         if (!d)
195             return;
196         EventListenerMap& eventListenerMap = d->eventListenerMap;
197         if (eventListenerMap.isEmpty())
198             return;
199         Vector<AtomicString> eventNames = eventListenerMap.eventTypes();
200         for (Vector<AtomicString>::iterator it = eventNames.begin(); it != eventNames.end(); ++it) {
201             AtomicString name = *it;
202             EventListenerVector* listeners = eventListenerMap.find(name);
203             int count = 0;
204             for (EventListenerVector::iterator j = listeners->begin(); j != listeners->end(); ++j) {
205                 if (j->listener->type() == EventListener::JSEventListenerType)
206                     ++count;
207             }
208             if (count)
209                 m_eventTypeToCount.set(name, m_eventTypeToCount.get(name) + count);
210         }
211     }
212
213     int m_totalNodeCount;
214     HashMap<AtomicString, int> m_eventTypeToCount;
215     HashMap<String, int> m_nodeNameToCount;
216     CharacterDataStatistics& m_characterDataStatistics;
217 };
218
219 class CounterVisitor : public DOMWrapperVisitor {
220 public:
221     CounterVisitor(Page* page)
222         : m_page(page)
223         , m_domGroups(TypeBuilder::Array<TypeBuilder::Memory::DOMGroup>::create())
224         , m_jsExternalStringSize(0)
225         , m_sharedStringSize(0) { }
226
227     TypeBuilder::Array<TypeBuilder::Memory::DOMGroup>* domGroups() { return m_domGroups.get(); }
228
229     PassRefPtr<StringStatistics> strings()
230     {
231         RefPtr<StringStatistics> stringStatistics = StringStatistics::create()
232             .setDom(m_characterDataStatistics.characterDataSize())
233             .setJs(m_jsExternalStringSize)
234             .setShared(m_sharedStringSize);
235         return stringStatistics.release();
236     }
237
238     virtual void visitNode(Node* node)
239     {
240         if (node->document()->frame() && m_page != node->document()->frame()->page())
241             return;
242
243         Node* rootNode = node;
244         while (rootNode->parentNode())
245             rootNode = rootNode->parentNode();
246
247         if (m_roots.contains(rootNode))
248             return;
249         m_roots.add(rootNode);
250
251         DOMTreeStatistics domTreeStats(rootNode, m_characterDataStatistics);
252
253         RefPtr<DOMGroup> domGroup = DOMGroup::create()
254             .setSize(domTreeStats.totalNodeCount())
255             .setTitle(rootNode->nodeType() == Node::ELEMENT_NODE ? elementTitle(static_cast<Element*>(rootNode)) : rootNode->nodeName())
256             .setNodeCount(domTreeStats.nodeCount())
257             .setListenerCount(domTreeStats.listenerCount());
258         if (rootNode->nodeType() == Node::DOCUMENT_NODE)
259             domGroup->setDocumentURI(static_cast<Document*>(rootNode)->documentURI());
260
261         m_domGroups->addItem(domGroup);
262     }
263
264     virtual void visitJSExternalString(StringImpl* string)
265     {
266         int size = stringSize(string);
267         m_jsExternalStringSize += size;
268         if (m_characterDataStatistics.contains(string))
269             m_sharedStringSize += size;
270     }
271
272 private:
273     String elementTitle(Element* element)
274     {
275         StringBuilder result;
276         result.append(nodeName(element));
277
278         const AtomicString& idValue = element->getIdAttribute();
279         String idString;
280         if (!idValue.isNull() && !idValue.isEmpty()) {
281             result.append("#");
282             result.append(idValue);
283         }
284
285         HashSet<AtomicString> usedClassNames;
286         if (element->hasClass() && element->isStyledElement()) {
287             const SpaceSplitString& classNamesString = static_cast<StyledElement*>(element)->classNames();
288             size_t classNameCount = classNamesString.size();
289             for (size_t i = 0; i < classNameCount; ++i) {
290                 const AtomicString& className = classNamesString[i];
291                 if (usedClassNames.contains(className))
292                     continue;
293                 usedClassNames.add(className);
294                 result.append(".");
295                 result.append(className);
296             }
297         }
298         return result.toString();
299     }
300
301     HashSet<Node*> m_roots;
302     Page* m_page;
303     RefPtr<TypeBuilder::Array<TypeBuilder::Memory::DOMGroup> > m_domGroups;
304     CharacterDataStatistics m_characterDataStatistics;
305     int m_jsExternalStringSize;
306     int m_sharedStringSize;
307 };
308
309 } // namespace
310
311 InspectorMemoryAgent::~InspectorMemoryAgent()
312 {
313 }
314
315 void InspectorMemoryAgent::getDOMNodeCount(ErrorString*, RefPtr<TypeBuilder::Array<TypeBuilder::Memory::DOMGroup> >& domGroups, RefPtr<TypeBuilder::Memory::StringStatistics>& strings)
316 {
317     CounterVisitor counterVisitor(m_page);
318     ScriptProfiler::visitJSDOMWrappers(&counterVisitor);
319
320     // Make sure all documents reachable from the main frame are accounted.
321     for (Frame* frame = m_page->mainFrame(); frame; frame = frame->tree()->traverseNext()) {
322         if (Document* doc = frame->document())
323             counterVisitor.visitNode(doc);
324     }
325
326     ScriptProfiler::visitExternalJSStrings(&counterVisitor);
327
328     domGroups = counterVisitor.domGroups();
329     strings = counterVisitor.strings();
330 }
331
332 static PassRefPtr<InspectorMemoryBlock> jsHeapInfo()
333 {
334     size_t usedJSHeapSize;
335     size_t totalJSHeapSize;
336     size_t jsHeapSizeLimit;
337     ScriptGCEvent::getHeapSize(usedJSHeapSize, totalJSHeapSize, jsHeapSizeLimit);
338
339     RefPtr<InspectorMemoryBlock> jsHeapAllocated = InspectorMemoryBlock::create().setName(MemoryBlockName::jsHeapAllocated);
340     jsHeapAllocated->setSize(totalJSHeapSize);
341
342     RefPtr<TypeBuilder::Array<InspectorMemoryBlock> > children = TypeBuilder::Array<InspectorMemoryBlock>::create();
343     RefPtr<InspectorMemoryBlock> jsHeapUsed = InspectorMemoryBlock::create().setName(MemoryBlockName::jsHeapUsed);
344     jsHeapUsed->setSize(usedJSHeapSize);
345     children->addItem(jsHeapUsed);
346
347     jsHeapAllocated->setChildren(children);
348     return jsHeapAllocated.release();
349 }
350
351 static PassRefPtr<InspectorMemoryBlock> inspectorData()
352 {
353     size_t dataSize = ScriptProfiler::profilerSnapshotsSize();
354     RefPtr<InspectorMemoryBlock> inspectorData = InspectorMemoryBlock::create().setName(MemoryBlockName::inspectorData);
355     inspectorData->setSize(dataSize);
356     return inspectorData.release();
357 }
358
359 static PassRefPtr<InspectorMemoryBlock> renderTreeInfo(Page* page)
360 {
361     ArenaSize arenaSize = page->renderTreeSize();
362
363     RefPtr<InspectorMemoryBlock> renderTreeAllocated = InspectorMemoryBlock::create().setName(MemoryBlockName::renderTreeAllocated);
364     renderTreeAllocated->setSize(arenaSize.allocated);
365
366     RefPtr<TypeBuilder::Array<InspectorMemoryBlock> > children = TypeBuilder::Array<InspectorMemoryBlock>::create();
367     RefPtr<InspectorMemoryBlock> renderTreeUsed = InspectorMemoryBlock::create().setName(MemoryBlockName::renderTreeUsed);
368     renderTreeUsed->setSize(arenaSize.treeSize);
369     children->addItem(renderTreeUsed);
370
371     renderTreeAllocated->setChildren(children);
372     return renderTreeAllocated.release();
373 }
374
375 static void addMemoryBlockFor(TypeBuilder::Array<InspectorMemoryBlock>* array, const MemoryCache::TypeStatistic& statistic, const char* name)
376 {
377     RefPtr<InspectorMemoryBlock> result = InspectorMemoryBlock::create().setName(name);
378     result->setSize(statistic.size);
379     array->addItem(result);
380 }
381
382 static PassRefPtr<InspectorMemoryBlock> memoryCacheInfo()
383 {
384     MemoryCache::Statistics stats = memoryCache()->getStatistics();
385     int totalSize = stats.images.size +
386                     stats.cssStyleSheets.size +
387                     stats.scripts.size +
388                     stats.xslStyleSheets.size +
389                     stats.fonts.size;
390     RefPtr<InspectorMemoryBlock> memoryCacheStats = InspectorMemoryBlock::create().setName(MemoryBlockName::memoryCache);
391     memoryCacheStats->setSize(totalSize);
392
393     RefPtr<TypeBuilder::Array<InspectorMemoryBlock> > children = TypeBuilder::Array<InspectorMemoryBlock>::create();
394     addMemoryBlockFor(children.get(), stats.images, MemoryBlockName::cachedImages);
395     addMemoryBlockFor(children.get(), stats.cssStyleSheets, MemoryBlockName::cachedCssStyleSheets);
396     addMemoryBlockFor(children.get(), stats.scripts, MemoryBlockName::cachedScripts);
397     addMemoryBlockFor(children.get(), stats.xslStyleSheets, MemoryBlockName::cachedXslStyleSheets);
398     addMemoryBlockFor(children.get(), stats.fonts, MemoryBlockName::cachedFonts);
399     memoryCacheStats->setChildren(children);
400     return memoryCacheStats.release();
401 }
402
403 void InspectorMemoryAgent::getProcessMemoryDistribution(ErrorString*, RefPtr<InspectorMemoryBlock>& processMemory)
404 {
405     size_t privateBytes = 0;
406     size_t sharedBytes = 0;
407     MemoryUsageSupport::processMemorySizesInBytes(&privateBytes, &sharedBytes);
408     processMemory = InspectorMemoryBlock::create().setName(MemoryBlockName::processPrivateMemory);
409     processMemory->setSize(privateBytes);
410
411     RefPtr<TypeBuilder::Array<InspectorMemoryBlock> > children = TypeBuilder::Array<InspectorMemoryBlock>::create();
412     children->addItem(jsHeapInfo());
413     children->addItem(inspectorData());
414     children->addItem(memoryCacheInfo());
415     children->addItem(renderTreeInfo(m_page)); // TODO: collect for all pages?
416     processMemory->setChildren(children);
417 }
418
419 InspectorMemoryAgent::InspectorMemoryAgent(InstrumentingAgents* instrumentingAgents, InspectorState* state, Page* page, InspectorDOMAgent*)
420     : InspectorBaseAgent<InspectorMemoryAgent>("Memory", instrumentingAgents, state)
421     , m_page(page)
422 {
423 }
424
425 } // namespace WebCore
426
427 #endif // ENABLE(INSPECTOR)