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