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.js"; 17import {LitTable} from "../../../../base-ui/table/lit-table.js"; 18import {SelectionParam} from "../../../bean/BoxSelection.js"; 19import {query, queryNativeHookEventTid} from "../../../database/SqlLite.js"; 20import {NativeHookCallInfo, NativeHookStatistics} from "../../../bean/NativeHook.js"; 21import {SpSystemTrace} from "../../SpSystemTrace.js"; 22import {Utils} from "../base/Utils.js"; 23import "./TabPaneFilter.js" 24import {FilterData, TabPaneFilter} from "./TabPaneFilter"; 25import "../../FrameChart.js"; 26import "../../../../base-ui/slicer/lit-slicer.js"; 27import {FrameChart} from "../../FrameChart.js"; 28import {ChartMode} from "../../../database/ProcedureWorkerCommon.js"; 29import {LitProgressBar} from "../../../../base-ui/progress-bar/LitProgressBar.js"; 30 31 32@element('tabpane-native-callinfo') 33export class TabPaneNMCallInfo extends BaseElement { 34 private tbl: LitTable | null | undefined; 35 private tblData: LitTable | null | undefined; 36 private progressEL:LitProgressBar | null | undefined; 37 private loadingList:number[] = [] 38 private loadingPage:any; 39 private source: Array<NativeHookCallInfo> = [] 40 private rightSource: Array<NativeHookCallInfo> = [] 41 private queryResult: Array<NativeHookStatistics> = [] 42 private native_type: Array<string> = ["All Heap & Anonymous VM", "All Heap", "All Anonymous VM"]; 43 private filterAllocationType: string = "0" 44 private filterNativeType: string = "0" 45 private currentSelection: SelectionParam | undefined 46 private frameChart: FrameChart | null | undefined; 47 private isChartShow: boolean = false; 48 private sortColumn: string = ''; 49 private sortType: number = 0; 50 51 set data(val: SelectionParam | any) { 52 if(val == this.currentSelection){ 53 return; 54 } 55 this.currentSelection = val 56 this.initFilterTypes() 57 let types: Array<string> = [] 58 if (val.nativeMemory.indexOf(this.native_type[0]) != -1) { 59 types.push("'AllocEvent'"); 60 types.push("'MmapEvent'"); 61 } else { 62 if (val.nativeMemory.indexOf(this.native_type[1]) != -1) { 63 types.push("'AllocEvent'"); 64 } 65 if (val.nativeMemory.indexOf(this.native_type[2]) != -1) { 66 types.push("'MmapEvent'"); 67 } 68 } 69 // @ts-ignore 70 this.tbl?.shadowRoot.querySelector(".table").style.height = (this.parentElement.clientHeight - 20 - 31) + "px" 71 // @ts-ignore 72 this.tblData?.shadowRoot.querySelector(".table").style.height = (this.parentElement.clientHeight) + "px" 73 // @ts-ignore 74 this.tblData?.recycleDataSource = []; 75 // @ts-ignore 76 this.tbl?.recycleDataSource = []; 77 this.progressEL!.loading = true 78 this.loadingPage.style.visibility = "visible" 79 queryNativeHookEventTid(val.leftNs, val.rightNs, types).then((result) => { 80 this.queryResult = result; 81 this.getDataByNativeMemoryWorker(val) 82 }) 83 } 84 85 getDataByNativeMemoryWorker(val: SelectionParam | any){ 86 let args = new Map<string,any>(); 87 args.set("data",this.queryResult); 88 args.set("filterAllocType",this.filterAllocationType); 89 args.set("filterEventType",this.filterNativeType); 90 args.set("leftNs",val.leftNs); 91 args.set("rightNs",val.rightNs); 92 args.set("actionType","call-info"); 93 this.startWorker(args,(results: any[]) => { 94 this.tblData!.recycleDataSource = [] 95 this.progressEL!.loading = false 96 if (results.length > 0) { 97 this.source = results; 98 this.sortTreeByColumn(this.sortColumn,this.sortType); 99 this.frameChart!.mode = ChartMode.Byte; 100 this.frameChart!.data = this.source; 101 this.frameChart?.updateCanvas(true,this.clientWidth); 102 this.frameChart?.calculateChartData(); 103 }else { 104 this.source = [] 105 this.tbl!.recycleDataSource = []; 106 this.frameChart!.data = []; 107 this.frameChart!.clearCanvas() 108 } 109 }) 110 } 111 112 startWorker(args: Map<string,any>, handler: Function) { 113 this.loadingList.push(1) 114 this.progressEL!.loading = true 115 this.loadingPage.style.visibility = "visible" 116 query("NativeMemory", "", args, "native-memory-action").then((results) => { 117 handler(results) 118 this.loadingList.splice(0,1) 119 if(this.loadingList.length == 0) { 120 this.progressEL!.loading = false 121 this.loadingPage.style.visibility = "hidden" 122 } 123 }) 124 } 125 126 getParentTree(src: Array<NativeHookCallInfo>, target: NativeHookCallInfo, parents: Array<NativeHookCallInfo>): boolean { 127 for (let hook of src) { 128 if (hook.id == target.id) { 129 parents.push(hook) 130 return true 131 } else { 132 if (this.getParentTree(hook.children as Array<NativeHookCallInfo>, target, parents)) { 133 parents.push(hook); 134 return true; 135 } 136 } 137 } 138 return false; 139 } 140 141 getChildTree(src: Array<NativeHookCallInfo>, eventId: number, children: Array<NativeHookCallInfo>): boolean { 142 for (let hook of src) { 143 if (hook.eventId == eventId && hook.children.length == 0) { 144 children.push(hook) 145 return true 146 } else { 147 if (this.getChildTree(hook.children as Array<NativeHookCallInfo>, eventId, children)) { 148 children.push(hook); 149 return true; 150 } 151 } 152 } 153 return false; 154 } 155 156 setRightTableData(hook: NativeHookCallInfo) { 157 let parents: Array<NativeHookCallInfo> = []; 158 let children: Array<NativeHookCallInfo> = []; 159 this.getParentTree(this.source, hook, parents); 160 let maxEventId = hook.eventId; 161 let maxHeap = 0; 162 163 function findMaxStack(hook: NativeHookCallInfo) { 164 if (hook.children.length == 0) { 165 if (hook.size > maxHeap) { 166 maxHeap = hook.size; 167 maxEventId = hook.eventId; 168 } 169 } else { 170 hook.children.map((hookChild) => { 171 findMaxStack(<NativeHookCallInfo>hookChild); 172 }) 173 } 174 } 175 176 findMaxStack(hook); 177 this.getChildTree(hook.children as Array<NativeHookCallInfo>, maxEventId, children); 178 this.rightSource = parents.reverse().concat(children.reverse()); 179 let len = this.rightSource.length; 180 // @ts-ignore 181 this.tblData?.dataSource = len == 0 ? [] : this.rightSource.slice(1, len); 182 183 } 184 185 initFilterTypes() { 186 let filter = this.shadowRoot?.querySelector<TabPaneFilter>("#filter") 187 this.queryResult = [] 188 filter!.firstSelect = "0" 189 filter!.secondSelect = "0" 190 this.filterAllocationType = "0" 191 this.filterNativeType = "0" 192 } 193 194 sortTreeByColumn(column:string,sort:number){ 195 this.sortColumn = column; 196 this.sortType = sort; 197 this.tbl!.recycleDataSource = this.sortTree(this.source,column,sort) 198 } 199 200 sortTree(arr: Array<NativeHookCallInfo>, column: string, sort: number): Array<NativeHookCallInfo> { 201 let sortArr = arr.sort((a, b) => { 202 if (column == 'size') { 203 if (sort == 0) { 204 return a.eventId - b.eventId; 205 } else if (sort == 1) { 206 return a.size - b.size; 207 } else { 208 return b.size - a.size; 209 } 210 } else { 211 if (sort == 0) { 212 return a.eventId - b.eventId; 213 } else if (sort == 1) { 214 return a.count - b.count; 215 } else { 216 return b.count - a.count; 217 } 218 } 219 }) 220 sortArr.map((call) => { 221 call.children = this.sortTree(call.children as Array<NativeHookCallInfo>, column, sort); 222 }) 223 return sortArr; 224 } 225 226 showButtomMenu(isShow : boolean){ 227 let filter = this.shadowRoot?.querySelector<TabPaneFilter>("#filter")! 228 if (isShow) { 229 filter.setAttribute('first',''); 230 filter.setAttribute('second',''); 231 } else { 232 filter.removeAttribute('first'); 233 filter.removeAttribute('second'); 234 } 235 } 236 237 initElements(): void { 238 this.loadingPage = this.shadowRoot?.querySelector('.loading'); 239 this.progressEL = this.shadowRoot?.querySelector('.progress') as LitProgressBar 240 this.tbl = this.shadowRoot?.querySelector<LitTable>('#tb-native-callinfo'); 241 this.tblData = this.shadowRoot?.querySelector<LitTable>('#tb-native-data'); 242 this.frameChart = this.shadowRoot?.querySelector<FrameChart>('#framechart'); 243 let pageTab = this.shadowRoot?.querySelector('#show_table'); 244 let pageChart = this.shadowRoot?.querySelector('#show_chart'); 245 this.frameChart?.addChartClickListener((showMenu : boolean) => { 246 this.parentElement!.scrollTo(0,0); 247 this.showButtomMenu(showMenu) 248 }); 249 250 this.tbl!.addEventListener("row-click", (e) => { 251 // @ts-ignore 252 let data = (e.detail.data as NativeHookCallInfo); 253 (data as any).isSelected = true 254 this.setRightTableData(data); 255 this.tblData?.clearAllSelection(data) 256 this.tblData?.setCurrentSelection(data) 257 // @ts-ignore 258 if ((e.detail as any).callBack) { 259 // @ts-ignore 260 (e.detail as any).callBack(true) 261 } 262 }) 263 this.tblData!.addEventListener("row-click", (e) => { 264 this.tbl!.expandList(this.rightSource) 265 // @ts-ignore 266 let detail = e.detail.data as NativeHookCallInfo; 267 this.tbl?.clearAllSelection(detail) 268 detail.isSelected = true 269 this.tbl!.scrollToData(this.rightSource[detail.depth + 1]) 270 // @ts-ignore 271 if ((e.detail as any).callBack) { 272 // @ts-ignore 273 (e.detail as any).callBack(true) 274 } 275 }) 276 this.tbl!.addEventListener('column-click', (evt) => { 277 // @ts-ignore 278 this.sortTreeByColumn((evt.detail.key == "countValue"||evt.detail.key == "countPercent") ? 'countValue':'size',evt.detail.sort) 279 }); 280 let filterHeight = 0; 281 new ResizeObserver((entries) => { 282 let tabPaneFilter = this.shadowRoot!.querySelector("#filter") as HTMLElement; 283 if (tabPaneFilter.clientHeight > 0) filterHeight = tabPaneFilter.clientHeight; 284 if (this.parentElement!.clientHeight > filterHeight) { 285 tabPaneFilter.style.display = "flex"; 286 } else { 287 tabPaneFilter.style.display = "none"; 288 } 289 if (this.parentElement?.clientHeight != 0) { 290 if (!this.isChartShow) { 291 // @ts-ignore 292 this.tbl?.shadowRoot.querySelector(".table").style.height = (this.parentElement.clientHeight) + "px" 293 this.tbl?.reMeauseHeight(); 294 } else { 295 // @ts-ignore 296 this.frameChart?.updateCanvas(false,entries[0].contentRect.width); 297 this.frameChart?.calculateChartData(); 298 } 299 // @ts-ignore 300 this.tbl?.shadowRoot.querySelector(".table").style.height = (this.parentElement.clientHeight) - 10 - 31 + "px"; 301 this.tbl?.reMeauseHeight(); 302 // @ts-ignore 303 this.tblData?.shadowRoot.querySelector(".table").style.height = (this.parentElement.clientHeight) - 10 - 31 + "px"; 304 this.tblData?.reMeauseHeight() 305 this.loadingPage.style.height = (this.parentElement!.clientHeight - 24) + "px" 306 } 307 }).observe(this.parentElement!); 308 this.shadowRoot?.querySelector<TabPaneFilter>("#filter")!.getFilterData((data: FilterData) => { 309 this.filterAllocationType = data.firstSelect || "0" 310 this.filterNativeType = data.secondSelect || "0" 311 this.getDataByNativeMemoryWorker(this.currentSelection) 312 if (data.icon == 'block') { 313 pageChart?.setAttribute('class', 'show'); 314 pageTab?.setAttribute('class', ''); 315 this.isChartShow = true; 316 this.frameChart!.data = this.source; 317 this.frameChart?.calculateChartData(); 318 } else if (data.icon == 'tree') { 319 pageChart?.setAttribute('class', ''); 320 pageTab?.setAttribute('class', 'show'); 321 this.isChartShow = false; 322 this.frameChart!.clearCanvas(); 323 this.tbl!.reMeauseHeight(); 324 } 325 }); 326 327 this.parentElement!.onscroll = () => { 328 this.frameChart!.tabPaneScrollTop = this.parentElement!.scrollTop; 329 }; 330 this.initFilterTypes() 331 } 332 333 initHtml(): string { 334 return ` 335 <style> 336 :host{ 337 display: flex; 338 flex-direction: column; 339 padding: 10px 10px 0 10px; 340 } 341 tab-pane-filter { 342 border: solid rgb(216,216,216) 1px; 343 float: left; 344 position: fixed; 345 bottom: 0; 346 width: 100%; 347 } 348 selector{ 349 display: none; 350 } 351 .progress{ 352 bottom: 33px; 353 position: absolute; 354 height: 1px; 355 left: 0; 356 right: 0; 357 } 358 .loading{ 359 bottom: 0; 360 position: absolute; 361 left: 0; 362 right: 0; 363 width:100%; 364 background:transparent; 365 z-index: 999999; 366 } 367 .show{ 368 display: flex; 369 flex: 1; 370 } 371 </style> 372 <div style="display: flex;flex-direction: row"> 373 <selector id='show_table' class="show"> 374 <lit-slicer style="width:100%"> 375 <div style="width: 65%"> 376 <lit-table id="tb-native-callinfo" style="height: auto" tree> 377 <lit-table-column width="60%" title="Symbol Name" data-index="title" key="title" align="flex-start"> 378 </lit-table-column> 379 <lit-table-column width="1fr" title="Size" data-index="heapSizeStr" key="heapSizeStr" align="flex-start" order> 380 </lit-table-column> 381 <lit-table-column width="1fr" title="%" data-index="heapPercent" key="heapPercent" align="flex-start" order> 382 </lit-table-column> 383 <lit-table-column width="1fr" title="Count" data-index="countValue" key="countValue" align="flex-start" order> 384 </lit-table-column> 385 <lit-table-column width="1fr" title="%" data-index="countPercent" key="countPercent" align="flex-start" order> 386 </lit-table-column> 387 <lit-table-column width="1fr" title=" " data-index="type" key="type" align="flex-start" > 388 <template> 389 <img src="img/library.png" size="20" v-if=" type == 1 "> 390 <img src="img/function.png" size="20" v-if=" type == 0 "> 391 <div v-if=" type == - 1 "></div> 392 </template> 393 </lit-table-column> 394 </lit-table> 395 </div> 396 <lit-slicer-track ></lit-slicer-track> 397 <lit-table id="tb-native-data" no-head style="height: auto;border-left: 1px solid var(--dark-border1,#e2e2e2)"> 398 <lit-table-column width="60px" title="" data-index="type" key="type" align="flex-start" > 399 <template> 400 <img src="img/library.png" size="20" v-if=" type == 1 "> 401 <img src="img/function.png" size="20" v-if=" type == 0 "> 402 </template> 403 </lit-table-column> 404 <lit-table-column width="1fr" title="" data-index="title" key="title" align="flex-start"> 405 </lit-table-column> 406 </lit-table> 407 </lit-slicer> 408 </selector> 409 <selector id='show_chart'> 410 <tab-framechart id='framechart' style='width: 100%;height: auto'> </tab-framechart> 411 </selector> 412 <lit-progress-bar class="progress"></lit-progress-bar> 413 <tab-pane-filter id="filter" icon first second></tab-pane-filter> 414 <div class="loading"></div> 415 </div> 416 `; 417 } 418} 419