Implement per activity state CPU usage reporting using diagnostic logging
authorcdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 19 Jan 2017 21:33:36 +0000 (21:33 +0000)
committercdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 19 Jan 2017 21:33:36 +0000 (21:33 +0000)
https://bugs.webkit.org/show_bug.cgi?id=167163
<rdar://problem/30058349>

Reviewed by Andreas Kling.

Implement per activity state CPU usage reporting using diagnostic logging.

Source/WebCore:

* WebCore.xcodeproj/project.pbxproj:
* page/ActivityState.h:
* page/ChromeClient.h:
* page/DiagnosticLoggingKeys.cpp:
(WebCore::DiagnosticLoggingKeys::nonVisibleStateKey):
(WebCore::DiagnosticLoggingKeys::visibleNonActiveStateKey):
(WebCore::DiagnosticLoggingKeys::visibleAndActiveStateKey):
(WebCore::DiagnosticLoggingKeys::foregroundCPUUsageToDiagnosticLogginKey):
(WebCore::DiagnosticLoggingKeys::backgroundCPUUsageToDiagnosticLogginKey):
* page/DiagnosticLoggingKeys.h:
* page/Page.cpp:
(WebCore::isUtilityPageChromeClient):
(WebCore::Page::Page):
(WebCore::Page::~Page):
(WebCore::Page::didStartProvisionalLoad):
(WebCore::Page::didFinishLoad):
(WebCore::Page::isUtilityPage):
(WebCore::Page::setActivityState):
(WebCore::Page::setIsVisibleInternal):
* page/Page.h:
(WebCore::Page::activityState):
(WebCore::Page::isUtilityPage):
* page/PerformanceMonitor.cpp: Added.
(WebCore::activityStateForCPUSampling):
(WebCore::PerformanceMonitor::PerformanceMonitor):
(WebCore::PerformanceMonitor::didStartProvisionalLoad):
(WebCore::PerformanceMonitor::didFinishLoad):
(WebCore::PerformanceMonitor::activityStateChanged):
(WebCore::PerformanceMonitor::measurePostLoadCPUUsage):
(WebCore::PerformanceMonitor::measurePostBackgroundingCPUUsage):
(WebCore::PerformanceMonitor::measurePerActivityStateCPUUsage):
(WebCore::stringForCPUSamplingActivityState):
(WebCore::PerformanceMonitor::measureCPUUsageInActivityState):
* page/PerformanceMonitor.h: Copied from Source/WebCore/page/ActivityState.h.
* page/Settings.cpp:
* page/Settings.h:
(WebCore::Settings::isPostLoadCPUUsageMeasurementEnabled):
(WebCore::Settings::isPostBackgroundingCPUUsageMeasurementEnabled):
(WebCore::Settings::isPerActivityStateCPUUsageMeasurementEnabled):

Source/WebKit2:

* Platform/Logging.h:
* UIProcess/PerActivityStateCPUUsageSampler.cpp: Added.
(WebKit::PerActivityStateCPUUsageSampler::PerActivityStateCPUUsageSampler):
(WebKit::PerActivityStateCPUUsageSampler::~PerActivityStateCPUUsageSampler):
(WebKit::PerActivityStateCPUUsageSampler::reportWebContentCPUTime):
(WebKit::loggingKeyForActivityState):
(WebKit::loggingKeyForCPUUsage):
(WebKit::PerActivityStateCPUUsageSampler::loggingTimerFired):
(WebKit::PerActivityStateCPUUsageSampler::pageForLogging):
* UIProcess/PerActivityStateCPUUsageSampler.h: Copied from Source/WebCore/page/ActivityState.h.
* UIProcess/WebProcessPool.cpp:
(WebKit::WebProcessPool::WebProcessPool):
(WebKit::WebProcessPool::reportWebContentCPUTime):
* UIProcess/WebProcessPool.h:
* UIProcess/WebProcessPool.messages.in:
* WebKit2.xcodeproj/project.pbxproj:
* WebProcess/WebCoreSupport/WebChromeClient.cpp:
(WebKit::WebChromeClient::reportProcessCPUTime):
* WebProcess/WebCoreSupport/WebChromeClient.h:

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

23 files changed:
Source/WebCore/CMakeLists.txt
Source/WebCore/ChangeLog
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/page/ActivityState.h
Source/WebCore/page/ChromeClient.h
Source/WebCore/page/DiagnosticLoggingKeys.cpp
Source/WebCore/page/DiagnosticLoggingKeys.h
Source/WebCore/page/Page.cpp
Source/WebCore/page/Page.h
Source/WebCore/page/PerformanceMonitor.cpp [new file with mode: 0644]
Source/WebCore/page/PerformanceMonitor.h [new file with mode: 0644]
Source/WebCore/page/Settings.cpp
Source/WebCore/page/Settings.h
Source/WebKit2/ChangeLog
Source/WebKit2/Platform/Logging.h
Source/WebKit2/UIProcess/PerActivityStateCPUUsageSampler.cpp [new file with mode: 0644]
Source/WebKit2/UIProcess/PerActivityStateCPUUsageSampler.h [new file with mode: 0644]
Source/WebKit2/UIProcess/WebProcessPool.cpp
Source/WebKit2/UIProcess/WebProcessPool.h
Source/WebKit2/UIProcess/WebProcessPool.messages.in
Source/WebKit2/WebKit2.xcodeproj/project.pbxproj
Source/WebKit2/WebProcess/WebCoreSupport/WebChromeClient.cpp
Source/WebKit2/WebProcess/WebCoreSupport/WebChromeClient.h

