Skip to content

Commit

Permalink
src: show context objects within findrefs
Browse files Browse the repository at this point in the history
When using `findrefs` we should be able to get all references
for the given value, this includes `Context` objects.

Refs: nodejs#195
  • Loading branch information
Drieger committed Sep 17, 2018
1 parent 816c46c commit d174865
Show file tree
Hide file tree
Showing 6 changed files with 174 additions and 23 deletions.
57 changes: 57 additions & 0 deletions src/llscan.cc
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,10 @@ void FindReferencesCmd::PrintReferences(SBCommandReturnObject& result,
// "\n", type, addr);
}
}

// Print references found directly inside Context objects
Error err;
scanner->PrintContextRefs(result, err);
}


Expand Down Expand Up @@ -668,6 +672,45 @@ char** FindReferencesCmd::ParseScanOptions(char** cmd, ScanType* type) {
return &cmd[optind - 1];
}

// Walk all contexts previously stored and print search_value_
// reference if it exists. Not all values are associated with
// a context object. It seems that Function-Local variables are
// stored in the stack, and when some nested closure references
// it is allocated in a Context object.
void FindReferencesCmd::ReferenceScanner::PrintContextRefs(
SBCommandReturnObject& result, Error& err) {
ContextVector* contexts = llscan_->GetContexts();
v8::LLV8* v8 = llscan_->v8();

for (auto ctx : *contexts) {
Error err;
v8::HeapObject context_obj(v8, ctx);
v8::Context c(context_obj);
v8::Context::Locals locals(&c);

v8::HeapObject scope_obj(locals.ctx->GetScopeInfo(err));
if (err.Fail()) return;

v8::ScopeInfo scope(scope_obj);
for (v8::Context::Locals::Iterator it = locals.begin(); it != locals.end();
it++) {
if (err.Fail()) {
continue;
}
if ((*it).raw() == search_value_.raw()) {
v8::String _name = scope.ContextLocalName(
it.current, locals.param_count, locals.stack_count, err);
if (err.Fail()) return;

std::string name = _name.ToString(err);
if (err.Fail()) return;

result.Printf("0x%" PRIx64 ": Context.%s=0x%" PRIx64 "\n", c.raw(),
name.c_str(), search_value_.raw());
}
}
}
}

