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