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'; 18 19import { BaseStruct } from '../../bean/BaseStruct'; 20import { CounterStruct, SdkCounterRender } from '../../database/ui-worker/ProduceWorkerSdkCounter'; 21import { renders } from '../../database/ui-worker/ProcedureWorker'; 22import { SdkSliceRender, SdkSliceStruct } from '../../database/ui-worker/ProduceWorkerSdkSlice'; 23import { EmptyRender } from '../../database/ui-worker/cpu/ProcedureWorkerCPU'; 24import { TabUtil } from '../trace/sheet/sdk/TabUtil'; 25import { queryCounterMax, querySdkCount, querySdkCounterData, querySdkSliceData } from '../../database/sql/Sdk.sql'; 26import { queryStartTime } from '../../database/sql/SqlLite.sql'; 27import { NUM_7 } from '../../bean/NumBean'; 28 29export class SpSdkChart { 30 trace: SpSystemTrace; 31 private pluginName = 'dubai-plugin'; 32 33 constructor(trace: SpSystemTrace) { 34 this.trace = trace; 35 } 36 37 private parseJsonByCounterType(startTime: number, showType: unknown, configObj: unknown, table: unknown): void { 38 let chartSql = this.createSql( 39 startTime, //@ts-ignore 40 showType.tableName, //@ts-ignore 41 showType.columns, 42 'where counter_id' + ' = $counter_id' 43 ); //@ts-ignore 44 let maxValue = this.createMaxValueSql(showType.tableName, 'where counter_id = $counter_id'); //@ts-ignore 45 let innerTable = showType.inner; 46 let countSql = this.createSql(startTime, innerTable.tableName, innerTable.columns); //@ts-ignore 47 table.push({ 48 countSql: countSql, 49 chartSql: chartSql, 50 maxSql: maxValue, 51 type: 'counter', //@ts-ignore 52 name: configObj.disPlayName, //@ts-ignore 53 pluginName: configObj.pluginName, 54 }); 55 } 56 57 private parseJsonBySliceType(startTime: number, showType: unknown, configObj: unknown, table: unknown[]): void { 58 let chartSql = this.createSliceSql( 59 startTime, //@ts-ignore 60 showType.tableName, //@ts-ignore 61 showType.columns, 62 'where' + ` slice_id = $column_id and (start_ts - ${startTime}) between $startNS and $endNS;` 63 ); //@ts-ignore 64 let innerTable = showType.inner; 65 let countSql; 66 let countOtherSql = ''; //@ts-ignore 67 if (configObj.pluginName === this.pluginName) { 68 countSql = this.createSql(startTime, innerTable.tableName, innerTable.columns, 'where slice_name like $suffix'); 69 countOtherSql = this.createSql( 70 startTime, 71 innerTable.tableName, 72 innerTable.columns, 73 '' + 74 "where slice_name not like '%_cpu' and slice_name not like '%_display' and " + 75 "slice_name not like '%_gpu' and slice_name not like '%_System_idle' and " + 76 "slice_name not like '%_wifi_data' and slice_name not like '%_sensor' and " + 77 "slice_name not like '%_audio' " 78 ); 79 } else { 80 countSql = this.createSql(startTime, innerTable.tableName, innerTable.columns); 81 } 82 table.push({ 83 countSql: countSql, 84 chartSql: chartSql, 85 type: 'slice', //@ts-ignore 86 name: configObj.disPlayName, //@ts-ignore 87 pluginName: configObj.pluginName, 88 countOtherSql: countOtherSql, 89 }); 90 } 91 92 parseJson(startTime: number, map: Map<number, string>): Map<number, unknown> { 93 let tablesMap = new Map(); 94 let keys = map.keys(); 95 for (let key of keys) { 96 let table: unknown[] = []; 97 let configObj: unknown = map.get(key); 98 if (configObj !== undefined) { 99 //@ts-ignore 100 let configStr = configObj.jsonConfig; 101 let json = JSON.parse(configStr); 102 let tableConfig = json.tableConfig; 103 if (tableConfig !== null) { 104 let showTypes = tableConfig.showType; 105 for (let i = 0; i < showTypes.length; i++) { 106 let showType = showTypes[i]; 107 let type = TabUtil.getTableType(showType); 108 if (type === 'counter') { 109 this.parseJsonByCounterType(startTime, showType, configObj, table); 110 } else if (type === 'slice') { 111 this.parseJsonBySliceType(startTime, showType, configObj, table); 112 } 113 } 114 tablesMap.set(key, table); 115 } 116 } 117 } 118 return tablesMap; 119 } 120 121 private createSliceSql(startTime: number, tableName: string, columns: Array<unknown>, where?: string): string { 122 let sliceSelectSql = 'select '; 123 for (let i = 0; i < columns.length; i++) { 124 let column = columns[i]; //@ts-ignore 125 if (column.column === 'start_ts') { 126 //@ts-ignore 127 column.column = `(start_ts - ${startTime}) AS start_ts`; 128 } //@ts-ignore 129 if (column.column === 'end_ts') { 130 //@ts-ignore 131 column.column = `(end_ts - ${startTime}) AS end_ts`; 132 } 133 if (i === columns.length - 1) { 134 //@ts-ignore 135 sliceSelectSql = `${sliceSelectSql + column.column} `; 136 } else { 137 //@ts-ignore 138 sliceSelectSql = `${sliceSelectSql + column.column}, `; 139 } 140 } 141 sliceSelectSql = `${sliceSelectSql}from ${tableName}`; 142 if (where !== undefined) { 143 sliceSelectSql = `${sliceSelectSql} ${where}`; 144 } 145 return sliceSelectSql; 146 } 147 148 private createMaxValueSql(tableName: string, where?: string): string { 149 let selectSql = `select max(value) as max_value from ${tableName}`; 150 if (where !== undefined) { 151 selectSql = `${selectSql} ${where}`; 152 } 153 return selectSql; 154 } 155 156 private createSql(startTime: number, tableName: string, columns: Array<unknown>, where?: string): string { 157 let selectSql = 'select '; 158 for (let i = 0; i < columns.length; i++) { 159 let column = columns[i]; //@ts-ignore 160 if (column.column === 'ts') { 161 //@ts-ignore 162 column.column = `ts - ${startTime} AS ts`; 163 } 164 if (i === columns.length - 1) { 165 //@ts-ignore 166 selectSql = `${selectSql + column.column} `; 167 } else { 168 //@ts-ignore 169 selectSql = `${selectSql + column.column}, `; 170 } 171 } 172 selectSql = `${selectSql}from ${tableName}`; 173 if (where !== undefined) { 174 selectSql = `${selectSql} ${where}`; 175 } 176 return selectSql; 177 } 178 179 async init(): Promise<void> { 180 let configMap = SpSystemTrace.SDK_CONFIG_MAP; 181 if (configMap === undefined) { 182 return; 183 } 184 let res = await queryStartTime(); 185 //@ts-ignore 186 let startTime = res[0].start_ts; 187 // @ts-ignore 188 let tablesMap = this.parseJson(startTime, configMap); 189 let tableKeys = tablesMap.keys(); 190 for (let componentId of tableKeys) { 191 let table = tablesMap.get(componentId); 192 if (table !== null) { 193 //@ts-ignore 194 let nodeRow = this.initNodeRow(componentId, table[0].name); //@ts-ignore 195 for (let index = 0; index < table.length; index++) { 196 //@ts-ignore 197 let sqlMap = table[index]; 198 if (sqlMap.type === 'counter') { 199 let result = await querySdkCount(sqlMap.countSql, componentId); 200 for (let i = 0; i < result.length; i++) { 201 await this.initCounter(nodeRow, i, result[i], sqlMap, componentId); 202 } 203 } else if (sqlMap.type === 'slice' && sqlMap.pluginName === this.pluginName) { 204 let suffixList = ['cpu', 'display', 'gpu', 'System_idle', 'wifi_data', 'sensor', 'audio']; 205 for (let i = 0; i < suffixList.length; i++) { 206 let result = await querySdkCount(sqlMap.countSql, componentId, { $suffix: `%${suffixList[i]}` }); 207 if (result.length > 0) { 208 let groupNodeRow = await this.initSecondaryRow(nodeRow, i, suffixList[i]); 209 for (let i = 0; i < result.length; i++) { 210 await this.initSlice(groupNodeRow, i, result[i], sqlMap, componentId); 211 } 212 } 213 } 214 let result = await querySdkCount(sqlMap.countOtherSql, componentId); 215 if (result.length > 0) { 216 let groupNodeRow = await this.initSecondaryRow(nodeRow, NUM_7, 'other'); 217 for (let i = 0; i < result.length; i++) { 218 await this.initSlice(groupNodeRow, i, result[i], sqlMap, componentId); 219 } 220 } 221 } else if (sqlMap.type === 'slice') { 222 let result = await querySdkCount(sqlMap.countSql, componentId, {}); 223 for (let i = 0; i < result.length; i++) { 224 await this.initSlice(nodeRow, i, result[i], sqlMap, componentId); 225 } 226 } 227 } 228 } 229 } 230 } 231 232 private initCounterChartRow( 233 componentId: number, 234 expansion: boolean, 235 counterId: string, 236 counterName: string 237 ): TraceRow<CounterStruct> { 238 let traceRow = TraceRow.skeleton<CounterStruct>(); 239 traceRow.rowParentId = `Sdk-${componentId}`; 240 traceRow.rowHidden = !expansion; 241 traceRow.rowId = `${counterId}-${componentId}`; 242 traceRow.rowType = TraceRow.ROW_TYPE_SDK_COUNTER; 243 traceRow.folderPaddingLeft = 30; 244 traceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 245 traceRow.selectChangeHandler = this.trace.selectChangeHandler; 246 traceRow.style.height = '40px'; 247 traceRow.style.width = '100%'; 248 traceRow.setAttribute('children', ''); 249 traceRow.name = `${counterName}`; 250 return traceRow; 251 } 252 253 private initCounter = async ( 254 nodeRow: TraceRow<BaseStruct>, 255 index: number, 256 result: unknown, 257 sqlMap: unknown, 258 componentId: number 259 ): Promise<void> => { 260 //@ts-ignore 261 let traceRow = this.initCounterChartRow(componentId, nodeRow.expansion, result.counter_id, result.counter_name); 262 traceRow.supplier = async (): Promise<CounterStruct[]> => //@ts-ignore 263 querySdkCounterData(sqlMap.chartSql, result.counter_id, componentId); 264 traceRow.focusHandler = (): void => { 265 this.trace?.displayTip( 266 traceRow, 267 CounterStruct.hoverCounterStruct, 268 `<span>${CounterStruct.hoverCounterStruct?.value?.toFixed(2)}</span>` 269 ); 270 }; 271 traceRow.findHoverStruct = (): void => { 272 CounterStruct.hoverCounterStruct = traceRow.getHoverStruct(); 273 }; //@ts-ignore 274 let maxList = await queryCounterMax(sqlMap.maxSql, result.counter_id, componentId); 275 //@ts-ignore 276 let maxCounter = maxList[0].max_value; 277 traceRow.onThreadHandler = (useCache: boolean): void => { 278 let context: CanvasRenderingContext2D; 279 if (traceRow.currentContext) { 280 context = traceRow.currentContext; 281 } else { 282 context = traceRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 283 } 284 traceRow.canvasSave(context); 285 //@ts-ignore 286 (renders[TraceRow.ROW_TYPE_SDK_COUNTER] as SdkCounterRender).renderMainThread( 287 { 288 context: context, 289 useCache: useCache, 290 type: `sdk-counter-${index}`, 291 maxName: `${maxCounter}`, 292 maxValue: maxCounter, 293 }, 294 traceRow 295 ); 296 traceRow.canvasRestore(context, this.trace); 297 }; 298 nodeRow.addChildTraceRow(traceRow); 299 }; 300 301 private initNodeRow = (index: number, name: string): TraceRow<BaseStruct> => { 302 let sdkFolder = TraceRow.skeleton(); 303 sdkFolder.rowId = `Sdk-${index}`; 304 sdkFolder.index = index; 305 sdkFolder.rowType = TraceRow.ROW_TYPE_SDK; 306 sdkFolder.rowParentId = ''; 307 sdkFolder.style.height = '40px'; 308 sdkFolder.folder = true; 309 sdkFolder.name = `${name}`; 310 sdkFolder.favoriteChangeHandler = this.trace.favoriteChangeHandler; 311 sdkFolder.selectChangeHandler = this.trace.selectChangeHandler; 312 sdkFolder.supplier = async (): Promise<BaseStruct[]> => new Promise<[]>((resolve) => resolve([])); 313 sdkFolder.onThreadHandler = (useCache: boolean): void => { 314 sdkFolder.canvasSave(this.trace.canvasPanelCtx!); 315 if (sdkFolder.expansion) { 316 // @ts-ignore 317 this.trace.canvasPanelCtx?.clearRect(0, 0, sdkFolder.frame.width, sdkFolder.frame.height); 318 } else { 319 (renders.empty as EmptyRender).renderMainThread( 320 { 321 context: this.trace.canvasPanelCtx, 322 useCache: useCache, 323 type: '', 324 }, 325 // @ts-ignore 326 sdkFolder 327 ); 328 } 329 sdkFolder.canvasRestore(this.trace.canvasPanelCtx!, this.trace); 330 }; 331 this.trace.rowsEL?.appendChild(sdkFolder); 332 return sdkFolder; 333 }; 334 335 private initSecondaryRow = async ( 336 nodeRow: TraceRow<BaseStruct>, 337 index: number, 338 name: string 339 ): Promise<TraceRow<BaseStruct>> => { 340 let sdkSecondFolder = TraceRow.skeleton(); 341 sdkSecondFolder.rowId = `Sdk-${name}-${index}`; 342 sdkSecondFolder.index = index; 343 sdkSecondFolder.rowType = TraceRow.ROW_TYPE_SDK; 344 sdkSecondFolder.rowParentId = nodeRow.rowId; 345 sdkSecondFolder.rowHidden = !nodeRow.expansion; 346 sdkSecondFolder.style.height = '40px'; 347 sdkSecondFolder.folder = true; 348 sdkSecondFolder.folderPaddingLeft = 30; 349 sdkSecondFolder.name = `${name}`; 350 sdkSecondFolder.favoriteChangeHandler = this.trace.favoriteChangeHandler; 351 sdkSecondFolder.selectChangeHandler = this.trace.selectChangeHandler; 352 sdkSecondFolder.supplier = async (): Promise<BaseStruct[]> => new Promise<[]>((resolve) => resolve([])); 353 sdkSecondFolder.onThreadHandler = (useCache: boolean): void => { 354 sdkSecondFolder.canvasSave(this.trace.canvasPanelCtx!); 355 if (sdkSecondFolder.expansion) { 356 // @ts-ignore 357 this.trace.canvasPanelCtx?.clearRect(0, 0, sdkSecondFolder.frame.width, sdkSecondFolder.frame.height); 358 } else { 359 (renders.empty as EmptyRender).renderMainThread( 360 { 361 context: this.trace.canvasPanelCtx, 362 useCache: useCache, 363 type: '', 364 }, 365 // @ts-ignore 366 sdkSecondFolder 367 ); 368 } 369 sdkSecondFolder.canvasRestore(this.trace.canvasPanelCtx!, this.trace); 370 }; 371 this.trace.rowsEL?.appendChild(sdkSecondFolder); 372 return sdkSecondFolder; 373 }; 374 375 private initSliceChartRow( 376 expansion: boolean, 377 rowId: string, 378 sliceId: string, 379 sliceName: string, 380 componentId: number 381 ): TraceRow<SdkSliceStruct> { 382 let traceRow = TraceRow.skeleton<SdkSliceStruct>(); 383 traceRow.rowType = TraceRow.ROW_TYPE_SDK_SLICE; 384 traceRow.rowHidden = !expansion; 385 traceRow.rowParentId = rowId; 386 traceRow.folderPaddingLeft = 30; 387 traceRow.style.height = '40px'; 388 traceRow.style.width = '100%'; 389 traceRow.name = `${sliceName}`; 390 traceRow.setAttribute('children', ''); 391 traceRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 392 traceRow.selectChangeHandler = this.trace.selectChangeHandler; 393 traceRow.rowId = `${sliceId}-${componentId}`; 394 return traceRow; 395 } 396 397 private initSlice = async ( 398 nodeRow: TraceRow<BaseStruct>, 399 index: number, 400 result: unknown, 401 sqlMap: unknown, 402 componentId: number 403 ): Promise<void> => { 404 let traceRow = this.initSliceChartRow( 405 nodeRow.expansion, 406 nodeRow.rowId!, //@ts-ignore 407 result.slice_id, //@ts-ignore 408 result.slice_name, 409 componentId 410 ); 411 traceRow.supplier = async (): Promise<SdkSliceStruct[]> => 412 querySdkSliceData( 413 //@ts-ignore 414 sqlMap.chartSql, //@ts-ignore 415 result.slice_id, 416 TraceRow.range?.startNS || 0, 417 TraceRow.range?.endNS || 0, 418 componentId 419 ); 420 traceRow.focusHandler = (): void => { 421 this.trace?.displayTip( 422 traceRow, 423 SdkSliceStruct.hoverSdkSliceStruct, 424 `<span>${SdkSliceStruct.hoverSdkSliceStruct?.value}</span>` 425 ); 426 }; 427 traceRow.findHoverStruct = (): void => { 428 SdkSliceStruct.hoverSdkSliceStruct = traceRow.getHoverStruct(); 429 }; 430 traceRow.onThreadHandler = (useCache: boolean): void => { 431 let context: CanvasRenderingContext2D; 432 if (traceRow.currentContext) { 433 context = traceRow.currentContext; 434 } else { 435 context = traceRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 436 } 437 traceRow.canvasSave(context); 438 //@ts-ignore 439 (renders[TraceRow.ROW_TYPE_SDK_SLICE] as SdkSliceRender).renderMainThread( 440 { 441 context: context, 442 useCache: useCache, 443 type: `sdk-slice-${index}`, 444 maxName: '', 445 maxValue: 0, 446 }, 447 traceRow 448 ); 449 traceRow.canvasRestore(context, this.trace); 450 }; 451 nodeRow.addChildTraceRow(traceRow); 452 }; 453} 454