1/* 2 * Copyright (C) 2022 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15import { SpSystemTrace } from '../SpSystemTrace'; 16import { TraceRow } from '../trace/base/TraceRow'; 17import { info } from '../../../log/Log'; 18import { renders } from '../../database/ui-worker/ProcedureWorker'; 19import { type EmptyRender } from '../../database/ui-worker/cpu/ProcedureWorkerCPU'; 20import { type HeapTimelineRender, HeapTimelineStruct } from '../../database/ui-worker/ProcedureWorkerHeapTimeline'; 21import { HeapDataInterface, type ParseListener } from '../../../js-heap/HeapDataInterface'; 22import { LoadDatabase } from '../../../js-heap/LoadDatabase'; 23import { type FileInfo } from '../../../js-heap/model/UiStruct'; 24import { type HeapSnapshotRender, HeapSnapshotStruct } from '../../database/ui-worker/ProcedureWorkerHeapSnapshot'; 25import { procedurePool } from '../../database/Procedure'; 26import { Utils } from '../trace/base/Utils'; 27import { type JsCpuProfilerChartFrame } from '../../bean/JsStruct'; 28import { type JsCpuProfilerRender, JsCpuProfilerStruct } from '../../database/ui-worker/ProcedureWorkerCpuProfiler'; 29import { ns2s } from '../../database/ui-worker/ProcedureWorkerCommon'; 30import { 31 cpuProfilerDataSender 32} from '../../database/data-trafic/ArkTsSender'; 33import {queryJsCpuProfilerConfig, queryJsCpuProfilerData} from "../../database/sql/Cpu.sql"; 34import {queryJsMemoryData} from "../../database/sql/Memory.sql"; 35 36const TYPE_SNAPSHOT = 0; 37const TYPE_TIMELINE = 1; 38const LAMBDA_FUNCTION_NAME = '(anonymous)'; 39export class SpArkTsChart implements ParseListener { 40 private trace: SpSystemTrace; 41 private folderRow: TraceRow<any> | undefined; 42 private jsCpuProfilerRow: TraceRow<JsCpuProfilerStruct> | undefined; 43 private heapTimelineRow: TraceRow<HeapTimelineStruct> | undefined; 44 private heapSnapshotRow: TraceRow<HeapSnapshotStruct> | undefined; 45 private loadJsDatabase: LoadDatabase; 46 private allCombineDataMap = new Map<number, JsCpuProfilerChartFrame>(); 47 private process: string = ''; 48 49 constructor(trace: SpSystemTrace) { 50 this.trace = trace; 51 this.loadJsDatabase = LoadDatabase.getInstance(); 52 } 53 54 public get chartFrameMap(): Map<number, JsCpuProfilerChartFrame> { 55 return this.allCombineDataMap; 56 } 57 58 public async initFolder(): Promise<void> { 59 let jsConfig = await queryJsCpuProfilerConfig(); 60 61 let jsCpu = await queryJsCpuProfilerData(); 62 let jsMemory = await queryJsMemoryData(); 63 if (jsMemory.length > 0 || jsCpu.length > 0) { 64 this.folderRow = TraceRow.skeleton(); 65 this.process = jsConfig[0].pid; 66 this.folderRow.rowId = this.process; 67 this.folderRow.rowType = TraceRow.ROW_TYPE_ARK_TS; 68 this.folderRow.style.height = '40px'; 69 this.folderRow.rowParentId = ''; 70 this.folderRow.folder = true; 71 this.folderRow.name = `Ark Ts ` + this.process; 72 this.folderRow.addTemplateTypes('ArkTs'); 73 this.folderRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 74 this.folderRow.selectChangeHandler = this.trace.selectChangeHandler; 75 this.folderRow.supplierFrame = (): Promise<Array<unknown>> => 76 new Promise<Array<unknown>>((resolve) => resolve([])); 77 this.folderRow.onThreadHandler = (useCache): void => { 78 this.folderRow!.canvasSave(this.trace.canvasPanelCtx!); 79 if (this.folderRow!.expansion) { 80 this.trace.canvasPanelCtx?.clearRect(0, 0, this.folderRow!.frame.width, this.folderRow!.frame.height); 81 } else { 82 (renders.empty as EmptyRender).renderMainThread( 83 { 84 context: this.trace.canvasPanelCtx, 85 useCache: useCache, 86 type: ``, 87 }, 88 this.folderRow! 89 ); 90 } 91 this.folderRow!.canvasRestore(this.trace.canvasPanelCtx!, this.trace); 92 }; 93 this.trace.rowsEL?.appendChild(this.folderRow); 94 if (this.folderRow && jsConfig[0].type !== -1 && jsMemory.length > 0) { 95 this.folderRow.addTemplateTypes('Memory'); 96 if (jsConfig[0].type === TYPE_SNAPSHOT) { 97 // snapshot 98 await this.initSnapshotChart(); 99 } else if (jsConfig[0].type === TYPE_TIMELINE) { 100 // timeline 101 await this.initTimelineChart(); 102 } 103 } 104 if (this.folderRow && jsConfig[0].enableCpuProfiler === 1 && jsCpu.length > 0) { 105 await this.initJsCpuChart(); 106 } 107 if ((this.heapSnapshotRow || this.heapTimelineRow) && jsMemory.length > 0) { 108 await this.loadJsDatabase.loadDatabase(this); 109 } 110 if (this.jsCpuProfilerRow && jsCpu.length > 0) { 111 this.jsCpuProfilerRow!.supplierFrame = (): Promise<Array<any>> => { 112 return cpuProfilerDataSender(this.jsCpuProfilerRow!).then((res: any) => { 113 let maxHeight = res.maxDepth * 20; 114 this.jsCpuProfilerRow!.style.height = `${maxHeight}px`; 115 if (res.dataList.length > 0) { 116 this.allCombineDataMap = new Map<number, JsCpuProfilerChartFrame>(); 117 for (let data of res.dataList) { 118 this.allCombineDataMap.set(data.id, data); 119 SpSystemTrace.jsProfilerMap.set(data.id, data); 120 } 121 res.dataList.forEach((data: any) => { 122 data.children = []; 123 if (data.childrenIds.length > 0) { 124 for (let id of data.childrenIds) { 125 let child = SpSystemTrace.jsProfilerMap.get(Number(id)); 126 data.children.push(child); 127 } 128 } 129 data.name = SpSystemTrace.DATA_DICT.get(data.nameId) || LAMBDA_FUNCTION_NAME; 130 data.url = SpSystemTrace.DATA_DICT.get(data.urlId) || 'unknown'; 131 if (data.url && data.url !== 'unknown') { 132 let dirs = data.url.split('/'); 133 data.scriptName = dirs.pop() || ''; 134 } 135 }); 136 } 137 return res.dataList; 138 }); 139 }; 140 } 141 } 142 } 143 144 private async initTimelineChart(): Promise<void> { 145 this.heapTimelineRow = TraceRow.skeleton<HeapTimelineStruct>(); 146 this.heapTimelineRow.rowParentId = this.process; 147 this.heapTimelineRow.rowHidden = !this.folderRow!.expansion; 148 this.heapTimelineRow.style.height = '40px'; 149 this.heapTimelineRow.name = `Heaptimeline`; 150 this.heapTimelineRow.folder = false; 151 this.heapTimelineRow.rowType = TraceRow.ROW_TYPE_HEAP_TIMELINE; 152 this.heapTimelineRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 153 this.heapTimelineRow.selectChangeHandler = this.trace.selectChangeHandler; 154 this.heapTimelineRow.setAttribute('children', ''); 155 this.heapTimelineRow!.focusHandler = (): void => { 156 this.trace?.displayTip( 157 this.heapTimelineRow!, 158 HeapTimelineStruct.hoverHeapTimelineStruct, 159 `<span>Size: ${Utils.getBinaryByteWithUnit(HeapTimelineStruct.hoverHeapTimelineStruct?.size || 0)}</span>` 160 ); 161 }; 162 this.heapTimelineRow!.findHoverStruct = () => { 163 HeapTimelineStruct.hoverHeapTimelineStruct = this.heapTimelineRow!.getHoverStruct(); 164 }; 165 this.folderRow!.addChildTraceRow(this.heapTimelineRow!); 166 } 167 168 private async initSnapshotChart(): Promise<void> { 169 this.heapSnapshotRow = TraceRow.skeleton<HeapSnapshotStruct>(); 170 this.heapSnapshotRow.rowParentId = this.process; 171 this.heapSnapshotRow.rowHidden = !this.folderRow!.expansion; 172 this.heapSnapshotRow.style.height = '40px'; 173 this.heapSnapshotRow.name = `Heapsnapshot`; 174 this.heapSnapshotRow.rowId = `heapsnapshot`; 175 this.heapSnapshotRow.folder = false; 176 this.heapSnapshotRow.rowType = TraceRow.ROW_TYPE_HEAP_SNAPSHOT; 177 this.heapSnapshotRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 178 this.heapSnapshotRow.selectChangeHandler = this.trace.selectChangeHandler; 179 this.heapSnapshotRow.setAttribute('children', ''); 180 this.heapSnapshotRow!.focusHandler = (): void => { 181 this.trace?.displayTip( 182 this.heapSnapshotRow!, 183 HeapSnapshotStruct.hoverSnapshotStruct, 184 `<span>Name: ${HeapSnapshotStruct.hoverSnapshotStruct?.name || ''}</span> 185 <span>Size: ${Utils.getBinaryByteWithUnit(HeapSnapshotStruct.hoverSnapshotStruct?.size || 0)}</span>` 186 ); 187 }; 188 this.heapSnapshotRow!.findHoverStruct = () => { 189 HeapSnapshotStruct.hoverSnapshotStruct = this.heapSnapshotRow!.getHoverStruct(); 190 }; 191 this.folderRow!.addChildTraceRow(this.heapSnapshotRow); 192 } 193 194 public async parseDone(fileModule: Array<FileInfo>): Promise<void> { 195 if (fileModule.length > 0) { 196 let heapFile = HeapDataInterface.getInstance().getFileStructs(); 197 let file = heapFile[0]; 198 this.trace.snapshotFile = file; 199 if (file.type === TYPE_TIMELINE) { 200 let samples = HeapDataInterface.getInstance().getSamples(file.id); 201 this.heapTimelineRow!.rowId = `heaptimeline` + file.id; 202 this.heapTimelineRow!.supplierFrame = (): Promise<any> => new Promise<any>((resolve) => resolve(samples)); 203 this.heapTimelineRow!.onThreadHandler = (useCache): void => { 204 let context: CanvasRenderingContext2D; 205 if (this.heapTimelineRow?.currentContext) { 206 context = this.heapTimelineRow!.currentContext; 207 } else { 208 context = this.heapTimelineRow!.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 209 } 210 this.heapTimelineRow!.canvasSave(context); 211 (renders['heap-timeline'] as HeapTimelineRender).renderMainThread( 212 { 213 context: context, 214 useCache: useCache, 215 type: `heap-timeline`, 216 samples: samples, 217 }, 218 this.heapTimelineRow! 219 ); 220 this.heapTimelineRow!.canvasRestore(context, this.trace); 221 }; 222 } else if (file.type === TYPE_SNAPSHOT) { 223 this.heapSnapshotRow!.supplierFrame = (): Promise<Array<any>> => 224 new Promise<Array<any>>((resolve) => resolve(heapFile)); 225 this.heapSnapshotRow!.onThreadHandler = (useCache): void => { 226 let context: CanvasRenderingContext2D; 227 if (this.heapSnapshotRow?.currentContext) { 228 context = this.heapSnapshotRow!.currentContext; 229 } else { 230 context = this.heapSnapshotRow!.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 231 } 232 this.heapSnapshotRow!.canvasSave(context); 233 (renders['heap-snapshot'] as HeapSnapshotRender).renderMainThread( 234 { 235 context: context, 236 useCache: useCache, 237 type: `heap-snapshot`, 238 }, 239 this.heapSnapshotRow! 240 ); 241 this.heapSnapshotRow!.canvasRestore(context, this.trace); 242 }; 243 } 244 } 245 } 246 247 private initJsCpuChart = async (): Promise<void> => { 248 this.jsCpuProfilerRow = TraceRow.skeleton<JsCpuProfilerStruct>(); 249 this.jsCpuProfilerRow.rowParentId = this.process; 250 this.jsCpuProfilerRow.rowHidden = !this.folderRow!.expansion; 251 this.jsCpuProfilerRow.name = 'CpuProfiler'; 252 this.jsCpuProfilerRow.rowId = 'JsCpuProfiler'; 253 this.jsCpuProfilerRow.folder = false; 254 this.jsCpuProfilerRow.rowType = TraceRow.ROW_TYPE_JS_CPU_PROFILER; 255 this.jsCpuProfilerRow!.style.height = '40px'; 256 this.jsCpuProfilerRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 257 this.jsCpuProfilerRow.selectChangeHandler = this.trace.selectChangeHandler; 258 this.jsCpuProfilerRow.setAttribute('children', ''); 259 this.jsCpuProfilerRow.focusHandler = (): void => { 260 this.trace?.displayTip( 261 this.jsCpuProfilerRow!, 262 JsCpuProfilerStruct.hoverJsCpuProfilerStruct, 263 `<span style='font-weight: bold;'>Name: </span> 264 <span>${JsCpuProfilerStruct.hoverJsCpuProfilerStruct?.name || ''}</span><br> 265 <span style='font-weight: bold;'>Self Time: </span> 266 <span>${ns2s(JsCpuProfilerStruct.hoverJsCpuProfilerStruct?.selfTime || 0)}</span><br> 267 <span style='font-weight: bold;'>Total Time: </span> 268 <span>${ns2s(JsCpuProfilerStruct.hoverJsCpuProfilerStruct?.totalTime || 0)}</span><br> 269 <span style='font-weight: bold;'>Url: </span> 270 <span>${JsCpuProfilerStruct.hoverJsCpuProfilerStruct?.url || 0}</span>` 271 ); 272 }; 273 this.jsCpuProfilerRow!.findHoverStruct = () => { 274 JsCpuProfilerStruct.hoverJsCpuProfilerStruct = this.jsCpuProfilerRow!.getHoverStruct(); 275 }; 276 this.jsCpuProfilerRow.onThreadHandler = (useCache): void => { 277 let context: CanvasRenderingContext2D; 278 if (this.jsCpuProfilerRow?.currentContext) { 279 context = this.jsCpuProfilerRow!.currentContext; 280 } else { 281 context = this.jsCpuProfilerRow!.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 282 } 283 this.jsCpuProfilerRow!.canvasSave(context); 284 (renders['js-cpu-profiler'] as JsCpuProfilerRender).renderMainThread( 285 { 286 context: context, 287 useCache: useCache, 288 type: `js-cpu-profiler`, 289 }, 290 this.jsCpuProfilerRow! 291 ); 292 this.jsCpuProfilerRow!.canvasRestore(context, this.trace); 293 }; 294 this.folderRow!.addChildTraceRow(this.jsCpuProfilerRow); 295 }; 296} 297