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