libcsdbg  1.28
C++ exception (and generic) stack trace debug library
util.cpp
Go to the documentation of this file.
1 #include "../include/util.hpp"
2 #include "../include/tracer.hpp"
3 
10 namespace csdbg {
11 
12 /* Static member variable definition */
13 
14 pthread_mutex_t util::m_lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
15 
16 chain<string> *util::m_config = NULL;
17 
18 
25 {
26  try {
27  m_config = new chain<string>;
28  return;
29  }
30 
31  catch (std::exception &x) {
32  std::cerr << x;
33  }
34 
35  exit(EXIT_FAILURE);
36 }
37 
38 
43 {
44  delete m_config;
45  m_config = NULL;
46 }
47 
48 
58 void util::version(u16 *major, u16 *minor)
59 {
60  if ( likely(major != NULL) )
61  *major = g_major;
62 
63  if ( likely(minor != NULL) )
64  *minor = g_minor;
65 }
66 
67 
73 const i8* util::prefix()
74 {
75  return g_prefix;
76 }
77 
78 
88 {
89  /*
90  * The procfs filesystem maintains a directory for each process (/proc/pid)
91  * and a symlink therein (exe) that contains the absolute path of the process
92  * executable
93  */
94  i8 path[PATH_MAX + 1];
95  i32 len = snprintf(path, PATH_MAX + 1, "/proc/%d/exe", getpid());
96  if ( unlikely(len < 0) )
97  throw exception("snprintf failed with retval %d", len);
98 
99  i8 *retval = new i8[PATH_MAX + 1];
100 
101  /* Read the contents of the symlink */
102  len = readlink(path, retval, PATH_MAX);
103  if ( unlikely(len < 0) ) {
104  delete[] retval;
105  throw exception(
106  "failed to read symlink '%s' (errno %d - %s)",
107  path,
108  errno,
109  strerror(errno)
110  );
111  }
112 
113  retval[len] = '\0';
114  return retval;
115 }
116 
117 
129 {
130  __D_ASSERT(var != NULL);
131  if ( unlikely(var == NULL) )
132  return NULL;
133 
134  const i8 *val = ::getenv(var);
135  if ( unlikely(val == NULL) )
136  return NULL;
137 
138  chain<string> *retval = new chain<string>;
139  string *token = NULL;
140 
141  try {
142  token = new string;
143 
144  /* Token parsing */
145  for (u32 i = 0, sz = strlen(val); likely(i < sz); i++) {
146  i8 ch = val[i];
147 
148  if ( likely(ch != ':') )
149  token->append(ch);
150 
151  /* New token */
152  else if ( likely(token->length() > 0) ) {
153  retval->add(token);
154  token = NULL;
155  token = new string;
156  }
157  }
158 
159  /* Final token */
160  if ( likely(token->length() > 0) )
161  retval->add(token);
162  else
163  delete token;
164 
165  return retval;
166  }
167 
168  catch (...) {
169  delete retval;
170  delete token;
171  throw;
172  }
173 }
174 
175 
188 void util::init(i32 &argc, i8 **argv)
189 {
190  __D_ASSERT(argc >= 1);
191  __D_ASSERT(argv != NULL);
192  if ( unlikely(argc <= 1 || argv == NULL) )
193  return;
194 
195  try {
196  for (i32 i = 0; likely(i < argc); i++) {
197  i8 *arg = argv[i];
198 
199  /* If the argument is not libcsdbg-related */
200  if ( likely(strstr(arg, "--csdbg-") != arg) )
201  continue;
202 
203  if ( likely(strlen(arg) > 8) )
204  m_config->add(new string(arg + 8));
205 
206  /* Remove it from the argument vector */
207  for (i32 j = i; likely(j < argc); j++)
208  argv[j] = argv[j + 1];
209 
210  i--;
211  argc--;
212  }
213 
214 #if CSDBG_DBG_LEVEL & CSDBG_DBGL_INFO
215  if ( unlikely(m_config->size() > 0) )
216  util::dbg_info("libcsdbg runtime configuration:");
217 
218  for (u32 i = 0, sz = m_config->size(); likely(i < sz); i++)
219  util::dbg_info(" arg %d: --csdbg-(%s)", i, m_config->at(i)->cstr());
220 #endif
221 
222  return;
223  }
224 
225  catch (exception &x) {
226  std::cerr << x;
227  }
228 
229  catch (std::exception &x) {
230  std::cerr << x;
231  }
232 
233  exit(EXIT_FAILURE);
234 }
235 
236 
243 {
244  return m_config->size();
245 }
246 
247 
259 const string* util::argv(u32 i)
260 {
261  return m_config->at(i);
262 }
263 
264 
276 const i8* util::type_name(const std::type_info &inf)
277 {
278  const i8 *nm = inf.name();
279 
280  /* The abi namespace is part of libstdc++ */
281  i8 *retval = abi::__cxa_demangle(nm, NULL, NULL, NULL);
282  if ( likely(retval != NULL) )
283  return retval;
284 
285  retval = new i8[strlen(nm) + 1];
286  strcpy(retval, nm);
287  return retval;
288 }
289 
290 
299 {
300  if (a < b)
301  return (a < c) ? a : c;
302 
303  return (b < c)? b : c;
304 }
305 
306 
320 void* util::memset(void *mem, u8 val, u32 sz)
321 {
322  __D_ASSERT(mem != NULL);
323  if ( unlikely(mem == NULL) )
324  return mem;
325 
326  u8 *p = static_cast<u8*> (mem);
327  while ( likely(sz-- > 0) )
328  *(p++) = val;
329 
330  return mem;
331 }
332 
333 
347 void* util::memcpy(void *dst, const void *src, u32 sz)
348 {
349  __D_ASSERT(dst != NULL);
350  __D_ASSERT(src != NULL);
351  if ( unlikely(dst == NULL || src == NULL) )
352  return dst;
353 
354  u8 *d = static_cast<u8*> (dst);
355  const u8 *s = static_cast<const u8*> (src);
356  while ( likely(sz-- > 0) )
357  *(d++) = *(s++);
358 
359  return dst;
360 }
361 
362 
374 void* util::memswap(void *mem, u32 sz)
375 {
376  __D_ASSERT(mem != NULL);
377  if ( unlikely(mem == NULL) )
378  return mem;
379 
380  u8 *l = static_cast<u8*> (mem);
381  u8 *r = l + sz - 1;
382  while ( likely(l < r) ) {
383  u8 tmp = *l;
384  *(l++) = *r;
385  *(r--) = tmp;
386  }
387 
388  return mem;
389 }
390 
391 
398 {
399  pthread_mutex_lock(&m_lock);
400 }
401 
402 
407 {
408  pthread_mutex_unlock(&m_lock);
409 }
410 
411 
419 bool util::is_regular(const fileinfo_t &inf)
420 {
421  return S_ISREG(inf.st_mode);
422 }
423 
424 
432 bool util::is_chardev(const fileinfo_t &inf)
433 {
434  return S_ISCHR(inf.st_mode);
435 }
436 
437 
446 {
447  if ( likely(geteuid() == inf.st_uid && (inf.st_mode & S_IRUSR)) )
448  return true;
449 
450  if ( likely(getegid() == inf.st_gid && (inf.st_mode & S_IRGRP)) )
451  return true;
452 
453  return inf.st_mode & S_IROTH;
454 }
455 
456 
465 {
466  if ( likely(geteuid() == inf.st_uid && (inf.st_mode & S_IWUSR)) )
467  return true;
468 
469  if ( likely(getegid() == inf.st_gid && (inf.st_mode & S_IWGRP)) )
470  return true;
471 
472  return inf.st_mode & S_IWOTH;
473 }
474 
475 
489 i32 util::va_size(const i8 *fmt, va_list args)
490 {
491  if ( unlikely(fmt == NULL) ) {
492  va_end(args);
493  throw exception("invalid argument: fmt (=%p)", fmt);
494  }
495 
496  i32 retval = vsnprintf(NULL, 0, fmt, args);
497  va_end(args);
498  if ( unlikely(retval < 0) )
499  throw exception("vsnprintf failed with retval %d", retval);
500 
501  return retval;
502 }
503 
504 
519 i8* util::va_format(const i8 *fmt, va_list args)
520 {
521  if ( unlikely(fmt == NULL) ) {
522  va_end(args);
523  throw exception("invalid argument: fmt (=%p)", fmt);
524  }
525 
526  va_list cpargs;
527  va_copy(cpargs, args);
528  i8 *retval = NULL;
529 
530  try {
531  i32 sz = va_size(fmt, cpargs);
532  retval = new i8[sz + 1];
533 
534  i32 check = vsprintf(retval, fmt, args);
535  if ( unlikely(check != sz) )
536  throw exception("vsprintf failed with retval %d", check);
537 
538  va_end(args);
539  return retval;
540  }
541 
542  catch (...) {
543  delete[] retval;
544  va_end(args);
545  throw;
546  }
547 }
548 
549 
568 i8* util::va_format(i8 *dst, const i8 *fmt, va_list args)
569 {
570  if ( unlikely(fmt == NULL) ) {
571  va_end(args);
572  throw exception("invalid argument: fmt (=%p)", fmt);
573  }
574 
575  if ( unlikely(dst == NULL) )
576  return va_format(fmt, args);
577 
578  i32 sz = vsprintf(dst, fmt, args);
579  va_end(args);
580  if ( unlikely(sz < 0) )
581  throw exception("vsprintf failed with retval %d", sz);
582 
583  return dst;
584 }
585 
586 
594 void util::header(std::ostream &stream, const i8 *tag)
595 {
596  __D_ASSERT(tag != NULL);
597  if ( unlikely(tag == NULL) )
598  return;
599 
600 #ifdef CSDBG_WITH_COLOR_TERM
601  stream << "\e[38;5;" << std::dec;
602 
603  i8 ch = tag[0];
604  if ( likely(ch == 'i') )
605  stream << INFO_TAG_FG;
606 
607  else if ( likely(ch == 'w') )
608  stream << WARNING_TAG_FG;
609 
610  else
611  stream << ERROR_TAG_FG;
612 
613  stream << "m[" << tag << "]\e[0m";
614 #else
615  stream << "[" << tag << "]";
616 #endif
617 
618  stream << " [" << std::dec << getpid() << ", ";
619  stream << "0x" << std::hex << pthread_self();
620 
621  const i8 *thr = NULL;
622  tracer *iface = tracer::interface();
623  if ( likely(iface != NULL) )
624  thr = iface->proc()->current_thread()->name();
625 
626  stream << " (" << ((thr != NULL) ? thr : "anon") << ")] ";
627 }
628 
629 
641 void util::dbg(const i8 *tag, const i8 *fmt, va_list args)
642 {
643 #ifdef CSDBG_WITH_DEBUG
644  __D_ASSERT(tag != NULL);
645  __D_ASSERT(fmt != NULL);
646  if ( unlikely(tag == NULL || fmt == NULL) ) {
647  va_end(args);
648  return;
649  }
650 
651  i8 *msg = NULL;
652  try {
653  msg = va_format(fmt, args);
654 
655  lock();
656  if ( likely(!isspace(fmt[0])) )
657  header(std::cerr, tag);
658 
659  std::cerr << msg << "\r\n";
660  delete[] msg;
661  unlock();
662  }
663 
664  catch (...) {
665  __D_ASSERT(msg != NULL);
666  }
667 #endif
668 }
669 
670 
680 void util::dbg_info(const i8 *fmt, ...)
681 {
682 #if CSDBG_DBG_LEVEL & CSDBG_DBGL_INFO
683  __D_ASSERT(fmt != NULL);
684  if ( unlikely(fmt == NULL) )
685  return;
686 
687  va_list args;
688  va_start(args, fmt);
689  dbg("i", fmt, args);
690 #endif
691 }
692 
693 
703 void util::dbg_warn(const i8 *fmt, ...)
704 {
705 #if CSDBG_DBG_LEVEL & CSDBG_DBGL_WARNING
706  __D_ASSERT(fmt != NULL);
707  if ( unlikely(fmt == NULL) )
708  return;
709 
710  va_list args;
711  va_start(args, fmt);
712  dbg("w", fmt, args);
713 #endif
714 }
715 
716 
726 void util::dbg_error(const i8 *fmt, ...)
727 {
728 #if CSDBG_DBG_LEVEL & CSDBG_DBGL_ERROR
729  __D_ASSERT(fmt != NULL);
730  if ( unlikely(fmt == NULL) )
731  return;
732 
733  va_list args;
734  va_start(args, fmt);
735  dbg("e", fmt, args);
736 #endif
737 }
738 
739 }
740 
static pthread_mutex_t m_lock
Global access mutex.
Definition: util.hpp:26
virtual string & append(const string &)
Append a string.
Definition: string.cpp:404
virtual T * at(u32) const
Get the node data pointer at a chain offset.
Definition: chain.hpp:440
static const i8 * exec_path()
Get the absolute path of the executable.
Definition: util.cpp:87
static bool is_writable(const fileinfo_t &)
Check if the process has write access to a file.
Definition: util.cpp:464
virtual u32 size() const
Get the chain size (node count)
Definition: chain.hpp:276
static void * memset(void *, u8, u32)
Fill a memory block with a constant byte.
Definition: util.cpp:320
char i8
8-bit signed integer
Definition: config.hpp:72
struct stat fileinfo_t
File metadata.
Definition: config.hpp:112
static tracer * interface()
Get the interface object.
Definition: tracer.cpp:629
static void version(u16 *, u16 *)
Get the library version numbers.
Definition: util.cpp:58
A tracer object is the default interface to libcsdbg for the instrumentation functions and for the li...
Definition: tracer.hpp:34
static const u16 g_minor
Library version minor.
Definition: config.hpp:195
static void on_lib_load() __attribute((constructor))
Library constructor.
Definition: util.cpp:24
static u32 argc()
Get the number of CLI arguments.
Definition: util.cpp:242
static const i8 * type_name(const std::type_info &)
Get the demangled name of a type.
Definition: util.cpp:276
unsigned short u16
16-bit unsigned integer
Definition: config.hpp:97
static void on_lib_unload() __attribute((destructor))
Library destructor.
Definition: util.cpp:42
#define likely(expr)
Offer a hint (positive) to the pipeline branch predictor.
Definition: config.hpp:344
static void dbg_warn(const i8 *,...)
Print a warning debug message on the standard error stream.
Definition: util.cpp:703
static const i8 * prefix()
Get the library installation prefix.
Definition: util.cpp:73
unsigned char u8
8-bit unsigned integer
Definition: config.hpp:92
static void lock()
Lock the global access mutex.
Definition: util.cpp:397
static void unlock()
Unlock the global access mutex.
Definition: util.cpp:406
static bool is_readable(const fileinfo_t &)
Check if the process has read access to a file.
Definition: util.cpp:445
static void dbg_error(const i8 *,...)
Print an error debug message on the standard error stream.
Definition: util.cpp:726
static bool is_chardev(const fileinfo_t &)
Check if a file is a character device node.
Definition: util.cpp:432
static chain< string > * m_config
Runtime configuration.
Definition: util.hpp:28
virtual const i8 * cstr() const
Get the C-string equivalent.
Definition: string.cpp:211
virtual chain & add(T *)
Add a node to the chain.
Definition: chain.hpp:363
static const i8 g_prefix[]
Library installation prefix.
Definition: config.hpp:178
static void dbg(const i8 *, const i8 *, va_list)
Print a tagged debug message on the standard error stream.
Definition: util.cpp:641
static u32 min(u32, u32, u32)
Get the minimum of three numbers.
Definition: util.cpp:298
Lightweight string buffer class (for ISO-8859-1 text)
Definition: string.hpp:36
static i8 * va_format(const i8 *, va_list)
Format a buffer with a printf-style string expanded with the values of a variable argument list...
Definition: util.cpp:519
static void * memswap(void *, u32)
Reverse the byte order of a memory block.
Definition: util.cpp:374
unsigned int u32
32-bit unsigned integer
Definition: config.hpp:102
int i32
32-bit signed integer
Definition: config.hpp:82
static chain< string > * getenv(const i8 *)
Parse a shell (environment) variable to its components.
Definition: util.cpp:128
static bool is_regular(const fileinfo_t &)
Check if a file is a regular one.
Definition: util.cpp:419
virtual u32 length() const
Get the character count.
Definition: string.cpp:222
#define ERROR_TAG_FG
Tag color for error and exception messages.
Definition: config.hpp:299
static void header(std::ostream &, const i8 *)
Print a tagged message header on an output stream.
Definition: util.cpp:594
virtual const i8 * name() const
Get the thread name.
Definition: thread.cpp:98
#define WARNING_TAG_FG
Tag color for warning messages.
Definition: config.hpp:294
virtual process * proc() const
Get the process handle.
Definition: tracer.cpp:618
This class is a throwable with a textual description of an error.
Definition: exception.hpp:26
virtual thread * current_thread()
Get the currently executing thread.
Definition: process.cpp:339
static i32 va_size(const i8 *, va_list)
Compute the size of a printf-style format string expanded with the values of a variable argument list...
Definition: util.cpp:489
#define INFO_TAG_FG
Tag color for informational messages.
Definition: config.hpp:289
#define unlikely(expr)
Offer a hint (negative) to the pipeline branch predictor.
Definition: config.hpp:349
static void * memcpy(void *, const void *, u32)
Copy a memory block.
Definition: util.cpp:347
static void dbg_info(const i8 *,...)
Print an informational debug message on the standard error stream.
Definition: util.cpp:680
static const string * argv(u32)
Get a CLI argument, given its offset in util::m_config.
Definition: util.cpp:259
static const u16 g_major
Library version major.
Definition: config.hpp:190
static void init(i32 &, i8 **)
Initialize the library runtime configuration. Seek command line arguments that are related with libcs...
Definition: util.cpp:188
#define __D_ASSERT(x)
Assertion macro.
Definition: config.hpp:268