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 Feb 5, 2018
1 parent 20b14d9 commit 3593860
Show file tree
Hide file tree
Showing 16 changed files with 819 additions and 4 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,11 @@ 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
<!--- TODO fix indentation for the other commands --->
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):
Expand Down
3 changes: 3 additions & 0 deletions llnode.gyp.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@
],

"sources": [
"src/constants.cc",
"src/llnode.cc",
"src/llnode-module.cc",
"src/llnode-constants.cc",
"src/llv8.cc",
"src/llv8-constants.cc",
"src/llscan.cc",
Expand Down
47 changes: 47 additions & 0 deletions src/constants.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#include "src/constants.h"
#include "src/llv8.h"

namespace llnode {
using v8::Error;
using v8::constants::IsDebugMode;
using v8::constants::LookupConstant;
namespace constants {

void ConstantsWrapper::Assign(SBTarget target) {
loaded_ = false;
target_ = target;
}

int64_t ConstantsWrapper::LoadRawConstant(const char* name, int64_t def) {
Error err;
int64_t v = LookupConstant(target_, name, def, err);
if (err.Fail() && IsDebugMode()) fprintf(stderr, "Failed to load %s\n", name);

return v;
}


int64_t ConstantsWrapper::LoadConstant(const char* name, int64_t def) {
Error err;
int64_t v =
LookupConstant(target_, (kConstantPrefix() + name).c_str(), def, err);
if (err.Fail() && IsDebugMode()) fprintf(stderr, "Failed to load %s\n", name);

return v;
}


int64_t ConstantsWrapper::LoadConstant(const char* name, const char* fallback,
int64_t def) {
Error err;
int64_t v =
LookupConstant(target_, (kConstantPrefix() + name).c_str(), def, err);
if (err.Fail())
v = LookupConstant(target_, (kConstantPrefix() + fallback).c_str(), def,
err);
if (err.Fail() && IsDebugMode()) fprintf(stderr, "Failed to load %s\n", name);

return v;
}
}
}
32 changes: 32 additions & 0 deletions src/constants.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#ifndef SRC_CONSTANTS_H_
#define SRC_CONSTANTS_H_

#include <lldb/API/LLDB.h>
using lldb::SBTarget;

namespace llnode {
namespace constants {

class ConstantsWrapper {
public:
ConstantsWrapper() : loaded_(false) {}

inline bool is_loaded() const { return loaded_; }

void Assign(lldb::SBTarget target);

inline virtual std::string kConstantPrefix() { return ""; };

protected:
int64_t LoadRawConstant(const char* name, int64_t def = -1);
int64_t LoadConstant(const char* name, int64_t def = -1);
int64_t LoadConstant(const char* name, const char* fallback,
int64_t def = -1);

lldb::SBTarget target_;
bool loaded_;
};
}
}

#endif
173 changes: 173 additions & 0 deletions src/llnode-constants.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
#include <lldb/API/LLDB.h>

#include "llnode-constants.h"
#include "llv8-constants.h"
#include "llv8-inl.h"
#include "llv8.h"

using lldb::SBProcess;
using lldb::SBThread;
using lldb::SBError;
using lldb::SBFrame;
using lldb::SBStream;

namespace llnode {
using v8::Error;
using v8::constants::LookupConstant;
namespace node {
namespace constants {
v8::LLV8 llv8;

void Environment::Load() {
kIsolate = LoadRawConstant("node::node_isolate");
kReqWrapQueueOffset = LoadConstant("class__Environment__reqWrapQueue", 1256);
kHandleWrapQueueOffset =
LoadConstant("class__Environment__handleWrapQueue", 1240);
kEnvContextEmbedderDataIndex =
LoadConstant("environment_context_idx_embedder_data", 32);
kCurrentEnvironment = LoadCurrentEnvironment();
}

addr_t Environment::LoadCurrentEnvironment() {
addr_t currentEnvironment = DefaultLoadCurrentEnvironment();
if (currentEnvironment == -1) {
currentEnvironment = FallbackLoadCurrentEnvironment();
}

return currentEnvironment;
}

addr_t Environment::DefaultLoadCurrentEnvironment() {
llv8.Load(target_);

SBProcess process = target_.GetProcess();
SBError sberr;
uint64_t env = -1;
uint64_t isolate_thread = 0;
uint64_t thread_context_ptr = 0;
uint64_t thread_context = 0;
v8::Error err;

if (!(llv8.isolate()->kThreadLocalTopOffset != -1 &&
llv8.thread_local_top()->kContextOffset != -1)) {
return env;
}

isolate_thread = kIsolate + llv8.isolate()->kThreadLocalTopOffset;

thread_context_ptr = isolate_thread + llv8.thread_local_top()->kContextOffset;
thread_context = process.ReadPointerFromMemory(thread_context_ptr, sberr);
if (sberr.Fail()) {
return -1;
}
v8::Context ctx(&llv8, thread_context);
v8::Value native = ctx.Native(err);
if (err.Fail()) {
return -1;
}
env = CurrentEnvironmentFromContext(native);
return env;
}

addr_t Environment::CurrentEnvironmentFromContext(v8::Value context) {
llv8.Load(target_);
v8::Error err;

v8::FixedArray contextArray = v8::FixedArray(context);
v8::FixedArray embed =
contextArray.Get<v8::FixedArray>(llv8.context()->kEmbedderDataIndex, err);
if (err.Fail()) {
return -1;
}
v8::Smi encodedEnv = embed.Get<v8::Smi>(kEnvContextEmbedderDataIndex, err);
if (err.Fail()) {
return -1;
} else {
return encodedEnv.raw();
}
}

addr_t Environment::FallbackLoadCurrentEnvironment() {
addr_t env = -1;
SBProcess process = target_.GetProcess();
SBThread thread = process.GetSelectedThread();
if (!thread.IsValid()) {
return -1;
}

llv8.Load(target_);

SBStream desc;
if (!thread.GetDescription(desc)) {
return -1;
}
SBFrame selected_frame = thread.GetSelectedFrame();

uint32_t num_frames = thread.GetNumFrames();
for (uint32_t i = 0; i < num_frames; i++) {
SBFrame frame = thread.GetFrameAtIndex(i);

if (!frame.GetSymbol().IsValid()) {
v8::Error err;
v8::JSFrame v8_frame(&llv8, static_cast<int64_t>(frame.GetFP()));
v8::JSFunction v8_function = v8_frame.GetFunction(err);
if (err.Fail()) {
continue;
}
v8::Value val;
val = v8_function.GetContext(err);
if (err.Fail()) {
continue;
}
bool found = false;
while (!found) {
v8::Context context(val);
v8::Value native = context.Native(err);
if (err.Success()) {
if (native.raw() == context.raw()) {
found = true;
env = CurrentEnvironmentFromContext(native);
break;
}
}

val = context.Previous(err);
if (err.Fail()) {
break;
}
}
if (found) {
break;
}
}
}

return env;
}


void ReqWrapQueue::Load() {
kHeadOffset = LoadConstant("class__ReqWrapQueue__headOffset", (int64_t)0);
kNextOffset = LoadConstant("class__ReqWrapQueue__nextOffset", (int64_t)8);
}

void ReqWrap::Load() {
kListNodeOffset = LoadConstant("class__ReqWrap__node", (int64_t)48);
}

void HandleWrapQueue::Load() {
kHeadOffset = LoadConstant("class__HandleWrapQueue__headOffset", (int64_t)0);
kNextOffset = LoadConstant("class__HandleWrapQueue__nextOffset", (int64_t)8);
}

void HandleWrap::Load() {
kListNodeOffset = LoadConstant("class__HandleWrap__node", (int64_t)48);
}

void BaseObject::Load() {
kPersistentHandleOffset =
LoadConstant("class__BaseObject__persistent_handle", (int64_t)8);
}
}
}
}
104 changes: 104 additions & 0 deletions src/llnode-constants.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#ifndef SRC_LLNODE_CONSTANTS_H_
#define SRC_LLNODE_CONSTANTS_H_

#include <lldb/API/LLDB.h>
#include "src/constants.h"
#include "src/llv8.h"

using lldb::addr_t;

namespace llnode {
using constants::ConstantsWrapper;
namespace node {
namespace constants {
#define MODULE_DEFAULT_METHODS(NAME) \
NAME() {} \
inline NAME* operator()() { \
if (loaded_) return this; \
loaded_ = true; \
Load(); \
return this; \
}


class Module : public ConstantsWrapper {
public:
inline std::string kConstantPrefix() override { return "nodedbg_"; };
};

class Environment : public Module {
public:
MODULE_DEFAULT_METHODS(Environment);

int64_t kIsolate;
int64_t kReqWrapQueueOffset;
int64_t kHandleWrapQueueOffset;
int64_t kEnvContextEmbedderDataIndex;
addr_t kCurrentEnvironment;

protected:
void Load();

private:
addr_t LoadCurrentEnvironment();
addr_t DefaultLoadCurrentEnvironment();
addr_t FallbackLoadCurrentEnvironment();
addr_t CurrentEnvironmentFromContext(v8::Value context);
};

class ReqWrapQueue : public Module {
public:
MODULE_DEFAULT_METHODS(ReqWrapQueue);

int64_t kHeadOffset;
int64_t kNextOffset;

protected:
void Load();
};

class ReqWrap : public Module {
public:
MODULE_DEFAULT_METHODS(ReqWrap);

int64_t kListNodeOffset;

protected:
void Load();
};

class HandleWrapQueue : public Module {
public:
MODULE_DEFAULT_METHODS(HandleWrapQueue);

int64_t kHeadOffset;
int64_t kNextOffset;

protected:
void Load();
};

class HandleWrap : public Module {
public:
MODULE_DEFAULT_METHODS(HandleWrap);

int64_t kListNodeOffset;

protected:
void Load();
};

class BaseObject : public Module {
public:
MODULE_DEFAULT_METHODS(BaseObject);

int64_t kPersistentHandleOffset;

protected:
void Load();
};
}
}
}

#endif
Loading

0 comments on commit 3593860

Please sign in to comment.