Skip to content

Commit 9b10c25

Browse files
Merge pull request #1206 from sstsimulator/devel
Automatically Merged using SST Master Branch Merger
2 parents 42b92ad + 2b5e873 commit 9b10c25

17 files changed

+692
-123
lines changed

.clang-format

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,5 +204,9 @@ WhitespaceSensitiveMacros:
204204
- CF_SWIFT_NAME
205205
- SST_SER
206206
- SST_SER_AS_PTR
207+
- SST_TP_VECTORCALL
208+
- SST_TP_PRINT_DEP
209+
- SST_TP_WATCHED
210+
- SST_TP_VERSIONS_USED
207211
...
208212

src/sst/core/Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,7 @@ include model/Makefile.inc
331331
include shared/Makefile.inc
332332
include impl/Makefile.inc
333333
include sync/Makefile.inc
334+
include util/Makefile.inc
334335
include testingframework/Makefile.inc
335336
include testElements/Makefile.inc
336337

src/sst/core/config.cc

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,14 @@ class ConfigHelper
373373
return 0;
374374
}
375375

376+
#if PY_MINOR_VERSION >= 9
377+
// enable Python coverage
378+
static int enablePythonCoverage(Config* cfg, const std::string& UNUSED(arg))
379+
{
380+
cfg->enable_python_coverage_ = true;
381+
return 0;
382+
}
383+
#endif
376384

377385
// Advanced options - profiling
378386
static int enableProfiling(Config* cfg, const std::string& arg)
@@ -408,6 +416,35 @@ class ConfigHelper
408416
return msg;
409417
}
410418