index 1e2466b..b5043c5 100644 (file)
@@ -1986,6 +1986,7 @@ set(WebCore_SOURCES
     page/Performance.cpp
     page/PerformanceEntry.cpp
     page/PerformanceLogging.cpp
+    page/PerformanceMonitor.cpp
     page/PerformanceNavigation.cpp
     page/PerformanceResourceTiming.cpp
     page/PerformanceTiming.cpp
index 24cc17d..070eb90 100644 (file)
@@ -1,3 +1,53 @@
+2017-01-19  Chris Dumez  <cdumez@apple.com>
+
+        Implement per activity state CPU usage reporting using diagnostic logging
+        https://bugs.webkit.org/show_bug.cgi?id=167163
+        <rdar://problem/30058349>
+
+        Reviewed by Andreas Kling.
+
+        Implement per activity state CPU usage reporting using diagnostic logging.
+
+        * WebCore.xcodeproj/project.pbxproj:
+        * page/ActivityState.h:
+        * page/ChromeClient.h:
+        * page/DiagnosticLoggingKeys.cpp:
+        (WebCore::DiagnosticLoggingKeys::nonVisibleStateKey):
+        (WebCore::DiagnosticLoggingKeys::visibleNonActiveStateKey):
+        (WebCore::DiagnosticLoggingKeys::visibleAndActiveStateKey):
+        (WebCore::DiagnosticLoggingKeys::foregroundCPUUsageToDiagnosticLogginKey):
+        (WebCore::DiagnosticLoggingKeys::backgroundCPUUsageToDiagnosticLogginKey):
+        * page/DiagnosticLoggingKeys.h:
+        * page/Page.cpp:
+        (WebCore::isUtilityPageChromeClient):
+        (WebCore::Page::Page):
+        (WebCore::Page::~Page):
+        (WebCore::Page::didStartProvisionalLoad):
+        (WebCore::Page::didFinishLoad):
+        (WebCore::Page::isUtilityPage):
+        (WebCore::Page::setActivityState):
+        (WebCore::Page::setIsVisibleInternal):
+        * page/Page.h:
+        (WebCore::Page::activityState):
+        (WebCore::Page::isUtilityPage):
+        * page/PerformanceMonitor.cpp: Added.
+        (WebCore::activityStateForCPUSampling):
+        (WebCore::PerformanceMonitor::PerformanceMonitor):
+        (WebCore::PerformanceMonitor::didStartProvisionalLoad):
+        (WebCore::PerformanceMonitor::didFinishLoad):
+        (WebCore::PerformanceMonitor::activityStateChanged):
+        (WebCore::PerformanceMonitor::measurePostLoadCPUUsage):
+        (WebCore::PerformanceMonitor::measurePostBackgroundingCPUUsage):
+        (WebCore::PerformanceMonitor::measurePerActivityStateCPUUsage):
+        (WebCore::stringForCPUSamplingActivityState):
+        (WebCore::PerformanceMonitor::measureCPUUsageInActivityState):
+        * page/PerformanceMonitor.h: Copied from Source/WebCore/page/ActivityState.h.
+        * page/Settings.cpp:
+        * page/Settings.h:
+        (WebCore::Settings::isPostLoadCPUUsageMeasurementEnabled):
+        (WebCore::Settings::isPostBackgroundingCPUUsageMeasurementEnabled):
+        (WebCore::Settings::isPerActivityStateCPUUsageMeasurementEnabled):
+
 2017-01-19  Myles C. Maxfield  <mmaxfield@apple.com>
 
         [Cocoa] Variation fonts are erroneously disabled on iOS
index 224b67c..9a40db8 100644 (file)
                83F1206B1B8C104700D75F63 /* JSNodeFilterCustom.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83F1206A1B8C103600D75F63 /* JSNodeFilterCustom.cpp */; };
                83FE7CA71DA9F1A70037237C /* UIEventInit.h in Headers */ = {isa = PBXBuildFile; fileRef = 83FE7CA41DA9F1660037237C /* UIEventInit.h */; settings = {ATTRIBUTES = (Private, ); }; };
                83FE7CA81DA9F1B60037237C /* EventModifierInit.h in Headers */ = {isa = PBXBuildFile; fileRef = 83FE7CA31DA9F1650037237C /* EventModifierInit.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               83FE90271E307C30003E9199 /* PerformanceMonitor.h in Headers */ = {isa = PBXBuildFile; fileRef = 83FE90261E307C1C003E9199 /* PerformanceMonitor.h */; };
+               83FE90281E307C33003E9199 /* PerformanceMonitor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83FE90251E307C1C003E9199 /* PerformanceMonitor.cpp */; };
                8419D2A7120D92D000141F8F /* SVGPathByteStream.h in Headers */ = {isa = PBXBuildFile; fileRef = 8419D2A4120D92D000141F8F /* SVGPathByteStream.h */; };
                8419D2A8120D92D000141F8F /* SVGPathByteStreamBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8419D2A5120D92D000141F8F /* SVGPathByteStreamBuilder.cpp */; };
                8419D2A9120D92D000141F8F /* SVGPathByteStreamBuilder.h in Headers */ = {isa = PBXBuildFile; fileRef = 8419D2A6120D92D000141F8F /* SVGPathByteStreamBuilder.h */; };
                83FE7CA41DA9F1660037237C /* UIEventInit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UIEventInit.h; sourceTree = "<group>"; };
                83FE7CA51DA9F1660037237C /* UIEventInit.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = UIEventInit.idl; sourceTree = "<group>"; };
                83FE7CA61DA9F1660037237C /* EventModifierInit.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = EventModifierInit.idl; sourceTree = "<group>"; };
+               83FE90251E307C1C003E9199 /* PerformanceMonitor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PerformanceMonitor.cpp; sourceTree = "<group>"; };
+               83FE90261E307C1C003E9199 /* PerformanceMonitor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PerformanceMonitor.h; sourceTree = "<group>"; };
                8419D2A4120D92D000141F8F /* SVGPathByteStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SVGPathByteStream.h; sourceTree = "<group>"; };
                8419D2A5120D92D000141F8F /* SVGPathByteStreamBuilder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SVGPathByteStreamBuilder.cpp; sourceTree = "<group>"; };
                8419D2A6120D92D000141F8F /* SVGPathByteStreamBuilder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SVGPathByteStreamBuilder.h; sourceTree = "<group>"; };
                                86BE33FC15058CB200CE0FD8 /* PerformanceEntry.idl */,
                                AD5A0C211DECA10100707054 /* PerformanceLogging.cpp */,
                                AD5A0C201DECA0B500707054 /* PerformanceLogging.h */,
+                               83FE90251E307C1C003E9199 /* PerformanceMonitor.cpp */,
+                               83FE90261E307C1C003E9199 /* PerformanceMonitor.h */,
                                8AF4E55211DC5A36000ED3DE /* PerformanceNavigation.cpp */,
                                8AF4E55311DC5A36000ED3DE /* PerformanceNavigation.h */,
                                8AF4E55411DC5A36000ED3DE /* PerformanceNavigation.idl */,
                                85031B440A44EFC700F992E0 /* KeyboardEvent.h in Headers */,
                                1AE00D59182DAC8D00087DD7 /* KeyedCoding.h in Headers */,
                                517A63C51B74318F00E7DCDC /* KeyedDecoderCF.h in Headers */,
+                               83FE90271E307C30003E9199 /* PerformanceMonitor.h in Headers */,
                                517A63C61B74319200E7DCDC /* KeyedEncoderCF.h in Headers */,
                                A513B3D7114B1666001C429B /* KeyEventCocoa.h in Headers */,
                                265541391489811C000DFC5D /* KeyEventCodesIOS.h in Headers */,
                                073BE34E17D180B2002BD431 /* RTCSessionDescriptionDescriptor.cpp in Sources */,
                                078E090E17D14CEE00420AA1 /* RTCStatsReport.cpp in Sources */,
                                078E091017D14CEE00420AA1 /* RTCStatsResponse.cpp in Sources */,
+                               83FE90281E307C33003E9199 /* PerformanceMonitor.cpp in Sources */,
                                5E2C43671BCEE3770001E2BC /* RTCTrackEvent.cpp in Sources */,
                                5824ABA21AE81116009074B7 /* RubyElement.cpp in Sources */,
                                5824ABA61AE81384009074B7 /* RubyTextElement.cpp in Sources */,
index b6ed782..0e534cc 100644 (file)
@@ -45,4 +45,10 @@ struct ActivityState {
     static const Flags AllFlags = WindowIsActive | IsFocused | IsVisible | IsVisibleOrOccluded | IsInWindow | IsVisuallyIdle | IsAudible | IsLoading;
 };
 
+enum class ActivityStateForCPUSampling {
+    NonVisible,
+    VisibleNonActive,
+    VisibleAndActive
+};
+
 } // namespace WebCore
index 331df4a..2c89b6c 100644 (file)
@@ -455,6 +455,8 @@ public:
 
     virtual void didInvalidateDocumentMarkerRects() { }
 
+    virtual void reportProcessCPUTime(int64_t, ActivityStateForCPUSampling) { }
+
 protected:
     virtual ~ChromeClient() { }
 };
index e16f59d..18b832d 100644 (file)
@@ -163,6 +163,11 @@ String DiagnosticLoggingKeys::noStoreKey()
     return ASCIILiteral("noStore");
 }
 
+String DiagnosticLoggingKeys::nonVisibleStateKey()
+{
+    return ASCIILiteral("nonVisibleState");
+}
+
 String DiagnosticLoggingKeys::notInMemoryCacheKey()
 {
     return ASCIILiteral("notInMemoryCache");
@@ -598,6 +603,16 @@ String DiagnosticLoggingKeys::videoKey()
     return ASCIILiteral("video");
 }
 
