/* * Copyright (C) 2022 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { SpSystemTrace } from '../SpSystemTrace'; import { TraceRow } from '../trace/base/TraceRow'; import { info } from '../../../log/Log'; import { renders } from '../../database/ui-worker/ProcedureWorker'; import { type EmptyRender } from '../../database/ui-worker/cpu/ProcedureWorkerCPU'; import { type HeapTimelineRender, HeapTimelineStruct } from '../../database/ui-worker/ProcedureWorkerHeapTimeline'; import { HeapDataInterface, type ParseListener } from '../../../js-heap/HeapDataInterface'; import { LoadDatabase } from '../../../js-heap/LoadDatabase'; import { type FileInfo } from '../../../js-heap/model/UiStruct'; import { type HeapSnapshotRender, HeapSnapshotStruct } from '../../database/ui-worker/ProcedureWorkerHeapSnapshot'; import { procedurePool } from '../../database/Procedure'; import { Utils } from '../trace/base/Utils'; import { type JsCpuProfilerChartFrame } from '../../bean/JsStruct'; import { type JsCpuProfilerRender, JsCpuProfilerStruct } from '../../database/ui-worker/ProcedureWorkerCpuProfiler'; import { ns2s } from '../../database/ui-worker/ProcedureWorkerCommon'; import { cpuProfilerDataSender } from '../../database/data-trafic/ArkTsSender'; import {queryJsCpuProfilerConfig, queryJsCpuProfilerData} from "../../database/sql/Cpu.sql"; import {queryJsMemoryData} from "../../database/sql/Memory.sql"; const TYPE_SNAPSHOT = 0; const TYPE_TIMELINE = 1; const LAMBDA_FUNCTION_NAME = '(anonymous)'; export class SpArkTsChart implements ParseListener { private trace: SpSystemTrace; private folderRow: TraceRow | undefined; private jsCpuProfilerRow: TraceRow | undefined; private heapTimelineRow: TraceRow | undefined; private heapSnapshotRow: TraceRow | undefined; private loadJsDatabase: LoadDatabase; private allCombineDataMap = new Map(); private process: string = ''; constructor(trace: SpSystemTrace) { this.trace = trace; this.loadJsDatabase = LoadDatabase.getInstance(); } public get chartFrameMap(): Map { return this.allCombineDataMap; } public async initFolder(): Promise { let jsConfig = await queryJsCpuProfilerConfig(); let jsCpu = await queryJsCpuProfilerData(); let jsMemory = await queryJsMemoryData(); if (jsMemory.length > 0 || jsCpu.length > 0) { this.folderRow = TraceRow.skeleton(); this.process = jsConfig[0].pid; this.folderRow.rowId = this.process; this.folderRow.rowType = TraceRow.ROW_TYPE_ARK_TS; this.folderRow.style.height = '40px'; this.folderRow.rowParentId = ''; this.folderRow.folder = true; this.folderRow.name = `Ark Ts ` + this.process; this.folderRow.addTemplateTypes('ArkTs'); this.folderRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; this.folderRow.selectChangeHandler = this.trace.selectChangeHandler; this.folderRow.supplierFrame = (): Promise> => new Promise>((resolve) => resolve([])); this.folderRow.onThreadHandler = (useCache): void => { this.folderRow!.canvasSave(this.trace.canvasPanelCtx!); if (this.folderRow!.expansion) { this.trace.canvasPanelCtx?.clearRect(0, 0, this.folderRow!.frame.width, this.folderRow!.frame.height); } else { (renders.empty as EmptyRender).renderMainThread( { context: this.trace.canvasPanelCtx, useCache: useCache, type: ``, }, this.folderRow! ); } this.folderRow!.canvasRestore(this.trace.canvasPanelCtx!, this.trace); }; this.trace.rowsEL?.appendChild(this.folderRow); if (this.folderRow && jsConfig[0].type !== -1 && jsMemory.length > 0) { this.folderRow.addTemplateTypes('Memory'); if (jsConfig[0].type === TYPE_SNAPSHOT) { // snapshot await this.initSnapshotChart(); } else if (jsConfig[0].type === TYPE_TIMELINE) { // timeline await this.initTimelineChart(); } } if (this.folderRow && jsConfig[0].enableCpuProfiler === 1 && jsCpu.length > 0) { await this.initJsCpuChart(); } if ((this.heapSnapshotRow || this.heapTimelineRow) && jsMemory.length > 0) { await this.loadJsDatabase.loadDatabase(this); } if (this.jsCpuProfilerRow && jsCpu.length > 0) { this.jsCpuProfilerRow!.supplierFrame = (): Promise> => { return cpuProfilerDataSender(this.jsCpuProfilerRow!).then((res: any) => { let maxHeight = res.maxDepth * 20; this.jsCpuProfilerRow!.style.height = `${maxHeight}px`; if (res.dataList.length > 0) { this.allCombineDataMap = new Map(); for (let data of res.dataList) { this.allCombineDataMap.set(data.id, data); SpSystemTrace.jsProfilerMap.set(data.id, data); } res.dataList.forEach((data: any) => { data.children = []; if (data.childrenIds.length > 0) { for (let id of data.childrenIds) { let child = SpSystemTrace.jsProfilerMap.get(Number(id)); data.children.push(child); } } data.name = SpSystemTrace.DATA_DICT.get(data.nameId) || LAMBDA_FUNCTION_NAME; data.url = SpSystemTrace.DATA_DICT.get(data.urlId) || 'unknown'; if (data.url && data.url !== 'unknown') { let dirs = data.url.split('/'); data.scriptName = dirs.pop() || ''; } }); } return res.dataList; }); }; } } } private async initTimelineChart(): Promise { this.heapTimelineRow = TraceRow.skeleton(); this.heapTimelineRow.rowParentId = this.process; this.heapTimelineRow.rowHidden = !this.folderRow!.expansion; this.heapTimelineRow.style.height = '40px'; this.heapTimelineRow.name = `Heaptimeline`; this.heapTimelineRow.folder = false; this.heapTimelineRow.rowType = TraceRow.ROW_TYPE_HEAP_TIMELINE; this.heapTimelineRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; this.heapTimelineRow.selectChangeHandler = this.trace.selectChangeHandler; this.heapTimelineRow.setAttribute('children', ''); this.heapTimelineRow!.focusHandler = (): void => { this.trace?.displayTip( this.heapTimelineRow!, HeapTimelineStruct.hoverHeapTimelineStruct, `Size: ${Utils.getBinaryByteWithUnit(HeapTimelineStruct.hoverHeapTimelineStruct?.size || 0)}` ); }; this.heapTimelineRow!.findHoverStruct = () => { HeapTimelineStruct.hoverHeapTimelineStruct = this.heapTimelineRow!.getHoverStruct(); }; this.folderRow!.addChildTraceRow(this.heapTimelineRow!); } private async initSnapshotChart(): Promise { this.heapSnapshotRow = TraceRow.skeleton(); this.heapSnapshotRow.rowParentId = this.process; this.heapSnapshotRow.rowHidden = !this.folderRow!.expansion; this.heapSnapshotRow.style.height = '40px'; this.heapSnapshotRow.name = `Heapsnapshot`; this.heapSnapshotRow.rowId = `heapsnapshot`; this.heapSnapshotRow.folder = false; this.heapSnapshotRow.rowType = TraceRow.ROW_TYPE_HEAP_SNAPSHOT; this.heapSnapshotRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; this.heapSnapshotRow.selectChangeHandler = this.trace.selectChangeHandler; this.heapSnapshotRow.setAttribute('children', ''); this.heapSnapshotRow!.focusHandler = (): void => { this.trace?.displayTip( this.heapSnapshotRow!, HeapSnapshotStruct.hoverSnapshotStruct, `Name: ${HeapSnapshotStruct.hoverSnapshotStruct?.name || ''} Size: ${Utils.getBinaryByteWithUnit(HeapSnapshotStruct.hoverSnapshotStruct?.size || 0)}` ); }; this.heapSnapshotRow!.findHoverStruct = () => { HeapSnapshotStruct.hoverSnapshotStruct = this.heapSnapshotRow!.getHoverStruct(); }; this.folderRow!.addChildTraceRow(this.heapSnapshotRow); } public async parseDone(fileModule: Array): Promise { if (fileModule.length > 0) { let heapFile = HeapDataInterface.getInstance().getFileStructs(); let file = heapFile[0]; this.trace.snapshotFile = file; if (file.type === TYPE_TIMELINE) { let samples = HeapDataInterface.getInstance().getSamples(file.id); this.heapTimelineRow!.rowId = `heaptimeline` + file.id; this.heapTimelineRow!.supplierFrame = (): Promise => new Promise((resolve) => resolve(samples)); this.heapTimelineRow!.onThreadHandler = (useCache): void => { let context: CanvasRenderingContext2D; if (this.heapTimelineRow?.currentContext) { context = this.heapTimelineRow!.currentContext; } else { context = this.heapTimelineRow!.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; } this.heapTimelineRow!.canvasSave(context); (renders['heap-timeline'] as HeapTimelineRender).renderMainThread( { context: context, useCache: useCache, type: `heap-timeline`, samples: samples, }, this.heapTimelineRow! ); this.heapTimelineRow!.canvasRestore(context, this.trace); }; } else if (file.type === TYPE_SNAPSHOT) { this.heapSnapshotRow!.supplierFrame = (): Promise> => new Promise>((resolve) => resolve(heapFile)); this.heapSnapshotRow!.onThreadHandler = (useCache): void => { let context: CanvasRenderingContext2D; if (this.heapSnapshotRow?.currentContext) { context = this.heapSnapshotRow!.currentContext; } else { context = this.heapSnapshotRow!.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; } this.heapSnapshotRow!.canvasSave(context); (renders['heap-snapshot'] as HeapSnapshotRender).renderMainThread( { context: context, useCache: useCache, type: `heap-snapshot`, }, this.heapSnapshotRow! ); this.heapSnapshotRow!.canvasRestore(context, this.trace); }; } } } private initJsCpuChart = async (): Promise => { this.jsCpuProfilerRow = TraceRow.skeleton(); this.jsCpuProfilerRow.rowParentId = this.process; this.jsCpuProfilerRow.rowHidden = !this.folderRow!.expansion; this.jsCpuProfilerRow.name = 'CpuProfiler'; this.jsCpuProfilerRow.rowId = 'JsCpuProfiler'; this.jsCpuProfilerRow.folder = false; this.jsCpuProfilerRow.rowType = TraceRow.ROW_TYPE_JS_CPU_PROFILER; this.jsCpuProfilerRow!.style.height = '40px'; this.jsCpuProfilerRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; this.jsCpuProfilerRow.selectChangeHandler = this.trace.selectChangeHandler; this.jsCpuProfilerRow.setAttribute('children', ''); this.jsCpuProfilerRow.focusHandler = (): void => { this.trace?.displayTip( this.jsCpuProfilerRow!, JsCpuProfilerStruct.hoverJsCpuProfilerStruct, `Name: ${JsCpuProfilerStruct.hoverJsCpuProfilerStruct?.name || ''}
Self Time: ${ns2s(JsCpuProfilerStruct.hoverJsCpuProfilerStruct?.selfTime || 0)}
Total Time: ${ns2s(JsCpuProfilerStruct.hoverJsCpuProfilerStruct?.totalTime || 0)}
Url: ${JsCpuProfilerStruct.hoverJsCpuProfilerStruct?.url || 0}` ); }; this.jsCpuProfilerRow!.findHoverStruct = () => { JsCpuProfilerStruct.hoverJsCpuProfilerStruct = this.jsCpuProfilerRow!.getHoverStruct(); }; this.jsCpuProfilerRow.onThreadHandler = (useCache): void => { let context: CanvasRenderingContext2D; if (this.jsCpuProfilerRow?.currentContext) { context = this.jsCpuProfilerRow!.currentContext; } else { context = this.jsCpuProfilerRow!.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; } this.jsCpuProfilerRow!.canvasSave(context); (renders['js-cpu-profiler'] as JsCpuProfilerRender).renderMainThread( { context: context, useCache: useCache, type: `js-cpu-profiler`, }, this.jsCpuProfilerRow! ); this.jsCpuProfilerRow!.canvasRestore(context, this.trace); }; this.folderRow!.addChildTraceRow(this.jsCpuProfilerRow); }; }