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