All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
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:
67  CallInfo_t(std::string const& s) { ParseString(s); }
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).
85  std::string shortLibrary() const
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.
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
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 "
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 "
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) {
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 
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
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
then echo echo For and will not be changed by echo further linking echo echo B echo The symbol is in the uninitialized data multiple common symbols may appear with the echo same name If the symbol is defined the common echo symbols are treated as undefined references For more echo details on common symbols
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
BEGIN_PROLOG TPC Trig offset(g4 rise time) ProjectToHeight
Definition: CORSIKAGen.fcl:7
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.
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
do one_file $F done echo for F in find $TOP name CMakeLists txt print
CallInfo_t(const char *s)
Definition: DebugUtils.h:68
static constexpr bool
CallInfoPrinter(opt opts)
Constructor: use specified options.
Definition: DebugUtils.h:163
Print the instruction pointer memory address.
Definition: DebugUtils.h:138
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
A range (interval) of integers.
Use demangled function names when possible.
Definition: DebugUtils.h:139
Number of available options.
Definition: DebugUtils.h:143
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
option_t
List of available options.
Definition: DebugUtils.h:137
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
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:174
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
then echo File list $list not found else cat $list while read file do echo $file sed s
Definition: file_to_url.sh:60
then echo echo For and will not be changed by echo further linking echo echo B echo The symbol is in the uninitialized data multiple common symbols may appear with the echo same name If the symbol is defined the common echo symbols are treated as undefined references For more echo details on common see the discussion of warn common echo in *Note Linker options
std::string mangledFunctionName
Parsed function name, unprocessed.
Definition: DebugUtils.h:96
then echo fcl name
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
esac echo uname r
void setOptions(opt opts)
Override all the options.
Definition: DebugUtils.h:166
CallInfo_t(std::string const &s)
Definition: DebugUtils.h:67
bnb BNB Stream
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