+String DiagnosticLoggingKeys::visibleNonActiveStateKey()
+{
+    return ASCIILiteral("visibleNonActiveState");
+}
+
+String DiagnosticLoggingKeys::visibleAndActiveStateKey()
+{
+    return ASCIILiteral("visibleAndActiveState");
+}
+
 String DiagnosticLoggingKeys::wastedSpeculativeWarmupWithRevalidationKey()
 {
     return ASCIILiteral("wastedSpeculativeWarmupWithRevalidation");
@@ -658,5 +673,37 @@ String WebCore::DiagnosticLoggingKeys::webGLKey()
     return ASCIILiteral("webGL");
 }
 
+String DiagnosticLoggingKeys::foregroundCPUUsageToDiagnosticLoggingKey(double cpuUsage)
+{
+    if (cpuUsage < 10)
+        return ASCIILiteral("Below10");
+    if (cpuUsage < 20)
+        return ASCIILiteral("10to20");
+    if (cpuUsage < 40)
+        return ASCIILiteral("20to40");
+    if (cpuUsage < 60)
+        return ASCIILiteral("40to60");
+    if (cpuUsage < 80)
+        return ASCIILiteral("60to80");
+    return ASCIILiteral("over80");
+}
+
+String DiagnosticLoggingKeys::backgroundCPUUsageToDiagnosticLoggingKey(double cpuUsage)
+{
+    if (cpuUsage < 1)
+        return ASCIILiteral("Below1");
+    if (cpuUsage < 5)
+        return ASCIILiteral("1to5");
+    if (cpuUsage < 10)
+        return ASCIILiteral("5to10");
+    if (cpuUsage < 30)
+        return ASCIILiteral("10to30");
+    if (cpuUsage < 50)
+        return ASCIILiteral("30to50");
+    if (cpuUsage < 70)
+        return ASCIILiteral("50to70");
+    return ASCIILiteral("over70");
+}
+
 } // namespace WebCore
 
index 6b6a2f0..50897bb 100644 (file)
@@ -40,7 +40,7 @@ public:
     static String cachedResourceRevalidationKey();
     static String canCacheKey();
     static String cannotSuspendActiveDOMObjectsKey();
-    static String cpuUsageKey();
+    WEBCORE_EXPORT static String cpuUsageKey();
     WEBCORE_EXPORT static String createSharedBufferFailedKey();
     WEBCORE_EXPORT static String deltaKey();
     static String deniedByClientKey();
@@ -88,6 +88,7 @@ public:
     static String noDocumentLoaderKey();
     WEBCORE_EXPORT static String noLongerInCacheKey();
     static String noStoreKey();
+    WEBCORE_EXPORT static String nonVisibleStateKey();
     WEBCORE_EXPORT static String notHTTPFamilyKey();
     WEBCORE_EXPORT static String notInCacheKey();
     static String notInMemoryCacheKey();
@@ -150,12 +151,17 @@ public:
     WEBCORE_EXPORT static String userKey();
     WEBCORE_EXPORT static String varyingHeaderMismatchKey();
     static String videoKey();
+    WEBCORE_EXPORT static String visibleNonActiveStateKey();
+    WEBCORE_EXPORT static String visibleAndActiveStateKey();
     WEBCORE_EXPORT static String wastedSpeculativeWarmupWithRevalidationKey();
     WEBCORE_EXPORT static String wastedSpeculativeWarmupWithoutRevalidationKey();
     WEBCORE_EXPORT static String webGLKey();
     WEBCORE_EXPORT static String webViewKey();
     WEBCORE_EXPORT static String zoomedKey();
 
+    WEBCORE_EXPORT static String foregroundCPUUsageToDiagnosticLoggingKey(double cpuUsage);
+    WEBCORE_EXPORT static String backgroundCPUUsageToDiagnosticLoggingKey(double cpuUsage);
+
     // Success / Failure keys.
     static String successKey();
     static String failureKey();
index 951e274..db17e6b 100644 (file)
@@ -66,6 +66,7 @@
 #include "PageDebuggable.h"
 #include "PageGroup.h"
 #include "PageOverlayController.h"
+#include "PerformanceMonitor.h"
 #include "PlatformMediaSessionManager.h"
 #include "PlugInClient.h"
 #include "PluginData.h"
 
 namespace WebCore {
 
-#define RELEASE_LOG_IF_ALLOWED(channel, fmt, ...) RELEASE_LOG_IF(isAlwaysOnLoggingAllowed(), channel, "%p - Page::" fmt, this, ##__VA_ARGS__)
-
 static HashSet<Page*>* allPages;
+static unsigned nonUtilityPageCount { 0 };
 
-static const std::chrono::seconds cpuUsageMeasurementDelay { 5 };
-static const std::chrono::seconds postLoadCPUUsageMeasurementDuration { 10 };
-static const std::chrono::minutes backgroundCPUUsageMeasurementDuration { 5 };
+static inline bool isUtilityPageChromeClient(ChromeClient& chromeClient)
+{
+    return chromeClient.isEmptyChromeClient() || chromeClient.isSVGImageChromeClient();
+}
 
 DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, pageCounter, ("Page"));
 
@@ -253,8 +254,8 @@ Page::Page(PageConfiguration&& pageConfiguration)
     , m_visitedLinkStore(*WTFMove(pageConfiguration.visitedLinkStore))
     , m_sessionID(SessionID::defaultSessionID())
     , m_isClosing(false)
