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 { TraceRow } from '../trace/base/TraceRow';
18import { info } from '../../../log/Log';
19import { procedurePool } from '../../database/Procedure';
20import { type NativeEventHeap } from '../../bean/NativeHook';
21import { HeapRender, HeapStruct } from '../../database/ui-worker/ProcedureWorkerHeap';
22import { Utils } from '../trace/base/Utils';
23import { renders } from '../../database/ui-worker/ProcedureWorker';
24import { EmptyRender } from '../../database/ui-worker/cpu/ProcedureWorkerCPU';
25import { type BaseStruct } from '../../bean/BaseStruct';
26import {
27  nativeMemoryChartDataCacheSender,
28  nativeMemoryChartDataSender,
29} from '../../database/data-trafic/NativeMemoryDataSender';
30import { queryNativeHookProcess, queryNativeHookStatisticsCount } from '../../database/sql/NativeHook.sql';
31import { queryHeapGroupByEvent } from '../../database/sql/SqlLite.sql';
32import { queryNativeMemoryRealTime } from '../../database/sql/Memory.sql';
33import { queryBootTime } from '../../database/sql/Clock.sql';
34import { SpStatisticsHttpUtil } from '../../../statistics/util/SpStatisticsHttpUtil';
35
36export class SpNativeMemoryChart {
37  static EVENT_HEAP: Array<NativeEventHeap> = [];
38  static REAL_TIME_DIF: number = 0;
39  private trace: SpSystemTrace;
40
41  constructor(trace: SpSystemTrace) {
42    this.trace = trace;
43  }
44
45  folderThreadHandler(row: TraceRow<BaseStruct>): void {
46    row.onThreadHandler = (useCache): void => {
47      row.canvasSave(this.trace.canvasPanelCtx!);
48      if (row.expansion) {
49        // @ts-ignore
50        this.trace.canvasPanelCtx?.clearRect(0, 0, row.frame.width, row.frame.height);
51      } else {
52        (renders.empty as EmptyRender).renderMainThread(
53          {
54            context: this.trace.canvasPanelCtx,
55            useCache: useCache,
56            type: '',
57          },
58          row
59        );
60      }
61      row.canvasRestore(this.trace.canvasPanelCtx!, this.trace);
62    };
63  }
64
65  chartThreadHandler(row: TraceRow<HeapStruct>): void {
66    row.onThreadHandler = (useCache): void => {
67      let context: CanvasRenderingContext2D;
68      if (row.currentContext) {
69        context = row.currentContext;
70      } else {
71        context = row.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
72      }
73      row.canvasSave(context);
74      (renders.heap as HeapRender).renderMainThread(
75        {
76          context: context,
77          useCache: useCache,
78          type: 'heap',
79        },
80        row
81      );
82      row.canvasRestore(context, this.trace);
83    };
84  }
85
86  initNativeMemoryFolder(process: number, ipid: number): TraceRow<BaseStruct> {
87    const nativeRow = TraceRow.skeleton();
88    nativeRow.rowId = `native-memory ${process} ${ipid}`;
89    nativeRow.index = 0;
90    nativeRow.rowType = TraceRow.ROW_TYPE_NATIVE_MEMORY;
91    nativeRow.drawType = 0;
92    nativeRow.style.height = '40px';
93    nativeRow.rowParentId = '';
94    nativeRow.folder = true;
95    nativeRow.addTemplateTypes('NativeMemory', 'Memory');
96    nativeRow.name = `Native Memory (${process})`;
97    nativeRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
98    nativeRow.selectChangeHandler = this.trace.selectChangeHandler;
99    nativeRow.addRowSettingPop();
100    nativeRow.rowSetting = 'enable';
101    nativeRow.rowSettingPopoverDirection = 'bottomLeft';
102    nativeRow.rowSettingList = [
103      {
104        key: '0',
105        title: 'Current Bytes',
106        checked: true,
107      },
108      {
109        key: '1',
110        title: 'Native Memory Density',
111      },
112    ];
113    nativeRow.onRowSettingChangeHandler = (value): void => {
114      nativeRow.childrenList.forEach((row) => (row.drawType = parseInt(value[0])));
115      this.trace
116        .getCollectRows((row) => row.rowType === 'heap')
117        .forEach((it) => {
118          it.drawType = parseInt(value[0]);
119        });
120      this.trace.refreshCanvas(false);
121    };
122    nativeRow.supplier = (): Promise<BaseStruct[]> => new Promise<Array<BaseStruct>>((resolve) => resolve([]));
123    this.folderThreadHandler(nativeRow);
124    this.trace.rowsEL?.appendChild(nativeRow);
125    return nativeRow;
126  }
127
128  initAllocMapChart(folder: TraceRow<BaseStruct>, type: string, process: { pid: number; ipid: number }): void {
129    const chartList = ['All Heap & Anonymous VM', 'All Heap', 'All Anonymous VM'];
130    for (let i = 0; i < chartList.length; i++) {
131      const nm = chartList[i];
132      const allHeapRow = TraceRow.skeleton<HeapStruct>();
133      allHeapRow.index = i;
134      allHeapRow.rowParentId = `native-memory ${process.pid} ${process.ipid}`;
135      allHeapRow.rowHidden = !folder.expansion;
136      allHeapRow.style.height = '40px';
137      allHeapRow.name = nm;
138      allHeapRow.rowId = nm;
139      allHeapRow.drawType = 0;
140      allHeapRow.isHover = true;
141      allHeapRow.folder = false;
142      allHeapRow.rowType = TraceRow.ROW_TYPE_HEAP;
143      allHeapRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
144      allHeapRow.selectChangeHandler = this.trace.selectChangeHandler;
145      allHeapRow.setAttribute('heap-type', type);
146      allHeapRow.setAttribute('children', '');
147      allHeapRow.focusHandler = (): void => {
148        let tip = '';
149        if (HeapStruct.hoverHeapStruct) {
150          if (allHeapRow.drawType === 1) {
151            tip = `<span>${HeapStruct.hoverHeapStruct.density}</span>`;
152          } else {
153            tip = `<span>${Utils.getByteWithUnit(HeapStruct.hoverHeapStruct.heapsize!)}</span>`;
154          }
155        }
156        this.trace?.displayTip(allHeapRow, HeapStruct.hoverHeapStruct, tip);
157      };
158      allHeapRow.findHoverStruct = (): void => {
159        HeapStruct.hoverHeapStruct = allHeapRow.getHoverStruct();
160      }; //@ts-ignore
161      allHeapRow.supplierFrame = (): Promise<unknown> =>
162        nativeMemoryChartDataSender(allHeapRow, {
163          eventType: i,
164          ipid: process.ipid,
165          model: type,
166          drawType: allHeapRow.drawType,
167        });
168      this.chartThreadHandler(allHeapRow);
169      folder.addChildTraceRow(allHeapRow);
170    }
171  }
172
173  initChart = async (): Promise<void> => {
174    let time = new Date().getTime();
175    let nativeMemoryType = 'native_hook';
176    let nmsCount = await queryNativeHookStatisticsCount();
177    if (nmsCount && nmsCount[0] && nmsCount[0].num > 0) {
178      nativeMemoryType = 'native_hook_statistic';
179    }
180    let nativeProcess = await queryNativeHookProcess(nativeMemoryType);
181    info('NativeHook Process data size is: ', nativeProcess!.length);
182    if (nativeProcess.length === 0) {
183      return;
184    }
185    // 有native_memory进程,统计nativehook插件
186    let requestBody = {
187      eventData: {
188        plugin: ['nativehook']
189      }
190    };
191    SpStatisticsHttpUtil.recordPluginUsage(requestBody);
192    await this.initNativeMemory();
193    await nativeMemoryChartDataCacheSender(
194      nativeProcess.map((it) => it.ipid),
195      nativeMemoryType
196    );
197    SpNativeMemoryChart.EVENT_HEAP = await queryHeapGroupByEvent(nativeMemoryType);
198    for (const process of nativeProcess) {
199      const nativeRow = this.initNativeMemoryFolder(process.pid, process.ipid);
200      this.initAllocMapChart(nativeRow, nativeMemoryType, process);
201    }
202    let durTime = new Date().getTime() - time;
203    info('The time to load the Native Memory data is: ', durTime);
204  };
205
206  initNativeMemory = async (): Promise<void> => {
207    let time = new Date().getTime();
208    let isRealtime = false;
209    let realTimeDif = 0;
210    SpNativeMemoryChart.REAL_TIME_DIF = 0;
211    let queryTime = await queryNativeMemoryRealTime();
212    let bootTime = await queryBootTime();
213    if (queryTime.length > 0) {
214      //@ts-ignore
215      isRealtime = queryTime[0].clock_name === 'realtime';
216    }
217    if (bootTime.length > 0 && isRealtime) {
218      //@ts-ignore
219      realTimeDif = queryTime[0].ts - bootTime[0].ts;
220      SpNativeMemoryChart.REAL_TIME_DIF = realTimeDif;
221    }
222    await new Promise<unknown>((resolve) => {
223      procedurePool.submitWithName(
224        'logic0',
225        'native-memory-init',
226        { isRealtime, realTimeDif },
227        undefined,
228        (res: unknown) => {
229          resolve(res);
230        }
231      );
232    });
233    let durTime = new Date().getTime() - time;
234    info('The time to init the native memory data is: ', durTime);
235  };
236}
237