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