Commit 57158b6f authored by alph@chromium.org's avatar alph@chromium.org

DevTools: Connect worker cpu profiles to worker tracing threads.

BUG=363976

Review URL: https://codereview.chromium.org/724013003

git-svn-id: svn://svn.chromium.org/blink/trunk@185446 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent a8b46d5b
......@@ -27,13 +27,13 @@ InspectorTest.tracingTimelineModel = function()
return InspectorTest._tracingTimelineModel;
}
InspectorTest.invokeWithTracing = function(functionName, callback, additionalCategories)
InspectorTest.invokeWithTracing = function(functionName, callback, additionalCategories, enableJSSampling)
{
InspectorTest.tracingTimelineModel().addEventListener(WebInspector.TimelineModel.Events.RecordingStarted, onTracingStarted, this);
var categories = "-*,disabled-by-default-devtools.timeline*";
if (additionalCategories)
categories += "," + additionalCategories;
InspectorTest.tracingTimelineModel()._startRecordingWithCategories(categories);
InspectorTest.tracingTimelineModel()._startRecordingWithCategories(categories, enableJSSampling);
function onTracingStarted(event)
{
......
Tests js cpu profile in timeline.
Main thread
WebCore: Worker
WebCore: Worker
<html>
<head>
<script src="../../http/tests/inspector/inspector-test.js"></script>
<script src="../../http/tests/inspector/timeline-test.js"></script>
<script src="../tracing-test.js"></script>
<script>
// Save references to the worker objects to make sure they are not GC'ed.
var worker1;
var worker2;
function startWorkerAndRunTest()
{
worker1 = new Worker("resources/worker.js");
worker1.postMessage("");
if (window.testRunner) {
testRunner.dumpAsText();
testRunner.waitUntilDone();
}
worker1.onmessage = function(event)
{
worker1.onmessage = null;
runTest();
}
}
function startSecondWorker(onActionComplete)
{
// FIXME: add a payload to the worker to make it produce a JSFrame.
worker2 = new Worker("resources/worker.js");
worker2.postMessage("");
worker2.onmessage = function(event)
{
onActionComplete();
worker2.onmessage = null;
}
}
function test()
{
InspectorTest.invokeWithTracing("startSecondWorker", processTracingEvents,
"disabled-by-default-devtools.timeline.stack", true);
var seenFunctions = new Set();
function processTracingEvents()
{
InspectorTest.addResult("Main thread");
InspectorTest.tracingTimelineModel()._mainThreadEvents.forEach(processEvent);
var threads = InspectorTest.tracingTimelineModel()._virtualThreads;
for (var i = 0; i < threads.length; ++i) {
InspectorTest.addResult(threads[i].name);
threads[i].events.forEach(processEvent);
}
InspectorTest.assertTrue(seenFunctions.has("startSecondWorker"));
InspectorTest.assertTrue(seenFunctions.has("worker2.onmessage"));
InspectorTest.completeTest();
}
function processEvent(event)
{
if (event.name !== WebInspector.TimelineModel.RecordType.JSFrame)
return;
seenFunctions.add(event.args.data.functionName);
}
}
</script>
</head>
<body onload="startWorkerAndRunTest()">
<p>
Tests js cpu profile in timeline.
</p>
</body>
</html>
......@@ -611,10 +611,11 @@ PassRefPtr<TraceEvent::ConvertableToTraceFormat> InspectorTimeStampEvent::data(E
return value.release();
}
PassRefPtr<TraceEvent::ConvertableToTraceFormat> InspectorTracingSessionIdForWorkerEvent::data(const String& sessionId, WorkerThread* workerThread)
PassRefPtr<TraceEvent::ConvertableToTraceFormat> InspectorTracingSessionIdForWorkerEvent::data(const String& sessionId, int workerId, WorkerThread* workerThread)
{
RefPtr<TracedValue> value = TracedValue::create();
value->setString("sessionId", sessionId);
value->setInteger("workerId", workerId);
value->setDouble("workerThreadId", workerThread->platformThreadId());
return value.release();
}
......
......@@ -254,7 +254,7 @@ public:
class InspectorTracingSessionIdForWorkerEvent {
public:
static PassRefPtr<TraceEvent::ConvertableToTraceFormat> data(const String& sessionId, WorkerThread*);
static PassRefPtr<TraceEvent::ConvertableToTraceFormat> data(const String& sessionId, int workerId, WorkerThread*);
};
} // namespace blink
......
......@@ -53,10 +53,10 @@ static const char autoconnectToWorkers[] = "autoconnectToWorkers";
class InspectorWorkerAgent::WorkerFrontendChannel final : public WorkerInspectorProxy::PageInspector {
WTF_MAKE_FAST_ALLOCATED;
public:
explicit WorkerFrontendChannel(InspectorFrontend::Worker* frontend, WorkerInspectorProxy* proxy)
WorkerFrontendChannel(InspectorFrontend::Worker* frontend, WorkerInspectorProxy* proxy, int id)
: m_frontend(frontend)
, m_proxy(proxy)
, m_id(s_nextId++)
, m_id(id)
, m_connected(false)
{
ASSERT(!proxy->pageInspector());
......@@ -102,11 +102,8 @@ private:
WorkerInspectorProxy* m_proxy;
int m_id;
bool m_connected;
static int s_nextId;
};
int InspectorWorkerAgent::WorkerFrontendChannel::s_nextId = 1;
PassOwnPtrWillBeRawPtr<InspectorWorkerAgent> InspectorWorkerAgent::create()
{
return adoptPtrWillBeNoop(new InspectorWorkerAgent());
......@@ -115,6 +112,7 @@ PassOwnPtrWillBeRawPtr<InspectorWorkerAgent> InspectorWorkerAgent::create()
InspectorWorkerAgent::InspectorWorkerAgent()
: InspectorBaseAgent<InspectorWorkerAgent>("Worker")
, m_frontend(0)
, m_nextId(1)
{
}
......@@ -206,8 +204,8 @@ void InspectorWorkerAgent::setTracingSessionId(const String& sessionId)
m_tracingSessionId = sessionId;
if (sessionId.isEmpty())
return;
for (WorkerIds::iterator it = m_workerIds.begin(); it != m_workerIds.end(); ++it)
it->key->writeTimelineStartedEvent(sessionId);
for (WorkerInfos::iterator it = m_workerInfos.begin(); it != m_workerInfos.end(); ++it)
it->key->writeTimelineStartedEvent(sessionId, it->value.id);
}
bool InspectorWorkerAgent::shouldPauseDedicatedWorkerOnStart()
......@@ -217,16 +215,17 @@ bool InspectorWorkerAgent::shouldPauseDedicatedWorkerOnStart()
void InspectorWorkerAgent::didStartWorker(WorkerInspectorProxy* workerInspectorProxy, const KURL& url)
{
m_workerIds.set(workerInspectorProxy, url.string());
int id = m_nextId++;
m_workerInfos.set(workerInspectorProxy, WorkerInfo(url.string(), id));
if (m_frontend && m_state->getBoolean(WorkerAgentState::workerInspectionEnabled))
createWorkerFrontendChannel(workerInspectorProxy, url.string());
createWorkerFrontendChannel(workerInspectorProxy, url.string(), id);
if (!m_tracingSessionId.isEmpty())
workerInspectorProxy->writeTimelineStartedEvent(m_tracingSessionId);
workerInspectorProxy->writeTimelineStartedEvent(m_tracingSessionId, id);
}
void InspectorWorkerAgent::workerTerminated(WorkerInspectorProxy* proxy)
{
m_workerIds.remove(proxy);
m_workerInfos.remove(proxy);
for (WorkerChannels::iterator it = m_idToChannel.begin(); it != m_idToChannel.end(); ++it) {
if (proxy == it->value->proxy()) {
m_frontend->workerTerminated(it->key);
......@@ -239,8 +238,8 @@ void InspectorWorkerAgent::workerTerminated(WorkerInspectorProxy* proxy)
void InspectorWorkerAgent::createWorkerFrontendChannelsForExistingWorkers()
{
for (WorkerIds::iterator it = m_workerIds.begin(); it != m_workerIds.end(); ++it)
createWorkerFrontendChannel(it->key, it->value);
for (WorkerInfos::iterator it = m_workerInfos.begin(); it != m_workerInfos.end(); ++it)
createWorkerFrontendChannel(it->key, it->value.url, it->value.id);
}
void InspectorWorkerAgent::destroyWorkerFrontendChannels()
......@@ -252,16 +251,16 @@ void InspectorWorkerAgent::destroyWorkerFrontendChannels()
m_idToChannel.clear();
}
void InspectorWorkerAgent::createWorkerFrontendChannel(WorkerInspectorProxy* workerInspectorProxy, const String& url)
void InspectorWorkerAgent::createWorkerFrontendChannel(WorkerInspectorProxy* workerInspectorProxy, const String& url, int id)
{
WorkerFrontendChannel* channel = new WorkerFrontendChannel(m_frontend, workerInspectorProxy);
m_idToChannel.set(channel->id(), channel);
WorkerFrontendChannel* channel = new WorkerFrontendChannel(m_frontend, workerInspectorProxy, id);
m_idToChannel.set(id, channel);
ASSERT(m_frontend);
bool autoconnectToWorkers = m_state->getBoolean(WorkerAgentState::autoconnectToWorkers);
if (autoconnectToWorkers)
channel->connectToWorker();
m_frontend->workerCreated(channel->id(), url, autoconnectToWorkers);
m_frontend->workerCreated(id, url, autoconnectToWorkers);
}
} // namespace blink
......@@ -72,17 +72,25 @@ public:
private:
InspectorWorkerAgent();
void createWorkerFrontendChannelsForExistingWorkers();
void createWorkerFrontendChannel(WorkerInspectorProxy*, const String& url);
void createWorkerFrontendChannel(WorkerInspectorProxy*, const String& url, int id);
void destroyWorkerFrontendChannels();
InspectorFrontend::Worker* m_frontend;
class WorkerInfo {
public:
WorkerInfo() : id(0) { }
WorkerInfo(const String& url, int id) : url(url), id(id) { }
String url;
int id;
};
class WorkerFrontendChannel;
typedef HashMap<int, WorkerFrontendChannel*> WorkerChannels;
WorkerChannels m_idToChannel;
typedef HashMap<WorkerInspectorProxy*, String> WorkerIds;
WorkerIds m_workerIds;
typedef HashMap<WorkerInspectorProxy*, WorkerInfo> WorkerInfos;
WorkerInfos m_workerInfos;
String m_tracingSessionId;
int m_nextId;
};
} // namespace blink
......
......@@ -87,11 +87,11 @@ void WorkerInspectorProxy::sendMessageToInspector(const String& message)
m_workerThread->interruptAndDispatchInspectorCommands();
}
void WorkerInspectorProxy::writeTimelineStartedEvent(const String& sessionId)
void WorkerInspectorProxy::writeTimelineStartedEvent(const String& sessionId, int workerId)
{
if (!m_workerThread)
return;
TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "TracingSessionIdForWorker", "data", InspectorTracingSessionIdForWorkerEvent::data(sessionId, m_workerThread));
TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "TracingSessionIdForWorker", "data", InspectorTracingSessionIdForWorkerEvent::data(sessionId, workerId, m_workerThread));
}
} // namespace blink
......@@ -33,9 +33,9 @@ public:
void connectToInspector(PageInspector*);
void disconnectFromInspector();
void sendMessageToInspector(const String&);
void writeTimelineStartedEvent(const String& sessionId);
void writeTimelineStartedEvent(const String& sessionId, int workerId);
PageInspector* pageInspector() const { return m_pageInspector; };
PageInspector* pageInspector() const { return m_pageInspector; }
private:
WorkerInspectorProxy();
......
......@@ -12,7 +12,9 @@ WebInspector.WorkerTargetManager = function(mainTarget, targetManager)
{
this._mainTarget = mainTarget;
this._targetManager = targetManager;
this._targetsByWorkerId = new Map();
mainTarget.workerManager.addEventListener(WebInspector.WorkerManager.Events.WorkerAdded, this._onWorkerAdded, this);
mainTarget.workerManager.addEventListener(WebInspector.WorkerManager.Events.WorkerRemoved, this._onWorkerRemoved, this);
mainTarget.workerManager.addEventListener(WebInspector.WorkerManager.Events.WorkersCleared, this._onWorkersCleared, this);
WebInspector.targetManager.addEventListener(WebInspector.TargetManager.Events.SuspendStateChanged, this._onSuspendStateChanged, this);
this._onSuspendStateChanged();
......@@ -35,27 +37,31 @@ WebInspector.WorkerTargetManager.prototype = {
_onWorkerAdded: function(event)
{
var data = /** @type {{workerId: number, url: string, inspectorConnected: boolean}} */ (event.data);
new WebInspector.WorkerConnection(this._mainTarget, data.workerId, data.inspectorConnected, onConnectionReady.bind(this));
new WebInspector.WorkerConnection(this._mainTarget, data.workerId, data.inspectorConnected, onConnectionReady.bind(this, data.workerId));
/**
* @this {WebInspector.WorkerTargetManager}
* @param {number} workerId
* @param {!InspectorBackendClass.Connection} connection
*/
function onConnectionReady(connection)
function onConnectionReady(workerId, connection)
{
var parsedURL = data.url.asParsedURL();
var workerId = parsedURL ? parsedURL.lastPathComponent : "#" + (++this._lastAnonymousTargetId);
this._targetManager.createTarget(WebInspector.UIString("Worker %s", workerId), connection, targetCreated.bind(this));
var workerName = parsedURL ? parsedURL.lastPathComponent : "#" + (++this._lastAnonymousTargetId);
this._targetManager.createTarget(WebInspector.UIString("Worker %s", workerName), connection, targetCreated.bind(this, workerId));
}
/**
* @this {WebInspector.WorkerTargetManager}
* @param {number} workerId
* @param {?WebInspector.Target} target
*/
function targetCreated(target)
function targetCreated(workerId, target)
{
if (!target)
return;
if (workerId)
this._targetsByWorkerId.set(workerId, target);
if (data.inspectorConnected) {
if (this._shouldPauseWorkerOnStart)
target.debuggerAgent().pause();
......@@ -105,6 +111,25 @@ WebInspector.WorkerTargetManager.prototype = {
_onWorkersCleared: function()
{
this._lastAnonymousTargetId = 0;
this._targetsByWorkerId.clear();
},
/**
* @param {!WebInspector.Event} event
*/
_onWorkerRemoved: function(event)
{
var workerId = /** @type {number} */ (event.data);
this._targetsByWorkerId.delete(workerId);
},
/**
* @param {number} workerId
* @return {?WebInspector.Target}
*/
targetByWorkerId: function(workerId)
{
return this._targetsByWorkerId.get(workerId) || null;
}
}
......@@ -173,3 +198,8 @@ WebInspector.WorkerConnection.prototype = {
__proto__: InspectorBackendClass.Connection.prototype
}
/**
* @type {?WebInspector.WorkerTargetManager}
*/
WebInspector.workerTargetManager;
......@@ -404,8 +404,6 @@ WebInspector.TimelineModel.prototype = {
];
if (captureCauses || enableJSSampling)
categoriesArray.push(disabledByDefault("devtools.timeline.stack"));
if (enableJSSampling)
this._startCpuProfilingOnAllTargets();
if (captureCauses && Runtime.experiments.isEnabled("timelineInvalidationTracking"))
categoriesArray.push(disabledByDefault("devtools.timeline.invalidationTracking"));
if (capturePictures) {
......@@ -415,7 +413,7 @@ WebInspector.TimelineModel.prototype = {
disabledByDefault("blink.graphics_context_annotations")]);
}
var categories = categoriesArray.join(",");
this._startRecordingWithCategories(categories);
this._startRecordingWithCategories(categories, enableJSSampling);
},
stopRecording: function()
......@@ -556,9 +554,12 @@ WebInspector.TimelineModel.prototype = {
/**
* @param {string} categories
* @param {boolean=} enableJSSampling
*/
_startRecordingWithCategories: function(categories)
_startRecordingWithCategories: function(categories, enableJSSampling)
{
if (enableJSSampling)
this._startCpuProfilingOnAllTargets();
this._tracingManager.start(categories, "");
},
......@@ -606,33 +607,43 @@ WebInspector.TimelineModel.prototype = {
if (error)
WebInspector.console.error(error);
if (!this._cpuProfiles)
this._cpuProfiles = {};
this._cpuProfiles[target.id()] = cpuProfile;
this._cpuProfiles = new Map();
this._cpuProfiles.set(target.id(), cpuProfile);
},
_didStopRecordingTraceEvents: function()
{
this._tracingModel.tracingComplete();
var events = this._tracingModel.devtoolsPageMetadataEvents();
var metaEvents = this._tracingModel.devtoolsPageMetadataEvents();
var workerMetadataEvents = this._tracingModel.devtoolsWorkerMetadataEvents();
this._resetProcessingState();
for (var i = 0, length = events.length; i < length; i++) {
var event = events[i];
var process = event.thread.process();
var startTime = event.startTime;
for (var i = 0, length = metaEvents.length; i < length; i++) {
var metaEvent = metaEvents[i];
var process = metaEvent.thread.process();
var startTime = metaEvent.startTime;
var endTime = Infinity;
if (i + 1 < length)
endTime = events[i + 1].startTime;
endTime = metaEvents[i + 1].startTime;
var threads = process.sortedThreads();
for (var j = 0; j < threads.length; j++) {
var thread = threads[j];
if (thread.name() === "WebCore: Worker" && workerMetadataEvents.every(function(e) { return e.args["data"]["workerThreadId"] !== thread.id(); }))
continue;
this._processThreadEvents(startTime, endTime, event.thread, thread);
var workerId = 0;
if (thread.name() === "WebCore: Worker") {
for (var k = 0; k < workerMetadataEvents.length; ++k) {
var eventData = workerMetadataEvents[k].args["data"];
if (eventData["workerThreadId"] === thread.id()) {
workerId = eventData["workerId"];
break;
}
}
if (!workerId)
continue;
}
this._processThreadEvents(startTime, endTime, metaEvent.thread, thread, workerId);
}
}
this._resetProcessingState();
......@@ -764,8 +775,9 @@ WebInspector.TimelineModel.prototype = {
* @param {?number} endTime
* @param {!WebInspector.TracingModel.Thread} mainThread
* @param {!WebInspector.TracingModel.Thread} thread
* @param {number} workerId
*/
_processThreadEvents: function(startTime, endTime, mainThread, thread)
_processThreadEvents: function(startTime, endTime, mainThread, thread, workerId)
{
var recordTypes = WebInspector.TimelineModel.RecordType;
var events = thread.events();
......@@ -798,20 +810,24 @@ WebInspector.TimelineModel.prototype = {
this._inspectedTargetEvents.push(event);
}
if (this._cpuProfiles && thread.target()) {
var cpuProfile = this._cpuProfiles[thread.target().id()];
if (cpuProfile) {
var jsSamples = WebInspector.TimelineJSProfileProcessor.generateTracingEventsFromCpuProfile(cpuProfile, thread);
var mergedEvents = threadEvents.mergeOrdered(jsSamples, WebInspector.TracingModel.Event.orderedCompareStartTime);
var jsFrameEvents = WebInspector.TimelineJSProfileProcessor.generateJSFrameEvents(mergedEvents);
mergedEvents = jsFrameEvents.mergeOrdered(mergedEvents, WebInspector.TracingModel.Event.orderedCompareStartTime);
if (virtualThread)
virtualThread.events = mergedEvents;
else
this._mainThreadEvents = mergedEvents;
this._inspectedTargetEvents = this._inspectedTargetEvents.concat(jsSamples, jsFrameEvents);
}
}
if (!this._cpuProfiles)
return;
var target = thread === mainThread ? WebInspector.targetManager.mainTarget() : WebInspector.workerTargetManager.targetByWorkerId(workerId);
if (!target)
return;
var cpuProfile = this._cpuProfiles.get(target.id());
if (!cpuProfile)
return;
this._cpuProfiles.delete(target.id());
var jsSamples = WebInspector.TimelineJSProfileProcessor.generateTracingEventsFromCpuProfile(cpuProfile, thread);
var mergedEvents = threadEvents.mergeOrdered(jsSamples, WebInspector.TracingModel.Event.orderedCompareStartTime);
var jsFrameEvents = WebInspector.TimelineJSProfileProcessor.generateJSFrameEvents(mergedEvents);
mergedEvents = jsFrameEvents.mergeOrdered(mergedEvents, WebInspector.TracingModel.Event.orderedCompareStartTime);
if (virtualThread)
virtualThread.events = mergedEvents;
else
this._mainThreadEvents = mergedEvents;
this._inspectedTargetEvents = this._inspectedTargetEvents.concat(jsSamples, jsFrameEvents);
},
/**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment