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 
25 #include "pxr/pxr.h"
26 #include "pxr/base/tf/diagnosticMgr.h"
27 
28 #include "pxr/base/tf/debugCodes.h"
29 #include "pxr/base/tf/error.h"
30 #include "pxr/base/tf/instantiateSingleton.h"
31 #include "pxr/base/tf/registryManager.h"
32 #include "pxr/base/tf/stackTrace.h"
33 #include "pxr/base/tf/stringUtils.h"
34 
35 #ifdef PXR_PYTHON_SUPPORT_ENABLED
36 #include "pxr/base/tf/pyExceptionState.h"
37 #endif // PXR_PYTHON_SUPPORT_ENABLED
38 
39 #include "pxr/base/arch/debugger.h"
40 #include "pxr/base/arch/demangle.h"
41 #include "pxr/base/arch/function.h"
42 #include "pxr/base/arch/stackTrace.h"
43 #include "pxr/base/arch/threads.h"
44 
45 #include <boost/utility.hpp>
46 
47 #include <signal.h>
48 #include <stdlib.h>
49 
50 #include <thread>
51 #include <memory>
52 
53 using std::list;
54 using std::string;
55 
56 PXR_NAMESPACE_OPEN_SCOPE
57 
58 namespace {
59 // Helper RAII struct for ensuring we protect functions
60 // that we wish to not have reentrant behaviors from delegates
61 // that we call out to.
62 struct _ReentrancyGuard {
63     public:
_ReentrancyGuard__anonce3825130111::_ReentrancyGuard64         _ReentrancyGuard(bool* reentrancyGuardValue) :
65             _reentrancyGuardValue(reentrancyGuardValue),
66             _scopeWasReentered(false)
67         {
68             if (!*_reentrancyGuardValue) {
69                 *_reentrancyGuardValue = true;
70             } else {
71                 _scopeWasReentered = true;
72             }
73         }
74 
ScopeWasReentered__anonce3825130111::_ReentrancyGuard75         bool ScopeWasReentered() {
76             return _scopeWasReentered;
77         }
78 
~_ReentrancyGuard__anonce3825130111::_ReentrancyGuard79         ~_ReentrancyGuard() {
80             if (!_scopeWasReentered) {
81                 *_reentrancyGuardValue = false;
82             }
83         }
84 
85     private:
86         bool* _reentrancyGuardValue;
87         bool _scopeWasReentered;
88 };
89 } // end anonymous namespace
90 
91 
92 // Helper function for printing a diagnostic message when a delegate is not
93 // available.
94 //
95 // If \p info contains a TfPyExceptionState, that will be printed too.
96 //
97 static void
98 _PrintDiagnostic(FILE *fout, const TfEnum &code, const TfCallContext &context,
99                  const std::string& msg, const TfDiagnosticInfo &info);
100 
101 static std::string
102 _FormatDiagnostic(const TfDiagnosticBase &d, const TfDiagnosticInfo &info);
103 
104 
TF_REGISTRY_FUNCTION(TfDebug)105 TF_REGISTRY_FUNCTION(TfDebug)
106 {
107     TF_DEBUG_ENVIRONMENT_SYMBOL(
108         TF_LOG_STACK_TRACE_ON_ERROR,
109         "log stack traces for all errors");
110     TF_DEBUG_ENVIRONMENT_SYMBOL(
111         TF_LOG_STACK_TRACE_ON_WARNING,
112         "log stack traces for all warnings");
113     TF_DEBUG_ENVIRONMENT_SYMBOL(
114         TF_ERROR_MARK_TRACKING,
115         "capture stack traces at TfErrorMark ctor/dtor, enable "
116         "TfReportActiveMarks debugging API.");
117     TF_DEBUG_ENVIRONMENT_SYMBOL(TF_PRINT_ALL_POSTED_ERRORS_TO_STDERR,
118         "print all posted errors immediately, meaning that even errors that "
119         "are expected and handled will be printed, producing possibly "
120         "confusing output");
121 }
122 
123 
124 // Abort without logging.  This is meant for use by things like TF_FATAL_ERROR,
125 // which already log (more extensive) session information before doing the
126 // abort.
127 static
128 void
Tf_UnhandledAbort()129 Tf_UnhandledAbort()
130 {
131     constexpr bool logging = true;
132     ArchAbort(!logging);
133 }
134 
135 TF_INSTANTIATE_SINGLETON(TfDiagnosticMgr);
136 
137 
~Delegate()138 TfDiagnosticMgr::Delegate::~Delegate() {}
139 
140 void
_UnhandledAbort() const141 TfDiagnosticMgr::Delegate::_UnhandledAbort() const
142 {
143     Tf_UnhandledAbort();
144 }
145 
TfDiagnosticMgr()146 TfDiagnosticMgr::TfDiagnosticMgr() :
147     _errorMarkCounts(static_cast<size_t>(0)),
148     _quiet(false)
149 {
150     _nextSerial = 0;
151     TfSingleton<This>::SetInstanceConstructed(*this);
152     TfRegistryManager::GetInstance().SubscribeTo<TfDiagnosticMgr>();
153 }
154 
~TfDiagnosticMgr()155 TfDiagnosticMgr::~TfDiagnosticMgr()
156 {
157 }
158 
159 void
AddDelegate(Delegate * delegate)160 TfDiagnosticMgr::AddDelegate(Delegate* delegate)
161 {
162     if (delegate == nullptr) {
163         return;
164     }
165 
166     tbb::spin_rw_mutex::scoped_lock lock(_delegatesMutex, /*writer=*/true);
167     _delegates.push_back(delegate);
168 }
169 
170 void
RemoveDelegate(Delegate * delegate)171 TfDiagnosticMgr::RemoveDelegate(Delegate* delegate)
172 {
173     if (delegate == nullptr) {
174         return;
175     }
176 
177     tbb::spin_rw_mutex::scoped_lock lock(_delegatesMutex, /*writer=*/true);
178     _delegates.erase(std::remove(_delegates.begin(),
179                                  _delegates.end(),
180                                  delegate),
181                      _delegates.end());
182 }
183 
184 void
AppendError(TfError const & e)185 TfDiagnosticMgr::AppendError(TfError const &e) {
186     if (!HasActiveErrorMark()) {
187         _ReportError(e);
188     } else {
189         ErrorList &errorList = _errorList.local();
190         errorList.push_back(e);
191         errorList.back()._serial = _nextSerial.fetch_add(1);
192         _AppendErrorsToLogText(std::prev(errorList.end()));
193     }
194 }
195 
196 void
_SpliceErrors(ErrorList & src)197 TfDiagnosticMgr::_SpliceErrors(ErrorList &src)
198 {
199     if (!HasActiveErrorMark()) {
200         for (ErrorList::const_iterator
201                  i = src.begin(), end = src.end(); i != end; ++i) {
202             _ReportError(*i);
203         }
204     } else {
205         // Reassign new serial numbers to the errors.
206         size_t serial = _nextSerial.fetch_add(src.size());
207         for (auto& error : src) {
208             error._serial = serial++;
209         }
210         // Now splice them into the main list.
211         ErrorList &errorList = _errorList.local();
212         // We store the begin iterator from the new list.  This iterator remains
213         // valid *after the splice*, and iterates the spliced elements from src
214         // in errorList.
215         ErrorList::iterator newErrorsBegin = src.begin();
216         errorList.splice(errorList.end(), src);
217         _AppendErrorsToLogText(newErrorsBegin);
218     }
219 }
220 
221 void
PostError(TfEnum errorCode,const char * errorCodeString,TfCallContext const & context,const string & commentary,TfDiagnosticInfo info,bool quiet)222 TfDiagnosticMgr::PostError(TfEnum errorCode, const char* errorCodeString,
223                            TfCallContext const &context,
224                            const string& commentary,
225                            TfDiagnosticInfo info, bool quiet)
226 {
227     if (TfDebug::IsEnabled(TF_ATTACH_DEBUGGER_ON_ERROR))
228         ArchDebuggerTrap();
229 
230     const bool logStackTraceOnError =
231         TfDebug::IsEnabled(TF_LOG_STACK_TRACE_ON_ERROR);
232 
233     if (logStackTraceOnError ||
234         TfDebug::IsEnabled(TF_PRINT_ALL_POSTED_ERRORS_TO_STDERR)) {
235 
236         _PrintDiagnostic(stderr, errorCode, context, commentary, info);
237     }
238 
239     if (logStackTraceOnError) {
240         TfLogStackTrace("ERROR", /* logToDb */ false);
241     }
242 
243     quiet |= _quiet;
244 
245     TfError err(errorCode, errorCodeString, context, commentary, info, quiet);
246     AppendError(err);
247 }
248 
249 void
PostError(const TfDiagnosticBase & diagnostic)250 TfDiagnosticMgr::PostError(const TfDiagnosticBase& diagnostic)
251 {
252     PostError(diagnostic.GetDiagnosticCode(),
253               diagnostic.GetDiagnosticCodeAsString().c_str(),
254               diagnostic.GetContext(), diagnostic.GetCommentary(),
255               diagnostic._info, diagnostic.GetQuiet());
256 }
257 
258 void
_ReportError(const TfError & err)259 TfDiagnosticMgr::_ReportError(const TfError &err)
260 {
261     _ReentrancyGuard guard(&_reentrantGuard.local());
262     if (guard.ScopeWasReentered()) {
263         return;
264     }
265 
266     bool dispatchedToDelegate = false;
267     {
268         tbb::spin_rw_mutex::scoped_lock lock(_delegatesMutex, /*writer=*/false);
269         for (auto const& delegate : _delegates) {
270             if (delegate) {
271                 delegate->IssueError(err);
272             }
273         }
274         dispatchedToDelegate = !_delegates.empty();
275     }
276 
277     if (!dispatchedToDelegate && !err.GetQuiet()) {
278         _PrintDiagnostic(stderr,
279                          err.GetDiagnosticCode(),
280                          err.GetContext(),
281                          err.GetCommentary(),
282                          err._info);
283     }
284 }
285 
286 void
PostWarning(TfEnum warningCode,const char * warningCodeString,TfCallContext const & context,std::string const & commentary,TfDiagnosticInfo info,bool quiet) const287 TfDiagnosticMgr::PostWarning(
288     TfEnum warningCode, const char *warningCodeString,
289     TfCallContext const &context, std::string const &commentary,
290     TfDiagnosticInfo info, bool quiet) const
291 {
292     _ReentrancyGuard guard(&_reentrantGuard.local());
293     if (guard.ScopeWasReentered()) {
294         return;
295     }
296 
297     if (TfDebug::IsEnabled(TF_ATTACH_DEBUGGER_ON_WARNING))
298         ArchDebuggerTrap();
299 
300     const bool logStackTraceOnWarning =
301         TfDebug::IsEnabled(TF_LOG_STACK_TRACE_ON_WARNING);
302 
303     if (logStackTraceOnWarning) {
304         _PrintDiagnostic(stderr, warningCode, context, commentary, info);
305         TfLogStackTrace("WARNING", /* logToDb */ false);
306     }
307 
308     quiet |= _quiet;
309 
310     TfWarning warning(warningCode, warningCodeString, context, commentary, info,
311                       quiet);
312 
313     bool dispatchedToDelegate = false;
314     {
315         tbb::spin_rw_mutex::scoped_lock lock(_delegatesMutex, /*writer=*/false);
316         for (auto const& delegate : _delegates) {
317             if (delegate) {
318                 delegate->IssueWarning(warning);
319             }
320         }
321         dispatchedToDelegate = !_delegates.empty();
322     }
323 
324     if (!logStackTraceOnWarning && !dispatchedToDelegate && !quiet) {
325         _PrintDiagnostic(stderr, warningCode, context, commentary, info);
326     }
327 }
328 
329 void
PostWarning(const TfDiagnosticBase & diagnostic) const330 TfDiagnosticMgr::PostWarning(const TfDiagnosticBase& diagnostic) const
331 {
332     PostWarning(diagnostic.GetDiagnosticCode(),
333                 diagnostic.GetDiagnosticCodeAsString().c_str(),
334                 diagnostic.GetContext(), diagnostic.GetCommentary(),
335                 diagnostic._info, diagnostic.GetQuiet());
336 }
337 
PostStatus(TfEnum statusCode,const char * statusCodeString,TfCallContext const & context,std::string const & commentary,TfDiagnosticInfo info,bool quiet) const338 void TfDiagnosticMgr::PostStatus(
339     TfEnum statusCode, const char *statusCodeString,
340     TfCallContext const &context, std::string const &commentary,
341     TfDiagnosticInfo info, bool quiet) const
342 {
343     _ReentrancyGuard guard(&_reentrantGuard.local());
344     if (guard.ScopeWasReentered()) {
345         return;
346     }
347 
348     quiet |= _quiet;
349 
350     TfStatus status(statusCode, statusCodeString, context, commentary, info,
351                     quiet);
352 
353     bool dispatchedToDelegate = false;
354     {
355         tbb::spin_rw_mutex::scoped_lock lock(_delegatesMutex, /*writer=*/false);
356         for (auto const& delegate : _delegates) {
357             if (delegate) {
358                 delegate->IssueStatus(status);
359             }
360         }
361         dispatchedToDelegate = !_delegates.empty();
362     }
363 
364     if (!dispatchedToDelegate && !quiet) {
365         _PrintDiagnostic(stderr, statusCode, context, commentary, info);
366     }
367 }
368 
369 void
PostStatus(const TfDiagnosticBase & diagnostic) const370 TfDiagnosticMgr::PostStatus(const TfDiagnosticBase& diagnostic) const
371 {
372     PostStatus(diagnostic.GetDiagnosticCode(),
373                diagnostic.GetDiagnosticCodeAsString().c_str(),
374                diagnostic.GetContext(), diagnostic.GetCommentary(),
375                diagnostic._info, diagnostic.GetQuiet());
376 }
377 
PostFatal(TfCallContext const & context,TfEnum statusCode,std::string const & msg) const378 void TfDiagnosticMgr::PostFatal(TfCallContext const &context,
379                                 TfEnum statusCode,
380                                 std::string const &msg) const
381 {
382     _ReentrancyGuard guard(&_reentrantGuard.local());
383     if (guard.ScopeWasReentered()) {
384         return;
385     }
386 
387     if (TfDebug::IsEnabled(TF_ATTACH_DEBUGGER_ON_ERROR) ||
388         TfDebug::IsEnabled(TF_ATTACH_DEBUGGER_ON_FATAL_ERROR))
389         ArchDebuggerTrap();
390 
391     bool dispatchedToDelegate = false;
392     {
393         tbb::spin_rw_mutex::scoped_lock lock(_delegatesMutex, /*writer=*/false);
394         for (auto const& delegate : _delegates) {
395             if (delegate) {
396                 delegate->IssueFatalError(context, msg);
397             }
398         }
399         dispatchedToDelegate = !_delegates.empty();
400     }
401 
402     if (!dispatchedToDelegate) {
403         if (statusCode == TF_DIAGNOSTIC_CODING_ERROR_TYPE) {
404             fprintf(stderr, "Fatal coding error: %s [%s], in %s(), %s:%zu\n",
405                     msg.c_str(), ArchGetProgramNameForErrors(),
406                     context.GetFunction(), context.GetFile(), context.GetLine());
407         }
408         else if (statusCode == TF_DIAGNOSTIC_RUNTIME_ERROR_TYPE) {
409             fprintf(stderr, "Fatal error: %s [%s].\n",
410                     msg.c_str(), ArchGetProgramNameForErrors());
411             exit(1);
412         }
413         else {
414             // Report and log information about the fatal error
415             TfLogCrash("FATAL ERROR", msg, std::string() /*additionalInfo*/,
416                        context, true /*logToDB*/);
417         }
418 
419         // Abort, but avoid the signal handler, since we've already logged the
420         // session info in TfLogStackTrace.
421         Tf_UnhandledAbort();
422     }
423 }
424 
425 TfDiagnosticMgr::ErrorIterator
EraseError(ErrorIterator i)426 TfDiagnosticMgr::EraseError(ErrorIterator i)
427 {
428     ErrorList &errorList = _errorList.local();
429 
430     return i == errorList.end() ? i : errorList.erase(i);
431 }
432 
433 TfDiagnosticMgr::ErrorIterator
_GetErrorMarkBegin(size_t mark,size_t * nErrors)434 TfDiagnosticMgr::_GetErrorMarkBegin(size_t mark, size_t *nErrors)
435 {
436     ErrorList &errorList = _errorList.local();
437 
438     if (mark >= _nextSerial || errorList.empty()) {
439         if (nErrors)
440             *nErrors = 0;
441         return errorList.end();
442     }
443 
444     // Search backward to find the the error with the smallest serial number
445     // that's greater than or equal to mark.
446     size_t count = 0;
447 
448     ErrorList::reverse_iterator i = errorList.rbegin(), end = errorList.rend();
449     while (i != end && i->_serial >= mark) {
450         ++i, ++count;
451     }
452 
453     if (nErrors)
454         *nErrors = count;
455     return i.base();
456 }
457 
458 TfDiagnosticMgr::ErrorIterator
EraseRange(ErrorIterator first,ErrorIterator last)459 TfDiagnosticMgr::EraseRange(ErrorIterator first, ErrorIterator last)
460 {
461     if (first == last)
462         return last;
463 
464     ErrorIterator result = _errorList.local().erase(first, last);
465     _RebuildErrorLogText();
466     return result;
467 }
468 
469 void
PostWithInfo(const string & msg,TfDiagnosticInfo info) const470 TfDiagnosticMgr::ErrorHelper::PostWithInfo(
471     const string& msg, TfDiagnosticInfo info) const
472 {
473     TfDiagnosticMgr::GetInstance().PostError(_errorCode, _errorCodeString,
474         _context, msg, info, false);
475 }
476 
477 void
Post(const string & msg) const478 TfDiagnosticMgr::ErrorHelper::Post(const string& msg) const
479 {
480     TfDiagnosticMgr::GetInstance().PostError(_errorCode, _errorCodeString,
481         _context, msg, TfDiagnosticInfo(), false);
482 }
483 
484 void
PostQuietly(const string & msg,TfDiagnosticInfo info) const485 TfDiagnosticMgr::ErrorHelper::PostQuietly(
486     const string& msg, TfDiagnosticInfo info) const
487 {
488     TfDiagnosticMgr::GetInstance().PostError(_errorCode, _errorCodeString,
489         _context, msg, info, true);
490 }
491 
492 
493 void
Post(const char * fmt,...) const494 TfDiagnosticMgr::ErrorHelper::Post(const char* fmt, ...) const
495 {
496     va_list ap;
497     va_start(ap, fmt);
498     Post(TfVStringPrintf(fmt, ap));
499     va_end(ap);
500 }
501 
502 void
PostQuietly(const char * fmt,...) const503 TfDiagnosticMgr::ErrorHelper::PostQuietly(const char* fmt, ...) const
504 {
505     va_list ap;
506     va_start(ap, fmt);
507     PostQuietly(TfVStringPrintf(fmt, ap));
508     va_end(ap);
509 }
510 
511 void
Post(const char * fmt,...) const512 TfDiagnosticMgr::WarningHelper::Post(const char *fmt, ...) const
513 {
514     va_list ap;
515     va_start(ap, fmt);
516     Post(TfVStringPrintf(fmt, ap));
517     va_end(ap);
518 }
519 
520 void
Post(const string & msg) const521 TfDiagnosticMgr::WarningHelper::Post(const string& msg) const
522 {
523     TfDiagnosticMgr::GetInstance().PostWarning(_warningCode, _warningCodeString,
524         _context, msg, TfDiagnosticInfo(), false);
525 }
526 
527 void
PostWithInfo(const string & msg,TfDiagnosticInfo info) const528 TfDiagnosticMgr::WarningHelper::PostWithInfo(
529     const string& msg, TfDiagnosticInfo info) const
530 {
531     TfDiagnosticMgr::GetInstance().PostWarning(_warningCode, _warningCodeString,
532         _context, msg, info, false);
533 }
534 
535 void
Post(const char * fmt,...) const536 TfDiagnosticMgr::StatusHelper::Post(const char *fmt, ...) const
537 {
538     va_list ap;
539     va_start(ap, fmt);
540     Post(TfVStringPrintf(fmt, ap));
541     va_end(ap);
542 }
543 
544 void
Post(const string & msg) const545 TfDiagnosticMgr::StatusHelper::Post(const string& msg) const
546 {
547     TfDiagnosticMgr::GetInstance().PostStatus(_statusCode, _statusCodeString,
548         _context, msg, TfDiagnosticInfo(), false);
549 }
550 
551 void
PostWithInfo(const string & msg,TfDiagnosticInfo info) const552 TfDiagnosticMgr::StatusHelper::PostWithInfo(
553     const string& msg, TfDiagnosticInfo info) const
554 {
555     TfDiagnosticMgr::GetInstance().PostStatus(_statusCode, _statusCodeString,
556         _context, msg, info, false);
557 }
558 
559 /* statuc */
560 std::string
GetCodeName(const TfEnum & code)561 TfDiagnosticMgr::GetCodeName(const TfEnum &code)
562 {
563     string codeName = TfEnum::GetDisplayName(code);
564     if (codeName.empty()) {
565         codeName = TfStringPrintf("(%s)%d",
566             ArchGetDemangled(code.GetType()).c_str(),
567             code.GetValueAsInt());
568     }
569     return codeName;
570 }
571 
572 void
_SetLogInfoForErrors(std::vector<std::string> const & logText) const573 TfDiagnosticMgr::_SetLogInfoForErrors(
574     std::vector<std::string> const &logText) const
575 {
576     ArchSetExtraLogInfoForErrors(
577         TfStringPrintf("Thread %s Pending Diagnostics",
578             TfStringify(std::this_thread::get_id()).c_str()),
579         logText.empty() ? nullptr : &logText);
580 }
581 
582 void
AppendAndPublish(ErrorIterator begin,ErrorIterator end)583 TfDiagnosticMgr::_LogText::AppendAndPublish(
584     ErrorIterator begin, ErrorIterator end)
585 {
586     return _AppendAndPublishImpl(/*clear=*/false, begin, end);
587 }
588 
589 void
RebuildAndPublish(ErrorIterator begin,ErrorIterator end)590 TfDiagnosticMgr::_LogText::RebuildAndPublish(
591     ErrorIterator begin, ErrorIterator end)
592 {
593     return _AppendAndPublishImpl(/*clear=*/true, begin, end);
594 }
595 
596 void
_AppendAndPublishImpl(bool clear,ErrorIterator begin,ErrorIterator end)597 TfDiagnosticMgr::_LogText::_AppendAndPublishImpl(
598     bool clear, ErrorIterator begin, ErrorIterator end)
599 {
600     // The requirement at the Arch level for ArchSetExtraLogInfoForErrors is
601     // that the pointer we hand it must remain valid, and we can't mutate the
602     // structure it points to since if another thread crashes, Arch will read it
603     // to generate the crash report.  So instead we maintain two copies.  We
604     // update one copy to the new text anpd then publish it to Arch, effectively
605     // swapping out the old copy.  Then we update the old copy to match.  Next
606     // time through we do it again but with the data structures swapped, which
607     // is tracked by 'parity'.
608     auto *first = &texts.first;
609     auto *second = &texts.second;
610     if (parity)
611         std::swap(first, second);
612 
613     // Update first.
614     if (clear)
615         first->clear();
616     for (ErrorIterator i = begin; i != end; ++i) {
617         first->push_back(_FormatDiagnostic(*i, i->_info));
618     }
619 
620     // Publish.
621     ArchSetExtraLogInfoForErrors(
622         TfStringPrintf("Thread %s Pending Diagnostics",
623                        TfStringify(std::this_thread::get_id()).c_str()),
624         first->empty() ? nullptr : first);
625 
626     // Update second to match, arch is no longer looking at it.
627     if (clear)
628         second->clear();
629     for (ErrorIterator i = begin; i != end; ++i) {
630         second->push_back(_FormatDiagnostic(*i, i->_info));
631     }
632 
633     // Switch parity.
634     parity = !parity;
635 }
636 
637 void
_AppendErrorsToLogText(ErrorIterator i)638 TfDiagnosticMgr::_AppendErrorsToLogText(ErrorIterator i)
639 {
640     _logText.local().AppendAndPublish(i, GetErrorEnd());
641 }
642 
643 void
_RebuildErrorLogText()644 TfDiagnosticMgr::_RebuildErrorLogText()
645 {
646     _logText.local().RebuildAndPublish(GetErrorBegin(), GetErrorEnd());
647 }
648 
649 std::string
FormatDiagnostic(const TfEnum & code,const TfCallContext & context,const std::string & msg,const TfDiagnosticInfo & info)650 TfDiagnosticMgr::FormatDiagnostic(const TfEnum &code,
651         const TfCallContext &context, const std::string &msg,
652         const TfDiagnosticInfo &info)
653 {
654     string output;
655     string codeName = TfDiagnosticMgr::GetCodeName(code);
656     if (context.IsHidden() ||
657         !strcmp(context.GetFunction(), "") || !strcmp(context.GetFile(), "")) {
658         output = TfStringPrintf("%s%s: %s [%s]\n",
659                                 codeName.c_str(),
660                                 ArchIsMainThread() ? "" : " (secondary thread)",
661                                 msg.c_str(),
662                                 ArchGetProgramNameForErrors());
663     }
664     else {
665         output = TfStringPrintf("%s%s: in %s at line %zu of %s -- %s\n",
666                                 codeName.c_str(),
667                                 ArchIsMainThread() ? "" : " (secondary thread)",
668                                 context.GetFunction(),
669                                 context.GetLine(),
670                                 context.GetFile(),
671                                 msg.c_str());
672     }
673 
674 #ifdef PXR_PYTHON_SUPPORT_ENABLED
675     if (const TfPyExceptionState* exc =
676             boost::any_cast<TfPyExceptionState>(&info)) {
677         output += TfStringPrintf("%s\n", exc->GetExceptionString().c_str());
678     }
679 #endif // PXR_PYTHON_SUPPORT_ENABLED
680 
681     return output;
682 }
683 
684 static std::string
_FormatDiagnostic(const TfDiagnosticBase & d,const TfDiagnosticInfo & info)685 _FormatDiagnostic(const TfDiagnosticBase &d, const TfDiagnosticInfo &info) {
686     return TfDiagnosticMgr::FormatDiagnostic(d.GetDiagnosticCode(),
687             d.GetContext(), d.GetCommentary(), info);
688 }
689 
690 static void
_PrintDiagnostic(FILE * fout,const TfEnum & code,const TfCallContext & context,const std::string & msg,const TfDiagnosticInfo & info)691 _PrintDiagnostic(FILE *fout, const TfEnum &code, const TfCallContext &context,
692     const std::string& msg, const TfDiagnosticInfo &info)
693 {
694     fprintf(fout, "%s", TfDiagnosticMgr::FormatDiagnostic(code, context, msg,
695                 info).c_str());
696 }
697 
698 PXR_NAMESPACE_CLOSE_SCOPE
699