1 | /* Copyright 2017 The TensorFlow Authors. All Rights Reserved. |
2 | |
3 | Licensed under the Apache License, Version 2.0 (the "License"); |
4 | you may not use this file except in compliance with the License. |
5 | You may obtain a copy of the License at |
6 | |
7 | http://www.apache.org/licenses/LICENSE-2.0 |
8 | |
9 | Unless required by applicable law or agreed to in writing, software |
10 | distributed under the License is distributed on an "AS IS" BASIS, |
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | See the License for the specific language governing permissions and |
13 | limitations under the License. |
14 | ==============================================================================*/ |
15 | |
16 | #ifndef TENSORFLOW_COMPILER_XLA_STATUS_MACROS_H_ |
17 | #define TENSORFLOW_COMPILER_XLA_STATUS_MACROS_H_ |
18 | |
19 | #include <memory> |
20 | #include <ostream> // NOLINT |
21 | #include <string> |
22 | #include <vector> |
23 | |
24 | #include "tensorflow/compiler/xla/statusor.h" |
25 | #include "tensorflow/compiler/xla/types.h" |
26 | #include "tensorflow/core/lib/core/status.h" |
27 | #include "tensorflow/core/platform/logging.h" |
28 | #include "tensorflow/core/platform/macros.h" |
29 | |
30 | namespace xla { |
31 | namespace status_macros { |
32 | |
33 | // Stream object used to collect error messages in MAKE_ERROR macros |
34 | // or append error messages with APPEND_ERROR. It accepts any |
35 | // arguments with operator<< to build an error string, and then has an |
36 | // implicit cast operator to Status, which converts the |
37 | // logged string to a Status object and returns it, after logging the |
38 | // error. At least one call to operator<< is required; a compile time |
39 | // error will be generated if none are given. Errors will only be |
40 | // logged by default for certain status codes, as defined in |
41 | // IsLoggedByDefault. This class will give ERROR errors if you don't |
42 | // retrieve a Status exactly once before destruction. |
43 | // |
44 | // The class converts into an intermediate wrapper object |
45 | // MakeErrorStreamWithOutput to check that the error stream gets at least one |
46 | // item of input. |
47 | class MakeErrorStream { |
48 | public: |
49 | // Wrapper around MakeErrorStream that only allows for output. This |
50 | // is created as output of the first operator<< call on |
51 | // MakeErrorStream. The bare MakeErrorStream does not have a |
52 | // Status operator. The net effect of that is that you |
53 | // have to call operator<< at least once or else you'll get a |
54 | // compile time error. |
55 | class MakeErrorStreamWithOutput { |
56 | public: |
57 | explicit MakeErrorStreamWithOutput(MakeErrorStream* error_stream) |
58 | : wrapped_error_stream_(error_stream) {} |
59 | |
60 | template <typename T> |
61 | MakeErrorStreamWithOutput& operator<<(const T& value) { |
62 | *wrapped_error_stream_ << value; |
63 | return *this; |
64 | } |
65 | |
66 | // Implicit cast operators to Status and StatusOr. |
67 | // Exactly one of these must be called exactly once before destruction. |
68 | operator Status() { return wrapped_error_stream_->GetStatus(); } |
69 | template <typename T> |
70 | operator xla::StatusOr<T>() { |
71 | return wrapped_error_stream_->GetStatus(); |
72 | } |
73 | |
74 | private: |
75 | MakeErrorStream* wrapped_error_stream_; |
76 | |
77 | TF_DISALLOW_COPY_AND_ASSIGN(MakeErrorStreamWithOutput); |
78 | }; |
79 | |
80 | // When starting from an existing error status, this determines whether we'll |
81 | // append or prepend to that status's error message. |
82 | enum PriorMessageHandling { kAppendToPriorMessage, kPrependToPriorMessage }; |
83 | |
84 | // Make an error with the given code. |
85 | template <typename ERROR_CODE_TYPE> |
86 | MakeErrorStream(const char* file, int line, ERROR_CODE_TYPE code) |
87 | : impl_(new Impl(file, line, code, this, true)) {} |
88 | |
89 | template <typename T> |
90 | MakeErrorStreamWithOutput& operator<<(const T& value) { |
91 | CheckNotDone(); |
92 | impl_->stream_ << value; |
93 | return impl_->make_error_stream_with_output_wrapper_; |
94 | } |
95 | |
96 | // When this message is logged (see with_logging()), include the stack trace. |
97 | MakeErrorStream& with_log_stack_trace() { |
98 | impl_->should_log_stack_trace_ = true; |
99 | return *this; |
100 | } |
101 | |
102 | // Adds RET_CHECK failure text to error message. |
103 | MakeErrorStreamWithOutput& add_ret_check_failure(const char* condition) { |
104 | return *this << "RET_CHECK failure (" << impl_->file_ << ":" << impl_->line_ |
105 | << ") " << condition << " " ; |
106 | } |
107 | |
108 | private: |
109 | class Impl { |
110 | public: |
111 | Impl(const char* file, int line, tensorflow::error::Code code, |
112 | MakeErrorStream* error_stream, bool is_logged_by_default = true); |
113 | Impl(const Status& status, PriorMessageHandling prior_message_handling, |
114 | const char* file, int line, MakeErrorStream* error_stream); |
115 | |
116 | ~Impl(); |
117 | |
118 | // This must be called exactly once before destruction. |
119 | Status GetStatus(); |
120 | |
121 | void CheckNotDone() const; |
122 | |
123 | private: |
124 | const char* file_; |
125 | int line_; |
126 | tensorflow::error::Code code_; |
127 | |
128 | PriorMessageHandling prior_message_handling_ = kAppendToPriorMessage; |
129 | string prior_message_; |
130 | bool is_done_; // true after Status object has been returned |
131 | std::ostringstream stream_; |
132 | bool should_log_; |
133 | int log_severity_; |
134 | bool should_log_stack_trace_; |
135 | |
136 | // Wrapper around the MakeErrorStream object that has a |
137 | // Status conversion. The first << operator called on |
138 | // MakeErrorStream will return this object, and only this object |
139 | // can implicitly convert to Status. The net effect of |
140 | // this is that you'll get a compile time error if you call |
141 | // MAKE_ERROR etc. without adding any output. |
142 | MakeErrorStreamWithOutput make_error_stream_with_output_wrapper_; |
143 | |
144 | friend class MakeErrorStream; |
145 | TF_DISALLOW_COPY_AND_ASSIGN(Impl); |
146 | }; |
147 | |
148 | void CheckNotDone() const; |
149 | |
150 | // Returns the status. Used by MakeErrorStreamWithOutput. |
151 | Status GetStatus() const { return impl_->GetStatus(); } |
152 | |
153 | // Store the actual data on the heap to reduce stack frame sizes. |
154 | std::unique_ptr<Impl> impl_; |
155 | |
156 | TF_DISALLOW_COPY_AND_ASSIGN(MakeErrorStream); |
157 | }; |
158 | |
159 | // Provides a conversion to bool so that it can be used inside an if statement |
160 | // that declares a variable. |
161 | class StatusAdaptorForMacros { |
162 | public: |
163 | explicit StatusAdaptorForMacros(Status status) : status_(std::move(status)) {} |
164 | |
165 | StatusAdaptorForMacros(const StatusAdaptorForMacros&) = delete; |
166 | StatusAdaptorForMacros& operator=(const StatusAdaptorForMacros&) = delete; |
167 | |
168 | explicit operator bool() const { return TF_PREDICT_TRUE(status_.ok()); } |
169 | |
170 | Status&& Consume() { return std::move(status_); } |
171 | |
172 | private: |
173 | Status status_; |
174 | }; |
175 | |
176 | } // namespace status_macros |
177 | } // namespace xla |
178 | |
179 | #define TF_RET_CHECK(condition) \ |
180 | while (TF_PREDICT_FALSE(!(condition))) \ |
181 | return xla::status_macros::MakeErrorStream(__FILE__, __LINE__, \ |
182 | tensorflow::error::INTERNAL) \ |
183 | .with_log_stack_trace() \ |
184 | .add_ret_check_failure(#condition) |
185 | |
186 | #define TF_ASSERT_OK_AND_ASSIGN(lhs, rexpr) \ |
187 | TF_ASSERT_OK_AND_ASSIGN_IMPL( \ |
188 | TF_STATUS_MACROS_CONCAT_NAME(_status_or_value, __COUNTER__), lhs, \ |
189 | rexpr); |
190 | |
191 | #define TF_ASSERT_OK_AND_ASSIGN_IMPL(statusor, lhs, rexpr) \ |
192 | auto statusor = (rexpr); \ |
193 | ASSERT_TRUE(statusor.status().ok()) << statusor.status(); \ |
194 | lhs = std::move(statusor.ValueOrDie()) |
195 | |
196 | #define TF_STATUS_MACROS_CONCAT_NAME(x, y) TF_STATUS_MACROS_CONCAT_IMPL(x, y) |
197 | #define TF_STATUS_MACROS_CONCAT_IMPL(x, y) x##y |
198 | |
199 | #define TF_ASSIGN_OR_RETURN(lhs, rexpr) \ |
200 | TF_ASSIGN_OR_RETURN_IMPL( \ |
201 | TF_STATUS_MACROS_CONCAT_NAME(_status_or_value, __COUNTER__), lhs, rexpr) |
202 | |
203 | #define TF_ASSIGN_OR_RETURN_IMPL(statusor, lhs, rexpr) \ |
204 | auto statusor = (rexpr); \ |
205 | if (TF_PREDICT_FALSE(!statusor.ok())) { \ |
206 | return statusor.status(); \ |
207 | } \ |
208 | lhs = std::move(statusor.ValueOrDie()) |
209 | |
210 | #endif // TENSORFLOW_COMPILER_XLA_STATUS_MACROS_H_ |
211 | |