Web Inspector: Show rendering frames (and FPS) in Layout and Rendering timeline
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 17 Mar 2015 08:41:19 +0000 (08:41 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 17 Mar 2015 08:41:19 +0000 (08:41 +0000)
https://bugs.webkit.org/show_bug.cgi?id=142029

Patch by Matt Baker <mattbaker@apple.com> on 2015-03-17
Reviewed by Timothy Hatcher.

Source/JavaScriptCore:

* inspector/protocol/Timeline.json:
Added new event type for runloop timeline records.

Source/WebCore:

Add new functionality to the Inspector timelines backend to add runloop data to timeline recordings.

* inspector/InspectorTimelineAgent.cpp:
(WebCore::currentRunLoop):
(WebCore::InspectorTimelineAgent::internalStart):
(WebCore::InspectorTimelineAgent::internalStop):
(WebCore::toProtocol):
(WebCore::InspectorTimelineAgent::InspectorTimelineAgent):
Install observers for the begining and end of the runloop when recording begins. All other
instrumented timeline events get added as children of the current runloop record, which is
sent to the frontend once the runloop completes.

* inspector/InspectorTimelineAgent.h:

* platform/cf/RunLoopObserver.cpp:
(WebCore::RunLoopObserver::schedule):
Wrapper changed to allow observing arbitrary runloop activities.

* platform/cf/RunLoopObserver.h:

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

Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/inspector/protocol/Timeline.json
Source/WebCore/ChangeLog
Source/WebCore/inspector/InspectorTimelineAgent.cpp
Source/WebCore/inspector/InspectorTimelineAgent.h
Source/WebCore/platform/cf/RunLoopObserver.cpp
Source/WebCore/platform/cf/RunLoopObserver.h

index 9a48dc50405c33dfc9754a0d45b3ed2caebd0180..32714bdc3bac56d02849a2fe94bd40577d3ab014 100644 (file)
@@ -1,3 +1,13 @@
+2015-03-17  Matt Baker  <mattbaker@apple.com>
+
+        Web Inspector: Show rendering frames (and FPS) in Layout and Rendering timeline
+        https://bugs.webkit.org/show_bug.cgi?id=142029
+
+        Reviewed by Timothy Hatcher.
+
+        * inspector/protocol/Timeline.json:
+        Added new event type for runloop timeline records.
+
 2015-03-16  Ryosuke Niwa  <rniwa@webkit.org>
 
         Enable ES6 classes by default
index 96422921f0d5179787fc79a492cfcf5c65f9566d..c4aae667b99b71b75ea5c9b315a0895beeabf355 100644 (file)
@@ -13,6 +13,7 @@
                 "InvalidateLayout",
                 "Layout",
                 "Paint",
+                "RunLoop",
                 "ScrollLayer",
                 "ParseHTML",
                 "TimerInstall",
index 230b3f456a24f1fad13ad7466fabfdc598048439..15eefde1d8e1dae088f6c1b05cb6c34dd1c77986 100644 (file)
@@ -1,3 +1,30 @@
+2015-03-17  Matt Baker  <mattbaker@apple.com>
+
+        Web Inspector: Show rendering frames (and FPS) in Layout and Rendering timeline
+        https://bugs.webkit.org/show_bug.cgi?id=142029
+
+        Reviewed by Timothy Hatcher.
+
+        Add new functionality to the Inspector timelines backend to add runloop data to timeline recordings.
+
+        * inspector/InspectorTimelineAgent.cpp:
+        (WebCore::currentRunLoop):
+        (WebCore::InspectorTimelineAgent::internalStart):
+        (WebCore::InspectorTimelineAgent::internalStop):
+        (WebCore::toProtocol):
+        (WebCore::InspectorTimelineAgent::InspectorTimelineAgent):
+        Install observers for the begining and end of the runloop when recording begins. All other
+        instrumented timeline events get added as children of the current runloop record, which is
+        sent to the frontend once the runloop completes.
+
+        * inspector/InspectorTimelineAgent.h:
+
+        * platform/cf/RunLoopObserver.cpp:
+        (WebCore::RunLoopObserver::schedule):
+        Wrapper changed to allow observing arbitrary runloop activities.
+
+        * platform/cf/RunLoopObserver.h:
+
 2015-03-17  Philippe Normand  <pnormand@igalia.com>
 
         [GTK] basic OpenWebRTC build support
index b4886de28f09224f781131997e98cd6a5fae6a3f..ecc120a8d1022ffd5e85547bf4fb9509d9aa11ea 100644 (file)
 #include <profiler/LegacyProfiler.h>
 #include <wtf/CurrentTime.h>
 
+#if PLATFORM(IOS)
+#include "RuntimeApplicationChecksIOS.h"
+#include <WebCore/WebCoreThread.h>
+#endif
+
+#if PLATFORM(COCOA)
+#include <WebCore/RunLoopObserver.h>
+#endif
+
 using namespace Inspector;
 
 namespace WebCore {
 
+#if PLATFORM(COCOA)
+static const CFIndex frameStopRunLoopOrder = (CFIndex)RunLoopObserver::WellKnownRunLoopOrders::CoreAnimationCommit + 1;
+
+static CFRunLoopRef currentRunLoop()
+{
+#if PLATFORM(IOS)
+    // A race condition during WebView deallocation can lead to a crash if the layer sync run loop
+    // observer is added to the main run loop <rdar://problem/9798550>. However, for responsiveness,
+    // we still allow this, see <rdar://problem/7403328>. Since the race condition and subsequent
+    // crash are especially troublesome for iBooks, we never allow the observer to be added to the
+    // main run loop in iBooks.
+    if (applicationIsIBooksOnIOS())
+        return WebThreadRunLoop();
+#endif
+    return CFRunLoopGetCurrent();
+}
+#endif
+
 InspectorTimelineAgent::~InspectorTimelineAgent()
 {
 }
@@ -122,6 +149,33 @@ void InspectorTimelineAgent::internalStart(const int* maxCallStackDepth)
 
     m_enabled = true;
 
+    // FIXME: Abstract away platform-specific code once https://bugs.webkit.org/show_bug.cgi?id=142748 is fixed.
+
+#if PLATFORM(COCOA)
+    m_frameStartObserver = RunLoopObserver::create(0, [this]() {
+        if (!m_enabled || m_didStartRecordingRunLoop)
+            return;
+
+        pushCurrentRecord(InspectorObject::create(), TimelineRecordType::RunLoop, false, nullptr);
+        m_didStartRecordingRunLoop = true;
+    });
+
+    m_frameStopObserver = RunLoopObserver::create(frameStopRunLoopOrder, [this]() {
+        if (!m_enabled || !m_didStartRecordingRunLoop)
+            return;
+
+        didCompleteCurrentRecord(TimelineRecordType::RunLoop);
+        m_didStartRecordingRunLoop = false;
+    });
+
+    m_frameStartObserver->schedule(currentRunLoop(), kCFRunLoopAfterWaiting | kCFRunLoopBeforeTimers);
+    m_frameStopObserver->schedule(currentRunLoop(), kCFRunLoopBeforeWaiting | kCFRunLoopExit);
+
+    // Create a runloop record immediately in order to capture the rest of the current runloop.
+    pushCurrentRecord(InspectorObject::create(), TimelineRecordType::RunLoop, false, nullptr);
+    m_didStartRecordingRunLoop = true;
+#endif
+
     if (m_frontendDispatcher)
         m_frontendDispatcher->recordingStarted();
 }
@@ -141,6 +195,11 @@ void InspectorTimelineAgent::internalStop()
     if (m_scriptDebugServer)
         m_scriptDebugServer->removeListener(this, true);
 
+#if PLATFORM(COCOA)
+    m_frameStartObserver = nullptr;
+    m_frameStopObserver = nullptr;
+#endif
+
     clearRecordStack();
 
     m_enabled = false;
@@ -539,6 +598,8 @@ static Inspector::Protocol::Timeline::EventType toProtocol(TimelineRecordType ty
         return Inspector::Protocol::Timeline::EventType::Layout;
     case TimelineRecordType::Paint:
         return Inspector::Protocol::Timeline::EventType::Paint;
+    case TimelineRecordType::RunLoop:
+        return Inspector::Protocol::Timeline::EventType::RunLoop;
     case TimelineRecordType::ScrollLayer:
         return Inspector::Protocol::Timeline::EventType::ScrollLayer;
 
@@ -654,6 +715,7 @@ InspectorTimelineAgent::InspectorTimelineAgent(InstrumentingAgents* instrumentin
     , m_client(client)
     , m_enabled(false)
     , m_enabledFromFrontend(false)
+    , m_didStartRecordingRunLoop(false)
 {
 }
 
index 45af63599f8ce7d35f71911da891627d0cf9ec7a..a0e7c7b39c1c90363473e9061359d5bf77cdac9b 100644 (file)
@@ -61,6 +61,7 @@ class PageScriptDebugServer;
 class RenderObject;
 class ResourceRequest;
 class ResourceResponse;
+class RunLoopObserver;
 
 typedef String ErrorString;
 
@@ -71,6 +72,7 @@ enum class TimelineRecordType {
     InvalidateLayout,
     Layout,
     Paint,
+    RunLoop,
     ScrollLayer,
 
     ParseHTML,
@@ -244,6 +246,12 @@ private:
 
     bool m_enabled;
     bool m_enabledFromFrontend;
+
+#if PLATFORM(COCOA)
+    std::unique_ptr<WebCore::RunLoopObserver> m_frameStartObserver;
+    std::unique_ptr<WebCore::RunLoopObserver> m_frameStopObserver;
+#endif
+    bool m_didStartRecordingRunLoop;
 };
 
 } // namespace WebCore
index e1ea4eb2d5dd158bce99f5345b803399bfbc02bc..7042f8fbeda7c9e72eb22ecd5d6d0d263b59e8d2 100644 (file)
@@ -49,7 +49,7 @@ void RunLoopObserver::runLoopObserverFired()
     m_callback();
 }
 
-void RunLoopObserver::schedule(CFRunLoopRef runLoop)
+void RunLoopObserver::schedule(CFRunLoopRef runLoop, CFRunLoopActivity activity)
 {
     if (!runLoop)
         runLoop = CFRunLoopGetCurrent();
@@ -61,7 +61,7 @@ void RunLoopObserver::schedule(CFRunLoopRef runLoop)
         return;
 
     CFRunLoopObserverContext context = { 0, this, 0, 0, 0 };
-    m_runLoopObserver = adoptCF(CFRunLoopObserverCreate(0, kCFRunLoopBeforeWaiting | kCFRunLoopExit, true, m_order, runLoopObserverFired, &context));
+    m_runLoopObserver = adoptCF(CFRunLoopObserverCreate(0, activity, true, m_order, runLoopObserverFired, &context));
 
     CFRunLoopAddObserver(runLoop, m_runLoopObserver.get(), kCFRunLoopCommonModes);
 }
index 08519e6d42ae7b99b5b87a7c0ca738c8c5a8cb10..2fa8f8ef5195be83827bcb1e78992b88b477ae07 100644 (file)
@@ -42,7 +42,7 @@ public:
 
     WEBCORE_EXPORT ~RunLoopObserver();
 
-    WEBCORE_EXPORT void schedule(CFRunLoopRef = nullptr);
+    WEBCORE_EXPORT void schedule(CFRunLoopRef = nullptr, CFRunLoopActivity = kCFRunLoopBeforeWaiting | kCFRunLoopExit);
     WEBCORE_EXPORT void invalidate();
 
     bool isScheduled() const { return m_runLoopObserver; }