LArSoft  v09_90_00
Liquid Argon Software toolkit - https://larsoft.org/
DebugUtils.h
Go to the documentation of this file.
1 
13 #ifndef LARCOREALG_COREUTILS_DEBUGUTILS_H
14 #define LARCOREALG_COREUTILS_DEBUGUTILS_H
15 
16 // LArSoft includes
18 
19 // framework and support libraries
20 #include "cetlib_except/demangle.h"
21 
22 // C/C++ standard libraries
23 #include <bitset>
24 #include <cstddef> // std::ptrdiff_t
25 #include <cstdlib> // std::free()
26 #include <ostream>
27 #include <string>
28 #include <typeinfo>
29 #include <utility> // std::pair<>
30 #include <utility> // std::forward()
31 #include <vector>
32 
33 // non-standard libraries
34 #include <execinfo.h> // backtrace()...
35 // #include <experimental/filesystem> // std::experimental::filesystem::path
36 
37 namespace lar::debug {
38 
55  template <typename T>
56  inline std::string demangle(T const* = nullptr);
57 
58  //----------------------------------------------------------------------------
60  struct CallInfo_t {
61  private:
62  using range_t = std::pair<size_t, size_t>;
63 
64  public:
65  CallInfo_t(std::string const& s) { ParseString(s); }
66  CallInfo_t(const char* s) { ParseString(std::string(s)); }
67 
69  operator bool() const { return !libraryName.empty() || !mangledFunctionName.empty(); }
71  bool operator!() const { return libraryName.empty() && mangledFunctionName.empty(); }
72 
74  bool ParseString(std::string const& s);
75 
77  std::string const& function() const
78  {
80  }
81 
83  std::string shortLibrary() const
84  {
85  size_t sep = libraryName.rfind('/');
86  return (sep == std::string::npos) ? libraryName : libraryName.substr(sep + 1);
87  // return std::experimental::filesystem::path(libraryName).filename();
88  }
89 
90  std::string original;
91  std::string libraryName;
92  std::string functionName;
93  std::string mangledFunctionName;
94  void* address = nullptr;
95  std::ptrdiff_t offset = 0;
96 
97  private:
99  static bool emptyRange(range_t const& r) { return r.first >= r.second; }
100 
102  static std::string extract(std::string const& s, range_t const& r)
103  {
104  return emptyRange(r) ? "" : s.substr(r.first, r.second - r.first);
105  }
106 
108  void demangleFunction() { functionName = cet::demangle_symbol(mangledFunctionName); }
109 
111  void setAll(std::string const& s,
112  range_t addressStr,
113  range_t libraryStr,
114  range_t functionStr,
115  range_t offsetStr);
116 
117  }; // CallInfo_t
118 
119  //----------------------------------------------------------------------------
129  public:
131  struct opt {
133  enum option_t {
139  NOptions
140  }; // option_t
141 
142  std::bitset<NOptions> options;
143 
145  opt& set(option_t o, bool set = true)
146  {
147  options.set(o, set);
148  return *this;
149  }
150 
152  bool has(option_t o) const { return options.test(o); }
153 
154  }; // opt
155 
157 
159  CallInfoPrinter() { setDefaultOptions(); }
160 
162  CallInfoPrinter(opt opts) : options(opts) {}
163 
165  void setOptions(opt opts) { options = opts; }
166 
168  template <typename Stream>
169  void print(Stream&& out, CallInfo_t const& info) const;
170 
172  template <typename Stream>
173  void operator()(Stream&& out, CallInfo_t const& info) const
174  {
175  print(std::forward<Stream>(out), info);
176  }
177 
179  void setDefaultOptions() { options = defaultOptions(); }
180 
183  {
184  opt options;
185  options.set(opt::demangled);
186  options.set(opt::library);
187  options.set(opt::shortLibrary);
188  options.set(opt::address);
189  return options;
190  }
191 
192  }; // CallInfoPrinter
193 
194  //----------------------------------------------------------------------------
196  template <typename Stream>
197  inline Stream& operator<<(Stream&& out, CallInfo_t const& info);
198 
199  //----------------------------------------------------------------------------
202 
203  unsigned int maxLines = 5;
204  unsigned int skipLines = 1;
205  bool countOthers = true;
206  std::string indent;
207  std::string firstIndent;
208 
211 
213  void setUniformIndent(std::string uniformIndent) { indent = firstIndent = uniformIndent; }
214 
215  }; // struct BacktracePrintOptions
216 
224  template <typename Stream>
225  void printBacktrace(Stream&& out, BacktracePrintOptions options);
226 
232  template <typename Stream>
233  void printBacktrace(Stream&& out)
234  {
235  printBacktrace(std::forward<Stream>(out), BacktracePrintOptions());
236  }
237 
250  template <typename Stream>
251  void printBacktrace(Stream&& out,
252  unsigned int maxLines,
253  std::string indent = " ",
254  CallInfoPrinter::opt const* callInfoOptions = nullptr);
255 
256  //----------------------------------------------------------------------------
329  template <typename T, bool Enable /* = true */>
331 
332  //----------------------------------------------------------------------------
333 
334 } // namespace lar::debug
335 
336 //------------------------------------------------------------------------------
337 //--- template implementation
338 //------------------------------------------------------------------------------
339 namespace lar::debug {
340 
341  //----------------------------------------------------------------------------
342  template <typename T>
343  inline std::string demangle(T const* /* = nullptr */)
344  {
345  return cet::demangle_symbol(typeid(std::decay_t<T>).name());
346  }
347 
348  //----------------------------------------------------------------------------
349  template <typename Stream>
350  void CallInfoPrinter::print(Stream&& out, CallInfo_t const& info) const
351  {
352  if (!info) {
353  out << info.original << " (?)";
354  return;
355  }
356 
357  if (info.mangledFunctionName.empty()) {
358  if (options.has(opt::library)) {
359  out << "in " << (options.has(opt::shortLibrary) ? info.shortLibrary() : info.libraryName);
360  }
361  else
362  out << "unknown";
363  }
364  else {
365  // auto flags = out.flags(); // not available in message facility streams
366  out << info.function();
367  auto offset = info.offset;
368  if (offset && options.has(opt::offset)) {
369  out << " [";
370  if (offset > 0)
371  out << '+';
372  else {
373  out << '-';
374  offset = -offset;
375  }
376  out << std::showbase << std::hex << offset << "]";
377  } // if has offset
378  // out << std::ios::setiosflags(flags);
379  if (!info.libraryName.empty() && options.has(opt::library)) {
380  out << " in " << (options.has(opt::shortLibrary) ? info.shortLibrary() : info.libraryName);
381  }
382  }
383  if (info.address && options.has(opt::address)) out << " at " << ((void*)info.address);
384  out << std::flush;
385  } // CallInfoPrinter::print()
386 
387  //----------------------------------------------------------------------------
388  template <typename Stream>
389  inline Stream& operator<<(Stream&& out, CallInfo_t const& info)
390  {
391  CallInfoPrinter print;
392  print(std::forward<Stream>(out), info);
393  return out;
394  }
395 
396  //----------------------------------------------------------------------------
397  template <typename Stream>
398  void printBacktrace(Stream&& out, BacktracePrintOptions options)
399  {
400  unsigned int nSkip = std::max(options.skipLines, 0U);
401  std::vector<void*> buffer(nSkip + std::max(options.maxLines, 200U), nullptr);
402 
403  unsigned int const nItems = (unsigned int)backtrace(buffer.data(), buffer.size());
404 
405  // convert the calls in the buffer into a vector of strings
406  char** symbols = backtrace_symbols(buffer.data(), buffer.size());
407  if (!symbols) {
408  out << options.firstIndent << "<failed to get the call stack>\n" << std::flush;
409  return;
410  }
411  std::vector<CallInfo_t> callStack;
412  for (size_t i = 0; i < buffer.size(); ++i)
413  callStack.push_back((const char*)symbols[i]);
414  std::free(symbols);
415 
416  size_t lastItem = nSkip + options.maxLines;
417  if (lastItem > nItems) lastItem = nItems;
418  if (lastItem >= buffer.size()) --lastItem;
419 
420  CallInfoPrinter print(options.callInfoOptions);
421  for (size_t i = nSkip; i < lastItem; ++i) {
422  out << (i == 0 ? options.firstIndent : options.indent);
423  print(std::forward<Stream>(out), callStack[i]);
424  out << "\n";
425  }
426  if ((lastItem < nItems) && options.countOthers) {
427  out << options.indent << " ... and other " << (nItems - lastItem);
428  if (nItems == buffer.size()) out << " (or more)";
429  out << " levels\n";
430  }
431  out << std::flush;
432 
433  } // printBacktrace()
434 
435  //----------------------------------------------------------------------------
436  template <typename Stream>
437  void printBacktrace(Stream&& out,
438  unsigned int maxLines,
439  std::string indent /* = " " */,
440  CallInfoPrinter::opt const* callInfoOptions /* = nullptr */
441  )
442  {
443  BacktracePrintOptions options;
444  options.maxLines = maxLines;
445  options.indent = options.firstIndent = indent;
446  if (callInfoOptions) options.callInfoOptions = *callInfoOptions;
447  printBacktrace(std::forward<Stream>(out), options);
448  }
449 
450  //----------------------------------------------------------------------------
451  namespace details {
452 
453  template <typename T>
454  struct THE_TYPE_IS {
455  // if the assertion condition didn't depend on a template parameters,
456  // the assertion failure error would *always* be triggered
457  static_assert(::util::always_false_v<T>,
458  "static_assert_on<T>: check the error message (\"THE_TYPE_IS<>\") for "
459  "expansion of type `T`.");
460  }; // THE_TYPE_IS<>
461 
462  } // namespace details
463 
464  template <typename T, bool Enable = true>
465  struct static_assert_on {
466  static_assert_on() = default;
469  }; // static_assert_on<>
470 
471  template <typename T>
472  struct static_assert_on<T, false> {};
473 
474  //----------------------------------------------------------------------------
475 
476 } // namespace lar::debug
477 
478 #endif // LARCOREALG_COREUTILS_DEBUGUTILS_H
TRandom r
Definition: spectrum.C:23
std::string original
String from the backtrace, unparsed.
Definition: DebugUtils.h:90
std::string demangle(T const *=nullptr)
Outputs a demangled name for type T.
Definition: DebugUtils.h:343
std::string firstIndent
Special indentation for the first line.
Definition: DebugUtils.h:207
static std::string extract(std::string const &s, range_t const &r)
Translates a range into a string.
Definition: DebugUtils.h:102
unsigned int maxLines
Total number of lines to print.
Definition: DebugUtils.h:203
std::string functionName
Parsed function name, demangled.
Definition: DebugUtils.h:92
Print the library name the function lives in.
Definition: DebugUtils.h:136
static opt defaultOptions()
Returns a set of default options.
Definition: DebugUtils.h:182
Basic C++ metaprogramming utilities.
void setDefaultOptions()
Sets this object to use a set of default options.
Definition: DebugUtils.h:179
std::string libraryName
Parsed library name.
Definition: DebugUtils.h:91
void setAll(std::string const &s, range_t addressStr, range_t libraryStr, range_t functionStr, range_t offsetStr)
Fills the information from an original string and parsed ranges.
Definition: DebugUtils.cxx:113
CallInfo_t(const char *s)
Definition: DebugUtils.h:66
CallInfoPrinter(opt opts)
Constructor: use specified options.
Definition: DebugUtils.h:162
Print the instruction pointer memory address.
Definition: DebugUtils.h:134
bool has(option_t o) const
Returns whether the specified option is set.
Definition: DebugUtils.h:152
void * address
Function address.
Definition: DebugUtils.h:94
void demangleFunction()
Runs the demangler and stores the result.
Definition: DebugUtils.h:108
Backtrace printing options.
Definition: DebugUtils.h:201
bool countOthers
Whether to print number of omitted lines.
Definition: DebugUtils.h:205
CallInfoPrinter()
Default constructor: use default options.
Definition: DebugUtils.h:159
Class handling the output of information in a CallInfo_t object.
Definition: DebugUtils.h:128
bool operator!() const
Returns whether no information was parsed out of the original.
Definition: DebugUtils.h:71
Use demangled function names when possible.
Definition: DebugUtils.h:135
Print a shorter library name (base name only).
Definition: DebugUtils.h:137
std::bitset< NOptions > options
Value of current options.
Definition: DebugUtils.h:142
bool ParseString(std::string const &s)
Returns whether the translation was complete (offset is optional!).
Definition: DebugUtils.cxx:10
std::pair< size_t, size_t > range_t
Definition: DebugUtils.h:62
opt options
Set of current options.
Definition: DebugUtils.h:156
std::string indent(std::size_t const i)
Structure with information about a single call, parsed.
Definition: DebugUtils.h:60
option_t
List of available options.
Definition: DebugUtils.h:133
static bool emptyRange(range_t const &r)
Returns whether the range is empty or invalid.
Definition: DebugUtils.h:99
void setUniformIndent(std::string uniformIndent)
Sets all indentation to the same specified uniformIndent string.
Definition: DebugUtils.h:213
CallInfoPrinter::opt callInfoOptions
Options for each single backtrace call information line.
Definition: DebugUtils.h:210
std::string indent
Indentation string for all lines.
Definition: DebugUtils.h:206
void operator()(Stream &&out, CallInfo_t const &info) const
Print the content of info into the stream out, using the current options.
Definition: DebugUtils.h:173
opt & set(option_t o, bool set=true)
Set one option o to the specified set value (true by default).
Definition: DebugUtils.h:145
Stream & operator<<(Stream &&out, CallInfo_t const &info)
Helper operator to insert a call information in a stream with default options.
Definition: DebugUtils.h:389
unsigned int skipLines
Number of lines to skip.
Definition: DebugUtils.h:204
std::string shortLibrary() const
Returns only the library name (with suffix).
Definition: DebugUtils.h:83
std::string mangledFunctionName
Parsed function name, unprocessed.
Definition: DebugUtils.h:93
details::THE_TYPE_IS< T > _
Definition: DebugUtils.h:468
std::string const & function() const
Returns the function name (mangled if nothing better).
Definition: DebugUtils.h:77
void print(Stream &&out, CallInfo_t const &info) const
Print the content of info into the stream out, using the current options.
Definition: DebugUtils.h:350
void printBacktrace(Stream &&out, BacktracePrintOptions options)
Prints the full backtrace into a stream.
Definition: DebugUtils.h:398
void setOptions(opt opts)
Override all the options.
Definition: DebugUtils.h:165
CallInfo_t(std::string const &s)
Definition: DebugUtils.h:65
std::ptrdiff_t offset
Instruction pointer offset.
Definition: DebugUtils.h:95
Set of options for printing.
Definition: DebugUtils.h:131
Print the offset from the beginning of function.
Definition: DebugUtils.h:138