Elements  6.2
A C++ base framework for the Euclid Software.
System.cpp
Go to the documentation of this file.
1 
20 #include "ElementsKernel/System.h"
21 
22 #include <cxxabi.h>
23 #include <dlfcn.h> // for Dl_info, dladdr, dlclose, etc
24 #include <execinfo.h> // for backtrace
25 #include <sys/utsname.h>
26 #include <unistd.h> // for environ
27 
28 #include <array> // for array
29 #include <cstdlib> // for free, getenv, malloc, etc
30 #include <iomanip>
31 #include <iostream>
32 #include <new> // for new
33 #include <sstream>
34 #include <string> // for string
35 #include <typeinfo> // for type_info
36 #include <vector> // for vector
37 
38 #include <cerrno> // for errno
39 #include <climits> // for HOST_NAME_MAX
40 #include <cstddef> // for size_t
41 #include <cstring> // for strnlen, strerror
42 
44 #include "ElementsKernel/ModuleInfo.h" // for ImageHandle
45 #include "ElementsKernel/Unused.h" // for ELEMENTS_UNUSED
46 
47 using std::size_t;
48 using std::string;
49 using std::vector;
50 
51 namespace Elements {
52 namespace System {
53 
54 // --------------------------------------------------------------------------------------
55 // Private functions
56 // --------------------------------------------------------------------------------------
57 
58 namespace {
59 
60 unsigned long doLoad(const string& name, ImageHandle* handle) {
61  void* mh = ::dlopen(name.length() == 0 ? 0 : name.c_str(), RTLD_LAZY | RTLD_GLOBAL);
62  *handle = mh;
63  if (0 == *handle) {
64  return getLastError();
65  }
66  return 1;
67 }
68 
69 unsigned long loadWithoutEnvironment(const string& name, ImageHandle* handle) {
70 
71  string dll_name = name;
72  size_t dll_len = dll_name.size();
73  size_t suf_len = SHLIB_SUFFIX.size();
74 
75  // Add the suffix at the end of the library name only if necessary
76  if (dll_len >= suf_len && dll_name.compare(dll_len - suf_len, suf_len, SHLIB_SUFFIX) != 0) {
77  dll_name += SHLIB_SUFFIX;
78  }
79 
80  // Load the library
81  return doLoad(dll_name, handle);
82 }
83 
84 } // anonymous namespace
85 // --------------------------------------------------------------------------------------
86 
88 unsigned long loadDynamicLib(const string& name, ImageHandle* handle) {
89  unsigned long res;
90  // if name is empty, just load it
91  if (name.length() == 0) {
92  res = loadWithoutEnvironment(name, handle);
93  } else {
94  // If the name is a logical name (environment variable), the try
95  // to load the corresponding library from there.
96  string imgName;
97  if (getEnv(name, imgName)) {
98  res = loadWithoutEnvironment(imgName, handle);
99  } else {
100  // build the dll name
101  string dllName = name;
102  dllName = "lib" + dllName;
103  dllName += SHLIB_SUFFIX;
104  // try to locate the dll using the standard PATH
105  res = loadWithoutEnvironment(dllName, handle);
106  }
107  if (res != 1) {
108  errno = static_cast<int>(0xAFFEDEAD);
109  }
110  }
111  return res;
112 }
113 
115 unsigned long unloadDynamicLib(ImageHandle handle) {
116  ::dlclose(handle);
117  if (0) {
118  return getLastError();
119  }
120  return 1;
121 }
122 
124 unsigned long getProcedureByName(ImageHandle handle, const string& name, EntryPoint* pFunction) {
125 #if defined(__linux__)
126  *pFunction = FuncPtrCast<EntryPoint>(::dlsym(handle, name.c_str()));
127  if (0 == *pFunction) {
128  errno = static_cast<int>(0xAFFEDEAD);
129  return 0;
130  }
131 #elif defined(__APPLE__)
132  *pFunction = (EntryPoint)::dlsym(handle, name.c_str());
133  if (not *pFunction) {
134  // Try with an underscore :
135  string sname = "_" + name;
136  *pFunction = (EntryPoint)::dlsym(handle, sname.c_str());
137  }
138  if (0 == *pFunction) {
139  errno = static_cast<int>(0xAFFEDEAD);
140  std::cout << "Elements::System::getProcedureByName>" << getLastErrorString() << std::endl;
141  return 0;
142  }
143 #endif
144  return 1;
145 }
146 
148 unsigned long getProcedureByName(ImageHandle handle, const string& name, Creator* pFunction) {
149  return getProcedureByName(handle, name, reinterpret_cast<EntryPoint*>(pFunction));
150 }
151 
153 unsigned long getLastError() {
154  // convert errno (int) to unsigned long
155  return static_cast<unsigned long>(static_cast<unsigned int>(errno));
156 }
157 
159 const string getLastErrorString() {
160  const string errString = getErrorString(getLastError());
161  return errString;
162 }
163 
165 const string getErrorString(unsigned long error) {
166  string errString = "";
167  char* cerrString(0);
168  // Remember: for linux dl* routines must be handled differently!
169  if (error == 0xAFFEDEAD) {
170  cerrString = reinterpret_cast<char*>(::dlerror());
171  if (0 == cerrString) {
172  cerrString = std::strerror(static_cast<int>(error));
173  }
174  if (0 == cerrString) {
175  cerrString = const_cast<char*>("Unknown error. No information found in strerror()!");
176  }
177  errString = string(cerrString);
178  errno = 0;
179  } else {
180  cerrString = std::strerror(static_cast<int>(error));
181  errString = string(cerrString);
182  }
183  return errString;
184 }
185 
186 const string typeinfoName(const std::type_info& tinfo) {
187  return typeinfoName(tinfo.name());
188 }
189 
190 const string typeinfoName(const char* class_name) {
191  string result;
192  if (strnlen(class_name, 1024) == 1) {
193  // See http://www.realitydiluted.com/mirrors/reality.sgi.com/dehnert_engr/cxx/abi.pdf
194  // for details
195  switch (class_name[0]) {
196  case 'v':
197  result = "void";
198  break;
199  case 'w':
200  result = "wchar_t";
201  break;
202  case 'b':
203  result = "bool";
204  break;
205  case 'c':
206  result = "char";
207  break;
208  case 'a':
209  result = "signed char";
210  break;
211  case 'h':
212  result = "unsigned char";
213  break;
214  case 's':
215  result = "short";
216  break;
217  case 't':
218  result = "unsigned short";
219  break;
220  case 'i':
221  result = "int";
222  break;
223  case 'j':
224  result = "unsigned int";
225  break;
226  case 'l':
227  result = "long";
228  break;
229  case 'm':
230  result = "unsigned long";
231  break;
232  case 'x':
233  result = "long long";
234  break;
235  case 'y':
236  result = "unsigned long long";
237  break;
238  case 'n':
239  result = "__int128";
240  break;
241  case 'o':
242  result = "unsigned __int128";
243  break;
244  case 'f':
245  result = "float";
246  break;
247  case 'd':
248  result = "double";
249  break;
250  case 'e':
251  result = "long double";
252  break;
253  case 'g':
254  result = "__float128";
255  break;
256  case 'z':
257  result = "ellipsis";
258  break;
259  }
260  } else {
261  int status;
262  std::unique_ptr<char, decltype(free)*> realname(abi::__cxa_demangle(class_name, 0, 0, &status), free);
263  if (realname == nullptr) {
264  return class_name;
265  }
266  result = realname.get();
268  string::size_type pos = result.find(", ");
269  while (string::npos != pos) {
270  result.replace(pos, static_cast<string::size_type>(2), ",");
271  pos = result.find(", ");
272  }
273  }
274  return result;
275 }
276 
278 const string& hostName() {
279  static string host{};
280  if (host.empty()) {
282  ::gethostname(buffer.data(), HOST_NAME_MAX);
283  host = buffer.data();
284  }
285  return host;
286 }
287 
289 const string& osName() {
290  static string osname = "";
291  struct utsname ut;
292  if (::uname(&ut) == 0) {
293  osname = ut.sysname;
294  } else {
295  osname = "UNKNOWN";
296  }
297  return osname;
298 }
299 
301 const string& osVersion() {
302  static string osver = "UNKNOWN";
303  struct utsname ut;
304 
305  if (uname(&ut) == 0) {
306  osver = ut.release;
307  }
308 
309  return osver;
310 }
311 
313 const string& machineType() {
314  static string mach = "UNKNOWN";
315  struct utsname ut;
316 
317  if (uname(&ut) == 0) {
318  mach = ut.machine;
319  }
320 
321  return mach;
322 }
323 
324 string getEnv(const string& var) {
325 
326  string env_str{};
327 
328  getEnv(var, env_str);
329 
330  return env_str;
331 }
332 
334 bool getEnv(const string& var, string& value) {
335  bool found = false;
336  value = "";
337 
338  char* env = ::getenv(var.c_str());
339  if (env != nullptr) {
340  found = true;
341  value = env;
342  }
343 
344  return found;
345 }
346 
347 bool isEnvSet(const string& var) {
348  string result;
349  return getEnv(var, result);
350 }
351 
353 #if defined(__APPLE__)
354 // Needed for _NSGetEnviron(void)
355 #include "crt_externs.h"
356 #endif
358 #if defined(__APPLE__)
359  static char** environ = *_NSGetEnviron();
360 #endif
361  vector<string> vars;
362  for (int i = 0; environ[i] != 0; ++i) {
363  vars.emplace_back(environ[i]);
364  }
365  return vars;
366 }
367 
369 int setEnv(const string& name, const string& value, bool overwrite) {
370 
371  int over = 1;
372  if (not overwrite) {
373  over = 0;
374  }
375 
376  return ::setenv(name.c_str(), value.c_str(), over);
377 }
378 
379 int unSetEnv(const string& name) {
380  return ::unsetenv(name.c_str());
381 }
382 
383 // -----------------------------------------------------------------------------
384 // backtrace utilities
385 // -----------------------------------------------------------------------------
387  ELEMENTS_UNUSED const int depth) {
388 
389  int count = ::backtrace(addresses.get(), depth);
390  if (count > 0) {
391  return count;
392  } else {
393  return 0;
394  }
395 }
396 
397 const vector<string> backTrace(const int depth, const int offset) {
398 
399  // Always hide the first two levels of the stack trace (that's us)
400  const int total_offset = offset + STACK_OFFSET;
401  const int total_depth = depth + total_offset;
402  vector<string> trace{};
403 
404  std::shared_ptr<void*> addresses{new (std::nothrow) void*[static_cast<std::size_t>(total_depth)],
406 
407  if (addresses.get() != nullptr) {
408 
409  int count = backTrace(addresses, total_depth);
410 
411  for (int i = total_offset; i < count; ++i) {
412  void* addr = 0;
413  string fnc;
414  string lib;
415  if (getStackLevel(addresses.get()[i], addr, fnc, lib)) {
416  std::ostringstream ost;
417  ost << "#" << std::setw(3) << std::setiosflags(std::ios::left) << i - total_offset + 1;
418  ost << std::hex << addr << std::dec << " " << fnc << " [" << lib << "]";
419  trace.emplace_back(ost.str());
420  }
421  }
422  }
423 
424  return trace;
425 }
426 
427 bool getStackLevel(void* addresses ELEMENTS_UNUSED, void*& addr ELEMENTS_UNUSED, string& fnc ELEMENTS_UNUSED,
428  string& lib ELEMENTS_UNUSED) {
429 
430  Dl_info info;
431 
432  if (::dladdr(addresses, &info) && info.dli_fname && info.dli_fname[0] != '\0') {
433  const char* symbol = info.dli_sname && info.dli_sname[0] != '\0' ? info.dli_sname : 0;
434 
435  lib = info.dli_fname;
436  addr = info.dli_saddr;
437 
438  if (symbol != 0) {
439  int stat;
440  std::unique_ptr<char, decltype(free)*> dmg(abi::__cxa_demangle(symbol, 0, 0, &stat), free);
441  fnc = string((stat == 0) ? dmg.get() : symbol);
442  } else {
443  fnc = "local";
444  }
445  return true;
446  } else {
447  return false;
448  }
449 }
450 
451 } // namespace System
452 } // namespace Elements
System.h
This file is intended to iron out all the differences between systems (currently Linux and MacOSX)
Elements::System::getEnv
ELEMENTS_API std::string getEnv(const std::string &var)
get a particular environment variable
Definition: System.cpp:324
Elements::System::getErrorString
const ELEMENTS_API std::string getErrorString(unsigned long error)
Retrieve error code as string for a given error.
Definition: System.cpp:165
Elements::System::STACK_OFFSET
const int STACK_OFFSET
Definition: System.h:92
std::string
STL class.
Elements::System::machineType
const ELEMENTS_API std::string & machineType()
Machine type.
Definition: System.cpp:313
std::shared_ptr
STL class.
Elements::System::typeinfoName
const ELEMENTS_API std::string typeinfoName(const std::type_info &)
Get platform independent information about the class type.
Definition: System.cpp:186
std::vector
STL class.
std::string::find
T find(T... args)
Elements::System::__attribute__
__attribute__((noinline)) int backTrace(ELEMENTS_UNUSED std
Definition: System.cpp:386
std::string::length
T length(T... args)
std::type_info
std::type_info::name
T name(T... args)
FuncPtrCast.h
defines a Small helper function that allows the cast from void * to function pointer
std::unique_ptr::get
T get(T... args)
Elements::System::getLastError
ELEMENTS_API unsigned long getLastError()
Get last system known error.
Definition: System.cpp:153
Elements::System::backTrace
ELEMENTS_API int backTrace(ELEMENTS_UNUSED std::shared_ptr< void * > addresses, ELEMENTS_UNUSED const int depth)
std::strerror
T strerror(T... args)
Elements::System::osVersion
const ELEMENTS_API std::string & osVersion()
OS version.
Definition: System.cpp:301
std::string::replace
T replace(T... args)
Elements::System::unSetEnv
ELEMENTS_API int unSetEnv(const std::string &name)
Simple wrap around unsetenv for strings.
Definition: System.cpp:379
std::hex
T hex(T... args)
Elements::System::Creator
void *(*)() Creator
Definition of the "generic" DLL entry point function.
Definition: System.h:115
std::cout
Elements::System::hostName
const ELEMENTS_API std::string & hostName()
Host name.
Definition: System.cpp:278
Elements::System::isEnvSet
ELEMENTS_API bool isEnvSet(const std::string &var)
Check if an environment variable is set or not.
Definition: System.cpp:347
std::string::c_str
T c_str(T... args)
std::string::compare
T compare(T... args)
std::array
STL class.
Elements::System::getLastErrorString
const ELEMENTS_API std::string getLastErrorString()
Get last system error as string.
Definition: System.cpp:159
Elements::System::loadDynamicLib
ELEMENTS_API unsigned long loadDynamicLib(const std::string &name, ImageHandle *handle)
Load dynamic link library.
Definition: System.cpp:88
std::ostringstream
STL class.
std::vector::emplace_back
T emplace_back(T... args)
std::endl
T endl(T... args)
Elements::System::SHLIB_SUFFIX
const std::string SHLIB_SUFFIX
alias for LIB_SUFFIX
Definition: System.h:84
std::setiosflags
T setiosflags(T... args)
Elements::System::getStackLevel
ELEMENTS_API bool getStackLevel(ELEMENTS_UNUSED void *addresses, ELEMENTS_UNUSED void *&addr, ELEMENTS_UNUSED std::string &fnc, ELEMENTS_UNUSED std::string &lib)
Elements::System::getProcedureByName
ELEMENTS_API unsigned long getProcedureByName(ImageHandle handle, const std::string &name, EntryPoint *pFunction)
Get a specific function defined in the DLL.
Definition: System.cpp:124
std::ostringstream::str
T str(T... args)
Elements::System::ImageHandle
void * ImageHandle
Definition of an image handle.
Definition: System.h:109
std::size_t
std::setw
T setw(T... args)
Unused.h
Macro to silence unused variables warnings from the compiler.
std::unique_ptr
STL class.
Elements::System::unloadDynamicLib
ELEMENTS_API unsigned long unloadDynamicLib(ImageHandle handle)
unload dynamic link library
Definition: System.cpp:115
ModuleInfo.h
OS specific details to access at run-time the module configuration of the process.
std::array::data
T data(T... args)
std::default_delete
ELEMENTS_UNUSED
#define ELEMENTS_UNUSED
Definition: Unused.h:39
Elements::System::setEnv
ELEMENTS_API int setEnv(const std::string &name, const std::string &value, bool overwrite=true)
set an environment variables.
Definition: System.cpp:369
Elements::System::EntryPoint
unsigned long(*)(const unsigned long iid, void **ppvObject) EntryPoint
Definition of the "generic" DLL entry point function.
Definition: System.h:113
Elements::System::osName
const ELEMENTS_API std::string & osName()
OS name.
Definition: System.cpp:289
HOST_NAME_MAX
#define HOST_NAME_MAX
Definition: System.h:103
Elements
Definition: callBackExample.h:35
Elements::System::InfoType::System
@ System