Commit 1fef739a authored by clemensh's avatar clemensh Committed by Commit bot

[wasm] Implement GetPossibleBreakpoints

This CL implements GetPossibleBreakpoints for wasm, by iterating over
all functions in the requested range and returning the location of all
instructions within that range.

The connection to the inspector will be added later, when setting
breakpoint also works for wasm: http://crrev.com/2536763002

BUG=chromium:613110
R=titzer@chromium.org

Review-Url: https://codereview.chromium.org/2588763002
Cr-Commit-Position: refs/heads/master@{#41818}
parent b6a57e6f
......@@ -5,6 +5,7 @@
#include "src/wasm/wasm-objects.h"
#include "src/utils.h"
#include "src/debug/debug-interface.h"
#include "src/wasm/module-decoder.h"
#include "src/wasm/wasm-module.h"
#include "src/wasm/wasm-text.h"
......@@ -652,6 +653,77 @@ v8::debug::WasmDisassembly WasmCompiledModule::DisassembleFunction(
return {disassembly_os.str(), std::move(offset_table)};
}
bool WasmCompiledModule::GetPossibleBreakpoints(
const v8::debug::Location& start, const v8::debug::Location& end,
std::vector<v8::debug::Location>* locations) const {
DisallowHeapAllocation no_gc;
std::vector<WasmFunction>& functions = 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_end_offset -
functions[start_func_index].code_start_offset;
if (start.GetColumnNumber() > start_func_len) return false;
uint32_t start_offset =
functions[start_func_index].code_start_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_start_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 = ptr_to_module_bytes()->GetChars();
for (uint32_t func_idx = start_func_index; func_idx <= end_func_index;
++func_idx) {
WasmFunction& func = functions[func_idx];
if (func.code_start_offset == func.code_end_offset) continue;
AstLocalDecls locals(&tmp);
BytecodeIterator iterator(module_start + func.code_start_offset,
module_start + func.code_end_offset, &locals);
DCHECK_LT(0u, locals.decls_encoded_size);
for (; iterator.has_next(); iterator.next()) {
uint32_t offset = func.code_start_offset + iterator.pc_offset();
if (offset >= end_offset) {
DCHECK_EQ(end_func_index, func_idx);
break;
}
if (offset < start_offset) continue;
locations->push_back(v8::debug::Location(func_idx, iterator.pc_offset()));
}
}
return true;
}
Handle<WasmInstanceWrapper> WasmInstanceWrapper::New(
Isolate* isolate, Handle<WasmInstanceObject> instance) {
Handle<FixedArray> array =
......
......@@ -317,6 +317,11 @@ class WasmCompiledModule : public FixedArray {
Isolate* isolate, Handle<WasmCompiledModule> compiled_module,
uint32_t offset, uint32_t size);
// Get a list of all possible breakpoints within a given range of this module.
bool GetPossibleBreakpoints(const debug::Location& start,
const debug::Location& end,
std::vector<debug::Location>* locations) const;
private:
void InitId();
......
......@@ -191,6 +191,7 @@ v8_executable("cctest") {
"wasm/test-run-wasm-module.cc",
"wasm/test-run-wasm-relocation.cc",
"wasm/test-run-wasm.cc",
"wasm/test-wasm-breakpoints.cc",
"wasm/test-wasm-stack.cc",
"wasm/test-wasm-trap-position.cc",
"wasm/wasm-run-utils.h",
......
......@@ -212,6 +212,7 @@
'wasm/test-run-wasm-js.cc',
'wasm/test-run-wasm-module.cc',
'wasm/test-run-wasm-relocation.cc',
'wasm/test-wasm-breakpoints.cc',
'wasm/test-wasm-stack.cc',
'wasm/test-wasm-trap-position.cc',
'wasm/wasm-run-utils.h',
......
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/debug/debug-interface.h"
#include "src/wasm/wasm-macro-gen.h"
#include "src/wasm/wasm-objects.h"
#include "test/cctest/cctest.h"
#include "test/cctest/compiler/value-helper.h"
#include "test/cctest/wasm/wasm-run-utils.h"
#include "test/common/wasm/test-signatures.h"
using namespace v8::internal;
using namespace v8::internal::wasm;
namespace debug = v8::debug;
namespace {
void CheckLocations(
WasmCompiledModule *compiled_module, debug::Location start,
debug::Location end,
std::initializer_list<debug::Location> expected_locations_init) {
std::vector<debug::Location> locations;
bool success =
compiled_module->GetPossibleBreakpoints(start, end, &locations);
CHECK(success);
printf("got %d locations: ", static_cast<int>(locations.size()));
for (size_t i = 0, e = locations.size(); i != e; ++i) {
printf("%s<%d,%d>", i == 0 ? "" : ", ", locations[i].GetLineNumber(),
locations[i].GetColumnNumber());
}
printf("\n");
std::vector<debug::Location> expected_locations(expected_locations_init);
CHECK_EQ(expected_locations.size(), locations.size());
for (size_t i = 0, e = locations.size(); i != e; ++i) {
CHECK_EQ(expected_locations[i].GetLineNumber(),
locations[i].GetLineNumber());
CHECK_EQ(expected_locations[i].GetColumnNumber(),
locations[i].GetColumnNumber());
}
}
void CheckLocationsFail(WasmCompiledModule *compiled_module,
debug::Location start, debug::Location end) {
std::vector<debug::Location> locations;
bool success =
compiled_module->GetPossibleBreakpoints(start, end, &locations);
CHECK(!success);
}
} // namespace
TEST(CollectPossibleBreakpoints) {
WasmRunner<int> runner(kExecuteCompiled);
BUILD(runner, WASM_NOP, WASM_I32_ADD(WASM_ZERO, WASM_ONE));
Handle<WasmInstanceObject> instance = runner.module().instance_object();
std::vector<debug::Location> locations;
CheckLocations(instance->get_compiled_module(), {0, 0}, {1, 0},
{{0, 1}, {0, 2}, {0, 4}, {0, 6}});
CheckLocations(instance->get_compiled_module(), {0, 2}, {0, 4}, {{0, 2}});
CheckLocations(instance->get_compiled_module(), {0, 2}, {0, 5},
{{0, 2}, {0, 4}});
CheckLocations(instance->get_compiled_module(), {0, 6}, {0, 7}, {{0, 6}});
CheckLocations(instance->get_compiled_module(), {0, 6}, {1, 0}, {{0, 6}});
CheckLocations(instance->get_compiled_module(), {0, 7}, {1, 0}, {});
CheckLocationsFail(instance->get_compiled_module(), {0, 8}, {1, 0});
}
......@@ -266,6 +266,19 @@ class TestingModule : public ModuleEnv {
}
}
uint32_t AddBytes(Vector<const byte> bytes) {
Handle<SeqOneByteString> old_bytes =
instance_object_->get_compiled_module()->module_bytes();
uint32_t old_size = static_cast<uint32_t>(old_bytes->length());
ScopedVector<byte> new_bytes(old_size + bytes.length());
memcpy(new_bytes.start(), old_bytes->GetChars(), old_size);
memcpy(new_bytes.start() + old_size, bytes.start(), bytes.length());
Handle<SeqOneByteString> new_bytes_str = Handle<SeqOneByteString>::cast(
isolate_->factory()->NewStringFromOneByte(new_bytes).ToHandleChecked());
instance_object_->get_compiled_module()->set_module_bytes(new_bytes_str);
return old_size;
}
WasmFunction* GetFunctionAt(int index) { return &module_.functions[index]; }
WasmInterpreter* interpreter() { return interpreter_; }
......@@ -294,19 +307,6 @@ class TestingModule : public ModuleEnv {
return &module->globals.back();
}
uint32_t AddBytes(Vector<const byte> bytes) {
Handle<SeqOneByteString> old_bytes =
instance_object_->get_compiled_module()->module_bytes();
uint32_t old_size = static_cast<uint32_t>(old_bytes->length());
ScopedVector<byte> new_bytes(old_size + bytes.length());
memcpy(new_bytes.start(), old_bytes->GetChars(), old_size);
memcpy(new_bytes.start() + old_size, bytes.start(), bytes.length());
Handle<SeqOneByteString> new_bytes_str = Handle<SeqOneByteString>::cast(
isolate_->factory()->NewStringFromOneByte(new_bytes).ToHandleChecked());
instance_object_->get_compiled_module()->set_module_bytes(new_bytes_str);
return old_size;
}
Handle<WasmInstanceObject> InitInstanceObject() {
Handle<Managed<wasm::WasmModule>> module_wrapper =
Managed<wasm::WasmModule>::New(isolate_, &module_, false);
......@@ -498,6 +498,13 @@ class WasmFunctionCompiler : private GraphAndBuilders {
void Build(const byte* start, const byte* end) {
local_decls.Prepend(zone(), &start, &end);
CHECK_GE(kMaxInt, end - start);
int len = static_cast<int>(end - start);
function_->code_start_offset =
testing_module_->AddBytes(Vector<const byte>(start, len));
function_->code_end_offset = function_->code_start_offset + len;
if (interpreter_) {
// Add the code to the interpreter.
CHECK(interpreter_->SetFunctionCodeForTesting(function_, start, end));
......
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