xapian-core  2.0.0
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, see
18  * <https://www.gnu.org/licenses/>.
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.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 = 1 << DEBUGLOG_CATEGORY_API;
98 
100  int fd = -1;
101 
103  int indent_level = 0;
104 
106  void initialise_categories_mask();
107 
108  public:
110  DebugLogger() { }
111 
113  ~DebugLogger();
114 
116  bool is_category_wanted(debuglog_categories category) {
117  // The argument will almost always be constant, so these inline checks
118  // against DEBUGLOG_CATEGORY_ALWAYS and DEBUGLOG_CATEGORY_NEVER will
119  // usually be optimised away, or become the only code path.
120  if (category == DEBUGLOG_CATEGORY_ALWAYS) return true;
121  if (category == DEBUGLOG_CATEGORY_NEVER) return false;
122  if (fd == -1) initialise_categories_mask();
123  return (categories_mask >> category) & 1;
124  }
125 
127  void log_line(debuglog_categories category, const std::string& msg);
128 
129  void indent() { ++indent_level; }
130 
131  void outdent() {
132  if (indent_level) --indent_level;
133  }
134 };
135 
136 namespace Xapian {
142  typedef enum { NO_ARGS } NoArguments_;
143 }
144 
145 inline std::ostream & operator<<(std::ostream &o, Xapian::NoArguments_) {
146  return o;
147 }
148 
149 using Xapian::NO_ARGS;
150 
151 extern DebugLogger xapian_debuglogger_;
152 
154 // Note that MSG can contain '<<' so we don't "protect" it with brackets.
155 #define LOGLINE_ALWAYS_(CATEGORY, MSG) do { \
156  std::ostringstream xapian_debuglog_ostream_; \
157  xapian_debuglog_ostream_ << MSG; \
158  xapian_debuglogger_.log_line(CATEGORY, xapian_debuglog_ostream_.str()); \
159 } while (false)
160 
162 // Note that MSG can contain '<<' so we don't "protect" it with brackets.
163 #define LOGLINE_(CATEGORY, MSG) do { \
164  debuglog_categories xapian_debuglog_category_ = (CATEGORY); \
165  if (xapian_debuglogger_.is_category_wanted(xapian_debuglog_category_)) { \
166  LOGLINE_ALWAYS_(xapian_debuglog_category_, MSG); \
167  } \
168 } while (false)
169 
179 class DebugLogFunc {
181  const void* this_ptr;
182 
184  debuglog_categories category;
185 
187  std::string func;
188 
190  int uncaught_exceptions;
191 
192  static int get_uncaught_exceptions() {
193  return std::uncaught_exceptions();
194  }
195 
196  public:
198  DebugLogFunc(const void* this_ptr_, debuglog_categories category_,
199  const char* return_type, const char* func_name,
200  const std::string& params)
201  : this_ptr(this_ptr_), category(category_),
202  uncaught_exceptions(get_uncaught_exceptions())
203  {
204  if (is_category_wanted()) {
205  func.assign(return_type);
206  func += ' ';
207  func += func_name;
208  func += '(';
209  func += params;
210  func += ')';
211  LOGLINE_ALWAYS_(category, '[' << this_ptr << "] " << func);
212  xapian_debuglogger_.indent();
213  }
214  }
215 
217  void log_return_value(const std::string& return_value) {
218  xapian_debuglogger_.outdent();
219  LOGLINE_(category, '[' << this_ptr << "] " << func << " returned: " <<
220  return_value);
221 
222  // Flag that we've logged the return already.
223  category = DEBUGLOG_CATEGORY_NEVER;
224  }
225 
227  bool is_category_wanted() const {
228  return xapian_debuglogger_.is_category_wanted(category);
229  }
230 
236  ~DebugLogFunc() {
237  if (!is_category_wanted()) return;
238  xapian_debuglogger_.outdent();
239  if (get_uncaught_exceptions() > uncaught_exceptions) {
240  // An exception is causing the stack to be unwound.
241  LOGLINE_(category, '[' << this_ptr << "] " << func <<
242  " exited due to exception");
243  } else {
244  LOGLINE_(category, '[' << this_ptr << "] " << func <<
245  " returned (not marked up for return logging)");
246  }
247  }
248 };
249 
259 class DebugLogFuncVoid {
261  const void* this_ptr;
262 
264  debuglog_categories category;
265 
267  std::string func;
268 
270  int uncaught_exceptions;
271 
272  static int get_uncaught_exceptions() {
273  return std::uncaught_exceptions();
274  }
275 
276  public:
278  DebugLogFuncVoid(const void* this_ptr_, debuglog_categories category_,
279  const char* func_name,
280  const std::string& params)
281  : this_ptr(this_ptr_), category(category_),
282  uncaught_exceptions(get_uncaught_exceptions())
283  {
284  if (is_category_wanted()) {
285  func.assign("void ");
286  func += func_name;
287  func += '(';
288  func += params;
289  func += ')';
290  LOGLINE_ALWAYS_(category, '[' << this_ptr << "] " << func);
291  xapian_debuglogger_.indent();
292  }
293  }
294 
296  DebugLogFuncVoid(const void* this_ptr_, debuglog_categories category_,
297  const std::string& params,
298  const char* class_name)
299  : this_ptr(this_ptr_), category(category_),
300  uncaught_exceptions(get_uncaught_exceptions())
301  {
302  if (is_category_wanted()) {
303  func.assign(class_name);
304  func += "::";
305  // The ctor name is the last component if there are colons (e.g.
306  // for Query::Internal, the ctor is Internal.
307  const char* ctor_name = std::strrchr(class_name, ':');
308  if (ctor_name)
309  ++ctor_name;
310  else
311  ctor_name = class_name;
312  func += ctor_name;
313  func += '(';
314  func += params;
315  func += ')';
316  LOGLINE_ALWAYS_(category, '[' << this_ptr << "] " << func);
317  xapian_debuglogger_.indent();
318  }
319  }
320 
322  DebugLogFuncVoid(const void* this_ptr_, debuglog_categories category_,
323  const char* class_name)
324  : this_ptr(this_ptr_), category(category_),
325  uncaught_exceptions(get_uncaught_exceptions())
326  {
327  if (is_category_wanted()) {
328  func.assign(class_name);
329  func += "::~";
330  // The dtor name is the last component if there are colons.
331  const char* dtor_name = std::strrchr(class_name, ':');
332  if (dtor_name)
333  ++dtor_name;
334  else
335  dtor_name = class_name;
336  func += dtor_name;
337  func += "()";
338  LOGLINE_(category, '[' << this_ptr << "] " << func);
339  xapian_debuglogger_.indent();
340  }
341  }
342 
344  bool is_category_wanted() const {
345  return xapian_debuglogger_.is_category_wanted(category);
346  }
347 
353  ~DebugLogFuncVoid() {
354  if (!is_category_wanted()) return;
355  xapian_debuglogger_.outdent();
356  const char* reason;
357  if (get_uncaught_exceptions() > uncaught_exceptions) {
358  // An exception is causing the stack to be unwound.
359  reason = " exited due to exception";
360  } else {
361  reason = " returned";
362  }
363  LOGLINE_ALWAYS_(category, '[' << this_ptr << "] " << func << reason);
364  }
365 };
366 
367 #ifdef __GNUC__
368 // __attribute__((unused)) supported since at least GCC 2.95.3.
369 # define XAPIAN_UNUSED __attribute__((unused))
370 #else
371 # define XAPIAN_UNUSED
372 #endif
373 
375 #define LOGCALL(CATEGORY, TYPE, FUNC, PARAMS) \
376  typedef TYPE xapian_logcall_return_type_ XAPIAN_UNUSED; \
377  std::string xapian_logcall_parameters_; \
378  if (xapian_debuglogger_.is_category_wanted(DEBUGLOG_CATEGORY_##CATEGORY)) { \
379  std::ostringstream xapian_logcall_ostream_; \
380  PrettyOStream<std::ostringstream> xapian_logcall_stream_(xapian_logcall_ostream_); \
381  xapian_logcall_stream_ << PARAMS; \
382  xapian_logcall_parameters_ = xapian_logcall_ostream_.str(); \
383  } \
384  DebugLogFunc xapian_logcall_(static_cast<const void*>(this), \
385  DEBUGLOG_CATEGORY_##CATEGORY, #TYPE, FUNC, \
386  xapian_logcall_parameters_)
387 
389 #define LOGCALL_VOID(CATEGORY, FUNC, PARAMS) \
390  std::string xapian_logcall_parameters_; \
391  if (xapian_debuglogger_.is_category_wanted(DEBUGLOG_CATEGORY_##CATEGORY)) { \
392  std::ostringstream xapian_logcall_ostream_; \
393  PrettyOStream<std::ostringstream> xapian_logcall_stream_(xapian_logcall_ostream_); \
394  xapian_logcall_stream_ << PARAMS; \
395  xapian_logcall_parameters_ = xapian_logcall_ostream_.str(); \
396  } \
397  DebugLogFuncVoid xapian_logcall_(static_cast<const void*>(this), \
398  DEBUGLOG_CATEGORY_##CATEGORY, FUNC, \
399  xapian_logcall_parameters_)
400 
402 #define LOGCALL_CTOR(CATEGORY, CLASS, PARAMS) \
403  std::string xapian_logcall_parameters_; \
404  if (xapian_debuglogger_.is_category_wanted(DEBUGLOG_CATEGORY_##CATEGORY)) { \
405  std::ostringstream xapian_logcall_ostream_; \
406  PrettyOStream<std::ostringstream> xapian_logcall_stream_(xapian_logcall_ostream_); \
407  xapian_logcall_stream_ << PARAMS; \
408  xapian_logcall_parameters_ = xapian_logcall_ostream_.str(); \
409  } \
410  DebugLogFuncVoid xapian_logcall_(static_cast<const void*>(this), \
411  DEBUGLOG_CATEGORY_##CATEGORY, \
412  xapian_logcall_parameters_, CLASS)
413 
415 #define LOGCALL_DTOR(CATEGORY, CLASS) \
416  DebugLogFuncVoid xapian_logcall_(static_cast<const void*>(this), \
417  DEBUGLOG_CATEGORY_##CATEGORY, CLASS)
418 
420 #define LOGCALL_STATIC(CATEGORY, TYPE, FUNC, PARAMS) \
421  typedef TYPE xapian_logcall_return_type_ XAPIAN_UNUSED; \
422  std::string xapian_logcall_parameters_; \
423  if (xapian_debuglogger_.is_category_wanted(DEBUGLOG_CATEGORY_##CATEGORY)) { \
424  std::ostringstream xapian_logcall_ostream_; \
425  PrettyOStream<std::ostringstream> xapian_logcall_stream_(xapian_logcall_ostream_); \
426  xapian_logcall_stream_ << PARAMS; \
427  xapian_logcall_parameters_ = xapian_logcall_ostream_.str(); \
428  } \
429  DebugLogFunc xapian_logcall_(0, DEBUGLOG_CATEGORY_##CATEGORY, #TYPE, FUNC, xapian_logcall_parameters_)
430 
432 #define LOGCALL_STATIC_VOID(CATEGORY, FUNC, PARAMS) \
433  std::string xapian_logcall_parameters_; \
434  if (xapian_debuglogger_.is_category_wanted(DEBUGLOG_CATEGORY_##CATEGORY)) { \
435  std::ostringstream xapian_logcall_ostream_; \
436  PrettyOStream<std::ostringstream> xapian_logcall_stream_(xapian_logcall_ostream_); \
437  xapian_logcall_stream_ << PARAMS; \
438  xapian_logcall_parameters_ = xapian_logcall_ostream_.str(); \
439  } \
440  DebugLogFuncVoid xapian_logcall_(0, DEBUGLOG_CATEGORY_##CATEGORY, FUNC, xapian_logcall_parameters_)
441 
443 /* Use __VA_ARGS__ so things like `RETURN({1, 2})` work. */
444 #define RETURN(...) do { \
445  xapian_logcall_return_type_ xapian_logcall_return_ = __VA_ARGS__; \
446  if (xapian_logcall_.is_category_wanted()) { \
447  std::ostringstream xapian_logcall_ostream_; \
448  PrettyOStream<std::ostringstream> xapian_logcall_stream_(xapian_logcall_ostream_); \
449  xapian_logcall_stream_ << xapian_logcall_return_; \
450  xapian_logcall_.log_return_value(xapian_logcall_ostream_.str()); \
451  } \
452  return xapian_logcall_return_; \
453 } while (false)
454 
460 #define LOGLINE(a,b) LOGLINE_(DEBUGLOG_CATEGORY_##a, b)
461 
463 #define LOGVALUE(a,b) LOGLINE_(DEBUGLOG_CATEGORY_##a, #b" = " << b)
464 
474 #define RETURN_TYPE(...) __VA_ARGS__
475 
476 #else
477 
478 #define LOGCALL(CATEGORY, TYPE, FUNC, PARAMS) (void)0
479 #define LOGCALL_VOID(CATEGORY, FUNC, PARAMS) (void)0
480 #define LOGCALL_CTOR(CATEGORY, CLASS, PARAMS) (void)0
481 #define LOGCALL_DTOR(CATEGORY, CLASS) (void)0
482 #define LOGCALL_STATIC(CATEGORY, TYPE, FUNC, PARAMS) (void)0
483 #define LOGCALL_STATIC_VOID(CATEGORY, FUNC, PARAMS) (void)0
484 #define RETURN(...) return __VA_ARGS__
485 #define LOGLINE(a,b) (void)0
486 #define LOGVALUE(a,b) (void)0
487 
488 #endif
489 
490 #endif // XAPIAN_INCLUDED_DEBUGLOG_H
category
Each Unicode character is in exactly one of these categories.
Definition: unicode.h:228
The Xapian namespace contains public interfaces for the Xapian library.
Definition: compactor.cc:82
std::ostream operator<< template for Xapian objects
auto operator<<(std::ostream &os, const T &t) -> decltype(t.get_description(), os)
Definition: output.h:34
Convert types to pretty representations.