commands.ml
(*****************************************************************************)
(* *)
(* Open Source License *)
(* Copyright (c) 2020 Nomadic Labs. <contact@nomadic-labs.com> *)
(* Copyright (c) 2023 Marigold <contact@marigold.dev> *)
(* *)
(* Permission is hereby granted, free of charge, to any person obtaining a *)
(* copy of this software and associated documentation files (the "Software"),*)
(* to deal in the Software without restriction, including without limitation *)
(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *)
(* and/or sell copies of the Software, and to permit persons to whom the *)
(* Software is furnished to do so, subject to the following conditions: *)
(* *)
(* The above copyright notice and this permission notice shall be included *)
(* in all copies or substantial portions of the Software. *)
(* *)
(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*)
(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *)
(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *)
(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*)
(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *)
(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *)
(* DEALINGS IN THE SOFTWARE. *)
(* *)
(*****************************************************************************)
open Cmdline
let lift_opt f opt_arg state =
match opt_arg with None -> state | Some arg -> f arg state
let parse_parameter f m =
Tezos_clic.parameter (fun (_ : unit) p ->
Lwt.return
@@
match f p with
| Some x -> Ok x
| None -> Error_monad.error_with_exn (Failure m))
let pp_tags =
Format.pp_print_list
~pp_sep:(fun formatter () -> Format.fprintf formatter "; ")
Format.pp_print_string
let print_model : type a. a Model.model -> string =
fun model ->
let module M = (val model) in
let module M = M.Def (Costlang.Pp) in
M.model
module Benchmark_cmd = struct
(* ----------------------------------------------------------------------- *)
(* Handling the options of the benchmarker *)
open Measure
let set_seed seed (options : options) = {options with seed = Some seed}
let set_nsamples nsamples options = {options with nsamples}
let set_save_file save_file options = {options with save_file}
let set_csv_export csv_export (options : Cmdline.benchmark_options) =
{options with csv_export}
let set_storage_kind storage options = {options with storage}
let set_bench_number bench_number options = {options with bench_number}
let set_minor_heap_size minor_heap_size options =
{options with minor_heap_size}
let set_config_file config_file options = {options with config_file}
let default_benchmark_options =
let options =
{
seed = None;
nsamples = 500;
bench_number = 300;
minor_heap_size = `words (256 * 1024);
config_file = None;
}
in
{
options;
save_file = "<This field /will/ be set by Tezos_clic>";
storage = Memory;
csv_export = None;
}
let benchmark_options_to_string (options : Cmdline.benchmark_options) =
let open Printf in
let save_file = options.save_file in
let storage =
match options.storage with
| Memory -> "Mem"
| Disk {source; base_dir; header_json} ->
let pp_src =
Format.asprintf
"%a"
Tezos_crypto.Signature.Public_key_hash.pp
source
in
sprintf
"Disk { source = %s ; base_dir = %s ; header_json = %s }"
pp_src
base_dir
header_json
in
Format.asprintf
"@[<v 2>{ options = %a;@, save_file = %s;@, storage = %s }@]"
Measure.pp_options
options.options
save_file
storage
(* ----------------------------------------------------------------------- *)
(* "benchmark" command handler *)
let benchmark_handler
(nsamples, seed, bench_number, minor_heap_size, config_file, csv_export)
bench_name save_file () =
let options =
default_benchmark_options.options
|> lift_opt set_nsamples nsamples
|> lift_opt set_seed seed
|> lift_opt set_bench_number bench_number
|> lift_opt set_minor_heap_size minor_heap_size
|> set_config_file config_file
in
let options =
{default_benchmark_options with options}
|> set_save_file save_file |> set_csv_export csv_export
in
let bench_name = Namespace.of_string bench_name in
commandline_outcome_ref :=
Some (Benchmark {bench_name; bench_opts = options}) ;
Lwt.return_ok ()
module Options = struct
(* Integer argument --nsamples *)
let nsamples_arg =
let nsamples_arg_param =
parse_parameter
int_of_string_opt
"Error while parsing --nsamples argument."
in
Tezos_clic.arg
~doc:"Number of samples per benchmark"
~long:"nsamples"
~placeholder:"strictly positive int"
nsamples_arg_param
(* Integer argument --seed *)
let seed_arg =
let seed =
parse_parameter int_of_string_opt "Error while parsing --seed argument."
in
Tezos_clic.arg ~doc:"RNG seed" ~long:"seed" ~placeholder:"int" seed
(* String argument --dump-csv
Parameter: filename of the file where to write the csv data. *)
let dump_csv_arg =
let dump_csv_arg_param =
Tezos_clic.parameter (fun (_ : unit) parsed -> Lwt.return_ok parsed)
in
Tezos_clic.arg
~doc:"Dumps raw benchmark results to CSV"
~long:"dump-csv"
~placeholder:"filename"
dump_csv_arg_param
(* String argument --bench-number
Parameter: Number of random stacks to generate. *)
let bench_number_arg =
let bench_number_param =
parse_parameter
int_of_string_opt
"Error while parsing --bench-num argument."
in
Tezos_clic.arg
~doc:"Number of benchmarks (i.e. random stacks)"
~long:"bench-num"
~placeholder:"strictly positive int"
bench_number_param
(* String argument --minor-heap-size
Parameter: size of minor heap in kb. *)
let minor_heap_size_arg =
let minor_heap_size_param =
parse_parameter
(fun s -> Option.map (fun p -> `words p) (int_of_string_opt s))
"Error while parsing --minor-heap-size argument."
in
Tezos_clic.arg
~doc:"Size of minor heap in words"
~long:"minor-heap-size"
~placeholder:"strictly positive int"
minor_heap_size_param
let config_file_arg =
let config_file_arg_param =
Tezos_clic.parameter (fun (_ : unit) parsed -> Lwt.return_ok parsed)
in
Tezos_clic.arg
~doc:"Specify a benchmark configuration file"
~short:'c'
~long:"config-file"
~placeholder:"file"
config_file_arg_param
end
let options =
let open Options in
Tezos_clic.args6
nsamples_arg
seed_arg
bench_number_arg
minor_heap_size_arg
config_file_arg
dump_csv_arg
let benchmark_param =
Tezos_clic.param
~name:"BENCH-NAME"
~desc:"Name of the benchmark"
(Tezos_clic.parameter
~autocomplete:(fun _ ->
let res =
List.map
(fun (name, _) -> Namespace.to_string name)
(Registration.all_benchmarks ())
in
Lwt.return_ok res)
(fun _ str -> Lwt.return_ok str))
let params =
Tezos_clic.(
prefix "benchmark" @@ benchmark_param
@@ prefixes ["and"; "save"; "to"]
@@ string
~name:"FILENAME"
~desc:"Name of the file where to save the workload data"
@@ stop)
let group =
{
Tezos_clic.name = "benchmark";
title = "Commands for benchmarking parts of the protocol";
}
let command =
Tezos_clic.command
~group
~desc:"Runs benchmarks"
options
params
benchmark_handler
end
module Infer_cmd = struct
(* -------------------------------------------------------------------------- *)
(* Handling options for the "infer parameters" command *)
let default_infer_parameters_options =
{
print_problem = false;
csv_export = None;
plot = false;
ridge_alpha = 1.0;
lasso_alpha = 1.0;
lasso_positive = false;
override_files = None;
report = NoReport;
save_solution = None;
dot_file = None;
display = Display.default_options;
}
let set_print_problem print_problem options = {options with print_problem}
let set_csv_export csv_export options = {options with csv_export}
let set_plot plot options = {options with plot}
let set_ridge_alpha ridge_alpha options = {options with ridge_alpha}
let set_lasso_alpha lasso_alpha options = {options with lasso_alpha}
let set_lasso_positive lasso_positive options = {options with lasso_positive}
let set_report report options =
match report with
| None -> {options with report = NoReport}
| Some file ->
if String.equal file "stdout" then
{options with report = ReportToStdout}
else {options with report = ReportToFile file}
let set_override_files override_files options = {options with override_files}
let set_save_solution save_solution options = {options with save_solution}
let set_dot_file dot_file options = {options with dot_file}
let set_full_plot_verbosity full_plot_verbosity options =
{
options with
display =
{options.display with reduced_plot_verbosity = not full_plot_verbosity};
}
let list_solvers () =
Printf.eprintf "ridge --ridge-alpha=<float>\n" ;
Printf.eprintf "lasso --lasso-alpha=<float> --lasso-positive\n" ;
Printf.eprintf "nnls\n%!"
let set_plot_raw_workload plot_raw_workload options =
match plot_raw_workload with
| None ->
{
options with
display = {options.display with plot_raw_workload = false};
}
| Some save_directory ->
{
options with
display =
{options.display with plot_raw_workload = true; save_directory};
}
let set_empirical_plot empirical_plot options =
{options with display = {options.display with empirical_plot}}
let infer_handler
( print_problem,
csv,
plot,
ridge_alpha,
lasso_alpha,
lasso_positive,
report,
override_files,
save_solution,
dot_file,
full_plot_verbosity,
plot_raw_workload,
empirical_plot ) model_name workload_data solver () =
let options =
default_infer_parameters_options
|> set_print_problem print_problem
|> set_csv_export csv |> set_plot plot
|> lift_opt set_ridge_alpha ridge_alpha
|> lift_opt set_lasso_alpha lasso_alpha
|> set_lasso_positive lasso_positive
|> set_report report
|> set_override_files override_files
|> set_save_solution save_solution
|> set_dot_file dot_file
|> set_full_plot_verbosity full_plot_verbosity
|> set_plot_raw_workload plot_raw_workload
|> lift_opt set_empirical_plot empirical_plot
in
commandline_outcome_ref :=
Some (Infer {model_name; workload_data; solver; infer_opts = options}) ;
Lwt.return_ok ()
module Options = struct
(* Boolean argument --print-problem *)
let print_problem =
Tezos_clic.switch
~doc:"Prints problem as obtained after applying model to workload data"
~long:"print-problem"
()
(* Boolean argument --plot *)
let plot_arg =
Tezos_clic.switch
~doc:"Plot results of parameter inference"
~long:"plot"
()
(* Float argument --ridge-alpha *)
let ridge_alpha_arg =
let ridge_alpha_arg_param =
parse_parameter
float_of_string_opt
"Error while parsing --ridge-alpha argument."
in
Tezos_clic.arg
~doc:"Regularization parameter for ridge regression"
~long:"ridge-alpha"
~placeholder:"positive float"
ridge_alpha_arg_param
(* Float argument --lasso-alpha *)
let lasso_alpha_arg =
let lasso_alpha_arg_param =
parse_parameter
float_of_string_opt
"Error while parsing --lasso-alpha argument."
in
Tezos_clic.arg
~doc:"Regularization parameter for lasso regression"
~long:"lasso-alpha"
~placeholder:"positive float"
lasso_alpha_arg_param
(* Boolean argument --lasso-positive *)
let lasso_positive_arg =
Tezos_clic.switch
~doc:"Constrains solution of lasso regression to be positive"
~long:"lasso-positive"
()
let dump_csv_arg =
let dump_csv_arg_param =
Tezos_clic.parameter (fun (_ : unit) parsed -> Lwt.return_ok parsed)
in
Tezos_clic.arg
~doc:"Dumps solution of inference to a CSV file"
~long:"dump-csv"
~placeholder:"filename"
dump_csv_arg_param
let report_arg =
let dump_report_param =
Tezos_clic.parameter (fun (_ : unit) parsed -> Lwt.return_ok parsed)
in
Tezos_clic.arg
~doc:"Produces a detailed report"
~long:"report"
~placeholder:"filename"
dump_report_param
let override_arg =
let override_file_param =
Tezos_clic.parameter (fun (_ : unit) parsed ->
let files = String.split_no_empty ',' parsed in
Lwt.return_ok files)
in
Tezos_clic.arg
~doc:"Specify CSV file containing overrided variables for inference"
~long:"override-csv"
~placeholder:"filename"
override_file_param
let save_solution_arg =
let override_file_param =
Tezos_clic.parameter (fun (_ : unit) parsed -> Lwt.return_ok parsed)
in
Tezos_clic.arg
~doc:
"Specify file to which inference solution will be saved for code \
generation"
~long:"save-solution"
~placeholder:"filename"
override_file_param
let dot_file_arg =
let override_file_param =
Tezos_clic.parameter (fun (_ : unit) parsed -> Lwt.return_ok parsed)
in
Tezos_clic.arg
~doc:
"Specify file to which dependency graph will be saved in graphviz \
format"
~long:"dot-file"
~placeholder:"filename"
override_file_param
let full_plot_verbosity_arg =
Tezos_clic.switch
~doc:"Produces all (possibly redundant) plots"
~long:"full-plot-verbosity"
()
let plot_raw_workload_arg =
let raw_workload_directory_param =
Tezos_clic.parameter (fun (_ : unit) parsed -> Lwt.return_ok parsed)
in
Tezos_clic.arg
~doc:
"For each workload, produces a file containing the plot of the raw \
data, in the specified directory"
~long:"plot-raw-workload"
~placeholder:"directory"
raw_workload_directory_param
let empirical_plot_arg =
let empirical_plot_param =
Tezos_clic.parameter (fun (_ : unit) parsed ->
match parsed with
| "full" -> Lwt.return_ok Display.Empirical_plot_full
| _ -> (
let splitted = String.split ',' parsed in
match List.map float_of_string splitted with
| exception Failure _ ->
Error_monad.failwith "can't parse quantile"
| floats ->
if List.exists (fun q -> q < 0.0 || q > 1.0) floats then
Error_monad.failwith "quantile not in [0;1] interval"
else Lwt.return_ok (Display.Empirical_plot_quantiles floats)
))
in
Tezos_clic.arg
~doc:"Options for plotting empirical data quantiles"
~long:"empirical-plot"
~placeholder:"full|q1,...,qn"
empirical_plot_param
end
let options =
let open Options in
Tezos_clic.args13
print_problem
dump_csv_arg
plot_arg
ridge_alpha_arg
lasso_alpha_arg
lasso_positive_arg
report_arg
override_arg
save_solution_arg
dot_file_arg
full_plot_verbosity_arg
plot_raw_workload_arg
empirical_plot_arg
let local_model_param =
Tezos_clic.param
~name:"LOCAL-MODEL-NAME"
~desc:"Name of the local model for which to infer parameter"
(Tezos_clic.parameter
~autocomplete:(fun _ ->
Lwt.return_ok (Registration.all_local_model_names ()))
(fun _ str -> Lwt.return_ok str))
let regression_param =
Tezos_clic.param
~name:"REGRESSION-METHOD"
~desc:"Regression method used"
(Tezos_clic.parameter
~autocomplete:(fun _ -> Lwt.return_ok ["lasso"; "ridge"; "nnls"])
(fun _ str -> Lwt.return_ok str))
let params =
Tezos_clic.(
prefixes ["infer"; "parameters"; "for"; "model"]
@@ local_model_param
@@ prefixes ["on"; "data"]
@@ string
~name:"WORKLOAD-DATA"
~desc:"File or directory containing workload data"
@@ prefix "using" @@ regression_param @@ stop)
let group =
{
Tezos_clic.name = "inference";
title = "Command for infering parameters of cost models";
}
let command =
Tezos_clic.command
~desc:"Perform parameter inference"
~group
options
params
infer_handler
end
module Codegen_cmd = struct
(* ------------------------------------------------------------------------- *)
(* Handling options for the "generate code" command *)
let load_fixed_point_parameters json_file =
try
let json =
let ic = open_in json_file in
let json = Ezjsonm.from_channel ic in
close_in ic ;
json
in
Fixed_point_transform
(Data_encoding.Json.destruct
Fixed_point_transform.options_encoding
json)
with _ ->
Format.eprintf
"Could not parse fixed-point transform parameters; aborting@." ;
Format.eprintf "Here's a well-formed file:@." ;
Format.eprintf
"%a@."
Data_encoding.Json.pp
(Data_encoding.Json.construct
~include_default_fields:`Always
Fixed_point_transform.options_encoding
Fixed_point_transform.default_options) ;
exit 1
let codegen_handler json solution model_name () =
let codegen_options =
match json with
| None -> No_transform
| Some json_file -> load_fixed_point_parameters json_file
in
let model_name = Namespace.of_string model_name in
commandline_outcome_ref :=
Some (Codegen {solution; model_name; codegen_options}) ;
Lwt.return_ok ()
let fixed_point_arg =
Tezos_clic.arg
~doc:"Apply fixed-point transform to the model"
~long:"fixed-point"
~placeholder:"json-config-file"
(Tezos_clic.parameter (fun () filename -> Lwt.return_ok filename))
let options = Tezos_clic.args1 fixed_point_arg
let codegen_model_param =
Tezos_clic.param
~name:"CODEGEN-MODEL-NAME"
~desc:"Name of the codegen model for which to generate code"
(Tezos_clic.parameter
~autocomplete:(fun _ ->
let res =
List.map
(fun (name, _) -> Namespace.to_string name)
(Registration.all_models ())
in
Lwt.return_ok res)
(fun _ str -> Lwt.return_ok str))
let params =
Tezos_clic.(
prefixes ["generate"; "code"; "using"; "solution"]
@@ string
~name:"SOLUTION-FILE"
~desc:
"File containing solution, as obtained using the --save-solution \
switch"
@@ prefixes ["and"; "model"]
@@ codegen_model_param @@ stop)
let group =
{Tezos_clic.name = "codegen"; title = "Command for generating code"}
let command =
Tezos_clic.command
~group
~desc:"Generate code for a specific model"
options
params
codegen_handler
end
module Codegen_all_cmd = struct
include Codegen_cmd
let codegen_all_handler json solution matching () =
let codegen_options =
match json with
| None -> No_transform
| Some json_file -> load_fixed_point_parameters json_file
in
commandline_outcome_ref :=
Some (Codegen_all {solution; matching; codegen_options}) ;
Lwt.return_ok ()
let params =
Tezos_clic.(
prefixes ["generate"; "code"; "using"; "solution"]
@@ string
~name:"SOLUTION-FILE"
~desc:
"File containing solution, as obtained using the --save-solution \
switch"
@@ prefixes ["for"; "all"; "models"; "matching"]
@@ string ~name:"REGEXP" ~desc:"Regular expression on model names"
@@ stop)
let command =
Tezos_clic.command
~group
~desc:"Generate code for all models matching regexp"
options
params
codegen_all_handler
end
(* Obsolete. It will be superceded by Codegen_for_solutions *)
module Codegen_inferred_cmd = struct
include Codegen_cmd
let codegen_inferred_handler (json, exclusions) solution () =
let codegen_options =
match json with
| None -> No_transform
| Some json_file -> load_fixed_point_parameters json_file
in
let exclusions =
Option.fold
~none:String.Set.empty
~some:Codegen.load_exclusions
exclusions
in
commandline_outcome_ref :=
Some (Codegen_inferred {solution; codegen_options; exclusions}) ;
Lwt.return_ok ()
let params =
Tezos_clic.(
prefixes ["generate"; "code"; "using"; "solution"]
@@ string
~name:"SOLUTION-FILE"
~desc:
"File containing solution, as obtained using the --save-solution \
switch"
@@ fixed ["for"; "inferred"; "models"])
let exclude_arg =
Tezos_clic.arg
~doc:"A file containing the function names to exclude for the codegen"
~long:"exclude-file"
~placeholder:"filename"
(Tezos_clic.parameter (fun (_ : unit) filename -> Lwt.return_ok filename))
let options = Tezos_clic.args2 Codegen_cmd.fixed_point_arg exclude_arg
let command =
Tezos_clic.command
~group
~desc:"Generate code for models inferred from the solution file"
options
params
codegen_inferred_handler
end
module Solution_print_cmd = struct
let solution_print_handler () solutions () =
commandline_outcome_ref := Some (Solution_print solutions) ;
Lwt.return_ok ()
let group =
{Tezos_clic.name = "solution"; title = "Command for solution file"}
let params =
Tezos_clic.(
prefixes ["solution"; "print"]
@@ seq_of_param
(string
~name:"SOLUTION-FILE"
~desc:
"File containing solution, as obtained using the \
--save-solution switch"))
let command =
Tezos_clic.command
~group
~desc:"Print out the given solution file(s)"
Tezos_clic.no_options
params
solution_print_handler
end
module Codegen_for_solutions_cmd = struct
include Codegen_cmd
let codegen_for_solutions_handler (json, exclusions) solutions () =
let codegen_options =
match json with
| None -> No_transform
| Some json_file -> load_fixed_point_parameters json_file
in
let exclusions =
Option.fold
~none:String.Set.empty
~some:Codegen.load_exclusions
exclusions
in
commandline_outcome_ref :=
Some (Codegen_for_solutions {solutions; codegen_options; exclusions}) ;
Lwt.return_ok ()
let params =
Tezos_clic.(
prefixes ["generate"; "code"; "for"; "solutions"]
@@ seq_of_param
(string
~name:"SOLUTION-FILE"
~desc:
"File containing solution, as obtained using the \
--save-solution switch"))
let options = Codegen_inferred_cmd.options
let command =
Tezos_clic.command
~group
~desc:"Generate code for the models inferred from the solution files"
options
params
codegen_for_solutions_handler
end
module Codegen_check_definitions_cmd = struct
let codegen_check_definitions_handler () files () =
commandline_outcome_ref := Some (Codegen_check_definitions {files}) ;
Lwt.return_ok ()
let group = Codegen_cmd.group
let params =
Tezos_clic.(
prefixes ["check"; "definitions"; "of"]
@@ seq_of_param
(string
~name:"MLFILE"
~desc:"File containing cost function definitions"))
let command =
Tezos_clic.(
command
~group
~desc:"Check cost functions defined in the given .ml files"
no_options
params
codegen_check_definitions_handler)
end
module List_cmd = struct
(* ------------------------------------------------------------------------- *)
let tag_param =
Tezos_clic.param
~name:"TAG"
~desc:"Tag of a benchmark"
(Tezos_clic.parameter
~autocomplete:(fun _ -> Lwt.return_ok (Registration.all_tags ()))
(fun _ s -> Lwt.return_ok s))
let benchmark_param () =
Tezos_clic.param
~name:"BENCH-NAME"
~desc:"Name of the benchmark"
(Tezos_clic.parameter
~autocomplete:(fun _ ->
let res =
List.map
(fun (name, _) -> Namespace.to_string name)
(Registration.all_benchmarks ())
in
Lwt.return_ok res)
(fun _ str -> Lwt.return_ok str))
let model_param () =
Tezos_clic.param
~name:"MODEL-NAME"
~desc:"Name of the model"
(Tezos_clic.parameter
~autocomplete:(fun _ ->
let res =
List.map
(fun model_name -> Namespace.to_string model_name)
(Registration.all_model_names ())
in
Lwt.return_ok res)
(fun _ str -> Lwt.return_ok str))
let local_model_param () =
Tezos_clic.param
~name:"LOCAL-MODEL-NAME"
~desc:"Name of the local model"
(Tezos_clic.parameter
~autocomplete:(fun _ ->
Registration.all_local_model_names () |> Lwt.return_ok)
(fun _ str -> Lwt.return_ok str))
let parameter_param () =
Tezos_clic.param
~name:"PARAM-NAME"
~desc:"Name of the parameter"
(Tezos_clic.parameter
~autocomplete:(fun _ ->
let res =
List.map
(fun (param, _) -> Free_variable.to_string param)
(Registration.all_parameters ())
in
Lwt.return_ok res)
(fun _ str -> Lwt.return_ok str))
let params_all_bench = Tezos_clic.fixed ["list"; "all"; "benchmarks"]
let options =
Tezos_clic.args1
(Tezos_clic.switch
~long:"show-tags"
~short:'t'
~doc:"Show the tags of the benchmarks"
())
let base_handler_bench bench_list show_tags =
Format.printf
"@[<v>%a@]@."
(Format.pp_print_list (fun fmt (module Bench : Benchmark.S) ->
Format.fprintf fmt "%a: %s" Namespace.pp Bench.name Bench.info ;
if show_tags then
Format.fprintf fmt "@.\tTags: %a" pp_tags Bench.tags
else ()))
bench_list ;
Lwt_result_syntax.return_unit
let handler_all_bench show_tags () =
base_handler_bench
(Registration.all_benchmarks () |> List.map snd)
show_tags
let params_all_tags = Tezos_clic.fixed ["list"; "all"; "tags"]
let handler_all_tags () () =
List.iter
(fun tag -> Format.fprintf Format.std_formatter "%s\n" tag)
(Registration.all_tags ()) ;
Lwt_result_syntax.return_unit
let params_bench_tags_any =
Tezos_clic.(
prefixes ["list"; "benchmarks"; "with"; "tags"; "any"; "of"]
@@ seq_of_param tag_param)
let handler_bench_tags_any show_tags tags () =
base_handler_bench
(Registration.find_benchmarks_with_tags ~mode:`Any tags |> List.map snd)
show_tags
let params_bench_tags_all =
Tezos_clic.(
prefixes ["list"; "benchmarks"; "with"; "tags"; "all"; "of"]
@@ seq_of_param tag_param)
let handler_bench_tags_all show_tags tags () =
base_handler_bench
(Registration.find_benchmarks_with_tags ~mode:`All tags |> List.map snd)
show_tags
let params_bench_tags_exact =
Tezos_clic.(
prefixes ["list"; "benchmarks"; "with"; "tags"; "exactly"]
@@ seq_of_param tag_param)
let handler_bench_tags_exact show_tags tags () =
base_handler_bench
(Registration.find_benchmarks_with_tags ~mode:`Exact tags |> List.map snd)
show_tags
let params_bench_match =
Tezos_clic.(
prefixes ["list"; "benchmarks"; "in"] @@ benchmark_param () @@ stop)
let handler_bench_match show_tags pattern () =
base_handler_bench
(Registration.find_benchmarks_in_namespace (Namespace.of_string pattern)
|> List.map snd)
show_tags
let params_all_param = Tezos_clic.fixed ["list"; "all"; "parameters"]
let handler_all_param () () =
Format.printf
"%a@."
(Format.pp_print_list (fun fmt (param, models) ->
Format.fprintf
fmt
"%a@.\tModels: %a"
Free_variable.pp
param
(Format.pp_print_list
~pp_sep:(fun formatter () -> Format.fprintf formatter "; ")
Namespace.pp)
models))
(Registration.all_parameters ()) ;
Lwt_result_syntax.return_unit
let params_all_models = Tezos_clic.fixed ["list"; "all"; "models"]
let handler_all_models () () =
Format.printf
"%a@."
(Format.pp_print_list (fun fmt (name, {Registration.model; _}) ->
let printed =
match model with Model.Model model -> print_model model
in
Format.fprintf fmt "%a@.\t%s" Namespace.pp name printed))
(Registration.all_models ()) ;
Lwt_result_syntax.return_unit
let params_all_local_models =
Tezos_clic.fixed ["list"; "all"; "local"; "models"]
let handler_all_local_models () () =
let module S = String.Set in
S.iter
(fun name -> Format.fprintf Format.std_formatter "%s\n" name)
(Registration.all_local_model_names () |> S.of_list) ;
Lwt_result_syntax.return_unit
let group =
{Tezos_clic.name = "list"; title = "Commands for displaying lists"}
let command_all_bench =
Tezos_clic.command
~group
~desc:"List all implemented benchmarks"
options
params_all_bench
handler_all_bench
let command_all_tags =
Tezos_clic.command
~group
~desc:"List all available tags"
Tezos_clic.no_options
params_all_tags
handler_all_tags
let command_bench_tags_any =
Tezos_clic.command
~group
~desc:"List all implemented benchmarks containing any of the given tags"
options
params_bench_tags_any
handler_bench_tags_any
let command_bench_tags_all =
Tezos_clic.command
~group
~desc:"List all implemented benchmarks containing all of the given tags"
options
params_bench_tags_all
handler_bench_tags_all
let command_bench_tags_exact =
Tezos_clic.command
~group
~desc:"List all implemented benchmarks containing exactly the given tags"
options
params_bench_tags_exact
handler_bench_tags_exact
let command_bench_match =
Tezos_clic.command
~group
~desc:"List all benchmarks in the given namespace"
options
params_bench_match
handler_bench_match
let command_all_param =
Tezos_clic.command
~group
~desc:"List all parameters"
Tezos_clic.no_options
params_all_param
handler_all_param
let command_all_models =
Tezos_clic.command
~group
~desc:"List all models"
Tezos_clic.no_options
params_all_models
handler_all_models
let command_all_local_models =
Tezos_clic.command
~group
~desc:"List all local models"
Tezos_clic.no_options
params_all_local_models
handler_all_local_models
let commands =
[
command_all_bench;
command_all_tags;
command_bench_tags_any;
command_bench_tags_all;
command_bench_tags_exact;
command_bench_match;
command_all_param;
command_all_models;
command_all_local_models;
]
end
module Config_cmd = struct
let config_file_param () =
Tezos_clic.param
~name:"CONFIG-FILE"
~desc:"Configuration file name"
(Tezos_clic.parameter (fun _ s -> Lwt.return_ok s))
let benchmark_param = List_cmd.benchmark_param
let options_merge =
Tezos_clic.args1
(Tezos_clic.switch
~long:"delete-source"
~short:'d'
~doc:"Deletes the source config file given as argument for merging"
())
let options_edit =
Tezos_clic.args4
(Tezos_clic.arg
~long:"use-editor"
~short:'e'
~placeholder:"EDITOR"
~doc:
"Specify the prefered text editor used for editing the config file"
(Tezos_clic.parameter (fun _ str -> Lwt.return_ok str)))
(Tezos_clic.switch
~long:"read-stdin"
~short:'i'
~doc:
"Read the standard input for a Json document to edit the config file"
())
(Tezos_clic.arg
~long:"read-file"
~short:'f'
~placeholder:"FILE"
~doc:"Use the given Json document to edit the config file"
(Tezos_clic.parameter (fun _ str -> Lwt.return_ok str)))
(Tezos_clic.arg
~long:"read-json"
~short:'j'
~placeholder:"JSON"
~doc:"Use inlined Json to edit the config file"
(Tezos_clic.parameter (fun _ str -> Lwt.return_ok str)))
let params_check =
Tezos_clic.(
prefixes ["config"; "check"]
@@ config_file_param () @@ prefix "for" @@ benchmark_param () @@ stop)
let handler_check () config_file benchmark () =
let benchmark = Namespace.of_string benchmark in
let bench = Registration.find_benchmark_exn benchmark in
match Benchmark.ex_unpack bench with
| Benchmark.Ex bench ->
let _ =
Config.parse_config ~print:Stdlib.stdout bench (Some config_file)
in
Lwt_result_syntax.return_unit
let params_generate_default =
Tezos_clic.(
prefixes ["config"; "generate"; "default"; "in"]
@@ config_file_param () @@ prefix "for"
@@ seq_of_param (benchmark_param ()))
let handler_generate_default () config_file namespaces () =
let namespaces = List.map Namespace.of_string namespaces in
let benchmarks =
List.map Registration.find_benchmarks_in_namespace namespaces
|> List.flatten |> List.map snd
in
let config = Config.generate_default benchmarks in
let str =
Data_encoding.Json.construct Config.encoding config
|> Data_encoding.Json.to_string
in
let open Lwt.Infix in
Tezos_stdlib_unix.Lwt_utils_unix.create_file config_file str >|= fun r ->
Error_monad.catch (fun () -> r)
let params_generate_empty =
Tezos_clic.(
prefixes ["config"; "generate"; "empty"; "in"]
@@ config_file_param () @@ stop)
let handler_generate_empty () config_file () =
let config = Config.empty in
let str =
Data_encoding.Json.construct Config.encoding config
|> Data_encoding.Json.to_string
in
let open Lwt.Infix in
Tezos_stdlib_unix.Lwt_utils_unix.create_file config_file str >|= fun r ->
Error_monad.catch (fun () -> r)
let config_file_param_dst =
Tezos_clic.param
~name:"DST"
~desc:"Configuration file path destination"
(Tezos_clic.parameter (fun _ s -> Lwt.return_ok s))
let config_file_param_src =
Tezos_clic.param
~name:"SRC"
~desc:"Configuration file path source"
(Tezos_clic.parameter (fun _ s -> Lwt.return_ok s))
let params_merge =
Tezos_clic.(
prefixes ["config"; "merge"]
@@ config_file_param_src @@ prefix "in" @@ config_file_param_dst @@ stop)
let handler_merge delete_flag src dst () =
let () = Config.merge_config_files ~delete_src:delete_flag ~dst ~src () in
Lwt_result_syntax.return_unit
let params_edit =
Tezos_clic.(
prefixes ["config"; "edit"]
@@ config_file_param () @@ prefix "for" @@ benchmark_param () @@ stop)
let handler_edit (editor, stdin, file, json) config_path namespace () =
let namespace = Namespace.of_string namespace in
let input =
match (editor, stdin, file, json) with
| Some e, false, None, None -> `Edit e
| None, _, None, None -> `Stdin
| None, false, Some f, None -> `File f
| None, false, None, Some s -> `String s
| _ ->
Format.eprintf
"Config file edition failed: too many input methods given as \
options@." ;
exit 1
in
let () = Config.edit_config ~input config_path namespace in
Lwt_result_syntax.return_unit
let group =
{
Tezos_clic.name = "config";
title = "Commands for manipulating config files";
}
let command_check =
Tezos_clic.command
~group
~desc:
"Prints the configuration that would be used for a given benchmark, \
given a configuration file"
Tezos_clic.no_options
params_check
handler_check
let command_generate_default =
Tezos_clic.command
~group
~desc:
"Generates a configuration file for the given benchmarks using their \
default configuration"
Tezos_clic.no_options
params_generate_default
handler_generate_default
let command_generate_empty =
Tezos_clic.command
~group
~desc:"Generates an empty configuration file for the given benchmarks"
Tezos_clic.no_options
params_generate_empty
handler_generate_empty
let command_merge =
Tezos_clic.command
~group
~desc:"Merges multiple configuration files. Fails in case of conflict"
options_merge
params_merge
handler_merge
let command_edit =
Tezos_clic.command
~group
~desc:"Edit configuration file at the given point"
options_edit
params_edit
handler_edit
let commands =
[
command_check;
command_generate_default;
command_generate_empty;
command_merge;
command_edit;
]
end
module Generate_config_cmd = struct
let params = Tezos_clic.fixed ["generate"; "default-config"]
let options =
Tezos_clic.args1
(Tezos_clic.arg
~doc:"save default config to file"
~long:"save-to"
~placeholder:"filename"
(Tezos_clic.parameter (fun (_ : unit) parsed -> Lwt.return_ok parsed)))
let show_config_handler filename () =
let json =
Data_encoding.Json.construct
~include_default_fields:`Always
Fixed_point_transform.options_encoding
Fixed_point_transform.default_options
in
(match filename with
| Some filename ->
Out_channel.with_open_text filename @@ fun oc ->
let outfile = Format.formatter_of_out_channel oc in
Format.fprintf outfile "%a@." Data_encoding.Json.pp json ;
Format.eprintf "Saved default config to %s@." filename
| None -> Format.printf "%a@." Data_encoding.Json.pp json) ;
Lwt.return_ok ()
let command =
Tezos_clic.command
~desc:
"Show the default configurations for fixed-point code generation as \
json"
options
params
show_config_handler
end
module Workload_cmd = struct
let options_dump =
Tezos_clic.args1
(Tezos_clic.arg
~doc:"JSON file name to write the content"
~short:'o'
~long:"out-file"
~placeholder:"OUTPUT-FILE"
(Tezos_clic.parameter (fun () parsed -> Lwt.return_ok parsed)))
let handler_dump output_path workload_data () =
let packed_measurement = Measure.load ~filename:workload_data in
Measure.packed_measurement_print_json packed_measurement output_path ;
Lwt.return_ok ()
let params_dump =
Tezos_clic.(
prefixes ["workload"; "dump"]
@@ string ~name:"WORKLOAD-FILE" ~desc:"Workload file name"
@@ stop)
let group =
{
Tezos_clic.name = "workload";
title = "Commands for manipulating workload files";
}
let command_dump =
Tezos_clic.command
~desc:"Dump the content of a workload file in JSON format"
~group
options_dump
params_dump
handler_dump
let commands = [command_dump]
end
module Display_info_cmd = struct
let group =
{
Tezos_clic.name = "display";
title =
"Commands for displaying detailed information for Snoop components";
}
let params_prefix = Tezos_clic.prefixes ["display"; "info"; "for"]
let options = Tezos_clic.no_options
let normal_block fmt title pp obj =
Format.fprintf fmt "%s:@; @[<v>%a@]@." title pp obj
let bold_block fmt title =
let bold_title = Format.asprintf "\027[0;1;4m%s\027[m" title in
normal_block fmt bold_title
let pp_abstract_model fmt (Model.Model m) =
let module M = (val m) in
Format.fprintf fmt "%a" Namespace.pp M.name
let pp_model fmt = function
| Model.Aggregate {sub_models; _} ->
normal_block
fmt
"Aggregated model containing the following abstract models"
(Format.pp_print_list pp_abstract_model)
sub_models
| Model.Abstract {model; _} ->
normal_block
fmt
"Abstract model. Name"
pp_abstract_model
(Model.Model model)
let pp_model_bench fmt (local_name, model) =
normal_block fmt local_name pp_model model
let pp_models () = Format.pp_print_list pp_model_bench
let pp_fancy_benchmark fmt (module B : Benchmark.S) =
bold_block fmt "Name" Namespace.pp B.name ;
bold_block fmt "Filename" Format.pp_print_string B.module_filename ;
bold_block fmt "Generated code destination" Format.pp_print_string
@@ Option.value
~default:"Destination not specified"
B.generated_code_destination ;
bold_block fmt "Info" Format.pp_print_string B.info ;
bold_block fmt "Tags" pp_tags B.tags ;
bold_block fmt "Models" (pp_models ()) B.models
let pp_fancy_model (type a) fmt
((module M : Model.Model_impl with type arg_type = a), l) =
let pp_local fmt {Registration.bench_name; local_model_name} =
Format.fprintf
fmt
"\027[0;33;40m(%s)\027[m %a"
local_model_name
Namespace.pp
bench_name
in
bold_block fmt "Name" Namespace.pp M.name ;
bold_block fmt "Expression" Format.pp_print_string (print_model (module M)) ;
let fv_seq =
Model.get_free_variable_set (module M) |> Free_variable.Set.to_seq
in
bold_block
fmt
"Free variables"
(Format.pp_print_seq Free_variable.pp)
fv_seq ;
bold_block
fmt
"Registered in the following benchmarks"
(Format.pp_print_list pp_local)
l
let pp_fancy_local_model fmt (local_model_name, benchmark_names) =
bold_block fmt "Name" Format.pp_print_string local_model_name ;
bold_block
fmt
"Benchmarks"
(Format.pp_print_list (fun fmt name -> Format.fprintf fmt "%s" name))
benchmark_names
let pp_fancy_parameter fmt (s, l) =
bold_block fmt "Name" Free_variable.pp s ;
bold_block fmt "In models" (Format.pp_print_list Namespace.pp) l
let display_benchmark_handler () s () =
let s = Namespace.of_string s in
let b = Registration.find_benchmark_exn s in
Format.printf "@.%a@." pp_fancy_benchmark b ;
Lwt.return_ok ()
let display_model_handler () s () =
let s = Namespace.of_string s in
let {Registration.model = Model.Model m; from = l} =
Registration.find_model_exn s
in
Format.printf "@.%a@." pp_fancy_model (m, l) ;
Lwt.return_ok ()
let display_local_model_handler () local_model_name () =
let local_models = Registration.all_local_model_names () in
let () =
if not (List.mem ~equal:String.equal local_model_name local_models) then (
Format.eprintf "No local model named %s found.@." local_model_name ;
exit 1)
in
let all_benchmarks =
Registration.all_models ()
|> List.concat_map (fun (_, {Registration.from; model = _}) -> from)
|> List.filter_map
(fun {Registration.bench_name; local_model_name = name} ->
if String.equal local_model_name name then
Some (Namespace.to_string bench_name)
else None)
|> List.sort_uniq String.compare
in
Format.printf
"@.%a@."
pp_fancy_local_model
(local_model_name, all_benchmarks) ;
Lwt.return_ok ()
let display_parameter_handler () s () =
let s = Free_variable.of_string s in
let l = Registration.find_parameter_exn s in
Format.printf "@.%a@." pp_fancy_parameter (s, l) ;
Lwt.return_ok ()
let display_benchmark_params =
Tezos_clic.(
params_prefix @@ prefix "benchmark" @@ List_cmd.benchmark_param () @@ stop)
let display_model_params =
Tezos_clic.(
params_prefix @@ prefix "model" @@ List_cmd.model_param () @@ stop)
let display_local_model_params =
Tezos_clic.(
params_prefix @@ prefix "local" @@ prefix "model"
@@ List_cmd.local_model_param ()
@@ stop)
let display_parameter_params =
Tezos_clic.(
params_prefix @@ prefix "parameter" @@ List_cmd.parameter_param () @@ stop)
let command_benchmark =
Tezos_clic.command
~group
~desc:"Display detailed information on the given benchmark"
options
display_benchmark_params
display_benchmark_handler
let command_model =
Tezos_clic.command
~group
~desc:"Display detailed information on the given model"
options
display_model_params
display_model_handler
let command_local_model =
Tezos_clic.command
~group
~desc:"Display detailed information on the given local model"
options
display_local_model_params
display_local_model_handler
let command_parameter =
Tezos_clic.command
~group
~desc:"Display detailed information on the given parameter"
options
display_parameter_params
display_parameter_handler
let commands =
[command_benchmark; command_model; command_parameter; command_local_model]
end
let all_commands =
[
Benchmark_cmd.command;
Infer_cmd.command;
Codegen_cmd.command;
Codegen_all_cmd.command;
Codegen_inferred_cmd.command;
Codegen_for_solutions_cmd.command;
Codegen_check_definitions_cmd.command;
Generate_config_cmd.command;
Solution_print_cmd.command;
]
@ List_cmd.commands @ Config_cmd.commands @ Workload_cmd.commands
@ Display_info_cmd.commands
@ Registration.all_custom_commands ()
module Global_options = struct
(* --list-solvers *)
let list_solvers =
Tezos_clic.switch ~doc:"List all available solvers" ~long:"list-solvers" ()
(* --list-models *)
let list_models =
Tezos_clic.switch ~doc:"List all models" ~long:"list-models" ()
let options = Tezos_clic.args2 list_solvers list_models
end
let commands_with_man =
Tezos_clic.add_manual
~executable_name:(Filename.basename Sys.executable_name)
~global_options:Global_options.options
(if Unix.isatty Unix.stdout then Tezos_clic.Ansi else Tezos_clic.Plain)
Format.std_formatter
all_commands
let usage () =
Tezos_clic.usage
Format.std_formatter
~executable_name:(Filename.basename Sys.executable_name)
~global_options:Global_options.options
commands_with_man
let original_args, autocomplete =
(* for shell aliases *)
let rec move_autocomplete_token_upfront acc = function
| "bash_autocomplete" :: prev_arg :: cur_arg :: script :: args ->
let args = List.rev acc @ args in
(args, Some (prev_arg, cur_arg, script))
| x :: rest -> move_autocomplete_token_upfront (x :: acc) rest
| [] -> (List.rev acc, None)
in
match Array.to_list Sys.argv with
| _ :: args -> move_autocomplete_token_upfront [] args
| [] -> ([], None)
let list_solvers, list_models =
ignore
Tezos_clic.(
setup_formatter
Format.std_formatter
(if Unix.isatty Unix.stdout then Ansi else Plain)
Short) ;
let result =
Lwt_main.run
(let open Lwt_result_syntax in
let* list_flags, args =
Tezos_clic.parse_global_options Global_options.options () original_args
in
match autocomplete with
| Some (prev_arg, cur_arg, script) ->
let* completions =
Tezos_clic.autocompletion
~script
~cur_arg
~prev_arg
~args:original_args
~global_options:Global_options.options
commands_with_man
()
in
List.iter print_endline completions ;
return list_flags
| None -> (
match args with
| [] -> return list_flags
| _ ->
let* () = Tezos_clic.dispatch commands_with_man () args in
return list_flags))
in
match result with
| Ok global_options -> global_options
| Error [Tezos_clic.Version] ->
let version = Tezos_version.Bin_version.version_string in
Format.printf "%s\n" version ;
exit 0
| Error [Tezos_clic.Help command] ->
Tezos_clic.usage
Format.std_formatter
~executable_name:(Filename.basename Sys.executable_name)
~global_options:Global_options.options
(match command with None -> [] | Some c -> [c]) ;
exit 0
| Error errors ->
Tezos_clic.pp_cli_errors
Format.err_formatter
~executable_name:(Filename.basename Sys.executable_name)
~global_options:Global_options.options
~default:(fun fmt err -> Error_monad.pp_print_trace fmt [err])
errors ;
exit 1