Design Document for Sui Custom Tracer
Summary
This document outlines the design and implementation details for a custom tracer for Sui. The goal is to create a flexible and minimally intrusive tracing framework that integrates seamlessly into the existing Sui infrastructure while maintaining compatibility with Sentio's custom functionalities.
Changes We've Made
Entry Point of Sentio's Trace Service: Sui Trace Server
- Developed a trace service that depends on
sui_replay to invoke the trace API.
- Modified
sui_replay to use executor for transaction replay and call trace retrieval.
- Altered the
download_latest_object method in sui_replay, replacing block_on with a thread channel to convert the asynchronous method to synchronous, ensuring it operates correctly within the API service (replay.rs).
- Added
call_trace to the PTB context and introduced execution_mode::DevCallTrace.
- Implemented
dev_transaction_call_trace in sui_executor to call execute_transaction_to_effects::<execution_mode::DevCallTrace>, which in turn calls the VM's call trace method.
- Integrated the
call_trace method into the VM runtime down to the interpreter level.
Background
The Sentio debugger feature (e.g., example transaction) obtains call traces of historical transactions to compute fund flows. The current changes to Sentio's SUI fork are tightly coupled with Sentio's requirements and include custom call trace data structures and methods within the Move VM. These modifications lead to duplicated code and hinder upstream contributions. Periodic merges from upstream are required, causing maintenance challenges. The objective is to refactor and redesign the solution to be more generic, facilitating integration with Sentio's call trace requirements and existing SUI features like gas profiling.
For reference, Geth's custom tracer API can be reviewed here.
Requirements
- Minimize intrusiveness to existing Move VM code.
- Maintain feature parity for Sentio's call trace feature and SUI gas profiler.
- Extensibility to support instruction-level debugging.
- Include in the debug build.
- Enable or disable tracers via cargo build feature flags.
- Initial design targets CLI usage (phase 1), with potential API implementation (persisting log files generated by the tracer).
- Trace API support is covered in a separate section, which can be considered as phase 2.
High-Level Flow

