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