• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/v8windbg-extension.h"
6 
7 #include <iostream>
8 
9 #include "tools/v8windbg/base/utilities.h"
10 #include "tools/v8windbg/src/cur-isolate.h"
11 #include "tools/v8windbg/src/js-stack.h"
12 #include "tools/v8windbg/src/local-variables.h"
13 #include "tools/v8windbg/src/object-inspection.h"
14 
15 std::unique_ptr<Extension> Extension::current_extension_ = nullptr;
16 const wchar_t* pcur_isolate = L"curisolate";
17 const wchar_t* pjs_stack = L"jsstack";
18 const wchar_t* pv8_object = L"v8object";
19 
CreateExtension()20 HRESULT CreateExtension() {
21   if (Extension::Current() != nullptr || sp_data_model_manager == nullptr ||
22       sp_debug_host == nullptr) {
23     return E_FAIL;
24   } else {
25     std::unique_ptr<Extension> new_extension(new (std::nothrow) Extension());
26     if (new_extension == nullptr) return E_FAIL;
27     RETURN_IF_FAIL(new_extension->Initialize());
28     Extension::SetExtension(std::move(new_extension));
29     return S_OK;
30   }
31 }
32 
DestroyExtension()33 void DestroyExtension() { Extension::SetExtension(nullptr); }
34 
DoesTypeDeriveFromObject(const WRL::ComPtr<IDebugHostType> & sp_type)35 bool Extension::DoesTypeDeriveFromObject(
36     const WRL::ComPtr<IDebugHostType>& sp_type) {
37   _bstr_t name;
38   HRESULT hr = sp_type->GetName(name.GetAddress());
39   if (!SUCCEEDED(hr)) return false;
40   if (std::string(static_cast<const char*>(name)) == kObject) return true;
41 
42   WRL::ComPtr<IDebugHostSymbolEnumerator> sp_super_class_enumerator;
43   hr = sp_type->EnumerateChildren(SymbolKind::SymbolBaseClass, nullptr,
44                                   &sp_super_class_enumerator);
45   if (!SUCCEEDED(hr)) return false;
46 
47   while (true) {
48     WRL::ComPtr<IDebugHostSymbol> sp_type_symbol;
49     if (sp_super_class_enumerator->GetNext(&sp_type_symbol) != S_OK) break;
50     WRL::ComPtr<IDebugHostBaseClass> sp_base_class;
51     if (FAILED(sp_type_symbol.As(&sp_base_class))) continue;
52     WRL::ComPtr<IDebugHostType> sp_base_type;
53     hr = sp_base_class->GetType(&sp_base_type);
54     if (!SUCCEEDED(hr)) continue;
55     if (DoesTypeDeriveFromObject(sp_base_type)) {
56       return true;
57     }
58   }
59 
60   return false;
61 }
62 
GetV8ObjectType(WRL::ComPtr<IDebugHostContext> & sp_ctx)63 WRL::ComPtr<IDebugHostType> Extension::GetV8ObjectType(
64     WRL::ComPtr<IDebugHostContext>& sp_ctx) {
65   return GetTypeFromV8Module(sp_ctx, kObjectU);
66 }
67 
GetTypeFromV8Module(WRL::ComPtr<IDebugHostContext> & sp_ctx,const char16_t * type_name)68 WRL::ComPtr<IDebugHostType> Extension::GetTypeFromV8Module(
69     WRL::ComPtr<IDebugHostContext>& sp_ctx, const char16_t* type_name) {
70   bool is_equal;
71   if (sp_v8_module_ctx_ == nullptr ||
72       !SUCCEEDED(sp_v8_module_ctx_->IsEqualTo(sp_ctx.Get(), &is_equal)) ||
73       !is_equal) {
74     // Context changed; clear the dictionary.
75     cached_v8_module_types_.clear();
76   }
77 
78   GetV8Module(sp_ctx);  // Will force the correct module to load
79   if (sp_v8_module_ == nullptr) return nullptr;
80 
81   auto& dictionary_entry = cached_v8_module_types_[type_name];
82   if (dictionary_entry == nullptr) {
83     const std::wstring type_name_w(reinterpret_cast<const wchar_t*>(type_name));
84     // The contract from debug_helper functions is to provide type names that
85     // would be valid if used in C++ code within the v8::internal namespace.
86     // They might be fully qualified but aren't required to be. Thus, we must
87     // simluate an "unqualified name lookup" here, by searching for the type
88     // starting in the innermost namespace and working outward.
89     if (SUCCEEDED(sp_v8_module_->FindTypeByName(
90             (L"v8::internal::" + type_name_w).c_str(), &dictionary_entry))) {
91       return dictionary_entry;
92     }
93     if (SUCCEEDED(sp_v8_module_->FindTypeByName((L"v8::" + type_name_w).c_str(),
94                                                 &dictionary_entry))) {
95       return dictionary_entry;
96     }
97     sp_v8_module_->FindTypeByName(reinterpret_cast<PCWSTR>(type_name),
98                                   &dictionary_entry);
99   }
100   return dictionary_entry;
101 }
102 
103 namespace {
104 
105 // Returns whether the given module appears to have symbols for V8 code.
IsV8Module(IDebugHostModule * module)106 bool IsV8Module(IDebugHostModule* module) {
107   WRL::ComPtr<IDebugHostSymbol> sp_isolate_sym;
108   // The below symbol is specific to the main V8 module and is specified with
109   // V8_NOINLINE, so it should always be present.
110   if (FAILED(module->FindSymbolByName(
111           L"v8::internal::Isolate::PushStackTraceAndDie", &sp_isolate_sym))) {
112     return false;
113   }
114   return true;
115 }
116 
117 }  // namespace
118 
GetV8Module(WRL::ComPtr<IDebugHostContext> & sp_ctx)119 WRL::ComPtr<IDebugHostModule> Extension::GetV8Module(
120     WRL::ComPtr<IDebugHostContext>& sp_ctx) {
121   // Return the cached version if it exists and the context is the same
122 
123   // Note: Context will often have the CUSTOM flag set, which never compares
124   // equal. So for now DON'T compare by context, but by proc_id. (An API is in
125   // progress to compare by address space, which should be usable when shipped).
126   /*
127   if (sp_v8_module_ != nullptr) {
128     bool is_equal;
129     if (SUCCEEDED(sp_v8_module_ctx_->IsEqualTo(sp_ctx.Get(), &is_equal)) &&
130   is_equal) { return sp_v8_module_; } else { sp_v8_module_ = nullptr;
131       sp_v8_module_ctx_ = nullptr;
132     }
133   }
134   */
135   WRL::ComPtr<IDebugSystemObjects> sp_sys_objects;
136   ULONG proc_id = 0;
137   if (SUCCEEDED(sp_debug_control.As(&sp_sys_objects))) {
138     if (SUCCEEDED(sp_sys_objects->GetCurrentProcessSystemId(&proc_id))) {
139       if (proc_id == v8_module_proc_id_ && sp_v8_module_ != nullptr)
140         return sp_v8_module_;
141     }
142   }
143 
144   // Search first for a few known module names, to avoid loading symbols for
145   // unrelated modules if we can easily avoid it. Generally, failing to find a
146   // module is fast but failing to find a symbol within a module is slow. Note
147   // that "v8" is listed first because it's highly likely to be the correct
148   // module if it exists. The others might include V8 symbols depending on the
149   // build configuration.
150   std::vector<const wchar_t*> known_names = {
151       L"v8", L"v8_for_testing", L"cctest_exe", L"chrome",
152       L"d8", L"msedge",         L"node",       L"unittests_exe"};
153   for (const wchar_t* name : known_names) {
154     WRL::ComPtr<IDebugHostModule> sp_module;
155     if (SUCCEEDED(sp_debug_host_symbols->FindModuleByName(sp_ctx.Get(), name,
156                                                           &sp_module))) {
157       if (IsV8Module(sp_module.Get())) {
158         sp_v8_module_ = sp_module;
159         sp_v8_module_ctx_ = sp_ctx;
160         v8_module_proc_id_ = proc_id;
161         return sp_v8_module_;
162       }
163     }
164   }
165 
166   // Loop through all modules looking for the one that holds a known symbol.
167   WRL::ComPtr<IDebugHostSymbolEnumerator> sp_enum;
168   if (SUCCEEDED(
169           sp_debug_host_symbols->EnumerateModules(sp_ctx.Get(), &sp_enum))) {
170     HRESULT hr = S_OK;
171     while (true) {
172       WRL::ComPtr<IDebugHostSymbol> sp_mod_sym;
173       hr = sp_enum->GetNext(&sp_mod_sym);
174       // hr == E_BOUNDS : hit the end of the enumerator
175       // hr == E_ABORT  : a user interrupt was requested
176       if (FAILED(hr)) break;
177       WRL::ComPtr<IDebugHostModule> sp_module;
178       if (SUCCEEDED(sp_mod_sym.As(&sp_module))) /* should always succeed */
179       {
180         if (IsV8Module(sp_module.Get())) {
181           sp_v8_module_ = sp_module;
182           sp_v8_module_ctx_ = sp_ctx;
183           v8_module_proc_id_ = proc_id;
184           break;
185         }
186       }
187     }
188   }
189   // This will be the located module, or still nullptr if above fails
190   return sp_v8_module_;
191 }
192 
193 Extension::Extension() = default;
194 
Initialize()195 HRESULT Extension::Initialize() {
196   // Create an instance of the DataModel parent for v8::internal::Object types.
197   auto object_data_model{WRL::Make<V8ObjectDataModel>()};
198   RETURN_IF_FAIL(sp_data_model_manager->CreateDataModelObject(
199       object_data_model.Get(), &sp_object_data_model_));
200   RETURN_IF_FAIL(sp_object_data_model_->SetConcept(
201       __uuidof(IStringDisplayableConcept),
202       static_cast<IStringDisplayableConcept*>(object_data_model.Get()),
203       nullptr));
204   RETURN_IF_FAIL(sp_object_data_model_->SetConcept(
205       __uuidof(IDynamicKeyProviderConcept),
206       static_cast<IDynamicKeyProviderConcept*>(object_data_model.Get()),
207       nullptr));
208 
209   // Register that parent model for all known types of V8 object.
210   std::vector<std::u16string> object_class_names = ListObjectClasses();
211   object_class_names.push_back(kObjectU);
212   object_class_names.push_back(kTaggedValueU);
213   for (const std::u16string& name : object_class_names) {
214     WRL::ComPtr<IDebugHostTypeSignature> sp_object_type_signature;
215     RETURN_IF_FAIL(sp_debug_host_symbols->CreateTypeSignature(
216         reinterpret_cast<const wchar_t*>(name.c_str()), nullptr,
217         &sp_object_type_signature));
218     RETURN_IF_FAIL(sp_data_model_manager->RegisterModelForTypeSignature(
219         sp_object_type_signature.Get(), sp_object_data_model_.Get()));
220     registered_types_.push_back(
221         {sp_object_type_signature.Get(), sp_object_data_model_.Get()});
222   }
223 
224   // Create an instance of the DataModel parent for custom iterable fields.
225   auto indexed_field_model{WRL::Make<IndexedFieldParent>()};
226   RETURN_IF_FAIL(sp_data_model_manager->CreateDataModelObject(
227       indexed_field_model.Get(), &sp_indexed_field_model_));
228   RETURN_IF_FAIL(sp_indexed_field_model_->SetConcept(
229       __uuidof(IIndexableConcept),
230       static_cast<IIndexableConcept*>(indexed_field_model.Get()), nullptr));
231   RETURN_IF_FAIL(sp_indexed_field_model_->SetConcept(
232       __uuidof(IIterableConcept),
233       static_cast<IIterableConcept*>(indexed_field_model.Get()), nullptr));
234 
235   // Create an instance of the DataModel parent class for v8::Local<*> types.
236   auto local_data_model{WRL::Make<V8LocalDataModel>()};
237   RETURN_IF_FAIL(sp_data_model_manager->CreateDataModelObject(
238       local_data_model.Get(), &sp_local_data_model_));
239 
240   // Register that parent model for all known types that act like v8::Local.
241   std::vector<const wchar_t*> handle_class_names = {
242       L"v8::Local<*>", L"v8::MaybeLocal<*>", L"v8::internal::Handle<*>",
243       L"v8::internal::MaybeHandle<*>"};
244   for (const wchar_t* name : handle_class_names) {
245     WRL::ComPtr<IDebugHostTypeSignature> signature;
246     RETURN_IF_FAIL(
247         sp_debug_host_symbols->CreateTypeSignature(name, nullptr, &signature));
248     RETURN_IF_FAIL(sp_data_model_manager->RegisterModelForTypeSignature(
249         signature.Get(), sp_local_data_model_.Get()));
250     registered_types_.push_back({signature.Get(), sp_local_data_model_.Get()});
251   }
252 
253   // Add the 'Value' property to the parent model.
254   auto local_value_property{WRL::Make<V8LocalValueProperty>()};
255   WRL::ComPtr<IModelObject> sp_local_value_property_model;
256   RETURN_IF_FAIL(CreateProperty(sp_data_model_manager.Get(),
257                                 local_value_property.Get(),
258                                 &sp_local_value_property_model));
259   RETURN_IF_FAIL(sp_local_data_model_->SetKey(
260       L"Value", sp_local_value_property_model.Get(), nullptr));
261 
262   // Register all function aliases.
263   std::vector<std::pair<const wchar_t*, WRL::ComPtr<IModelMethod>>> functions =
264       {{pcur_isolate, WRL::Make<CurrIsolateAlias>()},
265        {pjs_stack, WRL::Make<JSStackAlias>()},
266        {pv8_object, WRL::Make<InspectV8ObjectMethod>()}};
267   for (const auto& function : functions) {
268     WRL::ComPtr<IModelObject> method;
269     RETURN_IF_FAIL(CreateMethod(sp_data_model_manager.Get(),
270                                 function.second.Get(), &method));
271     RETURN_IF_FAIL(sp_debug_host_extensibility->CreateFunctionAlias(
272         function.first, method.Get()));
273   }
274 
275   // Register a handler for supplying stack frame locals. It has to override the
276   // getter functions for "LocalVariables" and "Parameters".
277   WRL::ComPtr<IModelObject> stack_frame;
278   RETURN_IF_FAIL(sp_data_model_manager->AcquireNamedModel(
279       L"Debugger.Models.StackFrame", &stack_frame));
280   RETURN_IF_FAIL(OverrideLocalsGetter(stack_frame.Get(), L"LocalVariables",
281                                       /*is_parameters=*/false));
282   RETURN_IF_FAIL(OverrideLocalsGetter(stack_frame.Get(), L"Parameters",
283                                       /*is_parameters=*/true));
284 
285   // Add node_id property for v8::internal::compiler::Node.
286   RETURN_IF_FAIL(
287       RegisterAndAddPropertyForClass<V8InternalCompilerNodeIdProperty>(
288           L"v8::internal::compiler::Node", L"node_id",
289           sp_compiler_node_data_model_));
290 
291   // Add bitset_name property for v8::internal::compiler::Type.
292   RETURN_IF_FAIL(
293       RegisterAndAddPropertyForClass<V8InternalCompilerBitsetNameProperty>(
294           L"v8::internal::compiler::Type", L"bitset_name",
295           sp_compiler_type_data_model_));
296 
297   return S_OK;
298 }
299 
300 template <class PropertyClass>
RegisterAndAddPropertyForClass(const wchar_t * class_name,const wchar_t * property_name,WRL::ComPtr<IModelObject> sp_data_model)301 HRESULT Extension::RegisterAndAddPropertyForClass(
302     const wchar_t* class_name, const wchar_t* property_name,
303     WRL::ComPtr<IModelObject> sp_data_model) {
304   // Create an instance of the DataModel parent class.
305   auto instance_data_model{WRL::Make<V8LocalDataModel>()};
306   RETURN_IF_FAIL(sp_data_model_manager->CreateDataModelObject(
307       instance_data_model.Get(), &sp_data_model));
308 
309   // Register that parent model.
310   WRL::ComPtr<IDebugHostTypeSignature> class_signature;
311   RETURN_IF_FAIL(sp_debug_host_symbols->CreateTypeSignature(class_name, nullptr,
312                                                             &class_signature));
313   RETURN_IF_FAIL(sp_data_model_manager->RegisterModelForTypeSignature(
314       class_signature.Get(), sp_data_model.Get()));
315   registered_types_.push_back({class_signature.Get(), sp_data_model.Get()});
316 
317   // Add the property to the parent model.
318   auto property{WRL::Make<PropertyClass>()};
319   WRL::ComPtr<IModelObject> sp_property_model;
320   RETURN_IF_FAIL(CreateProperty(sp_data_model_manager.Get(), property.Get(),
321                                 &sp_property_model));
322   RETURN_IF_FAIL(
323       sp_data_model->SetKey(property_name, sp_property_model.Get(), nullptr));
324 
325   return S_OK;
326 }
327 
OverrideLocalsGetter(IModelObject * stack_frame,const wchar_t * key_name,bool is_parameters)328 HRESULT Extension::OverrideLocalsGetter(IModelObject* stack_frame,
329                                         const wchar_t* key_name,
330                                         bool is_parameters) {
331   WRL::ComPtr<IModelObject> original_boxed_getter;
332   WRL::ComPtr<IKeyStore> original_getter_metadata;
333   RETURN_IF_FAIL(stack_frame->GetKey(key_name, &original_boxed_getter,
334                                      &original_getter_metadata));
335   WRL::ComPtr<IModelPropertyAccessor> original_getter;
336   RETURN_IF_FAIL(UnboxProperty(original_boxed_getter.Get(), &original_getter));
337   auto new_getter{WRL::Make<V8LocalVariables>(original_getter, is_parameters)};
338   WRL::ComPtr<IModelObject> new_boxed_getter;
339   RETURN_IF_FAIL(CreateProperty(sp_data_model_manager.Get(), new_getter.Get(),
340                                 &new_boxed_getter));
341   RETURN_IF_FAIL(stack_frame->SetKey(key_name, new_boxed_getter.Get(),
342                                      original_getter_metadata.Get()));
343   overridden_properties_.push_back(
344       {stack_frame, reinterpret_cast<const char16_t*>(key_name),
345        original_boxed_getter.Get(), original_getter_metadata.Get()});
346   return S_OK;
347 }
348 
349 Extension::PropertyOverride::PropertyOverride() = default;
PropertyOverride(IModelObject * parent,std::u16string key_name,IModelObject * original_value,IKeyStore * original_metadata)350 Extension::PropertyOverride::PropertyOverride(IModelObject* parent,
351                                               std::u16string key_name,
352                                               IModelObject* original_value,
353                                               IKeyStore* original_metadata)
354     : parent(parent),
355       key_name(std::move(key_name)),
356       original_value(original_value),
357       original_metadata(original_metadata) {}
358 Extension::PropertyOverride::~PropertyOverride() = default;
359 Extension::PropertyOverride::PropertyOverride(const PropertyOverride&) =
360     default;
361 Extension::PropertyOverride& Extension::PropertyOverride::operator=(
362     const PropertyOverride&) = default;
363 
364 Extension::RegistrationType::RegistrationType() = default;
RegistrationType(IDebugHostTypeSignature * sp_signature,IModelObject * sp_data_model)365 Extension::RegistrationType::RegistrationType(
366     IDebugHostTypeSignature* sp_signature, IModelObject* sp_data_model)
367     : sp_signature(sp_signature), sp_data_model(sp_data_model) {}
368 Extension::RegistrationType::~RegistrationType() = default;
369 Extension::RegistrationType::RegistrationType(const RegistrationType&) =
370     default;
371 Extension::RegistrationType& Extension::RegistrationType::operator=(
372     const RegistrationType&) = default;
373 
~Extension()374 Extension::~Extension() {
375   sp_debug_host_extensibility->DestroyFunctionAlias(pcur_isolate);
376   sp_debug_host_extensibility->DestroyFunctionAlias(pjs_stack);
377   sp_debug_host_extensibility->DestroyFunctionAlias(pv8_object);
378 
379   for (const auto& registered : registered_types_) {
380     sp_data_model_manager->UnregisterModelForTypeSignature(
381         registered.sp_data_model.Get(), registered.sp_signature.Get());
382   }
383 
384   for (const auto& override : overridden_properties_) {
385     override.parent->SetKey(
386         reinterpret_cast<const wchar_t*>(override.key_name.c_str()),
387         override.original_value.Get(), override.original_metadata.Get());
388   }
389 }
390