WebKit Bugzilla
New
Browse
Search+
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
[patch]
[PATCH] Proposed Fix
ScriptProfiler-1.patch (text/plain), 84.13 KB, created by
Joseph Pecoraro
on 2015-12-09 18:40:39 PST
(
hide
)
Description:
[PATCH] Proposed Fix
Filename:
MIME Type:
Creator:
Joseph Pecoraro
Created:
2015-12-09 18:40:39 PST
Size:
84.13 KB
patch
obsolete
>diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog >index b832909..646f0ce 100644 >--- a/LayoutTests/ChangeLog >+++ b/LayoutTests/ChangeLog >@@ -1,3 +1,18 @@ >+2015-12-09 Joseph Pecoraro <pecoraro@apple.com> >+ >+ Web Inspector: Add JSContext Script Profiling >+ https://bugs.webkit.org/show_bug.cgi?id=151899 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ * inspector/script-profiler/event-type-API-expected.txt: Added. >+ * inspector/script-profiler/event-type-API.html: Added. >+ * inspector/script-profiler/event-type-Microtask-expected.txt: Added. >+ * inspector/script-profiler/event-type-Microtask.html: Added. >+ * inspector/script-profiler/tracking-expected.txt: Added. >+ * inspector/script-profiler/tracking.html: Added. >+ Tests for the new ScriptProfiler domain. >+ > 2015-12-08 Mark Lam <mark.lam@apple.com> > > Polymorphic operand types for DFG and FTL div. >diff --git a/LayoutTests/inspector/script-profiler/event-type-API-expected.txt b/LayoutTests/inspector/script-profiler/event-type-API-expected.txt >new file mode 100644 >index 0000000..5118048 >--- /dev/null >+++ b/LayoutTests/inspector/script-profiler/event-type-API-expected.txt >@@ -0,0 +1,13 @@ >+CONSOLE MESSAGE: line 1: Log from API Evaluated Script >+Tests for generating a ScriptProfiler.Event with ScriptProfiler.EventType.API type. >+ >+ >+== Running test suite: ScriptProfiler.EventType.API >+-- Running test case: TrackingSession >+ScriptProfiler.trackingStart >+ScriptProfiler.trackingUpdate >+PASS: Event type should be API. >+ScriptProfiler.trackingComplete >+PASS: Should be profiles when complete. >+PASS: Should be 1 profile for this session. >+ >diff --git a/LayoutTests/inspector/script-profiler/event-type-API.html b/LayoutTests/inspector/script-profiler/event-type-API.html >new file mode 100644 >index 0000000..864ea33 >--- /dev/null >+++ b/LayoutTests/inspector/script-profiler/event-type-API.html >@@ -0,0 +1,50 @@ >+<!doctype html> >+<html> >+<head> >+<script src="../../http/tests/inspector/resources/protocol-test.js"></script> >+<script> >+function triggerAPIScript() { >+ testRunner.evaluateScriptInIsolatedWorld(0, "console.log('Log from API Evaluated Script')"); >+} >+ >+function test() >+{ >+ let suite = ProtocolTest.createAsyncSuite("ScriptProfiler.EventType.API"); >+ >+ suite.addTestCase({ >+ name: "TrackingSession", >+ description: "Start a tracking session that includes an API event type.", >+ test: function(resolve, reject) { >+ InspectorProtocol.eventHandler["ScriptProfiler.trackingStart"] = (messageObject) => { >+ ProtocolTest.log("ScriptProfiler.trackingStart"); >+ } >+ >+ InspectorProtocol.eventHandler["ScriptProfiler.trackingUpdate"] = (messageObject) => { >+ ProtocolTest.log("ScriptProfiler.trackingUpdate"); >+ ProtocolTest.expectThat(messageObject.params.event.type === "API", "Event type should be API."); >+ } >+ >+ InspectorProtocol.eventHandler["ScriptProfiler.trackingComplete"] = (messageObject) => { >+ ProtocolTest.log("ScriptProfiler.trackingComplete"); >+ ProtocolTest.expectThat(Array.isArray(messageObject.params.profiles), "Should be profiles when complete."); >+ ProtocolTest.expectThat(messageObject.params.profiles.length === 1, "Should be 1 profile for this session."); >+ resolve(); >+ } >+ >+ InspectorProtocol.sendCommand("ScriptProfiler.startTracking", {profile: true}); >+ ProtocolTest.evaluateInPage("triggerAPIScript()"); // This ultimately uses the JSEvaluateScript API on the Page's context. >+ InspectorProtocol.sendCommand("ScriptProfiler.stopTracking", {}); >+ } >+ }); >+ >+ // FIXME: Debugger should not need to be enabled for ScriptProfiling to work. >+ InspectorProtocol.sendCommand("Debugger.enable", {}); >+ >+ suite.runTestCasesAndFinish(); >+} >+</script> >+</head> >+<body onload="runTest()"> >+<p>Tests for generating a ScriptProfiler.Event with ScriptProfiler.EventType.API type.</p> >+</body> >+</html> >diff --git a/LayoutTests/inspector/script-profiler/event-type-Microtask-expected.txt b/LayoutTests/inspector/script-profiler/event-type-Microtask-expected.txt >new file mode 100644 >index 0000000..5871d9a >--- /dev/null >+++ b/LayoutTests/inspector/script-profiler/event-type-Microtask-expected.txt >@@ -0,0 +1,13 @@ >+CONSOLE MESSAGE: line 8: Promise Microtask Fired >+Tests for generating a ScriptProfiler.Event with ScriptProfiler.EventType.Microtask type. >+ >+ >+== Running test suite: ScriptProfiler.EventType.Microtask >+-- Running test case: TrackingSession >+ScriptProfiler.trackingStart >+ScriptProfiler.trackingUpdate >+PASS: Event type should be Microtask. >+ScriptProfiler.trackingComplete >+PASS: Should be profiles when complete. >+PASS: Should be 1 profile for this session. >+ >diff --git a/LayoutTests/inspector/script-profiler/event-type-Microtask.html b/LayoutTests/inspector/script-profiler/event-type-Microtask.html >new file mode 100644 >index 0000000..704b12f >--- /dev/null >+++ b/LayoutTests/inspector/script-profiler/event-type-Microtask.html >@@ -0,0 +1,52 @@ >+<!doctype html> >+<html> >+<head> >+<script src="../../http/tests/inspector/resources/protocol-test.js"></script> >+<script> >+function triggerMicrotask() { >+ Promise.resolve(100).then(function() { >+ console.log("Promise Microtask Fired"); >+ }); >+} >+ >+function test() >+{ >+ let suite = ProtocolTest.createAsyncSuite("ScriptProfiler.EventType.Microtask"); >+ >+ suite.addTestCase({ >+ name: "TrackingSession", >+ description: "Start a tracking session that includes a Microtask event type.", >+ test: function(resolve, reject) { >+ InspectorProtocol.eventHandler["ScriptProfiler.trackingStart"] = (messageObject) => { >+ ProtocolTest.log("ScriptProfiler.trackingStart"); >+ } >+ >+ InspectorProtocol.eventHandler["ScriptProfiler.trackingUpdate"] = (messageObject) => { >+ ProtocolTest.log("ScriptProfiler.trackingUpdate"); >+ ProtocolTest.expectThat(messageObject.params.event.type === "Microtask", "Event type should be Microtask."); >+ } >+ >+ InspectorProtocol.eventHandler["ScriptProfiler.trackingComplete"] = (messageObject) => { >+ ProtocolTest.log("ScriptProfiler.trackingComplete"); >+ ProtocolTest.expectThat(Array.isArray(messageObject.params.profiles), "Should be profiles when complete."); >+ ProtocolTest.expectThat(messageObject.params.profiles.length === 1, "Should be 1 profile for this session."); >+ resolve(); >+ } >+ >+ InspectorProtocol.sendCommand("ScriptProfiler.startTracking", {profile: true}); >+ ProtocolTest.evaluateInPage("triggerMicrotask()"); >+ InspectorProtocol.sendCommand("ScriptProfiler.stopTracking", {}); >+ } >+ }); >+ >+ // FIXME: Debugger should not need to be enabled for ScriptProfiling to work. >+ InspectorProtocol.sendCommand("Debugger.enable", {}); >+ >+ suite.runTestCasesAndFinish(); >+} >+</script> >+</head> >+<body onload="runTest()"> >+<p>Tests for generating a ScriptProfiler.Event with ScriptProfiler.EventType.Microtask type.</p> >+</body> >+</html> >diff --git a/LayoutTests/inspector/script-profiler/tracking-expected.txt b/LayoutTests/inspector/script-profiler/tracking-expected.txt >new file mode 100644 >index 0000000..6667503 >--- /dev/null >+++ b/LayoutTests/inspector/script-profiler/tracking-expected.txt >@@ -0,0 +1,11 @@ >+Tests that ScriptProfiler.startTracking and ScriptProfiler.stopTracking trigger trackingStart and trackingComplete events. >+ >+ >+== Running test suite: ScriptProfiler.startTracking and ScriptProfiler.stopTracking >+-- Running test case: StartAndStopTracking >+ScriptProfiler.trackingStart >+PASS: Should have a timestamp when starting. >+ScriptProfiler.trackingComplete >+PASS: Should be profiles when complete. >+PASS: Should be no profiles for this session. >+ >diff --git a/LayoutTests/inspector/script-profiler/tracking.html b/LayoutTests/inspector/script-profiler/tracking.html >new file mode 100644 >index 0000000..74085ea >--- /dev/null >+++ b/LayoutTests/inspector/script-profiler/tracking.html >@@ -0,0 +1,38 @@ >+<!doctype html> >+<html> >+<head> >+<script src="../../http/tests/inspector/resources/protocol-test.js"></script> >+<script> >+function test() >+{ >+ let suite = ProtocolTest.createAsyncSuite("ScriptProfiler.startTracking and ScriptProfiler.stopTracking"); >+ >+ suite.addTestCase({ >+ name: "StartAndStopTracking", >+ description: "Start and stop the ScriptProfiler should cause trackingStart and trackingComplete events.", >+ test: function(resolve, reject) { >+ InspectorProtocol.eventHandler["ScriptProfiler.trackingStart"] = (messageObject) => { >+ ProtocolTest.log("ScriptProfiler.trackingStart"); >+ ProtocolTest.expectThat(typeof messageObject.params.timestamp === "number", "Should have a timestamp when starting."); >+ } >+ >+ InspectorProtocol.eventHandler["ScriptProfiler.trackingComplete"] = (messageObject) => { >+ ProtocolTest.log("ScriptProfiler.trackingComplete"); >+ ProtocolTest.expectThat(Array.isArray(messageObject.params.profiles), "Should be profiles when complete."); >+ ProtocolTest.expectThat(!messageObject.params.profiles.length, "Should be no profiles for this session."); >+ resolve(); >+ } >+ >+ InspectorProtocol.sendCommand("ScriptProfiler.startTracking", {profile: true}); >+ InspectorProtocol.sendCommand("ScriptProfiler.stopTracking", {}); >+ } >+ }); >+ >+ suite.runTestCasesAndFinish(); >+} >+</script> >+</head> >+<body onload="runTest()"> >+<p>Tests that ScriptProfiler.startTracking and ScriptProfiler.stopTracking trigger trackingStart and trackingComplete events.</p> >+</body> >+</html> >diff --git a/Source/JavaScriptCore/API/JSBase.cpp b/Source/JavaScriptCore/API/JSBase.cpp >index 3c5594b..6cd4bd1 100644 >--- a/Source/JavaScriptCore/API/JSBase.cpp >+++ b/Source/JavaScriptCore/API/JSBase.cpp >@@ -65,7 +65,7 @@ JSValueRef JSEvaluateScript(JSContextRef ctx, JSStringRef script, JSObjectRef th > SourceCode source = makeSource(script->string(), sourceURL ? sourceURL->string() : String(), TextPosition(OrdinalNumber::fromOneBasedInt(startingLineNumber), OrdinalNumber::first())); > > NakedPtr<Exception> evaluationException; >- JSValue returnValue = evaluate(globalObject->globalExec(), source, jsThisObject, evaluationException); >+ JSValue returnValue = profiledEvaluate(globalObject->globalExec(), ProfilingReasonAPI, source, jsThisObject, evaluationException); > > if (evaluationException) { > if (exception) >diff --git a/Source/JavaScriptCore/API/JSObjectRef.cpp b/Source/JavaScriptCore/API/JSObjectRef.cpp >index e05fbbb..25e0f47 100644 >--- a/Source/JavaScriptCore/API/JSObjectRef.cpp >+++ b/Source/JavaScriptCore/API/JSObjectRef.cpp >@@ -556,7 +556,7 @@ JSValueRef JSObjectCallAsFunction(JSContextRef ctx, JSObjectRef object, JSObject > if (callType == CallTypeNone) > return 0; > >- JSValueRef result = toRef(exec, call(exec, jsObject, callType, callData, jsThisObject, argList)); >+ JSValueRef result = toRef(exec, profiledCall(exec, ProfilingReasonAPI, jsObject, callType, callData, jsThisObject, argList)); > if (handleExceptionIfNeeded(exec, exception) == ExceptionStatus::DidThrow) > result = 0; > return result; >@@ -589,7 +589,8 @@ JSObjectRef JSObjectCallAsConstructor(JSContextRef ctx, JSObjectRef object, size > MarkedArgumentBuffer argList; > for (size_t i = 0; i < argumentCount; i++) > argList.append(toJS(exec, arguments[i])); >- JSObjectRef result = toRef(construct(exec, jsObject, constructType, constructData, argList)); >+ >+ JSObjectRef result = toRef(profiledConstruct(exec, ProfilingReasonAPI, jsObject, constructType, constructData, argList)); > if (handleExceptionIfNeeded(exec, exception) == ExceptionStatus::DidThrow) > result = 0; > return result; >diff --git a/Source/JavaScriptCore/CMakeLists.txt b/Source/JavaScriptCore/CMakeLists.txt >index 06d1422..6d4adb2 100644 >--- a/Source/JavaScriptCore/CMakeLists.txt >+++ b/Source/JavaScriptCore/CMakeLists.txt >@@ -427,6 +427,7 @@ set(JavaScriptCore_SOURCES > inspector/agents/InspectorDebuggerAgent.cpp > inspector/agents/InspectorHeapAgent.cpp > inspector/agents/InspectorRuntimeAgent.cpp >+ inspector/agents/InspectorScriptProfilerAgent.cpp > inspector/agents/JSGlobalObjectConsoleAgent.cpp > inspector/agents/JSGlobalObjectDebuggerAgent.cpp > inspector/agents/JSGlobalObjectRuntimeAgent.cpp >diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog >index fad4c8b..65c8983 100644 >--- a/Source/JavaScriptCore/ChangeLog >+++ b/Source/JavaScriptCore/ChangeLog >@@ -1,3 +1,108 @@ >+2015-12-09 Joseph Pecoraro <pecoraro@apple.com> >+ >+ Web Inspector: Add JSContext Script Profiling >+ https://bugs.webkit.org/show_bug.cgi?id=151899 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Extend JSC::Debugger to include a profiling client interface >+ that the Inspector can implement to be told about script execution >+ entry and exit points. Add new profiledCall/Evalute/Construct >+ methods that are entry points that will notify the profiling >+ client if it exists. >+ >+ By putting the profiling client on Debugger it avoids having >+ special code paths for a JSGlobalObject being JSContext inspected >+ or a JSGlobalObject in a Page being Web inspected. In either case >+ the JSGlobalObject can go through its debugger() which is always >+ the correct inspector interfaces. >+ >+ * CMakeLists.txt: >+ * DerivedSources.make: >+ * JavaScriptCore.xcodeproj/project.pbxproj: >+ Handle new files. >+ >+ * runtime/CallData.cpp: >+ (JSC::profiledCall): >+ * runtime/CallData.h: >+ * runtime/Completion.cpp: >+ (JSC::profiledEvaluate): >+ * runtime/Completion.h: >+ (JSC::profiledEvaluate): >+ * runtime/ConstructData.cpp: >+ (JSC::profiledConstruct): >+ * runtime/ConstructData.h: >+ (JSC::profiledConstruct): >+ Create profiled versions of interpreter entry points. If a profiler client is >+ available, this will automatically tell inform it of entry/exit. Include a reason >+ why this is being profiled. Currently all reasons in JavaScriptCore are enumerated >+ (API, Microtask) and Other is to be used by WebCore or future clients. >+ >+ * debugger/DebuggerScriptProfiling.h: Added. >+ (JSC::DebuggerScriptProfiling::DebuggerScriptProfiling): >+ (JSC::DebuggerScriptProfiling::~DebuggerScriptProfiling): >+ (JSC::DebuggerScriptProfiling::shouldStartProfile): >+ (JSC::DebuggerScriptProfiling::shouldEndProfile): >+ At profiled entry points inform the profiling client if needed. >+ >+ * API/JSBase.cpp: >+ (JSEvaluateScript): >+ * API/JSObjectRef.cpp: >+ (JSObjectCallAsFunction): >+ (JSObjectCallAsConstructor): >+ * runtime/JSJob.cpp: >+ (JSC::JSJobMicrotask::run): >+ Use the profiled functions for API and Microtask execution entry points. >+ >+ * runtime/JSGlobalObject.cpp: >+ (JSC::JSGlobalObject::hasProfiler): >+ * runtime/JSGlobalObject.h: >+ (JSC::JSGlobalObject::hasProfiler): >+ Extend hasProfiler to also check the new Debugger script profiler. >+ >+ * debugger/Debugger.cpp: >+ (JSC::Debugger::setProfilingClient): >+ (JSC::Debugger::willEvaluateScript): >+ (JSC::Debugger::didEvaluateScript): >+ * debugger/Debugger.h: >+ Pass through to the profiling client. >+ >+ * inspector/protocol/ScriptProfiler.json: Added. >+ * inspector/agents/InspectorScriptProfilerAgent.cpp: Added. >+ (Inspector::InspectorScriptProfilerAgent::InspectorScriptProfilerAgent): >+ (Inspector::InspectorScriptProfilerAgent::~InspectorScriptProfilerAgent): >+ (Inspector::InspectorScriptProfilerAgent::didCreateFrontendAndBackend): >+ (Inspector::InspectorScriptProfilerAgent::willDestroyFrontendAndBackend): >+ (Inspector::InspectorScriptProfilerAgent::startTracking): >+ (Inspector::InspectorScriptProfilerAgent::stopTracking): >+ (Inspector::InspectorScriptProfilerAgent::isAlreadyProfiling): >+ (Inspector::InspectorScriptProfilerAgent::willEvaluateScript): >+ (Inspector::InspectorScriptProfilerAgent::didEvaluateScript): >+ (Inspector::toProtocol): >+ (Inspector::InspectorScriptProfilerAgent::addEvent): >+ (Inspector::buildAggregateCallInfoInspectorObject): >+ (Inspector::buildInspectorObject): >+ (Inspector::buildProfileInspectorObject): >+ (Inspector::InspectorScriptProfilerAgent::trackingComplete): >+ * inspector/agents/InspectorScriptProfilerAgent.h: Added. >+ New ScriptProfiler domain to just turn on / off script profiling. >+ It introduces a start/update/complete event model which we want >+ to include in new domains. >+ >+ * inspector/InspectorEnvironment.h: >+ * inspector/InjectedScriptBase.cpp: >+ (Inspector::InjectedScriptBase::callFunctionWithEvalEnabled): >+ Simplify this now that we want it to be the same for all clients. >+ >+ * inspector/JSGlobalObjectInspectorController.h: >+ * inspector/JSGlobalObjectInspectorController.cpp: >+ (Inspector::JSGlobalObjectInspectorController::JSGlobalObjectInspectorController): >+ Create the new agent. >+ >+ * inspector/InspectorProtocolTypes.h: >+ (Inspector::Protocol::Array::addItem): >+ Allow pushing a double onto a Protocol::Array. >+ > 2015-12-08 Joseph Pecoraro <pecoraro@apple.com> > > Create a Sandbox SPI header >diff --git a/Source/JavaScriptCore/DerivedSources.make b/Source/JavaScriptCore/DerivedSources.make >index cf857ef..ffe28f0 100644 >--- a/Source/JavaScriptCore/DerivedSources.make >+++ b/Source/JavaScriptCore/DerivedSources.make >@@ -194,6 +194,7 @@ INSPECTOR_DOMAINS = \ > $(JavaScriptCore)/inspector/protocol/OverlayTypes.json \ > $(JavaScriptCore)/inspector/protocol/Page.json \ > $(JavaScriptCore)/inspector/protocol/Runtime.json \ >+ $(JavaScriptCore)/inspector/protocol/ScriptProfiler.json \ > $(JavaScriptCore)/inspector/protocol/Timeline.json \ > # > >diff --git a/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj b/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj >index e80175f..990227b 100644 >--- a/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj >+++ b/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj >@@ -1543,6 +1543,9 @@ > A54CF2FA184EAEDA00237F19 /* ScriptObject.h in Headers */ = {isa = PBXBuildFile; fileRef = A54CF2F8184EAEDA00237F19 /* ScriptObject.h */; settings = {ATTRIBUTES = (Private, ); }; }; > A54E8EB018BFFBBB00556D28 /* GCSegmentedArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 2A343F7418A1748B0039B085 /* GCSegmentedArray.h */; settings = {ATTRIBUTES = (Private, ); }; }; > A54E8EB118BFFBBE00556D28 /* GCSegmentedArrayInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = 2A343F7718A1749D0039B085 /* GCSegmentedArrayInlines.h */; settings = {ATTRIBUTES = (Private, ); }; }; >+ A55165D21BDF0B98003B75C1 /* InspectorScriptProfilerAgent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A55165D01BDEFDBD003B75C1 /* InspectorScriptProfilerAgent.cpp */; }; >+ A55165D31BDF0B9E003B75C1 /* InspectorScriptProfilerAgent.h in Headers */ = {isa = PBXBuildFile; fileRef = A55165D11BDEFDBD003B75C1 /* InspectorScriptProfilerAgent.h */; settings = {ATTRIBUTES = (Private, ); }; }; >+ A55165D51BDF135A003B75C1 /* DebuggerScriptProfiling.h in Headers */ = {isa = PBXBuildFile; fileRef = A55165D41BDF134D003B75C1 /* DebuggerScriptProfiling.h */; }; > A552C37F1ADDB8FE00139726 /* JSRemoteInspector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A552C37D1ADDB8FE00139726 /* JSRemoteInspector.cpp */; }; > A552C3801ADDB8FE00139726 /* JSRemoteInspector.h in Headers */ = {isa = PBXBuildFile; fileRef = A552C37E1ADDB8FE00139726 /* JSRemoteInspector.h */; settings = {ATTRIBUTES = (Private, ); }; }; > A55D93A5185012A800400DED /* ScriptFunctionCall.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A55D93A3185012A800400DED /* ScriptFunctionCall.cpp */; }; >@@ -3695,6 +3698,9 @@ > A54CF2F3184EAB2400237F19 /* ScriptValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScriptValue.h; sourceTree = "<group>"; }; > A54CF2F7184EAEDA00237F19 /* ScriptObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ScriptObject.cpp; sourceTree = "<group>"; }; > A54CF2F8184EAEDA00237F19 /* ScriptObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScriptObject.h; sourceTree = "<group>"; }; >+ A55165D01BDEFDBD003B75C1 /* InspectorScriptProfilerAgent.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InspectorScriptProfilerAgent.cpp; sourceTree = "<group>"; }; >+ A55165D11BDEFDBD003B75C1 /* InspectorScriptProfilerAgent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InspectorScriptProfilerAgent.h; sourceTree = "<group>"; }; >+ A55165D41BDF134D003B75C1 /* DebuggerScriptProfiling.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DebuggerScriptProfiling.h; sourceTree = "<group>"; }; > A552C37D1ADDB8FE00139726 /* JSRemoteInspector.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSRemoteInspector.cpp; sourceTree = "<group>"; }; > A552C37E1ADDB8FE00139726 /* JSRemoteInspector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSRemoteInspector.h; sourceTree = "<group>"; }; > A55D93A3185012A800400DED /* ScriptFunctionCall.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ScriptFunctionCall.cpp; sourceTree = "<group>"; }; >@@ -5169,6 +5175,7 @@ > FEA0861F182B7A0400F6D851 /* DebuggerPrimitives.h */, > 0F2D4DDB19832D34007D4B19 /* DebuggerScope.cpp */, > 0F2D4DDC19832D34007D4B19 /* DebuggerScope.h */, >+ A55165D41BDF134D003B75C1 /* DebuggerScriptProfiling.h */, > ); > path = debugger; > sourceTree = "<group>"; >@@ -6539,6 +6546,8 @@ > A5339EC51BB399900054F005 /* InspectorHeapAgent.h */, > A50E4B5D18809DD50068A46D /* InspectorRuntimeAgent.cpp */, > A50E4B5E18809DD50068A46D /* InspectorRuntimeAgent.h */, >+ A55165D01BDEFDBD003B75C1 /* InspectorScriptProfilerAgent.cpp */, >+ A55165D11BDEFDBD003B75C1 /* InspectorScriptProfilerAgent.h */, > A5FD0083189B1B7E00633231 /* JSGlobalObjectConsoleAgent.cpp */, > A5FD0084189B1B7E00633231 /* JSGlobalObjectConsoleAgent.h */, > A57D23E71891B0770031C7FA /* JSGlobalObjectDebuggerAgent.cpp */, >@@ -7402,6 +7411,7 @@ > A125846E1B45A36000CC7F6C /* IntlNumberFormatConstructor.lut.h in Headers */, > A1D793011B43864B004516F5 /* IntlNumberFormatPrototype.h in Headers */, > A125846F1B45A36000CC7F6C /* IntlNumberFormatPrototype.lut.h in Headers */, >+ A55165D31BDF0B9E003B75C1 /* InspectorScriptProfilerAgent.h in Headers */, > A12BBFF21B044A8B00664B69 /* IntlObject.h in Headers */, > 860BD801148EA6F200112B2F /* Intrinsic.h in Headers */, > FE4D55B81AE716CA0052E459 /* IterationStatus.h in Headers */, >@@ -7602,6 +7612,7 @@ > 0F4680D314BBD16700BFE272 /* LLIntData.h in Headers */, > 0F38B01217CF078300B144D3 /* LLIntEntrypoint.h in Headers */, > 0F4680A314BA7F8D00BFE272 /* LLIntExceptions.h in Headers */, >+ A55165D51BDF135A003B75C1 /* DebuggerScriptProfiling.h in Headers */, > 0F4680CB14BBB17200BFE272 /* LLIntOfflineAsmConfig.h in Headers */, > FED287B215EC9A5700DA8161 /* LLIntOpcode.h in Headers */, > 0F4680A514BA7F8D00BFE272 /* LLIntSlowPaths.h in Headers */, >@@ -8760,6 +8771,7 @@ > 0F235BDC17178E1C00690C7F /* FTLOSRExit.cpp in Sources */, > 0F235BDF17178E1C00690C7F /* FTLOSRExitCompiler.cpp in Sources */, > 0FEA0A2A1709629600BB722C /* FTLOutput.cpp in Sources */, >+ A55165D21BDF0B98003B75C1 /* InspectorScriptProfilerAgent.cpp in Sources */, > 0F485329187DFDEC0083B687 /* FTLRecoveryOpcode.cpp in Sources */, > 0FCEFAAB1804C13E00472CE4 /* FTLSaveRestore.cpp in Sources */, > 0F25F1B1181635F300522F39 /* FTLSlowPathCall.cpp in Sources */, >diff --git a/Source/JavaScriptCore/debugger/Debugger.cpp b/Source/JavaScriptCore/debugger/Debugger.cpp >index d73fa07..62d1bf8 100644 >--- a/Source/JavaScriptCore/debugger/Debugger.cpp >+++ b/Source/JavaScriptCore/debugger/Debugger.cpp >@@ -226,6 +226,24 @@ void Debugger::registerCodeBlock(CodeBlock* codeBlock) > codeBlock->setSteppingMode(CodeBlock::SteppingModeEnabled); > } > >+void Debugger::setProfilingClient(DebuggerProfilingClient* client) >+{ >+ ASSERT(!!m_profilingClient != !!client); >+ m_profilingClient = client; >+ >+ recompileAllJSFunctions(); >+} >+ >+double Debugger::willEvaluateScript(JSGlobalObject& globalObject) >+{ >+ return m_profilingClient->willEvaluateScript(globalObject); >+} >+ >+void Debugger::didEvaluateScript(JSGlobalObject& globalObject, double startTime, ProfilingReason reason) >+{ >+ m_profilingClient->didEvaluateScript(globalObject, startTime, reason); >+} >+ > void Debugger::toggleBreakpoint(CodeBlock* codeBlock, Breakpoint& breakpoint, BreakpointState enabledOrNot) > { > ScriptExecutable* executable = codeBlock->ownerScriptExecutable(); >diff --git a/Source/JavaScriptCore/debugger/Debugger.h b/Source/JavaScriptCore/debugger/Debugger.h >index ae36f19..6d60315 100644 >--- a/Source/JavaScriptCore/debugger/Debugger.h >+++ b/Source/JavaScriptCore/debugger/Debugger.h >@@ -23,6 +23,7 @@ > #define Debugger_h > > #include "Breakpoint.h" >+#include "CallData.h" > #include "DebuggerCallFrame.h" > #include "DebuggerPrimitives.h" > #include "JSCJSValue.h" >@@ -42,6 +43,14 @@ class VM; > > typedef ExecState CallFrame; > >+class JS_EXPORT_PRIVATE DebuggerProfilingClient { >+public: >+ virtual ~DebuggerProfilingClient() { } >+ virtual bool isAlreadyProfiling() const = 0; >+ virtual double willEvaluateScript(JSGlobalObject&) = 0; >+ virtual void didEvaluateScript(JSGlobalObject&, double, ProfilingReason) = 0; >+}; >+ > class JS_EXPORT_PRIVATE Debugger { > public: > Debugger(VM&); >@@ -127,6 +136,12 @@ public: > > void registerCodeBlock(CodeBlock*); > >+ void setProfilingClient(DebuggerProfilingClient*); >+ bool hasProfilingClient() const { return m_profilingClient != nullptr; } >+ bool isAlreadyProfiling() const { return m_profilingClient && m_profilingClient->isAlreadyProfiling(); } >+ double willEvaluateScript(JSGlobalObject&); >+ void didEvaluateScript(JSGlobalObject&, double, ProfilingReason); >+ > protected: > virtual bool needPauseHandling(JSGlobalObject*) { return false; } > virtual void handleBreakpointHit(JSGlobalObject*, const Breakpoint&) { } >@@ -215,6 +230,8 @@ private: > > RefPtr<JSC::DebuggerCallFrame> m_currentDebuggerCallFrame; > >+ DebuggerProfilingClient* m_profilingClient { nullptr }; >+ > friend class DebuggerPausedScope; > friend class TemporaryPausedState; > friend class LLIntOffsetsExtractor; >diff --git a/Source/JavaScriptCore/debugger/DebuggerScriptProfiling.h b/Source/JavaScriptCore/debugger/DebuggerScriptProfiling.h >new file mode 100644 >index 0000000..729e0c5 >--- /dev/null >+++ b/Source/JavaScriptCore/debugger/DebuggerScriptProfiling.h >@@ -0,0 +1,92 @@ >+/* >+ * 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 met: >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY >+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE >+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR >+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR >+ * 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 DebuggerScriptProfiling_h >+#define DebuggerScriptProfiling_h >+ >+#include "Debugger.h" >+#include "JSGlobalObject.h" >+ >+namespace JSC { >+ >+class DebuggerScriptProfiling { >+public: >+ DebuggerScriptProfiling(JSGlobalObject* globalObject, ProfilingReason reason) >+ : m_globalObject(globalObject) >+ , m_reason(reason) >+ { >+ if (shouldStartProfile()) >+ m_startTime = m_globalObject->debugger()->willEvaluateScript(*m_globalObject); >+ } >+ >+ ~DebuggerScriptProfiling() >+ { >+ if (shouldEndProfile()) >+ m_globalObject->debugger()->didEvaluateScript(*m_globalObject, m_startTime, m_reason); >+ } >+ >+private: >+ bool shouldStartProfile() const >+ { >+ if (!m_globalObject) >+ return false; >+ >+ if (!m_globalObject->hasDebugger()) >+ return false; >+ >+ if (!m_globalObject->debugger()->hasProfilingClient()) >+ return false; >+ >+ if (m_globalObject->debugger()->isAlreadyProfiling()) >+ return false; >+ >+ return true; >+ } >+ >+ bool shouldEndProfile() const >+ { >+ // Did not start a profile. >+ if (std::isnan(m_startTime)) >+ return false; >+ >+ // Debugger may have been removed. >+ if (!m_globalObject->hasDebugger()) >+ return false; >+ >+ // Profiling Client may have been removed. >+ if (!m_globalObject->debugger()->hasProfilingClient()) >+ return false; >+ >+ return true; >+ } >+ >+ JSGlobalObject* m_globalObject; >+ double m_startTime { NAN }; >+ ProfilingReason m_reason; >+}; >+ >+} >+ >+#endif // DebuggerScriptProfiling_h >diff --git a/Source/JavaScriptCore/inspector/InjectedScriptBase.cpp b/Source/JavaScriptCore/inspector/InjectedScriptBase.cpp >index 9280a58..27ec8a1 100644 >--- a/Source/JavaScriptCore/inspector/InjectedScriptBase.cpp >+++ b/Source/JavaScriptCore/inspector/InjectedScriptBase.cpp >@@ -36,6 +36,7 @@ > #include "InspectorValues.h" > #include "JSCInlines.h" > #include "JSGlobalObject.h" >+#include "LegacyProfiler.h" > #include "ScriptFunctionCall.h" > #include <wtf/text/WTFString.h> > >@@ -70,19 +71,16 @@ const Deprecated::ScriptObject& InjectedScriptBase::injectedScriptObject() const > > Deprecated::ScriptValue InjectedScriptBase::callFunctionWithEvalEnabled(Deprecated::ScriptFunctionCall& function, bool& hadException) const > { >- if (m_environment) >- m_environment->willCallInjectedScriptFunction(m_injectedScriptObject.scriptState(), name(), 1); >- > JSC::ExecState* scriptState = m_injectedScriptObject.scriptState(); >+ JSC::LegacyProfiler::profiler()->suspendProfiling(scriptState); >+ > Deprecated::ScriptValue resultValue; >- > { > JSC::DebuggerEvalEnabler evalEnabler(scriptState); > resultValue = function.call(hadException); > } > >- if (m_environment) >- m_environment->didCallInjectedScriptFunction(m_injectedScriptObject.scriptState()); >+ JSC::LegacyProfiler::profiler()->unsuspendProfiling(scriptState); > > return resultValue; > } >diff --git a/Source/JavaScriptCore/inspector/InspectorEnvironment.h b/Source/JavaScriptCore/inspector/InspectorEnvironment.h >index b3d6948..6fd470a 100644 >--- a/Source/JavaScriptCore/inspector/InspectorEnvironment.h >+++ b/Source/JavaScriptCore/inspector/InspectorEnvironment.h >@@ -51,8 +51,6 @@ public: > virtual bool canAccessInspectedScriptState(JSC::ExecState*) const = 0; > virtual InspectorFunctionCallHandler functionCallHandler() const = 0; > virtual InspectorEvaluateHandler evaluateHandler() const = 0; >- virtual void willCallInjectedScriptFunction(JSC::ExecState*, const String& scriptName, int scriptLine) = 0; >- virtual void didCallInjectedScriptFunction(JSC::ExecState*) = 0; > virtual void frontendInitialized() = 0; > virtual Ref<WTF::Stopwatch> executionStopwatch() = 0; > virtual ScriptDebugServer& scriptDebugServer() = 0; >diff --git a/Source/JavaScriptCore/inspector/InspectorProtocolTypes.h b/Source/JavaScriptCore/inspector/InspectorProtocolTypes.h >index 6f06296..8069992 100644 >--- a/Source/JavaScriptCore/inspector/InspectorProtocolTypes.h >+++ b/Source/JavaScriptCore/inspector/InspectorProtocolTypes.h >@@ -93,7 +93,12 @@ public: > { > openAccessors().pushInteger(value); > } >- >+ >+ void addItem(double value) >+ { >+ openAccessors().pushDouble(value); >+ } >+ > static Ref<Array<T>> create() > { > return adoptRef(*new Array<T>()); >diff --git a/Source/JavaScriptCore/inspector/JSGlobalObjectInspectorController.cpp b/Source/JavaScriptCore/inspector/JSGlobalObjectInspectorController.cpp >index 4f984ee..40edff7 100644 >--- a/Source/JavaScriptCore/inspector/JSGlobalObjectInspectorController.cpp >+++ b/Source/JavaScriptCore/inspector/JSGlobalObjectInspectorController.cpp >@@ -37,6 +37,7 @@ > #include "InspectorFrontendChannel.h" > #include "InspectorFrontendRouter.h" > #include "InspectorHeapAgent.h" >+#include "InspectorScriptProfilerAgent.h" > #include "JSGlobalObject.h" > #include "JSGlobalObjectConsoleAgent.h" > #include "JSGlobalObjectConsoleClient.h" >@@ -97,6 +98,7 @@ JSGlobalObjectInspectorController::JSGlobalObjectInspectorController(JSGlobalObj > m_agents.append(WTF::move(consoleAgent)); > m_agents.append(WTF::move(debuggerAgent)); > m_agents.append(std::make_unique<InspectorHeapAgent>(context)); >+ m_agents.append(std::make_unique<InspectorScriptProfilerAgent>(context)); > > m_executionStopwatch->start(); > } >diff --git a/Source/JavaScriptCore/inspector/JSGlobalObjectInspectorController.h b/Source/JavaScriptCore/inspector/JSGlobalObjectInspectorController.h >index b75c705..7ee90c1 100644 >--- a/Source/JavaScriptCore/inspector/JSGlobalObjectInspectorController.h >+++ b/Source/JavaScriptCore/inspector/JSGlobalObjectInspectorController.h >@@ -55,6 +55,7 @@ class InspectorAgent; > class InspectorConsoleAgent; > class InspectorDebuggerAgent; > class InspectorHeapAgent; >+class InspectorScriptProfilerAgent; > class JSGlobalObjectConsoleClient; > class ScriptCallStack; > >@@ -90,8 +91,6 @@ public: > virtual bool canAccessInspectedScriptState(JSC::ExecState*) const override { return true; } > virtual InspectorFunctionCallHandler functionCallHandler() const override; > virtual InspectorEvaluateHandler evaluateHandler() const override; >- virtual void willCallInjectedScriptFunction(JSC::ExecState*, const String&, int) override { } >- virtual void didCallInjectedScriptFunction(JSC::ExecState*) override { } > virtual void frontendInitialized() override; > virtual Ref<WTF::Stopwatch> executionStopwatch() override; > virtual JSGlobalObjectScriptDebugServer& scriptDebugServer() override; >diff --git a/Source/JavaScriptCore/inspector/agents/InspectorScriptProfilerAgent.cpp b/Source/JavaScriptCore/inspector/agents/InspectorScriptProfilerAgent.cpp >new file mode 100644 >index 0000000..f712882 >--- /dev/null >+++ b/Source/JavaScriptCore/inspector/agents/InspectorScriptProfilerAgent.cpp >@@ -0,0 +1,211 @@ >+/* >+ * 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 met: >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY >+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE >+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR >+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR >+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, >+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, >+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR >+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY >+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#include "config.h" >+#include "InspectorScriptProfilerAgent.h" >+ >+#include "InspectorEnvironment.h" >+#include "LegacyProfiler.h" >+#include <wtf/RunLoop.h> >+#include <wtf/Stopwatch.h> >+ >+using namespace JSC; >+ >+namespace Inspector { >+ >+InspectorScriptProfilerAgent::InspectorScriptProfilerAgent(AgentContext& context) >+ : InspectorAgentBase(ASCIILiteral("ScriptProfiler")) >+ , m_frontendDispatcher(std::make_unique<ScriptProfilerFrontendDispatcher>(context.frontendRouter)) >+ , m_backendDispatcher(ScriptProfilerBackendDispatcher::create(context.backendDispatcher, this)) >+ , m_environment(context.environment) >+{ >+} >+ >+InspectorScriptProfilerAgent::~InspectorScriptProfilerAgent() >+{ >+} >+ >+void InspectorScriptProfilerAgent::didCreateFrontendAndBackend(FrontendRouter*, BackendDispatcher*) >+{ >+} >+ >+void InspectorScriptProfilerAgent::willDestroyFrontendAndBackend(DisconnectReason) >+{ >+ ErrorString ignored; >+ stopTracking(ignored); >+} >+ >+void InspectorScriptProfilerAgent::startTracking(ErrorString&, const bool* profile) >+{ >+ if (m_tracking) >+ return; >+ >+ m_tracking = true; >+ >+ if (profile && *profile) >+ m_enableLegacyProfiler = true; >+ >+ m_environment.scriptDebugServer().setProfilingClient(this); >+ >+ m_frontendDispatcher->trackingStart(m_environment.executionStopwatch()->elapsedTime()); >+} >+ >+void InspectorScriptProfilerAgent::stopTracking(ErrorString&) >+{ >+ if (!m_tracking) >+ return; >+ >+ m_tracking = false; >+ m_enableLegacyProfiler = false; >+ m_activeEvaluateScript = false; >+ >+ m_environment.scriptDebugServer().setProfilingClient(nullptr); >+ >+ trackingComplete(); >+} >+ >+bool InspectorScriptProfilerAgent::isAlreadyProfiling() const >+{ >+ return m_activeEvaluateScript; >+} >+ >+double InspectorScriptProfilerAgent::willEvaluateScript(JSGlobalObject& globalObject) >+{ >+ m_activeEvaluateScript = true; >+ >+ if (m_enableLegacyProfiler) >+ LegacyProfiler::profiler()->startProfiling(globalObject.globalExec(), ASCIILiteral("ScriptProfiler"), m_environment.executionStopwatch()); >+ >+ return m_environment.executionStopwatch()->elapsedTime(); >+} >+ >+void InspectorScriptProfilerAgent::didEvaluateScript(JSGlobalObject& globalObject, double startTime, ProfilingReason reason) >+{ >+ m_activeEvaluateScript = false; >+ >+ if (m_enableLegacyProfiler) >+ m_profiles.append(LegacyProfiler::profiler()->stopProfiling(globalObject.globalExec(), ASCIILiteral("ScriptProfiler"))); >+ >+ double endTime = m_environment.executionStopwatch()->elapsedTime(); >+ >+ addEvent(startTime, endTime, reason); >+} >+ >+static Inspector::Protocol::ScriptProfiler::EventType toProtocol(ProfilingReason reason) >+{ >+ switch (reason) { >+ case ProfilingReasonAPI: >+ return Inspector::Protocol::ScriptProfiler::EventType::API; >+ case ProfilingReasonMicrotask: >+ return Inspector::Protocol::ScriptProfiler::EventType::Microtask; >+ case ProfilingReasonOther: >+ return Inspector::Protocol::ScriptProfiler::EventType::Other; >+ } >+ >+ ASSERT_NOT_REACHED(); >+ return Inspector::Protocol::ScriptProfiler::EventType::Other; >+} >+ >+void InspectorScriptProfilerAgent::addEvent(double startTime, double endTime, ProfilingReason reason) >+{ >+ ASSERT(endTime >= startTime); >+ >+ auto event = Inspector::Protocol::ScriptProfiler::Event::create() >+ .setStartTime(startTime) >+ .setEndTime(endTime) >+ .setType(toProtocol(reason)) >+ .release(); >+ >+ m_frontendDispatcher->trackingUpdate(WTF::move(event)); >+} >+ >+static Ref<Protocol::Timeline::CPUProfileNodeAggregateCallInfo> buildAggregateCallInfoInspectorObject(const JSC::ProfileNode* node) >+{ >+ double startTime = node->calls()[0].startTime(); >+ double endTime = node->calls().last().startTime() + node->calls().last().elapsedTime(); >+ >+ double totalTime = 0; >+ for (const JSC::ProfileNode::Call& call : node->calls()) >+ totalTime += call.elapsedTime(); >+ >+ return Protocol::Timeline::CPUProfileNodeAggregateCallInfo::create() >+ .setCallCount(node->calls().size()) >+ .setStartTime(startTime) >+ .setEndTime(endTime) >+ .setTotalTime(totalTime) >+ .release(); >+} >+ >+static Ref<Protocol::Timeline::CPUProfileNode> buildInspectorObject(const JSC::ProfileNode* node) >+{ >+ auto result = Protocol::Timeline::CPUProfileNode::create() >+ .setId(node->id()) >+ .setCallInfo(buildAggregateCallInfoInspectorObject(node)) >+ .release(); >+ >+ if (!node->functionName().isEmpty()) >+ result->setFunctionName(node->functionName()); >+ >+ if (!node->url().isEmpty()) { >+ result->setUrl(node->url()); >+ result->setLineNumber(node->lineNumber()); >+ result->setColumnNumber(node->columnNumber()); >+ } >+ >+ if (!node->children().isEmpty()) { >+ auto children = Protocol::Array<Protocol::Timeline::CPUProfileNode>::create(); >+ for (RefPtr<JSC::ProfileNode> profileNode : node->children()) >+ children->addItem(buildInspectorObject(profileNode.get())); >+ result->setChildren(WTF::move(children)); >+ } >+ >+ return WTF::move(result); >+} >+ >+static Ref<Protocol::Timeline::CPUProfile> buildProfileInspectorObject(const JSC::Profile* profile) >+{ >+ auto rootNodes = Protocol::Array<Protocol::Timeline::CPUProfileNode>::create(); >+ for (RefPtr<JSC::ProfileNode> profileNode : profile->rootNode()->children()) >+ rootNodes->addItem(buildInspectorObject(profileNode.get())); >+ >+ return Protocol::Timeline::CPUProfile::create() >+ .setRootNodes(WTF::move(rootNodes)) >+ .release(); >+} >+ >+void InspectorScriptProfilerAgent::trackingComplete() >+{ >+ RefPtr<Inspector::Protocol::Array<InspectorValue>> profiles = Inspector::Protocol::Array<InspectorValue>::create(); >+ for (auto& profile : m_profiles) { >+ Ref<InspectorValue> value = buildProfileInspectorObject(profile.get()); >+ profiles->addItem(WTF::move(value)); >+ } >+ >+ m_frontendDispatcher->trackingComplete(profiles); >+ >+ m_profiles.clear(); >+} >+ >+} // namespace Inspector >diff --git a/Source/JavaScriptCore/inspector/agents/InspectorScriptProfilerAgent.h b/Source/JavaScriptCore/inspector/agents/InspectorScriptProfilerAgent.h >new file mode 100644 >index 0000000..4d35c55 >--- /dev/null >+++ b/Source/JavaScriptCore/inspector/agents/InspectorScriptProfilerAgent.h >@@ -0,0 +1,82 @@ >+/* >+ * 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 met: >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY >+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE >+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR >+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR >+ * 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 InspectorScriptProfilerAgent_h >+#define InspectorScriptProfilerAgent_h >+ >+#include "InspectorBackendDispatchers.h" >+#include "InspectorFrontendDispatchers.h" >+#include "inspector/InspectorAgentBase.h" >+#include "inspector/ScriptDebugServer.h" >+#include <wtf/Noncopyable.h> >+ >+namespace JSC { >+class Profile; >+} >+ >+namespace Inspector { >+ >+typedef String ErrorString; >+ >+class JS_EXPORT_PRIVATE InspectorScriptProfilerAgent final : public InspectorAgentBase, public ScriptProfilerBackendDispatcherHandler, public JSC::DebuggerProfilingClient { >+ WTF_MAKE_NONCOPYABLE(InspectorScriptProfilerAgent); >+public: >+ InspectorScriptProfilerAgent(AgentContext&); >+ virtual ~InspectorScriptProfilerAgent(); >+ >+ virtual void didCreateFrontendAndBackend(FrontendRouter*, BackendDispatcher*) override; >+ virtual void willDestroyFrontendAndBackend(DisconnectReason) override; >+ >+ // ScriptProfilerBackendDispatcherHandler >+ virtual void startTracking(ErrorString&, const bool* profile) override; >+ virtual void stopTracking(ErrorString&) override; >+ >+ // DebuggerProfilingClient >+ virtual bool isAlreadyProfiling() const override; >+ virtual double willEvaluateScript(JSC::JSGlobalObject&) override; >+ virtual void didEvaluateScript(JSC::JSGlobalObject&, double, JSC::ProfilingReason) override; >+ >+private: >+ struct Event { >+ Event(double start, double end) : startTime(start), endTime(end) { } >+ double startTime { 0 }; >+ double endTime { 0 }; >+ }; >+ >+ void addEvent(double startTime, double endTime, JSC::ProfilingReason); >+ void trackingComplete(); >+ >+ std::unique_ptr<ScriptProfilerFrontendDispatcher> m_frontendDispatcher; >+ RefPtr<ScriptProfilerBackendDispatcher> m_backendDispatcher; >+ Vector<RefPtr<JSC::Profile>> m_profiles; >+ InspectorEnvironment& m_environment; >+ bool m_tracking { false }; >+ bool m_enableLegacyProfiler { false }; >+ bool m_activeEvaluateScript { false }; >+}; >+ >+} // namespace Inspector >+ >+#endif // InspectorScriptProfilerAgent_h >diff --git a/Source/JavaScriptCore/inspector/protocol/ScriptProfiler.json b/Source/JavaScriptCore/inspector/protocol/ScriptProfiler.json >new file mode 100644 >index 0000000..fa7de25 >--- /dev/null >+++ b/Source/JavaScriptCore/inspector/protocol/ScriptProfiler.json >@@ -0,0 +1,56 @@ >+{ >+ "domain": "ScriptProfiler", >+ "description": "Profiler domain exposes JavaScript evaluation timing and profiling.", >+ "types": [ >+ { >+ "id": "EventType", >+ "type": "string", >+ "enum": ["API", "Microtask", "Other"] >+ }, >+ { >+ "id": "Event", >+ "type": "object", >+ "properties": [ >+ { "name": "startTime", "type": "number" }, >+ { "name": "endTime", "type": "number" }, >+ { "name": "type", "$ref": "EventType" } >+ ] >+ } >+ ], >+ "commands": [ >+ { >+ "name": "startTracking", >+ "description": "Start tracking script evaluations.", >+ "parameters": [ >+ { "name": "profile", "type": "boolean", "optional": true, "description": "Profile script evaluations, defaults to false." } >+ ] >+ }, >+ { >+ "name": "stopTracking", >+ "description": "Stop tracking script evaluations. This will produce a `trackingComplete` event." >+ } >+ ], >+ "events": [ >+ { >+ "name": "trackingStart", >+ "description": "Tracking started.", >+ "parameters": [ >+ { "name": "timestamp", "type": "number" } >+ ] >+ }, >+ { >+ "name": "trackingUpdate", >+ "description": "While tracking is enabled the backend will periodically update the frontend with event data. The data includes tuples.", >+ "parameters": [ >+ { "name": "event", "$ref": "Event" } >+ ] >+ }, >+ { >+ "name": "trackingComplete", >+ "description": "When tracking is complete the backend will send any buffered data, such as profiling information.", >+ "parameters": [ >+ { "name": "profiles", "type": "array", "items": { "type": "any" }, "optional": true } >+ ] >+ } >+ ] >+} >diff --git a/Source/JavaScriptCore/runtime/CallData.cpp b/Source/JavaScriptCore/runtime/CallData.cpp >index 31c28c3..8bc4119 100644 >--- a/Source/JavaScriptCore/runtime/CallData.cpp >+++ b/Source/JavaScriptCore/runtime/CallData.cpp >@@ -26,10 +26,11 @@ > #include "config.h" > #include "CallData.h" > >+#include "DebuggerScriptProfiling.h" > #include "Executable.h" > #include "Interpreter.h" >-#include "JSFunction.h" > #include "JSCInlines.h" >+#include "JSFunction.h" > > namespace JSC { > >@@ -51,4 +52,16 @@ JSValue call(ExecState* exec, JSValue functionObject, CallType callType, const C > return result; > } > >+JSValue profiledCall(ExecState* exec, ProfilingReason reason, JSValue functionObject, CallType callType, const CallData& callData, JSValue thisValue, const ArgList& args) >+{ >+ DebuggerScriptProfiling profile(exec->vmEntryGlobalObject(), reason); >+ return call(exec, functionObject, callType, callData, thisValue, args); >+} >+ >+JSValue profiledCall(ExecState* exec, ProfilingReason reason, JSValue functionObject, CallType callType, const CallData& callData, JSValue thisValue, const ArgList& args, NakedPtr<Exception>& returnedException) >+{ >+ DebuggerScriptProfiling profile(exec->vmEntryGlobalObject(), reason); >+ return call(exec, functionObject, callType, callData, thisValue, args, returnedException); >+} >+ > } // namespace JSC >diff --git a/Source/JavaScriptCore/runtime/CallData.h b/Source/JavaScriptCore/runtime/CallData.h >index e4a918d..5a677c1 100644 >--- a/Source/JavaScriptCore/runtime/CallData.h >+++ b/Source/JavaScriptCore/runtime/CallData.h >@@ -59,9 +59,18 @@ union CallData { > } js; > }; > >+enum ProfilingReason { >+ ProfilingReasonAPI, >+ ProfilingReasonMicrotask, >+ ProfilingReasonOther >+}; >+ > JS_EXPORT_PRIVATE JSValue call(ExecState*, JSValue functionObject, CallType, const CallData&, JSValue thisValue, const ArgList&); > JS_EXPORT_PRIVATE JSValue call(ExecState*, JSValue functionObject, CallType, const CallData&, JSValue thisValue, const ArgList&, NakedPtr<Exception>& returnedException); > >+JS_EXPORT_PRIVATE JSValue profiledCall(ExecState*, ProfilingReason, JSValue functionObject, CallType, const CallData&, JSValue thisValue, const ArgList&); >+JS_EXPORT_PRIVATE JSValue profiledCall(ExecState*, ProfilingReason, JSValue functionObject, CallType, const CallData&, JSValue thisValue, const ArgList&, NakedPtr<Exception>& returnedException); >+ > } // namespace JSC > > #endif // CallData_h >diff --git a/Source/JavaScriptCore/runtime/Completion.cpp b/Source/JavaScriptCore/runtime/Completion.cpp >index 02f395c..30e4a2f 100644 >--- a/Source/JavaScriptCore/runtime/Completion.cpp >+++ b/Source/JavaScriptCore/runtime/Completion.cpp >@@ -26,6 +26,7 @@ > #include "CallFrame.h" > #include "CodeProfiling.h" > #include "Debugger.h" >+#include "DebuggerScriptProfiling.h" > #include "Exception.h" > #include "Interpreter.h" > #include "JSCInlines.h" >@@ -113,6 +114,12 @@ JSValue evaluate(ExecState* exec, const SourceCode& source, JSValue thisValue, N > return result; > } > >+JSValue profiledEvaluate(ExecState* exec, ProfilingReason reason, const SourceCode& source, JSValue thisValue, NakedPtr<Exception>& returnedException) >+{ >+ DebuggerScriptProfiling debuggerScriptProfile(exec->vmEntryGlobalObject(), reason); >+ return evaluate(exec, source, thisValue, returnedException); >+} >+ > static JSValue identifierToJSValue(VM& vm, const Identifier& identifier) > { > if (identifier.isSymbol()) >diff --git a/Source/JavaScriptCore/runtime/Completion.h b/Source/JavaScriptCore/runtime/Completion.h >index 24d78f1..53ba195 100644 >--- a/Source/JavaScriptCore/runtime/Completion.h >+++ b/Source/JavaScriptCore/runtime/Completion.h >@@ -23,6 +23,7 @@ > #ifndef Completion_h > #define Completion_h > >+#include "CallData.h" > #include "JSCJSValue.h" > #include <wtf/NakedPtr.h> > >@@ -39,6 +40,7 @@ class JSInternalPromise; > JS_EXPORT_PRIVATE bool checkSyntax(VM&, const SourceCode&, ParserError&); > JS_EXPORT_PRIVATE bool checkSyntax(ExecState*, const SourceCode&, JSValue* exception = 0); > JS_EXPORT_PRIVATE bool checkModuleSyntax(ExecState*, const SourceCode&, ParserError&); >+ > JS_EXPORT_PRIVATE JSValue evaluate(ExecState*, const SourceCode&, JSValue thisValue, NakedPtr<Exception>& returnedException); > inline JSValue evaluate(ExecState* exec, const SourceCode& sourceCode, JSValue thisValue = JSValue()) > { >@@ -46,6 +48,13 @@ inline JSValue evaluate(ExecState* exec, const SourceCode& sourceCode, JSValue t > return evaluate(exec, sourceCode, thisValue, unused); > } > >+JS_EXPORT_PRIVATE JSValue profiledEvaluate(ExecState*, ProfilingReason, const SourceCode&, JSValue thisValue, NakedPtr<Exception>& returnedException); >+inline JSValue profiledEvaluate(ExecState* exec, ProfilingReason reason, const SourceCode& sourceCode, JSValue thisValue = JSValue()) >+{ >+ NakedPtr<Exception> unused; >+ return profiledEvaluate(exec, reason, sourceCode, thisValue, unused); >+} >+ > // Load the module source and evaluate it. > JS_EXPORT_PRIVATE JSInternalPromise* loadAndEvaluateModule(ExecState*, const String& moduleName); > JS_EXPORT_PRIVATE JSInternalPromise* loadAndEvaluateModule(ExecState*, const SourceCode&); >diff --git a/Source/JavaScriptCore/runtime/ConstructData.cpp b/Source/JavaScriptCore/runtime/ConstructData.cpp >index 6010e63..a15158a 100644 >--- a/Source/JavaScriptCore/runtime/ConstructData.cpp >+++ b/Source/JavaScriptCore/runtime/ConstructData.cpp >@@ -26,11 +26,12 @@ > #include "config.h" > #include "ConstructData.h" > >+#include "DebuggerScriptProfiling.h" > #include "Executable.h" > #include "Interpreter.h" >+#include "JSCInlines.h" > #include "JSFunction.h" > #include "JSGlobalObject.h" >-#include "JSCInlines.h" > > namespace JSC { > >@@ -40,4 +41,10 @@ JSObject* construct(ExecState* exec, JSValue constructorObject, ConstructType co > return exec->interpreter()->executeConstruct(exec, asObject(constructorObject), constructType, constructData, args, newTarget); > } > >+JSObject* profiledConstruct(ExecState* exec, ProfilingReason reason, JSValue constructorObject, ConstructType constructType, const ConstructData& constructData, const ArgList& args, JSValue newTarget) >+{ >+ DebuggerScriptProfiling profile(exec->vmEntryGlobalObject(), reason); >+ return construct(exec, constructorObject, constructType, constructData, args, newTarget); >+} >+ > } // namespace JSC >diff --git a/Source/JavaScriptCore/runtime/ConstructData.h b/Source/JavaScriptCore/runtime/ConstructData.h >index 17e1a5d..56dafac 100644 >--- a/Source/JavaScriptCore/runtime/ConstructData.h >+++ b/Source/JavaScriptCore/runtime/ConstructData.h >@@ -63,6 +63,13 @@ ALWAYS_INLINE JSObject* construct(ExecState* exec, JSValue constructorObject, Co > return construct(exec, constructorObject, constructType, constructData, args, constructorObject); > } > >+JS_EXPORT_PRIVATE JSObject* profiledConstruct(ExecState*, ProfilingReason, JSValue constructor, ConstructType, const ConstructData&, const ArgList&, JSValue newTarget); >+ >+ALWAYS_INLINE JSObject* profiledConstruct(ExecState* exec, ProfilingReason reason, JSValue constructorObject, ConstructType constructType, const ConstructData& constructData, const ArgList& args) >+{ >+ return profiledConstruct(exec, reason, constructorObject, constructType, constructData, args, constructorObject); >+} >+ > } // namespace JSC > > #endif // ConstructData_h >diff --git a/Source/JavaScriptCore/runtime/JSGlobalObject.cpp b/Source/JavaScriptCore/runtime/JSGlobalObject.cpp >index ecc82ba..43f442c 100644 >--- a/Source/JavaScriptCore/runtime/JSGlobalObject.cpp >+++ b/Source/JavaScriptCore/runtime/JSGlobalObject.cpp >@@ -598,6 +598,14 @@ putDirectWithoutTransition(vm, vm.propertyNames-> jsName, lowerName ## Construct > resetPrototype(vm, prototype()); > } > >+bool JSGlobalObject::hasProfiler() const >+{ >+ if (m_debugger && m_debugger->hasProfilingClient()) >+ return true; >+ >+ return globalObjectMethodTable()->supportsProfiling(this); >+} >+ > void JSGlobalObject::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot) > { > JSGlobalObject* thisObject = jsCast<JSGlobalObject*>(cell); >diff --git a/Source/JavaScriptCore/runtime/JSGlobalObject.h b/Source/JavaScriptCore/runtime/JSGlobalObject.h >index 62d1495..1d74305 100644 >--- a/Source/JavaScriptCore/runtime/JSGlobalObject.h >+++ b/Source/JavaScriptCore/runtime/JSGlobalObject.h >@@ -358,7 +358,7 @@ public: > DECLARE_EXPORT_INFO; > > bool hasDebugger() const { return m_debugger; } >- bool hasProfiler() const { return globalObjectMethodTable()->supportsProfiling(this); } >+ bool hasProfiler() const; > const RuntimeFlags& runtimeFlags() const { return m_runtimeFlags; } > > protected: >diff --git a/Source/JavaScriptCore/runtime/JSJob.cpp b/Source/JavaScriptCore/runtime/JSJob.cpp >index 92d2122..cdc2093 100644 >--- a/Source/JavaScriptCore/runtime/JSJob.cpp >+++ b/Source/JavaScriptCore/runtime/JSJob.cpp >@@ -70,7 +70,7 @@ void JSJobMicrotask::run(ExecState* exec) > MarkedArgumentBuffer handlerArguments; > for (unsigned index = 0, length = m_arguments->length(); index < length; ++index) > handlerArguments.append(m_arguments->JSArray::get(exec, index)); >- call(exec, m_job.get(), handlerCallType, handlerCallData, jsUndefined(), handlerArguments); >+ profiledCall(exec, ProfilingReasonMicrotask, m_job.get(), handlerCallType, handlerCallData, jsUndefined(), handlerArguments); > } > > } // namespace JSC >diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog >index 88b257f..2662f19 100644 >--- a/Source/WebCore/ChangeLog >+++ b/Source/WebCore/ChangeLog >@@ -1,3 +1,31 @@ >+2015-12-09 Joseph Pecoraro <pecoraro@apple.com> >+ >+ Web Inspector: Add JSContext Script Profiling >+ https://bugs.webkit.org/show_bug.cgi?id=151899 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Tests: inspector/script-profiler/event-type-API.html >+ inspector/script-profiler/event-type-Microtask.html >+ inspector/script-profiler/tracking.html >+ >+ * ForwardingHeaders/inspector/agents/InspectorScriptProfilerAgent.h: Added. >+ >+ * inspector/InspectorController.h: >+ * inspector/InspectorController.cpp: >+ (WebCore::InspectorController::InspectorController): >+ Create the new agent, even if we don't use it yet for Page inspection. >+ >+ (WebCore::InspectorController::willCallInjectedScriptFunction): Deleted. >+ (WebCore::InspectorController::didCallInjectedScriptFunction): Deleted. >+ No longer needed, implementation was pushed up into InjectedScriptBase. >+ >+ * inspector/InspectorCSSAgent.cpp: >+ (WebCore::InspectorCSSAgent::buildArrayForMatchedRuleList): >+ * inspector/InspectorReplayAgent.cpp: >+ (WebCore::buildInspectorObjectForSession): >+ Correct types. >+ > 2015-12-08 Per Arne Vollan <peavo@outlook.com> > > [WinCairo] Compile error. >diff --git a/Source/WebCore/ForwardingHeaders/inspector/agents/InspectorScriptProfilerAgent.h b/Source/WebCore/ForwardingHeaders/inspector/agents/InspectorScriptProfilerAgent.h >new file mode 100644 >index 0000000..5846a44 >--- /dev/null >+++ b/Source/WebCore/ForwardingHeaders/inspector/agents/InspectorScriptProfilerAgent.h >@@ -0,0 +1,4 @@ >+#ifndef WebCore_FWD_InspectorScriptProfilerAgent_h >+#define WebCore_FWD_InspectorScriptProfilerAgent_h >+#include <JavaScriptCore/InspectorScriptProfilerAgent.h> >+#endif >diff --git a/Source/WebCore/inspector/InspectorCSSAgent.cpp b/Source/WebCore/inspector/InspectorCSSAgent.cpp >index b353328..34eb769 100644 >--- a/Source/WebCore/inspector/InspectorCSSAgent.cpp >+++ b/Source/WebCore/inspector/InspectorCSSAgent.cpp >@@ -1047,7 +1047,7 @@ RefPtr<Inspector::Protocol::Array<Inspector::Protocol::CSS::RuleMatch>> Inspecto > > auto matchingSelectors = Inspector::Protocol::Array<int>::create(); > const CSSSelectorList& selectorList = matchedRule->selectorList(); >- long index = 0; >+ int index = 0; > for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(selector)) { > unsigned ignoredSpecificity; > bool matched = selectorChecker.match(selector, element, context, ignoredSpecificity); >diff --git a/Source/WebCore/inspector/InspectorController.cpp b/Source/WebCore/inspector/InspectorController.cpp >index e66d418..f7e41b2 100644 >--- a/Source/WebCore/inspector/InspectorController.cpp >+++ b/Source/WebCore/inspector/InspectorController.cpp >@@ -70,6 +70,7 @@ > #include <inspector/InspectorFrontendRouter.h> > #include <inspector/agents/InspectorAgent.h> > #include <inspector/agents/InspectorHeapAgent.h> >+#include <inspector/agents/InspectorScriptProfilerAgent.h> > #include <profiler/LegacyProfiler.h> > #include <runtime/JSLock.h> > #include <wtf/Stopwatch.h> >@@ -167,6 +168,7 @@ InspectorController::InspectorController(Page& page, InspectorClient* inspectorC > > m_agents.append(std::make_unique<InspectorDOMDebuggerAgent>(pageContext, m_domAgent, debuggerAgent)); > m_agents.append(std::make_unique<InspectorHeapAgent>(pageContext)); >+ m_agents.append(std::make_unique<InspectorScriptProfilerAgent>(pageContext)); > m_agents.append(std::make_unique<InspectorApplicationCacheAgent>(pageContext, pageAgent)); > m_agents.append(std::make_unique<InspectorLayerTreeAgent>(pageContext)); > >@@ -444,16 +446,6 @@ InspectorEvaluateHandler InspectorController::evaluateHandler() const > return WebCore::evaluateHandlerFromAnyThread; > } > >-void InspectorController::willCallInjectedScriptFunction(JSC::ExecState* scriptState, const String&, int) >-{ >- LegacyProfiler::profiler()->suspendProfiling(scriptState); >-} >- >-void InspectorController::didCallInjectedScriptFunction(JSC::ExecState* scriptState) >-{ >- LegacyProfiler::profiler()->unsuspendProfiling(scriptState); >-} >- > void InspectorController::frontendInitialized() > { > #if ENABLE(REMOTE_INSPECTOR) >diff --git a/Source/WebCore/inspector/InspectorController.h b/Source/WebCore/inspector/InspectorController.h >index f1d3e5c..0019a7b 100644 >--- a/Source/WebCore/inspector/InspectorController.h >+++ b/Source/WebCore/inspector/InspectorController.h >@@ -121,8 +121,6 @@ public: > virtual bool canAccessInspectedScriptState(JSC::ExecState*) const override; > virtual Inspector::InspectorFunctionCallHandler functionCallHandler() const override; > virtual Inspector::InspectorEvaluateHandler evaluateHandler() const override; >- virtual void willCallInjectedScriptFunction(JSC::ExecState*, const String& scriptName, int scriptLine) override; >- virtual void didCallInjectedScriptFunction(JSC::ExecState*) override; > virtual void frontendInitialized() override; > virtual Ref<WTF::Stopwatch> executionStopwatch() override; > virtual PageScriptDebugServer& scriptDebugServer() override; >diff --git a/Source/WebCore/inspector/InspectorReplayAgent.cpp b/Source/WebCore/inspector/InspectorReplayAgent.cpp >index b9d0433..3d4d5cb 100644 >--- a/Source/WebCore/inspector/InspectorReplayAgent.cpp >+++ b/Source/WebCore/inspector/InspectorReplayAgent.cpp >@@ -77,7 +77,7 @@ static Ref<Inspector::Protocol::Replay::ReplaySession> buildInspectorObjectForSe > auto segments = Inspector::Protocol::Array<SegmentIdentifier>::create(); > > for (auto& segment : *session) >- segments->addItem(segment->identifier()); >+ segments->addItem(static_cast<int>(segment->identifier())); > > return Inspector::Protocol::Replay::ReplaySession::create() > .setId(session->identifier()) >diff --git a/Source/WebInspectorUI/ChangeLog b/Source/WebInspectorUI/ChangeLog >index 88a6800..d9b6d71 100644 >--- a/Source/WebInspectorUI/ChangeLog >+++ b/Source/WebInspectorUI/ChangeLog >@@ -1,3 +1,63 @@ >+2015-12-09 Joseph Pecoraro <pecoraro@apple.com> >+ >+ Web Inspector: Add JSContext Script Profiling >+ https://bugs.webkit.org/show_bug.cgi?id=151899 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Enable the Timeline tab for JSContext inspection. Currently only >+ JSContext inspection will use the ScriptProfiler domain, it will >+ be enabled for Page inspection in a follow-up. >+ >+ First pass at the UI creates basic ScriptTimelineRecords for >+ ScriptProfiler events. At the end of recording, when profiles >+ are downloaded, it attaches profiles to the timeline records >+ and refreshes the Scripts timeline. >+ >+ * UserInterface/Base/Main.js: >+ (WebInspector.loaded): >+ * UserInterface/Controllers/TimelineManager.js: >+ (WebInspector.TimelineManager): >+ (WebInspector.TimelineManager.defaultInstruments): >+ (WebInspector.TimelineManager.prototype.computeElapsedTime): >+ (WebInspector.TimelineManager.prototype.scriptProfilerIsTracking): >+ (WebInspector.TimelineManager.prototype._loadNewRecording): >+ (WebInspector.TimelineManager.prototype._mainResourceDidChange): >+ (WebInspector.TimelineManager.prototype._resourceWasAdded): >+ (WebInspector.TimelineManager.prototype._garbageCollected): >+ (WebInspector.TimelineManager.prototype._addRecord): Deleted. >+ * UserInterface/Main.html: >+ * UserInterface/Models/Instrument.js: >+ (WebInspector.Instrument.startLegacyTimelineAgent): >+ * UserInterface/Models/ScriptInstrument.js: >+ (WebInspector.ScriptInstrument.prototype.startInstrumentation): >+ (WebInspector.ScriptInstrument.prototype.stopInstrumentation): >+ (WebInspector.ScriptInstrument): >+ * UserInterface/Models/ScriptTimelineRecord.js: >+ (WebInspector.ScriptTimelineRecord.prototype.get profilePayload): >+ (WebInspector.ScriptTimelineRecord.prototype._initializeProfileFromPayload): >+ (WebInspector.ScriptTimelineRecord): >+ (WebInspector.ScriptTimelineRecord.EventType.displayName): >+ * UserInterface/Models/Timeline.js: >+ (WebInspector.Timeline.prototype.refresh): >+ * UserInterface/Models/TimelineRecording.js: >+ (WebInspector.TimelineRecording.sourceCodeTimelinesSupported): >+ (WebInspector.TimelineRecording.prototype.timelineForRecordType): >+ (WebInspector.TimelineRecording.prototype.addRecord): >+ * UserInterface/Protocol/ScriptProfilerObserver.js: Copied from Source/WebInspectorUI/UserInterface/Models/ScriptInstrument.js. >+ (WebInspector.ScriptProfilerObserver.prototype.trackingStart): >+ (WebInspector.ScriptProfilerObserver.prototype.trackingUpdate): >+ (WebInspector.ScriptProfilerObserver.prototype.trackingComplete): >+ (WebInspector.ScriptProfilerObserver): >+ * UserInterface/Views/ScriptTimelineView.js: >+ (WebInspector.ScriptTimelineView): >+ (WebInspector.ScriptTimelineView.prototype._processPendingRecords): >+ (WebInspector.ScriptTimelineView.prototype._scriptTimelineRecordRefreshed): >+ * UserInterface/Views/TimelineRecordTreeElement.js: >+ (WebInspector.TimelineRecordTreeElement): >+ * UserInterface/Views/TimelineTabContentView.js: >+ (WebInspector.TimelineTabContentView.isTabAllowed): >+ > 2015-12-08 Joseph Pecoraro <pecoraro@apple.com> > > Web Inspector: Workaround arrow function issue in TimelineOverviewGraph.js >diff --git a/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js b/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js >index 4265004f08cfbc0bbe9f9da079291f949b4dfcc3..c974bc314e9ab412b6c0b7e3dd90ee551b235080 100644 >GIT binary patch >delta 58 >zcmezNl;zJemJO0Sd6OB681flP7!nzZ8L}rUa7#{(?%)#iWyoYemvP)Iy7OTr06Y{D >AI{*Lx > >delta 14 >WcmezKjOE)?mJO0SH_zXBw-NwCItZx% > >diff --git a/Source/WebInspectorUI/UserInterface/Base/Main.js b/Source/WebInspectorUI/UserInterface/Base/Main.js >index da9a131..2d3d22e 100644 >--- a/Source/WebInspectorUI/UserInterface/Base/Main.js >+++ b/Source/WebInspectorUI/UserInterface/Base/Main.js >@@ -76,6 +76,8 @@ WebInspector.loaded = function() > InspectorBackend.registerDOMStorageDispatcher(new WebInspector.DOMStorageObserver); > if (InspectorBackend.registerApplicationCacheDispatcher) > InspectorBackend.registerApplicationCacheDispatcher(new WebInspector.ApplicationCacheObserver); >+ if (InspectorBackend.registerScriptProfilerDispatcher) >+ InspectorBackend.registerScriptProfilerDispatcher(new WebInspector.ScriptProfilerObserver); > if (InspectorBackend.registerTimelineDispatcher) > InspectorBackend.registerTimelineDispatcher(new WebInspector.TimelineObserver); > if (InspectorBackend.registerCSSDispatcher) >diff --git a/Source/WebInspectorUI/UserInterface/Controllers/TimelineManager.js b/Source/WebInspectorUI/UserInterface/Controllers/TimelineManager.js >index 197b275..d1a45db 100644 >--- a/Source/WebInspectorUI/UserInterface/Controllers/TimelineManager.js >+++ b/Source/WebInspectorUI/UserInterface/Controllers/TimelineManager.js >@@ -42,6 +42,8 @@ WebInspector.TimelineManager = class TimelineManager extends WebInspector.Object > this._autoCapturingMainResource = null; > this._boundStopCapturing = this.stopCapturing.bind(this); > >+ this._scriptProfileRecords = null; >+ > this.reset(); > } > >@@ -49,6 +51,9 @@ WebInspector.TimelineManager = class TimelineManager extends WebInspector.Object > > static defaultInstruments() > { >+ if (WebInspector.debuggableType === WebInspector.DebuggableType.JavaScript) >+ return [new WebInspector.ScriptInstrument]; >+ > let defaults = [ > new WebInspector.NetworkInstrument, > new WebInspector.LayoutInstrument, >@@ -153,6 +158,11 @@ WebInspector.TimelineManager = class TimelineManager extends WebInspector.Object > return this._activeRecording.computeElapsedTime(timestamp); > } > >+ scriptProfilerIsTracking() >+ { >+ return this._scriptProfileRecords !== null; >+ } >+ > // Protected > > capturingStarted(startTime) >@@ -499,7 +509,7 @@ WebInspector.TimelineManager = class TimelineManager extends WebInspector.Object > this._activeRecording.addRecord(record); > > // Only worry about dead time after the load event. >- if (!isNaN(WebInspector.frameResourceManager.mainFrame.loadEventTimestamp)) >+ if (WebInspector.frameResourceManager.mainFrame && isNaN(WebInspector.frameResourceManager.mainFrame.loadEventTimestamp)) > this._resetAutoRecordingDeadTimeTimeout(); > } > >@@ -610,6 +620,36 @@ WebInspector.TimelineManager = class TimelineManager extends WebInspector.Object > let collection = event.data.collection; > this._addRecord(new WebInspector.ScriptTimelineRecord(WebInspector.ScriptTimelineRecord.EventType.GarbageCollected, collection.startTime, collection.endTime, null, null, collection)); > } >+ >+ scriptProfilerTrackingStarted(timestamp) >+ { >+ this._scriptProfileRecords = []; >+ >+ this.capturingStarted(timestamp); >+ } >+ >+ scriptProfilerTrackingUpdated(event) >+ { >+ let {startTime, endTime, type} = event; >+ let scriptRecordType = type === ScriptProfilerAgent.EventType.Microtask ? WebInspector.ScriptTimelineRecord.EventType.MicrotaskDispatched : WebInspector.ScriptTimelineRecord.EventType.ScriptEvaluated; >+ let record = new WebInspector.ScriptTimelineRecord(scriptRecordType, startTime, endTime, null, null, null, null); >+ this._scriptProfileRecords.push(record); >+ this._addRecord(record); >+ } >+ >+ scriptProfilerTrackingCompleted(profiles) >+ { >+ if (profiles) { >+ console.assert(this._scriptProfileRecords.length === profiles.length, this._scriptProfileRecords.length, profiles.length); >+ for (let i = 0; i < this._scriptProfileRecords.length; ++i) >+ this._scriptProfileRecords[i].setProfilePayload(profiles[i]); >+ } >+ >+ this._scriptProfileRecords = null; >+ >+ let timeline = this.activeRecording.timelineForRecordType(WebInspector.TimelineRecord.Type.Script); >+ timeline.refresh(); >+ } > }; > > WebInspector.TimelineManager.Event = { >diff --git a/Source/WebInspectorUI/UserInterface/Main.html b/Source/WebInspectorUI/UserInterface/Main.html >index 211bae4..4966089 100644 >--- a/Source/WebInspectorUI/UserInterface/Main.html >+++ b/Source/WebInspectorUI/UserInterface/Main.html >@@ -244,6 +244,7 @@ > <script src="Protocol/RemoteObject.js"></script> > <script src="Protocol/ReplayObserver.js"></script> > <script src="Protocol/RuntimeObserver.js"></script> >+ <script src="Protocol/ScriptProfilerObserver.js"></script> > <script src="Protocol/TimelineObserver.js"></script> > > <script src="Models/BreakpointAction.js"></script> >diff --git a/Source/WebInspectorUI/UserInterface/Models/Instrument.js b/Source/WebInspectorUI/UserInterface/Models/Instrument.js >index 5d12c51..b7385a6 100644 >--- a/Source/WebInspectorUI/UserInterface/Models/Instrument.js >+++ b/Source/WebInspectorUI/UserInterface/Models/Instrument.js >@@ -29,6 +29,8 @@ WebInspector.Instrument = class Instrument extends WebInspector.Object > > static startLegacyTimelineAgent() > { >+ console.assert(window.TimelineAgent, "Attempted to start legacy timeline agent without TimelineAgent."); >+ > if (WebInspector.Instrument._legacyTimelineAgentStarted) > return; > >diff --git a/Source/WebInspectorUI/UserInterface/Models/ScriptInstrument.js b/Source/WebInspectorUI/UserInterface/Models/ScriptInstrument.js >index f1022ea..7eb3c54 100644 >--- a/Source/WebInspectorUI/UserInterface/Models/ScriptInstrument.js >+++ b/Source/WebInspectorUI/UserInterface/Models/ScriptInstrument.js >@@ -31,4 +31,32 @@ WebInspector.ScriptInstrument = class ScriptInstrument extends WebInspector.Inst > { > return WebInspector.TimelineRecord.Type.Script; > } >+ >+ startInstrumentation() >+ { >+ // COMPATIBILITY (iOS 9): Legacy backends did not have ScriptProfilerAgent. They use TimelineAgent. >+ // FIXME: ScriptProfilerAgent is only enabled for JavaScript debuggables until we transition TimelineAgent for Web debuggables. >+ if (!window.ScriptProfilerAgent || WebInspector.debuggableType !== WebInspector.DebuggableType.JavaScript) { >+ super.startInstrumentation(); >+ return; >+ } >+ >+ // FIXME: Make this some UI visible option. >+ const includeProfiles = true; >+ >+ ScriptProfilerAgent.startTracking(includeProfiles); >+ } >+ >+ stopInstrumentation() >+ { >+ // COMPATIBILITY (iOS 9): Legacy backends did not have ScriptProfilerAgent. They use TimelineAgent. >+ // FIXME: ScriptProfilerAgent is only enabled for JavaScript debuggables until we transition TimelineAgent for Web debuggables. >+ if (!window.ScriptProfilerAgent || WebInspector.debuggableType !== WebInspector.DebuggableType.JavaScript) { >+ super.stopInstrumentation(); >+ return; >+ } >+ >+ ScriptProfilerAgent.stopTracking(); >+ } >+ > }; >diff --git a/Source/WebInspectorUI/UserInterface/Models/ScriptTimelineRecord.js b/Source/WebInspectorUI/UserInterface/Models/ScriptTimelineRecord.js >index 47f6b48..b942ecb 100644 >--- a/Source/WebInspectorUI/UserInterface/Models/ScriptTimelineRecord.js >+++ b/Source/WebInspectorUI/UserInterface/Models/ScriptTimelineRecord.js >@@ -71,6 +71,11 @@ WebInspector.ScriptTimelineRecord = class ScriptTimelineRecord extends WebInspec > cookie[WebInspector.ScriptTimelineRecord.DetailsCookieKey] = this._details; > } > >+ setProfilePayload(profilePayload) >+ { >+ this._profilePayload = profilePayload; >+ } >+ > // Private > > _initializeProfileFromPayload(payload) >@@ -155,7 +160,8 @@ WebInspector.ScriptTimelineRecord = class ScriptTimelineRecord extends WebInspec > > WebInspector.ScriptTimelineRecord.EventType = { > ScriptEvaluated: "script-timeline-record-script-evaluated", >- EventDispatched: "script-timeline-record-event-dispatch", >+ MicrotaskDispatched: "script-timeline-record-microtask-dispatched", >+ EventDispatched: "script-timeline-record-event-dispatched", > ProbeSampleRecorded: "script-timeline-record-probe-sample-recorded", > TimerFired: "script-timeline-record-timer-fired", > TimerInstalled: "script-timeline-record-timer-installed", >@@ -164,7 +170,7 @@ WebInspector.ScriptTimelineRecord.EventType = { > AnimationFrameRequested: "script-timeline-record-animation-frame-requested", > AnimationFrameCanceled: "script-timeline-record-animation-frame-canceled", > ConsoleProfileRecorded: "script-timeline-record-console-profile-recorded", >- GarbageCollected: "script-timeline-record-garbage-collected" >+ GarbageCollected: "script-timeline-record-garbage-collected", > }; > > WebInspector.ScriptTimelineRecord.EventType.displayName = function(eventType, details, includeDetailsInMainTitle) >@@ -328,12 +334,13 @@ WebInspector.ScriptTimelineRecord.EventType.displayName = function(eventType, de > switch(eventType) { > case WebInspector.ScriptTimelineRecord.EventType.ScriptEvaluated: > return WebInspector.UIString("Script Evaluated"); >+ case WebInspector.ScriptTimelineRecord.EventType.MicrotaskDispatched: >+ return WebInspector.UIString("Microtask Dispatched"); > case WebInspector.ScriptTimelineRecord.EventType.EventDispatched: > if (details && (details instanceof String || typeof details === "string")) { > var eventDisplayName = WebInspector.ScriptTimelineRecord._eventDisplayNames.get(details) || details.capitalize(); > return WebInspector.UIString("%s Event Dispatched").format(eventDisplayName); > } >- > return WebInspector.UIString("Event Dispatched"); > case WebInspector.ScriptTimelineRecord.EventType.ProbeSampleRecorded: > return WebInspector.UIString("Probe Sample Recorded"); >diff --git a/Source/WebInspectorUI/UserInterface/Models/Timeline.js b/Source/WebInspectorUI/UserInterface/Models/Timeline.js >index 3c8b8d3..9339b45 100644 >--- a/Source/WebInspectorUI/UserInterface/Models/Timeline.js >+++ b/Source/WebInspectorUI/UserInterface/Models/Timeline.js >@@ -95,6 +95,11 @@ WebInspector.Timeline = class Timeline extends WebInspector.Object > cookie[WebInspector.Timeline.TimelineTypeCookieKey] = this._type; > } > >+ refresh() >+ { >+ this.dispatchEventToListeners(WebInspector.Timeline.Event.Refreshed); >+ } >+ > // Private > > _updateTimesIfNeeded(record) >@@ -124,7 +129,8 @@ WebInspector.Timeline = class Timeline extends WebInspector.Object > WebInspector.Timeline.Event = { > Reset: "timeline-reset", > RecordAdded: "timeline-record-added", >- TimesUpdated: "timeline-times-updated" >+ TimesUpdated: "timeline-times-updated", >+ Refreshed: "timeline-refreshed", > }; > > WebInspector.Timeline.TimelineTypeCookieKey = "timeline-type"; >diff --git a/Source/WebInspectorUI/UserInterface/Models/TimelineRecording.js b/Source/WebInspectorUI/UserInterface/Models/TimelineRecording.js >index 0bdecd2..317378b 100644 >--- a/Source/WebInspectorUI/UserInterface/Models/TimelineRecording.js >+++ b/Source/WebInspectorUI/UserInterface/Models/TimelineRecording.js >@@ -55,6 +55,13 @@ WebInspector.TimelineRecording = class TimelineRecording extends WebInspector.Ob > this.reset(true); > } > >+ // Static >+ >+ static sourceCodeTimelinesSupported() >+ { >+ return WebInspector.debuggableType === WebInspector.DebuggableType.Web; >+ } >+ > // Public > > get displayName() >@@ -172,6 +179,11 @@ WebInspector.TimelineRecording = class TimelineRecording extends WebInspector.Ob > return this._timelines.get(instrument.timelineRecordType); > } > >+ timelineForRecordType(recordType) >+ { >+ return this._timelines.get(recordType); >+ } >+ > addInstrument(instrument) > { > console.assert(instrument instanceof WebInspector.Instrument, instrument); >@@ -216,6 +228,9 @@ WebInspector.TimelineRecording = class TimelineRecording extends WebInspector.Ob > if (record.type === WebInspector.TimelineRecord.Type.Network || record.type === WebInspector.TimelineRecord.Type.RenderingFrame) > return; > >+ if (!WebInspector.TimelineRecording.sourceCodeTimelinesSupported()) >+ return; >+ > // Add the record to the source code timelines. > var activeMainResource = WebInspector.frameResourceManager.mainFrame.provisionalMainResource || WebInspector.frameResourceManager.mainFrame.mainResource; > var sourceCode = record.sourceCodeLocation ? record.sourceCodeLocation.sourceCode : activeMainResource; >diff --git a/Source/WebInspectorUI/UserInterface/Models/ScriptInstrument.js b/Source/WebInspectorUI/UserInterface/Protocol/ScriptProfilerObserver.js >similarity index 74% >copy from Source/WebInspectorUI/UserInterface/Models/ScriptInstrument.js >copy to Source/WebInspectorUI/UserInterface/Protocol/ScriptProfilerObserver.js >index f1022ea..cd2a70f 100644 >--- a/Source/WebInspectorUI/UserInterface/Models/ScriptInstrument.js >+++ b/Source/WebInspectorUI/UserInterface/Protocol/ScriptProfilerObserver.js >@@ -23,12 +23,22 @@ > * THE POSSIBILITY OF SUCH DAMAGE. > */ > >-WebInspector.ScriptInstrument = class ScriptInstrument extends WebInspector.Instrument >+WebInspector.ScriptProfilerObserver = class ScriptProfilerObserver > { >- // Protected >+ // Events defined by the "ScriptProfiler" domain. > >- get timelineRecordType() >+ trackingStart(timestamp) > { >- return WebInspector.TimelineRecord.Type.Script; >+ WebInspector.timelineManager.scriptProfilerTrackingStarted(timestamp); >+ } >+ >+ trackingUpdate(event) >+ { >+ WebInspector.timelineManager.scriptProfilerTrackingUpdated(event); >+ } >+ >+ trackingComplete(profiles) >+ { >+ WebInspector.timelineManager.scriptProfilerTrackingCompleted(profiles); > } > }; >diff --git a/Source/WebInspectorUI/UserInterface/Views/ScriptTimelineView.js b/Source/WebInspectorUI/UserInterface/Views/ScriptTimelineView.js >index 126a49d..22f8168a 100644 >--- a/Source/WebInspectorUI/UserInterface/Views/ScriptTimelineView.js >+++ b/Source/WebInspectorUI/UserInterface/Views/ScriptTimelineView.js >@@ -71,6 +71,7 @@ WebInspector.ScriptTimelineView = class ScriptTimelineView extends WebInspector. > this.addSubview(this._dataGrid); > > timeline.addEventListener(WebInspector.Timeline.Event.RecordAdded, this._scriptTimelineRecordAdded, this); >+ timeline.addEventListener(WebInspector.Timeline.Event.Refreshed, this._scriptTimelineRecordRefreshed, this); > > this._pendingRecords = []; > } >@@ -217,6 +218,10 @@ WebInspector.ScriptTimelineView = class ScriptTimelineView extends WebInspector. > > _processPendingRecords() > { >+ // FIXME: Provide a "During Recording" UI for TimelineViews. >+ if (WebInspector.timelineManager.scriptProfilerIsTracking()) >+ return; >+ > if (!this._pendingRecords.length) > return; > >@@ -256,6 +261,11 @@ WebInspector.ScriptTimelineView = class ScriptTimelineView extends WebInspector. > this.needsLayout(); > } > >+ _scriptTimelineRecordRefreshed(event) >+ { >+ this.needsLayout(); >+ } >+ > _dataGridFiltersDidChange(event) > { > this.timelineSidebarPanel.updateFilter(); >diff --git a/Source/WebInspectorUI/UserInterface/Views/TimelineRecordTreeElement.js b/Source/WebInspectorUI/UserInterface/Views/TimelineRecordTreeElement.js >index a1880aa..b0796aa 100644 >--- a/Source/WebInspectorUI/UserInterface/Views/TimelineRecordTreeElement.js >+++ b/Source/WebInspectorUI/UserInterface/Views/TimelineRecordTreeElement.js >@@ -79,6 +79,7 @@ WebInspector.TimelineRecordTreeElement = class TimelineRecordTreeElement extends > case WebInspector.ScriptTimelineRecord.EventType.ScriptEvaluated: > iconStyleClass = WebInspector.TimelineRecordTreeElement.EvaluatedRecordIconStyleClass; > break; >+ case WebInspector.ScriptTimelineRecord.EventType.MicrotaskDispatched: > case WebInspector.ScriptTimelineRecord.EventType.EventDispatched: > iconStyleClass = WebInspector.TimelineRecordTreeElement.EventRecordIconStyleClass; > break; >diff --git a/Source/WebInspectorUI/UserInterface/Views/TimelineTabContentView.js b/Source/WebInspectorUI/UserInterface/Views/TimelineTabContentView.js >index 66a1a9d..bc6278d 100644 >--- a/Source/WebInspectorUI/UserInterface/Views/TimelineTabContentView.js >+++ b/Source/WebInspectorUI/UserInterface/Views/TimelineTabContentView.js >@@ -44,7 +44,7 @@ WebInspector.TimelineTabContentView = class TimelineTabContentView extends WebIn > > static isTabAllowed() > { >- return !!window.TimelineAgent; >+ return !!window.TimelineAgent || !!window.ScriptProfilerAgent; > } > > // Public
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Flags:
bburg
:
review-
buildbot
:
commit-queue-
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 151899
:
266690
|
266691
|
267061
|
267062
|
267066
|
267067
|
267226
|
267436
|
267438
|
267448