1/* 2 * Copyright (C) 2023 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 { SelectionParam } from '../../../../bean/BoxSelection'; 19import { getTabPaneFrequencySampleData } from '../../../../database/sql/SqlLite.sql'; 20import { getTabPaneCounterSampleData } from '../../../../database/sql/Cpu.sql'; 21import { ColorUtils } from '../../base/ColorUtils'; 22import { resizeObserver } from '../SheetUtils'; 23import { CpuFreqStruct } from '../../../../database/ui-worker/ProcedureWorkerFreq'; 24import { SpSystemTrace } from '../../../SpSystemTrace'; 25import { TraceRow } from '../../base/TraceRow'; 26import { drawLines } from '../../../../database/ui-worker/ProcedureWorkerCommon'; 27 28@element('tabpane-frequency-sample') 29export class TabPaneFrequencySample extends BaseElement { 30 private frequencySampleTbl: LitTable | null | undefined; 31 private selectionParam: SelectionParam | null | undefined; 32 private frequencyLoadingPage: unknown; 33 private frequencySampleSource: unknown[] = []; 34 private frequencySampleSortKey: string = 'counter'; 35 private frequencySampleSortType: number = 0; 36 private systemTrace: SpSystemTrace | undefined | null; // @ts-ignore 37 private _rangeRow: Array<TraceRow<unknown>> | undefined | null; 38 private frequencySampleClickType: boolean = false; 39 private busyTimeLoadingHide: boolean = false; 40 private freqBusyDataList: Array<unknown> = []; 41 private worker: Worker | undefined; 42 private freqResult: Array<unknown> = []; 43 44 set data(frequencySampleValue: SelectionParam | unknown) { 45 if (frequencySampleValue === this.selectionParam) { 46 return; 47 } 48 // @ts-ignore 49 this.selectionParam = frequencySampleValue; 50 if (this.frequencySampleTbl) { 51 // @ts-ignore 52 this.frequencySampleTbl.shadowRoot.querySelector('.table').style.height = `${this.parentElement!.clientHeight - 25}px`; 53 } 54 this.queryDataByDB(frequencySampleValue); 55 } 56 // @ts-ignore 57 set rangeTraceRow(rangeRow: Array<TraceRow<unknown>> | undefined) { 58 this._rangeRow = rangeRow; 59 } 60 61 initElements(): void { 62 this.frequencyLoadingPage = this.shadowRoot!.querySelector('.loadingFre'); 63 this.frequencySampleTbl = this.shadowRoot!.querySelector<LitTable>('#tb-states'); 64 this.systemTrace = document 65 .querySelector('body > sp-application') 66 ?.shadowRoot!.querySelector<SpSystemTrace>('#sp-system-trace'); 67 this.frequencySampleTbl!.addEventListener('column-click', (evt): void => { 68 // @ts-ignore 69 this.frequencySampleSortKey = evt.detail.key; 70 // @ts-ignore 71 this.frequencySampleSortType = evt.detail.sort; 72 // @ts-ignore 73 this.sortTable(evt.detail.key, evt.detail.sort); 74 }); 75 this.frequencySampleTbl!.addEventListener('row-click', (evt): void => { 76 this.clickTblRowEvent(evt); 77 }); 78 this.frequencySampleTbl!.addEventListener('button-click', (evt): void => { 79 //@ts-ignore 80 this.frequencySampleClickKey = evt.detail.key; 81 this.frequencySampleClickType = !this.frequencySampleClickType; 82 //@ts-ignore 83 this.handleClick(evt.detail.key, this.frequencySampleClickType); 84 }); 85 //开启一个线程计算busyTime 86 this.worker = new Worker(new URL('../../../../database/StateBusyTimeWorker', import.meta.url)); 87 } 88 clickTblRowEvent(evt: Event): void { 89 // @ts-ignore 90 let data = evt.detail.data; 91 if (this._rangeRow && this._rangeRow!.length > 0) { 92 let rangeTraceRow = this._rangeRow!.filter(function (item) { 93 return item.name.includes('Frequency'); 94 }); 95 let freqFilter = []; 96 for (let row of rangeTraceRow!) { 97 let context = row.collect ? this.systemTrace!.canvasFavoritePanelCtx! : this.systemTrace!.canvasPanelCtx!; 98 freqFilter.push(...row.dataListCache); 99 row.canvasSave(context); // @ts-ignore 100 context.clearRect(row.frame.x, row.frame.y, row.frame.width, row.frame.height); // @ts-ignore 101 drawLines(context!, TraceRow.range?.xs || [], row.frame.height, this.systemTrace!.timerShaftEL!.lineColor()); 102 if (row.name.includes('Frequency') && parseInt(row.name.replace(/[^\d]/g, ' ')) === data.cpu) { 103 CpuFreqStruct.hoverCpuFreqStruct = undefined; 104 for (let i = 0; i < freqFilter!.length; i++) { 105 if ( 106 // @ts-ignore 107 freqFilter[i].value === data.value && // @ts-ignore 108 freqFilter[i].cpu === data.cpu && // @ts-ignore 109 Math.max(TraceRow.rangeSelectObject?.startNS!, freqFilter[i].startNS!) < // @ts-ignore 110 Math.min(TraceRow.rangeSelectObject?.endNS!, freqFilter[i].startNS! + freqFilter[i].dur!) 111 ) { 112 // @ts-ignore 113 CpuFreqStruct.hoverCpuFreqStruct = freqFilter[i]; 114 } // @ts-ignore 115 if (freqFilter[i].cpu === data.cpu) { 116 // @ts-ignore 117 CpuFreqStruct.draw(context, freqFilter[i]); 118 } 119 } 120 } else { 121 for (let i = 0; i < freqFilter!.length; i++) { 122 if ( 123 row.name.includes('Frequency') && // @ts-ignore 124 freqFilter[i].cpu !== data.cpu && // @ts-ignore 125 freqFilter[i].cpu === parseInt(row.name.replace(/[^\d]/g, ' ')) 126 ) { 127 // @ts-ignore 128 CpuFreqStruct.draw(context, freqFilter[i]); 129 } 130 } 131 } 132 this.metricsText(context, row); 133 } 134 } 135 } 136 // @ts-ignore 137 private metricsText(context: CanvasRenderingContext2D, row: TraceRow<unknown>): void { 138 let s = CpuFreqStruct.maxFreqName; 139 let textMetrics = context.measureText(s); 140 context.globalAlpha = 0.8; 141 context.fillStyle = '#f0f0f0'; 142 context.fillRect(0, 5, textMetrics.width + 8, 18); 143 context.globalAlpha = 1; 144 context.fillStyle = '#333'; 145 context.textBaseline = 'middle'; 146 context.fillText(s, 4, 5 + 9); 147 row.canvasRestore(context, this.systemTrace); 148 } 149 150 connectedCallback(): void { 151 super.connectedCallback(); 152 // @ts-ignore 153 resizeObserver(this.parentElement!, this.frequencySampleTbl!, 25, this.frequencyLoadingPage, 24); 154 } 155 156 async queryDataByDB(frqSampleParam: SelectionParam | unknown): Promise<void> { 157 let sampleMap = new Map<unknown, unknown>(); 158 let weightMap = new Map<unknown, unknown>(); 159 let frqSampleList: unknown[] = []; 160 this.frequencySampleTbl!.loading = true; 161 if (this.frequencySampleClickType) { 162 this.frequencySampleClickType = !this.frequencySampleClickType; 163 } 164 if (this.busyTimeLoadingHide) { 165 this.busyTimeLoadingHide = !this.busyTimeLoadingHide; 166 } 167 let result = await getTabPaneFrequencySampleData( 168 // @ts-ignore 169 frqSampleParam.leftNs + frqSampleParam.recordStartNs, 170 // @ts-ignore 171 frqSampleParam.rightNs + frqSampleParam.recordStartNs, 172 // @ts-ignore 173 frqSampleParam.cpuFreqFilterIds 174 ); 175 this.freqResult = result; 176 // @ts-ignore 177 frqSampleParam.cpuFreqFilterIds.forEach((a: number): void => { 178 weightMap.set(`${a}`, { counter: '', filterId: a }); 179 this.getInitTime( 180 //@ts-ignore 181 result.filter((f) => f.filterId === a), 182 sampleMap, 183 // @ts-ignore 184 frqSampleParam 185 ); 186 }); 187 let tmpCpuArr = Array.from(sampleMap.entries()); 188 let weightMapArr = Array.from(weightMap.entries()); 189 for (let j = 0; j < weightMapArr.length; j++) { 190 // @ts-ignore 191 let singleCpuArr = tmpCpuArr.filter((item) => item[1].filterId && item[1].filterId === Number(weightMapArr[j][1].filterId)); 192 let tmpTotalTime = 0; 193 let tmpTotalCount = 0; 194 for (let i = 0; i < singleCpuArr.length; i++) { 195 // @ts-ignore 196 tmpTotalTime += singleCpuArr[i][1].time; 197 // @ts-ignore 198 tmpTotalCount += singleCpuArr[i][1].time / 1000000 * singleCpuArr[i][1].value; 199 } 200 // @ts-ignore 201 let tmpPosition = tmpCpuArr.findIndex(item => item[1].filterId === weightMapArr[j][1].filterId); 202 if (singleCpuArr[0] && singleCpuArr[0].length >= 2) { 203 // @ts-ignore 204 tmpCpuArr.splice(tmpPosition, 0, [`${weightMapArr[j][1].filterId}-0`, { counter: `${singleCpuArr[0][1].counter}:( WA )`, time: tmpTotalTime, valueStr: ColorUtils.formatNumberComma(Math.round(tmpTotalCount / (tmpTotalTime / 1000000))) }]); 205 } 206 }; 207 sampleMap = new Map(tmpCpuArr); 208 sampleMap.forEach((a): void => { 209 // @ts-ignore 210 if (a.time) { 211 // @ts-ignore 212 a.timeStr = parseFloat((a.time / 1000000.0).toFixed(6)); 213 } 214 frqSampleList.push(a); 215 }); 216 this.frequencySampleSource = frqSampleList; 217 this.frequencySampleTbl!.loading = false; 218 this.sortTable(this.frequencySampleSortKey, this.frequencySampleSortType); 219 // @ts-ignore 220 this.getBusyTimeData(frqSampleParam, sampleMap, result); 221 } 222 223 async getBusyTimeData( 224 frqSampleParam: SelectionParam, 225 sampleMap: Map<unknown, unknown>, 226 result: Array<unknown> 227 ): Promise<void> { 228 let stateFiliterIds: Array<unknown> = []; 229 let cpuFiliterOrder: Array<unknown> = []; 230 //找出框选的cpu fre所对应的cpu state 231 this.freqBusyDataList = []; 232 if (!frqSampleParam.cpuStateRowsId.length) { 233 sampleMap.forEach((value: unknown): void => { 234 // @ts-ignore 235 value.busyTime = 'NULL'; 236 this.freqBusyDataList.push(value); 237 }); 238 } else { 239 frqSampleParam.cpuFreqFilterNames.forEach((item: string): void => { 240 let cpuStateIds: unknown = frqSampleParam.cpuStateRowsId.filter( 241 // @ts-ignore 242 (it: unknown) => it.cpu === Number(item.replace(/[^\d]/g, ' ').trim()) 243 ); 244 // @ts-ignore 245 stateFiliterIds.push(cpuStateIds[0].filterId); 246 // @ts-ignore 247 cpuFiliterOrder.push(cpuStateIds[0].cpu); 248 }); 249 let res = await getTabPaneCounterSampleData( 250 frqSampleParam.leftNs + frqSampleParam.recordStartNs, 251 frqSampleParam.rightNs + frqSampleParam.recordStartNs, 252 // @ts-ignore 253 stateFiliterIds 254 ); 255 let msg = { 256 timeParam: { 257 leftNs: frqSampleParam.leftNs, 258 rightNs: frqSampleParam.rightNs, 259 recordStartNs: frqSampleParam.recordStartNs, 260 }, 261 result, 262 sampleMap, 263 res, 264 cpuFiliterOrder, 265 }; 266 this.worker!.postMessage(msg); 267 this.worker!.onmessage = (event: MessageEvent): void => { 268 sampleMap = event.data; 269 this.freqBusyDataList = [...sampleMap.values()]; 270 this.busyTimeLoadingHide = true; 271 //当busyTimebutton的状态为true但busyTime的计算未完成时 272 if (this.frequencySampleClickType) { 273 this.handleClick(this.frequencySampleSortKey, this.frequencySampleClickType); 274 } 275 }; 276 } 277 } 278 getInitTime(initFreqResult: Array<unknown>, sampleMap: Map<unknown, unknown>, selectionParam: SelectionParam): void { 279 let leftStartNs = selectionParam.leftNs + selectionParam.recordStartNs; 280 let rightEndNs = selectionParam.rightNs + selectionParam.recordStartNs; 281 if (initFreqResult.length === 0) { 282 return; 283 } 284 // @ts-ignore 285 let includeData = initFreqResult.findIndex((a) => a.ts > leftStartNs); 286 if (includeData !== 0) { 287 initFreqResult = initFreqResult.slice( 288 includeData === -1 ? initFreqResult.length - 1 : includeData - 1, 289 initFreqResult.length 290 ); 291 } 292 // @ts-ignore 293 if (initFreqResult[0].ts < leftStartNs && includeData !== 0) { 294 // @ts-ignore 295 initFreqResult[0].ts = leftStartNs; 296 } 297 initFreqResult.forEach((item, idx): void => { 298 if (idx + 1 === initFreqResult.length) { 299 // @ts-ignore 300 item.time = rightEndNs - item.ts; 301 } else { 302 // @ts-ignore 303 item.time = initFreqResult[idx + 1].ts - item.ts; 304 } 305 // @ts-ignore 306 if (sampleMap.has(`${item.filterId}-${item.value}`)) { 307 // @ts-ignore 308 let obj = sampleMap.get(`${item.filterId}-${item.value}`); 309 // @ts-ignore 310 obj.time += item.time; 311 } else { 312 // @ts-ignore 313 sampleMap.set(`${item.filterId}-${item.value}`, { 314 // @ts-ignore 315 ...item, 316 // @ts-ignore 317 counter: `Cpu ${item.cpu}`, 318 // @ts-ignore 319 valueStr: ColorUtils.formatNumberComma(item.value), 320 busyTimeStr: '-', 321 busyTime: 0, 322 }); 323 } 324 }); 325 } 326 327 //点击按钮控制busyTime显示与否 328 handleClick(key: string, type: boolean): void { 329 let res: unknown[] = []; 330 if (this.freqResult.length === 0) { 331 return; 332 } 333 //当busyTime的值计算完毕后进入if判断 334 if (this.busyTimeLoadingHide) { 335 this.busyTimeLoadingHide = false; 336 this.frequencySampleSource = this.freqBusyDataList; 337 this.sortTable(this.frequencySampleSortKey, this.frequencySampleSortType); 338 } 339 this.frequencySampleTbl!.loading = this.freqBusyDataList.length <= 0; 340 if (this.freqBusyDataList.length > 0) { 341 this.frequencySampleTbl!.recycleDataSource.forEach((value): void => { 342 // @ts-ignore 343 if (value.counter.includes('( WA )')) { 344 // @ts-ignore 345 value.busyTimeStr = '-'; 346 } else { 347 // @ts-ignore 348 value.busyTimeStr = type ? value.busyTime : '-'; 349 } 350 res.push(value); 351 }); 352 this.frequencySampleTbl!.recycleDataSource = res; 353 } 354 } 355 356 sortTable(key: string, type: number): void { 357 if (type === 0) { 358 this.frequencySampleTbl!.recycleDataSource = this.frequencySampleSource; 359 } else { 360 let arr = Array.from(this.frequencySampleSource); 361 arr.sort((frequencySampleLeftData, frequencySampleRightData): number => { 362 if (key === 'timeStr') { 363 if (type === 1) { 364 // @ts-ignore 365 return frequencySampleLeftData.time - frequencySampleRightData.time; 366 } else { 367 // @ts-ignore 368 return frequencySampleRightData.time - frequencySampleLeftData.time; 369 } 370 } else if (key === 'counter') { 371 if (type === 1) { 372 // @ts-ignore 373 return frequencySampleLeftData.cpu - frequencySampleRightData.cpu; 374 } else { 375 // @ts-ignore 376 return frequencySampleRightData.cpu - frequencySampleLeftData.cpu; 377 } 378 } else if (key === 'valueStr') { 379 if (type === 1) { 380 // @ts-ignore 381 return frequencySampleLeftData.value - frequencySampleRightData.value; 382 } else { 383 // @ts-ignore 384 return frequencySampleRightData.value - frequencySampleLeftData.value; 385 } 386 } else if (key === 'busyTimeStr') { 387 if (type === 1) { 388 // @ts-ignore 389 return frequencySampleLeftData.busyTimeStr - frequencySampleRightData.busyTimeStr; 390 } else { 391 // @ts-ignore 392 return frequencySampleRightData.busyTimeStr - frequencySampleLeftData.busyTimeStr; 393 } 394 } else { 395 return 0; 396 } 397 }); 398 this.frequencySampleTbl!.recycleDataSource = arr; 399 } 400 } 401 402 initHtml(): string { 403 return ` 404 <style> 405 :host{ 406 display: flex; 407 padding: 10px 10px; 408 flex-direction: column; 409 } 410 </style> 411 <lit-table id="tb-states" style="height: auto" > 412 <lit-table-column class="freq-sample-column" width="20%" title="Cpu" data-index="counter" key="counter" align="flex-start" order> 413 </lit-table-column> 414 <lit-table-column class="freq-sample-column" width="1fr" title="Time(ms)" data-index="timeStr" key="timeStr" align="flex-start" order> 415 </lit-table-column> 416 <lit-table-column class="freq-sample-column" width="1fr" title="Value(kHz)" data-index="valueStr" key="valueStr" align="flex-start" order> 417 </lit-table-column> 418 <lit-table-column class="freq-sample-column" width="1fr" title="" data-index="busyTimeStr" key="busyTimeStr" align="flex-start" order button> 419 </lit-table-column> 420 </lit-table> 421 `; 422 } 423} 424