Skip to content

Commit

Permalink
Added the ability to have extended help for command line options usin…
Browse files Browse the repository at this point in the history
…g --help=option. Also added extended help for --enable-profiling.
  • Loading branch information
feldergast committed Jul 25, 2023
1 parent f12ba92 commit 4cbcb1e
Show file tree
Hide file tree
Showing 9 changed files with 216 additions and 60 deletions.
2 changes: 1 addition & 1 deletion src/sst/core/bootsst.cc
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ main(int argc, char* argv[])
// Create a ConfigShred object. This object won't print any error
// messages about unknown command line options, that will be
// deferred to the actual sstsim.x executable.
SST::ConfigShared cfg(true, true, true, true, true);
SST::ConfigShared cfg(true, true, true, true);

// Make a copy of the argv array (shallow)
char* argv_copy[argc + 1];
Expand Down
2 changes: 1 addition & 1 deletion src/sst/core/bootsstinfo.cc
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ main(int argc, char* argv[])
// for the wrapper. We will suppress output so that it won't
// report unknown options which are only parsed by the actual
// sst-info executable.
SST::ConfigShared cfg(true, true, true, true, true);
SST::ConfigShared cfg(true, true, true, true);

// Make a copy of the argv array (shallow)
char* argv_copy[argc + 1];
Expand Down
66 changes: 58 additions & 8 deletions src/sst/core/config.cc
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,14 @@ class ConfigHelper
{
public:
// Print usage
static int printHelp(Config* cfg, const std::string& UNUSED(arg)) { return cfg->printUsage(); }
static int printUsage(Config* cfg, const std::string& UNUSED(arg)) { return cfg->printUsage(); }

// Print usage
static int printHelp(Config* cfg, const std::string& arg)
{
if ( arg != "" ) return cfg->printExtHelp(arg);
return cfg->printUsage();
}

// Prints the SST version
static int printVersion(Config* UNUSED(cfg), const std::string& UNUSED(arg))
Expand Down Expand Up @@ -355,6 +362,39 @@ class ConfigHelper
return 0;
}

static std::string getProfilingExtHelp()
{
std::string msg = "Profiling Points [EXPERIMENTAL]:\n\n";
msg.append(
"NOTE: Profiling points are still in development and syntax for enabling profiling tools, as well as "
"available profiling points is subject to change. However, it is intended that profiling points "
"will continue to be supported into the future.\n\n");
msg.append(" Profiling points are points in the code where a profiling tool can be instantiated. The "
"profiling tool allows you to collect various data about code segments. There are currently three "
"profiling points in SST core:\n");
msg.append(" - clock: profiles calls to user registered clock handlers\n");
msg.append(" - event: profiles calls to user registered event handlers set on Links\n");
msg.append(" - sync: profiles calls into the SyncManager (only valid for parallel simulations)\n");
msg.append("\n");
msg.append(" The format for enabling profile point is a semicolon separated list where each item specifies "
"details for a given profiling tool using the following format:\n");
msg.append(" name:type(params)[point]\n");
msg.append(" name: name of tool to be shown in output\n");
msg.append(" type: type of profiling tool in ELI format (lib.type)\n");
msg.append(" params: optional parameters to pass to profiling tool, format is key=value,key=value...\n");
msg.append(" point: profiling point to load the tool into\n");
msg.append("\n");
msg.append("Profiling tools can all be enabled in a single instance of --enable-profiling, or you can use "
"multiple instances of --enable-profiling can be used to enable more than one profiling tool. It "
"is also possible to attach more than one profiling tool to a given profiling point.\n");
msg.append("\n");
msg.append("Examples:\n");
msg.append(
" --enable-profiling=\"events:sst.profile.handler.event.time.high_resolution(level=component)[event]\"\n");
msg.append(" --enable-profiling=\"clocks:sst.profile.handler.clock.count(level=subcomponent)[clock]\"\n");
msg.append(" --enable-profiling=sync:sst.profile.sync.time.steady[sync]\n");
return msg;
}

// Advanced options - debug

Expand Down Expand Up @@ -464,8 +504,13 @@ Config::print()
std::cout << "no_env_config = " << no_env_config_ << std::endl;
}

static std::vector<AnnotationInfo> annotations = {
{ 'S',
"Options annotated with 'S' can be set in the SDL file (input configuration file)\n - Note: Options set on the "
"command line take precedence over options set in the SDL file\n" }
};

