Add a debug overlay with information about web process resource usage.
authorakling@apple.com <akling@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 1 Nov 2015 02:29:47 +0000 (02:29 +0000)
committerakling@apple.com <akling@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 1 Nov 2015 02:29:47 +0000 (02:29 +0000)
<https://webkit.org/b/150599>

Reviewed by Darin Adler.

Source/JavaScriptCore:

Have Heap track the exact number of bytes allocated in CopiedBlock, MarkedBlock and
WeakBlock objects, keeping them in a single location that can be sampled by the
resource usage overlay thread.

The bulk of these changes is threading a Heap& through from sites where blocks are
allocated or freed.

* heap/CopiedBlock.cpp:
(JSC::CopiedBlock::createNoZeroFill):
(JSC::CopiedBlock::destroy):
(JSC::CopiedBlock::create):
* heap/CopiedBlock.h:
* heap/CopiedSpace.cpp:
(JSC::CopiedSpace::~CopiedSpace):
(JSC::CopiedSpace::tryAllocateOversize):
(JSC::CopiedSpace::tryReallocateOversize):
* heap/CopiedSpaceInlines.h:
(JSC::CopiedSpace::recycleEvacuatedBlock):
(JSC::CopiedSpace::recycleBorrowedBlock):
(JSC::CopiedSpace::allocateBlockForCopyingPhase):
(JSC::CopiedSpace::allocateBlock):
(JSC::CopiedSpace::startedCopying):
* heap/Heap.cpp:
(JSC::Heap::~Heap):
(JSC::Heap::sweepNextLogicallyEmptyWeakBlock):
* heap/Heap.h:
(JSC::Heap::blockBytesAllocated):
* heap/HeapInlines.h:
(JSC::Heap::didAllocateBlock):
(JSC::Heap::didFreeBlock):
* heap/MarkedAllocator.cpp:
(JSC::MarkedAllocator::allocateBlock):
* heap/MarkedBlock.cpp:
(JSC::MarkedBlock::create):
(JSC::MarkedBlock::destroy):
* heap/MarkedBlock.h:
* heap/MarkedSpace.cpp:
(JSC::MarkedSpace::freeBlock):
* heap/WeakBlock.cpp:
(JSC::WeakBlock::create):
(JSC::WeakBlock::destroy):
* heap/WeakBlock.h:
* heap/WeakSet.cpp:
(JSC::WeakSet::~WeakSet):
(JSC::WeakSet::addAllocator):
(JSC::WeakSet::removeAllocator):

Source/WebCore:

A new kind of PageOverlay is added behind the ENABLE(RESOURCE_USAGE_OVERLAY) flag.
It's owned by Page, but not instantiated unless the Settings::resourceUsageOverlayVisible flag is set.

All ResourceUsageOverlay objects share a single sampler thread. The thread currently runs every 500ms
and samples CPU usage, dirty memory regions, and GC heap size/capacity.

Most things in here are currently quite Mac-specific, but I will be iterating on this towards a more
cross-platform friendly solution.

There are two small changes to PageOverlay in order to support dragging the resource usage overlay:

    - A "should ignore mouse events outside bounds" state flag. This is on by default
      but turned off during a drag.
    - PageOverlay::bounds() will now return the override frame verbatim if one is set,
      instead of returning it relocated to 0,0.

Note that this is intended as a tool for WebKit engine developers to better understand memory usage.
It's not a goal to expose this information to end users.

* WebCore.xcodeproj/project.pbxproj:
* page/Page.cpp:
(WebCore::Page::setResourceUsageOverlayVisible):
* page/Page.h:
* page/PageOverlay.cpp:
(WebCore::PageOverlay::bounds):
(WebCore::PageOverlay::mouseEvent):
* page/PageOverlay.h:
* page/ResourceUsageOverlay.cpp: Added.
(WebCore::ResourceUsageOverlay::ResourceUsageOverlay):
(WebCore::ResourceUsageOverlay::~ResourceUsageOverlay):
(WebCore::ResourceUsageOverlay::mouseEvent):
* page/ResourceUsageOverlay.h: Added.
* page/Settings.cpp:
(WebCore::Settings::setResourceUsageOverlayVisible):
* page/Settings.h:
(WebCore::Settings::resourceUsageOverlayVisible):
* page/cocoa/ResourceUsageOverlayCocoa.mm: Added.
(-[WebOverlayLayer initWithResourceUsageOverlay:]):
(-[WebOverlayLayer drawInContext:]):
(WebCore::RingBuffer::RingBuffer):
(WebCore::RingBuffer::append):
(WebCore::RingBuffer::last):
(WebCore::RingBuffer::forEach):
(WebCore::RingBuffer::incrementIndex):
(WebCore::RingBuffer::decrementIndex):
(WebCore::sharedData):
(WebCore::ResourceUsageOverlay::platformInitialize):
(WebCore::ResourceUsageOverlay::platformDestroy):
(WebCore::drawCpuHistory):
(WebCore::drawGCHistory):
(WebCore::drawSlice):
(WebCore::drawPlate):
(WebCore::drawMemoryPie):
(WebCore::formatByteNumber):
(WebCore::showText):
(WebCore::ResourceUsageOverlay::draw):
(WebCore::dirtyPagesPerVMTag):
(WebCore::cpuUsage):
(WebCore::runSamplerThread):
* platform/spi/cocoa/MachVMSPI.h:

Source/WebKit2:

Add WK2 preferences SPI for showing/hiding the resource usage overlay.

* Shared/WebPreferencesDefinitions.h:
* UIProcess/API/C/WKPreferences.cpp:
(WKPreferencesSetResourceUsageOverlayVisible):
(WKPreferencesGetResourceUsageOverlayVisible):
* UIProcess/API/C/WKPreferencesRefPrivate.h:
* UIProcess/API/Cocoa/WKPreferences.mm:
(-[WKPreferences _resourceUsageOverlayVisible]):
(-[WKPreferences _setResourceUsageOverlayVisible:]):
* UIProcess/API/Cocoa/WKPreferencesPrivate.h:
* WebProcess/WebPage/WebPage.cpp:
(WebKit::WebPage::updatePreferences):

Source/WTF:

Add ENABLE(RESOURCE_USAGE_OVERLAY) flag, enabled on Mac by default.

* wtf/Platform.h:

Tools:

Add a menu item to the MiniBrowser so we can toggle the resource usage overlay on/off.

* MiniBrowser/mac/SettingsController.h:
* MiniBrowser/mac/SettingsController.m:
(-[SettingsController _populateMenu]):
(-[SettingsController validateMenuItem:]):
(-[SettingsController toggleShowResourceUsageOverlay:]):
(-[SettingsController resourceUsageOverlayVisible]):
* MiniBrowser/mac/WK2BrowserWindowController.m:
(-[WK2BrowserWindowController didChangeSettings]):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@191849 268f45cc-cd09-0410-ab3c-d52691b4dbfc

42 files changed:
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/heap/CopiedBlock.cpp
Source/JavaScriptCore/heap/CopiedBlock.h
Source/JavaScriptCore/heap/CopiedSpace.cpp
Source/JavaScriptCore/heap/CopiedSpaceInlines.h
Source/JavaScriptCore/heap/Heap.cpp
Source/JavaScriptCore/heap/Heap.h
Source/JavaScriptCore/heap/HeapInlines.h
Source/JavaScriptCore/heap/MarkedAllocator.cpp
Source/JavaScriptCore/heap/MarkedBlock.cpp
Source/JavaScriptCore/heap/MarkedBlock.h
Source/JavaScriptCore/heap/MarkedSpace.cpp
Source/JavaScriptCore/heap/WeakBlock.cpp
Source/JavaScriptCore/heap/WeakBlock.h
Source/JavaScriptCore/heap/WeakSet.cpp
Source/WTF/ChangeLog
Source/WTF/wtf/Platform.h
Source/WebCore/CMakeLists.txt
Source/WebCore/ChangeLog
Source/WebCore/PlatformMac.cmake
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/page/Page.cpp
Source/WebCore/page/Page.h
Source/WebCore/page/PageOverlay.cpp
Source/WebCore/page/PageOverlay.h
Source/WebCore/page/ResourceUsageOverlay.cpp [new file with mode: 0644]
Source/WebCore/page/ResourceUsageOverlay.h [new file with mode: 0644]
Source/WebCore/page/Settings.cpp
Source/WebCore/page/Settings.h
Source/WebCore/page/cocoa/ResourceUsageOverlayCocoa.mm [new file with mode: 0644]
Source/WebCore/platform/spi/cocoa/MachVMSPI.h
Source/WebKit2/ChangeLog
Source/WebKit2/Shared/WebPreferencesDefinitions.h
Source/WebKit2/UIProcess/API/C/WKPreferences.cpp
Source/WebKit2/UIProcess/API/C/WKPreferencesRefPrivate.h
Source/WebKit2/UIProcess/API/Cocoa/WKPreferences.mm
Source/WebKit2/UIProcess/API/Cocoa/WKPreferencesPrivate.h
Source/WebKit2/WebProcess/WebPage/WebPage.cpp
Tools/ChangeLog
Tools/MiniBrowser/mac/SettingsController.h
Tools/MiniBrowser/mac/SettingsController.m
Tools/MiniBrowser/mac/WK2BrowserWindowController.m