-    , m_postPageLoadCPUUsageTimer(*this, &Page::measurePostLoadCPUUsage)
-    , m_postBackgroundingCPUUsageTimer(*this, &Page::measurePostBackgroundingCPUUsage)
+    , m_isUtilityPage(isUtilityPageChromeClient(chrome().client()))
+    , m_performanceMonitor(isUtilityPage() ? nullptr : std::make_unique<PerformanceMonitor>(*this))
 {
     updateTimerThrottlingState();
 
@@ -271,6 +272,8 @@ Page::Page(PageConfiguration&& pageConfiguration)
 
     ASSERT(!allPages->contains(this));
     allPages->add(this);
+    if (!isUtilityPage())
+        ++nonUtilityPageCount;
 
 #ifndef NDEBUG
     pageCounter.increment();
@@ -295,6 +298,8 @@ Page::~Page()
     m_mainFrame->setView(nullptr);
     setGroupName(String());
     allPages->remove(this);
+    if (!isUtilityPage())
+        --nonUtilityPageCount;
     
     m_settings->pageDestroyed();
 
@@ -934,91 +939,21 @@ void Page::setUserInterfaceLayoutDirection(UserInterfaceLayoutDirection userInte
 
 void Page::didStartProvisionalLoad()
 {
-    m_postLoadCPUTime = std::nullopt;
-    m_postPageLoadCPUUsageTimer.stop();
+    if (m_performanceMonitor)
+        m_performanceMonitor->didStartProvisionalLoad();
 }
 
 void Page::didFinishLoad()
 {
     resetRelevantPaintedObjectCounter();
 
-    // Only do post-load CPU usage measurement if there is a single Page in the process in order to reduce noise.
-    if (Settings::isPostLoadCPUUsageMeasurementEnabled() && allPages->size() == 1) {
-        m_postLoadCPUTime = std::nullopt;
-        m_postPageLoadCPUUsageTimer.startOneShot(cpuUsageMeasurementDelay);
-    }
-}
-
-static String foregroundCPUUsageToDiagnosticLogginKey(double cpuUsage)
-{
-    if (cpuUsage < 10)
-        return ASCIILiteral("Below10");
-    if (cpuUsage < 20)
-        return ASCIILiteral("10to20");
-    if (cpuUsage < 40)
-        return ASCIILiteral("20to40");
-    if (cpuUsage < 60)
-        return ASCIILiteral("40to60");
-    if (cpuUsage < 80)
-        return ASCIILiteral("60to80");
-    return ASCIILiteral("over80");
-}
-
-void Page::measurePostLoadCPUUsage()
-{
-    if (allPages->size() != 1)
-        return;
-
-    if (!m_postLoadCPUTime) {
-        m_postLoadCPUTime = getCPUTime();
-        if (m_postLoadCPUTime)
-            m_postPageLoadCPUUsageTimer.startOneShot(postLoadCPUUsageMeasurementDuration);
-        return;
-    }
-    std::optional<CPUTime> cpuTime = getCPUTime();
-    if (!cpuTime)
-        return;
-
-    double cpuUsage = cpuTime.value().percentageCPUUsageSince(*m_postLoadCPUTime);
-    RELEASE_LOG_IF_ALLOWED(PerformanceLogging, "measurePostLoadCPUUsage: Process was using %.1f%% percent CPU after the page load.", cpuUsage);
-    diagnosticLoggingClient().logDiagnosticMessageWithValue(DiagnosticLoggingKeys::postPageLoadKey(), DiagnosticLoggingKeys::cpuUsageKey(), foregroundCPUUsageToDiagnosticLogginKey(cpuUsage), ShouldSample::No);
+    if (m_performanceMonitor)
+        m_performanceMonitor->didFinishLoad();
 }
 
-static String backgroundCPUUsageToDiagnosticLogginKey(double cpuUsage)
+bool Page::isOnlyNonUtilityPage() const
 {
-    if (cpuUsage < 1)
-        return ASCIILiteral("Below1");
-    if (cpuUsage < 5)
-        return ASCIILiteral("1to5");
-    if (cpuUsage < 10)
-        return ASCIILiteral("5to10");
-    if (cpuUsage < 30)
-        return ASCIILiteral("10to30");
-    if (cpuUsage < 50)
-        return ASCIILiteral("30to50");
-    if (cpuUsage < 70)
-        return ASCIILiteral("50to70");
-    return ASCIILiteral("over70");
-}
-
-void Page::measurePostBackgroundingCPUUsage()
-{
-    if (allPages->size() != 1)
-        return;
-
-    if (!m_postBackgroundingCPUTime) {
-        m_postBackgroundingCPUTime = getCPUTime();
-        if (m_postBackgroundingCPUTime)
-            m_postBackgroundingCPUUsageTimer.startOneShot(backgroundCPUUsageMeasurementDuration);
-        return;
-    }
-    std::optional<CPUTime> cpuTime = getCPUTime();
-    if (!cpuTime)
-        return;
-
-    double cpuUsage = cpuTime.value().percentageCPUUsageSince(*m_postBackgroundingCPUTime);
-    RELEASE_LOG_IF_ALLOWED(PerformanceLogging, "measurePostBackgroundingCPUUsage: Process was using %.1f%% percent CPU after becoming non visible.", cpuUsage);
-    diagnosticLoggingClient().logDiagnosticMessageWithValue(DiagnosticLoggingKeys::postPageBackgroundingKey(), DiagnosticLoggingKeys::cpuUsageKey(), backgroundCPUUsageToDiagnosticLogginKey(cpuUsage), ShouldSample::No);
+    return !isUtilityPage() && nonUtilityPageCount == 1;
 }
 
 void Page::setTopContentInset(float contentInset)
@@ -1570,6 +1505,9 @@ void Page::setActivityState(ActivityState::Flags activityState)
 
     if (wasVisibleAndActive != isVisibleAndActive())
         PlatformMediaSessionManager::updateNowPlayingInfoIfNecessary();
+
+    if (m_performanceMonitor)
+        m_performanceMonitor->activityStateChanged(oldActivityState, activityState);
 }
 
 bool Page::isVisibleAndActive() const
@@ -1626,13 +1564,6 @@ void Page::setIsVisibleInternal(bool isVisible)
         if (FrameView* view = mainFrame().view())
             view->hide();
     }
-
-    // Measure CPU usage of pages when they are no longer visible.
-    m_postBackgroundingCPUTime = std::nullopt;
-    if (isVisible)
-        m_postBackgroundingCPUUsageTimer.stop();
-    else if (Settings::isPostBackgroundingCPUUsageMeasurementEnabled() && allPages->size() == 1)
-        m_postBackgroundingCPUUsageTimer.startOneShot(cpuUsageMeasurementDelay);
 }
 
 void Page::setIsPrerender()
index 02b5044..0d2c735 100644 (file)
@@ -21,7 +21,6 @@
 #pragma once
 
 #include "ActivityState.h"
-#include "CPUTime.h"
 #include "FindOptions.h"
 #include "FrameLoaderTypes.h"
 #include "LayoutMilestones.h"
@@ -105,6 +104,7 @@ class PageConfiguration;
 class PageConsoleClient;
 class PageDebuggable;
 class PageGroup;
+class PerformanceMonitor;
 class PlugInClient;
 class PluginData;
 class PluginInfoProvider;
@@ -357,6 +357,8 @@ public:
 
     // Notifications when the Page starts and stops being presented via a native window.
     WEBCORE_EXPORT void setActivityState(ActivityState::Flags);
+    ActivityState::Flags activityState() const { return m_activityState; }
+
     bool isVisibleAndActive() const;
     WEBCORE_EXPORT void setIsVisible(bool);
     WEBCORE_EXPORT void setIsPrerender();
@@ -559,6 +561,10 @@ public:
     void setEventThrottlingBehaviorOverride(std::optional<EventThrottlingBehavior> throttling) { m_eventThrottlingBehaviorOverride = throttling; }
 
     WebGLStateTracker* webGLStateTracker() const { return m_webGLStateTracker.get(); }
+
+    bool isOnlyNonUtilityPage() const;
+    bool isUtilityPage() const { return m_isUtilityPage; }
+
 private:
     WEBCORE_EXPORT void initGroup();
 
@@ -572,9 +578,6 @@ private:
     void checkSubframeCountConsistency() const;
 #endif
 
-    void measurePostLoadCPUUsage();
-    void measurePostBackgroundingCPUUsage();
-
     enum ShouldHighlightMatches { DoNotHighlightMatches, HighlightMatches };
     enum ShouldMarkMatches { DoNotMarkMatches, MarkMatches };
 
@@ -756,15 +759,13 @@ private:
     bool m_showAllPlugins { false };
     bool m_controlledByAutomation { false };
     bool m_resourceCachingDisabled { false };
+    bool m_isUtilityPage;
     UserInterfaceLayoutDirection m_userInterfaceLayoutDirection { UserInterfaceLayoutDirection::LTR };
     
     // For testing.
     std::optional<EventThrottlingBehavior> m_eventThrottlingBehaviorOverride;
 
-    Timer m_postPageLoadCPUUsageTimer;
-    std::optional<CPUTime> m_postLoadCPUTime;
-    Timer m_postBackgroundingCPUUsageTimer;
-    std::optional<CPUTime> m_postBackgroundingCPUTime;
+    std::unique_ptr<PerformanceMonitor> m_performanceMonitor;
 };
 
 inline PageGroup& Page::group()
