Commit a23adbbc authored by Benedikt Meurer's avatar Benedikt Meurer Committed by Commit Bot

[inspector][wasm] Improve Scope view and instance preview.

This adds the following internal properties to `WasmInstanceObject`
values in DevTools:

 - `[[Module]]` pointing to the `WasmModuleObject`, allowing the
   developer to find the module to an instance no matter where in
   DevTools front-end the instance is inspected.
 - `[[Functions]]`, `[[Globals]]`, `[[Memories]]`, and `[[Tables]]`
   are shown (when they aren't empty), allowing developers to inspect
   the entities within an instance no matter where in DevTools front-end
   it's inspected.

This also updates the _Module_ scope for Wasm frames to show the entity
containers (`functions`, `globals`, `memories` and `tables`) in addition
to the `instance` and `module` to make it easier accessible (fewer
clicks to get there), but also to align it better with the _Add property
path to Watch_ and _Copy property path_ features (since exactly the same
names are exposed via Debug Evaluate on Wasm frames).

```
> Stack
> Locals
v Module
  > module
  > instance
  > functions
  > globals
  > memories
  > tables
```

Drive-by-fix: Move GetWasmModuleObjectInternalProperties() logic into
debug-wasm-support.cc

Screenshot: https://imgur.com/ksEHG2I.png
Doc: http://bit.ly/devtools-wasm-entities
Fixed: chromium:1165294
Bug: chromium:1071432, chromium:1164241, chromium:1165304
Change-Id: Ia88fb2705287c79988ff2b432e4a33ac34e098f5
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2622912Reviewed-by: 's avatarPhilip Pfaffe <pfaffe@chromium.org>
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Auto-Submit: Benedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#72042}
parent be5738a8
......@@ -661,42 +661,6 @@ class ContextProxy {
}
};
Handle<JSObject> GetModuleScopeObject(Handle<WasmInstanceObject> instance) {
Isolate* isolate = instance->GetIsolate();
Handle<JSObject> module_scope_object =
isolate->factory()->NewJSObjectWithNullProto();
Handle<String> instance_name =
isolate->factory()->InternalizeString(StaticCharVector("instance"));
JSObject::AddProperty(isolate, module_scope_object, instance_name, instance,
NONE);
Handle<WasmModuleObject> module_object(instance->module_object(), isolate);
Handle<String> module_name =
isolate->factory()->InternalizeString(StaticCharVector("module"));
JSObject::AddProperty(isolate, module_scope_object, module_name,
module_object, NONE);
uint32_t memory_count = MemoriesProxy::Count(isolate, instance);
for (uint32_t memory_index = 0; memory_index < memory_count; ++memory_index) {
auto memory_name = MemoriesProxy::GetName(isolate, instance, memory_index);
auto memory_value = MemoriesProxy::Get(isolate, instance, memory_index);
JSObject::AddProperty(isolate, module_scope_object, memory_name,
memory_value, NONE);
}
if (GlobalsProxy::Count(isolate, instance) != 0) {
Handle<JSObject> globals_obj =
GetOrCreateInstanceProxy<GlobalsProxy>(isolate, instance);
Handle<String> globals_name =
isolate->factory()->InternalizeString(StaticCharVector("globals"));
JSObject::AddProperty(isolate, module_scope_object, globals_name,
globals_obj, NONE);
}
return module_scope_object;
}
class DebugWasmScopeIterator final : public debug::ScopeIterator {
public:
explicit DebugWasmScopeIterator(WasmFrame* frame)
......@@ -732,23 +696,48 @@ class DebugWasmScopeIterator final : public debug::ScopeIterator {
ScopeType GetType() override { return type_; }
v8::Local<v8::Object> GetObject() override {
DCHECK(!Done());
Isolate* isolate = frame_->isolate();
switch (type_) {
case debug::ScopeIterator::ScopeTypeModule: {
Handle<WasmInstanceObject> instance =
FrameSummary::GetTop(frame_).AsWasm().wasm_instance();
return Utils::ToLocal(GetModuleScopeObject(instance));
Handle<WasmInstanceObject> instance(frame_->wasm_instance(), isolate);
Handle<JSObject> object =
isolate->factory()->NewJSObjectWithNullProto();
JSObject::AddProperty(isolate, object, "instance", instance, FROZEN);
Handle<JSObject> module_object(instance->module_object(), isolate);
JSObject::AddProperty(isolate, object, "module", module_object, FROZEN);
if (FunctionsProxy::Count(isolate, instance) != 0) {
JSObject::AddProperty(
isolate, object, "functions",
GetOrCreateInstanceProxy<FunctionsProxy>(isolate, instance),
FROZEN);
}
if (GlobalsProxy::Count(isolate, instance) != 0) {
JSObject::AddProperty(
isolate, object, "globals",
GetOrCreateInstanceProxy<GlobalsProxy>(isolate, instance),
FROZEN);
}
if (MemoriesProxy::Count(isolate, instance) != 0) {
JSObject::AddProperty(
isolate, object, "memories",
GetOrCreateInstanceProxy<MemoriesProxy>(isolate, instance),
FROZEN);
}
if (TablesProxy::Count(isolate, instance) != 0) {
JSObject::AddProperty(
isolate, object, "tables",
GetOrCreateInstanceProxy<TablesProxy>(isolate, instance), FROZEN);
}
return Utils::ToLocal(object);
}
case debug::ScopeIterator::ScopeTypeLocal: {
DCHECK(frame_->is_inspectable());
return Utils::ToLocal(LocalsProxy::Create(frame_));
}
case debug::ScopeIterator::ScopeTypeWasmExpressionStack: {
DCHECK(frame_->is_inspectable());
return Utils::ToLocal(StackProxy::Create(frame_));
}
default:
return {};
UNREACHABLE();
}
}
v8::Local<v8::Value> GetFunctionDebugName() override {
......@@ -783,5 +772,79 @@ std::unique_ptr<debug::ScopeIterator> GetWasmScopeIterator(WasmFrame* frame) {
return std::make_unique<DebugWasmScopeIterator>(frame);
}
Handle<JSArray> GetWasmInstanceObjectInternalProperties(
Handle<WasmInstanceObject> instance) {
Isolate* isolate = instance->GetIsolate();
Handle<FixedArray> result = isolate->factory()->NewFixedArray(2 * 5);
int length = 0;
Handle<String> module_str =
isolate->factory()->NewStringFromAsciiChecked("[[Module]]");
Handle<Object> module_obj = handle(instance->module_object(), isolate);
result->set(length++, *module_str);
result->set(length++, *module_obj);
if (FunctionsProxy::Count(isolate, instance) != 0) {
Handle<String> functions_str =
isolate->factory()->NewStringFromAsciiChecked("[[Functions]]");
Handle<Object> functions_obj =
GetOrCreateInstanceProxy<FunctionsProxy>(isolate, instance);
result->set(length++, *functions_str);
result->set(length++, *functions_obj);
}
if (GlobalsProxy::Count(isolate, instance) != 0) {
Handle<String> globals_str =
isolate->factory()->NewStringFromAsciiChecked("[[Globals]]");
Handle<Object> globals_obj =
GetOrCreateInstanceProxy<GlobalsProxy>(isolate, instance);
result->set(length++, *globals_str);
result->set(length++, *globals_obj);
}
if (MemoriesProxy::Count(isolate, instance) != 0) {
Handle<String> memories_str =
isolate->factory()->NewStringFromAsciiChecked("[[Memories]]");
Handle<Object> memories_obj =
GetOrCreateInstanceProxy<MemoriesProxy>(isolate, instance);
result->set(length++, *memories_str);
result->set(length++, *memories_obj);
}
if (TablesProxy::Count(isolate, instance) != 0) {
Handle<String> tables_str =
isolate->factory()->NewStringFromAsciiChecked("[[Tables]]");
Handle<Object> tables_obj =
GetOrCreateInstanceProxy<TablesProxy>(isolate, instance);
result->set(length++, *tables_str);
result->set(length++, *tables_obj);
}
return isolate->factory()->NewJSArrayWithElements(result, PACKED_ELEMENTS,
length);
}
Handle<JSArray> GetWasmModuleObjectInternalProperties(
Handle<WasmModuleObject> module_object) {
Isolate* isolate = module_object->GetIsolate();
Handle<FixedArray> result = isolate->factory()->NewFixedArray(2 * 2);
int length = 0;
Handle<String> exports_str =
isolate->factory()->NewStringFromStaticChars("[[Exports]]");
Handle<JSArray> exports_obj = wasm::GetExports(isolate, module_object);
result->set(length++, *exports_str);
result->set(length++, *exports_obj);
Handle<String> imports_str =
isolate->factory()->NewStringFromStaticChars("[[Imports]]");
Handle<JSArray> imports_obj = wasm::GetImports(isolate, module_object);
result->set(length++, *imports_str);
result->set(length++, *imports_obj);
return isolate->factory()->NewJSArrayWithElements(result, PACKED_ELEMENTS,
length);
}
} // namespace internal
} // namespace v8
......@@ -16,13 +16,21 @@ namespace internal {
template <typename T>
class Handle;
class JSArray;
class JSObject;
class WasmFrame;
class WasmInstanceObject;
class WasmModuleObject;
Handle<JSObject> GetWasmDebugProxy(WasmFrame* frame);
std::unique_ptr<debug::ScopeIterator> GetWasmScopeIterator(WasmFrame* frame);
Handle<JSArray> GetWasmInstanceObjectInternalProperties(
Handle<WasmInstanceObject> instance);
Handle<JSArray> GetWasmModuleObjectInternalProperties(
Handle<WasmModuleObject> module_object);
} // namespace internal
} // namespace v8
......
......@@ -10,6 +10,7 @@
#include "src/debug/debug-evaluate.h"
#include "src/debug/debug-frames.h"
#include "src/debug/debug-scopes.h"
#include "src/debug/debug-wasm-support.h"
#include "src/debug/debug.h"
#include "src/debug/liveedit.h"
#include "src/execution/arguments-inl.h"
......@@ -365,20 +366,12 @@ MaybeHandle<JSArray> Runtime::GetInternalProperties(Isolate* isolate,
result->set(index++, *buffer_id);
return factory->NewJSArrayWithElements(result, PACKED_ELEMENTS, index);
} else if (object->IsWasmInstanceObject()) {
return GetWasmInstanceObjectInternalProperties(
Handle<WasmInstanceObject>::cast(object));
} else if (object->IsWasmModuleObject()) {
auto module_object = Handle<WasmModuleObject>::cast(object);
Handle<FixedArray> result = factory->NewFixedArray(2 * 2);
Handle<String> exports_str =
factory->NewStringFromStaticChars("[[Exports]]");
Handle<JSArray> exports_obj = wasm::GetExports(isolate, module_object);
result->set(0, *exports_str);
result->set(1, *exports_obj);
Handle<String> imports_str =
factory->NewStringFromStaticChars("[[Imports]]");
Handle<JSArray> imports_obj = wasm::GetImports(isolate, module_object);
result->set(2, *imports_str);
result->set(3, *imports_obj);
return factory->NewJSArrayWithElements(result, PACKED_ELEMENTS);
return GetWasmModuleObjectInternalProperties(
Handle<WasmModuleObject>::cast(object));
}
return factory->NewJSArray(0);
}
......
......@@ -50,24 +50,6 @@ function createInstance(moduleBytes) {
new WebAssembly.Instance(module, {module_name: {imported_mem: memory}});
}
async function logMemoryName(msg, Protocol) {
let callFrames = msg.params.callFrames;
InspectorTest.log('Paused in debugger.');
let scopeChain = callFrames[0].scopeChain;
for (let scope of scopeChain) {
if (scope.type != 'module') continue;
let moduleObjectProps = (await Protocol.Runtime.getProperties({
'objectId': scope.object.objectId
})).result.result;
for (let prop of moduleObjectProps) {
if (prop.name === 'instance' || prop.name === 'module') continue;
InspectorTest.log(`name: ${prop.name}`);
}
}
}
async function check(moduleBytes) {
Protocol.Runtime.evaluate({
expression: `
......@@ -91,8 +73,17 @@ async function check(moduleBytes) {
InspectorTest.log('Running main.');
Protocol.Runtime.evaluate({expression: 'instance.exports.main()'});
let msg = await Protocol.Debugger.oncePaused();
await logMemoryName(msg, Protocol);
const {params: {callFrames: [{callFrameId}]}} =
await Protocol.Debugger.oncePaused();
InspectorTest.log('Paused in debugger.');
const {result: {result: {objectId}}} =
await Protocol.Debugger.evaluateOnCallFrame(
{callFrameId, expression: `memories`});
const {result: {result: properties}} =
await Protocol.Runtime.getProperties({objectId});
for (const {name} of properties) {
InspectorTest.log(`name: ${name}`);
}
await Protocol.Debugger.resume();
InspectorTest.log('Finished.');
......
......@@ -14,6 +14,7 @@ at wasm_A (0:38):
- scope (module):
instance: exports: "main" (Function)
module: Module
functions: "$wasm_A" (Function), "$wasm_B" (Function)
at wasm_B (0:56):
- scope (wasm-expression-stack):
- scope (local):
......@@ -21,6 +22,7 @@ at wasm_B (0:56):
- scope (module):
instance: exports: "main" (Function)
module: Module
functions: "$wasm_A" (Function), "$wasm_B" (Function)
at (anonymous) (0:17):
-- skipped
Setting breakpoint at offset 39 on script v8://test/runWasm
......@@ -41,6 +43,7 @@ at wasm_A (0:39):
- scope (module):
instance: exports: "main" (Function)
module: Module
functions: "$wasm_A" (Function), "$wasm_B" (Function)
at wasm_B (0:56):
- scope (wasm-expression-stack):
- scope (local):
......@@ -48,6 +51,7 @@ at wasm_B (0:56):
- scope (module):
instance: exports: "main" (Function)
module: Module
functions: "$wasm_A" (Function), "$wasm_B" (Function)
at (anonymous) (0:17):
-- skipped
Paused:
......@@ -60,6 +64,7 @@ at wasm_B (0:45):
- scope (module):
instance: exports: "main" (Function)
module: Module
functions: "$wasm_A" (Function), "$wasm_B" (Function)
at (anonymous) (0:17):
-- skipped
Paused:
......@@ -73,6 +78,7 @@ at wasm_B (0:47):
- scope (module):
instance: exports: "main" (Function)
module: Module
functions: "$wasm_A" (Function), "$wasm_B" (Function)
at (anonymous) (0:17):
-- skipped
Paused:
......@@ -85,6 +91,7 @@ at wasm_B (0:49):
- scope (module):
instance: exports: "main" (Function)
module: Module
functions: "$wasm_A" (Function), "$wasm_B" (Function)
at (anonymous) (0:17):
-- skipped
Paused:
......@@ -98,6 +105,7 @@ at wasm_B (0:51):
- scope (module):
instance: exports: "main" (Function)
module: Module
functions: "$wasm_A" (Function), "$wasm_B" (Function)
at (anonymous) (0:17):
-- skipped
Paused:
......@@ -112,6 +120,7 @@ at wasm_B (0:53):
- scope (module):
instance: exports: "main" (Function)
module: Module
functions: "$wasm_A" (Function), "$wasm_B" (Function)
at (anonymous) (0:17):
-- skipped
Paused:
......@@ -125,6 +134,7 @@ at wasm_B (0:54):
- scope (module):
instance: exports: "main" (Function)
module: Module
functions: "$wasm_A" (Function), "$wasm_B" (Function)
at (anonymous) (0:17):
-- skipped
Paused:
......@@ -136,6 +146,7 @@ at wasm_A (0:38):
- scope (module):
instance: exports: "main" (Function)
module: Module
functions: "$wasm_A" (Function), "$wasm_B" (Function)
at wasm_B (0:56):
- scope (wasm-expression-stack):
- scope (local):
......@@ -143,6 +154,7 @@ at wasm_B (0:56):
- scope (module):
instance: exports: "main" (Function)
module: Module
functions: "$wasm_A" (Function), "$wasm_B" (Function)
at (anonymous) (0:17):
-- skipped
Paused:
......@@ -154,6 +166,7 @@ at wasm_A (0:39):
- scope (module):
instance: exports: "main" (Function)
module: Module
functions: "$wasm_A" (Function), "$wasm_B" (Function)
at wasm_B (0:56):
- scope (wasm-expression-stack):
- scope (local):
......@@ -161,6 +174,7 @@ at wasm_B (0:56):
- scope (module):
instance: exports: "main" (Function)
module: Module
functions: "$wasm_A" (Function), "$wasm_B" (Function)
at (anonymous) (0:17):
-- skipped
Paused:
......@@ -173,6 +187,7 @@ at wasm_B (0:45):
- scope (module):
instance: exports: "main" (Function)
module: Module
functions: "$wasm_A" (Function), "$wasm_B" (Function)
at (anonymous) (0:17):
-- skipped
Paused:
......@@ -186,6 +201,7 @@ at wasm_B (0:47):
- scope (module):
instance: exports: "main" (Function)
module: Module
functions: "$wasm_A" (Function), "$wasm_B" (Function)
at (anonymous) (0:17):
-- skipped
Paused:
......@@ -198,6 +214,7 @@ at wasm_B (0:49):
- scope (module):
instance: exports: "main" (Function)
module: Module
functions: "$wasm_A" (Function), "$wasm_B" (Function)
at (anonymous) (0:17):
-- skipped
Paused:
......@@ -211,6 +228,7 @@ at wasm_B (0:51):
- scope (module):
instance: exports: "main" (Function)
module: Module
functions: "$wasm_A" (Function), "$wasm_B" (Function)
at (anonymous) (0:17):
-- skipped
Paused:
......@@ -225,6 +243,7 @@ at wasm_B (0:53):
- scope (module):
instance: exports: "main" (Function)
module: Module
functions: "$wasm_A" (Function), "$wasm_B" (Function)
at (anonymous) (0:17):
-- skipped
Paused:
......@@ -238,6 +257,7 @@ at wasm_B (0:54):
- scope (module):
instance: exports: "main" (Function)
module: Module
functions: "$wasm_A" (Function), "$wasm_B" (Function)
at (anonymous) (0:17):
-- skipped
Paused:
......@@ -249,6 +269,7 @@ at wasm_A (0:38):
- scope (module):
instance: exports: "main" (Function)
module: Module
functions: "$wasm_A" (Function), "$wasm_B" (Function)
at wasm_B (0:56):
- scope (wasm-expression-stack):
- scope (local):
......@@ -256,6 +277,7 @@ at wasm_B (0:56):
- scope (module):
instance: exports: "main" (Function)
module: Module
functions: "$wasm_A" (Function), "$wasm_B" (Function)
at (anonymous) (0:17):
-- skipped
Paused:
......@@ -267,6 +289,7 @@ at wasm_A (0:39):
- scope (module):
instance: exports: "main" (Function)
module: Module
functions: "$wasm_A" (Function), "$wasm_B" (Function)
at wasm_B (0:56):
- scope (wasm-expression-stack):
- scope (local):
......@@ -274,6 +297,7 @@ at wasm_B (0:56):
- scope (module):
instance: exports: "main" (Function)
module: Module
functions: "$wasm_A" (Function), "$wasm_B" (Function)
at (anonymous) (0:17):
-- skipped
Paused:
......@@ -286,6 +310,7 @@ at wasm_B (0:45):
- scope (module):
instance: exports: "main" (Function)
module: Module
functions: "$wasm_A" (Function), "$wasm_B" (Function)
at (anonymous) (0:17):
-- skipped
Paused:
......@@ -299,6 +324,7 @@ at wasm_B (0:47):
- scope (module):
instance: exports: "main" (Function)
module: Module
functions: "$wasm_A" (Function), "$wasm_B" (Function)
at (anonymous) (0:17):
-- skipped
Paused:
......@@ -311,6 +337,7 @@ at wasm_B (0:49):
- scope (module):
instance: exports: "main" (Function)
module: Module
functions: "$wasm_A" (Function), "$wasm_B" (Function)
at (anonymous) (0:17):
-- skipped
Paused:
......@@ -324,6 +351,7 @@ at wasm_B (0:51):
- scope (module):
instance: exports: "main" (Function)
module: Module
functions: "$wasm_A" (Function), "$wasm_B" (Function)
at (anonymous) (0:17):
-- skipped
Paused:
......@@ -338,6 +366,7 @@ at wasm_B (0:53):
- scope (module):
instance: exports: "main" (Function)
module: Module
functions: "$wasm_A" (Function), "$wasm_B" (Function)
at (anonymous) (0:17):
-- skipped
Paused:
......@@ -351,6 +380,7 @@ at wasm_B (0:54):
- scope (module):
instance: exports: "main" (Function)
module: Module
functions: "$wasm_A" (Function), "$wasm_B" (Function)
at (anonymous) (0:17):
-- skipped
Paused:
......@@ -362,6 +392,7 @@ at wasm_A (0:38):
- scope (module):
instance: exports: "main" (Function)
module: Module
functions: "$wasm_A" (Function), "$wasm_B" (Function)
at wasm_B (0:56):
- scope (wasm-expression-stack):
- scope (local):
......@@ -369,6 +400,7 @@ at wasm_B (0:56):
- scope (module):
instance: exports: "main" (Function)
module: Module
functions: "$wasm_A" (Function), "$wasm_B" (Function)
at (anonymous) (0:17):
-- skipped
Paused:
......@@ -380,6 +412,7 @@ at wasm_A (0:39):
- scope (module):
instance: exports: "main" (Function)
module: Module
functions: "$wasm_A" (Function), "$wasm_B" (Function)
at wasm_B (0:56):
- scope (wasm-expression-stack):
- scope (local):
......@@ -387,6 +420,7 @@ at wasm_B (0:56):
- scope (module):
instance: exports: "main" (Function)
module: Module
functions: "$wasm_A" (Function), "$wasm_B" (Function)
at (anonymous) (0:17):
-- skipped
Paused:
......@@ -399,6 +433,7 @@ at wasm_B (0:45):
- scope (module):
instance: exports: "main" (Function)
module: Module
functions: "$wasm_A" (Function), "$wasm_B" (Function)
at (anonymous) (0:17):
-- skipped
Paused:
......@@ -412,6 +447,7 @@ at wasm_B (0:47):
- scope (module):
instance: exports: "main" (Function)
module: Module
functions: "$wasm_A" (Function), "$wasm_B" (Function)
at (anonymous) (0:17):
-- skipped
Paused:
......@@ -424,6 +460,7 @@ at wasm_B (0:61):
- scope (module):
instance: exports: "main" (Function)
module: Module
functions: "$wasm_A" (Function), "$wasm_B" (Function)
at (anonymous) (0:17):
-- skipped
exports.main returned!
......@@ -47,7 +47,7 @@ function printIfFailure(message) {
}
async function getScopeValues(name, value) {
if (value.type == 'object') {
if (value.type === 'object') {
if (value.subtype === 'typedarray' || value.subtype == 'webassemblymemory') return value.description;
if (name === 'instance') return dumpInstanceProperties(value);
if (name === 'module') return value.description;
......@@ -55,6 +55,9 @@ async function getScopeValues(name, value) {
let msg = await Protocol.Runtime.getProperties({objectId: value.objectId});
printIfFailure(msg);
const printProperty = function({name, value}) {
if ('className' in value) {
return `"${name}" (${value.className})`;
}
return `"${name}": ${WasmInspectorTest.getWasmValue(value)} (${value.subtype ?? value.type})`;
}
return msg.result.result.map(printProperty).join(', ');
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment