Skip to content

Commit 16ab2ff

Browse files
Neural-Link Teamtensorflow-copybara
authored andcommitted
Add a RET_CHECK for absl::Status.
PiperOrigin-RevId: 366374701
1 parent 6821466 commit 16ab2ff

File tree

6 files changed

+387
-17
lines changed

6 files changed

+387
-17
lines changed

research/carls/base/BUILD

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ cc_library(
8888
"@com_github_grpc_grpc//:grpc++",
8989
"@com_github_grpc_grpc//:grpc++_codegen_base",
9090
"@com_google_absl//absl/status",
91+
"@com_google_absl//absl/strings",
9192
"@tensorflow_includes//:includes",
9293
"@tensorflow_solib//:framework_lib",
9394
],
@@ -98,6 +99,8 @@ cc_test(
9899
srcs = ["status_helper_test.cc"],
99100
deps = [
100101
":status_helper",
102+
"//research/carls/testing:test_helper",
103+
"@com_google_absl//absl/strings",
101104
"@com_google_googletest//:gtest",
102105
"@com_google_googletest//:gtest_main",
103106
],

research/carls/base/status_helper.cc

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,197 @@ limitations under the License.
1515

1616
#include "research/carls/base/status_helper.h"
1717

18+
#include "absl/status/status.h"
19+
#include "absl/strings/str_cat.h"
20+
#include "tensorflow/core/platform/abi.h"
21+
22+
#if defined(TF_HAS_STACKTRACE)
23+
#include <dlfcn.h>
24+
#include <execinfo.h>
25+
#include <stdio.h>
26+
#include <string.h>
27+
#include <unistd.h>
28+
#endif
29+
1830
namespace carls {
31+
namespace internal {
32+
namespace {
33+
34+
static absl::Status MakeStatus(absl::StatusCode code,
35+
const std::string& message) {
36+
return absl::Status(code, message);
37+
}
38+
39+
// Function to create a pretty stacktrace.
40+
inline std::string CurrentStackTrace() {
41+
#if defined(TF_HAS_STACKTRACE)
42+
std::stringstream ss("");
43+
ss << "*** Begin stack trace ***" << std::endl;
44+
45+
// Get the mangled stack trace.
46+
int buffer_size = 128;
47+
void* trace[128];
48+
buffer_size = backtrace(trace, buffer_size);
49+
50+
for (int i = 0; i < buffer_size; ++i) {
51+
const char* symbol = "";
52+
Dl_info info;
53+
if (dladdr(trace[i], &info)) {
54+
if (info.dli_sname != nullptr) {
55+
symbol = info.dli_sname;
56+
}
57+
}
58+
59+
std::string demangled = tensorflow::port::MaybeAbiDemangle(symbol);
60+
if (demangled.length()) {
61+
ss << "\t" << demangled << std::endl;
62+
} else {
63+
ss << "\t" << symbol << std::endl;
64+
}
65+
}
66+
67+
ss << "*** End stack trace ***" << std::endl;
68+
return ss.str();
69+
#else
70+
return std::string();
71+
#endif // defined(TF_HAS_STACKTRACE)
72+
}
73+
74+
// Log the error at the given severity, optionally with a stack trace.
75+
// If log_severity is NUM_SEVERITIES, nothing is logged.
76+
static void LogError(const absl::Status& status, const char* filename, int line,
77+
int log_severity, bool should_log_stack_trace) {
78+
if (TF_PREDICT_TRUE(log_severity != tensorflow::NUM_SEVERITIES)) {
79+
std::string stack_trace;
80+
if (should_log_stack_trace) {
81+
stack_trace = absl::StrCat("\n", CurrentStackTrace());
82+
}
83+
switch (log_severity) {
84+
case tensorflow::INFO:
85+
LOG(INFO) << status << stack_trace;
86+
break;
87+
case tensorflow::WARNING:
88+
LOG(WARNING) << status << stack_trace;
89+
break;
90+
case tensorflow::ERROR:
91+
LOG(ERROR) << status << stack_trace;
92+
break;
93+
case tensorflow::FATAL:
94+
LOG(FATAL) << status << stack_trace;
95+
break;
96+
case tensorflow::NUM_SEVERITIES:
97+
break;
98+
default:
99+
LOG(FATAL) << "Unknown LOG severity " << log_severity;
100+
}
101+
}
102+
}
103+
104+
// Make a Status with a code, error message and payload,
105+
// and also send it to LOG(<log_severity>) using the given filename
106+
// and line (unless should_log is false, or log_severity is
107+
// NUM_SEVERITIES). If should_log_stack_trace is true, the stack
108+
// trace is included in the log message (ignored if should_log is
109+
// false).
110+
static absl::Status MakeError(const char* filename, int line,
111+
absl::StatusCode code, const std::string& message,
112+
bool should_log, int log_severity,
113+
bool should_log_stack_trace) {
114+
if (TF_PREDICT_FALSE(code == absl::StatusCode::kOk)) {
115+
LOG(ERROR) << "Cannot create error with status OK";
116+
code = absl::StatusCode::kUnknown;
117+
}
118+
const absl::Status status = MakeStatus(code, message);
119+
if (TF_PREDICT_TRUE(should_log)) {
120+
LogError(status, filename, line, log_severity, should_log_stack_trace);
121+
}
122+
return status;
123+
}
124+
125+
} // namespace
126+
127+
// This method is written out-of-line rather than in the header to avoid
128+
// generating a lot of inline code for error cases in all callers.
129+
void MakeErrorStream::CheckNotDone() const { impl_->CheckNotDone(); }
130+
131+
MakeErrorStream::Impl::Impl(const char* file, int line, absl::StatusCode code,
132+
MakeErrorStream* error_stream,
133+
bool is_logged_by_default)
134+
: file_(file),
135+
line_(line),
136+
code_(code),
137+
is_done_(false),
138+
should_log_(is_logged_by_default),
139+
log_severity_(tensorflow::ERROR),
140+
should_log_stack_trace_(false),
141+
make_error_stream_with_output_wrapper_(error_stream) {}
142+
143+
MakeErrorStream::Impl::Impl(const absl::Status& status,
144+
PriorMessageHandling prior_message_handling,
145+
const char* file, int line,
146+
MakeErrorStream* error_stream)
147+
: file_(file),
148+
line_(line),
149+
// Make sure we show some error, even if the call is incorrect.
150+
code_(!status.ok() ? status.code() : absl::StatusCode::kUnknown),
151+
prior_message_handling_(prior_message_handling),
152+
prior_message_(status.message()),
153+
is_done_(false),
154+
// Error code type is not visible here, so we can't call
155+
// IsLoggedByDefault.
156+
should_log_(true),
157+
log_severity_(tensorflow::ERROR),
158+
should_log_stack_trace_(false),
159+
make_error_stream_with_output_wrapper_(error_stream) {
160+
DCHECK(!status.ok()) << "Attempted to append/prepend error text to status OK";
161+
}
162+
163+
MakeErrorStream::Impl::~Impl() {
164+
// Note: error messages refer to the public MakeErrorStream class.
165+
166+
if (!is_done_) {
167+
LOG(ERROR) << "MakeErrorStream destructed without getting Status: " << file_
168+
<< ":" << line_ << " " << stream_.str();
169+
}
170+
}
171+
172+
absl::Status MakeErrorStream::Impl::GetStatus() {
173+
// Note: error messages refer to the public MakeErrorStream class.
174+
175+
// Getting a Status object out more than once is not harmful, but
176+
// it doesn't match the expected pattern, where the stream is constructed
177+
// as a temporary, loaded with a message, and then casted to Status.
178+
if (is_done_) {
179+
LOG(ERROR) << "MakeErrorStream got Status more than once: " << file_ << ":"
180+
<< line_ << " " << stream_.str();
181+
}
182+
183+
is_done_ = true;
184+
185+
const std::string& stream_str = stream_.str();
186+
const std::string str = prior_message_handling_ == kAppendToPriorMessage
187+
? absl::StrCat(prior_message_, stream_str)
188+
: absl::StrCat(stream_str, prior_message_);
189+
if (TF_PREDICT_FALSE(str.empty())) {
190+
return MakeError(
191+
file_, line_, code_,
192+
absl::StrCat(str, "Error without message at ", file_, ":", line_),
193+
true /* should_log */, tensorflow::ERROR /* log_severity */,
194+
should_log_stack_trace_);
195+
} else {
196+
return MakeError(file_, line_, code_, str, should_log_, log_severity_,
197+
should_log_stack_trace_);
198+
}
199+
}
200+
201+
void MakeErrorStream::Impl::CheckNotDone() const {
202+
if (is_done_) {
203+
LOG(ERROR) << "MakeErrorStream shift called after getting Status: " << file_
204+
<< ":" << line_ << " " << stream_.str();
205+
}
206+
}
207+
208+
} // namespace internal
19209

20210
absl::Status ToAbslStatus(const grpc::Status& status) {
21211
return absl::Status(static_cast<absl::StatusCode>(status.error_code()),

research/carls/base/status_helper.h

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,150 @@ limitations under the License.
2121
#include "tensorflow/core/platform/status.h"
2222

2323
namespace carls {
24+
namespace internal {
25+
26+
// Modified from tensorflow/compiler/xla/status_macros.h.
27+
// Stream object used to collect error messages in MAKE_ERROR macros
28+
// or append error messages with APPEND_ERROR. It accepts any
29+
// arguments with operator<< to build an error string, and then has an
30+
// implicit cast operator to Status, which converts the
31+
// logged string to a Status object and returns it, after logging the
32+
// error. At least one call to operator<< is required; a compile time
33+
// error will be generated if none are given. Errors will only be
34+
// logged by default for certain status codes, as defined in
35+
// IsLoggedByDefault. This class will give ERROR errors if you don't
36+
// retrieve a Status exactly once before destruction.
37+
//
38+
// The class converts into an intermediate wrapper object
39+
// MakeErrorStreamWithOutput to check that the error stream gets at least one
40+
// item of input.
41+
class MakeErrorStream {
42+
public:
43+
// Wrapper around MakeErrorStream that only allows for output. This
44+
// is created as output of the first operator<< call on
45+
// MakeErrorStream. The bare MakeErrorStream does not have a
46+
// Status operator. The net effect of that is that you
47+
// have to call operator<< at least once or else you'll get a
48+
// compile time error.
49+
class MakeErrorStreamWithOutput {
50+
public:
51+
explicit MakeErrorStreamWithOutput(MakeErrorStream* error_stream)
52+
: wrapped_error_stream_(error_stream) {}
53+
54+
template <typename T>
55+
MakeErrorStreamWithOutput& operator<<(const T& value) {
56+
*wrapped_error_stream_ << value;
57+
return *this;
58+
}
59+
60+
// Implicit cast operators to Status.
61+
// Exactly one of these must be called exactly once before destruction.
62+
operator absl::Status() { return wrapped_error_stream_->GetStatus(); }
63+
64+
private:
65+
MakeErrorStream* wrapped_error_stream_;
66+
67+
TF_DISALLOW_COPY_AND_ASSIGN(MakeErrorStreamWithOutput);
68+
};
69+
70+
// When starting from an existing error status, this determines whether we'll
71+
// append or prepend to that status's error message.
72+
enum PriorMessageHandling { kAppendToPriorMessage, kPrependToPriorMessage };
73+
74+
// Make an error with the given code.
75+
MakeErrorStream(const char* file, int line, absl::StatusCode code)
76+
: impl_(new Impl(file, line, code, this, true)) {}
77+
78+
template <typename T>
79+
MakeErrorStreamWithOutput& operator<<(const T& value) {
80+
CheckNotDone();
81+
impl_->stream_ << value;
82+
return impl_->make_error_stream_with_output_wrapper_;
83+
}
84+
85+
// When this message is logged (see with_logging()), include the stack trace.
86+
MakeErrorStream& with_log_stack_trace() {
87+
impl_->should_log_stack_trace_ = true;
88+
return *this;
89+
}
90+
91+
// Adds RET_CHECK failure text to error message.
92+
MakeErrorStreamWithOutput& add_ret_check_failure(const char* condition) {
93+
return *this << "RET_CHECK failure (" << impl_->file_ << ":" << impl_->line_
94+
<< ") " << condition << " ";
95+
}
96+
97+
private:
98+
class Impl {
99+
public:
100+
Impl(const char* file, int line, absl::StatusCode code,
101+
MakeErrorStream* error_stream, bool is_logged_by_default = true);
102+
Impl(const absl::Status& status,
103+
PriorMessageHandling prior_message_handling, const char* file,
104+
int line, MakeErrorStream* error_stream);
105+
106+
~Impl();
107+
108+
// This must be called exactly once before destruction.
109+
absl::Status GetStatus();
110+
111+
void CheckNotDone() const;
112+
113+
private:
114+
const char* file_;
115+
int line_;
116+
absl::StatusCode code_;
117+
118+
PriorMessageHandling prior_message_handling_ = kAppendToPriorMessage;
119+
std::string prior_message_;
120+
bool is_done_; // true after Status object has been returned
121+
std::ostringstream stream_;
122+
bool should_log_;
123+
int log_severity_;
124+
bool should_log_stack_trace_;
125+
126+
// Wrapper around the MakeErrorStream object that has a
127+
// Status conversion. The first << operator called on
128+
// MakeErrorStream will return this object, and only this object
129+
// can implicitly convert to Status. The net effect of
130+
// this is that you'll get a compile time error if you call
131+
// MAKE_ERROR etc. without adding any output.
132+
MakeErrorStreamWithOutput make_error_stream_with_output_wrapper_;
133+
134+
friend class MakeErrorStream;
135+
TF_DISALLOW_COPY_AND_ASSIGN(Impl);
136+
};
137+
138+
void CheckNotDone() const;
139+
140+
// Returns the status. Used by MakeErrorStreamWithOutput.
141+
absl::Status GetStatus() const { return impl_->GetStatus(); }
142+
143+
// Store the actual data on the heap to reduce stack frame sizes.
144+
std::unique_ptr<Impl> impl_;
145+
146+
TF_DISALLOW_COPY_AND_ASSIGN(MakeErrorStream);
147+
};
148+
149+
// Provides a conversion to bool so that it can be used inside an if statement
150+
// that declares a variable.
151+
class StatusAdaptorForMacros {
152+
public:
153+
explicit StatusAdaptorForMacros(absl::Status status)
154+
: status_(std::move(status)) {}
155+
156+
StatusAdaptorForMacros(const StatusAdaptorForMacros&) = delete;
157+
StatusAdaptorForMacros& operator=(const StatusAdaptorForMacros&) = delete;
158+
159+
explicit operator bool() const { return TF_PREDICT_TRUE(status_.ok()); }
160+
161+
absl::Status&& Consume() { return std::move(status_); }
162+
163+
private:
164+
absl::Status status_;
165+
};
166+
167+
} // namespace internal
24168

25169
// Converts from grpc::Status to absl::Status.
26170
absl::Status ToAbslStatus(const grpc::Status& status);
@@ -31,6 +175,16 @@ absl::Status ToAbslStatus(const tensorflow::Status& status);
31175
// Converts from absl::Status to grpc::Status.
32176
grpc::Status ToGrpcStatus(const absl::Status& status);
33177

178+
// Checks if given condition returns Ok status, if not, returns an error status
179+
// and stack trace.
180+
#undef RET_CHECK
181+
#define RET_CHECK(condition) \
182+
while (TF_PREDICT_FALSE(!(condition.ok()))) \
183+
return carls::internal::MakeErrorStream(__FILE__, __LINE__, \
184+
absl::StatusCode::kInternal) \
185+
.with_log_stack_trace() \
186+
.add_ret_check_failure(#condition)
187+
34188
} // namespace carls
35189

36190
#endif // NEURAL_STRUCTURED_LEARNING_RESEARCH_CARLS_BASE_STATUS_HELPER_H_

0 commit comments

Comments
 (0)