Skip to content

Commit

Permalink
src: refactor of workqueue related code
Browse files Browse the repository at this point in the history
All code related to the workqueue commands were refactored by assigning
constants in a different file and by creating helper classes to access
some node internal properties.

Besides that, a new fallback method to determine the current environment
was added. This method uses a heuristic approach to try to find the
root context of the process. With this, those new commands will work
most of the time even without the new symbols added to V8.
  • Loading branch information
Matheus Marchini committed Nov 21, 2017
1 parent 7837ce8 commit 9affb0e
Show file tree
Hide file tree
Showing 14 changed files with 666 additions and 111 deletions.
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
51 changes: 51 additions & 0 deletions src/constants.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#include "src/constants.h"
// TODO(mmarchini): This file shouldn't import llv8-constants nor llv8, as the
// intention is to inherit llv8-constants.h from constants.h
#include <iostream>
#include "src/llv8-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
169 changes: 169 additions & 0 deletions src/llnode-constants.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
#include <iostream>

#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)) {
// TODO warn user
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);
v8::Context ctx(&llv8, thread_context);
v8::Value native = ctx.Native(err);
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);
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;
}

// Load V8 constants from postmortem data
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 9affb0e

Please sign in to comment.