index d198105..c083001 100644 (file)
@@ -1,3 +1,57 @@
+2015-10-31  Andreas Kling  <akling@apple.com>
+
+        Add a debug overlay with information about web process resource usage.
+        <https://webkit.org/b/150599>
+
+        Reviewed by Darin Adler.
+
+        Have Heap track the exact number of bytes allocated in CopiedBlock, MarkedBlock and
+        WeakBlock objects, keeping them in a single location that can be sampled by the
+        resource usage overlay thread.
+
+        The bulk of these changes is threading a Heap& through from sites where blocks are
+        allocated or freed.
+
+        * heap/CopiedBlock.cpp:
+        (JSC::CopiedBlock::createNoZeroFill):
+        (JSC::CopiedBlock::destroy):
+        (JSC::CopiedBlock::create):
+        * heap/CopiedBlock.h:
+        * heap/CopiedSpace.cpp:
+        (JSC::CopiedSpace::~CopiedSpace):
+        (JSC::CopiedSpace::tryAllocateOversize):
+        (JSC::CopiedSpace::tryReallocateOversize):
+        * heap/CopiedSpaceInlines.h:
+        (JSC::CopiedSpace::recycleEvacuatedBlock):
+        (JSC::CopiedSpace::recycleBorrowedBlock):
+        (JSC::CopiedSpace::allocateBlockForCopyingPhase):
+        (JSC::CopiedSpace::allocateBlock):
+        (JSC::CopiedSpace::startedCopying):
+        * heap/Heap.cpp:
+        (JSC::Heap::~Heap):
+        (JSC::Heap::sweepNextLogicallyEmptyWeakBlock):
+        * heap/Heap.h:
+        (JSC::Heap::blockBytesAllocated):
+        * heap/HeapInlines.h:
+        (JSC::Heap::didAllocateBlock):
+        (JSC::Heap::didFreeBlock):
+        * heap/MarkedAllocator.cpp:
+        (JSC::MarkedAllocator::allocateBlock):
+        * heap/MarkedBlock.cpp:
+        (JSC::MarkedBlock::create):
+        (JSC::MarkedBlock::destroy):
+        * heap/MarkedBlock.h:
+        * heap/MarkedSpace.cpp:
+        (JSC::MarkedSpace::freeBlock):
+        * heap/WeakBlock.cpp:
+        (JSC::WeakBlock::create):
+        (JSC::WeakBlock::destroy):
+        * heap/WeakBlock.h:
+        * heap/WeakSet.cpp:
+        (JSC::WeakSet::~WeakSet):
+        (JSC::WeakSet::addAllocator):
+        (JSC::WeakSet::removeAllocator):
+
 2015-10-30  Filip Pizlo  <fpizlo@apple.com>
 
         Air should eliminate dead code
index ad7c4e8..13798a1 100644 (file)
@@ -33,30 +33,34 @@ namespace JSC {
 static const bool computeBalance = false;
 static size_t balance;
 
-CopiedBlock* CopiedBlock::createNoZeroFill(size_t capacity)
+CopiedBlock* CopiedBlock::createNoZeroFill(Heap& heap, size_t capacity)
 {
     if (computeBalance) {
         balance++;
         if (!(balance % 10))
             dataLog("CopiedBlock Balance: ", balance, "\n");
     }
-    return new(NotNull, fastAlignedMalloc(CopiedBlock::blockSize, capacity)) CopiedBlock(capacity);
+    CopiedBlock* block = new (NotNull, fastAlignedMalloc(CopiedBlock::blockSize, capacity)) CopiedBlock(capacity);
+    heap.didAllocateBlock(capacity);
+    return block;
 }
 
-void CopiedBlock::destroy(CopiedBlock* copiedBlock)
+void CopiedBlock::destroy(Heap& heap, CopiedBlock* copiedBlock)
 {
     if (computeBalance) {
         balance--;
         if (!(balance % 10))
             dataLog("CopiedBlock Balance: ", balance, "\n");
     }
+    size_t capacity = copiedBlock->capacity();
     copiedBlock->~CopiedBlock();
     fastAlignedFree(copiedBlock);
+    heap.didFreeBlock(capacity);
 }
 
-CopiedBlock* CopiedBlock::create(size_t capacity)
+CopiedBlock* CopiedBlock::create(Heap& heap, size_t capacity)
 {
-    CopiedBlock* newBlock = createNoZeroFill(capacity);
+    CopiedBlock* newBlock = createNoZeroFill(heap, capacity);
     newBlock->zeroFillWilderness();
     return newBlock;
 }
index 36abc09..dd3b445 100644 (file)
@@ -41,9 +41,9 @@ class CopiedBlock : public DoublyLinkedListNode<CopiedBlock> {
     friend class CopiedSpace;
     friend class CopiedAllocator;
 public:
-    static CopiedBlock* create(size_t = blockSize);
-    static CopiedBlock* createNoZeroFill(size_t = blockSize);
-    static void destroy(CopiedBlock*);
+    static CopiedBlock* create(Heap&, size_t = blockSize);
+    static CopiedBlock* createNoZeroFill(Heap&, size_t = blockSize);
+    static void destroy(Heap&, CopiedBlock*);
 
     void pin();
     bool isPinned();
index a979fb3..47656ed 100644 (file)
@@ -44,22 +44,22 @@ CopiedSpace::CopiedSpace(Heap* heap)
 CopiedSpace::~CopiedSpace()
 {
     while (!m_oldGen.toSpace->isEmpty())
-        CopiedBlock::destroy(m_oldGen.toSpace->removeHead());
+        CopiedBlock::destroy(*heap(), m_oldGen.toSpace->removeHead());
 
     while (!m_oldGen.fromSpace->isEmpty())
-        CopiedBlock::destroy(m_oldGen.fromSpace->removeHead());
+        CopiedBlock::destroy(*heap(), m_oldGen.fromSpace->removeHead());
 
     while (!m_oldGen.oversizeBlocks.isEmpty())
-        CopiedBlock::destroy(m_oldGen.oversizeBlocks.removeHead());
+        CopiedBlock::destroy(*heap(), m_oldGen.oversizeBlocks.removeHead());
 
     while (!m_newGen.toSpace->isEmpty())
-        CopiedBlock::destroy(m_newGen.toSpace->removeHead());
+        CopiedBlock::destroy(*heap(), m_newGen.toSpace->removeHead());
 
     while (!m_newGen.fromSpace->isEmpty())
-        CopiedBlock::destroy(m_newGen.fromSpace->removeHead());
+        CopiedBlock::destroy(*heap(), m_newGen.fromSpace->removeHead());
 
     while (!m_newGen.oversizeBlocks.isEmpty())
-        CopiedBlock::destroy(m_newGen.oversizeBlocks.removeHead());
+        CopiedBlock::destroy(*heap(), m_newGen.oversizeBlocks.removeHead());
 
     ASSERT(m_oldGen.toSpace->isEmpty());
     ASSERT(m_oldGen.fromSpace->isEmpty());
@@ -98,7 +98,7 @@ CheckedBoolean CopiedSpace::tryAllocateOversize(size_t bytes, void** outPtr)
 {
     ASSERT(isOversize(bytes));
     
-    CopiedBlock* block = CopiedBlock::create(WTF::roundUpToMultipleOf<sizeof(double)>(sizeof(CopiedBlock) + bytes));
+    CopiedBlock* block = CopiedBlock::create(*m_heap, WTF::roundUpToMultipleOf<sizeof(double)>(sizeof(CopiedBlock) + bytes));
     m_newGen.oversizeBlocks.push(block);
     m_newGen.blockFilter.add(reinterpret_cast<Bits>(block));
     m_blockSet.add(block);
@@ -164,7 +164,7 @@ CheckedBoolean CopiedSpace::tryReallocateOversize(void** ptr, size_t oldSize, si
         } else
             m_newGen.oversizeBlocks.remove(oldBlock);
         m_blockSet.remove(oldBlock);
-        CopiedBlock::destroy(oldBlock);
+        CopiedBlock::destroy(*heap(), oldBlock);
     }
     
     *ptr = newPtr;
index 3571ce8..6509b07 100644 (file)
@@ -104,12 +104,12 @@ inline void CopiedSpace::recycleEvacuatedBlock(CopiedBlock* block, HeapOperation
         else
             m_oldGen.fromSpace->remove(block);
     }
-    CopiedBlock::destroy(block);
+    CopiedBlock::destroy(*heap(), block);
 }
 
 inline void CopiedSpace::recycleBorrowedBlock(CopiedBlock* block)
 {
-    CopiedBlock::destroy(block);
+    CopiedBlock::destroy(*heap(), block);
 
     {
         LockHolder locker(m_loanedBlocksLock);
@@ -122,7 +122,7 @@ inline void CopiedSpace::recycleBorrowedBlock(CopiedBlock* block)
 inline CopiedBlock* CopiedSpace::allocateBlockForCopyingPhase()
 {
     ASSERT(m_inCopyingPhase);
-    CopiedBlock* block = CopiedBlock::createNoZeroFill();
+    CopiedBlock* block = CopiedBlock::createNoZeroFill(*m_heap);
 
     {
         LockHolder locker(m_loanedBlocksLock);
@@ -139,7 +139,7 @@ inline void CopiedSpace::allocateBlock()
 
     m_allocator.resetCurrentBlock();
     
-    CopiedBlock* block = CopiedBlock::create();
+    CopiedBlock* block = CopiedBlock::create(*m_heap);
         
     m_newGen.toSpace->push(block);
     m_newGen.blockFilter.add(reinterpret_cast<Bits>(block));
@@ -231,7 +231,7 @@ inline void CopiedSpace::startedCopying()
         } else {
             oversizeBlocks->remove(block);
             m_blockSet.remove(block);
-            CopiedBlock::destroy(block);
+            CopiedBlock::destroy(*heap(), block);
         } 
         block = next;
     }
index a7e486c..87e8bb7 100644 (file)
@@ -363,7 +363,7 @@ Heap::Heap(VM* vm, HeapType heapType)
 Heap::~Heap()
 {
     for (WeakBlock* block : m_logicallyEmptyWeakBlocks)
-        WeakBlock::destroy(block);
+        WeakBlock::destroy(*this, block);
 }
 
 bool Heap::isPagedOut(double deadline)
@@ -1572,7 +1572,7 @@ bool Heap::sweepNextLogicallyEmptyWeakBlock()
     if (block->isEmpty()) {
         std::swap(m_logicallyEmptyWeakBlocks[m_indexOfNextLogicallyEmptyWeakBlockToSweep], m_logicallyEmptyWeakBlocks.last());
         m_logicallyEmptyWeakBlocks.removeLast();
-        WeakBlock::destroy(block);
+        WeakBlock::destroy(*this, block);
     } else
         m_indexOfNextLogicallyEmptyWeakBlockToSweep++;
 
index ed2c3c4..207e0b3 100644 (file)
@@ -178,7 +178,7 @@ public:
     JS_EXPORT_PRIVATE void protect(JSValue);
     JS_EXPORT_PRIVATE bool unprotect(JSValue); // True when the protect count drops to 0.
     
-    size_t extraMemorySize(); // Non-GC memory referenced by GC objects.
+    JS_EXPORT_PRIVATE size_t extraMemorySize(); // Non-GC memory referenced by GC objects.
     JS_EXPORT_PRIVATE size_t size();
     JS_EXPORT_PRIVATE size_t capacity();
     JS_EXPORT_PRIVATE size_t objectCount();
@@ -236,6 +236,13 @@ public:
 
     void addLogicallyEmptyWeakBlock(WeakBlock*);
 
+#if ENABLE(RESOURCE_USAGE_OVERLAY)
+    size_t blockBytesAllocated() const { return m_blockBytesAllocated; }
+#endif
+
+    void didAllocateBlock(size_t capacity);
+    void didFreeBlock(size_t capacity);
+
 private:
     friend class CodeBlock;
     friend class CopiedBlock;
@@ -439,6 +446,10 @@ private:
     ListableHandler<UnconditionalFinalizer>::List m_unconditionalFinalizers;
 
     ParallelHelperClient m_helperClient;
+
+#if ENABLE(RESOURCE_USAGE_OVERLAY)
+    size_t m_blockBytesAllocated { 0 };
+#endif
 };
 
 } // namespace JSC
index 21d89b9..f4acf25 100644 (file)
@@ -307,7 +307,25 @@ inline void Heap::unregisterWeakGCMap(void* weakGCMap)
 {
     m_weakGCMaps.remove(weakGCMap);
 }
-    
+
+inline void Heap::didAllocateBlock(size_t capacity)
+{
+#if ENABLE(RESOURCE_USAGE_OVERLAY)
+    m_blockBytesAllocated += capacity;
+#else
+    UNUSED_PARAM(capacity);
+#endif
+}
+
+inline void Heap::didFreeBlock(size_t capacity)
+{
+#if ENABLE(RESOURCE_USAGE_OVERLAY)
+    m_blockBytesAllocated -= capacity;
+#else
+    UNUSED_PARAM(capacity);
+#endif
+}
+
 } // namespace JSC
 
 #endif // HeapInlines_h
index 281fac4..9e3f8ba 100644 (file)
@@ -181,7 +181,7 @@ MarkedBlock* MarkedAllocator::allocateBlock(size_t bytes)
 
     size_t cellSize = m_cellSize ? m_cellSize : WTF::roundUpToMultipleOf<MarkedBlock::atomSize>(bytes);
 
-    return MarkedBlock::create(this, blockSize, cellSize, m_needsDestruction);
+    return MarkedBlock::create(*m_heap, this, blockSize, cellSize, m_needsDestruction);
 }
 
 void MarkedAllocator::addBlock(MarkedBlock* block)
