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