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_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 | |
38 | namespace tsl { |
39 | |
40 | #if TF_HAS_CPP_ATTRIBUTE(nodiscard) |
41 | class [[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 | |
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 | typedef ::tensorflow::error::Code Code; |
73 | } // namespace errors |
74 | namespace error { |
75 | typedef ::tensorflow::error::Code Code; |
76 | } // namespace error |
77 | |
78 | /// @ingroup core |
79 | /// Denotes success or failure of a call in Tensorflow. |
80 | class 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. |
247 | Status OkStatus(); |
248 | |
249 | Status FromAbslStatus(const absl::Status& s); |
250 | absl::Status ToAbslStatus(const ::tsl::Status& s); |
251 | |
252 | // TODO(b/197552541) Move this namespace to errors.h. |
253 | namespace errors { |
254 | |
255 | void SetStackTrace(::tsl::Status& status, std::vector<StackFrame> stack_trace); |
256 | |
257 | std::vector<StackFrame> GetStackTrace(const ::tsl::Status& status); |
258 | } // namespace errors |
259 | |
260 | // Helper class to manage multiple child status values. |
261 | class 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 | |
317 | inline Status::Status(const Status& s) |
318 | : state_((s.state_ == nullptr) ? nullptr : NewStateFromNonOKStatus(s)) {} |
319 | |
320 | inline 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 |
330 | inline Status::Status(Status&& s, SourceLocation loc) noexcept |
331 | : state_(std::move(s.state_)) { |
332 | MaybeAddSourceLocation(loc); |
333 | } |
334 | |
335 | inline 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 | |
343 | inline bool Status::operator==(const Status& x) const { |
344 | return (this->state_ == x.state_) || (ToString() == x.ToString()); |
345 | } |
346 | |
347 | inline bool Status::operator!=(const Status& x) const { return !(*this == x); } |
348 | |
349 | /// @ingroup core |
350 | std::ostream& operator<<(std::ostream& os, const Status& x); |
351 | |
352 | typedef std::function<void(const Status&)> StatusCallback; |
353 | |
354 | extern tsl::string* TfCheckOpHelperOutOfLine(const ::tsl::Status& v, |
355 | const char* msg); |
356 | |
357 | std::string error_name(error::Code code); |
358 | |
359 | inline 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 | |