[wasm] Provide scope information via inspector
This CL implements the proposed change to show information about
WebAssembly values and call frames via the inspector interface.
Each interpreted WebAssembly frame will have two scopes: A global scope
showing information about the memory (to be extended for globals), and
a local scope showing information about parameters, local variables, and
stack values.
Names of local variables will be added later.
R=ahaas@chromium.org, yangguo@chromium.org
BUG=v8:6245,v8:5822
Change-Id: I0a35fddd0a353933c86adf62083233b08098a2c7
Reviewed-on: https://chromium-review.googlesource.com/474865
Commit-Queue: Clemens Hammacher <clemensh@chromium.org>
Reviewed-by: Aleksey Kozyatinskiy <kozyatinskiy@chromium.org>
Reviewed-by: Andreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#44633}diff --git a/src/frames.cc b/src/frames.cc
index f4f6690..87a1d37 100644
--- a/src/frames.cc
+++ b/src/frames.cc
@@ -1833,6 +1833,10 @@
return FrameSummary::GetBottom(this).AsWasmInterpreted().SourcePosition();
}
+Object* WasmInterpreterEntryFrame::context() const {
+ return wasm_instance()->compiled_module()->ptr_to_native_context();
+}
+
Address WasmInterpreterEntryFrame::GetCallerStackPointer() const {
return fp() + ExitFrameConstants::kCallerSPOffset;
}
diff --git a/src/frames.h b/src/frames.h
index 25baa46..8d643f0 100644
--- a/src/frames.h
+++ b/src/frames.h
@@ -1350,6 +1350,7 @@
WasmInstanceObject* wasm_instance() const;
Script* script() const override;
int position() const override;
+ Object* context() const override;
static WasmInterpreterEntryFrame* cast(StackFrame* frame) {
DCHECK(frame->is_wasm_interpreter_entry());
diff --git a/src/inspector/debugger-script.js b/src/inspector/debugger-script.js
index d9cb12a..89f0d75 100644
--- a/src/inspector/debugger-script.js
+++ b/src/inspector/debugger-script.js
@@ -402,10 +402,9 @@
*/
function contextId()
{
- var mirror = ensureFuncMirror();
- var context = mirror.context();
- if (context && context.data())
- return Number(context.data());
+ var context =
+ ensureFuncMirror().context() || ensureScriptMirror().context();
+ if (context && context.data()) return Number(context.data());
return 0;
}
diff --git a/src/inspector/debugger_script_externs.js b/src/inspector/debugger_script_externs.js
index 6f36fb9..656bada 100644
--- a/src/inspector/debugger_script_externs.js
+++ b/src/inspector/debugger_script_externs.js
@@ -355,6 +355,9 @@
/** @return {number} */
ScriptMirror.prototype.id = function() {}
+/** @return {ContextMirror} */
+ScriptMirror.prototype.context = function() {}
+
/**
* @param {number} position
* @param {boolean=} includeResourceOffset
diff --git a/src/runtime/runtime-debug.cc b/src/runtime/runtime-debug.cc
index a13d3f9..b65757b 100644
--- a/src/runtime/runtime-debug.cc
+++ b/src/runtime/runtime-debug.cc
@@ -551,7 +551,7 @@
// bit 0: invoked in the debugger context.
// bit 1: optimized frame.
// bit 2: inlined in optimized frame
- int flags = 0;
+ int flags = inlined_frame_index << 2;
if (*save->context() == *isolate->debug()->debug_context()) {
flags |= 1 << 0;
}
@@ -830,7 +830,7 @@
CHECK(isolate->debug()->CheckExecutionState(break_id));
CONVERT_SMI_ARG_CHECKED(wrapped_id, 1);
- CONVERT_NUMBER_CHECKED(int, inlined_jsframe_index, Int32, args[2]);
+ CONVERT_NUMBER_CHECKED(int, inlined_frame_index, Int32, args[2]);
ScopeIterator::Option option = ScopeIterator::DEFAULT;
if (args.length() == 4) {
@@ -842,9 +842,19 @@
StackFrame::Id id = DebugFrameHelper::UnwrapFrameId(wrapped_id);
StackTraceFrameIterator frame_it(isolate, id);
StandardFrame* frame = frame_it.frame();
- FrameInspector frame_inspector(frame, inlined_jsframe_index, isolate);
- List<Handle<JSObject> > result(4);
+ // Handle wasm frames specially. They provide exactly two scopes (global /
+ // local).
+ if (frame->is_wasm_interpreter_entry()) {
+ Handle<WasmDebugInfo> debug_info(
+ WasmInterpreterEntryFrame::cast(frame)->wasm_instance()->debug_info(),
+ isolate);
+ return *WasmDebugInfo::GetScopeDetails(debug_info, frame->fp(),
+ inlined_frame_index);
+ }
+
+ FrameInspector frame_inspector(frame, inlined_frame_index, isolate);
+ List<Handle<JSObject>> result(4);
ScopeIterator it(isolate, &frame_inspector, option);
for (; !it.Done(); it.Next()) {
Handle<JSObject> details;
diff --git a/src/wasm/wasm-debug.cc b/src/wasm/wasm-debug.cc
index 40f05d8..27ac7dd 100644
--- a/src/wasm/wasm-debug.cc
+++ b/src/wasm/wasm-debug.cc
@@ -7,6 +7,7 @@
#include "src/assembler-inl.h"
#include "src/assert-scope.h"
#include "src/compiler/wasm-compiler.h"
+#include "src/debug/debug-scopes.h"
#include "src/debug/debug.h"
#include "src/factory.h"
#include "src/frames-inl.h"
@@ -24,6 +25,42 @@
namespace {
+template <bool internal, typename... Args>
+Handle<String> PrintFToOneByteString(Isolate* isolate, const char* format,
+ Args... args) {
+ // Maximum length of a formatted value name ("param#%d", "local#%d",
+ // "global#%d").
+ constexpr int kMaxStrLen = 18;
+ EmbeddedVector<char, kMaxStrLen> value;
+ int len = SNPrintF(value, format, args...);
+ CHECK(len > 0 && len < value.length());
+ Vector<uint8_t> name = Vector<uint8_t>::cast(value.SubVector(0, len));
+ return internal
+ ? isolate->factory()->InternalizeOneByteString(name)
+ : isolate->factory()->NewStringFromOneByte(name).ToHandleChecked();
+}
+
+Handle<Object> WasmValToValueObject(Isolate* isolate, WasmVal value) {
+ switch (value.type) {
+ case kWasmI32:
+ if (Smi::IsValid(value.to<int32_t>()))
+ return handle(Smi::FromInt(value.to<int32_t>()), isolate);
+ return PrintFToOneByteString<false>(isolate, "%d", value.to<int32_t>());
+ case kWasmI64:
+ if (Smi::IsValid(value.to<int64_t>()))
+ return handle(Smi::FromIntptr(value.to<int64_t>()), isolate);
+ return PrintFToOneByteString<false>(isolate, "%" PRId64,
+ value.to<int64_t>());
+ case kWasmF32:
+ return isolate->factory()->NewNumber(value.to<float>());
+ case kWasmF64:
+ return isolate->factory()->NewNumber(value.to<double>());
+ default:
+ UNIMPLEMENTED();
+ return isolate->factory()->undefined_value();
+ }
+}
+
// Forward declaration.
class InterpreterHandle;
InterpreterHandle* GetInterpreterHandle(WasmDebugInfo* debug_info);
@@ -367,6 +404,88 @@
instance_.mem_start = reinterpret_cast<byte*>(new_memory->backing_store());
CHECK(new_memory->byte_length()->ToUint32(&instance_.mem_size));
}
+
+ Handle<JSArray> GetScopeDetails(Address frame_pointer, int frame_index,
+ Handle<WasmInstanceObject> instance) {
+ auto frame = GetInterpretedFrame(frame_pointer, frame_index);
+
+ Handle<FixedArray> global_scope =
+ isolate_->factory()->NewFixedArray(ScopeIterator::kScopeDetailsSize);
+ global_scope->set(ScopeIterator::kScopeDetailsTypeIndex,
+ Smi::FromInt(ScopeIterator::ScopeTypeGlobal));
+ Handle<JSObject> global_scope_object =
+ isolate_->factory()->NewJSObjectWithNullProto();
+ global_scope->set(ScopeIterator::kScopeDetailsObjectIndex,
+ *global_scope_object);
+
+ // TODO(clemensh): Add globals to the global scope.
+
+ if (instance->has_memory_buffer()) {
+ Handle<String> name = isolate_->factory()->InternalizeOneByteString(
+ STATIC_CHAR_VECTOR("memory"));
+ Handle<JSArrayBuffer> memory_buffer(instance->memory_buffer(), isolate_);
+ uint32_t byte_length;
+ CHECK(memory_buffer->byte_length()->ToUint32(&byte_length));
+ Handle<JSTypedArray> uint8_array = isolate_->factory()->NewJSTypedArray(
+ kExternalUint8Array, memory_buffer, 0, byte_length);
+ JSObject::SetOwnPropertyIgnoreAttributes(global_scope_object, name,
+ uint8_array, NONE)
+ .Check();
+ }
+
+ Handle<FixedArray> local_scope =
+ isolate_->factory()->NewFixedArray(ScopeIterator::kScopeDetailsSize);
+ local_scope->set(ScopeIterator::kScopeDetailsTypeIndex,
+ Smi::FromInt(ScopeIterator::ScopeTypeLocal));
+ Handle<JSObject> local_scope_object =
+ isolate_->factory()->NewJSObjectWithNullProto();
+ local_scope->set(ScopeIterator::kScopeDetailsObjectIndex,
+ *local_scope_object);
+
+ // Fill parameters and locals.
+ int num_params = frame->GetParameterCount();
+ int num_locals = frame->GetLocalCount();
+ DCHECK_LE(num_params, num_locals);
+ for (int i = 0; i < num_locals; ++i) {
+ // TODO(clemensh): Use names from name section if present.
+ const char* label = i < num_params ? "param#%d" : "local#%d";
+ Handle<String> name = PrintFToOneByteString<true>(isolate_, label, i);
+ WasmVal value = frame->GetLocalValue(i);
+ Handle<Object> value_obj = WasmValToValueObject(isolate_, value);
+ JSObject::SetOwnPropertyIgnoreAttributes(local_scope_object, name,
+ value_obj, NONE)
+ .Check();
+ }
+
+ // Fill stack values.
+ int stack_count = frame->GetStackHeight();
+ // Use an object without prototype instead of an Array, for nicer displaying
+ // in DevTools. For Arrays, the length field and prototype is displayed,
+ // which does not make too much sense here.
+ Handle<JSObject> stack_obj =
+ isolate_->factory()->NewJSObjectWithNullProto();
+ for (int i = 0; i < stack_count; ++i) {
+ WasmVal value = frame->GetStackValue(i);
+ Handle<Object> value_obj = WasmValToValueObject(isolate_, value);
+ JSObject::SetOwnElementIgnoreAttributes(
+ stack_obj, static_cast<uint32_t>(i), value_obj, NONE)
+ .Check();
+ }
+ Handle<String> stack_name = isolate_->factory()->InternalizeOneByteString(
+ STATIC_CHAR_VECTOR("stack"));
+ JSObject::SetOwnPropertyIgnoreAttributes(local_scope_object, stack_name,
+ stack_obj, NONE)
+ .Check();
+
+ Handle<JSArray> global_jsarr =
+ isolate_->factory()->NewJSArrayWithElements(global_scope);
+ Handle<JSArray> local_jsarr =
+ isolate_->factory()->NewJSArrayWithElements(local_scope);
+ Handle<FixedArray> all_scopes = isolate_->factory()->NewFixedArray(2);
+ all_scopes->set(0, *global_jsarr);
+ all_scopes->set(1, *local_jsarr);
+ return isolate_->factory()->NewJSArrayWithElements(all_scopes);
+ }
};
InterpreterHandle* GetOrCreateInterpreterHandle(
@@ -553,3 +672,12 @@
if (!interp_handle) return;
interp_handle->UpdateMemory(new_memory);
}
+
+// static
+Handle<JSArray> WasmDebugInfo::GetScopeDetails(Handle<WasmDebugInfo> debug_info,
+ Address frame_pointer,
+ int frame_index) {
+ InterpreterHandle* interp_handle = GetInterpreterHandle(*debug_info);
+ Handle<WasmInstanceObject> instance(debug_info->wasm_instance());
+ return interp_handle->GetScopeDetails(frame_pointer, frame_index, instance);
+}
diff --git a/src/wasm/wasm-interpreter.cc b/src/wasm/wasm-interpreter.cc
index 02f9f0d..8981ae0 100644
--- a/src/wasm/wasm-interpreter.cc
+++ b/src/wasm/wasm-interpreter.cc
@@ -2259,8 +2259,10 @@
static_cast<size_t>(index_) + 1 == thread_->frames_.size();
size_t stack_limit =
is_top_frame ? thread_->stack_.size() : thread_->frames_[index_ + 1].sp;
- DCHECK_LE(GetLocalCount(), stack_limit);
- return static_cast<int>(stack_limit) - GetLocalCount();
+ DCHECK_LE(frame()->sp, stack_limit);
+ size_t frame_size = stack_limit - frame()->sp;
+ DCHECK_LE(GetLocalCount(), frame_size);
+ return static_cast<int>(frame_size) - GetLocalCount();
}
WasmVal GetLocalValue(int index) const {
diff --git a/src/wasm/wasm-objects.h b/src/wasm/wasm-objects.h
index ddb7e77..94ac540 100644
--- a/src/wasm/wasm-objects.h
+++ b/src/wasm/wasm-objects.h
@@ -593,6 +593,17 @@
// Update the memory view of the interpreter after executing GrowMemory in
// compiled code.
void UpdateMemory(JSArrayBuffer* new_memory);
+
+ // Get scope details for a specific interpreted frame.
+ // This returns a JSArray of length two: One entry for the global scope, one
+ // for the local scope. Both elements are JSArrays of size
+ // ScopeIterator::kScopeDetailsSize and layout as described in debug-scopes.h.
+ // The global scope contains information about globals and the memory.
+ // The local scope contains information about parameters, locals, and stack
+ // values.
+ static Handle<JSArray> GetScopeDetails(Handle<WasmDebugInfo>,
+ Address frame_pointer,
+ int frame_index);
};
class WasmInstanceWrapper : public FixedArray {
diff --git a/test/inspector/debugger/wasm-stepping-expected.txt b/test/inspector/debugger/wasm-stepping-expected.txt
index a2df3e4..ab80865 100644
--- a/test/inspector/debugger/wasm-stepping-expected.txt
+++ b/test/inspector/debugger/wasm-stepping-expected.txt
@@ -32,52 +32,298 @@
scriptId : <scriptId>
}
Paused at wasm://wasm/wasm-0c10a5fe/wasm-0c10a5fe-1:7:6: >set_local 0
-Step action: stepInto
+at wasm_B (7:6):
+ - scope (global):
+ -- skipped
+ - scope (local):
+ param#0: 4 (number)
+ stack: {"0":3} (Object)
+at (anonymous) (0:17):
+ - scope (global):
+ -- skipped
+Debugger.stepInto called
Paused at wasm://wasm/wasm-0c10a5fe/wasm-0c10a5fe-1:8:6: >call 0
-Step action: stepInto
+at wasm_B (8:6):
+ - scope (global):
+ -- skipped
+ - scope (local):
+ param#0: 3 (number)
+ stack: {} (Object)
+at (anonymous) (0:17):
+ - scope (global):
+ -- skipped
+Debugger.stepInto called
Paused at wasm://wasm/wasm-0c10a5fe/wasm-0c10a5fe-0:1:2: >nop
-Step action: stepOver
+at wasm_A (1:2):
+ - scope (global):
+ -- skipped
+ - scope (local):
+ stack: {} (Object)
+at wasm_B (8:6):
+ - scope (global):
+ -- skipped
+ - scope (local):
+ param#0: 3 (number)
+ stack: {} (Object)
+at (anonymous) (0:17):
+ - scope (global):
+ -- skipped
+Debugger.stepOver called
Paused at wasm://wasm/wasm-0c10a5fe/wasm-0c10a5fe-0:2:2: >nop
-Step action: stepOut
+at wasm_A (2:2):
+ - scope (global):
+ -- skipped
+ - scope (local):
+ stack: {} (Object)
+at wasm_B (8:6):
+ - scope (global):
+ -- skipped
+ - scope (local):
+ param#0: 3 (number)
+ stack: {} (Object)
+at (anonymous) (0:17):
+ - scope (global):
+ -- skipped
+Debugger.stepOut called
Paused at wasm://wasm/wasm-0c10a5fe/wasm-0c10a5fe-1:9:6: >br 1
-Step action: stepOut
+at wasm_B (9:6):
+ - scope (global):
+ -- skipped
+ - scope (local):
+ param#0: 3 (number)
+ stack: {} (Object)
+at (anonymous) (0:17):
+ - scope (global):
+ -- skipped
+Debugger.stepOut called
Paused at wasm://wasm/wasm-0c10a5fe/wasm-0c10a5fe-1:7:6: >set_local 0
-Step action: stepOver
+at wasm_B (7:6):
+ - scope (global):
+ -- skipped
+ - scope (local):
+ param#0: 3 (number)
+ stack: {"0":2} (Object)
+at (anonymous) (0:17):
+ - scope (global):
+ -- skipped
+Debugger.stepOver called
Paused at wasm://wasm/wasm-0c10a5fe/wasm-0c10a5fe-1:8:6: >call 0
-Step action: stepOver
+at wasm_B (8:6):
+ - scope (global):
+ -- skipped
+ - scope (local):
+ param#0: 2 (number)
+ stack: {} (Object)
+at (anonymous) (0:17):
+ - scope (global):
+ -- skipped
+Debugger.stepOver called
Paused at wasm://wasm/wasm-0c10a5fe/wasm-0c10a5fe-1:9:6: >br 1
-Step action: resume
+at wasm_B (9:6):
+ - scope (global):
+ -- skipped
+ - scope (local):
+ param#0: 2 (number)
+ stack: {} (Object)
+at (anonymous) (0:17):
+ - scope (global):
+ -- skipped
+Debugger.resume called
Paused at wasm://wasm/wasm-0c10a5fe/wasm-0c10a5fe-1:7:6: >set_local 0
-Step action: stepInto
+at wasm_B (7:6):
+ - scope (global):
+ -- skipped
+ - scope (local):
+ param#0: 2 (number)
+ stack: {"0":1} (Object)
+at (anonymous) (0:17):
+ - scope (global):
+ -- skipped
+Debugger.stepInto called
Paused at wasm://wasm/wasm-0c10a5fe/wasm-0c10a5fe-1:8:6: >call 0
-Step action: stepInto
+at wasm_B (8:6):
+ - scope (global):
+ -- skipped
+ - scope (local):
+ param#0: 1 (number)
+ stack: {} (Object)
+at (anonymous) (0:17):
+ - scope (global):
+ -- skipped
+Debugger.stepInto called
Paused at wasm://wasm/wasm-0c10a5fe/wasm-0c10a5fe-0:1:2: >nop
-Step action: stepOut
+at wasm_A (1:2):
+ - scope (global):
+ -- skipped
+ - scope (local):
+ stack: {} (Object)
+at wasm_B (8:6):
+ - scope (global):
+ -- skipped
+ - scope (local):
+ param#0: 1 (number)
+ stack: {} (Object)
+at (anonymous) (0:17):
+ - scope (global):
+ -- skipped
+Debugger.stepOut called
Paused at wasm://wasm/wasm-0c10a5fe/wasm-0c10a5fe-1:9:6: >br 1
-Step action: stepInto
+at wasm_B (9:6):
+ - scope (global):
+ -- skipped
+ - scope (local):
+ param#0: 1 (number)
+ stack: {} (Object)
+at (anonymous) (0:17):
+ - scope (global):
+ -- skipped
+Debugger.stepInto called
Paused at wasm://wasm/wasm-0c10a5fe/wasm-0c10a5fe-1:1:2: >loop
-Step action: stepInto
+at wasm_B (1:2):
+ - scope (global):
+ -- skipped
+ - scope (local):
+ param#0: 1 (number)
+ stack: {} (Object)
+at (anonymous) (0:17):
+ - scope (global):
+ -- skipped
+Debugger.stepInto called
Paused at wasm://wasm/wasm-0c10a5fe/wasm-0c10a5fe-1:2:4: >get_local 0
-Step action: stepInto
+at wasm_B (2:4):
+ - scope (global):
+ -- skipped
+ - scope (local):
+ param#0: 1 (number)
+ stack: {} (Object)
+at (anonymous) (0:17):
+ - scope (global):
+ -- skipped
+Debugger.stepInto called
Paused at wasm://wasm/wasm-0c10a5fe/wasm-0c10a5fe-1:3:4: >if
-Step action: stepInto
+at wasm_B (3:4):
+ - scope (global):
+ -- skipped
+ - scope (local):
+ param#0: 1 (number)
+ stack: {"0":1} (Object)
+at (anonymous) (0:17):
+ - scope (global):
+ -- skipped
+Debugger.stepInto called
Paused at wasm://wasm/wasm-0c10a5fe/wasm-0c10a5fe-1:4:6: >get_local 0
-Step action: stepInto
+at wasm_B (4:6):
+ - scope (global):
+ -- skipped
+ - scope (local):
+ param#0: 1 (number)
+ stack: {} (Object)
+at (anonymous) (0:17):
+ - scope (global):
+ -- skipped
+Debugger.stepInto called
Paused at wasm://wasm/wasm-0c10a5fe/wasm-0c10a5fe-1:5:6: >i32.const 1
-Step action: stepInto
+at wasm_B (5:6):
+ - scope (global):
+ -- skipped
+ - scope (local):
+ param#0: 1 (number)
+ stack: {"0":1} (Object)
+at (anonymous) (0:17):
+ - scope (global):
+ -- skipped
+Debugger.stepInto called
Paused at wasm://wasm/wasm-0c10a5fe/wasm-0c10a5fe-1:6:6: >i32.sub
-Step action: stepInto
+at wasm_B (6:6):
+ - scope (global):
+ -- skipped
+ - scope (local):
+ param#0: 1 (number)
+ stack: {"0":1,"1":1} (Object)
+at (anonymous) (0:17):
+ - scope (global):
+ -- skipped
+Debugger.stepInto called
Paused at wasm://wasm/wasm-0c10a5fe/wasm-0c10a5fe-1:7:6: >set_local 0
-Step action: stepInto
+at wasm_B (7:6):
+ - scope (global):
+ -- skipped
+ - scope (local):
+ param#0: 1 (number)
+ stack: {"0":0} (Object)
+at (anonymous) (0:17):
+ - scope (global):
+ -- skipped
+Debugger.stepInto called
Paused at wasm://wasm/wasm-0c10a5fe/wasm-0c10a5fe-1:8:6: >call 0
-Step action: stepInto
+at wasm_B (8:6):
+ - scope (global):
+ -- skipped
+ - scope (local):
+ param#0: 0 (number)
+ stack: {} (Object)
+at (anonymous) (0:17):
+ - scope (global):
+ -- skipped
+Debugger.stepInto called
Paused at wasm://wasm/wasm-0c10a5fe/wasm-0c10a5fe-0:1:2: >nop
-Step action: stepInto
+at wasm_A (1:2):
+ - scope (global):
+ -- skipped
+ - scope (local):
+ stack: {} (Object)
+at wasm_B (8:6):
+ - scope (global):
+ -- skipped
+ - scope (local):
+ param#0: 0 (number)
+ stack: {} (Object)
+at (anonymous) (0:17):
+ - scope (global):
+ -- skipped
+Debugger.stepInto called
Paused at wasm://wasm/wasm-0c10a5fe/wasm-0c10a5fe-0:2:2: >nop
-Step action: stepInto
+at wasm_A (2:2):
+ - scope (global):
+ -- skipped
+ - scope (local):
+ stack: {} (Object)
+at wasm_B (8:6):
+ - scope (global):
+ -- skipped
+ - scope (local):
+ param#0: 0 (number)
+ stack: {} (Object)
+at (anonymous) (0:17):
+ - scope (global):
+ -- skipped
+Debugger.stepInto called
Paused at wasm://wasm/wasm-0c10a5fe/wasm-0c10a5fe-0:3:0: >end
-Step action: stepInto
+at wasm_A (3:0):
+ - scope (global):
+ -- skipped
+ - scope (local):
+ stack: {} (Object)
+at wasm_B (8:6):
+ - scope (global):
+ -- skipped
+ - scope (local):
+ param#0: 0 (number)
+ stack: {} (Object)
+at (anonymous) (0:17):
+ - scope (global):
+ -- skipped
+Debugger.stepInto called
Paused at wasm://wasm/wasm-0c10a5fe/wasm-0c10a5fe-1:9:6: >br 1
-Step action: resume
+at wasm_B (9:6):
+ - scope (global):
+ -- skipped
+ - scope (local):
+ param#0: 0 (number)
+ stack: {} (Object)
+at (anonymous) (0:17):
+ - scope (global):
+ -- skipped
+Debugger.resume called
exports.main returned!
Finished!
diff --git a/test/inspector/debugger/wasm-stepping.js b/test/inspector/debugger/wasm-stepping.js
index 5f132df..7de6451 100644
--- a/test/inspector/debugger/wasm-stepping.js
+++ b/test/inspector/debugger/wasm-stepping.js
@@ -68,6 +68,9 @@
// then just resume.
'resume'
];
+for (var action of step_actions) {
+ InspectorTest.logProtocolCommandCalls('Debugger.' + action)
+}
var sources = {};
var urls = {};
var afterTwoSourcesCallback;
@@ -151,10 +154,48 @@
line);
}
+async function getValueString(value) {
+ if (value.type == 'object') {
+ var msg = await Protocol.Runtime.callFunctionOn({
+ objectId: value.objectId,
+ functionDeclaration: 'function () { return JSON.stringify(this); }'
+ });
+ printFailure(msg);
+ return msg.result.result.value + ' (' + value.description + ')';
+ }
+ return value.value + ' (' + value.type + ')';
+}
+
+async function dumpProperties(message) {
+ printFailure(message);
+ for (var value of message.result.result) {
+ var value_str = await getValueString(value.value);
+ InspectorTest.log(' ' + value.name + ': ' + value_str);
+ }
+}
+
+async function dumpScopeChainsOnPause(message) {
+ for (var frame of message.params.callFrames) {
+ 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') {
+ InspectorTest.logObject(' -- skipped');
+ } else {
+ var properties = await Protocol.Runtime.getProperties(
+ {'objectId': scope.object.objectId});
+ await dumpProperties(properties);
+ }
+ }
+ }
+}
+
function handlePaused(msg) {
var loc = msg.params.callFrames[0].location;
printPauseLocation(loc.scriptId, loc.lineNumber, loc.columnNumber);
- var action = step_actions.shift();
- InspectorTest.log('Step action: ' + action);
- Protocol.Debugger[action]();
+ dumpScopeChainsOnPause(msg)
+ .then(Protocol.Debugger[step_actions.shift() || 'resume']);
}