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 {LitProgressBar} from "../../../../../base-ui/progress-bar/LitProgressBar.js"; 19import {FrameChart} from "../../../chart/FrameChart.js"; 20import "../../../chart/FrameChart.js"; 21import {DisassemblingWindow} from "../../../DisassemblingWindow.js"; 22import "../../../DisassemblingWindow.js"; 23import {SelectionParam} from "../../../../bean/BoxSelection.js"; 24import {ChartMode} from "../../../../bean/FrameChartStruct.js"; 25import {FilterData, TabPaneFilter} from "../TabPaneFilter.js"; 26import "../TabPaneFilter.js"; 27import {procedurePool} from "../../../../database/Procedure.js"; 28import {FileMerageBean} from "../../../../database/logic-worker/ProcedureLogicWorkerFileSystem.js"; 29 30@element('tabpane-filesystem-calltree') 31export class TabpaneFilesystemCalltree extends BaseElement { 32 private tbl: LitTable | null | undefined; 33 private tbr: LitTable | null | undefined; 34 private progressEL:LitProgressBar | null | undefined; 35 private rightSource: Array<FileMerageBean> = []; 36 private filter: any 37 private dataSource: any[] = [] 38 private sortKey = "weight"; 39 private sortType = 0; 40 private currentSelectedData: any = undefined 41 private frameChart: FrameChart | null | undefined; 42 private isChartShow: boolean = false; 43 private systmeRuleName = "/system/" 44 private numRuleName = "/max/min/" 45 private modal: DisassemblingWindow | null | undefined; 46 private needShowMenu = true; 47 private searchValue: string = "" 48 private loadingList:number[] = [] 49 private loadingPage:any; 50 private currentSelection:SelectionParam|undefined 51 52 set data(val: SelectionParam | any) { 53 this.currentSelection = val 54 this.modal!.style.display = 'none'; 55 this.tbl!.style.visibility = "visible"; 56 if (this.parentElement!.clientHeight > this.filter!.clientHeight) { 57 this.filter!.style.display = "flex"; 58 } else { 59 this.filter!.style.display = "none"; 60 } 61 this.filter!.initializeFilterTree(true, true, true) 62 this.filter!.filterValue = "" 63 this.progressEL!.loading = true 64 this.loadingPage.style.visibility = "visible" 65 this.getDataByWorker([{ 66 funcName: "setSearchValue", 67 funcArgs: [""] 68 }, { 69 funcName: "getCurrentDataFromDb", 70 funcArgs: [{queryFuncName:"fileSystem",...val}] 71 }], (results: any[]) => { 72 this.setLTableData(results) 73 this.tbr!.recycleDataSource = [] 74 this.frameChart!.mode = ChartMode.Duration; 75 this.frameChart!.data = this.dataSource; 76 this.frameChart?.updateCanvas(true, this.clientWidth); 77 this.frameChart?.calculateChartData(); 78 }) 79 } 80 81 getParentTree(src: Array<FileMerageBean>, target: FileMerageBean, parents: Array<FileMerageBean>): boolean { 82 for (let call of src) { 83 if (call.id == target.id) { 84 parents.push(call) 85 return true 86 } else { 87 if (this.getParentTree(call.children as Array<FileMerageBean>, target, parents)) { 88 parents.push(call); 89 return true; 90 } 91 } 92 } 93 return false; 94 } 95 96 getChildTree(src: Array<FileMerageBean>, id: string, children: Array<FileMerageBean>): boolean { 97 for (let call of src) { 98 if (call.id == id && call.children.length == 0) { 99 children.push(call) 100 return true 101 } else { 102 if (this.getChildTree(call.children as Array<FileMerageBean>, id, children)) { 103 children.push(call); 104 return true; 105 } 106 } 107 } 108 return false; 109 } 110 111 setRightTableData(call: FileMerageBean) { 112 let parents: Array<FileMerageBean> = []; 113 let children: Array<FileMerageBean> = []; 114 this.getParentTree(this.dataSource, call, parents); 115 let maxId = call.id; 116 let maxDur = 0; 117 118 function findMaxStack(call: FileMerageBean) { 119 if (call.children.length == 0) { 120 if (call.dur > maxDur) { 121 maxDur = call.dur; 122 maxId = call.id; 123 } 124 } else { 125 call.children.map((callChild:any) => { 126 findMaxStack(<FileMerageBean>callChild); 127 }) 128 } 129 } 130 131 findMaxStack(call); 132 this.getChildTree(call.children as Array<FileMerageBean>, maxId, children); 133 let arr = parents.reverse().concat(children.reverse()); 134 for (let data of arr) { 135 data.type = (data.libName.endsWith(".so.1") || data.libName.endsWith(".dll") || data.libName.endsWith(".so")) ? 0 : 1; 136 } 137 let len = arr.length; 138 this.rightSource = arr; 139 this.tbr!.dataSource = len == 0 ? [] : arr 140 } 141 142 showButtomMenu(isShow: boolean) { 143 if (isShow) { 144 this.filter.setAttribute('tree', ''); 145 this.filter.setAttribute('input', ''); 146 this.filter.setAttribute('inputLeftText', ''); 147 } else { 148 this.filter.removeAttribute('tree'); 149 this.filter.removeAttribute('input'); 150 this.filter.removeAttribute('inputLeftText') 151 } 152 } 153 154 initElements(): void { 155 this.tbl = this.shadowRoot?.querySelector<LitTable>('#tb-filesystem-calltree'); 156 this.progressEL = this.shadowRoot?.querySelector('.progress') as LitProgressBar 157 this.frameChart = this.shadowRoot?.querySelector<FrameChart>('#framechart'); 158 this.modal = this.shadowRoot?.querySelector<DisassemblingWindow>('tab-native-data-modal'); 159 this.loadingPage = this.shadowRoot?.querySelector('.loading'); 160 this.frameChart!.addChartClickListener((needShowMenu: boolean) => { 161 this.parentElement!.scrollTo(0, 0); 162 this.showButtomMenu(needShowMenu) 163 this.needShowMenu = needShowMenu; 164 }); 165 this.tbl!.rememberScrollTop = true; 166 this.filter = this.shadowRoot?.querySelector<TabPaneFilter>("#filter") 167 this.tbl!.addEventListener('row-click', (evt: any) => { 168 // @ts-ignore 169 let data = (evt.detail.data as FileMerageBean); 170 this.setRightTableData(data); 171 data.isSelected = true; 172 this.currentSelectedData = data; 173 this.tbr?.clearAllSelection(data); 174 this.tbr?.setCurrentSelection(data); 175 // @ts-ignore 176 if ((evt.detail as any).callBack) { 177 // @ts-ignore 178 (evt.detail as any).callBack(true) 179 } 180 }) 181 this.tbr = this.shadowRoot?.querySelector<LitTable>('#tb-filesystem-list'); 182 this.tbr!.addEventListener('row-click', (evt: any) => { 183 // @ts-ignore 184 let data = (evt.detail.data as FileMerageBean); 185 this.tbl?.clearAllSelection(data); 186 (data as any).isSelected = true 187 this.tbl!.scrollToData(data) 188 // @ts-ignore 189 if ((evt.detail as any).callBack) { 190 // @ts-ignore 191 (evt.detail as any).callBack(true) 192 } 193 }) 194 this.modal!.setCloseListener(() => { 195 this.modal!.style.display = 'none'; 196 this.tbl!.style.visibility = "visible"; 197 this.shadowRoot!.querySelector<TabPaneFilter>("#filter")!.style.display = 'flex'; 198 }); 199 let filterFunc = (data: any) => { 200 let args: any[] = [] 201 if (data.type == "check") { 202 if (data.item.checked) { 203 args.push({ 204 funcName: "splitTree", 205 funcArgs: [data.item.name, data.item.select == "0", data.item.type == "symbol"] 206 }) 207 } else { 208 args.push({ 209 funcName: "resotreAllNode", 210 funcArgs: [[data.item.name]] 211 }) 212 args.push({ 213 funcName: "resetAllNode", 214 funcArgs: [] 215 }) 216 args.push({ 217 funcName: "clearSplitMapData", 218 funcArgs: [data.item.name] 219 }) 220 } 221 } else if (data.type == "select") { 222 args.push({ 223 funcName: "resotreAllNode", 224 funcArgs: [[data.item.name]] 225 }) 226 args.push({ 227 funcName: "clearSplitMapData", 228 funcArgs: [data.item.name] 229 }) 230 args.push({ 231 funcName: "splitTree", 232 funcArgs: [data.item.name, data.item.select == "0", data.item.type == "symbol"] 233 }) 234 } else if (data.type == "button") { 235 if (data.item == "symbol") { 236 if (this.currentSelectedData && !this.currentSelectedData.canCharge) { 237 return 238 } 239 if (this.currentSelectedData != undefined) { 240 this.filter!.addDataMining({name: this.currentSelectedData.symbolName}, data.item) 241 args.push({ 242 funcName: "splitTree", 243 funcArgs: [this.currentSelectedData.symbolName, false, true] 244 }) 245 } else { 246 return 247 } 248 } else if (data.item == "library") { 249 if (this.currentSelectedData && !this.currentSelectedData.canCharge) { 250 return 251 } 252 if (this.currentSelectedData != undefined && this.currentSelectedData.libName != "") { 253 this.filter!.addDataMining({name: this.currentSelectedData.libName}, data.item) 254 args.push({ 255 funcName: "splitTree", 256 funcArgs: [this.currentSelectedData.libName, false, false] 257 }) 258 } else { 259 return 260 } 261 } else if (data.item == "restore") { 262 if (data.remove != undefined && data.remove.length > 0) { 263 let list = data.remove.map((item: any) => { 264 return item.name 265 }) 266 args.push({ 267 funcName: "resotreAllNode", 268 funcArgs: [list] 269 }) 270 args.push({ 271 funcName: "resetAllNode", 272 funcArgs: [] 273 }) 274 list.forEach((symbolName: string) => { 275 args.push({ 276 funcName: "clearSplitMapData", 277 funcArgs: [symbolName] 278 }) 279 }) 280 } 281 } 282 } 283 this.getDataByWorker(args, (result: any[]) => { 284 this.setLTableData(result) 285 this.frameChart!.data = this.dataSource; 286 if (this.isChartShow) this.frameChart?.calculateChartData(); 287 this.tbl!.move1px() 288 if (this.currentSelectedData) { 289 this.currentSelectedData.isSelected = false; 290 this.tbl?.clearAllSelection(this.currentSelectedData) 291 this.tbr!.recycleDataSource = [] 292 this.currentSelectedData = undefined 293 } 294 }) 295 } 296 this.filter!.getDataLibrary(filterFunc) 297 this.filter!.getDataMining(filterFunc) 298 this.filter!.getCallTreeData((data: any) => { 299 if (data.value == 0) { 300 this.refreshAllNode({...this.filter!.getFilterTreeData(), callTree: data.checks}) 301 } else { 302 let args: any[] = [] 303 if (data.checks[1]) { 304 args.push({ 305 funcName: "hideSystemLibrary", 306 funcArgs: [] 307 }) 308 args.push({ 309 funcName: "resetAllNode", 310 funcArgs: [] 311 }) 312 } else { 313 args.push({ 314 funcName: "resotreAllNode", 315 funcArgs: [[this.systmeRuleName]] 316 }) 317 args.push({ 318 funcName: "resetAllNode", 319 funcArgs: [] 320 }) 321 args.push({ 322 funcName: "clearSplitMapData", 323 funcArgs: [this.systmeRuleName] 324 }) 325 } 326 this.getDataByWorker(args, (result: any[]) => { 327 this.setLTableData(result) 328 this.frameChart!.data = this.dataSource; 329 if (this.isChartShow) this.frameChart?.calculateChartData(); 330 }) 331 332 } 333 }) 334 this.filter!.getCallTreeConstraintsData((data: any) => { 335 let args: any[] = [{ 336 funcName: "resotreAllNode", 337 funcArgs: [[this.numRuleName]] 338 }, { 339 funcName: "clearSplitMapData", 340 funcArgs: [this.numRuleName] 341 }] 342 if (data.checked) { 343 args.push({ 344 funcName: "hideNumMaxAndMin", 345 funcArgs: [parseInt(data.min), data.max] 346 }) 347 } 348 args.push({ 349 funcName: "resetAllNode", 350 funcArgs: [] 351 }) 352 this.getDataByWorker(args, (result: any[]) => { 353 this.setLTableData(result) 354 this.frameChart!.data = this.dataSource; 355 if (this.isChartShow) this.frameChart?.calculateChartData(); 356 }) 357 358 }) 359 this.filter!.getFilterData((data: FilterData) => { 360 if (this.searchValue != this.filter!.filterValue) { 361 this.searchValue = this.filter!.filterValue 362 let args = [ 363 { 364 funcName: "setSearchValue", 365 funcArgs: [this.searchValue] 366 }, 367 { 368 funcName: "resetAllNode", 369 funcArgs: [] 370 } 371 ] 372 this.getDataByWorker(args, (result: any[]) => { 373 this.setLTableData(result) 374 this.frameChart!.data = this.dataSource; 375 this.switchFlameChart(data) 376 }) 377 }else { 378 this.switchFlameChart(data) 379 } 380 381 }) 382 this.tbl!.addEventListener('column-click', (evt) => { 383 // @ts-ignore 384 this.sortKey = evt.detail.key 385 // @ts-ignore 386 this.sortType = evt.detail.sort 387 // @ts-ignore 388 this.setLTableData(this.dataSource) 389 this.frameChart!.data = this.dataSource; 390 }); 391 } 392 393 connectedCallback() { 394 super.connectedCallback(); 395 let filterHeight = 0; 396 new ResizeObserver((entries) => { 397 let tabPaneFilter = this.shadowRoot!.querySelector("#filter") as HTMLElement; 398 if (tabPaneFilter.clientHeight > 0) filterHeight = tabPaneFilter.clientHeight; 399 if (this.parentElement!.clientHeight > filterHeight) { 400 tabPaneFilter.style.display = "flex"; 401 } else { 402 tabPaneFilter.style.display = "none"; 403 } 404 this.modal!.style.height = this.tbl!.clientHeight - 2 + 'px'; //2 is borderWidth 405 if (this.tbl!.style.visibility == "hidden") { 406 tabPaneFilter.style.display = "none"; 407 } 408 if (this.parentElement?.clientHeight != 0) { 409 if (this.isChartShow) { 410 this.frameChart?.updateCanvas(false, entries[0].contentRect.width); 411 this.frameChart?.calculateChartData(); 412 } 413 // @ts-ignore 414 this.tbl?.shadowRoot.querySelector(".table").style.height = (this.parentElement.clientHeight - 10 - 35) + "px" 415 this.tbl?.reMeauseHeight() 416 // @ts-ignore 417 this.tbr?.shadowRoot.querySelector(".table").style.height = (this.parentElement.clientHeight - 45 - 21) + "px" 418 this.tbr?.reMeauseHeight() 419 this.loadingPage.style.height = (this.parentElement!.clientHeight - 24) + "px" 420 } 421 }).observe(this.parentElement!); 422 this.parentElement!.onscroll = () => { 423 this.frameChart!.tabPaneScrollTop = this.parentElement!.scrollTop; 424 }; 425 } 426 427 switchFlameChart(data:any){ 428 let pageTab = this.shadowRoot?.querySelector('#show_table'); 429 let pageChart = this.shadowRoot?.querySelector('#show_chart'); 430 if (data.icon == 'block') { 431 pageChart?.setAttribute('class', 'show'); 432 pageTab?.setAttribute('class', ''); 433 this.isChartShow = true; 434 this.filter!.disabledMining = true; 435 this.showButtomMenu(this.needShowMenu); 436 this.frameChart!.data = this.dataSource; 437 this.frameChart?.calculateChartData(); 438 } else if (data.icon == 'tree') { 439 pageChart?.setAttribute('class', ''); 440 pageTab?.setAttribute('class', 'show'); 441 this.showButtomMenu(true); 442 this.isChartShow = false; 443 this.filter!.disabledMining = false; 444 this.frameChart!.clearCanvas(); 445 this.tbl!.reMeauseHeight() 446 } 447 } 448 449 450 refreshAllNode(filterData: any) { 451 let args:any[] = [] 452 let isTopDown: boolean = !filterData.callTree[0]; 453 let isHideSystemLibrary = filterData.callTree[1]; 454 let list = filterData.dataMining.concat(filterData.dataLibrary); 455 args.push({ 456 funcName: "getCallChainsBySampleIds", 457 funcArgs: [isTopDown] 458 }) 459 this.tbr!.recycleDataSource = [] 460 if (isHideSystemLibrary) { 461 args.push({ 462 funcName: "hideSystemLibrary", 463 funcArgs: [] 464 }) 465 } 466 if (filterData.callTreeConstraints.checked) { 467 args.push({ 468 funcName: "hideNumMaxAndMin", 469 funcArgs: [parseInt(filterData.callTreeConstraints.inputs[0]), filterData.callTreeConstraints.inputs[1]] 470 }) 471 } 472 args.push({ 473 funcName: "splitAllProcess", 474 funcArgs: [list] 475 }) 476 args.push({ 477 funcName: "resetAllNode", 478 funcArgs: [] 479 }) 480 this.getDataByWorker(args, (result: any[]) => { 481 this.setLTableData(result) 482 this.frameChart!.data = this.dataSource; 483 if (this.isChartShow) this.frameChart?.calculateChartData(); 484 }) 485 } 486 487 setLTableData(resultData:any[]) { 488 this.dataSource = this.sortTree(resultData) 489 this.tbl!.recycleDataSource = this.dataSource 490 } 491 492 sortTree(arr: Array<any>): Array<any> { 493 let sortArr = arr.sort((a, b) => { 494 if (this.sortKey == 'self') { 495 if (this.sortType == 0) { 496 return b.dur - a.dur; 497 } else if (this.sortType == 1) { 498 return a.selfDur - b.selfDur; 499 } else { 500 return b.selfDur - a.selfDur; 501 } 502 } else { 503 if (this.sortType == 0) { 504 return b.dur - a.dur; 505 } else if (this.sortType == 1) { 506 return a.dur - b.dur; 507 } else { 508 return b.dur - a.dur; 509 } 510 } 511 }) 512 sortArr.map((call) => { 513 call.children = this.sortTree(call.children); 514 }) 515 return sortArr; 516 } 517 518 getDataByWorker(args: any[], handler: Function) { 519 this.loadingList.push(1) 520 this.progressEL!.loading = true 521 this.loadingPage.style.visibility = "visible" 522 procedurePool.submitWithName("logic0","fileSystem-action",args,undefined,(results:any)=>{ 523 handler(results) 524 this.loadingList.splice(0,1) 525 if(this.loadingList.length == 0) { 526 this.progressEL!.loading = false 527 this.loadingPage.style.visibility = "hidden" 528 } 529 }) 530 } 531 532 initHtml(): string { 533 return ` 534 <style> 535 :host{ 536 display: flex; 537 flex-direction: column; 538 padding: 10px 10px 0 10px; 539 } 540 tab-pane-filter { 541 border: solid rgb(216,216,216) 1px; 542 float: left; 543 position: fixed; 544 bottom: 0; 545 width: 100%; 546 } 547 selector{ 548 display: none; 549 } 550 .show{ 551 display: flex; 552 flex: 1; 553 } 554 .progress{ 555 bottom: 33px; 556 position: absolute; 557 height: 1px; 558 left: 0; 559 right: 0; 560 } 561 .loading{ 562 bottom: 0; 563 position: absolute; 564 left: 0; 565 right: 0; 566 width:100%; 567 background:transparent; 568 z-index: 999999; 569 } 570 </style> 571 <div style="display: flex;flex-direction: row"> 572 573 <selector id='show_table' class="show"> 574 <lit-slicer style="width:100%"> 575 <div id="left_table" style="width: 65%"> 576 <tab-native-data-modal id="modal"></tab-native-data-modal> 577 <lit-table id="tb-filesystem-calltree" style="height: auto" tree> 578 <lit-table-column width="70%" title="Call Stack" data-index="symbolName" key="symbolName" align="flex-start" ></lit-table-column> 579 <lit-table-column width="1fr" title="Local" data-index="self" key="self" align="flex-start" order></lit-table-column> 580 <lit-table-column width="1fr" title="Weight" data-index="weight" key="weight" align="flex-start" order></lit-table-column> 581 <lit-table-column width="1fr" title="%" data-index="weightPercent" key="weightPercent" align="flex-start" order></lit-table-column> 582 </lit-table> 583 584 </div> 585 <lit-slicer-track ></lit-slicer-track> 586 <lit-table id="tb-filesystem-list" no-head style="height: auto;border-left: 1px solid var(--dark-border1,#e2e2e2)"> 587 <span slot="head">Heaviest Stack Trace</span> 588 <lit-table-column width="30px" title="" data-index="type" key="type" align="flex-start" > 589 <template> 590 <img src="img/library.png" size="20" v-if=" type == 1 "> 591 <img src="img/function.png" size="20" v-if=" type == 0 "> 592 </template> 593 </lit-table-column> 594 <lit-table-column width="60px" title="" data-index="count" key="count" align="flex-start"></lit-table-column> 595 <lit-table-column width="1fr" title="" data-index="symbolName" key="symbolName" align="flex-start"></lit-table-column> 596 </lit-table> 597 </div> 598 </lit-slicer> 599 </selector> 600 <tab-pane-filter id="filter" input inputLeftText icon tree></tab-pane-filter> 601 <lit-progress-bar class="progress"></lit-progress-bar> 602 <selector id='show_chart'> 603 <tab-framechart id='framechart' style='width: 100%;height: auto'> </tab-framechart> 604 </selector> 605 <div class="loading"></div> 606 </div>`; 607 } 608 609}