1/* 2 * Copyright (c) 2024 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'; 17 18export enum EventList { 19 OBFUSCATION_INITIALIZATION = 'Obfuscation initialization', 20 ALL_FILES_OBFUSCATION = 'All files obfuscation', 21 CREATE_AST = 'Create AST', 22 OBFUSCATE_AST = 'Obfuscate AST', 23 VIRTUAL_CONSTRUCTOR_OBFUSCATION = 'Virtual constructor obfuscation', 24 SHORT_HAND_OBFUSCATION = 'Shorthand obfuscation', 25 REMOVE_CONSOLE = 'Remove console', 26 PROPERTY_OBFUSCATION = 'Property obfuscation', 27 IDENTIFIER_OBFUSCATION = 'Identifier obfuscation', 28 CREATE_CHECKER = 'Create checker', 29 SCOPE_ANALYZE = 'Scope analyze', 30 CREATE_OBFUSCATED_NAMES = 'Create obfuscated names', 31 OBFUSCATE_NODES = 'Obfuscate nodes', 32 FILENAME_OBFUSCATION = 'Filename obfuscation', 33 CREATE_PRINTER = 'Create Printer' 34} 35 36export enum EventIndentation { 37 TWOSPACE = 2, 38 ONESPACE = 1, 39 NOSPACE = 0, 40}; 41 42export const eventList = new Map<string, number>([ 43 [EventList.OBFUSCATION_INITIALIZATION, EventIndentation.NOSPACE], 44 [EventList.ALL_FILES_OBFUSCATION, EventIndentation.NOSPACE], 45 [EventList.CREATE_AST, EventIndentation.ONESPACE], 46 [EventList.OBFUSCATE_AST, EventIndentation.ONESPACE], 47 [EventList.VIRTUAL_CONSTRUCTOR_OBFUSCATION, EventIndentation.TWOSPACE], 48 [EventList.SHORT_HAND_OBFUSCATION, EventIndentation.TWOSPACE], 49 [EventList.REMOVE_CONSOLE, EventIndentation.TWOSPACE], 50 [EventList.PROPERTY_OBFUSCATION, EventIndentation.TWOSPACE], 51 [EventList.IDENTIFIER_OBFUSCATION, EventIndentation.TWOSPACE], 52 [EventList.CREATE_CHECKER, EventIndentation.TWOSPACE], 53 [EventList.SCOPE_ANALYZE, EventIndentation.TWOSPACE], 54 [EventList.CREATE_OBFUSCATED_NAMES, EventIndentation.TWOSPACE], 55 [EventList.OBFUSCATE_NODES, EventIndentation.TWOSPACE], 56 [EventList.FILENAME_OBFUSCATION, EventIndentation.TWOSPACE], 57 [EventList.CREATE_PRINTER, EventIndentation.ONESPACE], 58]); 59 60export interface TimeAndMemInfo { 61 start: number; 62 duration: number; 63 startMemory: number; 64 endMemory: number; 65 memoryUsage: number; 66} 67 68const MILLISECOND_TO_SECOND = 1000; 69const BYTE_TO_MB = 1024 * 1024; 70const SIG_FIGS = 3;// 有效数字位数 71const INDENT = ' '; 72 73abstract class BasePrinter { 74 protected outputPath: string | undefined; 75 76 protected abstract getCurrentEventData(): string; 77 78 constructor(outputPath: string = "") { 79 this.outputPath = outputPath; 80 } 81 82 setOutputPath(outputPath: string | undefined) { 83 this.outputPath = outputPath; 84 } 85 86 print(message: string): void { 87 if (this.outputPath && this.outputPath !== "") { 88 fs.appendFileSync(`${this.outputPath}`, message + "\n"); 89 } else { 90 console.log(message); 91 } 92 } 93 94 outputData(): void { 95 const eventData = this.getCurrentEventData(); 96 this.print(eventData); 97 } 98 99 // Only used for ut 100 getOutputPath(): string { 101 return this.outputPath; 102 } 103} 104 105export class TimeTracker extends BasePrinter { 106 private eventStack: Map<string, TimeAndMemInfo> = new Map<string, TimeAndMemInfo>(); 107 private filesTimeSum: number = 0; 108 private maxTimeUsage = 0; 109 private maxTimeFile = ""; 110 private maxMemoryUsage: number = 0; 111 private maxMemoryFile: string = ''; 112 113 startEvent(eventName: string, timeSumPrinter?: TimeSumPrinter, currentFile?: string): void { 114 this.eventStack.set(eventName, {start: Date.now(), duration: 0, startMemory: process.memoryUsage().heapUsed, 115 endMemory: 0, memoryUsage: 0}); 116 timeSumPrinter?.addEventDuration(eventName, 0); 117 if (eventName === EventList.CREATE_AST) { 118 this.print(currentFile); 119 } 120 } 121 122 endEvent(eventName: string, timeSumPrinter?: TimeSumPrinter, isFilesPrinter?: boolean): void { 123 if (!this.eventStack.get(eventName)) { 124 throw new Error(`Event "${eventName}" not started`); 125 } 126 127 const eventStartTime = this.eventStack.get(eventName).start; 128 const duration = (Date.now() - eventStartTime)/MILLISECOND_TO_SECOND; 129 const eventEndMemory = process.memoryUsage().heapUsed; 130 const eventStartMemory = this.eventStack.get(eventName).startMemory; 131 const memoryUsage = eventEndMemory - eventStartMemory; 132 133 if (isFilesPrinter) { 134 this.filesTimeSum += duration; 135 136 if (duration > this.maxTimeUsage) { 137 this.maxTimeUsage = duration; 138 this.maxTimeFile = eventName; 139 } 140 141 if (eventStartMemory > this.maxMemoryUsage) { 142 this.maxMemoryUsage = eventStartMemory; 143 this.maxMemoryFile = eventName; 144 } 145 146 if (eventEndMemory > this.maxMemoryUsage) { 147 this.maxMemoryUsage = eventEndMemory; 148 this.maxMemoryFile = eventName; 149 } 150 } 151 152 this.eventStack.get(eventName).duration = duration; 153 this.eventStack.get(eventName).endMemory = eventEndMemory; 154 this.eventStack.get(eventName).memoryUsage = memoryUsage; 155 156 timeSumPrinter?.addEventDuration(eventName, duration); 157 158 if ((eventName === EventList.ALL_FILES_OBFUSCATION)) { 159 this.eventStack.get(eventName).duration = this.filesTimeSum; 160 this.outputData(); 161 const maxTimeUsage = this.maxTimeUsage.toFixed(SIG_FIGS); 162 const maxMemoryUsage = (this.maxMemoryUsage/BYTE_TO_MB).toFixed(SIG_FIGS); 163 this.print(`Max time cost: ${this.maxTimeFile}: ${maxTimeUsage}s`) 164 this.print(`Max memory usage: ${this.maxMemoryFile}: ${maxMemoryUsage}MB\n`) 165 } 166 167 if ((eventName === EventList.CREATE_PRINTER)) { 168 this.outputData(); 169 } 170 } 171 172 getCurrentEventData(): string { 173 let eventData = ""; 174 for (const eventName of this.eventStack.keys()) { 175 let depth = eventList.get(eventName)?? 0; 176 let eventInfo = this.eventStack.get(eventName); 177 const duration = eventInfo.duration; 178 const startMemory = eventInfo.startMemory/BYTE_TO_MB; 179 const endMemory = eventInfo.endMemory/BYTE_TO_MB; 180 const memoryUsage = eventInfo.memoryUsage/BYTE_TO_MB; 181 eventData += this.formatEvent(eventName, duration, startMemory, endMemory, memoryUsage, depth); 182 } 183 return eventData; 184 } 185 186 private formatEvent(eventName: string, duration: number, startMemory: number, endMemory: number, 187 memoryUsage: number, depth: number): string { 188 const indent = INDENT.repeat(depth); 189 const formattedDuration = duration.toFixed(SIG_FIGS) + 's'; 190 const formatttedStartMemory = startMemory.toFixed(SIG_FIGS) + 'MB'; 191 const formatttedEndMemory = endMemory.toFixed(SIG_FIGS) + 'MB'; 192 const formatttedMemoryUsage = memoryUsage.toFixed(SIG_FIGS) + 'MB'; 193 return `${indent}${eventName}: timeCost:${formattedDuration} startMemory:${formatttedStartMemory} `+ 194 `endMemory:${formatttedEndMemory} memoryUsage:${formatttedMemoryUsage}\n`; 195 } 196 197 // Only used for ut 198 getEventStack(): Map<string, TimeAndMemInfo> { 199 return this.eventStack; 200 } 201 202 getFilesTimeSum(): number { 203 return this.filesTimeSum; 204 } 205 206 getMaxTimeUsage(): number { 207 return this.maxTimeUsage; 208 } 209 210 getMaxTimeFile(): string { 211 return this.maxTimeFile; 212 } 213 214 getMaxMemoryUsage(): number { 215 return this.maxMemoryUsage; 216 } 217 218 getMaxMemoryFile(): string { 219 return this.maxMemoryFile; 220 } 221} 222 223export class TimeSumPrinter extends BasePrinter { 224 private eventSum: Map<string, number> = new Map<string, number>(); 225 addEventDuration(eventName: string, duration: number): void { 226 const currentValue = this.eventSum.get(eventName) ?? 0; 227 this.eventSum.set(eventName, currentValue + duration); 228 } 229 230 summarizeEventDuration(): void { 231 const eventData = this.getCurrentEventData(); 232 this.print(eventData); 233 } 234 235 getCurrentEventData(): string { 236 let eventData = ""; 237 for (const eventName of this.eventSum.keys()) { 238 let depth = eventList.get(eventName)?? 0; 239 const duration = this.eventSum.get(eventName); 240 eventData += this.formatEvent(eventName, duration, depth); 241 } 242 return eventData; 243 } 244 245 private formatEvent(eventName: string, duration: number, depth: number): string { 246 const indent = INDENT.repeat(depth); 247 const formattedDuration = duration.toFixed(SIG_FIGS) + 's'; 248 return `${indent}${eventName}: ${formattedDuration}\n`; 249 } 250 251 getEventSum(): Map<string, number> { 252 return this.eventSum; 253 } 254}