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 #include "tensorflow/compiler/xla/status_macros.h"
17 
18 #include <algorithm>
19 
20 #include "tensorflow/compiler/xla/types.h"
21 #include "tensorflow/core/lib/strings/str_util.h"
22 #include "tensorflow/core/lib/strings/strcat.h"
23 #include "tensorflow/core/platform/logging.h"
24 #include "tensorflow/core/platform/stacktrace.h"
25 
26 namespace xla {
27 namespace status_macros {
28 
MakeStatus(tensorflow::error::Code code,const string & message)29 static Status MakeStatus(tensorflow::error::Code code, const string& message) {
30   return Status(code, message);
31 }
32 
33 // Log the error at the given severity, optionally with a stack trace.
34 // If log_severity is NUM_SEVERITIES, nothing is logged.
LogError(const Status & status,const char * filename,int line,int log_severity,bool should_log_stack_trace)35 static void LogError(const Status& status, const char* filename, int line,
36                      int log_severity, bool should_log_stack_trace) {
37   if (TF_PREDICT_TRUE(log_severity != tensorflow::NUM_SEVERITIES)) {
38     string stack_trace;
39     if (should_log_stack_trace) {
40       stack_trace =
41           tensorflow::strings::StrCat("\n", tensorflow::CurrentStackTrace());
42     }
43     switch (log_severity) {
44       case tensorflow::INFO:
45         LOG(INFO) << status << stack_trace;
46         break;
47       case tensorflow::WARNING:
48         LOG(WARNING) << status << stack_trace;
49         break;
50       case tensorflow::ERROR:
51         LOG(ERROR) << status << stack_trace;
52         break;
53       case tensorflow::FATAL:
54         LOG(FATAL) << status << stack_trace;
55         break;
56       case tensorflow::NUM_SEVERITIES:
57         break;
58       default:
59         LOG(FATAL) << "Unknown LOG severity " << log_severity;
60     }
61   }
62 }
63 
64 // Make a Status with a code, error message and payload,
65 // and also send it to LOG(<log_severity>) using the given filename
66 // and line (unless should_log is false, or log_severity is
67 // NUM_SEVERITIES).  If should_log_stack_trace is true, the stack
68 // trace is included in the log message (ignored if should_log is
69 // false).
MakeError(const char * filename,int line,tensorflow::error::Code code,const string & message,bool should_log,int log_severity,bool should_log_stack_trace)70 static Status MakeError(const char* filename, int line,
71                         tensorflow::error::Code code, const string& message,
72                         bool should_log, int log_severity,
73                         bool should_log_stack_trace) {
74   if (TF_PREDICT_FALSE(code == tensorflow::error::OK)) {
75     LOG(ERROR) << "Cannot create error with status OK";
76     code = tensorflow::error::UNKNOWN;
77   }
78   const Status status = MakeStatus(code, message);
79   if (TF_PREDICT_TRUE(should_log)) {
80     LogError(status, filename, line, log_severity, should_log_stack_trace);
81   }
82   return status;
83 }
84 
85 // This method is written out-of-line rather than in the header to avoid
86 // generating a lot of inline code for error cases in all callers.
CheckNotDone() const87 void MakeErrorStream::CheckNotDone() const { impl_->CheckNotDone(); }
88 
Impl(const char * file,int line,tensorflow::error::Code code,MakeErrorStream * error_stream,bool is_logged_by_default)89 MakeErrorStream::Impl::Impl(const char* file, int line,
90                             tensorflow::error::Code code,
91                             MakeErrorStream* error_stream,
92                             bool is_logged_by_default)
93     : file_(file),
94       line_(line),
95       code_(code),
96       is_done_(false),
97       should_log_(is_logged_by_default),
98       log_severity_(tensorflow::ERROR),
99       should_log_stack_trace_(false),
100       make_error_stream_with_output_wrapper_(error_stream) {}
101 
Impl(const Status & status,PriorMessageHandling prior_message_handling,const char * file,int line,MakeErrorStream * error_stream)102 MakeErrorStream::Impl::Impl(const Status& status,
103                             PriorMessageHandling prior_message_handling,
104                             const char* file, int line,
105                             MakeErrorStream* error_stream)
106     : file_(file),
107       line_(line),
108       // Make sure we show some error, even if the call is incorrect.
109       code_(!status.ok() ? status.code() : tensorflow::error::UNKNOWN),
110       prior_message_handling_(prior_message_handling),
111       prior_message_(status.error_message()),
112       is_done_(false),
113       // Error code type is not visible here, so we can't call
114       // IsLoggedByDefault.
115       should_log_(true),
116       log_severity_(tensorflow::ERROR),
117       should_log_stack_trace_(false),
118       make_error_stream_with_output_wrapper_(error_stream) {
119   DCHECK(!status.ok()) << "Attempted to append/prepend error text to status OK";
120 }
121 
~Impl()122 MakeErrorStream::Impl::~Impl() {
123   // Note: error messages refer to the public MakeErrorStream class.
124 
125   if (!is_done_) {
126     LOG(ERROR) << "MakeErrorStream destructed without getting Status: " << file_
127                << ":" << line_ << " " << stream_.str();
128   }
129 }
130 
GetStatus()131 Status MakeErrorStream::Impl::GetStatus() {
132   // Note: error messages refer to the public MakeErrorStream class.
133 
134   // Getting a Status object out more than once is not harmful, but
135   // it doesn't match the expected pattern, where the stream is constructed
136   // as a temporary, loaded with a message, and then casted to Status.
137   if (is_done_) {
138     LOG(ERROR) << "MakeErrorStream got Status more than once: " << file_ << ":"
139                << line_ << " " << stream_.str();
140   }
141 
142   is_done_ = true;
143 
144   const string& stream_str = stream_.str();
145   const string str =
146       prior_message_handling_ == kAppendToPriorMessage
147           ? tensorflow::strings::StrCat(prior_message_, stream_str)
148           : tensorflow::strings::StrCat(stream_str, prior_message_);
149   if (TF_PREDICT_FALSE(str.empty())) {
150     return MakeError(file_, line_, code_,
151                      tensorflow::strings::StrCat(
152                          str, "Error without message at ", file_, ":", line_),
153                      true /* should_log */,
154                      tensorflow::ERROR /* log_severity */,
155                      should_log_stack_trace_);
156   } else {
157     return MakeError(file_, line_, code_, str, should_log_, log_severity_,
158                      should_log_stack_trace_);
159   }
160 }
161 
CheckNotDone() const162 void MakeErrorStream::Impl::CheckNotDone() const {
163   if (is_done_) {
164     LOG(ERROR) << "MakeErrorStream shift called after getting Status: " << file_
165                << ":" << line_ << " " << stream_.str();
166   }
167 }
168 
169 }  // namespace status_macros
170 }  // namespace xla
171