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