DebugUtils.h
Go to the documentation of this file.
1 /**
2  * @file larcorealg/CoreUtils/DebugUtils.h
3  * @brief Functions to help debugging by instrumenting code.
4  * @author Gianluca Petrillo (petrillo@fnal.gov)
5  * @date April 8, 2016
6  * @see `larcorealg/CoreUtils/DebugUtils.cxx`
7  *
8  * This library contains:
9  * - a function to return the name of the type of a variable
10  * - a function printing into a stream the current call stack
11  *
12  */
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 <cstddef> // std::ptrdiff_t
24 #include <cstdlib> // std::free()
25 #include <utility> // std::pair<>
26 #include <vector>
27 #include <string>
28 #include <bitset>
29 #include <typeinfo>
30 #include <ostream>
31 #include <utility> // std::forward()
32 
33 // non-standard libraries
34 #include <execinfo.h> // backtrace()...
35 // #include <experimental/filesystem> // std::experimental::filesystem::path
36 
37 
38 namespace lar::debug {
39 
40  /** ***********************************************************************
41  * @brief Outputs a demangled name for type T.
42  * @tparam T type whose name must be demangled (optional)
43  * @return a string with demangled name
44  *
45  * It relies on cetlib.
46  * The type to be demangled can be specified either as template argument:
47  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
48  * auto name = lar::debug::demangle<std::string>();
49  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
50  * or via a argument pointer:
51  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
52  * auto name = lar::debug::demangle(this);
53  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
54  *
55  */
56  template <typename T>
57  inline std::string demangle(T const* = nullptr);
58 
59 
60  //----------------------------------------------------------------------------
61  /// Structure with information about a single call, parsed.
62  struct CallInfo_t {
63  private:
64  using range_t = std::pair<size_t, size_t>;
65 
66  public:
68  CallInfo_t(const char* s) { ParseString(std::string(s)); }
69 
70  /// Returns whether there is some information parsed.
71  operator bool() const
72  { return !libraryName.empty() || !mangledFunctionName.empty(); }
73  /// Returns whether no information was parsed out of the original.
74  bool operator! () const
75  { return libraryName.empty() && mangledFunctionName.empty(); }
76 
77  /// Returns whether the translation was complete (offset is optional!).
78  bool ParseString(std::string const& s);
79 
80  /// Returns the function name (mangled if nothing better).
81  std::string const& function() const
82  { return functionName.empty()? mangledFunctionName: functionName; }
83 
84  /// Returns only the library name (with suffix).
86  {
87  size_t sep = libraryName.rfind('/');
88  return (sep == std::string::npos)
89  ? libraryName: libraryName.substr(sep + 1);
90  // return std::experimental::filesystem::path(libraryName).filename();
91  }
92 
93  std::string original; ///< String from the backtrace, unparsed.
94  std::string libraryName; ///< Parsed library name.
95  std::string functionName; ///< Parsed function name, demangled.
96  std::string mangledFunctionName; ///< Parsed function name, unprocessed.
97  void* address = nullptr; ///< Function address.
98  std::ptrdiff_t offset = 0; ///< Instruction pointer offset.
99 
100  private:
101 
102  /// Returns whether the range is empty or invalid.
103  static bool emptyRange(range_t const& r) { return r.first >= r.second; }
104 
105  /// Translates a range into a string.
106  static std::string extract(std::string const& s, range_t const& r)
107  { return emptyRange(r)? "": s.substr(r.first, r.second - r.first); }
108 
109  /// Runs the demangler and stores the result.
111  { functionName = cet::demangle_symbol(mangledFunctionName); }
112 
113  /// Fills the information from an original string and parsed ranges.
114  void setAll(
115  std::string const& s,
116  range_t addressStr, range_t libraryStr,
117  range_t functionStr, range_t offsetStr
118  );
119 
120  }; // CallInfo_t
121 
122 
123  //----------------------------------------------------------------------------
124  /**
125  * @brief Class handling the output of information in a CallInfo_t object.
126  *
127  * This class has a "default" print function (also replicated as a call
128  * operator), and a set of options that can be tweaked to change the amount
129  * of information and format to be printed.
130  *
131  */
133  public:
134  /// Set of options for printing.
135  struct opt {
136  /// List of available options.
137  enum option_t {
138  address, ///< Print the instruction pointer memory address.
139  demangled, ///< Use demangled function names when possible.
140  library, ///< Print the library name the function lives in.
141  shortLibrary, ///< Print a shorter library name (base name only).
142  offset, ///< Print the offset from the beginning of function.
143  NOptions ///< Number of available options.
144  }; // option_t
145 
146  std::bitset<NOptions> options; ///< Value of current options.
147 
148  /// Set one option `o` to the specified set value (true by default).
149  opt& set(option_t o, bool set = true)
150  { options.set(o, set); return *this; }
151 
152  /// Returns whether the specified option is set.
153  bool has(option_t o) const { return options.test(o); }
154 
155  }; // opt
156 
157  opt options; ///< Set of current options.
158 
159  /// Default constructor: use default options.
160  CallInfoPrinter() { setDefaultOptions(); }
161 
162  /// Constructor: use specified options.
163  CallInfoPrinter(opt opts): options(opts) {}
164 
165  /// Override all the options.
166  void setOptions(opt opts) { options = opts; }
167 
168  /// Print the content of info into the stream out, using the current options.
169  template <typename Stream>
170  void print(Stream&& out, CallInfo_t const& info) const;
171 
172  /// Print the content of info into the stream out, using the current options
173  template <typename Stream>
174  void operator() (Stream&& out, CallInfo_t const& info) const
175  { print(std::forward<Stream>(out), info); }
176 
177  /// Sets this object to use a set of default options
178  void setDefaultOptions() { options = defaultOptions(); }
179 
180  /// Returns a set of default options
182  {
183  opt options;
184  options.set(opt::demangled);
185  options.set(opt::library);
186  options.set(opt::shortLibrary);
187  options.set(opt::address);
188  return options;
189  }
190 
191  }; // CallInfoPrinter
192 
193 
194  //----------------------------------------------------------------------------
195  /// Helper operator to insert a call information in a stream with default options.
196  template <typename Stream>
197  inline Stream& operator<< (Stream&& out, CallInfo_t const& info);
198 
199 
200  //----------------------------------------------------------------------------
201  /// Backtrace printing options
203 
204  unsigned int maxLines = 5; ///< Total number of lines to print.
205  unsigned int skipLines = 1; ///< Number of lines to skip.
206  bool countOthers = true; ///< Whether to print number of omitted lines.
207  std::string indent; ///< Indentation string for all lines.
208  std::string firstIndent; ///< Special indentation for the first line.
209 
210  /// Options for each single backtrace call information line.
212 
213  /// Sets all indentation to the same specified `uniformIndent` string.
214  void setUniformIndent(std::string uniformIndent)
215  { indent = firstIndent = uniformIndent; }
216 
217  }; // struct BacktracePrintOptions
218 
219 
220  /**
221  * @brief Prints the full backtrace into a stream.
222  * @tparam Stream type of output stream
223  * @param out the output stream to insert output into
224  * @param options printing options (see BacktracePrintOptions)
225  *
226  */
227  template <typename Stream>
228  void printBacktrace(Stream&& out, BacktracePrintOptions options);
229 
230  /**
231  * @brief Prints the full backtrace into a stream with default options.
232  * @tparam Stream type of output stream
233  * @param out the output stream to insert output into
234  */
235  template <typename Stream>
236  void printBacktrace(Stream&& out)
237  { printBacktrace(std::forward<Stream>(out), BacktracePrintOptions()); }
238 
239  /**
240  * @brief Prints the full backtrace into a stream.
241  * @tparam Stream type of output stream
242  * @param out the output stream to insert output into
243  * @param maxLines print at most this many lines in the output (default: 5)
244  * @param indent prepend a string in front of any new line (default: " ")
245  * @param callInfoOptions use these output options (default ones if null)
246  *
247  * The call information output options are described in
248  * `CallInfoPrinter::opt` structure.
249  *
250  */
251  template <typename Stream>
252  void printBacktrace(
253  Stream&& out,
254  unsigned int maxLines, std::string indent = " ",
255  CallInfoPrinter::opt const* callInfoOptions = nullptr
256  );
257 
258  //----------------------------------------------------------------------------
259  /**
260  * @brief Class triggering a `static_assert` failure.
261  * @tparam T type accompanying the assertion
262  * @tparam Enable assertion will fail only if `Enable` expands to `true`
263  * @addtogroup MetaprogrammingBase
264  *
265  * Instantiating this class anywhere (where it's legit) will trigger a static
266  * assertion failure. Since the error message emitted by the compiler usually
267  * contains an expansion of the template parameters, it is then possible to
268  * see the "value" of type `T` that was used when the assertion failed.
269  * The argument `Enable` allows to tune when the assertion should fail.
270  *
271  * For the following example, we want to investigate the value of the type
272  * `element_type`, which is provided, among others, by `std::unique_ptr`.
273  * We want to find out the exact type `element_type` of the collection type
274  * passed to `OurClass`, but only when the collection type is, say, not
275  * constant:
276  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
277  * template <typename Coll>
278  * struct OurClass {
279  *
280  * using Collection_t = Coll;
281  *
282  * using value_type = typename Collection_t::element_type;
283  *
284  * // DEBUG: have the compiler print `value_type`
285  * lar::debug::static_assert_on
286  * <value_type, std::is_const_v<std::remove_reference_t<Coll>>>
287  * debugVar;
288  *
289  * }; // struct OurClass
290  *
291  *
292  * // this should never trigger a static assertion failure:
293  * OurClass<std::unique_ptr<double>> doubleData;
294  *
295  * // this triggers a static assertion failure:
296  * OurClass<std::unique_ptr<double[4]> const fourVectorData;
297  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
298  * (a working example is provided in `DebugUtils_test.h`).
299  * The output with GCC 7.2 is similar to the following:
300  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
301  * In file included from larcorealg/test/CoreUtils/DebugUtils_test.cc:17:0:
302  * larcorealg/larcorealg/CoreUtils/DebugUtils.h: In instantiation of ‘struct lar::debug::details::THE_TYPE_IS<int [10]>’:
303  * larcorealg/larcorealg/CoreUtils/DebugUtils.h:476:29: required from ‘struct lar::debug::static_assert_on<int [10], true>’
304  * larcorealg/test/CoreUtils/DebugUtils_test.cc:49:5: required from ‘struct OurClass<const std::unique_ptr<int [10]> >’
305  * larcorealg/test/CoreUtils/DebugUtils_test.cc:61:51: required from here
306  * larcorealg/larcorealg/CoreUtils/DebugUtils.h:467:7: error: static assertion failed: static_assert_on<T>: check the error message ("THE_TYPE_IS<>") for expansion of type `T`.
307  * static_assert(::util::always_false_v<T>,
308  * ^~~~~~~~~~~~~
309  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
310  * The message of the assertion points to the key string ("THE_TYPE_IS"), and
311  * it can be seen in the second line of this excerpt that the information is
312  * printed as `struct lar::debug::details::THE_TYPE_IS<int [10]>`.
313  * This is Clang 5.0:
314  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
315  * In file included from larcorealg/test/CoreUtils/DebugUtils_test.cc:17:
316  * larcorealg/larcorealg/CoreUtils/DebugUtils.h:451:7: error: static_assert failed "static_assert_on<T>: check the error message (\"THE_TYPE_IS<>\") for expansion of type `T`."
317  * static_assert(::util::always_false_v<T>,
318  * ^ ~~~~~~~~~~~~~~~~~~~~~~~~~
319  * larcorealg/larcorealg/CoreUtils/DebugUtils.h:460:29: note: in instantiation of template class 'lar::debug::details::THE_TYPE_IS<int [10]>' requested here
320  * details::THE_TYPE_IS<T> _;
321  * ^
322  * larcorealg/test/CoreUtils/DebugUtils_test.cc:49:5: note: in instantiation of template class 'lar::debug::static_assert_on<int [10], true>' requested here
323  * debugVar;
324  * ^
325  * larcorealg/test/CoreUtils/DebugUtils_test.cc:61:10: note: in instantiation of template class 'OurClass<const std::__1::unique_ptr<int [10], std::__1::default_delete<int [10]> > >' requested here
326  * (void) OurClass<std::unique_ptr<int[10]> const>();
327  * ^
328  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
329  * where the type can be read in the message of the first note.
330  */
331  template <typename T, bool Enable /* = true */>
333 
334 
335  //----------------------------------------------------------------------------
336 
337 
338 } // namespace lar::debug
339 
340 
341 //------------------------------------------------------------------------------
342 //--- template implementation
343 //------------------------------------------------------------------------------
344 namespace lar::debug {
345 
346  //----------------------------------------------------------------------------
347  template <typename T>
348  inline std::string demangle(T const* /* = nullptr */)
349  { return cet::demangle_symbol(typeid(std::decay_t<T>).name()); }
350 
351 
352  //----------------------------------------------------------------------------
353  template <typename Stream>
354  void CallInfoPrinter::print(Stream&& out, CallInfo_t const& info) const {
355  if (!info) {
356  out << info.original << " (?)";
357  return;
358  }
359 
360  if (info.mangledFunctionName.empty()) {
361  if (options.has(opt::library)) {
362  out << "in "
363  << (options.has(opt::shortLibrary)? info.shortLibrary(): info.libraryName);
364  }
365  else out << "unknown";
366  }
367  else {
368  // auto flags = out.flags(); // not available in message facility streams
369  out << info.function();
370  auto offset = info.offset;
371  if (offset && options.has(opt::offset)) {
372  out << " [";
373  if (offset > 0) out << '+';
374  else {
375  out << '-';
376  offset = -offset;
377  }
378  out << std::showbase << std::hex << offset << "]";
379  } // if has offset
380  // out << std::ios::setiosflags(flags);
381  if (!info.libraryName.empty() && options.has(opt::library)) {
382  out << " in "
383  << (options.has(opt::shortLibrary)? info.shortLibrary(): info.libraryName);
384  }
385  }
386  if (info.address && options.has(opt::address))
387  out << " at " << ((void*) info.address);
388  out << std::flush;
389  } // CallInfoPrinter::print()
390 
391 
392  //----------------------------------------------------------------------------
393  template <typename Stream>
394  inline Stream& operator<< (Stream&& out, CallInfo_t const& info) {
395  CallInfoPrinter print;
396  print(std::forward<Stream>(out), info);
397  return out;
398  }
399 
400 
401  //----------------------------------------------------------------------------
402  template <typename Stream>
404  unsigned int nSkip = std::max(options.skipLines, 0U);
405  std::vector<void*> buffer
406  (nSkip + std::max(options.maxLines, 200U), nullptr);
407 
408  unsigned int const nItems
409  = (unsigned int) backtrace(buffer.data(), buffer.size());
410 
411  // convert the calls in the buffer into a vector of strings
412  char** symbols = backtrace_symbols(buffer.data(), buffer.size());
413  if (!symbols) {
414  out << options.firstIndent << "<failed to get the call stack>\n"
415  << std::flush;
416  return;
417  }
418  std::vector<CallInfo_t> callStack;
419  for (size_t i = 0; i < buffer.size(); ++i)
420  callStack.push_back((const char*) symbols[i]);
421  std::free(symbols);
422 
423  size_t lastItem = nSkip + options.maxLines;
424  if (lastItem > nItems) lastItem = nItems;
425  if (lastItem >= buffer.size()) --lastItem;
426 
427  CallInfoPrinter print(options.callInfoOptions);
428  for (size_t i = nSkip; i < lastItem; ++i) {
429  out << (i == 0? options.firstIndent: options.indent);
430  print(std::forward<Stream>(out), callStack[i]);
431  out << "\n";
432  }
433  if ((lastItem < nItems) && options.countOthers) {
434  out << options.indent << " ... and other " << (nItems - lastItem);
435  if (nItems == buffer.size()) out << " (or more)";
436  out << " levels\n";
437  }
438  out << std::flush;
439 
440  } // printBacktrace()
441 
442 
443  //----------------------------------------------------------------------------
444  template <typename Stream>
446  Stream&& out,
447  unsigned int maxLines, std::string indent /* = " " */,
448  CallInfoPrinter::opt const* callInfoOptions /* = nullptr */
449  )
450  {
452  options.maxLines = maxLines;
453  options.indent = options.firstIndent = indent;
454  if (callInfoOptions) options.callInfoOptions = *callInfoOptions;
455  printBacktrace(std::forward<Stream>(out), options);
456  }
457 
458 
459  //----------------------------------------------------------------------------
460  namespace details {
461 
462  template <typename T>
463  struct THE_TYPE_IS {
464  // if the assertion condition didn't depend on a template parameters,
465  // the assertion failure error would *always* be triggered
466  static_assert(::util::always_false_v<T>,
467  "static_assert_on<T>: check the error message (\"THE_TYPE_IS<>\") for expansion of type `T`."
468  );
469  }; // THE_TYPE_IS<>
470 
471  } // namespace details
472 
473  template <typename T, bool Enable = true>
474  struct static_assert_on {
475  static_assert_on() = default;
478  }; // static_assert_on<>
479 
480  template <typename T>
481  struct static_assert_on<T, false> {};
482 
483 
484  //----------------------------------------------------------------------------
485 
486 } // namespace lar::debug
487 
488 
489 #endif // LARCOREALG_COREUTILS_DEBUGUTILS_H
static QCString name
Definition: declinfo.cpp:673
std::string original
String from the backtrace, unparsed.
Definition: DebugUtils.h:93
std::string demangle(T const *=nullptr)
Outputs a demangled name for type T.
Definition: DebugUtils.h:348
std::string firstIndent
Special indentation for the first line.
Definition: DebugUtils.h:208
static std::string extract(std::string const &s, range_t const &r)
Translates a range into a string.
Definition: DebugUtils.h:106
unsigned int maxLines
Total number of lines to print.
Definition: DebugUtils.h:204
std::string functionName
Parsed function name, demangled.
Definition: DebugUtils.h:95
Print the library name the function lives in.
Definition: DebugUtils.h:140
static opt defaultOptions()
Returns a set of default options.
Definition: DebugUtils.h:181
Basic C++ metaprogramming utilities.
std::string string
Definition: nybbler.cc:12
void setDefaultOptions()
Sets this object to use a set of default options.
Definition: DebugUtils.h:178
std::string libraryName
Parsed library name.
Definition: DebugUtils.h:94
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:121
CallInfo_t(const char *s)
Definition: DebugUtils.h:68
CallInfoPrinter(opt opts)
Constructor: use specified options.
Definition: DebugUtils.h:163
Print the instruction pointer memory address.
Definition: DebugUtils.h:138
QTextStream & hex(QTextStream &s)
bool has(option_t o) const
Returns whether the specified option is set.
Definition: DebugUtils.h:153
void * address
Function address.
Definition: DebugUtils.h:97
void demangleFunction()
Runs the demangler and stores the result.
Definition: DebugUtils.h:110
Backtrace printing options.
Definition: DebugUtils.h:202
bool countOthers
Whether to print number of omitted lines.
Definition: DebugUtils.h:206
CallInfoPrinter()
Default constructor: use default options.
Definition: DebugUtils.h:160
Class handling the output of information in a CallInfo_t object.
Definition: DebugUtils.h:132
bool operator!() const
Returns whether no information was parsed out of the original.
Definition: DebugUtils.h:74
Use demangled function names when possible.
Definition: DebugUtils.h:139
Print a shorter library name (base name only).
Definition: DebugUtils.h:141
std::bitset< NOptions > options
Value of current options.
Definition: DebugUtils.h:146
bool ParseString(std::string const &s)
Returns whether the translation was complete (offset is optional!).
Definition: DebugUtils.cxx:11
std::pair< size_t, size_t > range_t
Definition: DebugUtils.h:64
opt options
Set of current options.
Definition: DebugUtils.h:157
Structure with information about a single call, parsed.
Definition: DebugUtils.h:62
QTextStream & flush(QTextStream &s)
option_t
List of available options.
Definition: DebugUtils.h:137
static int max(int a, int b)
static bool emptyRange(range_t const &r)
Returns whether the range is empty or invalid.
Definition: DebugUtils.h:103
void setUniformIndent(std::string uniformIndent)
Sets all indentation to the same specified uniformIndent string.
Definition: DebugUtils.h:214
CallInfoPrinter::opt callInfoOptions
Options for each single backtrace call information line.
Definition: DebugUtils.h:211
std::string indent
Indentation string for all lines.
Definition: DebugUtils.h:207
opt & set(option_t o, bool set=true)
Set one option o to the specified set value (true by default).
Definition: DebugUtils.h:149
Stream & operator<<(Stream &&out, CallInfo_t const &info)
Helper operator to insert a call information in a stream with default options.
Definition: DebugUtils.h:394
unsigned int skipLines
Number of lines to skip.
Definition: DebugUtils.h:205
std::string shortLibrary() const
Returns only the library name (with suffix).
Definition: DebugUtils.h:85
opts
Definition: ECLAPI.py:241
std::string mangledFunctionName
Parsed function name, unprocessed.
Definition: DebugUtils.h:96
details::THE_TYPE_IS< T > _
Definition: DebugUtils.h:477
std::string const & function() const
Returns the function name (mangled if nothing better).
Definition: DebugUtils.h:81
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:354
void printBacktrace(Stream &&out, BacktracePrintOptions options)
Prints the full backtrace into a stream.
Definition: DebugUtils.h:403
int bool
Definition: qglobal.h:345
void setOptions(opt opts)
Override all the options.
Definition: DebugUtils.h:166
CallInfo_t(std::string const &s)
Definition: DebugUtils.h:67
static QCString * s
Definition: config.cpp:1042
std::ptrdiff_t offset
Instruction pointer offset.
Definition: DebugUtils.h:98
Set of options for printing.
Definition: DebugUtils.h:135
Print the offset from the beginning of function.
Definition: DebugUtils.h:142