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 15namespace 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. 19class V8_NODISCARD MemReaderScope { 20 public: 21 explicit MemReaderScope(WRL::ComPtr<IDebugHostContext> sp_context) 22 : sp_context_(sp_context) { 23 _ASSERTE(!context_); 24 context_ = sp_context_.Get(); 25 } 26 ~MemReaderScope() { context_ = nullptr; } 27 d::MemoryAccessor GetReader() { return &MemReaderScope::Read; } 28 29 private: 30 MemReaderScope(const MemReaderScope&) = delete; 31 MemReaderScope& operator=(const MemReaderScope&) = delete; 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}; 45IDebugHostContext* MemReaderScope::context_; 46 47StructField::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) {} 56StructField::~StructField() = default; 57StructField::StructField(const StructField&) = default; 58StructField::StructField(StructField&&) = default; 59StructField& StructField::operator=(const StructField&) = default; 60StructField& StructField::operator=(StructField&&) = default; 61 62Property::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) {} 71Property::~Property() = default; 72Property::Property(const Property&) = default; 73Property::Property(Property&&) = default; 74Property& Property::operator=(const Property&) = default; 75Property& Property::operator=(Property&&) = default; 76 77V8HeapObject::V8HeapObject() = default; 78V8HeapObject::~V8HeapObject() = default; 79V8HeapObject::V8HeapObject(const V8HeapObject&) = default; 80V8HeapObject::V8HeapObject(V8HeapObject&&) = default; 81V8HeapObject& V8HeapObject::operator=(const V8HeapObject&) = default; 82V8HeapObject& V8HeapObject::operator=(V8HeapObject&&) = default; 83 84std::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 120V8HeapObject 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 158std::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 167const char* BitsetName(uint64_t payload) { return d::BitsetName(payload); } 168 169std::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