diff --git a/Source/WebCore/page/PerformanceMonitor.cpp b/Source/WebCore/page/PerformanceMonitor.cpp
new file mode 100644 (file)
index 0000000..2caebef
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2017 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 "PerformanceMonitor.h"
+
+#include "Chrome.h"
+#include "ChromeClient.h"
+#include "DiagnosticLoggingClient.h"
+#include "DiagnosticLoggingKeys.h"
+#include "Logging.h"
+#include "Page.h"
+#include "Settings.h"
+
+namespace WebCore {
+
+#define RELEASE_LOG_IF_ALLOWED(channel, fmt, ...) RELEASE_LOG_IF(m_page.isAlwaysOnLoggingAllowed(), channel, "%p - PerformanceMonitor::" fmt, this, ##__VA_ARGS__)
+
+static const std::chrono::seconds cpuUsageMeasurementDelay { 5 };
+static const std::chrono::seconds postLoadCPUUsageMeasurementDuration { 10 };
+static const std::chrono::minutes backgroundCPUUsageMeasurementDuration { 5 };
+static const std::chrono::minutes cpuUsageSamplingInterval { 10 };
+
+static inline ActivityStateForCPUSampling activityStateForCPUSampling(ActivityState::Flags state)
+{
+    if (!(state & ActivityState::IsVisible))
+        return ActivityStateForCPUSampling::NonVisible;
+    if (state & ActivityState::WindowIsActive)
+        return ActivityStateForCPUSampling::VisibleAndActive;
+    return ActivityStateForCPUSampling::VisibleNonActive;
+}
+
+PerformanceMonitor::PerformanceMonitor(Page& page)
+    : m_page(page)
+    , m_postPageLoadCPUUsageTimer(*this, &PerformanceMonitor::measurePostLoadCPUUsage)
+    , m_postBackgroundingCPUUsageTimer(*this, &PerformanceMonitor::measurePostBackgroundingCPUUsage)
+    , m_perActivityStateCPUUsageTimer(*this, &PerformanceMonitor::measurePerActivityStateCPUUsage)
+{
+    ASSERT(!page.isUtilityPage());
+
+    if (Settings::isPerActivityStateCPUUsageMeasurementEnabled()) {
+        m_perActivityStateCPUTime = getCPUTime();
+        m_perActivityStateCPUUsageTimer.startRepeating(cpuUsageSamplingInterval);
+    }
+}
+
+void PerformanceMonitor::didStartProvisionalLoad()
+{
+    m_postLoadCPUTime = std::nullopt;
+    m_postPageLoadCPUUsageTimer.stop();
+}
+
+void PerformanceMonitor::didFinishLoad()
+{
+    // Only do post-load CPU usage measurement if there is a single Page in the process in order to reduce noise.
+    if (Settings::isPostLoadCPUUsageMeasurementEnabled() && m_page.isOnlyNonUtilityPage()) {
+        m_postLoadCPUTime = std::nullopt;
+        m_postPageLoadCPUUsageTimer.startOneShot(cpuUsageMeasurementDelay);
+    }
+}
+
+void PerformanceMonitor::activityStateChanged(ActivityState::Flags oldState, ActivityState::Flags newState)
+{
+    ActivityState::Flags changed = oldState ^ newState;
+    bool visibilityChanged = changed & ActivityState::IsVisible;
+
+    // Measure CPU usage of pages when they are no longer visible.
+    if (Settings::isPostBackgroundingCPUUsageMeasurementEnabled() && visibilityChanged) {
+        m_postBackgroundingCPUTime = std::nullopt;
+        if (newState & ActivityState::IsVisible)
+            m_postBackgroundingCPUUsageTimer.stop();
+        else if (m_page.isOnlyNonUtilityPage())
+            m_postBackgroundingCPUUsageTimer.startOneShot(cpuUsageMeasurementDelay);
+    }
+
+    if (Settings::isPerActivityStateCPUUsageMeasurementEnabled()) {
+        // If visibility changed then report CPU usage right away because CPU usage is connected to visibility state.
+        auto oldActivityStateForCPUSampling = activityStateForCPUSampling(oldState);
+        if (oldActivityStateForCPUSampling != activityStateForCPUSampling(newState)) {
+            measureCPUUsageInActivityState(oldActivityStateForCPUSampling);
+            m_perActivityStateCPUUsageTimer.startRepeating(cpuUsageSamplingInterval);
+        }
+    }
+}
+
+void PerformanceMonitor::measurePostLoadCPUUsage()
+{
+    if (!m_page.isOnlyNonUtilityPage()) {
+        m_postLoadCPUTime = std::nullopt;
+        return;
+    }
+
+    if (!m_postLoadCPUTime) {
+        m_postLoadCPUTime = getCPUTime();
+        if (m_postLoadCPUTime)
+            m_postPageLoadCPUUsageTimer.startOneShot(postLoadCPUUsageMeasurementDuration);
+        return;
+    }
+    std::optional<CPUTime> cpuTime = getCPUTime();
+    if (!cpuTime)
+        return;
+
+    double cpuUsage = cpuTime.value().percentageCPUUsageSince(*m_postLoadCPUTime);
+    RELEASE_LOG_IF_ALLOWED(PerformanceLogging, "measurePostLoadCPUUsage: Process was using %.1f%% CPU after the page load.", cpuUsage);
+    m_page.diagnosticLoggingClient().logDiagnosticMessageWithValue(DiagnosticLoggingKeys::postPageLoadKey(), DiagnosticLoggingKeys::cpuUsageKey(), DiagnosticLoggingKeys::foregroundCPUUsageToDiagnosticLoggingKey(cpuUsage), ShouldSample::No);
+}
+
+void PerformanceMonitor::measurePostBackgroundingCPUUsage()
+{
+    if (!m_page.isOnlyNonUtilityPage()) {
+        m_postBackgroundingCPUTime = std::nullopt;
+        return;
+    }
+
+    if (!m_postBackgroundingCPUTime) {
+        m_postBackgroundingCPUTime = getCPUTime();
+        if (m_postBackgroundingCPUTime)
+            m_postBackgroundingCPUUsageTimer.startOneShot(backgroundCPUUsageMeasurementDuration);
+        return;
+    }
+    std::optional<CPUTime> cpuTime = getCPUTime();
+    if (!cpuTime)
+        return;
+
+    double cpuUsage = cpuTime.value().percentageCPUUsageSince(*m_postBackgroundingCPUTime);
+    RELEASE_LOG_IF_ALLOWED(PerformanceLogging, "measurePostBackgroundingCPUUsage: Process was using %.1f%% CPU after becoming non visible.", cpuUsage);
+    m_page.diagnosticLoggingClient().logDiagnosticMessageWithValue(DiagnosticLoggingKeys::postPageBackgroundingKey(), DiagnosticLoggingKeys::cpuUsageKey(), DiagnosticLoggingKeys::backgroundCPUUsageToDiagnosticLoggingKey(cpuUsage), ShouldSample::No);
+}
+
+void PerformanceMonitor::measurePerActivityStateCPUUsage()
+{
+    measureCPUUsageInActivityState(activityStateForCPUSampling(m_page.activityState()));
+}
+
+#if !RELEASE_LOG_DISABLED
+
+static inline const char* stringForCPUSamplingActivityState(ActivityStateForCPUSampling activityState)
+{
+    switch (activityState) {
+    case ActivityStateForCPUSampling::NonVisible:
+        return "NonVisible";
+    case ActivityStateForCPUSampling::VisibleNonActive:
+        return "VisibleNonActive";
+    case ActivityStateForCPUSampling::VisibleAndActive:
+        return "VisibleAndActive";
+    }
+}
+
+#endif
+
+void PerformanceMonitor::measureCPUUsageInActivityState(ActivityStateForCPUSampling activityState)
+{
+    if (!m_page.isOnlyNonUtilityPage()) {
+        m_perActivityStateCPUTime = std::nullopt;
+        return;
+    }
+
+    if (!m_perActivityStateCPUTime) {
+        m_perActivityStateCPUTime = getCPUTime();
+        return;
+    }
+
+    std::optional<CPUTime> cpuTime = getCPUTime();
+    if (!cpuTime) {
+        m_perActivityStateCPUTime = std::nullopt;
+        return;
+    }
+
+#if !RELEASE_LOG_DISABLED
+    double cpuUsage = cpuTime.value().percentageCPUUsageSince(*m_perActivityStateCPUTime);
+    RELEASE_LOG_IF_ALLOWED(PerformanceLogging, "measureCPUUsageInActivityState: Process is using %.1f%% CPU in state: %s", cpuUsage, stringForCPUSamplingActivityState(activityState));
+#endif
+    m_page.chrome().client().reportProcessCPUTime((cpuTime.value().systemTime + cpuTime.value().userTime) - (m_perActivityStateCPUTime.value().systemTime + m_perActivityStateCPUTime.value().userTime), activityState);
+
+    m_perActivityStateCPUTime = WTFMove(cpuTime);
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/page/PerformanceMonitor.h b/Source/WebCore/page/PerformanceMonitor.h
new file mode 100644 (file)
index 0000000..60cac00
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#pragma once
+
+#include "ActivityState.h"
+#include "CPUTime.h"
+#include "Timer.h"
+#include <wtf/Optional.h>
+
+namespace WebCore {
+
+class Page;
+
+class PerformanceMonitor {
+public:
+    explicit PerformanceMonitor(Page&);
+
+    void didStartProvisionalLoad();
+    void didFinishLoad();
+    void activityStateChanged(ActivityState::Flags oldState, ActivityState::Flags newState);
+
+private:
+    void measurePostLoadCPUUsage();
+    void measurePostBackgroundingCPUUsage();
+    void measurePerActivityStateCPUUsage();
+    void measureCPUUsageInActivityState(ActivityStateForCPUSampling);
+
+    Page& m_page;
+
+    Timer m_postPageLoadCPUUsageTimer;
+    std::optional<CPUTime> m_postLoadCPUTime;
+    Timer m_postBackgroundingCPUUsageTimer;
+    std::optional<CPUTime> m_postBackgroundingCPUTime;
+    Timer m_perActivityStateCPUUsageTimer;
+    std::optional<CPUTime> m_perActivityStateCPUTime;
+};
+
+}
index 759796a..6bb9d03 100644 (file)
@@ -778,24 +778,6 @@ bool Settings::globalConstRedeclarationShouldThrow()
 #endif
 }
 
-bool Settings::isPostLoadCPUUsageMeasurementEnabled()
-{
-#if PLATFORM(COCOA)
-    return true;
-#else
-    return false;
-#endif
-}
-
-bool Settings::isPostBackgroundingCPUUsageMeasurementEnabled()
-{
-#if PLATFORM(MAC)
-    return true;
-#else
-    return false;
-#endif
-}
-
 void Settings::setAllowsAnySSLCertificate(bool allowAnySSLCertificate)
 {
     gAllowsAnySSLCertificate = allowAnySSLCertificate;
index a974021..2d3c83c 100644 (file)
@@ -201,6 +201,7 @@ public:
 
     static bool isPostLoadCPUUsageMeasurementEnabled();
     static bool isPostBackgroundingCPUUsageMeasurementEnabled();
+    static bool isPerActivityStateCPUUsageMeasurementEnabled();
 
     static bool globalConstRedeclarationShouldThrow();
 
@@ -423,4 +424,31 @@ private:
     static bool gAllowsAnySSLCertificate;
 };
 
+inline bool Settings::isPostLoadCPUUsageMeasurementEnabled()
+{
+#if PLATFORM(COCOA)
+    return true;
+#else
+    return false;
+#endif
+}
+
+inline bool Settings::isPostBackgroundingCPUUsageMeasurementEnabled()
+{
+#if PLATFORM(MAC)
+    return true;
+#else
+    return false;
+#endif
+}
+
+inline bool Settings::isPerActivityStateCPUUsageMeasurementEnabled()
+{
+#if PLATFORM(MAC)
+    return true;
+#else
+    return false;
+#endif
+}
+
 } // namespace WebCore
