1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
// Copyright (c) The Diem Core Contributors
// Copyright (c) The Move Contributors
// SPDX-License-Identifier: Apache-2.0
//! Transformation which injects trace instructions which are used to visualize execution.
//!
//! This transformation should run before copy propagation and any other bytecode modifications.
//! It emits instructions of the form `trace_local[original_idx](idx)`. Initially
//! `original_idx == idx`, where the temp `idx` is a named variable from the Move
//! compiler. Later transformations may replace `idx` but `original_idx` will be preserved so
//! the user sees the value of their named variable.
use crate::{
function_data_builder::FunctionDataBuilder,
function_target::FunctionData,
function_target_pipeline::{FunctionTargetProcessor, FunctionTargetsHolder},
stackless_bytecode::{Bytecode, Operation},
};
use move_model::{exp_generator::ExpGenerator, model::FunctionEnv};
use std::collections::BTreeSet;
pub struct DebugInstrumenter {}
impl DebugInstrumenter {
pub fn new() -> Box<Self> {
Box::new(Self {})
}
}
impl FunctionTargetProcessor for DebugInstrumenter {
fn process(
&self,
_targets: &mut FunctionTargetsHolder,
fun_env: &FunctionEnv<'_>,
data: FunctionData,
) -> FunctionData {
use Bytecode::*;
if fun_env.is_native() {
// Nothing to do
return data;
}
let mut builder = FunctionDataBuilder::new(fun_env, data);
let code = std::mem::take(&mut builder.data.code);
// Emit trace instructions for parameters at entry.
builder.set_loc(builder.fun_env.get_loc().at_start());
for i in 0..builder.fun_env.get_parameter_count() {
builder.emit_with(|id| Call(id, vec![], Operation::TraceLocal(i), vec![i], None));
}
for bc in code {
match &bc {
Ret(id, locals) => {
// Emit trace instructions for return values.
builder.set_loc_from_attr(*id);
for (i, l) in locals.iter().enumerate() {
builder.emit_with(|id| {
Call(id, vec![], Operation::TraceReturn(i), vec![*l], None)
});
}
builder.emit(bc);
}
Abort(id, l) => {
builder.set_loc_from_attr(*id);
builder.emit_with(|id| Call(id, vec![], Operation::TraceAbort, vec![*l], None));
builder.emit(bc);
}
Call(_, _, Operation::WriteRef, srcs, _) if srcs[0] < fun_env.get_local_count() => {
builder.set_loc_from_attr(bc.get_attr_id());
builder.emit(bc.clone());
builder.emit_with(|id| {
Call(
id,
vec![],
Operation::TraceLocal(srcs[0]),
vec![srcs[0]],
None,
)
});
}
_ => {
builder.set_loc_from_attr(bc.get_attr_id());
builder.emit(bc.clone());
// Emit trace instructions for modified values.
let (val_targets, mut_targets) = bc.modifies(&builder.get_target());
let affected_variables: BTreeSet<_> = val_targets
.into_iter()
.chain(mut_targets.into_iter().map(|(idx, _)| idx))
.collect();
for idx in affected_variables {
// Only emit this for user declared locals, not for ones introduced
// by stack elimination.
if idx < fun_env.get_local_count() {
builder.emit_with(|id| {
Call(id, vec![], Operation::TraceLocal(idx), vec![idx], None)
});
}
}
}
}
}
builder.data
}
fn name(&self) -> String {
"debug_instrumenter".to_string()
}
}