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.js'; 16import { TraceRow } from '../trace/base/TraceRow.js'; 17import { info } from '../../../log/Log.js'; 18import { renders } from '../../database/ui-worker/ProcedureWorker.js'; 19import { EmptyRender } from '../../database/ui-worker/ProcedureWorkerCPU.js'; 20import { HeapTimelineRender, HeapTimelineStruct } from '../../database/ui-worker/ProcedureWorkerHeapTimeline.js'; 21import { HeapDataInterface, ParseListener } from '../../../js-heap/HeapDataInterface.js'; 22import { LoadDatabase } from '../../../js-heap/LoadDatabase.js'; 23import { FileInfo } from '../../../js-heap/model/UiStruct.js'; 24import { HeapSnapshotRender, HeapSnapshotStruct } from '../../database/ui-worker/ProcedureWorkerHeapSnapshot.js'; 25import { procedurePool } from '../../database/Procedure.js'; 26import { Utils } from '../trace/base/Utils.js'; 27import { JsCpuProfilerChartFrame } from '../../bean/JsStruct.js'; 28import { JsCpuProfilerRender, JsCpuProfilerStruct } from '../../database/ui-worker/ProcedureWorkerCpuProfiler.js'; 29import { ns2s } from '../../database/ui-worker/ProcedureWorkerCommon.js'; 30import { queryJsCpuProfilerConfig, queryJsCpuProfilerData, queryJsMemoryData } from '../../database/SqlLite.js'; 31const TYPE_SNAPSHOT = 0; 32const TYPE_TIMELINE = 1; 33export class SpArkTsChart implements ParseListener { 34 private trace: SpSystemTrace; 35 private folderRow: TraceRow<any> | undefined; 36 private jsCpuProfilerRow: TraceRow<JsCpuProfilerStruct> | undefined; 37 private heapTimelineRow: TraceRow<HeapTimelineStruct> | undefined; 38 private heapSnapshotRow: TraceRow<HeapSnapshotStruct> | undefined; 39 private loadJsDatabase: LoadDatabase; 40 private allCombineDataMap = new Map<number, JsCpuProfilerChartFrame>(); 41 private process: string = ''; 42 constructor(trace: SpSystemTrace) { 43 this.trace = trace; 44 this.loadJsDatabase = LoadDatabase.getInstance(); 45 } 46 47 public get chartFrameMap(): Map<number, JsCpuProfilerChartFrame> { 48 return this.allCombineDataMap; 49 } 50 51 public async initFolder(): Promise<void> { 52 let jsConfig = await queryJsCpuProfilerConfig(); 53 54 let jsCpu = await queryJsCpuProfilerData(); 55 let jsMemory = await queryJsMemoryData(); 56 if (jsMemory.length > 0 || jsCpu.length > 0) { 57 this.folderRow = TraceRow.skeleton(); 58 this.process = jsConfig[0].pid; 59 this.folderRow.rowId = this.process; 60 this.folderRow.rowType = TraceRow.ROW_TYPE_ARK_TS; 61 this.folderRow.style.height = '40px'; 62 this.folderRow.rowParentId = ''; 63 this.folderRow.folder = true; 64 this.folderRow.name = `Ark Ts ` + this.process; 65 this.folderRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 66 this.folderRow.selectChangeHandler = this.trace.selectChangeHandler; 67 this.folderRow.supplier = (): Promise<Array<unknown>> => new Promise<Array<unknown>>((resolve) => resolve([])); 68 this.folderRow.onThreadHandler = (useCache): void => { 69 this.folderRow!.canvasSave(this.trace.canvasPanelCtx!); 70 if (this.folderRow!.expansion) { 71 this.trace.canvasPanelCtx?.clearRect(0, 0, this.folderRow!.frame.width, this.folderRow!.frame.height); 72 } else { 73 (renders['empty'] as EmptyRender).renderMainThread( 74 { 75 context: this.trace.canvasPanelCtx, 76 useCache: useCache, 77 type: ``, 78 }, 79 this.folderRow! 80 ); 81 } 82 this.folderRow!.canvasRestore(this.trace.canvasPanelCtx!); 83 }; 84 this.trace.rowsEL?.appendChild(this.folderRow); 85 if (this.folderRow && jsConfig[0].type !== -1 && jsMemory.length > 0) { 86 if (jsConfig[0].type === TYPE_SNAPSHOT) { 87 // snapshot 88 await this.initSnapshotChart(); 89 } else if (jsConfig[0].type === TYPE_TIMELINE) { 90 // timeline 91 await this.initTimelineChart(); 92 } 93 } 94 if (this.folderRow && jsConfig[0].enableCpuProfiler === 1 && jsCpu.length > 0) { 95 await this.initJsCpuChart(); 96 } 97 if ((this.heapSnapshotRow || this.heapTimelineRow) && jsMemory.length > 0) { 98 await this.initJsMemoryChartData(); 99 } 100 if (this.jsCpuProfilerRow && jsCpu.length > 0) { 101 await this.initJsCpuProfilerChartData(); 102 } 103 } 104 } 105 106 private async initTimelineChart(): Promise<void> { 107 this.heapTimelineRow = TraceRow.skeleton<HeapTimelineStruct>(); 108 this.heapTimelineRow.rowParentId = this.process; 109 this.heapTimelineRow.rowHidden = !this.folderRow!.expansion; 110 this.heapTimelineRow.style.height = '40px'; 111 this.heapTimelineRow.name = `Heaptimeline`; 112 this.heapTimelineRow.folder = false; 113 this.heapTimelineRow.rowType = TraceRow.ROW_TYPE_HEAP_TIMELINE; 114 this.heapTimelineRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 115 this.heapTimelineRow.selectChangeHandler = this.trace.selectChangeHandler; 116 this.heapTimelineRow.setAttribute('children', ''); 117 this.heapTimelineRow!.focusHandler = (): void => { 118 this.trace?.displayTip( 119 this.heapTimelineRow!, 120 HeapTimelineStruct.hoverHeapTimelineStruct, 121 `<span>Size: ${Utils.getBinaryByteWithUnit(HeapTimelineStruct.hoverHeapTimelineStruct?.size || 0)}</span>` 122 ); 123 }; 124 this.folderRow!.addChildTraceRow(this.heapTimelineRow!); 125 } 126 127 private async initSnapshotChart(): Promise<void> { 128 this.heapSnapshotRow = TraceRow.skeleton<HeapSnapshotStruct>(); 129 this.heapSnapshotRow.rowParentId = this.process; 130 this.heapSnapshotRow.rowHidden = !this.folderRow!.expansion; 131 this.heapSnapshotRow.style.height = '40px'; 132 this.heapSnapshotRow.name = `Heapsnapshot`; 133 this.heapSnapshotRow.rowId = `heapsnapshot`; 134 this.heapSnapshotRow.folder = false; 135 this.heapSnapshotRow.rowType = TraceRow.ROW_TYPE_HEAP_SNAPSHOT; 136 this.heapSnapshotRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 137 this.heapSnapshotRow.selectChangeHandler = this.trace.selectChangeHandler; 138 this.heapSnapshotRow.setAttribute('children', ''); 139 this.heapSnapshotRow!.focusHandler = (): void => { 140 this.trace?.displayTip( 141 this.heapSnapshotRow!, 142 HeapSnapshotStruct.hoverSnapshotStruct, 143 `<span>Name: ${HeapSnapshotStruct.hoverSnapshotStruct?.name || ''}</span> 144 <span>Size: ${Utils.getBinaryByteWithUnit(HeapSnapshotStruct.hoverSnapshotStruct?.size || 0)}</span>` 145 ); 146 }; 147 this.folderRow!.addChildTraceRow(this.heapSnapshotRow); 148 } 149 150 private initJsMemoryChartData = async (): Promise<void> => { 151 let time = new Date().getTime(); 152 await this.loadJsDatabase.loadDatabase(this); 153 let durTime = new Date().getTime() - time; 154 info('The time to load the js Memory data is: ', durTime); 155 }; 156 157 public async parseDone(fileModule: Array<FileInfo>): Promise<void> { 158 if (fileModule.length > 0) { 159 let heapFile = HeapDataInterface.getInstance().getFileStructs(); 160 let file = heapFile[0]; 161 this.trace.snapshotFile = file; 162 if (file.type === TYPE_TIMELINE) { 163 let samples = HeapDataInterface.getInstance().getSamples(file.id); 164 this.heapTimelineRow!.rowId = `heaptimeline` + file.id; 165 this.heapTimelineRow!.supplier = (): Promise<any> => new Promise<any>((resolve) => resolve(samples)); 166 this.heapTimelineRow!.onThreadHandler = (useCache): void => { 167 let context = this.heapTimelineRow!.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 168 this.heapTimelineRow!.canvasSave(context); 169 (renders['heap-timeline'] as HeapTimelineRender).renderMainThread( 170 { 171 context: context, 172 useCache: useCache, 173 type: `heap-timeline`, 174 samples: samples, 175 }, 176 this.heapTimelineRow! 177 ); 178 this.heapTimelineRow!.canvasRestore(context); 179 }; 180 } else if (file.type === TYPE_SNAPSHOT) { 181 this.heapSnapshotRow!.supplier = (): Promise<Array<any>> => 182 new Promise<Array<any>>((resolve) => resolve(heapFile)); 183 this.heapSnapshotRow!.onThreadHandler = (useCache): void => { 184 let context = this.heapSnapshotRow!.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 185 this.heapSnapshotRow!.canvasSave(context); 186 (renders['heap-snapshot'] as HeapSnapshotRender).renderMainThread( 187 { 188 context: context, 189 useCache: useCache, 190 type: `heap-snapshot`, 191 }, 192 this.heapSnapshotRow! 193 ); 194 this.heapSnapshotRow!.canvasRestore(context); 195 }; 196 } 197 } 198 } 199 200 private async initJsCpuProfilerChartData(): Promise<void> { 201 procedurePool.submitWithName( 202 'logic1', 203 'jsCpuProfiler-init', 204 SpSystemTrace.DATA_DICT, 205 undefined, 206 (res: Array<JsCpuProfilerChartFrame>) => { 207 let allCombineData: Array<JsCpuProfilerChartFrame> = []; 208 this.getAllCombineData(res, allCombineData); 209 this.allCombineDataMap = new Map<number, JsCpuProfilerChartFrame>(); 210 for (let data of allCombineData) { 211 this.allCombineDataMap.set(data.id, data); 212 } 213 let max = Math.max(...allCombineData.map((it) => it.depth || 0)) + 1; 214 let maxHeight = max * 20; 215 this.jsCpuProfilerRow!.style.height = `${maxHeight}px`; 216 this.jsCpuProfilerRow!.supplier = (): Promise<Array<any>> => 217 new Promise<Array<any>>((resolve) => resolve(allCombineData)); 218 } 219 ); 220 } 221 222 private initJsCpuChart = async (): Promise<void> => { 223 this.jsCpuProfilerRow = TraceRow.skeleton<JsCpuProfilerStruct>(); 224 this.jsCpuProfilerRow.rowParentId = this.process; 225 this.jsCpuProfilerRow.rowHidden = !this.folderRow!.expansion; 226 this.jsCpuProfilerRow.name = 'CpuProfiler'; 227 this.jsCpuProfilerRow.rowId = 'JsCpuProfiler'; 228 this.jsCpuProfilerRow.folder = false; 229 this.jsCpuProfilerRow.rowType = TraceRow.ROW_TYPE_JS_CPU_PROFILER; 230 this.jsCpuProfilerRow!.style.height = '40px'; 231 this.jsCpuProfilerRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 232 this.jsCpuProfilerRow.selectChangeHandler = this.trace.selectChangeHandler; 233 this.jsCpuProfilerRow.setAttribute('children', ''); 234 this.jsCpuProfilerRow.focusHandler = (): void => { 235 this.trace?.displayTip( 236 this.jsCpuProfilerRow!, 237 JsCpuProfilerStruct.hoverJsCpuProfilerStruct, 238 `<span style='font-weight: bold;'>Name: </span> 239 <span>${JsCpuProfilerStruct.hoverJsCpuProfilerStruct?.name || ''}</span><br> 240 <span style='font-weight: bold;'>Self Time: </span> 241 <span>${ns2s(JsCpuProfilerStruct.hoverJsCpuProfilerStruct?.selfTime || 0)}</span><br> 242 <span style='font-weight: bold;'>Total Time: </span> 243 <span>${ns2s(JsCpuProfilerStruct.hoverJsCpuProfilerStruct?.totalTime || 0)}</span><br> 244 <span style='font-weight: bold;'>Url: </span> 245 <span>${JsCpuProfilerStruct.hoverJsCpuProfilerStruct?.url || 0}</span>` 246 ); 247 }; 248 this.jsCpuProfilerRow.onThreadHandler = (useCache): void => { 249 let context = this.jsCpuProfilerRow!.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 250 this.jsCpuProfilerRow!.canvasSave(context); 251 (renders['js-cpu-profiler'] as JsCpuProfilerRender).renderMainThread( 252 { 253 context: context, 254 useCache: useCache, 255 type: `js-cpu-profiler`, 256 }, 257 this.jsCpuProfilerRow! 258 ); 259 this.jsCpuProfilerRow!.canvasRestore(context); 260 }; 261 this.folderRow!.addChildTraceRow(this.jsCpuProfilerRow); 262 }; 263 264 private getAllCombineData( 265 combineData: Array<JsCpuProfilerChartFrame>, 266 allCombineData: Array<JsCpuProfilerChartFrame> 267 ): void { 268 for (let data of combineData) { 269 allCombineData.push(data); 270 if (data.children.length > 0) { 271 this.getAllCombineData(data.children, allCombineData); 272 } 273 } 274 } 275} 276