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:
parent
04425b81ce
commit
fb79e2bbb8
|
@ -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());
|
||||
|
|
|
@ -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);
|
Loading…
Reference in New Issue