HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
diagnostic.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_H
8 #define PXR_BASE_TF_DIAGNOSTIC_H
9 
10 /// \file tf/diagnostic.h
11 /// \ingroup group_tf_Diagnostic
12 /// Low-level utilities for informing users of various internal and external
13 /// diagnostic conditions.
14 ///
15 /// lib/tf supports a range of error-reporting routines.
16 ///
17 /// For a more detailed explanation of when each of the facilities described
18 /// in this file is appropriate, (and more importantly, when they're not!)
19 /// see \ref page_tf_Diagnostic.
20 
21 #include "pxr/pxr.h"
22 #include "pxr/base/arch/function.h"
24 #include "pxr/base/tf/api.h"
25 
26 #if defined(__cplusplus) || defined (doxygen)
27 
28 #include "pxr/base/arch/hints.h"
30 
31 #include <stddef.h>
32 #include <stdarg.h>
33 #include <string>
34 
36 
37 // Note: diagnosticLite.h defines the various macros, but we'll document
38 // them here. The following block is only for doxygen, not seen by a real
39 // compile. To see the actual macro definition, look in diagnosticLite.h.
40 
41 #if defined(doxygen)
42 
43 /// \addtogroup group_tf_Diagnostic
44 ///@{
45 
46 /// Issue an internal programming error, but continue execution.
47 ///
48 /// Please see \ref page_tf_TfError for more information about how to use
49 /// TF_ERROR().
50 ///
51 /// This is safe to call in secondary threads.
52 ///
53 /// \hideinitializer
54 #define TF_ERROR(...)
55 
56 /// Issue an internal programming error, but continue execution.
57 ///
58 /// This macro is a convenience. It produces a TF_ERROR() with an error code
59 /// indicating a coding error. It takes a printf-like format specification or a
60 /// std::string. Generally, an error handling delegate will take action to turn
61 /// this error into a python exception, and if it remains unhandled at the end of
62 /// an application iteration will roll-back the undo stack to a last-known-good
63 /// state.
64 ///
65 /// This is safe to call in secondary threads.
66 ///
67 /// \hideinitializer
68 #define TF_CODING_ERROR(fmt, args)
69 
70 /// Issue a generic runtime error, but continue execution.
71 ///
72 /// This macro is a convenience. It produces a TF_ERROR() with an error code
73 /// indicating a generic runtime error. It is preferred over TF_ERROR(0),
74 /// but using a specific error code is preferred over this. It takes a
75 /// printf-like format specification or a std::string. Generally, an error
76 /// handling delegate will take action to turn this error into a python
77 /// exception, and if it remains unhandled at the end of an application iteration
78 /// will roll-back the undo stack to a last-known-good state.
79 ///
80 /// This is safe to call in secondary threads.
81 ///
82 /// \hideinitializer
83 #define TF_RUNTIME_ERROR(fmt, args)
84 
85 /// Issue a fatal error and end the program.
86 ///
87 /// This macro takes a printf-like format specification or a std::string. The
88 /// program will generally terminate upon a fatal error.
89 ///
90 /// \hideinitializer
91 #define TF_FATAL_ERROR(fmt, args)
92 
93 /// Issue a warning, but continue execution.
94 ///
95 /// This macro works with a variety of argument sets. It supports simple
96 /// printf-like format specification or a std::string. It also supports
97 /// specification of a diagnostic code and a piece of arbitrary information in
98 /// the form of a TfDiagnosticInfo. The following is a full list of supported
99 /// argument lists:
100 ///
101 /// \code
102 /// TF_WARN(const char *) // plain old string
103 /// TF_WARN(const char *, ...) // printf like formatting
104 /// TF_WARN(std::string) // stl string
105 /// \endcode
106 ///
107 /// A diagnostic code can be passed in along with the warning message. See
108 /// \ref DiagnosticEnumConventions for an example of registering an enum type
109 /// and it's values as diagnostic codes.
110 ///
111 /// \code
112 /// TF_WARN(DIAGNOSTIC_ENUM, const char *)
113 /// TF_WARN(DIAGNOSTIC_ENUM, const char *, ...)
114 /// TF_WARN(DIAGNOSTIC_ENUM, std::string)
115 /// \endcode
116 ///
117 /// A piece of arbitrary data can also be passed in along with the diagnostic
118 /// code and warning message as follows:
119 ///
120 /// \code
121 /// TF_WARN(info, DIAGNOSTIC_ENUM, const char *)
122 /// TF_WARN(info, DIAGNOSTIC_ENUM, const char *, ...)
123 /// TF_WARN(info, DIAGNOSTIC_ENUM, std::string)
124 /// \endcode
125 ///
126 /// Generally, no adjustment to program state should occur as the result of
127 /// this macro. This is in contrast with errors as mentioned above.
128 ///
129 /// This is safe to call in secondary threads.
130 ///
131 /// \hideinitializer
132 #define TF_WARN(...)
133 
134 /// Issue a status message, but continue execution.
135 ///
136 /// This macro works with a variety of argument sets. It supports simple
137 /// printf-like format specification or a std::string. It also supports
138 /// specification of a diagnostic code and a piece of arbitrary information in
139 /// the form of a TfDiagnosticInfo. The following is a full list of supported
140 /// argument lists:
141 ///
142 /// \code
143 /// TF_STATUS(const char *) // plain old string
144 /// TF_STATUS(const char *, ...) // printf like formatting
145 /// TF_STATUS(std::string) // stl string
146 /// \endcode
147 ///
148 /// A diagnostic code can be passed in along with the status message. See
149 /// \ref DiagnosticEnumConventions for an example of registering an enum type
150 /// and it's values as diagnostic codes.
151 ///
152 /// \code
153 /// TF_STATUS(DIAGNOSTIC_ENUM, const char *)
154 /// TF_STATUS(DIAGNOSTIC_ENUM, const char *, ...)
155 /// TF_STATUS(DIAGNOSTIC_ENUM, std::string)
156 /// \endcode
157 ///
158 /// A piece of arbitrary data can also be passed in along with the diagnostic
159 /// code and status message as follows:
160 ///
161 /// \code
162 /// TF_STATUS(info, DIAGNOSTIC_ENUM, const char *)
163 /// TF_STATUS(info, DIAGNOSTIC_ENUM, const char *, ...)
164 /// TF_STATUS(info, DIAGNOSTIC_ENUM, std::string)
165 /// \endcode
166 ///
167 /// Generally, no adjustment to program state should occur as the result of
168 /// this macro. This is in contrast with errors as mentioned above.
169 ///
170 /// This is safe to call in secondary threads.
171 ///
172 /// \hideinitializer
173 #define TF_STATUS(...)
174 
175 /// Aborts if the condition \c cond is not met.
176 ///
177 /// \param cond is any expression convertible to bool; if the condition evaluates
178 /// to \c false, program execution ends with this call.
179 ///
180 /// Note that the diagnostic message sent is the code \c cond, in the form of
181 /// a string. Unless the condition expression is self-explanatory, use
182 /// \c TF_FATAL_ERROR(). See \ref DiagnosticTF_FATAL_ERROR for further
183 /// discussion.
184 ///
185 /// Currently, a \c TF_AXIOM() statement is not made a no-op in optimized
186 /// builds; however, it always possible that either (a) the axiom statement
187 /// might be removed at some point if the code is deemed correct or (b) in the
188 /// future, some flavor of build might choose to make axioms be no-ops. Thus,
189 /// programmers must make \e certain that the code in \p cond is entirely free
190 /// of side effects.
191 ///
192 /// \hideinitializer
193 #define TF_AXIOM(cond)
194 
195 /// The same as TF_AXIOM, but compiled only in dev builds.
196 ///
197 /// \param cond is any expression convertible to bool; if the condition evaluates
198 /// to \c false, program execution ends with this call.
199 ///
200 /// This macro has the same behavior as TF_AXIOM, but it is compiled only
201 /// in dev builds. This version should only be used in code that is
202 /// known (not just suspected!) to be performance critical.
203 ///
204 /// \hideinitializer
205 #define TF_DEV_AXIOM(cond)
206 
207 /// Checks a condition and reports an error if it evaluates false.
208 ///
209 /// This can be thought of as something like a softer, recoverable TF_AXIOM.
210 ///
211 /// The macro expands to an expression whose value is either true or false
212 /// depending on \c cond. If \c cond evaluates to false, issues a coding error
213 /// indicating the failure.
214 ///
215 /// \param cond is any expression convertible to bool.
216 ///
217 /// Usage generally follows patterns like these:
218 /// \code
219 /// // Simple check. This is like a non-fatal TF_AXIOM.
220 /// TF_VERIFY(condition);
221 ///
222 /// // Avoiding code that requires the condition be met.
223 /// if (TF_VERIFY(condition)) {
224 /// // code requiring condition be met.
225 /// }
226 ///
227 /// // Executing recovery code in case the condition is not met.
228 /// if (not TF_VERIFY(condition)) {
229 /// // recovery code to execute since condition was not met.
230 /// }
231 /// \endcode
232 ///
233 /// Here are some examples:
234 /// \code
235 /// // List should be empty. If not, issue an error, clear it out and continue.
236 /// if (not TF_VERIFY(list.empty()) {
237 /// // The list was unexpectedly not empty. TF_VERIFY will have
238 /// // issued a coding error with details. We clear the list and continue.
239 /// list.clear();
240 /// }
241 ///
242 /// // Only add to string if ptr is valid.
243 /// string result = ...;
244 /// if (TF_VERIFY(ptr != NULL)) {
245 /// result += ptr->Method();
246 /// }
247 /// \endcode
248 ///
249 /// The macro also optionally accepts printf-style arguments to generate a
250 /// message emitted in case the condition is not met. For example:
251 /// \code
252 /// if (not TF_VERIFY(index < size,
253 /// "Index out of bounds (%zu >= %zu)", index, size)) {
254 /// // Recovery code...
255 /// }
256 /// \endcode
257 ///
258 /// Unmet conditions generate TF_CODING_ERRORs by default, but setting the
259 /// environment variable TF_FATAL_VERIFY to 1 will make unmet conditions
260 /// generate TF_FATAL_ERRORs instead and abort the program. This is intended for
261 /// testing.
262 ///
263 /// This is safe to call in secondary threads.
264 ///
265 /// \hideinitializer
266 #define TF_VERIFY(cond [, format, ...])
267 
268 #endif /* defined(doxygen) */
269 
270 //
271 // The rest of this is seen by a regular compile (or doxygen).
272 //
273 
274 #if defined(__cplusplus) || defined(doxygen)
275 
276 /// Get the name of the current function as a \c std::string.
277 ///
278 /// This macro will return the name of the current function, nicely
279 /// formatted, as an \c std::string. This is meant primarily for
280 /// diagnostics. Code should not rely on a specific format, because it
281 /// may change in the future or vary across architectures. For example,
282 /// \code
283 /// void YourClass::SomeMethod(int x) {
284 /// cout << "Debugging info about function " << TF_FUNC_NAME() << "." << endl;
285 /// ...
286 /// }
287 /// \endcode
288 /// Should display something like:
289 /// "Debugging info about function YourClass::SomeMethod."
290 ///
291 /// \hideinitializer
292 #define TF_FUNC_NAME() \
293  ArchGetPrettierFunctionName(__ARCH_FUNCTION__, __ARCH_PRETTY_FUNCTION__)
294 
295 void Tf_TerminateHandler();
296 
297 #if !defined(doxygen)
298 
299 // Redefine these macros from DiagnosticLite to versions that will accept
300 // either string or printf-like args.
301 
302 #ifdef TF_CODING_ERROR
303 #undef TF_CODING_ERROR
304 #endif
305 #define TF_CODING_ERROR(...) \
306  Tf_PostErrorHelper(TF_CALL_CONTEXT, \
307  TF_DIAGNOSTIC_CODING_ERROR_TYPE, __VA_ARGS__)
308 
309 #ifdef TF_FATAL_CODING_ERROR
310 #undef TF_FATAL_CODING_ERROR
311 #endif
312 #define TF_FATAL_CODING_ERROR \
313  Tf_DiagnosticHelper(TF_CALL_CONTEXT, \
314  TF_DIAGNOSTIC_CODING_ERROR_TYPE).IssueFatalError
315 
316 
317 #ifdef TF_CODING_WARNING
318 #undef TF_CODING_WARNING
319 #endif
320 #define TF_CODING_WARNING(...) \
321  Tf_PostWarningHelper(TF_CALL_CONTEXT, \
322  TF_DIAGNOSTIC_CODING_ERROR_TYPE, __VA_ARGS__)
323 
324 #ifdef TF_DIAGNOSTIC_WARNING
325 #undef TF_DIAGNOSTIC_WARNING
326 #endif
327 #define TF_DIAGNOSTIC_WARNING \
328  Tf_DiagnosticHelper(TF_CALL_CONTEXT.Hide(), \
329  TF_DIAGNOSTIC_WARNING_TYPE).IssueWarning
330 
331 #ifdef TF_RUNTIME_ERROR
332 #undef TF_RUNTIME_ERROR
333 #endif // TF_RUNTIME_ERROR
334 #define TF_RUNTIME_ERROR(...) \
335  Tf_PostErrorHelper(TF_CALL_CONTEXT, \
336  TF_DIAGNOSTIC_RUNTIME_ERROR_TYPE, __VA_ARGS__)
337 
338 #ifdef TF_FATAL_ERROR
339 #undef TF_FATAL_ERROR
340 #endif // TF_FATAL_ERROR
341 #define TF_FATAL_ERROR \
342  Tf_DiagnosticHelper(TF_CALL_CONTEXT, \
343  TF_DIAGNOSTIC_FATAL_ERROR_TYPE).IssueFatalError
344 
345 #ifdef TF_DIAGNOSTIC_FATAL_ERROR
346 #undef TF_DIAGNOSTIC_FATAL_ERROR
347 #endif // TF_DIAGNOSTIC_FATAL_ERROR
348 #define TF_DIAGNOSTIC_FATAL_ERROR \
349  Tf_DiagnosticHelper(TF_CALL_CONTEXT, \
350  TF_DIAGNOSTIC_RUNTIME_ERROR_TYPE).IssueFatalError
351 
352 #ifdef TF_DIAGNOSTIC_NONFATAL_ERROR
353 #undef TF_DIAGNOSTIC_NONFATAL_ERROR
354 #endif // TF_DIAGNOSTIC_NONFATAL_ERROR
355 #define TF_DIAGNOSTIC_NONFATAL_ERROR \
356  Tf_DiagnosticHelper(TF_CALL_CONTEXT, \
357  TF_DIAGNOSTIC_WARNING_TYPE).IssueWarning
358 
359 // Redefine the following three macros from DiagnosticLite to versions that will
360 // accept the following sets of arguments:
361 // * MACRO(const char *, ...)
362 // * MACRO(const std::string &msg)
363 // * MACRO(ENUM, const char *, ...)
364 // * MACRO(ENUM, const std::string *msg)
365 // * MACRO(TfDiagnosticInfo, ENUM, const char *, ...)
366 // * MACRO(TfDiagnosticInfo, ENUM, const std::string *msg)
367 
368 #ifdef TF_WARN
369 #undef TF_WARN
370 #endif // TF_WARN
371 #define TF_WARN(...) \
372  Tf_PostWarningHelper(TF_CALL_CONTEXT, __VA_ARGS__)
373 
374 #ifdef TF_STATUS
375 #undef TF_STATUS
376 #endif // TF_STATUS
377 #define TF_STATUS(...) \
378  Tf_PostStatusHelper(TF_CALL_CONTEXT, __VA_ARGS__)
379 
380 #ifdef TF_ERROR
381 #undef TF_ERROR
382 #endif // TF_ERROR
383 #define TF_ERROR(...) \
384  Tf_PostErrorHelper(TF_CALL_CONTEXT, __VA_ARGS__)
385 
386 #ifdef TF_QUIET_ERROR
387 #undef TF_QUIET_ERROR
388 #endif // TF_ERROR
389 #define TF_QUIET_ERROR(...) \
390  Tf_PostQuietlyErrorHelper(TF_CALL_CONTEXT, __VA_ARGS__)
391 
392 // See documentation above.
393 #define TF_VERIFY(cond, ...) \
394  (ARCH_LIKELY(cond) ? true : \
395  Tf_FailedVerifyHelper(TF_CALL_CONTEXT, # cond, \
396  Tf_VerifyStringFormat(__VA_ARGS__)))
397 
398 // Helpers for TF_VERIFY.
399 TF_API bool
400 Tf_FailedVerifyHelper(TfCallContext const &context,
401  char const *condition, char const *msg);
402 
403 // Helpers for TF_VERIFY.
404 inline char const *
405 Tf_VerifyStringFormat() { return nullptr; }
406 TF_API char const *
407 Tf_VerifyStringFormat(const char *format, ...) ARCH_PRINTF_FUNCTION(1, 2);
408 
409 #endif // !doxygen
410 
411 #endif // __cplusplus || doxygen
412 
413 /// Sets program name for reporting errors.
414 ///
415 /// This function simply calls to ArchSetProgramNameForErrors().
416 void TfSetProgramNameForErrors(std::string const& programName);
417 
418 /// Returns currently set program info.
419 ///
420 /// This function simply calls to ArchGetProgramNameForErrors().
421 std::string TfGetProgramNameForErrors();
422 
423 /// \private
424 struct Tf_DiagnosticHelper {
425  Tf_DiagnosticHelper(TfCallContext const &context,
427  _context(context),
428  _type(type)
429  {
430  }
431 
432  TfCallContext const &GetContext() const { return _context; }
433  TfDiagnosticType GetType() const { return _type; }
434 
435  TF_API void IssueError(std::string const &msg) const;
436  TF_API void IssueError(char const *fmt, ...) const ARCH_PRINTF_FUNCTION(2,3);
437  TF_API void IssueFatalError(std::string const &msg) const;
438  TF_API void IssueFatalError(char const *fmt, ...) const ARCH_PRINTF_FUNCTION(2,3);
439  TF_API void IssueWarning(std::string const &msg) const;
440  TF_API void IssueWarning(char const *fmt, ...) const ARCH_PRINTF_FUNCTION(2,3);
441  TF_API void IssueStatus(std::string const &msg) const;
442  TF_API void IssueStatus(char const *fmt, ...) const ARCH_PRINTF_FUNCTION(2,3);
443 
444  private:
445  TfCallContext _context;
446  TfDiagnosticType _type;
447 };
448 
449 #endif
450 
451 /// (Re)install Tf's crash handler. This should not generally need to be
452 /// called since Tf does this itself when loaded. However, when run in 3rd
453 /// party environments that install their own signal handlers, possibly
454 /// overriding Tf's, this provides a way to reinstall them, in hopes that
455 /// they'll stick.
456 ///
457 /// This calls std::set_terminate() and installs signal handlers for SIGSEGV,
458 /// SIGBUS, SIGFPE, and SIGABRT.
459 TF_API
461 
462 ///@}
463 
465 
466 #endif // PXR_BASE_TF_DIAGNOSTIC_H
#define TF_API
Definition: api.h:23
GLint GLint GLsizei GLint GLenum GLenum type
Definition: glcorearb.h:108
GLint GLint GLsizei GLint GLenum format
Definition: glcorearb.h:108
TfDiagnosticType
PXR_NAMESPACE_CLOSE_SCOPE PXR_NAMESPACE_OPEN_SCOPE
Definition: path.h:1425
#define PXR_NAMESPACE_CLOSE_SCOPE
Definition: pxr.h:74
TF_API void TfInstallTerminateAndCrashHandlers()