• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
19 namespace i = v8::internal;
20 
21 namespace v8 {
22 namespace internal {
23 namespace debug_helper_internal {
24 
25 constexpr char kObject[] = "v8::internal::Object";
26 constexpr char kTaggedValue[] = "v8::internal::TaggedValue";
27 constexpr char kSmi[] = "v8::internal::Smi";
28 constexpr char kHeapObject[] = "v8::internal::HeapObject";
29 #ifdef V8_COMPRESS_POINTERS
30 constexpr char kObjectAsStoredInHeap[] = "v8::internal::TaggedValue";
31 #else
32 constexpr char kObjectAsStoredInHeap[] = "v8::internal::Object";
33 #endif
34 
AppendAddressAndType(const std::string & brief,uintptr_t address,const char * type)35 std::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 
JoinWithSpace(const std::string & a,const std::string & b)43 std::string JoinWithSpace(const std::string& a, const std::string& b) {
44   return a.empty() || b.empty() ? a + b : a + " " + b;
45 }
46 
47 struct TypedObject {
TypedObjectv8::internal::debug_helper_internal::TypedObject48   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 
GetTypedObjectByHint(uintptr_t address,std::string type_hint_string)64 TypedObject 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 
GetTypedObjectForString(uintptr_t address,i::InstanceType type,d::TypeCheckResult type_source)82 TypedObject 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 
GetTypedObjectByInstanceType(uintptr_t address,i::InstanceType type,d::TypeCheckResult type_source)105 TypedObject 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 
IsTypedHeapObjectInstanceTypeOf(uintptr_t address,d::MemoryAccessor accessor,i::InstanceType instance_type)137 bool 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 
GetTypedHeapObject(uintptr_t address,d::MemoryAccessor accessor,const char * type_hint,const d::HeapAddresses & heap_addresses)154 TypedObject 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.
220 class ReadStringVisitor : public TqObjectVisitor {
221  public:
Visit(d::MemoryAccessor accessor,const d::HeapAddresses & heap_addresses,const TqString * object)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.
GetString()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>
ReadCharacter(uintptr_t data_address,int32_t index)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>
ReadStringCharacters(const TqString * object,uintptr_t data_address)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>
ReadSeqString(const TString * object)268   void ReadSeqString(const TString* object) {
269     ReadStringCharacters<TChar>(object, object->GetCharsAddress());
270   }
271 
VisitSeqOneByteString(const TqSeqOneByteString * object)272   void VisitSeqOneByteString(const TqSeqOneByteString* object) override {
273     ReadSeqString<char>(object);
274   }
275 
VisitSeqTwoByteString(const TqSeqTwoByteString * object)276   void VisitSeqTwoByteString(const TqSeqTwoByteString* object) override {
277     ReadSeqString<char16_t>(object);
278   }
279 
VisitConsString(const TqConsString * object)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 
VisitSlicedString(const TqSlicedString * object)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 
VisitThinString(const TqThinString * object)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 
IsExternalStringCached(const TqExternalString * object)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>
ReadExternalString(const TqExternalString * object)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 
VisitExternalOneByteString(const TqExternalOneByteString * object)370   void VisitExternalOneByteString(
371       const TqExternalOneByteString* object) override {
372     ReadExternalString<char>(object);
373   }
374 
VisitExternalTwoByteString(const TqExternalTwoByteString * object)375   void VisitExternalTwoByteString(
376       const TqExternalTwoByteString* object) override {
377     ReadExternalString<char16_t>(object);
378   }
379 
VisitObject(const TqObject * object)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:
ReadStringVisitor(d::MemoryAccessor accessor,const d::HeapAddresses & heap_addresses)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>
GetOrFinish(Value<T> value)399   T GetOrFinish(Value<T> value) {
400     if (value.validity != d::MemoryAccessResult::kOk) {
401       AddEllipsisAndFinish();
402     }
403     return value.value;
404   }
405 
AddEllipsisAndFinish()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 
AddCharacter(char16_t c)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:
IndexModifier(ReadStringVisitor * that,int32_t index_adjust,int32_t limit_adjust)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;
~IndexModifier()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.
462 class 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>>>
Visit(const TqObject * object,d::MemoryAccessor accessor,const d::HeapAddresses & heap_addresses)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 
VisitString(const TqString * object)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 
VisitExternalString(const TqExternalString * object)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 
VisitJSObject(const TqJSObject * object)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:
AddInfoVisitor(d::MemoryAccessor accessor,const d::HeapAddresses & heap_addresses)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 
GetHeapObjectPropertiesNotCompressed(uintptr_t address,d::MemoryAccessor accessor,const char * type_hint,const d::HeapAddresses & heap_addresses)553 std::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 
GetHeapObjectPropertiesMaybeCompressed(uintptr_t address,d::MemoryAccessor memory_accessor,d::HeapAddresses heap_addresses,const char * type_hint)599 std::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 
GetObjectProperties(uintptr_t address,d::MemoryAccessor memory_accessor,const d::HeapAddresses & heap_addresses,const char * type_hint)629 std::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 
GetStackFrame(uintptr_t frame_pointer,d::MemoryAccessor memory_accessor)659 std::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 
773 namespace di = v8::internal::debug_helper_internal;
774 
775 extern "C" {
776 V8_DEBUG_HELPER_EXPORT d::ObjectPropertiesResult*
_v8_debug_helper_GetObjectProperties(uintptr_t object,d::MemoryAccessor memory_accessor,const d::HeapAddresses & heap_addresses,const char * type_hint)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 }
_v8_debug_helper_Free_ObjectPropertiesResult(d::ObjectPropertiesResult * result)786 V8_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 
_v8_debug_helper_GetStackFrame(uintptr_t frame_pointer,d::MemoryAccessor memory_accessor)792 V8_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 }
_v8_debug_helper_Free_StackFrameResult(d::StackFrameResult * result)798 V8_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