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