index 0acf415..a210dcf 100644 (file)
@@ -1,3 +1,33 @@
+2017-01-19  Chris Dumez  <cdumez@apple.com>
+
+        Implement per activity state CPU usage reporting using diagnostic logging
+        https://bugs.webkit.org/show_bug.cgi?id=167163
+        <rdar://problem/30058349>
+
+        Reviewed by Andreas Kling.
+
+        Implement per activity state CPU usage reporting using diagnostic logging.
+
+        * Platform/Logging.h:
+        * UIProcess/PerActivityStateCPUUsageSampler.cpp: Added.
+        (WebKit::PerActivityStateCPUUsageSampler::PerActivityStateCPUUsageSampler):
+        (WebKit::PerActivityStateCPUUsageSampler::~PerActivityStateCPUUsageSampler):
+        (WebKit::PerActivityStateCPUUsageSampler::reportWebContentCPUTime):
+        (WebKit::loggingKeyForActivityState):
+        (WebKit::loggingKeyForCPUUsage):
+        (WebKit::PerActivityStateCPUUsageSampler::loggingTimerFired):
+        (WebKit::PerActivityStateCPUUsageSampler::pageForLogging):
+        * UIProcess/PerActivityStateCPUUsageSampler.h: Copied from Source/WebCore/page/ActivityState.h.
+        * UIProcess/WebProcessPool.cpp:
+        (WebKit::WebProcessPool::WebProcessPool):
+        (WebKit::WebProcessPool::reportWebContentCPUTime):
+        * UIProcess/WebProcessPool.h:
+        * UIProcess/WebProcessPool.messages.in:
+        * WebKit2.xcodeproj/project.pbxproj:
+        * WebProcess/WebCoreSupport/WebChromeClient.cpp:
+        (WebKit::WebChromeClient::reportProcessCPUTime):
+        * WebProcess/WebCoreSupport/WebChromeClient.h:
+
 2017-01-19  Dan Bernstein  <mitz@apple.com>
 
         Build fix.
