| /* |
| * Copyright (C) 2011 Google Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| |
| #if ENABLE(INSPECTOR) |
| |
| #include "InspectorMemoryAgent.h" |
| |
| #include "BindingVisitors.h" |
| #include "CharacterData.h" |
| #include "Document.h" |
| #include "EventListenerMap.h" |
| #include "Frame.h" |
| #include "InspectorFrontend.h" |
| #include "InspectorState.h" |
| #include "InspectorValues.h" |
| #include "InstrumentingAgents.h" |
| #include "MemoryCache.h" |
| #include "MemoryInstrumentation.h" |
| #include "MemoryUsageSupport.h" |
| #include "Node.h" |
| #include "Page.h" |
| #include "ScriptGCEvent.h" |
| #include "ScriptProfiler.h" |
| #include "StyledElement.h" |
| #include <wtf/ArrayBuffer.h> |
| #include <wtf/ArrayBufferView.h> |
| #include <wtf/HashSet.h> |
| #include <wtf/text/StringBuilder.h> |
| #include <wtf/text/StringImpl.h> |
| #include <wtf/text/WTFString.h> |
| |
| using WebCore::TypeBuilder::Memory::DOMGroup; |
| using WebCore::TypeBuilder::Memory::ListenerCount; |
| using WebCore::TypeBuilder::Memory::NodeCount; |
| using WebCore::TypeBuilder::Memory::StringStatistics; |
| |
| // Use a type alias instead of 'using' here which would cause a conflict on Mac. |
| typedef WebCore::TypeBuilder::Memory::MemoryBlock InspectorMemoryBlock; |
| |
| namespace WebCore { |
| |
| namespace MemoryBlockName { |
| static const char jsHeapAllocated[] = "JSHeapAllocated"; |
| static const char jsHeapUsed[] = "JSHeapUsed"; |
| static const char jsExternalResources[] = "JSExternalResources"; |
| static const char jsExternalArrays[] = "JSExternalArrays"; |
| static const char jsExternalStrings[] = "JSExternalStrings"; |
| static const char inspectorData[] = "InspectorData"; |
| static const char memoryCache[] = "MemoryCache"; |
| static const char processPrivateMemory[] = "ProcessPrivateMemory"; |
| |
| static const char cachedImages[] = "CachedImages"; |
| static const char cachedCssStyleSheets[] = "CachedCssStyleSheets"; |
| static const char cachedScripts[] = "CachedScripts"; |
| static const char cachedXslStyleSheets[] = "CachedXslStyleSheets"; |
| static const char cachedFonts[] = "CachedFonts"; |
| static const char renderTreeUsed[] = "RenderTreeUsed"; |
| static const char renderTreeAllocated[] = "RenderTreeAllocated"; |
| |
| static const char dom[] = "DOM"; |
| static const char domTreeOther[] = "DOMTreeOther"; |
| static const char domTreeDOM[] = "DOMTreeDOM"; |
| static const char domTreeCSS[] = "DOMTreeCSS"; |
| } |
| |
| namespace { |
| |
| typedef HashSet<const void*> VisitedObjects; |
| |
| String nodeName(Node* node) |
| { |
| if (node->document()->isXHTMLDocument()) |
| return node->nodeName(); |
| return node->nodeName().lower(); |
| } |
| |
| size_t stringSize(StringImpl* string) |
| { |
| // TODO: support substrings |
| size_t size = string->length(); |
| if (string->is8Bit()) { |
| if (string->has16BitShadow()) { |
| size += 2 * size; |
| if (string->hasTerminatingNullCharacter()) |
| size += 2; |
| } |
| } else |
| 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, CharacterDataStatistics& characterDataStatistics) |
| : m_totalNodeCount(0) |
| , m_characterDataStatistics(characterDataStatistics) |
| { |
| collectTreeStatistics(rootNode); |
| } |
| |
| int totalNodeCount() { return m_totalNodeCount; } |
| |
| PassRefPtr<TypeBuilder::Array<TypeBuilder::Memory::NodeCount> > nodeCount() |
| { |
| RefPtr<TypeBuilder::Array<TypeBuilder::Memory::NodeCount> > childrenStats = TypeBuilder::Array<TypeBuilder::Memory::NodeCount>::create(); |
| for (HashMap<String, int>::iterator it = m_nodeNameToCount.begin(); it != m_nodeNameToCount.end(); ++it) { |
| RefPtr<NodeCount> nodeCount = NodeCount::create().setNodeName(it->first) |
| .setCount(it->second); |
| childrenStats->addItem(nodeCount); |
| } |
| return childrenStats.release(); |
| } |
| |
| PassRefPtr<TypeBuilder::Array<TypeBuilder::Memory::ListenerCount> > listenerCount() |
| { |
| RefPtr<TypeBuilder::Array<TypeBuilder::Memory::ListenerCount> > listenerStats = TypeBuilder::Array<TypeBuilder::Memory::ListenerCount>::create(); |
| for (HashMap<AtomicString, int>::iterator it = m_eventTypeToCount.begin(); it != m_eventTypeToCount.end(); ++it) { |
| RefPtr<ListenerCount> listenerCount = ListenerCount::create().setType(it->first) |
| .setCount(it->second); |
| listenerStats->addItem(listenerCount); |
| } |
| return listenerStats.release(); |
| } |
| |
| private: |
| void collectTreeStatistics(Node* rootNode) |
| { |
| Node* currentNode = rootNode; |
| collectListenersInfo(rootNode); |
| while ((currentNode = currentNode->traverseNextNode(rootNode))) { |
| ++m_totalNodeCount; |
| collectNodeStatistics(currentNode); |
| } |
| } |
| void collectNodeStatistics(Node* node) |
| { |
| m_characterDataStatistics.collectCharacterData(node); |
| collectNodeNameInfo(node); |
| collectListenersInfo(node); |
| } |
| |
| void collectNodeNameInfo(Node* node) |
| { |
| String name = nodeName(node); |
| int currentCount = m_nodeNameToCount.get(name); |
| m_nodeNameToCount.set(name, currentCount + 1); |
| } |
| |
| void collectListenersInfo(Node* node) |
| { |
| EventTargetData* d = node->eventTargetData(); |
| if (!d) |
| return; |
| EventListenerMap& eventListenerMap = d->eventListenerMap; |
| if (eventListenerMap.isEmpty()) |
| return; |
| Vector<AtomicString> eventNames = eventListenerMap.eventTypes(); |
| for (Vector<AtomicString>::iterator it = eventNames.begin(); it != eventNames.end(); ++it) { |
| AtomicString name = *it; |
| EventListenerVector* listeners = eventListenerMap.find(name); |
| int count = 0; |
| for (EventListenerVector::iterator j = listeners->begin(); j != listeners->end(); ++j) { |
| if (j->listener->type() == EventListener::JSEventListenerType) |
| ++count; |
| } |
| if (count) |
| m_eventTypeToCount.set(name, m_eventTypeToCount.get(name) + count); |
| } |
| } |
| |
| int m_totalNodeCount; |
| HashMap<AtomicString, int> m_eventTypeToCount; |
| HashMap<String, int> m_nodeNameToCount; |
| CharacterDataStatistics& m_characterDataStatistics; |
| }; |
| |
| class CounterVisitor : public NodeWrapperVisitor, public ExternalStringVisitor { |
| public: |
| CounterVisitor(Page* page) |
| : m_page(page) |
| , m_domGroups(TypeBuilder::Array<TypeBuilder::Memory::DOMGroup>::create()) |
| , m_jsExternalStringSize(0) |
| , m_sharedStringSize(0) { } |
| |
| TypeBuilder::Array<TypeBuilder::Memory::DOMGroup>* domGroups() { return m_domGroups.get(); } |
| |
| PassRefPtr<StringStatistics> strings() |
| { |
| RefPtr<StringStatistics> stringStatistics = StringStatistics::create() |
| .setDom(m_characterDataStatistics.characterDataSize()) |
| .setJs(m_jsExternalStringSize) |
| .setShared(m_sharedStringSize); |
| return stringStatistics.release(); |
| } |
| |
| virtual void visitNode(Node* node) |
| { |
| if (node->document()->frame() && m_page != node->document()->frame()->page()) |
| return; |
| |
| Node* rootNode = node; |
| while (rootNode->parentNode()) |
| rootNode = rootNode->parentNode(); |
| |
| if (m_roots.contains(rootNode)) |
| return; |
| m_roots.add(rootNode); |
| |
| DOMTreeStatistics domTreeStats(rootNode, m_characterDataStatistics); |
| |
| RefPtr<DOMGroup> domGroup = DOMGroup::create() |
| .setSize(domTreeStats.totalNodeCount()) |
| .setTitle(rootNode->nodeType() == Node::ELEMENT_NODE ? elementTitle(static_cast<Element*>(rootNode)) : rootNode->nodeName()) |
| .setNodeCount(domTreeStats.nodeCount()) |
| .setListenerCount(domTreeStats.listenerCount()); |
| if (rootNode->nodeType() == Node::DOCUMENT_NODE) |
| domGroup->setDocumentURI(static_cast<Document*>(rootNode)->documentURI()); |
| |
| m_domGroups->addItem(domGroup); |
| } |
| |
| virtual void visitJSExternalString(StringImpl* string) |
| { |
| int size = stringSize(string); |
| m_jsExternalStringSize += size; |
| if (m_characterDataStatistics.contains(string)) |
| m_sharedStringSize += size; |
| } |
| |
| private: |
| String elementTitle(Element* element) |
| { |
| StringBuilder result; |
| result.append(nodeName(element)); |
| |
| const AtomicString& idValue = element->getIdAttribute(); |
| String idString; |
| if (!idValue.isNull() && !idValue.isEmpty()) { |
| result.append("#"); |
| result.append(idValue); |
| } |
| |
| HashSet<AtomicString> usedClassNames; |
| if (element->hasClass() && element->isStyledElement()) { |
| const SpaceSplitString& classNamesString = static_cast<StyledElement*>(element)->classNames(); |
| size_t classNameCount = classNamesString.size(); |
| for (size_t i = 0; i < classNameCount; ++i) { |
| const AtomicString& className = classNamesString[i]; |
| if (usedClassNames.contains(className)) |
| continue; |
| usedClassNames.add(className); |
| result.append("."); |
| result.append(className); |
| } |
| } |
| return result.toString(); |
| } |
| |
| HashSet<Node*> m_roots; |
| Page* m_page; |
| RefPtr<TypeBuilder::Array<TypeBuilder::Memory::DOMGroup> > m_domGroups; |
| CharacterDataStatistics m_characterDataStatistics; |
| int m_jsExternalStringSize; |
| int m_sharedStringSize; |
| }; |
| |
| class ExternalResourceVisitor : public ExternalStringVisitor, public ExternalArrayVisitor { |
| public: |
| explicit ExternalResourceVisitor(VisitedObjects& visitedObjects) |
| : m_visitedObjects(visitedObjects) |
| , m_jsExternalStringSize(0) |
| , m_externalArraySize(0) |
| { } |
| |
| size_t externalStringSize() const { return m_jsExternalStringSize; } |
| size_t externalArraySize() const { return m_externalArraySize; } |
| |
| private: |
| virtual void visitJSExternalArray(ArrayBufferView* bufferView) |
| { |
| ArrayBuffer* buffer = bufferView->buffer().get(); |
| if (m_visitedObjects.add(buffer).isNewEntry) |
| m_externalArraySize += buffer->byteLength(); |
| } |
| virtual void visitJSExternalString(StringImpl* string) |
| { |
| if (m_visitedObjects.add(string).isNewEntry) |
| m_jsExternalStringSize += stringSize(string); |
| } |
| |
| VisitedObjects& m_visitedObjects; |
| size_t m_jsExternalStringSize; |
| size_t m_externalArraySize; |
| }; |
| |
| } // namespace |
| |
| InspectorMemoryAgent::~InspectorMemoryAgent() |
| { |
| } |
| |
| void InspectorMemoryAgent::getDOMNodeCount(ErrorString*, RefPtr<TypeBuilder::Array<TypeBuilder::Memory::DOMGroup> >& domGroups, RefPtr<TypeBuilder::Memory::StringStatistics>& strings) |
| { |
| CounterVisitor counterVisitor(m_page); |
| ScriptProfiler::visitNodeWrappers(&counterVisitor); |
| |
| // Make sure all documents reachable from the main frame are accounted. |
| for (Frame* frame = m_page->mainFrame(); frame; frame = frame->tree()->traverseNext()) { |
| if (Document* doc = frame->document()) |
| counterVisitor.visitNode(doc); |
| } |
| |
| ScriptProfiler::visitExternalStrings(&counterVisitor); |
| |
| domGroups = counterVisitor.domGroups(); |
| strings = counterVisitor.strings(); |
| } |
| |
| static PassRefPtr<InspectorMemoryBlock> jsHeapInfo() |
| { |
| size_t usedJSHeapSize; |
| size_t totalJSHeapSize; |
| size_t jsHeapSizeLimit; |
| ScriptGCEvent::getHeapSize(usedJSHeapSize, totalJSHeapSize, jsHeapSizeLimit); |
| |
| RefPtr<InspectorMemoryBlock> jsHeapAllocated = InspectorMemoryBlock::create().setName(MemoryBlockName::jsHeapAllocated); |
| jsHeapAllocated->setSize(totalJSHeapSize); |
| |
| RefPtr<TypeBuilder::Array<InspectorMemoryBlock> > children = TypeBuilder::Array<InspectorMemoryBlock>::create(); |
| RefPtr<InspectorMemoryBlock> jsHeapUsed = InspectorMemoryBlock::create().setName(MemoryBlockName::jsHeapUsed); |
| jsHeapUsed->setSize(usedJSHeapSize); |
| children->addItem(jsHeapUsed); |
| |
| jsHeapAllocated->setChildren(children); |
| return jsHeapAllocated.release(); |
| } |
| |
| static PassRefPtr<InspectorMemoryBlock> inspectorData() |
| { |
| size_t dataSize = ScriptProfiler::profilerSnapshotsSize(); |
| RefPtr<InspectorMemoryBlock> inspectorData = InspectorMemoryBlock::create().setName(MemoryBlockName::inspectorData); |
| inspectorData->setSize(dataSize); |
| return inspectorData.release(); |
| } |
| |
| static PassRefPtr<InspectorMemoryBlock> renderTreeInfo(Page* page) |
| { |
| ArenaSize arenaSize = page->renderTreeSize(); |
| |
| RefPtr<InspectorMemoryBlock> renderTreeAllocated = InspectorMemoryBlock::create().setName(MemoryBlockName::renderTreeAllocated); |
| renderTreeAllocated->setSize(arenaSize.allocated); |
| |
| RefPtr<TypeBuilder::Array<InspectorMemoryBlock> > children = TypeBuilder::Array<InspectorMemoryBlock>::create(); |
| RefPtr<InspectorMemoryBlock> renderTreeUsed = InspectorMemoryBlock::create().setName(MemoryBlockName::renderTreeUsed); |
| renderTreeUsed->setSize(arenaSize.treeSize); |
| children->addItem(renderTreeUsed); |
| |
| renderTreeAllocated->setChildren(children); |
| return renderTreeAllocated.release(); |
| } |
| |
| static void addMemoryBlockFor(TypeBuilder::Array<InspectorMemoryBlock>* array, size_t size, const char* name) |
| { |
| RefPtr<InspectorMemoryBlock> result = InspectorMemoryBlock::create().setName(name); |
| result->setSize(size); |
| array->addItem(result); |
| } |
| |
| namespace { |
| |
| class MemoryInstrumentationImpl : public MemoryInstrumentation { |
| public: |
| explicit MemoryInstrumentationImpl(VisitedObjects& visitedObjects) |
| : m_visitedObjects(visitedObjects) |
| { |
| for (int i = 0; i < LastTypeEntry; ++i) |
| m_totalSizes[i] = 0; |
| } |
| |
| PassRefPtr<InspectorMemoryBlock> dumpStatistics() |
| { |
| size_t totalSize = 0; |
| for (int i = Other; i < LastTypeEntry; ++i) |
| totalSize += m_totalSizes[i]; |
| |
| RefPtr<TypeBuilder::Array<InspectorMemoryBlock> > domChildren = TypeBuilder::Array<InspectorMemoryBlock>::create(); |
| addMemoryBlockFor(domChildren.get(), m_totalSizes[Other], MemoryBlockName::domTreeOther); |
| addMemoryBlockFor(domChildren.get(), m_totalSizes[DOM], MemoryBlockName::domTreeDOM); |
| addMemoryBlockFor(domChildren.get(), m_totalSizes[CSS], MemoryBlockName::domTreeCSS); |
| |
| RefPtr<InspectorMemoryBlock> dom = InspectorMemoryBlock::create().setName(MemoryBlockName::dom); |
| dom->setSize(totalSize); |
| dom->setChildren(domChildren.release()); |
| return dom.release(); |
| } |
| |
| private: |
| virtual void reportString(ObjectType objectType, const String& string) |
| { |
| if (visited(string.impl())) |
| return; |
| countObjectSize(objectType, stringSize(string.impl())); |
| } |
| |
| virtual void countObjectSize(ObjectType objectType, size_t size) |
| { |
| ASSERT(objectType >= 0 && objectType < LastTypeEntry); |
| m_totalSizes[objectType] += size; |
| } |
| |
| virtual bool visited(const void* object) |
| { |
| return !m_visitedObjects.add(object).isNewEntry; |
| } |
| size_t m_totalSizes[LastTypeEntry]; |
| VisitedObjects& m_visitedObjects; |
| }; |
| |
| class DOMTreesIterator : public NodeWrapperVisitor { |
| public: |
| DOMTreesIterator(Page* page, VisitedObjects& visitedObjects) |
| : m_page(page) |
| , m_domMemoryUsage(visitedObjects) |
| { |
| } |
| |
| virtual void visitNode(Node* node) |
| { |
| if (node->document() && node->document()->frame() && m_page != node->document()->frame()->page()) |
| return; |
| |
| m_domMemoryUsage.reportInstrumentedPointer(node); |
| } |
| |
| PassRefPtr<InspectorMemoryBlock> dumpStatistics() { return m_domMemoryUsage.dumpStatistics(); } |
| |
| private: |
| Page* m_page; |
| MemoryInstrumentationImpl m_domMemoryUsage; |
| }; |
| |
| } |
| |
| static PassRefPtr<InspectorMemoryBlock> domTreeInfo(Page* page, VisitedObjects& visitedObjects) |
| { |
| DOMTreesIterator domTreesIterator(page, visitedObjects); |
| ScriptProfiler::visitNodeWrappers(&domTreesIterator); |
| |
| // Make sure all documents reachable from the main frame are accounted. |
| for (Frame* frame = page->mainFrame(); frame; frame = frame->tree()->traverseNext()) { |
| if (Document* doc = frame->document()) |
| domTreesIterator.visitNode(doc); |
| } |
| |
| return domTreesIterator.dumpStatistics(); |
| } |
| |
| static PassRefPtr<InspectorMemoryBlock> memoryCacheInfo() |
| { |
| MemoryCache::Statistics stats = memoryCache()->getStatistics(); |
| int totalSize = stats.images.size + |
| stats.cssStyleSheets.size + |
| stats.scripts.size + |
| stats.xslStyleSheets.size + |
| stats.fonts.size; |
| RefPtr<InspectorMemoryBlock> memoryCacheStats = InspectorMemoryBlock::create().setName(MemoryBlockName::memoryCache); |
| memoryCacheStats->setSize(totalSize); |
| |
| RefPtr<TypeBuilder::Array<InspectorMemoryBlock> > children = TypeBuilder::Array<InspectorMemoryBlock>::create(); |
| addMemoryBlockFor(children.get(), stats.images.size, MemoryBlockName::cachedImages); |
| addMemoryBlockFor(children.get(), stats.cssStyleSheets.size, MemoryBlockName::cachedCssStyleSheets); |
| addMemoryBlockFor(children.get(), stats.scripts.size, MemoryBlockName::cachedScripts); |
| addMemoryBlockFor(children.get(), stats.xslStyleSheets.size, MemoryBlockName::cachedXslStyleSheets); |
| addMemoryBlockFor(children.get(), stats.fonts.size, MemoryBlockName::cachedFonts); |
| memoryCacheStats->setChildren(children.get()); |
| return memoryCacheStats.release(); |
| } |
| |
| static PassRefPtr<InspectorMemoryBlock> jsExternalResourcesInfo(VisitedObjects& visitedObjects) |
| { |
| ExternalResourceVisitor visitor(visitedObjects); |
| ScriptProfiler::visitExternalStrings(&visitor); |
| ScriptProfiler::visitExternalArrays(&visitor); |
| |
| RefPtr<InspectorMemoryBlock> externalResourcesStats = InspectorMemoryBlock::create().setName(MemoryBlockName::jsExternalResources); |
| externalResourcesStats->setSize(visitor.externalStringSize() + visitor.externalArraySize()); |
| RefPtr<TypeBuilder::Array<InspectorMemoryBlock> > children = TypeBuilder::Array<InspectorMemoryBlock>::create(); |
| |
| RefPtr<InspectorMemoryBlock> externalStringStats = InspectorMemoryBlock::create().setName(MemoryBlockName::jsExternalStrings); |
| externalStringStats->setSize(visitor.externalStringSize()); |
| children->addItem(externalStringStats); |
| |
| RefPtr<InspectorMemoryBlock> externalArrayStats = InspectorMemoryBlock::create().setName(MemoryBlockName::jsExternalArrays); |
| externalArrayStats->setSize(visitor.externalArraySize()); |
| children->addItem(externalArrayStats); |
| |
| return externalResourcesStats.release(); |
| } |
| |
| void InspectorMemoryAgent::getProcessMemoryDistribution(ErrorString*, RefPtr<InspectorMemoryBlock>& processMemory) |
| { |
| size_t privateBytes = 0; |
| size_t sharedBytes = 0; |
| MemoryUsageSupport::processMemorySizesInBytes(&privateBytes, &sharedBytes); |
| processMemory = InspectorMemoryBlock::create().setName(MemoryBlockName::processPrivateMemory); |
| processMemory->setSize(privateBytes); |
| |
| VisitedObjects visitedObjects; |
| RefPtr<TypeBuilder::Array<InspectorMemoryBlock> > children = TypeBuilder::Array<InspectorMemoryBlock>::create(); |
| children->addItem(jsHeapInfo()); |
| children->addItem(inspectorData()); |
| children->addItem(memoryCacheInfo()); |
| children->addItem(renderTreeInfo(m_page)); // TODO: collect for all pages? |
| children->addItem(domTreeInfo(m_page, visitedObjects)); // TODO: collect for all pages? |
| children->addItem(jsExternalResourcesInfo(visitedObjects)); |
| processMemory->setChildren(children); |
| } |
| |
| InspectorMemoryAgent::InspectorMemoryAgent(InstrumentingAgents* instrumentingAgents, InspectorState* state, Page* page, InspectorDOMAgent*) |
| : InspectorBaseAgent<InspectorMemoryAgent>("Memory", instrumentingAgents, state) |
| , m_page(page) |
| { |
| } |
| |
| } // namespace WebCore |
| |
| #endif // ENABLE(INSPECTOR) |