1"use strict"; 2/* 3 * Copyright (c) 2022-2025 Huawei Device Co., Ltd. 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16Object.defineProperty(exports, "__esModule", { value: true }); 17exports.getMainPerfProbe = exports.enterMainPerfProbe = void 0; 18const compat_1 = require("#koalaui/compat"); 19/** 20 * Creates a {@link MainPerfProbe} instance with the {@link name} and enters its main probe. 21 * 22 * If a {@link MainPerfProbe} with this {@link name} already exists then it is canceled and the new one is created. 23 * 24 * Exit it with {@link MainPerfProbe.exit}. 25 */ 26function enterMainPerfProbe(name) { 27 return new MainPerfProbeImpl(name); 28} 29exports.enterMainPerfProbe = enterMainPerfProbe; 30/** 31 * Returns {@link MainPerfProbe} instance with the {@link name} if it exists, 32 * otherwise a dummy instance. 33 * 34 * @see MainPerfProbe.dummy 35 */ 36function getMainPerfProbe(name) { 37 const instance = MainPerfProbeImpl.mainProbes.get(name); 38 return instance ? instance : MainPerfProbeImpl.DUMMY; 39} 40exports.getMainPerfProbe = getMainPerfProbe; 41class DummyPerfProbe { 42 get name() { return "dummy"; } 43 get dummy() { return true; } 44 exit(log) { } 45 cancel() { } 46 get canceled() { return false; } 47 enterProbe(name) { return PerfProbeImpl.DUMMY; } 48 exitProbe(name) { return PerfProbeImpl.DUMMY; } 49 getProbe(name) { return PerfProbeImpl.DUMMY; } 50 //performProbe: <T>(_: string, func: () => T) => func(), 51 performProbe(name, func) { return func(); } 52 probePerformed(_) { return false; } 53 get userData() { 54 return undefined; 55 } 56 set userData(_) { } 57} 58class PerfProbeImpl { 59 constructor(name, main, parent, remainder = false) { 60 this.children = new Array(); 61 this.childrenSorted = false; 62 this.index = 0; 63 this.startTime = 0.0; 64 this.totalTime = 0.0; 65 this.callCount = 0; 66 this.currentRecursionDepth = 0; 67 this.recursiveCallCount = 0; 68 this._canceled = false; 69 this._name = name; 70 this._main = main; 71 this.parent = parent; 72 this.remainder = remainder; 73 } 74 get name() { 75 return this._name; 76 } 77 get dummy() { 78 return false; 79 } 80 get main() { 81 return this._main; 82 } 83 get performing() { 84 return this.startTime > 0; 85 } 86 get userData() { 87 return this._userData; 88 } 89 set userData(value) { 90 this._userData = value; 91 } 92 exit(log) { 93 if (this.canceled) 94 return; 95 if (this.currentRecursionDepth == 0) { 96 this.totalTime += (0, compat_1.timeNow)() - this.startTime; 97 this.startTime = 0; 98 } 99 else { 100 this.currentRecursionDepth--; 101 } 102 if (!this.performing) { 103 this.main.pop(this); 104 } 105 if (log) 106 this.log(); 107 } 108 cancel(cancelChildren = true) { 109 this._canceled = true; 110 if (cancelChildren) 111 this.maybeCancelChildren(); 112 } 113 maybeCancelChildren() { 114 MainPerfProbeImpl.visit(this, false, (probe, depth) => { 115 if (probe.performing) 116 probe.cancel(false); 117 }); 118 } 119 get canceled() { 120 return this._canceled; 121 } 122 toString() { 123 if (this.canceled) { 124 return `[${this.name}] canceled`; 125 } 126 if (this.performing) { 127 return `[${this.name}] still performing...`; 128 } 129 const mainProbe = this.main.probes.get(this.main.name); 130 const percentage = mainProbe.totalTime > 0 ? Math.round((100 / mainProbe.totalTime) * this.totalTime) : 0; 131 let result = `[${this.name}] call count: ${this.callCount}` 132 + ` | recursive call count: ${this.recursiveCallCount}` 133 + ` | time: ${this.totalTime}ms ${percentage}%`; 134 if (this.userData) { 135 result += ` | user data: ${this.userData}`; 136 } 137 return result; 138 } 139 log(prefix) { 140 console.log(prefix ? `${prefix}${this.toString()}` : this.toString()); 141 } 142} 143PerfProbeImpl.DUMMY = new DummyPerfProbe(); 144class MainPerfProbeImpl extends PerfProbeImpl { 145 constructor(name) { 146 super(name); 147 this.probes = new Map(); 148 const prev = MainPerfProbeImpl.mainProbes.get(name); 149 if (prev) 150 prev.cancel(); 151 MainPerfProbeImpl.mainProbes.set(name, this); 152 this.currentProbe = this.enterProbe(name); 153 } 154 createProbe(name) { 155 const probes = name == this.name ? this : new PerfProbeImpl(name, this); 156 this.push(probes); 157 return probes; 158 } 159 get main() { 160 return this; 161 } 162 push(probe) { 163 probe.parent = this.currentProbe; 164 probe.index = probe.parent ? probe.parent.children.length : 0; 165 if (probe.parent) 166 probe.parent.children.push(probe); 167 this.currentProbe = probe; 168 } 169 pop(probe) { 170 if (probe.parent) { 171 this.currentProbe = probe.parent; 172 } 173 } 174 performProbe(name, func) { 175 const probe = this.enterProbe(name); 176 try { 177 return func(); 178 } 179 finally { 180 probe.exit(); 181 } 182 } 183 enterProbe(name) { 184 let probe = this.probes.get(name); 185 if (!probe) { 186 probe = this.createProbe(name); 187 this.probes.set(name, probe); 188 } 189 probe._canceled = false; 190 if (probe.performing) { 191 probe.recursiveCallCount++; 192 probe.currentRecursionDepth++; 193 } 194 else { 195 probe.startTime = (0, compat_1.timeNow)(); 196 probe.callCount++; 197 } 198 return probe; 199 } 200 exitProbe(name) { 201 const probe = this.getProbe(name); 202 probe.exit(undefined); 203 return probe; 204 } 205 getProbe(name) { 206 const probe = this.probes.get(name); 207 return probe !== undefined ? probe : PerfProbeImpl.DUMMY; 208 } 209 probePerformed(name) { 210 const probe = this.probes.get(name); 211 return probe != undefined && !probe.performing && !probe.canceled; 212 } 213 exit(log) { 214 super.exit(); 215 if (log) 216 this.log(); 217 this.cancel(); 218 } 219 cancel() { 220 MainPerfProbeImpl.mainProbes.delete(this.name); 221 } 222 static visit(root, logging, apply) { 223 let current = root; 224 let index = 0; 225 let depth = 0; 226 let visiting = true; 227 while (true) { 228 if (visiting) { 229 current.index = 0; 230 apply(current, depth); 231 } 232 if (index >= current.children.length) { 233 if (!current.parent) { 234 break; 235 } 236 current = current.parent; 237 index = ++current.index; 238 depth--; 239 visiting = false; 240 continue; 241 } 242 visiting = true; 243 if (logging && !current.childrenSorted) { 244 current.childrenSorted = true; 245 current.children = current.children.sort((p1, p2) => p2.totalTime - p1.totalTime); 246 if (depth == 0) { 247 // a special probe to log the time remained 248 current.children.push(new PerfProbeImpl("<remainder>", root.main, current, true)); 249 } 250 } 251 current = current.children[index]; 252 index = 0; 253 depth++; 254 } 255 } 256 log(prefix) { 257 prefix = prefix !== undefined ? `${prefix}: ` : ""; 258 console.log(`${prefix}perf probes:`); 259 MainPerfProbeImpl.visit(this.main, true, (probe, depth) => { 260 let indent = "\t"; 261 for (let i = 0; i < depth; i++) 262 indent += "\t"; 263 if (probe.remainder) { 264 const parentTime = probe.parent.totalTime; 265 let childrenTime = 0; 266 probe.parent.children.forEach((a) => { childrenTime += a.totalTime; }); 267 probe.totalTime = Math.max(0, parentTime - childrenTime); 268 const percentage = parentTime > 0 ? Math.round((100 / parentTime) * probe.totalTime) : 0; 269 console.log(`${prefix}${indent}[${probe.name}] time: ${(0, compat_1.numberToFixed)(probe.totalTime, 2)}ms ${percentage}%`); 270 } 271 else { 272 console.log(`${prefix}${indent}${probe.toString()}`); 273 } 274 }); 275 } 276} 277MainPerfProbeImpl.mainProbes = new Map(); 278MainPerfProbeImpl.DUMMY = new DummyPerfProbe(); 279//# sourceMappingURL=PerfProbe.js.map