index 8720272..e3e88af 100644 (file)
@@ -57,6 +57,7 @@ extern "C" {
     M(NetworkCacheStorage) \
     M(NetworkScheduling) \
     M(NetworkSession) \
+    M(PerformanceLogging) \
     M(Plugins) \
     M(Printing) \
     M(ProcessSuspension) \
diff --git a/Source/WebKit2/UIProcess/PerActivityStateCPUUsageSampler.cpp b/Source/WebKit2/UIProcess/PerActivityStateCPUUsageSampler.cpp
new file mode 100644 (file)
index 0000000..2437e97
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2017 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 "PerActivityStateCPUUsageSampler.h"
+
+#include "Logging.h"
+#include "WebProcessPool.h"
+#include "WebProcessProxy.h"
+#include <WebCore/DiagnosticLoggingKeys.h>
+
+namespace WebKit {
+
+static const int64_t microsecondsPerSecond = 1000000;
+
+using namespace WebCore;
+
+static const std::chrono::minutes loggingInterval { 60 };
+
+PerActivityStateCPUUsageSampler::PerActivityStateCPUUsageSampler(WebProcessPool& processPool)
+    : m_processPool(processPool)
+    , m_loggingTimer(RunLoop::main(), this, &PerActivityStateCPUUsageSampler::loggingTimerFired)
+{
+    m_lastCPUTime = monotonicallyIncreasingTime();
+    m_loggingTimer.startRepeating(loggingInterval);
+}
+
+PerActivityStateCPUUsageSampler::~PerActivityStateCPUUsageSampler()
+{
+}
+
+void PerActivityStateCPUUsageSampler::reportWebContentCPUTime(int64_t cpuTime, ActivityStateForCPUSampling activityState)
+{
+    auto result = m_cpuTimeInActivityState.add(activityState, cpuTime);
+    if (!result.isNewEntry)
+        result.iterator->value += cpuTime;
+}
+
+static inline String loggingKeyForActivityState(ActivityStateForCPUSampling state)
+{
+    switch (state) {
+    case ActivityStateForCPUSampling::NonVisible:
+        return DiagnosticLoggingKeys::nonVisibleStateKey();
+    case ActivityStateForCPUSampling::VisibleNonActive:
+        return DiagnosticLoggingKeys::visibleNonActiveStateKey();
+    case ActivityStateForCPUSampling::VisibleAndActive:
+        return DiagnosticLoggingKeys::visibleAndActiveStateKey();
+    }
+}
+
+static inline String loggingKeyForCPUUsage(ActivityStateForCPUSampling state, double cpuUsage)
+{
+    switch (state) {
+    case ActivityStateForCPUSampling::NonVisible:
+        return DiagnosticLoggingKeys::backgroundCPUUsageToDiagnosticLoggingKey(cpuUsage);
+    case ActivityStateForCPUSampling::VisibleNonActive:
+    case ActivityStateForCPUSampling::VisibleAndActive:
+        return DiagnosticLoggingKeys::foregroundCPUUsageToDiagnosticLoggingKey(cpuUsage);
+    }
+}
+
+void PerActivityStateCPUUsageSampler::loggingTimerFired()
+{
+    auto* page = pageForLogging();
+    if (!page) {
+        m_cpuTimeInActivityState.clear();
+        return;
+    }
+
+    double currentCPUTime = monotonicallyIncreasingTime();
+    int64_t cpuTimeDelta = (currentCPUTime - m_lastCPUTime) * microsecondsPerSecond;
+
+    for (auto& pair : m_cpuTimeInActivityState) {
+        double cpuUsage = static_cast<double>(pair.value * 100.) / cpuTimeDelta;
+        String loggingKey = loggingKeyForActivityState(pair.key);
+        page->logDiagnosticMessageWithValue(DiagnosticLoggingKeys::cpuUsageKey(), loggingKey, loggingKeyForCPUUsage(pair.key, cpuUsage), false);
+        RELEASE_LOG(PerformanceLogging, "WebContent processes used %.1f%% CPU in %s state", cpuUsage, loggingKey.utf8().data());
+    }
+
+    m_cpuTimeInActivityState.clear();
+    m_lastCPUTime = currentCPUTime;
+}
+
+WebPageProxy* PerActivityStateCPUUsageSampler::pageForLogging() const
+{
+    for (auto& webProcess : m_processPool.processes()) {
+        if (!webProcess->pageCount())
+            continue;
+        return *webProcess->pages().begin();
+    }
+    return nullptr;
+}
+
+} // namespace WebKit
diff --git a/Source/WebKit2/UIProcess/PerActivityStateCPUUsageSampler.h b/Source/WebKit2/UIProcess/PerActivityStateCPUUsageSampler.h
new file mode 100644 (file)
index 0000000..dab6f4b
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#pragma once
+
+#include <WebCore/Page.h>
+#include <wtf/HashMap.h>
+#include <wtf/RunLoop.h>
+
+namespace WebKit {
+
+class WebPageProxy;
+class WebProcessPool;
+
+class PerActivityStateCPUUsageSampler {
+public:
+    explicit PerActivityStateCPUUsageSampler(WebProcessPool&);
+    ~PerActivityStateCPUUsageSampler();
+
+    void reportWebContentCPUTime(int64_t cpuTime, WebCore::ActivityStateForCPUSampling);
+
+private:
+    void loggingTimerFired();
+    WebPageProxy* pageForLogging() const;
+
+    WebProcessPool& m_processPool;
+    RunLoop::Timer<PerActivityStateCPUUsageSampler> m_loggingTimer;
+    typedef HashMap<WebCore::ActivityStateForCPUSampling, int64_t, WTF::IntHash<WebCore::ActivityStateForCPUSampling>, WTF::StrongEnumHashTraits<WebCore::ActivityStateForCPUSampling>> CPUTimeInActivityStateMap;
+    CPUTimeInActivityStateMap m_cpuTimeInActivityState;
+    double m_lastCPUTime;
+};
+
+} // namespace WebKit
index c2a18bb..32051bf 100644 (file)
@@ -42,6 +42,7 @@
 #include "NetworkProcessCreationParameters.h"
 #include "NetworkProcessMessages.h"
 #include "NetworkProcessProxy.h"
+#include "PerActivityStateCPUUsageSampler.h"
 #include "SandboxExtension.h"
 #include "StatisticsData.h"
 #include "TextChecker.h"
@@ -168,6 +169,7 @@ WebProcessPool::WebProcessPool(API::ProcessPoolConfiguration& configuration)
     , m_websiteDataStore(m_configuration->shouldHaveLegacyDataStore() ? API::WebsiteDataStore::create(legacyWebsiteDataStoreConfiguration(m_configuration)).ptr() : nullptr)
 #if PLATFORM(MAC)
     , m_highPerformanceGraphicsUsageSampler(std::make_unique<HighPerformanceGraphicsUsageSampler>(*this))
+    , m_perActivityStateCPUUsageSampler(std::make_unique<PerActivityStateCPUUsageSampler>(*this))
 #endif
     , m_shouldUseTestingNetworkSession(false)
     , m_processTerminationEnabled(true)
@@ -1431,4 +1433,15 @@ void WebProcessPool::updateHiddenPageThrottlingAutoIncreaseLimit()
     sendToAllProcesses(Messages::WebProcess::SetHiddenPageTimerThrottlingIncreaseLimit(limitInMilliseconds));
 }
 
+void WebProcessPool::reportWebContentCPUTime(int64_t cpuTime, uint64_t activityState)
+{
+#if PLATFORM(MAC)
+    if (m_perActivityStateCPUUsageSampler)
+        m_perActivityStateCPUUsageSampler->reportWebContentCPUTime(cpuTime, static_cast<WebCore::ActivityStateForCPUSampling>(activityState));
+#else
+    UNUSED_PARAM(cpuTime);
+    UNUSED_PARAM(activityState);
+#endif
+}
+
 } // namespace WebKit
