Skip to content

Commit

Permalink
process: add getActiveResources()
Browse files Browse the repository at this point in the history
This is supposed to be a public alternative of the private APIs,
`process._getActiveResources()` and `process._getActiveHandles()`. When
called, it returns an array of objects containing the `type`, `asyncId`
and the `triggerAsyncId` of the currently active `requests`, `handles`
and `timers`.

Signed-off-by: Darshan Sen <darshan.sen@postman.com>
  • Loading branch information
RaisinTen committed Nov 14, 2021
1 parent dd52c05 commit 489e4ce
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 7 deletions.
42 changes: 40 additions & 2 deletions lib/internal/bootstrap/node.js
Expand Up @@ -39,19 +39,24 @@
setupPrepareStackTrace();

const {
ArrayPrototypeConcat,
ArrayPrototypeForEach,
ArrayPrototypePush,
FunctionPrototypeCall,
JSONParse,
ObjectDefineProperty,
ObjectDefineProperties,
ObjectGetPrototypeOf,
ObjectPreventExtensions,
ObjectSetPrototypeOf,
ObjectValues,
ReflectGet,
ReflectSet,
SymbolToStringTag,
globalThis,
} = primordials;
const config = internalBinding('config');
const internalTimers = require('internal/timers');
const { deprecate, lazyDOMExceptionClass } = require('internal/util');

setupProcessObject();
Expand Down Expand Up @@ -150,6 +155,37 @@ const rawMethods = internalBinding('process_methods');
process._getActiveRequests = rawMethods._getActiveRequests;
process._getActiveHandles = rawMethods._getActiveHandles;

process.getActiveResources = function() {
const resources = ArrayPrototypeConcat(rawMethods._getActiveRequests2(),
rawMethods._getActiveHandles2());

ArrayPrototypeForEach(ObjectValues(internalTimers.timerListMap), (list) => {
let timeout = list._idlePrev === list ? null : list._idlePrev;
while (timeout !== null) {
ArrayPrototypePush(resources, {
type: 'Timeout',
asyncId: timeout[internalTimers.async_id_symbol],
triggerAsyncId: timeout[internalTimers.trigger_async_id_symbol],
});
timeout = timeout._idlePrev === list ? null : list._idlePrev;
}
});

const queue = internalTimers.outstandingQueue.head !== null ?
internalTimers.outstandingQueue : internalTimers.immediateQueue;
let immediate = queue.head;
while (immediate !== null) {
ArrayPrototypePush(resources, {
type: 'Immediate',
asyncId: immediate[internalTimers.async_id_symbol],
triggerAsyncId: immediate[internalTimers.trigger_async_id_symbol],
});
immediate = immediate._idleNext;
}

return resources;
};

// TODO(joyeecheung): remove these
process.reallyExit = rawMethods.reallyExit;
process._kill = rawMethods._kill;
Expand Down Expand Up @@ -357,9 +393,11 @@ process.emitWarning = emitWarning;
// TODO(joyeecheung): either remove it or make it public
process._tickCallback = runNextTicks;

const { getTimerCallbacks } = require('internal/timers');
const { setupTimers } = internalBinding('timers');
const { processImmediate, processTimers } = getTimerCallbacks(runNextTicks);
const {
processImmediate,
processTimers,
} = internalTimers.getTimerCallbacks(runNextTicks);
// Sets two per-Environment callbacks that will be run from libuv:
// - processImmediate will be run in the callback of the per-Environment
// check handle.
Expand Down
11 changes: 6 additions & 5 deletions lib/internal/timers.js
Expand Up @@ -139,6 +139,11 @@ const kRefed = Symbol('refed');
// Create a single linked list instance only once at startup
const immediateQueue = new ImmediateList();

// If an uncaught exception was thrown during execution of immediateQueue,
// this queue will store all remaining Immediates that need to run upon
// resolution of all error handling (if process is still alive).
const outstandingQueue = new ImmediateList();

let nextExpiry = Infinity;
let refCount = 0;

Expand Down Expand Up @@ -413,11 +418,6 @@ function setPosition(node, pos) {
}

