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 '../TabPaneFilter'; 19import { TabPaneFilter } from '../TabPaneFilter'; 20import { SelectionParam } from '../../../../bean/BoxSelection'; 21import '../../../chart/FrameChart'; 22import '../../../../../base-ui/slicer/lit-slicer'; 23import '../../../../../base-ui/progress-bar/LitProgressBar'; 24import { procedurePool } from '../../../../database/Procedure'; 25import { type LitProgressBar } from '../../../../../base-ui/progress-bar/LitProgressBar'; 26import { type PerfBottomUpStruct } from '../../../../bean/PerfBottomUpStruct'; 27import { findSearchNode } from '../../../../database/ui-worker/ProcedureWorkerCommon'; 28import { SpSystemTrace } from '../../../SpSystemTrace'; 29 30@element('tabpane-perf-bottom-up') 31export class TabpanePerfBottomUp extends BaseElement { 32 private bottomUpTable: LitTable | null | undefined; 33 private stackTable: LitTable | null | undefined; 34 private sortKey = ''; 35 private sortType = 0; 36 private bottomUpSource: Array<PerfBottomUpStruct> = []; 37 private bottomUpFilter: TabPaneFilter | undefined | null; 38 private progressEL: LitProgressBar | null | undefined; 39 private searchValue: string = ''; 40 private currentSelection: SelectionParam | undefined; 41 42 public initElements(): void { 43 this.bottomUpTable = this.shadowRoot?.querySelector('#callTreeTable') as LitTable; 44 this.stackTable = this.shadowRoot?.querySelector('#stackTable') as LitTable; 45 this.progressEL = this.shadowRoot?.querySelector('.progress') as LitProgressBar; 46 this.bottomUpFilter = this.shadowRoot?.querySelector('#filter') as TabPaneFilter; 47 let spApplication = document.querySelector('body > sp-application'); 48 let spSystemTrace = spApplication?.shadowRoot?.querySelector( 49 'div > div.content > sp-system-trace' 50 ) as SpSystemTrace; 51 this.bottomUpTable!.addEventListener('row-click', (evt) => this.bottomUpTableRowClickHandler(evt)); 52 this.stackTable!.addEventListener('row-click', (evt) => this.stackTableRowClick(evt)); 53 this.bottomUpTable!.addEventListener('column-click', (evt) => this.bottomUpTableColumnClickHandler(evt)); 54 this.bottomUpFilter!.getFilterData(() => { 55 if (this.searchValue !== this.bottomUpFilter!.filterValue) { 56 this.searchValue = this.bottomUpFilter!.filterValue; 57 findSearchNode(this.bottomUpSource, this.searchValue, false); 58 } 59 this.bottomUpTable!.setStatus(this.bottomUpSource, true); 60 this.setBottomUpTableData(this.bottomUpSource); 61 }); 62 this.bottomUpFilter?.addEventListener('focus', () => { 63 spSystemTrace.focusTarget = 'bottomUpInput'; 64 }); 65 this.bottomUpFilter?.addEventListener('blur', () => { 66 spSystemTrace.focusTarget = ''; 67 }); 68 } 69 70 private getDataByWorker(val: SelectionParam, handler: (results: Array<PerfBottomUpStruct>) => void): void { 71 this.progressEL!.loading = true; 72 const args = [ 73 { 74 funcName: 'setPerfBottomUp', 75 funcArgs: [''], 76 }, 77 { 78 funcName: 'setSearchValue', 79 funcArgs: [''], 80 }, 81 { 82 funcName: 'getCurrentDataFromDb', 83 funcArgs: [val], 84 }, 85 ]; 86 procedurePool.submitWithName('logic0', 'perf-action', args, undefined, (results: Array<PerfBottomUpStruct>) => { 87 handler(results); 88 this.progressEL!.loading = false; 89 }); 90 } 91 92 set data(data: SelectionParam) { 93 if (data == this.currentSelection) { 94 return; 95 } 96 this.currentSelection = data; 97 this.sortKey = ''; 98 this.sortType = 0; 99 this.bottomUpFilter!.filterValue = ''; 100 this.getDataByWorker(data, (results: Array<PerfBottomUpStruct>) => { 101 this.setBottomUpTableData(results); 102 }); 103 } 104 105 private setBottomUpTableData(results: Array<PerfBottomUpStruct>): void { 106 const percentageDenominator = 100; 107 const percentFraction = 1; 108 this.stackTable!.recycleDataSource = []; 109 let sum = results.reduce( 110 (sum, struct) => { 111 sum.totalCount += struct.selfTime; 112 sum.totalEvent += struct.eventCount; 113 return sum; 114 }, 115 { 116 totalCount: 0, 117 totalEvent: 0, 118 } 119 ); 120 const setTabData = (array: Array<PerfBottomUpStruct>): void => { 121 array.forEach((data) => { 122 data.totalTimePercent = `${((data.totalTime / sum.totalCount) * percentageDenominator).toFixed( 123 percentFraction 124 )}%`; 125 data.selfTimePercent = `${((data.selfTime / sum.totalCount) * percentageDenominator).toFixed( 126 percentFraction 127 )}%`; 128 data.eventPercent = `${((data.eventCount / sum.totalEvent) * percentageDenominator).toFixed(percentFraction)}%`; 129 setTabData(data.children); 130 }); 131 }; 132 setTabData(results); 133 this.bottomUpSource = this.sortTree(results); 134 this.bottomUpTable!.recycleDataSource = this.bottomUpSource; 135 } 136 137 private bottomUpTableRowClickHandler(evt: Event): void { 138 const callStack: Array<PerfBottomUpStruct> = []; 139 const getCallStackChildren = (children: Array<PerfBottomUpStruct>): void => { 140 if (children.length === 0) { 141 return; 142 } 143 const heaviestChild = children.reduce((max, struct) => 144 Math.max(max.totalTime, struct.totalTime) === max.totalTime ? max : struct 145 ); 146 callStack?.push(heaviestChild); 147 getCallStackChildren(heaviestChild.children); 148 }; 149 const getParent = (list: PerfBottomUpStruct): void => { 150 if (list.parentNode && list.parentNode!.symbolName !== 'root') { 151 callStack.push(list.parentNode!); 152 getParent(list.parentNode!); 153 } 154 }; 155 156 //@ts-ignore 157 const bottomUpData = evt.detail.data as PerfBottomUpStruct; 158 document.dispatchEvent( 159 new CustomEvent('number_calibration', { 160 detail: { time: bottomUpData.tsArray }, 161 }) 162 ); 163 callStack!.push(bottomUpData); 164 if (bottomUpData.parentNode && bottomUpData.parentNode!.symbolName !== 'root') { 165 callStack.push(bottomUpData.parentNode!); 166 getParent(bottomUpData.parentNode!); 167 } 168 callStack.reverse(); 169 getCallStackChildren(bottomUpData.children); 170 this.stackTable!.recycleDataSource = callStack; 171 bottomUpData.isSelected = true; 172 this.stackTable?.clearAllSelection(bottomUpData); 173 this.stackTable?.setCurrentSelection(bottomUpData); 174 // @ts-ignore 175 if (evt.detail.callBack) { 176 // @ts-ignore 177 evt.detail.callBack(true); 178 } 179 } 180 181 private bottomUpTableColumnClickHandler(evt: Event): void { 182 // @ts-ignore 183 this.sortKey = evt.detail.key; 184 // @ts-ignore 185 this.sortType = evt.detail.sort; 186 this.setBottomUpTableData(this.bottomUpSource); 187 } 188 189 private stackTableRowClick(evt: Event): void { 190 //@ts-ignore 191 const data = evt.detail.data as PerfBottomUpStruct; 192 data.isSelected = true; 193 this.bottomUpTable!.clearAllSelection(data); 194 this.bottomUpTable!.scrollToData(data); 195 // @ts-ignore 196 if (evt.detail.callBack) { 197 // @ts-ignore 198 evt.detail.callBack(true); 199 } 200 } 201 202 public connectedCallback(): void { 203 const tableOffsetHeight = 32; 204 const spanHeight = 22; 205 super.connectedCallback(); 206 new ResizeObserver(() => { 207 // @ts-ignore 208 this.bottomUpTable?.shadowRoot.querySelector('.table').style.height = `${ 209 this.parentElement!.clientHeight - tableOffsetHeight 210 }px`; 211 this.bottomUpTable?.reMeauseHeight(); 212 // @ts-ignore 213 this.stackTable?.shadowRoot.querySelector('.table').style.height = `${ 214 this.parentElement!.clientHeight - tableOffsetHeight - spanHeight 215 }px`; 216 this.stackTable?.reMeauseHeight(); 217 }).observe(this.parentElement!); 218 } 219 220 private sortTree(arr: Array<PerfBottomUpStruct>): Array<PerfBottomUpStruct> { 221 const defaultSortType = 0; 222 223 function defaultSort(callTreeLeftData: PerfBottomUpStruct, callTreeRightData: PerfBottomUpStruct): number { 224 return callTreeRightData.totalTime - callTreeLeftData.totalTime; 225 } 226 227 const CallTreeSortArr = arr.sort((callTreeLeftData, callTreeRightData) => { 228 if (this.sortKey === 'selfTime' || this.sortKey === 'selfTimePercent') { 229 if (this.sortType === defaultSortType) { 230 return defaultSort(callTreeLeftData, callTreeRightData); 231 } else if (this.sortType === 1) { 232 return callTreeLeftData.selfTime - callTreeRightData.selfTime; 233 } else { 234 return callTreeRightData.selfTime - callTreeLeftData.selfTime; 235 } 236 } else if (this.sortKey === 'symbolName') { 237 if (this.sortType === defaultSortType) { 238 return defaultSort(callTreeLeftData, callTreeRightData); 239 } else if (this.sortType === 1) { 240 return `${callTreeLeftData.symbolName}`.localeCompare(`${callTreeRightData.symbolName}`); 241 } else { 242 return `${callTreeRightData.symbolName}`.localeCompare(`${callTreeLeftData.symbolName}`); 243 } 244 } else { 245 if (this.sortType === defaultSortType) { 246 return defaultSort(callTreeLeftData, callTreeRightData); 247 } else if (this.sortType === 1) { 248 return callTreeLeftData.totalTime - callTreeRightData.totalTime; 249 } else { 250 return callTreeRightData.totalTime - callTreeLeftData.totalTime; 251 } 252 } 253 }); 254 255 CallTreeSortArr.map((call) => { 256 call.children = this.sortTree(call.children); 257 }); 258 return CallTreeSortArr; 259 } 260 261 private initHtmlStyle(): string { 262 return ` 263 <style> 264 :host{ 265 display: flex; 266 flex-direction: column; 267 padding: 0 10px 0 10px; 268 } 269 .show-bottom-up{ 270 display: flex; 271 flex: 1; 272 } 273 .perf-bottom-up-progress{ 274 bottom: 33px; 275 position: absolute; 276 height: 1px; 277 left: 0; 278 right: 0; 279 } 280 </style> 281 `; 282 } 283 284 public initHtml(): string { 285 return ` 286 ${this.initHtmlStyle()} 287 <div class="perf-bottom-up-content"> 288 <selector id='show_table' class="show-bottom-up"> 289 <lit-slicer style="width:100%"> 290 <div id="left_table" style="width: 65%"> 291 <lit-table id="callTreeTable" style="height: 100%" tree> 292 <lit-table-column width="50%" title="Symbol" data-index="symbolName" key="symbolName" 293 align="flex-start" order retract></lit-table-column> 294 <lit-table-column width="1fr" title="Local" data-index="selfTime" key="selfTime" 295 align="flex-start" order></lit-table-column> 296 <lit-table-column width="1fr" title="%" data-index="selfTimePercent" key="selfTimePercent" 297 align="flex-start" order></lit-table-column> 298 <lit-table-column width="1fr" title="Sample Count" data-index="totalTime" key="totalTime" 299 align="flex-start" order></lit-table-column> 300 <lit-table-column width="1fr" title="%" data-index="totalTimePercent" key="totalTimePercent" 301 align="flex-start" order></lit-table-column> 302 <lit-table-column width="1fr" title="Event Count" data-index="eventCount" key="eventCount" 303 align="flex-start" order></lit-table-column> 304 <lit-table-column width="1fr" title="%" data-index="eventPercent" key="eventPercent" 305 align="flex-start" order></lit-table-column> 306 </lit-table> 307 </div> 308 <lit-slicer-track ></lit-slicer-track> 309 <div class="right" style="flex: 1;display: flex; flex-direction: row;"> 310 <div style="flex: 1;display: block;"> 311 <span slot="head" style="height: 22px">Call Stack</span> 312 <lit-table id="stackTable" style="height: auto;"> 313 <lit-table-column width="50%" title="Symbol" data-index="symbolName" key="symbolName" 314 align="flex-start"></lit-table-column> 315 <lit-table-column width="1fr" title="Sample Count" data-index="totalTime" key="totalTime" 316 align="flex-start" ></lit-table-column> 317 <lit-table-column width="1fr" title="%" data-index="totalTimePercent" key="totalTimePercent" 318 align="flex-start"></lit-table-column> 319 </lit-table> 320 </div> 321 </div> 322 </lit-slicer> 323 </selector> 324 <tab-pane-filter id="filter" input inputLeftText ></tab-pane-filter> 325 <lit-progress-bar class="progress perf-bottom-up-progress"></lit-progress-bar> 326 </div> 327 `; 328 } 329} 330