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'; 17import { TraceRow } from '../trace/base/TraceRow'; 18import { info } from '../../../log/Log'; 19import { procedurePool } from '../../database/Procedure'; 20import { type NativeEventHeap } from '../../bean/NativeHook'; 21import { HeapRender, HeapStruct } from '../../database/ui-worker/ProcedureWorkerHeap'; 22import { Utils } from '../trace/base/Utils'; 23import { renders } from '../../database/ui-worker/ProcedureWorker'; 24import { EmptyRender } from '../../database/ui-worker/cpu/ProcedureWorkerCPU'; 25import { type BaseStruct } from '../../bean/BaseStruct'; 26import { 27 nativeMemoryChartDataCacheSender, 28 nativeMemoryChartDataSender, 29} from '../../database/data-trafic/NativeMemoryDataSender'; 30import {queryNativeHookProcess, queryNativeHookStatisticsCount} from "../../database/sql/NativeHook.sql"; 31import {queryHeapGroupByEvent} from "../../database/sql/SqlLite.sql"; 32import {queryNativeMemoryRealTime} from "../../database/sql/Memory.sql"; 33import {queryBootTime} from "../../database/sql/Clock.sql"; 34 35export class SpNativeMemoryChart { 36 static EVENT_HEAP: Array<NativeEventHeap> = []; 37 static REAL_TIME_DIF: number = 0; 38 private trace: SpSystemTrace; 39 40 constructor(trace: SpSystemTrace) { 41 this.trace = trace; 42 } 43 44 folderThreadHandler(row: TraceRow<BaseStruct>): void { 45 row.onThreadHandler = (useCache): void => { 46 row.canvasSave(this.trace.canvasPanelCtx!); 47 if (row.expansion) { 48 this.trace.canvasPanelCtx?.clearRect(0, 0, row.frame.width, row.frame.height); 49 } else { 50 (renders.empty as EmptyRender).renderMainThread( 51 { 52 context: this.trace.canvasPanelCtx, 53 useCache: useCache, 54 type: '', 55 }, 56 row 57 ); 58 } 59 row.canvasRestore(this.trace.canvasPanelCtx!, this.trace); 60 }; 61 } 62 63 chartThreadHandler(row: TraceRow<HeapStruct>): void { 64 row.onThreadHandler = (useCache): void => { 65 let context: CanvasRenderingContext2D; 66 if (row.currentContext) { 67 context = row.currentContext; 68 } else { 69 context = row.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 70 } 71 row.canvasSave(context); 72 (renders.heap as HeapRender).renderMainThread( 73 { 74 context: context, 75 useCache: useCache, 76 type: 'heap', 77 }, 78 row 79 ); 80 row.canvasRestore(context, this.trace); 81 }; 82 } 83 84 initNativeMemoryFolder(process: number, ipid: number): TraceRow<BaseStruct> { 85 const nativeRow = TraceRow.skeleton(); 86 nativeRow.rowId = `native-memory ${process} ${ipid}`; 87 nativeRow.index = 0; 88 nativeRow.rowType = TraceRow.ROW_TYPE_NATIVE_MEMORY; 89 nativeRow.drawType = 0; 90 nativeRow.style.height = '40px'; 91 nativeRow.rowParentId = ''; 92 nativeRow.folder = true; 93 nativeRow.addTemplateTypes('NativeMemory', 'Memory'); 94 nativeRow.name = `Native Memory (${process})`; 95 nativeRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 96 nativeRow.selectChangeHandler = this.trace.selectChangeHandler; 97 nativeRow.addRowSettingPop(); 98 nativeRow.rowSetting = 'enable'; 99 nativeRow.rowSettingPopoverDirection = 'bottomLeft'; 100 nativeRow.rowSettingList = [ 101 { 102 key: '0', 103 title: 'Current Bytes', 104 checked: true, 105 }, 106 { 107 key: '1', 108 title: 'Native Memory Density', 109 }, 110 ]; 111 nativeRow.onRowSettingChangeHandler = (value): void => { 112 nativeRow.childrenList.forEach((row) => (row.drawType = parseInt(value[0]))); 113 this.trace 114 .getCollectRows((row) => row.rowType === 'heap') 115 .forEach((it) => { 116 it.drawType = parseInt(value[0]); 117 }); 118 this.trace.refreshCanvas(false); 119 }; 120 nativeRow.supplier = (): Promise<BaseStruct[]> => new Promise<Array<BaseStruct>>((resolve) => resolve([])); 121 this.folderThreadHandler(nativeRow); 122 this.trace.rowsEL?.appendChild(nativeRow); 123 return nativeRow; 124 } 125 126 initAllocMapChart(folder: TraceRow<BaseStruct>, type: string, process: { pid: number; ipid: number }): void { 127 const chartList = ['All Heap & Anonymous VM', 'All Heap', 'All Anonymous VM']; 128 for (let i = 0; i < chartList.length; i++) { 129 const nm = chartList[i]; 130 const allHeapRow = TraceRow.skeleton<HeapStruct>(); 131 allHeapRow.index = i; 132 allHeapRow.rowParentId = `native-memory ${process.pid} ${process.ipid}`; 133 allHeapRow.rowHidden = !folder.expansion; 134 allHeapRow.style.height = '40px'; 135 allHeapRow.name = nm; 136 allHeapRow.rowId = nm; 137 allHeapRow.drawType = 0; 138 allHeapRow.isHover = true; 139 allHeapRow.folder = false; 140 allHeapRow.rowType = TraceRow.ROW_TYPE_HEAP; 141 allHeapRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 142 allHeapRow.selectChangeHandler = this.trace.selectChangeHandler; 143 allHeapRow.setAttribute('heap-type', type); 144 allHeapRow.setAttribute('children', ''); 145 allHeapRow.focusHandler = (): void => { 146 let tip = ''; 147 if (HeapStruct.hoverHeapStruct) { 148 if (allHeapRow.drawType === 1) { 149 tip = `<span>${HeapStruct.hoverHeapStruct.density}</span>`; 150 } else { 151 tip = `<span>${Utils.getByteWithUnit(HeapStruct.hoverHeapStruct.heapsize!)}</span>`; 152 } 153 } 154 this.trace?.displayTip(allHeapRow, HeapStruct.hoverHeapStruct, tip); 155 }; 156 allHeapRow.findHoverStruct = (): void => { 157 HeapStruct.hoverHeapStruct = allHeapRow.getHoverStruct(); 158 }; 159 allHeapRow.supplierFrame = (): Promise<any> => 160 nativeMemoryChartDataSender(allHeapRow, { 161 eventType: i, 162 ipid: process.ipid, 163 model: type, 164 drawType: allHeapRow.drawType, 165 }); 166 this.chartThreadHandler(allHeapRow); 167 folder.addChildTraceRow(allHeapRow); 168 } 169 } 170 171 initChart = async (): Promise<void> => { 172 let time = new Date().getTime(); 173 let nativeMemoryType = 'native_hook'; 174 let nmsCount = await queryNativeHookStatisticsCount(); 175 if (nmsCount && nmsCount[0] && nmsCount[0].num > 0) { 176 nativeMemoryType = 'native_hook_statistic'; 177 } 178 let nativeProcess = await queryNativeHookProcess(nativeMemoryType); 179 info('NativeHook Process data size is: ', nativeProcess!.length); 180 if (nativeProcess.length === 0) { 181 return; 182 } 183 await this.initNativeMemory(); 184 await nativeMemoryChartDataCacheSender( 185 nativeProcess.map((it) => it.ipid), 186 nativeMemoryType 187 ); 188 SpNativeMemoryChart.EVENT_HEAP = await queryHeapGroupByEvent(nativeMemoryType); 189 for (const process of nativeProcess) { 190 const nativeRow = this.initNativeMemoryFolder(process.pid, process.ipid); 191 this.initAllocMapChart(nativeRow, nativeMemoryType, process); 192 } 193 let durTime = new Date().getTime() - time; 194 info('The time to load the Native Memory data is: ', durTime); 195 }; 196 197 initNativeMemory = async (): Promise<void> => { 198 let time = new Date().getTime(); 199 let isRealtime = false; 200 let realTimeDif = 0; 201 SpNativeMemoryChart.REAL_TIME_DIF = 0; 202 let queryTime = await queryNativeMemoryRealTime(); 203 let bootTime = await queryBootTime(); 204 if (queryTime.length > 0) { 205 isRealtime = queryTime[0].clock_name === 'realtime'; 206 } 207 if (bootTime.length > 0 && isRealtime) { 208 realTimeDif = queryTime[0].ts - bootTime[0].ts; 209 SpNativeMemoryChart.REAL_TIME_DIF = realTimeDif; 210 } 211 await new Promise<any>((resolve) => { 212 procedurePool.submitWithName( 213 'logic0', 214 'native-memory-init', 215 { isRealtime, realTimeDif }, 216 undefined, 217 (res: any) => { 218 resolve(res); 219 } 220 ); 221 }); 222 let durTime = new Date().getTime() - time; 223 info('The time to init the native memory data is: ', durTime); 224 }; 225} 226