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 */ 15 16import { SpSystemTrace } from '../SpSystemTrace.js'; 17import { 18 queryDmaSampsData, 19 queryGpuMemoryData, 20 querySmapsData, 21 querySmapsExits, 22 queryVmTrackerShmData, 23 queryPurgeableProcessData, 24 queryGpuGLData, 25 queryGpuTotalData, 26 queryGpuTotalType, 27 queryGpuWindowData, 28 queryGpuWindowType, 29} from '../../database/SqlLite.js'; 30import { TraceRow } from '../trace/base/TraceRow.js'; 31import { BaseStruct } from '../../bean/BaseStruct.js'; 32import { renders } from '../../database/ui-worker/ProcedureWorker.js'; 33import { Utils } from '../trace/base/Utils.js'; 34import { EmptyRender } from '../../database/ui-worker/ProcedureWorkerCPU.js'; 35import { info } from '../../../log/Log.js'; 36import { SnapshotRender, SnapshotStruct } from '../../database/ui-worker/ProcedureWorkerSnapshot.js'; 37import { TreeItemData } from '../../../base-ui/tree/LitTree.js'; 38import { MemoryConfig } from '../../bean/MemoryConfig.js'; 39 40export class VmTrackerChart { 41 private trace: SpSystemTrace; 42 private rowFolder!: TraceRow<BaseStruct>; 43 private sMapsFolder!: TraceRow<BaseStruct>; 44 private gpuFolder!: TraceRow<BaseStruct>; 45 private memoryConfig: MemoryConfig = MemoryConfig.getInstance(); 46 static gpuTotalModule: number | null = null; //ns 47 static gpuWindow: number | null = null; //ns 48 static gpuWindowModule: number | null = null; //ns 49 constructor(trace: SpSystemTrace) { 50 this.trace = trace; 51 } 52 53 async init(): Promise<void> { 54 const result = await querySmapsExits(); 55 if (result.length <= 0) { 56 return; 57 } 58 await this.initVmTrackerFolder(); 59 await this.initSMapsFolder(); 60 const rowNameList: Array<string> = ['Dirty', 'Swapped', 'RSS', 'PSS', 'USS']; 61 for (const rowName of rowNameList) { 62 await this.initSmapsRows(rowName); 63 } 64 await this.initShmRows(); 65 await this.initPurgeableVM(); 66 await this.initDmaRow(); 67 const gpuMemoryData = await queryGpuMemoryData(this.memoryConfig.iPid); 68 const glArr = await queryGpuGLData(MemoryConfig.getInstance().iPid).then((res) => { 69 res.forEach((gl, index) => { 70 (gl as any).name = `SnapShot ${index}`; 71 }); 72 return res as SnapshotStruct[]; 73 }); 74 if (gpuMemoryData.length > 0 || glArr.length > 0) { 75 await this.initGpuFolder(); 76 if (gpuMemoryData.length > 0) { 77 await this.initGpuMemoryRow(gpuMemoryData); 78 } 79 await this.addGpuGLRow(glArr); 80 if (glArr.length > 0) { 81 await this.addGpuTotalRow(); 82 await this.addGpuWindowRow(); 83 } 84 } 85 } 86 87 private initVmTrackerFolder = async (): Promise<void> => { 88 let VmTrackerRow = TraceRow.skeleton(); 89 VmTrackerRow.rowId = 'VmTrackerRow'; 90 VmTrackerRow.rowType = TraceRow.ROW_TYPE_VM_TRACKER; 91 VmTrackerRow.rowParentId = ''; 92 VmTrackerRow.style.height = '40px'; 93 VmTrackerRow.index = 0; 94 VmTrackerRow.folder = true; 95 VmTrackerRow.name = `VM Tracker (${this.memoryConfig.processName} ${this.memoryConfig.pid})`; 96 VmTrackerRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 97 VmTrackerRow.selectChangeHandler = this.trace.selectChangeHandler; 98 VmTrackerRow.supplier = (): Promise<Array<SnapshotStruct>> => 99 new Promise<Array<SnapshotStruct>>((resolve) => resolve([])); 100 VmTrackerRow.onThreadHandler = (useCache): void => { 101 VmTrackerRow.canvasSave(this.trace.canvasPanelCtx!); 102 if (VmTrackerRow.expansion) { 103 this.trace.canvasPanelCtx?.clearRect(0, 0, VmTrackerRow.frame.width, VmTrackerRow.frame.height); 104 } else { 105 (renders.empty as EmptyRender).renderMainThread( 106 { 107 context: this.trace.canvasPanelCtx, 108 useCache: useCache, 109 type: '', 110 }, 111 VmTrackerRow 112 ); 113 } 114 VmTrackerRow.canvasRestore(this.trace.canvasPanelCtx!); 115 }; 116 this.rowFolder = VmTrackerRow; 117 this.trace.rowsEL?.appendChild(VmTrackerRow); 118 }; 119 120 private initSMapsFolder = async (): Promise<void> => { 121 let sMapsRow = TraceRow.skeleton<SnapshotStruct>(); 122 sMapsRow.rowId = 'smapsRow'; 123 sMapsRow.rowParentId = 'VmTrackerRow'; 124 sMapsRow.rowHidden = !this.rowFolder.expansion; 125 sMapsRow.rowType = TraceRow.ROW_TYPE_VM_TRACKER_SMAPS; 126 sMapsRow.folder = true; 127 sMapsRow.name = 'Smaps'; 128 sMapsRow.folderPaddingLeft = 20; 129 sMapsRow.style.height = '40px'; 130 sMapsRow.style.width = '100%'; 131 sMapsRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 132 sMapsRow.selectChangeHandler = this.trace.selectChangeHandler; 133 sMapsRow.supplier = (): Promise<Array<SnapshotStruct>> => 134 new Promise<Array<SnapshotStruct>>((resolve) => resolve([])); 135 sMapsRow.onThreadHandler = (useCache): void => { 136 sMapsRow.canvasSave(this.trace.canvasPanelCtx!); 137 if (sMapsRow.expansion) { 138 this.trace.canvasPanelCtx?.clearRect(0, 0, sMapsRow.frame.width, sMapsRow.frame.height); 139 } else { 140 (renders.empty as EmptyRender).renderMainThread( 141 { 142 context: this.trace.canvasPanelCtx, 143 useCache: useCache, 144 type: '', 145 }, 146 sMapsRow 147 ); 148 } 149 sMapsRow.canvasRestore(this.trace.canvasPanelCtx!); 150 }; 151 this.sMapsFolder = sMapsRow; 152 this.rowFolder?.addChildTraceRow(sMapsRow); 153 }; 154 155 private initGpuFolder = async (): Promise<TraceRow<SnapshotStruct>> => { 156 let gpuTraceRow = TraceRow.skeleton<SnapshotStruct>(); 157 gpuTraceRow.rowId = 'skiaGpuTraceRow'; 158 gpuTraceRow.rowType = TraceRow.ROW_TYPE_SYS_MEMORY_GPU; 159 gpuTraceRow.rowParentId = 'VmTrackerRow'; 160 gpuTraceRow.style.height = '40px'; 161 gpuTraceRow.folder = true; 162 gpuTraceRow.folderPaddingLeft = 20; 163 gpuTraceRow.rowHidden = !this.rowFolder.expansion; 164 gpuTraceRow.name = 'GPU'; 165 gpuTraceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 166 gpuTraceRow.selectChangeHandler = this.trace.selectChangeHandler; 167 gpuTraceRow.supplier = (): Promise<Array<SnapshotStruct>> => 168 new Promise<Array<SnapshotStruct>>((resolve) => resolve([])); 169 gpuTraceRow.onThreadHandler = (useCache): void => { 170 gpuTraceRow.canvasSave(this.trace.canvasPanelCtx!); 171 if (gpuTraceRow.expansion) { 172 this.trace.canvasPanelCtx?.clearRect(0, 0, gpuTraceRow.frame.width, gpuTraceRow.frame.height); 173 } else { 174 (renders.empty as EmptyRender).renderMainThread( 175 { 176 context: this.trace.canvasPanelCtx, 177 useCache: useCache, 178 type: '', 179 }, 180 gpuTraceRow 181 ); 182 } 183 gpuTraceRow.canvasRestore(this.trace.canvasPanelCtx!); 184 }; 185 this.gpuFolder = gpuTraceRow; 186 this.rowFolder.addChildTraceRow(gpuTraceRow); 187 return gpuTraceRow; 188 }; 189 190 private getSmapsKeyName(rowName: string): string { 191 let columnName = rowName.toLowerCase(); 192 let keyName = ''; 193 switch (rowName) { 194 case 'USS': 195 keyName = 'private_clean + private_dirty'; 196 break; 197 case 'RSS': 198 keyName = 'resident_size'; 199 break; 200 default: 201 keyName = columnName; 202 } 203 return keyName; 204 } 205 206 private initSmapsRows = async (rowName: string): Promise<void> => { 207 let sMapsTraceRow = this.initTraceRow(rowName, TraceRow.ROW_TYPE_VM_TRACKER_SMAPS, 'smapsRow'); 208 sMapsTraceRow.rowHidden = !this.sMapsFolder.expansion; 209 sMapsTraceRow.folderTextLeft = 40; 210 let sMapsData = await querySmapsData(this.getSmapsKeyName(rowName)); 211 for (let i = 0; i < sMapsData.length; i++) { 212 sMapsData[i].name = `Snapshot${i}`; 213 } 214 sMapsTraceRow.supplier = (): Promise<Array<SnapshotStruct>> => 215 new Promise<Array<SnapshotStruct>>((resolve) => resolve(sMapsData)); 216 this.sMapsFolder.addChildTraceRow(sMapsTraceRow); 217 }; 218 219 private initShmRows = async (): Promise<void> => { 220 let shmTraceRow = this.initTraceRow('SHM', TraceRow.ROW_TYPE_VMTRACKER_SHM, 'VmTrackerRow'); 221 let shmData = await queryVmTrackerShmData(this.memoryConfig.iPid); 222 for (let i = 0; i < shmData.length; i++) { 223 shmData[i].name = `Snapshot${i}`; 224 } 225 shmTraceRow.supplier = (): Promise<Array<SnapshotStruct>> => 226 new Promise<Array<SnapshotStruct>>((resolve) => resolve(shmData)); 227 if (shmData.length > 0) { 228 this.rowFolder.addChildTraceRow(shmTraceRow); 229 } 230 }; 231 232 private async initPurgeableTotal(): Promise<void> { 233 let purgeableTotalData = await queryPurgeableProcessData(this.memoryConfig.iPid); 234 if (purgeableTotalData.length > 0) { 235 for (let i = 0; i < purgeableTotalData.length; i++) { 236 purgeableTotalData[i].name = `Snapshot${i}`; 237 } 238 let totalTraceRow = this.initTraceRow('Purgeable Total', TraceRow.ROW_TYPE_PURGEABLE_TOTAL_VM, 'VmTrackerRow'); 239 totalTraceRow.supplier = (): Promise<Array<SnapshotStruct>> => 240 new Promise<Array<SnapshotStruct>>((resolve) => resolve(purgeableTotalData)); 241 this.rowFolder.addChildTraceRow(totalTraceRow); 242 } 243 } 244 245 private async initPurgeablePin(): Promise<void> { 246 let purgeablePinData = await queryPurgeableProcessData(this.memoryConfig.iPid, true); 247 if (purgeablePinData.length > 0) { 248 for (let i = 0; i < purgeablePinData.length; i++) { 249 purgeablePinData[i].name = `Snapshot${i}`; 250 } 251 let pinTraceRow = this.initTraceRow('Purgeable Pin', TraceRow.ROW_TYPE_PURGEABLE_PIN_VM, 'VmTrackerRow'); 252 pinTraceRow.supplier = (): Promise<Array<SnapshotStruct>> => 253 new Promise<Array<SnapshotStruct>>((resolve) => resolve(purgeablePinData)); 254 this.rowFolder.addChildTraceRow(pinTraceRow); 255 } 256 } 257 258 private initPurgeableVM = async (): Promise<void> => { 259 let time = new Date().getTime(); 260 await this.initPurgeableTotal(); 261 await this.initPurgeablePin(); 262 let durTime = new Date().getTime() - time; 263 info('The time to load the VM Purgeable is: ', durTime); 264 }; 265 266 private initDmaRow = async (): Promise<void> => { 267 let dmaData = await queryDmaSampsData(this.memoryConfig.iPid); 268 if (dmaData.length > 0) { 269 for (let i = 0; i < dmaData.length; i++) { 270 dmaData[i].name = `Snapshot${i}`; 271 } 272 let dmaTraceRow = this.initTraceRow('DMA', TraceRow.ROW_TYPE_DMA_VMTRACKER, 'VmTrackerRow'); 273 dmaTraceRow.supplier = (): Promise<Array<SnapshotStruct>> => 274 new Promise<Array<SnapshotStruct>>((resolve) => resolve(dmaData)); 275 this.rowFolder.addChildTraceRow(dmaTraceRow); 276 } 277 }; 278 279 private initGpuMemoryRow = async (gpuMemoryData: Array<SnapshotStruct>): Promise<void> => { 280 for (let i = 0; i < gpuMemoryData.length; i++) { 281 gpuMemoryData[i].name = `Snapshot${i}`; 282 } 283 let gpuMemoryTraceRow = this.initTraceRow( 284 'Skia Gpu Memory', 285 TraceRow.ROW_TYPE_GPU_MEMORY_VMTRACKER, 286 'skiaGpuTraceRow' 287 ); 288 gpuMemoryTraceRow.rowHidden = !this.gpuFolder.expansion; 289 gpuMemoryTraceRow.folderTextLeft = 40; 290 gpuMemoryTraceRow.supplier = (): Promise<Array<SnapshotStruct>> => 291 new Promise<Array<SnapshotStruct>>((resolve) => resolve(gpuMemoryData)); 292 this.gpuFolder.addChildTraceRow(gpuMemoryTraceRow); 293 }; 294 295 private async addGpuGLRow(glArr: Array<SnapshotStruct>): Promise<void> { 296 if (glArr.length > 0) { 297 let glRow = this.initTraceRow('GL', TraceRow.ROW_TYPE_SYS_MEMORY_GPU_GL, this.gpuFolder.rowId!); 298 glRow.addTemplateTypes('sys-memory'); 299 glRow.folderTextLeft = 40; 300 glRow.supplier = () => new Promise((resolve) => resolve(glArr)); 301 this.gpuFolder.addChildTraceRow(glRow); 302 } 303 } 304 305 private async addGpuTotalRow(): Promise<void> { 306 let types = await queryGpuTotalType(); 307 let gpuTotalRow = this.initTraceRow( 308 'Skia Gpu Dump Total', 309 TraceRow.ROW_TYPE_SYS_MEMORY_GPU_TOTAL, 310 this.gpuFolder.rowId! 311 ); 312 gpuTotalRow.folderTextLeft = 40; 313 gpuTotalRow.addTemplateTypes('sys-memory'); 314 gpuTotalRow.rowSetting = 'enable'; 315 gpuTotalRow.rowSettingList = [ 316 { 317 key: 'total', 318 title: 'Total', 319 checked: true, 320 }, 321 ...types.map((it) => { 322 return { 323 key: `${it.id}`, 324 title: it.data, 325 }; 326 }), 327 ]; 328 gpuTotalRow.onRowSettingChangeHandler = (setting) => { 329 if (setting && setting.length > 0) { 330 gpuTotalRow.dataListCache = []; 331 gpuTotalRow.dataList = []; 332 gpuTotalRow.isComplete = false; 333 VmTrackerChart.gpuTotalModule = setting[0] === 'total' ? null : parseInt(setting[0]); 334 this.trace.refreshCanvas(false); 335 } 336 }; 337 gpuTotalRow.supplier = (): Promise<Array<SnapshotStruct>> => { 338 return queryGpuTotalData(VmTrackerChart.gpuTotalModule).then((res) => { 339 res.forEach((it, index) => { 340 (it as any).name = `SnapShot ${index}`; 341 }); 342 return res as SnapshotStruct[]; 343 }); 344 }; 345 this.gpuFolder.addChildTraceRow(gpuTotalRow); 346 } 347 348 private async addGpuWindowRow(): Promise<void> { 349 let types = await queryGpuWindowType(); 350 let settings: TreeItemData[] = types 351 .filter((it) => it.pid === null) 352 .map((it) => { 353 return { 354 key: `${it.id}`, 355 title: it.data, 356 children: [], 357 }; 358 }); 359 settings.forEach((it) => { 360 it.children = types 361 .filter((child) => `${child.pid}` === it.key) 362 .map((item) => { 363 return { 364 key: `${it.key}-${item.id}`, 365 title: item.data, 366 }; 367 }); 368 }); 369 settings[0].checked = true; 370 VmTrackerChart.gpuWindow = parseInt(settings[0].key); 371 VmTrackerChart.gpuWindowModule = null; 372 let gpuWindowRow = this.initTraceRow( 373 'Skia Gpu Dump Window', 374 TraceRow.ROW_TYPE_SYS_MEMORY_GPU_WINDOW, 375 this.gpuFolder.rowId! 376 ); 377 gpuWindowRow.folderTextLeft = 40; 378 gpuWindowRow.rowSetting = 'enable'; 379 gpuWindowRow.rowSettingList = settings; 380 gpuWindowRow.addTemplateTypes('sys-memory'); 381 gpuWindowRow.onRowSettingChangeHandler = (setting) => { 382 if (setting && setting.length > 0) { 383 let split = setting[0].split('-'); 384 VmTrackerChart.gpuWindow = parseInt(split[0]); 385 VmTrackerChart.gpuWindowModule = split.length > 1 ? parseInt(split[1]) : null; 386 gpuWindowRow.dataListCache = []; 387 gpuWindowRow.dataList = []; 388 gpuWindowRow.isComplete = false; 389 this.trace.refreshCanvas(false); 390 } 391 }; 392 gpuWindowRow.supplier = () => { 393 return queryGpuWindowData(VmTrackerChart.gpuWindow!, VmTrackerChart.gpuWindowModule).then((res) => { 394 res.forEach((window, index) => { 395 (window as any).name = `SnapShot ${index}`; 396 }); 397 return res as SnapshotStruct[]; 398 }); 399 }; 400 this.gpuFolder.addChildTraceRow(gpuWindowRow); 401 } 402 403 private initTraceRow(rowName: string, type: string, rowParentId: string): TraceRow<SnapshotStruct> { 404 let vmTrackerTraceRow = TraceRow.skeleton<SnapshotStruct>(); 405 vmTrackerTraceRow.rowParentId = rowParentId; 406 vmTrackerTraceRow.rowId = rowName; 407 vmTrackerTraceRow.rowType = type; 408 vmTrackerTraceRow.folderTextLeft = 20; 409 vmTrackerTraceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 410 vmTrackerTraceRow.selectChangeHandler = this.trace.selectChangeHandler; 411 vmTrackerTraceRow.style.height = '40px'; 412 vmTrackerTraceRow.style.width = '100%'; 413 vmTrackerTraceRow.setAttribute('children', ''); 414 vmTrackerTraceRow.name = rowName; 415 vmTrackerTraceRow.focusHandler = (): void => { 416 this.showTip(vmTrackerTraceRow); 417 }; 418 vmTrackerTraceRow.onThreadHandler = (useCache): void => { 419 let context = vmTrackerTraceRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 420 vmTrackerTraceRow.canvasSave(context); 421 (renders.snapshot as SnapshotRender).renderMainThread( 422 { 423 context: context, 424 useCache: useCache, 425 type: 'snapshot', 426 }, 427 vmTrackerTraceRow 428 ); 429 vmTrackerTraceRow.canvasRestore(context); 430 }; 431 return vmTrackerTraceRow; 432 } 433 434 private showTip(traceRow: TraceRow<SnapshotStruct>): void { 435 this.trace?.displayTip( 436 traceRow, 437 SnapshotStruct.hoverSnapshotStruct, 438 `<span>Name: ${SnapshotStruct.hoverSnapshotStruct?.name || ''}</span> 439 <span>Size: ${Utils.getBinaryByteWithUnit(SnapshotStruct.hoverSnapshotStruct?.value || 0)}</span>` 440 ); 441 } 442} 443