index 42370b2..9123f4e 100644 (file)
@@ -35,25 +35,29 @@ namespace JSC {
 static const bool computeBalance = false;
 static size_t balance;
 
-MarkedBlock* MarkedBlock::create(MarkedAllocator* allocator, size_t capacity, size_t cellSize, bool needsDestruction)
+MarkedBlock* MarkedBlock::create(Heap& heap, MarkedAllocator* allocator, size_t capacity, size_t cellSize, bool needsDestruction)
 {
     if (computeBalance) {
         balance++;
         if (!(balance % 10))
             dataLog("MarkedBlock Balance: ", balance, "\n");
     }
-    return new (NotNull, fastAlignedMalloc(blockSize, capacity)) MarkedBlock(allocator, capacity, cellSize, needsDestruction);
+    MarkedBlock* block = new (NotNull, fastAlignedMalloc(blockSize, capacity)) MarkedBlock(allocator, capacity, cellSize, needsDestruction);
+    heap.didAllocateBlock(capacity);
+    return block;
 }
 
-void MarkedBlock::destroy(MarkedBlock* block)
+void MarkedBlock::destroy(Heap& heap, MarkedBlock* block)
 {
     if (computeBalance) {
         balance--;
         if (!(balance % 10))
             dataLog("MarkedBlock Balance: ", balance, "\n");
     }
+    size_t capacity = block->capacity();
     block->~MarkedBlock();
     fastAlignedFree(block);
+    heap.didFreeBlock(capacity);
 }
 
 MarkedBlock::MarkedBlock(MarkedAllocator* allocator, size_t capacity, size_t cellSize, bool needsDestruction)
index c1a0dbb..d5b1492 100644 (file)
@@ -106,8 +106,8 @@ namespace JSC {
             ReturnType m_count;
         };
 
-        static MarkedBlock* create(MarkedAllocator*, size_t capacity, size_t cellSize, bool needsDestruction);
-        static void destroy(MarkedBlock*);
+        static MarkedBlock* create(Heap&, MarkedAllocator*, size_t capacity, size_t cellSize, bool needsDestruction);
+        static void destroy(Heap&, MarkedBlock*);
 
         static bool isAtomAligned(const void*);
         static MarkedBlock* blockFor(const void*);
index 00adc5a..7e71743 100644 (file)
@@ -209,7 +209,7 @@ void MarkedSpace::freeBlock(MarkedBlock* block)
     block->allocator()->removeBlock(block);
     m_capacity -= block->capacity();
     m_blocks.remove(block);
-    MarkedBlock::destroy(block);
+    MarkedBlock::destroy(*m_heap, block);
 }
 
 void MarkedSpace::freeOrShrinkBlock(MarkedBlock* block)
