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