Loading...
Searching...
No Matches
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
11
12#include "pxr/pxr.h"
14#include "pxr/base/tf/debug.h"
17#include "pxr/base/tf/error.h"
20#include "pxr/base/tf/spinRWMutex.h"
21#include "pxr/base/tf/status.h"
23#include "pxr/base/tf/warning.h"
24#include "pxr/base/tf/weakPtr.h"
25#include "pxr/base/tf/enum.h"
26#include "pxr/base/tf/api.h"
27
28#include "pxr/base/arch/align.h"
32
33#include <tbb/enumerable_thread_specific.h>
34
35#include <atomic>
36#include <cstdarg>
37#include <list>
38#include <memory>
39#include <string>
40#include <utility>
41#include <vector>
42
43PXR_NAMESPACE_OPEN_SCOPE
44
46 TF_LOG_STACK_TRACE_ON_ERROR,
47 TF_LOG_STACK_TRACE_ON_WARNING,
48 TF_ERROR_MARK_TRACKING,
49 TF_PRINT_ALL_POSTED_ERRORS_TO_STDERR
50 );
51
53class TfError;
54class TfErrorMark;
55
61public:
62
63 typedef TfDiagnosticMgr This;
64
65 typedef std::list<TfError> ErrorList;
66
85 typedef ErrorList::iterator ErrorIterator;
86
88 TF_API
89 static std::string GetCodeName(const TfEnum &code);
90
95 TF_API
96 static std::string FormatDiagnostic(const TfEnum &code,
97 const TfCallContext &context, const std::string &msg,
98 const TfDiagnosticInfo &info);
99
117 class Delegate {
118 public:
119 TF_API
120 virtual ~Delegate() = 0;
121
123 virtual void IssueStatus(TfStatus const &status) = 0;
124
126 virtual void IssueWarning(TfWarning const &warning) = 0;
127
129 virtual void IssueError(TfError const &err) = 0;
130
133 virtual void IssueFatalError(TfCallContext const &context,
134 std::string const &msg) = 0;
135 protected:
139 TF_API
140 void _UnhandledAbort() const;
141 };
142
144 TF_API static This &GetInstance() {
146 }
147
155 TF_API
156 void AddDelegate(Delegate* delegate);
157
161 TF_API
162 void RemoveDelegate(Delegate* delegate);
163
166 TF_API
167 void SetQuiet(bool quiet) { _quiet = quiet; }
168
171 return _threadState.local()._errorList.begin();
172 }
173
176 return _threadState.local()._errorList.end();
177 }
178
181 TF_API
183
187 TF_API
189
194 TF_API
195 void AppendError(TfError const &e);
196
202 TF_API
203 void PostError(TfEnum errorCode, const char* errorCodeString,
204 TfCallContext const &context,
205 const std::string& commentary, TfDiagnosticInfo info,
206 bool quiet);
207
213 TF_API
214 void PostError(const TfDiagnosticBase& diagnostic);
215
220 TF_API
221 void PostWarning(TfEnum warningCode, const char *warningCodeString,
222 TfCallContext const &context, std::string const &commentary,
223 TfDiagnosticInfo info, bool quiet);
224
229 TF_API
230 void PostWarning(const TfDiagnosticBase& diagnostic);
231
236 TF_API
237 void PostStatus(TfEnum statusCode, const char *statusCodeString,
238 TfCallContext const &context, std::string const &commentary,
239 TfDiagnosticInfo info, bool quiet);
240
245 TF_API
246 void PostStatus(const TfDiagnosticBase& diagnostic);
247
252 [[noreturn]]
253 TF_API
254 void PostFatal(TfCallContext const &context, TfEnum statusCode,
255 std::string const &msg) const;
256
260 return _markCountsAndTrapStacks.local().markCount > 0;
261 }
262
263#if !defined(doxygen)
264 //
265 // Public, but *only* meant to be used by the TF_ERROR() macro.
266 //
268 class ErrorHelper {
269 public:
270 ErrorHelper(TfCallContext const &context, TfEnum errorCode,
271 const char* errorCodeString)
272 : _context(context), _errorCode(errorCode),
273 _errorCodeString(errorCodeString)
274 {
275 }
276
277 TF_API
278 void Post(const char* fmt, ...) const
280
281 TF_API
282 void PostQuietly(const char* fmt, ...) const
284
285 TF_API
286 void Post(const std::string& msg) const;
287
288 TF_API
289 void PostWithInfo(
290 const std::string& msg,
291 TfDiagnosticInfo info = TfDiagnosticInfo()) const;
292
293 TF_API
294 void PostQuietly(const std::string& msg,
295 TfDiagnosticInfo info = TfDiagnosticInfo()) const;
296
297 private:
298 TfCallContext _context;
299 TfEnum _errorCode;
300 const char *_errorCodeString;
301 };
302
303 struct WarningHelper {
304 WarningHelper(TfCallContext const &context, TfEnum warningCode,
305 const char *warningCodeString)
306 : _context(context), _warningCode(warningCode),
307 _warningCodeString(warningCodeString)
308 {
309 }
310
311 TF_API
312 void Post(const char* fmt, ...) const
314
315 TF_API
316 void PostQuietly(const char* fmt, ...) const
318
319 TF_API
320 void Post(const std::string &str) const;
321
322 TF_API
323 void PostWithInfo(
324 const std::string& msg,
325 TfDiagnosticInfo info = TfDiagnosticInfo()) const;
326
327 TF_API
328 void PostQuietly(const std::string& msg) const;
329
330 private:
331 TfCallContext _context;
332 TfEnum _warningCode;
333 const char *_warningCodeString;
334 };
335
336 struct StatusHelper {
337 StatusHelper(TfCallContext const &context, TfEnum statusCode,
338 const char *statusCodeString)
339 : _context(context), _statusCode(statusCode),
340 _statusCodeString(statusCodeString)
341 {
342 }
343
344 TF_API
345 void Post(const char* fmt, ...) const
347
348 TF_API
349 void PostQuietly(const char* fmt, ...) const
351
352 TF_API
353 void Post(const std::string &str) const;
354
355 TF_API
356 void PostWithInfo(
357 const std::string& msg,
358 TfDiagnosticInfo info = TfDiagnosticInfo()) const;
359
360 TF_API
361 void PostQuietly(const std::string& msg) const;
362
363 private:
364 TfCallContext _context;
365 TfEnum _statusCode;
366 const char *_statusCodeString;
367 };
368
369 struct FatalHelper {
370 FatalHelper(TfCallContext const &context, TfEnum statusCode)
371 : _context(context),
372 _statusCode(statusCode)
373 {
374 }
375 [[noreturn]]
376 void Post(const std::string &str) const {
377 This::GetInstance().PostFatal(_context, _statusCode, str);
378 }
379 private:
380 TfCallContext _context;
381 TfEnum _statusCode;
382 };
383
384#endif
385
386private:
387
389 virtual ~TfDiagnosticMgr();
390 friend class TfSingleton<This>;
391
392 // Return an iterator to the first error with serial number >= mark, or the
393 // past-the-end iterator, if no such errors exist.
394 TF_API
395 ErrorIterator _GetErrorMarkBegin(size_t mark, size_t *nErrors);
396
397 // Invoked by ErrorMark ctor.
398 inline void *_CreateErrorMark() {
399 size_t &markCount = _markCountsAndTrapStacks.local().markCount;
400 ++markCount;
401 return &markCount;
402 }
403
404 // Invoked by ErrorMark dtor.
405 inline bool _DestroyErrorMark(void *key) {
406 size_t &markCount = *static_cast<size_t *>(key);
407 return --markCount == 0;
408 }
409
410 // Invoked by TfDiagnosticTrap constructor.
411 void *_PushTrap(TfDiagnosticTrap *trap);
412
413 // Invoked by TfDiagnosticTrap destructor.
414 void _PopTrap(TfDiagnosticTrap *trap, void *key);
415
416 TfDiagnosticTrap *_GetActiveTrap();
417
418 // Report an error, either via delegate or print to stderr, and issue a
419 // notice if this thread of execution is the main thread.
420 void _ReportError(const TfError &err);
421
422 // Splice the errors in src into this thread's local list. Also reassign
423 // serial numbers to all the spliced errors to ensure they work correctly
424 // with local error marks.
425 void _SpliceErrors(ErrorList &src);
426
427 // Helper to append pending error messages to the crash log.
428 void _AppendPendingErrorsLogText(ErrorIterator i);
429
430 // Rebuild the pending-errors crash log from startSerial onward.
431 // startSerial = 0 performs a full rebuild. The backward scan to locate the
432 // dirty boundary is O(tail), not O(total).
433 void _RebuildPendingErrorLogText(size_t startSerial = 0);
434
435 // Similar log text for trapped diagnostics.
436 void _AppendTrappedDiagnosticsLogText(TfDiagnosticBase const &d,
437 TfDiagnosticTrap *trap);
438
439 // Rebuild the trapped-diagnostics crash log from validLogEnd onward.
440 // validLogEnd = 0 performs a full rebuild. Only entries at index >=
441 // validLogEnd are re-scanned; the clean prefix is kept in place.
442 void _RebuildTrappedDiagnosticsLogText(size_t validLogEnd = 0);
443
444 // A handle owning a pinned transient diagnostics log text snapshot
445 // registered with ArchSetExtraLogInfoForErrors(), constructed by
446 // _Pin*LogText helpers. Destruction deregisters the snapshot. Move-only.
447 struct _LogTextPin {
448 _LogTextPin() = default;
449 _LogTextPin(_LogTextPin &&) = default;
450 TF_API _LogTextPin &operator=(_LogTextPin &&);
451 _LogTextPin(const _LogTextPin &) = delete;
452 _LogTextPin &operator=(const _LogTextPin &) = delete;
453 TF_API ~_LogTextPin();
454 private:
455 friend class Tf_DiagnosticMgrTestAccess;
456 friend class TfDiagnosticMgr;
457 _LogTextPin(std::string &&key,
458 std::unique_ptr<std::vector<std::string>> &&lines);
459 std::string _key;
460 std::unique_ptr<std::vector<std::string>> _lines;
461 };
462
463
464 // Build a transient log text snapshot of [first, last) and register it
465 // with Arch. Return a _LogTextPin that deregisters it on destruction.
466 _LogTextPin
467 _PinErrorLogText(ErrorIterator first, ErrorIterator last) const;
468
469 // Build a transient log text snapshot of container and register it with
470 // Arch. Returns a _LogTextPin that deregisters it on destruction.
471 _LogTextPin
472 _PinDiagnosticsLogText(Tf_DiagnosticContainer const &container) const;
473
474 // Helper to apply a function to all the delegates. Return true if `fn` was
475 // invoked (i.e. there were delegates to call).
476 template <class Fn>
477 bool _ForEachDelegate(Fn const &fn) const;
478
479 // A guard used to protect reentrency when adding/removing
480 // delegates as well as posting errors/warnings/statuses
481 mutable tbb::enumerable_thread_specific<bool> _reentrantGuard;
482
483 // The registered delegates global delegates.
484 std::vector<Delegate*> _delegates;
485
486 mutable TfSpinRWMutex _delegatesMutex;
487
488 // Global serial number for sorting.
489 std::atomic<size_t> _nextSerial;
490
491 // Double-buffered vector<string> that publishes to
492 // ArchSetExtraLogInfoForErrors() under a fixed label. The double-buffer is
493 // required because Arch may read the registered pointer at any time; we
494 // write to one buffer, publish it, then synchronize the other.
495 struct _LogTextBuffer {
496 _LogTextBuffer() = default;
497 explicit _LogTextBuffer(std::string &&label);
498 ~_LogTextBuffer();
499
500 template <class Fn> void Update(Fn &&fn);
501
502 size_t GetSize() const {
503 return _texts.first.size();
504 }
505
506 std::string _label;
507 std::pair<std::vector<std::string>,
508 std::vector<std::string>> _texts;
509 bool _parity = false;
510 };
511
512 // Thread-specific error list and the two primary crash log text buffers.
513 struct _ThreadState {
514 _ThreadState()
515 : _pendingErrorsLogText(_MakeLabel("Pending Errors"))
516 , _trappedDiagnosticsLogText(_MakeLabel("Pending Diagnostics"))
517 {}
518 static std::string _MakeLabel(const char *suffix);
519 ErrorList _errorList;
520 _LogTextBuffer _pendingErrorsLogText;
521 _LogTextBuffer _trappedDiagnosticsLogText;
522 };
523
524 tbb::enumerable_thread_specific<_ThreadState> _threadState;
525
526 // Store as many trap pointers locally as we can in a cache line, minus the
527 // size needed for the error mark count.
528 static constexpr size_t _TrapStackLocalCap =
529 TfComputeSmallVectorLocalCapacityForTotalSize<
530 TfDiagnosticTrap*, ARCH_CACHE_LINE_SIZE - sizeof(size_t)>();
531
533
534 // Struct containing thread-local error mark count, and stack of active
535 // diagnostic traps, innermost last.
536 struct _MarkCountAndTrapStack {
537 size_t markCount = 0;
538 _TrapStack trapStack;
539 };
540
541 static_assert(sizeof(_MarkCountAndTrapStack) == ARCH_CACHE_LINE_SIZE);
542
543 // Thread-specific error mark counts & trap stacks. Use a native key for
544 // best performance here.
545 tbb::enumerable_thread_specific<
546 _MarkCountAndTrapStack,
547 tbb::cache_aligned_allocator<_MarkCountAndTrapStack>,
548 tbb::ets_key_per_instance> _markCountsAndTrapStacks;
549
550 bool _quiet;
551
552 friend class Tf_DiagnosticContainer;
553 friend class Tf_DiagnosticMgrTestAccess;
554 friend class TfDiagnosticTransport;
555 friend class TfDiagnosticTrap;
556 friend class TfError;
557 friend class TfErrorTransport;
558 friend class TfErrorMark;
559};
560
561TF_API_TEMPLATE_CLASS(TfSingleton<TfDiagnosticMgr>);
562
563PXR_NAMESPACE_CLOSE_SCOPE
564
565#endif // PXR_BASE_TF_DIAGNOSTIC_MGR_H
Provide architecture-specific memory-alignment information.
Define function attributes.
#define ARCH_PRINTF_FUNCTION(_fmt, _firstArg)
Macro used to indicate a function takes a printf-like specification.
Definition attributes.h:36
Provide facilities for error handling in script.
Functions for recording call locations.
Represents the base class of an object representing a diagnostic message.
One may set a delegate with the TfDiagnosticMgr which will be called to respond to errors and diagnos...
virtual void IssueStatus(TfStatus const &status)=0
Called when a TF_STATUS() is issued.
virtual void IssueError(TfError const &err)=0
Called when a TfError is posted.
TF_API void _UnhandledAbort() const
Abort the program, but avoid the session logging mechanism.
virtual void IssueWarning(TfWarning const &warning)=0
Called when a TF_WARNING() is issued.
virtual void IssueFatalError(TfCallContext const &context, std::string const &msg)=0
Called when a TF_FATAL_ERROR is issued (or a failed TF_AXIOM).
Singleton class through which all errors and diagnostics pass.
TF_API void RemoveDelegate(Delegate *delegate)
Removes all delegates equal to delegate from the current delegates.
TF_API void PostError(const TfDiagnosticBase &diagnostic)
This method will create a TfError, append it to the error list, and pass it to all delegates.
TF_API void PostStatus(TfEnum statusCode, const char *statusCodeString, TfCallContext const &context, std::string const &commentary, TfDiagnosticInfo info, bool quiet)
This method will create a TfStatus and pass it to all delegates.
TF_API void AddDelegate(Delegate *delegate)
Add the delegate delegate to the list of current delegates.
TF_API void SetQuiet(bool quiet)
Set whether errors, warnings and status messages should be printed out to the terminal.
TF_API void PostWarning(const TfDiagnosticBase &diagnostic)
This method will create a TfWarning and pass it to all delegates.
TF_API void PostError(TfEnum errorCode, const char *errorCodeString, TfCallContext const &context, const std::string &commentary, TfDiagnosticInfo info, bool quiet)
This method will create a TfError, append it to the error list, and pass it to all delegates.
TF_API ErrorIterator EraseError(ErrorIterator i)
Remove error specified by iterator i.
static TF_API This & GetInstance()
Return the singleton instance.
TF_API void PostFatal(TfCallContext const &context, TfEnum statusCode, std::string const &msg) const
This method will issue a fatal error to all delegates.
ErrorIterator GetErrorEnd()
Return an iterator to the end of this thread's error list.
TF_API void AppendError(TfError const &e)
Append an error to the list of active errors.
ErrorList::iterator ErrorIterator
Synonym for standard STL iterator to traverse the error list.
TF_API ErrorIterator EraseRange(ErrorIterator first, ErrorIterator last)
Remove all the errors in [first, last) from this thread's error stream.
bool HasActiveErrorMark()
Return true if an instance of TfErrorMark exists in the current thread of execution,...
TF_API void PostStatus(const TfDiagnosticBase &diagnostic)
This method will create a TfStatus and pass it to all delegates.
TF_API void PostWarning(TfEnum warningCode, const char *warningCodeString, TfCallContext const &context, std::string const &commentary, TfDiagnosticInfo info, bool quiet)
This method will create a TfWarning and pass it to all delegates.
static TF_API std::string FormatDiagnostic(const TfEnum &code, const TfCallContext &context, const std::string &msg, const TfDiagnosticInfo &info)
Return a human-readable diagnostic message.
ErrorIterator GetErrorBegin()
Return an iterator to the beginning of this thread's error list.
static TF_API std::string GetCodeName(const TfEnum &code)
Returns the name of the given diagnostic code.
A facility for transporting diagnostics from thread to thread.
A scoped, stack-based mechanism for intercepting and examining diagnostics issued on the current thre...
An enum class that records both enum type and enum value.
Definition enum.h:120
Represents an object that contains error information.
Definition error.h:32
Class used to record the end of the error-list.
Definition errorMark.h:48
A facility for transporting errors from thread to thread.
Manage a single instance of an object (see.
Definition singleton.h:107
static T & GetInstance()
Return a reference to an object of type T, creating it if necessary.
Definition singleton.h:122
This is a small-vector class with local storage optimization, the local storage can be specified via ...
This class implements a readers-writer spin lock that emphasizes throughput when there is light conte...
Definition spinRWMutex.h:52
Represents an object that contains information about a status message.
Definition status.h:28
Represents an object that contains information about a warning.
Definition warning.h:28
Enable a concrete base class for use with TfWeakPtr.
Definition weakBase.h:124
Conditional debugging output class and macros.
Stripped down version of diagnostic.h that doesn't define std::string.
Define preprocessor function name macros.
#define ARCH_CACHE_LINE_SIZE
The size of a CPU cache line on the current processor architecture in bytes.
Definition align.h:67
#define TF_DEBUG_CODES(...)
Define debugging symbols.
Definition debug.h:392
Define integral types.
STL namespace.
Manage a single instance of an object.
Definitions of basic string utilities in tf.
Pointer storage with deletion detection.