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