Skip to content

Commit 489e4ce

Browse files
committed
process: add getActiveResources()
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 <[email protected]>
1 parent dd52c05 commit 489e4ce

File tree

3 files changed

+111
-7
lines changed

3 files changed

+111
-7
lines changed

lib/internal/bootstrap/node.js

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,19 +39,24 @@
3939
setupPrepareStackTrace();
4040

4141
const {
42+
ArrayPrototypeConcat,
43+
ArrayPrototypeForEach,
44+
ArrayPrototypePush,
4245
FunctionPrototypeCall,
4346
JSONParse,
4447
ObjectDefineProperty,
4548
ObjectDefineProperties,
4649
ObjectGetPrototypeOf,
4750
ObjectPreventExtensions,
4851
ObjectSetPrototypeOf,
52+
ObjectValues,
4953
ReflectGet,
5054
ReflectSet,
5155
SymbolToStringTag,
5256
globalThis,
5357
} = primordials;
5458
const config = internalBinding('config');
59+
const internalTimers = require('internal/timers');
5560
const { deprecate, lazyDOMExceptionClass } = require('internal/util');
5661

5762
setupProcessObject();
@@ -150,6 +155,37 @@ const rawMethods = internalBinding('process_methods');
150155
process._getActiveRequests = rawMethods._getActiveRequests;
151156
process._getActiveHandles = rawMethods._getActiveHandles;
152157

158+
process.getActiveResources = function() {
159+
const resources = ArrayPrototypeConcat(rawMethods._getActiveRequests2(),
160+
rawMethods._getActiveHandles2());
161+
162+
ArrayPrototypeForEach(ObjectValues(internalTimers.timerListMap), (list) => {
163+
let timeout = list._idlePrev === list ? null : list._idlePrev;
164+
while (timeout !== null) {
165+
ArrayPrototypePush(resources, {
166+
type: 'Timeout',
167+
asyncId: timeout[internalTimers.async_id_symbol],
168+
triggerAsyncId: timeout[internalTimers.trigger_async_id_symbol],
169+
});
170+
timeout = timeout._idlePrev === list ? null : list._idlePrev;
171+
}
172+
});
173+
174+
const queue = internalTimers.outstandingQueue.head !== null ?
175+
internalTimers.outstandingQueue : internalTimers.immediateQueue;
176+
let immediate = queue.head;
177+
while (immediate !== null) {
178+
ArrayPrototypePush(resources, {
179+
type: 'Immediate',
180+
asyncId: immediate[internalTimers.async_id_symbol],
181+
triggerAsyncId: immediate[internalTimers.trigger_async_id_symbol],
182+
});
183+
immediate = immediate._idleNext;
184+
}
185+
186+
return resources;
187+
};
188+
153189
// TODO(joyeecheung): remove these
154190
process.reallyExit = rawMethods.reallyExit;
155191
process._kill = rawMethods._kill;
@@ -357,9 +393,11 @@ process.emitWarning = emitWarning;
357393
// TODO(joyeecheung): either remove it or make it public
358394
process._tickCallback = runNextTicks;
359395

360-
const { getTimerCallbacks } = require('internal/timers');
361396
const { setupTimers } = internalBinding('timers');
362-
const { processImmediate, processTimers } = getTimerCallbacks(runNextTicks);
397+
const {
398+
processImmediate,
399+
processTimers,
400+
} = internalTimers.getTimerCallbacks(runNextTicks);
363401
// Sets two per-Environment callbacks that will be run from libuv:
364402
// - processImmediate will be run in the callback of the per-Environment
365403
// check handle.

lib/internal/timers.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,11 @@ const kRefed = Symbol('refed');
139139
// Create a single linked list instance only once at startup
140140
const immediateQueue = new ImmediateList();
141141

142+
// If an uncaught exception was thrown during execution of immediateQueue,
143+
// this queue will store all remaining Immediates that need to run upon
144+
// resolution of all error handling (if process is still alive).
145+
const outstandingQueue = new ImmediateList();
146+
142147
let nextExpiry = Infinity;
143148
let refCount = 0;
144149

@@ -413,11 +418,6 @@ function setPosition(node, pos) {
413418
}
414419

