home *** CD-ROM | disk | FTP | other *** search
Text File | 2013-04-03 | 348.1 KB | 11,149 lines |
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- /**
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- * WARNING: This file is generated by generate_about_tracing_contents.py
- *
- * Do not edit directly.
- */
-
- window.FLATTENED = {};
- window.FLATTENED['base'] = true;
- window.FLATTENED['event_target'] = true;
- window.FLATTENED['measuring_stick'] = true;
- window.FLATTENED['timeline_filter'] = true;
- window.FLATTENED['timeline_selection'] = true;
- window.FLATTENED['timeline_viewport'] = true;
- window.FLATTENED['ui'] = true;
- window.FLATTENED['tracks.timeline_track'] = true;
- window.FLATTENED['tracks.timeline_container_track'] = true;
- window.FLATTENED['fast_rect_renderer'] = true;
- window.FLATTENED['timeline_color_scheme'] = true;
- window.FLATTENED['tracks.timeline_canvas_based_track'] = true;
- window.FLATTENED['sorted_array_utils'] = true;
- window.FLATTENED['tracks.timeline_slice_track'] = true;
- window.FLATTENED['timeline_slice'] = true;
- window.FLATTENED['timeline_slice_group'] = true;
- window.FLATTENED['timeline_async_slice_group'] = true;
- window.FLATTENED['timeline_thread'] = true;
- window.FLATTENED['timeline_counter'] = true;
- window.FLATTENED['timeline_process'] = true;
- window.FLATTENED['timeline_cpu'] = true;
- window.FLATTENED['timeline_model'] = true;
- window.FLATTENED['tracks.timeline_cpu_track'] = true;
- window.FLATTENED['tracks.timeline_counter_track'] = true;
- window.FLATTENED['tracks.timeline_slice_group_track'] = true;
- window.FLATTENED['tracks.timeline_async_slice_group_track'] = true;
- window.FLATTENED['tracks.timeline_thread_track'] = true;
- window.FLATTENED['tracks.timeline_process_track'] = true;
- window.FLATTENED['tracks.timeline_model_track'] = true;
- window.FLATTENED['tracks.timeline_viewport_track'] = true;
- window.FLATTENED['timeline'] = true;
- window.FLATTENED['timeline_analysis'] = true;
- window.FLATTENED['overlay'] = true;
- window.FLATTENED['timeline_category_filter_dialog'] = true;
- window.FLATTENED['timeline_find_control'] = true;
- window.FLATTENED['trace_event_importer'] = true;
- window.FLATTENED['linux_perf_parser'] = true;
- window.FLATTENED['linux_perf_bus_parser'] = true;
- window.FLATTENED['linux_perf_clock_parser'] = true;
- window.FLATTENED['linux_perf_cpufreq_parser'] = true;
- window.FLATTENED['linux_perf_drm_parser'] = true;
- window.FLATTENED['linux_perf_exynos_parser'] = true;
- window.FLATTENED['linux_perf_gesture_parser'] = true;
- window.FLATTENED['linux_perf_i915_parser'] = true;
- window.FLATTENED['linux_perf_mali_parser'] = true;
- window.FLATTENED['linux_perf_power_parser'] = true;
- window.FLATTENED['linux_perf_sched_parser'] = true;
- window.FLATTENED['linux_perf_workqueue_parser'] = true;
- window.FLATTENED['linux_perf_android_parser'] = true;
- window.FLATTENED['linux_perf_importer'] = true;
- window.FLATTENED['settings'] = true;
- window.FLATTENED['timeline_view'] = true;
- window.FLATTENED['tracing_controller'] = true;
- window.FLATTENED['profiling_view'] = true;
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- 'use strict';
-
-
- /**
- * The global object.
- * @type {!Object}
- * @const
- */
- var global = this;
-
-
- /** Platform, package, object property, and Event support. */
- this.base = (function() {
-
- /**
- * Base path for modules. Used to form URLs for module 'require' requests.
- */
- var moduleBasePath = '.';
- function setModuleBasePath(path) {
- if (path[path.length - 1] == '/')
- path = path.substring(0, path.length - 1);
- moduleBasePath = path;
- }
-
-
- function mLog(text, opt_indentLevel) {
- if (true)
- return;
-
- var spacing = '';
- var indentLevel = opt_indentLevel || 0;
- for (var i = 0; i < indentLevel; i++)
- spacing += ' ';
- console.log(spacing + text);
- }
-
- /**
- * Builds an object structure for the provided namespace path,
- * ensuring that names that already exist are not overwritten. For
- * example:
- * 'a.b.c' -> a = {};a.b={};a.b.c={};
- * @param {string} name Name of the object that this file defines.
- * @param {*=} opt_object The object to expose at the end of the path.
- * @param {Object=} opt_objectToExportTo The object to add the path to;
- * default is {@code global}.
- * @private
- */
- function exportPath(name, opt_object, opt_objectToExportTo) {
- var parts = name.split('.');
- var cur = opt_objectToExportTo || global;
-
- for (var part; parts.length && (part = parts.shift());) {
- if (!parts.length && opt_object !== undefined) {
- // last part and we have an object; use it
- cur[part] = opt_object;
- } else if (part in cur) {
- cur = cur[part];
- } else {
- cur = cur[part] = {};
- }
- }
- return cur;
- };
-
- var didLoadModules = false;
- var moduleDependencies = {};
- var moduleStylesheets = {};
-
- function addModuleDependency(moduleName, dependentModuleName) {
- if (!moduleDependencies[moduleName])
- moduleDependencies[moduleName] = [];
-
- var dependentModules = moduleDependencies[moduleName];
- var found = false;
- for (var i = 0; i < dependentModules.length; i++)
- if (dependentModules[i] == dependentModuleName)
- found = true;
- if (!found)
- dependentModules.push(dependentModuleName);
- }
-
- function addModuleStylesheet(moduleName, stylesheetName) {
- if (!moduleStylesheets[moduleName])
- moduleStylesheets[moduleName] = [];
-
- var stylesheets = moduleStylesheets[moduleName];
- var found = false;
- for (var i = 0; i < stylesheets.length; i++)
- if (stylesheets[i] == stylesheetName)
- found = true;
- if (!found)
- stylesheets.push(stylesheetName);
- }
-
- function ensureDepsLoaded() {
- if (didLoadModules)
- return;
- didLoadModules = true;
-
- var req = new XMLHttpRequest();
- var src = moduleBasePath + '/' + 'deps.js';
- req.open('GET', src, false);
- req.send(null);
- if (req.status != 200)
- throw new Error('Could not find ' + src +
- '. Run calcdeps.py and try again.');
-
- base.addModuleStylesheet = addModuleStylesheet;
- base.addModuleDependency = addModuleDependency;
- try {
- // By construction, the deps file should call addModuleDependency.
- eval(req.responseText);
- } catch (e) {
- throw new Error('When loading deps, got ' + e.stack ? e.stack : e);
- }
- delete base.addModuleDependency;
- delete base.addModuleStylesheet;
- }
-
- var moduleLoadStatus = {};
- function require(dependentModuleName, opt_indentLevel) {
- var indentLevel = opt_indentLevel || 0;
-
- if (window.FLATTENED) {
- if (!window.FLATTENED[dependentModuleName])
- throw new Error('Somehow, module ' + dependentModuleName +
- ' didn\'t get stored in the flattened js file! ' +
- 'You may need to rerun build/flatten.py');
- return;
- }
- ensureDepsLoaded();
-
- mLog('require(' + dependentModuleName + ')', indentLevel);
-
- if (moduleLoadStatus[dependentModuleName] == 'APPENDED')
- return;
- if (moduleLoadStatus[dependentModuleName] == 'RESOLVING')
- throw new Error('Circular dependency betwen modules. Cannot continue!');
- moduleLoadStatus[dependentModuleName] = 'RESOLVING';
-
- // Load the module stylesheet first.
- var stylesheets = moduleStylesheets[dependentModuleName] || [];
- for (var i = 0; i < stylesheets.length; i++)
- requireStylesheet(stylesheets[i]);
-
- // Load the module's dependent scripts after.
- var dependentModules =
- moduleDependencies[dependentModuleName] || [];
- for (var i = 0; i < dependentModules.length; i++)
- require(dependentModules[i], indentLevel + 1);
-
- mLog('load(' + dependentModuleName + ')', indentLevel);
- // Load the module itself.
- var localPath = dependentModuleName.replace(/\./g, '/') + '.js';
- var src = moduleBasePath + '/' + localPath;
- var text = '<script type="text/javascript" src="' + src +
- '"></' + 'script>';
- base.doc.write(text);
- moduleLoadStatus[dependentModuleName] = 'APPENDED';
- }
-
- var stylesheetLoadStatus = {};
- function requireStylesheet(dependentStylesheetName) {
- if (window.FLATTENED)
- return;
-
- if (stylesheetLoadStatus[dependentStylesheetName])
- return;
- stylesheetLoadStatus[dependentStylesheetName] = true;
- var localPath = dependentStylesheetName.replace(/\./g, '/') + '.css';
- var stylesheetPath = moduleBasePath + '/' + localPath;
-
- var linkEl = document.createElement('link');
- linkEl.setAttribute('rel', 'stylesheet');
- linkEl.setAttribute('href', stylesheetPath);
- base.doc.head.appendChild(linkEl);
- }
-
- function exportTo(namespace, fn) {
- var obj = exportPath(namespace);
- try {
- var exports = fn();
- } catch (e) {
- console.log('While running exports for ', name, ':');
- console.log(e.stack || e);
- return;
- }
-
- for (var propertyName in exports) {
- // Maybe we should check the prototype chain here? The current usage
- // pattern is always using an object literal so we only care about own
- // properties.
- var propertyDescriptor = Object.getOwnPropertyDescriptor(exports,
- propertyName);
- if (propertyDescriptor) {
- Object.defineProperty(obj, propertyName, propertyDescriptor);
- mLog(' +' + propertyName);
- }
- }
- };
-
- /**
- * Fires a property change event on the target.
- * @param {EventTarget} target The target to dispatch the event on.
- * @param {string} propertyName The name of the property that changed.
- * @param {*} newValue The new value for the property.
- * @param {*} oldValue The old value for the property.
- */
- function dispatchPropertyChange(target, propertyName, newValue, oldValue) {
- var e = new base.Event(propertyName + 'Change');
- e.propertyName = propertyName;
- e.newValue = newValue;
- e.oldValue = oldValue;
- target.dispatchEvent(e);
- }
-
- /**
- * Converts a camelCase javascript property name to a hyphenated-lower-case
- * attribute name.
- * @param {string} jsName The javascript camelCase property name.
- * @return {string} The equivalent hyphenated-lower-case attribute name.
- */
- function getAttributeName(jsName) {
- return jsName.replace(/([A-Z])/g, '-$1').toLowerCase();
- }
-
- /**
- * The kind of property to define in {@code defineProperty}.
- * @enum {number}
- * @const
- */
- var PropertyKind = {
- /**
- * Plain old JS property where the backing data is stored as a 'private'
- * field on the object.
- */
- JS: 'js',
-
- /**
- * The property backing data is stored as an attribute on an element.
- */
- ATTR: 'attr',
-
- /**
- * The property backing data is stored as an attribute on an element. If the
- * element has the attribute then the value is true.
- */
- BOOL_ATTR: 'boolAttr'
- };
-
- /**
- * Helper function for defineProperty that returns the getter to use for the
- * property.
- * @param {string} name The name of the property.
- * @param {base.PropertyKind} kind The kind of the property.
- * @return {function():*} The getter for the property.
- */
- function getGetter(name, kind) {
- switch (kind) {
- case PropertyKind.JS:
- var privateName = name + '_';
- return function() {
- return this[privateName];
- };
- case PropertyKind.ATTR:
- var attributeName = getAttributeName(name);
- return function() {
- return this.getAttribute(attributeName);
- };
- case PropertyKind.BOOL_ATTR:
- var attributeName = getAttributeName(name);
- return function() {
- return this.hasAttribute(attributeName);
- };
- }
- }
-
- /**
- * Helper function for defineProperty that returns the setter of the right
- * kind.
- * @param {string} name The name of the property we are defining the setter
- * for.
- * @param {base.PropertyKind} kind The kind of property we are getting the
- * setter for.
- * @param {function(*):void} opt_setHook A function to run after the property
- * is set, but before the propertyChange event is fired.
- * @return {function(*):void} The function to use as a setter.
- */
- function getSetter(name, kind, opt_setHook) {
- switch (kind) {
- case PropertyKind.JS:
- var privateName = name + '_';
- return function(value) {
- var oldValue = this[privateName];
- if (value !== oldValue) {
- this[privateName] = value;
- if (opt_setHook)
- opt_setHook.call(this, value, oldValue);
- dispatchPropertyChange(this, name, value, oldValue);
- }
- };
-
- case PropertyKind.ATTR:
- var attributeName = getAttributeName(name);
- return function(value) {
- var oldValue = this[attributeName];
- if (value !== oldValue) {
- if (value == undefined)
- this.removeAttribute(attributeName);
- else
- this.setAttribute(attributeName, value);
- if (opt_setHook)
- opt_setHook.call(this, value, oldValue);
- dispatchPropertyChange(this, name, value, oldValue);
- }
- };
-
- case PropertyKind.BOOL_ATTR:
- var attributeName = getAttributeName(name);
- return function(value) {
- var oldValue = this[attributeName];
- if (value !== oldValue) {
- if (value)
- this.setAttribute(attributeName, name);
- else
- this.removeAttribute(attributeName);
- if (opt_setHook)
- opt_setHook.call(this, value, oldValue);
- dispatchPropertyChange(this, name, value, oldValue);
- }
- };
- }
- }
-
- /**
- * Defines a property on an object. When the setter changes the value a
- * property change event with the type {@code name + 'Change'} is fired.
- * @param {!Object} obj The object to define the property for.
- * @param {string} name The name of the property.
- * @param {base.PropertyKind=} opt_kind What kind of underlying storage to
- * use.
- * @param {function(*):void} opt_setHook A function to run after the
- * property is set, but before the propertyChange event is fired.
- */
- function defineProperty(obj, name, opt_kind, opt_setHook) {
- if (typeof obj == 'function')
- obj = obj.prototype;
-
- var kind = opt_kind || PropertyKind.JS;
-
- if (!obj.__lookupGetter__(name))
- obj.__defineGetter__(name, getGetter(name, kind));
-
- if (!obj.__lookupSetter__(name))
- obj.__defineSetter__(name, getSetter(name, kind, opt_setHook));
- }
-
- /**
- * Counter for use with createUid
- */
- var uidCounter = 1;
-
- /**
- * @return {number} A new unique ID.
- */
- function createUid() {
- return uidCounter++;
- }
-
- /**
- * Returns a unique ID for the item. This mutates the item so it needs to be
- * an object
- * @param {!Object} item The item to get the unique ID for.
- * @return {number} The unique ID for the item.
- */
- function getUid(item) {
- if (item.hasOwnProperty('uid'))
- return item.uid;
- return item.uid = createUid();
- }
-
- /**
- * Dispatches a simple event on an event target.
- * @param {!EventTarget} target The event target to dispatch the event on.
- * @param {string} type The type of the event.
- * @param {boolean=} opt_bubbles Whether the event bubbles or not.
- * @param {boolean=} opt_cancelable Whether the default action of the event
- * can be prevented.
- * @return {boolean} If any of the listeners called {@code preventDefault}
- * during the dispatch this will return false.
- */
- function dispatchSimpleEvent(target, type, opt_bubbles, opt_cancelable) {
- var e = new base.Event(type, opt_bubbles, opt_cancelable);
- return target.dispatchEvent(e);
- }
-
- /**
- * Adds a {@code getInstance} static method that always return the same
- * instance object.
- * @param {!Function} ctor The constructor for the class to add the static
- * method to.
- */
- function addSingletonGetter(ctor) {
- ctor.getInstance = function() {
- return ctor.instance_ || (ctor.instance_ = new ctor());
- };
- }
-
- /**
- * Creates a new event to be used with base.EventTarget or DOM EventTarget
- * objects.
- * @param {string} type The name of the event.
- * @param {boolean=} opt_bubbles Whether the event bubbles.
- * Default is false.
- * @param {boolean=} opt_preventable Whether the default action of the event
- * can be prevented.
- * @constructor
- * @extends {Event}
- */
- function Event(type, opt_bubbles, opt_preventable) {
- var e = base.doc.createEvent('Event');
- e.initEvent(type, !!opt_bubbles, !!opt_preventable);
- e.__proto__ = global.Event.prototype;
- return e;
- };
-
- /**
- * Initialization which must be deferred until run-time.
- */
- function initialize() {
- // If 'document' isn't defined, then we must be being pre-compiled,
- // so set a trap so that we're initialized on first access at run-time.
- if (!global.document) {
- var originalCr = cr;
-
- Object.defineProperty(global, 'cr', {
- get: function() {
- Object.defineProperty(global, 'cr', {value: originalCr});
- originalBase.initialize();
- return originalCr;
- },
- configurable: true
- });
-
- return;
- }
-
- Event.prototype = {__proto__: global.Event.prototype};
-
- base.doc = document;
-
- base.isMac = /Mac/.test(navigator.platform);
- base.isWindows = /Win/.test(navigator.platform);
- base.isChromeOS = /CrOS/.test(navigator.userAgent);
- base.isLinux = /Linux/.test(navigator.userAgent);
- base.isGTK = /GTK/.test(chrome.toolkit);
- base.isViews = /views/.test(chrome.toolkit);
-
- setModuleBasePath('/src');
- }
-
- return {
- set moduleBasePath(path) {
- setModuleBasePath(path);
- },
-
- get moduleBasePath() {
- return moduleBasePath;
- },
-
- require: require,
- requireStylesheet: requireStylesheet,
- exportTo: exportTo,
-
- addSingletonGetter: addSingletonGetter,
- createUid: createUid,
- defineProperty: defineProperty,
- dispatchPropertyChange: dispatchPropertyChange,
- dispatchSimpleEvent: dispatchSimpleEvent,
- Event: Event,
- getUid: getUid,
- initialize: initialize,
- PropertyKind: PropertyKind
- };
- })();
-
-
- /**
- * TODO(kgr): Move this to another file which is to be loaded last.
- * This will be done as part of future work to make this code pre-compilable.
- */
- base.initialize();
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- /**
- * @fileoverview This contains an implementation of the EventTarget interface
- * as defined by DOM Level 2 Events.
- */
- base.exportTo('base', function() {
-
- /**
- * Creates a new EventTarget. This class implements the DOM level 2
- * EventTarget interface and can be used wherever those are used.
- * @constructor
- */
- function EventTarget() {
- }
-
- EventTarget.prototype = {
-
- /**
- * Adds an event listener to the target.
- * @param {string} type The name of the event.
- * @param {!Function|{handleEvent:Function}} handler The handler for the
- * event. This is called when the event is dispatched.
- */
- addEventListener: function(type, handler) {
- if (!this.listeners_)
- this.listeners_ = Object.create(null);
- if (!(type in this.listeners_)) {
- this.listeners_[type] = [handler];
- } else {
- var handlers = this.listeners_[type];
- if (handlers.indexOf(handler) < 0)
- handlers.push(handler);
- }
- },
-
- /**
- * Removes an event listener from the target.
- * @param {string} type The name of the event.
- * @param {!Function|{handleEvent:Function}} handler The handler for the
- * event.
- */
- removeEventListener: function(type, handler) {
- if (!this.listeners_)
- return;
- if (type in this.listeners_) {
- var handlers = this.listeners_[type];
- var index = handlers.indexOf(handler);
- if (index >= 0) {
- // Clean up if this was the last listener.
- if (handlers.length == 1)
- delete this.listeners_[type];
- else
- handlers.splice(index, 1);
- }
- }
- },
-
- /**
- * Dispatches an event and calls all the listeners that are listening to
- * the type of the event.
- * @param {!cr.event.Event} event The event to dispatch.
- * @return {boolean} Whether the default action was prevented. If someone
- * calls preventDefault on the event object then this returns false.
- */
- dispatchEvent: function(event) {
- if (!this.listeners_)
- return true;
-
- // Since we are using DOM Event objects we need to override some of the
- // properties and methods so that we can emulate this correctly.
- var self = this;
- event.__defineGetter__('target', function() {
- return self;
- });
- event.preventDefault = function() {
- this.returnValue = false;
- };
-
- var type = event.type;
- var prevented = 0;
- if (type in this.listeners_) {
- // Clone to prevent removal during dispatch
- var handlers = this.listeners_[type].concat();
- for (var i = 0, handler; handler = handlers[i]; i++) {
- if (handler.handleEvent)
- prevented |= handler.handleEvent.call(handler, event) === false;
- else
- prevented |= handler.call(this, event) === false;
- }
- }
-
- return !prevented && event.returnValue;
- }
- };
-
- // Export
- return {
- EventTarget: EventTarget
- };
- });
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- base.exportTo('tracing', function() {
-
- /**
- * Uses an embedded iframe to measure provided elements without forcing layout
- * on the main document. You must call attach() on the stick before using it,
- * and call detach() on it when you are done using it.
- * @constructor
- * @extends {Object}
- */
- function MeasuringStick() {
- this.iframe_ = undefined;
- }
-
- MeasuringStick.prototype = {
- __proto__: Object.prototype,
-
- /**
- * Measures the provided element without forcing layout on the main
- * document.
- */
- measure: function(element) {
- this.iframe_.contentDocument.body.appendChild(element);
- var style = this.iframe_.contentWindow.getComputedStyle(element);
- var width = parseInt(style.width, 10);
- var height = parseInt(style.height, 10);
- this.iframe_.contentDocument.body.removeChild(element);
- return { width: width, height: height };
- },
-
- attach: function() {
- var iframe = document.createElement('iframe');
- iframe.style.cssText =
- 'width:100%;height:0;border:0;visibility:hidden';
- document.body.appendChild(iframe);
- this.iframe_ = iframe;
- this.iframe_.contentDocument.body.style.cssText =
- 'padding:0;margin:0;overflow:hidden';
-
- var stylesheets = document.querySelectorAll('link[rel=stylesheet]');
- for (var i = 0; i < stylesheets.length; i++) {
- var stylesheet = stylesheets[i];
- var link = this.iframe_.contentDocument.createElement('link');
- link.rel = 'stylesheet';
- link.href = stylesheet.href;
- this.iframe_.contentDocument.head.appendChild(link);
- }
- },
-
- detach: function() {
- document.body.removeChild(this.iframe_);
- this.iframe_ = undefined;
- }
- };
-
- return {
- MeasuringStick: MeasuringStick
- };
- });
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- 'use strict';
-
- /**
- * @fileoverview TimelineModel is a parsed representation of the
- * TraceEvents obtained from base/trace_event in which the begin-end
- * tokens are converted into a hierarchy of processes, threads,
- * subrows, and slices.
- *
- * The building block of the model is a slice. A slice is roughly
- * equivalent to function call executing on a specific thread. As a
- * result, slices may have one or more subslices.
- *
- * A thread contains one or more subrows of slices. Row 0 corresponds to
- * the "root" slices, e.g. the topmost slices. Row 1 contains slices that
- * are nested 1 deep in the stack, and so on. We use these subrows to draw
- * nesting tasks.
- *
- */
- base.exportTo('tracing', function() {
-
- function filterSliceArray(filter, slices) {
- if (filter === undefined)
- return slices;
-
- var matched = [];
- for (var i = 0; i < slices.length; ++i) {
- if (filter.matchSlice(slices[i]))
- matched.push(slices[i]);
- }
- return matched;
- }
-
- function filterCounterArray(filter, counters) {
- if (filter === undefined)
- return counters;
-
- var matched = [];
- for (var i = 0; i < counters.length; ++i) {
- if (filter.matchCounter(counters[i]))
- matched.push(counters[i]);
- }
- return matched;
- }
-
- /**
- * @constructor The generic base class for filtering a TimelineModel based on
- * various rules. The base class returns true for everything.
- */
- function TimelineFilter() {
- }
-
- TimelineFilter.prototype = {
- __proto__: Object.prototype,
-
- matchCounter: function(counter) {
- return true;
- },
-
- matchCpu: function(cpu) {
- return true;
- },
-
- matchProcess: function(process) {
- return true;
- },
-
- matchSlice: function(slice) {
- return true;
- },
-
- matchThread: function(thread) {
- return true;
- }
- };
-
- /**
- * @constructor A filter that matches objects by their name.
- * Timeline.findAllObjectsMatchingFilter
- */
- function TimelineTitleFilter(text) {
- TimelineFilter.call(this);
- this.text_ = text;
- }
- TimelineTitleFilter.prototype = {
- __proto__: TimelineFilter.prototype,
-
- matchCounter: function(counter) {
- if (this.text_.length == 0)
- return false;
- if (counter.name === undefined)
- return false;
- return counter.name.indexOf(this.text_) != -1;
- },
-
- matchSlice: function(slice) {
- if (this.text_.length == 0)
- return false;
- if (slice.title === undefined)
- return false;
- return slice.title.indexOf(this.text_) != -1;
- }
- };
-
- /**
- * @constructor A filter that filters objects by their category.
- * Objects match if they are NOT in the list of categories
- * @param {Array<string>} opt_categories Categories to blacklist.
- */
- function TimelineCategoryFilter(opt_categories) {
- TimelineFilter.call(this);
- this.categories_ = {};
- var cats = opt_categories || [];
- for (var i = 0; i < cats.length; i++)
- this.addCategory(cats[i]);
- }
- TimelineCategoryFilter.prototype = {
- __proto__: TimelineFilter.prototype,
-
- addCategory: function(cat) {
- this.categories_[cat] = true;
- },
-
- matchCounter: function(counter) {
- if (!counter.category)
- return true;
- return !this.categories_[counter.category];
- },
-
- matchSlice: function(slice) {
- if (!slice.category)
- return true;
- return !this.categories_[slice.category];
- }
- };
-
- return {
- filterCounterArray: filterCounterArray,
- filterSliceArray: filterSliceArray,
- TimelineFilter: TimelineFilter,
- TimelineTitleFilter: TimelineTitleFilter,
- TimelineCategoryFilter: TimelineCategoryFilter
- };
- });
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- 'use strict';
-
- /**
- * @fileoverview Code for the timeline viewport.
- */
- base.require('event_target');
- base.exportTo('tracing', function() {
-
- function TimelineSelectionSliceHit(track, slice) {
- this.track = track;
- this.slice = slice;
- }
- TimelineSelectionSliceHit.prototype = {
- get selected() {
- return this.slice.selected;
- },
- set selected(v) {
- this.slice.selected = v;
- }
- };
-
- function TimelineSelectionCounterSampleHit(track, counter, sampleIndex) {
- this.track = track;
- this.counter = counter;
- this.sampleIndex = sampleIndex;
- }
- TimelineSelectionCounterSampleHit.prototype = {
- get selected() {
- return this.track.selectedSamples[this.sampleIndex] == true;
- },
- set selected(v) {
- if (v)
- this.track.selectedSamples[this.sampleIndex] = true;
- else
- this.track.selectedSamples[this.sampleIndex] = false;
- this.track.invalidate();
- }
- };
-
-
- /**
- * Represents a selection within a Timeline and its associated set of tracks.
- * @constructor
- */
- function TimelineSelection() {
- this.range_dirty_ = true;
- this.range_ = {};
- this.length_ = 0;
- }
- TimelineSelection.prototype = {
- __proto__: Object.prototype,
-
- get range() {
- if (this.range_dirty_) {
- var wmin = Infinity;
- var wmax = -wmin;
- for (var i = 0; i < this.length_; i++) {
- var hit = this[i];
- if (hit.slice) {
- wmin = Math.min(wmin, hit.slice.start);
- wmax = Math.max(wmax, hit.slice.end);
- }
- }
- this.range_ = {
- min: wmin,
- max: wmax
- };
- this.range_dirty_ = false;
- }
- return this.range_;
- },
-
- get duration() {
- return this.range.max - this.range.min;
- },
-
- get length() {
- return this.length_;
- },
-
- clear: function() {
- for (var i = 0; i < this.length_; ++i)
- delete this[i];
- this.length_ = 0;
- this.range_dirty_ = true;
- },
-
- pushHit: function(hit) {
- this.push_(hit);
- },
-
- push_: function(hit) {
- this[this.length_++] = hit;
- this.range_dirty_ = true;
- return hit;
- },
-
- addSlice: function(track, slice) {
- return this.push_(new TimelineSelectionSliceHit(track, slice));
- },
-
- addCounterSample: function(track, counter, sampleIndex) {
- return this.push_(
- new TimelineSelectionCounterSampleHit(
- track, counter, sampleIndex));
- },
-
- subSelection: function(index, count) {
- count = count || 1;
-
- var selection = new TimelineSelection();
- selection.range_dirty_ = true;
- if (index < 0 || index + count > this.length_)
- throw new Error('Index out of bounds');
-
- for (var i = index; i < index + count; i++)
- selection.push_(this[i]);
-
- return selection;
- },
-
- getCounterSampleHitsAsSelection: function() {
- var selection = new TimelineSelection();
- for (var i = 0; i < this.length_; i++)
- if (this[i] instanceof TimelineSelectionCounterSampleHit)
- selection.push_(this[i]);
- return selection;
- },
-
- getSliceHitsAsSelection: function() {
- var selection = new TimelineSelection();
- for (var i = 0; i < this.length_; i++)
- if (this[i] instanceof TimelineSelectionSliceHit)
- selection.push_(this[i]);
- return selection;
- },
-
- getNumSliceHits: function() {
- var numHits = 0;
- for (var i = 0; i < this.length_; i++)
- if (this[i] instanceof TimelineSelectionSliceHit)
- numHits++;
- return numHits;
- },
-
- getNumCounterHits: function() {
- var numHits = 0;
- for (var i = 0; i < this.length_; i++)
- if (this[i] instanceof TimelineSelectionCounterSampleHit)
- numHits++;
- return numHits;
- },
-
- map: function(fn) {
- for (var i = 0; i < this.length_; i++)
- fn(this[i]);
- },
-
- /**
- * Helper for selection previous or next.
- * @param {boolean} forwardp If true, select one forward (next).
- * Else, select previous.
- * @return {boolean} true if current selection changed.
- */
- getShiftedSelection: function(offset) {
- var newSelection = new TimelineSelection();
- for (var i = 0; i < this.length_; i++) {
- var hit = this[i];
- hit.track.addItemNearToProvidedHitToSelection(
- hit, offset, newSelection);
- }
-
- if (newSelection.length == 0)
- return undefined;
- return newSelection;
- }
- };
-
- return {
- TimelineSelectionSliceHit: TimelineSelectionSliceHit,
- TimelineSelectionCounterSampleHit: TimelineSelectionCounterSampleHit,
- TimelineSelection: TimelineSelection
- };
- });
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- 'use strict';
-
- /**
- * @fileoverview Code for the timeline viewport.
- */
- base.require('event_target');
-
- base.exportTo('tracing', function() {
-
- /**
- * The TimelineViewport manages the transform used for navigating
- * within the timeline. It is a simple transform:
- * x' = (x+pan) * scale
- *
- * The timeline code tries to avoid directly accessing this transform,
- * instead using this class to do conversion between world and viewspace,
- * as well as the math for centering the viewport in various interesting
- * ways.
- *
- * @constructor
- * @extends {base.EventTarget}
- */
- function TimelineViewport(parentEl) {
- this.parentEl_ = parentEl;
- this.scaleX_ = 1;
- this.panX_ = 0;
- this.gridTimebase_ = 0;
- this.gridStep_ = 1000 / 60;
- this.gridEnabled_ = false;
- this.hasCalledSetupFunction_ = false;
-
- this.onResizeBoundToThis_ = this.onResize_.bind(this);
-
- // The following code uses an interval to detect when the parent element
- // is attached to the document. That is a trigger to run the setup function
- // and install a resize listener.
- this.checkForAttachInterval_ = setInterval(
- this.checkForAttach_.bind(this), 250);
-
- this.markers = [];
- }
-
- TimelineViewport.prototype = {
- __proto__: base.EventTarget.prototype,
-
- drawUnderContent: function(ctx, viewLWorld, viewRWorld, canvasH) {
- },
-
- drawOverContent: function(ctx, viewLWorld, viewRWorld, canvasH) {
- if (this.gridEnabled) {
- var x = this.gridTimebase;
-
- ctx.beginPath();
- while (x < viewRWorld) {
- if (x >= viewLWorld) {
- // Do conversion to viewspace here rather than on
- // x to avoid precision issues.
- var vx = this.xWorldToView(x);
- ctx.moveTo(vx, 0);
- ctx.lineTo(vx, canvasH);
- }
- x += this.gridStep;
- }
- ctx.strokeStyle = 'rgba(255,0,0,0.25)';
- ctx.stroke();
- }
-
- for (var i = 0; i < this.markers.length; ++i) {
- this.markers[i].drawLine(ctx, viewLWorld, viewRWorld, canvasH, this);
- }
- },
-
- /**
- * Allows initialization of the viewport when the viewport's parent element
- * has been attached to the document and given a size.
- * @param {Function} fn Function to call when the viewport can be safely
- * initialized.
- */
- setWhenPossible: function(fn) {
- this.pendingSetFunction_ = fn;
- },
-
- /**
- * @return {boolean} Whether the current timeline is attached to the
- * document.
- */
- get isAttachedToDocument_() {
- var cur = this.parentEl_;
- while (cur.parentNode)
- cur = cur.parentNode;
- return cur == this.parentEl_.ownerDocument;
- },
-
- onResize_: function() {
- this.dispatchChangeEvent();
- },
-
- /**
- * Checks whether the parentNode is attached to the document.
- * When it is, it installs the iframe-based resize detection hook
- * and then runs the pendingSetFunction_, if present.
- */
- checkForAttach_: function() {
- if (!this.isAttachedToDocument_ || this.clientWidth == 0)
- return;
-
- if (!this.iframe_) {
- this.iframe_ = document.createElement('iframe');
- this.iframe_.style.cssText =
- 'position:absolute;width:100%;height:0;border:0;visibility:hidden;';
- this.parentEl_.appendChild(this.iframe_);
-
- this.iframe_.contentWindow.addEventListener('resize',
- this.onResizeBoundToThis_);
- }
-
- var curSize = this.clientWidth + 'x' + this.clientHeight;
- if (this.pendingSetFunction_) {
- this.lastSize_ = curSize;
- try {
- this.pendingSetFunction_();
- } catch (ex) {
- console.log('While running setWhenPossible:', ex);
- }
- this.pendingSetFunction_ = undefined;
- }
-
- window.clearInterval(this.checkForAttachInterval_);
- this.checkForAttachInterval_ = undefined;
- },
-
- /**
- * Fires the change event on this viewport. Used to notify listeners
- * to redraw when the underlying model has been mutated.
- */
- dispatchChangeEvent: function() {
- base.dispatchSimpleEvent(this, 'change');
- },
-
- dispatchMarkersChangeEvent_: function() {
- base.dispatchSimpleEvent(this, 'markersChange');
- },
-
- detach: function() {
- if (this.checkForAttachInterval_) {
- window.clearInterval(this.checkForAttachInterval_);
- this.checkForAttachInterval_ = undefined;
- }
- if (this.iframe_) {
- this.iframe_.removeEventListener('resize', this.onResizeBoundToThis_);
- this.parentEl_.removeChild(this.iframe_);
- }
- },
-
- get scaleX() {
- return this.scaleX_;
- },
- set scaleX(s) {
- var changed = this.scaleX_ != s;
- if (changed) {
- this.scaleX_ = s;
- this.dispatchChangeEvent();
- }
- },
-
- get panX() {
- return this.panX_;
- },
- set panX(p) {
- var changed = this.panX_ != p;
- if (changed) {
- this.panX_ = p;
- this.dispatchChangeEvent();
- }
- },
-
- setPanAndScale: function(p, s) {
- var changed = this.scaleX_ != s || this.panX_ != p;
- if (changed) {
- this.scaleX_ = s;
- this.panX_ = p;
- this.dispatchChangeEvent();
- }
- },
-
- xWorldToView: function(x) {
- return (x + this.panX_) * this.scaleX_;
- },
-
- xWorldVectorToView: function(x) {
- return x * this.scaleX_;
- },
-
- xViewToWorld: function(x) {
- return (x / this.scaleX_) - this.panX_;
- },
-
- xViewVectorToWorld: function(x) {
- return x / this.scaleX_;
- },
-
- xPanWorldPosToViewPos: function(worldX, viewX, viewWidth) {
- if (typeof viewX == 'string') {
- if (viewX == 'left') {
- viewX = 0;
- } else if (viewX == 'center') {
- viewX = viewWidth / 2;
- } else if (viewX == 'right') {
- viewX = viewWidth - 1;
- } else {
- throw new Error('unrecognized string for viewPos. left|center|right');
- }
- }
- this.panX = (viewX / this.scaleX_) - worldX;
- },
-
- xPanWorldRangeIntoView: function(worldMin, worldMax, viewWidth) {
- if (this.xWorldToView(worldMin) < 0)
- this.xPanWorldPosToViewPos(worldMin, 'left', viewWidth);
- else if (this.xWorldToView(worldMax) > viewWidth)
- this.xPanWorldPosToViewPos(worldMax, 'right', viewWidth);
- },
-
- xSetWorldRange: function(worldMin, worldMax, viewWidth) {
- var worldRange = worldMax - worldMin;
- var scaleX = viewWidth / worldRange;
- var panX = -worldMin;
- this.setPanAndScale(panX, scaleX);
- },
-
- get gridEnabled() {
- return this.gridEnabled_;
- },
-
- set gridEnabled(enabled) {
- if (this.gridEnabled_ == enabled)
- return;
- this.gridEnabled_ = enabled && true;
- this.dispatchChangeEvent();
- },
-
- get gridTimebase() {
- return this.gridTimebase_;
- },
-
- set gridTimebase(timebase) {
- if (this.gridTimebase_ == timebase)
- return;
- this.gridTimebase_ = timebase;
- base.dispatchSimpleEvent(this, 'change');
- },
-
- get gridStep() {
- return this.gridStep_;
- },
-
- applyTransformToCanvas: function(ctx) {
- ctx.transform(this.scaleX_, 0, 0, 1, this.panX_ * this.scaleX_, 0);
- },
-
- addMarker: function(positionWorld) {
- var marker = new TimelineViewportMarker(this, positionWorld);
- this.markers.push(marker);
- this.dispatchChangeEvent();
- this.dispatchMarkersChangeEvent_();
- return marker;
- },
-
- removeMarker: function(marker) {
- for (var i = 0; i < this.markers.length; ++i) {
- if (this.markers[i] === marker) {
- this.markers.splice(i, 1);
- this.dispatchChangeEvent();
- this.dispatchMarkersChangeEvent_();
- return true;
- }
- }
- },
-
- findMarkerNear: function(positionWorld, nearnessInViewPixels) {
- // Converts pixels into distance in world.
- var nearnessThresholdWorld = this.xViewVectorToWorld(
- nearnessInViewPixels);
- for (var i = 0; i < this.markers.length; ++i) {
- if (Math.abs(this.markers[i].positionWorld - positionWorld) <=
- nearnessThresholdWorld) {
- var marker = this.markers[i];
- return marker;
- }
- }
- return undefined;
- }
- };
-
- /**
- * Represents a marked position in the world, at a viewport level.
- * @constructor
- */
- function TimelineViewportMarker(vp, positionWorld) {
- this.viewport_ = vp;
- this.positionWorld_ = positionWorld;
- this.selected_ = false;
- }
-
- TimelineViewportMarker.prototype = {
- get positionWorld() {
- return this.positionWorld_;
- },
-
- set positionWorld(positionWorld) {
- this.positionWorld_ = positionWorld;
- this.viewport_.dispatchChangeEvent();
- },
-
- set selected(selected) {
- this.selected_ = selected;
- this.viewport_.dispatchChangeEvent();
- },
-
- get selected() {
- return this.selected_;
- },
-
- get color() {
- if (this.selected)
- return 'rgb(255,0,0)';
- return 'rgb(0,0,0)';
- },
-
- drawTriangle_: function(ctx, viewLWorld, viewRWorld,
- canvasH, rulerHeight, vp) {
- ctx.beginPath();
- var ts = this.positionWorld_;
- if (ts >= viewLWorld && ts < viewRWorld) {
- var viewX = vp.xWorldToView(ts);
- ctx.moveTo(viewX, rulerHeight);
- ctx.lineTo(viewX - 3, rulerHeight / 2);
- ctx.lineTo(viewX + 3, rulerHeight / 2);
- ctx.lineTo(viewX, rulerHeight);
- ctx.closePath();
- ctx.fillStyle = this.color;
- ctx.fill();
- if (rulerHeight != canvasH) {
- ctx.beginPath();
- ctx.moveTo(viewX, rulerHeight);
- ctx.lineTo(viewX, canvasH);
- ctx.closePath();
- ctx.strokeStyle = this.color;
- ctx.stroke();
- }
- }
- },
-
- drawLine: function(ctx, viewLWorld, viewRWorld, canvasH, vp) {
- ctx.beginPath();
- var ts = this.positionWorld_;
- if (ts >= viewLWorld && ts < viewRWorld) {
- var viewX = vp.xWorldToView(ts);
- ctx.moveTo(viewX, 0);
- ctx.lineTo(viewX, canvasH);
- }
- ctx.strokeStyle = this.color;
- ctx.stroke();
- }
- };
-
- return {
- TimelineViewport: TimelineViewport,
- TimelineViewportMarker: TimelineViewportMarker
- };
- });
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- base.exportTo('base.ui', function() {
-
- /**
- * Decorates elements as an instance of a class.
- * @param {string|!Element} source The way to find the element(s) to decorate.
- * If this is a string then {@code querySeletorAll} is used to find the
- * elements to decorate.
- * @param {!Function} constr The constructor to decorate with. The constr
- * needs to have a {@code decorate} function.
- */
- function decorate(source, constr) {
- var elements;
- if (typeof source == 'string')
- elements = base.doc.querySelectorAll(source);
- else
- elements = [source];
-
- for (var i = 0, el; el = elements[i]; i++) {
- if (!(el instanceof constr))
- constr.decorate(el);
- }
- }
-
- /**
- * Helper function for creating new element for define.
- */
- function createElementHelper(tagName, opt_bag) {
- // Allow passing in ownerDocument to create in a different document.
- var doc;
- if (opt_bag && opt_bag.ownerDocument)
- doc = opt_bag.ownerDocument;
- else
- doc = base.doc;
- return doc.createElement(tagName);
- }
-
- /**
- * Creates the constructor for a UI element class.
- *
- * Usage:
- * <pre>
- * var List = base.ui.define('list');
- * List.prototype = {
- * __proto__: HTMLUListElement.prototype,
- * decorate: function() {
- * ...
- * },
- * ...
- * };
- * </pre>
- *
- * @param {string|Function} tagNameOrFunction The tagName or
- * function to use for newly created elements. If this is a function it
- * needs to return a new element when called.
- * @return {function(Object=):Element} The constructor function which takes
- * an optional property bag. The function also has a static
- * {@code decorate} method added to it.
- */
- function define(tagNameOrFunction) {
- var createFunction, tagName;
- if (typeof tagNameOrFunction == 'function') {
- createFunction = tagNameOrFunction;
- tagName = '';
- } else {
- createFunction = createElementHelper;
- tagName = tagNameOrFunction;
- }
-
- /**
- * Creates a new UI element constructor.
- * @param {Object=} opt_propertyBag Optional bag of properties to set on the
- * object after created. The property {@code ownerDocument} is special
- * cased and it allows you to create the element in a different
- * document than the default.
- * @constructor
- */
- function f(opt_propertyBag) {
- var el = createFunction(tagName, opt_propertyBag);
- f.decorate(el);
- for (var propertyName in opt_propertyBag) {
- el[propertyName] = opt_propertyBag[propertyName];
- }
- return el;
- }
-
- /**
- * Decorates an element as a UI element class.
- * @param {!Element} el The element to decorate.
- */
- f.decorate = function(el) {
- el.__proto__ = f.prototype;
- el.decorate();
- };
-
- return f;
- }
-
- /**
- * Input elements do not grow and shrink with their content. This is a simple
- * (and not very efficient) way of handling shrinking to content with support
- * for min width and limited by the width of the parent element.
- * @param {HTMLElement} el The element to limit the width for.
- * @param {number} parentEl The parent element that should limit the size.
- * @param {number} min The minimum width.
- */
- function limitInputWidth(el, parentEl, min) {
- // Needs a size larger than borders
- el.style.width = '10px';
- var doc = el.ownerDocument;
- var win = doc.defaultView;
- var computedStyle = win.getComputedStyle(el);
- var parentComputedStyle = win.getComputedStyle(parentEl);
- var rtl = computedStyle.direction == 'rtl';
-
- // To get the max width we get the width of the treeItem minus the position
- // of the input.
- var inputRect = el.getBoundingClientRect(); // box-sizing
- var parentRect = parentEl.getBoundingClientRect();
- var startPos = rtl ? parentRect.right - inputRect.right :
- inputRect.left - parentRect.left;
-
- // Add up border and padding of the input.
- var inner = parseInt(computedStyle.borderLeftWidth, 10) +
- parseInt(computedStyle.paddingLeft, 10) +
- parseInt(computedStyle.paddingRight, 10) +
- parseInt(computedStyle.borderRightWidth, 10);
-
- // We also need to subtract the padding of parent to prevent it to overflow.
- var parentPadding = rtl ? parseInt(parentComputedStyle.paddingLeft, 10) :
- parseInt(parentComputedStyle.paddingRight, 10);
-
- var max = parentEl.clientWidth - startPos - inner - parentPadding;
-
- function limit() {
- if (el.scrollWidth > max) {
- el.style.width = max + 'px';
- } else {
- el.style.width = 0;
- var sw = el.scrollWidth;
- if (sw < min) {
- el.style.width = min + 'px';
- } else {
- el.style.width = sw + 'px';
- }
- }
- }
-
- el.addEventListener('input', limit);
- limit();
- }
-
- /**
- * Takes a number and spits out a value CSS will be happy with. To avoid
- * subpixel layout issues, the value is rounded to the nearest integral value.
- * @param {number} pixels The number of pixels.
- * @return {string} e.g. '16px'.
- */
- function toCssPx(pixels) {
- if (!window.isFinite(pixels))
- console.error('Pixel value is not a number: ' + pixels);
- return Math.round(pixels) + 'px';
- }
-
- return {
- decorate: decorate,
- define: define,
- limitInputWidth: limitInputWidth,
- toCssPx: toCssPx
- };
- });
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
-
- /**
- * @fileoverview Renders an array of slices into the provided div,
- * using a child canvas element. Uses a FastRectRenderer to draw only
- * the visible slices.
- */
- base.requireStylesheet('tracks.timeline_track');
- base.require('ui');
- base.exportTo('tracks', function() {
-
- /**
- * The base class for all tracks.
- * @constructor
- */
- var TimelineTrack = base.ui.define('div');
- TimelineTrack.prototype = {
- __proto__: HTMLDivElement.prototype,
-
- decorate: function() {
- },
-
- get visible() {
- return this.style.display !== 'none';
- },
-
- set visible(v) {
- this.style.display = (v ? '' : 'none');
- },
-
- get numVisibleTracks() {
- return (this.visible ? 1 : 0);
- },
-
- addControlButtonElements_: function(canCollapse) {
- var closeEl = document.createElement('div');
- closeEl.classList.add('timeline-track-button');
- closeEl.classList.add('timeline-track-close-button');
- closeEl.textContent = String.fromCharCode(215); // ×
- var that = this;
- closeEl.addEventListener('click', function() {
- that.style.display = 'None';
- });
- this.appendChild(closeEl);
-
- var collapseEl = document.createElement('div');
- collapseEl.classList.add('timeline-track-button');
- collapseEl.classList.add('timeline-track-collapse-button');
- var minus = '\u2212'; // minus sign;
- var plus = '\u002b'; // plus sign;
- collapseEl.textContent = minus;
- var collapsed = false;
- collapseEl.addEventListener('click', function() {
- collapsed = !collapsed;
- this.collapsedDidChange(collapsed);
- collapseEl.textContent = collapsed ? plus : minus;
- });
- this.appendChild(collapseEl);
- if (!canCollapse)
- collapseEl.style.display = 'None';
- }
- };
-
- return {
- TimelineTrack: TimelineTrack
- };
- });
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- 'use strict';
-
- base.require('tracks.timeline_track');
- base.require('timeline_filter');
- base.require('ui');
- base.exportTo('tracks', function() {
-
- /**
- * A generic track that contains other tracks as its children.
- * @constructor
- */
- var TimelineContainerTrack = base.ui.define(tracks.TimelineTrack);
- TimelineContainerTrack.prototype = {
- __proto__: tracks.TimelineTrack.prototype,
-
- decorate: function() {
- this.categoryFilter_ = new tracing.TimelineFilter();
- this.headingWidth_ = undefined;
- this.tracks_ = [];
- },
-
- detach: function() {
- this.detachAllChildren();
- },
-
- detachAllChildren: function() {
- for (var i = 0; i < this.tracks_.length; i++)
- this.tracks_[i].detach();
- this.tracks_ = [];
- this.textContent = '';
- },
-
- get viewport() {
- return this.viewport_;
- },
-
- set viewport(v) {
- this.viewport_ = v;
- for (var i = 0; i < this.tracks_.length; i++)
- this.tracks_[i].viewport = v;
- },
-
- get firstCanvas() {
- for (var i = 0; i < this.tracks_.length; i++)
- if (this.tracks_[i].visible)
- return this.tracks_[i].firstCanvas;
- return undefined;
- },
-
- // The number of tracks actually displayed.
- get numVisibleTracks() {
- if (!this.visible)
- return 0;
- return this.numVisibleChildTracks;
- },
-
- // The number of tracks that would be displayed if this track were visible.
- get numVisibleChildTracks() {
- var sum = 0;
- for (var i = 0; i < this.tracks_.length; ++i) {
- sum += this.tracks_[i].numVisibleTracks;
- }
- return sum;
- },
-
- get headingWidth() {
- return this.headingWidth_;
- },
-
- set headingWidth(w) {
- this.headingWidth_ = w;
- for (var i = 0; i < this.tracks_.length; ++i) {
- this.tracks_[i].headingWidth = w;
- }
- },
-
- get categoryFilter() {
- return this.categoryFilter_;
- },
-
- set categoryFilter(v) {
- this.categoryFilter_ = v;
- for (var i = 0; i < this.tracks_.length; ++i) {
- this.tracks_[i].categoryFilter = v;
- }
- this.applyCategoryFilter_();
- this.updateFirstVisibleChildCSS();
- },
-
- applyCategoryFilter_: function() {
- },
-
- addTrack_: function(track) {
- track.headingWidth = this.headingWidth_;
- track.viewport = this.viewport_;
- track.categoryFilter = this.categoryFilter;
-
- this.tracks_.push(track);
- this.appendChild(track);
- return track;
- },
-
- updateFirstVisibleChildCSS: function() {
- var isFirst = true;
- for (var i = 0; i < this.tracks_.length; ++i) {
- var track = this.tracks_[i];
- if (isFirst && track.visible) {
- track.classList.add('first-visible-child');
- isFirst = false;
- } else {
- track.classList.remove('first-visible-child');
- }
- }
- },
-
- /**
- * Adds items intersecting a point to a selection.
- * @param {number} vX X location to search at, in viewspace.
- * @param {number} vY Y location to search at, in viewspace.
- * @param {TimelineSelection} selection Selection to which to add hits.
- * @return {boolean} true if a slice was found, otherwise false.
- */
- addIntersectingItemsToSelection: function(vX, vY, selection) {
- for (var i = 0; i < this.tracks_.length; i++) {
- var trackClientRect = this.tracks_[i].getBoundingClientRect();
- if (vY >= trackClientRect.top && vY < trackClientRect.bottom)
- this.tracks_[i].addIntersectingItemsToSelection(vX, vY, selection);
- }
- return false;
- },
-
- /**
- * Adds items intersecting the given range to a selection.
- * @param {number} loVX Lower X bound of the interval to search, in
- * viewspace.
- * @param {number} hiVX Upper X bound of the interval to search, in
- * viewspace.
- * @param {number} loY Lower Y bound of the interval to search, in
- * viewspace space.
- * @param {number} hiY Upper Y bound of the interval to search, in
- * viewspace space.
- * @param {TimelineSelection} selection Selection to which to add hits.
- */
- addIntersectingItemsInRangeToSelection: function(
- loVX, hiVX, loY, hiY, selection) {
- for (var i = 0; i < this.tracks_.length; i++) {
- var trackClientRect = this.tracks_[i].getBoundingClientRect();
- var a = Math.max(loY, trackClientRect.top);
- var b = Math.min(hiY, trackClientRect.bottom);
- if (a <= b)
- this.tracks_[i].addIntersectingItemsInRangeToSelection(
- loVX, hiVX, loY, hiY, selection);
- }
- },
-
- addAllObjectsMatchingFilterToSelection: function(filter, selection) {
- for (var i = 0; i < this.tracks_.length; i++)
- this.tracks_[i].addAllObjectsMatchingFilterToSelection(
- filter, selection);
- }
- };
-
- return {
- TimelineContainerTrack: TimelineContainerTrack
- };
- });
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
-
- /**
- * @fileoverview Provides a mechanism for drawing massive numbers of
- * colored rectangles into a canvas in an efficient manner, provided
- * they are drawn left to right with fixed y and height throughout.
- *
- * The basic idea used here is to fuse subpixel rectangles together so that
- * we never issue a canvas fillRect for them. It turns out Javascript can
- * do this quite efficiently, compared to asking Canvas2D to do the same.
- *
- * A few extra things are done by this class in the name of speed:
- * - Viewport culling: off-viewport rectangles are discarded.
- *
- * - The actual discarding operation is done in world space,
- * e.g. pre-transform.
- *
- * - Rather than expending compute cycles trying to figure out an average
- * color for fused rectangles from css strings, you instead draw using
- * palletized colors. The fused rect is the max pallete index encountered.
- *
- * Make sure to flush the trackRenderer before finishing drawing in order
- * to commit any queued drawing operations.
- */
- base.exportTo('tracing', function() {
-
- /**
- * Creates a fast rect renderer with a specific set of culling rules
- * and color pallette.
- * @param {GraphicsContext2D} ctx Canvas2D drawing context.
- * @param {number} minRectSize Only rectangles with width < minRectSize are
- * considered for merging.
- * @param {number} maxMergeDist Controls how many successive small rectangles
- * can be merged together before issuing a rectangle.
- * @param {Array} pallette The color pallete for drawing. Pallette slots
- * should map to valid Canvas fillStyle strings.
- *
- * @constructor
- */
- function FastRectRenderer(ctx, minRectSize, maxMergeDist, pallette) {
- this.ctx_ = ctx;
- this.minRectSize_ = minRectSize;
- this.maxMergeDist_ = maxMergeDist;
- this.pallette_ = pallette;
- }
-
- FastRectRenderer.prototype = {
- y_: 0,
- h_: 0,
- merging_: false,
- mergeStartX_: 0,
- mergeCurRight_: 0,
-
- /**
- * Changes the y position and height for subsequent fillRect
- * calls. x and width are specifieid on the fillRect calls.
- */
- setYandH: function(y, h) {
- this.flush();
- this.y_ = y;
- this.h_ = h;
- },
-
- /**
- * Fills rectangle at the specified location, if visible. If the
- * rectangle is subpixel, it will be merged with adjacent rectangles.
- * The drawing operation may not take effect until flush is called.
- * @param {number} colorId The color of this rectangle, as an index
- * in the renderer's color pallete.
- */
- fillRect: function(x, w, colorId) {
- var r = x + w;
- if (w < this.minRectSize_) {
- if (r - this.mergeStartX_ > this.maxMergeDist_)
- this.flush();
- if (!this.merging_) {
- this.merging_ = true;
- this.mergeStartX_ = x;
- this.mergeCurRight_ = r;
- this.mergedColorId = colorId;
- } else {
- this.mergeCurRight_ = r;
- this.mergedColorId = Math.max(this.mergedColorId, colorId);
- }
- } else {
- if (this.merging_)
- this.flush();
- this.ctx_.fillStyle = this.pallette_[colorId];
- this.ctx_.fillRect(x, this.y_, w, this.h_);
- }
- },
-
- /**
- * Commits any pending fillRect operations to the underlying graphics
- * context.
- */
- flush: function() {
- if (this.merging_) {
- this.ctx_.fillStyle = this.pallette_[this.mergedColorId];
- this.ctx_.fillRect(this.mergeStartX_, this.y_,
- this.mergeCurRight_ - this.mergeStartX_, this.h_);
- this.merging_ = false;
- }
- }
- };
-
- return {
- FastRectRenderer: FastRectRenderer
- };
-
- });
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- 'use strict';
-
- /**
- * @fileoverview Provides color scheme related functions.
- */
- base.exportTo('tracing', function() {
-
- // The color palette is split in half, with the upper
- // half of the palette being the "highlighted" verison
- // of the base color. So, color 7's highlighted form is
- // 7 + (palette.length / 2).
- //
- // These bright versions of colors are automatically generated
- // from the base colors.
- //
- // Within the color palette, there are "regular" colors,
- // which can be used for random color selection, and
- // reserved colors, which are used when specific colors
- // need to be used, e.g. where red is desired.
- var paletteBase = [
- {r: 138, g: 113, b: 152},
- {r: 175, g: 112, b: 133},
- {r: 127, g: 135, b: 225},
- {r: 93, g: 81, b: 137},
- {r: 116, g: 143, b: 119},
- {r: 178, g: 214, b: 122},
- {r: 87, g: 109, b: 147},
- {r: 119, g: 155, b: 95},
- {r: 114, g: 180, b: 160},
- {r: 132, g: 85, b: 103},
- {r: 157, g: 210, b: 150},
- {r: 148, g: 94, b: 86},
- {r: 164, g: 108, b: 138},
- {r: 139, g: 191, b: 150},
- {r: 110, g: 99, b: 145},
- {r: 80, g: 129, b: 109},
- {r: 125, g: 140, b: 149},
- {r: 93, g: 124, b: 132},
- {r: 140, g: 85, b: 140},
- {r: 104, g: 163, b: 162},
- {r: 132, g: 141, b: 178},
- {r: 131, g: 105, b: 147},
- {r: 135, g: 183, b: 98},
- {r: 152, g: 134, b: 177},
- {r: 141, g: 188, b: 141},
- {r: 133, g: 160, b: 210},
- {r: 126, g: 186, b: 148},
- {r: 112, g: 198, b: 205},
- {r: 180, g: 122, b: 195},
- {r: 203, g: 144, b: 152},
- // Reserved Entires
- {r: 182, g: 125, b: 143},
- {r: 126, g: 200, b: 148},
- {r: 133, g: 160, b: 210},
- {r: 240, g: 240, b: 240}];
-
- // Make sure this number tracks the number of reserved entries in the
- // palette.
- var numReservedColorIds = 4;
-
- function brighten(c) {
- var k;
- if (c.r >= 240 && c.g >= 240 && c.b >= 240)
- k = -0.20;
- else
- k = 0.45;
-
- return {r: Math.min(255, c.r + Math.floor(c.r * k)),
- g: Math.min(255, c.g + Math.floor(c.g * k)),
- b: Math.min(255, c.b + Math.floor(c.b * k))};
- }
- function colorToString(c) {
- return 'rgb(' + c.r + ',' + c.g + ',' + c.b + ')';
- }
-
- /**
- * The number of color IDs that getStringColorId can choose from.
- */
- var numRegularColorIds = paletteBase.length - numReservedColorIds;
- var highlightIdBoost = paletteBase.length;
-
- var palette = paletteBase.concat(paletteBase.map(brighten)).
- map(colorToString);
- /**
- * Computes a simplistic hashcode of the provide name. Used to chose colors
- * for slices.
- * @param {string} name The string to hash.
- */
- function getStringHash(name) {
- var hash = 0;
- for (var i = 0; i < name.length; ++i)
- hash = (hash + 37 * hash + 11 * name.charCodeAt(i)) % 0xFFFFFFFF;
- return hash;
- }
-
- /**
- * Gets the color palette.
- */
- function getColorPalette() {
- return palette;
- }
-
- /**
- * @return {Number} The value to add to a color ID to get its highlighted
- * colro ID. E.g. 7 + getPaletteHighlightIdBoost() yields a brightened from
- * of 7's base color.
- */
- function getColorPaletteHighlightIdBoost() {
- return highlightIdBoost;
- }
-
- /**
- * @param {String} name The color name.
- * @return {Number} The color ID for the given color name.
- */
- function getColorIdByName(name) {
- if (name == 'iowait')
- return numRegularColorIds;
- if (name == 'running')
- return numRegularColorIds + 1;
- if (name == 'runnable')
- return numRegularColorIds + 2;
- if (name == 'sleeping')
- return numRegularColorIds + 3;
- throw new Error('Unrecognized color ') + name;
- }
-
- // Previously computed string color IDs. They are based on a stable hash, so
- // it is safe to save them throughout the program time.
- var stringColorIdCache = {};
-
- /**
- * @return {Number} A color ID that is stably associated to the provided via
- * the getStringHash method. The color ID will be chosen from the regular
- * ID space only, e.g. no reserved ID will be used.
- */
- function getStringColorId(string) {
- if (stringColorIdCache[string] === undefined) {
- var hash = getStringHash(string);
- stringColorIdCache[string] = hash % numRegularColorIds;
- }
- return stringColorIdCache[string];
- }
-
- return {
- getColorPalette: getColorPalette,
- getColorPaletteHighlightIdBoost: getColorPaletteHighlightIdBoost,
- getColorIdByName: getColorIdByName,
- getStringHash: getStringHash,
- getStringColorId: getStringColorId
- };
- });
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- 'use strict';
-
- base.requireStylesheet('tracks.timeline_canvas_based_track');
- base.require('tracks.timeline_track');
- base.require('fast_rect_renderer');
- base.require('timeline_color_scheme');
- base.require('ui');
-
- base.exportTo('tracks', function() {
-
- /**
- * A canvas-based track constructed. Provides the basic heading and
- * invalidation-managment infrastructure. Subclasses must implement drawing
- * and picking code.
- * @constructor
- * @extends {HTMLDivElement}
- */
- var TimelineCanvasBasedTrack = base.ui.define(tracks.TimelineTrack);
-
- TimelineCanvasBasedTrack.prototype = {
- __proto__: tracks.TimelineTrack.prototype,
-
- decorate: function() {
- this.className = 'timeline-canvas-based-track';
- this.slices_ = null;
-
- this.headingDiv_ = document.createElement('div');
- this.headingDiv_.className = 'timeline-canvas-based-track-title';
- this.appendChild(this.headingDiv_);
-
- this.canvasContainer_ = document.createElement('div');
- this.canvasContainer_.className =
- 'timeline-canvas-based-track-canvas-container';
- this.appendChild(this.canvasContainer_);
- this.canvas_ = document.createElement('canvas');
- this.canvas_.className = 'timeline-canvas-based-track-canvas';
- this.canvasContainer_.appendChild(this.canvas_);
-
- this.ctx_ = this.canvas_.getContext('2d');
- },
-
- detach: function() {
- if (this.viewport_) {
- this.viewport_.removeEventListener('change',
- this.viewportChangeBoundToThis_);
- this.viewport_.removeEventListener('markersChange',
- this.viewportMarkersChangeBoundToThis_);
- }
- },
-
- set headingWidth(width) {
- this.headingDiv_.style.width = width;
- },
-
- get heading() {
- return this.headingDiv_.textContent;
- },
-
- set heading(text) {
- this.headingDiv_.textContent = text;
- },
-
- set tooltip(text) {
- this.headingDiv_.title = text;
- },
-
- get viewport() {
- return this.viewport_;
- },
-
- set viewport(v) {
- this.viewport_ = v;
- if (this.viewport_) {
- this.viewport_.removeEventListener('change',
- this.viewportChangeBoundToThis_);
- this.viewport_.removeEventListener('markersChange',
- this.viewportMarkersChangeBoundToThis_);
- }
- this.viewport_ = v;
- if (this.viewport_) {
- this.viewportChangeBoundToThis_ = this.viewportChange_.bind(this);
- this.viewport_.addEventListener('change',
- this.viewportChangeBoundToThis_);
- this.viewportMarkersChangeBoundToThis_ =
- this.viewportMarkersChange_.bind(this);
- this.viewport_.addEventListener('markersChange',
- this.viewportMarkersChangeBoundToThis_);
- if (this.isAttachedToDocument_)
- this.updateCanvasSizeIfNeeded_();
- }
- this.invalidate();
- },
-
- viewportChange_: function() {
- this.invalidate();
- },
-
- viewportMarkersChange_: function() {
- if (this.viewport_.markers.length < 2)
- this.classList.remove('timeline-viewport-track-with' +
- '-distance-measurements');
- else
- this.classList.add('timeline-viewport-track-with' +
- '-distance-measurements');
- },
-
- invalidate: function() {
- if (this.rafPending_)
- return;
- webkitRequestAnimationFrame(function() {
- this.rafPending_ = false;
- if (!this.viewport_)
- return;
- this.updateCanvasSizeIfNeeded_();
- this.redraw();
- }.bind(this), this);
- this.rafPending_ = true;
- },
-
- /**
- * @return {boolean} Whether the current timeline is attached to the
- * document.
- */
- get isAttachedToDocument_() {
- var cur = this.parentNode;
- if (!cur)
- return;
- while (cur.parentNode)
- cur = cur.parentNode;
- return cur == this.ownerDocument;
- },
-
-
- updateCanvasSizeIfNeeded_: function() {
- var style = window.getComputedStyle(this.canvasContainer_);
- var innerWidth = parseInt(style.width) -
- parseInt(style.paddingLeft) - parseInt(style.paddingRight) -
- parseInt(style.borderLeftWidth) - parseInt(style.borderRightWidth);
- var innerHeight = parseInt(style.height) -
- parseInt(style.paddingTop) - parseInt(style.paddingBottom) -
- parseInt(style.borderTopWidth) - parseInt(style.borderBottomWidth);
- var pixelRatio = window.devicePixelRatio || 1;
- if (this.canvas_.width != innerWidth) {
- this.canvas_.width = innerWidth * pixelRatio;
- this.canvas_.style.width = innerWidth + 'px';
- }
- if (this.canvas_.height != innerHeight) {
- this.canvas_.height = innerHeight * pixelRatio;
- this.canvas_.style.height = innerHeight + 'px';
- }
- },
- get firstCanvas() {
- return this.canvas_;
- }
- };
-
- return {
- TimelineCanvasBasedTrack: TimelineCanvasBasedTrack
- };
- });
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
-
- /**
- * @fileoverview Helper functions for doing intersections and iteration
- * over sorted arrays and intervals.
- *
- */
- base.exportTo('tracing', function() {
- /**
- * Finds the first index in the array whose value is >= loVal.
- *
- * The key for the search is defined by the mapFn. This array must
- * be prearranged such that ary.map(mapFn) would also be sorted in
- * ascending order.
- *
- * @param {Array} ary An array of arbitrary objects.
- * @param {function():*} mapFn Callback that produces a key value
- * from an element in ary.
- * @param {number} loVal Value for which to search.
- * @return {Number} Offset o into ary where all ary[i] for i <= o
- * are < loVal, or ary.length if loVal is greater than all elements in
- * the array.
- */
- function findLowIndexInSortedArray(ary, mapFn, loVal) {
- if (ary.length == 0)
- return 1;
-
- var low = 0;
- var high = ary.length - 1;
- var i, comparison;
- var hitPos = -1;
- while (low <= high) {
- i = Math.floor((low + high) / 2);
- comparison = mapFn(ary[i]) - loVal;
- if (comparison < 0) {
- low = i + 1; continue;
- } else if (comparison > 0) {
- high = i - 1; continue;
- } else {
- hitPos = i;
- high = i - 1;
- }
- }
- // return where we hit, or failing that the low pos
- return hitPos != -1 ? hitPos : low;
- }
-
- /**
- * Finds an index in an array of intervals that either
- * intersects the provided loVal, or if no intersection is found,
- * the index of the first interval whose start is > loVal.
- *
- * The array of intervals is defined implicitly via two mapping functions
- * over the provided ary. mapLoFn determines the lower value of the interval,
- * mapWidthFn the width. Intersection is lower-inclusive, e.g. [lo,lo+w).
- *
- * The array of intervals formed by this mapping must be non-overlapping and
- * sorted in ascending order by loVal.
- *
- * @param {Array} ary An array of objects that can be converted into sorted
- * nonoverlapping ranges [x,y) using the mapLoFn and mapWidth.
- * @param {function():*} mapLoFn Callback that produces the low value for the
- * interval represented by an element in the array.
- * @param {function():*} mapLoFn Callback that produces the width for the
- * interval represented by an element in the array.
- * @param {number} loVal The low value for the search.
- * @return {Number} An index in the array that intersects or is first-above
- * loVal, -1 if none found and loVal is below than all the intervals,
- * ary.length if loVal is greater than all the intervals.
- */
- function findLowIndexInSortedIntervals(ary, mapLoFn, mapWidthFn, loVal) {
- var first = findLowIndexInSortedArray(ary, mapLoFn, loVal);
- if (first == 0) {
- if (loVal >= mapLoFn(ary[0]) &&
- loVal < mapLoFn(ary[0] + mapWidthFn(ary[0]))) {
- return 0;
- } else {
- return -1;
- }
- } else if (first <= ary.length &&
- loVal >= mapLoFn(ary[first - 1]) &&
- loVal < mapLoFn(ary[first - 1]) + mapWidthFn(ary[first - 1])) {
- return first - 1;
- } else {
- return ary.length;
- }
- }
-
- /**
- * Calls cb for all intervals in the implicit array of intervals
- * defnied by ary, mapLoFn and mapHiFn that intersect the range
- * [loVal,hiVal)
- *
- * This function uses the same scheme as findLowIndexInSortedArray
- * to define the intervals. The same restrictions on sortedness and
- * non-overlappingness apply.
- *
- * @param {Array} ary An array of objects that can be converted into sorted
- * nonoverlapping ranges [x,y) using the mapLoFn and mapWidth.
- * @param {function():*} mapLoFn Callback that produces the low value for the
- * interval represented by an element in the array.
- * @param {function():*} mapLoFn Callback that produces the width for the
- * interval represented by an element in the array.
- * @param {number} The low value for the search, inclusive.
- * @param {number} loVal The high value for the search, non inclusive.
- * @param {function():*} cb The function to run for intersecting intervals.
- */
- function iterateOverIntersectingIntervals(ary, mapLoFn, mapWidthFn, loVal,
- hiVal, cb) {
- if (ary.length == 0)
- return;
-
- if (loVal > hiVal) return;
-
- var i = findLowIndexInSortedArray(ary, mapLoFn, loVal);
- if (i == -1) {
- return;
- }
- if (i > 0) {
- var hi = mapLoFn(ary[i - 1]) + mapWidthFn(ary[i - 1]);
- if (hi >= loVal) {
- cb(ary[i - 1]);
- }
- }
- if (i == ary.length) {
- return;
- }
-
- for (var n = ary.length; i < n; i++) {
- var lo = mapLoFn(ary[i]);
- if (lo >= hiVal)
- break;
- cb(ary[i]);
- }
- }
-
- /**
- * Non iterative version of iterateOverIntersectingIntervals.
- *
- * @return {Array} Array of elements in ary that intersect loVal, hiVal.
- */
- function getIntersectingIntervals(ary, mapLoFn, mapWidthFn, loVal, hiVal) {
- var tmp = [];
- iterateOverIntersectingIntervals(ary, mapLoFn, mapWidthFn, loVal, hiVal,
- function(d) {
- tmp.push(d);
- });
- return tmp;
- }
-
- return {
- findLowIndexInSortedArray: findLowIndexInSortedArray,
- findLowIndexInSortedIntervals: findLowIndexInSortedIntervals,
- iterateOverIntersectingIntervals: iterateOverIntersectingIntervals,
- getIntersectingIntervals: getIntersectingIntervals
- };
- });
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- 'use strict';
-
- base.requireStylesheet('tracks.timeline_slice_track');
-
- base.require('tracks.timeline_canvas_based_track');
- base.require('sorted_array_utils');
- base.require('fast_rect_renderer');
- base.require('timeline_color_scheme');
- base.require('ui');
-
- base.exportTo('tracks', function() {
-
- var palette = tracing.getColorPalette();
-
- /**
- * A track that displays an array of TimelineSlice objects.
- * @constructor
- * @extends {CanvasBasedTrack}
- */
-
- var TimelineSliceTrack = base.ui.define(tracks.TimelineCanvasBasedTrack);
-
- TimelineSliceTrack.prototype = {
-
- __proto__: tracks.TimelineCanvasBasedTrack.prototype,
-
- /**
- * Should we elide text on trace labels?
- * Without eliding, text that is too wide isn't drawn at all.
- * Disable if you feel this causes a performance problem.
- * This is a default value that can be overridden in tracks for testing.
- * @const
- */
- SHOULD_ELIDE_TEXT: true,
-
- decorate: function() {
- this.classList.add('timeline-slice-track');
- this.elidedTitleCache = new ElidedTitleCache();
- this.asyncStyle_ = false;
- },
-
- /**
- * Called by all the addToSelection functions on the created selection
- * hit objects. Override this function on parent classes to add
- * context-specific information to the hit.
- */
- decorateHit: function(hit) {
- },
-
- get asyncStyle() {
- return this.asyncStyle_;
- },
-
- set asyncStyle(v) {
- this.asyncStyle_ = !!v;
- this.invalidate();
- },
-
- get slices() {
- return this.slices_;
- },
-
- set slices(slices) {
- this.slices_ = slices || [];
- if (!slices)
- this.visible = false;
- this.invalidate();
- },
-
- get height() {
- return window.getComputedStyle(this).height;
- },
-
- set height(height) {
- this.style.height = height;
- this.invalidate();
- },
-
- labelWidth: function(title) {
- return quickMeasureText(this.ctx_, title) + 2;
- },
-
- labelWidthWorld: function(title, pixWidth) {
- return this.labelWidth(title) * pixWidth;
- },
-
- redraw: function() {
- var ctx = this.ctx_;
- var canvasW = this.canvas_.width;
- var canvasH = this.canvas_.height;
-
- ctx.clearRect(0, 0, canvasW, canvasH);
-
- // Culling parameters.
- var vp = this.viewport_;
- var pixWidth = vp.xViewVectorToWorld(1);
- var viewLWorld = vp.xViewToWorld(0);
- var viewRWorld = vp.xViewToWorld(canvasW);
-
- // Give the viewport a chance to draw onto this canvas.
- vp.drawUnderContent(ctx, viewLWorld, viewRWorld, canvasH);
-
- // Begin rendering in world space.
- ctx.save();
- vp.applyTransformToCanvas(ctx);
-
- // Slices.
- if (this.asyncStyle_)
- ctx.globalAlpha = 0.25;
- var tr = new tracing.FastRectRenderer(ctx, 2 * pixWidth, 2 * pixWidth,
- palette);
- tr.setYandH(0, canvasH);
- var slices = this.slices_;
- var lowSlice = tracing.findLowIndexInSortedArray(slices,
- function(slice) {
- return slice.start +
- slice.duration;
- },
- viewLWorld);
- for (var i = lowSlice; i < slices.length; ++i) {
- var slice = slices[i];
- var x = slice.start;
- if (x > viewRWorld) {
- break;
- }
- // Less than 0.001 causes short events to disappear when zoomed in.
- var w = Math.max(slice.duration, 0.001);
- var colorId = slice.selected ?
- slice.colorId + highlightIdBoost :
- slice.colorId;
-
- if (w < pixWidth)
- w = pixWidth;
- if (slice.duration > 0) {
- tr.fillRect(x, w, colorId);
- } else {
- // Instant: draw a triangle. If zoomed too far, collapse
- // into the FastRectRenderer.
- if (pixWidth > 0.001) {
- tr.fillRect(x, pixWidth, colorId);
- } else {
- ctx.fillStyle = palette[colorId];
- ctx.beginPath();
- ctx.moveTo(x - (4 * pixWidth), canvasH);
- ctx.lineTo(x, 0);
- ctx.lineTo(x + (4 * pixWidth), canvasH);
- ctx.closePath();
- ctx.fill();
- }
- }
- }
- tr.flush();
- ctx.restore();
-
- // Labels.
- var pixelRatio = window.devicePixelRatio || 1;
- if (canvasH > 8) {
- ctx.textAlign = 'center';
- ctx.textBaseline = 'top';
- ctx.font = (10 * pixelRatio) + 'px sans-serif';
- ctx.strokeStyle = 'rgb(0,0,0)';
- ctx.fillStyle = 'rgb(0,0,0)';
- // Don't render text until until it is 20px wide
- var quickDiscardThresshold = pixWidth * 20;
- var shouldElide = this.SHOULD_ELIDE_TEXT;
- for (var i = lowSlice; i < slices.length; ++i) {
- var slice = slices[i];
- if (slice.start > viewRWorld) {
- break;
- }
- if (slice.duration > quickDiscardThresshold) {
- var title = slice.title;
- if (slice.didNotFinish) {
- title += ' (Did Not Finish)';
- }
- var drawnTitle = title;
- var drawnWidth = this.labelWidth(drawnTitle);
- if (shouldElide &&
- this.labelWidthWorld(drawnTitle, pixWidth) > slice.duration) {
- var elidedValues = this.elidedTitleCache.get(
- this, pixWidth,
- drawnTitle, drawnWidth,
- slice.duration);
- drawnTitle = elidedValues.string;
- drawnWidth = elidedValues.width;
- }
- if (drawnWidth * pixWidth < slice.duration) {
- var cX = vp.xWorldToView(slice.start + 0.5 * slice.duration);
- ctx.fillText(drawnTitle, cX, 2.5 * pixelRatio, drawnWidth);
- }
- }
- }
- }
-
- // Give the viewport a chance to draw over this canvas.
- vp.drawOverContent(ctx, viewLWorld, viewRWorld, canvasH);
- },
-
- /**
- * Finds slices intersecting the given interval.
- * @param {number} vX X location to search at, in viewspace.
- * @param {number} vY Y location to search at, in viewspace.
- * @param {TimelineSelection} selection Selection to which to add hits.
- * @return {boolean} true if a slice was found, otherwise false.
- */
- addIntersectingItemsToSelection: function(vX, vY, selection) {
- var clientRect = this.getBoundingClientRect();
- if (vY < clientRect.top || vY >= clientRect.bottom)
- return false;
- var pixelRatio = window.devicePixelRatio || 1;
- var wX = this.viewport_.xViewVectorToWorld(vX * devicePixelRatio);
- var x = tracing.findLowIndexInSortedIntervals(this.slices_,
- function(x) { return x.start; },
- function(x) { return x.duration; },
- wX);
- if (x >= 0 && x < this.slices_.length) {
- var hit = selection.addSlice(this, this.slices_[x]);
- this.decorateHit(hit);
- return true;
- }
- return false;
- },
-
- /**
- * Adds items intersecting the given range to a selection.
- * @param {number} loVX Lower X bound of the interval to search, in
- * viewspace.
- * @param {number} hiVX Upper X bound of the interval to search, in
- * viewspace.
- * @param {number} loVY Lower Y bound of the interval to search, in
- * viewspace.
- * @param {number} hiVY Upper Y bound of the interval to search, in
- * viewspace.
- * @param {TimelineSelection} selection Selection to which to add hits.
- */
- addIntersectingItemsInRangeToSelection: function(
- loVX, hiVX, loVY, hiVY, selection) {
-
- var pixelRatio = window.devicePixelRatio || 1;
- var loWX = this.viewport_.xViewToWorld(loVX * pixelRatio);
- var hiWX = this.viewport_.xViewToWorld(hiVX * pixelRatio);
-
- var clientRect = this.getBoundingClientRect();
- var a = Math.max(loVY, clientRect.top);
- var b = Math.min(hiVY, clientRect.bottom);
- if (a > b)
- return;
-
- var that = this;
- function onPickHit(slice) {
- var hit = selection.addSlice(that, slice);
- that.decorateHit(hit);
- }
- tracing.iterateOverIntersectingIntervals(this.slices_,
- function(x) { return x.start; },
- function(x) { return x.duration; },
- loWX, hiWX,
- onPickHit);
- },
-
- /**
- * Find the index for the given slice.
- * @return {index} Index of the given slice, or undefined.
- * @private
- */
- indexOfSlice_: function(slice) {
- var index = tracing.findLowIndexInSortedArray(this.slices_,
- function(x) { return x.start; },
- slice.start);
- while (index < this.slices_.length &&
- slice.start == this.slices_[index].start &&
- slice.colorId != this.slices_[index].colorId) {
- index++;
- }
- return index < this.slices_.length ? index : undefined;
- },
-
- /**
- * Add the item to the left or right of the provided hit, if any, to the
- * selection.
- * @param {slice} The current slice.
- * @param {Number} offset Number of slices away from the hit to look.
- * @param {TimelineSelection} selection The selection to add a hit to,
- * if found.
- * @return {boolean} Whether a hit was found.
- * @private
- */
- addItemNearToProvidedHitToSelection: function(hit, offset, selection) {
- if (!hit.slice)
- return false;
-
- var index = this.indexOfSlice_(hit.slice);
- if (index === undefined)
- return false;
-
- var newIndex = index + offset;
- if (newIndex < 0 || newIndex >= this.slices_.length)
- return false;
-
- var hit = selection.addSlice(this, this.slices_[newIndex]);
- this.decorateHit(hit);
- return true;
- },
-
- addAllObjectsMatchingFilterToSelection: function(filter, selection) {
- for (var i = 0; i < this.slices_.length; ++i) {
- if (filter.matchSlice(this.slices_[i])) {
- var hit = selection.addSlice(this, this.slices_[i]);
- this.decorateHit(hit);
- }
- }
- }
- };
-
- var highlightIdBoost = tracing.getColorPaletteHighlightIdBoost();
-
- // TODO(jrg): possibly obsoleted with the elided string cache.
- // Consider removing.
- var textWidthMap = { };
- function quickMeasureText(ctx, text) {
- var w = textWidthMap[text];
- if (!w) {
- w = ctx.measureText(text).width;
- textWidthMap[text] = w;
- }
- return w;
- }
-
- /**
- * Cache for elided strings.
- * Moved from the ElidedTitleCache protoype to a "global" for speed
- * (variable reference is 100x faster).
- * key: String we wish to elide.
- * value: Another dict whose key is width
- * and value is an ElidedStringWidthPair.
- */
- var elidedTitleCacheDict = {};
-
- /**
- * A cache for elided strings.
- * @constructor
- */
- function ElidedTitleCache() {
- }
-
- ElidedTitleCache.prototype = {
- /**
- * Return elided text.
- * @param {track} A timeline slice track or other object that defines
- * functions labelWidth() and labelWidthWorld().
- * @param {pixWidth} Pixel width.
- * @param {title} Original title text.
- * @param {width} Drawn width in world coords.
- * @param {sliceDuration} Where the title must fit (in world coords).
- * @return {ElidedStringWidthPair} Elided string and width.
- */
- get: function(track, pixWidth, title, width, sliceDuration) {
- var elidedDict = elidedTitleCacheDict[title];
- if (!elidedDict) {
- elidedDict = {};
- elidedTitleCacheDict[title] = elidedDict;
- }
- var elidedDictForPixWidth = elidedDict[pixWidth];
- if (!elidedDictForPixWidth) {
- elidedDict[pixWidth] = {};
- elidedDictForPixWidth = elidedDict[pixWidth];
- }
- var stringWidthPair = elidedDictForPixWidth[sliceDuration];
- if (stringWidthPair === undefined) {
- var newtitle = title;
- var elided = false;
- while (track.labelWidthWorld(newtitle, pixWidth) > sliceDuration) {
- newtitle = newtitle.substring(0, newtitle.length * 0.75);
- elided = true;
- }
- if (elided && newtitle.length > 3)
- newtitle = newtitle.substring(0, newtitle.length - 3) + '...';
- stringWidthPair = new ElidedStringWidthPair(
- newtitle,
- track.labelWidth(newtitle));
- elidedDictForPixWidth[sliceDuration] = stringWidthPair;
- }
- return stringWidthPair;
- }
- };
-
- /**
- * A pair representing an elided string and world-coordinate width
- * to draw it.
- * @constructor
- */
- function ElidedStringWidthPair(string, width) {
- this.string = string;
- this.width = width;
- }
-
- return {
- TimelineSliceTrack: TimelineSliceTrack
- };
- });
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- 'use strict';
-
- /**
- * @fileoverview Provides the TimelineSlice class.
- */
- base.exportTo('tracing', function() {
-
- /**
- * A TimelineSlice represents an interval of time plus parameters associated
- * with that interval.
- *
- * All time units are stored in milliseconds.
- * @constructor
- */
- function TimelineSlice(category, title, colorId, start, args, opt_duration) {
- this.category = category || '';
- this.title = title;
- this.start = start;
- this.colorId = colorId;
- this.args = args;
- this.didNotFinish = false;
- if (opt_duration !== undefined)
- this.duration = opt_duration;
- }
-
- TimelineSlice.prototype = {
- selected: false,
-
- duration: undefined,
-
- get end() {
- return this.start + this.duration;
- }
- };
-
- return {
- TimelineSlice: TimelineSlice
- };
- });
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- 'use strict';
-
- /**
- * @fileoverview Provides the TimelineSliceGroup class.
- */
- base.require('timeline_slice');
- base.require('timeline_color_scheme');
- base.require('timeline_filter');
-
- base.exportTo('tracing', function() {
- var TimelineSlice = tracing.TimelineSlice;
-
- /**
- * A group of TimelineSlices, plus code to create them from B/E events, as
- * well as arrange them into subRows.
- *
- * Do not mutate the slices array directly. Modify it only by
- * TimelineSliceGroup mutation methods.
- *
- * @constructor
- * @param {function(new:TimelineSlice, category, title, colorId, start, args)}
- * opt_sliceConstructor The constructor to use when creating slices.
- */
- function TimelineSliceGroup(opt_sliceConstructor) {
- var sliceConstructor = opt_sliceConstructor || TimelineSlice;
- this.sliceConstructor = sliceConstructor;
-
- this.openPartialSlices_ = [];
-
- this.slices = [];
- }
-
- TimelineSliceGroup.prototype = {
- __proto__: Object.prototype,
-
- /**
- * Helper function that pushes the provided slice onto the slices array.
- * @param {TimelineSlice} slice The slice to be added to the slices array.
- */
- pushSlice: function(slice) {
- this.slices.push(slice);
- return slice;
- },
-
- /**
- * Helper function that pushes the provided slice onto the slices array.
- * @param {Array.<TimelineSlice>} slices An array of slices to be added.
- */
- pushSlices: function(slices) {
- this.slices.push.apply(this.slices, slices);
- },
-
- /**
- * Opens a new slice in the group's slices.
- *
- * Calls to beginSlice and
- * endSlice must be made with non-monotonically-decreasing timestamps.
- *
- * @param {String} title Title of the slice to add.
- * @param {Number} ts The timetsamp of the slice, in milliseconds.
- * @param {Object.<string, Object>} opt_args Arguments associated with
- * the slice.
- */
- beginSlice: function(category, title, ts, opt_args) {
- if (this.openPartialSlices_.length) {
- var prevSlice = this.openPartialSlices_[
- this.openPartialSlices_.length - 1];
- if (ts < prevSlice.start)
- throw new Error('Slices must be added in increasing timestamp order');
- }
-
- var colorId = tracing.getStringColorId(title);
- var slice = new this.sliceConstructor(category, title, colorId, ts,
- opt_args ? opt_args : {});
- this.openPartialSlices_.push(slice);
- return slice;
- },
-
- isTimestampValidForBeginOrEnd: function(ts) {
- if (!this.openPartialSlices_.length)
- return true;
- var top = this.openPartialSlices_[this.openPartialSlices_.length - 1];
- return ts >= top.start;
- },
-
- /**
- * @return {Number} The number of beginSlices for which an endSlice has not
- * been issued.
- */
- get openSliceCount() {
- return this.openPartialSlices_.length;
- },
-
- /**
- * Ends the last begun slice in this group and pushes it onto the slice
- * array.
- *
- * @param {Number} ts Timestamp when the slice ended.
- * @return {TimelineSlice} slice.
- */
- endSlice: function(ts) {
- if (!this.openSliceCount)
- throw new Error('endSlice called without an open slice');
- var slice = this.openPartialSlices_[this.openSliceCount - 1];
- this.openPartialSlices_.splice(this.openSliceCount - 1, 1);
- if (ts < slice.start)
- throw new Error('Slice ' + slice.name +
- ' end time is before its start.');
-
- slice.duration = ts - slice.start;
- this.pushSlice(slice);
-
- return slice;
- },
-
- /**
- * Closes any open slices.
- * @param {Number} opt_maxTimestamp The end time to use for the closed
- * slices. If not provided,
- * the max timestamp for this slice is provided.
- */
- autoCloseOpenSlices: function(opt_maxTimestamp) {
- if (!opt_maxTimestamp) {
- this.updateBounds();
- opt_maxTimestamp = this.maxTimestamp;
- }
- while (this.openSliceCount > 0) {
- var slice = this.endSlice(opt_maxTimestamp);
- slice.didNotFinish = true;
- }
- },
-
- /**
- * Shifts all the timestamps inside this group forward by the amount
- * specified.
- */
- shiftTimestampsForward: function(amount) {
- for (var sI = 0; sI < this.slices.length; sI++) {
- var slice = this.slices[sI];
- slice.start = (slice.start + amount);
- }
- for (var sI = 0; sI < this.openPartialSlices_.length; sI++) {
- var slice = this.openPartialSlices_[i];
- slice.start = (slice.start + amount);
- }
- },
-
- /**
- * Updates the bounds for this group based on the slices it contains.
- */
- updateBounds: function() {
- var vals = [];
- if (this.slices.length) {
- var minTimestamp = Number.MAX_VALUE;
- var maxTimestamp = -Number.MAX_VALUE;
- for (var i = 0; i < this.slices.length; i++) {
- if (this.slices[i].start < minTimestamp)
- minTimestamp = this.slices[i].start;
- if (this.slices[i].end > maxTimestamp)
- maxTimestamp = this.slices[i].end;
- }
- vals.push(minTimestamp);
- vals.push(maxTimestamp);
- }
-
- if (this.openPartialSlices_.length) {
- vals.push(this.openPartialSlices_[0].start);
- vals.push(
- this.openPartialSlices_[this.openPartialSlices_.length - 1].start);
- }
-
- if (vals.length) {
- this.minTimestamp = Math.min.apply(Math, vals);
- this.maxTimestamp = Math.max.apply(Math, vals);
- } else {
- this.minTimestamp = undefined;
- this.maxTimestamp = undefined;
- }
- }
- };
-
- return {
- TimelineSliceGroup: TimelineSliceGroup
- };
- });
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- 'use strict';
-
- /**
- * @fileoverview Provides the TimelineAsyncSliceGroup class.
- */
- base.require('timeline_slice');
- base.exportTo('tracing', function() {
-
- var TimelineSlice = tracing.TimelineSlice;
-
- /**
- * 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(category, title, colorId, start, args) {
- TimelineSlice.call(this, category, title, colorId, start, args);
- };
-
- TimelineAsyncSlice.prototype = {
- __proto__: TimelineSlice.prototype,
-
- toJSON: function() {
- var obj = new Object();
- var keys = Object.keys(this);
- for (var i = 0; i < keys.length; i++) {
- var key = keys[i];
- if (typeof this[key] == 'function')
- continue;
- if (key == 'startThread' || key == 'endThread') {
- obj[key] = this[key].ptid;
- continue;
- }
- obj[key] = this[key];
- }
- return obj;
- },
-
- id: undefined,
-
- startThread: undefined,
-
- endThread: undefined,
-
- subSlices: undefined
- };
-
- /**
- * 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;
- },
-
- /**
- * Shifts all the timestamps inside this group forward by the amount
- * specified.
- */
- shiftTimestampsForward: function(amount) {
- for (var sI = 0; sI < this.slices.length; sI++) {
- var slice = this.slices[sI];
- slice.start = (slice.start + amount);
- for (var sJ = 0; sJ < slice.subSlices.length; sJ++)
- slice.subSlices[sJ].start += amount;
- }
- },
-
- /**
- * Updates the bounds for this group based on the slices it contains.
- */
- updateBounds: function() {
- if (this.slices.length) {
- var minTimestamp = Number.MAX_VALUE;
- var maxTimestamp = -Number.MAX_VALUE;
- for (var i = 0; i < this.slices.length; i++) {
- if (this.slices[i].start < minTimestamp)
- minTimestamp = this.slices[i].start;
- if (this.slices[i].end > maxTimestamp)
- maxTimestamp = this.slices[i].end;
- }
- this.minTimestamp = minTimestamp;
- this.maxTimestamp = maxTimestamp;
- } else {
- this.minTimestamp = undefined;
- this.maxTimestamp = undefined;
- }
- },
-
- /**
- * 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;
- }
- };
-
- return {
- TimelineAsyncSlice: TimelineAsyncSlice,
- TimelineAsyncSliceGroup: TimelineAsyncSliceGroup
- };
- });
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- 'use strict';
-
- /**
- * @fileoverview Provides the TimelineThread class.
- */
- base.require('timeline_slice');
- base.require('timeline_slice_group');
- base.require('timeline_async_slice_group');
- base.exportTo('tracing', function() {
-
- var TimelineSlice = tracing.TimelineSlice;
- var TimelineSliceGroup = tracing.TimelineSliceGroup;
- var TimelineAsyncSlice = tracing.TimelineAsyncSlice;
- var TimelineAsyncSliceGroup = tracing.TimelineAsyncSliceGroup;
-
- /**
- * 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(cat, title, colorId, start, args, opt_duration) {
- TimelineSlice.call(this, cat, title, colorId, start, args, opt_duration);
- // Do not modify this directly.
- // subSlices is configured by TimelineSliceGroup.rebuildSubRows_.
- this.subSlices = [];
- }
-
- TimelineThreadSlice.prototype = {
- __proto__: TimelineSlice.prototype
- };
-
- /**
- * A TimelineThread stores all the trace events collected for a particular
- * 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) {
- TimelineSliceGroup.call(this, TimelineThreadSlice);
- if (!parent)
- throw new Error('Parent must be provided.');
- this.pid = parent.pid;
- this.tid = tid;
- this.cpuSlices = undefined;
- 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 = {
-
- __proto__: TimelineSliceGroup.prototype,
-
- /**
- * Name of the thread, if present.
- */
- name: undefined,
-
- /**
- * @return {string} A concatenation of the pid and the thread's
- * tid. Can be used to uniquely identify a thread.
- */
- get ptid() {
- return TimelineThread.getPTIDFromPidAndTid(this.tid, this.pid);
- },
-
- /**
- * Shifts all the timestamps inside this thread forward by the amount
- * specified.
- */
- shiftTimestampsForward: function(amount) {
- TimelineSliceGroup.prototype.shiftTimestampsForward.call(this, amount);
-
- if (this.cpuSlices) {
- for (var i = 0; i < this.cpuSlices.length; i++) {
- var slice = this.cpuSlices[i];
- slice.start += amount;
- }
- }
-
- this.asyncSlices.shiftTimestampsForward(amount);
- },
-
- /**
- * Determins whether this thread is empty. If true, it usually implies
- * that it should be pruned from the model.
- */
- get isEmpty() {
- if (this.slices.length)
- return false;
- if (this.openSliceCount)
- return false;
- if (this.cpuSlices && this.cpuSlices.length)
- return false;
- if (this.asyncSlices.length)
- return false;
- return true;
- },
-
- /**
- * Updates the minTimestamp and maxTimestamp fields based on the
- * current objects associated with the thread.
- */
- updateBounds: function() {
- TimelineSliceGroup.prototype.updateBounds.call(this);
- var values = [];
- if (this.minTimestamp !== undefined)
- values.push(this.minTimestamp, this.maxTimestamp);
-
- if (this.asyncSlices.slices.length) {
- this.asyncSlices.updateBounds();
- values.push(this.asyncSlices.minTimestamp);
- values.push(this.asyncSlices.maxTimestamp);
- }
-
- if (this.cpuSlices && this.cpuSlices.length) {
- values.push(this.cpuSlices[0].start);
- values.push(this.cpuSlices[this.cpuSlices.length - 1].end);
- }
-
- if (values.length) {
- this.minTimestamp = Math.min.apply(Math, values);
- this.maxTimestamp = Math.max.apply(Math, values);
- } else {
- this.minTimestamp = undefined;
- this.maxTimestamp = undefined;
- }
- },
-
- /**
- * @return {String} A user-friendly name for this thread.
- */
- get userFriendlyName() {
- var tname = this.name || this.tid;
- return this.pid + ': ' + tname;
- },
-
- /**
- * @return {String} User friendly details about this thread.
- */
- get userFriendlyDetails() {
- return 'pid: ' + this.pid +
- ', tid: ' + this.tid +
- (this.name ? ', name: ' + this.name : '');
- }
- };
-
- /**
- * Comparison between threads that orders first by pid,
- * then by names, then by tid.
- */
- TimelineThread.compare = function(x, y) {
- if (x.pid != y.pid)
- return x.pid - y.pid;
-
- if (x.name && y.name) {
- var tmp = x.name.localeCompare(y.name);
- if (tmp == 0)
- return x.tid - y.tid;
- return tmp;
- } else if (x.name) {
- return -1;
- } else if (y.name) {
- return 1;
- } else {
- return x.tid - y.tid;
- }
- };
-
- return {
- TimelineThreadSlice: TimelineThreadSlice,
- TimelineThread: TimelineThread
- };
- });
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- 'use strict';
-
- /**
- * @fileoverview Provides the TimelineCounter class.
- */
- base.exportTo('tracing', function() {
-
- var nextCounterGUID = 1;
-
- /**
- * Stores all the samples for a given counter.
- * @constructor
- */
- function TimelineCounter(parent, id, category, name) {
- if (parent == null) {
- this.parent_id = null;
- } else if (parent.pid != undefined) {
- this.parent_id = parent.pid;
- } else if (parent.cpuNumber != undefined) {
- this.parent_id = parent.cpuNumber;
- }
- this.id = id;
- this.category = category || '';
- this.name = name;
- this.seriesNames = [];
- this.seriesColors = [];
- this.timestamps = [];
- this.samples = [];
- this.guid_ = nextCounterGUID++;
- }
-
- TimelineCounter.prototype = {
- __proto__: Object.prototype,
- /*
- * @return {Number} A globally unique identifier for this counter.
- */
- get guid() {
- return this.guid_;
- },
-
- get numSeries() {
- return this.seriesNames.length;
- },
-
- get numSamples() {
- return this.timestamps.length;
- },
-
- getSampleValue: function(index, seriesIndex) {
- return this.samples[index * this.numSeries + seriesIndex];
- },
-
- /**
- * Obtains min, max, avg, values, start, and end for different series for
- * a given counter
- * getSampleStatistics([0,1])
- * The statistics objects that this returns are an array of objects, one
- * object for each series for the counter in the form:
- * {min: minVal, max: maxVal, avg: avgVal, start: startVal, end: endVal}
- *
- * @param {Array.<Number>} Indices to summarize.
- * @return {Object} An array of statistics. Each element in the array
- * has data for one of the series in the selected counter.
- */
- getSampleStatistics: function(sampleIndices) {
- sampleIndices.sort();
- var sampleIndex = this.sampleIndex;
- var numSeries = this.numSeries;
- var numSamples = this.numSamples;
-
- var ret = [];
-
- for (var i = 0; i < numSeries; ++i) {
- var sum = 0;
- var min = Number.MAX_VALUE;
- var max = -Number.MAX_VALUE;
- for (var j = 0; j < sampleIndices.length; j++) {
- var x = sampleIndices[j];
- sum += this.getSampleValue(x, i);
- min = Math.min(this.getSampleValue(x, i), min);
- max = Math.max(this.getSampleValue(x, i), max);
- }
- var avg = sum / sampleIndices.length;
- var start = this.getSampleValue(sampleIndices[0], i);
- var end = this.getSampleValue(
- sampleIndices[sampleIndices.length - 1], i);
-
- ret.push({min: min,
- max: max,
- avg: avg,
- start: start,
- end: end});
- }
- return ret;
- },
-
- /**
- * 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() {
- if (this.seriesNames.length != this.seriesColors.length)
- throw new Error('seriesNames.length must match seriesColors.length');
- if (this.numSeries * this.numSamples != this.samples.length)
- throw new Error('samples.length must be a multiple of numSamples.');
-
- this.totals = [];
- if (this.samples.length == 0) {
- this.minTimestamp = undefined;
- this.maxTimestamp = undefined;
- this.maxTotal = 0;
- return;
- }
- this.minTimestamp = this.timestamps[0];
- this.maxTimestamp = this.timestamps[this.timestamps.length - 1];
-
- var numSeries = this.numSeries;
- var maxTotal = -Infinity;
- for (var i = 0; i < this.timestamps.length; i++) {
- var total = 0;
- for (var j = 0; j < numSeries; j++) {
- total += this.samples[i * numSeries + j];
- this.totals.push(total);
- }
- if (total > maxTotal)
- maxTotal = total;
- }
- this.maxTotal = maxTotal;
- }
-
- };
-
- /**
- * Comparison between counters that orders by parent_id, then name.
- */
- TimelineCounter.compare = function(x, y) {
- if (x.parent_id != y.parent_id) {
- return x.parent_id - y.parent_id;
- }
- var tmp = x.name.localeCompare(y.name);
- if (tmp == 0)
- return x.tid - y.tid;
- return tmp;
- };
-
- return {
- TimelineCounter: TimelineCounter
- };
- });
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- 'use strict';
-
- /**
- * @fileoverview Provides the TimelineProcess class.
- */
- base.require('timeline_thread');
- base.require('timeline_counter');
- base.exportTo('tracing', function() {
-
- var TimelineThread = tracing.TimelineThread;
- var TimelineCounter = tracing.TimelineCounter;
-
- /**
- * The TimelineProcess represents a single process in the
- * trace. Right now, we keep this around purely for bookkeeping
- * reasons.
- * @constructor
- */
- function TimelineProcess(pid) {
- this.pid = pid;
- this.threads = {};
- this.counters = {};
- };
-
- TimelineProcess.prototype = {
- get numThreads() {
- var n = 0;
- for (var p in this.threads) {
- n++;
- }
- return n;
- },
-
- /**
- * 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.
- */
- getOrCreateThread: function(tid) {
- if (!this.threads[tid])
- this.threads[tid] = new TimelineThread(this, tid);
- return this.threads[tid];
- },
-
- /**
- * @return {TimlineCounter} The counter on this process named 'name',
- * creating it if it doesn't exist.
- */
- getOrCreateCounter: function(cat, name) {
- var id = cat + '.' + name;
- if (!this.counters[id])
- this.counters[id] = new TimelineCounter(this, id, cat, name);
- return this.counters[id];
- }
- };
-
- /**
- * Comparison between processes that orders by pid.
- */
- TimelineProcess.compare = function(x, y) {
- return x.pid - y.pid;
- };
-
- return {
- TimelineProcess: TimelineProcess
- };
- });
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- 'use strict';
-
- /**
- * @fileoverview Provides the TimelineCpu class.
- */
- base.require('timeline_slice');
- base.require('timeline_counter');
- base.exportTo('tracing', function() {
-
- var TimelineCounter = tracing.TimelineCounter;
-
- /**
- * The TimelineCpu represents a Cpu from the kernel's point of view.
- * @constructor
- */
- function TimelineCpu(number) {
- this.cpuNumber = number;
- this.slices = [];
- this.counters = {};
- };
-
- TimelineCpu.prototype = {
- /**
- * @return {TimlineCounter} The counter on this process named 'name',
- * creating it if it doesn't exist.
- */
- getOrCreateCounter: function(cat, name) {
- var id;
- if (cat.length)
- id = cat + '.' + name;
- else
- id = name;
- if (!this.counters[id])
- this.counters[id] = new TimelineCounter(this, id, cat, name);
- return this.counters[id];
- },
-
- /**
- * 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.
- */
- updateBounds: function() {
- var values = [];
- 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;
- }
- }
- };
-
- /**
- * Comparison between processes that orders by cpuNumber.
- */
- TimelineCpu.compare = function(x, y) {
- return x.cpuNumber - y.cpuNumber;
- };
-
-
- return {
- TimelineCpu: TimelineCpu
- };
- });
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- 'use strict';
-
- /**
- * @fileoverview TimelineModel is a parsed representation of the
- * TraceEvents obtained from base/trace_event in which the begin-end
- * tokens are converted into a hierarchy of processes, threads,
- * subrows, and slices.
- *
- * The building block of the model is a slice. A slice is roughly
- * equivalent to function call executing on a specific thread. As a
- * result, slices may have one or more subslices.
- *
- * A thread contains one or more subrows of slices. Row 0 corresponds to
- * the "root" slices, e.g. the topmost slices. Row 1 contains slices that
- * are nested 1 deep in the stack, and so on. We use these subrows to draw
- * nesting tasks.
- *
- */
- base.require('event_target');
- base.require('timeline_process');
- base.require('timeline_cpu');
- base.require('timeline_filter');
- base.exportTo('tracing', function() {
-
- var TimelineProcess = tracing.TimelineProcess;
- var TimelineCpu = tracing.TimelineCpu;
-
- /**
- * Builds a model from an array of TraceEvent objects.
- * @param {Object=} opt_eventData Data from a single trace to be imported into
- * the new model. See TimelineModel.importTraces for details on how to
- * import multiple traces at once.
- * @param {bool=} opt_shiftWorldToZero Whether to shift the world to zero.
- * Defaults to true.
- * @constructor
- */
- function TimelineModel(opt_eventData, opt_shiftWorldToZero) {
- this.cpus = {};
- this.processes = {};
- this.importErrors = [];
- this.metadata = [];
- this.categories = [];
-
- if (opt_eventData)
- this.importTraces([opt_eventData], opt_shiftWorldToZero);
- }
-
- var importerConstructors = [];
-
- /**
- * Registers an importer. All registered importers are considered
- * when processing an import request.
- *
- * @param {Function} importerConstructor The importer's constructor function.
- */
- TimelineModel.registerImporter = function(importerConstructor) {
- importerConstructors.push(importerConstructor);
- };
-
- function TimelineModelEmptyImporter(events) {
- this.importPriority = 0;
- };
-
- TimelineModelEmptyImporter.canImport = function(eventData) {
- if (eventData instanceof Array && eventData.length == 0)
- return true;
- if (typeof(eventData) === 'string' || eventData instanceof String) {
- return eventData.length == 0;
- }
- return false;
- };
-
- TimelineModelEmptyImporter.prototype = {
- __proto__: Object.prototype,
-
- importEvents: function() {
- },
- finalizeImport: function() {
- }
- };
-
- TimelineModel.registerImporter(TimelineModelEmptyImporter);
-
- TimelineModel.prototype = {
- __proto__: base.EventTarget.prototype,
-
- get numProcesses() {
- var n = 0;
- for (var p in this.processes)
- n++;
- return n;
- },
-
- /**
- * @return {TimelineProcess} Gets a specific TimelineCpu or creates one if
- * it does not exist.
- */
- getOrCreateCpu: function(cpuNumber) {
- if (!this.cpus[cpuNumber])
- this.cpus[cpuNumber] = new TimelineCpu(cpuNumber);
- return this.cpus[cpuNumber];
- },
-
- /**
- * @return {TimelineProcess} Gets a TimlineProcess for a specified pid or
- * creates one if it does not exist.
- */
- getOrCreateProcess: function(pid) {
- if (!this.processes[pid])
- this.processes[pid] = new TimelineProcess(pid);
- return this.processes[pid];
- },
-
- /**
- * Closes any slices that need closing
- */
- autoCloseOpenSlices_: function() {
- this.updateBounds();
- var maxTimestamp = this.maxTimestamp;
- for (var pid in this.processes) {
- var process = this.processes[pid];
- for (var tid in process.threads) {
- var thread = process.threads[tid];
- thread.autoCloseOpenSlices(maxTimestamp);
- }
- }
- },
-
- /**
- * Generates the set of categories from the slices and counters.
- */
- updateCategories_: function() {
- var threads = this.getAllThreads();
- for (var tI = 0; tI < threads.length; tI++) {
- var slices = threads[tI].slices;
- for (var i = 0; i < slices.length; i++) {
- var category = slices[i].category;
- if (category && this.categories.indexOf(category) == -1) {
- this.categories.push(category);
- }
- }
- }
- var counters = this.getAllCounters();
- for (var tI = 0; tI < counters.length; tI++) {
- var category = counters[tI].category;
- if (category && this.categories.indexOf(category) == -1) {
- this.categories.push(category);
- }
- }
- for (var cpu in this.cpus) {
- var slices = this.cpus[cpu].slices;
- for (var i = 0; i < slices.length; i++) {
- var category = slices[i].category;
- if (category && this.categories.indexOf(category) == -1) {
- this.categories.push(category);
- }
- }
- }
- },
-
- /**
- * Removes threads from the model that are fully empty.
- */
- pruneEmptyThreads_: function() {
- for (var pid in this.processes) {
- var process = this.processes[pid];
- var threadsToKeep = {};
- for (var tid in process.threads) {
- var thread = process.threads[tid];
- if (!thread.isEmpty)
- threadsToKeep[tid] = thread;
- }
- process.threads = threadsToKeep;
- }
- },
-
- updateBounds: function() {
- var wmin = Infinity;
- var wmax = -wmin;
- var hasData = false;
-
- var threads = this.getAllThreads();
- for (var tI = 0; tI < threads.length; tI++) {
- var thread = threads[tI];
- thread.updateBounds();
- if (thread.minTimestamp != undefined &&
- thread.maxTimestamp != undefined) {
- wmin = Math.min(wmin, thread.minTimestamp);
- wmax = Math.max(wmax, thread.maxTimestamp);
- hasData = true;
- }
- }
- var counters = this.getAllCounters();
- for (var tI = 0; tI < counters.length; tI++) {
- var counter = counters[tI];
- counter.updateBounds();
- if (counter.minTimestamp != undefined &&
- counter.maxTimestamp != undefined) {
- hasData = true;
- wmin = Math.min(wmin, counter.minTimestamp);
- wmax = Math.max(wmax, counter.maxTimestamp);
- }
- }
-
- for (var cpuNumber in this.cpus) {
- var cpu = this.cpus[cpuNumber];
- cpu.updateBounds();
- if (cpu.minTimestamp != undefined &&
- cpu.maxTimestamp != undefined) {
- hasData = true;
- wmin = Math.min(wmin, cpu.minTimestamp);
- wmax = Math.max(wmax, cpu.maxTimestamp);
- }
- }
-
- if (hasData) {
- this.minTimestamp = wmin;
- this.maxTimestamp = wmax;
- } else {
- this.maxTimestamp = undefined;
- this.minTimestamp = undefined;
- }
- },
-
- shiftWorldToZero: function() {
- if (this.minTimestamp === undefined)
- return;
- var timeBase = this.minTimestamp;
- for (var pid in this.processes)
- this.processes[pid].shiftTimestampsForward(-timeBase);
- for (var cpuNumber in this.cpus)
- this.cpus[cpuNumber].shiftTimestampsForward(-timeBase);
- this.updateBounds();
- },
-
- getAllThreads: function() {
- var threads = [];
- for (var pid in this.processes) {
- var process = this.processes[pid];
- for (var tid in process.threads) {
- threads.push(process.threads[tid]);
- }
- }
- return threads;
- },
-
- /**
- * @return {Array} An array of all cpus in the model.
- */
- getAllCpus: function() {
- var cpus = [];
- for (var cpu in this.cpus)
- cpus.push(this.cpus[cpu]);
- return cpus;
- },
-
- /**
- * @return {Array} An array of all processes in the model.
- */
- getAllProcesses: function() {
- var processes = [];
- for (var pid in this.processes)
- processes.push(this.processes[pid]);
- return processes;
- },
-
- /**
- * @return {Array} An array of all the counters in the model.
- */
- getAllCounters: function() {
- var counters = [];
- for (var pid in this.processes) {
- var process = this.processes[pid];
- for (var tid in process.counters) {
- counters.push(process.counters[tid]);
- }
- }
- for (var cpuNumber in this.cpus) {
- var cpu = this.cpus[cpuNumber];
- for (var counterName in cpu.counters)
- counters.push(cpu.counters[counterName]);
- }
- return counters;
- },
-
- /**
- * @param {String} The name of the thread to find.
- * @return {Array} An array of all the matched threads.
- */
- findAllThreadsNamed: function(name) {
- var namedThreads = [];
- var threads = this.getAllThreads();
- for (var i = 0; i < threads.length; i++) {
- var thread = threads[i];
- if (thread.name == name)
- namedThreads.push(thread);
- }
- return namedThreads;
- },
-
- createImporter_: function(eventData) {
- var importerConstructor;
- for (var i = 0; i < importerConstructors.length; ++i) {
- if (importerConstructors[i].canImport(eventData)) {
- importerConstructor = importerConstructors[i];
- break;
- }
- }
- if (!importerConstructor)
- throw new Error(
- 'Could not find an importer for the provided eventData.');
-
- var importer = new importerConstructor(
- this, eventData);
- return importer;
- },
-
- /**
- * Imports the provided traces into the model. The eventData type
- * is undefined and will be passed to all the timeline importers registered
- * via TimelineModel.registerImporter. The first importer that returns true
- * for canImport(events) will be used to import the events.
- *
- * The primary trace is provided via the eventData variable. If multiple
- * traces are to be imported, specify the first one as events, and the
- * remainder in the opt_additionalEventData array.
- *
- * @param {Array} traces An array of eventData to be imported. Each
- * eventData should correspond to a single trace file and will be handled by
- * a separate importer.
- * @param {bool=} opt_shiftWorldToZero Whether to shift the world to zero.
- * Defaults to true.
- */
- importTraces: function(traces,
- opt_shiftWorldToZero) {
- if (opt_shiftWorldToZero === undefined)
- opt_shiftWorldToZero = true;
-
- // Figure out which importers to use.
- var importers = [];
- for (var i = 0; i < traces.length; ++i)
- importers.push(this.createImporter_(traces[i]));
-
- // Sort them on priority. This ensures importing happens in a predictable
- // order, e.g. linux_perf_importer before trace_event_importer.
- importers.sort(function(x, y) {
- return x.importPriority - y.importPriority;
- });
-
- // Run the import.
- for (var i = 0; i < importers.length; i++)
- importers[i].importEvents(i > 0);
-
- this.autoCloseOpenSlices_();
-
- for (var i = 0; i < importers.length; i++)
- importers[i].finalizeImport();
-
- this.pruneEmptyThreads_();
- this.updateBounds();
-
- this.updateCategories_();
-
- if (opt_shiftWorldToZero)
- this.shiftWorldToZero();
- }
- };
-
- return {
- TimelineModel: TimelineModel
- };
-
- });
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- 'use strict';
-
- base.require('tracks.timeline_container_track');
- base.require('tracks.timeline_slice_track');
- base.require('timeline_filter');
- base.require('timeline_model');
- base.require('ui');
- base.exportTo('tracks', function() {
-
- /**
- * Visualizes a TimelineCpu using a series of of TimelineSliceTracks.
- * @constructor
- */
- var TimelineCpuTrack = base.ui.define(tracks.TimelineContainerTrack);
- TimelineCpuTrack.prototype = {
- __proto__: tracks.TimelineContainerTrack.prototype,
-
- decorate: function() {
- this.classList.add('timeline-cpu-track');
- },
-
- get cpu() {
- return this.cpu_;
- },
-
- set cpu(cpu) {
- this.cpu_ = cpu;
- this.updateChildTracks_();
- },
-
- get tooltip() {
- return this.tooltip_;
- },
-
- set tooltip(value) {
- this.tooltip_ = value;
- this.updateChildTracks_();
- },
-
- get heading() {
- return this.heading_;
- },
-
- set heading(h) {
- this.heading_ = h;
- this.updateChildTracks_();
- },
-
- applyCategoryFilter_: function() {
- if (this.categoryFilter.matchCpu(this.cpu_))
- this.updateChildTracks_();
- else
- this.visible = false;
- },
-
- updateChildTracks_: function() {
- this.detach();
- if (this.cpu_) {
- var slices = tracing.filterSliceArray(this.categoryFilter_,
- this.cpu_.slices);
- if (slices.length) {
- var track = new tracks.TimelineSliceTrack();
- track.slices = slices;
- track.heading = this.heading_;
- track.tooltip = this.tooltip_;
- this.addTrack_(track);
- }
-
- for (var counterName in this.cpu_.counters) {
- var counter = this.cpu_.counters[counterName];
- track = new tracks.TimelineCounterTrack();
- track.heading = 'CPU ' + this.cpu_.cpuNumber + ' ' +
- counter.name + ':';
- track.counter = counter;
- this.addTrack_(track);
- }
- }
- this.addControlButtonElements_(false);
- }
- };
-
- return {
- TimelineCpuTrack: TimelineCpuTrack
- };
- });
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- 'use strict';
-
- base.requireStylesheet('tracks.timeline_counter_track');
-
- base.require('tracks.timeline_canvas_based_track');
- base.require('timeline_color_scheme');
- base.require('ui');
-
- base.exportTo('tracks', function() {
-
- var palette = tracing.getColorPalette();
-
- /**
- * A track that displays a TimelineCounter object.
- * @constructor
- * @extends {CanvasBasedTrack}
- */
-
- var TimelineCounterTrack = base.ui.define(tracks.TimelineCanvasBasedTrack);
-
- TimelineCounterTrack.prototype = {
-
- __proto__: tracks.TimelineCanvasBasedTrack.prototype,
-
- decorate: function() {
- this.classList.add('timeline-counter-track');
- this.addControlButtonElements_(false);
- this.selectedSamples_ = {};
- this.categoryFilter_ = new tracing.TimelineFilter();
- },
-
- /**
- * Called by all the addToSelection functions on the created selection
- * hit objects. Override this function on parent classes to add
- * context-specific information to the hit.
- */
- decorateHit: function(hit) {
- },
-
- get counter() {
- return this.counter_;
- },
-
- set counter(counter) {
- this.counter_ = counter;
- this.invalidate();
- this.updateVisibility_();
- },
-
- set categoryFilter(v) {
- this.categoryFilter_ = v;
- this.updateVisibility_();
- },
-
- /**
- * @return {Object} A sparse, mutable map from sample index to bool. Samples
- * indices the map that are true are drawn as selected. Callers that mutate
- * the map must manually call invalidate on the track to trigger a redraw.
- */
- get selectedSamples() {
- return this.selectedSamples_;
- },
-
- updateVisibility_: function() {
- this.visible = (this.counter_ &&
- this.categoryFilter_.matchCounter(this.counter_));
- },
-
- redraw: function() {
- var ctr = this.counter_;
- var ctx = this.ctx_;
- var canvasW = this.canvas_.width;
- var canvasH = this.canvas_.height;
-
- ctx.clearRect(0, 0, canvasW, canvasH);
-
- // Culling parametrs.
- var vp = this.viewport_;
- var pixWidth = vp.xViewVectorToWorld(1);
- var viewLWorld = vp.xViewToWorld(0);
- var viewRWorld = vp.xViewToWorld(canvasW);
-
- // Give the viewport a chance to draw onto this canvas.
- vp.drawUnderContent(ctx, viewLWorld, viewRWorld, canvasH);
-
- // Drop sampels that are less than skipDistancePix apart.
- var skipDistancePix = 1;
- var skipDistanceWorld = vp.xViewVectorToWorld(skipDistancePix);
-
- // Begin rendering in world space.
- ctx.save();
- vp.applyTransformToCanvas(ctx);
-
- // Figure out where drawing should begin.
- var numSeries = ctr.numSeries;
- var numSamples = ctr.numSamples;
- var startIndex = tracing.findLowIndexInSortedArray(ctr.timestamps,
- function(x) {
- return x;
- },
- viewLWorld);
- startIndex = startIndex - 1 > 0 ? startIndex - 1 : 0;
-
- // Draw indices one by one until we fall off the viewRWorld.
- var yScale = canvasH / ctr.maxTotal;
- for (var seriesIndex = ctr.numSeries - 1;
- seriesIndex >= 0; seriesIndex--) {
- var colorId = ctr.seriesColors[seriesIndex];
- ctx.fillStyle = palette[colorId];
- ctx.beginPath();
-
- // Set iLast and xLast such that the first sample we draw is the
- // startIndex sample.
- var iLast = startIndex - 1;
- var xLast = iLast >= 0 ? ctr.timestamps[iLast] - skipDistanceWorld : -1;
- var yLastView = canvasH;
-
- // Iterate over samples from iLast onward until we either fall off the
- // viewRWorld or we run out of samples. To avoid drawing too much, after
- // drawing a sample at xLast, skip subsequent samples that are less than
- // skipDistanceWorld from xLast.
- var hasMoved = false;
- while (true) {
- var i = iLast + 1;
- if (i >= numSamples) {
- ctx.lineTo(xLast, yLastView);
- ctx.lineTo(xLast + 8 * pixWidth, yLastView);
- ctx.lineTo(xLast + 8 * pixWidth, canvasH);
- break;
- }
-
- var x = ctr.timestamps[i];
-
- var y = ctr.totals[i * numSeries + seriesIndex];
- var yView = canvasH - (yScale * y);
-
- if (x > viewRWorld) {
- ctx.lineTo(x, yLastView);
- ctx.lineTo(x, canvasH);
- break;
- }
-
- if (x - xLast < skipDistanceWorld) {
- iLast = i;
- continue;
- }
-
- if (!hasMoved) {
- ctx.moveTo(viewLWorld, canvasH);
- hasMoved = true;
- }
- ctx.lineTo(x, yLastView);
- ctx.lineTo(x, yView);
- iLast = i;
- xLast = x;
- yLastView = yView;
- }
- ctx.closePath();
- ctx.fill();
- }
- ctx.fillStyle = 'rgba(255, 0, 0, 1)';
- for (var i in this.selectedSamples_) {
- if (!this.selectedSamples_[i])
- continue;
-
- var x = ctr.timestamps[i];
- for (var seriesIndex = ctr.numSeries - 1;
- seriesIndex >= 0; seriesIndex--) {
- var y = ctr.totals[i * numSeries + seriesIndex];
- var yView = canvasH - (yScale * y);
- ctx.fillRect(x - pixWidth, yView - 1, 3 * pixWidth, 3);
- }
- }
- ctx.restore();
-
- // Give the viewport a chance to draw over this canvas.
- vp.drawOverContent(ctx, viewLWorld, viewRWorld, canvasH);
- },
-
- /**
- * Adds items intersecting a point to a selection.
- * @param {number} vX X location to search at, in viewspace.
- * @param {number} vY Y location to search at, in viewspace.
- * @param {TimelineSelection} selection Selection to which to add hits.
- * @return {boolean} true if a slice was found, otherwise false.
- */
- addIntersectingItemsToSelection: function(vX, vY, selection) {
- var clientRect = this.getBoundingClientRect();
- if (vY < clientRect.top || vY >= clientRect.bottom)
- return false;
-
- var pixelRatio = window.devicePixelRatio || 1;
- var wX = this.viewport_.xViewVectorToWorld(vX * devicePixelRatio);
-
- var ctr = this.counter_;
- if (vX < this.counter_.timestamps[0])
- return false;
- var i = tracing.findLowIndexInSortedArray(ctr.timestamps,
- function(x) { return x; },
- wX);
- if (i < 0 || i >= ctr.timestamps.length)
- return false;
-
- // Sample i is going to either be exactly at wX or slightly above it,
- // E.g. asking for 7.5 in [7,8] gives i=1. So bump i back by 1 if needed.
- if (i > 0 && wX > this.counter_.timestamps[i - 1])
- i--;
-
- // Some preliminaries.
- var canvasH = this.getBoundingClientRect().height;
- var yScale = canvasH / ctr.maxTotal;
-
- /*
- // Figure out which sample we hit
- var seriesIndexHit;
- for (var seriesIndex = 0; seriesIndex < ctr.numSeries; seriesIndex++) {
- var y = ctr.totals[i * ctr.numSeries + seriesIndex];
- var yView = canvasH - (yScale * y) + clientRect.top;
- if (wY >= yView) {
- seriesIndexHit = seriesIndex;
- break;
- }
- }
- if (seriesIndexHit === undefined)
- return false;
- */
- var hit = selection.addCounterSample(this, this.counter, i);
- this.decorateHit(hit);
- return true;
- },
-
- /**
- * Adds items intersecting the given range to a selection.
- * @param {number} loVX Lower X bound of the interval to search, in
- * viewspace.
- * @param {number} hiVX Upper X bound of the interval to search, in
- * viewspace.
- * @param {number} loVY Lower Y bound of the interval to search, in
- * viewspace.
- * @param {number} hiVY Upper Y bound of the interval to search, in
- * viewspace.
- * @param {TimelineSelection} selection Selection to which to add hits.
- */
- addIntersectingItemsInRangeToSelection: function(
- loVX, hiVX, loVY, hiVY, selection) {
-
- var clientRect = this.getBoundingClientRect();
- var a = Math.max(loVY, clientRect.top);
- var b = Math.min(hiVY, clientRect.bottom);
- if (a > b)
- return;
-
- var ctr = this.counter_;
-
- var pixelRatio = window.devicePixelRatio || 1;
- var loWX = this.viewport_.xViewToWorld(loVX * pixelRatio);
- var hiWX = this.viewport_.xViewToWorld(hiVX * pixelRatio);
-
- var iLo = tracing.findLowIndexInSortedArray(ctr.timestamps,
- function(x) { return x; },
- loWX);
- var iHi = tracing.findLowIndexInSortedArray(ctr.timestamps,
- function(x) { return x; },
- hiWX);
-
- // Sample i is going to either be exactly at wX or slightly above it,
- // E.g. asking for 7.5 in [7,8] gives i=1. So bump i back by 1 if needed.
- if (iLo > 0 && loWX > ctr.timestamps[iLo - 1])
- iLo--;
- if (iHi > 0 && hiWX > ctr.timestamps[iHi - 1])
- iHi--;
-
- // Iterate over every sample intersecting..
- for (var i = iLo; i <= iHi; i++) {
- if (i >= ctr.timestamps.length)
- continue;
-
- // TODO(nduca): Pick the seriesIndexHit based on the loY - hiY values.
- var hit = selection.addCounterSample(this, this.counter, i);
- this.decorateHit(hit);
- }
- },
-
- addAllObjectsMatchingFilterToSelection: function(filter, selection) {
- }
- };
-
- return {
- TimelineCounterTrack: TimelineCounterTrack
- };
- });
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- 'use strict';
-
- base.require('tracks.timeline_container_track');
- base.require('sorted_array_utils');
- base.require('ui');
-
- base.exportTo('tracks', function() {
-
- /**
- * A track that displays a TimelineSliceGroup.
- * @constructor
- * @extends {TimelineContainerTrack}
- */
-
- var TimelineSliceGroupTrack = base.ui.define(tracks.TimelineContainerTrack);
-
- TimelineSliceGroupTrack.prototype = {
-
- __proto__: tracks.TimelineContainerTrack.prototype,
-
- decorate: function() {
- this.classList.add('timeline-slice-group-track');
- },
-
- get group() {
- return this.group_;
- },
-
- set group(g) {
- this.group_ = g;
- this.updateChildTracks_();
- },
-
- set heading(h) {
- if (this.tracks_.length)
- this.tracks_[0].heading = h;
- },
-
- set tooltip(t) {
- if (this.tracks_.length)
- this.tracks_[0].tooltip = t;
- },
-
- set decorateHit(f) {
- this.decorateHit_ = f;
- this.updateChildTracks_();
- },
-
- applyCategoryFilter_: function() {
- this.updateChildTracks_();
- },
-
- addSliceTrack_: function(slices) {
- var track = new tracks.TimelineSliceTrack();
- track.slices = slices;
- track.decorateHit = this.decorateHit_;
- this.addTrack_(track);
- return track;
- },
-
- updateChildTracks_: function() {
- if (!this.group_) {
- this.visible = false;
- return;
- }
-
- var slices = tracing.filterSliceArray(this.categoryFilter,
- this.group_.slices);
- if (!slices.length) {
- this.visible = false;
- return;
- }
- this.visible = true;
-
- if (this.areArrayContentsSame_(this.filteredSlices_, slices))
- return;
-
- this.filteredSlices_ = slices;
- this.detach();
- this.subRows_ = this.buildSubRows_(slices);
- for (var srI = 0; srI < this.subRows_.length; srI++) {
- if (this.subRows_[srI].length) {
- this.addSliceTrack_(this.subRows_[srI]);
- }
- }
- },
-
- /**
- * Breaks up the list of slices into N rows, each of which is a list of
- * slices that are non overlapping.
- */
- buildSubRows_: function(slices) {
- // This function works by walking through slices by start time.
- //
- // The basic idea here is to insert each slice as deep into the subrow
- // list as it can go such that every subSlice is fully contained by its
- // parent slice.
- //
- // Visually, if we start with this:
- // 0: [ a ]
- // 1: [ b ]
- // 2: [c][d]
- //
- // To place this slice:
- // [e]
- // We first check row 2's last item, [d]. [e] wont fit into [d] (they dont
- // even intersect). So we go to row 1. That gives us [b], and [d] wont fit
- // into that either. So, we go to row 0 and its last slice, [a]. That can
- // completely contain [e], so that means we should add [e] as a subchild
- // of [a]. That puts it on row 1, yielding:
- // 0: [ a ]
- // 1: [ b ][e]
- // 2: [c][d]
- //
- // If we then get this slice:
- // [f]
- // We do the same deepest-to-shallowest walk of the subrows trying to fit
- // it. This time, it doesn't fit in any open slice. So, we simply append
- // it to row 0:
- // 0: [ a ] [f]
- // 1: [ b ][e]
- // 2: [c][d]
- if (!slices.length)
- return [];
-
- var ops = [];
- for (var i = 0; i < slices.length; i++) {
- if (slices[i].subSlices)
- slices[i].subSlices.splice(0,
- slices[i].subSlices.length);
- ops.push(i);
- }
-
- ops.sort(function(ix, iy) {
- var x = slices[ix];
- var y = slices[iy];
- if (x.start != y.start)
- return x.start - y.start;
-
- // Elements get inserted into the slices array in order of when the
- // slices end. Because slices must be properly nested, we break
- // start-time ties by assuming that the elements appearing earlier in
- // the slices array (and thus ending earlier) start later.
- return iy - ix;
- });
-
- var subRows = [[]];
- this.badSlices_ = []; // TODO(simonjam): Connect this again.
-
- for (var i = 0; i < ops.length; i++) {
- var op = ops[i];
- var slice = slices[op];
-
- // Try to fit the slice into the existing subrows.
- var inserted = false;
- for (var j = subRows.length - 1; j >= 0; j--) {
- if (subRows[j].length == 0)
- continue;
-
- var insertedSlice = subRows[j][subRows[j].length - 1];
- if (slice.start < insertedSlice.start) {
- this.badSlices_.push(slice);
- inserted = true;
- }
- if (slice.start >= insertedSlice.start &&
- slice.end <= insertedSlice.end) {
- // Insert it into subRow j + 1.
- while (subRows.length <= j + 1)
- subRows.push([]);
- subRows[j + 1].push(slice);
- if (insertedSlice.subSlices)
- insertedSlice.subSlices.push(slice);
- inserted = true;
- break;
- }
- }
- if (inserted)
- continue;
-
- // Append it to subRow[0] as a root.
- subRows[0].push(slice);
- }
-
- return subRows;
- },
-
- areArrayContentsSame_: function(a, b) {
- if (!a || !b)
- return false;
- if (!a.length || !b.length)
- return false;
- if (a.length != b.length)
- return false;
- for (var i = 0; i < a.length; ++i) {
- if (a[i] != b[i])
- return false;
- }
- return true;
- }
- };
-
- return {
- TimelineSliceGroupTrack: TimelineSliceGroupTrack
- };
- });
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- 'use strict';
-
- base.require('tracks.timeline_slice_group_track');
- base.require('ui');
- base.exportTo('tracks', function() {
-
- /**
- * A track that displays a TimelineAsyncSliceGroup.
- * @constructor
- * @extends {TimelineSliceGroup}
- */
-
- var TimelineAsyncSliceGroupTrack = base.ui.define(
- tracks.TimelineSliceGroupTrack);
-
- TimelineAsyncSliceGroupTrack.prototype = {
-
- __proto__: tracks.TimelineSliceGroupTrack.prototype,
-
- decorate: function() {
- this.classList.add('timeline-async-slice-group-track');
- },
-
- addSliceTrack_: function(slices) {
- var track = tracks.TimelineSliceGroupTrack.prototype.addSliceTrack_.call(
- this, slices);
- track.asyncStyle = true;
- return track;
- },
-
- /**
- * 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.
- */
- buildSubRows_: function() {
- var slices = tracing.filterSliceArray(this.categoryFilter,
- this.group_.slices);
- slices.sort(function(x, y) {
- return x.start - y.start;
- });
-
- var subRows = [];
- for (var i = 0; i < slices.length; i++) {
- var slice = 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;
- // Instead of plotting one big slice for the entire
- // TimelineAsyncEvent, we plot each of the subSlices.
- if (slice.subSlices === undefined || slice.subSlices.length < 1)
- throw new Error('TimelineAsyncEvent missing subSlices: ') +
- slice.name;
- for (var k = 0; k < slice.subSlices.length; k++)
- subRow.push(slice.subSlices[k]);
- break;
- }
- }
- if (!found) {
- var subRow = [];
- if (slice.subSlices !== undefined) {
- for (var k = 0; k < slice.subSlices.length; k++)
- subRow.push(slice.subSlices[k]);
- subRows.push(subRow);
- }
- }
- }
- return subRows;
- }
- };
-
- return {
- TimelineAsyncSliceGroupTrack: TimelineAsyncSliceGroupTrack
- };
- });
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- 'use strict';
-
- base.requireStylesheet('tracks.timeline_thread_track');
-
- base.require('tracks.timeline_container_track');
- base.require('tracks.timeline_slice_track');
- base.require('tracks.timeline_slice_group_track');
- base.require('tracks.timeline_async_slice_group_track');
- base.require('timeline_filter');
- base.require('ui');
-
- base.exportTo('tracks', function() {
-
- /**
- * Visualizes a TimelineThread using a series of of TimelineSliceTracks.
- * @constructor
- */
- var TimelineThreadTrack = base.ui.define(tracks.TimelineContainerTrack);
- TimelineThreadTrack.prototype = {
- __proto__: tracks.TimelineContainerTrack.prototype,
-
- decorate: function() {
- this.classList.add('timeline-thread-track');
- this.categoryFilter_ = new tracing.TimelineFilter();
- },
-
- get thread() {
- return this.thread_;
- },
-
- set thread(thread) {
- this.thread_ = thread;
- this.updateChildTracks_();
- },
-
- get tooltip() {
- return this.tooltip_;
- },
-
- set tooltip(value) {
- this.tooltip_ = value;
- this.updateChildTracks_();
- },
-
- get heading() {
- return this.heading_;
- },
-
- set heading(h) {
- this.heading_ = h;
- this.updateChildTracks_();
- },
-
- applyCategoryFilter_: function() {
- this.updateVisibility_();
- },
-
- updateChildTracks_: function() {
- this.detach();
- if (this.thread_) {
- var cpuTrack = new tracks.TimelineSliceTrack();
- cpuTrack.heading = '';
- cpuTrack.slices = this.thread_.cpuSlices;
- cpuTrack.height = '4px';
- cpuTrack.decorateHit = function(hit) {
- hit.thread = this.thread_;
- }
- this.addTrack_(cpuTrack);
-
- var asyncTrack = new tracks.TimelineAsyncSliceGroupTrack();
- asyncTrack.categoryFilter = this.categoryFilter;
- asyncTrack.decorateHit = function(hit) {
- // TODO(simonjam): figure out how to associate subSlice hits back
- // to their parent slice.
- }
- asyncTrack.group = this.thread_.asyncSlices;
- this.addTrack_(asyncTrack);
-
- var track = new tracks.TimelineSliceGroupTrack();
- track.decorateHit = function(hit) {
- hit.thread = this.thread_;
- }
- track.group = this.thread_;
- this.addTrack_(track);
-
- this.updateVisibility_();
- }
- this.addControlButtonElements_(this.tracks_.length >= 4);
- },
-
- updateVisibility_: function() {
- if (!this.categoryFilter.matchThread(this.thread)) {
- this.visible = false;
- return;
- }
- var shouldBeVisible = false;
- for (var i = 0; i < this.tracks_.length; ++i) {
- var track = this.tracks_[i];
- if (track.visible) {
- shouldBeVisible = true;
- if (i >= 1) {
- track.heading = this.heading_;
- track.tooltip = this.tooltip_;
- break;
- }
- }
- }
- this.visible = shouldBeVisible;
- },
-
- collapsedDidChange: function(collapsed) {
- if (collapsed) {
- var h = parseInt(this.tracks_[0].height);
- for (var i = 0; i < this.tracks_.length; ++i) {
- if (h > 2) {
- this.tracks_[i].height = Math.floor(h) + 'px';
- } else {
- this.tracks_[i].style.display = 'none';
- }
- h = h * 0.5;
- }
- } else {
- for (var i = 0; i < this.tracks_.length; ++i) {
- this.tracks_[i].height = this.tracks_[0].height;
- this.tracks_[i].style.display = '';
- }
- }
- }
- };
-
- return {
- TimelineThreadTrack: TimelineThreadTrack
- };
- });
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- 'use strict';
-
- base.require('tracks.timeline_container_track');
- base.require('tracks.timeline_counter_track');
- base.require('tracks.timeline_thread_track');
- base.require('timeline_filter');
- base.require('ui');
-
- base.exportTo('tracks', function() {
-
- /**
- * Visualizes a TimelineProcess by building TimelineThreadTracks and
- * TimelineCounterTracks.
- * @constructor
- */
- var TimelineProcessTrack = base.ui.define(tracks.TimelineContainerTrack);
-
- TimelineProcessTrack.prototype = {
-
- __proto__: tracks.TimelineContainerTrack.prototype,
-
- decorate: function() {
- this.classList.add('timeline-process-track');
- this.categoryFilter_ = new tracing.TimelineFilter();
- },
-
- get process() {
- return this.process_;
- },
-
- set process(process) {
- this.process_ = process;
- this.updateChildTracks_();
- },
-
- applyCategoryFilter_: function() {
- this.visible = (this.categoryFilter.matchProcess(this.process) &&
- !!this.numVisibleChildTracks);
- },
-
- updateChildTracks_: function() {
- this.detach();
- if (this.process_) {
- // Add counter tracks for this process.
- var counters = [];
- for (var tid in this.process.counters) {
- counters.push(this.process.counters[tid]);
- }
- counters.sort(tracing.TimelineCounter.compare);
-
- // Create the counters for this process.
- counters.forEach(function(counter) {
- var track = new tracks.TimelineCounterTrack();
- track.heading = counter.name + ':';
- track.counter = counter;
- this.addTrack_(track);
- }.bind(this));
-
- // Get a sorted list of threads.
- var threads = [];
- for (var tid in this.process.threads)
- threads.push(this.process.threads[tid]);
- threads.sort(tracing.TimelineThread.compare);
-
- // Create the threads.
- threads.forEach(function(thread) {
- var track = new tracks.TimelineThreadTrack();
- track.heading = thread.userFriendlyName + ':';
- track.tooltip = thread.userFriendlyDetails;
- track.thread = thread;
- this.addTrack_(track);
- }.bind(this));
- }
- }
- };
-
- return {
- TimelineProcessTrack: TimelineProcessTrack
- };
- });
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- 'use strict';
-
- base.requireStylesheet('tracks.timeline_model_track');
- base.require('tracks.timeline_container_track');
- base.require('tracks.timeline_cpu_track');
- base.require('tracks.timeline_process_track');
- base.require('ui');
-
- base.exportTo('tracks', function() {
-
- /**
- * Visualizes a TimelineModel by building TimelineProcessTracks and
- * TimelineCpuTracks.
- * @constructor
- */
- var TimelineModelTrack = base.ui.define(tracks.TimelineContainerTrack);
-
- TimelineModelTrack.prototype = {
-
- __proto__: tracks.TimelineContainerTrack.prototype,
-
- decorate: function() {
- this.classList.add('timeline-model-track');
- this.measuringStick_ = new tracing.MeasuringStick();
- this.measuringStick_.attach();
- },
-
- detach: function() {
- tracks.TimelineContainerTrack.prototype.detach.call(this);
- this.measuringStick_.detach();
- },
-
- get model() {
- return this.model_;
- },
-
- set model(model) {
- this.model_ = model;
- this.updateHeadingWidth_();
- this.updateChildTracks_();
- },
-
- updateHeadingWidth_: function() {
- // Figure out all the headings.
- var allHeadings = [];
- this.model.getAllThreads().forEach(function(t) {
- allHeadings.push(t.userFriendlyName);
- });
- this.model.getAllCounters().forEach(function(c) {
- allHeadings.push(c.name);
- });
- this.model.getAllCpus().forEach(function(c) {
- allHeadings.push('CPU ' + c.cpuNumber);
- });
-
- // Figure out the maximum heading size.
- var maxHeadingWidth = 0;
- var headingEl = document.createElement('div');
- headingEl.style.position = 'fixed';
- headingEl.className = 'timeline-canvas-based-track-title';
- for (var i = 0; i < allHeadings.length; i++) {
- var text = allHeadings[i];
- headingEl.textContent = text + ':__';
- var w = this.measuringStick_.measure(headingEl).width;
- // Limit heading width to 300px.
- if (w > 300)
- w = 300;
- if (w > maxHeadingWidth)
- maxHeadingWidth = w;
- }
- this.headingWidth = maxHeadingWidth + 'px';
- },
-
- updateChildTracks_: function() {
- this.detachAllChildren();
- if (this.model_) {
- var cpus = this.model_.getAllCpus();
- cpus.sort(tracing.TimelineCpu.compare);
-
- for (var i = 0; i < cpus.length; ++i) {
- var cpu = cpus[i];
- var track = new tracks.TimelineCpuTrack();
- track.heading = 'CPU ' + cpu.cpuNumber + ':';
- track.cpu = cpu;
- this.addTrack_(track);
- }
-
- // Get a sorted list of processes.
- var processes = this.model_.getAllProcesses();
- processes.sort(tracing.TimelineProcess.compare);
-
- for (var i = 0; i < processes.length; ++i) {
- var process = processes[i];
- var track = new tracks.TimelineProcessTrack();
- track.process = process;
- this.addTrack_(track);
- }
- }
- }
- };
-
- return {
- TimelineModelTrack: TimelineModelTrack
- };
- });
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- 'use strict';
-
- base.requireStylesheet('tracks.timeline_viewport_track');
-
- base.require('tracks.timeline_track');
- base.require('tracks.timeline_canvas_based_track');
- base.require('ui');
-
- base.exportTo('tracks', function() {
-
- /**
- * A track that displays the viewport size and scale.
- * @constructor
- * @extends {CanvasBasedTrack}
- */
-
- var TimelineViewportTrack = base.ui.define(tracks.TimelineCanvasBasedTrack);
-
- var logOf10 = Math.log(10);
- function log10(x) {
- return Math.log(x) / logOf10;
- }
-
- TimelineViewportTrack.prototype = {
-
- __proto__: tracks.TimelineCanvasBasedTrack.prototype,
-
- decorate: function() {
- this.classList.add('timeline-viewport-track');
- this.strings_secs_ = [];
- this.strings_msecs_ = [];
- this.addEventListener('mousedown', this.onMouseDown);
- },
-
- onMouseDown: function(e) {
- if (e.button != 0)
- return;
- this.placeAndBeginDraggingMarker(e.clientX);
- },
-
-
- placeAndBeginDraggingMarker: function(clientX) {
- var viewX = clientX - this.canvasContainer_.offsetLeft;
- var worldX = this.viewport_.xViewToWorld(viewX);
- var marker = this.viewport_.findMarkerNear(worldX, 6);
- var createdMarker = false;
- var movedMarker = false;
- if (!marker) {
- marker = this.viewport_.addMarker(worldX);
- createdMarker = true;
- }
- marker.selected = true;
-
- var that = this;
- var onMouseMove = function(e) {
- var viewX = e.clientX - that.canvasContainer_.offsetLeft;
- var worldX = that.viewport_.xViewToWorld(viewX);
- marker.positionWorld = worldX;
- movedMarker = true;
- };
-
- var onMouseUp = function(e) {
- marker.selected = false;
- if (!movedMarker && !createdMarker)
- that.viewport_.removeMarker(marker);
- document.removeEventListener('mouseup', onMouseUp);
- document.removeEventListener('mousemove', onMouseMove);
- };
-
- document.addEventListener('mouseup', onMouseUp);
- document.addEventListener('mousemove', onMouseMove);
- },
-
- drawLine_: function(ctx, x1, y1, x2, y2, color) {
- ctx.beginPath();
- ctx.moveTo(x1, y1);
- ctx.lineTo(x2, y2);
- ctx.closePath();
- ctx.strokeStyle = color;
- ctx.stroke();
- },
-
- drawArrow_: function(ctx, x1, y1, x2, y2, arrowWidth, color) {
-
- this.drawLine_(ctx, x1, y1, x2, y2, color);
-
- var dx = x2 - x1;
- var dy = y2 - y1;
- var len = Math.sqrt(dx * dx + dy * dy);
- var perc = (len - 10) / len;
- var bx = x1 + perc * dx;
- var by = y1 + perc * dy;
- var ux = dx / len;
- var uy = dy / len;
- var ax = uy * arrowWidth;
- var ay = -ux * arrowWidth;
-
- ctx.beginPath();
- ctx.fillStyle = color;
- ctx.moveTo(bx + ax, by + ay);
- ctx.lineTo(x2, y2);
- ctx.lineTo(bx - ax, by - ay);
- ctx.lineTo(bx + ax, by + ay);
- ctx.closePath();
- ctx.fill();
- },
-
- redraw: function() {
- var ctx = this.ctx_;
- var canvasW = this.canvas_.width;
- var canvasH = this.canvas_.height;
-
- ctx.clearRect(0, 0, canvasW, canvasH);
-
- // Culling parametrs.
- var vp = this.viewport_;
- var pixWidth = vp.xViewVectorToWorld(1);
- var viewLWorld = vp.xViewToWorld(0);
- var viewRWorld = vp.xViewToWorld(canvasW);
-
- var measurements = this.classList.contains('timeline-viewport' +
- '-track-with-distance-measurements');
-
- var rulerHeight = measurements ? canvasH / 2 : canvasH;
-
- for (var i = 0; i < vp.markers.length; ++i) {
- vp.markers[i].drawTriangle_(ctx, viewLWorld, viewRWorld,
- canvasH, rulerHeight, vp);
- }
-
- var idealMajorMarkDistancePix = 150;
- var idealMajorMarkDistanceWorld =
- vp.xViewVectorToWorld(idealMajorMarkDistancePix);
-
- var majorMarkDistanceWorld;
- var unit;
- var unitDivisor;
- var tickLabels;
-
- // The conservative guess is the nearest enclosing 0.1, 1, 10, 100, etc.
- var conservativeGuess =
- Math.pow(10, Math.ceil(log10(idealMajorMarkDistanceWorld)));
-
- // Once we have a conservative guess, consider things that evenly add up
- // to the conservative guess, e.g. 0.5, 0.2, 0.1 Pick the one that still
- // exceeds the ideal mark distance.
- var divisors = [10, 5, 2, 1];
- for (var i = 0; i < divisors.length; ++i) {
- var tightenedGuess = conservativeGuess / divisors[i];
- if (vp.xWorldVectorToView(tightenedGuess) < idealMajorMarkDistancePix)
- continue;
- majorMarkDistanceWorld = conservativeGuess / divisors[i - 1];
- break;
- }
- var tickLabels = undefined;
- if (majorMarkDistanceWorld < 100) {
- unit = 'ms';
- unitDivisor = 1;
- tickLabels = this.strings_msecs_;
- } else {
- unit = 's';
- unitDivisor = 1000;
- tickLabels = this.strings_secs_;
- }
-
- var numTicksPerMajor = 5;
- var minorMarkDistanceWorld = majorMarkDistanceWorld / numTicksPerMajor;
- var minorMarkDistancePx = vp.xWorldVectorToView(minorMarkDistanceWorld);
-
- var firstMajorMark =
- Math.floor(viewLWorld / majorMarkDistanceWorld) *
- majorMarkDistanceWorld;
-
- var minorTickH = Math.floor(canvasH * 0.25);
-
- ctx.fillStyle = 'rgb(0, 0, 0)';
- ctx.strokeStyle = 'rgb(0, 0, 0)';
- ctx.textAlign = 'left';
- ctx.textBaseline = 'top';
-
- var pixelRatio = window.devicePixelRatio || 1;
- ctx.font = (9 * pixelRatio) + 'px sans-serif';
-
- // Each iteration of this loop draws one major mark
- // and numTicksPerMajor minor ticks.
- //
- // Rendering can't be done in world space because canvas transforms
- // affect line width. So, do the conversions manually.
- for (var curX = firstMajorMark;
- curX < viewRWorld;
- curX += majorMarkDistanceWorld) {
-
- var curXView = Math.floor(vp.xWorldToView(curX));
-
- var unitValue = curX / unitDivisor;
- var roundedUnitValue = Math.floor(unitValue * 100000) / 100000;
-
- if (!tickLabels[roundedUnitValue])
- tickLabels[roundedUnitValue] = roundedUnitValue + ' ' + unit;
- ctx.fillText(tickLabels[roundedUnitValue],
- curXView + 2 * pixelRatio, 0);
- ctx.beginPath();
-
- // Major mark
- ctx.moveTo(curXView, 0);
- ctx.lineTo(curXView, rulerHeight);
-
- // Minor marks
- for (var i = 1; i < numTicksPerMajor; ++i) {
- var xView = Math.floor(curXView + minorMarkDistancePx * i);
- ctx.moveTo(xView, rulerHeight - minorTickH);
- ctx.lineTo(xView, rulerHeight);
- }
-
- ctx.stroke();
- }
-
- // Give distance between directly adjacent markers.
- if (measurements) {
-
- // Divide canvas horizontally between ruler and measurements.
- ctx.moveTo(0, rulerHeight);
- ctx.lineTo(canvasW, rulerHeight);
- ctx.stroke();
-
- // Obtain a sorted array of markers
- var sortedMarkers = vp.markers.slice();
- sortedMarkers.sort(function(a, b) {
- return a.positionWorld_ - b.positionWorld_;
- });
-
- // Distance Variables.
- var displayDistance;
- var unitDivisor;
- var displayTextColor = 'rgb(0,0,0)';
- var measurementsPosY = rulerHeight + 2;
-
- // Arrow Variables.
- var arrowSpacing = 10;
- var arrowColor = 'rgb(128,121,121)';
- var arrowPosY = measurementsPosY + 4;
- var arrowWidthView = 3;
- var spaceForArrowsView = 2 * (arrowWidthView + arrowSpacing);
-
- for (i = 0; i < sortedMarkers.length - 1; i++) {
- var rightMarker = sortedMarkers[i + 1];
- var leftMarker = sortedMarkers[i];
- var distanceBetweenMarkers =
- rightMarker.positionWorld - leftMarker.positionWorld;
- var distanceBetweenMarkersView =
- vp.xWorldVectorToView(distanceBetweenMarkers);
-
- var positionInMiddleOfMarkers = leftMarker.positionWorld +
- distanceBetweenMarkers / 2;
- var positionInMiddleOfMarkersView =
- vp.xWorldToView(positionInMiddleOfMarkers);
-
- // Determine units.
- if (distanceBetweenMarkers < 100) {
- unit = 'ms';
- unitDivisor = 1;
- } else {
- unit = 's';
- unitDivisor = 1000;
- }
- // Calculate display value to print.
- displayDistance = distanceBetweenMarkers / unitDivisor;
- var roundedDisplayDistance =
- Math.abs((Math.floor(displayDistance * 1000) / 1000));
- var textToDraw = roundedDisplayDistance + ' ' + unit;
- var textWidthView = ctx.measureText(textToDraw).width;
- var textWidthWorld = vp.xViewVectorToWorld(textWidthView);
- var spaceForArrowsAndTextView = textWidthView +
- spaceForArrowsView + arrowSpacing;
-
- // Set text positions.
- var textLeft = leftMarker.positionWorld +
- (distanceBetweenMarkers / 2) - (textWidthWorld / 2);
- var textRight = textLeft + textWidthWorld;
- var textPosY = measurementsPosY;
- var textLeftView = vp.xWorldToView(textLeft);
- var textRightView = vp.xWorldToView(textRight);
- var leftMarkerView = vp.xWorldToView(leftMarker.positionWorld);
- var rightMarkerView = vp.xWorldToView(rightMarker.positionWorld);
- var textDrawn = false;
-
- if (spaceForArrowsAndTextView <= distanceBetweenMarkersView) {
- // Print the display distance text.
- ctx.fillStyle = displayTextColor;
- ctx.fillText(textToDraw, textLeftView, textPosY);
- textDrawn = true;
- }
-
- if (spaceForArrowsView <= distanceBetweenMarkersView) {
- var leftArrowStart;
- var rightArrowStart;
- if (textDrawn) {
- leftArrowStart = textLeftView - arrowSpacing;
- rightArrowStart = textRightView + arrowSpacing;
- } else {
- leftArrowStart = positionInMiddleOfMarkersView;
- rightArrowStart = positionInMiddleOfMarkersView;
- }
- // Draw left arrow.
- this.drawArrow_(ctx, leftArrowStart, arrowPosY,
- leftMarkerView, arrowPosY, arrowWidthView, arrowColor);
- // Draw right arrow.
- this.drawArrow_(ctx, rightArrowStart, arrowPosY,
- rightMarkerView, arrowPosY, arrowWidthView, arrowColor);
- }
- }
- }
- },
-
- /**
- * Adds items intersecting a point to a selection.
- * @param {number} vX X location to search at, in viewspace.
- * @param {number} vY Y location to search at, in viewspace.
- * @param {TimelineSelection} selection Selection to which to add hits.
- * @return {boolean} true if a slice was found, otherwise false.
- */
- addIntersectingItemsToSelection: function(vX, vY, selection) {
- // Does nothing. There's nothing interesting to pick on the viewport
- // track.
- },
-
- /**
- * Adds items intersecting the given range to a selection.
- * @param {number} loVX Lower X bound of the interval to search, in
- * viewspace.
- * @param {number} hiVX Upper X bound of the interval to search, in
- * viewspace.
- * @param {number} loVY Lower Y bound of the interval to search, in
- * viewspace.
- * @param {number} hiVY Upper Y bound of the interval to search, in
- * viewspace.
- * @param {TimelineSelection} selection Selection to which to add hits.
- */
- addIntersectingItemsInRangeToSelection: function(
- loVX, hiVX, loY, hiY, selection) {
- // Does nothing. There's nothing interesting to pick on the viewport
- // track.
- },
-
- addAllObjectsMatchingFilterToSelection: function(filter, selection) {
- }
- };
-
- return {
- TimelineViewportTrack: TimelineViewportTrack
- };
- });
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- 'use strict';
-
- /**
- * @fileoverview Interactive visualizaiton of TimelineModel objects
- * based loosely on gantt charts. Each thread in the TimelineModel is given a
- * set of TimelineTracks, one per subrow in the thread. The Timeline class
- * acts as a controller, creating the individual tracks, while TimelineTracks
- * do actual drawing.
- *
- * Visually, the Timeline produces (prettier) visualizations like the following:
- * Thread1: AAAAAAAAAA AAAAA
- * BBBB BB
- * Thread2: CCCCCC CCCCC
- *
- */
- base.requireStylesheet('timeline');
- base.require('event_target');
- base.require('measuring_stick');
- base.require('timeline_filter');
- base.require('timeline_selection');
- base.require('timeline_viewport');
- base.require('tracks.timeline_model_track');
- base.require('tracks.timeline_viewport_track');
- base.require('ui');
-
- base.exportTo('tracing', function() {
-
- var TimelineSelection = tracing.TimelineSelection;
- var TimelineViewport = tracing.TimelineViewport;
-
- function intersectRect_(r1, r2) {
- var results = new Object;
- if (r2.left > r1.right || r2.right < r1.left ||
- r2.top > r1.bottom || r2.bottom < r1.top) {
- return false;
- }
- results.left = Math.max(r1.left, r2.left);
- results.top = Math.max(r1.top, r2.top);
- results.right = Math.min(r1.right, r2.right);
- results.bottom = Math.min(r1.bottom, r2.bottom);
- results.width = (results.right - results.left);
- results.height = (results.bottom - results.top);
- return results;
- }
-
- /**
- * Renders a TimelineModel into a div element, making one
- * TimelineTrack for each subrow in each thread of the model, managing
- * overall track layout, and handling user interaction with the
- * viewport.
- *
- * @constructor
- * @extends {HTMLDivElement}
- */
- var Timeline = base.ui.define('div');
-
- Timeline.prototype = {
- __proto__: HTMLDivElement.prototype,
-
- model_: null,
-
- decorate: function() {
- this.classList.add('timeline');
-
- this.categoryFilter_ = new tracing.TimelineCategoryFilter();
-
- this.viewport_ = new TimelineViewport(this);
-
- // Add the viewport track.
- this.viewportTrack_ = new tracks.TimelineViewportTrack();
- this.viewportTrack_.viewport = this.viewport_;
- this.appendChild(this.viewportTrack_);
-
- this.modelTrackContainer_ = document.createElement('div');
- this.modelTrackContainer_.className = 'timeline-model-track-container';
- this.appendChild(this.modelTrackContainer_);
-
- this.modelTrack_ = new tracks.TimelineModelTrack();
- this.modelTrackContainer_.appendChild(this.modelTrack_);
-
- this.dragBox_ = this.ownerDocument.createElement('div');
- this.dragBox_.className = 'timeline-drag-box';
- this.appendChild(this.dragBox_);
- this.hideDragBox_();
-
- this.bindEventListener_(document, 'keypress', this.onKeypress_, this);
- this.bindEventListener_(document, 'keydown', this.onKeydown_, this);
- this.bindEventListener_(document, 'keyup', this.onKeyup_, this);
- this.bindEventListener_(document, 'mousemove', this.onMouseMove_, this);
- this.bindEventListener_(document, 'mouseup', this.onMouseUp_, this);
-
- this.addEventListener('mousewheel', this.onMouseWheel_);
- this.addEventListener('mousedown', this.onMouseDown_);
- this.addEventListener('dblclick', this.onDblClick_);
-
- this.lastMouseViewPos_ = {x: 0, y: 0};
- this.maxHeadingWidth_ = 0;
-
- this.selection_ = new TimelineSelection();
- },
-
- /**
- * Wraps the standard addEventListener but automatically binds the provided
- * func to the provided target, tracking the resulting closure. When detach
- * is called, these listeners will be automatically removed.
- */
- bindEventListener_: function(object, event, func, target) {
- if (!this.boundListeners_)
- this.boundListeners_ = [];
- var boundFunc = func.bind(target);
- this.boundListeners_.push({object: object,
- event: event,
- boundFunc: boundFunc});
- object.addEventListener(event, boundFunc);
- },
-
- detach: function() {
- this.modelTrack_.detach();
-
- for (var i = 0; i < this.boundListeners_.length; i++) {
- var binding = this.boundListeners_[i];
- binding.object.removeEventListener(binding.event, binding.boundFunc);
- }
- this.boundListeners_ = undefined;
- this.viewport_.detach();
- },
-
- get viewport() {
- return this.viewport_;
- },
-
- get categoryFilter() {
- return this.categoryFilter_;
- },
-
- set categoryFilter(filter) {
- this.categoryFilter_ = filter;
- this.modelTrack_.categoryFilter = filter;
- },
-
- get model() {
- return this.model_;
- },
-
- set model(model) {
- if (!model)
- throw new Error('Model cannot be null');
-
- var modelInstanceChanged = this.model_ != model;
- this.model_ = model;
- this.modelTrack_.model = model;
- this.modelTrack_.viewport = this.viewport_;
- this.modelTrack_.categoryFilter = this.categoryFilter;
- this.viewportTrack_.headingWidth = this.modelTrack_.headingWidth;
-
- // Set up a reasonable viewport.
- if (modelInstanceChanged)
- this.viewport_.setWhenPossible(this.setInitialViewport_.bind(this));
- },
-
- get numVisibleTracks() {
- return this.modelTrack_.numVisibleTracks;
- },
-
- setInitialViewport_: function() {
- var w = this.firstCanvas.width;
- var boost =
- (this.model_.maxTimestamp - this.model_.minTimestamp) * 0.15;
- this.viewport_.xSetWorldRange(this.model_.minTimestamp - boost,
- this.model_.maxTimestamp + boost,
- w);
- },
-
- /**
- * @param {TimelineFilter} filter The filter to use for finding matches.
- * @param {TimelineSelection} selection The selection to add matches to.
- * @return {Array} An array of objects that match the provided
- * TimelineTitleFilter.
- */
- addAllObjectsMatchingFilterToSelection: function(filter, selection) {
- this.modelTrack_.addAllObjectsMatchingFilterToSelection(filter,
- selection);
- },
-
- /**
- * @return {Element} The element whose focused state determines
- * whether to respond to keyboard inputs.
- * Defaults to the parent element.
- */
- get focusElement() {
- if (this.focusElement_)
- return this.focusElement_;
- return this.parentElement;
- },
-
- /**
- * Sets the element whose focus state will determine whether
- * to respond to keybaord input.
- */
- set focusElement(value) {
- this.focusElement_ = value;
- },
-
- get listenToKeys_() {
- if (!this.viewport_.isAttachedToDocument_)
- return false;
- if (this.activeElement instanceof tracing.TimelineFindControl)
- return false;
- if (!this.focusElement_)
- return true;
- if (this.focusElement.tabIndex >= 0)
- return document.activeElement == this.focusElement;
- return true;
- },
-
- onKeypress_: function(e) {
- var vp = this.viewport_;
- if (!this.firstCanvas)
- return;
- if (!this.listenToKeys_)
- return;
- if (document.activeElement.nodeName == 'INPUT')
- return;
- var viewWidth = this.firstCanvas.clientWidth;
- var curMouseV, curCenterW;
- switch (e.keyCode) {
- case 119: // w
- case 44: // ,
- this.zoomBy_(1.5);
- break;
- case 115: // s
- case 111: // o
- this.zoomBy_(1 / 1.5);
- break;
- case 103: // g
- this.onGridToggle_(true);
- break;
- case 71: // G
- this.onGridToggle_(false);
- break;
- case 87: // W
- case 60: // <
- this.zoomBy_(10);
- break;
- case 83: // S
- case 79: // O
- this.zoomBy_(1 / 10);
- break;
- case 97: // a
- vp.panX += vp.xViewVectorToWorld(viewWidth * 0.1);
- break;
- case 100: // d
- case 101: // e
- vp.panX -= vp.xViewVectorToWorld(viewWidth * 0.1);
- break;
- case 65: // A
- vp.panX += vp.xViewVectorToWorld(viewWidth * 0.5);
- break;
- case 68: // D
- vp.panX -= vp.xViewVectorToWorld(viewWidth * 0.5);
- break;
- case 48: // 0
- case 122: // z
- this.setInitialViewport_();
- break;
- case 102: // f
- this.zoomToSelection_();
- break;
- }
- },
-
- onMouseWheel_: function(e) {
- if (e.altKey) {
- var delta = e.wheelDeltaY / 120;
- var zoomScale = Math.pow(1.5, delta);
- this.zoomBy_(zoomScale);
- e.preventDefault();
- }
- },
-
- // Not all keys send a keypress.
- onKeydown_: function(e) {
- if (!this.listenToKeys_)
- return;
- var sel;
- var vp = this.viewport_;
- var viewWidth = this.firstCanvas.clientWidth;
- switch (e.keyCode) {
- case 37: // left arrow
- sel = this.selection.getShiftedSelection(-1);
- if (sel) {
- this.setSelectionAndMakeVisible(sel);
- e.preventDefault();
- } else {
- if (!this.firstCanvas)
- return;
- vp.panX += vp.xViewVectorToWorld(viewWidth * 0.1);
- }
- break;
- case 39: // right arrow
- sel = this.selection.getShiftedSelection(1);
- if (sel) {
- this.setSelectionAndMakeVisible(sel);
- e.preventDefault();
- } else {
- if (!this.firstCanvas)
- return;
- vp.panX -= vp.xViewVectorToWorld(viewWidth * 0.1);
- }
- break;
- case 9: // TAB
- if (this.focusElement.tabIndex == -1) {
- if (e.shiftKey)
- this.selectPrevious_(e);
- else
- this.selectNext_(e);
- e.preventDefault();
- }
- break;
- }
- if (e.shiftKey && this.dragBeginEvent_) {
- var vertical = e.shiftKey;
- if (this.dragBeginEvent_) {
- this.setDragBoxPosition_(this.dragBoxXStart_, this.dragBoxYStart_,
- this.dragBoxXEnd_, this.dragBoxYEnd_, vertical);
- }
- }
- },
-
- onKeyup_: function(e) {
- if (!this.listenToKeys_)
- return;
- if (!e.shiftKey) {
- if (this.dragBeginEvent_) {
- var vertical = e.shiftKey;
- this.setDragBoxPosition_(this.dragBoxXStart_, this.dragBoxYStart_,
- this.dragBoxXEnd_, this.dragBoxYEnd_, vertical);
- }
- }
- },
-
- /**
- * Zoom in or out on the timeline by the given scale factor.
- * @param {integer} scale The scale factor to apply. If <1, zooms out.
- */
- zoomBy_: function(scale) {
- if (!this.firstCanvas)
- return;
- var vp = this.viewport_;
- var viewWidth = this.firstCanvas.clientWidth;
- var pixelRatio = window.devicePixelRatio || 1;
- var curMouseV = this.lastMouseViewPos_.x * pixelRatio;
- var curCenterW = vp.xViewToWorld(curMouseV);
- vp.scaleX = vp.scaleX * scale;
- vp.xPanWorldPosToViewPos(curCenterW, curMouseV, viewWidth);
- },
-
- /**
- * Zoom into the current selection.
- */
- zoomToSelection_: function() {
- if (!this.selection)
- return;
- var range = this.selection.range;
- var worldCenter = range.min + (range.max - range.min) * 0.5;
- var worldRange = (range.max - range.min) * 0.5;
- var boost = worldRange * 0.15;
- this.viewport_.xSetWorldRange(worldCenter - worldRange - boost,
- worldCenter + worldRange + boost,
- this.firstCanvas.width);
- },
-
- get keyHelp() {
- var mod = navigator.platform.indexOf('Mac') == 0 ? 'cmd' : 'ctrl';
- var help = 'Qwerty Controls\n' +
- ' w/s : Zoom in/out (with shift: go faster)\n' +
- ' a/d : Pan left/right\n\n' +
- 'Dvorak Controls\n' +
- ' ,/o : Zoom in/out (with shift: go faster)\n' +
- ' a/e : Pan left/right\n\n' +
- 'Mouse Controls\n' +
- ' drag : Select slices (with ' + mod +
- ': zoom to slices)\n' +
- ' drag + shift : Select all slices vertically\n\n';
-
- if (this.focusElement.tabIndex) {
- help +=
- ' <- : Select previous event on current timeline\n' +
- ' -> : Select next event on current timeline\n';
- } else {
- help += 'General Navigation\n' +
- ' g/General : Shows grid at the start/end of the selected' +
- ' task\n' +
- ' <-,^TAB : Select previous event on current timeline\n' +
- ' ->, TAB : Select next event on current timeline\n';
- }
- help +=
- '\n' +
- 'Alt + Scroll to zoom in/out\n' +
- 'Dbl-click to zoom in; Shift dbl-click to zoom out\n' +
- 'f to zoom into selection\n' +
- 'z to reset zoom and pan to initial view\n';
- return help;
- },
-
- get selection() {
- return this.selection_;
- },
-
- set selection(selection) {
- if (!(selection instanceof TimelineSelection))
- throw new Error('Expected TimelineSelection');
-
- // Clear old selection.
- var i;
- for (i = 0; i < this.selection_.length; i++)
- this.selection_[i].selected = false;
-
- this.selection_ = selection;
-
- base.dispatchSimpleEvent(this, 'selectionChange');
- for (i = 0; i < this.selection_.length; i++)
- this.selection_[i].selected = true;
- this.viewport_.dispatchChangeEvent(); // Triggers a redraw.
- },
-
- setSelectionAndMakeVisible: function(selection, zoomAllowed) {
- if (!(selection instanceof TimelineSelection))
- throw new Error('Expected TimelineSelection');
- this.selection = selection;
- var range = this.selection.range;
- var size = this.viewport_.xWorldVectorToView(range.max - range.min);
- if (zoomAllowed && size < 50) {
- var worldCenter = range.min + (range.max - range.min) * 0.5;
- var worldRange = (range.max - range.min) * 5;
- this.viewport_.xSetWorldRange(worldCenter - worldRange * 0.5,
- worldCenter + worldRange * 0.5,
- this.firstCanvas.width);
- return;
- }
-
- this.viewport_.xPanWorldRangeIntoView(range.min, range.max,
- this.firstCanvas.width);
- },
-
- get firstCanvas() {
- if (this.viewportTrack_)
- return this.viewportTrack_.firstCanvas;
- if (this.modelTrack_)
- return this.modelTrack_.firstCanvas;
- return undefined;
- },
-
- hideDragBox_: function() {
- this.dragBox_.style.left = '-1000px';
- this.dragBox_.style.top = '-1000px';
- this.dragBox_.style.width = 0;
- this.dragBox_.style.height = 0;
- },
-
- setDragBoxPosition_: function(xStart, yStart, xEnd, yEnd, vertical) {
- var loY;
- var hiY;
- var loX = Math.min(xStart, xEnd);
- var hiX = Math.max(xStart, xEnd);
- var modelTrackRect = this.modelTrack_.getBoundingClientRect();
-
- if (vertical) {
- loY = modelTrackRect.top;
- hiY = modelTrackRect.bottom;
- } else {
- loY = Math.min(yStart, yEnd);
- hiY = Math.max(yStart, yEnd);
- }
-
- var dragRect = {left: loX, top: loY, width: hiX - loX, height: hiY - loY};
- dragRect.right = dragRect.left + dragRect.width;
- dragRect.bottom = dragRect.top + dragRect.height;
- var modelTrackContainerRect =
- this.modelTrackContainer_.getBoundingClientRect();
- var clipRect = {
- left: modelTrackContainerRect.left,
- top: modelTrackContainerRect.top,
- right: modelTrackContainerRect.right,
- bottom: modelTrackContainerRect.bottom,
- };
- var trackTitleWidth = parseInt(this.modelTrack_.headingWidth);
- clipRect.left = clipRect.left + trackTitleWidth;
-
- var finalDragBox = intersectRect_(clipRect, dragRect);
-
- this.dragBox_.style.left = finalDragBox.left + 'px';
- this.dragBox_.style.width = finalDragBox.width + 'px';
- this.dragBox_.style.top = finalDragBox.top + 'px';
- this.dragBox_.style.height = finalDragBox.height + 'px';
-
- var pixelRatio = window.devicePixelRatio || 1;
- var canv = this.firstCanvas;
- var loWX = this.viewport_.xViewToWorld(
- (loX - canv.offsetLeft) * pixelRatio);
- var hiWX = this.viewport_.xViewToWorld(
- (hiX - canv.offsetLeft) * pixelRatio);
-
- var roundedDuration = Math.round((hiWX - loWX) * 100) / 100;
- this.dragBox_.textContent = roundedDuration + 'ms';
-
- var e = new base.Event('selectionChanging');
- e.loWX = loWX;
- e.hiWX = hiWX;
- this.dispatchEvent(e);
- },
-
- onGridToggle_: function(left) {
- var tb;
- if (left)
- tb = this.selection_.range.min;
- else
- tb = this.selection_.range.max;
-
- // Shift the timebase left until its just left of minTimestamp.
- var numInterfvalsSinceStart = Math.ceil((tb - this.model_.minTimestamp) /
- this.viewport_.gridStep_);
- this.viewport_.gridTimebase = tb -
- (numInterfvalsSinceStart + 1) * this.viewport_.gridStep_;
- this.viewport_.gridEnabled = true;
- },
-
- isChildOfThis_: function(el) {
- if (el == this)
- return;
-
- var isChildOfThis = false;
- var cur = el;
- while (cur.parentNode) {
- if (cur == this)
- return true;
- cur = cur.parentNode;
- }
- return false;
- },
-
- onMouseDown_: function(e) {
- if (e.button !== 0)
- return;
-
- if (e.shiftKey) {
- this.viewportTrack_.placeAndBeginDraggingMarker(e.clientX);
- return;
- }
-
- var canv = this.firstCanvas;
- var rect = this.modelTrack_.getBoundingClientRect();
- var canvRect = this.firstCanvas.getBoundingClientRect();
-
- var inside = rect &&
- e.clientX >= rect.left &&
- e.clientX < rect.right &&
- e.clientY >= rect.top &&
- e.clientY < rect.bottom &&
- e.clientX >= canvRect.left &&
- e.clientX < canvRect.right;
-
- if (!inside)
- return;
-
- var pos = {
- x: e.clientX - canv.offsetLeft,
- y: e.clientY - canv.offsetTop
- };
-
- var wX = this.viewport_.xViewToWorld(pos.x);
-
- this.dragBeginEvent_ = e;
- e.preventDefault();
- if (document.activeElement)
- document.activeElement.blur();
- if (this.focusElement.tabIndex >= 0)
- this.focusElement.focus();
- },
-
- onMouseMove_: function(e) {
- if (!this.firstCanvas)
- return;
- var canv = this.firstCanvas;
- var pos = {
- x: e.clientX - canv.offsetLeft,
- y: e.clientY - canv.offsetTop
- };
-
- // Remember position. Used during keyboard zooming.
- this.lastMouseViewPos_ = pos;
-
- // Update the drag box
- if (this.dragBeginEvent_) {
- this.dragBoxXStart_ = this.dragBeginEvent_.clientX;
- this.dragBoxXEnd_ = e.clientX;
- this.dragBoxYStart_ = this.dragBeginEvent_.clientY;
- this.dragBoxYEnd_ = e.clientY;
- var vertical = e.shiftKey;
- this.setDragBoxPosition_(this.dragBoxXStart_, this.dragBoxYStart_,
- this.dragBoxXEnd_, this.dragBoxYEnd_, vertical);
- }
- },
-
- onMouseUp_: function(e) {
- var i;
- if (this.dragBeginEvent_) {
- // Stop the dragging.
- this.hideDragBox_();
- var eDown = this.dragBeginEvent_;
- this.dragBeginEvent_ = null;
-
- // Figure out extents of the drag.
- var loY;
- var hiY;
- var loX = Math.min(eDown.clientX, e.clientX);
- var hiX = Math.max(eDown.clientX, e.clientX);
- var tracksContainer = this.modelTrackContainer_.getBoundingClientRect();
- var topBoundary = tracksContainer.height;
- var vertical = e.shiftKey;
- if (vertical) {
- var modelTrackRect = this.modelTrack_.getBoundingClientRect();
- loY = modelTrackRect.top;
- hiY = modelTrackRect.bottom;
- } else {
- loY = Math.min(eDown.clientY, e.clientY);
- hiY = Math.max(eDown.clientY, e.clientY);
- }
-
- // Convert to worldspace.
- var canv = this.firstCanvas;
- var loVX = loX - canv.offsetLeft;
- var hiVX = hiX - canv.offsetLeft;
-
- // Figure out what has been hit.
- var selection = new TimelineSelection();
- this.modelTrack_.addIntersectingItemsInRangeToSelection(
- loVX, hiVX, loY, hiY, selection);
-
- // Activate the new selection, and zoom if ctrl key held down.
- this.selection = selection;
- var isMac = navigator.platform.indexOf('Mac') == 0;
- if ((isMac && e.metaKey) || (!isMac && e.ctrlKey)) {
- this.zoomToSelection_();
- }
- }
- },
-
- onDblClick_: function(e) {
- var modelTrackContainerRect =
- this.modelTrackContainer_.getBoundingClientRect();
- var clipBounds = {
- left: modelTrackContainerRect.left,
- right: modelTrackContainerRect.right,
- };
- var trackTitleWidth = parseInt(this.modelTrack_.headingWidth);
- clipBounds.left = clipBounds.left + trackTitleWidth;
-
- if (e.clientX < clipBounds.left || e.clientX > clipBounds.right)
- return;
-
- var canv = this.firstCanvas;
-
- var scale = 4;
- if (e.shiftKey)
- scale = 1 / scale;
- this.zoomBy_(scale);
- e.preventDefault();
- }
- };
-
- /**
- * The TimelineModel being viewed by the timeline
- * @type {TimelineModel}
- */
- base.defineProperty(Timeline, 'model', base.PropertyKind.JS);
-
- return {
- Timeline: Timeline
- };
- });
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- 'use strict';
-
- /**
- * @fileoverview TimelineAnalysis summarizes info about the selected slices
- * to the analysis panel.
- */
- base.require('ui');
- base.requireStylesheet('timeline_analysis');
- base.exportTo('tracing', function() {
-
- var AnalysisResults = base.ui.define('div');
-
- AnalysisResults.prototype = {
- __proto__: HTMLDivElement.prototype,
-
- decorate: function() {
- },
-
- tsRound_: function(ts) {
- return Math.round(ts * 1000.0) / 1000.0;
- },
-
- appendElement_: function(parent, tagName, opt_text) {
- var n = parent.ownerDocument.createElement(tagName);
- parent.appendChild(n);
- if (opt_text != undefined)
- n.textContent = opt_text;
- return n;
- },
-
- appendText_: function(parent, text) {
- var textElement = parent.ownerDocument.createTextNode(text);
- parent.appendChild(textNode);
- return textNode;
- },
-
- appendTableCell_: function(table, row, cellnum, text) {
- var td = this.appendElement_(row, 'td', text);
- td.className = table.className + '-col-' + cellnum;
- return td;
- },
-
- appendTableCellWithTooltip_: function(table, row, cellnum, text, tooltip) {
- if (tooltip) {
- var td = this.appendElement_(row, 'td');
- td.className = table.className + '-col-' + cellnum;
- var span = this.appendElement_(td, 'span', text);
- span.className = 'tooltip';
- span.title = tooltip;
- return td;
- } else {
- this.appendTableCell_(table, row, cellnum, text);
- }
- },
-
- /**
- * Adds a table with the given className.
- * @return {HTMLTableElement} The newly created table.
- */
- appendTable: function(className, numColumns) {
- var table = this.appendElement_(this, 'table');
- table.className = className + ' timeline-analysis-table';
- table.numColumns = numColumns;
- return table;
- },
-
- /**
- * Creates and appends a row to |table| with a left-aligned |label]
- * header that spans all columns.
- */
- appendTableHeader: function(table, label) {
- var row = this.appendElement_(table, 'tr');
-
- var th = this.appendElement_(row, 'th', label);
- th.className = 'timeline-analysis-table-header';
- },
-
- /**
- * Creates and appends a row to |table| with a left-aligned |label]
- * in the first column and an optional |opt_text| value in the second
- * column.
- */
- appendSummaryRow: function(table, label, opt_text) {
- var row = this.appendElement_(table, 'tr');
- row.className = 'timeline-analysis-table-row';
-
- this.appendTableCell_(table, row, 0, label);
- if (opt_text !== undefined) {
- this.appendTableCell_(table, row, 1, opt_text);
- for (var i = 2; i < table.numColumns; i++)
- this.appendTableCell_(table, row, i, '');
- } else {
- for (var i = 1; i < table.numColumns; i++)
- this.appendTableCell_(table, row, 1, '');
- }
- },
-
- /**
- * Adds a spacing row to spread out results.
- */
- appendSpacingRow: function(table) {
- var row = this.appendElement_(table, 'tr');
- row.className = 'timeline-analysis-table-row';
- for (var i = 0; i < table.numColumns; i++)
- this.appendTableCell_(table, row, i, ' ');
- },
-
- /**
- * Creates and appends a row to |table| with a left-aligned |label]
- * in the first column and a millisecvond |time| value in the second
- * column.
- */
- appendSummaryRowTime: function(table, label, time) {
- this.appendSummaryRow(table, label, this.tsRound_(time) + ' ms');
- },
-
- /**
- * Creates and appends a row to |table| that summarizes one or more slices,
- * or one or more counters.
- * The row has a left-aligned |label| in the first column, the |duration|
- * of the data in the second, the number of |occurrences| in the third.
- * @param {object} opt_statistics May be undefined, or an object which
- * contains calculated staistics containing min/max/avg for slices, or
- * min/max/avg/start/end for counters.
- */
- appendDataRow: function(
- table, label, opt_duration, opt_occurences, opt_statistics) {
-
- var tooltip = undefined;
- if (opt_statistics) {
- tooltip = 'Min Duration:\u0009' + this.tsRound_(opt_statistics.min) +
- ' ms \u000DMax Duration:\u0009' +
- this.tsRound_(opt_statistics.max) +
- ' ms \u000DAvg Duration:\u0009' +
- this.tsRound_(opt_statistics.avg) + ' ms (\u03C3 = ' +
- this.tsRound_(opt_statistics.avg_stddev) + ')';
-
- if (opt_statistics.start) {
- tooltip += '\u000DStart Time:\u0009' +
- this.tsRound_(opt_statistics.start) + ' ms';
- }
- if (opt_statistics.end) {
- tooltip += '\u000DEnd Time:\u0009' +
- this.tsRound_(opt_statistics.end) + ' ms';
- }
- if (opt_statistics.frequency && opt_statistics.frequency_stddev) {
- tooltip += '\u000DFrequency:\u0009' +
- this.tsRound_(opt_statistics.frequency) +
- ' occurrences/s (\u03C3 = ' +
- this.tsRound_(opt_statistics.frequency_stddev) + ')';
- }
- }
-
- var row = this.appendElement_(table, 'tr');
- row.className = 'timeline-analysis-table-row';
-
- this.appendTableCellWithTooltip_(table, row, 0, label, tooltip);
-
- if (opt_duration !== undefined) {
- this.appendTableCellWithTooltip_(table, row, 1,
- this.tsRound_(opt_duration) + ' ms', tooltip);
- } else {
- this.appendTableCell_(table, row, 1, '');
- }
-
- if (opt_occurences !== undefined) {
- this.appendTableCellWithTooltip_(table, row, 2,
- String(opt_occurences) + ' occurrences', tooltip);
-
- } else {
- this.appendTableCell_(table, row, 2, '');
- }
- }
- };
-
- /**
- * Analyzes the selection, outputting the analysis results into the provided
- * results object.
- *
- * @param {AnalysisResults} results Where the analysis is placed.
- * @param {TimelineSelection} selection What to analyze.
- */
- function analyzeSelection(results, selection) {
-
- var sliceHits = selection.getSliceHitsAsSelection();
- var counterSampleHits = selection.getCounterSampleHitsAsSelection();
-
- if (sliceHits.length == 1) {
- var slice = sliceHits[0].slice;
- var table = results.appendTable('timeline-analysis-slice-table', 2);
-
- results.appendTableHeader(table, 'Selected slice:');
- results.appendSummaryRow(table, 'Title', slice.title);
-
- if (slice.category)
- results.appendSummaryRow(table, 'Category', slice.category);
-
- results.appendSummaryRowTime(table, 'Start', slice.start);
- results.appendSummaryRowTime(table, 'Duration', slice.duration);
-
- if (slice.durationInUserTime) {
- results.appendSummaryRowTime(
- table, 'Duration (U)', slice.durationInUserTime);
- }
-
- var n = 0;
- for (var argName in slice.args) {
- n += 1;
- }
- if (n > 0) {
- results.appendSummaryRow(table, 'Args');
- for (var argName in slice.args) {
- var argVal = slice.args[argName];
- // TODO(sleffler) use span instead?
- results.appendSummaryRow(table, ' ' + argName, argVal);
- }
- }
- } else if (sliceHits.length > 1) {
- var tsLo = sliceHits.range.min;
- var tsHi = sliceHits.range.max;
-
- // compute total sliceHits duration
- var titles = sliceHits.map(function(i) { return i.slice.title; });
-
- var numTitles = 0;
- var slicesByTitle = {};
- for (var i = 0; i < sliceHits.length; i++) {
- var slice = sliceHits[i].slice;
- if (!slicesByTitle[slice.title]) {
- slicesByTitle[slice.title] = {
- slices: []
- };
- numTitles++;
- }
- slicesByTitle[slice.title].slices.push(slice);
- }
-
- var table;
- table = results.appendTable('timeline-analysis-slices-table', 3);
- results.appendTableHeader(table, 'Slices:');
-
- var totalDuration = 0;
- for (var sliceGroupTitle in slicesByTitle) {
- var sliceGroup = slicesByTitle[sliceGroupTitle];
- var duration = 0;
- var avg = 0;
- var startOfFirstOccurrence = Number.MAX_VALUE;
- var startOfLastOccurrence = -Number.MAX_VALUE;
- var frequencyDetails = undefined;
- var min = Number.MAX_VALUE;
- var max = -Number.MAX_VALUE;
- for (var i = 0; i < sliceGroup.slices.length; i++) {
- duration += sliceGroup.slices[i].duration;
- startOfFirstOccurrence = Math.min(sliceGroup.slices[i].start,
- startOfFirstOccurrence);
- startOfLastOccurrence = Math.max(sliceGroup.slices[i].start,
- startOfLastOccurrence);
- min = Math.min(sliceGroup.slices[i].duration, min);
- max = Math.max(sliceGroup.slices[i].duration, max);
- }
-
- totalDuration += duration;
-
- if (sliceGroup.slices.length == 0)
- avg = 0;
- avg = duration / sliceGroup.slices.length;
-
- var details = {min: min,
- max: max,
- avg: avg,
- avg_stddev: undefined,
- frequency: undefined,
- frequency_stddev: undefined};
-
- // Compute the stddev of the slice durations.
- var sumOfSquaredDistancesToMean = 0;
- for (var i = 0; i < sliceGroup.slices.length; i++) {
- var signedDistance = details.avg - sliceGroup.slices[i].duration;
- sumOfSquaredDistancesToMean += signedDistance * signedDistance;
- }
-
- details.avg_stddev = Math.sqrt(
- sumOfSquaredDistancesToMean / (sliceGroup.slices.length - 1));
-
- // We require at least 3 samples to compute the stddev.
- var elapsed = startOfLastOccurrence - startOfFirstOccurrence;
- if (sliceGroup.slices.length > 2 && elapsed > 0) {
- var numDistances = sliceGroup.slices.length - 1;
- details.frequency = (1000 * numDistances) / elapsed;
-
- // Compute the stddev.
- sumOfSquaredDistancesToMean = 0;
- for (var i = 1; i < sliceGroup.slices.length; i++) {
- var currentFrequency = 1000 /
- (sliceGroup.slices[i].start - sliceGroup.slices[i - 1].start);
- var signedDistance = details.frequency - currentFrequency;
- sumOfSquaredDistancesToMean += signedDistance * signedDistance;
- }
-
- details.frequency_stddev = Math.sqrt(
- sumOfSquaredDistancesToMean / (numDistances - 1));
- }
- results.appendDataRow(
- table, sliceGroupTitle, duration, sliceGroup.slices.length,
- details);
- }
- results.appendDataRow(table, '*Totals', totalDuration, sliceHits.length);
- results.appendSpacingRow(table);
- results.appendSummaryRowTime(table, 'Selection start', tsLo);
- results.appendSummaryRowTime(table, 'Selection extent', tsHi - tsLo);
- }
-
- if (counterSampleHits.length == 1) {
- var hit = counterSampleHits[0];
- var ctr = hit.counter;
- var sampleIndex = hit.sampleIndex;
- var values = [];
- for (var i = 0; i < ctr.numSeries; ++i)
- values.push(ctr.samples[ctr.numSeries * sampleIndex + i]);
-
- var table = results.appendTable('timeline-analysis-counter-table', 2);
- results.appendTableHeader(table, 'Selected counter:');
- results.appendSummaryRow(table, 'Title', ctr.name);
- results.appendSummaryRowTime(
- table, 'Timestamp', ctr.timestamps[sampleIndex]);
-
- for (var i = 0; i < ctr.numSeries; i++)
- results.appendSummaryRow(table, ctr.seriesNames[i], values[i]);
- } else if (counterSampleHits.length > 1) {
- var hitsByCounter = {};
- for (var i = 0; i < counterSampleHits.length; i++) {
- var ctr = counterSampleHits[i].counter;
- if (!hitsByCounter[ctr.guid])
- hitsByCounter[ctr.guid] = [];
- hitsByCounter[ctr.guid].push(counterSampleHits[i]);
- }
-
- var table = results.appendTable('timeline-analysis-counter-table', 7);
- results.appendTableHeader(table, 'Counters:');
- for (var id in hitsByCounter) {
- var hits = hitsByCounter[id];
- var ctr = hits[0].counter;
- var sampleIndices = [];
- for (var i = 0; i < hits.length; i++)
- sampleIndices.push(hits[i].sampleIndex);
-
- var stats = ctr.getSampleStatistics(sampleIndices);
- for (var i = 0; i < stats.length; i++) {
- results.appendDataRow(
- table, ctr.name + ': ' + ctr.seriesNames[i], undefined,
- undefined, stats[i]);
- }
- }
- }
- }
-
- var TimelineAnalysisView = base.ui.define('div');
-
- TimelineAnalysisView.prototype = {
- __proto__: HTMLDivElement.prototype,
-
- decorate: function() {
- this.className = 'timeline-analysis';
- },
-
- set selection(selection) {
- this.textContent = '';
- var results = new AnalysisResults();
- analyzeSelection(results, selection);
- this.appendChild(results);
- }
- };
-
- return {
- TimelineAnalysisView: TimelineAnalysisView,
- analyzeSelection_: analyzeSelection
- };
- });
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
-
- /**
- * @fileoverview Implements an element that is hidden by default, but
- * when shown, dims and (attempts to) disable the main document.
- *
- * You can turn any div into an overlay. Note that while an
- * overlay element is shown, its parent is changed. Hiding the overlay
- * restores its original parentage.
- *
- */
- base.requireStylesheet('overlay');
- base.require('ui');
- base.require('event_target');
- base.exportTo('tracing', function() {
- /**
- * Manages a full-window div that darkens the window, disables
- * input, and hosts the currently-visible overlays. You shouldn't
- * have to instantiate this directly --- it gets set automatically.
- * @param {Object=} opt_propertyBag Optional properties.
- * @constructor
- * @extends {HTMLDivElement}
- */
- var OverlayRoot = base.ui.define('div');
- OverlayRoot.prototype = {
- __proto__: HTMLDivElement.prototype,
- decorate: function() {
- this.classList.add('overlay-root');
- this.visible = false;
-
- this.contentHost = this.ownerDocument.createElement('div');
- this.contentHost.classList.add('content-host');
-
- this.tabCatcher = this.ownerDocument.createElement('span');
- this.tabCatcher.tabIndex = 0;
-
- this.appendChild(this.contentHost);
-
- this.onKeydownBoundToThis_ = this.onKeydown_.bind(this);
- this.onFocusInBoundToThis_ = this.onFocusIn_.bind(this);
- this.addEventListener('mousedown', this.onMousedown_.bind(this));
- },
-
- /**
- * Adds an overlay, attaching it to the contentHost so that it is visible.
- */
- showOverlay: function(overlay) {
- // Reparent this to the overlay content host.
- overlay.oldParent_ = overlay.parentNode;
- this.contentHost.appendChild(overlay);
- this.contentHost.appendChild(this.tabCatcher);
-
- // Show the overlay root.
- this.ownerDocument.body.classList.add('disabled-by-overlay');
- this.visible = true;
-
- // Bring overlay into focus.
- overlay.tabIndex = 0;
- var focusElement =
- overlay.querySelector('button, input, list, select, a');
- if (!focusElement) {
- focusElement = overlay;
- }
- focusElement.focus();
-
- // Listen to key and focus events to prevent focus from
- // leaving the overlay.
- this.ownerDocument.addEventListener('focusin',
- this.onFocusInBoundToThis_, true);
- overlay.addEventListener('keydown', this.onKeydownBoundToThis_);
- },
-
- /**
- * Clicking outside of the overlay will de-focus the overlay. The
- * next tab will look at the entire document to determine the focus.
- * For certain documents, this can cause focus to "leak" outside of
- * the overlay.
- */
- onMousedown_: function(e) {
- if (e.target == this) {
- e.preventDefault();
- }
- },
-
- /**
- * Prevents forward-tabbing out of the overlay
- */
- onFocusIn_: function(e) {
- if (e.target == this.tabCatcher) {
- window.setTimeout(this.focusOverlay_.bind(this), 0);
- }
- },
-
- focusOverlay_: function() {
- this.contentHost.firstChild.focus();
- },
-
- /**
- * Prevent the user from shift-tabbing backwards out of the overlay.
- */
- onKeydown_: function(e) {
- if (e.keyCode == 9 &&
- e.shiftKey &&
- e.target == this.contentHost.firstChild) {
- e.preventDefault();
- }
- },
-
- /**
- * Hides an overlay, attaching it to its original parent if needed.
- */
- hideOverlay: function(overlay) {
- // hide the overlay root
- this.visible = false;
- this.ownerDocument.body.classList.remove('disabled-by-overlay');
- this.lastFocusOut_ = undefined;
-
- // put the overlay back on its previous parent
- overlay.parentNode.removeChild(this.tabCatcher);
- if (overlay.oldParent_) {
- overlay.oldParent_.appendChild(overlay);
- delete overlay.oldParent_;
- } else {
- this.contentHost.removeChild(overlay);
- }
-
- // remove listeners
- overlay.removeEventListener('keydown', this.onKeydownBoundToThis_);
- this.ownerDocument.removeEventListener('focusin',
- this.onFocusInBoundToThis_);
- }
- };
-
- base.defineProperty(OverlayRoot, 'visible', base.PropertyKind.BOOL_ATTR);
-
- /**
- * Creates a new overlay element. It will not be visible until shown.
- * @param {Object=} opt_propertyBag Optional properties.
- * @constructor
- * @extends {HTMLDivElement}
- */
- var Overlay = base.ui.define('div');
-
- Overlay.prototype = {
- __proto__: HTMLDivElement.prototype,
-
- /**
- * Initializes the overlay element.
- */
- decorate: function() {
- // create the overlay root on this document if its not present
- if (!this.ownerDocument.querySelector('.overlay-root')) {
- var overlayRoot = this.ownerDocument.createElement('div');
- base.ui.decorate(overlayRoot, OverlayRoot);
- this.ownerDocument.body.appendChild(overlayRoot);
- }
-
- this.classList.add('overlay');
- this.visible = false;
- this.defaultClickShouldClose = true;
- this.autoClose = false;
- this.additionalCloseKeyCodes = [];
- this.onKeyDown = this.onKeyDown.bind(this);
- this.onKeyPress = this.onKeyPress.bind(this);
- this.onDocumentClick = this.onDocumentClick.bind(this);
- },
-
- onVisibleChanged_: function() {
- var overlayRoot = this.ownerDocument.querySelector('.overlay-root');
- base.dispatchSimpleEvent(this, 'visibleChange');
- if (this.visible) {
- overlayRoot.showOverlay(this);
- document.addEventListener('keydown', this.onKeyDown, true);
- document.addEventListener('keypress', this.onKeyPress, true);
- document.addEventListener('click', this.onDocumentClick, true);
- } else {
- document.removeEventListener('keydown', this.onKeyDown, true);
- document.removeEventListener('keypress', this.onKeyPress, true);
- document.removeEventListener('click', this.onDocumentClick, true);
- overlayRoot.hideOverlay(this);
- }
- },
-
- onKeyDown: function(e) {
- if (!this.autoClose)
- return;
-
- if (e.keyCode == 27) {
- this.visible = false;
- e.preventDefault();
- return;
- }
- },
-
- onKeyPress: function(e) {
- if (!this.autoClose)
- return;
-
- for (var i = 0; i < this.additionalCloseKeyCodes.length; i++) {
- if (e.keyCode == this.additionalCloseKeyCodes[i]) {
- this.visible = false;
- e.preventDefault();
- return;
- }
- }
- },
-
- onDocumentClick: function(e) {
- if (!this.defaultClickShouldClose)
- return;
- var target = e.target;
- while (target !== null) {
- if (target === this)
- return;
- target = target.parentNode;
- }
- this.visible = false;
- e.preventDefault();
- return;
- }
-
- };
-
- /**
- * Shows and hides the overlay. Note that while visible == true, the overlay
- * element will be tempoarily reparented to another place in the DOM.
- */
- base.defineProperty(Overlay, 'visible', base.PropertyKind.BOOL_ATTR,
- Overlay.prototype.onVisibleChanged_);
- base.defineProperty(Overlay, 'defaultClickShouldClose',
- base.PropertyKind.BOOL_ATTR);
-
- return {
- Overlay: Overlay
- };
- });
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- 'use strict';
-
- /**
- * @fileoverview TimelineCategoryFilterButton extracts categories from the model
- * and shows/hides them based on settings.
- */
- base.require('timeline_filter');
- base.require('overlay');
- base.exportTo('tracing', function() {
-
- var TimelineCategoryFilterDialog = base.ui.define('div');
-
- TimelineCategoryFilterDialog.prototype = {
- __proto__: tracing.Overlay.prototype,
-
- decorate: function() {
- tracing.Overlay.prototype.decorate.call(this);
-
- this.className = 'timeline-view-category-filter-overlay';
- this.autoClose = true;
-
- var containerEl = document.createElement('div');
- containerEl.className = 'category-filter-dialog';
- containerEl.textContent = 'Select active categories:';
- this.formEl_ = document.createElement('form');
- this.formEl_.className = 'category-filter-dialog-form';
- containerEl.appendChild(this.formEl_);
- this.appendChild(containerEl);
-
- this.addEventListener('visibleChange', this.onVisibleChange_.bind(this));
- },
-
- get model() {
- return this.model_;
- },
-
- set model(m) {
- this.model_ = m;
- },
-
- get settings() {
- return this.settings_;
- },
-
- set settings(s) {
- this.settings_ = s;
- },
-
- set settingUpdatedCallback(c) {
- this.settingUpdatedCallback_ = c;
- },
-
- onVisibleChange_: function() {
- if (this.visible) {
- this.updateForm_();
- }
- },
-
- updateForm_: function() {
- // Clear and update the form every time the dialog is shown, in case
- // the model or settings have changed with new categories.
- this.formEl_.innerHTML = ''; // Clear old categories
- var categories = this.model_.categories;
- categories.concat(this.settings_.keys('categories'));
- for (var i = 0; i < categories.length; i++) {
- var category = categories[i];
- var inputEl = document.createElement('input');
- inputEl.type = 'checkbox';
- inputEl.id = inputEl.value = category;
- inputEl.checked =
- this.settings_.get(category, 'true', 'categories') == 'true';
- inputEl.onchange = this.updateSetting_.bind(this);
- var labelEl = document.createElement('label');
- labelEl.textContent = category;
- labelEl.setAttribute('for', category);
- this.formEl_.appendChild(inputEl);
- this.formEl_.appendChild(labelEl);
- this.formEl_.appendChild(document.createElement('br'));
- }
- },
-
- updateSetting_: function(e) {
- var checkbox = e.target;
- this.settings_.set(checkbox.value, checkbox.checked, 'categories');
- this.settingUpdatedCallback_();
- }
- };
-
-
- return {
- TimelineCategoryFilterDialog: TimelineCategoryFilterDialog
- };
- });
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- 'use strict';
-
- /**
- * @fileoverview TimelineFindControl and TimelineFindController.
- */
- base.require('timeline');
- base.require('timeline_filter');
- base.require('overlay');
- base.exportTo('tracing', function() {
-
- /**
- * TimelineFindControl
- * @constructor
- * @extends {tracing.Overlay}
- */
- var TimelineFindControl = base.ui.define('div');
-
- TimelineFindControl.prototype = {
- __proto__: tracing.Overlay.prototype,
-
- decorate: function() {
- tracing.Overlay.prototype.decorate.call(this);
-
- this.className = 'timeline-find-control';
-
- this.hitCountEl_ = document.createElement('div');
- this.hitCountEl_.className = 'hit-count-label';
- this.hitCountEl_.textContent = '1 of 7';
-
- var findPreviousBn = document.createElement('div');
- findPreviousBn.className = 'timeline-button find-previous';
- findPreviousBn.textContent = '\u2190';
- findPreviousBn.addEventListener('click', function() {
- this.controller.findPrevious();
- this.updateHitCountEl_();
- }.bind(this));
-
- var findNextBn = document.createElement('div');
- findNextBn.className = 'timeline-button find-next';
- findNextBn.textContent = '\u2192';
- findNextBn.addEventListener('click', function() {
- this.controller.findNext();
- this.updateHitCountEl_();
- }.bind(this));
-
- // Filter input element.
- this.filterEl_ = document.createElement('input');
- this.filterEl_.type = 'input';
-
- this.filterEl_.addEventListener('input', function(e) {
- this.controller.filterText = this.filterEl_.value;
- this.updateHitCountEl_();
- }.bind(this));
-
- this.filterEl_.addEventListener('keydown', function(e) {
- if (e.keyCode == 13) {
- findNextBn.click();
- } else if (e.keyCode == 27) {
- this.filterEl_.blur();
- this.updateHitCountEl_();
- }
- }.bind(this));
-
- this.filterEl_.addEventListener('blur', function(e) {
- this.updateHitCountEl_();
- }.bind(this));
-
- this.filterEl_.addEventListener('focus', function(e) {
- this.updateHitCountEl_();
- }.bind(this));
-
- // Attach everything.
- this.appendChild(this.filterEl_);
-
- this.appendChild(findPreviousBn);
- this.appendChild(findNextBn);
- this.appendChild(this.hitCountEl_);
-
- this.updateHitCountEl_();
- },
-
- get controller() {
- return this.controller_;
- },
-
- set controller(c) {
- this.controller_ = c;
- this.updateHitCountEl_();
- },
-
- focus: function() {
- this.filterEl_.selectionStart = 0;
- this.filterEl_.selectionEnd = this.filterEl_.value.length;
- this.filterEl_.focus();
- },
-
- updateHitCountEl_: function() {
- if (!this.controller || document.activeElement != this.filterEl_) {
- this.hitCountEl_.textContent = '';
- return;
- }
- var i = this.controller.currentHitIndex;
- var n = this.controller.filterHits.length;
- if (n == 0)
- this.hitCountEl_.textContent = '0 of 0';
- else
- this.hitCountEl_.textContent = (i + 1) + ' of ' + n;
- }
- };
-
- function TimelineFindController() {
- this.timeline_ = undefined;
- this.model_ = undefined;
- this.filterText_ = '';
- this.filterHits_ = new tracing.TimelineSelection();
- this.filterHitsDirty_ = true;
- this.currentHitIndex_ = 0;
- };
-
- TimelineFindController.prototype = {
- __proto__: Object.prototype,
-
- get timeline() {
- return this.timeline_;
- },
-
- set timeline(t) {
- this.timeline_ = t;
- this.filterHitsDirty_ = true;
- },
-
- get filterText() {
- return this.filterText_;
- },
-
- set filterText(f) {
- if (f == this.filterText_)
- return;
- this.filterText_ = f;
- this.filterHitsDirty_ = true;
- this.findNext();
- },
-
- get filterHits() {
- if (this.filterHitsDirty_) {
- this.filterHitsDirty_ = false;
- if (this.timeline_) {
- var filter = new tracing.TimelineTitleFilter(this.filterText);
- this.filterHits_.clear();
- this.timeline.addAllObjectsMatchingFilterToSelection(
- filter, this.filterHits_);
- this.currentHitIndex_ = this.filterHits_.length - 1;
- } else {
- this.filterHits_.clear();
- this.currentHitIndex_ = 0;
- }
- }
- return this.filterHits_;
- },
-
- get currentHitIndex() {
- return this.currentHitIndex_;
- },
-
- find_: function(dir) {
- if (!this.timeline)
- return;
-
- var N = this.filterHits.length;
- this.currentHitIndex_ = this.currentHitIndex_ + dir;
-
- if (this.currentHitIndex_ < 0) this.currentHitIndex_ = N - 1;
- if (this.currentHitIndex_ >= N) this.currentHitIndex_ = 0;
-
- if (this.currentHitIndex_ < 0 || this.currentHitIndex_ >= N) {
- this.timeline.selection = new tracing.TimelineSelection();
- return;
- }
-
- // We allow the zoom level to change on the first hit level. But, when
- // then cycling through subsequent changes, restrict it to panning.
- var zoomAllowed = this.currentHitIndex_ == 0;
- var subSelection = this.filterHits.subSelection(this.currentHitIndex_);
- this.timeline.setSelectionAndMakeVisible(subSelection, zoomAllowed);
- },
-
- findNext: function() {
- this.find_(1);
- },
-
- findPrevious: function() {
- this.find_(-1);
- }
- };
-
- return {
- TimelineFindControl: TimelineFindControl,
- TimelineFindController: TimelineFindController
- };
- });
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- /**
- * @fileoverview TraceEventImporter imports TraceEvent-formatted data
- * into the provided timeline model.
- */
- base.require('timeline_model');
- base.require('timeline_color_scheme');
- base.exportTo('tracing', function() {
-
- function TraceEventImporter(model, eventData) {
- this.importPriority = 1;
- this.model_ = model;
-
- if (typeof(eventData) === 'string' || eventData instanceof String) {
- // If the event data begins with a [, then we know it should end with a ].
- // The reason we check for this is because some tracing implementations
- // cannot guarantee that a ']' gets written to the trace file. So, we are
- // forgiving and if this is obviously the case, we fix it up before
- // throwing the string at JSON.parse.
- if (eventData[0] == '[') {
- n = eventData.length;
- if (eventData[n - 1] == '\n') {
- eventData = eventData.substring(0, n - 1);
- n--;
-
- if (eventData[n - 1] == '\r') {
- eventData = eventData.substring(0, n - 1);
- n--;
- }
- }
-
- if (eventData[n - 1] == ',')
- eventData = eventData.substring(0, n - 1);
- if (eventData[n - 1] != ']')
- eventData = eventData + ']';
- }
-
- this.events_ = JSON.parse(eventData);
-
- } else {
- this.events_ = eventData;
- }
-
- // Some trace_event implementations put the actual trace events
- // inside a container. E.g { ... , traceEvents: [ ] }
- // If we see that, just pull out the trace events.
- if (this.events_.traceEvents) {
- this.events_ = this.events_.traceEvents;
- for (fieldName in this.events_) {
- if (fieldName == 'traceEvents')
- continue;
- this.model_.metadata.push({name: fieldName,
- value: this.events_[fieldName]});
- }
- }
-
- // Async events need to be processed durign finalizeEvents
- this.allAsyncEvents_ = [];
- }
-
- /**
- * @return {boolean} Whether obj is a TraceEvent array.
- */
- TraceEventImporter.canImport = function(eventData) {
- // May be encoded JSON. But we dont want to parse it fully yet.
- // Use a simple heuristic:
- // - eventData that starts with [ are probably trace_event
- // - eventData that starts with { are probably trace_event
- // May be encoded JSON. Treat files that start with { as importable by us.
- if (typeof(eventData) === 'string' || eventData instanceof String) {
- return eventData[0] == '{' || eventData[0] == '[';
- }
-
- // Might just be an array of events
- if (eventData instanceof Array && eventData.length && eventData[0].ph)
- return true;
-
- // Might be an object with a traceEvents field in it.
- if (eventData.traceEvents)
- return eventData.traceEvents instanceof Array &&
- eventData.traceEvents[0].ph;
-
- return false;
- };
-
- TraceEventImporter.prototype = {
-
- __proto__: Object.prototype,
-
- /**
- * Helper to process an 'async finish' event, which will close an open slice
- * on a TimelineAsyncSliceGroup object.
- */
- processAsyncEvent: function(index, event) {
- var thread = this.model_.getOrCreateProcess(event.pid).
- getOrCreateThread(event.tid);
- this.allAsyncEvents_.push({
- event: event,
- thread: thread});
- },
-
- /**
- * Helper that creates and adds samples to a TimelineCounter object based on
- * 'C' phase events.
- */
- processCounterEvent: function(event) {
- var ctr_name;
- if (event.id !== undefined)
- ctr_name = event.name + '[' + event.id + ']';
- else
- ctr_name = event.name;
-
- var ctr = this.model_.getOrCreateProcess(event.pid)
- .getOrCreateCounter(event.cat, ctr_name);
- // Initialize the counter's series fields if needed.
- if (ctr.numSeries == 0) {
- for (var seriesName in event.args) {
- ctr.seriesNames.push(seriesName);
- ctr.seriesColors.push(
- tracing.getStringColorId(ctr.name + '.' + seriesName));
- }
- if (ctr.numSeries == 0) {
- this.model_.importErrors.push('Expected counter ' + event.name +
- ' to have at least one argument to use as a value.');
- // Drop the counter.
- delete ctr.parent.counters[ctr.name];
- return;
- }
- }
-
- // Add the sample values.
- ctr.timestamps.push(event.ts / 1000);
- for (var i = 0; i < ctr.numSeries; i++) {
- var seriesName = ctr.seriesNames[i];
- if (event.args[seriesName] === undefined) {
- ctr.samples.push(0);
- continue;
- }
- ctr.samples.push(event.args[seriesName]);
- }
- },
-
- /**
- * Walks through the events_ list and outputs the structures discovered to
- * model_.
- */
- importEvents: function() {
- // Walk through events
- var events = this.events_;
- // Some events cannot be handled until we have done a first pass over the
- // data set. So, accumulate them into a temporary data structure.
- var second_pass_events = [];
- for (var eI = 0; eI < events.length; eI++) {
- var event = events[eI];
- if (event.ph == 'B') {
- var thread = this.model_.getOrCreateProcess(event.pid)
- .getOrCreateThread(event.tid);
- if (!thread.isTimestampValidForBeginOrEnd(event.ts / 1000)) {
- this.model_.importErrors.push(
- 'Timestamps are moving backward.');
- continue;
- }
- thread.beginSlice(event.cat, event.name, event.ts / 1000, event.args);
- } else if (event.ph == 'E') {
- var thread = this.model_.getOrCreateProcess(event.pid)
- .getOrCreateThread(event.tid);
- if (!thread.isTimestampValidForBeginOrEnd(event.ts / 1000)) {
- this.model_.importErrors.push(
- 'Timestamps are moving backward.');
- continue;
- }
- if (!thread.openSliceCount) {
- this.model_.importErrors.push(
- 'E phase event without a matching B phase event.');
- continue;
- }
-
- var slice = thread.endSlice(event.ts / 1000);
- for (var arg in event.args) {
- if (slice.args[arg] !== undefined) {
- this.model_.importErrors.push(
- 'Both the B and E phases of ' + slice.name +
- 'provided values for argument ' + arg + '. ' +
- 'The value of the E phase event will be used.');
- }
- slice.args[arg] = event.args[arg];
- }
-
- } else if (event.ph == 'S') {
- this.processAsyncEvent(eI, event);
- } else if (event.ph == 'F') {
- this.processAsyncEvent(eI, event);
- } else if (event.ph == 'T') {
- this.processAsyncEvent(eI, event);
- } else if (event.ph == 'I') {
- // Treat an Instant event as a duration 0 slice.
- // TimelineSliceTrack's redraw() knows how to handle this.
- var thread = this.model_.getOrCreateProcess(event.pid)
- .getOrCreateThread(event.tid);
- thread.beginSlice(event.cat, event.name, event.ts / 1000, event.args);
- thread.endSlice(event.ts / 1000);
- } else if (event.ph == 'C') {
- this.processCounterEvent(event);
- } else if (event.ph == 'M') {
- if (event.name == 'thread_name') {
- var thread = this.model_.getOrCreateProcess(event.pid)
- .getOrCreateThread(event.tid);
- thread.name = event.args.name;
- } else {
- this.model_.importErrors.push(
- 'Unrecognized metadata name: ' + event.name);
- }
- } else if (event.ph == 's') {
- // NB: toss until there's proper support
- } else if (event.ph == 't') {
- // NB: toss until there's proper support
- } else if (event.ph == 'f') {
- // NB: toss until there's proper support
- } else {
- this.model_.importErrors.push(
- 'Unrecognized event phase: ' + event.ph +
- '(' + event.name + ')');
- }
- }
- },
-
- /**
- * Called by the TimelineModel after all other importers have imported their
- * events.
- */
- finalizeImport: function() {
- this.createAsyncSlices_();
- },
-
- createAsyncSlices_: function() {
- if (this.allAsyncEvents_.length == 0)
- return;
-
- this.allAsyncEvents_.sort(function(x, y) {
- return x.event.ts - y.event.ts;
- });
-
- var asyncEventStatesByNameThenID = {};
-
- var allAsyncEvents = this.allAsyncEvents_;
- for (var i = 0; i < allAsyncEvents.length; i++) {
- var asyncEventState = allAsyncEvents[i];
-
- var event = asyncEventState.event;
- var name = event.name;
- if (name === undefined) {
- this.model_.importErrors.push(
- 'Async events (ph: S, T or F) require an name parameter.');
- continue;
- }
-
- var id = event.id;
- if (id === undefined) {
- this.model_.importErrors.push(
- 'Async events (ph: S, T or F) require an id parameter.');
- continue;
- }
-
- // TODO(simonjam): Add a synchronous tick on the appropriate thread.
-
- if (event.ph == 'S') {
- if (asyncEventStatesByNameThenID[name] === undefined)
- asyncEventStatesByNameThenID[name] = {};
- if (asyncEventStatesByNameThenID[name][id]) {
- this.model_.importErrors.push(
- 'At ' + event.ts + ', a slice of the same id ' + id +
- ' was alrady open.');
- continue;
- }
- asyncEventStatesByNameThenID[name][id] = [];
- asyncEventStatesByNameThenID[name][id].push(asyncEventState);
- } else {
- if (asyncEventStatesByNameThenID[name] === undefined) {
- this.model_.importErrors.push(
- 'At ' + event.ts + ', no slice named ' + name +
- ' was open.');
- continue;
- }
- if (asyncEventStatesByNameThenID[name][id] === undefined) {
- this.model_.importErrors.push(
- 'At ' + event.ts + ', no slice named ' + name +
- ' with id=' + id + ' was open.');
- continue;
- }
- var events = asyncEventStatesByNameThenID[name][id];
- events.push(asyncEventState);
-
- if (event.ph == 'F') {
- // Create a slice from start to end.
- var slice = new tracing.TimelineAsyncSlice(
- events[0].event.cat,
- name,
- tracing.getStringColorId(name),
- events[0].event.ts / 1000);
-
- slice.duration = (event.ts / 1000) - (events[0].event.ts / 1000);
-
- slice.startThread = events[0].thread;
- slice.endThread = asyncEventState.thread;
- slice.id = id;
- slice.args = events[0].event.args;
- slice.subSlices = [];
-
- // Create subSlices for each step.
- for (var j = 1; j < events.length; ++j) {
- var subName = name;
- if (events[j - 1].event.ph == 'T')
- subName = name + ':' + events[j - 1].event.args.step;
- var subSlice = new tracing.TimelineAsyncSlice(
- events[0].event.cat,
- subName,
- tracing.getStringColorId(name + j),
- events[j - 1].event.ts / 1000);
-
- subSlice.duration =
- (events[j].event.ts / 1000) - (events[j - 1].event.ts / 1000);
-
- subSlice.startThread = events[j - 1].thread;
- subSlice.endThread = events[j].thread;
- subSlice.id = id;
- subSlice.args = events[j - 1].event.args;
-
- slice.subSlices.push(subSlice);
- }
-
- // The args for the finish event go in the last subSlice.
- var lastSlice = slice.subSlices[slice.subSlices.length - 1];
- for (var arg in event.args)
- lastSlice.args[arg] = event.args[arg];
-
- // Add |slice| to the start-thread's asyncSlices.
- slice.startThread.asyncSlices.push(slice);
- delete asyncEventStatesByNameThenID[name][id];
- }
- }
- }
- }
- };
-
- tracing.TimelineModel.registerImporter(TraceEventImporter);
-
- return {
- TraceEventImporter: TraceEventImporter
- };
- });
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- /**
- * @fileoverview Base class for linux perf event parsers.
- *
- * The linux perf trace event importer depends on subclasses of
- * LinuxPerfParser to parse event data. Each subclass corresponds
- * to a group of trace events; e.g. LinuxPerfSchedParser implements
- * parsing of sched:* kernel trace events. Parser subclasses must
- * call LinuxPerfParser.registerSubtype to arrange to be instantiated
- * and their constructor must register their event handlers with the
- * importer. For example,
- *
- * var LinuxPerfParser = tracing.LinuxPerfParser;
- *
- * function LinuxPerfWorkqueueParser(importer) {
- * LinuxPerfParser.call(this, importer);
- *
- * importer.registerEventHandler('workqueue_execute_start',
- * LinuxPerfWorkqueueParser.prototype.executeStartEvent.bind(this));
- * importer.registerEventHandler('workqueue_execute_end',
- * LinuxPerfWorkqueueParser.prototype.executeEndEvent.bind(this));
- * }
- *
- * LinuxPerfParser.registerSubtype(LinuxPerfWorkqueueParser);
- *
- * When a registered event name is found in the data stream the associated
- * event handler is invoked:
- *
- * executeStartEvent: function(eventName, cpuNumber, ts, eventBase)
- *
- * If the routine returns false the caller will generate an import error
- * saying there was a problem parsing it. Handlers can also emit import
- * messages using this.importer.importError. If this is done in lieu of
- * the generic import error it may be desirable for the handler to return
- * true.
- *
- * Trace events generated by writing to the trace_marker file are expected
- * to have a leading text marker followed by a ':'; e.g. the trace clock
- * synchronization event is:
- *
- * tracing_mark_write: trace_event_clock_sync: parent_ts=0
- *
- * To register an event handler for these events, prepend the marker with
- * 'tracing_mark_write:'; e.g.
- *
- * this.registerEventHandler('tracing_mark_write:trace_event_clock_sync',
- *
- * All subclasses should depend on linux_perf_parser, e.g.
- *
- * base.defineModule('linux_perf_workqueue_parser')
- * .dependsOn('linux_perf_parser')
- * .exportsTo('tracing', function()
- *
- * and be listed in the dependsOn of LinuxPerfImporter. Beware that after
- * adding a new subclass you must run build/generate_about_tracing_contents.py
- * to regenerate about_tracing.*.
- */
- base.exportTo('tracing', function() {
-
- var subtypeConstructors = [];
-
- /**
- * Registers a subclass that will help parse linux perf events.
- * The importer will call createParsers (below) before importing
- * data so each subclass can register its handlers.
- *
- * @param {Function} subtypeConstructor The subtype's constructor function.
- */
- LinuxPerfParser.registerSubtype = function(subtypeConstructor) {
- subtypeConstructors.push(subtypeConstructor);
- };
-
- LinuxPerfParser.getSubtypeConstructors = function() {
- return subtypeConstructors;
- };
-
- /**
- * Parses linux perf events.
- * @constructor
- */
- function LinuxPerfParser(importer) {
- this.importer = importer;
- this.model = importer.model;
- }
-
- LinuxPerfParser.prototype = {
- __proto__: Object.prototype
- };
-
- return {
- LinuxPerfParser: LinuxPerfParser
- };
-
- });
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- /**
- * @fileoverview Parses trace_marker events that were inserted in the trace by
- * userland.
- */
- base.require('linux_perf_parser');
- base.exportTo('tracing', function() {
-
- var LinuxPerfParser = tracing.LinuxPerfParser;
-
- /**
- * Parses linux trace mark events that were inserted in the trace by userland.
- * @constructor
- */
- function LinuxPerfBusParser(importer) {
- LinuxPerfParser.call(this, importer);
-
- importer.registerEventHandler('memory_bus_usage',
- LinuxPerfBusParser.prototype.traceMarkWriteBusEvent.bind(this));
-
- this.model_ = importer.model_;
- this.ppids_ = {};
- }
-
- LinuxPerfBusParser.prototype = {
- __proto__: LinuxPerfParser.prototype,
-
- traceMarkWriteBusEvent: function(eventName, cpuNumber, pid, ts,
- eventBase, threadName) {
- var re = new RegExp('bus=(\\S+) rw_bytes=(\\d+) r_bytes=(\\d+) ' +
- 'w_bytes=(\\d+) cycles=(\\d+) ns=(\\d+)');
- var event = re.exec(eventBase[5]);
-
- var name = event[1];
- var rw_bytes = parseInt(event[2]);
- var r_bytes = parseInt(event[3]);
- var w_bytes = parseInt(event[4]);
- var cycles = parseInt(event[5]);
- var ns = parseInt(event[6]);
-
- // BW in MB/s
- var r_bw = r_bytes * 1000000000 / ns;
- r_bw /= 1024 * 1024;
- var w_bw = w_bytes * 1000000000 / ns;
- w_bw /= 1024 * 1024;
-
- var ctr = this.model_.getOrCreateProcess(0)
- .getOrCreateCounter(null, 'bus ' + name + ' read');
- // Initialize the counter's series fields if needed.
- if (ctr.numSeries == 0) {
- ctr.seriesNames.push('value');
- ctr.seriesColors.push(
- tracing.getStringColorId(ctr.name + '.' + 'value'));
- }
-
- // Add the sample value.
- ctr.timestamps.push(ts);
- ctr.samples.push(r_bw);
-
- ctr = this.model_.getOrCreateProcess(0)
- .getOrCreateCounter(null, 'bus ' + name + ' write');
- // Initialize the counter's series fields if needed.
- if (ctr.numSeries == 0) {
- ctr.seriesNames.push('value');
- ctr.seriesColors.push(
- tracing.getStringColorId(ctr.name + '.' + 'value'));
- }
-
- // Add the sample value.
- ctr.timestamps.push(ts);
- ctr.samples.push(w_bw);
-
- return true;
- },
- };
-
- LinuxPerfParser.registerSubtype(LinuxPerfBusParser);
-
- return {
- LinuxPerfBusParser: LinuxPerfBusParser
- };
- });
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- /**
- * @fileoverview Parses trace_marker events that were inserted in the trace by
- * userland.
- */
- base.require('linux_perf_parser');
- base.exportTo('tracing', function() {
-
- var LinuxPerfParser = tracing.LinuxPerfParser;
-
- /**
- * Parses linux trace mark events that were inserted in the trace by userland.
- * @constructor
- */
- function LinuxPerfClockParser(importer) {
- LinuxPerfParser.call(this, importer);
-
- importer.registerEventHandler('clock_set_rate',
- LinuxPerfClockParser.prototype.traceMarkWriteClockEvent.bind(this));
-
- this.model_ = importer.model_;
- this.ppids_ = {};
- }
-
- LinuxPerfClockParser.prototype = {
- __proto__: LinuxPerfParser.prototype,
-
- traceMarkWriteClockEvent: function(eventName, cpuNumber, pid, ts,
- eventBase, threadName) {
- var event = /(\S+) state=(\d+) cpu_id=(\d+)/.exec(eventBase[5]);
-
-
- var name = event[1];
- var rate = parseInt(event[2]);
-
- var ctr = this.model_.getOrCreateProcess(0)
- .getOrCreateCounter(null, name);
- // Initialize the counter's series fields if needed.
- if (ctr.numSeries == 0) {
- ctr.seriesNames.push('value');
- ctr.seriesColors.push(
- tracing.getStringColorId(ctr.name + '.' + 'value'));
- }
-
- // Add the sample value.
- ctr.timestamps.push(ts);
- ctr.samples.push(rate);
-
- return true;
- },
- };
-
- LinuxPerfParser.registerSubtype(LinuxPerfClockParser);
-
- return {
- LinuxPerfClockParser: LinuxPerfClockParser
- };
- });
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- /**
- * @fileoverview Parses cpufreq events in the Linux event trace format.
- */
- base.require('linux_perf_parser');
- base.exportTo('tracing', function() {
-
- var LinuxPerfParser = tracing.LinuxPerfParser;
-
- /**
- * Parses linux cpufreq trace events.
- * @constructor
- */
- function LinuxPerfCpufreqParser(importer) {
- LinuxPerfParser.call(this, importer);
-
- importer.registerEventHandler('cpufreq_interactive_up',
- LinuxPerfCpufreqParser.prototype.cpufreqUpDownEvent.bind(this));
- importer.registerEventHandler('cpufreq_interactive_down',
- LinuxPerfCpufreqParser.prototype.cpufreqUpDownEvent.bind(this));
- importer.registerEventHandler('cpufreq_interactive_already',
- LinuxPerfCpufreqParser.prototype.cpufreqTargetEvent.bind(this));
- importer.registerEventHandler('cpufreq_interactive_notyet',
- LinuxPerfCpufreqParser.prototype.cpufreqTargetEvent.bind(this));
- importer.registerEventHandler('cpufreq_interactive_target',
- LinuxPerfCpufreqParser.prototype.cpufreqTargetEvent.bind(this));
- importer.registerEventHandler('cpufreq_interactive_boost',
- LinuxPerfCpufreqParser.prototype.cpufreqBoostUnboostEvent.bind(this));
- importer.registerEventHandler('cpufreq_interactive_unboost',
- LinuxPerfCpufreqParser.prototype.cpufreqBoostUnboostEvent.bind(this));
- }
-
- LinuxPerfCpufreqParser.prototype = {
- __proto__: LinuxPerfParser.prototype,
-
- cpufreqSlice: function(ts, eventName, cpu, args) {
- // TODO(sleffler) should be per-cpu
- var kthread = this.importer.getOrCreatePseudoThread('cpufreq');
- kthread.openSlice = eventName;
- var slice = new tracing.TimelineSlice('', kthread.openSlice,
- tracing.getStringColorId(kthread.openSlice), ts, args, 0);
-
- kthread.thread.pushSlice(slice);
- },
-
- cpufreqBoostSlice: function(ts, eventName, args) {
- var kthread = this.importer.getOrCreatePseudoThread('cpufreq_boost');
- kthread.openSlice = eventName;
- var slice = new tracing.TimelineSlice('', kthread.openSlice,
- tracing.getStringColorId(kthread.openSlice), ts, args, 0);
-
- kthread.thread.pushSlice(slice);
- },
-
- /**
- * Parses cpufreq events and sets up state in the importer.
- */
- cpufreqUpDownEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
- var event = /cpu=(\d+) targ=(\d+) actual=(\d+)/.exec(eventBase[5]);
- if (!event)
- return false;
-
- // TODO(sleffler) split by cpu
- var cpu = parseInt(event[1]);
- var targ = parseInt(event[2]);
- var actual = parseInt(event[3]);
- this.cpufreqSlice(ts, eventName, cpu,
- {
- cpu: cpu,
- targ: targ,
- actual: actual
- });
- return true;
- },
-
- cpufreqTargetEvent: function(eventName, cpuNumber, pid, ts,
- eventBase) {
- var event = /cpu=(\d+) load=(\d+) cur=(\d+) targ=(\d+)/
- .exec(eventBase[5]);
- if (!event)
- return false;
-
- // TODO(sleffler) split by cpu
- var cpu = parseInt(event[1]);
- var load = parseInt(event[2]);
- var cur = parseInt(event[3]);
- var targ = parseInt(event[4]);
- this.cpufreqSlice(ts, eventName, cpu,
- {
- cpu: cpu,
- load: load,
- cur: cur,
- targ: targ
- });
- return true;
- },
-
- cpufreqBoostUnboostEvent: function(eventName, cpuNumber, pid, ts,
- eventBase) {
- this.cpufreqBoostSlice(ts, eventName,
- {
- type: eventBase[5]
- });
- return true;
- }
- };
-
- LinuxPerfParser.registerSubtype(LinuxPerfCpufreqParser);
-
- return {
- LinuxPerfCpufreqParser: LinuxPerfCpufreqParser
- };
- });
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- /**
- * @fileoverview Parses drm driver events in the Linux event trace format.
- */
- base.require('linux_perf_parser');
- base.exportTo('tracing', function() {
-
- var LinuxPerfParser = tracing.LinuxPerfParser;
-
- /**
- * Parses linux drm trace events.
- * @constructor
- */
- function LinuxPerfDrmParser(importer) {
- LinuxPerfParser.call(this, importer);
-
- importer.registerEventHandler('drm_vblank_event',
- LinuxPerfDrmParser.prototype.vblankEvent.bind(this));
- }
-
- LinuxPerfDrmParser.prototype = {
- __proto__: LinuxPerfParser.prototype,
-
- drmVblankSlice: function(ts, eventName, args) {
- var kthread = this.importer.getOrCreatePseudoThread('drm_vblank');
- kthread.openSlice = eventName;
- var slice = new tracing.TimelineSlice('', kthread.openSlice,
- tracing.getStringColorId(kthread.openSlice), ts, args, 0);
-
- kthread.thread.pushSlice(slice);
- },
-
- /**
- * Parses drm driver events and sets up state in the importer.
- */
- vblankEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
- var event = /crtc=(\d+), seq=(\d+)/.exec(eventBase[5]);
- if (!event)
- return false;
-
- var crtc = parseInt(event[1]);
- var seq = parseInt(event[2]);
- this.drmVblankSlice(ts, 'vblank:' + crtc,
- {
- crtc: crtc,
- seq: seq
- });
- return true;
- }
- };
-
- LinuxPerfParser.registerSubtype(LinuxPerfDrmParser);
-
- return {
- LinuxPerfDrmParser: LinuxPerfDrmParser
- };
- });
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- /**
- * @fileoverview Parses exynos events in the Linux event trace format.
- */
- base.require('linux_perf_parser');
- base.exportTo('tracing', function() {
-
- var LinuxPerfParser = tracing.LinuxPerfParser;
-
- /**
- * Parses linux exynos trace events.
- * @constructor
- */
- function LinuxPerfExynosParser(importer) {
- LinuxPerfParser.call(this, importer);
-
- importer.registerEventHandler('exynos_flip_request',
- LinuxPerfExynosParser.prototype.flipEvent.bind(this));
- importer.registerEventHandler('exynos_flip_complete',
- LinuxPerfExynosParser.prototype.flipEvent.bind(this));
-
- importer.registerEventHandler('exynos_busfreq_target_int',
- LinuxPerfExynosParser.prototype.busfreqTargetIntEvent.bind(this));
- importer.registerEventHandler('exynos_busfreq_target_mif',
- LinuxPerfExynosParser.prototype.busfreqTargetMifEvent.bind(this));
- }
-
- LinuxPerfExynosParser.prototype = {
- __proto__: LinuxPerfParser.prototype,
-
- exynosFlipOpenSlice: function(ts, pipe) {
- // use pipe?
- var kthread = this.importer.getOrCreatePseudoThread('exynos_flip');
- kthread.openSliceTS = ts;
- kthread.openSlice = 'flip:' + pipe;
- },
-
- exynosFlipCloseSlice: function(ts, args) {
- var kthread = this.importer.getOrCreatePseudoThread('exynos_flip');
- if (kthread.openSlice) {
- var slice = new tracing.TimelineSlice('', kthread.openSlice,
- tracing.getStringColorId(kthread.openSlice),
- kthread.openSliceTS,
- args,
- ts - kthread.openSliceTS);
-
- kthread.thread.pushSlice(slice);
- }
- kthread.openSlice = undefined;
- },
-
- /**
- * Parses exynos events and sets up state in the importer.
- */
- flipEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
- var event = /pipe=(\d+)/.exec(eventBase[5]);
- if (!event)
- return false;
-
- var pipe = parseInt(event[1]);
- if (eventName == 'exynos_flip_request')
- this.exynosFlipOpenSlice(ts, pipe);
- else
- this.exynosFlipCloseSlice(ts,
- {
- pipe: pipe
- });
- return true;
- },
-
- exynosBusfreqSample: function(name, ts, frequency) {
- var targetCpu = this.importer.getOrCreateCpuState(0);
- var counter = targetCpu.cpu.getOrCreateCounter('', name);
- if (counter.numSeries == 0) {
- counter.seriesNames.push('frequency');
- counter.seriesColors.push(
- tracing.getStringColorId(counter.name + '.' + 'frequency'));
- }
- counter.timestamps.push(ts);
- counter.samples.push(frequency);
- },
-
- /**
- * Parses exynos_busfreq_target_int events and sets up state.
- */
- busfreqTargetIntEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
- var event = /frequency=(\d+)/.exec(eventBase[5]);
- if (!event)
- return false;
-
- this.exynosBusfreqSample('INT Frequency', ts, parseInt(event[1]));
- return true;
- },
-
- /**
- * Parses exynos_busfreq_target_mif events and sets up state.
- */
- busfreqTargetMifEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
- var event = /frequency=(\d+)/.exec(eventBase[5]);
- if (!event)
- return false;
-
- this.exynosBusfreqSample('MIF Frequency', ts, parseInt(event[1]));
- return true;
- },
- };
-
- LinuxPerfParser.registerSubtype(LinuxPerfExynosParser);
-
- return {
- LinuxPerfExynosParser: LinuxPerfExynosParser
- };
- });
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- /**
- * @fileoverview Parses gesture events in the Linux event trace format.
- */
- base.require('linux_perf_parser');
- base.exportTo('tracing', function() {
-
- var LinuxPerfParser = tracing.LinuxPerfParser;
-
- /**
- * Parses trace events generated by gesture library for touchpad.
- * @constructor
- */
- function LinuxPerfGestureParser(importer) {
- LinuxPerfParser.call(this, importer);
- importer.registerEventHandler('tracing_mark_write:log',
- LinuxPerfGestureParser.prototype.logEvent.bind(this));
- importer.registerEventHandler('tracing_mark_write:SyncInterpret',
- LinuxPerfGestureParser.prototype.syncEvent.bind(this));
- importer.registerEventHandler('tracing_mark_write:HandleTimer',
- LinuxPerfGestureParser.prototype.timerEvent.bind(this));
- }
-
- LinuxPerfGestureParser.prototype = {
- __proto__: LinuxPerfParser.prototype,
-
- /**
- * Parse events generate by gesture library.
- * gestureOpenSlice and gestureCloseSlice are two common
- * functions to store the begin time and end time for all
- * events in gesture library
- */
- gestureOpenSlice: function(title, ts, opt_args) {
- this.importer.getOrCreatePseudoThread('gesture').thread.beginSlice(
- 'touchpad_gesture', title, ts, opt_args);
- },
-
- gestureCloseSlice: function(title, ts) {
- var thread = this.importer.getOrCreatePseudoThread('gesture').thread;
- if (thread.openSliceCount) {
- var slice = thread.openPartialSlices_[thread.openSliceCount - 1];
- if (slice.title != title) {
- this.importer.importError('Titles do not match. Title is ' +
- slice.title + ' in openSlice, and is ' +
- title + ' in endSlice');
- } else {
- thread.endSlice(ts);
- }
- }
- },
-
- /**
- * For log events, events will come in pairs with a tag log:
- * like this:
- * tracing_mark_write: log: start: TimerLogOutputs
- * tracing_mark_write: log: end: TimerLogOutputs
- * which represent the start and the end time of certain log behavior
- * Take these logs above for example, they are the start and end time
- * of logging Output for HandleTimer function
- */
- logEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
- var innerEvent =
- /^\s*(\w+):\s*(\w+)$/.exec(eventBase[2]);
- switch (innerEvent[1]) {
- case 'start':
- this.gestureOpenSlice('GestureLog', ts, {name: innerEvent[2]});
- break;
- case 'end':
- this.gestureCloseSlice('GestureLog', ts);
- }
- return true;
- },
-
- /**
- * For SyncInterpret events, events will come in pairs with
- * a tag SyncInterpret:
- * like this:
- * tracing_mark_write: SyncInterpret: start: ClickWiggleFilterInterpreter
- * tracing_mark_write: SyncInterpret: end: ClickWiggleFilterInterpreter
- * which represent the start and the end time of SyncInterpret function
- * inside the certain interpreter in the gesture library.
- * Take the logs above for example, they are the start and end time
- * of the SyncInterpret function inside ClickWiggleFilterInterpreter
- */
- syncEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
- var innerEvent = /^\s*(\w+):\s*(\w+)$/.exec(eventBase[2]);
- switch (innerEvent[1]) {
- case 'start':
- this.gestureOpenSlice('SyncInterpret', ts,
- {interpreter: innerEvent[2]});
- break;
- case 'end':
- this.gestureCloseSlice('SyncInterpret', ts);
- }
- return true;
- },
-
- /**
- * For HandleTimer events, events will come in pairs with
- * a tag HandleTimer:
- * like this:
- * tracing_mark_write: HandleTimer: start: LookaheadFilterInterpreter
- * tracing_mark_write: HandleTimer: end: LookaheadFilterInterpreter
- * which represent the start and the end time of HandleTimer function
- * inside the certain interpreter in the gesture library.
- * Take the logs above for example, they are the start and end time
- * of the HandleTimer function inside LookaheadFilterInterpreter
- */
- timerEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
- var innerEvent = /^\s*(\w+):\s*(\w+)$/.exec(eventBase[2]);
- switch (innerEvent[1]) {
- case 'start':
- this.gestureOpenSlice('HandleTimer', ts,
- {interpreter: innerEvent[2]});
- break;
- case 'end':
- this.gestureCloseSlice('HandleTimer', ts);
- }
- return true;
- }
- };
-
- LinuxPerfParser.registerSubtype(LinuxPerfGestureParser);
-
- return {
- LinuxPerfGestureParser: LinuxPerfGestureParser
- };
- });
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- /**
- * @fileoverview Parses i915 driver events in the Linux event trace format.
- */
- base.require('linux_perf_parser');
- base.exportTo('tracing', function() {
-
- var LinuxPerfParser = tracing.LinuxPerfParser;
-
- /**
- * Parses linux i915 trace events.
- * @constructor
- */
- function LinuxPerfI915Parser(importer) {
- LinuxPerfParser.call(this, importer);
-
- importer.registerEventHandler('i915_gem_object_create',
- LinuxPerfI915Parser.prototype.gemObjectCreateEvent.bind(this));
- importer.registerEventHandler('i915_gem_object_bind',
- LinuxPerfI915Parser.prototype.gemObjectBindEvent.bind(this));
- importer.registerEventHandler('i915_gem_object_unbind',
- LinuxPerfI915Parser.prototype.gemObjectBindEvent.bind(this));
- importer.registerEventHandler('i915_gem_object_change_domain',
- LinuxPerfI915Parser.prototype.gemObjectChangeDomainEvent.bind(this));
- importer.registerEventHandler('i915_gem_object_pread',
- LinuxPerfI915Parser.prototype.gemObjectPreadWriteEvent.bind(this));
- importer.registerEventHandler('i915_gem_object_pwrite',
- LinuxPerfI915Parser.prototype.gemObjectPreadWriteEvent.bind(this));
- importer.registerEventHandler('i915_gem_object_fault',
- LinuxPerfI915Parser.prototype.gemObjectFaultEvent.bind(this));
- importer.registerEventHandler('i915_gem_object_clflush',
- // NB: reuse destroy handler
- LinuxPerfI915Parser.prototype.gemObjectDestroyEvent.bind(this));
- importer.registerEventHandler('i915_gem_object_destroy',
- LinuxPerfI915Parser.prototype.gemObjectDestroyEvent.bind(this));
- importer.registerEventHandler('i915_gem_ring_dispatch',
- LinuxPerfI915Parser.prototype.gemRingDispatchEvent.bind(this));
- importer.registerEventHandler('i915_gem_ring_flush',
- LinuxPerfI915Parser.prototype.gemRingFlushEvent.bind(this));
- importer.registerEventHandler('i915_gem_request',
- LinuxPerfI915Parser.prototype.gemRequestEvent.bind(this));
- importer.registerEventHandler('i915_gem_request_add',
- LinuxPerfI915Parser.prototype.gemRequestEvent.bind(this));
- importer.registerEventHandler('i915_gem_request_complete',
- LinuxPerfI915Parser.prototype.gemRequestEvent.bind(this));
- importer.registerEventHandler('i915_gem_request_retire',
- LinuxPerfI915Parser.prototype.gemRequestEvent.bind(this));
- importer.registerEventHandler('i915_gem_request_wait_begin',
- LinuxPerfI915Parser.prototype.gemRequestEvent.bind(this));
- importer.registerEventHandler('i915_gem_request_wait_end',
- LinuxPerfI915Parser.prototype.gemRequestEvent.bind(this));
- importer.registerEventHandler('i915_gem_ring_wait_begin',
- LinuxPerfI915Parser.prototype.gemRingWaitEvent.bind(this));
- importer.registerEventHandler('i915_gem_ring_wait_end',
- LinuxPerfI915Parser.prototype.gemRingWaitEvent.bind(this));
- importer.registerEventHandler('i915_reg_rw',
- LinuxPerfI915Parser.prototype.regRWEvent.bind(this));
- importer.registerEventHandler('i915_flip_request',
- LinuxPerfI915Parser.prototype.flipEvent.bind(this));
- importer.registerEventHandler('i915_flip_complete',
- LinuxPerfI915Parser.prototype.flipEvent.bind(this));
- }
-
- LinuxPerfI915Parser.prototype = {
- __proto__: LinuxPerfParser.prototype,
-
- i915FlipOpenSlice: function(ts, obj, plane) {
- // use i915_flip_obj_plane?
- var kthread = this.importer.getOrCreatePseudoThread('i915_flip');
- kthread.openSliceTS = ts;
- kthread.openSlice = 'flip:' + obj + '/' + plane;
- },
-
- i915FlipCloseSlice: function(ts, args) {
- var kthread = this.importer.getOrCreatePseudoThread('i915_flip');
- if (kthread.openSlice) {
- var slice = new tracing.TimelineSlice('', kthread.openSlice,
- tracing.getStringColorId(kthread.openSlice),
- kthread.openSliceTS,
- args,
- ts - kthread.openSliceTS);
-
- kthread.thread.pushSlice(slice);
- }
- kthread.openSlice = undefined;
- },
-
- i915GemObjectSlice: function(ts, eventName, obj, args) {
- var kthread = this.importer.getOrCreatePseudoThread('i915_gem');
- kthread.openSlice = eventName + ':' + obj;
- var slice = new tracing.TimelineSlice('', kthread.openSlice,
- tracing.getStringColorId(kthread.openSlice), ts, args, 0);
-
- kthread.thread.pushSlice(slice);
- },
-
- i915GemRingSlice: function(ts, eventName, dev, ring, args) {
- var kthread = this.importer.getOrCreatePseudoThread('i915_gem_ring');
- kthread.openSlice = eventName + ':' + dev + '.' + ring;
- var slice = new tracing.TimelineSlice('', kthread.openSlice,
- tracing.getStringColorId(kthread.openSlice), ts, args, 0);
-
- kthread.thread.pushSlice(slice);
- },
-
- i915RegSlice: function(ts, eventName, reg, args) {
- var kthread = this.importer.getOrCreatePseudoThread('i915_reg');
- kthread.openSlice = eventName + ':' + reg;
- var slice = new tracing.TimelineSlice('', kthread.openSlice,
- tracing.getStringColorId(kthread.openSlice), ts, args, 0);
-
- kthread.thread.pushSlice(slice);
- },
-
- /**
- * Parses i915 driver events and sets up state in the importer.
- */
- gemObjectCreateEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
- var event = /obj=(\w+), size=(\d+)/.exec(eventBase[5]);
- if (!event)
- return false;
-
- var obj = event[1];
- var size = parseInt(event[2]);
- this.i915GemObjectSlice(ts, eventName, obj,
- {
- obj: obj,
- size: size
- });
- return true;
- },
-
- gemObjectBindEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
- // TODO(sleffler) mappable
- var event = /obj=(\w+), offset=(\w+), size=(\d+)/.exec(eventBase[5]);
- if (!event)
- return false;
-
- var obj = event[1];
- var offset = event[2];
- var size = parseInt(event[3]);
- this.i915ObjectGemSlice(ts, eventName + ':' + obj,
- {
- obj: obj,
- offset: offset,
- size: size
- });
- return true;
- },
-
- gemObjectChangeDomainEvent: function(eventName, cpuNumber, pid, ts,
- eventBase) {
- var event = /obj=(\w+), read=(\w+=>\w+), write=(\w+=>\w+)/
- .exec(eventBase[5]);
- if (!event)
- return false;
-
- var obj = event[1];
- var read = event[2];
- var write = event[3];
- this.i915GemObjectSlice(ts, eventName, obj,
- {
- obj: obj,
- read: read,
- write: write
- });
- return true;
- },
-
- gemObjectPreadWriteEvent: function(eventName, cpuNumber, pid, ts,
- eventBase) {
- var event = /obj=(\w+), offset=(\d+), len=(\d+)/.exec(eventBase[5]);
- if (!event)
- return false;
-
- var obj = event[1];
- var offset = parseInt(event[2]);
- var len = parseInt(event[3]);
- this.i915GemObjectSlice(ts, eventName, obj,
- {
- obj: obj,
- offset: offset,
- len: len
- });
- return true;
- },
-
- gemObjectFaultEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
- // TODO(sleffler) writable
- var event = /obj=(\w+), (\w+) index=(\d+)/.exec(eventBase[5]);
- if (!event)
- return false;
-
- var obj = event[1];
- var type = event[2];
- var index = parseInt(event[3]);
- this.i915GemObjectSlice(ts, eventName, obj,
- {
- obj: obj,
- type: type,
- index: index
- });
- return true;
- },
-
- gemObjectDestroyEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
- var event = /obj=(\w+)/.exec(eventBase[5]);
- if (!event)
- return false;
-
- var obj = event[1];
- this.i915GemObjectSlice(ts, eventName, obj,
- {
- obj: obj
- });
- return true;
- },
-
- gemRingDispatchEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
- var event = /dev=(\d+), ring=(\d+), seqno=(\d+)/.exec(eventBase[5]);
- if (!event)
- return false;
-
- var dev = parseInt(event[1]);
- var ring = parseInt(event[2]);
- var seqno = parseInt(event[3]);
- this.i915GemRingSlice(ts, eventName, dev, ring,
- {
- dev: dev,
- ring: ring,
- seqno: seqno
- });
- return true;
- },
-
- gemRingFlushEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
- var event = /dev=(\d+), ring=(\w+), invalidate=(\w+), flush=(\w+)/
- .exec(eventBase[5]);
- if (!event)
- return false;
-
- var dev = parseInt(event[1]);
- var ring = parseInt(event[2]);
- var invalidate = event[3];
- var flush = event[4];
- this.i915GemRingSlice(ts, eventName, dev, ring,
- {
- dev: dev,
- ring: ring,
- invalidate: invalidate,
- flush: flush
- });
- return true;
- },
-
- gemRequestEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
- var event = /dev=(\d+), ring=(\d+), seqno=(\d+)/.exec(eventBase[5]);
- if (!event)
- return false;
-
- var dev = parseInt(event[1]);
- var ring = parseInt(event[2]);
- var seqno = parseInt(event[3]);
- this.i915GemRingSlice(ts, eventName, dev, ring,
- {
- dev: dev,
- ring: ring,
- seqno: seqno
- });
- return true;
- },
-
- gemRingWaitEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
- var event = /dev=(\d+), ring=(\d+)/.exec(eventBase[5]);
- if (!event)
- return false;
-
- var dev = parseInt(event[1]);
- var ring = parseInt(event[2]);
- this.i915GemRingSlice(ts, eventName, dev, ring,
- {
- dev: dev,
- ring: ring
- });
- return true;
- },
-
- regRWEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
- var event = /(\w+) reg=(\w+), len=(\d+), val=(\(\w+, \w+\))/
- .exec(eventBase[5]);
- if (!event)
- return false;
-
- var rw = event[1];
- var reg = event[2];
- var len = event[3];
- var data = event[3];
- this.i915RegSlice(ts, rw, reg,
- {
- rw: rw,
- reg: reg,
- len: len,
- data: data
- });
- return true;
- },
-
- flipEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
- var event = /plane=(\d+), obj=(\w+)/.exec(eventBase[5]);
- if (!event)
- return false;
-
- var plane = parseInt(event[1]);
- var obj = event[2];
- if (eventName == 'i915_flip_request')
- this.i915FlipOpenSlice(ts, obj, plane);
- else
- this.i915FlipCloseSlice(ts,
- {
- obj: obj,
- plane: plane
- });
- return true;
- }
- };
-
- LinuxPerfParser.registerSubtype(LinuxPerfI915Parser);
-
- return {
- LinuxPerfI915Parser: LinuxPerfI915Parser
- };
- });
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- /**
- * @fileoverview Parses Mali DDK/kernel events in the Linux event trace format.
- */
- base.require('linux_perf_parser');
- base.exportTo('tracing', function() {
-
- var LinuxPerfParser = tracing.LinuxPerfParser;
-
- /**
- * Parses Mali DDK/kernel trace events.
- * @constructor
- */
- function LinuxPerfMaliParser(importer) {
- LinuxPerfParser.call(this, importer);
-
- // kernel DVFS events
- importer.registerEventHandler('mali_dvfs_event',
- LinuxPerfMaliParser.prototype.dvfsEventEvent.bind(this));
- importer.registerEventHandler('mali_dvfs_set_clock',
- LinuxPerfMaliParser.prototype.dvfsSetClockEvent.bind(this));
- importer.registerEventHandler('mali_dvfs_set_voltage',
- LinuxPerfMaliParser.prototype.dvfsSetVoltageEvent.bind(this));
-
- // kernel Mali hw counter events
- this.addJMCounter('mali_hwc_MESSAGES_SENT', 'Messages Sent');
- this.addJMCounter('mali_hwc_MESSAGES_RECEIVED', 'Messages Received');
- this.addJMCycles('mali_hwc_GPU_ACTIVE', 'GPU Active');
- this.addJMCycles('mali_hwc_IRQ_ACTIVE', 'IRQ Active');
-
- for (var i = 0; i < 7; i++) {
- var jobStr = 'JS' + i;
- var jobHWCStr = 'mali_hwc_' + jobStr;
- this.addJMCounter(jobHWCStr + '_JOBS', jobStr + ' Jobs');
- this.addJMCounter(jobHWCStr + '_TASKS', jobStr + ' Tasks');
- this.addJMCycles(jobHWCStr + '_ACTIVE', jobStr + ' Active');
- this.addJMCycles(jobHWCStr + '_WAIT_READ', jobStr + ' Wait Read');
- this.addJMCycles(jobHWCStr + '_WAIT_ISSUE', jobStr + ' Wait Issue');
- this.addJMCycles(jobHWCStr + '_WAIT_DEPEND', jobStr + ' Wait Depend');
- this.addJMCycles(jobHWCStr + '_WAIT_FINISH', jobStr + ' Wait Finish');
- }
-
- this.addTilerCounter('mali_hwc_TRIANGLES', 'Triangles');
- this.addTilerCounter('mali_hwc_QUADS', 'Quads');
- this.addTilerCounter('mali_hwc_POLYGONS', 'Polygons');
- this.addTilerCounter('mali_hwc_POINTS', 'Points');
- this.addTilerCounter('mali_hwc_LINES', 'Lines');
- this.addTilerCounter('mali_hwc_VCACHE_HIT', 'VCache Hit');
- this.addTilerCounter('mali_hwc_VCACHE_MISS', 'VCache Miss');
- this.addTilerCounter('mali_hwc_FRONT_FACING', 'Front Facing');
- this.addTilerCounter('mali_hwc_BACK_FACING', 'Back Facing');
- this.addTilerCounter('mali_hwc_PRIM_VISIBLE', 'Prim Visible');
- this.addTilerCounter('mali_hwc_PRIM_CULLED', 'Prim Culled');
- this.addTilerCounter('mali_hwc_PRIM_CLIPPED', 'Prim Clipped');
-
- this.addTilerCounter('mali_hwc_WRBUF_HIT', 'Wrbuf Hit');
- this.addTilerCounter('mali_hwc_WRBUF_MISS', 'Wrbuf Miss');
- this.addTilerCounter('mali_hwc_WRBUF_LINE', 'Wrbuf Line');
- this.addTilerCounter('mali_hwc_WRBUF_PARTIAL', 'Wrbuf Partial');
- this.addTilerCounter('mali_hwc_WRBUF_STALL', 'Wrbuf Stall');
-
- this.addTilerCycles('mali_hwc_ACTIVE', 'Tiler Active');
- this.addTilerCycles('mali_hwc_INDEX_WAIT', 'Index Wait');
- this.addTilerCycles('mali_hwc_INDEX_RANGE_WAIT', 'Index Range Wait');
- this.addTilerCycles('mali_hwc_VERTEX_WAIT', 'Vertex Wait');
- this.addTilerCycles('mali_hwc_PCACHE_WAIT', 'Pcache Wait');
- this.addTilerCycles('mali_hwc_WRBUF_WAIT', 'Wrbuf Wait');
- this.addTilerCycles('mali_hwc_BUS_READ', 'Bus Read');
- this.addTilerCycles('mali_hwc_BUS_WRITE', 'Bus Write');
-
- this.addTilerCycles('mali_hwc_TILER_UTLB_STALL', 'Tiler UTLB Stall');
- this.addTilerCycles('mali_hwc_TILER_UTLB_HIT', 'Tiler UTLB Hit');
-
- this.addFragCycles('mali_hwc_FRAG_ACTIVE', 'Active');
- /* NB: don't propagate spelling mistakes to labels */
- this.addFragCounter('mali_hwc_FRAG_PRIMATIVES', 'Primitives');
- this.addFragCounter('mali_hwc_FRAG_PRIMATIVES_DROPPED',
- 'Primitives Dropped');
- this.addFragCycles('mali_hwc_FRAG_CYCLE_DESC', 'Descriptor Processing');
- this.addFragCycles('mali_hwc_FRAG_CYCLES_PLR', 'PLR Processing??');
- this.addFragCycles('mali_hwc_FRAG_CYCLES_VERT', 'Vertex Processing');
- this.addFragCycles('mali_hwc_FRAG_CYCLES_TRISETUP', 'Triangle Setup');
- this.addFragCycles('mali_hwc_FRAG_CYCLES_RAST', 'Rasterization???');
- this.addFragCounter('mali_hwc_FRAG_THREADS', 'Threads');
- this.addFragCounter('mali_hwc_FRAG_DUMMY_THREADS', 'Dummy Threads');
- this.addFragCounter('mali_hwc_FRAG_QUADS_RAST', 'Quads Rast');
- this.addFragCounter('mali_hwc_FRAG_QUADS_EZS_TEST', 'Quads EZS Test');
- this.addFragCounter('mali_hwc_FRAG_QUADS_EZS_KILLED', 'Quads EZS Killed');
- this.addFragCounter('mali_hwc_FRAG_QUADS_LZS_TEST', 'Quads LZS Test');
- this.addFragCounter('mali_hwc_FRAG_QUADS_LZS_KILLED', 'Quads LZS Killed');
- this.addFragCycles('mali_hwc_FRAG_CYCLE_NO_TILE', 'No Tiles');
- this.addFragCounter('mali_hwc_FRAG_NUM_TILES', 'Tiles');
- this.addFragCounter('mali_hwc_FRAG_TRANS_ELIM', 'Transactions Eliminated');
-
- this.addComputeCycles('mali_hwc_COMPUTE_ACTIVE', 'Active');
- this.addComputeCounter('mali_hwc_COMPUTE_TASKS', 'Tasks');
- this.addComputeCounter('mali_hwc_COMPUTE_THREADS', 'Threads Started');
- this.addComputeCycles('mali_hwc_COMPUTE_CYCLES_DESC',
- 'Waiting for Descriptors');
-
- this.addTripipeCycles('mali_hwc_TRIPIPE_ACTIVE', 'Active');
-
- this.addArithCounter('mali_hwc_ARITH_WORDS', 'Instructions (/Pipes)');
- this.addArithCycles('mali_hwc_ARITH_CYCLES_REG',
- 'Reg scheduling stalls (/Pipes)');
- this.addArithCycles('mali_hwc_ARITH_CYCLES_L0',
- 'L0 cache miss stalls (/Pipes)');
- this.addArithCounter('mali_hwc_ARITH_FRAG_DEPEND',
- 'Frag dep check failures (/Pipes)');
-
- this.addLSCounter('mali_hwc_LS_WORDS', 'Instruction Words Completed');
- this.addLSCounter('mali_hwc_LS_ISSUES', 'Full Pipeline Issues');
- this.addLSCounter('mali_hwc_LS_RESTARTS', 'Restarts (unpairable insts)');
- this.addLSCounter('mali_hwc_LS_REISSUES_MISS',
- 'Pipeline reissue (cache miss/uTLB)');
- this.addLSCounter('mali_hwc_LS_REISSUES_VD',
- 'Pipeline reissue (varying data)');
- /* TODO(sleffler) fix kernel event typo */
- this.addLSCounter('mali_hwc_LS_REISSUE_ATTRIB_MISS',
- 'Pipeline reissue (attribute cache miss)');
- this.addLSCounter('mali_hwc_LS_REISSUE_NO_WB', 'Writeback not used');
-
- this.addTexCounter('mali_hwc_TEX_WORDS', 'Words');
- this.addTexCounter('mali_hwc_TEX_BUBBLES', 'Bubbles');
- this.addTexCounter('mali_hwc_TEX_WORDS_L0', 'Words L0');
- this.addTexCounter('mali_hwc_TEX_WORDS_DESC', 'Words Desc');
- this.addTexCounter('mali_hwc_TEX_THREADS', 'Threads');
- this.addTexCounter('mali_hwc_TEX_RECIRC_FMISS', 'Recirc due to Full Miss');
- this.addTexCounter('mali_hwc_TEX_RECIRC_DESC', 'Recirc due to Desc Miss');
- this.addTexCounter('mali_hwc_TEX_RECIRC_MULTI', 'Recirc due to Multipass');
- this.addTexCounter('mali_hwc_TEX_RECIRC_PMISS',
- 'Recirc due to Partial Cache Miss');
- this.addTexCounter('mali_hwc_TEX_RECIRC_CONF',
- 'Recirc due to Cache Conflict');
-
- this.addLSCCounter('mali_hwc_LSC_READ_HITS', 'Read Hits');
- this.addLSCCounter('mali_hwc_LSC_READ_MISSES', 'Read Misses');
- this.addLSCCounter('mali_hwc_LSC_WRITE_HITS', 'Write Hits');
- this.addLSCCounter('mali_hwc_LSC_WRITE_MISSES', 'Write Misses');
- this.addLSCCounter('mali_hwc_LSC_ATOMIC_HITS', 'Atomic Hits');
- this.addLSCCounter('mali_hwc_LSC_ATOMIC_MISSES', 'Atomic Misses');
- this.addLSCCounter('mali_hwc_LSC_LINE_FETCHES', 'Line Fetches');
- this.addLSCCounter('mali_hwc_LSC_DIRTY_LINE', 'Dirty Lines');
- this.addLSCCounter('mali_hwc_LSC_SNOOPS', 'Snoops');
-
- this.addAXICounter('mali_hwc_AXI_TLB_STALL', 'Address channel stall');
- this.addAXICounter('mali_hwc_AXI_TLB_MISS', 'Cache Miss');
- this.addAXICounter('mali_hwc_AXI_TLB_TRANSACTION', 'Transactions');
- this.addAXICounter('mali_hwc_LS_TLB_MISS', 'LS Cache Miss');
- this.addAXICounter('mali_hwc_LS_TLB_HIT', 'LS Cache Hit');
- this.addAXICounter('mali_hwc_AXI_BEATS_READ', 'Read Beats');
- this.addAXICounter('mali_hwc_AXI_BEATS_WRITE', 'Write Beats');
-
- this.addMMUCounter('mali_hwc_MMU_TABLE_WALK', 'Page Table Walks');
- this.addMMUCounter('mali_hwc_MMU_REPLAY_MISS',
- 'Cache Miss from Replay Buffer');
- this.addMMUCounter('mali_hwc_MMU_REPLAY_FULL', 'Replay Buffer Full');
- this.addMMUCounter('mali_hwc_MMU_NEW_MISS', 'Cache Miss on New Request');
- this.addMMUCounter('mali_hwc_MMU_HIT', 'Cache Hit');
-
- this.addMMUCycles('mali_hwc_UTLB_STALL', 'UTLB Stalled');
- this.addMMUCycles('mali_hwc_UTLB_REPLAY_MISS', 'UTLB Replay Miss');
- this.addMMUCycles('mali_hwc_UTLB_REPLAY_FULL', 'UTLB Replay Full');
- this.addMMUCycles('mali_hwc_UTLB_NEW_MISS', 'UTLB New Miss');
- this.addMMUCycles('mali_hwc_UTLB_HIT', 'UTLB Hit');
-
- this.addL2Counter('mali_hwc_L2_READ_BEATS', 'Read Beats');
- this.addL2Counter('mali_hwc_L2_WRITE_BEATS', 'Write Beats');
- this.addL2Counter('mali_hwc_L2_ANY_LOOKUP', 'Any Lookup');
- this.addL2Counter('mali_hwc_L2_READ_LOOKUP', 'Read Lookup');
- this.addL2Counter('mali_hwc_L2_SREAD_LOOKUP', 'Shareable Read Lookup');
- this.addL2Counter('mali_hwc_L2_READ_REPLAY', 'Read Replayed');
- this.addL2Counter('mali_hwc_L2_READ_SNOOP', 'Read Snoop');
- this.addL2Counter('mali_hwc_L2_READ_HIT', 'Read Cache Hit');
- this.addL2Counter('mali_hwc_L2_CLEAN_MISS', 'CleanUnique Miss');
- this.addL2Counter('mali_hwc_L2_WRITE_LOOKUP', 'Write Lookup');
- this.addL2Counter('mali_hwc_L2_SWRITE_LOOKUP', 'Shareable Write Lookup');
- this.addL2Counter('mali_hwc_L2_WRITE_REPLAY', 'Write Replayed');
- this.addL2Counter('mali_hwc_L2_WRITE_SNOOP', 'Write Snoop');
- this.addL2Counter('mali_hwc_L2_WRITE_HIT', 'Write Cache Hit');
- this.addL2Counter('mali_hwc_L2_EXT_READ_FULL', 'ExtRD with BIU Full');
- this.addL2Counter('mali_hwc_L2_EXT_READ_HALF', 'ExtRD with BIU >1/2 Full');
- this.addL2Counter('mali_hwc_L2_EXT_WRITE_FULL', 'ExtWR with BIU Full');
- this.addL2Counter('mali_hwc_L2_EXT_WRITE_HALF', 'ExtWR with BIU >1/2 Full');
-
- this.addL2Counter('mali_hwc_L2_EXT_READ', 'External Read (ExtRD)');
- this.addL2Counter('mali_hwc_L2_EXT_READ_LINE', 'ExtRD (linefill)');
- this.addL2Counter('mali_hwc_L2_EXT_WRITE', 'External Write (ExtWR)');
- this.addL2Counter('mali_hwc_L2_EXT_WRITE_LINE', 'ExtWR (linefill)');
- this.addL2Counter('mali_hwc_L2_EXT_WRITE_SMALL', 'ExtWR (burst size <64B)');
- this.addL2Counter('mali_hwc_L2_EXT_BARRIER', 'External Barrier');
- this.addL2Counter('mali_hwc_L2_EXT_AR_STALL', 'Address Read stalls');
- this.addL2Counter('mali_hwc_L2_EXT_R_BUF_FULL',
- 'Response Buffer full stalls');
- this.addL2Counter('mali_hwc_L2_EXT_RD_BUF_FULL',
- 'Read Data Buffer full stalls');
- this.addL2Counter('mali_hwc_L2_EXT_R_RAW', 'RAW hazard stalls');
- this.addL2Counter('mali_hwc_L2_EXT_W_STALL', 'Write Data stalls');
- this.addL2Counter('mali_hwc_L2_EXT_W_BUF_FULL', 'Write Data Buffer full');
- this.addL2Counter('mali_hwc_L2_EXT_R_W_HAZARD', 'WAW or WAR hazard stalls');
- this.addL2Counter('mali_hwc_L2_TAG_HAZARD', 'Tag hazard replays');
- this.addL2Cycles('mali_hwc_L2_SNOOP_FULL', 'Snoop buffer full');
- this.addL2Cycles('mali_hwc_L2_REPLAY_FULL', 'Replay buffer full');
-
- // DDK events (from X server)
- importer.registerEventHandler('tracing_mark_write:mali_driver',
- LinuxPerfMaliParser.prototype.maliDDKEvent.bind(this));
-
- this.model_ = importer.model_;
- }
-
- LinuxPerfMaliParser.prototype = {
- __proto__: LinuxPerfParser.prototype,
-
- maliDDKOpenSlice: function(pid, tid, ts, func, blockinfo) {
- var thread = this.importer.model_.getOrCreateProcess(pid)
- .getOrCreateThread(tid);
- var funcArgs = /^([\w\d_]*)(?:\(\))?:?\s*(.*)$/.exec(func);
- thread.beginSlice('gpu-driver', funcArgs[1], ts,
- { 'args': funcArgs[2],
- 'blockinfo': blockinfo });
- },
-
- maliDDKCloseSlice: function(pid, tid, ts, args, blockinfo) {
- var thread = this.importer.model_.getOrCreateProcess(pid)
- .getOrCreateThread(tid);
- if (!thread.openSliceCount) {
- // Discard unmatched ends.
- return;
- }
- thread.endSlice(ts);
- },
-
- /**
- * Deduce the format of Mali perf events.
- *
- * @return {RegExp} the regular expression for parsing data when the format
- * is recognized; otherwise null.
- */
- autoDetectLineRE: function(line) {
- // Matches Mali perf events with thread info
- var lineREWithThread =
- /^\s*\(([\w\-]*)\)\s*(\w+):\s*([\w\\\/\.\-]*@\d*):?\s*(.*)$/;
- if (lineREWithThread.test(line))
- return lineREWithThread;
-
- // Matches old-style Mali perf events
- var lineRENoThread = /^s*()(\w+):\s*([\w\\\/.\-]*):?\s*(.*)$/;
- if (lineRENoThread.test(line))
- return lineRENoThread;
- return null;
- },
-
- lineRE: null,
-
- /**
- * Parses maliDDK events and sets up state in the importer.
- * events will come in pairs with a cros_trace_print_enter
- * like this (line broken here for formatting):
- *
- * tracing_mark_write: mali_driver: (mali-012345) cros_trace_print_enter: \
- * gles/src/texture/mali_gles_texture_slave.c@1505: gles2_texturep_upload
- *
- * and a cros_trace_print_exit like this:
- *
- * tracing_mark_write: mali_driver: (mali-012345) cros_trace_print_exit: \
- * gles/src/texture/mali_gles_texture_slave.c@1505:
- */
- maliDDKEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
- if (this.lineRE == null) {
- this.lineRE = this.autoDetectLineRE(eventBase[2]);
- if (this.lineRE == null)
- return false;
- }
- var maliEvent = this.lineRE.exec(eventBase[2]);
- // Old-style Mali perf events have no thread id, so make one.
- var tid = (maliEvent[1] === '' ? 'mali' : maliEvent[1]);
- switch (maliEvent[2]) {
- case 'cros_trace_print_enter':
- this.maliDDKOpenSlice(pid, tid, ts, maliEvent[4],
- maliEvent[3]);
- break;
- case 'cros_trace_print_exit':
- this.maliDDKCloseSlice(pid, tid, ts, [], maliEvent[3]);
- }
- return true;
- },
-
- /*
- * Kernel event support.
- */
-
- dvfsSample: function(counterName, seriesName, ts, s) {
- var value = parseInt(s);
- var counter = this.model_.getOrCreateProcess(0).
- getOrCreateCounter('DVFS', counterName);
- if (counter.numSeries == 0) {
- counter.seriesNames.push(seriesName);
- counter.seriesColors.push(tracing.getStringColorId(counter.name));
- }
- counter.timestamps.push(ts);
- counter.samples.push(value);
- },
-
- dvfsEventEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
- var event = /utilization=(\d+)/.exec(eventBase[5]);
- if (!event)
- return false;
-
- this.dvfsSample('DVFS Utilization', 'utilization', ts, event[1]);
- return true;
- },
-
- dvfsSetClockEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
- var event = /frequency=(\d+)/.exec(eventBase[5]);
- if (!event)
- return false;
-
- this.dvfsSample('DVFS Frequency', 'frequency', ts, event[1]);
- return true;
- },
-
- dvfsSetVoltageEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
- var event = /voltage=(\d+)/.exec(eventBase[5]);
- if (!event)
- return false;
-
- this.dvfsSample('DVFS Voltage', 'voltage', ts, event[1]);
- return true;
- },
-
- hwcSample: function(cat, counterName, seriesName, ts, eventBase) {
- var event = /val=(\d+)/.exec(eventBase[5]);
- if (!event)
- return false;
- var value = parseInt(event[1]);
-
- var counter = this.model_.getOrCreateProcess(0).
- getOrCreateCounter(cat, counterName);
- if (counter.numSeries == 0) {
- counter.seriesNames.push(seriesName);
- counter.seriesColors.push(tracing.getStringColorId(counter.name));
- }
- counter.timestamps.push(ts);
- counter.samples.push(value);
- return true;
- },
-
- /*
- * Job Manager block counters.
- */
- jmSample: function(ctrName, seriesName, ts, eventBase) {
- return this.hwcSample('mali:jm', 'JM: ' + ctrName, seriesName, ts,
- eventBase);
- },
- addJMCounter: function(hwcEventName, hwcTitle) {
- function handler(eventName, cpuNumber, pid, ts, eventBase) {
- return this.jmSample(hwcTitle, 'count', ts, eventBase);
- }
- this.importer.registerEventHandler(hwcEventName, handler.bind(this));
- },
- addJMCycles: function(hwcEventName, hwcTitle) {
- function handler(eventName, cpuNumber, pid, ts, eventBase) {
- return this.jmSample(hwcTitle, 'cycles', ts, eventBase);
- }
- this.importer.registerEventHandler(hwcEventName, handler.bind(this));
- },
-
- /*
- * Tiler block counters.
- */
- tilerSample: function(ctrName, seriesName, ts, eventBase) {
- return this.hwcSample('mali:tiler', 'Tiler: ' + ctrName, seriesName,
- ts, eventBase);
- },
- addTilerCounter: function(hwcEventName, hwcTitle) {
- function handler(eventName, cpuNumber, pid, ts, eventBase) {
- return this.tilerSample(hwcTitle, 'count', ts, eventBase);
- }
- this.importer.registerEventHandler(hwcEventName, handler.bind(this));
- },
- addTilerCycles: function(hwcEventName, hwcTitle) {
- function handler(eventName, cpuNumber, pid, ts, eventBase) {
- return this.tilerSample(hwcTitle, 'cycles', ts, eventBase);
- }
- this.importer.registerEventHandler(hwcEventName, handler.bind(this));
- },
-
- /*
- * Fragment counters.
- */
- fragSample: function(ctrName, seriesName, ts, eventBase) {
- return this.hwcSample('mali:fragment', 'Fragment: ' + ctrName,
- seriesName, ts, eventBase);
- },
- addFragCounter: function(hwcEventName, hwcTitle) {
- function handler(eventName, cpuNumber, pid, ts, eventBase) {
- return this.fragSample(hwcTitle, 'count', ts, eventBase);
- }
- this.importer.registerEventHandler(hwcEventName, handler.bind(this));
- },
- addFragCycles: function(hwcEventName, hwcTitle) {
- function handler(eventName, cpuNumber, pid, ts, eventBase) {
- return this.fragSample(hwcTitle, 'cycles', ts, eventBase);
- }
- this.importer.registerEventHandler(hwcEventName, handler.bind(this));
- },
-
- /*
- * Compute counters.
- */
- computeSample: function(ctrName, seriesName, ts, eventBase) {
- return this.hwcSample('mali:compute', 'Compute: ' + ctrName,
- seriesName, ts, eventBase);
- },
- addComputeCounter: function(hwcEventName, hwcTitle) {
- function handler(eventName, cpuNumber, pid, ts, eventBase) {
- return this.computeSample(hwcTitle, 'count', ts, eventBase);
- }
- this.importer.registerEventHandler(hwcEventName, handler.bind(this));
- },
- addComputeCycles: function(hwcEventName, hwcTitle) {
- function handler(eventName, cpuNumber, pid, ts, eventBase) {
- return this.computeSample(hwcTitle, 'cycles', ts, eventBase);
- }
- this.importer.registerEventHandler(hwcEventName, handler.bind(this));
- },
-
- /*
- * Tripipe counters.
- */
- addTripipeCycles: function(hwcEventName, hwcTitle) {
- function handler(eventName, cpuNumber, pid, ts, eventBase) {
- return this.hwcSample('mali:shader', 'Tripipe: ' + hwcTitle, 'cycles',
- ts, eventBase);
- }
- this.importer.registerEventHandler(hwcEventName, handler.bind(this));
- },
-
- /*
- * Arith counters.
- */
- arithSample: function(ctrName, seriesName, ts, eventBase) {
- return this.hwcSample('mali:arith', 'Arith: ' + ctrName, seriesName, ts,
- eventBase);
- },
- addArithCounter: function(hwcEventName, hwcTitle) {
- function handler(eventName, cpuNumber, pid, ts, eventBase) {
- return this.arithSample(hwcTitle, 'count', ts, eventBase);
- }
- this.importer.registerEventHandler(hwcEventName, handler.bind(this));
- },
- addArithCycles: function(hwcEventName, hwcTitle) {
- function handler(eventName, cpuNumber, pid, ts, eventBase) {
- return this.arithSample(hwcTitle, 'cycles', ts, eventBase);
- }
- this.importer.registerEventHandler(hwcEventName, handler.bind(this));
- },
-
- /*
- * Load/Store counters.
- */
- addLSCounter: function(hwcEventName, hwcTitle) {
- function handler(eventName, cpuNumber, pid, ts, eventBase) {
- return this.hwcSample('mali:ls', 'LS: ' + hwcTitle, 'count', ts,
- eventBase);
- }
- this.importer.registerEventHandler(hwcEventName, handler.bind(this));
- },
-
- /*
- * Texture counters.
- */
- textureSample: function(ctrName, seriesName, ts, eventBase) {
- return this.hwcSample('mali:texture', 'Texture: ' + ctrName,
- seriesName, ts, eventBase);
- },
- addTexCounter: function(hwcEventName, hwcTitle) {
- function handler(eventName, cpuNumber, pid, ts, eventBase) {
- return this.textureSample(hwcTitle, 'count', ts, eventBase);
- }
- this.importer.registerEventHandler(hwcEventName, handler.bind(this));
- },
-
- /*
- * LSC counters.
- */
- addLSCCounter: function(hwcEventName, hwcTitle) {
- function handler(eventName, cpuNumber, pid, ts, eventBase) {
- return this.hwcSample('mali:lsc', 'LSC: ' + hwcTitle, 'count', ts,
- eventBase);
- }
- this.importer.registerEventHandler(hwcEventName, handler.bind(this));
- },
-
- /*
- * TLB counters.
- */
- addAXICounter: function(hwcEventName, hwcTitle) {
- function handler(eventName, cpuNumber, pid, ts, eventBase) {
- return this.hwcSample('mali:axi', 'AXI: ' + hwcTitle, 'count', ts,
- eventBase);
- }
- this.importer.registerEventHandler(hwcEventName, handler.bind(this));
- },
-
- /*
- * MMU counters.
- */
- mmuSample: function(ctrName, seriesName, ts, eventBase) {
- return this.hwcSample('mali:mmu', 'MMU: ' + ctrName, seriesName, ts,
- eventBase);
- },
- addMMUCounter: function(hwcEventName, hwcTitle) {
- function handler(eventName, cpuNumber, pid, ts, eventBase) {
- return this.mmuSample(hwcTitle, 'count', ts, eventBase);
- }
- this.importer.registerEventHandler(hwcEventName, handler.bind(this));
- },
- addMMUCycles: function(hwcEventName, hwcTitle) {
- function handler(eventName, cpuNumber, pid, ts, eventBase) {
- return this.mmuSample(hwcTitle, 'cycles', ts, eventBase);
- }
- this.importer.registerEventHandler(hwcEventName, handler.bind(this));
- },
-
- /*
- * L2 counters.
- */
- l2Sample: function(ctrName, seriesName, ts, eventBase) {
- return this.hwcSample('mali:l2', 'L2: ' + ctrName, seriesName, ts,
- eventBase);
- },
- addL2Counter: function(hwcEventName, hwcTitle) {
- function handler(eventName, cpuNumber, pid, ts, eventBase) {
- return this.l2Sample(hwcTitle, 'count', ts, eventBase);
- }
- this.importer.registerEventHandler(hwcEventName, handler.bind(this));
- },
- addL2Cycles: function(hwcEventName, hwcTitle) {
- function handler(eventName, cpuNumber, pid, ts, eventBase) {
- return this.l2Sample(hwcTitle, 'cycles', ts, eventBase);
- }
- this.importer.registerEventHandler(hwcEventName, handler.bind(this));
- }
- };
-
- LinuxPerfParser.registerSubtype(LinuxPerfMaliParser);
-
- return {
- LinuxPerfMaliParser: LinuxPerfMaliParser
- };
- });
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- /**
- * @fileoverview Parses power events in the Linux event trace format.
- */
- base.require('linux_perf_parser');
- base.exportTo('tracing', function() {
-
- var LinuxPerfParser = tracing.LinuxPerfParser;
-
- /**
- * Parses linux power trace events.
- * @constructor
- */
- function LinuxPerfPowerParser(importer) {
- LinuxPerfParser.call(this, importer);
-
- // NB: old-style power events, deprecated
- importer.registerEventHandler('power_start',
- LinuxPerfPowerParser.prototype.powerStartEvent.bind(this));
- importer.registerEventHandler('power_frequency',
- LinuxPerfPowerParser.prototype.powerFrequencyEvent.bind(this));
-
- importer.registerEventHandler('cpu_frequency',
- LinuxPerfPowerParser.prototype.cpuFrequencyEvent.bind(this));
- importer.registerEventHandler('cpu_idle',
- LinuxPerfPowerParser.prototype.cpuIdleEvent.bind(this));
- }
-
- LinuxPerfPowerParser.prototype = {
- __proto__: LinuxPerfParser.prototype,
-
- cpuStateSlice: function(ts, targetCpuNumber, eventType, cpuState) {
- var targetCpu = this.importer.getOrCreateCpuState(targetCpuNumber);
- var powerCounter;
- if (eventType != '1') {
- this.importer.importError('Don\'t understand power_start events of ' +
- 'type ' + eventType);
- return;
- }
- powerCounter = targetCpu.cpu.getOrCreateCounter('', 'C-State');
- if (powerCounter.numSeries == 0) {
- powerCounter.seriesNames.push('state');
- powerCounter.seriesColors.push(
- tracing.getStringColorId(powerCounter.name + '.' + 'state'));
- }
- powerCounter.timestamps.push(ts);
- powerCounter.samples.push(cpuState);
- },
-
- cpuIdleSlice: function(ts, targetCpuNumber, cpuState) {
- var targetCpu = this.importer.getOrCreateCpuState(targetCpuNumber);
- var powerCounter = targetCpu.cpu.getOrCreateCounter('', 'C-State');
- if (powerCounter.numSeries == 0) {
- powerCounter.seriesNames.push('state');
- powerCounter.seriesColors.push(
- tracing.getStringColorId(powerCounter.name));
- }
- // NB: 4294967295/-1 means an exit from the current state
- if (cpuState != 4294967295)
- powerCounter.samples.push(cpuState);
- else
- powerCounter.samples.push(0);
- powerCounter.timestamps.push(ts);
- },
-
- cpuFrequencySlice: function(ts, targetCpuNumber, powerState) {
- var targetCpu = this.importer.getOrCreateCpuState(targetCpuNumber);
- var powerCounter =
- targetCpu.cpu.getOrCreateCounter('', 'Clock Frequency');
- if (powerCounter.numSeries == 0) {
- powerCounter.seriesNames.push('state');
- powerCounter.seriesColors.push(
- tracing.getStringColorId(powerCounter.name + '.' + 'state'));
- }
- powerCounter.timestamps.push(ts);
- powerCounter.samples.push(powerState);
- },
-
- /**
- * Parses power events and sets up state in the importer.
- */
- powerStartEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
- var event = /type=(\d+) state=(\d) cpu_id=(\d)+/.exec(eventBase[5]);
- if (!event)
- return false;
-
- var targetCpuNumber = parseInt(event[3]);
- var cpuState = parseInt(event[2]);
- this.cpuStateSlice(ts, targetCpuNumber, event[1], cpuState);
- return true;
- },
-
- powerFrequencyEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
- var event = /type=(\d+) state=(\d+) cpu_id=(\d)+/
- .exec(eventBase[5]);
- if (!event)
- return false;
-
- var targetCpuNumber = parseInt(event[3]);
- var powerState = parseInt(event[2]);
- this.cpuFrequencySlice(ts, targetCpuNumber, powerState);
- return true;
- },
-
- cpuFrequencyEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
- var event = /state=(\d+) cpu_id=(\d)+/.exec(eventBase[5]);
- if (!event)
- return false;
-
- var targetCpuNumber = parseInt(event[2]);
- var powerState = parseInt(event[1]);
- this.cpuFrequencySlice(ts, targetCpuNumber, powerState);
- return true;
- },
-
- cpuIdleEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
- var event = /state=(\d+) cpu_id=(\d)+/.exec(eventBase[5]);
- if (!event)
- return false;
-
- var targetCpuNumber = parseInt(event[2]);
- var cpuState = parseInt(event[1]);
- this.cpuIdleSlice(ts, targetCpuNumber, cpuState);
- return true;
- }
- };
-
- LinuxPerfParser.registerSubtype(LinuxPerfPowerParser);
-
- return {
- LinuxPerfPowerParser: LinuxPerfPowerParser
- };
- });
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- /**
- * @fileoverview Parses scheduler events in the Linux event trace format.
- */
- base.require('linux_perf_parser');
- base.exportTo('tracing', function() {
-
- var LinuxPerfParser = tracing.LinuxPerfParser;
-
- /**
- * Parses linux sched trace events.
- * @constructor
- */
- function LinuxPerfSchedParser(importer) {
- LinuxPerfParser.call(this, importer);
-
- importer.registerEventHandler('sched_switch',
- LinuxPerfSchedParser.prototype.schedSwitchEvent.bind(this));
- importer.registerEventHandler('sched_wakeup',
- LinuxPerfSchedParser.prototype.schedWakeupEvent.bind(this));
- }
-
- TestExports = {};
-
- // Matches the sched_switch record
- var schedSwitchRE = new RegExp(
- 'prev_comm=(.+) prev_pid=(\\d+) prev_prio=(\\d+) ' +
- 'prev_state=(\\S\\+?|\\S\\|\\S) ==> ' +
- 'next_comm=(.+) next_pid=(\\d+) next_prio=(\\d+)');
- TestExports.schedSwitchRE = schedSwitchRE;
-
- // Matches the sched_wakeup record
- var schedWakeupRE =
- /comm=(.+) pid=(\d+) prio=(\d+) success=(\d+) target_cpu=(\d+)/;
- TestExports.schedWakeupRE = schedWakeupRE;
-
- LinuxPerfSchedParser.prototype = {
- __proto__: LinuxPerfParser.prototype,
-
- /**
- * Parses scheduler events and sets up state in the importer.
- */
- schedSwitchEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
- var event = schedSwitchRE.exec(eventBase[5]);
- if (!event)
- return false;
-
- var prevState = event[4];
- var nextComm = event[5];
- var nextPid = parseInt(event[6]);
- var nextPrio = parseInt(event[7]);
-
- var cpuState = this.importer.getOrCreateCpuState(cpuNumber);
- cpuState.switchRunningLinuxPid(this.importer,
- prevState, ts, nextPid, nextComm, nextPrio);
- return true;
- },
-
- schedWakeupEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
- var event = schedWakeupRE.exec(eventBase[5]);
- if (!event)
- return false;
-
- var comm = event[1];
- var pid = parseInt(event[2]);
- var prio = parseInt(event[3]);
- this.importer.markPidRunnable(ts, pid, comm, prio);
- return true;
- }
- };
-
- LinuxPerfParser.registerSubtype(LinuxPerfSchedParser);
-
- return {
- LinuxPerfSchedParser: LinuxPerfSchedParser,
- _LinuxPerfSchedParserTestExports: TestExports
- };
- });
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- /**
- * @fileoverview Parses workqueue events in the Linux event trace format.
- */
- base.require('linux_perf_parser');
- base.exportTo('tracing', function() {
-
- var LinuxPerfParser = tracing.LinuxPerfParser;
-
- /**
- * Parses linux workqueue trace events.
- * @constructor
- */
- function LinuxPerfWorkqueueParser(importer) {
- LinuxPerfParser.call(this, importer);
-
- importer.registerEventHandler('workqueue_execute_start',
- LinuxPerfWorkqueueParser.prototype.executeStartEvent.bind(this));
- importer.registerEventHandler('workqueue_execute_end',
- LinuxPerfWorkqueueParser.prototype.executeEndEvent.bind(this));
- }
-
- // Matches the workqueue_execute_start record
- // workqueue_execute_start: work struct c7a8a89c: function MISRWrapper
- var workqueueExecuteStartRE = /work struct (.+): function (\S+)/;
-
- // Matches the workqueue_execute_start record
- // workqueue_execute_end: work struct c7a8a89c
- var workqueueExecuteEndRE = /work struct (.+)/;
-
- LinuxPerfWorkqueueParser.prototype = {
- __proto__: LinuxPerfParser.prototype,
-
- /**
- * Parses workqueue events and sets up state in the importer.
- */
- executeStartEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
- var event = workqueueExecuteStartRE.exec(eventBase[5]);
- if (!event)
- return false;
-
- var kthread = this.importer.getOrCreateKernelThread(eventBase[1]);
- kthread.openSliceTS = ts;
- kthread.openSlice = event[2];
- return true;
- },
-
- executeEndEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
- var event = workqueueExecuteEndRE.exec(eventBase[5]);
- if (!event)
- return false;
-
- var kthread = this.importer.getOrCreateKernelThread(eventBase[1]);
- if (kthread.openSlice) {
- var slice = new tracing.TimelineSlice('', kthread.openSlice,
- tracing.getStringColorId(kthread.openSlice),
- kthread.openSliceTS,
- {},
- ts - kthread.openSliceTS);
-
- kthread.thread.pushSlice(slice);
- }
- kthread.openSlice = undefined;
- return true;
- }
- };
-
- LinuxPerfParser.registerSubtype(LinuxPerfWorkqueueParser);
-
- return {
- LinuxPerfWorkqueueParser: LinuxPerfWorkqueueParser
- };
- });
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- /**
- * @fileoverview Parses trace_marker events that were inserted in the trace by
- * userland.
- */
- base.require('linux_perf_parser');
- base.exportTo('tracing', function() {
-
- var LinuxPerfParser = tracing.LinuxPerfParser;
-
- /**
- * Parses linux trace mark events that were inserted in the trace by userland.
- * @constructor
- */
- function LinuxPerfAndroidParser(importer) {
- LinuxPerfParser.call(this, importer);
-
- importer.registerEventHandler('tracing_mark_write:android',
- LinuxPerfAndroidParser.prototype.traceMarkWriteAndroidEvent.bind(this));
- importer.registerEventHandler('0:android',
- LinuxPerfAndroidParser.prototype.traceMarkWriteAndroidEvent.bind(this));
-
- this.model_ = importer.model_;
- this.ppids_ = {};
- }
-
- function parseArgs(argsString) {
- var args = {};
- if (argsString) {
- var argsArray = argsString.split(';');
- for (var i = 0; i < argsArray.length; ++i) {
- var parts = argsArray[i].split('=');
- if (parts[0])
- args[parts[0]] = parts[1];
- }
- }
- return args;
- }
-
- LinuxPerfAndroidParser.prototype = {
- __proto__: LinuxPerfParser.prototype,
-
- traceMarkWriteAndroidEvent: function(eventName, cpuNumber, pid, ts,
- eventBase, threadName) {
- var eventData = eventBase[2].split('|');
- switch (eventData[0]) {
- case 'B':
- var ppid = parseInt(eventData[1]);
- var name = eventData[2];
- var thread = this.model_.getOrCreateProcess(ppid)
- .getOrCreateThread(pid);
- thread.name = threadName;
- if (!thread.isTimestampValidForBeginOrEnd(ts)) {
- this.model_.importErrors.push(
- 'Timestamps are moving backward.');
- return false;
- }
-
- this.ppids_[pid] = ppid;
- thread.beginSlice(null, name, ts, parseArgs(eventData[3]));
-
- break;
- case 'E':
- var ppid = this.ppids_[pid];
- if (ppid === undefined) {
- // Silently ignore unmatched E events.
- break;
- }
-
- var thread = this.model_.getOrCreateProcess(ppid)
- .getOrCreateThread(pid);
- if (!thread.openSliceCount) {
- // Silently ignore unmatched E events.
- break;
- }
-
- var slice = thread.endSlice(ts);
-
- var args = parseArgs(eventData[3]);
- for (var arg in args) {
- if (slice.args[arg] !== undefined) {
- this.model_.importErrors.push(
- 'Both the B and E events of ' + slice.name +
- 'provided values for argument ' + arg + '. ' +
- 'The value of the E event will be used.');
- }
- slice.args[arg] = args[arg];
- }
-
- break;
- case 'C':
- var ppid = parseInt(eventData[1]);
- var name = eventData[2];
- var value = parseInt(eventData[3]);
-
- var ctr = this.model_.getOrCreateProcess(ppid)
- .getOrCreateCounter(null, name);
- // Initialize the counter's series fields if needed.
- if (ctr.numSeries == 0) {
- ctr.seriesNames.push('value');
- ctr.seriesColors.push(
- tracing.getStringColorId(ctr.name + '.' + 'value'));
- }
-
- // Add the sample value.
- ctr.timestamps.push(ts);
- ctr.samples.push(value);
-
- break;
- default:
- return false;
- }
-
- return true;
- },
- };
-
- LinuxPerfParser.registerSubtype(LinuxPerfAndroidParser);
-
- return {
- LinuxPerfAndroidParser: LinuxPerfAndroidParser
- };
- });
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- /**
- * @fileoverview Imports text files in the Linux event trace format into the
- * timeline model. This format is output both by sched_trace and by Linux's perf
- * tool.
- *
- * This importer assumes the events arrive as a string. The unit tests provide
- * examples of the trace format.
- *
- * Linux scheduler traces use a definition for 'pid' that is different than
- * tracing uses. Whereas tracing uses pid to identify a specific process, a pid
- * in a linux trace refers to a specific thread within a process. Within this
- * file, we the definition used in Linux traces, as it improves the importing
- * code's readability.
- */
- base.require('timeline_model');
- base.require('timeline_color_scheme');
- base.require('linux_perf_bus_parser');
- base.require('linux_perf_clock_parser');
- base.require('linux_perf_cpufreq_parser');
- base.require('linux_perf_drm_parser');
- base.require('linux_perf_exynos_parser');
- base.require('linux_perf_gesture_parser');
- base.require('linux_perf_i915_parser');
- base.require('linux_perf_mali_parser');
- base.require('linux_perf_power_parser');
- base.require('linux_perf_sched_parser');
- base.require('linux_perf_workqueue_parser');
- base.require('linux_perf_android_parser');
-
- base.exportTo('tracing', function() {
- /**
- * Represents the scheduling state for a single thread.
- * @constructor
- */
- function CpuState(cpu) {
- this.cpu = cpu;
- }
-
- CpuState.prototype = {
- __proto__: Object.prototype,
-
- /**
- * Switches the active pid on this Cpu. If necessary, add a TimelineSlice
- * to the cpu representing the time spent on that Cpu since the last call to
- * switchRunningLinuxPid.
- */
- switchRunningLinuxPid: function(importer, prevState, ts, pid, comm, prio) {
- // Generate a slice if the last active pid was not the idle task
- if (this.lastActivePid !== undefined && this.lastActivePid != 0) {
- var duration = ts - this.lastActiveTs;
- var thread = importer.threadsByLinuxPid[this.lastActivePid];
- if (thread)
- name = thread.userFriendlyName;
- else
- name = this.lastActiveComm;
-
- var slice = new tracing.TimelineSlice('', name,
- tracing.getStringColorId(name),
- this.lastActiveTs,
- {
- comm: this.lastActiveComm,
- tid: this.lastActivePid,
- prio: this.lastActivePrio,
- stateWhenDescheduled: prevState
- },
- duration);
- this.cpu.slices.push(slice);
- }
-
- this.lastActiveTs = ts;
- this.lastActivePid = pid;
- this.lastActiveComm = comm;
- this.lastActivePrio = prio;
- }
- };
-
- /**
- * Imports linux perf events into a specified model.
- * @constructor
- */
- function LinuxPerfImporter(model, events) {
- this.importPriority = 2;
- this.model_ = model;
- this.events_ = events;
- this.clockSyncRecords_ = [];
- this.cpuStates_ = {};
- this.kernelThreadStates_ = {};
- this.buildMapFromLinuxPidsToTimelineThreads();
- this.lineNumber = -1;
- this.pseudoThreadCounter = 1;
- this.parsers_ = [];
- this.eventHandlers_ = {};
- }
-
- TestExports = {};
-
- // Matches the default trace record in 3.2 and later (includes irq-info):
- // <idle>-0 [001] d... 1.23: sched_switch
- var lineREWithIRQInfo = new RegExp(
- '^\\s*(.+?)\\s+\\[(\\d+)\\]' + '\\s+[dX.][N.][Hhs.][0-9a-f.]' +
- '\\s+(\\d+\\.\\d+):\\s+(\\S+):\\s(.*)$');
- TestExports.lineREWithIRQInfo = lineREWithIRQInfo;
-
- // Matches the default trace record pre-3.2:
- // <idle>-0 [001] 1.23: sched_switch
- var lineRE = /^\s*(.+?)\s+\[(\d+)\]\s*(\d+\.\d+):\s+(\S+):\s(.*)$/;
- TestExports.lineRE = lineRE;
-
- // Matches the trace_event_clock_sync record
- // 0: trace_event_clock_sync: parent_ts=19581477508
- var traceEventClockSyncRE = /trace_event_clock_sync: parent_ts=(\d+\.?\d*)/;
- TestExports.traceEventClockSyncRE = traceEventClockSyncRE;
-
- // Some kernel trace events are manually classified in slices and
- // hand-assigned a pseudo PID.
- var pseudoKernelPID = 0;
-
- /**
- * Deduce the format of trace data. Linix kernels prior to 3.3 used
- * one format (by default); 3.4 and later used another.
- *
- * @return {string} the regular expression for parsing data when
- * the format is recognized; otherwise null.
- */
- function autoDetectLineRE(line) {
- if (lineREWithIRQInfo.test(line))
- return lineREWithIRQInfo;
- if (lineRE.test(line))
- return lineRE;
- return null;
- };
- TestExports.autoDetectLineRE = autoDetectLineRE;
-
- /**
- * Guesses whether the provided events is a Linux perf string.
- * Looks for the magic string "# tracer" at the start of the file,
- * or the typical task-pid-cpu-timestamp-function sequence of a typical
- * trace's body.
- *
- * @return {boolean} True when events is a linux perf array.
- */
- LinuxPerfImporter.canImport = function(events) {
- if (!(typeof(events) === 'string' || events instanceof String))
- return false;
-
- if (/^# tracer:/.test(events))
- return true;
-
- var m = /^(.+)\n/.exec(events);
- if (m)
- events = m[1];
- if (autoDetectLineRE(events))
- return true;
-
- return false;
- };
-
- LinuxPerfImporter.prototype = {
- __proto__: Object.prototype,
-
- get model() {
- return this.model_;
- },
-
- /**
- * Precomputes a lookup table from linux pids back to existing
- * TimelineThreads. This is used during importing to add information to each
- * timeline thread about whether it was running, descheduled, sleeping, et
- * cetera.
- */
- buildMapFromLinuxPidsToTimelineThreads: function() {
- this.threadsByLinuxPid = {};
- this.model_.getAllThreads().forEach(
- function(thread) {
- this.threadsByLinuxPid[thread.tid] = thread;
- }.bind(this));
- },
-
- /**
- * @return {CpuState} A CpuState corresponding to the given cpuNumber.
- */
- getOrCreateCpuState: function(cpuNumber) {
- if (!this.cpuStates_[cpuNumber]) {
- var cpu = this.model_.getOrCreateCpu(cpuNumber);
- this.cpuStates_[cpuNumber] = new CpuState(cpu);
- }
- return this.cpuStates_[cpuNumber];
- },
-
- /**
- * @return {TimelinThread} A thread corresponding to the kernelThreadName.
- */
- getOrCreateKernelThread: function(kernelThreadName, opt_pid, opt_tid) {
- if (!this.kernelThreadStates_[kernelThreadName]) {
- var pid = opt_pid;
- if (pid == undefined) {
- pid = /.+-(\d+)/.exec(kernelThreadName)[1];
- pid = parseInt(pid, 10);
- }
- var tid = opt_tid;
- if (tid == undefined)
- tid = pid;
-
- var thread = this.model_.getOrCreateProcess(pid).getOrCreateThread(tid);
- thread.name = kernelThreadName;
- this.kernelThreadStates_[kernelThreadName] = {
- pid: pid,
- thread: thread,
- openSlice: undefined,
- openSliceTS: undefined
- };
- this.threadsByLinuxPid[pid] = thread;
- }
- return this.kernelThreadStates_[kernelThreadName];
- },
-
- /**
- * @return {TimelinThread} A pseudo thread corresponding to the
- * threadName. Pseudo threads are for events that we want to break
- * out to a separate timeline but would not otherwise happen.
- * These threads are assigned to pseudoKernelPID and given a
- * unique (incrementing) TID.
- */
- getOrCreatePseudoThread: function(threadName) {
- var thread = this.kernelThreadStates_[threadName];
- if (!thread) {
- thread = this.getOrCreateKernelThread(threadName, pseudoKernelPID,
- this.pseudoThreadCounter);
- this.pseudoThreadCounter++;
- }
- return thread;
- },
-
- /**
- * Imports the data in this.events_ into model_.
- */
- importEvents: function(isSecondaryImport) {
- this.createParsers();
- this.importCpuData();
- if (!this.alignClocks(isSecondaryImport))
- return;
- this.buildMapFromLinuxPidsToTimelineThreads();
- this.buildPerThreadCpuSlicesFromCpuState();
- },
-
- /**
- * Called by the TimelineModel after all other importers have imported their
- * events.
- */
- finalizeImport: function() {
- },
-
- /**
- * Builds the cpuSlices array on each thread based on our knowledge of what
- * each Cpu is doing. This is done only for TimelineThreads that are
- * already in the model, on the assumption that not having any traced data
- * on a thread means that it is not of interest to the user.
- */
- buildPerThreadCpuSlicesFromCpuState: function() {
- // Push the cpu slices to the threads that they run on.
- for (var cpuNumber in this.cpuStates_) {
- var cpuState = this.cpuStates_[cpuNumber];
- var cpu = cpuState.cpu;
-
- for (var i = 0; i < cpu.slices.length; i++) {
- var slice = cpu.slices[i];
-
- var thread = this.threadsByLinuxPid[slice.args.tid];
- if (!thread)
- continue;
- if (!thread.tempCpuSlices)
- thread.tempCpuSlices = [];
- thread.tempCpuSlices.push(slice);
- }
- }
-
- // Create slices for when the thread is not running.
- var runningId = tracing.getColorIdByName('running');
- var runnableId = tracing.getColorIdByName('runnable');
- var sleepingId = tracing.getColorIdByName('sleeping');
- var ioWaitId = tracing.getColorIdByName('iowait');
- this.model_.getAllThreads().forEach(function(thread) {
- if (!thread.tempCpuSlices)
- return;
- var origSlices = thread.tempCpuSlices;
- delete thread.tempCpuSlices;
-
- origSlices.sort(function(x, y) {
- return x.start - y.start;
- });
-
- // Walk the slice list and put slices between each original slice
- // to show when the thread isn't running
- var slices = [];
- if (origSlices.length) {
- var slice = origSlices[0];
- slices.push(new tracing.TimelineSlice('', 'Running', runningId,
- slice.start, {}, slice.duration));
- }
- for (var i = 1; i < origSlices.length; i++) {
- var prevSlice = origSlices[i - 1];
- var nextSlice = origSlices[i];
- var midDuration = nextSlice.start - prevSlice.end;
- if (prevSlice.args.stateWhenDescheduled == 'S') {
- slices.push(new tracing.TimelineSlice('', 'Sleeping', sleepingId,
- prevSlice.end, {}, midDuration));
- } else if (prevSlice.args.stateWhenDescheduled == 'R' ||
- prevSlice.args.stateWhenDescheduled == 'R+') {
- slices.push(new tracing.TimelineSlice('', 'Runnable', runnableId,
- prevSlice.end, {}, midDuration));
- } else if (prevSlice.args.stateWhenDescheduled == 'D') {
- slices.push(new tracing.TimelineSlice(
- '', 'Uninterruptible Sleep', ioWaitId,
- prevSlice.end, {}, midDuration));
- } else if (prevSlice.args.stateWhenDescheduled == 'T') {
- slices.push(new tracing.TimelineSlice('', '__TASK_STOPPED',
- ioWaitId, prevSlice.end, {}, midDuration));
- } else if (prevSlice.args.stateWhenDescheduled == 't') {
- slices.push(new tracing.TimelineSlice('', 'debug', ioWaitId,
- prevSlice.end, {}, midDuration));
- } else if (prevSlice.args.stateWhenDescheduled == 'Z') {
- slices.push(new tracing.TimelineSlice('', 'Zombie', ioWaitId,
- prevSlice.end, {}, midDuration));
- } else if (prevSlice.args.stateWhenDescheduled == 'X') {
- slices.push(new tracing.TimelineSlice('', 'Exit Dead', ioWaitId,
- prevSlice.end, {}, midDuration));
- } else if (prevSlice.args.stateWhenDescheduled == 'x') {
- slices.push(new tracing.TimelineSlice('', 'Task Dead', ioWaitId,
- prevSlice.end, {}, midDuration));
- } else if (prevSlice.args.stateWhenDescheduled == 'W') {
- slices.push(new tracing.TimelineSlice('', 'WakeKill', ioWaitId,
- prevSlice.end, {}, midDuration));
- } else if (prevSlice.args.stateWhenDescheduled == 'D|W') {
- slices.push(new tracing.TimelineSlice(
- '', 'Uninterruptable Sleep | WakeKill', ioWaitId,
- prevSlice.end, {}, midDuration));
- } else {
- throw new Error('Unrecognized state: ') +
- prevSlice.args.stateWhenDescheduled;
- }
-
- slices.push(new tracing.TimelineSlice('', 'Running', runningId,
- nextSlice.start, {}, nextSlice.duration));
- }
- thread.cpuSlices = slices;
- });
- },
-
- /**
- * Walks the slices stored on this.cpuStates_ and adjusts their timestamps
- * based on any alignment metadata we discovered.
- */
- alignClocks: function(isSecondaryImport) {
- if (this.clockSyncRecords_.length == 0) {
- // If this is a secondary import, and no clock syncing records were
- // found, then abort the import. Otherwise, just skip clock alignment.
- if (!isSecondaryImport)
- return true;
-
- // Remove the newly imported CPU slices from the model.
- this.abortImport();
- return false;
- }
-
- // Shift all the slice times based on the sync record.
- var sync = this.clockSyncRecords_[0];
- // NB: parentTS of zero denotes no times-shift; this is
- // used when user and kernel event clocks are identical.
- if (sync.parentTS == 0 || sync.parentTS == sync.perfTS)
- return true;
- var timeShift = sync.parentTS - sync.perfTS;
- for (var cpuNumber in this.cpuStates_) {
- var cpuState = this.cpuStates_[cpuNumber];
- var cpu = cpuState.cpu;
-
- for (var i = 0; i < cpu.slices.length; i++) {
- var slice = cpu.slices[i];
- slice.start = slice.start + timeShift;
- slice.duration = slice.duration;
- }
-
- for (var counterName in cpu.counters) {
- var counter = cpu.counters[counterName];
- for (var sI = 0; sI < counter.timestamps.length; sI++)
- counter.timestamps[sI] = (counter.timestamps[sI] + timeShift);
- }
- }
- for (var kernelThreadName in this.kernelThreadStates_) {
- var kthread = this.kernelThreadStates_[kernelThreadName];
- var thread = kthread.thread;
- thread.shiftTimestampsForward(timeShift);
- }
- return true;
- },
-
- /**
- * Removes any data that has been added to the model because of an error
- * detected during the import.
- */
- abortImport: function() {
- if (this.pushedEventsToThreads)
- throw new Error('Cannot abort, have alrady pushedCpuDataToThreads.');
-
- for (var cpuNumber in this.cpuStates_)
- delete this.model_.cpus[cpuNumber];
- for (var kernelThreadName in this.kernelThreadStates_) {
- var kthread = this.kernelThreadStates_[kernelThreadName];
- var thread = kthread.thread;
- var process = thread.parent;
- delete process.threads[thread.tid];
- delete this.model_.processes[process.pid];
- }
- this.model_.importErrors.push(
- 'Cannot import kernel trace without a clock sync.');
- },
-
- /**
- * Creates an instance of each registered linux perf event parser.
- * This allows the parsers to register handlers for the events they
- * understand. We also register our own special handlers (for the
- * timestamp synchronization markers).
- */
- createParsers: function() {
- // Instantiate the parsers; this will register handlers for known events
- var parserConstructors = tracing.LinuxPerfParser.getSubtypeConstructors();
- for (var i = 0; i < parserConstructors.length; ++i) {
- var parserConstructor = parserConstructors[i];
- this.parsers_.push(new parserConstructor(this));
- }
-
- this.registerEventHandler('tracing_mark_write:trace_event_clock_sync',
- LinuxPerfImporter.prototype.traceClockSyncEvent.bind(this));
- this.registerEventHandler('tracing_mark_write',
- LinuxPerfImporter.prototype.traceMarkingWriteEvent.bind(this));
- // NB: old-style trace markers; deprecated
- this.registerEventHandler('0:trace_event_clock_sync',
- LinuxPerfImporter.prototype.traceClockSyncEvent.bind(this));
- this.registerEventHandler('0',
- LinuxPerfImporter.prototype.traceMarkingWriteEvent.bind(this));
- },
-
- /**
- * Registers a linux perf event parser used by importCpuData.
- */
- registerEventHandler: function(eventName, handler) {
- // TODO(sleffler) how to handle conflicts?
- this.eventHandlers_[eventName] = handler;
- },
-
- /**
- * Records the fact that a pid has become runnable. This data will
- * eventually get used to derive each thread's cpuSlices array.
- */
- markPidRunnable: function(ts, pid, comm, prio) {
- // TODO(nduca): implement this functionality.
- },
-
- importError: function(message) {
- this.model_.importErrors.push('Line ' + (this.lineNumber + 1) +
- ': ' + message);
- },
-
- /**
- * Processes a trace_event_clock_sync event.
- */
- traceClockSyncEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
- var event = /parent_ts=(\d+\.?\d*)/.exec(eventBase[2]);
- if (!event)
- return false;
-
- this.clockSyncRecords_.push({
- perfTS: ts,
- parentTS: event[1] * 1000
- });
- return true;
- },
-
- /**
- * Processes a trace_marking_write event.
- */
- traceMarkingWriteEvent: function(eventName, cpuNumber, pid, ts, eventBase,
- threadName) {
- var event = /^\s*(\w+):\s*(.*)$/.exec(eventBase[5]);
- if (!event) {
- // Check if the event matches events traced by the Android framework
- var tag = eventBase[5].substring(0, 2);
- if (tag == 'B|' || tag == 'E' || tag == 'E|' || tag == 'C|')
- event = [eventBase[5], 'android', eventBase[5]];
- else
- return false;
- }
-
- var writeEventName = eventName + ':' + event[1];
- var threadName = (/(.+)-\d+/.exec(eventBase[1]))[1];
- var handler = this.eventHandlers_[writeEventName];
- if (!handler) {
- this.importError('Unknown trace_marking_write event ' + writeEventName);
- return true;
- }
- return handler(writeEventName, cpuNumber, pid, ts, event, threadName);
- },
-
- /**
- * Walks the this.events_ structure and creates TimelineCpu objects.
- */
- importCpuData: function() {
- this.lines_ = this.events_.split('\n');
-
- var lineRE = null;
- for (this.lineNumber = 0; this.lineNumber < this.lines_.length;
- ++this.lineNumber) {
- var line = this.lines_[this.lineNumber];
- if (line.length == 0 || /^#/.test(line))
- continue;
- if (lineRE == null) {
- lineRE = autoDetectLineRE(line);
- if (lineRE == null) {
- this.importError('Cannot parse line: ' + line);
- continue;
- }
- }
- var eventBase = lineRE.exec(line);
- if (!eventBase) {
- this.importError('Unrecognized line: ' + line);
- continue;
- }
-
- var pid = parseInt((/.+-(\d+)/.exec(eventBase[1]))[1]);
- var cpuNumber = parseInt(eventBase[2]);
- var ts = parseFloat(eventBase[3]) * 1000;
- var eventName = eventBase[4];
-
- var handler = this.eventHandlers_[eventName];
- if (!handler) {
- this.importError('Unknown event ' + eventName + ' (' + line + ')');
- continue;
- }
- if (!handler(eventName, cpuNumber, pid, ts, eventBase))
- this.importError('Malformed ' + eventName + ' event (' + line + ')');
- }
- }
- };
-
- tracing.TimelineModel.registerImporter(LinuxPerfImporter);
-
- return {
- LinuxPerfImporter: LinuxPerfImporter,
- _LinuxPerfImporterTestExports: TestExports
- };
-
- });
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- 'use strict';
-
- /**
- * @fileoverview Provides the Settings class.
- */
- base.exportTo('base', function() {
-
- /**
- * Settings is a simple wrapper around local storage, to make it easier
- * to test classes that have settings.
- *
- * @constructor
- */
- function Settings() {
- if ('G_testRunner' in global) {
- /**
- * In unit tests, use a mock object for storage so we don't change
- * localStorage in tests.
- */
- this.storage_ = FakeLocalStorage();
- } else {
- this.storage_ = localStorage;
- }
- }
-
- Settings.prototype = {
-
- /**
- * Get the setting with the given name.
- *
- * @param {string} key The name of the setting.
- * @param {string} opt_default The default value to return if not set.
- * @param {string} opt_namespace If set, the setting name will be prefixed
- * with this namespace, e.g. "categories.settingName". This is useful for
- * a set of related settings.
- */
- get: function(key, opt_default, opt_namespace) {
- key = this.namespace_(key, opt_namespace);
- if (!(key in this.storage_))
- return opt_default;
- return String(this.storage_[key]);
- },
-
- /**
- * Set the setting with the given name to the given value.
- *
- * @param {string} key The name of the setting.
- * @param {string} value The value of the setting.
- * @param {string} opt_namespace If set, the setting name will be prefixed
- * with this namespace, e.g. "categories.settingName". This is useful for
- * a set of related settings.
- */
- set: function(key, value, opt_namespace) {
- this.storage_[this.namespace_(key, opt_namespace)] = String(value);
- },
-
- /**
- * Return a list of all the keys, or all the keys in the given namespace
- * if one is provided.
- *
- * @param {string} opt_namespace If set, only return settings which
- * begin with this prefix.
- */
- keys: function(opt_namespace) {
- var result = [];
- opt_namespace = opt_namespace || '';
- for (var i = 0; i < this.storage_.length; i++) {
- var key = this.storage_.key(i);
- if (this.isnamespaced_(key, opt_namespace))
- result.push(this.unnamespace_(key, opt_namespace));
- }
- return result;
- },
-
- isnamespaced_: function(key, opt_namespace) {
- return key.indexOf(this.normalize_(opt_namespace)) == 0;
- },
-
- namespace_: function(key, opt_namespace) {
- return this.normalize_(opt_namespace) + key;
- },
-
- unnamespace_: function(key, opt_namespace) {
- return key.replace(this.normalize_(opt_namespace), '');
- },
-
- /**
- * All settings are prefixed with a global namespace to avoid collisions.
- * Settings may also be namespaced with an additional prefix passed into
- * the get, set, and keys methods in order to group related settings.
- * This method makes sure the two namespaces are always set properly.
- */
- normalize_: function(opt_namespace) {
- return Settings.NAMESPACE + (opt_namespace ? opt_namespace + '.' : '');
- }
- };
-
- Settings.NAMESPACE = 'trace-viewer';
-
- return {
- Settings: Settings
- };
- });
-
-
-
- /**
- * Create a Fake localStorage object which just stores to a dictionary
- * instead of actually saving into localStorage. Only used in unit tests.
- * @constructor
- */
- function FakeLocalStorage() {
- return Object.create({}, {
- key: { value: function(i) {
- return Object.keys(this).sort()[i];
- }},
- length: { get: function() {
- return Object.keys(this).length;
- }}
- });
- }
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- 'use strict';
-
- /**
- * @fileoverview TimelineView visualizes TRACE_EVENT events using the
- * tracing.Timeline component and adds in selection summary and control buttons.
- */
- base.requireStylesheet('timeline_view');
-
- base.require('timeline');
- base.require('timeline_analysis');
- base.require('timeline_category_filter_dialog');
- base.require('timeline_filter');
- base.require('timeline_find_control');
- base.require('overlay');
- base.require('trace_event_importer');
- base.require('linux_perf_importer');
- base.require('settings');
-
- base.exportTo('tracing', function() {
-
- /**
- * TimelineView
- * @constructor
- * @extends {HTMLDivElement}
- */
- var TimelineView = base.ui.define('div');
-
- TimelineView.prototype = {
- __proto__: HTMLDivElement.prototype,
-
- decorate: function() {
- this.classList.add('timeline-view');
-
- // Create individual elements.
- this.titleEl_ = document.createElement('div');
- this.titleEl_.textContent = 'Tracing: ';
- this.titleEl_.className = 'title';
-
- this.controlDiv_ = document.createElement('div');
- this.controlDiv_.className = 'control';
-
- this.leftControlsEl_ = document.createElement('div');
- this.leftControlsEl_.className = 'controls';
- this.rightControlsEl_ = document.createElement('div');
- this.rightControlsEl_.className = 'controls';
-
- var spacingEl = document.createElement('div');
- spacingEl.className = 'spacer';
-
- this.timelineContainer_ = document.createElement('div');
- this.timelineContainer_.className = 'timeline-container';
-
- var analysisContainer_ = document.createElement('div');
- analysisContainer_.className = 'analysis-container';
-
- this.analysisEl_ = new tracing.TimelineAnalysisView();
-
- this.dragEl_ = new TimelineDragHandle();
- this.dragEl_.target = analysisContainer_;
-
- this.findCtl_ = new tracing.TimelineFindControl();
- this.findCtl_.controller = new tracing.TimelineFindController();
-
- this.importErrorsButton_ = this.createImportErrorsButton_();
- this.categoryFilterButton_ = this.createCategoryFilterButton_();
- this.categoryFilterButton_.callback =
- this.updateCategoryFilterFromSettings_.bind(this);
- this.metadataButton_ = this.createMetadataButton_();
-
- // Connect everything up.
- this.rightControls.appendChild(this.importErrorsButton_);
- this.rightControls.appendChild(this.categoryFilterButton_);
- this.rightControls.appendChild(this.metadataButton_);
- this.rightControls.appendChild(this.findCtl_);
- this.controlDiv_.appendChild(this.titleEl_);
- this.controlDiv_.appendChild(this.leftControlsEl_);
- this.controlDiv_.appendChild(spacingEl);
- this.controlDiv_.appendChild(this.rightControlsEl_);
- this.appendChild(this.controlDiv_);
-
- this.appendChild(this.timelineContainer_);
- this.appendChild(this.dragEl_);
-
- analysisContainer_.appendChild(this.analysisEl_);
- this.appendChild(analysisContainer_);
-
- this.rightControls.appendChild(this.createHelpButton_());
-
- // Bookkeeping.
- this.onSelectionChangedBoundToThis_ = this.onSelectionChanged_.bind(this);
- document.addEventListener('keypress', this.onKeypress_.bind(this), true);
- },
-
- createImportErrorsButton_: function() {
- var dlg = new tracing.Overlay();
- dlg.classList.add('timeline-view-import-errors-overlay');
- dlg.autoClose = true;
-
- var showEl = document.createElement('div');
- showEl.className = 'timeline-button timeline-view-import-errors-button' +
- ' timeline-view-info-button';
- showEl.textContent = 'Import errors!';
-
- var textEl = document.createElement('div');
- textEl.className = 'info-button-text import-errors-dialog-text';
-
- var containerEl = document.createElement('div');
- containerEl.className = 'info-button-container' +
- 'import-errors-dialog';
-
- containerEl.textContent = 'Errors occurred during import:';
- containerEl.appendChild(textEl);
- dlg.appendChild(containerEl);
-
- var that = this;
- function onClick() {
- dlg.visible = true;
- textEl.textContent = that.model.importErrors.join('\n');
- }
- showEl.addEventListener('click', onClick.bind(this));
-
- function updateVisibility() {
- if (that.model &&
- that.model.importErrors.length)
- showEl.style.display = '';
- else
- showEl.style.display = 'none';
- }
- updateVisibility();
- that.addEventListener('modelChange', updateVisibility);
-
- return showEl;
- },
-
- createCategoryFilterButton_: function() {
- // Set by the embedder of the help button that we create in this function.
- var callback;
-
- var showEl = document.createElement('div');
- showEl.className = 'timeline-button timeline-view-info-button';
- showEl.textContent = 'Categories';
- showEl.__defineSetter__('callback', function(value) {
- callback = value;
- });
-
-
- var that = this;
- function onClick() {
- var dlg = new tracing.TimelineCategoryFilterDialog();
- dlg.model = that.model;
- dlg.settings = that.settings;
- dlg.settingUpdatedCallback = callback;
- dlg.visible = true;
- }
-
- function updateVisibility() {
- if (that.model)
- showEl.style.display = '';
- else
- showEl.style.display = 'none';
- }
- updateVisibility();
- that.addEventListener('modelChange', updateVisibility);
-
- showEl.addEventListener('click', onClick.bind(this));
- return showEl;
- },
-
- createHelpButton_: function() {
- var dlg = new tracing.Overlay();
- dlg.classList.add('timeline-view-help-overlay');
- dlg.autoClose = true;
- dlg.additionalCloseKeyCodes.push('?'.charCodeAt(0));
-
- var showEl = document.createElement('div');
- showEl.className = 'timeline-button timeline-view-help-button';
- showEl.textContent = '?';
-
- var helpTextEl = document.createElement('div');
- helpTextEl.style.whiteSpace = 'pre';
- helpTextEl.style.fontFamily = 'monospace';
- dlg.appendChild(helpTextEl);
-
- function onClick(e) {
- dlg.visible = true;
- if (this.timeline_)
- helpTextEl.textContent = this.timeline_.keyHelp;
- else
- helpTextEl.textContent = 'No content loaded. For interesting help,' +
- ' load something.';
-
- // Stop event so it doesn't trigger new click listener on document.
- e.stopPropagation();
- return false;
- }
-
- showEl.addEventListener('click', onClick.bind(this));
-
- return showEl;
- },
-
- createMetadataButton_: function() {
- var dlg = new tracing.Overlay();
- dlg.classList.add('timeline-view-metadata-overlay');
- dlg.autoClose = true;
-
- var showEl = document.createElement('div');
- showEl.className = 'timeline-button timeline-view-metadata-button' +
- ' timeline-view-info-button';
- showEl.textContent = 'Metadata';
-
- var textEl = document.createElement('div');
- textEl.className = 'info-button-text metadata-dialog-text';
-
- var containerEl = document.createElement('div');
- containerEl.className = 'info-button-container metadata-dialog';
-
- containerEl.textContent = 'Metadata Info:';
- containerEl.appendChild(textEl);
- dlg.appendChild(containerEl);
-
- var that = this;
- function onClick() {
- dlg.visible = true;
-
- var metadataStrings = [];
-
- var model = that.model;
- for (var data in model.metadata) {
- metadataStrings.push(JSON.stringify(model.metadata[data].name) +
- ': ' + JSON.stringify(model.metadata[data].value));
- }
- textEl.textContent = metadataStrings.join('\n');
- }
- showEl.addEventListener('click', onClick.bind(this));
-
- function updateVisibility() {
- if (that.model &&
- that.model.metadata.length)
- showEl.style.display = '';
- else
- showEl.style.display = 'none';
- }
- updateVisibility();
- that.addEventListener('modelChange', updateVisibility);
-
- return showEl;
- },
-
- get leftControls() {
- return this.leftControlsEl_;
- },
-
- get rightControls() {
- return this.rightControlsEl_;
- },
-
- get title() {
- return this.titleEl_.textContent.substring(
- this.titleEl_.textContent.length - 2);
- },
-
- set title(text) {
- this.titleEl_.textContent = text + ':';
- },
-
- set traceData(traceData) {
- this.model = new tracing.TimelineModel(traceData);
- },
-
- get model() {
- if (this.timeline_)
- return this.timeline_.model;
- return undefined;
- },
-
- set model(model) {
- var modelInstanceChanged = model != this.model;
- var modelValid = model && model.minTimestamp !== undefined;
-
- // Remove old timeline if the model has completely changed.
- if (modelInstanceChanged) {
- this.timelineContainer_.textContent = '';
- if (this.timeline_) {
- this.timeline_.removeEventListener(
- 'selectionChange', this.onSelectionChangedBoundToThis_);
- this.timeline_.detach();
- this.timeline_ = undefined;
- this.findCtl_.controller.timeline = undefined;
- }
- }
-
- // Create new timeline if needed.
- if (modelValid && !this.timeline_) {
- this.timeline_ = new tracing.Timeline();
- this.timeline_.focusElement =
- this.focusElement_ ? this.focusElement_ : this.parentElement;
- this.timelineContainer_.appendChild(this.timeline_);
- this.findCtl_.controller.timeline = this.timeline_;
- this.timeline_.addEventListener(
- 'selectionChange', this.onSelectionChangedBoundToThis_);
- this.updateCategoryFilterFromSettings_();
- }
-
- // Set the model.
- if (modelValid)
- this.timeline_.model = model;
- base.dispatchSimpleEvent(this, 'modelChange');
-
- // Do things that are selection specific
- if (modelInstanceChanged)
- this.onSelectionChanged_();
- },
-
- get timeline() {
- return this.timeline_;
- },
-
- get settings() {
- if (!this.settings_)
- this.settings_ = new base.Settings();
- return this.settings_;
- },
-
- /**
- * Sets the element whose focus state will determine whether
- * to respond to keybaord input.
- */
- set focusElement(value) {
- this.focusElement_ = value;
- if (this.timeline_)
- this.timeline_.focusElement = value;
- },
-
- /**
- * @return {Element} The element whose focused state determines
- * whether to respond to keyboard inputs.
- * Defaults to the parent element.
- */
- get focusElement() {
- if (this.focusElement_)
- return this.focusElement_;
- return this.parentElement;
- },
-
- /**
- * @return {boolean} Whether the current timeline is attached to the
- * document.
- */
- get isAttachedToDocument_() {
- var cur = this;
- while (cur.parentNode)
- cur = cur.parentNode;
- return cur == this.ownerDocument;
- },
-
- get listenToKeys_() {
- if (!this.isAttachedToDocument_)
- return;
- if (!this.focusElement_)
- return true;
- if (this.focusElement.tabIndex >= 0)
- return document.activeElement == this.focusElement;
- return true;
- },
-
- onKeypress_: function(e) {
- if (!this.listenToKeys_)
- return;
-
- if (event.keyCode == '/'.charCodeAt(0)) { // / key
- this.findCtl_.focus();
- event.preventDefault();
- return;
- } else if (e.keyCode == '?'.charCodeAt(0)) {
- this.querySelector('.timeline-view-help-button').click();
- e.preventDefault();
- }
- },
-
- beginFind: function() {
- if (this.findInProgress_)
- return;
- this.findInProgress_ = true;
- var dlg = tracing.TimelineFindControl();
- dlg.controller = new tracing.TimelineFindController();
- dlg.controller.timeline = this.timeline;
- dlg.visible = true;
- dlg.addEventListener('close', function() {
- this.findInProgress_ = false;
- }.bind(this));
- dlg.addEventListener('findNext', function() {
- });
- dlg.addEventListener('findPrevious', function() {
- });
- },
-
- onSelectionChanged_: function(e) {
- var oldScrollTop = this.timelineContainer_.scrollTop;
-
- var selection = this.timeline_ ?
- this.timeline_.selection :
- new tracing.TimelineSelection();
- this.analysisEl_.selection = selection;
- this.timelineContainer_.scrollTop = oldScrollTop;
- },
-
- updateCategoryFilterFromSettings_: function() {
- if (!this.timeline_)
- return;
-
- // Get the disabled categories from settings.
- var categories = this.settings.keys('categories');
- var disabledCategories = [];
- for (var i = 0; i < categories.length; i++) {
- if (this.settings.get(categories[i], 'true', 'categories') == 'false')
- disabledCategories.push(categories[i]);
- }
-
- this.timeline_.categoryFilter =
- new tracing.TimelineCategoryFilter(disabledCategories);
- }
- };
-
- /**
- * Timeline Drag Handle
- * Detects when user clicks handle determines new height of container based
- * on user's vertical mouse move and resizes the target.
- * @constructor
- * @extends {HTMLDivElement}
- * You will need to set target to be the draggable element
- */
- var TimelineDragHandle = base.ui.define('div');
-
- TimelineDragHandle.prototype = {
- __proto__: HTMLDivElement.prototype,
-
- decorate: function() {
- this.className = 'timeline-drag-handle';
- this.lastMousePosY = 0;
- this.dragAnalysis = this.dragAnalysis.bind(this);
- this.onMouseUp = this.onMouseUp.bind(this);
- this.addEventListener('mousedown', this.onMouseDown);
- },
-
- dragAnalysis: function(e) {
- // Compute the difference in height position.
- var dy = this.lastMousePosY - e.clientY;
- // If style is not set, start off with computed height.
- if (!this.target.style.height)
- this.target.style.height = window.getComputedStyle(this.target).height;
- // Calculate new height of the container.
- this.target.style.height = parseInt(this.target.style.height) + dy + 'px';
- this.lastMousePosY = e.clientY;
- },
-
- onMouseDown: function(e) {
- this.lastMousePosY = e.clientY;
- document.addEventListener('mousemove', this.dragAnalysis);
- document.addEventListener('mouseup', this.onMouseUp);
- e.stopPropagation();
- return false;
- },
-
- onMouseUp: function(e) {
- document.removeEventListener('mousemove', this.dragAnalysis);
- document.removeEventListener('mouseup', this.onMouseUp);
- }
- };
-
- return {
- TimelineView: TimelineView
- };
- });
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
-
- /**
- * @fileoverview State and UI for trace data collection.
- */
- base.requireStylesheet('tracing_controller');
- base.require('event_target');
- base.exportTo('tracing', function() {
-
- /**
- * The tracing controller is responsible for talking to tracing_ui.cc in
- * chrome
- * @constructor
- * @param {function(String, opt_Array.<String>} Function to be used to send
- * data to chrome.
- */
- function TracingController(sendFn) {
- this.sendFn_ = sendFn;
- this.overlay_ = document.createElement('div');
- this.overlay_.className = 'tracing-overlay';
-
- base.ui.decorate(this.overlay_, tracing.Overlay);
-
- this.statusDiv_ = document.createElement('div');
- this.overlay_.appendChild(this.statusDiv_);
-
- this.bufferPercentDiv_ = document.createElement('div');
- this.overlay_.appendChild(this.bufferPercentDiv_);
-
- this.stopButton_ = document.createElement('button');
- this.stopButton_.onclick = this.endTracing.bind(this);
- this.stopButton_.textContent = 'Stop tracing';
- this.overlay_.appendChild(this.stopButton_);
-
- this.traceEvents_ = [];
- this.systemTraceEvents_ = [];
-
- this.onKeydownBoundToThis_ = this.onKeydown_.bind(this);
- this.onKeypressBoundToThis_ = this.onKeypress_.bind(this);
-
- this.supportsSystemTracing_ = base.isChromeOS;
-
- if (this.sendFn_)
- this.sendFn_('tracingControllerInitialized');
- }
-
- TracingController.prototype = {
- __proto__: base.EventTarget.prototype,
-
- gpuInfo_: undefined,
- clientInfo_: undefined,
- tracingEnabled_: false,
- tracingEnding_: false,
- systemTraceDataFilename_: undefined,
-
- get supportsSystemTracing() {
- return this.supportsSystemTracing_;
- },
-
- onRequestBufferPercentFullComplete: function(percent_full) {
- if (!this.overlay_.visible)
- return;
-
- window.setTimeout(this.beginRequestBufferPercentFull_.bind(this), 250);
-
- this.bufferPercentDiv_.textContent = 'Buffer usage: ' +
- Math.round(100 * percent_full) + '%';
- },
-
- /**
- * Begin requesting the buffer fullness
- */
- beginRequestBufferPercentFull_: function() {
- this.sendFn_('beginRequestBufferPercentFull');
- },
-
- /**
- * Called by info_view to empty the trace buffer
- *
- * |opt_trace_categories| is a comma-delimited list of category wildcards.
- * A category can have an optional '-' prefix to make it an excluded
- * category. All the same rules apply above, so for example, having both
- * included and excluded categories in the same list would not be
- * supported.
- *
- * Example: beginTracing("test_MyTest*");
- * Example: beginTracing("test_MyTest*,test_OtherStuff");
- * Example: beginTracing("-excluded_category1,-excluded_category2");
- */
- beginTracing: function(opt_systemTracingEnabled, opt_trace_categories) {
- if (this.tracingEnabled_)
- throw new Error('Tracing already begun.');
-
- this.stopButton_.hidden = false;
- this.statusDiv_.textContent = 'Tracing active.';
- this.overlay_.visible = true;
- this.overlay_.defaultClickShouldClose = false;
-
- this.tracingEnabled_ = true;
-
- console.log('Beginning to trace...');
- this.statusDiv_.textContent = 'Tracing active.';
-
- this.traceEvents_ = [];
- this.systemTraceEvents_ = [];
- this.sendFn_(
- 'beginTracing',
- [
- opt_systemTracingEnabled || false,
- opt_trace_categories || '-test_*'
- ]
- );
- this.beginRequestBufferPercentFull_();
-
- var e = new base.Event('traceBegun');
- e.events = this.traceEvents_;
- this.dispatchEvent(e);
-
- e = new base.Event('traceEventsChanged');
- e.numEvents = this.traceEvents_.length;
- this.dispatchEvent(e);
-
- window.addEventListener('keypress', this.onKeypressBoundToThis_);
- window.addEventListener('keydown', this.onKeydownBoundToThis_);
- },
-
- onKeydown_: function(e) {
- if (e.keyCode == 27) {
- this.endTracing();
- }
- },
-
- onKeypress_: function(e) {
- if (e.keyIdentifier == 'Enter') {
- this.endTracing();
- }
- },
-
- /**
- * Called from gpu c++ code when ClientInfo is updated.
- */
- onClientInfoUpdate: function(clientInfo) {
- this.clientInfo_ = clientInfo;
- },
-
- /**
- * Called from gpu c++ code when GPU Info is updated.
- */
- onGpuInfoUpdate: function(gpuInfo) {
- this.gpuInfo_ = gpuInfo;
- },
-
- /**
- * Checks whether tracing is enabled
- */
- get isTracingEnabled() {
- return this.tracingEnabled_;
- },
-
- /**
- * Gets the currently traced events. If tracing is active, then
- * this can change on the fly.
- */
- get traceEvents() {
- return this.traceEvents_;
- },
-
- /**
- * Called by tracing c++ code when new trace data arrives.
- */
- onTraceDataCollected: function(events) {
- this.statusDiv_.textContent = 'Processing trace...';
- this.traceEvents_.push.apply(this.traceEvents_, events);
- },
-
- /**
- * Called to finish tracing and update all views.
- */
- endTracing: function() {
- if (!this.tracingEnabled_) throw new Error('Tracing not begun.');
- if (this.tracingEnding_) return;
- this.tracingEnding_ = true;
-
- this.statusDiv_.textContent = 'Ending trace...';
- console.log('Finishing trace');
- this.statusDiv_.textContent = 'Downloading trace data...';
- this.stopButton_.hidden = true;
- // delay sending endTracingAsync until we get a chance to
- // update the screen...
- var that = this;
- window.setTimeout(function() {
- that.sendFn_('endTracingAsync');
- }, 100);
- },
-
- /**
- * Called by the browser when all processes complete tracing.
- */
- onEndTracingComplete: function() {
- window.removeEventListener('keydown', this.onKeydownBoundToThis_);
- window.removeEventListener('keypress', this.onKeypressBoundToThis_);
- this.overlay_.visible = false;
- this.tracingEnabled_ = false;
- this.tracingEnding_ = false;
- console.log('onEndTracingComplete p1 with ' +
- this.traceEvents_.length + ' events.');
- var e = new base.Event('traceEnded');
- e.events = this.traceEvents_;
- this.dispatchEvent(e);
- },
-
- /**
- * Called by tracing c++ code when new system trace data arrives.
- */
- onSystemTraceDataCollected: function(events) {
- console.log('onSystemTraceDataCollected with ' +
- events.length + ' chars of data.');
- this.systemTraceEvents_ = events;
- },
-
- /**
- * Gets the currentl system trace events. If tracing is active, then
- * this can change on the fly.
- */
- get systemTraceEvents() {
- return this.systemTraceEvents_;
- },
-
- /**
- * Tells browser to put up a load dialog and load the trace file
- */
- beginLoadTraceFile: function() {
- this.sendFn_('loadTraceFile');
- },
-
- /**
- * Called by the browser when a trace file is loaded.
- */
- onLoadTraceFileComplete: function(data) {
- if (data.traceEvents) {
- this.traceEvents_ = data.traceEvents;
- } else { // path for loading traces saved without metadata
- if (!data.length)
- console.log('Expected an array when loading the trace file');
- else
- this.traceEvents_ = data;
- }
-
- if (data.systemTraceEvents)
- this.systemTraceEvents_ = data.systemTraceEvents;
- else
- this.systemTraceEvents_ = [];
-
- var e = new base.Event('loadTraceFileComplete');
- e.events = this.traceEvents_;
- this.dispatchEvent(e);
- },
-
- /**
- * Called by the browser when loading a trace file was canceled.
- */
- onLoadTraceFileCanceled: function() {
- base.dispatchSimpleEvent(this, 'loadTraceFileCanceled');
- },
-
- /**
- * Tells browser to put up a save dialog and save the trace file
- */
- beginSaveTraceFile: function(traceEvents, systemTraceEvents) {
- var data = {
- traceEvents: this.traceEvents_,
- systemTraceEvents: this.systemTraceEvents_,
- clientInfo: this.clientInfo_,
- gpuInfo: this.gpuInfo_
- };
- this.sendFn_('saveTraceFile', [JSON.stringify(data)]);
- },
-
- /**
- * Called by the browser when a trace file is saveed.
- */
- onSaveTraceFileComplete: function() {
- base.dispatchSimpleEvent(this, 'saveTraceFileComplete');
- },
-
- /**
- * Called by the browser when saving a trace file was canceled.
- */
- onSaveTraceFileCanceled: function() {
- base.dispatchSimpleEvent(this, 'saveTraceFileCanceled');
- }
- };
- return {
- TracingController: TracingController
- };
- });
-
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
-
- 'use strict';
-
- /**
- * @fileoverview ProfilingView glues the TimelineView control to
- * TracingController.
- */
- base.requireStylesheet('profiling_view');
- base.require('timeline_view');
- base.require('tracing_controller');
- base.exportTo('tracing', function() {
- /**
- * ProfilingView
- * @constructor
- * @extends {HTMLDivElement}
- */
- var ProfilingView = base.ui.define('div');
-
- ProfilingView.prototype = {
- __proto__: HTMLDivElement.prototype,
-
- traceEvents_: [],
- systemTraceEvents_: [],
-
- decorate: function() {
- this.classList.add('profiling-view');
-
- // make the <list>/add/save/record element
- this.recordBn_ = document.createElement('button');
- this.recordBn_.className = 'record';
- this.recordBn_.textContent = 'Record';
- this.recordBn_.addEventListener('click', this.onRecord_.bind(this));
-
- this.saveBn_ = document.createElement('button');
- this.saveBn_.textContent = 'Save';
- this.saveBn_.addEventListener('click', this.onSave_.bind(this));
-
- this.loadBn_ = document.createElement('button');
- this.loadBn_.textContent = 'Load';
- this.loadBn_.addEventListener('click', this.onLoad_.bind(this));
-
- this.systemTracingBn_ = document.createElement('input');
- this.systemTracingBn_.type = 'checkbox';
- this.systemTracingBn_.checked = false;
-
- this.systemTracingLabelEl_ = document.createElement('label');
- this.systemTracingLabelEl_.textContent = 'System events';
- this.systemTracingLabelEl_.appendChild(this.systemTracingBn_);
- this.systemTracingLabelEl_.style.display = 'none';
- this.systemTracingLabelEl_.style.marginLeft = '16px';
-
- this.timelineView_ = new tracing.TimelineView();
- this.timelineView_.leftControls.appendChild(this.recordBn_);
- this.timelineView_.leftControls.appendChild(this.saveBn_);
- this.timelineView_.leftControls.appendChild(this.loadBn_);
- this.timelineView_.leftControls.appendChild(this.systemTracingLabelEl_);
-
- this.appendChild(this.timelineView_);
-
- document.addEventListener('keypress', this.onKeypress_.bind(this));
-
- this.refresh_();
- },
-
- didSetTracingController_: function(value, oldValue) {
- if (oldValue)
- throw new Error('Can only set tracing controller once.');
-
- if (this.tracingController_.supportsSystemTracing) {
- this.systemTracingLabelEl_.style.display = 'block';
- this.systemTracingBn_.checked = true;
- } else {
- this.systemTracingLabelEl_.style.display = 'none';
- }
-
- this.refresh_();
- },
-
- refresh_: function() {
- if (!this.tracingController_)
- return;
-
- var traceEvents = this.tracingController_.traceEvents;
- var hasEvents = traceEvents && traceEvents.length;
-
- this.saveBn_.disabled = !hasEvents;
-
- if (!hasEvents) return;
-
- var traces = [traceEvents];
- if (this.tracingController_.systemTraceEvents.length)
- traces.push(this.tracingController_.systemTraceEvents);
-
- var m = new tracing.TimelineModel();
- m.importTraces(traces, true);
- this.timelineView_.model = m;
- },
-
- onKeypress_: function(event) {
- if (event.keyCode == 114 && !this.tracingController_.isTracingEnabled &&
- document.activeElement.nodeName != 'INPUT') {
- this.onRecord_();
- }
- },
-
- get timelineView() {
- return this.timelineView_;
- },
-
- ///////////////////////////////////////////////////////////////////////////
-
- onRecord_: function() {
- var that = this;
- var tc = this.tracingController_;
- tc.beginTracing(this.systemTracingBn_.checked);
- function response() {
- that.refresh_();
- setTimeout(function() {
- tc.removeEventListener('traceEnded', response);
- }, 0);
- }
- tc.addEventListener('traceEnded', response);
- },
-
- ///////////////////////////////////////////////////////////////////////////
-
- onSave_: function() {
- this.overlayEl_ = new tracing.Overlay();
- this.overlayEl_.className = 'profiling-overlay';
-
- var labelEl = document.createElement('div');
- labelEl.className = 'label';
- labelEl.textContent = 'Saving...';
- this.overlayEl_.appendChild(labelEl);
- this.overlayEl_.visible = true;
-
- var that = this;
- var tc = this.tracingController_;
- function response() {
- that.overlayEl_.visible = false;
- that.overlayEl_ = undefined;
- setTimeout(function() {
- tc.removeEventListener('saveTraceFileComplete', response);
- tc.removeEventListener('saveTraceFileCanceled', response);
- }, 0);
- }
- tc.addEventListener('saveTraceFileComplete', response);
- tc.addEventListener('saveTraceFileCanceled', response);
- tc.beginSaveTraceFile();
- },
-
- ///////////////////////////////////////////////////////////////////////////
-
- onLoad_: function() {
- this.overlayEl_ = new tracing.Overlay();
- this.overlayEl_.className = 'profiling-overlay';
-
- var labelEl = document.createElement('div');
- labelEl.className = 'label';
- labelEl.textContent = 'Loading...';
- this.overlayEl_.appendChild(labelEl);
- this.overlayEl_.visible = true;
-
- var that = this;
- var tc = this.tracingController_;
- this.tracingController_.beginLoadTraceFile();
- function response(e) {
- that.overlayEl_.visible = false;
- that.overlayEl_ = undefined;
- if (e.type == 'loadTraceFileComplete')
- that.refresh_();
- setTimeout(function() {
- tc.removeEventListener('loadTraceFileComplete', response);
- tc.removeEventListener('loadTraceFileCanceled', response);
- }, 0);
- }
-
- tc.addEventListener('loadTraceFileComplete', response);
- tc.addEventListener('loadTraceFileCanceled', response);
- }
- };
-
- base.defineProperty(ProfilingView, 'tracingController', base.PropertyKind.JS,
- ProfilingView.prototype.didSetTracingController_);
-
- return {
- ProfilingView: ProfilingView
- };
- });
-
-
-
- var tracingController;
- var profilingView; // Made global for debugging purposes only.
-
- document.addEventListener('DOMContentLoaded', function() {
- tracingController = new tracing.TracingController(
- chrome.send.bind(chrome));
-
- profilingView = document.body.querySelector('#profiling-view');
- base.ui.decorate(profilingView, tracing.ProfilingView);
- profilingView.tracingController = tracingController;
- });
-