Elements 6.1.2
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>
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
47using std::size_t;
48using std::string;
49using std::vector;
50
51namespace Elements {
52namespace System {
53
54// --------------------------------------------------------------------------------------
55// Private functions
56// --------------------------------------------------------------------------------------
57
58namespace {
59
60unsigned 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
69unsigned 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
88unsigned 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
115unsigned long unloadDynamicLib(ImageHandle handle) {
116 ::dlclose(handle);
117 if (0) {
118 return getLastError();
119 }
120 return 1;
121}
122
124unsigned 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
148unsigned long getProcedureByName(ImageHandle handle, const string& name, Creator* pFunction) {
149 return getProcedureByName(handle, name, reinterpret_cast<EntryPoint*>(pFunction));
150}
151
153unsigned long getLastError() {
154 // convert errno (int) to unsigned long
155 return static_cast<unsigned long>(static_cast<unsigned int>(errno));
156}
157
159const string getLastErrorString() {
160 const string errString = getErrorString(getLastError());
161 return errString;
162}
163
165const 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
186const string typeinfoName(const std::type_info& tinfo) {
187 return typeinfoName(tinfo.name());
188}
189
190const 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
278const 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
289const 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
301const 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
313const 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
324string getEnv(const string& var) {
325
326 string env_str{};
327
328 getEnv(var, env_str);
329
330 return env_str;
331}
332
334bool 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
347bool 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
369int 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
379int 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
397const 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)) {
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
427bool 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
OS specific details to access at run-time the module configuration of the process.
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)
#define __attribute__(x)
Definition: Attribute.h:32
#define ELEMENTS_UNUSED
Definition: Unused.h:39
T hex(T... args)
T name(T... args)
ELEMENTS_API bool isEnvSet(const std::string &var)
Check if an environment variable is set or not.
Definition: System.cpp:347
const std::string SHLIB_SUFFIX
alias for LIB_SUFFIX
Definition: System.h:84
unsigned long(*)(const unsigned long iid, void **ppvObject) EntryPoint
Definition of the "generic" DLL entry point function.
Definition: System.h:113
ELEMENTS_API const std::string getErrorString(unsigned long error)
Retrieve error code as string for a given error.
Definition: System.cpp:165
void * ImageHandle
Definition of an image handle.
Definition: System.h:109
ELEMENTS_API int setEnv(const std::string &name, const std::string &value, bool overwrite=true)
set an environment variables.
Definition: System.cpp:369
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:115
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:357
ELEMENTS_API unsigned long getLastError()
Get last system known error.
Definition: System.cpp:153
ELEMENTS_API const std::string & osName()
OS name.
Definition: System.cpp:289
ELEMENTS_API unsigned long loadDynamicLib(const std::string &name, ImageHandle *handle)
Load dynamic link library.
Definition: System.cpp:88
ELEMENTS_API const std::string typeinfoName(const std::type_info &)
Get platform independent information about the class type.
Definition: System.cpp:186
ELEMENTS_API int unSetEnv(const std::string &name)
Simple wrap around unsetenv for strings.
Definition: System.cpp:379
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
ELEMENTS_API const std::string & osVersion()
OS version.
Definition: System.cpp:301
ELEMENTS_API const std::string getLastErrorString()
Get last system error as string.
Definition: System.cpp:159
ELEMENTS_API const std::string & machineType()
Machine type.
Definition: System.cpp:313
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:278
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)