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 let isTwoArray: boolean = results.some(item => { 139 return Array.isArray(item); 140 }); 141 let dataList: unknown = []; 142 if (isTwoArray) { 143 results.forEach(v => { 144 // @ts-ignore 145 dataList = dataList.concat(v); 146 }); 147 } else { 148 dataList = results; 149 } 150 // @ts-ignore 151 dataList.forEach((item) => { 152 //@ts-ignore 153 let tmpNumber = item.addr.split('x'); 154 //@ts-ignore 155 item.addr = '0x' + Number(tmpNumber[1]).toString(16); 156 }); 157 this.memorySource = results; 158 this.memoryTbl!.recycleDataSource = this.memorySource; 159 } else { 160 this.memorySource = []; 161 this.memoryTbl!.recycleDataSource = []; 162 } 163 }); 164 } 165 166 startNmMemoryWorker(type: string, args: unknown, handler: Function): void { 167 this.setNmMemoryLoading(true); 168 procedurePool.submitWithName('logic0', type, args, undefined, (res: unknown) => { 169 //@ts-ignore 170 if (Array.isArray(res) || (res.tag === 'end' && res.index === 0)) { 171 //@ts-ignore 172 handler(res.data ? res.data : res); 173 this.setNmMemoryLoading(false); 174 } else { 175 //@ts-ignore 176 this.memorySource.push(res.data); //@ts-ignore 177 if (res.tag === 'end') { 178 handler(this.memorySource); 179 this.setNmMemoryLoading(false); 180 } 181 } 182 }); 183 } 184 185 setNmMemoryLoading(loading: boolean): void { 186 if (loading) { 187 this.nmMemoryLoadingList.push(1); 188 this.progressEL!.loading = true; //@ts-ignore 189 this.loadingPage.style.visibility = 'visible'; 190 } else { 191 this.nmMemoryLoadingList.splice(0, 1); 192 if (this.nmMemoryLoadingList.length === 0) { 193 this.progressEL!.loading = false; //@ts-ignore 194 this.loadingPage.style.visibility = 'hidden'; 195 } 196 } 197 } 198 199 fromStastics(val: SelectionParam | unknown): void { 200 let nmFilterEl = this.shadowRoot?.querySelector<TabPaneFilter>('#filter'); 201 if (this.currentSelection !== val) { 202 this.resetFilter(); 203 this.initFilterTypes(() => { 204 //@ts-ignore 205 this.currentSelection = val; 206 let typeIndexOf = this.setFilterNativeTypeSelection(this.currentSelection!); 207 this.filterSetSelectList(nmFilterEl!, typeIndexOf); 208 this.filterNativeType = `${typeIndexOf}`; 209 this.rowSelectData = undefined; 210 this.queryData(val, true); 211 this.fromStastics(val); 212 }); 213 } else { 214 //@ts-ignore 215 let typeIndexOf = this.setFilterNativeTypeSelection(val); 216 this.tblData!.recycleDataSource = []; 217 this.rowSelectData = undefined; 218 this.filterSetSelectList(nmFilterEl!, typeIndexOf); 219 this.filterNativeType = `${typeIndexOf}`; 220 //直接将当前数据过滤即可 221 this.getDataByNativeMemoryWorker(val); 222 } 223 } 224 225 private setFilterNativeTypeSelection(val: SelectionParam): number { 226 let typeIndexOf = -1; 227 if (val.statisticsSelectData) { 228 // @ts-ignore 229 typeIndexOf = this.nativeType.indexOf(val.statisticsSelectData.memoryTap); 230 if (this.statsticsSelection.indexOf(val.statisticsSelectData) === -1 && typeIndexOf === -1) { 231 this.statsticsSelection.push(val.statisticsSelectData); 232 // @ts-ignore 233 this.nativeType.push(val.statisticsSelectData.memoryTap); 234 typeIndexOf = this.nativeType.length - 1; 235 } else { 236 // @ts-ignore 237 let index = this.statsticsSelection.findIndex((mt) => mt.memoryTap === val.statisticsSelectData.memoryTap); 238 if (index !== -1) { 239 this.statsticsSelection[index] = val.statisticsSelectData; 240 } 241 } 242 } 243 return typeIndexOf; 244 } 245 246 private filterSetSelectList(nmFilterEl: TabPaneFilter, typeIndexOf: number): void { 247 nmFilterEl!.setSelectList( 248 null, 249 this.nativeType, 250 'Allocation Lifespan', 251 'Allocation Type', 252 this.responseTypes.map((item: unknown) => { 253 //@ts-ignore 254 return item.value; 255 }) 256 ); 257 nmFilterEl!.secondSelect = `${typeIndexOf}`; 258 nmFilterEl!.thirdSelect = this.filterResponseSelect; 259 } 260 261 initFilterTypes(initCallback?: () => void): void { 262 this.nativeType = [...this.defaultNativeTypes]; 263 this.statsticsSelection = []; 264 if (this.currentSelection) { 265 this.setFilterNativeTypeSelection(this.currentSelection); 266 } 267 procedurePool.submitWithName('logic0', 'native-memory-get-responseType', {}, undefined, (res: unknown) => { 268 this.filter!.setSelectList( 269 null, 270 this.nativeType, 271 'Allocation Lifespan', 272 'Allocation Type', //@ts-ignore 273 res.map((item: unknown) => { 274 //@ts-ignore 275 return item.value; 276 }) 277 ); 278 this.filter!.setFilterModuleSelect('#first-select', 'width', '150px'); 279 this.filter!.setFilterModuleSelect('#second-select', 'width', '150px'); 280 this.filter!.setFilterModuleSelect('#third-select', 'width', '150px'); //@ts-ignore 281 this.responseTypes = res; 282 this.rowSelectData = undefined; 283 if (initCallback) { 284 initCallback(); 285 } 286 }); 287 } 288 289 resetFilter(): void { 290 this.filter!.firstSelect = '0'; 291 this.filter!.secondSelect = '0'; 292 this.filter!.thirdSelect = '0'; 293 this.filterResponseSelect = '0'; 294 this.filterAllocationType = '0'; 295 this.filterNativeType = '0'; 296 this.filterResponseType = -1; 297 } 298 299 initElements(): void { 300 this.systemTrace = document 301 .querySelector('body > sp-application') 302 ?.shadowRoot!.querySelector<SpSystemTrace>('#sp-system-trace'); 303 this.loadingPage = this.shadowRoot?.querySelector('.loading'); 304 this.progressEL = this.shadowRoot?.querySelector('.progress') as LitProgressBar; 305 this.memoryTbl = this.shadowRoot?.querySelector<LitPageTable>('#tb-native-memory'); 306 this.tblData = this.shadowRoot?.querySelector<LitTable>('#tb-native-data'); 307 this.filter = this.shadowRoot?.querySelector<TabPaneFilter>('#filter'); 308 this.memoryTbl!.addEventListener('row-click', (e) => { 309 // @ts-ignore 310 let data = e.detail.data as NativeMemory; 311 data.isSelected = true; 312 this.rowSelectData = data; 313 this.setRightTableData(data); 314 // @ts-ignore 315 if ((e.detail as unknown).callBack) { 316 // @ts-ignore 317 (e.detail as unknown).callBack(true); 318 } 319 let flagList = this.systemTrace?.timerShaftEL!.sportRuler?.flagList || []; 320 flagList.forEach((it, i) => { 321 if (it.type === 'triangle') { 322 flagList.splice(i, 1); 323 } 324 }); 325 326 for (let i = 0; i < flagList!.length; i++) { 327 if (flagList[i].time === data.startTs) { 328 flagList[i].type = 'triangle'; 329 flagList[i].selected = true; 330 } else { 331 flagList[i].type = ''; 332 flagList[i].selected = false; 333 } 334 } 335 document.dispatchEvent( 336 new CustomEvent('triangle-flag', { 337 detail: { time: [data.startTs], type: 'triangle' }, 338 }) 339 ); 340 }); 341 this.memoryTbl!.addEventListener('column-click', (evt: unknown) => { 342 //@ts-ignore 343 this.sortColumn = evt.detail.key; //@ts-ignore 344 this.sortType = evt.detail.sort; 345 this.getDataByNativeMemoryWorker(this.currentSelection); 346 }); 347 this.setItemTextHandleMapByMemoryTbl(); 348 this.memoryTbl!.exportTextHandleMap.set('heapSize', (value) => { 349 // @ts-ignore 350 return `${value['heapSize']}`; 351 }); 352 this.shadowRoot?.querySelector<TabPaneFilter>('#filter')!.getFilterData((data: FilterData) => { 353 if (data.mark) { 354 this.getFilterDataByMark(); 355 } else { 356 this.filterAllocationType = data.firstSelect || '0'; 357 this.filterNativeType = data.secondSelect || '0'; 358 this.filterResponseSelect = data.thirdSelect || '0'; 359 let thirdIndex = parseInt(data.thirdSelect || '0'); 360 if (this.responseTypes.length > thirdIndex) { 361 this.filterResponseType = //@ts-ignore 362 this.responseTypes[thirdIndex].key === undefined ? -1 : this.responseTypes[thirdIndex].key; 363 } 364 this.getDataByNativeMemoryWorker(this.currentSelection); 365 } 366 }); 367 this.filter!.firstSelect = '1'; 368 } 369 370 private setItemTextHandleMapByMemoryTbl(): void { 371 this.memoryTbl!.itemTextHandleMap.set('startTs', (startTs) => { 372 return SpNativeMemoryChart.REAL_TIME_DIF === 0 // @ts-ignore 373 ? getTimeString(startTs) // @ts-ignore 374 : formatRealDateMs(startTs + SpNativeMemoryChart.REAL_TIME_DIF); 375 }); 376 this.memoryTbl!.itemTextHandleMap.set('endTs', (endTs) => { 377 // @ts-ignore 378 return endTs > this.currentSelection!.leftNs && // @ts-ignore 379 endTs <= this.currentSelection!.rightNs && 380 endTs !== 0 && 381 endTs !== null 382 ? 'Freed' 383 : 'Existing'; 384 }); 385 this.memoryTbl!.itemTextHandleMap.set('heapSize', (heapSize) => { 386 // @ts-ignore 387 return getByteWithUnit(heapSize); 388 }); 389 } 390 391 private getFilterDataByMark(): void { 392 document.dispatchEvent( 393 new CustomEvent('triangle-flag', { 394 detail: { 395 time: '', 396 type: 'square', 397 timeCallback: (timeArr: number[]): void => { 398 if (timeArr && timeArr.length > 0) { 399 let checkTs = timeArr[0]; 400 let minTs = 0; 401 let minItem: unknown = undefined; 402 let filterTemp = this.memorySource.filter((tempItem) => { 403 //@ts-ignore 404 if (minTs === 0 || (tempItem.startTs - checkTs !== 0 && Math.abs(tempItem.startTs - checkTs) < minTs)) { 405 //@ts-ignore 406 minTs = Math.abs(tempItem.startTs - checkTs); 407 minItem = tempItem; 408 } //@ts-ignore 409 return tempItem.startTs === checkTs; 410 }); 411 if (filterTemp.length > 0) { 412 //@ts-ignore 413 filterTemp[0].isSelected = true; 414 } else { 415 if (minItem) { 416 filterTemp.push(minItem); //@ts-ignore 417 minItem.isSelected = true; 418 } 419 } 420 if (filterTemp.length > 0) { 421 this.rowSelectData = filterTemp[0]; 422 let args = new Map<string, unknown>(); //@ts-ignore 423 args.set('startTs', this.rowSelectData.startTs); 424 args.set('actionType', 'native-memory-state-change'); 425 this.startNmMemoryWorker('native-memory-action', args, (results: unknown[]) => { }); 426 TabPaneNMSampleList.addSampleData(this.rowSelectData, this.currentSelection!.nativeMemoryCurrentIPid); 427 this.memoryTbl!.scrollToData(this.rowSelectData); 428 } 429 } 430 }, 431 }, 432 }) 433 ); 434 } 435 436 connectedCallback(): void { 437 super.connectedCallback(); 438 new ResizeObserver((entries) => { 439 if (this.parentElement?.clientHeight !== 0) { 440 if (this.memoryTbl) { 441 // @ts-ignore 442 this.memoryTbl.shadowRoot.querySelector('.table').style.height = `${this.parentElement!.clientHeight - 10 - 31 443 }px`; 444 } 445 this.memoryTbl?.reMeauseHeight(); 446 if (this.tblData) { 447 // @ts-ignore 448 this.tblData.shadowRoot.querySelector('.table').style.height = `${this.parentElement!.clientHeight - 10 - 31 449 }px`; 450 } 451 this.tblData?.reMeauseHeight(); //@ts-ignore 452 this.loadingPage.style.height = `${this.parentElement!.clientHeight - 24}px`; 453 } 454 }).observe(this.parentElement!); 455 } 456 457 setRightTableData(nativeMemoryHook: NativeMemory): void { 458 let args = new Map<string, unknown>(); 459 args.set('eventId', nativeMemoryHook.eventId); 460 args.set('actionType', 'memory-stack'); 461 this.startNmMemoryWorker('native-memory-action', args, (results: unknown[]) => { 462 let thread = new NativeHookCallInfo(); 463 thread.threadId = nativeMemoryHook.threadId; 464 thread.threadName = Utils.getInstance().getThreadMap().get(thread.threadId) || 'Thread'; 465 thread.symbol = `${nativeMemoryHook.threadName ?? ''}【${nativeMemoryHook.threadId}】`; 466 thread.type = -1; 467 let currentSource = []; 468 currentSource.push(thread); 469 currentSource.push(...results); 470 this.progressEL!.loading = false; 471 this.tblData!.dataSource = currentSource; 472 }); 473 } 474 475 initHtml(): string { 476 return TabPaneNMemoryHtml; 477 } 478} 479