• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 // This is a useful error message when encountering XLA Compiler errors that
34 // could be handled with the non-strict AutoJit mode.
35 extern const char kPossibleAutoJitAlternative[];
36 
37 // Stream object used to collect error messages in MAKE_ERROR macros
38 // or append error messages with APPEND_ERROR.  It accepts any
39 // arguments with operator<< to build an error string, and then has an
40 // implicit cast operator to Status, which converts the
41 // logged string to a Status object and returns it, after logging the
42 // error.  At least one call to operator<< is required; a compile time
43 // error will be generated if none are given. Errors will only be
44 // logged by default for certain status codes, as defined in
45 // IsLoggedByDefault. This class will give ERROR errors if you don't
46 // retrieve a Status exactly once before destruction.
47 //
48 // The class converts into an intermediate wrapper object
49 // MakeErrorStreamWithOutput to check that the error stream gets at least one
50 // item of input.
51 class MakeErrorStream {
52  public:
53   // Wrapper around MakeErrorStream that only allows for output. This
54   // is created as output of the first operator<< call on
55   // MakeErrorStream. The bare MakeErrorStream does not have a
56   // Status operator. The net effect of that is that you
57   // have to call operator<< at least once or else you'll get a
58   // compile time error.
59   class MakeErrorStreamWithOutput {
60    public:
MakeErrorStreamWithOutput(MakeErrorStream * error_stream)61     explicit MakeErrorStreamWithOutput(MakeErrorStream* error_stream)
62         : wrapped_error_stream_(error_stream) {}
63 
64     template <typename T>
65     MakeErrorStreamWithOutput& operator<<(const T& value) {
66       *wrapped_error_stream_ << value;
67       return *this;
68     }
69 
70     // Implicit cast operators to Status and StatusOr.
71     // Exactly one of these must be called exactly once before destruction.
Status()72     operator Status() { return wrapped_error_stream_->GetStatus(); }
73     template <typename T>
74     operator xla::StatusOr<T>() {
75       return wrapped_error_stream_->GetStatus();
76     }
77 
78    private:
79     MakeErrorStream* wrapped_error_stream_;
80 
81     TF_DISALLOW_COPY_AND_ASSIGN(MakeErrorStreamWithOutput);
82   };
83 
84   // When starting from an existing error status, this determines whether we'll
85   // append or prepend to that status's error message.
86   enum PriorMessageHandling { kAppendToPriorMessage, kPrependToPriorMessage };
87 
88   // Make an error with the given code.
89   template <typename ERROR_CODE_TYPE>
MakeErrorStream(const char * file,int line,ERROR_CODE_TYPE code)90   MakeErrorStream(const char* file, int line, ERROR_CODE_TYPE code)
91       : impl_(new Impl(file, line, code, this, true)) {}
92 
93   template <typename T>
94   MakeErrorStreamWithOutput& operator<<(const T& value) {
95     CheckNotDone();
96     impl_->stream_ << value;
97     return impl_->make_error_stream_with_output_wrapper_;
98   }
99 
100   // When this message is logged (see with_logging()), include the stack trace.
with_log_stack_trace()101   MakeErrorStream& with_log_stack_trace() {
102     impl_->should_log_stack_trace_ = true;
103     return *this;
104   }
105 
106   // Adds RET_CHECK failure text to error message.
add_ret_check_failure(const char * condition)107   MakeErrorStreamWithOutput& add_ret_check_failure(const char* condition) {
108     return *this << "RET_CHECK failure (" << impl_->file_ << ":" << impl_->line_
109                  << ") " << condition << " ";
110   }
111 
112  private:
113   class Impl {
114    public:
115     Impl(const char* file, int line, tensorflow::error::Code code,
116          MakeErrorStream* error_stream, bool is_logged_by_default = true);
117     Impl(const Status& status, PriorMessageHandling prior_message_handling,
118          const char* file, int line, MakeErrorStream* error_stream);
119 
120     ~Impl();
121 
122     // This must be called exactly once before destruction.
123     Status GetStatus();
124 
125     void CheckNotDone() const;
126 
127    private:
128     const char* file_;
129     int line_;
130     tensorflow::error::Code code_;
131 
132     PriorMessageHandling prior_message_handling_ = kAppendToPriorMessage;
133     string prior_message_;
134     bool is_done_;  // true after Status object has been returned
135     std::ostringstream stream_;
136     bool should_log_;
137     int log_severity_;
138     bool should_log_stack_trace_;
139 
140     // Wrapper around the MakeErrorStream object that has a
141     // Status conversion. The first << operator called on
142     // MakeErrorStream will return this object, and only this object
143     // can implicitly convert to Status. The net effect of
144     // this is that you'll get a compile time error if you call
145     // MAKE_ERROR etc. without adding any output.
146     MakeErrorStreamWithOutput make_error_stream_with_output_wrapper_;
147 
148     friend class MakeErrorStream;
149     TF_DISALLOW_COPY_AND_ASSIGN(Impl);
150   };
151 
152   void CheckNotDone() const;
153 
154   // Returns the status. Used by MakeErrorStreamWithOutput.
GetStatus()155   Status GetStatus() const { return impl_->GetStatus(); }
156 
157   // Store the actual data on the heap to reduce stack frame sizes.
158   std::unique_ptr<Impl> impl_;
159 
160   TF_DISALLOW_COPY_AND_ASSIGN(MakeErrorStream);
161 };
162 
163 // Provides a conversion to bool so that it can be used inside an if statement
164 // that declares a variable.
165 class StatusAdaptorForMacros {
166  public:
StatusAdaptorForMacros(Status status)167   explicit StatusAdaptorForMacros(Status status) : status_(std::move(status)) {}
168 
169   StatusAdaptorForMacros(const StatusAdaptorForMacros&) = delete;
170   StatusAdaptorForMacros& operator=(const StatusAdaptorForMacros&) = delete;
171 
172   explicit operator bool() const { return TF_PREDICT_TRUE(status_.ok()); }
173 
Consume()174   Status&& Consume() { return std::move(status_); }
175 
176  private:
177   Status status_;
178 };
179 
180 }  // namespace status_macros
181 }  // namespace xla
182 
183 #define TF_RET_CHECK(condition)                                           \
184   while (TF_PREDICT_FALSE(!(condition)))                                  \
185   return xla::status_macros::MakeErrorStream(__FILE__, __LINE__,          \
186                                              tensorflow::error::INTERNAL) \
187       .with_log_stack_trace()                                             \
188       .add_ret_check_failure(#condition)
189 
190 #define TF_ASSERT_OK_AND_ASSIGN(lhs, rexpr)                             \
191   TF_ASSERT_OK_AND_ASSIGN_IMPL(                                         \
192       TF_STATUS_MACROS_CONCAT_NAME(_status_or_value, __COUNTER__), lhs, \
193       rexpr);
194 
195 #define TF_ASSERT_OK_AND_ASSIGN_IMPL(statusor, lhs, rexpr)  \
196   auto statusor = (rexpr);                                  \
197   ASSERT_TRUE(statusor.status().ok()) << statusor.status(); \
198   lhs = std::move(statusor.ValueOrDie())
199 
200 #define TF_STATUS_MACROS_CONCAT_NAME(x, y) TF_STATUS_MACROS_CONCAT_IMPL(x, y)
201 #define TF_STATUS_MACROS_CONCAT_IMPL(x, y) x##y
202 
203 #define TF_ASSIGN_OR_RETURN(lhs, rexpr) \
204   TF_ASSIGN_OR_RETURN_IMPL(             \
205       TF_STATUS_MACROS_CONCAT_NAME(_status_or_value, __COUNTER__), lhs, rexpr)
206 
207 #define TF_ASSIGN_OR_RETURN_IMPL(statusor, lhs, rexpr) \
208   auto statusor = (rexpr);                             \
209   if (TF_PREDICT_FALSE(!statusor.ok())) {              \
210     return statusor.status();                          \
211   }                                                    \
212   lhs = std::move(statusor.ValueOrDie())
213 
214 #endif  // TENSORFLOW_COMPILER_XLA_STATUS_MACROS_H_
215