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