V8 Wasm locations should always be based on byte offsets
Currently there are two ways wasm locations are represented in the inspector. This remains unchanged for now. Also, currently there are multiple ways location is represented within V8, with the line number sometimes being a function index and sometimes being 0, and the column number being a byte offset which is sometimes function relative and sometimes module relative. With this change, the line number is never used within V8 (it is always 0), and the column number is always a byte offset from the beginning of the module. This simplifies translation logic and keeps it in one place, and will simplify future changes to wasm location representation in the inspector API. Bug: chromium:1013527 Change-Id: I8813d47c881988f9ab49d7529fb81fe10dbbccff Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1886915 Commit-Queue: Eric Leese <leese@chromium.org> Reviewed-by: Simon Zünd <szuend@chromium.org> Reviewed-by: Yang Guo <yangguo@chromium.org> Reviewed-by: Clemens Backes <clemensb@chromium.org> Cr-Commit-Position: refs/heads/master@{#64774}
This commit is contained in:
parent
4fa5f2b504
commit
5c23e6b5f2
@ -2013,6 +2013,12 @@ class V8_EXPORT Message {
|
||||
*/
|
||||
int GetEndPosition() const;
|
||||
|
||||
/**
|
||||
* Returns the Wasm function index where the error occurred. Returns -1 if
|
||||
* message is not from a Wasm script.
|
||||
*/
|
||||
int GetWasmFunctionIndex() const;
|
||||
|
||||
/**
|
||||
* Returns the error level of the message.
|
||||
*/
|
||||
@ -2045,6 +2051,7 @@ class V8_EXPORT Message {
|
||||
static const int kNoLineNumberInfo = 0;
|
||||
static const int kNoColumnInfo = 0;
|
||||
static const int kNoScriptIdInfo = 0;
|
||||
static const int kNoWasmFunctionIndexInfo = -1;
|
||||
};
|
||||
|
||||
|
||||
|
@ -2920,6 +2920,26 @@ int Message::GetStartColumn() const {
|
||||
return self->GetColumnNumber();
|
||||
}
|
||||
|
||||
int Message::GetWasmFunctionIndex() const {
|
||||
auto self = Utils::OpenHandle(this);
|
||||
i::Isolate* isolate = self->GetIsolate();
|
||||
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(isolate);
|
||||
EscapableHandleScope handle_scope(reinterpret_cast<Isolate*>(isolate));
|
||||
i::JSMessageObject::EnsureSourcePositionsAvailable(isolate, self);
|
||||
int start_position = self->GetColumnNumber();
|
||||
if (start_position == -1) return Message::kNoWasmFunctionIndexInfo;
|
||||
|
||||
i::Handle<i::Script> script(self->script(), isolate);
|
||||
|
||||
if (script->type() != i::Script::TYPE_WASM) {
|
||||
return Message::kNoWasmFunctionIndexInfo;
|
||||
}
|
||||
|
||||
auto debug_script = ToApiHandle<debug::Script>(script);
|
||||
return Local<debug::WasmScript>::Cast(debug_script)
|
||||
->GetContainingFunction(start_position);
|
||||
}
|
||||
|
||||
Maybe<int> Message::GetStartColumn(Local<Context> context) const {
|
||||
return Just(GetStartColumn());
|
||||
}
|
||||
@ -9446,12 +9466,6 @@ bool debug::Script::GetPossibleBreakpoints(
|
||||
int debug::Script::GetSourceOffset(const debug::Location& location) const {
|
||||
i::Handle<i::Script> script = Utils::OpenHandle(this);
|
||||
if (script->type() == i::Script::TYPE_WASM) {
|
||||
if (this->SourceMappingURL().IsEmpty()) {
|
||||
i::wasm::NativeModule* native_module = script->wasm_native_module();
|
||||
const i::wasm::WasmModule* module = native_module->module();
|
||||
return i::wasm::GetWasmFunctionOffset(module, location.GetLineNumber()) +
|
||||
location.GetColumnNumber();
|
||||
}
|
||||
DCHECK_EQ(0, location.GetLineNumber());
|
||||
return location.GetColumnNumber();
|
||||
}
|
||||
@ -9576,6 +9590,17 @@ std::pair<int, int> debug::WasmScript::GetFunctionRange(
|
||||
static_cast<int>(func.code.end_offset()));
|
||||
}
|
||||
|
||||
int debug::WasmScript::GetContainingFunction(int byte_offset) const {
|
||||
i::DisallowHeapAllocation no_gc;
|
||||
i::Handle<i::Script> script = Utils::OpenHandle(this);
|
||||
DCHECK_EQ(i::Script::TYPE_WASM, script->type());
|
||||
i::wasm::NativeModule* native_module = script->wasm_native_module();
|
||||
const i::wasm::WasmModule* module = native_module->module();
|
||||
DCHECK_LE(0, byte_offset);
|
||||
|
||||
return i::wasm::GetContainingWasmFunction(module, byte_offset);
|
||||
}
|
||||
|
||||
uint32_t debug::WasmScript::GetFunctionHash(int function_index) {
|
||||
i::DisallowHeapAllocation no_gc;
|
||||
i::Handle<i::Script> script = Utils::OpenHandle(this);
|
||||
|
@ -1210,6 +1210,7 @@ extern class StackFrameInfo extends Struct {
|
||||
column_number: Smi;
|
||||
promise_all_index: Smi;
|
||||
script_id: Smi;
|
||||
wasm_function_index: Smi;
|
||||
script_name: String|Null|Undefined;
|
||||
script_name_or_source_url: String|Null|Undefined;
|
||||
function_name: String|Null|Undefined;
|
||||
|
@ -1680,9 +1680,9 @@ void Shell::ReportException(Isolate* isolate, v8::TryCatch* try_catch) {
|
||||
printf("%s\n", exception_string);
|
||||
} else if (message->GetScriptOrigin().Options().IsWasm()) {
|
||||
// Print wasm-function[(function index)]:(offset): (message).
|
||||
int function_index = message->GetLineNumber(context).FromJust() - 1;
|
||||
int function_index = message->GetWasmFunctionIndex();
|
||||
int offset = message->GetStartColumn(context).FromJust();
|
||||
printf("wasm-function[%d]:%d: %s\n", function_index, offset,
|
||||
printf("wasm-function[%d]:0x%x: %s\n", function_index, offset,
|
||||
exception_string);
|
||||
} else {
|
||||
// Print (filename):(line number): (message).
|
||||
|
@ -162,6 +162,7 @@ class WasmScript : public Script {
|
||||
MemorySpan<const uint8_t> Bytecode() const;
|
||||
|
||||
std::pair<int, int> GetFunctionRange(int function_index) const;
|
||||
int GetContainingFunction(int byte_offset) const;
|
||||
|
||||
debug::WasmDisassembly DisassembleFunction(int function_index) const;
|
||||
uint32_t GetFunctionHash(int function_index);
|
||||
|
@ -320,6 +320,8 @@ Handle<Object> StackFrameBase::GetWasmModuleName() {
|
||||
return isolate_->factory()->undefined_value();
|
||||
}
|
||||
|
||||
int StackFrameBase::GetWasmFunctionIndex() { return StackFrameBase::kNone; }
|
||||
|
||||
Handle<Object> StackFrameBase::GetWasmInstance() {
|
||||
return isolate_->factory()->undefined_value();
|
||||
}
|
||||
|
@ -83,6 +83,8 @@ class StackFrameBase {
|
||||
virtual int GetLineNumber() = 0;
|
||||
// Return 1-based column number, including column offset if first line.
|
||||
virtual int GetColumnNumber() = 0;
|
||||
// Return 0-based Wasm function index. Returns -1 for non-Wasm frames.
|
||||
virtual int GetWasmFunctionIndex();
|
||||
|
||||
// Returns index for Promise.all() async frames, or -1 for other frames.
|
||||
virtual int GetPromiseIndex() const = 0;
|
||||
@ -174,8 +176,9 @@ class WasmStackFrame : public StackFrameBase {
|
||||
Handle<Object> GetWasmInstance() override;
|
||||
|
||||
int GetPosition() const override;
|
||||
int GetLineNumber() override { return wasm_func_index_; }
|
||||
int GetLineNumber() override { return 0; }
|
||||
int GetColumnNumber() override;
|
||||
int GetWasmFunctionIndex() override { return wasm_func_index_; }
|
||||
|
||||
int GetPromiseIndex() const override { return GetPosition(); }
|
||||
|
||||
|
@ -3693,6 +3693,7 @@ Handle<StackFrameInfo> Factory::NewStackFrameInfo(
|
||||
|
||||
int line = frame->GetLineNumber();
|
||||
int column = frame->GetColumnNumber();
|
||||
int wasm_function_index = frame->GetWasmFunctionIndex();
|
||||
|
||||
const int script_id = frame->GetScriptId();
|
||||
|
||||
@ -3744,6 +3745,7 @@ Handle<StackFrameInfo> Factory::NewStackFrameInfo(
|
||||
info->set_is_user_java_script(is_user_java_script);
|
||||
info->set_line_number(line);
|
||||
info->set_column_number(column);
|
||||
info->set_wasm_function_index(wasm_function_index);
|
||||
info->set_script_id(script_id);
|
||||
|
||||
info->set_script_name(*script_name);
|
||||
|
@ -394,9 +394,9 @@ class WasmVirtualScript : public V8DebuggerScript {
|
||||
|
||||
v8::debug::Location translatedEnd = end;
|
||||
if (translatedEnd.IsEmpty()) {
|
||||
// Stop before the start of the next function.
|
||||
translatedEnd =
|
||||
v8::debug::Location(translatedStart.GetLineNumber() + 1, 0);
|
||||
// Stop at the end of the function.
|
||||
int end_offset = m_wasmTranslation->GetEndOffset(scriptId());
|
||||
translatedEnd = v8::debug::Location(0, end_offset);
|
||||
} else {
|
||||
TranslateProtocolLocationToV8Location(m_wasmTranslation, &translatedEnd,
|
||||
scriptId(), v8ScriptId);
|
||||
|
@ -102,9 +102,17 @@ class WasmTranslation::TranslatorImpl {
|
||||
}
|
||||
|
||||
void Translate(TransLocation* loc) {
|
||||
const OffsetTable& offset_table = GetOffsetTable(loc);
|
||||
v8::Isolate* isolate = loc->translation->isolate_;
|
||||
int func_index = GetFunctionIndexFromLocation(loc);
|
||||
DCHECK_GE(func_index, 0);
|
||||
const OffsetTable& offset_table = GetOffsetTable(isolate, func_index);
|
||||
DCHECK(!offset_table.empty());
|
||||
uint32_t byte_offset = static_cast<uint32_t>(loc->column);
|
||||
std::pair<int, int> func_range =
|
||||
script_.Get(isolate)->GetFunctionRange(func_index);
|
||||
DCHECK_LE(func_range.first, loc->column);
|
||||
DCHECK_LT(loc->column, func_range.second);
|
||||
uint32_t byte_offset =
|
||||
static_cast<uint32_t>(loc->column - func_range.first);
|
||||
|
||||
// Binary search for the given offset.
|
||||
unsigned left = 0; // inclusive
|
||||
@ -143,25 +151,25 @@ class WasmTranslation::TranslatorImpl {
|
||||
// Binary search for the given line and column.
|
||||
auto element = std::lower_bound(reverse_table.begin(), reverse_table.end(),
|
||||
*loc, LessThan);
|
||||
std::pair<int, int> func_range =
|
||||
script_.Get(isolate)->GetFunctionRange(func_index);
|
||||
DCHECK_LE(func_range.first, func_range.second);
|
||||
|
||||
int found_byte_offset = 0;
|
||||
int found_byte_offset = func_range.first;
|
||||
// We want an entry on the same line if possible.
|
||||
if (element == reverse_table.end()) {
|
||||
// We did not find an element, so this points after the function.
|
||||
std::pair<int, int> func_range =
|
||||
script_.Get(isolate)->GetFunctionRange(func_index);
|
||||
DCHECK_LE(func_range.first, func_range.second);
|
||||
found_byte_offset = func_range.second - func_range.first;
|
||||
found_byte_offset = func_range.second;
|
||||
} else if (element->line == loc->line || element == reverse_table.begin()) {
|
||||
found_byte_offset = element->byte_offset;
|
||||
found_byte_offset = element->byte_offset + func_range.first;
|
||||
} else {
|
||||
auto prev = element - 1;
|
||||
DCHECK(prev->line == loc->line);
|
||||
found_byte_offset = prev->byte_offset;
|
||||
found_byte_offset = prev->byte_offset + func_range.first;
|
||||
}
|
||||
|
||||
loc->script_id = String16::fromInteger(script_.Get(isolate)->Id());
|
||||
loc->line = func_index;
|
||||
loc->line = 0;
|
||||
loc->column = found_byte_offset;
|
||||
}
|
||||
|
||||
@ -197,6 +205,7 @@ class WasmTranslation::TranslatorImpl {
|
||||
}
|
||||
|
||||
private:
|
||||
friend class WasmTranslation;
|
||||
String16 GetFakeScriptUrl(v8::Isolate* isolate, int func_index) {
|
||||
v8::Local<v8::debug::WasmScript> script = script_.Get(isolate);
|
||||
String16 script_name =
|
||||
@ -222,7 +231,7 @@ class WasmTranslation::TranslatorImpl {
|
||||
return String16::concat(script_id, '-', String16::fromInteger(func_index));
|
||||
}
|
||||
String16 GetFakeScriptId(const TransLocation* loc) {
|
||||
return GetFakeScriptId(loc->script_id, loc->line);
|
||||
return GetFakeScriptId(loc->script_id, GetFunctionIndexFromLocation(loc));
|
||||
}
|
||||
|
||||
void ReportFakeScript(v8::Isolate* isolate,
|
||||
@ -249,10 +258,15 @@ class WasmTranslation::TranslatorImpl {
|
||||
return func_index;
|
||||
}
|
||||
|
||||
const OffsetTable& GetOffsetTable(const TransLocation* loc) {
|
||||
int func_index = loc->line;
|
||||
return GetSourceInformation(loc->translation->isolate_, func_index)
|
||||
.offset_table;
|
||||
int GetFunctionIndexFromLocation(const TransLocation* loc) {
|
||||
DCHECK_EQ(0, loc->line);
|
||||
v8::Isolate* isolate = loc->translation->isolate_;
|
||||
v8::Local<v8::debug::WasmScript> script = script_.Get(isolate);
|
||||
return script->GetContainingFunction(loc->column);
|
||||
}
|
||||
|
||||
const OffsetTable& GetOffsetTable(v8::Isolate* isolate, int func_index) {
|
||||
return GetSourceInformation(isolate, func_index).offset_table;
|
||||
}
|
||||
|
||||
const OffsetTable& GetReverseTable(v8::Isolate* isolate, int func_index) {
|
||||
@ -384,6 +398,17 @@ bool WasmTranslation::TranslateProtocolLocationToWasmScriptLocation(
|
||||
return true;
|
||||
}
|
||||
|
||||
// Find the end byte offset for a fake script.
|
||||
int WasmTranslation::GetEndOffset(const String16& script_id) {
|
||||
auto it = fake_scripts_.find(script_id);
|
||||
DCHECK_NE(fake_scripts_.end(), it);
|
||||
TranslatorImpl* translator = it->second;
|
||||
int func_index = translator->GetFunctionIndexFromFakeScriptId(script_id);
|
||||
std::pair<int, int> func_range =
|
||||
translator->script_.Get(isolate_)->GetFunctionRange(func_index);
|
||||
return func_range.second;
|
||||
}
|
||||
|
||||
void WasmTranslation::AddFakeScript(const String16& scriptId,
|
||||
TranslatorImpl* translator) {
|
||||
DCHECK_EQ(0, fake_scripts_.count(scriptId));
|
||||
|
@ -55,6 +55,9 @@ class WasmTranslation {
|
||||
int* line_number,
|
||||
int* column_number);
|
||||
|
||||
// Find the end byte offset for a fake script.
|
||||
int GetEndOffset(const String16& script_id);
|
||||
|
||||
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; }
|
||||
|
@ -4662,29 +4662,16 @@ bool Script::GetPositionInfo(int position, PositionInfo* info,
|
||||
OffsetFlag offset_flag) const {
|
||||
DisallowHeapAllocation no_allocation;
|
||||
|
||||
// For wasm, we do not rely on the line_ends array, but do the translation
|
||||
// directly.
|
||||
// For wasm, we use the byte offset as the column.
|
||||
if (type() == Script::TYPE_WASM) {
|
||||
DCHECK_LE(0, position);
|
||||
wasm::NativeModule* native_module = wasm_native_module();
|
||||
const wasm::WasmModule* module = native_module->module();
|
||||
if (source_mapping_url().IsString()) {
|
||||
if (module->functions.size() == 0) return false;
|
||||
info->line = 0;
|
||||
info->column = position;
|
||||
info->line_start = module->functions[0].code.offset();
|
||||
info->line_end = module->functions.back().code.end_offset();
|
||||
return true;
|
||||
}
|
||||
int func_index = GetContainingWasmFunction(module, position);
|
||||
if (func_index < 0) return false;
|
||||
|
||||
const wasm::WasmFunction& function = module->functions[func_index];
|
||||
|
||||
info->line = func_index;
|
||||
info->column = position - function.code.offset();
|
||||
info->line_start = function.code.offset();
|
||||
info->line_end = function.code.end_offset();
|
||||
if (module->functions.size() == 0) return false;
|
||||
info->line = 0;
|
||||
info->column = position;
|
||||
info->line_start = module->functions[0].code.offset();
|
||||
info->line_end = module->functions.back().code.end_offset();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -27,6 +27,7 @@ CAST_ACCESSOR(StackFrameInfo)
|
||||
SMI_ACCESSORS(StackFrameInfo, line_number, kLineNumberOffset)
|
||||
SMI_ACCESSORS(StackFrameInfo, column_number, kColumnNumberOffset)
|
||||
SMI_ACCESSORS(StackFrameInfo, script_id, kScriptIdOffset)
|
||||
SMI_ACCESSORS(StackFrameInfo, wasm_function_index, kWasmFunctionIndexOffset)
|
||||
SMI_ACCESSORS(StackFrameInfo, promise_all_index, kPromiseAllIndexOffset)
|
||||
SMI_ACCESSORS_CHECKED(StackFrameInfo, function_offset, kPromiseAllIndexOffset,
|
||||
is_wasm())
|
||||
|
@ -57,6 +57,11 @@ int StackTraceFrame::GetFunctionOffset(Handle<StackTraceFrame> frame) {
|
||||
return GetFrameInfo(frame)->function_offset();
|
||||
}
|
||||
|
||||
// static
|
||||
int StackTraceFrame::GetWasmFunctionIndex(Handle<StackTraceFrame> frame) {
|
||||
return GetFrameInfo(frame)->wasm_function_index();
|
||||
}
|
||||
|
||||
// static
|
||||
Handle<Object> StackTraceFrame::GetFileName(Handle<StackTraceFrame> frame) {
|
||||
auto name = GetFrameInfo(frame)->script_name();
|
||||
@ -381,7 +386,7 @@ void SerializeWasmStackFrame(Isolate* isolate, Handle<StackTraceFrame> frame,
|
||||
builder->AppendCString(" (");
|
||||
}
|
||||
|
||||
const int wasm_func_index = StackTraceFrame::GetLineNumber(frame);
|
||||
const int wasm_func_index = StackTraceFrame::GetWasmFunctionIndex(frame);
|
||||
|
||||
builder->AppendCString("wasm-function[");
|
||||
builder->AppendInt(wasm_func_index);
|
||||
|
@ -22,6 +22,7 @@ class StackFrameInfo : public Struct {
|
||||
DECL_INT_ACCESSORS(line_number)
|
||||
DECL_INT_ACCESSORS(column_number)
|
||||
DECL_INT_ACCESSORS(script_id)
|
||||
DECL_INT_ACCESSORS(wasm_function_index)
|
||||
DECL_INT_ACCESSORS(promise_all_index)
|
||||
// Wasm frames only: function_offset instead of promise_all_index.
|
||||
DECL_INT_ACCESSORS(function_offset)
|
||||
@ -88,6 +89,7 @@ class StackTraceFrame
|
||||
static int GetScriptId(Handle<StackTraceFrame> frame);
|
||||
static int GetPromiseAllIndex(Handle<StackTraceFrame> frame);
|
||||
static int GetFunctionOffset(Handle<StackTraceFrame> frame);
|
||||
static int GetWasmFunctionIndex(Handle<StackTraceFrame> frame);
|
||||
|
||||
static Handle<Object> GetFileName(Handle<StackTraceFrame> frame);
|
||||
static Handle<Object> GetScriptNameOrSourceUrl(Handle<StackTraceFrame> frame);
|
||||
|
@ -781,9 +781,7 @@ struct implement<Ref> {
|
||||
using type = RefImpl<Ref, i::JSReceiver>;
|
||||
};
|
||||
|
||||
Ref::~Ref() {
|
||||
delete impl(this);
|
||||
}
|
||||
Ref::~Ref() { delete impl(this); }
|
||||
|
||||
void Ref::operator delete(void* p) {}
|
||||
|
||||
@ -895,7 +893,7 @@ own<Frame> CreateFrameFromInternal(i::Handle<i::FixedArray> frames, int index,
|
||||
isolate);
|
||||
i::Handle<i::WasmInstanceObject> instance =
|
||||
i::StackTraceFrame::GetWasmInstance(frame);
|
||||
uint32_t func_index = i::StackTraceFrame::GetLineNumber(frame);
|
||||
uint32_t func_index = i::StackTraceFrame::GetWasmFunctionIndex(frame);
|
||||
size_t func_offset = i::StackTraceFrame::GetFunctionOffset(frame);
|
||||
size_t module_offset = i::StackTraceFrame::GetColumnNumber(frame);
|
||||
return own<Frame>(seal<Frame>(new (std::nothrow) FrameImpl(
|
||||
@ -1977,7 +1975,7 @@ own<Instance> Instance::make(Store* store_abs, const Module* module_abs,
|
||||
if (thrower.error()) {
|
||||
*trap = implement<Trap>::type::make(
|
||||
store, GetProperException(isolate, thrower.Reify()));
|
||||
DCHECK(!thrower.error()); // Reify() called Reset().
|
||||
DCHECK(!thrower.error()); // Reify() called Reset().
|
||||
DCHECK(!isolate->has_pending_exception()); // Hasn't been thrown yet.
|
||||
return own<Instance>();
|
||||
} else if (isolate->has_pending_exception()) {
|
||||
|
@ -916,52 +916,45 @@ bool WasmScript::GetPossibleBreakpoints(
|
||||
std::vector<v8::debug::BreakLocation>* locations) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
|
||||
const std::vector<wasm::WasmFunction>& functions =
|
||||
native_module->module()->functions;
|
||||
if (start.GetLineNumber() < 0 || start.GetColumnNumber() < 0 ||
|
||||
const wasm::WasmModule* module = native_module->module();
|
||||
const std::vector<wasm::WasmFunction>& functions = module->functions;
|
||||
|
||||
if (start.GetLineNumber() != 0 || start.GetColumnNumber() < 0 ||
|
||||
(!end.IsEmpty() &&
|
||||
(end.GetLineNumber() < 0 || end.GetColumnNumber() < 0)))
|
||||
(end.GetLineNumber() != 0 || end.GetColumnNumber() < 0 ||
|
||||
end.GetColumnNumber() < start.GetColumnNumber())))
|
||||
return false;
|
||||
|
||||
// start_func_index, start_offset and end_func_index is inclusive.
|
||||
// end_offset is exclusive.
|
||||
// start_offset and end_offset are module-relative byte offsets.
|
||||
uint32_t start_func_index = start.GetLineNumber();
|
||||
if (start_func_index >= functions.size()) return false;
|
||||
int start_func_len = functions[start_func_index].code.length();
|
||||
if (start.GetColumnNumber() > start_func_len) return false;
|
||||
uint32_t start_offset =
|
||||
functions[start_func_index].code.offset() + start.GetColumnNumber();
|
||||
uint32_t end_func_index;
|
||||
// We set strict to false because offsets may be between functions.
|
||||
int start_func_index =
|
||||
GetNearestWasmFunction(module, start.GetColumnNumber());
|
||||
if (start_func_index < 0) return false;
|
||||
uint32_t start_offset = start.GetColumnNumber();
|
||||
int end_func_index;
|
||||
uint32_t end_offset;
|
||||
|
||||
if (end.IsEmpty()) {
|
||||
// Default: everything till the end of the Script.
|
||||
end_func_index = static_cast<uint32_t>(functions.size() - 1);
|
||||
end_offset = functions[end_func_index].code.end_offset();
|
||||
} else {
|
||||
// If end is specified: Use it and check for valid input.
|
||||
end_func_index = static_cast<uint32_t>(end.GetLineNumber());
|
||||
|
||||
// Special case: Stop before the start of the next function. Change to: Stop
|
||||
// at the end of the function before, such that we don't disassemble the
|
||||
// next function also.
|
||||
if (end.GetColumnNumber() == 0 && end_func_index > 0) {
|
||||
--end_func_index;
|
||||
end_offset = functions[end_func_index].code.end_offset();
|
||||
} else {
|
||||
if (end_func_index >= functions.size()) return false;
|
||||
end_offset =
|
||||
functions[end_func_index].code.offset() + end.GetColumnNumber();
|
||||
if (end_offset > functions[end_func_index].code.end_offset())
|
||||
return false;
|
||||
}
|
||||
end_offset = end.GetColumnNumber();
|
||||
end_func_index = GetNearestWasmFunction(module, end_offset);
|
||||
DCHECK_GE(end_func_index, start_func_index);
|
||||
}
|
||||
|
||||
if (start_func_index == end_func_index &&
|
||||
start_offset > functions[end_func_index].code.end_offset())
|
||||
return false;
|
||||
AccountingAllocator alloc;
|
||||
Zone tmp(&alloc, ZONE_NAME);
|
||||
const byte* module_start = native_module->wire_bytes().begin();
|
||||
|
||||
for (uint32_t func_idx = start_func_index; func_idx <= end_func_index;
|
||||
for (int func_idx = start_func_index; func_idx <= end_func_index;
|
||||
++func_idx) {
|
||||
const wasm::WasmFunction& func = functions[func_idx];
|
||||
if (func.code.length() == 0) continue;
|
||||
@ -978,7 +971,7 @@ bool WasmScript::GetPossibleBreakpoints(
|
||||
break;
|
||||
}
|
||||
if (total_offset < start_offset) continue;
|
||||
locations->emplace_back(func_idx, offset, debug::kCommonBreakLocation);
|
||||
locations->emplace_back(0, total_offset, debug::kCommonBreakLocation);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
@ -68,13 +68,13 @@ int GetWasmFunctionOffset(const WasmModule* module, uint32_t func_index) {
|
||||
}
|
||||
|
||||
// static
|
||||
int GetContainingWasmFunction(const WasmModule* module, uint32_t byte_offset) {
|
||||
int GetNearestWasmFunction(const WasmModule* module, uint32_t byte_offset) {
|
||||
const std::vector<WasmFunction>& functions = module->functions;
|
||||
|
||||
// Binary search for a function containing the given position.
|
||||
int left = 0; // inclusive
|
||||
int right = static_cast<int>(functions.size()); // exclusive
|
||||
if (right == 0) return false;
|
||||
if (right == 0) return -1;
|
||||
while (right - left > 1) {
|
||||
int mid = left + (right - left) / 2;
|
||||
if (functions[mid].code.offset() <= byte_offset) {
|
||||
@ -83,16 +83,25 @@ int GetContainingWasmFunction(const WasmModule* module, uint32_t byte_offset) {
|
||||
right = mid;
|
||||
}
|
||||
}
|
||||
// If the found function does not contains the given position, return -1.
|
||||
const WasmFunction& func = functions[left];
|
||||
if (byte_offset < func.code.offset() ||
|
||||
byte_offset >= func.code.end_offset()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return left;
|
||||
}
|
||||
|
||||
// static
|
||||
int GetContainingWasmFunction(const WasmModule* module, uint32_t byte_offset) {
|
||||
int func_index = GetNearestWasmFunction(module, byte_offset);
|
||||
|
||||
if (func_index >= 0) {
|
||||
// If the found function does not contain the given position, return -1.
|
||||
const WasmFunction& func = module->functions[func_index];
|
||||
if (byte_offset < func.code.offset() ||
|
||||
byte_offset >= func.code.end_offset()) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return func_index;
|
||||
}
|
||||
|
||||
// static
|
||||
v8::debug::WasmDisassembly DisassembleWasmFunction(
|
||||
const WasmModule* module, const ModuleWireBytes& wire_bytes,
|
||||
|
@ -250,10 +250,15 @@ int GetExportWrapperIndex(const WasmModule* module, const FunctionSig* sig,
|
||||
int GetWasmFunctionOffset(const WasmModule* module, uint32_t func_index);
|
||||
|
||||
// Returns the function containing the given byte offset.
|
||||
// Returns -1 if the byte offset is not contained in any function of this
|
||||
// module.
|
||||
// Returns -1 if the byte offset is not contained in any
|
||||
// function of this module.
|
||||
int GetContainingWasmFunction(const WasmModule* module, uint32_t byte_offset);
|
||||
|
||||
// Returns the function containing the given byte offset.
|
||||
// Will return preceding function if the byte offset is not
|
||||
// contained within a function.
|
||||
int GetNearestWasmFunction(const WasmModule* module, uint32_t byte_offset);
|
||||
|
||||
// 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.
|
||||
|
@ -21,12 +21,29 @@ namespace wasm {
|
||||
|
||||
namespace {
|
||||
|
||||
debug::Location TranslateLocation(WasmRunnerBase* runner,
|
||||
const debug::Location& loc) {
|
||||
// Convert locations from {func_index, offset_in_func} to
|
||||
// {0, offset_in_module}.
|
||||
int func_index = loc.GetLineNumber();
|
||||
int func_offset = runner->builder().GetFunctionAt(func_index)->code.offset();
|
||||
int offset = loc.GetColumnNumber() + func_offset;
|
||||
return {0, offset};
|
||||
}
|
||||
|
||||
void CheckLocations(
|
||||
NativeModule* native_module, debug::Location start, debug::Location end,
|
||||
WasmRunnerBase* runner, NativeModule* native_module, debug::Location start,
|
||||
debug::Location end,
|
||||
std::initializer_list<debug::Location> expected_locations_init) {
|
||||
std::vector<debug::BreakLocation> locations;
|
||||
bool success =
|
||||
WasmScript::GetPossibleBreakpoints(native_module, start, end, &locations);
|
||||
std::vector<debug::Location> expected_locations;
|
||||
for (auto loc : expected_locations_init) {
|
||||
expected_locations.push_back(TranslateLocation(runner, loc));
|
||||
}
|
||||
|
||||
bool success = WasmScript::GetPossibleBreakpoints(
|
||||
native_module, TranslateLocation(runner, start),
|
||||
TranslateLocation(runner, end), &locations);
|
||||
CHECK(success);
|
||||
|
||||
printf("got %d locations: ", static_cast<int>(locations.size()));
|
||||
@ -36,7 +53,6 @@ void CheckLocations(
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
std::vector<debug::Location> expected_locations(expected_locations_init);
|
||||
CHECK_EQ(expected_locations.size(), locations.size());
|
||||
for (size_t i = 0, e = locations.size(); i != e; ++i) {
|
||||
CHECK_EQ(expected_locations[i].GetLineNumber(),
|
||||
@ -46,11 +62,12 @@ void CheckLocations(
|
||||
}
|
||||
}
|
||||
|
||||
void CheckLocationsFail(NativeModule* native_module, debug::Location start,
|
||||
debug::Location end) {
|
||||
void CheckLocationsFail(WasmRunnerBase* runner, NativeModule* native_module,
|
||||
debug::Location start, debug::Location end) {
|
||||
std::vector<debug::BreakLocation> locations;
|
||||
bool success =
|
||||
WasmScript::GetPossibleBreakpoints(native_module, start, end, &locations);
|
||||
bool success = WasmScript::GetPossibleBreakpoints(
|
||||
native_module, TranslateLocation(runner, start),
|
||||
TranslateLocation(runner, end), &locations);
|
||||
CHECK(!success);
|
||||
}
|
||||
|
||||
@ -275,21 +292,21 @@ WASM_COMPILED_EXEC_TEST(WasmCollectPossibleBreakpoints) {
|
||||
|
||||
std::vector<debug::Location> locations;
|
||||
// Check all locations for function 0.
|
||||
CheckLocations(native_module, {0, 0}, {1, 0},
|
||||
CheckLocations(&runner, native_module, {0, 0}, {0, 10},
|
||||
{{0, 1}, {0, 2}, {0, 4}, {0, 6}, {0, 7}});
|
||||
// Check a range ending at an instruction.
|
||||
CheckLocations(native_module, {0, 2}, {0, 4}, {{0, 2}});
|
||||
CheckLocations(&runner, native_module, {0, 2}, {0, 4}, {{0, 2}});
|
||||
// Check a range ending one behind an instruction.
|
||||
CheckLocations(native_module, {0, 2}, {0, 5}, {{0, 2}, {0, 4}});
|
||||
CheckLocations(&runner, native_module, {0, 2}, {0, 5}, {{0, 2}, {0, 4}});
|
||||
// Check a range starting at an instruction.
|
||||
CheckLocations(native_module, {0, 7}, {0, 8}, {{0, 7}});
|
||||
CheckLocations(&runner, native_module, {0, 7}, {0, 8}, {{0, 7}});
|
||||
// Check from an instruction to beginning of next function.
|
||||
CheckLocations(native_module, {0, 7}, {1, 0}, {{0, 7}});
|
||||
CheckLocations(&runner, native_module, {0, 7}, {0, 10}, {{0, 7}});
|
||||
// Check from end of one function (no valid instruction position) to beginning
|
||||
// of next function. Must be empty, but not fail.
|
||||
CheckLocations(native_module, {0, 8}, {1, 0}, {});
|
||||
CheckLocations(&runner, native_module, {0, 8}, {0, 10}, {});
|
||||
// Check from one after the end of the function. Must fail.
|
||||
CheckLocationsFail(native_module, {0, 9}, {1, 0});
|
||||
CheckLocationsFail(&runner, native_module, {0, 9}, {0, 10});
|
||||
}
|
||||
|
||||
WASM_COMPILED_EXEC_TEST(WasmSimpleBreak) {
|
||||
|
@ -140,13 +140,12 @@ WASM_EXEC_TEST(CollectDetailedWasmStack_ExplicitThrowFromJs) {
|
||||
Execution::MessageHandling::kReport, &maybe_exc);
|
||||
CHECK(returnObjMaybe.is_null());
|
||||
|
||||
// Line and column are 1-based, so add 1 for the expected wasm output.
|
||||
ExceptionInfo expected_exceptions[] = {
|
||||
{"a", 3, 8}, // -
|
||||
{"js", 4, 2}, // -
|
||||
{"main", static_cast<int>(wasm_index_1) + 1, 8}, // -
|
||||
{"call_main", static_cast<int>(wasm_index_2) + 1, 21}, // -
|
||||
{"callFn", 1, 24} // -
|
||||
{"a", 3, 8}, // -
|
||||
{"js", 4, 2}, // -
|
||||
{"main", 1, 8}, // -
|
||||
{"call_main", 1, 21}, // -
|
||||
{"callFn", 1, 24} // -
|
||||
};
|
||||
CheckExceptionInfos(isolate, maybe_exc.ToHandleChecked(),
|
||||
expected_exceptions);
|
||||
@ -196,15 +195,15 @@ WASM_EXEC_TEST(CollectDetailedWasmStack_WasmError) {
|
||||
const int call_main_offset =
|
||||
r.builder().GetFunctionAt(wasm_index_2)->code.offset();
|
||||
|
||||
// Line and column are 1-based, so add 1 for the expected wasm output.
|
||||
// Column is 1-based, so add 1 for the expected wasm output. Line number
|
||||
// is always 1.
|
||||
const int expected_main_pos =
|
||||
unreachable_pos + main_offset + kMainLocalsLength + 1;
|
||||
const int expected_call_main_pos = call_main_offset + kMainLocalsLength + 1;
|
||||
ExceptionInfo expected_exceptions[] = {
|
||||
{"main", static_cast<int>(wasm_index_1) + 1, expected_main_pos}, // -
|
||||
{"call_main", static_cast<int>(wasm_index_2) + 1,
|
||||
expected_call_main_pos}, // -
|
||||
{"callFn", 1, 24} //-
|
||||
{"main", 1, expected_main_pos}, // -
|
||||
{"call_main", 1, expected_call_main_pos}, // -
|
||||
{"callFn", 1, 24} //-
|
||||
};
|
||||
CheckExceptionInfos(isolate, exception, expected_exceptions);
|
||||
}
|
||||
|
@ -91,10 +91,9 @@ WASM_EXEC_TEST(Unreachable) {
|
||||
Execution::MessageHandling::kReport, &maybe_exc);
|
||||
CHECK(returnObjMaybe.is_null());
|
||||
|
||||
// Line and column are 1-based, so add 1 for the expected wasm output.
|
||||
ExceptionInfo expected_exceptions[] = {
|
||||
{"main", static_cast<int>(wasm_index) + 1, 7}, // --
|
||||
{"callFn", 1, 24} // --
|
||||
{"main", 1, 7}, // --
|
||||
{"callFn", 1, 24} // --
|
||||
};
|
||||
CheckExceptionInfos(isolate, maybe_exc.ToHandleChecked(),
|
||||
expected_exceptions);
|
||||
@ -134,11 +133,10 @@ WASM_EXEC_TEST(IllegalLoad) {
|
||||
Execution::MessageHandling::kReport, &maybe_exc);
|
||||
CHECK(returnObjMaybe.is_null());
|
||||
|
||||
// Line and column are 1-based, so add 1 for the expected wasm output.
|
||||
ExceptionInfo expected_exceptions[] = {
|
||||
{"main", static_cast<int>(wasm_index_1) + 1, 13}, // --
|
||||
{"call_main", static_cast<int>(wasm_index_2) + 1, 30}, // --
|
||||
{"callFn", 1, 24} // --
|
||||
{"main", 1, 13}, // --
|
||||
{"call_main", 1, 30}, // --
|
||||
{"callFn", 1, 24} // --
|
||||
};
|
||||
CheckExceptionInfos(isolate, maybe_exc.ToHandleChecked(),
|
||||
expected_exceptions);
|
||||
|
@ -1,4 +1,4 @@
|
||||
wasm-function[0]:5: RuntimeError: wasm exception
|
||||
wasm-function[0]:0x32: RuntimeError: wasm exception
|
||||
RuntimeError: wasm exception
|
||||
at rethrow0 (wasm-function[0]:0x32)
|
||||
at *%(basename)s:21:18
|
||||
|
@ -1,4 +1,4 @@
|
||||
wasm-function[0]:3: RuntimeError: wasm exception
|
||||
wasm-function[0]:0x2e: RuntimeError: wasm exception
|
||||
RuntimeError: wasm exception
|
||||
at throw0 (wasm-function[0]:0x2e)
|
||||
at *%(basename)s:17:18
|
||||
|
@ -1,5 +1,4 @@
|
||||
wasm-function[0]:1: RuntimeError: unreachable
|
||||
wasm-function[0]:0x22: RuntimeError: unreachable
|
||||
RuntimeError: unreachable
|
||||
at main (wasm-function[0]:0x22)
|
||||
at *%(basename)s:{NUMBER}:31
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
wasm-function[0]:1: RuntimeError: unreachable
|
||||
wasm-function[0]:0x22: RuntimeError: unreachable
|
||||
RuntimeError: unreachable
|
||||
at test-module.main (wasm-function[0]:0x22)
|
||||
at *%(basename)s:{NUMBER}:31
|
||||
|
@ -1,5 +1,4 @@
|
||||
wasm-function[0]:1: RuntimeError: unreachable
|
||||
wasm-function[0]:0x22: RuntimeError: unreachable
|
||||
RuntimeError: unreachable
|
||||
at test-module (wasm-function[0]:0x22)
|
||||
at *%(basename)s:{NUMBER}:31
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
wasm-function[0]:1: RuntimeError: unreachable
|
||||
wasm-function[0]:0x22: RuntimeError: unreachable
|
||||
RuntimeError: unreachable
|
||||
at wasm-function[0]:0x22
|
||||
at *%(basename)s:{NUMBER}:31
|
||||
|
||||
|
@ -7,6 +7,9 @@
|
||||
load("test/mjsunit/wasm/wasm-module-builder.js");
|
||||
|
||||
var builder = new WasmModuleBuilder();
|
||||
// Add a dummy function to make the main function index 1.
|
||||
builder.addFunction('dummy', kSig_i_v)
|
||||
.addBody([kExprI32Const, 0]);
|
||||
builder.addFunction('main', kSig_i_v)
|
||||
.addBody([kExprI32Const, 2, kExprI32Const, 0, kExprI32DivU])
|
||||
.exportFunc();
|
||||
|
@ -1,5 +1,4 @@
|
||||
wasm-function[0]:5: RuntimeError: divide by zero
|
||||
wasm-function[1]:0x30: RuntimeError: divide by zero
|
||||
RuntimeError: divide by zero
|
||||
at main (wasm-function[0]:0x26)
|
||||
at main (wasm-function[1]:0x30)
|
||||
at *%(basename)s:{NUMBER}:16
|
||||
|
||||
|
@ -23,7 +23,7 @@ function verifyStack(frames, expected) {
|
||||
assertContains(exp[4], frames[i].getFileName(), "["+i+"].getFileName()");
|
||||
var toString;
|
||||
if (exp[0]) {
|
||||
toString = "wasm-function[" + exp[2] + "]:" + exp[5];
|
||||
toString = "wasm-function[" + exp[6] + "]:" + exp[5];
|
||||
if (exp[1] !== null) toString = exp[1] + " (" + toString + ")";
|
||||
} else {
|
||||
toString = exp[4] + ":" + exp[2] + ":";
|
||||
@ -88,9 +88,9 @@ Error.prepareStackTrace = function(error, frames) {
|
||||
module.exports.main();
|
||||
|
||||
verifyStack(stack, [
|
||||
// isWasm function line pos file offset
|
||||
// isWasm function line pos file offset funcIndex
|
||||
[ false, "STACK", 38, 0, "stack.js"],
|
||||
[ true, "main", 1, 1, null, '0x86'],
|
||||
[ true, "main", 0, 1, null, '0x86', 1],
|
||||
[ false, "testStackFrames", 88, 0, "stack.js"],
|
||||
[ false, null, 97, 0, "stack.js"]
|
||||
]);
|
||||
@ -103,8 +103,8 @@ Error.prepareStackTrace = function(error, frames) {
|
||||
} catch (e) {
|
||||
assertContains("unreachable", e.message);
|
||||
verifyStack(e.stack, [
|
||||
// isWasm function line pos file offset
|
||||
[ true, "exec_unreachable", 2, 1, null, '0x8b'],
|
||||
// isWasm function line pos file offset funcIndex
|
||||
[ true, "exec_unreachable", 0, 1, null, '0x8b', 2],
|
||||
[ false, "testWasmUnreachable", 101, 0, "stack.js"],
|
||||
[ false, null, 112, 0, "stack.js"]
|
||||
]);
|
||||
@ -118,9 +118,9 @@ Error.prepareStackTrace = function(error, frames) {
|
||||
} catch (e) {
|
||||
assertContains("out of bounds", e.message);
|
||||
verifyStack(e.stack, [
|
||||
// isWasm function line pos file offset
|
||||
[ true, null, 3, 3, null, '0x91'],
|
||||
[ true, "call_mem_out_of_bounds", 4, 1, null, '0x97'],
|
||||
// isWasm function line pos file offset funcIndex
|
||||
[ true, null, 0, 3, null, '0x91', 3],
|
||||
[ true, "call_mem_out_of_bounds", 0, 1, null, '0x97', 4],
|
||||
[ false, "testWasmMemOutOfBounds", 116, 0, "stack.js"],
|
||||
[ false, null, 128, 0, "stack.js"]
|
||||
]);
|
||||
@ -147,11 +147,11 @@ Error.prepareStackTrace = function(error, frames) {
|
||||
assertEquals("Maximum call stack size exceeded", e.message, "trap reason");
|
||||
assertTrue(e.stack.length >= 4, "expected at least 4 stack entries");
|
||||
verifyStack(e.stack.splice(0, 4), [
|
||||
// isWasm function line pos file offset
|
||||
[ true, "recursion", 0, 0, null, '0x34'],
|
||||
[ true, "recursion", 0, 3, null, '0x37'],
|
||||
[ true, "recursion", 0, 3, null, '0x37'],
|
||||
[ true, "recursion", 0, 3, null, '0x37']
|
||||
// isWasm function line pos file offset funcIndex
|
||||
[ true, "recursion", 0, 0, null, '0x34', 0],
|
||||
[ true, "recursion", 0, 3, null, '0x37', 0],
|
||||
[ true, "recursion", 0, 3, null, '0x37', 0],
|
||||
[ true, "recursion", 0, 3, null, '0x37', 0]
|
||||
]);
|
||||
}
|
||||
})();
|
||||
@ -175,10 +175,10 @@ Error.prepareStackTrace = function(error, frames) {
|
||||
assertEquals('unreachable', e.message, 'trap reason');
|
||||
let hexOffset = '0x' + (unreachable_pos + 0x25).toString(16);
|
||||
verifyStack(e.stack, [
|
||||
// isWasm, function, line, pos, file, offset
|
||||
[true, 'main', 0, unreachable_pos + 1, null, hexOffset], // -
|
||||
[false, 'testBigOffset', 172, 0, 'stack.js'], //-
|
||||
[false, null, 184, 0, 'stack.js']
|
||||
// isWasm function line pos file offset funcIndex
|
||||
[ true, 'main', 0, unreachable_pos + 1, null, hexOffset, 0],
|
||||
[ false, 'testBigOffset', 172, 0, 'stack.js'],
|
||||
[ false, null, 184, 0, 'stack.js']
|
||||
]);
|
||||
}
|
||||
})();
|
||||
|
Loading…
Reference in New Issue
Block a user