Function wasm_instrument::gas_metering::inject
source · pub fn inject<R: Rules, B: Backend>(
module: Module,
backend: B,
rules: &R
) -> Result<Module, Module>
Expand description
Transforms a given module into one that tracks the gas charged during its execution.
The output module uses the gas
function to track the gas spent. The function could be either
an imported or a local one modifying a mutable global. The argument is the amount of gas
required to continue execution. The execution engine is meant to keep track of the total amount
of gas used and trap or otherwise halt execution of the runtime if the gas usage exceeds some
allowed limit.
The body of each function of the original module is divided into metered blocks, and the calls to charge gas are inserted at the beginning of every such block of code. A metered block is defined so that, unless there is a trap, either all of the instructions are executed or none are. These are similar to basic blocks in a control flow graph, except that in some cases multiple basic blocks can be merged into a single metered block. This is the case if any path through the control flow graph containing one basic block also contains another.
Charging gas at the beginning of each metered block ensures that 1) all instructions
executed are already paid for, 2) instructions that will not be executed are not charged for
unless execution traps, and 3) the number of calls to gas
is minimized. The corollary is
that modules instrumented with this metering code may charge gas for instructions not
executed in the event of a trap.
Additionally, each memory.grow
instruction found in the module is instrumented to first
make a call to charge gas for the additional pages requested. This cannot be done as part of
the block level gas charges as the gas cost is not static and depends on the stack argument
to memory.grow
.
The above transformations are performed for every function body defined in the module. This
function also rewrites all function indices references by code, table elements, etc., since
the addition of an imported functions changes the indices of module-defined functions. If
the module has a NameSection
, added by calling parse_names
, the indices will also be
updated.
Syncronizing the amount of gas charged with the execution engine can be done in two ways. The
first way is by calling the imported gas
host function, see host_function
for details. The
second way is by using a local gas
function together with a mutable global, see
mutable_global
for details.
This routine runs in time linear in the size of the input module.
The function fails if the module contains any operation forbidden by gas rule set, returning
the original module as an Err
.