1'use strict'; 2 3// This files contains process bootstrappers that can be 4// run when setting up each thread, including the main 5// thread and the worker threads. 6 7const { 8 ArrayIsArray, 9 BigUint64Array, 10 NumberMAX_SAFE_INTEGER, 11 ObjectDefineProperties, 12 ObjectDefineProperty, 13 ObjectFreeze, 14 ObjectGetOwnPropertyDescriptors, 15 RegExpPrototypeTest, 16 Set, 17 SetPrototype, 18 SetPrototypeHas, 19 StringPrototypeReplace, 20 Uint32Array, 21} = primordials; 22 23const { 24 errnoException, 25 codes: { 26 ERR_ASSERTION, 27 ERR_CPU_USAGE, 28 ERR_INVALID_ARG_TYPE, 29 ERR_INVALID_OPT_VALUE, 30 ERR_OUT_OF_RANGE, 31 ERR_UNKNOWN_SIGNAL 32 } 33} = require('internal/errors'); 34const format = require('internal/util/inspect').format; 35const constants = internalBinding('constants').os.signals; 36 37function assert(x, msg) { 38 if (!x) throw new ERR_ASSERTION(msg || 'assertion error'); 39} 40 41// The execution of this function itself should not cause any side effects. 42function wrapProcessMethods(binding) { 43 const { 44 hrtime: _hrtime, 45 hrtimeBigInt: _hrtimeBigInt, 46 cpuUsage: _cpuUsage, 47 memoryUsage: _memoryUsage, 48 resourceUsage: _resourceUsage 49 } = binding; 50 51 function _rawDebug(...args) { 52 binding._rawDebug(format.apply(null, args)); 53 } 54 55 // Create the argument array that will be passed to the native function. 56 const cpuValues = new Float64Array(2); 57 58 // Replace the native function with the JS version that calls the native 59 // function. 60 function cpuUsage(prevValue) { 61 // If a previous value was passed in, ensure it has the correct shape. 62 if (prevValue) { 63 if (!previousValueIsValid(prevValue.user)) { 64 if (typeof prevValue !== 'object') 65 throw new ERR_INVALID_ARG_TYPE('prevValue', 'object', prevValue); 66 67 if (typeof prevValue.user !== 'number') { 68 throw new ERR_INVALID_ARG_TYPE('prevValue.user', 69 'number', prevValue.user); 70 } 71 throw new ERR_INVALID_OPT_VALUE.RangeError('prevValue.user', 72 prevValue.user); 73 } 74 75 if (!previousValueIsValid(prevValue.system)) { 76 if (typeof prevValue.system !== 'number') { 77 throw new ERR_INVALID_ARG_TYPE('prevValue.system', 78 'number', prevValue.system); 79 } 80 throw new ERR_INVALID_OPT_VALUE.RangeError('prevValue.system', 81 prevValue.system); 82 } 83 } 84 85 // Call the native function to get the current values. 86 const errmsg = _cpuUsage(cpuValues); 87 if (errmsg) { 88 throw new ERR_CPU_USAGE(errmsg); 89 } 90 91 // If a previous value was passed in, return diff of current from previous. 92 if (prevValue) { 93 return { 94 user: cpuValues[0] - prevValue.user, 95 system: cpuValues[1] - prevValue.system 96 }; 97 } 98 99 // If no previous value passed in, return current value. 100 return { 101 user: cpuValues[0], 102 system: cpuValues[1] 103 }; 104 } 105 106 // Ensure that a previously passed in value is valid. Currently, the native 107 // implementation always returns numbers <= Number.MAX_SAFE_INTEGER. 108 function previousValueIsValid(num) { 109 return typeof num === 'number' && 110 num <= NumberMAX_SAFE_INTEGER && 111 num >= 0; 112 } 113 114 // The 3 entries filled in by the original process.hrtime contains 115 // the upper/lower 32 bits of the second part of the value, 116 // and the remaining nanoseconds of the value. 117 const hrValues = new Uint32Array(3); 118 119 function hrtime(time) { 120 _hrtime(hrValues); 121 122 if (time !== undefined) { 123 if (!ArrayIsArray(time)) { 124 throw new ERR_INVALID_ARG_TYPE('time', 'Array', time); 125 } 126 if (time.length !== 2) { 127 throw new ERR_OUT_OF_RANGE('time', 2, time.length); 128 } 129 130 const sec = (hrValues[0] * 0x100000000 + hrValues[1]) - time[0]; 131 const nsec = hrValues[2] - time[1]; 132 const needsBorrow = nsec < 0; 133 return [needsBorrow ? sec - 1 : sec, needsBorrow ? nsec + 1e9 : nsec]; 134 } 135 136 return [ 137 hrValues[0] * 0x100000000 + hrValues[1], 138 hrValues[2] 139 ]; 140 } 141 142 // Use a BigUint64Array in the closure because this is actually a bit 143 // faster than simply returning a BigInt from C++ in V8 7.1. 144 const hrBigintValues = new BigUint64Array(1); 145 function hrtimeBigInt() { 146 _hrtimeBigInt(hrBigintValues); 147 return hrBigintValues[0]; 148 } 149 150 const memValues = new Float64Array(5); 151 function memoryUsage() { 152 _memoryUsage(memValues); 153 return { 154 rss: memValues[0], 155 heapTotal: memValues[1], 156 heapUsed: memValues[2], 157 external: memValues[3], 158 arrayBuffers: memValues[4] 159 }; 160 } 161 162 function exit(code) { 163 if (code || code === 0) 164 process.exitCode = code; 165 166 if (!process._exiting) { 167 process._exiting = true; 168 process.emit('exit', process.exitCode || 0); 169 } 170 // FIXME(joyeecheung): This is an undocumented API that gets monkey-patched 171 // in the user land. Either document it, or deprecate it in favor of a 172 // better public alternative. 173 process.reallyExit(process.exitCode || 0); 174 } 175 176 function kill(pid, sig) { 177 let err; 178 179 // eslint-disable-next-line eqeqeq 180 if (pid != (pid | 0)) { 181 throw new ERR_INVALID_ARG_TYPE('pid', 'number', pid); 182 } 183 184 // Preserve null signal 185 if (sig === (sig | 0)) { 186 // XXX(joyeecheung): we have to use process._kill here because 187 // it's monkey-patched by tests. 188 err = process._kill(pid, sig); 189 } else { 190 sig = sig || 'SIGTERM'; 191 if (constants[sig]) { 192 err = process._kill(pid, constants[sig]); 193 } else { 194 throw new ERR_UNKNOWN_SIGNAL(sig); 195 } 196 } 197 198 if (err) 199 throw errnoException(err, 'kill'); 200 201 return true; 202 } 203 204 const resourceValues = new Float64Array(16); 205 function resourceUsage() { 206 _resourceUsage(resourceValues); 207 return { 208 userCPUTime: resourceValues[0], 209 systemCPUTime: resourceValues[1], 210 maxRSS: resourceValues[2], 211 sharedMemorySize: resourceValues[3], 212 unsharedDataSize: resourceValues[4], 213 unsharedStackSize: resourceValues[5], 214 minorPageFault: resourceValues[6], 215 majorPageFault: resourceValues[7], 216 swappedOut: resourceValues[8], 217 fsRead: resourceValues[9], 218 fsWrite: resourceValues[10], 219 ipcSent: resourceValues[11], 220 ipcReceived: resourceValues[12], 221 signalsCount: resourceValues[13], 222 voluntaryContextSwitches: resourceValues[14], 223 involuntaryContextSwitches: resourceValues[15] 224 }; 225 } 226 227 228 return { 229 _rawDebug, 230 hrtime, 231 hrtimeBigInt, 232 cpuUsage, 233 resourceUsage, 234 memoryUsage, 235 kill, 236 exit 237 }; 238} 239 240const replaceUnderscoresRegex = /_/g; 241const leadingDashesRegex = /^--?/; 242const trailingValuesRegex = /=.*$/; 243 244// This builds the initial process.allowedNodeEnvironmentFlags 245// from data in the config binding. 246function buildAllowedFlags() { 247 const { 248 envSettings: { kAllowedInEnvironment } 249 } = internalBinding('options'); 250 const { options, aliases } = require('internal/options'); 251 252 const allowedNodeEnvironmentFlags = []; 253 for (const [name, info] of options) { 254 if (info.envVarSettings === kAllowedInEnvironment) { 255 allowedNodeEnvironmentFlags.push(name); 256 } 257 } 258 259 for (const [ from, expansion ] of aliases) { 260 let isAccepted = true; 261 for (const to of expansion) { 262 if (!to.startsWith('-') || to === '--') continue; 263 const recursiveExpansion = aliases.get(to); 264 if (recursiveExpansion) { 265 if (recursiveExpansion[0] === to) 266 recursiveExpansion.splice(0, 1); 267 expansion.push(...recursiveExpansion); 268 continue; 269 } 270 isAccepted = options.get(to).envVarSettings === kAllowedInEnvironment; 271 if (!isAccepted) break; 272 } 273 if (isAccepted) { 274 let canonical = from; 275 if (canonical.endsWith('=')) 276 canonical = canonical.substr(0, canonical.length - 1); 277 if (canonical.endsWith(' <arg>')) 278 canonical = canonical.substr(0, canonical.length - 4); 279 allowedNodeEnvironmentFlags.push(canonical); 280 } 281 } 282 283 const trimLeadingDashes = 284 (flag) => StringPrototypeReplace(flag, leadingDashesRegex, ''); 285 286 // Save these for comparison against flags provided to 287 // process.allowedNodeEnvironmentFlags.has() which lack leading dashes. 288 // Avoid interference w/ user code by flattening `Set.prototype` into 289 // each object. 290 const nodeFlags = ObjectDefineProperties( 291 new Set(allowedNodeEnvironmentFlags.map(trimLeadingDashes)), 292 ObjectGetOwnPropertyDescriptors(SetPrototype) 293 ); 294 295 class NodeEnvironmentFlagsSet extends Set { 296 constructor(...args) { 297 super(...args); 298 299 // The super constructor consumes `add`, but 300 // disallow any future adds. 301 ObjectDefineProperty(this, 'add', { 302 value: () => this 303 }); 304 } 305 306 delete() { 307 // No-op, `Set` API compatible 308 return false; 309 } 310 311 clear() { 312 // No-op 313 } 314 315 has(key) { 316 // This will return `true` based on various possible 317 // permutations of a flag, including present/missing leading 318 // dash(es) and/or underscores-for-dashes. 319 // Strips any values after `=`, inclusive. 320 // TODO(addaleax): It might be more flexible to run the option parser 321 // on a dummy option set and see whether it rejects the argument or 322 // not. 323 if (typeof key === 'string') { 324 key = StringPrototypeReplace(key, replaceUnderscoresRegex, '-'); 325 if (RegExpPrototypeTest(leadingDashesRegex, key)) { 326 key = StringPrototypeReplace(key, trailingValuesRegex, ''); 327 return SetPrototypeHas(this, key); 328 } 329 return SetPrototypeHas(nodeFlags, key); 330 } 331 return false; 332 } 333 } 334 335 ObjectFreeze(NodeEnvironmentFlagsSet.prototype.constructor); 336 ObjectFreeze(NodeEnvironmentFlagsSet.prototype); 337 338 return ObjectFreeze(new NodeEnvironmentFlagsSet( 339 allowedNodeEnvironmentFlags 340 )); 341} 342 343// Lazy load internal/trace_events_async_hooks only if the async_hooks 344// trace event category is enabled. 345let traceEventsAsyncHook; 346// Dynamically enable/disable the traceEventsAsyncHook 347function toggleTraceCategoryState(asyncHooksEnabled) { 348 if (asyncHooksEnabled) { 349 if (!traceEventsAsyncHook) { 350 traceEventsAsyncHook = 351 require('internal/trace_events_async_hooks').createHook(); 352 } 353 traceEventsAsyncHook.enable(); 354 } else if (traceEventsAsyncHook) { 355 traceEventsAsyncHook.disable(); 356 } 357} 358 359module.exports = { 360 toggleTraceCategoryState, 361 assert, 362 buildAllowedFlags, 363 wrapProcessMethods 364}; 365