• 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 */
15import { SpSystemTrace } from '../SpSystemTrace';
16import { TraceRow } from '../trace/base/TraceRow';
17import { renders } from '../../database/ui-worker/ProcedureWorker';
18import { type EmptyRender } from '../../database/ui-worker/cpu/ProcedureWorkerCPU';
19import { type HeapTimelineRender, HeapTimelineStruct } from '../../database/ui-worker/ProcedureWorkerHeapTimeline';
20import { HeapDataInterface, type ParseListener } from '../../../js-heap/HeapDataInterface';
21import { LoadDatabase } from '../../../js-heap/LoadDatabase';
22import { type FileInfo } from '../../../js-heap/model/UiStruct';
23import { type HeapSnapshotRender, HeapSnapshotStruct } from '../../database/ui-worker/ProcedureWorkerHeapSnapshot';
24import { Utils } from '../trace/base/Utils';
25import { type JsCpuProfilerChartFrame } from '../../bean/JsStruct';
26import { type JsCpuProfilerRender, JsCpuProfilerStruct } from '../../database/ui-worker/ProcedureWorkerCpuProfiler';
27import { ns2s } from '../../database/ui-worker/ProcedureWorkerCommon';
28import { cpuProfilerDataSender } from '../../database/data-trafic/ArkTsSender';
29import { queryJsCpuProfilerConfig, queryJsCpuProfilerData } from '../../database/sql/Cpu.sql';
30import { queryJsMemoryData } from '../../database/sql/Memory.sql';
31import { type HeapSample } from '../../../js-heap/model/DatabaseStruct';
32
33const TYPE_SNAPSHOT = 0;
34const TYPE_TIMELINE = 1;
35const LAMBDA_FUNCTION_NAME = '(anonymous)';
36export class SpArkTsChart implements ParseListener {
37  private trace: SpSystemTrace; // @ts-ignore
38  private folderRow: TraceRow<unknown> | undefined;
39  private jsCpuProfilerRow: TraceRow<JsCpuProfilerStruct> | undefined;
40  private heapTimelineRow: TraceRow<HeapTimelineStruct> | undefined;
41  private heapSnapshotRow: TraceRow<HeapSnapshotStruct> | undefined;
42  private loadJsDatabase: LoadDatabase;
43  private allCombineDataMap = new Map<number, JsCpuProfilerChartFrame>();
44  private process: string = '';
45
46  constructor(trace: SpSystemTrace) {
47    this.trace = trace;
48    this.loadJsDatabase = LoadDatabase.getInstance();
49  }
50
51  public get chartFrameMap(): Map<number, JsCpuProfilerChartFrame> {
52    return this.allCombineDataMap;
53  }
54
55  private cpuProfilerSupplierFrame(): void {
56    // @ts-ignore
57    this.jsCpuProfilerRow!.supplierFrame = (): Promise<Array<unknown>> => {
58      return cpuProfilerDataSender(this.jsCpuProfilerRow!).then((res: unknown) => {
59        // @ts-ignore
60        let maxHeight = res.maxDepth * 20;
61        this.jsCpuProfilerRow!.style.height = `${maxHeight}px`; // @ts-ignore
62        if (res.dataList.length > 0) {
63          this.allCombineDataMap = new Map<number, JsCpuProfilerChartFrame>(); // @ts-ignore
64          for (let data of res.dataList) {
65            this.allCombineDataMap.set(data.id, data);
66            SpSystemTrace.jsProfilerMap.set(data.id, data);
67          } // @ts-ignore
68          res.dataList.forEach((data: unknown) => {
69            // @ts-ignore
70            data.children = []; // @ts-ignore
71            if (data.childrenIds.length > 0) {
72              // @ts-ignore
73              for (let id of data.childrenIds) {
74                let child = SpSystemTrace.jsProfilerMap.get(Number(id)); // @ts-ignore
75                data.children.push(child);
76              }
77            } // @ts-ignore
78            data.name = SpSystemTrace.DATA_DICT.get(data.nameId) || LAMBDA_FUNCTION_NAME; // @ts-ignore
79            data.url = SpSystemTrace.DATA_DICT.get(data.urlId) || 'unknown'; // @ts-ignore
80            if (data.url && data.url !== 'unknown') {
81              // @ts-ignore
82              let dirs = data.url.split('/'); // @ts-ignore
83              data.scriptName = dirs.pop() || '';
84            }
85          });
86        } // @ts-ignore
87        return res.dataList;
88      });
89    };
90  }
91
92  private folderThreadHandler(): void {
93    this.folderRow!.onThreadHandler = (useCache): void => {
94      this.folderRow!.canvasSave(this.trace.canvasPanelCtx!);
95      if (this.folderRow!.expansion) {
96        // @ts-ignore
97        this.trace.canvasPanelCtx?.clearRect(0, 0, this.folderRow!.frame.width, this.folderRow!.frame.height);
98      } else {
99        (renders.empty as EmptyRender).renderMainThread(
100          {
101            context: this.trace.canvasPanelCtx,
102            useCache: useCache,
103            type: '',
104          },
105          this.folderRow!
106        );
107      }
108      this.folderRow!.canvasRestore(this.trace.canvasPanelCtx!, this.trace);
109    };
110  }
111
112  public async initFolder(): Promise<void> {
113    let jsConfig = await queryJsCpuProfilerConfig();
114    let jsCpu = await queryJsCpuProfilerData();
115    let jsMemory = await queryJsMemoryData();
116    if (jsMemory.length > 0 || jsCpu.length > 0) {
117      this.folderRow = TraceRow.skeleton();
118      //@ts-ignore
119      this.process = jsConfig[0].pid;
120      this.folderRow.rowId = this.process;
121      this.folderRow.rowType = TraceRow.ROW_TYPE_ARK_TS;
122      this.folderRow.style.height = '40px';
123      this.folderRow.rowParentId = '';
124      this.folderRow.folder = true;
125      this.folderRow.name = `Ark Ts ${this.process}`;
126      this.folderRow.addTemplateTypes('ArkTs');
127      this.folderRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
128      this.folderRow.selectChangeHandler = this.trace.selectChangeHandler;
129      this.folderRow.supplierFrame = (): Promise<Array<unknown>> =>
130        new Promise<Array<unknown>>((resolve) => resolve([]));
131      this.folderThreadHandler();
132      this.trace.rowsEL?.appendChild(this.folderRow); //@ts-ignore
133      if (this.folderRow && jsConfig[0].type !== -1 && jsMemory.length > 0) {
134        this.folderRow.addTemplateTypes('Memory');
135        if (
136          //@ts-ignore
137          jsConfig[0].type === TYPE_SNAPSHOT
138        ) {
139          // snapshot
140          await this.initSnapshotChart();
141        } else if (
142          //@ts-ignore
143          jsConfig[0].type === TYPE_TIMELINE
144        ) {
145          // timeline
146          await this.initTimelineChart();
147        }
148      }
149      //@ts-ignore
150      if (this.folderRow && jsConfig[0].enableCpuProfiler === 1 && jsCpu.length > 0) {
151        await this.initJsCpuChart();
152      }
153      if ((this.heapSnapshotRow || this.heapTimelineRow) && jsMemory.length > 0) {
154        await this.loadJsDatabase.loadDatabase(this);
155      }
156      if (this.jsCpuProfilerRow && jsCpu.length > 0) {
157        this.cpuProfilerSupplierFrame();
158      }
159    }
160  }
161
162  private async initTimelineChart(): Promise<void> {
163    this.heapTimelineRow = TraceRow.skeleton<HeapTimelineStruct>();
164    this.heapTimelineRow.rowParentId = this.process;
165    this.heapTimelineRow.rowHidden = !this.folderRow!.expansion;
166    this.heapTimelineRow.style.height = '40px';
167    this.heapTimelineRow.name = 'Heaptimeline';
168    this.heapTimelineRow.folder = false;
169    this.heapTimelineRow.rowType = TraceRow.ROW_TYPE_HEAP_TIMELINE;
170    this.heapTimelineRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
171    this.heapTimelineRow.selectChangeHandler = this.trace.selectChangeHandler;
172    this.heapTimelineRow.setAttribute('children', '');
173    this.heapTimelineRow!.focusHandler = (): void => {
174      this.trace?.displayTip(
175        this.heapTimelineRow!,
176        HeapTimelineStruct.hoverHeapTimelineStruct,
177        `<span>Size: ${Utils.getBinaryByteWithUnit(HeapTimelineStruct.hoverHeapTimelineStruct?.size || 0)}</span>`
178      );
179    };
180    this.heapTimelineRow!.findHoverStruct = (): void => {
181      HeapTimelineStruct.hoverHeapTimelineStruct = this.heapTimelineRow!.getHoverStruct();
182    };
183    this.folderRow!.addChildTraceRow(this.heapTimelineRow!);
184  }
185
186  private async initSnapshotChart(): Promise<void> {
187    this.heapSnapshotRow = TraceRow.skeleton<HeapSnapshotStruct>();
188    this.heapSnapshotRow.rowParentId = this.process;
189    this.heapSnapshotRow.rowHidden = !this.folderRow!.expansion;
190    this.heapSnapshotRow.style.height = '40px';
191    this.heapSnapshotRow.name = 'Heapsnapshot';
192    this.heapSnapshotRow.rowId = 'heapsnapshot';
193    this.heapSnapshotRow.folder = false;
194
195    this.heapSnapshotRow.rowType = TraceRow.ROW_TYPE_HEAP_SNAPSHOT;
196    this.heapSnapshotRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
197    this.heapSnapshotRow.selectChangeHandler = this.trace.selectChangeHandler;
198    this.heapSnapshotRow.setAttribute('children', '');
199    this.heapSnapshotRow!.focusHandler = (): void => {
200      this.trace?.displayTip(
201        this.heapSnapshotRow!,
202        HeapSnapshotStruct.hoverSnapshotStruct,
203        `<span>Name: ${HeapSnapshotStruct.hoverSnapshotStruct?.name || ''}</span>
204            <span>Size: ${Utils.getBinaryByteWithUnit(HeapSnapshotStruct.hoverSnapshotStruct?.size || 0)}</span>`
205      );
206    };
207    this.heapSnapshotRow!.findHoverStruct = (): void => {
208      HeapSnapshotStruct.hoverSnapshotStruct = this.heapSnapshotRow!.getHoverStruct();
209    };
210    this.folderRow!.addChildTraceRow(this.heapSnapshotRow);
211  }
212
213  private heapLineThreadHandler(samples: HeapSample[]): void {
214    this.heapTimelineRow!.onThreadHandler = (useCache): void => {
215      let context: CanvasRenderingContext2D;
216      if (this.heapTimelineRow?.currentContext) {
217        context = this.heapTimelineRow!.currentContext;
218      } else {
219        context = this.heapTimelineRow!.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
220      }
221      this.heapTimelineRow!.canvasSave(context);
222      (renders['heap-timeline'] as HeapTimelineRender).renderMainThread(
223        {
224          context: context,
225          useCache: useCache,
226          type: 'heap-timeline',
227          samples: samples,
228        },
229        this.heapTimelineRow!
230      );
231      this.heapTimelineRow!.canvasRestore(context, this.trace);
232    };
233  }
234
235  private heapSnapshotThreadHandler(): void {
236    this.heapSnapshotRow!.onThreadHandler = (useCache): void => {
237      let context: CanvasRenderingContext2D;
238      if (this.heapSnapshotRow?.currentContext) {
239        context = this.heapSnapshotRow!.currentContext;
240      } else {
241        context = this.heapSnapshotRow!.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
242      }
243      this.heapSnapshotRow!.canvasSave(context);
244      (renders['heap-snapshot'] as HeapSnapshotRender).renderMainThread(
245        {
246          context: context,
247          useCache: useCache,
248          type: 'heap-snapshot',
249        },
250        this.heapSnapshotRow!
251      );
252      this.heapSnapshotRow!.canvasRestore(context, this.trace);
253    };
254  }
255
256  public async parseDone(fileModule: Array<FileInfo>): Promise<void> {
257    if (fileModule.length > 0) {
258      let heapFile = HeapDataInterface.getInstance().getFileStructs();
259      let file = heapFile[0];
260      this.trace.snapshotFile = file;
261      if (file.type === TYPE_TIMELINE) {
262        let samples = HeapDataInterface.getInstance().getSamples(file.id);
263        this.heapTimelineRow!.rowId = `heaptimeline${file.id}`; // @ts-ignore
264        this.heapTimelineRow!.supplierFrame = (): Promise<unknown> =>
265          new Promise<unknown>((resolve) => resolve(samples));
266        this.heapLineThreadHandler(samples);
267      } else if (file.type === TYPE_SNAPSHOT) {
268        // @ts-ignore
269        this.heapSnapshotRow!.supplierFrame = (): Promise<Array<unknown>> =>
270          new Promise<Array<unknown>>((resolve) => resolve(heapFile));
271        this.heapSnapshotThreadHandler();
272      }
273    }
274  }
275
276  private initJsCpuChart = async (): Promise<void> => {
277    this.jsCpuProfilerRow = TraceRow.skeleton<JsCpuProfilerStruct>();
278    this.jsCpuProfilerRow.rowParentId = this.process;
279    this.jsCpuProfilerRow.rowHidden = !this.folderRow!.expansion;
280    this.jsCpuProfilerRow.name = 'CpuProfiler';
281    this.jsCpuProfilerRow.rowId = 'JsCpuProfiler';
282    this.jsCpuProfilerRow.folder = false;
283    this.jsCpuProfilerRow.rowType = TraceRow.ROW_TYPE_JS_CPU_PROFILER;
284    this.jsCpuProfilerRow!.style.height = '40px';
285    this.jsCpuProfilerRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
286    this.jsCpuProfilerRow.selectChangeHandler = this.trace.selectChangeHandler;
287    this.jsCpuProfilerRow.setAttribute('children', '');
288    this.jsCpuProfilerRow.focusHandler = (): void => {
289      this.trace?.displayTip(
290        this.jsCpuProfilerRow!,
291        JsCpuProfilerStruct.hoverJsCpuProfilerStruct,
292        `<span style='font-weight: bold;'>Name: </span>
293        <span>${JsCpuProfilerStruct.hoverJsCpuProfilerStruct?.name || ''}</span><br>
294        <span style='font-weight: bold;'>Self Time: </span>
295        <span>${ns2s(JsCpuProfilerStruct.hoverJsCpuProfilerStruct?.selfTime || 0)}</span><br>
296        <span style='font-weight: bold;'>Total Time: </span>
297        <span>${ns2s(JsCpuProfilerStruct.hoverJsCpuProfilerStruct?.totalTime || 0)}</span><br>
298        <span style='font-weight: bold;'>Url: </span>
299        <span>${JsCpuProfilerStruct.hoverJsCpuProfilerStruct?.url || 0}</span>`
300      );
301    };
302    this.jsCpuProfilerRow!.findHoverStruct = (): void => {
303      JsCpuProfilerStruct.hoverJsCpuProfilerStruct = this.jsCpuProfilerRow!.getHoverStruct();
304    };
305    this.jsCpuProfilerRow.onThreadHandler = (useCache): void => {
306      let context: CanvasRenderingContext2D;
307      if (this.jsCpuProfilerRow?.currentContext) {
308        context = this.jsCpuProfilerRow!.currentContext;
309      } else {
310        context = this.jsCpuProfilerRow!.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
311      }
312      this.jsCpuProfilerRow!.canvasSave(context);
313      (renders['js-cpu-profiler'] as JsCpuProfilerRender).renderMainThread(
314        {
315          context: context,
316          useCache: useCache,
317          type: 'js-cpu-profiler',
318        },
319        this.jsCpuProfilerRow!
320      );
321      this.jsCpuProfilerRow!.canvasRestore(context, this.trace);
322    };
323    this.folderRow!.addChildTraceRow(this.jsCpuProfilerRow);
324  };
325}
326