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