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 { ProcessRender, ProcessStruct } from '../../database/ui-worker/ProcedureWorkerProcess'; 21import { ThreadRender, ThreadStruct } from '../../database/ui-worker/ProcedureWorkerThread'; 22import { FuncRender, FuncStruct } from '../../database/ui-worker/ProcedureWorkerFunc'; 23import { MemRender, ProcessMemStruct } from '../../database/ui-worker/ProcedureWorkerMem'; 24import { folderSupplier, folderThreadHandler, getRowContext, rowThreadHandler, SpChartManager } from './SpChartManager'; 25import { JankRender, JankStruct } from '../../database/ui-worker/ProcedureWorkerJank'; 26import { isFrameContainPoint, ns2xByTimeShaft, PairPoint } from '../../database/ui-worker/ProcedureWorkerCommon'; 27import { AppStartupRender, AppStartupStruct } from '../../database/ui-worker/ProcedureWorkerAppStartup'; 28import { SoRender, SoStruct } from '../../database/ui-worker/ProcedureWorkerSoInit'; 29import { FlagsConfig } from '../SpFlags'; 30import { processDataSender } from '../../database/data-trafic/process/ProcessDataSender'; 31import { threadDataSender } from '../../database/data-trafic/process/ThreadDataSender'; 32import { funcDataSender } from '../../database/data-trafic/process/FuncDataSender'; 33import { processMemDataSender } from '../../database/data-trafic/process/ProcessMemDataSender'; 34import { processStartupDataSender } from '../../database/data-trafic/process/ProcessStartupDataSender'; 35import { processSoInitDataSender } from '../../database/data-trafic/process/ProcessSoInitDataSender'; 36import { processExpectedDataSender } from '../../database/data-trafic/process/ProcessExpectedDataSender'; 37import { processActualDataSender } from '../../database/data-trafic/process/ProcessActualDataSender'; 38import { processDeliverInputEventDataSender } from '../../database/data-trafic/process/ProcessDeliverInputEventDataSender'; 39import { processTouchEventDispatchDataSender } from '../../database/data-trafic/process/ProcessTouchEventDispatchDataSender'; 40import { getMaxDepthByTid, queryProcessAsyncFunc, queryProcessAsyncFuncCat } from '../../database/sql/Func.sql'; 41import { queryMemFilterIdMaxValue } from '../../database/sql/Memory.sql'; 42import { queryAllSoInitNames, queryAllSrcSlices, queryEventCountMap } from '../../database/sql/SqlLite.sql'; 43import { 44 queryProcessByTable, 45 queryProcessContentCount, 46 queryProcessMem, 47 queryProcessSoMaxDepth, 48 queryProcessThreadsByTable, 49 queryStartupPidArray, 50 queryRsProcess, 51 queryTaskPoolProcessIds, 52 queryDistributedRelationData, 53} from '../../database/sql/ProcessThread.sql'; 54import { queryAllJankProcess } from '../../database/sql/Janks.sql'; 55import { BaseStruct } from '../../bean/BaseStruct'; 56import { promises } from 'dns'; 57import { HangStruct } from '../../database/ui-worker/ProcedureWorkerHang'; 58import { hangDataSender } from '../../database/data-trafic/HangDataSender'; 59import { SpHangChart } from './SpHangChart'; 60import { queryHangData } from '../../database/sql/Hang.sql'; 61import { EmptyRender } from '../../database/ui-worker/cpu/ProcedureWorkerCPU'; 62import { renders } from '../../database/ui-worker/ProcedureWorker'; 63 64const FOLD_HEIGHT = 24; 65export class SpProcessChart { 66 private readonly trace: SpSystemTrace; 67 private processAsyncFuncMap: unknown = {}; 68 private processAsyncFuncArray: unknown[] = []; 69 private processAsyncFuncCatMap: unknown = {}; 70 private eventCountMap: unknown; 71 private processThreads: Array<ThreadStruct> = []; 72 private processMem: Array<unknown> = []; 73 private processThreadCountMap: Map<number, number> = new Map(); 74 private processThreadDataCountMap: Map<number, number> = new Map(); 75 private processFuncDataCountMap: Map<number, number> = new Map(); 76 private processMemDataCountMap: Map<number, number> = new Map(); 77 private threadFuncMaxDepthMap: Map<string, number> = new Map(); 78 private startupProcessArr: { pid: number }[] = []; 79 private processSoMaxDepth: { pid: number; maxDepth: number }[] = []; 80 private filterIdMaxValue: Map<number, number> = new Map(); 81 private soInitNameMap: Map<number, string> = new Map(); 82 private processSrcSliceMap: Map<number, string> = new Map(); 83 private distributedDataMap: Map< 84 string, 85 { 86 chainId: string; 87 spanId: string; 88 parentSpanId: string; 89 chainFlag: string; 90 traceId: string 91 } 92 > = new Map(); 93 private renderRow: TraceRow<BaseStruct> | null = null; 94 private loadAppStartup: boolean = false; 95 private isDistributed: boolean = false; 96 private traceId?: string | undefined; 97 private parentRow: TraceRow<BaseStruct> | undefined; 98 static asyncFuncCache: unknown[] = []; 99 static threadStateList: Map<string, unknown> = new Map(); 100 static processRowSortMap: Map<string, unknown> = new Map(); 101 private sameThreadFolder!: TraceRow<ProcessStruct>; 102 103 private hangProcessSet: Set<number> = new Set(); 104 constructor(trace: SpSystemTrace) { 105 this.trace = trace; 106 } 107 108 clearCache(): void { 109 this.processAsyncFuncArray = []; 110 this.processAsyncFuncMap = {}; 111 this.processAsyncFuncCatMap = {}; 112 this.eventCountMap = {}; 113 this.processThreads = []; 114 this.processMem = []; 115 this.processThreadCountMap.clear(); 116 this.processThreadDataCountMap.clear(); 117 this.processFuncDataCountMap.clear(); 118 this.processMemDataCountMap.clear(); 119 this.threadFuncMaxDepthMap.clear(); 120 this.startupProcessArr = []; 121 this.processSoMaxDepth = []; 122 this.filterIdMaxValue.clear(); 123 this.soInitNameMap.clear(); 124 this.processSrcSliceMap.clear(); 125 this.distributedDataMap.clear(); 126 this.renderRow = null; 127 SpProcessChart.asyncFuncCache = []; 128 if (this.parentRow) { 129 this.parentRow.clearMemory(); 130 this.parentRow = undefined; 131 } 132 } 133 134 initAsyncFuncData = async (traceRange: { startTs: number; endTs: number }, traceId?: string): Promise<void> => { 135 let asyncFuncList: unknown[] = await queryProcessAsyncFunc(traceRange, traceId); 136 for (const func of asyncFuncList) { 137 //@ts-ignore 138 func.funName = this.traceId ? Utils.getInstance().getCallStatckMap().get(`${this.traceId}_${func.id}`) : Utils.getInstance().getCallStatckMap().get(func.id); //@ts-ignore 139 func.threadName = Utils.getInstance().getThreadMap(traceId).get(func.tid); 140 } 141 info('AsyncFuncData Count is: ', asyncFuncList!.length); 142 this.processAsyncFuncArray = asyncFuncList; 143 this.processAsyncFuncMap = Utils.groupBy(asyncFuncList, 'pid'); 144 145 let asyncFuncCatList: unknown[] = await queryProcessAsyncFuncCat(traceRange); 146 info('AsyncFuncCatData Count is: ', asyncFuncCatList!.length); 147 this.processAsyncFuncCatMap = Utils.groupBy(asyncFuncCatList, 'pid'); 148 }; 149 150 initDeliverInputEvent = async (): Promise<void> => { 151 let row = TraceRow.skeleton(); 152 row.rowId = 'DeliverInputEvent'; 153 row.index = 0; 154 row.rowType = TraceRow.ROW_TYPE_DELIVER_INPUT_EVENT; 155 row.rowParentId = ''; 156 row.folder = true; 157 row.style.height = '40px'; 158 row.name = 'DeliverInputEvent'; 159 // @ts-ignore 160 row.supplier = folderSupplier(); 161 row.onThreadHandler = folderThreadHandler(row, this.trace); 162 163 let asyncFuncGroup = Utils.groupBy( 164 //@ts-ignore 165 this.processAsyncFuncArray.filter((it) => it.funName === 'deliverInputEvent'), 166 'pid' 167 ); // @ts-ignore 168 if (Reflect.ownKeys(asyncFuncGroup).length > 0) { 169 this.trace.rowsEL?.appendChild(row); 170 } // @ts-ignore 171 Reflect.ownKeys(asyncFuncGroup).map((key: unknown) => { 172 // @ts-ignore 173 let asyncFuncGroups: Array<unknown> = asyncFuncGroup[key]; 174 if (asyncFuncGroups.length > 0) { 175 //@ts-ignore 176 row.addChildTraceRow(this.createDeliverInputEventRow(row, key, asyncFuncGroups)); 177 } 178 }); 179 }; 180 181 private createDeliverInputEventRow( 182 //@ts-ignore 183 parentRow: TraceRow<unknown>, 184 key: number, 185 asyncFuncGroups: Array<unknown> 186 ): TraceRow<FuncStruct> { 187 let funcRow = TraceRow.skeleton<FuncStruct>(); 188 //@ts-ignore 189 funcRow.rowId = `${asyncFuncGroups[0].funName}-${key}`; //@ts-ignore 190 funcRow.asyncFuncName = asyncFuncGroups[0].funName; 191 funcRow.asyncFuncNamePID = key; 192 funcRow.rowType = TraceRow.ROW_TYPE_FUNC; 193 funcRow.enableCollapseChart(FOLD_HEIGHT, this.trace); //允许折叠泳道图 194 funcRow.rowParentId = `${parentRow.rowId}`; 195 funcRow.rowHidden = !parentRow.expansion; 196 funcRow.style.width = '100%'; 197 //@ts-ignore 198 funcRow.name = `${asyncFuncGroups[0].funName} ${key}`; 199 funcRow.setAttribute('children', ''); 200 funcRow.supplierFrame = async (): Promise<FuncStruct[]> => { 201 const res = await processDeliverInputEventDataSender(key, funcRow!); 202 this.deliverInputEventSendCallback(res, funcRow, asyncFuncGroups); 203 return res; 204 }; 205 funcRow.findHoverStruct = (): void => { 206 FuncStruct.hoverFuncStruct = funcRow.getHoverStruct(); 207 }; 208 funcRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 209 funcRow.selectChangeHandler = this.trace.selectChangeHandler; 210 funcRow.onThreadHandler = rowThreadHandler<FuncRender>( 211 'func', 212 'context', 213 { 214 //@ts-ignore 215 type: `func-${asyncFuncGroups[0].funName}-${key}`, 216 }, 217 funcRow, 218 this.trace 219 ); 220 return funcRow; 221 } 222 //@ts-ignore 223 private deliverInputEventSendCallback( 224 res: Array<unknown>, //@ts-ignore 225 funcRow: TraceRow<unknown>, 226 asyncFuncGroups: Array<unknown> 227 ): void { 228 let isIntersect = ( 229 left: unknown, 230 right: unknown 231 ): boolean => //@ts-ignore 232 Math.max(left.startTs + left.dur, right.startTs + right.dur) - Math.min(left.startTs, right.startTs) < 233 //@ts-ignore 234 left.dur + right.dur; 235 let depths: unknown = []; 236 let createDepth = (currentDepth: number, index: number): void => { 237 //@ts-ignore 238 if (depths[currentDepth] === undefined || !isIntersect(depths[currentDepth], res[index])) { 239 //@ts-ignore 240 res[index].depth = currentDepth; 241 //@ts-ignore 242 depths[currentDepth] = res[index]; 243 } else { 244 createDepth(++currentDepth, index); 245 } 246 }; 247 res.forEach((it, i): void => { 248 //@ts-ignore 249 res[i].funName = this.traceId ? Utils.getInstance().getCallStatckMap().get(`${this.traceId}_${res[i].id!}`) : Utils.getInstance().getCallStatckMap().get(res[i].id!); //@ts-ignore 250 res[i].threadName = Utils.getInstance().getThreadMap().get(res[i].tid!); 251 //@ts-ignore 252 if (it.dur === -1 || it.dur === null || it.dur === undefined) { 253 //@ts-ignore 254 it.dur = (TraceRow.range?.endNS || 0) - it.startTs; 255 //@ts-ignore 256 it.flag = 'Did not end'; 257 } 258 createDepth(0, i); 259 }); 260 if (funcRow && !funcRow.isComplete) { 261 //@ts-ignore 262 let max = Math.max(...asyncFuncGroups.map((it) => it.depth || 0)) + 1; 263 let maxHeight = max * 18 + 6; 264 funcRow.style.height = `${maxHeight}px`; 265 funcRow.setAttribute('height', `${maxHeight}`); 266 } 267 } 268 269 initTouchEventDispatch = async (): Promise<void> => { 270 let row = TraceRow.skeleton() as TraceRow<ProcessStruct>; 271 row.rowId = 'TouchEventDispatch'; 272 row.index = 0; 273 row.rowType = TraceRow.ROW_TYPE_TOUCH_EVENT_DISPATCH; 274 row.rowParentId = ''; 275 row.folder = true; 276 row.style.height = '40px'; 277 row.name = 'TouchEventDispatch'; 278 //@ts-ignore 279 row.supplier = folderSupplier(); 280 row.onThreadHandler = folderThreadHandler(row, this.trace); 281 282 let asyncFuncGroup = Utils.groupBy( 283 //@ts-ignore 284 this.processAsyncFuncArray.filter((it) => it.funName === 'H:touchEventDispatch' || it.funName === 'H:TouchEventDispatch'), 285 'pid' 286 ); 287 //@ts-ignore 288 if (Reflect.ownKeys(asyncFuncGroup).length > 0) { 289 this.trace.rowsEL?.appendChild(row); 290 } 291 //@ts-ignore 292 Reflect.ownKeys(asyncFuncGroup).map((key: unknown) => { 293 //@ts-ignore 294 let asyncFuncGroups: Array<unknown> = asyncFuncGroup[key]; 295 if (asyncFuncGroups.length > 0) { 296 // @ts-ignore 297 row.addChildTraceRow(this.createTouchEventDispatchRow(row, key, asyncFuncGroups)); 298 } 299 }); 300 }; 301 302 private createTouchEventDispatchRow( 303 parentRow: TraceRow<ProcessStruct>, 304 key: number, 305 asyncFuncGroups: Array<unknown> 306 ): TraceRow<FuncStruct> { 307 let funcRow = TraceRow.skeleton<FuncStruct>(); 308 // @ts-ignore 309 funcRow.rowId = `${asyncFuncGroups[0].funName}-${key}`; 310 // @ts-ignore 311 funcRow.asyncFuncName = asyncFuncGroups[0].funName; 312 funcRow.asyncFuncNamePID = key; 313 funcRow.rowType = TraceRow.ROW_TYPE_FUNC; 314 funcRow.enableCollapseChart(FOLD_HEIGHT, this.trace); //允许折叠泳道图 315 funcRow.rowParentId = `${parentRow.rowId}`; 316 funcRow.rowHidden = !parentRow.expansion; 317 funcRow.style.width = '100%'; 318 funcRow.style.height = '24px'; 319 //@ts-ignore 320 funcRow.name = `${asyncFuncGroups[0].funName} ${key}`; 321 funcRow.setAttribute('children', ''); 322 //@ts-ignore 323 funcRow.supplierFrame = (): Promise => { 324 return processTouchEventDispatchDataSender(key, funcRow!).then((res: Array<unknown>) => { 325 this.touchEventDispatchSendCallback(res, funcRow, asyncFuncGroups); 326 return res; 327 }); 328 }; 329 330 funcRow.findHoverStruct = (): void => { 331 FuncStruct.hoverFuncStruct = funcRow.getHoverStruct(); 332 }; 333 funcRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 334 funcRow.selectChangeHandler = this.trace.selectChangeHandler; 335 funcRow.onThreadHandler = rowThreadHandler<FuncRender>( 336 'func', 337 'context', 338 { 339 // @ts-ignore 340 type: `func-${asyncFuncGroups[0].funName}-${key}`, 341 }, 342 funcRow, 343 this.trace 344 ); 345 return funcRow; 346 } 347 348 // @ts-ignore 349 private touchEventDispatchSendCallback(res: Array<unknown>, funcRow: TraceRow<unknown>, asyncFuncGroups: Array<unknown>): void { 350 let isIntersect = (left: unknown, right: unknown): boolean => 351 // @ts-ignore 352 Math.max(left.startTs + left.dur, right.startTs + right.dur) - Math.min(left.startTs, right.startTs) < 353 // @ts-ignore 354 left.dur + right.dur; 355 let depths: unknown = []; 356 let createDepth = (currentDepth: number, index: number): void => { 357 // @ts-ignore 358 if (depths[currentDepth] === undefined || !isIntersect(depths[currentDepth], res[index])) { 359 //@ts-ignore 360 res[index].depth = currentDepth; 361 // @ts-ignore 362 depths[currentDepth] = res[index]; 363 } else { 364 createDepth(++currentDepth, index); 365 } 366 }; 367 res.forEach((it, i) => { 368 //@ts-ignore 369 res[i].funName = this.traceId ? Utils.getInstance().getCallStatckMap().get(`${this.traceId}_${res[i].id!}`) : Utils.getInstance().getCallStatckMap().get(res[i].id!); 370 //@ts-ignore 371 res[i].threadName = Utils.getInstance().getThreadMap().get(res[i].tid!); 372 //@ts-ignore 373 if (it.dur === -1 || it.dur === null || it.dur === undefined) { 374 //@ts-ignore 375 it.dur = (TraceRow.range?.endNS || 0) - it.startTs; 376 //@ts-ignore 377 it.flag = 'Did not end'; 378 } 379 createDepth(0, i); 380 }); 381 if (funcRow && !funcRow.isComplete) { 382 // @ts-ignore 383 let max = Math.max(...asyncFuncGroups.map((it) => it.depth || 0)) + 1; 384 let maxHeight = max * 18 + 6; 385 funcRow.style.height = `${maxHeight}px`; 386 funcRow.setAttribute('height', `${maxHeight}`); 387 } 388 } 389 390 // @ts-ignore 391 async init(isDistributed: boolean, parentRow?: TraceRow<unknown>, traceId?: string): Promise<void> { 392 this.traceId = traceId; 393 // @ts-ignore 394 this.parentRow = parentRow; 395 this.isDistributed = isDistributed; 396 await this.prepareData(traceId); 397 if ( 398 //@ts-ignore 399 this.eventCountMap.print === 0 && //@ts-ignore 400 this.eventCountMap.tracing_mark_write === 0 && //@ts-ignore 401 this.eventCountMap.sched_switch === 0 402 ) { 403 return; 404 } 405 let time = new Date().getTime(); 406 let processSortArray = Array.from(SpProcessChart.processRowSortMap); 407 // @ts-ignore 408 processSortArray.sort((a: Array<unknown>, b: Array<unknown>) => 409 // @ts-ignore 410 b[1] - a[1] 411 ); 412 let processFromTable = await queryProcessByTable(traceId); 413 let processList = Utils.sortThreadRow(processSortArray, processFromTable, 'process'); 414 SpProcessChart.processRowSortMap.clear(); 415 let allJankProcess: Array<number> = []; 416 let allTaskPoolPid: Array<{ pid: number }> = []; 417 let renderServiceProcess: unknown[] = []; 418 if (!this.isDistributed) { 419 let allJankProcessData = await queryAllJankProcess(); 420 if (allJankProcessData.length > 0) { 421 allJankProcessData.forEach((name, index) => { 422 allJankProcess.push(name.pid!); 423 }); 424 } 425 if (FlagsConfig.getFlagsConfigEnableStatus('TaskPool')) { 426 allTaskPoolPid = await queryTaskPoolProcessIds(); 427 } 428 renderServiceProcess = await queryRsProcess(); 429 } 430 431 // @ts-ignore 432 info('ProcessList Data size is: ', processList!.length); 433 434 435 this.hangProcessSet = new Set<number>((await queryHangData()).map(item => item.id)); 436 437 // @ts-ignore 438 await this.initProcessRow(processList, allTaskPoolPid, allJankProcess, renderServiceProcess, traceId); 439 let durTime = new Date().getTime() - time; 440 info('The time to load the Process data is: ', durTime); 441 } 442 443 private async prepareData(traceId?: string): Promise<void> { 444 if (!this.isDistributed) { 445 let maxValues = await queryMemFilterIdMaxValue(); 446 maxValues.forEach((it) => { 447 this.filterIdMaxValue.set(it.filterId, it.maxValue); 448 }); 449 let soInitNamesArray = await queryAllSoInitNames(); 450 soInitNamesArray.forEach((it) => { 451 // @ts-ignore 452 this.soInitNameMap.set(it.id, it.name); 453 }); 454 let processSrcSliceArray = await queryAllSrcSlices(); 455 processSrcSliceArray.forEach((it) => { 456 // @ts-ignore 457 this.processSrcSliceMap.set(it.id, it.src); 458 }); 459 this.processMem = await queryProcessMem(); 460 info('The amount of initialized process memory data is : ', this.processMem!.length); 461 this.loadAppStartup = FlagsConfig.getFlagsConfigEnableStatus('AppStartup'); 462 info('Prepare App startup data '); 463 if (this.loadAppStartup) { 464 this.startupProcessArr = await queryStartupPidArray(); 465 this.processSoMaxDepth = await queryProcessSoMaxDepth(); 466 } 467 } 468 let threadFuncMaxDepthArray = await getMaxDepthByTid(traceId); 469 info('Gets the maximum tier per thread , tid and maxDepth'); 470 threadFuncMaxDepthArray.forEach((it) => { 471 //@ts-ignore 472 this.threadFuncMaxDepthMap.set(`${it.ipid}-${it.tid}`, it.maxDepth); 473 }); 474 info('convert tid and maxDepth array to map'); 475 let pidCountArray = await queryProcessContentCount(traceId); 476 info('fetch per process pid,switch_count,thread_count,slice_count,mem_count'); 477 pidCountArray.forEach((it) => { 478 //@ts-ignore 479 this.processThreadDataCountMap.set(it.pid, it.switch_count); 480 //@ts-ignore 481 this.processThreadCountMap.set(it.pid, it.thread_count); 482 //@ts-ignore 483 this.processFuncDataCountMap.set(it.pid, it.slice_count); 484 //@ts-ignore 485 this.processMemDataCountMap.set(it.pid, it.mem_count); 486 }); 487 let eventCountList: Array<unknown> = await queryEventCountMap(traceId); 488 this.eventCountMap = eventCountList.reduce((pre, current) => { 489 //@ts-ignore 490 pre[`${current.eventName}`] = current.count; 491 return pre; 492 }, {}); 493 // threadStateList转数组 494 let threadArray = Array.from(SpProcessChart.threadStateList); 495 // @ts-ignore 排序 496 threadArray.sort((a: Array<unknown>, b: Array<unknown>) => 497 // @ts-ignore 498 b[1] - a[1]); 499 let queryProcessThreadsByTableResult = await queryProcessThreadsByTable(traceId); 500 // @ts-ignore 501 // 全量threads排序 502 // @ts-ignore 503 this.processThreads = Utils.sortThreadRow(threadArray, queryProcessThreadsByTableResult, 'thread'); 504 SpProcessChart.threadStateList.clear(); 505 let distributedDataLists = await queryDistributedRelationData(traceId); 506 distributedDataLists.forEach((item) => { 507 this.distributedDataMap.set(`${item.id}_${traceId}`, { 508 chainId: item.chainId, 509 spanId: item.spanId, 510 parentSpanId: item.parentSpanId, 511 chainFlag: item.chainFlag, 512 traceId: traceId!, 513 }); 514 }); 515 info('The amount of initialized process threads data is : ', this.processThreads!.length); 516 } 517 518 private async initProcessRow( 519 pArr: Array<unknown>, 520 allTaskPoolPid: Array<{ pid: number }>, 521 jankArr: Array<number>, 522 rsProcess: Array<unknown>, 523 traceId?: string 524 ): Promise<void> { 525 for (let i = 0; i < pArr.length; i++) { 526 const it = pArr[i] as { 527 pid: number; 528 processName: string | null 529 }; 530 if ( 531 (this.processThreadDataCountMap.get(it.pid) || 0) === 0 && 532 (this.processThreadCountMap.get(it.pid) || 0) === 0 && 533 (this.processFuncDataCountMap.get(it.pid) || 0) === 0 && 534 (this.processMemDataCountMap.get(it.pid) || 0) === 0 535 ) { 536 continue; 537 } 538 let processRow = this.createProcessRow(i, it, allTaskPoolPid); 539 if (this.parentRow) { 540 this.parentRow.addChildTraceRow(processRow); 541 } else { 542 this.trace.rowsEL?.appendChild(processRow); 543 } 544 /* App Startup row*/ 545 let startupRow: TraceRow<AppStartupStruct> | undefined = undefined; 546 let soRow: TraceRow<SoStruct> | undefined = undefined; 547 let actualRow: TraceRow<JankStruct> | null = null; 548 let expectedRow: TraceRow<JankStruct> | null = null; 549 //@ts-ignore 550 let currentPid = it.pid; 551 if (!this.isDistributed) { 552 if (this.loadAppStartup) { 553 if (this.startupProcessArr.find((sp) => sp.pid === currentPid)) { 554 startupRow = this.addStartUpRow(processRow); 555 } 556 let maxSoDepth = this.processSoMaxDepth.find((md) => md.pid === currentPid); 557 if (maxSoDepth) { 558 soRow = this.addSoInitRow(processRow, maxSoDepth.maxDepth); 559 } 560 } 561 if (jankArr.indexOf(currentPid) > -1) { 562 expectedRow = this.addExpectedRow(it, processRow, rsProcess); 563 actualRow = this.addActualRow(it, processRow, rsProcess); 564 } 565 } 566 this.renderRow = null; 567 if (it.processName === 'render_service') { 568 //@ts-ignore 569 this.addThreadList(it, processRow, expectedRow, actualRow, soRow, startupRow, traceId); //@ts-ignore 570 this.addProcessMemInfo(it, processRow); //@ts-ignore 571 this.addAsyncFunction(it, processRow);//@ts-ignore 572 this.addAsyncCatFunction(it, processRow); 573 } else { 574 //@ts-ignore 575 this.addAsyncFunction(it, processRow); //@ts-ignore 576 this.addProcessMemInfo(it, processRow); //@ts-ignore 577 this.addThreadList(it, processRow, expectedRow, actualRow, soRow, startupRow, traceId);//@ts-ignore 578 this.addAsyncCatFunction(it, processRow); 579 } 580 this.addProcessRowListener(processRow, actualRow); 581 if (!this.isDistributed) { 582 //@ts-ignore 583 await this.trace.chartManager?.frameTimeChart.initAnimatedScenesChart(processRow, it, expectedRow!, actualRow!); 584 } 585 } 586 } 587 588 private createProcessRow( 589 index: number, 590 process: unknown, 591 allTaskPoolPid: Array<{ pid: number }> 592 ): TraceRow<ProcessStruct> { 593 let processRow = TraceRow.skeleton<ProcessStruct>(this.traceId); //@ts-ignore 594 processRow.rowId = `${process.pid}`; 595 processRow.index = index; 596 processRow.rowType = TraceRow.ROW_TYPE_PROCESS; 597 processRow.rowParentId = ''; 598 processRow.style.height = '40px'; 599 processRow.folder = true; 600 if ( 601 //@ts-ignore 602 SpChartManager.APP_STARTUP_PID_ARR.find((pid) => pid === process.pid) !== undefined || //@ts-ignore 603 process.processName === 'render_service' 604 ) { 605 processRow.addTemplateTypes('AppStartup'); 606 } 607 if (allTaskPoolPid.find((process) => process.pid === process.pid) !== undefined) { 608 processRow.addTemplateTypes('TaskPool'); 609 } //@ts-ignore 610 processRow.name = `${process.processName || 'Process'} ${process.pid}`; //@ts-ignore 611 processRow.supplierFrame = (): Promise<Array<unknown>> => { 612 //@ts-ignore 613 return processDataSender(process.pid || -1, processRow, this.traceId); 614 }; 615 processRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 616 processRow.selectChangeHandler = this.trace.selectChangeHandler; 617 processRow.onThreadHandler = rowThreadHandler<ProcessRender>( 618 'process', 619 'context', 620 { 621 //@ts-ignore 622 pid: process.pid, //@ts-ignore 623 type: `process ${processRow.index} ${process.processName}`, 624 }, 625 processRow, 626 this.trace 627 ); 628 return processRow; 629 } 630 631 addProcessRowListener(processRow: TraceRow<ProcessStruct>, actualRow: TraceRow<JankStruct> | null): void { 632 let offsetYTimeOut: unknown = undefined; 633 processRow.addEventListener('expansion-change', (e: unknown) => { 634 JankStruct.delJankLineFlag = false; 635 if (offsetYTimeOut) { 636 //@ts-ignore 637 clearTimeout(offsetYTimeOut); 638 } 639 if (JankStruct.selectJankStruct !== null && JankStruct.selectJankStruct !== undefined) { 640 //@ts-ignore 641 if (e.detail.expansion) { 642 offsetYTimeOut = setTimeout(() => { 643 this.trace.linkNodes.forEach((linkNodeItem) => this.handler1(e, linkNodeItem, actualRow)); 644 }, 300); 645 } else { 646 if (JankStruct!.selectJankStruct) { 647 JankStruct.selectJankStructList?.push(<JankStruct>JankStruct!.selectJankStruct); 648 } 649 offsetYTimeOut = setTimeout(() => { 650 this.trace.linkNodes?.forEach((linkProcessItem) => this.handler2(e, linkProcessItem, processRow)); 651 }, 300); 652 } 653 } else if (FuncStruct.selectFuncStruct) { //@ts-ignore 654 if (e.detail.expansion) { 655 offsetYTimeOut = setTimeout(() => { 656 this.trace.linkNodes.forEach((linkNodeItem) => this.handler3(e, linkNodeItem)); 657 }, 300); 658 } else { 659 offsetYTimeOut = setTimeout(() => { 660 this.trace.linkNodes?.forEach((linkProcessItem) => { 661 this.handler4(e, linkProcessItem, processRow); 662 JankStruct.selectJankStructList = []; 663 }); 664 }, 300); 665 } 666 this.trace.resetDistributedLine(); 667 668 } else { 669 //@ts-ignore 670 if (e.detail.expansion) { 671 offsetYTimeOut = setTimeout(() => { 672 this.trace.linkNodes.forEach((linkNodeItem) => this.handler3(e, linkNodeItem)); 673 }, 300); 674 } else { 675 if (ThreadStruct!.selectThreadStruct) { 676 ThreadStruct.selectThreadStructList?.push(<ThreadStruct>ThreadStruct!.selectThreadStruct); 677 } 678 offsetYTimeOut = setTimeout(() => { 679 this.trace.linkNodes?.forEach((linkProcessItem) => { 680 this.handler4(e, linkProcessItem, processRow); 681 JankStruct.selectJankStructList = []; 682 }); 683 }, 300); 684 } 685 } 686 let refreshTimeOut = setTimeout(() => { 687 this.trace.refreshCanvas(true); 688 clearTimeout(refreshTimeOut); 689 }, 360); 690 }); 691 } 692 693 handler1(e: unknown, linkItem: PairPoint[], actualRow: TraceRow<JankStruct> | null): void { 694 JankStruct.selectJankStructList?.forEach((selectProcessStruct: unknown) => { 695 //@ts-ignore 696 if (e.detail.rowId === selectProcessStruct.pid) { 697 //@ts-ignore 698 JankStruct.selectJankStruct = selectProcessStruct; //@ts-ignore 699 JankStruct.hoverJankStruct = selectProcessStruct; 700 } 701 }); 702 this.updatePairPointTranslateY(linkItem[0]); 703 linkItem[0].y = linkItem[0].rowEL!.translateY! + linkItem[0].offsetY; 704 this.updatePairPointTranslateY(linkItem[1]); 705 linkItem[1].y = linkItem[1].rowEL!.translateY! + linkItem[1].offsetY; 706 if (actualRow) { 707 //@ts-ignore 708 if (linkItem[0].rowEL.rowId === e.detail.rowId) { 709 linkItem[0].x = ns2xByTimeShaft(linkItem[0].ns, this.trace.timerShaftEL!); 710 linkItem[0].y = actualRow!.translateY! + linkItem[0].offsetY * 2; 711 linkItem[0].offsetY = linkItem[0].offsetY * 2; 712 //@ts-ignore 713 linkItem[0].rowEL = actualRow!; 714 //@ts-ignore 715 } else if (linkItem[1].rowEL.rowId === e.detail.rowId) { 716 linkItem[1].x = ns2xByTimeShaft(linkItem[1].ns, this.trace.timerShaftEL!); 717 linkItem[1].y = actualRow!.translateY! + linkItem[1].offsetY * 2; 718 linkItem[1].offsetY = linkItem[1].offsetY * 2; 719 //@ts-ignore 720 linkItem[1].rowEL = actualRow!; 721 } 722 } 723 } 724 725 handler2(e: unknown, linkItem: PairPoint[], processRow: TraceRow<ProcessStruct>): void { 726 this.updatePairPointTranslateY(linkItem[0]); 727 linkItem[0].y = linkItem[0].rowEL!.translateY! + linkItem[0].offsetY; 728 this.updatePairPointTranslateY(linkItem[1]); 729 linkItem[1].y = linkItem[1].rowEL!.translateY! + linkItem[1].offsetY; //@ts-ignore 730 if (linkItem[0].rowEL.rowParentId === e.detail.rowId) { 731 this.updatePairPoint(linkItem[0], processRow); //@ts-ignore 732 } else if (linkItem[1].rowEL.rowParentId === e.detail.rowId) { 733 this.updatePairPoint(linkItem[1], processRow); 734 } 735 } 736 737 handler3(e: unknown, linkItem: PairPoint[]): void { 738 ThreadStruct.selectThreadStructList?.forEach((selectProcessStruct: unknown) => { 739 //@ts-ignore 740 if (e.detail.rowId === selectProcessStruct.pid) { 741 //@ts-ignore 742 ThreadStruct.selectThreadStruct = selectProcessStruct; //@ts-ignore 743 ThreadStruct.hoverThreadStruct = selectProcessStruct; 744 } 745 }); 746 //@ts-ignore 747 linkItem[0].backrowEL = linkItem[0].sourcebackrowEL!.parentRowEl?.expansion ? 748 linkItem[0].sourcebackrowEL! : linkItem[0].sourcebackrowEL!.parentRowEl; 749 //@ts-ignore 750 linkItem[1].backrowEL = linkItem[1].sourcebackrowEL!.parentRowEl?.expansion ? 751 linkItem[1].sourcebackrowEL! : linkItem[1].sourcebackrowEL!.parentRowEl; 752 if (linkItem[0].rowEL.expansion && linkItem[0].backrowEL) { 753 this.updatePairPointTranslateY(linkItem[0]); 754 linkItem[0].x = ns2xByTimeShaft(linkItem[0].ns, this.trace.timerShaftEL!); 755 linkItem[0].y = linkItem[0].rowEL.translateY + linkItem[0].offsetY; 756 linkItem[0].offsetY = linkItem[0].offsetY * 2; 757 linkItem[0].rowEL = linkItem[0].backrowEL; 758 } 759 if (linkItem[1].rowEL.expansion && linkItem[1].backrowEL) { 760 this.updatePairPointTranslateY(linkItem[1]); 761 linkItem[1].x = ns2xByTimeShaft(linkItem[1].ns, this.trace.timerShaftEL!); 762 linkItem[1].y = linkItem[1].rowEL!.translateY! + linkItem[1].offsetY; 763 linkItem[1].offsetY = linkItem[1].offsetY * 2; 764 linkItem[1].rowEL = linkItem[1].backrowEL; 765 } 766 this.updatePairPointTranslateY(linkItem[0]); 767 this.updatePairPointTranslateY(linkItem[1]); 768 } 769 770 handler4(e: unknown, linkItem: PairPoint[], processRow: TraceRow<ProcessStruct>): void { 771 this.updatePairPointTranslateY(linkItem[0]); 772 linkItem[0].y = processRow!.translateY + linkItem[0].offsetY; 773 this.updatePairPointTranslateY(linkItem[1]); 774 linkItem[1].y = linkItem[1].rowEL!.translateY + linkItem[1].offsetY; //@ts-ignore 775 if (e.detail.rowId === 'sameThreadProcess') {//@ts-ignore 776 if (linkItem[0].rowEL.parentRowEl?.rowId === e.detail.rowId) { 777 //@ts-ignore 778 this.updatePairPoint(linkItem[0], processRow); 779 } //@ts-ignore 780 if (linkItem[1].rowEL.parentRowEl?.rowId === e.detail.rowId) { 781 this.updatePairPoint(linkItem[1], processRow); 782 } 783 } 784 //@ts-ignore 785 if (e.detail.rowId !== 'sameThreadProcess') {//@ts-ignore 786 if (linkItem[0].rowEL.rowParentId === e.detail.rowId) { 787 this.updatePairPoint(linkItem[0], processRow); 788 }//@ts-ignore 789 if (linkItem[1].rowEL.rowParentId === e.detail.rowId) { 790 this.updatePairPoint(linkItem[1], processRow); 791 } 792 } 793 } 794 795 updatePairPointTranslateY(pair: PairPoint): void { 796 let linkNodeYTimeOut: unknown = undefined; 797 if (linkNodeYTimeOut) { 798 //@ts-ignore 799 clearTimeout(linkNodeYTimeOut); 800 } 801 if (pair.rowEL.collect) { 802 pair.rowEL.translateY = pair.rowEL.getBoundingClientRect().top - 195; 803 linkNodeYTimeOut = setTimeout(() => { 804 if (this.trace.favoriteChartListEL?.collect1Expand === false) { // G1折叠 805 if (pair.rowEL.collectGroup === '1') { 806 pair.rowEL.translateY = 23; 807 } 808 } 809 if (this.trace.favoriteChartListEL?.collect2Expand === false) {// G2折叠 810 if (pair.rowEL.collectGroup === '2') { 811 if (this.trace.groupTitle1?.style.display === 'none') { 812 pair.rowEL.translateY = 23; 813 } else if (this.trace.groupTitle1?.style.display === 'flex' || this.trace.groupTitle1?.style.display === 'block') { 814 pair.rowEL.translateY = Number(this.trace.groupTitle1?.clientHeight) + Number(this.trace.collectEl1?.clientHeight) + 27; 815 } 816 } 817 } 818 }, 300); 819 } else { 820 pair.rowEL.translateY = pair.rowEL.offsetTop - this.trace.rowsPaneEL!.scrollTop; 821 } 822 let refreshTimeOut = setTimeout(() => { 823 this.trace.refreshCanvas(true); 824 clearTimeout(refreshTimeOut); 825 }, 300); 826 } 827 828 updatePairPoint(pair: PairPoint, processRow: TraceRow<ProcessStruct>): void { 829 if (!pair.rowEL.collect) { 830 pair.x = ns2xByTimeShaft(pair.ns, this.trace.timerShaftEL!); 831 pair.y = processRow!.translateY! + pair.offsetY / 2; 832 pair.offsetY = pair.offsetY / 2; 833 pair.rowEL = processRow!; 834 } 835 } 836 837 /* Janks Frames */ 838 //@ts-ignore 839 addExpectedRow( 840 process: unknown, //@ts-ignore 841 processRow: TraceRow<unknown>, 842 renderServiceProcess: Array<unknown> 843 ): TraceRow<JankStruct> { 844 let expectedRow = TraceRow.skeleton<JankStruct>(); //@ts-ignore 845 expectedRow.asyncFuncName = process.processName; //@ts-ignore 846 expectedRow.asyncFuncNamePID = process.pid; 847 expectedRow.rowType = TraceRow.ROW_TYPE_JANK; //@ts-ignore 848 expectedRow.rowParentId = `${process.pid}`; 849 expectedRow.rowHidden = !processRow.expansion; 850 expectedRow.style.width = '100%'; 851 expectedRow.name = 'Expected Timeline'; 852 expectedRow.addTemplateTypes('FrameTimeline'); 853 expectedRow.setAttribute('children', ''); 854 expectedRow.supplierFrame = async (): Promise<JankStruct[]> => { 855 //@ts-ignore 856 let res = await processExpectedDataSender(process.pid, expectedRow!); 857 this.jankSenderCallback(res, 'expected', process, expectedRow, renderServiceProcess); 858 return res; 859 }; 860 expectedRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 861 expectedRow.selectChangeHandler = this.trace.selectChangeHandler; 862 expectedRow.onThreadHandler = rowThreadHandler<JankRender>( 863 'jank', 864 'context', 865 { 866 type: 'expected_frame_timeline_slice', 867 }, 868 expectedRow, 869 this.trace 870 ); 871 if (this.renderRow) { 872 processRow.addChildTraceRowBefore(expectedRow, this.renderRow); 873 } else { 874 processRow.addChildTraceRow(expectedRow); 875 } 876 return expectedRow; 877 } 878 879 //@ts-ignore 880 addActualRow( 881 process: unknown, //@ts-ignore 882 processRow: TraceRow<unknown>, 883 renderServiceProcess: Array<unknown> 884 ): TraceRow<JankStruct> { 885 let actualRow = TraceRow.skeleton<JankStruct>(); 886 actualRow.rowType = TraceRow.ROW_TYPE_JANK; //@ts-ignore 887 actualRow.rowParentId = `${process.pid}`; 888 actualRow.rowHidden = !processRow.expansion; 889 actualRow.style.width = '100%'; 890 actualRow.name = 'Actual Timeline'; 891 actualRow.addTemplateTypes('FrameTimeline'); 892 actualRow.setAttribute('children', ''); 893 actualRow.supplierFrame = async (): Promise<JankStruct[]> => { 894 //@ts-ignore 895 let res = await processActualDataSender(process.pid, actualRow!); 896 this.jankSenderCallback(res, 'actual', process, actualRow, renderServiceProcess); 897 return res; 898 }; 899 actualRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 900 actualRow.selectChangeHandler = this.trace.selectChangeHandler; 901 actualRow.onThreadHandler = rowThreadHandler<JankRender>( 902 'jank', 903 'context', 904 { 905 type: 'actual_frame_timeline_slice', 906 }, 907 actualRow, 908 this.trace 909 ); 910 if (this.renderRow) { 911 processRow.addChildTraceRowBefore(actualRow, this.renderRow); 912 } else { 913 processRow.addChildTraceRow(actualRow); 914 } 915 return actualRow; 916 } 917 918 //@ts-ignore 919 addHangRow( 920 data: { 921 pid: number | null; 922 processName: string | null; 923 }, 924 processRow: TraceRow<ProcessStruct>, 925 threadRow: TraceRow<ThreadStruct>, 926 thread: unknown 927 ): TraceRow<HangStruct> | null { 928 if (this.hangProcessSet.has(data.pid!) && FlagsConfig.getFlagsConfigEnableStatus('Hangs Detection')) { 929 //@ts-ignore 930 if (data.pid === thread.tid) { 931 let hangsRow = TraceRow.skeleton<HangStruct>(); 932 hangsRow.rowType = TraceRow.ROW_TYPE_HANG_INNER; 933 hangsRow.rowId = `${data.processName ?? 'Process'} ${data.pid}`; 934 hangsRow.rowParentId = `${data.pid}`; 935 hangsRow.rowHidden = !processRow.expansion; 936 hangsRow.style.width = '100%'; 937 hangsRow.name = 'Hangs'; 938 hangsRow.addTemplateTypes('FrameTimeline'); 939 hangsRow.setAttribute('children', ''); 940 hangsRow.supplierFrame = async (): Promise<HangStruct[]> => { 941 let promiseData = hangDataSender(data.pid!, hangsRow); 942 if (promiseData === null) { 943 return new Promise<Array<HangStruct>>((resolve) => resolve([])); 944 } else { 945 return promiseData.then((resultHang: Array<HangStruct>) => 946 resultHang.map(hangItem => ({ 947 ...hangItem, 948 pname: data.processName ?? 'process', 949 type: SpHangChart.calculateHangType(hangItem.dur!), 950 content: SpHangChart.funcNameMap.get(hangItem.id!) 951 })) 952 ); 953 } 954 }; 955 hangsRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 956 hangsRow.selectChangeHandler = this.trace.selectChangeHandler; 957 hangsRow.findHoverStruct = (): void => { 958 HangStruct.hoverHangStruct = hangsRow.getHoverStruct(); 959 }; 960 hangsRow.onThreadHandler = rowThreadHandler<HangStruct>( 961 'hang', 962 'context', 963 { 964 type: 'hangs_frame_timeline_slice', 965 }, 966 hangsRow, 967 this.trace 968 ); 969 processRow.addChildTraceRowBefore(hangsRow, threadRow); 970 return hangsRow; 971 } 972 } 973 return null; 974 } 975 976 jankSenderCallback( 977 res: JankStruct[], 978 type: string, 979 process: unknown, 980 row: TraceRow<JankStruct>, 981 renderServiceProcess: Array<unknown>, 982 ): void { 983 let maxDepth: number = 1; 984 let unitHeight: number = 20; 985 for (let j = 0; j < res.length; j++) { 986 let struct = res[j]; 987 if (struct.depth! >= maxDepth) { 988 maxDepth = struct.depth! + 1; 989 } 990 if (type === 'actual') { 991 struct.src_slice = this.processSrcSliceMap.get(res[j].id!); 992 if (struct.tid && struct.tid !== struct.pid) { 993 struct.name = `${struct.name}-${struct.tid}`; 994 } 995 } 996 struct.cmdline = Utils.getInstance().getProcessMap().get(res[j].pid!); //@ts-ignore 997 if (res[j].pid! === renderServiceProcess[0].pid) { 998 struct.cmdline = 'render_service'; 999 struct.frameType = struct.cmdline; 1000 } else { 1001 struct.frameType = 'app'; 1002 } 1003 } 1004 if (row && !row.isComplete && res.length > 0) { 1005 let maxHeight: number = maxDepth * unitHeight; 1006 row.style.height = `${maxHeight}px`; 1007 row.setAttribute('height', `${maxHeight}`); 1008 if (res[0]) { 1009 let timeLineType = res[0].type; //@ts-ignore 1010 row.rowId = `${timeLineType}-${process.pid}`; 1011 row.setAttribute('frame_type', res[0].frameType || ''); 1012 if (type === 'actual') { 1013 row.dataList = res; 1014 } 1015 } 1016 } 1017 } 1018 1019 addStartUpRow(processRow: TraceRow<ProcessStruct>): TraceRow<AppStartupStruct> { 1020 processRow.setAttribute('hasStartup', 'true'); 1021 let startupRow: TraceRow<AppStartupStruct> = TraceRow.skeleton<AppStartupStruct>(); 1022 startupRow.rowId = `app-start-${processRow.rowId}`; 1023 startupRow.rowType = TraceRow.ROW_TYPE_APP_STARTUP; 1024 startupRow.rowParentId = `${processRow.rowId}`; 1025 startupRow.rowHidden = !processRow.expansion; 1026 startupRow.index = 0; 1027 startupRow.style.height = '30px'; 1028 startupRow.style.width = '100%'; 1029 startupRow.name = 'App Startups'; 1030 startupRow.findHoverStruct = (): void => { 1031 AppStartupStruct.hoverStartupStruct = startupRow.getHoverStruct(); 1032 }; 1033 startupRow.setAttribute('children', ''); 1034 startupRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 1035 startupRow.selectChangeHandler = this.trace.selectChangeHandler; 1036 startupRow.supplierFrame = (): Promise<Array<AppStartupStruct>> => 1037 processStartupDataSender(parseInt(processRow.rowId!), startupRow).then((res) => { 1038 if (res.length <= 0) { 1039 this.trace.refreshCanvas(true); 1040 } 1041 for (let i = 0; i < res.length; i++) { 1042 if (res[i].startName! < 6 && i < res.length - 1) { 1043 res[i].endItid = res[i + 1].itid; 1044 } 1045 } 1046 return res; 1047 }); 1048 startupRow.onThreadHandler = rowThreadHandler<AppStartupRender>( 1049 'app-start-up', 1050 'appStartupContext', 1051 { 1052 type: `app-startup ${processRow.rowId}`, 1053 }, 1054 startupRow, 1055 this.trace 1056 ); 1057 processRow.addChildTraceRow(startupRow); 1058 return startupRow; 1059 } 1060 1061 addSoInitRow(processRow: TraceRow<ProcessStruct>, maxDepth: number): TraceRow<SoStruct> { 1062 processRow.setAttribute('hasStaticInit', 'true'); 1063 let maxHeight = (maxDepth + 1) * 20; 1064 let soRow: TraceRow<SoStruct> = TraceRow.skeleton<SoStruct>(); 1065 soRow.rowId = `app-start-${processRow.rowId}`; 1066 soRow.rowType = TraceRow.ROW_TYPE_STATIC_INIT; 1067 soRow.rowParentId = `${processRow.rowId}`; 1068 soRow.rowHidden = !processRow.expansion; 1069 soRow.index = 0; 1070 soRow.style.height = `${maxHeight}px`; 1071 soRow.style.width = '100%'; 1072 soRow.name = 'Static Initialization'; 1073 soRow.setAttribute('children', ''); 1074 soRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 1075 soRow.selectChangeHandler = this.trace.selectChangeHandler; 1076 soRow.findHoverStruct = (): void => { 1077 SoStruct.hoverSoStruct = soRow.getHoverStruct(); 1078 }; 1079 soRow.supplierFrame = (): Promise<Array<SoStruct>> => 1080 processSoInitDataSender(parseInt(processRow.rowId!), soRow).then((res) => { 1081 if (res.length <= 0) { 1082 this.trace.refreshCanvas(true); 1083 } 1084 res.forEach((so, index) => { 1085 let soName = this.soInitNameMap.get(res[index].id!); 1086 if (soName) { 1087 so.soName = soName.replace('dlopen: ', ''); 1088 } 1089 }); 1090 return res; 1091 }); 1092 soRow.onThreadHandler = rowThreadHandler<SoRender>( 1093 'app-so-init', 1094 'context', 1095 { 1096 type: `static-init ${processRow.rowId}`, 1097 }, 1098 soRow, 1099 this.trace 1100 ); 1101 processRow.addChildTraceRow(soRow); 1102 return soRow; 1103 } 1104 1105 insertAfter(newEl: HTMLElement, targetEl: HTMLElement): void { 1106 let parentEl = targetEl.parentNode; 1107 if (parentEl!.lastChild === targetEl) { 1108 parentEl!.appendChild(newEl); 1109 } else { 1110 parentEl!.insertBefore(newEl, targetEl.nextSibling); 1111 } 1112 } 1113 1114 //add thread list 1115 async addThreadList( 1116 it: { pid: number | null; processName: string | null }, 1117 pRow: TraceRow<ProcessStruct>, 1118 expectedRow: TraceRow<JankStruct> | null, 1119 actualRow: TraceRow<JankStruct> | null, 1120 soRow: TraceRow<SoStruct> | undefined, 1121 startupRow: TraceRow<AppStartupStruct> | undefined, 1122 traceId?: string 1123 ): Promise<void> { 1124 let threads = this.processThreads.filter((thread) => thread.pid === it.pid && thread.tid !== 0); 1125 const sameThreadCounts: Record<string, number> = {}; //同名thread添加子进程 1126 const sameThreadList: unknown[] = []; 1127 const differentThreadList: unknown[] = []; 1128 threads.forEach(item => { 1129 const sameThread = item.threadName; 1130 if (sameThread !== undefined) { 1131 if (sameThread in sameThreadCounts && item.tid !== item.pid) { 1132 sameThreadCounts[sameThread]++; 1133 } else { 1134 sameThreadCounts[sameThread] = 1; 1135 } 1136 } 1137 }); 1138 1139 threads.forEach((item) => { 1140 const sameThread = item.threadName; 1141 if (sameThreadCounts[sameThread!] > 128 && item.tid !== item.pid) { 1142 sameThreadList.push(item); 1143 } else { 1144 differentThreadList.push(item); 1145 } 1146 }); 1147 1148 differentThreadList.length && this.addDifferentThread(it, pRow, expectedRow, actualRow, soRow, startupRow, differentThreadList, traceId!); 1149 if (sameThreadList.length) { 1150 let sameThreadFolder = this.initSameThreadFolder(it, pRow, sameThreadList, traceId!); 1151 if (sameThreadFolder) { 1152 pRow.addChildTraceRow(this.sameThreadFolder); 1153 this.addProcessRowListener(this.sameThreadFolder, actualRow); 1154 } 1155 await this.initSameThreadData(sameThreadFolder, it, expectedRow, actualRow, soRow, startupRow, sameThreadList, traceId); 1156 } 1157 } 1158 1159 initSameThreadFolder( 1160 it: { pid: number | null; processName: string | null }, 1161 pRow: TraceRow<ProcessStruct>, 1162 list: Array<unknown>, 1163 traceId?: string 1164 ): TraceRow<ProcessStruct> { 1165 let sameThreadRow = TraceRow.skeleton<ProcessStruct>(); 1166 sameThreadRow.rowId = 'sameThreadProcess'; 1167 sameThreadRow.rowParentId = `${it.pid}`; 1168 sameThreadRow.rowHidden = !pRow.expansion; 1169 sameThreadRow.rowType = TraceRow.ROW_TYPE_THREAD_NAME; 1170 sameThreadRow.folder = true; 1171 // @ts-ignore 1172 sameThreadRow.name = list[0].threadName; 1173 sameThreadRow.folderPaddingLeft = 20; 1174 sameThreadRow.style.height = '40px'; 1175 sameThreadRow.style.width = '100%'; 1176 sameThreadRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 1177 sameThreadRow.selectChangeHandler = this.trace.selectChangeHandler; 1178 sameThreadRow.supplierFrame = (): Promise<Array<ProcessStruct>> => 1179 new Promise<Array<ProcessStruct>>((resolve) => resolve([])); 1180 sameThreadRow.onThreadHandler = (useCache): void => { 1181 let context: CanvasRenderingContext2D; 1182 if (sameThreadRow.currentContext) { 1183 context = sameThreadRow.currentContext; 1184 } else { 1185 context = sameThreadRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 1186 } 1187 sameThreadRow.canvasSave(context); 1188 if (sameThreadRow.expansion) { 1189 // @ts-ignore 1190 context?.clearRect(0, 0, sameThreadRow.frame.width, sameThreadRow.frame.height); 1191 } else { 1192 (renders.empty as EmptyRender).renderMainThread( 1193 { 1194 context: context, 1195 useCache: useCache, 1196 type: '', 1197 }, 1198 sameThreadRow 1199 ); 1200 } 1201 sameThreadRow.canvasRestore(context, this.trace); 1202 }; 1203 this.sameThreadFolder = sameThreadRow; 1204 return this.sameThreadFolder; 1205 } 1206 1207 async initSameThreadData(sameThreadFolder: TraceRow<ProcessStruct>, it: { pid: number | null; processName: string | null }, 1208 expectedRow: TraceRow<JankStruct> | null, 1209 actualRow: TraceRow<JankStruct> | null, 1210 soRow: TraceRow<SoStruct> | undefined, 1211 startupRow: TraceRow<AppStartupStruct> | undefined, sameThreadList: Array<unknown>, traceId?: string): Promise<void> { 1212 let tRowArr: Array<TraceRow<BaseStruct>> = []; 1213 for (let j = 0; j < sameThreadList.length; j++) { 1214 let thread = sameThreadList[j]; 1215 let tRow = TraceRow.skeleton<ThreadStruct>(this.traceId); 1216 // @ts-ignore 1217 tRow.rowId = `${thread.tid}`; 1218 tRow.rowType = TraceRow.ROW_TYPE_THREAD; 1219 // @ts-ignore 1220 tRow.rowParentId = `${thread.pid}`; 1221 tRow.rowHidden = !sameThreadFolder.expansion; 1222 tRow.index = j; 1223 tRow.style.height = '18px'; 1224 tRow.style.width = '100%'; 1225 // @ts-ignore 1226 tRow.name = `${thread.threadName || 'Thread'} ${thread.tid}`; 1227 // @ts-ignore 1228 tRow.namePrefix = `${thread.threadName || 'Thread'}`; 1229 tRow.setAttribute('children', ''); 1230 tRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 1231 tRow.selectChangeHandler = this.trace.selectChangeHandler; 1232 tRow.findHoverStruct = (): void => this.threadRowFindHoverStruct(tRow); 1233 tRow.supplierFrame = async (): Promise<Array<ThreadStruct>> => { 1234 // @ts-ignore 1235 const res = await threadDataSender(thread.tid || 0, it.pid || 0, tRow, this.traceId); 1236 if (res === true) { 1237 return []; 1238 } 1239 let rs = res as ThreadStruct[]; 1240 if (rs.length <= 0 && !tRow.isComplete) { 1241 this.trace.refreshCanvas(true); 1242 } 1243 return rs; 1244 }; 1245 tRow.onThreadHandler = rowThreadHandler<ThreadRender>( 1246 'thread', 1247 'context', 1248 { 1249 // @ts-ignore 1250 type: `thread ${thread.tid} ${thread.threadName}`, 1251 translateY: tRow.translateY, 1252 }, 1253 tRow, 1254 this.trace 1255 ); 1256 // @ts-ignore 1257 this.insertRowToDoc(it, j, thread, sameThreadFolder, tRow, sameThreadList, tRowArr, actualRow, expectedRow, startupRow, soRow); 1258 this.addFuncStackRow(it, thread, j, sameThreadList, tRowArr, tRow, sameThreadFolder); 1259 // @ts-ignore 1260 if ((thread.switchCount || 0) === 0) { 1261 tRow.rowDiscard = true; 1262 } 1263 sameThreadFolder.addChildTraceRow(tRow); 1264 } 1265 } 1266 1267 addDifferentThread(it: { pid: number | null; processName: string | null }, 1268 pRow: TraceRow<ProcessStruct>, 1269 expectedRow: TraceRow<JankStruct> | null, 1270 actualRow: TraceRow<JankStruct> | null, 1271 soRow: TraceRow<SoStruct> | undefined, 1272 startupRow: TraceRow<AppStartupStruct> | undefined, 1273 list: Array<unknown>, traceId?: string): void { 1274 let tRowArr: Array<TraceRow<BaseStruct>> = []; 1275 for (let j = 0; j < list.length; j++) { 1276 let thread = list[j]; 1277 let tRow = TraceRow.skeleton<ThreadStruct>(this.traceId); 1278 // @ts-ignore 1279 tRow.rowId = `${thread.tid}`; 1280 tRow.rowType = TraceRow.ROW_TYPE_THREAD; 1281 tRow.rowParentId = `${it.pid}`; 1282 tRow.rowHidden = !pRow.expansion; 1283 tRow.index = j; 1284 tRow.style.height = '18px'; 1285 tRow.style.width = '100%'; 1286 // @ts-ignore 1287 tRow.name = `${thread.threadName || 'Thread'} ${thread.tid}`; 1288 // @ts-ignore 1289 tRow.namePrefix = `${thread.threadName || 'Thread'}`; 1290 tRow.setAttribute('children', ''); 1291 tRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 1292 tRow.selectChangeHandler = this.trace.selectChangeHandler; 1293 tRow.findHoverStruct = (): void => this.threadRowFindHoverStruct(tRow); 1294 tRow.supplierFrame = async (): Promise<Array<ThreadStruct>> => { 1295 // @ts-ignore 1296 const res = await threadDataSender(thread.tid || 0, it.pid || 0, tRow, this.traceId); 1297 if (res === true) { 1298 return []; 1299 } 1300 let rs = res as ThreadStruct[]; 1301 if (rs.length <= 0 && !tRow.isComplete) { 1302 this.trace.refreshCanvas(true); 1303 } 1304 return rs; 1305 }; 1306 tRow.onThreadHandler = rowThreadHandler<ThreadRender>( 1307 'thread', 1308 'context', 1309 { 1310 // @ts-ignore 1311 type: `thread ${thread.tid} ${thread.threadName}`, 1312 translateY: tRow.translateY, 1313 }, 1314 tRow, 1315 this.trace 1316 ); 1317 let hangRow: TraceRow<HangStruct> | null = null; 1318 // @ts-ignore 1319 this.insertRowToDoc(it, j, thread, pRow, tRow, list, tRowArr, actualRow, expectedRow, hangRow, startupRow, soRow); 1320 this.addFuncStackRow(it, thread, j, list, tRowArr, tRow, pRow); 1321 // @ts-ignore 1322 if ((thread.switchCount || 0) === 0) { 1323 tRow.rowDiscard = true; 1324 } 1325 } 1326 } 1327 1328 threadRowFindHoverStruct(threadRow: TraceRow<ThreadStruct>): void { 1329 let arr = threadRow.dataListCache.filter( 1330 (re) => re.frame && isFrameContainPoint(re.frame, threadRow.hoverX, threadRow.hoverY, true, false) 1331 ); 1332 let runItem = arr.find((it) => it.state === 'Running'); 1333 if (runItem) { 1334 ThreadStruct.hoverThreadStruct = runItem; 1335 } else { 1336 let otherItem = arr.find((it) => it.state !== 'S'); 1337 if (otherItem) { 1338 ThreadStruct.hoverThreadStruct = otherItem; 1339 } else { 1340 ThreadStruct.hoverThreadStruct = arr[0]; 1341 } 1342 } 1343 } 1344 1345 insertRowToDoc( 1346 it: unknown, 1347 index: number, 1348 thread: ThreadStruct, 1349 processRow: TraceRow<ProcessStruct>, 1350 threadRow: TraceRow<ThreadStruct>, 1351 threads: ThreadStruct[], 1352 //@ts-ignore 1353 threadRowArr: TraceRow<unknown>[], 1354 //@ts-ignore 1355 actualRow: TraceRow<unknown> | null, 1356 //@ts-ignore 1357 expectedRow: TraceRow<unknown> | null, 1358 hangRow: TraceRow<HangStruct> | null, 1359 startupRow: TraceRow<AppStartupStruct> | null | undefined, 1360 soRow: TraceRow<SoStruct> | null | undefined 1361 ): void { 1362 //@ts-ignore 1363 if (it.processName === 'render_service') { 1364 //@ts-ignore 1365 if (threadRow.name === `${it.processName} ${it.pid}`) { 1366 this.renderRow = threadRow; 1367 } 1368 let flag = threads.length === index + 1 && !this.threadFuncMaxDepthMap.has(`${thread.upid}-${thread.tid}`); 1369 //@ts-ignore 1370 processRow.sortRenderServiceData(threadRow, threadRow, threadRowArr, flag); 1371 } else { 1372 if (threadRow.rowId === threadRow.rowParentId) { 1373 if (actualRow !== null) { 1374 processRow.addChildTraceRowAfter(threadRow, actualRow); 1375 } else if (expectedRow !== null) { 1376 processRow.addChildTraceRowAfter(threadRow, expectedRow); 1377 } else if (soRow) { 1378 processRow.addChildTraceRowAfter(threadRow, soRow); 1379 } else if (startupRow) { 1380 processRow.addChildTraceRowAfter(threadRow, startupRow); 1381 } else if (hangRow !== null) { 1382 processRow.addChildTraceRowAfter(threadRow, hangRow); 1383 } else { 1384 processRow.addChildTraceRowSpecifyLocation(threadRow, 0); 1385 } 1386 } else { 1387 processRow.addChildTraceRow(threadRow); 1388 } 1389 } 1390 } 1391 1392 addFuncStackRow( 1393 process: { pid: number | null; processName: string | null }, 1394 thread: unknown, 1395 index: number, 1396 threads: Array<unknown>, 1397 threadRowArr: Array<unknown>, 1398 threadRow: TraceRow<ThreadStruct>, 1399 processRow: TraceRow<ProcessStruct> 1400 ): void { 1401 //@ts-ignore 1402 if (this.threadFuncMaxDepthMap.get(`${thread.upid}-${thread.tid}`) !== undefined) { 1403 this.addHangRow(process, processRow, threadRow, thread); 1404 //@ts-ignore 1405 let max = this.threadFuncMaxDepthMap.get(`${thread.upid}-${thread.tid}`) || 1; 1406 let maxHeight = max * 18 + 6; 1407 let funcRow = TraceRow.skeleton<FuncStruct>(this.traceId); 1408 //@ts-ignore 1409 funcRow.rowId = `${thread.tid}`; 1410 funcRow.rowType = TraceRow.ROW_TYPE_FUNC; 1411 funcRow.enableCollapseChart(FOLD_HEIGHT, this.trace); //允许折叠泳道图 1412 //@ts-ignore 1413 funcRow.rowParentId = `${thread.pid}`; 1414 funcRow.rowHidden = !processRow.expansion; 1415 funcRow.checkType = threadRow.checkType; 1416 funcRow.style.width = '100%'; 1417 funcRow.style.height = `${maxHeight}px`; 1418 //@ts-ignore 1419 funcRow.name = `${thread.threadName || 'Thread'} ${thread.tid}`; 1420 //@ts-ignore 1421 funcRow.namePrefix = `${thread.threadName || 'Thread'}`; 1422 funcRow.setAttribute('children', ''); 1423 funcRow.supplierFrame = async (): Promise<Array<FuncStruct>> => { 1424 //@ts-ignore 1425 const rs = await funcDataSender(thread.tid || 0, thread.upid || 0, funcRow, this.traceId); 1426 //@ts-ignore 1427 return this.funDataSenderCallback(rs, funcRow, thread); 1428 }; 1429 funcRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 1430 funcRow.selectChangeHandler = this.trace.selectChangeHandler; 1431 funcRow.findHoverStruct = (): void => { 1432 FuncStruct.hoverFuncStruct = funcRow.getHoverStruct(); 1433 }; 1434 funcRow.onThreadHandler = rowThreadHandler<FuncRender>( 1435 'func', 1436 'context', 1437 { 1438 //@ts-ignore 1439 type: `func${thread.tid}${thread.threadName}`, 1440 }, 1441 funcRow, 1442 this.trace 1443 ); //@ts-ignore 1444 if (process.processName === 'render_service') { 1445 let flag = threads.length === index + 1; 1446 //@ts-ignore 1447 processRow.sortRenderServiceData(funcRow, threadRow, threadRowArr, flag); 1448 } else { 1449 processRow.addChildTraceRowAfter(funcRow, threadRow); 1450 } 1451 } 1452 } 1453 1454 funDataSenderCallback( 1455 rs: Array<unknown> | boolean, 1456 funcRow: TraceRow<FuncStruct>, 1457 thread: ThreadStruct 1458 ): FuncStruct[] { 1459 if (rs === true) { 1460 funcRow.rowDiscard = true; 1461 return []; 1462 } else { 1463 let funs = rs as FuncStruct[]; 1464 if (funs.length > 0) { 1465 funs.forEach((fun, index) => { 1466 funs[index].itid = thread.utid; 1467 funs[index].ipid = thread.upid; 1468 funs[index].tid = thread.tid; 1469 funs[index].pid = thread.pid; 1470 funs[index].funName = this.traceId ? Utils.getInstance().getCallStatckMap().get(`${this.traceId}_${funs[index].id}`) : Utils.getInstance().getCallStatckMap().get(funs[index].id!); 1471 if (Utils.isBinder(fun)) { 1472 } else { 1473 if (fun.nofinish) { 1474 fun.flag = 'Did not end'; 1475 } 1476 } 1477 if (fun.id && this.distributedDataMap.has(`${fun.id}_${this.traceId}`)) { 1478 let distributedData = this.distributedDataMap.get(`${fun.id}_${this.traceId}`); 1479 funs[index].chainId = distributedData!.chainId; 1480 funs[index].spanId = distributedData!.spanId; 1481 funs[index].parentSpanId = distributedData!.parentSpanId; 1482 funs[index].chainFlag = distributedData!.chainFlag; 1483 funs[index].traceId = this.traceId; 1484 } 1485 }); 1486 } else { 1487 this.trace.refreshCanvas(true); 1488 } 1489 return funs; 1490 } 1491 } 1492 1493 //进程内存信息 1494 addProcessMemInfo(it: { pid: number | null; processName: string | null }, processRow: TraceRow<ProcessStruct>): void { 1495 //@ts-ignore 1496 let processMem = this.processMem.filter((mem) => mem.pid === it.pid); 1497 processMem.forEach((mem) => { 1498 let row = TraceRow.skeleton<ProcessMemStruct>(); 1499 //@ts-ignore 1500 row.rowId = `${mem.trackId}`; 1501 row.rowType = TraceRow.ROW_TYPE_MEM; 1502 row.rowParentId = `${it.pid}`; 1503 row.rowHidden = !processRow.expansion; 1504 row.style.height = '40px'; 1505 row.style.width = '100%'; 1506 //@ts-ignore 1507 row.name = `${mem.trackName}`; 1508 row.setAttribute('children', ''); 1509 row.favoriteChangeHandler = this.trace.favoriteChangeHandler; 1510 row.selectChangeHandler = this.trace.selectChangeHandler; 1511 row.focusHandler = (): void => { 1512 this.trace.displayTip( 1513 row, 1514 ProcessMemStruct.hoverProcessMemStruct, 1515 `<span>${ProcessMemStruct.hoverProcessMemStruct?.value || '0'}</span>` 1516 ); 1517 }; 1518 row.findHoverStruct = (): void => { 1519 ProcessMemStruct.hoverProcessMemStruct = row.getHoverStruct(false); 1520 }; 1521 row.supplierFrame = (): Promise<Array<ProcessMemStruct>> => 1522 //@ts-ignore 1523 processMemDataSender(mem.trackId, row).then((resultProcess) => { 1524 //@ts-ignore 1525 let maxValue = this.filterIdMaxValue.get(mem.trackId) || 0; 1526 for (let j = 0; j < resultProcess.length; j++) { 1527 resultProcess[j].maxValue = maxValue; 1528 if (j === resultProcess.length - 1) { 1529 resultProcess[j].duration = (TraceRow.range?.totalNS || 0) - (resultProcess[j].startTime || 0); 1530 } else { 1531 resultProcess[j].duration = (resultProcess[j + 1].startTime || 0) - (resultProcess[j].startTime || 0); 1532 } 1533 if (j > 0) { 1534 resultProcess[j].delta = (resultProcess[j].value || 0) - (resultProcess[j - 1].value || 0); 1535 } else { 1536 resultProcess[j].delta = 0; 1537 } 1538 } 1539 return resultProcess; 1540 }); 1541 row.onThreadHandler = rowThreadHandler<MemRender>( 1542 'mem', 1543 'context', 1544 { 1545 //@ts-ignore 1546 type: `mem ${mem.trackId} ${mem.trackName}`, 1547 }, 1548 row, 1549 this.trace 1550 ); 1551 if (this.renderRow && row.name === 'H:PreferredFrameRate') { 1552 processRow.addChildTraceRowBefore(row, this.renderRow); 1553 } else { 1554 processRow.addChildTraceRow(row); 1555 } 1556 }); 1557 } 1558 private calMaxHeight(asyncFunctions: unknown[]): number { 1559 let max = 0; 1560 asyncFunctions.forEach((it) => { 1561 //@ts-ignore 1562 const depth = it.depth || 0; 1563 if (depth > max) { 1564 max = depth; 1565 } 1566 }); 1567 max += 1; 1568 return max * 18 + 6; 1569 } 1570 //处理缓存数据的'startTs'字段统一成'startTime' 1571 private toAsyncFuncCache(object: unknown, name: string): void { 1572 // @ts-ignore 1573 let modifiedObject = { ...object }; 1574 modifiedObject.startTime = modifiedObject.startTs; 1575 Reflect.deleteProperty(modifiedObject, 'startTs'); 1576 modifiedObject.rowId = name; 1577 modifiedObject.type = 'func'; 1578 SpProcessChart.asyncFuncCache.push({ ...modifiedObject }); 1579 } 1580 //Async Function 1581 addAsyncFunction(it: { pid: number; processName: string | null }, processRow: TraceRow<ProcessStruct>): void { 1582 let isCategoryAsyncfunc: boolean = FlagsConfig.getFlagsConfigEnableStatus('Start&Finish Trace Category'); 1583 let asyncRemoveCatArr: unknown[]; 1584 let asyncCat: unknown; 1585 let setArrayLenThanOne: unknown; 1586 let setArrayLenOnlyOne: unknown; 1587 //@ts-ignore 1588 let asyncFuncList = this.processAsyncFuncMap[it.pid] || []; 1589 if (!asyncFuncList.length) { 1590 return; 1591 } 1592 let flag = FlagsConfig.getSecondarySelectValue('catValue') === 'business'; 1593 if (isCategoryAsyncfunc) {//聚合异步trace 1594 ({ asyncRemoveCatArr, asyncCat } = this.hanldCatFunc(asyncFuncList, flag));//处理是否cat 1595 ({ setArrayLenThanOne, setArrayLenOnlyOne } = this.hanldAsyncFunc(it, asyncRemoveCatArr));//len等于0和大于0的分类 1596 //@ts-ignore 1597 let aggregateData = { ...setArrayLenThanOne, ...setArrayLenOnlyOne }; 1598 Reflect.ownKeys(aggregateData).map((key: unknown) => { 1599 let param: Array<unknown> = aggregateData[key]; 1600 //@ts-ignore 1601 this.makeAddAsyncFunction(param, it, processRow, key); 1602 }); 1603 //@ts-ignore 1604 Reflect.ownKeys(asyncCat).map((key: unknown) => { 1605 //@ts-ignore 1606 let param: Array<unknown> = asyncCat[key]; 1607 if (flag) {//处理business 1608 //@ts-ignore 1609 this.makeAddAsyncFunction(param, it, processRow, key); 1610 } else {//处理thread 1611 //@ts-ignore 1612 this.makeAddAsyncFunction(param, it, processRow, key, param[0].tid); 1613 } 1614 }); 1615 } else { 1616 //不聚合异步trace 1617 let asyncFuncGroup = Utils.groupBy(asyncFuncList, 'funName'); 1618 //@ts-ignore 1619 Reflect.ownKeys(asyncFuncGroup).map((key: unknown) => { 1620 //@ts-ignore 1621 let asyncFunctions: Array<unknown> = asyncFuncGroup[key]; 1622 if (asyncFunctions.length > 0) { 1623 let isIntersect = (a: unknown, b: unknown): boolean => 1624 // @ts-ignore 1625 Math.max(a.startTs + a.dur, b.startTs + b.dur) - Math.min(a.startTs, b.startTs) < a.dur + b.dur; 1626 let depthArray: unknown = []; 1627 asyncFunctions.forEach((it, i) => { 1628 // @ts-ignore 1629 if (it.dur === -1 || it.dur === null || it.dur === undefined) { 1630 // @ts-ignore 1631 it.dur = (TraceRow.range?.endNS || 0) - it.startTs; 1632 // @ts-ignore 1633 it.flag = 'Did not end'; 1634 } 1635 let currentDepth = 0; 1636 let index = i; 1637 while ( 1638 // @ts-ignore 1639 depthArray[currentDepth] !== undefined && 1640 // @ts-ignore 1641 isIntersect(depthArray[currentDepth], asyncFunctions[index]) 1642 ) { 1643 currentDepth++; 1644 } 1645 // @ts-ignore 1646 asyncFunctions[index].depth = currentDepth; 1647 // @ts-ignore 1648 depthArray[currentDepth] = asyncFunctions[index]; 1649 // @ts-ignore 1650 //处理缓存的异步trace数据缺失的字段 1651 this.toAsyncFuncCache(asyncFunctions[index], `${asyncFunctions[i].funName}-${it.pid}`); 1652 }); 1653 } 1654 //@ts-ignore 1655 this.lanesConfig(asyncFunctions, it, processRow, `${asyncFunctions[0].funName}`); 1656 }); 1657 } 1658 } 1659 //处理CategoryAsyncFunc 1660 hanldCatFunc( 1661 asyncFuncList: Array<unknown>, 1662 flag: boolean 1663 ): { asyncRemoveCatArr: Array<unknown>, asyncCat: unknown } { 1664 let asyncCat; 1665 let asyncCatArr = new Array(); 1666 let asyncCatMap: Map<string, unknown> = new Map<string, unknown>(); 1667 let asyncRemoveCatArr = new Array(); 1668 //取出cat字段(category)不为null的数据 1669 for (let i = 0; i < asyncFuncList.length; i++) { 1670 const el = asyncFuncList[i]; 1671 // @ts-ignore 1672 if (el.cat !== null) { 1673 if (flag) {//business first 1674 asyncCatArr.push(el); 1675 } else {//thread first 1676 //@ts-ignore 1677 if (asyncCatMap.has(`${el.cat}:${el.threadName} ${el.tid}`)) { 1678 //@ts-ignore 1679 let item: Array<unknown> = asyncCatMap.get(`${el.cat}:${el.threadName} ${el.tid}`); 1680 item.push(el); 1681 } else { 1682 //@ts-ignore 1683 asyncCatMap.set(`${el.cat}:${el.threadName} ${el.tid}`, [el]); 1684 } 1685 } 1686 } else { 1687 //取cat字段为null的数据 1688 asyncRemoveCatArr.push(el); 1689 } 1690 } 1691 asyncCat = flag ? Utils.groupBy(asyncCatArr, 'cat') : Object.fromEntries(asyncCatMap); 1692 return { asyncRemoveCatArr, asyncCat }; 1693 } 1694 //处理cat字段为null的数据,按funname分类,分别按len>1和=1去处理 1695 hanldAsyncFunc( 1696 it: { pid: number; processName: string | null }, 1697 asyncRemoveCatList: Array<unknown> 1698 ): { setArrayLenThanOne: unknown, setArrayLenOnlyOne: unknown } { 1699 let asyncFuncGroup = Utils.groupBy(asyncRemoveCatList, 'funName'); 1700 let funcArr: unknown[] = []; 1701 let setArrayLenThanOne: unknown = {}; 1702 let setArrayLenOnlyOne: unknown = {}; 1703 //@ts-ignore 1704 Reflect.ownKeys(asyncFuncGroup).map((key: unknown) => { 1705 //@ts-ignore 1706 let asyncFunctions: Array<unknown> = asyncFuncGroup[key]; 1707 if (asyncFunctions.length > 1) { 1708 //@ts-ignore 1709 setArrayLenThanOne[key] = asyncFunctions; 1710 } else if (asyncFunctions.length === 1) { 1711 funcArr.push(...asyncFunctions); 1712 } 1713 }); 1714 //len=1的数据继续按tid分类 1715 let asyncFuncTidGroup = Utils.groupBy(funcArr, 'tid'); 1716 //@ts-ignore 1717 Reflect.ownKeys(asyncFuncTidGroup).map((key: unknown) => { 1718 //@ts-ignore 1719 let asyncTidFunc: Array<unknown> = asyncFuncTidGroup[key]; 1720 //@ts-ignore 1721 setArrayLenOnlyOne[`H:${asyncTidFunc[0].threadName} ${asyncTidFunc[0].tid}`] = asyncTidFunc; 1722 }); 1723 return { setArrayLenThanOne, setArrayLenOnlyOne }; 1724 } 1725 makeAddAsyncFunction( 1726 asyncFunctions: unknown[], 1727 it: { pid: number; processName: string | null }, 1728 processRow: TraceRow<ProcessStruct>, 1729 key: string, 1730 rowSingleTid?: number 1731 ): void { 1732 let maxDepth: number = -1; 1733 let i = 0; 1734 let mapDepth = new Map(); 1735 let noEndData = new Array(); 1736 let normalData = new Array(); 1737 if (asyncFunctions.length) { 1738 while (i < asyncFunctions.length) { 1739 let param = asyncFunctions[i]; 1740 // @ts-ignore 1741 if (param.dur !== null) { 1742 // @ts-ignore 1743 let itemEndTime = param.startTs + param.dur; 1744 let flag = false; 1745 for (let [key, val] of mapDepth.entries()) { 1746 // @ts-ignore 1747 if (val.et < param.startTs) { 1748 flag = true; 1749 //更新endts 1750 val.et = itemEndTime; 1751 // @ts-ignore 1752 param.depth = Number(key); 1753 break; 1754 } 1755 } 1756 if (!flag) { 1757 //depth增加 1758 maxDepth++; 1759 mapDepth.set(`${maxDepth}`, { et: itemEndTime }); 1760 // @ts-ignore 1761 param.depth = maxDepth; 1762 } 1763 this.toAsyncFuncCache(param, `${key}-${it.pid}`); 1764 normalData.push(param); 1765 } else { 1766 noEndData.push(param); 1767 } 1768 i++; 1769 } 1770 if (noEndData.length) { 1771 noEndData.forEach((it: unknown, i: unknown) => { 1772 // @ts-ignore 1773 if (it.dur === -1 || it.dur === null || it.dur === undefined) { 1774 // @ts-ignore 1775 it.dur = (TraceRow.range?.endNS || 0) - it.startTs; 1776 // @ts-ignore 1777 it.nofinish = true; 1778 // @ts-ignore 1779 it.flag = 'Did not end'; 1780 } 1781 let index = i; 1782 maxDepth++; 1783 // @ts-ignore 1784 noEndData[index].depth = maxDepth; 1785 //@ts-ignore 1786 this.toAsyncFuncCache(noEndData[index], `${key}-${it.pid}`); 1787 }); 1788 } 1789 this.lanesConfig([...normalData, ...noEndData], it, processRow, key, rowSingleTid); 1790 } 1791 } 1792 //初始化异步泳道信息 1793 lanesConfig( 1794 asyncFunctions: unknown[], 1795 it: { pid: number; processName: string | null }, 1796 processRow: TraceRow<ProcessStruct>, 1797 key: string, 1798 rowSingleTid?: number 1799 ): void { 1800 const maxHeight = this.calMaxHeight(asyncFunctions); 1801 // @ts-ignore 1802 const namesSet = new Set(asyncFunctions.map((item) => item.funName)); 1803 const asyncFuncName = Array.from(namesSet); 1804 let funcRow = TraceRow.skeleton<FuncStruct>(); 1805 funcRow.rowId = `${key}-${it.pid}`; 1806 funcRow.asyncFuncName = asyncFuncName; 1807 funcRow.asyncFuncNamePID = it.pid; 1808 funcRow.asyncFuncStartTID = rowSingleTid ? rowSingleTid : undefined; 1809 funcRow.rowType = TraceRow.ROW_TYPE_FUNC; 1810 funcRow.enableCollapseChart(FOLD_HEIGHT, this.trace); //允许折叠泳道图 1811 funcRow.rowParentId = `${it.pid}`; 1812 funcRow.rowHidden = !processRow.expansion; 1813 funcRow.style.width = '100%'; 1814 funcRow.style.height = `${maxHeight}px`; 1815 funcRow.setAttribute('height', `${maxHeight}`); 1816 funcRow.name = key; 1817 funcRow.setAttribute('children', ''); 1818 funcRow.findHoverStruct = (): void => { 1819 FuncStruct.hoverFuncStruct = funcRow.getHoverStruct(); 1820 }; 1821 // @ts-ignore 1822 funcRow.supplier = (): Promise<unknown> => new Promise((resolve) => resolve(asyncFunctions)); 1823 funcRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 1824 funcRow.selectChangeHandler = this.trace.selectChangeHandler; 1825 funcRow.onThreadHandler = rowThreadHandler<FuncRender>( 1826 'func', 1827 'context', 1828 { 1829 type: `func-${funcRow.rowId}`, 1830 }, 1831 funcRow, 1832 this.trace 1833 ); 1834 processRow.addChildTraceRow(funcRow); 1835 } 1836 1837 addAsyncCatFunction(it: { pid: number; processName: string | null }, processRow: TraceRow<ProcessStruct>): void { 1838 //@ts-ignore 1839 let asyncFuncCatList = this.processAsyncFuncCatMap[it.pid] || []; 1840 let asyncFuncGroup: unknown = Utils.groupBy(asyncFuncCatList, 'threadName'); 1841 //@ts-ignore 1842 Reflect.ownKeys(asyncFuncGroup).map((key: unknown) => { 1843 //@ts-ignore 1844 let asyncFunctions: Array<unknown> = asyncFuncGroup[key]; 1845 if (asyncFunctions.length > 0) { 1846 let isIntersect = (a: unknown, b: unknown): boolean => 1847 //@ts-ignore 1848 Math.max(a.startTs + a.dur, b.startTs + b.dur) - Math.min(a.startTs, b.startTs) < a.dur + b.dur; 1849 let depthArray: unknown = []; 1850 asyncFunctions.forEach((it, i) => { 1851 //@ts-ignore 1852 if (it.dur === -1 || it.dur === null || it.dur === undefined) { 1853 //@ts-ignore 1854 it.dur = (TraceRow.range?.endNS || 0) - it.startTs; 1855 //@ts-ignore 1856 it.flag = 'Did not end'; 1857 //@ts-ignore 1858 it.nofinish = true; 1859 } 1860 let currentDepth = 0; 1861 let index = i; 1862 //@ts-ignore 1863 while (depthArray[currentDepth] !== undefined && isIntersect(depthArray[currentDepth], asyncFunctions[index])) { 1864 currentDepth++; 1865 }//@ts-ignore 1866 asyncFunctions[index].depth = currentDepth; 1867 //@ts-ignore 1868 depthArray[currentDepth] = asyncFunctions[index]; 1869 //@ts-ignore 1870 this.toAsyncFuncCache(asyncFunctions[index], `${asyncFunctions[0].threadName}`); 1871 }); 1872 const maxHeight = this.calMaxHeight(asyncFunctions); 1873 let funcRow = TraceRow.skeleton<FuncStruct>(); 1874 //@ts-ignore 1875 funcRow.rowId = `${asyncFunctions[0].threadName}`; 1876 //@ts-ignore 1877 funcRow.asyncFuncThreadName = asyncFunctions[0].threadName; 1878 funcRow.asyncFuncNamePID = it.pid; 1879 funcRow.rowType = TraceRow.ROW_TYPE_FUNC; 1880 funcRow.enableCollapseChart(FOLD_HEIGHT, this.trace); 1881 //允许折叠泳道图 1882 funcRow.rowParentId = `${it.pid}`; 1883 funcRow.rowHidden = !processRow.expansion; 1884 funcRow.style.width = '100%'; 1885 funcRow.style.height = `${maxHeight}px`; 1886 funcRow.setAttribute('height', `${maxHeight}`); 1887 //@ts-ignore 1888 funcRow.name = `${asyncFunctions[0].threadName}`; 1889 funcRow.setAttribute('children', ''); 1890 funcRow.findHoverStruct = (): void => { 1891 FuncStruct.hoverFuncStruct = funcRow.getHoverStruct(); 1892 }; 1893 //@ts-ignore 1894 funcRow.supplier = (): Promise<unknown> => new Promise((resolve) => resolve(asyncFunctions)); 1895 funcRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 1896 funcRow.selectChangeHandler = this.trace.selectChangeHandler; 1897 funcRow.onThreadHandler = rowThreadHandler<FuncRender>('func', 'context', { 1898 //@ts-ignore 1899 type: `func-${asyncFunctions[0].threadName}-${it.pid}`, 1900 }, funcRow, this.trace); 1901 processRow.addChildTraceRow(funcRow); 1902 } 1903 }); 1904 } 1905} 1906