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.js'; 17import { 18 getAsyncEvents, 19 getFunDataByTid, 20 getMaxDepthByTid, 21 queryAllActualData, 22 queryAllExpectedData, 23 queryAllJankProcess, 24 queryEventCountMap, 25 queryProcess, 26 queryProcessAsyncFunc, 27 queryProcessByTable, 28 queryProcessContentCount, 29 queryProcessData, 30 queryProcessMem, 31 queryProcessMemData, 32 queryProcessSoInitData, 33 queryProcessSoMaxDepth, 34 queryProcessStartup, 35 queryProcessThreads, 36 queryProcessThreadsByTable, 37 queryStartupPidArray, 38 queryThreadData, 39} from '../../database/SqlLite.js'; 40import { Utils } from '../trace/base/Utils.js'; 41import { info } from '../../../log/Log.js'; 42import { TraceRow } from '../trace/base/TraceRow.js'; 43import { renders } from '../../database/ui-worker/ProcedureWorker.js'; 44import { ProcessRender, ProcessStruct } from '../../database/ui-worker/ProcedureWorkerProcess.js'; 45import { ThreadRender, ThreadStruct } from '../../database/ui-worker/ProcedureWorkerThread.js'; 46import { FuncRender, FuncStruct } from '../../database/ui-worker/ProcedureWorkerFunc.js'; 47import { MemRender, ProcessMemStruct } from '../../database/ui-worker/ProcedureWorkerMem.js'; 48import { FolderSupplier, FolderThreadHandler } from './SpChartManager.js'; 49import { JankRender, JankStruct } from '../../database/ui-worker/ProcedureWorkerJank.js'; 50import { ns2xByTimeShaft } from '../../database/ui-worker/ProcedureWorkerCommon.js'; 51import { AppStartupRender, AppStartupStruct } from '../../database/ui-worker/ProcedureWorkerAppStartup.js'; 52import { SoRender, SoStruct } from '../../database/ui-worker/ProcedureWorkerSoInit.js'; 53 54export class SpProcessChart { 55 private readonly trace: SpSystemTrace; 56 private processAsyncFuncMap: any = {}; 57 private processAsyncFuncArray: any[] = []; 58 private eventCountMap: any; 59 private processThreads: Array<ThreadStruct> = []; 60 private processAsyncEvent: Array<ProcessMemStruct> = []; 61 private processMem: Array<any> = []; 62 private processThreadCountMap: Map<number, number> = new Map(); 63 private processThreadDataCountMap: Map<number, number> = new Map(); 64 private processFuncDataCountMap: Map<number, number> = new Map(); 65 private processMemDataCountMap: Map<number, number> = new Map(); 66 private threadFuncMaxDepthMap: Map<string, number> = new Map(); 67 private startupProcessArr: { pid: number }[] = []; 68 private processSoMaxDepth: { pid: number; maxDepth: number }[] = []; 69 70 constructor(trace: SpSystemTrace) { 71 this.trace = trace; 72 } 73 74 initAsyncFuncData = async () => { 75 let asyncFuncList: any[] = await queryProcessAsyncFunc(); 76 info('AsyncFuncData Count is: ', asyncFuncList!.length); 77 this.processAsyncFuncArray = asyncFuncList; 78 this.processAsyncFuncMap = Utils.groupBy(asyncFuncList, 'pid'); 79 }; 80 81 initDeliverInputEvent = async () => { 82 let row = TraceRow.skeleton(); 83 row.setAttribute('disabled-check', ''); 84 row.rowId = 'DeliverInputEvent'; 85 row.index = 0; 86 row.rowType = TraceRow.ROW_TYPE_DELIVER_INPUT_EVENT; 87 row.rowParentId = ''; 88 row.folder = true; 89 row.style.height = '40px'; 90 row.name = 'DeliverInputEvent'; 91 row.supplier = FolderSupplier(); 92 row.onThreadHandler = FolderThreadHandler(row, this.trace); 93 94 let asyncFuncGroup = Utils.groupBy( 95 this.processAsyncFuncArray.filter((it) => it.funName === 'deliverInputEvent'), 96 'tid' 97 ); 98 if (Reflect.ownKeys(asyncFuncGroup).length > 0) { 99 this.trace.rowsEL?.appendChild(row); 100 } 101 Reflect.ownKeys(asyncFuncGroup).map((key: any) => { 102 let asyncFuncGroups: Array<any> = asyncFuncGroup[key]; 103 if (asyncFuncGroups.length > 0) { 104 let isIntersect = (left: any, right: any) => 105 Math.max(left.startTs + left.dur, right.startTs + right.dur) - Math.min(left.startTs, right.startTs) < 106 left.dur + right.dur; 107 let depths: any = []; 108 let createDepth = (currentDepth: number, index: number) => { 109 if (depths[currentDepth] == undefined || !isIntersect(depths[currentDepth], asyncFuncGroups[index])) { 110 asyncFuncGroups[index].depth = currentDepth; 111 depths[currentDepth] = asyncFuncGroups[index]; 112 } else { 113 createDepth(++currentDepth, index); 114 } 115 }; 116 asyncFuncGroups.forEach((it, i) => { 117 if (it.dur == -1) { 118 it.dur = (TraceRow.range?.endNS || 0) - it.startTs; 119 it.flag = 'Did not end'; 120 } 121 createDepth(0, i); 122 }); 123 let max = Math.max(...asyncFuncGroups.map((it) => it.depth || 0)) + 1; 124 let maxHeight = max * 20; 125 let funcRow = TraceRow.skeleton<FuncStruct>(); 126 funcRow.rowId = `${asyncFuncGroups[0].funName}-${key}`; 127 funcRow.asyncFuncName = asyncFuncGroups[0].funName; 128 funcRow.asyncFuncNamePID = key; 129 funcRow.rowType = TraceRow.ROW_TYPE_FUNC; 130 funcRow.rowParentId = `${row.rowId}`; 131 funcRow.rowHidden = !row.expansion; 132 funcRow.style.width = '100%'; 133 funcRow.style.height = `${maxHeight}px`; 134 funcRow.setAttribute('height', `${maxHeight}`); 135 funcRow.name = `${asyncFuncGroups[0].funName} ${key}`; 136 funcRow.setAttribute('children', ''); 137 funcRow.supplier = () => new Promise((resolve) => resolve(asyncFuncGroups)); 138 funcRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 139 funcRow.selectChangeHandler = this.trace.selectChangeHandler; 140 funcRow.onThreadHandler = (useCache) => { 141 let context = funcRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 142 funcRow.canvasSave(context); 143 (renders['func'] as FuncRender).renderMainThread( 144 { 145 context: context, 146 useCache: useCache, 147 type: `func-${asyncFuncGroups[0].funName}-${key}`, 148 }, 149 funcRow 150 ); 151 funcRow.canvasRestore(context); 152 }; 153 row.addChildTraceRow(funcRow); 154 } 155 }); 156 }; 157 158 async init() { 159 let threadFuncMaxDepthArray = await getMaxDepthByTid(); 160 info('Gets the maximum tier per thread , tid and maxDepth'); 161 threadFuncMaxDepthArray.forEach((it) => { 162 this.threadFuncMaxDepthMap.set(`${it.ipid}-${it.tid}`, it.maxDepth); 163 }); 164 info('convert tid and maxDepth array to map'); 165 let pidCountArray = await queryProcessContentCount(); 166 info('fetch per process pid,switch_count,thread_count,slice_count,mem_count'); 167 pidCountArray.forEach((it) => { 168 this.processThreadDataCountMap.set(it.pid, it.switch_count); 169 this.processThreadCountMap.set(it.pid, it.thread_count); 170 this.processFuncDataCountMap.set(it.pid, it.slice_count); 171 this.processMemDataCountMap.set(it.pid, it.mem_count); 172 }); 173 let queryProcessThreadResult = await queryProcessThreads(); 174 let queryProcessThreadsByTableResult = await queryProcessThreadsByTable(); 175 this.processAsyncEvent = await getAsyncEvents(); 176 info('The amount of initialized process Event data is : ', this.processAsyncEvent!.length); 177 this.processMem = await queryProcessMem(); 178 this.startupProcessArr = await queryStartupPidArray(); 179 this.processSoMaxDepth = await queryProcessSoMaxDepth(); 180 info('The amount of initialized process memory data is : ', this.processMem!.length); 181 let eventCountList: Array<any> = await queryEventCountMap(); 182 this.eventCountMap = eventCountList.reduce((pre, current) => { 183 pre[`${current.eventName}`] = current.count; 184 return pre; 185 }, {}); 186 this.processThreads = Utils.removeDuplicates(queryProcessThreadResult, queryProcessThreadsByTableResult, 'tid'); 187 info('The amount of initialized process threads data is : ', this.processThreads!.length); 188 if ( 189 this.eventCountMap['print'] == 0 && 190 this.eventCountMap['tracing_mark_write'] == 0 && 191 this.eventCountMap['sched_switch'] == 0 192 ) { 193 return; 194 } 195 let time = new Date().getTime(); 196 let processes = await queryProcess(); 197 let processFromTable = await queryProcessByTable(); 198 let processList = Utils.removeDuplicates(processes, processFromTable, 'pid'); 199 let allJankProcessData = await queryAllJankProcess(); 200 let allJankProcess: Array<number> = []; 201 let allExpectedProcess: Array<any> = []; 202 let allActualProcess: Array<any> = []; 203 if (allJankProcessData.length > 0) { 204 allJankProcessData.forEach((name, index) => { 205 allJankProcess.push(name.pid!); 206 }); 207 allExpectedProcess = await queryAllExpectedData(); 208 allActualProcess = await queryAllActualData(); 209 } 210 info('ProcessList Data size is: ', processList!.length); 211 for (let i = 0; i < processList.length; i++) { 212 const it = processList[i]; 213 if ( 214 (this.processThreadDataCountMap.get(it.pid) || 0) == 0 && 215 (this.processThreadCountMap.get(it.pid) || 0) == 0 && 216 (this.processFuncDataCountMap.get(it.pid) || 0) == 0 && 217 (this.processMemDataCountMap.get(it.pid) || 0) == 0 218 ) { 219 continue; 220 } 221 let processRow = TraceRow.skeleton<ProcessStruct>(); 222 processRow.rowId = `${it.pid}`; 223 processRow.index = i; 224 processRow.rowType = TraceRow.ROW_TYPE_PROCESS; 225 processRow.rowParentId = ''; 226 processRow.style.height = '40px'; 227 processRow.folder = true; 228 processRow.name = `${it.processName || 'Process'} ${it.pid}`; 229 processRow.supplier = () => queryProcessData(it.pid || -1, 0, TraceRow.range?.totalNS || 0); 230 processRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 231 processRow.selectChangeHandler = this.trace.selectChangeHandler; 232 processRow.onThreadHandler = (useCache) => { 233 processRow.canvasSave(this.trace.canvasPanelCtx!); 234 if (processRow.expansion) { 235 this.trace.canvasPanelCtx?.clearRect(0, 0, processRow.frame.width, processRow.frame.height); 236 } else { 237 (renders['process'] as ProcessRender).renderMainThread( 238 { 239 context: this.trace.canvasPanelCtx, 240 pid: it.pid, 241 useCache: useCache, 242 type: `process ${processRow.index} ${it.processName}`, 243 }, 244 processRow 245 ); 246 } 247 processRow.canvasRestore(this.trace.canvasPanelCtx!); 248 }; 249 this.trace.rowsEL?.appendChild(processRow); 250 let startupRow: TraceRow<AppStartupStruct> | undefined = undefined; 251 let soRow: TraceRow<SoStruct> | undefined = undefined; 252 if (this.startupProcessArr.find((sp) => sp.pid === it.pid)) { 253 startupRow = this.addStartUpRow(processRow); 254 } 255 let maxSoDepth = this.processSoMaxDepth.find((md) => md.pid === it.pid); 256 if (maxSoDepth) { 257 soRow = this.addSoInitRow(processRow, maxSoDepth.maxDepth); 258 } 259 /** 260 * Janks Frames 261 */ 262 let actualRow: TraceRow<JankStruct> | null = null; 263 let expectedRow: TraceRow<JankStruct> | null = null; 264 if (allJankProcess.indexOf(it.pid) > -1 && allExpectedProcess.length > 0) { 265 let expectedData = allExpectedProcess.filter((ite) => ite.pid == it.pid); 266 if (expectedData.length > 0) { 267 // @ts-ignore 268 let isIntersect = (a: JanksStruct, b: JanksStruct) => 269 Math.max(a.ts + a.dur, b.ts + b.dur) - Math.min(a.ts, b.ts) < a.dur + b.dur; 270 let depthArray: any = []; 271 for (let j = 0; j < expectedData.length; j++) { 272 let expectedItem = expectedData[j]; 273 if (expectedItem.cmdline != 'render_service') { 274 expectedItem.frame_type = 'app'; 275 } else { 276 expectedItem.frame_type = expectedItem.cmdline; 277 } 278 if (!expectedItem.dur || expectedItem.dur < 0) { 279 continue; 280 } 281 if (depthArray.length === 0) { 282 expectedItem.depth = 0; 283 depthArray.push(expectedItem); 284 } else { 285 if (isIntersect(depthArray[0], expectedItem)) { 286 if (isIntersect(depthArray[depthArray.length - 1], expectedItem)) { 287 expectedItem.depth = depthArray.length; 288 depthArray.push(expectedItem); 289 } 290 } else { 291 expectedItem.depth = 0; 292 depthArray = [expectedItem]; 293 } 294 } 295 } 296 let max = Math.max(...expectedData.map((it) => it.depth || 0)) + 1; 297 let maxHeight = max * 20; 298 expectedRow = TraceRow.skeleton<JankStruct>(); 299 let timeLineType = expectedData[0].type; 300 expectedRow.rowId = `${timeLineType}-${it.pid}`; 301 expectedRow.asyncFuncName = it.processName; 302 expectedRow.asyncFuncNamePID = it.pid; 303 expectedRow.rowType = TraceRow.ROW_TYPE_JANK; 304 expectedRow.rowParentId = `${it.pid}`; 305 expectedRow.rowHidden = !processRow.expansion; 306 expectedRow.style.width = '100%'; 307 expectedRow.style.height = `${maxHeight}px`; 308 expectedRow.setAttribute('height', `${maxHeight}`); 309 expectedRow.setAttribute('frame_type', expectedData[0].frame_type); 310 expectedRow.name = 'Expected Timeline'; 311 expectedRow.addTemplateTypes('FrameTimeline'); 312 expectedRow.setAttribute('children', ''); 313 expectedRow.supplier = () => 314 new Promise((resolve) => { 315 resolve(expectedData); 316 }); 317 expectedRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 318 expectedRow.selectChangeHandler = this.trace.selectChangeHandler; 319 expectedRow.onThreadHandler = (useCache) => { 320 let context = expectedRow!.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 321 expectedRow!.canvasSave(context); 322 (renders['jank'] as JankRender).renderMainThread( 323 { 324 context: context, 325 useCache: useCache, 326 type: 'expected_frame_timeline_slice', 327 }, 328 expectedRow! 329 ); 330 expectedRow!.canvasRestore(context); 331 }; 332 processRow.addChildTraceRow(expectedRow); 333 let actualData = allActualProcess.filter((ite) => ite.pid == it.pid); 334 if (actualData.length > 0) { 335 let isIntersect = (a: any, b: any) => 336 Math.max(a.ts + a.dur, b.ts + b.dur) - Math.min(a.ts, b.ts) < a.dur + b.dur; 337 let depthArray: any = []; 338 for (let j = 0; j < actualData.length; j++) { 339 let actualItem = actualData[j]; 340 if (actualItem.cmdline != 'render_service') { 341 actualItem.frame_type = 'app'; 342 } else { 343 actualItem.frame_type = actualItem.cmdline; 344 } 345 if (!actualItem.dur || actualItem.dur < 0) { 346 continue; 347 } 348 if (depthArray.length === 0) { 349 actualItem.depth = 0; 350 depthArray.push(actualItem); 351 } else { 352 if (isIntersect(depthArray[0], actualItem)) { 353 if (isIntersect(depthArray[depthArray.length - 1], actualItem)) { 354 actualItem.depth = depthArray.length; 355 depthArray.push(actualItem); 356 } else { 357 actualItem.depth = depthArray.length - 1; 358 depthArray[length - 1] = actualItem; 359 } 360 } else { 361 actualItem.depth = 0; 362 depthArray = [actualItem]; 363 } 364 } 365 } 366 let max = Math.max(...actualData.map((it) => it.depth || 0)) + 1; 367 let maxHeight = max * 20; 368 actualRow = TraceRow.skeleton<JankStruct>(); 369 let timeLineType = actualData[0].type; 370 actualRow.rowId = `${timeLineType}-${it.pid}`; 371 actualRow.rowType = TraceRow.ROW_TYPE_JANK; 372 actualRow.rowParentId = `${it.pid}`; 373 actualRow.rowHidden = !processRow.expansion; 374 actualRow.style.width = '100%'; 375 actualRow.style.height = `${maxHeight}px`; 376 actualRow.setAttribute('height', `${maxHeight}`); 377 actualRow.name = 'Actual Timeline'; 378 actualRow.addTemplateTypes('FrameTimeline'); 379 actualRow.setAttribute('frame_type', actualData[0].frame_type); 380 actualRow.setAttribute('children', ''); 381 actualRow.dataList = actualData; 382 actualRow.supplier = () => new Promise((resolve) => resolve(actualData)); 383 actualRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 384 actualRow.selectChangeHandler = this.trace.selectChangeHandler; 385 actualRow.onThreadHandler = (useCache) => { 386 let context = actualRow!.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 387 actualRow!.canvasSave(context); 388 (renders['jank'] as JankRender).renderMainThread( 389 { 390 context: context, 391 useCache: useCache, 392 type: 'actual_frame_timeline_slice', 393 }, 394 actualRow! 395 ); 396 actualRow!.canvasRestore(context); 397 }; 398 processRow.addChildTraceRow(actualRow); 399 } 400 } 401 } 402 let offsetYTimeOut: any = undefined; 403 processRow.addEventListener('expansion-change', (e: any) => { 404 JankStruct.delJankLineFlag = false; 405 if (offsetYTimeOut) { 406 clearTimeout(offsetYTimeOut); 407 } 408 if (e.detail.expansion) { 409 offsetYTimeOut = setTimeout(() => { 410 this.trace.linkNodes.forEach((linkNodeItem) => { 411 JankStruct.selectJankStructList?.forEach((selectProcessStruct: any) => { 412 if (e.detail.rowId == selectProcessStruct.pid) { 413 JankStruct.selectJankStruct = selectProcessStruct; 414 JankStruct.hoverJankStruct = selectProcessStruct; 415 } 416 }); 417 if (linkNodeItem[0].rowEL.collect) { 418 linkNodeItem[0].rowEL.translateY = linkNodeItem[0].rowEL.getBoundingClientRect().top - 195; 419 } else { 420 linkNodeItem[0].rowEL.translateY = linkNodeItem[0].rowEL.offsetTop - this.trace.rowsPaneEL!.scrollTop; 421 } 422 linkNodeItem[0].y = linkNodeItem[0].rowEL!.translateY! + linkNodeItem[0].offsetY; 423 if (linkNodeItem[1].rowEL.collect) { 424 linkNodeItem[1].rowEL.translateY = linkNodeItem[1].rowEL.getBoundingClientRect().top - 195; 425 } else { 426 linkNodeItem[1].rowEL.translateY = linkNodeItem[1].rowEL.offsetTop - this.trace.rowsPaneEL!.scrollTop; 427 } 428 linkNodeItem[1].y = linkNodeItem[1].rowEL!.translateY! + linkNodeItem[1].offsetY; 429 if (linkNodeItem[0].rowEL.rowId == e.detail.rowId) { 430 linkNodeItem[0].x = ns2xByTimeShaft(linkNodeItem[0].ns, this.trace.timerShaftEL!); 431 linkNodeItem[0].y = actualRow!.translateY! + linkNodeItem[0].offsetY * 2; 432 linkNodeItem[0].offsetY = linkNodeItem[0].offsetY * 2; 433 linkNodeItem[0].rowEL = actualRow!; 434 } else if (linkNodeItem[1].rowEL.rowId == e.detail.rowId) { 435 linkNodeItem[1].x = ns2xByTimeShaft(linkNodeItem[1].ns, this.trace.timerShaftEL!); 436 linkNodeItem[1].y = actualRow!.translateY! + linkNodeItem[1].offsetY * 2; 437 linkNodeItem[1].offsetY = linkNodeItem[1].offsetY * 2; 438 linkNodeItem[1].rowEL = actualRow!; 439 } 440 }); 441 }, 300); 442 } else { 443 if (JankStruct!.selectJankStruct) { 444 JankStruct.selectJankStructList?.push(<JankStruct>JankStruct!.selectJankStruct); 445 } 446 offsetYTimeOut = setTimeout(() => { 447 this.trace.linkNodes?.forEach((linkProcessItem) => { 448 if (linkProcessItem[0].rowEL.collect) { 449 linkProcessItem[0].rowEL.translateY = linkProcessItem[0].rowEL.getBoundingClientRect().top - 195; 450 } else { 451 linkProcessItem[0].rowEL.translateY = 452 linkProcessItem[0].rowEL.offsetTop - this.trace.rowsPaneEL!.scrollTop; 453 } 454 linkProcessItem[0].y = linkProcessItem[0].rowEL!.translateY! + linkProcessItem[0].offsetY; 455 if (linkProcessItem[1].rowEL.collect) { 456 linkProcessItem[1].rowEL.translateY = linkProcessItem[1].rowEL.getBoundingClientRect().top - 195; 457 } else { 458 linkProcessItem[1].rowEL.translateY = 459 linkProcessItem[1].rowEL.offsetTop - this.trace.rowsPaneEL!.scrollTop; 460 } 461 linkProcessItem[1].y = linkProcessItem[1].rowEL!.translateY! + linkProcessItem[1].offsetY; 462 if (linkProcessItem[0].rowEL.rowParentId == e.detail.rowId) { 463 linkProcessItem[0].x = ns2xByTimeShaft(linkProcessItem[0].ns, this.trace.timerShaftEL!); 464 linkProcessItem[0].y = processRow!.translateY! + linkProcessItem[0].offsetY / 2; 465 linkProcessItem[0].offsetY = linkProcessItem[0].offsetY / 2; 466 linkProcessItem[0].rowEL = processRow!; 467 } else if (linkProcessItem[1].rowEL.rowParentId == e.detail.rowId) { 468 linkProcessItem[1].x = ns2xByTimeShaft(linkProcessItem[1].ns, this.trace.timerShaftEL!); 469 linkProcessItem[1].y = processRow!.translateY! + linkProcessItem[1].offsetY / 2; 470 linkProcessItem[1].offsetY = linkProcessItem[1].offsetY / 2; 471 linkProcessItem[1].rowEL = processRow!; 472 } 473 }); 474 }, 300); 475 } 476 let refreshTimeOut = setTimeout(() => { 477 this.trace.refreshCanvas(true); 478 clearTimeout(refreshTimeOut); 479 }, 360); 480 }); 481 /** 482 * Async Function 483 */ 484 let asyncFuncList = this.processAsyncFuncMap[it.pid] || []; 485 let asyncFuncGroup = Utils.groupBy(asyncFuncList, 'funName'); 486 Reflect.ownKeys(asyncFuncGroup).map((key: any) => { 487 let asyncFunctions: Array<any> = asyncFuncGroup[key]; 488 if (asyncFunctions.length > 0) { 489 let isIntersect = (a: any, b: any) => 490 Math.max(a.startTs + a.dur, b.startTs + b.dur) - Math.min(a.startTs, b.startTs) < a.dur + b.dur; 491 let depthArray: any = []; 492 let createDepth = (currentDepth: number, index: number) => { 493 if ( 494 depthArray[currentDepth] == undefined || 495 !isIntersect(depthArray[currentDepth], asyncFunctions[index]) 496 ) { 497 asyncFunctions[index].depth = currentDepth; 498 depthArray[currentDepth] = asyncFunctions[index]; 499 } else { 500 createDepth(++currentDepth, index); 501 } 502 }; 503 asyncFunctions.forEach((it, i) => { 504 if (it.dur == -1) { 505 it.dur = (TraceRow.range?.endNS || 0) - it.startTs; 506 it.flag = 'Did not end'; 507 } 508 createDepth(0, i); 509 }); 510 let max = Math.max(...asyncFunctions.map((it) => it.depth || 0)) + 1; 511 let maxHeight = max * 20; 512 let funcRow = TraceRow.skeleton<FuncStruct>(); 513 funcRow.rowId = `${asyncFunctions[0].funName}-${it.pid}`; 514 funcRow.asyncFuncName = asyncFunctions[0].funName; 515 funcRow.asyncFuncNamePID = it.pid; 516 funcRow.rowType = TraceRow.ROW_TYPE_FUNC; 517 funcRow.rowParentId = `${it.pid}`; 518 funcRow.rowHidden = !processRow.expansion; 519 funcRow.style.width = '100%'; 520 funcRow.style.height = `${maxHeight}px`; 521 funcRow.setAttribute('height', `${maxHeight}`); 522 funcRow.name = `${asyncFunctions[0].funName}`; 523 funcRow.setAttribute('children', ''); 524 funcRow.supplier = () => new Promise((resolve) => resolve(asyncFunctions)); 525 funcRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 526 funcRow.selectChangeHandler = this.trace.selectChangeHandler; 527 funcRow.onThreadHandler = (cacheFlag) => { 528 let context = funcRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 529 funcRow.canvasSave(context); 530 (renders['func'] as FuncRender).renderMainThread( 531 { 532 context: context, 533 useCache: cacheFlag, 534 type: `func-${asyncFunctions[0].funName}-${it.pid}`, 535 }, 536 funcRow 537 ); 538 funcRow.canvasRestore(context); 539 }; 540 processRow.addChildTraceRow(funcRow); 541 } 542 }); 543 544 /** 545 * 添加进程内存信息 546 */ 547 let processMem = this.processMem.filter((mem) => mem.pid === it.pid); 548 processMem.forEach((mem) => { 549 let row = TraceRow.skeleton<ProcessMemStruct>(); 550 row.rowId = `${mem.trackId}`; 551 row.rowType = TraceRow.ROW_TYPE_MEM; 552 row.rowParentId = `${it.pid}`; 553 row.rowHidden = !processRow.expansion; 554 row.style.height = '40px'; 555 row.style.width = '100%'; 556 row.name = `${mem.trackName}`; 557 row.setAttribute('children', ''); 558 row.favoriteChangeHandler = this.trace.favoriteChangeHandler; 559 row.selectChangeHandler = this.trace.selectChangeHandler; 560 row.focusHandler = () => { 561 this.trace.displayTip( 562 row, 563 ProcessMemStruct.hoverProcessMemStruct, 564 `<span>${ProcessMemStruct.hoverProcessMemStruct?.value || '0'}</span>` 565 ); 566 }; 567 row.supplier = () => 568 queryProcessMemData(mem.trackId).then((resultProcess) => { 569 let maxValue = Math.max(...resultProcess.map((it) => it.value || 0)); 570 for (let j = 0; j < resultProcess.length; j++) { 571 resultProcess[j].maxValue = maxValue; 572 if (j == resultProcess.length - 1) { 573 resultProcess[j].duration = (TraceRow.range?.totalNS || 0) - (resultProcess[j].startTime || 0); 574 } else { 575 resultProcess[j].duration = (resultProcess[j + 1].startTime || 0) - (resultProcess[j].startTime || 0); 576 } 577 if (j > 0) { 578 resultProcess[j].delta = (resultProcess[j].value || 0) - (resultProcess[j - 1].value || 0); 579 } else { 580 resultProcess[j].delta = 0; 581 } 582 } 583 return resultProcess; 584 }); 585 row.onThreadHandler = (useCache) => { 586 let context = row.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 587 row.canvasSave(context); 588 (renders['mem'] as MemRender).renderMainThread( 589 { 590 context: context, 591 useCache: useCache, 592 type: `mem ${mem.trackId} ${mem.trackName}`, 593 }, 594 row 595 ); 596 row.canvasRestore(context); 597 }; 598 processRow.addChildTraceRow(row); 599 }); 600 /** 601 * add thread list 602 */ 603 let threads = this.processThreads.filter((thread) => thread.pid === it.pid && thread.tid != 0); 604 for (let j = 0; j < threads.length; j++) { 605 let thread = threads[j]; 606 let threadRow = TraceRow.skeleton<ThreadStruct>(); 607 threadRow.rowId = `${thread.tid}`; 608 threadRow.rowType = TraceRow.ROW_TYPE_THREAD; 609 threadRow.rowParentId = `${it.pid}`; 610 threadRow.rowHidden = !processRow.expansion; 611 threadRow.index = j; 612 threadRow.style.height = '30px'; 613 threadRow.style.width = '100%'; 614 threadRow.name = `${thread.threadName || 'Thread'} ${thread.tid}`; 615 threadRow.setAttribute('children', ''); 616 threadRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 617 threadRow.selectChangeHandler = this.trace.selectChangeHandler; 618 threadRow.supplier = () => 619 queryThreadData(thread.tid || 0, it.pid || 0).then((res) => { 620 if (res.length <= 0) { 621 threadRow.rowDiscard = true; 622 this.trace.refreshCanvas(true); 623 } 624 return res; 625 }); 626 threadRow.focusHandler = (ev) => {}; 627 threadRow.onThreadHandler = (useCache) => { 628 let context = threadRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 629 threadRow.canvasSave(context); 630 (renders['thread'] as ThreadRender).renderMainThread( 631 { 632 context: context, 633 useCache: useCache, 634 type: `thread ${thread.tid} ${thread.threadName}`, 635 translateY: threadRow.translateY, 636 }, 637 threadRow 638 ); 639 threadRow.canvasRestore(context); 640 }; 641 if (threadRow.rowId == threadRow.rowParentId) { 642 if (actualRow != null) { 643 processRow.addChildTraceRowAfter(threadRow, actualRow); 644 } else if (expectedRow != null) { 645 processRow.addChildTraceRowAfter(threadRow, expectedRow); 646 } else if (soRow) { 647 processRow.addChildTraceRowAfter(threadRow, soRow); 648 } else if (startupRow) { 649 processRow.addChildTraceRowAfter(threadRow, startupRow); 650 } else { 651 processRow.addChildTraceRowSpecifyLocation(threadRow, 0); 652 } 653 } else { 654 processRow.addChildTraceRow(threadRow); 655 } 656 if (this.threadFuncMaxDepthMap.get(`${thread.upid}-${thread.tid}`) != undefined) { 657 let max = this.threadFuncMaxDepthMap.get(`${thread.upid}-${thread.tid}`) || 1; 658 let maxHeight = max * 20; 659 let funcRow = TraceRow.skeleton<FuncStruct>(); 660 funcRow.rowId = `${thread.tid}`; 661 funcRow.rowType = TraceRow.ROW_TYPE_FUNC; 662 funcRow.rowParentId = `${it.pid}`; 663 funcRow.rowHidden = !processRow.expansion; 664 funcRow.checkType = threadRow.checkType; 665 funcRow.style.width = '100%'; 666 funcRow.style.height = `${maxHeight}px`; 667 funcRow.name = `${thread.threadName || 'Thread'} ${thread.tid}`; 668 funcRow.setAttribute('children', ''); 669 funcRow.supplier = () => 670 getFunDataByTid(thread.tid || 0, thread.upid || 0).then((funs: Array<FuncStruct>) => { 671 if (funs.length > 0) { 672 let isBinder = (data: FuncStruct): boolean => { 673 return ( 674 data.funName != null && 675 (data.funName.toLowerCase().startsWith('binder transaction async') || //binder transaction 676 data.funName.toLowerCase().startsWith('binder async') || 677 data.funName.toLowerCase().startsWith('binder reply')) 678 ); 679 }; 680 funs.forEach((fun) => { 681 if (isBinder(fun)) { 682 } else { 683 if (fun.dur == -1) { 684 fun.dur = (TraceRow.range?.totalNS || 0) - (fun.startTs || 0); 685 fun.flag = 'Did not end'; 686 } 687 } 688 }); 689 } else { 690 funcRow.rowDiscard = true; 691 this.trace.refreshCanvas(true); 692 } 693 return funs; 694 }); 695 funcRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 696 funcRow.selectChangeHandler = this.trace.selectChangeHandler; 697 funcRow.onThreadHandler = (useCache) => { 698 let context = funcRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 699 funcRow.canvasSave(context); 700 (renders['func'] as FuncRender).renderMainThread( 701 { 702 context: context, 703 useCache: useCache, 704 type: `func${thread.tid}${thread.threadName}`, 705 }, 706 funcRow 707 ); 708 funcRow.canvasRestore(context); 709 }; 710 processRow.addChildTraceRowAfter(funcRow, threadRow); 711 } 712 } 713 } 714 let durTime = new Date().getTime() - time; 715 info('The time to load the Process data is: ', durTime); 716 } 717 718 addStartUpRow(processRow: TraceRow<ProcessStruct>) { 719 processRow.setAttribute('hasStartup','true'); 720 let startupRow: TraceRow<AppStartupStruct> = TraceRow.skeleton<AppStartupStruct>(); 721 startupRow.rowId = `app-start-${processRow.rowId}`; 722 startupRow.rowType = TraceRow.ROW_TYPE_APP_STARTUP; 723 startupRow.rowParentId = `${processRow.rowId}`; 724 startupRow.rowHidden = !processRow.expansion; 725 startupRow.index = 0; 726 startupRow.style.height = '30px'; 727 startupRow.style.width = `100%`; 728 startupRow.name = `App Startups`; 729 startupRow.setAttribute('children', ''); 730 startupRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 731 startupRow.selectChangeHandler = this.trace.selectChangeHandler; 732 startupRow.supplier = () => 733 queryProcessStartup(parseInt(processRow.rowId!)).then((res) => { 734 if (res.length <= 0) { 735 startupRow.rowDiscard = true; 736 this.trace.refreshCanvas(true); 737 } 738 for (let i = 0; i < res.length; i++) { 739 if (res[i].startName! < 4 && i < res.length - 1) { 740 res[i].endItid = res[i + 1].itid; 741 } 742 } 743 return res; 744 }); 745 startupRow.focusHandler = (ev) => {}; 746 startupRow.onThreadHandler = (useCache) => { 747 let context = startupRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 748 startupRow.canvasSave(context); 749 (renders['app-start-up'] as AppStartupRender).renderMainThread( 750 { 751 context: context, 752 useCache: useCache, 753 type: `app-startup ${processRow.rowId}`, 754 }, 755 startupRow 756 ); 757 startupRow.canvasRestore(context); 758 }; 759 processRow.addChildTraceRow(startupRow); 760 return startupRow; 761 } 762 763 addSoInitRow(processRow: TraceRow<ProcessStruct>, maxDepth: number) { 764 processRow.setAttribute('hasStaticInit','true'); 765 let maxHeight = (maxDepth + 1) * 20; 766 let soRow: TraceRow<SoStruct> = TraceRow.skeleton<SoStruct>(); 767 soRow.rowId = `app-start-${processRow.rowId}`; 768 soRow.rowType = TraceRow.ROW_TYPE_STATIC_INIT; 769 soRow.rowParentId = `${processRow.rowId}`; 770 soRow.rowHidden = !processRow.expansion; 771 soRow.index = 0; 772 soRow.style.height = `${maxHeight}px`; 773 soRow.style.width = `100%`; 774 soRow.name = `Static Initialization`; 775 soRow.setAttribute('children', ''); 776 soRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 777 soRow.selectChangeHandler = this.trace.selectChangeHandler; 778 soRow.supplier = () => 779 queryProcessSoInitData(parseInt(processRow.rowId!)).then((res) => { 780 if (res.length <= 0) { 781 soRow.rowDiscard = true; 782 this.trace.refreshCanvas(true); 783 } 784 res.forEach(so => { 785 if (so.soName) { 786 so.soName = so.soName.replace('dlopen: ', ''); 787 } 788 }) 789 return res; 790 }); 791 soRow.focusHandler = (ev) => {}; 792 soRow.onThreadHandler = (useCache) => { 793 let context = soRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 794 soRow.canvasSave(context); 795 (renders['app-so-init'] as SoRender).renderMainThread( 796 { 797 context: context, 798 useCache: useCache, 799 type: `static-init ${processRow.rowId}`, 800 }, 801 soRow 802 ); 803 soRow.canvasRestore(context); 804 }; 805 processRow.addChildTraceRow(soRow); 806 return soRow; 807 } 808 809 insertAfter(newEl: HTMLElement, targetEl: HTMLElement) { 810 let parentEl = targetEl.parentNode; 811 if (parentEl!.lastChild == targetEl) { 812 parentEl!.appendChild(newEl); 813 } else { 814 parentEl!.insertBefore(newEl, targetEl.nextSibling); 815 } 816 } 817} 818