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 { LitTable } from '../../../../../base-ui/table/lit-table'; 18import { SelectionData, SelectionParam } from '../../../../bean/BoxSelection'; 19import { initSort, resizeObserver } from '../SheetUtils'; 20import { queryIrqDataBoxSelect, querySoftIrqDataBoxSelect, queryIrqSelectData, querySoftirqSelectData } from '../../../../database/sql/Irq.sql'; 21import { FlagsConfig } from '../../../SpFlags'; 22import { IrqAndSoftirqBean, byCallidGroupBean, finalResultBean } from './irqAndSoftirqBean'; 23 24@element('tabpane-irq-counter') 25export class TabPaneIrqCounter extends BaseElement { 26 private irqCounterTbl: LitTable | null | undefined; 27 private irqRange: HTMLLabelElement | null | undefined; 28 private irqCounterSource: Array<SelectionData> = []; 29 private sortColumn: string = 'wallDurationFormat'; 30 private sortType: number = 2; 31 private loadIrq: boolean = false;//flag开关 32 private irqAndSoftirqSource: Array<finalResultBean> = []; 33 34 set data(irqParam: SelectionParam | unknown) { 35 if (this.irqCounterTbl) { 36 //@ts-ignore 37 this.irqCounterTbl.shadowRoot.querySelector('.table').style.height = `${this.parentElement!.clientHeight - 45}px`; 38 } 39 this.irqRange!.textContent = `Selected range: ${parseFloat( 40 // @ts-ignore 41 ((irqParam.rightNs - irqParam.leftNs) / 1000000.0).toFixed(5) 42 )} ms`; 43 this.irqCounterTbl!.loading = true; 44 let dataSource: Array<SelectionData> = []; 45 this.loadIrq = FlagsConfig.getFlagsConfigEnableStatus('CPU by Irq');//flag开关 46 if (this.loadIrq) {//@ts-ignore 47 let irqCallIds = irqParam.softIrqCallIds.length > 0 ? irqParam.softIrqCallIds : irqParam.irqCallIds; 48 Promise.all([//@ts-ignore 49 queryIrqSelectData(irqCallIds, irqParam.leftNs, irqParam.rightNs),//@ts-ignore 50 querySoftirqSelectData(irqParam.softIrqCallIds, irqParam.leftNs, irqParam.rightNs), 51 ]).then(([irqData, softirqData]) => { 52 this.irqCounterTbl!.loading = false; 53 const resArr = irqData.concat(softirqData); 54 if (resArr != null && resArr.length > 0) {//@ts-ignore 55 let isSelectIrq = irqParam.irqCallIds.length > 0 ? true : false; 56 const cutData: finalResultBean[] = this.groupByCallid(resArr); 57 this.aggregateData(cutData, isSelectIrq);//整合数据 58 } else { 59 this.irqAndSoftirqSource = []; 60 this.irqCounterTbl!.recycleDataSource = this.irqAndSoftirqSource; 61 } 62 }); 63 } else { 64 Promise.all([ 65 // @ts-ignore 66 queryIrqDataBoxSelect(irqParam.irqCallIds, irqParam.leftNs, irqParam.rightNs), // @ts-ignore 67 querySoftIrqDataBoxSelect(irqParam.softIrqCallIds, irqParam.leftNs, irqParam.rightNs), 68 ]).then((resArr) => { 69 this.irqCounterTbl!.loading = false; 70 resArr.forEach((res) => { 71 res.forEach((item) => { 72 let selectData = new SelectionData(); 73 //@ts-ignore 74 selectData.name = item.irqName; 75 //@ts-ignore 76 selectData.cat = item.cat; 77 //@ts-ignore 78 selectData.count = item.count; 79 //@ts-ignore 80 selectData.wallDuration = item.wallDuration; 81 //@ts-ignore 82 selectData.wallDurationFormat = (item.wallDuration / 1000).toFixed(2); 83 //@ts-ignore 84 selectData.maxDuration = item.wallDuration; 85 //@ts-ignore 86 selectData.maxDurationFormat = (item.maxDuration / 1000).toFixed(2); 87 //@ts-ignore 88 selectData.avgDuration = (item.avgDuration / 1000).toFixed(2); 89 dataSource.push(selectData); 90 }); 91 }); 92 initSort(this.irqCounterTbl!, this.sortColumn, this.sortType); 93 this.irqCounterSource = dataSource; 94 this.irqCounterTbl!.recycleDataSource = dataSource; 95 this.sortByColumn(this.sortColumn, this.sortType); 96 }); 97 } 98 } 99 100 initElements(): void { 101 this.irqCounterTbl = this.shadowRoot?.querySelector<LitTable>('#tb-irq-counter'); 102 this.irqRange = this.shadowRoot?.querySelector('#time-range'); 103 this.irqCounterTbl!.addEventListener('column-click', (event) => { 104 if (!this.loadIrq) { 105 // @ts-ignore 106 this.sortByColumn(event.detail.key, event.detail.sort); 107 } else { 108 // @ts-ignore 109 this.reSortByColum(event.detail.key, event.detail.sort); 110 } 111 }); 112 } 113 114 connectedCallback(): void { 115 super.connectedCallback(); 116 resizeObserver(this.parentElement!, this.irqCounterTbl!); 117 } 118 119 initHtml(): string { 120 return ` 121 <style> 122 .irq-counter-label{ 123 font-size: 10pt; 124 } 125 :host{ 126 display: flex; 127 flex-direction: column; 128 padding: 10px 10px; 129 } 130 </style> 131 <label id="time-range" class="irq-counter-label" style="width: 100%;height: 20px;text-align: end;margin-bottom: 5px;">Selected range:0.0 ms</label> 132 <lit-table id="tb-irq-counter" style="height: auto"> 133 <lit-table-column width="30%" title="Name" data-index="name" key="name" align="flex-start" order> 134 </lit-table-column> 135 <lit-table-column width="10%" title="Type" data-index="cat" key="cat" align="flex-start" order> 136 </lit-table-column> 137 <lit-table-column width="1fr" title="Duration(μs)" data-index="wallDurationFormat" key="wallDurationFormat" align="flex-start" order > 138 </lit-table-column> 139 <lit-table-column width="1fr" title="Max Duration(μs)" data-index="maxDurationFormat" key="maxDurationFormat" align="flex-start" order > 140 </lit-table-column> 141 <lit-table-column width="1fr" title="Average Duration(μs)" data-index="avgDuration" key="avgDuration" align="flex-start" order > 142 </lit-table-column> 143 <lit-table-column width="1fr" title="Occurrences" data-index="count" key="count" align="flex-start" order > 144 </lit-table-column> 145 </lit-table> 146 `; 147 } 148 149 sortByColumn(sortColumn: string, sortType: number): void { 150 let key = sortColumn; 151 let type = sortType; 152 let arr = Array.from(this.irqCounterSource); 153 arr.sort((irqCounterLeftData, irqCounterRightData): number => { 154 if (key === 'wallDurationFormat' || type === 0) { 155 return (type === 1 ? 1 : -1) * (irqCounterLeftData.wallDuration - irqCounterRightData.wallDuration); 156 } else if (key === 'count') { 157 return (type === 1 ? 1 : -1) * (parseInt(irqCounterLeftData.count) - parseInt(irqCounterRightData.count)); 158 } else if (key === 'maxDurationFormat') { 159 return (type === 1 ? 1 : -1) * (irqCounterLeftData.maxDuration - irqCounterRightData.maxDuration); 160 } else if (key === 'avgDuration') { 161 const avgDiff = 162 irqCounterLeftData.wallDuration / parseInt(irqCounterLeftData.count) - 163 irqCounterRightData.wallDuration / parseInt(irqCounterRightData.count); 164 return (type === 1 ? 1 : -1) * avgDiff; 165 } else if (key === 'name') { 166 const nameDiff = irqCounterLeftData.name.localeCompare(irqCounterRightData.name); 167 return (type === 2 ? -1 : 1) * nameDiff; 168 } else { 169 return 0; 170 } 171 }); 172 this.irqCounterTbl!.recycleDataSource = arr; 173 } 174 175 //将所有数据按callid重新分组 176 private groupByCallid(data: Array<IrqAndSoftirqBean>): finalResultBean[] { 177 const callidObject: { [callid: number]: byCallidGroupBean } = 178 data.reduce((groups, item) => { 179 const { callid, ...restProps } = item; 180 const newIrqAndSoftirqBean: IrqAndSoftirqBean = { callid, ...restProps }; 181 182 if (!groups[callid]) { 183 groups[callid] = { Callid: [] }; 184 } 185 groups[callid].Callid!.push(newIrqAndSoftirqBean); 186 187 return groups; 188 }, {} as { [callid: number]: byCallidGroupBean }); 189 const cutObj: { [callid: number]: finalResultBean[] } = {}; 190 Object.entries(callidObject).forEach(([callidStr, { Callid }]) => { 191 const callid = Number(callidStr); 192 cutObj[callid] = this.callidByIrq(Callid); 193 }); 194 const cutList: finalResultBean[] = Object.values(cutObj).flat(); 195 return cutList; 196 } 197 198 //具体切割方法 199 private callidByIrq(data: IrqAndSoftirqBean[]): finalResultBean[] { 200 let sourceData = data.sort((a, b) => a.startTime - b.startTime); 201 let waitArr: IrqAndSoftirqBean[] = []; 202 let completedArr: finalResultBean[] = []; 203 let globalTs: number = 0; 204 let index: number = 0; 205 while (index < sourceData.length || waitArr.length > 0) { 206 let minEndTs = Math.min(...waitArr.map((item: IrqAndSoftirqBean) => item.endTime)); 207 let minIndex = waitArr.findIndex((item: IrqAndSoftirqBean) => item.endTime === minEndTs); 208 //当waitArr为空时 209 if (waitArr.length === 0) { 210 globalTs = sourceData[index].startTime; 211 waitArr.push(sourceData[index]); 212 index++; 213 continue; 214 } 215 //当全局Ts等于minEndTs时,只做删除处理 216 if (globalTs === minEndTs) { 217 if (minIndex !== -1) { waitArr.splice(minIndex, 1) }; 218 continue; 219 } 220 let obj: finalResultBean = { 221 cat: '', 222 name: '', 223 wallDuration: 0, 224 count: 0, 225 }; 226 if (index < sourceData.length) { 227 if (sourceData[index].startTime < minEndTs) { 228 if (globalTs === sourceData[index].startTime) { 229 waitArr.push(sourceData[index]); 230 index++; 231 continue; 232 } else { 233 const maxPriorityItem = this.findMaxPriority(waitArr); 234 obj = { 235 cat: maxPriorityItem.cat, 236 name: maxPriorityItem.name, 237 wallDuration: sourceData[index].startTime - globalTs, 238 count: maxPriorityItem.isFirstObject === 1 ? 1 : 0 239 } 240 completedArr.push(obj); 241 maxPriorityItem.isFirstObject = 0; 242 waitArr.push(sourceData[index]); 243 globalTs = sourceData[index].startTime; 244 index++; 245 } 246 } else { 247 const maxPriorityItem = this.findMaxPriority(waitArr); 248 obj = { 249 cat: maxPriorityItem.cat, 250 name: maxPriorityItem.name, 251 wallDuration: minEndTs - globalTs, 252 count: maxPriorityItem.isFirstObject === 1 ? 1 : 0 253 } 254 completedArr.push(obj); 255 maxPriorityItem.isFirstObject = 0; 256 globalTs = minEndTs; 257 if (minIndex !== -1) { waitArr.splice(minIndex, 1) }; 258 } 259 } else { 260 const maxPriorityItem = this.findMaxPriority(waitArr); 261 obj = { 262 cat: maxPriorityItem.cat, 263 name: maxPriorityItem.name, 264 wallDuration: minEndTs - globalTs, 265 count: maxPriorityItem.isFirstObject === 1 ? 1 : 0 266 } 267 completedArr.push(obj); 268 maxPriorityItem.isFirstObject = 0; 269 globalTs = minEndTs; 270 if (minIndex !== -1) { waitArr.splice(minIndex, 1) }; 271 } 272 } 273 return completedArr; 274 } 275 276 private findMaxPriority(arr: IrqAndSoftirqBean[]): IrqAndSoftirqBean { 277 return arr.reduce((maxItem: IrqAndSoftirqBean, currentItem: IrqAndSoftirqBean) => { 278 return maxItem.priority > currentItem.priority ? maxItem : currentItem; 279 }, arr[0]);; 280 } 281 282 // 聚合数据 283 private aggregateData(data: finalResultBean[], isSelectIrq: boolean): void { 284 function groupAndSumDurations(items: finalResultBean[]): finalResultBean[] { 285 const grouped: Record<string, finalResultBean> = items.reduce((acc, item) => { 286 if (item.wallDuration !== 0) { 287 if (item.cat === 'irq' && !isSelectIrq) {//若没有框选irq,则不对其进行处理 288 return acc; 289 } 290 if (!acc[item.name]) { 291 acc[item.name] = { 292 wallDuration: 0, 293 maxDuration: 0, 294 name: item.name, 295 cat: item.cat, 296 count: 0, 297 avgDuration: 0 298 }; 299 } 300 acc[item.name].wallDuration += item.wallDuration; 301 acc[item.name].wallDurationFormat = (acc[item.name].wallDuration / 1000).toFixed(2); 302 acc[item.name].count += item.count; 303 acc[item.name].avgDuration = (acc[item.name].wallDuration / acc[item.name].count / 1000).toFixed(2); 304 if (item.wallDuration > acc[item.name].maxDuration!) { 305 acc[item.name].maxDuration = item.wallDuration; 306 acc[item.name].maxDurationFormat = (acc[item.name].maxDuration! / 1000).toFixed(2); 307 } 308 } 309 return acc; 310 }, {} as Record<string, finalResultBean>); 311 return Object.values(grouped); 312 } 313 this.irqAndSoftirqSource = groupAndSumDurations(data); 314 this.irqCounterTbl!.recycleDataSource = this.irqAndSoftirqSource; 315 } 316 317 private reSortByColum(key: string, type: number): void { 318 // 如果数组为空,则直接返回 319 if (!this.irqAndSoftirqSource.length) return; 320 let sortObject: finalResultBean[] = JSON.parse(JSON.stringify(this.irqAndSoftirqSource)); 321 let sortList: Array<finalResultBean> = []; 322 sortList.push(...sortObject); 323 if (type === 0) { 324 this.irqCounterTbl!.recycleDataSource = this.irqAndSoftirqSource; 325 } else { 326 sortList.sort((a, b) => { 327 let aValue: number | string, bValue: number | string; 328 if (key === 'name' || key === 'cat') { 329 aValue = a[key]; 330 bValue = b[key]; 331 } else { 332 // @ts-ignore 333 aValue = parseFloat(a[key]); 334 // @ts-ignore 335 bValue = parseFloat(b[key]); 336 } 337 if (typeof aValue === 'string' && typeof bValue === 'string') { 338 return type === 1 ? aValue.localeCompare(bValue) : bValue.localeCompare(aValue); 339 } else if (typeof aValue === 'number' && typeof bValue === 'number') { 340 return type === 1 ? aValue - bValue : bValue - aValue; 341 } else { 342 return 0; 343 } 344 }); 345 this.irqCounterTbl!.recycleDataSource = sortList; 346 } 347 } 348} 349