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