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: any; 33 private frequencySampleSource: any[] = []; 34 private frequencySampleSortKey: string = 'counter'; 35 private frequencySampleSortType: number = 0; 36 private systemTrace: SpSystemTrace | undefined | null; 37 private _rangeRow: Array<TraceRow<any>> | undefined | null; 38 private frequencySampleClickType: boolean = false; 39 private busyTimeLoadingHide: boolean = false; 40 private freqBusyDataList: Array<any> = []; 41 private worker: Worker | undefined; 42 private freqResult: Array<any> = []; 43 44 set data(frequencySampleValue: SelectionParam | any) { 45 if (frequencySampleValue === this.selectionParam) { 46 return; 47 } 48 this.selectionParam = frequencySampleValue; 49 if (this.frequencySampleTbl) { 50 // @ts-ignore 51 this.frequencySampleTbl.shadowRoot.querySelector('.table').style.height = 52 this.parentElement!.clientHeight - 25 + 'px'; 53 } 54 this.queryDataByDB(frequencySampleValue); 55 } 56 57 set rangeTraceRow(rangeRow: Array<TraceRow<any>> | 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) => { 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) => { 76 this.clickTblRowEvent(evt); 77 }); 78 this.frequencySampleTbl!.addEventListener('button-click', (evt) => { 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); 100 context.clearRect(row.frame.x, row.frame.y, row.frame.width, row.frame.height); 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 freqFilter[i].value === data.value && 107 freqFilter[i].cpu === data.cpu && 108 Math.max(TraceRow.rangeSelectObject?.startNS!, freqFilter[i].startNS!) < 109 Math.min(TraceRow.rangeSelectObject?.endNS!, freqFilter[i].startNS! + freqFilter[i].dur!) 110 ) { 111 CpuFreqStruct.hoverCpuFreqStruct = freqFilter[i]; 112 } 113 if (freqFilter[i].cpu === data.cpu) { 114 CpuFreqStruct.draw(context, freqFilter[i]); 115 } 116 } 117 } else { 118 for (let i = 0; i < freqFilter!.length; i++) { 119 if ( 120 row.name.includes('Frequency') && 121 freqFilter[i].cpu !== data.cpu && 122 freqFilter[i].cpu === parseInt(row.name.replace(/[^\d]/g, ' ')) 123 ) { 124 CpuFreqStruct.draw(context, freqFilter[i]); 125 } 126 } 127 } 128 this.metricsText(context, row); 129 } 130 } 131 } 132 133 private metricsText(context: CanvasRenderingContext2D, row: TraceRow<any>): void { 134 let s = CpuFreqStruct.maxFreqName; 135 let textMetrics = context.measureText(s); 136 context.globalAlpha = 0.8; 137 context.fillStyle = '#f0f0f0'; 138 context.fillRect(0, 5, textMetrics.width + 8, 18); 139 context.globalAlpha = 1; 140 context.fillStyle = '#333'; 141 context.textBaseline = 'middle'; 142 context.fillText(s, 4, 5 + 9); 143 row.canvasRestore(context, this.systemTrace); 144 } 145 146 connectedCallback() { 147 super.connectedCallback(); 148 resizeObserver(this.parentElement!, this.frequencySampleTbl!, 25, this.frequencyLoadingPage, 24); 149 } 150 151 async queryDataByDB(frqSampleParam: SelectionParam | any) { 152 let sampleMap = new Map<any, any>(); 153 let frqSampleList = new Array(); 154 this.frequencySampleTbl!.loading = true; 155 if (this.frequencySampleClickType) this.frequencySampleClickType = !this.frequencySampleClickType; 156 if (this.busyTimeLoadingHide) this.busyTimeLoadingHide = !this.busyTimeLoadingHide; 157 let result = await getTabPaneFrequencySampleData( 158 frqSampleParam.leftNs + frqSampleParam.recordStartNs, 159 frqSampleParam.rightNs + frqSampleParam.recordStartNs, 160 frqSampleParam.cpuFreqFilterIds 161 ); 162 this.freqResult = result; 163 frqSampleParam.cpuFreqFilterIds.forEach((a: number) => { 164 this.getInitTime( 165 result.filter((f) => f.filterId === a), 166 sampleMap, 167 frqSampleParam 168 ); 169 }); 170 sampleMap.forEach((a) => { 171 a.timeStr = parseFloat((a.time / 1000000.0).toFixed(6)); 172 frqSampleList.push(a); 173 }); 174 this.frequencySampleSource = frqSampleList; 175 this.frequencySampleTbl!.loading = false; 176 this.sortTable(this.frequencySampleSortKey, this.frequencySampleSortType); 177 this.getBusyTimeData(frqSampleParam, sampleMap, result); 178 } 179 180 async getBusyTimeData(frqSampleParam: SelectionParam, sampleMap: Map<any, any>, result: Array<any>) { 181 let stateFiliterIds: Array<any> = []; 182 let cpuFiliterOrder: Array<any> = []; 183 //找出框选的cpu fre所对应的cpu state 184 this.freqBusyDataList = []; 185 if (!frqSampleParam.cpuStateRowsId.length) { 186 sampleMap.forEach((value: any) => { 187 value.busyTime = 'NULL'; 188 this.freqBusyDataList.push(value); 189 }); 190 } else { 191 frqSampleParam.cpuFreqFilterNames.forEach((item: string) => { 192 let cpuStateIds:any = frqSampleParam.cpuStateRowsId.filter( 193 (it: any) => it.cpu === Number(item.replace(/[^\d]/g, ' ').trim()) 194 ); 195 stateFiliterIds.push(cpuStateIds[0].filterId); 196 cpuFiliterOrder.push(cpuStateIds[0].cpu); 197 }); 198 let res = await getTabPaneCounterSampleData( 199 frqSampleParam.leftNs + frqSampleParam.recordStartNs, 200 frqSampleParam.rightNs + frqSampleParam.recordStartNs, 201 stateFiliterIds 202 ); 203 let msg = { 204 frqSampleParam, 205 result, 206 sampleMap, 207 res, 208 cpuFiliterOrder, 209 }; 210 this.worker!.postMessage(msg); 211 this.worker!.onmessage = (event: MessageEvent) => { 212 sampleMap = event.data; 213 sampleMap.forEach((value) => { 214 this.freqBusyDataList.push(value); 215 }); 216 this.busyTimeLoadingHide = true; 217 //当busyTimebutton的状态为true但busyTime的计算未完成时 218 if (this.frequencySampleClickType) { 219 this.handleClick(this.frequencySampleSortKey, this.frequencySampleClickType); 220 } 221 }; 222 } 223 } 224 getInitTime(initFreqResult: Array<any>, sampleMap: Map<any, any>, selectionParam: SelectionParam) { 225 let leftStartNs = selectionParam.leftNs + selectionParam.recordStartNs; 226 let rightEndNs = selectionParam.rightNs + selectionParam.recordStartNs; 227 if (initFreqResult.length === 0) { return }; 228 let includeData = initFreqResult.findIndex((a) => a.ts >= leftStartNs); 229 if (includeData !== 0) { 230 initFreqResult = initFreqResult.slice( 231 includeData === -1 ? initFreqResult.length - 1 : includeData - 1, 232 initFreqResult.length 233 ); 234 } 235 if (initFreqResult[0].ts < leftStartNs && includeData !== 0) { initFreqResult[0].ts = leftStartNs }; 236 initFreqResult.forEach((item, idx) => { 237 if (idx + 1 === initFreqResult.length) { 238 item.time = rightEndNs - item.ts; 239 } else { 240 item.time = initFreqResult[idx + 1].ts - item.ts; 241 } 242 if (sampleMap.has(item.filterId + '-' + item.value)) { 243 let obj = sampleMap.get(item.filterId + '-' + item.value); 244 obj.time += item.time; 245 } else { 246 sampleMap.set(item.filterId + '-' + item.value, { 247 ...item, 248 counter: 'Cpu ' + item.cpu, 249 valueStr: ColorUtils.formatNumberComma(item.value), 250 busyTimeStr: '-', 251 busyTime: 0, 252 }); 253 } 254 }); 255 } 256 257 //点击按钮控制busyTime显示与否 258 handleClick(key: string, type: boolean) { 259 let res = new Array(); 260 if (this.freqResult.length === 0) { 261 return; 262 } 263 //当busyTime的值计算完毕后进入if判断 264 if (this.busyTimeLoadingHide) { 265 this.busyTimeLoadingHide = false; 266 this.frequencySampleSource = this.freqBusyDataList; 267 this.sortTable(this.frequencySampleSortKey, this.frequencySampleSortType); 268 } 269 this.frequencySampleTbl!.loading = this.freqBusyDataList.length > 0 ? false : true; 270 if (this.freqBusyDataList.length > 0) { 271 this.frequencySampleTbl!.recycleDataSource.forEach((value) => { 272 value.busyTimeStr = type ? value.busyTime : '-'; 273 res.push(value); 274 }); 275 this.frequencySampleTbl!.recycleDataSource = res; 276 } 277 } 278 279 sortTable(key: string, type: number) { 280 if (type === 0) { 281 this.frequencySampleTbl!.recycleDataSource = this.frequencySampleSource; 282 } else { 283 let arr = Array.from(this.frequencySampleSource); 284 arr.sort((frequencySampleLeftData, frequencySampleRightData): number => { 285 if (key === 'timeStr') { 286 if (type === 1) { 287 return frequencySampleLeftData.time - frequencySampleRightData.time; 288 } else { 289 return frequencySampleRightData.time - frequencySampleLeftData.time; 290 } 291 } else if (key === 'counter') { 292 if (frequencySampleLeftData.counter > frequencySampleRightData.counter) { 293 return type === 2 ? -1 : 1; 294 } else if (frequencySampleLeftData.counter === frequencySampleRightData.counter) { 295 return 0; 296 } else { 297 return type === 2 ? 1 : -1; 298 } 299 } else if (key === 'valueStr') { 300 if (type === 1) { 301 return frequencySampleLeftData.value - frequencySampleRightData.value; 302 } else { 303 return frequencySampleRightData.value - frequencySampleLeftData.value; 304 } 305 } else if (key === 'busyTimeStr') { 306 if (type === 1) { 307 return frequencySampleLeftData.busyTimeStr - frequencySampleRightData.busyTimeStr; 308 } else { 309 return frequencySampleRightData.busyTimeStr - frequencySampleLeftData.busyTimeStr; 310 } 311 } else { 312 return 0; 313 } 314 }); 315 this.frequencySampleTbl!.recycleDataSource = arr; 316 } 317 } 318 319 initHtml(): string { 320 return ` 321 <style> 322 :host{ 323 display: flex; 324 padding: 10px 10px; 325 flex-direction: column; 326 } 327 </style> 328 <lit-table id="tb-states" style="height: auto" > 329 <lit-table-column class="freq-sample-column" width="20%" title="Cpu" data-index="counter" key="counter" align="flex-start" order> 330 </lit-table-column> 331 <lit-table-column class="freq-sample-column" width="1fr" title="Time(ms)" data-index="timeStr" key="timeStr" align="flex-start" order> 332 </lit-table-column> 333 <lit-table-column class="freq-sample-column" width="1fr" title="Value(kHz)" data-index="valueStr" key="valueStr" align="flex-start" order> 334 </lit-table-column> 335 <lit-table-column class="freq-sample-column" width="1fr" title="" data-index="busyTimeStr" key="busyTimeStr" align="flex-start" order button> 336 </lit-table-column> 337 </lit-table> 338 `; 339 } 340} 341