[wasm] Display wasm globals in the inspector
They are added under the global scope object, as follows: { "memory": ..., "globals": { "global#0": ..., "global#1": ..., "global#2": ..., } } We currently don't have any way to name globals in the wasm binary format, but it is possible to extend the name section with these names in the future. Bug: v8:6846 Change-Id: I79fa4ed3d83964bc8e26d66516605d41e92b3d03 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1601829 Commit-Queue: Ben Smith <binji@chromium.org> Reviewed-by: Clemens Hammacher <clemensh@chromium.org> Cr-Commit-Position: refs/heads/master@{#61425}
This commit is contained in:
parent
402570546f
commit
baf84940f4
@ -60,6 +60,8 @@ Handle<Object> WasmValueToValueObject(Isolate* isolate, WasmValue value) {
|
||||
return isolate->factory()->NewNumber(value.to<float>());
|
||||
case kWasmF64:
|
||||
return isolate->factory()->NewNumber(value.to<double>());
|
||||
case kWasmAnyRef:
|
||||
return value.to_anyref();
|
||||
default:
|
||||
UNIMPLEMENTED();
|
||||
return isolate->factory()->undefined_value();
|
||||
@ -359,7 +361,6 @@ class InterpreterHandle {
|
||||
Isolate* isolate = isolate_;
|
||||
Handle<WasmInstanceObject> instance(debug_info->wasm_instance(), isolate);
|
||||
|
||||
// TODO(clemensh): Add globals to the global scope.
|
||||
Handle<JSObject> global_scope_object =
|
||||
isolate_->factory()->NewJSObjectWithNullProto();
|
||||
if (instance->has_memory_object()) {
|
||||
@ -373,6 +374,32 @@ class InterpreterHandle {
|
||||
uint8_array, NONE)
|
||||
.Assert();
|
||||
}
|
||||
|
||||
DCHECK_EQ(1, interpreter()->GetThreadCount());
|
||||
WasmInterpreter::Thread* thread = interpreter()->GetThread(0);
|
||||
|
||||
uint32_t global_count = thread->GetGlobalCount();
|
||||
if (global_count > 0) {
|
||||
Handle<JSObject> globals_obj =
|
||||
isolate_->factory()->NewJSObjectWithNullProto();
|
||||
Handle<String> globals_name =
|
||||
isolate_->factory()->InternalizeOneByteString(
|
||||
StaticCharVector("globals"));
|
||||
JSObject::SetOwnPropertyIgnoreAttributes(global_scope_object,
|
||||
globals_name, globals_obj, NONE)
|
||||
.Assert();
|
||||
|
||||
for (uint32_t i = 0; i < global_count; ++i) {
|
||||
const char* label = "global#%d";
|
||||
Handle<String> name = PrintFToOneByteString<true>(isolate_, label, i);
|
||||
WasmValue value = thread->GetGlobalValue(i);
|
||||
Handle<Object> value_obj = WasmValueToValueObject(isolate_, value);
|
||||
JSObject::SetOwnPropertyIgnoreAttributes(globals_obj, name, value_obj,
|
||||
NONE)
|
||||
.Assert();
|
||||
}
|
||||
}
|
||||
|
||||
return global_scope_object;
|
||||
}
|
||||
|
||||
|
@ -1262,6 +1262,37 @@ class ThreadImpl {
|
||||
return WasmInterpreter::Thread::HANDLED;
|
||||
}
|
||||
|
||||
uint32_t GetGlobalCount() {
|
||||
return static_cast<uint32_t>(module()->globals.size());
|
||||
}
|
||||
|
||||
WasmValue GetGlobalValue(uint32_t index) {
|
||||
const WasmGlobal* global = &module()->globals[index];
|
||||
switch (global->type) {
|
||||
#define CASE_TYPE(wasm, ctype) \
|
||||
case kWasm##wasm: { \
|
||||
byte* ptr = GetGlobalPtr(global); \
|
||||
return WasmValue( \
|
||||
ReadLittleEndianValue<ctype>(reinterpret_cast<Address>(ptr))); \
|
||||
break; \
|
||||
}
|
||||
WASM_CTYPES(CASE_TYPE)
|
||||
#undef CASE_TYPE
|
||||
case kWasmAnyRef:
|
||||
case kWasmAnyFunc:
|
||||
case kWasmExceptRef: {
|
||||
HandleScope handle_scope(isolate_); // Avoid leaking handles.
|
||||
Handle<FixedArray> global_buffer; // The buffer of the global.
|
||||
uint32_t global_index = 0; // The index into the buffer.
|
||||
GetGlobalBufferAndIndex(global, &global_buffer, &global_index);
|
||||
Handle<Object> value(global_buffer->get(global_index), isolate_);
|
||||
return WasmValue(handle_scope.CloseAndEscape(value));
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// Handle a thrown exception. Returns whether the exception was handled inside
|
||||
// the current activation. Unwinds the interpreted stack accordingly.
|
||||
@ -3055,31 +3086,8 @@ class ThreadImpl {
|
||||
case kExprGetGlobal: {
|
||||
GlobalIndexImmediate<Decoder::kNoValidate> imm(&decoder,
|
||||
code->at(pc));
|
||||
const WasmGlobal* global = &module()->globals[imm.index];
|
||||
switch (global->type) {
|
||||
#define CASE_TYPE(wasm, ctype) \
|
||||
case kWasm##wasm: { \
|
||||
byte* ptr = GetGlobalPtr(global); \
|
||||
Push(WasmValue( \
|
||||
ReadLittleEndianValue<ctype>(reinterpret_cast<Address>(ptr)))); \
|
||||
break; \
|
||||
}
|
||||
WASM_CTYPES(CASE_TYPE)
|
||||
#undef CASE_TYPE
|
||||
case kWasmAnyRef:
|
||||
case kWasmAnyFunc:
|
||||
case kWasmExceptRef: {
|
||||
HandleScope handle_scope(isolate_); // Avoid leaking handles.
|
||||
Handle<FixedArray> global_buffer; // The buffer of the global.
|
||||
uint32_t global_index = 0; // The index into the buffer.
|
||||
GetGlobalBufferAndIndex(global, &global_buffer, &global_index);
|
||||
Handle<Object> value(global_buffer->get(global_index), isolate_);
|
||||
Push(WasmValue(value));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
HandleScope handle_scope(isolate_);
|
||||
Push(GetGlobalValue(imm.index));
|
||||
len = 1 + imm.length;
|
||||
break;
|
||||
}
|
||||
@ -3821,6 +3829,12 @@ WasmValue WasmInterpreter::Thread::GetReturnValue(int index) {
|
||||
TrapReason WasmInterpreter::Thread::GetTrapReason() {
|
||||
return ToImpl(this)->GetTrapReason();
|
||||
}
|
||||
uint32_t WasmInterpreter::Thread::GetGlobalCount() {
|
||||
return ToImpl(this)->GetGlobalCount();
|
||||
}
|
||||
WasmValue WasmInterpreter::Thread::GetGlobalValue(uint32_t index) {
|
||||
return ToImpl(this)->GetGlobalValue(index);
|
||||
}
|
||||
bool WasmInterpreter::Thread::PossibleNondeterminism() {
|
||||
return ToImpl(this)->PossibleNondeterminism();
|
||||
}
|
||||
|
@ -138,6 +138,9 @@ class V8_EXPORT_PRIVATE WasmInterpreter {
|
||||
WasmValue GetReturnValue(int index = 0);
|
||||
TrapReason GetTrapReason();
|
||||
|
||||
uint32_t GetGlobalCount();
|
||||
WasmValue GetGlobalValue(uint32_t index);
|
||||
|
||||
// Returns true if the thread executed an instruction which may produce
|
||||
// nondeterministic results, e.g. float div, float sqrt, and float mul,
|
||||
// where the sign bit of a NaN is nondeterministic.
|
||||
|
7
test/inspector/debugger/wasm-anyref-global-expected.txt
Normal file
7
test/inspector/debugger/wasm-anyref-global-expected.txt
Normal file
@ -0,0 +1,7 @@
|
||||
Test wasm scope information with anyref globals
|
||||
Waiting for wasm script to be parsed.
|
||||
Setting breakpoint in wasm.
|
||||
Running main.
|
||||
Paused in debugger.
|
||||
globals: {"global#0": hello, world}
|
||||
Finished.
|
83
test/inspector/debugger/wasm-anyref-global.js
Normal file
83
test/inspector/debugger/wasm-anyref-global.js
Normal file
@ -0,0 +1,83 @@
|
||||
// Copyright 2019 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.
|
||||
|
||||
// Flags: --experimental-wasm-anyref
|
||||
|
||||
let {session, contextGroup, Protocol} =
|
||||
InspectorTest.start('Test wasm scope information with anyref globals');
|
||||
|
||||
(async function() {
|
||||
try {
|
||||
utils.load('test/mjsunit/wasm/wasm-module-builder.js');
|
||||
|
||||
let builder = new WasmModuleBuilder();
|
||||
builder.addImportedGlobal('m', 'global', kWasmAnyRef, false);
|
||||
builder.addFunction('func', kSig_v_v)
|
||||
.addBody([
|
||||
kExprGetGlobal, 0, //
|
||||
kExprDrop, //
|
||||
])
|
||||
.exportAs('main');
|
||||
let moduleBytes = JSON.stringify(builder.toArray());
|
||||
|
||||
function test(moduleBytes) {
|
||||
let module = new WebAssembly.Module((new Uint8Array(moduleBytes)).buffer);
|
||||
let global = 'hello, world';
|
||||
instance = new WebAssembly.Instance(module, {m: {global}});
|
||||
}
|
||||
|
||||
Protocol.Debugger.enable();
|
||||
Protocol.Runtime.evaluate({
|
||||
expression: `
|
||||
let instance;
|
||||
${test.toString()}
|
||||
test(${moduleBytes});`
|
||||
});
|
||||
|
||||
InspectorTest.log('Waiting for wasm script to be parsed.');
|
||||
let scriptId;
|
||||
while (true) {
|
||||
let msg = await Protocol.Debugger.onceScriptParsed();
|
||||
if (msg.params.url.startsWith('wasm://')) {
|
||||
scriptId = msg.params.scriptId;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
InspectorTest.log('Setting breakpoint in wasm.');
|
||||
await Protocol.Debugger.setBreakpoint(
|
||||
{location: {scriptId, lineNumber: 2}});
|
||||
|
||||
InspectorTest.log('Running main.');
|
||||
Protocol.Runtime.evaluate({expression: 'instance.exports.main()'});
|
||||
|
||||
let msg = await Protocol.Debugger.oncePaused();
|
||||
let callFrames = msg.params.callFrames;
|
||||
InspectorTest.log('Paused in debugger.');
|
||||
let scopeChain = callFrames[0].scopeChain;
|
||||
for (let scope of scopeChain) {
|
||||
if (scope.type != 'global') continue;
|
||||
|
||||
let globalObjectProps = (await Protocol.Runtime.getProperties({
|
||||
'objectId': scope.object.objectId
|
||||
})).result.result;
|
||||
|
||||
for (let prop of globalObjectProps) {
|
||||
let subProps = (await Protocol.Runtime.getProperties({
|
||||
objectId: prop.value.objectId
|
||||
})).result.result;
|
||||
let values =
|
||||
subProps.map((value) => `"${value.name}": ${value.value.value}`)
|
||||
.join(', ');
|
||||
InspectorTest.log(` ${prop.name}: {${values}}`);
|
||||
}
|
||||
}
|
||||
|
||||
InspectorTest.log('Finished.');
|
||||
} catch (exc) {
|
||||
InspectorTest.log(`Failed with exception: ${exc}.`);
|
||||
} finally {
|
||||
InspectorTest.completeTest();
|
||||
}
|
||||
})();
|
@ -17,7 +17,7 @@ Paused:
|
||||
Scope:
|
||||
at func (2:2):
|
||||
- scope (global):
|
||||
-- skipped globals
|
||||
globals: "global#0": 0 (number)
|
||||
- scope (local):
|
||||
locals: "i32Arg": 4 (number), "local#1": 0 (number), "i64_local": 0 (number), "unicode☼f64": 0 (number)
|
||||
stack:
|
||||
@ -33,7 +33,7 @@ Paused:
|
||||
Scope:
|
||||
at func (3:2):
|
||||
- scope (global):
|
||||
-- skipped globals
|
||||
globals: "global#0": 0 (number)
|
||||
- scope (local):
|
||||
locals: "i32Arg": 4 (number), "local#1": 0 (number), "i64_local": 0 (number), "unicode☼f64": 0 (number)
|
||||
stack: "0": 11 (number)
|
||||
@ -49,7 +49,7 @@ Paused:
|
||||
Scope:
|
||||
at func (4:2):
|
||||
- scope (global):
|
||||
-- skipped globals
|
||||
globals: "global#0": 0 (number)
|
||||
- scope (local):
|
||||
locals: "i32Arg": 11 (number), "local#1": 0 (number), "i64_local": 0 (number), "unicode☼f64": 0 (number)
|
||||
stack:
|
||||
@ -65,7 +65,7 @@ Paused:
|
||||
Scope:
|
||||
at func (5:2):
|
||||
- scope (global):
|
||||
-- skipped globals
|
||||
globals: "global#0": 0 (number)
|
||||
- scope (local):
|
||||
locals: "i32Arg": 11 (number), "local#1": 0 (number), "i64_local": 0 (number), "unicode☼f64": 0 (number)
|
||||
stack: "0": 47 (number)
|
||||
@ -81,7 +81,7 @@ Paused:
|
||||
Scope:
|
||||
at func (6:2):
|
||||
- scope (global):
|
||||
-- skipped globals
|
||||
globals: "global#0": 0 (number)
|
||||
- scope (local):
|
||||
locals: "i32Arg": 11 (number), "local#1": 47 (number), "i64_local": 0 (number), "unicode☼f64": 0 (number)
|
||||
stack:
|
||||
@ -97,7 +97,7 @@ Paused:
|
||||
Scope:
|
||||
at func (7:2):
|
||||
- scope (global):
|
||||
-- skipped globals
|
||||
globals: "global#0": 0 (number)
|
||||
- scope (local):
|
||||
locals: "i32Arg": 11 (number), "local#1": 47 (number), "i64_local": 0 (number), "unicode☼f64": 0 (number)
|
||||
stack: "0": 9223372036854775807 (string)
|
||||
@ -113,7 +113,7 @@ Paused:
|
||||
Scope:
|
||||
at func (8:2):
|
||||
- scope (global):
|
||||
-- skipped globals
|
||||
globals: "global#0": 0 (number)
|
||||
- scope (local):
|
||||
locals: "i32Arg": 11 (number), "local#1": 47 (number), "i64_local": 9223372036854775807 (string), "unicode☼f64": 0 (number)
|
||||
stack:
|
||||
@ -129,7 +129,7 @@ Paused:
|
||||
Scope:
|
||||
at func (9:2):
|
||||
- scope (global):
|
||||
-- skipped globals
|
||||
globals: "global#0": 0 (number)
|
||||
- scope (local):
|
||||
locals: "i32Arg": 11 (number), "local#1": 47 (number), "i64_local": 9223372036854775807 (string), "unicode☼f64": 0 (number)
|
||||
stack: "0": -9223372036854775808 (string)
|
||||
@ -145,7 +145,7 @@ Paused:
|
||||
Scope:
|
||||
at func (10:2):
|
||||
- scope (global):
|
||||
-- skipped globals
|
||||
globals: "global#0": 0 (number)
|
||||
- scope (local):
|
||||
locals: "i32Arg": 11 (number), "local#1": 47 (number), "i64_local": -9223372036854775808 (string), "unicode☼f64": 0 (number)
|
||||
stack:
|
||||
@ -161,7 +161,7 @@ Paused:
|
||||
Scope:
|
||||
at func (11:2):
|
||||
- scope (global):
|
||||
-- skipped globals
|
||||
globals: "global#0": 0 (number)
|
||||
- scope (local):
|
||||
locals: "i32Arg": 11 (number), "local#1": 47 (number), "i64_local": -9223372036854775808 (string), "unicode☼f64": 0 (number)
|
||||
stack: "0": 1 (number)
|
||||
@ -177,7 +177,7 @@ Paused:
|
||||
Scope:
|
||||
at func (12:2):
|
||||
- scope (global):
|
||||
-- skipped globals
|
||||
globals: "global#0": 0 (number)
|
||||
- scope (local):
|
||||
locals: "i32Arg": 11 (number), "local#1": 47 (number), "i64_local": -9223372036854775808 (string), "unicode☼f64": 0 (number)
|
||||
stack: "0": 1 (number)
|
||||
@ -193,7 +193,7 @@ Paused:
|
||||
Scope:
|
||||
at func (13:2):
|
||||
- scope (global):
|
||||
-- skipped globals
|
||||
globals: "global#0": 0 (number)
|
||||
- scope (local):
|
||||
locals: "i32Arg": 11 (number), "local#1": 47 (number), "i64_local": -9223372036854775808 (string), "unicode☼f64": 0 (number)
|
||||
stack: "0": 1 (number), "1": 7 (number)
|
||||
@ -209,7 +209,7 @@ Paused:
|
||||
Scope:
|
||||
at func (14:2):
|
||||
- scope (global):
|
||||
-- skipped globals
|
||||
globals: "global#0": 0 (number)
|
||||
- scope (local):
|
||||
locals: "i32Arg": 11 (number), "local#1": 47 (number), "i64_local": -9223372036854775808 (string), "unicode☼f64": 0 (number)
|
||||
stack: "0": 1 (number), "1": 7 (number)
|
||||
@ -220,12 +220,12 @@ at (anonymous) (0:17):
|
||||
Paused:
|
||||
f64.div
|
||||
#set_local 3
|
||||
end
|
||||
i32.const 15
|
||||
|
||||
Scope:
|
||||
at func (15:2):
|
||||
- scope (global):
|
||||
-- skipped globals
|
||||
globals: "global#0": 0 (number)
|
||||
- scope (local):
|
||||
locals: "i32Arg": 11 (number), "local#1": 47 (number), "i64_local": -9223372036854775808 (string), "unicode☼f64": 0 (number)
|
||||
stack: "0": 0.14285714285714285 (number)
|
||||
@ -235,13 +235,45 @@ at (anonymous) (0:17):
|
||||
|
||||
Paused:
|
||||
set_local 3
|
||||
#i32.const 15
|
||||
set_global 0
|
||||
|
||||
Scope:
|
||||
at func (16:2):
|
||||
- scope (global):
|
||||
globals: "global#0": 0 (number)
|
||||
- scope (local):
|
||||
locals: "i32Arg": 11 (number), "local#1": 47 (number), "i64_local": -9223372036854775808 (string), "unicode☼f64": 0.14285714285714285 (number)
|
||||
stack:
|
||||
at (anonymous) (0:17):
|
||||
- scope (global):
|
||||
-- skipped globals
|
||||
|
||||
Paused:
|
||||
i32.const 15
|
||||
#set_global 0
|
||||
end
|
||||
|
||||
Scope:
|
||||
at func (17:2):
|
||||
- scope (global):
|
||||
globals: "global#0": 0 (number)
|
||||
- scope (local):
|
||||
locals: "i32Arg": 11 (number), "local#1": 47 (number), "i64_local": -9223372036854775808 (string), "unicode☼f64": 0.14285714285714285 (number)
|
||||
stack: "0": 15 (number)
|
||||
at (anonymous) (0:17):
|
||||
- scope (global):
|
||||
-- skipped globals
|
||||
|
||||
Paused:
|
||||
set_global 0
|
||||
#end
|
||||
|
||||
|
||||
Scope:
|
||||
at func (16:0):
|
||||
at func (18:0):
|
||||
- scope (global):
|
||||
-- skipped globals
|
||||
globals: "global#0": 15 (number)
|
||||
- scope (local):
|
||||
locals: "i32Arg": 11 (number), "local#1": 47 (number), "i64_local": -9223372036854775808 (string), "unicode☼f64": 0.14285714285714285 (number)
|
||||
stack:
|
||||
|
@ -33,6 +33,7 @@ async function instantiateWasm() {
|
||||
utils.load('test/mjsunit/wasm/wasm-module-builder.js');
|
||||
|
||||
var builder = new WasmModuleBuilder();
|
||||
builder.addGlobal(kWasmI32, true);
|
||||
|
||||
builder.addFunction('func', kSig_v_i)
|
||||
.addLocals(
|
||||
@ -51,7 +52,10 @@ async function instantiateWasm() {
|
||||
kExprSetLocal, 2,
|
||||
// Set local 3 to 1/7.
|
||||
kExprI32Const, 1, kExprF64UConvertI32, kExprI32Const, 7,
|
||||
kExprF64UConvertI32, kExprF64Div, kExprSetLocal, 3
|
||||
kExprF64UConvertI32, kExprF64Div, kExprSetLocal, 3,
|
||||
|
||||
// Set global 0 to 15
|
||||
kExprI32Const, 15, kExprSetGlobal, 0,
|
||||
])
|
||||
.exportAs('main');
|
||||
|
||||
@ -129,13 +133,15 @@ async function dumpScopeProperties(message) {
|
||||
async function dumpScopeChainsOnPause(message) {
|
||||
InspectorTest.log(`Scope:`);
|
||||
for (var frame of message.params.callFrames) {
|
||||
var isWasmFrame = /^wasm/.test(frame.url);
|
||||
var functionName = frame.functionName || '(anonymous)';
|
||||
var lineNumber = frame.location ? frame.location.lineNumber : frame.lineNumber;
|
||||
var columnNumber = frame.location ? frame.location.columnNumber : frame.columnNumber;
|
||||
InspectorTest.log(`at ${functionName} (${lineNumber}:${columnNumber}):`);
|
||||
for (var scope of frame.scopeChain) {
|
||||
InspectorTest.logObject(' - scope (' + scope.type + '):');
|
||||
if (scope.type == 'global') {
|
||||
if (!isWasmFrame && scope.type == 'global') {
|
||||
// Skip global scope for non wasm-functions.
|
||||
InspectorTest.logObject(' -- skipped globals');
|
||||
continue;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user