Skip to content

Commit 0f313fd

Browse files
committed
introduce debug value
1 parent 7700454 commit 0f313fd

8 files changed

Lines changed: 1138 additions & 577 deletions

File tree

third_party/move/move-vm/runtime/src/debug.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ use std::{
1414
str::FromStr,
1515
};
1616

17+
pub mod interpreter;
18+
1719
// ── Batch-command queue ───────────────────
1820

1921
thread_local! {
@@ -336,6 +338,7 @@ impl DebugContext {
336338
function,
337339
locals,
338340
runtime_environment,
341+
interpreter,
339342
false,
340343
)
341344
.unwrap();
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
// Copyright (c) Aptos Foundation
2+
// Licensed pursuant to the Innovation-Enabling Source Code License, available at https://github.com/aptos-labs/aptos-core/blob/main/LICENSE
3+
4+
use crate::{
5+
frame::Frame,
6+
interpreter::InterpreterImpl,
7+
module_traversal::{TraversalContext, TraversalStorage},
8+
source_locator, Loader, RuntimeEnvironment,
9+
};
10+
use move_binary_format::errors::{ExecutionState, PartialVMResult};
11+
use move_vm_types::{
12+
debug_write, debug_writeln,
13+
gas::UnmeteredGasMeter,
14+
loaded_data::{runtime_types::StructType, struct_name_indexing::StructNameIndex},
15+
};
16+
use std::{cmp::min, fmt::Write, sync::Arc};
17+
18+
pub(crate) trait InterpreterDebugInterface {
19+
fn get_stack_frames(&self, count: usize) -> ExecutionState;
20+
fn get_stack_depth(&self) -> usize;
21+
fn debug_print_stack_trace(
22+
&self,
23+
buf: &mut String,
24+
runtime_environment: &RuntimeEnvironment,
25+
) -> PartialVMResult<()>;
26+
/// Required for the `Type::Struct { idx } -> StructType` transformation, to retrieve field types in debugger.
27+
fn load_struct_type(&self, idx: &StructNameIndex) -> Option<Arc<StructType>>;
28+
}
29+
30+
impl<LoaderImpl> InterpreterDebugInterface for InterpreterImpl<'_, LoaderImpl>
31+
where
32+
LoaderImpl: Loader,
33+
{
34+
#[cold]
35+
fn debug_print_stack_trace(
36+
&self,
37+
buf: &mut String,
38+
runtime_environment: &RuntimeEnvironment,
39+
) -> PartialVMResult<()> {
40+
debug_writeln!(buf, "Call Stack:")?;
41+
for (i, frame) in self.call_stack.0.iter().enumerate() {
42+
self.debug_print_frame(buf, runtime_environment, i, frame)?;
43+
}
44+
debug_writeln!(buf, "Operand Stack:")?;
45+
for (idx, val) in self.operand_stack.value.iter().enumerate() {
46+
// TODO: Currently we do not know the types of the values on the operand stack.
47+
// Revisit.
48+
debug_write!(buf, " [{}] ", idx)?;
49+
source_locator::print_value(buf, val)?;
50+
debug_writeln!(buf)?;
51+
}
52+
Ok(())
53+
}
54+
55+
/// Get count stack frames starting from the top of the stack.
56+
fn get_stack_frames(&self, count: usize) -> ExecutionState {
57+
// collect frames in the reverse order as this is what is
58+
// normally expected from the stack trace (outermost frame
59+
// is the last one)
60+
let stack_trace = self
61+
.call_stack
62+
.0
63+
.iter()
64+
.rev()
65+
.take(count)
66+
.map(|frame| {
67+
(
68+
frame.function.module_id().cloned(),
69+
frame.function.index(),
70+
frame.pc,
71+
)
72+
})
73+
.collect();
74+
ExecutionState::new(stack_trace)
75+
}
76+
77+
fn get_stack_depth(&self) -> usize {
78+
self.call_stack.0.len()
79+
}
80+
81+
fn load_struct_type(&self, idx: &StructNameIndex) -> Option<Arc<StructType>> {
82+
let storage = TraversalStorage::new();
83+
let mut ctx = TraversalContext::new(&storage);
84+
self.loader
85+
.load_struct_definition(&mut UnmeteredGasMeter, &mut ctx, idx)
86+
.ok()
87+
}
88+
}
89+
90+
impl<LoaderImpl> InterpreterImpl<'_, LoaderImpl>
91+
where
92+
LoaderImpl: Loader,
93+
{
94+
pub(crate) fn debug_print_frame<B: Write>(
95+
&self,
96+
buf: &mut B,
97+
runtime_environment: &RuntimeEnvironment,
98+
idx: usize,
99+
frame: &Frame,
100+
) -> PartialVMResult<()> {
101+
debug_write!(buf, " [{}] ", idx)?;
102+
103+
// Print out the function name.
104+
let function = &frame.function;
105+
debug_write!(buf, "{}", function.name_as_pretty_string())?;
106+
107+
// Print out type arguments, if they exist.
108+
let ty_args = function.ty_args();
109+
if !ty_args.is_empty() {
110+
let mut ty_tags = vec![];
111+
for ty in ty_args {
112+
let tag = runtime_environment.ty_to_ty_tag(ty)?;
113+
ty_tags.push(tag);
114+
}
115+
debug_write!(buf, "<")?;
116+
let mut it = ty_tags.iter();
117+
if let Some(tag) = it.next() {
118+
debug_write!(buf, "{}", tag.to_canonical_string())?;
119+
for tag in it {
120+
debug_write!(buf, ", ")?;
121+
debug_write!(buf, "{}", tag.to_canonical_string())?;
122+
}
123+
}
124+
debug_write!(buf, ">")?;
125+
}
126+
debug_writeln!(buf)?;
127+
128+
// Print source location if available.
129+
if let Some(module_id) = function.module_id() {
130+
if let Some(loc) =
131+
source_locator::get_bytecode_source_location(module_id, function.index(), frame.pc)
132+
{
133+
debug_writeln!(buf, " at {}", loc)?;
134+
}
135+
}
136+
137+
// Print out the current instruction.
138+
debug_writeln!(buf)?;
139+
debug_writeln!(buf, " Code:")?;
140+
let pc = frame.pc as usize;
141+
let code = function.code();
142+
let before = pc.saturating_sub(3);
143+
let after = min(code.len(), pc + 4);
144+
for (idx, instr) in code.iter().enumerate().take(pc).skip(before) {
145+
debug_writeln!(buf, " [{}] {:?}", idx, instr)?;
146+
}
147+
debug_writeln!(buf, " > [{}] {:?}", pc, &code[pc])?;
148+
for (idx, instr) in code.iter().enumerate().take(after).skip(pc + 1) {
149+
debug_writeln!(buf, " [{}] {:?}", idx, instr)?;
150+
}
151+
152+
// Print out the locals.
153+
debug_writeln!(buf)?;
154+
if function.local_tys().is_empty() {
155+
debug_writeln!(buf, " Locals:")?;
156+
debug_writeln!(buf, " (none)")?;
157+
} else {
158+
source_locator::print_locals_enriched(
159+
buf,
160+
function,
161+
&frame.locals,
162+
runtime_environment,
163+
self,
164+
true,
165+
)?;
166+
debug_writeln!(buf)?;
167+
}
168+
169+
debug_writeln!(buf)?;
170+
Ok(())
171+
}
172+
}

0 commit comments

Comments
 (0)