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] Added bytecode map generation during bytecode disassembly #20797

Merged
merged 13 commits into from
Jan 10, 2025
4 changes: 4 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/sui-framework-tests/tests/move_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ pub(crate) fn tests(path: &Path) -> datatest_stable::Result<()> {
testing_config.filter = std::env::var("FILTER").ok().map(|s| s.to_string());

assert_eq!(
run_move_unit_tests(path, move_config, Some(testing_config), false).unwrap(),
run_move_unit_tests(path, move_config, Some(testing_config), false, false).unwrap(),
UnitTestResult::Success
);

Expand Down
23 changes: 11 additions & 12 deletions crates/sui-graphql-rpc/src/types/move_module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -415,18 +415,17 @@ impl MoveModule {

/// Textual representation of the module's bytecode.
async fn disassembly(&self) -> Result<Option<String>> {
Ok(Some(
Disassembler::from_module_with_max_size(
self.parsed.bytecode(),
Loc::invalid(),
*move_package::MAX_DISASSEMBLED_MODULE_SIZE,
)
.map_err(|e| Error::Internal(format!("Error creating disassembler: {e}")))
.extend()?
.disassemble()
.map_err(|e| Error::Internal(format!("Error creating disassembly: {e}")))
.extend()?,
))
let (disassemble_string, _) = Disassembler::from_module_with_max_size(
self.parsed.bytecode(),
Loc::invalid(),
*move_package::MAX_DISASSEMBLED_MODULE_SIZE,
)
.map_err(|e| Error::Internal(format!("Error creating disassembler: {e}")))
.extend()?
.disassemble()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure how I feel about this. I'd probably prefer something like adding a new function, e.g., disasssemble_with_source_map (or something like that) that would also generate the source map during disassembly, and then keeping this function the same.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

.map_err(|e| Error::Internal(format!("Error creating disassembly: {e}")))
.extend()?;
Ok(Some(disassemble_string))
}
}

Expand Down
10 changes: 5 additions & 5 deletions crates/sui-json-rpc-types/src/sui_object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -786,11 +786,11 @@ impl SuiData for SuiParsedData {
.map_err(|e| SuiError::ObjectSerializationError {
error: e.to_string(),
})?;
let bytecode_str = d
.disassemble()
.map_err(|e| SuiError::ObjectSerializationError {
error: e.to_string(),
})?;
let (bytecode_str, _) =
d.disassemble()
.map_err(|e| SuiError::ObjectSerializationError {
error: e.to_string(),
})?;
disassembled.insert(module.name().to_string(), Value::String(bytecode_str));
}

Expand Down
1 change: 1 addition & 0 deletions crates/sui-move/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ move-disassembler.workspace = true
move-ir-types.workspace = true
move-package.workspace = true
move-prover.workspace = true
move-bytecode-source-map.workspace = true
move-unit-test.workspace = true
telemetry-subscribers.workspace = true
tokio = { workspace = true, features = ["full"] }
Expand Down
17 changes: 15 additions & 2 deletions crates/sui-move/src/disassemble.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

use clap::Parser;
use move_binary_format::CompiledModule;
use move_bytecode_source_map::utils::serialize_to_json_string;
use move_cli::base;
use move_disassembler::disassembler::Disassembler;
use move_ir_types::location::Spanned;
Expand All @@ -25,6 +26,10 @@ pub struct Disassemble {

#[clap(short = 'i', long = "interactive")]
interactive: bool,

/// Print the "bytecode map" (source map for disassembled bytecode)
#[clap(long = "bytecode-map")]
pub bytecode_map: bool,
}

impl Disassemble {
Expand All @@ -47,6 +52,7 @@ impl Disassemble {
package_name: None,
module_or_script_name: module_name,
debug: self.debug,
bytecode_map: self.bytecode_map,
}
.execute(package_path, build_config)?;
return Ok(());
Expand All @@ -68,8 +74,15 @@ impl Disassemble {
if self.debug {
println!("{module:#?}");
} else {
let d = Disassembler::from_module(&module, Spanned::unsafe_no_loc(()).loc)?;
println!("{}", d.disassemble()?);
let mut d = Disassembler::from_module(&module, Spanned::unsafe_no_loc(()).loc)?;
if self.bytecode_map {
d.generate_bytecode_map();
}
let (disassemble_string, bcode_map) = d.disassemble()?;
if self.bytecode_map {
println!("{}", serialize_to_json_string(&bcode_map)?);
}
println!("{}", disassemble_string);
}

Ok(())
Expand Down
5 changes: 5 additions & 0 deletions crates/sui-move/src/unit_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ impl Test {
"The --coverage flag is currently supported only in debug builds. Please build the Sui CLI from source in debug mode."
));
}
// save disassembly if trace execution is enabled
let save_disassembly = self.test.trace_execution.is_some();
// find manifest file directory from a given path or (if missing) from current dir
let rerooted_path = base::reroot_path(path)?;
let unit_test_config = self.test.unit_test_config();
Expand All @@ -50,6 +52,7 @@ impl Test {
build_config,
Some(unit_test_config),
compute_coverage,
save_disassembly,
)
}
}
Expand All @@ -71,6 +74,7 @@ pub fn run_move_unit_tests(
build_config: BuildConfig,
config: Option<UnitTestingConfig>,
compute_coverage: bool,
save_disassembly: bool,
) -> anyhow::Result<UnitTestResult> {
// bind the extension hook if it has not yet been done
Lazy::force(&SET_EXTENSION_HOOK);
Expand All @@ -91,6 +95,7 @@ pub fn run_move_unit_tests(
),
Some(initial_cost_schedule_for_unit_tests()),
compute_coverage,
save_disassembly,
&mut std::io::stdout(),
);
result.map(|(test_result, warning_diags)| {
Expand Down
6 changes: 3 additions & 3 deletions crates/sui-mvr-graphql-rpc/src/types/move_module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -414,14 +414,14 @@ impl MoveModule {

/// Textual representation of the module's bytecode.
async fn disassembly(&self) -> Result<Option<String>> {
Ok(Some(
let (disassemble_string, _) =
Disassembler::from_module(self.parsed.bytecode(), Loc::invalid())
.map_err(|e| Error::Internal(format!("Error creating disassembler: {e}")))
.extend()?
.disassemble()
.map_err(|e| Error::Internal(format!("Error creating disassembly: {e}")))
.extend()?,
))
.extend()?;
Ok(Some(disassemble_string))
}
}

Expand Down
3 changes: 3 additions & 0 deletions external-crates/move/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -712,4 +712,68 @@ impl SourceMap {

Ok(empty_source_map)
}

pub fn replace_file_hashes(&mut self, file_hash: FileHash) {
self.definition_location = Loc::new(
file_hash,
self.definition_location.start(),
self.definition_location.end(),
);
for (_, struct_map) in self.struct_map.iter_mut() {
struct_map.definition_location = Loc::new(
file_hash,
struct_map.definition_location.start(),
struct_map.definition_location.end(),
);
for (_, loc) in struct_map.type_parameters.iter_mut() {
*loc = Loc::new(file_hash, loc.start(), loc.end());
}
for loc in struct_map.fields.iter_mut() {
*loc = Loc::new(file_hash, loc.start(), loc.end());
}
}
for (_, enum_map) in self.enum_map.iter_mut() {
enum_map.definition_location = Loc::new(
file_hash,
enum_map.definition_location.start(),
enum_map.definition_location.end(),
);
for (_, loc) in enum_map.type_parameters.iter_mut() {
*loc = Loc::new(file_hash, loc.start(), loc.end());
}
for ((_, loc), field_locations) in enum_map.variants.iter_mut() {
*loc = Loc::new(file_hash, loc.start(), loc.end());
for field_loc in field_locations.iter_mut() {
*field_loc = Loc::new(file_hash, field_loc.start(), field_loc.end());
}
}
}
for (_, function_map) in self.function_map.iter_mut() {
function_map.location = Loc::new(
file_hash,
function_map.location.start(),
function_map.location.end(),
);
function_map.definition_location = Loc::new(
file_hash,
function_map.definition_location.start(),
function_map.definition_location.end(),
);
for (_, loc) in function_map.type_parameters.iter_mut() {
*loc = Loc::new(file_hash, loc.start(), loc.end());
}
for (_, loc) in function_map.parameters.iter_mut() {
*loc = Loc::new(file_hash, loc.start(), loc.end());
}
for loc in function_map.returns.iter_mut() {
*loc = Loc::new(file_hash, loc.start(), loc.end());
}
for (_, loc) in function_map.locals.iter_mut() {
*loc = Loc::new(file_hash, loc.start(), loc.end());
}
for (_, loc) in function_map.code_map.iter_mut() {
*loc = Loc::new(file_hash, loc.start(), loc.end());
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,16 @@ pub fn source_map_from_file(file_path: &Path) -> Result<SourceMap> {
.map_err(|_| format_err!("Error deserializing into source map"))
}

pub fn serialize_to_json_string(map: &SourceMap) -> Result<String> {
serde_json::to_string_pretty(map).map_err(|e| format_err!("Error serializing to json: {}", e))
}

pub fn serialize_to_json(map: &SourceMap) -> Result<Vec<u8>> {
serde_json::to_vec(map).map_err(|e| format_err!("Error serializing to json: {}", e))
}

pub fn serialize_to_json_file(map: &SourceMap, file_path: &Path) -> Result<()> {
let json = serde_json::to_string_pretty(map)
.map_err(|e| format_err!("Error serializing to json: {}", e))?;
let json = serialize_to_json_string(map)?;
let mut f =
std::fs::File::create(file_path).map_err(|e| format_err!("Error creating file: {}", e))?;
f.write_all(json.as_bytes())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ impl<'a> BytecodeViewer<'a> {
print_basic_blocks: true,
..Default::default()
};
let disassembled_string = Disassembler::new(source_mapping, options)
let (disassembled_string, _) = Disassembler::new(source_mapping, options)
.disassemble()
.unwrap();

Expand Down
2 changes: 2 additions & 0 deletions external-crates/move/crates/move-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ toml_edit.workspace = true

bcs.workspace = true

move-bytecode-source-map.workspace = true
move-bytecode-verifier.workspace = true
move-disassembler.workspace = true
move-docgen.workspace = true
Expand All @@ -40,6 +41,7 @@ move-vm-test-utils.workspace = true
move-binary-format.workspace = true
move-package.workspace = true
move-prover.workspace = true
move-symbol-pool.workspace = true
move-unit-test.workspace = true
move-bytecode-viewer.workspace = true

Expand Down
3 changes: 2 additions & 1 deletion external-crates/move/crates/move-cli/src/base/coverage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ impl Coverage {
let unit = package.get_module_by_name_from_root(&module_name)?;
let mut disassembler = Disassembler::from_unit(&unit.unit);
disassembler.add_coverage_map(coverage_map.to_unified_exec_map());
println!("{}", disassembler.disassemble()?);
let (disassemble_string, _) = disassembler.disassemble()?;
println!("{}", disassemble_string);
}
}
Ok(())
Expand Down
15 changes: 14 additions & 1 deletion external-crates/move/crates/move-cli/src/base/disassemble.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

use super::reroot_path;
use clap::*;
use move_bytecode_source_map::utils::serialize_to_json_string;
use move_compiler::compiled_unit::NamedCompiledModule;
use move_disassembler::disassembler::Disassembler;
use move_package::{compilation::compiled_package::CompiledUnitWithSource, BuildConfig};
Expand All @@ -24,6 +25,9 @@ pub struct Disassemble {
#[clap(long = "Xdebug")]
/// Also print the raw disassembly using Rust's Debug output, at the end.
pub debug: bool,
#[clap(long = "bytecode-map")]
/// Print the "bytecode map" (source map for disassembled bytecode)
pub bytecode_map: bool,
}

impl Disassemble {
Expand All @@ -34,6 +38,7 @@ impl Disassemble {
package_name,
module_or_script_name,
debug,
bytecode_map,
} = self;
// Make sure the package is built
let package = config.compile_package(&rerooted_path, &mut Vec::new())?;
Expand Down Expand Up @@ -66,7 +71,15 @@ impl Disassemble {
source_path,
)
} else {
println!("{}", Disassembler::from_unit(&unit.unit).disassemble()?);
let mut d = Disassembler::from_unit(&unit.unit);
if bytecode_map {
d.generate_bytecode_map();
}
let (disassemble_string, bcode_map) = d.disassemble()?;
if bytecode_map {
println!("{}", serialize_to_json_string(&bcode_map)?);
}
println!("{}", disassemble_string);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think places like this will be nicer with the two function approach as this will also allow removing the generate_bytecode_map method as well. E.g., something like this

Suggested change
if bytecode_map {
d.generate_bytecode_map();
}
let (disassemble_string, bcode_map) = d.disassemble()?;
if bytecode_map {
println!("{}", serialize_to_json_string(&bcode_map)?);
}
println!("{}", disassemble_string);
let diss = if bytecode_map {
let (disassemble_string, bcode_map) = d.disassemble_with_source_map()?;
println!("{}", serialize_to_json_string(&bcode_map);
disassemble_string
} else {
d.disassemble()?;
};
println!("{}", disassemble_string);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, it allowed me to remove this function but it also means that having a global flag is no longer necessary (and somewhat awkward even)

if debug {
println!("\n{:#?}", &unit.unit.module)
}
Expand Down
Loading
Loading