Title: [174319] trunk/Source
Revision
174319
Author
[email protected]
Date
2014-10-04 12:18:38 -0700 (Sat, 04 Oct 2014)

Log Message

Web Inspector: timelines should not count time elapsed while paused in the debugger
https://bugs.webkit.org/show_bug.cgi?id=136351

Reviewed by Timothy Hatcher.

Source/_javascript_Core:

Now that we have a stopwatch to provide pause-aware timing data, we can remove the
profiler's handling of debugger pause/continue callbacks. The timeline agent accounts
for debugger pauses by pausing and resuming the stopwatch.

* API/JSProfilerPrivate.cpp:
(JSStartProfiling): Use a fresh stopwatch when profiling from the JSC API.
* inspector/ScriptDebugServer.cpp:
(Inspector::ScriptDebugServer::handlePause):
* profiler/LegacyProfiler.cpp:
(JSC::LegacyProfiler::profiler): Use nullptr.
(JSC::LegacyProfiler::startProfiling): Hand off a stopwatch to the profile generator.
(JSC::LegacyProfiler::stopProfiling): Use nullptr.
(JSC::LegacyProfiler::didPause): Deleted.
(JSC::LegacyProfiler::didContinue): Deleted.
* profiler/LegacyProfiler.h:
* profiler/ProfileGenerator.cpp: Remove debugger pause/continue callbacks and the
timestamp member that was used to track time elapsed by the debugger. Just use the
stopwatch's elapsed times to generate start/elapsed times for function calls.
(JSC::ProfileGenerator::create):
(JSC::ProfileGenerator::ProfileGenerator):
(JSC::ProfileGenerator::beginCallEntry):
(JSC::ProfileGenerator::endCallEntry):
(JSC::ProfileGenerator::didPause): Deleted.
(JSC::ProfileGenerator::didContinue): Deleted.
* profiler/ProfileGenerator.h:

Source/WebCore:

To avoid counting time elapsed while the debugger is paused, timeline records should
keep track of time elapsed since the start of timeline capturing, rather than wall clock
timestamps. We can easily compute elapsed time by sharing Stopwatch instance among
all timeline record-generating code. The stopwatch is paused while the debugger is paused,
so subsequent time measurements will not include time elapsed while the debugger is paused.

Agents use the shared stopwatch to generate timestamps if the timeline agent is active
(i.e., a timeline recording is being captured). If not, use a zero timestamp since the timing data is only revealed through the Timeline interface.

This refactoring is safe because start and end times are only used to graph records; the
timestamp's actual value is irrelevant and is not displayed in the user interface. Date
timestamps are still included with network-related records as part of their header data.

No new tests, because we cannot reliably test timing changes induced by debugger pauses.
It is possible for records to accrue time before the debugger pauses or after it resumes.

* inspector/InspectorCSSAgent.cpp: Remove unused include.
* inspector/InspectorPageAgent.cpp: Use timestamps from the shared stopwatch.
(WebCore::InspectorPageAgent::timestamp):
(WebCore::InspectorPageAgent::domContentEventFired):
(WebCore::InspectorPageAgent::loadEventFired):
* inspector/InspectorPageAgent.h:
* inspector/InspectorResourceAgent.cpp: Use timestamps from the shared stopwatch.
(WebCore::InspectorResourceAgent::timestamp):
(WebCore::InspectorResourceAgent::willSendRequest):
(WebCore::InspectorResourceAgent::didReceiveResponse):
(WebCore::InspectorResourceAgent::didReceiveData):
(WebCore::InspectorResourceAgent::didFinishLoading):
(WebCore::InspectorResourceAgent::didFailLoading):
(WebCore::InspectorResourceAgent::didLoadResourceFromMemoryCache):
(WebCore::InspectorResourceAgent::willSendWebSocketHandshakeRequest):
(WebCore::InspectorResourceAgent::didReceiveWebSocketHandshakeResponse):
(WebCore::InspectorResourceAgent::didCloseWebSocket):
(WebCore::InspectorResourceAgent::didReceiveWebSocketFrame):
(WebCore::InspectorResourceAgent::didSendWebSocketFrame):
(WebCore::InspectorResourceAgent::didReceiveWebSocketFrameError):
* inspector/InspectorResourceAgent.h:
* inspector/InspectorTimelineAgent.cpp: Add calls to reset, start, and stop the stopwatch.
(WebCore::InspectorTimelineAgent::didCreateFrontendAndBackend):
(WebCore::InspectorTimelineAgent::internalStart):
(WebCore::InspectorTimelineAgent::internalStop):
(WebCore::startProfiling):
(WebCore::InspectorTimelineAgent::startFromConsole):
(WebCore::InspectorTimelineAgent::willCallFunction):
(WebCore::InspectorTimelineAgent::willEvaluateScript):
(WebCore::InspectorTimelineAgent::didPause):
(WebCore::InspectorTimelineAgent::didContinue):
(WebCore::InspectorTimelineAgent::InspectorTimelineAgent):
(WebCore::InspectorTimelineAgent::timestamp):
(WebCore::TimelineTimeConverter::reset): Deleted.
* inspector/InspectorTimelineAgent.h: Make timestamp() public, and remove old timepieces.
(WebCore::TimelineTimeConverter::TimelineTimeConverter): Deleted.
(WebCore::TimelineTimeConverter::fromMonotonicallyIncreasingTime): Deleted.
(WebCore::InspectorTimelineAgent::timeConverter): Deleted.
* inspector/TimelineRecordFactory.cpp:

Source/WebInspectorUI:

Don't update the timeline's current time when the debugger is paused.

Start and end times for timeline records are now in seconds elapsed since timeline
recording started, rather than milliseconds since the epoch. Add a workaround to
preserve compatibility with old backends.

* UserInterface/Controllers/TimelineManager.js:
(WebInspector.TimelineManager.prototype.capturingStarted):
(WebInspector.TimelineManager.prototype.eventRecorded.processRecord):
(WebInspector.TimelineManager.prototype.eventRecorded):
* UserInterface/Views/TimelineContentView.js:
(WebInspector.TimelineContentView.prototype._debuggerPaused):
(WebInspector.TimelineContentView.prototype._debuggerResumed):

Source/WTF:

* WTF.vcxproj/WTF.vcxproj:
* WTF.vcxproj/WTF.vcxproj.filters:
* WTF.xcodeproj/project.pbxproj:
* wtf/CMakeLists.txt:
* wtf/Stopwatch.h: Added. This implements a refcounted monotonic stopwatch.
(WTF::Stopwatch::reset):
(WTF::Stopwatch::start):
(WTF::Stopwatch::stop):

Modified Paths

Added Paths

Diff

Modified: trunk/Source/_javascript_Core/API/JSProfilerPrivate.cpp (174318 => 174319)


--- trunk/Source/_javascript_Core/API/JSProfilerPrivate.cpp	2014-10-04 17:18:25 UTC (rev 174318)
+++ trunk/Source/_javascript_Core/API/JSProfilerPrivate.cpp	2014-10-04 19:18:38 UTC (rev 174319)
@@ -34,7 +34,7 @@
 
 void JSStartProfiling(JSContextRef ctx, JSStringRef title)
 {
-    LegacyProfiler::profiler()->startProfiling(toJS(ctx), title->string());
+    LegacyProfiler::profiler()->startProfiling(toJS(ctx), title->string(), Stopwatch::create());
 }
 
 void JSEndProfiling(JSContextRef ctx, JSStringRef title)

Modified: trunk/Source/_javascript_Core/ChangeLog (174318 => 174319)


