Commit ce443bf5 authored by Philip Pfaffe's avatar Philip Pfaffe Committed by Commit Bot

[wasm debugging] Implement __getOperand and __getGlobal

This CL implements two additional evaluator module proxy operations for
accessing globals and values on the wasm operand stack.

Drive-By: Also fix how the breakpoint position is computed in the evalutor
tests.

Bug: chromium:1020120
Change-Id: I161768da9e12586b2c710f5b26922b9600527814
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2282526
Commit-Queue: Philip Pfaffe <pfaffe@chromium.org>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#68704}
parent 6ed44953
......@@ -124,57 +124,73 @@ class DebugEvaluatorProxy {
}
static void SbrkTrampoline(const v8::FunctionCallbackInfo<v8::Value>& args) {
auto& proxy = GetProxy(args);
DebugEvaluatorProxy& proxy = GetProxy(args);
uint32_t size = proxy.GetArgAsUInt32(args, 0);
uint32_t result = proxy.Sbrk(size);
args.GetReturnValue().Set(result);
}
template <typename T>
void write_result(const WasmValue& result, uint32_t result_offset) {
wasm::ScheduledErrorThrower thrower(isolate_, "debug evaluate proxy");
T val = result.to<T>();
static_assert(static_cast<uint32_t>(sizeof(T)) == sizeof(T),
"Unexpected size");
if (CheckRangeOutOfBounds(result_offset, sizeof(T),
evaluator_->memory_size(), &thrower)) {
return;
}
memcpy(&evaluator_->memory_start()[result_offset], &val, sizeof(T));
}
// void __getLocal(uint32_t local, void* result);
void GetLocal(uint32_t local, uint32_t result_offset) {
WasmValue result = LoadLocalValue(local);
DCHECK(frame_->is_wasm());
wasm::DebugInfo* debug_info =
WasmFrame::cast(frame_)->native_module()->GetDebugInfo();
WasmValue result = debug_info->GetLocalValue(
local, frame_->pc(), frame_->fp(), frame_->callee_fp());
WriteResult(result, result_offset);
}
switch (result.type().kind()) {
case ValueType::kI32:
write_result<uint32_t>(result, result_offset);
break;
case ValueType::kI64:
write_result<int64_t>(result, result_offset);
break;
case ValueType::kF32:
write_result<float>(result, result_offset);
break;
case ValueType::kF64:
write_result<double>(result, result_offset);
break;
default:
UNIMPLEMENTED();
}
void GetGlobal(uint32_t global, uint32_t result_offset) {
DCHECK(frame_->is_wasm());
WasmGlobal global_variable =
WasmFrame::cast(frame_)->native_module()->module()->globals.at(global);
Handle<WasmInstanceObject> instance(
WasmFrame::cast(frame_)->wasm_instance(), isolate_);
WasmValue result =
WasmInstanceObject::GetGlobalValue(instance, global_variable);
WriteResult(result, result_offset);
}
void GetOperand(uint32_t operand, uint32_t result_offset) {
DCHECK(frame_->is_wasm());
wasm::DebugInfo* debug_info =
WasmFrame::cast(frame_)->native_module()->GetDebugInfo();
WasmValue result = debug_info->GetStackValue(
operand, frame_->pc(), frame_->fp(), frame_->callee_fp());
WriteResult(result, result_offset);
}
static void GetLocalTrampoline(
const v8::FunctionCallbackInfo<v8::Value>& args) {
auto& proxy = GetProxy(args);
DebugEvaluatorProxy& proxy = GetProxy(args);
uint32_t local = proxy.GetArgAsUInt32(args, 0);
uint32_t result = proxy.GetArgAsUInt32(args, 1);
proxy.GetLocal(local, result);
}
static void GetGlobalTrampoline(
const v8::FunctionCallbackInfo<v8::Value>& args) {
DebugEvaluatorProxy& proxy = GetProxy(args);
uint32_t global = proxy.GetArgAsUInt32(args, 0);
uint32_t result = proxy.GetArgAsUInt32(args, 1);
proxy.GetGlobal(global, result);
}
static void GetOperandTrampoline(
const v8::FunctionCallbackInfo<v8::Value>& args) {
DebugEvaluatorProxy& proxy = GetProxy(args);
uint32_t operand = proxy.GetArgAsUInt32(args, 0);
uint32_t result = proxy.GetArgAsUInt32(args, 1);
proxy.GetOperand(operand, result);
}
Handle<JSObject> CreateImports() {
Handle<JSObject> imports_obj =
isolate_->factory()->NewJSObject(isolate_->object_function());
......@@ -184,6 +200,10 @@ class DebugEvaluatorProxy {
import_module_obj)
.Assert();
AddImport(import_module_obj, "__getOperand",
DebugEvaluatorProxy::GetOperandTrampoline);
AddImport(import_module_obj, "__getGlobal",
DebugEvaluatorProxy::GetGlobalTrampoline);
AddImport(import_module_obj, "__getLocal",
DebugEvaluatorProxy::GetLocalTrampoline);
AddImport(import_module_obj, "__getMemory",
......@@ -200,12 +220,35 @@ class DebugEvaluatorProxy {
}
private:
WasmValue LoadLocalValue(uint32_t local) {
DCHECK(frame_->is_wasm());
wasm::DebugInfo* debug_info =
WasmFrame::cast(frame_)->native_module()->GetDebugInfo();
return debug_info->GetLocalValue(local, frame_->pc(), frame_->fp(),
frame_->callee_fp());
template <typename T>
void WriteResultImpl(const WasmValue& result, uint32_t result_offset) {
wasm::ScheduledErrorThrower thrower(isolate_, "debug evaluate proxy");
T val = result.to<T>();
STATIC_ASSERT(static_cast<uint32_t>(sizeof(T)) == sizeof(T));
if (CheckRangeOutOfBounds(result_offset, sizeof(T),
evaluator_->memory_size(), &thrower)) {
return;
}
memcpy(&evaluator_->memory_start()[result_offset], &val, sizeof(T));
}
void WriteResult(const WasmValue& result, uint32_t result_offset) {
switch (result.type().kind()) {
case ValueType::kI32:
WriteResultImpl<uint32_t>(result, result_offset);
break;
case ValueType::kI64:
WriteResultImpl<int64_t>(result, result_offset);
break;
case ValueType::kF32:
WriteResultImpl<float>(result, result_offset);
break;
case ValueType::kF64:
WriteResultImpl<double>(result, result_offset);
break;
default:
UNIMPLEMENTED();
}
}
uint32_t GetArgAsUInt32(const v8::FunctionCallbackInfo<v8::Value>& args,
......@@ -233,7 +276,7 @@ class DebugEvaluatorProxy {
v8::External::New(api_isolate, this))
.ToLocalChecked();
auto wrapped_function = Utils::OpenHandle(*v8_function);
Handle<JSReceiver> wrapped_function = Utils::OpenHandle(*v8_function);
Object::SetProperty(isolate_, import_module_obj,
V8String(isolate_, function_name), wrapped_function)
......@@ -264,6 +307,16 @@ static bool VerifyEvaluatorInterface(const WasmModule* raw_module,
thrower)) {
continue;
}
} else if (field_name == "__getOperand") {
// void __getOperand(uint32_t local, void* result)
if (CheckSignature(kWasmBottom, {kWasmI32, kWasmI32}, F.sig, thrower)) {
continue;
}
} else if (field_name == "__getGlobal") {
// void __getGlobal(uint32_t local, void* result)
if (CheckSignature(kWasmBottom, {kWasmI32, kWasmI32}, F.sig, thrower)) {
continue;
}
} else if (field_name == "__getLocal") {
// void __getLocal(uint32_t local, void* result)
if (CheckSignature(kWasmBottom, {kWasmI32, kWasmI32}, F.sig, thrower)) {
......
......@@ -46,7 +46,7 @@ class TestCode {
std::initializer_list<ValueType::Kind> locals = {})
: compiler_(&runner->NewFunction<FunctionArgsT...>()),
code_(code),
locals_(static_cast<uint32_t>(locals.size())) {
locals_(static_cast<int32_t>(locals.size())) {
for (ValueType::Kind T : locals) {
compiler_->AllocateLocal(ValueType::Primitive(T));
}
......@@ -55,7 +55,10 @@ class TestCode {
Handle<BreakPoint> BreakOnReturn(WasmRunnerBase* runner) {
runner->TierDown();
uint32_t return_offset_in_function = locals_ + FindReturn();
uint32_t return_idx = FindReturn();
uint32_t return_offset_in_function =
static_cast<uint32_t>(LEBHelper::sizeof_i32v(locals_)) + 2 * locals_ +
return_idx;
int function_index = compiler_->function_index();
int function_offset =
......@@ -69,8 +72,13 @@ class TestCode {
Handle<BreakPoint> break_point =
runner->main_isolate()->factory()->NewBreakPoint(
break_index++, runner->main_isolate()->factory()->empty_string());
int expected_breakpoint_position = return_offset_in_module;
CHECK(WasmScript::SetBreakPoint(script, &return_offset_in_module,
break_point));
// Check that the breakpoint doesn't slide
DCHECK_EQ(expected_breakpoint_position, return_offset_in_module);
USE(expected_breakpoint_position);
return break_point;
}
......@@ -95,7 +103,7 @@ class TestCode {
WasmFunctionCompiler* compiler_;
std::vector<byte> code_;
uint32_t locals_;
int32_t locals_;
};
class WasmEvaluatorBuilder {
......@@ -108,6 +116,10 @@ class WasmEvaluatorBuilder {
CStrVector("__getMemory"));
get_local_function_index =
AddImport<void, uint32_t, uint32_t>(CStrVector("__getLocal"));
get_global_function_index =
AddImport<void, uint32_t, uint32_t>(CStrVector("__getGlobal"));
get_operand_function_index =
AddImport<void, uint32_t, uint32_t>(CStrVector("__getOperand"));
sbrk_function_index = AddImport<uint32_t, uint32_t>(CStrVector("__sbrk"));
wasm_format_function =
builder_.AddFunction(WasmRunnerBase::CreateSig<uint32_t>(&zone_));
......@@ -134,6 +146,16 @@ class WasmEvaluatorBuilder {
push_back({WASM_CALL_FUNCTION0(sbrk_function_index)});
}
void CallGetOperand(std::initializer_list<byte> args) {
push_back(args);
push_back({WASM_CALL_FUNCTION0(get_operand_function_index)});
}
void CallGetGlobal(std::initializer_list<byte> args) {
push_back(args);
push_back({WASM_CALL_FUNCTION0(get_global_function_index)});
}
void CallGetLocal(std::initializer_list<byte> args) {
push_back(args);
push_back({WASM_CALL_FUNCTION0(get_local_function_index)});
......@@ -156,6 +178,8 @@ class WasmEvaluatorBuilder {
WasmModuleBuilder builder_;
uint32_t get_memory_function_index = 0;
uint32_t get_local_function_index = 0;
uint32_t get_global_function_index = 0;
uint32_t get_operand_function_index = 0;
uint32_t sbrk_function_index = 0;
WasmFunctionBuilder* wasm_format_function = nullptr;
};
......@@ -373,6 +397,58 @@ WASM_COMPILED_EXEC_TEST(WasmDebugEvaluate_Locals) {
CHECK_EQ(result.result.ToChecked(), "A");
}
WASM_COMPILED_EXEC_TEST(WasmDebugEvaluate_Globals) {
WasmRunner<int> runner(execution_tier);
runner.builder().AddMemoryElems<int32_t>(64);
runner.builder().AddGlobal<int32_t>();
runner.builder().AddGlobal<int32_t>();
TestCode<void> code(&runner,
{WASM_SET_GLOBAL(0, WASM_I32V_1('4')),
WASM_SET_GLOBAL(1, WASM_I32V_1('5')), WASM_RETURN0},
{});
code.BreakOnReturn(&runner);
WasmEvaluatorBuilder evaluator(execution_tier);
evaluator.CallGetGlobal({WASM_I32V_1(0), WASM_I32V_1(33)});
evaluator.CallGetGlobal({WASM_I32V_1(1), WASM_I32V_1(34)});
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(), "45");
}
WASM_COMPILED_EXEC_TEST(WasmDebugEvaluate_Operands) {
WasmRunner<int> runner(execution_tier);
runner.builder().AddMemoryElems<int32_t>(64);
TestCode<int> code(&runner,
{WASM_SET_LOCAL(0, WASM_I32V_1('4')), WASM_GET_LOCAL(0),
WASM_RETURN1(WASM_I32V_1('5'))},
{ValueType::kI32});
code.BreakOnReturn(&runner);
WasmEvaluatorBuilder evaluator(execution_tier);
evaluator.CallGetOperand({WASM_I32V_1(0), WASM_I32V_1(33)});
evaluator.CallGetOperand({WASM_I32V_1(1), WASM_I32V_1(34)});
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(), "45");
}
} // namespace
} // namespace wasm
} // namespace internal
......
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