1// Copyright 2019 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <sstream> 6 7#include "debug-helper-internal.h" 8#include "heap-constants.h" 9#include "include/v8-internal.h" 10#include "src/execution/frame-constants.h" 11#include "src/execution/frames.h" 12#include "src/execution/isolate-utils.h" 13#include "src/objects/string-inl.h" 14#include "src/sandbox/external-pointer.h" 15#include "src/strings/unicode-inl.h" 16#include "torque-generated/class-debug-readers.h" 17#include "torque-generated/debug-macros.h" 18 19namespace i = v8::internal; 20 21namespace v8 { 22namespace internal { 23namespace debug_helper_internal { 24 25constexpr char kObject[] = "v8::internal::Object"; 26constexpr char kTaggedValue[] = "v8::internal::TaggedValue"; 27constexpr char kSmi[] = "v8::internal::Smi"; 28constexpr char kHeapObject[] = "v8::internal::HeapObject"; 29#ifdef V8_COMPRESS_POINTERS 30constexpr char kObjectAsStoredInHeap[] = "v8::internal::TaggedValue"; 31#else 32constexpr char kObjectAsStoredInHeap[] = "v8::internal::Object"; 33#endif 34 35std::string AppendAddressAndType(const std::string& brief, uintptr_t address, 36 const char* type) { 37 std::stringstream brief_stream; 38 brief_stream << "0x" << std::hex << address << " <" << type << ">"; 39 return brief.empty() ? brief_stream.str() 40 : brief + " (" + brief_stream.str() + ")"; 41} 42 43std::string JoinWithSpace(const std::string& a, const std::string& b) { 44 return a.empty() || b.empty() ? a + b : a + " " + b; 45} 46 47struct TypedObject { 48 TypedObject(d::TypeCheckResult type_check_result, 49 std::unique_ptr<TqObject> object) 50 : type_check_result(type_check_result), object(std::move(object)) {} 51 52 // How we discovered the object's type, or why we failed to do so. 53 d::TypeCheckResult type_check_result; 54 55 // Pointer to some TqObject subclass, representing the most specific known 56 // type for the object. 57 std::unique_ptr<TqObject> object; 58 59 // Collection of other guesses at more specific types than the one represented 60 // by |object|. 61 std::vector<TypedObject> possible_types; 62}; 63 64TypedObject GetTypedObjectByHint(uintptr_t address, 65 std::string type_hint_string) { 66#define TYPE_NAME_CASE(ClassName, ...) \ 67 if (type_hint_string == "v8::internal::" #ClassName) { \ 68 return {d::TypeCheckResult::kUsedTypeHint, \ 69 std::make_unique<Tq##ClassName>(address)}; \ 70 } 71 72 TORQUE_INSTANCE_CHECKERS_SINGLE_FULLY_DEFINED(TYPE_NAME_CASE) 73 TORQUE_INSTANCE_CHECKERS_RANGE_FULLY_DEFINED(TYPE_NAME_CASE) 74 STRING_CLASS_TYPES(TYPE_NAME_CASE) 75 76#undef TYPE_NAME_CASE 77 78 return {d::TypeCheckResult::kUnknownTypeHint, 79 std::make_unique<TqHeapObject>(address)}; 80} 81 82TypedObject GetTypedObjectForString(uintptr_t address, i::InstanceType type, 83 d::TypeCheckResult type_source) { 84 class StringGetDispatcher : public i::AllStatic { 85 public: 86#define DEFINE_METHOD(ClassName) \ 87 static inline TypedObject Handle##ClassName( \ 88 uintptr_t address, d::TypeCheckResult type_source) { \ 89 return {type_source, std::make_unique<Tq##ClassName>(address)}; \ 90 } 91 STRING_CLASS_TYPES(DEFINE_METHOD) 92#undef DEFINE_METHOD 93 static inline TypedObject HandleInvalidString( 94 uintptr_t address, d::TypeCheckResult type_source) { 95 return {d::TypeCheckResult::kUnknownInstanceType, 96 std::make_unique<TqString>(address)}; 97 } 98 }; 99 100 return i::StringShape(type) 101 .DispatchToSpecificTypeWithoutCast<StringGetDispatcher, TypedObject>( 102 address, type_source); 103} 104 105TypedObject GetTypedObjectByInstanceType(uintptr_t address, 106 i::InstanceType type, 107 d::TypeCheckResult type_source) { 108 switch (type) { 109#define INSTANCE_TYPE_CASE(ClassName, INSTANCE_TYPE) \ 110 case i::INSTANCE_TYPE: \ 111 return {type_source, std::make_unique<Tq##ClassName>(address)}; 112 TORQUE_INSTANCE_CHECKERS_SINGLE_FULLY_DEFINED(INSTANCE_TYPE_CASE) 113 TORQUE_INSTANCE_CHECKERS_MULTIPLE_FULLY_DEFINED(INSTANCE_TYPE_CASE) 114#undef INSTANCE_TYPE_CASE 115 116 default: 117 118 // Special case: concrete subtypes of String are not included in the 119 // main instance type list because they use the low bits of the instance 120 // type enum as flags. 121 if (type <= i::LAST_STRING_TYPE) { 122 return GetTypedObjectForString(address, type, type_source); 123 } 124 125#define INSTANCE_RANGE_CASE(ClassName, FIRST_TYPE, LAST_TYPE) \ 126 if (type >= i::FIRST_TYPE && type <= i::LAST_TYPE) { \ 127 return {type_source, std::make_unique<Tq##ClassName>(address)}; \ 128 } 129 TORQUE_INSTANCE_CHECKERS_RANGE_FULLY_DEFINED(INSTANCE_RANGE_CASE) 130#undef INSTANCE_RANGE_CASE 131 132 return {d::TypeCheckResult::kUnknownInstanceType, 133 std::make_unique<TqHeapObject>(address)}; 134 } 135} 136 137bool IsTypedHeapObjectInstanceTypeOf(uintptr_t address, 138 d::MemoryAccessor accessor, 139 i::InstanceType instance_type) { 140 auto heap_object = std::make_unique<TqHeapObject>(address); 141 Value<uintptr_t> map_ptr = heap_object->GetMapValue(accessor); 142 143 if (map_ptr.validity == d::MemoryAccessResult::kOk) { 144 Value<i::InstanceType> type = 145 TqMap(map_ptr.value).GetInstanceTypeValue(accessor); 146 if (type.validity == d::MemoryAccessResult::kOk) { 147 return instance_type == type.value; 148 } 149 } 150 151 return false; 152} 153 154TypedObject GetTypedHeapObject(uintptr_t address, d::MemoryAccessor accessor, 155 const char* type_hint, 156 const d::HeapAddresses& heap_addresses) { 157 auto heap_object = std::make_unique<TqHeapObject>(address); 158 Value<uintptr_t> map_ptr = heap_object->GetMapValue(accessor); 159 160 if (map_ptr.validity != d::MemoryAccessResult::kOk) { 161 // If we can't read the Map pointer from the object, then we likely can't 162 // read anything else, so there's not any point in attempting to use the 163 // type hint. Just return a failure. 164 return {map_ptr.validity == d::MemoryAccessResult::kAddressNotValid 165 ? d::TypeCheckResult::kObjectPointerInvalid 166 : d::TypeCheckResult::kObjectPointerValidButInaccessible, 167 std::move(heap_object)}; 168 } 169 170 Value<i::InstanceType> type = 171 TqMap(map_ptr.value).GetInstanceTypeValue(accessor); 172 if (type.validity == d::MemoryAccessResult::kOk) { 173 return GetTypedObjectByInstanceType(address, type.value, 174 d::TypeCheckResult::kUsedMap); 175 } 176 177 // We can't read the Map, so check whether it is in the list of known Maps, 178 // as another way to get its instance type. 179 KnownInstanceType known_map_type = 180 FindKnownMapInstanceTypes(map_ptr.value, heap_addresses); 181 if (known_map_type.confidence == KnownInstanceType::Confidence::kHigh) { 182 DCHECK_EQ(known_map_type.types.size(), 1); 183 return GetTypedObjectByInstanceType(address, known_map_type.types[0], 184 d::TypeCheckResult::kKnownMapPointer); 185 } 186 187 // Create a basic result that says that the object is a HeapObject and we 188 // couldn't read its Map. 189 TypedObject result = { 190 type.validity == d::MemoryAccessResult::kAddressNotValid 191 ? d::TypeCheckResult::kMapPointerInvalid 192 : d::TypeCheckResult::kMapPointerValidButInaccessible, 193 std::move(heap_object)}; 194 195 // If a type hint is available, it may give us something more specific than 196 // HeapObject. However, a type hint of Object would be even less specific, so 197 // we'll only use the type hint if it's a subclass of HeapObject. 198 if (type_hint != nullptr) { 199 TypedObject hint_result = GetTypedObjectByHint(address, type_hint); 200 if (result.object->IsSuperclassOf(hint_result.object.get())) { 201 result = std::move(hint_result); 202 } 203 } 204 205 // If low-confidence results are available from known Maps, include them only 206 // if they don't contradict the primary type and would provide some additional 207 // specificity. 208 for (const i::InstanceType type_guess : known_map_type.types) { 209 TypedObject guess_result = GetTypedObjectByInstanceType( 210 address, type_guess, d::TypeCheckResult::kKnownMapPointer); 211 if (result.object->IsSuperclassOf(guess_result.object.get())) { 212 result.possible_types.push_back(std::move(guess_result)); 213 } 214 } 215 216 return result; 217} 218 219// An object visitor that accumulates the first few characters of a string. 220class ReadStringVisitor : public TqObjectVisitor { 221 public: 222 static v8::base::Optional<std::string> Visit( 223 d::MemoryAccessor accessor, const d::HeapAddresses& heap_addresses, 224 const TqString* object) { 225 ReadStringVisitor visitor(accessor, heap_addresses); 226 object->Visit(&visitor); 227 return visitor.GetString(); 228 } 229 230 // Returns the result as UTF-8 once visiting is complete. 231 v8::base::Optional<std::string> GetString() { 232 if (failed_) return {}; 233 std::vector<char> result( 234 string_.size() * unibrow::Utf16::kMaxExtraUtf8BytesForOneUtf16CodeUnit); 235 unsigned write_index = 0; 236 int prev_char = unibrow::Utf16::kNoPreviousCharacter; 237 for (size_t read_index = 0; read_index < string_.size(); ++read_index) { 238 uint16_t character = string_[read_index]; 239 write_index += 240 unibrow::Utf8::Encode(result.data() + write_index, character, 241 prev_char, /*replace_invalid=*/true); 242 prev_char = character; 243 } 244 return std::string(result.data(), write_index); 245 } 246 247 template <typename TChar> 248 Value<TChar> ReadCharacter(uintptr_t data_address, int32_t index) { 249 TChar value{}; 250 d::MemoryAccessResult validity = 251 accessor_(data_address + index * sizeof(TChar), 252 reinterpret_cast<uint8_t*>(&value), sizeof(value)); 253 return {validity, value}; 254 } 255 256 template <typename TChar> 257 void ReadStringCharacters(const TqString* object, uintptr_t data_address) { 258 int32_t length = GetOrFinish(object->GetLengthValue(accessor_)); 259 for (; index_ < length && index_ < limit_ && !done_; ++index_) { 260 STATIC_ASSERT(sizeof(TChar) <= sizeof(char16_t)); 261 char16_t c = static_cast<char16_t>( 262 GetOrFinish(ReadCharacter<TChar>(data_address, index_))); 263 if (!done_) AddCharacter(c); 264 } 265 } 266 267 template <typename TChar, typename TString> 268 void ReadSeqString(const TString* object) { 269 ReadStringCharacters<TChar>(object, object->GetCharsAddress()); 270 } 271 272 void VisitSeqOneByteString(const TqSeqOneByteString* object) override { 273 ReadSeqString<char>(object); 274 } 275 276 void VisitSeqTwoByteString(const TqSeqTwoByteString* object) override { 277 ReadSeqString<char16_t>(object); 278 } 279 280 void VisitConsString(const TqConsString* object) override { 281 uintptr_t first_address = GetOrFinish(object->GetFirstValue(accessor_)); 282 if (done_) return; 283 auto first = 284 GetTypedHeapObject(first_address, accessor_, nullptr, heap_addresses_) 285 .object; 286 first->Visit(this); 287 if (done_) return; 288 int32_t first_length = GetOrFinish( 289 static_cast<TqString*>(first.get())->GetLengthValue(accessor_)); 290 uintptr_t second = GetOrFinish(object->GetSecondValue(accessor_)); 291 if (done_) return; 292 IndexModifier modifier(this, -first_length, -first_length); 293 GetTypedHeapObject(second, accessor_, nullptr, heap_addresses_) 294 .object->Visit(this); 295 } 296 297 void VisitSlicedString(const TqSlicedString* object) override { 298 uintptr_t parent = GetOrFinish(object->GetParentValue(accessor_)); 299 int32_t length = GetOrFinish(object->GetLengthValue(accessor_)); 300 int32_t offset = i::PlatformSmiTagging::SmiToInt( 301 GetOrFinish(object->GetOffsetValue(accessor_))); 302 if (done_) return; 303 int32_t limit_adjust = offset + length - limit_; 304 IndexModifier modifier(this, offset, limit_adjust < 0 ? limit_adjust : 0); 305 GetTypedHeapObject(parent, accessor_, nullptr, heap_addresses_) 306 .object->Visit(this); 307 } 308 309 void VisitThinString(const TqThinString* object) override { 310 uintptr_t actual = GetOrFinish(object->GetActualValue(accessor_)); 311 if (done_) return; 312 GetTypedHeapObject(actual, accessor_, nullptr, heap_addresses_) 313 .object->Visit(this); 314 } 315 316 bool IsExternalStringCached(const TqExternalString* object) { 317 // The safest way to get the instance type is to use known map pointers, in 318 // case the map data is not available. 319 Value<uintptr_t> map_ptr = object->GetMapValue(accessor_); 320 DCHECK_IMPLIES(map_ptr.validity == d::MemoryAccessResult::kOk, 321 !v8::internal::MapWord::IsPacked(map_ptr.value)); 322 uintptr_t map = GetOrFinish(map_ptr); 323 if (done_) return false; 324 auto instance_types = FindKnownMapInstanceTypes(map, heap_addresses_); 325 // Exactly one of the matched instance types should be a string type, 326 // because all maps for string types are in the same space (read-only 327 // space). The "uncached" flag on that instance type tells us whether it's 328 // safe to read the cached data. 329 for (const auto& type : instance_types.types) { 330 if ((type & i::kIsNotStringMask) == i::kStringTag && 331 (type & i::kStringRepresentationMask) == i::kExternalStringTag) { 332 return (type & i::kUncachedExternalStringMask) != 333 i::kUncachedExternalStringTag; 334 } 335 } 336 337 // If for some reason we can't find an external string type here (maybe the 338 // caller provided an external string type as the type hint, but it doesn't 339 // actually match the in-memory map pointer), then we can't safely use the 340 // cached data. 341 return false; 342 } 343 344 template <typename TChar> 345 void ReadExternalString(const TqExternalString* object) { 346 // Cached external strings are easy to read; uncached external strings 347 // require knowledge of the embedder. For now, we only read cached external 348 // strings. 349 if (IsExternalStringCached(object)) { 350 ExternalPointer_t resource_data = 351 GetOrFinish(object->GetResourceDataValue(accessor_)); 352#ifdef V8_COMPRESS_POINTERS 353 Isolate* isolate = GetIsolateForSandbox( 354 HeapObject::unchecked_cast(Object(heap_addresses_.any_heap_pointer))); 355 uintptr_t data_address = static_cast<uintptr_t>(DecodeExternalPointer( 356 isolate, resource_data, kExternalStringResourceDataTag)); 357#else 358 uintptr_t data_address = static_cast<uintptr_t>(resource_data); 359#endif // V8_COMPRESS_POINTERS 360 if (done_) return; 361 ReadStringCharacters<TChar>(object, data_address); 362 } else { 363 // TODO(v8:9376): Come up with some way that a caller with full knowledge 364 // of a particular embedder could provide a callback function for getting 365 // uncached string data. 366 AddEllipsisAndFinish(); 367 } 368 } 369 370 void VisitExternalOneByteString( 371 const TqExternalOneByteString* object) override { 372 ReadExternalString<char>(object); 373 } 374 375 void VisitExternalTwoByteString( 376 const TqExternalTwoByteString* object) override { 377 ReadExternalString<char16_t>(object); 378 } 379 380 void VisitObject(const TqObject* object) override { 381 // If we fail to find a specific type for a sub-object within a cons string, 382 // sliced string, or thin string, we will end up here. 383 AddEllipsisAndFinish(); 384 } 385 386 private: 387 ReadStringVisitor(d::MemoryAccessor accessor, 388 const d::HeapAddresses& heap_addresses) 389 : accessor_(accessor), 390 heap_addresses_(heap_addresses), 391 index_(0), 392 limit_(INT32_MAX), 393 done_(false), 394 failed_(false) {} 395 396 // Unpacks a value that was fetched from the debuggee. If the value indicates 397 // that it couldn't successfully fetch memory, then prevents further work. 398 template <typename T> 399 T GetOrFinish(Value<T> value) { 400 if (value.validity != d::MemoryAccessResult::kOk) { 401 AddEllipsisAndFinish(); 402 } 403 return value.value; 404 } 405 406 void AddEllipsisAndFinish() { 407 if (!done_) { 408 done_ = true; 409 if (string_.empty()) { 410 failed_ = true; 411 } else { 412 string_ += u"..."; 413 } 414 } 415 } 416 417 void AddCharacter(char16_t c) { 418 if (string_.size() >= kMaxCharacters) { 419 AddEllipsisAndFinish(); 420 } else { 421 string_.push_back(c); 422 } 423 } 424 425 // Temporarily adds offsets to both index_ and limit_, to handle ConsString 426 // and SlicedString. 427 class IndexModifier { 428 public: 429 IndexModifier(ReadStringVisitor* that, int32_t index_adjust, 430 int32_t limit_adjust) 431 : that_(that), 432 index_adjust_(index_adjust), 433 limit_adjust_(limit_adjust) { 434 that_->index_ += index_adjust_; 435 that_->limit_ += limit_adjust_; 436 } 437 IndexModifier(const IndexModifier&) = delete; 438 IndexModifier& operator=(const IndexModifier&) = delete; 439 ~IndexModifier() { 440 that_->index_ -= index_adjust_; 441 that_->limit_ -= limit_adjust_; 442 } 443 444 private: 445 ReadStringVisitor* that_; 446 int32_t index_adjust_; 447 int32_t limit_adjust_; 448 }; 449 450 static constexpr int kMaxCharacters = 80; // How many characters to print. 451 452 std::u16string string_; // Result string. 453 d::MemoryAccessor accessor_; 454 const d::HeapAddresses& heap_addresses_; 455 int32_t index_; // Index of next char to read. 456 int32_t limit_; // Don't read past this index (set by SlicedString). 457 bool done_; // Whether to stop further work. 458 bool failed_; // Whether an error was encountered before any valid data. 459}; 460 461// An object visitor that supplies extra information for some types. 462class AddInfoVisitor : public TqObjectVisitor { 463 public: 464 // Returns a descriptive string and a list of properties for the given object. 465 // Both may be empty, and are meant as an addition or a replacement for, 466 // the Torque-generated data about the object. 467 static std::pair<std::string, std::vector<std::unique_ptr<ObjectProperty>>> 468 Visit(const TqObject* object, d::MemoryAccessor accessor, 469 const d::HeapAddresses& heap_addresses) { 470 AddInfoVisitor visitor(accessor, heap_addresses); 471 object->Visit(&visitor); 472 return {std::move(visitor.brief_), std::move(visitor.properties_)}; 473 } 474 475 void VisitString(const TqString* object) override { 476 auto str = ReadStringVisitor::Visit(accessor_, heap_addresses_, object); 477 if (str.has_value()) { 478 brief_ = "\"" + *str + "\""; 479 } 480 } 481 482 void VisitExternalString(const TqExternalString* object) override { 483 VisitString(object); 484 // Cast resource field to v8::String::ExternalStringResourceBase* would add 485 // more info. 486 properties_.push_back(std::make_unique<ObjectProperty>( 487 "resource", 488 CheckTypeName<v8::String::ExternalStringResourceBase*>( 489 "v8::String::ExternalStringResourceBase*"), 490 CheckTypeName<v8::String::ExternalStringResourceBase*>( 491 "v8::String::ExternalStringResourceBase*"), 492 object->GetResourceAddress(), 1, 493 sizeof(v8::String::ExternalStringResourceBase*), 494 std::vector<std::unique_ptr<StructProperty>>(), 495 d::PropertyKind::kSingle)); 496 } 497 498 void VisitJSObject(const TqJSObject* object) override { 499 // JSObject and its subclasses can be followed directly by an array of 500 // property values. The start and end offsets of those values are described 501 // by a pair of values in its Map. 502 auto map_ptr = object->GetMapValue(accessor_); 503 if (map_ptr.validity != d::MemoryAccessResult::kOk) { 504 return; // Can't read the JSObject. Nothing useful to do. 505 } 506 DCHECK(!v8::internal::MapWord::IsPacked(map_ptr.value)); 507 TqMap map(map_ptr.value); 508 509 // On JSObject instances, this value is the start of in-object properties. 510 // The constructor function index option is only for primitives. 511 auto start_offset = 512 map.GetInobjectPropertiesStartOrConstructorFunctionIndexValue( 513 accessor_); 514 515 // The total size of the object in memory. This may include over-allocated 516 // expansion space that doesn't correspond to any user-accessible property. 517 auto instance_size = map.GetInstanceSizeInWordsValue(accessor_); 518 519 if (start_offset.validity != d::MemoryAccessResult::kOk || 520 instance_size.validity != d::MemoryAccessResult::kOk) { 521 return; // Can't read the Map. Nothing useful to do. 522 } 523 int num_properties = instance_size.value - start_offset.value; 524 if (num_properties > 0) { 525 properties_.push_back(std::make_unique<ObjectProperty>( 526 "in-object properties", kObjectAsStoredInHeap, kObject, 527 object->GetMapAddress() + start_offset.value * i::kTaggedSize, 528 num_properties, i::kTaggedSize, 529 std::vector<std::unique_ptr<StructProperty>>(), 530 d::PropertyKind::kArrayOfKnownSize)); 531 } 532 } 533 534 private: 535 AddInfoVisitor(d::MemoryAccessor accessor, 536 const d::HeapAddresses& heap_addresses) 537 : accessor_(accessor), heap_addresses_(heap_addresses) {} 538 539 // Inputs used by this visitor: 540 541 d::MemoryAccessor accessor_; 542 const d::HeapAddresses& heap_addresses_; 543 544 // Outputs generated by this visitor: 545 546 // A brief description of the object. 547 std::string brief_; 548 // A list of extra properties to append after the automatic ones that are 549 // created for all Torque-defined class fields. 550 std::vector<std::unique_ptr<ObjectProperty>> properties_; 551}; 552 553std::unique_ptr<ObjectPropertiesResult> GetHeapObjectPropertiesNotCompressed( 554 uintptr_t address, d::MemoryAccessor accessor, const char* type_hint, 555 const d::HeapAddresses& heap_addresses) { 556 // Regardless of whether we can read the object itself, maybe we can find its 557 // pointer in the list of known objects. 558 std::string brief = FindKnownObject(address, heap_addresses); 559 560 TypedObject typed = 561 GetTypedHeapObject(address, accessor, type_hint, heap_addresses); 562 auto props = typed.object->GetProperties(accessor); 563 564 // Use the AddInfoVisitor to get any extra properties or descriptive text that 565 // can't be directly derived from Torque class definitions. 566 auto extra_info = 567 AddInfoVisitor::Visit(typed.object.get(), accessor, heap_addresses); 568 brief = JoinWithSpace(brief, extra_info.first); 569 570 // Overwrite existing properties if they have the same name. 571 for (size_t i = 0; i < extra_info.second.size(); i++) { 572 bool overwrite = false; 573 for (size_t j = 0; j < props.size(); j++) { 574 if (strcmp(props[j]->GetPublicView()->name, 575 extra_info.second[i]->GetPublicView()->name) == 0) { 576 props[j] = std::move(extra_info.second[i]); 577 overwrite = true; 578 break; 579 } 580 } 581 if (overwrite) continue; 582 props.push_back(std::move(extra_info.second[i])); 583 } 584 585 brief = AppendAddressAndType(brief, address, typed.object->GetName()); 586 587 // Convert the low-confidence guessed types to a list of strings as expected 588 // for the response. 589 std::vector<std::string> guessed_types; 590 for (const auto& guess : typed.possible_types) { 591 guessed_types.push_back(guess.object->GetName()); 592 } 593 594 return std::make_unique<ObjectPropertiesResult>( 595 typed.type_check_result, brief, typed.object->GetName(), std::move(props), 596 std::move(guessed_types)); 597} 598 599std::unique_ptr<ObjectPropertiesResult> GetHeapObjectPropertiesMaybeCompressed( 600 uintptr_t address, d::MemoryAccessor memory_accessor, 601 d::HeapAddresses heap_addresses, const char* type_hint) { 602 // Try to figure out the heap range, for pointer compression (this is unused 603 // if pointer compression is disabled). 604 uintptr_t any_uncompressed_ptr = 0; 605 if (!IsPointerCompressed(address)) any_uncompressed_ptr = address; 606 if (any_uncompressed_ptr == 0) 607 any_uncompressed_ptr = heap_addresses.any_heap_pointer; 608 if (any_uncompressed_ptr == 0) 609 any_uncompressed_ptr = heap_addresses.map_space_first_page; 610 if (any_uncompressed_ptr == 0) 611 any_uncompressed_ptr = heap_addresses.old_space_first_page; 612 if (any_uncompressed_ptr == 0) 613 any_uncompressed_ptr = heap_addresses.read_only_space_first_page; 614 FillInUnknownHeapAddresses(&heap_addresses, any_uncompressed_ptr); 615 if (any_uncompressed_ptr == 0) { 616 // We can't figure out the heap range. Just check for known objects. 617 std::string brief = FindKnownObject(address, heap_addresses); 618 brief = AppendAddressAndType(brief, address, kTaggedValue); 619 return std::make_unique<ObjectPropertiesResult>( 620 d::TypeCheckResult::kUnableToDecompress, brief, kTaggedValue); 621 } 622 623 address = EnsureDecompressed(address, any_uncompressed_ptr); 624 625 return GetHeapObjectPropertiesNotCompressed(address, memory_accessor, 626 type_hint, heap_addresses); 627} 628 629std::unique_ptr<ObjectPropertiesResult> GetObjectProperties( 630 uintptr_t address, d::MemoryAccessor memory_accessor, 631 const d::HeapAddresses& heap_addresses, const char* type_hint) { 632 if (static_cast<uint32_t>(address) == i::kClearedWeakHeapObjectLower32) { 633 return std::make_unique<ObjectPropertiesResult>( 634 d::TypeCheckResult::kWeakRef, "cleared weak ref", kHeapObject); 635 } 636 bool is_weak = (address & i::kHeapObjectTagMask) == i::kWeakHeapObjectTag; 637 if (is_weak) { 638 address &= ~i::kWeakHeapObjectMask; 639 } 640 if (i::Internals::HasHeapObjectTag(address)) { 641 std::unique_ptr<ObjectPropertiesResult> result = 642 GetHeapObjectPropertiesMaybeCompressed(address, memory_accessor, 643 heap_addresses, type_hint); 644 if (is_weak) { 645 result->Prepend("weak ref to "); 646 } 647 return result; 648 } 649 650 // For smi values, construct a response with a description representing the 651 // untagged value. 652 int32_t value = i::PlatformSmiTagging::SmiToInt(address); 653 std::stringstream stream; 654 stream << value << " (0x" << std::hex << value << ")"; 655 return std::make_unique<ObjectPropertiesResult>(d::TypeCheckResult::kSmi, 656 stream.str(), kSmi); 657} 658 659std::unique_ptr<StackFrameResult> GetStackFrame( 660 uintptr_t frame_pointer, d::MemoryAccessor memory_accessor) { 661 // Read the data at frame_pointer + kContextOrFrameTypeOffset. 662 intptr_t context_or_frame_type = 0; 663 d::MemoryAccessResult validity = memory_accessor( 664 frame_pointer + CommonFrameConstants::kContextOrFrameTypeOffset, 665 reinterpret_cast<void*>(&context_or_frame_type), sizeof(intptr_t)); 666 auto props = std::vector<std::unique_ptr<ObjectProperty>>(); 667 if (validity == d::MemoryAccessResult::kOk) { 668 // If it is context, not frame marker then add new property 669 // "currently_executing_function". 670 if (!StackFrame::IsTypeMarker(context_or_frame_type)) { 671 props.push_back(std::make_unique<ObjectProperty>( 672 "currently_executing_jsfunction", 673 CheckTypeName<v8::internal::JSFunction>("v8::internal::JSFunction"), 674 CheckTypeName<v8::internal::JSFunction*>("v8::internal::JSFunction"), 675 frame_pointer + StandardFrameConstants::kFunctionOffset, 1, 676 sizeof(v8::internal::JSFunction), 677 std::vector<std::unique_ptr<StructProperty>>(), 678 d::PropertyKind::kSingle)); 679 // Add more items in the Locals pane representing the JS function name, 680 // source file name, and line & column numbers within the source file, so 681 // that the user doesn’t need to dig through the shared_function_info to 682 // find them. 683 intptr_t js_function_ptr = 0; 684 validity = memory_accessor( 685 frame_pointer + StandardFrameConstants::kFunctionOffset, 686 reinterpret_cast<void*>(&js_function_ptr), sizeof(intptr_t)); 687 if (validity == d::MemoryAccessResult::kOk) { 688 TqJSFunction js_function(js_function_ptr); 689 auto shared_function_info_ptr = 690 js_function.GetSharedFunctionInfoValue(memory_accessor); 691 if (shared_function_info_ptr.validity == d::MemoryAccessResult::kOk) { 692 TqSharedFunctionInfo shared_function_info( 693 shared_function_info_ptr.value); 694 auto script_or_debug_info_ptr = 695 shared_function_info.GetScriptOrDebugInfoValue(memory_accessor); 696 if (script_or_debug_info_ptr.validity == d::MemoryAccessResult::kOk) { 697 // Make sure script_or_debug_info_ptr is script. 698 auto address = script_or_debug_info_ptr.value; 699 if (IsTypedHeapObjectInstanceTypeOf(address, memory_accessor, 700 i::InstanceType::SCRIPT_TYPE)) { 701 TqScript script(script_or_debug_info_ptr.value); 702 props.push_back(std::make_unique<ObjectProperty>( 703 "script_name", kObjectAsStoredInHeap, kObject, 704 script.GetNameAddress(), 1, i::kTaggedSize, 705 std::vector<std::unique_ptr<StructProperty>>(), 706 d::PropertyKind::kSingle)); 707 props.push_back(std::make_unique<ObjectProperty>( 708 "script_source", kObjectAsStoredInHeap, kObject, 709 script.GetSourceAddress(), 1, i::kTaggedSize, 710 std::vector<std::unique_ptr<StructProperty>>(), 711 d::PropertyKind::kSingle)); 712 } 713 } 714 auto name_or_scope_info_ptr = 715 shared_function_info.GetNameOrScopeInfoValue(memory_accessor); 716 if (name_or_scope_info_ptr.validity == d::MemoryAccessResult::kOk) { 717 auto scope_info_address = name_or_scope_info_ptr.value; 718 // Make sure name_or_scope_info_ptr is scope info. 719 if (IsTypedHeapObjectInstanceTypeOf( 720 scope_info_address, memory_accessor, 721 i::InstanceType::SCOPE_INFO_TYPE)) { 722 auto indexed_field_slice_function_variable_info = 723 TqDebugFieldSliceScopeInfoFunctionVariableInfo( 724 memory_accessor, scope_info_address); 725 if (indexed_field_slice_function_variable_info.validity == 726 d::MemoryAccessResult::kOk) { 727 props.push_back(std::make_unique<ObjectProperty>( 728 "function_name", kObjectAsStoredInHeap, kObject, 729 scope_info_address - i::kHeapObjectTag + 730 std::get<1>( 731 indexed_field_slice_function_variable_info.value), 732 std::get<2>( 733 indexed_field_slice_function_variable_info.value), 734 i::kTaggedSize, 735 std::vector<std::unique_ptr<StructProperty>>(), 736 d::PropertyKind::kSingle)); 737 } 738 std::vector<std::unique_ptr<StructProperty>> 739 position_info_struct_field_list; 740 position_info_struct_field_list.push_back( 741 std::make_unique<StructProperty>( 742 "start", kObjectAsStoredInHeap, kObject, 0, 0, 0)); 743 position_info_struct_field_list.push_back( 744 std::make_unique<StructProperty>("end", kObjectAsStoredInHeap, 745 kObject, 4, 0, 0)); 746 auto indexed_field_slice_position_info = 747 TqDebugFieldSliceScopeInfoPositionInfo(memory_accessor, 748 scope_info_address); 749 if (indexed_field_slice_position_info.validity == 750 d::MemoryAccessResult::kOk) { 751 props.push_back(std::make_unique<ObjectProperty>( 752 "function_character_offset", "", "", 753 scope_info_address - i::kHeapObjectTag + 754 std::get<1>(indexed_field_slice_position_info.value), 755 std::get<2>(indexed_field_slice_position_info.value), 756 i::kTaggedSize, std::move(position_info_struct_field_list), 757 d::PropertyKind::kSingle)); 758 } 759 } 760 } 761 } 762 } 763 } 764 } 765 766 return std::make_unique<StackFrameResult>(std::move(props)); 767} 768 769} // namespace debug_helper_internal 770} // namespace internal 771} // namespace v8 772 773namespace di = v8::internal::debug_helper_internal; 774 775extern "C" { 776V8_DEBUG_HELPER_EXPORT d::ObjectPropertiesResult* 777_v8_debug_helper_GetObjectProperties(uintptr_t object, 778 d::MemoryAccessor memory_accessor, 779 const d::HeapAddresses& heap_addresses, 780 const char* type_hint) { 781 return di::GetObjectProperties(object, memory_accessor, heap_addresses, 782 type_hint) 783 .release() 784 ->GetPublicView(); 785} 786V8_DEBUG_HELPER_EXPORT void _v8_debug_helper_Free_ObjectPropertiesResult( 787 d::ObjectPropertiesResult* result) { 788 std::unique_ptr<di::ObjectPropertiesResult> ptr( 789 static_cast<di::ObjectPropertiesResultExtended*>(result)->base); 790} 791 792V8_DEBUG_HELPER_EXPORT d::StackFrameResult* _v8_debug_helper_GetStackFrame( 793 uintptr_t frame_pointer, d::MemoryAccessor memory_accessor) { 794 return di::GetStackFrame(frame_pointer, memory_accessor) 795 .release() 796 ->GetPublicView(); 797} 798V8_DEBUG_HELPER_EXPORT void _v8_debug_helper_Free_StackFrameResult( 799 d::StackFrameResult* result) { 800 std::unique_ptr<di::StackFrameResult> ptr( 801 static_cast<di::StackFrameResultExtended*>(result)->base); 802} 803} 804