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