blob: fa4d83c150947cf6b393d08d035679658eb5012f [file] [log] [blame]
// Copyright 2016 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.
/**
* @unrestricted
*/
PerfUI.LineLevelProfile = class {
constructor() {
this._locationPool = new Bindings.LiveLocationPool();
this.reset();
}
/**
* @return {!PerfUI.LineLevelProfile}
*/
static instance() {
if (!PerfUI.LineLevelProfile._instance)
PerfUI.LineLevelProfile._instance = new PerfUI.LineLevelProfile();
return PerfUI.LineLevelProfile._instance;
}
/**
* @param {!SDK.CPUProfileDataModel} profile
*/
appendCPUProfile(profile) {
const nodesToGo = [profile.profileHead];
const sampleDuration = (profile.profileEndTime - profile.profileStartTime) / profile.totalHitCount;
while (nodesToGo.length) {
const nodes = nodesToGo.pop().children;
for (let i = 0; i < nodes.length; ++i) {
const node = nodes[i];
nodesToGo.push(node);
if (!node.url || !node.positionTicks)
continue;
let fileInfo = this._files.get(node.url);
if (!fileInfo) {
fileInfo = new Map();
this._files.set(node.url, fileInfo);
}
for (let j = 0; j < node.positionTicks.length; ++j) {
const lineInfo = node.positionTicks[j];
const line = lineInfo.line;
const time = lineInfo.ticks * sampleDuration;
fileInfo.set(line, (fileInfo.get(line) || 0) + time);
}
}
}
this._scheduleUpdate();
}
reset() {
/** @type {!Map<string, !Map<number, number>>} */
this._files = new Map();
this._scheduleUpdate();
}
_scheduleUpdate() {
if (this._updateTimer)
return;
this._updateTimer = setTimeout(() => {
this._updateTimer = null;
this._doUpdate();
}, 0);
}
_doUpdate() {
// TODO(alph): use scriptId instead of urls for the target.
this._locationPool.disposeAll();
Workspace.workspace.uiSourceCodes().forEach(
uiSourceCode => uiSourceCode.removeDecorationsForType(PerfUI.LineLevelProfile.LineDecorator.type));
for (const fileInfo of this._files) {
const url = /** @type {string} */ (fileInfo[0]);
const uiSourceCode = Workspace.workspace.uiSourceCodeForURL(url);
if (!uiSourceCode)
continue;
const target = Bindings.NetworkProject.targetForUISourceCode(uiSourceCode) || SDK.targetManager.mainTarget();
const debuggerModel = target ? target.model(SDK.DebuggerModel) : null;
if (!debuggerModel)
continue;
for (const lineInfo of fileInfo[1]) {
const line = lineInfo[0] - 1;
const time = lineInfo[1];
const rawLocation = debuggerModel.createRawLocationByURL(url, line, 0);
if (rawLocation)
new PerfUI.LineLevelProfile.Presentation(rawLocation, time, this._locationPool);
else if (uiSourceCode)
uiSourceCode.addLineDecoration(line, PerfUI.LineLevelProfile.LineDecorator.type, time);
}
}
}
};
/**
* @unrestricted
*/
PerfUI.LineLevelProfile.Presentation = class {
/**
* @param {!SDK.DebuggerModel.Location} rawLocation
* @param {number} time
* @param {!Bindings.LiveLocationPool} locationPool
*/
constructor(rawLocation, time, locationPool) {
this._time = time;
Bindings.debuggerWorkspaceBinding.createLiveLocation(rawLocation, this.updateLocation.bind(this), locationPool);
}
/**
* @param {!Bindings.LiveLocation} liveLocation
*/
updateLocation(liveLocation) {
if (this._uiLocation)
this._uiLocation.uiSourceCode.removeDecorationsForType(PerfUI.LineLevelProfile.LineDecorator.type);
this._uiLocation = liveLocation.uiLocation();
if (this._uiLocation) {
this._uiLocation.uiSourceCode.addLineDecoration(
this._uiLocation.lineNumber, PerfUI.LineLevelProfile.LineDecorator.type, this._time);
}
}
};
/**
* @implements {SourceFrame.LineDecorator}
* @unrestricted
*/
PerfUI.LineLevelProfile.LineDecorator = class {
/**
* @override
* @param {!Workspace.UISourceCode} uiSourceCode
* @param {!TextEditor.CodeMirrorTextEditor} textEditor
*/
decorate(uiSourceCode, textEditor) {
const gutterType = 'CodeMirror-gutter-performance';
const decorations = uiSourceCode.decorationsForType(PerfUI.LineLevelProfile.LineDecorator.type);
textEditor.uninstallGutter(gutterType);
if (!decorations || !decorations.size)
return;
textEditor.installGutter(gutterType, false);
for (const decoration of decorations) {
const time = /** @type {number} */ (decoration.data());
const text = Common.UIString('%.1f\xa0ms', time);
const intensity = Number.constrain(Math.log10(1 + 2 * time) / 5, 0.02, 1);
const element = createElementWithClass('div', 'text-editor-line-marker-performance');
element.textContent = text;
element.style.backgroundColor = `hsla(44, 100%, 50%, ${intensity.toFixed(3)})`;
textEditor.setGutterDecoration(decoration.range().startLine, gutterType, element);
}
}
};
PerfUI.LineLevelProfile.LineDecorator.type = 'performance';