Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(956)

Unified Diff: src/wasm/wasm-debug.cc

Issue 2096863003: [wasm] prototype for breakpoint support. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@extend-script-functionality
Patch Set: Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/wasm/wasm-debug.h ('k') | src/wasm/wasm-external-refs.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/wasm/wasm-debug.cc
diff --git a/src/wasm/wasm-debug.cc b/src/wasm/wasm-debug.cc
index f1b01593cf437415f2d4143af5e03734149a2851..1ee45d5ccfa29de433ddedef17ec3b39a0e889a5 100644
--- a/src/wasm/wasm-debug.cc
+++ b/src/wasm/wasm-debug.cc
@@ -5,10 +5,15 @@
#include "src/wasm/wasm-debug.h"
#include "src/assert-scope.h"
+#include "src/compiler/wasm-compiler.h"
#include "src/debug/debug.h"
#include "src/factory.h"
+#include "src/frames-inl.h"
#include "src/isolate.h"
+// We need src/assembler-$arch-inl.h, and macro-assembler.h includes this.
+#include "src/macro-assembler.h"
#include "src/wasm/module-decoder.h"
+#include "src/wasm/wasm-interpreter.h"
#include "src/wasm/wasm-module.h"
using namespace v8::internal;
@@ -21,6 +26,9 @@ enum {
kWasmDebugInfoWasmBytesHash,
kWasmDebugInfoFunctionByteOffsets,
kWasmDebugInfoFunctionScripts,
+ kWasmDebugInfoInterpreterAddr,
+ kWasmDebugInfoInterpretedFunctions,
+ kWasmDebugInfoDebugInfos,
kWasmDebugInfoNumEntries
};
@@ -68,17 +76,312 @@ std::pair<int, int> GetFunctionOffsetAndLength(Handle<WasmDebugInfo> debug_info,
Vector<const uint8_t> GetFunctionBytes(Handle<WasmDebugInfo> debug_info,
int func_index) {
- SeqOneByteString *module_bytes =
- wasm::GetWasmBytes(debug_info->wasm_object());
std::pair<int, int> offset_and_length =
GetFunctionOffsetAndLength(debug_info, func_index);
+ SeqOneByteString *module_bytes =
+ wasm::GetWasmBytes(debug_info->wasm_object());
return Vector<const uint8_t>(
module_bytes->GetChars() + offset_and_length.first,
offset_and_length.second);
}
+FixedArray *GetOrCreateInterpretedFunctions(Handle<WasmDebugInfo> debug_info) {
+ Object *maybe_arr = debug_info->get(kWasmDebugInfoInterpretedFunctions);
+ Isolate *isolate = debug_info->GetIsolate();
+ if (!maybe_arr->IsUndefined(isolate)) return FixedArray::cast(maybe_arr);
+
+ FixedArray *new_arr = *isolate->factory()->NewFixedArray(
+ wasm::GetNumberOfFunctions(debug_info->wasm_object()));
+ debug_info->set(kWasmDebugInfoInterpretedFunctions, new_arr);
+ return new_arr;
+}
+
+void RedirectFunction(Code *code, Code *old_target, Code *new_target) {
+ for (RelocIterator it(code, RelocInfo::kCodeTargetMask); !it.done();
+ it.next()) {
+ DCHECK(RelocInfo::IsCodeTarget(it.rinfo()->rmode()));
+ Code *target = Code::GetCodeFromTargetAddress(it.rinfo()->target_address());
+ if (target != old_target) continue;
+ it.rinfo()->set_target_address(new_target->instruction_start(),
+ SKIP_WRITE_BARRIER, SKIP_ICACHE_FLUSH);
+ }
+}
+
+void RedirectFunctionEverywhere(Handle<JSObject> wasm, Handle<Code> old_target,
+ Handle<Code> new_target) {
+ // Redirect in all wasm functions.
+ for (int i = 0, e = wasm::GetNumberOfFunctions(*wasm); i < e; ++i)
+ RedirectFunction(wasm::GetWasmFunctionCode(*wasm, i), *old_target,
+ *new_target);
+
+ // Redirect in the code of all exported functions.
+ Isolate *isolate = wasm->GetIsolate();
+ // For AsmJs modules, the exports are stored on the wasm object itself.
+ MaybeHandle<Object> maybe_exports =
+ JSReceiver::GetProperty(isolate, wasm, "exports");
+ Handle<JSObject> exports_object =
+ maybe_exports.is_null()
+ ? wasm
+ : Handle<JSObject>::cast(maybe_exports.ToHandleChecked());
+
+ Handle<FixedArray> values;
+ if (JSReceiver::GetOwnValues(Handle<JSReceiver>::cast(exports_object),
+ ALL_PROPERTIES)
+ .ToHandle(&values)) {
+ for (int i = 0, e = values->length(); i != e; ++i) {
+ if (!values->get(i)->IsJSFunction()) continue;
+ Code *code = JSFunction::cast(values->get(i))->code();
+ RedirectFunction(code, *old_target, *new_target);
+ }
+ }
+}
+
+class InterpreterHandle {
+ v8::base::AccountingAllocator allocator;
+ Zone zone;
+ Handle<Object> global_ref;
+ std::unique_ptr<const WasmModule> module;
+ std::unique_ptr<WasmModuleInstance> module_instance;
+ Isolate *isolate;
+
+ uint8_t *parameter_buffer;
+ uint32_t parameter_buffer_size = 0;
+#ifdef DEBUG
+ // Track the function index for which the last buffer was allocated.
+ // This is updated on AllocateParameterBuffer, and checked at Execute.
+ uint32_t parameter_buffer_taken = static_cast<uint32_t>(-1);
+#endif
+
+ public:
+ WasmInterpreter interpreter;
+
+ // Initialize in the right order. WasmInterpreter has to be allocate in place,
+ // since it's not std::move'able.
+ explicit InterpreterHandle(WasmDebugInfo *debug_info)
+ : allocator(),
+ zone(&allocator),
+ module(CreateModule(&zone, debug_info)),
+ isolate(debug_info->GetIsolate()),
+ interpreter(CreateModuleInstance(module.get(), debug_info), &zone) {
+ global_ref = debug_info->GetIsolate()->global_handles()->Create(debug_info);
+ GlobalHandles::MakeWeak(global_ref.location(), this,
+ InterpreterHandle::Finalize,
+ v8::WeakCallbackType::kParameter);
+ }
+
+ static void Finalize(const v8::WeakCallbackInfo<void> &data) {
+ void *param = data.GetParameter();
+ InterpreterHandle *handle = reinterpret_cast<InterpreterHandle *>(param);
+ GlobalHandles::ClearWeakness(handle->global_ref.location());
+ GlobalHandles::Destroy(handle->global_ref.location());
+ delete handle;
+ }
+
+ static const WasmModule *CreateModule(Zone *zone, WasmDebugInfo *debug_info) {
+ SeqOneByteString *wasm_bytes =
+ wasm::GetWasmBytes(debug_info->wasm_object());
+ const byte *bytes_start = wasm_bytes->GetChars();
+ const byte *bytes_end = bytes_start + wasm_bytes->length();
+ ModuleResult module_result =
+ DecodeWasmModule(debug_info->GetIsolate(), zone, bytes_start, bytes_end,
+ false, ModuleOrigin::kWasmOrigin);
+ DCHECK(module_result.ok());
+ return module_result.val;
+ }
+
+ static WasmModuleInstance *CreateModuleInstance(const WasmModule *module,
+ WasmDebugInfo *debug_info) {
+ JSArrayBuffer *memory =
+ wasm::GetWasmMemoryBuffer(debug_info->wasm_object());
+
+ WasmModuleInstance *instance = new WasmModuleInstance(module);
+ instance->mem_start = reinterpret_cast<byte *>(memory->backing_store());
+ instance->mem_size = memory->byte_length()->Number();
+ instance->mem_buffer = handle(memory, debug_info->GetIsolate());
+
+ return instance;
+ }
+
+ uint8_t *GetParameterBuffer(uint32_t function_index) {
+ DCHECK_LE(function_index, module->functions.size());
+ DCHECK_EQ(static_cast<uint32_t>(-1), parameter_buffer_taken);
+#ifdef DEBUG
+ parameter_buffer_taken = function_index;
+#endif
+ uint32_t size_needed = 0;
+ FunctionSig *sig = module->functions[function_index].sig;
+ for (size_t i = 0, e = sig->parameter_count(); i != e; ++i) {
+ size_needed += 1 << ElementSizeLog2Of(sig->GetParam(i));
+ }
+ if (size_needed > parameter_buffer_size) {
+ uint32_t alloc_size = 2 * parameter_buffer_size;
+ if (size_needed > alloc_size) alloc_size = size_needed;
+ parameter_buffer = zone.NewArray<uint8_t>(alloc_size);
+ parameter_buffer_size = alloc_size;
+ }
+ return parameter_buffer;
+ }
+
+ void Execute(uint32_t func_index) {
+ DCHECK_EQ(parameter_buffer_taken, func_index);
+#ifdef DEBUG
+ parameter_buffer_taken = static_cast<uint32_t>(-1);
+#endif
+ FunctionSig *sig = module->functions[func_index].sig;
+ DCHECK_LE(sig->parameter_count(), static_cast<size_t>(kMaxInt));
+ int num_params = static_cast<int>(sig->parameter_count());
+ ScopedVector<WasmVal> wasm_args(num_params);
+ uint8_t *buf_ptr = parameter_buffer;
+ for (int i = 0; i < num_params; ++i) {
+ uint32_t param_size = 1 << ElementSizeLog2Of(sig->GetParam(i));
+ switch (sig->GetParam(i)) {
+ case kAstI32:
+ DCHECK_EQ(param_size, sizeof(uint32_t));
+ wasm_args[i] = WasmVal(*reinterpret_cast<uint32_t *>(buf_ptr));
+ break;
+ case kAstI64:
+ DCHECK_EQ(param_size, sizeof(uint64_t));
+ wasm_args[i] = WasmVal(*reinterpret_cast<uint64_t *>(buf_ptr));
+ break;
+ case kAstF32:
+ DCHECK_EQ(param_size, sizeof(float));
+ wasm_args[i] = WasmVal(*reinterpret_cast<float *>(buf_ptr));
+ break;
+ case kAstF64:
+ DCHECK_EQ(param_size, sizeof(double));
+ wasm_args[i] = WasmVal(*reinterpret_cast<double *>(buf_ptr));
+ break;
+ default:
+ UNREACHABLE();
+ }
+ buf_ptr += param_size;
+ }
+
+ WasmInterpreter::Thread *thread = interpreter.GetThread(0);
+ // We do not support reentering an already running interpreter at the moment
+ // (like INTERPRETER -> JS -> WASM -> INTERPRETER).
+ DCHECK(thread->state() == WasmInterpreter::STOPPED ||
+ thread->state() == WasmInterpreter::FINISHED);
+ thread->Reset();
+ thread->PushFrame(&module->functions[func_index], wasm_args.start());
+ WasmInterpreter::State state;
+ do {
+ state = thread->Run();
+ switch (state) {
+ case WasmInterpreter::State::PAUSED: {
+ // We hit a breakpoint.
+ StackTraceFrameIterator frame_it(isolate);
+ WasmInterpretedFrame *top_frame =
+ WasmInterpretedFrame::cast(frame_it.frame());
+ isolate->debug()->Break(top_frame);
+ } break;
+ case WasmInterpreter::State::FINISHED:
+ // Perfect, just break the switch and exit the loop.
+ break;
+ case WasmInterpreter::State::TRAPPED:
+ // TODO(clemensh): Generate appropriate JS exception.
+ UNIMPLEMENTED();
+ break;
+ // STOPPED and RUNNING should never occur here.
+ case WasmInterpreter::State::STOPPED:
+ case WasmInterpreter::State::RUNNING:
+ default:
+ UNREACHABLE();
+ }
+ } while (state != WasmInterpreter::State::FINISHED);
+ }
+
+ void EnsureInterpreterRedirect(Handle<WasmDebugInfo> debug_info,
+ int func_index) {
+ Isolate *isolate = debug_info->GetIsolate();
+ Handle<FixedArray> interpreted_functions(
+ GetOrCreateInterpretedFunctions(debug_info), isolate);
+ DCHECK_LT(func_index, interpreted_functions->length());
+ if (!interpreted_functions->get(func_index)->IsUndefined(isolate)) return;
+
+ Handle<JSObject> wasm(debug_info->wasm_object(), isolate);
+ Handle<Code> new_code = compiler::CompileWasmToInterpreter(
+ isolate, func_index, module->functions[func_index].sig, wasm);
+ Handle<Code> old_code(wasm::GetWasmFunctionCode(*wasm, func_index),
+ isolate);
+ interpreted_functions->set(func_index, *new_code);
+
+ RedirectFunctionEverywhere(wasm, old_code, new_code);
+ }
+};
+
+InterpreterHandle *GetOrCreateInterpreterHandle(WasmDebugInfo *debug_info) {
+ DisallowHeapAllocation no_gc;
+ // We store the raw pointer to the InterpreterHandle in a slot in the debug
+ // info.
+ // Since the pointer is aligned, it will look like a Smi, and the gc will
+ // ignore it.
+ Object *handle_obj = debug_info->get(kWasmDebugInfoInterpreterAddr);
+ if (!handle_obj->IsUndefined(debug_info->GetIsolate())) {
+ DCHECK(!handle_obj->IsHeapObject());
+ return reinterpret_cast<InterpreterHandle *>(handle_obj);
+ }
+
+ InterpreterHandle *handle = new InterpreterHandle(debug_info);
+ handle_obj = reinterpret_cast<Object *>(handle);
+ DCHECK(!handle_obj->IsHeapObject());
+ debug_info->set(kWasmDebugInfoInterpreterAddr, handle_obj);
+ return handle;
+}
+
+InterpreterHandle *GetInterpreterHandleIfExists(WasmDebugInfo *debug_info) {
+ DisallowHeapAllocation no_gc;
+ Object *handle_obj = debug_info->get(kWasmDebugInfoInterpreterAddr);
+ return handle_obj->IsUndefined(debug_info->GetIsolate())
+ ? nullptr
+ : reinterpret_cast<InterpreterHandle *>(handle_obj);
+}
+
} // namespace
+WasmInstructionIterator::WasmInstructionIterator(const byte *start,
+ const byte *end)
+ : start_(start), end_(end) {
+ base::AccountingAllocator allocator;
+ Zone tmp_zone(&allocator);
+ AstLocalDecls locals(&tmp_zone);
+ bool valid = DecodeLocalDecls(locals, start, end);
+ DCHECK(valid);
+ USE(valid);
+ pc_ = start + locals.decls_encoded_size;
+}
+
+void WasmInstructionIterator::Next() {
+ DCHECK(!Done());
+ int len = OpcodeLength(pc_, end_);
+ DCHECK_LE(0, len); // 0 means validation error / read past the end.
+ pc_ += len;
+}
+
+Script *InterpreterFrameInfo::GetScript() const {
+ return WasmDebugInfo::GetFunctionScript(debug_info_, func_index_);
+}
+
+InterpreterFrameIterator::InterpreterFrameIterator(
+ Handle<WasmDebugInfo> debug_info)
+ : frame_nr_(0),
+ frame_count_(GetOrCreateInterpreterHandle(*debug_info)
+ ->interpreter.GetThread(0)
+ ->GetFrameCount()),
+ debug_info_(debug_info) {}
+
+InterpreterFrameInfo InterpreterFrameIterator::GetFrameInfo() const {
+ DCHECK(!Done());
+ WasmInterpreter::Thread *thread =
+ GetOrCreateInterpreterHandle(*debug_info_)->interpreter.GetThread(0);
+ DCHECK_EQ(frame_count_, thread->GetFrameCount());
+ DCHECK_LE(0, frame_nr_);
+ std::unique_ptr<const WasmInterpreterFrame> frame(
+ thread->GetFrame(frame_nr_));
+ return InterpreterFrameInfo(debug_info_, frame->function()->func_index,
+ frame->pc());
+}
+
Handle<WasmDebugInfo> WasmDebugInfo::New(Handle<JSObject> wasm) {
Isolate *isolate = wasm->GetIsolate();
Factory *factory = isolate->factory();
@@ -98,7 +401,7 @@ Handle<WasmDebugInfo> WasmDebugInfo::New(Handle<JSObject> wasm) {
return Handle<WasmDebugInfo>::cast(arr);
}
-bool WasmDebugInfo::IsDebugInfo(Object *object) {
+bool WasmDebugInfo::IsWasmDebugInfo(Object *object) {
if (!object->IsFixedArray()) return false;
FixedArray *arr = FixedArray::cast(object);
Isolate *isolate = arr->GetIsolate();
@@ -108,11 +411,15 @@ bool WasmDebugInfo::IsDebugInfo(Object *object) {
(arr->get(kWasmDebugInfoFunctionByteOffsets)->IsUndefined(isolate) ||
arr->get(kWasmDebugInfoFunctionByteOffsets)->IsByteArray()) &&
(arr->get(kWasmDebugInfoFunctionScripts)->IsUndefined(isolate) ||
- arr->get(kWasmDebugInfoFunctionScripts)->IsFixedArray());
+ arr->get(kWasmDebugInfoFunctionScripts)->IsFixedArray()) &&
+ (arr->get(kWasmDebugInfoInterpretedFunctions)->IsUndefined(isolate) ||
+ arr->get(kWasmDebugInfoInterpretedFunctions)->IsFixedArray()) &&
+ (arr->get(kWasmDebugInfoDebugInfos)->IsUndefined(isolate) ||
+ arr->get(kWasmDebugInfoDebugInfos)->IsFixedArray());
}
WasmDebugInfo *WasmDebugInfo::cast(Object *object) {
- DCHECK(IsDebugInfo(object));
+ DCHECK(IsWasmDebugInfo(object));
return reinterpret_cast<WasmDebugInfo *>(object);
}
@@ -120,6 +427,25 @@ JSObject *WasmDebugInfo::wasm_object() {
return JSObject::cast(get(kWasmDebugInfoWasmObj));
}
+void WasmDebugInfo::SetBreakpoint(Handle<WasmDebugInfo> debug_info,
+ int func_index, int byte_offset) {
+ DCHECK_LE(0, func_index);
+ DCHECK_LE(0, byte_offset);
+ InterpreterHandle *handle = GetOrCreateInterpreterHandle(*debug_info);
+ handle->EnsureInterpreterRedirect(debug_info, func_index);
+ handle->interpreter.SetBreakpoint(static_cast<uint32_t>(func_index),
+ byte_offset, true);
+}
+
+bool WasmDebugInfo::HasBreakpoint(int func_index, int byte_offset) {
+ DCHECK_LE(0, func_index);
+ DCHECK_LE(0, byte_offset);
+ InterpreterHandle *handle = GetInterpreterHandleIfExists(this);
+ return handle &&
+ handle->interpreter.GetBreakpoint(static_cast<uint32_t>(func_index),
+ byte_offset);
+}
+
Script *WasmDebugInfo::GetFunctionScript(Handle<WasmDebugInfo> debug_info,
int func_index) {
Isolate *isolate = debug_info->GetIsolate();
@@ -228,3 +554,73 @@ Handle<FixedArray> WasmDebugInfo::GetFunctionOffsetTable(
return offset_table;
}
+
+DebugInfo *WasmDebugInfo::GetDebugInfo(Handle<WasmDebugInfo> debug_info,
+ int func_index) {
+ Object *maybe_arr = debug_info->get(kWasmDebugInfoDebugInfos);
+ Isolate *isolate = debug_info->GetIsolate();
+ Handle<FixedArray> arr;
+ if (maybe_arr->IsUndefined(isolate)) {
+ arr = isolate->factory()->NewFixedArray(
+ wasm::GetNumberOfFunctions(debug_info->wasm_object()));
+ debug_info->set(kWasmDebugInfoDebugInfos, *arr);
+ } else {
+ arr = handle(FixedArray::cast(maybe_arr));
+ }
+
+ DCHECK_LE(func_index, static_cast<uint32_t>(arr->length()));
+ Object *maybe_info = arr->get(static_cast<int>(func_index));
+ if (!maybe_info->IsUndefined(isolate)) return DebugInfo::cast(maybe_info);
+
+ Handle<AbstractCode> code(AbstractCode::cast(
+ wasm::GetWasmFunctionCode(debug_info->wasm_object(), func_index)));
+ Handle<DebugInfo> new_debug_info = isolate->factory()->NewDebugInfo(code);
+ arr->set(static_cast<int>(func_index), *new_debug_info);
+ return *new_debug_info;
+}
+
+WasmDebugInfo::InstructionType WasmDebugInfo::GetInstructionType(
+ int func_index, int byte_offset) {
+ SeqOneByteString *bytes = wasm::GetWasmBytes(wasm_object());
+ DCHECK(byte_offset >= 0 && byte_offset < bytes->length());
+ switch (static_cast<WasmOpcode>(bytes->Get(byte_offset))) {
+ case WasmOpcode::kExprReturn:
+ return RETURN;
+ case WasmOpcode::kExprCallImport:
+ case WasmOpcode::kExprCallFunction:
+ case WasmOpcode::kExprCallIndirect:
+ return CALL;
+ default:
+ return OTHER;
+ }
+}
+
+WasmInstructionIterator WasmDebugInfo::GetInstructionIterator(
+ Handle<WasmDebugInfo> debug_info, int func_index) {
+ Vector<const uint8_t> function_bytes =
+ GetFunctionBytes(debug_info, func_index);
+
+ return WasmInstructionIterator(function_bytes.start(), function_bytes.end());
+}
+
+InterpreterFrameIterator WasmDebugInfo::GetInterpreterFrameIterator() {
+ return InterpreterFrameIterator(handle(this));
+}
+
+Object *WasmDebugInfo::GetInterpreterArgBuffer(int func_index) {
+ printf("GetInterpreterArgBuffer %d\n", func_index);
+ DCHECK_LE(0, func_index);
+ InterpreterHandle *interp_handle = GetOrCreateInterpreterHandle(this);
+ uint8_t *buf =
+ interp_handle->GetParameterBuffer(static_cast<uint32_t>(func_index));
+ Object *obj = reinterpret_cast<Object *>(buf);
+ DCHECK(!obj->IsHeapObject());
+ return obj;
+}
+
+void WasmDebugInfo::RunInterpreter(int func_index) {
+ printf("RunInterpreter %d\n", func_index);
+ DCHECK_LE(0, func_index);
+ InterpreterHandle *interp_handle = GetOrCreateInterpreterHandle(this);
+ interp_handle->Execute(static_cast<uint32_t>(func_index));
+}
« no previous file with comments | « src/wasm/wasm-debug.h ('k') | src/wasm/wasm-external-refs.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698