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.setAttribute('children', ''); 307 traceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 308 traceRow.selectChangeHandler = this.trace.selectChangeHandler; 309 this.xpowerThreadCountSupplierFrame(traceRow); 310 traceRow.getCacheData = (args: unknown): Promise<XpowerThreadCountStruct[]> | undefined => { 311 let result: Promise<XpowerThreadCountStruct[]> | undefined; 312 result = xpowerThreadCountDataSender(traceRow, args); 313 return result; 314 }; 315 traceRow.focusHandler = (ev): void => { 316 this.trace?.displayTip( 317 traceRow, 318 XpowerThreadCountStruct.hoverXpowerStruct, 319 `<span>Count:${XpowerThreadCountStruct.hoverXpowerStruct?.value}</span>` 320 ); 321 }; 322 traceRow.findHoverStruct = (): void => { 323 XpowerThreadCountStruct.hoverXpowerStruct = traceRow.getHoverStruct(); 324 }; 325 traceRow.onThreadHandler = (useCache): void => { 326 let context: CanvasRenderingContext2D; 327 if (traceRow.currentContext) { 328 context = traceRow.currentContext; 329 } else { 330 context = traceRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 331 } 332 traceRow.canvasSave(context); 333 (renders.xpowerThreadCount as XpowerThreadCountRender).renderMainThread( 334 { 335 context: context, 336 useCache: useCache, 337 }, 338 traceRow 339 ); 340 traceRow.canvasRestore(context, this.trace); 341 }; 342 folder.addChildTraceRow(traceRow); 343 } 344 345 private xpowerThreadCountSupplierFrame(traceRow: TraceRow<XpowerThreadCountStruct>): void { 346 traceRow.supplierFrame = (): Promise<XpowerThreadCountStruct[]> => { 347 return xpowerThreadCountDataSender(traceRow).then((resultXpower: Array<XpowerThreadCountStruct>) => { 348 return resultXpower; 349 }); 350 }; 351 } 352 353 async initGpuFreqCountData(folder: TraceRow<BaseStruct>, traceId?: string): Promise<void> { 354 let traceRow = TraceRow.skeleton<XpowerGpuFreqCountStruct>(traceId); 355 traceRow.rowId = 'GpuFreqCount'; 356 traceRow.rowType = TraceRow.ROW_TYPE_XPOWER_GPU_COUNT; 357 traceRow.rowParentId = folder.rowId; 358 traceRow.style.height = '40px'; 359 traceRow.name = 'Gpu Freq Count'; 360 traceRow.rowHidden = !folder.expansion; 361 traceRow.folderTextLeft = 40; 362 traceRow.setAttribute('children', ''); 363 traceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 364 traceRow.selectChangeHandler = this.trace.selectChangeHandler; 365 this.xpowerGpuFreqCountSupplierFrame(traceRow); 366 traceRow.getCacheData = (args: unknown): Promise<XpowerGpuFreqCountStruct[]> | undefined => { 367 let result: Promise<XpowerGpuFreqCountStruct[]> | undefined; 368 result = xpowerGpuFreqCountDataSender(traceRow, args); 369 return result; 370 }; 371 traceRow.focusHandler = (ev): void => { 372 this.trace?.displayTip( 373 traceRow, 374 XpowerGpuFreqCountStruct.hoverXpowerStruct, 375 `<span>Count:${XpowerGpuFreqCountStruct.hoverXpowerStruct?.value}</span>` 376 ); 377 }; 378 traceRow.findHoverStruct = (): void => { 379 XpowerGpuFreqCountStruct.hoverXpowerStruct = traceRow.getHoverStruct(); 380 }; 381 traceRow.onThreadHandler = (useCache): void => { 382 let context: CanvasRenderingContext2D; 383 if (traceRow.currentContext) { 384 context = traceRow.currentContext; 385 } else { 386 context = traceRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 387 } 388 traceRow.canvasSave(context); 389 (renders.xpowerGpuFreqCount as XpowerGpuFreqCountRender).renderMainThread( 390 { 391 context: context, 392 useCache: useCache, 393 }, 394 traceRow 395 ); 396 traceRow.canvasRestore(context, this.trace); 397 }; 398 folder.addChildTraceRow(traceRow); 399 } 400 401 private xpowerGpuFreqCountSupplierFrame(traceRow: TraceRow<XpowerGpuFreqCountStruct>): void { 402 traceRow.supplierFrame = (): Promise<XpowerGpuFreqCountStruct[]> => { 403 return xpowerGpuFreqCountDataSender(traceRow).then((resultXpower: Array<XpowerGpuFreqCountStruct>) => { 404 return resultXpower; 405 }); 406 }; 407 } 408 409 async initThreadInfoData(folder: TraceRow<BaseStruct>, valueType: string, traceId?: string): Promise<void> { 410 let value = ''; 411 let rowId = ''; 412 let rowName = ''; 413 if (valueType === THREAD_ENERGY) { 414 value = 'Energy'; 415 rowId = 'thread_energy'; 416 rowName = 'Thread Energy'; 417 } else if (valueType === THREAD_LOAD) { 418 value = 'Load'; 419 rowId = 'thread_loads'; 420 rowName = 'Thread Load'; 421 } 422 let traceRow = TraceRow.skeleton<XpowerThreadInfoStruct>(traceId); 423 traceRow.rowId = rowId; 424 traceRow.rowType = TraceRow.ROW_TYPE_XPOWER_THREAD_INFO; 425 traceRow.rowParentId = folder.rowId; 426 traceRow.style.height = `${ROW_HEIGHT}px`; 427 traceRow.name = rowName; 428 traceRow.rowHidden = !folder.expansion; 429 traceRow.folderTextLeft = 40; 430 traceRow.setAttribute('children', ''); 431 traceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 432 traceRow.selectChangeHandler = this.trace.selectChangeHandler; 433 this.xpowerThreadInfoSupplierFrame(traceRow, valueType); 434 traceRow.getCacheData = (args: unknown): Promise<XpowerThreadInfoStruct[]> | undefined => { 435 let result: Promise<XpowerThreadInfoStruct[]>; 436 result = xpowerThreadInfoDataSender(valueType, traceRow, args); 437 return result.then((resultXpower: Array<XpowerThreadInfoStruct>) => { 438 for (let index = 0; index < resultXpower.length; index++) { 439 let item = resultXpower[index]; 440 item.threadName = SpSystemTrace.DATA_DICT.get(resultXpower[index].threadNameId!) || ''; 441 item.valueType = valueType; 442 } 443 return resultXpower; 444 }); 445 }; 446 traceRow.focusHandler = (ev): void => { 447 this.setTips(traceRow, value); 448 }; 449 traceRow.findHoverStruct = (): void => { 450 XpowerThreadInfoStruct.hoverXpowerStruct = traceRow.getHoverStruct(); 451 }; 452 this.xpowerThreadInfoThreadHandler(traceRow, valueType); 453 folder.addChildTraceRow(traceRow); 454 } 455 456 private xpowerThreadInfoSupplierFrame(traceRow: TraceRow<XpowerThreadInfoStruct>, valueType: string): void { 457 traceRow.supplierFrame = (): Promise<XpowerThreadInfoStruct[]> => { 458 let promiseData = xpowerThreadInfoDataSender(valueType, traceRow); 459 if (promiseData === null) { 460 return new Promise<Array<XpowerThreadInfoStruct>>((resolve) => resolve([])); 461 } else { 462 return promiseData.then((resultXpower: Array<XpowerThreadInfoStruct>) => { 463 for (let index = 0; index < resultXpower.length; index++) { 464 let item = resultXpower[index]; 465 item.threadName = SpSystemTrace.DATA_DICT.get(resultXpower[index].threadNameId!) || ''; 466 item.valueType = valueType; 467 } 468 return this.getThreadInfoDrawData(resultXpower, valueType); 469 }); 470 } 471 }; 472 } 473 474 private setDataMap(resultXpower: Array<XpowerThreadInfoStruct>): Map<number, Array<XpowerThreadInfoStruct>> { 475 let threadInfoStructMap = new Map(); 476 resultXpower.forEach((item: XpowerThreadInfoStruct) => { 477 const startNS = item.startNS; 478 if (threadInfoStructMap.has(startNS)) { 479 const data = threadInfoStructMap.get(startNS)!; 480 data.push(item); 481 } else { 482 const data: XpowerThreadInfoStruct[] = []; 483 data.push(item); 484 threadInfoStructMap.set(startNS, data); 485 } 486 }); 487 return threadInfoStructMap; 488 } 489 490 private getThreadInfoDrawData( 491 resultXpower: Array<XpowerThreadInfoStruct>, 492 valueType: string 493 ): XpowerThreadInfoStruct[] { 494 let newArr: XpowerThreadInfoStruct[] = []; 495 let sumOfRemainingValues = 0; 496 let sumOfRemainingTimes = 0; 497 let maxValue = 0; 498 let itemArraySum = 0; 499 this.threadInfoStructMap = this.setDataMap(resultXpower); 500 for (let itemArray of this.threadInfoStructMap.values()) { 501 itemArray.sort((a, b) => { 502 return b.value - a.value; 503 }); 504 if (itemArray.length > 10) { 505 newArr = JSON.parse(JSON.stringify(itemArray.slice(0, 10))); 506 sumOfRemainingValues = itemArray.slice(9).reduce((acc, obj) => acc + obj.value, 0); 507 sumOfRemainingTimes = itemArray.slice(9).reduce((acc, obj) => acc + obj.threadTime, 0); 508 newArr[9].value = sumOfRemainingValues; 509 newArr[9].threadTime = sumOfRemainingTimes; 510 newArr[9].threadName = 'other'; 511 } else { 512 newArr = JSON.parse(JSON.stringify(itemArray)); 513 } 514 itemArraySum = newArr.reduce((acc, obj) => acc + obj.value, 0); 515 if (itemArraySum > maxValue) { 516 maxValue = itemArraySum; 517 } 518 this.threadInfoStructMap.set(itemArray[0].startNS, newArr); 519 } 520 if (valueType === THREAD_ENERGY) { 521 XpowerThreadInfoStruct.energyMaxValue = maxValue; 522 this.threadEnergyStructMap = this.threadInfoStructMap; 523 XpowerThreadInfoStruct.threadEnergyStructMap = this.setDataMap(resultXpower); 524 } else if (valueType === THREAD_LOAD) { 525 XpowerThreadInfoStruct.loadMaxValue = maxValue; 526 this.threadLoadStructMap = this.threadInfoStructMap; 527 XpowerThreadInfoStruct.threadLoadStructMap = this.setDataMap(resultXpower); 528 } 529 let resultXpowerLit = Array.from(this.threadInfoStructMap.values()).reduce( 530 (acc, valueArray) => acc.concat(valueArray), 531 [] 532 ); 533 //@ts-ignore 534 return resultXpowerLit; 535 } 536 537 private setTips(traceRow: TraceRow<XpowerThreadInfoStruct>, value: string): void { 538 let tipsHtml = ''; 539 if (XpowerThreadInfoStruct.hoverXpowerStruct) { 540 let hoverData: XpowerThreadInfoStruct[] = []; 541 let unit = ''; 542 if (XpowerThreadInfoStruct.hoverXpowerStruct.valueType === THREAD_ENERGY) { 543 hoverData = this.threadEnergyStructMap!.get(XpowerThreadInfoStruct.hoverXpowerStruct.startNS) || []; 544 unit = 'mAh'; 545 } else if (XpowerThreadInfoStruct.hoverXpowerStruct.valueType === THREAD_LOAD) { 546 hoverData = this.threadLoadStructMap!.get(XpowerThreadInfoStruct.hoverXpowerStruct.startNS) || []; 547 unit = '%'; 548 } 549 hoverData = [...hoverData].reverse(); 550 for (let i = 0; i < hoverData.length; i++) { 551 if (hoverData[i].value > 0) { 552 tipsHtml += `<div style=" display: flex; flex-wrap: nowrap; justify-content: space-between;"> 553 <div style=" line-height: 20px; flex-grow: 2; flex-shrink: 1; flex-basis: auto;">Name: ${ 554 hoverData[i].threadName! || '' 555 }</div> 556 <div style="line-height: 20px; flex-grow: 1; flex-shrink: 1; flex-basis: auto;"> Duration: ${ 557 hoverData[i].threadTime! || 0 558 } ms</div> 559 <div style="line-height: 20px; flex-grow: 1; flex-shrink: 1; flex-basis: auto;"> ${value}: ${ 560 hoverData[i].value! || 0 561 } ${unit}</div> 562 </div>`; 563 } 564 } 565 } 566 this.trace?.displayTip(traceRow, XpowerThreadInfoStruct.hoverXpowerStruct, `${tipsHtml}`); 567 } 568 569 private xpowerThreadInfoThreadHandler(traceRow: TraceRow<XpowerThreadInfoStruct>, valueType: string): void { 570 traceRow.onThreadHandler = (useCache): void => { 571 let context: CanvasRenderingContext2D; 572 if (traceRow.currentContext) { 573 context = traceRow.currentContext; 574 } else { 575 context = traceRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 576 } 577 traceRow.canvasSave(context); 578 (renders.xpowerThreadInfo as XpowerThreadInfoRender).renderMainThread( 579 { 580 context: context, 581 useCache: useCache, 582 type: valueType, 583 }, 584 traceRow 585 ); 586 traceRow.canvasRestore(context, this.trace); 587 }; 588 } 589 590 private xpowerGpuFreqSupplierFrame(traceRow: TraceRow<XpowerGpuFreqStruct>): void { 591 traceRow.supplierFrame = (): Promise<XpowerGpuFreqStruct[]> => { 592 let promiseData = xpowerGpuFreqDataSender(traceRow); 593 if (promiseData === null) { 594 return new Promise<Array<XpowerGpuFreqStruct>>((resolve) => resolve([])); 595 } else { 596 return promiseData.then((resultXpower: Array<XpowerGpuFreqStruct>) => { 597 return this.getGpuFreqDrawData(resultXpower); 598 }); 599 } 600 }; 601 } 602 603 private setGpuFreqDataMap(resultXpower: Array<XpowerGpuFreqStruct>): Map<number, Array<XpowerGpuFreqStruct>> { 604 let gpuFreqStructMap = new Map(); 605 resultXpower.forEach((item: XpowerGpuFreqStruct, index: number) => { 606 const startNS = item.startNS; 607 if (gpuFreqStructMap.has(startNS)) { 608 const data = gpuFreqStructMap.get(startNS)!; 609 data.push(item); 610 } else { 611 const data = Array<XpowerGpuFreqStruct>(); 612 data.push(item); 613 gpuFreqStructMap.set(startNS, data); 614 } 615 let hoverHtml = ''; 616 if (item.runTime > 0) { 617 hoverHtml = `<div style=" display: flex; flex-wrap: nowrap; justify-content: space-between;"> 618 <div style="line-height: 20px; flex-grow: 1; flex-shrink: 1; flex-basis: auto;">frequency: ${ 619 item.frequency! || 0 620 }</div> 621 <div style=" line-height: 20px; flex-grow: 2; flex-shrink: 1; flex-basis: auto;"> runTime: ${ 622 item.runTime! || 0 623 } ms</div> 624 <div style="line-height: 20px; flex-grow: 1; flex-shrink: 1; flex-basis: auto;"> idleTime: ${ 625 item.idleTime! || 0 626 } ms</div> 627 </div>`; 628 } 629 item.hoverHtml = hoverHtml; 630 }); 631 return gpuFreqStructMap; 632 } 633 634 private sumTime(arr: Array<XpowerGpuFreqStruct>): Array<XpowerGpuFreqStruct> { 635 return arr.reduce((accumulator: Array<XpowerGpuFreqStruct>, current) => { 636 const { frequency, runTime, idleTime } = current; 637 const existingEntry = accumulator.find((entry) => entry.frequency === frequency); 638 if (existingEntry) { 639 existingEntry.runTime += runTime; 640 existingEntry.idleTime += idleTime; 641 } else { 642 accumulator.push({ ...current }); 643 } 644 return accumulator; 645 }, []); 646 } 647 648 private getGpuFreqDrawData(resultXpower: Array<XpowerGpuFreqStruct>): XpowerGpuFreqStruct[] { 649 this.gpuFreqStructMap = new Map(); 650 this.gpuFreqStructMap = this.setGpuFreqDataMap(resultXpower); 651 for (let itemArray of this.gpuFreqStructMap.values()) { 652 itemArray = this.sumTime(itemArray); 653 itemArray.sort((a, b) => { 654 return b.runTime - a.runTime; 655 }); 656 this.gpuFreqStructMap.set(itemArray[0].startNS, itemArray); 657 } 658 XpowerGpuFreqStruct.gpuFreqStructMap = this.gpuFreqStructMap; 659 let resultXpowerLit = Array.from(this.gpuFreqStructMap.values()).reduce( 660 (acc, valueArray) => acc.concat(valueArray), 661 [] 662 ); 663 return resultXpowerLit; 664 } 665 666 private xpowerStatisticThreadHandler(traceRow: TraceRow<XpowerStatisticStruct>): void { 667 traceRow.onThreadHandler = (useCache): void => { 668 let context: CanvasRenderingContext2D; 669 if (traceRow.currentContext) { 670 context = traceRow.currentContext; 671 } else { 672 context = traceRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 673 } 674 traceRow.canvasSave(context); 675 (renders.xpowerStatistic as XpowerStatisticRender).renderMainThread( 676 { 677 context: context, 678 useCache: useCache, 679 }, 680 traceRow 681 ); 682 traceRow.canvasRestore(context, this.trace); 683 }; 684 } 685 686 async initXpowerStatisticData(folder: TraceRow<BaseStruct>, traceId?: string): Promise<void> { 687 let traceRow = TraceRow.skeleton<XpowerStatisticStruct>(traceId); 688 traceRow.rowId = 'Statistic'; 689 traceRow.rowType = TraceRow.ROW_TYPE_XPOWER_STATISTIC; 690 traceRow.rowParentId = folder.rowId; 691 traceRow.style.height = '200px'; 692 traceRow.setAttribute('height', '200px'); 693 let element = traceRow.shadowRoot?.querySelector('.root') as HTMLDivElement; 694 element!.style.height = '200px'; 695 traceRow.name = 'Statistic'; 696 traceRow.rowHidden = !folder.expansion; 697 traceRow.folderTextLeft = 40; 698 traceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 699 traceRow.selectChangeHandler = this.trace.selectChangeHandler; 700 traceRow.setAttribute('children', ''); 701 702 let list = [ 703 'audio', 704 'bluetooth', 705 'camera', 706 'cpu', 707 'display', 708 'flashlight', 709 'gpu', 710 'location', 711 'wifiscan', 712 'wifi', 713 'modem', 714 ]; 715 traceRow.rowSettingCheckBoxList = list; 716 traceRow.addRowSettingCheckBox(); 717 traceRow.rowSetting = 'enable'; 718 traceRow.rowSettingPopoverDirection = 'bottomLeft'; 719 traceRow.onRowSettingCheckBoxChangeHandler = (value: boolean[]): void => { 720 this.trace.refreshCanvas(false); 721 }; 722 traceRow.supplierFrame = (): Promise<XpowerStatisticStruct[]> => 723 xpowerStatisticDataSender(traceRow).then((res): XpowerStatisticStruct[] => { 724 res.forEach((item) => { 725 item.typeStr = SpSystemTrace.DATA_DICT.get(item.type)!; 726 }); 727 return res; 728 }); 729 traceRow.getCacheData = (args: unknown): Promise<XpowerStatisticStruct[]> | undefined => { 730 let result: Promise<XpowerStatisticStruct[]> | undefined; 731 result = xpowerStatisticDataSender(traceRow, args); 732 return result; 733 }; 734 traceRow.focusHandler = (ev): void => { 735 this.trace?.displayTip( 736 traceRow, 737 XpowerStatisticStruct.hoverXpowerStruct, 738 XpowerStatisticStruct.hoverXpowerStruct?.hoverHtml! 739 ); 740 }; 741 this.xpowerStatisticThreadHandler(traceRow); 742 folder.addChildTraceRow(traceRow); 743 } 744 745 private xpowerAppDetailThreadHandler(traceRow: TraceRow<XpowerAppDetailStruct>): void { 746 traceRow.onThreadHandler = (useCache): void => { 747 let context: CanvasRenderingContext2D; 748 if (traceRow.currentContext) { 749 context = traceRow.currentContext; 750 } else { 751 context = traceRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 752 } 753 traceRow.canvasSave(context); 754 (renders.xpowerAppDetail as XpowerAppDetailRender).renderMainThread( 755 { 756 context: context, 757 useCache: useCache, 758 }, 759 traceRow 760 ); 761 traceRow.canvasRestore(context, this.trace); 762 }; 763 } 764 765 async initXpowerAppDetatilDisplayData(folder: TraceRow<BaseStruct>, traceId?: string): Promise<void> { 766 let traceRow = TraceRow.skeleton<XpowerAppDetailStruct>(traceId); 767 traceRow.rowId = 'AppDetailDisplay'; 768 traceRow.rowType = TraceRow.ROW_TYPE_XPOWER_APP_DETAIL_DISPLAY; 769 traceRow.rowParentId = folder.rowId; 770 traceRow.style.height = '200px'; 771 traceRow.setAttribute('height', '200px'); 772 let element = traceRow.shadowRoot?.querySelector('.root') as HTMLDivElement; 773 element!.style.height = '200px'; 774 traceRow.name = 'Display'; 775 traceRow.rowHidden = !folder.expansion; 776 traceRow.folderTextLeft = 40; 777 traceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 778 traceRow.selectChangeHandler = this.trace.selectChangeHandler; 779 traceRow.setAttribute('children', ''); 780 781 let list = ['180hz', '120hz', '90hz', '60hz', '45hz', '30hz', '24hz', '15hz', '10hz', '5hz', '1hz']; 782 traceRow.rowSettingCheckBoxList = list; 783 traceRow.addRowSettingCheckBox(); 784 traceRow.rowSetting = 'enable'; 785 traceRow.rowSettingPopoverDirection = 'bottomLeft'; 786 traceRow.onRowSettingCheckBoxChangeHandler = (value: boolean[]): void => { 787 this.trace.refreshCanvas(false); 788 }; 789 790 traceRow.supplierFrame = (): Promise<XpowerAppDetailStruct[]> => 791 xpowerAppDetailDataSender(traceRow).then((res): XpowerAppDetailStruct[] => { 792 return res; 793 }); 794 traceRow.getCacheData = (args: unknown): Promise<XpowerAppDetailStruct[]> | undefined => { 795 let result: Promise<XpowerAppDetailStruct[]> | undefined; 796 result = xpowerAppDetailDataSender(traceRow, args); 797 return result; 798 }; 799 traceRow.focusHandler = (ev): void => { 800 this.trace?.displayTip( 801 traceRow, 802 XpowerAppDetailStruct.hoverXpowerStruct, 803 XpowerAppDetailStruct.hoverXpowerStruct?.hoverHtml! 804 ); 805 }; 806 this.xpowerAppDetailThreadHandler(traceRow); 807 folder.addChildTraceRow(traceRow); 808 } 809 810 private xpowerWifiThreadHandler(traceRow: TraceRow<XpowerWifiStruct>, name: string): void { 811 traceRow.onThreadHandler = (useCache): void => { 812 let context: CanvasRenderingContext2D; 813 if (traceRow.currentContext) { 814 context = traceRow.currentContext; 815 } else { 816 context = traceRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 817 } 818 traceRow.canvasSave(context); 819 (renders.xpowerWifi as XpowerWifiRender).renderMainThread( 820 { 821 context: context, 822 useCache: useCache, 823 name: name, 824 }, 825 traceRow 826 ); 827 traceRow.canvasRestore(context, this.trace); 828 }; 829 } 830 831 async initXpowerWifiData(folder: TraceRow<BaseStruct>, traceId?: string): Promise<void> { 832 let name = ['WIFIPackets', 'WIFIBytes']; 833 for (let it of name) { 834 let traceRow = TraceRow.skeleton<XpowerWifiStruct>(traceId); 835 traceRow.rowId = it; 836 traceRow.rowType = it === name[0] ? TraceRow.ROW_TYPE_XPOWER_WIFI_PACKETS : TraceRow.ROW_TYPE_XPOWER_WIFI_BYTES; 837 traceRow.rowParentId = folder.rowId; 838 traceRow.style.height = '100px'; 839 traceRow.setAttribute('height', '100px'); 840 let element = traceRow.shadowRoot?.querySelector('.root') as HTMLDivElement; 841 element!.style.height = '100px'; 842 traceRow.name = it; 843 traceRow.rowHidden = !folder.expansion; 844 traceRow.folderTextLeft = 40; 845 traceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 846 traceRow.selectChangeHandler = this.trace.selectChangeHandler; 847 traceRow.setAttribute('children', ''); 848 traceRow.rowSettingCheckBoxList = ['tx', 'rx']; 849 traceRow.addRowSettingCheckBox(); 850 traceRow.rowSetting = 'enable'; 851 traceRow.rowSettingPopoverDirection = 'bottomLeft'; 852 traceRow.onRowSettingCheckBoxChangeHandler = (value: boolean[]): void => { 853 this.trace.refreshCanvas(false); 854 }; 855 traceRow.supplierFrame = (): Promise<XpowerWifiStruct[]> => 856 xpowerWifiDataSender(traceRow, it).then((res): XpowerWifiStruct[] => { 857 return res; 858 }); 859 traceRow.getCacheData = (args: unknown): Promise<XpowerWifiStruct[]> | undefined => { 860 let result: Promise<XpowerWifiStruct[]> | undefined; 861 result = xpowerWifiDataSender(traceRow, it, args); 862 return result; 863 }; 864 traceRow.focusHandler = (ev): void => { 865 it === 'WIFIPackets' 866 ? this.trace?.displayTip( 867 traceRow, 868 XpowerWifiStruct.hoverPacketsStruct, 869 XpowerWifiStruct.hoverPacketsStruct?.hoverHtmlPackets! 870 ) 871 : this.trace?.displayTip( 872 traceRow, 873 XpowerWifiStruct.hoverBytesStruct, 874 XpowerWifiStruct.hoverBytesStruct?.hoverHtmlBytes! 875 ); 876 }; 877 this.xpowerWifiThreadHandler(traceRow, it); 878 folder.addChildTraceRow(traceRow); 879 } 880 } 881 882 private xpowerGpuFreqThreadHandler(traceRow: TraceRow<XpowerGpuFreqStruct>): void { 883 traceRow.onThreadHandler = (useCache): void => { 884 let context: CanvasRenderingContext2D; 885 if (traceRow.currentContext) { 886 context = traceRow.currentContext; 887 } else { 888 context = traceRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 889 } 890 traceRow.canvasSave(context); 891 (renders.xpowerGpuFreq as XpowerGpuFreqRender).renderMainThread( 892 { 893 context: context, 894 useCache: useCache, 895 }, 896 traceRow 897 ); 898 traceRow.canvasRestore(context, this.trace); 899 }; 900 } 901 902 async initGpuFreqData(folder: TraceRow<BaseStruct>, traceId?: string): Promise<void> { 903 let traceRow = TraceRow.skeleton<XpowerGpuFreqStruct>(traceId); 904 traceRow.rowId = 'gpu-frequency'; 905 traceRow.rowType = TraceRow.ROW_TYPE_XPOWER_GPU_FREQUENCY; 906 traceRow.rowParentId = folder.rowId; 907 traceRow.style.height = `${ROW_HEIGHT}px`; 908 traceRow.name = 'GPU Freq'; 909 traceRow.rowHidden = !folder.expansion; 910 traceRow.folderTextLeft = 40; 911 traceRow.setAttribute('children', ''); 912 traceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 913 traceRow.selectChangeHandler = this.trace.selectChangeHandler; 914 let freqList = await queryFreq(); 915 // @ts-ignore 916 const values = freqList.map((item) => item.frequency.toString()); 917 let freqSet = new Set(values); 918 traceRow.rowSettingCheckBoxList = [...freqSet]; 919 traceRow.addRowSettingCheckBox(); 920 traceRow.rowSetting = 'enable'; 921 traceRow.rowSettingPopoverDirection = 'bottomLeft'; 922 traceRow.onRowSettingCheckBoxChangeHandler = (value: boolean[]): void => { 923 this.trace.refreshCanvas(false); 924 }; 925 this.xpowerGpuFreqSupplierFrame(traceRow); 926 traceRow.getCacheData = (args: unknown): Promise<XpowerGpuFreqStruct[]> | undefined => { 927 let result: Promise<XpowerGpuFreqStruct[]> | undefined; 928 result = xpowerGpuFreqDataSender(traceRow, args); 929 return result; 930 }; 931 traceRow.focusHandler = (ev): void => { 932 let html = 933 (XpowerGpuFreqStruct.hoverXpowerStruct && 934 XpowerGpuFreqStruct.hoverMap.get(XpowerGpuFreqStruct.hoverXpowerStruct.startNS)) || 935 ''; 936 this.trace?.displayTip(traceRow, XpowerGpuFreqStruct.hoverXpowerStruct, html); 937 }; 938 this.xpowerGpuFreqThreadHandler(traceRow); 939 folder.addChildTraceRow(traceRow); 940 } 941} 942 943// 鼠标悬浮时转换xpower泳道名 944export function convertTitle(title: string): string { 945 switch (title) { 946 case 'Battery.Capacity': 947 return '电池容量(单位mAh)'; 948 case 'Battery.Charge': 949 return '充电状态(充电1,非充电0)'; 950 case 'Battery.GasGauge': 951 return '电池剩余电量(单位mAh)'; 952 case 'Battery.Level': 953 return '电池百分比'; 954 case 'Battery.RealCurrent': 955 return '实时电流(单位mAh,充电时为正数,耗电时为负数)'; 956 case 'Battery.Screen': 957 return '屏幕状态(亮屏1,灭屏0)'; 958 case 'ThermalReport.ShellTemp': 959 return '外壳温度(单位℃)'; 960 case 'ThermalReport.ThermalLevel': 961 return '温度等级'; 962 default: 963 return title; 964 } 965} 966 967// 鼠标悬浮ThermalReport.ThermalLevel泳道时转换悬浮框内容 968export function convertHoverValue(value: string): string { 969 switch (value) { 970 case '0': 971 return 'COOL'; 972 case '1': 973 return 'WARM'; 974 case '2': 975 return 'HOT'; 976 case '3': 977 return 'OVERHEATED'; 978 default: 979 return value; 980 } 981} 982