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