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 { SelectionParam } from '../../../../bean/BoxSelection'; 18import { TraceRow } from '../../base/TraceRow'; 19import { TraceSheet } from '../../base/TraceSheet'; 20import { Flag } from '../../timer-shaft/Flag'; 21import { SpSystemTrace } from '../../../SpSystemTrace'; 22import { ns2Timestamp, ns2x, Rect } from '../../../../database/ui-worker/ProcedureWorkerCommon'; 23import { ColorUtils } from '../../base/ColorUtils'; 24import { LitPageTable } from '../../../../../base-ui/table/LitPageTable'; 25import { LitProgressBar } from '../../../../../base-ui/progress-bar/LitProgressBar'; 26import { TabPaneHangHtml } from './TabPaneHang.html'; 27import { HangStruct } from '../../../../database/ui-worker/ProcedureWorkerHang'; 28import { queryAllHangs } from '../../../../database/sql/Hang.sql'; 29import { HangType, SpHangChart } from '../../../chart/SpHangChart'; 30import { getTimeString } from '../TabPaneCurrentSelection'; 31 32/// Hangs 框选Tab页1 33@element('tab-hang') 34export class TabPaneHang extends BaseElement { 35 // Elements 36 private spSystemTrace: SpSystemTrace | undefined | null; 37 private traceSheetEl: TraceSheet | undefined | null; 38 private levelFilterInput: HTMLSelectElement | undefined | null; 39 private searchFilterInput: HTMLInputElement | undefined | null; 40 private processFilter: HTMLInputElement | undefined | null; 41 private hangTableTitle: HTMLDivElement | undefined | null; 42 private hangTbl: LitPageTable | undefined | null; 43 44 private tableTimeHandle: (() => void) | undefined; 45 private tableTitleTimeHandle: (() => void) | undefined; 46 private systemHangSource: HangStructInPane[] = []; 47 private filterData: HangStructInPane[] = []; 48 49 private optionLevel: string[] = ['Instant', 'Circumstantial', 'Micro', 'Severe']; 50 private allowTag: Set<string> = new Set(); 51 private progressEL: LitProgressBar | null | undefined; 52 private timeOutId: number | undefined; 53 54 /// 框选时段范围时触发 55 set data(selectionParam: SelectionParam) { 56 if (this.hangTbl) { 57 this.hangTbl.recycleDataSource = []; 58 this.filterData = []; 59 } 60 window.clearTimeout(this.timeOutId); 61 queryAllHangs().then((ret) => { 62 const filter = new Set([...selectionParam.hangMapData.keys()].map(key => key.split(' ').at(-1))); 63 ret = ret.filter(struct => ( 64 filter.has(`${struct.pid ?? 0}`) && 65 ((struct.startNS ?? 0) <= selectionParam.rightNs) && 66 (selectionParam.leftNs <= ((struct.startNS ?? 0) + (struct.dur ?? 0))) 67 )); 68 69 if (ret.length === 0) { 70 this.progressEL!.loading = false; 71 } 72 this.systemHangSource = ret.map(HangStructInPane.new); 73 this.refreshTable(); 74 }); 75 } 76 77 init(): void { 78 this.levelFilterInput = this.shadowRoot?.querySelector<HTMLSelectElement>('#level-filter'); 79 this.hangTableTitle = this.shadowRoot?.querySelector<HTMLDivElement>('#hang-title'); 80 this.searchFilterInput = this.shadowRoot?.querySelector<HTMLInputElement>('#search-filter'); 81 this.processFilter = this.shadowRoot?.querySelector<HTMLInputElement>('#process-filter'); 82 this.spSystemTrace = document.querySelector('body > sp-application')?.shadowRoot?.querySelector<SpSystemTrace>('#sp-system-trace'); 83 this.tableTimeHandle = this.delayedRefresh(this.refreshTable); 84 this.tableTitleTimeHandle = this.delayedRefresh(this.refreshHangsTitle); 85 this.hangTbl = this.shadowRoot?.querySelector<LitPageTable>('#tb-hang'); 86 this.progressEL = this.shadowRoot?.querySelector('.progress') as LitProgressBar; 87 this.hangTbl!.getItemTextColor = (data): string => { 88 const hangData = data as HangStructInPane; 89 return ColorUtils.getHangColor(hangData.type as HangType); 90 }; 91 this.hangTbl!.itemTextHandleMap.set('startNS', (startTs) => { 92 // @ts-ignore 93 return ns2Timestamp(startTs); 94 }); 95 this.hangTbl!.addEventListener('row-hover', (e): void => { 96 // @ts-ignore 97 let data = e.detail.data as HangStructInPane; 98 if (data) { 99 let pointX: number = ns2x( 100 data.startNS || 0, 101 TraceRow.range!.startNS, 102 TraceRow.range!.endNS, 103 TraceRow.range!.totalNS, 104 new Rect(0, 0, TraceRow.FRAME_WIDTH, 0), 105 ); 106 this.traceSheetEl!.systemLogFlag = new Flag( 107 Math.floor(pointX), 0, 0, 0, data.startNS, '#999999', '', true, '', 108 ); 109 this.spSystemTrace?.refreshCanvas(false); 110 } 111 }); 112 let tbl = this.hangTbl?.shadowRoot?.querySelector<HTMLDivElement>('.table'); 113 tbl!.addEventListener('scroll', () => { 114 this.tableTitleTimeHandle?.(); 115 }); 116 } 117 118 initElements(): void { 119 this.init(); 120 this.searchFilterInput!.oninput = (): void => { 121 this.tableTimeHandle?.(); 122 }; 123 this.processFilter!.oninput = (): void => { 124 this.tableTimeHandle?.(); 125 }; 126 this.levelFilterInput!.onchange = (): void => { 127 this.tableTimeHandle?.(); 128 }; 129 } 130 131 connectedCallback(): void { 132 super.connectedCallback(); 133 new ResizeObserver((): void => { 134 this.parentElement!.style.overflow = 'hidden'; 135 if (this.hangTbl) { 136 // @ts-ignore 137 this.hangTbl.shadowRoot.querySelector('.table').style.height = 138 this.parentElement!.clientHeight - 20 - 45 + 'px'; 139 } 140 if (this.filterData.length > 0) { 141 this.refreshTable(); 142 this.tableTitleTimeHandle?.(); 143 } 144 }).observe(this.parentElement!); 145 } 146 147 disconnectedCallback(): void { 148 super.disconnectedCallback(); 149 } 150 151 initHtml(): string { 152 return TabPaneHangHtml; 153 } 154 155 refreshHangTab(): void { 156 let tbl = this.hangTbl?.shadowRoot?.querySelector<HTMLDivElement>('.table'); 157 let height = 0; 158 if (tbl) { 159 const trs = tbl.querySelectorAll<HTMLElement>('.tr'); 160 trs.forEach((trEl: HTMLElement, index: number): void => { 161 if (index === 0) { 162 let frontTotalRowSize = Math.round((tbl!.scrollTop / trEl.clientHeight) * 100) / 100; 163 if (frontTotalRowSize.toString().indexOf('.') >= 0) { 164 let rowCount = frontTotalRowSize.toString().split('.'); 165 height += trEl.clientHeight - (Number(rowCount[1]) / 100) * trEl.clientHeight; 166 } 167 } 168 let allTdEl = trEl.querySelectorAll<HTMLElement>('.td'); 169 allTdEl[0].style.color = '#3D88C7'; 170 allTdEl[0].style.textDecoration = 'underline'; 171 allTdEl[0].style.textDecorationColor = '#3D88C7'; 172 }); 173 } 174 } 175 176 refreshHangsTitle(): void { 177 let tbl = this.hangTbl?.shadowRoot?.querySelector<HTMLDivElement>('.table'); 178 let height = 0; 179 let firstRowHeight = 27; 180 let tableHeadHeight = 26; 181 this.refreshHangTab(); 182 if (this.hangTbl && this.hangTbl.currentRecycleList.length > 0) { 183 let startDataIndex = this.hangTbl.startSkip + 1; 184 let endDataIndex = startDataIndex; 185 let crossTopHeight = tbl!.scrollTop % firstRowHeight; 186 let topShowHeight = crossTopHeight === 0 ? 0 : firstRowHeight - crossTopHeight; 187 if (topShowHeight < firstRowHeight * 0.3) { 188 startDataIndex++; 189 } 190 let tableHeight = Number(tbl!.style.height.replace('px', '')) - tableHeadHeight; 191 while (height < tableHeight) { 192 if (firstRowHeight <= 0 || height + firstRowHeight > tableHeight) { 193 break; 194 } 195 height += firstRowHeight; 196 endDataIndex++; 197 } 198 if (tableHeight - height - topShowHeight > firstRowHeight * 0.3) { 199 endDataIndex++; 200 } 201 if (endDataIndex >= this.filterData.length) { 202 endDataIndex = this.filterData.length; 203 } else { 204 endDataIndex = this.hangTbl.startSkip === 0 ? endDataIndex - 1 : endDataIndex; 205 } 206 this.hangTableTitle!.textContent = `Hangs [${this.hangTbl.startSkip === 0 ? 1 : startDataIndex}, 207 ${endDataIndex}] / ${this.filterData.length || 0}`; 208 } else { 209 this.hangTableTitle!.textContent = 'Hangs [0, 0] / 0'; 210 } 211 if (this.hangTbl!.recycleDataSource.length > 0) { 212 this.progressEL!.loading = false; 213 } 214 } 215 216 initTabSheetEl(traceSheet: TraceSheet): void { 217 this.traceSheetEl = traceSheet; 218 this.levelFilterInput!.selectedIndex = 0; 219 this.allowTag.clear(); 220 this.processFilter!.value = ''; 221 this.searchFilterInput!.value = ''; 222 } 223 224 private updateFilterData(): void { 225 if (this.systemHangSource?.length > 0) { 226 this.filterData = this.systemHangSource.filter((data) => this.isFilterHang(data)); 227 } 228 if (this.hangTbl) { 229 // @ts-ignore 230 this.hangTbl.shadowRoot.querySelector('.table').style.height = this.parentElement.clientHeight - 20 - 45 + 'px'; 231 } 232 if (this.filterData.length > 0) { 233 this.hangTbl!.recycleDataSource = this.filterData; 234 } else { 235 this.hangTbl!.recycleDataSource = []; 236 } 237 this.refreshHangsTitle(); 238 } 239 240 private isFilterHang(data: HangStructInPane): boolean { 241 let type = this.levelFilterInput?.selectedIndex ?? 0; 242 let search = this.searchFilterInput?.value.toLocaleLowerCase() ?? ''; 243 let process = this.processFilter?.value.toLocaleLowerCase() ?? ''; 244 return ( 245 (type === 0 || this.optionLevel.indexOf(data.type) >= type) && 246 (search === '' || data.caller.toLocaleLowerCase().indexOf(search) >= 0) && 247 (process === '' || data.pname.toLocaleLowerCase().indexOf(process) >= 0) 248 ); 249 } 250 251 private refreshTable(): void { 252 if (this.traceSheetEl) { 253 this.traceSheetEl.systemLogFlag = undefined; 254 this.spSystemTrace?.refreshCanvas(false); 255 this.updateFilterData(); 256 } 257 } 258 259 private delayedRefresh(optionFn: Function, dur: number = tableTimeOut): () => void { 260 return (...args: []): void => { 261 window.clearTimeout(this.timeOutId); 262 this.timeOutId = window.setTimeout((): void => { 263 optionFn.apply(this, ...args); 264 }, dur); 265 }; 266 } 267} 268 269let defaultIndex: number = 1; 270let tableTimeOut: number = 50; 271 272export class HangStructInPane { 273 startNS: number = 0; 274 dur: string = '0'; 275 pname: string = 'Process'; 276 type: string; 277 278 sendEventTid: string; 279 sendTime: string; 280 expectHandleTime: string; 281 taskNameId: string; 282 caller: string; 283 284 constructor(parent: HangStruct) { 285 this.startNS = parent.startNS ?? this.startNS; 286 this.dur = getTimeString(parent.dur ?? 0); 287 this.pname = `${parent.pname ?? this.pname} ${parent.pid ?? ''}`.trim(); 288 this.type = SpHangChart.calculateHangType(parent.dur ?? 0); 289 [this.sendEventTid, this.sendTime, this.expectHandleTime, this.taskNameId, this.caller] = (parent.content ?? ',0,0,,').split(',').map(i => i.trim()); 290 this.sendEventTid = this.sendEventTid.split(':').at(-1)!; 291 } 292 293 static new(parent: HangStruct): HangStructInPane { 294 return new HangStructInPane(parent); 295 } 296}