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: #195

PR-URL: #227
Reviewed-By: Matheus Marchini <[email protected]>
Reviewed-By: Joyee Cheung <[email protected]>
  • Loading branch information
Drieger authored and joyeecheung committed Sep 25, 2018
1 parent 816c46c commit 089dcc9
Show file tree
Hide file tree
Showing 7 changed files with 210 additions and 38 deletions.
51 changes: 51 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,39 @@ 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, err);
if (err.Fail()) return;

for (v8::Context::Locals::Iterator it = locals.begin(); it != locals.end();
it++) {
if ((*it).raw() == search_value_.raw()) {
v8::String _name = it.LocalName(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 +1230,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
67 changes: 45 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,43 @@ 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* context, Error& err) {
context_ = context;
HeapObject scope_obj = context_->GetScopeInfo(err);
if (err.Fail()) return;

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

param_count_ = param_count_smi.GetValue();
stack_count_ = stack_count_smi.GetValue();
local_count_ = local_count_smi.GetValue();
}

SharedFunctionInfo info = closure.Info(err);
if (err.Fail()) return HeapObject();
Context::Locals::Iterator Context::Locals::begin() { return Iterator(0, this); }

return info.GetScopeInfo(err);
Context::Locals::Iterator Context::Locals::end() {
return Iterator(local_count_, this);
}

const Context::Locals::Iterator Context::Locals::Iterator::operator++(int) {
current_++;
return Iterator(current_, this->outer_);
}

bool Context::Locals::Iterator::operator!=(Context::Locals::Iterator that) {
return current_ != that.current_ || outer_->context_ != that.outer_->context_;
}

v8::Value Context::Locals::Iterator::operator*() {
Error err;
return outer_->context_->ContextSlot(current_, err);
}

std::string Context::Inspect(InspectOptions* options, Error& err) {
Expand All @@ -1093,13 +1123,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 +1168,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, err);
if (err.Fail()) return std::string();
for (v8::Context::Locals::Iterator it = locals.begin(); it != locals.end();
it++) {
String name = it.LocalName(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 = it.GetValue(err);
if (err.Fail()) return std::string();

InspectOptions val_options;
Expand Down
65 changes: 53 additions & 12 deletions src/llv8.h
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,19 @@ class NameDictionary : public FixedArray {
inline int64_t Length(Error& err);
};

class ScopeInfo : public FixedArray {
public:
V8_VALUE_DEFAULT_METHODS(ScopeInfo, FixedArray)

inline Smi ParameterCount(Error& err);
inline Smi StackLocalCount(Error& err);
inline Smi ContextLocalCount(Error& err);

inline String ContextLocalName(int index, int param_count, int stack_count,
Error& err);
inline HeapObject MaybeFunctionName(Error& err);
};

class Context : public FixedArray {
public:
V8_VALUE_DEFAULT_METHODS(Context, FixedArray)
Expand All @@ -397,23 +410,51 @@ 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, Locals* outer)
: current_(current), outer_(outer){};

String LocalName(Error& err) {
return outer_->scope_info_.ContextLocalName(
current_, outer_->param_count_, outer_->stack_count_, err);
}

Value GetValue(Error& err) {
return outer_->context_->ContextSlot(current_, err);
}

private:
int current_;
Locals* outer_;
};

Locals(Context* context, Error& err);

Iterator begin();
Iterator end();

private:
int local_count_;
int param_count_;
int stack_count_;
Context* context_;
ScopeInfo scope_info_;
};

private:
inline JSFunction Closure(Error& err);
};

class ScopeInfo : public FixedArray {
public:
V8_VALUE_DEFAULT_METHODS(ScopeInfo, FixedArray)

inline Smi ParameterCount(Error& err);
inline Smi StackLocalCount(Error& err);
inline Smi ContextLocalCount(Error& err);

inline String ContextLocalName(int index, int param_count, int stack_count,
Error& err);
inline HeapObject MaybeFunctionName(Error& err);
};

class Oddball : public HeapObject {
public:
Expand Down
18 changes: 17 additions & 1 deletion test/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ else
exports.llnodePath = path.join(exports.projectDir, pluginName);
exports.saveCoreTimeout = 360 * 1000;
exports.loadCoreTimeout = 60 * 1000;
exports.versionMark = /^lldb-|^lldb version/;

let versionMark = /^lldb-|^lldb version/;
exports.versionMark = versionMark;

function SessionOutput(session, stream, timeout) {
EventEmitter.call(this);
Expand Down Expand Up @@ -316,6 +318,20 @@ Session.prototype.send = function send(line, callback) {
this.lldb.stdin.write(line + '\n', callback);
};

Session.prototype.hasSymbol = function hasSymbol(symbol, callback) {
this.send('target modules dump symtab');
this.send('version');

let pattern = new RegExp(symbol);
this.linesUntil(versionMark, (err, lines) => {
if(pattern.test(lines.join('\n'))) {
callback(err, true);
} else {
return callback(err, false);
}
});
}

exports.generateRanges = function generateRanges(core, dest, cb) {
let script;
if (process.platform === 'darwin')
Expand Down
Loading

0 comments on commit 089dcc9

Please sign in to comment.