Skip to content
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

[move][trace-format] Update to Move value rep in traces to track more tightly with json representation #20852

Draft
wants to merge 1 commit into
base: tzakian/compress-traces
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Running Move unit tests
[ PASS ] 0x1::errors::underflow
[ PASS ] 0x1::natives::get_orig_type_name_test
[ PASS ] 0x1::natives::get_type_name_test
[ PASS ] 0x1::packs::test_enums_structs
[ PASS ] 0x1::packs::test_gen_pack_order
[ PASS ] 0x1::packs::test_gen_unpack_order
[ PASS ] 0x1::packs::test_pack_order
Expand All @@ -25,5 +26,5 @@ Running Move unit tests
[ PASS ] 0x1::references::test_struct_borrow
[ PASS ] 0x1::references::test_vector_mut_borrow
[ PASS ] 0x1::references::test_vector_mut_borrow_pop
Test result: OK. Total tests: 23; passed: 23; failed: 0
Test result: OK. Total tests: 24; passed: 24; failed: 0
External Command `diff -qr new_traces saved_traces`:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ module 0x1::packs {

public struct GenX<A, B, C>(A, B, C) has drop;

public enum Foo has drop {
A,
B(u64),
C(u64, bool),
D(X, address),
}

#[test]
fun test_pack_order() {
let a = 1;
Expand Down Expand Up @@ -41,4 +48,10 @@ module 0x1::packs {
let GenX(a, b, c) = x;
assert!(c == 0u8 && b, a);
}

#[test]
fun test_enums_structs() {
let x = X(1, true, 0);
let _foo = Foo::D(x, @0x1);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -614,7 +614,7 @@ impl fmt::Display for MoveTypeLayout {
/// Helper type that uses `T`'s `Display` implementation as its own `Debug` implementation, to allow
/// other `Display` implementations in this module to take advantage of the structured formatting
/// helpers that Rust uses for its own debug types.
struct DebugAsDisplay<'a, T>(&'a T);
pub struct DebugAsDisplay<'a, T>(pub &'a T);
impl<'a, T: fmt::Display> fmt::Debug for DebugAsDisplay<'a, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
Expand Down
42 changes: 20 additions & 22 deletions external-crates/move/crates/move-trace-format/src/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,13 @@
// IDEA: Post trace analysis -- report when values are dropped.

use crate::interface::{NopTracer, Tracer, Writer};
use crate::value::SerializableMoveValue;
use move_binary_format::{
file_format::{Bytecode, FunctionDefinitionIndex as BinaryFunctionDefinitionIndex},
file_format_common::instruction_opcode,
};
use move_core_types::{
annotated_value::MoveValue,
language_storage::{ModuleId, TypeTag},
};
use serde::Serialize;
use move_core_types::language_storage::{ModuleId, TypeTag};
use serde::{Deserialize, Serialize};
use std::{fmt::Display, sync::mpsc::Receiver};

/// An index into the trace. This should be used when referring to locations in the trace.
Expand Down Expand Up @@ -42,7 +40,7 @@ const CHANNEL_BUFFER_SIZE: usize = 100;
///
/// Note that we track aliasing through the locations so you can always trace back to the root
/// value for the reference.
#[derive(Debug, Clone, Eq, PartialEq, Serialize)]
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub enum Location {
// Local index in a frame. The frame is identified by the index in the trace where it was created.
// The `usize` is the index into the locals of the frame.
Expand All @@ -57,7 +55,7 @@ pub enum Location {

/// A Read event. This represents a read from a location, with the value read and whether the value
/// was moved or not.
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct Read {
pub location: Location,
pub root_value_read: TraceValue,
Expand All @@ -67,7 +65,7 @@ pub struct Read {
/// A Write event. This represents a write to a location with the value written and a snapshot of
/// the value that was written. Note that the `root_value_after_write` is a snapshot of the
/// _entire_ (root) value that was written after the write.
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct Write {
pub location: Location,
pub root_value_after_write: TraceValue,
Expand All @@ -76,24 +74,24 @@ pub struct Write {
/// A TraceValue is a value in the standard MoveValue domain + references.
/// References hold their own snapshot of the root value they point to, along with the rooted path to
/// the value that they reference within that snapshot.
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub enum TraceValue {
RuntimeValue {
value: MoveValue,
value: SerializableMoveValue,
},
ImmRef {
location: Location,
// Snapshot of the root value.
snapshot: Box<MoveValue>,
snapshot: Box<SerializableMoveValue>,
},
MutRef {
location: Location,
// Snapshot of the root value.
snapshot: Box<MoveValue>,
snapshot: Box<SerializableMoveValue>,
},
}

#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub enum RefType {
Imm,
Mut,
Expand All @@ -102,14 +100,14 @@ pub enum RefType {
/// Type tag with references. This is a type tag that also supports references.
/// if ref_type is None, this is a value type. If ref_type is Some, this is a reference type of the
/// given reference type.
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct TypeTagWithRefs {
pub type_: TypeTag,
pub ref_type: Option<RefType>,
}

/// A `Frame` represents a stack frame in the Move VM and a given instantiation of a function.
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct Frame {
// The frame id is the offset in the trace where this frame was opened.
pub frame_id: TraceIndex,
Expand All @@ -126,7 +124,7 @@ pub struct Frame {

/// An instruction effect is a single effect of an instruction. This can be a push/pop of a value
/// or a reference to a value, or a read/write of a value.
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub enum Effect {
// Pop a value off the stack (pre-effect only)
Pop(TraceValue),
Expand All @@ -148,16 +146,16 @@ pub enum Effect {
/// Represent a data load event. This is a load of a value from storage. We only record loads by
/// reference in the trace, and we snapshot the value at the reference location at the time of load
/// and record its global reference ID (i.e., the location in the trace at which it was loaded).
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct DataLoad {
pub ref_type: RefType,
pub location: Location,
pub snapshot: MoveValue,
pub snapshot: SerializableMoveValue,
}

/// A TraceEvent is a single event in the Move VM, external events can also be interleaved in the
/// trace. MoveVM events, are well structured, and can be a frame event or an instruction event.
#[derive(Debug, Clone, Eq, PartialEq, Serialize)]
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub enum TraceEvent {
OpenFrame {
frame: Box<Frame>,
Expand All @@ -178,7 +176,7 @@ pub enum TraceEvent {
External(Box<serde_json::Value>),
}

#[derive(Debug, Clone, Eq, PartialEq, Serialize)]
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct TraceVersionData {
version: TraceVersion,
}
Expand All @@ -204,14 +202,14 @@ pub struct MoveTraceBuilder {
}

impl TraceValue {
pub fn snapshot(&self) -> &MoveValue {
pub fn snapshot(&self) -> &SerializableMoveValue {
match self {
TraceValue::ImmRef { snapshot, .. } | TraceValue::MutRef { snapshot, .. } => snapshot,
TraceValue::RuntimeValue { value } => value,
}
}

pub fn value_mut(&mut self) -> Option<&mut MoveValue> {
pub fn value_mut(&mut self) -> Option<&mut SerializableMoveValue> {
match self {
TraceValue::RuntimeValue { value, .. } => Some(value),
_ => None,
Expand Down
1 change: 1 addition & 0 deletions external-crates/move/crates/move-trace-format/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
pub mod format;
pub mod interface;
pub mod memory_tracer;
pub mod value;
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,16 @@
use crate::{
format::{DataLoad, Effect, Location, Read, TraceEvent, TraceIndex, TraceValue, Write},
interface::{Tracer, Writer},
value::SerializableMoveValue,
};
use core::fmt;
use move_core_types::annotated_value::MoveValue;
use std::collections::BTreeMap;

#[derive(Debug, Clone)]
pub struct TraceState {
// Tracks "global memory" state (i.e., references out in to global memory/references returned
// from native functions).
pub loaded_state: BTreeMap<TraceIndex, MoveValue>,
pub loaded_state: BTreeMap<TraceIndex, SerializableMoveValue>,
// The current state (i.e., values) of the VM's operand stack.
pub operand_stack: Vec<TraceValue>,
// The current call stack indexed by frame id. Maps from the frame id to the current state of
Expand Down Expand Up @@ -126,7 +126,7 @@ impl TraceState {

/// Given a reference "location" return a mutable reference to the value it points to so that
/// it can be updated.
fn get_mut_location(&mut self, location: &Location) -> &mut MoveValue {
fn get_mut_location(&mut self, location: &Location) -> &mut SerializableMoveValue {
match location {
Location::Local(frame_idx, idx) => {
let frame = self.call_stack.get_mut(frame_idx).unwrap();
Expand Down
153 changes: 153 additions & 0 deletions external-crates/move/crates/move-trace-format/src/value.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
// Copyright (c) The Move Contributors
// SPDX-License-Identifier: Apache-2.0

//---------------------------------------------------------------------------
// Serializable Move Values -- these are a representation of Move values that
// keep sizedness of integers in the serialized form and do not need a type layout in order to be
// deserialized into an annotated value.
//---------------------------------------------------------------------------

use core::fmt;
use move_core_types::{
account_address::AccountAddress,
annotated_value::{DebugAsDisplay, MoveStruct, MoveValue, MoveVariant},
identifier::Identifier,
language_storage::StructTag,
u256,
};
use serde::{Deserialize, Serialize};

/// A simplified representation of Move values (that in particular drops integer sizing
/// information as this is lost during the serialization/deserialization process to json).
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type", content = "value")]
pub enum SerializableMoveValue {
Bool(bool),
U8(u8),
U16(u16),
U32(u32),
U64(u64),
U128(u128),
U256(u256::U256),
Address(AccountAddress),
Struct(SimplifiedMoveStruct),
Vector(Vec<SerializableMoveValue>),
Variant(SimplifiedMoveVariant),
}

#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct SimplifiedMoveStruct {
pub type_: StructTag,
pub fields: Vec<(Identifier, SerializableMoveValue)>,
}

#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct SimplifiedMoveVariant {
pub type_: StructTag,
pub variant_name: Identifier,
pub tag: u16,
pub fields: Vec<(Identifier, SerializableMoveValue)>,
}

impl From<MoveValue> for SerializableMoveValue {
fn from(value: MoveValue) -> Self {
match value {
MoveValue::Bool(b) => SerializableMoveValue::Bool(b),
MoveValue::U256(n) => SerializableMoveValue::U256(n),
MoveValue::U128(n) => SerializableMoveValue::U128(n),
MoveValue::U64(n) => SerializableMoveValue::U64(n),
MoveValue::U32(n) => SerializableMoveValue::U32(n),
MoveValue::U16(n) => SerializableMoveValue::U16(n),
MoveValue::U8(n) => SerializableMoveValue::U8(n),
MoveValue::Address(a) => SerializableMoveValue::Address(a),
MoveValue::Struct(MoveStruct { type_, fields }) => {
SerializableMoveValue::Struct(SimplifiedMoveStruct {
type_,
fields: fields.into_iter().map(|(id, v)| (id, v.into())).collect(),
})
}
MoveValue::Vector(v) => {
SerializableMoveValue::Vector(v.into_iter().map(Into::into).collect())
}
MoveValue::Variant(MoveVariant {
type_,
variant_name,
tag,
fields,
}) => SerializableMoveValue::Variant(SimplifiedMoveVariant {
type_,
variant_name,
tag,
fields: fields.into_iter().map(|(id, v)| (id, v.into())).collect(),
}),
MoveValue::Signer(account_address) => SerializableMoveValue::Address(account_address),
}
}
}

impl fmt::Display for SerializableMoveValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SerializableMoveValue::Bool(b) => write!(f, "{}", b),
SerializableMoveValue::U8(n) => write!(f, "{}u8", n),
SerializableMoveValue::U16(n) => write!(f, "{}u16", n),
SerializableMoveValue::U32(n) => write!(f, "{}u32", n),
SerializableMoveValue::U64(n) => write!(f, "{}u64", n),
SerializableMoveValue::U128(n) => write!(f, "{}u128", n),
SerializableMoveValue::U256(n) => write!(f, "{}u256", n),
SerializableMoveValue::Address(a) => write!(f, "{}", a),
SerializableMoveValue::Struct(s) => {
write!(f, "{} {{", s.type_)?;
for (id, v) in &s.fields {
write!(f, "{}: {}, ", id, v)?;
}
write!(f, "}}")
}
SerializableMoveValue::Vector(v) => {
write!(f, "[")?;
for e in v {
write!(f, "{}, ", e)?;
}
write!(f, "]")
}
SerializableMoveValue::Variant(v) => {
write!(f, "{}::{} {{", v.type_, v.variant_name)?;
for (id, v) in &v.fields {
write!(f, "{}: {}, ", id, v)?;
}
write!(f, "}}")
}
}
}
}

impl fmt::Display for SimplifiedMoveStruct {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use DebugAsDisplay as DD;
fmt::Display::fmt(&self.type_, f)?;
write!(f, " ")?;
let mut map = f.debug_map();
for (field, value) in &self.fields {
map.entry(&DD(field), &DD(value));
}
map.finish()
}
}

impl fmt::Display for SimplifiedMoveVariant {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use DebugAsDisplay as DD;
let SimplifiedMoveVariant {
type_,
variant_name,
tag: _,
fields,
} = self;
write!(f, "{}::{}", type_, variant_name)?;
let mut map = f.debug_map();
for (field, value) in fields {
map.entry(&DD(field), &DD(value));
}
map.finish()
}
}
Loading
Loading