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