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/local-variables.h"
6 
7 #include <vector>
8 
9 #include "tools/v8windbg/base/utilities.h"
10 #include "tools/v8windbg/src/object-inspection.h"
11 #include "tools/v8windbg/src/v8-debug-helper-interop.h"
12 #include "tools/v8windbg/src/v8windbg-extension.h"
13 
V8LocalVariables(WRL::ComPtr<IModelPropertyAccessor> original,bool is_parameters)14 V8LocalVariables::V8LocalVariables(WRL::ComPtr<IModelPropertyAccessor> original,
15                                    bool is_parameters)
16     : original_(original), is_parameters_(is_parameters) {}
17 V8LocalVariables::~V8LocalVariables() = default;
18 
GetValue(PCWSTR key,IModelObject * context,IModelObject ** value)19 IFACEMETHODIMP V8LocalVariables::GetValue(PCWSTR key, IModelObject* context,
20                                           IModelObject** value) noexcept {
21   // See if the frame can fetch locals based on symbols. If so, it's a normal
22   // C++ frame, so we can be done.
23   HRESULT original_hr = original_->GetValue(key, context, value);
24   if (SUCCEEDED(original_hr)) return original_hr;
25 
26   // Next, try to find out about the instruction pointer. If it is within the V8
27   // module, or points to unknown space outside a module (generated code), then
28   // we're interested. Otherwise, we have nothing useful to do.
29   WRL::ComPtr<IModelObject> attributes;
30   RETURN_IF_FAIL(context->GetKeyValue(L"Attributes", &attributes, nullptr));
31   WRL::ComPtr<IModelObject> boxed_instruction_offset;
32   RETURN_IF_FAIL(attributes->GetKeyValue(L"InstructionOffset",
33                                          &boxed_instruction_offset, nullptr));
34   ULONG64 instruction_offset{};
35   RETURN_IF_FAIL(
36       UnboxULong64(boxed_instruction_offset.Get(), &instruction_offset));
37   WRL::ComPtr<IDebugHostSymbols> symbols;
38   RETURN_IF_FAIL(sp_debug_host.As(&symbols));
39   WRL::ComPtr<IDebugHostContext> host_context;
40   RETURN_IF_FAIL(sp_debug_host->GetCurrentContext(&host_context));
41   WRL::ComPtr<IDebugHostModule> module;
42   if (SUCCEEDED(symbols->FindModuleByLocation(host_context.Get(),
43                                               instruction_offset, &module))) {
44     Location module_base;
45     RETURN_IF_FAIL(module->GetBaseLocation(&module_base));
46     WRL::ComPtr<IDebugHostModule> v8_module =
47         Extension::Current()->GetV8Module(host_context);
48     if (v8_module == nullptr) {
49       // Anything in a module must not be in the V8 module if the V8 module
50       // doesn't exist.
51       return original_hr;
52     }
53     Location v8_base;
54     RETURN_IF_FAIL(v8_module->GetBaseLocation(&v8_base));
55     if (module_base != v8_base) {
56       // It's in a module, but not the one that contains V8.
57       return original_hr;
58     }
59   }
60 
61   // Initialize an empty result object.
62   WRL::ComPtr<IModelObject> result;
63   RETURN_IF_FAIL(sp_data_model_manager->CreateSyntheticObject(
64       host_context.Get(), &result));
65   WRL::ComPtr<IModelObject> parent_model;
66   RETURN_IF_FAIL(sp_data_model_manager->AcquireNamedModel(
67       is_parameters_ ? L"Debugger.Models.Parameters"
68                      : L"Debugger.Models.LocalVariables",
69       &parent_model));
70   RETURN_IF_FAIL(result->AddParentModel(parent_model.Get(), /*context=*/nullptr,
71                                         /*override=*/false));
72 
73   if (is_parameters_) {
74     // We're not actually adding any parameters data yet; we just need it to not
75     // fail so that the locals pane displays the LocalVariables. The locals pane
76     // displays nothing if getting either LocalVariables or Parameters fails.
77     *value = result.Detach();
78     return S_OK;
79   }
80 
81   // Get the stack and frame pointers for the current frame.
82   WRL::ComPtr<IModelObject> boxed_stack_offset;
83   RETURN_IF_FAIL(
84       attributes->GetKeyValue(L"StackOffset", &boxed_stack_offset, nullptr));
85   ULONG64 stack_offset{};
86   RETURN_IF_FAIL(UnboxULong64(boxed_stack_offset.Get(), &stack_offset));
87   WRL::ComPtr<IModelObject> boxed_frame_offset;
88   RETURN_IF_FAIL(
89       attributes->GetKeyValue(L"FrameOffset", &boxed_frame_offset, nullptr));
90   ULONG64 frame_offset{};
91   RETURN_IF_FAIL(UnboxULong64(boxed_frame_offset.Get(), &frame_offset));
92 
93   // Eventually v8_debug_helper will provide some help here, but for now, just
94   // provide the option to view the whole stack frame as tagged data. It can
95   // be somewhat useful.
96   WRL::ComPtr<IDebugHostType> object_type =
97       Extension::Current()->GetV8ObjectType(host_context);
98   if (object_type == nullptr) {
99     // There's nothing useful to do if we can't find the symbol for
100     // v8::internal::Object.
101     return original_hr;
102   }
103   ULONG64 object_size{};
104   RETURN_IF_FAIL(object_type->GetSize(&object_size));
105   ULONG64 num_objects = (frame_offset - stack_offset) / object_size;
106   ArrayDimension dimensions[] = {
107       {/*start=*/0, /*length=*/num_objects, /*stride=*/object_size}};
108   WRL::ComPtr<IDebugHostType> object_array_type;
109   RETURN_IF_FAIL(object_type->CreateArrayOf(/*dimensions=*/1, dimensions,
110                                             &object_array_type));
111   WRL::ComPtr<IModelObject> array;
112   RETURN_IF_FAIL(sp_data_model_manager->CreateTypedObject(
113       host_context.Get(), stack_offset, object_array_type.Get(), &array));
114   RETURN_IF_FAIL(
115       result->SetKey(L"memory interpreted as Objects", array.Get(), nullptr));
116 
117   std::vector<Property> properties = GetStackFrame(host_context, frame_offset);
118   for (const auto& prop : properties) {
119     WRL::ComPtr<IModelObject> property;
120     RETURN_IF_FAIL(GetModelForProperty(prop, host_context, &property));
121     result->SetKey(reinterpret_cast<const wchar_t*>(prop.name.c_str()),
122                    property.Get(), nullptr);
123   }
124 
125   *value = result.Detach();
126   return S_OK;
127 }
128 
SetValue(PCWSTR key,IModelObject * context,IModelObject * value)129 IFACEMETHODIMP V8LocalVariables::SetValue(PCWSTR key, IModelObject* context,
130                                           IModelObject* value) noexcept {
131   return E_NOTIMPL;
132 }
133