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 '../../../../../base-ui/table/lit-table-column'; 17import { BaseElement, element } from '../../../../../base-ui/BaseElement'; 18import { LitTable } from '../../../../../base-ui/table/lit-table'; 19import { SelectionParam } from '../../../../bean/BoxSelection'; 20import { 21 NativeHookCallInfo, 22 NativeHookSampleQueryInfo, 23 NativeHookSamplerInfo, 24 NativeMemory, 25} from '../../../../bean/NativeHook'; 26import { Utils } from '../../base/Utils'; 27import '../TabPaneFilter'; 28import { FilterData, TabPaneFilter } from '../TabPaneFilter'; 29import '../../../../../base-ui/slicer/lit-slicer'; 30import { procedurePool } from '../../../../database/Procedure'; 31import { 32 formatRealDateMs, 33 getTimeString 34} from '../../../../database/logic-worker/ProcedureLogicWorkerCommon'; 35import { SpNativeMemoryChart } from '../../../chart/SpNativeMemoryChart'; 36import { 37 queryAllHookData, 38 queryNativeHookSnapshotTypes 39} from '../../../../database/sql/NativeHook.sql'; 40 41@element('tabpane-native-sample') 42export class TabPaneNMSampleList extends BaseElement { 43 static tblData: LitTable | null | undefined; 44 static sampleTbl: LitTable | null | undefined; 45 static filter: any; 46 static filterSelect: string = '0'; 47 static samplerInfoSource: Array<NativeHookSamplerInfo> = []; 48 static types: Array<string> = []; 49 static nativeType: Array<string> = ['All Heap & Anonymous VM', 'All Heap', 'All Anonymous VM']; 50 static tableMarkData: Array<NativeMemory> = []; 51 static selectionParam: SelectionParam | undefined = undefined; 52 static sampleTypes: Array<NativeHookSampleQueryInfo> = []; 53 static sampleTypesList: any[] = []; 54 private currentSelection: SelectionParam | undefined; 55 56 set data(sampleParam: SelectionParam | any) { 57 if (sampleParam === this.currentSelection) { 58 return; 59 } 60 this.currentSelection = sampleParam; 61 TabPaneNMSampleList.serSelection(sampleParam); 62 this.filterAllList(); 63 } 64 65 static serSelection(sampleParam: SelectionParam): void { 66 if (this.selectionParam !== sampleParam) { 67 this.clearData(); 68 this.selectionParam = sampleParam; 69 this.initTypes(sampleParam.nativeMemoryCurrentIPid); 70 } 71 if (sampleParam.nativeMemory.indexOf(this.nativeType[0]) !== -1) { 72 this.types.push('\'AllocEvent\''); 73 this.types.push('\'MmapEvent\''); 74 } else { 75 if (sampleParam.nativeMemory.indexOf(this.nativeType[1]) !== -1) { 76 this.types.push('\'AllocEvent\''); 77 } 78 if (sampleParam.nativeMemory.indexOf(this.nativeType[2]) !== -1) { 79 this.types.push('\'MmapEvent\''); 80 } 81 } 82 } 83 84 static initTypes(ipid: number): void { 85 queryNativeHookSnapshotTypes(ipid).then((result) => { 86 if (result.length > 0) { 87 this.sampleTypes = result; 88 } 89 }); 90 } 91 92 static addSampleData(data: any, ipid: number): void { 93 if (TabPaneNMSampleList.tableMarkData.indexOf(data) !== -1) { 94 return; 95 } 96 TabPaneNMSampleList.tableMarkData.push(data); 97 let rootSample = new NativeHookSamplerInfo(); 98 rootSample.snapshot = `Snapshot${this.numberToWord(this.samplerInfoSource.length + 1)}`; 99 rootSample.startTs = data.startTs; 100 rootSample.timestamp = 101 SpNativeMemoryChart.REAL_TIME_DIF === 0 ? getTimeString(data.startTs) : 102 formatRealDateMs(data.startTs + SpNativeMemoryChart.REAL_TIME_DIF); 103 rootSample.eventId = data.eventId; 104 rootSample.threadId = data.threadId; 105 rootSample.threadName = data.threadName; 106 this.queryAllHookInfo(data, rootSample, ipid); 107 } 108 109 static merageSampleData( 110 leftTime: number, 111 startNs: number, 112 rootSample: NativeHookSampleQueryInfo, 113 merageSample: NativeHookSampleQueryInfo 114 ): void { 115 if (merageSample.endTs >= startNs) { 116 rootSample.growth += merageSample.growth; 117 } 118 if (merageSample.startTs > leftTime) { 119 rootSample.existing++; 120 let childSample = new NativeHookSamplerInfo(); //新增最下层的叶子节点 121 childSample.snapshot = `0x${merageSample.addr}`; 122 childSample.eventId = merageSample.eventId; 123 childSample.heapSize = merageSample.growth; 124 childSample.growth = Utils.getByteWithUnit(merageSample.growth); 125 childSample.totalGrowth = childSample.growth; 126 childSample.startTs = merageSample.startTs; 127 childSample.timestamp = 128 SpNativeMemoryChart.REAL_TIME_DIF === 0 129 ? getTimeString(merageSample.startTs) 130 : formatRealDateMs(merageSample.startTs + SpNativeMemoryChart.REAL_TIME_DIF); 131 childSample.threadId = merageSample.threadId; 132 childSample.threadName = merageSample.threadName; 133 (childSample as any).existing = ''; 134 rootSample.children.push(childSample); 135 } 136 rootSample.total += merageSample.growth; 137 } 138 139 static queryAllHookInfo(data: any, rootSample: NativeHookSamplerInfo, ipid: number): void { 140 let copyTypes = this.sampleTypes.map((type) => { 141 let copyType = new NativeHookSampleQueryInfo(); 142 copyType.eventType = type.eventType; 143 copyType.subType = type.subType; 144 return copyType; 145 }); 146 queryAllHookData(data.startTs, ipid).then((nmSamplerHookResult) => { 147 if (nmSamplerHookResult.length > 0) { 148 let nameGroup: any = {}; 149 copyTypes.forEach((item) => { 150 nameGroup[item.eventType] = nameGroup[item.eventType] || []; 151 nameGroup[item.eventType].push(item); 152 }); 153 let leftTime = 154 TabPaneNMSampleList.tableMarkData.length === 1 ? 0 : 155 TabPaneNMSampleList.tableMarkData[TabPaneNMSampleList.tableMarkData.length - 2].startTs; 156 nmSamplerHookResult.forEach((item) => { 157 item.threadId = rootSample.threadId; 158 item.threadName = rootSample.threadName; 159 if (nameGroup[item.eventType] !== undefined) { 160 if (item.subType === null) { 161 this.merageSampleData(leftTime, data.startTs, nameGroup[item.eventType][0], item); 162 } else { 163 let filter = nameGroup[item.eventType].filter((type: any) => { 164 return type.subType === item.subType; 165 }); 166 if (filter.length > 0) { 167 this.merageSampleData(leftTime, data.startTs, filter[0], item); 168 } 169 } 170 } 171 }); 172 this.updateSampleTypesList(copyTypes); 173 this.createTree(nameGroup, rootSample); 174 rootSample.tempList = [...rootSample.children]; 175 this.samplerInfoSource.push(rootSample); 176 TabPaneNMSampleList.sampleTbl!.recycleDataSource = TabPaneNMSampleList.samplerInfoSource; 177 } 178 }); 179 } 180 181 private static updateSampleTypesList(copyTypes: NativeHookSampleQueryInfo[]): void { 182 if (this.sampleTypesList.length > 0) { 183 let sampleTypesListElement = this.sampleTypesList[this.sampleTypesList.length - 1]; 184 sampleTypesListElement.forEach((item: any, index: number) => { 185 copyTypes[index].current = copyTypes[index].growth; 186 if (index < copyTypes.length) { 187 copyTypes[index].growth -= item.current; 188 copyTypes[index].total -= item.total; 189 } 190 }); 191 } else { 192 copyTypes.forEach((item: any) => { 193 item.current = item.growth; 194 }); 195 } 196 this.sampleTypesList.push(copyTypes); 197 } 198 199 static createTree(nameGroup: any, rootSample: NativeHookSamplerInfo): void { 200 Object.keys(nameGroup).forEach((key) => { 201 let parentSample = new NativeHookSamplerInfo(); 202 parentSample.snapshot = key; 203 if (nameGroup[key].length > 0) { 204 nameGroup[key].forEach((child: any) => { 205 let childSample = new NativeHookSamplerInfo(); 206 childSample.snapshot = child.subType || child.eventType; 207 childSample.heapSize = child.growth; 208 childSample.growth = Utils.getByteWithUnit(child.growth); 209 childSample.total = child.total; 210 childSample.totalGrowth = Utils.getByteWithUnit(child.total); 211 childSample.existing = child.existing; 212 childSample.currentSize = child.current; 213 childSample.current = Utils.getByteWithUnit(child.current); 214 childSample.threadName = rootSample.threadName; 215 childSample.threadId = rootSample.threadId; 216 parentSample.merageObj(childSample); 217 if (childSample.snapshot !== parentSample.snapshot) { 218 //根据名称是否一致来判断是否需要添加子节点 219 childSample.children.push(...child.children); 220 parentSample.children.push(childSample); 221 } else { 222 parentSample.children.push(...child.children); 223 } 224 }); 225 } 226 rootSample.merageObj(parentSample); 227 rootSample.children.push(parentSample); 228 }); 229 } 230 231 static prepChild(currentSample: NativeHookSamplerInfo, rootSample: NativeHookSamplerInfo) { 232 currentSample.heapSize -= rootSample.heapSize; 233 currentSample.growth = Utils.getByteWithUnit(currentSample.heapSize); 234 let currentMap: any = {}; 235 currentSample.children.forEach((currentChild) => { 236 currentMap[currentChild.snapshot] = currentChild; 237 }); 238 rootSample.children.forEach((rootChild) => { 239 if (currentMap[rootChild.snapshot] === undefined) { 240 let perpSample = new NativeHookSamplerInfo(); 241 perpSample.snapshot = rootChild.snapshot; 242 currentMap[rootChild.snapshot] = perpSample; 243 currentSample.children.push(perpSample); 244 } 245 this.prepChild(currentMap[rootChild.snapshot], rootChild); 246 }); 247 } 248 249 static clearData() { 250 this.types = []; 251 this.samplerInfoSource = []; 252 this.tblData!.dataSource = []; 253 this.sampleTbl!.recycleDataSource = []; 254 TabPaneNMSampleList.sampleTbl!.recycleDataSource = []; 255 this.sampleTypesList = []; 256 this.tableMarkData = []; 257 TabPaneNMSampleList.filter!.firstSelect = '0'; 258 } 259 260 static numberToWord(num: number) { 261 let word = ''; 262 while (num > 0) { 263 let end = num % 26; 264 end = end === 0 ? (end = 26) : end; 265 word = String.fromCharCode(96 + end) + word; 266 num = (num - end) / 26; 267 } 268 return word.toUpperCase(); 269 } 270 271 startWorker(args: Map<string, any>, handler: Function) { 272 procedurePool.submitWithName('logic0', 'native-memory-action', args, undefined, (res: any) => { 273 handler(res); 274 }); 275 } 276 277 setRightTableData(hookSamplerInfo: NativeHookSamplerInfo) { 278 let nmSamplerArgs = new Map<string, any>(); 279 nmSamplerArgs.set('eventId', hookSamplerInfo.eventId); 280 nmSamplerArgs.set('actionType', 'memory-stack'); 281 this.startWorker(nmSamplerArgs, (results: any[]) => { 282 let source = []; 283 if (results.length > 0) { 284 let hookCallInfo = new NativeHookCallInfo(); 285 hookCallInfo.threadId = hookSamplerInfo.threadId; 286 hookCallInfo.threadName = hookSamplerInfo.threadName; 287 hookCallInfo.title = `${hookSamplerInfo.threadName ?? ''}【${hookSamplerInfo.threadId}】`; 288 hookCallInfo.type = -1; 289 source.push(hookCallInfo); 290 source.push(...results); 291 } 292 // @ts-ignore 293 TabPaneNMSampleList.tblData?.dataSource = source; 294 }); 295 } 296 297 initElements(): void { 298 TabPaneNMSampleList.sampleTbl = this.shadowRoot?.querySelector<LitTable>('#tb-native-sample'); 299 TabPaneNMSampleList.sampleTbl!.addEventListener('row-click', (evt: any) => { 300 // @ts-ignore 301 this.setRightTableData(evt.detail.data); 302 }); 303 TabPaneNMSampleList.tblData = this.shadowRoot?.querySelector<LitTable>('#tb-native-data'); 304 TabPaneNMSampleList.filter = this.shadowRoot?.querySelector<TabPaneFilter>('#filter'); 305 this.shadowRoot?.querySelector<TabPaneFilter>('#filter')!.setSelectList(TabPaneNMSampleList.nativeType, null); 306 this.shadowRoot?.querySelector<TabPaneFilter>('#filter')!.getFilterData((data: FilterData) => { 307 if (data.firstSelect) { 308 TabPaneNMSampleList.filterSelect = data.firstSelect; 309 this.filterAllList(); 310 } 311 }); 312 TabPaneNMSampleList.filter!.firstSelect = TabPaneNMSampleList.filterSelect; 313 TabPaneNMSampleList.sampleTbl!.exportTextHandleMap.set('totalGrowth', (value) => { 314 return `${value['total']}`; 315 }); 316 TabPaneNMSampleList.sampleTbl!.exportTextHandleMap.set('growth', (value) => { 317 return `${value['heapSize']}`; 318 }); 319 } 320 321 connectedCallback() { 322 super.connectedCallback(); 323 new ResizeObserver((entries) => { 324 if (this.parentElement?.clientHeight !== 0) { 325 // @ts-ignore 326 TabPaneNMSampleList.sampleTbl?.shadowRoot.querySelector('.table').style.height = 327 this.parentElement!.clientHeight - 10 - 31 + 'px'; 328 TabPaneNMSampleList.sampleTbl?.reMeauseHeight(); 329 // @ts-ignore 330 TabPaneNMSampleList.tblData?.shadowRoot.querySelector('.table').style.height = 331 this.parentElement!.clientHeight - 10 + 'px'; 332 TabPaneNMSampleList.tblData?.reMeauseHeight(); 333 } 334 }).observe(this.parentElement!); 335 } 336 337 filterAllList() { 338 TabPaneNMSampleList.samplerInfoSource.forEach((nmRootSample) => { 339 nmRootSample.heapSize = 0; 340 nmRootSample.existing = 0; 341 nmRootSample.total = 0; 342 if (TabPaneNMSampleList.filterSelect === '0') { 343 nmRootSample.children = [...nmRootSample.tempList]; 344 nmRootSample.tempList.forEach((parentSample) => { 345 nmRootSample.heapSize += parentSample.heapSize; 346 nmRootSample.existing += parentSample.existing; 347 nmRootSample.total += parentSample.total; 348 }); 349 nmRootSample.growth = Utils.getByteWithUnit(nmRootSample.heapSize); 350 nmRootSample.totalGrowth = Utils.getByteWithUnit(nmRootSample.total); 351 } else if (TabPaneNMSampleList.filterSelect === '2') { 352 if (nmRootSample.tempList.length > 1) { 353 nmRootSample.children = [nmRootSample.tempList[1]]; 354 nmRootSample.heapSize += nmRootSample.tempList[1].heapSize; 355 nmRootSample.existing += nmRootSample.tempList[1].existing; 356 nmRootSample.growth = Utils.getByteWithUnit(nmRootSample.heapSize); 357 nmRootSample.total += nmRootSample.tempList[1].total; 358 nmRootSample.totalGrowth = Utils.getByteWithUnit(nmRootSample.total); 359 } else { 360 nmRootSample.children = []; 361 nmRootSample.growth = ''; 362 nmRootSample.totalGrowth = ''; 363 } 364 } else { 365 if (nmRootSample.tempList.length > 0) { 366 nmRootSample.children = [nmRootSample.tempList[0]]; 367 nmRootSample.heapSize += nmRootSample.tempList[0].heapSize; 368 nmRootSample.existing += nmRootSample.tempList[0].existing; 369 nmRootSample.growth = Utils.getByteWithUnit(nmRootSample.heapSize); 370 nmRootSample.total += nmRootSample.tempList[0].total; 371 nmRootSample.totalGrowth = Utils.getByteWithUnit(nmRootSample.total); 372 } else { 373 nmRootSample.children = []; 374 nmRootSample.growth = ''; 375 nmRootSample.totalGrowth = ''; 376 } 377 } 378 }); 379 TabPaneNMSampleList.sampleTbl!.recycleDataSource = TabPaneNMSampleList.samplerInfoSource; 380 } 381 382 initHtml(): string { 383 return ` 384 <style> 385 .nm-sample-tbl { 386 height: auto; 387 } 388 :host{ 389 padding: 10px 10px 0 10px; 390 display: flex; 391 flex-direction: column; 392 } 393 </style> 394 <lit-slicer style="width:100%"> 395 <div class="nm-sample-content" style="width: 65%"> 396 <lit-table id="tb-native-sample" class="nm-sample-tbl" tree> 397 <lit-table-column class="nm-sample-column" width="25%" title="Snapshot" data-index="snapshot" key="snapshot" align="flex-start"retract> 398 </lit-table-column> 399 <lit-table-column class="nm-sample-column" width="1fr" title="Timestamp" data-index="timestamp" key="timestamp" align="flex-start" > 400 </lit-table-column> 401 <lit-table-column class="nm-sample-column" width="1fr" title="Net Growth" data-index="growth" key="growth" align="flex-start" > 402 </lit-table-column> 403 <lit-table-column class="nm-sample-column" width="1fr" title="Total Growth" data-index="totalGrowth" key="totalGrowth" align="flex-start" > 404 </lit-table-column> 405 <lit-table-column class="nm-sample-column" width="1fr" title="# Existing" data-index="existing" key="existing" align="flex-start" > 406 </lit-table-column> 407 </lit-table> 408 <tab-pane-filter id="filter" first></tab-pane-filter> 409 </div> 410 <lit-slicer-track ></lit-slicer-track> 411 <lit-table id="tb-native-data" no-head style="height: auto;border-left: 1px solid var(--dark-border1,#e2e2e2)" hideDownload> 412 <lit-table-column class="nm-sample-column" width="80px" title="" data-index="type" key="type" align="flex-start" > 413 <template> 414 <div v-if=" type === -1 ">Thread:</div> 415 <img src="img/library.png" size="20" v-if=" type === 1 "> 416 <img src="img/function.png" size="20" v-if=" type === 0 "> 417 </template> 418 </lit-table-column> 419 <lit-table-column class="nm-sample-column" width="1fr" title="" data-index="title" key="title" align="flex-start"> 420 </lit-table-column> 421 </lit-table> 422 </lit-slicer> 423 `; 424 } 425} 426