index d9b8603..ae56ffe 100644 (file)
@@ -85,6 +85,7 @@ namespace WebKit {
 class DownloadProxy;
 class HighPerformanceGraphicsUsageSampler;
 class UIGamepad;
+class PerActivityStateCPUUsageSampler;
 class WebAutomationSession;
 class WebContextSupplement;
 class WebIconDatabase;
@@ -259,6 +260,8 @@ public:
     void clearCachedCredentials();
     void terminateDatabaseProcess();
 
+    void reportWebContentCPUTime(int64_t cpuTime, uint64_t activityState);
+
     void allowSpecificHTTPSCertificateForHost(const WebCertificateInfo*, const String& host);
 
     WebProcessProxy& createNewWebProcessRespectingProcessCountLimit(); // Will return an existing one if limit is met.
@@ -532,6 +535,7 @@ private:
     RetainPtr<NSObject> m_automaticDashSubstitutionNotificationObserver;
 
     std::unique_ptr<HighPerformanceGraphicsUsageSampler> m_highPerformanceGraphicsUsageSampler;
+    std::unique_ptr<PerActivityStateCPUUsageSampler> m_perActivityStateCPUUsageSampler;
 #endif
 
     String m_overrideIconDatabasePath;
index 8eba8e5..8990c00 100644 (file)
@@ -34,4 +34,6 @@ messages -> WebProcessPool {
     # Plug-in messages.
     void AddPlugInAutoStartOriginHash(String pageOrigin, uint32_t hash, WebCore::SessionID sessionID)
     void PlugInDidReceiveUserInteraction(uint32_t hash, WebCore::SessionID sessionID)
+
+    ReportWebContentCPUTime(int64_t cpuTime, uint64_t activityState)
 }
index f3ea56a..5d88a9d 100644 (file)
                831EEBBE1BD85C4300BB64C3 /* NetworkCacheSpeculativeLoad.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 831EEBBC1BD85C4300BB64C3 /* NetworkCacheSpeculativeLoad.cpp */; };
                832AE2521BE2E8CD00FAAE10 /* NetworkCacheSpeculativeLoadManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 832AE2501BE2E8CD00FAAE10 /* NetworkCacheSpeculativeLoadManager.h */; };
                832AE2531BE2E8CD00FAAE10 /* NetworkCacheSpeculativeLoadManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 832AE2511BE2E8CD00FAAE10 /* NetworkCacheSpeculativeLoadManager.cpp */; };
+               832ED18B1E2FE157006BA64A /* PerActivityStateCPUUsageSampler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 832ED1891E2FE13B006BA64A /* PerActivityStateCPUUsageSampler.cpp */; };
+               832ED18C1E2FE157006BA64A /* PerActivityStateCPUUsageSampler.h in Headers */ = {isa = PBXBuildFile; fileRef = 832ED18A1E2FE13B006BA64A /* PerActivityStateCPUUsageSampler.h */; };
                834B250F1A831A8D00CFB150 /* NetworkCacheFileSystem.h in Headers */ = {isa = PBXBuildFile; fileRef = 834B250E1A831A8D00CFB150 /* NetworkCacheFileSystem.h */; };
                834B25121A842C8700CFB150 /* NetworkCacheStatistics.h in Headers */ = {isa = PBXBuildFile; fileRef = 834B25101A842C8700CFB150 /* NetworkCacheStatistics.h */; };
                8360349F1ACB34D600626549 /* WebSQLiteDatabaseTracker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8360349D1ACB34D600626549 /* WebSQLiteDatabaseTracker.cpp */; };
                831EEBBC1BD85C4300BB64C3 /* NetworkCacheSpeculativeLoad.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NetworkCacheSpeculativeLoad.cpp; sourceTree = "<group>"; };
                832AE2501BE2E8CD00FAAE10 /* NetworkCacheSpeculativeLoadManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NetworkCacheSpeculativeLoadManager.h; sourceTree = "<group>"; };
                832AE2511BE2E8CD00FAAE10 /* NetworkCacheSpeculativeLoadManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NetworkCacheSpeculativeLoadManager.cpp; sourceTree = "<group>"; };
+               832ED1891E2FE13B006BA64A /* PerActivityStateCPUUsageSampler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PerActivityStateCPUUsageSampler.cpp; sourceTree = "<group>"; };
+               832ED18A1E2FE13B006BA64A /* PerActivityStateCPUUsageSampler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PerActivityStateCPUUsageSampler.h; sourceTree = "<group>"; };
                834B250E1A831A8D00CFB150 /* NetworkCacheFileSystem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NetworkCacheFileSystem.h; sourceTree = "<group>"; };
                834B25101A842C8700CFB150 /* NetworkCacheStatistics.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NetworkCacheStatistics.h; sourceTree = "<group>"; };
                8360349D1ACB34D600626549 /* WebSQLiteDatabaseTracker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WebSQLiteDatabaseTracker.cpp; sourceTree = "<group>"; };
                                BC6EDAA5111271C600E7678B /* PageClient.h */,
                                1AC75379183A9FDA0072CB15 /* PageLoadState.cpp */,
                                1AC7537A183A9FDB0072CB15 /* PageLoadState.h */,
+                               832ED1891E2FE13B006BA64A /* PerActivityStateCPUUsageSampler.cpp */,
+                               832ED18A1E2FE13B006BA64A /* PerActivityStateCPUUsageSampler.h */,
                                37716A59195B910500EE8B1B /* ProcessAssertion.cpp */,
                                86F9536018FF4FD4001DB2EF /* ProcessAssertion.h */,
                                86E67A22190F411800004AB7 /* ProcessThrottler.cpp */,
                                CE1A0BD71A48E6C60054EF74 /* TextInputSPI.h in Headers */,
                                1AAF263914687C39004A1E8A /* TiledCoreAnimationDrawingArea.h in Headers */,
                                1AF05D8714688348008B1E81 /* TiledCoreAnimationDrawingAreaProxy.h in Headers */,
+                               832ED18C1E2FE157006BA64A /* PerActivityStateCPUUsageSampler.h in Headers */,
                                1AFE436618B6C081009C7A48 /* UIDelegate.h in Headers */,
                                515BE1B51D5917FF00DD7C68 /* UIGamepad.h in Headers */,
                                515BE1A91D55293400DD7C68 /* UIGamepadProvider.h in Headers */,
                                93A88B471BC8829700ABA5C2 /* APIHitTestResult.cpp in Sources */,
                                7CD3A4821A5D02FA009623B8 /* APINavigation.cpp in Sources */,
                                BCF69FA31176D01400471A52 /* APINavigationData.cpp in Sources */,
+                               832ED18B1E2FE157006BA64A /* PerActivityStateCPUUsageSampler.cpp in Sources */,
                                B63403F914910D57001070B5 /* APIObject.cpp in Sources */,
                                378E1A3D181ED6FF0031007A /* APIObject.mm in Sources */,
                                BC857FB612B830E600EDEB2E /* APIOpenPanelParameters.cpp in Sources */,
index 7314f61..49834c3 100644 (file)
@@ -49,6 +49,7 @@
 #include "WebPageProxyMessages.h"
 #include "WebPopupMenu.h"
 #include "WebProcess.h"
+#include "WebProcessPoolMessages.h"
 #include "WebProcessProxyMessages.h"
 #include "WebSearchPopupMenu.h"
 #include <WebCore/ApplicationCacheStorage.h>
@@ -274,6 +275,11 @@ void WebChromeClient::runModal()
     m_page.runModal();
 }
 
+void WebChromeClient::reportProcessCPUTime(int64_t cpuTime, ActivityStateForCPUSampling activityState)
+{
+    WebProcess::singleton().send(Messages::WebProcessPool::ReportWebContentCPUTime(cpuTime, static_cast<uint64_t>(activityState)), 0);
+}
+
 void WebChromeClient::setToolbarsVisible(bool toolbarsAreVisible)
 {
     m_page.send(Messages::WebPageProxy::SetToolbarsAreVisible(toolbarsAreVisible));
index 64e8b14..5c9d773 100644 (file)
@@ -67,6 +67,8 @@ private:
     
     bool canRunModal() final;
     void runModal() final;
+
+    void reportProcessCPUTime(int64_t, WebCore::ActivityStateForCPUSampling) final;
     
     void setToolbarsVisible(bool) final;
     bool toolbarsVisible() final;