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 { BaseElement, element } from '../../../../../base-ui/BaseElement'; 17import { LitTable } from '../../../../../base-ui/table/lit-table'; 18import { SelectionParam } from '../../../../bean/BoxSelection'; 19import { NativeHookMalloc, NativeHookStatisticsTableData } from '../../../../bean/NativeHook'; 20import { Utils } from '../../base/Utils'; 21import { SpSystemTrace } from '../../../SpSystemTrace'; 22import '../TabProgressBar'; 23import { SpNativeMemoryChart } from '../../../chart/SpNativeMemoryChart'; 24import { resizeObserver } from '../SheetUtils'; 25import { TabPaneNMSampleList } from './TabPaneNMSampleList'; 26import { 27 queryNativeHookStatistics, 28 queryNativeHookStatisticsMalloc, 29 queryNativeHookStatisticsSubType, 30} from '../../../../database/sql/NativeHook.sql'; 31import { queryHeapSizeByIpid } from '../../../../database/sql/SqlLite.sql'; 32 33 34@element('tabpane-native-statistics') 35export class TabPaneNMStatstics extends BaseElement { 36 private nativeStatisticsTbl: LitTable | null | undefined; 37 private nativeStatisticsSource: Array<NativeHookStatisticsTableData> = []; 38 private nativeType: Array<string> = ['All Heap & Anonymous VM', 'All Heap', 'All Anonymous VM']; 39 private allMax: number = 0; 40 private sortColumn: string = ''; 41 private sortType: number = 0; 42 private currentSelection: SelectionParam | undefined; 43 private currentSelectIPid = 1; 44 45 set data(nativeStatisticsParam: SelectionParam) { 46 if (nativeStatisticsParam === this.currentSelection) { 47 return; 48 } 49 this.currentSelectIPid = nativeStatisticsParam.nativeMemoryCurrentIPid; 50 this.currentSelection = nativeStatisticsParam; 51 this.allMax = 0; 52 TabPaneNMSampleList.clearData(); 53 this.recordEventHeap(nativeStatisticsParam.nativeMemoryCurrentIPid); 54 if (nativeStatisticsParam.nativeMemory.length > 0) { 55 Utils.getInstance().setCurrentSelectIPid(this.currentSelectIPid); 56 Utils.getInstance().initResponseTypeList(nativeStatisticsParam); 57 } 58 if (this.nativeStatisticsTbl) { 59 // @ts-ignore 60 this.nativeStatisticsTbl.shadowRoot.querySelector('.table').style.height = `${this.parentElement!.clientHeight - 25 61 }px`; 62 // @ts-ignore 63 this.nativeStatisticsTbl.recycleDataSource = []; 64 } 65 this.nativeStatisticsTbl!.loading = true; 66 this.queryData(nativeStatisticsParam); 67 } 68 69 async recordEventHeap(ipid: number): Promise<void> { 70 SpNativeMemoryChart.EVENT_HEAP = await queryHeapSizeByIpid(ipid); 71 SpNativeMemoryChart.EVENT_HEAP.map((heap) => { 72 this.allMax += heap.sumHeapSize; 73 }); 74 } 75 76 queryData(nativeStatisticsParam: SelectionParam): void { 77 Promise.all([ 78 queryNativeHookStatistics(nativeStatisticsParam.leftNs, nativeStatisticsParam.rightNs, this.currentSelectIPid), 79 queryNativeHookStatisticsSubType( 80 nativeStatisticsParam.leftNs, 81 nativeStatisticsParam.rightNs, 82 this.currentSelectIPid 83 ), 84 queryNativeHookStatisticsMalloc( 85 nativeStatisticsParam.leftNs, 86 nativeStatisticsParam.rightNs, 87 this.currentSelectIPid 88 ), 89 ]).then((values) => { 90 this.nativeStatisticsTbl!.loading = false; 91 let arr: Array<NativeHookStatisticsTableData> = []; 92 let index1 = nativeStatisticsParam.nativeMemory.indexOf(this.nativeType[0]); 93 let index2 = nativeStatisticsParam.nativeMemory.indexOf(this.nativeType[1]); 94 let index3 = nativeStatisticsParam.nativeMemory.indexOf(this.nativeType[2]); 95 this.setMemoryTypeData(nativeStatisticsParam, values[0], arr); 96 if (index1 !== -1 || index3 !== -1) { 97 this.setSubTypeTableData(values[1], arr); 98 } 99 let type = 0; 100 if (index1 !== -1 || (index2 !== -1 && index3 !== -1)) { 101 type = 0; 102 } else { 103 type = index2 !== -1 ? 1 : 2; 104 } 105 this.setMallocTableData(values[2], arr, type); 106 this.nativeStatisticsSource = arr; 107 this.sortByColumn(this.sortColumn, this.sortType); 108 }); 109 } 110 111 setMallocTableData(result: Array<NativeHookMalloc>, arr: Array<NativeHookStatisticsTableData>, type: number): void { 112 result.map((malloc) => { 113 let data = new NativeHookStatisticsTableData(); 114 if (malloc.eventType === 'AllocEvent') { 115 data.memoryTap = `Malloc ${Utils.getByteWithUnit(malloc.heapSize)}`; 116 } else { 117 data.memoryTap = `Mmap ${Utils.getByteWithUnit(malloc.heapSize)}`; 118 } 119 data.existing = malloc.allocByte - malloc.freeByte; 120 data.allocCount = malloc.allocCount - malloc.freeCount; 121 data.freeCount = malloc.freeCount; 122 data.freeByte = malloc.freeByte; 123 data.totalBytes = malloc.allocByte; 124 data.totalCount = malloc.allocCount; 125 data.max = malloc.heapSize; 126 data.existingString = Utils.getByteWithUnit(data.existing); 127 data.freeByteString = Utils.getByteWithUnit(malloc.freeByte); 128 data.totalBytesString = Utils.getByteWithUnit(data.totalBytes); 129 data.maxStr = Utils.getByteWithUnit(malloc.heapSize); 130 data.existingValue = [data.existing, data.totalBytes, this.allMax]; 131 if (type === 0) { 132 arr.push(data); 133 } else if (type === 1 && malloc.eventType === 'AllocEvent') { 134 arr.push(data); 135 } else if (type === 2 && malloc.eventType === 'MmapEvent') { 136 arr.push(data); 137 } else { 138 } 139 }); 140 } 141 142 setSubTypeTableData(result: Array<NativeHookMalloc>, arr: Array<NativeHookStatisticsTableData>): void { 143 result.map((sub) => { 144 let subType = SpSystemTrace.DATA_DICT.get(sub.subTypeId); 145 if (subType !== null && subType !== undefined) { 146 let data = new NativeHookStatisticsTableData(); 147 data.memoryTap = subType; 148 data.existing = sub.allocByte - sub.freeByte; 149 data.allocCount = sub.allocCount - sub.freeCount; 150 data.freeCount = sub.freeCount; 151 data.freeByte = sub.freeByte; 152 data.totalBytes = sub.allocByte; 153 data.totalCount = sub.allocCount; 154 data.max = sub.max; 155 data.freeByteString = Utils.getByteWithUnit(sub.freeByte); 156 data.existingString = Utils.getByteWithUnit(data.existing); 157 data.totalBytesString = Utils.getByteWithUnit(data.totalBytes); 158 data.maxStr = Utils.getByteWithUnit(sub.max); 159 data.existingValue = [data.existing, data.totalBytes, this.allMax]; 160 arr.push(data); 161 } 162 }); 163 } 164 165 setMemoryTypeData( 166 val: SelectionParam, 167 result: Array<NativeHookMalloc>, 168 arr: Array<NativeHookStatisticsTableData> 169 ): void { 170 let all: NativeHookStatisticsTableData | null = null; 171 let heap: NativeHookStatisticsTableData | null = null; 172 let anonymous: NativeHookStatisticsTableData | null = null; 173 if (val.nativeMemory.indexOf(this.nativeType[0]) !== -1) { 174 all = new NativeHookStatisticsTableData(); 175 all.memoryTap = this.nativeType[0]; 176 } 177 if (val.nativeMemory.indexOf(this.nativeType[1]) !== -1) { 178 heap = new NativeHookStatisticsTableData(); 179 heap.memoryTap = this.nativeType[1]; 180 } 181 if (val.nativeMemory.indexOf(this.nativeType[2]) !== -1) { 182 anonymous = new NativeHookStatisticsTableData(); 183 anonymous.memoryTap = this.nativeType[2]; 184 } 185 for (let hook of result) { 186 if (all !== null) { 187 this.processHookData(hook, all); 188 } 189 if (heap !== null && hook.eventType === 'AllocEvent') { 190 this.processHookData(hook, heap); 191 } 192 if (anonymous !== null && hook.eventType === 'MmapEvent') { 193 this.processHookData(hook, anonymous); 194 } 195 } 196 if (all?.maxStr === '' && all?.max === 0) { 197 all.maxStr = Utils.getByteWithUnit(all?.max); 198 } 199 if (heap?.maxStr === '' && heap?.max === 0) { 200 heap.maxStr = Utils.getByteWithUnit(heap?.max); 201 } 202 if (anonymous?.maxStr === '' && anonymous?.max === 0) { 203 anonymous.maxStr = Utils.getByteWithUnit(anonymous?.max); 204 } 205 if (all !== null) { 206 this.updateHookData(all, arr); 207 } 208 if (heap !== null) { 209 this.updateHookData(heap, arr); 210 } 211 if (anonymous !== null) { 212 this.updateHookData(anonymous, arr); 213 } 214 } 215 216 private processHookData(hook: unknown, data: NativeHookStatisticsTableData): void { 217 // @ts-ignore 218 data.totalBytes += hook.allocByte; 219 // @ts-ignore 220 data.totalCount += hook.allocCount; 221 // @ts-ignore 222 data.freeByte += hook.freeByte; 223 // @ts-ignore 224 data.freeCount += hook.freeCount; // @ts-ignore 225 if (hook.max > data.max) { 226 // @ts-ignore 227 data.max = hook.max; 228 data.maxStr = Utils.getByteWithUnit(data.max); 229 } 230 } 231 232 private updateHookData(data: NativeHookStatisticsTableData, arr: Array<NativeHookStatisticsTableData>): void { 233 data.existing = data.totalBytes - data.freeByte; 234 data.allocCount = data.totalCount - data.freeCount; 235 data.existingString = Utils.getByteWithUnit(data.existing); 236 data.totalBytesString = Utils.getByteWithUnit(data.totalBytes); 237 data.freeByteString = Utils.getByteWithUnit(data.freeByte); 238 data.existingValue = [data.existing, data.totalBytes, this.allMax]; 239 arr.push(data); 240 } 241 242 initElements(): void { 243 this.nativeStatisticsTbl = this.shadowRoot?.querySelector<LitTable>('#tb-native-statstics'); 244 this.nativeStatisticsTbl!.addEventListener('column-click', (evt) => { 245 // @ts-ignore 246 this.sortByColumn(evt.detail.key, evt.detail.sort); 247 }); 248 this.nativeStatisticsTbl!.exportTextHandleMap.set('existingString', (value) => { 249 // @ts-ignore 250 return `${value.existing}`; 251 }); 252 this.nativeStatisticsTbl!.exportTextHandleMap.set('freeByteString', (value) => { 253 // @ts-ignore 254 return `${value.totalBytes - value.existing}`; 255 }); 256 this.nativeStatisticsTbl!.exportTextHandleMap.set('totalBytesString', (value) => { 257 // @ts-ignore 258 return `${value.totalBytes}`; 259 }); 260 this.nativeStatisticsTbl!.exportTextHandleMap.set('maxStr', (value) => { 261 // @ts-ignore 262 return `${value.max}`; 263 }); 264 } 265 266 connectedCallback(): void { 267 super.connectedCallback(); 268 resizeObserver(this.parentElement!, this.nativeStatisticsTbl!, 25); 269 } 270 271 sortByColumn(nmStatColumn: string, nmStatSort: number): void { 272 this.sortColumn = nmStatColumn; 273 this.sortType = nmStatSort; 274 if (nmStatSort === 0) { 275 this.nativeStatisticsTbl!.recycleDataSource = this.nativeStatisticsSource; 276 } else { 277 let arr = [...this.nativeStatisticsSource]; 278 let compareFunction = ( 279 nativeStatisticsLeftData: unknown, 280 nativeStatisticsRightData: unknown, 281 column: string, 282 sortType: number 283 ): number => { 284 if (sortType === 1) { 285 // @ts-ignore 286 return nativeStatisticsLeftData[column] - nativeStatisticsRightData[column]; 287 } else { 288 // @ts-ignore 289 return nativeStatisticsRightData[column] - nativeStatisticsLeftData[column]; 290 } 291 }; 292 293 let columnMap: { [key: string]: string } = { 294 existingString: 'existing', 295 allocCount: 'allocCount', 296 freeByteString: 'totalBytes', 297 freeCount: 'freeCount', 298 totalBytesString: 'totalBytes', 299 maxStr: 'max', 300 totalCount: 'totalCount', 301 }; 302 let sortColumnKey = columnMap[nmStatColumn]; 303 this.nativeStatisticsTbl!.recycleDataSource = arr.sort((leftData, rightData) => 304 compareFunction(leftData, rightData, sortColumnKey, nmStatSort) 305 ); 306 } 307 } 308 309 initHtml(): string { 310 return ` 311<style> 312.nm-stat-tbl { 313 height: auto 314} 315:host{ 316 display: flex; 317 flex-direction: column; 318 padding: 10px 10px; 319} 320</style> 321<lit-table id="tb-native-statstics" class="nm-stat-tbl"> 322 <lit-table-column class="nm-stat-column" width="25%" title="Memory Type" 323 data-index="memoryTap" key="memoryTap" align="flex-start"></lit-table-column> 324 <lit-table-column class="nm-stat-column" width="1fr" title="Existing" 325 data-index="existingString" key="existingString" align="flex-start" order></lit-table-column> 326 <lit-table-column class="nm-stat-column" width="1fr" title="# Existing" 327 data-index="allocCount" key="allocCount" align="flex-start" order></lit-table-column> 328 <lit-table-column class="nm-stat-column" width="1fr" title="Transient" 329 data-index="freeByteString" key="freeByteString" align="flex-start" order></lit-table-column> 330 <lit-table-column class="nm-stat-column" width="1fr" title="# Transient" 331 data-index="freeCount" key="freeCount" align="flex-start" order></lit-table-column> 332 <lit-table-column class="nm-stat-column" width="1fr" title="Total Bytes" 333 data-index="totalBytesString" key="totalBytesString" align="flex-start" order></lit-table-column> 334 <lit-table-column class="nm-stat-column" width="1fr" title="# Total" 335 data-index="totalCount" key="totalCount" align="flex-start" order></lit-table-column> 336 <lit-table-column class="nm-stat-column" width="1fr" title="Peak Value" 337 data-index="maxStr" key="maxStr" align="flex-start" order></lit-table-column> 338 <lit-table-column class="nm-stat-column" width="160px" title="Existing / Total" 339 data-index="existingValue" key="existingValue" align="flex-start" > 340 <template><tab-progress-bar data="{{existingValue}}"></tab-progress-bar></template> 341 </lit-table-column> 342</lit-table> 343 `; 344 } 345} 346