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