Teuchos Package Browser (Single Doxygen Collection)  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Teuchos_SystemInformation.cpp
Go to the documentation of this file.
1 // @HEADER
2 // *****************************************************************************
3 // Teuchos: Common Tools Package
4 //
5 // Copyright 2004 NTESS and the Teuchos contributors.
6 // SPDX-License-Identifier: BSD-3-Clause
7 // *****************************************************************************
8 // @HEADER
9 
11 #include "Teuchos_EnvVariables.hpp"
12 #include "Teuchos_StrUtils.hpp"
14 
15 #if !defined(__GNUC__) || (__GNUC__ >= 10)
16 #include <filesystem>
17 #endif
18 #include <iostream>
19 #include <stdexcept>
20 #include <set>
21 
22 // environ should be available on posix platforms
23 #if not(defined(WIN) && (_MSC_VER >= 1900))
24 // needs to be in the global namespace
25 extern char **environ;
26 #endif
27 
28 namespace Teuchos::SystemInformation {
29 
30 namespace {
31 
32 std::map<std::string, std::pair<std::string, std::string>>& getCommandsMap() {
33  static std::map<std::string, std::pair<std::string, std::string>> commands;
34  return commands;
35 }
36 
37 std::set<std::string>& getEnvironmentVariablesSet() {
38  static std::set<std::string> environmentVariables;
39  return environmentVariables;
40 }
41 
42 }
43 
44 bool commandIsAvailable(const std::string &command) {
45  return !std::system(
46  std::string("command -v " + command + " > /dev/null 2>&1").c_str());
47 }
48 
49 std::string runCommandAndCaptureOutput(const std::string &command) {
50  char buffer[128];
51  std::string result = "";
52  FILE *pipe = popen(command.c_str(), "r");
53  if (!pipe)
54  return "command \"" + command + "\"failed";
55  try {
56  while (fgets(buffer, sizeof buffer, pipe) != NULL) {
57  result += buffer;
58  }
59  } catch (...) {
60  pclose(pipe);
61  return "command \"" + command + "\"failed";
62  }
63  pclose(pipe);
64  return result;
65 }
66 
68 registerEnvironmentVariable(const std::string &variableName) {
69  auto &environmentVariables = getEnvironmentVariablesSet();
70 
71  if (auto search = environmentVariables.find(variableName);
72  search != environmentVariables.end()) {
73  // variable is already present
74  return ALREADY_PRESENT;
75  } else {
76  // variable not found
77  environmentVariables.insert(variableName);
78  return REGISTERED;
79  }
80 }
81 
82 void registerAllPrefixedVariables(const std::string &prefix) {
83  char **env;
84 #if defined(WIN) && (_MSC_VER >= 1900)
85  env = *__p__environ();
86 #else
87  env = environ; // defined at the top of this file as extern char **environ;
88 #endif
89  for (; *env; ++env) {
90  // const std::string_view ev(*env);
91 
92  // split name=value on the first =, everything before = is name
93  auto substrings = StrUtils::splitString(*env, '=');
94  std::string name = substrings[0];
95 
96  if (name.size() >= prefix.size() &&
97  name.substr(0, prefix.size()) == prefix) {
99  }
100  }
101 }
102 
104 registerCommand(const std::string &commandLabel,
105  const std::string &commandToRunAndCapture,
106  const std::string &commandToCheckForExistence) {
107  auto &commands = getCommandsMap();
108 
109  std::string myCommandToRunAndCapture = commandToRunAndCapture;
110  if (myCommandToRunAndCapture.empty())
111  myCommandToRunAndCapture = commandLabel;
112 
113  std::string myCommandToCheckForExistence = commandToCheckForExistence;
114  if (myCommandToCheckForExistence.empty())
115  myCommandToCheckForExistence = myCommandToRunAndCapture;
116 
117  if (auto search = commands.find(commandLabel); search != commands.end()) {
118  if ((commands[commandLabel].first == myCommandToCheckForExistence) &&
119  (commands[commandLabel].second == myCommandToRunAndCapture))
120  return ALREADY_PRESENT;
121  else {
122  std::cerr
123  << "Teuchos::SystemInformation: Attempted to register a command (\""
124  << commandLabel << "\", \"" << myCommandToRunAndCapture << "\", \""
125  << myCommandToCheckForExistence << "\") "
126  << "that clashes with already registered command: (" << commandLabel
127  << "\", \"" << commands[commandLabel].first << "\", \""
128  << commands[commandLabel].second << "\")." << std::endl;
129  return FAILURE;
130  }
131  } else {
132  // command not found
133  commands[commandLabel] = {myCommandToCheckForExistence, myCommandToRunAndCapture};
134  return REGISTERED;
135  }
136 }
137 
139 
140  std::string userProvidedEnvVariables =
141  Teuchos::getEnvironmentVariable<std::string>(
142  "TEUCHOS_USER_ENVIRONMENT_VARIABLES", "");
143  if (!userProvidedEnvVariables.empty()) {
144  auto strings = StrUtils::splitString(userProvidedEnvVariables, ';');
145  bool isValid = (strings.size() >= 1);
146  if (!isValid)
147  std::cerr
148  << "Teuchos::SystemInformation: The value of the environment "
149  "variable "
150  "TEUCHOS_USER_ENVIRONMENT_VARIABLES needs to be a semi-colon "
151  "seperated string. Value: "
152  << userProvidedEnvVariables << std::endl;
153  for (int itemNo = 0; itemNo < strings.size(); ++itemNo) {
154  std::string &variableName = strings[itemNo];
155  if (registerEnvironmentVariable(variableName) == FAILURE)
156  isValid = false;
157  }
159  !isValid, std::runtime_error,
160  "Teuchos::SystemInformation: Invalid environment variable "
161  "TEUCHOS_USER_ENVIRONMENT_VARIABLES");
162  }
163 
164  std::string userProvidedCommands =
165  Teuchos::getEnvironmentVariable<std::string>("TEUCHOS_USER_COMMANDS", "");
166  if (!userProvidedCommands.empty()) {
167  auto strings = StrUtils::splitString(userProvidedEnvVariables, ';');
168  bool isValid = (strings.size() % 3 == 0) && (strings.size() >= 3);
169  if (!isValid)
170  std::cerr << "Teuchos::SystemInformation: The value of the environment "
171  "variable TEUCHOS_USER_COMMANDS "
172  "needs to be a semi-colon seperated string with a number of "
173  "elements that is a multiple of 3. Value: "
174  << userProvidedCommands << std::endl;
175  int tupleNo = 0;
176  while (isValid && (3 * tupleNo + 2 < strings.size())) {
177  std::string &commandLabel = strings[3 * tupleNo];
178  std::string &commandToRunAndCapture = strings[3 * tupleNo + 1];
179  std::string &commandToCheckForExistence = strings[3 * tupleNo + 2];
180  if (registerCommand(commandLabel, commandToRunAndCapture,
181  commandToCheckForExistence) == FAILURE) {
182  isValid = false;
183  }
184  ++tupleNo;
185  }
186  TEUCHOS_TEST_FOR_EXCEPTION(!isValid, std::runtime_error,
187  "Teuchos::SystemInformation: Invalid "
188  "environment variable TEUCHOS_USER_COMMANDS");
189  }
190 
191 #if !defined(__GNUC__) || (__GNUC__ >= 10)
192  try {
193  const std::string executable = std::filesystem::canonical("/proc/self/exe");
194  if (!executable.empty()) {
195  registerCommand("ldd", "ldd " + executable, "ldd");
196  }
197  } catch (std::filesystem::filesystem_error &) {
198  // ignore
199  }
200 #endif
201 
202  // CPUs
203  registerCommand("lscpu");
204 
205  // temperature sensore
206  registerCommand("sensors");
207 
208  // OpenMP
210 
211  // OpenMPI
212  registerCommand("ompi_info");
214 
215  // MPICH
216  registerCommand("mpichinfo");
218 
219  // CRAY
221 
222  // modules
223  registerCommand("module", "module list", "module");
224 
225  // CUDA
226  registerCommand("nvidia-smi", "nvidia-smi --query", "nvidia-smi");
228 
229  // ROCm
230  registerCommand("rocm-smi", "rocm-smi --showallinfo", "rocm-smi");
231 
232  // SYCL
233  registerCommand("sycl-ls", "sycl-ls --verbose", "sycl-ls");
234 
235  // package namespaced environment variables
236  for (auto &prefix : {"TEUCHOS", "KOKKOS", "TPETRA", "STK"})
238 }
239 
240 std::map<std::string, std::string> collectSystemInformation() {
241 
242  std::map<std::string, std::string> data;
243 
244  const std::string DATA_NOT_AVAILABLE = "NOT AVAILABLE";
245 
246  auto &commands = getCommandsMap();
247  for (auto &command : commands) {
248  const bool isAvailable = commandIsAvailable(command.second.first);
249  if (isAvailable) {
250  data[command.first] = runCommandAndCaptureOutput(command.second.second);
251  } else {
252  data[command.first] = DATA_NOT_AVAILABLE;
253  }
254  }
255 
256  auto &environmentVariables = getEnvironmentVariablesSet();
257  for (auto &envVariable : environmentVariables) {
258  const char *varVal = std::getenv(envVariable.c_str());
259  if (varVal == nullptr)
260  data[envVariable] = "NOT SET";
261  else {
262  data[envVariable] =
263  Teuchos::getEnvironmentVariable<std::string>(envVariable, "");
264  }
265  }
266 
267  return data;
268 }
269 
270 } // namespace Teuchos::SystemInformation
RegistrationResult registerEnvironmentVariable(const std::string &variableName)
Register an environment variable that should be tracked.
void initializeCollection()
Track commonly used environment variables and commands.
#define TEUCHOS_TEST_FOR_EXCEPTION(throw_exception_test, Exception, msg)
Macro for throwing an exception with breakpointing to ease debugging.
A std::string utilities class for Teuchos.
RegistrationResult registerCommand(const std::string &commandLabel, const std::string &commandToRunAndCapture, const std::string &commandToCheckForExistence)
Register a command.
static Array< std::string > splitString(const std::string_view s, const char sep= ',')
Split an input std::string using a seperator char sep.
void registerAllPrefixedVariables(const std::string &prefix)
bool commandIsAvailable(const std::string &command)
Check whether a command is available on the system.
Collect information about the runtime environment.
Standard test and throw macros.
std::string runCommandAndCaptureOutput(const std::string &command)
Run a command and capture its output.
std::map< std::string, std::string > collectSystemInformation()
Collect information about the system.