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