LArSoft  v06_85_00
Liquid Argon Software toolkit - http://larsoft.org/
DebugUtils.h
Go to the documentation of this file.
1 
13 #ifndef LARCORE_COREUTILS_DEBUGUTILS_H
14 #define LARCORE_COREUTILS_DEBUGUTILS_H 1
15 
16 // framework and support libraries
17 #include "cetlib_except/demangle.h"
18 
19 // C/C++ standard libraries
20 #include <cstddef> // std::ptrdiff_t
21 #include <cstdlib> // std::free()
22 #include <utility> // std::pair<>
23 #include <vector>
24 #include <string>
25 #include <sstream>
26 #include <bitset>
27 #include <typeinfo>
28 #include <ostream>
29 #include <utility> // std::forward()
30 
31 // non-standard libraries
32 #include <execinfo.h> // backtrace()...
33 // #include <experimental/filesystem> // std::experimental::filesystem::path
34 
35 
36 namespace lar {
37  namespace debug {
38 
55  template <typename T>
56  inline std::string demangle(T const* = nullptr)
57  { return cet::demangle_symbol(typeid(std::decay_t<T>).name()); }
58 
59 
61  struct CallInfo_t {
62  private:
63  using range_t = std::pair<size_t, size_t>;
64 
65  public:
66  CallInfo_t(std::string const& s) { ParseString(s); }
67  CallInfo_t(const char* s) { ParseString(std::string(s)); }
68 
70  operator bool() const
71  { return !libraryName.empty() || !mangledFunctionName.empty(); }
73  bool operator! () const
74  { return libraryName.empty() && mangledFunctionName.empty(); }
75 
77  bool ParseString(std::string const& s);
78 
80  std::string const& function() const
81  { return functionName.empty()? mangledFunctionName: functionName; }
82 
84  std::string shortLibrary() const
85  {
86  size_t sep = libraryName.rfind('/');
87  return (sep == std::string::npos)
88  ? libraryName: libraryName.substr(sep + 1);
89  // return std::experimental::filesystem::path(libraryName).filename();
90  }
91 
92  std::string original;
93  std::string libraryName;
94  std::string functionName;
95  std::string mangledFunctionName;
96  void* address = nullptr;
97  std::ptrdiff_t offset = 0;
98 
99  private:
100 
102  static bool emptyRange(range_t const& r) { return r.first >= r.second; }
103 
105  static std::string extract(std::string const& s, range_t const& r)
106  { return emptyRange(r)? "": s.substr(r.first, r.second - r.first); }
107 
110  { functionName = cet::demangle_symbol(mangledFunctionName); }
111 
113  void setAll(
114  std::string const& s,
115  range_t addressStr, range_t libraryStr,
116  range_t functionStr, range_t offsetStr
117  );
118 
119  }; // CallInfo_t
120 
121 
131  public:
133  struct opt {
135  enum option_t {
141  NOptions
142  }; // option_t
143 
144  std::bitset<NOptions> options;
145 
147  opt& set(option_t o, bool set = true)
148  { options.set(o, set); return *this; }
149 
151  bool has(option_t o) const { return options.test(o); }
152 
153  }; // opt
154 
156 
158  CallInfoPrinter() { setDefaultOptions(); }
159 
161  CallInfoPrinter(opt opts): options(opts) {}
162 
164  void setOptions(opt opts) { options = opts; }
165 
167  template <typename Stream>
168  void print(Stream&& out, CallInfo_t const& info) const {
169  if (!info) {
170  out << info.original << " (?)";
171  return;
172  }
173 
174  if (info.mangledFunctionName.empty()) {
175  if (options.has(opt::library)) {
176  out << "in "
177  << (options.has(opt::shortLibrary)? info.shortLibrary(): info.libraryName);
178  }
179  else out << "unknown";
180  }
181  else {
182  // auto flags = out.flags(); // not available in message facility streams
183  out << info.function();
184  auto offset = info.offset;
185  if (offset && options.has(opt::offset)) {
186  out << " [";
187  if (offset > 0) out << '+';
188  else {
189  out << '-';
190  offset = -offset;
191  }
192  out << std::showbase << std::hex << offset << "]";
193  } // if has offset
194  // out << std::ios::setiosflags(flags);
195  if (!info.libraryName.empty() && options.has(opt::library)) {
196  out << " in "
197  << (options.has(opt::shortLibrary)? info.shortLibrary(): info.libraryName);
198  }
199  }
200  if (info.address && options.has(opt::address))
201  out << " at " << ((void*) info.address);
202  out << std::flush;
203  } // print()
204 
206  template <typename Stream>
207  void operator() (Stream&& out, CallInfo_t const& info) const
208  { print(std::forward<Stream>(out), info); }
209 
211  void setDefaultOptions() { options = defaultOptions(); }
212 
215  {
216  opt options;
217  options.set(opt::demangled);
218  options.set(opt::library);
219  options.set(opt::shortLibrary);
220  options.set(opt::address);
221  return options;
222  }
223 
224  }; // CallInfoPrinter
225 
227  template <typename Stream>
228  inline Stream& operator<< (Stream&& out, CallInfo_t const& info)
229  {
230  CallInfoPrinter print;
231  print(std::forward<Stream>(out), info);
232  return out;
233  }
234 
237 
238  unsigned int maxLines = 5;
239  unsigned int skipLines = 1;
240  bool countOthers = true;
241  std::string indent;
242  std::string firstIndent;
243 
246 
248  void setUniformIndent(std::string uniformIndent)
249  { indent = firstIndent = uniformIndent; }
250 
251  }; // struct BacktracePrintOptions
252 
253 
261  template <typename Stream>
262  void printBacktrace(Stream&& out, BacktracePrintOptions options) {
263  unsigned int nSkip = std::max(options.skipLines, 0U);
264  std::vector<void*> buffer
265  (nSkip + std::max(options.maxLines, 200U), nullptr);
266 
267  unsigned int const nItems
268  = (unsigned int) backtrace(buffer.data(), buffer.size());
269 
270  // convert the calls in the buffer into a vector of strings
271  char** symbols = backtrace_symbols(buffer.data(), buffer.size());
272  if (!symbols) {
273  out << options.firstIndent << "<failed to get the call stack>\n"
274  << std::flush;
275  return;
276  }
277  std::vector<CallInfo_t> callStack;
278  for (size_t i = 0; i < buffer.size(); ++i)
279  callStack.push_back((const char*) symbols[i]);
280  std::free(symbols);
281 
282  size_t lastItem = nSkip + options.maxLines;
283  if (lastItem > nItems) lastItem = nItems;
284  if (lastItem >= buffer.size()) --lastItem;
285 
286  CallInfoPrinter print(options.callInfoOptions);
287  for (size_t i = nSkip; i < lastItem; ++i) {
288  out << (i == 0? options.firstIndent: options.indent);
289  print(std::forward<Stream>(out), callStack[i]);
290  out << "\n";
291  }
292  if ((lastItem < nItems) && options.countOthers) {
293  out << options.indent << " ... and other " << (nItems - lastItem);
294  if (nItems == buffer.size()) out << " (or more)";
295  out << " levels\n";
296  }
297  out << std::flush;
298 
299  } // printBacktrace()
300 
306  template <typename Stream>
307  void printBacktrace(Stream&& out)
308  { printBacktrace(std::forward<Stream>(out), BacktracePrintOptions()); }
309 
322  template <typename Stream>
324  Stream&& out,
325  unsigned int maxLines, std::string indent = " ",
326  CallInfoPrinter::opt const* callInfoOptions = nullptr
327  )
328  {
329  BacktracePrintOptions options;
330  options.maxLines = maxLines;
331  options.indent = options.firstIndent = indent;
332  if (callInfoOptions) options.callInfoOptions = *callInfoOptions;
333  printBacktrace(std::forward<Stream>(out), options);
334  }
335 
336 
337  } // namespace debug
338 } // namespace lar
339 
340 
341 #endif // LARCORE_COREUTILS_DEBUGUTILS_H
342 
std::string original
String from the backtrace, unparsed.
Definition: DebugUtils.h:92
std::string demangle(T const *=nullptr)
Outputs a demangled name for type T.
Definition: DebugUtils.h:56
std::string firstIndent
Special indentation for the first line.
Definition: DebugUtils.h:242
Float_t s
Definition: plot.C:23
static std::string extract(std::string const &s, range_t const &r)
Translates a range into a string.
Definition: DebugUtils.h:105
unsigned int maxLines
Total number of lines to print.
Definition: DebugUtils.h:238
std::string functionName
Parsed function name, demangled.
Definition: DebugUtils.h:94
Print the library name the function lives in.
Definition: DebugUtils.h:138
static opt defaultOptions()
Returns a set of default options.
Definition: DebugUtils.h:214
void setDefaultOptions()
Sets this object to use a set of default options.
Definition: DebugUtils.h:211
std::string libraryName
Parsed library name.
Definition: DebugUtils.h:93
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:67
CallInfoPrinter(opt opts)
Constructor: use specified options.
Definition: DebugUtils.h:161
Print the instruction pointer memory address.
Definition: DebugUtils.h:136
bool has(option_t o) const
Returns whether the specified option is set.
Definition: DebugUtils.h:151
Stream & operator<<(Stream &&out, CallInfo_t const &info)
Helper operator to insert a call information in a stream with default options.
Definition: DebugUtils.h:228
void * address
Function address.
Definition: DebugUtils.h:96
void demangleFunction()
Runs the demangler and stores the result.
Definition: DebugUtils.h:109
Backtrace printing options.
Definition: DebugUtils.h:236
bool countOthers
Whether to print number of omitted lines.
Definition: DebugUtils.h:240
CallInfoPrinter()
Default constructor: use default options.
Definition: DebugUtils.h:158
Class handling the output of information in a CallInfo_t object.
Definition: DebugUtils.h:130
Int_t max
Definition: plot.C:27
bool operator!() const
Returns whether no information was parsed out of the original.
Definition: DebugUtils.h:73
Use demangled function names when possible.
Definition: DebugUtils.h:137
Print a shorter library name (base name only).
Definition: DebugUtils.h:139
std::bitset< NOptions > options
Value of current options.
Definition: DebugUtils.h:144
bool ParseString(std::string const &s)
Returns whether the translation was complete (offset is optional!).
Definition: DebugUtils.cxx:11
DebugStuff debug
Definition: DebugStruct.cxx:4
std::pair< size_t, size_t > range_t
Definition: DebugUtils.h:63
opt options
Set of current options.
Definition: DebugUtils.h:155
std::string indent(std::size_t const i)
Structure with information about a single call, parsed.
Definition: DebugUtils.h:61
option_t
List of available options.
Definition: DebugUtils.h:135
static bool emptyRange(range_t const &r)
Returns whether the range is empty or invalid.
Definition: DebugUtils.h:102
void setUniformIndent(std::string uniformIndent)
Sets all indentation to the same specified uniformIndent string.
Definition: DebugUtils.h:248
CallInfoPrinter::opt callInfoOptions
Options for each single backtrace call information line.
Definition: DebugUtils.h:245
std::string indent
Indentation string for all lines.
Definition: DebugUtils.h:241
opt & set(option_t o, bool set=true)
Set one option o to the specified set value (true by default).
Definition: DebugUtils.h:147
unsigned int skipLines
Number of lines to skip.
Definition: DebugUtils.h:239
LArSoft-specific namespace.
std::string shortLibrary() const
Returns only the library name (with suffix).
Definition: DebugUtils.h:84
std::string mangledFunctionName
Parsed function name, unprocessed.
Definition: DebugUtils.h:95
std::string const & function() const
Returns the function name (mangled if nothing better).
Definition: DebugUtils.h:80
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:168
void printBacktrace(Stream &&out, BacktracePrintOptions options)
Prints the full backtrace into a stream.
Definition: DebugUtils.h:262
void setOptions(opt opts)
Override all the options.
Definition: DebugUtils.h:164
CallInfo_t(std::string const &s)
Definition: DebugUtils.h:66
std::ptrdiff_t offset
Instruction pointer offset.
Definition: DebugUtils.h:97
Set of options for printing.
Definition: DebugUtils.h:133
Print the offset from the beginning of function.
Definition: DebugUtils.h:140