419+
#if PY_MINOR_VERSION >= 9
420+
static std::string getPythonCoverageExtHelp()
421+
{
422+
std::string msg = "Python Coverage (EXPERIMENTAL):\n\n";
423+
424+
msg.append("NOTE: This feature is considered experimental until we can complete further testing.\n\n");
425+
426+
msg.append("If you are using python configuration (model definition) files as part of a larger project and are "
427+
"interested in measuring code coverage of a test/example/application suite, you can instruct sst to "
428+
"enable the python coverage module when launching python configuration files as part of a "
429+
"simulation invocation. To do so, you need three things:\n\n");
430+
431+
msg.append("\t1.\t\vInstall python’s coverage module (via an OS package or pip) on your system "
432+
"<https://pypi.org/project/coverage/>\n");
433+
434+
msg.append("\t2.\t\vEnsure that the \"coverage\" command is in your path and that you can invoke the python "
435+
"that SST uses and import the coverage module without error.\n");
436+
437+
msg.append(
438+
"\t3.\t\vSet the environment variable SST_CONFIG_PYTHON_COVERAGE to a value of 1, yes, on, true or t; or "
439+
"invoke coverage on the command line by using the command line option --enable-python-coverage.\n\n");
440+
441+
msg.append("Then invoke SST as normal using the python model configuration file for which you want to measure "
442+
"coverage.\n");
443+
444+
return msg;
445+
}
446+
#endif
447+
411448
static std::string getProfilingExtHelp()
412449
{
413450
std::string msg = "Profiling Points [EXPERIMENTAL]:\n\n";
@@ -802,6 +839,10 @@ Config::Config(uint32_t num_ranks, bool first_rank) : ConfigShared(!first_rank,
802839
#endif
803840
debugFile_ = "/dev/null";
804841

842+
#if PY_MINOR_VERSION >= 9
843+
enable_python_coverage_ = false;
844+
#endif
845+
805846
// Advance Options - Profiling
806847
enabled_profiling_ = "";
807848
profiling_output_ = "stdout";
@@ -990,6 +1031,16 @@ Config::insertOptions()
9901031
true);
9911032
addLibraryPathOptions();
9921033

1034+
#if PY_MINOR_VERSION >= 9
1035+
DEF_FLAG_EH(
1036+
"enable-python-coverage", 0,
1037+
"[EXPERIMENTAL] Causes the base Python interpreter to activate the coverage.Coverage object. This option can "
1038+
"also be turned "
1039+
"on by setting the environment variable SST_CONFIG_PYTHON_COVERAGE to true.",
1040+
std::bind(&ConfigHelper::enablePythonCoverage, this, _1), std::bind(&ConfigHelper::getPythonCoverageExtHelp),
1041+
false);
1042+
#endif
1043+
9931044
/* Advanced Features - Profiling */
9941045
DEF_SECTION_HEADING("Advanced Options - Profiling (EXPERIMENTAL)");
9951046
DEF_ARG_EH(

src/sst/core/config.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
#include "sst/core/serialization/serializable.h"
1717
#include "sst/core/sst_types.h"
1818

19+
// Pull in the patchlevel for Python so we can check Python version
20+
#include <patchlevel.h>
1921
#include <string>
2022

2123
/* Forward declare for Friendship */
@@ -288,6 +290,14 @@ class Config : public ConfigShared, public SST::Core::Serialization::serializabl
288290
const std::string& addLibPath() const { return addLibPath_; }
289291
*/
290292

293+
294+
#if PY_MINOR_VERSION >= 9
295+
/**
296+
Controls whether the Python coverage object will be loaded
297+
*/
298+
bool enablePythonCoverage() const { return enable_python_coverage_; }
299+
#endif
300+
291301
// Advanced options - Profiling
292302

293303
/**
@@ -437,6 +447,9 @@ class Config : public ConfigShared, public SST::Core::Serialization::serializabl
437447
ser& debugFile_;
438448
ser& libpath_;
439449
ser& addlibpath_;
450+
#if PY_MINOR_VERSION >= 9
451+
ser& enable_python_coverage_;
452+
#endif
440453
ser& enabled_profiling_;
441454
ser& profiling_output_;
442455
ser& runMode_;
@@ -534,6 +547,9 @@ class Config : public ConfigShared, public SST::Core::Serialization::serializabl
534547
std::string debugFile_; /*!< File to which debug information should be written */
535548
// std::string libpath_; ** in ConfigShared
536549
// std::string addLibPath_; ** in ConfigShared
550+
#if PY_MINOR_VERSION >= 9
551+
bool enable_python_coverage_; /*!< Enable the Python coverage module */
552+
#endif
537553

538554
// Advanced options - profiling
539555
std::string enabled_profiling_; /*!< Enabled default profiling points */

src/sst/core/configBase.cc

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "sst/core/configBase.h"
1515

1616
#include "sst/core/env/envquery.h"
17+
#include "sst/core/util/smartTextFormatter.h"
1718
#include "sst/core/warnmacros.h"
1819

1920
#include <cstdlib>
@@ -284,9 +285,12 @@ ConfigBase::printExtHelp(const std::string& option)
284285
fprintf(stderr, "No additional help found for option \"%s\"\n", option.c_str());
285286
}
286287
else {
288+
Util::SmartTextFormatter formatter({ 2, 5, 8 }, 1);
289+
287290
std::function<std::string(void)>& func = extra_help_map[option];
288291
std::string help = func();
289-
fprintf(stderr, "%s\n", help.c_str());
292+
formatter.append(help);
293+
fprintf(stderr, "%s\n", formatter.str().c_str());
290294
}
291295

292296
return 1; /* Should not continue */

src/sst/core/configBase.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ struct AnnotationInfo
8686
addOption({ longName, optional_argument, 0, shortName }, "[" argName "]", text, func, { __VA_ARGS__ });
8787

8888
// Macros that include extended help
89+
#define DEF_FLAG_EH(longName, shortName, text, func, eh, ...) \
90+
addOption({ longName, no_argument, 0, shortName }, "", text, func, { __VA_ARGS__ }, eh);
91+
8992
#define DEF_ARG_EH(longName, shortName, argName, text, func, eh, ...) \
9093
addOption({ longName, required_argument, 0, shortName }, argName, text, func, { __VA_ARGS__ }, eh);
9194

src/sst/core/from_string.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ from_string(const std::string& input)
5151
return false;
5252
}
5353
else {
54-
throw new std::invalid_argument("from_string: no valid conversion");
54+
throw std::invalid_argument("from_string: no valid conversion");
5555
}
5656
}
5757
else if ( std::is_same<unsigned long, T>::value ) {

src/sst/core/model/python/pymodel.cc

Lines changed: 114 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "sst/core/configGraph.h"
2020
#include "sst/core/cputimer.h"
2121
#include "sst/core/factory.h"
22+
#include "sst/core/from_string.h"
2223
#include "sst/core/memuse.h"
2324
#include "sst/core/model/element_python.h"
2425
#include "sst/core/model/python/pymacros.h"
@@ -88,57 +89,57 @@ DISABLE_WARN_DEPRECATED_DECLARATION
8889
#endif
8990
#endif
9091
static PyTypeObject ModuleLoaderType = {
91-
SST_PY_OBJ_HEAD "ModuleLoader", /* tp_name */
92-
sizeof(ModuleLoaderPy_t), /* tp_basicsize */
93-
0, /* tp_itemsize */
94-
nullptr, /* tp_dealloc */
95-
0, /* tp_vectorcall_offset */
96-
nullptr, /* tp_getattr */
97-
nullptr, /* tp_setattr */
98-
nullptr, /* tp_as_sync */
99-
nullptr, /* tp_repr */
100-
nullptr, /* tp_as_number */
101-
nullptr, /* tp_as_sequence */
102-
nullptr, /* tp_as_mapping */
103-
nullptr, /* tp_hash */
104-
nullptr, /* tp_call */
105-
nullptr, /* tp_str */
106-
nullptr, /* tp_getattro */
107-
nullptr, /* tp_setattro */
108-
nullptr, /* tp_as_buffer */
109-
Py_TPFLAGS_DEFAULT, /* tp_flags */
110-
"SST Module Loader", /* tp_doc */
111-
nullptr, /* tp_traverse */
112-
nullptr, /* tp_clear */
113-
nullptr, /* tp_rich_compare */
114-
0, /* tp_weaklistoffset */
115-
nullptr, /* tp_iter */
116-
nullptr, /* tp_iternext */
117-
mlMethods, /* tp_methods */
118-
nullptr, /* tp_members */
119-
nullptr, /* tp_getset */
120-
nullptr, /* tp_base */
121-
nullptr, /* tp_dict */
122-
nullptr, /* tp_descr_get */
123-
nullptr, /* tp_descr_set */
124-
0, /* tp_dictoffset */
125-
nullptr, /* tp_init */
126-
nullptr, /* tp_alloc */
127-
nullptr, /* tp_new */
128-
nullptr, /* tp_free */
129-
nullptr, /* tp_is_gc */
130-
nullptr, /* tp_bases */
131-
nullptr, /* tp_mro */
132-
nullptr, /* tp_cache */
133-
nullptr, /* tp_subclasses */
134-
nullptr, /* tp_weaklist */
135-
nullptr, /* tp_del */
136-
0, /* tp_version_tag */
137-
nullptr, /* tp_finalize */
138-
SST_TP_VECTORCALL /* Python3.8+ */
139-
SST_TP_PRINT_DEP /* Python3.8 only */
140-
SST_TP_WATCHED /* Python3.12+ */
141-
SST_TP_VERSIONS_USED /* Python3.13+ */
92+
SST_PY_OBJ_HEAD "ModuleLoader", /* tp_name */
93+
sizeof(ModuleLoaderPy_t), /* tp_basicsize */
94+
0, /* tp_itemsize */
95+
nullptr, /* tp_dealloc */
96+
0, /* tp_vectorcall_offset */
97+
nullptr, /* tp_getattr */
98+
nullptr, /* tp_setattr */
99+
nullptr, /* tp_as_sync */
100+
nullptr, /* tp_repr */
101+
nullptr, /* tp_as_number */
102+
nullptr, /* tp_as_sequence */
103+
nullptr, /* tp_as_mapping */
104+
nullptr, /* tp_hash */
105+
nullptr, /* tp_call */
106+
nullptr, /* tp_str */
107+
nullptr, /* tp_getattro */
108+
nullptr, /* tp_setattro */
109+
nullptr, /* tp_as_buffer */
110+
Py_TPFLAGS_DEFAULT, /* tp_flags */
111+
"SST Module Loader", /* tp_doc */
112+
nullptr, /* tp_traverse */
113+
nullptr, /* tp_clear */
114+
nullptr, /* tp_rich_compare */
115+
0, /* tp_weaklistoffset */
116+
nullptr, /* tp_iter */
117+
nullptr, /* tp_iternext */
118+
mlMethods, /* tp_methods */
119+
nullptr, /* tp_members */
120+
nullptr, /* tp_getset */
121+
nullptr, /* tp_base */
122+
nullptr, /* tp_dict */
123+
nullptr, /* tp_descr_get */
124+
nullptr, /* tp_descr_set */
125+
0, /* tp_dictoffset */
126+
nullptr, /* tp_init */
127+
nullptr, /* tp_alloc */
128+
nullptr, /* tp_new */
129+
nullptr, /* tp_free */
130+
nullptr, /* tp_is_gc */
131+
nullptr, /* tp_bases */
132+
nullptr, /* tp_mro */
133+
nullptr, /* tp_cache */
134+
nullptr, /* tp_subclasses */
135+
nullptr, /* tp_weaklist */
136+
nullptr, /* tp_del */
137+
0, /* tp_version_tag */
138+
nullptr, /* tp_finalize */
139+
SST_TP_VECTORCALL /* Python3.8+ */
140+
SST_TP_PRINT_DEP /* Python3.8 only */
141+
SST_TP_WATCHED /* Python3.12+ */
142+
SST_TP_VERSIONS_USED /* Python3.13+ */
142143
};
143144
#if PY_MAJOR_VERSION == 3
144145
#if PY_MINOR_VERSION == 8
@@ -1063,7 +1064,7 @@ PyInit_sst(void)
10631064

10641065
void
10651066
SSTPythonModelDefinition::initModel(
1066-
const std::string& script_file, int verbosity, Config* UNUSED(config), int argc, char** argv)
1067+
const std::string& script_file, int verbosity, Config* config, int argc, char** argv)
10671068
{
10681069
output = new Output("SSTPythonModel: ", verbosity, 0, SST::Output::STDOUT);
10691070

@@ -1144,6 +1145,28 @@ SSTPythonModelDefinition::initModel(
11441145
// // directory to the path
11451146
PyRun_SimpleString("sys.meta_path.append(sst.ModuleLoader())\n");
11461147
#endif
1148+
1149+
1150+
// Check to see if we need to import the coverage module (only works in Python >=3.9
1151+
#if PY_MINOR_VERSION >= 9
1152+
if ( config->enablePythonCoverage() ) { enablePythonCoverage = true; }
1153+
else {
1154+
const char* envConfigCoverage = getenv("SST_CONFIG_PYTHON_COVERAGE");
1155+
if ( nullptr != envConfigCoverage ) {
1156+
std::string value(envConfigCoverage);
1157+
try {
1158+
enablePythonCoverage = SST::Core::from_string<bool>(value);
1159+
}
1160+
catch ( std::invalid_argument& e ) {
1161+
output->fatal(
1162+
CALL_INFO, 1,
1163+
"ERROR: Invalid format for SST_CONFIG_PYTHON_COVERAGE. Valid boolean pairs are true/false, t/f, "
1164+
"yes/no, y/n, on/off, or 1/0\n");
1165+
enablePythonCoverage = false;
1166+
}
1167+
}
1168+
}
1169+
#endif
11471170
}
11481171

11491172
SSTPythonModelDefinition::SSTPythonModelDefinition(
@@ -1236,8 +1259,29 @@ SSTPythonModelDefinition::createConfigGraph()
12361259
{
12371260
output->verbose(CALL_INFO, 1, 0, "Creating config graph for SST using Python model...\n");
12381261

1262+
#if PY_MINOR_VERSION >= 9
1263+
if ( enablePythonCoverage ) {
1264+
// Create coverage object with a name unlikely to be used in the user script
1265+
int startcoverageReturn = PyRun_SimpleString("import coverage\n"
1266+
"zzzSSTPythonCoverageModule = coverage.Coverage()\n"
1267+
"zzzSSTPythonCoverageModule.start()\n");
1268+
if ( nullptr != PyErr_Occurred() ) {
1269+
// Print the Python error and then let SST exit as a fatal-stop.
1270+
output->fatal(
1271+
CALL_INFO, 1,
1272+
"ERROR: Error occurred starting test coverage of the SST model script. Reported error:\n");
1273+
PyErr_Print();
1274+
}
1275+
if ( -1 == startcoverageReturn ) {
1276+
output->fatal(CALL_INFO, 1, "Execution of starting test coverage failed\n%s", loadErrors.c_str());
1277+
}
1278+
}
1279+
#endif
1280+
1281+
// Open the input script
12391282
FILE* fp = fopen(scriptName.c_str(), "r");
12401283
if ( !fp ) { output->fatal(CALL_INFO, 1, "Unable to open python script %s\n", scriptName.c_str()); }
1284+
PyErr_Clear();
12411285
int createReturn = PyRun_AnyFileEx(fp, scriptName.c_str(), 1);
12421286

12431287
if ( nullptr != PyErr_Occurred() ) {
@@ -1256,6 +1300,24 @@ SSTPythonModelDefinition::createConfigGraph()
12561300
output->fatal(CALL_INFO, 1, "Error occured handling the creation of the component graph in Python.\n");
12571301
}
12581302

1303+
#if PY_MINOR_VERSION >= 9
1304+
// If coverage was enabled, stop the module and output the results
1305+
if ( enablePythonCoverage ) {
1306+
PyErr_Clear();
1307+
int stopcoverageReturn = PyRun_SimpleString("zzzSSTPythonCoverageModule.stop()\n"
1308+
"zzzSSTPythonCoverageModule.save()\n");
1309+
1310+
if ( nullptr != PyErr_Occurred() ) {
1311+
// Print the Python error and then let SST exit as a fatal-stop.
1312+
output->fatal(
1313+
CALL_INFO, 1, "ERROR: Error occurred stopping coverage of the SST model script. Reported error:\n");
1314+
PyErr_Print();
1315+
}
1316+
if ( -1 == stopcoverageReturn ) {
1317+
output->fatal(CALL_INFO, 1, "Execution of stopping coverage failed\n%s", loadErrors.c_str());
1318+
}
1319+
}
1320+
#endif
12591321
return graph;
12601322
}
12611323

0 commit comments

Comments
 (0)