--- trunk/Source/_javascript_Core/ChangeLog	2014-10-04 17:18:25 UTC (rev 174318)
+++ trunk/Source/_javascript_Core/ChangeLog	2014-10-04 19:18:38 UTC (rev 174319)
@@ -1,3 +1,36 @@
+2014-10-04  Brian J. Burg  <[email protected]>
+
+        Web Inspector: timelines should not count time elapsed while paused in the debugger
+        https://bugs.webkit.org/show_bug.cgi?id=136351
+
+        Reviewed by Timothy Hatcher.
+
+        Now that we have a stopwatch to provide pause-aware timing data, we can remove the
+        profiler's handling of debugger pause/continue callbacks. The timeline agent accounts
+        for debugger pauses by pausing and resuming the stopwatch.
+
+        * API/JSProfilerPrivate.cpp:
+        (JSStartProfiling): Use a fresh stopwatch when profiling from the JSC API.
+        * inspector/ScriptDebugServer.cpp:
+        (Inspector::ScriptDebugServer::handlePause):
+        * profiler/LegacyProfiler.cpp:
+        (JSC::LegacyProfiler::profiler): Use nullptr.
+        (JSC::LegacyProfiler::startProfiling): Hand off a stopwatch to the profile generator.
+        (JSC::LegacyProfiler::stopProfiling): Use nullptr.
+        (JSC::LegacyProfiler::didPause): Deleted.
+        (JSC::LegacyProfiler::didContinue): Deleted.
+        * profiler/LegacyProfiler.h:
+        * profiler/ProfileGenerator.cpp: Remove debugger pause/continue callbacks and the
+        timestamp member that was used to track time elapsed by the debugger. Just use the
+        stopwatch's elapsed times to generate start/elapsed times for function calls.
+        (JSC::ProfileGenerator::create):
+        (JSC::ProfileGenerator::ProfileGenerator):
+        (JSC::ProfileGenerator::beginCallEntry):
+        (JSC::ProfileGenerator::endCallEntry):
+        (JSC::ProfileGenerator::didPause): Deleted.
+        (JSC::ProfileGenerator::didContinue): Deleted.
+        * profiler/ProfileGenerator.h:
+
 2014-10-04  Filip Pizlo  <[email protected]>
 
         FTL should sink PutLocals

Modified: trunk/Source/_javascript_Core/inspector/ScriptDebugServer.cpp (174318 => 174319)


--- trunk/Source/_javascript_Core/inspector/ScriptDebugServer.cpp	2014-10-04 17:18:25 UTC (rev 174318)
+++ trunk/Source/_javascript_Core/inspector/ScriptDebugServer.cpp	2014-10-04 19:18:38 UTC (rev 174319)
@@ -38,7 +38,6 @@
 #include "JSJavaScriptCallFrame.h"
 #include "JSLock.h"
 #include "_javascript_CallFrame.h"
-#include "LegacyProfiler.h"
 #include "ScriptValue.h"
 #include "SourceProvider.h"
 #include <wtf/NeverDestroyed.h>
@@ -307,14 +306,12 @@
 void ScriptDebugServer::handlePause(Debugger::ReasonForPause, JSGlobalObject* vmEntryGlobalObject)
 {
     dispatchFunctionToListeners(&ScriptDebugServer::dispatchDidPause);
-    LegacyProfiler::profiler()->didPause(currentDebuggerCallFrame());
     didPause(vmEntryGlobalObject);
 
     m_doneProcessingDebuggerEvents = false;
     runEventLoopWhilePaused();
 
     didContinue(vmEntryGlobalObject);
-    LegacyProfiler::profiler()->didContinue(currentDebuggerCallFrame());
     dispatchFunctionToListeners(&ScriptDebugServer::dispatchDidContinue);
 }
 

Modified: trunk/Source/_javascript_Core/profiler/LegacyProfiler.cpp (174318 => 174319)


--- trunk/Source/_javascript_Core/profiler/LegacyProfiler.cpp	2014-10-04 17:18:25 UTC (rev 174318)
+++ trunk/Source/_javascript_Core/profiler/LegacyProfiler.cpp	2014-10-04 19:18:38 UTC (rev 174319)
@@ -32,7 +32,6 @@
 #include "CallFrame.h"
 #include "CodeBlock.h"
 #include "CommonIdentifiers.h"
-#include "DebuggerCallFrame.h"
 #include "InternalFunction.h"
 #include "JSFunction.h"
 #include "JSGlobalObject.h"
@@ -50,16 +49,16 @@
 
 static CallIdentifier createCallIdentifierFromFunctionImp(ExecState*, JSObject*, const String& defaultSourceURL, unsigned defaultLineNumber, unsigned defaultColumnNumber);
 
-LegacyProfiler* LegacyProfiler::s_sharedLegacyProfiler = 0;
+LegacyProfiler* LegacyProfiler::s_sharedLegacyProfiler = nullptr;
 
 LegacyProfiler* LegacyProfiler::profiler()
 {
     if (!s_sharedLegacyProfiler)
         s_sharedLegacyProfiler = new LegacyProfiler();
     return s_sharedLegacyProfiler;
-}   
+}
 
-void LegacyProfiler::startProfiling(ExecState* exec, const String& title)
+void LegacyProfiler::startProfiling(ExecState* exec, const String& title, PassRefPtr<Stopwatch> stopwatch)
 {
     if (!exec)
         return;
@@ -75,14 +74,14 @@
     }
 
     exec->vm().setEnabledProfiler(this);
-    RefPtr<ProfileGenerator> profileGenerator = ProfileGenerator::create(exec, title, ++ProfilesUID);
+    RefPtr<ProfileGenerator> profileGenerator = ProfileGenerator::create(exec, title, ++ProfilesUID, stopwatch);
     m_currentProfiles.append(profileGenerator);
 }
 
 PassRefPtr<Profile> LegacyProfiler::stopProfiling(ExecState* exec, const String& title)
 {
     if (!exec)
-        return 0;
+        return nullptr;
 
     JSGlobalObject* origin = exec->lexicalGlobalObject();
     for (ptrdiff_t i = m_currentProfiles.size() - 1; i >= 0; --i) {
@@ -94,12 +93,12 @@
             m_currentProfiles.remove(i);
             if (!m_currentProfiles.size())
                 exec->vm().setEnabledProfiler(nullptr);
-            
+
             return returnProfile;
         }
     }
 
-    return 0;
+    return nullptr;
 }
 
 void LegacyProfiler::stopProfiling(JSGlobalObject* origin)
@@ -184,28 +183,6 @@
     callFunctionForProfilesWithGroup(std::bind(&ProfileGenerator::exceptionUnwind, std::placeholders::_1, handlerCallFrame, callIdentifier), m_currentProfiles, handlerCallFrame->lexicalGlobalObject()->profileGroup());
 }
 