void FindReferencesCmd::ReferenceScanner::PrintRefs(
SBCommandReturnObject& result, v8::JSObject& js_obj, Error& err) {
Expand Down Expand Up @@ -1193,6 +1236,20 @@ uint64_t FindJSObjectsVisitor::Visit(uint64_t location, uint64_t word) {
v8::HeapObject heap_object(v8_value);
if (!heap_object.Check()) return address_byte_size_;

bool is_context = v8::Context::IsContext(llscan_->v8(), heap_object, err);
if (err.Fail()) {
return address_byte_size_;
}

if (is_context) {
ContextVector* contexts;
contexts = llscan_->GetContexts();

if (std::find(contexts->begin(), contexts->end(), word) == contexts->end())
contexts->push_back(word);
return address_byte_size_;
}

v8::HeapObject map_object = heap_object.GetMap(err);
if (err.Fail() || !map_object.Check()) return address_byte_size_;

Expand Down
14 changes: 14 additions & 0 deletions src/llscan.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ namespace llnode {
class LLScan;

typedef std::vector<uint64_t> ReferencesVector;
typedef std::vector<uint64_t> ContextVector;

typedef std::map<uint64_t, ReferencesVector*> ReferencesByValueMap;
typedef std::map<std::string, ReferencesVector*> ReferencesByPropertyMap;
Expand Down Expand Up @@ -89,6 +90,9 @@ class FindReferencesCmd : public CommandBase {
virtual void PrintRefs(lldb::SBCommandReturnObject& result, v8::String& str,
Error& err) {}

virtual void PrintContextRefs(lldb::SBCommandReturnObject& result,
Error& err) {}

static const char* const property_reference_template;
static const char* const array_reference_template;
};
Expand All @@ -115,6 +119,9 @@ class FindReferencesCmd : public CommandBase {
void PrintRefs(lldb::SBCommandReturnObject& result, v8::String& str,
Error& err) override;

void PrintContextRefs(lldb::SBCommandReturnObject& result,
Error& err) override;

private:
LLScan* llscan_;
v8::Value search_value_;
Expand Down Expand Up @@ -315,6 +322,7 @@ class LLScan {
return references_by_value_[address];
};

// References By Property
inline bool AreReferencesByPropertyLoaded() {
return references_by_property_.size() > 0;
};
Expand All @@ -325,6 +333,7 @@ class LLScan {
return references_by_property_[property];
};

// References By String
inline bool AreReferencesByStringLoaded() {
return references_by_string_.size() > 0;
};
Expand All @@ -335,6 +344,10 @@ class LLScan {
return references_by_string_[string_value];
};

// Contexts
inline bool AreContextsLoaded() { return contexts_.size() > 0; };
inline ContextVector* GetContexts() { return &contexts_; }

v8::LLV8* llv8_;

private:
Expand Down Expand Up @@ -362,6 +375,7 @@ class LLScan {
ReferencesByValueMap references_by_value_;
ReferencesByPropertyMap references_by_property_;
ReferencesByStringMap references_by_string_;
ContextVector contexts_;
};

} // namespace llnode
Expand Down
23 changes: 22 additions & 1 deletion src/llv8-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,15 @@ inline bool Map::IsJSObjectMap(Error& err) {
return InstanceType(err) >= v8()->types()->kFirstJSObjectType;
}

inline bool Context::IsContext(LLV8* v8, HeapObject heap_object, Error& err) {
if (!heap_object.Check()) return false;

int64_t type = heap_object.GetType(err);
if (err.Fail()) return false;

return type >= v8->types()->kFirstContextType &&
type <= v8->types()->kLastContextType;
}

inline int64_t Map::InObjectProperties(Error& err) {
if (!IsJSObjectMap(err)) {
Expand Down Expand Up @@ -261,7 +270,6 @@ ACCESSOR(SharedFunctionInfo, scope_info, shared_info()->kScopeInfoOffset,
ACCESSOR(SharedFunctionInfo, name_or_scope_info,
shared_info()->kNameOrScopeInfoOffset, HeapObject)


HeapObject SharedFunctionInfo::GetScopeInfo(Error& err) {
if (v8()->shared_info()->kNameOrScopeInfoOffset == -1) return scope_info(err);

Expand Down Expand Up @@ -580,6 +588,19 @@ inline T Context::GetEmbedderData(int64_t index, Error& err) {
return embedder_data.Get<T>(index, err);
}

HeapObject Context::GetScopeInfo(Error& err) {
if (v8()->context()->kScopeInfoIndex != -1) {
return FixedArray::Get<HeapObject>(v8()->context()->kScopeInfoIndex, err);
}
JSFunction closure = Closure(err);
if (err.Fail()) return HeapObject();

SharedFunctionInfo info = closure.Info(err);
if (err.Fail()) return HeapObject();

return info.GetScopeInfo(err);
}

inline Value Context::ContextSlot(int index, Error& err) {
return FixedArray::Get<Value>(v8()->context()->kMinContextSlots + index, err);
}
Expand Down
68 changes: 46 additions & 22 deletions src/llv8.cc
Original file line number Diff line number Diff line change
Expand Up @@ -876,6 +876,10 @@ std::string HeapObject::GetTypeName(Error& err) {
if (type == v8()->types()->kMapType) {
return "(Map)";
}
if (type >= v8()->types()->kFirstContextType &&
type <= v8()->types()->kLastContextType) {
return "Context";
}

if (JSObject::IsObjectType(v8(), type)) {
v8::HeapObject map_obj = GetMap(err);
Expand Down Expand Up @@ -1058,17 +1062,44 @@ std::string FixedArray::InspectContents(int length, Error& err) {
return res;
}

HeapObject Context::GetScopeInfo(Error& err) {
if (v8()->context()->kScopeInfoIndex != -1) {
return FixedArray::Get<HeapObject>(v8()->context()->kScopeInfoIndex, err);
}
JSFunction closure = Closure(err);
if (err.Fail()) return HeapObject();
// Context locals iterator implementations
Context::Locals::Locals(Context* ctx_) {
ctx = ctx_;
Error err;
HeapObject scope_obj = ctx->GetScopeInfo(err);
if (err.Fail()) return;

ScopeInfo scope(scope_obj);
Smi param_count_smi = scope.ParameterCount(err);
if (err.Fail()) return;
Smi stack_count_smi = scope.StackLocalCount(err);
if (err.Fail()) return;
Smi local_count_smi = scope.ContextLocalCount(err);
if (err.Fail()) return;

param_count = param_count_smi.GetValue();
stack_count = stack_count_smi.GetValue();
local_count = local_count_smi.GetValue();
}

Context::Locals::Iterator Context::Locals::begin() { return Iterator(0, ctx); }

Context::Locals::Iterator Context::Locals::end() {
return Iterator(local_count, ctx);
}

SharedFunctionInfo info = closure.Info(err);
if (err.Fail()) return HeapObject();
const Context::Locals::Iterator Context::Locals::Iterator::operator++(int) {
current++;
return Iterator(current, ctx);
}

bool Context::Locals::Iterator::operator!=(Context::Locals::Iterator that) {
return current != that.current || ctx != that.ctx;
}

return info.GetScopeInfo(err);
v8::Value Context::Locals::Iterator::operator*() {
Error err;
return ctx->ContextSlot(current, err);
}

std::string Context::Inspect(InspectOptions* options, Error& err) {
Expand All @@ -1093,13 +1124,6 @@ std::string Context::Inspect(InspectOptions* options, Error& err) {

ScopeInfo scope(scope_obj);

Smi param_count_smi = scope.ParameterCount(err);
if (err.Fail()) return std::string();
Smi stack_count_smi = scope.StackLocalCount(err);
if (err.Fail()) return std::string();
Smi local_count_smi = scope.ContextLocalCount(err);
if (err.Fail()) return std::string();

HeapObject heap_previous = HeapObject(previous);
if (heap_previous.Check()) {
char tmp[128];
Expand Down Expand Up @@ -1145,19 +1169,19 @@ std::string Context::Inspect(InspectOptions* options, Error& err) {
res += ">";
}

int param_count = param_count_smi.GetValue();
int stack_count = stack_count_smi.GetValue();
int local_count = local_count_smi.GetValue();
for (int i = 0; i < local_count; i++) {
String name = scope.ContextLocalName(i, param_count, stack_count, err);
Context::Locals locals(this);
for (v8::Context::Locals::Iterator it = locals.begin(); it != locals.end();
it++) {
String name = scope.ContextLocalName(it.current, locals.param_count,
locals.stack_count, err);
if (err.Fail()) return std::string();

if (!res.empty()) res += ",\n";

res += options->get_indent_spaces() + name.ToString(err) + "=";
if (err.Fail()) return std::string();

Value value = ContextSlot(i, err);
Value value = ContextSlot(it.current, err);
if (err.Fail()) return std::string();

InspectOptions val_options;
Expand Down
29 changes: 29 additions & 0 deletions src/llv8.h
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,35 @@ class Context : public FixedArray {
inline Value ContextSlot(int index, Error& err);

std::string Inspect(InspectOptions* options, Error& err);
static inline bool IsContext(LLV8* v8, HeapObject heap_object, Error& err);

// Iterator class to walk all local references on a context
class Locals {
public:
class Iterator {
public:
Value operator*();
const Context::Locals::Iterator operator++(int);
bool operator!=(Context::Locals::Iterator that);

inline Iterator(int current, Context* ctx) : current(current), ctx(ctx){};

public:
int current;
Context* ctx;
};

Locals(Context* ctx_);

Iterator begin();
Iterator end();

public:
int local_count;
int param_count;
int stack_count;
Context* ctx;
};

private:
inline JSFunction Closure(Error& err);
Expand Down
6 changes: 6 additions & 0 deletions test/plugin/scan-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ function test(executable, core, t) {
}
t.ok(found, 'Zlib should be in findjsinstances');

sess.send("target modules dump symtab");
// Just a separator
sess.send('version');
});
Expand All @@ -83,6 +84,11 @@ function test(executable, core, t) {
t.ok(/Object\.holder/.test(lines.join('\n')), 'Should find reference #2');
t.ok(/\(Array\)\[1\]/.test(lines.join('\n')), 'Should find reference #3');

// Test if LastContextType constat exists
let patt = new RegExp("v8dbg_LastContextType");
if (patt.test(lines.join('\n'))) {
t.ok(/Context\.scopedAPI/.test(lines.join('\n')), 'Should find reference #4');
}
sess.quit();
t.end();
});
Expand Down

0 comments on commit d174865

Please sign in to comment.