Skip to content
Merged
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
11 changes: 11 additions & 0 deletions src/cli/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1247,6 +1247,7 @@ pub fn run(args: RunArgs, verbosity: Verbosity) -> Result<()> {
reason: "breakpoint".to_string(),
location: None,
call_stack: stack_summary.clone(),
storage_mutation,
});
}

Expand Down Expand Up @@ -3463,4 +3464,14 @@ mod tests {
}
}
//
///////
assert!(json.get("binary").is_some());
assert!(json.get("config").is_some());
assert!(json.get("history").is_some());
assert!(json.get("plugins").is_some());
assert!(json.get("protocol").is_some());
assert!(json.get("vscode_extension").is_some());
}
}
//
///////
24 changes: 24 additions & 0 deletions src/inspector/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,13 @@ impl StorageInspector {
*self.writes.entry(key.to_string()).or_insert(0) += 1;
}

/// Get a sorted list of all keys that have been written to
pub fn mutated_keys(&self) -> Vec<String> {
let mut keys: Vec<String> = self.writes.keys().cloned().collect();
keys.sort();
keys
}

/// Analyze access patterns
pub fn analyze_access_patterns(&self) -> AccessPatternReport {
let mut stats: HashMap<String, AccessStats> = HashMap::new();
Expand Down Expand Up @@ -706,6 +713,17 @@ impl StorageDiff {
pub fn is_empty(&self) -> bool {
self.added.is_empty() && self.modified.is_empty() && self.deleted.is_empty()
}

/// Extract a sorted list of all keys that were mutated (added, modified, or deleted)
pub fn mutated_keys(&self) -> Vec<String> {
let mut keys: Vec<String> = self.added.keys().cloned()
.chain(self.modified.keys().cloned())
.chain(self.deleted.iter().cloned())
.collect();
keys.sort();
keys.dedup();
keys
}
}

/// Statistics for a single storage access key
Expand Down Expand Up @@ -1152,6 +1170,12 @@ mod tests {
&("old".to_string(), "new".to_string())
);
assert!(diff.deleted.contains(&"key_76".to_string()));

let mutated = diff.mutated_keys();
assert_eq!(mutated.len(), 75);
assert!(mutated.contains(&"key_101".to_string()));
assert!(mutated.contains(&"key_51".to_string()));
assert!(mutated.contains(&"key_76".to_string()));
}

#[test]
Expand Down
8 changes: 6 additions & 2 deletions src/runtime/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use crate::inspector::budget::MemorySummary;
use crate::output::InvocationReason;
use crate::runtime::env::DebugEnv;
use crate::runtime::mocking::{MockCallLogEntry, MockContractDispatcher, MockRegistry};
use crate::debugger::timeline::StorageMutationMarker;
use crate::server::protocol::{DynamicTraceEvent, DynamicTraceEventKind};
use crate::utils::arguments::ArgumentParser;
use crate::{DebuggerError, Result};
Expand Down Expand Up @@ -152,7 +153,7 @@ impl ContractExecutor {

// Track storage changes as accesses
let storage_after = &record.storage_after;
self.track_storage_changes(&storage_before, storage_after);
let _mutation_marker = self.track_storage_changes(&storage_before, storage_after);

// Record completed function call
let result_str = display.clone();
Expand All @@ -178,15 +179,18 @@ impl ContractExecutor {
&mut self,
storage_before: &HashMap<String, String>,
storage_after: &HashMap<String, String>,
) {
) -> Option<StorageMutationMarker> {
let mut mutated_keys = Vec::new();
// Track writes (new or modified entries)
for (key, value) in storage_after {
if !storage_before.contains_key(key) {
// New write
self.debug_env.track_storage_write(key, value);
mutated_keys.push(key.clone());
} else if storage_before.get(key) != Some(value) {
// Modified write
self.debug_env.track_storage_write(key, value);
mutated_keys.push(key.clone());
}
}

Expand Down