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