Elements  6.0.1
A C++ base framework for the Euclid Software.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
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  return 1;
132 #elif defined(__APPLE__)
133  *pFunction = (EntryPoint)::dlsym(handle, name.c_str());
134  if (not *pFunction) {
135  // Try with an underscore :
136  string sname = "_" + name;
137  *pFunction = (EntryPoint)::dlsym(handle, sname.c_str());
138  }
139  if (0 == *pFunction) {
140  errno = static_cast<int>(0xAFFEDEAD);
141  std::cout << "Elements::System::getProcedureByName>" << getLastErrorString() << std::endl;
142  return 0;
143  }
144  return 1;
145 #endif
146 }
147 
149 unsigned long getProcedureByName(ImageHandle handle, const string& name, Creator* pFunction) {
150  return getProcedureByName(handle, name, reinterpret_cast<EntryPoint*>(pFunction));
151 }
152 
154 unsigned long getLastError() {
155  // convert errno (int) to unsigned long
156  return static_cast<unsigned long>(static_cast<unsigned int>(errno));
157 }
158 
160 const string getLastErrorString() {
161  const string errString = getErrorString(getLastError());
162  return errString;
163 }
164 
166 const string getErrorString(unsigned long error) {
167  string errString = "";
168  char* cerrString(0);
169  // Remember: for linux dl* routines must be handled differently!
170  if (error == 0xAFFEDEAD) {
171  cerrString = reinterpret_cast<char*>(::dlerror());
172  if (0 == cerrString) {
173  cerrString = std::strerror(static_cast<int>(error));
174  }
175  if (0 == cerrString) {
176  cerrString = const_cast<char*>("Unknown error. No information found in strerror()!");
177  }
178  errString = string(cerrString);
179  errno = 0;
180  } else {
181  cerrString = std::strerror(static_cast<int>(error));
182  errString = string(cerrString);
183  }
184  return errString;
185 }
186 
187 const string typeinfoName(const std::type_info& tinfo) {
188  return typeinfoName(tinfo.name());
189 }
190 
191 const string typeinfoName(const char* class_name) {
192  string result;
193  if (strnlen(class_name, 1024) == 1) {
194  // See http://www.realitydiluted.com/mirrors/reality.sgi.com/dehnert_engr/cxx/abi.pdf
195  // for details
196  switch (class_name[0]) {
197  case 'v':
198  result = "void";
199  break;
200  case 'w':
201  result = "wchar_t";
202  break;
203  case 'b':
204  result = "bool";
205  break;
206  case 'c':
207  result = "char";
208  break;
209  case 'a':
210  result = "signed char";
211  break;
212  case 'h':
213  result = "unsigned char";
214  break;
215  case 's':
216  result = "short";
217  break;
218  case 't':
219  result = "unsigned short";
220  break;
221  case 'i':
222  result = "int";
223  break;
224  case 'j':
225  result = "unsigned int";
226  break;
227  case 'l':
228  result = "long";
229  break;
230  case 'm':
231  result = "unsigned long";
232  break;
233  case 'x':
234  result = "long long";
235  break;
236  case 'y':
237  result = "unsigned long long";
238  break;
239  case 'n':
240  result = "__int128";
241  break;
242  case 'o':
243  result = "unsigned __int128";
244  break;
245  case 'f':
246  result = "float";
247  break;
248  case 'd':
249  result = "double";
250  break;
251  case 'e':
252  result = "long double";
253  break;
254  case 'g':
255  result = "__float128";
256  break;
257  case 'z':
258  result = "ellipsis";
259  break;
260  }
261  } else {
262  int status;
263  std::unique_ptr<char, decltype(free)*> realname(abi::__cxa_demangle(class_name, 0, 0, &status), free);
264  if (realname == nullptr) {
265  return class_name;
266  }
267  result = realname.get();
269  string::size_type pos = result.find(", ");
270  while (string::npos != pos) {
271  result.replace(pos, static_cast<string::size_type>(2), ",");
272  pos = result.find(", ");
273  }
274  }
275  return result;
276 }
277 
279 const string& hostName() {
280  static string host{};
281  if (host.empty()) {
283  ::gethostname(buffer.data(), HOST_NAME_MAX);
284  host = buffer.data();
285  }
286  return host;
287 }
288 
290 const string& osName() {
291  static string osname = "";
292  struct utsname ut;
293  if (::uname(&ut) == 0) {
294  osname = ut.sysname;
295  } else {
296  osname = "UNKNOWN";
297  }
298  return osname;
299 }
300 
302 const string& osVersion() {
303  static string osver = "UNKNOWN";
304  struct utsname ut;
305 
306  if (uname(&ut) == 0) {
307  osver = ut.release;
308  }
309 
310  return osver;
311 }
312 
314 const string& machineType() {
315  static string mach = "UNKNOWN";
316  struct utsname ut;
317 
318  if (uname(&ut) == 0) {
319  mach = ut.machine;
320  }
321 
322  return mach;
323 }
324 
325 string getEnv(const string& var) {
326 
327  string env_str{};
328 
329  getEnv(var, env_str);
330 
331  return env_str;
332 }
333 
335 bool getEnv(const string& var, string& value) {
336  bool found = false;
337  value = "";
338 
339  char* env = ::getenv(var.c_str());
340  if (env != nullptr) {
341  found = true;
342  value = env;
343  }
344 
345  return found;
346 }
347 
348 bool isEnvSet(const string& var) {
349  string result;
350  return getEnv(var, result);
351 }
352 
354 #if defined(__APPLE__)
355 // Needed for _NSGetEnviron(void)
356 #include "crt_externs.h"
357 #endif
359 #if defined(__APPLE__)
360  static char** environ = *_NSGetEnviron();
361 #endif
362  vector<string> vars;
363  for (int i = 0; environ[i] != 0; ++i) {
364  vars.emplace_back(environ[i]);
365  }
366  return vars;
367 }
368 
370 int setEnv(const string& name, const string& value, bool overwrite) {
371 
372  int over = 1;
373  if (not overwrite) {
374  over = 0;
375  }
376 
377  return ::setenv(name.c_str(), value.c_str(), over);
378 }
379 
380 int unSetEnv(const string& name) {
381  return ::unsetenv(name.c_str());
382 }
383 
384 // -----------------------------------------------------------------------------
385 // backtrace utilities
386 // -----------------------------------------------------------------------------
387 __attribute__((noinline)) int backTrace(ELEMENTS_UNUSED std::shared_ptr<void*> addresses,
388  ELEMENTS_UNUSED const int depth) {
389 
390  int count = ::backtrace(addresses.get(), depth);
391  if (count > 0) {
392  return count;
393  } else {
394  return 0;
395  }
396 }
397 
398 const vector<string> backTrace(const int depth, const int offset) {
399 
400  // Always hide the first two levels of the stack trace (that's us)
401  const int total_offset = offset + STACK_OFFSET;
402  const int total_depth = depth + total_offset;
403  vector<string> trace{};
404 
405  std::shared_ptr<void*> addresses{new (std::nothrow) void*[static_cast<std::size_t>(total_depth)],
407 
408  if (addresses.get() != nullptr) {
409 
410  int count = backTrace(addresses, total_depth);
411 
412  for (int i = total_offset; i < count; ++i) {
413  void* addr = 0;
414  string fnc;
415  string lib;
416  if (getStackLevel(addresses.get()[i], addr, fnc, lib)) {
417  std::ostringstream ost;
418  ost << "#" << std::setw(3) << std::setiosflags(std::ios::left) << i - total_offset + 1;
419  ost << std::hex << addr << std::dec << " " << fnc << " [" << lib << "]";
420  trace.emplace_back(ost.str());
421  }
422  }
423  }
424 
425  return trace;
426 }
427 
428 bool getStackLevel(void* addresses ELEMENTS_UNUSED, void*& addr ELEMENTS_UNUSED, string& fnc ELEMENTS_UNUSED,
429  string& lib ELEMENTS_UNUSED) {
430 
431  Dl_info info;
432 
433  if (::dladdr(addresses, &info) && info.dli_fname && info.dli_fname[0] != '\0') {
434  const char* symbol = info.dli_sname && info.dli_sname[0] != '\0' ? info.dli_sname : 0;
435 
436  lib = info.dli_fname;
437  addr = info.dli_saddr;
438 
439  if (symbol != 0) {
440  int stat;
441  std::unique_ptr<char, decltype(free)*> dmg(abi::__cxa_demangle(symbol, 0, 0, &stat), free);
442  fnc = string((stat == 0) ? dmg.get() : symbol);
443  } else {
444  fnc = "local";
445  }
446  return true;
447  } else {
448  return false;
449  }
450 }
451 
452 } // namespace System
453 } // namespace Elements
ELEMENTS_API bool getStackLevel(ELEMENTS_UNUSED void *addresses, ELEMENTS_UNUSED void *&addr, ELEMENTS_UNUSED std::string &fnc, ELEMENTS_UNUSED std::string &lib)
ELEMENTS_API unsigned long getLastError()
Get last system known error.
Definition: System.cpp:154
Macro to silence unused variables warnings from the compiler.
ELEMENTS_API unsigned long unloadDynamicLib(ImageHandle handle)
unload dynamic link library
Definition: System.cpp:115
ELEMENTS_API const std::string getLastErrorString()
Get last system error as string.
Definition: System.cpp:160
void *(*)( Creator)
Definition of the &quot;generic&quot; DLL entry point function.
Definition: System.h:115
T strerror(T...args)
ELEMENTS_API int setEnv(const std::string &name, const std::string &value, bool overwrite=true)
set an environment variables.
Definition: System.cpp:370
OS specific details to access at run-time the module configuration of the process.
T endl(T...args)
defines a Small helper function that allows the cast from void * to function pointer ...
T setiosflags(T...args)
T setw(T...args)
STL class.
ELEMENTS_API bool isEnvSet(const std::string &var)
Check if an environment variable is set or not.
Definition: System.cpp:348
#define HOST_NAME_MAX
Definition: System.h:103
T data(T...args)
ELEMENTS_API int backTrace(ELEMENTS_UNUSED std::shared_ptr< void * > addresses, ELEMENTS_UNUSED const int depth)
T replace(T...args)
void * ImageHandle
Definition of an image handle.
Definition: System.h:109
ELEMENTS_API std::string getEnv(const std::string &var)
get a particular environment variable
Definition: System.cpp:325
ELEMENTS_API const std::string & osVersion()
OS version.
Definition: System.cpp:302
This file is intended to iron out all the differences between systems (currently Linux and MacOSX) ...
ELEMENTS_API int unSetEnv(const std::string &name)
Simple wrap around unsetenv for strings.
Definition: System.cpp:380
ELEMENTS_API const std::string getErrorString(unsigned long error)
Retrieve error code as string for a given error.
Definition: System.cpp:166
const int STACK_OFFSET
Definition: System.h:92
T get(T...args)
T find(T...args)
T length(T...args)
STL class.
T name(T...args)
STL class.
__attribute__((noinline)) int backTrace(ELEMENTS_UNUSED std
Definition: System.cpp:387
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
T c_str(T...args)
STL class.
T hex(T...args)
ELEMENTS_API const std::string typeinfoName(const std::type_info &)
Get platform independent information about the class type.
Definition: System.cpp:187
ELEMENTS_API const std::string & hostName()
Host name.
Definition: System.cpp:279
ELEMENTS_API const std::string & osName()
OS name.
Definition: System.cpp:290
#define ELEMENTS_UNUSED
Definition: Unused.h:39
unsigned long(*)(const unsigned long iid, void **ppvObject) EntryPoint
Definition of the &quot;generic&quot; DLL entry point function.
Definition: System.h:113
ELEMENTS_API unsigned long loadDynamicLib(const std::string &name, ImageHandle *handle)
Load dynamic link library.
Definition: System.cpp:88
const std::string SHLIB_SUFFIX
alias for LIB_SUFFIX
Definition: System.h:84
ELEMENTS_API const std::string & machineType()
Machine type.
Definition: System.cpp:314
T compare(T...args)
T emplace_back(T...args)