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 COMPACT_ASYLO_LOG_FATAL
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 };
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, const char *exprtext) {
166  CheckOpMessageBuilder comb(exprtext);
167  MakeCheckOpValueString(comb.ForVar1(), v1);
168  MakeCheckOpValueString(comb.ForVar2(), v2);
169  return comb.NewString();
170 }
171 
172 // Helper functions for CHECK_OP macro.
173 // The (int, int) specialization works around the issue that the compiler
174 // will not instantiate the template version of the function on values of
175 // unnamed enum type - see comment below.
176 //
177 /// \param name An identifier that is the name of the comparison, such as
178 /// Check_EQ or Check_NE.
179 /// \param op The comparison operator, such as == or !=.
180 #define DEFINE_CHECK_OP_IMPL(name, op)
181  template <typename T1, typename T2>
182  inline std::string *name##Impl(const T1 &v1, const T2 &v2,
183  const char *exprtext) {
184  if (ABSL_PREDICT_TRUE(v1 op v2)) return nullptr;
185  return MakeCheckOpString(v1, v2, exprtext);
186  }
187  inline std::string *name##Impl(int v1, int v2, const char *exprtext) {
188  return name##Impl<int, int>(v1, v2, exprtext);
189  }
190 
191 // We use the full name Check_EQ, Check_NE, etc.
192 //
193 // This is to prevent conflicts when the file including logging.h provides its
194 // own #defines for the simpler names EQ, NE, etc. This happens if, for
195 // example, those are used as token names in a yacc grammar.
196 DEFINE_CHECK_OP_IMPL(Check_EQ, ==)
197 DEFINE_CHECK_OP_IMPL(Check_NE, !=)
198 DEFINE_CHECK_OP_IMPL(Check_LE, <=)
199 DEFINE_CHECK_OP_IMPL(Check_LT, <)
200 DEFINE_CHECK_OP_IMPL(Check_GE, >=)
201 DEFINE_CHECK_OP_IMPL(Check_GT, >)
202 #undef DEFINE_CHECK_OP_IMPL
203 /// \endcond
204 
205 /// \cond Internal
206 // Function is overloaded for integral types to allow static const
207 // integrals declared in classes and not defined to be used as arguments to
208 // CHECK* macros. It's not encouraged though.
209 template <typename T>
210 inline const T &GetReferenceableValue(const T &t) {
211  return t;
212 }
213 inline char GetReferenceableValue(char t) { return t; }
214 inline uint8_t GetReferenceableValue(uint8_t t) { return t; }
215 inline int8_t GetReferenceableValue(int8_t t) { return t; }
216 inline int16_t GetReferenceableValue(int16_t t) { return t; }
217 inline uint16_t GetReferenceableValue(uint16_t t) { return t; }
218 inline int32_t GetReferenceableValue(int32_t t) { return t; }
219 inline uint32_t GetReferenceableValue(uint32_t t) { return t; }
220 inline int64_t GetReferenceableValue(int64_t t) { return t; }
221 inline uint64_t GetReferenceableValue(uint64_t t) { return t; }
222 /// \endcond
223 
224 /// Compares `val1` and `val2` with `op`, and does `log` if false.
225 ///
226 /// \param name An identifier that is the name of the comparison, such as
227 /// Check_EQ or Check_NE.
228 /// \param op The comparison operator, such as == or !=.
229 /// \param val1 The first variable to be compared.
230 /// \param val2 The second variable to be compared.
231 /// \param log The log action to be performed if the comparison returns false.
232 #define CHECK_OP_LOG(name, op, val1, val2, log)
233  while (std::unique_ptr<std::string> _result = std::unique_ptr<std::string>(
234  asylo::name##Impl(asylo::GetReferenceableValue(val1),
235  asylo::GetReferenceableValue(val2),
236  #val1 " " #op " " #val2)))
237  log(__FILE__, __LINE__, *_result).stream()
238 
239 /// Compares `val1` and `val2` with `op`, and produces a LOG(FATAL) if false.
240 ///
241 /// \param name An identifier that is the name of the comparison, such as
242 /// Check_EQ or Check_NE.
243 /// \param op The comparison operator, such as == or !=.
244 /// \param val1 The first variable to be compared.
245 /// \param val2 The second variable to be compared.
246 #define CHECK_OP(name, op, val1, val2)
247  CHECK_OP_LOG(name, op, val1, val2, asylo::LogMessageFatal)
248 
249 /// Produces a LOG(FATAL) unless `val1` equals `val2`.
250 #define CHECK_EQ(val1, val2) CHECK_OP(Check_EQ, ==, val1, val2)
251 /// Produces a LOG(FATAL) unless `val1` does not equal to `val2`.
252 #define CHECK_NE(val1, val2) CHECK_OP(Check_NE, !=, val1, val2)
253 /// Produces a LOG(FATAL) unless `val1` is less than or equal to `val2`.
254 #define CHECK_LE(val1, val2) CHECK_OP(Check_LE, <=, val1, val2)
255 /// Produces a LOG(FATAL) unless `val1` is less than `val2`.
256 #define CHECK_LT(val1, val2) CHECK_OP(Check_LT, <, val1, val2)
257 /// Produces a LOG(FATAL) unless `val1` is greater than or equal to `val2`.
258 #define CHECK_GE(val1, val2) CHECK_OP(Check_GE, >=, val1, val2)
259 /// Produces a LOG(FATAL) unless `val1` is greater than `val2`.
260 #define CHECK_GT(val1, val2) CHECK_OP(Check_GT, >, val1, val2)
261 
262 /// Checks that the argument is not null, and returns it.
263 ///
264 /// Unlike other `CHECK` macros, this one returns its input, so it can be used
265 /// in initializers. Outside initializers, prefer `CHECK`.
266 ///
267 /// `CHECK_NOTNULL` works for both raw pointers and (compatible) smart pointers
268 /// including `std::unique_ptr` and `std::shared_ptr`.
269 ///
270 /// For smart pointers `CHECK_NOTNULL` returns a reference to its argument,
271 /// preserving the value category (i.e., an rvalue reference for an
272 /// rvalue argument, and an lvalue reference otherwise). For pre-C++11
273 /// compilers that's not possible, so as a best available approximation
274 /// a reference-to-const will be returned if the argument is an rvalue.
275 ///
276 /// \param val The value being compared.
277 #define CHECK_NOTNULL(val)
278  asylo::CheckNotNull(__FILE__, __LINE__, "'" #val "' Must be non NULL", (val))
279 
280 /// Sets the verbosity threshold for VLOG. A VLOG command with a level greater
281 /// than this will be ignored.
282 ///
283 /// \param level The verbosity threshold for VLOG to be set. A VLOG command with
284 /// level less than or equal to this will be logged.
285 void set_vlog_level(int level);
286 
287 /// Gets the verbosity threshold for VLOG. A VLOG command with a level greater
288 /// than this will be ignored.
289 ///
290 /// \return The current verbosity threshold for VLOG.
291 int get_vlog_level();
292 
293 /// Sets the log directory, as specified when this enclave is initialized. This
294 /// is only set once. Any request to reset it will return false.
295 ///
296 /// \param log_directory The log file directory.
297 /// \return True if and only if the log directory is set successfully.
298 bool set_log_directory(const std::string &log_directory);
299 
300 /// Gets the log directory that was specified when this enclave is initialized.
301 ///
302 /// \return The directory where the log files will be.
303 const std::string get_log_directory();
304 
305 /// Checks the log directory to make sure it's accessible, and creates it if it
306 /// does not exist.
307 ///
308 /// \param path The directory to be checked.
309 bool EnsureDirectory(const char *path);
310 
311 /// Initializes minimal logging library.
312 ///
313 /// For untrusted logging, the program name specified by argv0 will be used as
314 /// log filename; For enclave logging, the enclave name will be used as log
315 /// filename (any slashes or dots will be removed). This method is called during
316 /// enclave initialization. For untrusted logging, this should be called in
317 /// main().
318 ///
319 /// \param directory The log file directory.
320 /// \param file_name The name of the log file.
321 /// \param level The verbosity threshold for VLOG commands. A VLOG command with
322 /// a level equal to or lower than it will be logged.
323 bool InitLogging(const char *directory, const char *file_name, int level);
324 
325 /// Class representing a log message created by a log macro.
326 class LogMessage {
327  public:
328  /// Constructs a new message with `INFO` severity.
329  ///
330  /// \param file The source file that produced the log.
331  /// \param line The source code line that produced the log.
332  LogMessage(const char *file, int line);
333 
334  /// Constructs a new message with the specified severity.
335  ///
336  /// \param file The source file that produced the log.
337  /// \param line The source code line that produced the log.
338  /// \param severity The severity level of the log.
339  LogMessage(const char *file, int line, LogSeverity severity);
340 
341  /// Constructs a log message with additional text that is provided by CHECK
342  /// macros.
343  ///
344  /// \param file The source file that produced the log.
345  /// \param line The source code line that produced the log.
346  /// \param result The result message when check fails.
347  LogMessage(const char *file, int line, const std::string &result);
348 
349  /// The destructor flushes the message.
350  ~LogMessage();
351 
352  /// Gets a reference to the underlying string stream.
353  ///
354  /// \return A reference to the underlying string stream.
355  std::ostringstream &stream() { return stream_; }
356 
357  private:
358  void Init(const char *file, int line, LogSeverity severity);
359 
360  // Sends the message to print.
361  void SendToLog(const std::string &message_text);
362 
363  // stream_ reads all the input messages into a stringstream, then it's
364  // converted into a string in the destructor for printing.
365  std::ostringstream stream_;
366  LogSeverity severity_;
367 
368  LogMessage(const LogMessage &) = delete;
369  void operator=(const LogMessage &) = delete;
370 };
371 
372 /// This class is used just to take an `ostream` type and make it a `void` type
373 /// to satisify the ternary operator in `LOG_IF`.
374 /// `operand&` is used because it has precedence lower than `<<` but higher than
375 /// `:?`
377  public:
378  void operator&(const std::ostream &) {}
379 };
380 
381 /// Default LogSeverity FATAL version of LogMessage.
382 class LogMessageFatal : public LogMessage {
383  public:
384  /// Constructs a new message with FATAL severity.
385  ///
386  /// \param file The source file that produced the log.
387  /// \param line The source code line that produced the log.
388  LogMessageFatal(const char *file, int line) : LogMessage(file, line, FATAL) {}
389  /// Constructs a message with FATAL severity for use by CHECK macros.
390  ///
391  /// \param file The source file that produced the log.
392  /// \param line The source code line that produced the log.
393  /// \param result The result message when check fails.
394  LogMessageFatal(const char *file, int line, const std::string &result)
395  : LogMessage(file, line, result) {}
396 };
397 
398 /// Logs a message if the given value of type `T` is null, and then forwards the
399 /// value.
400 ///
401 /// In C++11, all cases can be handled by a single function. Since the value
402 /// category of the argument is preserved (also for rvalue references),
403 /// member initializer lists like the one below will compile correctly:
404 ///
405 /// ```
406 /// Foo()
407 /// : x_(CHECK_NOTNULL(MethodReturningUniquePtr())) {}
408 /// ```
409 ///
410 /// \param file The source file that produced the log.
411 /// \param line The source code line that produced the log.
412 /// \param exprtext A string representation of the code in `file` at `line`.
413 /// \param t The parameter being checked for null.
414 template <typename T>
415 T CheckNotNull(const char *file, int line, const char *exprtext, T &&t) {
416  if (ABSL_PREDICT_FALSE(!t)) {
417  LogMessage(file, line, std::string(exprtext));
418  }
419  return std::forward<T>(t);
420 }
421 
422 } // namespace asylo
423 
424 #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:326
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:246
LogMessageFatal(const char *file, int line, const std::string &result)
Constructs a message with FATAL severity for use by CHECK macros.
Definition: logging.h:394
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:232
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:388
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:382
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:415
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:355
void operator &(const std::ostream &)
Definition: logging.h:378
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:376