| Index: chrome/tools/test/reference_build/chrome_linux/resources/inspector/profiler_processor.js
|
| diff --git a/chrome/tools/test/reference_build/chrome_linux/resources/inspector/profiler_processor.js b/chrome/tools/test/reference_build/chrome_linux/resources/inspector/profiler_processor.js
|
| index e2903501f957d0bf21c81ed49ebee447f1fcb09a..65cbeee899c698b2fed7f372a67e185bc6f32d54 100644
|
| --- a/chrome/tools/test/reference_build/chrome_linux/resources/inspector/profiler_processor.js
|
| +++ b/chrome/tools/test/reference_build/chrome_linux/resources/inspector/profiler_processor.js
|
| @@ -11,6 +11,95 @@ goog.provide('devtools.profiler.Processor');
|
|
|
|
|
| /**
|
| + * Creates a Profile View builder object compatible with WebKit Profiler UI.
|
| + *
|
| + * @param {number} samplingRate Number of ms between profiler ticks.
|
| + * @constructor
|
| + */
|
| +devtools.profiler.WebKitViewBuilder = function(samplingRate) {
|
| + devtools.profiler.ViewBuilder.call(this, samplingRate);
|
| +};
|
| +goog.inherits(devtools.profiler.WebKitViewBuilder,
|
| + devtools.profiler.ViewBuilder);
|
| +
|
| +
|
| +/**
|
| + * @override
|
| + */
|
| +devtools.profiler.WebKitViewBuilder.prototype.createViewNode = function(
|
| + funcName, totalTime, selfTime, head) {
|
| + return new devtools.profiler.WebKitViewNode(
|
| + funcName, totalTime, selfTime, head);
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Constructs a Profile View node object for displaying in WebKit Profiler UI.
|
| + *
|
| + * @param {string} internalFuncName A fully qualified function name.
|
| + * @param {number} totalTime Amount of time that application spent in the
|
| + * corresponding function and its descendants (not that depending on
|
| + * profile they can be either callees or callers.)
|
| + * @param {number} selfTime Amount of time that application spent in the
|
| + * corresponding function only.
|
| + * @param {devtools.profiler.ProfileView.Node} head Profile view head.
|
| + * @constructor
|
| + */
|
| +devtools.profiler.WebKitViewNode = function(
|
| + internalFuncName, totalTime, selfTime, head) {
|
| + devtools.profiler.ProfileView.Node.call(this,
|
| + internalFuncName, totalTime, selfTime, head);
|
| + this.initFuncInfo_();
|
| + this.callUID = internalFuncName;
|
| +};
|
| +goog.inherits(devtools.profiler.WebKitViewNode,
|
| + devtools.profiler.ProfileView.Node);
|
| +
|
| +
|
| +/**
|
| + * RegEx for stripping V8's prefixes of compiled functions.
|
| + */
|
| +devtools.profiler.WebKitViewNode.FUNC_NAME_STRIP_RE =
|
| + /^(?:LazyCompile|Function): (.*)$/;
|
| +
|
| +
|
| +/**
|
| + * RegEx for extracting script source URL and line number.
|
| + */
|
| +devtools.profiler.WebKitViewNode.FUNC_NAME_PARSE_RE =
|
| + /^([^ ]+) (.*):(\d+)( \{\d+\})?$/;
|
| +
|
| +
|
| +/**
|
| + * Inits 'functionName', 'url', and 'lineNumber' fields using 'internalFuncName'
|
| + * field.
|
| + * @private
|
| + */
|
| +devtools.profiler.WebKitViewNode.prototype.initFuncInfo_ = function() {
|
| + var nodeAlias = devtools.profiler.WebKitViewNode;
|
| + this.functionName = this.internalFuncName;
|
| +
|
| + var strippedName = nodeAlias.FUNC_NAME_STRIP_RE.exec(this.functionName);
|
| + if (strippedName) {
|
| + this.functionName = strippedName[1];
|
| + }
|
| +
|
| + var parsedName = nodeAlias.FUNC_NAME_PARSE_RE.exec(this.functionName);
|
| + if (parsedName) {
|
| + this.functionName = parsedName[1];
|
| + if (parsedName[4]) {
|
| + this.functionName += parsedName[4];
|
| + }
|
| + this.url = parsedName[2];
|
| + this.lineNumber = parsedName[3];
|
| + } else {
|
| + this.url = '';
|
| + this.lineNumber = 0;
|
| + }
|
| +};
|
| +
|
| +
|
| +/**
|
| * Ancestor of a profile object that leaves out only JS-related functions.
|
| * @constructor
|
| */
|
| @@ -21,165 +110,329 @@ goog.inherits(devtools.profiler.JsProfile, devtools.profiler.Profile);
|
|
|
|
|
| /**
|
| + * RegExp that leaves only JS functions.
|
| * @type {RegExp}
|
| */
|
| devtools.profiler.JsProfile.JS_FUNC_RE = /^(LazyCompile|Function|Script):/;
|
|
|
| +/**
|
| + * RegExp that filters out native code (ending with "native src.js:xxx").
|
| + * @type {RegExp}
|
| + */
|
| +devtools.profiler.JsProfile.JS_NATIVE_FUNC_RE = /\ native\ \w+\.js:\d+$/;
|
| +
|
| +/**
|
| + * RegExp that filters out native scripts.
|
| + * @type {RegExp}
|
| + */
|
| +devtools.profiler.JsProfile.JS_NATIVE_SCRIPT_RE = /^Script:\ native/;
|
| +
|
| +/**
|
| + * RegExp that filters out devtools functions. See inject.js and
|
| + * inject_dispatch.js.
|
| + * @type {RegExp}
|
| + */
|
| +devtools.profiler.JsProfile.JS_DEVTOOLS_FUNC_RE =
|
| + /^\w+:\ devtools(\$\$|\.Injected)/;
|
| +
|
|
|
| /**
|
| * @override
|
| */
|
| devtools.profiler.JsProfile.prototype.skipThisFunction = function(name) {
|
| - return !devtools.profiler.JsProfile.JS_FUNC_RE.test(name);
|
| + return !devtools.profiler.JsProfile.JS_FUNC_RE.test(name) ||
|
| + // To profile V8's natives comment out two lines below and '||' above.
|
| + devtools.profiler.JsProfile.JS_NATIVE_FUNC_RE.test(name) ||
|
| + devtools.profiler.JsProfile.JS_NATIVE_SCRIPT_RE.test(name) ||
|
| + devtools.profiler.JsProfile.JS_DEVTOOLS_FUNC_RE.test(name);
|
| };
|
|
|
|
|
| /**
|
| * Profiler processor. Consumes profiler log and builds profile views.
|
| + *
|
| + * @param {function(devtools.profiler.ProfileView)} newProfileCallback Callback
|
| + * that receives a new processed profile.
|
| * @constructor
|
| */
|
| devtools.profiler.Processor = function() {
|
| + devtools.profiler.LogReader.call(this, {
|
| + 'code-creation': {
|
| + parsers: [null, this.createAddressParser('code'), parseInt, null],
|
| + processor: this.processCodeCreation_, backrefs: true,
|
| + needsProfile: true },
|
| + 'code-move': { parsers: [this.createAddressParser('code'),
|
| + this.createAddressParser('code-move-to')],
|
| + processor: this.processCodeMove_, backrefs: true,
|
| + needsProfile: true },
|
| + 'code-delete': { parsers: [this.createAddressParser('code')],
|
| + processor: this.processCodeDelete_, backrefs: true,
|
| + needsProfile: true },
|
| + 'tick': { parsers: [this.createAddressParser('code'),
|
| + this.createAddressParser('stack'), parseInt, 'var-args'],
|
| + processor: this.processTick_, backrefs: true, needProfile: true },
|
| + 'profiler': { parsers: [null, 'var-args'],
|
| + processor: this.processProfiler_, needsProfile: false },
|
| + 'heap-sample-begin': { parsers: [null, null, parseInt],
|
| + processor: this.processHeapSampleBegin_ },
|
| + 'heap-sample-stats': { parsers: [null, null, parseInt, parseInt],
|
| + processor: this.processHeapSampleStats_ },
|
| + 'heap-sample-item': { parsers: [null, parseInt, parseInt],
|
| + processor: this.processHeapSampleItem_ },
|
| + 'heap-js-cons-item': { parsers: [null, parseInt, parseInt],
|
| + processor: this.processHeapJsConsItem_ },
|
| + 'heap-sample-end': { parsers: [null, null],
|
| + processor: this.processHeapSampleEnd_ },
|
| + // Not used in DevTools Profiler.
|
| + 'shared-library': null,
|
| + // Obsolete row types.
|
| + 'code-allocate': null,
|
| + 'begin-code-region': null,
|
| + 'end-code-region': null});
|
| +
|
| +
|
| + /**
|
| + * Callback that is called when a new profile is encountered in the log.
|
| + * @type {function()}
|
| + */
|
| + this.startedProfileProcessing_ = null;
|
| +
|
| + /**
|
| + * Callback that is called periodically to display processing status.
|
| + * @type {function()}
|
| + */
|
| + this.profileProcessingStatus_ = null;
|
| +
|
| + /**
|
| + * Callback that is called when a profile has been processed and is ready
|
| + * to be shown.
|
| + * @type {function(devtools.profiler.ProfileView)}
|
| + */
|
| + this.finishedProfileProcessing_ = null;
|
| +
|
| /**
|
| - * Current profile.
|
| + * The current profile.
|
| * @type {devtools.profiler.JsProfile}
|
| */
|
| - this.profile_ = new devtools.profiler.JsProfile();
|
| + this.currentProfile_ = null;
|
|
|
| /**
|
| - * Builder of profile views.
|
| - * @type {devtools.profiler.ViewBuilder}
|
| + * Builder of profile views. Created during "profiler,begin" event processing.
|
| + * @type {devtools.profiler.WebKitViewBuilder}
|
| */
|
| - this.viewBuilder_ = new devtools.profiler.ViewBuilder(1);
|
| + this.viewBuilder_ = null;
|
|
|
| /**
|
| * Next profile id.
|
| * @type {number}
|
| */
|
| this.profileId_ = 1;
|
| +
|
| + /**
|
| + * Counter for processed ticks.
|
| + * @type {number}
|
| + */
|
| + this.ticksCount_ = 0;
|
| +
|
| + /**
|
| + * The current heap snapshot.
|
| + * @type {string}
|
| + */
|
| + this.currentHeapSnapshot_ = null;
|
| +
|
| + /**
|
| + * Next heap snapshot id.
|
| + * @type {number}
|
| + */
|
| + this.heapSnapshotId_ = 1;
|
| };
|
| +goog.inherits(devtools.profiler.Processor, devtools.profiler.LogReader);
|
|
|
|
|
| /**
|
| - * A dispatch table for V8 profiler event log records.
|
| - * @private
|
| + * @override
|
| */
|
| -devtools.profiler.Processor.RecordsDispatch_ = {
|
| - 'code-creation': { parsers: [null, parseInt, parseInt, null],
|
| - processor: 'processCodeCreation_' },
|
| - 'code-move': { parsers: [parseInt, parseInt],
|
| - processor: 'processCodeMove_' },
|
| - 'code-delete': { parsers: [parseInt], processor: 'processCodeDelete_' },
|
| - 'tick': { parsers: [parseInt, parseInt, parseInt, 'var-args'],
|
| - processor: 'processTick_' },
|
| - // Not used in DevTools Profiler.
|
| - 'profiler': null,
|
| - 'shared-library': null,
|
| - // Obsolete row types.
|
| - 'code-allocate': null,
|
| - 'begin-code-region': null,
|
| - 'end-code-region': null
|
| +devtools.profiler.Processor.prototype.printError = function(str) {
|
| + debugPrint(str);
|
| };
|
|
|
|
|
| /**
|
| - * Processes a portion of V8 profiler event log.
|
| - *
|
| - * @param {string} chunk A portion of log.
|
| + * @override
|
| */
|
| -devtools.profiler.Processor.prototype.processLogChunk = function(chunk) {
|
| - this.processLog_(chunk.split('\n'));
|
| +devtools.profiler.Processor.prototype.skipDispatch = function(dispatch) {
|
| + return dispatch.needsProfile && this.currentProfile_ == null;
|
| };
|
|
|
|
|
| /**
|
| - * Processes a log lines.
|
| + * Sets profile processing callbacks.
|
| *
|
| - * @param {Array<string>} lines Log lines.
|
| - * @private
|
| + * @param {function()} started Started processing callback.
|
| + * @param {function(devtools.profiler.ProfileView)} finished Finished
|
| + * processing callback.
|
| */
|
| -devtools.profiler.Processor.prototype.processLog_ = function(lines) {
|
| - var csvParser = new devtools.profiler.CsvParser();
|
| - try {
|
| - for (var i = 0, n = lines.length; i < n; ++i) {
|
| - var line = lines[i];
|
| - if (!line) {
|
| - continue;
|
| - }
|
| - var fields = csvParser.parseLine(line);
|
| - this.dispatchLogRow_(fields);
|
| - }
|
| - } catch (e) {
|
| - debugPrint('line ' + (i + 1) + ': ' + (e.message || e));
|
| - throw e;
|
| - }
|
| +devtools.profiler.Processor.prototype.setCallbacks = function(
|
| + started, processing, finished) {
|
| + this.startedProfileProcessing_ = started;
|
| + this.profileProcessingStatus_ = processing;
|
| + this.finishedProfileProcessing_ = finished;
|
| };
|
|
|
|
|
| /**
|
| - * Does a dispatch of a log record.
|
| + * An address for the fake "(program)" entry. WebKit's visualisation
|
| + * has assumptions on how the top of the call tree should look like,
|
| + * and we need to add a fake entry as the topmost function. This
|
| + * address is chosen because it's the end address of the first memory
|
| + * page, which is never used for code or data, but only as a guard
|
| + * page for catching AV errors.
|
| *
|
| - * @param {Array<string>} fields Log record.
|
| - * @private
|
| + * @type {number}
|
| */
|
| -devtools.profiler.Processor.prototype.dispatchLogRow_ = function(fields) {
|
| - // Obtain the dispatch.
|
| - var command = fields[0];
|
| - if (!(command in devtools.profiler.Processor.RecordsDispatch_)) {
|
| - throw new Error('unknown command: ' + command);
|
| - }
|
| - var dispatch = devtools.profiler.Processor.RecordsDispatch_[command];
|
| +devtools.profiler.Processor.PROGRAM_ENTRY = 0xffff;
|
| +/**
|
| + * @type {string}
|
| + */
|
| +devtools.profiler.Processor.PROGRAM_ENTRY_STR = '0xffff';
|
|
|
| - if (dispatch === null) {
|
| - return;
|
| - }
|
|
|
| - // Parse fields.
|
| - var parsedFields = [];
|
| - for (var i = 0; i < dispatch.parsers.length; ++i) {
|
| - var parser = dispatch.parsers[i];
|
| - if (parser === null) {
|
| - parsedFields.push(fields[1 + i]);
|
| - } else if (typeof parser == 'function') {
|
| - parsedFields.push(parser(fields[1 + i]));
|
| - } else {
|
| - // var-args
|
| - parsedFields.push(fields.slice(1 + i));
|
| +/**
|
| + * Sets new profile callback.
|
| + * @param {function(devtools.profiler.ProfileView)} callback Callback function.
|
| + */
|
| +devtools.profiler.Processor.prototype.setNewProfileCallback = function(
|
| + callback) {
|
| + this.newProfileCallback_ = callback;
|
| +};
|
| +
|
| +
|
| +devtools.profiler.Processor.prototype.processProfiler_ = function(
|
| + state, params) {
|
| + var processingInterval = null;
|
| + switch (state) {
|
| + case 'resume':
|
| + if (this.currentProfile_ == null) {
|
| + this.currentProfile_ = new devtools.profiler.JsProfile();
|
| + // see the comment for devtools.profiler.Processor.PROGRAM_ENTRY
|
| + this.currentProfile_.addCode(
|
| + 'Function', '(program)',
|
| + devtools.profiler.Processor.PROGRAM_ENTRY, 1);
|
| + if (this.startedProfileProcessing_) {
|
| + this.startedProfileProcessing_();
|
| + }
|
| + this.ticksCount_ = 0;
|
| + var self = this;
|
| + if (this.profileProcessingStatus_) {
|
| + processingInterval = window.setInterval(
|
| + function() { self.profileProcessingStatus_(self.ticksCount_); },
|
| + 1000);
|
| + }
|
| + }
|
| break;
|
| - }
|
| + case 'pause':
|
| + if (this.currentProfile_ != null) {
|
| + window.clearInterval(processingInterval);
|
| + if (this.finishedProfileProcessing_) {
|
| + this.finishedProfileProcessing_(this.createProfileForView());
|
| + }
|
| + this.currentProfile_ = null;
|
| + }
|
| + break;
|
| + case 'begin':
|
| + var samplingRate = NaN;
|
| + if (params.length > 0) {
|
| + samplingRate = parseInt(params[0]);
|
| + }
|
| + if (isNaN(samplingRate)) {
|
| + samplingRate = 1;
|
| + }
|
| + this.viewBuilder_ = new devtools.profiler.WebKitViewBuilder(samplingRate);
|
| + break;
|
| + // These events are valid but aren't used.
|
| + case 'compression':
|
| + case 'end': break;
|
| + default:
|
| + throw new Error('unknown profiler state: ' + state);
|
| }
|
| -
|
| - // Run the processor.
|
| - this[dispatch.processor].apply(this, parsedFields);
|
| };
|
|
|
|
|
| devtools.profiler.Processor.prototype.processCodeCreation_ = function(
|
| type, start, size, name) {
|
| - this.profile_.addCode(type, name, start, size);
|
| + this.currentProfile_.addCode(this.expandAlias(type), name, start, size);
|
| };
|
|
|
|
|
| devtools.profiler.Processor.prototype.processCodeMove_ = function(from, to) {
|
| - this.profile_.moveCode(from, to);
|
| + this.currentProfile_.moveCode(from, to);
|
| };
|
|
|
|
|
| devtools.profiler.Processor.prototype.processCodeDelete_ = function(start) {
|
| - this.profile_.deleteCode(start);
|
| + this.currentProfile_.deleteCode(start);
|
| };
|
|
|
|
|
| devtools.profiler.Processor.prototype.processTick_ = function(
|
| pc, sp, vmState, stack) {
|
| - var fullStack = [pc];
|
| - for (var i = 0, n = stack.length; i < n; ++i) {
|
| - var frame = stack[i];
|
| - // Leave only numbers starting with 0x. Filter possible 'overflow' string.
|
| - if (frame.charAt(0) == '0') {
|
| - fullStack.push(parseInt(frame, 16));
|
| - }
|
| + // see the comment for devtools.profiler.Processor.PROGRAM_ENTRY
|
| + stack.push(devtools.profiler.Processor.PROGRAM_ENTRY_STR);
|
| + this.currentProfile_.recordTick(this.processStack(pc, stack));
|
| + this.ticksCount_++;
|
| +};
|
| +
|
| +
|
| +devtools.profiler.Processor.prototype.processHeapSampleBegin_ = function(
|
| + space, state, ticks) {
|
| + if (space != 'Heap') return;
|
| + this.currentHeapSnapshot_ = {
|
| + number: this.heapSnapshotId_++,
|
| + entries: {},
|
| + lowlevels: {},
|
| + ticks: ticks
|
| + };
|
| +};
|
| +
|
| +
|
| +devtools.profiler.Processor.prototype.processHeapSampleStats_ = function(
|
| + space, state, capacity, used) {
|
| + if (space != 'Heap') return;
|
| + this.currentHeapSnapshot_.capacity = capacity;
|
| + this.currentHeapSnapshot_.used = used;
|
| +};
|
| +
|
| +
|
| +devtools.profiler.Processor.prototype.processHeapSampleItem_ = function(
|
| + item, number, size) {
|
| + if (!this.currentHeapSnapshot_) return;
|
| + this.currentHeapSnapshot_.lowlevels[item] = {
|
| + type: item, count: number, size: size
|
| + };
|
| +};
|
| +
|
| +
|
| +devtools.profiler.Processor.prototype.processHeapJsConsItem_ = function(
|
| + item, number, size) {
|
| + if (!this.currentHeapSnapshot_) return;
|
| + this.currentHeapSnapshot_.entries[item] = {
|
| + cons: item, count: number, size: size
|
| + };
|
| +};
|
| +
|
| +
|
| +devtools.profiler.Processor.prototype.processHeapSampleEnd_ = function(
|
| + space, state) {
|
| + if (space != 'Heap') return;
|
| + var snapshot = this.currentHeapSnapshot_;
|
| + this.currentHeapSnapshot_ = null;
|
| + // For some reason, 'used' from 'heap-sample-stats' sometimes differ from
|
| + // the sum of objects sizes. To avoid discrepancy, we re-calculate 'used'.
|
| + snapshot.used = 0;
|
| + for (var item in snapshot.lowlevels) {
|
| + snapshot.used += snapshot.lowlevels[item].size;
|
| }
|
| - this.profile_.recordTick(fullStack);
|
| + WebInspector.panels.heap.addSnapshot(snapshot);
|
| };
|
|
|
|
|
| @@ -187,15 +440,9 @@ devtools.profiler.Processor.prototype.processTick_ = function(
|
| * Creates a profile for further displaying in ProfileView.
|
| */
|
| devtools.profiler.Processor.prototype.createProfileForView = function() {
|
| - var profile = new devtools.profiler.ProfileView();
|
| + var profile = this.viewBuilder_.buildView(
|
| + this.currentProfile_.getTopDownProfile());
|
| profile.uid = this.profileId_++;
|
| profile.title = UserInitiatedProfileName + '.' + profile.uid;
|
| - // A trick to cope with ProfileView.bottomUpProfileDataGridTree and
|
| - // ProfileView.topDownProfileDataGridTree behavior.
|
| - profile.head = profile;
|
| - profile.heavyProfile = this.viewBuilder_.buildView(
|
| - this.profile_.getBottomUpProfile(), true);
|
| - profile.treeProfile = this.viewBuilder_.buildView(
|
| - this.profile_.getTopDownProfile());
|
| return profile;
|
| };
|
|
|