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