[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/+/1862570 Reviewed-by: Jakob Gruber <jgruber@chromium.org> Reviewed-by: Clemens Backes <clemensb@chromium.org> Commit-Queue: Michael Starzinger <mstarzinger@chromium.org> Cr-Commit-Position: refs/heads/master@{#64308}
This commit is contained in:
parent
0d7889d0b1
commit
1fd8f2ecb1
@ -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 =
|
||||
|
Loading…
Reference in New Issue
Block a user