1 //
2 // Copyright 2021 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 #ifndef PXR_BASE_TF_EXCEPTION_H
26 #define PXR_BASE_TF_EXCEPTION_H
27 
28 /// \file tf/exception.h
29 /// \ingroup group_tf_Diagnostic
30 
31 #include "pxr/pxr.h"
32 #include "pxr/base/tf/api.h"
33 #include "pxr/base/tf/callContext.h"
34 #include "pxr/base/tf/functionRef.h"
35 
36 #include <cstdint>
37 #include <exception>
38 #include <string>
39 #include <vector>
40 
41 PXR_NAMESPACE_OPEN_SCOPE
42 
43 /// This structure is used to indicate that some number of caller frames should
44 /// be skipped when capturing exception stack traces at the throw point.
45 struct TfSkipCallerFrames
46 {
numToSkipTfSkipCallerFrames47     explicit TfSkipCallerFrames(int n=0) : numToSkip(n) {}
48     int numToSkip;
49 };
50 
51 /// The base class for exceptions supported by the Tf exceptions facilities.
52 /// Typical usage is to publically derive your own exception class from this
53 /// one, and throw using the TF_THROW() macro.
54 ///
55 /// Deriving this base class and throwing via TF_THROW() will record the throw
56 /// point's call context (see GetThrowContext()) and will also capture a portion
57 /// of the throwing thread's call stack (see GetThrowStack()).
58 ///
59 /// Additionally, the Tf library registers an exception translator with
60 /// boost.python to raise a Python exeption wrapping the thrown exception
61 /// object.  Similarly utilties that call Python via Tf will re-throw the
62 /// embedded C++ exception if the Python exception unwinds back into C++.
63 class TfBaseException : public std::exception
64 {
65 public:
66     TF_API
67     virtual ~TfBaseException();
68 
69     /// Construct with \p message, reported by this class's what()
70     /// implementation.
71     TF_API
72     explicit TfBaseException(std::string const &message);
73 
74     /// Return the call context from the throw point associated with this
75     /// exception.  Note that this context may be invalid if this exception was
76     /// not thrown with TF_THROW().
GetThrowContext()77     TfCallContext const &GetThrowContext() const {
78         return _callContext;
79     }
80 
81     /// Return the stack frame pointers from the throw point.  See
82     /// ArchPrintStackFrames() to turn these into human-readable strings.
GetThrowStack()83     std::vector<uintptr_t> const &GetThrowStack() const {
84         return _throwStack;
85     }
86 
87     /// Move the stack frame pointers from the throw point to \p out.  See
88     /// GetThrowStack() for more details.
MoveThrowStackTo(std::vector<uintptr_t> & out)89     void MoveThrowStackTo(std::vector<uintptr_t> &out) {
90         out = std::move(_throwStack);
91         _throwStack.clear();
92     }
93 
94     /// Override std::exception::what() to return the message passed during
95     /// construction.
96     TF_API
97     virtual const char *what() const noexcept override;
98 
99     // Friend throw support.
100     template <class Derived, class ... Args>
101     friend void
102     Tf_Throw(TfCallContext const &cc,
103              TfSkipCallerFrames skipFrames,
104              Args && ... args);
105 
106 private:
107     TF_API
108     static void _ThrowImpl(TfCallContext const &cc,
109                            TfBaseException &exc,
110                            TfFunctionRef<void ()> thrower,
111                            int skipNCallerFrames);
112 
113     TfCallContext _callContext;
114     std::vector<uintptr_t> _throwStack;
115     std::string _message;
116 };
117 
118 // TF_THROW() support function.
119 template <class Exception, class ... Args>
120 void
Tf_Throw(TfCallContext const & cc,TfSkipCallerFrames skipFrames,Args &&...args)121 Tf_Throw(TfCallContext const &cc,
122          TfSkipCallerFrames skipFrames,
123          Args && ... args) {
124     Exception exc(std::forward<Args>(args)...);
125     auto thrower = [&exc]() { throw std::move(exc); };
126     TfBaseException::_ThrowImpl(cc, exc, thrower, skipFrames.numToSkip);
127 }
128 
129 // TF_THROW() support function.
130 template <class Exception, class ... Args>
Tf_Throw(TfCallContext const & cc,Args &&...args)131 void Tf_Throw(TfCallContext const &cc, Args && ... args) {
132     Tf_Throw<Exception>(cc, TfSkipCallerFrames(), std::forward<Args>(args)...);
133 }
134 
135 #ifdef doxygen
136 
137 /// Construct an instance of Exception (which must derive TfBaseException) with
138 /// Exception-ctor-args and throw it.  Also capture a portion of this thread's
139 /// current call stack and the throw point's source filename & line number to
140 /// embed in the exception.  If the exception goes unhandled these will be
141 /// reported in the crash report that Tf's terminate handler generates, or in
142 /// the unhandled exception message in the python interpreter.
143 #define TF_THROW(Exception, Exception-ctor-args...)
144 #define TF_THROW(Exception, TfSkipCallerFrames, Exception-ctor-args...)
145 
146 #else
147 
148 #define TF_THROW(Exception, ...)                        \
149     Tf_Throw<Exception>(TF_CALL_CONTEXT, __VA_ARGS__)
150 
151 #endif
152 
153 PXR_NAMESPACE_CLOSE_SCOPE
154 
155 #endif // PXR_BASE_TF_EXCEPTION_H
156