Detailed Design
Changes We Propose - Phase 1 (CLI Support)
- Add a new tracer trait for customized tracers.
- Add two new methods into the
GasMeter to initialize and retrieve the customized tracers.
- Add a new module
move_tracer with several useful macros to control the on/off state of each tracer.
- Add logic to invoke the customized tracers within the interpreter's execution logic.
Tracer Trait
trait Tracer {
fn on_instruction(&self) {}
fn on_function_call(&self) {}
fn on_function_generics_call(&self) {}
fn on_function_return(&self) {}
fn dump_file(&self) {}
}
Each tracer will have its own feature flag (e.g., gas_profiler). Tracers will be added to the tracer pool only when their feature flag is enabled.
Modifications to GasStatus
pub struct GasStatus {
// ...
tracers: Vec<Box<dyn Tracer>>,
}
impl GasStatus {
fn get_tracers_mut(&mut self) -> &mut Vec<Box<dyn Tracer>> {
&mut self.tracers
}
fn add_tracer(&mut self, tracer: Box<dyn Tracer>) {
self.tracers.push(tracer);
}
}
Initialization
move_vm_tracer::tracer_feature_enabled! {
use move_vm_tracer::Tracer;
gas_meter.add_tracer(Box::new(GasProfilerTracer::init_default_cfg(
function_name.to_string(),
gas_meter.remaining_gas().into(),
)));
}
Macros
#[macro_export]
macro_rules! trace_open_frame {
($gas_meter:expr, $frame_name:expr) => {
#[cfg(feature = "sui-tracer")]
{
let gas_rem = $gas_meter.remaining_gas().into();
move_vm_tracer::trace_open_frame_impl!(
$gas_meter.get_tracers_mut(),
$frame_name,
gas_rem
);
}
};
}
#[macro_export]
macro_rules! trace_open_frame_impl {
($tracers:expr, $frame_name:expr, $gas_rem:expr) => {
#[cfg(feature = "sui-tracer")]
{
for tracer in $tracers {
tracer.on_function_call();
}
}
};
}
#[macro_export]
macro_rules! trace_close_frame {
($gas_meter:expr, $frame_name:expr) => {
#[cfg(feature = "sui-tracer")]
{
let gas_rem = $gas_meter.remaining_gas().into();
move_vm_tracer::trace_close_frame_impl!(
$gas_meter.get_tracers_mut(),
$frame_name,
gas_rem
);
}
};
}
#[macro_export]
macro_rules! trace_close_frame_impl {
($tracers:expr, $frame_name:expr, $gas_rem:expr) => {
#[cfg(feature = "sui-tracer")]
{
for tracer in $tracers {
tracer.on_function_return();
}
}
};
}
// Instruction level macros go here
Usage During Execution
match exit_code {
ExitCode::Call(fh_idx) => {
let func = resolver.function_from_handle(fh_idx);
#[cfg(debug_assertions)]
let func_name = func.pretty_string();
trace_open_frame!(gas_meter, func_name.clone());
}
ExitCode::Return => {
let non_ref_vals = current_frame
.locals
.drop_all_values()
.map(|(_idx, val)| val);
gas_meter
.charge_drop_frame(non_ref_vals.into_iter())
.map_err(|e| self.set_location(e))?;
trace_close_frame!(gas_meter, current_frame.function.pretty_string());
}
}
Limitations - Phase 1
The current design is tailored for CLI use and is not optimized for API services. For API support in a separate service based on the replay method, additional changes are needed. The existing method dev_inspect_transaction in the executor returns only transaction effects, and tracer output is written to a separate log file. Implementing an API would require adding a new method like dev_transaction_call_trace, which may not be accepted upstream. A potential solution involves invoking the CLI and using an asynchronous approach to fetch local log files in the API service and obtain trace results, which is to be determined.
We will discuss the API version in the next section.
Open Questions - Phase 1
- Tracers are placed in the same place as the gas profiler, i.e., GasMeter, which may not be ideal. To avoid changing the signature by introducing a new parameter in the VM and interpreter, are there any alternative approaches? One approach is to add a new container for the GasMeter and pass the object of this new container into the VM.
Changes We Propose - Phase 2 (API Support)
- Add a new API
sui_devTraceTransactionBlock like sui_devInspectTransactionBlock.
- Add a new execution mode
DevTrace into the execution engine.
- Add an optional trace result in the VM result.
- Extend the tracer trait to obtain the trace result.
- Implement tracer-related logic in the PTB executor if needed.
API Specification: sui_devTraceTransactionBlock
Description
This endpoint replays a historic transaction block and returns the VM tracing result, providing detailed information about the execution of the transaction block.
Parameters
| Index |
Type |
Description |
| 0 |
string |
Required. Sender address. |
| 1 |
string |
Required. BCS encoded TransactionKind (as opposed to TransactionData, which includes gasBudget and gasPrice). |
| 2 |
number |
Optional. Gas is not charged, but gas usage is still calculated. Defaults to use reference gas price. |
| 3 |
number |
Optional. The epoch to perform the call. Will be set from the system state object |
if not provided. |
| 4 | string | Optional. Tracer name, native tracer by default. |
| 5 | null | Optional. Additional arguments for tracer. |
Example Request Body
{
"jsonrpc": "2.0",
"id": 1,
"method": "sui_devTraceTransactionBlock",
"params": [
"0xd70420418b84502e506794227f897237764dde8d79a01ab2104bf742a277a2ab",
"AAACACBnxtMcbJcOVn8D72fYEaT4Q2ZbjePygvpIs+AQO6m77QEAagYVO5/EhuEB8OnicDrIZm0GrsxN3355JqNhlwxlpbECAAAAAAAAACDoQ3EipycU+/EOvBcDPFtMkZiSbdzWAw3CwdmQCAtBWAEBAQEBAAEAAC9cVD1xauQ9RT3rOxmbva8bxwMMdoL4dwPc5DEkj+3gASxDgF0Nb1QCp60Npb3sVJx83qBrxKHTOaIlIe6pM7iJAgAAAAAAAAAgnvsgc1pPauyCE27/c+aBnHN3fSsxRAWdEJYzYFOryNAvXFQ9cWrkPUU96zsZm72vG8cDDHaC+HcD3OQxJI/t4AoAAAAAAAAAoIYBAAAAAAAA",
1000,
8888,
null,
null
]
}
Response
| Name |
Type |
Description |
vm_trace |
object |
Trace result |
Example Response Body
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"vm_trace": {
"steps": [
{
"step": 1,
"pc": 0,
"instr": "Pop",
"locals": ["0x0"]
},
{
"step": 2,
"pc": 1,
"instr": "CastU8",
"locals": ["0x0", "0x1"]
}
// More steps...
],
"gas_used": 21000,
"error": null
}
}
}
Tracer Trait
trait Tracer {
fn on_instruction(&self) {}
fn on_function_call(&self) {}
fn on_function_generics_call(&self) {}
fn on_function_return(&self) {}
fn dump_file(&self) {}
fn into_result(&self) -> serde_json::Value // Customizable tracer result
}
Definition of the New VM Result
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct TraceResult {
traces: serde_json::Value,
}
pub type VMResultWithTraces<T> = ::std::result::Result<T, (VMError, Vec<TraceResult>)>;
Open Questions - Phase 2
- Does the new API need to be proposed and reviewed by the community?
- Do the Move VM changes need to be merged into the official Move VM, or is it specific to SUI?
Backward Compatibility
VM traces should be available for all the historical transactions. And in the execution layer, changes will be added in the latest version.
In the Move VM layer, since the tracer related code is behind the feature flag and will be no included in the release build, so it would not affect the normal transaction execution.
Conclusion
This design aims to create a modular and maintainable tracing framework for the Sui blockchain, addressing both current requirements and potential future enhancements. The proposed approach minimizes disruption to existing codebases and provides a path for integrating advanced debugging and profiling features.
Design Document for Sui Custom Tracer
Summary
This document outlines the design and implementation details for a custom tracer for Sui. The goal is to create a flexible and minimally intrusive tracing framework that integrates seamlessly into the existing Sui infrastructure while maintaining compatibility with Sentio's custom functionalities.
Changes We've Made
Entry Point of Sentio's Trace Service: Sui Trace Server
sui_replayto invoke the trace API.sui_replayto useexecutorfor transaction replay and call trace retrieval.download_latest_objectmethod insui_replay, replacingblock_onwith a thread channel to convert the asynchronous method to synchronous, ensuring it operates correctly within the API service (replay.rs).call_traceto the PTB context and introducedexecution_mode::DevCallTrace.dev_transaction_call_traceinsui_executorto callexecute_transaction_to_effects::<execution_mode::DevCallTrace>, which in turn calls the VM's call trace method.call_tracemethod into the VM runtime down to the interpreter level.Background
The Sentio debugger feature (e.g., example transaction) obtains call traces of historical transactions to compute fund flows. The current changes to Sentio's SUI fork are tightly coupled with Sentio's requirements and include custom call trace data structures and methods within the Move VM. These modifications lead to duplicated code and hinder upstream contributions. Periodic merges from upstream are required, causing maintenance challenges. The objective is to refactor and redesign the solution to be more generic, facilitating integration with Sentio's call trace requirements and existing SUI features like gas profiling.
For reference, Geth's custom tracer API can be reviewed here.
Requirements
High-Level Flow
Detailed Design
Changes We Propose - Phase 1 (CLI Support)
GasMeterto initialize and retrieve the customized tracers.move_tracerwith several useful macros to control the on/off state of each tracer.Tracer Trait
Each tracer will have its own feature flag (e.g.,
gas_profiler). Tracers will be added to the tracer pool only when their feature flag is enabled.Modifications to
GasStatusInitialization
Macros
Usage During Execution
Limitations - Phase 1
The current design is tailored for CLI use and is not optimized for API services. For API support in a separate service based on the replay method, additional changes are needed. The existing method
dev_inspect_transactionin the executor returns only transaction effects, and tracer output is written to a separate log file. Implementing an API would require adding a new method like dev_transaction_call_trace, which may not be accepted upstream. A potential solution involves invoking the CLI and using an asynchronous approach to fetch local log files in the API service and obtain trace results, which is to be determined.We will discuss the API version in the next section.
Open Questions - Phase 1
Changes We Propose - Phase 2 (API Support)
sui_devTraceTransactionBlocklike sui_devInspectTransactionBlock.DevTraceinto the execution engine.API Specification: sui_devTraceTransactionBlock
Description
This endpoint replays a historic transaction block and returns the VM tracing result, providing detailed information about the execution of the transaction block.
Parameters
if not provided. |
| 4 | string | Optional. Tracer name, native tracer by default. |
| 5 | null | Optional. Additional arguments for tracer. |
Example Request Body
{ "jsonrpc": "2.0", "id": 1, "method": "sui_devTraceTransactionBlock", "params": [ "0xd70420418b84502e506794227f897237764dde8d79a01ab2104bf742a277a2ab", "AAACACBnxtMcbJcOVn8D72fYEaT4Q2ZbjePygvpIs+AQO6m77QEAagYVO5/EhuEB8OnicDrIZm0GrsxN3355JqNhlwxlpbECAAAAAAAAACDoQ3EipycU+/EOvBcDPFtMkZiSbdzWAw3CwdmQCAtBWAEBAQEBAAEAAC9cVD1xauQ9RT3rOxmbva8bxwMMdoL4dwPc5DEkj+3gASxDgF0Nb1QCp60Npb3sVJx83qBrxKHTOaIlIe6pM7iJAgAAAAAAAAAgnvsgc1pPauyCE27/c+aBnHN3fSsxRAWdEJYzYFOryNAvXFQ9cWrkPUU96zsZm72vG8cDDHaC+HcD3OQxJI/t4AoAAAAAAAAAoIYBAAAAAAAA", 1000, 8888, null, null ] }Response
vm_traceExample Response Body
{ "jsonrpc": "2.0", "id": 1, "result": { "vm_trace": { "steps": [ { "step": 1, "pc": 0, "instr": "Pop", "locals": ["0x0"] }, { "step": 2, "pc": 1, "instr": "CastU8", "locals": ["0x0", "0x1"] } // More steps... ], "gas_used": 21000, "error": null } } }Tracer Trait
Definition of the New VM Result
Open Questions - Phase 2
Backward Compatibility
VM traces should be available for all the historical transactions. And in the execution layer, changes will be added in the latest version.
In the Move VM layer, since the tracer related code is behind the feature flag and will be no included in the release build, so it would not affect the normal transaction execution.
Conclusion
This design aims to create a modular and maintainable tracing framework for the Sui blockchain, addressing both current requirements and potential future enhancements. The proposed approach minimizes disruption to existing codebases and provides a path for integrating advanced debugging and profiling features.