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