Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

src: add commands to inspect the workqueue #210

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -76,6 +76,7 @@
"src/llv8.cc",
"src/llv8-constants.cc",
"src/llscan.cc",
"src/node.cc",
"src/node-constants.cc",
]
}],
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