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 { 18 LitTable, 19 RedrawTreeForm, 20} from '../../../../../base-ui/table/lit-table'; 21import { SelectionParam } from '../../../../bean/BoxSelection'; 22import '../../../StackBar'; 23import { getTabRunningPercent } from '../../../../database/sql/ProcessThread.sql'; 24import { 25 queryCpuFreqUsageData, 26 queryCpuFreqFilterId, 27} from '../../../../database/sql/Cpu.sql'; 28import { Utils } from '../../base/Utils'; 29import { resizeObserver } from '../SheetUtils'; 30import { SpSegmentationChart } from '../../../chart/SpSegmentationChart'; 31import { 32 type CpuFreqData, 33 type RunningFreqData, 34 type RunningData, 35 type CpuFreqTd, 36} from './TabPaneFreqUsageConfig'; 37import { getCpuData } from '../../../../database/sql/CpuAndIrq.sql'; 38 39@element('tabpane-frequsage') 40export class TabPaneFreqUsage extends BaseElement { 41 private threadStatesTbl: LitTable | null | undefined; 42 private currentSelectionParam: SelectionParam | undefined; 43 private worker: Worker | undefined; 44 static element: TabPaneFreqUsage; 45 private sortConsumpowerFlags: number = 0; 46 private sortConsumptionFlags: number = 0; 47 private sortCpuloadFlags: number = 0; 48 private sortDurFlags: number = 0; 49 private sortPercentFlags: number = 0; 50 51 set data(threadStatesParam: SelectionParam) { 52 if (this.currentSelectionParam === threadStatesParam) { 53 return; 54 } 55 this.currentSelectionParam = threadStatesParam; 56 this.threadStatesTbl!.recycleDataSource = []; 57 // @ts-ignore 58 this.threadStatesTbl.value = []; 59 this.queryAllData(threadStatesParam); 60 } 61 62 static refresh(): void { 63 this.prototype.queryAllData(TabPaneFreqUsage.element.currentSelectionParam!); 64 } 65 66 async queryAllData(threadStatesParam: SelectionParam): Promise<void> { 67 TabPaneFreqUsage.element.threadStatesTbl!.loading = true; 68 let runningResult: Array<RunningData> = await getTabRunningPercent( 69 threadStatesParam.threadIds, 70 threadStatesParam.processIds, 71 threadStatesParam.leftNs, 72 threadStatesParam.rightNs 73 ); 74 // 查询cpu及id信息 75 let cpuIdResult: Array<{ id: number; cpu: number }> = 76 await queryCpuFreqFilterId(); 77 // 以键值对形式将cpu及id进行对应,后续会将频点数据与其对应cpu进行整合 78 let IdMap: Map<number, number> = new Map(); 79 let queryId: Array<number> = []; 80 let cpuArray: Array<number> = []; 81 for (let i = 0; i < cpuIdResult.length; i++) { 82 queryId.push(cpuIdResult[i].id); 83 IdMap.set(cpuIdResult[i].id, cpuIdResult[i].cpu); 84 cpuArray.push(cpuIdResult[i].cpu); 85 } 86 // 通过id去查询频点数据 87 let cpuFreqResult: Array<CpuFreqTd> = await queryCpuFreqUsageData(queryId); 88 let cpuFreqData: Array<CpuFreqData> = []; 89 for (let i of cpuFreqResult) { 90 cpuFreqData.push({ 91 ts: i.startNS + threadStatesParam.recordStartNs, 92 cpu: IdMap.get(i.filter_id)!, 93 value: i.value, 94 dur: i.dur, 95 }); 96 } 97 const cpuData = await getCpuData(cpuArray, threadStatesParam.leftNs, threadStatesParam.rightNs); 98 const LEFT_TIME: number = threadStatesParam.leftNs + threadStatesParam.recordStartNs; 99 const RIGHT_TIME: number = threadStatesParam.rightNs + threadStatesParam.recordStartNs; 100 const comPower = 101 SpSegmentationChart.freqInfoMapData.size > 0 102 ? SpSegmentationChart.freqInfoMapData 103 : undefined; 104 const args = { 105 runData: runningResult, 106 cpuFreqData: cpuFreqData, 107 leftNs: LEFT_TIME, 108 rightNs: RIGHT_TIME, 109 cpuArray: cpuArray, 110 comPower: comPower, 111 broCpuData: cpuData, 112 // @ts-ignore 113 recordStartNS: (window as unknown).recordStartNS 114 }; 115 TabPaneFreqUsage.element.worker!.postMessage(args); 116 TabPaneFreqUsage.element.worker!.onmessage = (event: MessageEvent): void => { 117 let resultArr: Array<RunningFreqData> = event.data; 118 TabPaneFreqUsage.element.fixedDeal(resultArr, threadStatesParam.traceId); 119 TabPaneFreqUsage.element.threadClick(resultArr); 120 TabPaneFreqUsage.element.threadStatesTbl!.recycleDataSource = resultArr; 121 TabPaneFreqUsage.element.threadStatesTbl!.loading = false; 122 }; 123 } 124 125 /** 126 * 表头点击事件 127 */ 128 private threadClick(data: Array<RunningFreqData>): void { 129 let labels = this.threadStatesTbl?.shadowRoot 130 ?.querySelector('.th > .td')! 131 .querySelectorAll('label'); 132 let tabHeads = this.threadStatesTbl?.shadowRoot 133 ?.querySelector('.th')!.querySelectorAll('.td'); 134 if (labels) { 135 for (let i = 0; i < labels.length; i++) { 136 let label = labels[i].innerHTML; 137 labels[i].addEventListener('click', (e) => { 138 if (label.includes('Process') && i === 0) { 139 this.threadStatesTbl!.setStatus(data, false); 140 this.threadStatesTbl!.recycleDs = 141 this.threadStatesTbl!.meauseTreeRowElement( 142 data, 143 RedrawTreeForm.Retract 144 ); 145 } else if (label.includes('Thread') && i === 1) { 146 for (let item of data) { 147 // @ts-ignore 148 item.status = true; 149 if (item.children !== undefined && item.children.length > 0) { 150 this.threadStatesTbl!.setStatus(item.children, false); 151 } 152 } 153 this.threadStatesTbl!.recycleDs = 154 this.threadStatesTbl!.meauseTreeRowElement( 155 data, 156 RedrawTreeForm.Retract 157 ); 158 } else if (label.includes('CPU') && i === 2) { 159 this.threadStatesTbl!.setStatus(data, true); 160 this.threadStatesTbl!.recycleDs = 161 this.threadStatesTbl!.meauseTreeRowElement( 162 data, 163 RedrawTreeForm.Expand 164 ); 165 } 166 }); 167 } 168 } 169 if (tabHeads && tabHeads.length) { 170 this.restoreFlags(); 171 tabHeads.forEach((item) => { 172 // @ts-ignore 173 switch (item.innerText) { 174 case 'Consume(cap*ms)': 175 item.addEventListener('click', () => { this.sortDataTree(data, 'Consume(cap*ms)') }); 176 break; 177 case 'Consume(MHz*ms)': 178 item.addEventListener('click', () => { this.sortDataTree(data, 'Consume(MHz*ms)') }); 179 break; 180 case 'TaskUtil(%)': 181 item.addEventListener('click', () => { this.sortDataTree(data, 'TaskUtil') }); 182 break; 183 case 'Dur(ms)': 184 item.addEventListener('click', () => { this.sortDataTree(data, 'Dur') }); 185 break; 186 case 'Dur\n/All_Dur(%)': 187 item.addEventListener('click', () => { this.sortDataTree(data, 'All_Dur') }); 188 break; 189 } 190 }) 191 } 192 } 193 194 sortDataTree(data: Array<RunningFreqData>, type: string) { 195 this.threadStatesTbl!.recycleDs = this.sortTree(data, type); 196 this.threadStatesTbl!.recycleDs = 197 this.threadStatesTbl!.meauseTreeRowElement( 198 data, 199 RedrawTreeForm.Expand 200 ); 201 switch (type) { 202 case 'Consume(cap*ms)': 203 this.sortConsumptionFlags = 0; 204 this.sortCpuloadFlags = 0; 205 this.sortDurFlags = 0; 206 this.sortPercentFlags = 0; 207 if (this.sortConsumpowerFlags === 2) { 208 this.sortConsumpowerFlags = 0; 209 } else { 210 this.sortConsumpowerFlags++; 211 } 212 break; 213 case 'Consume(MHz*ms)': 214 this.sortConsumpowerFlags = 0; 215 this.sortCpuloadFlags = 0; 216 this.sortDurFlags = 0; 217 this.sortPercentFlags = 0; 218 if (this.sortConsumptionFlags === 2) { 219 this.sortConsumptionFlags = 0; 220 } else { 221 this.sortConsumptionFlags++; 222 } 223 break; 224 case 'TaskUtil': 225 this.sortConsumpowerFlags = 0; 226 this.sortConsumptionFlags = 0; 227 this.sortDurFlags = 0; 228 this.sortPercentFlags = 0; 229 if (this.sortCpuloadFlags === 2) { 230 this.sortCpuloadFlags = 0; 231 } else { 232 this.sortCpuloadFlags++; 233 } 234 break; 235 case 'Dur': 236 this.sortConsumpowerFlags = 0; 237 this.sortConsumptionFlags = 0; 238 this.sortCpuloadFlags = 0; 239 this.sortPercentFlags = 0; 240 if (this.sortDurFlags === 2) { 241 this.sortDurFlags = 0; 242 } else { 243 this.sortDurFlags++; 244 } 245 break; 246 case 'All_Dur': 247 this.sortConsumpowerFlags = 0; 248 this.sortConsumptionFlags = 0; 249 this.sortCpuloadFlags = 0; 250 this.sortDurFlags = 0; 251 if (this.sortPercentFlags === 2) { 252 this.sortPercentFlags = 0; 253 } else { 254 this.sortPercentFlags++; 255 } 256 break; 257 } 258 } 259 260 sortTree(arr: Array<unknown>, type: string): Array<unknown> { 261 if (arr.length > 1) { 262 // @ts-ignore 263 arr = arr.sort((sortArrA, sortArrB): number => { 264 // @ts-ignore 265 if (sortArrA.depth === sortArrB.depth) { 266 switch (type) { 267 case 'Consume(cap*ms)': 268 if (this.sortConsumpowerFlags === 0) { 269 //@ts-ignore 270 return Number(sortArrA.consumpower) - Number(sortArrB.consumpower); 271 } else if (this.sortConsumpowerFlags === 1) { 272 //@ts-ignore 273 return Number(sortArrB.consumpower) - Number(sortArrA.consumpower); 274 } else { 275 //@ts-ignore 276 return Number(sortArrA.cpu) - Number(sortArrB.cpu); 277 } 278 break; 279 case 'Consume(MHz*ms)': 280 if (this.sortConsumptionFlags === 0) { 281 //@ts-ignore 282 return Number(sortArrA.consumption) - Number(sortArrB.consumption); 283 } else if (this.sortConsumptionFlags === 1) { 284 //@ts-ignore 285 return Number(sortArrB.consumption) - Number(sortArrA.consumption); 286 } else { 287 //@ts-ignore 288 return Number(sortArrA.cpu) - Number(sortArrB.cpu); 289 } 290 break; 291 case 'TaskUtil': 292 if (this.sortCpuloadFlags === 0) { 293 //@ts-ignore 294 return Number(sortArrA.cpuload) - Number(sortArrB.cpuload); 295 } else if (this.sortCpuloadFlags === 1) { 296 //@ts-ignore 297 return Number(sortArrB.cpuload) - Number(sortArrA.cpuload); 298 } else { 299 //@ts-ignore 300 return Number(sortArrA.cpu) - Number(sortArrB.cpu); 301 } 302 break; 303 case 'Dur': 304 if (this.sortDurFlags === 0) { 305 //@ts-ignore 306 return Number(sortArrA.dur) - Number(sortArrB.dur); 307 } else if (this.sortDurFlags === 1) { 308 //@ts-ignore 309 return Number(sortArrB.dur) - Number(sortArrA.dur); 310 } else { 311 //@ts-ignore 312 return Number(sortArrA.cpu) - Number(sortArrB.cpu); 313 } 314 break; 315 case 'All_Dur': 316 if (this.sortPercentFlags === 0) { 317 //@ts-ignore 318 return Number(sortArrA.percent) - Number(sortArrB.percent); 319 } else if (this.sortPercentFlags === 1) { 320 //@ts-ignore 321 return Number(sortArrB.percent) - Number(sortArrA.percent); 322 } else { 323 //@ts-ignore 324 return Number(sortArrA.cpu) - Number(sortArrB.cpu); 325 } 326 break; 327 } 328 } 329 }) 330 } 331 arr.map((call: unknown): void => { 332 // @ts-ignore 333 if (call.children && call.children.length > 1 && call.status) { 334 // @ts-ignore 335 call.children = this.sortTree(call.children, type); 336 } 337 }) 338 return arr; 339 } 340 341 restoreFlags() { 342 this.sortConsumpowerFlags = 0; 343 this.sortConsumptionFlags = 0; 344 this.sortCpuloadFlags = 0; 345 this.sortDurFlags = 0; 346 this.sortPercentFlags = 0; 347 } 348 349 350 initElements(): void { 351 this.threadStatesTbl = this.shadowRoot?.querySelector<LitTable>( 352 '#tb-running-percent' 353 ); 354 //开启一个线程计算busyTime 355 this.worker = new Worker( 356 new URL('../../../../database/TabPaneFreqUsageWorker', import.meta.url) 357 ); 358 TabPaneFreqUsage.element = this; 359 } 360 361 connectedCallback(): void { 362 super.connectedCallback(); 363 resizeObserver(this.parentElement!, this.threadStatesTbl!, 20); 364 } 365 366 initHtml(): string { 367 return ` 368 <style> 369 :host{ 370 padding: 10px 10px; 371 display: flex; 372 flex-direction: column; 373 } 374 </style> 375 <lit-table id="tb-running-percent" style="height: auto; overflow-x:auto;" tree> 376 <lit-table-column class="running-percent-column" width="320px" title="Process/Thread/CPU" data-index="thread" key="thread" align="flex-start" retract> 377 </lit-table-column> 378 <lit-table-column class="running-percent-column" width="100px" title="CPU" data-index="cpu" key="cpu" align="flex-start"> 379 </lit-table-column> 380 <lit-table-column class="running-percent-column" width="240px" title="Consume(MHz*ms)" data-index="consumption" key="consumption" align="flex-start"> 381 </lit-table-column> 382 <lit-table-column class="running-percent-column" width="200px" title="Freq(MHz:Cap)" data-index="frequency" key="frequency" align="flex-start"> 383 </lit-table-column> 384 <lit-table-column class="running-percent-column" width="240px" title="Consume(cap*ms)" data-index="consumpower" key="consumpower" align="flex-start"> 385 </lit-table-column> 386 <lit-table-column class="running-percent-column" width="100px" title="TaskUtil(%)" data-index="cpuload" key="cpuload" align="flex-start"> 387 </lit-table-column> 388 <lit-table-column class="running-percent-column" width="200px" title="Dur(ms)" data-index="dur" key="dur" align="flex-start"> 389 </lit-table-column> 390 <lit-table-column class="running-percent-column" width="140px" title="Dur/All_Dur(%)" data-index="percent" key="percent" align="flex-start"> 391 </lit-table-column> 392 </lit-table> 393 `; 394 } 395 396 /** 397 * 递归整理数据小数位 398 */ 399 fixedDeal(arr: Array<RunningFreqData>, traceId?: string | null): void { 400 if (arr == undefined) { 401 return; 402 } 403 const TIME_MUTIPLE: number = 1000000; 404 // KHz->MHz * ns->ms 405 const CONS_MUTIPLE: number = 1000000000; 406 const MIN_PERCENT: number = 2; 407 const MIN_FREQ: number = 3; 408 const MIN_POWER: number = 6; 409 for (let i = 0; i < arr.length; i++) { 410 let trackId: number; 411 // 若存在空位元素则进行删除处理 412 if (arr[i] === undefined) { 413 arr.splice(i, 1); 414 i--; 415 continue; 416 } 417 if (arr[i].thread?.indexOf('P') !== -1) { 418 trackId = Number(arr[i].thread?.slice(1)!); 419 arr[i].thread = `${Utils.getInstance().getProcessMap(traceId).get(trackId) || 'Process'} ${trackId}`; 420 } else if (arr[i].thread === 'summary data') { 421 } else { 422 trackId = Number(arr[i].thread!.split('_')[1]); 423 arr[i].thread = `${Utils.getInstance().getThreadMap(traceId).get(trackId) || 'Thread'} ${trackId}`; 424 } 425 if (arr[i].cpu < 0) { 426 // @ts-ignore 427 arr[i].cpu = ''; 428 } 429 // @ts-ignore 430 if (arr[i].frequency < 0) { 431 arr[i].frequency = ''; 432 } 433 if (!arr[i].cpuload) { 434 // @ts-ignore 435 arr[i].cpuload = '0.000000'; 436 } else { 437 // @ts-ignore 438 arr[i].cpuload = arr[i].cpuload.toFixed(MIN_POWER); 439 } 440 // @ts-ignore 441 arr[i].percent = arr[i].percent.toFixed(MIN_PERCENT); 442 // @ts-ignore 443 arr[i].dur = (arr[i].dur / TIME_MUTIPLE).toFixed(MIN_FREQ); 444 // @ts-ignore 445 arr[i].consumption = (arr[i].consumption / CONS_MUTIPLE).toFixed(MIN_FREQ); 446 // @ts-ignore 447 arr[i].consumpower = (arr[i].consumpower / TIME_MUTIPLE).toFixed(MIN_FREQ); 448 if (arr[i].frequency !== '') { 449 if (arr[i].frequency === 'unknown') { 450 arr[i].frequency = 'unknown'; 451 } else { 452 arr[i].frequency = arr[i].frequency; 453 } 454 } 455 this.fixedDeal(arr[i].children!, traceId); 456 } 457 } 458}