function getTimerCallbacks(runNextTicks) {
// If an uncaught exception was thrown during execution of immediateQueue,
// this queue will store all remaining Immediates that need to run upon
// resolution of all error handling (if process is still alive).
const outstandingQueue = new ImmediateList();

function processImmediate() {
const queue = outstandingQueue.head !== null ?
outstandingQueue : immediateQueue;
Expand Down Expand Up @@ -649,6 +649,7 @@ module.exports = {
setUnrefTimeout,
getTimerDuration,
immediateQueue,
outstandingQueue,
getTimerCallbacks,
immediateInfoFields: {
kCount,
Expand Down
65 changes: 65 additions & 0 deletions src/node_process_methods.cc
@@ -1,3 +1,4 @@
#include "async_wrap-inl.h"
#include "base_object-inl.h"
#include "debug_utils-inl.h"
#include "env-inl.h"
Expand Down Expand Up @@ -257,13 +258,44 @@ static void GetActiveRequests(const FunctionCallbackInfo<Value>& args) {
AsyncWrap* w = req_wrap->GetAsyncWrap();
if (w->persistent().IsEmpty())
continue;

request_v.emplace_back(w->GetOwner());
}

args.GetReturnValue().Set(
Array::New(env->isolate(), request_v.data(), request_v.size()));
}

static void GetActiveRequests2(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);

std::vector<Local<Value>> requests;
for (ReqWrapBase* req_wrap : *env->req_wrap_queue()) {
AsyncWrap* w = req_wrap->GetAsyncWrap();
if (w->persistent().IsEmpty())
continue;

Local<Object> request = Object::New(env->isolate());
if (request->Set(env->context(),
OneByteString(env->isolate(), "type"),
OneByteString(env->isolate(), w->MemoryInfoName().c_str()))
.IsNothing() ||
request->Set(env->context(),
OneByteString(env->isolate(), "asyncId"),
Number::New(env->isolate(), w->get_async_id()))
.IsNothing() ||
request->Set(env->context(),
OneByteString(env->isolate(), "triggerAsyncId"),
Number::New(env->isolate(), w->get_trigger_async_id()))
.IsNothing()) return;

requests.emplace_back(request);
}

args.GetReturnValue().Set(
Array::New(env->isolate(), requests.data(), requests.size()));
}

// Non-static, friend of HandleWrap. Could have been a HandleWrap method but
// implemented here for consistency with GetActiveRequests().
void GetActiveHandles(const FunctionCallbackInfo<Value>& args) {
Expand All @@ -279,6 +311,35 @@ void GetActiveHandles(const FunctionCallbackInfo<Value>& args) {
Array::New(env->isolate(), handle_v.data(), handle_v.size()));
}

void GetActiveHandles2(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);

std::vector<Local<Value>> handles;
for (HandleWrap* w : *env->handle_wrap_queue()) {
if (w->persistent().IsEmpty() || !HandleWrap::HasRef(w))
continue;

Local<Object> handle = Object::New(env->isolate());
if (handle->Set(env->context(),
OneByteString(env->isolate(), "type"),
OneByteString(env->isolate(), w->MemoryInfoName().c_str()))
.IsNothing() ||
handle->Set(env->context(),
OneByteString(env->isolate(), "asyncId"),
Number::New(env->isolate(), w->get_async_id()))
.IsNothing() ||
handle->Set(env->context(),
OneByteString(env->isolate(), "triggerAsyncId"),
Number::New(env->isolate(), w->get_trigger_async_id()))
.IsNothing()) return;

handles.emplace_back(handle);
}

args.GetReturnValue().Set(
Array::New(env->isolate(), handles.data(), handles.size()));
}

static void ResourceUsage(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);

Expand Down Expand Up @@ -559,7 +620,9 @@ static void InitializeProcessMethods(Local<Object> target,
env->SetMethod(target, "resourceUsage", ResourceUsage);

env->SetMethod(target, "_getActiveRequests", GetActiveRequests);
env->SetMethod(target, "_getActiveRequests2", GetActiveRequests2);
env->SetMethod(target, "_getActiveHandles", GetActiveHandles);
env->SetMethod(target, "_getActiveHandles2", GetActiveHandles2);
env->SetMethod(target, "_kill", Kill);

env->SetMethodNoSideEffect(target, "cwd", Cwd);
Expand All @@ -586,7 +649,9 @@ void RegisterProcessMethodsExternalReferences(
registry->Register(ResourceUsage);

registry->Register(GetActiveRequests);
registry->Register(GetActiveRequests2);
registry->Register(GetActiveHandles);
registry->Register(GetActiveHandles2);
registry->Register(Kill);

registry->Register(Cwd);
Expand Down

0 comments on commit 489e4ce

Please sign in to comment.