UNPKG

debug-server-next

Version:

Dev server for hippy-core.

287 lines (286 loc) 10.9 kB
// 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. /* eslint-disable rulesdir/no_underscored_properties */ import * as i18n from '../../../../core/i18n/i18n.js'; import * as Platform from '../../../../core/platform/platform.js'; import * as SDK from '../../../../core/sdk/sdk.js'; import * as Bindings from '../../../../models/bindings/bindings.js'; import * as Workspace from '../../../../models/workspace/workspace.js'; import * as SourceFrame from '../source_frame/source_frame.js'; const UIStrings = { /** *@description The milisecond unit */ ms: 'ms', /** *@description Unit for data size in DevTools */ mb: 'MB', /** *@description A unit */ kb: 'kB', }; const str_ = i18n.i18n.registerUIStrings('ui/legacy/components/perf_ui/LineLevelProfile.ts', UIStrings); const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_); let performanceInstance; export class Performance { _helper; constructor() { this._helper = new Helper('performance'); } static instance(opts = { forceNew: null }) { const { forceNew } = opts; if (!performanceInstance || forceNew) { performanceInstance = new Performance(); } return performanceInstance; } reset() { this._helper.reset(); } _appendLegacyCPUProfile(profile) { const target = profile.target(); const nodesToGo = [profile.profileHead]; const sampleDuration = (profile.profileEndTime - profile.profileStartTime) / profile.totalHitCount; while (nodesToGo.length) { const nodes = // TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration) // eslint-disable-next-line @typescript-eslint/no-explicit-any nodesToGo.pop().children; // Cast to any because runtime checks assert the props. for (let i = 0; i < nodes.length; ++i) { const node = nodes[i]; nodesToGo.push(node); if (!node.url || !node.positionTicks) { continue; } for (let j = 0; j < node.positionTicks.length; ++j) { const lineInfo = node.positionTicks[j]; const line = lineInfo.line; const time = lineInfo.ticks * sampleDuration; this._helper.addLineData(target, node.url, line, time); } } } } appendCPUProfile(profile) { if (!profile.lines) { this._appendLegacyCPUProfile(profile); this._helper.scheduleUpdate(); return; } const target = profile.target(); if (!profile.samples) { return; } for (let i = 1; i < profile.samples.length; ++i) { const line = profile.lines[i]; if (!line) { continue; } const node = profile.nodeByIndex(i); if (!node) { continue; } const scriptIdOrUrl = node.scriptId || node.url; if (!scriptIdOrUrl) { continue; } const time = profile.timestamps[i] - profile.timestamps[i - 1]; this._helper.addLineData(target, scriptIdOrUrl, line, time); } this._helper.scheduleUpdate(); } } let memoryInstance; export class Memory { _helper; constructor() { this._helper = new Helper('memory'); } static instance(opts = { forceNew: null }) { const { forceNew } = opts; if (!memoryInstance || forceNew) { memoryInstance = new Memory(); } return memoryInstance; } reset() { this._helper.reset(); } appendHeapProfile(profile, target) { const helper = this._helper; processNode(profile.head); helper.scheduleUpdate(); function processNode(node) { node.children.forEach(processNode); if (!node.selfSize) { return; } const script = Number(node.callFrame.scriptId) || node.callFrame.url; if (!script) { return; } const line = node.callFrame.lineNumber + 1; helper.addLineData(target, script, line, node.selfSize); } } } export class Helper { _type; _locationPool; _updateTimer; _lineData; constructor(type) { this._type = type; this._locationPool = new Bindings.LiveLocation.LiveLocationPool(); this._updateTimer = null; this.reset(); } reset() { // The second map uses string keys for script URLs and numbers for scriptId. this._lineData = new Map(); this.scheduleUpdate(); } addLineData(target, scriptIdOrUrl, line, data) { let targetData = this._lineData.get(target); if (!targetData) { targetData = new Map(); this._lineData.set(target, targetData); } let scriptData = targetData.get(scriptIdOrUrl); if (!scriptData) { scriptData = new Map(); targetData.set(scriptIdOrUrl, scriptData); } scriptData.set(line, (scriptData.get(line) || 0) + data); } scheduleUpdate() { if (this._updateTimer) { return; } this._updateTimer = window.setTimeout(() => { this._updateTimer = null; this._doUpdate(); }, 0); } _doUpdate() { this._locationPool.disposeAll(); Workspace.Workspace.WorkspaceImpl.instance().uiSourceCodes().forEach(uiSourceCode => uiSourceCode.removeDecorationsForType(this._type)); for (const targetToScript of this._lineData) { const target = targetToScript[0]; const debuggerModel = target ? target.model(SDK.DebuggerModel.DebuggerModel) : null; const scriptToLineMap = targetToScript[1]; for (const scriptToLine of scriptToLineMap) { const scriptIdOrUrl = scriptToLine[0]; const lineToDataMap = scriptToLine[1]; // debuggerModel is null when the profile is loaded from file. // Try to get UISourceCode by the URL in this case. const uiSourceCode = !debuggerModel && typeof scriptIdOrUrl === 'string' ? Workspace.Workspace.WorkspaceImpl.instance().uiSourceCodeForURL(scriptIdOrUrl) : null; if (!debuggerModel && !uiSourceCode) { continue; } for (const lineToData of lineToDataMap) { const line = lineToData[0] - 1; const data = lineToData[1]; if (uiSourceCode) { uiSourceCode.addLineDecoration(line, this._type, data); continue; } if (debuggerModel) { const rawLocation = typeof scriptIdOrUrl === 'string' ? debuggerModel.createRawLocationByURL(scriptIdOrUrl, line, 0) : debuggerModel.createRawLocationByScriptId(String(scriptIdOrUrl), line, 0); if (rawLocation) { new Presentation(rawLocation, this._type, data, this._locationPool); } } } } } } } export class Presentation { _type; _time; _uiLocation; constructor(rawLocation, type, time, locationPool) { this._type = type; this._time = time; this._uiLocation = null; Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance().createLiveLocation(rawLocation, this.updateLocation.bind(this), locationPool); } async updateLocation(liveLocation) { if (this._uiLocation) { this._uiLocation.uiSourceCode.removeDecorationsForType(this._type); } this._uiLocation = await liveLocation.uiLocation(); if (this._uiLocation) { this._uiLocation.uiSourceCode.addLineDecoration(this._uiLocation.lineNumber, this._type, this._time); } } } let lineDecoratorInstance; export class LineDecorator { static instance(opts = { forceNew: null }) { const { forceNew } = opts; if (!lineDecoratorInstance || forceNew) { lineDecoratorInstance = new LineDecorator(); } return lineDecoratorInstance; } decorate(uiSourceCode, textEditor, type) { const gutterType = `CodeMirror-gutter-${type}`; const decorations = uiSourceCode.decorationsForType(type); textEditor.uninstallGutter(gutterType); if (!decorations || !decorations.size) { return; } textEditor.installGutter(gutterType, false); for (const decoration of decorations) { const value = decoration.data(); const element = this._createElement(type, value); textEditor.setGutterDecoration(decoration.range().startLine, gutterType, element); } } _createElement(type, value) { const element = document.createElement('div'); element.classList.add('text-editor-line-marker-text'); if (type === 'performance') { const intensity = Platform.NumberUtilities.clamp(Math.log10(1 + 10 * value) / 5, 0.02, 1); element.textContent = value.toFixed(1); element.style.backgroundColor = `hsla(44, 100%, 50%, ${intensity.toFixed(3)})`; element.createChild('span', 'line-marker-units').textContent = i18nString(UIStrings.ms); } else { const intensity = Platform.NumberUtilities.clamp(Math.log10(1 + 2e-3 * value) / 5, 0.02, 1); element.style.backgroundColor = `hsla(217, 100%, 70%, ${intensity.toFixed(3)})`; value /= 1e3; let units; let fractionDigits; if (value >= 1e3) { units = i18nString(UIStrings.mb); value /= 1e3; fractionDigits = value >= 20 ? 0 : 1; } else { units = i18nString(UIStrings.kb); fractionDigits = 0; } element.textContent = value.toFixed(fractionDigits); element.createChild('span', 'line-marker-units').textContent = units; } return element; } } SourceFrame.SourceFrame.registerLineDecorator({ lineDecorator: LineDecorator.instance, decoratorType: SourceFrame.SourceFrame.DecoratorType.MEMORY, }); SourceFrame.SourceFrame.registerLineDecorator({ lineDecorator: LineDecorator.instance, decoratorType: SourceFrame.SourceFrame.DecoratorType.PERFORMANCE, });