HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
debug.h
Go to the documentation of this file.
1 //
2 // Copyright 2016 Pixar
3 //
4 // Licensed under the terms set forth in the LICENSE.txt file available at
5 // https://openusd.org/license.
6 //
7 #ifndef PXR_BASE_TF_DEBUG_H
8 #define PXR_BASE_TF_DEBUG_H
9 
10 /// \file tf/debug.h
11 /// \ingroup group_tf_DebuggingOutput
12 /// Conditional debugging output class and macros.
13 
14 #include "pxr/pxr.h"
15 #include "pxr/base/tf/api.h"
16 #include "pxr/base/tf/tf.h"
17 #include "pxr/base/tf/enum.h"
20 #include "pxr/base/tf/stopwatch.h"
22 #include "pxr/base/arch/hints.h"
23 
24 #include <atomic>
25 #include <cstdio>
26 #include <string>
27 #include <vector>
28 
30 
31 class Tf_DebugSymbolRegistry;
32 
33 /// \addtogroup group_tf_DebuggingOutput
34 ///@{
35 
36 /// \class TfDebug
37 ///
38 /// Enum-based debugging messages.
39 ///
40 /// The \c TfDebug class encapsulates a simple enum-based conditional
41 /// debugging message system. It is meant as a tool for developers, and
42 /// \e NOT as a means of issuing diagnostic messages to end-users. (This is
43 /// not strictly true. The TfDebug class is extremely useful and has many
44 /// properties that make its use attractive for issuing messages to end-users.
45 /// However, for this purpose, please use the \c TF_INFO macro which more
46 /// clearly indicates its intent.)
47 ///
48 /// The features of \c TfDebug are:
49 /// \li Debugging messages/calls for an entire enum group can be
50 /// compiled out-of-existence.
51 /// \li The cost of checking if a specific message should be printed
52 /// at runtime (assuming the enum group of the message has not been
53 /// compile-time disabled) is a single inline array lookup,
54 /// with a compile-time index into a global array.
55 ///
56 /// The use of the facility is simple:
57 /// \code
58 /// // header file
59 /// #include "pxr/base/tf/debug.h"
60 /// TF_DEBUG_CODES(MY_E1, MY_E2, MY_E3);
61 ///
62 /// // source file
63 /// TF_DEBUG(MY_E2).Msg("something about e2\n");
64 ///
65 /// TF_DEBUG(MY_E3).Msg("val = %d\n", value);
66 /// \endcode
67 ///
68 /// The code in the header file declares the debug symbols to use. Under
69 /// the hood, this creates an enum with the values given in the argument to
70 /// TF_DEBUG_CODES, along with a first and last sentinel values and passes
71 /// that to TF_DEBUG_RANGE.
72 ///
73 /// If you need to obtain the enum type name, use decltype(SOME_ENUM_VALUE).
74 ///
75 /// In the source file, the indicated debugging messages are printed
76 /// only if the debugging symbols are enabled. Effectively, the construct
77 /// \code
78 /// TF_DEBUG(MY_E1).Msg(msgExpr)
79 /// \endcode
80 /// is translated to
81 /// \code
82 /// if (symbol-MY_E1-is-enabled)
83 /// output(msgExpr)
84 /// \endcode
85 ///
86 /// The implications are that \c msgExpr is only evaluated if symbol \c MY_E1
87 /// symbol is enabled.
88 ///
89 /// To totally disable TF_DEBUG output for a set of codes at compile time,
90 /// declare the codes using
91 /// TF_CONDITIONALLY_COMPILE_TIME_ENABLED_DEBUG_CODES(condition, ...) where
92 /// ... is all the debug codes. If 'condition' is false at compile time then
93 /// all TF_DEBUG().Msg()s for these codes are elminated at compile time, so they
94 /// have zero cost.
95 ///
96 /// Most commonly debug symbols are inactive by default, but can be turned
97 /// on either by an environment variable \c TF_DEBUG, or interactively once
98 /// a program has started.
99 ///
100 /// \code
101 /// TfDebug::DisableAll<MyDebugCodes>(); // disable everything
102 ///
103 /// TfDebug::Enable(MY_E1); // enable just MY_E1
104 /// \endcode
105 ///
106 /// Description strings may be associated with debug codes as follows:
107 /// \code
108 /// // source file xyz/debugCodes.cpp
109 ///
110 /// #include "proj/my/debugCodes.h"
111 /// #include "pxr/base/tf/debug.h"
112 /// #include "pxr/base/tf/registryManager.h"
113 ///
114 /// TF_REGISTRY_FUNCTION(TfDebug) {
115 /// TF_DEBUG_ENVIRONMENT_SYMBOL(MY_E1, "loading of blah-blah files");
116 /// TF_DEBUG_ENVIRONMENT_SYMBOL(MY_E2, "parsing of mdl code");
117 /// // etc.
118 /// }
119 /// \endcode
120 ///
121 ///
122 class TfDebug {
123  enum _NodeState { _NodeUninitialized, _NodeDisabled, _NodeEnabled };
124 
125 public:
126  /// Mark debugging as enabled for enum value \c val.
127  ///
128  /// The default state for all debugging symbols is disabled. Note that the
129  /// template parameter is deduced from \c val:
130  /// \code
131  /// TfDebug::Enable(MY_E3);
132  /// \endcode
133  template <class T>
134  static void Enable(T val) {
135  _SetNode(_GetNode(val), Tf_DebugGetEnumName(val), true);
136  }
137 
138  /// Mark debugging as disabled for enum value \c val.
139  template <class T>
140  static void Disable(T val) {
141  _SetNode(_GetNode(val), Tf_DebugGetEnumName(val), false);
142  }
143 
144  /// Mark debugging as enabled for all enum values of type \c T.
145  ///
146  /// Note that the template parameter must be explicitly supplied:
147  /// \code
148  /// TfDebug::EnableAll<MyDebugCodes>()
149  /// \endcode
150  template <class T>
151  static void EnableAll() {
152  const int n = _Traits<T>::NumCodes;
153  for (int i = 0; i != n; ++i) {
154  T code = static_cast<T>(i);
155  _SetNode(_GetNode(code), Tf_DebugGetEnumName(code), true);
156  }
157  }
158 
159  /// Mark debugging as disabled for all enum values of type \c T.
160  template <class T>
161  static void DisableAll() {
162  const int n = _Traits<T>::NumCodes;
163  for (int i = 0; i != n; ++i) {
164  T code = static_cast<T>(i);
165  _SetNode(_GetNode(code), Tf_DebugGetEnumName(code), false);
166  }
167  }
168 
169  /// True if debugging is enabled for the enum value \c val.
170  ///
171  /// Note that not only must the specific enum value \c val be marked as
172  /// enabled, but the enum type \c T must be globally enabled; this is
173  /// controlled by the first argument to the
174  /// \c TF_CONDITIONALLY_COMPILE_TIME_ENABLED_DEBUG_CODES() macro.
175  template <class T>
176  static bool IsEnabled(T val) {
177  static_assert(_Traits<T>::IsDeclared,
178  "Must declare debug codes with TF_DEBUG_CODES()");
180  _Node &node = _GetNode(val);
181  _NodeState curState = node.state.load();
182  if (ARCH_UNLIKELY(curState == _NodeUninitialized)) {
183  _InitializeNode(_GetNode(val), Tf_DebugGetEnumName(val));
184  curState = node.state.load();
185  }
186  return curState == _NodeEnabled;
187  }
188  return false;
189  }
190 
191  /// True if debugging can be activated at run-time, whether or not it is
192  /// currently enabled.
193  template <class T>
194  static bool IsCompileTimeEnabled() {
195  static_assert(_Traits<T>::IsDeclared,
196  "Must declare debug codes with TF_DEBUG_CODES()");
198  }
199 
200  /// Return the number of debugging symbols of this type.
201  ///
202  /// Returns the number of different enums in the range.
203  template <class T>
204  static size_t GetNumDebugCodes() {
205  static_assert(_Traits<T>::IsDeclared,
206  "Must declare debug codes with TF_DEBUG_CODES()");
207  return _Traits<T>::NumCodes;
208  }
209 
210 #if !defined(doxygen)
211  struct Helper {
212  static TF_API void Msg(const std::string& msg);
213  static TF_API void Msg(const char* msg, ...) ARCH_PRINTF_FUNCTION(1,2);
214  };
215 #endif
216 
217  template <bool B>
218  struct ScopeHelper {
219  ScopeHelper(bool enabled, const char* name) {
220  if ((active = enabled)) {
221  str = name;
222  TfDebug::_ScopedOutput(true, str);
223  }
224  else
225  str = NULL;
226  }
227 
229  if (active)
230  TfDebug::_ScopedOutput(false, str);
231  }
232 
233  bool active;
234  const char* str;
235  };
236 
237  template <bool B>
239  TimedScopeHelper(bool enabled, const char* fmt, ...)
240  ARCH_PRINTF_FUNCTION(3, 4);
242 
243  bool active;
244  std::string str;
246  };
247 
248  /// Set registered debug symbols matching \p pattern to \p value.
249  ///
250  /// All registered debug symbols matching \p pattern are set to \p value.
251  /// The only matching is an exact match with \p pattern, or if \p pattern
252  /// ends with an '*' as is otherwise a prefix of a debug symbols. The
253  /// names of all debug symbols set by this call are returned as a vector.
254  TF_API
255  static std::vector<std::string> SetDebugSymbolsByName(
256  const std::string& pattern, bool value);
257 
258  /// True if the specified debug symbol is set.
259  TF_API
260  static bool IsDebugSymbolNameEnabled(const std::string& name);
261 
262  /// Get a description of all debug symbols and their purpose.
263  ///
264  /// A single string describing all registered debug symbols along with
265  /// short descriptions is returned.
266  TF_API
267  static std::string GetDebugSymbolDescriptions();
268 
269  /// Get a listing of all debug symbols.
270  TF_API
271  static std::vector<std::string> GetDebugSymbolNames();
272 
273  /// Get a description for the specified debug symbol.
274  ///
275  /// A short description of the debug symbol is returned. This is the same
276  /// description string that is embedded in the return value of
277  /// GetDebugSymbolDescriptions.
278  TF_API
279  static std::string GetDebugSymbolDescription(const std::string& name);
280 
281  /// Direct debug output to \a either stdout or stderr.
282  ///
283  /// Note that \a file MUST be either stdout or stderr. If not, issue an
284  /// error and do nothing. Debug output is issued to stdout by default.
285  /// If the environment variable TF_DEBUG_OUTPUT_FILE is set to 'stderr',
286  /// then output is issued to stderr by default.
287  TF_API
288  static void SetOutputFile(FILE *file);
289 
290  struct _Node;
291 
292  // Public, to be used in TF_DEBUG_ENVIRONMENT_SYMBOL() macro,
293  // but not meant to be used otherwise.
294  template <class T>
295  static void _RegisterDebugSymbol(
296  T enumVal, char const *name, char const *descrip) {
297  static_assert(_Traits<T>::IsDeclared,
298  "Must declare debug codes with TF_DEBUG_CODES()");
299  const int index = static_cast<int>(enumVal);
300  const int numCodes = _Traits<T>::NumCodes;
301  if (ARCH_UNLIKELY(index < 0 || index >= numCodes)) {
302  _ComplainAboutInvalidSymbol(name);
303  return;
304  }
305  _RegisterDebugSymbolImpl(&_GetNode(enumVal), name, descrip);
306  }
307 
308  TF_API
309  static void _RegisterDebugSymbolImpl(_Node *addr, char const *enumName,
310  char const *descrip);
311 
312  // Unfortunately, we need to make both _Traits and _Node, below
313  // public because of their use in macros.
314  // Please treat both as a private data structures!
315 
316  template <class T>
317  struct _Traits {
318  static constexpr bool IsDeclared = false;
319  };
320 
321  // Note: this structure gets initialized statically zero
322  // (_NodeUninitialized) statically.
323  struct _Node {
324  mutable std::atomic<_NodeState> state;
325  };
326 
327 private:
328 
329  template <class T>
330  struct _Data {
332  };
333 
334  template <class T>
335  static _Node &_GetNode(T val) {
336  return _Data<T>::nodes[static_cast<int>(val)];
337  }
338 
340 
341  TF_API
342  static void _InitializeNode(_Node &node, char const *name);
343 
344  TF_API
345  static void _ComplainAboutInvalidSymbol(char const *name);
346 
347  TF_API
348  static void _SetNode(_Node &node, char const *name, bool state);
349 
350  TF_API
351  static void _ScopedOutput(bool start, char const *str);
352 };
353 
354 template <class T>
355 TfDebug::_Node TfDebug::_Data<T>::nodes[];
356 
357 template <>
359  TimedScopeHelper(bool, const char*, ...)
360  ARCH_PRINTF_FUNCTION(3, 4) {
361  }
362 };
363 
364 /// Define debugging symbols
365 ///
366 /// This is a simple macro that takes care of declaring debug codes. Use it as
367 /// follows:
368 /// \code
369 /// TF_DEBUG_CODES(
370 /// MY_E1,
371 /// MY_E2
372 /// );
373 /// \endcode
374 ///
375 /// \hideinitializer
376 #define TF_DEBUG_CODES(...) \
377  TF_CONDITIONALLY_COMPILE_TIME_ENABLED_DEBUG_CODES(true, __VA_ARGS__)
378 
379 /// Define debugging symbols
380 ///
381 /// This is a simple macro that takes care of declaring debug codes, subject to
382 /// a compile-time condition that enables or disables them completely. Use it as
383 /// follows:
384 /// \code
385 /// TF_CONDITIONALLY_COMPILE_TIME_ENABLED_DEBUG_CODES(
386 /// <Enabled State: a compile-time value convertible to bool>
387 /// MY_E1,
388 /// MY_E2
389 /// );
390 /// \endcode
391 ///
392 /// If the Enabled State is true, this is equivalent to the TF_DEBUG_CODES()
393 /// macro. If it is false, then these debug codes are disabled at compile time
394 /// and generated code pays no cost for them.
395 ///
396 /// \hideinitializer
397 #define TF_CONDITIONALLY_COMPILE_TIME_ENABLED_DEBUG_CODES(condition, ...) \
398  enum _TF_DEBUG_ENUM_NAME(__VA_ARGS__) { \
399  __VA_ARGS__ , \
400  TF_PP_CAT( _TF_DEBUG_ENUM_NAME(__VA_ARGS__), __PAST_END) \
401  }; \
402  template <> \
403  struct TfDebug::_Traits<_TF_DEBUG_ENUM_NAME(__VA_ARGS__)> { \
404  static constexpr bool IsDeclared = true; \
405  static constexpr int NumCodes = \
406  TF_PP_CAT(_TF_DEBUG_ENUM_NAME(__VA_ARGS__), __PAST_END); \
407  static constexpr bool CompileTimeEnabled = (condition); \
408  }; \
409  inline char const * \
410  Tf_DebugGetEnumName(_TF_DEBUG_ENUM_NAME(__VA_ARGS__) val) { \
411  constexpr char const *CStrings[] = { \
412  TF_PP_FOR_EACH(_TF_DEBUG_MAKE_STRING, __VA_ARGS__) \
413  }; \
414  return CStrings[static_cast<int>(val)]; \
415  };
416 
417 #define _TF_DEBUG_MAKE_STRING(x) #x,
418 
419 // In the _TF_DEBUG_ENUM_NAME macro below we pass 'dummy' to
420 // _TF_DEBUG_FIRST_CODE as the second argument to ensure that we always
421 // have more than one argument as expected by _TF_DEBUG_FIRST_CODE.
422 #define _TF_DEBUG_ENUM_NAME(...) \
423  TF_PP_CAT(_TF_DEBUG_FIRST_CODE(__VA_ARGS__, dummy), __DebugCodes)
424 
425 #define _TF_DEBUG_FIRST_CODE(first, ...) first
426 
427 /// Evaluate and print debugging message \c msg if \c enumVal is enabled for
428 /// debugging.
429 ///
430 /// This macro is a newer, more convenient form of the \c TF_DEBUG() macro.
431 /// Writing
432 /// \code
433 /// TF_DEBUG_MSG(enumVal, msg, ...);
434 /// \endcode
435 /// is equivalent to
436 /// \code
437 /// TF_DEBUG(enumVal).Msg(msg, ...);
438 /// \endcode
439 ///
440 /// The TF_DEBUG_MSG() macro allows either an std::string argument or
441 /// a printf-like format string followed by a variable number of arguments:
442 /// \code
443 /// TF_DEBUG_MSG(enumVal, "opening file %s\n", file.c_str());
444 ///
445 /// TF_DEBUG_MSG(enumVal, "opening file " + file);
446 /// \endcode
447 ///
448 /// \hideinitializer
449 #define TF_DEBUG_MSG(enumVal, ...) \
450  if (!TfDebug::IsEnabled(enumVal)) /* empty */ ; else TfDebug::Helper().Msg(__VA_ARGS__)
451 
452 /// Evaluate and print debugging message \c msg if \c enumVal is enabled for
453 /// debugging.
454 ///
455 /// The \c TF_DEBUG() macro is used as follows:
456 /// \code
457 /// TF_DEBUG(enumVal).Msg("opening file %s, count = %d\n",
458 /// file.c_str(), count);
459 /// \endcode
460 ///
461 /// If \c enumVal is of enumerated type \c enumType, and \c enumType
462 /// has been enabled for debugging (see \c TF_DEBUG_CODES()), and
463 /// the specific value \c enumVal has been enabled for debugging by a call
464 /// to \c TfDebug::Enable(), then the arguments in the \c Msg() call are
465 /// evaluated and printed. The argument to \c Msg() may either be a
466 /// \c const \c char* and a variable number of arguments, using standard
467 /// printf-formatting rules, or a \c std::string variable:
468 /// \code
469 /// TF_DEBUG(enumVal).Msg("opening file " + file + "\n");
470 /// \endcode
471 ///
472 /// Note that the arguments to \c Msg() are unevaluated when the value
473 /// \c enumVal is not enabled for debugging, so \c Msg() must be free
474 /// of side-effects; however, when \c enumVal is not enabled, there is
475 /// no expense incurred in computing the arguments to \c Msg(). Note
476 /// that if the entire enum type corresponding to \c enumVal is
477 /// disabled (a compile-time determination) then the code for the \e
478 /// entire \c TF_DEBUG().Msg() statement will typically not even be
479 /// generated!
480 ///
481 /// \sa TF_DEBUG_MSG()
482 ///
483 /// \hideinitializer
484 #define TF_DEBUG(enumVal) \
485  if (!TfDebug::IsEnabled(enumVal)) /* empty */ ; else TfDebug::Helper()
486 
487 /// Evaluate and print diagnostic messages intended for end-users.
488 ///
489 /// The TF_INFO(x) macro is cosmetic; it actually just calls the TF_DEBUG
490 /// macro (see above). This macro should be used if its output is intended to
491 /// be seen by end-users.
492 ///
493 /// \hideinitializer
494 #define TF_INFO(x) TF_DEBUG(x)
495 
496 /// Print description and time spent in scope upon beginning and exiting it if
497 /// \p enumVal is enabled for debugging.
498 ///
499 /// The \c TF_DEBUG_TIMED_SCOPE() macro is used as follows:
500 /// \code
501 /// void Attribute::Compute()
502 /// {
503 /// TF_DEBUG_TIMED_SCOPE(ATTR_COMPUTE, "Computing %s", name.c_str());
504 /// ...
505 /// }
506 /// \endcode
507 ///
508 /// When the \c TF_DEBUG_TIMED_SCOPE macro is invoked, a timer is started and
509 /// the supplied description is printed. When the enclosing scope is exited
510 /// (in the example, when Attribute::Compute() finishes) the timer is stopped
511 /// and the scope description and measured time are printed. This allows for
512 /// very fine-grained timing of operations.
513 ///
514 /// Note that if the entire enum type corresponding to \p enumVal is disabled
515 /// (a compile-time determination) then the presence of a
516 /// \c TF_DEBUG_TIMED_SCOPE() macro should not produce any extra generated
517 /// code (in an optimized build). If the enum type is enabled, but the
518 /// particular value \p enumVal is disabled, the cost of the macro should be
519 /// quite minimal; still, it would be best not to embed the macro in functions
520 /// that are called in very tight loops, in final released code.
521 ///
522 /// \hideinitializer
523 #define TF_DEBUG_TIMED_SCOPE(enumVal, ...) \
524  TfDebug::TimedScopeHelper< \
525  TfDebug::_Traits< \
526  std::decay<decltype(enumVal)>::type>::CompileTimeEnabled> \
527  TF_PP_CAT(local__TfScopeDebugSwObject, __LINE__)( \
528  TfDebug::IsEnabled(enumVal), __VA_ARGS__)
529 
530 /// Register description strings with enum symbols for debugging.
531 ///
532 /// This call should be used in source files, not header files, and should
533 /// This macro should usually appear within a
534 /// \c TF_REGISTRY_FUNCTION(TfDebug,...) call. The first argument should be
535 /// the literal name of the enum symbol, while the second argument should be a
536 /// (short) description of what debugging will be enabled if the symbol is
537 /// activated. The enum being registered must be one which is contained in
538 /// some TF_DEBUG_CODES() call. For example:
539 /// \code
540 /// TF_REGISTRY_FUNCTION(TfDebug) {
541 /// TF_DEBUG_ENVIRONMENT_SYMBOL(MY_E1, "loading of blah-blah files");
542 /// TF_DEBUG_ENVIRONMENT_SYMBOL(MY_E2, "parsing of mdl code");
543 /// // etc.
544 /// }
545 /// \endcode
546 ///
547 /// \hideinitializer
548 #define TF_DEBUG_ENVIRONMENT_SYMBOL(VAL, descrip) \
549  if (TfDebug::_Traits< \
550  std::decay<decltype(VAL)>::type>::CompileTimeEnabled) { \
551  TF_ADD_ENUM_NAME(VAL); \
552  TfDebug::_RegisterDebugSymbol(VAL, #VAL, descrip); \
553  }
554 
555 ///@}
556 
558 
559 #endif
static TF_API void SetOutputFile(FILE *file)
static size_t GetNumDebugCodes()
Definition: debug.h:204
static TF_API std::vector< std::string > SetDebugSymbolsByName(const std::string &pattern, bool value)
static TF_API std::string GetDebugSymbolDescription(const std::string &name)
#define TF_API
Definition: api.h:23
GLuint start
Definition: glcorearb.h:475
GLsizei const GLfloat * value
Definition: glcorearb.h:824
static void Disable(T val)
Mark debugging as disabled for enum value val.
Definition: debug.h:140
static TF_API void Msg(const std::string &msg)
GLenum GLenum GLsizei const GLuint GLboolean enabled
Definition: glcorearb.h:2539
static TF_API std::string GetDebugSymbolDescriptions()
Definition: debug.h:122
static void Enable(T val)
Definition: debug.h:134
#define ARCH_UNLIKELY(x)
Definition: hints.h:30
GLdouble n
Definition: glcorearb.h:2008
TfStopwatch stopwatch
Definition: debug.h:245
friend class Tf_DebugSymbolRegistry
Definition: debug.h:339
static void _RegisterDebugSymbol(T enumVal, char const *name, char const *descrip)
Definition: debug.h:295
static constexpr bool IsDeclared
Definition: debug.h:318
static TF_API void _RegisterDebugSymbolImpl(_Node *addr, char const *enumName, char const *descrip)
GLuint const GLchar * name
Definition: glcorearb.h:786
GLushort pattern
Definition: glad.h:2583
static bool IsEnabled(T val)
Definition: debug.h:176
static bool IsCompileTimeEnabled()
Definition: debug.h:194
PXR_NAMESPACE_CLOSE_SCOPE PXR_NAMESPACE_OPEN_SCOPE
Definition: path.h:1425
GLuint index
Definition: glcorearb.h:786
static void EnableAll()
Definition: debug.h:151
GLuint GLfloat * val
Definition: glcorearb.h:1608
#define PXR_NAMESPACE_CLOSE_SCOPE
Definition: pxr.h:74
static TF_API bool IsDebugSymbolNameEnabled(const std::string &name)
True if the specified debug symbol is set.
TimedScopeHelper(bool enabled, const char *fmt,...) ARCH_PRINTF_FUNCTION(3
ScopeHelper(bool enabled, const char *name)
Definition: debug.h:219
state
Definition: core.h:2289
static void DisableAll()
Mark debugging as disabled for all enum values of type T.
Definition: debug.h:161
std::atomic< _NodeState > state
Definition: debug.h:324
static TF_API std::vector< std::string > GetDebugSymbolNames()
Get a listing of all debug symbols.
const char * str
Definition: debug.h:234