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