diff --git a/docs/md/tutorial.md b/docs/md/tutorial.md index e0b042a30d..93dbbe71c8 100644 --- a/docs/md/tutorial.md +++ b/docs/md/tutorial.md @@ -30,6 +30,43 @@ int main(int argc, char** argv) { } \endcode +If, for any reason, you want to predefine configuration, you can do it +by creating `AppConfig` object, setting its members as you wish, +and passing it to `vt::initialize`: + +\code{.cpp} +int main(int argc, char** argv) { + arguments::AppConfig appConfig{}; + appConfig.vt_lb_name = "RotateLB"; + appConfig.vt_lb_stats = true; + + vt::initialize(argc, argv, &appConfig); + // program here + vt::finalize(); +} +\endcode + +You can do also do it if you initialized MPI on your own: + +\code{.cpp} +int main(int argc, char** argv) { + MPI_Init(&argc, &argv); + + arguments::AppConfig appConfig{}; + appConfig.vt_lb_name = "RotateLB"; + appConfig.vt_lb_stats = true; + + vt::initialize(argc, argv, &MPI_COMM_WORLD, &appConfig); + // program here + vt::finalize(); + MPI_Finalize(); +} +\endcode + +It is worth noting that if you run your application with any of vt's command-line arguments and at the same time you define and pass `AppConfig` to `vt::initialize`, CLI arguments have a higher priority. In other words, if you predefine in source code and give from the command line the same vt's argument, but with a different value, the program will use the CLI one. + +There is also an option to use configuration file. Refer to CLI11 documentation for details https://cliutils.github.io/CLI11/book/chapters/config.html. Important thing to remember - CLI11 processes configuration file before command line arguments, so in the end command line arguments might overwrite values defined in configuration file. + \section tutorial-walkthrough Tutorial Code Snippets This page walks through the tutorial that exists in the source code. See diff --git a/examples/hello_world/objgroup.cc b/examples/hello_world/objgroup.cc index e5e36c1c32..4c07e01d2c 100644 --- a/examples/hello_world/objgroup.cc +++ b/examples/hello_world/objgroup.cc @@ -57,7 +57,7 @@ struct MyObjGroup { }; int main(int argc, char** argv) { - vt::initialize(argc, argv, nullptr); + vt::initialize(argc, argv); vt::NodeType this_node = vt::theContext()->getNode(); vt::NodeType num_nodes = vt::theContext()->getNumNodes(); diff --git a/src/vt/collective/collective_ops.cc b/src/vt/collective/collective_ops.cc index 8da2080fdd..93a2d2d6c9 100644 --- a/src/vt/collective/collective_ops.cc +++ b/src/vt/collective/collective_ops.cc @@ -42,6 +42,7 @@ */ #include "vt/collective/collective_ops.h" +#include "vt/context/context.h" #include "vt/runtime/runtime.h" #include "vt/scheduler/scheduler.h" #include "vt/runtime/runtime_inst.h" @@ -53,10 +54,166 @@ namespace vt { +namespace { + +#define printIfOverwritten(opt) \ + do { \ + if (cliConfig.opt != appConfig.opt) { \ + ++overwrittens; \ + fmt::print( \ + "{}\t{}--" #opt "{}\n", \ + vt_pre, magenta, reset \ + ); \ + } \ + } while (0) + +void printOverwrittens( + vt::arguments::AppConfig const &cliConfig, + vt::arguments::AppConfig const &appConfig +) { + auto const green = debug::green(); + auto const reset = debug::reset(); + auto const magenta = debug::magenta(); + auto const vt_pre = debug::vtPre(); + + fmt::print( + "{}{}Predefined options overwritten by CLI arguments:{}\n", + vt_pre, green, reset + ); + + int overwrittens = 0; + + printIfOverwritten(vt_color); + printIfOverwritten(vt_no_color); + printIfOverwritten(vt_quiet); + printIfOverwritten(vt_sched_progress_han); + printIfOverwritten(vt_sched_progress_sec); + printIfOverwritten(vt_no_sigint); + printIfOverwritten(vt_no_sigsegv); + printIfOverwritten(vt_no_sigbus); + printIfOverwritten(vt_no_terminate); + printIfOverwritten(vt_memory_reporters); + printIfOverwritten(vt_print_memory_each_phase); + printIfOverwritten(vt_print_memory_node); + printIfOverwritten(vt_allow_memory_report_with_ps); + printIfOverwritten(vt_print_memory_at_threshold); + printIfOverwritten(vt_print_memory_threshold); + printIfOverwritten(vt_print_memory_sched_poll); + printIfOverwritten(vt_print_memory_footprint); + printIfOverwritten(vt_no_warn_stack); + printIfOverwritten(vt_no_assert_stack); + printIfOverwritten(vt_no_abort_stack); + printIfOverwritten(vt_no_stack); + printIfOverwritten(vt_stack_file); + printIfOverwritten(vt_stack_dir); + printIfOverwritten(vt_stack_mod); + printIfOverwritten(vt_trace); + printIfOverwritten(vt_trace_mpi); + printIfOverwritten(vt_trace_pmpi); + printIfOverwritten(vt_trace_sys_all); + printIfOverwritten(vt_trace_sys_term); + printIfOverwritten(vt_trace_sys_location); + printIfOverwritten(vt_trace_sys_collection); + printIfOverwritten(vt_trace_sys_serial_msg); + printIfOverwritten(vt_trace_file); + printIfOverwritten(vt_trace_dir); + printIfOverwritten(vt_trace_mod); + printIfOverwritten(vt_trace_flush_size); + printIfOverwritten(vt_trace_spec); + printIfOverwritten(vt_trace_spec_file); + printIfOverwritten(vt_trace_memory_usage); + printIfOverwritten(vt_trace_event_polling); + printIfOverwritten(vt_trace_irecv_polling); + printIfOverwritten(vt_lb); + printIfOverwritten(vt_lb_show_spec); + printIfOverwritten(vt_lb_quiet); + printIfOverwritten(vt_lb_file_name); + printIfOverwritten(vt_lb_name); + printIfOverwritten(vt_lb_args); + printIfOverwritten(vt_lb_interval); + printIfOverwritten(vt_lb_stats); + printIfOverwritten(vt_lb_stats_compress); + printIfOverwritten(vt_lb_stats_dir); + printIfOverwritten(vt_lb_stats_file); + printIfOverwritten(vt_lb_stats_dir_in); + printIfOverwritten(vt_lb_stats_file_in); + printIfOverwritten(vt_help_lb_args); + printIfOverwritten(vt_no_detect_hang); + printIfOverwritten(vt_print_no_progress); + printIfOverwritten(vt_epoch_graph_on_hang); + printIfOverwritten(vt_epoch_graph_terse); + printIfOverwritten(vt_term_rooted_use_ds); + printIfOverwritten(vt_term_rooted_use_wave); + printIfOverwritten(vt_hang_freq); + printIfOverwritten(vt_diag_enable); + printIfOverwritten(vt_diag_print_summary); + printIfOverwritten(vt_diag_summary_csv_file); + printIfOverwritten(vt_diag_summary_file); + printIfOverwritten(vt_diag_csv_base_units); + printIfOverwritten(vt_pause); + printIfOverwritten(vt_no_assert_fail); + printIfOverwritten(vt_throw_on_abort); + printIfOverwritten(vt_max_mpi_send_size); + printIfOverwritten(vt_debug_level); + printIfOverwritten(vt_debug_all); + printIfOverwritten(vt_debug_none); + printIfOverwritten(vt_debug_gen); + printIfOverwritten(vt_debug_runtime); + printIfOverwritten(vt_debug_active); + printIfOverwritten(vt_debug_term); + printIfOverwritten(vt_debug_termds); + printIfOverwritten(vt_debug_barrier); + printIfOverwritten(vt_debug_event); + printIfOverwritten(vt_debug_pipe); + printIfOverwritten(vt_debug_pool); + printIfOverwritten(vt_debug_reduce); + printIfOverwritten(vt_debug_rdma); + printIfOverwritten(vt_debug_rdma_channel); + printIfOverwritten(vt_debug_rdma_state); + printIfOverwritten(vt_debug_param); + printIfOverwritten(vt_debug_handler); + printIfOverwritten(vt_debug_hierlb); + printIfOverwritten(vt_debug_temperedlb); + printIfOverwritten(vt_debug_scatter); + printIfOverwritten(vt_debug_sequence); + printIfOverwritten(vt_debug_sequence_vrt); + printIfOverwritten(vt_debug_serial_msg); + printIfOverwritten(vt_debug_trace); + printIfOverwritten(vt_debug_location); + printIfOverwritten(vt_debug_lb); + printIfOverwritten(vt_debug_vrt); + printIfOverwritten(vt_debug_vrt_coll); + printIfOverwritten(vt_debug_worker); + printIfOverwritten(vt_debug_group); + printIfOverwritten(vt_debug_broadcast); + printIfOverwritten(vt_debug_objgroup); + printIfOverwritten(vt_debug_phase); + printIfOverwritten(vt_debug_context); + printIfOverwritten(vt_debug_epoch); + printIfOverwritten(vt_debug_print_flush); + printIfOverwritten(vt_user_1); + printIfOverwritten(vt_user_2); + printIfOverwritten(vt_user_3); + printIfOverwritten(vt_user_int_1); + printIfOverwritten(vt_user_int_2); + printIfOverwritten(vt_user_int_3); + printIfOverwritten(vt_user_str_1); + printIfOverwritten(vt_user_str_2); + printIfOverwritten(vt_user_str_3); + printIfOverwritten(vt_output_config); + printIfOverwritten(vt_output_config_file); + + if (overwrittens == 0) { + fmt::print("{}\tNone.\n", vt_pre); + } +} + +} /* end anon namespace */ + template RuntimePtrType CollectiveAnyOps::initialize( int& argc, char**& argv, WorkerCountType const num_workers, - bool is_interop, MPI_Comm* comm + bool is_interop, MPI_Comm* comm, arguments::AppConfig const* appConfig ) { using vt::runtime::RuntimeInst; using vt::runtime::Runtime; @@ -66,7 +223,8 @@ RuntimePtrType CollectiveAnyOps::initialize( #pragma sst global rt RuntimeInst::rt = std::make_unique( - argc, argv, num_workers, is_interop, resolved_comm + argc, argv, num_workers, is_interop, resolved_comm, + eRuntimeInstance::DefaultInstance, appConfig ); #pragma sst global rt @@ -79,6 +237,12 @@ RuntimePtrType CollectiveAnyOps::initialize( #pragma sst global rt RuntimeInst::rt->initialize(); + // If appConfig is not nullptr, compare CLI arguments with user-defined ones, + // and report overwritten ones. + if (appConfig && theContext()->getNode() == 0) { + printOverwrittens(*rt->getAppConfig(), *appConfig); + } + return runtime::makeRuntimePtr(rt_ptr); } diff --git a/src/vt/collective/collective_ops.h b/src/vt/collective/collective_ops.h index 2fb310e40e..849ac3a6a7 100644 --- a/src/vt/collective/collective_ops.h +++ b/src/vt/collective/collective_ops.h @@ -63,7 +63,8 @@ struct CollectiveAnyOps { // The general methods that interact with the managed runtime holder static RuntimePtrType initialize( int& argc, char**& argv, WorkerCountType const num_workers = no_workers, - bool is_interop = false, MPI_Comm* comm = nullptr + bool is_interop = false, MPI_Comm* comm = nullptr, + arguments::AppConfig const* appConfig = nullptr ); static void finalize(RuntimePtrType in_rt = nullptr); static void scheduleThenFinalize( diff --git a/src/vt/collective/startup.cc b/src/vt/collective/startup.cc index f6c862ab12..1c01d3d56d 100644 --- a/src/vt/collective/startup.cc +++ b/src/vt/collective/startup.cc @@ -52,14 +52,20 @@ namespace vt { // vt::{initialize,finalize} for main ::vt namespace RuntimePtrType initialize( int& argc, char**& argv, WorkerCountType const num_workers, - bool is_interop, MPI_Comm* comm + bool is_interop, MPI_Comm* comm, arguments::AppConfig const* appConfig ) { - return CollectiveOps::initialize(argc,argv,num_workers,is_interop,comm); + return CollectiveOps::initialize( + argc, argv, num_workers, is_interop, comm, appConfig + ); } -RuntimePtrType initialize(int& argc, char**& argv, MPI_Comm* comm) { +RuntimePtrType initialize( + int& argc, char**& argv, MPI_Comm* comm, arguments::AppConfig const* appConfig +) { bool const is_interop = comm != nullptr; - return CollectiveOps::initialize(argc,argv,no_workers,is_interop,comm); + return CollectiveOps::initialize( + argc, argv, no_workers, is_interop, comm, appConfig + ); } RuntimePtrType initialize(MPI_Comm* comm) { @@ -69,6 +75,18 @@ RuntimePtrType initialize(MPI_Comm* comm) { return CollectiveOps::initialize(argc,argv,no_workers,is_interop,comm); } +RuntimePtrType initialize( + int& argc, char**& argv, arguments::AppConfig const* appConfig +) { + return initialize(argc, argv, nullptr, appConfig); +} + +RuntimePtrType initialize(arguments::AppConfig const* appConfig) { + int argc = 0; + char** argv = nullptr; + return initialize(argc, argv, nullptr, appConfig); +} + void finalize(RuntimePtrType in_rt) { if (in_rt) { return CollectiveOps::finalize(std::move(in_rt)); diff --git a/src/vt/collective/startup.h b/src/vt/collective/startup.h index e03a09c36d..2f50850c0c 100644 --- a/src/vt/collective/startup.h +++ b/src/vt/collective/startup.h @@ -51,10 +51,17 @@ namespace vt { RuntimePtrType initialize( int& argc, char**& argv, WorkerCountType const num_workers, - bool is_interop = false, MPI_Comm* comm = nullptr + bool is_interop = false, MPI_Comm* comm = nullptr, + arguments::AppConfig const* appConfig = nullptr +); +RuntimePtrType initialize( + int& argc, char**& argv, MPI_Comm* comm = nullptr, + arguments::AppConfig const* appConfig = nullptr ); -RuntimePtrType initialize(int& argc, char**& argv, MPI_Comm* comm = nullptr); RuntimePtrType initialize(MPI_Comm* comm = nullptr); +RuntimePtrType initialize( + int& argc, char**& argv, arguments::AppConfig const* appConfig +); void finalize(RuntimePtrType in_rt); void finalize(); diff --git a/src/vt/configs/arguments/args.cc b/src/vt/configs/arguments/args.cc index 7a8dfc1218..1f3b92a0a7 100644 --- a/src/vt/configs/arguments/args.cc +++ b/src/vt/configs/arguments/args.cc @@ -56,21 +56,141 @@ namespace vt { namespace arguments { // Temporary variables used only for parsing artifacts. namespace { + std::vector arg_trace_mpi; -} /* end anon namespace */ -/*static*/ std::unique_ptr -ArgConfig::construct(std::unique_ptr arg) { - return arg; +/** + * \internal + * Application specific cleanup and mapping to actual app args. + */ +void postParseTransform(AppConfig& appConfig) { + auto contains = [](std::vector &v, std::string str){ + return std::find(v.begin(), v.end(), str) not_eq v.end(); + }; + + appConfig.vt_trace_mpi = contains(arg_trace_mpi, "internal"); + appConfig.vt_trace_pmpi = contains(arg_trace_mpi, "external"); + + using config::ModeEnum; + + auto const& level = appConfig.vt_debug_level; + if (level == "terse" or level == "0") { + appConfig.vt_debug_level_val = ModeEnum::terse; + } else if (level == "normal" or level == "1") { + appConfig.vt_debug_level_val = ModeEnum::terse | ModeEnum::normal; + } else if (level == "verbose" or level == "2") { + appConfig.vt_debug_level_val = + ModeEnum::terse | ModeEnum::normal | ModeEnum::verbose; + } else { + vtAbort("Invalid value passed to --vt_debug_level"); + } +} + +std::tuple parseArguments( + CLI::App& app, int& argc, char**& argv, AppConfig& appConfig +) { + + std::vector vt_args; + + // Load up vectors (has curious ability to altnerate vt/mpi/passthru) + std::vector* rargs = nullptr; + for (int i = 1; i < argc; i++) { + char* c = argv[i]; + if (0 == strcmp(c, "--vt_args")) { + rargs = &vt_args; + } else if (0 == strcmp(c, "--")) { + rargs = &appConfig.passthru_args; + } else if (rargs) { + rargs->push_back(c); + } else if (0 == strncmp(c, "--vt_", 5)) { + // Implicit start of VT args allows pass-thru 'for compatibility' + // although the recommended calling pattern to always provide VT args first. + rargs = &vt_args; + rargs->push_back(c); + } else { + appConfig.passthru_args.push_back(c); + } + } + + // All must be accounted for + app.allow_extras(false); + + // Build string-vector and reverse order to parse (CLI quirk) + std::vector args_to_parse; + for (auto it = vt_args.crbegin(); it != vt_args.crend(); ++it) { + args_to_parse.push_back(*it); + } + + // Allow a input config file + app.set_config( + "--vt_input_config", + "", // no default file name + "Read in an ini config file for VT", + false // not required + ); + + try { + app.parse(args_to_parse); + } catch (CLI::Error &ex) { + // Return exit code and message, delaying logic processing of such. + // The default exit code for 'help' is 0. + std::stringstream message_stream; + int result = app.exit(ex, message_stream, message_stream); + + return std::make_tuple(result, message_stream.str()); + } + + // If the user specified to output the full configuration, save it in a string + // so node 0 can output in the runtime once MPI is init'ed + if (appConfig.vt_output_config) { + appConfig.vt_output_config_str = app.config_to_str(true, true); + } + + // Get the clean prog name; don't allow path bleed in usages. + // std::filesystem is C++17. + std::string clean_prog_name = argv[0]; + size_t l = clean_prog_name.find_last_of("/\\"); + if (l not_eq std::string::npos and l + 1 < clean_prog_name.size()) { + clean_prog_name = clean_prog_name.substr(l + 1, std::string::npos); + } + + appConfig.prog_name = clean_prog_name; + appConfig.argv_prog_name = argv[0]; + + postParseTransform(appConfig); + + // Rebuild passthru into ref-returned argc/argv + + // It should be possible to modify the original argv as the outgoing + // number of arguments is always less. As currently allocated here, + // ownership of the new object is ill-defined. + int new_argc = appConfig.passthru_args.size() + 1; // does not include argv[0] + + static std::unique_ptr new_argv = nullptr; + + new_argv = std::make_unique(new_argc + 1); + + int i = 0; + new_argv[i++] = appConfig.argv_prog_name; + for (auto&& arg : appConfig.passthru_args) { + new_argv[i++] = arg; + } + new_argv[i++] = nullptr; + + // Set them back with all vt (and MPI) arguments elided + argc = new_argc; + argv = new_argv.get(); + + return std::make_tuple(-1, std::string{}); } -void ArgConfig::addColorArgs(CLI::App& app) { +void addColorArgs(CLI::App& app, AppConfig& appConfig) { auto quiet = "Quiet the output from vt (only errors, warnings)"; auto always = "Colorize output (default)"; auto never = "Do not colorize output (overrides --vt_color)"; - auto a = app.add_flag("--vt_color", config_.vt_color, always); - auto b = app.add_flag("--vt_no_color", config_.vt_no_color, never); - auto a1 = app.add_flag("--vt_quiet", config_.vt_quiet, quiet); + auto a = app.add_flag("--vt_color", appConfig.vt_color, always); + auto b = app.add_flag("--vt_no_color", appConfig.vt_no_color, never); + auto a1 = app.add_flag("--vt_quiet", appConfig.vt_quiet, quiet); auto outputGroup = "Output Control"; a->group(outputGroup); b->group(outputGroup); @@ -81,15 +201,15 @@ void ArgConfig::addColorArgs(CLI::App& app) { // b->excludes(a); } -void ArgConfig::addSignalArgs(CLI::App& app) { +void addSignalArgs(CLI::App& app, AppConfig& appConfig) { auto no_sigint = "Do not register signal handler for SIGINT"; auto no_sigsegv = "Do not register signal handler for SIGSEGV"; auto no_sigbus = "Do not register signal handler for SIGBUS"; auto no_terminate = "Do not register handler for std::terminate"; - auto d = app.add_flag("--vt_no_SIGINT", config_.vt_no_sigint, no_sigint); - auto e = app.add_flag("--vt_no_SIGSEGV", config_.vt_no_sigsegv, no_sigsegv); - auto g = app.add_flag("--vt_no_SIGBUS", config_.vt_no_sigbus, no_sigbus); - auto f = app.add_flag("--vt_no_terminate", config_.vt_no_terminate, no_terminate); + auto d = app.add_flag("--vt_no_SIGINT", appConfig.vt_no_sigint, no_sigint); + auto e = app.add_flag("--vt_no_SIGSEGV", appConfig.vt_no_sigsegv, no_sigsegv); + auto g = app.add_flag("--vt_no_SIGBUS", appConfig.vt_no_sigbus, no_sigbus); + auto f = app.add_flag("--vt_no_terminate", appConfig.vt_no_terminate, no_terminate); auto signalGroup = "Signal Handling"; d->group(signalGroup); e->group(signalGroup); @@ -97,7 +217,7 @@ void ArgConfig::addSignalArgs(CLI::App& app) { g->group(signalGroup); } -void ArgConfig::addMemUsageArgs(CLI::App& app) { +void addMemUsageArgs(CLI::App& app, AppConfig& appConfig) { /* * Flags for controlling memory usage reporting */ @@ -109,14 +229,14 @@ void ArgConfig::addMemUsageArgs(CLI::App& app) { auto mem_thresh = "The threshold increments to print memory usage: \" {GiB,MiB,KiB,B}\""; auto mem_sched = "The frequency to query the memory threshold check (some memory reporters might be expensive)"; auto mem_footprint = "Print live components' memory footprint after initialization and before shutdown"; - auto mm = app.add_option("--vt_memory_reporters", config_.vt_memory_reporters, mem_desc, true); - auto mn = app.add_flag("--vt_print_memory_each_phase", config_.vt_print_memory_each_phase, mem_phase); - auto mo = app.add_option("--vt_print_memory_node", config_.vt_print_memory_node, mem_node, true); - auto mp = app.add_flag("--vt_allow_memory_report_with_ps", config_.vt_allow_memory_report_with_ps, mem_ps); - auto mq = app.add_flag("--vt_print_memory_at_threshold", config_.vt_print_memory_at_threshold, mem_at_thresh); - auto mr = app.add_option("--vt_print_memory_threshold", config_.vt_print_memory_threshold, mem_thresh, true); - auto ms = app.add_option("--vt_print_memory_sched_poll", config_.vt_print_memory_sched_poll, mem_sched, true); - auto mf = app.add_flag("--vt_print_memory_footprint", config_.vt_print_memory_footprint, mem_footprint); + auto mm = app.add_option("--vt_memory_reporters", appConfig.vt_memory_reporters, mem_desc, true); + auto mn = app.add_flag("--vt_print_memory_each_phase", appConfig.vt_print_memory_each_phase, mem_phase); + auto mo = app.add_option("--vt_print_memory_node", appConfig.vt_print_memory_node, mem_node, true); + auto mp = app.add_flag("--vt_allow_memory_report_with_ps", appConfig.vt_allow_memory_report_with_ps, mem_ps); + auto mq = app.add_flag("--vt_print_memory_at_threshold", appConfig.vt_print_memory_at_threshold, mem_at_thresh); + auto mr = app.add_option("--vt_print_memory_threshold", appConfig.vt_print_memory_threshold, mem_thresh, true); + auto ms = app.add_option("--vt_print_memory_sched_poll", appConfig.vt_print_memory_sched_poll, mem_sched, true); + auto mf = app.add_flag("--vt_print_memory_footprint", appConfig.vt_print_memory_footprint, mem_footprint); auto memoryGroup = "Memory Usage Reporting"; mm->group(memoryGroup); mn->group(memoryGroup); @@ -128,8 +248,7 @@ void ArgConfig::addMemUsageArgs(CLI::App& app) { mf->group(memoryGroup); } - -void ArgConfig::addStackDumpArgs(CLI::App& app) { +void addStackDumpArgs(CLI::App& app, AppConfig& appConfig) { /* * Flags to control stack dumping */ @@ -140,13 +259,13 @@ void ArgConfig::addStackDumpArgs(CLI::App& app) { auto file = "Dump stack traces to file instead of stdout"; auto dir = "Name of directory to write stack files"; auto mod = "Write stack dump if (node % config_.vt_stack_mod) == 0"; - auto g = app.add_flag("--vt_no_warn_stack", config_.vt_no_warn_stack, warn); - auto h = app.add_flag("--vt_no_assert_stack", config_.vt_no_assert_stack, assert); - auto i = app.add_flag("--vt_no_abort_stack", config_.vt_no_abort_stack, abort); - auto j = app.add_flag("--vt_no_stack", config_.vt_no_stack, stack); - auto k = app.add_option("--vt_stack_file", config_.vt_stack_file, file, ""); - auto l = app.add_option("--vt_stack_dir", config_.vt_stack_dir, dir, ""); - auto m = app.add_option("--vt_stack_mod", config_.vt_stack_mod, mod, 1); + auto g = app.add_flag("--vt_no_warn_stack", appConfig.vt_no_warn_stack, warn); + auto h = app.add_flag("--vt_no_assert_stack", appConfig.vt_no_assert_stack, assert); + auto i = app.add_flag("--vt_no_abort_stack", appConfig.vt_no_abort_stack, abort); + auto j = app.add_flag("--vt_no_stack", appConfig.vt_no_stack, stack); + auto k = app.add_option("--vt_stack_file", appConfig.vt_stack_file, file, ""); + auto l = app.add_option("--vt_stack_dir", appConfig.vt_stack_dir, dir, ""); + auto m = app.add_option("--vt_stack_mod", appConfig.vt_stack_mod, mod, 1); auto stackGroup = "Dump Stack Backtrace"; g->group(stackGroup); h->group(stackGroup); @@ -157,7 +276,7 @@ void ArgConfig::addStackDumpArgs(CLI::App& app) { m->group(stackGroup); } -void ArgConfig::addTraceArgs(CLI::App& app) { +void addTraceArgs(CLI::App& app, AppConfig& appConfig) { /* * Flags to control tracing output */ @@ -179,24 +298,24 @@ void ArgConfig::addTraceArgs(CLI::App& app) { auto tmemusage = "Trace memory usage using first memory reporter"; auto tpolled = "Trace AsyncEvent component polling (inc. MPI_Isend requests)"; auto tirecv = "Trace MPI_Irecv request polling"; - auto n = app.add_flag("--vt_trace", config_.vt_trace, trace); + auto n = app.add_flag("--vt_trace", appConfig.vt_trace, trace); auto nm = app.add_option("--vt_trace_mpi", arg_trace_mpi, trace_mpi) ->check(CLI::IsMember({"internal", "external"})); - auto o = app.add_option("--vt_trace_file", config_.vt_trace_file, tfile, ""); - auto p = app.add_option("--vt_trace_dir", config_.vt_trace_dir, tdir, ""); - auto q = app.add_option("--vt_trace_mod", config_.vt_trace_mod, tmod, 1); - auto qf = app.add_option("--vt_trace_flush_size", config_.vt_trace_flush_size, tflushmod, 0); - auto qg = app.add_flag("--vt_trace_gzip_finish_flush", config_.vt_trace_gzip_finish_flush, zflush); - auto qt = app.add_flag("--vt_trace_sys_all", config_.vt_trace_sys_all, tsysall); - auto qw = app.add_flag("--vt_trace_sys_term", config_.vt_trace_sys_term, tsysTD); - auto qx = app.add_flag("--vt_trace_sys_location", config_.vt_trace_sys_location, tsysloc); - auto qy = app.add_flag("--vt_trace_sys_collection", config_.vt_trace_sys_collection, tsyscoll); - auto qz = app.add_flag("--vt_trace_sys_serial_msg", config_.vt_trace_sys_serial_msg, tsyssmsg); - auto qza = app.add_flag("--vt_trace_spec", config_.vt_trace_spec, tspec); - auto qzb = app.add_option("--vt_trace_spec_file", config_.vt_trace_spec_file, tspecfile, ""); - auto qzc = app.add_flag("--vt_trace_memory_usage", config_.vt_trace_memory_usage, tmemusage); - auto qzd = app.add_flag("--vt_trace_event_polling", config_.vt_trace_event_polling, tpolled); - auto qze = app.add_flag("--vt_trace_irecv_polling", config_.vt_trace_irecv_polling, tirecv); + auto o = app.add_option("--vt_trace_file", appConfig.vt_trace_file, tfile, ""); + auto p = app.add_option("--vt_trace_dir", appConfig.vt_trace_dir, tdir, ""); + auto q = app.add_option("--vt_trace_mod", appConfig.vt_trace_mod, tmod, 1); + auto qf = app.add_option("--vt_trace_flush_size", appConfig.vt_trace_flush_size, tflushmod, 0); + auto qg = app.add_flag("--vt_trace_gzip_finish_flush", appConfig.vt_trace_gzip_finish_flush, zflush); + auto qt = app.add_flag("--vt_trace_sys_all", appConfig.vt_trace_sys_all, tsysall); + auto qw = app.add_flag("--vt_trace_sys_term", appConfig.vt_trace_sys_term, tsysTD); + auto qx = app.add_flag("--vt_trace_sys_location", appConfig.vt_trace_sys_location, tsysloc); + auto qy = app.add_flag("--vt_trace_sys_collection", appConfig.vt_trace_sys_collection, tsyscoll); + auto qz = app.add_flag("--vt_trace_sys_serial_msg", appConfig.vt_trace_sys_serial_msg, tsyssmsg); + auto qza = app.add_flag("--vt_trace_spec", appConfig.vt_trace_spec, tspec); + auto qzb = app.add_option("--vt_trace_spec_file", appConfig.vt_trace_spec_file, tspecfile, ""); + auto qzc = app.add_flag("--vt_trace_memory_usage", appConfig.vt_trace_memory_usage, tmemusage); + auto qzd = app.add_flag("--vt_trace_event_polling", appConfig.vt_trace_event_polling, tpolled); + auto qze = app.add_flag("--vt_trace_irecv_polling", appConfig.vt_trace_irecv_polling, tirecv); auto traceGroup = "Tracing Configuration"; n->group(traceGroup); nm->group(traceGroup); @@ -217,8 +336,8 @@ void ArgConfig::addTraceArgs(CLI::App& app) { qze->group(traceGroup); } -void ArgConfig::addDebugPrintArgs(CLI::App& app) { - #define debug_pp(opt) +std::string(vt::config::PrettyPrintCat::str)+ +void addDebugPrintArgs(CLI::App& app, AppConfig& appConfig) { + #define debug_pp(opt) +std::string(config::PrettyPrintCat::str)+ auto rp = "Enable all debug prints"; auto rq = "Set level for debug prints (0=>terse, 1=>normal, 2=>verbose)"; @@ -257,43 +376,43 @@ void ArgConfig::addDebugPrintArgs(CLI::App& app) { auto ddp = "Enable debug_context = \"" debug_pp(context) "\""; auto dep = "Enable debug_epoch = \"" debug_pp(epoch) "\""; - auto r1 = app.add_option("--vt_debug_level", config_.vt_debug_level, rq); - - auto r = app.add_flag("--vt_debug_all", config_.vt_debug_all, rp); - auto aa = app.add_flag("--vt_debug_none", config_.vt_debug_none, aap); - auto ba = app.add_flag("--vt_debug_gen", config_.vt_debug_gen, bap); - auto ca = app.add_flag("--vt_debug_runtime", config_.vt_debug_runtime, cap); - auto da = app.add_flag("--vt_debug_active", config_.vt_debug_active, dap); - auto ea = app.add_flag("--vt_debug_term", config_.vt_debug_term, eap); - auto fa = app.add_flag("--vt_debug_termds", config_.vt_debug_termds, fap); - auto ga = app.add_flag("--vt_debug_barrier", config_.vt_debug_barrier, gap); - auto ha = app.add_flag("--vt_debug_event", config_.vt_debug_event, hap); - auto ia = app.add_flag("--vt_debug_pipe", config_.vt_debug_pipe, iap); - auto ja = app.add_flag("--vt_debug_pool", config_.vt_debug_pool, jap); - auto ka = app.add_flag("--vt_debug_reduce", config_.vt_debug_reduce, kap); - auto la = app.add_flag("--vt_debug_rdma", config_.vt_debug_rdma, lap); - auto ma = app.add_flag("--vt_debug_rdma_channel", config_.vt_debug_rdma_channel, map); - auto na = app.add_flag("--vt_debug_rdma_state", config_.vt_debug_rdma_state, nap); - auto oa = app.add_flag("--vt_debug_param", config_.vt_debug_param, oap); - auto pa = app.add_flag("--vt_debug_handler", config_.vt_debug_handler, pap); - auto qa = app.add_flag("--vt_debug_hierlb", config_.vt_debug_hierlb, qap); - auto qb = app.add_flag("--vt_debug_temperedlb", config_.vt_debug_temperedlb, qbp); - auto ra = app.add_flag("--vt_debug_scatter", config_.vt_debug_scatter, rap); - auto sa = app.add_flag("--vt_debug_sequence", config_.vt_debug_sequence, sap); - auto ta = app.add_flag("--vt_debug_sequence_vrt", config_.vt_debug_sequence_vrt, tap); - auto ua = app.add_flag("--vt_debug_serial_msg", config_.vt_debug_serial_msg, uap); - auto va = app.add_flag("--vt_debug_trace", config_.vt_debug_trace, vap); - auto wa = app.add_flag("--vt_debug_location", config_.vt_debug_location, wap); - auto xa = app.add_flag("--vt_debug_lb", config_.vt_debug_lb, xap); - auto ya = app.add_flag("--vt_debug_vrt", config_.vt_debug_vrt, yap); - auto za = app.add_flag("--vt_debug_vrt_coll", config_.vt_debug_vrt_coll, zap); - auto ab = app.add_flag("--vt_debug_worker", config_.vt_debug_worker, abp); - auto bb = app.add_flag("--vt_debug_group", config_.vt_debug_group, bbp); - auto cb = app.add_flag("--vt_debug_broadcast", config_.vt_debug_broadcast, cbp); - auto db = app.add_flag("--vt_debug_objgroup", config_.vt_debug_objgroup, dbp); - auto dc = app.add_flag("--vt_debug_phase", config_.vt_debug_phase, dcp); - auto dd = app.add_flag("--vt_debug_context", config_.vt_debug_context, ddp); - auto de = app.add_flag("--vt_debug_epoch", config_.vt_debug_epoch, dep); + auto r1 = app.add_option("--vt_debug_level", appConfig.vt_debug_level, rq); + + auto r = app.add_flag("--vt_debug_all", appConfig.vt_debug_all, rp); + auto aa = app.add_flag("--vt_debug_none", appConfig.vt_debug_none, aap); + auto ba = app.add_flag("--vt_debug_gen", appConfig.vt_debug_gen, bap); + auto ca = app.add_flag("--vt_debug_runtime", appConfig.vt_debug_runtime, cap); + auto da = app.add_flag("--vt_debug_active", appConfig.vt_debug_active, dap); + auto ea = app.add_flag("--vt_debug_term", appConfig.vt_debug_term, eap); + auto fa = app.add_flag("--vt_debug_termds", appConfig.vt_debug_termds, fap); + auto ga = app.add_flag("--vt_debug_barrier", appConfig.vt_debug_barrier, gap); + auto ha = app.add_flag("--vt_debug_event", appConfig.vt_debug_event, hap); + auto ia = app.add_flag("--vt_debug_pipe", appConfig.vt_debug_pipe, iap); + auto ja = app.add_flag("--vt_debug_pool", appConfig.vt_debug_pool, jap); + auto ka = app.add_flag("--vt_debug_reduce", appConfig.vt_debug_reduce, kap); + auto la = app.add_flag("--vt_debug_rdma", appConfig.vt_debug_rdma, lap); + auto ma = app.add_flag("--vt_debug_rdma_channel", appConfig.vt_debug_rdma_channel, map); + auto na = app.add_flag("--vt_debug_rdma_state", appConfig.vt_debug_rdma_state, nap); + auto oa = app.add_flag("--vt_debug_param", appConfig.vt_debug_param, oap); + auto pa = app.add_flag("--vt_debug_handler", appConfig.vt_debug_handler, pap); + auto qa = app.add_flag("--vt_debug_hierlb", appConfig.vt_debug_hierlb, qap); + auto qb = app.add_flag("--vt_debug_temperedlb", appConfig.vt_debug_temperedlb, qbp); + auto ra = app.add_flag("--vt_debug_scatter", appConfig.vt_debug_scatter, rap); + auto sa = app.add_flag("--vt_debug_sequence", appConfig.vt_debug_sequence, sap); + auto ta = app.add_flag("--vt_debug_sequence_vrt", appConfig.vt_debug_sequence_vrt, tap); + auto ua = app.add_flag("--vt_debug_serial_msg", appConfig.vt_debug_serial_msg, uap); + auto va = app.add_flag("--vt_debug_trace", appConfig.vt_debug_trace, vap); + auto wa = app.add_flag("--vt_debug_location", appConfig.vt_debug_location, wap); + auto xa = app.add_flag("--vt_debug_lb", appConfig.vt_debug_lb, xap); + auto ya = app.add_flag("--vt_debug_vrt", appConfig.vt_debug_vrt, yap); + auto za = app.add_flag("--vt_debug_vrt_coll", appConfig.vt_debug_vrt_coll, zap); + auto ab = app.add_flag("--vt_debug_worker", appConfig.vt_debug_worker, abp); + auto bb = app.add_flag("--vt_debug_group", appConfig.vt_debug_group, bbp); + auto cb = app.add_flag("--vt_debug_broadcast", appConfig.vt_debug_broadcast, cbp); + auto db = app.add_flag("--vt_debug_objgroup", appConfig.vt_debug_objgroup, dbp); + auto dc = app.add_flag("--vt_debug_phase", appConfig.vt_debug_phase, dcp); + auto dd = app.add_flag("--vt_debug_context", appConfig.vt_debug_context, ddp); + auto de = app.add_flag("--vt_debug_epoch", appConfig.vt_debug_epoch, dep); auto debugGroup = "Debug Print Configuration (must be compile-time enabled)"; r->group(debugGroup); @@ -334,11 +453,11 @@ void ArgConfig::addDebugPrintArgs(CLI::App& app) { de->group(debugGroup); auto dbq = "Always flush VT runtime prints"; - auto eb = app.add_flag("--vt_debug_print_flush", config_.vt_debug_print_flush, dbq); + auto eb = app.add_flag("--vt_debug_print_flush", appConfig.vt_debug_print_flush, dbq); eb->group(debugGroup); } -void ArgConfig::addLbArgs(CLI::App& app) { +void addLbArgs(CLI::App& app, AppConfig& appConfig) { /* * Flags for enabling load balancing and configuring it */ @@ -362,21 +481,21 @@ void ArgConfig::addLbArgs(CLI::App& app) { auto lbd = "vt_lb_stats"; auto lbs = "stats"; auto lba = ""; - auto s = app.add_flag("--vt_lb", config_.vt_lb, lb); - auto t1 = app.add_flag("--vt_lb_quiet", config_.vt_lb_quiet, lb_quiet); - auto u = app.add_option("--vt_lb_file_name", config_.vt_lb_file_name, lb_file_name, lbf) + auto s = app.add_flag("--vt_lb", appConfig.vt_lb, lb); + auto t1 = app.add_flag("--vt_lb_quiet", appConfig.vt_lb_quiet, lb_quiet); + auto u = app.add_option("--vt_lb_file_name", appConfig.vt_lb_file_name, lb_file_name, lbf) ->check(CLI::ExistingFile); - auto u1 = app.add_flag("--vt_lb_show_spec", config_.vt_lb_show_spec, lb_show_spec); - auto v = app.add_option("--vt_lb_name", config_.vt_lb_name, lb_name, lbn); - auto v1 = app.add_option("--vt_lb_args", config_.vt_lb_args, lb_args, lba); - auto w = app.add_option("--vt_lb_interval", config_.vt_lb_interval, lb_interval, lbi); - auto wl = app.add_flag("--vt_lb_keep_last_elm", config_.vt_lb_keep_last_elm, lb_keep_last_elm); - auto ww = app.add_flag("--vt_lb_stats", config_.vt_lb_stats, lb_stats); - auto xz = app.add_flag("--vt_lb_stats_compress", config_.vt_lb_stats_compress, lb_stats_comp); - auto wx = app.add_option("--vt_lb_stats_dir", config_.vt_lb_stats_dir, lb_stats_dir, lbd); - auto wy = app.add_option("--vt_lb_stats_file", config_.vt_lb_stats_file, lb_stats_file,lbs); - auto xx = app.add_option("--vt_lb_stats_dir_in", config_.vt_lb_stats_dir_in, lb_stats_dir_in, lbd); - auto xy = app.add_option("--vt_lb_stats_file_in", config_.vt_lb_stats_file_in, lb_stats_file_in,lbs); + auto u1 = app.add_flag("--vt_lb_show_spec", appConfig.vt_lb_show_spec, lb_show_spec); + auto v = app.add_option("--vt_lb_name", appConfig.vt_lb_name, lb_name, lbn); + auto v1 = app.add_option("--vt_lb_args", appConfig.vt_lb_args, lb_args, lba); + auto w = app.add_option("--vt_lb_interval", appConfig.vt_lb_interval, lb_interval, lbi); + auto wl = app.add_flag("--vt_lb_keep_last_elm", appConfig.vt_lb_keep_last_elm, lb_keep_last_elm); + auto ww = app.add_flag("--vt_lb_stats", appConfig.vt_lb_stats, lb_stats); + auto xz = app.add_flag("--vt_lb_stats_compress", appConfig.vt_lb_stats_compress, lb_stats_comp); + auto wx = app.add_option("--vt_lb_stats_dir", appConfig.vt_lb_stats_dir, lb_stats_dir, lbd); + auto wy = app.add_option("--vt_lb_stats_file", appConfig.vt_lb_stats_file, lb_stats_file,lbs); + auto xx = app.add_option("--vt_lb_stats_dir_in", appConfig.vt_lb_stats_dir_in, lb_stats_dir_in, lbd); + auto xy = app.add_option("--vt_lb_stats_file_in", appConfig.vt_lb_stats_file_in, lb_stats_file_in,lbs); auto debugLB = "Load Balancing"; s->group(debugLB); @@ -397,11 +516,11 @@ void ArgConfig::addLbArgs(CLI::App& app) { // help options deliberately omitted from the debugLB group above so that // they appear grouped with --vt_help when --vt_help is used auto help_lb_args = "Print help for --vt_lb_args"; - auto h1 = app.add_flag("--vt_help_lb_args", config_.vt_help_lb_args, help_lb_args); + auto h1 = app.add_flag("--vt_help_lb_args", appConfig.vt_help_lb_args, help_lb_args); (void) h1; } -void ArgConfig::addDiagnosticArgs(CLI::App& app) { +void addDiagnosticArgs(CLI::App& app, AppConfig& appConfig) { /* * Flags for controlling diagnostic collection and output */ @@ -410,11 +529,11 @@ void ArgConfig::addDiagnosticArgs(CLI::App& app) { auto file = "Output diagnostic summary table to text file"; auto csv = "Output diagnostic summary table to a comma-separated file"; auto base = "Use base units (seconds, units, etc.) for CSV file output"; - auto a = app.add_flag("--vt_diag_enable,!--vt_diag_disable", config_.vt_diag_enable, diag); - auto b = app.add_flag("--vt_diag_print_summary", config_.vt_diag_print_summary, sum); - auto c = app.add_option("--vt_diag_summary_file", config_.vt_diag_summary_file, file); - auto d = app.add_option("--vt_diag_summary_csv_file", config_.vt_diag_summary_csv_file, csv); - auto e = app.add_flag("--vt_diag_csv_base_units", config_.vt_diag_csv_base_units, base); + auto a = app.add_flag("--vt_diag_enable,!--vt_diag_disable", appConfig.vt_diag_enable, diag); + auto b = app.add_flag("--vt_diag_print_summary", appConfig.vt_diag_print_summary, sum); + auto c = app.add_option("--vt_diag_summary_file", appConfig.vt_diag_summary_file, file); + auto d = app.add_option("--vt_diag_summary_csv_file", appConfig.vt_diag_summary_csv_file, csv); + auto e = app.add_flag("--vt_diag_csv_base_units", appConfig.vt_diag_csv_base_units, base); auto diagnosticGroup = "Diagnostics"; a->group(diagnosticGroup); @@ -424,7 +543,7 @@ void ArgConfig::addDiagnosticArgs(CLI::App& app) { e->group(diagnosticGroup); } -void ArgConfig::addTerminationArgs(CLI::App& app) { +void addTerminationArgs(CLI::App& app, AppConfig& appConfig) { auto hang = "Disable termination hang detection"; auto hang_freq = "The number of tree traversals before a hang is detected"; auto ds = "Force use of Dijkstra-Scholten (DS) algorithm for rooted epoch termination detection"; @@ -433,13 +552,13 @@ void ArgConfig::addTerminationArgs(CLI::App& app) { auto terse = "Output epoch graph to file in terse mode"; auto progress = "Print termination counts when progress is stalled"; auto hfd = 1024; - auto x = app.add_flag("--vt_no_detect_hang", config_.vt_no_detect_hang, hang); - auto x1 = app.add_flag("--vt_term_rooted_use_ds", config_.vt_term_rooted_use_ds, ds); - auto x2 = app.add_flag("--vt_term_rooted_use_wave", config_.vt_term_rooted_use_wave, wave); - auto x3 = app.add_option("--vt_epoch_graph_on_hang", config_.vt_epoch_graph_on_hang, graph_on, true); - auto x4 = app.add_flag("--vt_epoch_graph_terse", config_.vt_epoch_graph_terse, terse); - auto x5 = app.add_option("--vt_print_no_progress", config_.vt_print_no_progress, progress, true); - auto y = app.add_option("--vt_hang_freq", config_.vt_hang_freq, hang_freq, hfd); + auto x = app.add_flag("--vt_no_detect_hang", appConfig.vt_no_detect_hang, hang); + auto x1 = app.add_flag("--vt_term_rooted_use_ds", appConfig.vt_term_rooted_use_ds, ds); + auto x2 = app.add_flag("--vt_term_rooted_use_wave", appConfig.vt_term_rooted_use_wave, wave); + auto x3 = app.add_option("--vt_epoch_graph_on_hang", appConfig.vt_epoch_graph_on_hang, graph_on, true); + auto x4 = app.add_flag("--vt_epoch_graph_terse", appConfig.vt_epoch_graph_terse, terse); + auto x5 = app.add_option("--vt_print_no_progress", appConfig.vt_print_no_progress, progress, true); + auto y = app.add_option("--vt_hang_freq", appConfig.vt_hang_freq, hang_freq, hfd); auto debugTerm = "Termination"; x->group(debugTerm); x1->group(debugTerm); @@ -450,14 +569,14 @@ void ArgConfig::addTerminationArgs(CLI::App& app) { y->group(debugTerm); } -void ArgConfig::addDebuggerArgs(CLI::App& app) { +void addDebuggerArgs(CLI::App& app, AppConfig& appConfig) { auto pause = "Pause at startup so GDB/LLDB can be attached"; - auto z = app.add_flag("--vt_pause", config_.vt_pause, pause); + auto z = app.add_flag("--vt_pause", appConfig.vt_pause, pause); auto launchTerm = "Debugging/Launch"; z->group(launchTerm); } -void ArgConfig::addUserArgs(CLI::App& app) { +void addUserArgs(CLI::App& app, AppConfig& appConfig) { auto user1 = "User Option 1a (boolean)"; auto user2 = "User Option 2a (boolean)"; auto user3 = "User Option 3a (boolean)"; @@ -467,15 +586,15 @@ void ArgConfig::addUserArgs(CLI::App& app) { auto userstr1 = "User Option 1c (std::string)"; auto userstr2 = "User Option 2c (std::string)"; auto userstr3 = "User Option 3c (std::string)"; - auto u1 = app.add_flag("--vt_user_1", config_.vt_user_1, user1); - auto u2 = app.add_flag("--vt_user_2", config_.vt_user_2, user2); - auto u3 = app.add_flag("--vt_user_3", config_.vt_user_3, user3); - auto ui1 = app.add_option("--vt_user_int_1", config_.vt_user_int_1, userint1, 0); - auto ui2 = app.add_option("--vt_user_int_2", config_.vt_user_int_2, userint2, 0); - auto ui3 = app.add_option("--vt_user_int_3", config_.vt_user_int_3, userint3, 0); - auto us1 = app.add_option("--vt_user_str_1", config_.vt_user_str_1, userstr1, ""); - auto us2 = app.add_option("--vt_user_str_2", config_.vt_user_str_2, userstr2, ""); - auto us3 = app.add_option("--vt_user_str_3", config_.vt_user_str_3, userstr3, ""); + auto u1 = app.add_flag("--vt_user_1", appConfig.vt_user_1, user1); + auto u2 = app.add_flag("--vt_user_2", appConfig.vt_user_2, user2); + auto u3 = app.add_flag("--vt_user_3", appConfig.vt_user_3, user3); + auto ui1 = app.add_option("--vt_user_int_1", appConfig.vt_user_int_1, userint1, 0); + auto ui2 = app.add_option("--vt_user_int_2", appConfig.vt_user_int_2, userint2, 0); + auto ui3 = app.add_option("--vt_user_int_3", appConfig.vt_user_int_3, userint3, 0); + auto us1 = app.add_option("--vt_user_str_1", appConfig.vt_user_str_1, userstr1, ""); + auto us2 = app.add_option("--vt_user_str_2", appConfig.vt_user_str_2, userstr2, ""); + auto us3 = app.add_option("--vt_user_str_3", appConfig.vt_user_str_3, userstr3, ""); auto userOpts = "User Options"; u1->group(userOpts); u2->group(userOpts); @@ -488,32 +607,32 @@ void ArgConfig::addUserArgs(CLI::App& app) { us3->group(userOpts); } -void ArgConfig::addSchedulerArgs(CLI::App& app) { +void addSchedulerArgs(CLI::App& app, AppConfig& appConfig) { auto nsched = "Number of times to run the progress function in scheduler"; auto ksched = "Run the MPI progress function at least every k handlers that run"; auto ssched = "Run the MPI progress function at least every s seconds"; - auto sca = app.add_option("--vt_sched_num_progress", config_.vt_sched_num_progress, nsched, 2); - auto hca = app.add_option("--vt_sched_progress_han", config_.vt_sched_progress_han, ksched, 0); - auto kca = app.add_option("--vt_sched_progress_sec", config_.vt_sched_progress_sec, ssched, 0.0); + auto sca = app.add_option("--vt_sched_num_progress", appConfig.vt_sched_num_progress, nsched, 2); + auto hca = app.add_option("--vt_sched_progress_han", appConfig.vt_sched_progress_han, ksched, 0); + auto kca = app.add_option("--vt_sched_progress_sec", appConfig.vt_sched_progress_sec, ssched, 0.0); auto schedulerGroup = "Scheduler Configuration"; sca->group(schedulerGroup); hca->group(schedulerGroup); kca->group(schedulerGroup); } -void ArgConfig::addConfigFileArgs(CLI::App& app) { +void addConfigFileArgs(CLI::App& app, AppConfig& appConfig) { auto doconfig = "Output all VT args to configuration file"; auto configname = "Name of configuration file to output"; - auto a1 = app.add_flag("--vt_output_config", config_.vt_output_config, doconfig); - auto a2 = app.add_option("--vt_output_config_file", config_.vt_output_config_file, configname, true); + auto a1 = app.add_flag("--vt_output_config", appConfig.vt_output_config, doconfig); + auto a2 = app.add_option("--vt_output_config_file", appConfig.vt_output_config_file, configname, true); auto configGroup = "Configuration File"; a1->group(configGroup); a2->group(configGroup); } -void ArgConfig::addRuntimeArgs(CLI::App& app) { +void addRuntimeArgs(CLI::App& app, AppConfig& appConfig) { auto max_size = "Maximum MPI send size (causes larger messages to be split " "into multiple MPI sends)"; auto assert = "Do not abort the program when vtAssert(..) is invoked"; @@ -521,13 +640,13 @@ void ArgConfig::addRuntimeArgs(CLI::App& app) { auto a1 = app.add_option( - "--vt_max_mpi_send_size", config_.vt_max_mpi_send_size, max_size, true + "--vt_max_mpi_send_size", appConfig.vt_max_mpi_send_size, max_size, true ); auto a2 = app.add_flag( - "--vt_no_assert_fail", config_.vt_no_assert_fail, assert + "--vt_no_assert_fail", appConfig.vt_no_assert_fail, assert ); auto a3 = app.add_flag( - "--vt_throw_on_abort", config_.vt_throw_on_abort, throw_on_abort + "--vt_throw_on_abort", appConfig.vt_throw_on_abort, throw_on_abort ); @@ -537,16 +656,16 @@ void ArgConfig::addRuntimeArgs(CLI::App& app) { a3->group(configRuntime); } -void ArgConfig::addThreadingArgs(CLI::App& app) { +void addThreadingArgs(CLI::App& app, AppConfig& appConfig) { #if (vt_feature_fcontext != 0) auto ult_disable = "Disable running handlers in user-level threads"; auto stack_size = "The default stack size for user-level threads"; auto a1 = app.add_flag( - "--vt_ult_disable", config_.vt_ult_disable, ult_disable + "--vt_ult_disable", appConfig.vt_ult_disable, ult_disable ); auto a2 = app.add_option( - "--vt_ult_stack_size", config_.vt_ult_stack_size, stack_size, true + "--vt_ult_stack_size", appConfig.vt_ult_stack_size, stack_size, true ); auto configThreads = "Threads"; @@ -555,6 +674,13 @@ void ArgConfig::addThreadingArgs(CLI::App& app) { #endif } +} /* end anon namespace */ + +/*static*/ std::unique_ptr +ArgConfig::construct(std::unique_ptr arg) { + return arg; +} + class VtFormatter : public CLI::Formatter { public: std::string make_usage(const CLI::App *, std::string name) const override { @@ -582,7 +708,26 @@ class VtFormatter : public CLI::Formatter { } }; -std::tuple ArgConfig::parse(int& argc, char**& argv) { +std::tuple ArgConfig::parse( + int& argc, char**& argv, AppConfig const* appConfig +) { + // If user didn't define appConfig, parse into this->config_. + if (not appConfig) { + return parseToConfig(argc, argv, config_); + } + + // If user defines appConfig, parse into temporary config for later comparison. + AppConfig config{*appConfig}; + auto const parse_result = parseToConfig(argc, argv, config); + + config_ = config; + + return parse_result; +} + +std::tuple ArgConfig::parseToConfig( + int& argc, char**& argv, AppConfig& appConfig +) { if (parsed_ || argc == 0 || argv == nullptr) { // Odd case.. pretend nothing bad happened. return std::make_tuple(-1, std::string{}); @@ -594,35 +739,35 @@ std::tuple ArgConfig::parse(int& argc, char**& argv) { app.set_help_flag("--vt_help", "Display help"); - addColorArgs(app); - addSignalArgs(app); - addMemUsageArgs(app); - addStackDumpArgs(app); - addTraceArgs(app); - addDebugPrintArgs(app); - addLbArgs(app); - addDiagnosticArgs(app); - addTerminationArgs(app); - addDebuggerArgs(app); - addUserArgs(app); - addSchedulerArgs(app); - addConfigFileArgs(app); - addRuntimeArgs(app); - addThreadingArgs(app); - - std::tuple result = parseArguments(app, /*out*/ argc, /*out*/ argv); + addColorArgs(app, appConfig); + addSignalArgs(app, appConfig); + addMemUsageArgs(app, appConfig); + addStackDumpArgs(app, appConfig); + addTraceArgs(app, appConfig); + addDebugPrintArgs(app, appConfig); + addLbArgs(app, appConfig); + addDiagnosticArgs(app, appConfig); + addTerminationArgs(app, appConfig); + addDebuggerArgs(app, appConfig); + addUserArgs(app, appConfig); + addSchedulerArgs(app, appConfig); + addConfigFileArgs(app, appConfig); + addRuntimeArgs(app, appConfig); + addThreadingArgs(app, appConfig); + + std::tuple result = parseArguments(app, argc, argv, appConfig); if (std::get<0>(result) not_eq -1) { // non-success return result; } // Determine the final colorization setting. - if (config_.vt_no_color) { - config_.colorize_output = false; + if (appConfig.vt_no_color) { + appConfig.colorize_output = false; } else { // Otherwise, colorize. // (Within MPI there is no good method to auto-detect.) - config_.colorize_output = true; + appConfig.colorize_output = true; } parsed_ = true; @@ -630,129 +775,6 @@ std::tuple ArgConfig::parse(int& argc, char**& argv) { return result; } -/** - * \internal - * Application specific cleanup and mapping to actual app args. - */ -void ArgConfig::postParseTransform() { - auto contains = [](std::vector &v, std::string str){ - return std::find(v.begin(), v.end(), str) not_eq v.end(); - }; - - config_.vt_trace_mpi = contains(arg_trace_mpi, "internal"); - config_.vt_trace_pmpi = contains(arg_trace_mpi, "external"); - - using config::ModeEnum; - - auto const& level = config_.vt_debug_level; - if (level == "terse" or level == "0") { - config_.vt_debug_level_val = ModeEnum::terse; - } else if (level == "normal" or level == "1") { - config_.vt_debug_level_val = ModeEnum::terse | ModeEnum::normal; - } else if (level == "verbose" or level == "2") { - config_.vt_debug_level_val = - ModeEnum::terse | ModeEnum::normal | ModeEnum::verbose; - } else { - vtAbort("Invalid value passed to --vt_debug_level"); - } -} - -std::tuple ArgConfig::parseArguments(CLI::App& app, int& argc, char**& argv) { - - std::vector vt_args; - - // Load up vectors (has curious ability to altnerate vt/mpi/passthru) - std::vector* rargs = nullptr; - for (int i = 1; i < argc; i++) { - char* c = argv[i]; - if (0 == strcmp(c, "--vt_args")) { - rargs = &vt_args; - } else if (0 == strcmp(c, "--")) { - rargs = &config_.passthru_args; - } else if (rargs) { - rargs->push_back(c); - } else if (0 == strncmp(c, "--vt_", 5)) { - // Implicit start of VT args allows pass-thru 'for compatibility' - // although the recommended calling pattern to always provide VT args first. - rargs = &vt_args; - rargs->push_back(c); - } else { - config_.passthru_args.push_back(c); - } - } - - // All must be accounted for - app.allow_extras(false); - - // Build string-vector and reverse order to parse (CLI quirk) - std::vector args_to_parse; - for (auto it = vt_args.crbegin(); it != vt_args.crend(); ++it) { - args_to_parse.push_back(*it); - } - - // Allow a input config file - app.set_config( - "--vt_input_config", - "", // no default file name - "Read in an ini config file for VT", - false // not required - ); - - try { - app.parse(args_to_parse); - } catch (CLI::Error &ex) { - // Return exit code and message, delaying logic processing of such. - // The default exit code for 'help' is 0. - std::stringstream message_stream; - int result = app.exit(ex, message_stream, message_stream); - - return std::make_tuple(result, message_stream.str()); - } - - // If the user specified to output the full configuration, save it in a string - // so node 0 can output in the runtime once MPI is init'ed - if (config_.vt_output_config) { - config_.vt_output_config_str = app.config_to_str(true, true); - } - - // Get the clean prog name; don't allow path bleed in usages. - // std::filesystem is C++17. - std::string clean_prog_name = argv[0]; - size_t l = clean_prog_name.find_last_of("/\\"); - if (l not_eq std::string::npos and l + 1 < clean_prog_name.size()) { - clean_prog_name = clean_prog_name.substr(l + 1, std::string::npos); - } - - config_.prog_name = clean_prog_name; - config_.argv_prog_name = argv[0]; - - postParseTransform(); - - // Rebuild passthru into ref-returned argc/argv - - // It should be possible to modify the original argv as the outgoing - // number of arguments is always less. As currently allocated here, - // ownership of the new object is ill-defined. - int new_argc = config_.passthru_args.size() + 1; // does not include argv[0] - - static std::unique_ptr new_argv = nullptr; - - new_argv = std::make_unique(new_argc + 1); - - int i = 0; - new_argv[i++] = config_.argv_prog_name; - for (auto&& arg : config_.passthru_args) { - new_argv[i++] = arg; - } - new_argv[i++] = nullptr; - - // Set them back with all vt (and MPI) arguments elided - argc = new_argc; - argv = new_argv.get(); - - return std::make_tuple(-1, std::string{}); -} - namespace { static std::string buildFile(std::string const& file, std::string const& dir) { std::string name = file; diff --git a/src/vt/configs/arguments/args.h b/src/vt/configs/arguments/args.h index 0efe341fa7..c2da8ae1da 100644 --- a/src/vt/configs/arguments/args.h +++ b/src/vt/configs/arguments/args.h @@ -73,8 +73,9 @@ struct ArgConfig : runtime::component::Component { /// On success the tuple will be {-1, ""}. Otherwise the exit code /// (which may be 0 if help was requested) will be returned along /// with an appropriate display message. - std::tuple parse(int& argc, char**& argv); - std::tuple parseArguments(CLI::App& app, int& argc, char**& argv); + std::tuple parse( + int& argc, char**& argv, AppConfig const* appConfig + ); static std::unique_ptr construct(std::unique_ptr arg); @@ -89,23 +90,9 @@ struct ArgConfig : runtime::component::Component { AppConfig config_; private: - void addColorArgs(CLI::App& app); - void addSignalArgs(CLI::App& app); - void addMemUsageArgs(CLI::App& app); - void addStackDumpArgs(CLI::App& app); - void addTraceArgs(CLI::App& app); - void addDebugPrintArgs(CLI::App& app); - void addLbArgs(CLI::App& app); - void addDiagnosticArgs(CLI::App& app); - void addTerminationArgs(CLI::App& app); - void addDebuggerArgs(CLI::App& app); - void addUserArgs(CLI::App& app); - void addSchedulerArgs(CLI::App& app); - void addConfigFileArgs(CLI::App& app); - void addRuntimeArgs(CLI::App& app); - void addThreadingArgs(CLI::App& app); - - void postParseTransform(); + std::tuple parseToConfig( + int& argc, char**& argv, AppConfig& appConfig + ); bool parsed_ = false; }; diff --git a/src/vt/runtime/runtime.cc b/src/vt/runtime/runtime.cc index fffb8aca34..b452bab950 100644 --- a/src/vt/runtime/runtime.cc +++ b/src/vt/runtime/runtime.cc @@ -99,7 +99,8 @@ namespace vt { namespace runtime { Runtime::Runtime( int& argc, char**& argv, WorkerCountType in_num_workers, - bool const interop_mode, MPI_Comm in_comm, RuntimeInstType const in_instance + bool const interop_mode, MPI_Comm in_comm, RuntimeInstType const in_instance, + arguments::AppConfig const* appConfig ) : instance_(in_instance), runtime_active_(false), is_interop_(interop_mode), num_workers_(in_num_workers), initial_communicator_(in_comm), @@ -152,7 +153,7 @@ Runtime::Runtime( // n.b. ref-update of args with pass-through arguments std::tuple result = - arg_config_->parse(/*out*/ argc, /*out*/ argv); + arg_config_->parse(argc, argv, appConfig); int exit_code = std::get<0>(result); if (getAppConfig()->vt_help_lb_args) { diff --git a/src/vt/runtime/runtime.h b/src/vt/runtime/runtime.h index 9820150378..0c76211beb 100644 --- a/src/vt/runtime/runtime.h +++ b/src/vt/runtime/runtime.h @@ -101,7 +101,8 @@ struct Runtime { WorkerCountType in_num_workers = no_workers, bool const interop_mode = false, MPI_Comm in_comm = MPI_COMM_WORLD, - RuntimeInstType const in_instance = RuntimeInstType::DefaultInstance + RuntimeInstType const in_instance = RuntimeInstType::DefaultInstance, + arguments::AppConfig const* appConfig = nullptr ); Runtime(Runtime const&) = delete; diff --git a/tests/unit/runtime/test_initialization.cc b/tests/unit/runtime/test_initialization.cc index d47fb7afb5..efa91da93e 100644 --- a/tests/unit/runtime/test_initialization.cc +++ b/tests/unit/runtime/test_initialization.cc @@ -47,40 +47,204 @@ #include +#include + namespace vt { namespace tests { namespace unit { -struct TestInitialization : TestParallelHarness { - void SetUp() override { - using namespace vt; +struct TestInitialization : TestParallelHarness { }; + +TEST_F(TestInitialization, test_initialize_with_args) { + MPI_Comm comm = MPISingletonMultiTest::Get()->getComm(); + + static char prog_name[]{"vt_program"}; + static char cli_argument[]{"--cli_argument=100"}; + static char vt_no_terminate[]{"--vt_no_terminate"}; + + std::vector custom_args; + custom_args.emplace_back(prog_name); + custom_args.emplace_back(cli_argument); + custom_args.emplace_back(vt_no_terminate); + custom_args.emplace_back(nullptr); + + int custom_argc = custom_args.size() - 1; + char** custom_argv = custom_args.data(); + + EXPECT_EQ(custom_argc, 3); - TestHarness::SetUp(); + vt::initialize(custom_argc, custom_argv, no_workers, true, &comm); - // communicator is duplicated. - MPI_Comm comm = MPISingletonMultiTest::Get()->getComm(); + EXPECT_EQ(theConfig()->prog_name, "vt_program"); + EXPECT_EQ(theConfig()->vt_no_terminate, true); - static char prog_name[]{"vt_program"}; - static char cli_argument[]{"--cli_argument=100"}; - static char vt_no_terminate[]{"--vt_no_terminate"}; - custom_args.emplace_back(prog_name); - custom_args.emplace_back(cli_argument); - custom_args.emplace_back(vt_no_terminate); - custom_args.emplace_back(nullptr); + EXPECT_EQ(custom_argc, 2); + EXPECT_STREQ(custom_argv[0], "vt_program"); + EXPECT_STREQ(custom_argv[1], "--cli_argument=100"); + EXPECT_EQ(custom_argv[2], nullptr); +} + +TEST_F(TestInitialization, test_initialize_with_appconfig) { + MPI_Comm comm = MPISingletonMultiTest::Get()->getComm(); + + static char prog_name[]{"vt_program"}; + static char cli_argument[]{"--cli_argument=100"}; + + std::vector custom_args; + custom_args.emplace_back(prog_name); + custom_args.emplace_back(cli_argument); + custom_args.emplace_back(nullptr); + + int custom_argc = custom_args.size() - 1; + char** custom_argv = custom_args.data(); + + EXPECT_EQ(custom_argc, 2); + + arguments::AppConfig appConfig{}; + appConfig.vt_epoch_graph_on_hang = false; + appConfig.vt_lb_name = "RotateLB"; + appConfig.vt_lb_stats = true; + + vt::initialize(custom_argc, custom_argv, no_workers, true, &comm, &appConfig); + + EXPECT_EQ(theConfig()->prog_name, "vt_program"); + EXPECT_EQ(theConfig()->vt_epoch_graph_on_hang, false); + EXPECT_EQ(theConfig()->vt_lb_name, "RotateLB"); + EXPECT_EQ(theConfig()->vt_lb_stats, true); + + // vt_no_detect_hang wasn't set, should be default + EXPECT_EQ(theConfig()->vt_no_detect_hang, false); + + EXPECT_EQ(custom_argc, 2); + EXPECT_STREQ(custom_argv[0], "vt_program"); + EXPECT_STREQ(custom_argv[1], "--cli_argument=100"); + EXPECT_EQ(custom_argv[2], nullptr); +} + +TEST_F(TestInitialization, test_initialize_with_args_and_appconfig) { + MPI_Comm comm = MPISingletonMultiTest::Get()->getComm(); + + static char prog_name[]{"vt_program"}; + static char cli_argument[]{"--cli_argument=100"}; + static char vt_no_terminate[]{"--vt_no_terminate"}; + static char vt_no_detect_hang[]{"--vt_no_detect_hang"}; + + std::vector custom_args; + custom_args.emplace_back(prog_name); + custom_args.emplace_back(cli_argument); + custom_args.emplace_back(vt_no_terminate); + custom_args.emplace_back(vt_no_detect_hang); + custom_args.emplace_back(nullptr); - custom_argc = custom_args.size() - 1; - custom_argv = custom_args.data(); - EXPECT_EQ(custom_argc, 3); + int custom_argc = custom_args.size() - 1; + char** custom_argv = custom_args.data(); - vt::initialize(custom_argc, custom_argv, no_workers, true, &comm); + EXPECT_EQ(custom_argc, 4); + + arguments::AppConfig appConfig{}; + appConfig.vt_color = false; + appConfig.vt_epoch_graph_on_hang = false; + appConfig.vt_lb_name = "RotateLB"; + appConfig.vt_lb_stats = true; + appConfig.vt_no_detect_hang = false; + + vt::initialize(custom_argc, custom_argv, no_workers, true, &comm, &appConfig); + + EXPECT_EQ(theConfig()->prog_name, "vt_program"); + EXPECT_EQ(theConfig()->vt_color, false); + EXPECT_EQ(theConfig()->vt_epoch_graph_on_hang, false); + EXPECT_EQ(theConfig()->vt_lb_name, "RotateLB"); + EXPECT_EQ(theConfig()->vt_lb_stats, true); + EXPECT_EQ(theConfig()->vt_no_terminate, true); + // CLI args should overwrite hardcoded appConfig + EXPECT_EQ(theConfig()->vt_no_detect_hang, true); + + EXPECT_EQ(custom_argc, 2); + EXPECT_STREQ(custom_argv[0], "vt_program"); + EXPECT_STREQ(custom_argv[1], "--cli_argument=100"); + EXPECT_EQ(custom_argv[2], nullptr); +} + +TEST_F(TestInitialization, test_initialize_with_file_and_args) { + MPI_Comm comm = MPISingletonMultiTest::Get()->getComm(); + + static char prog_name[]{"vt_program"}; + static char cli_argument[]{"--cli_argument=100"}; + static char vt_no_terminate[]{"--vt_no_terminate"}; + static char vt_lb_name[]{"--vt_lb_name=RotateLB"}; + static char vt_input_config[]{"--vt_input_config=test_cfg.toml"}; + + std::vector custom_args; + custom_args.emplace_back(prog_name); + custom_args.emplace_back(cli_argument); + custom_args.emplace_back(vt_no_terminate); + custom_args.emplace_back(vt_input_config); + custom_args.emplace_back(vt_lb_name); + custom_args.emplace_back(nullptr); + + int custom_argc = custom_args.size() - 1; + char **custom_argv = custom_args.data(); + + EXPECT_EQ(custom_argc, 5); + + int this_rank; + MPI_Comm_rank(comm, &this_rank); + if (this_rank == 0) { + std::ofstream cfg_file_{"test_cfg.toml", std::ofstream::out | std::ofstream::trunc}; + cfg_file_ << "vt_lb_name = RandomLB\n"; + cfg_file_.close(); } + MPI_Barrier(comm); + + vt::initialize(custom_argc, custom_argv, no_workers, true, &comm); + + EXPECT_EQ(theConfig()->prog_name, "vt_program"); + EXPECT_EQ(theConfig()->vt_no_terminate, true); + EXPECT_EQ(theConfig()->vt_lb_name, "RotateLB"); + + EXPECT_EQ(custom_argc, 2); + EXPECT_STREQ(custom_argv[0], "vt_program"); + EXPECT_STREQ(custom_argv[1], "--cli_argument=100"); + EXPECT_EQ(custom_argv[2], nullptr); +} + +TEST_F(TestInitialization, test_initialize_with_file_args_and_appconfig) { + MPI_Comm comm = MPISingletonMultiTest::Get()->getComm(); + + static char prog_name[]{"vt_program"}; + static char cli_argument[]{"--cli_argument=100"}; + static char vt_no_terminate[]{"--vt_no_terminate"}; + static char vt_lb_name[]{"--vt_lb_name=RotateLB"}; + static char vt_input_config[]{"--vt_input_config=test_cfg.toml"}; std::vector custom_args; - int custom_argc; - char** custom_argv; -}; + custom_args.emplace_back(prog_name); + custom_args.emplace_back(cli_argument); + custom_args.emplace_back(vt_no_terminate); + custom_args.emplace_back(vt_input_config); + custom_args.emplace_back(vt_lb_name); + custom_args.emplace_back(nullptr); + + int custom_argc = custom_args.size() - 1; + char** custom_argv = custom_args.data(); + + EXPECT_EQ(custom_argc, 5); + + arguments::AppConfig appConfig{}; + appConfig.vt_lb_name = "GreedyLB"; + + int this_rank; + MPI_Comm_rank(comm, &this_rank); + if (this_rank == 0) { + std::ofstream cfg_file_{"test_cfg.toml", std::ofstream::out | std::ofstream::trunc}; + cfg_file_ << "vt_lb_name = RandomLB\n"; + cfg_file_.close(); + } + MPI_Barrier(comm); + + vt::initialize(custom_argc, custom_argv, no_workers, true, &comm, &appConfig); -TEST_F(TestInitialization, test_initialize_with_args) { EXPECT_EQ(theConfig()->prog_name, "vt_program"); EXPECT_EQ(theConfig()->vt_no_terminate, true); + EXPECT_EQ(theConfig()->vt_lb_name, "RotateLB"); EXPECT_EQ(custom_argc, 2); EXPECT_STREQ(custom_argv[0], "vt_program");