Commit cee2946f authored by Stephan Herhut's avatar Stephan Herhut Committed by Commit Bot

Lazily generate disassembly for WASM functions

Instead of computing the disassmebly and offset tables eagerly on
registering a WASM function with a debugger agent, only generate
it when the source or offset tables are actually required. This is
implemented using a lazy, memoizing supplier that is shared
between the debugger agent and wasm translator.

Bug: chromium:794941
Cq-Include-Trybots: master.tryserver.blink:linux_trusty_blink_rel;master.tryserver.chromium.linux:linux_chromium_rel_ng
Change-Id: I1a2f7dd71ab65c80f91ddee4f7babbdf33d2e74b
Reviewed-on: https://chromium-review.googlesource.com/918641
Commit-Queue: Stephan Herhut <herhut@chromium.org>
Reviewed-by: 's avatarDmitry Gozman <dgozman@chromium.org>
Reviewed-by: 's avatarClemens Hammacher <clemensh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#51762}
parent db5affec
......@@ -69,6 +69,7 @@
#include "src/snapshot/natives.h"
#include "src/snapshot/snapshot.h"
#include "src/startup-data-util.h"
#include "src/string-hasher.h"
#include "src/tracing/trace-event.h"
#include "src/trap-handler/trap-handler.h"
#include "src/unicode-cache-inl.h"
......@@ -9467,6 +9468,25 @@ std::pair<int, int> debug::WasmScript::GetFunctionRange(
static_cast<int>(func.code.end_offset()));
}
uint32_t debug::WasmScript::GetFunctionHash(int function_index) {
i::DisallowHeapAllocation no_gc;
i::Handle<i::Script> script = Utils::OpenHandle(this);
DCHECK_EQ(i::Script::TYPE_WASM, script->type());
i::WasmCompiledModule* compiled_module =
i::WasmCompiledModule::cast(script->wasm_compiled_module());
i::wasm::WasmModule* module = compiled_module->shared()->module();
DCHECK_LE(0, function_index);
DCHECK_GT(module->functions.size(), function_index);
i::wasm::WasmFunction& func = module->functions[function_index];
i::SeqOneByteString* module_bytes = compiled_module->shared()->module_bytes();
i::wasm::ModuleWireBytes wire_bytes(
module_bytes->GetFlatContent().ToOneByteVector());
i::Vector<const i::byte> function_bytes = wire_bytes.GetFunctionBytes(&func);
// TODO(herhut): Maybe also take module, name and signature into account.
return i::StringHasher::HashSequentialString(function_bytes.start(),
function_bytes.length(), 0);
}
debug::WasmDisassembly debug::WasmScript::DisassembleFunction(
int function_index) const {
i::DisallowHeapAllocation no_gc;
......
......@@ -166,6 +166,7 @@ class WasmScript : public Script {
std::pair<int, int> GetFunctionRange(int function_index) const;
debug::WasmDisassembly DisassembleFunction(int function_index) const;
uint32_t GetFunctionHash(int function_index);
};
void GetLoadedScripts(Isolate* isolate, PersistentValueVector<Script>& scripts);
......
......@@ -493,22 +493,39 @@ void String16Builder::append(const char* characters, size_t length) {
}
void String16Builder::appendNumber(int number) {
const int kBufferSize = 11;
constexpr int kBufferSize = 11;
char buffer[kBufferSize];
int chars = v8::base::OS::SNPrintF(buffer, kBufferSize, "%d", number);
DCHECK_GT(kBufferSize, chars);
DCHECK_LE(0, chars);
m_buffer.insert(m_buffer.end(), buffer, buffer + chars);
}
void String16Builder::appendNumber(size_t number) {
const int kBufferSize = 20;
constexpr int kBufferSize = 20;
char buffer[kBufferSize];
#if !defined(_WIN32) && !defined(_WIN64)
int chars = v8::base::OS::SNPrintF(buffer, kBufferSize, "%zu", number);
#else
int chars = v8::base::OS::SNPrintF(buffer, kBufferSize, "%Iu", number);
#endif
DCHECK_GT(kBufferSize, chars);
DCHECK_LE(0, chars);
m_buffer.insert(m_buffer.end(), buffer, buffer + chars);
}
void String16Builder::appendUnsignedAsHex(uint64_t number) {
constexpr int kBufferSize = 17;
char buffer[kBufferSize];
int chars =
v8::base::OS::SNPrintF(buffer, kBufferSize, "%016" PRIx64, number);
DCHECK_LE(0, chars);
m_buffer.insert(m_buffer.end(), buffer, buffer + chars);
}
void String16Builder::appendUnsignedAsHex(uint32_t number) {
constexpr int kBufferSize = 9;
char buffer[kBufferSize];
int chars = v8::base::OS::SNPrintF(buffer, kBufferSize, "%08" PRIx32, number);
DCHECK_LE(0, chars);
m_buffer.insert(m_buffer.end(), buffer, buffer + chars);
}
......
......@@ -112,6 +112,8 @@ class String16Builder {
void append(const char*, size_t);
void appendNumber(int);
void appendNumber(size_t);
void appendUnsignedAsHex(uint64_t);
void appendUnsignedAsHex(uint32_t);
String16 toString();
void reserveCapacity(size_t);
......
......@@ -156,9 +156,8 @@ String16 breakpointHint(const V8DebuggerScript& script, int lineNumber,
return hint;
}
void adjustBreakpointLocation(const V8DebuggerScript& script,
const String16& hint, int* lineNumber,
int* columnNumber) {
void adjustBreakpointLocation(V8DebuggerScript& script, const String16& hint,
int* lineNumber, int* columnNumber) {
if (*lineNumber < script.startLine() || *lineNumber > script.endLine())
return;
if (hint.isEmpty()) return;
......@@ -467,7 +466,7 @@ Response V8DebuggerAgentImpl::setSkipAllPauses(bool skip) {
return Response::OK();
}
static bool matches(V8InspectorImpl* inspector, const V8DebuggerScript& script,
static bool matches(V8InspectorImpl* inspector, V8DebuggerScript& script,
BreakpointType type, const String16& selector) {
switch (type) {
case BreakpointType::kByUrl:
......@@ -1357,10 +1356,12 @@ bool V8DebuggerAgentImpl::isPaused() const {
void V8DebuggerAgentImpl::didParseSource(
std::unique_ptr<V8DebuggerScript> script, bool success) {
v8::HandleScope handles(m_isolate);
if (!success) {
DCHECK(!script->isSourceLoadedLazily());
String16 scriptSource = script->source();
if (!success) script->setSourceURL(findSourceURL(scriptSource, false));
if (!success)
script->setSourceURL(findSourceURL(scriptSource, false));
script->setSourceMappingURL(findSourceMapURL(scriptSource, false));
}
int contextId = script->executionContextId();
int contextGroupId = m_inspector->contextGroupId(contextId);
......@@ -1402,6 +1403,15 @@ void V8DebuggerAgentImpl::didParseSource(
stack && !stack->isEmpty() ? stack->buildInspectorObjectImpl(m_debugger)
: nullptr;
if (success) {
// TODO(herhut, dgozman): Report correct length for WASM if needed for
// coverage. Or do not send the length at all and change coverage instead.
if (scriptRef->isSourceLoadedLazily()) {
m_frontend.scriptParsed(
scriptId, scriptURL, 0, 0, 0, 0, contextId, scriptRef->hash(),
std::move(executionContextAuxDataParam), isLiveEditParam,
std::move(sourceMapURLParam), hasSourceURLParam, isModuleParam, 0,
std::move(stackTrace));
} else {
m_frontend.scriptParsed(
scriptId, scriptURL, scriptRef->startLine(), scriptRef->startColumn(),
scriptRef->endLine(), scriptRef->endColumn(), contextId,
......@@ -1409,6 +1419,7 @@ void V8DebuggerAgentImpl::didParseSource(
isLiveEditParam, std::move(sourceMapURLParam), hasSourceURLParam,
isModuleParam, static_cast<int>(scriptRef->source().length()),
std::move(stackTrace));
}
} else {
m_frontend.scriptFailedToParse(
scriptId, scriptURL, scriptRef->startLine(), scriptRef->startColumn(),
......
......@@ -16,10 +16,10 @@ namespace {
const char hexDigits[17] = "0123456789ABCDEF";
const char kGlobalDebuggerScriptHandleLabel[] = "DevTools debugger";
void appendUnsignedAsHex(uint64_t number, String16Builder* destination) {
void appendUnsignedAsHex(uint64_t number, String16Builder& destination) {
for (size_t i = 0; i < 8; ++i) {
UChar c = hexDigits[number & 0xF];
destination->append(c);
destination.append(c);
number >>= 4;
}
}
......@@ -82,7 +82,8 @@ String16 calculateHash(const String16& str) {
hashes[i] = (hashes[i] + zi[i] * (prime[i] - 1)) % prime[i];
String16Builder hash;
for (size_t i = 0; i < hashesSize; ++i) appendUnsignedAsHex(hashes[i], &hash);
// TODO(herhut): Use String16Builder.appendUnsignedAsHex.
for (size_t i = 0; i < hashesSize; ++i) appendUnsignedAsHex(hashes[i], hash);
return hash.toString();
}
......@@ -157,6 +158,13 @@ class ActualScript : public V8DebuggerScript {
bool isLiveEdit() const override { return m_isLiveEdit; }
bool isModule() const override { return m_isModule; }
const String16& source() const override { return m_source; }
int startLine() const override { return m_startLine; }
int startColumn() const override { return m_startColumn; }
int endLine() const override { return m_endLine; }
int endColumn() const override { return m_endColumn; }
bool isSourceLoadedLazily() const override { return false; }
const String16& sourceMappingURL() const override {
return m_sourceMappingURL;
}
......@@ -241,6 +249,12 @@ class ActualScript : public V8DebuggerScript {
id);
}
const String16& hash() override {
if (m_hash.isEmpty()) m_hash = calculateHash(source());
DCHECK(!m_hash.isEmpty());
return m_hash;
}
private:
String16 GetNameOrSourceUrl(v8::Local<v8::debug::Script> script) {
v8::Local<v8::String> name;
......@@ -256,6 +270,12 @@ class ActualScript : public V8DebuggerScript {
String16 m_sourceMappingURL;
bool m_isLiveEdit = false;
bool m_isModule = false;
String16 m_source;
mutable String16 m_hash;
int m_startLine = 0;
int m_startColumn = 0;
int m_endLine = 0;
int m_endColumn = 0;
v8::Global<v8::debug::Script> m_script;
};
......@@ -265,22 +285,12 @@ class WasmVirtualScript : public V8DebuggerScript {
public:
WasmVirtualScript(v8::Isolate* isolate, WasmTranslation* wasmTranslation,
v8::Local<v8::debug::WasmScript> script, String16 id,
String16 url, String16 source)
String16 url, int functionIndex)
: V8DebuggerScript(isolate, std::move(id), std::move(url)),
m_script(isolate, script),
m_wasmTranslation(wasmTranslation) {
m_wasmTranslation(wasmTranslation),
m_functionIndex(functionIndex) {
m_script.AnnotateStrongRetainer(kGlobalDebuggerScriptHandleLabel);
int num_lines = 0;
int last_newline = -1;
size_t next_newline = source.find('\n', last_newline + 1);
while (next_newline != String16::kNotFound) {
last_newline = static_cast<int>(next_newline);
next_newline = source.find('\n', last_newline + 1);
++num_lines;
}
m_endLine = num_lines;
m_endColumn = static_cast<int>(source.length()) - last_newline - 1;
m_source = std::move(source);
m_executionContextId = script->ContextId().ToChecked();
}
......@@ -289,6 +299,22 @@ class WasmVirtualScript : public V8DebuggerScript {
bool isModule() const override { return false; }
void setSourceMappingURL(const String16&) override {}
void setSource(const String16&, bool, bool*) override { UNREACHABLE(); }
bool isSourceLoadedLazily() const override { return true; }
const String16& source() const override {
return m_wasmTranslation->GetSource(m_id, m_functionIndex);
}
int startLine() const override {
return m_wasmTranslation->GetStartLine(m_id, m_functionIndex);
}
int startColumn() const override {
return m_wasmTranslation->GetStartColumn(m_id, m_functionIndex);
}
int endLine() const override {
return m_wasmTranslation->GetEndLine(m_id, m_functionIndex);
}
int endColumn() const override {
return m_wasmTranslation->GetEndColumn(m_id, m_functionIndex);
}
bool getPossibleBreakpoints(
const v8::debug::Location& start, const v8::debug::Location& end,
......@@ -347,6 +373,13 @@ class WasmVirtualScript : public V8DebuggerScript {
return true;
}
const String16& hash() override {
if (m_hash.isEmpty()) {
m_hash = m_wasmTranslation->GetHash(m_id, m_functionIndex);
}
return m_hash;
}
private:
static const String16& emptyString() {
static const String16 singleEmptyString;
......@@ -359,6 +392,8 @@ class WasmVirtualScript : public V8DebuggerScript {
v8::Global<v8::debug::WasmScript> m_script;
WasmTranslation* m_wasmTranslation;
int m_functionIndex;
mutable String16 m_hash;
};
} // namespace
......@@ -373,10 +408,10 @@ std::unique_ptr<V8DebuggerScript> V8DebuggerScript::Create(
std::unique_ptr<V8DebuggerScript> V8DebuggerScript::CreateWasm(
v8::Isolate* isolate, WasmTranslation* wasmTranslation,
v8::Local<v8::debug::WasmScript> underlyingScript, String16 id,
String16 url, String16 source) {
String16 url, int functionIndex) {
return std::unique_ptr<WasmVirtualScript>(
new WasmVirtualScript(isolate, wasmTranslation, underlyingScript,
std::move(id), std::move(url), std::move(source)));
std::move(id), std::move(url), functionIndex));
}
V8DebuggerScript::V8DebuggerScript(v8::Isolate* isolate, String16 id,
......@@ -389,12 +424,6 @@ const String16& V8DebuggerScript::sourceURL() const {
return m_sourceURL.isEmpty() ? m_url : m_sourceURL;
}
const String16& V8DebuggerScript::hash() const {
if (m_hash.isEmpty()) m_hash = calculateHash(source());
DCHECK(!m_hash.isEmpty());
return m_hash;
}
void V8DebuggerScript::setSourceURL(const String16& sourceURL) {
m_sourceURL = sourceURL;
}
......
......@@ -50,7 +50,7 @@ class V8DebuggerScript {
static std::unique_ptr<V8DebuggerScript> CreateWasm(
v8::Isolate* isolate, WasmTranslation* wasmTranslation,
v8::Local<v8::debug::WasmScript> underlyingScript, String16 id,
String16 url, String16 source);
String16 url, int functionIndex);
virtual ~V8DebuggerScript();
......@@ -59,15 +59,16 @@ class V8DebuggerScript {
bool hasSourceURL() const { return !m_sourceURL.isEmpty(); }
const String16& sourceURL() const;
virtual const String16& sourceMappingURL() const = 0;
const String16& source() const { return m_source; }
const String16& hash() const;
int startLine() const { return m_startLine; }
int startColumn() const { return m_startColumn; }
int endLine() const { return m_endLine; }
int endColumn() const { return m_endColumn; }
virtual const String16& source() const = 0;
virtual const String16& hash() = 0;
virtual int startLine() const = 0;
virtual int startColumn() const = 0;
virtual int endLine() const = 0;
virtual int endColumn() const = 0;
int executionContextId() const { return m_executionContextId; }
virtual bool isLiveEdit() const = 0;
virtual bool isModule() const = 0;
virtual bool isSourceLoadedLazily() const = 0;
void setSourceURL(const String16&);
virtual void setSourceMappingURL(const String16&) = 0;
......@@ -95,12 +96,6 @@ class V8DebuggerScript {
String16 m_id;
String16 m_url;
String16 m_sourceURL;
String16 m_source;
mutable String16 m_hash;
int m_startLine = 0;
int m_startColumn = 0;
int m_endLine = 0;
int m_endColumn = 0;
int m_executionContextId = 0;
v8::Isolate* m_isolate;
......
This diff is collapsed.
......@@ -56,6 +56,13 @@ class WasmTranslation {
int* line_number,
int* column_number);
const String16& GetSource(const String16& script_id, int func_index);
int GetStartLine(const String16& script_id, int func_index) { return 0; }
int GetStartColumn(const String16& script_id, int func_index) { return 0; }
int GetEndLine(const String16& script_id, int func_index);
int GetEndColumn(const String16& script_id, int func_index);
String16 GetHash(const String16& script_id, int func_index);
private:
class TranslatorImpl;
friend class TranslatorImpl;
......
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