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 */ 15import { ReadonlyESMap, Map } from "./_namespaces/ts"; 16export namespace PerformanceDotting { 17 const eventStack: string[] = []; // record the event 18 const timeStack: string[] = []; // sort the relation of Parent-Child time 19 let tempMap = new Map<string, Array<PerformanceData>>; // deal with the template performance dotting 20 let recordMap = new Map<string, Array<PerformanceData>>; // record the performance dotting 21 let timeHooks = require('perf_hooks'); // the method of processing time 22 let count: number = 0; 23 const layerCount = 5; // limit the layer number 24 let advancedSwitch = false; // the advanced switch 25 let traceSwitch = false; // the trace switch 26 const TIME_CONVERSION: number = 1000000; // the conversion of time 27 28 // Accordding to this parameter based on the enumeration sequence of switches set in the hvigor. 29 export enum AnalyzeMode { 30 DEFAULT = 0, 31 VERBOSE = 1, 32 TRACE = 3, 33 } 34 35 interface PerformanceData { 36 startTime: number; 37 endTime: number; 38 duration: number; 39 name: string; 40 parentEvent: string; 41 tier: number; 42 fullPath: string; 43 id: string; 44 parentId: string; 45 fileName: string; 46 } 47 48 export function setPerformanceSwitch(projectConfigPerf: AnalyzeMode): void { 49 if (projectConfigPerf === AnalyzeMode.VERBOSE) { 50 advancedSwitch = true; 51 } 52 if (projectConfigPerf === AnalyzeMode.TRACE) { 53 advancedSwitch = true; 54 traceSwitch = true; 55 } 56 } 57 58 function processSharedStart(eventName: string, fileName: string): void { 59 if (++count > layerCount) { 60 return; 61 } 62 63 const currentEvent = eventName; 64 const currentParentEvent = eventStack.length > 0 ? eventStack[eventStack.length - 1] : ''; 65 const currentParentId = timeStack.length > 0 ? timeStack[timeStack.length - 1] : ''; 66 const fullPath = eventStack.length > 0 ? `${eventStack.join('-')}-${eventName}` : eventName; 67 let startTime = timeHooks.performance.now(); 68 let id = startTime.toString(); 69 const recordCurrentData: PerformanceData = { 70 startTime: startTime, 71 endTime: -1, 72 duration: 0, 73 name: currentEvent, 74 parentEvent: currentParentEvent, 75 tier: count, 76 fullPath: fullPath, 77 id: id, 78 parentId: currentParentId, 79 fileName: fileName 80 }; 81 82 addOrUpdateEventData(tempMap, eventName, recordCurrentData); 83 eventStack.push(eventName); 84 timeStack.push(id); 85 } 86 87 function processSharedStop(eventName: string): void { 88 if (count-- > layerCount) { 89 return; 90 } 91 92 if (eventStack.length === 0 || eventStack[eventStack.length - 1] !== eventName) { 93 throw new Error(`Event ${eventName} is not the current active event`); 94 } 95 96 const records = tempMap.get(eventName); 97 if (records && records.length > 0) { 98 const lastRecord = records.pop()!; 99 lastRecord.endTime = timeHooks.performance.now(); 100 lastRecord.duration = lastRecord.endTime - lastRecord.startTime; 101 addOrUpdateEventData(recordMap, eventName, lastRecord); 102 } else { 103 throw new Error(`No active record found for event ${eventName}`); 104 } 105 106 // remove the current event 107 eventStack.pop(); 108 timeStack.pop(); 109 } 110 111 export function startAdvanced(eventName: string, fileName: string = ''): void { 112 if (!advancedSwitch) { 113 return; 114 } 115 processSharedStart(eventName, fileName); 116 } 117 118 export function stopAdvanced(eventName: string): void { 119 if (!advancedSwitch) { 120 return; 121 } 122 processSharedStop(eventName); 123 } 124 125 export function start(eventName: string, fileName: string = ''): void { 126 if (!traceSwitch) { 127 return; 128 } 129 processSharedStart(eventName, fileName); 130 } 131 132 function addOrUpdateEventData(map: Map<Array<PerformanceData>>, eventName: string, data: PerformanceData): void { 133 let records = map.get(eventName); 134 if (!records) { 135 records = []; 136 map.set(eventName, records); 137 } 138 records.push(data); 139 } 140 141 export function stop(eventName: string): void { 142 if (!traceSwitch) { 143 return; 144 } 145 processSharedStop(eventName); 146 } 147 148 export function getEventData(): Array<PerformanceData> { 149 const mergedRecordsMap: Map<PerformanceData> = new Map(); 150 151 // traverse all records 152 recordMap.forEach((records) => { 153 records.forEach((record) => { 154 // generate unique key 155 const key = `${record.fullPath}-${record.fileName}-${record.parentId}`; 156 157 // check whether the system already exists 158 if (mergedRecordsMap.has(key)) { 159 const existingRecord = mergedRecordsMap.get(key)!; 160 // add duration 161 existingRecord.duration += record.duration; 162 // update the smaller value of startTime 163 existingRecord.startTime = Math.min(existingRecord.startTime, record.startTime); 164 existingRecord.endTime = Math.max(existingRecord.endTime, record.endTime); 165 } else { 166 // add new record, and remove tier 167 mergedRecordsMap.set(key, record); 168 } 169 }); 170 }); 171 172 let mergedRecords: Array<PerformanceData> = Array.from(mapValuesToArray(mergedRecordsMap)); 173 mergedRecords = mergedRecords.map(record => ({ 174 ...record, 175 duration: record.duration * TIME_CONVERSION 176 })); 177 clearEvent(); 178 return mergedRecords; 179 } 180 181 function mapValuesToArray<K, V>(map: ReadonlyESMap<K, V>): V[] { 182 const result: V[] = []; 183 const iterator = map.values(); 184 185 let item: IteratorResult<V>; 186 while (!(item = iterator.next()).done) { 187 result.push(item.value); 188 } 189 return result; 190 } 191 192 export function clearEvent(): void { 193 recordMap.clear(); 194 tempMap.clear(); 195 eventStack.length = 0; 196 timeStack.length = 0; 197 count = 0; 198 advancedSwitch = false; 199 traceSwitch = false; 200 } 201}