efea740768
This reverts commit f2ea42d6b8
.
Reason for revert: Makes UBSan unhappy: https://ci.chromium.org/p/v8/builders/ci/V8%20Linux64%20UBSan/10634
Original change's description:
> [wasm-debug-eval] Implement additional evaluator API methods
>
> This CL implements the __getLocal and __sbrk APIs of the evaluator
> interface. Also includes a drive-by fix of the imports' module: put
> them on the "env" module.
>
> Change-Id: Ie16d1b1cf924b88734eda184d1ce98d52f32f828
> Bug: chromium:1020120
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2132786
> Commit-Queue: Philip Pfaffe <pfaffe@chromium.org>
> Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#67122}
TBR=jkummerow@chromium.org,pfaffe@chromium.org
Change-Id: I23b078d37971e083c08c9b83994bbf38ac13f103
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: chromium:1020120
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2148787
Reviewed-by: Clemens Backes <clemensb@chromium.org>
Commit-Queue: Clemens Backes <clemensb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#67124}
316 lines
11 KiB
C++
316 lines
11 KiB
C++
// Copyright 2020 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 <initializer_list>
|
|
|
|
#include "src/api/api-inl.h"
|
|
#include "src/base/macros.h"
|
|
#include "src/codegen/assembler-inl.h"
|
|
#include "src/compiler/heap-refs.h"
|
|
#include "src/debug/debug-evaluate.h"
|
|
#include "src/debug/debug-interface.h"
|
|
#include "src/diagnostics/disassembler.h"
|
|
#include "src/execution/frames-inl.h"
|
|
#include "src/execution/frames.h"
|
|
#include "src/objects/js-objects.h"
|
|
#include "src/objects/property-descriptor.h"
|
|
#include "src/utils/utils.h"
|
|
#include "src/utils/vector.h"
|
|
#include "src/wasm/compilation-environment.h"
|
|
#include "src/wasm/module-decoder.h"
|
|
#include "src/wasm/value-type.h"
|
|
#include "src/wasm/wasm-constants.h"
|
|
#include "src/wasm/wasm-debug-evaluate.h"
|
|
#include "src/wasm/wasm-debug.h"
|
|
#include "src/wasm/wasm-interpreter.h"
|
|
#include "src/wasm/wasm-module-builder.h"
|
|
#include "src/wasm/wasm-module.h"
|
|
#include "src/wasm/wasm-objects-inl.h"
|
|
#include "src/wasm/wasm-opcodes.h"
|
|
#include "src/wasm/wasm-tier.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"
|
|
#include "test/common/wasm/wasm-macro-gen.h"
|
|
|
|
namespace v8 {
|
|
namespace internal {
|
|
namespace wasm {
|
|
|
|
namespace {
|
|
template <typename... FunctionArgsT>
|
|
class TestCode {
|
|
public:
|
|
TestCode(WasmRunnerBase* runner, std::initializer_list<byte> code)
|
|
: compiler_(&runner->NewFunction<FunctionArgsT...>()), code_(code) {
|
|
compiler_->Build(code.begin(), code.end());
|
|
}
|
|
|
|
Handle<BreakPoint> BreakOnReturn(WasmRunnerBase* runner) {
|
|
runner->TierDown();
|
|
uint32_t return_offset_in_function = FindReturn();
|
|
|
|
int function_index = compiler_->function_index();
|
|
int function_offset =
|
|
runner->builder().GetFunctionAt(function_index)->code.offset();
|
|
int return_offset_in_module = function_offset + return_offset_in_function;
|
|
|
|
Handle<WasmInstanceObject> instance = runner->builder().instance_object();
|
|
Handle<Script> script(instance->module_object().script(),
|
|
runner->main_isolate());
|
|
static int break_index = 0;
|
|
Handle<BreakPoint> break_point =
|
|
runner->main_isolate()->factory()->NewBreakPoint(
|
|
break_index++, runner->main_isolate()->factory()->empty_string());
|
|
CHECK(WasmScript::SetBreakPoint(script, &return_offset_in_module,
|
|
break_point));
|
|
int set_breakpoint_offset = return_offset_in_module - function_offset;
|
|
// Also set breakpoint on the debug info of the instance directly, since
|
|
// the instance chain is not set up properly in tests.
|
|
Handle<WasmDebugInfo> debug_info =
|
|
WasmInstanceObject::GetOrCreateDebugInfo(instance);
|
|
WasmDebugInfo::SetBreakpoint(debug_info, function_index,
|
|
set_breakpoint_offset);
|
|
|
|
return break_point;
|
|
}
|
|
|
|
MaybeHandle<Object> Run(WasmRunnerBase* runner) {
|
|
Isolate* isolate = runner->main_isolate();
|
|
Handle<JSFunction> fun_wrapper =
|
|
runner->builder().WrapCode(compiler_->function_index());
|
|
Handle<Object> global(isolate->context().global_object(), isolate);
|
|
return Execution::Call(isolate, fun_wrapper, global, 0, nullptr);
|
|
}
|
|
|
|
private:
|
|
uint32_t FindReturn() const {
|
|
for (auto i = code_.begin(); i != code_.end();
|
|
i += OpcodeLength(&*i, &*code_.end())) {
|
|
if (*i == kExprReturn) {
|
|
return static_cast<uint32_t>(std::distance(code_.begin(), i));
|
|
}
|
|
}
|
|
UNREACHABLE();
|
|
}
|
|
|
|
WasmFunctionCompiler* compiler_;
|
|
std::vector<byte> code_;
|
|
};
|
|
|
|
class WasmEvaluatorBuilder {
|
|
public:
|
|
explicit WasmEvaluatorBuilder(ExecutionTier execution_tier,
|
|
uint32_t min_memory = 1,
|
|
uint32_t max_memory = 1)
|
|
: zone_(&allocator_, ZONE_NAME), builder_(&zone_) {
|
|
get_memory_function_index = AddImport<void, uint32_t, uint32_t, uint32_t>(
|
|
CStrVector("__getMemory"));
|
|
wasm_format_function =
|
|
builder_.AddFunction(WasmRunnerBase::CreateSig<uint32_t>(&zone_));
|
|
wasm_format_function->SetName(CStrVector("wasm_format"));
|
|
builder_.AddExport(CStrVector("wasm_format"), wasm_format_function);
|
|
builder_.SetMinMemorySize(min_memory);
|
|
builder_.SetMaxMemorySize(max_memory);
|
|
}
|
|
|
|
template <typename ReturnT, typename... ArgTs>
|
|
uint32_t AddImport(Vector<const char> name) {
|
|
return builder_.AddImport(
|
|
name, WasmRunnerBase::CreateSig<ReturnT, ArgTs...>(&zone_));
|
|
}
|
|
|
|
void push_back(std::initializer_list<byte> code) {
|
|
wasm_format_function->EmitCode(code.begin(),
|
|
static_cast<uint32_t>(code.size()));
|
|
}
|
|
|
|
void CallGetMemory(std::initializer_list<byte> args) {
|
|
push_back(args);
|
|
push_back({WASM_CALL_FUNCTION0(get_memory_function_index)});
|
|
}
|
|
|
|
ZoneBuffer bytes() {
|
|
ZoneBuffer bytes(&zone_);
|
|
builder_.WriteTo(&bytes);
|
|
return bytes;
|
|
}
|
|
|
|
private:
|
|
v8::internal::AccountingAllocator allocator_;
|
|
Zone zone_;
|
|
WasmModuleBuilder builder_;
|
|
uint32_t get_memory_function_index = 0;
|
|
WasmFunctionBuilder* wasm_format_function = nullptr;
|
|
};
|
|
|
|
class WasmBreakHandler : public debug::DebugDelegate {
|
|
public:
|
|
struct EvaluationResult {
|
|
Maybe<std::string> result = Nothing<std::string>();
|
|
Maybe<std::string> error = Nothing<std::string>();
|
|
};
|
|
|
|
WasmBreakHandler(Isolate* isolate, ZoneBuffer evaluator_bytes)
|
|
: isolate_(isolate),
|
|
evaluator_bytes_(std::move(evaluator_bytes)),
|
|
result_(Nothing<EvaluationResult>()) {
|
|
v8::debug::SetDebugDelegate(reinterpret_cast<v8::Isolate*>(isolate_), this);
|
|
}
|
|
|
|
~WasmBreakHandler() override {
|
|
v8::debug::SetDebugDelegate(reinterpret_cast<v8::Isolate*>(isolate_),
|
|
nullptr);
|
|
}
|
|
|
|
const Maybe<EvaluationResult>& result() const { return result_; }
|
|
|
|
private:
|
|
Isolate* isolate_;
|
|
ZoneBuffer evaluator_bytes_;
|
|
Maybe<EvaluationResult> result_;
|
|
|
|
Maybe<std::string> GetPendingExceptionAsString() {
|
|
if (!isolate_->has_pending_exception()) return Nothing<std::string>();
|
|
Handle<Object> exception(isolate_->pending_exception(), isolate_);
|
|
isolate_->clear_pending_exception();
|
|
|
|
Handle<String> exception_string;
|
|
if (!Object::ToString(isolate_, exception).ToHandle(&exception_string)) {
|
|
return Just<std::string>("");
|
|
}
|
|
return Just<std::string>(exception_string->ToCString().get());
|
|
}
|
|
|
|
void BreakProgramRequested(v8::Local<v8::Context> paused_context,
|
|
const std::vector<int>&) override {
|
|
// Check the current position.
|
|
StackTraceFrameIterator frame_it(isolate_);
|
|
|
|
FrameSummary::WasmInterpretedFrameSummary summary =
|
|
FrameSummary::GetTop(frame_it.frame()).AsWasmInterpreted();
|
|
Handle<WasmInstanceObject> instance = summary.wasm_instance();
|
|
WasmInterpreter::FramePtr frame =
|
|
instance->debug_info().GetInterpretedFrame(frame_it.frame()->fp(), 0);
|
|
|
|
MaybeHandle<String> result_handle = v8::internal::wasm::DebugEvaluate(
|
|
{evaluator_bytes_.begin(), evaluator_bytes_.size()}, instance,
|
|
std::move(frame));
|
|
|
|
Maybe<std::string> error_message = GetPendingExceptionAsString();
|
|
Maybe<std::string> result_message =
|
|
result_handle.is_null()
|
|
? Nothing<std::string>()
|
|
: Just<std::string>(
|
|
result_handle.ToHandleChecked()->ToCString().get());
|
|
|
|
isolate_->clear_pending_exception();
|
|
result_ = Just<EvaluationResult>({result_message, error_message});
|
|
}
|
|
};
|
|
|
|
WASM_COMPILED_EXEC_TEST(WasmDebugEvaluate_CompileFailed) {
|
|
WasmRunner<int> runner(execution_tier);
|
|
|
|
TestCode<int> code(&runner, {WASM_RETURN1(WASM_I32V_1(32))});
|
|
code.BreakOnReturn(&runner);
|
|
|
|
WasmEvaluatorBuilder evaluator(execution_tier);
|
|
// Create a module that doesn't compile by missing the END bytecode
|
|
evaluator.push_back({WASM_RETURN1(WASM_I32V_1(33))});
|
|
|
|
Isolate* isolate = runner.main_isolate();
|
|
WasmBreakHandler break_handler(isolate, evaluator.bytes());
|
|
CHECK(!code.Run(&runner).is_null());
|
|
|
|
WasmBreakHandler::EvaluationResult result =
|
|
break_handler.result().ToChecked();
|
|
CHECK(result.result.IsNothing());
|
|
CHECK_NE(result.error.ToChecked().find(
|
|
"function body must end with \"end\" opcode"),
|
|
std::string::npos);
|
|
}
|
|
|
|
WASM_COMPILED_EXEC_TEST(WasmDebugEvaluate_MissingEntrypoint) {
|
|
WasmRunner<int> runner(execution_tier);
|
|
|
|
TestCode<int> code(&runner, {WASM_RETURN1(WASM_I32V_1(32))});
|
|
code.BreakOnReturn(&runner);
|
|
|
|
v8::internal::AccountingAllocator allocator;
|
|
Zone zone(&allocator, ZONE_NAME);
|
|
WasmModuleBuilder evaluator(&zone);
|
|
ZoneBuffer evaluator_bytes(&zone);
|
|
evaluator.WriteTo(&evaluator_bytes);
|
|
|
|
Isolate* isolate = runner.main_isolate();
|
|
WasmBreakHandler break_handler(isolate, std::move(evaluator_bytes));
|
|
CHECK(!code.Run(&runner).is_null());
|
|
|
|
WasmBreakHandler::EvaluationResult result =
|
|
break_handler.result().ToChecked();
|
|
CHECK(result.result.IsNothing());
|
|
CHECK_NE(result.error.ToChecked().find("Missing export: \"wasm_format\""),
|
|
std::string::npos);
|
|
}
|
|
|
|
WASM_COMPILED_EXEC_TEST(WasmDebugEvaluate_ExecuteFailed_SEGV) {
|
|
WasmRunner<int> runner(execution_tier);
|
|
runner.builder().AddMemoryElems<int32_t>(64);
|
|
|
|
TestCode<int> code(&runner, {WASM_RETURN1(WASM_I32V_1(32))});
|
|
|
|
// Create a module that doesn't compile by missing the END bytecode
|
|
WasmEvaluatorBuilder evaluator(execution_tier);
|
|
code.BreakOnReturn(&runner);
|
|
|
|
// Load 1 byte from an address that's too high
|
|
evaluator.CallGetMemory(
|
|
{WASM_I32V_1(32), WASM_I32V_1(1), WASM_I32V_3((1 << 16) + 1)});
|
|
evaluator.push_back({WASM_RETURN1(WASM_I32V_1(33)), WASM_END});
|
|
|
|
Isolate* isolate = runner.main_isolate();
|
|
WasmBreakHandler break_handler(isolate, evaluator.bytes());
|
|
CHECK(!code.Run(&runner).is_null());
|
|
|
|
WasmBreakHandler::EvaluationResult result =
|
|
break_handler.result().ToChecked();
|
|
CHECK(result.result.IsNothing());
|
|
CHECK_NE(
|
|
result.error.ToChecked().find("Illegal access to out-of-bounds memory"),
|
|
std::string::npos);
|
|
}
|
|
|
|
WASM_COMPILED_EXEC_TEST(WasmDebugEvaluate_LinearMemory) {
|
|
WasmRunner<int> runner(execution_tier);
|
|
runner.builder().AddMemoryElems<int32_t>(64);
|
|
|
|
TestCode<int> code(
|
|
&runner,
|
|
{WASM_STORE_MEM(MachineType::Int32(), WASM_I32V_1(32), WASM_I32V_2('A')),
|
|
WASM_RETURN1(WASM_LOAD_MEM(MachineType::Int32(), WASM_I32V_1(32)))});
|
|
code.BreakOnReturn(&runner);
|
|
|
|
WasmEvaluatorBuilder evaluator(execution_tier);
|
|
// Load 4 bytes from debuggee memory at address 32, and store at the offset 33
|
|
// of the linear memory.
|
|
evaluator.CallGetMemory({WASM_I32V_1(32), WASM_I32V_1(4), WASM_I32V_1(33)});
|
|
evaluator.push_back({WASM_RETURN1(WASM_I32V_1(33)), WASM_END});
|
|
|
|
Isolate* isolate = runner.main_isolate();
|
|
WasmBreakHandler break_handler(isolate, evaluator.bytes());
|
|
CHECK(!code.Run(&runner).is_null());
|
|
|
|
WasmBreakHandler::EvaluationResult result =
|
|
break_handler.result().ToChecked();
|
|
CHECK(result.error.IsNothing());
|
|
CHECK_EQ(result.result.ToChecked(), "A");
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace wasm
|
|
} // namespace internal
|
|
} // namespace v8
|