xref: /aosp_15_r20/external/federated-compute/fcp/base/monitoring.h (revision 14675a029014e728ec732f129a32e299b2da0601)
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