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: MakeErrorStreamWithOutput(MakeErrorStream * error_stream)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. Status()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> MakeErrorStream(const char * file,int line,ERROR_CODE_TYPE code)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. with_log_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. add_ret_check_failure(const char * condition)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. GetStatus()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: StatusAdaptorForMacros(Status status)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 Consume()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