|  | // Copyright 2020 the V8 project authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "tools/v8windbg/src/v8-debug-helper-interop.h" | 
|  |  | 
|  | #include <Windows.h> | 
|  | #include <crtdbg.h> | 
|  |  | 
|  | #include "src/common/globals.h" | 
|  | #include "tools/debug_helper/debug-helper.h" | 
|  | #include "tools/v8windbg/base/utilities.h" | 
|  | #include "tools/v8windbg/src/v8windbg-extension.h" | 
|  |  | 
|  | namespace d = v8::debug_helper; | 
|  |  | 
|  | // We need a plain C function pointer for interop with v8_debug_helper. We can | 
|  | // use this to get one as long as we never need two at once. | 
|  | class MemReaderScope { | 
|  | public: | 
|  | explicit MemReaderScope(WRL::ComPtr<IDebugHostContext> sp_context) | 
|  | : sp_context_(sp_context) { | 
|  | _ASSERTE(!context_); | 
|  | context_ = sp_context_.Get(); | 
|  | } | 
|  | ~MemReaderScope() { context_ = nullptr; } | 
|  | d::MemoryAccessor GetReader() { return &MemReaderScope::Read; } | 
|  |  | 
|  | private: | 
|  | MemReaderScope(const MemReaderScope&) = delete; | 
|  | MemReaderScope& operator=(const MemReaderScope&) = delete; | 
|  | static d::MemoryAccessResult Read(uintptr_t address, void* destination, | 
|  | size_t byte_count) { | 
|  | ULONG64 bytes_read; | 
|  | Location loc{address}; | 
|  | HRESULT hr = sp_debug_host_memory->ReadBytes(context_, loc, destination, | 
|  | byte_count, &bytes_read); | 
|  | // TODO determine when an address is valid but inaccessible | 
|  | return SUCCEEDED(hr) ? d::MemoryAccessResult::kOk | 
|  | : d::MemoryAccessResult::kAddressNotValid; | 
|  | } | 
|  | WRL::ComPtr<IDebugHostContext> sp_context_; | 
|  | static IDebugHostContext* context_; | 
|  | }; | 
|  | IDebugHostContext* MemReaderScope::context_; | 
|  |  | 
|  | StructField::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) | 
|  | : name(field_name), | 
|  | type_name(type_name), | 
|  | uncompressed_type_name(uncompressed_type_name), | 
|  | offset(offset), | 
|  | num_bits(num_bits), | 
|  | shift_bits(shift_bits) {} | 
|  | StructField::~StructField() = default; | 
|  | StructField::StructField(const StructField&) = default; | 
|  | StructField::StructField(StructField&&) = default; | 
|  | StructField& StructField::operator=(const StructField&) = default; | 
|  | StructField& StructField::operator=(StructField&&) = default; | 
|  |  | 
|  | Property::Property(std::u16string property_name, std::u16string type_name, | 
|  | std::string uncompressed_type_name, uint64_t address, | 
|  | size_t item_size) | 
|  | : name(property_name), | 
|  | type(PropertyType::kPointer), | 
|  | type_name(type_name), | 
|  | uncompressed_type_name(uncompressed_type_name), | 
|  | addr_value(address), | 
|  | item_size(item_size) {} | 
|  | Property::~Property() = default; | 
|  | Property::Property(const Property&) = default; | 
|  | Property::Property(Property&&) = default; | 
|  | Property& Property::operator=(const Property&) = default; | 
|  | Property& Property::operator=(Property&&) = default; | 
|  |  | 
|  | V8HeapObject::V8HeapObject() = default; | 
|  | V8HeapObject::~V8HeapObject() = default; | 
|  | V8HeapObject::V8HeapObject(const V8HeapObject&) = default; | 
|  | V8HeapObject::V8HeapObject(V8HeapObject&&) = default; | 
|  | V8HeapObject& V8HeapObject::operator=(const V8HeapObject&) = default; | 
|  | V8HeapObject& V8HeapObject::operator=(V8HeapObject&&) = default; | 
|  |  | 
|  | std::vector<Property> GetPropertiesAsVector(size_t num_properties, | 
|  | d::ObjectProperty** properties) { | 
|  | std::vector<Property> result; | 
|  | for (size_t property_index = 0; property_index < num_properties; | 
|  | ++property_index) { | 
|  | const auto& source_prop = *(properties)[property_index]; | 
|  | Property dest_prop(ConvertToU16String(source_prop.name), | 
|  | ConvertToU16String(source_prop.type), | 
|  | source_prop.decompressed_type, source_prop.address, | 
|  | source_prop.size); | 
|  | if (source_prop.kind != d::PropertyKind::kSingle) { | 
|  | dest_prop.type = PropertyType::kArray; | 
|  | dest_prop.length = source_prop.num_values; | 
|  | } | 
|  | if (dest_prop.type_name.empty() || source_prop.num_struct_fields > 0) { | 
|  | // If the helper library didn't provide a type, then it should have | 
|  | // provided struct fields instead. Set the struct type flag and copy the | 
|  | // fields into the result. | 
|  | dest_prop.type = | 
|  | static_cast<PropertyType>(static_cast<int>(dest_prop.type) | | 
|  | static_cast<int>(PropertyType::kStruct)); | 
|  | for (size_t field_index = 0; field_index < source_prop.num_struct_fields; | 
|  | ++field_index) { | 
|  | const auto& struct_field = *source_prop.struct_fields[field_index]; | 
|  | dest_prop.fields.push_back({ConvertToU16String(struct_field.name), | 
|  | ConvertToU16String(struct_field.type), | 
|  | struct_field.decompressed_type, | 
|  | struct_field.offset, struct_field.num_bits, | 
|  | struct_field.shift_bits}); | 
|  | } | 
|  | } | 
|  | result.push_back(dest_prop); | 
|  | } | 
|  | return result; | 
|  | } | 
|  |  | 
|  | V8HeapObject GetHeapObject(WRL::ComPtr<IDebugHostContext> sp_context, | 
|  | uint64_t tagged_ptr, uint64_t referring_pointer, | 
|  | const char* type_name, bool is_compressed) { | 
|  | // Read the value at the address, and see if it is a tagged pointer | 
|  |  | 
|  | V8HeapObject obj; | 
|  | MemReaderScope reader_scope(sp_context); | 
|  |  | 
|  | d::HeapAddresses heap_addresses = {0, 0, 0, 0}; | 
|  | // TODO ideally we'd provide real heap page pointers. For now, just testing | 
|  | // decompression based on the pointer to wherever we found this value, | 
|  | // which is likely (though not guaranteed) to be a heap pointer itself. | 
|  | heap_addresses.any_heap_pointer = referring_pointer; | 
|  |  | 
|  | auto props = d::GetObjectProperties(tagged_ptr, reader_scope.GetReader(), | 
|  | heap_addresses, type_name); | 
|  | obj.friendly_name = ConvertToU16String(props->brief); | 
|  | obj.properties = | 
|  | GetPropertiesAsVector(props->num_properties, props->properties); | 
|  |  | 
|  | // For each guessed type, create a synthetic property that will request data | 
|  | // about the same object again but with a more specific type hint. | 
|  | if (referring_pointer != 0) { | 
|  | for (size_t type_index = 0; type_index < props->num_guessed_types; | 
|  | ++type_index) { | 
|  | const std::string& type_name = props->guessed_types[type_index]; | 
|  | Property dest_prop( | 
|  | ConvertToU16String(("guessed type " + type_name).c_str()), | 
|  | ConvertToU16String(is_compressed ? kTaggedValue : type_name), | 
|  | type_name, referring_pointer, | 
|  | is_compressed ? i::kTaggedSize : sizeof(void*)); | 
|  | obj.properties.push_back(dest_prop); | 
|  | } | 
|  | } | 
|  |  | 
|  | return obj; | 
|  | } | 
|  |  | 
|  | std::vector<std::u16string> ListObjectClasses() { | 
|  | const d::ClassList* class_list = d::ListObjectClasses(); | 
|  | std::vector<std::u16string> result; | 
|  | for (size_t i = 0; i < class_list->num_class_names; ++i) { | 
|  | result.push_back(ConvertToU16String(class_list->class_names[i])); | 
|  | } | 
|  | return result; | 
|  | } | 
|  |  | 
|  | const char* BitsetName(uint64_t payload) { return d::BitsetName(payload); } | 
|  |  | 
|  | std::vector<Property> GetStackFrame(WRL::ComPtr<IDebugHostContext> sp_context, | 
|  |  | 
|  | uint64_t frame_pointer) { | 
|  | MemReaderScope reader_scope(sp_context); | 
|  | auto props = d::GetStackFrame(static_cast<uintptr_t>(frame_pointer), | 
|  | reader_scope.GetReader()); | 
|  | return GetPropertiesAsVector(props->num_properties, props->properties); | 
|  | } |