-void LegacyProfiler::didPause(PassRefPtr<DebuggerCallFrame> prpCallFrame)
-{
-    if (m_currentProfiles.isEmpty())
-        return;
-
-    RefPtr<DebuggerCallFrame> callFrame = prpCallFrame;
-    CallIdentifier callIdentifier = createCallIdentifier(callFrame->exec(), JSValue(), StringImpl::empty(), 0, 0);
-
-    callFunctionForProfilesWithGroup(std::bind(&ProfileGenerator::didPause, std::placeholders::_1, callFrame, callIdentifier), m_currentProfiles, callFrame->vmEntryGlobalObject()->profileGroup());
-}
-
-void LegacyProfiler::didContinue(PassRefPtr<DebuggerCallFrame> prpCallFrame)
-{
-    if (m_currentProfiles.isEmpty())
-        return;
-
-    RefPtr<DebuggerCallFrame> callFrame = prpCallFrame;
-    CallIdentifier callIdentifier = createCallIdentifier(callFrame->exec(), JSValue(), StringImpl::empty(), 0, 0);
-
-    callFunctionForProfilesWithGroup(std::bind(&ProfileGenerator::didContinue, std::placeholders::_1, callFrame, callIdentifier), m_currentProfiles, callFrame->vmEntryGlobalObject()->profileGroup());
-}
-
 CallIdentifier LegacyProfiler::createCallIdentifier(ExecState* exec, JSValue functionValue, const String& defaultSourceURL, unsigned defaultLineNumber, unsigned defaultColumnNumber)
 {
     if (!functionValue)

Modified: trunk/Source/_javascript_Core/profiler/LegacyProfiler.h (174318 => 174319)


--- trunk/Source/_javascript_Core/profiler/LegacyProfiler.h	2014-10-04 17:18:25 UTC (rev 174318)
+++ trunk/Source/_javascript_Core/profiler/LegacyProfiler.h	2014-10-04 19:18:38 UTC (rev 174319)
@@ -32,13 +32,12 @@
 #include "Profile.h"
 #include <wtf/PassRefPtr.h>
 #include <wtf/RefPtr.h>
+#include <wtf/Stopwatch.h>
 #include <wtf/Vector.h>
 
 namespace JSC {
 
-class DebuggerCallFrame;
 class ExecState;
-class VM;
 class JSGlobalObject;
 class JSObject;
 class JSValue;
@@ -48,10 +47,10 @@
 class LegacyProfiler {
     WTF_MAKE_FAST_ALLOCATED;
 public:
-    JS_EXPORT_PRIVATE static LegacyProfiler* profiler(); 
+    JS_EXPORT_PRIVATE static LegacyProfiler* profiler();
     static CallIdentifier createCallIdentifier(ExecState*, JSValue, const WTF::String& sourceURL, unsigned defaultLineNumber, unsigned defaultColumnNumber);
 
-    JS_EXPORT_PRIVATE void startProfiling(ExecState*, const WTF::String& title);
+    JS_EXPORT_PRIVATE void startProfiling(ExecState*, const WTF::String& title, PassRefPtr<Stopwatch>);
     JS_EXPORT_PRIVATE PassRefPtr<Profile> stopProfiling(ExecState*, const WTF::String& title);
     void stopProfiling(JSGlobalObject*);
 
@@ -66,9 +65,6 @@
 
     void exceptionUnwind(ExecState* handlerCallFrame);
 
-    void didPause(PassRefPtr<DebuggerCallFrame>);
-    void didContinue(PassRefPtr<DebuggerCallFrame>);
-
     const Vector<RefPtr<ProfileGenerator>>& currentProfiles() { return m_currentProfiles; };
 
 private:

Modified: trunk/Source/_javascript_Core/profiler/ProfileGenerator.cpp (174318 => 174319)


--- trunk/Source/_javascript_Core/profiler/ProfileGenerator.cpp	2014-10-04 17:18:25 UTC (rev 174318)
+++ trunk/Source/_javascript_Core/profiler/ProfileGenerator.cpp	2014-10-04 19:18:38 UTC (rev 174319)
@@ -28,7 +28,6 @@
 
 #include "CallFrame.h"
 #include "CodeBlock.h"
-#include "Debugger.h"
 #include "JSGlobalObject.h"
 #include "JSStringRef.h"
 #include "JSFunction.h"
@@ -40,21 +39,18 @@
 
 namespace JSC {
 
-PassRefPtr<ProfileGenerator> ProfileGenerator::create(ExecState* exec, const String& title, unsigned uid)
+PassRefPtr<ProfileGenerator> ProfileGenerator::create(ExecState* exec, const String& title, unsigned uid, PassRefPtr<Stopwatch> stopwatch)
 {
-    return adoptRef(new ProfileGenerator(exec, title, uid));
+    return adoptRef(new ProfileGenerator(exec, title, uid, stopwatch));
 }
 
-ProfileGenerator::ProfileGenerator(ExecState* exec, const String& title, unsigned uid)
+ProfileGenerator::ProfileGenerator(ExecState* exec, const String& title, unsigned uid, PassRefPtr<Stopwatch> stopwatch)
     : m_origin(exec ? exec->lexicalGlobalObject() : nullptr)
     , m_profileGroup(exec ? exec->lexicalGlobalObject()->profileGroup() : 0)
-    , m_debuggerPausedTimestamp(NAN)
+    , m_stopwatch(stopwatch)
     , m_foundConsoleStartParent(false)
     , m_suspended(false)
 {
-    if (Debugger* debugger = exec->lexicalGlobalObject()->debugger())
-        m_debuggerPausedTimestamp = debugger->isPaused() ? currentTime() : NAN;
-
     m_profile = Profile::create(title, uid);
     m_currentNode = m_rootNode = m_profile->rootNode();
     if (exec)
@@ -118,13 +114,8 @@
     ASSERT_ARG(node, node);
 
     if (isnan(startTime))
-        startTime = currentTime();
+        startTime = m_stopwatch->elapsedTime();
 
-    // If the debugger is paused when beginning, then don't set the start time. It
-    // will be fixed up when the debugger unpauses or the call entry ends.
-    if (!isnan(m_debuggerPausedTimestamp))
-        startTime = NAN;
-
     node->appendCall(ProfileNode::Call(startTime));
 }
 
@@ -133,22 +124,8 @@
     ASSERT_ARG(node, node);
 
     ProfileNode::Call& last = node->lastCall();
-
-    // If the debugger is paused, ignore the interval that ends now.
-    if (!isnan(m_debuggerPausedTimestamp) && !isnan(last.elapsedTime()))
-        return;
-
-    // If paused and no time was accrued then the debugger was never unpaused. The call will
-    // have no time accrued and appear to have started when the debugger was paused.
-    if (!isnan(m_debuggerPausedTimestamp)) {
-        last.setStartTime(m_debuggerPausedTimestamp);
-        last.setElapsedTime(0.0);
-        return;
-    }
-
-    // Otherwise, add the interval ending now to elapsed time.
     double previousElapsedTime = isnan(last.elapsedTime()) ? 0.0 : last.elapsedTime();
-    double newlyElapsedTime = currentTime() - last.startTime();
+    double newlyElapsedTime = m_stopwatch->elapsedTime() - last.startTime();
     last.setElapsedTime(previousElapsedTime + newlyElapsedTime);
 }
 
@@ -223,33 +200,6 @@
     }
 }
 
-void ProfileGenerator::didPause(PassRefPtr<DebuggerCallFrame>, const CallIdentifier&)
-{
-    ASSERT(isnan(m_debuggerPausedTimestamp));
-
-    m_debuggerPausedTimestamp = currentTime();
-
-    for (ProfileNode* node = m_currentNode.get(); node != m_profile->rootNode(); node = node->parent()) {
-        ProfileNode::Call& last = node->lastCall();
-        ASSERT(!isnan(last.startTime()));
-
-        double previousElapsedTime = isnan(last.elapsedTime()) ? 0.0 : last.elapsedTime();
-        double additionalElapsedTime = m_debuggerPausedTimestamp - last.startTime();
-        last.setStartTime(NAN);
-        last.setElapsedTime(previousElapsedTime + additionalElapsedTime);
-    }
-}
-
-void ProfileGenerator::didContinue(PassRefPtr<DebuggerCallFrame>, const CallIdentifier&)
-{
-    ASSERT(!isnan(m_debuggerPausedTimestamp));
-
-    for (ProfileNode* node = m_currentNode.get(); node != m_profile->rootNode(); node = node->parent())
-        node->lastCall().setStartTime(m_debuggerPausedTimestamp);
-
-    m_debuggerPausedTimestamp = NAN;
-}
-
 void ProfileGenerator::stopProfiling()
 {
     for (ProfileNode* node = m_currentNode.get(); node != m_profile->rootNode(); node = node->parent())

Modified: trunk/Source/_javascript_Core/profiler/ProfileGenerator.h (174318 => 174319)


--- trunk/Source/_javascript_Core/profiler/ProfileGenerator.h	2014-10-04 17:18:25 UTC (rev 174318)
+++ trunk/Source/_javascript_Core/profiler/ProfileGenerator.h	2014-10-04 19:18:38 UTC (rev 174319)
@@ -29,6 +29,7 @@
 #include <wtf/PassRefPtr.h>
 #include <wtf/RefCounted.h>
 #include <wtf/RefPtr.h>
+#include <wtf/Stopwatch.h>
 #include <wtf/text/WTFString.h>
 
 namespace JSC {
@@ -42,7 +43,7 @@
 
     class ProfileGenerator : public RefCounted<ProfileGenerator>  {
     public:
-        static PassRefPtr<ProfileGenerator> create(ExecState*, const WTF::String& title, unsigned uid);
+        static PassRefPtr<ProfileGenerator> create(ExecState*, const WTF::String& title, unsigned uid, PassRefPtr<Stopwatch>);
 
         // Members
         const WTF::String& title() const;
@@ -54,15 +55,12 @@
         void didExecute(ExecState* callerCallFrame, const CallIdentifier&);
         void exceptionUnwind(ExecState* handlerCallFrame, const CallIdentifier&);
 
-        void didPause(PassRefPtr<DebuggerCallFrame>, const CallIdentifier&);
-        void didContinue(PassRefPtr<DebuggerCallFrame>, const CallIdentifier&);
-
         void setIsSuspended(bool suspended) { ASSERT(m_suspended != suspended); m_suspended = suspended; }
 
         void stopProfiling();
 
     private:
-        ProfileGenerator(ExecState*, const WTF::String& title, unsigned uid);
+        ProfileGenerator(ExecState*, const WTF::String& title, unsigned uid, PassRefPtr<Stopwatch>);
         void addParentForConsoleStart(ExecState*);
 
         void removeProfileStart();
@@ -74,8 +72,7 @@
         RefPtr<Profile> m_profile;
         JSGlobalObject* m_origin;
         unsigned m_profileGroup;
-        // Timestamp is set to NAN when the debugger is not currently paused.
-        double m_debuggerPausedTimestamp;
+        RefPtr<Stopwatch> m_stopwatch;
         RefPtr<ProfileNode> m_rootNode;
         RefPtr<ProfileNode> m_currentNode;
         bool m_foundConsoleStartParent;

Modified: trunk/Source/WTF/ChangeLog (174318 => 174319)


--- trunk/Source/WTF/ChangeLog	2014-10-04 17:18:25 UTC (rev 174318)
+++ trunk/Source/WTF/ChangeLog	2014-10-04 19:18:38 UTC (rev 174319)
@@ -1,3 +1,19 @@
+2014-10-04  Brian J. Burg  <[email protected]>
+
+        Web Inspector: timelines should not count time elapsed while paused in the debugger
+        https://bugs.webkit.org/show_bug.cgi?id=136351
+
+        Reviewed by Timothy Hatcher.
+
+        * WTF.vcxproj/WTF.vcxproj:
+        * WTF.vcxproj/WTF.vcxproj.filters:
+        * WTF.xcodeproj/project.pbxproj:
+        * wtf/CMakeLists.txt:
+        * wtf/Stopwatch.h: Added. This implements a refcounted monotonic stopwatch.
+        (WTF::Stopwatch::reset):
+        (WTF::Stopwatch::start):
+        (WTF::Stopwatch::stop):
+
 2014-10-04  Filip Pizlo  <[email protected]>
 
         FTL should sink PutLocals

Modified: trunk/Source/WTF/WTF.vcxproj/WTF.vcxproj (174318 => 174319)


--- trunk/Source/WTF/WTF.vcxproj/WTF.vcxproj	2014-10-04 17:18:25 UTC (rev 174318)
+++ trunk/Source/WTF/WTF.vcxproj/WTF.vcxproj	2014-10-04 19:18:38 UTC (rev 174319)
@@ -269,6 +269,7 @@
     <ClInclude Include="..\wtf\StackBounds.h" />
     <ClInclude Include="..\wtf\StaticConstructors.h" />
     <ClInclude Include="..\wtf\StdLibExtras.h" />
+    <ClInclude Include="..\wtf\Stopwatch.h" />
     <ClInclude Include="..\wtf\StringExtras.h" />
     <ClInclude Include="..\wtf\StringHasher.h" />
     <ClInclude Include="..\wtf\StringPrintStream.h" />

Modified: trunk/Source/WTF/WTF.vcxproj/WTF.vcxproj.filters (174318 => 174319)


--- trunk/Source/WTF/WTF.vcxproj/WTF.vcxproj.filters	2014-10-04 17:18:25 UTC (rev 174318)
+++ trunk/Source/WTF/WTF.vcxproj/WTF.vcxproj.filters	2014-10-04 19:18:38 UTC (rev 174319)
@@ -609,6 +609,9 @@
     <ClInclude Include="..\wtf\StdLibExtras.h">
       <Filter>wtf</Filter>
     </ClInclude>
+    <ClInclude Include="..\wtf\Stopwatch.h">
+      <Filter>wtf</Filter>
+    </ClInclude>
     <ClInclude Include="..\wtf\StringExtras.h">
       <Filter>wtf</Filter>
     </ClInclude>

Modified: trunk/Source/WTF/WTF.xcodeproj/project.pbxproj (174318 => 174319)


--- trunk/Source/WTF/WTF.xcodeproj/project.pbxproj	2014-10-04 17:18:25 UTC (rev 174318)
+++ trunk/Source/WTF/WTF.xcodeproj/project.pbxproj	2014-10-04 19:18:38 UTC (rev 174319)
@@ -272,6 +272,7 @@
 		A8A47487151A825B004123FF /* WTFThreadData.h in Headers */ = {isa = PBXBuildFile; fileRef = A8A4737B151A825B004123FF /* WTFThreadData.h */; };
 		A8A4748C151A8264004123FF /* config.h in Headers */ = {isa = PBXBuildFile; fileRef = A8A4748B151A8264004123FF /* config.h */; };
 		B38FD7BD168953E80065C969 /* FeatureDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = B38FD7BC168953E80065C969 /* FeatureDefines.h */; };
+		C4F8A93719C65EB400B2B15D /* Stopwatch.h in Headers */ = {isa = PBXBuildFile; fileRef = C4F8A93619C65EB400B2B15D /* Stopwatch.h */; };
 		CD5497AC15857D0300B5BC30 /* MediaTime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD5497AA15857D0300B5BC30 /* MediaTime.cpp */; };
 		CD5497AD15857D0300B5BC30 /* MediaTime.h in Headers */ = {isa = PBXBuildFile; fileRef = CD5497AB15857D0300B5BC30 /* MediaTime.h */; };
 		CE46516E19DB1FB4003ECA05 /* NSMapTableSPI.h in Headers */ = {isa = PBXBuildFile; fileRef = CE46516D19DB1FB4003ECA05 /* NSMapTableSPI.h */; };
@@ -564,6 +565,7 @@
 		A8A4737B151A825B004123FF /* WTFThreadData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WTFThreadData.h; sourceTree = "<group>"; };
 		A8A4748B151A8264004123FF /* config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = config.h; sourceTree = "<group>"; };
 		B38FD7BC168953E80065C969 /* FeatureDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FeatureDefines.h; sourceTree = "<group>"; };
+		C4F8A93619C65EB400B2B15D /* Stopwatch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Stopwatch.h; sourceTree = "<group>"; };
 		CD5497AA15857D0300B5BC30 /* MediaTime.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MediaTime.cpp; sourceTree = "<group>"; };
 		CD5497AB15857D0300B5BC30 /* MediaTime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MediaTime.h; sourceTree = "<group>"; };
 		CE46516D19DB1FB4003ECA05 /* NSMapTableSPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSMapTableSPI.h; sourceTree = "<group>"; };
@@ -856,6 +858,7 @@
 				FEDACD3C1630F83F00C69634 /* StackStats.h */,
 				A8A47310151A825B004123FF /* StaticConstructors.h */,
 				A8A47311151A825B004123FF /* StdLibExtras.h */,
+				C4F8A93619C65EB400B2B15D /* Stopwatch.h */,
 				1A6BB768162F300500DD16DB /* StreamBuffer.h */,
 				A8A47313151A825B004123FF /* StringExtras.h */,
 				A748745117A0BDAE00FA04CB /* StringHashDumpContext.h */,
@@ -1075,6 +1078,7 @@
 				A8A473A3151A825B004123FF /* DecimalNumber.h in Headers */,
 				0F2B66A617B6B4FB00A7AE3F /* DeferrableRefCounted.h in Headers */,
 				A8A473A5151A825B004123FF /* Deque.h in Headers */,
+				C4F8A93719C65EB400B2B15D /* Stopwatch.h in Headers */,
 				A8A473A6151A825B004123FF /* DisallowCType.h in Headers */,
 				A8A473AF151A825B004123FF /* diy-fp.h in Headers */,
 				A8A473B1151A825B004123FF /* double-conversion.h in Headers */,

Modified: trunk/Source/WTF/wtf/CMakeLists.txt (174318 => 174319)


--- trunk/Source/WTF/wtf/CMakeLists.txt	2014-10-04 17:18:25 UTC (rev 174318)
+++ trunk/Source/WTF/wtf/CMakeLists.txt	2014-10-04 19:18:38 UTC (rev 174319)
@@ -92,6 +92,7 @@
     StackStats.h
     StaticConstructors.h
     StdLibExtras.h
+    Stopwatch.h
     StringExtras.h
     StringHasher.h
     StringPrintStream.h

Added: trunk/Source/WTF/wtf/Stopwatch.h (0 => 174319)


--- trunk/Source/WTF/wtf/Stopwatch.h	                        (rev 0)
+++ trunk/Source/WTF/wtf/Stopwatch.h	2014-10-04 19:18:38 UTC (rev 174319)
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2014 University of Washington. 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 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 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 Stopwatch_h
+#define Stopwatch_h
+
+#include <cmath>
+#include <wtf/CurrentTime.h>
+#include <wtf/RefCounted.h>
+
+namespace WTF {
+
+class Stopwatch : public RefCounted<Stopwatch> {
+public:
+    static PassRefPtr<Stopwatch> create()
+    {
+        return adoptRef(new Stopwatch());
+    }
+
+    void reset();
+    void start();
+    void stop();
+
+    double elapsedTime();
+
+private:
+    Stopwatch()
+        : m_elapsedTime(NAN)
+        , m_lastStartTime(NAN)
+    {
+    }
+
+    double m_elapsedTime;
+    double m_lastStartTime;
+};
+
+inline void Stopwatch::reset()
+{
+    m_elapsedTime = 0.0;
+    m_lastStartTime = NAN;
+}
+
+inline void Stopwatch::start()
+{
+    ASSERT(!isnan(m_elapsedTime) && isnan(m_lastStartTime));
+
+    m_lastStartTime = monotonicallyIncreasingTime();
+}
+
+inline void Stopwatch::stop()
+{
+    ASSERT(!isnan(m_elapsedTime) && !isnan(m_lastStartTime));
+
+    m_elapsedTime += monotonicallyIncreasingTime() - m_lastStartTime;
+    m_lastStartTime = NAN;
+}
+
+inline double Stopwatch::elapsedTime()
+{
+    bool shouldSuspend = !isnan(m_lastStartTime);
+    if (shouldSuspend)
+        stop();
+
+    ASSERT(!isnan(m_elapsedTime) && isnan(m_lastStartTime));
+    double elapsedTime = m_elapsedTime;
+
+    if (shouldSuspend)
+        start();
+    return elapsedTime;
+}
+
+} // namespace WTF
+
+using WTF::Stopwatch;
+
+#endif // Stopwatch_h

Modified: trunk/Source/WebCore/ChangeLog (174318 => 174319)


--- trunk/Source/WebCore/ChangeLog	2014-10-04 17:18:25 UTC (rev 174318)
+++ trunk/Source/WebCore/ChangeLog	2014-10-04 19:18:38 UTC (rev 174319)
@@ -1,3 +1,66 @@
+2014-10-04  Brian J. Burg  <[email protected]>
+
+        Web Inspector: timelines should not count time elapsed while paused in the debugger
+        https://bugs.webkit.org/show_bug.cgi?id=136351
+
+        Reviewed by Timothy Hatcher.
+
+        To avoid counting time elapsed while the debugger is paused, timeline records should
+        keep track of time elapsed since the start of timeline capturing, rather than wall clock
+        timestamps. We can easily compute elapsed time by sharing Stopwatch instance among
+        all timeline record-generating code. The stopwatch is paused while the debugger is paused,
+        so subsequent time measurements will not include time elapsed while the debugger is paused.
+
+        Agents use the shared stopwatch to generate timestamps if the timeline agent is active
+        (i.e., a timeline recording is being captured). If not, use a zero timestamp since the timing data is only revealed through the Timeline interface.
+
+        This refactoring is safe because start and end times are only used to graph records; the
+        timestamp's actual value is irrelevant and is not displayed in the user interface. Date
+        timestamps are still included with network-related records as part of their header data.
+
+        No new tests, because we cannot reliably test timing changes induced by debugger pauses.
+        It is possible for records to accrue time before the debugger pauses or after it resumes.
+
+        * inspector/InspectorCSSAgent.cpp: Remove unused include.
+        * inspector/InspectorPageAgent.cpp: Use timestamps from the shared stopwatch.
+        (WebCore::InspectorPageAgent::timestamp):
+        (WebCore::InspectorPageAgent::domContentEventFired):
+        (WebCore::InspectorPageAgent::loadEventFired):
+        * inspector/InspectorPageAgent.h:
+        * inspector/InspectorResourceAgent.cpp: Use timestamps from the shared stopwatch.
+        (WebCore::InspectorResourceAgent::timestamp):
+        (WebCore::InspectorResourceAgent::willSendRequest):
+        (WebCore::InspectorResourceAgent::didReceiveResponse):
+        (WebCore::InspectorResourceAgent::didReceiveData):
+        (WebCore::InspectorResourceAgent::didFinishLoading):
+        (WebCore::InspectorResourceAgent::didFailLoading):
+        (WebCore::InspectorResourceAgent::didLoadResourceFromMemoryCache):
+        (WebCore::InspectorResourceAgent::willSendWebSocketHandshakeRequest):
+        (WebCore::InspectorResourceAgent::didReceiveWebSocketHandshakeResponse):
+        (WebCore::InspectorResourceAgent::didCloseWebSocket):
+        (WebCore::InspectorResourceAgent::didReceiveWebSocketFrame):
+        (WebCore::InspectorResourceAgent::didSendWebSocketFrame):
+        (WebCore::InspectorResourceAgent::didReceiveWebSocketFrameError):
+        * inspector/InspectorResourceAgent.h:
+        * inspector/InspectorTimelineAgent.cpp: Add calls to reset, start, and stop the stopwatch.
+        (WebCore::InspectorTimelineAgent::didCreateFrontendAndBackend):
+        (WebCore::InspectorTimelineAgent::internalStart):
+        (WebCore::InspectorTimelineAgent::internalStop):
+        (WebCore::startProfiling):
+        (WebCore::InspectorTimelineAgent::startFromConsole):
+        (WebCore::InspectorTimelineAgent::willCallFunction):
+        (WebCore::InspectorTimelineAgent::willEvaluateScript):
+        (WebCore::InspectorTimelineAgent::didPause):
+        (WebCore::InspectorTimelineAgent::didContinue):
+        (WebCore::InspectorTimelineAgent::InspectorTimelineAgent):
+        (WebCore::InspectorTimelineAgent::timestamp):
+        (WebCore::TimelineTimeConverter::reset): Deleted.
+        * inspector/InspectorTimelineAgent.h: Make timestamp() public, and remove old timepieces.
+        (WebCore::TimelineTimeConverter::TimelineTimeConverter): Deleted.
+        (WebCore::TimelineTimeConverter::fromMonotonicallyIncreasingTime): Deleted.
+        (WebCore::InspectorTimelineAgent::timeConverter): Deleted.
+        * inspector/TimelineRecordFactory.cpp:
+
 2014-10-04  Tim Horton  <[email protected]>
 
         Make it possible to test page overlays

Modified: trunk/Source/WebCore/inspector/InspectorCSSAgent.cpp (174318 => 174319)


--- trunk/Source/WebCore/inspector/InspectorCSSAgent.cpp	2014-10-04 17:18:25 UTC (rev 174318)
+++ trunk/Source/WebCore/inspector/InspectorCSSAgent.cpp	2014-10-04 19:18:38 UTC (rev 174319)
@@ -57,7 +57,6 @@
 #include "StyleSheetList.h"
 #include "WebKitNamedFlow.h"
 #include <inspector/InspectorValues.h>
-#include <wtf/CurrentTime.h>
 #include <wtf/HashSet.h>
 #include <wtf/Ref.h>
 #include <wtf/Vector.h>

Modified: trunk/Source/WebCore/inspector/InspectorPageAgent.cpp (174318 => 174319)


--- trunk/Source/WebCore/inspector/InspectorPageAgent.cpp	2014-10-04 17:18:25 UTC (rev 174318)
+++ trunk/Source/WebCore/inspector/InspectorPageAgent.cpp	2014-10-04 19:18:38 UTC (rev 174319)
@@ -57,6 +57,7 @@
 #include "InspectorDOMAgent.h"
 #include "InspectorInstrumentation.h"
 #include "InspectorOverlay.h"
+#include "InspectorTimelineAgent.h"
 #include "InspectorWebFrontendDispatchers.h"
 #include "InstrumentingAgents.h"
 #include "MainFrame.h"
@@ -73,7 +74,6 @@
 #include <inspector/ContentSearchUtilities.h>
 #include <inspector/IdentifiersFactory.h>
 #include <inspector/InspectorValues.h>
-#include <wtf/CurrentTime.h>
 #include <wtf/ListHashSet.h>
 #include <wtf/Vector.h>
 #include <wtf/text/Base64.h>
@@ -361,6 +361,16 @@
 #endif
 }
 
+double InspectorPageAgent::timestamp()
+{
+    // If the timeline agent is recording now, use its stopwatch. Otherwise, just report 0.0
+    // since the timestamps are only used to accurately graph records on the timeline.
+    if (InspectorTimelineAgent* timelineAgent = m_instrumentingAgents->inspectorTimelineAgent())
+        return timelineAgent->timestamp();
+
+    return 0.0;
+}
+
 void InspectorPageAgent::enable(ErrorString&)
 {
     m_enabled = true;
@@ -752,12 +762,12 @@
 void InspectorPageAgent::domContentEventFired()
 {
     m_isFirstLayoutAfterOnLoad = true;
-    m_frontendDispatcher->domContentEventFired(currentTime());
+    m_frontendDispatcher->domContentEventFired(timestamp());
 }
 
 void InspectorPageAgent::loadEventFired()
 {
-    m_frontendDispatcher->loadEventFired(currentTime());
+    m_frontendDispatcher->loadEventFired(timestamp());
 }
 
 void InspectorPageAgent::frameNavigated(DocumentLoader* loader)

Modified: trunk/Source/WebCore/inspector/InspectorPageAgent.h (174318 => 174319)


--- trunk/Source/WebCore/inspector/InspectorPageAgent.h	2014-10-04 17:18:25 UTC (rev 174318)
+++ trunk/Source/WebCore/inspector/InspectorPageAgent.h	2014-10-04 19:18:38 UTC (rev 174319)
@@ -167,6 +167,8 @@
     void updateTouchEventEmulationInPage(bool);
 #endif
 
+    double timestamp();
+
     static bool mainResourceContent(Frame*, bool withBase64Encode, String* result);
     static bool dataContent(const char* data, unsigned size, const String& textEncodingName, bool withBase64Encode, String* result);
 

Modified: trunk/Source/WebCore/inspector/InspectorResourceAgent.cpp (174318 => 174319)


--- trunk/Source/WebCore/inspector/InspectorResourceAgent.cpp	2014-10-04 17:18:25 UTC (rev 174318)
+++ trunk/Source/WebCore/inspector/InspectorResourceAgent.cpp	2014-10-04 19:18:38 UTC (rev 174319)
@@ -47,6 +47,7 @@
 #include "IconController.h"
 #include "InspectorClient.h"
 #include "InspectorPageAgent.h"
+#include "InspectorTimelineAgent.h"
 #include "InspectorWebFrontendDispatchers.h"
 #include "InstrumentingAgents.h"
 #include "JSMainThreadExecState.h"
@@ -69,7 +70,6 @@
 #include <inspector/InspectorValues.h>
 #include <inspector/ScriptCallStack.h>
 #include <inspector/ScriptCallStackFactory.h>
-#include <wtf/CurrentTime.h>
 #include <wtf/RefPtr.h>
 #include <wtf/text/StringBuilder.h>
 
@@ -276,6 +276,16 @@
     ASSERT(!m_instrumentingAgents->inspectorResourceAgent());
 }
 
+double InspectorResourceAgent::timestamp()
+{
+    // If the timeline agent is recording now, use its stopwatch. Otherwise, just report 0.0
+    // since the timestamps are only used to accurately graph records on the timeline.
+    if (InspectorTimelineAgent* timelineAgent = m_instrumentingAgents->inspectorTimelineAgent())
+        return timelineAgent->timestamp();
+
+    return 0.0;
+}
+
 void InspectorResourceAgent::willSendRequest(unsigned long identifier, DocumentLoader* loader, ResourceRequest& request, const ResourceResponse& redirectResponse)
 {
     if (request.hiddenFromInspector()) {
@@ -320,7 +330,7 @@
     Inspector::Protocol::Page::ResourceType resourceType = InspectorPageAgent::resourceTypeJson(type);
 
     RefPtr<Inspector::Protocol::Network::Initiator> initiatorObject = buildInitiatorObject(loader->frame() ? loader->frame()->document() : nullptr);
-    m_frontendDispatcher->requestWillBeSent(requestId, m_pageAgent->frameId(loader->frame()), m_pageAgent->loaderId(loader), loader->url().string(), buildObjectForResourceRequest(request), currentTime(), initiatorObject, buildObjectForResourceResponse(redirectResponse, loader), type != InspectorPageAgent::OtherResource ? &resourceType : nullptr);
+    m_frontendDispatcher->requestWillBeSent(requestId, m_pageAgent->frameId(loader->frame()), m_pageAgent->loaderId(loader), loader->url().string(), buildObjectForResourceRequest(request), timestamp(), initiatorObject, buildObjectForResourceResponse(redirectResponse, loader), type != InspectorPageAgent::OtherResource ? &resourceType : nullptr);
 }
 
 void InspectorResourceAgent::markResourceAsCached(unsigned long identifier)
@@ -365,7 +375,7 @@
     m_resourcesData->responseReceived(requestId, m_pageAgent->frameId(loader->frame()), response);
     m_resourcesData->setResourceType(requestId, type);
 
-    m_frontendDispatcher->responseReceived(requestId, m_pageAgent->frameId(loader->frame()), m_pageAgent->loaderId(loader), currentTime(), InspectorPageAgent::resourceTypeJson(type), resourceResponse);
+    m_frontendDispatcher->responseReceived(requestId, m_pageAgent->frameId(loader->frame()), m_pageAgent->loaderId(loader), timestamp(), InspectorPageAgent::resourceTypeJson(type), resourceResponse);
 
     // If we revalidated the resource and got Not modified, send content length following didReceiveResponse
     // as there will be no calls to didReceiveData from the network stack.
@@ -391,7 +401,7 @@
             m_resourcesData->maybeAddResourceData(requestId, data, dataLength);
     }
 
-    m_frontendDispatcher->dataReceived(requestId, currentTime(), dataLength, encodedDataLength);
+    m_frontendDispatcher->dataReceived(requestId, timestamp(), dataLength, encodedDataLength);
 }
 
 void InspectorResourceAgent::didFinishLoading(unsigned long identifier, DocumentLoader* loader, double finishTime)
