1 /*
2 * Copyright 2017 Google LLC
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #ifndef FCP_BASE_MONITORING_H_
17 #define FCP_BASE_MONITORING_H_
18
19 #include <string>
20 #include <utility>
21
22 #include "fcp/base/new.h"
23
24 #ifdef FCP_BAREMETAL
25 #include "fcp/base/string_stream.h"
26 #else
27 #include <cstdlib>
28 #include <iostream>
29 #include <ostream>
30 #include <sstream>
31
32 #include "absl/base/attributes.h"
33 #include "absl/base/log_severity.h"
34 #include "absl/base/optimization.h"
35 #include "absl/status/status.h"
36 #include "absl/status/statusor.h"
37 #endif // FCP_BAREMETAL
38
39 namespace fcp {
40
41 // General Definitions
42 // ===================
43
44 /**
45 * Indicates to run the functionality in this file in debugging mode. This
46 * creates more exhaustive diagnostics in some cases, as documented case by
47 * case.
48 *
49 * We may want to make this a flag if this turns out to be often needed.
50 */
51 constexpr bool fcp_debug = false;
52
53 // Logging and Assertions
54 // ======================
55
56 /**
57 * Defines a subset of Google style logging. Use FCP_LOG(INFO),
58 * FCP_LOG(WARNING), FCP_LOG(ERROR) or FCP_LOG(FATAL) to stream log messages.
59 * Example:
60 *
61 * FCP_LOG(INFO) << "some info log";
62 */
63 // TODO(team): adapt to absl logging once available
64 #define FCP_LOG(severity) _FCP_LOG_##severity
65
66 #define FCP_LOG_IF(severity, condition) \
67 !(condition) ? (void)0 \
68 : ::fcp::internal::LogMessageVoidify() & _FCP_LOG_##severity
69
70 /**
71 * Log Severity
72 */
73 #ifdef FCP_BAREMETAL
74 enum class LogSeverity : int {
75 kInfo = 0,
76 kWarning = 1,
77 kError = 2,
78 kFatal = 3,
79 };
80 #else
81 using LogSeverity = absl::LogSeverity;
82 #endif // FCP_BAREMETAL
83
84 #define _FCP_LOG_INFO \
85 ::fcp::internal::LogMessage(__FILE__, __LINE__, ::fcp::LogSeverity::kInfo)
86 #define _FCP_LOG_WARNING \
87 ::fcp::internal::LogMessage(__FILE__, __LINE__, ::fcp::LogSeverity::kWarning)
88 #define _FCP_LOG_ERROR \
89 ::fcp::internal::LogMessage(__FILE__, __LINE__, ::fcp::LogSeverity::kError)
90 #define _FCP_LOG_FATAL \
91 ::fcp::internal::LogMessage(__FILE__, __LINE__, ::fcp::LogSeverity::kFatal)
92
93 #ifdef FCP_BAREMETAL
94 #define FCP_PREDICT_FALSE(x) (x)
95 #define FCP_PREDICT_TRUE(x) (x)
96 #else
97 #define FCP_PREDICT_FALSE(x) ABSL_PREDICT_FALSE(x)
98 #define FCP_PREDICT_TRUE(x) ABSL_PREDICT_TRUE(x)
99 #endif
100
101 /**
102 * Check that the condition holds, otherwise die. Any additional messages can
103 * be streamed into the invocation. Example:
104 *
105 * FCP_CHECK(condition) << "stuff went wrong";
106 */
107 #define FCP_CHECK(condition) \
108 FCP_LOG_IF(FATAL, FCP_PREDICT_FALSE(!(condition))) \
109 << ("Check failed: " #condition ". ")
110
111 /**
112 * Check that the expression generating a status code is OK, otherwise die.
113 * Any additional messages can be streamed into the invocation.
114 */
115 #define FCP_CHECK_STATUS(status) \
116 for (auto __check_status = (status); \
117 __check_status.code() != ::fcp::StatusCode::kOk;) \
118 FCP_LOG_IF(FATAL, __check_status.code() != ::fcp::StatusCode::kOk) \
119 << "status not OK: " << __check_status
120
121 // Logging Implementation Details
122 // ==============================
123
124 namespace internal {
125
126 /**
127 * An object which implements a logger sink. The default sink sends log
128 * messages to stderr.
129 */
130 class Logger {
131 public:
132 virtual ~Logger() = default;
133
134 /**
135 * Basic log function.
136 *
137 * @param file The name of the associated file.
138 * @param line The line in this file.
139 * @param severity Severity of the log message.
140 * @param message The message to log.
141 */
142 virtual void Log(const char* file, int line, LogSeverity severity,
143 const char* message);
144 };
145
146 /**
147 * Gets/sets the active logger object.
148 */
149 Logger* logger();
150 void set_logger(Logger* logger);
151
152 #ifndef FCP_BAREMETAL
153 using StringStream = std::ostringstream;
154 #endif // FCP_BAREMETAL
155
156 /**
157 * Object allowing to construct a log message by streaming into it. This is
158 * used by the macro LOG(severity).
159 */
160 class LogMessage {
161 public:
LogMessage(const char * file,int line,LogSeverity severity)162 LogMessage(const char* file, int line, LogSeverity severity)
163 : file_(file), line_(line), severity_(severity) {}
164
165 template <typename T>
166 LogMessage& operator<<(const T& x) {
167 message_ << x;
168 return *this;
169 }
170
171 #ifndef FCP_BAREMETAL
172 LogMessage& operator<<(std::ostream& (*pf)(std::ostream&)) {
173 message_ << pf;
174 return *this;
175 }
176 #endif
177
~LogMessage()178 ~LogMessage() {
179 logger()->Log(file_, line_, severity_, message_.str().c_str());
180 if (severity_ == LogSeverity::kFatal) {
181 abort();
182 }
183 }
184
185 private:
186 const char* file_;
187 int line_;
188 LogSeverity severity_;
189 StringStream message_;
190 };
191
192 /**
193 * This class is used to cast a LogMessage instance to void within a ternary
194 * expression in the expansion of FCP_LOG_IF. The cast is necessary so
195 * that the types of the expressions on either side of the : match, and the &
196 * operator is used because its precedence is lower than << but higher than
197 * ?:.
198 */
199 class LogMessageVoidify {
200 public:
201 void operator&(LogMessage&) {} // NOLINT
202 };
203
204 } // namespace internal
205
206 // Status and StatusOr
207 // ===================
208
209 /**
210 * Constructor for a status. A status message can be streamed into it. This
211 * captures the current file and line position and includes it into the status
212 * message if the status code is not OK.
213 *
214 * Use as in:
215 *
216 * FCP_STATUS(OK); // signal success
217 * FCP_STATUS(code) << message; // signal failure
218 *
219 * FCP_STATUS can be used in places which either expect a Status or a
220 * StatusOr<T>.
221 *
222 * You can configure the constructed status to also emit a log entry if the
223 * status is not OK by using LogInfo, LogWarning, LogError, or LogFatal as
224 * below:
225 *
226 * FCP_STATUS(code).LogInfo() << message;
227 *
228 * If the constant rx_debug is true, by default, all FCP_STATUS invocations
229 * will be logged on INFO level.
230 */
231 #define FCP_STATUS(code) \
232 ::fcp::internal::MakeStatusBuilder(code, __FILE__, __LINE__)
233
234 #ifdef FCP_BAREMETAL
235 #define FCP_MUST_USE_RESULT __attribute__((warn_unused_result))
236 #else
237 #define FCP_MUST_USE_RESULT ABSL_MUST_USE_RESULT
238 #endif
239
240 #ifdef FCP_BAREMETAL
241
242 // The bare-metal implementation doesn't depend on Abseil library and
243 // provides its own implementations of StatusCode, Status and StatusOr that are
244 // source code compatible with absl::StatusCode, absl::Status,
245 // and absl::StatusOr.
246
247 // See absl::StatusCode for details.
248 enum StatusCode : int {
249 kOk = 0,
250 kCancelled = 1,
251 kUnknown = 2,
252 kInvalidArgument = 3,
253 kDeadlineExceeded = 4,
254 kNotFound = 5,
255 kAlreadyExists = 6,
256 kPermissionDenied = 7,
257 kResourceExhausted = 8,
258 kFailedPrecondition = 9,
259 kAborted = 10,
260 kOutOfRange = 11,
261 kUnimplemented = 12,
262 kInternal = 13,
263 kUnavailable = 14,
264 kDataLoss = 15,
265 kUnauthenticated = 16,
266 };
267
268 class FCP_MUST_USE_RESULT Status final {
269 public:
Status()270 Status() : code_(StatusCode::kOk) {}
Status(StatusCode code,const std::string & message)271 Status(StatusCode code, const std::string& message)
272 : code_(code), message_(message) {}
273
274 // Status is copyable and moveable.
275 Status(const Status&) = default;
276 Status& operator=(const Status&) = default;
277 Status(Status&&) = default;
278 Status& operator=(Status&&) = default;
279
280 // Tests whether this status is OK.
ok()281 bool ok() const { return code_ == StatusCode::kOk; }
282
283 // Gets this status code.
code()284 StatusCode code() const { return code_; }
285
286 // Gets this status message.
message()287 const std::string& message() const { return message_; }
288
289 private:
290 StatusCode code_;
291 std::string message_;
292 };
293
294 template <typename T>
295 class FCP_MUST_USE_RESULT StatusOr final {
296 public:
297 // Default constructor initializes StatusOr with kUnknown code.
StatusOr()298 explicit StatusOr() : StatusOr(StatusCode::kUnknown) {}
299
300 // Constructs a StatusOr from a failed status. The passed status must not be
301 // OK. This constructor is expected to be implicitly called.
StatusOr(Status status)302 StatusOr(Status status) // NOLINT
303 : status_(std::move(status)) {
304 FCP_CHECK(!this->status().ok());
305 }
306
307 // Constructs a StatusOr from a status code.
StatusOr(StatusCode code)308 explicit StatusOr(StatusCode code) : StatusOr(code, "") {}
309
310 // Constructs a StatusOr from a status code and a message.
StatusOr(StatusCode code,const std::string & message)311 StatusOr(StatusCode code, const std::string& message)
312 : status_(Status(code, message)) {
313 FCP_CHECK(!this->status().ok());
314 }
315
316 // Construct a StatusOr from a value.
StatusOr(const T & value)317 StatusOr(const T& value) // NOLINT
318 : value_(value) {}
319
320 // Construct a StatusOr from an R-value.
StatusOr(T && value)321 StatusOr(T&& value) // NOLINT
322 : value_(std::move(value)) {}
323
324 // StatusOr is copyable and moveable.
StatusOr(const StatusOr & other)325 StatusOr(const StatusOr& other) : status_(other.status_) {
326 if (ok()) {
327 AssignValue(other.value_);
328 }
329 }
330
StatusOr(StatusOr && other)331 StatusOr(StatusOr&& other) : status_(std::move(other.status_)) {
332 if (ok()) {
333 AssignValue(std::move(other.value_));
334 }
335 }
336
337 StatusOr& operator=(const StatusOr& other) {
338 if (this != &other) {
339 ClearValue();
340 if (other.ok()) {
341 AssignValue(other.value_);
342 }
343 status_ = other.status_;
344 }
345 return *this;
346 }
347
348 StatusOr& operator=(StatusOr&& other) {
349 if (this != &other) {
350 ClearValue();
351 if (other.ok()) {
352 AssignValue(std::move(other.value_));
353 }
354 status_ = std::move(other.status_);
355 }
356 return *this;
357 }
358
~StatusOr()359 ~StatusOr() { ClearValue(); }
360
361 // Tests whether this StatusOr is OK and has a value.
ok()362 bool ok() const { return status_.ok(); }
363
364 // Returns the status.
status()365 const Status& status() const { return status_; }
366
367 // Returns the value if the StatusOr is OK.
value()368 const T& value() const& {
369 CheckOk();
370 return value_;
371 }
value()372 T& value() & {
373 CheckOk();
374 return value_;
375 }
value()376 T&& value() && {
377 CheckOk();
378 return std::move(value_);
379 }
380
381 // Operator *
382 const T& operator*() const& { return value(); }
383 T& operator*() & { return value(); }
384 T&& operator*() && { return std::move(value()); }
385
386 // Operator ->
387 const T* operator->() const { return &value(); }
388 T* operator->() { return &value(); }
389
390 // Used to explicitly ignore a StatusOr (avoiding unused-result warnings).
Ignore()391 void Ignore() const {}
392
393 private:
CheckOk()394 void CheckOk() const { FCP_CHECK(ok()) << "StatusOr has no value"; }
395
396 // This is used to assign the value in place without invoking the assignment
397 // operator. Using the assignment operator wouldn't work in case the value_
398 // wasn't previously initialized. For example the value_ object might try
399 // to clear its previous value.
400 template <typename Arg>
AssignValue(Arg && arg)401 void AssignValue(Arg&& arg) {
402 new (&unused_) T(std::forward<Arg>(arg));
403 }
404
405 // Destroy the current value if it was initialized.
ClearValue()406 void ClearValue() {
407 if (ok()) value_.~T();
408 }
409
410 Status status_;
411
412 // Using the union allows to avoid initializing the value_ field when
413 // StatusOr is constructed with Status.
414 struct Unused {};
415 union {
416 Unused unused_;
417 T value_;
418 };
419 };
420
421 #else
422
423 // By default absl::Status and absl::StatusOr classes are used.
424 using Status = absl::Status;
425 using StatusCode = absl::StatusCode;
426 template <typename T>
427 using StatusOr = absl::StatusOr<T>;
428
429 #endif // FCP_BAREMETAL
430
431 constexpr auto OK = StatusCode::kOk;
432 constexpr auto CANCELLED = StatusCode::kCancelled;
433 constexpr auto UNKNOWN = StatusCode::kUnknown;
434 constexpr auto INVALID_ARGUMENT = StatusCode::kInvalidArgument;
435 constexpr auto DEADLINE_EXCEEDED = StatusCode::kDeadlineExceeded;
436 constexpr auto NOT_FOUND = StatusCode::kNotFound;
437 constexpr auto ALREADY_EXISTS = StatusCode::kAlreadyExists;
438 constexpr auto PERMISSION_DENIED = StatusCode::kPermissionDenied;
439 constexpr auto RESOURCE_EXHAUSTED = StatusCode::kResourceExhausted;
440 constexpr auto FAILED_PRECONDITION = StatusCode::kFailedPrecondition;
441 constexpr auto ABORTED = StatusCode::kAborted;
442 constexpr auto OUT_OF_RANGE = StatusCode::kOutOfRange;
443 constexpr auto UNIMPLEMENTED = StatusCode::kUnimplemented;
444 constexpr auto INTERNAL = StatusCode::kInternal;
445 constexpr auto UNAVAILABLE = StatusCode::kUnavailable;
446 constexpr auto DATA_LOSS = StatusCode::kDataLoss;
447 constexpr auto UNAUTHENTICATED = StatusCode::kUnauthenticated;
448
449 namespace internal {
450 /** Functions to assist with FCP_RETURN_IF_ERROR() */
AsStatus(const Status & status)451 inline const Status AsStatus(const Status& status) { return status; }
452 template <typename T>
AsStatus(const StatusOr<T> & status_or)453 inline const Status AsStatus(const StatusOr<T>& status_or) {
454 return status_or.status();
455 }
456 } // namespace internal
457
458 /**
459 * Macro which allows to check for a Status (or StatusOr) and return from the
460 * current method if not OK. Example:
461 *
462 * Status DoSomething() {
463 * FCP_RETURN_IF_ERROR(Step1());
464 * FCP_RETURN_IF_ERROR(Step2ReturningStatusOr().status());
465 * return FCP_STATUS(OK);
466 * }
467 */
468 #define FCP_RETURN_IF_ERROR(expr) \
469 do { \
470 ::fcp::Status __status = ::fcp::internal::AsStatus(expr); \
471 if (__status.code() != ::fcp::StatusCode::kOk) { \
472 return (__status); \
473 } \
474 } while (false)
475
476 /**
477 * Macro which allows to check for a StatusOr and return it's status if not OK,
478 * otherwise assign the value in the StatusOr to variable or declaration. Usage:
479 *
480 * StatusOr<bool> DoSomething() {
481 * FCP_ASSIGN_OR_RETURN(auto value, TryComputeSomething());
482 * if (!value) {
483 * FCP_ASSIGN_OR_RETURN(value, TryComputeSomethingElse());
484 * }
485 * return value;
486 * }
487 */
488 #define FCP_ASSIGN_OR_RETURN(lhs, expr) \
489 _FCP_ASSIGN_OR_RETURN_1( \
490 _FCP_ASSIGN_OR_RETURN_CONCAT(statusor_for_aor, __LINE__), lhs, expr)
491
492 #define _FCP_ASSIGN_OR_RETURN_1(statusor, lhs, expr) \
493 auto statusor = (expr); \
494 if (!statusor.ok()) { \
495 return statusor.status(); \
496 } \
497 lhs = std::move(statusor).value()
498
499 // See https://goo.gl/x3iba2 for the reason of this construction.
500 #define _FCP_ASSIGN_OR_RETURN_CONCAT(x, y) \
501 _FCP_ASSIGN_OR_RETURN_CONCAT_INNER(x, y)
502 #define _FCP_ASSIGN_OR_RETURN_CONCAT_INNER(x, y) x##y
503
504 // Status Implementation Details
505 // =============================
506
507 namespace internal {
508
509 /**
510 * Helper class which allows to construct a status with message by streaming
511 * into it. Implicitly converts to Status and StatusOr so can be used as a drop
512 * in replacement when those types are expected.
513 */
514 class FCP_MUST_USE_RESULT StatusBuilder {
515 public:
516 /** Construct a StatusBuilder from status code. */
517 StatusBuilder(StatusCode code, const char* file, int line);
518
519 /**
520 * Copy constructor for status builder. Most of the time not needed because of
521 * copy ellision. */
522 StatusBuilder(StatusBuilder const& other);
523
524 /** Return true if the constructed status will be OK. */
ok()525 inline bool ok() const { return code_ == OK; }
526
527 /** Returns the code of the constructed status. */
code()528 inline StatusCode code() const { return code_; }
529
530 /** Stream into status message of this builder. */
531 template <typename T>
532 StatusBuilder& operator<<(T x) {
533 message_ << x;
534 return *this;
535 }
536
537 /** Mark this builder to emit a log message when the result is constructed. */
LogInfo()538 inline StatusBuilder& LogInfo() {
539 log_severity_ = LogSeverity::kInfo;
540 return *this;
541 }
542
543 /** Mark this builder to emit a log message when the result is constructed. */
LogWarning()544 inline StatusBuilder& LogWarning() {
545 log_severity_ = LogSeverity::kWarning;
546 return *this;
547 }
548
549 /** Mark this builder to emit a log message when the result is constructed. */
LogError()550 inline StatusBuilder& LogError() {
551 log_severity_ = LogSeverity::kError;
552 return *this;
553 }
554
555 /** Mark this builder to emit a log message when the result is constructed. */
LogFatal()556 inline StatusBuilder& LogFatal() {
557 log_severity_ = LogSeverity::kFatal;
558 return *this;
559 }
560
561 /** Implicit conversion to Status. */
562 operator Status(); // NOLINT
563
564 /** Implicit conversion to StatusOr. */
565 template <typename T>
566 inline operator StatusOr<T>() { // NOLINT
567 return StatusOr<T>(static_cast<Status>(*this));
568 }
569
570 private:
571 static constexpr LogSeverity kNoLog = static_cast<LogSeverity>(-1);
572 const char* const file_;
573 const int line_;
574 const StatusCode code_;
575 StringStream message_;
576 LogSeverity log_severity_ = fcp_debug ? LogSeverity::kInfo : kNoLog;
577 };
578
MakeStatusBuilder(StatusCode code,const char * file,int line)579 inline StatusBuilder MakeStatusBuilder(StatusCode code, const char* file,
580 int line) {
581 return StatusBuilder(code, file, line);
582 }
583
584 } // namespace internal
585 } // namespace fcp
586
587 #endif // FCP_BASE_MONITORING_H_
588