Commit eb36a7db authored by clemensh's avatar clemensh Committed by Commit bot

[wasm] Fix importing wasm functions which are being debugged

If the imported wasm function is being debugged (i.e. redirects to the
interpreter), call it via the JS_TO_WASM stub, such that we can disable
the breakpoint later by patching the exported function.

This also contains a drive-by fix in wasm-translation.cc (for the case
that all known positions are bigger than the requested one).

R=titzer@chromium.org, kozyatinskiy@chromium.org
BUG=v8:5971, v8:5822

Review-Url: https://codereview.chromium.org/2720813002
Cr-Commit-Position: refs/heads/master@{#43583}
parent 1a6487fd
......@@ -123,16 +123,18 @@ class WasmTranslation::TranslatorImpl::DisassemblingTranslator
}
int found_byte_offset = 0;
// If we found an exact match, use it. Otherwise check whether the next
// bigger entry is still in the same line. Report that one then.
// Otherwise we might have hit the special case of pointing after the last
// line, which is translated to the end of the function (one byte after the
// last function byte).
// [left] is <= <line,column>, or left==0 and [0] > <line,column>.
// We are searching for the smallest entry >= <line,column> which is still
// on the same line. This must be either [left] or [left + 1].
// If we don't find such an entry, we might have hit the special case of
// pointing after the last line, which is translated to the end of the
// function (one byte after the last function byte).
if ((*reverse_table)[left].line == loc->line &&
(*reverse_table)[left].column == loc->column) {
(*reverse_table)[left].column >= loc->column) {
found_byte_offset = (*reverse_table)[left].byte_offset;
} else if (left + 1 < reverse_table->size() &&
(*reverse_table)[left + 1].line == loc->line) {
(*reverse_table)[left + 1].line == loc->line &&
(*reverse_table)[left + 1].column >= loc->column) {
found_byte_offset = (*reverse_table)[left + 1].byte_offset;
} else if (left == reverse_table->size() - 1 &&
(*reverse_table)[left].line == loc->line - 1 &&
......
......@@ -869,6 +869,11 @@ static Handle<Code> UnwrapImportWrapper(Handle<Object> import_wrapper) {
for (RelocIterator it(*export_wrapper_code, mask);; it.next()) {
DCHECK(!it.done());
Code* target = Code::GetCodeFromTargetAddress(it.rinfo()->target_address());
if (target->kind() == Code::WASM_INTERPRETER_ENTRY) {
// Don't call the interpreter entry directly, otherwise we cannot
// disable the breakpoint later by patching the exported code.
return Handle<Code>::null();
}
if (target->kind() != Code::WASM_FUNCTION &&
target->kind() != Code::WASM_TO_JS_FUNCTION)
continue;
......@@ -893,18 +898,18 @@ Handle<Code> CompileImportWrapper(Isolate* isolate, int index, FunctionSig* sig,
ModuleOrigin origin) {
WasmFunction* other_func = GetWasmFunctionForImportWrapper(isolate, target);
if (other_func) {
if (sig->Equals(other_func->sig)) {
// Signature matched. Unwrap the JS->WASM wrapper and return the raw
// WASM function code.
return UnwrapImportWrapper(target);
} else {
return Handle<Code>::null();
}
} else {
// Signature mismatch. Compile a new wrapper for the new signature.
return compiler::CompileWasmToJSWrapper(isolate, target, sig, index,
module_name, import_name, origin);
}
if (!sig->Equals(other_func->sig)) return Handle<Code>::null();
// Signature matched. Unwrap the JS->WASM wrapper and return the raw
// WASM function code.
Handle<Code> code = UnwrapImportWrapper(target);
// If we got no code (imported function is being debugged), fall through
// to CompileWasmToJSWrapper.
if (!code.is_null()) return code;
}
// No wasm function or being debugged. Compile a new wrapper for the new
// signature.
return compiler::CompileWasmToJSWrapper(isolate, target, sig, index,
module_name, import_name, origin);
}
void UpdateDispatchTablesInternal(Isolate* isolate,
......
......@@ -13,7 +13,7 @@ Protocol.Debugger.onPaused(message => {
}
var scriptId = frames[0].location.scriptId;
InspectorTest.log('break at:');
InspectorTest.logCallFrameSourceLocation(frames[0])
InspectorTest.logSourceLocation(frames[0].location)
.then(() => Protocol.Debugger.stepInto());
});
......
Installing code and global variable.
Calling instantiate function for module A.
Waiting for wasm script to be parsed.
Got wasm script!
Setting breakpoint in line 1:
func $func
#nop
end
Calling instantiate function for module B.
Calling main function on module B.
Paused at 1:2.
func $func
#nop
end
Getting current stack trace via "new Error().stack".
Error
at v8://test/getStack:1:1
at main (<WASM>[1]+1)
at v8://test/runWasm:1:22
exports.main returned.
Finished.
// Copyright 2017 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.
utils.load('test/mjsunit/wasm/wasm-constants.js');
utils.load('test/mjsunit/wasm/wasm-module-builder.js');
// Build two modules A and B. A defines function func, which contains a
// breakpoint. This function is then imported by B and called via main. The
// breakpoint must be hit.
// This failed before (http://crbug.com/v8/5971).
var builder_a = new WasmModuleBuilder();
var func_idx = builder_a.addFunction('func', kSig_v_v)
.addBody([kExprNop])
.exportFunc()
.index;
var module_a_bytes = builder_a.toArray();
var builder_b = new WasmModuleBuilder();
var import_idx = builder_b.addImport('imp', 'f', kSig_v_v);
builder_b.addFunction('main', kSig_v_v)
.addBody([kExprCallFunction, import_idx])
.exportFunc();
var module_b_bytes = builder_b.toArray();
function instantiate(bytes, imp) {
var buffer = new ArrayBuffer(bytes.length);
var view = new Uint8Array(buffer);
for (var i = 0; i < bytes.length; ++i) {
view[i] = bytes[i] | 0;
}
var module = new WebAssembly.Module(buffer);
// Add to global instances array.
instances.push(new WebAssembly.Instance(module, imp));
}
var evalWithUrl = (code, url) => Protocol.Runtime.evaluate(
{'expression': code + '\n//# sourceURL=v8://test/' + url});
InspectorTest.setupScriptMap();
// Main promise chain:
Protocol.Debugger.enable()
.then(() => InspectorTest.log('Installing code and global variable.'))
.then(
() => evalWithUrl(
'var instances = [];\n' + instantiate.toString(), 'setup'))
.then(() => InspectorTest.log('Calling instantiate function for module A.'))
.then(
() =>
(evalWithUrl(
'instantiate(' + JSON.stringify(module_a_bytes) + ')',
'instantiateA'),
0))
.then(() => InspectorTest.log('Waiting for wasm script to be parsed.'))
.then(waitForWasmScript)
.then(url => (InspectorTest.log('Setting breakpoint in line 1:'), url))
.then(
url =>
Protocol.Debugger.setBreakpointByUrl({lineNumber: 1, url: url}))
.then(printFailure)
.then(msg => InspectorTest.logSourceLocations(msg.result.locations))
.then(() => InspectorTest.log('Calling instantiate function for module B.'))
.then(
() =>
(evalWithUrl(
'instantiate(' + JSON.stringify(module_b_bytes) +
', {imp: {f: instances[0].exports.func}})',
'instantiateB'),
0))
.then(() => InspectorTest.log('Calling main function on module B.'))
.then(() => evalWithUrl('instances[1].exports.main()', 'runWasm'))
.then(() => InspectorTest.log('exports.main returned.'))
.then(() => InspectorTest.log('Finished.'))
.then(InspectorTest.completeTest);
// Separate promise chain for the asynchronous pause:
Protocol.Debugger.oncePaused()
.then(msg => msg.params.callFrames[0].location)
.then(
loc =>
(InspectorTest.log(
'Paused at ' + loc.lineNumber + ':' + loc.columnNumber + '.'),
loc))
.then(InspectorTest.logSourceLocation)
.then(
() => InspectorTest.log(
'Getting current stack trace via "new Error().stack".'))
.then(() => evalWithUrl('new Error().stack', 'getStack'))
.then(msg => InspectorTest.log(msg.result.result.value))
.then(Protocol.Debugger.resume);
function printFailure(message) {
if (!message.result) {
InspectorTest.logMessage(message);
}
return message;
}
function waitForWasmScript(msg) {
if (!msg || !msg.params.url.startsWith('wasm://')) {
return Protocol.Debugger.onceScriptParsed().then(waitForWasmScript);
}
InspectorTest.log('Got wasm script!');
return Promise.resolve(msg.params.url);
}
......@@ -138,9 +138,9 @@ InspectorTest.logCallFrames = function(callFrames)
}
}
InspectorTest.logCallFrameSourceLocation = function(callFrame)
InspectorTest.logSourceLocation = function(location)
{
var scriptId = callFrame.location.scriptId;
var scriptId = location.scriptId;
if (!InspectorTest._scriptMap || !InspectorTest._scriptMap.has(scriptId)) {
InspectorTest.log("InspectorTest.setupScriptMap should be called before Protocol.Debugger.enable.");
InspectorTest.completeTest();
......@@ -154,7 +154,6 @@ InspectorTest.logCallFrameSourceLocation = function(callFrame)
return Promise.resolve().then(dumpSourceWithLocation);
function dumpSourceWithLocation() {
var location = callFrame.location;
var lines = script.scriptSource.split('\n');
var line = lines[location.lineNumber];
line = line.slice(0, location.columnNumber) + '#' + (line.slice(location.columnNumber) || '');
......@@ -164,6 +163,12 @@ InspectorTest.logCallFrameSourceLocation = function(callFrame)
}
}
InspectorTest.logSourceLocations = function(locations) {
if (locations.length == 0) return Promise.resolve();
return InspectorTest.logSourceLocation(locations[0])
.then(() => InspectorTest.logSourceLocations(locations.splice(1)));
}
InspectorTest.logAsyncStackTrace = function(asyncStackTrace)
{
while (asyncStackTrace) {
......
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