@@ -411,7 +421,7 @@
     // However, all other times passed to the Inspector are generated from the web process. Mixing
     // times from different processes can cause the finish time to be earlier than the response
     // received time due to inter-process communication lag.
-    finishTime = currentTime();
+    finishTime = timestamp();
 
     String sourceMappingURL;
     NetworkResourcesData::ResourceData const* resourceData = m_resourcesData->data(requestId);
@@ -437,7 +447,7 @@
     }
 
     bool canceled = error.isCancellation();
-    m_frontendDispatcher->loadingFailed(requestId, currentTime(), error.localizedDescription(), canceled ? &canceled : nullptr);
+    m_frontendDispatcher->loadingFailed(requestId, timestamp(), error.localizedDescription(), canceled ? &canceled : nullptr);
 }
 
 void InspectorResourceAgent::didLoadResourceFromMemoryCache(DocumentLoader* loader, CachedResource* resource)
@@ -456,7 +466,7 @@
 
     RefPtr<Inspector::Protocol::Network::Initiator> initiatorObject = buildInitiatorObject(loader->frame() ? loader->frame()->document() : nullptr);
 
-    m_frontendDispatcher->requestServedFromMemoryCache(requestId, frameId, loaderId, loader->url().string(), currentTime(), initiatorObject, buildObjectForCachedResource(resource, loader));
+    m_frontendDispatcher->requestServedFromMemoryCache(requestId, frameId, loaderId, loader->url().string(), timestamp(), initiatorObject, buildObjectForCachedResource(resource, loader));
 }
 
 void InspectorResourceAgent::setInitialScriptContent(unsigned long identifier, const String& sourceString)
