Elements 6.0.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>
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 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
149unsigned long getProcedureByName(ImageHandle handle, const string& name, Creator* pFunction) {
150 return getProcedureByName(handle, name, reinterpret_cast<EntryPoint*>(pFunction));
151}
152
154unsigned long getLastError() {
155 // convert errno (int) to unsigned long
156 return static_cast<unsigned long>(static_cast<unsigned int>(errno));
157}
158
160const string getLastErrorString() {
161 const string errString = getErrorString(getLastError());
162 return errString;
163}
164
166const 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
187const string typeinfoName(const std::type_info& tinfo) {
188 return typeinfoName(tinfo.name());
189}
190
191const 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
279const 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
290const 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
302const 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
314const 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
325string getEnv(const string& var) {
326
327 string env_str{};
328
329 getEnv(var, env_str);
330
331 return env_str;
332}
333
335bool 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
348bool 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
370int 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
380int unSetEnv(const string& name) {
381 return ::unsetenv(name.c_str());
382}
383
384// -----------------------------------------------------------------------------
385// backtrace utilities
386// -----------------------------------------------------------------------------
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
398const 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)) {
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
428bool 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
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:348
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:166
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:370
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:358
ELEMENTS_API unsigned long getLastError()
Get last system known error.
Definition: System.cpp:154
ELEMENTS_API const std::string & osName()
OS name.
Definition: System.cpp:290
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:187
ELEMENTS_API int unSetEnv(const std::string &name)
Simple wrap around unsetenv for strings.
Definition: System.cpp:380
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:302
ELEMENTS_API const std::string getLastErrorString()
Get last system error as string.
Definition: System.cpp:160
ELEMENTS_API const std::string & machineType()
Machine type.
Definition: System.cpp:314
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:279
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)