-
Notifications
You must be signed in to change notification settings - Fork 11.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
VM 2 Tracing #20848
base: tzakian/vm-rebase1
Are you sure you want to change the base?
VM 2 Tracing #20848
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,7 @@ use crate::{ | |
set_err_info, | ||
state::{CallStack, MachineState, ResolvableType}, | ||
}, | ||
tracing::{trace, tracer::VMTracer}, | ||
values::{ | ||
IntegerValue, Reference, Struct, StructRef, VMValueCast, Value, Variant, VariantRef, | ||
Vector, VectorRef, | ||
|
@@ -43,13 +44,15 @@ enum StepStatus { | |
Done, | ||
} | ||
|
||
struct RunContext<'vm_cache, 'native, 'native_lifetimes> { | ||
struct RunContext<'vm_cache, 'native, 'native_lifetimes, 'tracer, 'trace_builder> { | ||
vtables: &'vm_cache VMDispatchTables, | ||
vm_config: Arc<VMConfig>, | ||
extensions: &'native mut NativeContextExtensions<'native_lifetimes>, | ||
// TODO: consider making this `Option<&mut VMTracer<'_>>` and passing it like that everywhere? | ||
tracer: &'tracer mut Option<VMTracer<'trace_builder>>, | ||
} | ||
|
||
impl RunContext<'_, '_, '_> { | ||
impl RunContext<'_, '_, '_, '_, '_> { | ||
// TODO: The Run Context should hold this, not go get it from the Loader. | ||
fn vm_config(&self) -> &VMConfig { | ||
&self.vm_config | ||
|
@@ -66,12 +69,14 @@ pub(super) fn run( | |
vtables: &VMDispatchTables, | ||
vm_config: Arc<VMConfig>, | ||
extensions: &mut NativeContextExtensions, | ||
tracer: &mut Option<VMTracer<'_>>, | ||
gas_meter: &mut impl GasMeter, | ||
) -> VMResult<Vec<Value>> { | ||
let mut run_context = RunContext { | ||
extensions, | ||
vtables, | ||
vm_config, | ||
tracer, | ||
}; | ||
|
||
let mut state = start_state; | ||
|
@@ -137,6 +142,13 @@ fn step( | |
}); | ||
|
||
profile_open_instr!(gas_meter, format!("{:?}", instruction)); | ||
trace(run_context.tracer, |tracer| { | ||
tracer.start_instruction( | ||
run_context.vtables, | ||
state, | ||
&gas_meter.remaining_gas().into(), | ||
) | ||
}); | ||
dbg_println!(flag: eval_step, "Instruction: {instruction:?}"); | ||
// These are split out because `PartialVMError` and `VMError` are different types. It's unclear | ||
// why as they hold identical data, but if we could combine them, we could entirely inline | ||
|
@@ -145,6 +157,14 @@ fn step( | |
Bytecode::Ret => { | ||
let charge_result = gas_meter.charge_simple_instr(SimpleInstruction::Ret); | ||
profile_close_instr!(gas_meter, format!("{:?}", instruction)); | ||
trace(run_context.tracer, |tracer| { | ||
tracer.end_instruction( | ||
run_context.vtables, | ||
state, | ||
&gas_meter.remaining_gas().into(), | ||
None, | ||
) | ||
}); | ||
|
||
partial_error_to_error(state, run_context, charge_result)?; | ||
let non_ref_vals = state | ||
|
@@ -161,9 +181,17 @@ fn step( | |
|
||
profile_close_frame!( | ||
gas_meter, | ||
arena::to_ref(current_frame.function).pretty_string() | ||
state.call_stack.current_frame.function().pretty_string() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is a bug in the current tracer as well, but I think above you'll need to do something like let err = gas_meter.charge_drop_frame(...).map_err(|e| ..);
profile_close_frame!( ...);
trace(..., err.as_ref().err());
err?; Otherwise if the charge for the drop frame causes an OOG you wont see it in the trace. |
||
); | ||
|
||
trace(run_context.tracer, |tracer| { | ||
tracer.exit_frame( | ||
run_context.vtables, | ||
state, | ||
&gas_meter.remaining_gas().into(), | ||
state.call_stack.current_frame.function(), | ||
None, | ||
) | ||
}); | ||
if state.can_pop_call_frame() { | ||
state.pop_call_frame()?; | ||
// Note: the caller will find the callee's return values at the top of the shared operand stack | ||
|
@@ -176,6 +204,14 @@ fn step( | |
} | ||
Bytecode::CallGeneric(idx) => { | ||
profile_close_instr!(gas_meter, format!("{:?}", instruction)); | ||
trace(run_context.tracer, |tracer| { | ||
tracer.end_instruction( | ||
run_context.vtables, | ||
state, | ||
&gas_meter.remaining_gas().into(), | ||
None, | ||
) | ||
}); | ||
let ty_args = state | ||
.call_stack | ||
.current_frame | ||
|
@@ -194,6 +230,14 @@ fn step( | |
} | ||
Bytecode::VirtualCall(vtable_key) => { | ||
profile_close_instr!(gas_meter, format!("{:?}", instruction)); | ||
trace(run_context.tracer, |tracer| { | ||
tracer.end_instruction( | ||
run_context.vtables, | ||
state, | ||
&gas_meter.remaining_gas().into(), | ||
None, | ||
) | ||
}); | ||
let function = run_context | ||
.vtables | ||
.resolve_function(vtable_key) | ||
|
@@ -203,11 +247,27 @@ fn step( | |
} | ||
Bytecode::DirectCall(function) => { | ||
profile_close_instr!(gas_meter, format!("{:?}", instruction)); | ||
trace(run_context.tracer, |tracer| { | ||
tracer.end_instruction( | ||
run_context.vtables, | ||
state, | ||
&gas_meter.remaining_gas().into(), | ||
None, | ||
) | ||
}); | ||
call_function(state, run_context, gas_meter, *function, vec![])?; | ||
Ok(StepStatus::Running) | ||
} | ||
_ => { | ||
let step_result = op_step_impl(state, run_context, gas_meter, instruction); | ||
trace(run_context.tracer, |tracer| { | ||
tracer.end_instruction( | ||
run_context.vtables, | ||
state, | ||
&gas_meter.remaining_gas().into(), | ||
step_result.as_ref().err(), | ||
) | ||
}); | ||
partial_error_to_error(state, run_context, step_result)?; | ||
Ok(StepStatus::Running) | ||
} | ||
|
@@ -883,7 +943,16 @@ fn call_function( | |
ty_args: Vec<Type>, | ||
) -> VMResult<()> { | ||
let fun_ref = function.to_ref(); | ||
profile_open_frame!(gas_meter, func_name.clone()); | ||
trace(run_context.tracer, |tracer| { | ||
tracer.enter_frame( | ||
run_context.vtables, | ||
state, | ||
&gas_meter.remaining_gas().into(), | ||
fun_ref, | ||
&ty_args, | ||
) | ||
}); | ||
profile_open_frame!(gas_meter, fun_ref.name().to_string()); | ||
|
||
// Charge gas | ||
let module_id = fun_ref.module_id(); | ||
|
@@ -918,11 +987,22 @@ fn call_function( | |
} | ||
|
||
if fun_ref.is_native() { | ||
call_native(state, run_context, gas_meter, fun_ref, ty_args)?; | ||
let native_result = call_native(state, run_context, gas_meter, fun_ref, ty_args); | ||
|
||
state.call_stack.current_frame.pc += 1; // advance past the Call instruction in the caller | ||
// NB: Pass any error into the tracer before raising it. | ||
trace(run_context.tracer, |tracer| { | ||
tracer.exit_frame( | ||
run_context.vtables, | ||
state, | ||
&gas_meter.remaining_gas().into(), | ||
function.to_ref(), | ||
native_result.as_ref().err(), | ||
) | ||
}); | ||
|
||
profile_close_frame!(gas_meter, func_name.clone()); | ||
native_result?; | ||
state.call_stack.current_frame.pc += 1; // advance past the Call instruction in the caller | ||
profile_close_frame!(gas_meter, fun_ref.name().to_string()); | ||
} else { | ||
// Note: the caller will find the callee's return values at the top of the shared | ||
// operand stack when the new frame returns. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ use crate::{ | |
execution::{ | ||
dispatch_tables::VMDispatchTables, | ||
interpreter::state::{CallStack, MachineState, ModuleDefinitionResolver}, | ||
tracing::trace, | ||
values::Value, | ||
}, | ||
jit::execution::ast::{Function, Type}, | ||
|
@@ -23,19 +24,29 @@ pub(crate) mod state; | |
/// Entrypoint into the interpreter. All external calls need to be routed through this | ||
/// function. | ||
pub(crate) fn run( | ||
function: VMPointer<Function>, | ||
ty_args: Vec<Type>, | ||
args: Vec<Value>, | ||
vtables: &VMDispatchTables, | ||
vm_config: Arc<VMConfig>, | ||
extensions: &mut NativeContextExtensions, | ||
tracer: &mut Option<VMTracer<'_>>, | ||
gas_meter: &mut impl GasMeter, | ||
function: VMPointer<Function>, | ||
ty_args: Vec<Type>, | ||
args: Vec<Value>, | ||
) -> VMResult<Vec<Value>> { | ||
let fun_ref = function.to_ref(); | ||
trace(tracer, |tracer| { | ||
tracer.enter_initial_frame( | ||
vtables, | ||
&gas_meter.remaining_gas().into(), | ||
function.ptr_clone().to_ref(), | ||
&ty_args, | ||
&args, | ||
) | ||
}); | ||
profile_open_frame!(gas_meter, fun_ref.pretty_string()); | ||
|
||
if fun_ref.is_native() { | ||
let return_values = eval::call_native_with_args( | ||
let return_result = eval::call_native_with_args( | ||
None, | ||
vtables, | ||
gas_meter, | ||
|
@@ -48,11 +59,12 @@ pub(crate) fn run( | |
.map_err(|e| { | ||
e.at_code_offset(fun_ref.index(), 0) | ||
.finish(Location::Module(fun_ref.module_id().clone())) | ||
})?; | ||
|
||
}); | ||
trace(tracer, |tracer| { | ||
tracer.exit_initial_native_frame(&return_result, &gas_meter.remaining_gas().into()) | ||
}); | ||
profile_close_frame!(gas_meter, fun_ref.pretty_string()); | ||
|
||
Ok(return_values.into_iter().collect()) | ||
return_result.map(|values| values.into_iter().collect()) | ||
} else { | ||
let module_id = function.to_ref().module_id(); | ||
let resolver = ModuleDefinitionResolver::new(vtables, module_id) | ||
|
@@ -62,7 +74,7 @@ pub(crate) fn run( | |
.finish(Location::Module(fun_ref.module_id().clone())) | ||
})?; | ||
let state = MachineState::new(call_stack); | ||
eval::run(state, vtables, vm_config, extensions, gas_meter) | ||
eval::run(state, vtables, vm_config, extensions, tracer, gas_meter) | ||
} | ||
} | ||
|
||
|
@@ -75,3 +87,5 @@ macro_rules! set_err_info { | |
} | ||
|
||
pub(crate) use set_err_info; | ||
|
||
use super::tracing::tracer::VMTracer; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems a weird place for this |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See comment lower down, but I kinda like the way this turned out at the entry points so I'd be in favor of keeping it as-is here.