Skip to content
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
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