Reorganize ledger-tool run sub-command (#31572)

* Rename ledger-tool run subcommand to program subcommand

* Reorganize ledger-tool program command to multiple subcommands
This commit is contained in:
Dmitri Makarov 2023-05-10 15:25:38 -04:00 committed by GitHub
parent 04425b81ce
commit fb79e2bbb8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 122 additions and 97 deletions

View File

@ -1,6 +1,6 @@
#![allow(clippy::integer_arithmetic)]
use {
crate::{args::*, bigtable::*, ledger_path::*, ledger_utils::*, output::*, run::*},
crate::{args::*, bigtable::*, ledger_path::*, ledger_utils::*, output::*, program::*},
chrono::{DateTime, Utc},
clap::{
crate_description, crate_name, value_t, value_t_or_exit, values_t_or_exit, App,
@ -106,7 +106,7 @@ mod bigtable;
mod ledger_path;
mod ledger_utils;
mod output;
mod run;
mod program;
#[derive(PartialEq, Eq)]
enum LedgerOutputMethod {
@ -1286,7 +1286,7 @@ fn main() {
.takes_value(true)
.possible_values(&["json", "json-compact"])
.help("Return information in specified output format, \
currently only available for bigtable and run subcommands"),
currently only available for bigtable and program subcommands"),
)
.arg(
Arg::with_name("verbose")
@ -2028,7 +2028,7 @@ fn main() {
If no file name is specified, it will print the metadata of all ledger files.")
)
)
.run_subcommand()
.program_subcommand()
.get_matches();
info!("{} {}", crate_name!(), solana_version::version!());
@ -4058,8 +4058,8 @@ fn main() {
eprintln!("{err}");
}
}
("run", Some(arg_matches)) => {
run(&ledger_path, arg_matches);
("program", Some(arg_matches)) => {
program(&ledger_path, arg_matches);
}
("", _) => {
eprintln!("{}", matches.usage());

View File

@ -1,6 +1,6 @@
use {
crate::{args::*, ledger_utils::*},
clap::{value_t, App, Arg, ArgMatches, SubCommand},
clap::{value_t, App, AppSettings, Arg, ArgMatches, SubCommand},
log::*,
serde::{Deserialize, Serialize},
serde_json::Result,
@ -131,18 +131,52 @@ fn load_blockstore(ledger_path: &Path, arg_matches: &ArgMatches<'_>) -> Arc<Bank
bank
}
pub trait RunSubCommand {
fn run_subcommand(self) -> Self;
pub trait ProgramSubCommand {
fn program_subcommand(self) -> Self;
}
impl RunSubCommand for App<'_, '_> {
fn run_subcommand(self) -> Self {
impl ProgramSubCommand for App<'_, '_> {
fn program_subcommand(self) -> Self {
let program_arg = Arg::with_name("PROGRAM")
.help(
"Program file to use. This is either an ELF shared-object file to be executed, \
or an assembly file to be assembled and executed.",
)
.required(true)
.index(1);
let max_genesis_arg = Arg::with_name("max_genesis_archive_unpacked_size")
.long("max-genesis-archive-unpacked-size")
.value_name("NUMBER")
.takes_value(true)
.default_value("10485760")
.help("maximum total uncompressed size of unpacked genesis archive");
self.subcommand(
SubCommand::with_name("program")
.about("Run to test, debug, and analyze on-chain programs.")
.setting(AppSettings::InferSubcommands)
.setting(AppSettings::SubcommandRequiredElseHelp)
.subcommand(
SubCommand::with_name("cfg")
.about("generates Control Flow Graph of the program.")
.arg(&max_genesis_arg)
.arg(&program_arg)
)
.subcommand(
SubCommand::with_name("disassemble")
.about("dumps disassembled code of the program.")
.arg(&max_genesis_arg)
.arg(&program_arg)
)
.subcommand(
SubCommand::with_name("run")
.about(
r##"Run to test, debug, and analyze on-chain programs.
The tool executes on-chain programs in a mocked environment.
.about(
"The command executes on-chain programs in a mocked environment.",
)
.arg(
Arg::with_name("input")
.help(
r##"Input for the program to run on, where FILE is a name of a JSON file
with input data, or BYTES is the number of 0-valued bytes to allocate for program parameters"
The input data for a program execution have to be in JSON format
and the following fields are required
@ -161,84 +195,63 @@ and the following fields are required
"instruction_data": [31, 32, 23, 24]
}
"##,
)
.arg(
Arg::with_name("PROGRAM")
.help(
"Program file to use. This is either an ELF shared-object file to be executed, \
or an assembly file to be assembled and executed.",
)
.short("i")
.long("input")
.value_name("FILE / BYTES")
.takes_value(true)
.default_value("0"),
)
.required(true)
.index(1)
)
.arg(
Arg::with_name("input")
.help(
"Input for the program to run on, where FILE is a name of a JSON file \
with input data, or BYTES is the number of 0-valued bytes to allocate for program parameters",
.arg(&max_genesis_arg)
.arg(
Arg::with_name("memory")
.help("Heap memory for the program to run on")
.short("m")
.long("memory")
.value_name("BYTES")
.takes_value(true)
.default_value("0"),
)
.short("i")
.long("input")
.value_name("FILE / BYTES")
.takes_value(true)
.default_value("0"),
)
.arg(
Arg::with_name("memory")
.help("Heap memory for the program to run on")
.short("m")
.long("memory")
.value_name("BYTES")
.takes_value(true)
.default_value("0"),
)
.arg(
Arg::with_name("use")
.help(
"Method of execution to use, where 'cfg' generates Control Flow Graph \
of the program, 'disassembler' dumps disassembled code of the program, 'interpreter' runs \
the program in the virtual machine's interpreter, 'debugger' is the same as 'interpreter' \
but hosts a GDB interface, and 'jit' precompiles the program to native machine code \
before execting it in the virtual machine.",
.arg(
Arg::with_name("mode")
.help(
"Mode of execution, where 'interpreter' runs \
the program in the virtual machine's interpreter, 'debugger' is the same as 'interpreter' \
but hosts a GDB interface, and 'jit' precompiles the program to native machine code \
before execting it in the virtual machine.",
)
.short("e")
.long("mode")
.takes_value(true)
.value_name("VALUE")
.possible_values(&["interpreter", "debugger", "jit"])
.default_value("jit"),
)
.short("u")
.long("use")
.takes_value(true)
.value_name("VALUE")
.possible_values(&["cfg", "disassembler", "interpreter", "debugger", "jit"])
.default_value("jit"),
)
.arg(
Arg::with_name("instruction limit")
.help("Limit the number of instructions to execute")
.long("limit")
.takes_value(true)
.value_name("COUNT")
.default_value("9223372036854775807"),
)
.arg(
Arg::with_name("max_genesis_archive_unpacked_size")
.long("max-genesis-archive-unpacked-size")
.value_name("NUMBER")
.takes_value(true)
.default_value("10485760")
.help("maximum total uncompressed size of unpacked genesis archive")
)
.arg(
Arg::with_name("port")
.help("Port to use for the connection with a remote debugger")
.long("port")
.takes_value(true)
.value_name("PORT")
.default_value("9001"),
)
.arg(
Arg::with_name("trace")
.help("Output instruction trace")
.short("t")
.long("trace")
.takes_value(true)
.value_name("FILE"),
.arg(
Arg::with_name("instruction limit")
.help("Limit the number of instructions to execute")
.long("limit")
.takes_value(true)
.value_name("COUNT")
.default_value("9223372036854775807"),
)
.arg(
Arg::with_name("port")
.help("Port to use for the connection with a remote debugger")
.long("port")
.takes_value(true)
.value_name("PORT")
.default_value("9001"),
)
.arg(
Arg::with_name("trace")
.help("Output instruction trace")
.short("t")
.long("trace")
.takes_value(true)
.value_name("FILE"),
)
.arg(&program_arg)
)
)
}
@ -311,7 +324,18 @@ fn output_trace(
}
}
pub fn run(ledger_path: &Path, matches: &ArgMatches<'_>) {
pub fn program(ledger_path: &Path, matches: &ArgMatches<'_>) {
enum Action {
Cfg,
Dis,
Run,
}
let (action, matches) = match matches.subcommand() {
("cfg", Some(arg_matches)) => (Action::Cfg, arg_matches),
("disassembler", Some(arg_matches)) => (Action::Dis, arg_matches),
("run", Some(arg_matches)) => (Action::Run, arg_matches),
_ => unreachable!(),
};
let bank = load_blockstore(ledger_path, matches);
let loader_id = bpf_loader_upgradeable::id();
let mut transaction_accounts = Vec::new();
@ -410,7 +434,7 @@ pub fn run(ledger_path: &Path, matches: &ArgMatches<'_>) {
program_id, // ID of the loaded program. It can modify accounts with the same owner key
AccountSharedData::new(0, 0, &loader_id),
));
let interpreted = matches.value_of("use").unwrap() != "jit";
let interpreted = matches.value_of("mode").unwrap() != "jit";
with_mock_invoke_context!(
invoke_context,
transaction_context,
@ -516,8 +540,8 @@ pub fn run(ledger_path: &Path, matches: &ArgMatches<'_>) {
verified_executable.jit_compile().unwrap();
let mut analysis = LazyAnalysis::new(verified_executable.get_executable());
match matches.value_of("use") {
Some("cfg") => {
match action {
Action::Cfg => {
let mut file = File::create("cfg.dot").unwrap();
analysis
.analyze()
@ -525,13 +549,14 @@ pub fn run(ledger_path: &Path, matches: &ArgMatches<'_>) {
.unwrap();
return;
}
Some("disassembler") => {
Action::Dis => {
let stdout = std::io::stdout();
analysis.analyze().disassemble(&mut stdout.lock()).unwrap();
return;
}
_ => {}
}
};
create_vm!(
vm,
&verified_executable,
@ -541,7 +566,7 @@ pub fn run(ledger_path: &Path, matches: &ArgMatches<'_>) {
);
let mut vm = vm.unwrap();
let start_time = Instant::now();
if matches.value_of("use").unwrap() == "debugger" {
if matches.value_of("mode").unwrap() == "debugger" {
vm.debug_port = Some(matches.value_of("port").unwrap().parse::<u16>().unwrap());
}
let (instruction_count, result) = vm.execute_program(interpreted);