• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright 2015 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_CORE_PLATFORM_STATUS_H_
17 #define TENSORFLOW_CORE_PLATFORM_STATUS_H_
18 
19 #include <functional>
20 #include <iosfwd>
21 #include <memory>
22 #include <set>
23 #include <string>
24 #include <unordered_map>
25 #include <utility>
26 
27 #include "absl/status/status.h"
28 #include "absl/strings/cord.h"
29 #include "absl/strings/string_view.h"
30 #include "absl/types/optional.h"
31 #include "tensorflow/core/platform/logging.h"
32 #include "tensorflow/core/platform/macros.h"
33 #include "tensorflow/core/platform/stack_frame.h"
34 #include "tensorflow/core/platform/types.h"
35 #include "tensorflow/core/protobuf/error_codes.pb.h"
36 
37 namespace tensorflow {
38 
39 #if defined(__clang__)
40 // Only clang supports warn_unused_result as a type annotation.
41 class TF_MUST_USE_RESULT Status;
42 #endif
43 
44 #if ABSL_HAVE_BUILTIN(__builtin_LINE) && ABSL_HAVE_BUILTIN(__builtin_FILE)
45 #define TF_INTERNAL_HAVE_BUILTIN_LINE_FILE 1
46 #endif
47 
48 struct SourceLocation {
49   uint32_t line;
50   const char* file_name;
51 
52 #ifdef TF_INTERNAL_HAVE_BUILTIN_LINE_FILE
53   static SourceLocation current(uint32_t line = __builtin_LINE(),
54                                 const char* file_name = __builtin_FILE()) {
55     SourceLocation loc;
56     loc.line = line;
57     loc.file_name = file_name;
58     return loc;
59   }
60 #else
61   static SourceLocation current(uint32_t line = 0,
62                                 const char* file_name = nullptr) {
63     SourceLocation loc;
64     loc.line = line;
65     loc.file_name = file_name;
66     return loc;
67   }
68 #endif
69 };
70 
71 namespace errors {
72 
73 typedef ::tensorflow::error::Code Code;
74 
75 }  // namespace errors
76 /// @ingroup core
77 /// Denotes success or failure of a call in Tensorflow.
78 class Status {
79  public:
80   /// Create a success status.
Status()81   Status() {}
82 
83   /// \brief Create a status with the specified error code and msg as a
84   /// human-readable string containing more detailed information.
85   Status(tensorflow::error::Code code, absl::string_view msg,
86          SourceLocation loc = SourceLocation::current());
87 
88   /// Copy the specified status.
89   Status(const Status& s);
90   Status& operator=(const Status& s);
91 #ifndef SWIG
92   Status(Status&& s, SourceLocation loc = SourceLocation::current()) noexcept;
93   Status& operator=(Status&& s) noexcept;
94 #endif  // SWIG
95 
96   // Prefer using OkStatus().
OK()97   static Status OK() { return Status(); }
98 
99   /// Returns true iff the status indicates success.
ok()100   bool ok() const { return (state_ == nullptr); }
101 
code()102   tensorflow::error::Code code() const {
103     return ok() ? tensorflow::error::OK : state_->code;
104   }
105 
error_message()106   const std::string& error_message() const {
107     return ok() ? empty_string() : state_->msg;
108   }
109 
110   bool operator==(const Status& x) const;
111   bool operator!=(const Status& x) const;
112 
113   /// \brief If `ok()`, stores `new_status` into `*this`.  If `!ok()`,
114   /// preserves the current status, but may augment with additional
115   /// information about `new_status`.
116   ///
117   /// Convenient way of keeping track of the first error encountered.
118   /// Instead of:
119   ///   `if (overall_status.ok()) overall_status = new_status`
120   /// Use:
121   ///   `overall_status.Update(new_status);`
122   void Update(const Status& new_status);
123 
124   /// \brief Return a string representation of this status suitable for
125   /// printing. Returns the string `"OK"` for success.
126   ///
127   /// By default, it returns combination of the error code name, the message and
128   /// any associated payload messages. This string is designed simply to be
129   /// human readable and its exact format should not be load bearing. Do not
130   /// depend on the exact format of the result of `ToString()` which is subject
131   /// to change.
132   std::string ToString() const;
133 
134   // Ignores any errors. This method does nothing except potentially suppress
135   // complaints from any tools that are checking that errors are not dropped on
136   // the floor.
137   void IgnoreError() const;
138 
139   //----------------------------------------------------------------------------
140   // Payload Management APIs (Cloned from absl::Status)
141   //----------------------------------------------------------------------------
142   // A payload may be attached to a status to provide additional context to an
143   // error that may not be satisfied by an existing `tensorflow::error::Code`.
144   // Typically, this payload serves one of several purposes:
145   //
146   //   * It may provide more fine-grained semantic information about the error
147   //     to facilitate actionable remedies.
148   //   * It may provide human-readable contexual information that is more
149   //     appropriate to display to an end user.
150   //
151   // A payload consists of a [key,value] pair, where the key is a string
152   // referring to a unique "type URL" and the value is an object of type
153   // `absl::Cord` to hold the contextual data.
154   //
155   // The "type URL" should be unique and follow the format of a URL
156   // (https://en.wikipedia.org/wiki/URL) and, ideally, provide some
157   // documentation or schema on how to interpret its associated data. For
158   // example, the default type URL for a protobuf message type is
159   // "type.googleapis.com/packagename.messagename". Other custom wire formats
160   // should define the format of type URL in a similar practice so as to
161   // minimize the chance of conflict between type URLs.
162   // Users should ensure that the type URL can be mapped to a concrete
163   // C++ type if they want to deserialize the payload and read it effectively.
164   //
165   // To attach a payload to a status object, call `Status::SetPayload()`,
166   // passing it the type URL and an `absl::Cord` of associated data. Similarly,
167   // to extract the payload from a status, call `Status::GetPayload()`. You
168   // may attach multiple payloads (with differing type URLs) to any given
169   // status object, provided that the status is currently exhibiting an error
170   // code (i.e. is not OK).
171   // TODO(b/197552541): Use absl::Cord for payload value type.
172 
173   // The Payload-related APIs are cloned from absl::Status.
174   //
175   // Returns the payload of a status given its unique `type_url` key, if
176   // present.
177   absl::optional<absl::Cord> GetPayload(absl::string_view type_url) const;
178 
179   // Sets the payload for a non-ok status using a `type_url` key, overwriting
180   // any existing payload for that `type_url`.
181   //
182   // This function does nothing if the Status is ok.
183   void SetPayload(absl::string_view type_url, absl::string_view payload);
184 
185   // Erases the payload corresponding to the `type_url` key.  Returns `true` if
186   // the payload was present.
187   bool ErasePayload(absl::string_view type_url);
188 
189   // Iterates over the stored payloads and calls the
190   // `visitor(type_key, payload)` callable for each one.
191   //
192   // The order of calls to `visitor()` is not specified and may change at
193   // any time and any mutation on the same Status object during visitation is
194   // forbidden and could result in undefined behavior.
195   void ForEachPayload(
196       const std::function<void(absl::string_view, absl::string_view)>& visitor)
197       const;
198 
199   void SetStackTrace(std::vector<StackFrame>);
200   std::vector<StackFrame> GetStackTrace() const;
201 
202   absl::Span<const SourceLocation> GetSourceLocations() const;
203 
204  private:
205   void MaybeAddSourceLocation(SourceLocation loc);
206 
207   static const std::string& empty_string();
208   std::vector<StackFrame> stack_trace_;
209   struct State {
210     tensorflow::error::Code code;
211     std::string msg;
212     std::unordered_map<std::string, std::string> payloads;
213     absl::InlinedVector<SourceLocation, 4> source_locations;
214   };
215 
216   // OK status has a `NULL` state_.  Otherwise, `state_` points to
217   // a `State` structure containing the error code and message(s)
218   std::unique_ptr<State> state_;
219 
220   void SlowCopyFrom(const State* src);
221 };
222 
223 // OkStatus()
224 //
225 // Returns an OK status, equivalent to a default constructed instance. Prefer
226 // usage of `OkStatus()` when constructing such an OK status.
227 Status OkStatus();
228 
229 Status FromAbslStatus(const absl::Status& s);
230 absl::Status ToAbslStatus(const ::tensorflow::Status& s);
231 
232 // TODO(b/197552541) Move this namespace to errors.h.
233 namespace errors {
234 
235 void SetStackTrace(::tensorflow::Status& status,
236                    std::vector<StackFrame> stack_trace);
237 
238 std::vector<StackFrame> GetStackTrace(const ::tensorflow::Status& status);
239 }  // namespace errors
240 
241 // Helper class to manage multiple child status values.
242 class StatusGroup {
243  public:
244   StatusGroup();
245   // Constructor to form a StatusGroup from any N set of Status arguments.
246   // Usage: StatusGroup({status_a, status_b, status_c});
247   StatusGroup(std::initializer_list<Status> statuses);
248 
249   // Utility function to mark a Status as derived. By marking derived status,
250   // Derived status messages are ignored when reporting errors to end users.
251   static Status MakeDerived(const Status& s);
252   static bool IsDerived(const Status& s);
253 
254   // Enable warning and error log collection for appending to the aggregated
255   // status. This function may be called more than once.
256   static void ConfigureLogHistory();
257 
258   // Returns merged payloads of all statuses. In case multiple statuses have the
259   // same payload key, non-derived statuses have priority over derived ones,
260   // otherwise one payload value will be chosen in an unspecified but
261   // deterministic order.
262   // NOTE: The payload marking derived statuses as derived will not be returned.
263   std::unordered_map<std::string, std::string> GetPayloads() const;
264 
265   // Return a merged status with combined child status messages with a summary.
266   Status as_summary_status() const;
267   // Return a merged status with combined child status messages with
268   // concatenation.
269   Status as_concatenated_status() const;
270 
ok()271   bool ok() const { return ok_; }
272 
273   // Augment this group with the child status `status`.
274   void Update(const Status& status);
275 
276   // Attach recent warning and error log messages
277   void AttachLogMessages();
HasLogMessages()278   bool HasLogMessages() const { return !recent_logs_.empty(); }
279 
280  private:
281   bool ok_ = true;
282   size_t num_ok_ = 0;
283 
284   // Maintain a sorted collection of statuses.
285   struct CompareStatus {
operatorCompareStatus286     bool operator()(const Status& a, const Status& b) const {
287       return a.ToString() > b.ToString();
288     }
289   };
290   // Using std::set instead of absl::btree_set to keep size for certain
291   // dependent libraries under the limit.
292   std::set<Status, CompareStatus> derived_;
293   std::set<Status, CompareStatus> non_derived_;
294 
295   std::vector<std::string> recent_logs_;  // recent warning and error logs
296 };
297 
Status(const Status & s)298 inline Status::Status(const Status& s)
299     : state_((s.state_ == nullptr) ? nullptr : new State(*s.state_)) {}
300 
301 inline Status& Status::operator=(const Status& s) {
302   // The following condition catches both aliasing (when this == &s),
303   // and the common case where both s and *this are ok.
304   if (state_ != s.state_) {
305     SlowCopyFrom(s.state_.get());
306   }
307   return *this;
308 }
309 
310 #ifndef SWIG
Status(Status && s,SourceLocation loc)311 inline Status::Status(Status&& s, SourceLocation loc) noexcept
312     : state_(std::move(s.state_)) {
313   MaybeAddSourceLocation(loc);
314 }
315 
316 inline Status& Status::operator=(Status&& s) noexcept {
317   if (state_ != s.state_) {
318     state_ = std::move(s.state_);
319   }
320   return *this;
321 }
322 #endif  // SWIG
323 
324 inline bool Status::operator==(const Status& x) const {
325   return (this->state_ == x.state_) || (ToString() == x.ToString());
326 }
327 
328 inline bool Status::operator!=(const Status& x) const { return !(*this == x); }
329 
330 /// @ingroup core
331 std::ostream& operator<<(std::ostream& os, const Status& x);
332 
333 typedef std::function<void(const Status&)> StatusCallback;
334 
335 extern tensorflow::string* TfCheckOpHelperOutOfLine(
336     const ::tensorflow::Status& v, const char* msg);
337 
338 std::string error_name(error::Code code);
339 
TfCheckOpHelper(::tensorflow::Status v,const char * msg)340 inline tensorflow::string* TfCheckOpHelper(::tensorflow::Status v,
341                                            const char* msg) {
342   if (v.ok()) return nullptr;
343   return TfCheckOpHelperOutOfLine(v, msg);
344 }
345 
346 #define TF_DO_CHECK_OK(val, level)                                \
347   while (auto _result = ::tensorflow::TfCheckOpHelper(val, #val)) \
348   LOG(level) << *(_result)
349 
350 #define TF_CHECK_OK(val) TF_DO_CHECK_OK(val, FATAL)
351 #define TF_QCHECK_OK(val) TF_DO_CHECK_OK(val, QFATAL)
352 
353 // DEBUG only version of TF_CHECK_OK.  Compiler still parses 'val' even in opt
354 // mode.
355 #ifndef NDEBUG
356 #define TF_DCHECK_OK(val) TF_CHECK_OK(val)
357 #else
358 #define TF_DCHECK_OK(val) \
359   while (false && (::tensorflow::OkStatus() == (val))) LOG(FATAL)
360 #endif
361 
362 }  // namespace tensorflow
363 
364 #endif  // TENSORFLOW_CORE_PLATFORM_STATUS_H_
365