The Gaudi Framework  v40r0 (475e45c1)
System.cpp
Go to the documentation of this file.
1 /***********************************************************************************\
2 * (c) Copyright 1998-2025 CERN for the benefit of the LHCb and ATLAS collaborations *
3 * *
4 * This software is distributed under the terms of the Apache version 2 licence, *
5 * copied verbatim in the file "LICENSE". *
6 * *
7 * In applying this licence, CERN does not waive the privileges and immunities *
8 * granted to it by virtue of its status as an Intergovernmental Organization *
9 * or submit itself to any jurisdiction. *
10 \***********************************************************************************/
11 #include <GaudiKernel/System.h>
12 #include <algorithm>
13 #include <array>
14 #include <cstdlib>
15 #include <cstring>
16 #include <ctime>
17 #include <iomanip>
18 #include <iostream>
19 #include <memory>
20 #include <regex>
21 #include <sstream>
22 #include <typeinfo>
23 
24 // Platform specific include(s):
25 #ifdef __linux__
26 # include "Platform/SystemLinux.h"
27 #elif defined( __APPLE__ )
28 # include "Platform/SystemMacOS.h"
29 #elif defined( _WIN32 )
30 # include "Platform/SystemWin32.h"
31 #endif
32 
33 #ifdef __x86_64
34 # define VCL_NAMESPACE Gaudi
35 # include <instrset_detect.cpp>
36 # undef VCL_NAMESPACE
37 #endif
38 
39 #ifdef _WIN32
40 # define strcasecmp _stricmp
41 # define strncasecmp _strnicmp
42 # define getpid _getpid
43 # define NOMSG
44 # define NOGDI
45 # include <process.h>
46 # include <windows.h>
47 # undef NOMSG
48 # undef NOGDI
49 static const std::array<const char*, 1> SHLIB_SUFFIXES = { ".dll" };
50 #else // UNIX...: first the EGCS stuff, then the OS dependent includes
51 # include <cstdio>
52 # include <cxxabi.h>
53 # include <errno.h>
54 # include <libgen.h>
55 # include <string.h>
56 # include <sys/times.h>
57 # include <unistd.h>
58 # if defined( __linux ) || defined( __APPLE__ )
59 # include <dlfcn.h>
60 # include <sys/utsname.h>
61 # include <unistd.h>
62 # elif __hpux
63 # include <dl.h>
64 struct HMODULE {
65  shl_descriptor dsc;
66  long numSym;
67  shl_symbol* sym;
68 };
69 # endif // HPUX or not...
70 
71 # ifdef __APPLE__
72 static const std::array<const char*, 2> SHLIB_SUFFIXES = { ".dylib", ".so" };
73 # else
74 static const std::array<const char*, 1> SHLIB_SUFFIXES = { ".so" };
75 # endif // __APPLE__
76 
77 #endif // Windows or Unix...
78 
79 static unsigned long doLoad( const std::string& name, System::ImageHandle* handle ) {
80 #ifdef _WIN32
81  void* mh = ::LoadLibrary( name.length() == 0 ? System::exeName().c_str() : name.c_str() );
82  *handle = mh;
83 #else
84  const char* path = name.c_str();
85 # if defined( __linux )
86  void* mh = ::dlopen( name.length() == 0 ? nullptr : path, RTLD_LAZY | RTLD_GLOBAL );
87  *handle = mh;
88 # elif defined( __APPLE__ )
89  void* mh = ::dlopen( name.length() == 0 ? nullptr : path, RTLD_LAZY | RTLD_GLOBAL );
90  *handle = mh;
91 # elif __hpux
92  shl_t mh = ::shl_load( name.length() == 0 ? 0 : path, BIND_IMMEDIATE | BIND_VERBOSE, 0 );
93  HMODULE* mod = new HMODULE;
94  if ( 0 != mh ) {
95  if ( 0 != ::shl_gethandle_r( mh, &mod->dsc ) ) {
96  std::cout << "System::loadDynamicLib>" << ::strerror( getLastError() ) << std::endl;
97  } else {
98  typedef void* ( *___all )();
99  ___all _alloc = (___all)malloc;
100  mod->numSym = ::shl_getsymbols( mod->dsc.handle, TYPE_PROCEDURE, EXPORT_SYMBOLS, malloc, &mod->sym );
101  *handle = mod;
102  }
103  }
104 # endif
105 #endif
106  if ( !*handle ) { return System::getLastError(); }
107  return 1;
108 }
109 
110 static unsigned long loadWithoutEnvironment( const std::string& name, System::ImageHandle* handle ) {
111 
112  // If the name is empty, don't do anything complicated.
113  if ( name.length() == 0 ) { return doLoad( name, handle ); }
114 
115  // Check if the specified name has a shared library suffix already. If it
116  // does, don't bother the name any more.
117  std::string dllName = name;
118  bool hasShlibSuffix = false;
119  for ( const char* suffix : SHLIB_SUFFIXES ) {
120  const size_t len = strlen( suffix );
121  if ( dllName.compare( dllName.length() - len, len, suffix ) == 0 ) {
122  hasShlibSuffix = true;
123  break;
124  }
125  }
126 
127  // If it doesn't have a shared library suffix on it, add the "default" shared
128  // library suffix to the name.
129  if ( !hasShlibSuffix ) { dllName += SHLIB_SUFFIXES[0]; }
130 
131  // Load the library.
132  return doLoad( dllName, handle );
133 }
134 
136 unsigned long System::loadDynamicLib( const std::string& name, ImageHandle* handle ) {
137  unsigned long res = 0;
138  // if name is empty, just load it
139  if ( name.length() == 0 ) {
140  res = loadWithoutEnvironment( name, handle );
141  } else {
142  // If the name is a logical name (environment variable), the try
143  // to load the corresponding library from there.
144  std::string imgName;
145  if ( getEnv( name, imgName ) ) {
146  res = loadWithoutEnvironment( imgName, handle );
147  } else {
148  // build the dll name
149  std::string dllName = name;
150 // Add a possible "lib" prefix to the name on unix platforms. But only if
151 // it's not an absolute path name.
152 #if defined( __linux ) || defined( __APPLE__ )
153  if ( ( dllName.find( '/' ) == std::string::npos ) && ( dllName.compare( 0, 3, "lib" ) != 0 ) ) {
154  dllName = "lib" + dllName;
155  }
156 #endif // unix
157  // Now try loading the library with all possible suffixes supported by the
158  // platform.
159  for ( const char* suffix : SHLIB_SUFFIXES ) {
160  // Add the suffix if necessary.
161  std::string libName = dllName;
162  const size_t len = strlen( suffix );
163  if ( dllName.compare( dllName.length() - len, len, suffix ) != 0 ) { libName += suffix; }
164  // Try to load the library.
165  res = loadWithoutEnvironment( libName, handle );
166  // If the load succeeded, stop here.
167  if ( res == 1 ) { break; }
168  }
169  }
170  if ( res != 1 ) {
171 #if defined( __linux ) || defined( __APPLE__ )
172  errno = 0xAFFEDEAD;
173 #endif
174  // std::cout << "System::loadDynamicLib>" << getLastErrorString() << std::endl;
175  }
176  }
177  return res;
178 }
179 
181 unsigned long System::unloadDynamicLib( ImageHandle handle ) {
182 #ifdef _WIN32
183  if ( !::FreeLibrary( (HINSTANCE)handle ) ) {
184 #elif defined( __linux ) || defined( __APPLE__ )
185  ::dlclose( handle );
186  if ( 0 ) {
187 #elif __hpux
188  // On HP we have to run finalization ourselves.....
189  Creator pFinalize = 0;
190  if ( getProcedureByName( handle, "_fini", &pFinalize ) ) { pFinalize(); }
191  HMODULE* mod = (HMODULE*)handle;
192  if ( 0 == ::shl_unload( mod->dsc.handle ) ) {
193  delete mod;
194  } else {
195 #else
196  if ( false ) {
197 #endif
198  return getLastError();
199  }
200  return 1;
201 }
202 
204 unsigned long System::getProcedureByName( ImageHandle handle, const std::string& name, EntryPoint* pFunction ) {
205 #ifdef _WIN32
206  *pFunction = ( EntryPoint )::GetProcAddress( (HINSTANCE)handle, name.data() );
207  if ( 0 == *pFunction ) { return System::getLastError(); }
208  return 1;
209 #elif defined( __linux )
210  *pFunction = reinterpret_cast<EntryPoint>( ::dlsym( handle, name.c_str() ) );
211  if ( !*pFunction ) {
212  errno = 0xAFFEDEAD;
213  // std::cout << "System::getProcedureByName>" << getLastErrorString() << std::endl;
214  return 0;
215  }
216  return 1;
217 #elif defined( __APPLE__ )
218  *pFunction = ( EntryPoint )::dlsym( handle, name.c_str() );
219  if ( !( *pFunction ) ) {
220  // Try with an underscore :
221  std::string sname = "_" + name;
222  *pFunction = ( EntryPoint )::dlsym( handle, sname.c_str() );
223  }
224  if ( 0 == *pFunction ) {
225  errno = 0xAFFEDEAD;
226  std::cout << "System::getProcedureByName>" << getLastErrorString() << std::endl;
227  // std::cout << "System::getProcedureByName> failure" << std::endl;
228  return 0;
229  }
230  return 1;
231 #elif __hpux
232  HMODULE* mod = (HMODULE*)handle;
233  if ( 0 != mod ) {
234  long ll1 = name.length();
235  for ( int i = 0; i < mod->numSym; i++ ) {
236  long ll2 = strlen( mod->sym[i].name );
237  if ( 0 != ::strncmp( mod->sym[i].name, name.c_str(), ( ll1 > ll2 ) ? ll1 : ll2 ) == 0 ) {
238  *pFunction = (EntryPoint)mod->sym[i].value;
239  return 1;
240  }
241  }
242  }
243  return 0;
244 #else
245  return 0;
246 #endif
247 }
248 
250 unsigned long System::getProcedureByName( ImageHandle handle, const std::string& name, Creator* pFunction ) {
251  return getProcedureByName( handle, name, (EntryPoint*)pFunction );
252 }
253 
255 unsigned long System::getLastError() {
256 #ifdef _WIN32
257  return ::GetLastError();
258 #else
259  // convert errno (int) to unsigned long
260  return static_cast<unsigned long>( static_cast<unsigned int>( errno ) );
261 #endif
262 }
263 
265 const std::string System::getLastErrorString() {
266  const std::string errString = getErrorString( getLastError() );
267  return errString;
268 }
269 
271 const std::string System::getErrorString( unsigned long error ) {
272  std::string errString = "";
273 #ifdef _WIN32
274  LPVOID lpMessageBuffer;
275  ::FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, error,
276  MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // The user default language
277  (LPTSTR)&lpMessageBuffer, 0, NULL );
278  errString = (const char*)lpMessageBuffer;
279  // Free the buffer allocated by the system
280  ::LocalFree( lpMessageBuffer );
281 #else
282  char* cerrString( nullptr );
283  // Remember: for linux dl* routines must be handled differently!
284  if ( error == 0xAFFEDEAD ) {
285  cerrString = ::dlerror();
286  if ( !cerrString ) cerrString = ::strerror( error );
287  if ( !cerrString ) {
288  cerrString = (char*)"Unknown error. No information found in strerror()!";
289  } else {
290  errString = std::string( cerrString );
291  }
292  errno = 0;
293  } else {
294  cerrString = ::strerror( error );
295  errString = std::string( cerrString );
296  }
297 #endif
298  return errString;
299 }
300 
301 const std::string System::typeinfoName( const std::type_info& tinfo ) { return typeinfoName( tinfo.name() ); }
302 
303 const std::string System::typeinfoName( const char* class_name ) { return Platform::typeinfoName( class_name ); }
304 
306 const std::string& System::hostName() {
307  static const std::string host = Platform::hostName();
308  return host;
309 }
310 
312 const std::string& System::osName() {
313  static const std::string osname = Platform::osName();
314  return osname;
315 }
316 
318 const std::string& System::osVersion() {
319  static const std::string osver = Platform::osVersion();
320  return osver;
321 }
322 
324 const std::string& System::machineType() {
325  static const std::string mach = Platform::machineType();
326  return mach;
327 }
328 
330 #ifdef __x86_64
331  using namespace Gaudi;
332  return instrset_detect();
333 #else
334  return -1;
335 #endif
336 }
337 
339 const std::string& System::accountName() {
340  static const std::string account = Platform::accountName();
341  return account;
342 }
343 
345 long System::numCmdLineArgs() { return cmdLineArgs().size(); }
346 
348 long System::argc() { return cmdLineArgs().size(); }
349 
351 const std::vector<std::string> System::cmdLineArgs() {
352  static const std::vector<std::string> args = Platform::cmdLineArgs();
353  return args;
354 }
355 
357 char** System::argv() {
358  auto helperFunc = []( const std::vector<std::string>& args ) -> std::vector<const char*> {
359  std::vector<const char*> result;
360  std::transform( args.begin(), args.end(), std::back_inserter( result ),
361  []( const std::string& s ) { return s.c_str(); } );
362  return result;
363  };
364  static const std::vector<const char*> args = helperFunc( cmdLineArgs() );
365  // We rely here on the fact that a vector's allocation table is contiguous
366  return (char**)&( args[0] );
367 }
368 
369 #ifdef WIN32
370 // disable warning
371 // C4996: 'getenv': This function or variable may be unsafe.
372 # pragma warning( disable : 4996 )
373 #endif
374 
376 std::string System::getEnv( const char* var ) {
377  char* env;
378  if ( ( env = getenv( var ) ) != nullptr ) {
379  return env;
380  } else {
381  return "UNKNOWN";
382  }
383 }
384 
386 bool System::getEnv( const char* var, std::string& value ) {
387  char* env;
388  if ( ( env = getenv( var ) ) != nullptr ) {
389  value = env;
390  return true;
391  } else {
392  return false;
393  }
394 }
395 
396 bool System::isEnvSet( const char* var ) { return getenv( var ) != nullptr; }
397 
399 #if defined( __APPLE__ )
400 // Needed for _NSGetEnviron(void)
401 # include <crt_externs.h>
402 #endif
403 std::vector<std::string> System::getEnv() {
404 #if defined( _WIN32 )
405 # define environ _environ
406 #elif defined( __APPLE__ )
407  static char** environ = *_NSGetEnviron();
408 #endif
409  std::vector<std::string> vars;
410  for ( int i = 0; environ[i] != nullptr; ++i ) { vars.push_back( environ[i] ); }
411  return vars;
412 }
413 
414 // -----------------------------------------------------------------------------
415 // backtrace utilities
416 // -----------------------------------------------------------------------------
417 #ifdef __linux
418 # include <execinfo.h>
419 #endif
420 
421 int System::backTrace( [[maybe_unused]] void** addresses, [[maybe_unused]] const int depth ) {
422 
423 #ifdef __linux
424 
425  int count = backtrace( addresses, depth );
426  return count > 0 ? count : 0;
427 
428 #else // windows and osx parts not implemented
429  return 0;
430 #endif
431 }
432 
433 bool System::backTrace( std::string& btrace, const int depth, const int offset ) {
434  try {
435  // Always hide the first two levels of the stack trace (that's us)
436  const size_t totalOffset = offset + 2;
437  const size_t totalDepth = depth + totalOffset;
438 
439  std::string fnc, lib;
440 
441  std::vector<void*> addresses( totalDepth, nullptr );
442  const size_t count = System::backTrace( addresses.data(), totalDepth );
443  for ( size_t i = totalOffset; i < count; ++i ) {
444  void* addr = nullptr;
445 
446  if ( System::getStackLevel( addresses[i], addr, fnc, lib ) ) {
447  std::ostringstream ost;
448  ost << "#" << std::setw( 3 ) << std::setiosflags( std::ios::left ) << i - totalOffset + 1;
449  ost << std::hex << addr << std::dec << " " << fnc << " [" << lib << "]" << std::endl;
450  btrace += ost.str();
451  }
452  }
453  return true;
454  } catch ( const std::bad_alloc& e ) { return false; }
455 }
456 
457 bool System::getStackLevel( [[maybe_unused]] void* addresses, [[maybe_unused]] void*& addr,
458  [[maybe_unused]] std::string& fnc, [[maybe_unused]] std::string& lib ) {
459 
460 #ifdef __linux
461 
462  Dl_info info;
463 
464  if ( dladdr( addresses, &info ) && info.dli_fname && info.dli_fname[0] != '\0' ) {
465  const char* symbol = info.dli_sname && info.dli_sname[0] != '\0' ? info.dli_sname : nullptr;
466 
467  lib = info.dli_fname;
468  addr = info.dli_saddr;
469 
470  if ( symbol ) {
471  int stat = -1;
472  auto dmg =
473  std::unique_ptr<char, decltype( free )*>( abi::__cxa_demangle( symbol, nullptr, nullptr, &stat ), std::free );
474  fnc = ( stat == 0 ) ? dmg.get() : symbol;
475  } else {
476  fnc = "local";
477  }
478  return true;
479  } else {
480  return false;
481  }
482 
483 #else // not implemented for windows and osx
484  return false;
485 #endif
486 }
487 
489 int System::setEnv( const std::string& name, const std::string& value, int overwrite ) {
490 #ifndef WIN32
491  // UNIX version
492  return value.empty() ?
493  // remove if set to nothing (and return success)
494  ::unsetenv( name.c_str() ),
495  0 :
496  // set the value
497  ::setenv( name.c_str(), value.c_str(), overwrite );
498 #else
499  // Windows version
500  if ( value.empty() ) {
501  // equivalent to unsetenv
502  return ::_putenv( ( name + "=" ).c_str() );
503  } else {
504  if ( !getenv( name.c_str() ) || overwrite ) {
505  // set if not yet present or overwrite is set (force)
506  return ::_putenv( ( name + "=" + value ).c_str() );
507  }
508  }
509  return 0; // if we get here, we are trying to set a variable already set, but
510  // not to overwrite.
511  // It is considered a success on Linux (man P setenv)
512 #endif
513 }
System::getErrorString
GAUDI_API const std::string getErrorString(unsigned long error)
Retrieve error code as string for a given error.
Definition: System.cpp:271
instrset_detect.cpp
MSG::hex
MsgStream & hex(MsgStream &log)
Definition: MsgStream.h:277
System::loadDynamicLib
GAUDI_API unsigned long loadDynamicLib(const std::string &name, ImageHandle *handle)
Load dynamic link library.
Definition: System.cpp:136
compareOutputFiles.sname
sname
Definition: compareOutputFiles.py:483
System::argc
GAUDI_API long argc()
Number of arguments passed to the commandline (==numCmdLineArgs()); just to match argv call....
Definition: System.cpp:348
AtlasMCRecoFullPrecedenceDump.path
path
Definition: AtlasMCRecoFullPrecedenceDump.py:49
System::getLastError
GAUDI_API unsigned long getLastError()
Get last system known error.
Definition: System.cpp:255
System::EntryPoint
unsigned long(* EntryPoint)(const unsigned long iid, void **ppvObject)
Definition of the "generic" DLL entry point function.
Definition: System.h:41
System.h
gaudirun.s
string s
Definition: gaudirun.py:346
System::getEnv
GAUDI_API std::string getEnv(const char *var)
get a particular environment variable (returning "UNKNOWN" if not set)
Definition: System.cpp:376
System::setEnv
GAUDI_API int setEnv(const std::string &name, const std::string &value, int overwrite=1)
Set an environment variables.
Definition: System.cpp:489
System::instructionsetLevel
GAUDI_API int instructionsetLevel()
Instruction Set "Level".
Definition: System.cpp:329
System::ImageHandle
void * ImageHandle
Definition of an image handle.
Definition: ModuleInfo.h:25
System::getProcedureByName
GAUDI_API unsigned long getProcedureByName(ImageHandle handle, const std::string &name, EntryPoint *pFunction)
Get a specific function defined in the DLL.
Definition: System.cpp:204
System::typeinfoName
GAUDI_API const std::string typeinfoName(const std::type_info &)
Get platform independent information about the class type.
Definition: System.cpp:301
System::getStackLevel
GAUDI_API bool getStackLevel(void *addresses, void *&addr, std::string &fnc, std::string &lib)
System::backTrace
GAUDI_API int backTrace(void **addresses, const int depth)
System::osName
GAUDI_API const std::string & osName()
OS name.
Definition: System.cpp:312
SystemWin32.h
System::exeName
GAUDI_API const std::string & exeName()
Name of the executable file running.
Definition: ModuleInfo.cpp:185
System::accountName
GAUDI_API const std::string & accountName()
User login name.
Definition: System.cpp:339
Gaudi.CommonGaudiConfigurables.mod
mod
Definition: CommonGaudiConfigurables.py:39
SystemLinux.h
SystemMacOS.h
MSG::dec
MsgStream & dec(MsgStream &log)
Definition: MsgStream.h:273
System::numCmdLineArgs
GAUDI_API long numCmdLineArgs()
Number of arguments passed to the commandline.
Definition: System.cpp:345
instrset_detect
int instrset_detect(void)
Definition: instrset_detect.cpp:63
System::isEnvSet
GAUDI_API bool isEnvSet(const char *var)
Check if an environment variable is set or not.
Definition: System.cpp:396
Gaudi
This file provides a Grammar for the type Gaudi::Accumulators::Axis It allows to use that type from p...
Definition: __init__.py:1
System::Creator
void *(* Creator)()
Definition of the "generic" DLL entry point function.
Definition: System.h:43
System::unloadDynamicLib
GAUDI_API unsigned long unloadDynamicLib(ImageHandle handle)
unload dynamic link library
Definition: System.cpp:181
ConditionsStallTest.name
name
Definition: ConditionsStallTest.py:77
gaudirun.args
args
Definition: gaudirun.py:336
System::cmdLineArgs
GAUDI_API const std::vector< std::string > cmdLineArgs()
Command line arguments including executable name as arg[0] as vector of strings.
Definition: System.cpp:351
System::machineType
GAUDI_API const std::string & machineType()
Machine type.
Definition: System.cpp:324
System::hostName
GAUDI_API const std::string & hostName()
Host name.
Definition: System.cpp:306
System::getLastErrorString
GAUDI_API const std::string getLastErrorString()
Get last system error as string.
Definition: System.cpp:265
System::osVersion
GAUDI_API const std::string & osVersion()
OS version.
Definition: System.cpp:318
System::argv
GAUDI_API char ** argv()
char** command line arguments including executable name as arg[0]; You may not modify them!
Definition: System.cpp:357