@@ -588,7 +598,7 @@
 {
     RefPtr<Inspector::Protocol::Network::WebSocketRequest> requestObject = Inspector::Protocol::Network::WebSocketRequest::create()
         .setHeaders(buildObjectForHeaders(request.httpHeaderFields()));
-    m_frontendDispatcher->webSocketWillSendHandshakeRequest(IdentifiersFactory::requestId(identifier), currentTime(), requestObject);
+    m_frontendDispatcher->webSocketWillSendHandshakeRequest(IdentifiersFactory::requestId(identifier), timestamp(), requestObject);
 }
 
 void InspectorResourceAgent::didReceiveWebSocketHandshakeResponse(unsigned long identifier, const ResourceResponse& response)
@@ -597,12 +607,12 @@
         .setStatus(response.httpStatusCode())
         .setStatusText(response.httpStatusText())
         .setHeaders(buildObjectForHeaders(response.httpHeaderFields()));
-    m_frontendDispatcher->webSocketHandshakeResponseReceived(IdentifiersFactory::requestId(identifier), currentTime(), responseObject);
+    m_frontendDispatcher->webSocketHandshakeResponseReceived(IdentifiersFactory::requestId(identifier), timestamp(), responseObject);
 }
 
 void InspectorResourceAgent::didCloseWebSocket(unsigned long identifier)
 {
-    m_frontendDispatcher->webSocketClosed(IdentifiersFactory::requestId(identifier), currentTime());
+    m_frontendDispatcher->webSocketClosed(IdentifiersFactory::requestId(identifier), timestamp());
 }
 
 void InspectorResourceAgent::didReceiveWebSocketFrame(unsigned long identifier, const WebSocketFrame& frame)