415420
function getTimerCallbacks(runNextTicks) {
416-
// If an uncaught exception was thrown during execution of immediateQueue,
417-
// this queue will store all remaining Immediates that need to run upon
418-
// resolution of all error handling (if process is still alive).
419-
const outstandingQueue = new ImmediateList();
420-
421421
function processImmediate() {
422422
const queue = outstandingQueue.head !== null ?
423423
outstandingQueue : immediateQueue;
@@ -649,6 +649,7 @@ module.exports = {
649649
setUnrefTimeout,
650650
getTimerDuration,
651651
immediateQueue,
652+
outstandingQueue,
652653
getTimerCallbacks,
653654
immediateInfoFields: {
654655
kCount,

src/node_process_methods.cc

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#include "async_wrap-inl.h"
12
#include "base_object-inl.h"
23
#include "debug_utils-inl.h"
34
#include "env-inl.h"
@@ -257,13 +258,44 @@ static void GetActiveRequests(const FunctionCallbackInfo<Value>& args) {
257258
AsyncWrap* w = req_wrap->GetAsyncWrap();
258259
if (w->persistent().IsEmpty())
259260
continue;
261+
260262
request_v.emplace_back(w->GetOwner());
261263
}
262264

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

269+
static void GetActiveRequests2(const FunctionCallbackInfo<Value>& args) {
270+
Environment* env = Environment::GetCurrent(args);
271+
272+
std::vector<Local<Value>> requests;
273+
for (ReqWrapBase* req_wrap : *env->req_wrap_queue()) {
274+
AsyncWrap* w = req_wrap->GetAsyncWrap();
275+
if (w->persistent().IsEmpty())
276+
continue;
277+
278+
Local<Object> request = Object::New(env->isolate());
279+
if (request->Set(env->context(),
280+
OneByteString(env->isolate(), "type"),
281+
OneByteString(env->isolate(), w->MemoryInfoName().c_str()))
282+
.IsNothing() ||
283+
request->Set(env->context(),
284+
OneByteString(env->isolate(), "asyncId"),
285+
Number::New(env->isolate(), w->get_async_id()))
286+
.IsNothing() ||
287+
request->Set(env->context(),
288+
OneByteString(env->isolate(), "triggerAsyncId"),
289+
Number::New(env->isolate(), w->get_trigger_async_id()))
290+
.IsNothing()) return;
291+
292+
requests.emplace_back(request);
293+
}
294+
295+
args.GetReturnValue().Set(
296+
Array::New(env->isolate(), requests.data(), requests.size()));
297+
}
298+
267299
// Non-static, friend of HandleWrap. Could have been a HandleWrap method but
268300
// implemented here for consistency with GetActiveRequests().
269301
void GetActiveHandles(const FunctionCallbackInfo<Value>& args) {
@@ -279,6 +311,35 @@ void GetActiveHandles(const FunctionCallbackInfo<Value>& args) {
279311
Array::New(env->isolate(), handle_v.data(), handle_v.size()));
280312
}
281313

314+
void GetActiveHandles2(const FunctionCallbackInfo<Value>& args) {
315+
Environment* env = Environment::GetCurrent(args);
316+
317+
std::vector<Local<Value>> handles;
318+
for (HandleWrap* w : *env->handle_wrap_queue()) {
319+
if (w->persistent().IsEmpty() || !HandleWrap::HasRef(w))
320+
continue;
321+
322+
Local<Object> handle = Object::New(env->isolate());
323+
if (handle->Set(env->context(),
324+
OneByteString(env->isolate(), "type"),
325+
OneByteString(env->isolate(), w->MemoryInfoName().c_str()))
326+
.IsNothing() ||
327+
handle->Set(env->context(),
328+
OneByteString(env->isolate(), "asyncId"),
329+
Number::New(env->isolate(), w->get_async_id()))
330+
.IsNothing() ||
331+
handle->Set(env->context(),
332+
OneByteString(env->isolate(), "triggerAsyncId"),
333+
Number::New(env->isolate(), w->get_trigger_async_id()))
334+
.IsNothing()) return;
335+
336+
handles.emplace_back(handle);
337+
}
338+
339+
args.GetReturnValue().Set(
340+
Array::New(env->isolate(), handles.data(), handles.size()));
341+
}
342+
282343
static void ResourceUsage(const FunctionCallbackInfo<Value>& args) {
283344
Environment* env = Environment::GetCurrent(args);
284345

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

561622
env->SetMethod(target, "_getActiveRequests", GetActiveRequests);
623+
env->SetMethod(target, "_getActiveRequests2", GetActiveRequests2);
562624
env->SetMethod(target, "_getActiveHandles", GetActiveHandles);
625+
env->SetMethod(target, "_getActiveHandles2", GetActiveHandles2);
563626
env->SetMethod(target, "_kill", Kill);
564627

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

588651
registry->Register(GetActiveRequests);
652+
registry->Register(GetActiveRequests2);
589653
registry->Register(GetActiveHandles);
654+
registry->Register(GetActiveHandles2);
590655
registry->Register(Kill);
591656

592657
registry->Register(Cwd);

0 commit comments

Comments
 (0)