Config::Config(uint32_t num_ranks, bool first_rank) : ConfigShared(!first_rank, false)
Config::Config(uint32_t num_ranks, bool first_rank) : ConfigShared(!first_rank, annotations)
{
// Basic Options
first_rank_ = first_rank;
Expand Down Expand Up @@ -567,7 +612,10 @@ Config::insertOptions()
using namespace std::placeholders;
/* Informational options */
DEF_SECTION_HEADING("Informational Options");
DEF_FLAG("help", 'h', "Print help message", std::bind(&ConfigHelper::printHelp, this, _1));
DEF_FLAG("usage", 'h', "Print usage information.", std::bind(&ConfigHelper::printUsage, this, _1));
DEF_ARG(
"help", 0, "option", "Print extended help information for requested option.",
std::bind(&ConfigHelper::printHelp, this, _1), false);
DEF_FLAG("version", 'V', "Print SST Release Version", std::bind(&ConfigHelper::printVersion, this, _1));

/* Basic Options */
Expand Down Expand Up @@ -679,11 +727,11 @@ Config::insertOptions()

/* Advanced Features - Profiling */
DEF_SECTION_HEADING("Advanced Options - Profiling (EXPERIMENTAL)");
DEF_ARG(
DEF_ARG_EH(
"enable-profiling", 0, "POINTS",
"Enables default profiling for the specified points. Argument is a semicolon separated list specifying the "
"points to enable.",
std::bind(&ConfigHelper::enableProfiling, this, _1), true);
std::bind(&ConfigHelper::enableProfiling, this, _1), std::bind(&ConfigHelper::getProfilingExtHelp), true);
DEF_ARG(
"profiling-output", 0, "FILE", "Set output location for profiling data [stdout (default) or a filename]",
std::bind(&ConfigHelper::setProfilingOutput, this, _1), true);
Expand Down Expand Up @@ -721,8 +769,6 @@ Config::getUsagePrelude()
{
std::string prelude = "Usage: sst [options] config-file\n";
prelude.append(" Arguments to options contained in [] are optional\n");
prelude.append(" Options available to be set in the sdl file (input configuration file) are denoted by (S)\n");
prelude.append(" - Options set on the command line take precedence over options set in the SDL file\n");
prelude.append(" Notes on flag options (options that take an optional BOOL value):\n");
prelude.append(" - BOOL values can be expressed as true/false, yes/no, on/off or 1/0\n");
prelude.append(" - Program default for flags is false\n");
Expand Down Expand Up @@ -782,7 +828,11 @@ Config::checkArgsAfterParsing()
bool
Config::setOptionFromModel(const string& entryName, const string& value)
{
return setOptionExternal(entryName, value);
// Check to make sure option is settable in the SDL file
if ( getAnnotation(entryName, 'S') ) { return setOptionExternal(entryName, value); }
fprintf(stderr, "ERROR: Option \"%s\" is not available to be set in the SDL file\n", entryName.c_str());
exit(-1);
return false;
}


Expand Down
2 changes: 1 addition & 1 deletion src/sst/core/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class Config : public ConfigShared, public SST::Core::Serialization::serializabl
Default constructor used for serialization. At this point,
first_rank_ is no longer needed, so just initialize to false.
*/
Config() : ConfigShared(true, true), first_rank_(false) {}
Config() : ConfigShared(true, {}), first_rank_(false) {}

//// Functions for use in main

Expand Down
118 changes: 95 additions & 23 deletions src/sst/core/configBase.cc
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,11 @@ ConfigBase::parseBoolean(const std::string& arg, bool& success, const std::strin

void
ConfigBase::addOption(
struct option opt, const char* argname, const char* desc, std::function<int(const char* arg)> callback, bool header,
bool sdl_avail)
struct option opt, const char* argname, const char* desc, std::function<int(const char* arg)> callback,
std::vector<bool> annotations, std::function<std::string(void)> ext_help)
{
// Put this into the options vector
options.emplace_back(opt, argname, desc, callback, header, sdl_avail, false);
options.emplace_back(opt, argname, desc, callback, false, annotations, ext_help, false);

LongOption& new_option = options.back();

Expand All @@ -74,6 +74,9 @@ ConfigBase::addOption(
// Increment the number of options
num_options++;

// See if there is extended help
if ( ext_help ) has_extended_help_ = true;

// See if this is the longest option
size_t size = 0;
if ( new_option.opt.name != nullptr ) { size = strlen(new_option.opt.name); }
Expand All @@ -95,6 +98,18 @@ ConfigBase::addOption(
short_options_string.append("::");
}
}

// Handle any extra help functions
if ( ext_help ) { extra_help_map[opt.name] = ext_help; }
}

void
ConfigBase::addHeading(const char* desc)
{
struct option opt = { "", optional_argument, 0, 0 };
std::vector<bool> vec;
options.emplace_back(
opt, "", desc, std::function<int(const char* arg)>(), true, vec, std::function<std::string(void)>(), false);
}

std::string
Expand All @@ -121,6 +136,7 @@ ConfigBase::addPositionalCallback(std::function<int(int num, const char* arg)> c
positional_args = callback;
}


int
ConfigBase::printUsage()
{
Expand All @@ -138,14 +154,21 @@ ConfigBase::printUsage()
if ( errno == E_OK ) MAX_WIDTH = x;
}

const char* sdl_indicator = suppress_sdl_ ? "" : "(S)";
const uint32_t sdl_start = longest_option + 6;
const uint32_t desc_start = sdl_start + strlen(sdl_indicator) + 1;
const uint32_t desc_width = MAX_WIDTH - desc_start;
const uint32_t ann_start = longest_option + 6;
const uint32_t desc_start = ann_start + annotations_.size() + 2;
const uint32_t desc_width = MAX_WIDTH - desc_start;

/* Print usage */
/* Print usage prelude */
fprintf(stderr, "%s", getUsagePrelude().c_str());

/* Print info about annotations */
if ( has_extended_help_ ) { fprintf(stderr, "\nOptions annotated with 'H' have extended help available\n"); }
for ( size_t i = 0; i < annotations_.size(); ++i ) {
fprintf(stderr, "%s\n", annotations_[i].help.c_str());
}

// Print info about annotations

for ( auto& option : options ) {
if ( option.header ) {
// Just a section heading
Expand All @@ -164,17 +187,26 @@ ConfigBase::printUsage()
if ( option.opt.has_arg != no_argument ) { npos += fprintf(stderr, "=%s", option.argname.c_str()); }
// If we have already gone beyond the description start,
// description starts on new line
if ( npos >= sdl_start ) {
if ( npos >= ann_start ) {
fprintf(stderr, "\n");
npos = 0;
}

// If this can be set in the sdl file, start description with
// "(S)"
while ( npos < sdl_start ) {
// Get to the start of the annotations
while ( npos < ann_start ) {
npos += fprintf(stderr, " ");
}
if ( option.sdl_avail ) { npos += fprintf(stderr, "%s", sdl_indicator); }

// Print the annotations
// First check for extended help
npos += fprintf(stderr, "%c", option.ext_help ? 'H' : ' ');

// Now do the rest of the annotations
for ( size_t i = 0; i < annotations_.size(); ++i ) {
char c = ' ';
if ( option.annotations.size() >= (i + 1) && option.annotations[i] ) c = annotations_[i].annotation;
npos += fprintf(stderr, "%c", c);
}

const char* text = option.desc.c_str();
while ( text != nullptr && *text != '\0' ) {
Expand Down Expand Up @@ -209,6 +241,25 @@ ConfigBase::printUsage()
return 1; /* Should not continue */
}


int
ConfigBase::printExtHelp(const std::string& option)
{
if ( suppress_print_ ) return 1;

if ( extra_help_map.find(option) == extra_help_map.end() ) {
fprintf(stderr, "No additional help found for option \"%s\"\n", option.c_str());
}
else {
std::function<std::string(void)>& func = extra_help_map[option];
std::string help = func();
fprintf(stderr, "%s\n", help.c_str());
}

return 1; /* Should not continue */
}


int
ConfigBase::parseCmdLine(int argc, char* argv[], bool ignore_unknown)
{
Expand Down Expand Up @@ -348,21 +399,42 @@ ConfigBase::setOptionExternal(const string& entryName, const string& value)
// NOTE: print outs in this function will not be suppressed
for ( auto& option : options ) {
if ( !entryName.compare(option.opt.name) ) {
if ( option.sdl_avail ) {
// If this was set on the command line, skip it
if ( option.set_cmdline ) return false;
return option.callback(value.c_str());
}
else {
fprintf(stderr, "ERROR: Option \"%s\" is not available to be set in the SDL file\n", entryName.c_str());
exit(-1);
return false;
}
if ( option.set_cmdline ) return false;
return option.callback(value.c_str());
}
}
fprintf(stderr, "ERROR: Unknown configuration entry \"%s\"\n", entryName.c_str());
exit(-1);
return false;
}

bool
ConfigBase::getAnnotation(const std::string& entryName, char annotation)
{
// Need to look for the index of the annotation
size_t index = std::numeric_limits<size_t>::max();
for ( size_t i = 0; i < annotations_.size(); ++i ) {
if ( annotations_[i].annotation == annotation ) { index = i; }
}

if ( index == std::numeric_limits<size_t>::max() ) {
fprintf(stderr, "ERROR: Searching for unknown annotation: '%c'\n", annotation);
exit(-1);
}

// NOTE: print outs in this function will not be suppressed
for ( auto& option : options ) {
if ( !entryName.compare(option.opt.name) ) {
// Check for the annotation. If the index is not in the
// vector, we assume false
if ( option.annotations.size() <= index ) return false;
return option.annotations[index];
}
}

fprintf(stderr, "ERROR: Unknown configuration entry \"%s\"\n", entryName.c_str());
exit(-1);
return false;
}

} // namespace SST
Loading

0 comments on commit 4cbcb1e

Please sign in to comment.