diff --git a/lib/internal/bootstrap/node.js b/lib/internal/bootstrap/node.js index c51961e9ce98dc..c3753741641387 100644 --- a/lib/internal/bootstrap/node.js +++ b/lib/internal/bootstrap/node.js @@ -20,11 +20,7 @@ const { _setupTraceCategoryState, _setupNextTick, - _setupPromises, _chdir, _cpuUsage, - _hrtime, _hrtimeBigInt, - _memoryUsage, _rawDebug, - _umask, - _shouldAbortOnUncaughtToggle + _setupPromises } = bootstrappers; const { internalBinding, NativeModule } = loaderExports; @@ -57,15 +53,57 @@ function startup() { ); } - perThreadSetup.setupAssert(); - perThreadSetup.setupConfig(); + // process.config is serialized config.gypi + process.config = JSON.parse(internalBinding('native_module').config); + const rawMethods = internalBinding('process_methods'); + // Set up methods and events on the process object for the main thread if (isMainThread) { + // This depends on process being an event emitter mainThreadSetup.setupSignalHandlers(internalBinding); + + process.abort = rawMethods.abort; + const wrapped = mainThreadSetup.wrapProcessMethods(rawMethods); + process.umask = wrapped.umask; + process.chdir = wrapped.chdir; + + // TODO(joyeecheung): deprecate and remove these underscore methods + process._debugProcess = rawMethods._debugProcess; + process._debugEnd = rawMethods._debugEnd; + process._startProfilerIdleNotifier = + rawMethods._startProfilerIdleNotifier; + process._stopProfilerIdleNotifier = rawMethods._stopProfilerIdleNotifier; } - perThreadSetup.setupUncaughtExceptionCapture(exceptionHandlerState, - _shouldAbortOnUncaughtToggle); + // Set up methods on the process object for all threads + { + process.cwd = rawMethods.cwd; + process.dlopen = rawMethods.dlopen; + process.uptime = rawMethods.uptime; + + // TODO(joyeecheung): either remove them or make them public + process._getActiveRequests = rawMethods._getActiveRequests; + process._getActiveHandles = rawMethods._getActiveHandles; + + // TODO(joyeecheung): remove these + process.reallyExit = rawMethods.reallyExit; + process._kill = rawMethods._kill; + + const wrapped = perThreadSetup.wrapProcessMethods( + rawMethods, exceptionHandlerState + ); + process._rawDebug = wrapped._rawDebug; + process.hrtime = wrapped.hrtime; + process.hrtime.bigint = wrapped.hrtimeBigInt; + process.cpuUsage = wrapped.cpuUsage; + process.memoryUsage = wrapped.memoryUsage; + process.kill = wrapped.kill; + process.exit = wrapped.exit; + process.setUncaughtExceptionCaptureCallback = + wrapped.setUncaughtExceptionCaptureCallback; + process.hasUncaughtExceptionCaptureCallback = + wrapped.hasUncaughtExceptionCaptureCallback; + } NativeModule.require('internal/process/warning').setup(); NativeModule.require('internal/process/next_tick').setup(_setupNextTick, @@ -91,22 +129,10 @@ function startup() { if (isMainThread) { mainThreadSetup.setupStdio(); - mainThreadSetup.setupProcessMethods(_chdir, _umask); } else { workerThreadSetup.setupStdio(); } - const perf = internalBinding('performance'); - const { - NODE_PERFORMANCE_MILESTONE_BOOTSTRAP_COMPLETE, - } = perf.constants; - - perThreadSetup.setupRawDebug(_rawDebug); - perThreadSetup.setupHrtime(_hrtime, _hrtimeBigInt); - perThreadSetup.setupCpuUsage(_cpuUsage); - perThreadSetup.setupMemoryUsage(_memoryUsage); - perThreadSetup.setupKillAndExit(); - if (global.__coverage__) NativeModule.require('internal/process/write-coverage').setup(); @@ -209,9 +235,37 @@ function startup() { } } - perf.markMilestone(NODE_PERFORMANCE_MILESTONE_BOOTSTRAP_COMPLETE); + // process.allowedNodeEnvironmentFlags + Object.defineProperty(process, 'allowedNodeEnvironmentFlags', { + get() { + const flags = perThreadSetup.buildAllowedFlags(); + process.allowedNodeEnvironmentFlags = flags; + return process.allowedNodeEnvironmentFlags; + }, + // If the user tries to set this to another value, override + // this completely to that value. + set(value) { + Object.defineProperty(this, 'allowedNodeEnvironmentFlags', { + value, + configurable: true, + enumerable: true, + writable: true + }); + }, + enumerable: true, + configurable: true + }); + // process.assert + process.assert = deprecate( + perThreadSetup.assert, + 'process.assert() is deprecated. Please use the `assert` module instead.', + 'DEP0100'); - perThreadSetup.setupAllowedFlags(); + const perf = internalBinding('performance'); + const { + NODE_PERFORMANCE_MILESTONE_BOOTSTRAP_COMPLETE, + } = perf.constants; + perf.markMilestone(NODE_PERFORMANCE_MILESTONE_BOOTSTRAP_COMPLETE); startExecution(); } diff --git a/lib/internal/process/main_thread_only.js b/lib/internal/process/main_thread_only.js index 5f53f3408406b9..1db693f1ec716d 100644 --- a/lib/internal/process/main_thread_only.js +++ b/lib/internal/process/main_thread_only.js @@ -27,21 +27,25 @@ function setupStdio() { setupProcessStdio(getMainThreadStdio()); } -// Non-POSIX platforms like Windows don't have certain methods. -// Workers also lack these methods since they change process-global state. -function setupProcessMethods(_chdir, _umask) { - process.chdir = function chdir(directory) { +// The execution of this function itself should not cause any side effects. +function wrapProcessMethods(binding) { + function chdir(directory) { validateString(directory, 'directory'); - return _chdir(directory); - }; + return binding.chdir(directory); + } - process.umask = function umask(mask) { + function umask(mask) { if (mask === undefined) { // Get the mask - return _umask(mask); + return binding.umask(mask); } mask = validateMode(mask, 'mask'); - return _umask(mask); + return binding.umask(mask); + } + + return { + chdir, + umask }; } @@ -175,7 +179,7 @@ function setupChildProcessIpcChannel() { module.exports = { setupStdio, - setupProcessMethods, + wrapProcessMethods, setupSignalHandlers, setupChildProcessIpcChannel, wrapPosixCredentialSetters diff --git a/lib/internal/process/per_thread.js b/lib/internal/process/per_thread.js index 57cc9c38143f79..f1629e3a97c336 100644 --- a/lib/internal/process/per_thread.js +++ b/lib/internal/process/per_thread.js @@ -18,25 +18,30 @@ const { } = require('internal/errors'); const util = require('util'); const constants = internalBinding('constants').os.signals; -const { deprecate } = require('internal/util'); - -function setupAssert() { - process.assert = deprecate( - function(x, msg) { - if (!x) throw new ERR_ASSERTION(msg || 'assertion error'); - }, - 'process.assert() is deprecated. Please use the `assert` module instead.', - 'DEP0100'); + +function assert(x, msg) { + if (!x) throw new ERR_ASSERTION(msg || 'assertion error'); } -// Set up the process.cpuUsage() function. -function setupCpuUsage(_cpuUsage) { +// The execution of this function itself should not cause any side effects. +function wrapProcessMethods(binding, exceptionHandlerState) { + const { + hrtime: _hrtime, + hrtimeBigInt: _hrtimeBigInt, + cpuUsage: _cpuUsage, + memoryUsage: _memoryUsage + } = binding; + + function _rawDebug(...args) { + binding._rawDebug(util.format.apply(null, args)); + } + // Create the argument array that will be passed to the native function. const cpuValues = new Float64Array(2); // Replace the native function with the JS version that calls the native // function. - process.cpuUsage = function cpuUsage(prevValue) { + function cpuUsage(prevValue) { // If a previous value was passed in, ensure it has the correct shape. if (prevValue) { if (!previousValueIsValid(prevValue.user)) { @@ -80,7 +85,7 @@ function setupCpuUsage(_cpuUsage) { user: cpuValues[0], system: cpuValues[1] }; - }; + } // Ensure that a previously passed in value is valid. Currently, the native // implementation always returns numbers <= Number.MAX_SAFE_INTEGER. @@ -89,15 +94,13 @@ function setupCpuUsage(_cpuUsage) { num <= Number.MAX_SAFE_INTEGER && num >= 0; } -} -// The 3 entries filled in by the original process.hrtime contains -// the upper/lower 32 bits of the second part of the value, -// and the remaining nanoseconds of the value. -function setupHrtime(_hrtime, _hrtimeBigInt) { + // The 3 entries filled in by the original process.hrtime contains + // the upper/lower 32 bits of the second part of the value, + // and the remaining nanoseconds of the value. const hrValues = new Uint32Array(3); - process.hrtime = function hrtime(time) { + function hrtime(time) { _hrtime(hrValues); if (time !== undefined) { @@ -118,21 +121,18 @@ function setupHrtime(_hrtime, _hrtimeBigInt) { hrValues[0] * 0x100000000 + hrValues[1], hrValues[2] ]; - }; + } // Use a BigUint64Array in the closure because V8 does not have an API for // creating a BigInt out of a uint64_t yet. const hrBigintValues = new BigUint64Array(1); - process.hrtime.bigint = function() { + function hrtimeBigInt() { _hrtimeBigInt(hrBigintValues); return hrBigintValues[0]; - }; -} + } -function setupMemoryUsage(_memoryUsage) { const memValues = new Float64Array(4); - - process.memoryUsage = function memoryUsage() { + function memoryUsage() { _memoryUsage(memValues); return { rss: memValues[0], @@ -140,18 +140,9 @@ function setupMemoryUsage(_memoryUsage) { heapUsed: memValues[2], external: memValues[3] }; - }; -} - -function setupConfig() { - // Serialized config.gypi - process.config = JSON.parse(internalBinding('native_module').config); -} - - -function setupKillAndExit() { + } - process.exit = function(code) { + function exit(code) { if (code || code === 0) process.exitCode = code; @@ -159,10 +150,10 @@ function setupKillAndExit() { process._exiting = true; process.emit('exit', process.exitCode || 0); } - process.reallyExit(process.exitCode || 0); - }; + binding.reallyExit(process.exitCode || 0); + } - process.kill = function(pid, sig) { + function kill(pid, sig) { var err; if (process.env.NODE_V8_COVERAGE) { const { writeCoverage } = require('internal/process/coverage'); @@ -176,6 +167,8 @@ function setupKillAndExit() { // preserve null signal if (sig === (sig | 0)) { + // XXX(joyeecheung): we have to use process._kill here because + // it's monkey-patched by tests. err = process._kill(pid, sig); } else { sig = sig || 'SIGTERM'; @@ -190,22 +183,13 @@ function setupKillAndExit() { throw errnoException(err, 'kill'); return true; - }; -} - -function setupRawDebug(_rawDebug) { - process._rawDebug = function() { - _rawDebug(util.format.apply(null, arguments)); - }; -} - + } -function setupUncaughtExceptionCapture(exceptionHandlerState, - shouldAbortOnUncaughtToggle) { // shouldAbortOnUncaughtToggle is a typed array for faster // communication with JS. + const { shouldAbortOnUncaughtToggle } = binding; - process.setUncaughtExceptionCaptureCallback = function(fn) { + function setUncaughtExceptionCaptureCallback(fn) { if (fn === null) { exceptionHandlerState.captureFn = fn; shouldAbortOnUncaughtToggle[0] = 1; @@ -219,10 +203,22 @@ function setupUncaughtExceptionCapture(exceptionHandlerState, } exceptionHandlerState.captureFn = fn; shouldAbortOnUncaughtToggle[0] = 0; - }; + } - process.hasUncaughtExceptionCaptureCallback = function() { + function hasUncaughtExceptionCaptureCallback() { return exceptionHandlerState.captureFn !== null; + } + + return { + _rawDebug, + hrtime, + hrtimeBigInt, + cpuUsage, + memoryUsage, + kill, + exit, + setUncaughtExceptionCaptureCallback, + hasUncaughtExceptionCaptureCallback }; } @@ -326,38 +322,13 @@ function buildAllowedFlags() { Object.freeze(NodeEnvironmentFlagsSet.prototype.constructor); Object.freeze(NodeEnvironmentFlagsSet.prototype); - return process.allowedNodeEnvironmentFlags = Object.freeze( - new NodeEnvironmentFlagsSet( - allowedNodeEnvironmentFlags - )); -} - -function setupAllowedFlags() { - Object.defineProperty(process, 'allowedNodeEnvironmentFlags', { - get: buildAllowedFlags, - set(value) { - // If the user tries to set this to another value, override - // this completely to that value. - Object.defineProperty(this, 'allowedNodeEnvironmentFlags', { - value, - configurable: true, - enumerable: true, - writable: true - }); - }, - enumerable: true, - configurable: true - }); + return Object.freeze(new NodeEnvironmentFlagsSet( + allowedNodeEnvironmentFlags + )); } module.exports = { - setupAllowedFlags, - setupAssert, - setupCpuUsage, - setupHrtime, - setupMemoryUsage, - setupConfig, - setupKillAndExit, - setupRawDebug, - setupUncaughtExceptionCapture + assert, + buildAllowedFlags, + wrapProcessMethods }; diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index 0a1cfed2109e5c..60c05a58a6f47f 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -139,20 +139,6 @@ void SetupBootstrapObject(Environment* env, BOOTSTRAP_METHOD(_setupTraceCategoryState, SetupTraceCategoryState); BOOTSTRAP_METHOD(_setupNextTick, SetupNextTick); BOOTSTRAP_METHOD(_setupPromises, SetupPromises); - BOOTSTRAP_METHOD(_chdir, Chdir); - BOOTSTRAP_METHOD(_cpuUsage, CPUUsage); - BOOTSTRAP_METHOD(_hrtime, Hrtime); - BOOTSTRAP_METHOD(_hrtimeBigInt, HrtimeBigInt); - BOOTSTRAP_METHOD(_memoryUsage, MemoryUsage); - BOOTSTRAP_METHOD(_rawDebug, RawDebug); - BOOTSTRAP_METHOD(_umask, Umask); - - Local should_abort_on_uncaught_toggle = - FIXED_ONE_BYTE_STRING(env->isolate(), "_shouldAbortOnUncaughtToggle"); - CHECK(bootstrapper->Set(env->context(), - should_abort_on_uncaught_toggle, - env->should_abort_on_uncaught_toggle().GetJSArray()) - .FromJust()); } #undef BOOTSTRAP_METHOD diff --git a/src/node.cc b/src/node.cc index 077d9c49966d61..9bd2ac9ff6f920 100644 --- a/src/node.cc +++ b/src/node.cc @@ -95,8 +95,6 @@ #if defined(_MSC_VER) #include #include -#define umask _umask -typedef int mode_t; #else #include #include // getrlimit, setrlimit @@ -689,8 +687,7 @@ static void WaitForInspectorDisconnect(Environment* env) { #endif } - -static void Exit(const FunctionCallbackInfo& args) { +void Exit(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); WaitForInspectorDisconnect(env); v8_platform.StopTracingAgent(); @@ -1114,29 +1111,6 @@ void SetupProcessObject(Environment* env, DebugPortGetter, env->is_main_thread() ? DebugPortSetter : nullptr, env->as_external()).FromJust()); - - // define various internal methods - if (env->is_main_thread()) { - env->SetMethod(process, "_debugProcess", DebugProcess); - env->SetMethod(process, "_debugEnd", DebugEnd); - env->SetMethod(process, - "_startProfilerIdleNotifier", - StartProfilerIdleNotifier); - env->SetMethod(process, - "_stopProfilerIdleNotifier", - StopProfilerIdleNotifier); - env->SetMethod(process, "abort", Abort); - env->SetMethod(process, "chdir", Chdir); - env->SetMethod(process, "umask", Umask); - } - env->SetMethod(process, "_getActiveRequests", GetActiveRequests); - env->SetMethod(process, "_getActiveHandles", GetActiveHandles); - env->SetMethod(process, "_kill", Kill); - - env->SetMethodNoSideEffect(process, "cwd", Cwd); - env->SetMethod(process, "dlopen", binding::DLOpen); - env->SetMethod(process, "reallyExit", Exit); - env->SetMethodNoSideEffect(process, "uptime", Uptime); } @@ -1278,135 +1252,8 @@ void RegisterSignalHandler(int signal, CHECK_EQ(sigaction(signal, &sa, nullptr), 0); } - -void DebugProcess(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - - if (args.Length() != 1) { - return env->ThrowError("Invalid number of arguments."); - } - - CHECK(args[0]->IsNumber()); - pid_t pid = args[0].As()->Value(); - int r = kill(pid, SIGUSR1); - - if (r != 0) { - return env->ThrowErrnoException(errno, "kill"); - } -} #endif // __POSIX__ - -#ifdef _WIN32 -static int GetDebugSignalHandlerMappingName(DWORD pid, wchar_t* buf, - size_t buf_len) { - return _snwprintf(buf, buf_len, L"node-debug-handler-%u", pid); -} - - -static void DebugProcess(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - Isolate* isolate = args.GetIsolate(); - - if (args.Length() != 1) { - env->ThrowError("Invalid number of arguments."); - return; - } - - HANDLE process = nullptr; - HANDLE thread = nullptr; - HANDLE mapping = nullptr; - wchar_t mapping_name[32]; - LPTHREAD_START_ROUTINE* handler = nullptr; - DWORD pid = 0; - - OnScopeLeave cleanup([&]() { - if (process != nullptr) - CloseHandle(process); - if (thread != nullptr) - CloseHandle(thread); - if (handler != nullptr) - UnmapViewOfFile(handler); - if (mapping != nullptr) - CloseHandle(mapping); - }); - - CHECK(args[0]->IsNumber()); - pid = args[0].As()->Value(); - - process = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | - PROCESS_VM_OPERATION | PROCESS_VM_WRITE | - PROCESS_VM_READ, - FALSE, - pid); - if (process == nullptr) { - isolate->ThrowException( - WinapiErrnoException(isolate, GetLastError(), "OpenProcess")); - return; - } - - if (GetDebugSignalHandlerMappingName(pid, - mapping_name, - arraysize(mapping_name)) < 0) { - env->ThrowErrnoException(errno, "sprintf"); - return; - } - - mapping = OpenFileMappingW(FILE_MAP_READ, FALSE, mapping_name); - if (mapping == nullptr) { - isolate->ThrowException(WinapiErrnoException(isolate, - GetLastError(), - "OpenFileMappingW")); - return; - } - - handler = reinterpret_cast( - MapViewOfFile(mapping, - FILE_MAP_READ, - 0, - 0, - sizeof *handler)); - if (handler == nullptr || *handler == nullptr) { - isolate->ThrowException( - WinapiErrnoException(isolate, GetLastError(), "MapViewOfFile")); - return; - } - - thread = CreateRemoteThread(process, - nullptr, - 0, - *handler, - nullptr, - 0, - nullptr); - if (thread == nullptr) { - isolate->ThrowException(WinapiErrnoException(isolate, - GetLastError(), - "CreateRemoteThread")); - return; - } - - // Wait for the thread to terminate - if (WaitForSingleObject(thread, INFINITE) != WAIT_OBJECT_0) { - isolate->ThrowException(WinapiErrnoException(isolate, - GetLastError(), - "WaitForSingleObject")); - return; - } -} -#endif // _WIN32 - - -static void DebugEnd(const FunctionCallbackInfo& args) { -#if HAVE_INSPECTOR - Environment* env = Environment::GetCurrent(args); - if (env->inspector_agent()->IsListening()) { - env->inspector_agent()->Stop(); - } -#endif -} - - inline void PlatformInit() { #ifdef __POSIX__ #if HAVE_INSPECTOR diff --git a/src/node_binding.cc b/src/node_binding.cc index 0758741ffcb852..46ae5f6840f658 100644 --- a/src/node_binding.cc +++ b/src/node_binding.cc @@ -44,6 +44,7 @@ V(performance) \ V(pipe_wrap) \ V(process_wrap) \ + V(process_methods) \ V(serdes) \ V(signal_wrap) \ V(spawn_sync) \ diff --git a/src/node_internals.h b/src/node_internals.h index 03017fb4f5185e..99498a6218d35a 100644 --- a/src/node_internals.h +++ b/src/node_internals.h @@ -119,6 +119,7 @@ void GetSockOrPeerName(const v8::FunctionCallbackInfo& args) { args.GetReturnValue().Set(err); } +void Exit(const v8::FunctionCallbackInfo& args); void SignalExit(int signo); #ifdef __POSIX__ void RegisterSignalHandler(int signal, @@ -698,21 +699,7 @@ static inline const char* errno_string(int errorno) { extern double prog_start_time; -void Abort(const v8::FunctionCallbackInfo& args); -void Chdir(const v8::FunctionCallbackInfo& args); -void CPUUsage(const v8::FunctionCallbackInfo& args); -void Cwd(const v8::FunctionCallbackInfo& args); -void GetActiveHandles(const v8::FunctionCallbackInfo& args); -void GetActiveRequests(const v8::FunctionCallbackInfo& args); -void Hrtime(const v8::FunctionCallbackInfo& args); -void HrtimeBigInt(const v8::FunctionCallbackInfo& args); -void Kill(const v8::FunctionCallbackInfo& args); -void MemoryUsage(const v8::FunctionCallbackInfo& args); void RawDebug(const v8::FunctionCallbackInfo& args); -void StartProfilerIdleNotifier(const v8::FunctionCallbackInfo& args); -void StopProfilerIdleNotifier(const v8::FunctionCallbackInfo& args); -void Umask(const v8::FunctionCallbackInfo& args); -void Uptime(const v8::FunctionCallbackInfo& args); void DebugPortGetter(v8::Local property, const v8::PropertyCallbackInfo& info); diff --git a/src/node_process.cc b/src/node_process.cc index b9376953e241be..477ac2adc8eb6c 100644 --- a/src/node_process.cc +++ b/src/node_process.cc @@ -8,6 +8,8 @@ #include "uv.h" #include "v8.h" +#include + #if HAVE_INSPECTOR #include "inspector_io.h" #endif @@ -36,10 +38,12 @@ using v8::Float64Array; using v8::Function; using v8::FunctionCallbackInfo; using v8::HeapStatistics; +using v8::Integer; using v8::Isolate; using v8::Local; using v8::Name; using v8::NewStringType; +using v8::Object; using v8::PropertyCallbackInfo; using v8::String; using v8::Uint32; @@ -61,11 +65,11 @@ Mutex environ_mutex; #define CHDIR_BUFSIZE (PATH_MAX) #endif -void Abort(const FunctionCallbackInfo& args) { +static void Abort(const FunctionCallbackInfo& args) { Abort(); } -void Chdir(const FunctionCallbackInfo& args) { +static void Chdir(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); CHECK(env->is_main_thread()); @@ -88,7 +92,7 @@ void Chdir(const FunctionCallbackInfo& args) { // which are uv_timeval_t structs (long tv_sec, long tv_usec). // Returns those values as Float64 microseconds in the elements of the array // passed to the function. -void CPUUsage(const FunctionCallbackInfo& args) { +static void CPUUsage(const FunctionCallbackInfo& args) { uv_rusage_t rusage; // Call libuv to get the values we'll return. @@ -111,7 +115,7 @@ void CPUUsage(const FunctionCallbackInfo& args) { fields[1] = MICROS_PER_SEC * rusage.ru_stime.tv_sec + rusage.ru_stime.tv_usec; } -void Cwd(const FunctionCallbackInfo& args) { +static void Cwd(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); char buf[CHDIR_BUFSIZE]; size_t cwd_len = sizeof(buf); @@ -138,7 +142,7 @@ void Cwd(const FunctionCallbackInfo& args) { // broken into the upper/lower 32 bits to be converted back in JS, // because there is no Uint64Array in JS. // The third entry contains the remaining nanosecond part of the value. -void Hrtime(const FunctionCallbackInfo& args) { +static void Hrtime(const FunctionCallbackInfo& args) { uint64_t t = uv_hrtime(); Local ab = args[0].As()->Buffer(); @@ -149,13 +153,13 @@ void Hrtime(const FunctionCallbackInfo& args) { fields[2] = t % NANOS_PER_SEC; } -void HrtimeBigInt(const FunctionCallbackInfo& args) { +static void HrtimeBigInt(const FunctionCallbackInfo& args) { Local ab = args[0].As()->Buffer(); uint64_t* fields = static_cast(ab->GetContents().Data()); fields[0] = uv_hrtime(); } -void Kill(const FunctionCallbackInfo& args) { +static void Kill(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); Local context = env->context(); @@ -170,8 +174,7 @@ void Kill(const FunctionCallbackInfo& args) { args.GetReturnValue().Set(err); } - -void MemoryUsage(const FunctionCallbackInfo& args) { +static void MemoryUsage(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); size_t rss; @@ -209,18 +212,17 @@ void RawDebug(const FunctionCallbackInfo& args) { fflush(stderr); } -void StartProfilerIdleNotifier(const FunctionCallbackInfo& args) { +static void StartProfilerIdleNotifier(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); env->StartProfilerIdleNotifier(); } - -void StopProfilerIdleNotifier(const FunctionCallbackInfo& args) { +static void StopProfilerIdleNotifier(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); env->StopProfilerIdleNotifier(); } -void Umask(const FunctionCallbackInfo& args) { +static void Umask(const FunctionCallbackInfo& args) { uint32_t old; CHECK_EQ(args.Length(), 1); @@ -237,7 +239,7 @@ void Umask(const FunctionCallbackInfo& args) { args.GetReturnValue().Set(old); } -void Uptime(const FunctionCallbackInfo& args) { +static void Uptime(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); double uptime; @@ -255,7 +257,6 @@ void ProcessTitleGetter(Local property, NewStringType::kNormal).ToLocalChecked()); } - void ProcessTitleSetter(Local property, Local value, const PropertyCallbackInfo& info) { @@ -270,7 +271,7 @@ void GetParentProcessId(Local property, info.GetReturnValue().Set(uv_os_getppid()); } -void GetActiveRequests(const FunctionCallbackInfo& args) { +static void GetActiveRequests(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); std::vector> request_v; @@ -315,5 +316,159 @@ void DebugPortSetter(Local property, env->inspector_host_port()->set_port(static_cast(port)); } +#ifdef __POSIX__ +static void DebugProcess(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + + if (args.Length() != 1) { + return env->ThrowError("Invalid number of arguments."); + } + + CHECK(args[0]->IsNumber()); + pid_t pid = args[0].As()->Value(); + int r = kill(pid, SIGUSR1); + + if (r != 0) { + return env->ThrowErrnoException(errno, "kill"); + } +} +#endif // __POSIX__ + +#ifdef _WIN32 +static int GetDebugSignalHandlerMappingName(DWORD pid, + wchar_t* buf, + size_t buf_len) { + return _snwprintf(buf, buf_len, L"node-debug-handler-%u", pid); +} + +static void DebugProcess(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + Isolate* isolate = args.GetIsolate(); + + if (args.Length() != 1) { + env->ThrowError("Invalid number of arguments."); + return; + } + + HANDLE process = nullptr; + HANDLE thread = nullptr; + HANDLE mapping = nullptr; + wchar_t mapping_name[32]; + LPTHREAD_START_ROUTINE* handler = nullptr; + DWORD pid = 0; + + OnScopeLeave cleanup([&]() { + if (process != nullptr) CloseHandle(process); + if (thread != nullptr) CloseHandle(thread); + if (handler != nullptr) UnmapViewOfFile(handler); + if (mapping != nullptr) CloseHandle(mapping); + }); + + CHECK(args[0]->IsNumber()); + pid = args[0].As()->Value(); + + process = + OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | + PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, + FALSE, + pid); + if (process == nullptr) { + isolate->ThrowException( + WinapiErrnoException(isolate, GetLastError(), "OpenProcess")); + return; + } + + if (GetDebugSignalHandlerMappingName( + pid, mapping_name, arraysize(mapping_name)) < 0) { + env->ThrowErrnoException(errno, "sprintf"); + return; + } + + mapping = OpenFileMappingW(FILE_MAP_READ, FALSE, mapping_name); + if (mapping == nullptr) { + isolate->ThrowException( + WinapiErrnoException(isolate, GetLastError(), "OpenFileMappingW")); + return; + } + + handler = reinterpret_cast( + MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, sizeof *handler)); + if (handler == nullptr || *handler == nullptr) { + isolate->ThrowException( + WinapiErrnoException(isolate, GetLastError(), "MapViewOfFile")); + return; + } + + thread = + CreateRemoteThread(process, nullptr, 0, *handler, nullptr, 0, nullptr); + if (thread == nullptr) { + isolate->ThrowException( + WinapiErrnoException(isolate, GetLastError(), "CreateRemoteThread")); + return; + } + + // Wait for the thread to terminate + if (WaitForSingleObject(thread, INFINITE) != WAIT_OBJECT_0) { + isolate->ThrowException( + WinapiErrnoException(isolate, GetLastError(), "WaitForSingleObject")); + return; + } +} +#endif // _WIN32 + +static void DebugEnd(const FunctionCallbackInfo& args) { +#if HAVE_INSPECTOR + Environment* env = Environment::GetCurrent(args); + if (env->inspector_agent()->IsListening()) { + env->inspector_agent()->Stop(); + } +#endif +} + +static void InitializeProcessMethods(Local target, + Local unused, + Local context, + void* priv) { + Environment* env = Environment::GetCurrent(context); + + // define various internal methods + if (env->is_main_thread()) { + env->SetMethod(target, "_debugProcess", DebugProcess); + env->SetMethod(target, "_debugEnd", DebugEnd); + env->SetMethod( + target, "_startProfilerIdleNotifier", StartProfilerIdleNotifier); + env->SetMethod( + target, "_stopProfilerIdleNotifier", StopProfilerIdleNotifier); + env->SetMethod(target, "abort", Abort); + env->SetMethod(target, "chdir", Chdir); + env->SetMethod(target, "umask", Umask); + } + + env->SetMethod(target, "_rawDebug", RawDebug); + env->SetMethod(target, "memoryUsage", MemoryUsage); + env->SetMethod(target, "cpuUsage", CPUUsage); + env->SetMethod(target, "hrtime", Hrtime); + env->SetMethod(target, "hrtimeBigInt", HrtimeBigInt); + + env->SetMethod(target, "_getActiveRequests", GetActiveRequests); + env->SetMethod(target, "_getActiveHandles", GetActiveHandles); + env->SetMethod(target, "_kill", Kill); + + env->SetMethodNoSideEffect(target, "cwd", Cwd); + env->SetMethod(target, "dlopen", binding::DLOpen); + env->SetMethod(target, "reallyExit", Exit); + env->SetMethodNoSideEffect(target, "uptime", Uptime); + + Local should_abort_on_uncaught_toggle = + FIXED_ONE_BYTE_STRING(env->isolate(), "shouldAbortOnUncaughtToggle"); + CHECK(target + ->Set(env->context(), + should_abort_on_uncaught_toggle, + env->should_abort_on_uncaught_toggle().GetJSArray()) + .FromJust()); +} } // namespace node + +NODE_MODULE_CONTEXT_AWARE_INTERNAL(process_methods, + node::InitializeProcessMethods) diff --git a/test/parallel/test-bootstrap-modules.js b/test/parallel/test-bootstrap-modules.js index 5c1693ca89c5db..3f67144f5fb2ce 100644 --- a/test/parallel/test-bootstrap-modules.js +++ b/test/parallel/test-bootstrap-modules.js @@ -9,7 +9,7 @@ const common = require('../common'); const assert = require('assert'); const isMainThread = common.isMainThread; -const kMaxModuleCount = isMainThread ? 60 : 82; +const kMaxModuleCount = isMainThread ? 61 : 83; assert(list.length <= kMaxModuleCount, `Total length: ${list.length}\n` + list.join('\n')