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 { type LitTable } from '../../../../../base-ui/table/lit-table'; 18import { type LitPageTable } from '../../../../../base-ui/table/LitPageTable'; 19import '../../../../../base-ui/table/LitPageTable'; 20import '../../../../../base-ui/slicer/lit-slicer'; 21import { type SelectionParam } from '../../../../bean/BoxSelection'; 22import { type NativeMemory, NativeHookCallInfo } from '../../../../bean/NativeHook'; 23import '../TabPaneFilter'; 24import { FilterData, TabPaneFilter } from '../TabPaneFilter'; 25import { TabPaneNMSampleList } from './TabPaneNMSampleList'; 26import { type LitProgressBar } from '../../../../../base-ui/progress-bar/LitProgressBar'; 27import { procedurePool } from '../../../../database/Procedure'; 28import { 29 formatRealDateMs, 30 getByteWithUnit, 31 getTimeString, 32} from '../../../../database/logic-worker/ProcedureLogicWorkerCommon'; 33import { SpNativeMemoryChart } from '../../../chart/SpNativeMemoryChart'; 34import { Utils } from '../../base/Utils'; 35import { TabPaneNMemoryHtml } from './TabPaneNMemory.html'; 36import { SpSystemTrace } from '../../../SpSystemTrace'; 37import { TabPaneFlag } from '../../timer-shaft/TabPaneFlag'; 38 39@element('tabpane-native-memory') 40export class TabPaneNMemory extends BaseElement { 41 private defaultNativeTypes = ['All Heap & Anonymous VM', 'All Heap', 'All Anonymous VM']; 42 private memoryTbl: LitPageTable | null | undefined; 43 private filter: TabPaneFilter | null | undefined; 44 private tblData: LitTable | null | undefined; 45 private progressEL: LitProgressBar | null | undefined; 46 private nmMemoryLoadingList: number[] = []; 47 private loadingPage: unknown; 48 private memorySource: Array<unknown> = []; 49 private nativeType: Array<string> = [...this.defaultNativeTypes]; 50 private statsticsSelection: Array<unknown> = []; 51 private filterAllocationType: string = '0'; 52 private filterNativeType: string = '0'; 53 private filterResponseType: number = -1; 54 private filterResponseSelect: string = '0'; 55 private currentSelection: SelectionParam | undefined; 56 private rowSelectData: unknown = undefined; 57 private sortColumn: string = ''; 58 private sortType: number = 0; 59 private responseTypes: unknown[] = []; 60 private eventTypes: string[] = []; 61 private systemTrace: SpSystemTrace | undefined | null; 62 63 set data(memoryParam: SelectionParam | unknown) { 64 if (memoryParam === this.currentSelection) { 65 return; 66 } //@ts-ignore 67 this.currentSelection = memoryParam; 68 this.queryData(memoryParam); 69 } 70 71 queryData(memoryParam: SelectionParam | unknown, resetFilter: boolean = true): void { 72 this.eventTypes = []; //@ts-ignore 73 if (memoryParam.nativeMemory.indexOf(this.defaultNativeTypes[0]) !== -1) { 74 this.eventTypes.push("'AllocEvent'"); 75 this.eventTypes.push("'MmapEvent'"); 76 } else { 77 //@ts-ignore 78 if (memoryParam.nativeMemory.indexOf(this.defaultNativeTypes[1]) !== -1) { 79 this.eventTypes.push("'AllocEvent'"); 80 } //@ts-ignore 81 if (memoryParam.nativeMemory.indexOf(this.defaultNativeTypes[2]) !== -1) { 82 this.eventTypes.push("'MmapEvent'"); 83 } 84 } //@ts-ignore 85 TabPaneNMSampleList.serSelection(memoryParam); 86 if (this.memoryTbl) { 87 // @ts-ignore 88 this.memoryTbl.shadowRoot.querySelector('.table').style.height = `${this.parentElement.clientHeight - 20 - 31}px`; 89 // @ts-ignore 90 this.tblData.shadowRoot.querySelector('.table').style.height = `${this.parentElement.clientHeight - 20 - 31}px`; 91 // @ts-ignore 92 this.tblData.recycleDataSource = []; 93 // @ts-ignore 94 this.memoryTbl.recycleDataSource = []; 95 } 96 if (resetFilter) { 97 this.resetFilter(); 98 this.setNmMemoryLoading(true); 99 this.initFilterTypes(() => { 100 this.filterSetSelectList(this.filter!, 0); 101 this.getDataByNativeMemoryWorker(memoryParam, resetFilter); 102 }); 103 } else { 104 this.getDataByNativeMemoryWorker(memoryParam, resetFilter); 105 } 106 } 107 108 getDataByNativeMemoryWorker(val: SelectionParam | unknown, refresh = false): void { 109 let args = new Map<string, unknown>(); 110 args.set('filterAllocType', this.filterAllocationType); 111 args.set('filterEventType', this.filterNativeType); 112 args.set('filterResponseType', this.filterResponseType); //@ts-ignore 113 args.set('leftNs', val.leftNs); //@ts-ignore 114 args.set('rightNs', val.rightNs); 115 args.set('types', this.eventTypes); 116 args.set('refresh', refresh); 117 let selections: Array<unknown> = []; 118 if (this.statsticsSelection.length > 0) { 119 this.statsticsSelection.map((memory) => { 120 selections.push({ 121 //@ts-ignore 122 memoryTap: memory.memoryTap, //@ts-ignore 123 max: memory.max, 124 }); 125 }); 126 } 127 args.set('statisticsSelection', selections); 128 args.set('sortColumn', this.sortColumn); 129 args.set('sortType', this.sortType); 130 this.memorySource = []; 131 if (this.memoryTbl!.recycleDs.length > 1_0000) { 132 this.memoryTbl!.recycleDataSource = []; 133 } 134 this.startNmMemoryWorker('native-memory-queryNativeHookEvent', args, (results: unknown[]) => { 135 this.tblData!.recycleDataSource = []; 136 this.setNmMemoryLoading(false); 137 if (results.length > 0) { 138 this.memorySource = results; 139 this.memoryTbl!.recycleDataSource = this.memorySource; 140 } else { 141 this.memorySource = []; 142 this.memoryTbl!.recycleDataSource = []; 143 } 144 }); 145 } 146 147 startNmMemoryWorker(type: string, args: unknown, handler: Function): void { 148 this.setNmMemoryLoading(true); 149 procedurePool.submitWithName('logic0', type, args, undefined, (res: unknown) => { 150 //@ts-ignore 151 if (Array.isArray(res) || (res.tag === 'end' && res.index === 0)) { 152 //@ts-ignore 153 handler(res.data ? res.data : res); 154 this.setNmMemoryLoading(false); 155 } else { 156 //@ts-ignore 157 this.memorySource.push(res.data); //@ts-ignore 158 if (res.tag === 'end') { 159 handler(this.memorySource); 160 this.setNmMemoryLoading(false); 161 } 162 } 163 }); 164 } 165 166 setNmMemoryLoading(loading: boolean): void { 167 if (loading) { 168 this.nmMemoryLoadingList.push(1); 169 this.progressEL!.loading = true; //@ts-ignore 170 this.loadingPage.style.visibility = 'visible'; 171 } else { 172 this.nmMemoryLoadingList.splice(0, 1); 173 if (this.nmMemoryLoadingList.length === 0) { 174 this.progressEL!.loading = false; //@ts-ignore 175 this.loadingPage.style.visibility = 'hidden'; 176 } 177 } 178 } 179 180 fromStastics(val: SelectionParam | unknown): void { 181 let nmFilterEl = this.shadowRoot?.querySelector<TabPaneFilter>('#filter'); 182 if (this.currentSelection !== val) { 183 this.resetFilter(); 184 this.initFilterTypes(() => { 185 //@ts-ignore 186 this.currentSelection = val; 187 let typeIndexOf = this.setFilterNativeTypeSelection(this.currentSelection!); 188 this.filterSetSelectList(nmFilterEl!, typeIndexOf); 189 this.filterNativeType = `${typeIndexOf}`; 190 this.rowSelectData = undefined; 191 this.queryData(val, false); 192 }); 193 } else { 194 //@ts-ignore 195 let typeIndexOf = this.setFilterNativeTypeSelection(val); 196 this.tblData!.recycleDataSource = []; 197 this.rowSelectData = undefined; 198 this.filterSetSelectList(nmFilterEl!, typeIndexOf); 199 this.filterNativeType = `${typeIndexOf}`; 200 //直接将当前数据过滤即可 201 this.getDataByNativeMemoryWorker(val); 202 } 203 } 204 205 private setFilterNativeTypeSelection(val: SelectionParam): number { 206 let typeIndexOf = -1; 207 if (val.statisticsSelectData) { 208 // @ts-ignore 209 typeIndexOf = this.nativeType.indexOf(val.statisticsSelectData.memoryTap); 210 if (this.statsticsSelection.indexOf(val.statisticsSelectData) === -1 && typeIndexOf === -1) { 211 this.statsticsSelection.push(val.statisticsSelectData); 212 // @ts-ignore 213 this.nativeType.push(val.statisticsSelectData.memoryTap); 214 typeIndexOf = this.nativeType.length - 1; 215 } else { 216 // @ts-ignore 217 let index = this.statsticsSelection.findIndex((mt) => mt.memoryTap === val.statisticsSelectData.memoryTap); 218 if (index !== -1) { 219 this.statsticsSelection[index] = val.statisticsSelectData; 220 } 221 } 222 } 223 return typeIndexOf; 224 } 225 226 private filterSetSelectList(nmFilterEl: TabPaneFilter, typeIndexOf: number): void { 227 nmFilterEl!.setSelectList( 228 null, 229 this.nativeType, 230 'Allocation Lifespan', 231 'Allocation Type', 232 this.responseTypes.map((item: unknown) => { 233 //@ts-ignore 234 return item.value; 235 }) 236 ); 237 nmFilterEl!.secondSelect = `${typeIndexOf}`; 238 nmFilterEl!.thirdSelect = this.filterResponseSelect; 239 } 240 241 initFilterTypes(initCallback?: () => void): void { 242 this.nativeType = [...this.defaultNativeTypes]; 243 this.statsticsSelection = []; 244 if (this.currentSelection) { 245 this.setFilterNativeTypeSelection(this.currentSelection); 246 } 247 procedurePool.submitWithName('logic0', 'native-memory-get-responseType', {}, undefined, (res: unknown) => { 248 this.filter!.setSelectList( 249 null, 250 this.nativeType, 251 'Allocation Lifespan', 252 'Allocation Type', //@ts-ignore 253 res.map((item: unknown) => { 254 //@ts-ignore 255 return item.value; 256 }) 257 ); 258 this.filter!.setFilterModuleSelect('#first-select', 'width', '150px'); 259 this.filter!.setFilterModuleSelect('#second-select', 'width', '150px'); 260 this.filter!.setFilterModuleSelect('#third-select', 'width', '150px'); //@ts-ignore 261 this.responseTypes = res; 262 this.rowSelectData = undefined; 263 if (initCallback) { 264 initCallback(); 265 } 266 }); 267 } 268 269 resetFilter(): void { 270 this.filter!.firstSelect = '0'; 271 this.filter!.secondSelect = '0'; 272 this.filter!.thirdSelect = '0'; 273 this.filterResponseSelect = '0'; 274 this.filterAllocationType = '0'; 275 this.filterNativeType = '0'; 276 this.filterResponseType = -1; 277 } 278 279 initElements(): void { 280 this.systemTrace = document 281 .querySelector('body > sp-application') 282 ?.shadowRoot!.querySelector<SpSystemTrace>('#sp-system-trace'); 283 this.loadingPage = this.shadowRoot?.querySelector('.loading'); 284 this.progressEL = this.shadowRoot?.querySelector('.progress') as LitProgressBar; 285 this.memoryTbl = this.shadowRoot?.querySelector<LitPageTable>('#tb-native-memory'); 286 this.tblData = this.shadowRoot?.querySelector<LitTable>('#tb-native-data'); 287 this.filter = this.shadowRoot?.querySelector<TabPaneFilter>('#filter'); 288 this.memoryTbl!.addEventListener('row-click', (e) => { 289 // @ts-ignore 290 let data = e.detail.data as NativeMemory; 291 data.isSelected = true; 292 this.rowSelectData = data; 293 this.setRightTableData(data); 294 // @ts-ignore 295 if ((e.detail as unknown).callBack) { 296 // @ts-ignore 297 (e.detail as unknown).callBack(true); 298 } 299 let flagList = this.systemTrace?.timerShaftEL!.sportRuler?.flagList || []; 300 flagList.forEach((it, i) => { 301 if (it.type === 'triangle') { 302 flagList.splice(i, 1); 303 } 304 }); 305 306 for (let i = 0; i < flagList!.length; i++) { 307 if (flagList[i].time === data.startTs) { 308 flagList[i].type = 'triangle'; 309 flagList[i].selected = true; 310 } else { 311 flagList[i].type = ''; 312 flagList[i].selected = false; 313 } 314 } 315 document.dispatchEvent( 316 new CustomEvent('triangle-flag', { 317 detail: { time: [data.startTs], type: 'triangle' }, 318 }) 319 ); 320 }); 321 this.memoryTbl!.addEventListener('column-click', (evt: unknown) => { 322 //@ts-ignore 323 this.sortColumn = evt.detail.key; //@ts-ignore 324 this.sortType = evt.detail.sort; 325 this.getDataByNativeMemoryWorker(this.currentSelection); 326 }); 327 this.setItemTextHandleMapByMemoryTbl(); 328 this.memoryTbl!.exportTextHandleMap.set('heapSize', (value) => { 329 // @ts-ignore 330 return `${value['heapSize']}`; 331 }); 332 this.shadowRoot?.querySelector<TabPaneFilter>('#filter')!.getFilterData((data: FilterData) => { 333 if (data.mark) { 334 this.getFilterDataByMark(); 335 } else { 336 this.filterAllocationType = data.firstSelect || '0'; 337 this.filterNativeType = data.secondSelect || '0'; 338 this.filterResponseSelect = data.thirdSelect || '0'; 339 let thirdIndex = parseInt(data.thirdSelect || '0'); 340 if (this.responseTypes.length > thirdIndex) { 341 this.filterResponseType = //@ts-ignore 342 this.responseTypes[thirdIndex].key === undefined ? -1 : this.responseTypes[thirdIndex].key; 343 } 344 this.getDataByNativeMemoryWorker(this.currentSelection); 345 } 346 }); 347 this.filter!.firstSelect = '1'; 348 } 349 350 private setItemTextHandleMapByMemoryTbl(): void { 351 this.memoryTbl!.itemTextHandleMap.set('startTs', (startTs) => { 352 return SpNativeMemoryChart.REAL_TIME_DIF === 0 // @ts-ignore 353 ? getTimeString(startTs) // @ts-ignore 354 : formatRealDateMs(startTs + SpNativeMemoryChart.REAL_TIME_DIF); 355 }); 356 this.memoryTbl!.itemTextHandleMap.set('endTs', (endTs) => { 357 // @ts-ignore 358 return endTs > this.currentSelection!.leftNs && // @ts-ignore 359 endTs <= this.currentSelection!.rightNs && 360 endTs !== 0 && 361 endTs !== null 362 ? 'Freed' 363 : 'Existing'; 364 }); 365 this.memoryTbl!.itemTextHandleMap.set('heapSize', (heapSize) => { 366 // @ts-ignore 367 return getByteWithUnit(heapSize); 368 }); 369 } 370 371 private getFilterDataByMark(): void { 372 document.dispatchEvent( 373 new CustomEvent('triangle-flag', { 374 detail: { 375 time: '', 376 type: 'square', 377 timeCallback: (timeArr: number[]): void => { 378 if (timeArr && timeArr.length > 0) { 379 let checkTs = timeArr[0]; 380 let minTs = 0; 381 let minItem: unknown = undefined; 382 let filterTemp = this.memorySource.filter((tempItem) => { 383 //@ts-ignore 384 if (minTs === 0 || (tempItem.startTs - checkTs !== 0 && Math.abs(tempItem.startTs - checkTs) < minTs)) { 385 //@ts-ignore 386 minTs = Math.abs(tempItem.startTs - checkTs); 387 minItem = tempItem; 388 } //@ts-ignore 389 return tempItem.startTs === checkTs; 390 }); 391 if (filterTemp.length > 0) { 392 //@ts-ignore 393 filterTemp[0].isSelected = true; 394 } else { 395 if (minItem) { 396 filterTemp.push(minItem); //@ts-ignore 397 minItem.isSelected = true; 398 } 399 } 400 if (filterTemp.length > 0) { 401 this.rowSelectData = filterTemp[0]; 402 let args = new Map<string, unknown>(); //@ts-ignore 403 args.set('startTs', this.rowSelectData.startTs); 404 args.set('actionType', 'native-memory-state-change'); 405 this.startNmMemoryWorker('native-memory-action', args, (results: unknown[]) => {}); 406 TabPaneNMSampleList.addSampleData(this.rowSelectData, this.currentSelection!.nativeMemoryCurrentIPid); 407 this.memoryTbl!.scrollToData(this.rowSelectData); 408 } 409 } 410 }, 411 }, 412 }) 413 ); 414 } 415 416 connectedCallback(): void { 417 super.connectedCallback(); 418 new ResizeObserver((entries) => { 419 if (this.parentElement?.clientHeight !== 0) { 420 if (this.memoryTbl) { 421 // @ts-ignore 422 this.memoryTbl.shadowRoot.querySelector('.table').style.height = `${ 423 this.parentElement!.clientHeight - 10 - 31 424 }px`; 425 } 426 this.memoryTbl?.reMeauseHeight(); 427 if (this.tblData) { 428 // @ts-ignore 429 this.tblData.shadowRoot.querySelector('.table').style.height = `${ 430 this.parentElement!.clientHeight - 10 - 31 431 }px`; 432 } 433 this.tblData?.reMeauseHeight(); //@ts-ignore 434 this.loadingPage.style.height = `${this.parentElement!.clientHeight - 24}px`; 435 } 436 }).observe(this.parentElement!); 437 } 438 439 setRightTableData(nativeMemoryHook: NativeMemory): void { 440 let args = new Map<string, unknown>(); 441 args.set('eventId', nativeMemoryHook.eventId); 442 args.set('actionType', 'memory-stack'); 443 this.startNmMemoryWorker('native-memory-action', args, (results: unknown[]) => { 444 let thread = new NativeHookCallInfo(); 445 thread.threadId = nativeMemoryHook.threadId; 446 thread.threadName = Utils.getInstance().getThreadMap().get(thread.threadId) || 'Thread'; 447 thread.symbol = `${nativeMemoryHook.threadName ?? ''}【${nativeMemoryHook.threadId}】`; 448 thread.type = -1; 449 let currentSource = []; 450 currentSource.push(thread); 451 currentSource.push(...results); 452 this.progressEL!.loading = false; 453 this.tblData!.dataSource = currentSource; 454 }); 455 } 456 457 initHtml(): string { 458 return TabPaneNMemoryHtml; 459 } 460} 461