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