summaryrefslogtreecommitdiff
path: root/Source/WebCore/page/PerformanceMonitor.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/page/PerformanceMonitor.cpp')
-rw-r--r--Source/WebCore/page/PerformanceMonitor.cpp243
1 files changed, 243 insertions, 0 deletions
diff --git a/Source/WebCore/page/PerformanceMonitor.cpp b/Source/WebCore/page/PerformanceMonitor.cpp
new file mode 100644
index 000000000..3bfe4571c
--- /dev/null
+++ b/Source/WebCore/page/PerformanceMonitor.cpp
@@ -0,0 +1,243 @@
+/*
+ * 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 "PerformanceLogging.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 const std::chrono::seconds memoryUsageMeasurementDelay { 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)
+ , m_postPageLoadMemoryUsageTimer(*this, &PerformanceMonitor::measurePostLoadMemoryUsage)
+ , m_postBackgroundingMemoryUsageTimer(*this, &PerformanceMonitor::measurePostBackgroundingMemoryUsage)
+{
+ ASSERT(!page.isUtilityPage());
+
+ if (Settings::isPerActivityStateCPUUsageMeasurementEnabled()) {
+ m_perActivityStateCPUTime = getCPUTime();
+ m_perActivityStateCPUUsageTimer.startRepeating(cpuUsageSamplingInterval);
+ }
+}
+
+void PerformanceMonitor::didStartProvisionalLoad()
+{
+ m_postLoadCPUTime = std::nullopt;
+ m_postPageLoadCPUUsageTimer.stop();
+ m_postPageLoadMemoryUsageTimer.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);
+ }
+
+ // Likewise for post-load memory usage measurement.
+ if (Settings::isPostLoadMemoryUsageMeasurementEnabled() && m_page.isOnlyNonUtilityPage())
+ m_postPageLoadMemoryUsageTimer.startOneShot(memoryUsageMeasurementDelay);
+}
+
+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);
+ }
+ }
+
+ if (Settings::isPostBackgroundingMemoryUsageMeasurementEnabled() && visibilityChanged) {
+ if (newState & ActivityState::IsVisible)
+ m_postBackgroundingMemoryUsageTimer.stop();
+ else if (m_page.isOnlyNonUtilityPage())
+ m_postBackgroundingMemoryUsageTimer.startOneShot(memoryUsageMeasurementDelay);
+ }
+}
+
+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().logDiagnosticMessage(DiagnosticLoggingKeys::postPageLoadCPUUsageKey(), DiagnosticLoggingKeys::foregroundCPUUsageToDiagnosticLoggingKey(cpuUsage), ShouldSample::No);
+}
+
+void PerformanceMonitor::measurePostLoadMemoryUsage()
+{
+ if (!m_page.isOnlyNonUtilityPage())
+ return;
+
+ std::optional<uint64_t> memoryUsage = PerformanceLogging::physicalFootprint();
+ if (!memoryUsage)
+ return;
+
+ RELEASE_LOG_IF_ALLOWED(PerformanceLogging, "measurePostLoadMemoryUsage: Process was using %llu bytes of memory after the page load.", memoryUsage.value());
+ m_page.diagnosticLoggingClient().logDiagnosticMessage(DiagnosticLoggingKeys::postPageLoadMemoryUsageKey(), DiagnosticLoggingKeys::memoryUsageToDiagnosticLoggingKey(memoryUsage.value()), ShouldSample::No);
+}
+
+void PerformanceMonitor::measurePostBackgroundingMemoryUsage()
+{
+ if (!m_page.isOnlyNonUtilityPage())
+ return;
+
+ std::optional<uint64_t> memoryUsage = PerformanceLogging::physicalFootprint();
+ if (!memoryUsage)
+ return;
+
+ RELEASE_LOG_IF_ALLOWED(PerformanceLogging, "measurePostBackgroundingMemoryUsage: Process was using %llu bytes of memory after becoming non visible.", memoryUsage.value());
+ m_page.diagnosticLoggingClient().logDiagnosticMessage(DiagnosticLoggingKeys::postPageBackgroundingMemoryUsageKey(), DiagnosticLoggingKeys::memoryUsageToDiagnosticLoggingKey(memoryUsage.value()), 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().logDiagnosticMessage(DiagnosticLoggingKeys::postPageBackgroundingCPUUsageKey(), 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