HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
diagnosticMgr.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_DIAGNOSTIC_MGR_H
8 #define PXR_BASE_TF_DIAGNOSTIC_MGR_H
9 
10 /// \file tf/diagnosticMgr.h
11 
12 #include "pxr/pxr.h"
14 #include "pxr/base/tf/debug.h"
16 #include "pxr/base/tf/error.h"
17 #include "pxr/base/tf/singleton.h"
19 #include "pxr/base/tf/status.h"
21 #include "pxr/base/tf/warning.h"
22 #include "pxr/base/tf/weakPtr.h"
23 #include "pxr/base/tf/enum.h"
24 #include "pxr/base/tf/api.h"
25 
26 #include "pxr/base/arch/inttypes.h"
29 
30 #include <tbb/enumerable_thread_specific.h>
31 
32 #include <atomic>
33 #include <cstdarg>
34 #include <list>
35 #include <string>
36 #include <vector>
37 
39 
41  TF_LOG_STACK_TRACE_ON_ERROR,
42  TF_LOG_STACK_TRACE_ON_WARNING,
43  TF_ERROR_MARK_TRACKING,
44  TF_PRINT_ALL_POSTED_ERRORS_TO_STDERR
45  );
46 
47 class TfDiagnosticTrap;
48 class TfError;
49 class TfErrorMark;
50 
51 /// \class TfDiagnosticMgr
52 /// \ingroup group_tf_Diagnostic
53 ///
54 /// Singleton class through which all errors and diagnostics pass.
55 class TfDiagnosticMgr : public TfWeakBase {
56 public:
57 
59 
60  typedef std::list<TfError> ErrorList;
61 
62  /// Synonym for standard STL iterator to traverse the error list.
63  ///
64  /// The error list for a thread is an STL list. The \c ErrorIterator type
65  /// is an STL iterator and can be used without restriction in any way that
66  /// it is legal to use an STL iterator.
67  ///
68  /// Given an iterator, one accesses the error in the standard STL fashion:
69  /// \code
70  /// TfErrorMark m;
71  ///
72  /// ... ;
73  /// if (!m.IsClean()) {
74  /// TfErrorMark::Iterator i;
75  /// for (i = m.GetBegin(); i != m.GetEnd(); ++i) {
76  /// cout << "file = " << i->GetSourceFileName()
77  /// << "line = " << i->GetSourceLineNumber() << "\n";
78  /// }
79  /// \endcode
80  typedef ErrorList::iterator ErrorIterator;
81 
82  /// Returns the name of the given diagnostic code.
83  TF_API
84  static std::string GetCodeName(const TfEnum &code);
85 
86  /// Return a human-readable diagnostic message. The TfDiagnosticMgr uses
87  /// this function to print diagnostics when no diagnostic delegates are
88  /// installed. Diagnostic delegate implementations can call this to produce
89  /// messages in the same format, if desired.
90  TF_API
91  static std::string FormatDiagnostic(const TfEnum &code,
92  const TfCallContext &context, const std::string &msg,
93  const TfDiagnosticInfo &info);
94 
95  /// \class Delegate
96  /// One may set a delegate with the \c TfDiagnosticMgr which will be
97  /// called to respond to errors and diagnostics.
98  ///
99  /// \note None of the methods in \c TfDiagnosticMgr::Delegate can be
100  /// reentrant.
101  ///
102  /// Practically speaking, this means they cannot invoke:
103  ///
104  /// - TF_ERROR
105  /// - TF_RUNTIME_ERROR
106  /// - TF_CODING_ERROR
107  /// - TF_WARN
108  /// - TF_STATUS
109  ///
110  /// For a more complete list, see diagnostic.h
111  ///
112  class Delegate {
113  public:
114  TF_API
115  virtual ~Delegate() = 0;
116 
117  /// Called when a \c TF_STATUS() is issued.
118  virtual void IssueStatus(TfStatus const &status) = 0;
119 
120  /// Called when a \c TF_WARNING() is issued.
121  virtual void IssueWarning(TfWarning const &warning) = 0;
122 
123  /// Called when a \c TfError is posted.
124  virtual void IssueError(TfError const &err) = 0;
125 
126  /// Called when a \c TF_FATAL_ERROR is issued (or a failed
127  /// \c TF_AXIOM).
128  virtual void IssueFatalError(TfCallContext const &context,
129  std::string const &msg) = 0;
130  protected:
131  /// Abort the program, but avoid the session logging mechanism. This
132  /// is intended to be used for fatal error cases where any information
133  /// has already been logged.
134  TF_API
135  void _UnhandledAbort() const;
136  };
137 
138  /// Return the singleton instance.
139  TF_API static This &GetInstance() {
141  }
142 
143  /// Add the delegate \p delegate to the list of current delegates.
144  ///
145  /// This will add the delegate even if it already exists in the list.
146  ///
147  /// Each delegate will be called when diagnostics and errors are invoked
148  ///
149  /// This function is thread safe.
150  TF_API
151  void AddDelegate(Delegate* delegate);
152 
153  /// Removes all delegates equal to \p delegate from the current delegates.
154  ///
155  /// This function is thread safe.
156  TF_API
157  void RemoveDelegate(Delegate* delegate);
158 
159  /// Set whether errors, warnings and status messages should be printed out
160  /// to the terminal.
161  TF_API
162  void SetQuiet(bool quiet) { _quiet = quiet; }
163 
164  /// Return an iterator to the beginning of this thread's error list.
165  ErrorIterator GetErrorBegin() { return _errorList.local().begin(); }
166 
167  /// Return an iterator to the end of this thread's error list.
168  ErrorIterator GetErrorEnd() { return _errorList.local().end(); }
169 
170  /// Remove error specified by iterator \p i.
171  /// \deprecated Use TfErrorMark instead.
172  TF_API
174 
175  /// Remove all the errors in [first, last) from this thread's error
176  /// stream. This should generally not be invoked directly. Use TfErrorMark
177  /// instead.
178  TF_API
180 
181  /// Append an error to the list of active errors. This is generally not
182  /// meant to be called by user code. It is public so that the system
183  /// which translates tf errors to and from python exceptions can manage
184  /// errors.
185  TF_API
186  void AppendError(TfError const &e);
187 
188  /// This method will create a TfError, append it to the error list, and
189  /// pass it to all delegates.
190  ///
191  /// If no delegates have been registered and no error mark is active, this
192  /// method will print the error to stderr.
193  TF_API
194  void PostError(TfEnum errorCode, const char* errorCodeString,
195  TfCallContext const &context,
196  const std::string& commentary, TfDiagnosticInfo info,
197  bool quiet);
198 
199  /// This method will create a TfError, append it to the error list, and
200  /// pass it to all delegates.
201  ///
202  /// If no delegates have been registered and no error mark is active, this
203  /// method will print the error to stderr.
204  TF_API
205  void PostError(const TfDiagnosticBase& diagnostic);
206 
207  /// This method will create a TfWarning and pass it to all delegates.
208  ///
209  /// If no delegates have been registered, this method will print the
210  /// warning msg to stderr.
211  TF_API
212  void PostWarning(TfEnum warningCode, const char *warningCodeString,
213  TfCallContext const &context, std::string const &commentary,
214  TfDiagnosticInfo info, bool quiet);
215 
216  /// This method will create a TfWarning and pass it to all delegates.
217  ///
218  /// If no delegates have been registered, this method will print the
219  /// warning msg to stderr.
220  TF_API
221  void PostWarning(const TfDiagnosticBase& diagnostic);
222 
223  /// This method will create a TfStatus and pass it to all delegates.
224  ///
225  /// If no delegates have been registered, this method will print the
226  /// status msg to stderr.
227  TF_API
228  void PostStatus(TfEnum statusCode, const char *statusCodeString,
229  TfCallContext const &context, std::string const &commentary,
230  TfDiagnosticInfo info, bool quiet);
231 
232  /// This method will create a TfStatus and pass it to all delegates.
233  ///
234  /// If no delegates have been registered, this method will print the
235  /// status msg to stderr.
236  TF_API
237  void PostStatus(const TfDiagnosticBase& diagnostic);
238 
239  /// This method will issue a fatal error to all delegates.
240  ///
241  /// If no delegates have been registered, or if none of the delegates abort
242  /// the process, this method will print the error msg and abort the process.
243  [[noreturn]]
244  TF_API
245  void PostFatal(TfCallContext const &context, TfEnum statusCode,
246  std::string const &msg) const;
247 
248  /// Return true if an instance of TfErrorMark exists in the current thread
249  /// of execution, false otherwise.
250  bool HasActiveErrorMark() { return _errorMarkCounts.local() > 0; }
251 
252 #if !defined(doxygen)
253  //
254  // Public, but *only* meant to be used by the TF_ERROR() macro.
255  //
256  /// \private
257  class ErrorHelper {
258  public:
259  ErrorHelper(TfCallContext const &context, TfEnum errorCode,
260  const char* errorCodeString)
261  : _context(context), _errorCode(errorCode),
262  _errorCodeString(errorCodeString)
263  {
264  }
265 
266  TF_API
267  void Post(const char* fmt, ...) const
268  ARCH_PRINTF_FUNCTION(2,3);
269 
270  TF_API
271  void PostQuietly(const char* fmt, ...) const
272  ARCH_PRINTF_FUNCTION(2,3);
273 
274  TF_API
275  void Post(const std::string& msg) const;
276 
277  TF_API
278  void PostWithInfo(
279  const std::string& msg,
280  TfDiagnosticInfo info = TfDiagnosticInfo()) const;
281 
282  TF_API
283  void PostQuietly(const std::string& msg,
284  TfDiagnosticInfo info = TfDiagnosticInfo()) const;
285 
286  private:
287  TfCallContext _context;
288  TfEnum _errorCode;
289  const char *_errorCodeString;
290  };
291 
292  struct WarningHelper {
293  WarningHelper(TfCallContext const &context, TfEnum warningCode,
294  const char *warningCodeString)
295  : _context(context), _warningCode(warningCode),
296  _warningCodeString(warningCodeString)
297  {
298  }
299 
300  TF_API
301  void Post(const char* fmt, ...) const
302  ARCH_PRINTF_FUNCTION(2,3);
303 
304  TF_API
305  void PostQuietly(const char* fmt, ...) const
306  ARCH_PRINTF_FUNCTION(2,3);
307 
308  TF_API
309  void Post(const std::string &str) const;
310 
311  TF_API
312  void PostWithInfo(
313  const std::string& msg,
314  TfDiagnosticInfo info = TfDiagnosticInfo()) const;
315 
316  TF_API
317  void PostQuietly(const std::string& msg) const;
318 
319  private:
320  TfCallContext _context;
321  TfEnum _warningCode;
322  const char *_warningCodeString;
323  };
324 
325  struct StatusHelper {
326  StatusHelper(TfCallContext const &context, TfEnum statusCode,
327  const char *statusCodeString)
328  : _context(context), _statusCode(statusCode),
329  _statusCodeString(statusCodeString)
330  {
331  }
332 
333  TF_API
334  void Post(const char* fmt, ...) const
335  ARCH_PRINTF_FUNCTION(2,3);
336 
337  TF_API
338  void PostQuietly(const char* fmt, ...) const
339  ARCH_PRINTF_FUNCTION(2,3);
340 
341  TF_API
342  void Post(const std::string &str) const;
343 
344  TF_API
345  void PostWithInfo(
346  const std::string& msg,
347  TfDiagnosticInfo info = TfDiagnosticInfo()) const;
348 
349  TF_API
350  void PostQuietly(const std::string& msg) const;
351 
352  private:
353  TfCallContext _context;
354  TfEnum _statusCode;
355  const char *_statusCodeString;
356  };
357 
358  struct FatalHelper {
359  FatalHelper(TfCallContext const &context, TfEnum statusCode)
360  : _context(context),
361  _statusCode(statusCode)
362  {
363  }
364  [[noreturn]]
365  void Post(const std::string &str) const {
366  This::GetInstance().PostFatal(_context, _statusCode, str);
367  }
368  private:
369  TfCallContext _context;
370  TfEnum _statusCode;
371  };
372 
373 #endif
374 
375 private:
376 
377  TfDiagnosticMgr();
378  virtual ~TfDiagnosticMgr();
379  friend class TfSingleton<This>;
380 
381  // Return an iterator to the first error with serial number >= mark, or the
382  // past-the-end iterator, if no such errors exist.
383  TF_API
384  ErrorIterator _GetErrorMarkBegin(size_t mark, size_t *nErrors);
385 
386  // Invoked by ErrorMark ctor.
387  inline void _CreateErrorMark() { ++_errorMarkCounts.local(); }
388 
389  // Invoked by ErrorMark dtor.
390  inline bool _DestroyErrorMark() { return --_errorMarkCounts.local() == 0; }
391 
392  // Invoked by TfDiagnosticTrap constructor.
393  void _PushTrap(TfDiagnosticTrap *trap);
394 
395  // Invoked by TfDiagnosticTrap destructor.
396  void _PopTrap(TfDiagnosticTrap *trap);
397 
398  TfDiagnosticTrap *_GetActiveTrap();
399 
400  // Report an error, either via delegate or print to stderr, and issue a
401  // notice if this thread of execution is the main thread.
402  void _ReportError(const TfError &err);
403 
404  // Splice the errors in src into this thread's local list. Also reassign
405  // serial numbers to all the spliced errors to ensure they work correctly
406  // with local error marks.
407  void _SpliceErrors(ErrorList &src);
408 
409  // Helper to append pending error messages to the crash log.
410  void _AppendPendingErrorsLogText(ErrorIterator i);
411 
412  // Helper to fully rebuild the crash log error text when errors are erased
413  // from the middle.
414  void _RebuildPendingErrorLogText();
415 
416  // Similar log text for trapped diagnostics.
417  void _AppendTrappedDiagnosticsLogText(TfDiagnosticBase const &d);
418  void _RebuildTrappedDiagnosticsLogText();
419 
420  // Helper to apply a function to all the delegates. Return true if `fn` was
421  // invoked (i.e. there were delegates to call).
422  template <class Fn>
423  bool _ForEachDelegate(Fn const &fn) const;
424 
425  // A guard used to protect reentrency when adding/removing
426  // delegates as well as posting errors/warnings/statuses
427  mutable tbb::enumerable_thread_specific<bool> _reentrantGuard;
428 
429  // The registered delegates global delegates.
430  std::vector<Delegate*> _delegates;
431 
432  mutable TfSpinRWMutex _delegatesMutex;
433 
434  // Thread-local stack of active diagnostic traps, innermost last.
435  tbb::enumerable_thread_specific<
436  std::vector<TfDiagnosticTrap*>> _scopedTrapStack;
437 
438  // Global serial number for sorting.
439  std::atomic<size_t> _nextSerial;
440 
441  // Thread-specific error list.
442  tbb::enumerable_thread_specific<ErrorList> _errorList;
443 
444  // Double-buffered vector<string> that publishes to
445  // ArchSetExtraLogInfoForErrors under a fixed label. Update calls
446  // fn(active-text) *twice* in order to update the text in both buffers, and
447  // it must produce the same results on each call.
448  struct _LogTextBuffer {
449  _LogTextBuffer() = default;
450  explicit _LogTextBuffer(std::string &&label);
451  template <class Fn> void Update(Fn &&fn);
452  private:
453  std::string _label;
454  std::pair<std::vector<std::string>,
455  std::vector<std::string>> _texts;
456  bool _parity = false;
457  };
458 
459  // Thread-specific diagnostic log text for pending errors.
460  tbb::enumerable_thread_specific<_LogTextBuffer> _pendingErrorsLogText;
461 
462  // Thread-specific log text for trapped diagnostics.
463  tbb::enumerable_thread_specific<_LogTextBuffer> _trappedDiagnosticsLogText;
464 
465  // Thread-specific error mark counts. Use a native key for best performance
466  // here.
467  tbb::enumerable_thread_specific<
468  size_t, tbb::cache_aligned_allocator<size_t>,
469  tbb::ets_key_per_instance> _errorMarkCounts;
470 
471  bool _quiet;
472 
474  friend class TfDiagnosticTransport;
475  friend class TfDiagnosticTrap;
476  friend class TfError;
477  friend class TfErrorTransport;
478  friend class TfErrorMark;
479 };
480 
482 
484 
485 #endif // PXR_BASE_TF_DIAGNOSTIC_MGR_H
GLint first
Definition: glcorearb.h:405
WarningHelper(TfCallContext const &context, TfEnum warningCode, const char *warningCodeString)
static T & GetInstance()
Definition: singleton.h:122
GLuint GLsizei const GLchar * label
Definition: glcorearb.h:2545
virtual TF_API ~Delegate()=0
#define TF_API
Definition: api.h:23
TF_API ErrorIterator EraseRange(ErrorIterator first, ErrorIterator last)
static TF_API std::string GetCodeName(const TfEnum &code)
Returns the name of the given diagnostic code.
TF_API void PostError(TfEnum errorCode, const char *errorCodeString, TfCallContext const &context, const std::string &commentary, TfDiagnosticInfo info, bool quiet)
TF_API void _UnhandledAbort() const
ErrorIterator GetErrorBegin()
Return an iterator to the beginning of this thread's error list.
#define PXR_NAMESPACE_OPEN_SCOPE
Definition: pxr.h:73
ErrorList::iterator ErrorIterator
Definition: diagnosticMgr.h:80
StatusHelper(TfCallContext const &context, TfEnum statusCode, const char *statusCodeString)
static TF_API This & GetInstance()
Return the singleton instance.
TfDiagnosticMgr This
Definition: diagnosticMgr.h:58
Definition: enum.h:119
PXR_NAMESPACE_OPEN_SCOPE typedef std::any TfDiagnosticInfo
TF_API ErrorIterator EraseError(ErrorIterator i)
TF_API void PostWarning(TfEnum warningCode, const char *warningCodeString, TfCallContext const &context, std::string const &commentary, TfDiagnosticInfo info, bool quiet)
TF_API void PostFatal(TfCallContext const &context, TfEnum statusCode, std::string const &msg) const
virtual void IssueError(TfError const &err)=0
Called when a TfError is posted.
TF_API void AppendError(TfError const &e)
std::list< TfError > ErrorList
Definition: diagnosticMgr.h:60
FatalHelper(TfCallContext const &context, TfEnum statusCode)
virtual void IssueWarning(TfWarning const &warning)=0
Called when a TF_WARNING() is issued.
TF_API void RemoveDelegate(Delegate *delegate)
bool HasActiveErrorMark()
TF_API void AddDelegate(Delegate *delegate)
TF_API void SetQuiet(bool quiet)
virtual void IssueStatus(TfStatus const &status)=0
Called when a TF_STATUS() is issued.
Definition: error.h:32
__hostdev__ uint64_t last(uint32_t i) const
Definition: NanoVDB.h:5976
ErrorIterator GetErrorEnd()
Return an iterator to the end of this thread's error list.
void Post(const std::string &str) const
static TF_API std::string FormatDiagnostic(const TfEnum &code, const TfCallContext &context, const std::string &msg, const TfDiagnosticInfo &info)
#define PXR_NAMESPACE_CLOSE_SCOPE
Definition: pxr.h:74
virtual void IssueFatalError(TfCallContext const &context, std::string const &msg)=0
PXR_NAMESPACE_OPEN_SCOPE TF_DEBUG_CODES(TF_LOG_STACK_TRACE_ON_ERROR, TF_LOG_STACK_TRACE_ON_WARNING, TF_ERROR_MARK_TRACKING, TF_PRINT_ALL_POSTED_ERRORS_TO_STDERR)
TF_API_TEMPLATE_CLASS(TfSingleton< TfDiagnosticMgr >)
TF_API void PostStatus(TfEnum statusCode, const char *statusCodeString, TfCallContext const &context, std::string const &commentary, TfDiagnosticInfo info, bool quiet)
GLenum src
Definition: glcorearb.h:1793