Commit 1fd8f2ec authored by Michael Starzinger's avatar Michael Starzinger Committed by Commit Bot

[wasm] Move breakpoint functions onto {WasmScript}.

R=clemensb@chromium.org
BUG=v8:6847,chromium:893069

Change-Id: I5b5ada546e1d0b9d42ea8f7278671bf2b128bef8
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1862570Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarClemens Backes <clemensb@chromium.org>
Commit-Queue: Michael Starzinger <mstarzinger@chromium.org>
Cr-Commit-Position: refs/heads/master@{#64308}
parent 0d7889d0
......@@ -9265,8 +9265,8 @@ bool debug::Script::GetPossibleBreakpoints(
if (script->type() == i::Script::TYPE_WASM &&
this->SourceMappingURL().IsEmpty()) {
i::wasm::NativeModule* native_module = script->wasm_native_module();
return i::WasmModuleObject::GetPossibleBreakpoints(native_module, start,
end, locations);
return i::WasmScript::GetPossibleBreakpoints(native_module, start, end,
locations);
}
i::Script::InitLineEnds(script);
......
......@@ -622,8 +622,7 @@ bool Debug::SetBreakPointForScript(Handle<Script> script,
Handle<BreakPoint> break_point =
isolate_->factory()->NewBreakPoint(*id, condition);
if (script->type() == Script::TYPE_WASM) {
return WasmModuleObject::SetBreakPoint(script, source_position,
break_point);
return WasmScript::SetBreakPoint(script, source_position, break_point);
}
HandleScope scope(isolate_);
......
......@@ -536,7 +536,7 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
// Debugging support.
//--------------------------------------------------------------------------
// Set all breakpoints that were set on the shared module.
WasmModuleObject::SetBreakpointsOnNewInstance(
WasmScript::SetBreakpointsOnNewInstance(
handle(module_object_->script(), isolate_), instance);
//--------------------------------------------------------------------------
......
......@@ -280,7 +280,7 @@ class InterpreterHandle {
Handle<Script> script(module_object->script(), isolate_);
int position = GetTopPosition(module_object);
Handle<FixedArray> breakpoints;
if (WasmModuleObject::CheckBreakPoints(isolate_, script, position)
if (WasmScript::CheckBreakPoints(isolate_, script, position)
.ToHandle(&breakpoints)) {
// We hit one or several breakpoints. Clear stepping, notify the
// listeners and return.
......@@ -664,5 +664,359 @@ Handle<Code> WasmDebugInfo::GetCWasmEntry(Handle<WasmDebugInfo> debug_info,
return handle(Code::cast(entries->get(index)), isolate);
}
namespace {
#ifdef DEBUG
bool IsBreakablePosition(wasm::NativeModule* native_module, int func_index,
int offset_in_func) {
AccountingAllocator alloc;
Zone tmp(&alloc, ZONE_NAME);
wasm::BodyLocalDecls locals(&tmp);
const byte* module_start = native_module->wire_bytes().begin();
const wasm::WasmFunction& func =
native_module->module()->functions[func_index];
wasm::BytecodeIterator iterator(module_start + func.code.offset(),
module_start + func.code.end_offset(),
&locals);
DCHECK_LT(0, locals.encoded_size);
for (uint32_t offset : iterator.offsets()) {
if (offset > static_cast<uint32_t>(offset_in_func)) break;
if (offset == static_cast<uint32_t>(offset_in_func)) return true;
}
return false;
}
#endif // DEBUG
} // namespace
// static
bool WasmScript::SetBreakPoint(Handle<Script> script, int* position,
Handle<BreakPoint> break_point) {
Isolate* isolate = script->GetIsolate();
// Find the function for this breakpoint.
const wasm::WasmModule* module = script->wasm_native_module()->module();
int func_index = GetContainingWasmFunction(module, *position);
if (func_index < 0) return false;
const wasm::WasmFunction& func = module->functions[func_index];
int offset_in_func = *position - func.code.offset();
// According to the current design, we should only be called with valid
// breakable positions.
DCHECK(IsBreakablePosition(script->wasm_native_module(), func_index,
offset_in_func));
// Insert new break point into break_positions of module object.
WasmScript::AddBreakpointToInfo(script, *position, break_point);
// Iterate over all instances and tell them to set this new breakpoint.
// We do this using the weak list of all instances from the script.
Handle<WeakArrayList> weak_instance_list(script->wasm_weak_instance_list(),
isolate);
for (int i = 0; i < weak_instance_list->length(); ++i) {
MaybeObject maybe_instance = weak_instance_list->Get(i);
if (maybe_instance->IsWeak()) {
Handle<WasmInstanceObject> instance(
WasmInstanceObject::cast(maybe_instance->GetHeapObjectAssumeWeak()),
isolate);
Handle<WasmDebugInfo> debug_info =
WasmInstanceObject::GetOrCreateDebugInfo(instance);
WasmDebugInfo::SetBreakpoint(debug_info, func_index, offset_in_func);
}
}
return true;
}
// static
bool WasmScript::ClearBreakPoint(Handle<Script> script, int position,
Handle<BreakPoint> break_point) {
Isolate* isolate = script->GetIsolate();
// Find the function for this breakpoint.
const wasm::WasmModule* module = script->wasm_native_module()->module();
int func_index = GetContainingWasmFunction(module, position);
if (func_index < 0) return false;
const wasm::WasmFunction& func = module->functions[func_index];
int offset_in_func = position - func.code.offset();
if (!WasmScript::RemoveBreakpointFromInfo(script, position, break_point)) {
return false;
}
// Iterate over all instances and tell them to remove this breakpoint.
// We do this using the weak list of all instances from the script.
Handle<WeakArrayList> weak_instance_list(script->wasm_weak_instance_list(),
isolate);
for (int i = 0; i < weak_instance_list->length(); ++i) {
MaybeObject maybe_instance = weak_instance_list->Get(i);
if (maybe_instance->IsWeak()) {
Handle<WasmInstanceObject> instance(
WasmInstanceObject::cast(maybe_instance->GetHeapObjectAssumeWeak()),
isolate);
Handle<WasmDebugInfo> debug_info =
WasmInstanceObject::GetOrCreateDebugInfo(instance);
WasmDebugInfo::ClearBreakpoint(debug_info, func_index, offset_in_func);
}
}
return true;
}
namespace {
int GetBreakpointPos(Isolate* isolate, Object break_point_info_or_undef) {
if (break_point_info_or_undef.IsUndefined(isolate)) return kMaxInt;
return BreakPointInfo::cast(break_point_info_or_undef).source_position();
}
int FindBreakpointInfoInsertPos(Isolate* isolate,
Handle<FixedArray> breakpoint_infos,
int position) {
// Find insert location via binary search, taking care of undefined values on
// the right. Position is always greater than zero.
DCHECK_LT(0, position);
int left = 0; // inclusive
int right = breakpoint_infos->length(); // exclusive
while (right - left > 1) {
int mid = left + (right - left) / 2;
Object mid_obj = breakpoint_infos->get(mid);
if (GetBreakpointPos(isolate, mid_obj) <= position) {
left = mid;
} else {
right = mid;
}
}
int left_pos = GetBreakpointPos(isolate, breakpoint_infos->get(left));
return left_pos < position ? left + 1 : left;
}
} // namespace
// static
void WasmScript::AddBreakpointToInfo(Handle<Script> script, int position,
Handle<BreakPoint> break_point) {
Isolate* isolate = script->GetIsolate();
Handle<FixedArray> breakpoint_infos;
if (script->has_wasm_breakpoint_infos()) {
breakpoint_infos = handle(script->wasm_breakpoint_infos(), isolate);
} else {
breakpoint_infos =
isolate->factory()->NewFixedArray(4, AllocationType::kOld);
script->set_wasm_breakpoint_infos(*breakpoint_infos);
}
int insert_pos =
FindBreakpointInfoInsertPos(isolate, breakpoint_infos, position);
// If a BreakPointInfo object already exists for this position, add the new
// breakpoint object and return.
if (insert_pos < breakpoint_infos->length() &&
GetBreakpointPos(isolate, breakpoint_infos->get(insert_pos)) ==
position) {
Handle<BreakPointInfo> old_info(
BreakPointInfo::cast(breakpoint_infos->get(insert_pos)), isolate);
BreakPointInfo::SetBreakPoint(isolate, old_info, break_point);
return;
}
// Enlarge break positions array if necessary.
bool need_realloc = !breakpoint_infos->get(breakpoint_infos->length() - 1)
.IsUndefined(isolate);
Handle<FixedArray> new_breakpoint_infos = breakpoint_infos;
if (need_realloc) {
new_breakpoint_infos = isolate->factory()->NewFixedArray(
2 * breakpoint_infos->length(), AllocationType::kOld);
script->set_wasm_breakpoint_infos(*new_breakpoint_infos);
// Copy over the entries [0, insert_pos).
for (int i = 0; i < insert_pos; ++i)
new_breakpoint_infos->set(i, breakpoint_infos->get(i));
}
// Move elements [insert_pos, ...] up by one.
for (int i = breakpoint_infos->length() - 1; i >= insert_pos; --i) {
Object entry = breakpoint_infos->get(i);
if (entry.IsUndefined(isolate)) continue;
new_breakpoint_infos->set(i + 1, entry);
}
// Generate new BreakpointInfo.
Handle<BreakPointInfo> breakpoint_info =
isolate->factory()->NewBreakPointInfo(position);
BreakPointInfo::SetBreakPoint(isolate, breakpoint_info, break_point);
// Now insert new position at insert_pos.
new_breakpoint_infos->set(insert_pos, *breakpoint_info);
}
// static
bool WasmScript::RemoveBreakpointFromInfo(Handle<Script> script, int position,
Handle<BreakPoint> break_point) {
if (!script->has_wasm_breakpoint_infos()) return false;
Isolate* isolate = script->GetIsolate();
Handle<FixedArray> breakpoint_infos(script->wasm_breakpoint_infos(), isolate);
int pos = FindBreakpointInfoInsertPos(isolate, breakpoint_infos, position);
// Does a BreakPointInfo object already exist for this position?
if (pos == breakpoint_infos->length()) return false;
Handle<BreakPointInfo> info(BreakPointInfo::cast(breakpoint_infos->get(pos)),
isolate);
BreakPointInfo::ClearBreakPoint(isolate, info, break_point);
// Check if there are no more breakpoints at this location.
if (info->GetBreakPointCount(isolate) == 0) {
// Update array by moving breakpoints up one position.
for (int i = pos; i < breakpoint_infos->length() - 1; i++) {
Object entry = breakpoint_infos->get(i + 1);
breakpoint_infos->set(i, entry);
if (entry.IsUndefined(isolate)) break;
}
// Make sure last array element is empty as a result.
breakpoint_infos->set_undefined(breakpoint_infos->length() - 1);
}
return true;
}
void WasmScript::SetBreakpointsOnNewInstance(
Handle<Script> script, Handle<WasmInstanceObject> instance) {
if (!script->has_wasm_breakpoint_infos()) return;
Isolate* isolate = script->GetIsolate();
Handle<WasmDebugInfo> debug_info =
WasmInstanceObject::GetOrCreateDebugInfo(instance);
Handle<FixedArray> breakpoint_infos(script->wasm_breakpoint_infos(), isolate);
// If the array exists, it should not be empty.
DCHECK_LT(0, breakpoint_infos->length());
for (int i = 0, e = breakpoint_infos->length(); i < e; ++i) {
Handle<Object> obj(breakpoint_infos->get(i), isolate);
if (obj->IsUndefined(isolate)) {
for (; i < e; ++i) {
DCHECK(breakpoint_infos->get(i).IsUndefined(isolate));
}
break;
}
Handle<BreakPointInfo> breakpoint_info = Handle<BreakPointInfo>::cast(obj);
int position = breakpoint_info->source_position();
// Find the function for this breakpoint, and set the breakpoint.
const wasm::WasmModule* module = script->wasm_native_module()->module();
int func_index = GetContainingWasmFunction(module, position);
DCHECK_LE(0, func_index);
const wasm::WasmFunction& func = module->functions[func_index];
int offset_in_func = position - func.code.offset();
WasmDebugInfo::SetBreakpoint(debug_info, func_index, offset_in_func);
}
}
// static
bool WasmScript::GetPossibleBreakpoints(
wasm::NativeModule* native_module, const v8::debug::Location& start,
const v8::debug::Location& end,
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 ||
(!end.IsEmpty() &&
(end.GetLineNumber() < 0 || end.GetColumnNumber() < 0)))
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;
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;
}
}
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;
++func_idx) {
const wasm::WasmFunction& func = functions[func_idx];
if (func.code.length() == 0) continue;
wasm::BodyLocalDecls locals(&tmp);
wasm::BytecodeIterator iterator(module_start + func.code.offset(),
module_start + func.code.end_offset(),
&locals);
DCHECK_LT(0u, locals.encoded_size);
for (uint32_t offset : iterator.offsets()) {
uint32_t total_offset = func.code.offset() + offset;
if (total_offset >= end_offset) {
DCHECK_EQ(end_func_index, func_idx);
break;
}
if (total_offset < start_offset) continue;
locations->emplace_back(func_idx, offset, debug::kCommonBreakLocation);
}
}
return true;
}
// static
MaybeHandle<FixedArray> WasmScript::CheckBreakPoints(Isolate* isolate,
Handle<Script> script,
int position) {
if (!script->has_wasm_breakpoint_infos()) return {};
Handle<FixedArray> breakpoint_infos(script->wasm_breakpoint_infos(), isolate);
int insert_pos =
FindBreakpointInfoInsertPos(isolate, breakpoint_infos, position);
if (insert_pos >= breakpoint_infos->length()) return {};
Handle<Object> maybe_breakpoint_info(breakpoint_infos->get(insert_pos),
isolate);
if (maybe_breakpoint_info->IsUndefined(isolate)) return {};
Handle<BreakPointInfo> breakpoint_info =
Handle<BreakPointInfo>::cast(maybe_breakpoint_info);
if (breakpoint_info->source_position() != position) return {};
// There is no support for conditional break points. Just assume that every
// break point always hits.
Handle<Object> break_points(breakpoint_info->break_points(), isolate);
if (break_points->IsFixedArray()) {
return Handle<FixedArray>::cast(break_points);
}
Handle<FixedArray> break_points_hit = isolate->factory()->NewFixedArray(1);
break_points_hit->set(0, *break_points);
return break_points_hit;
}
} // namespace internal
} // namespace v8
......@@ -173,26 +173,6 @@ WasmInstanceNativeAllocations* GetNativeAllocations(
.raw();
}
#ifdef DEBUG
bool IsBreakablePosition(wasm::NativeModule* native_module, int func_index,
int offset_in_func) {
AccountingAllocator alloc;
Zone tmp(&alloc, ZONE_NAME);
wasm::BodyLocalDecls locals(&tmp);
const byte* module_start = native_module->wire_bytes().begin();
const WasmFunction& func = native_module->module()->functions[func_index];
wasm::BytecodeIterator iterator(module_start + func.code.offset(),
module_start + func.code.end_offset(),
&locals);
DCHECK_LT(0, locals.encoded_size);
for (uint32_t offset : iterator.offsets()) {
if (offset > static_cast<uint32_t>(offset_in_func)) break;
if (offset == static_cast<uint32_t>(offset_in_func)) return true;
}
return false;
}
#endif // DEBUG
enum DispatchTableElements : int {
kDispatchTableInstanceOffset,
kDispatchTableIndexOffset,
......@@ -253,231 +233,7 @@ Handle<WasmModuleObject> WasmModuleObject::New(
return module_object;
}
// static
bool WasmModuleObject::SetBreakPoint(Handle<Script> script, int* position,
Handle<BreakPoint> break_point) {
Isolate* isolate = script->GetIsolate();
// Find the function for this breakpoint.
const WasmModule* module = script->wasm_native_module()->module();
int func_index = GetContainingWasmFunction(module, *position);
if (func_index < 0) return false;
const WasmFunction& func = module->functions[func_index];
int offset_in_func = *position - func.code.offset();
// According to the current design, we should only be called with valid
// breakable positions.
DCHECK(IsBreakablePosition(script->wasm_native_module(), func_index,
offset_in_func));
// Insert new break point into break_positions of module object.
WasmModuleObject::AddBreakpointToInfo(script, *position, break_point);
// Iterate over all instances and tell them to set this new breakpoint.
// We do this using the weak list of all instances from the script.
Handle<WeakArrayList> weak_instance_list(script->wasm_weak_instance_list(),
isolate);
for (int i = 0; i < weak_instance_list->length(); ++i) {
MaybeObject maybe_instance = weak_instance_list->Get(i);
if (maybe_instance->IsWeak()) {
Handle<WasmInstanceObject> instance(
WasmInstanceObject::cast(maybe_instance->GetHeapObjectAssumeWeak()),
isolate);
Handle<WasmDebugInfo> debug_info =
WasmInstanceObject::GetOrCreateDebugInfo(instance);
WasmDebugInfo::SetBreakpoint(debug_info, func_index, offset_in_func);
}
}
return true;
}
// static
bool WasmModuleObject::ClearBreakPoint(Handle<Script> script, int position,
Handle<BreakPoint> break_point) {
Isolate* isolate = script->GetIsolate();
// Find the function for this breakpoint.
const WasmModule* module = script->wasm_native_module()->module();
int func_index = GetContainingWasmFunction(module, position);
if (func_index < 0) return false;
const WasmFunction& func = module->functions[func_index];
int offset_in_func = position - func.code.offset();
if (!WasmModuleObject::RemoveBreakpointFromInfo(script, position,
break_point)) {
return false;
}
// Iterate over all instances and tell them to remove this breakpoint.
// We do this using the weak list of all instances from the script.
Handle<WeakArrayList> weak_instance_list(script->wasm_weak_instance_list(),
isolate);
for (int i = 0; i < weak_instance_list->length(); ++i) {
MaybeObject maybe_instance = weak_instance_list->Get(i);
if (maybe_instance->IsWeak()) {
Handle<WasmInstanceObject> instance(
WasmInstanceObject::cast(maybe_instance->GetHeapObjectAssumeWeak()),
isolate);
Handle<WasmDebugInfo> debug_info =
WasmInstanceObject::GetOrCreateDebugInfo(instance);
WasmDebugInfo::ClearBreakpoint(debug_info, func_index, offset_in_func);
}
}
return true;
}
namespace {
int GetBreakpointPos(Isolate* isolate, Object break_point_info_or_undef) {
if (break_point_info_or_undef.IsUndefined(isolate)) return kMaxInt;
return BreakPointInfo::cast(break_point_info_or_undef).source_position();
}
int FindBreakpointInfoInsertPos(Isolate* isolate,
Handle<FixedArray> breakpoint_infos,
int position) {
// Find insert location via binary search, taking care of undefined values on
// the right. Position is always greater than zero.
DCHECK_LT(0, position);
int left = 0; // inclusive
int right = breakpoint_infos->length(); // exclusive
while (right - left > 1) {
int mid = left + (right - left) / 2;
Object mid_obj = breakpoint_infos->get(mid);
if (GetBreakpointPos(isolate, mid_obj) <= position) {
left = mid;
} else {
right = mid;
}
}
int left_pos = GetBreakpointPos(isolate, breakpoint_infos->get(left));
return left_pos < position ? left + 1 : left;
}
} // namespace
// static
void WasmModuleObject::AddBreakpointToInfo(Handle<Script> script, int position,
Handle<BreakPoint> break_point) {
Isolate* isolate = script->GetIsolate();
Handle<FixedArray> breakpoint_infos;
if (script->has_wasm_breakpoint_infos()) {
breakpoint_infos = handle(script->wasm_breakpoint_infos(), isolate);
} else {
breakpoint_infos =
isolate->factory()->NewFixedArray(4, AllocationType::kOld);
script->set_wasm_breakpoint_infos(*breakpoint_infos);
}
int insert_pos =
FindBreakpointInfoInsertPos(isolate, breakpoint_infos, position);
// If a BreakPointInfo object already exists for this position, add the new
// breakpoint object and return.
if (insert_pos < breakpoint_infos->length() &&
GetBreakpointPos(isolate, breakpoint_infos->get(insert_pos)) ==
position) {
Handle<BreakPointInfo> old_info(
BreakPointInfo::cast(breakpoint_infos->get(insert_pos)), isolate);
BreakPointInfo::SetBreakPoint(isolate, old_info, break_point);
return;
}
// Enlarge break positions array if necessary.
bool need_realloc = !breakpoint_infos->get(breakpoint_infos->length() - 1)
.IsUndefined(isolate);
Handle<FixedArray> new_breakpoint_infos = breakpoint_infos;
if (need_realloc) {
new_breakpoint_infos = isolate->factory()->NewFixedArray(
2 * breakpoint_infos->length(), AllocationType::kOld);
script->set_wasm_breakpoint_infos(*new_breakpoint_infos);
// Copy over the entries [0, insert_pos).
for (int i = 0; i < insert_pos; ++i)
new_breakpoint_infos->set(i, breakpoint_infos->get(i));
}
// Move elements [insert_pos, ...] up by one.
for (int i = breakpoint_infos->length() - 1; i >= insert_pos; --i) {
Object entry = breakpoint_infos->get(i);
if (entry.IsUndefined(isolate)) continue;
new_breakpoint_infos->set(i + 1, entry);
}
// Generate new BreakpointInfo.
Handle<BreakPointInfo> breakpoint_info =
isolate->factory()->NewBreakPointInfo(position);
BreakPointInfo::SetBreakPoint(isolate, breakpoint_info, break_point);
// Now insert new position at insert_pos.
new_breakpoint_infos->set(insert_pos, *breakpoint_info);
}
// static
bool WasmModuleObject::RemoveBreakpointFromInfo(
Handle<Script> script, int position, Handle<BreakPoint> break_point) {
if (!script->has_wasm_breakpoint_infos()) return false;
Isolate* isolate = script->GetIsolate();
Handle<FixedArray> breakpoint_infos(script->wasm_breakpoint_infos(), isolate);
int pos = FindBreakpointInfoInsertPos(isolate, breakpoint_infos, position);
// Does a BreakPointInfo object already exist for this position?
if (pos == breakpoint_infos->length()) return false;
Handle<BreakPointInfo> info(BreakPointInfo::cast(breakpoint_infos->get(pos)),
isolate);
BreakPointInfo::ClearBreakPoint(isolate, info, break_point);
// Check if there are no more breakpoints at this location.
if (info->GetBreakPointCount(isolate) == 0) {
// Update array by moving breakpoints up one position.
for (int i = pos; i < breakpoint_infos->length() - 1; i++) {
Object entry = breakpoint_infos->get(i + 1);
breakpoint_infos->set(i, entry);
if (entry.IsUndefined(isolate)) break;
}
// Make sure last array element is empty as a result.
breakpoint_infos->set_undefined(breakpoint_infos->length() - 1);
}
return true;
}
void WasmModuleObject::SetBreakpointsOnNewInstance(
Handle<Script> script, Handle<WasmInstanceObject> instance) {
if (!script->has_wasm_breakpoint_infos()) return;
Isolate* isolate = script->GetIsolate();
Handle<WasmDebugInfo> debug_info =
WasmInstanceObject::GetOrCreateDebugInfo(instance);
Handle<FixedArray> breakpoint_infos(script->wasm_breakpoint_infos(), isolate);
// If the array exists, it should not be empty.
DCHECK_LT(0, breakpoint_infos->length());
for (int i = 0, e = breakpoint_infos->length(); i < e; ++i) {
Handle<Object> obj(breakpoint_infos->get(i), isolate);
if (obj->IsUndefined(isolate)) {
for (; i < e; ++i) {
DCHECK(breakpoint_infos->get(i).IsUndefined(isolate));
}
break;
}
Handle<BreakPointInfo> breakpoint_info = Handle<BreakPointInfo>::cast(obj);
int position = breakpoint_info->source_position();
// Find the function for this breakpoint, and set the breakpoint.
const WasmModule* module = script->wasm_native_module()->module();
int func_index = GetContainingWasmFunction(module, position);
DCHECK_LE(0, func_index);
const WasmFunction& func = module->functions[func_index];
int offset_in_func = position - func.code.offset();
WasmDebugInfo::SetBreakpoint(debug_info, func_index, offset_in_func);
}
}
namespace {
......@@ -596,109 +352,6 @@ int WasmModuleObject::GetSourcePosition(Handle<WasmModuleObject> module_object,
return offset_table->get_int(kOTESize * left + idx);
}
// static
bool WasmModuleObject::GetPossibleBreakpoints(
wasm::NativeModule* native_module, const v8::debug::Location& start,
const v8::debug::Location& end,
std::vector<v8::debug::BreakLocation>* locations) {
DisallowHeapAllocation no_gc;
const std::vector<WasmFunction>& functions =
native_module->module()->functions;
if (start.GetLineNumber() < 0 || start.GetColumnNumber() < 0 ||
(!end.IsEmpty() &&
(end.GetLineNumber() < 0 || end.GetColumnNumber() < 0)))
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;
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;
}
}
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;
++func_idx) {
const WasmFunction& func = functions[func_idx];
if (func.code.length() == 0) continue;
wasm::BodyLocalDecls locals(&tmp);
wasm::BytecodeIterator iterator(module_start + func.code.offset(),
module_start + func.code.end_offset(),
&locals);
DCHECK_LT(0u, locals.encoded_size);
for (uint32_t offset : iterator.offsets()) {
uint32_t total_offset = func.code.offset() + offset;
if (total_offset >= end_offset) {
DCHECK_EQ(end_func_index, func_idx);
break;
}
if (total_offset < start_offset) continue;
locations->emplace_back(func_idx, offset, debug::kCommonBreakLocation);
}
}
return true;
}
// static
MaybeHandle<FixedArray> WasmModuleObject::CheckBreakPoints(
Isolate* isolate, Handle<Script> script, int position) {
if (!script->has_wasm_breakpoint_infos()) return {};
Handle<FixedArray> breakpoint_infos(script->wasm_breakpoint_infos(), isolate);
int insert_pos =
FindBreakpointInfoInsertPos(isolate, breakpoint_infos, position);
if (insert_pos >= breakpoint_infos->length()) return {};
Handle<Object> maybe_breakpoint_info(breakpoint_infos->get(insert_pos),
isolate);
if (maybe_breakpoint_info->IsUndefined(isolate)) return {};
Handle<BreakPointInfo> breakpoint_info =
Handle<BreakPointInfo>::cast(maybe_breakpoint_info);
if (breakpoint_info->source_position() != position) return {};
// There is no support for conditional break points. Just assume that every
// break point always hits.
Handle<Object> break_points(breakpoint_info->break_points(), isolate);
if (break_points->IsFixedArray()) {
return Handle<FixedArray>::cast(break_points);
}
Handle<FixedArray> break_points_hit = isolate->factory()->NewFixedArray(1);
break_points_hit->set(0, *break_points);
return break_points_hit;
}
MaybeHandle<String> WasmModuleObject::ExtractUtf8StringFromModuleBytes(
Isolate* isolate, Handle<WasmModuleObject> module_object,
wasm::WireBytesRef ref) {
......
......@@ -152,30 +152,9 @@ class WasmModuleObject : public JSObject {
Handle<Script> script, Handle<FixedArray> export_wrappers,
size_t code_size_estimate);
// TODO(mstarzinger): The below breakpoint handling methods taking a {Script}
// instead of a {WasmModuleObject} as first argument should be moved onto a
// separate {WasmScript} class, implementation move to wasm-debug.cc then.
// Set a breakpoint on the given byte position inside the given module.
// This will affect all live and future instances of the module.
// The passed position might be modified to point to the next breakable
// location inside the same function.
// If it points outside a function, or behind the last breakable location,
// this function returns false and does not set any breakpoint.
V8_EXPORT_PRIVATE static bool SetBreakPoint(Handle<Script>, int* position,
Handle<BreakPoint> break_point);
// Remove a previously set breakpoint at the given byte position inside the
// given module. If this breakpoint is not found this function returns false.
V8_EXPORT_PRIVATE static bool ClearBreakPoint(Handle<Script>, int position,
Handle<BreakPoint> break_point);
// Check whether this module was generated from asm.js source.
inline bool is_asm_js();
static void SetBreakpointsOnNewInstance(Handle<Script>,
Handle<WasmInstanceObject>);
// Get the module name, if set. Returns an empty handle otherwise.
static MaybeHandle<String> GetModuleNameOrNull(Isolate*,
Handle<WasmModuleObject>);
......@@ -214,25 +193,7 @@ class WasmModuleObject : public JSObject {
Isolate* isolate, Vector<const uint8_t> wire_byte,
wasm::WireBytesRef ref);
// Get a list of all possible breakpoints within a given range of this module.
V8_EXPORT_PRIVATE static bool GetPossibleBreakpoints(
wasm::NativeModule* native_module, const debug::Location& start,
const debug::Location& end, std::vector<debug::BreakLocation>* locations);
// Return an empty handle if no breakpoint is hit at that location, or a
// FixedArray with all hit breakpoint objects.
static MaybeHandle<FixedArray> CheckBreakPoints(Isolate*, Handle<Script>,
int position);
OBJECT_CONSTRUCTORS(WasmModuleObject, JSObject);
private:
// Helper functions that update the breakpoint info list.
static void AddBreakpointToInfo(Handle<Script>, int position,
Handle<BreakPoint> break_point);
static bool RemoveBreakpointFromInfo(Handle<Script>, int position,
Handle<BreakPoint> break_point);
};
// Representation of a WebAssembly.Table JavaScript-level object.
......@@ -915,6 +876,44 @@ class WasmDebugInfo : public Struct {
OBJECT_CONSTRUCTORS(WasmDebugInfo, Struct);
};
class WasmScript : public AllStatic {
public:
// Set a breakpoint on the given byte position inside the given module.
// This will affect all live and future instances of the module.
// The passed position might be modified to point to the next breakable
// location inside the same function.
// If it points outside a function, or behind the last breakable location,
// this function returns false and does not set any breakpoint.
V8_EXPORT_PRIVATE static bool SetBreakPoint(Handle<Script>, int* position,
Handle<BreakPoint> break_point);
// Remove a previously set breakpoint at the given byte position inside the
// given module. If this breakpoint is not found this function returns false.
V8_EXPORT_PRIVATE static bool ClearBreakPoint(Handle<Script>, int position,
Handle<BreakPoint> break_point);
static void SetBreakpointsOnNewInstance(Handle<Script>,
Handle<WasmInstanceObject>);
// Get a list of all possible breakpoints within a given range of this module.
V8_EXPORT_PRIVATE static bool GetPossibleBreakpoints(
wasm::NativeModule* native_module, const debug::Location& start,
const debug::Location& end, std::vector<debug::BreakLocation>* locations);
// Return an empty handle if no breakpoint is hit at that location, or a
// FixedArray with all hit breakpoint objects.
static MaybeHandle<FixedArray> CheckBreakPoints(Isolate*, Handle<Script>,
int position);
private:
// Helper functions that update the breakpoint info list.
static void AddBreakpointToInfo(Handle<Script>, int position,
Handle<BreakPoint> break_point);
static bool RemoveBreakpointFromInfo(Handle<Script>, int position,
Handle<BreakPoint> break_point);
};
// Tags provide an object identity for each exception defined in a wasm module
// header. They are referenced by the following fields:
// - {WasmExceptionObject::exception_tag} : The tag of the exception object.
......
......@@ -25,8 +25,8 @@ void CheckLocations(
NativeModule* native_module, debug::Location start, debug::Location end,
std::initializer_list<debug::Location> expected_locations_init) {
std::vector<debug::BreakLocation> locations;
bool success = WasmModuleObject::GetPossibleBreakpoints(native_module, start,
end, &locations);
bool success =
WasmScript::GetPossibleBreakpoints(native_module, start, end, &locations);
CHECK(success);
printf("got %d locations: ", static_cast<int>(locations.size()));
......@@ -49,8 +49,8 @@ void CheckLocations(
void CheckLocationsFail(NativeModule* native_module, debug::Location start,
debug::Location end) {
std::vector<debug::BreakLocation> locations;
bool success = WasmModuleObject::GetPossibleBreakpoints(native_module, start,
end, &locations);
bool success =
WasmScript::GetPossibleBreakpoints(native_module, start, end, &locations);
CHECK(!success);
}
......@@ -133,7 +133,7 @@ Handle<BreakPoint> SetBreakpoint(WasmRunnerBase* runner, int function_index,
Handle<BreakPoint> break_point =
runner->main_isolate()->factory()->NewBreakPoint(
break_index++, runner->main_isolate()->factory()->empty_string());
CHECK(WasmModuleObject::SetBreakPoint(script, &code_offset, break_point));
CHECK(WasmScript::SetBreakPoint(script, &code_offset, break_point));
int set_byte_offset = code_offset - func_offset;
CHECK_EQ(expected_set_byte_offset, set_byte_offset);
// Also set breakpoint on the debug info of the instance directly, since the
......@@ -153,7 +153,7 @@ void ClearBreakpoint(WasmRunnerBase* runner, int function_index,
Handle<WasmInstanceObject> instance = runner->builder().instance_object();
Handle<Script> script(instance->module_object().script(),
runner->main_isolate());
CHECK(WasmModuleObject::ClearBreakPoint(script, code_offset, break_point));
CHECK(WasmScript::ClearBreakPoint(script, code_offset, break_point));
// Also clear breakpoint on the debug info of the instance directly, since the
// instance chain is not setup properly in tests.
Handle<WasmDebugInfo> debug_info =
......
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