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

[inspector] Introduce translation of wasm frames

This allows to show wasm source (disassembled wasm code) in DevTools.
See design doc for details.

More tests for the disassembly will have to follow. Also, the text
format (generated by V8) will be changed.

BUG=chromium:659715
R=yangguo@chromium.org, kozyatinskiy@chromium.org, titzer@chromium.org, dgozman@chromium.org

Review-Url: https://codereview.chromium.org/2493773003
Cr-Commit-Position: refs/heads/master@{#41055}
parent 32793ab3
......@@ -8910,6 +8910,7 @@ int DebugInterface::Script::ColumnOffset() const {
std::vector<int> DebugInterface::Script::LineEnds() const {
i::Handle<i::Script> script = Utils::OpenHandle(this);
if (script->type() == i::Script::TYPE_WASM) return std::vector<int>();
i::Isolate* isolate = script->GetIsolate();
i::HandleScope scope(isolate);
i::Script::InitLineEnds(script);
......@@ -8973,6 +8974,10 @@ MaybeLocal<String> DebugInterface::Script::Source() const {
handle_scope.CloseAndEscape(i::Handle<i::String>::cast(value)));
}
bool DebugInterface::Script::IsWasm() const {
return Utils::OpenHandle(this)->type() == i::Script::TYPE_WASM;
}
namespace {
int GetSmiValue(i::Handle<i::FixedArray> array, int index) {
return i::Smi::cast(array->get(index))->value();
......@@ -8984,6 +8989,10 @@ bool DebugInterface::Script::GetPossibleBreakpoints(
std::vector<Location>* locations) const {
CHECK(!start.IsEmpty());
i::Handle<i::Script> script = Utils::OpenHandle(this);
if (script->type() == i::Script::TYPE_WASM) {
// TODO(clemensh): Return the proper thing once we support wasm breakpoints.
return false;
}
i::Script::InitLineEnds(script);
CHECK(script->line_ends()->IsFixedArray());
......@@ -9029,6 +9038,10 @@ bool DebugInterface::Script::GetPossibleBreakpoints(
int DebugInterface::Script::GetSourcePosition(const Location& location) const {
i::Handle<i::Script> script = Utils::OpenHandle(this);
if (script->type() == i::Script::TYPE_WASM) {
// TODO(clemensh): Return the proper thing for wasm.
return 0;
}
int line = std::max(location.GetLineNumber() - script->line_offset(), 0);
int column = location.GetColumnNumber();
......@@ -9062,7 +9075,10 @@ MaybeLocal<DebugInterface::Script> DebugInterface::Script::Wrap(
return MaybeLocal<Script>();
}
i::Handle<i::Script> script_obj = i::Handle<i::Script>::cast(script_value);
if (script_obj->type() != i::Script::TYPE_NORMAL) return MaybeLocal<Script>();
if (script_obj->type() != i::Script::TYPE_NORMAL &&
script_obj->type() != i::Script::TYPE_WASM) {
return MaybeLocal<Script>();
}
return ToApiHandle<DebugInterface::Script>(
handle_scope.CloseAndEscape(script_obj));
}
......@@ -9112,6 +9128,24 @@ void DebugInterface::GetLoadedScripts(
}
}
std::pair<std::string, std::vector<std::tuple<uint32_t, int, int>>>
DebugInterface::DisassembleWasmFunction(Isolate* v8_isolate,
Local<Object> v8_script,
int function_index) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
if (v8_script.IsEmpty()) return {};
i::Handle<i::Object> script_wrapper = Utils::OpenHandle(*v8_script);
if (!script_wrapper->IsJSValue()) return {};
i::Handle<i::Object> script_obj(
i::Handle<i::JSValue>::cast(script_wrapper)->value(), isolate);
if (!script_obj->IsScript()) return {};
i::Handle<i::Script> script = i::Handle<i::Script>::cast(script_obj);
if (script->type() != i::Script::TYPE_WASM) return {};
i::Handle<i::WasmCompiledModule> compiled_module(
i::WasmCompiledModule::cast(script->wasm_compiled_module()), isolate);
return i::wasm::DisassembleFunction(compiled_module, function_index);
}
Local<String> CpuProfileNode::GetFunctionName() const {
const i::ProfileNode* node = reinterpret_cast<const i::ProfileNode*>(this);
i::Isolate* isolate = node->isolate();
......
......@@ -179,6 +179,7 @@ class DebugInterface {
MaybeLocal<String> SourceMappingURL() const;
MaybeLocal<String> ContextData() const;
MaybeLocal<String> Source() const;
bool IsWasm() const;
bool GetPossibleBreakpoints(const Location& start, const Location& end,
std::vector<Location>* locations) const;
......@@ -202,6 +203,16 @@ class DebugInterface {
*/
static void GetLoadedScripts(Isolate* isolate,
PersistentValueVector<Script>& scripts);
/**
* Compute the disassembly of a wasm function.
* Returns the disassembly string and a list of <byte_offset, line, column>
* entries, mapping wasm byte offsets to line and column in the disassembly.
* The list is guaranteed to be ordered by the byte_offset.
*/
static std::pair<std::string, std::vector<std::tuple<uint32_t, int, int>>>
DisassembleWasmFunction(Isolate* isolate, v8::Local<v8::Object> script,
int function_index);
};
} // namespace v8
......
......@@ -186,5 +186,7 @@ v8_source_set("inspector") {
"v8-stack-trace-impl.h",
"v8-value-copier.cc",
"v8-value-copier.h",
"wasm-translation.cc",
"wasm-translation.h",
]
}
......@@ -90,6 +90,8 @@
'inspector/v8-stack-trace-impl.h',
'inspector/v8-value-copier.cc',
'inspector/v8-value-copier.h',
'inspector/wasm-translation.cc',
'inspector/wasm-translation.h',
]
}
}
......@@ -60,8 +60,29 @@ static const char kDebuggerNotEnabled[] = "Debugger agent is not enabled";
static const char kDebuggerNotPaused[] =
"Can only perform operation while paused.";
static String16 breakpointIdSuffix(
V8DebuggerAgentImpl::BreakpointSource source) {
namespace {
void TranslateWasmStackTraceLocations(Array<CallFrame>* stackTrace,
WasmTranslation* wasmTranslation,
int context_group_id) {
for (size_t i = 0, e = stackTrace->length(); i != e; ++i) {
protocol::Debugger::Location* location = stackTrace->get(i)->getLocation();
String16 scriptId = location->getScriptId();
int lineNumber = location->getLineNumber();
int columnNumber = location->getColumnNumber(-1);
if (!wasmTranslation->TranslateWasmScriptLocationToProtocolLocation(
&scriptId, &lineNumber, &columnNumber, context_group_id)) {
continue;
}
location->setScriptId(std::move(scriptId));
location->setLineNumber(lineNumber);
location->setColumnNumber(columnNumber);
}
}
String16 breakpointIdSuffix(V8DebuggerAgentImpl::BreakpointSource source) {
switch (source) {
case V8DebuggerAgentImpl::UserBreakpointSource:
break;
......@@ -73,9 +94,8 @@ static String16 breakpointIdSuffix(
return String16();
}
static String16 generateBreakpointId(
const ScriptBreakpoint& breakpoint,
V8DebuggerAgentImpl::BreakpointSource source) {
String16 generateBreakpointId(const ScriptBreakpoint& breakpoint,
V8DebuggerAgentImpl::BreakpointSource source) {
String16Builder builder;
builder.append(breakpoint.script_id);
builder.append(':');
......@@ -86,13 +106,13 @@ static String16 generateBreakpointId(
return builder.toString();
}
static bool positionComparator(const std::pair<int, int>& a,
const std::pair<int, int>& b) {
bool positionComparator(const std::pair<int, int>& a,
const std::pair<int, int>& b) {
if (a.first != b.first) return a.first < b.first;
return a.second < b.second;
}
static std::unique_ptr<protocol::Debugger::Location> buildProtocolLocation(
std::unique_ptr<protocol::Debugger::Location> buildProtocolLocation(
const String16& scriptId, int lineNumber, int columnNumber) {
return protocol::Debugger::Location::create()
.setScriptId(scriptId)
......@@ -101,6 +121,8 @@ static std::unique_ptr<protocol::Debugger::Location> buildProtocolLocation(
.build();
}
} // namespace
V8DebuggerAgentImpl::V8DebuggerAgentImpl(
V8InspectorSessionImpl* session, protocol::FrontendChannel* frontendChannel,
protocol::DictionaryValue* state)
......@@ -503,10 +525,18 @@ V8DebuggerAgentImpl::resolveBreakpoint(const String16& breakpointId,
scriptIterator->second->endLine() < breakpoint.line_number)
return nullptr;
ScriptBreakpoint translatedBreakpoint = breakpoint;
if (m_scripts.count(breakpoint.script_id) == 0) {
m_debugger->wasmTranslation()
->TranslateProtocolLocationToWasmScriptLocation(
&translatedBreakpoint.script_id, &translatedBreakpoint.line_number,
&translatedBreakpoint.column_number);
}
int actualLineNumber;
int actualColumnNumber;
String16 debuggerBreakpointId = m_debugger->setBreakpoint(
breakpoint, &actualLineNumber, &actualColumnNumber);
translatedBreakpoint, &actualLineNumber, &actualColumnNumber);
if (debuggerBreakpointId.isEmpty()) return nullptr;
m_serverBreakpoints[debuggerBreakpointId] =
......@@ -515,7 +545,7 @@ V8DebuggerAgentImpl::resolveBreakpoint(const String16& breakpointId,
m_breakpointIdToDebuggerBreakpointIds[breakpointId].push_back(
debuggerBreakpointId);
return buildProtocolLocation(breakpoint.script_id, actualLineNumber,
return buildProtocolLocation(translatedBreakpoint.script_id, actualLineNumber,
actualColumnNumber);
}
......@@ -529,9 +559,8 @@ Response V8DebuggerAgentImpl::searchInContent(
return Response::Error("No script for id: " + scriptId);
std::vector<std::unique_ptr<protocol::Debugger::SearchMatch>> matches =
searchInTextByLinesImpl(m_session,
toProtocolString(it->second->source(m_isolate)),
query, optionalCaseSensitive.fromMaybe(false),
searchInTextByLinesImpl(m_session, it->second->source(m_isolate), query,
optionalCaseSensitive.fromMaybe(false),
optionalIsRegex.fromMaybe(false));
*results = protocol::Array<protocol::Debugger::SearchMatch>::create();
for (size_t i = 0; i < matches.size(); ++i)
......@@ -602,7 +631,7 @@ Response V8DebuggerAgentImpl::getScriptSource(const String16& scriptId,
if (it == m_scripts.end())
return Response::Error("No script for id: " + scriptId);
v8::HandleScope handles(m_isolate);
*scriptSource = toProtocolString(it->second->source(m_isolate));
*scriptSource = it->second->source(m_isolate);
return Response::OK();
}
......@@ -1004,6 +1033,8 @@ Response V8DebuggerAgentImpl::currentCallFrames(
protocol::ErrorSupport errorSupport;
*result = Array<CallFrame>::parse(protocolValue.get(), &errorSupport);
if (!*result) return Response::Error(errorSupport.errors());
TranslateWasmStackTraceLocations(result->get(), m_debugger->wasmTranslation(),
m_session->contextGroupId());
return Response::OK();
}
......@@ -1017,7 +1048,7 @@ std::unique_ptr<StackTrace> V8DebuggerAgentImpl::currentAsyncStackTrace() {
void V8DebuggerAgentImpl::didParseSource(
std::unique_ptr<V8DebuggerScript> script, bool success) {
v8::HandleScope handles(m_isolate);
String16 scriptSource = toProtocolString(script->source(m_isolate));
String16 scriptSource = script->source(m_isolate);
if (!success) script->setSourceURL(findSourceURL(scriptSource, false));
if (!success)
script->setSourceMappingURL(findSourceMapURL(scriptSource, false));
......@@ -1046,14 +1077,14 @@ void V8DebuggerAgentImpl::didParseSource(
m_frontend.scriptParsed(
scriptId, scriptURL, scriptRef->startLine(), scriptRef->startColumn(),
scriptRef->endLine(), scriptRef->endColumn(),
scriptRef->executionContextId(), scriptRef->hash(),
scriptRef->executionContextId(), scriptRef->hash(m_isolate),
std::move(executionContextAuxDataParam), isLiveEditParam,
std::move(sourceMapURLParam), hasSourceURLParam);
else
m_frontend.scriptFailedToParse(
scriptId, scriptURL, scriptRef->startLine(), scriptRef->startColumn(),
scriptRef->endLine(), scriptRef->endColumn(),
scriptRef->executionContextId(), scriptRef->hash(),
scriptRef->executionContextId(), scriptRef->hash(m_isolate),
std::move(executionContextAuxDataParam), std::move(sourceMapURLParam),
hasSourceURLParam);
......
......@@ -116,9 +116,8 @@ V8DebuggerScript::V8DebuggerScript(v8::Isolate* isolate,
m_isLiveEdit = isLiveEdit;
if (script->Source().ToLocal(&tmp)) {
m_source.Reset(m_isolate, tmp);
m_sourceObj.Reset(m_isolate, tmp);
String16 source = toProtocolString(tmp);
m_hash = calculateHash(source);
// V8 will not count last line if script source ends with \n.
if (source.length() > 1 && source[source.length() - 1] == '\n') {
m_endLine++;
......@@ -129,14 +128,35 @@ V8DebuggerScript::V8DebuggerScript(v8::Isolate* isolate,
m_script.Reset(m_isolate, script);
}
V8DebuggerScript::V8DebuggerScript(String16 id, String16 url, String16 source)
: m_id(std::move(id)), m_url(std::move(url)), m_source(std::move(source)) {
int num_lines = 0;
int last_newline = -1;
size_t next_newline = m_source.find('\n', last_newline + 1);
while (next_newline != String16::kNotFound) {
last_newline = static_cast<int>(next_newline);
next_newline = m_source.find('\n', last_newline + 1);
++num_lines;
}
m_endLine = num_lines;
m_endColumn = static_cast<int>(m_source.length()) - last_newline - 1;
}
V8DebuggerScript::~V8DebuggerScript() {}
const String16& V8DebuggerScript::sourceURL() const {
return m_sourceURL.isEmpty() ? m_url : m_sourceURL;
}
v8::Local<v8::String> V8DebuggerScript::source(v8::Isolate* isolate) const {
return m_source.Get(isolate);
String16 V8DebuggerScript::source(v8::Isolate* isolate) const {
if (m_sourceObj.IsEmpty()) return m_source;
return toProtocolString(m_sourceObj.Get(isolate));
}
const String16& V8DebuggerScript::hash(v8::Isolate* isolate) const {
if (m_hash.isEmpty()) m_hash = calculateHash(source(isolate));
DCHECK(!m_hash.isEmpty());
return m_hash;
}
void V8DebuggerScript::setSourceURL(const String16& sourceURL) {
......@@ -148,8 +168,9 @@ void V8DebuggerScript::setSourceMappingURL(const String16& sourceMappingURL) {
}
void V8DebuggerScript::setSource(v8::Local<v8::String> source) {
m_source.Reset(m_isolate, source);
m_hash = calculateHash(toProtocolString(source));
m_source = String16();
m_sourceObj.Reset(m_isolate, source);
m_hash = String16();
}
bool V8DebuggerScript::getPossibleBreakpoints(
......
......@@ -43,6 +43,7 @@ class V8DebuggerScript {
V8DebuggerScript(v8::Isolate* isolate,
v8::Local<v8::DebugInterface::Script> script,
bool isLiveEdit);
V8DebuggerScript(String16 id, String16 url, String16 source);
~V8DebuggerScript();
const String16& scriptId() const { return m_id; }
......@@ -50,8 +51,8 @@ class V8DebuggerScript {
bool hasSourceURL() const { return !m_sourceURL.isEmpty(); }
const String16& sourceURL() const;
const String16& sourceMappingURL() const { return m_sourceMappingURL; }
v8::Local<v8::String> source(v8::Isolate*) const;
const String16& hash() const { return m_hash; }
String16 source(v8::Isolate*) const;
const String16& hash(v8::Isolate*) const;
int startLine() const { return m_startLine; }
int startColumn() const { return m_startColumn; }
int endLine() const { return m_endLine; }
......@@ -76,15 +77,16 @@ class V8DebuggerScript {
String16 m_url;
String16 m_sourceURL;
String16 m_sourceMappingURL;
v8::Global<v8::String> m_source;
String16 m_hash;
int m_startLine;
int m_startColumn;
int m_endLine;
int m_endColumn;
int m_executionContextId;
v8::Global<v8::String> m_sourceObj;
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;
String16 m_executionContextAuxData;
bool m_isLiveEdit;
bool m_isLiveEdit = false;
v8::Isolate* m_isolate;
v8::Global<v8::DebugInterface::Script> m_script;
......
......@@ -56,7 +56,8 @@ V8Debugger::V8Debugger(v8::Isolate* isolate, V8InspectorImpl* inspector)
m_runningNestedMessageLoop(false),
m_ignoreScriptParsedEventsCounter(0),
m_maxAsyncCallStackDepth(0),
m_pauseOnExceptionsState(v8::DebugInterface::NoBreakOnException) {}
m_pauseOnExceptionsState(v8::DebugInterface::NoBreakOnException),
m_wasmTranslation(isolate, this) {}
V8Debugger::~V8Debugger() {}
......@@ -82,6 +83,7 @@ void V8Debugger::disable() {
m_debuggerScript.Reset();
m_debuggerContext.Reset();
allAsyncTasksCanceled();
m_wasmTranslation.Clear();
v8::DebugInterface::SetDebugEventListener(m_isolate, nullptr);
}
......@@ -574,12 +576,25 @@ void V8Debugger::handleV8DebugEvent(
V8DebuggerAgentImpl* agent =
m_inspector->enabledDebuggerAgentForGroup(getGroupId(eventContext));
if (agent) {
v8::HandleScope scope(m_isolate);
if (m_ignoreScriptParsedEventsCounter == 0 &&
(event == v8::AfterCompile || event == v8::CompileError)) {
v8::Local<v8::Context> context = debuggerContext();
v8::Context::Scope contextScope(context);
if (!agent) return;
v8::HandleScope scope(m_isolate);
if (event == v8::AfterCompile || event == v8::CompileError) {
v8::Context::Scope contextScope(debuggerContext());
// Determine if the script is a wasm script.
v8::Local<v8::Value> scriptMirror =
callInternalGetterFunction(eventDetails.GetEventData(), "script");
DCHECK(scriptMirror->IsObject());
v8::Local<v8::Value> scriptWrapper =
callInternalGetterFunction(scriptMirror.As<v8::Object>(), "value");
DCHECK(scriptWrapper->IsObject());
v8::Local<v8::DebugInterface::Script> script =
v8::DebugInterface::Script::Wrap(m_isolate,
scriptWrapper.As<v8::Object>())
.ToLocalChecked();
if (script->IsWasm()) {
m_wasmTranslation.AddScript(scriptWrapper.As<v8::Object>());
} else if (m_ignoreScriptParsedEventsCounter == 0) {
v8::Local<v8::Value> argv[] = {eventDetails.GetEventData()};
v8::Local<v8::Value> value =
callDebuggerMethod("getAfterCompileScript", 1, argv).ToLocalChecked();
......@@ -593,29 +608,28 @@ void V8Debugger::handleV8DebugEvent(
agent->didParseSource(
wrapUnique(new V8DebuggerScript(m_isolate, script, inLiveEditScope)),
event == v8::AfterCompile);
} else if (event == v8::Exception) {
v8::Local<v8::Context> context = debuggerContext();
v8::Local<v8::Object> eventData = eventDetails.GetEventData();
v8::Local<v8::Value> exception =
callInternalGetterFunction(eventData, "exception");
v8::Local<v8::Value> promise =
callInternalGetterFunction(eventData, "promise");
bool isPromiseRejection = !promise.IsEmpty() && promise->IsObject();
v8::Local<v8::Value> uncaught =
callInternalGetterFunction(eventData, "uncaught");
bool isUncaught = uncaught->BooleanValue(context).FromJust();
handleProgramBreak(eventContext, eventDetails.GetExecutionState(),
exception, v8::Local<v8::Array>(), isPromiseRejection,
isUncaught);
} else if (event == v8::Break) {
v8::Local<v8::Value> argv[] = {eventDetails.GetEventData()};
v8::Local<v8::Value> hitBreakpoints =
callDebuggerMethod("getBreakpointNumbers", 1, argv).ToLocalChecked();
DCHECK(hitBreakpoints->IsArray());
handleProgramBreak(eventContext, eventDetails.GetExecutionState(),
v8::Local<v8::Value>(),
hitBreakpoints.As<v8::Array>());
}
} else if (event == v8::Exception) {
v8::Local<v8::Context> context = debuggerContext();
v8::Local<v8::Object> eventData = eventDetails.GetEventData();
v8::Local<v8::Value> exception =
callInternalGetterFunction(eventData, "exception");
v8::Local<v8::Value> promise =
callInternalGetterFunction(eventData, "promise");
bool isPromiseRejection = !promise.IsEmpty() && promise->IsObject();
v8::Local<v8::Value> uncaught =
callInternalGetterFunction(eventData, "uncaught");
bool isUncaught = uncaught->BooleanValue(context).FromJust();
handleProgramBreak(eventContext, eventDetails.GetExecutionState(),
exception, v8::Local<v8::Array>(), isPromiseRejection,
isUncaught);
} else if (event == v8::Break) {
v8::Local<v8::Value> argv[] = {eventDetails.GetEventData()};
v8::Local<v8::Value> hitBreakpoints =
callDebuggerMethod("getBreakpointNumbers", 1, argv).ToLocalChecked();
DCHECK(hitBreakpoints->IsArray());
handleProgramBreak(eventContext, eventDetails.GetExecutionState(),
v8::Local<v8::Value>(), hitBreakpoints.As<v8::Array>());
}
}
......
......@@ -13,6 +13,7 @@
#include "src/inspector/protocol/Forward.h"
#include "src/inspector/protocol/Runtime.h"
#include "src/inspector/v8-debugger-script.h"
#include "src/inspector/wasm-translation.h"
#include "include/v8-inspector.h"
......@@ -94,6 +95,8 @@ class V8Debugger {
V8InspectorImpl* inspector() { return m_inspector; }
WasmTranslation* wasmTranslation() { return &m_wasmTranslation; }
private:
void compileDebuggerScript();
v8::MaybeLocal<v8::Value> callDebuggerMethod(const char* functionName,
......@@ -149,6 +152,8 @@ class V8Debugger {
v8::DebugInterface::ExceptionBreakState m_pauseOnExceptionsState;
WasmTranslation m_wasmTranslation;
DISALLOW_COPY_AND_ASSIGN(V8Debugger);
};
......
......@@ -260,6 +260,7 @@ void V8InspectorImpl::resetContextGroup(int contextGroupId) {
SessionMap::iterator session = m_sessions.find(contextGroupId);
if (session != m_sessions.end()) session->second->reset();
m_contexts.erase(contextGroupId);
m_debugger->wasmTranslation()->Clear();
}
void V8InspectorImpl::willExecuteScript(v8::Local<v8::Context> context,
......
......@@ -5,6 +5,7 @@
#include "src/inspector/v8-stack-trace-impl.h"
#include "src/inspector/string-util.h"
#include "src/inspector/v8-debugger-agent-impl.h"
#include "src/inspector/v8-debugger.h"
#include "src/inspector/v8-inspector-impl.h"
......@@ -21,7 +22,9 @@ static const v8::StackTrace::StackTraceOptions stackTraceOptions =
v8::StackTrace::kScriptId | v8::StackTrace::kScriptNameOrSourceURL |
v8::StackTrace::kFunctionName);
V8StackTraceImpl::Frame toFrame(v8::Local<v8::StackFrame> frame) {
V8StackTraceImpl::Frame toFrame(v8::Local<v8::StackFrame> frame,
WasmTranslation* wasmTranslation,
int contextGroupId) {
String16 scriptId = String16::fromInteger(frame->GetScriptId());
String16 sourceName;
v8::Local<v8::String> sourceNameValue(frame->GetScriptNameOrSourceURL());
......@@ -33,22 +36,30 @@ V8StackTraceImpl::Frame toFrame(v8::Local<v8::StackFrame> frame) {
if (!functionNameValue.IsEmpty())
functionName = toProtocolString(functionNameValue);
int sourceLineNumber = frame->GetLineNumber();
int sourceColumn = frame->GetColumn();
int sourceLineNumber = frame->GetLineNumber() - 1;
int sourceColumn = frame->GetColumn() - 1;
// TODO(clemensh): Figure out a way to do this translation only right before
// sending the stack trace over wire.
if (wasmTranslation)
wasmTranslation->TranslateWasmScriptLocationToProtocolLocation(
&scriptId, &sourceLineNumber, &sourceColumn, contextGroupId);
return V8StackTraceImpl::Frame(functionName, scriptId, sourceName,
sourceLineNumber, sourceColumn);
sourceLineNumber + 1, sourceColumn + 1);
}
void toFramesVector(v8::Local<v8::StackTrace> stackTrace,
std::vector<V8StackTraceImpl::Frame>& frames,
size_t maxStackSize, v8::Isolate* isolate) {
size_t maxStackSize, v8::Isolate* isolate,
V8Debugger* debugger, int contextGroupId) {
DCHECK(isolate->InContext());
int frameCount = stackTrace->GetFrameCount();
if (frameCount > static_cast<int>(maxStackSize))
frameCount = static_cast<int>(maxStackSize);
WasmTranslation* wasmTranslation =
debugger ? debugger->wasmTranslation() : nullptr;
for (int i = 0; i < frameCount; i++) {
v8::Local<v8::StackFrame> stackFrame = stackTrace->GetFrame(i);
frames.push_back(toFrame(stackFrame));
frames.push_back(toFrame(stackFrame, wasmTranslation, contextGroupId));
}
}
......@@ -111,7 +122,8 @@ std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::create(
v8::HandleScope scope(isolate);
std::vector<V8StackTraceImpl::Frame> frames;
if (!stackTrace.IsEmpty())
toFramesVector(stackTrace, frames, maxStackSize, isolate);
toFramesVector(stackTrace, frames, maxStackSize, isolate, debugger,
contextGroupId);
int maxAsyncCallChainDepth = 1;
V8StackTraceImpl* asyncCallChain = nullptr;
......
This diff is collapsed.
// Copyright 2016 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.
#ifndef V8_INSPECTOR_WASMTRANSLATION_H_
#define V8_INSPECTOR_WASMTRANSLATION_H_
#include <unordered_map>
#include "include/v8.h"
#include "src/base/macros.h"
#include "src/inspector/string-16.h"
namespace v8_inspector {
// Forward declarations.
class V8Debugger;
class V8DebuggerScript;
struct ScriptBreakpoint;
namespace protocol {
namespace Debugger {
class Location;
}
}
class WasmTranslation {
public:
enum Mode { Raw, Disassemble };
WasmTranslation(v8::Isolate* isolate, V8Debugger* debugger);
~WasmTranslation();
// Set translation mode.
void SetMode(Mode mode) { mode_ = mode; }
// Make a wasm script known to the translation. Only locations referencing a
// registered script will be translated by the Translate functions below.
void AddScript(v8::Local<v8::Object> script_wrapper);
// Clear all registered scripts.
void Clear();
// Translate a location as generated by V8 to a location that should be sent
// over protocol.
// Does nothing for locations referencing a script which was not registered
// before via AddScript.
// Line and column are 0-based.
// The context group id specifies the context of the script.
// If the script was registered and the respective wasm function was not seen
// before, a new artificial script representing this function will be created
// and made public to the frontend.
// Returns true if the location was translated, false otherwise.
bool TranslateWasmScriptLocationToProtocolLocation(String16* script_id,
int* line_number,
int* column_number,
int context_group_id);
// Translate back from protocol locations (potentially referencing artificial
// scripts for individual wasm functions) to locations that make sense to V8.
// Does nothing if the location was not generated by the translate method
// above.
// Returns true if the location was translated, false otherwise.
bool TranslateProtocolLocationToWasmScriptLocation(String16* script_id,
int* line_number,
int* column_number);
private:
class TranslatorImpl;
friend class TranslatorImpl;
void AddFakeScript(std::unique_ptr<V8DebuggerScript> fake_script,
TranslatorImpl* translator, int context_group_id);
v8::Isolate* isolate_;
V8Debugger* debugger_;
std::unordered_map<int, std::unique_ptr<TranslatorImpl>> wasm_translators_;
std::unordered_map<String16, TranslatorImpl*> fake_scripts_;
Mode mode_;
DISALLOW_COPY_AND_ASSIGN(WasmTranslation);
};
} // namespace v8_inspector
#endif // V8_INSPECTOR_WASMTRANSLATION_H_
......@@ -597,6 +597,15 @@ std::pair<int, int> GetFunctionOffsetAndLength(
static_cast<int>(func.code_end_offset - func.code_start_offset)};
}
Vector<const uint8_t> GetFunctionBytes(
Handle<WasmCompiledModule> compiled_module, int func_index) {
int offset, length;
std::tie(offset, length) =
GetFunctionOffsetAndLength(compiled_module, func_index);
return Vector<const uint8_t>(
compiled_module->module_bytes()->GetChars() + offset, length);
}
} // namespace
const char* wasm::SectionName(WasmSectionCode code) {
......@@ -1882,6 +1891,26 @@ Handle<Script> wasm::GetScript(Handle<JSObject> instance) {
return compiled_module->script();
}
std::pair<std::string, std::vector<std::tuple<uint32_t, int, int>>>
wasm::DisassembleFunction(Handle<WasmCompiledModule> compiled_module,
int func_index) {
std::ostringstream disassembly_os;
std::vector<std::tuple<uint32_t, int, int>> offset_table;
Vector<const uint8_t> func_bytes =
GetFunctionBytes(compiled_module, func_index);
DisallowHeapAllocation no_gc;
if (func_bytes.is_empty()) return {};
AccountingAllocator allocator;
bool ok = PrintAst(
&allocator, FunctionBodyForTesting(func_bytes.start(), func_bytes.end()),
disassembly_os, &offset_table);
CHECK(ok);
return {disassembly_os.str(), std::move(offset_table)};
}
int wasm::GetAsmWasmSourcePosition(Handle<JSObject> instance, int func_index,
int byte_offset) {
return WasmDebugInfo::GetAsmJsSourcePosition(GetDebugInfo(instance),
......
......@@ -387,6 +387,13 @@ bool WasmIsAsmJs(Object* instance, Isolate* isolate);
// it's of type TYPE_WASM.
Handle<Script> GetScript(Handle<JSObject> instance);
// Compute the disassembly of a wasm function.
// Returns the disassembly string and a list of <byte_offset, line, column>
// entries, mapping wasm byte offsets to line and column in the disassembly.
// The list is guaranteed to be ordered by the byte_offset.
std::pair<std::string, std::vector<std::tuple<uint32_t, int, int>>>
DisassembleFunction(Handle<WasmCompiledModule> compiled_module, int func_index);
// Get the asm.js source position for the given byte offset in the given
// function.
int GetAsmWasmSourcePosition(Handle<JSObject> instance, int func_index,
......
......@@ -2,8 +2,8 @@ Running testFunction with generated WASM bytes...
Paused on 'debugger;'
Number of frames: 5
- [0] {"functionName":"call_debugger","function_lineNumber":1,"function_columnNumber":24,"lineNumber":2,"columnNumber":4}
- [1] {"functionName":"call_func","lineNumber":1,"columnNumber":1}
- [2] {"functionName":"main","lineNumber":2,"columnNumber":1}
- [1] {"functionName":"call_func","lineNumber":3,"columnNumber":0}
- [2] {"functionName":"main","lineNumber":4,"columnNumber":2}
- [3] {"functionName":"testFunction","function_lineNumber":0,"function_columnNumber":21,"lineNumber":14,"columnNumber":19}
- [4] {"functionName":"","function_lineNumber":0,"function_columnNumber":0,"lineNumber":0,"columnNumber":0}
Getting v8-generated stack trace...
......@@ -12,7 +12,7 @@ Error: this is your stack trace:
-- skipped --
at call_debugger (<anonymous>:3:5)
at call_func (<WASM>[1]+1)
at main (<WASM>[2]+1)
at main (<WASM>[2]+3)
at testFunction (<anonymous>:15:20)
at <anonymous>:1:1
Finished!
......@@ -11,13 +11,15 @@ var builder = new WasmModuleBuilder();
var imported_idx = builder.addImport("func", kSig_v_v);
var call_imported_idx = builder.addFunction("call_func", kSig_v_v)
var call_imported_idx = builder.addFunction('call_func', kSig_v_v)
.addBody([kExprCallFunction, imported_idx])
.index;
builder.addFunction("main", kSig_v_v)
.addBody([kExprCallFunction, call_imported_idx])
.exportAs("main");
// Open a block in order to make the positions more interesting...
builder.addFunction('main', kSig_v_v)
.addBody(
[kExprBlock, kAstStmt, kExprCallFunction, call_imported_idx, kExprEnd])
.exportAs('main');
var module_bytes = builder.toArray();
......
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