Remove unused member variables found by clang's -Wunused-private-field
[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 memoryCache[] = "MemoryCache";
70 static const char processPrivateMemory[] = "ProcessPrivateMemory";
71
72 static const char cachedImages[] = "CachedImages";
73 static const char cachedCssStyleSheets[] = "CachedCssStyleSheets";
74 static const char cachedScripts[] = "CachedScripts";
75 static const char cachedXslStyleSheets[] = "CachedXslStyleSheets";
76 static const char cachedFonts[] = "CachedFonts";
77 static const char renderTreeUsed[] = "RenderTreeUsed";
78 static const char renderTreeAllocated[] = "RenderTreeAllocated";
79 }
80
81 namespace {
82
83 String nodeName(Node* node)
84 {
85     if (node->document()->isXHTMLDocument())
86          return node->nodeName();
87     return node->nodeName().lower();
88 }
89
90 int stringSize(StringImpl* string)
91 {
92     int size = string->length();
93     if (!string->is8Bit())
94         size *= 2;
95     return size + sizeof(*string);
96 }
97
98 typedef HashSet<StringImpl*, PtrHash<StringImpl*> > StringImplIdentitySet;
99
100 class CharacterDataStatistics {
101     WTF_MAKE_NONCOPYABLE(CharacterDataStatistics);
102 public:
103     CharacterDataStatistics() : m_characterDataSize(0) { }
104
105     void collectCharacterData(Node* node)
106     {
107         if (!node->isCharacterDataNode())
108             return;
109
110         CharacterData* characterData = static_cast<CharacterData*>(node);
111         StringImpl* dataImpl = characterData->dataImpl();
112         if (m_domStringImplSet.contains(dataImpl))
113             return;
114         m_domStringImplSet.add(dataImpl);
115
116         m_characterDataSize += stringSize(dataImpl);
117     }
118
119     bool contains(StringImpl* s) { return m_domStringImplSet.contains(s); }
120
121     int characterDataSize() { return m_characterDataSize; }
122
123 private:
124     StringImplIdentitySet m_domStringImplSet;
125     int m_characterDataSize;
126 };
127
128 class DOMTreeStatistics {
129     WTF_MAKE_NONCOPYABLE(DOMTreeStatistics);
130 public:
131     DOMTreeStatistics(Node* rootNode, CharacterDataStatistics& characterDataStatistics)
132         : m_totalNodeCount(0)
133         , m_characterDataStatistics(characterDataStatistics)
134     {
135         collectTreeStatistics(rootNode);
136     }
137
138     int totalNodeCount() { return m_totalNodeCount; }
139
140     PassRefPtr<TypeBuilder::Array<TypeBuilder::Memory::NodeCount> > nodeCount()
141     {
142         RefPtr<TypeBuilder::Array<TypeBuilder::Memory::NodeCount> > childrenStats = TypeBuilder::Array<TypeBuilder::Memory::NodeCount>::create();
143         for (HashMap<String, int>::iterator it = m_nodeNameToCount.begin(); it != m_nodeNameToCount.end(); ++it) {
144             RefPtr<NodeCount> nodeCount = NodeCount::create().setNodeName(it->first)
145                                                              .setCount(it->second);
146             childrenStats->addItem(nodeCount);
147         }
148         return childrenStats.release();
149     }
150
151     PassRefPtr<TypeBuilder::Array<TypeBuilder::Memory::ListenerCount> > listenerCount()
152     {
153         RefPtr<TypeBuilder::Array<TypeBuilder::Memory::ListenerCount> > listenerStats = TypeBuilder::Array<TypeBuilder::Memory::ListenerCount>::create();
154         for (HashMap<AtomicString, int>::iterator it = m_eventTypeToCount.begin(); it != m_eventTypeToCount.end(); ++it) {
155             RefPtr<ListenerCount> listenerCount = ListenerCount::create().setType(it->first)
156                                                                          .setCount(it->second);
157             listenerStats->addItem(listenerCount);
158         }
159         return listenerStats.release();
160     }
161
162 private:
163     void collectTreeStatistics(Node* rootNode)
164     {
165         Node* currentNode = rootNode;
166         collectListenersInfo(rootNode);
167         while ((currentNode = currentNode->traverseNextNode(rootNode))) {
168             ++m_totalNodeCount;
169             collectNodeStatistics(currentNode);
170         }
171     }
172     void collectNodeStatistics(Node* node)
173     {
174         m_characterDataStatistics.collectCharacterData(node);
175         collectNodeNameInfo(node);
176         collectListenersInfo(node);
177     }
178
179     void collectCharacterData(Node*)
180     {
181     }
182
183     void collectNodeNameInfo(Node* node)
184     {
185         String name = nodeName(node);
186         int currentCount = m_nodeNameToCount.get(name);
187         m_nodeNameToCount.set(name, currentCount + 1);
188     }
189
190     void collectListenersInfo(Node* node)
191     {
192         EventTargetData* d = node->eventTargetData();
193         if (!d)
194             return;
195         EventListenerMap& eventListenerMap = d->eventListenerMap;
196         if (eventListenerMap.isEmpty())
197             return;
198         Vector<AtomicString> eventNames = eventListenerMap.eventTypes();
199         for (Vector<AtomicString>::iterator it = eventNames.begin(); it != eventNames.end(); ++it) {
200             AtomicString name = *it;
201             EventListenerVector* listeners = eventListenerMap.find(name);
202             int count = 0;
203             for (EventListenerVector::iterator j = listeners->begin(); j != listeners->end(); ++j) {
204                 if (j->listener->type() == EventListener::JSEventListenerType)
205                     ++count;
206             }
207             if (count)
208                 m_eventTypeToCount.set(name, m_eventTypeToCount.get(name) + count);
209         }
210     }
211
212     int m_totalNodeCount;
213     HashMap<AtomicString, int> m_eventTypeToCount;
214     HashMap<String, int> m_nodeNameToCount;
215     CharacterDataStatistics& m_characterDataStatistics;
216 };
217
218 class CounterVisitor : public DOMWrapperVisitor {
219 public:
220     CounterVisitor(Page* page)
221         : m_page(page)
222         , m_domGroups(TypeBuilder::Array<TypeBuilder::Memory::DOMGroup>::create())
223         , m_jsExternalStringSize(0)
224         , m_sharedStringSize(0) { }
225
226     TypeBuilder::Array<TypeBuilder::Memory::DOMGroup>* domGroups() { return m_domGroups.get(); }
227
228     PassRefPtr<StringStatistics> strings()
229     {
230         RefPtr<StringStatistics> stringStatistics = StringStatistics::create()
231             .setDom(m_characterDataStatistics.characterDataSize())
232             .setJs(m_jsExternalStringSize)
233             .setShared(m_sharedStringSize);
234         return stringStatistics.release();
235     }
236
237     virtual void visitNode(Node* node)
238     {
239         if (node->document()->frame() && m_page != node->document()->frame()->page())
240             return;
241
242         Node* rootNode = node;
243         while (rootNode->parentNode())
244             rootNode = rootNode->parentNode();
245
246         if (m_roots.contains(rootNode))
247             return;
248         m_roots.add(rootNode);
249
250         DOMTreeStatistics domTreeStats(rootNode, m_characterDataStatistics);
251
252         RefPtr<DOMGroup> domGroup = DOMGroup::create()
253             .setSize(domTreeStats.totalNodeCount())
254             .setTitle(rootNode->nodeType() == Node::ELEMENT_NODE ? elementTitle(static_cast<Element*>(rootNode)) : rootNode->nodeName())
255             .setNodeCount(domTreeStats.nodeCount())
256             .setListenerCount(domTreeStats.listenerCount());
257         if (rootNode->nodeType() == Node::DOCUMENT_NODE)
258             domGroup->setDocumentURI(static_cast<Document*>(rootNode)->documentURI());
259
260         m_domGroups->addItem(domGroup);
261     }
262
263     virtual void visitJSExternalString(StringImpl* string)
264     {
265         int size = stringSize(string);
266         m_jsExternalStringSize += size;
267         if (m_characterDataStatistics.contains(string))
268             m_sharedStringSize += size;
269     }
270
271 private:
272     String elementTitle(Element* element)
273     {
274         StringBuilder result;
275         result.append(nodeName(element));
276
277         const AtomicString& idValue = element->getIdAttribute();
278         String idString;
279         if (!idValue.isNull() && !idValue.isEmpty()) {
280             result.append("#");
281             result.append(idValue);
282         }
283
284         HashSet<AtomicString> usedClassNames;
285         if (element->hasClass() && element->isStyledElement()) {
286             const SpaceSplitString& classNamesString = static_cast<StyledElement*>(element)->classNames();
287             size_t classNameCount = classNamesString.size();
288             for (size_t i = 0; i < classNameCount; ++i) {
289                 const AtomicString& className = classNamesString[i];
290                 if (usedClassNames.contains(className))
291                     continue;
292                 usedClassNames.add(className);
293                 result.append(".");
294                 result.append(className);
295             }
296         }
297         return result.toString();
298     }
299
300     HashSet<Node*> m_roots;
301     Page* m_page;
302     RefPtr<TypeBuilder::Array<TypeBuilder::Memory::DOMGroup> > m_domGroups;
303     CharacterDataStatistics m_characterDataStatistics;
304     int m_jsExternalStringSize;
305     int m_sharedStringSize;
306 };
307
308 } // namespace
309
310 InspectorMemoryAgent::~InspectorMemoryAgent()
311 {
312 }
313
314 void InspectorMemoryAgent::getDOMNodeCount(ErrorString*, RefPtr<TypeBuilder::Array<TypeBuilder::Memory::DOMGroup> >& domGroups, RefPtr<TypeBuilder::Memory::StringStatistics>& strings)
315 {
316     CounterVisitor counterVisitor(m_page);
317     ScriptProfiler::visitJSDOMWrappers(&counterVisitor);
318
319     // Make sure all documents reachable from the main frame are accounted.
320     for (Frame* frame = m_page->mainFrame(); frame; frame = frame->tree()->traverseNext()) {
321         if (Document* doc = frame->document())
322             counterVisitor.visitNode(doc);
323     }
324
325     ScriptProfiler::visitExternalJSStrings(&counterVisitor);
326
327     domGroups = counterVisitor.domGroups();
328     strings = counterVisitor.strings();
329 }
330
331 static PassRefPtr<InspectorMemoryBlock> jsHeapInfo()
332 {
333     size_t usedJSHeapSize;
334     size_t totalJSHeapSize;
335     size_t jsHeapSizeLimit;
336     ScriptGCEvent::getHeapSize(usedJSHeapSize, totalJSHeapSize, jsHeapSizeLimit);
337
338     RefPtr<InspectorMemoryBlock> jsHeapAllocated = InspectorMemoryBlock::create().setName(MemoryBlockName::jsHeapAllocated);
339     jsHeapAllocated->setSize(static_cast<int>(totalJSHeapSize));
340
341     RefPtr<TypeBuilder::Array<InspectorMemoryBlock> > children = TypeBuilder::Array<InspectorMemoryBlock>::create();
342     RefPtr<InspectorMemoryBlock> jsHeapUsed = InspectorMemoryBlock::create().setName(MemoryBlockName::jsHeapUsed);
343     jsHeapUsed->setSize(static_cast<int>(usedJSHeapSize));
344     children->addItem(jsHeapUsed);
345
346     jsHeapAllocated->setChildren(children);
347     return jsHeapAllocated.release();
348 }
349
350 static PassRefPtr<InspectorMemoryBlock> renderTreeInfo(Page* page)
351 {
352     ArenaSize arenaSize = page->renderTreeSize();
353
354     RefPtr<InspectorMemoryBlock> renderTreeAllocated = InspectorMemoryBlock::create().setName(MemoryBlockName::renderTreeAllocated);
355     renderTreeAllocated->setSize(static_cast<int>(arenaSize.allocated));
356
357     RefPtr<TypeBuilder::Array<InspectorMemoryBlock> > children = TypeBuilder::Array<InspectorMemoryBlock>::create();
358     RefPtr<InspectorMemoryBlock> renderTreeUsed = InspectorMemoryBlock::create().setName(MemoryBlockName::renderTreeUsed);
359     renderTreeUsed->setSize(static_cast<int>(arenaSize.treeSize));
360     children->addItem(renderTreeUsed);
361
362     renderTreeAllocated->setChildren(children);
363     return renderTreeAllocated.release();
364 }
365
366 static void addMemoryBlockFor(TypeBuilder::Array<InspectorMemoryBlock>* array, const MemoryCache::TypeStatistic& statistic, const char* name)
367 {
368     RefPtr<InspectorMemoryBlock> result = InspectorMemoryBlock::create().setName(name);
369     result->setSize(statistic.size);
370     array->addItem(result);
371 }
372
373 static PassRefPtr<InspectorMemoryBlock> memoryCacheInfo()
374 {
375     MemoryCache::Statistics stats = memoryCache()->getStatistics();
376     int totalSize = stats.images.size +
377                     stats.cssStyleSheets.size +
378                     stats.scripts.size +
379                     stats.xslStyleSheets.size +
380                     stats.fonts.size;
381     RefPtr<InspectorMemoryBlock> memoryCacheStats = InspectorMemoryBlock::create().setName(MemoryBlockName::memoryCache);
382     memoryCacheStats->setSize(totalSize);
383
384     RefPtr<TypeBuilder::Array<InspectorMemoryBlock> > children = TypeBuilder::Array<InspectorMemoryBlock>::create();
385     addMemoryBlockFor(children.get(), stats.images, MemoryBlockName::cachedImages);
386     addMemoryBlockFor(children.get(), stats.cssStyleSheets, MemoryBlockName::cachedCssStyleSheets);
387     addMemoryBlockFor(children.get(), stats.scripts, MemoryBlockName::cachedScripts);
388     addMemoryBlockFor(children.get(), stats.xslStyleSheets, MemoryBlockName::cachedXslStyleSheets);
389     addMemoryBlockFor(children.get(), stats.fonts, MemoryBlockName::cachedFonts);
390     memoryCacheStats->setChildren(children);
391     return memoryCacheStats.release();
392 }
393
394 void InspectorMemoryAgent::getProcessMemoryDistribution(ErrorString*, RefPtr<InspectorMemoryBlock>& processMemory)
395 {
396     size_t privateBytes = 0;
397     size_t sharedBytes = 0;
398     MemoryUsageSupport::processMemorySizesInBytes(&privateBytes, &sharedBytes);
399     processMemory = InspectorMemoryBlock::create().setName(MemoryBlockName::processPrivateMemory);
400     processMemory->setSize(static_cast<int>(privateBytes));
401
402     RefPtr<TypeBuilder::Array<InspectorMemoryBlock> > children = TypeBuilder::Array<InspectorMemoryBlock>::create();
403     children->addItem(jsHeapInfo());
404     children->addItem(memoryCacheInfo());
405     children->addItem(renderTreeInfo(m_page)); // TODO: collect for all pages?
406     processMemory->setChildren(children);
407 }
408
409 InspectorMemoryAgent::InspectorMemoryAgent(InstrumentingAgents* instrumentingAgents, InspectorState* state, Page* page, InspectorDOMAgent*)
410     : InspectorBaseAgent<InspectorMemoryAgent>("Memory", instrumentingAgents, state)
411     , m_page(page)
412 {
413 }
414
415 } // namespace WebCore
416
417 #endif // ENABLE(INSPECTOR)