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 { renders } from '../../database/ui-worker/ProcedureWorker'; 18import { type EmptyRender } from '../../database/ui-worker/cpu/ProcedureWorkerCPU'; 19import { type HeapTimelineRender, HeapTimelineStruct } from '../../database/ui-worker/ProcedureWorkerHeapTimeline'; 20import { HeapDataInterface, type ParseListener } from '../../../js-heap/HeapDataInterface'; 21import { LoadDatabase } from '../../../js-heap/LoadDatabase'; 22import { type FileInfo } from '../../../js-heap/model/UiStruct'; 23import { type HeapSnapshotRender, HeapSnapshotStruct } from '../../database/ui-worker/ProcedureWorkerHeapSnapshot'; 24import { Utils } from '../trace/base/Utils'; 25import { type JsCpuProfilerChartFrame } from '../../bean/JsStruct'; 26import { type JsCpuProfilerRender, JsCpuProfilerStruct } from '../../database/ui-worker/ProcedureWorkerCpuProfiler'; 27import { ns2s } from '../../database/ui-worker/ProcedureWorkerCommon'; 28import { cpuProfilerDataSender } from '../../database/data-trafic/ArkTsSender'; 29import { queryJsCpuProfilerConfig, queryJsCpuProfilerData } from '../../database/sql/Cpu.sql'; 30import { queryJsMemoryData } from '../../database/sql/Memory.sql'; 31import { type HeapSample } from '../../../js-heap/model/DatabaseStruct'; 32import { SpStatisticsHttpUtil } from '../../../statistics/util/SpStatisticsHttpUtil'; 33 34const TYPE_SNAPSHOT = 0; 35const TYPE_TIMELINE = 1; 36const LAMBDA_FUNCTION_NAME = '(anonymous)'; 37export class SpArkTsChart implements ParseListener { 38 private trace: SpSystemTrace; // @ts-ignore 39 private folderRow: TraceRow<unknown> | undefined; 40 private jsCpuProfilerRow: TraceRow<JsCpuProfilerStruct> | undefined; 41 private heapTimelineRow: TraceRow<HeapTimelineStruct> | undefined; 42 private heapSnapshotRow: TraceRow<HeapSnapshotStruct> | undefined; 43 private loadJsDatabase: LoadDatabase; 44 private allCombineDataMap = new Map<number, JsCpuProfilerChartFrame>(); 45 private process: string = ''; 46 47 constructor(trace: SpSystemTrace) { 48 this.trace = trace; 49 this.loadJsDatabase = LoadDatabase.getInstance(); 50 } 51 52 public get chartFrameMap(): Map<number, JsCpuProfilerChartFrame> { 53 return this.allCombineDataMap; 54 } 55 56 private cpuProfilerSupplierFrame(): void { 57 // @ts-ignore 58 this.jsCpuProfilerRow!.supplierFrame = (): Promise<Array<unknown>> => { 59 return cpuProfilerDataSender(this.jsCpuProfilerRow!).then((res: unknown) => { 60 // @ts-ignore 61 let maxHeight = res.maxDepth * 20; 62 this.jsCpuProfilerRow!.style.height = `${maxHeight}px`; // @ts-ignore 63 if (res.dataList.length > 0) { 64 this.allCombineDataMap = new Map<number, JsCpuProfilerChartFrame>(); // @ts-ignore 65 for (let data of res.dataList) { 66 this.allCombineDataMap.set(data.id, data); 67 SpSystemTrace.jsProfilerMap.set(data.id, data); 68 } // @ts-ignore 69 res.dataList.forEach((data: unknown) => { 70 // @ts-ignore 71 data.children = []; // @ts-ignore 72 if (data.childrenIds.length > 0) { 73 // @ts-ignore 74 for (let id of data.childrenIds) { 75 let child = SpSystemTrace.jsProfilerMap.get(Number(id)); // @ts-ignore 76 data.children.push(child); 77 } 78 } // @ts-ignore 79 data.name = SpSystemTrace.DATA_DICT.get(data.nameId) || LAMBDA_FUNCTION_NAME; // @ts-ignore 80 data.url = SpSystemTrace.DATA_DICT.get(data.urlId) || 'unknown'; // @ts-ignore 81 if (data.url && data.url !== 'unknown') { 82 // @ts-ignore 83 let dirs = data.url.split('/'); // @ts-ignore 84 data.scriptName = dirs.pop() || ''; 85 } 86 }); 87 } // @ts-ignore 88 return res.dataList; 89 }); 90 }; 91 } 92 93 private folderThreadHandler(): void { 94 this.folderRow!.onThreadHandler = (useCache): void => { 95 this.folderRow!.canvasSave(this.trace.canvasPanelCtx!); 96 if (this.folderRow!.expansion) { 97 // @ts-ignore 98 this.trace.canvasPanelCtx?.clearRect(0, 0, this.folderRow!.frame.width, this.folderRow!.frame.height); 99 } else { 100 (renders.empty as EmptyRender).renderMainThread( 101 { 102 context: this.trace.canvasPanelCtx, 103 useCache: useCache, 104 type: '', 105 }, 106 this.folderRow! 107 ); 108 } 109 this.folderRow!.canvasRestore(this.trace.canvasPanelCtx!, this.trace); 110 }; 111 } 112 113 public async initFolder(): Promise<void> { 114 let jsConfig = await queryJsCpuProfilerConfig(); 115 let jsCpu = await queryJsCpuProfilerData(); 116 let jsMemory = await queryJsMemoryData(); 117 if (jsMemory.length > 0 || jsCpu.length > 0) { 118 this.folderRow = TraceRow.skeleton(); 119 //@ts-ignore 120 this.process = jsConfig[0].pid; 121 this.folderRow.rowId = this.process; 122 this.folderRow.rowType = TraceRow.ROW_TYPE_ARK_TS; 123 this.folderRow.style.height = '40px'; 124 this.folderRow.rowParentId = ''; 125 this.folderRow.folder = true; 126 this.folderRow.name = `Ark Ts ${this.process}`; 127 this.folderRow.addTemplateTypes('ArkTs'); 128 this.folderRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 129 this.folderRow.selectChangeHandler = this.trace.selectChangeHandler; 130 this.folderRow.supplierFrame = (): Promise<Array<unknown>> => 131 new Promise<Array<unknown>>((resolve) => resolve([])); 132 this.folderThreadHandler(); 133 this.trace.rowsEL?.appendChild(this.folderRow); //@ts-ignore 134 if (this.folderRow && jsConfig[0].type !== -1 && jsMemory.length > 0) { 135 this.folderRow.addTemplateTypes('Memory'); 136 if ( 137 //@ts-ignore 138 jsConfig[0].type === TYPE_SNAPSHOT 139 ) { 140 // snapshot 141 await this.initSnapshotChart(); 142 } else if ( 143 //@ts-ignore 144 jsConfig[0].type === TYPE_TIMELINE 145 ) { 146 // timeline 147 await this.initTimelineChart(); 148 } 149 } 150 //@ts-ignore 151 if (this.folderRow && jsConfig[0].enableCpuProfiler === 1 && jsCpu.length > 0) { 152 await this.initJsCpuChart(); 153 } 154 if ((this.heapSnapshotRow || this.heapTimelineRow) && jsMemory.length > 0) { 155 await this.loadJsDatabase.loadDatabase(this); 156 } 157 if (this.jsCpuProfilerRow && jsCpu.length > 0) { 158 this.cpuProfilerSupplierFrame(); 159 } 160 } 161 } 162 163 private async initTimelineChart(): Promise<void> { 164 this.heapTimelineRow = TraceRow.skeleton<HeapTimelineStruct>(); 165 this.heapTimelineRow.rowParentId = this.process; 166 this.heapTimelineRow.rowHidden = !this.folderRow!.expansion; 167 this.heapTimelineRow.style.height = '40px'; 168 this.heapTimelineRow.name = 'Heaptimeline'; 169 this.heapTimelineRow.folder = false; 170 this.heapTimelineRow.rowType = TraceRow.ROW_TYPE_HEAP_TIMELINE; 171 this.heapTimelineRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 172 this.heapTimelineRow.selectChangeHandler = this.trace.selectChangeHandler; 173 this.heapTimelineRow.setAttribute('children', ''); 174 this.heapTimelineRow!.focusHandler = (): void => { 175 this.trace?.displayTip( 176 this.heapTimelineRow!, 177 HeapTimelineStruct.hoverHeapTimelineStruct, 178 `<span>Size: ${Utils.getBinaryByteWithUnit(HeapTimelineStruct.hoverHeapTimelineStruct?.size || 0)}</span>` 179 ); 180 }; 181 this.heapTimelineRow!.findHoverStruct = (): void => { 182 HeapTimelineStruct.hoverHeapTimelineStruct = this.heapTimelineRow!.getHoverStruct(); 183 }; 184 this.folderRow!.addChildTraceRow(this.heapTimelineRow!); 185 } 186 187 private async initSnapshotChart(): Promise<void> { 188 this.heapSnapshotRow = TraceRow.skeleton<HeapSnapshotStruct>(); 189 this.heapSnapshotRow.rowParentId = this.process; 190 this.heapSnapshotRow.rowHidden = !this.folderRow!.expansion; 191 this.heapSnapshotRow.style.height = '40px'; 192 this.heapSnapshotRow.name = 'Heapsnapshot'; 193 this.heapSnapshotRow.rowId = 'heapsnapshot'; 194 this.heapSnapshotRow.folder = false; 195 196 this.heapSnapshotRow.rowType = TraceRow.ROW_TYPE_HEAP_SNAPSHOT; 197 this.heapSnapshotRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 198 this.heapSnapshotRow.selectChangeHandler = this.trace.selectChangeHandler; 199 this.heapSnapshotRow.setAttribute('children', ''); 200 this.heapSnapshotRow!.focusHandler = (): void => { 201 this.trace?.displayTip( 202 this.heapSnapshotRow!, 203 HeapSnapshotStruct.hoverSnapshotStruct, 204 `<span>Name: ${HeapSnapshotStruct.hoverSnapshotStruct?.name || ''}</span> 205 <span>Size: ${Utils.getBinaryByteWithUnit(HeapSnapshotStruct.hoverSnapshotStruct?.size || 0)}</span>` 206 ); 207 }; 208 this.heapSnapshotRow!.findHoverStruct = (): void => { 209 HeapSnapshotStruct.hoverSnapshotStruct = this.heapSnapshotRow!.getHoverStruct(); 210 }; 211 this.folderRow!.addChildTraceRow(this.heapSnapshotRow); 212 } 213 214 private heapLineThreadHandler(samples: HeapSample[]): void { 215 this.heapTimelineRow!.onThreadHandler = (useCache): void => { 216 let context: CanvasRenderingContext2D; 217 if (this.heapTimelineRow?.currentContext) { 218 context = this.heapTimelineRow!.currentContext; 219 } else { 220 context = this.heapTimelineRow!.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 221 } 222 this.heapTimelineRow!.canvasSave(context); 223 (renders['heap-timeline'] as HeapTimelineRender).renderMainThread( 224 { 225 context: context, 226 useCache: useCache, 227 type: 'heap-timeline', 228 samples: samples, 229 }, 230 this.heapTimelineRow! 231 ); 232 this.heapTimelineRow!.canvasRestore(context, this.trace); 233 }; 234 } 235 236 private heapSnapshotThreadHandler(): void { 237 this.heapSnapshotRow!.onThreadHandler = (useCache): void => { 238 let context: CanvasRenderingContext2D; 239 if (this.heapSnapshotRow?.currentContext) { 240 context = this.heapSnapshotRow!.currentContext; 241 } else { 242 context = this.heapSnapshotRow!.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 243 } 244 this.heapSnapshotRow!.canvasSave(context); 245 (renders['heap-snapshot'] as HeapSnapshotRender).renderMainThread( 246 { 247 context: context, 248 useCache: useCache, 249 type: 'heap-snapshot', 250 }, 251 this.heapSnapshotRow! 252 ); 253 this.heapSnapshotRow!.canvasRestore(context, this.trace); 254 }; 255 } 256 257 public async parseDone(fileModule: Array<FileInfo>): Promise<void> { 258 if (fileModule.length > 0) { 259 let heapFile = HeapDataInterface.getInstance().getFileStructs(); 260 let file = heapFile[0]; 261 this.trace.snapshotFile = file; 262 if (file.type === TYPE_TIMELINE) { 263 let samples = HeapDataInterface.getInstance().getSamples(file.id); 264 this.heapTimelineRow!.rowId = `heaptimeline${file.id}`; // @ts-ignore 265 this.heapTimelineRow!.supplierFrame = (): Promise<unknown> => 266 new Promise<unknown>((resolve) => resolve(samples)); 267 this.heapLineThreadHandler(samples); 268 } else if (file.type === TYPE_SNAPSHOT) { 269 // @ts-ignore 270 this.heapSnapshotRow!.supplierFrame = (): Promise<Array<unknown>> => 271 new Promise<Array<unknown>>((resolve) => resolve(heapFile)); 272 this.heapSnapshotThreadHandler(); 273 } 274 } 275 } 276 277 private initJsCpuChart = async (): Promise<void> => { 278 this.jsCpuProfilerRow = TraceRow.skeleton<JsCpuProfilerStruct>(); 279 this.jsCpuProfilerRow.rowParentId = this.process; 280 this.jsCpuProfilerRow.rowHidden = !this.folderRow!.expansion; 281 this.jsCpuProfilerRow.name = 'CpuProfiler'; 282 this.jsCpuProfilerRow.rowId = 'JsCpuProfiler'; 283 this.jsCpuProfilerRow.folder = false; 284 this.jsCpuProfilerRow.rowType = TraceRow.ROW_TYPE_JS_CPU_PROFILER; 285 this.jsCpuProfilerRow!.style.height = '40px'; 286 this.jsCpuProfilerRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 287 this.jsCpuProfilerRow.selectChangeHandler = this.trace.selectChangeHandler; 288 this.jsCpuProfilerRow.setAttribute('children', ''); 289 this.jsCpuProfilerRow.focusHandler = (): void => { 290 this.trace?.displayTip( 291 this.jsCpuProfilerRow!, 292 JsCpuProfilerStruct.hoverJsCpuProfilerStruct, 293 `<span style='font-weight: bold;'>Name: </span> 294 <span>${JsCpuProfilerStruct.hoverJsCpuProfilerStruct?.name || ''}</span><br> 295 <span style='font-weight: bold;'>Self Time: </span> 296 <span>${ns2s(JsCpuProfilerStruct.hoverJsCpuProfilerStruct?.selfTime || 0)}</span><br> 297 <span style='font-weight: bold;'>Total Time: </span> 298 <span>${ns2s(JsCpuProfilerStruct.hoverJsCpuProfilerStruct?.totalTime || 0)}</span><br> 299 <span style='font-weight: bold;'>Url: </span> 300 <span>${JsCpuProfilerStruct.hoverJsCpuProfilerStruct?.url || 0}</span>` 301 ); 302 }; 303 this.jsCpuProfilerRow!.findHoverStruct = (): void => { 304 JsCpuProfilerStruct.hoverJsCpuProfilerStruct = this.jsCpuProfilerRow!.getHoverStruct(); 305 }; 306 this.jsCpuProfilerRow.onThreadHandler = (useCache): void => { 307 let context: CanvasRenderingContext2D; 308 if (this.jsCpuProfilerRow?.currentContext) { 309 context = this.jsCpuProfilerRow!.currentContext; 310 } else { 311 context = this.jsCpuProfilerRow!.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 312 } 313 this.jsCpuProfilerRow!.canvasSave(context); 314 (renders['js-cpu-profiler'] as JsCpuProfilerRender).renderMainThread( 315 { 316 context: context, 317 useCache: useCache, 318 type: 'js-cpu-profiler', 319 }, 320 this.jsCpuProfilerRow! 321 ); 322 this.jsCpuProfilerRow!.canvasRestore(context, this.trace); 323 }; 324 this.folderRow!.addChildTraceRow(this.jsCpuProfilerRow); 325 }; 326} 327