• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2025 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 * as fs from 'fs';
17import path from 'path';
18import type { IOptions } from '../configs/IOptions';
19import type { IPrinterOption } from '../configs/INameObfuscationOption';
20import { performance } from 'perf_hooks';
21import { performanceTimeAndMemPrinter } from '../ArkObfuscator';
22import { printerTimeAndMemDataConfig } from '../initialization/Initializer';
23
24// DevEco Studio perf mode
25export enum PerfMode {
26  NORMAL = 0, // Normal mode
27  ADVANCED = 1, // Advanced mode
28};
29
30export enum EventList {
31  OBFUSCATION_INITIALIZATION = 'Obfuscation initialization',
32  SCAN_SYSTEMAPI = 'Scan system api',
33  SCAN_SOURCEFILES = 'Scan source files',
34  ALL_FILES_OBFUSCATION = 'All files obfuscation',
35  OBFUSCATE = 'Obfuscate',
36  CREATE_AST = 'Create AST',
37  OBFUSCATE_AST = 'Obfuscate AST',
38  VIRTUAL_CONSTRUCTOR_OBFUSCATION = 'Virtual constructor obfuscation',
39  SHORT_HAND_OBFUSCATION = 'Shorthand obfuscation',
40  REMOVE_CONSOLE = 'Remove console',
41  PROPERTY_OBFUSCATION = 'Property obfuscation',
42  IDENTIFIER_OBFUSCATION = 'Identifier obfuscation',
43  CREATE_CHECKER = 'Create checker',
44  CREATE_PROGRAM = 'Create program',
45  GET_CHECKER = 'Get checker',
46  SCOPE_ANALYZE = 'Scope analyze',
47  CREATE_OBFUSCATED_NAMES = 'Create obfuscated names',
48  OBFUSCATE_NODES = 'Obfuscate nodes',
49  FILENAME_OBFUSCATION = 'Filename obfuscation',
50  CREATE_PRINTER = 'Create Printer',
51  GET_SOURCEMAP_GENERATOR = 'Get sourcemap generator',
52  SOURCEMAP_MERGE = 'Sourcemap merge',
53  CREATE_NAMECACHE = 'Create namecache',
54  WRITE_FILE = 'Write file',
55  RESOLVE_OBFUSCATION_CONFIGS = 'Resolve obfuscation configs',
56  CONFIG_INITIALIZATION = 'Config initialization',
57  WRITE_OBFUSCATION_RESULT = 'Write obfuscation result',
58  GET_OBFUSCATED_CODE = 'Get obfuscated code',
59  PROCESS_SOURCEMAP = 'Process sourcemap',
60  GEN_CONSUMER_CONFIG = 'Gen consumer config',
61  FILENAME_OBFUSCATION_INITIALIZATION = 'Filename obfuscation initialization',
62  UPDATE_PARENT_NODE = 'Update parent node',
63  UPDATE_IMPORT_OR_EXPORT = 'Update import or export',
64  UPDATE_DYNAMIC_IMPORT = 'Update dynamic import',
65  INIT_WHITELIST = 'Init whitelist',
66  RENAME = 'Rename',
67  HANDLE_POSITION_INFO = 'Handle position info',
68  UPDATE_NAME_NODE = 'Update name node',
69  RENAME_PROPERTIES = 'Rename properties',
70  UPDATE_MEMBER_METHOD_NAME = 'Update member method name',
71  SET_PARENT_RECURSIVE = 'Set parent recursive',
72  TRANSFORM_SHORT_HAND_PROPERTY = 'Transform short hand property',
73  REMOVE_VIRTUAL_CONSTRUCTOR = 'Remove virtual constructor',
74  ANALYZE_SOURCE_FILE = 'Analyze sourceFile',
75  ANALYZE_FUNCTION_LIKE = 'Analyze function like',
76  ANALYZE_CLASS_LIKE = 'Analyze class like',
77  RENAME_IDENTIFIERS = 'Rename identifiers',
78  VISIT_PROPERTY_PARAMETER = 'Visit property parameter',
79  GET_SYSTEM_API_CONFIGS_BY_CACHE = 'Get system api configs by cache',
80}
81
82// Time and memory performance data interface
83export interface TimeAndMemInfo {
84  start: number;
85  startMemory: number;
86  endMemory: number;
87  pid: number; // Process ID
88}
89
90// Time performance data interface
91export interface TimeInfo {
92  name: string;
93  ph: string; // Type of event
94  pid: number; // Process ID
95  tid: number; // Thread ID
96  ts: number; // Start time
97  dur: number; // Duration
98}
99
100// Memory performance data interface
101export interface MemInfo {
102  name: string;
103  startMemory: string;
104  endMemory: string;
105}
106
107const MILLISECOND_TO_MICROSECOND = 1000; // Millisecond to microsecond
108const BYTE_TO_MB = 1024 * 1024; // Byte to MB
109const SIG_FIGS = 3; // Significant figures
110const TID = 1; // single-threaded
111const END_MEMORY_INITIALIZE = 0; // Initialize the memory to zero and terminate the process
112const EVENT_STACK_EMPTY_LENGTH = 0; // The length of the eventStack when it is empty
113const PH = 'X'; // Type of event
114const INDENTATION_SPACES = 2; // Number of indentation spaces
115const TIME_PERFORMANCE_FILE_NAME = 'timePerformanceData.json'; // Time performance data file name
116const MEMORY_PERFORMANCE_FILE_NAME = 'memoryPerformanceData.json'; // Memory performance data file name
117
118export const eventListTimeAndMemValues = new Set<string>(Object.values(EventList));
119
120export class TimeAndMemTimeTracker {
121  static obfuscationCacheDir: string | undefined = ''; // Obtain the directory of the obfuscated output file
122  private eventStack: Array<{ eventName: string; data: TimeAndMemInfo }> = [];
123  timeDataArr: TimeInfo[] = [];
124  memoryDataArr: MemInfo[] = [];
125
126  startEvent(eventName: string): void {
127    this.eventStack.push({
128      eventName: eventName,
129      data: {
130        start: performance.now(),
131        startMemory: process.memoryUsage().heapUsed,
132        endMemory: END_MEMORY_INITIALIZE,
133        pid: process.pid,
134      },
135    });
136  }
137
138  endEvent(eventName: string): void {
139    if (this.eventStack.length === EVENT_STACK_EMPTY_LENGTH) {
140      throw new Error(`Event stack is empty`);
141    }
142
143    const lastEvent = this.eventStack.pop();
144
145    if (
146      !eventListTimeAndMemValues.has(lastEvent.eventName) ||
147      lastEvent.eventName === EventList.OBFUSCATION_INITIALIZATION ||
148      lastEvent.eventName === EventList.SCAN_SOURCEFILES
149    ) {
150      const startMemory = lastEvent.data.startMemory / BYTE_TO_MB;
151      const endMemory = process.memoryUsage().heapUsed / BYTE_TO_MB;
152      this.memoryDataArr.push(this.formatMemoryPerformanceData(lastEvent.eventName, startMemory, endMemory));
153    }
154
155    if (eventListTimeAndMemValues.has(lastEvent.eventName)) {
156      const timeDuration = performance.now() - lastEvent.data.start;
157      this.timeDataArr.push(
158        this.formatTimePerformanceData(eventName, lastEvent.data.pid, TID, lastEvent.data.start, timeDuration),
159      );
160    }
161  }
162
163  /**
164   * Only for ut
165   */
166  getEventStack(): Array<{ eventName: string; data: TimeAndMemInfo }> {
167    return this.eventStack;
168  }
169
170  private formatTimePerformanceData(
171    eventName: string,
172    pid: number,
173    tid: number,
174    startTime: number,
175    duration: number,
176  ): TimeInfo {
177    const formatttedTs = startTime * MILLISECOND_TO_MICROSECOND;
178    const formatttedDur = duration * MILLISECOND_TO_MICROSECOND;
179    return {
180      name: eventName,
181      ph: PH,
182      pid: pid,
183      tid: tid,
184      ts: formatttedTs,
185      dur: formatttedDur,
186    };
187  }
188
189  private formatMemoryPerformanceData(eventName: string, startMemory: number, endMemory: number): MemInfo {
190    const formatttedName = path.basename(eventName);
191    const formatttedStartMemory = startMemory.toFixed(SIG_FIGS) + 'MB';
192    const formatttedEndMemory = endMemory.toFixed(SIG_FIGS) + 'MB';
193    return {
194      name: formatttedName,
195      startMemory: formatttedStartMemory,
196      endMemory: formatttedEndMemory,
197    };
198  }
199}
200
201/**
202 * Initialize performance printer
203 */
204export function initPerformanceTimeAndMemPrinter(): void {
205  if (!printerTimeAndMemDataConfig.mTimeAndMemPrinter) {
206    blockTimeAndMemPrinter();
207  }
208}
209
210/**
211 * Configures the performance printer based on the specified performance mode
212 */
213export function configurePerformancePrinter(perf: number): void {
214  if (perf === PerfMode.ADVANCED) {
215    initPrinterTimeAndMemConfig();
216
217    // If singleFilePrinter and filesPrinter do not exist, create the corresponding instance
218    if (!performanceTimeAndMemPrinter.singleFilePrinter) {
219      performanceTimeAndMemPrinter.singleFilePrinter = new TimeAndMemTimeTracker();
220    }
221    if (!performanceTimeAndMemPrinter.filesPrinter) {
222      performanceTimeAndMemPrinter.filesPrinter = new TimeAndMemTimeTracker();
223    }
224  } else {
225    blockTimeAndMemPrinter();
226  }
227}
228
229/**
230 * Disable performance printer
231 */
232export function blockTimeAndMemPrinter(): void {
233  disablePrinterTimeAndMemConfig();
234  performanceTimeAndMemPrinter.filesPrinter = undefined;
235  performanceTimeAndMemPrinter.singleFilePrinter = undefined;
236}
237
238/**
239 * Clear Time&Mem performance printer
240 */
241export function clearTimeAndMemPrinterData(): void {
242  if (performanceTimeAndMemPrinter.singleFilePrinter) {
243    performanceTimeAndMemPrinter.singleFilePrinter.timeDataArr = [];
244    performanceTimeAndMemPrinter.singleFilePrinter.memoryDataArr = [];
245  }
246  if (performanceTimeAndMemPrinter.filesPrinter) {
247    performanceTimeAndMemPrinter.filesPrinter.timeDataArr = [];
248    performanceTimeAndMemPrinter.filesPrinter.memoryDataArr = [];
249  }
250}
251
252/**
253 * Initialize the configuration of the TimeAndMem performance printer
254 */
255export function initPrinterTimeAndMemConfig(): void {
256  printerTimeAndMemDataConfig.mTimeAndMemPrinter = true;
257}
258
259/**
260 * Disable the configuration of the TimeAndMem performance printer
261 */
262export function disablePrinterTimeAndMemConfig(): void {
263  printerTimeAndMemDataConfig.mTimeAndMemPrinter = false;
264}
265
266/**
267 * Start recording singleFileForMoreTimePrinter event
268 */
269export function startSingleFileForMoreTimeEvent(eventName: string): void {
270  if (printerTimeAndMemDataConfig.mMoreTimePrint) {
271    performanceTimeAndMemPrinter.singleFilePrinter?.startEvent(eventName);
272  }
273}
274
275/**
276 * End recording singleFileForMoreTimePrinter event
277 */
278export function endSingleFileForMoreTimeEvent(eventName: string): void {
279  if (printerTimeAndMemDataConfig.mMoreTimePrint) {
280    performanceTimeAndMemPrinter.singleFilePrinter?.endEvent(eventName);
281  }
282}
283
284/**
285 * Enable timeAndMemoryPrint
286 */
287export function enableTimeAndMemoryPrint(): void {
288  if (printerTimeAndMemDataConfig.mTimeAndMemPrinter) {
289    printTimeAndMemory();
290  }
291}
292
293/**
294 * Print data of timeAndMemory
295 */
296export function printTimeAndMemory(): void {
297  printTimePerformanceData();
298  printMemoryPerformanceData();
299}
300
301/**
302 * Print data of timePerformance
303 */
304export function printTimePerformanceData(): void {
305  writeTimeAndMemoryPerformanceData(getTimePerformanceData(), TIME_PERFORMANCE_FILE_NAME);
306}
307
308/**
309 * Print data of memoryPerformance
310 */
311export function printMemoryPerformanceData(): void {
312  writeTimeAndMemoryPerformanceData(getMemoryPerformanceData(), MEMORY_PERFORMANCE_FILE_NAME);
313}
314
315/**
316 * Write data of timeAndMemoryPerformance
317 */
318export async function writeTimeAndMemoryPerformanceData(data: (TimeInfo | MemInfo)[], fileName: string): Promise<void> {
319  if (TimeAndMemTimeTracker.obfuscationCacheDir) {
320    const filePath: string = path.join(TimeAndMemTimeTracker.obfuscationCacheDir, fileName);
321    try {
322      await fs.promises.writeFile(filePath, JSON.stringify(data, null, INDENTATION_SPACES));
323    } catch (error) {
324      throw new Error('Failed to write file: ' + error.message.toString());
325    }
326  }
327}
328
329/**
330 * Get data of obfuscationCacheDir
331 */
332export function getObfuscationCacheDir(projectConfig: any): void {
333  if (projectConfig) {
334    TimeAndMemTimeTracker.obfuscationCacheDir = projectConfig.obfuscationOptions?.obfuscationCacheDir;
335  }
336}
337
338/**
339 * Get data of timePerformance
340 */
341export function getTimePerformanceData(): TimeInfo[] {
342  const singleFilePrinterTime: TimeInfo[] = performanceTimeAndMemPrinter.singleFilePrinter?.timeDataArr || [];
343  const filesPrinterTime: TimeInfo[] = performanceTimeAndMemPrinter.filesPrinter?.timeDataArr || [];
344  return filesPrinterTime.concat(singleFilePrinterTime);
345}
346
347/**
348 * Print data of memoryPerformance
349 */
350export function getMemoryPerformanceData(): MemInfo[] {
351  const filesPrinterMemory: MemInfo[] = performanceTimeAndMemPrinter.filesPrinter?.memoryDataArr || [];
352  const singleFilePrinterMemory: MemInfo[] = performanceTimeAndMemPrinter.singleFilePrinter?.memoryDataArr || [];
353  return filesPrinterMemory.concat(singleFilePrinterMemory);
354}
355