xapian-core  1.4.27
debuglog.h
Go to the documentation of this file.
1 
4 /* Copyright (C) 2008,2009,2010,2011,2014,2015,2021,2023 Olly Betts
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
21 #ifndef XAPIAN_INCLUDED_DEBUGLOG_H
22 #define XAPIAN_INCLUDED_DEBUGLOG_H
23 
24 // In places where we include library code in non-library contexts, we can't
25 // have debug logging enabled, as the support functions aren't visible, so
26 // we define XAPIAN_REALLY_NO_DEBUG_LOG there.
27 #ifdef XAPIAN_REALLY_NO_DEBUG_LOG
28 # ifdef XAPIAN_DEBUG_LOG
29 # undef XAPIAN_DEBUG_LOG
30 # endif
31 #endif
32 
33 #ifdef XAPIAN_DEBUG_LOG
34 
35 #include "output-internal.h"
36 #include "pretty.h"
37 
38 #include <cstring>
39 #include <ostream>
40 #include <sstream>
41 #include <string>
42 
44 enum debuglog_categories {
45  // Never wanted.
46  DEBUGLOG_CATEGORY_NEVER = 0,
47 
49  DEBUGLOG_CATEGORY_API = ('A' - '@'),
50 
52  DEBUGLOG_CATEGORY_DB = ('D' - '@'),
53 
55  DEBUGLOG_CATEGORY_EXCEPTION = ('X' - '@'),
56 
58  DEBUGLOG_CATEGORY_EXPAND = ('E' - '@'),
59 
61  DEBUGLOG_CATEGORY_MATCH = ('M' - '@'),
62 
64  DEBUGLOG_CATEGORY_QUERYPARSER = ('Q' - '@'),
65 
67  DEBUGLOG_CATEGORY_REMOTE = ('R' - '@'),
68 
70  DEBUGLOG_CATEGORY_REPLICA = ('C' - '@'),
71 
73  DEBUGLOG_CATEGORY_SPELLING = ('S' - '@'),
74 
76  DEBUGLOG_CATEGORY_UNKNOWN = ('U' - '@'),
77 
79  DEBUGLOG_CATEGORY_WTCALC = ('W' - '@'),
80 
82  DEBUGLOG_CATEGORY_QUERY = ('Y' - '@'),
83 
85  DEBUGLOG_CATEGORY_ALWAYS = 31
86 };
87 
89 class DebugLogger {
91  void operator=(const DebugLogger&);
92 
94  DebugLogger(const DebugLogger&);
95 
97  unsigned int categories_mask;
98 
100  int fd;
101 
103  int indent_level;
104 
106  void initialise_categories_mask();
107 
108  public:
110  DebugLogger()
111  : categories_mask(1 << DEBUGLOG_CATEGORY_API), fd(-1), indent_level(0)
112  { }
113 
115  ~DebugLogger();
116 
118  bool is_category_wanted(debuglog_categories category) {
119  // The argument will almost always be constant, so these inline checks
120  // against DEBUGLOG_CATEGORY_ALWAYS and DEBUGLOG_CATEGORY_NEVER will
121  // usually be optimised away, or become the only code path.
122  if (category == DEBUGLOG_CATEGORY_ALWAYS) return true;
123  if (category == DEBUGLOG_CATEGORY_NEVER) return false;
124  if (fd == -1) initialise_categories_mask();
125  return (categories_mask >> category) & 1;
126  }
127 
129  void log_line(debuglog_categories category, const std::string& msg);
130 
131  void indent() { ++indent_level; }
132 
133  void outdent() {
134  if (indent_level) --indent_level;
135  }
136 };
137 
138 namespace Xapian {
144  typedef enum { NO_ARGS } NoArguments_;
145 }
146 
147 inline std::ostream & operator<<(std::ostream &o, Xapian::NoArguments_) {
148  return o;
149 }
150 
151 using Xapian::NO_ARGS;
152 
153 extern DebugLogger xapian_debuglogger_;
154 
156 // Note that MSG can contain '<<' so we don't "protect" it with brackets.
157 #define LOGLINE_ALWAYS_(CATEGORY, MSG) do { \
158  std::ostringstream xapian_debuglog_ostream_; \
159  xapian_debuglog_ostream_ << MSG; \
160  xapian_debuglogger_.log_line(CATEGORY, xapian_debuglog_ostream_.str()); \
161 } while (false)
162 
164 // Note that MSG can contain '<<' so we don't "protect" it with brackets.
165 #define LOGLINE_(CATEGORY, MSG) do { \
166  debuglog_categories xapian_debuglog_category_ = (CATEGORY); \
167  if (xapian_debuglogger_.is_category_wanted(xapian_debuglog_category_)) { \
168  LOGLINE_ALWAYS_(xapian_debuglog_category_, MSG); \
169  } \
170 } while (false)
171 
181 class DebugLogFunc {
183  const void* this_ptr;
184 
186  debuglog_categories category;
187 
189  std::string func;
190 
192  int uncaught_exceptions;
193 
194  static int get_uncaught_exceptions() {
195 #if __cplusplus >= 201703L
196  return std::uncaught_exceptions();
197 #else
198  return int(std::uncaught_exception());
199 #endif
200  }
201 
202  public:
204  DebugLogFunc(const void* this_ptr_, debuglog_categories category_,
205  const char* return_type, const char* func_name,
206  const std::string& params)
207  : this_ptr(this_ptr_), category(category_),
208  uncaught_exceptions(get_uncaught_exceptions())
209  {
210  if (is_category_wanted()) {
211  func.assign(return_type);
212  func += ' ';
213  func += func_name;
214  func += '(';
215  func += params;
216  func += ')';
217  LOGLINE_ALWAYS_(category, '[' << this_ptr << "] " << func);
218  xapian_debuglogger_.indent();
219  }
220  }
221 
223  void log_return_value(const std::string& return_value) {
224  xapian_debuglogger_.outdent();
225  LOGLINE_(category, '[' << this_ptr << "] " << func << " returned: " <<
226  return_value);
227 
228  // Flag that we've logged the return already.
229  category = DEBUGLOG_CATEGORY_NEVER;
230  }
231 
233  bool is_category_wanted() const {
234  return xapian_debuglogger_.is_category_wanted(category);
235  }
236 
242  ~DebugLogFunc() {
243  if (!is_category_wanted()) return;
244  xapian_debuglogger_.outdent();
245  if (get_uncaught_exceptions() > uncaught_exceptions) {
246  // An exception is causing the stack to be unwound.
247  LOGLINE_(category, '[' << this_ptr << "] " << func <<
248  " exited due to exception");
249  } else {
250  LOGLINE_(category, '[' << this_ptr << "] " << func <<
251  " returned (not marked up for return logging)");
252  }
253  }
254 };
255 
265 class DebugLogFuncVoid {
267  const void* this_ptr;
268 
270  debuglog_categories category;
271 
273  std::string func;
274 
276  int uncaught_exceptions;
277 
278  static int get_uncaught_exceptions() {
279 #if __cplusplus >= 201703L
280  return std::uncaught_exceptions();
281 #else
282  return int(std::uncaught_exception());
283 #endif
284  }
285 
286  public:
288  DebugLogFuncVoid(const void* this_ptr_, debuglog_categories category_,
289  const char* func_name,
290  const std::string& params)
291  : this_ptr(this_ptr_), category(category_),
292  uncaught_exceptions(get_uncaught_exceptions())
293  {
294  if (is_category_wanted()) {
295  func.assign("void ");
296  func += func_name;
297  func += '(';
298  func += params;
299  func += ')';
300  LOGLINE_ALWAYS_(category, '[' << this_ptr << "] " << func);
301  xapian_debuglogger_.indent();
302  }
303  }
304 
306  DebugLogFuncVoid(const void* this_ptr_, debuglog_categories category_,
307  const std::string& params,
308  const char* class_name)
309  : this_ptr(this_ptr_), category(category_),
310  uncaught_exceptions(get_uncaught_exceptions())
311  {
312  if (is_category_wanted()) {
313  func.assign(class_name);
314  func += "::";
315  // The ctor name is the last component if there are colons (e.g.
316  // for Query::Internal, the ctor is Internal.
317  const char* ctor_name = std::strrchr(class_name, ':');
318  if (ctor_name)
319  ++ctor_name;
320  else
321  ctor_name = class_name;
322  func += ctor_name;
323  func += '(';
324  func += params;
325  func += ')';
326  LOGLINE_ALWAYS_(category, '[' << this_ptr << "] " << func);
327  xapian_debuglogger_.indent();
328  }
329  }
330 
332  DebugLogFuncVoid(const void* this_ptr_, debuglog_categories category_,
333  const char* class_name)
334  : this_ptr(this_ptr_), category(category_),
335  uncaught_exceptions(get_uncaught_exceptions())
336  {
337  if (is_category_wanted()) {
338  func.assign(class_name);
339  func += "::~";
340  // The dtor name is the last component if there are colons.
341  const char* dtor_name = std::strrchr(class_name, ':');
342  if (dtor_name)
343  ++dtor_name;
344  else
345  dtor_name = class_name;
346  func += dtor_name;
347  func += "()";
348  LOGLINE_(category, '[' << this_ptr << "] " << func);
349  xapian_debuglogger_.indent();
350  }
351  }
352 
354  bool is_category_wanted() const {
355  return xapian_debuglogger_.is_category_wanted(category);
356  }
357 
363  ~DebugLogFuncVoid() {
364  if (!is_category_wanted()) return;
365  xapian_debuglogger_.outdent();
366  const char* reason;
367  if (get_uncaught_exceptions() > uncaught_exceptions) {
368  // An exception is causing the stack to be unwound.
369  reason = " exited due to exception";
370  } else {
371  reason = " returned";
372  }
373  LOGLINE_ALWAYS_(category, '[' << this_ptr << "] " << func << reason);
374  }
375 };
376 
377 #ifdef __GNUC__
378 // __attribute__((unused)) supported since at least GCC 2.95.3.
379 # define XAPIAN_UNUSED __attribute__((unused))
380 #else
381 # define XAPIAN_UNUSED
382 #endif
383 
385 #define LOGCALL(CATEGORY, TYPE, FUNC, PARAMS) \
386  typedef TYPE xapian_logcall_return_type_ XAPIAN_UNUSED; \
387  std::string xapian_logcall_parameters_; \
388  if (xapian_debuglogger_.is_category_wanted(DEBUGLOG_CATEGORY_##CATEGORY)) { \
389  std::ostringstream xapian_logcall_ostream_; \
390  PrettyOStream<std::ostringstream> xapian_logcall_stream_(xapian_logcall_ostream_); \
391  xapian_logcall_stream_ << PARAMS; \
392  xapian_logcall_parameters_ = xapian_logcall_ostream_.str(); \
393  } \
394  DebugLogFunc xapian_logcall_(static_cast<const void*>(this), \
395  DEBUGLOG_CATEGORY_##CATEGORY, #TYPE, FUNC, \
396  xapian_logcall_parameters_)
397 
399 #define LOGCALL_VOID(CATEGORY, FUNC, PARAMS) \
400  std::string xapian_logcall_parameters_; \
401  if (xapian_debuglogger_.is_category_wanted(DEBUGLOG_CATEGORY_##CATEGORY)) { \
402  std::ostringstream xapian_logcall_ostream_; \
403  PrettyOStream<std::ostringstream> xapian_logcall_stream_(xapian_logcall_ostream_); \
404  xapian_logcall_stream_ << PARAMS; \
405  xapian_logcall_parameters_ = xapian_logcall_ostream_.str(); \
406  } \
407  DebugLogFuncVoid xapian_logcall_(static_cast<const void*>(this), \
408  DEBUGLOG_CATEGORY_##CATEGORY, FUNC, \
409  xapian_logcall_parameters_)
410 
412 #define LOGCALL_CTOR(CATEGORY, CLASS, PARAMS) \
413  std::string xapian_logcall_parameters_; \
414  if (xapian_debuglogger_.is_category_wanted(DEBUGLOG_CATEGORY_##CATEGORY)) { \
415  std::ostringstream xapian_logcall_ostream_; \
416  PrettyOStream<std::ostringstream> xapian_logcall_stream_(xapian_logcall_ostream_); \
417  xapian_logcall_stream_ << PARAMS; \
418  xapian_logcall_parameters_ = xapian_logcall_ostream_.str(); \
419  } \
420  DebugLogFuncVoid xapian_logcall_(static_cast<const void*>(this), \
421  DEBUGLOG_CATEGORY_##CATEGORY, \
422  xapian_logcall_parameters_, CLASS)
423 
425 #define LOGCALL_DTOR(CATEGORY, CLASS) \
426  DebugLogFuncVoid xapian_logcall_(static_cast<const void*>(this), \
427  DEBUGLOG_CATEGORY_##CATEGORY, CLASS)
428 
430 #define LOGCALL_STATIC(CATEGORY, TYPE, FUNC, PARAMS) \
431  typedef TYPE xapian_logcall_return_type_ XAPIAN_UNUSED; \
432  std::string xapian_logcall_parameters_; \
433  if (xapian_debuglogger_.is_category_wanted(DEBUGLOG_CATEGORY_##CATEGORY)) { \
434  std::ostringstream xapian_logcall_ostream_; \
435  PrettyOStream<std::ostringstream> xapian_logcall_stream_(xapian_logcall_ostream_); \
436  xapian_logcall_stream_ << PARAMS; \
437  xapian_logcall_parameters_ = xapian_logcall_ostream_.str(); \
438  } \
439  DebugLogFunc xapian_logcall_(0, DEBUGLOG_CATEGORY_##CATEGORY, #TYPE, FUNC, xapian_logcall_parameters_)
440 
442 #define LOGCALL_STATIC_VOID(CATEGORY, FUNC, PARAMS) \
443  std::string xapian_logcall_parameters_; \
444  if (xapian_debuglogger_.is_category_wanted(DEBUGLOG_CATEGORY_##CATEGORY)) { \
445  std::ostringstream xapian_logcall_ostream_; \
446  PrettyOStream<std::ostringstream> xapian_logcall_stream_(xapian_logcall_ostream_); \
447  xapian_logcall_stream_ << PARAMS; \
448  xapian_logcall_parameters_ = xapian_logcall_ostream_.str(); \
449  } \
450  DebugLogFuncVoid xapian_logcall_(0, DEBUGLOG_CATEGORY_##CATEGORY, FUNC, xapian_logcall_parameters_)
451 
453 #define RETURN(A) do { \
454  xapian_logcall_return_type_ xapian_logcall_return_ = A; \
455  if (xapian_logcall_.is_category_wanted()) { \
456  std::ostringstream xapian_logcall_ostream_; \
457  PrettyOStream<std::ostringstream> xapian_logcall_stream_(xapian_logcall_ostream_); \
458  xapian_logcall_stream_ << xapian_logcall_return_; \
459  xapian_logcall_.log_return_value(xapian_logcall_ostream_.str()); \
460  } \
461  return xapian_logcall_return_; \
462 } while (false)
463 
469 #define LOGLINE(a,b) LOGLINE_(DEBUGLOG_CATEGORY_##a, b)
470 
472 #define LOGVALUE(a,b) LOGLINE_(DEBUGLOG_CATEGORY_##a, #b" = " << b)
473 
483 #define RETURN_TYPE(...) __VA_ARGS__
484 
485 #else
486 
487 #define LOGCALL(CATEGORY, TYPE, FUNC, PARAMS) (void)0
488 #define LOGCALL_VOID(CATEGORY, FUNC, PARAMS) (void)0
489 #define LOGCALL_CTOR(CATEGORY, CLASS, PARAMS) (void)0
490 #define LOGCALL_DTOR(CATEGORY, CLASS) (void)0
491 #define LOGCALL_STATIC(CATEGORY, TYPE, FUNC, PARAMS) (void)0
492 #define LOGCALL_STATIC_VOID(CATEGORY, FUNC, PARAMS) (void)0
493 #define RETURN(A) return A
494 #define LOGLINE(a,b) (void)0
495 #define LOGVALUE(a,b) (void)0
496 
497 #endif
498 
499 #endif // XAPIAN_INCLUDED_DEBUGLOG_H
The Xapian namespace contains public interfaces for the Xapian library.
Definition: compactor.cc:80
category
Each Unicode character is in exactly one of these categories.
Definition: unicode.h:220
Convert types to pretty representations.
Functions for output of strings describing internal Xapian objects.
PrettyOStream< S > & operator<<(PrettyOStream< S > &ps, const T &t)
Default is to output as std::ostream would.
Definition: pretty.h:54