• 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 { getMaxDepthByTid, queryAllFuncNames, queryProcessAsyncFunc } from '../../database/sql/Func.sql';
40import { queryMemFilterIdMaxValue } from '../../database/sql/Memory.sql';
41import { queryAllSoInitNames, queryAllSrcSlices, queryEventCountMap } from '../../database/sql/SqlLite.sql';
42import {
43  queryProcess,
44  queryProcessByTable,
45  queryProcessContentCount,
46  queryProcessMem,
47  queryProcessSoMaxDepth,
48  queryProcessThreads,
49  queryProcessThreadsByTable,
50  queryStartupPidArray,
51  queryRsProcess,
52  queryTaskPoolProcessIds,
53} from '../../database/sql/ProcessThread.sql';
54import { queryAllJankProcess } from '../../database/sql/Janks.sql';
55import { BaseStruct } from '../../bean/BaseStruct';
56
57export class SpProcessChart {
58  private readonly trace: SpSystemTrace;
59  private processAsyncFuncMap: unknown = {};
60  private processAsyncFuncArray: unknown[] = [];
61  private eventCountMap: unknown;
62  private processThreads: Array<ThreadStruct> = [];
63  private processMem: Array<unknown> = [];
64  private processThreadCountMap: Map<number, number> = new Map();
65  private processThreadDataCountMap: Map<number, number> = new Map();
66  private processFuncDataCountMap: Map<number, number> = new Map();
67  private processMemDataCountMap: Map<number, number> = new Map();
68  private threadFuncMaxDepthMap: Map<string, number> = new Map();
69  private startupProcessArr: { pid: number }[] = [];
70  private processSoMaxDepth: { pid: number; maxDepth: number }[] = [];
71  private funcNameMap: Map<number, string> = new Map();
72  private filterIdMaxValue: Map<number, number> = new Map();
73  private soInitNameMap: Map<number, string> = new Map();
74  private processSrcSliceMap: Map<number, string> = new Map();
75  private renderRow: TraceRow<BaseStruct> | null = null;
76  private loadAppStartup: boolean = false;
77  constructor(trace: SpSystemTrace) {
78    this.trace = trace;
79  }
80
81  initAsyncFuncData = async (traceRange: { startTs: number; endTs: number }): Promise<void> => {
82    const funcNamesArray = await queryAllFuncNames();
83    funcNamesArray.forEach((it) => {
84      //@ts-ignore
85      this.funcNameMap.set(it.id, it.name);
86    });
87    let asyncFuncList: unknown[] = await queryProcessAsyncFunc(traceRange);
88    for (const func of asyncFuncList) {
89      //@ts-ignore
90      func.funName = this.funcNameMap.get(func.id); //@ts-ignore
91      func.threadName = Utils.THREAD_MAP.get(func.tid);
92    }
93    info('AsyncFuncData Count is: ', asyncFuncList!.length);
94    this.processAsyncFuncArray = asyncFuncList;
95    this.processAsyncFuncMap = Utils.groupBy(asyncFuncList, 'pid');
96  };
97
98  initDeliverInputEvent = async (): Promise<void> => {
99    let row = TraceRow.skeleton();
100    row.setAttribute('disabled-check', '');
101    row.rowId = 'DeliverInputEvent';
102    row.index = 0;
103    row.rowType = TraceRow.ROW_TYPE_DELIVER_INPUT_EVENT;
104    row.rowParentId = '';
105    row.folder = true;
106    row.style.height = '40px';
107    row.name = 'DeliverInputEvent';
108    // @ts-ignore
109    row.supplier = folderSupplier();
110    row.onThreadHandler = folderThreadHandler(row, this.trace);
111
112    let asyncFuncGroup = Utils.groupBy(
113      //@ts-ignore
114      this.processAsyncFuncArray.filter((it) => it.funName === 'deliverInputEvent'),
115      'tid'
116    ); // @ts-ignore
117    if (Reflect.ownKeys(asyncFuncGroup).length > 0) {
118      this.trace.rowsEL?.appendChild(row);
119    } // @ts-ignore
120    Reflect.ownKeys(asyncFuncGroup).map((key: unknown) => {
121      // @ts-ignore
122      let asyncFuncGroups: Array<unknown> = asyncFuncGroup[key];
123      if (asyncFuncGroups.length > 0) {
124        //@ts-ignore
125        row.addChildTraceRow(this.createDeliverInputEventRow(row, key, asyncFuncGroups));
126      }
127    });
128  };
129
130  private createDeliverInputEventRow(
131    //@ts-ignore
132    parentRow: TraceRow<unknown>,
133    key: number,
134    asyncFuncGroups: Array<unknown>
135  ): TraceRow<FuncStruct> {
136    let funcRow = TraceRow.skeleton<FuncStruct>(); //@ts-ignore
137    funcRow.rowId = `${asyncFuncGroups[0].funName}-${key}`; //@ts-ignore
138    funcRow.asyncFuncName = asyncFuncGroups[0].funName;
139    funcRow.asyncFuncNamePID = key;
140    funcRow.rowType = TraceRow.ROW_TYPE_FUNC;
141    funcRow.enableCollapseChart(); //允许折叠泳道图
142    funcRow.rowParentId = `${parentRow.rowId}`;
143    funcRow.rowHidden = !parentRow.expansion;
144    funcRow.style.width = '100%'; //@ts-ignore
145    funcRow.name = `${asyncFuncGroups[0].funName} ${key}`;
146    funcRow.setAttribute('children', '');
147    funcRow.supplierFrame = async (): Promise<FuncStruct[]> => {
148      const res = await processDeliverInputEventDataSender(key, funcRow!);
149      this.deliverInputEventSendCallback(res, funcRow, asyncFuncGroups);
150      return res;
151    };
152    funcRow.findHoverStruct = (): void => {
153      FuncStruct.hoverFuncStruct = funcRow.getHoverStruct();
154    };
155    funcRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
156    funcRow.selectChangeHandler = this.trace.selectChangeHandler;
157    funcRow.onThreadHandler = rowThreadHandler<FuncRender>(
158      'func',
159      'context',
160      {
161        //@ts-ignore
162        type: `func-${asyncFuncGroups[0].funName}-${key}`,
163      },
164      funcRow,
165      this.trace
166    );
167    return funcRow;
168  }
169  //@ts-ignore
170  private deliverInputEventSendCallback(
171    res: Array<unknown>, //@ts-ignore
172    funcRow: TraceRow<unknown>,
173    asyncFuncGroups: Array<unknown>
174  ): void {
175    let isIntersect = (
176      left: unknown,
177      right: unknown
178    ): boolean => //@ts-ignore
179      Math.max(left.startTs + left.dur, right.startTs + right.dur) - Math.min(left.startTs, right.startTs) < //@ts-ignore
180      left.dur + right.dur;
181    let depths: unknown = [];
182    let createDepth = (currentDepth: number, index: number): void => {
183      //@ts-ignore
184      if (depths[currentDepth] === undefined || !isIntersect(depths[currentDepth], res[index])) {
185        //@ts-ignore
186        res[index].depth = currentDepth; //@ts-ignore
187        depths[currentDepth] = res[index];
188      } else {
189        createDepth(++currentDepth, index);
190      }
191    };
192    res.forEach((it, i): void => {
193      //@ts-ignore
194      res[i].funName = this.funcNameMap.get(res[i].id!); //@ts-ignore
195      res[i].threadName = Utils.THREAD_MAP.get(res[i].tid!); //@ts-ignore
196      if (it.dur === -1 || it.dur === null || it.dur === undefined) {
197        //@ts-ignore
198        it.dur = (TraceRow.range?.endNS || 0) - it.startTs; //@ts-ignore
199        it.flag = 'Did not end';
200      }
201      createDepth(0, i);
202    });
203    if (funcRow && !funcRow.isComplete) {
204      //@ts-ignore
205      let max = Math.max(...asyncFuncGroups.map((it) => it.depth || 0)) + 1;
206      let maxHeight = max * 18 + 6;
207      funcRow.style.height = `${maxHeight}px`;
208      funcRow.setAttribute('height', `${maxHeight}`);
209    }
210  }
211
212  async init(): Promise<void> {
213    await this.prepareData();
214    if (
215      //@ts-ignore
216      this.eventCountMap.print === 0 && //@ts-ignore
217      this.eventCountMap.tracing_mark_write === 0 && //@ts-ignore
218      this.eventCountMap.sched_switch === 0
219    ) {
220      return;
221    }
222    let time = new Date().getTime();
223    let processes = await queryProcess();
224    let processFromTable = await queryProcessByTable();
225    let processList = Utils.removeDuplicates(processes, processFromTable, 'pid');
226    let allJankProcessData = await queryAllJankProcess();
227    let allJankProcess: Array<number> = [];
228    if (allJankProcessData.length > 0) {
229      allJankProcessData.forEach((name, index) => {
230        allJankProcess.push(name.pid!);
231      });
232    }
233    let allTaskPoolPid: Array<{ pid: number }> = [];
234    if (FlagsConfig.getFlagsConfigEnableStatus('TaskPool')) {
235      allTaskPoolPid = await queryTaskPoolProcessIds();
236    }
237    let renderServiceProcess = await queryRsProcess(); // @ts-ignore
238    info('ProcessList Data size is: ', processList!.length); // @ts-ignore
239    await this.initProcessRow(processList, allTaskPoolPid, allJankProcess, renderServiceProcess);
240    let durTime = new Date().getTime() - time;
241    info('The time to load the Process data is: ', durTime);
242  }
243
244  private async prepareData(): Promise<void> {
245    let maxValues = await queryMemFilterIdMaxValue();
246    maxValues.forEach((it) => {
247      this.filterIdMaxValue.set(it.filterId, it.maxValue);
248    });
249
250    let soInitNamesArray = await queryAllSoInitNames();
251    soInitNamesArray.forEach((it) => {
252      //@ts-ignore
253      this.soInitNameMap.set(it.id, it.name);
254    });
255    let processSrcSliceArray = await queryAllSrcSlices();
256    processSrcSliceArray.forEach((it) => {
257      //@ts-ignore
258      this.processSrcSliceMap.set(it.id, it.src);
259    });
260    let threadFuncMaxDepthArray = await getMaxDepthByTid();
261    info('Gets the maximum tier per thread , tid and maxDepth');
262    threadFuncMaxDepthArray.forEach((it) => {
263      //@ts-ignore
264      this.threadFuncMaxDepthMap.set(`${it.ipid}-${it.tid}`, it.maxDepth);
265    });
266    info('convert tid and maxDepth array to map');
267    let pidCountArray = await queryProcessContentCount();
268    info('fetch per process  pid,switch_count,thread_count,slice_count,mem_count');
269    pidCountArray.forEach((it) => {
270      //@ts-ignore
271      this.processThreadDataCountMap.set(it.pid, it.switch_count); //@ts-ignore
272      this.processThreadCountMap.set(it.pid, it.thread_count); //@ts-ignore
273      this.processFuncDataCountMap.set(it.pid, it.slice_count); //@ts-ignore
274      this.processMemDataCountMap.set(it.pid, it.mem_count);
275    });
276    this.processMem = await queryProcessMem();
277    info('The amount of initialized process memory data is : ', this.processMem!.length);
278    this.loadAppStartup = FlagsConfig.getFlagsConfigEnableStatus('AppStartup');
279    info('Prepare App startup data ');
280    if (this.loadAppStartup) {
281      this.startupProcessArr = await queryStartupPidArray();
282      this.processSoMaxDepth = await queryProcessSoMaxDepth();
283    }
284    let eventCountList: Array<unknown> = await queryEventCountMap();
285    this.eventCountMap = eventCountList.reduce((pre, current) => {
286      //@ts-ignore
287      pre[`${current.eventName}`] = current.count;
288      return pre;
289    }, {});
290    let queryProcessThreadResult = await queryProcessThreads();
291    let queryProcessThreadsByTableResult = await queryProcessThreadsByTable(); // @ts-ignore
292    this.processThreads = Utils.removeDuplicates(queryProcessThreadResult, queryProcessThreadsByTableResult, 'tid');
293    info('The amount of initialized process threads data is : ', this.processThreads!.length);
294  }
295
296  private async initProcessRow(
297    pArr: Array<unknown>,
298    allTaskPoolPid: Array<{ pid: number }>,
299    jankArr: Array<number>,
300    rsProcess: Array<unknown>
301  ): Promise<void> {
302    for (let i = 0; i < pArr.length; i++) {
303      const it = pArr[i];
304      if (
305        //@ts-ignore
306        (this.processThreadDataCountMap.get(it.pid) || 0) === 0 && //@ts-ignore
307        (this.processThreadCountMap.get(it.pid) || 0) === 0 && //@ts-ignore
308        (this.processFuncDataCountMap.get(it.pid) || 0) === 0 && //@ts-ignore
309        (this.processMemDataCountMap.get(it.pid) || 0) === 0
310      ) {
311        continue;
312      }
313      let processRow = this.createProcessRow(i, it, allTaskPoolPid);
314      this.trace.rowsEL?.appendChild(processRow);
315      /* App Startup row*/
316      let startupRow: TraceRow<AppStartupStruct> | undefined = undefined;
317      let soRow: TraceRow<SoStruct> | undefined = undefined;
318      if (this.loadAppStartup) {
319        //@ts-ignore
320        if (this.startupProcessArr.find((sp) => sp.pid === it.pid)) {
321          startupRow = this.addStartUpRow(processRow);
322        } //@ts-ignore
323        let maxSoDepth = this.processSoMaxDepth.find((md) => md.pid === it.pid);
324        if (maxSoDepth) {
325          soRow = this.addSoInitRow(processRow, maxSoDepth.maxDepth);
326        }
327      }
328
329      let actualRow: TraceRow<JankStruct> | null = null;
330      let expectedRow: TraceRow<JankStruct> | null = null;
331      this.renderRow = null; //@ts-ignore
332      if (it.processName === 'render_service') {
333        //@ts-ignore
334        this.addThreadList(it, processRow, expectedRow, actualRow, soRow, startupRow); //@ts-ignore
335        this.addProcessMemInfo(it, processRow); //@ts-ignore
336        if (jankArr.indexOf(it.pid!) > -1) {
337          expectedRow = this.addExpectedRow(it, processRow, rsProcess);
338          actualRow = this.addActualRow(it, processRow, rsProcess);
339        }
340        this.addProcessRowListener(processRow, actualRow); //@ts-ignore
341        this.addAsyncFunction(it, processRow);
342      } else {
343        //@ts-ignore
344        if (jankArr.indexOf(it.pid!) > -1) {
345          expectedRow = this.addExpectedRow(it, processRow, rsProcess);
346          actualRow = this.addActualRow(it, processRow, rsProcess);
347        }
348        this.addProcessRowListener(processRow, actualRow); //@ts-ignore
349        this.addAsyncFunction(it, processRow); //@ts-ignore
350        this.addProcessMemInfo(it, processRow); //@ts-ignore
351        this.addThreadList(it, processRow, expectedRow, actualRow, soRow, startupRow);
352      }
353
354      //@ts-ignore
355      await this.trace.chartManager?.frameTimeChart.initAnimatedScenesChart(processRow, it, expectedRow!, actualRow!);
356    }
357  }
358
359  private createProcessRow(
360    index: number,
361    process: unknown,
362    allTaskPoolPid: Array<{ pid: number }>
363  ): TraceRow<ProcessStruct> {
364    let processRow = TraceRow.skeleton<ProcessStruct>(); //@ts-ignore
365    processRow.rowId = `${process.pid}`;
366    processRow.index = index;
367    processRow.rowType = TraceRow.ROW_TYPE_PROCESS;
368    processRow.rowParentId = '';
369    processRow.style.height = '40px';
370    processRow.folder = true;
371    if (
372      //@ts-ignore
373      SpChartManager.APP_STARTUP_PID_ARR.find((pid) => pid === process.pid) !== undefined || //@ts-ignore
374      process.processName === 'render_service'
375    ) {
376      processRow.addTemplateTypes('AppStartup');
377    }
378    if (allTaskPoolPid.find((process) => process.pid === process.pid) !== undefined) {
379      processRow.addTemplateTypes('TaskPool');
380    } //@ts-ignore
381    processRow.name = `${process.processName || 'Process'} ${process.pid}`; //@ts-ignore
382    processRow.supplierFrame = (): Promise<Array<unknown>> => {
383      //@ts-ignore
384      return processDataSender(process.pid || -1, processRow);
385    };
386    processRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
387    processRow.selectChangeHandler = this.trace.selectChangeHandler;
388    processRow.onThreadHandler = rowThreadHandler<ProcessRender>(
389      'process',
390      'context',
391      {
392        //@ts-ignore
393        pid: process.pid, //@ts-ignore
394        type: `process ${processRow.index} ${process.processName}`,
395      },
396      processRow,
397      this.trace
398    );
399    return processRow;
400  }
401
402  addProcessRowListener(processRow: TraceRow<ProcessStruct>, actualRow: TraceRow<JankStruct> | null): void {
403    let offsetYTimeOut: unknown = undefined;
404    processRow.addEventListener('expansion-change', (e: unknown) => {
405      JankStruct.delJankLineFlag = false;
406      if (offsetYTimeOut) {
407        //@ts-ignore
408        clearTimeout(offsetYTimeOut);
409      }
410      if (JankStruct.selectJankStruct !== null && JankStruct.selectJankStruct !== undefined) {
411        //@ts-ignore
412        if (e.detail.expansion) {
413          offsetYTimeOut = setTimeout(() => {
414            this.trace.linkNodes.forEach((linkNodeItem) => this.handler1(e, linkNodeItem, actualRow));
415          }, 300);
416        } else {
417          if (JankStruct!.selectJankStruct) {
418            JankStruct.selectJankStructList?.push(<JankStruct>JankStruct!.selectJankStruct);
419          }
420          offsetYTimeOut = setTimeout(() => {
421            this.trace.linkNodes?.forEach((linkProcessItem) => this.handler2(e, linkProcessItem, processRow));
422          }, 300);
423        }
424      } else {
425        //@ts-ignore
426        if (e.detail.expansion) {
427          offsetYTimeOut = setTimeout(() => {
428            this.trace.linkNodes.forEach((linkNodeItem) => this.handler3(e, linkNodeItem));
429          }, 300);
430        } else {
431          if (ThreadStruct!.selectThreadStruct) {
432            ThreadStruct.selectThreadStructList?.push(<ThreadStruct>ThreadStruct!.selectThreadStruct);
433          }
434          offsetYTimeOut = setTimeout(() => {
435            this.trace.linkNodes?.forEach((linkProcessItem) => {
436              this.handler4(e, linkProcessItem, processRow);
437              JankStruct.selectJankStructList = [];
438            });
439          }, 300);
440        }
441      }
442      let refreshTimeOut = setTimeout(() => {
443        this.trace.refreshCanvas(true);
444        clearTimeout(refreshTimeOut);
445      }, 360);
446    });
447  }
448
449  handler1(e: unknown, linkItem: PairPoint[], actualRow: TraceRow<JankStruct> | null): void {
450    JankStruct.selectJankStructList?.forEach((selectProcessStruct: unknown) => {
451      //@ts-ignore
452      if (e.detail.rowId === selectProcessStruct.pid) {
453        //@ts-ignore
454        JankStruct.selectJankStruct = selectProcessStruct; //@ts-ignore
455        JankStruct.hoverJankStruct = selectProcessStruct;
456      }
457    });
458    this.updatePairPointTranslateY(linkItem[0]);
459    linkItem[0].y = linkItem[0].rowEL!.translateY! + linkItem[0].offsetY;
460    this.updatePairPointTranslateY(linkItem[1]);
461    linkItem[1].y = linkItem[1].rowEL!.translateY! + linkItem[1].offsetY;
462    if (actualRow) {
463      //@ts-ignore
464      if (linkItem[0].rowEL.rowId === e.detail.rowId) {
465        linkItem[0].x = ns2xByTimeShaft(linkItem[0].ns, this.trace.timerShaftEL!);
466        linkItem[0].y = actualRow!.translateY! + linkItem[0].offsetY * 2;
467        linkItem[0].offsetY = linkItem[0].offsetY * 2;
468        //@ts-ignore
469        linkItem[0].rowEL = actualRow!;
470        //@ts-ignore
471      } else if (linkItem[1].rowEL.rowId === e.detail.rowId) {
472        linkItem[1].x = ns2xByTimeShaft(linkItem[1].ns, this.trace.timerShaftEL!);
473        linkItem[1].y = actualRow!.translateY! + linkItem[1].offsetY * 2;
474        linkItem[1].offsetY = linkItem[1].offsetY * 2;
475        //@ts-ignore
476        linkItem[1].rowEL = actualRow!;
477      }
478    }
479  }
480
481  handler2(e: unknown, linkItem: PairPoint[], processRow: TraceRow<ProcessStruct>): void {
482    this.updatePairPointTranslateY(linkItem[0]);
483    linkItem[0].y = linkItem[0].rowEL!.translateY! + linkItem[0].offsetY;
484    this.updatePairPointTranslateY(linkItem[1]);
485    linkItem[1].y = linkItem[1].rowEL!.translateY! + linkItem[1].offsetY; //@ts-ignore
486    if (linkItem[0].rowEL.rowParentId === e.detail.rowId) {
487      this.updatePairPoint(linkItem[0], processRow); //@ts-ignore
488    } else if (linkItem[1].rowEL.rowParentId === e.detail.rowId) {
489      this.updatePairPoint(linkItem[1], processRow);
490    }
491  }
492
493  handler3(e: unknown, linkItem: PairPoint[]): void {
494    ThreadStruct.selectThreadStructList?.forEach((selectProcessStruct: unknown) => {
495      //@ts-ignore
496      if (e.detail.rowId === selectProcessStruct.pid) {
497        //@ts-ignore
498        ThreadStruct.selectThreadStruct = selectProcessStruct; //@ts-ignore
499        ThreadStruct.hoverThreadStruct = selectProcessStruct;
500      }
501    });
502    if (linkItem[0].rowEL.expansion && linkItem[0].backrowEL) {
503      this.updatePairPointTranslateY(linkItem[0]);
504      linkItem[0].x = ns2xByTimeShaft(linkItem[0].ns, this.trace.timerShaftEL!);
505      linkItem[0].y = linkItem[0].rowEL.translateY + linkItem[0].offsetY;
506      linkItem[0].offsetY = linkItem[0].offsetY * 2;
507      linkItem[0].rowEL = linkItem[0].backrowEL;
508    }
509    if (linkItem[1].rowEL.expansion && linkItem[1].backrowEL) {
510      this.updatePairPointTranslateY(linkItem[1]);
511      linkItem[1].x = ns2xByTimeShaft(linkItem[1].ns, this.trace.timerShaftEL!);
512      linkItem[1].y = linkItem[1].rowEL!.translateY! + linkItem[1].offsetY;
513      linkItem[1].offsetY = linkItem[1].offsetY * 2;
514      linkItem[1].rowEL = linkItem[1].backrowEL;
515    }
516  }
517
518  handler4(e: unknown, linkItem: PairPoint[], processRow: TraceRow<ProcessStruct>): void {
519    this.updatePairPointTranslateY(linkItem[0]);
520    linkItem[0].y = processRow!.translateY + linkItem[0].offsetY;
521    this.updatePairPointTranslateY(linkItem[1]);
522    linkItem[1].y = linkItem[1].rowEL!.translateY + linkItem[1].offsetY; //@ts-ignore
523    if (linkItem[0].rowEL.rowParentId === e.detail.rowId) {
524      //@ts-ignore
525      this.updatePairPoint(linkItem[0], processRow);
526    } //@ts-ignore
527    if (linkItem[1].rowEL.rowParentId === e.detail.rowId) {
528      this.updatePairPoint(linkItem[1], processRow);
529    }
530  }
531
532  updatePairPointTranslateY(pair: PairPoint): void {
533    if (pair.rowEL.collect) {
534      pair.rowEL.translateY = pair.rowEL.getBoundingClientRect().top - 195;
535    } else {
536      pair.rowEL.translateY = pair.rowEL.offsetTop - this.trace.rowsPaneEL!.scrollTop;
537    }
538  }
539
540  updatePairPoint(pair: PairPoint, processRow: TraceRow<ProcessStruct>): void {
541    if (!pair.rowEL.collect) {
542      pair.x = ns2xByTimeShaft(pair.ns, this.trace.timerShaftEL!);
543      pair.y = processRow!.translateY! + pair.offsetY / 2;
544      pair.offsetY = pair.offsetY / 2;
545      pair.rowEL = processRow!;
546    }
547  }
548  /* Janks Frames */
549  //@ts-ignore
550  addExpectedRow(
551    process: unknown, //@ts-ignore
552    processRow: TraceRow<unknown>,
553    renderServiceProcess: Array<unknown>
554  ): TraceRow<JankStruct> {
555    let expectedRow = TraceRow.skeleton<JankStruct>(); //@ts-ignore
556    expectedRow.asyncFuncName = process.processName; //@ts-ignore
557    expectedRow.asyncFuncNamePID = process.pid;
558    expectedRow.rowType = TraceRow.ROW_TYPE_JANK; //@ts-ignore
559    expectedRow.rowParentId = `${process.pid}`;
560    expectedRow.rowHidden = !processRow.expansion;
561    expectedRow.style.width = '100%';
562    expectedRow.name = 'Expected Timeline';
563    expectedRow.addTemplateTypes('FrameTimeline');
564    expectedRow.setAttribute('children', '');
565    expectedRow.supplierFrame = async (): Promise<JankStruct[]> => {
566      //@ts-ignore
567      let res = await processExpectedDataSender(process.pid, expectedRow!);
568      this.jankSenderCallback(res, 'expected', process, expectedRow, renderServiceProcess);
569      return res;
570    };
571    expectedRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
572    expectedRow.selectChangeHandler = this.trace.selectChangeHandler;
573    expectedRow.onThreadHandler = rowThreadHandler<JankRender>(
574      'jank',
575      'context',
576      {
577        type: 'expected_frame_timeline_slice',
578      },
579      expectedRow,
580      this.trace
581    );
582    if (this.renderRow) {
583      processRow.addChildTraceRowBefore(expectedRow, this.renderRow);
584    } else {
585      processRow.addChildTraceRow(expectedRow);
586    }
587    return expectedRow;
588  }
589
590  //@ts-ignore
591  addActualRow(
592    process: unknown, //@ts-ignore
593    processRow: TraceRow<unknown>,
594    renderServiceProcess: Array<unknown>
595  ): TraceRow<JankStruct> {
596    let actualRow = TraceRow.skeleton<JankStruct>();
597    actualRow.rowType = TraceRow.ROW_TYPE_JANK; //@ts-ignore
598    actualRow.rowParentId = `${process.pid}`;
599    actualRow.rowHidden = !processRow.expansion;
600    actualRow.style.width = '100%';
601    actualRow.name = 'Actual Timeline';
602    actualRow.addTemplateTypes('FrameTimeline');
603    actualRow.setAttribute('children', '');
604    actualRow.supplierFrame = async (): Promise<JankStruct[]> => {
605      //@ts-ignore
606      let res = await processActualDataSender(process.pid, actualRow!);
607      this.jankSenderCallback(res, 'actual', process, actualRow, renderServiceProcess);
608      return res;
609    };
610    actualRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
611    actualRow.selectChangeHandler = this.trace.selectChangeHandler;
612    actualRow.onThreadHandler = rowThreadHandler<JankRender>(
613      'jank',
614      'context',
615      {
616        type: 'actual_frame_timeline_slice',
617      },
618      actualRow,
619      this.trace
620    );
621    if (this.renderRow) {
622      processRow.addChildTraceRowBefore(actualRow, this.renderRow);
623    } else {
624      processRow.addChildTraceRow(actualRow);
625    }
626    return actualRow;
627  }
628
629  jankSenderCallback(
630    res: JankStruct[],
631    type: string,
632    process: unknown,
633    row: TraceRow<JankStruct>,
634    renderServiceProcess: Array<unknown>
635  ): void {
636    let maxDepth: number = 1;
637    let unitHeight: number = 20;
638    for (let j = 0; j < res.length; j++) {
639      let struct = res[j];
640      if (struct.depth! >= maxDepth) {
641        maxDepth = struct.depth! + 1;
642      }
643      if (type === 'actual') {
644        struct.src_slice = this.processSrcSliceMap.get(res[j].id!);
645      }
646      struct.cmdline = Utils.PROCESS_MAP.get(res[j].pid!); //@ts-ignore
647      if (res[j].pid! === renderServiceProcess[0].pid) {
648        struct.cmdline = 'render_service';
649        struct.frame_type = struct.cmdline;
650      } else {
651        struct.frame_type = 'app';
652      }
653    }
654    if (row && !row.isComplete && res.length > 0) {
655      let maxHeight: number = maxDepth * unitHeight;
656      row.style.height = `${maxHeight}px`;
657      row.setAttribute('height', `${maxHeight}`);
658      if (res[0]) {
659        let timeLineType = res[0].type; //@ts-ignore
660        row.rowId = `${timeLineType}-${process.pid}`;
661        row.setAttribute('frame_type', res[0].frame_type || '');
662        if (type === 'actual') {
663          row.dataList = res;
664        }
665      }
666    }
667  }
668
669  addStartUpRow(processRow: TraceRow<ProcessStruct>): TraceRow<AppStartupStruct> {
670    processRow.setAttribute('hasStartup', 'true');
671    let startupRow: TraceRow<AppStartupStruct> = TraceRow.skeleton<AppStartupStruct>();
672    startupRow.rowId = `app-start-${processRow.rowId}`;
673    startupRow.rowType = TraceRow.ROW_TYPE_APP_STARTUP;
674    startupRow.rowParentId = `${processRow.rowId}`;
675    startupRow.rowHidden = !processRow.expansion;
676    startupRow.index = 0;
677    startupRow.style.height = '30px';
678    startupRow.style.width = '100%';
679    startupRow.name = 'App Startups';
680    startupRow.findHoverStruct = (): void => {
681      AppStartupStruct.hoverStartupStruct = startupRow.getHoverStruct();
682    };
683    startupRow.setAttribute('children', '');
684    startupRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
685    startupRow.selectChangeHandler = this.trace.selectChangeHandler;
686    startupRow.supplierFrame = (): Promise<Array<AppStartupStruct>> =>
687      processStartupDataSender(parseInt(processRow.rowId!), startupRow).then((res) => {
688        if (res.length <= 0) {
689          this.trace.refreshCanvas(true);
690        }
691        for (let i = 0; i < res.length; i++) {
692          if (res[i].startName! < 6 && i < res.length - 1) {
693            res[i].endItid = res[i + 1].itid;
694          }
695        }
696        return res;
697      });
698    startupRow.onThreadHandler = rowThreadHandler<AppStartupRender>(
699      'app-start-up',
700      'appStartupContext',
701      {
702        type: `app-startup ${processRow.rowId}`,
703      },
704      startupRow,
705      this.trace
706    );
707    processRow.addChildTraceRow(startupRow);
708    return startupRow;
709  }
710
711  addSoInitRow(processRow: TraceRow<ProcessStruct>, maxDepth: number): TraceRow<SoStruct> {
712    processRow.setAttribute('hasStaticInit', 'true');
713    let maxHeight = (maxDepth + 1) * 20;
714    let soRow: TraceRow<SoStruct> = TraceRow.skeleton<SoStruct>();
715    soRow.rowId = `app-start-${processRow.rowId}`;
716    soRow.rowType = TraceRow.ROW_TYPE_STATIC_INIT;
717    soRow.rowParentId = `${processRow.rowId}`;
718    soRow.rowHidden = !processRow.expansion;
719    soRow.index = 0;
720    soRow.style.height = `${maxHeight}px`;
721    soRow.style.width = '100%';
722    soRow.name = 'Static Initialization';
723    soRow.setAttribute('children', '');
724    soRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
725    soRow.selectChangeHandler = this.trace.selectChangeHandler;
726    soRow.findHoverStruct = (): void => {
727      SoStruct.hoverSoStruct = soRow.getHoverStruct();
728    };
729    soRow.supplierFrame = (): Promise<Array<SoStruct>> =>
730      processSoInitDataSender(parseInt(processRow.rowId!), soRow).then((res) => {
731        if (res.length <= 0) {
732          this.trace.refreshCanvas(true);
733        }
734        res.forEach((so, index) => {
735          let soName = this.soInitNameMap.get(res[index].id!);
736          if (soName) {
737            so.soName = soName.replace('dlopen: ', '');
738          }
739        });
740        return res;
741      });
742    soRow.onThreadHandler = rowThreadHandler<SoRender>(
743      'app-so-init',
744      'context',
745      {
746        type: `static-init ${processRow.rowId}`,
747      },
748      soRow,
749      this.trace
750    );
751    processRow.addChildTraceRow(soRow);
752    return soRow;
753  }
754
755  insertAfter(newEl: HTMLElement, targetEl: HTMLElement): void {
756    let parentEl = targetEl.parentNode;
757    if (parentEl!.lastChild === targetEl) {
758      parentEl!.appendChild(newEl);
759    } else {
760      parentEl!.insertBefore(newEl, targetEl.nextSibling);
761    }
762  }
763
764  //add thread list
765  addThreadList(
766    it: { pid: number | null; processName: string | null },
767    pRow: TraceRow<ProcessStruct>,
768    expectedRow: TraceRow<JankStruct> | null,
769    actualRow: TraceRow<JankStruct> | null,
770    soRow: TraceRow<SoStruct> | undefined,
771    startupRow: TraceRow<AppStartupStruct> | undefined
772  ): void {
773    let threads = this.processThreads.filter((thread) => thread.pid === it.pid && thread.tid !== 0);
774    let tRowArr: Array<TraceRow<BaseStruct>> = [];
775    for (let j = 0; j < threads.length; j++) {
776      let thread = threads[j];
777      let tRow = TraceRow.skeleton<ThreadStruct>();
778      tRow.rowId = `${thread.tid}`;
779      tRow.rowType = TraceRow.ROW_TYPE_THREAD;
780      tRow.rowParentId = `${it.pid}`;
781      tRow.rowHidden = !pRow.expansion;
782      tRow.index = j;
783      tRow.style.height = '18px';
784      tRow.style.width = '100%';
785      tRow.name = `${thread.threadName || 'Thread'} ${thread.tid}`;
786      tRow.namePrefix = `${thread.threadName || 'Thread'}`;
787      tRow.setAttribute('children', '');
788      tRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
789      tRow.selectChangeHandler = this.trace.selectChangeHandler;
790      tRow.findHoverStruct = (): void => this.threadRowFindHoverStruct(tRow);
791      tRow.supplierFrame = async (): Promise<Array<ThreadStruct>> => {
792        const res = await threadDataSender(thread.tid || 0, it.pid || 0, tRow);
793        if (res === true) {
794          return [];
795        } else {
796          let rs = res as ThreadStruct[];
797          if (rs.length <= 0 && !tRow.isComplete) {
798            this.trace.refreshCanvas(true);
799          }
800          return rs;
801        }
802      };
803      tRow.onThreadHandler = rowThreadHandler<ThreadRender>(
804        'thread',
805        'context',
806        {
807          type: `thread ${thread.tid} ${thread.threadName}`,
808          translateY: tRow.translateY,
809        },
810        tRow,
811        this.trace
812      );
813      this.insertRowToDoc(it, j, thread, pRow, tRow, threads, tRowArr, actualRow, expectedRow, startupRow, soRow);
814      this.addFuncStackRow(it, thread, j, threads, tRowArr, tRow, pRow);
815      if ((thread.switchCount || 0) === 0) {
816        tRow.rowDiscard = true;
817      }
818    }
819  }
820
821  threadRowFindHoverStruct(threadRow: TraceRow<ThreadStruct>): void {
822    let arr = threadRow.dataListCache.filter(
823      (re) => re.frame && isFrameContainPoint(re.frame, threadRow.hoverX, threadRow.hoverY, true, false)
824    );
825    let runItem = arr.find((it) => it.state === 'Running');
826    if (runItem) {
827      ThreadStruct.hoverThreadStruct = runItem;
828    } else {
829      let otherItem = arr.find((it) => it.state !== 'S');
830      if (otherItem) {
831        ThreadStruct.hoverThreadStruct = otherItem;
832      } else {
833        ThreadStruct.hoverThreadStruct = arr[0];
834      }
835    }
836  }
837
838  insertRowToDoc(
839    it: unknown,
840    index: number,
841    thread: ThreadStruct,
842    processRow: TraceRow<ProcessStruct>,
843    threadRow: TraceRow<ThreadStruct>,
844    threads: ThreadStruct[], //@ts-ignore
845    threadRowArr: TraceRow<unknown>[], //@ts-ignore
846    actualRow: TraceRow<unknown> | null, //@ts-ignore
847    expectedRow: TraceRow<unknown> | null,
848    startupRow: TraceRow<AppStartupStruct> | null | undefined,
849    soRow: TraceRow<SoStruct> | null | undefined
850  ): void {
851    //@ts-ignore
852    if (it.processName === 'render_service') {
853      //@ts-ignore
854      if (threadRow.name === `${it.processName} ${it.pid}`) {
855        this.renderRow = threadRow;
856      }
857      let flag = threads.length === index + 1 && !this.threadFuncMaxDepthMap.has(`${thread.upid}-${thread.tid}`); //@ts-ignore
858      processRow.sortRenderServiceData(threadRow, threadRow, threadRowArr, flag);
859    } else {
860      if (threadRow.rowId === threadRow.rowParentId) {
861        if (actualRow !== null) {
862          processRow.addChildTraceRowAfter(threadRow, actualRow);
863        } else if (expectedRow !== null) {
864          processRow.addChildTraceRowAfter(threadRow, expectedRow);
865        } else if (soRow) {
866          processRow.addChildTraceRowAfter(threadRow, soRow);
867        } else if (startupRow) {
868          processRow.addChildTraceRowAfter(threadRow, startupRow);
869        } else {
870          processRow.addChildTraceRowSpecifyLocation(threadRow, 0);
871        }
872      } else {
873        processRow.addChildTraceRow(threadRow);
874      }
875    }
876  }
877
878  addFuncStackRow(
879    process: unknown,
880    thread: unknown,
881    index: number,
882    threads: Array<unknown>,
883    threadRowArr: Array<unknown>,
884    threadRow: TraceRow<ThreadStruct>,
885    processRow: TraceRow<ProcessStruct>
886  ): void {
887    //@ts-ignore
888    if (this.threadFuncMaxDepthMap.get(`${thread.upid}-${thread.tid}`) !== undefined) {
889      //@ts-ignore
890      let max = this.threadFuncMaxDepthMap.get(`${thread.upid}-${thread.tid}`) || 1;
891      let maxHeight = max * 18 + 6;
892      let funcRow = TraceRow.skeleton<FuncStruct>(); //@ts-ignore
893      funcRow.rowId = `${thread.tid}`;
894      funcRow.rowType = TraceRow.ROW_TYPE_FUNC;
895      funcRow.enableCollapseChart(); //允许折叠泳道图
896      //@ts-ignore
897      funcRow.rowParentId = `${process.pid}`;
898      funcRow.rowHidden = !processRow.expansion;
899      funcRow.checkType = threadRow.checkType;
900      funcRow.style.width = '100%';
901      funcRow.style.height = `${maxHeight}px`; //@ts-ignore
902      funcRow.name = `${thread.threadName || 'Thread'} ${thread.tid}`; //@ts-ignore
903      funcRow.namePrefix = `${thread.threadName || 'Thread'}`;
904      funcRow.setAttribute('children', '');
905      funcRow.supplierFrame = async (): Promise<Array<FuncStruct>> => {
906        //@ts-ignore
907        const rs = await funcDataSender(thread.tid || 0, thread.upid || 0, funcRow); //@ts-ignore
908        return this.funDataSenderCallback(rs, funcRow, thread);
909      };
910      funcRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
911      funcRow.selectChangeHandler = this.trace.selectChangeHandler;
912      funcRow.findHoverStruct = (): void => {
913        FuncStruct.hoverFuncStruct = funcRow.getHoverStruct();
914      };
915      funcRow.onThreadHandler = rowThreadHandler<FuncRender>(
916        'func',
917        'context',
918        {
919          //@ts-ignore
920          type: `func${thread.tid}${thread.threadName}`,
921        },
922        funcRow,
923        this.trace
924      ); //@ts-ignore
925      if (process.processName === 'render_service') {
926        let flag = threads.length === index + 1; //@ts-ignore
927        processRow.sortRenderServiceData(funcRow, threadRow, threadRowArr, flag);
928      } else {
929        processRow.addChildTraceRowAfter(funcRow, threadRow);
930      }
931    }
932  }
933
934  funDataSenderCallback(
935    rs: Array<unknown> | boolean,
936    funcRow: TraceRow<FuncStruct>,
937    thread: ThreadStruct
938  ): FuncStruct[] {
939    if (rs === true) {
940      funcRow.rowDiscard = true;
941      return [];
942    } else {
943      let funs = rs as FuncStruct[];
944      if (funs.length > 0) {
945        funs.forEach((fun, index) => {
946          funs[index].itid = thread.utid;
947          funs[index].ipid = thread.upid;
948          funs[index].funName = this.funcNameMap.get(funs[index].id!);
949          if (Utils.isBinder(fun)) {
950          } else {
951            if (fun.nofinish) {
952              fun.flag = 'Did not end';
953            }
954          }
955        });
956      } else {
957        this.trace.refreshCanvas(true);
958      }
959      return funs;
960    }
961  }
962
963  //进程内存信息
964  addProcessMemInfo(it: { pid: number | null; processName: string | null }, processRow: TraceRow<ProcessStruct>): void {
965    //@ts-ignore
966    let processMem = this.processMem.filter((mem) => mem.pid === it.pid);
967    processMem.forEach((mem) => {
968      let row = TraceRow.skeleton<ProcessMemStruct>(); //@ts-ignore
969      row.rowId = `${mem.trackId}`;
970      row.rowType = TraceRow.ROW_TYPE_MEM;
971      row.rowParentId = `${it.pid}`;
972      row.rowHidden = !processRow.expansion;
973      row.style.height = '40px';
974      row.style.width = '100%'; //@ts-ignore
975      row.name = `${mem.trackName}`;
976      row.setAttribute('children', '');
977      row.favoriteChangeHandler = this.trace.favoriteChangeHandler;
978      row.selectChangeHandler = this.trace.selectChangeHandler;
979      row.focusHandler = (): void => {
980        this.trace.displayTip(
981          row,
982          ProcessMemStruct.hoverProcessMemStruct,
983          `<span>${ProcessMemStruct.hoverProcessMemStruct?.value || '0'}</span>`
984        );
985      };
986      row.findHoverStruct = (): void => {
987        ProcessMemStruct.hoverProcessMemStruct = row.getHoverStruct(false);
988      };
989      row.supplierFrame = (): Promise<Array<ProcessMemStruct>> => //@ts-ignore
990        processMemDataSender(mem.trackId, row).then((resultProcess) => {
991          //@ts-ignore
992          let maxValue = this.filterIdMaxValue.get(mem.trackId) || 0;
993          for (let j = 0; j < resultProcess.length; j++) {
994            resultProcess[j].maxValue = maxValue;
995            if (j === resultProcess.length - 1) {
996              resultProcess[j].duration = (TraceRow.range?.totalNS || 0) - (resultProcess[j].startTime || 0);
997            } else {
998              resultProcess[j].duration = (resultProcess[j + 1].startTime || 0) - (resultProcess[j].startTime || 0);
999            }
1000            if (j > 0) {
1001              resultProcess[j].delta = (resultProcess[j].value || 0) - (resultProcess[j - 1].value || 0);
1002            } else {
1003              resultProcess[j].delta = 0;
1004            }
1005          }
1006          return resultProcess;
1007        });
1008      row.onThreadHandler = rowThreadHandler<MemRender>(
1009        'mem',
1010        'context',
1011        {
1012          //@ts-ignore
1013          type: `mem ${mem.trackId} ${mem.trackName}`,
1014        },
1015        row,
1016        this.trace
1017      );
1018      if (this.renderRow && row.name === 'H:PreferredFrameRate') {
1019        processRow.addChildTraceRowBefore(row, this.renderRow);
1020      } else {
1021        processRow.addChildTraceRow(row);
1022      }
1023    });
1024  }
1025  private calMaxHeight(asyncFunctions: unknown[]): number {
1026    let max = 0;
1027    asyncFunctions.forEach((it) => {
1028      //@ts-ignore
1029      const depth = it.depth || 0;
1030      if (depth > max) {
1031        max = depth;
1032      }
1033    });
1034    max += 1;
1035    return max * 18 + 6;
1036  }
1037
1038  //Async Function
1039  addAsyncFunction(it: { pid: number; processName: string | null }, processRow: TraceRow<ProcessStruct>): void {
1040    //@ts-ignore
1041    let asyncFuncList = this.processAsyncFuncMap[it.pid] || [];
1042    let asyncFuncGroup = Utils.groupBy(asyncFuncList, 'funName'); // @ts-ignore
1043    Reflect.ownKeys(asyncFuncGroup).map((key: unknown) => {
1044      // @ts-ignore
1045      let asyncFunctions: Array<unknown> = asyncFuncGroup[key];
1046      if (asyncFunctions.length > 0) {
1047        let isIntersect = (a: unknown, b: unknown): boolean =>
1048          //@ts-ignore
1049          Math.max(a.startTs + a.dur, b.startTs + b.dur) - Math.min(a.startTs, b.startTs) < a.dur + b.dur;
1050        let depthArray: unknown = [];
1051        asyncFunctions.forEach((it, i) => {
1052          //@ts-ignore
1053          if (it.dur === -1 || it.dur === null || it.dur === undefined) {
1054            //@ts-ignore
1055            it.dur = (TraceRow.range?.endNS || 0) - it.startTs; //@ts-ignore
1056            it.flag = 'Did not end';
1057          }
1058          let currentDepth = 0;
1059          let index = i;
1060          while (
1061            //@ts-ignore
1062            depthArray[currentDepth] !== undefined && //@ts-ignore
1063            isIntersect(depthArray[currentDepth], asyncFunctions[index])
1064          ) {
1065            currentDepth++;
1066          } //@ts-ignore
1067          asyncFunctions[index].depth = currentDepth; //@ts-ignore
1068          depthArray[currentDepth] = asyncFunctions[index];
1069        });
1070        const maxHeight = this.calMaxHeight(asyncFunctions);
1071        let funcRow = TraceRow.skeleton<FuncStruct>(); //@ts-ignore
1072        funcRow.rowId = `${asyncFunctions[0].funName}-${it.pid}`; //@ts-ignore
1073        funcRow.asyncFuncName = asyncFunctions[0].funName;
1074        funcRow.asyncFuncNamePID = it.pid;
1075        funcRow.rowType = TraceRow.ROW_TYPE_FUNC;
1076        funcRow.enableCollapseChart(); //允许折叠泳道图
1077        funcRow.rowParentId = `${it.pid}`;
1078        funcRow.rowHidden = !processRow.expansion;
1079        funcRow.style.width = '100%';
1080        funcRow.style.height = `${maxHeight}px`;
1081        funcRow.setAttribute('height', `${maxHeight}`); //@ts-ignore
1082        funcRow.name = `${asyncFunctions[0].funName}`;
1083        funcRow.setAttribute('children', '');
1084        funcRow.findHoverStruct = (): void => {
1085          FuncStruct.hoverFuncStruct = funcRow.getHoverStruct();
1086        }; //@ts-ignore
1087        funcRow.supplier = (): Promise<unknown> => new Promise((resolve) => resolve(asyncFunctions));
1088        funcRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
1089        funcRow.selectChangeHandler = this.trace.selectChangeHandler;
1090        funcRow.onThreadHandler = rowThreadHandler<FuncRender>(
1091          'func',
1092          'context',
1093          {
1094            //@ts-ignore
1095            type: `func-${asyncFunctions[0].funName}-${it.pid}`,
1096          },
1097          funcRow,
1098          this.trace
1099        );
1100        processRow.addChildTraceRow(funcRow);
1101      }
1102    });
1103  }
1104}
1105