Commit 31f44eea authored by Ingvar Stepanyan's avatar Ingvar Stepanyan Committed by Commit Bot

Fix Wasm reporting to multiple inspectors

Separate creating Wasm translations from reporting them to an agent.
This is done in order to support multiple connected sessions.

Previously connecting more than one agent would fail assertion in debug
mode and overwrite translation objects over and over
(and potentially do something worse) in release mode.

Bug: v8:9725
Change-Id: I13fde5ebf6e64e7268eb6870f9c21ac9a5bed81e
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1807273Reviewed-by: 's avatarYang Guo <yangguo@chromium.org>
Commit-Queue: Ingvar Stepanyan <rreverser@google.com>
Cr-Commit-Position: refs/heads/master@{#63867}
parent db3df9f6
...@@ -67,15 +67,20 @@ class WasmTranslation::TranslatorImpl { ...@@ -67,15 +67,20 @@ class WasmTranslation::TranslatorImpl {
column(column) {} column(column) {}
}; };
TranslatorImpl(v8::Isolate* isolate, v8::Local<v8::debug::WasmScript> script) TranslatorImpl(v8::Isolate* isolate, WasmTranslation* translation,
v8::Local<v8::debug::WasmScript> script)
: script_(isolate, script) { : script_(isolate, script) {
script_.AnnotateStrongRetainer(kGlobalScriptHandleLabel); script_.AnnotateStrongRetainer(kGlobalScriptHandleLabel);
ForEachFunction(script, [this, translation](String16& script_id,
int func_idx) {
translation->AddFakeScript(GetFakeScriptId(script_id, func_idx), this);
});
} }
void Init(v8::Isolate* isolate, WasmTranslation* translation, template <typename Callback>
V8DebuggerAgentImpl* agent) { void ForEachFunction(v8::Local<v8::debug::WasmScript> script,
// Register fake scripts for each function in this wasm module/script. Callback callback) {
v8::Local<v8::debug::WasmScript> script = script_.Get(isolate);
int num_functions = script->NumFunctions(); int num_functions = script->NumFunctions();
int num_imported_functions = script->NumImportedFunctions(); int num_imported_functions = script->NumImportedFunctions();
DCHECK_LE(0, num_imported_functions); DCHECK_LE(0, num_imported_functions);
...@@ -84,10 +89,18 @@ class WasmTranslation::TranslatorImpl { ...@@ -84,10 +89,18 @@ class WasmTranslation::TranslatorImpl {
String16 script_id = String16::fromInteger(script->Id()); String16 script_id = String16::fromInteger(script->Id());
for (int func_idx = num_imported_functions; func_idx < num_functions; for (int func_idx = num_imported_functions; func_idx < num_functions;
++func_idx) { ++func_idx) {
AddFakeScript(isolate, script_id, func_idx, translation, agent); callback(script_id, func_idx);
} }
} }
void ReportFakeScripts(v8::Isolate* isolate, WasmTranslation* translation,
V8DebuggerAgentImpl* agent) {
ForEachFunction(
script_.Get(isolate), [=](String16& script_id, int func_idx) {
ReportFakeScript(isolate, script_id, func_idx, translation, agent);
});
}
void Translate(TransLocation* loc) { void Translate(TransLocation* loc) {
const OffsetTable& offset_table = GetOffsetTable(loc); const OffsetTable& offset_table = GetOffsetTable(loc);
DCHECK(!offset_table.empty()); DCHECK(!offset_table.empty());
...@@ -212,8 +225,9 @@ class WasmTranslation::TranslatorImpl { ...@@ -212,8 +225,9 @@ class WasmTranslation::TranslatorImpl {
return GetFakeScriptId(loc->script_id, loc->line); return GetFakeScriptId(loc->script_id, loc->line);
} }
void AddFakeScript(v8::Isolate* isolate, const String16& underlyingScriptId, void ReportFakeScript(v8::Isolate* isolate,
int func_idx, WasmTranslation* translation, const String16& underlyingScriptId, int func_idx,
WasmTranslation* translation,
V8DebuggerAgentImpl* agent) { V8DebuggerAgentImpl* agent) {
String16 fake_script_id = GetFakeScriptId(underlyingScriptId, func_idx); String16 fake_script_id = GetFakeScriptId(underlyingScriptId, func_idx);
String16 fake_script_url = GetFakeScriptUrl(isolate, func_idx); String16 fake_script_url = GetFakeScriptUrl(isolate, func_idx);
...@@ -223,7 +237,6 @@ class WasmTranslation::TranslatorImpl { ...@@ -223,7 +237,6 @@ class WasmTranslation::TranslatorImpl {
fake_script_id, std::move(fake_script_url), fake_script_id, std::move(fake_script_url),
func_idx); func_idx);
translation->AddFakeScript(fake_script->scriptId(), this);
agent->didParseSource(std::move(fake_script), true); agent->didParseSource(std::move(fake_script), true);
} }
...@@ -254,6 +267,9 @@ class WasmTranslation::TranslatorImpl { ...@@ -254,6 +267,9 @@ class WasmTranslation::TranslatorImpl {
// We assume to only disassemble a subset of the functions, so store them in a // We assume to only disassemble a subset of the functions, so store them in a
// map instead of an array. // map instead of an array.
std::unordered_map<int, WasmSourceInformation> source_informations_; std::unordered_map<int, WasmSourceInformation> source_informations_;
// Disallow copies, because our pointer is registered in translation.
DISALLOW_COPY_AND_ASSIGN(TranslatorImpl);
}; };
constexpr char WasmTranslation::TranslatorImpl::kGlobalScriptHandleLabel[]; constexpr char WasmTranslation::TranslatorImpl::kGlobalScriptHandleLabel[];
...@@ -264,15 +280,11 @@ WasmTranslation::~WasmTranslation() { Clear(); } ...@@ -264,15 +280,11 @@ WasmTranslation::~WasmTranslation() { Clear(); }
void WasmTranslation::AddScript(v8::Local<v8::debug::WasmScript> script, void WasmTranslation::AddScript(v8::Local<v8::debug::WasmScript> script,
V8DebuggerAgentImpl* agent) { V8DebuggerAgentImpl* agent) {
std::unique_ptr<TranslatorImpl> impl; auto& impl = wasm_translators_[script->Id()];
impl.reset(new TranslatorImpl(isolate_, script)); if (impl == nullptr) {
DCHECK(impl); impl = std::make_unique<TranslatorImpl>(isolate_, this, script);
auto inserted = }
wasm_translators_.insert(std::make_pair(script->Id(), std::move(impl))); impl->ReportFakeScripts(isolate_, this, agent);
// Check that no mapping for this script id existed before.
DCHECK(inserted.second);
// impl has been moved, use the returned iterator to call Init.
inserted.first->second->Init(isolate_, this, agent);
} }
void WasmTranslation::Clear() { void WasmTranslation::Clear() {
......
Tests how wasm scripts are reported Tests how wasm scripts are reported
Check that inspector gets two wasm scripts at module creation time. Check that each inspector gets two wasm scripts at module creation time.
Script #0 parsed. URL: v8://test/testFunction Session #1: Script #0 parsed. URL: wasm://wasm/wasm-7b04570e/wasm-7b04570e-0
Script #1 parsed. URL: Session #1: Script #1 parsed. URL: wasm://wasm/wasm-7b04570e/wasm-7b04570e-1
Script #2 parsed. URL: v8://test/runTestRunction Session #2: Script #0 parsed. URL: wasm://wasm/wasm-7b04570e/wasm-7b04570e-0
Script #3 parsed. URL: wasm://wasm/wasm-7b04570e/wasm-7b04570e-0 Session #2: Script #1 parsed. URL: wasm://wasm/wasm-7b04570e/wasm-7b04570e-1
Script #4 parsed. URL: wasm://wasm/wasm-7b04570e/wasm-7b04570e-1 Session #1: Source for wasm://wasm/wasm-7b04570e/wasm-7b04570e-0:
Source for wasm://wasm/wasm-7b04570e/wasm-7b04570e-0:
func $nopFunction func $nopFunction
nop nop
end end
Source for wasm://wasm/wasm-7b04570e/wasm-7b04570e-1: Session #1: Source for wasm://wasm/wasm-7b04570e/wasm-7b04570e-1:
func $main func $main
block block
i32.const 2 i32.const 2
...@@ -18,3 +17,15 @@ func $main ...@@ -18,3 +17,15 @@ func $main
end end
end end
Session #2: Source for wasm://wasm/wasm-7b04570e/wasm-7b04570e-0:
func $nopFunction
nop
end
Session #2: Source for wasm://wasm/wasm-7b04570e/wasm-7b04570e-1:
func $main
block
i32.const 2
drop
end
end
...@@ -4,7 +4,16 @@ ...@@ -4,7 +4,16 @@
// Flags: --expose-wasm // Flags: --expose-wasm
let {session, contextGroup, Protocol} = InspectorTest.start('Tests how wasm scripts are reported'); InspectorTest.log("Tests how wasm scripts are reported");
let contextGroup = new InspectorTest.ContextGroup();
let sessions = [
// Main session.
trackScripts(),
// Extra session to verify that all inspectors get same messages.
// See https://bugs.chromium.org/p/v8/issues/detail?id=9725.
trackScripts(),
];
utils.load('test/mjsunit/wasm/wasm-module-builder.js'); utils.load('test/mjsunit/wasm/wasm-module-builder.js');
...@@ -18,54 +27,55 @@ builder.addFunction('main', kSig_v_v) ...@@ -18,54 +27,55 @@ builder.addFunction('main', kSig_v_v)
var module_bytes = builder.toArray(); var module_bytes = builder.toArray();
function testFunction(bytes) { function testFunction(bytes) {
var buffer = new ArrayBuffer(bytes.length);
var view = new Uint8Array(buffer);
for (var i = 0; i < bytes.length; i++) {
view[i] = bytes[i] | 0;
}
// Compilation triggers registration of wasm scripts. // Compilation triggers registration of wasm scripts.
new WebAssembly.Module(buffer); new WebAssembly.Module(new Uint8Array(bytes));
} }
contextGroup.addScript(testFunction.toString(), 0, 0, 'v8://test/testFunction'); contextGroup.addScript(testFunction.toString(), 0, 0, 'v8://test/testFunction');
contextGroup.addScript('var module_bytes = ' + JSON.stringify(module_bytes)); contextGroup.addScript('var module_bytes = ' + JSON.stringify(module_bytes));
Protocol.Debugger.enable();
Protocol.Debugger.onScriptParsed(handleScriptParsed);
InspectorTest.log( InspectorTest.log(
'Check that inspector gets two wasm scripts at module creation time.'); 'Check that each inspector gets two wasm scripts at module creation time.');
Protocol.Runtime
sessions[0].Protocol.Runtime
.evaluate({ .evaluate({
'expression': '//# sourceURL=v8://test/runTestRunction\n' + 'expression': '//# sourceURL=v8://test/runTestRunction\n' +
'testFunction(module_bytes)' 'testFunction(module_bytes)'
}) })
.then(checkFinished); .then(() => (
// At this point all scripts were parsed.
var num_scripts = 0; // Stop tracking and wait for script sources in each session.
var missing_sources = 0; Promise.all(sessions.map(session => session.getScripts()))
))
.catch(err => {
InspectorTest.log("FAIL: " + err.message);
})
.then(() => InspectorTest.completeTest());
function checkFinished() { function trackScripts() {
if (missing_sources == 0) let {id: sessionId, Protocol} = contextGroup.connect();
InspectorTest.completeTest(); let scripts = [];
}
function handleScriptParsed(messageObject) Protocol.Debugger.enable();
{ Protocol.Debugger.onScriptParsed(handleScriptParsed);
var url = messageObject.params.url;
InspectorTest.log("Script #" + num_scripts + " parsed. URL: " + url);
++num_scripts;
if (url.startsWith("wasm://")) { async function getScript({url, scriptId}) {
++missing_sources; let {result: {scriptSource}} = await Protocol.Debugger.getScriptSource({scriptId});
function dumpScriptSource(message) { InspectorTest.log(`Session #${sessionId}: Source for ${url}:`);
InspectorTest.log("Source for " + url + ":"); InspectorTest.log(scriptSource);
InspectorTest.log(message.result.scriptSource); return {url, scriptSource};
--missing_sources;
} }
Protocol.Debugger.getScriptSource({scriptId: messageObject.params.scriptId}) function handleScriptParsed({params}) {
.then(dumpScriptSource.bind(null)) let {url} = params;
.then(checkFinished); if (!url.startsWith("wasm://")) return;
InspectorTest.log(`Session #${sessionId}: Script #${scripts.length} parsed. URL: ${url}`);
scripts.push(getScript(params));
} }
return {
Protocol,
getScripts: () => Promise.all(scripts),
};
} }
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