DebugUtils.h
Go to the documentation of this file.
1 /**
2  * @file 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 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 COREUTILS_DEBUGUTILS_H
14 #define 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 gar {
37  namespace debug {
38 
39  /** ***********************************************************************
40  * @brief Outputs a demangled name for type T
41  * @param T type whose name must be demangled (optional)
42  * @return a string with demangled name
43  *
44  * It relies on cetlib.
45  * The type to be demangled can be specified either as template argument:
46  *
47  * auto name = gar::debug::demangle<std::string>();
48  *
49  * or via a argument pointer:
50  *
51  * auto name = gar::debug::demangle(this);
52  *
53  *
54  */
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 
60  /// Structure with information about a single call, parsed
61  struct CallInfo_t {
62  private:
63  using range_t = std::pair<size_t, size_t>;
64 
65  public:
67  CallInfo_t(const char* s) { ParseString(std::string(s)); }
68 
69  /// Returns whether there is some information parsed
70  operator bool() const
71  { return !libraryName.empty() || !mangledFunctionName.empty(); }
72  /// Returns whether no information was parsed out of the original
73  bool operator! () const
74  { return libraryName.empty() && mangledFunctionName.empty(); }
75 
76  /// Returns whether the translation was complete (offset is optional!)
77  bool ParseString(std::string const& s);
78 
79  /// Returns the function name (mangled if nothing better)
80  std::string const& function() const
81  { return functionName.empty()? mangledFunctionName: functionName; }
82 
83  /// Returns only the library name (with suffix)
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; ///< string from the backtrace, unparsed
93  std::string libraryName; ///< parsed library name
94  std::string functionName; ///< parsed function name, demangled
95  std::string mangledFunctionName; ///< parsed function name, unprocessed
96  void* address = nullptr; ///< function address
97  std::ptrdiff_t offset = 0; ///< instruction pointer offset
98 
99  private:
100 
101  /// Returns whether the range is empty or invalid
102  static bool emptyRange(range_t const& r) { return r.first >= r.second; }
103 
104  /// Translates a range into a string
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 
108  /// Runs the demangler and stores the result
110  { functionName = cet::demangle_symbol(mangledFunctionName); }
111 
112  /// Fills the information from an original string and parsed ranges
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 
122  /**
123  * @brief Class handling the output of information in a CallInfo_t object
124  *
125  * This class has a "default" print function (also replicated as a call
126  * operator), and a set of options that can be tweaked to change the amount
127  * of information and format to be printed.
128  *
129  */
131  public:
132  /// Set of options for printing
133  struct opt {
134  /// List of available options
135  enum option_t {
136  address, ///< print the instruction pointer memory address
137  demangled, ///< use demangled function names when possible
138  library, ///< print the library name the function lives in
139  shortLibrary, ///< print a shorter library name (base name only)
140  offset, ///< print the offset from the beginning of function
141  NOptions ///< number of available options
142  }; // option_t
143 
144  std::bitset<NOptions> options; ///< value of current options
145 
146  /// Set one option o to the specified set value (true by default)
147  opt& set(option_t o, bool set = true)
148  { options.set(o, set); return *this; }
149 
150  /// Returns whether the specified option is set
151  bool has(option_t o) const { return options.test(o); }
152 
153  }; // opt
154 
155  opt options; ///< set of current options
156 
157  /// Default constructor: use default options
158  CallInfoPrinter() { setDefaultOptions(); }
159 
160  /// Constructor: use specified options
161  CallInfoPrinter(opt opts): options(opts) {}
162 
163  /// Override all the options
164  void setOptions(opt opts) { options = opts; }
165 
166  /// Print the content of info into the stream out, using the current options
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
197  << " in "
198  << (options.has(opt::shortLibrary)? info.shortLibrary(): info.libraryName);
199  }
200  }
201  if (info.address && options.has(opt::address))
202  out << " at " << ((void*) info.address);
203  out << std::flush;
204  } // print()
205 
206  /// Print the content of info into the stream out, using the current options
207  template <typename Stream>
208  void operator() (Stream&& out, CallInfo_t const& info) const
209  { print(std::forward<Stream>(out), info); }
210 
211  /// Sets this object to use a set of default options
212  void setDefaultOptions() { options = defaultOptions(); }
213 
214  /// Returns a set of default options
216  {
217  opt options;
218  options.set(opt::demangled);
219  options.set(opt::library);
220  options.set(opt::shortLibrary);
221  options.set(opt::address);
222  return options;
223  }
224 
225  }; // CallInfoPrinter
226 
227  /// Helper operator to insert a call information in a stream with default options
228  template <typename Stream>
229  inline Stream& operator<< (Stream&& out, CallInfo_t const& info)
230  {
231  CallInfoPrinter print;
232  print(std::forward<Stream>(out), info);
233  return out;
234  }
235 
236  /**
237  * @brief Prints the full backtrace into a stream
238  * @tparam Stream type of output stream
239  * @param out the output stream to insert output into
240  * @param maxLines print at most this many lines in the output (default: 5)
241  * @param indent prepend a string in front of any new line (default: " ")
242  * @param options use these output options (default ones if null pointer)
243  *
244  * The output options are described in CallInfo_t::opt structure.
245  *
246  *
247  */
248  template <typename Stream>
250  Stream&& out,
251  unsigned int maxLines = 5, std::string indent = " ",
252  CallInfoPrinter::opt const* options = nullptr
253  )
254  {
255  constexpr unsigned int nSkip = 1;
256  std::vector<void*> buffer(nSkip + std::max(maxLines, 200U), nullptr);
257 
258  unsigned int nItems
259  = (unsigned int) backtrace(buffer.data(), buffer.size());
260 
261  // convert the calls in the buffer into a vector of strings
262  char** symbols = backtrace_symbols(buffer.data(), buffer.size());
263  if (!symbols) {
264  out << indent << "<failed to get the call stack>" << std::endl;
265  }
266  std::vector<CallInfo_t> callStack;
267  for (size_t i = 0; i < buffer.size(); ++i)
268  callStack.push_back((const char*) symbols[i]);
269  std::free(symbols);
270 
271  size_t lastItem = nSkip + maxLines;
272  if (lastItem > nItems) lastItem = nItems;
273  if (lastItem >= buffer.size()) --lastItem;
274 
275  CallInfoPrinter print;
276  if (options) print.setOptions(*options);
277  for (size_t i = nSkip; i < lastItem; ++i) {
278  out << indent;
279  print(std::forward<Stream>(out), callStack[i]);
280  out << "\n";
281  }
282  if (lastItem < nItems) {
283  out << indent << " ... and other " << (nItems - lastItem);
284  if (nItems == buffer.size()) out << " (or more)";
285  out << " levels\n";
286  }
287  out << std::flush;
288 
289  } // printBacktrace()
290 
291  } // namespace debug
292 } // namespace gar
293 
294 
295 #endif // COREUTILS_DEBUGUTILS_H
296 
static QCString name
Definition: declinfo.cpp:673
std::string const & function() const
Returns the function name (mangled if nothing better)
Definition: DebugUtils.h:80
void * address
function address
Definition: DebugUtils.h:96
bool ParseString(std::string const &s)
Returns whether the translation was complete (offset is optional!)
Definition: DebugUtils.cxx:11
std::string shortLibrary() const
Returns only the library name (with suffix)
Definition: DebugUtils.h:84
std::string libraryName
parsed library name
Definition: DebugUtils.h:93
std::string demangle(T const *=nullptr)
Outputs a demangled name for type T.
Definition: DebugUtils.h:56
option_t
List of available options.
Definition: DebugUtils.h:135
void demangleFunction()
Runs the demangler and stores the result.
Definition: DebugUtils.h:109
std::string string
Definition: nybbler.cc:12
use demangled function names when possible
Definition: DebugUtils.h:137
std::pair< size_t, size_t > range_t
Definition: DebugUtils.h:63
std::ptrdiff_t offset
instruction pointer offset
Definition: DebugUtils.h:97
Set of options for printing.
Definition: DebugUtils.h:133
QTextStream & hex(QTextStream &s)
std::string original
string from the backtrace, unparsed
Definition: DebugUtils.h:92
static opt defaultOptions()
Returns a set of default options.
Definition: DebugUtils.h:215
bool operator!() const
Returns whether no information was parsed out of the original.
Definition: DebugUtils.h:73
static bool emptyRange(range_t const &r)
Returns whether the range is empty or invalid.
Definition: DebugUtils.h:102
CallInfo_t(std::string const &s)
Definition: DebugUtils.h:66
print the offset from the beginning of function
Definition: DebugUtils.h:140
Structure with information about a single call, parsed.
Definition: DebugUtils.h:61
std::string functionName
parsed function name, demangled
Definition: DebugUtils.h:94
std::string mangledFunctionName
parsed function name, unprocessed
Definition: DebugUtils.h:95
opt & set(option_t o, bool set=true)
Set one option o to the specified set value (true by default)
Definition: DebugUtils.h:147
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
QTextStream & flush(QTextStream &s)
opt options
set of current options
Definition: DebugUtils.h:155
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, unsigned int maxLines=5, std::string indent=" ", CallInfoPrinter::opt const *options=nullptr)
Prints the full backtrace into a stream.
Definition: DebugUtils.h:249
static int max(int a, int b)
print a shorter library name (base name only)
Definition: DebugUtils.h:139
CallInfoPrinter()
Default constructor: use default options.
Definition: DebugUtils.h:158
General GArSoft Utilities.
void setOptions(opt opts)
Override all the options.
Definition: DebugUtils.h:164
CallInfoPrinter(opt opts)
Constructor: use specified options.
Definition: DebugUtils.h:161
opts
Definition: ECLAPI.py:241
Class handling the output of information in a CallInfo_t object.
Definition: DebugUtils.h:130
Stream & operator<<(Stream &&out, CallInfo_t const &info)
Helper operator to insert a call information in a stream with default options.
Definition: DebugUtils.h:229
print the instruction pointer memory address
Definition: DebugUtils.h:136
print the library name the function lives in
Definition: DebugUtils.h:138
CallInfo_t(const char *s)
Definition: DebugUtils.h:67
int bool
Definition: qglobal.h:345
static QCString * s
Definition: config.cpp:1042
std::bitset< NOptions > options
value of current options
Definition: DebugUtils.h:144
void setDefaultOptions()
Sets this object to use a set of default options.
Definition: DebugUtils.h:212
QTextStream & endl(QTextStream &s)
bool has(option_t o) const
Returns whether the specified option is set.
Definition: DebugUtils.h:151
static std::string extract(std::string const &s, range_t const &r)
Translates a range into a string.
Definition: DebugUtils.h:105