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