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