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