• 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.js';
17import {
18  getAsyncEvents,
19  getFunDataByTid,
20  getMaxDepthByTid,
21  queryAllActualData,
22  queryAllExpectedData,
23  queryAllJankProcess,
24  queryEventCountMap,
25  queryProcess,
26  queryProcessAsyncFunc,
27  queryProcessByTable,
28  queryProcessContentCount,
29  queryProcessData,
30  queryProcessMem,
31  queryProcessMemData,
32  queryProcessSoInitData,
33  queryProcessSoMaxDepth,
34  queryProcessStartup,
35  queryProcessThreads,
36  queryProcessThreadsByTable,
37  queryStartupPidArray,
38  queryThreadData,
39} from '../../database/SqlLite.js';
40import { Utils } from '../trace/base/Utils.js';
41import { info } from '../../../log/Log.js';
42import { TraceRow } from '../trace/base/TraceRow.js';
43import { renders } from '../../database/ui-worker/ProcedureWorker.js';
44import { ProcessRender, ProcessStruct } from '../../database/ui-worker/ProcedureWorkerProcess.js';
45import { ThreadRender, ThreadStruct } from '../../database/ui-worker/ProcedureWorkerThread.js';
46import { FuncRender, FuncStruct } from '../../database/ui-worker/ProcedureWorkerFunc.js';
47import { MemRender, ProcessMemStruct } from '../../database/ui-worker/ProcedureWorkerMem.js';
48import { FolderSupplier, FolderThreadHandler } from './SpChartManager.js';
49import { JankRender, JankStruct } from '../../database/ui-worker/ProcedureWorkerJank.js';
50import { ns2xByTimeShaft } from '../../database/ui-worker/ProcedureWorkerCommon.js';
51import { AppStartupRender, AppStartupStruct } from '../../database/ui-worker/ProcedureWorkerAppStartup.js';
52import { SoRender, SoStruct } from '../../database/ui-worker/ProcedureWorkerSoInit.js';
53
54export class SpProcessChart {
55  private readonly trace: SpSystemTrace;
56  private processAsyncFuncMap: any = {};
57  private processAsyncFuncArray: any[] = [];
58  private eventCountMap: any;
59  private processThreads: Array<ThreadStruct> = [];
60  private processAsyncEvent: Array<ProcessMemStruct> = [];
61  private processMem: Array<any> = [];
62  private processThreadCountMap: Map<number, number> = new Map();
63  private processThreadDataCountMap: Map<number, number> = new Map();
64  private processFuncDataCountMap: Map<number, number> = new Map();
65  private processMemDataCountMap: Map<number, number> = new Map();
66  private threadFuncMaxDepthMap: Map<string, number> = new Map();
67  private startupProcessArr: { pid: number }[] = [];
68  private processSoMaxDepth: { pid: number; maxDepth: number }[] = [];
69
70  constructor(trace: SpSystemTrace) {
71    this.trace = trace;
72  }
73
74  initAsyncFuncData = async () => {
75    let asyncFuncList: any[] = await queryProcessAsyncFunc();
76    info('AsyncFuncData Count is: ', asyncFuncList!.length);
77    this.processAsyncFuncArray = asyncFuncList;
78    this.processAsyncFuncMap = Utils.groupBy(asyncFuncList, 'pid');
79  };
80
81  initDeliverInputEvent = async () => {
82    let row = TraceRow.skeleton();
83    row.setAttribute('disabled-check', '');
84    row.rowId = 'DeliverInputEvent';
85    row.index = 0;
86    row.rowType = TraceRow.ROW_TYPE_DELIVER_INPUT_EVENT;
87    row.rowParentId = '';
88    row.folder = true;
89    row.style.height = '40px';
90    row.name = 'DeliverInputEvent';
91    row.supplier = FolderSupplier();
92    row.onThreadHandler = FolderThreadHandler(row, this.trace);
93
94    let asyncFuncGroup = Utils.groupBy(
95      this.processAsyncFuncArray.filter((it) => it.funName === 'deliverInputEvent'),
96      'tid'
97    );
98    if (Reflect.ownKeys(asyncFuncGroup).length > 0) {
99      this.trace.rowsEL?.appendChild(row);
100    }
101    Reflect.ownKeys(asyncFuncGroup).map((key: any) => {
102      let asyncFuncGroups: Array<any> = asyncFuncGroup[key];
103      if (asyncFuncGroups.length > 0) {
104        let isIntersect = (left: any, right: any) =>
105          Math.max(left.startTs + left.dur, right.startTs + right.dur) - Math.min(left.startTs, right.startTs) <
106          left.dur + right.dur;
107        let depths: any = [];
108        let createDepth = (currentDepth: number, index: number) => {
109          if (depths[currentDepth] == undefined || !isIntersect(depths[currentDepth], asyncFuncGroups[index])) {
110            asyncFuncGroups[index].depth = currentDepth;
111            depths[currentDepth] = asyncFuncGroups[index];
112          } else {
113            createDepth(++currentDepth, index);
114          }
115        };
116        asyncFuncGroups.forEach((it, i) => {
117          if (it.dur == -1) {
118            it.dur = (TraceRow.range?.endNS || 0) - it.startTs;
119            it.flag = 'Did not end';
120          }
121          createDepth(0, i);
122        });
123        let max = Math.max(...asyncFuncGroups.map((it) => it.depth || 0)) + 1;
124        let maxHeight = max * 20;
125        let funcRow = TraceRow.skeleton<FuncStruct>();
126        funcRow.rowId = `${asyncFuncGroups[0].funName}-${key}`;
127        funcRow.asyncFuncName = asyncFuncGroups[0].funName;
128        funcRow.asyncFuncNamePID = key;
129        funcRow.rowType = TraceRow.ROW_TYPE_FUNC;
130        funcRow.rowParentId = `${row.rowId}`;
131        funcRow.rowHidden = !row.expansion;
132        funcRow.style.width = '100%';
133        funcRow.style.height = `${maxHeight}px`;
134        funcRow.setAttribute('height', `${maxHeight}`);
135        funcRow.name = `${asyncFuncGroups[0].funName} ${key}`;
136        funcRow.setAttribute('children', '');
137        funcRow.supplier = () => new Promise((resolve) => resolve(asyncFuncGroups));
138        funcRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
139        funcRow.selectChangeHandler = this.trace.selectChangeHandler;
140        funcRow.onThreadHandler = (useCache) => {
141          let context = funcRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
142          funcRow.canvasSave(context);
143          (renders['func'] as FuncRender).renderMainThread(
144            {
145              context: context,
146              useCache: useCache,
147              type: `func-${asyncFuncGroups[0].funName}-${key}`,
148            },
149            funcRow
150          );
151          funcRow.canvasRestore(context);
152        };
153        row.addChildTraceRow(funcRow);
154      }
155    });
156  };
157
158  async init() {
159    let threadFuncMaxDepthArray = await getMaxDepthByTid();
160    info('Gets the maximum tier per thread , tid and maxDepth');
161    threadFuncMaxDepthArray.forEach((it) => {
162      this.threadFuncMaxDepthMap.set(`${it.ipid}-${it.tid}`, it.maxDepth);
163    });
164    info('convert tid and maxDepth array to map');
165    let pidCountArray = await queryProcessContentCount();
166    info('fetch per process  pid,switch_count,thread_count,slice_count,mem_count');
167    pidCountArray.forEach((it) => {
168      this.processThreadDataCountMap.set(it.pid, it.switch_count);
169      this.processThreadCountMap.set(it.pid, it.thread_count);
170      this.processFuncDataCountMap.set(it.pid, it.slice_count);
171      this.processMemDataCountMap.set(it.pid, it.mem_count);
172    });
173    let queryProcessThreadResult = await queryProcessThreads();
174    let queryProcessThreadsByTableResult = await queryProcessThreadsByTable();
175    this.processAsyncEvent = await getAsyncEvents();
176    info('The amount of initialized process Event data is : ', this.processAsyncEvent!.length);
177    this.processMem = await queryProcessMem();
178    this.startupProcessArr = await queryStartupPidArray();
179    this.processSoMaxDepth = await queryProcessSoMaxDepth();
180    info('The amount of initialized process memory data is : ', this.processMem!.length);
181    let eventCountList: Array<any> = await queryEventCountMap();
182    this.eventCountMap = eventCountList.reduce((pre, current) => {
183      pre[`${current.eventName}`] = current.count;
184      return pre;
185    }, {});
186    this.processThreads = Utils.removeDuplicates(queryProcessThreadResult, queryProcessThreadsByTableResult, 'tid');
187    info('The amount of initialized process threads data is : ', this.processThreads!.length);
188    if (
189      this.eventCountMap['print'] == 0 &&
190      this.eventCountMap['tracing_mark_write'] == 0 &&
191      this.eventCountMap['sched_switch'] == 0
192    ) {
193      return;
194    }
195    let time = new Date().getTime();
196    let processes = await queryProcess();
197    let processFromTable = await queryProcessByTable();
198    let processList = Utils.removeDuplicates(processes, processFromTable, 'pid');
199    let allJankProcessData = await queryAllJankProcess();
200    let allJankProcess: Array<number> = [];
201    let allExpectedProcess: Array<any> = [];
202    let allActualProcess: Array<any> = [];
203    if (allJankProcessData.length > 0) {
204      allJankProcessData.forEach((name, index) => {
205        allJankProcess.push(name.pid!);
206      });
207      allExpectedProcess = await queryAllExpectedData();
208      allActualProcess = await queryAllActualData();
209    }
210    info('ProcessList Data size is: ', processList!.length);
211    for (let i = 0; i < processList.length; i++) {
212      const it = processList[i];
213      if (
214        (this.processThreadDataCountMap.get(it.pid) || 0) == 0 &&
215        (this.processThreadCountMap.get(it.pid) || 0) == 0 &&
216        (this.processFuncDataCountMap.get(it.pid) || 0) == 0 &&
217        (this.processMemDataCountMap.get(it.pid) || 0) == 0
218      ) {
219        continue;
220      }
221      let processRow = TraceRow.skeleton<ProcessStruct>();
222      processRow.rowId = `${it.pid}`;
223      processRow.index = i;
224      processRow.rowType = TraceRow.ROW_TYPE_PROCESS;
225      processRow.rowParentId = '';
226      processRow.style.height = '40px';
227      processRow.folder = true;
228      processRow.name = `${it.processName || 'Process'} ${it.pid}`;
229      processRow.supplier = () => queryProcessData(it.pid || -1, 0, TraceRow.range?.totalNS || 0);
230      processRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
231      processRow.selectChangeHandler = this.trace.selectChangeHandler;
232      processRow.onThreadHandler = (useCache) => {
233        processRow.canvasSave(this.trace.canvasPanelCtx!);
234        if (processRow.expansion) {
235          this.trace.canvasPanelCtx?.clearRect(0, 0, processRow.frame.width, processRow.frame.height);
236        } else {
237          (renders['process'] as ProcessRender).renderMainThread(
238            {
239              context: this.trace.canvasPanelCtx,
240              pid: it.pid,
241              useCache: useCache,
242              type: `process ${processRow.index} ${it.processName}`,
243            },
244            processRow
245          );
246        }
247        processRow.canvasRestore(this.trace.canvasPanelCtx!);
248      };
249      this.trace.rowsEL?.appendChild(processRow);
250      let startupRow: TraceRow<AppStartupStruct> | undefined = undefined;
251      let soRow: TraceRow<SoStruct> | undefined = undefined;
252      if (this.startupProcessArr.find((sp) => sp.pid === it.pid)) {
253        startupRow = this.addStartUpRow(processRow);
254      }
255      let maxSoDepth = this.processSoMaxDepth.find((md) => md.pid === it.pid);
256      if (maxSoDepth) {
257        soRow = this.addSoInitRow(processRow, maxSoDepth.maxDepth);
258      }
259      /**
260       * Janks Frames
261       */
262      let actualRow: TraceRow<JankStruct> | null = null;
263      let expectedRow: TraceRow<JankStruct> | null = null;
264      if (allJankProcess.indexOf(it.pid) > -1 && allExpectedProcess.length > 0) {
265        let expectedData = allExpectedProcess.filter((ite) => ite.pid == it.pid);
266        if (expectedData.length > 0) {
267          // @ts-ignore
268          let isIntersect = (a: JanksStruct, b: JanksStruct) =>
269            Math.max(a.ts + a.dur, b.ts + b.dur) - Math.min(a.ts, b.ts) < a.dur + b.dur;
270          let depthArray: any = [];
271          for (let j = 0; j < expectedData.length; j++) {
272            let expectedItem = expectedData[j];
273            if (expectedItem.cmdline != 'render_service') {
274              expectedItem.frame_type = 'app';
275            } else {
276              expectedItem.frame_type = expectedItem.cmdline;
277            }
278            if (!expectedItem.dur || expectedItem.dur < 0) {
279              continue;
280            }
281            if (depthArray.length === 0) {
282              expectedItem.depth = 0;
283              depthArray.push(expectedItem);
284            } else {
285              if (isIntersect(depthArray[0], expectedItem)) {
286                if (isIntersect(depthArray[depthArray.length - 1], expectedItem)) {
287                  expectedItem.depth = depthArray.length;
288                  depthArray.push(expectedItem);
289                }
290              } else {
291                expectedItem.depth = 0;
292                depthArray = [expectedItem];
293              }
294            }
295          }
296          let max = Math.max(...expectedData.map((it) => it.depth || 0)) + 1;
297          let maxHeight = max * 20;
298          expectedRow = TraceRow.skeleton<JankStruct>();
299          let timeLineType = expectedData[0].type;
300          expectedRow.rowId = `${timeLineType}-${it.pid}`;
301          expectedRow.asyncFuncName = it.processName;
302          expectedRow.asyncFuncNamePID = it.pid;
303          expectedRow.rowType = TraceRow.ROW_TYPE_JANK;
304          expectedRow.rowParentId = `${it.pid}`;
305          expectedRow.rowHidden = !processRow.expansion;
306          expectedRow.style.width = '100%';
307          expectedRow.style.height = `${maxHeight}px`;
308          expectedRow.setAttribute('height', `${maxHeight}`);
309          expectedRow.setAttribute('frame_type', expectedData[0].frame_type);
310          expectedRow.name = 'Expected Timeline';
311          expectedRow.addTemplateTypes('FrameTimeline');
312          expectedRow.setAttribute('children', '');
313          expectedRow.supplier = () =>
314            new Promise((resolve) => {
315              resolve(expectedData);
316            });
317          expectedRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
318          expectedRow.selectChangeHandler = this.trace.selectChangeHandler;
319          expectedRow.onThreadHandler = (useCache) => {
320            let context = expectedRow!.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
321            expectedRow!.canvasSave(context);
322            (renders['jank'] as JankRender).renderMainThread(
323              {
324                context: context,
325                useCache: useCache,
326                type: 'expected_frame_timeline_slice',
327              },
328              expectedRow!
329            );
330            expectedRow!.canvasRestore(context);
331          };
332          processRow.addChildTraceRow(expectedRow);
333          let actualData = allActualProcess.filter((ite) => ite.pid == it.pid);
334          if (actualData.length > 0) {
335            let isIntersect = (a: any, b: any) =>
336              Math.max(a.ts + a.dur, b.ts + b.dur) - Math.min(a.ts, b.ts) < a.dur + b.dur;
337            let depthArray: any = [];
338            for (let j = 0; j < actualData.length; j++) {
339              let actualItem = actualData[j];
340              if (actualItem.cmdline != 'render_service') {
341                actualItem.frame_type = 'app';
342              } else {
343                actualItem.frame_type = actualItem.cmdline;
344              }
345              if (!actualItem.dur || actualItem.dur < 0) {
346                continue;
347              }
348              if (depthArray.length === 0) {
349                actualItem.depth = 0;
350                depthArray.push(actualItem);
351              } else {
352                if (isIntersect(depthArray[0], actualItem)) {
353                  if (isIntersect(depthArray[depthArray.length - 1], actualItem)) {
354                    actualItem.depth = depthArray.length;
355                    depthArray.push(actualItem);
356                  } else {
357                    actualItem.depth = depthArray.length - 1;
358                    depthArray[length - 1] = actualItem;
359                  }
360                } else {
361                  actualItem.depth = 0;
362                  depthArray = [actualItem];
363                }
364              }
365            }
366            let max = Math.max(...actualData.map((it) => it.depth || 0)) + 1;
367            let maxHeight = max * 20;
368            actualRow = TraceRow.skeleton<JankStruct>();
369            let timeLineType = actualData[0].type;
370            actualRow.rowId = `${timeLineType}-${it.pid}`;
371            actualRow.rowType = TraceRow.ROW_TYPE_JANK;
372            actualRow.rowParentId = `${it.pid}`;
373            actualRow.rowHidden = !processRow.expansion;
374            actualRow.style.width = '100%';
375            actualRow.style.height = `${maxHeight}px`;
376            actualRow.setAttribute('height', `${maxHeight}`);
377            actualRow.name = 'Actual Timeline';
378            actualRow.addTemplateTypes('FrameTimeline');
379            actualRow.setAttribute('frame_type', actualData[0].frame_type);
380            actualRow.setAttribute('children', '');
381            actualRow.dataList = actualData;
382            actualRow.supplier = () => new Promise((resolve) => resolve(actualData));
383            actualRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
384            actualRow.selectChangeHandler = this.trace.selectChangeHandler;
385            actualRow.onThreadHandler = (useCache) => {
386              let context = actualRow!.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
387              actualRow!.canvasSave(context);
388              (renders['jank'] as JankRender).renderMainThread(
389                {
390                  context: context,
391                  useCache: useCache,
392                  type: 'actual_frame_timeline_slice',
393                },
394                actualRow!
395              );
396              actualRow!.canvasRestore(context);
397            };
398            processRow.addChildTraceRow(actualRow);
399          }
400        }
401      }
402      let offsetYTimeOut: any = undefined;
403      processRow.addEventListener('expansion-change', (e: any) => {
404        JankStruct.delJankLineFlag = false;
405        if (offsetYTimeOut) {
406          clearTimeout(offsetYTimeOut);
407        }
408        if (e.detail.expansion) {
409          offsetYTimeOut = setTimeout(() => {
410            this.trace.linkNodes.forEach((linkNodeItem) => {
411              JankStruct.selectJankStructList?.forEach((selectProcessStruct: any) => {
412                if (e.detail.rowId == selectProcessStruct.pid) {
413                  JankStruct.selectJankStruct = selectProcessStruct;
414                  JankStruct.hoverJankStruct = selectProcessStruct;
415                }
416              });
417              if (linkNodeItem[0].rowEL.collect) {
418                linkNodeItem[0].rowEL.translateY = linkNodeItem[0].rowEL.getBoundingClientRect().top - 195;
419              } else {
420                linkNodeItem[0].rowEL.translateY = linkNodeItem[0].rowEL.offsetTop - this.trace.rowsPaneEL!.scrollTop;
421              }
422              linkNodeItem[0].y = linkNodeItem[0].rowEL!.translateY! + linkNodeItem[0].offsetY;
423              if (linkNodeItem[1].rowEL.collect) {
424                linkNodeItem[1].rowEL.translateY = linkNodeItem[1].rowEL.getBoundingClientRect().top - 195;
425              } else {
426                linkNodeItem[1].rowEL.translateY = linkNodeItem[1].rowEL.offsetTop - this.trace.rowsPaneEL!.scrollTop;
427              }
428              linkNodeItem[1].y = linkNodeItem[1].rowEL!.translateY! + linkNodeItem[1].offsetY;
429              if (linkNodeItem[0].rowEL.rowId == e.detail.rowId) {
430                linkNodeItem[0].x = ns2xByTimeShaft(linkNodeItem[0].ns, this.trace.timerShaftEL!);
431                linkNodeItem[0].y = actualRow!.translateY! + linkNodeItem[0].offsetY * 2;
432                linkNodeItem[0].offsetY = linkNodeItem[0].offsetY * 2;
433                linkNodeItem[0].rowEL = actualRow!;
434              } else if (linkNodeItem[1].rowEL.rowId == e.detail.rowId) {
435                linkNodeItem[1].x = ns2xByTimeShaft(linkNodeItem[1].ns, this.trace.timerShaftEL!);
436                linkNodeItem[1].y = actualRow!.translateY! + linkNodeItem[1].offsetY * 2;
437                linkNodeItem[1].offsetY = linkNodeItem[1].offsetY * 2;
438                linkNodeItem[1].rowEL = actualRow!;
439              }
440            });
441          }, 300);
442        } else {
443          if (JankStruct!.selectJankStruct) {
444            JankStruct.selectJankStructList?.push(<JankStruct>JankStruct!.selectJankStruct);
445          }
446          offsetYTimeOut = setTimeout(() => {
447            this.trace.linkNodes?.forEach((linkProcessItem) => {
448              if (linkProcessItem[0].rowEL.collect) {
449                linkProcessItem[0].rowEL.translateY = linkProcessItem[0].rowEL.getBoundingClientRect().top - 195;
450              } else {
451                linkProcessItem[0].rowEL.translateY =
452                  linkProcessItem[0].rowEL.offsetTop - this.trace.rowsPaneEL!.scrollTop;
453              }
454              linkProcessItem[0].y = linkProcessItem[0].rowEL!.translateY! + linkProcessItem[0].offsetY;
455              if (linkProcessItem[1].rowEL.collect) {
456                linkProcessItem[1].rowEL.translateY = linkProcessItem[1].rowEL.getBoundingClientRect().top - 195;
457              } else {
458                linkProcessItem[1].rowEL.translateY =
459                  linkProcessItem[1].rowEL.offsetTop - this.trace.rowsPaneEL!.scrollTop;
460              }
461              linkProcessItem[1].y = linkProcessItem[1].rowEL!.translateY! + linkProcessItem[1].offsetY;
462              if (linkProcessItem[0].rowEL.rowParentId == e.detail.rowId) {
463                linkProcessItem[0].x = ns2xByTimeShaft(linkProcessItem[0].ns, this.trace.timerShaftEL!);
464                linkProcessItem[0].y = processRow!.translateY! + linkProcessItem[0].offsetY / 2;
465                linkProcessItem[0].offsetY = linkProcessItem[0].offsetY / 2;
466                linkProcessItem[0].rowEL = processRow!;
467              } else if (linkProcessItem[1].rowEL.rowParentId == e.detail.rowId) {
468                linkProcessItem[1].x = ns2xByTimeShaft(linkProcessItem[1].ns, this.trace.timerShaftEL!);
469                linkProcessItem[1].y = processRow!.translateY! + linkProcessItem[1].offsetY / 2;
470                linkProcessItem[1].offsetY = linkProcessItem[1].offsetY / 2;
471                linkProcessItem[1].rowEL = processRow!;
472              }
473            });
474          }, 300);
475        }
476        let refreshTimeOut = setTimeout(() => {
477          this.trace.refreshCanvas(true);
478          clearTimeout(refreshTimeOut);
479        }, 360);
480      });
481      /**
482       * Async Function
483       */
484      let asyncFuncList = this.processAsyncFuncMap[it.pid] || [];
485      let asyncFuncGroup = Utils.groupBy(asyncFuncList, 'funName');
486      Reflect.ownKeys(asyncFuncGroup).map((key: any) => {
487        let asyncFunctions: Array<any> = asyncFuncGroup[key];
488        if (asyncFunctions.length > 0) {
489          let isIntersect = (a: any, b: any) =>
490            Math.max(a.startTs + a.dur, b.startTs + b.dur) - Math.min(a.startTs, b.startTs) < a.dur + b.dur;
491          let depthArray: any = [];
492          let createDepth = (currentDepth: number, index: number) => {
493            if (
494              depthArray[currentDepth] == undefined ||
495              !isIntersect(depthArray[currentDepth], asyncFunctions[index])
496            ) {
497              asyncFunctions[index].depth = currentDepth;
498              depthArray[currentDepth] = asyncFunctions[index];
499            } else {
500              createDepth(++currentDepth, index);
501            }
502          };
503          asyncFunctions.forEach((it, i) => {
504            if (it.dur == -1) {
505              it.dur = (TraceRow.range?.endNS || 0) - it.startTs;
506              it.flag = 'Did not end';
507            }
508            createDepth(0, i);
509          });
510          let max = Math.max(...asyncFunctions.map((it) => it.depth || 0)) + 1;
511          let maxHeight = max * 20;
512          let funcRow = TraceRow.skeleton<FuncStruct>();
513          funcRow.rowId = `${asyncFunctions[0].funName}-${it.pid}`;
514          funcRow.asyncFuncName = asyncFunctions[0].funName;
515          funcRow.asyncFuncNamePID = it.pid;
516          funcRow.rowType = TraceRow.ROW_TYPE_FUNC;
517          funcRow.rowParentId = `${it.pid}`;
518          funcRow.rowHidden = !processRow.expansion;
519          funcRow.style.width = '100%';
520          funcRow.style.height = `${maxHeight}px`;
521          funcRow.setAttribute('height', `${maxHeight}`);
522          funcRow.name = `${asyncFunctions[0].funName}`;
523          funcRow.setAttribute('children', '');
524          funcRow.supplier = () => new Promise((resolve) => resolve(asyncFunctions));
525          funcRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
526          funcRow.selectChangeHandler = this.trace.selectChangeHandler;
527          funcRow.onThreadHandler = (cacheFlag) => {
528            let context = funcRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
529            funcRow.canvasSave(context);
530            (renders['func'] as FuncRender).renderMainThread(
531              {
532                context: context,
533                useCache: cacheFlag,
534                type: `func-${asyncFunctions[0].funName}-${it.pid}`,
535              },
536              funcRow
537            );
538            funcRow.canvasRestore(context);
539          };
540          processRow.addChildTraceRow(funcRow);
541        }
542      });
543
544      /**
545       * 添加进程内存信息
546       */
547      let processMem = this.processMem.filter((mem) => mem.pid === it.pid);
548      processMem.forEach((mem) => {
549        let row = TraceRow.skeleton<ProcessMemStruct>();
550        row.rowId = `${mem.trackId}`;
551        row.rowType = TraceRow.ROW_TYPE_MEM;
552        row.rowParentId = `${it.pid}`;
553        row.rowHidden = !processRow.expansion;
554        row.style.height = '40px';
555        row.style.width = '100%';
556        row.name = `${mem.trackName}`;
557        row.setAttribute('children', '');
558        row.favoriteChangeHandler = this.trace.favoriteChangeHandler;
559        row.selectChangeHandler = this.trace.selectChangeHandler;
560        row.focusHandler = () => {
561          this.trace.displayTip(
562            row,
563            ProcessMemStruct.hoverProcessMemStruct,
564            `<span>${ProcessMemStruct.hoverProcessMemStruct?.value || '0'}</span>`
565          );
566        };
567        row.supplier = () =>
568          queryProcessMemData(mem.trackId).then((resultProcess) => {
569            let maxValue = Math.max(...resultProcess.map((it) => it.value || 0));
570            for (let j = 0; j < resultProcess.length; j++) {
571              resultProcess[j].maxValue = maxValue;
572              if (j == resultProcess.length - 1) {
573                resultProcess[j].duration = (TraceRow.range?.totalNS || 0) - (resultProcess[j].startTime || 0);
574              } else {
575                resultProcess[j].duration = (resultProcess[j + 1].startTime || 0) - (resultProcess[j].startTime || 0);
576              }
577              if (j > 0) {
578                resultProcess[j].delta = (resultProcess[j].value || 0) - (resultProcess[j - 1].value || 0);
579              } else {
580                resultProcess[j].delta = 0;
581              }
582            }
583            return resultProcess;
584          });
585        row.onThreadHandler = (useCache) => {
586          let context = row.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
587          row.canvasSave(context);
588          (renders['mem'] as MemRender).renderMainThread(
589            {
590              context: context,
591              useCache: useCache,
592              type: `mem ${mem.trackId} ${mem.trackName}`,
593            },
594            row
595          );
596          row.canvasRestore(context);
597        };
598        processRow.addChildTraceRow(row);
599      });
600      /**
601       * add thread list
602       */
603      let threads = this.processThreads.filter((thread) => thread.pid === it.pid && thread.tid != 0);
604      for (let j = 0; j < threads.length; j++) {
605        let thread = threads[j];
606        let threadRow = TraceRow.skeleton<ThreadStruct>();
607        threadRow.rowId = `${thread.tid}`;
608        threadRow.rowType = TraceRow.ROW_TYPE_THREAD;
609        threadRow.rowParentId = `${it.pid}`;
610        threadRow.rowHidden = !processRow.expansion;
611        threadRow.index = j;
612        threadRow.style.height = '30px';
613        threadRow.style.width = '100%';
614        threadRow.name = `${thread.threadName || 'Thread'} ${thread.tid}`;
615        threadRow.setAttribute('children', '');
616        threadRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
617        threadRow.selectChangeHandler = this.trace.selectChangeHandler;
618        threadRow.supplier = () =>
619          queryThreadData(thread.tid || 0, it.pid || 0).then((res) => {
620            if (res.length <= 0) {
621              threadRow.rowDiscard = true;
622              this.trace.refreshCanvas(true);
623            }
624            return res;
625          });
626        threadRow.focusHandler = (ev) => {};
627        threadRow.onThreadHandler = (useCache) => {
628          let context = threadRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
629          threadRow.canvasSave(context);
630          (renders['thread'] as ThreadRender).renderMainThread(
631            {
632              context: context,
633              useCache: useCache,
634              type: `thread ${thread.tid} ${thread.threadName}`,
635              translateY: threadRow.translateY,
636            },
637            threadRow
638          );
639          threadRow.canvasRestore(context);
640        };
641        if (threadRow.rowId == threadRow.rowParentId) {
642          if (actualRow != null) {
643            processRow.addChildTraceRowAfter(threadRow, actualRow);
644          } else if (expectedRow != null) {
645            processRow.addChildTraceRowAfter(threadRow, expectedRow);
646          } else if (soRow) {
647            processRow.addChildTraceRowAfter(threadRow, soRow);
648          } else if (startupRow) {
649            processRow.addChildTraceRowAfter(threadRow, startupRow);
650          } else {
651            processRow.addChildTraceRowSpecifyLocation(threadRow, 0);
652          }
653        } else {
654          processRow.addChildTraceRow(threadRow);
655        }
656        if (this.threadFuncMaxDepthMap.get(`${thread.upid}-${thread.tid}`) != undefined) {
657          let max = this.threadFuncMaxDepthMap.get(`${thread.upid}-${thread.tid}`) || 1;
658          let maxHeight = max * 20;
659          let funcRow = TraceRow.skeleton<FuncStruct>();
660          funcRow.rowId = `${thread.tid}`;
661          funcRow.rowType = TraceRow.ROW_TYPE_FUNC;
662          funcRow.rowParentId = `${it.pid}`;
663          funcRow.rowHidden = !processRow.expansion;
664          funcRow.checkType = threadRow.checkType;
665          funcRow.style.width = '100%';
666          funcRow.style.height = `${maxHeight}px`;
667          funcRow.name = `${thread.threadName || 'Thread'} ${thread.tid}`;
668          funcRow.setAttribute('children', '');
669          funcRow.supplier = () =>
670            getFunDataByTid(thread.tid || 0, thread.upid || 0).then((funs: Array<FuncStruct>) => {
671              if (funs.length > 0) {
672                let isBinder = (data: FuncStruct): boolean => {
673                  return (
674                    data.funName != null &&
675                    (data.funName.toLowerCase().startsWith('binder transaction async') || //binder transaction
676                      data.funName.toLowerCase().startsWith('binder async') ||
677                      data.funName.toLowerCase().startsWith('binder reply'))
678                  );
679                };
680                funs.forEach((fun) => {
681                  if (isBinder(fun)) {
682                  } else {
683                    if (fun.dur == -1) {
684                      fun.dur = (TraceRow.range?.totalNS || 0) - (fun.startTs || 0);
685                      fun.flag = 'Did not end';
686                    }
687                  }
688                });
689              } else {
690                funcRow.rowDiscard = true;
691                this.trace.refreshCanvas(true);
692              }
693              return funs;
694            });
695          funcRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
696          funcRow.selectChangeHandler = this.trace.selectChangeHandler;
697          funcRow.onThreadHandler = (useCache) => {
698            let context = funcRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
699            funcRow.canvasSave(context);
700            (renders['func'] as FuncRender).renderMainThread(
701              {
702                context: context,
703                useCache: useCache,
704                type: `func${thread.tid}${thread.threadName}`,
705              },
706              funcRow
707            );
708            funcRow.canvasRestore(context);
709          };
710          processRow.addChildTraceRowAfter(funcRow, threadRow);
711        }
712      }
713    }
714    let durTime = new Date().getTime() - time;
715    info('The time to load the Process data is: ', durTime);
716  }
717
718  addStartUpRow(processRow: TraceRow<ProcessStruct>) {
719    processRow.setAttribute('hasStartup','true');
720    let startupRow: TraceRow<AppStartupStruct> = TraceRow.skeleton<AppStartupStruct>();
721    startupRow.rowId = `app-start-${processRow.rowId}`;
722    startupRow.rowType = TraceRow.ROW_TYPE_APP_STARTUP;
723    startupRow.rowParentId = `${processRow.rowId}`;
724    startupRow.rowHidden = !processRow.expansion;
725    startupRow.index = 0;
726    startupRow.style.height = '30px';
727    startupRow.style.width = `100%`;
728    startupRow.name = `App Startups`;
729    startupRow.setAttribute('children', '');
730    startupRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
731    startupRow.selectChangeHandler = this.trace.selectChangeHandler;
732    startupRow.supplier = () =>
733      queryProcessStartup(parseInt(processRow.rowId!)).then((res) => {
734        if (res.length <= 0) {
735          startupRow.rowDiscard = true;
736          this.trace.refreshCanvas(true);
737        }
738        for (let i = 0; i < res.length; i++) {
739          if (res[i].startName! < 4 && i < res.length - 1) {
740            res[i].endItid = res[i + 1].itid;
741          }
742        }
743        return res;
744      });
745    startupRow.focusHandler = (ev) => {};
746    startupRow.onThreadHandler = (useCache) => {
747      let context = startupRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
748      startupRow.canvasSave(context);
749      (renders['app-start-up'] as AppStartupRender).renderMainThread(
750        {
751          context: context,
752          useCache: useCache,
753          type: `app-startup ${processRow.rowId}`,
754        },
755        startupRow
756      );
757      startupRow.canvasRestore(context);
758    };
759    processRow.addChildTraceRow(startupRow);
760    return startupRow;
761  }
762
763  addSoInitRow(processRow: TraceRow<ProcessStruct>, maxDepth: number) {
764    processRow.setAttribute('hasStaticInit','true');
765    let maxHeight = (maxDepth + 1) * 20;
766    let soRow: TraceRow<SoStruct> = TraceRow.skeleton<SoStruct>();
767    soRow.rowId = `app-start-${processRow.rowId}`;
768    soRow.rowType = TraceRow.ROW_TYPE_STATIC_INIT;
769    soRow.rowParentId = `${processRow.rowId}`;
770    soRow.rowHidden = !processRow.expansion;
771    soRow.index = 0;
772    soRow.style.height = `${maxHeight}px`;
773    soRow.style.width = `100%`;
774    soRow.name = `Static Initialization`;
775    soRow.setAttribute('children', '');
776    soRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
777    soRow.selectChangeHandler = this.trace.selectChangeHandler;
778    soRow.supplier = () =>
779      queryProcessSoInitData(parseInt(processRow.rowId!)).then((res) => {
780        if (res.length <= 0) {
781          soRow.rowDiscard = true;
782          this.trace.refreshCanvas(true);
783        }
784        res.forEach(so => {
785          if (so.soName) {
786            so.soName = so.soName.replace('dlopen: ', '');
787          }
788        })
789        return res;
790      });
791    soRow.focusHandler = (ev) => {};
792    soRow.onThreadHandler = (useCache) => {
793      let context = soRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
794      soRow.canvasSave(context);
795      (renders['app-so-init'] as SoRender).renderMainThread(
796        {
797          context: context,
798          useCache: useCache,
799          type: `static-init ${processRow.rowId}`,
800        },
801        soRow
802      );
803      soRow.canvasRestore(context);
804    };
805    processRow.addChildTraceRow(soRow);
806    return soRow;
807  }
808
809  insertAfter(newEl: HTMLElement, targetEl: HTMLElement) {
810    let parentEl = targetEl.parentNode;
811    if (parentEl!.lastChild == targetEl) {
812      parentEl!.appendChild(newEl);
813    } else {
814      parentEl!.insertBefore(newEl, targetEl.nextSibling);
815    }
816  }
817}
818