Web Inspector: Native Memory Instrumentation: reportLeaf method doesn't report the...
[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 "BindingVisitors.h"
38 #include "CharacterData.h"
39 #include "Document.h"
40 #include "EventListenerMap.h"
41 #include "Frame.h"
42 #include "HeapGraphSerializer.h"
43 #include "InspectorClient.h"
44 #include "InspectorDOMStorageAgent.h"
45 #include "InspectorFrontend.h"
46 #include "InspectorState.h"
47 #include "InspectorValues.h"
48 #include "InstrumentingAgents.h"
49 #include "MemoryCache.h"
50 #include "MemoryInstrumentationImpl.h"
51 #include "MemoryUsageSupport.h"
52 #include "Node.h"
53 #include "NodeTraversal.h"
54 #include "Page.h"
55 #include "ScriptGCEvent.h"
56 #include "ScriptProfiler.h"
57 #include "StyledElement.h"
58 #include <wtf/ArrayBufferView.h>
59 #include <wtf/HashSet.h>
60 #include <wtf/MemoryInstrumentationArrayBufferView.h>
61 #include <wtf/NonCopyingSort.h>
62 #include <wtf/OwnPtr.h>
63 #include <wtf/PassOwnPtr.h>
64 #include <wtf/Vector.h>
65 #include <wtf/text/StringBuilder.h>
66 #include <wtf/text/StringImpl.h>
67 #include <wtf/text/WTFString.h>
68
69 // Use a type alias instead of 'using' here which would cause a conflict on Mac.
70 typedef WebCore::TypeBuilder::Memory::MemoryBlock InspectorMemoryBlock;
71 typedef WebCore::TypeBuilder::Array<InspectorMemoryBlock> InspectorMemoryBlocks;
72
73 namespace WebCore {
74
75 namespace {
76
77 class MemoryUsageStatsGenerator {
78 public:
79     MemoryUsageStatsGenerator() { }
80
81     void dump(const TypeNameToSizeMap& sizesMap, InspectorMemoryBlocks* children)
82     {
83         m_sizesMap = sizesMap;
84
85         // FIXME: We filter out Rendering type because the coverage is not good enough at the moment
86         // and report RenderArena size instead.
87         for (TypeNameToSizeMap::iterator i = m_sizesMap.begin(); i != m_sizesMap.end(); ++i) {
88             if (i->key == PlatformMemoryTypes::Rendering) {
89                 m_sizesMap.remove(i);
90                 break;
91             }
92         }
93         Vector<String> objectTypes;
94         objectTypes.appendRange(m_sizesMap.keys().begin(), m_sizesMap.keys().end());
95
96         for (Vector<String>::const_iterator i = objectTypes.begin(); i != objectTypes.end(); ++i)
97             updateParentSizes(*i, m_sizesMap.get(*i));
98
99         objectTypes.clear();
100         objectTypes.appendRange(m_sizesMap.keys().begin(), m_sizesMap.keys().end());
101         nonCopyingSort(objectTypes.begin(), objectTypes.end(), stringCompare);
102
103         size_t index = 0;
104         while (index < objectTypes.size())
105             index = buildObjectForIndex(index, objectTypes, children);
106
107     }
108
109 private:
110     static bool stringCompare(const String& a, const String& b) { return WTF::codePointCompare(a, b) < 0; }
111
112     void updateParentSizes(String objectType, const size_t size)
113     {
114         for (size_t dotPosition = objectType.reverseFind('.'); dotPosition != notFound; dotPosition = objectType.reverseFind('.', dotPosition)) {
115             objectType = objectType.substring(0, dotPosition);
116             TypeNameToSizeMap::AddResult result = m_sizesMap.add(objectType, size);
117             if (!result.isNewEntry)
118                 result.iterator->value += size;
119         }
120     }
121
122     size_t buildObjectForIndex(size_t index, const Vector<String>& objectTypes, InspectorMemoryBlocks* array)
123     {
124         String typeName = objectTypes[index];
125         size_t dotPosition = typeName.reverseFind('.');
126         String blockName = (dotPosition == notFound) ? typeName : typeName.substring(dotPosition + 1);
127         RefPtr<InspectorMemoryBlock> block = InspectorMemoryBlock::create().setName(blockName);
128         block->setSize(m_sizesMap.get(typeName));
129         String prefix = typeName;
130         prefix.append('.');
131         array->addItem(block);
132         ++index;
133         RefPtr<InspectorMemoryBlocks> children;
134         while (index < objectTypes.size() && objectTypes[index].startsWith(prefix)) {
135             if (!children)
136                 children = InspectorMemoryBlocks::create();
137             index = buildObjectForIndex(index, objectTypes, children.get());
138         }
139         if (children)
140             block->setChildren(children.release());
141         return index;
142     }
143
144     TypeNameToSizeMap m_sizesMap;
145 };
146
147 class ExternalStringsRoot : public ExternalStringVisitor {
148 public:
149     ExternalStringsRoot() : m_memoryClassInfo(0) { }
150
151     void reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const
152     {
153         MemoryClassInfo info(memoryObjectInfo, this, WebCoreMemoryTypes::ExternalStrings);
154         m_memoryClassInfo = &info;
155         ScriptProfiler::visitExternalStrings(const_cast<ExternalStringsRoot*>(this));
156         m_memoryClassInfo = 0;
157         info.ignoreMember(m_memoryClassInfo);
158     }
159
160 private:
161     virtual void visitJSExternalString(StringImpl* string)
162     {
163         m_memoryClassInfo->addMember(string, "externalString");
164     }
165
166     mutable MemoryClassInfo* m_memoryClassInfo;
167 };
168
169 class ExternalArraysRoot : public ExternalArrayVisitor {
170 public:
171     ExternalArraysRoot() : m_memoryClassInfo(0) { }
172
173     void reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const
174     {
175         MemoryClassInfo info(memoryObjectInfo, this, WebCoreMemoryTypes::ExternalArrays);
176         m_memoryClassInfo = &info;
177         ScriptProfiler::visitExternalArrays(const_cast<ExternalArraysRoot*>(this));
178         m_memoryClassInfo = 0;
179         info.ignoreMember(m_memoryClassInfo);
180     }
181
182 private:
183     virtual void visitJSExternalArray(ArrayBufferView* arrayBufferView)
184     {
185         m_memoryClassInfo->addMember(arrayBufferView, "externalArray");
186     }
187
188     mutable MemoryClassInfo* m_memoryClassInfo;
189 };
190
191 } // namespace
192
193 InspectorMemoryAgent::~InspectorMemoryAgent()
194 {
195 }
196
197 void InspectorMemoryAgent::getDOMCounters(ErrorString*, int* documents, int* nodes, int* jsEventListeners)
198 {
199     *documents = InspectorCounters::counterValue(InspectorCounters::DocumentCounter);
200     *nodes = InspectorCounters::counterValue(InspectorCounters::NodeCounter);
201     *jsEventListeners = ThreadLocalInspectorCounters::current().counterValue(ThreadLocalInspectorCounters::JSEventListenerCounter);
202 }
203
204 static void reportJSHeapInfo(WTF::MemoryInstrumentationClient& memoryInstrumentationClient)
205 {
206     HeapInfo info;
207     ScriptGCEvent::getHeapSize(info);
208
209     memoryInstrumentationClient.countObjectSize(0, WebCoreMemoryTypes::JSHeapUsed, info.usedJSHeapSize);
210     memoryInstrumentationClient.countObjectSize(0, WebCoreMemoryTypes::JSHeapUnused, info.totalJSHeapSize - info.usedJSHeapSize);
211 }
212
213 static void reportRenderTreeInfo(WTF::MemoryInstrumentationClient& memoryInstrumentationClient, Page* page)
214 {
215     ArenaSize arenaSize = page->renderTreeSize();
216
217     memoryInstrumentationClient.countObjectSize(0, WebCoreMemoryTypes::RenderTreeUsed, arenaSize.treeSize);
218     memoryInstrumentationClient.countObjectSize(0, WebCoreMemoryTypes::RenderTreeUnused, arenaSize.allocated - arenaSize.treeSize);
219 }
220
221 namespace {
222
223 class DOMTreesIterator : public WrappedNodeVisitor {
224 public:
225     DOMTreesIterator(MemoryInstrumentationImpl& memoryInstrumentation, Page* page)
226         : m_page(page)
227         , m_memoryInstrumentation(memoryInstrumentation)
228     {
229     }
230
231     virtual void visitNode(Node* node) OVERRIDE
232     {
233         if (node->document() && node->document()->frame() && m_page != node->document()->frame()->page())
234             return;
235
236         while (Node* parentNode = node->parentNode())
237             node = parentNode;
238
239         m_memoryInstrumentation.addRootObject(node);
240     }
241
242     void visitFrame(Frame* frame)
243     {
244         m_memoryInstrumentation.addRootObject(frame);
245     }
246
247     void visitBindings()
248     {
249         ScriptProfiler::collectBindingMemoryInfo(&m_memoryInstrumentation);
250     }
251
252     void visitMemoryCache()
253     {
254         m_memoryInstrumentation.addRootObject(memoryCache());
255     }
256
257
258 private:
259     Page* m_page;
260     MemoryInstrumentationImpl& m_memoryInstrumentation;
261 };
262
263 }
264
265 static void collectDomTreeInfo(MemoryInstrumentationImpl& memoryInstrumentation, Page* page)
266 {
267     ExternalStringsRoot stringsRoot;
268     memoryInstrumentation.addRootObject(stringsRoot);
269
270     ExternalArraysRoot arraysRoot;
271     memoryInstrumentation.addRootObject(arraysRoot);
272
273     DOMTreesIterator domTreesIterator(memoryInstrumentation, page);
274
275     ScriptProfiler::visitNodeWrappers(&domTreesIterator);
276
277     // Make sure all documents reachable from the main frame are accounted.
278     for (Frame* frame = page->mainFrame(); frame; frame = frame->tree()->traverseNext()) {
279         if (Document* doc = frame->document()) {
280             domTreesIterator.visitNode(doc);
281             domTreesIterator.visitFrame(frame);
282         }
283     }
284
285     domTreesIterator.visitBindings();
286     domTreesIterator.visitMemoryCache();
287 }
288
289 static void addPlatformComponentsInfo(TypeNameToSizeMap* memoryInfo)
290 {
291     Vector<MemoryUsageSupport::ComponentInfo> components;
292     MemoryUsageSupport::memoryUsageByComponents(components);
293     for (Vector<MemoryUsageSupport::ComponentInfo>::iterator it = components.begin(); it != components.end(); ++it)
294         memoryInfo->add(it->m_name, it->m_sizeInBytes);
295 }
296
297 static void addMemoryInstrumentationDebugData(MemoryInstrumentationClientImpl* client, TypeNameToSizeMap* memoryInfo)
298 {
299     if (client->checkInstrumentedObjects()) {
300         memoryInfo->add("InstrumentedObjectsCount", client->totalCountedObjects());
301         memoryInfo->add("InstrumentedButNotAllocatedObjectsCount", client->totalObjectsNotInAllocatedSet());
302     }
303 }
304
305 void InspectorMemoryAgent::getProcessMemoryDistributionMap(TypeNameToSizeMap* memoryInfo)
306 {
307     getProcessMemoryDistributionImpl(false, memoryInfo);
308 }
309
310 void InspectorMemoryAgent::getProcessMemoryDistribution(ErrorString*, const bool* reportGraph, RefPtr<InspectorMemoryBlock>& processMemory)
311 {
312     TypeNameToSizeMap memoryInfo;
313     getProcessMemoryDistributionImpl(reportGraph && *reportGraph, &memoryInfo);
314
315     MemoryUsageStatsGenerator statsGenerator;
316     RefPtr<InspectorMemoryBlocks> children = InspectorMemoryBlocks::create();
317     statsGenerator.dump(memoryInfo, children.get());
318
319     processMemory = InspectorMemoryBlock::create().setName(WebCoreMemoryTypes::ProcessPrivateMemory);
320     processMemory->setChildren(children);
321
322     size_t privateBytes = 0;
323     size_t sharedBytes = 0;
324     MemoryUsageSupport::processMemorySizesInBytes(&privateBytes, &sharedBytes);
325     processMemory->setSize(privateBytes);
326 }
327
328 void InspectorMemoryAgent::reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const
329 {
330     MemoryClassInfo info(memoryObjectInfo, this, WebCoreMemoryTypes::Inspector);
331     InspectorBaseAgent<InspectorMemoryAgent>::reportMemoryUsage(memoryObjectInfo);
332     info.addWeakPointer(m_inspectorClient);
333     info.addMember(m_page, "page");
334 }
335
336 namespace {
337
338 class FrontendWrapper : public HeapGraphSerializer::Client {
339 public:
340     explicit FrontendWrapper(InspectorFrontend::Memory* frontend) : m_frontend(frontend) { }
341     virtual void addNativeSnapshotChunk(PassRefPtr<TypeBuilder::Memory::HeapSnapshotChunk> heapSnapshotChunk) OVERRIDE
342     {
343         m_frontend->addNativeSnapshotChunk(heapSnapshotChunk);
344     }
345 private:
346     InspectorFrontend::Memory* m_frontend;
347 };
348
349 }
350
351 void InspectorMemoryAgent::getProcessMemoryDistributionImpl(bool reportGraph, TypeNameToSizeMap* memoryInfo)
352 {
353     OwnPtr<HeapGraphSerializer> graphSerializer;
354     OwnPtr<FrontendWrapper> frontendWrapper;
355
356     if (reportGraph) {
357         frontendWrapper = adoptPtr(new FrontendWrapper(m_frontend));
358         graphSerializer = adoptPtr(new HeapGraphSerializer(frontendWrapper.get()));
359     }
360
361     MemoryInstrumentationClientImpl memoryInstrumentationClient(graphSerializer.get());
362     m_inspectorClient->getAllocatedObjects(memoryInstrumentationClient.allocatedObjects());
363     MemoryInstrumentationImpl memoryInstrumentation(&memoryInstrumentationClient);
364
365     reportJSHeapInfo(memoryInstrumentationClient);
366     reportRenderTreeInfo(memoryInstrumentationClient, m_page);
367     collectDomTreeInfo(memoryInstrumentation, m_page); // FIXME: collect for all pages?
368
369     PlatformMemoryInstrumentation::reportStaticMembersMemoryUsage(&memoryInstrumentation);
370     WebCoreMemoryInstrumentation::reportStaticMembersMemoryUsage(&memoryInstrumentation);
371
372     memoryInstrumentation.addRootObject(this);
373     memoryInstrumentation.addRootObject(memoryInstrumentation);
374     memoryInstrumentation.addRootObject(memoryInstrumentationClient);
375     if (graphSerializer) {
376         memoryInstrumentation.addRootObject(graphSerializer.get());
377         graphSerializer->finish();
378         graphSerializer.release(); // Release it earlier than frontendWrapper
379         frontendWrapper.release();
380     }
381
382     m_inspectorClient->dumpUncountedAllocatedObjects(memoryInstrumentationClient.countedObjects());
383
384     *memoryInfo = memoryInstrumentationClient.sizesMap();
385     addPlatformComponentsInfo(memoryInfo);
386     addMemoryInstrumentationDebugData(&memoryInstrumentationClient, memoryInfo);
387 }
388
389 InspectorMemoryAgent::InspectorMemoryAgent(InstrumentingAgents* instrumentingAgents, InspectorClient* client, InspectorCompositeState* state, Page* page)
390     : InspectorBaseAgent<InspectorMemoryAgent>("Memory", instrumentingAgents, state)
391     , m_inspectorClient(client)
392     , m_page(page)
393     , m_frontend(0)
394 {
395 }
396
397 void InspectorMemoryAgent::setFrontend(InspectorFrontend* frontend)
398 {
399     ASSERT(!m_frontend);
400     m_frontend = frontend->memory();
401 }
402
403 void InspectorMemoryAgent::clearFrontend()
404 {
405     m_frontend = 0;
406 }
407
408 } // namespace WebCore
409
410 #endif // ENABLE(INSPECTOR)