| // 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/local-variables.h" |
| |
| #include <vector> |
| |
| #include "tools/v8windbg/base/utilities.h" |
| #include "tools/v8windbg/src/object-inspection.h" |
| #include "tools/v8windbg/src/v8-debug-helper-interop.h" |
| #include "tools/v8windbg/src/v8windbg-extension.h" |
| |
| V8LocalVariables::V8LocalVariables(WRL::ComPtr<IModelPropertyAccessor> original, |
| bool is_parameters) |
| : original_(original), is_parameters_(is_parameters) {} |
| V8LocalVariables::~V8LocalVariables() = default; |
| |
| IFACEMETHODIMP V8LocalVariables::GetValue(PCWSTR key, IModelObject* context, |
| IModelObject** value) noexcept { |
| // See if the frame can fetch locals based on symbols. If so, it's a normal |
| // C++ frame, so we can be done. |
| HRESULT original_hr = original_->GetValue(key, context, value); |
| if (SUCCEEDED(original_hr)) return original_hr; |
| |
| // Next, try to find out about the instruction pointer. If it is within the V8 |
| // module, or points to unknown space outside a module (generated code), then |
| // we're interested. Otherwise, we have nothing useful to do. |
| WRL::ComPtr<IModelObject> attributes; |
| RETURN_IF_FAIL(context->GetKeyValue(L"Attributes", &attributes, nullptr)); |
| WRL::ComPtr<IModelObject> boxed_instruction_offset; |
| RETURN_IF_FAIL(attributes->GetKeyValue(L"InstructionOffset", |
| &boxed_instruction_offset, nullptr)); |
| ULONG64 instruction_offset{}; |
| RETURN_IF_FAIL( |
| UnboxULong64(boxed_instruction_offset.Get(), &instruction_offset)); |
| WRL::ComPtr<IDebugHostSymbols> symbols; |
| RETURN_IF_FAIL(sp_debug_host.As(&symbols)); |
| WRL::ComPtr<IDebugHostContext> host_context; |
| RETURN_IF_FAIL(sp_debug_host->GetCurrentContext(&host_context)); |
| WRL::ComPtr<IDebugHostModule> module; |
| if (SUCCEEDED(symbols->FindModuleByLocation(host_context.Get(), |
| instruction_offset, &module))) { |
| Location module_base; |
| RETURN_IF_FAIL(module->GetBaseLocation(&module_base)); |
| WRL::ComPtr<IDebugHostModule> v8_module = |
| Extension::Current()->GetV8Module(host_context); |
| if (v8_module == nullptr) { |
| // Anything in a module must not be in the V8 module if the V8 module |
| // doesn't exist. |
| return original_hr; |
| } |
| Location v8_base; |
| RETURN_IF_FAIL(v8_module->GetBaseLocation(&v8_base)); |
| if (module_base != v8_base) { |
| // It's in a module, but not the one that contains V8. |
| return original_hr; |
| } |
| } |
| |
| // Initialize an empty result object. |
| WRL::ComPtr<IModelObject> result; |
| RETURN_IF_FAIL(sp_data_model_manager->CreateSyntheticObject( |
| host_context.Get(), &result)); |
| WRL::ComPtr<IModelObject> parent_model; |
| RETURN_IF_FAIL(sp_data_model_manager->AcquireNamedModel( |
| is_parameters_ ? L"Debugger.Models.Parameters" |
| : L"Debugger.Models.LocalVariables", |
| &parent_model)); |
| RETURN_IF_FAIL(result->AddParentModel(parent_model.Get(), /*context=*/nullptr, |
| /*override=*/false)); |
| |
| if (is_parameters_) { |
| // We're not actually adding any parameters data yet; we just need it to not |
| // fail so that the locals pane displays the LocalVariables. The locals pane |
| // displays nothing if getting either LocalVariables or Parameters fails. |
| *value = result.Detach(); |
| return S_OK; |
| } |
| |
| // Get the stack and frame pointers for the current frame. |
| WRL::ComPtr<IModelObject> boxed_stack_offset; |
| RETURN_IF_FAIL( |
| attributes->GetKeyValue(L"StackOffset", &boxed_stack_offset, nullptr)); |
| ULONG64 stack_offset{}; |
| RETURN_IF_FAIL(UnboxULong64(boxed_stack_offset.Get(), &stack_offset)); |
| WRL::ComPtr<IModelObject> boxed_frame_offset; |
| RETURN_IF_FAIL( |
| attributes->GetKeyValue(L"FrameOffset", &boxed_frame_offset, nullptr)); |
| ULONG64 frame_offset{}; |
| RETURN_IF_FAIL(UnboxULong64(boxed_frame_offset.Get(), &frame_offset)); |
| |
| // Eventually v8_debug_helper will provide some help here, but for now, just |
| // provide the option to view the whole stack frame as tagged data. It can |
| // be somewhat useful. |
| WRL::ComPtr<IDebugHostType> object_type = |
| Extension::Current()->GetV8ObjectType(host_context); |
| if (object_type == nullptr) { |
| // There's nothing useful to do if we can't find the symbol for |
| // v8::internal::Object. |
| return original_hr; |
| } |
| ULONG64 object_size{}; |
| RETURN_IF_FAIL(object_type->GetSize(&object_size)); |
| ULONG64 num_objects = (frame_offset - stack_offset) / object_size; |
| ArrayDimension dimensions[] = { |
| {/*start=*/0, /*length=*/num_objects, /*stride=*/object_size}}; |
| WRL::ComPtr<IDebugHostType> object_array_type; |
| RETURN_IF_FAIL(object_type->CreateArrayOf(/*dimensions=*/1, dimensions, |
| &object_array_type)); |
| WRL::ComPtr<IModelObject> array; |
| RETURN_IF_FAIL(sp_data_model_manager->CreateTypedObject( |
| host_context.Get(), stack_offset, object_array_type.Get(), &array)); |
| RETURN_IF_FAIL( |
| result->SetKey(L"memory interpreted as Objects", array.Get(), nullptr)); |
| |
| std::vector<Property> properties = GetStackFrame(host_context, frame_offset); |
| for (const auto& prop : properties) { |
| WRL::ComPtr<IModelObject> property; |
| RETURN_IF_FAIL(GetModelForProperty(prop, host_context, &property)); |
| result->SetKey(reinterpret_cast<const wchar_t*>(prop.name.c_str()), |
| property.Get(), nullptr); |
| } |
| |
| *value = result.Detach(); |
| return S_OK; |
| } |
| |
| IFACEMETHODIMP V8LocalVariables::SetValue(PCWSTR key, IModelObject* context, |
| IModelObject* value) noexcept { |
| return E_NOTIMPL; |
| } |