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