MaCh3  2.4.2
Reference Guide
Monitor.cpp
Go to the documentation of this file.
1 #include "Manager/Monitor.h"
2 #include <unistd.h>
3 
4 //Only if GPU is enabled
5 #ifdef MaCh3_CUDA
6 #include "Manager/gpuUtils.cuh"
7 #endif
8 
9 namespace MaCh3Utils {
10 
11 // *************************
12 void MaCh3Welcome() {
13 // *************************
14  // KS: Just make sure we only call it once
15  static bool MaCh3WelcomeInitialised = false;
16 
17  if(MaCh3WelcomeInitialised) return;
18 
19  std::string MaCh3_VERSION = GetMaCh3Version();
20 
21  MACH3LOG_INFO("##################################");
22  MACH3LOG_INFO("Welcome to: ");
23  MACH3LOG_INFO(" __ __ _____ _ ____ ");
24  MACH3LOG_INFO(" | \\/ | / ____| | |___ \\ ");
25  MACH3LOG_INFO(" | \\ / | __ _| | | |__ __) |");
26  MACH3LOG_INFO(" | |\\/| |/ _` | | | '_ \\ |__ < ");
27  MACH3LOG_INFO(" | | | | (_| | |____| | | |___) |");
28  MACH3LOG_INFO(" |_| |_|\\__,_|\\_____|_| |_|____/ ");
29  MACH3LOG_INFO("Version: {}", MaCh3_VERSION);
30  MACH3LOG_INFO("##################################");
31 
32  GetCPUInfo();
33 
34  GetGPUInfo();
35 
36  #ifdef DEBUG
37  GetOSInfo();
38  GetDiskUsage();
39  #endif
40 
41  MaCh3WelcomeInitialised = true;
42 }
43 
44 // ************************
45 // KS: Get version of MaCh3
46 std::string GetMaCh3Version() {
47 // ************************
48  //KS: Find MaCh3 version based on header file. There could be better way to just include version.h but as long as we don't have to hardcode version I am content
49  std::string MaCh3_VERSION = "";
50 
51  if(std::getenv("MaCh3_ROOT") == nullptr){
52  throw MaCh3Exception(__FILE__, __LINE__, "Error: you haven't sourced setup.MaCh3.sh in core!");
53  }
54 
55  std::string file = std::string(std::getenv("MaCh3_ROOT")) + "/version.h";
56  // Open the version.h file
57  std::ifstream versionFile(file);
58 
59  // Check if the file is opened successfully
60  if (!versionFile.is_open()) {
61  MACH3LOG_ERROR("Error: Couldn't open version.h {}", file);
62  throw MaCh3Exception(__FILE__, __LINE__);
63  }
64 
65  std::string line;
66  const std::string searchKey = "MaCh3_VERSION=";
67 
68  // Read each line from the file
69  while (std::getline(versionFile, line)) {
70  // Search for the line containing MaCh3_VERSION
71  auto pos = line.find(searchKey);
72  if (pos != std::string::npos) {
73  // Extract the version string
74  MaCh3_VERSION = line.substr(pos + searchKey.length());
75  MaCh3_VERSION.erase(0, MaCh3_VERSION.find_first_not_of("\"")); // Remove leading quote
76  MaCh3_VERSION.erase(MaCh3_VERSION.find_last_not_of("\";") + 1); // Remove trailing quote and semicolon
77  break; // Stop searching once found
78  }
79  }
80  // Close the file
81  versionFile.close();
82 
83  return MaCh3_VERSION;
84 }
85 
86 // ************************
87 // KS: Find out more about operational system
88 void GetOSInfo() {
89 // ************************
90  MACH3LOG_INFO("Operating System Information:");
91 
92  // Distribution and version
93  MACH3LOG_INFO("Distribution: {}", TerminalToString("lsb_release -d | awk -F':' '{print $2}'"));
94  MACH3LOG_INFO("Kernel Version: {}", TerminalToString("uname -r"));
95 }
96 
97 // ************************
98 //KS: Simple function retrieving CPU info
99 void GetCPUInfo() {
100 // ************************
101  //KS: Use -m 1 to limit to only one grep because in one computing node there is a lot of CPU which are the same
102  MACH3LOG_INFO("Using following CPU:");
103 
104  MACH3LOG_INFO("{}", TerminalToString("cat /proc/cpuinfo | grep -m 1 name"));
105  MACH3LOG_INFO("{}", TerminalToString("cat /proc/cpuinfo | grep -m 1 MHz"));
106  //KS: Below code is convoluted because I mostly work on English based Linux but sometimes on Polish based Linux, this ensures it works on both. We can add support for other languages if needed
107  MACH3LOG_INFO("{}", TerminalToString("lscpu | grep -i Archit"));
108  MACH3LOG_INFO("{}", TerminalToString("lscpu | grep -m 1 -E 'L1d |L1d:'"));
109  MACH3LOG_INFO("{}", TerminalToString("lscpu | grep -m 1 -E 'L1i |L1i:'"));
110  MACH3LOG_INFO("{}", TerminalToString("lscpu | grep -m 1 -E 'L2 |L2:'"));
111  MACH3LOG_INFO("{}", TerminalToString("lscpu | grep -m 1 -E 'L3 |L3:'"));
112  MACH3LOG_INFO("{}", TerminalToString("lscpu | grep -m 1 -E 'Thread.* per core:|Wątków na rdzeń:'"));
113  MACH3LOG_INFO("{}", TerminalToString("lscpu | grep -m 1 -E '^CPU(:|\\(s\\)):?\\s+[0-9]+'"));
114  MACH3LOG_INFO("With available threads {}", M3::GetNThreads());
115 
116  NThreadsSanity();
117  //KS: /proc/cpuinfo and lscpu holds much more info I have limited it but one can expand it if needed
118 }
119 
120 
121 // ************************
123 // ************************
124  //KS: If OMP_NUM_THREADS is exported, assume user did this on purpose
125  if (std::getenv("OMP_NUM_THREADS") != nullptr) return;
126 
127  const int nThreads = M3::GetNThreads();
128  constexpr int MaxAllowedThreads = 16;
129  constexpr int RecommendedThreads = 8;
130 
131  if (nThreads > MaxAllowedThreads) {
132  MACH3LOG_CRITICAL("You specified more than {} threads ({})", MaxAllowedThreads, nThreads);
133  MACH3LOG_CRITICAL("With so many threads code will be slower, please use:");
134  MACH3LOG_CRITICAL("export OMP_NUM_THREADS={}", RecommendedThreads);
135  MACH3LOG_CRITICAL("To use different number of threads");
136  throw MaCh3Exception(__FILE__, __LINE__, "Too many threads");
137  }
138 }
139 
140 
141 // ************************
142 //KS: Simple function retrieving GPU info
143 void GetGPUInfo(){
144 // ************************
145 #ifdef MaCh3_CUDA
146  int nDevices;
147  cudaGetDeviceCount(&nDevices);
148  if (nDevices == 0) {
149  MACH3LOG_CRITICAL("MaCh3 compiled with CUDA support (for GPU acceleration) but could not find GPU");
150  MACH3LOG_CRITICAL("Either allocate GPU for your job or recompile MaCh3 without GPU support");
151  throw MaCh3Exception(__FILE__, __LINE__);
152  }
153 
154  MACH3LOG_INFO("Using following GPU:");
155  // Print GPU name
156  MACH3LOG_INFO("GPU Name: {}", TerminalToString("nvidia-smi --query-gpu=name --format=csv,noheader"));
157  // Print number of GPUs
158  MACH3LOG_INFO("Number of GPUs: {}", TerminalToString("nvidia-smi --query-gpu=count --format=csv,noheader"));
159  // Print total VRAM
160  MACH3LOG_INFO("Total VRAM: {} MB", TerminalToString("nvidia-smi --query-gpu=memory.total --format=csv,noheader,nounits"));
161  // Print Driver Version
162  MACH3LOG_INFO("Driver Version: {}", TerminalToString("nvidia-smi --query-gpu=driver_version --format=csv,noheader"));
163  // Print N GPU thread
164  MACH3LOG_INFO("Currently used GPU has: {} threads", GetNumGPUThreads());
165  // Print L2 cache
166  MACH3LOG_INFO("Currently used GPU has: {} KB L2 cache", GetL2CacheSize() / 1024);
167  // Shared memory info
168  MACH3LOG_INFO("Max shared memory per block: {} KB", GetSharedMemoryPerBlock() / 1024);
169  // Print 1D texture size
170  MACH3LOG_INFO("Max 1D texture size: {}", GetMaxTexture1DSize());
171 #endif
172 }
173 
174 // ************************
175 // KS: Find out about Disk usage
176 void GetDiskUsage() {
177 // ************************
178  MACH3LOG_INFO("Disk Usage:");
179 
180  // Get disk usage
181  MACH3LOG_INFO("{}", TerminalToString("df -h --total | grep total"));
182 }
183 
184 // ************************
185 // KS: Convoluted code to grab output from terminal to string
186 std::string TerminalToString(std::string cmd) {
187 // ************************
188  std::array<char, 128> buffer;
189  std::string result;
190 
191  struct PCloseDeleter {
192  void operator()(FILE* f) const {
193  if (f) pclose(f);
194  }
195  };
196  std::unique_ptr<FILE, PCloseDeleter> pipe(popen(cmd.c_str(), "r"));
197 
198  if (!pipe) {
199  throw MaCh3Exception(__FILE__, __LINE__, "popen() failed!");
200  }
201  while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
202  result += buffer.data();
203  }
204  // Remove trailing newline characters
205  result.erase(std::remove(result.begin(), result.end(), '\n'), result.end());
206  return result;
207 }
208 
209 // ************************
210 //KS: Simple to retrieve speed of get entry inspired by
211 void EstimateDataTransferRate(TChain* chain, const Long64_t entry){
212 // ************************
213  TStopwatch timer;
214 
215  timer.Start();
216  Int_t bytesProcessed{ chain->GetEntry(entry) };
217 
218  timer.Stop();
219 
220  Double_t timeInSeconds = timer.RealTime();
221  Double_t dataRateMBps = (double(bytesProcessed) / (1024.0 * 1024.0)) / timeInSeconds;
222 
223  MACH3LOG_INFO("Data transfer: {} B, rate: {:.2f} MB/s", bytesProcessed, dataRateMBps);
224 }
225 
226 // ************************
227 //KS: Simply print progress bar
228 void PrintProgressBar(const Long64_t Done, const Long64_t All) {
229 // ************************
230  double progress = double(Done)/double(All);
231  const int barWidth = 20;
232  std::ostringstream progressBar;
233 
234  progressBar << "[";
235  int pos = int(barWidth * progress);
236  for (int i = 0; i < barWidth; ++i) {
237  if (i < pos)
238  progressBar << "=";
239  else if (i == pos)
240  progressBar << ">";
241  else
242  progressBar << " ";
243  }
244 
245  progressBar << "] " << std::setw(3) << Done <<"/"<< All<<" ("<<static_cast<int>(progress * 100.0)<<"%)\r";
246  MACH3LOG_INFO("{}", progressBar.str());
247 }
248 
249 // ***************************************************************************
250 //CW: Get memory, which is probably silly
251 int getValue(const std::string& Type) { //Note: this value is in KB!
252 // ***************************************************************************
253  std::ifstream file("/proc/self/status");
254  int result = -1;
255  std::string line;
256 
257  if (Type == "VmSize")
258  {
259  while (std::getline(file, line))
260  {
261  if (line.compare(0, 7, "VmSize:") == 0)
262  {
263  result = parseLine(line.substr(7));
264  break;
265  }
266  }
267  }
268  else if (Type == "VmRSS")
269  {
270  while (std::getline(file, line))
271  {
272  if (line.compare(0, 6, "VmRSS:") == 0)
273  {
274  result = parseLine(line.substr(6));
275  break;
276  }
277  }
278  }
279  else if (Type == "MemTotal")
280  {
281  std::ifstream meminfo("/proc/meminfo");
282  while (std::getline(meminfo, line))
283  {
284  if (line.find("MemTotal:") != std::string::npos) {
285  result = parseLine(line.substr(9));
286  break;
287  }
288  }
289  }
290  else
291  {
292  MACH3LOG_ERROR("Not supported getValue: {}", Type);
293  throw MaCh3Exception(__FILE__, __LINE__);
294  }
295  return result;
296 }
297 
298 // ***************************************************************************
299 //CW: Get memory, which is probably silly
300 int parseLine(const std::string& line){
301 // ***************************************************************************
302  std::istringstream iss(line);
303  int value;
304  iss >> value;
305  return value;
306 }
307 
308 // ***************************************************************************
309 //KS: Print Yaml config using logger
310 void PrintConfig(const YAML::Node& node){
311 // ***************************************************************************
312  std::stringstream ss;
313  ss << node;
314  std::string yamlString = ss.str();
315 
316  std::istringstream iss(yamlString);
317  std::string line;
318  while (std::getline(iss, line)) {
319  MACH3LOG_INFO("{}", line);
320  }
321 }
322 
323 // ***************************************************************************
324 //KS: Print content of TFile using logger
325 void Print(const TTree* tree) {
326 // ***************************************************************************
327  if (!tree) return;
328 
329  // Create a temporary file to capture stdout
330  FILE* tmpFile = tmpfile();
331  if (!tmpFile) return;
332 
333  // Save old stdout
334  int oldStdout = dup(fileno(stdout));
335  // Redirect stdout to tmpFile
336  dup2(fileno(tmpFile), fileno(stdout));
337 
338  tree->Print(); // ROOT writes to stdout
339 
340  fflush(stdout);
341  // Restore old stdout
342  dup2(oldStdout, fileno(stdout));
343  close(oldStdout);
344 
345  // Read tmpFile content
346  fseek(tmpFile, 0, SEEK_SET);
347  char buffer[1024];
348  while (fgets(buffer, sizeof(buffer), tmpFile)) {
349  std::string line(buffer);
350  if (!line.empty() && line.back() == '\n') line.pop_back();
351  MACH3LOG_INFO("{}", line);
352  }
353 
354  fclose(tmpFile);
355 }
356 
357 // ***************************************************************************
358 //KS: Almost all MaCh3 executables have the same usage, prepare simple printer
359 void MaCh3Usage(int argc, char **argv) {
360 // ***************************************************************************
361  if (argc != 2) {
362  MACH3LOG_ERROR("Wrong usage of MaCh3 executable!");
363  MACH3LOG_ERROR("Syntax is $: {} config.yaml", argv[0]);
364  MACH3LOG_ERROR("Where config.yaml is a valid config file, compatible with the manager class (manager/manager.cpp/h)");
365  throw MaCh3Exception(__FILE__, __LINE__);
366  }
367 }
368 } //end namespace
369 
370 namespace M3 {
371 // ***************************************************************************
372 int GetNThreads() {
373 // ***************************************************************************
374  #ifdef MULTITHREAD
375  return omp_get_max_threads();
376  #else
377  return 1;
378  #endif
379 }
380 
381 // ***************************************************************************
382 void AddPath(std::string& FilePath) {
383 // ***************************************************************************
384  //KS:Most inputs are in ${MACH3}/inputs/blarb.root
385  if (std::getenv("MACH3") == nullptr) {
386  MACH3LOG_ERROR("MACH3 is not defined");
387  MACH3LOG_ERROR("Please source your setup script");
388  MACH3LOG_ERROR("Read more: https://github.com/mach3-software/MaCh3/wiki/0.-FAQ#error-need-mach3-environment-variable");
389  throw MaCh3Exception(__FILE__, __LINE__);
390  }
391 
392  std::string MaCh3Path = std::string(std::getenv("MACH3")) + "/";
393  // Check if FilePath does NOT start with MaCh3Path
394  if (FilePath.find(MaCh3Path) != 0) {
395  FilePath.insert(0, MaCh3Path);
396  }
397 }
398 } //end namespace
399 
#define MACH3LOG_CRITICAL
Definition: MaCh3Logger.h:38
#define MACH3LOG_ERROR
Definition: MaCh3Logger.h:37
#define MACH3LOG_INFO
Definition: MaCh3Logger.h:35
System and monitoring utilities for printing system information and status updates.
Custom exception class used throughout MaCh3.
size_t GetMaxTexture1DSize(const int device)
KS: Get the maximum size for 1D textures on the specified GPU device.
Definition: gpuUtils.cu:134
int GetNumGPUThreads(const int Device)
KS: Get number of GPU threads for currently used GPU.
Definition: gpuUtils.cu:102
size_t GetL2CacheSize(const int device)
KS: Get L2 cache size (in bytes) for the specified GPU device.
Definition: gpuUtils.cu:122
size_t GetSharedMemoryPerBlock(const int device)
KS: Returns the maximum shared memory per block for a given GPU device.
Definition: gpuUtils.cu:146
Common CUDA utilities and definitions for shared GPU functionality.
Main namespace for MaCh3 software.
int GetNThreads()
number of threads which we need for example for TRandom3
Definition: Monitor.cpp:372
void AddPath(std::string &FilePath)
Prepends the MACH3 environment path to FilePath if it is not already present.
Definition: Monitor.cpp:382
void GetOSInfo()
KS: Find out more about operational system.
Definition: Monitor.cpp:88
std::string TerminalToString(std::string cmd)
KS: Convoluted code to grab output from terminal to string.
Definition: Monitor.cpp:186
void PrintProgressBar(const Long64_t Done, const Long64_t All)
KS: Simply print progress bar.
Definition: Monitor.cpp:228
std::string GetMaCh3Version()
KS: Get version of MaCh3.
Definition: Monitor.cpp:46
void Print(const TTree *tree)
Definition: Monitor.cpp:325
void GetDiskUsage()
KS: Find out about Disk usage.
Definition: Monitor.cpp:176
void NThreadsSanity()
KS: Check if user is not using huge number of threads and throw error.
Definition: Monitor.cpp:122
void GetCPUInfo()
KS: Check what CPU you are using.
Definition: Monitor.cpp:99
void GetGPUInfo()
KS: Check what GPU you are using.
Definition: Monitor.cpp:143
int getValue(const std::string &Type)
CW: Get info like RAM.
Definition: Monitor.cpp:251
void PrintConfig(const YAML::Node &node)
KS: Print Yaml config using logger.
Definition: Monitor.cpp:310
void MaCh3Usage(int argc, char **argv)
KS: Almost all MaCh3 executables have the same usage, prepare simple printer.
Definition: Monitor.cpp:359
void MaCh3Welcome()
KS: Prints welcome message with MaCh3 logo.
Definition: Monitor.cpp:12
int parseLine(const std::string &line)
CW: Get memory, which is probably silly.
Definition: Monitor.cpp:300
void EstimateDataTransferRate(TChain *chain, const Long64_t entry)
KS: Check what CPU you are using.
Definition: Monitor.cpp:211