Asylo
logging.h
Go to the documentation of this file.
1 /*
2  *
3  * Copyright 2018 Asylo authors
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */
18 
19 #ifndef ASYLO_UTIL_LOGGING_H_
20 #define ASYLO_UTIL_LOGGING_H_
21 
22 #include <cstdint>
23 #include <cstdlib>
24 #include <memory>
25 #include <sstream>
26 #include <string>
27 
28 #include "absl/base/attributes.h"
29 #include "absl/base/optimization.h"
30 
31 /// \cond Internal
32 #define COMPACT_ASYLO_LOG_INFO ::asylo::LogMessage(__FILE__, __LINE__)
33 #define COMPACT_ASYLO_LOG_WARNING
34  ::asylo::LogMessage(__FILE__, __LINE__, WARNING)
35 #define COMPACT_ASYLO_LOG_ERROR ::asylo::LogMessage(__FILE__, __LINE__, ERROR)
36 #define COMPACT_ASYLO_LOG_FATAL
37  ::asylo::LogMessageFatal(__FILE__, __LINE__, FATAL)
38 #define COMPACT_ASYLO_LOG_QFATAL
39  ::asylo::LogMessageFatal(__FILE__, __LINE__, QFATAL)
40 
41 #ifdef NDEBUG
42 #define COMPACT_ASYLO_LOG_DFATAL COMPACT_ASYLO_LOG_ERROR
43 #else
44 #define COMPACT_ASYLO_LOG_DFATAL COMPACT_ASYLO_LOG_FATAL
45 #endif
46 /// \endcond
47 
48 /// Creates a message and logs it to file.
49 ///
50 /// `LOG(severity)` returns a stream object that can be written to with the `<<`
51 /// operator. Log messages are emitted with terminating newlines.
52 /// Example:
53 ///
54 /// ```
55 /// LOG(INFO) << "Found" << num_cookies << " cookies";
56 /// ```
57 ///
58 /// \param severity The severity of the log message, one of `LogSeverity`. The
59 /// FATAL severity will terminate the program after the log is emitted.
60 #define LOG(severity) COMPACT_ASYLO_LOG_##severity.stream()
61 
62 /// A command to LOG only if a condition is true. If the condition is false,
63 /// nothing is logged.
64 /// Example:
65 ///
66 /// ```
67 /// LOG_IF(INFO, num_cookies > 10) << "Got lots of cookies";
68 /// ```
69 ///
70 /// \param severity The severity of the log message, one of `LogSeverity`. The
71 /// FATAL severity will terminate the program after the log is emitted.
72 /// \param condition The condition that determines whether to log the message.
73 #define LOG_IF(severity, condition)
74  !(condition) ? (void)0 : asylo::LogMessageVoidify() & LOG(severity)
75 
76 /// A `LOG` command with an associated verbosity level. The verbosity threshold
77 /// may be configured at runtime with `set_vlog_level` and `InitLogging`.
78 ///
79 /// `VLOG` statements are logged at `INFO` severity if they are logged at all.
80 /// The numeric levels are on a different scale than the severity levels.
81 /// Example:
82 ///
83 /// ```
84 /// VLOG(1) << "Print when VLOG level is set to be 1 or higher";
85 /// ```
86 ///
87 /// \param level The numeric level that determines whether to log the message.
88 #define VLOG(level) LOG_IF(INFO, (level) <= get_vlog_level())
89 
90 /// Terminates the program with a fatal error if the specified condition is
91 /// false.
92 ///
93 /// Example:
94 ///
95 /// ```
96 /// CHECK(!cheese.empty()) << "Out of Cheese";
97 /// ```
98 ///
99 /// Might produce a message like:
100 ///
101 /// `Check_failed: !cheese.empty() Out of Cheese`
102 #define CHECK(condition)
103  LOG_IF(FATAL, !(condition)) << "Check failed: " #condition " "
104 
105 /// Severity level definitions. These represent the four severity levels INFO
106 /// through FATAL.
107 enum LogSeverity { INFO, WARNING, ERROR, FATAL, QFATAL };
108 
109 namespace asylo {
110 
111 /// \cond Internal
112 /// This formats a value for a failing CHECK_XX statement. Ordinarily,
113 /// it uses the definition for `operator<<`, with a few special cases below.
114 template <typename T>
115 inline void MakeCheckOpValueString(std::ostream *os, const T &v) {
116  (*os) << v;
117 }
118 
119 // Overrides for char types provide readable values for unprintable
120 // characters.
121 template <>
122 void MakeCheckOpValueString(std::ostream *os, const char &v);
123 template <>
124 void MakeCheckOpValueString(std::ostream *os, const signed char &v);
125 template <>
126 void MakeCheckOpValueString(std::ostream *os, const unsigned char &v);
127 /// \endcond
128 
129 // We need an explicit specialization for `std::nullptr_t`.
130 template <>
131 void MakeCheckOpValueString(std::ostream *os, const std::nullptr_t &p);
132 
133 /// A helper class for formatting "expr (V1 vs. V2)" in a CHECK_XX
134 /// statement. See MakeCheckOpString for sample usage.
136  public:
137  /// Constructs an object to format a CheckOp message. This constructor
138  /// initializes the message first with `exprtext` followed by " (".
139  ///
140  /// \param exprtext A string representation of the code in `file` at `line`.
141  explicit CheckOpMessageBuilder(const char *exprtext);
142 
143  /// Deletes "stream_".
145 
146  /// Gets the output stream for the first argument of the message.
147  ///
148  /// \return The current stream message.
149  std::ostream *ForVar1() { return stream_; }
150 
151  /// Gets the output stream for writing the argument of the message. This
152  /// writes " vs. " to the stream first.
153  ///
154  /// \return The current stream message.
155  std::ostream *ForVar2();
156 
157  /// Gets the built string contents. The stream is finished with an added ")".
158  ///
159  /// \return The current stream message.
160  std::string *NewString();
161 
162  private:
163  std::ostringstream *stream_;
164 };
165 
166 /// \cond Internal
167 template <typename T1, typename T2>
168 std::string *MakeCheckOpString(const T1 &v1, const T2 &v2,
169  const char *exprtext) {
170  CheckOpMessageBuilder comb(exprtext);
171  MakeCheckOpValueString(comb.ForVar1(), v1);
172  MakeCheckOpValueString(comb.ForVar2(), v2);
173  return comb.NewString();
174 }
175 
176 // Helper functions for CHECK_OP macro.
177 // The (int, int) specialization works around the issue that the compiler
178 // will not instantiate the template version of the function on values of
179 // unnamed enum type - see comment below.
180 //
181 /// \param name An identifier that is the name of the comparison, such as
182 /// Check_EQ or Check_NE.
183 /// \param op The comparison operator, such as == or !=.
184 #define DEFINE_CHECK_OP_IMPL(name, op)
185  template <typename T1, typename T2>
186  inline std::string *name##Impl(const T1 &v1, const T2 &v2,
187  const char *exprtext) {
188  if (ABSL_PREDICT_TRUE(v1 op v2)) return nullptr;
189  return MakeCheckOpString(v1, v2, exprtext);
190  }
191  inline std::string *name##Impl(int v1, int v2, const char *exprtext) {
192  return name##Impl<int, int>(v1, v2, exprtext);
193  }
194 
195 // We use the full name Check_EQ, Check_NE, etc.
196 //
197 // This is to prevent conflicts when the file including logging.h provides its
198 // own #defines for the simpler names EQ, NE, etc. This happens if, for
199 // example, those are used as token names in a yacc grammar.
200 DEFINE_CHECK_OP_IMPL(Check_EQ, ==)
201 DEFINE_CHECK_OP_IMPL(Check_NE, !=)
202 DEFINE_CHECK_OP_IMPL(Check_LE, <=)
203 DEFINE_CHECK_OP_IMPL(Check_LT, <)
204 DEFINE_CHECK_OP_IMPL(Check_GE, >=)
205 DEFINE_CHECK_OP_IMPL(Check_GT, >)
206 #undef DEFINE_CHECK_OP_IMPL
207 /// \endcond
208 
209 /// \cond Internal
210 // Function is overloaded for integral types to allow static const
211 // integrals declared in classes and not defined to be used as arguments to
212 // CHECK* macros. It's not encouraged though.
213 template <typename T>
214 inline const T &GetReferenceableValue(const T &t) {
215  return t;
216 }
217 inline char GetReferenceableValue(char t) { return t; }
218 inline uint8_t GetReferenceableValue(uint8_t t) { return t; }
219 inline int8_t GetReferenceableValue(int8_t t) { return t; }
220 inline int16_t GetReferenceableValue(int16_t t) { return t; }
221 inline uint16_t GetReferenceableValue(uint16_t t) { return t; }
222 inline int32_t GetReferenceableValue(int32_t t) { return t; }
223 inline uint32_t GetReferenceableValue(uint32_t t) { return t; }
224 inline int64_t GetReferenceableValue(int64_t t) { return t; }
225 inline uint64_t GetReferenceableValue(uint64_t t) { return t; }
226 /// \endcond
227 
228 /// Compares `val1` and `val2` with `op`, and does `log` if false.
229 ///
230 /// \param name An identifier that is the name of the comparison, such as
231 /// Check_EQ or Check_NE.
232 /// \param op The comparison operator, such as == or !=.
233 /// \param val1 The first variable to be compared.
234 /// \param val2 The second variable to be compared.
235 /// \param log The log action to be performed if the comparison returns false.
236 #define CHECK_OP_LOG(name, op, val1, val2, log)
237  while (std::unique_ptr<std::string> _result = std::unique_ptr<std::string>(
238  asylo::name##Impl(asylo::GetReferenceableValue(val1),
239  asylo::GetReferenceableValue(val2),
240  #val1 " " #op " " #val2)))
241  log(__FILE__, __LINE__, *_result).stream()
242 
243 /// Compares `val1` and `val2` with `op`, and produces a LOG(FATAL) if false.
244 ///
245 /// \param name An identifier that is the name of the comparison, such as
246 /// Check_EQ or Check_NE.
247 /// \param op The comparison operator, such as == or !=.
248 /// \param val1 The first variable to be compared.
249 /// \param val2 The second variable to be compared.
250 #define CHECK_OP(name, op, val1, val2)
251  CHECK_OP_LOG(name, op, val1, val2, asylo::LogMessageFatal)
252 
253 /// Produces a LOG(FATAL) unless `val1` equals `val2`.
254 #define CHECK_EQ(val1, val2) CHECK_OP(Check_EQ, ==, val1, val2)
255 /// Produces a LOG(FATAL) unless `val1` does not equal to `val2`.
256 #define CHECK_NE(val1, val2) CHECK_OP(Check_NE, !=, val1, val2)
257 /// Produces a LOG(FATAL) unless `val1` is less than or equal to `val2`.
258 #define CHECK_LE(val1, val2) CHECK_OP(Check_LE, <=, val1, val2)
259 /// Produces a LOG(FATAL) unless `val1` is less than `val2`.
260 #define CHECK_LT(val1, val2) CHECK_OP(Check_LT, <, val1, val2)
261 /// Produces a LOG(FATAL) unless `val1` is greater than or equal to `val2`.
262 #define CHECK_GE(val1, val2) CHECK_OP(Check_GE, >=, val1, val2)
263 /// Produces a LOG(FATAL) unless `val1` is greater than `val2`.
264 #define CHECK_GT(val1, val2) CHECK_OP(Check_GT, >, val1, val2)
265 
266 /// Checks that the argument is not null, and returns it.
267 ///
268 /// Unlike other `CHECK` macros, this one returns its input, so it can be used
269 /// in initializers. Outside initializers, prefer `CHECK`.
270 ///
271 /// `CHECK_NOTNULL` works for both raw pointers and (compatible) smart pointers
272 /// including `std::unique_ptr` and `std::shared_ptr`.
273 ///
274 /// For smart pointers `CHECK_NOTNULL` returns a reference to its argument,
275 /// preserving the value category (i.e., an rvalue reference for an
276 /// rvalue argument, and an lvalue reference otherwise). For pre-C++11
277 /// compilers that's not possible, so as a best available approximation
278 /// a reference-to-const will be returned if the argument is an rvalue.
279 ///
280 /// \param val The value being compared.
281 #define CHECK_NOTNULL(val)
282  asylo::CheckNotNull(__FILE__, __LINE__, "'" #val "' Must be non NULL", (val))
283 
284 /// Sets the verbosity threshold for VLOG. A VLOG command with a level greater
285 /// than this will be ignored.
286 ///
287 /// \param level The verbosity threshold for VLOG to be set. A VLOG command with
288 /// level less than or equal to this will be logged.
289 void set_vlog_level(int level);
290 
291 /// Gets the verbosity threshold for VLOG. A VLOG command with a level greater
292 /// than this will be ignored.
293 ///
294 /// \return The current verbosity threshold for VLOG.
295 int get_vlog_level();
296 
297 /// Sets the log directory, as specified when this enclave is initialized. This
298 /// is only set once. Any request to reset it will return false.
299 ///
300 /// \param log_directory The log file directory.
301 /// \return True if and only if the log directory is set successfully.
302 bool set_log_directory(const std::string &log_directory);
303 
304 /// Gets the log directory that was specified when this enclave is initialized.
305 ///
306 /// \return The directory where the log files will be.
307 const std::string get_log_directory();
308 
309 /// Checks the log directory to make sure it's accessible, and creates it if it
310 /// does not exist.
311 ///
312 /// \param path The directory to be checked.
313 bool EnsureDirectory(const char *path);
314 
315 /// Initializes minimal logging library.
316 ///
317 /// For untrusted logging, the program name specified by argv0 will be used as
318 /// log filename; For enclave logging, the enclave name will be used as log
319 /// filename (any slashes or dots will be removed). This method is called during
320 /// enclave initialization. For untrusted logging, this should be called in
321 /// main().
322 ///
323 /// \param directory The log file directory.
324 /// \param file_name The name of the log file.
325 /// \param level The verbosity threshold for VLOG commands. A VLOG command with
326 /// a level equal to or lower than it will be logged.
327 bool InitLogging(const char *directory, const char *file_name, int level);
328 
329 /// Class representing a log message created by a log macro.
330 class LogMessage {
331  public:
332  /// Constructs a new message with `INFO` severity.
333  ///
334  /// \param file The source file that produced the log.
335  /// \param line The source code line that produced the log.
336  LogMessage(const char *file, int line);
337 
338  /// Constructs a new message with the specified severity.
339  ///
340  /// \param file The source file that produced the log.
341  /// \param line The source code line that produced the log.
342  /// \param severity The severity level of the log.
343  LogMessage(const char *file, int line, LogSeverity severity);
344 
345  /// Constructs a log message with additional text that is provided by CHECK
346  /// macros.
347  ///
348  /// \param file The source file that produced the log.
349  /// \param line The source code line that produced the log.
350  /// \param result The result message when check fails.
351  LogMessage(const char *file, int line, const std::string &result);
352 
353  /// The destructor flushes the message.
354  ~LogMessage();
355 
356  /// Gets a reference to the underlying string stream.
357  ///
358  /// \return A reference to the underlying string stream.
359  std::ostringstream &stream() { return stream_; }
360 
361  protected:
362  // Sends the message to print.
363  void SendToLog(const std::string &message_text);
364 
366 
367  // stream_ reads all the input messages into a stringstream, then it's
368  // converted into a string in the destructor for printing.
369  std::ostringstream stream_;
370 
371  private:
372  void Init(const char *file, int line, LogSeverity severity);
373 
374  LogMessage(const LogMessage &) = delete;
375  void operator=(const LogMessage &) = delete;
376 };
377 
378 /// This class is used just to take an `ostream` type and make it a `void` type
379 /// to satisify the ternary operator in `LOG_IF`.
380 /// `operand&` is used because it has precedence lower than `<<` but higher than
381 /// `:?`
383  public:
384  void operator&(const std::ostream &) {}
385 };
386 
387 /// A LogSeverity FATAL (or QFATAL) version of LogMessage that the compiler can
388 /// interpret as noreturn.
389 class LogMessageFatal : public LogMessage {
390  public:
391  /// The destructor flushes the message and does not return.
393 
394  /// Constructs a new message with FATAL severity.
395  ///
396  /// \param file The source file that produced the log.
397  /// \param line The source code line that produced the log.
398  /// \param severity The severity for the message (FATAL or QFATAL).
399  LogMessageFatal(const char *file, int line, LogSeverity severity)
400  : LogMessage(file, line, severity) {}
401 
402  /// Constructs a message with FATAL severity for use by CHECK macros.
403  ///
404  /// \param file The source file that produced the log.
405  /// \param line The source code line that produced the log.
406  /// \param result The result message when check fails.
407  LogMessageFatal(const char *file, int line, const std::string &result)
408  : LogMessage(file, line, result) {}
409 };
410 
411 /// Logs a message if the given value of type `T` is null, and then forwards the
412 /// value.
413 ///
414 /// In C++11, all cases can be handled by a single function. Since the value
415 /// category of the argument is preserved (also for rvalue references),
416 /// member initializer lists like the one below will compile correctly:
417 ///
418 /// ```
419 /// Foo()
420 /// : x_(CHECK_NOTNULL(MethodReturningUniquePtr())) {}
421 /// ```
422 ///
423 /// \param file The source file that produced the log.
424 /// \param line The source code line that produced the log.
425 /// \param exprtext A string representation of the code in `file` at `line`.
426 /// \param t The parameter being checked for null.
427 template <typename T>
428 T CheckNotNull(const char *file, int line, const char *exprtext, T &&t) {
429  if (ABSL_PREDICT_FALSE(!t)) {
430  LogMessage(file, line, std::string(exprtext));
431  }
432  return std::forward<T>(t);
433 }
434 
435 } // namespace asylo
436 
437 #endif // ASYLO_UTIL_LOGGING_H_
std::string * NewString()
Gets the built string contents.
bool InitLogging(const char *directory, const char *file_name, int level)
Initializes minimal logging library.
const std::string get_log_directory()
Gets the log directory that was specified when this enclave is initialized.
int get_vlog_level()
Gets the verbosity threshold for VLOG.
std::ostringstream stream_
Definition: logging.h:369
LogMessage(const char *file, int line)
Constructs a new message with INFO severity.
LogMessageFatal(const char *file, int line, LogSeverity severity)
Constructs a new message with FATAL severity.
Definition: logging.h:399
Class representing a log message created by a log macro.
Definition: logging.h:330
std::ostream * ForVar1()
Gets the output stream for the first argument of the message.
Definition: logging.h:149
#define CHECK_OP(name, op, val1, val2)
Compares val1 and val2 with op, and produces a LOG(FATAL) if false.
Definition: logging.h:250
LogMessageFatal(const char *file, int line, const std::string &result)
Constructs a message with FATAL severity for use by CHECK macros.
Definition: logging.h:407
void set_vlog_level(int level)
Sets the verbosity threshold for VLOG.
CheckOpMessageBuilder(const char *exprtext)
Constructs an object to format a CheckOp message.
~CheckOpMessageBuilder()
Deletes "stream_".
#define CHECK_OP_LOG(name, op, val1, val2, log)
Compares val1 and val2 with op, and does log if false.
Definition: logging.h:236
void SendToLog(const std::string &message_text)
LogMessage(const char *file, int line, const std::string &result)
Constructs a log message with additional text that is provided by CHECK macros.
bool EnsureDirectory(const char *path)
Checks the log directory to make sure it&#39;s accessible, and creates it if it does not exist...
std::ostream * ForVar2()
Gets the output stream for writing the argument of the message.
LogSeverity severity_
Definition: logging.h:365
void MakeCheckOpValueString(std::ostream *os, const std::nullptr_t &p)
A LogSeverity FATAL (or QFATAL) version of LogMessage that the compiler can interpret as noreturn...
Definition: logging.h:389
T CheckNotNull(const char *file, int line, const char *exprtext, T &&t)
Logs a message if the given value of type T is null, and then forwards the value. ...
Definition: logging.h:428
bool set_log_directory(const std::string &log_directory)
Sets the log directory, as specified when this enclave is initialized.
ABSL_ATTRIBUTE_NORETURN ~LogMessageFatal()
The destructor flushes the message and does not return.
#define LOG(severity)
Creates a message and logs it to file.
Definition: logging.h:60
~LogMessage()
The destructor flushes the message.
std::ostringstream & stream()
Gets a reference to the underlying string stream.
Definition: logging.h:359
void operator &(const std::ostream &)
Definition: logging.h:384
A helper class for formatting "expr (V1 vs. V2)" in a CHECK_XX statement.
Definition: logging.h:135
LogMessage(const char *file, int line, LogSeverity severity)
Constructs a new message with the specified severity.
#define LOG_IF(severity, condition)
A command to LOG only if a condition is true.
Definition: logging.h:73
This class is used just to take an ostream type and make it a void type to satisify the ternary opera...
Definition: logging.h:382