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:
Eric Leese 2019-11-05 14:15:28 +01:00 committed by Commit Bot
parent 4fa5f2b504
commit 5c23e6b5f2
31 changed files with 234 additions and 152 deletions

View File

@ -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;
};

View File

@ -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);

View File

@ -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;

View File

@ -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).

View File

@ -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);

View File

@ -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();
}

View File

@ -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(); }

View File

@ -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);

View File

@ -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);

View File

@ -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);
int found_byte_offset = 0;
// 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;
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.
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));

View File

@ -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; }

View File

@ -4662,13 +4662,11 @@ 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;
@ -4676,17 +4674,6 @@ bool Script::GetPositionInfo(int position, PositionInfo* info,
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();
return true;
}
if (line_ends().IsUndefined()) {
// Slow mode: we do not have line_ends. We have to iterate through source.

View File

@ -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())

View File

@ -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);

View File

@ -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);

View File

@ -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(

View File

@ -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());
end_offset = end.GetColumnNumber();
end_func_index = GetNearestWasmFunction(module, end_offset);
DCHECK_GE(end_func_index, start_func_index);
}
// 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())
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;

View File

@ -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,14 +83,23 @@ 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];
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 left;
}
return func_index;
}
// static

View File

@ -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.

View File

@ -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) {

View File

@ -140,12 +140,11 @@ 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}, // -
{"main", 1, 8}, // -
{"call_main", 1, 21}, // -
{"callFn", 1, 24} // -
};
CheckExceptionInfos(isolate, maybe_exc.ToHandleChecked(),
@ -196,14 +195,14 @@ 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}, // -
{"main", 1, expected_main_pos}, // -
{"call_main", 1, expected_call_main_pos}, // -
{"callFn", 1, 24} //-
};
CheckExceptionInfos(isolate, exception, expected_exceptions);

View File

@ -91,9 +91,8 @@ 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}, // --
{"main", 1, 7}, // --
{"callFn", 1, 24} // --
};
CheckExceptionInfos(isolate, maybe_exc.ToHandleChecked(),
@ -134,10 +133,9 @@ 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}, // --
{"main", 1, 13}, // --
{"call_main", 1, 30}, // --
{"callFn", 1, 24} // --
};
CheckExceptionInfos(isolate, maybe_exc.ToHandleChecked(),

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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();

View File

@ -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

View File

@ -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']
]);
}
})();