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 if (linkItem[0].rowEL.expansion && linkItem[0].backrowEL) { 747 this.updatePairPointTranslateY(linkItem[0]); 748 linkItem[0].x = ns2xByTimeShaft(linkItem[0].ns, this.trace.timerShaftEL!); 749 linkItem[0].y = linkItem[0].rowEL.translateY + linkItem[0].offsetY; 750 linkItem[0].offsetY = linkItem[0].offsetY * 2; 751 linkItem[0].rowEL = linkItem[0].backrowEL; 752 } 753 if (linkItem[1].rowEL.expansion && linkItem[1].backrowEL) { 754 this.updatePairPointTranslateY(linkItem[1]); 755 linkItem[1].x = ns2xByTimeShaft(linkItem[1].ns, this.trace.timerShaftEL!); 756 linkItem[1].y = linkItem[1].rowEL!.translateY! + linkItem[1].offsetY; 757 linkItem[1].offsetY = linkItem[1].offsetY * 2; 758 linkItem[1].rowEL = linkItem[1].backrowEL; 759 } 760 } 761 762 handler4(e: unknown, linkItem: PairPoint[], processRow: TraceRow<ProcessStruct>): void { 763 this.updatePairPointTranslateY(linkItem[0]); 764 linkItem[0].y = processRow!.translateY + linkItem[0].offsetY; 765 this.updatePairPointTranslateY(linkItem[1]); 766 linkItem[1].y = linkItem[1].rowEL!.translateY + linkItem[1].offsetY; //@ts-ignore 767 if (linkItem[0].rowEL.rowParentId === e.detail.rowId) { 768 //@ts-ignore 769 this.updatePairPoint(linkItem[0], processRow); 770 } //@ts-ignore 771 if (linkItem[1].rowEL.rowParentId === e.detail.rowId) { 772 this.updatePairPoint(linkItem[1], processRow); 773 } 774 } 775 776 updatePairPointTranslateY(pair: PairPoint): void { 777 if (pair.rowEL.collect) { 778 pair.rowEL.translateY = pair.rowEL.getBoundingClientRect().top - 195; 779 } else { 780 pair.rowEL.translateY = pair.rowEL.offsetTop - this.trace.rowsPaneEL!.scrollTop; 781 } 782 } 783 784 updatePairPoint(pair: PairPoint, processRow: TraceRow<ProcessStruct>): void { 785 if (!pair.rowEL.collect) { 786 pair.x = ns2xByTimeShaft(pair.ns, this.trace.timerShaftEL!); 787 pair.y = processRow!.translateY! + pair.offsetY / 2; 788 pair.offsetY = pair.offsetY / 2; 789 pair.rowEL = processRow!; 790 } 791 } 792 793 /* Janks Frames */ 794 //@ts-ignore 795 addExpectedRow( 796 process: unknown, //@ts-ignore 797 processRow: TraceRow<unknown>, 798 renderServiceProcess: Array<unknown> 799 ): TraceRow<JankStruct> { 800 let expectedRow = TraceRow.skeleton<JankStruct>(); //@ts-ignore 801 expectedRow.asyncFuncName = process.processName; //@ts-ignore 802 expectedRow.asyncFuncNamePID = process.pid; 803 expectedRow.rowType = TraceRow.ROW_TYPE_JANK; //@ts-ignore 804 expectedRow.rowParentId = `${process.pid}`; 805 expectedRow.rowHidden = !processRow.expansion; 806 expectedRow.style.width = '100%'; 807 expectedRow.name = 'Expected Timeline'; 808 expectedRow.addTemplateTypes('FrameTimeline'); 809 expectedRow.setAttribute('children', ''); 810 expectedRow.supplierFrame = async (): Promise<JankStruct[]> => { 811 //@ts-ignore 812 let res = await processExpectedDataSender(process.pid, expectedRow!); 813 this.jankSenderCallback(res, 'expected', process, expectedRow, renderServiceProcess); 814 return res; 815 }; 816 expectedRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 817 expectedRow.selectChangeHandler = this.trace.selectChangeHandler; 818 expectedRow.onThreadHandler = rowThreadHandler<JankRender>( 819 'jank', 820 'context', 821 { 822 type: 'expected_frame_timeline_slice', 823 }, 824 expectedRow, 825 this.trace 826 ); 827 if (this.renderRow) { 828 processRow.addChildTraceRowBefore(expectedRow, this.renderRow); 829 } else { 830 processRow.addChildTraceRow(expectedRow); 831 } 832 return expectedRow; 833 } 834 835 //@ts-ignore 836 addActualRow( 837 process: unknown, //@ts-ignore 838 processRow: TraceRow<unknown>, 839 renderServiceProcess: Array<unknown> 840 ): TraceRow<JankStruct> { 841 let actualRow = TraceRow.skeleton<JankStruct>(); 842 actualRow.rowType = TraceRow.ROW_TYPE_JANK; //@ts-ignore 843 actualRow.rowParentId = `${process.pid}`; 844 actualRow.rowHidden = !processRow.expansion; 845 actualRow.style.width = '100%'; 846 actualRow.name = 'Actual Timeline'; 847 actualRow.addTemplateTypes('FrameTimeline'); 848 actualRow.setAttribute('children', ''); 849 actualRow.supplierFrame = async (): Promise<JankStruct[]> => { 850 //@ts-ignore 851 let res = await processActualDataSender(process.pid, actualRow!); 852 this.jankSenderCallback(res, 'actual', process, actualRow, renderServiceProcess); 853 return res; 854 }; 855 actualRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 856 actualRow.selectChangeHandler = this.trace.selectChangeHandler; 857 actualRow.onThreadHandler = rowThreadHandler<JankRender>( 858 'jank', 859 'context', 860 { 861 type: 'actual_frame_timeline_slice', 862 }, 863 actualRow, 864 this.trace 865 ); 866 if (this.renderRow) { 867 processRow.addChildTraceRowBefore(actualRow, this.renderRow); 868 } else { 869 processRow.addChildTraceRow(actualRow); 870 } 871 return actualRow; 872 } 873 874 //@ts-ignore 875 addHangRow( 876 data: { 877 pid: number | null; 878 processName: string | null; 879 }, 880 processRow: TraceRow<ProcessStruct>, 881 funcRow: TraceRow<FuncStruct>, 882 thread: unknown 883 ): void { 884 if (this.hangProcessSet.has(data.pid!) && FlagsConfig.getFlagsConfigEnableStatus('Hangs Detection')) { 885 //@ts-ignore 886 if (data.pid === thread.tid) { 887 let hangsRow = TraceRow.skeleton<HangStruct>(); 888 hangsRow.rowType = TraceRow.ROW_TYPE_HANG_INNER; 889 hangsRow.rowId = `${data.processName ?? 'Process'} ${data.pid}`; 890 hangsRow.rowParentId = `${data.pid}`; 891 hangsRow.rowHidden = !processRow.expansion; 892 hangsRow.style.width = '100%'; 893 hangsRow.name = 'Hangs'; 894 hangsRow.addTemplateTypes('FrameTimeline'); 895 hangsRow.setAttribute('children', ''); 896 hangsRow.supplierFrame = async (): Promise<HangStruct[]> => { 897 let promiseData = hangDataSender(data.pid!, hangsRow); 898 if (promiseData === null) { 899 return new Promise<Array<HangStruct>>((resolve) => resolve([])); 900 } else { 901 return promiseData.then((resultHang: Array<HangStruct>) => 902 resultHang.map(hangItem => ({ 903 ...hangItem, 904 pname: data.processName ?? 'process', 905 type: SpHangChart.calculateHangType(hangItem.dur!), 906 content: SpHangChart.funcNameMap.get(hangItem.id!) 907 })) 908 ); 909 } 910 }; 911 hangsRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 912 hangsRow.selectChangeHandler = this.trace.selectChangeHandler; 913 hangsRow.findHoverStruct = (): void => { 914 HangStruct.hoverHangStruct = hangsRow.getHoverStruct(); 915 }; 916 hangsRow.onThreadHandler = rowThreadHandler<HangStruct>( 917 'hang', 918 'context', 919 { 920 type: 'hangs_frame_timeline_slice', 921 }, 922 hangsRow, 923 this.trace 924 ); 925 processRow.addChildTraceRowAfter(hangsRow, funcRow); 926 } 927 } 928 } 929 930 jankSenderCallback( 931 res: JankStruct[], 932 type: string, 933 process: unknown, 934 row: TraceRow<JankStruct>, 935 renderServiceProcess: Array<unknown>, 936 ): void { 937 let maxDepth: number = 1; 938 let unitHeight: number = 20; 939 for (let j = 0; j < res.length; j++) { 940 let struct = res[j]; 941 if (struct.depth! >= maxDepth) { 942 maxDepth = struct.depth! + 1; 943 } 944 if (type === 'actual') { 945 struct.src_slice = this.processSrcSliceMap.get(res[j].id!); 946 } 947 struct.cmdline = Utils.getInstance().getProcessMap().get(res[j].pid!); //@ts-ignore 948 if (res[j].pid! === renderServiceProcess[0].pid) { 949 struct.cmdline = 'render_service'; 950 struct.frameType = struct.cmdline; 951 } else { 952 struct.frameType = 'app'; 953 } 954 } 955 if (row && !row.isComplete && res.length > 0) { 956 let maxHeight: number = maxDepth * unitHeight; 957 row.style.height = `${maxHeight}px`; 958 row.setAttribute('height', `${maxHeight}`); 959 if (res[0]) { 960 let timeLineType = res[0].type; //@ts-ignore 961 row.rowId = `${timeLineType}-${process.pid}`; 962 row.setAttribute('frame_type', res[0].frameType || ''); 963 if (type === 'actual') { 964 row.dataList = res; 965 } 966 } 967 } 968 } 969 970 addStartUpRow(processRow: TraceRow<ProcessStruct>): TraceRow<AppStartupStruct> { 971 processRow.setAttribute('hasStartup', 'true'); 972 let startupRow: TraceRow<AppStartupStruct> = TraceRow.skeleton<AppStartupStruct>(); 973 startupRow.rowId = `app-start-${processRow.rowId}`; 974 startupRow.rowType = TraceRow.ROW_TYPE_APP_STARTUP; 975 startupRow.rowParentId = `${processRow.rowId}`; 976 startupRow.rowHidden = !processRow.expansion; 977 startupRow.index = 0; 978 startupRow.style.height = '30px'; 979 startupRow.style.width = '100%'; 980 startupRow.name = 'App Startups'; 981 startupRow.findHoverStruct = (): void => { 982 AppStartupStruct.hoverStartupStruct = startupRow.getHoverStruct(); 983 }; 984 startupRow.setAttribute('children', ''); 985 startupRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 986 startupRow.selectChangeHandler = this.trace.selectChangeHandler; 987 startupRow.supplierFrame = (): Promise<Array<AppStartupStruct>> => 988 processStartupDataSender(parseInt(processRow.rowId!), startupRow).then((res) => { 989 if (res.length <= 0) { 990 this.trace.refreshCanvas(true); 991 } 992 for (let i = 0; i < res.length; i++) { 993 if (res[i].startName! < 6 && i < res.length - 1) { 994 res[i].endItid = res[i + 1].itid; 995 } 996 } 997 return res; 998 }); 999 startupRow.onThreadHandler = rowThreadHandler<AppStartupRender>( 1000 'app-start-up', 1001 'appStartupContext', 1002 { 1003 type: `app-startup ${processRow.rowId}`, 1004 }, 1005 startupRow, 1006 this.trace 1007 ); 1008 processRow.addChildTraceRow(startupRow); 1009 return startupRow; 1010 } 1011 1012 addSoInitRow(processRow: TraceRow<ProcessStruct>, maxDepth: number): TraceRow<SoStruct> { 1013 processRow.setAttribute('hasStaticInit', 'true'); 1014 let maxHeight = (maxDepth + 1) * 20; 1015 let soRow: TraceRow<SoStruct> = TraceRow.skeleton<SoStruct>(); 1016 soRow.rowId = `app-start-${processRow.rowId}`; 1017 soRow.rowType = TraceRow.ROW_TYPE_STATIC_INIT; 1018 soRow.rowParentId = `${processRow.rowId}`; 1019 soRow.rowHidden = !processRow.expansion; 1020 soRow.index = 0; 1021 soRow.style.height = `${maxHeight}px`; 1022 soRow.style.width = '100%'; 1023 soRow.name = 'Static Initialization'; 1024 soRow.setAttribute('children', ''); 1025 soRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 1026 soRow.selectChangeHandler = this.trace.selectChangeHandler; 1027 soRow.findHoverStruct = (): void => { 1028 SoStruct.hoverSoStruct = soRow.getHoverStruct(); 1029 }; 1030 soRow.supplierFrame = (): Promise<Array<SoStruct>> => 1031 processSoInitDataSender(parseInt(processRow.rowId!), soRow).then((res) => { 1032 if (res.length <= 0) { 1033 this.trace.refreshCanvas(true); 1034 } 1035 res.forEach((so, index) => { 1036 let soName = this.soInitNameMap.get(res[index].id!); 1037 if (soName) { 1038 so.soName = soName.replace('dlopen: ', ''); 1039 } 1040 }); 1041 return res; 1042 }); 1043 soRow.onThreadHandler = rowThreadHandler<SoRender>( 1044 'app-so-init', 1045 'context', 1046 { 1047 type: `static-init ${processRow.rowId}`, 1048 }, 1049 soRow, 1050 this.trace 1051 ); 1052 processRow.addChildTraceRow(soRow); 1053 return soRow; 1054 } 1055 1056 insertAfter(newEl: HTMLElement, targetEl: HTMLElement): void { 1057 let parentEl = targetEl.parentNode; 1058 if (parentEl!.lastChild === targetEl) { 1059 parentEl!.appendChild(newEl); 1060 } else { 1061 parentEl!.insertBefore(newEl, targetEl.nextSibling); 1062 } 1063 } 1064 1065 //add thread list 1066 async addThreadList( 1067 it: { pid: number | null; processName: string | null }, 1068 pRow: TraceRow<ProcessStruct>, 1069 expectedRow: TraceRow<JankStruct> | null, 1070 actualRow: TraceRow<JankStruct> | null, 1071 soRow: TraceRow<SoStruct> | undefined, 1072 startupRow: TraceRow<AppStartupStruct> | undefined, 1073 traceId?: string 1074 ): Promise<void> { 1075 let threads = this.processThreads.filter((thread) => thread.pid === it.pid && thread.tid !== 0); 1076 const sameThreadCounts: Record<string, number> = {}; //同名thread添加子进程 1077 const sameThreadList: any[] = []; 1078 const differentThreadList: any[] = []; 1079 threads.forEach(item => { 1080 const sameThread = item.threadName; 1081 if (sameThread !== undefined) { 1082 if (sameThread in sameThreadCounts) { 1083 sameThreadCounts[sameThread]++; 1084 } else { 1085 sameThreadCounts[sameThread] = 1; 1086 } 1087 } 1088 }); 1089 1090 threads.forEach((item) => { 1091 const sameThread = item.threadName; 1092 if (sameThreadCounts[sameThread!] > 128) { 1093 sameThreadList.push(item); 1094 } else { 1095 differentThreadList.push(item); 1096 } 1097 }); 1098 1099 differentThreadList.length && this.addDifferentThread(it, pRow, expectedRow, actualRow, soRow, startupRow, differentThreadList, traceId!); 1100 if (sameThreadList.length) { 1101 let sameThreadFolder = await this.initSameThreadFolder(it, pRow, sameThreadList, traceId!); 1102 if (sameThreadFolder) { 1103 pRow.addChildTraceRow(this.sameThreadFolder); 1104 } 1105 await this.initSameThreadData(sameThreadFolder, it, expectedRow, actualRow, soRow, startupRow, sameThreadList, traceId); 1106 } 1107 } 1108 1109 initSameThreadFolder(it: { pid: number | null; processName: string | null }, pRow: TraceRow<ProcessStruct>, list: Array<any>, traceId?: string) { 1110 let sameThreadRow = TraceRow.skeleton<ProcessStruct>(); 1111 sameThreadRow.rowId = 'sameThreadProcess'; 1112 sameThreadRow.rowParentId = `${it.pid}`; 1113 sameThreadRow.rowHidden = !pRow.expansion; 1114 sameThreadRow.rowType = TraceRow.ROW_TYPE_THREAD_NAME; 1115 sameThreadRow.folder = true; 1116 sameThreadRow.name = list[0].threadName; 1117 sameThreadRow.folderPaddingLeft = 20; 1118 sameThreadRow.style.height = '40px'; 1119 sameThreadRow.style.width = '100%'; 1120 sameThreadRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 1121 sameThreadRow.selectChangeHandler = this.trace.selectChangeHandler; 1122 sameThreadRow.supplierFrame = (): Promise<Array<ProcessStruct>> => 1123 new Promise<Array<ProcessStruct>>((resolve) => resolve([])); 1124 sameThreadRow.onThreadHandler = (useCache): void => { 1125 let context: CanvasRenderingContext2D; 1126 if (sameThreadRow.currentContext) { 1127 context = sameThreadRow.currentContext; 1128 } else { 1129 context = sameThreadRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!; 1130 } 1131 sameThreadRow.canvasSave(context); 1132 if (sameThreadRow.expansion) { 1133 // @ts-ignore 1134 context?.clearRect(0, 0, sameThreadRow.frame.width, sameThreadRow.frame.height); 1135 } else { 1136 (renders.empty as EmptyRender).renderMainThread( 1137 { 1138 context: context, 1139 useCache: useCache, 1140 type: '', 1141 }, 1142 sameThreadRow 1143 ); 1144 } 1145 sameThreadRow.canvasRestore(context, this.trace); 1146 }; 1147 this.sameThreadFolder = sameThreadRow; 1148 return this.sameThreadFolder; 1149 } 1150 1151 async initSameThreadData(sameThreadFolder: TraceRow<ProcessStruct>, it: { pid: number | null; processName: string | null }, 1152 expectedRow: TraceRow<JankStruct> | null, 1153 actualRow: TraceRow<JankStruct> | null, 1154 soRow: TraceRow<SoStruct> | undefined, 1155 startupRow: TraceRow<AppStartupStruct> | undefined, sameThreadList: Array<any>, traceId?: string): Promise<void> { 1156 let tRowArr: Array<TraceRow<BaseStruct>> = []; 1157 for (let j = 0; j < sameThreadList.length; j++) { 1158 let thread = sameThreadList[j]; 1159 let tRow = TraceRow.skeleton<ThreadStruct>(this.traceId); 1160 tRow.rowId = `${thread.tid}`; 1161 tRow.rowType = TraceRow.ROW_TYPE_THREAD; 1162 tRow.rowParentId = sameThreadFolder.rowId; 1163 tRow.rowHidden = !sameThreadFolder.expansion; 1164 tRow.index = j; 1165 tRow.style.height = '18px'; 1166 tRow.style.width = '100%'; 1167 tRow.name = `${thread.threadName || 'Thread'} ${thread.tid}`; 1168 tRow.namePrefix = `${thread.threadName || 'Thread'}`; 1169 tRow.setAttribute('children', ''); 1170 tRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 1171 tRow.selectChangeHandler = this.trace.selectChangeHandler; 1172 tRow.findHoverStruct = (): void => this.threadRowFindHoverStruct(tRow); 1173 tRow.supplierFrame = async (): Promise<Array<ThreadStruct>> => { 1174 const res = await threadDataSender(thread.tid || 0, it.pid || 0, tRow, this.traceId); 1175 if (res === true) { 1176 return []; 1177 } 1178 let rs = res as ThreadStruct[]; 1179 if (rs.length <= 0 && !tRow.isComplete) { 1180 this.trace.refreshCanvas(true); 1181 } 1182 return rs; 1183 }; 1184 tRow.onThreadHandler = rowThreadHandler<ThreadRender>( 1185 'thread', 1186 'context', 1187 { 1188 type: `thread ${thread.tid} ${thread.threadName}`, 1189 translateY: tRow.translateY, 1190 }, 1191 tRow, 1192 this.trace 1193 ); 1194 this.insertRowToDoc(it, j, thread, sameThreadFolder, tRow, sameThreadList, tRowArr, actualRow, expectedRow, startupRow, soRow); 1195 this.addFuncStackRow(it, thread, j, sameThreadList, tRowArr, tRow, sameThreadFolder, sameThreadFolder.rowId!); 1196 if ((thread.switchCount || 0) === 0) { 1197 tRow.rowDiscard = true; 1198 } 1199 sameThreadFolder.addChildTraceRow(tRow); 1200 } 1201 } 1202 1203 addDifferentThread(it: { pid: number | null; processName: string | null }, 1204 pRow: TraceRow<ProcessStruct>, 1205 expectedRow: TraceRow<JankStruct> | null, 1206 actualRow: TraceRow<JankStruct> | null, 1207 soRow: TraceRow<SoStruct> | undefined, 1208 startupRow: TraceRow<AppStartupStruct> | undefined, 1209 list: Array<any>, traceId?: string) { 1210 let tRowArr: Array<TraceRow<BaseStruct>> = []; 1211 for (let j = 0; j < list.length; j++) { 1212 let thread = list[j]; 1213 let tRow = TraceRow.skeleton<ThreadStruct>(this.traceId); 1214 tRow.rowId = `${thread.tid}`; 1215 tRow.rowType = TraceRow.ROW_TYPE_THREAD; 1216 tRow.rowParentId = `${it.pid}`; 1217 tRow.rowHidden = !pRow.expansion; 1218 tRow.index = j; 1219 tRow.style.height = '18px'; 1220 tRow.style.width = '100%'; 1221 tRow.name = `${thread.threadName || 'Thread'} ${thread.tid}`; 1222 tRow.namePrefix = `${thread.threadName || 'Thread'}`; 1223 tRow.setAttribute('children', ''); 1224 tRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 1225 tRow.selectChangeHandler = this.trace.selectChangeHandler; 1226 tRow.findHoverStruct = (): void => this.threadRowFindHoverStruct(tRow); 1227 tRow.supplierFrame = async (): Promise<Array<ThreadStruct>> => { 1228 const res = await threadDataSender(thread.tid || 0, it.pid || 0, tRow, this.traceId); 1229 if (res === true) { 1230 return []; 1231 } 1232 let rs = res as ThreadStruct[]; 1233 if (rs.length <= 0 && !tRow.isComplete) { 1234 this.trace.refreshCanvas(true); 1235 } 1236 return rs; 1237 }; 1238 tRow.onThreadHandler = rowThreadHandler<ThreadRender>( 1239 'thread', 1240 'context', 1241 { 1242 type: `thread ${thread.tid} ${thread.threadName}`, 1243 translateY: tRow.translateY, 1244 }, 1245 tRow, 1246 this.trace 1247 ); 1248 this.insertRowToDoc(it, j, thread, pRow, tRow, list, tRowArr, actualRow, expectedRow, startupRow, soRow); 1249 this.addFuncStackRow(it, thread, j, list, tRowArr, tRow, pRow); 1250 if ((thread.switchCount || 0) === 0) { 1251 tRow.rowDiscard = true; 1252 } 1253 } 1254 } 1255 1256 threadRowFindHoverStruct(threadRow: TraceRow<ThreadStruct>): void { 1257 let arr = threadRow.dataListCache.filter( 1258 (re) => re.frame && isFrameContainPoint(re.frame, threadRow.hoverX, threadRow.hoverY, true, false) 1259 ); 1260 let runItem = arr.find((it) => it.state === 'Running'); 1261 if (runItem) { 1262 ThreadStruct.hoverThreadStruct = runItem; 1263 } else { 1264 let otherItem = arr.find((it) => it.state !== 'S'); 1265 if (otherItem) { 1266 ThreadStruct.hoverThreadStruct = otherItem; 1267 } else { 1268 ThreadStruct.hoverThreadStruct = arr[0]; 1269 } 1270 } 1271 } 1272 1273 insertRowToDoc( 1274 it: unknown, 1275 index: number, 1276 thread: ThreadStruct, 1277 processRow: TraceRow<ProcessStruct>, 1278 threadRow: TraceRow<ThreadStruct>, 1279 threads: ThreadStruct[], 1280 //@ts-ignore 1281 threadRowArr: TraceRow<unknown>[], 1282 //@ts-ignore 1283 actualRow: TraceRow<unknown> | null, 1284 //@ts-ignore 1285 expectedRow: TraceRow<unknown> | null, 1286 startupRow: TraceRow<AppStartupStruct> | null | undefined, 1287 soRow: TraceRow<SoStruct> | null | undefined 1288 ): void { 1289 //@ts-ignore 1290 if (it.processName === 'render_service') { 1291 //@ts-ignore 1292 if (threadRow.name === `${it.processName} ${it.pid}`) { 1293 this.renderRow = threadRow; 1294 } 1295 let flag = threads.length === index + 1 && !this.threadFuncMaxDepthMap.has(`${thread.upid}-${thread.tid}`); 1296 //@ts-ignore 1297 processRow.sortRenderServiceData(threadRow, threadRow, threadRowArr, flag); 1298 } else { 1299 if (threadRow.rowId === threadRow.rowParentId) { 1300 if (actualRow !== null) { 1301 processRow.addChildTraceRowAfter(threadRow, actualRow); 1302 } else if (expectedRow !== null) { 1303 processRow.addChildTraceRowAfter(threadRow, expectedRow); 1304 } else if (soRow) { 1305 processRow.addChildTraceRowAfter(threadRow, soRow); 1306 } else if (startupRow) { 1307 processRow.addChildTraceRowAfter(threadRow, startupRow); 1308 } else { 1309 processRow.addChildTraceRowSpecifyLocation(threadRow, 0); 1310 } 1311 } else { 1312 processRow.addChildTraceRow(threadRow); 1313 } 1314 } 1315 } 1316 1317 addFuncStackRow( 1318 process: { pid: number | null; processName: string | null }, 1319 thread: unknown, 1320 index: number, 1321 threads: Array<unknown>, 1322 threadRowArr: Array<unknown>, 1323 threadRow: TraceRow<ThreadStruct>, 1324 processRow: TraceRow<ProcessStruct>, 1325 parentId?: string 1326 ): void { 1327 //@ts-ignore 1328 if (this.threadFuncMaxDepthMap.get(`${thread.upid}-${thread.tid}`) !== undefined) { 1329 //@ts-ignore 1330 let max = this.threadFuncMaxDepthMap.get(`${thread.upid}-${thread.tid}`) || 1; 1331 let maxHeight = max * 18 + 6; 1332 let funcRow = TraceRow.skeleton<FuncStruct>(this.traceId); 1333 //@ts-ignore 1334 funcRow.rowId = `${thread.tid}`; 1335 funcRow.rowType = TraceRow.ROW_TYPE_FUNC; 1336 funcRow.enableCollapseChart(FOLD_HEIGHT, this.trace); //允许折叠泳道图 1337 //@ts-ignore 1338 funcRow.rowParentId = parentId ? parentId : `${process.pid}`; 1339 funcRow.rowHidden = !processRow.expansion; 1340 funcRow.checkType = threadRow.checkType; 1341 funcRow.style.width = '100%'; 1342 funcRow.style.height = `${maxHeight}px`; 1343 //@ts-ignore 1344 funcRow.name = `${thread.threadName || 'Thread'} ${thread.tid}`; 1345 //@ts-ignore 1346 funcRow.namePrefix = `${thread.threadName || 'Thread'}`; 1347 funcRow.setAttribute('children', ''); 1348 funcRow.supplierFrame = async (): Promise<Array<FuncStruct>> => { 1349 //@ts-ignore 1350 const rs = await funcDataSender(thread.tid || 0, thread.upid || 0, funcRow, this.traceId); 1351 //@ts-ignore 1352 return this.funDataSenderCallback(rs, funcRow, thread); 1353 }; 1354 funcRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 1355 funcRow.selectChangeHandler = this.trace.selectChangeHandler; 1356 funcRow.findHoverStruct = (): void => { 1357 FuncStruct.hoverFuncStruct = funcRow.getHoverStruct(); 1358 }; 1359 funcRow.onThreadHandler = rowThreadHandler<FuncRender>( 1360 'func', 1361 'context', 1362 { 1363 //@ts-ignore 1364 type: `func${thread.tid}${thread.threadName}`, 1365 }, 1366 funcRow, 1367 this.trace 1368 ); //@ts-ignore 1369 if (process.processName === 'render_service') { 1370 let flag = threads.length === index + 1; 1371 //@ts-ignore 1372 processRow.sortRenderServiceData(funcRow, threadRow, threadRowArr, flag); 1373 } else { 1374 processRow.addChildTraceRowAfter(funcRow, threadRow); 1375 } 1376 this.addHangRow(process, processRow, funcRow, thread); 1377 } 1378 } 1379 1380 funDataSenderCallback( 1381 rs: Array<unknown> | boolean, 1382 funcRow: TraceRow<FuncStruct>, 1383 thread: ThreadStruct 1384 ): FuncStruct[] { 1385 if (rs === true) { 1386 funcRow.rowDiscard = true; 1387 return []; 1388 } else { 1389 let funs = rs as FuncStruct[]; 1390 if (funs.length > 0) { 1391 funs.forEach((fun, index) => { 1392 funs[index].itid = thread.utid; 1393 funs[index].ipid = thread.upid; 1394 funs[index].tid = thread.tid; 1395 funs[index].pid = thread.pid; 1396 funs[index].funName = this.traceId ? Utils.getInstance().getCallStatckMap().get(`${this.traceId}_${funs[index].id}`) : Utils.getInstance().getCallStatckMap().get(funs[index].id!); 1397 if (Utils.isBinder(fun)) { 1398 } else { 1399 if (fun.nofinish) { 1400 fun.flag = 'Did not end'; 1401 } 1402 } 1403 if (fun.id && this.distributedDataMap.has(`${fun.id}_${this.traceId}`)) { 1404 let distributedData = this.distributedDataMap.get(`${fun.id}_${this.traceId}`); 1405 funs[index].chainId = distributedData!.chainId; 1406 funs[index].spanId = distributedData!.spanId; 1407 funs[index].parentSpanId = distributedData!.parentSpanId; 1408 funs[index].chainFlag = distributedData!.chainFlag; 1409 funs[index].traceId = this.traceId; 1410 } 1411 }); 1412 } else { 1413 this.trace.refreshCanvas(true); 1414 } 1415 return funs; 1416 } 1417 } 1418 1419 //进程内存信息 1420 addProcessMemInfo(it: { pid: number | null; processName: string | null }, processRow: TraceRow<ProcessStruct>): void { 1421 //@ts-ignore 1422 let processMem = this.processMem.filter((mem) => mem.pid === it.pid); 1423 processMem.forEach((mem) => { 1424 let row = TraceRow.skeleton<ProcessMemStruct>(); 1425 //@ts-ignore 1426 row.rowId = `${mem.trackId}`; 1427 row.rowType = TraceRow.ROW_TYPE_MEM; 1428 row.rowParentId = `${it.pid}`; 1429 row.rowHidden = !processRow.expansion; 1430 row.style.height = '40px'; 1431 row.style.width = '100%'; 1432 //@ts-ignore 1433 row.name = `${mem.trackName}`; 1434 row.setAttribute('children', ''); 1435 row.favoriteChangeHandler = this.trace.favoriteChangeHandler; 1436 row.selectChangeHandler = this.trace.selectChangeHandler; 1437 row.focusHandler = (): void => { 1438 this.trace.displayTip( 1439 row, 1440 ProcessMemStruct.hoverProcessMemStruct, 1441 `<span>${ProcessMemStruct.hoverProcessMemStruct?.value || '0'}</span>` 1442 ); 1443 }; 1444 row.findHoverStruct = (): void => { 1445 ProcessMemStruct.hoverProcessMemStruct = row.getHoverStruct(false); 1446 }; 1447 row.supplierFrame = (): Promise<Array<ProcessMemStruct>> => 1448 //@ts-ignore 1449 processMemDataSender(mem.trackId, row).then((resultProcess) => { 1450 //@ts-ignore 1451 let maxValue = this.filterIdMaxValue.get(mem.trackId) || 0; 1452 for (let j = 0; j < resultProcess.length; j++) { 1453 resultProcess[j].maxValue = maxValue; 1454 if (j === resultProcess.length - 1) { 1455 resultProcess[j].duration = (TraceRow.range?.totalNS || 0) - (resultProcess[j].startTime || 0); 1456 } else { 1457 resultProcess[j].duration = (resultProcess[j + 1].startTime || 0) - (resultProcess[j].startTime || 0); 1458 } 1459 if (j > 0) { 1460 resultProcess[j].delta = (resultProcess[j].value || 0) - (resultProcess[j - 1].value || 0); 1461 } else { 1462 resultProcess[j].delta = 0; 1463 } 1464 } 1465 return resultProcess; 1466 }); 1467 row.onThreadHandler = rowThreadHandler<MemRender>( 1468 'mem', 1469 'context', 1470 { 1471 //@ts-ignore 1472 type: `mem ${mem.trackId} ${mem.trackName}`, 1473 }, 1474 row, 1475 this.trace 1476 ); 1477 if (this.renderRow && row.name === 'H:PreferredFrameRate') { 1478 processRow.addChildTraceRowBefore(row, this.renderRow); 1479 } else { 1480 processRow.addChildTraceRow(row); 1481 } 1482 }); 1483 } 1484 private calMaxHeight(asyncFunctions: unknown[]): number { 1485 let max = 0; 1486 asyncFunctions.forEach((it) => { 1487 //@ts-ignore 1488 const depth = it.depth || 0; 1489 if (depth > max) { 1490 max = depth; 1491 } 1492 }); 1493 max += 1; 1494 return max * 18 + 6; 1495 } 1496 //处理缓存数据的'startTs'字段统一成'startTime' 1497 private toAsyncFuncCache(object: unknown, name: string): void { 1498 // @ts-ignore 1499 let modifiedObject = { ...object }; 1500 modifiedObject.startTime = modifiedObject.startTs; 1501 Reflect.deleteProperty(modifiedObject, 'startTs'); 1502 modifiedObject.rowId = name; 1503 modifiedObject.type = 'func'; 1504 SpProcessChart.asyncFuncCache.push({ ...modifiedObject }); 1505 } 1506 //Async Function 1507 addAsyncFunction(it: { pid: number; processName: string | null }, processRow: TraceRow<ProcessStruct>): void { 1508 let isCategoryAsyncfunc: boolean = FlagsConfig.getFlagsConfigEnableStatus('Start&Finish Trace Category'); 1509 let asyncRemoveCatArr: unknown[]; 1510 let asyncCat: unknown; 1511 let setArrayLenThanOne: unknown; 1512 let setArrayLenOnlyOne: unknown; 1513 //@ts-ignore 1514 let asyncFuncList = this.processAsyncFuncMap[it.pid] || []; 1515 if (!asyncFuncList.length) { 1516 return; 1517 } 1518 let flag = FlagsConfig.getSecondarySelectValue('catValue') === 'business'; 1519 if (isCategoryAsyncfunc) {//聚合异步trace 1520 ({ asyncRemoveCatArr, asyncCat } = this.hanldCatFunc(asyncFuncList, flag));//处理是否cat 1521 ({ setArrayLenThanOne, setArrayLenOnlyOne } = this.hanldAsyncFunc(it, asyncRemoveCatArr));//len等于0和大于0的分类 1522 //@ts-ignore 1523 let aggregateData = {...setArrayLenThanOne, ...setArrayLenOnlyOne }; 1524 Reflect.ownKeys(aggregateData).map((key: unknown) => { 1525 let param: Array<unknown> = aggregateData[key]; 1526 //@ts-ignore 1527 this.makeAddAsyncFunction(param, it, processRow, key); 1528 }); 1529 //@ts-ignore 1530 Reflect.ownKeys(asyncCat).map((key: unknown) => { 1531 //@ts-ignore 1532 let param: Array<unknown> = asyncCat[key]; 1533 if (flag) {//处理business 1534 //@ts-ignore 1535 this.makeAddAsyncFunction(param, it, processRow, key); 1536 } else {//处理thread 1537 //@ts-ignore 1538 this.makeAddAsyncFunction(param, it, processRow, key, param[0].tid); 1539 } 1540 }); 1541 } else { 1542 //不聚合异步trace 1543 let asyncFuncGroup = Utils.groupBy(asyncFuncList, 'funName'); 1544 //@ts-ignore 1545 Reflect.ownKeys(asyncFuncGroup).map((key: unknown) => { 1546 //@ts-ignore 1547 let asyncFunctions: Array<unknown> = asyncFuncGroup[key]; 1548 if (asyncFunctions.length > 0) { 1549 let isIntersect = (a: unknown, b: unknown): boolean => 1550 // @ts-ignore 1551 Math.max(a.startTs + a.dur, b.startTs + b.dur) - Math.min(a.startTs, b.startTs) < a.dur + b.dur; 1552 let depthArray: unknown = []; 1553 asyncFunctions.forEach((it, i) => { 1554 // @ts-ignore 1555 if (it.dur === -1 || it.dur === null || it.dur === undefined) { 1556 // @ts-ignore 1557 it.dur = (TraceRow.range?.endNS || 0) - it.startTs; 1558 // @ts-ignore 1559 it.flag = 'Did not end'; 1560 } 1561 let currentDepth = 0; 1562 let index = i; 1563 while ( 1564 // @ts-ignore 1565 depthArray[currentDepth] !== undefined && 1566 // @ts-ignore 1567 isIntersect(depthArray[currentDepth], asyncFunctions[index]) 1568 ) { 1569 currentDepth++; 1570 } 1571 // @ts-ignore 1572 asyncFunctions[index].depth = currentDepth; 1573 // @ts-ignore 1574 depthArray[currentDepth] = asyncFunctions[index]; 1575 // @ts-ignore 1576 //处理缓存的异步trace数据缺失的字段 1577 this.toAsyncFuncCache(asyncFunctions[index], `${asyncFunctions[i].funName}-${it.pid}`); 1578 }); 1579 } 1580 //@ts-ignore 1581 this.lanesConfig(asyncFunctions, it, processRow, `${asyncFunctions[0].funName}`); 1582 }); 1583 } 1584 } 1585 //处理CategoryAsyncFunc 1586 hanldCatFunc( 1587 asyncFuncList: Array<unknown>, 1588 flag: boolean 1589 ): { asyncRemoveCatArr: Array<unknown>, asyncCat: unknown } { 1590 let asyncCat; 1591 let asyncCatArr = new Array(); 1592 let asyncCatMap: Map<string, unknown> = new Map<string, unknown>(); 1593 let asyncRemoveCatArr = new Array(); 1594 //取出cat字段(category)不为null的数据 1595 for (let i = 0; i < asyncFuncList.length; i++) { 1596 const el = asyncFuncList[i]; 1597 // @ts-ignore 1598 if (el.cat !== null) { 1599 if (flag) {//business first 1600 asyncCatArr.push(el); 1601 } else {//thread first 1602 //@ts-ignore 1603 if (asyncCatMap.has(`${el.cat}:${el.threadName} ${el.tid}`)) { 1604 //@ts-ignore 1605 let item: Array<unknown> = asyncCatMap.get(`${el.cat}:${el.threadName} ${el.tid}`); 1606 item.push(el); 1607 } else { 1608 //@ts-ignore 1609 asyncCatMap.set(`${el.cat}:${el.threadName} ${el.tid}`, [el]); 1610 } 1611 } 1612 } else { 1613 //取cat字段为null的数据 1614 asyncRemoveCatArr.push(el); 1615 } 1616 } 1617 asyncCat = flag ? Utils.groupBy(asyncCatArr, 'cat') : Object.fromEntries(asyncCatMap); 1618 return { asyncRemoveCatArr, asyncCat }; 1619 } 1620 //处理cat字段为null的数据,按funname分类,分别按len>1和=1去处理 1621 hanldAsyncFunc( 1622 it: { pid: number; processName: string | null }, 1623 asyncRemoveCatList: Array<unknown> 1624 ): { setArrayLenThanOne: unknown, setArrayLenOnlyOne: unknown } { 1625 let asyncFuncGroup = Utils.groupBy(asyncRemoveCatList, 'funName'); 1626 let funcArr: unknown[] = []; 1627 let setArrayLenThanOne: unknown = {}; 1628 let setArrayLenOnlyOne: unknown = {}; 1629 //@ts-ignore 1630 Reflect.ownKeys(asyncFuncGroup).map((key: unknown) => { 1631 //@ts-ignore 1632 let asyncFunctions: Array<unknown> = asyncFuncGroup[key]; 1633 if (asyncFunctions.length > 1) { 1634 //@ts-ignore 1635 setArrayLenThanOne[key] = asyncFunctions; 1636 } else if (asyncFunctions.length === 1) { 1637 funcArr.push(...asyncFunctions); 1638 } 1639 }); 1640 //len=1的数据继续按tid分类 1641 let asyncFuncTidGroup = Utils.groupBy(funcArr, 'tid'); 1642 //@ts-ignore 1643 Reflect.ownKeys(asyncFuncTidGroup).map((key: unknown) => { 1644 //@ts-ignore 1645 let asyncTidFunc: Array<unknown> = asyncFuncTidGroup[key]; 1646 //@ts-ignore 1647 setArrayLenOnlyOne[`H:${asyncTidFunc[0].threadName} ${asyncTidFunc[0].tid}`] = asyncTidFunc; 1648 }); 1649 return { setArrayLenThanOne, setArrayLenOnlyOne }; 1650 } 1651 makeAddAsyncFunction( 1652 asyncFunctions: unknown[], 1653 it: { pid: number; processName: string | null }, 1654 processRow: TraceRow<ProcessStruct>, 1655 key: string, 1656 rowSingleTid?: number 1657 ): void { 1658 let maxDepth: number = -1; 1659 let i = 0; 1660 let mapDepth = new Map(); 1661 let noEndData = new Array(); 1662 let normalData = new Array(); 1663 if (asyncFunctions.length) { 1664 while (i < asyncFunctions.length) { 1665 let param = asyncFunctions[i]; 1666 // @ts-ignore 1667 if (param.dur !== null) { 1668 // @ts-ignore 1669 let itemEndTime = param.startTs + param.dur; 1670 let flag = false; 1671 for (let [key, val] of mapDepth.entries()) { 1672 // @ts-ignore 1673 if (val.et < param.startTs) { 1674 flag = true; 1675 //更新endts 1676 val.et = itemEndTime; 1677 // @ts-ignore 1678 param.depth = Number(key); 1679 break; 1680 } 1681 } 1682 if (!flag) { 1683 //depth增加 1684 maxDepth++; 1685 mapDepth.set(`${maxDepth}`, { et: itemEndTime }); 1686 // @ts-ignore 1687 param.depth = maxDepth; 1688 } 1689 this.toAsyncFuncCache(param, `${key}-${it.pid}`); 1690 normalData.push(param); 1691 } else { 1692 noEndData.push(param); 1693 } 1694 i++; 1695 } 1696 if (noEndData.length) { 1697 noEndData.forEach((it: unknown, i: unknown) => { 1698 // @ts-ignore 1699 if (it.dur === -1 || it.dur === null || it.dur === undefined) { 1700 // @ts-ignore 1701 it.dur = (TraceRow.range?.endNS || 0) - it.startTs; 1702 // @ts-ignore 1703 it.nofinish = true; 1704 // @ts-ignore 1705 it.flag = 'Did not end'; 1706 } 1707 let index = i; 1708 maxDepth++; 1709 // @ts-ignore 1710 noEndData[index].depth = maxDepth; 1711 //@ts-ignore 1712 this.toAsyncFuncCache(noEndData[index], `${key}-${it.pid}`); 1713 }); 1714 } 1715 this.lanesConfig([...normalData, ...noEndData], it, processRow, key, rowSingleTid); 1716 } 1717 } 1718 //初始化异步泳道信息 1719 lanesConfig( 1720 asyncFunctions: unknown[], 1721 it: { pid: number; processName: string | null }, 1722 processRow: TraceRow<ProcessStruct>, 1723 key: string, 1724 rowSingleTid?: number 1725 ): void { 1726 const maxHeight = this.calMaxHeight(asyncFunctions); 1727 // @ts-ignore 1728 const namesSet = new Set(asyncFunctions.map((item) => item.funName)); 1729 const asyncFuncName = Array.from(namesSet); 1730 let funcRow = TraceRow.skeleton<FuncStruct>(); 1731 funcRow.rowId = `${key}-${it.pid}`; 1732 funcRow.asyncFuncName = asyncFuncName; 1733 funcRow.asyncFuncNamePID = it.pid; 1734 funcRow.asyncFuncStartTID = rowSingleTid ? rowSingleTid : undefined; 1735 funcRow.rowType = TraceRow.ROW_TYPE_FUNC; 1736 funcRow.enableCollapseChart(FOLD_HEIGHT, this.trace); //允许折叠泳道图 1737 funcRow.rowParentId = `${it.pid}`; 1738 funcRow.rowHidden = !processRow.expansion; 1739 funcRow.style.width = '100%'; 1740 funcRow.style.height = `${maxHeight}px`; 1741 funcRow.setAttribute('height', `${maxHeight}`); 1742 funcRow.name = key; 1743 funcRow.setAttribute('children', ''); 1744 funcRow.findHoverStruct = (): void => { 1745 FuncStruct.hoverFuncStruct = funcRow.getHoverStruct(); 1746 }; 1747 // @ts-ignore 1748 funcRow.supplier = (): Promise<unknown> => new Promise((resolve) => resolve(asyncFunctions)); 1749 funcRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 1750 funcRow.selectChangeHandler = this.trace.selectChangeHandler; 1751 funcRow.onThreadHandler = rowThreadHandler<FuncRender>( 1752 'func', 1753 'context', 1754 { 1755 type: `func-${funcRow.rowId}`, 1756 }, 1757 funcRow, 1758 this.trace 1759 ); 1760 processRow.addChildTraceRow(funcRow); 1761 } 1762 1763 addAsyncCatFunction(it: { pid: number; processName: string | null }, processRow: TraceRow<ProcessStruct>): void { 1764 //@ts-ignore 1765 let asyncFuncCatList = this.processAsyncFuncCatMap[it.pid] || []; 1766 let asyncFuncGroup: unknown = Utils.groupBy(asyncFuncCatList, 'threadName'); 1767 //@ts-ignore 1768 Reflect.ownKeys(asyncFuncGroup).map((key: unknown) => { 1769 //@ts-ignore 1770 let asyncFunctions: Array<unknown> = asyncFuncGroup[key]; 1771 if (asyncFunctions.length > 0) { 1772 let isIntersect = (a: unknown, b: unknown): boolean => 1773 //@ts-ignore 1774 Math.max(a.startTs + a.dur, b.startTs + b.dur) - Math.min(a.startTs, b.startTs) < a.dur + b.dur; 1775 let depthArray: unknown = []; 1776 asyncFunctions.forEach((it, i) => { 1777 //@ts-ignore 1778 if (it.dur === -1 || it.dur === null || it.dur === undefined) { 1779 //@ts-ignore 1780 it.dur = (TraceRow.range?.endNS || 0) - it.startTs; 1781 //@ts-ignore 1782 it.flag = 'Did not end'; 1783 //@ts-ignore 1784 it.nofinish = true; 1785 } 1786 let currentDepth = 0; 1787 let index = i; 1788 //@ts-ignore 1789 while (depthArray[currentDepth] !== undefined && isIntersect(depthArray[currentDepth], asyncFunctions[index])) { 1790 currentDepth++; 1791 }//@ts-ignore 1792 asyncFunctions[index].depth = currentDepth; 1793 //@ts-ignore 1794 depthArray[currentDepth] = asyncFunctions[index]; 1795 //@ts-ignore 1796 this.toAsyncFuncCache(asyncFunctions[index], `${asyncFunctions[0].threadName}`); 1797 }); 1798 const maxHeight = this.calMaxHeight(asyncFunctions); 1799 let funcRow = TraceRow.skeleton<FuncStruct>(); 1800 //@ts-ignore 1801 funcRow.rowId = `${asyncFunctions[0].threadName}`; 1802 //@ts-ignore 1803 funcRow.asyncFuncThreadName = asyncFunctions[0].threadName; 1804 funcRow.asyncFuncNamePID = it.pid; 1805 funcRow.rowType = TraceRow.ROW_TYPE_FUNC; 1806 funcRow.enableCollapseChart(FOLD_HEIGHT, this.trace); 1807 //允许折叠泳道图 1808 funcRow.rowParentId = `${it.pid}`; 1809 funcRow.rowHidden = !processRow.expansion; 1810 funcRow.style.width = '100%'; 1811 funcRow.style.height = `${maxHeight}px`; 1812 funcRow.setAttribute('height', `${maxHeight}`); 1813 //@ts-ignore 1814 funcRow.name = `${asyncFunctions[0].threadName}`; 1815 funcRow.setAttribute('children', ''); 1816 funcRow.findHoverStruct = (): void => { 1817 FuncStruct.hoverFuncStruct = funcRow.getHoverStruct(); 1818 }; 1819 //@ts-ignore 1820 funcRow.supplier = (): Promise<unknown> => new Promise((resolve) => resolve(asyncFunctions)); 1821 funcRow.favoriteChangeHandler = this.trace.favoriteChangeHandler; 1822 funcRow.selectChangeHandler = this.trace.selectChangeHandler; 1823 funcRow.onThreadHandler = rowThreadHandler<FuncRender>('func', 'context', { 1824 //@ts-ignore 1825 type: `func-${asyncFunctions[0].threadName}-${it.pid}`, 1826 }, funcRow, this.trace); 1827 processRow.addChildTraceRow(funcRow); 1828 } 1829 }); 1830 } 1831} 1832