index ef1dad3..4b2ae66 100644 (file)
 
 namespace JSC {
 
-WeakBlock* WeakBlock::create(MarkedBlock& markedBlock)
+WeakBlock* WeakBlock::create(Heap& heap, MarkedBlock& markedBlock)
 {
+    heap.didAllocateBlock(WeakBlock::blockSize);
     return new (NotNull, fastMalloc(blockSize)) WeakBlock(markedBlock);
 }
 
-void WeakBlock::destroy(WeakBlock* block)
+void WeakBlock::destroy(Heap& heap, WeakBlock* block)
 {
     block->~WeakBlock();
     fastFree(block);
+    heap.didFreeBlock(WeakBlock::blockSize);
 }
 
 WeakBlock::WeakBlock(MarkedBlock& markedBlock)
index 9482606..f5fbfdc 100644 (file)
@@ -32,6 +32,7 @@
 
 namespace JSC {
 
+class Heap;
 class HeapRootVisitor;
 class MarkedBlock;
 
@@ -52,8 +53,8 @@ public:
         FreeCell* freeList { nullptr };
     };
 
-    static WeakBlock* create(MarkedBlock&);
-    static void destroy(WeakBlock*);
+    static WeakBlock* create(Heap&, MarkedBlock&);
+    static void destroy(Heap&, WeakBlock*);
 
     static WeakImpl* asWeakImpl(FreeCell*);
 
index 1eed8c3..8624993 100644 (file)
@@ -34,10 +34,11 @@ namespace JSC {
 
 WeakSet::~WeakSet()
 {
+    Heap& heap = *this->heap();
     WeakBlock* next = 0;
     for (WeakBlock* block = m_blocks.head(); block; block = next) {
         next = block->next();
-        WeakBlock::destroy(block);
+        WeakBlock::destroy(heap, block);
     }
     m_blocks.clear();
 }
@@ -85,7 +86,7 @@ WeakBlock::FreeCell* WeakSet::tryFindAllocator()
 
 WeakBlock::FreeCell* WeakSet::addAllocator()
 {
-    WeakBlock* block = WeakBlock::create(m_markedBlock);
+    WeakBlock* block = WeakBlock::create(*heap(), m_markedBlock);
     heap()->didAllocate(WeakBlock::blockSize);
     m_blocks.append(block);
     WeakBlock::SweepResult sweepResult = block->takeSweepResult();
@@ -96,7 +97,7 @@ WeakBlock::FreeCell* WeakSet::addAllocator()
 void WeakSet::removeAllocator(WeakBlock* block)
 {
     m_blocks.remove(block);
-    WeakBlock::destroy(block);
+    WeakBlock::destroy(*heap(), block);
 }
 
 } // namespace JSC
index dc03493..2898e92 100644 (file)
@@ -1,3 +1,14 @@
+2015-10-31  Andreas Kling  <akling@apple.com>
+
+        Add a debug overlay with information about web process resource usage.
+        <https://webkit.org/b/150599>
+
+        Reviewed by Darin Adler.
+
+        Add ENABLE(RESOURCE_USAGE_OVERLAY) flag, enabled on Mac by default.
+
+        * wtf/Platform.h:
+
 2015-10-30  Chris Dumez  <cdumez@apple.com>
 
         Regression(r191673): Crash in RunLoopTimer::schedule()
index d9fd437..9ad7dc7 100644 (file)
 #define USE_IOSURFACE 1
 #endif
 
+#if PLATFORM(MAC)
+#define ENABLE_RESOURCE_USAGE_OVERLAY 1
+#endif
+
 #if PLATFORM(GTK) || PLATFORM(EFL)
 #undef ENABLE_OPENTYPE_VERTICAL
 #define ENABLE_OPENTYPE_VERTICAL 1
index 08e8f53..caadb95 100644 (file)
@@ -2045,6 +2045,7 @@ set(WebCore_SOURCES
     page/PerformanceUserTiming.cpp
     page/PointerLockController.cpp
     page/PrintContext.cpp
+    page/ResourceUsageOverlay.cpp
     page/Screen.cpp
     page/SecurityOrigin.cpp
     page/SecurityOriginData.cpp
index 26ba3ee..887cc55 100644 (file)
@@ -1,3 +1,71 @@
+2015-10-31  Andreas Kling  <akling@apple.com>
+
+        Add a debug overlay with information about web process resource usage.
+        <https://webkit.org/b/150599>
+
+        Reviewed by Darin Adler.
+
+        A new kind of PageOverlay is added behind the ENABLE(RESOURCE_USAGE_OVERLAY) flag.
+        It's owned by Page, but not instantiated unless the Settings::resourceUsageOverlayVisible flag is set.
+
+        All ResourceUsageOverlay objects share a single sampler thread. The thread currently runs every 500ms
+        and samples CPU usage, dirty memory regions, and GC heap size/capacity.
+
+        Most things in here are currently quite Mac-specific, but I will be iterating on this towards a more
+        cross-platform friendly solution.
+
+        There are two small changes to PageOverlay in order to support dragging the resource usage overlay:
+
+            - A "should ignore mouse events outside bounds" state flag. This is on by default
+              but turned off during a drag.
+            - PageOverlay::bounds() will now return the override frame verbatim if one is set,
+              instead of returning it relocated to 0,0.
+
+        Note that this is intended as a tool for WebKit engine developers to better understand memory usage.
+        It's not a goal to expose this information to end users.
+
+        * WebCore.xcodeproj/project.pbxproj:
+        * page/Page.cpp:
+        (WebCore::Page::setResourceUsageOverlayVisible):
+        * page/Page.h:
+        * page/PageOverlay.cpp:
+        (WebCore::PageOverlay::bounds):
+        (WebCore::PageOverlay::mouseEvent):
+        * page/PageOverlay.h:
+        * page/ResourceUsageOverlay.cpp: Added.
+        (WebCore::ResourceUsageOverlay::ResourceUsageOverlay):
+        (WebCore::ResourceUsageOverlay::~ResourceUsageOverlay):
+        (WebCore::ResourceUsageOverlay::mouseEvent):
+        * page/ResourceUsageOverlay.h: Added.
+        * page/Settings.cpp:
+        (WebCore::Settings::setResourceUsageOverlayVisible):
+        * page/Settings.h:
+        (WebCore::Settings::resourceUsageOverlayVisible):
+        * page/cocoa/ResourceUsageOverlayCocoa.mm: Added.
+        (-[WebOverlayLayer initWithResourceUsageOverlay:]):
+        (-[WebOverlayLayer drawInContext:]):
+        (WebCore::RingBuffer::RingBuffer):
+        (WebCore::RingBuffer::append):
+        (WebCore::RingBuffer::last):
+        (WebCore::RingBuffer::forEach):
+        (WebCore::RingBuffer::incrementIndex):
+        (WebCore::RingBuffer::decrementIndex):
+        (WebCore::sharedData):
+        (WebCore::ResourceUsageOverlay::platformInitialize):
+        (WebCore::ResourceUsageOverlay::platformDestroy):
+        (WebCore::drawCpuHistory):
+        (WebCore::drawGCHistory):
+        (WebCore::drawSlice):
+        (WebCore::drawPlate):
+        (WebCore::drawMemoryPie):
+        (WebCore::formatByteNumber):
+        (WebCore::showText):
+        (WebCore::ResourceUsageOverlay::draw):
+        (WebCore::dirtyPagesPerVMTag):
+        (WebCore::cpuUsage):
+        (WebCore::runSamplerThread):
+        * platform/spi/cocoa/MachVMSPI.h:
+
 2015-10-31  Brady Eidson  <beidson@apple.com>
 
         storage/indexeddb/modern/idbdatabase-deleteobjectstore-failures.html is flaky.
index ce5583f..5c206f0 100644 (file)
@@ -269,6 +269,7 @@ list(APPEND WebCore_SOURCES
     page/PageDebuggable.cpp
 
     page/cocoa/UserAgent.mm
+    page/cocoa/ResourceUsageOverlayCocoa.mm
 
     page/mac/ChromeMac.mm
     page/mac/DragControllerMac.mm
index 22af8ac..ef793e1 100644 (file)
                AD726FEF16DA11F5003A4E6D /* JSCSSRuleCustom.h in Headers */ = {isa = PBXBuildFile; fileRef = AD726FE916D9F40A003A4E6D /* JSCSSRuleCustom.h */; settings = {ATTRIBUTES = (Private, ); }; };
                AD9FF6E11908391D003B61E0 /* IOSurfacePoolCocoa.mm in Sources */ = {isa = PBXBuildFile; fileRef = AD9FF6E01908391D003B61E0 /* IOSurfacePoolCocoa.mm */; };
                ADB6B29818FB90240081963E /* MemoryPressureHandlerCocoa.mm in Sources */ = {isa = PBXBuildFile; fileRef = ADB6B29718FB90240081963E /* MemoryPressureHandlerCocoa.mm */; };
+               ADBAD6EE1BCDD95500381325 /* ResourceUsageOverlay.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ADBAD6EC1BCDD95000381325 /* ResourceUsageOverlay.cpp */; };
+               ADBAD6EF1BCDD95700381325 /* ResourceUsageOverlay.h in Headers */ = {isa = PBXBuildFile; fileRef = ADBAD6ED1BCDD95000381325 /* ResourceUsageOverlay.h */; };
                ADDA94C219687AA500453029 /* JSDocumentCustom.h in Headers */ = {isa = PBXBuildFile; fileRef = ADDA94BF19686F8000453029 /* JSDocumentCustom.h */; settings = {ATTRIBUTES = (Private, ); }; };
                ADDF1AD71257CD9A0003A759 /* RenderSVGPath.h in Headers */ = {isa = PBXBuildFile; fileRef = ADDF1AD51257CD9A0003A759 /* RenderSVGPath.h */; };
                ADEC78F818EE5308001315C2 /* JSElementCustom.h in Headers */ = {isa = PBXBuildFile; fileRef = ADEC78F718EE5308001315C2 /* JSElementCustom.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               ADFE2B551BD5F61200DAB457 /* ResourceUsageOverlayCocoa.mm in Sources */ = {isa = PBXBuildFile; fileRef = ADFE2B541BD5F41200DAB457 /* ResourceUsageOverlayCocoa.mm */; };
                B10B6980140C174000BC1C26 /* WebVTTToken.h in Headers */ = {isa = PBXBuildFile; fileRef = B10B697D140C174000BC1C26 /* WebVTTToken.h */; };
                B10B6981140C174000BC1C26 /* WebVTTTokenizer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B10B697E140C174000BC1C26 /* WebVTTTokenizer.cpp */; };
                B10B6982140C174000BC1C26 /* WebVTTTokenizer.h in Headers */ = {isa = PBXBuildFile; fileRef = B10B697F140C174000BC1C26 /* WebVTTTokenizer.h */; };
                AD726FEC16D9F4B9003A4E6D /* JSStyleSheetCustom.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSStyleSheetCustom.h; sourceTree = "<group>"; };
                AD9FF6E01908391D003B61E0 /* IOSurfacePoolCocoa.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = IOSurfacePoolCocoa.mm; sourceTree = "<group>"; };
                ADB6B29718FB90240081963E /* MemoryPressureHandlerCocoa.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MemoryPressureHandlerCocoa.mm; sourceTree = "<group>"; };
+               ADBAD6EC1BCDD95000381325 /* ResourceUsageOverlay.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ResourceUsageOverlay.cpp; sourceTree = "<group>"; };
+               ADBAD6ED1BCDD95000381325 /* ResourceUsageOverlay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ResourceUsageOverlay.h; sourceTree = "<group>"; };
                ADDA94BF19686F8000453029 /* JSDocumentCustom.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSDocumentCustom.h; sourceTree = "<group>"; };
                ADDF1AD41257CD9A0003A759 /* RenderSVGPath.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RenderSVGPath.cpp; sourceTree = "<group>"; };
                ADDF1AD51257CD9A0003A759 /* RenderSVGPath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RenderSVGPath.h; sourceTree = "<group>"; };
                ADE11F4A18D8311B0078983B /* ElementDescendantIterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ElementDescendantIterator.h; sourceTree = "<group>"; };
                ADE16736181050C300463A2E /* RenderPtr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RenderPtr.h; sourceTree = "<group>"; };
                ADEC78F718EE5308001315C2 /* JSElementCustom.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSElementCustom.h; sourceTree = "<group>"; };
+               ADFE2B541BD5F41200DAB457 /* ResourceUsageOverlayCocoa.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ResourceUsageOverlayCocoa.mm; sourceTree = "<group>"; };
                B10B697D140C174000BC1C26 /* WebVTTToken.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebVTTToken.h; sourceTree = "<group>"; };
                B10B697E140C174000BC1C26 /* WebVTTTokenizer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WebVTTTokenizer.cpp; sourceTree = "<group>"; };
                B10B697F140C174000BC1C26 /* WebVTTTokenizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebVTTTokenizer.h; sourceTree = "<group>"; };
                        runOnlyForDeploymentPostprocessing = 0;
                };
 /* End PBXFrameworksBuildPhase section */
-
 /* Begin PBXGroup section */
                00B9318013BA867F0035A948 /* parser */ = {
                        isa = PBXGroup;
                5D5975B5196362BE00D00878 /* cocoa */ = {
                        isa = PBXGroup;
                        children = (
+                               ADFE2B541BD5F41200DAB457 /* ResourceUsageOverlayCocoa.mm */,
                                26255F0118878E110006E1FD /* UserAgent.h */,
                                5D5975B61963637B00D00878 /* UserAgent.mm */,
                        );
                65BF02180974806300C43196 /* page */ = {
                        isa = PBXGroup;
                        children = (
+                               ADBAD6EC1BCDD95000381325 /* ResourceUsageOverlay.cpp */,
+                               ADBAD6ED1BCDD95000381325 /* ResourceUsageOverlay.h */,
                                316FE1060E6E1D8400BF6088 /* animation */,
                                5D5975B5196362BE00D00878 /* cocoa */,
                                18A6CD6F0D8F2025001DC3CE /* ios */,
                                854FE7370A2297BE0058D7AD /* Traversal.h in Headers */,
                                37FD4298118368460093C029 /* TreeDepthLimit.h in Headers */,
                                14D64B5D134A5B6B00E58FDA /* TreeScope.h in Headers */,
+                               ADBAD6EF1BCDD95700381325 /* ResourceUsageOverlay.h in Headers */,
                                A77E1FF014AACB6E005B7CB6 /* TreeScopeAdopter.h in Headers */,
                                854FE7390A2297BE0058D7AD /* TreeWalker.h in Headers */,
                                2D232C001A326F02006BF2DB /* TUCallSPI.h in Headers */,
                                BC46C2060C0DDCA10020CFC3 /* JSCSSStyleRule.cpp in Sources */,
                                BCC5BE000C0E93110011C2DB /* JSCSSStyleSheet.cpp in Sources */,
                                FD67773A195CB14A0072E0D3 /* JSCSSSupportsRule.cpp in Sources */,
+                               ADFE2B551BD5F61200DAB457 /* ResourceUsageOverlayCocoa.mm in Sources */,
                                14CF78A409F58CBF00EB3665 /* JSCSSValue.cpp in Sources */,
                                BC20FB7F0C0E8E6C00D1447F /* JSCSSValueCustom.cpp in Sources */,
                                A8D05FAB0A23B30F005E7203 /* JSCSSValueList.cpp in Sources */,
                                A5A2AF0B1829734300DE1729 /* PageDebuggable.cpp in Sources */,
                                F34742DC134362F000531BC2 /* PageDebuggerAgent.cpp in Sources */,
                                9302B0BD0D79F82900C7EE83 /* PageGroup.cpp in Sources */,
+                               ADBAD6EE1BCDD95500381325 /* ResourceUsageOverlay.cpp in Sources */,
                                7A674BDB0F9EBF4E006CF099 /* PageGroupLoadDeferrer.cpp in Sources */,
                                2D5C9CFF19C7B52E00B3C5C1 /* PageOverlay.cpp in Sources */,
                                2D5C9D0119C7B52E00B3C5C1 /* PageOverlayController.cpp in Sources */,
index c4bf31e..89f7ef1 100644 (file)
@@ -73,6 +73,7 @@
 #include "RenderTheme.h"
 #include "RenderView.h"
 #include "RenderWidget.h"
+#include "ResourceUsageOverlay.h"
 #include "RuntimeEnabledFeatures.h"
 #include "SchemeRegistry.h"
 #include "ScriptController.h"
@@ -1782,4 +1783,17 @@ IDBClient::IDBConnectionToServer& Page::idbConnection()
 }
 #endif
 
+#if ENABLE(RESOURCE_USAGE_OVERLAY)
+void Page::setResourceUsageOverlayVisible(bool visible)
+{
+    if (!visible) {
+        m_resourceUsageOverlay = nullptr;
+        return;
+    }
+
+    if (!m_resourceUsageOverlay)
+        m_resourceUsageOverlay = std::make_unique<ResourceUsageOverlay>(*this);
+}
+#endif
+
 } // namespace WebCore
index d757c7a..598836d 100644 (file)
@@ -109,6 +109,7 @@ class Range;
 class RenderObject;
 class RenderTheme;
 class ReplayController;
+class ResourceUsageOverlay;
 class VisibleSelection;
 class ScrollableArea;
 class ScrollingCoordinator;
@@ -333,6 +334,10 @@ public:
     void dnsPrefetchingStateChanged();
     void storageBlockingStateChanged();
 
+#if ENABLE(RESOURCE_USAGE_OVERLAY)
+    void setResourceUsageOverlayVisible(bool);
+#endif
+
     void setDebugger(JSC::Debugger*);
     JSC::Debugger* debugger() const { return m_debugger; }
 
@@ -631,6 +636,10 @@ private:
 
     HashSet<ViewStateChangeObserver*> m_viewStateChangeObservers;
 
+#if ENABLE(RESOURCE_USAGE_OVERLAY)
+    std::unique_ptr<ResourceUsageOverlay> m_resourceUsageOverlay;
+#endif
+
     SessionID m_sessionID;
 
     bool m_isClosing;
index e56600b..9ecea84 100644 (file)
@@ -79,7 +79,7 @@ PageOverlayController* PageOverlay::controller() const
 IntRect PageOverlay::bounds() const
 {
     if (!m_overrideFrame.isEmpty())
-        return IntRect(IntPoint(), m_overrideFrame.size());
+        return m_overrideFrame;
 
     FrameView* frameView = m_page->mainFrame().view();
 
@@ -192,7 +192,7 @@ bool PageOverlay::mouseEvent(const PlatformMouseEvent& mouseEvent)
         mousePositionInOverlayCoordinates = m_page->mainFrame().view()->windowToContents(mousePositionInOverlayCoordinates);
 
     // Ignore events outside the bounds.
-    if (!bounds().contains(mousePositionInOverlayCoordinates))
+    if (m_shouldIgnoreMouseEventsOutsideBounds && !bounds().contains(mousePositionInOverlayCoordinates))
         return false;
 
     return m_client.mouseEvent(*this, mouseEvent);
index f723650..7fc1dd1 100644 (file)
@@ -110,6 +110,8 @@ public:
     RGBA32 backgroundColor() const { return m_backgroundColor; }
     void setBackgroundColor(RGBA32);
 
+    void setShouldIgnoreMouseEventsOutsideBounds(bool flag) { m_shouldIgnoreMouseEventsOutsideBounds = flag; }
+
     // FIXME: PageOverlay should own its layer, instead of PageOverlayController.
     WEBCORE_EXPORT GraphicsLayer& layer();
 
@@ -140,6 +142,8 @@ private:
 
     RGBA32 m_backgroundColor;
     PageOverlayID m_pageOverlayID;
+
+    bool m_shouldIgnoreMouseEventsOutsideBounds { true };
 };
 
 } // namespace WebKit
diff --git a/Source/WebCore/page/ResourceUsageOverlay.cpp b/Source/WebCore/page/ResourceUsageOverlay.cpp
new file mode 100644 (file)
index 0000000..dc650aa
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2015 Apple 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:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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(RESOURCE_USAGE_OVERLAY)
+
+#include "ResourceUsageOverlay.h"
+
+#include "FrameView.h"
+#include "PageOverlayController.h"
+#include "PlatformMouseEvent.h"
+
+namespace WebCore {
+
+ResourceUsageOverlay::ResourceUsageOverlay(Page& page)
+    : m_page(page)
+    , m_overlay(PageOverlay::create(*this, PageOverlay::OverlayType::View))
+{
+    m_overlay->setFrame(IntRect(80, 80, 500, 120));
+    m_overlay->setShouldIgnoreMouseEventsOutsideBounds(false);
+
+    m_page.mainFrame().pageOverlayController().installPageOverlay(m_overlay.get(), PageOverlay::FadeMode::DoNotFade);
+
+    platformInitialize();
+}
+
+ResourceUsageOverlay::~ResourceUsageOverlay()
+{
+    platformDestroy();
+}
+
+bool ResourceUsageOverlay::mouseEvent(PageOverlay&, const PlatformMouseEvent& event)
+{
+    if (event.button() != LeftButton)
+        return false;
+
+    switch (event.type()) {
+    case PlatformEvent::MousePressed: {
+        m_overlay->setShouldIgnoreMouseEventsOutsideBounds(false);
+        m_dragging = true;
+        IntPoint location = m_overlay->frame().location();
+        m_dragPoint = event.position() + IntPoint(-location.x(), -location.y());
+        return true;
+    }
+    case PlatformEvent::MouseReleased:
+        if (m_dragging) {
+            m_overlay->setShouldIgnoreMouseEventsOutsideBounds(true);
+            m_dragging = false;
+            return true;
+        }
+        break;
+    case PlatformEvent::MouseMoved:
+        if (m_dragging) {
+            IntRect newFrame = m_overlay->frame();
+
+            // Move the new frame relative to the point where the drag was initiated.
+            newFrame.setLocation(event.position());
+            newFrame.moveBy(IntPoint(-m_dragPoint.x(), -m_dragPoint.y()));
+
+            // Force the frame to stay inside the viewport entirely.
+            if (newFrame.x() < 0)
+                newFrame.setX(0);
+            if (newFrame.y() < m_page.topContentInset())
+                newFrame.setY(m_page.topContentInset());
+            FrameView& frameView = *m_page.mainFrame().view();
+            if (newFrame.maxX() > frameView.width())
+                newFrame.setX(frameView.width() - newFrame.width());
+            if (newFrame.maxY() > frameView.height())
+                newFrame.setY(frameView.height() - newFrame.height());
+
+            m_overlay->setFrame(newFrame);
+            m_overlay->setNeedsDisplay();
+            return true;
+        }
+        break;
+    default:
+        break;
+    }
+    return false;
+}
+
+}
+
+#endif
diff --git a/Source/WebCore/page/ResourceUsageOverlay.h b/Source/WebCore/page/ResourceUsageOverlay.h
new file mode 100644 (file)
index 0000000..89e4b63
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2015 Apple 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:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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.
+ */
+
+#ifndef ResourceUsageOverlay_h
+#define ResourceUsageOverlay_h
+
+#if ENABLE(RESOURCE_USAGE_OVERLAY)
+
+#include "FloatRect.h"
+#include "IntRect.h"
+#include "MainFrame.h"
+#include "PageOverlay.h"
+#include <wtf/Noncopyable.h>
+#include <wtf/RetainPtr.h>
+
+#if PLATFORM(COCOA)
+#include "PlatformCALayer.h"
+#endif
+
+namespace WebCore {
+
+class FloatRect;
+class IntPoint;
+class IntRect;
+
+class ResourceUsageOverlay final : public PageOverlay::Client {
+    WTF_MAKE_FAST_ALLOCATED;
+    WTF_MAKE_NONCOPYABLE(ResourceUsageOverlay);
+public:
+    explicit ResourceUsageOverlay(Page&);
+    ~ResourceUsageOverlay();
+
+    PageOverlay& overlay() { return *m_overlay; }
+
+    void draw(GraphicsContext&);
+
+private:
+    void pageOverlayDestroyed(PageOverlay&) override { }
+    void willMoveToPage(PageOverlay&, Page*) override { }
+    void didMoveToPage(PageOverlay&, Page*) override { }
+    void drawRect(PageOverlay&, GraphicsContext&, const IntRect&) override { }
+    bool mouseEvent(PageOverlay&, const PlatformMouseEvent&) override;
+    void didScrollFrame(PageOverlay&, Frame&) override { }
+
+    void platformInitialize();
+    void platformDestroy();
+
+    Page& m_page;
+    RefPtr<PageOverlay> m_overlay;
+    bool m_dragging { false };
+    IntPoint m_dragPoint;
+
+#if PLATFORM(COCOA)
+    ThreadIdentifier m_threadID { 0 };
+    RetainPtr<CALayer> m_layer;
+#endif
+};
+
+}
+
+#endif
+
+#endif
index 4086619..b109503 100644 (file)
@@ -531,6 +531,18 @@ void Settings::setShowTiledScrollingIndicator(bool enabled)
     m_showTiledScrollingIndicator = enabled;
 }
 
+#if ENABLE(RESOURCE_USAGE_OVERLAY)
+void Settings::setResourceUsageOverlayVisible(bool visible)
+{
+    if (m_resourceUsageOverlayVisible == visible)
+        return;
+
+    m_resourceUsageOverlayVisible = visible;
+    if (m_page)
+        m_page->setResourceUsageOverlayVisible(visible);
+}
+#endif
+
 #if PLATFORM(WIN)
 void Settings::setShouldUseHighResolutionTimers(bool shouldUseHighResolutionTimers)
 {
index cf7b04f..936eae1 100644 (file)
@@ -189,6 +189,11 @@ public:
     WEBCORE_EXPORT void setShowTiledScrollingIndicator(bool);
     bool showTiledScrollingIndicator() const { return m_showTiledScrollingIndicator; }
 
+#if ENABLE(RESOURCE_USAGE_OVERLAY)
+    bool resourceUsageOverlayVisible() const { return m_resourceUsageOverlayVisible; }
+    WEBCORE_EXPORT void setResourceUsageOverlayVisible(bool);
+#endif
+
 #if PLATFORM(WIN)
     static void setShouldUseHighResolutionTimers(bool);
     static bool shouldUseHighResolutionTimers() { return gShouldUseHighResolutionTimers; }
@@ -333,6 +338,10 @@ private:
 
     bool m_forcePendingWebGLPolicy : 1;
 
+#if ENABLE(RESOURCE_USAGE_OVERLAY)
+    bool m_resourceUsageOverlayVisible { false };
+#endif
+
 #if USE(AVFOUNDATION)
     WEBCORE_EXPORT static bool gAVFoundationEnabled;
 #endif
diff --git a/Source/WebCore/page/cocoa/ResourceUsageOverlayCocoa.mm b/Source/WebCore/page/cocoa/ResourceUsageOverlayCocoa.mm
new file mode 100644 (file)
index 0000000..d126aa7
--- /dev/null
@@ -0,0 +1,474 @@
+/*
+ * Copyright (C) 2015 Apple 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:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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"
+#include "ResourceUsageOverlay.h"
+
+#if ENABLE(RESOURCE_USAGE_OVERLAY)
+
+#include "GraphicsContext.h"
+#include "JSDOMWindow.h"
+#include "MachVMSPI.h"
+#include "PlatformCALayer.h"
+#include <QuartzCore/CALayer.h>
+#include <QuartzCore/CATransaction.h>
+#include <array>
+#include <mach/mach.h>
+#include <mach/vm_statistics.h>
+#include <runtime/JSLock.h>
+#include <thread>
+#include <wtf/MathExtras.h>
+#include <wtf/NeverDestroyed.h>
+
+using namespace WebCore;
+
+@interface WebOverlayLayer : CALayer {
+    ResourceUsageOverlay* m_overlay;
+}
+@end
+
+@implementation WebOverlayLayer
+
+- (WebOverlayLayer *)initWithResourceUsageOverlay:(ResourceUsageOverlay *)overlay
+{
+    self = [super init];
+    if (!self)
+        return nil;
+    m_overlay = overlay;
+    return self;
+}
+
+- (void)drawInContext:(CGContextRef)context
+{
+    GraphicsContext gc(context);
+    m_overlay->draw(gc);
+}
+
+@end
+
+namespace WebCore {
+
+static const RGBA32 colorForJITCode    = 0xFFFF60FF;
+static const RGBA32 colorForImages     = 0xFFFFFF00;
+static const RGBA32 colorForLayers     = 0xFF00FFFF;
+static const RGBA32 colorForGCHeap     = 0xFFA0A0FF;
+static const RGBA32 colorForLibcMalloc = 0xFF00FF00;
+static const RGBA32 colorForFastMalloc = 0xFFFF6060;
+static const RGBA32 colorForOther      = 0xFFC0FF00;
+static const RGBA32 colorForLabels     = 0xFFE0E0E0;
+
+template<typename T, size_t size = 50>
+class RingBuffer {
+public:
+    RingBuffer()
+    {
+        m_data.fill(0);
+    }
+
+    void append(T v)
+    {
+        m_data[m_current] = WTF::move(v);
+        incrementIndex(m_current);
+    }
+
+    T last() const
+    {
+        unsigned index = m_current;
+        decrementIndex(index);
+        return m_data[index];
+    }
+
+    void forEach(std::function<void(T)> func) const
+    {
+        unsigned i = m_current;
+        for (unsigned visited = 0; visited < size; ++visited) {
+            func(m_data[i]);
+            incrementIndex(i);
+        }
+    }
+
+private:
+    static void incrementIndex(unsigned& index)
+    {
+        if (++index == size)
+            index = 0;
+    }
+
+    static void decrementIndex(unsigned& index)
+    {
+        if (index)
+            --index;
+        else
+            index = size - 1;
+    }
+
+    std::array<T, size> m_data;
+    unsigned m_current { 0 };
+};
+
+struct ResourceUsageData {
+    Lock lock;
+    RingBuffer<float> cpuHistory;
+    RingBuffer<size_t> gcHeapSizeHistory;
+    RingBuffer<size_t> gcHeapCapacityHistory;
+    size_t layers { 0 };
+    size_t images { 0 };
+    size_t jitCode { 0 };
+    size_t libcMalloc { 0 };
+    size_t bmalloc { 0 };
+    size_t sumDirty { 0 };
+
+    HashSet<CALayer *> overlayLayers;
+    JSC::VM* vm { nullptr };
+};
+
+static ResourceUsageData& sharedData()
+{
+    static NeverDestroyed<ResourceUsageData> data;
+    return data;
+}
+
+void runSamplerThread(void*);
+
+void ResourceUsageOverlay::platformInitialize()
+{
+    auto& data = sharedData();
+    LockHolder locker(data.lock);
+
+    // FIXME: The sampler thread will never stop once started.
+    static std::once_flag onceFlag;
+    std::call_once(onceFlag, [&] {
+        data.vm = &JSDOMWindow::commonVM();
+        createThread(runSamplerThread, nullptr, "ResourceUsageOverlay Sampler");
+    });
+
+    m_layer = adoptNS([[WebOverlayLayer alloc] initWithResourceUsageOverlay:this]);
+
+    [overlay().layer().platformLayer() addSublayer:m_layer.get()];
+
+    [m_layer.get() setContentsScale:2.0];
+    [m_layer.get() setBackgroundColor:adoptCF(CGColorCreateGenericRGB(0, 0, 0, 0.8)).get()];
+    [m_layer.get() setFrame:CGRectMake(0, 0, 500, 120)];
+
+    data.overlayLayers.add(m_layer.get());
+}
+
+void ResourceUsageOverlay::platformDestroy()
+{
+    auto& data = sharedData();
+    LockHolder locker(data.lock);
+    data.overlayLayers.remove(m_layer.get());
+}
+
+static void drawCpuHistory(GraphicsContext& gc, float x1, float y1, float y2, RingBuffer<float>& history)
+{
+    Ref<Gradient> gradient = Gradient::create(FloatPoint(0, y1), FloatPoint(0, y2));
+    gradient->addColorStop(0.0, Color(0, 255, 0));
+    gradient->addColorStop(0.5, Color(255, 255, 0));
+    gradient->addColorStop(1.0, Color(255, 0, 0));
+    gc.setStrokeGradient(WTF::move(gradient));
+    gc.setStrokeThickness(1);
+
+    int i = 0;
+
+    history.forEach([&](float c) {
+        float cpu = c / 100;
+        float yScale = y2 - y1;
+
+        Path path;
+        path.moveTo(FloatPoint(x1 + i, y1));
+        path.addLineTo(FloatPoint(x1 + i, y1 + (yScale * cpu)));
+        gc.strokePath(path);
+        i++;
+    });
+}
+
+static void drawGCHistory(GraphicsContext& gc, float x1, float y1, float y2, RingBuffer<size_t>& sizeHistory, RingBuffer<size_t>& capacityHistory)
+{
+    size_t peak = 0;
+    capacityHistory.forEach([&](size_t m) {
+        if (m > peak)
+            peak = m;
+    });
+
+    gc.setStrokeThickness(1);
+
+    Ref<Gradient> capacityGradient = Gradient::create(FloatPoint(0, y1), FloatPoint(0, y2));
+    capacityGradient->addColorStop(0.0, Color(0xCC, 0x00, 0x33));
+    capacityGradient->addColorStop(0.5, Color(0xFF, 0x00, 0x33));
+    capacityGradient->addColorStop(1.0, Color(0xFF, 0x00, 0x00));
+    gc.setStrokeGradient(WTF::move(capacityGradient));
+
+    size_t i = 0;
+
+    capacityHistory.forEach([&](size_t m) {
+        float mem = (float)m / (float)peak;
+        float yScale = y2 - y1;
+
+        Path path;
+        path.moveTo(FloatPoint(x1 + i, y1));
+        path.addLineTo(FloatPoint(x1 + i, y1 + (yScale * mem)));
+        gc.strokePath(path);
+        i++;
+    });
+
+    Ref<Gradient> sizeGradient = Gradient::create(FloatPoint(0, y1), FloatPoint(0, y2));
+    sizeGradient->addColorStop(0.0, Color(0x29, 0x56, 0x8F));
+    sizeGradient->addColorStop(0.5, Color(0x47, 0x71, 0xA5));
+    sizeGradient->addColorStop(1.0, Color(0x96, 0xB1, 0xD2));
+    gc.setStrokeGradient(WTF::move(sizeGradient));
+
+    i = 0;
+
+    sizeHistory.forEach([&](size_t m) {
+        float mem = (float)m / (float)peak;
+        float yScale = y2 - y1;
+
+        Path path;
+        path.moveTo(FloatPoint(x1 + i, y1));
+        path.addLineTo(FloatPoint(x1 + i, y1 + (yScale * mem)));
+        gc.strokePath(path);
+        i++;
+    });
+}
+
+static const float fullCircleInRadians = piFloat * 2;
+
+static void drawSlice(GraphicsContext& context, FloatPoint center, float& angle, size_t sliceSize, size_t totalSize, Color color)
+{
+    Path path;
+    path.moveTo(center);
+    float part = (float)sliceSize / (float)totalSize;
+    path.addArc(center, 30, angle, angle + part * fullCircleInRadians, false);
+    context.setFillColor(color, ColorSpaceDeviceRGB);
+    context.fillPath(path);
+    angle += part * fullCircleInRadians;
+}
+
+static void drawPlate(GraphicsContext& context, FloatPoint center, float& angle, Color color)
+{
+    Path path;
+    path.moveTo(center);
+    path.addArc(center, 30, angle, fullCircleInRadians, false);
+    context.setFillColor(color, ColorSpaceDeviceRGB);
+    context.fillPath(path);
+}
+
+static void drawMemoryPie(GraphicsContext& context, float x, float y, ResourceUsageData& data)
+{
+    GraphicsContextStateSaver saver(context);
+
+    context.setShouldAntialias(true);
+
+    FloatPoint center(x - 15, y + 60);
+
+    size_t bmallocWithDeductions = data.bmalloc - data.gcHeapCapacityHistory.last();
+
+    float angle = 0;
+    drawSlice(context, center, angle, bmallocWithDeductions, data.sumDirty, colorForFastMalloc);
+    drawSlice(context, center, angle, data.libcMalloc, data.sumDirty, colorForLibcMalloc);
+    drawSlice(context, center, angle, data.gcHeapCapacityHistory.last(), data.sumDirty, colorForGCHeap);
+    drawSlice(context, center, angle, data.layers, data.sumDirty, colorForLayers);
+    drawSlice(context, center, angle, data.images, data.sumDirty, colorForImages);
+    drawSlice(context, center, angle, data.jitCode, data.sumDirty, colorForJITCode);
+    drawPlate(context, center, angle, colorForOther);
+}
+
+static String formatByteNumber(size_t number)
+{
+    if (number >= 1024 * 1048576)
+        return String::format("%.3f GB", static_cast<double>(number) / 1024 * 1048576);
+    if (number >= 1048576)
+        return String::format("%.2f MB", static_cast<double>(number) / 1048576);
+    if (number >= 1024)
+        return String::format("%.1f kB", static_cast<double>(number) / 1024);
+    return String::format("%lu", number);
+}
+
+// FIXME: All of this should be done without using CGContext directly, so the code can be cross-platform.
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+
+static void showText(GraphicsContext& gc, float x, float y, Color color, const String& text)
+{
+    gc.setFillColor(color, ColorSpaceDeviceRGB);
+    CString cstr = text.ascii();
+    CGContextShowTextAtPoint(gc.platformContext(), x, y, cstr.data(), cstr.length());
+}
+
+void ResourceUsageOverlay::draw(GraphicsContext& context)
+{
+    auto& data = sharedData();
+    LockHolder locker(data.lock);
+
+    size_t gcHeapSize = data.gcHeapSizeHistory.last();
+    size_t gcHeapCapacity = data.gcHeapCapacityHistory.last();
+
+    context.setShouldAntialias(false);
+    context.setShouldSmoothFonts(false);
+
+    context.clearRect(m_overlay->bounds());
+    CGContextRef ctx = context.platformContext();
+    CGRect viewBounds = m_overlay->bounds();
+
+    context.translate(0, viewBounds.size.height);
+    context.scale(FloatSize(1, -1));
+
+    {
+    GraphicsContextStateSaver saver(context);
+
+    CGContextSetLineWidth(ctx, 2.0);
+    CGContextSelectFont(ctx, "Menlo", 11.0, kCGEncodingMacRoman);
+    CGContextSetCharacterSpacing(ctx, 1.7);
+    CGContextSetTextDrawingMode(ctx, kCGTextFill);
+
+    size_t bmallocWithDeductions = data.bmalloc - gcHeapCapacity;
+    size_t footprintWithDeductions = data.sumDirty - data.bmalloc - data.layers - data.images - data.libcMalloc - data.jitCode;
+
+    showText(context, 10, 10, colorForOther,      "      Other: " + formatByteNumber(footprintWithDeductions));
+    showText(context, 10, 20, colorForGCHeap,     "    GC heap: " + formatByteNumber(gcHeapSize) + " (" + formatByteNumber(gcHeapCapacity) + ")");
+    showText(context, 10, 30, colorForJITCode,    "     JS JIT: " + formatByteNumber(data.jitCode));
+    showText(context, 10, 40, colorForLayers,     "     Layers: " + formatByteNumber(data.layers));
+    showText(context, 10, 50, colorForImages,     "     Images: " + formatByteNumber(data.images));
+    showText(context, 10, 60, colorForLibcMalloc, "libc malloc: " + formatByteNumber(data.libcMalloc));
+    showText(context, 10, 70, colorForFastMalloc, "    bmalloc: " + formatByteNumber(bmallocWithDeductions));
+    
+    showText(context, 10, 90, colorForLabels,     "  Footprint: " + formatByteNumber(data.sumDirty));
+    showText(context, 10, 100, colorForLabels, String::format("        CPU: %g", data.cpuHistory.last()));
+    }
+
+    drawCpuHistory(context, m_overlay->frame().width() - 50, 0, viewBounds.size.height, data.cpuHistory);
+    drawGCHistory(context, m_overlay->frame().width() - 100, 0, viewBounds.size.height, data.gcHeapSizeHistory, data.gcHeapCapacityHistory);
+    drawMemoryPie(context, m_overlay->frame().width() - 150, 0, data);
+}
+
+#pragma clang diagnostic pop
+
+static std::array<size_t, 256> dirtyPagesPerVMTag()
+{
+    std::array<size_t, 256> dirty;
+    dirty.fill(0);
+    task_t task = mach_task_self();
+    mach_vm_size_t size;
+    uint32_t depth = 0;
+    struct vm_region_submap_info_64 info = { };
+    mach_msg_type_number_t count = VM_REGION_SUBMAP_INFO_COUNT_64;
+    for (mach_vm_address_t addr = 0; ; addr += size) {
+        kern_return_t kr = mach_vm_region_recurse(task, &addr, &size, &depth, (vm_region_info_t)&info, &count);
+        if (kr != KERN_SUCCESS)
+            break;
+        dirty[info.user_tag] += info.pages_dirtied;
+    }
+    return dirty;
+}
+
+static float cpuUsage()
+{
+    thread_array_t threadList;
+    mach_msg_type_number_t threadCount;
+    kern_return_t kr = task_threads(mach_task_self(), &threadList, &threadCount);
+    if (kr != KERN_SUCCESS)
+        return -1;
+
+    float usage = 0;
+
+    for (mach_msg_type_number_t i = 0; i < threadCount; ++i) {
+        thread_info_data_t threadInfo;
+        thread_basic_info_t threadBasicInfo;
+
+        mach_msg_type_number_t threadInfoCount = THREAD_INFO_MAX;
+        kr = thread_info(threadList[i], THREAD_BASIC_INFO, static_cast<thread_info_t>(threadInfo), &threadInfoCount);
+        if (kr != KERN_SUCCESS)
+            return -1;
+
+        threadBasicInfo = reinterpret_cast<thread_basic_info_t>(threadInfo);
+
+        if (!(threadBasicInfo->flags & TH_FLAGS_IDLE))
+            usage += threadBasicInfo->cpu_usage / static_cast<float>(TH_USAGE_SCALE) * 100.0;
+    }
+
+    kr = vm_deallocate(mach_task_self(), (vm_offset_t)threadList, threadCount * sizeof(thread_t));
+    ASSERT(kr == KERN_SUCCESS);
+
+    return usage;
+}
+
+NO_RETURN void runSamplerThread(void*)
+{
+    static size_t vmPageSize = getpagesize();
+    auto& data = sharedData();
+    while (1) {
+        float cpu = cpuUsage();
+        auto dirtyPages = dirtyPagesPerVMTag();
+        Vector<CALayer *, 8> layers;
+
+        {
+            LockHolder locker(data.lock);
+            data.cpuHistory.append(cpu);
+            data.layers = (dirtyPages[VM_MEMORY_IOKIT] + dirtyPages[VM_MEMORY_LAYERKIT]) * vmPageSize;
+            data.images = (dirtyPages[VM_MEMORY_IMAGEIO] + dirtyPages[VM_MEMORY_CGIMAGE]) * vmPageSize;
+            data.jitCode = dirtyPages[VM_MEMORY_JAVASCRIPT_JIT_EXECUTABLE_ALLOCATOR] * vmPageSize;
+            data.libcMalloc = vmPageSize *
+                (dirtyPages[VM_MEMORY_MALLOC]
+                + dirtyPages[VM_MEMORY_MALLOC_HUGE]
+                + dirtyPages[VM_MEMORY_MALLOC_LARGE]
+                + dirtyPages[VM_MEMORY_MALLOC_SMALL]
+                + dirtyPages[VM_MEMORY_MALLOC_TINY]
+                + dirtyPages[VM_MEMORY_MALLOC_NANO]);
+            data.bmalloc = vmPageSize * dirtyPages[VM_MEMORY_TCMALLOC];
+
+            data.sumDirty = 0;
+            for (auto dirty : dirtyPages)
+                data.sumDirty += dirty;
+            data.sumDirty *= vmPageSize;
+
+            copyToVector(data.overlayLayers, layers);
+
+            data.gcHeapCapacityHistory.append(data.vm->heap.blockBytesAllocated());
+        }
+
+        [CATransaction begin];
+        for (CALayer *layer : layers)
+            [layer setNeedsDisplay];
+        [CATransaction commit];
+
+        // FIXME: Find a way to get the size of the current GC heap size safely from the sampler thread.
+        callOnMainThread([] {
+            auto& data = sharedData();
+            JSC::JSLockHolder lock(data.vm);
+            size_t gcHeapSize = data.vm->heap.size() - data.vm->heap.extraMemorySize();
+
+            LockHolder locker(data.lock);
+            data.gcHeapSizeHistory.append(gcHeapSize);
+        });
+
+        std::this_thread::sleep_for(std::chrono::milliseconds(500));
+    }
+}
+
+}
+
+#endif
index 17901b1..0360783 100644 (file)
@@ -41,5 +41,6 @@ EXTERN_C kern_return_t mach_vm_map(vm_map_t targetTask, mach_vm_address_t*, mach
 EXTERN_C kern_return_t mach_vm_protect(vm_map_t targetTask, mach_vm_address_t, mach_vm_size_t, boolean_t setMaximum, vm_prot_t newProtection);
 EXTERN_C kern_return_t mach_vm_region(vm_map_t targetTask, mach_vm_address_t*, mach_vm_size_t*, vm_region_flavor_t, vm_region_info_t,
                                       mach_msg_type_number_t* infoCount, mach_port_t* objectName);
+EXTERN_C kern_return_t mach_vm_region_recurse(vm_map_t targetTask, mach_vm_address_t*, mach_vm_size_t*, uint32_t* depth, vm_region_recurse_info_t, mach_msg_type_number_t* infoCount);
 
 #endif // MachVMSPI_h
index b15a52a..15f87d6 100644 (file)
@@ -1,3 +1,24 @@
+2015-10-31  Andreas Kling  <akling@apple.com>
+
+        Add a debug overlay with information about web process resource usage.
+        <https://webkit.org/b/150599>
+
+        Reviewed by Darin Adler.
+
+        Add WK2 preferences SPI for showing/hiding the resource usage overlay.
+
+        * Shared/WebPreferencesDefinitions.h:
+        * UIProcess/API/C/WKPreferences.cpp:
+        (WKPreferencesSetResourceUsageOverlayVisible):
+        (WKPreferencesGetResourceUsageOverlayVisible):
+        * UIProcess/API/C/WKPreferencesRefPrivate.h:
+        * UIProcess/API/Cocoa/WKPreferences.mm:
+        (-[WKPreferences _resourceUsageOverlayVisible]):
+        (-[WKPreferences _setResourceUsageOverlayVisible:]):
+        * UIProcess/API/Cocoa/WKPreferencesPrivate.h:
+        * WebProcess/WebPage/WebPage.cpp:
+        (WebKit::WebPage::updatePreferences):
+
 2015-10-31  Alex Christensen  <achristensen@webkit.org>
 
         Share more code between NETWORK_SESSION and non-NETWORK_SESSION NetworkResourceLoaders
index fed00e1..1c08ad2 100644 (file)
     macro(LogsPageMessagesToSystemConsoleEnabled, logsPageMessagesToSystemConsoleEnabled, Bool, bool, false) \
     macro(IgnoreViewportScalingConstraints, ignoreViewportScalingConstraints, Bool, bool, true) \
     macro(ForceAlwaysUserScalable, forceAlwaysUserScalable, Bool, bool, false) \
+    macro(ResourceUsageOverlayVisible, resourceUsageOverlayVisible, Bool, bool, false) \
     \
 
 #define FOR_EACH_WEBKIT_DEBUG_UINT32_PREFERENCE(macro) \
index 7996b1d..7ff030f 100644 (file)
@@ -1406,3 +1406,14 @@ bool WKPreferencesGetAllowsAirPlayForMediaPlayback(WKPreferencesRef preferencesR
 {
     return toImpl(preferencesRef)->allowsAirPlayForMediaPlayback();
 }
+
+void WKPreferencesSetResourceUsageOverlayVisible(WKPreferencesRef preferencesRef, bool javaEnabled)
+{
+    toImpl(preferencesRef)->setResourceUsageOverlayVisible(javaEnabled);
+}
+
+bool WKPreferencesGetResourceUsageOverlayVisible(WKPreferencesRef preferencesRef)
+{
+    return toImpl(preferencesRef)->resourceUsageOverlayVisible();
+}
+
index bb027e5..a6939aa 100644 (file)
@@ -383,6 +383,10 @@ WK_EXPORT bool WKPreferencesGetMetaRefreshEnabled(WKPreferencesRef preferences);
 WK_EXPORT void WKPreferencesSetHTTPEquivEnabled(WKPreferencesRef preferences, bool enabled);
 WK_EXPORT bool WKPreferencesGetHTTPEquivEnabled(WKPreferencesRef preferences);
 
+// Defaults to false.
+WK_EXPORT void WKPreferencesSetResourceUsageOverlayVisible(WKPreferencesRef, bool);
+WK_EXPORT bool WKPreferencesGetResourceUsageOverlayVisible(WKPreferencesRef);
+
 #ifdef __cplusplus
 }
 #endif
index 4c8dae0..fd88cca 100644 (file)
@@ -217,6 +217,16 @@ static _WKStorageBlockingPolicy toAPI(WebCore::SecurityOrigin::StorageBlockingPo
     _preferences->setTiledScrollingIndicatorVisible(tiledScrollingIndicatorVisible);
 }
 
+- (BOOL)_resourceUsageOverlayVisible
+{
+    return _preferences->resourceUsageOverlayVisible();
+}
+
+- (void)_setResourceUsageOverlayVisible:(BOOL)resourceUsageOverlayVisible
+{
+    _preferences->setResourceUsageOverlayVisible(resourceUsageOverlayVisible);
+}
+
 - (_WKDebugOverlayRegions)_visibleDebugOverlayRegions
 {
     return _preferences->visibleDebugOverlayRegions();
index 73584ca..2b7d4f7 100644 (file)
@@ -55,6 +55,7 @@ typedef NS_OPTIONS(NSUInteger, _WKJavaScriptRuntimeFlags) {
 @property (nonatomic, setter=_setCompositingBordersVisible:) BOOL _compositingBordersVisible;
 @property (nonatomic, setter=_setCompositingRepaintCountersVisible:) BOOL _compositingRepaintCountersVisible;
 @property (nonatomic, setter=_setTiledScrollingIndicatorVisible:) BOOL _tiledScrollingIndicatorVisible;
+@property (nonatomic, setter=_setResourceUsageOverlayVisible:) BOOL _resourceUsageOverlayVisible WK_AVAILABLE(WK_MAC_TBA, WK_IOS_TBA);
 @property (nonatomic, setter=_setVisibleDebugOverlayRegions:) _WKDebugOverlayRegions _visibleDebugOverlayRegions WK_AVAILABLE(10_11, 9_0);
 @property (nonatomic, setter=_setSimpleLineLayoutDebugBordersEnabled:) BOOL _simpleLineLayoutDebugBordersEnabled WK_AVAILABLE(10_11, 9_0);
 @property (nonatomic, setter=_setAcceleratedDrawingEnabled:) BOOL _acceleratedDrawingEnabled WK_AVAILABLE(WK_MAC_TBA, WK_IOS_TBA);
index 9baa8bc..107244d 100644 (file)
@@ -2824,6 +2824,10 @@ void WebPage::updatePreferences(const WebPreferencesStore& store)
     settings.setAllowsAirPlayForMediaPlayback(store.getBoolValueForKey(WebPreferencesKey::allowsAirPlayForMediaPlaybackKey()));
 #endif
 
+#if ENABLE(RESOURCE_USAGE_OVERLAY)
+    settings.setResourceUsageOverlayVisible(store.getBoolValueForKey(WebPreferencesKey::resourceUsageOverlayVisibleKey()));
+#endif
+
     settings.setSuppressesIncrementalRendering(store.getBoolValueForKey(WebPreferencesKey::suppressesIncrementalRenderingKey()));
     settings.setIncrementalRenderingSuppressionTimeoutInSeconds(store.getDoubleValueForKey(WebPreferencesKey::incrementalRenderingSuppressionTimeoutKey()));
     settings.setBackspaceKeyNavigationEnabled(store.getBoolValueForKey(WebPreferencesKey::backspaceKeyNavigationEnabledKey()));
index c2a176f..bd25311 100644 (file)
@@ -1,3 +1,21 @@
+2015-10-31  Andreas Kling  <akling@apple.com>
+
+        Add a debug overlay with information about web process resource usage.
+        <https://webkit.org/b/150599>
+
+        Reviewed by Darin Adler.
+
+        Add a menu item to the MiniBrowser so we can toggle the resource usage overlay on/off.
+
+        * MiniBrowser/mac/SettingsController.h:
+        * MiniBrowser/mac/SettingsController.m:
+        (-[SettingsController _populateMenu]):
+        (-[SettingsController validateMenuItem:]):
+        (-[SettingsController toggleShowResourceUsageOverlay:]):
+        (-[SettingsController resourceUsageOverlayVisible]):
+        * MiniBrowser/mac/WK2BrowserWindowController.m:
+        (-[WK2BrowserWindowController didChangeSettings]):
+
 2015-10-31  Lucas Forschler  <lforschler@apple.com>
 
         Teach the CompileWebKit step to look for additional arguments.
index e5687e6..59b0e54 100644 (file)
@@ -40,6 +40,7 @@
 @property (nonatomic, readonly) BOOL simpleLineLayoutDebugBordersEnabled;
 @property (nonatomic, readonly) BOOL incrementalRenderingSuppressed;
 @property (nonatomic, readonly) BOOL tiledScrollingIndicatorVisible;
+@property (nonatomic, readonly) BOOL resourceUsageOverlayVisible;
 @property (nonatomic, readonly) BOOL nonFastScrollableRegionOverlayVisible;
 @property (nonatomic, readonly) BOOL wheelEventHandlerRegionOverlayVisible;
 @property (nonatomic, readonly) BOOL useUISideCompositing;
index 87652c6..b078ffa 100644 (file)
@@ -35,6 +35,7 @@ static NSString * const UseWebKit2ByDefaultPreferenceKey = @"UseWebKit2ByDefault
 static NSString * const LayerBordersVisiblePreferenceKey = @"LayerBordersVisible";
 static NSString * const SimpleLineLayoutDebugBordersEnabledPreferenceKey = @"SimpleLineLayoutDebugBordersEnabled";
 static NSString * const TiledScrollingIndicatorVisiblePreferenceKey = @"TiledScrollingIndicatorVisible";
+static NSString * const ResourceUsageOverlayVisiblePreferenceKey = @"ResourceUsageOverlayVisible";
 static NSString * const IncrementalRenderingSuppressedPreferenceKey = @"IncrementalRenderingSuppressed";
 static NSString * const AcceleratedDrawingEnabledPreferenceKey = @"AcceleratedDrawingEnabled";
 
@@ -115,6 +116,7 @@ typedef NS_ENUM(NSInteger, DebugOverylayMenuItemTag) {
     [self _addItemWithTitle:@"Show Tiled Scrolling Indicator" action:@selector(toggleShowTiledScrollingIndicator:) indented:YES];
     [self _addItemWithTitle:@"Use UI-Side Compositing" action:@selector(toggleUseUISideCompositing:) indented:YES];
     [self _addItemWithTitle:@"Disable Per-Window Web Processes" action:@selector(togglePerWindowWebProcessesDisabled:) indented:YES];
+    [self _addItemWithTitle:@"Show Resource Usage Overlay" action:@selector(toggleShowResourceUsageOverlay:) indented:YES];
 
     NSMenuItem *debugOverlaysSubmenuItem = [[NSMenuItem alloc] initWithTitle:@"Debug Overlays" action:nil keyEquivalent:@""];
     NSMenu *debugOverlaysMenu = [[NSMenu alloc] initWithTitle:@"Debug Overlays"];
@@ -158,6 +160,8 @@ typedef NS_ENUM(NSInteger, DebugOverylayMenuItemTag) {
         [menuItem setState:[self acceleratedDrawingEnabled] ? NSOnState : NSOffState];
     else if (action == @selector(toggleShowTiledScrollingIndicator:))
         [menuItem setState:[self tiledScrollingIndicatorVisible] ? NSOnState : NSOffState];
+    else if (action == @selector(toggleShowResourceUsageOverlay:))
+        [menuItem setState:[self resourceUsageOverlayVisible] ? NSOnState : NSOffState];
     else if (action == @selector(toggleUseUISideCompositing:))
         [menuItem setState:[self useUISideCompositing] ? NSOnState : NSOffState];
     else if (action == @selector(togglePerWindowWebProcessesDisabled:))
@@ -283,11 +287,21 @@ typedef NS_ENUM(NSInteger, DebugOverylayMenuItemTag) {
     [self _toggleBooleanDefault:TiledScrollingIndicatorVisiblePreferenceKey];
 }
 
+- (void)toggleShowResourceUsageOverlay:(id)sender
+{
+    [self _toggleBooleanDefault:ResourceUsageOverlayVisiblePreferenceKey];
+}
+
 - (BOOL)tiledScrollingIndicatorVisible
 {
     return [[NSUserDefaults standardUserDefaults] boolForKey:TiledScrollingIndicatorVisiblePreferenceKey];
 }
 
+- (BOOL)resourceUsageOverlayVisible
+{
+    return [[NSUserDefaults standardUserDefaults] boolForKey:ResourceUsageOverlayVisiblePreferenceKey];
+}
+
 - (void)toggleEnableSubPixelCSSOMMetrics:(id)sender
 {
     [self _toggleBooleanDefault:EnableSubPixelCSSOMMetricsPreferenceKey];
index eeff99a..04e30d1 100644 (file)
@@ -342,6 +342,7 @@ static CGFloat viewScaleForMenuItemTag(NSInteger tag)
     preferences._compositingRepaintCountersVisible = settings.layerBordersVisible;
     preferences._simpleLineLayoutDebugBordersEnabled = settings.simpleLineLayoutDebugBordersEnabled;
     preferences._acceleratedDrawingEnabled = settings.acceleratedDrawingEnabled;
+    preferences._resourceUsageOverlayVisible = settings.resourceUsageOverlayVisible;
 
     BOOL useTransparentWindows = settings.useTransparentWindows;
     if (useTransparentWindows != _webView._drawsTransparentBackground) {