diff options
Diffstat (limited to 'deps/v8/tools/debug_helper')
-rw-r--r-- | deps/v8/tools/debug_helper/BUILD.gn | 3 | ||||
-rw-r--r-- | deps/v8/tools/debug_helper/debug-helper-internal.cc | 11 | ||||
-rw-r--r-- | deps/v8/tools/debug_helper/debug-helper-internal.h | 42 | ||||
-rw-r--r-- | deps/v8/tools/debug_helper/debug-helper.h | 28 | ||||
-rw-r--r-- | deps/v8/tools/debug_helper/gen-heap-constants.py | 19 | ||||
-rw-r--r-- | deps/v8/tools/debug_helper/get-object-properties.cc | 321 | ||||
-rw-r--r-- | deps/v8/tools/debug_helper/heap-constants.cc | 52 | ||||
-rw-r--r-- | deps/v8/tools/debug_helper/heap-constants.h | 47 |
8 files changed, 365 insertions, 158 deletions
diff --git a/deps/v8/tools/debug_helper/BUILD.gn b/deps/v8/tools/debug_helper/BUILD.gn index c81fddc9e5..2fe5f0d8be 100644 --- a/deps/v8/tools/debug_helper/BUILD.gn +++ b/deps/v8/tools/debug_helper/BUILD.gn @@ -100,5 +100,8 @@ v8_component("v8_debug_helper") { configs += [ "//third_party/icu:icu_config" ] } + remove_configs = [ "//build/config/compiler:no_rtti" ] + configs += [ "//build/config/compiler:rtti" ] + public_configs = [ ":external_config" ] } diff --git a/deps/v8/tools/debug_helper/debug-helper-internal.cc b/deps/v8/tools/debug_helper/debug-helper-internal.cc index ee5629b438..597ea7a639 100644 --- a/deps/v8/tools/debug_helper/debug-helper-internal.cc +++ b/deps/v8/tools/debug_helper/debug-helper-internal.cc @@ -12,15 +12,14 @@ namespace v8_debug_helper_internal { bool IsPointerCompressed(uintptr_t address) { #if COMPRESS_POINTERS_BOOL - STATIC_ASSERT(i::kPtrComprHeapReservationSize == uintptr_t{1} << 32); - intptr_t signed_address = static_cast<intptr_t>(address); - return signed_address >= INT32_MIN && signed_address <= INT32_MAX; + return address < i::kPtrComprHeapReservationSize; #else return false; #endif } -uintptr_t Decompress(uintptr_t address, uintptr_t any_uncompressed_ptr) { +uintptr_t EnsureDecompressed(uintptr_t address, + uintptr_t any_uncompressed_ptr) { if (!COMPRESS_POINTERS_BOOL || !IsPointerCompressed(address)) return address; return i::DecompressTaggedAny(any_uncompressed_ptr, static_cast<i::Tagged_t>(address)); @@ -55,4 +54,8 @@ void TqObject::Visit(TqObjectVisitor* visitor) const { visitor->VisitObject(this); } +bool TqObject::IsSuperclassOf(const TqObject* other) const { + return GetName() != other->GetName(); +} + } // namespace v8_debug_helper_internal diff --git a/deps/v8/tools/debug_helper/debug-helper-internal.h b/deps/v8/tools/debug_helper/debug-helper-internal.h index 82506c0941..e2161e25ba 100644 --- a/deps/v8/tools/debug_helper/debug-helper-internal.h +++ b/deps/v8/tools/debug_helper/debug-helper-internal.h @@ -10,6 +10,7 @@ #ifndef V8_TOOLS_DEBUG_HELPER_DEBUG_HELPER_INTERNAL_H_ #define V8_TOOLS_DEBUG_HELPER_DEBUG_HELPER_INTERNAL_H_ +#include <memory> #include <string> #include <vector> @@ -27,6 +28,7 @@ struct Value { TValue value; }; +// Internal version of API class v8::debug_helper::ObjectProperty. class ObjectProperty { public: inline ObjectProperty(std::string name, std::string type, @@ -68,15 +70,20 @@ struct ObjectPropertiesResultExtended : public d::ObjectPropertiesResult { ObjectPropertiesResultInternal* base; // Back reference for cleanup }; +// Internal version of API class v8::debug_helper::ObjectPropertiesResult. class ObjectPropertiesResult { public: - inline ObjectPropertiesResult( + ObjectPropertiesResult(d::TypeCheckResult type_check_result, + std::string brief, std::string type) + : type_check_result_(type_check_result), brief_(brief), type_(type) {} + ObjectPropertiesResult( d::TypeCheckResult type_check_result, std::string brief, std::string type, - std::vector<std::unique_ptr<ObjectProperty>> properties) - : type_check_result_(type_check_result), - brief_(brief), - type_(type), - properties_(std::move(properties)) {} + std::vector<std::unique_ptr<ObjectProperty>> properties, + std::vector<std::string> guessed_types) + : ObjectPropertiesResult(type_check_result, brief, type) { + properties_ = std::move(properties); + guessed_types_ = std::move(guessed_types); + } inline void Prepend(const char* prefix) { brief_ = prefix + brief_; } @@ -85,11 +92,17 @@ class ObjectPropertiesResult { public_view_.brief = brief_.c_str(); public_view_.type = type_.c_str(); public_view_.num_properties = properties_.size(); - properties_raw_.resize(0); + properties_raw_.clear(); for (const auto& property : properties_) { properties_raw_.push_back(property->GetPublicView()); } public_view_.properties = properties_raw_.data(); + public_view_.num_guessed_types = guessed_types_.size(); + guessed_types_raw_.clear(); + for (const auto& guess : guessed_types_) { + guessed_types_raw_.push_back(guess.c_str()); + } + public_view_.guessed_types = guessed_types_raw_.data(); public_view_.base = this; return &public_view_; } @@ -99,9 +112,11 @@ class ObjectPropertiesResult { std::string brief_; std::string type_; std::vector<std::unique_ptr<ObjectProperty>> properties_; + std::vector<std::string> guessed_types_; ObjectPropertiesResultExtended public_view_; std::vector<d::ObjectProperty*> properties_raw_; + std::vector<const char*> guessed_types_raw_; }; class TqObjectVisitor; @@ -116,13 +131,24 @@ class TqObject { d::MemoryAccessor accessor) const; virtual const char* GetName() const; virtual void Visit(TqObjectVisitor* visitor) const; + virtual bool IsSuperclassOf(const TqObject* other) const; protected: uintptr_t address_; }; +// In ptr-compr builds, returns whether the address looks like a compressed +// pointer (sign-extended from 32 bits). Otherwise returns false because no +// pointers can be compressed. bool IsPointerCompressed(uintptr_t address); -uintptr_t Decompress(uintptr_t address, uintptr_t any_uncompressed_address); + +// If the given address looks like a compressed pointer, returns a decompressed +// representation of it. Otherwise returns the address unmodified. +uintptr_t EnsureDecompressed(uintptr_t address, + uintptr_t any_uncompressed_address); + +// Converts the MemoryAccessResult from attempting to read an array's length +// into the corresponding PropertyKind for the array. d::PropertyKind GetArrayKind(d::MemoryAccessResult mem_result); } // namespace v8_debug_helper_internal diff --git a/deps/v8/tools/debug_helper/debug-helper.h b/deps/v8/tools/debug_helper/debug-helper.h index 9bbec76c7c..7d75843bf6 100644 --- a/deps/v8/tools/debug_helper/debug-helper.h +++ b/deps/v8/tools/debug_helper/debug-helper.h @@ -46,6 +46,7 @@ enum class TypeCheckResult { kSmi, kWeakRef, kUsedMap, + kKnownMapPointer, kUsedTypeHint, // Failure cases: @@ -98,6 +99,16 @@ struct ObjectPropertiesResult { const char* type; // Runtime type of the object. size_t num_properties; ObjectProperty** properties; + + // If not all relevant memory is available, GetObjectProperties may respond + // with a technically correct but uninteresting type such as HeapObject, and + // use other heuristics to make reasonable guesses about what specific type + // the object actually is. You may request data about the same object again + // using any of these guesses as the type hint, but the results should be + // formatted to the user in a way that clearly indicates that they're only + // guesses. + size_t num_guessed_types; + const char** guessed_types; }; // Copies byte_count bytes of memory from the given address in the debuggee to @@ -109,7 +120,7 @@ typedef MemoryAccessResult (*MemoryAccessor)(uintptr_t address, // Additional data that can help GetObjectProperties to be more accurate. Any // fields you don't know can be set to zero and this library will do the best it // can with the information available. -struct Roots { +struct HeapAddresses { // Beginning of allocated space for various kinds of data. These can help us // to detect certain common objects that are placed in memory during startup. // These values might be provided via name-value pairs in CrashPad dumps. @@ -119,9 +130,9 @@ struct Roots { // key stored in v8::internal::Isolate::isolate_key_. // 2. Get isolate->heap_.map_space_->memory_chunk_list_.front_ and similar for // old_space_ and read_only_space_. - uintptr_t map_space; - uintptr_t old_space; - uintptr_t read_only_space; + uintptr_t map_space_first_page; + uintptr_t old_space_first_page; + uintptr_t read_only_space_first_page; // Any valid heap pointer address. On platforms where pointer compression is // enabled, this can allow us to get data from compressed pointers even if the @@ -139,7 +150,8 @@ extern "C" { V8_DEBUG_HELPER_EXPORT v8::debug_helper::ObjectPropertiesResult* _v8_debug_helper_GetObjectProperties( uintptr_t object, v8::debug_helper::MemoryAccessor memory_accessor, - const v8::debug_helper::Roots& heap_roots, const char* type_hint); + const v8::debug_helper::HeapAddresses& heap_addresses, + const char* type_hint); V8_DEBUG_HELPER_EXPORT void _v8_debug_helper_Free_ObjectPropertiesResult( v8::debug_helper::ObjectPropertiesResult* result); } @@ -159,16 +171,16 @@ using ObjectPropertiesResultPtr = // Get information about the given object pointer, which could be: // - A tagged pointer, strong or weak // - A cleared weak pointer -// - A compressed tagged pointer, sign-extended to 64 bits +// - A compressed tagged pointer, zero-extended to 64 bits // - A tagged small integer // The type hint is only used if the object's Map is missing or corrupt. It // should be the fully-qualified name of a class that inherits from // v8::internal::Object. inline ObjectPropertiesResultPtr GetObjectProperties( uintptr_t object, v8::debug_helper::MemoryAccessor memory_accessor, - const Roots& heap_roots, const char* type_hint = nullptr) { + const HeapAddresses& heap_addresses, const char* type_hint = nullptr) { return ObjectPropertiesResultPtr(_v8_debug_helper_GetObjectProperties( - object, memory_accessor, heap_roots, type_hint)); + object, memory_accessor, heap_addresses, type_hint)); } } // namespace debug_helper diff --git a/deps/v8/tools/debug_helper/gen-heap-constants.py b/deps/v8/tools/debug_helper/gen-heap-constants.py index 0fd575a994..1d81f2e510 100644 --- a/deps/v8/tools/debug_helper/gen-heap-constants.py +++ b/deps/v8/tools/debug_helper/gen-heap-constants.py @@ -16,6 +16,9 @@ out = """ #include <cstdint> #include <string> +#include "src/common/ptr-compr-inl.h" +#include "tools/debug_helper/debug-helper-internal.h" + namespace v8_debug_helper_internal { """ @@ -51,6 +54,22 @@ def iterate_maps(target_space, camel_space_name): iterate_maps('map_space', 'MapSpace') iterate_maps('read_only_space', 'ReadOnlySpace') +out = out + '\nvoid FillInUnknownHeapAddresses(' + \ + 'd::HeapAddresses* heap_addresses, uintptr_t any_uncompressed_ptr) {\n' +if (hasattr(v8heapconst, 'HEAP_FIRST_PAGES')): # Only exists in ptr-compr builds. + out = out + ' if (heap_addresses->any_heap_pointer == 0) {\n' + out = out + ' heap_addresses->any_heap_pointer = any_uncompressed_ptr;\n' + out = out + ' }\n' + expected_spaces = set(['map_space', 'read_only_space', 'old_space']) + for offset, space_name in v8heapconst.HEAP_FIRST_PAGES.items(): + if (space_name in expected_spaces): + out = out + ' if (heap_addresses->' + space_name + '_first_page == 0) {\n' + out = out + ' heap_addresses->' + space_name + \ + '_first_page = i::DecompressTaggedPointer(any_uncompressed_ptr, ' + \ + str(offset) + ');\n' + out = out + ' }\n' +out = out + '}\n' + out = out + '\n}\n' try: diff --git a/deps/v8/tools/debug_helper/get-object-properties.cc b/deps/v8/tools/debug_helper/get-object-properties.cc index fbe992c40e..8eeeb84093 100644 --- a/deps/v8/tools/debug_helper/get-object-properties.cc +++ b/deps/v8/tools/debug_helper/get-object-properties.cc @@ -34,7 +34,7 @@ namespace v8_debug_helper_internal { V(Foreign, FOREIGN_TYPE) \ V(FreeSpace, FREE_SPACE_TYPE) \ V(HeapNumber, HEAP_NUMBER_TYPE) \ - V(JSArgumentsObject, JS_ARGUMENTS_TYPE) \ + V(JSArgumentsObject, JS_ARGUMENTS_OBJECT_TYPE) \ V(JSArray, JS_ARRAY_TYPE) \ V(JSArrayBuffer, JS_ARRAY_BUFFER_TYPE) \ V(JSArrayIterator, JS_ARRAY_ITERATOR_TYPE) \ @@ -52,8 +52,8 @@ namespace v8_debug_helper_internal { V(JSModuleNamespace, JS_MODULE_NAMESPACE_TYPE) \ V(JSPromise, JS_PROMISE_TYPE) \ V(JSProxy, JS_PROXY_TYPE) \ - V(JSRegExp, JS_REGEXP_TYPE) \ - V(JSRegExpStringIterator, JS_REGEXP_STRING_ITERATOR_TYPE) \ + V(JSRegExp, JS_REG_EXP_TYPE) \ + V(JSRegExpStringIterator, JS_REG_EXP_STRING_ITERATOR_TYPE) \ V(JSSet, JS_SET_TYPE) \ V(JSStringIterator, JS_STRING_ITERATOR_TYPE) \ V(JSTypedArray, JS_TYPED_ARRAY_TYPE) \ @@ -71,27 +71,27 @@ namespace v8_debug_helper_internal { V(PropertyCell, PROPERTY_CELL_TYPE) \ V(SharedFunctionInfo, SHARED_FUNCTION_INFO_TYPE) \ V(Symbol, SYMBOL_TYPE) \ - V(WasmExceptionObject, WASM_EXCEPTION_TYPE) \ - V(WasmGlobalObject, WASM_GLOBAL_TYPE) \ - V(WasmMemoryObject, WASM_MEMORY_TYPE) \ - V(WasmModuleObject, WASM_MODULE_TYPE) \ - V(WasmTableObject, WASM_TABLE_TYPE) \ + V(WasmExceptionObject, WASM_EXCEPTION_OBJECT_TYPE) \ + V(WasmGlobalObject, WASM_GLOBAL_OBJECT_TYPE) \ + V(WasmMemoryObject, WASM_MEMORY_OBJECT_TYPE) \ + V(WasmModuleObject, WASM_MODULE_OBJECT_TYPE) \ + V(WasmTableObject, WASM_TABLE_OBJECT_TYPE) \ V(WeakArrayList, WEAK_ARRAY_LIST_TYPE) \ V(WeakCell, WEAK_CELL_TYPE) #ifdef V8_INTL_SUPPORT -#define TQ_INSTANCE_TYPES_SINGLE_NOSTRUCTS(V) \ - TQ_INSTANCE_TYPES_SINGLE_BASE(V) \ - V(JSV8BreakIterator, JS_INTL_V8_BREAK_ITERATOR_TYPE) \ - V(JSCollator, JS_INTL_COLLATOR_TYPE) \ - V(JSDateTimeFormat, JS_INTL_DATE_TIME_FORMAT_TYPE) \ - V(JSListFormat, JS_INTL_LIST_FORMAT_TYPE) \ - V(JSLocale, JS_INTL_LOCALE_TYPE) \ - V(JSNumberFormat, JS_INTL_NUMBER_FORMAT_TYPE) \ - V(JSPluralRules, JS_INTL_PLURAL_RULES_TYPE) \ - V(JSRelativeTimeFormat, JS_INTL_RELATIVE_TIME_FORMAT_TYPE) \ - V(JSSegmentIterator, JS_INTL_SEGMENT_ITERATOR_TYPE) \ - V(JSSegmenter, JS_INTL_SEGMENTER_TYPE) +#define TQ_INSTANCE_TYPES_SINGLE_NOSTRUCTS(V) \ + TQ_INSTANCE_TYPES_SINGLE_BASE(V) \ + V(JSV8BreakIterator, JS_V8_BREAK_ITERATOR_TYPE) \ + V(JSCollator, JS_COLLATOR_TYPE) \ + V(JSDateTimeFormat, JS_DATE_TIME_FORMAT_TYPE) \ + V(JSListFormat, JS_LIST_FORMAT_TYPE) \ + V(JSLocale, JS_LOCALE_TYPE) \ + V(JSNumberFormat, JS_NUMBER_FORMAT_TYPE) \ + V(JSPluralRules, JS_PLURAL_RULES_TYPE) \ + V(JSRelativeTimeFormat, JS_RELATIVE_TIME_FORMAT_TYPE) \ + V(JSSegmentIterator, JS_SEGMENT_ITERATOR_TYPE) \ + V(JSSegmenter, JS_SEGMENTER_TYPE) #else @@ -99,12 +99,14 @@ namespace v8_debug_helper_internal { #endif // V8_INTL_SUPPORT +// Used in the static assertion below. enum class InstanceTypeCheckersSingle { #define ENUM_VALUE(ClassName, INSTANCE_TYPE) k##ClassName = i::INSTANCE_TYPE, INSTANCE_TYPE_CHECKERS_SINGLE(ENUM_VALUE) #undef ENUM_VALUE }; +// Verify that the instance type list above stays in sync with the truth. #define CHECK_VALUE(ClassName, INSTANCE_TYPE) \ static_assert( \ static_cast<i::InstanceType>( \ @@ -117,6 +119,9 @@ TQ_INSTANCE_TYPES_SINGLE_NOSTRUCTS(CHECK_VALUE) // Adapts one STRUCT_LIST_GENERATOR entry to (Name, NAME) format. #define STRUCT_INSTANCE_TYPE_ADAPTER(V, NAME, Name, name) V(Name, NAME) +// Pairs of (ClassName, CLASS_NAME_TYPE) for every instance type that +// corresponds to a single Torque-defined class. Note that all Struct-derived +// classes are defined in Torque. #define TQ_INSTANCE_TYPES_SINGLE(V) \ TQ_INSTANCE_TYPES_SINGLE_NOSTRUCTS(V) \ STRUCT_LIST_GENERATOR(STRUCT_INSTANCE_TYPE_ADAPTER, V) @@ -147,109 +152,154 @@ struct TypedObject { TypedObject(d::TypeCheckResult type_check_result, std::unique_ptr<TqObject> object) : type_check_result(type_check_result), object(std::move(object)) {} + + // How we discovered the object's type, or why we failed to do so. d::TypeCheckResult type_check_result; + + // Pointer to some TqObject subclass, representing the most specific known + // type for the object. std::unique_ptr<TqObject> object; + + // Collection of other guesses at more specific types than the one represented + // by |object|. + std::vector<TypedObject> possible_types; }; TypedObject GetTypedObjectByHint(uintptr_t address, std::string type_hint_string) { -#define TYPE_NAME_CASE(ClassName, ...) \ - if (type_hint_string == "v8::internal::" #ClassName) { \ - return {d::TypeCheckResult::kUsedTypeHint, \ - v8::base::make_unique<Tq##ClassName>(address)}; \ +#define TYPE_NAME_CASE(ClassName, ...) \ + if (type_hint_string == "v8::internal::" #ClassName) { \ + return {d::TypeCheckResult::kUsedTypeHint, \ + std::make_unique<Tq##ClassName>(address)}; \ } TQ_INSTANCE_TYPES_SINGLE(TYPE_NAME_CASE) TQ_INSTANCE_TYPES_RANGE(TYPE_NAME_CASE) + STRING_CLASS_TYPES(TYPE_NAME_CASE) #undef TYPE_NAME_CASE return {d::TypeCheckResult::kUnknownTypeHint, - v8::base::make_unique<TqHeapObject>(address)}; + std::make_unique<TqHeapObject>(address)}; } -TypedObject GetTypedObjectForString(uintptr_t address, i::InstanceType type) { +TypedObject GetTypedObjectForString(uintptr_t address, i::InstanceType type, + d::TypeCheckResult type_source) { class StringGetDispatcher : public i::AllStatic { public: -#define DEFINE_METHOD(ClassName) \ - static inline TypedObject Handle##ClassName(uintptr_t address) { \ - return {d::TypeCheckResult::kUsedMap, \ - v8::base::make_unique<Tq##ClassName>(address)}; \ +#define DEFINE_METHOD(ClassName) \ + static inline TypedObject Handle##ClassName( \ + uintptr_t address, d::TypeCheckResult type_source) { \ + return {type_source, std::make_unique<Tq##ClassName>(address)}; \ } STRING_CLASS_TYPES(DEFINE_METHOD) #undef DEFINE_METHOD - static inline TypedObject HandleInvalidString(uintptr_t address) { + static inline TypedObject HandleInvalidString( + uintptr_t address, d::TypeCheckResult type_source) { return {d::TypeCheckResult::kUnknownInstanceType, - v8::base::make_unique<TqString>(address)}; + std::make_unique<TqString>(address)}; } }; return i::StringShape(type) .DispatchToSpecificTypeWithoutCast<StringGetDispatcher, TypedObject>( - address); + address, type_source); +} + +TypedObject GetTypedObjectByInstanceType(uintptr_t address, + i::InstanceType type, + d::TypeCheckResult type_source) { + switch (type) { +#define INSTANCE_TYPE_CASE(ClassName, INSTANCE_TYPE) \ + case i::INSTANCE_TYPE: \ + return {type_source, std::make_unique<Tq##ClassName>(address)}; + TQ_INSTANCE_TYPES_SINGLE(INSTANCE_TYPE_CASE) +#undef INSTANCE_TYPE_CASE + + default: + + // Special case: concrete subtypes of String are not included in the + // main instance type list because they use the low bits of the instance + // type enum as flags. + if (type <= i::LAST_STRING_TYPE) { + return GetTypedObjectForString(address, type, type_source); + } + +#define INSTANCE_RANGE_CASE(ClassName, FIRST_TYPE, LAST_TYPE) \ + if (type >= i::FIRST_TYPE && type <= i::LAST_TYPE) { \ + return {type_source, std::make_unique<Tq##ClassName>(address)}; \ + } + TQ_INSTANCE_TYPES_RANGE(INSTANCE_RANGE_CASE) +#undef INSTANCE_RANGE_CASE + + return {d::TypeCheckResult::kUnknownInstanceType, + std::make_unique<TqHeapObject>(address)}; + } } TypedObject GetTypedHeapObject(uintptr_t address, d::MemoryAccessor accessor, - const char* type_hint) { - auto heap_object = v8::base::make_unique<TqHeapObject>(address); + const char* type_hint, + const d::HeapAddresses& heap_addresses) { + auto heap_object = std::make_unique<TqHeapObject>(address); Value<uintptr_t> map_ptr = heap_object->GetMapValue(accessor); if (map_ptr.validity != d::MemoryAccessResult::kOk) { + // If we can't read the Map pointer from the object, then we likely can't + // read anything else, so there's not any point in attempting to use the + // type hint. Just return a failure. return {map_ptr.validity == d::MemoryAccessResult::kAddressNotValid ? d::TypeCheckResult::kObjectPointerInvalid : d::TypeCheckResult::kObjectPointerValidButInaccessible, std::move(heap_object)}; } + Value<i::InstanceType> type = TqMap(map_ptr.value).GetInstanceTypeValue(accessor); - if (type.validity == d::MemoryAccessResult::kOk) { - // Dispatch to the appropriate method for each instance type. After calling - // the generated method to fetch properties, we can add custom properties. - switch (type.value) { -#define INSTANCE_TYPE_CASE(ClassName, INSTANCE_TYPE) \ - case i::INSTANCE_TYPE: \ - return {d::TypeCheckResult::kUsedMap, \ - v8::base::make_unique<Tq##ClassName>(address)}; - TQ_INSTANCE_TYPES_SINGLE(INSTANCE_TYPE_CASE) -#undef INSTANCE_TYPE_CASE - - default: + return GetTypedObjectByInstanceType(address, type.value, + d::TypeCheckResult::kUsedMap); + } - // Special case: concrete subtypes of String are not included in the - // main instance type list because they use the low bits of the instance - // type enum as flags. - if (type.value <= i::LAST_STRING_TYPE) { - return GetTypedObjectForString(address, type.value); - } + // We can't read the Map, so check whether it is in the list of known Maps, + // as another way to get its instance type. + KnownInstanceType known_map_type = + FindKnownMapInstanceType(map_ptr.value, heap_addresses); + if (known_map_type.confidence == KnownInstanceType::Confidence::kHigh) { + DCHECK_EQ(known_map_type.types.size(), 1); + return GetTypedObjectByInstanceType(address, known_map_type.types[0], + d::TypeCheckResult::kKnownMapPointer); + } -#define INSTANCE_RANGE_CASE(ClassName, FIRST_TYPE, LAST_TYPE) \ - if (type.value >= i::FIRST_TYPE && type.value <= i::LAST_TYPE) { \ - return {d::TypeCheckResult::kUsedMap, \ - v8::base::make_unique<Tq##ClassName>(address)}; \ + // Create a basic result that says that the object is a HeapObject and we + // couldn't read its Map. + TypedObject result = { + type.validity == d::MemoryAccessResult::kAddressNotValid + ? d::TypeCheckResult::kMapPointerInvalid + : d::TypeCheckResult::kMapPointerValidButInaccessible, + std::move(heap_object)}; + + // If a type hint is available, it may give us something more specific than + // HeapObject. However, a type hint of Object would be even less specific, so + // we'll only use the type hint if it's a subclass of HeapObject. + if (type_hint != nullptr) { + TypedObject hint_result = GetTypedObjectByHint(address, type_hint); + if (result.object->IsSuperclassOf(hint_result.object.get())) { + result = std::move(hint_result); + } } - TQ_INSTANCE_TYPES_RANGE(INSTANCE_RANGE_CASE) -#undef INSTANCE_RANGE_CASE - return {d::TypeCheckResult::kUnknownInstanceType, - std::move(heap_object)}; - break; + // If low-confidence results are available from known Maps, include them only + // if they don't contradict the primary type and would provide some additional + // specificity. + for (const i::InstanceType type_guess : known_map_type.types) { + TypedObject guess_result = GetTypedObjectByInstanceType( + address, type_guess, d::TypeCheckResult::kKnownMapPointer); + if (result.object->IsSuperclassOf(guess_result.object.get())) { + result.possible_types.push_back(std::move(guess_result)); } - } else if (type_hint != nullptr) { - // Try to use the provided type hint, since the real instance type is - // unavailable. - return GetTypedObjectByHint(address, type_hint); - } else { - // TODO(v8:9376): Use known maps here. If known map is just a guess (because - // root pointers weren't provided), then create a synthetic property with - // the more specific type. Then the caller could presumably ask us again - // with the type hint we provided. Otherwise, just go ahead and use it to - // generate properties. - return {type.validity == d::MemoryAccessResult::kAddressNotValid - ? d::TypeCheckResult::kMapPointerInvalid - : d::TypeCheckResult::kMapPointerValidButInaccessible, - std::move(heap_object)}; } + + return result; } #undef STRUCT_INSTANCE_TYPE_ADAPTER @@ -261,8 +311,13 @@ TypedObject GetTypedHeapObject(uintptr_t address, d::MemoryAccessor accessor, // An object visitor that accumulates the first few characters of a string. class ReadStringVisitor : public TqObjectVisitor { public: - ReadStringVisitor(d::MemoryAccessor accessor) - : accessor_(accessor), index_(0), limit_(INT32_MAX), done_(false) {} + ReadStringVisitor(d::MemoryAccessor accessor, + const d::HeapAddresses& heap_addresses) + : accessor_(accessor), + heap_addresses_(heap_addresses), + index_(0), + limit_(INT32_MAX), + done_(false) {} // Returns the result as UTF-8 once visiting is complete. std::string GetString() { @@ -301,7 +356,9 @@ class ReadStringVisitor : public TqObjectVisitor { void VisitConsString(const TqConsString* object) override { uintptr_t first_address = GetOrFinish(object->GetFirstValue(accessor_)); if (done_) return; - auto first = GetTypedHeapObject(first_address, accessor_, nullptr).object; + auto first = + GetTypedHeapObject(first_address, accessor_, nullptr, heap_addresses_) + .object; first->Visit(this); if (done_) return; int32_t first_length = GetOrFinish( @@ -309,7 +366,8 @@ class ReadStringVisitor : public TqObjectVisitor { uintptr_t second = GetOrFinish(object->GetSecondValue(accessor_)); if (done_) return; IndexModifier modifier(this, -first_length, -first_length); - GetTypedHeapObject(second, accessor_, nullptr).object->Visit(this); + GetTypedHeapObject(second, accessor_, nullptr, heap_addresses_) + .object->Visit(this); } void VisitSlicedString(const TqSlicedString* object) override { @@ -320,13 +378,15 @@ class ReadStringVisitor : public TqObjectVisitor { if (done_) return; int32_t limit_adjust = offset + length - limit_; IndexModifier modifier(this, offset, limit_adjust < 0 ? limit_adjust : 0); - GetTypedHeapObject(parent, accessor_, nullptr).object->Visit(this); + GetTypedHeapObject(parent, accessor_, nullptr, heap_addresses_) + .object->Visit(this); } void VisitThinString(const TqThinString* object) override { uintptr_t actual = GetOrFinish(object->GetActualValue(accessor_)); if (done_) return; - GetTypedHeapObject(actual, accessor_, nullptr).object->Visit(this); + GetTypedHeapObject(actual, accessor_, nullptr, heap_addresses_) + .object->Visit(this); } void VisitExternalString(const TqExternalString* object) override { @@ -398,6 +458,7 @@ class ReadStringVisitor : public TqObjectVisitor { std::u16string string_; // Result string. d::MemoryAccessor accessor_; + const d::HeapAddresses& heap_addresses_; int32_t index_; // Index of next char to read. int32_t limit_; // Don't read past this index (set by SlicedString). bool done_; // Whether to stop further work. @@ -406,14 +467,15 @@ class ReadStringVisitor : public TqObjectVisitor { // An object visitor that adds extra debugging information for some types. class AddInfoVisitor : public TqObjectVisitor { public: - AddInfoVisitor(const std::string& brief, d::MemoryAccessor accessor) - : accessor_(accessor), brief_(brief) {} + AddInfoVisitor(const std::string& brief, d::MemoryAccessor accessor, + const d::HeapAddresses& heap_addresses) + : accessor_(accessor), brief_(brief), heap_addresses_(heap_addresses) {} // Returns the brief object description, once visiting is complete. const std::string& GetBrief() { return brief_; } void VisitString(const TqString* object) override { - ReadStringVisitor visitor(accessor_); + ReadStringVisitor visitor(accessor_, heap_addresses_); object->Visit(&visitor); if (!brief_.empty()) brief_ += " "; brief_ += "\"" + visitor.GetString() + "\""; @@ -422,12 +484,18 @@ class AddInfoVisitor : public TqObjectVisitor { private: d::MemoryAccessor accessor_; std::string brief_; + const d::HeapAddresses& heap_addresses_; }; -std::unique_ptr<ObjectPropertiesResult> GetHeapObjectProperties( +std::unique_ptr<ObjectPropertiesResult> GetHeapObjectPropertiesNotCompressed( uintptr_t address, d::MemoryAccessor accessor, const char* type_hint, - std::string brief) { - TypedObject typed = GetTypedHeapObject(address, accessor, type_hint); + const d::HeapAddresses& heap_addresses) { + // Regardless of whether we can read the object itself, maybe we can find its + // pointer in the list of known objects. + std::string brief = FindKnownObject(address, heap_addresses); + + TypedObject typed = + GetTypedHeapObject(address, accessor, type_hint, heap_addresses); // TODO(v8:9376): Many object types need additional data that is not included // in their Torque layout definitions. For example, JSObject has an array of @@ -435,59 +503,62 @@ std::unique_ptr<ObjectPropertiesResult> GetHeapObjectProperties( // should be represented as an array in this response. If the relevant memory // is available, we should instead represent those properties (and any out-of- // object properties) using their JavaScript property names. - AddInfoVisitor visitor(brief, accessor); + AddInfoVisitor visitor(brief, accessor, heap_addresses); typed.object->Visit(&visitor); brief = visitor.GetBrief(); brief = AppendAddressAndType(brief, address, typed.object->GetName()); - return v8::base::make_unique<ObjectPropertiesResult>( + // Convert the low-confidence guessed types to a list of strings as expected + // for the response. + std::vector<std::string> guessed_types; + for (const auto& guess : typed.possible_types) { + guessed_types.push_back(guess.object->GetName()); + } + + return std::make_unique<ObjectPropertiesResult>( typed.type_check_result, brief, typed.object->GetName(), - typed.object->GetProperties(accessor)); + typed.object->GetProperties(accessor), std::move(guessed_types)); } -std::unique_ptr<ObjectPropertiesResult> GetHeapObjectProperties( - uintptr_t address, d::MemoryAccessor memory_accessor, const d::Roots& roots, - const char* type_hint) { +std::unique_ptr<ObjectPropertiesResult> GetHeapObjectPropertiesMaybeCompressed( + uintptr_t address, d::MemoryAccessor memory_accessor, + d::HeapAddresses heap_addresses, const char* type_hint) { // Try to figure out the heap range, for pointer compression (this is unused // if pointer compression is disabled). uintptr_t any_uncompressed_ptr = 0; if (!IsPointerCompressed(address)) any_uncompressed_ptr = address; - if (any_uncompressed_ptr == 0) any_uncompressed_ptr = roots.any_heap_pointer; - if (any_uncompressed_ptr == 0) any_uncompressed_ptr = roots.map_space; - if (any_uncompressed_ptr == 0) any_uncompressed_ptr = roots.old_space; - if (any_uncompressed_ptr == 0) any_uncompressed_ptr = roots.read_only_space; + if (any_uncompressed_ptr == 0) + any_uncompressed_ptr = heap_addresses.any_heap_pointer; + if (any_uncompressed_ptr == 0) + any_uncompressed_ptr = heap_addresses.map_space_first_page; + if (any_uncompressed_ptr == 0) + any_uncompressed_ptr = heap_addresses.old_space_first_page; + if (any_uncompressed_ptr == 0) + any_uncompressed_ptr = heap_addresses.read_only_space_first_page; + FillInUnknownHeapAddresses(&heap_addresses, any_uncompressed_ptr); if (any_uncompressed_ptr == 0) { // We can't figure out the heap range. Just check for known objects. - std::string brief = FindKnownObject(address, roots); + std::string brief = FindKnownObject(address, heap_addresses); brief = AppendAddressAndType(brief, address, "v8::internal::TaggedValue"); - return v8::base::make_unique<ObjectPropertiesResult>( + return std::make_unique<ObjectPropertiesResult>( d::TypeCheckResult::kUnableToDecompress, brief, - "v8::internal::TaggedValue", - std::vector<std::unique_ptr<ObjectProperty>>()); + "v8::internal::TaggedValue"); } - // TODO(v8:9376): It seems that the space roots are at predictable offsets - // within the heap reservation block when pointer compression is enabled, so - // we should be able to set those here. - - address = Decompress(address, any_uncompressed_ptr); - // From here on all addresses should be decompressed. + address = EnsureDecompressed(address, any_uncompressed_ptr); - // Regardless of whether we can read the object itself, maybe we can find its - // pointer in the list of known objects. - std::string brief = FindKnownObject(address, roots); - return GetHeapObjectProperties(address, memory_accessor, type_hint, brief); + return GetHeapObjectPropertiesNotCompressed(address, memory_accessor, + type_hint, heap_addresses); } -std::unique_ptr<ObjectPropertiesResult> GetObjectPropertiesImpl( - uintptr_t address, d::MemoryAccessor memory_accessor, const d::Roots& roots, - const char* type_hint) { - std::vector<std::unique_ptr<ObjectProperty>> props; +std::unique_ptr<ObjectPropertiesResult> GetObjectProperties( + uintptr_t address, d::MemoryAccessor memory_accessor, + const d::HeapAddresses& heap_addresses, const char* type_hint) { if (static_cast<uint32_t>(address) == i::kClearedWeakHeapObjectLower32) { - return v8::base::make_unique<ObjectPropertiesResult>( + return std::make_unique<ObjectPropertiesResult>( d::TypeCheckResult::kWeakRef, "cleared weak ref", - "v8::internal::HeapObject", std::move(props)); + "v8::internal::HeapObject"); } bool is_weak = (address & i::kHeapObjectTagMask) == i::kWeakHeapObjectTag; if (is_weak) { @@ -495,7 +566,8 @@ std::unique_ptr<ObjectPropertiesResult> GetObjectPropertiesImpl( } if (i::Internals::HasHeapObjectTag(address)) { std::unique_ptr<ObjectPropertiesResult> result = - GetHeapObjectProperties(address, memory_accessor, roots, type_hint); + GetHeapObjectPropertiesMaybeCompressed(address, memory_accessor, + heap_addresses, type_hint); if (is_weak) { result->Prepend("weak ref to "); } @@ -507,9 +579,8 @@ std::unique_ptr<ObjectPropertiesResult> GetObjectPropertiesImpl( int32_t value = i::PlatformSmiTagging::SmiToInt(address); std::stringstream stream; stream << value << " (0x" << std::hex << value << ")"; - return v8::base::make_unique<ObjectPropertiesResult>( - d::TypeCheckResult::kSmi, stream.str(), "v8::internal::Smi", - std::move(props)); + return std::make_unique<ObjectPropertiesResult>( + d::TypeCheckResult::kSmi, stream.str(), "v8::internal::Smi"); } } // namespace v8_debug_helper_internal @@ -520,10 +591,10 @@ extern "C" { V8_DEBUG_HELPER_EXPORT d::ObjectPropertiesResult* _v8_debug_helper_GetObjectProperties(uintptr_t object, d::MemoryAccessor memory_accessor, - const d::Roots& heap_roots, + const d::HeapAddresses& heap_addresses, const char* type_hint) { - return di::GetObjectPropertiesImpl(object, memory_accessor, heap_roots, - type_hint) + return di::GetObjectProperties(object, memory_accessor, heap_addresses, + type_hint) .release() ->GetPublicView(); } diff --git a/deps/v8/tools/debug_helper/heap-constants.cc b/deps/v8/tools/debug_helper/heap-constants.cc index 2bd0420690..9b9ed04cc1 100644 --- a/deps/v8/tools/debug_helper/heap-constants.cc +++ b/deps/v8/tools/debug_helper/heap-constants.cc @@ -9,36 +9,37 @@ namespace d = v8::debug_helper; namespace v8_debug_helper_internal { -std::string FindKnownObject(uintptr_t address, const d::Roots& roots) { +std::string FindKnownObject(uintptr_t address, + const d::HeapAddresses& heap_addresses) { uintptr_t containing_page = address & ~i::kPageAlignmentMask; uintptr_t offset_in_page = address & i::kPageAlignmentMask; - // If there's a match with a known root, then search only that page. - if (containing_page == roots.map_space) { + // If there's a match with a known page, then search only that page. + if (containing_page == heap_addresses.map_space_first_page) { return FindKnownObjectInMapSpace(offset_in_page); } - if (containing_page == roots.old_space) { + if (containing_page == heap_addresses.old_space_first_page) { return FindKnownObjectInOldSpace(offset_in_page); } - if (containing_page == roots.read_only_space) { + if (containing_page == heap_addresses.read_only_space_first_page) { return FindKnownObjectInReadOnlySpace(offset_in_page); } - // For any unknown roots, compile a list of things this object might be. + // For any unknown pages, compile a list of things this object might be. std::string result; - if (roots.map_space == 0) { + if (heap_addresses.map_space_first_page == 0) { std::string sub_result = FindKnownObjectInMapSpace(offset_in_page); if (!sub_result.empty()) { result += "maybe " + sub_result; } } - if (roots.old_space == 0) { + if (heap_addresses.old_space_first_page == 0) { std::string sub_result = FindKnownObjectInOldSpace(offset_in_page); if (!sub_result.empty()) { result = (result.empty() ? "" : result + ", ") + "maybe " + sub_result; } } - if (roots.read_only_space == 0) { + if (heap_addresses.read_only_space_first_page == 0) { std::string sub_result = FindKnownObjectInReadOnlySpace(offset_in_page); if (!sub_result.empty()) { result = (result.empty() ? "" : result + ", ") + "maybe " + sub_result; @@ -48,4 +49,37 @@ std::string FindKnownObject(uintptr_t address, const d::Roots& roots) { return result; } +KnownInstanceType FindKnownMapInstanceType( + uintptr_t address, const d::HeapAddresses& heap_addresses) { + uintptr_t containing_page = address & ~i::kPageAlignmentMask; + uintptr_t offset_in_page = address & i::kPageAlignmentMask; + + // If there's a match with a known page, then search only that page. + if (containing_page == heap_addresses.map_space_first_page) { + return KnownInstanceType( + FindKnownMapInstanceTypeInMapSpace(offset_in_page)); + } + if (containing_page == heap_addresses.read_only_space_first_page) { + return KnownInstanceType( + FindKnownMapInstanceTypeInReadOnlySpace(offset_in_page)); + } + + // For any unknown pages, compile a list of things this object might be. + KnownInstanceType result; + if (heap_addresses.map_space_first_page == 0) { + int sub_result = FindKnownMapInstanceTypeInMapSpace(offset_in_page); + if (sub_result >= 0) { + result.types.push_back(static_cast<i::InstanceType>(sub_result)); + } + } + if (heap_addresses.read_only_space_first_page == 0) { + int sub_result = FindKnownMapInstanceTypeInReadOnlySpace(offset_in_page); + if (sub_result >= 0) { + result.types.push_back(static_cast<i::InstanceType>(sub_result)); + } + } + + return result; +} + } // namespace v8_debug_helper_internal diff --git a/deps/v8/tools/debug_helper/heap-constants.h b/deps/v8/tools/debug_helper/heap-constants.h index f3149bbb47..6c1f17dc82 100644 --- a/deps/v8/tools/debug_helper/heap-constants.h +++ b/deps/v8/tools/debug_helper/heap-constants.h @@ -7,21 +7,60 @@ #include <cstdint> #include <string> +#include <vector> #include "debug-helper.h" +#include "src/objects/instance-type.h" namespace d = v8::debug_helper; namespace v8_debug_helper_internal { -// Functions generated by mkgrokdump: +// ===== Functions generated by gen-heap-constants.py: ========================= + +// Returns the name of a known object, given its offset within the first page of +// the space, or empty string on failure. std::string FindKnownObjectInOldSpace(uintptr_t offset); std::string FindKnownObjectInReadOnlySpace(uintptr_t offset); std::string FindKnownObjectInMapSpace(uintptr_t offset); -std::string FindKnownMapInstanceTypeInMapSpace(uintptr_t offset); -std::string FindKnownMapInstanceTypeInReadOnlySpace(uintptr_t offset); -std::string FindKnownObject(uintptr_t address, const d::Roots& roots); +// In builds with pointer compression enabled, sets the *_first_page members in +// the HeapAddresses object. In other builds, does nothing. +void FillInUnknownHeapAddresses(d::HeapAddresses* heap_addresses, + uintptr_t any_uncompressed_ptr); + +// Returns the instance type for the known Map, given its offset within the +// first page of the space, or empty string on failure. +int FindKnownMapInstanceTypeInMapSpace(uintptr_t offset); +int FindKnownMapInstanceTypeInReadOnlySpace(uintptr_t offset); + +// ===== End of generated functions. =========================================== + +// Returns a descriptive string if the given address matches a known object, or +// an empty string otherwise. +std::string FindKnownObject(uintptr_t address, + const d::HeapAddresses& heap_addresses); + +struct KnownInstanceType { + enum class Confidence { + kLow, + kHigh, + }; + KnownInstanceType() : confidence(Confidence::kLow) {} + KnownInstanceType(int type) : KnownInstanceType() { + if (type >= 0) { + confidence = Confidence::kHigh; + types.push_back(static_cast<v8::internal::InstanceType>(type)); + } + } + Confidence confidence; + std::vector<v8::internal::InstanceType> types; +}; + +// Returns information about the instance type of the Map at the given address, +// based on the list of known Maps. +KnownInstanceType FindKnownMapInstanceType( + uintptr_t address, const d::HeapAddresses& heap_addresses); } // namespace v8_debug_helper_internal |