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