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 { SpSystemTrace } from '../SpSystemTrace'; 17import { TraceRow } from '../trace/base/TraceRow'; 18import { renders } from '../../database/ui-worker/ProcedureWorker'; 19import { info } from '../../../log/Log'; 20import { XpowerRender, XpowerStruct } from '../../database/ui-worker/ProcedureWorkerXpower'; 21import { XpowerStatisticRender, XpowerStatisticStruct } from '../../database/ui-worker/ProcedureWorkerXpowerStatistic'; 22import { XpowerAppDetailRender, XpowerAppDetailStruct } from '../../database/ui-worker/ProcedureWorkerXpowerAppDetail'; 23import { XpowerWifiRender, XpowerWifiStruct } from '../../database/ui-worker/ProcedureWorkerXpowerWifi'; 24import { ColorUtils } from '../trace/base/ColorUtils'; 25import { EmptyRender } from '../../database/ui-worker/cpu/ProcedureWorkerCPU'; 26import { xpowerDataSender } from '../../database/data-trafic/xpower/XpowerDataSender'; 27import { xpowerStatisticDataSender } from '../../database/data-trafic/xpower/XpowerStatisticDataSender'; 28import { xpowerWifiDataSender } from '../../database/data-trafic/xpower/XpowerWifiDataSender'; 29import { xpowerAppDetailDataSender } from '../../database/data-trafic/xpower/XpowerAppDetailDataSender'; 30import { 31 XpowerThreadCountRender, 32 XpowerThreadCountStruct, 33} from '../../database/ui-worker/ProcedureWorkerXpowerThreadCount'; 34import { 35 XpowerThreadInfoRender, 36 XpowerThreadInfoStruct, 37} from '../../database/ui-worker/ProcedureWorkerXpowerThreadInfo'; 38import { 39 xpowerThreadCountDataSender, 40 xpowerThreadInfoDataSender, 41} from '../../database/data-trafic/xpower/XpowerThreadSender'; 42import { 43 xpowerGpuFreqDataSender, 44 xpowerGpuFreqCountDataSender, 45} from '../../database/data-trafic/xpower/XpowerGpuFrequencySender'; 46import { queryTraceConfig, queryXpowerData, queryXpowerMeasureData, queryFreq } from '../../database/sql/Xpower.sql'; 47import { BaseStruct } from '../../bean/BaseStruct'; 48import { 49 XpowerGpuFreqCountRender, 50 XpowerGpuFreqCountStruct, 51} from '../../database/ui-worker/ProcedureWorkerXpowerGpuFreqCount'; 52import { XpowerGpuFreqStruct, XpowerGpuFreqRender } from '../../database/ui-worker/ProcedureWorkerXpowerGpuFreq'; 53export const THREAD_ENERGY = 'thread_energy'; 54export const THREAD_LOAD = 'thread_loads'; 55const ROW_HEIGHT = 200; 56 57export class SpXpowerChart { 58 private readonly trace: SpSystemTrace; 59 private rowFolder!: TraceRow<BaseStruct>; 60 private systemFolder!: TraceRow<BaseStruct>; 61 private bundleNameFolder!: TraceRow<BaseStruct>; 62 private threadInfoStructMap = new Map<number, XpowerThreadInfoStruct[]>(); 63 private threadEnergyStructMap = new Map<number, XpowerThreadInfoStruct[]>(); 64 private threadLoadStructMap = new Map<number, XpowerThreadInfoStruct[]>(); 65 private gpuFreqStructMap = new Map<number, XpowerGpuFreqStruct[]>(); 66 private valueTypeList: string[] = [THREAD_ENERGY, THREAD_LOAD]; 67 68 constructor(trace: SpSystemTrace) { 69 this.trace = trace; 70 } 71 72 async init(traceId?: string): Promise<void> { 73 let XpowerMeasureData = await queryXpowerMeasureData(traceId); 74 if (XpowerMeasureData.length <= 0) { 75 return; 76 } 77 let xpowerList = await queryXpowerData(traceId); 78 if (xpowerList.length <= 0) { 79 return; 80 } 81 await this.initXpowerFolder(traceId); 82 let systemGroupRowType = TraceRow.ROW_TYPE_XPOWER_SYSTEM_GROUP; 83 await this.initFolder('system', systemGroupRowType, 'System', traceId); 84 await this.initSystemData(this.systemFolder, xpowerList, traceId); 85 let traceConfig = await queryTraceConfig(traceId); 86 let bundleNameRowType = TraceRow.ROW_TYPE_XPOWER_BUNDLE_NAME_GROUP; 87 for (let i = 0; i < traceConfig.length; i++) { 88 if (traceConfig[i].traceSource === 'xpower_config') { 89 await this.initFolder('bundleName', bundleNameRowType, traceConfig[i].value, traceId); 90 await this.initXpowerStatisticData(this.bundleNameFolder, traceId); 91 await this.initXpowerWifiData(this.bundleNameFolder, traceId); 92 await this.initXpowerAppDetatilDisplayData(this.bundleNameFolder, traceId); 93 await this.initGpuFreqCountData(this.bundleNameFolder, traceId); 94 await this.initGpuFreqData(this.bundleNameFolder, traceId); 95 await this.initThreadCountData(this.bundleNameFolder, traceId); 96 for (let value of this.valueTypeList) { 97 await this.initThreadInfoData(this.bundleNameFolder, value, traceId); 98 } 99 break; 100 } 101 } 102 } 103 104 initXpowerFolder = async (traceId?: string): Promise<void> => { 105 let xpowerFolder = TraceRow.skeleton(traceId); 106 xpowerFolder.rowId = 'Xpowers'; 107 xpowerFolder.index = 0; 108 xpowerFolder.rowType = TraceRow.ROW_TYPE_XPOWER; 109 xpowerFolder.rowParentId = ''; 110 xpowerFolder.style.height = '40px'; 111 xpowerFolder.folder = true; 112 xpowerFolder.name = 'Xpower'; 113 xpowerFolder.favoriteChangeHandler = this.trace.favoriteChangeHandler; 114 xpowerFolder.selectChangeHandler = this.trace.selectChangeHandler; 115 xpowerFolder.supplier = (): Promise<BaseStruct[]> => new Promise<Array<BaseStruct>>((resolve) => resolve([])); 116 xpowerFolder.onThreadHandler = (useCache): void => { 117 xpowerFolder.canvasSave(this.trace.canvasPanelCtx!); 118 if (xpowerFolder.expansion) { 119 this.trace.canvasPanelCtx?.clearRect(0, 0, xpowerFolder.frame.width, xpowerFolder.frame.height); 120 } else { 121 (renders.empty as EmptyRender).renderMainThread( 122 { 123 context: this.trace.canvasPanelCtx, 124 useCache: useCache, 125 type: '', 126 }, 127 xpowerFolder 128 ); 129 } 130 xpowerFolder.canvasRestore(this.trace.canvasPanelCtx!, this.trace); 131 }; 132 this.rowFolder = xpowerFolder; 133 this.trace.rowsEL?.appendChild(xpowerFolder); 134 }; 135 136 private initFolder = async (rowId: string, rowType: string, name: string, traceId?: string): Promise<void> => { 137 let folder = TraceRow.skeleton(traceId); 138 folder.rowId = rowId; 139 folder.rowParentId = 'Xpowers'; 140 folder.rowHidden = !this.rowFolder.expansion; 141 folder.rowType = rowType; 142 folder.folder = true; 143 folder.name = name; 144 folder.folderPaddingLeft = 20; 145 folder.style.height = '40px'; 146 folder.favoriteChangeHandler = this.trace.favoriteChangeHandler; 147 folder.selectChangeHandler = this.trace.selectChangeHandler; 148 folder.supplier = (): Promise<BaseStruct[]> => new Promise<Array<BaseStruct>>((resolve) => resolve([])); 149 folder.onThreadHandler = (useCache): void => { 150 folder.canvasSave(this.trace.canvasPanelCtx!); 151 if (folder.expansion) { 152 this.trace.canvasPanelCtx?.clearRect(0, 0, folder.frame.width, folder.frame.height); 153 } else { 154 (renders.empty as EmptyRender).renderMainThread( 155 { 156 context: this.trace.canvasPanelCtx, 157 useCache: useCache, 158 type: '', 159 }, 160 folder 161 ); 162 } 163 folder.canvasRestore(this.trace.canvasPanelCtx!, this.trace); 164 }; 165 if (rowType === TraceRow.ROW_TYPE_XPOWER_SYSTEM_GROUP) { 166 this.systemFolder = folder; 167 } else if (rowType === TraceRow.ROW_TYPE_XPOWER_BUNDLE_NAME_GROUP) { 168 this.bundleNameFolder = folder; 169 } 170 this.rowFolder?.addChildTraceRow(folder); 171 }; 172 173 private xpowerSupplierFrame( 174 traceRow: TraceRow<XpowerStruct>, 175 it: { 176 name: string; 177 num: number; 178 maxValue?: number; 179 minValue?: number; 180 } 181 ): void { 182 traceRow.supplierFrame = (): Promise<XpowerStruct[]> => { 183 let promiseData = null; 184 // @ts-ignore 185 promiseData = xpowerDataSender(it.name, traceRow); 186 if (promiseData === null) { 187 // @ts-ignore 188 return new Promise<Array<unknown>>((resolve) => resolve([])); 189 } else { 190 // @ts-ignore 191 return promiseData.then((resultXpower: Array<unknown>) => { 192 for (let j = 0; j < resultXpower.length; j++) { 193 if (j > 0) { 194 // @ts-ignore 195 resultXpower[j].delta = (resultXpower[j].value || 0) - (resultXpower[j - 1].value || 0); 196 } else { 197 // @ts-ignore 198 resultXpower[j].delta = 0; 199 } 200 } 201 return resultXpower; 202 }); 203 } 204 }; 205 } 206 207 private xpowerThreadHandler( 208 traceRow: TraceRow<XpowerStruct>, 209 it: { 210 name: string; 211 num: number; 212 maxValue?: number; 213 minValue?: number; 214 }, 215 xpowerId: number 216 ): void { 217 traceRow.onThreadHandler = (useCache): void => { 218 let context: CanvasRenderingContext2D; 219 if (traceRow.currentContext) { 220 context = traceRow.currentContext; 221 } else { 222 context = traceRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 223 } 224 traceRow.canvasSave(context); 225 (renders.xpower as XpowerRender).renderMainThread( 226 { 227 context: context, 228 useCache: useCache, 229 type: it.name, 230 maxValue: it.maxValue === 0 ? 1 : it.maxValue!, 231 minValue: it.minValue || 0, 232 index: xpowerId, 233 maxName: it.maxValue!.toString(), 234 }, 235 traceRow 236 ); 237 traceRow.canvasRestore(context, this.trace); 238 }; 239 } 240 241 async initSystemData( 242 folder: TraceRow<BaseStruct>, 243 xpowerList: Array<{ 244 name: string; 245 num: number; 246 maxValue: number; 247 minValue: number; 248 }>, 249 traceId?: string 250 ): Promise<void> { 251 info('xpowerList data size is: ', xpowerList!.length); 252 XpowerStruct.maxValue = xpowerList.map((item) => item.num).reduce((a, b) => Math.max(a, b)); 253 for (let i = 0; i < xpowerList.length; i++) { 254 const it = xpowerList[i]; 255 let traceRow = TraceRow.skeleton<XpowerStruct>(traceId); 256 traceRow.rowId = it.name; 257 traceRow.rowType = TraceRow.ROW_TYPE_XPOWER_SYSTEM; 258 traceRow.rowParentId = folder.rowId; 259 traceRow.style.height = '40px'; 260 traceRow.name = it.name; 261 traceRow.rowHidden = !folder.expansion; 262 traceRow.folderTextLeft = 40; 263 if (it.name === 'ThermalReport.ShellTemp') { 264 it.maxValue = it.maxValue / 1000; 265 } 266 traceRow.xpowerRowTitle = convertTitle(it.name); 267 traceRow.setAttribute('children', ''); 268 traceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 269 traceRow.selectChangeHandler = this.trace.selectChangeHandler; 270 this.xpowerSupplierFrame(traceRow, it); 271 traceRow.getCacheData = (args: unknown): Promise<XpowerStruct[]> | undefined => { 272 let result: Promise<XpowerStruct[]> | undefined; 273 result = xpowerDataSender(it.name, traceRow, args); 274 return result; 275 }; 276 traceRow.focusHandler = (ev): void => { 277 this.trace?.displayTip( 278 traceRow, 279 XpowerStruct.hoverXpowerStruct, 280 `<span>${ 281 it.name === 'ThermalReport.ShellTemp' 282 ? XpowerStruct.hoverXpowerStruct?.value! 283 : it.name === 'ThermalReport.ThermalLevel' 284 ? convertHoverValue(String(XpowerStruct.hoverXpowerStruct?.value!)) 285 : ColorUtils.formatNumberComma(XpowerStruct.hoverXpowerStruct?.value!) 286 }</span>` 287 ); 288 }; 289 traceRow.findHoverStruct = (): void => { 290 XpowerStruct.hoverXpowerStruct = traceRow.getHoverStruct(); 291 }; 292 this.xpowerThreadHandler(traceRow, it, i); 293 folder.addChildTraceRow(traceRow); 294 } 295 } 296 297 async initThreadCountData(folder: TraceRow<BaseStruct>, traceId?: string): Promise<void> { 298 let traceRow = TraceRow.skeleton<XpowerThreadCountStruct>(traceId); 299 traceRow.rowId = 'ThreadCount'; 300 traceRow.rowType = TraceRow.ROW_TYPE_XPOWER_THREAD_COUNT; 301 traceRow.rowParentId = folder.rowId; 302 traceRow.style.height = '40px'; 303 traceRow.name = 'Thread Count'; 304 traceRow.rowHidden = !folder.expansion; 305 traceRow.folderTextLeft = 40; 306 traceRow.xpowerRowTitle = convertTitle(traceRow.name); 307 traceRow.setAttribute('children', ''); 308 traceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 309 traceRow.selectChangeHandler = this.trace.selectChangeHandler; 310 this.xpowerThreadCountSupplierFrame(traceRow); 311 traceRow.getCacheData = (args: unknown): Promise<XpowerThreadCountStruct[]> | undefined => { 312 let result: Promise<XpowerThreadCountStruct[]> | undefined; 313 result = xpowerThreadCountDataSender(traceRow, args); 314 return result; 315 }; 316 traceRow.focusHandler = (ev): void => { 317 this.trace?.displayTip( 318 traceRow, 319 XpowerThreadCountStruct.hoverXpowerStruct, 320 `<span>Count:${XpowerThreadCountStruct.hoverXpowerStruct?.value}</span>` 321 ); 322 }; 323 traceRow.findHoverStruct = (): void => { 324 XpowerThreadCountStruct.hoverXpowerStruct = traceRow.getHoverStruct(); 325 }; 326 traceRow.onThreadHandler = (useCache): void => { 327 let context: CanvasRenderingContext2D; 328 if (traceRow.currentContext) { 329 context = traceRow.currentContext; 330 } else { 331 context = traceRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 332 } 333 traceRow.canvasSave(context); 334 (renders.xpowerThreadCount as XpowerThreadCountRender).renderMainThread( 335 { 336 context: context, 337 useCache: useCache, 338 }, 339 traceRow 340 ); 341 traceRow.canvasRestore(context, this.trace); 342 }; 343 folder.addChildTraceRow(traceRow); 344 } 345 346 private xpowerThreadCountSupplierFrame(traceRow: TraceRow<XpowerThreadCountStruct>): void { 347 traceRow.supplierFrame = (): Promise<XpowerThreadCountStruct[]> => { 348 return xpowerThreadCountDataSender(traceRow).then((resultXpower: Array<XpowerThreadCountStruct>) => { 349 return resultXpower; 350 }); 351 }; 352 } 353 354 async initGpuFreqCountData(folder: TraceRow<BaseStruct>, traceId?: string): Promise<void> { 355 let traceRow = TraceRow.skeleton<XpowerGpuFreqCountStruct>(traceId); 356 traceRow.rowId = 'GpuFreqCount'; 357 traceRow.rowType = TraceRow.ROW_TYPE_XPOWER_GPU_COUNT; 358 traceRow.rowParentId = folder.rowId; 359 traceRow.style.height = '40px'; 360 traceRow.name = 'Gpu Freq Count'; 361 traceRow.rowHidden = !folder.expansion; 362 traceRow.folderTextLeft = 40; 363 traceRow.xpowerRowTitle = convertTitle(traceRow.name); 364 traceRow.setAttribute('children', ''); 365 traceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 366 traceRow.selectChangeHandler = this.trace.selectChangeHandler; 367 this.xpowerGpuFreqCountSupplierFrame(traceRow); 368 traceRow.getCacheData = (args: unknown): Promise<XpowerGpuFreqCountStruct[]> | undefined => { 369 let result: Promise<XpowerGpuFreqCountStruct[]> | undefined; 370 result = xpowerGpuFreqCountDataSender(traceRow, args); 371 return result; 372 }; 373 traceRow.focusHandler = (ev): void => { 374 this.trace?.displayTip( 375 traceRow, 376 XpowerGpuFreqCountStruct.hoverXpowerStruct, 377 `<span>Count:${XpowerGpuFreqCountStruct.hoverXpowerStruct?.value}</span>` 378 ); 379 }; 380 traceRow.findHoverStruct = (): void => { 381 XpowerGpuFreqCountStruct.hoverXpowerStruct = traceRow.getHoverStruct(); 382 }; 383 traceRow.onThreadHandler = (useCache): void => { 384 let context: CanvasRenderingContext2D; 385 if (traceRow.currentContext) { 386 context = traceRow.currentContext; 387 } else { 388 context = traceRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 389 } 390 traceRow.canvasSave(context); 391 (renders.xpowerGpuFreqCount as XpowerGpuFreqCountRender).renderMainThread( 392 { 393 context: context, 394 useCache: useCache, 395 }, 396 traceRow 397 ); 398 traceRow.canvasRestore(context, this.trace); 399 }; 400 folder.addChildTraceRow(traceRow); 401 } 402 403 private xpowerGpuFreqCountSupplierFrame(traceRow: TraceRow<XpowerGpuFreqCountStruct>): void { 404 traceRow.supplierFrame = (): Promise<XpowerGpuFreqCountStruct[]> => { 405 return xpowerGpuFreqCountDataSender(traceRow).then((resultXpower: Array<XpowerGpuFreqCountStruct>) => { 406 return resultXpower; 407 }); 408 }; 409 } 410 411 async initThreadInfoData(folder: TraceRow<BaseStruct>, valueType: string, traceId?: string): Promise<void> { 412 let value = ''; 413 let rowId = ''; 414 let rowName = ''; 415 if (valueType === THREAD_ENERGY) { 416 value = 'Energy'; 417 rowId = 'thread_energy'; 418 rowName = 'Thread Energy'; 419 } else if (valueType === THREAD_LOAD) { 420 value = 'Load'; 421 rowId = 'thread_loads'; 422 rowName = 'Thread Load'; 423 } 424 let traceRow = TraceRow.skeleton<XpowerThreadInfoStruct>(traceId); 425 traceRow.rowId = rowId; 426 traceRow.rowType = TraceRow.ROW_TYPE_XPOWER_THREAD_INFO; 427 traceRow.rowParentId = folder.rowId; 428 traceRow.style.height = `${ROW_HEIGHT}px`; 429 traceRow.name = rowName; 430 traceRow.rowHidden = !folder.expansion; 431 traceRow.folderTextLeft = 40; 432 traceRow.xpowerRowTitle = convertTitle(traceRow.name); 433 traceRow.setAttribute('children', ''); 434 traceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 435 traceRow.selectChangeHandler = this.trace.selectChangeHandler; 436 this.xpowerThreadInfoSupplierFrame(traceRow, valueType); 437 traceRow.getCacheData = (args: unknown): Promise<XpowerThreadInfoStruct[]> | undefined => { 438 let result: Promise<XpowerThreadInfoStruct[]>; 439 result = xpowerThreadInfoDataSender(valueType, traceRow, args); 440 return result.then((resultXpower: Array<XpowerThreadInfoStruct>) => { 441 for (let index = 0; index < resultXpower.length; index++) { 442 let item = resultXpower[index]; 443 item.threadName = SpSystemTrace.DATA_DICT.get(resultXpower[index].threadNameId!) || ''; 444 item.valueType = valueType; 445 } 446 return resultXpower; 447 }); 448 }; 449 traceRow.focusHandler = (ev): void => { 450 this.setTips(traceRow, value); 451 }; 452 traceRow.findHoverStruct = (): void => { 453 XpowerThreadInfoStruct.hoverXpowerStruct = traceRow.getHoverStruct(); 454 }; 455 this.xpowerThreadInfoThreadHandler(traceRow, valueType); 456 folder.addChildTraceRow(traceRow); 457 } 458 459 private xpowerThreadInfoSupplierFrame(traceRow: TraceRow<XpowerThreadInfoStruct>, valueType: string): void { 460 traceRow.supplierFrame = (): Promise<XpowerThreadInfoStruct[]> => { 461 let promiseData = xpowerThreadInfoDataSender(valueType, traceRow); 462 if (promiseData === null) { 463 return new Promise<Array<XpowerThreadInfoStruct>>((resolve) => resolve([])); 464 } else { 465 return promiseData.then((resultXpower: Array<XpowerThreadInfoStruct>) => { 466 for (let index = 0; index < resultXpower.length; index++) { 467 let item = resultXpower[index]; 468 item.threadName = SpSystemTrace.DATA_DICT.get(resultXpower[index].threadNameId!) || ''; 469 item.valueType = valueType; 470 } 471 return this.getThreadInfoDrawData(resultXpower, valueType); 472 }); 473 } 474 }; 475 } 476 477 private setDataMap(resultXpower: Array<XpowerThreadInfoStruct>): Map<number, Array<XpowerThreadInfoStruct>> { 478 let threadInfoStructMap = new Map(); 479 resultXpower.forEach((item: XpowerThreadInfoStruct) => { 480 const startNS = item.startNS; 481 if (threadInfoStructMap.has(startNS)) { 482 const data = threadInfoStructMap.get(startNS)!; 483 data.push(item); 484 } else { 485 const data: XpowerThreadInfoStruct[] = []; 486 data.push(item); 487 threadInfoStructMap.set(startNS, data); 488 } 489 }); 490 return threadInfoStructMap; 491 } 492 493 private getThreadInfoDrawData( 494 resultXpower: Array<XpowerThreadInfoStruct>, 495 valueType: string 496 ): XpowerThreadInfoStruct[] { 497 let newArr: XpowerThreadInfoStruct[] = []; 498 let sumOfRemainingValues = 0; 499 let sumOfRemainingTimes = 0; 500 let maxValue = 0; 501 let itemArraySum = 0; 502 this.threadInfoStructMap = this.setDataMap(resultXpower); 503 for (let itemArray of this.threadInfoStructMap.values()) { 504 itemArray.sort((a, b) => { 505 return b.value - a.value; 506 }); 507 if (itemArray.length > 10) { 508 newArr = JSON.parse(JSON.stringify(itemArray.slice(0, 10))); 509 sumOfRemainingValues = itemArray.slice(9).reduce((acc, obj) => acc + obj.value, 0); 510 sumOfRemainingTimes = itemArray.slice(9).reduce((acc, obj) => acc + obj.threadTime, 0); 511 newArr[9].value = sumOfRemainingValues; 512 newArr[9].threadTime = sumOfRemainingTimes; 513 newArr[9].threadName = 'other'; 514 } else { 515 newArr = JSON.parse(JSON.stringify(itemArray)); 516 } 517 itemArraySum = newArr.reduce((acc, obj) => acc + obj.value, 0); 518 if (itemArraySum > maxValue) { 519 maxValue = itemArraySum; 520 } 521 this.threadInfoStructMap.set(itemArray[0].startNS, newArr); 522 } 523 if (valueType === THREAD_ENERGY) { 524 XpowerThreadInfoStruct.energyMaxValue = maxValue; 525 this.threadEnergyStructMap = this.threadInfoStructMap; 526 XpowerThreadInfoStruct.threadEnergyStructMap = this.setDataMap(resultXpower); 527 } else if (valueType === THREAD_LOAD) { 528 XpowerThreadInfoStruct.loadMaxValue = maxValue; 529 this.threadLoadStructMap = this.threadInfoStructMap; 530 XpowerThreadInfoStruct.threadLoadStructMap = this.setDataMap(resultXpower); 531 } 532 let resultXpowerLit = Array.from(this.threadInfoStructMap.values()).reduce( 533 (acc, valueArray) => acc.concat(valueArray), 534 [] 535 ); 536 //@ts-ignore 537 return resultXpowerLit; 538 } 539 540 private setTips(traceRow: TraceRow<XpowerThreadInfoStruct>, value: string): void { 541 let tipsHtml = ''; 542 if (XpowerThreadInfoStruct.hoverXpowerStruct) { 543 let hoverData: XpowerThreadInfoStruct[] = []; 544 let unit = ''; 545 if (XpowerThreadInfoStruct.hoverXpowerStruct.valueType === THREAD_ENERGY) { 546 hoverData = this.threadEnergyStructMap!.get(XpowerThreadInfoStruct.hoverXpowerStruct.startNS) || []; 547 unit = 'mAh'; 548 } else if (XpowerThreadInfoStruct.hoverXpowerStruct.valueType === THREAD_LOAD) { 549 hoverData = this.threadLoadStructMap!.get(XpowerThreadInfoStruct.hoverXpowerStruct.startNS) || []; 550 unit = '%'; 551 } 552 hoverData = [...hoverData].reverse(); 553 for (let i = 0; i < hoverData.length; i++) { 554 if (hoverData[i].value > 0) { 555 tipsHtml += `<div style=" display: flex; flex-wrap: nowrap; justify-content: space-between;"> 556 <div style=" line-height: 20px; flex-grow: 2; flex-shrink: 1; flex-basis: auto;">Name: ${ 557 hoverData[i].threadName! || '' 558 }</div> 559 <div style="line-height: 20px; flex-grow: 1; flex-shrink: 1; flex-basis: auto;"> Duration: ${ 560 hoverData[i].threadTime! || 0 561 } ms</div> 562 <div style="line-height: 20px; flex-grow: 1; flex-shrink: 1; flex-basis: auto;"> ${value}: ${ 563 hoverData[i].value! || 0 564 } ${unit}</div> 565 </div>`; 566 } 567 } 568 } 569 this.trace?.displayTip(traceRow, XpowerThreadInfoStruct.hoverXpowerStruct, `${tipsHtml}`); 570 } 571 572 private xpowerThreadInfoThreadHandler(traceRow: TraceRow<XpowerThreadInfoStruct>, valueType: string): void { 573 traceRow.onThreadHandler = (useCache): void => { 574 let context: CanvasRenderingContext2D; 575 if (traceRow.currentContext) { 576 context = traceRow.currentContext; 577 } else { 578 context = traceRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 579 } 580 traceRow.canvasSave(context); 581 (renders.xpowerThreadInfo as XpowerThreadInfoRender).renderMainThread( 582 { 583 context: context, 584 useCache: useCache, 585 type: valueType, 586 }, 587 traceRow 588 ); 589 traceRow.canvasRestore(context, this.trace); 590 }; 591 } 592 593 private xpowerGpuFreqSupplierFrame(traceRow: TraceRow<XpowerGpuFreqStruct>): void { 594 traceRow.supplierFrame = (): Promise<XpowerGpuFreqStruct[]> => { 595 let promiseData = xpowerGpuFreqDataSender(traceRow); 596 if (promiseData === null) { 597 return new Promise<Array<XpowerGpuFreqStruct>>((resolve) => resolve([])); 598 } else { 599 return promiseData.then((resultXpower: Array<XpowerGpuFreqStruct>) => { 600 return this.getGpuFreqDrawData(resultXpower); 601 }); 602 } 603 }; 604 } 605 606 private setGpuFreqDataMap(resultXpower: Array<XpowerGpuFreqStruct>): Map<number, Array<XpowerGpuFreqStruct>> { 607 let gpuFreqStructMap = new Map(); 608 resultXpower.forEach((item: XpowerGpuFreqStruct, index: number) => { 609 const startNS = item.startNS; 610 if (gpuFreqStructMap.has(startNS)) { 611 const data = gpuFreqStructMap.get(startNS)!; 612 data.push(item); 613 } else { 614 const data = Array<XpowerGpuFreqStruct>(); 615 data.push(item); 616 gpuFreqStructMap.set(startNS, data); 617 } 618 let hoverHtml = ''; 619 if (item.runTime > 0) { 620 hoverHtml = `<div style=" display: flex; flex-wrap: nowrap; justify-content: space-between;"> 621 <div style="line-height: 20px; flex-grow: 1; flex-shrink: 1; flex-basis: auto;">frequency: ${ 622 item.frequency! || 0 623 }</div> 624 <div style=" line-height: 20px; flex-grow: 2; flex-shrink: 1; flex-basis: auto;"> runTime: ${ 625 item.runTime! || 0 626 } ms</div> 627 <div style="line-height: 20px; flex-grow: 1; flex-shrink: 1; flex-basis: auto;"> idleTime: ${ 628 item.idleTime! || 0 629 } ms</div> 630 </div>`; 631 } 632 item.hoverHtml = hoverHtml; 633 }); 634 return gpuFreqStructMap; 635 } 636 637 private sumTime(arr: Array<XpowerGpuFreqStruct>): Array<XpowerGpuFreqStruct> { 638 return arr.reduce((accumulator: Array<XpowerGpuFreqStruct>, current) => { 639 const { frequency, runTime, idleTime } = current; 640 const existingEntry = accumulator.find((entry) => entry.frequency === frequency); 641 if (existingEntry) { 642 existingEntry.runTime += runTime; 643 existingEntry.idleTime += idleTime; 644 } else { 645 accumulator.push({ ...current }); 646 } 647 return accumulator; 648 }, []); 649 } 650 651 private getGpuFreqDrawData(resultXpower: Array<XpowerGpuFreqStruct>): XpowerGpuFreqStruct[] { 652 this.gpuFreqStructMap = new Map(); 653 this.gpuFreqStructMap = this.setGpuFreqDataMap(resultXpower); 654 for (let itemArray of this.gpuFreqStructMap.values()) { 655 itemArray = this.sumTime(itemArray); 656 itemArray.sort((a, b) => { 657 return b.runTime - a.runTime; 658 }); 659 this.gpuFreqStructMap.set(itemArray[0].startNS, itemArray); 660 } 661 XpowerGpuFreqStruct.gpuFreqStructMap = this.gpuFreqStructMap; 662 let resultXpowerLit = Array.from(this.gpuFreqStructMap.values()).reduce( 663 (acc, valueArray) => acc.concat(valueArray), 664 [] 665 ); 666 return resultXpowerLit; 667 } 668 669 private xpowerStatisticThreadHandler(traceRow: TraceRow<XpowerStatisticStruct>): void { 670 traceRow.onThreadHandler = (useCache): void => { 671 let context: CanvasRenderingContext2D; 672 if (traceRow.currentContext) { 673 context = traceRow.currentContext; 674 } else { 675 context = traceRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 676 } 677 traceRow.canvasSave(context); 678 (renders.xpowerStatistic as XpowerStatisticRender).renderMainThread( 679 { 680 context: context, 681 useCache: useCache, 682 }, 683 traceRow 684 ); 685 traceRow.canvasRestore(context, this.trace); 686 }; 687 } 688 689 async initXpowerStatisticData(folder: TraceRow<BaseStruct>, traceId?: string): Promise<void> { 690 let traceRow = TraceRow.skeleton<XpowerStatisticStruct>(traceId); 691 traceRow.rowId = 'Statistic'; 692 traceRow.rowType = TraceRow.ROW_TYPE_XPOWER_STATISTIC; 693 traceRow.rowParentId = folder.rowId; 694 traceRow.style.height = '200px'; 695 traceRow.setAttribute('height', '200px'); 696 let element = traceRow.shadowRoot?.querySelector('.root') as HTMLDivElement; 697 element!.style.height = '200px'; 698 traceRow.name = 'Statistic'; 699 traceRow.rowHidden = !folder.expansion; 700 traceRow.folderTextLeft = 40; 701 traceRow.xpowerRowTitle = convertTitle(traceRow.name); 702 traceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 703 traceRow.selectChangeHandler = this.trace.selectChangeHandler; 704 traceRow.setAttribute('children', ''); 705 706 let list = [ 707 'audio', 708 'bluetooth', 709 'camera', 710 'cpu', 711 'display', 712 'flashlight', 713 'gpu', 714 'location', 715 'wifiscan', 716 'wifi', 717 'modem', 718 ]; 719 traceRow.rowSettingCheckBoxList = list; 720 traceRow.addRowSettingCheckBox(); 721 traceRow.rowSetting = 'enable'; 722 traceRow.rowSettingPopoverDirection = 'bottomLeft'; 723 traceRow.onRowSettingCheckBoxChangeHandler = (value: boolean[]): void => { 724 this.trace.refreshCanvas(false); 725 }; 726 traceRow.supplierFrame = (): Promise<XpowerStatisticStruct[]> => 727 xpowerStatisticDataSender(traceRow).then((res): XpowerStatisticStruct[] => { 728 res.forEach((item) => { 729 item.typeStr = SpSystemTrace.DATA_DICT.get(item.type)!; 730 }); 731 return res; 732 }); 733 traceRow.getCacheData = (args: unknown): Promise<XpowerStatisticStruct[]> | undefined => { 734 let result: Promise<XpowerStatisticStruct[]> | undefined; 735 result = xpowerStatisticDataSender(traceRow, args); 736 return result; 737 }; 738 traceRow.focusHandler = (ev): void => { 739 this.trace?.displayTip( 740 traceRow, 741 XpowerStatisticStruct.hoverXpowerStruct, 742 XpowerStatisticStruct.hoverXpowerStruct?.hoverHtml! 743 ); 744 }; 745 this.xpowerStatisticThreadHandler(traceRow); 746 folder.addChildTraceRow(traceRow); 747 } 748 749 private xpowerAppDetailThreadHandler(traceRow: TraceRow<XpowerAppDetailStruct>): void { 750 traceRow.onThreadHandler = (useCache): void => { 751 let context: CanvasRenderingContext2D; 752 if (traceRow.currentContext) { 753 context = traceRow.currentContext; 754 } else { 755 context = traceRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 756 } 757 traceRow.canvasSave(context); 758 (renders.xpowerAppDetail as XpowerAppDetailRender).renderMainThread( 759 { 760 context: context, 761 useCache: useCache, 762 }, 763 traceRow 764 ); 765 traceRow.canvasRestore(context, this.trace); 766 }; 767 } 768 769 async initXpowerAppDetatilDisplayData(folder: TraceRow<BaseStruct>, traceId?: string): Promise<void> { 770 let traceRow = TraceRow.skeleton<XpowerAppDetailStruct>(traceId); 771 traceRow.rowId = 'AppDetailDisplay'; 772 traceRow.rowType = TraceRow.ROW_TYPE_XPOWER_APP_DETAIL_DISPLAY; 773 traceRow.rowParentId = folder.rowId; 774 traceRow.style.height = '200px'; 775 traceRow.setAttribute('height', '200px'); 776 let element = traceRow.shadowRoot?.querySelector('.root') as HTMLDivElement; 777 element!.style.height = '200px'; 778 traceRow.name = 'Display'; 779 traceRow.rowHidden = !folder.expansion; 780 traceRow.folderTextLeft = 40; 781 traceRow.xpowerRowTitle = convertTitle(traceRow.name); 782 traceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 783 traceRow.selectChangeHandler = this.trace.selectChangeHandler; 784 traceRow.setAttribute('children', ''); 785 786 let list = ['180hz', '120hz', '90hz', '60hz', '45hz', '30hz', '24hz', '15hz', '10hz', '5hz', '1hz']; 787 traceRow.rowSettingCheckBoxList = list; 788 traceRow.addRowSettingCheckBox(); 789 traceRow.rowSetting = 'enable'; 790 traceRow.rowSettingPopoverDirection = 'bottomLeft'; 791 traceRow.onRowSettingCheckBoxChangeHandler = (value: boolean[]): void => { 792 this.trace.refreshCanvas(false); 793 }; 794 795 traceRow.supplierFrame = (): Promise<XpowerAppDetailStruct[]> => 796 xpowerAppDetailDataSender(traceRow).then((res): XpowerAppDetailStruct[] => { 797 return res; 798 }); 799 traceRow.getCacheData = (args: unknown): Promise<XpowerAppDetailStruct[]> | undefined => { 800 let result: Promise<XpowerAppDetailStruct[]> | undefined; 801 result = xpowerAppDetailDataSender(traceRow, args); 802 return result; 803 }; 804 traceRow.focusHandler = (ev): void => { 805 this.trace?.displayTip( 806 traceRow, 807 XpowerAppDetailStruct.hoverXpowerStruct, 808 XpowerAppDetailStruct.hoverXpowerStruct?.hoverHtml! 809 ); 810 }; 811 this.xpowerAppDetailThreadHandler(traceRow); 812 folder.addChildTraceRow(traceRow); 813 } 814 815 private xpowerWifiThreadHandler(traceRow: TraceRow<XpowerWifiStruct>, name: string): void { 816 traceRow.onThreadHandler = (useCache): void => { 817 let context: CanvasRenderingContext2D; 818 if (traceRow.currentContext) { 819 context = traceRow.currentContext; 820 } else { 821 context = traceRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 822 } 823 traceRow.canvasSave(context); 824 (renders.xpowerWifi as XpowerWifiRender).renderMainThread( 825 { 826 context: context, 827 useCache: useCache, 828 name: name, 829 }, 830 traceRow 831 ); 832 traceRow.canvasRestore(context, this.trace); 833 }; 834 } 835 836 async initXpowerWifiData(folder: TraceRow<BaseStruct>, traceId?: string): Promise<void> { 837 let name = ['WIFIPackets', 'WIFIBytes']; 838 for (let it of name) { 839 let traceRow = TraceRow.skeleton<XpowerWifiStruct>(traceId); 840 traceRow.rowId = it; 841 traceRow.rowType = it === name[0] ? TraceRow.ROW_TYPE_XPOWER_WIFI_PACKETS : TraceRow.ROW_TYPE_XPOWER_WIFI_BYTES; 842 traceRow.rowParentId = folder.rowId; 843 traceRow.style.height = '100px'; 844 traceRow.setAttribute('height', '100px'); 845 let element = traceRow.shadowRoot?.querySelector('.root') as HTMLDivElement; 846 element!.style.height = '100px'; 847 traceRow.name = it; 848 traceRow.rowHidden = !folder.expansion; 849 traceRow.folderTextLeft = 40; 850 traceRow.xpowerRowTitle = convertTitle(traceRow.name); 851 traceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 852 traceRow.selectChangeHandler = this.trace.selectChangeHandler; 853 traceRow.setAttribute('children', ''); 854 traceRow.rowSettingCheckBoxList = ['tx', 'rx']; 855 traceRow.addRowSettingCheckBox(); 856 traceRow.rowSetting = 'enable'; 857 traceRow.rowSettingPopoverDirection = 'bottomLeft'; 858 traceRow.onRowSettingCheckBoxChangeHandler = (value: boolean[]): void => { 859 this.trace.refreshCanvas(false); 860 }; 861 traceRow.supplierFrame = (): Promise<XpowerWifiStruct[]> => 862 xpowerWifiDataSender(traceRow, it).then((res): XpowerWifiStruct[] => { 863 return res; 864 }); 865 traceRow.getCacheData = (args: unknown): Promise<XpowerWifiStruct[]> | undefined => { 866 let result: Promise<XpowerWifiStruct[]> | undefined; 867 result = xpowerWifiDataSender(traceRow, it, args); 868 return result; 869 }; 870 traceRow.focusHandler = (ev): void => { 871 it === 'WIFIPackets' 872 ? this.trace?.displayTip( 873 traceRow, 874 XpowerWifiStruct.hoverPacketsStruct, 875 XpowerWifiStruct.hoverPacketsStruct?.hoverHtmlPackets! 876 ) 877 : this.trace?.displayTip( 878 traceRow, 879 XpowerWifiStruct.hoverBytesStruct, 880 XpowerWifiStruct.hoverBytesStruct?.hoverHtmlBytes! 881 ); 882 }; 883 this.xpowerWifiThreadHandler(traceRow, it); 884 folder.addChildTraceRow(traceRow); 885 } 886 } 887 888 private xpowerGpuFreqThreadHandler(traceRow: TraceRow<XpowerGpuFreqStruct>): void { 889 traceRow.onThreadHandler = (useCache): void => { 890 let context: CanvasRenderingContext2D; 891 if (traceRow.currentContext) { 892 context = traceRow.currentContext; 893 } else { 894 context = traceRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 895 } 896 traceRow.canvasSave(context); 897 (renders.xpowerGpuFreq as XpowerGpuFreqRender).renderMainThread( 898 { 899 context: context, 900 useCache: useCache, 901 }, 902 traceRow 903 ); 904 traceRow.canvasRestore(context, this.trace); 905 }; 906 } 907 908 async initGpuFreqData(folder: TraceRow<BaseStruct>, traceId?: string): Promise<void> { 909 let traceRow = TraceRow.skeleton<XpowerGpuFreqStruct>(traceId); 910 traceRow.rowId = 'gpu-frequency'; 911 traceRow.rowType = TraceRow.ROW_TYPE_XPOWER_GPU_FREQUENCY; 912 traceRow.rowParentId = folder.rowId; 913 traceRow.style.height = `${ROW_HEIGHT}px`; 914 traceRow.name = 'GPU Freq'; 915 traceRow.rowHidden = !folder.expansion; 916 traceRow.folderTextLeft = 40; 917 traceRow.xpowerRowTitle = convertTitle(traceRow.name); 918 traceRow.setAttribute('children', ''); 919 traceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 920 traceRow.selectChangeHandler = this.trace.selectChangeHandler; 921 let freqList = await queryFreq(); 922 // @ts-ignore 923 const values = freqList.map((item) => item.frequency.toString()); 924 let freqSet = new Set(values); 925 traceRow.rowSettingCheckBoxList = [...freqSet]; 926 traceRow.addRowSettingCheckBox(); 927 traceRow.rowSetting = 'enable'; 928 traceRow.rowSettingPopoverDirection = 'bottomLeft'; 929 traceRow.onRowSettingCheckBoxChangeHandler = (value: boolean[]): void => { 930 this.trace.refreshCanvas(false); 931 }; 932 this.xpowerGpuFreqSupplierFrame(traceRow); 933 traceRow.getCacheData = (args: unknown): Promise<XpowerGpuFreqStruct[]> | undefined => { 934 let result: Promise<XpowerGpuFreqStruct[]> | undefined; 935 result = xpowerGpuFreqDataSender(traceRow, args); 936 return result; 937 }; 938 traceRow.focusHandler = (ev): void => { 939 let html = 940 (XpowerGpuFreqStruct.hoverXpowerStruct && 941 XpowerGpuFreqStruct.hoverMap.get(XpowerGpuFreqStruct.hoverXpowerStruct.startNS)) || 942 ''; 943 this.trace?.displayTip(traceRow, XpowerGpuFreqStruct.hoverXpowerStruct, html); 944 }; 945 this.xpowerGpuFreqThreadHandler(traceRow); 946 folder.addChildTraceRow(traceRow); 947 } 948} 949 950// 鼠标悬浮时转换xpower泳道名 951export function convertTitle(title: string): string { 952 switch (title) { 953 case 'Battery.Capacity': 954 return '电池容量(单位mAh)'; 955 case 'Battery.Charge': 956 return '充电状态(充电1,非充电0)'; 957 case 'Battery.GasGauge': 958 return '电池剩余电量(单位mAh)'; 959 case 'Battery.Level': 960 return '电池百分比'; 961 case 'Battery.RealCurrent': 962 return '实时电流(单位mAh,充电时为正数,耗电时为负数)'; 963 case 'Battery.Screen': 964 return '屏幕状态(亮屏1,灭屏0)'; 965 case 'ThermalReport.ShellTemp': 966 return '外壳温度(单位℃)'; 967 case 'ThermalReport.ThermalLevel': 968 return '温度等级'; 969 case 'Statistic': 970 return '器件统计数据'; 971 case 'WIFIPackets': 972 return 'wifi传输数据包个数'; 973 case 'WIFIBytes': 974 return 'wifi字节数'; 975 case 'Display': 976 return '屏幕刷新率'; 977 case 'Gpu Freq Count': 978 return 'GPU频率计数'; 979 case 'GPU Freq': 980 return 'GPU频率时间'; 981 case 'Thread Count': 982 return '时间线程数'; 983 case 'Thread Energy': 984 return '线程能耗'; 985 case 'Thread Load': 986 return '线程负载'; 987 default: 988 return title; 989 } 990} 991 992// 鼠标悬浮ThermalReport.ThermalLevel泳道时转换悬浮框内容 993export function convertHoverValue(value: string): string { 994 switch (value) { 995 case '0': 996 return 'COOL'; 997 case '1': 998 return 'WARM'; 999 case '2': 1000 return 'HOT'; 1001 case '3': 1002 return 'OVERHEATED'; 1003 default: 1004 return value; 1005 } 1006} 1007