Skip to content

Commit 7d9d1e1

Browse files
author
Matheus Marchini
committed
src: add commands to inspect the workqueue
Added two new commands (getactivehandles and getactiverequests) which prints all pending handles and requests (same return given by process._getActiveHandles() and process._getActiveRequests()). Those changes were built upon the symbols added on nodejs/node#14901, which means it's currently not working with node's latest build. Fixes: nodejs#100 Ref: nodejs/node#14901
1 parent 75c6e9a commit 7d9d1e1

File tree

5 files changed

+252
-37
lines changed

5 files changed

+252
-37
lines changed

README.md

+38-36
Original file line numberDiff line numberDiff line change
@@ -219,42 +219,44 @@ Syntax: v8
219219
220220
The following subcommands are supported:
221221
222-
bt -- Show a backtrace with node.js JavaScript functions and their args. An optional argument is accepted; if
223-
that argument is a number, it specifies the number of frames to display. Otherwise all frames will be
224-
dumped.
225-
226-
Syntax: v8 bt [number]
227-
findjsinstances -- List all objects which share the specified map.
228-
Accepts the same options as `v8 inspect`
229-
findjsobjects -- List all object types and instance counts grouped by map and sorted by instance count.
230-
Requires `LLNODE_RANGESFILE` environment variable to be set to a file containing memory ranges for the
231-
core file being debugged.
232-
There are scripts for generating this file on Linux and Mac in the scripts directory of the llnode
233-
repository.
234-
findrefs -- Finds all the object properties which meet the search criteria.
235-
The default is to list all the object properties that reference the specified value.
236-
Flags:
237-
238-
* -v, --value expr - all properties that refer to the specified JavaScript object (default)
239-
* -n, --name name - all properties with the specified name
240-
* -s, --string string - all properties that refer to the specified JavaScript string value
241-
* --array-length num - print maximum of `num` elements in array
242-
243-
inspect -- Print detailed description and contents of the JavaScript value.
244-
245-
Possible flags (all optional):
246-
247-
* -F, --full-string - print whole string without adding ellipsis
248-
* -m, --print-map - print object's map address
249-
* -s, --print-source - print source code for function objects
250-
* --string-length num - print maximum of `num` characters in string
251-
252-
Syntax: v8 inspect [flags] expr
253-
nodeinfo -- Print information about Node.js
254-
print -- Print short description of the JavaScript value.
255-
256-
Syntax: v8 print expr
257-
source -- Source code information
222+
bt -- Show a backtrace with node.js JavaScript functions and their args. An optional argument is accepted; if
223+
that argument is a number, it specifies the number of frames to display. Otherwise all frames will be
224+
dumped.
225+
226+
Syntax: v8 bt [number]
227+
findjsinstances -- List all objects which share the specified map.
228+
Accepts the same options as `v8 inspect`
229+
findjsobjects -- List all object types and instance counts grouped by map and sorted by instance count.
230+
Requires `LLNODE_RANGESFILE` environment variable to be set to a file containing memory ranges for the
231+
core file being debugged.
232+
There are scripts for generating this file on Linux and Mac in the scripts directory of the llnode
233+
repository.
234+
findrefs -- Finds all the object properties which meet the search criteria.
235+
The default is to list all the object properties that reference the specified value.
236+
Flags:
237+
238+
* -v, --value expr - all properties that refer to the specified JavaScript object (default)
239+
* -n, --name name - all properties with the specified name
240+
* -s, --string string - all properties that refer to the specified JavaScript string value
241+
* --array-length num - print maximum of `num` elements in array
242+
243+
getactivehandles -- *EXPERIMENTAL* Equivalent to running process._getActiveHandles. This command is still being developed and for now it only works building node from source.
244+
getactiverequests -- *EXPERIMENTAL* Equivalent to running process._getActiveRequests. This command is still being developed and for now it only works building node from source.
245+
inspect -- Print detailed description and contents of the JavaScript value.
246+
247+
Possible flags (all optional):
248+
249+
* -F, --full-string - print whole string without adding ellipsis
250+
* -m, --print-map - print object's map address
251+
* -s, --print-source - print source code for function objects
252+
* --string-length num - print maximum of `num` characters in string
253+
254+
Syntax: v8 inspect [flags] expr
255+
nodeinfo -- Print information about Node.js
256+
print -- Print short description of the JavaScript value.
257+
258+
Syntax: v8 print expr
259+
source -- Source code information
258260
259261
For more help on any particular subcommand, type 'help <command> <subcommand>'.
260262
```

src/llnode.cc

+191
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@
44
#include <string.h>
55

66
#include <cinttypes>
7+
#include <iostream>
8+
#include <sstream>
79

810
#include <lldb/API/SBExpressionOptions.h>
911

1012
#include "src/llnode.h"
1113
#include "src/llscan.h"
1214
#include "src/llv8.h"
15+
#include "src/llv8-constants.h"
1316

1417
namespace llnode {
1518

@@ -26,6 +29,12 @@ using lldb::SBThread;
2629
using lldb::SBValue;
2730
using lldb::eReturnStatusFailed;
2831
using lldb::eReturnStatusSuccessFinishResult;
32+
using lldb::SBProcess;
33+
using lldb::SBAddress;
34+
using lldb::SBSymbolContext;
35+
using lldb::SBSymbolContextList;
36+
using lldb::addr_t;
37+
using v8::constants::LookupConstant;
2938

3039
v8::LLV8 llv8;
3140

@@ -296,6 +305,176 @@ bool ListCmd::DoExecute(SBDebugger d, char** cmd,
296305
return true;
297306
}
298307

308+
bool GetActiveHandlesCmd::DoExecute(SBDebugger d, char** cmd,
309+
SBCommandReturnObject& result) {
310+
SBTarget target = d.GetSelectedTarget();
311+
SBProcess process = target.GetProcess();
312+
SBThread thread = process.GetSelectedThread();
313+
SBError sberr;
314+
std::ostringstream resultMsg;
315+
v8::Value::InspectOptions inspect_options;
316+
inspect_options.detailed = true;
317+
318+
llv8.Load(target);
319+
320+
int size = 8; // TODO size is arch-dependent
321+
int64_t envPtr = 0;
322+
uint64_t env = 0;
323+
int64_t queue = 0;
324+
int64_t head = 0;
325+
int64_t next = 0;
326+
int64_t node = 0;
327+
int64_t persistant_handle = 0;
328+
v8::Error err2;
329+
330+
envPtr = LookupConstant(target, "nodedbg_currentEnvironment", envPtr, err2);
331+
process.ReadMemory(envPtr, &env, size, sberr);
332+
333+
queue = LookupConstant(target, "nodedbg_class__Environment__handleWrapQueue", queue, err2);
334+
head = LookupConstant(target, "nodedbg_class__HandleWrapQueue__headOffset", head, err2);
335+
next = LookupConstant(target, "nodedbg_class__HandleWrapQueue__nextOffset", next, err2);
336+
node = LookupConstant(target, "nodedbg_class__HandleWrap__node", node, err2);
337+
persistant_handle = LookupConstant(target, "nodedbg_class__BaseObject__persistant_handle", persistant_handle, err2);
338+
339+
// uint8_t *buffer = new uint8_t[size];
340+
// XXX Ozadia time
341+
uint64_t buffer = 0;
342+
bool go=true;
343+
if (!thread.IsValid()) {
344+
result.SetError("No valid process, please start something\n");
345+
return false;
346+
}
347+
348+
int activeHandles = 0;
349+
uint64_t currentNode = env;
350+
currentNode += queue; // env.handle_wrap_queue_
351+
currentNode += head; // env.handle_wrap_queue_.head_
352+
currentNode += next; // env.handle_wrap_queue_.head_.next_
353+
process.ReadMemory(currentNode, &buffer, size, sberr);
354+
currentNode = buffer;
355+
// TODO needs a stop condition, currently it's being stopped by a break
356+
while(go) {
357+
addr_t myMemory = currentNode;
358+
myMemory = myMemory - node; // wrap
359+
myMemory += persistant_handle;
360+
// w->persistent().IsEmpty()
361+
if(myMemory == 0) {
362+
continue;
363+
}
364+
365+
process.ReadMemory(myMemory, &buffer, size, sberr);
366+
myMemory = buffer;
367+
process.ReadMemory(myMemory, &buffer, size, sberr);
368+
// TODO needs a better check
369+
if(sberr.Fail()) {
370+
break;
371+
}
372+
373+
v8::JSObject v8_object(&llv8, buffer);
374+
v8::Error err;
375+
std::string res = v8_object.Inspect(&inspect_options, err);
376+
if (err.Fail()) {
377+
// result.SetError("Failed to evaluate expression");
378+
break;
379+
}
380+
381+
activeHandles++;
382+
resultMsg << res.c_str() << std::endl;
383+
384+
currentNode += next; // env.handle_wrap_queue_.head_.next_->next_->(...)->next_
385+
process.ReadMemory(currentNode, &buffer, size, sberr);
386+
currentNode = buffer;
387+
}
388+
result.Printf("Active handles: %d\n\n", activeHandles);
389+
result.Printf("%s", resultMsg.str().c_str());
390+
return true;
391+
}
392+
393+
bool GetActiveRequestsCmd::DoExecute(SBDebugger d, char** cmd,
394+
SBCommandReturnObject& result) {
395+
SBTarget target = d.GetSelectedTarget();
396+
SBProcess process = target.GetProcess();
397+
SBThread thread = process.GetSelectedThread();
398+
SBError sberr;
399+
std::ostringstream resultMsg;
400+
v8::Value::InspectOptions inspect_options;
401+
inspect_options.detailed = true;
402+
403+
llv8.Load(target);
404+
405+
int size = 8; // TODO size is arch-dependent
406+
int64_t envPtr = 0;
407+
uint64_t env = 0;
408+
int64_t queue = 0;
409+
int64_t head = 0;
410+
int64_t next = 0;
411+
int64_t node = 0;
412+
int64_t persistant_handle = 0;
413+
v8::Error err2;
414+
415+
envPtr = LookupConstant(target, "nodedbg_currentEnvironment", envPtr, err2);
416+
process.ReadMemory(envPtr, &env, size, sberr);
417+
418+
queue = LookupConstant(target, "nodedbg_class__Environment__reqWrapQueue", queue, err2);
419+
head = LookupConstant(target, "nodedbg_class__ReqWrapQueue__headOffset", head, err2);
420+
next = LookupConstant(target, "nodedbg_class__ReqWrapQueue__nextOffset", next, err2);
421+
node = LookupConstant(target, "nodedbg_class__ReqWrap__node", node, err2);
422+
persistant_handle = LookupConstant(target, "nodedbg_class__BaseObject__persistant_handle", persistant_handle, err2);
423+
424+
// uint8_t *buffer = new uint8_t[size];
425+
// XXX Ozadia time
426+
uint64_t buffer = 0;
427+
bool go=true;
428+
if (!thread.IsValid()) {
429+
result.SetError("No valid process, please start something\n");
430+
return false;
431+
}
432+
433+
int activeHandles = 0;
434+
uint64_t currentNode = env;
435+
currentNode += queue; // env.handle_wrap_queue_
436+
currentNode += head; // env.handle_wrap_queue_.head_
437+
currentNode += next; // env.handle_wrap_queue_.head_.next_
438+
process.ReadMemory(currentNode, &buffer, size, sberr);
439+
currentNode = buffer;
440+
// TODO needs a stop condition
441+
while(go) {
442+
addr_t myMemory = currentNode;
443+
myMemory = myMemory - node;
444+
myMemory += persistant_handle;
445+
// w->persistent().IsEmpty()
446+
if(myMemory == 0) {
447+
continue;
448+
}
449+
450+
process.ReadMemory(myMemory, &buffer, size, sberr);
451+
myMemory = buffer;
452+
process.ReadMemory(myMemory, &buffer, size, sberr);
453+
// TODO needs a better check
454+
if(sberr.Fail()) {
455+
break;
456+
}
457+
458+
v8::JSObject v8_object(&llv8, buffer);
459+
v8::Error err;
460+
std::string res = v8_object.Inspect(&inspect_options, err);
461+
if (err.Fail()) {
462+
// result.SetError("Failed to evaluate expression");
463+
break;
464+
}
465+
466+
activeHandles++;
467+
resultMsg << res.c_str() << std::endl;
468+
469+
currentNode += next; // env.handle_wrap_queue_.head_.next_->next_->(...)->next_
470+
process.ReadMemory(currentNode, &buffer, size, sberr);
471+
currentNode = buffer;
472+
}
473+
result.Printf("Active handles: %d\n\n", activeHandles);
474+
result.Printf("%s", resultMsg.str().c_str());
475+
return true;
476+
}
477+
299478
} // namespace llnode
300479

301480
namespace lldb {
@@ -379,6 +558,18 @@ bool PluginInitialize(SBDebugger d) {
379558
"JavaScript string value\n"
380559
"\n");
381560

561+
v8.AddCommand(
562+
"getactivehandles", new llnode::GetActiveHandlesCmd(),
563+
"*EXPERIMENTAL* Equivalent to running process._getActiveHandles. "
564+
"This command is still being developed and for now it only works "
565+
"building node from source.\n");
566+
567+
v8.AddCommand(
568+
"getactiverequests", new llnode::GetActiveRequestsCmd(),
569+
"*EXPERIMENTAL* Equivalent to running process._getActiveRequests. "
570+
"This command is still being developed and for now it only works "
571+
"building node from source.\n");
572+
382573
return true;
383574
}
384575

src/llnode.h

+16
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,22 @@ class PrintCmd : public CommandBase {
3535
bool detailed_;
3636
};
3737

38+
class GetActiveHandlesCmd : public CommandBase {
39+
public:
40+
~GetActiveHandlesCmd() override {}
41+
42+
bool DoExecute(lldb::SBDebugger d, char** cmd,
43+
lldb::SBCommandReturnObject& result) override;
44+
};
45+
46+
class GetActiveRequestsCmd : public CommandBase {
47+
public:
48+
~GetActiveRequestsCmd() override {}
49+
50+
bool DoExecute(lldb::SBDebugger d, char** cmd,
51+
lldb::SBCommandReturnObject& result) override;
52+
};
53+
3854
class ListCmd : public CommandBase {
3955
public:
4056
~ListCmd() override {}

src/llv8-constants.cc

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ void Module::Assign(SBTarget target, Common* common) {
3838
}
3939

4040

41-
static int64_t LookupConstant(SBTarget target, const char* name, int64_t def,
41+
int64_t LookupConstant(SBTarget target, const char* name, int64_t def,
4242
Error& err) {
4343
int64_t res;
4444

src/llv8-constants.h

+6
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,16 @@
33

44
#include <lldb/API/LLDB.h>
55

6+
using lldb::SBTarget;
7+
68
namespace llnode {
79
namespace v8 {
10+
class Error;
811
namespace constants {
912

13+
int64_t LookupConstant(SBTarget target, const char* name, int64_t def,
14+
Error& err);
15+
1016
// Forward declarations
1117
class Common;
1218

0 commit comments

Comments
 (0)