@@ -611,7 +621,7 @@
         .setOpcode(frame.opCode)
         .setMask(frame.masked)
         .setPayloadData(String(frame.payload, frame.payloadLength));
-    m_frontendDispatcher->webSocketFrameReceived(IdentifiersFactory::requestId(identifier), currentTime(), frameObject);
+    m_frontendDispatcher->webSocketFrameReceived(IdentifiersFactory::requestId(identifier), timestamp(), frameObject);
 }
 
 void InspectorResourceAgent::didSendWebSocketFrame(unsigned long identifier, const WebSocketFrame& frame)
@@ -620,12 +630,12 @@
         .setOpcode(frame.opCode)
         .setMask(frame.masked)
         .setPayloadData(String(frame.payload, frame.payloadLength));
-    m_frontendDispatcher->webSocketFrameSent(IdentifiersFactory::requestId(identifier), currentTime(), frameObject);
+    m_frontendDispatcher->webSocketFrameSent(IdentifiersFactory::requestId(identifier), timestamp(), frameObject);
 }
 
 void InspectorResourceAgent::didReceiveWebSocketFrameError(unsigned long identifier, const String& errorMessage)
 {
-    m_frontendDispatcher->webSocketFrameError(IdentifiersFactory::requestId(identifier), currentTime(), errorMessage);
+    m_frontendDispatcher->webSocketFrameError(IdentifiersFactory::requestId(identifier), timestamp(), errorMessage);
 }
 
 #endif // ENABLE(WEB_SOCKETS)

