Skip to content

Commit

Permalink
src: add commands to inspect the workqueue
Browse files Browse the repository at this point in the history
Add two new commands: `v8 getactivehandles` and `v8 getactiverequests`.
These comamnds will print all pending handles and requests. The result
should be similar to running process._getActiveHandles() and
process._getActiveRequests() on the living process.

PR-URL: #210
Fixes: #100
Reviewed-By: Joyee Cheung <[email protected]>
  • Loading branch information
Matheus Marchini committed Jul 9, 2018
1 parent 775dd6c commit baf4cae
Show file tree
Hide file tree
Showing 11 changed files with 482 additions and 4 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,12 @@ The following subcommands are supported:
* -n, --name name - all properties with the specified name
* -s, --string string - all properties that refer to the specified JavaScript string value
getactivehandles -- Print all pending handles in the queue. Equivalent to running process._getActiveHandles() on
the living process.
getactiverequests -- Print all pending handles in the queue. Equivalent to running process._getActiveHandles() on
the living process.
inspect -- Print detailed description and contents of the JavaScript value.
Possible flags (all optional):
Expand Down
1 change: 1 addition & 0 deletions binding.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
"src/llv8.cc",
"src/llv8-constants.cc",
"src/llscan.cc",
"src/node.cc",
"src/node-constants.cc",
],
"conditions": [
Expand Down
108 changes: 108 additions & 0 deletions src/llnode.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@
#include <string.h>

#include <cinttypes>
#include <sstream>
#include <string>

#include <lldb/API/SBExpressionOptions.h>

#include "src/error.h"
#include "src/llnode.h"
#include "src/llscan.h"
#include "src/llv8.h"
#include "src/node-inl.h"

namespace llnode {

Expand Down Expand Up @@ -245,6 +248,100 @@ bool ListCmd::DoExecute(SBDebugger d, char** cmd,
return true;
}

bool WorkqueueCmd::DoExecute(SBDebugger d, char** cmd,
SBCommandReturnObject& result) {
SBTarget target = d.GetSelectedTarget();
SBThread thread = target.GetProcess().GetSelectedThread();
if (!thread.IsValid()) {
result.SetError("No valid process, please start something\n");
return false;
}

std::string result_message;
Error err;

llv8_->Load(target);
node_->Load(target);

node::Environment env = node::Environment::GetCurrent(node_, err);
if (err.Fail()) {
result.SetError(err.GetMessage());
return false;
}

result_message = GetResultMessage(&env, err);
if (err.Fail()) {
result.SetError(err.GetMessage());
return false;
}

result.Printf("%s", result_message.c_str());
return true;
}

std::string GetActiveHandlesCmd::GetResultMessage(node::Environment* env,
Error& err) {
int active_handles = 0;
v8::Value::InspectOptions inspect_options;
inspect_options.detailed = true;
std::ostringstream result_message;

for (auto w : env->handle_wrap_queue()) {
addr_t persistent = w.Persistent(err);
if (err.Fail()) break;
if (persistent == 0) continue;

addr_t raw_object = w.Object(err);
if (err.Fail()) break;

v8::JSObject v8_object(llv8(), raw_object);
std::string res = v8_object.Inspect(&inspect_options, err);
if (err.Fail()) {
Error::PrintInDebugMode("Failed to load object at address %" PRIx64,
raw_object);
break;
}

active_handles++;
result_message << res.c_str() << std::endl;
}

result_message << "Total: " << active_handles << std::endl;
return result_message.str();
}


std::string GetActiveRequestsCmd::GetResultMessage(node::Environment* env,
Error& err) {
int active_requests = 0;
v8::Value::InspectOptions inspect_options;
inspect_options.detailed = true;
std::ostringstream result_message;

for (auto w : env->req_wrap_queue()) {
addr_t persistent = w.Persistent(err);
if (err.Fail()) break;
if (persistent == 0) continue;

addr_t raw_object = w.Object(err);
if (err.Fail()) break;

v8::JSObject v8_object(llv8(), raw_object);
std::string res = v8_object.Inspect(&inspect_options, err);
if (err.Fail()) {
Error::PrintInDebugMode("Failed to load object at address %" PRIx64,
raw_object);
break;
}

active_requests++;
result_message << res.c_str() << std::endl;
}

result_message << "Total: " << active_requests << std::endl;
return result_message.str();
}


void InitDebugMode() {
bool is_debug_mode = false;
Expand All @@ -264,6 +361,7 @@ bool PluginInitialize(SBDebugger d) {
llnode::InitDebugMode();

static llnode::v8::LLV8 llv8;
static llnode::node::Node node(&llv8);
static llnode::LLScan llscan = llnode::LLScan(&llv8);

SBCommandInterpreter interpreter = d.GetCommandInterpreter();
Expand Down Expand Up @@ -349,6 +447,16 @@ bool PluginInitialize(SBDebugger d) {
"JavaScript string value\n"
"\n");

v8.AddCommand("getactivehandles",
new llnode::GetActiveHandlesCmd(&llv8, &node),
"Print all pending handles in the queue. Equivalent to running "
"process._getActiveHandles() on the living process.\n");

v8.AddCommand(
"getactiverequests", new llnode::GetActiveRequestsCmd(&llv8, &node),
"Print all pending requests in the queue. Equivalent to "
"running process._getActiveRequests() on the living process.\n");

return true;
}

Expand Down
41 changes: 39 additions & 2 deletions src/llnode.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
#include <lldb/API/LLDB.h>

#include "src/llv8.h"
#include "src/node.h"

namespace llnode {

class CommandBase : public lldb::SBCommandPluginInterface {
};
class CommandBase : public lldb::SBCommandPluginInterface {};

class BacktraceCmd : public CommandBase {
public:
Expand Down Expand Up @@ -50,6 +50,43 @@ class ListCmd : public CommandBase {
v8::LLV8* llv8_;
};

class WorkqueueCmd : public CommandBase {
public:
WorkqueueCmd(v8::LLV8* llv8, node::Node* node) : llv8_(llv8), node_(node) {}
~WorkqueueCmd() override {}

inline v8::LLV8* llv8() { return llv8_; };
inline node::Node* node() { return node_; };

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

virtual std::string GetResultMessage(node::Environment* env, Error& err) {
return std::string();
};

private:
v8::LLV8* llv8_;
node::Node* node_;
};

class GetActiveHandlesCmd : public WorkqueueCmd {
public:
GetActiveHandlesCmd(v8::LLV8* llv8, node::Node* node)
: WorkqueueCmd(llv8, node) {}

std::string GetResultMessage(node::Environment* env, Error& err) override;
};

class GetActiveRequestsCmd : public WorkqueueCmd {
public:
GetActiveRequestsCmd(v8::LLV8* llv8, node::Node* node)
: WorkqueueCmd(llv8, node) {}

std::string GetResultMessage(node::Environment* env, Error& err) override;
};


} // namespace llnode

#endif // SRC_LLNODE_H_
3 changes: 2 additions & 1 deletion src/node-constants.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ void Environment::Load() {
kHandleWrapQueueOffset = LoadConstant(
"offset_Environment__handle_wrap_queue___Environment_HandleWrapQueue");
kEnvContextEmbedderDataIndex =
LoadConstant("const_Environment__kContextEmbedderDataIndex__int");
LoadConstant("const_Environment__kContextEmbedderDataIndex__int",
"const_ContextEmbedderIndex__kEnvironment__int");

Error err;
kCurrentEnvironment = LoadCurrentEnvironment(err);
Expand Down
38 changes: 38 additions & 0 deletions src/node-inl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#include "node.h"

namespace llnode {
namespace node {

template <typename T, typename C>
T Queue<T, C>::Iterator::operator*() const {
return T::GetItemFromList(node_, current_);
}

template <typename T, typename C>
const typename Queue<T, C>::Iterator Queue<T, C>::Iterator::operator++() {
lldb::SBError sberr;

current_ = node_->process().ReadPointerFromMemory(
current_ + constants_->kNextOffset, sberr);
return Iterator(node_, current_, constants_);
}

template <typename T, typename C>
bool Queue<T, C>::Iterator::operator!=(const Iterator& that) const {
return current_ != that.current_;
}

template <typename T, typename C>
typename Queue<T, C>::Iterator Queue<T, C>::begin() const {
lldb::SBError sberr;

addr_t first = node_->process().ReadPointerFromMemory(next(head()), sberr);
return Iterator(node_, first, constants_);
}

template <typename T, typename C>
typename Queue<T, C>::Iterator Queue<T, C>::end() const {
return Iterator(node_, head(), constants_);
}
} // namespace node
} // namespace llnode
76 changes: 76 additions & 0 deletions src/node.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#include "node.h"

namespace llnode {
namespace node {

addr_t BaseObject::Persistent(Error& err) {
lldb::SBError sberr;

addr_t persistent_ptr = raw_ + node_->base_object()->kPersistentHandleOffset;
addr_t persistent =
node_->process().ReadPointerFromMemory(persistent_ptr, sberr);
if (sberr.Fail()) {
err = Error::Failure("Failed to load persistent handle");
return 0;
}
return persistent;
}

addr_t BaseObject::Object(Error& err) {
lldb::SBError sberr;

addr_t persistent = Persistent(err);
addr_t obj = node_->process().ReadPointerFromMemory(persistent, sberr);
if (sberr.Fail()) {
err = Error::Failure("Failed to load object from persistent handle");
return 0;
}
return obj;
}

HandleWrap HandleWrap::GetItemFromList(Node* node, addr_t list_node_addr) {
return HandleWrap(node,
list_node_addr - node->handle_wrap()->kListNodeOffset);
}

ReqWrap ReqWrap::GetItemFromList(Node* node, addr_t list_node_addr) {
return ReqWrap(node, list_node_addr - node->req_wrap()->kListNodeOffset);
}

Environment Environment::GetCurrent(Node* node, Error& err) {
addr_t envAddr = node->env()->kCurrentEnvironment;
if (envAddr == 0) {
err = Error::Failure("Couldn't get node's Environment");
}

return Environment(node, envAddr);
}

HandleWrapQueue Environment::handle_wrap_queue() const {
return HandleWrapQueue(node_, raw_ + node_->env()->kHandleWrapQueueOffset,
node_->handle_wrap_queue());
}

ReqWrapQueue Environment::req_wrap_queue() const {
return ReqWrapQueue(node_, raw_ + node_->env()->kReqWrapQueueOffset,
node_->req_wrap_queue());
}

void Node::Load(SBTarget target) {
// Reload process anyway
process_ = target.GetProcess();

// No need to reload
if (target_ == target) return;

target_ = target;

env.Assign(target);
req_wrap_queue.Assign(target);
req_wrap.Assign(target);
handle_wrap_queue.Assign(target);
handle_wrap.Assign(target);
base_object.Assign(target);
}
} // namespace node
} // namespace llnode
Loading

0 comments on commit baf4cae

Please sign in to comment.