summaryrefslogtreecommitdiff
path: root/Source/WebCore/inspector/InspectorTimelineAgent.cpp
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
commit1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch)
tree46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/inspector/InspectorTimelineAgent.cpp
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/WebCore/inspector/InspectorTimelineAgent.cpp')
-rw-r--r--Source/WebCore/inspector/InspectorTimelineAgent.cpp745
1 files changed, 423 insertions, 322 deletions
diff --git a/Source/WebCore/inspector/InspectorTimelineAgent.cpp b/Source/WebCore/inspector/InspectorTimelineAgent.cpp
index f8a6c38a3..1f0d0576d 100644
--- a/Source/WebCore/inspector/InspectorTimelineAgent.cpp
+++ b/Source/WebCore/inspector/InspectorTimelineAgent.cpp
@@ -1,5 +1,7 @@
/*
* Copyright (C) 2013 Google Inc. All rights reserved.
+* Copyright (C) 2014 University of Washington.
+* Copyright (C) 2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@@ -29,62 +31,133 @@
*/
#include "config.h"
-
-#if ENABLE(INSPECTOR)
-
#include "InspectorTimelineAgent.h"
#include "Event.h"
#include "Frame.h"
-#include "FrameView.h"
-#include "IdentifiersFactory.h"
-#include "InspectorClient.h"
-#include "InspectorCounters.h"
-#include "InspectorInstrumentation.h"
#include "InspectorMemoryAgent.h"
#include "InspectorPageAgent.h"
-#include "InspectorWebFrontendDispatchers.h"
#include "InstrumentingAgents.h"
-#include "IntRect.h"
#include "JSDOMWindow.h"
-#include "RenderElement.h"
+#include "PageScriptDebugServer.h"
#include "RenderView.h"
-#include "ResourceRequest.h"
-#include "ResourceResponse.h"
+#include "ScriptState.h"
#include "TimelineRecordFactory.h"
-#include <wtf/CurrentTime.h>
+#include "WebConsoleAgent.h"
+#include <inspector/ConsoleMessage.h>
+#include <inspector/ScriptBreakpoint.h>
+#include <inspector/agents/InspectorDebuggerAgent.h>
+#include <inspector/agents/InspectorHeapAgent.h>
+#include <inspector/agents/InspectorScriptProfilerAgent.h>
+#include <wtf/Stopwatch.h>
+
+#if PLATFORM(IOS)
+#include "RuntimeApplicationChecks.h"
+#include "WebCoreThreadInternal.h"
+#endif
+
+#if PLATFORM(COCOA)
+#include "RunLoopObserver.h"
+#endif
using namespace Inspector;
namespace WebCore {
-void TimelineTimeConverter::reset()
+#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 (IOSApplication::isIBooks())
+ return WebThreadRunLoop();
+#endif
+ return CFRunLoopGetCurrent();
+}
+#endif
+
+InspectorTimelineAgent::InspectorTimelineAgent(WebAgentContext& context, InspectorScriptProfilerAgent* scriptProfileAgent, InspectorHeapAgent* heapAgent, InspectorPageAgent* pageAgent)
+ : InspectorAgentBase(ASCIILiteral("Timeline"), context)
+ , m_frontendDispatcher(std::make_unique<Inspector::TimelineFrontendDispatcher>(context.frontendRouter))
+ , m_backendDispatcher(Inspector::TimelineBackendDispatcher::create(context.backendDispatcher, this))
+ , m_scriptProfilerAgent(scriptProfileAgent)
+ , m_heapAgent(heapAgent)
+ , m_pageAgent(pageAgent)
{
- m_startOffset = monotonicallyIncreasingTime() - currentTime();
}
InspectorTimelineAgent::~InspectorTimelineAgent()
{
}
-void InspectorTimelineAgent::didCreateFrontendAndBackend(Inspector::InspectorFrontendChannel* frontendChannel, InspectorBackendDispatcher* backendDispatcher)
+void InspectorTimelineAgent::didCreateFrontendAndBackend(Inspector::FrontendRouter*, Inspector::BackendDispatcher*)
+{
+ m_instrumentingAgents.setPersistentInspectorTimelineAgent(this);
+}
+
+void InspectorTimelineAgent::willDestroyFrontendAndBackend(Inspector::DisconnectReason)
+{
+ m_instrumentingAgents.setPersistentInspectorTimelineAgent(nullptr);
+
+ ErrorString unused;
+ stop(unused);
+
+ m_autoCaptureEnabled = false;
+ m_instruments.clear();
+}
+
+void InspectorTimelineAgent::start(ErrorString&, const int* maxCallStackDepth)
+{
+ m_enabledFromFrontend = true;
+
+ internalStart(maxCallStackDepth);
+}
+
+void InspectorTimelineAgent::stop(ErrorString&)
+{
+ internalStop();
+
+ m_enabledFromFrontend = false;
+}
+
+void InspectorTimelineAgent::setAutoCaptureEnabled(ErrorString&, bool enabled)
{
- m_frontendDispatcher = std::make_unique<InspectorTimelineFrontendDispatcher>(frontendChannel);
- m_backendDispatcher = InspectorTimelineBackendDispatcher::create(backendDispatcher, this);
+ m_autoCaptureEnabled = enabled;
}
-void InspectorTimelineAgent::willDestroyFrontendAndBackend(InspectorDisconnectReason)
+void InspectorTimelineAgent::setInstruments(ErrorString& errorString, const InspectorArray& instruments)
{
- m_frontendDispatcher = nullptr;
- m_backendDispatcher.clear();
+ Vector<Protocol::Timeline::Instrument> newInstruments;
+ newInstruments.reserveCapacity(instruments.length());
+
+ for (auto instrumentValue : instruments) {
+ String enumValueString;
+ if (!instrumentValue->asString(enumValueString)) {
+ errorString = ASCIILiteral("Unexpected type in instruments list, should be string");
+ return;
+ }
+
+ std::optional<Protocol::Timeline::Instrument> instrumentType = Protocol::InspectorHelpers::parseEnumValueFromString<Protocol::Timeline::Instrument>(enumValueString);
+ if (!instrumentType) {
+ errorString = makeString("Unexpected enum value: ", enumValueString);
+ return;
+ }
+
+ newInstruments.uncheckedAppend(*instrumentType);
+ }
- ErrorString error;
- stop(&error);
+ m_instruments.swap(newInstruments);
}
-void InspectorTimelineAgent::start(ErrorString*, const int* maxCallStackDepth, const bool* includeDomCounters)
+void InspectorTimelineAgent::internalStart(const int* maxCallStackDepth)
{
- if (!m_frontendDispatcher)
+ if (m_enabled)
return;
if (maxCallStackDepth && *maxCallStackDepth > 0)
@@ -92,46 +165,134 @@ void InspectorTimelineAgent::start(ErrorString*, const int* maxCallStackDepth, c
else
m_maxCallStackDepth = 5;
- if (includeDomCounters)
- m_includeDOMCounters = *includeDomCounters;
+ m_instrumentingAgents.setInspectorTimelineAgent(this);
- m_timeConverter.reset();
+ m_environment.scriptDebugServer().addListener(this);
- m_instrumentingAgents->setInspectorTimelineAgent(this);
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 = std::make_unique<RunLoopObserver>(0, [this]() {
+ if (!m_enabled || m_environment.scriptDebugServer().isPaused())
+ return;
+
+ if (!m_runLoopNestingLevel)
+ pushCurrentRecord(InspectorObject::create(), TimelineRecordType::RenderingFrame, false, nullptr);
+ m_runLoopNestingLevel++;
+ });
+
+ m_frameStopObserver = std::make_unique<RunLoopObserver>(frameStopRunLoopOrder, [this]() {
+ if (!m_enabled || m_environment.scriptDebugServer().isPaused())
+ return;
+
+ ASSERT(m_runLoopNestingLevel > 0);
+ m_runLoopNestingLevel--;
+ if (m_runLoopNestingLevel)
+ return;
+
+ if (m_startedComposite)
+ didComposite();
+
+ didCompleteCurrentRecord(TimelineRecordType::RenderingFrame);
+ });
+
+ m_frameStartObserver->schedule(currentRunLoop(), kCFRunLoopEntry | kCFRunLoopAfterWaiting);
+ m_frameStopObserver->schedule(currentRunLoop(), kCFRunLoopExit | kCFRunLoopBeforeWaiting);
+
+ // Create a runloop record and increment the runloop nesting level, to capture the current turn of the main runloop
+ // (which is the outer runloop if recording started while paused in the debugger).
+ pushCurrentRecord(InspectorObject::create(), TimelineRecordType::RenderingFrame, false, nullptr);
+
+ m_runLoopNestingLevel = 1;
+#endif
+
+ m_frontendDispatcher->recordingStarted(timestamp());
}
-void InspectorTimelineAgent::stop(ErrorString*)
+void InspectorTimelineAgent::internalStop()
{
if (!m_enabled)
return;
- m_weakFactory.revokeAll();
- m_instrumentingAgents->setInspectorTimelineAgent(nullptr);
+ m_instrumentingAgents.setInspectorTimelineAgent(nullptr);
+
+ m_environment.scriptDebugServer().removeListener(this, true);
+
+#if PLATFORM(COCOA)
+ m_frameStartObserver = nullptr;
+ m_frameStopObserver = nullptr;
+ m_runLoopNestingLevel = 0;
+
+ // Complete all pending records to prevent discarding events that are currently in progress.
+ while (!m_recordStack.isEmpty())
+ didCompleteCurrentRecord(m_recordStack.last().type);
+#endif
clearRecordStack();
m_enabled = false;
-}
+ m_startedComposite = false;
+ m_autoCapturePhase = AutoCapturePhase::None;
-void InspectorTimelineAgent::canMonitorMainThread(ErrorString*, bool* result)
-{
- *result = m_client && m_client->canMonitorMainThread();
+ m_frontendDispatcher->recordingStopped(timestamp());
}
-void InspectorTimelineAgent::supportsFrameInstrumentation(ErrorString*, bool* result)
+double InspectorTimelineAgent::timestamp()
{
- *result = m_client && m_client->supportsFrameInstrumentation();
+ return m_environment.executionStopwatch()->elapsedTime();
}
-void InspectorTimelineAgent::didBeginFrame()
+void InspectorTimelineAgent::startFromConsole(JSC::ExecState* exec, const String& title)
{
- m_pendingFrameRecord = TimelineRecordFactory::createGenericRecord(timestamp(), 0);
+ // Allow duplicate unnamed profiles. Disallow duplicate named profiles.
+ if (!title.isEmpty()) {
+ for (const TimelineRecordEntry& record : m_pendingConsoleProfileRecords) {
+ String recordTitle;
+ record.data->getString(ASCIILiteral("title"), recordTitle);
+ if (recordTitle == title) {
+ if (WebConsoleAgent* consoleAgent = m_instrumentingAgents.webConsoleAgent()) {
+ // FIXME: Send an enum to the frontend for localization?
+ String warning = title.isEmpty() ? ASCIILiteral("Unnamed Profile already exists") : makeString("Profile \"", title, "\" already exists");
+ consoleAgent->addMessageToConsole(std::make_unique<ConsoleMessage>(MessageSource::ConsoleAPI, MessageType::Profile, MessageLevel::Warning, warning));
+ }
+ return;
+ }
+ }
+ }
+
+ if (!m_enabled && m_pendingConsoleProfileRecords.isEmpty())
+ startProgrammaticCapture();
+
+ m_pendingConsoleProfileRecords.append(createRecordEntry(TimelineRecordFactory::createConsoleProfileData(title), TimelineRecordType::ConsoleProfile, true, frameFromExecState(exec)));
}
-void InspectorTimelineAgent::didCancelFrame()
+void InspectorTimelineAgent::stopFromConsole(JSC::ExecState*, const String& title)
{
- m_pendingFrameRecord.clear();
+ // Stop profiles in reverse order. If the title is empty, then stop the last profile.
+ // Otherwise, match the title of the profile to stop.
+ for (int i = m_pendingConsoleProfileRecords.size() - 1; i >= 0; --i) {
+ const TimelineRecordEntry& record = m_pendingConsoleProfileRecords[i];
+
+ String recordTitle;
+ record.data->getString(ASCIILiteral("title"), recordTitle);
+ if (title.isEmpty() || recordTitle == title) {
+ didCompleteRecordEntry(record);
+ m_pendingConsoleProfileRecords.remove(i);
+
+ if (!m_enabledFromFrontend && m_pendingConsoleProfileRecords.isEmpty())
+ stopProgrammaticCapture();
+
+ return;
+ }
+ }
+
+ if (WebConsoleAgent* consoleAgent = m_instrumentingAgents.webConsoleAgent()) {
+ // FIXME: Send an enum to the frontend for localization?
+ String warning = title.isEmpty() ? ASCIILiteral("No profiles exist") : makeString("Profile \"", title, "\" does not exist");
+ consoleAgent->addMessageToConsole(std::make_unique<ConsoleMessage>(MessageSource::ConsoleAPI, MessageType::ProfileEnd, MessageLevel::Warning, warning));
+ }
}
void InspectorTimelineAgent::willCallFunction(const String& scriptName, int scriptLine, Frame* frame)
@@ -139,7 +300,7 @@ void InspectorTimelineAgent::willCallFunction(const String& scriptName, int scri
pushCurrentRecord(TimelineRecordFactory::createFunctionCallData(scriptName, scriptLine), TimelineRecordType::FunctionCall, true, frame);
}
-void InspectorTimelineAgent::didCallFunction()
+void InspectorTimelineAgent::didCallFunction(Frame*)
{
didCompleteCurrentRecord(TimelineRecordType::FunctionCall);
}
@@ -154,37 +315,24 @@ void InspectorTimelineAgent::didDispatchEvent()
didCompleteCurrentRecord(TimelineRecordType::EventDispatch);
}
-void InspectorTimelineAgent::didInvalidateLayout(Frame* frame)
+void InspectorTimelineAgent::didInvalidateLayout(Frame& frame)
{
- appendRecord(InspectorObject::create(), TimelineRecordType::InvalidateLayout, true, frame);
+ appendRecord(InspectorObject::create(), TimelineRecordType::InvalidateLayout, true, &frame);
}
-void InspectorTimelineAgent::willLayout(Frame* frame)
+void InspectorTimelineAgent::willLayout(Frame& frame)
{
- RenderObject* root = frame->view()->layoutRoot();
- bool partialLayout = !!root;
-
- if (!partialLayout)
- root = frame->contentRenderer();
-
- unsigned dirtyObjects = 0;
- unsigned totalObjects = 0;
- for (RenderObject* o = root; o; o = o->nextInPreOrder(root)) {
- ++totalObjects;
- if (o->needsLayout())
- ++dirtyObjects;
- }
- pushCurrentRecord(TimelineRecordFactory::createLayoutData(dirtyObjects, totalObjects, partialLayout), TimelineRecordType::Layout, true, frame);
+ pushCurrentRecord(InspectorObject::create(), TimelineRecordType::Layout, true, &frame);
}
-void InspectorTimelineAgent::didLayout(RenderObject* root)
+void InspectorTimelineAgent::didLayout(RenderObject& root)
{
if (m_recordStack.isEmpty())
return;
TimelineRecordEntry& entry = m_recordStack.last();
ASSERT(entry.type == TimelineRecordType::Layout);
Vector<FloatQuad> quads;
- root->absoluteQuads(quads);
+ root.absoluteQuads(quads);
if (quads.size() >= 1)
TimelineRecordFactory::appendLayoutRoot(entry.data.get(), quads[0]);
else
@@ -207,56 +355,36 @@ void InspectorTimelineAgent::didRecalculateStyle()
didCompleteCurrentRecord(TimelineRecordType::RecalculateStyles);
}
-void InspectorTimelineAgent::willPaint(Frame* frame)
+void InspectorTimelineAgent::willComposite(Frame& frame)
{
- pushCurrentRecord(InspectorObject::create(), TimelineRecordType::Paint, true, frame);
-}
-
-void InspectorTimelineAgent::didPaint(RenderObject* renderer, const LayoutRect& clipRect)
-{
- TimelineRecordEntry& entry = m_recordStack.last();
- ASSERT(entry.type == TimelineRecordType::Paint);
- FloatQuad quad;
- localToPageQuad(*renderer, clipRect, &quad);
- entry.data = TimelineRecordFactory::createPaintData(quad);
- didCompleteCurrentRecord(TimelineRecordType::Paint);
-}
-
-void InspectorTimelineAgent::willScroll(Frame* frame)
-{
- pushCurrentRecord(InspectorObject::create(), TimelineRecordType::ScrollLayer, false, frame);
-}
-
-void InspectorTimelineAgent::didScroll()
-{
- didCompleteCurrentRecord(TimelineRecordType::ScrollLayer);
-}
-
-void InspectorTimelineAgent::willComposite()
-{
- pushCurrentRecord(InspectorObject::create(), TimelineRecordType::CompositeLayers, false, nullptr);
+ ASSERT(!m_startedComposite);
+ pushCurrentRecord(InspectorObject::create(), TimelineRecordType::Composite, true, &frame);
+ m_startedComposite = true;
}
void InspectorTimelineAgent::didComposite()
{
- didCompleteCurrentRecord(TimelineRecordType::CompositeLayers);
+ ASSERT(m_startedComposite);
+ didCompleteCurrentRecord(TimelineRecordType::Composite);
+ m_startedComposite = false;
}
-void InspectorTimelineAgent::willWriteHTML(unsigned startLine, Frame* frame)
+void InspectorTimelineAgent::willPaint(Frame& frame)
{
- pushCurrentRecord(TimelineRecordFactory::createParseHTMLData(startLine), TimelineRecordType::ParseHTML, true, frame);
+ pushCurrentRecord(InspectorObject::create(), TimelineRecordType::Paint, true, &frame);
}
-void InspectorTimelineAgent::didWriteHTML(unsigned endLine)
+void InspectorTimelineAgent::didPaint(RenderObject& renderer, const LayoutRect& clipRect)
{
- if (!m_recordStack.isEmpty()) {
- TimelineRecordEntry entry = m_recordStack.last();
- entry.data->setNumber("endLine", endLine);
- didCompleteCurrentRecord(TimelineRecordType::ParseHTML);
- }
+ TimelineRecordEntry& entry = m_recordStack.last();
+ ASSERT(entry.type == TimelineRecordType::Paint);
+ FloatQuad quad;
+ localToPageQuad(renderer, clipRect, &quad);
+ entry.data = TimelineRecordFactory::createPaintData(quad);
+ didCompleteCurrentRecord(TimelineRecordType::Paint);
}
-void InspectorTimelineAgent::didInstallTimer(int timerId, int timeout, bool singleShot, Frame* frame)
+void InspectorTimelineAgent::didInstallTimer(int timerId, std::chrono::milliseconds timeout, bool singleShot, Frame* frame)
{
appendRecord(TimelineRecordFactory::createTimerInstallData(timerId, timeout, singleShot), TimelineRecordType::TimerInstall, true, frame);
}
@@ -276,99 +404,174 @@ void InspectorTimelineAgent::didFireTimer()
didCompleteCurrentRecord(TimelineRecordType::TimerFire);
}
-void InspectorTimelineAgent::willDispatchXHRReadyStateChangeEvent(const String& url, int readyState, Frame* frame)
+void InspectorTimelineAgent::willEvaluateScript(const String& url, int lineNumber, Frame& frame)
{
- pushCurrentRecord(TimelineRecordFactory::createXHRReadyStateChangeData(url, readyState), TimelineRecordType::XHRReadyStateChange, false, frame);
+ pushCurrentRecord(TimelineRecordFactory::createEvaluateScriptData(url, lineNumber), TimelineRecordType::EvaluateScript, true, &frame);
}
-void InspectorTimelineAgent::didDispatchXHRReadyStateChangeEvent()
+void InspectorTimelineAgent::didEvaluateScript(Frame&)
{
- didCompleteCurrentRecord(TimelineRecordType::XHRReadyStateChange);
+ didCompleteCurrentRecord(TimelineRecordType::EvaluateScript);
}
-void InspectorTimelineAgent::willDispatchXHRLoadEvent(const String& url, Frame* frame)
+void InspectorTimelineAgent::didTimeStamp(Frame& frame, const String& message)
{
- pushCurrentRecord(TimelineRecordFactory::createXHRLoadData(url), TimelineRecordType::XHRLoad, true, frame);
+ appendRecord(TimelineRecordFactory::createTimeStampData(message), TimelineRecordType::TimeStamp, true, &frame);
}
-void InspectorTimelineAgent::didDispatchXHRLoadEvent()
+void InspectorTimelineAgent::time(Frame& frame, const String& message)
{
- didCompleteCurrentRecord(TimelineRecordType::XHRLoad);
+ appendRecord(TimelineRecordFactory::createTimeStampData(message), TimelineRecordType::Time, true, &frame);
}
-void InspectorTimelineAgent::willEvaluateScript(const String& url, int lineNumber, Frame* frame)
+void InspectorTimelineAgent::timeEnd(Frame& frame, const String& message)
{
- pushCurrentRecord(TimelineRecordFactory::createEvaluateScriptData(url, lineNumber), TimelineRecordType::EvaluateScript, true, frame);
+ appendRecord(TimelineRecordFactory::createTimeStampData(message), TimelineRecordType::TimeEnd, true, &frame);
}
-void InspectorTimelineAgent::didEvaluateScript()
+void InspectorTimelineAgent::mainFrameStartedLoading()
{
- didCompleteCurrentRecord(TimelineRecordType::EvaluateScript);
-}
+ if (m_enabled)
+ return;
-void InspectorTimelineAgent::didScheduleResourceRequest(const String& url, Frame* frame)
-{
- appendRecord(TimelineRecordFactory::createScheduleResourceRequestData(url), TimelineRecordType::ScheduleResourceRequest, true, frame);
-}
+ if (!m_autoCaptureEnabled)
+ return;
-void InspectorTimelineAgent::willSendResourceRequest(unsigned long identifier, const ResourceRequest& request, Frame* frame)
-{
- String requestId = IdentifiersFactory::requestId(identifier);
- appendRecord(TimelineRecordFactory::createResourceSendRequestData(requestId, request), TimelineRecordType::ResourceSendRequest, true, frame);
-}
+ if (m_instruments.isEmpty())
+ return;
-void InspectorTimelineAgent::willReceiveResourceData(unsigned long identifier, Frame* frame, int length)
-{
- String requestId = IdentifiersFactory::requestId(identifier);
- pushCurrentRecord(TimelineRecordFactory::createReceiveResourceData(requestId, length), TimelineRecordType::ResourceReceivedData, false, frame);
-}
+ m_autoCapturePhase = AutoCapturePhase::BeforeLoad;
-void InspectorTimelineAgent::didReceiveResourceData()
-{
- didCompleteCurrentRecord(TimelineRecordType::ResourceReceivedData);
+ // Pre-emptively disable breakpoints. The frontend must re-enable them.
+ if (InspectorDebuggerAgent* debuggerAgent = m_instrumentingAgents.inspectorDebuggerAgent()) {
+ ErrorString unused;
+ debuggerAgent->setBreakpointsActive(unused, false);
+ }
+
+ // Inform the frontend we started an auto capture. The frontend must stop capture.
+ m_frontendDispatcher->autoCaptureStarted();
+
+ toggleInstruments(InstrumentState::Start);
}
-void InspectorTimelineAgent::willReceiveResourceResponse(unsigned long identifier, const ResourceResponse& response, Frame* frame)
+void InspectorTimelineAgent::mainFrameNavigated()
{
- String requestId = IdentifiersFactory::requestId(identifier);
- pushCurrentRecord(TimelineRecordFactory::createResourceReceiveResponseData(requestId, response), TimelineRecordType::ResourceReceiveResponse, false, frame);
+ if (m_autoCapturePhase == AutoCapturePhase::BeforeLoad) {
+ m_autoCapturePhase = AutoCapturePhase::FirstNavigation;
+ toggleInstruments(InstrumentState::Start);
+ m_autoCapturePhase = AutoCapturePhase::AfterFirstNavigation;
+ }
}
-void InspectorTimelineAgent::didReceiveResourceResponse()
+void InspectorTimelineAgent::startProgrammaticCapture()
{
- didCompleteCurrentRecord(TimelineRecordType::ResourceReceiveResponse);
+ ASSERT(!m_enabled);
+
+ // Disable breakpoints during programmatic capture.
+ if (InspectorDebuggerAgent* debuggerAgent = m_instrumentingAgents.inspectorDebuggerAgent()) {
+ m_programmaticCaptureRestoreBreakpointActiveValue = debuggerAgent->breakpointsActive();
+ if (m_programmaticCaptureRestoreBreakpointActiveValue) {
+ ErrorString unused;
+ debuggerAgent->setBreakpointsActive(unused, false);
+ }
+ } else
+ m_programmaticCaptureRestoreBreakpointActiveValue = false;
+
+ m_frontendDispatcher->programmaticCaptureStarted();
+
+ toggleScriptProfilerInstrument(InstrumentState::Start); // Ensure JavaScript samping data.
+ toggleTimelineInstrument(InstrumentState::Start); // Ensure Console Profile event records.
+ toggleInstruments(InstrumentState::Start); // Any other instruments the frontend wants us to record.
}
-void InspectorTimelineAgent::didFinishLoadingResource(unsigned long identifier, bool didFail, double finishTime, Frame* frame)
+void InspectorTimelineAgent::stopProgrammaticCapture()
{
- appendRecord(TimelineRecordFactory::createResourceFinishData(IdentifiersFactory::requestId(identifier), didFail, finishTime * 1000), TimelineRecordType::ResourceFinish, false, frame);
+ ASSERT(m_enabled);
+ ASSERT(!m_enabledFromFrontend);
+
+ toggleInstruments(InstrumentState::Stop);
+ toggleTimelineInstrument(InstrumentState::Stop);
+ toggleScriptProfilerInstrument(InstrumentState::Stop);
+
+ // Re-enable breakpoints if they were enabled.
+ if (m_programmaticCaptureRestoreBreakpointActiveValue) {
+ if (InspectorDebuggerAgent* debuggerAgent = m_instrumentingAgents.inspectorDebuggerAgent()) {
+ ErrorString unused;
+ debuggerAgent->setBreakpointsActive(unused, true);
+ }
+ }
+
+ m_frontendDispatcher->programmaticCaptureStopped();
}
-void InspectorTimelineAgent::didTimeStamp(Frame* frame, const String& message)
+void InspectorTimelineAgent::toggleInstruments(InstrumentState state)
{
- appendRecord(TimelineRecordFactory::createTimeStampData(message), TimelineRecordType::TimeStamp, true, frame);
+ for (auto instrumentType : m_instruments) {
+ switch (instrumentType) {
+ case Inspector::Protocol::Timeline::Instrument::ScriptProfiler: {
+ toggleScriptProfilerInstrument(state);
+ break;
+ }
+ case Inspector::Protocol::Timeline::Instrument::Heap: {
+ toggleHeapInstrument(state);
+ break;
+ }
+ case Inspector::Protocol::Timeline::Instrument::Memory: {
+ toggleMemoryInstrument(state);
+ break;
+ }
+ case Inspector::Protocol::Timeline::Instrument::Timeline:
+ toggleTimelineInstrument(state);
+ break;
+ }
+ }
}
-void InspectorTimelineAgent::time(Frame* frame, const String& message)
+void InspectorTimelineAgent::toggleScriptProfilerInstrument(InstrumentState state)
{
- appendRecord(TimelineRecordFactory::createTimeStampData(message), TimelineRecordType::Time, true, frame);
+ if (m_scriptProfilerAgent) {
+ ErrorString unused;
+ if (state == InstrumentState::Start) {
+ const bool includeSamples = true;
+ m_scriptProfilerAgent->startTracking(unused, &includeSamples);
+ } else
+ m_scriptProfilerAgent->stopTracking(unused);
+ }
}
-void InspectorTimelineAgent::timeEnd(Frame* frame, const String& message)
+void InspectorTimelineAgent::toggleHeapInstrument(InstrumentState state)
{
- appendRecord(TimelineRecordFactory::createTimeStampData(message), TimelineRecordType::TimeEnd, true, frame);
+ if (m_heapAgent) {
+ ErrorString unused;
+ if (state == InstrumentState::Start) {
+ if (m_autoCapturePhase == AutoCapturePhase::None || m_autoCapturePhase == AutoCapturePhase::FirstNavigation)
+ m_heapAgent->startTracking(unused);
+ } else
+ m_heapAgent->stopTracking(unused);
+ }
}
-void InspectorTimelineAgent::didMarkDOMContentEvent(Frame* frame)
+void InspectorTimelineAgent::toggleMemoryInstrument(InstrumentState state)
{
- bool isMainFrame = frame && m_pageAgent && (frame == m_pageAgent->mainFrame());
- appendRecord(TimelineRecordFactory::createMarkData(isMainFrame), TimelineRecordType::MarkDOMContent, false, frame);
+#if ENABLE(RESOURCE_USAGE)
+ if (InspectorMemoryAgent* memoryAgent = m_instrumentingAgents.inspectorMemoryAgent()) {
+ ErrorString unused;
+ if (state == InstrumentState::Start)
+ memoryAgent->startTracking(unused);
+ else
+ memoryAgent->stopTracking(unused);
+ }
+#else
+ UNUSED_PARAM(state);
+#endif
}
-void InspectorTimelineAgent::didMarkLoadEvent(Frame* frame)
+void InspectorTimelineAgent::toggleTimelineInstrument(InstrumentState state)
{
- bool isMainFrame = frame && m_pageAgent && (frame == m_pageAgent->mainFrame());
- appendRecord(TimelineRecordFactory::createMarkData(isMainFrame), TimelineRecordType::MarkLoad, false, frame);
+ if (state == InstrumentState::Start)
+ internalStart();
+ else
+ internalStop();
}
void InspectorTimelineAgent::didCommitLoad()
@@ -396,160 +599,83 @@ void InspectorTimelineAgent::didFireAnimationFrame()
didCompleteCurrentRecord(TimelineRecordType::FireAnimationFrame);
}
-#if ENABLE(WEB_SOCKETS)
-void InspectorTimelineAgent::didCreateWebSocket(unsigned long identifier, const URL& url, const String& protocol, Frame* frame)
-{
- appendRecord(TimelineRecordFactory::createWebSocketCreateData(identifier, url, protocol), TimelineRecordType::WebSocketCreate, true, frame);
-}
+// ScriptDebugListener
-void InspectorTimelineAgent::willSendWebSocketHandshakeRequest(unsigned long identifier, Frame* frame)
+void InspectorTimelineAgent::breakpointActionProbe(JSC::ExecState& state, const Inspector::ScriptBreakpointAction& action, unsigned /*batchId*/, unsigned sampleId, JSC::JSValue)
{
- appendRecord(TimelineRecordFactory::createGenericWebSocketData(identifier), TimelineRecordType::WebSocketSendHandshakeRequest, true, frame);
+ appendRecord(TimelineRecordFactory::createProbeSampleData(action, sampleId), TimelineRecordType::ProbeSample, false, frameFromExecState(&state));
}
-void InspectorTimelineAgent::didReceiveWebSocketHandshakeResponse(unsigned long identifier, Frame* frame)
-{
- appendRecord(TimelineRecordFactory::createGenericWebSocketData(identifier), TimelineRecordType::WebSocketReceiveHandshakeResponse, false, frame);
-}
-
-void InspectorTimelineAgent::didDestroyWebSocket(unsigned long identifier, Frame* frame)
-{
- appendRecord(TimelineRecordFactory::createGenericWebSocketData(identifier), TimelineRecordType::WebSocketDestroy, true, frame);
-}
-#endif // ENABLE(WEB_SOCKETS)
-
-void InspectorTimelineAgent::addRecordToTimeline(PassRefPtr<InspectorObject> record, TimelineRecordType type)
-{
- commitFrameRecord();
- innerAddRecordToTimeline(record, type);
-}
-
-static Inspector::TypeBuilder::Timeline::EventType::Enum toProtocol(TimelineRecordType type)
+static Inspector::Protocol::Timeline::EventType toProtocol(TimelineRecordType type)
{
switch (type) {
case TimelineRecordType::EventDispatch:
- return Inspector::TypeBuilder::Timeline::EventType::EventDispatch;
- case TimelineRecordType::BeginFrame:
- return Inspector::TypeBuilder::Timeline::EventType::BeginFrame;
+ return Inspector::Protocol::Timeline::EventType::EventDispatch;
case TimelineRecordType::ScheduleStyleRecalculation:
- return Inspector::TypeBuilder::Timeline::EventType::ScheduleStyleRecalculation;
+ return Inspector::Protocol::Timeline::EventType::ScheduleStyleRecalculation;
case TimelineRecordType::RecalculateStyles:
- return Inspector::TypeBuilder::Timeline::EventType::RecalculateStyles;
+ return Inspector::Protocol::Timeline::EventType::RecalculateStyles;
case TimelineRecordType::InvalidateLayout:
- return Inspector::TypeBuilder::Timeline::EventType::InvalidateLayout;
+ return Inspector::Protocol::Timeline::EventType::InvalidateLayout;
case TimelineRecordType::Layout:
- return Inspector::TypeBuilder::Timeline::EventType::Layout;
+ return Inspector::Protocol::Timeline::EventType::Layout;
case TimelineRecordType::Paint:
- return Inspector::TypeBuilder::Timeline::EventType::Paint;
- case TimelineRecordType::ScrollLayer:
- return Inspector::TypeBuilder::Timeline::EventType::ScrollLayer;
- case TimelineRecordType::ResizeImage:
- return Inspector::TypeBuilder::Timeline::EventType::ResizeImage;
- case TimelineRecordType::CompositeLayers:
- return Inspector::TypeBuilder::Timeline::EventType::CompositeLayers;
-
- case TimelineRecordType::ParseHTML:
- return Inspector::TypeBuilder::Timeline::EventType::ParseHTML;
+ return Inspector::Protocol::Timeline::EventType::Paint;
+ case TimelineRecordType::Composite:
+ return Inspector::Protocol::Timeline::EventType::Composite;
+ case TimelineRecordType::RenderingFrame:
+ return Inspector::Protocol::Timeline::EventType::RenderingFrame;
case TimelineRecordType::TimerInstall:
- return Inspector::TypeBuilder::Timeline::EventType::TimerInstall;
+ return Inspector::Protocol::Timeline::EventType::TimerInstall;
case TimelineRecordType::TimerRemove:
- return Inspector::TypeBuilder::Timeline::EventType::TimerRemove;
+ return Inspector::Protocol::Timeline::EventType::TimerRemove;
case TimelineRecordType::TimerFire:
- return Inspector::TypeBuilder::Timeline::EventType::TimerFire;
+ return Inspector::Protocol::Timeline::EventType::TimerFire;
case TimelineRecordType::EvaluateScript:
- return Inspector::TypeBuilder::Timeline::EventType::EvaluateScript;
-
- case TimelineRecordType::MarkLoad:
- return Inspector::TypeBuilder::Timeline::EventType::MarkLoad;
- case TimelineRecordType::MarkDOMContent:
- return Inspector::TypeBuilder::Timeline::EventType::MarkDOMContent;
+ return Inspector::Protocol::Timeline::EventType::EvaluateScript;
case TimelineRecordType::TimeStamp:
- return Inspector::TypeBuilder::Timeline::EventType::TimeStamp;
+ return Inspector::Protocol::Timeline::EventType::TimeStamp;
case TimelineRecordType::Time:
- return Inspector::TypeBuilder::Timeline::EventType::Time;
+ return Inspector::Protocol::Timeline::EventType::Time;
case TimelineRecordType::TimeEnd:
- return Inspector::TypeBuilder::Timeline::EventType::TimeEnd;
-
- case TimelineRecordType::ScheduleResourceRequest:
- return Inspector::TypeBuilder::Timeline::EventType::ScheduleResourceRequest;
- case TimelineRecordType::ResourceSendRequest:
- return Inspector::TypeBuilder::Timeline::EventType::ResourceSendRequest;
- case TimelineRecordType::ResourceReceiveResponse:
- return Inspector::TypeBuilder::Timeline::EventType::ResourceReceiveResponse;
- case TimelineRecordType::ResourceReceivedData:
- return Inspector::TypeBuilder::Timeline::EventType::ResourceReceivedData;
- case TimelineRecordType::ResourceFinish:
- return Inspector::TypeBuilder::Timeline::EventType::ResourceFinish;
-
- case TimelineRecordType::XHRReadyStateChange:
- return Inspector::TypeBuilder::Timeline::EventType::XHRReadyStateChange;
- case TimelineRecordType::XHRLoad:
- return Inspector::TypeBuilder::Timeline::EventType::XHRLoad;
+ return Inspector::Protocol::Timeline::EventType::TimeEnd;
case TimelineRecordType::FunctionCall:
- return Inspector::TypeBuilder::Timeline::EventType::FunctionCall;
+ return Inspector::Protocol::Timeline::EventType::FunctionCall;
+ case TimelineRecordType::ProbeSample:
+ return Inspector::Protocol::Timeline::EventType::ProbeSample;
+ case TimelineRecordType::ConsoleProfile:
+ return Inspector::Protocol::Timeline::EventType::ConsoleProfile;
case TimelineRecordType::RequestAnimationFrame:
- return Inspector::TypeBuilder::Timeline::EventType::RequestAnimationFrame;
+ return Inspector::Protocol::Timeline::EventType::RequestAnimationFrame;
case TimelineRecordType::CancelAnimationFrame:
- return Inspector::TypeBuilder::Timeline::EventType::CancelAnimationFrame;
+ return Inspector::Protocol::Timeline::EventType::CancelAnimationFrame;
case TimelineRecordType::FireAnimationFrame:
- return Inspector::TypeBuilder::Timeline::EventType::FireAnimationFrame;
-
- case TimelineRecordType::WebSocketCreate:
- return Inspector::TypeBuilder::Timeline::EventType::WebSocketCreate;
- case TimelineRecordType::WebSocketSendHandshakeRequest:
- return Inspector::TypeBuilder::Timeline::EventType::WebSocketSendHandshakeRequest;
- case TimelineRecordType::WebSocketReceiveHandshakeResponse:
- return Inspector::TypeBuilder::Timeline::EventType::WebSocketReceiveHandshakeResponse;
- case TimelineRecordType::WebSocketDestroy:
- return Inspector::TypeBuilder::Timeline::EventType::WebSocketDestroy;
+ return Inspector::Protocol::Timeline::EventType::FireAnimationFrame;
}
- return Inspector::TypeBuilder::Timeline::EventType::TimeStamp;
+ return Inspector::Protocol::Timeline::EventType::TimeStamp;
}
-void InspectorTimelineAgent::innerAddRecordToTimeline(PassRefPtr<InspectorObject> prpRecord, TimelineRecordType type)
+void InspectorTimelineAgent::addRecordToTimeline(RefPtr<InspectorObject>&& record, TimelineRecordType type)
{
- prpRecord->setString("type", Inspector::TypeBuilder::getWebEnumConstantValue(toProtocol(type)));
+ ASSERT_ARG(record, record);
+ record->setString("type", Inspector::Protocol::InspectorHelpers::getEnumConstantValue(toProtocol(type)));
- RefPtr<Inspector::TypeBuilder::Timeline::TimelineEvent> record = Inspector::TypeBuilder::Timeline::TimelineEvent::runtimeCast(prpRecord);
+ if (m_recordStack.isEmpty()) {
+ auto recordObject = BindingTraits<Inspector::Protocol::Timeline::TimelineEvent>::runtimeCast(WTFMove(record));
+ sendEvent(WTFMove(recordObject));
+ } else {
+ const TimelineRecordEntry& parent = m_recordStack.last();
+ // Nested paint records are an implementation detail and add no information not already contained in the parent.
+ if (type == TimelineRecordType::Paint && parent.type == type)
+ return;
- setDOMCounters(record.get());
-
- if (m_recordStack.isEmpty())
- sendEvent(record.release());
- else {
- TimelineRecordEntry parent = m_recordStack.last();
- parent.children->pushObject(record.release());
- }
-}
-
-static size_t usedHeapSize()
-{
- return JSDOMWindow::commonVM()->heap.size();
-}
-
-void InspectorTimelineAgent::setDOMCounters(Inspector::TypeBuilder::Timeline::TimelineEvent* record)
-{
- record->setUsedHeapSize(usedHeapSize());
-
- if (m_includeDOMCounters) {
- int documentCount = 0;
- int nodeCount = 0;
- if (m_inspectorType == PageInspector) {
- documentCount = InspectorCounters::counterValue(InspectorCounters::DocumentCounter);
- nodeCount = InspectorCounters::counterValue(InspectorCounters::NodeCounter);
- }
- int listenerCount = ThreadLocalInspectorCounters::current().counterValue(ThreadLocalInspectorCounters::JSEventListenerCounter);
- RefPtr<Inspector::TypeBuilder::Timeline::DOMCounters> counters = Inspector::TypeBuilder::Timeline::DOMCounters::create()
- .setDocuments(documentCount)
- .setNodes(nodeCount)
- .setJsEventListeners(listenerCount);
- record->setCounters(counters.release());
+ parent.children->pushObject(WTFMove(record));
}
}
@@ -563,6 +689,14 @@ void InspectorTimelineAgent::setFrameIdentifier(InspectorObject* record, Frame*
record->setString("frameId", frameId);
}
+void InspectorTimelineAgent::didCompleteRecordEntry(const TimelineRecordEntry& entry)
+{
+ entry.record->setObject(ASCIILiteral("data"), entry.data);
+ entry.record->setArray(ASCIILiteral("children"), entry.children);
+ entry.record->setDouble(ASCIILiteral("endTime"), timestamp());
+ addRecordToTimeline(entry.record.copyRef(), entry.type);
+}
+
void InspectorTimelineAgent::didCompleteCurrentRecord(TimelineRecordType type)
{
// An empty stack could merely mean that the timeline agent was turned on in the middle of
@@ -570,66 +704,45 @@ void InspectorTimelineAgent::didCompleteCurrentRecord(TimelineRecordType type)
if (!m_recordStack.isEmpty()) {
TimelineRecordEntry entry = m_recordStack.last();
m_recordStack.removeLast();
- ASSERT(entry.type == type);
- entry.record->setObject("data", entry.data);
- entry.record->setArray("children", entry.children);
- entry.record->setNumber("endTime", timestamp());
- size_t usedHeapSizeDelta = usedHeapSize() - entry.usedHeapSizeAtStart;
- if (usedHeapSizeDelta)
- entry.record->setNumber("usedHeapSizeDelta", usedHeapSizeDelta);
- addRecordToTimeline(entry.record, type);
- }
-}
+ ASSERT_UNUSED(type, entry.type == type);
-InspectorTimelineAgent::InspectorTimelineAgent(InstrumentingAgents* instrumentingAgents, InspectorPageAgent* pageAgent, InspectorMemoryAgent* memoryAgent, InspectorType type, InspectorClient* client)
- : InspectorAgentBase(ASCIILiteral("Timeline"), instrumentingAgents)
- , m_pageAgent(pageAgent)
- , m_memoryAgent(memoryAgent)
- , m_id(1)
- , m_maxCallStackDepth(5)
- , m_inspectorType(type)
- , m_client(client)
- , m_weakFactory(this)
- , m_enabled(false)
- , m_includeDOMCounters(false)
-{
+ // Don't send RenderingFrame records that have no children to reduce noise.
+ if (entry.type == TimelineRecordType::RenderingFrame && !entry.children->length())
+ return;
+
+ didCompleteRecordEntry(entry);
+ }
}
-void InspectorTimelineAgent::appendRecord(PassRefPtr<InspectorObject> data, TimelineRecordType type, bool captureCallStack, Frame* frame)
+void InspectorTimelineAgent::appendRecord(RefPtr<InspectorObject>&& data, TimelineRecordType type, bool captureCallStack, Frame* frame)
{
- RefPtr<InspectorObject> record = TimelineRecordFactory::createGenericRecord(timestamp(), captureCallStack ? m_maxCallStackDepth : 0);
- record->setObject("data", data);
- setFrameIdentifier(record.get(), frame);
- addRecordToTimeline(record.release(), type);
+ Ref<InspectorObject> record = TimelineRecordFactory::createGenericRecord(timestamp(), captureCallStack ? m_maxCallStackDepth : 0);
+ record->setObject("data", WTFMove(data));
+ setFrameIdentifier(&record.get(), frame);
+ addRecordToTimeline(WTFMove(record), type);
}
-void InspectorTimelineAgent::sendEvent(PassRefPtr<InspectorObject> event)
+void InspectorTimelineAgent::sendEvent(RefPtr<InspectorObject>&& event)
{
// FIXME: runtimeCast is a hack. We do it because we can't build TimelineEvent directly now.
- RefPtr<Inspector::TypeBuilder::Timeline::TimelineEvent> recordChecked = Inspector::TypeBuilder::Timeline::TimelineEvent::runtimeCast(event);
- m_frontendDispatcher->eventRecorded(recordChecked.release());
+ auto recordChecked = BindingTraits<Inspector::Protocol::Timeline::TimelineEvent>::runtimeCast(WTFMove(event));
+ m_frontendDispatcher->eventRecorded(WTFMove(recordChecked));
}
-void InspectorTimelineAgent::pushCurrentRecord(PassRefPtr<InspectorObject> data, TimelineRecordType type, bool captureCallStack, Frame* frame)
+InspectorTimelineAgent::TimelineRecordEntry InspectorTimelineAgent::createRecordEntry(RefPtr<InspectorObject>&& data, TimelineRecordType type, bool captureCallStack, Frame* frame)
{
- commitFrameRecord();
- RefPtr<InspectorObject> record = TimelineRecordFactory::createGenericRecord(timestamp(), captureCallStack ? m_maxCallStackDepth : 0);
- setFrameIdentifier(record.get(), frame);
- m_recordStack.append(TimelineRecordEntry(record.release(), data, InspectorArray::create(), type, usedHeapSize()));
+ Ref<InspectorObject> record = TimelineRecordFactory::createGenericRecord(timestamp(), captureCallStack ? m_maxCallStackDepth : 0);
+ setFrameIdentifier(&record.get(), frame);
+ return TimelineRecordEntry(WTFMove(record), WTFMove(data), InspectorArray::create(), type);
}
-void InspectorTimelineAgent::commitFrameRecord()
+void InspectorTimelineAgent::pushCurrentRecord(RefPtr<InspectorObject>&& data, TimelineRecordType type, bool captureCallStack, Frame* frame)
{
- if (!m_pendingFrameRecord)
- return;
-
- m_pendingFrameRecord->setObject("data", InspectorObject::create());
- innerAddRecordToTimeline(m_pendingFrameRecord.release(), TimelineRecordType::BeginFrame);
+ pushCurrentRecord(createRecordEntry(WTFMove(data), type, captureCallStack, frame));
}
void InspectorTimelineAgent::clearRecordStack()
{
- m_pendingFrameRecord.clear();
m_recordStack.clear();
m_id++;
}
@@ -644,16 +757,4 @@ void InspectorTimelineAgent::localToPageQuad(const RenderObject& renderer, const
quad->setP4(frameView.contentsToRootView(roundedIntPoint(absolute.p4())));
}
-double InspectorTimelineAgent::timestamp()
-{
- return m_timeConverter.fromMonotonicallyIncreasingTime(monotonicallyIncreasingTime());
-}
-
-Page* InspectorTimelineAgent::page()
-{
- return m_pageAgent ? m_pageAgent->page() : nullptr;
-}
-
} // namespace WebCore
-
-#endif // ENABLE(INSPECTOR)