diff --git a/binding.gyp b/binding.gyp index 78864d48..0540a7ac 100644 --- a/binding.gyp +++ b/binding.gyp @@ -58,10 +58,13 @@ "target_name": "plugin", "type": "shared_library", "sources": [ + "src/constants.cc", + "src/error.cc", "src/llnode.cc", "src/llv8.cc", "src/llv8-constants.cc", "src/llscan.cc", + "src/node-constants.cc", ] }], } diff --git a/src/constants.cc b/src/constants.cc new file mode 100644 index 00000000..782f0fd6 --- /dev/null +++ b/src/constants.cc @@ -0,0 +1,121 @@ +#include + +#include + +#include "src/constants.h" + +using lldb::SBAddress; +using lldb::SBError; +using lldb::SBSymbol; +using lldb::SBSymbolContext; +using lldb::SBSymbolContextList; + +namespace llnode { + +template +T ReadSymbolFromTarget(SBTarget& target, SBAddress& start, const char* name, + Error& err) { + SBError sberr; + T res = 0; + target.ReadMemory(start, &res, sizeof(T), sberr); + if (!sberr.Fail()) { + err = Error::Ok(); + } else { + err = Error::Failure("Failed to read symbol %s", name); + } + return res; +} + +int64_t Constants::LookupConstant(SBTarget target, const char* name, + int64_t def, Error& err) { + int64_t res = 0; + res = def; + + SBSymbolContextList context_list = target.FindSymbols(name); + + if (!context_list.IsValid() || context_list.GetSize() == 0) { + err = Error::Failure("Failed to find symbol %s", name); + return res; + } + + SBSymbolContext context = context_list.GetContextAtIndex(0); + SBSymbol symbol = context.GetSymbol(); + if (!symbol.IsValid()) { + err = Error::Failure("Failed to fetch symbol %s from context", name); + return res; + } + + SBAddress start = symbol.GetStartAddress(); + SBAddress end = symbol.GetEndAddress(); + uint32_t size = end.GetOffset() - start.GetOffset(); + + // NOTE: size could be bigger for at the end symbols + if (size >= 8) { + res = ReadSymbolFromTarget(target, start, name, err); + } else if (size == 4) { + int32_t tmp = ReadSymbolFromTarget(target, start, name, err); + res = static_cast(tmp); + } else if (size == 2) { + int16_t tmp = ReadSymbolFromTarget(target, start, name, err); + res = static_cast(tmp); + } else if (size == 1) { + int8_t tmp = ReadSymbolFromTarget(target, start, name, err); + res = static_cast(tmp); + } else { + err = Error::Failure("Unexpected symbol size %" PRIu32 " of symbol %s", + size, name); + } + + return res; +} + +void Constants::Assign(SBTarget target) { + loaded_ = false; + target_ = target; +} + + +int64_t Constants::LoadRawConstant(const char* name, int64_t def) { + Error err; + int64_t v = Constants::LookupConstant(target_, name, def, err); + if (err.Fail()) { + Error::PrintInDebugMode( + "Failed to load raw constant %s, default to %" PRId64, name, def); + } + + return v; +} + +int64_t Constants::LoadConstant(const char* name, Error& err, int64_t def) { + int64_t v = Constants::LookupConstant( + target_, (constant_prefix() + name).c_str(), def, err); + return v; +} + +int64_t Constants::LoadConstant(const char* name, int64_t def) { + Error err; + int64_t v = LoadConstant(name, err, def); + if (err.Fail()) { + Error::PrintInDebugMode("Failed to load constant %s, default to %" PRId64, + name, def); + } + + return v; +} + +int64_t Constants::LoadConstant(const char* name, const char* fallback, + int64_t def) { + Error err; + int64_t v = LoadConstant(name, err, def); + if (err.Fail()) v = LoadConstant(fallback, err, def); + if (err.Fail()) { + Error::PrintInDebugMode( + "Failed to load constant %s, fallback %s, default to %" PRId64, name, + fallback, def); + } + + return v; +} + + +} // namespace llnode diff --git a/src/constants.h b/src/constants.h new file mode 100644 index 00000000..aefec402 --- /dev/null +++ b/src/constants.h @@ -0,0 +1,47 @@ +#ifndef SRC_CONSTANTS_H_ +#define SRC_CONSTANTS_H_ + +#include +#include + +#include "src/error.h" + +using lldb::SBTarget; + +namespace llnode { + +#define CONSTANTS_DEFAULT_METHODS(NAME) \ + inline NAME* operator()() { \ + if (loaded_) return this; \ + loaded_ = true; \ + Load(); \ + return this; \ + } + +class Constants { + public: + Constants() : loaded_(false) {} + + inline bool is_loaded() const { return loaded_; } + + void Assign(lldb::SBTarget target); + + inline virtual std::string constant_prefix() { return ""; }; + + static int64_t LookupConstant(SBTarget target, const char* name, int64_t def, + Error& err); + + protected: + int64_t LoadRawConstant(const char* name, int64_t def = -1); + int64_t LoadConstant(const char* name, Error& err, 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_; +}; + +} // namespace llnode + +#endif diff --git a/src/error.cc b/src/error.cc new file mode 100644 index 00000000..463b5cbc --- /dev/null +++ b/src/error.cc @@ -0,0 +1,46 @@ +#include + +#include "error.h" + +namespace llnode { +bool Error::is_debug_mode = false; + +Error::Error(bool failed, const char* format, ...) { + failed_ = failed; + char tmp[kMaxMessageLength]; + va_list arglist; + va_start(arglist, format); + vsnprintf(tmp, sizeof(tmp), format, arglist); + va_end(arglist); + msg_ = tmp; +} + + +void Error::PrintInDebugMode(const char* format, ...) { + if (!is_debug_mode) { + return; + } + char fmt[kMaxMessageLength]; + snprintf(fmt, sizeof(fmt), "[llv8] %s\n", format); + va_list arglist; + va_start(arglist, format); + vfprintf(stderr, fmt, arglist); + va_end(arglist); +} + + +Error Error::Failure(std::string msg) { + PrintInDebugMode("%s", msg.c_str()); + return Error(true, msg); +} + + +Error Error::Failure(const char* format, ...) { + char tmp[kMaxMessageLength]; + va_list arglist; + va_start(arglist, format); + vsnprintf(tmp, sizeof(tmp), format, arglist); + va_end(arglist); + return Error::Failure(std::string(tmp)); +} +} // namespace llnode diff --git a/src/error.h b/src/error.h new file mode 100644 index 00000000..73018c9b --- /dev/null +++ b/src/error.h @@ -0,0 +1,37 @@ +#ifndef SRC_ERROR_H_ +#define SRC_ERROR_H_ + +#include + +namespace llnode { + +class Error { + public: + Error() : failed_(false), msg_("") {} + Error(bool failed, std::string msg) : failed_(failed), msg_(msg) {} + Error(bool failed, const char* format, ...) + __attribute__((format(printf, 3, 4))); + + static inline Error Ok() { return Error(false, "ok"); } + static Error Failure(std::string msg); + static Error Failure(const char* format, ...) + __attribute__((format(printf, 1, 2))); + static void PrintInDebugMode(const char* format, ...) + __attribute__((format(printf, 1, 2))); + + inline bool Success() const { return !Fail(); } + inline bool Fail() const { return failed_; } + + inline const char* GetMessage() { return msg_.c_str(); } + + static void SetDebugMode(bool mode) { is_debug_mode = mode; } + + private: + bool failed_; + std::string msg_; + static const size_t kMaxMessageLength = 128; + static bool is_debug_mode; +}; +} // namespace llnode + +#endif diff --git a/src/llnode.cc b/src/llnode.cc index bf3bad03..10b4b7c4 100644 --- a/src/llnode.cc +++ b/src/llnode.cc @@ -7,6 +7,7 @@ #include +#include "src/error.h" #include "src/llnode.h" #include "src/llscan.h" #include "src/llv8.h" @@ -121,7 +122,7 @@ bool BacktraceCmd::DoExecute(SBDebugger d, char** cmd, const uint64_t pc = frame.GetPC(); if (!frame.GetSymbol().IsValid()) { - v8::Error err; + Error err; v8::JSFrame v8_frame(llv8_, static_cast(frame.GetFP())); std::string res = v8_frame.Inspect(true, err); if (err.Success()) { @@ -129,7 +130,7 @@ bool BacktraceCmd::DoExecute(SBDebugger d, char** cmd, res.c_str()); continue; } else { - v8::Error::PrintInDebugMode("%s", err.GetMessage()); + Error::PrintInDebugMode("%s", err.GetMessage()); } } @@ -199,7 +200,7 @@ bool PrintCmd::DoExecute(SBDebugger d, char** cmd, llv8_->Load(target); v8::Value v8_value(llv8_, value.GetValueAsSigned()); - v8::Error err; + Error err; std::string res = v8_value.Inspect(&inspect_options, err); if (err.Fail()) { result.SetError(err.GetMessage()); @@ -278,7 +279,7 @@ bool ListCmd::DoExecute(SBDebugger d, char** cmd, } // V8 frame - v8::Error err; + Error err; v8::JSFrame v8_frame(llv8_, static_cast(frame.GetFP())); const static uint32_t kDisplayLines = 4; @@ -309,7 +310,7 @@ void InitDebugMode() { is_debug_mode = true; } - v8::Error::SetDebugMode(is_debug_mode); + Error::SetDebugMode(is_debug_mode); } } // namespace llnode diff --git a/src/llscan.cc b/src/llscan.cc index be6182be..e9cfa404 100644 --- a/src/llscan.cc +++ b/src/llscan.cc @@ -10,6 +10,7 @@ #include +#include "src/error.h" #include "src/llnode.h" #include "src/llscan.h" #include "src/llv8-inl.h" @@ -177,7 +178,7 @@ bool FindInstancesCmd::DoExecute(SBDebugger d, char** cmd, TypeRecord* t = instance_it->second; for (std::set::iterator it = t->GetInstances().begin(); it != t->GetInstances().end(); ++it) { - v8::Error err; + Error err; v8::Value v8_value(llscan_->v8(), *it); std::string res = v8_value.Inspect(&inspect_options, err); result.Printf("%s\n", res.c_str()); @@ -219,7 +220,7 @@ bool NodeInfoCmd::DoExecute(SBDebugger d, char** cmd, TypeRecord* t = instance_it->second; for (std::set::iterator it = t->GetInstances().begin(); it != t->GetInstances().end(); ++it) { - v8::Error err; + Error err; // The properties object should be a JSObject v8::JSObject process_obj(llscan_->v8(), *it); @@ -487,7 +488,7 @@ void FindReferencesCmd::ScanForReferences(ObjectScanner* scanner) { for (auto const entry : mapstoinstances) { TypeRecord* typerecord = entry.second; for (uint64_t addr : typerecord->GetInstances()) { - v8::Error err; + Error err; v8::Value obj_value(llscan_->v8(), addr); v8::HeapObject heap_object(obj_value); int64_t type = heap_object.GetType(err); @@ -526,7 +527,7 @@ void FindReferencesCmd::PrintReferences(SBCommandReturnObject& result, // Walk all the object instances and handle them according to their type. TypeRecordMap mapstoinstances = llscan_->GetMapsToInstances(); for (uint64_t addr : *references) { - v8::Error err; + Error err; v8::Value obj_value(llscan_->v8(), addr); v8::HeapObject heap_object(obj_value); int64_t type = heap_object.GetType(err); @@ -613,7 +614,7 @@ char** FindReferencesCmd::ParseScanOptions(char** cmd, ScanType* type) { void FindReferencesCmd::ReferenceScanner::PrintRefs( - SBCommandReturnObject& result, v8::JSObject& js_obj, v8::Error& err) { + SBCommandReturnObject& result, v8::JSObject& js_obj, Error& err) { int64_t length = js_obj.GetArrayLength(err); for (int64_t i = 0; i < length; ++i) { v8::Value v = js_obj.GetArrayElement(i, err); @@ -648,7 +649,7 @@ void FindReferencesCmd::ReferenceScanner::PrintRefs( void FindReferencesCmd::ReferenceScanner::PrintRefs( - SBCommandReturnObject& result, v8::String& str, v8::Error& err) { + SBCommandReturnObject& result, v8::String& str, Error& err) { v8::LLV8* v8 = str.v8(); int64_t repr = str.Representation(err); @@ -693,7 +694,7 @@ void FindReferencesCmd::ReferenceScanner::PrintRefs( void FindReferencesCmd::ReferenceScanner::ScanRefs(v8::JSObject& js_obj, - v8::Error& err) { + Error& err) { ReferencesVector* references; std::set already_saved; @@ -730,7 +731,7 @@ void FindReferencesCmd::ReferenceScanner::ScanRefs(v8::JSObject& js_obj, void FindReferencesCmd::ReferenceScanner::ScanRefs(v8::String& str, - v8::Error& err) { + Error& err) { ReferencesVector* references; std::set already_saved; @@ -788,7 +789,7 @@ ReferencesVector* FindReferencesCmd::ReferenceScanner::GetReferences() { void FindReferencesCmd::PropertyScanner::PrintRefs( - SBCommandReturnObject& result, v8::JSObject& js_obj, v8::Error& err) { + SBCommandReturnObject& result, v8::JSObject& js_obj, Error& err) { // (Note: We skip array elements as they don't have names.) // Walk all the properties in this object. @@ -814,7 +815,7 @@ void FindReferencesCmd::PropertyScanner::PrintRefs( void FindReferencesCmd::PropertyScanner::ScanRefs(v8::JSObject& js_obj, - v8::Error& err) { + Error& err) { // (Note: We skip array elements as they don't have names.) // Walk all the properties in this object. @@ -849,7 +850,7 @@ ReferencesVector* FindReferencesCmd::PropertyScanner::GetReferences() { void FindReferencesCmd::StringScanner::PrintRefs(SBCommandReturnObject& result, v8::JSObject& js_obj, - v8::Error& err) { + Error& err) { v8::LLV8* v8 = js_obj.v8(); int64_t length = js_obj.GetArrayLength(err); @@ -914,8 +915,7 @@ void FindReferencesCmd::StringScanner::PrintRefs(SBCommandReturnObject& result, void FindReferencesCmd::StringScanner::PrintRefs(SBCommandReturnObject& result, - v8::String& str, - v8::Error& err) { + v8::String& str, Error& err) { v8::LLV8* v8 = str.v8(); // Concatenated and sliced strings refer to other strings so @@ -984,7 +984,7 @@ void FindReferencesCmd::StringScanner::PrintRefs(SBCommandReturnObject& result, void FindReferencesCmd::StringScanner::ScanRefs(v8::JSObject& js_obj, - v8::Error& err) { + Error& err) { v8::LLV8* v8 = js_obj.v8(); ReferencesVector* references; std::set already_saved; @@ -1044,8 +1044,7 @@ void FindReferencesCmd::StringScanner::ScanRefs(v8::JSObject& js_obj, } -void FindReferencesCmd::StringScanner::ScanRefs(v8::String& str, - v8::Error& err) { +void FindReferencesCmd::StringScanner::ScanRefs(v8::String& str, Error& err) { v8::LLV8* v8 = str.v8(); ReferencesVector* references; @@ -1129,7 +1128,7 @@ FindJSObjectsVisitor::FindJSObjectsVisitor(SBTarget& target, LLScan* llscan) uint64_t FindJSObjectsVisitor::Visit(uint64_t location, uint64_t word) { v8::Value v8_value(llscan_->v8(), word); - v8::Error err; + Error err; // Test if this is SMI // Skip inspecting things that look like Smi's, they aren't objects. v8::Smi smi(v8_value); @@ -1177,7 +1176,7 @@ uint64_t FindJSObjectsVisitor::Visit(uint64_t location, uint64_t word) { void FindJSObjectsVisitor::InsertOnMapsToInstances( uint64_t word, v8::Map map, FindJSObjectsVisitor::MapCacheEntry map_info, - v8::Error& err) { + Error& err) { TypeRecord* t; auto entry = std::make_pair(map_info.type_name, nullptr); @@ -1196,7 +1195,7 @@ void FindJSObjectsVisitor::InsertOnMapsToInstances( void FindJSObjectsVisitor::InsertOnDetailedMapsToInstances( uint64_t word, v8::Map map, FindJSObjectsVisitor::MapCacheEntry map_info, - v8::Error& err) { + Error& err) { DetailedTypeRecord* t; auto type_name_with_properties = map_info.GetTypeNameWithProperties(); @@ -1223,7 +1222,7 @@ void FindJSObjectsVisitor::InsertOnDetailedMapsToInstances( } -bool FindJSObjectsVisitor::IsAHistogramType(v8::Map& map, v8::Error& err) { +bool FindJSObjectsVisitor::IsAHistogramType(v8::Map& map, Error& err) { int64_t type = map.GetType(err); if (err.Fail()) return false; @@ -1317,7 +1316,7 @@ std::string FindJSObjectsVisitor::MapCacheEntry::GetTypeNameWithProperties( bool FindJSObjectsVisitor::MapCacheEntry::Load(v8::Map map, v8::HeapObject heap_object, - v8::LLV8* llv8, v8::Error& err) { + v8::LLV8* llv8, Error& err) { // Check type first is_histogram = FindJSObjectsVisitor::IsAHistogramType(map, err); diff --git a/src/llscan.h b/src/llscan.h index 83fe74ff..9e352984 100644 --- a/src/llscan.h +++ b/src/llscan.h @@ -4,6 +4,8 @@ #include #include #include + +#include "src/error.h" #include "src/llnode.h" namespace llnode { @@ -78,13 +80,13 @@ class FindReferencesCmd : public CommandBase { virtual ReferencesVector* GetReferences() { return nullptr; }; - virtual void ScanRefs(v8::JSObject& js_obj, v8::Error& err){}; - virtual void ScanRefs(v8::String& str, v8::Error& err){}; + virtual void ScanRefs(v8::JSObject& js_obj, Error& err){}; + virtual void ScanRefs(v8::String& str, Error& err){}; virtual void PrintRefs(lldb::SBCommandReturnObject& result, - v8::JSObject& js_obj, v8::Error& err) {} + v8::JSObject& js_obj, Error& err) {} virtual void PrintRefs(lldb::SBCommandReturnObject& result, v8::String& str, - v8::Error& err) {} + Error& err) {} static const char* const property_reference_template; static const char* const array_reference_template; @@ -104,13 +106,13 @@ class FindReferencesCmd : public CommandBase { ReferencesVector* GetReferences() override; - void ScanRefs(v8::JSObject& js_obj, v8::Error& err) override; - void ScanRefs(v8::String& str, v8::Error& err) override; + void ScanRefs(v8::JSObject& js_obj, Error& err) override; + void ScanRefs(v8::String& str, Error& err) override; void PrintRefs(lldb::SBCommandReturnObject& result, v8::JSObject& js_obj, - v8::Error& err) override; + Error& err) override; void PrintRefs(lldb::SBCommandReturnObject& result, v8::String& str, - v8::Error& err) override; + Error& err) override; private: LLScan* llscan_; @@ -126,12 +128,12 @@ class FindReferencesCmd : public CommandBase { ReferencesVector* GetReferences() override; - void ScanRefs(v8::JSObject& js_obj, v8::Error& err) override; + void ScanRefs(v8::JSObject& js_obj, Error& err) override; // We only scan properties on objects not Strings, use default no-op impl // of PrintRefs for Strings. void PrintRefs(lldb::SBCommandReturnObject& result, v8::JSObject& js_obj, - v8::Error& err) override; + Error& err) override; private: LLScan* llscan_; @@ -148,13 +150,13 @@ class FindReferencesCmd : public CommandBase { ReferencesVector* GetReferences() override; - void ScanRefs(v8::JSObject& js_obj, v8::Error& err) override; - void ScanRefs(v8::String& str, v8::Error& err) override; + void ScanRefs(v8::JSObject& js_obj, Error& err) override; + void ScanRefs(v8::String& str, Error& err) override; void PrintRefs(lldb::SBCommandReturnObject& result, v8::JSObject& js_obj, - v8::Error& err) override; + Error& err) override; void PrintRefs(lldb::SBCommandReturnObject& result, v8::String& str, - v8::Error& err) override; + Error& err) override; static const char* const property_reference_template; static const char* const array_reference_template; @@ -264,17 +266,17 @@ class FindJSObjectsVisitor : MemoryVisitor { size_t max_properties = 0); bool Load(v8::Map map, v8::HeapObject heap_object, v8::LLV8* llv8, - v8::Error& err); + Error& err); }; - static bool IsAHistogramType(v8::Map& map, v8::Error& err); + static bool IsAHistogramType(v8::Map& map, Error& err); void InsertOnMapsToInstances(uint64_t word, v8::Map map, FindJSObjectsVisitor::MapCacheEntry map_info, - v8::Error& err); + Error& err); void InsertOnDetailedMapsToInstances( uint64_t word, v8::Map map, FindJSObjectsVisitor::MapCacheEntry map_info, - v8::Error& err); + Error& err); lldb::SBTarget& target_; uint32_t address_byte_size_; diff --git a/src/llv8-constants.cc b/src/llv8-constants.cc index 8b600189..2c903771 100644 --- a/src/llv8-constants.cc +++ b/src/llv8-constants.cc @@ -22,8 +22,6 @@ using lldb::SBSymbolContextList; using lldb::SBTarget; using lldb::addr_t; -static std::string kConstantPrefix = "v8dbg_"; - void Module::Assign(SBTarget target, Common* common) { loaded_ = false; target_ = target; @@ -31,107 +29,6 @@ void Module::Assign(SBTarget target, Common* common) { } -template -T ReadSymbolFromTarget(SBTarget& target, SBAddress& start, const char* name, - Error& err) { - SBError sberr; - T res = 0; - target.ReadMemory(start, &res, sizeof(T), sberr); - if (!sberr.Fail()) { - err = Error::Ok(); - } else { - err = Error::Failure("Failed to read symbol %s", name); - } - return res; -} - -static int64_t LookupConstant(SBTarget target, const char* name, int64_t def, - Error& err) { - int64_t res = 0; - res = def; - - SBSymbolContextList context_list = target.FindSymbols(name); - - if (!context_list.IsValid() || context_list.GetSize() == 0) { - err = Error::Failure("Failed to find symbol %s", name); - return res; - } - - SBSymbolContext context = context_list.GetContextAtIndex(0); - SBSymbol symbol = context.GetSymbol(); - if (!symbol.IsValid()) { - err = Error::Failure("Failed to fetch symbol %s from context", name); - return res; - } - - SBAddress start = symbol.GetStartAddress(); - SBAddress end = symbol.GetEndAddress(); - uint32_t size = end.GetOffset() - start.GetOffset(); - - // NOTE: size could be bigger for at the end symbols - if (size >= 8) { - res = ReadSymbolFromTarget(target, start, name, err); - } else if (size == 4) { - int32_t tmp = ReadSymbolFromTarget(target, start, name, err); - res = static_cast(tmp); - } else if (size == 2) { - int16_t tmp = ReadSymbolFromTarget(target, start, name, err); - res = static_cast(tmp); - } else if (size == 1) { - int8_t tmp = ReadSymbolFromTarget(target, start, name, err); - res = static_cast(tmp); - } else { - err = Error::Failure("Unexpected symbol size %" PRIu32 " of symbol %s", - size, name); - } - - return res; -} - - -int64_t Module::LoadRawConstant(const char* name, int64_t def) { - Error err; - int64_t v = LookupConstant(target_, name, def, err); - if (err.Fail()) { - Error::PrintInDebugMode( - "Failed to load raw constant %s, default to %" PRId64, name, def); - } - - return v; -} - -int64_t Module::LoadConstant(const char* name, Error& err, int64_t def) { - int64_t v = - LookupConstant(target_, (kConstantPrefix + name).c_str(), def, err); - return v; -} - -int64_t Module::LoadConstant(const char* name, int64_t def) { - Error err; - int64_t v = LoadConstant(name, err, def); - if (err.Fail()) { - Error::PrintInDebugMode("Failed to load constant %s, default to %" PRId64, - name, def); - } - - return v; -} - -int64_t Module::LoadConstant(const char* name, const char* fallback, - int64_t def) { - Error err; - int64_t v = LoadConstant(name, err, def); - if (err.Fail()) v = LoadConstant(fallback, err, def); - if (err.Fail()) { - Error::PrintInDebugMode( - "Failed to load constant %s, fallback %s, default to %" PRId64, name, - fallback, def); - } - - return v; -} - - void Common::Load() { kPointerSize = 1 << LoadConstant("PointerSizeLog2"); kVersionMajor = LoadRawConstant("v8::internal::Version::major_"); @@ -351,6 +248,15 @@ void Context::Load() { LoadConstant("class_Context__closure_index__int", "context_idx_closure"); kPreviousIndex = LoadConstant("class_Context__previous_index__int", "context_idx_prev"); + // TODO (mmarchini) change LoadConstant to accept variable arguments, a list + // of constants or a fallback list). + kNativeIndex = + LoadConstant("class_Context__native_index__int", "context_idx_native"); + if (kNativeIndex == -1) { + kNativeIndex = LoadConstant("class_Context__native_context_index__int"); + } + kEmbedderDataIndex = LoadConstant("context_idx_embedder_data", (int)5); + kMinContextSlots = LoadConstant("class_Context__min_context_slots__int", "context_min_slots"); } diff --git a/src/llv8-constants.h b/src/llv8-constants.h index a880893e..47ed84ab 100644 --- a/src/llv8-constants.h +++ b/src/llv8-constants.h @@ -3,49 +3,29 @@ #include +#include "constants.h" + namespace llnode { namespace v8 { - -class Error; - namespace constants { // Forward declarations class Common; -class Module { +class Module : public Constants { public: - Module() : loaded_(false) {} - - inline bool is_loaded() const { return loaded_; } - void Assign(lldb::SBTarget target, Common* common = nullptr); - protected: - int64_t LoadRawConstant(const char* name, int64_t def = -1); - int64_t LoadConstant(const char* name, Error& err, 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); + inline std::string constant_prefix() override { return "v8dbg_"; } - lldb::SBTarget target_; + protected: Common* common_; - bool loaded_; }; -#define MODULE_DEFAULT_METHODS(NAME) \ - NAME() {} \ - inline NAME* operator()() { \ - if (loaded_) return this; \ - loaded_ = true; \ - Load(); \ - return this; \ - } - class Common : public Module { public: - MODULE_DEFAULT_METHODS(Common); + CONSTANTS_DEFAULT_METHODS(Common); int64_t kPointerSize; int64_t kVersionMajor; @@ -61,7 +41,7 @@ class Common : public Module { class Smi : public Module { public: - MODULE_DEFAULT_METHODS(Smi); + CONSTANTS_DEFAULT_METHODS(Smi); int64_t kTag; int64_t kTagMask; @@ -73,7 +53,7 @@ class Smi : public Module { class HeapObject : public Module { public: - MODULE_DEFAULT_METHODS(HeapObject); + CONSTANTS_DEFAULT_METHODS(HeapObject); int64_t kTag; int64_t kTagMask; @@ -86,7 +66,7 @@ class HeapObject : public Module { class Map : public Module { public: - MODULE_DEFAULT_METHODS(Map); + CONSTANTS_DEFAULT_METHODS(Map); int64_t kMapTypeMask; int64_t kInstanceAttrsOffset; @@ -108,7 +88,7 @@ class Map : public Module { class JSObject : public Module { public: - MODULE_DEFAULT_METHODS(JSObject); + CONSTANTS_DEFAULT_METHODS(JSObject); int64_t kPropertiesOffset; int64_t kElementsOffset; @@ -120,7 +100,7 @@ class JSObject : public Module { class HeapNumber : public Module { public: - MODULE_DEFAULT_METHODS(HeapNumber); + CONSTANTS_DEFAULT_METHODS(HeapNumber); int64_t kValueOffset; @@ -130,7 +110,7 @@ class HeapNumber : public Module { class JSArray : public Module { public: - MODULE_DEFAULT_METHODS(JSArray); + CONSTANTS_DEFAULT_METHODS(JSArray); int64_t kLengthOffset; @@ -140,7 +120,7 @@ class JSArray : public Module { class JSFunction : public Module { public: - MODULE_DEFAULT_METHODS(JSFunction); + CONSTANTS_DEFAULT_METHODS(JSFunction); int64_t kSharedInfoOffset; int64_t kContextOffset; @@ -151,7 +131,7 @@ class JSFunction : public Module { class JSRegExp : public Module { public: - MODULE_DEFAULT_METHODS(JSRegExp); + CONSTANTS_DEFAULT_METHODS(JSRegExp); int64_t kSourceOffset; @@ -161,7 +141,7 @@ class JSRegExp : public Module { class JSDate : public Module { public: - MODULE_DEFAULT_METHODS(JSDate); + CONSTANTS_DEFAULT_METHODS(JSDate); int64_t kValueOffset; @@ -171,7 +151,7 @@ class JSDate : public Module { class SharedInfo : public Module { public: - MODULE_DEFAULT_METHODS(SharedInfo); + CONSTANTS_DEFAULT_METHODS(SharedInfo); int64_t kNameOrScopeInfoOffset; int64_t kNameOffset; @@ -192,7 +172,7 @@ class SharedInfo : public Module { class Code : public Module { public: - MODULE_DEFAULT_METHODS(Code) + CONSTANTS_DEFAULT_METHODS(Code) int64_t kStartOffset; int64_t kSizeOffset; @@ -203,7 +183,7 @@ class Code : public Module { class ScopeInfo : public Module { public: - MODULE_DEFAULT_METHODS(ScopeInfo); + CONSTANTS_DEFAULT_METHODS(ScopeInfo); int64_t kParameterCountOffset; int64_t kStackLocalCountOffset; @@ -216,11 +196,13 @@ class ScopeInfo : public Module { class Context : public Module { public: - MODULE_DEFAULT_METHODS(Context); + CONSTANTS_DEFAULT_METHODS(Context); int64_t kClosureIndex; int64_t kGlobalObjectIndex; int64_t kPreviousIndex; + int64_t kNativeIndex; + int64_t kEmbedderDataIndex; int64_t kMinContextSlots; protected: @@ -229,7 +211,7 @@ class Context : public Module { class Script : public Module { public: - MODULE_DEFAULT_METHODS(Script); + CONSTANTS_DEFAULT_METHODS(Script); int64_t kNameOffset; int64_t kLineOffsetOffset; @@ -242,7 +224,7 @@ class Script : public Module { class String : public Module { public: - MODULE_DEFAULT_METHODS(String); + CONSTANTS_DEFAULT_METHODS(String); int64_t kEncodingMask; int64_t kRepresentationMask; @@ -266,7 +248,7 @@ class String : public Module { class OneByteString : public Module { public: - MODULE_DEFAULT_METHODS(OneByteString); + CONSTANTS_DEFAULT_METHODS(OneByteString); int64_t kCharsOffset; @@ -276,7 +258,7 @@ class OneByteString : public Module { class TwoByteString : public Module { public: - MODULE_DEFAULT_METHODS(TwoByteString); + CONSTANTS_DEFAULT_METHODS(TwoByteString); int64_t kCharsOffset; @@ -286,7 +268,7 @@ class TwoByteString : public Module { class ConsString : public Module { public: - MODULE_DEFAULT_METHODS(ConsString); + CONSTANTS_DEFAULT_METHODS(ConsString); int64_t kFirstOffset; int64_t kSecondOffset; @@ -297,7 +279,7 @@ class ConsString : public Module { class SlicedString : public Module { public: - MODULE_DEFAULT_METHODS(SlicedString); + CONSTANTS_DEFAULT_METHODS(SlicedString); int64_t kParentOffset; int64_t kOffsetOffset; @@ -308,7 +290,7 @@ class SlicedString : public Module { class ThinString : public Module { public: - MODULE_DEFAULT_METHODS(ThinString); + CONSTANTS_DEFAULT_METHODS(ThinString); int64_t kActualOffset; @@ -318,7 +300,7 @@ class ThinString : public Module { class FixedArrayBase : public Module { public: - MODULE_DEFAULT_METHODS(FixedArrayBase); + CONSTANTS_DEFAULT_METHODS(FixedArrayBase); int64_t kLengthOffset; @@ -328,7 +310,7 @@ class FixedArrayBase : public Module { class FixedArray : public Module { public: - MODULE_DEFAULT_METHODS(FixedArray); + CONSTANTS_DEFAULT_METHODS(FixedArray); int64_t kDataOffset; @@ -338,7 +320,7 @@ class FixedArray : public Module { class FixedTypedArrayBase : public Module { public: - MODULE_DEFAULT_METHODS(FixedTypedArrayBase); + CONSTANTS_DEFAULT_METHODS(FixedTypedArrayBase); int64_t kBasePointerOffset; int64_t kExternalPointerOffset; @@ -349,7 +331,7 @@ class FixedTypedArrayBase : public Module { class Oddball : public Module { public: - MODULE_DEFAULT_METHODS(Oddball); + CONSTANTS_DEFAULT_METHODS(Oddball); int64_t kKindOffset; @@ -367,7 +349,7 @@ class Oddball : public Module { class JSArrayBuffer : public Module { public: - MODULE_DEFAULT_METHODS(JSArrayBuffer); + CONSTANTS_DEFAULT_METHODS(JSArrayBuffer); int64_t kKindOffset; @@ -384,7 +366,7 @@ class JSArrayBuffer : public Module { class JSArrayBufferView : public Module { public: - MODULE_DEFAULT_METHODS(JSArrayBufferView); + CONSTANTS_DEFAULT_METHODS(JSArrayBufferView); int64_t kBufferOffset; int64_t kByteOffsetOffset; @@ -396,7 +378,7 @@ class JSArrayBufferView : public Module { class DescriptorArray : public Module { public: - MODULE_DEFAULT_METHODS(DescriptorArray); + CONSTANTS_DEFAULT_METHODS(DescriptorArray); int64_t kDetailsOffset; int64_t kKeyOffset; @@ -440,7 +422,7 @@ class DescriptorArray : public Module { class NameDictionary : public Module { public: - MODULE_DEFAULT_METHODS(NameDictionary); + CONSTANTS_DEFAULT_METHODS(NameDictionary); int64_t kKeyOffset; int64_t kValueOffset; @@ -455,7 +437,7 @@ class NameDictionary : public Module { class Frame : public Module { public: - MODULE_DEFAULT_METHODS(Frame); + CONSTANTS_DEFAULT_METHODS(Frame); int64_t kContextOffset; int64_t kFunctionOffset; @@ -478,7 +460,7 @@ class Frame : public Module { class Types : public Module { public: - MODULE_DEFAULT_METHODS(Types); + CONSTANTS_DEFAULT_METHODS(Types); int64_t kFirstNonstringType; int64_t kFirstJSObjectType; @@ -506,8 +488,6 @@ class Types : public Module { void Load(); }; -#undef MODULE_DEFAULT_METHODS - } // namespace constants } // namespace v8 } // namespace llnode diff --git a/src/llv8-inl.h b/src/llv8-inl.h index 4e5ba6ed..e075360e 100644 --- a/src/llv8-inl.h +++ b/src/llv8-inl.h @@ -537,6 +537,28 @@ inline Value Context::Previous(Error& err) { return FixedArray::Get(v8()->context()->kPreviousIndex, err); } +inline Value Context::Native(Error& err) { + return FixedArray::Get(v8()->context()->kNativeIndex, err); +} + +inline bool Context::IsNative(Error& err) { + Value native = Native(err); + if (err.Fail()) { + return false; + } + return native.raw() == raw(); +} + +template +inline T Context::GetEmbedderData(int64_t index, Error& err) { + FixedArray embedder_data = FixedArray(*this).Get( + v8()->context()->kEmbedderDataIndex, err); + if (err.Fail()) { + return T(); + } + return embedder_data.Get(index, err); +} + inline Value Context::ContextSlot(int index, Error& err) { return FixedArray::Get(v8()->context()->kMinContextSlots + index, err); } diff --git a/src/llv8.cc b/src/llv8.cc index 856128a9..b43363ed 100644 --- a/src/llv8.cc +++ b/src/llv8.cc @@ -58,48 +58,6 @@ void LLV8::Load(SBTarget target) { types.Assign(target, &common); } -bool Error::is_debug_mode = false; - -Error::Error(bool failed, const char* format, ...) { - failed_ = failed; - char tmp[kMaxMessageLength]; - va_list arglist; - va_start(arglist, format); - vsnprintf(tmp, sizeof(tmp), format, arglist); - va_end(arglist); - msg_ = tmp; -} - - -void Error::PrintInDebugMode(const char* format, ...) { - if (!is_debug_mode) { - return; - } - char fmt[kMaxMessageLength]; - snprintf(fmt, sizeof(fmt), "[llv8] %s\n", format); - va_list arglist; - va_start(arglist, format); - vfprintf(stderr, fmt, arglist); - va_end(arglist); -} - - -Error Error::Failure(std::string msg) { - PrintInDebugMode("%s", msg.c_str()); - return Error(true, msg); -} - - -Error Error::Failure(const char* format, ...) { - char tmp[kMaxMessageLength]; - va_list arglist; - va_start(arglist, format); - vsnprintf(tmp, sizeof(tmp), format, arglist); - va_end(arglist); - return Error::Failure(std::string(tmp)); -} - - int64_t LLV8::LoadPtr(int64_t addr, Error& err) { SBError sberr; int64_t value = diff --git a/src/llv8.h b/src/llv8.h index 433fe469..cba33b28 100644 --- a/src/llv8.h +++ b/src/llv8.h @@ -6,10 +6,17 @@ #include +#include "src/error.h" #include "src/llv8-constants.h" namespace llnode { +namespace node { +namespace constants { +class Environment; +} +} // namespace node + class FindJSObjectsVisitor; class FindReferencesCmd; class FindObjectsCmd; @@ -20,33 +27,6 @@ namespace v8 { class LLV8; class CodeMap; -class Error { - public: - Error() : failed_(false), msg_("") {} - Error(bool failed, std::string msg) : failed_(failed), msg_(msg) {} - Error(bool failed, const char* format, ...) - __attribute__((format(printf, 3, 4))); - - static inline Error Ok() { return Error(false, "ok"); } - static Error Failure(std::string msg); - static Error Failure(const char* format, ...) - __attribute__((format(printf, 1, 2))); - static void PrintInDebugMode(const char* format, ...) - __attribute__((format(printf, 1, 2))); - - inline bool Success() const { return !Fail(); } - inline bool Fail() const { return failed_; } - - inline const char* GetMessage() { return msg_.c_str(); } - - static void SetDebugMode(bool mode) { is_debug_mode = mode; } - - private: - bool failed_; - std::string msg_; - static const size_t kMaxMessageLength = 128; - static bool is_debug_mode; -}; #define V8_VALUE_DEFAULT_METHODS(NAME, PARENT) \ NAME(const NAME& v) = default; \ @@ -403,6 +383,10 @@ class Context : public FixedArray { inline JSFunction Closure(Error& err); inline Value Previous(Error& err); + inline Value Native(Error& err); + inline bool IsNative(Error& err); + template + inline T GetEmbedderData(int64_t index, Error& err); inline Value ContextSlot(int index, Error& err); std::string Inspect(Error& err); @@ -563,6 +547,7 @@ class LLV8 { friend class llnode::FindJSObjectsVisitor; friend class llnode::FindObjectsCmd; friend class llnode::FindReferencesCmd; + friend class llnode::node::constants::Environment; }; #undef V8_VALUE_DEFAULT_METHODS diff --git a/src/node-constants.cc b/src/node-constants.cc new file mode 100644 index 00000000..b3af3806 --- /dev/null +++ b/src/node-constants.cc @@ -0,0 +1,137 @@ +#include +#include + +#include "src/llv8-inl.h" +#include "src/node-constants.h" + +using lldb::SBFrame; +using lldb::SBProcess; +using lldb::SBStream; +using lldb::SBThread; + +namespace llnode { +namespace node { +namespace constants { + +void Environment::Load() { + kReqWrapQueueOffset = LoadConstant( + "offset_Environment__req_wrap_queue___Environment_ReqWrapQueue"); + kHandleWrapQueueOffset = LoadConstant( + "offset_Environment__handle_wrap_queue___Environment_HandleWrapQueue"); + kEnvContextEmbedderDataIndex = + LoadConstant("const_Environment__kContextEmbedderDataIndex__int"); + + Error err; + kCurrentEnvironment = LoadCurrentEnvironment(err); +} + +addr_t Environment::LoadCurrentEnvironment(Error& err) { + if (kEnvContextEmbedderDataIndex == -1) { + err = Error::Failure("Missing Node's embedder data index"); + return 0; + } + addr_t current_environment = 0; + SBProcess process = target_.GetProcess(); + SBThread thread = process.GetSelectedThread(); + if (!thread.IsValid()) { + err = Error::Failure("Invalid thread"); + return 0; + } + + llv8()->Load(target_); + + uint32_t num_frames = thread.GetNumFrames(); + + // Heuristically finds the native context and gets the Environment from its + // embedder data. + for (uint32_t i = 0; i < num_frames; i++) { + SBFrame frame = thread.GetFrameAtIndex(i); + + if (!frame.GetSymbol().IsValid()) { + Error v8_err; + v8::JSFrame v8_frame(llv8(), static_cast(frame.GetFP())); + v8::JSFunction v8_function = v8_frame.GetFunction(v8_err); + if (v8_err.Fail()) { + continue; + } + v8::Value val; + val = v8_function.GetContext(v8_err); + if (v8_err.Fail()) { + continue; + } + bool found = false; + std::set visited_contexts; + while (!found) { + // Necessary to avoid an infinite loop. + if (visited_contexts.count(val.raw())) { + break; + } + visited_contexts.insert(val.raw()); + v8::Context context(val); + if (context.IsNative(err)) { + found = true; + current_environment = CurrentEnvironmentFromContext(context, err); + break; + } + + val = context.Previous(v8_err); + if (v8_err.Fail()) { + break; + } + } + if (found) { + break; + } + } + } + + if (!current_environment) { + err = + Error::Failure("Couldn't find the Environemnt from the native context"); + } + + return current_environment; +} + +addr_t Environment::CurrentEnvironmentFromContext(v8::Context context, + Error& err) { + llv8()->Load(target_); + + v8::Smi environment = + context.GetEmbedderData(kEnvContextEmbedderDataIndex, err); + if (err.Fail()) { + return 0; + } + return environment.raw(); +} + + +void ReqWrapQueue::Load() { + kHeadOffset = LoadConstant( + "offset_Environment_ReqWrapQueue__head___ListNode_ReqWrapQueue"); + kNextOffset = LoadConstant("offset_ListNode_ReqWrap__next___uintptr_t"); +} + +void ReqWrap::Load() { + kListNodeOffset = + LoadConstant("offset_ReqWrap__req_wrap_queue___ListNode_ReqWrapQueue"); +} + +void HandleWrapQueue::Load() { + kHeadOffset = LoadConstant( + "offset_Environment_HandleWrapQueue__head___ListNode_HandleWrap"); + kNextOffset = LoadConstant("offset_ListNode_HandleWrap__next___uintptr_t"); +} + +void HandleWrap::Load() { + kListNodeOffset = LoadConstant( + "offset_HandleWrap__handle_wrap_queue___ListNode_HandleWrap"); +} + +void BaseObject::Load() { + kPersistentHandleOffset = LoadConstant( + "offset_BaseObject__persistent_handle___v8_Persistent_v8_Object"); +} +} // namespace constants +} // namespace node +} // namespace llnode diff --git a/src/node-constants.h b/src/node-constants.h new file mode 100644 index 00000000..975fae89 --- /dev/null +++ b/src/node-constants.h @@ -0,0 +1,102 @@ +#ifndef SRC_NODE_CONSTANTS_H_ +#define SRC_NODE_CONSTANTS_H_ + +#include +#include "src/constants.h" +#include "src/llv8.h" + +using lldb::addr_t; + +namespace llnode { +namespace node { +namespace constants { + +#define NODE_CONSTANTS_DEFAULT_METHODS(Class) \ + Class(v8::LLV8* llv8) : Module(llv8) {} \ + CONSTANTS_DEFAULT_METHODS(Class) + + +class Module : public Constants { + public: + Module(v8::LLV8* llv8) : llv8_(llv8) {} + inline std::string constant_prefix() override { return "nodedbg_"; }; + + inline v8::LLV8* llv8() { return llv8_; } + + private: + v8::LLV8* llv8_; +}; + +class Environment : public Module { + public: + NODE_CONSTANTS_DEFAULT_METHODS(Environment); + + int64_t kReqWrapQueueOffset; + int64_t kHandleWrapQueueOffset; + int64_t kEnvContextEmbedderDataIndex; + addr_t kCurrentEnvironment; + + protected: + void Load(); + + private: + addr_t LoadCurrentEnvironment(Error& err); + addr_t CurrentEnvironmentFromContext(v8::Context context, Error& err); +}; + +class ReqWrapQueue : public Module { + public: + NODE_CONSTANTS_DEFAULT_METHODS(ReqWrapQueue); + + int64_t kHeadOffset; + int64_t kNextOffset; + + protected: + void Load(); +}; + +class ReqWrap : public Module { + public: + NODE_CONSTANTS_DEFAULT_METHODS(ReqWrap); + + int64_t kListNodeOffset; + + protected: + void Load(); +}; + +class HandleWrapQueue : public Module { + public: + NODE_CONSTANTS_DEFAULT_METHODS(HandleWrapQueue); + + int64_t kHeadOffset; + int64_t kNextOffset; + + protected: + void Load(); +}; + +class HandleWrap : public Module { + public: + NODE_CONSTANTS_DEFAULT_METHODS(HandleWrap); + + int64_t kListNodeOffset; + + protected: + void Load(); +}; + +class BaseObject : public Module { + public: + NODE_CONSTANTS_DEFAULT_METHODS(BaseObject); + + int64_t kPersistentHandleOffset; + + protected: + void Load(); +}; +} +} // namespace node +} // namespace llnode + +#endif