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 { ...@@ -124,57 +124,73 @@ class DebugEvaluatorProxy {
} }
static void SbrkTrampoline(const v8::FunctionCallbackInfo<v8::Value>& args) { 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 size = proxy.GetArgAsUInt32(args, 0);
uint32_t result = proxy.Sbrk(size); uint32_t result = proxy.Sbrk(size);
args.GetReturnValue().Set(result); 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, void* result);
void GetLocal(uint32_t local, uint32_t result_offset) { 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()) { void GetGlobal(uint32_t global, uint32_t result_offset) {
case ValueType::kI32: DCHECK(frame_->is_wasm());
write_result<uint32_t>(result, result_offset);
break; WasmGlobal global_variable =
case ValueType::kI64: WasmFrame::cast(frame_)->native_module()->module()->globals.at(global);
write_result<int64_t>(result, result_offset);
break; Handle<WasmInstanceObject> instance(
case ValueType::kF32: WasmFrame::cast(frame_)->wasm_instance(), isolate_);
write_result<float>(result, result_offset); WasmValue result =
break; WasmInstanceObject::GetGlobalValue(instance, global_variable);
case ValueType::kF64: WriteResult(result, result_offset);
write_result<double>(result, result_offset); }
break;
default: void GetOperand(uint32_t operand, uint32_t result_offset) {
UNIMPLEMENTED(); 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( static void GetLocalTrampoline(
const v8::FunctionCallbackInfo<v8::Value>& args) { const v8::FunctionCallbackInfo<v8::Value>& args) {
auto& proxy = GetProxy(args); DebugEvaluatorProxy& proxy = GetProxy(args);
uint32_t local = proxy.GetArgAsUInt32(args, 0); uint32_t local = proxy.GetArgAsUInt32(args, 0);
uint32_t result = proxy.GetArgAsUInt32(args, 1); uint32_t result = proxy.GetArgAsUInt32(args, 1);
proxy.GetLocal(local, result); 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> CreateImports() {
Handle<JSObject> imports_obj = Handle<JSObject> imports_obj =
isolate_->factory()->NewJSObject(isolate_->object_function()); isolate_->factory()->NewJSObject(isolate_->object_function());
...@@ -184,6 +200,10 @@ class DebugEvaluatorProxy { ...@@ -184,6 +200,10 @@ class DebugEvaluatorProxy {
import_module_obj) import_module_obj)
.Assert(); .Assert();
AddImport(import_module_obj, "__getOperand",
DebugEvaluatorProxy::GetOperandTrampoline);
AddImport(import_module_obj, "__getGlobal",
DebugEvaluatorProxy::GetGlobalTrampoline);
AddImport(import_module_obj, "__getLocal", AddImport(import_module_obj, "__getLocal",
DebugEvaluatorProxy::GetLocalTrampoline); DebugEvaluatorProxy::GetLocalTrampoline);
AddImport(import_module_obj, "__getMemory", AddImport(import_module_obj, "__getMemory",
...@@ -200,12 +220,35 @@ class DebugEvaluatorProxy { ...@@ -200,12 +220,35 @@ class DebugEvaluatorProxy {
} }
private: private:
WasmValue LoadLocalValue(uint32_t local) { template <typename T>
DCHECK(frame_->is_wasm()); void WriteResultImpl(const WasmValue& result, uint32_t result_offset) {
wasm::DebugInfo* debug_info = wasm::ScheduledErrorThrower thrower(isolate_, "debug evaluate proxy");
WasmFrame::cast(frame_)->native_module()->GetDebugInfo(); T val = result.to<T>();
return debug_info->GetLocalValue(local, frame_->pc(), frame_->fp(), STATIC_ASSERT(static_cast<uint32_t>(sizeof(T)) == sizeof(T));
frame_->callee_fp()); 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, uint32_t GetArgAsUInt32(const v8::FunctionCallbackInfo<v8::Value>& args,
...@@ -233,7 +276,7 @@ class DebugEvaluatorProxy { ...@@ -233,7 +276,7 @@ class DebugEvaluatorProxy {
v8::External::New(api_isolate, this)) v8::External::New(api_isolate, this))
.ToLocalChecked(); .ToLocalChecked();
auto wrapped_function = Utils::OpenHandle(*v8_function); Handle<JSReceiver> wrapped_function = Utils::OpenHandle(*v8_function);
Object::SetProperty(isolate_, import_module_obj, Object::SetProperty(isolate_, import_module_obj,
V8String(isolate_, function_name), wrapped_function) V8String(isolate_, function_name), wrapped_function)
...@@ -264,6 +307,16 @@ static bool VerifyEvaluatorInterface(const WasmModule* raw_module, ...@@ -264,6 +307,16 @@ static bool VerifyEvaluatorInterface(const WasmModule* raw_module,
thrower)) { thrower)) {
continue; 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") { } else if (field_name == "__getLocal") {
// void __getLocal(uint32_t local, void* result) // void __getLocal(uint32_t local, void* result)
if (CheckSignature(kWasmBottom, {kWasmI32, kWasmI32}, F.sig, thrower)) { if (CheckSignature(kWasmBottom, {kWasmI32, kWasmI32}, F.sig, thrower)) {
......
...@@ -46,7 +46,7 @@ class TestCode { ...@@ -46,7 +46,7 @@ class TestCode {
std::initializer_list<ValueType::Kind> locals = {}) std::initializer_list<ValueType::Kind> locals = {})
: compiler_(&runner->NewFunction<FunctionArgsT...>()), : compiler_(&runner->NewFunction<FunctionArgsT...>()),
code_(code), code_(code),
locals_(static_cast<uint32_t>(locals.size())) { locals_(static_cast<int32_t>(locals.size())) {
for (ValueType::Kind T : locals) { for (ValueType::Kind T : locals) {
compiler_->AllocateLocal(ValueType::Primitive(T)); compiler_->AllocateLocal(ValueType::Primitive(T));
} }
...@@ -55,7 +55,10 @@ class TestCode { ...@@ -55,7 +55,10 @@ class TestCode {
Handle<BreakPoint> BreakOnReturn(WasmRunnerBase* runner) { Handle<BreakPoint> BreakOnReturn(WasmRunnerBase* runner) {
runner->TierDown(); 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_index = compiler_->function_index();
int function_offset = int function_offset =
...@@ -69,8 +72,13 @@ class TestCode { ...@@ -69,8 +72,13 @@ class TestCode {
Handle<BreakPoint> break_point = Handle<BreakPoint> break_point =
runner->main_isolate()->factory()->NewBreakPoint( runner->main_isolate()->factory()->NewBreakPoint(
break_index++, runner->main_isolate()->factory()->empty_string()); break_index++, runner->main_isolate()->factory()->empty_string());
int expected_breakpoint_position = return_offset_in_module;
CHECK(WasmScript::SetBreakPoint(script, &return_offset_in_module, CHECK(WasmScript::SetBreakPoint(script, &return_offset_in_module,
break_point)); 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; return break_point;
} }
...@@ -95,7 +103,7 @@ class TestCode { ...@@ -95,7 +103,7 @@ class TestCode {
WasmFunctionCompiler* compiler_; WasmFunctionCompiler* compiler_;
std::vector<byte> code_; std::vector<byte> code_;
uint32_t locals_; int32_t locals_;
}; };
class WasmEvaluatorBuilder { class WasmEvaluatorBuilder {
...@@ -108,6 +116,10 @@ class WasmEvaluatorBuilder { ...@@ -108,6 +116,10 @@ class WasmEvaluatorBuilder {
CStrVector("__getMemory")); CStrVector("__getMemory"));
get_local_function_index = get_local_function_index =
AddImport<void, uint32_t, uint32_t>(CStrVector("__getLocal")); 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")); sbrk_function_index = AddImport<uint32_t, uint32_t>(CStrVector("__sbrk"));
wasm_format_function = wasm_format_function =
builder_.AddFunction(WasmRunnerBase::CreateSig<uint32_t>(&zone_)); builder_.AddFunction(WasmRunnerBase::CreateSig<uint32_t>(&zone_));
...@@ -134,6 +146,16 @@ class WasmEvaluatorBuilder { ...@@ -134,6 +146,16 @@ class WasmEvaluatorBuilder {
push_back({WASM_CALL_FUNCTION0(sbrk_function_index)}); 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) { void CallGetLocal(std::initializer_list<byte> args) {
push_back(args); push_back(args);
push_back({WASM_CALL_FUNCTION0(get_local_function_index)}); push_back({WASM_CALL_FUNCTION0(get_local_function_index)});
...@@ -156,6 +178,8 @@ class WasmEvaluatorBuilder { ...@@ -156,6 +178,8 @@ class WasmEvaluatorBuilder {
WasmModuleBuilder builder_; WasmModuleBuilder builder_;
uint32_t get_memory_function_index = 0; uint32_t get_memory_function_index = 0;
uint32_t get_local_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; uint32_t sbrk_function_index = 0;
WasmFunctionBuilder* wasm_format_function = nullptr; WasmFunctionBuilder* wasm_format_function = nullptr;
}; };
...@@ -373,6 +397,58 @@ WASM_COMPILED_EXEC_TEST(WasmDebugEvaluate_Locals) { ...@@ -373,6 +397,58 @@ WASM_COMPILED_EXEC_TEST(WasmDebugEvaluate_Locals) {
CHECK_EQ(result.result.ToChecked(), "A"); 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
} // namespace wasm } // namespace wasm
} // namespace internal } // 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