1 // Copyright 2020 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 "tools/v8windbg/src/v8-debug-helper-interop.h"
6 
7 #include <Windows.h>
8 #include <crtdbg.h>
9 
10 #include "src/common/globals.h"
11 #include "tools/debug_helper/debug-helper.h"
12 #include "tools/v8windbg/base/utilities.h"
13 #include "tools/v8windbg/src/v8windbg-extension.h"
14 
15 namespace d = v8::debug_helper;
16 
17 // We need a plain C function pointer for interop with v8_debug_helper. We can
18 // use this to get one as long as we never need two at once.
19 class MemReaderScope {
20  public:
MemReaderScope(WRL::ComPtr<IDebugHostContext> sp_context)21   explicit MemReaderScope(WRL::ComPtr<IDebugHostContext> sp_context)
22       : sp_context_(sp_context) {
23     _ASSERTE(!context_);
24     context_ = sp_context_.Get();
25   }
~MemReaderScope()26   ~MemReaderScope() { context_ = nullptr; }
GetReader()27   d::MemoryAccessor GetReader() { return &MemReaderScope::Read; }
28 
29  private:
30   MemReaderScope(const MemReaderScope&) = delete;
31   MemReaderScope& operator=(const MemReaderScope&) = delete;
Read(uintptr_t address,void * destination,size_t byte_count)32   static d::MemoryAccessResult Read(uintptr_t address, void* destination,
33                                     size_t byte_count) {
34     ULONG64 bytes_read;
35     Location loc{address};
36     HRESULT hr = sp_debug_host_memory->ReadBytes(context_, loc, destination,
37                                                  byte_count, &bytes_read);
38     // TODO determine when an address is valid but inaccessible
39     return SUCCEEDED(hr) ? d::MemoryAccessResult::kOk
40                          : d::MemoryAccessResult::kAddressNotValid;
41   }
42   WRL::ComPtr<IDebugHostContext> sp_context_;
43   static IDebugHostContext* context_;
44 };
45 IDebugHostContext* MemReaderScope::context_;
46 
StructField(std::u16string field_name,std::u16string type_name,std::string uncompressed_type_name,uint64_t offset,uint8_t num_bits,uint8_t shift_bits)47 StructField::StructField(std::u16string field_name, std::u16string type_name,
48                          std::string uncompressed_type_name, uint64_t offset,
49                          uint8_t num_bits, uint8_t shift_bits)
50     : name(field_name),
51       type_name(type_name),
52       uncompressed_type_name(uncompressed_type_name),
53       offset(offset),
54       num_bits(num_bits),
55       shift_bits(shift_bits) {}
56 StructField::~StructField() = default;
57 StructField::StructField(const StructField&) = default;
58 StructField::StructField(StructField&&) = default;
59 StructField& StructField::operator=(const StructField&) = default;
60 StructField& StructField::operator=(StructField&&) = default;
61 
Property(std::u16string property_name,std::u16string type_name,std::string uncompressed_type_name,uint64_t address,size_t item_size)62 Property::Property(std::u16string property_name, std::u16string type_name,
63                    std::string uncompressed_type_name, uint64_t address,
64                    size_t item_size)
65     : name(property_name),
66       type(PropertyType::kPointer),
67       type_name(type_name),
68       uncompressed_type_name(uncompressed_type_name),
69       addr_value(address),
70       item_size(item_size) {}
71 Property::~Property() = default;
72 Property::Property(const Property&) = default;
73 Property::Property(Property&&) = default;
74 Property& Property::operator=(const Property&) = default;
75 Property& Property::operator=(Property&&) = default;
76 
77 V8HeapObject::V8HeapObject() = default;
78 V8HeapObject::~V8HeapObject() = default;
79 V8HeapObject::V8HeapObject(const V8HeapObject&) = default;
80 V8HeapObject::V8HeapObject(V8HeapObject&&) = default;
81 V8HeapObject& V8HeapObject::operator=(const V8HeapObject&) = default;
82 V8HeapObject& V8HeapObject::operator=(V8HeapObject&&) = default;
83 
GetPropertiesAsVector(size_t num_properties,d::ObjectProperty ** properties)84 std::vector<Property> GetPropertiesAsVector(size_t num_properties,
85                                             d::ObjectProperty** properties) {
86   std::vector<Property> result;
87   for (size_t property_index = 0; property_index < num_properties;
88        ++property_index) {
89     const auto& source_prop = *(properties)[property_index];
90     Property dest_prop(ConvertToU16String(source_prop.name),
91                        ConvertToU16String(source_prop.type),
92                        source_prop.decompressed_type, source_prop.address,
93                        source_prop.size);
94     if (source_prop.kind != d::PropertyKind::kSingle) {
95       dest_prop.type = PropertyType::kArray;
96       dest_prop.length = source_prop.num_values;
97     }
98     if (dest_prop.type_name.empty() || source_prop.num_struct_fields > 0) {
99       // If the helper library didn't provide a type, then it should have
100       // provided struct fields instead. Set the struct type flag and copy the
101       // fields into the result.
102       dest_prop.type =
103           static_cast<PropertyType>(static_cast<int>(dest_prop.type) |
104                                     static_cast<int>(PropertyType::kStruct));
105       for (size_t field_index = 0; field_index < source_prop.num_struct_fields;
106            ++field_index) {
107         const auto& struct_field = *source_prop.struct_fields[field_index];
108         dest_prop.fields.push_back({ConvertToU16String(struct_field.name),
109                                     ConvertToU16String(struct_field.type),
110                                     struct_field.decompressed_type,
111                                     struct_field.offset, struct_field.num_bits,
112                                     struct_field.shift_bits});
113       }
114     }
115     result.push_back(dest_prop);
116   }
117   return result;
118 }
119 
GetHeapObject(WRL::ComPtr<IDebugHostContext> sp_context,uint64_t tagged_ptr,uint64_t referring_pointer,const char * type_name,bool is_compressed)120 V8HeapObject GetHeapObject(WRL::ComPtr<IDebugHostContext> sp_context,
121                            uint64_t tagged_ptr, uint64_t referring_pointer,
122                            const char* type_name, bool is_compressed) {
123   // Read the value at the address, and see if it is a tagged pointer
124 
125   V8HeapObject obj;
126   MemReaderScope reader_scope(sp_context);
127 
128   d::HeapAddresses heap_addresses = {0, 0, 0, 0};
129   // TODO ideally we'd provide real heap page pointers. For now, just testing
130   // decompression based on the pointer to wherever we found this value,
131   // which is likely (though not guaranteed) to be a heap pointer itself.
132   heap_addresses.any_heap_pointer = referring_pointer;
133 
134   auto props = d::GetObjectProperties(tagged_ptr, reader_scope.GetReader(),
135                                       heap_addresses, type_name);
136   obj.friendly_name = ConvertToU16String(props->brief);
137   obj.properties =
138       GetPropertiesAsVector(props->num_properties, props->properties);
139 
140   // For each guessed type, create a synthetic property that will request data
141   // about the same object again but with a more specific type hint.
142   if (referring_pointer != 0) {
143     for (size_t type_index = 0; type_index < props->num_guessed_types;
144          ++type_index) {
145       const std::string& type_name = props->guessed_types[type_index];
146       Property dest_prop(
147           ConvertToU16String(("guessed type " + type_name).c_str()),
148           ConvertToU16String(is_compressed ? kTaggedValue : type_name),
149           type_name, referring_pointer,
150           is_compressed ? i::kTaggedSize : sizeof(void*));
151       obj.properties.push_back(dest_prop);
152     }
153   }
154 
155   return obj;
156 }
157 
ListObjectClasses()158 std::vector<std::u16string> ListObjectClasses() {
159   const d::ClassList* class_list = d::ListObjectClasses();
160   std::vector<std::u16string> result;
161   for (size_t i = 0; i < class_list->num_class_names; ++i) {
162     result.push_back(ConvertToU16String(class_list->class_names[i]));
163   }
164   return result;
165 }
166 
BitsetName(uint64_t payload)167 const char* BitsetName(uint64_t payload) { return d::BitsetName(payload); }
168 
GetStackFrame(WRL::ComPtr<IDebugHostContext> sp_context,uint64_t frame_pointer)169 std::vector<Property> GetStackFrame(WRL::ComPtr<IDebugHostContext> sp_context,
170 
171                                     uint64_t frame_pointer) {
172   MemReaderScope reader_scope(sp_context);
173   auto props = d::GetStackFrame(static_cast<uintptr_t>(frame_pointer),
174                                 reader_scope.GetReader());
175   return GetPropertiesAsVector(props->num_properties, props->properties);
176 }
177