Modified: trunk/Source/WebCore/inspector/InspectorResourceAgent.h (174318 => 174319)


--- trunk/Source/WebCore/inspector/InspectorResourceAgent.h	2014-10-04 17:18:25 UTC (rev 174318)
+++ trunk/Source/WebCore/inspector/InspectorResourceAgent.h	2014-10-04 19:18:38 UTC (rev 174319)
@@ -138,6 +138,8 @@
 private:
     void enable();
 
+    double timestamp();
+
     InspectorPageAgent* m_pageAgent;
     InspectorClient* m_client;
     std::unique_ptr<Inspector::InspectorNetworkFrontendDispatcher> m_frontendDispatcher;

Modified: trunk/Source/WebCore/inspector/InspectorTimelineAgent.cpp (174318 => 174319)


--- trunk/Source/WebCore/inspector/InspectorTimelineAgent.cpp	2014-10-04 17:18:25 UTC (rev 174318)
+++ trunk/Source/WebCore/inspector/InspectorTimelineAgent.cpp	2014-10-04 19:18:38 UTC (rev 174319)
@@ -61,11 +61,6 @@
 
 namespace WebCore {
 
-void TimelineTimeConverter::reset()
-{
-    m_startOffset = monotonicallyIncreasingTime() - currentTime();
-}
-
 InspectorTimelineAgent::~InspectorTimelineAgent()
 {
 }
@@ -76,6 +71,7 @@
     m_backendDispatcher = InspectorTimelineBackendDispatcher::create(backendDispatcher, this);
 
     m_instrumentingAgents->setPersistentInspectorTimelineAgent(this);
+    m_stopwatch->reset();
 
     if (m_scriptDebugServer)
         m_scriptDebugServer->recompileAllJSFunctions();
@@ -118,7 +114,7 @@
     else
         m_maxCallStackDepth = 5;
 
-    m_timeConverter.reset();
+    m_stopwatch->start();
 
     m_instrumentingAgents->setInspectorTimelineAgent(this);
 
@@ -136,6 +132,8 @@
     if (!m_enabled)
         return;
 
+    m_stopwatch->stop();
+
     m_instrumentingAgents->setInspectorTimelineAgent(nullptr);
 
     if (m_scriptDebugServer)
@@ -157,9 +155,9 @@
     m_scriptDebugServer = scriptDebugServer;
 }
 
-static inline void startProfiling(JSC::ExecState* exec, const String& title)
+static inline void startProfiling(JSC::ExecState* exec, const String& title, PassRefPtr<Stopwatch> stopwatch)
 {
-    JSC::LegacyProfiler::profiler()->startProfiling(exec, title);
+    JSC::LegacyProfiler::profiler()->startProfiling(exec, title, stopwatch);
 }
 
 static inline PassRefPtr<JSC::Profile> stopProfiling(JSC::ExecState* exec, const String& title)
@@ -167,9 +165,9 @@
     return JSC::LegacyProfiler::profiler()->stopProfiling(exec, title);
 }
 
-static inline void startProfiling(Frame* frame, const String& title)
+static inline void startProfiling(Frame* frame, const String& title, PassRefPtr<Stopwatch> stopwatch)
 {
-    startProfiling(toJSDOMWindow(frame, debuggerWorld())->globalExec(), title);
+    startProfiling(toJSDOMWindow(frame, debuggerWorld())->globalExec(), title, stopwatch);
 }
 
 static inline PassRefPtr<JSC::Profile> stopProfiling(Frame* frame, const String& title)
@@ -193,7 +191,7 @@
     if (!m_enabled && m_pendingConsoleProfileRecords.isEmpty())
         internalStart();
 
-    startProfiling(exec, title);
+    startProfiling(exec, title, m_stopwatch);
 
     m_pendingConsoleProfileRecords.append(createRecordEntry(TimelineRecordFactory::createConsoleProfileData(title), TimelineRecordType::ConsoleProfile, true, frameFromExecState(exec)));
 }
@@ -232,7 +230,7 @@
     pushCurrentRecord(TimelineRecordFactory::createFunctionCallData(scriptName, scriptLine), TimelineRecordType::FunctionCall, true, frame);
 
     if (frame && !m_callStackDepth)
-        startProfiling(frame, ASCIILiteral("Timeline FunctionCall"));
+        startProfiling(frame, ASCIILiteral("Timeline FunctionCall"), m_stopwatch);
 
     ++m_callStackDepth;
 }
@@ -407,7 +405,7 @@
 
     if (frame && !m_callStackDepth) {
         ++m_callStackDepth;
-        startProfiling(frame, ASCIILiteral("Timeline EvaluateScript"));
+        startProfiling(frame, ASCIILiteral("Timeline EvaluateScript"), m_stopwatch);
     }
 }
 
@@ -554,6 +552,16 @@
     appendRecord(TimelineRecordFactory::createProbeSampleData(action, hitCount), TimelineRecordType::ProbeSample, false, frameFromExecState(exec));
 }
 
