Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(6967)

Unified Diff: chrome/browser/resources/tracing/timeline_model.js

Issue 9706010: about:tracing support for TRACE_ASYNC_START/FINISH events. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: try again Created 8 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/resources/tracing/timeline_model.js
diff --git a/chrome/browser/resources/tracing/timeline_model.js b/chrome/browser/resources/tracing/timeline_model.js
index b2cc3803385886531005a6ae79a052232d9d6b43..a309f17a20aeffac7b20b4ad33da7902246391d1 100644
--- a/chrome/browser/resources/tracing/timeline_model.js
+++ b/chrome/browser/resources/tracing/timeline_model.js
@@ -21,18 +21,8 @@
*/
cr.define('tracing', function() {
/**
- * A TimelineSlice represents an interval of time on a given resource plus
- * parameters associated with that interval.
- *
- * A slice is typically associated with a specific trace event pair on a
- * specific thread.
- * For example,
- * TRACE_EVENT_BEGIN1("x","myArg", 7) at time=0.1ms
- * TRACE_EVENT_END() at time=0.3ms
- * This results in a single timeline slice from 0.1 with duration 0.2 on a
- * specific thread.
- *
- * A slice can also be an interval of time on a Cpu on a TimelineCpu.
+ * A TimelineSlice represents an interval of time plus parameters associated
+ * with that interval.
*
* All time units are stored in milliseconds.
* @constructor
@@ -43,7 +33,6 @@ cr.define('tracing', function() {
this.colorId = colorId;
this.args = args;
this.didNotFinish = false;
- this.subSlices = [];
if (opt_duration !== undefined)
this.duration = opt_duration;
}
@@ -59,18 +48,81 @@ cr.define('tracing', function() {
};
/**
+ * A TimelineThreadSlice represents an interval of time on a thread resource
+ * with associated nestinged slice information.
+ *
+ * ThreadSlices are typically associated with a specific trace event pair on a
+ * specific thread.
+ * For example,
+ * TRACE_EVENT_BEGIN1("x","myArg", 7) at time=0.1ms
+ * TRACE_EVENT_END0() at time=0.3ms
+ * This results in a single timeline slice from 0.1 with duration 0.2 on a
+ * specific thread.
+ *
+ * @constructor
+ */
+ function TimelineThreadSlice(title, colorId, start, args, opt_duration) {
+ TimelineSlice.call(this, title, colorId, start, args, opt_duration);
+ this.subSlices = [];
+ }
+
+ TimelineThreadSlice.prototype = {
+ __proto__: TimelineSlice.prototype
+ };
+
+ /**
+ * A TimelineAsyncSlice represents an interval of time during which an
+ * asynchronous operation is in progress. An AsyncSlice consumes no CPU time
+ * itself and so is only associated with Threads at its start and end point.
+ *
+ * @constructor
+ */
+ function TimelineAsyncSlice(title, colorId, start, args) {
+ TimelineSlice.call(this, title, colorId, start, args);
+ };
+
+ TimelineAsyncSlice.prototype = {
+ __proto__: TimelineSlice.prototype,
+
+ id: undefined,
+
+ startThread: undefined,
+
+ endThread: undefined
+ };
+
+ /**
* A TimelineThread stores all the trace events collected for a particular
- * thread. We organize the slices on a thread by "subrows," where subrow 0
- * has all the root slices, subrow 1 those nested 1 deep, and so on. There
- * is also a set of non-nested subrows.
+ * thread. We organize the synchronous slices on a thread by "subrows," where
+ * subrow 0 has all the root slices, subrow 1 those nested 1 deep, and so on.
+ * The asynchronous slices are stored in an TimelineAsyncSliceGroup object.
+ *
+ * The slices stored on a TimelineThread should be instances of
+ * TimelineThreadSlice.
*
* @constructor
*/
function TimelineThread(parent, tid) {
+ if (!parent)
+ throw 'Parent must be provided.';
this.parent = parent;
this.tid = tid;
this.subRows = [[]];
- this.nonNestedSubRows = [];
+ this.asyncSlices = new TimelineAsyncSliceGroup(this.ptid);
+ }
+
+ var ptidMap = {};
+
+ /**
+ * @return {String} A string that can be used as a unique key for a specific
+ * thread within a process.
+ */
+ TimelineThread.getPTIDFromPidAndTid = function(pid, tid) {
+ if (!ptidMap[pid])
+ ptidMap[pid] = {};
+ if (!ptidMap[pid][tid])
+ ptidMap[pid][tid] = pid + ':' + tid;
+ return ptidMap[pid][tid];
}
TimelineThread.prototype = {
@@ -79,27 +131,46 @@ cr.define('tracing', function() {
*/
name: undefined,
+ /**
+ * @return {string} A concatenation of the parent id and the thread's
+ * tid. Can be used to uniquely identify a thread.
+ */
+ get ptid() {
+ return TimelineThread.getPTIDFromPidAndTid(this.tid, this.parent.pid);
+ },
+
getSubrow: function(i) {
while (i >= this.subRows.length)
this.subRows.push([]);
return this.subRows[i];
},
- addNonNestedSlice: function(slice) {
- for (var i = 0; i < this.nonNestedSubRows.length; i++) {
- var currSubRow = this.nonNestedSubRows[i];
- var lastSlice = currSubRow[currSubRow.length - 1];
- if (slice.start >= lastSlice.start + lastSlice.duration) {
- currSubRow.push(slice);
- return;
- }
+
+ shiftSubRow_: function(subRow, amount) {
+ for (var tS = 0; tS < subRow.length; tS++) {
+ var slice = subRow[tS];
+ slice.start = (slice.start + amount);
+ }
+ },
+
+ /**
+ * Shifts all the timestamps inside this thread forward by the amount
+ * specified.
+ */
+ shiftTimestampsForward: function(amount) {
+ if (this.cpuSlices)
+ this.shiftSubRow_(this.cpuSlices, amount);
+
+ for (var tSR = 0; tSR < this.subRows.length; tSR++) {
+ this.shiftSubRow_(this.subRows[tSR], amount);
}
- this.nonNestedSubRows.push([slice]);
+
+ this.asyncSlices.shiftTimestampsForward(amount);
},
/**
* Updates the minTimestamp and maxTimestamp fields based on the
- * current slices and nonNestedSubRows attached to the thread.
+ * current objects associated with the thread.
*/
updateBounds: function() {
var values = [];
@@ -109,10 +180,10 @@ cr.define('tracing', function() {
values.push(slices[0].start);
values.push(slices[slices.length - 1].end);
}
- for (var i = 0; i < this.nonNestedSubRows.length; ++i) {
- slices = this.nonNestedSubRows[i];
- values.push(slices[0].start);
- values.push(slices[slices.length - 1].end);
+ if (this.asyncSlices.slices.length) {
+ this.asyncSlices.updateBounds();
+ values.push(this.asyncSlices.minTimestamp);
+ values.push(this.asyncSlices.maxTimestamp);
}
if (values.length) {
this.minTimestamp = Math.min.apply(Math, values);
@@ -134,7 +205,7 @@ cr.define('tracing', function() {
/**
* @return {String} User friendly details about this thread.
*/
- get userFriendlyDetials() {
+ get userFriendlyDetails() {
return 'pid: ' + this.parent.pid +
', tid: ' + this.tid +
(this.name ? ', name: ' + this.name : '');
@@ -191,6 +262,15 @@ cr.define('tracing', function() {
},
/**
+ * Shifts all the timestamps inside this counter forward by the amount
+ * specified.
+ */
+ shiftTimestampsForward: function(amount) {
+ for (var sI = 0; sI < this.timestamps.length; sI++)
+ this.timestamps[sI] = (this.timestamps[sI] + amount);
+ },
+
+ /**
* Updates the bounds for this counter based on the samples it contains.
*/
updateBounds: function() {
@@ -260,6 +340,17 @@ cr.define('tracing', function() {
},
/**
+ * Shifts all the timestamps inside this process forward by the amount
+ * specified.
+ */
+ shiftTimestampsForward: function(amount) {
+ for (var tid in this.threads)
+ this.threads[tid].shiftTimestampsForward(amount);
+ for (var id in this.counters)
+ this.counters[id].shiftTimestampsForward(amount);
+ },
+
+ /**
* @return {TimlineThread} The thread identified by tid on this process,
* creating it if it doesn't exist.
*/
@@ -315,6 +406,17 @@ cr.define('tracing', function() {
},
/**
+ * Shifts all the timestamps inside this CPU forward by the amount
+ * specified.
+ */
+ shiftTimestampsForward: function(amount) {
+ for (var sI = 0; sI < this.slices.length; sI++)
+ this.slices[sI].start = (this.slices[sI].start + amount);
+ for (var id in this.counters)
+ this.counters[id].shiftTimestampsForward(amount);
+ },
+
+ /**
* Updates the minTimestamp and maxTimestamp fields based on the
* current slices attached to the cpu.
*/
@@ -337,6 +439,145 @@ cr.define('tracing', function() {
return x.cpuNumber - y.cpuNumber;
};
+ /**
+ * A group of AsyncSlices.
+ * @constructor
+ */
+ function TimelineAsyncSliceGroup(name) {
+ this.name = name;
+ this.slices = [];
+ }
+
+ TimelineAsyncSliceGroup.prototype = {
+ __proto__: Object.prototype,
+
+ /**
+ * Helper function that pushes the provided slice onto the slices array.
+ */
+ push: function(slice) {
+ this.slices.push(slice);
+ },
+
+ /**
+ * @return {Number} The number of slices in this group.
+ */
+ get length() {
+ return this.slices.length;
+ },
+
+ /**
+ * Built automatically by rebuildSubRows().
+ */
+ subRows_: undefined,
+
+ /**
+ * Updates the bounds for this group based on the slices it contains.
+ */
+ sortSlices_: function() {
+ this.slices.sort(function(x, y) {
+ return x.start - y.start;
+ });
+ },
+
+ /**
+ * Shifts all the timestamps inside this group forward by the amount
+ * specified.
+ */
+ shiftTimestampsForward: function(amount) {
+ for (var sI = 0; sI < this.slices.length; sI++)
+ this.slices[sI].start = (this.slices[sI].start + amount);
+ },
+
+ /**
+ * Updates the bounds for this group based on the slices it contains.
+ */
+ updateBounds: function() {
+ this.sortSlices_();
+ if (this.slices.length) {
+ this.minTimestamp = this.slices[0].start;
+ this.maxTimestamp = this.slices[this.slices.length - 1].end;
+ } else {
+ this.minTimestamp = undefined;
+ this.maxTimestamp = undefined;
+ }
+ this.subRows_ = undefined;
+ },
+
+ get subRows() {
+ if (!this.subRows_)
+ this.rebuildSubRows_();
+ return this.subRows_;
+ },
+
+ /**
+ * Breaks up the list of slices into N rows, each of which is a list of
+ * slices that are non overlapping.
+ *
+ * It uses a very simple approach: walk through the slices in sorted order
+ * by start time. For each slice, try to fit it in an existing subRow. If it
+ * doesn't fit in any subrow, make another subRow.
+ */
+ rebuildSubRows_: function() {
+ this.sortSlices_();
+ var subRows = [];
+ for (var i = 0; i < this.slices.length; i++) {
+ var slice = this.slices[i];
+
+ var found = false;
+ for (var j = 0; j < subRows.length; j++) {
+ var subRow = subRows[j];
+ var lastSliceInSubRow = subRow[subRow.length - 1];
+ if (slice.start >= lastSliceInSubRow.end) {
+ found = true;
+ subRow.push(slice);
+ }
+ }
+ if (!found) {
+ subRows.push([slice]);
+ }
+ }
+ this.subRows_ = subRows;
+ },
+
+ /**
+ * Breaks up this group into slices based on start thread.
+ *
+ * @return {Array} An array of TimelineAsyncSliceGroups where each group has
+ * slices that started on the same thread.
+ **/
+ computeSubGroups: function() {
+ var subGroupsByPTID = {};
+ for (var i = 0; i < this.slices.length; ++i) {
+ var slice = this.slices[i];
+ var slicePTID = slice.startThread.ptid;
+ if (!subGroupsByPTID[slicePTID])
+ subGroupsByPTID[slicePTID] = new TimelineAsyncSliceGroup(this.name);
+ subGroupsByPTID[slicePTID].slices.push(slice);
+ }
+ var groups = [];
+ for (var ptid in subGroupsByPTID) {
+ var group = subGroupsByPTID[ptid];
+ group.updateBounds();
+ groups.push(group);
+ }
+ return groups;
+ }
+
+ };
+
+ /**
+ * Comparison between counters that orders by pid, then name.
+ */
+ TimelineCounter.compare = function(x, y) {
+ if (x.parent.pid != y.parent.pid) {
+ return TimelineProcess.compare(x.parent, y.parent.pid);
+ }
+ var tmp = x.name.localeCompare(y.name);
+ if (tmp == 0)
+ return x.tid - y.tid;
+ return tmp;
+ };
+
// The color pallette is split in half, with the upper
// half of the pallette being the "highlighted" verison
// of the base color. So, color 7's highlighted form is
@@ -487,6 +728,7 @@ cr.define('tracing', function() {
this.cpus = {};
this.processes = {};
this.importErrors = [];
+ this.asyncSliceGroups = {};
if (opt_eventData)
this.importEvents(opt_eventData, opt_zeroAndBoost);
@@ -519,7 +761,9 @@ cr.define('tracing', function() {
TimelineModelEmptyImporter.prototype = {
__proto__: Object.prototype,
- importEvents : function() {
+ importEvents: function() {
+ },
+ finalizeImport: function() {
}
};
@@ -579,7 +823,7 @@ cr.define('tracing', function() {
for (var s = 0; s < thread.subRows.length; s++)
hasNonEmptySubrow |= thread.subRows[s].length > 0;
- if (hasNonEmptySubrow || thread.nonNestedSubRows.legnth)
+ if (hasNonEmptySubrow || thread.asyncSlices.length > 0)
prunedThreads[tid] = thread;
}
process.threads = prunedThreads;
@@ -638,38 +882,10 @@ cr.define('tracing', function() {
if (this.minTimestamp === undefined)
return;
var timeBase = this.minTimestamp;
- var threads = this.getAllThreads();
- for (var tI = 0; tI < threads.length; tI++) {
- var thread = threads[tI];
- var shiftSubRow = function(subRow) {
- for (var tS = 0; tS < subRow.length; tS++) {
- var slice = subRow[tS];
- slice.start = (slice.start - timeBase);
- }
- };
-
- if (thread.cpuSlices)
- shiftSubRow(thread.cpuSlices);
-
- for (var tSR = 0; tSR < thread.subRows.length; tSR++) {
- shiftSubRow(thread.subRows[tSR]);
- }
- for (var tSR = 0; tSR < thread.nonNestedSubRows.length; tSR++) {
- shiftSubRow(thread.nonNestedSubRows[tSR]);
- }
- }
- var counters = this.getAllCounters();
- for (var tI = 0; tI < counters.length; tI++) {
- var counter = counters[tI];
- for (var sI = 0; sI < counter.timestamps.length; sI++)
- counter.timestamps[sI] = (counter.timestamps[sI] - timeBase);
- }
- var cpus = this.getAllCpus();
- for (var tI = 0; tI < cpus.length; tI++) {
- var cpu = cpus[tI];
- for (var sI = 0; sI < cpu.slices.length; sI++)
- cpu.slices[sI].start = (cpu.slices[sI].start - timeBase);
- }
+ for (var pid in this.processes)
+ this.processes[pid].shiftTimestampsForward(-timeBase);
+ for (var cpuNumber in this.cpus)
+ this.cpus[cpuNumber].shiftTimestampsForward(-timeBase);
this.updateBounds();
},
@@ -732,6 +948,7 @@ cr.define('tracing', function() {
* @param {Object} events Events to import.
* @param {boolean} isChildImport True the eventData being imported is an
* additional trace after the primary eventData.
+ * @return {TimelineModelImporter} The importer used for the eventData.
*/
importOneTrace_: function(eventData, isAdditionalImport) {
var importerConstructor;
@@ -747,7 +964,7 @@ cr.define('tracing', function() {
var importer = new importerConstructor(
this, eventData, isAdditionalImport);
importer.importEvents();
- this.pruneEmptyThreads();
+ return importer;
},
/**
@@ -772,12 +989,20 @@ cr.define('tracing', function() {
if (opt_zeroAndBoost === undefined)
opt_zeroAndBoost = true;
- this.importOneTrace_(eventData, false);
+ activeImporters = [];
+ var importer = this.importOneTrace_(eventData, false);
+ activeImporters.push(importer);
if (opt_additionalEventData) {
for (var i = 0; i < opt_additionalEventData.length; ++i) {
- this.importOneTrace_(opt_additionalEventData[i], true);
+ importer = this.importOneTrace_(opt_additionalEventData[i], true);
+ activeImporters.push(importer);
}
}
+ for (var i = 0; i < activeImporters.length; ++i)
+ activeImporters[i].finalizeImport();
+
+ for (var i = 0; i < activeImporters.length; ++i)
+ this.pruneEmptyThreads();
this.updateBounds();
@@ -802,10 +1027,13 @@ cr.define('tracing', function() {
getStringColorId: getStringColorId,
TimelineSlice: TimelineSlice,
+ TimelineThreadSlice: TimelineThreadSlice,
+ TimelineAsyncSlice: TimelineAsyncSlice,
TimelineThread: TimelineThread,
TimelineCounter: TimelineCounter,
TimelineProcess: TimelineProcess,
TimelineCpu: TimelineCpu,
+ TimelineAsyncSliceGroup: TimelineAsyncSliceGroup,
TimelineModel: TimelineModel
};
« no previous file with comments | « chrome/browser/resources/tracing/timeline.js ('k') | chrome/browser/resources/tracing/timeline_model_test.html » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698