| Index: test/cctest/wasm/test-wasm-breakpoints.cc
|
| diff --git a/test/cctest/wasm/test-wasm-breakpoints.cc b/test/cctest/wasm/test-wasm-breakpoints.cc
|
| index d2374a44c02c99ff16091d62bd42c1f54ee7b39d..d287606a50d87b2b503f99e78836aaa65d2545d1 100644
|
| --- a/test/cctest/wasm/test-wasm-breakpoints.cc
|
| +++ b/test/cctest/wasm/test-wasm-breakpoints.cc
|
| @@ -3,6 +3,7 @@
|
| // found in the LICENSE file.
|
|
|
| #include "src/debug/debug-interface.h"
|
| +#include "src/property-descriptor.h"
|
| #include "src/wasm/wasm-macro-gen.h"
|
| #include "src/wasm/wasm-objects.h"
|
|
|
| @@ -50,6 +51,93 @@ void CheckLocationsFail(WasmCompiledModule *compiled_module,
|
| CHECK(!success);
|
| }
|
|
|
| +class BreakHandler {
|
| + public:
|
| + explicit BreakHandler(Isolate* isolate) : isolate_(isolate) {
|
| + current_handler = this;
|
| + isolate->debug()->SetMessageHandler(&HandleMessage);
|
| + }
|
| + ~BreakHandler() {
|
| + CHECK_EQ(this, current_handler);
|
| + current_handler = nullptr;
|
| + isolate_->debug()->SetMessageHandler(nullptr);
|
| + }
|
| +
|
| + int count() const { return count_; }
|
| +
|
| + private:
|
| + Isolate* isolate_;
|
| + int count_ = 0;
|
| +
|
| + static BreakHandler* current_handler;
|
| +
|
| + static void HandleMessage(const v8::Debug::Message& message) {
|
| + // Ignore responses.
|
| + if (!message.IsEvent()) return;
|
| + // Ignore everything except break events.
|
| + if (message.GetEvent() != v8::DebugEvent::Break) return;
|
| +
|
| + printf("break!\n");
|
| + CHECK_NOT_NULL(current_handler);
|
| + current_handler->count_ += 1;
|
| + // Don't run into an endless loop.
|
| + CHECK_GT(100, current_handler->count_);
|
| +
|
| + const char command[] = "{\"type\":\"request\", \"command\":\"continue\"}";
|
| + uint16_t command_u16[arraysize(command) - 1];
|
| + for (unsigned i = 0; i < arraysize(command) - 1; ++i) {
|
| + command_u16[i] = command[i];
|
| + }
|
| + current_handler->isolate_->debug()->EnqueueCommandMessage(
|
| + ArrayVector(command_u16), message.GetClientData());
|
| + }
|
| +};
|
| +
|
| +// static
|
| +BreakHandler* BreakHandler::current_handler = nullptr;
|
| +
|
| +Handle<JSObject> MakeFakeBreakpoint(Isolate* isolate, int position) {
|
| + Handle<JSObject> obj =
|
| + isolate->factory()->NewJSObject(isolate->object_function());
|
| + // Generate an "isTriggered" method that always returns true.
|
| + // This can/must be refactored once we remove remaining JS parts from the
|
| + // debugger (bug 5530).
|
| + Handle<String> source = isolate->factory()->NewStringFromStaticChars("true");
|
| + Handle<Context> context(isolate->context(), isolate);
|
| + Handle<JSFunction> triggered_fun =
|
| + Compiler::GetFunctionFromString(context, source, NO_PARSE_RESTRICTION)
|
| + .ToHandleChecked();
|
| + PropertyDescriptor desc;
|
| + desc.set_value(triggered_fun);
|
| + Handle<String> name =
|
| + isolate->factory()->InternalizeUtf8String(CStrVector("isTriggered"));
|
| + CHECK(
|
| + JSObject::DefineOwnProperty(isolate, obj, name, &desc, Object::DONT_THROW)
|
| + .FromMaybe(false));
|
| + return obj;
|
| +}
|
| +
|
| +void SetBreakpoint(WasmRunnerBase& runner, int function_index, int byte_offset,
|
| + int expected_set_byte_offset = -1) {
|
| + int func_offset =
|
| + runner.module().module->functions[function_index].code_start_offset;
|
| + int code_offset = func_offset + byte_offset;
|
| + if (expected_set_byte_offset == -1) expected_set_byte_offset = byte_offset;
|
| + Handle<WasmInstanceObject> instance = runner.module().instance_object();
|
| + Handle<WasmCompiledModule> compiled_module(instance->compiled_module());
|
| + Handle<JSObject> fake_breakpoint_object =
|
| + MakeFakeBreakpoint(runner.main_isolate(), code_offset);
|
| + CHECK(WasmCompiledModule::SetBreakPoint(compiled_module, &code_offset,
|
| + fake_breakpoint_object));
|
| + 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
|
| + // instance chain is not setup properly in tests.
|
| + Handle<WasmDebugInfo> debug_info =
|
| + WasmInstanceObject::GetOrCreateDebugInfo(instance);
|
| + WasmDebugInfo::SetBreakpoint(debug_info, function_index, set_byte_offset);
|
| +}
|
| +
|
| } // namespace
|
|
|
| TEST(CollectPossibleBreakpoints) {
|
| @@ -59,12 +147,44 @@ TEST(CollectPossibleBreakpoints) {
|
|
|
| Handle<WasmInstanceObject> instance = runner.module().instance_object();
|
| std::vector<debug::Location> locations;
|
| + // Check all locations for function 0.
|
| CheckLocations(instance->compiled_module(), {0, 0}, {1, 0},
|
| {{0, 1}, {0, 2}, {0, 4}, {0, 6}, {0, 7}});
|
| + // Check a range ending at an instruction.
|
| CheckLocations(instance->compiled_module(), {0, 2}, {0, 4}, {{0, 2}});
|
| + // Check a range ending one behind an instruction.
|
| CheckLocations(instance->compiled_module(), {0, 2}, {0, 5}, {{0, 2}, {0, 4}});
|
| + // Check a range starting at an instruction.
|
| CheckLocations(instance->compiled_module(), {0, 7}, {0, 8}, {{0, 7}});
|
| + // Check from an instruction to beginning of next function.
|
| CheckLocations(instance->compiled_module(), {0, 7}, {1, 0}, {{0, 7}});
|
| + // Check from end of one function (no valid instruction position) to beginning
|
| + // of next function. Must be empty, but not fail.
|
| CheckLocations(instance->compiled_module(), {0, 8}, {1, 0}, {});
|
| + // Check from one after the end of the function. Must fail.
|
| CheckLocationsFail(instance->compiled_module(), {0, 9}, {1, 0});
|
| }
|
| +
|
| +TEST(TestSimpleBreak) {
|
| + WasmRunner<int> runner(kExecuteCompiled);
|
| + Isolate* isolate = runner.main_isolate();
|
| +
|
| + BUILD(runner, WASM_NOP, WASM_I32_ADD(WASM_I32V_1(11), WASM_I32V_1(3)));
|
| +
|
| + Handle<JSFunction> main_fun_wrapper =
|
| + runner.module().WrapCode(runner.function_index());
|
| + SetBreakpoint(runner, runner.function_index(), 4, 4);
|
| +
|
| + BreakHandler count_breaks(isolate);
|
| + CHECK_EQ(0, count_breaks.count());
|
| +
|
| + Handle<Object> global(isolate->context()->global_object(), isolate);
|
| + MaybeHandle<Object> retval =
|
| + Execution::Call(isolate, main_fun_wrapper, global, 0, nullptr);
|
| + CHECK(!retval.is_null());
|
| + int result;
|
| + CHECK(retval.ToHandleChecked()->ToInt32(&result));
|
| + CHECK_EQ(14, result);
|
| +
|
| + CHECK_EQ(1, count_breaks.count());
|
| +}
|
|
|