Skip to content

Commit

Permalink
src: add commands to inspect the workqueue
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Matheus Marchini committed Aug 17, 2017
1 parent 75c6e9a commit 7d9d1e1
Show file tree
Hide file tree
Showing 5 changed files with 252 additions and 37 deletions.
74 changes: 38 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,42 +219,44 @@ Syntax: v8
The following subcommands are supported:
bt -- Show a backtrace with node.js JavaScript functions and their args. An optional argument is accepted; if
that argument is a number, it specifies the number of frames to display. Otherwise all frames will be
dumped.
Syntax: v8 bt [number]
findjsinstances -- List all objects which share the specified map.
Accepts the same options as `v8 inspect`
findjsobjects -- List all object types and instance counts grouped by map and sorted by instance count.
Requires `LLNODE_RANGESFILE` environment variable to be set to a file containing memory ranges for the
core file being debugged.
There are scripts for generating this file on Linux and Mac in the scripts directory of the llnode
repository.
findrefs -- Finds all the object properties which meet the search criteria.
The default is to list all the object properties that reference the specified value.
Flags:
* -v, --value expr - all properties that refer to the specified JavaScript object (default)
* -n, --name name - all properties with the specified name
* -s, --string string - all properties that refer to the specified JavaScript string value
* --array-length num - print maximum of `num` elements in array
inspect -- Print detailed description and contents of the JavaScript value.
Possible flags (all optional):
* -F, --full-string - print whole string without adding ellipsis
* -m, --print-map - print object's map address
* -s, --print-source - print source code for function objects
* --string-length num - print maximum of `num` characters in string
Syntax: v8 inspect [flags] expr
nodeinfo -- Print information about Node.js
print -- Print short description of the JavaScript value.
Syntax: v8 print expr
source -- Source code information
bt -- Show a backtrace with node.js JavaScript functions and their args. An optional argument is accepted; if
that argument is a number, it specifies the number of frames to display. Otherwise all frames will be
dumped.
Syntax: v8 bt [number]
findjsinstances -- List all objects which share the specified map.
Accepts the same options as `v8 inspect`
findjsobjects -- List all object types and instance counts grouped by map and sorted by instance count.
Requires `LLNODE_RANGESFILE` environment variable to be set to a file containing memory ranges for the
core file being debugged.
There are scripts for generating this file on Linux and Mac in the scripts directory of the llnode
repository.
findrefs -- Finds all the object properties which meet the search criteria.
The default is to list all the object properties that reference the specified value.
Flags:
* -v, --value expr - all properties that refer to the specified JavaScript object (default)
* -n, --name name - all properties with the specified name
* -s, --string string - all properties that refer to the specified JavaScript string value
* --array-length num - print maximum of `num` elements in array
getactivehandles -- *EXPERIMENTAL* Equivalent to running process._getActiveHandles. This command is still being developed and for now it only works building node from source.
getactiverequests -- *EXPERIMENTAL* Equivalent to running process._getActiveRequests. This command is still being developed and for now it only works building node from source.
inspect -- Print detailed description and contents of the JavaScript value.
Possible flags (all optional):
* -F, --full-string - print whole string without adding ellipsis
* -m, --print-map - print object's map address
* -s, --print-source - print source code for function objects
* --string-length num - print maximum of `num` characters in string
Syntax: v8 inspect [flags] expr
nodeinfo -- Print information about Node.js
print -- Print short description of the JavaScript value.
Syntax: v8 print expr
source -- Source code information
For more help on any particular subcommand, type 'help <command> <subcommand>'.
```
Expand Down
191 changes: 191 additions & 0 deletions src/llnode.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@
#include <string.h>

#include <cinttypes>
#include <iostream>
#include <sstream>

#include <lldb/API/SBExpressionOptions.h>

#include "src/llnode.h"
#include "src/llscan.h"
#include "src/llv8.h"
#include "src/llv8-constants.h"

namespace llnode {

Expand All @@ -26,6 +29,12 @@ using lldb::SBThread;
using lldb::SBValue;
using lldb::eReturnStatusFailed;
using lldb::eReturnStatusSuccessFinishResult;
using lldb::SBProcess;
using lldb::SBAddress;
using lldb::SBSymbolContext;
using lldb::SBSymbolContextList;
using lldb::addr_t;
using v8::constants::LookupConstant;

v8::LLV8 llv8;

Expand Down Expand Up @@ -296,6 +305,176 @@ bool ListCmd::DoExecute(SBDebugger d, char** cmd,
return true;
}

bool GetActiveHandlesCmd::DoExecute(SBDebugger d, char** cmd,
SBCommandReturnObject& result) {
SBTarget target = d.GetSelectedTarget();
SBProcess process = target.GetProcess();
SBThread thread = process.GetSelectedThread();
SBError sberr;
std::ostringstream resultMsg;
v8::Value::InspectOptions inspect_options;
inspect_options.detailed = true;

llv8.Load(target);

int size = 8; // TODO size is arch-dependent
int64_t envPtr = 0;
uint64_t env = 0;
int64_t queue = 0;
int64_t head = 0;
int64_t next = 0;
int64_t node = 0;
int64_t persistant_handle = 0;
v8::Error err2;

envPtr = LookupConstant(target, "nodedbg_currentEnvironment", envPtr, err2);
process.ReadMemory(envPtr, &env, size, sberr);

queue = LookupConstant(target, "nodedbg_class__Environment__handleWrapQueue", queue, err2);
head = LookupConstant(target, "nodedbg_class__HandleWrapQueue__headOffset", head, err2);
next = LookupConstant(target, "nodedbg_class__HandleWrapQueue__nextOffset", next, err2);
node = LookupConstant(target, "nodedbg_class__HandleWrap__node", node, err2);
persistant_handle = LookupConstant(target, "nodedbg_class__BaseObject__persistant_handle", persistant_handle, err2);

// uint8_t *buffer = new uint8_t[size];
// XXX Ozadia time
uint64_t buffer = 0;
bool go=true;
if (!thread.IsValid()) {
result.SetError("No valid process, please start something\n");
return false;
}

int activeHandles = 0;
uint64_t currentNode = env;
currentNode += queue; // env.handle_wrap_queue_
currentNode += head; // env.handle_wrap_queue_.head_
currentNode += next; // env.handle_wrap_queue_.head_.next_
process.ReadMemory(currentNode, &buffer, size, sberr);
currentNode = buffer;
// TODO needs a stop condition, currently it's being stopped by a break
while(go) {
addr_t myMemory = currentNode;
myMemory = myMemory - node; // wrap
myMemory += persistant_handle;
// w->persistent().IsEmpty()
if(myMemory == 0) {
continue;
}

process.ReadMemory(myMemory, &buffer, size, sberr);
myMemory = buffer;
process.ReadMemory(myMemory, &buffer, size, sberr);
// TODO needs a better check
if(sberr.Fail()) {
break;
}

v8::JSObject v8_object(&llv8, buffer);
v8::Error err;
std::string res = v8_object.Inspect(&inspect_options, err);
if (err.Fail()) {
// result.SetError("Failed to evaluate expression");
break;
}

activeHandles++;
resultMsg << res.c_str() << std::endl;

currentNode += next; // env.handle_wrap_queue_.head_.next_->next_->(...)->next_
process.ReadMemory(currentNode, &buffer, size, sberr);
currentNode = buffer;
}
result.Printf("Active handles: %d\n\n", activeHandles);
result.Printf("%s", resultMsg.str().c_str());
return true;
}

bool GetActiveRequestsCmd::DoExecute(SBDebugger d, char** cmd,
SBCommandReturnObject& result) {
SBTarget target = d.GetSelectedTarget();
SBProcess process = target.GetProcess();
SBThread thread = process.GetSelectedThread();
SBError sberr;
std::ostringstream resultMsg;
v8::Value::InspectOptions inspect_options;
inspect_options.detailed = true;

llv8.Load(target);

int size = 8; // TODO size is arch-dependent
int64_t envPtr = 0;
uint64_t env = 0;
int64_t queue = 0;
int64_t head = 0;
int64_t next = 0;
int64_t node = 0;
int64_t persistant_handle = 0;
v8::Error err2;

envPtr = LookupConstant(target, "nodedbg_currentEnvironment", envPtr, err2);
process.ReadMemory(envPtr, &env, size, sberr);

queue = LookupConstant(target, "nodedbg_class__Environment__reqWrapQueue", queue, err2);
head = LookupConstant(target, "nodedbg_class__ReqWrapQueue__headOffset", head, err2);
next = LookupConstant(target, "nodedbg_class__ReqWrapQueue__nextOffset", next, err2);
node = LookupConstant(target, "nodedbg_class__ReqWrap__node", node, err2);
persistant_handle = LookupConstant(target, "nodedbg_class__BaseObject__persistant_handle", persistant_handle, err2);

// uint8_t *buffer = new uint8_t[size];
// XXX Ozadia time
uint64_t buffer = 0;
bool go=true;
if (!thread.IsValid()) {
result.SetError("No valid process, please start something\n");
return false;
}

int activeHandles = 0;
uint64_t currentNode = env;
currentNode += queue; // env.handle_wrap_queue_
currentNode += head; // env.handle_wrap_queue_.head_
currentNode += next; // env.handle_wrap_queue_.head_.next_
process.ReadMemory(currentNode, &buffer, size, sberr);
currentNode = buffer;
// TODO needs a stop condition
while(go) {
addr_t myMemory = currentNode;
myMemory = myMemory - node;
myMemory += persistant_handle;
// w->persistent().IsEmpty()
if(myMemory == 0) {
continue;
}

process.ReadMemory(myMemory, &buffer, size, sberr);
myMemory = buffer;
process.ReadMemory(myMemory, &buffer, size, sberr);
// TODO needs a better check
if(sberr.Fail()) {
break;
}

v8::JSObject v8_object(&llv8, buffer);
v8::Error err;
std::string res = v8_object.Inspect(&inspect_options, err);
if (err.Fail()) {
// result.SetError("Failed to evaluate expression");
break;
}

activeHandles++;
resultMsg << res.c_str() << std::endl;

currentNode += next; // env.handle_wrap_queue_.head_.next_->next_->(...)->next_
process.ReadMemory(currentNode, &buffer, size, sberr);
currentNode = buffer;
}
result.Printf("Active handles: %d\n\n", activeHandles);
result.Printf("%s", resultMsg.str().c_str());
return true;
}

} // namespace llnode

namespace lldb {
Expand Down Expand Up @@ -379,6 +558,18 @@ bool PluginInitialize(SBDebugger d) {
"JavaScript string value\n"
"\n");

v8.AddCommand(
"getactivehandles", new llnode::GetActiveHandlesCmd(),
"*EXPERIMENTAL* Equivalent to running process._getActiveHandles. "
"This command is still being developed and for now it only works "
"building node from source.\n");

v8.AddCommand(
"getactiverequests", new llnode::GetActiveRequestsCmd(),
"*EXPERIMENTAL* Equivalent to running process._getActiveRequests. "
"This command is still being developed and for now it only works "
"building node from source.\n");

return true;
}

Expand Down
16 changes: 16 additions & 0 deletions src/llnode.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,22 @@ class PrintCmd : public CommandBase {
bool detailed_;
};

class GetActiveHandlesCmd : public CommandBase {
public:
~GetActiveHandlesCmd() override {}

bool DoExecute(lldb::SBDebugger d, char** cmd,
lldb::SBCommandReturnObject& result) override;
};

class GetActiveRequestsCmd : public CommandBase {
public:
~GetActiveRequestsCmd() override {}

bool DoExecute(lldb::SBDebugger d, char** cmd,
lldb::SBCommandReturnObject& result) override;
};

class ListCmd : public CommandBase {
public:
~ListCmd() override {}
Expand Down
2 changes: 1 addition & 1 deletion src/llv8-constants.cc
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ void Module::Assign(SBTarget target, Common* common) {
}


static int64_t LookupConstant(SBTarget target, const char* name, int64_t def,
int64_t LookupConstant(SBTarget target, const char* name, int64_t def,
Error& err) {
int64_t res;

Expand Down
6 changes: 6 additions & 0 deletions src/llv8-constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,16 @@

#include <lldb/API/LLDB.h>

using lldb::SBTarget;

namespace llnode {
namespace v8 {
class Error;
namespace constants {

int64_t LookupConstant(SBTarget target, const char* name, int64_t def,
Error& err);

// Forward declarations
class Common;

Expand Down

0 comments on commit 7d9d1e1

Please sign in to comment.