+void InspectorTimelineAgent::didPause(JSC::ExecState*, const Deprecated::ScriptValue&, const Deprecated::ScriptValue&)
+{
+    m_stopwatch->stop();
+}
+
+void InspectorTimelineAgent::didContinue()
+{
+    m_stopwatch->start();
+}
+
 static Inspector::Protocol::Timeline::EventType toProtocol(TimelineRecordType type)
 {
     switch (type) {
@@ -690,6 +698,7 @@
     : InspectorAgentBase(ASCIILiteral("Timeline"), instrumentingAgents)
     , m_pageAgent(pageAgent)
     , m_scriptDebugServer(nullptr)
+    , m_stopwatch(Stopwatch::create())
     , m_id(1)
     , m_callStackDepth(0)
     , m_maxCallStackDepth(5)
@@ -748,7 +757,7 @@
 
 double InspectorTimelineAgent::timestamp()
 {
-    return m_timeConverter.fromMonotonicallyIncreasingTime(monotonicallyIncreasingTime());
+    return m_stopwatch->elapsedTime();
 }
 
 Page* InspectorTimelineAgent::page()

Modified: trunk/Source/WebCore/inspector/InspectorTimelineAgent.h (174318 => 174319)


--- trunk/Source/WebCore/inspector/InspectorTimelineAgent.h	2014-10-04 17:18:25 UTC (rev 174318)
+++ trunk/Source/WebCore/inspector/InspectorTimelineAgent.h	2014-10-04 19:18:38 UTC (rev 174319)
@@ -40,6 +40,7 @@
 #include "LayoutRect.h"
 #include <inspector/InspectorValues.h>
 #include <inspector/ScriptDebugListener.h>
+#include <wtf/Stopwatch.h>
 #include <wtf/Vector.h>
 #include <wtf/WeakPtr.h>
 
@@ -113,19 +114,6 @@
     WebSocketDestroy
 };
 
-class TimelineTimeConverter {
-public:
-    TimelineTimeConverter()
-        : m_startOffset(0)
-    {
-    }
-    double fromMonotonicallyIncreasingTime(double time) const  { return (time - m_startOffset) * 1000.0; }
-    void reset();
-
-private:
-    double m_startOffset;
-};
-
 class InspectorTimelineAgent
     : public InspectorAgentBase
     , public Inspector::InspectorTimelineBackendDispatcherHandler
@@ -210,6 +198,10 @@
     void willFireAnimationFrame(int callbackId, Frame*);
     void didFireAnimationFrame();
 
+    // Returns the elapsed time from a monotonic stopwatch that starts with timeline recording and
+    // pauses when the debugger pauses or execution is otherwise suspended.
+    double timestamp();
+
 #if ENABLE(WEB_SOCKETS)
     void didCreateWebSocket(unsigned long identifier, const URL&, const String& protocol, Frame*);
     void willSendWebSocketHandshakeRequest(unsigned long identifier, Frame*);
@@ -218,11 +210,11 @@
 #endif
 
 protected:
-    // ScriptDebugListener. This is only used to create records for probe samples.
+    // ScriptDebugListener
     virtual void didParseSource(JSC::SourceID, const Script&) override { }
     virtual void failedToParseSource(const String&, const String&, int, int, const String&) override { }
-    virtual void didPause(JSC::ExecState*, const Deprecated::ScriptValue&, const Deprecated::ScriptValue&) override { }
-    virtual void didContinue() override { }
+    virtual void didPause(JSC::ExecState*, const Deprecated::ScriptValue&, const Deprecated::ScriptValue&) override;
+    virtual void didContinue() override;
 
     virtual void breakpointActionLog(JSC::ExecState*, const String&) override { }
     virtual void breakpointActionSound(int) override { }
@@ -264,17 +256,15 @@
     void clearRecordStack();
 
     void localToPageQuad(const RenderObject&, const LayoutRect&, FloatQuad*);
-    const TimelineTimeConverter& timeConverter() const { return m_timeConverter; }
-    double timestamp();
     Page* page();
 
     InspectorPageAgent* m_pageAgent;
     PageScriptDebugServer* m_scriptDebugServer;
-    TimelineTimeConverter m_timeConverter;
 
+    RefPtr<Stopwatch> m_stopwatch;
+
     std::unique_ptr<Inspector::InspectorTimelineFrontendDispatcher> m_frontendDispatcher;
     RefPtr<Inspector::InspectorTimelineBackendDispatcher> m_backendDispatcher;
-    double m_timestampOffset;
 
     Vector<TimelineRecordEntry> m_recordStack;
 

Modified: trunk/Source/WebCore/inspector/TimelineRecordFactory.cpp (174318 => 174319)


--- trunk/Source/WebCore/inspector/TimelineRecordFactory.cpp	2014-10-04 17:18:25 UTC (rev 174318)
+++ trunk/Source/WebCore/inspector/TimelineRecordFactory.cpp	2014-10-04 19:18:38 UTC (rev 174319)
@@ -47,7 +47,6 @@
 #include <inspector/ScriptCallStack.h>
 #include <inspector/ScriptCallStackFactory.h>
 #include <profiler/Profile.h>
-#include <wtf/CurrentTime.h>
 
 using namespace Inspector;
 

Modified: trunk/Source/WebInspectorUI/ChangeLog (174318 => 174319)


--- trunk/Source/WebInspectorUI/ChangeLog	2014-10-04 17:18:25 UTC (rev 174318)
+++ trunk/Source/WebInspectorUI/ChangeLog	2014-10-04 19:18:38 UTC (rev 174319)
@@ -1,3 +1,24 @@
+2014-10-04  Brian J. Burg  <[email protected]>
+
+        Web Inspector: timelines should not count time elapsed while paused in the debugger
+        https://bugs.webkit.org/show_bug.cgi?id=136351
+
+        Reviewed by Timothy Hatcher.
+
+        Don't update the timeline's current time when the debugger is paused.
+
+        Start and end times for timeline records are now in seconds elapsed since timeline
+        recording started, rather than milliseconds since the epoch. Add a workaround to
+        preserve compatibility with old backends.
+
+        * UserInterface/Controllers/TimelineManager.js:
+        (WebInspector.TimelineManager.prototype.capturingStarted):
+        (WebInspector.TimelineManager.prototype.eventRecorded.processRecord):
+        (WebInspector.TimelineManager.prototype.eventRecorded):
+        * UserInterface/Views/TimelineContentView.js:
+        (WebInspector.TimelineContentView.prototype._debuggerPaused):
+        (WebInspector.TimelineContentView.prototype._debuggerResumed):
+
 2014-10-03  Saam Barati  <[email protected]>
 
         Web Inspector: Move the computation that results in UI strings from JSC to the Web Inspector

Modified: trunk/Source/WebInspectorUI/UserInterface/Controllers/TimelineManager.js (174318 => 174319)


--- trunk/Source/WebInspectorUI/UserInterface/Controllers/TimelineManager.js	2014-10-04 17:18:25 UTC (rev 174318)
+++ trunk/Source/WebInspectorUI/UserInterface/Controllers/TimelineManager.js	2014-10-04 19:18:38 UTC (rev 174319)
@@ -35,6 +35,8 @@
     this._activeRecording = null;
     this._isCapturing = false;
 
+    // For legacy backends, we compute the elapsed time of records relative to this timestamp.
+    this._legacyFirstRecordTimestamp = NaN;
     this._nextRecordingIdentifier = 1;
 
     this._boundStopCapturing = this.stopCapturing.bind(this);
@@ -55,6 +57,7 @@
     CapturingStopped: "timeline-manager-capturing-stopped"
 };
 
+WebInspector.TimelineManager.StartTimeThresholdForLegacyRecordConversion = 28800000; // Date.parse("Jan 1, 1970")
 WebInspector.TimelineManager.MaximumAutoRecordDuration = 90000; // 90 seconds
 WebInspector.TimelineManager.MaximumAutoRecordDurationAfterLoadEvent = 10000; // 10 seconds
 WebInspector.TimelineManager.DeadTimeRequiredToStopAutoRecordingEarly = 2000; // 2 seconds
@@ -163,10 +166,21 @@
 
         function processRecord(recordPayload, parentRecordPayload)
         {
-            // Convert the timestamps to seconds to match the resource timestamps.
-            var startTime = recordPayload.startTime / 1000;
-            var endTime = recordPayload.endTime / 1000;
+            var startTime = recordPayload.startTime;
+            var endTime = recordPayload.endTime;
 
+            // COMPATIBILITY (iOS8): old versions use milliseconds since the epoch, rather
+            // than seconds elapsed since timeline capturing started. We approximate the latter by
+            // subtracting the start timestamp, as old versions did not use monotonic times.
+            if (isNaN(this._legacyFirstRecordTimestamp))
+                this._legacyFirstRecordTimestamp = recordPayload.startTime;
+
+            // If the record's start time sems unreasonably large, treat it as a legacy timestamp.
+            if (startTime > WebInspector.StartTimeThresholdForLegacyRecordConversion) {
+                startTime = (startTime - this._legacyFirstRecordTimestamp) / 1000;
+                endTime = isNaN(endTime) ? NaN : (startTime - this._legacyFirstRecordTimestamp) / 1000;
+            }
+
             var callFrames = this._callFramesFromPayload(recordPayload.stackTrace);
 
             var significantCallFrame = null;

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/TimelineContentView.js (174318 => 174319)


--- trunk/Source/WebInspectorUI/UserInterface/Views/TimelineContentView.js	2014-10-04 17:18:25 UTC (rev 174318)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/TimelineContentView.js	2014-10-04 19:18:38 UTC (rev 174319)
@@ -98,6 +98,9 @@
     WebInspector.timelineManager.addEventListener(WebInspector.TimelineManager.Event.CapturingStarted, this._capturingStarted, this);
     WebInspector.timelineManager.addEventListener(WebInspector.TimelineManager.Event.CapturingStopped, this._capturingStopped, this);
 
+    WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.Paused, this._debuggerPaused, this);
+    WebInspector.debuggerManager.addEventListener(WebInspector.DebuggerManager.Event.Resumed, this._debuggerResumed, this);
+
     this.showOverviewTimelineView();
 };
 
@@ -407,6 +410,22 @@
         this._stopUpdatingCurrentTime();
     },
 
+    _debuggerPaused: function(event)
+    {
+        if (WebInspector.replayManager.sessionState === WebInspector.ReplayManager.SessionState.Replaying)
+            return;
+
+        this._stopUpdatingCurrentTime();
+    },
+
+    _debuggerResumed: function(event)
+    {
+        if (WebInspector.replayManager.sessionState === WebInspector.ReplayManager.SessionState.Replaying)
+            return;
+
+        this._startUpdatingCurrentTime();
+    },
+
     _recordingTimesUpdated: function(event)
     {
         if (!this._waitingToResetCurrentTime)
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to