summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--base/logging.cc195
-rw-r--r--base/logging.h150
2 files changed, 337 insertions, 8 deletions
diff --git a/base/logging.cc b/base/logging.cc
index d35cfdb..26aeff6 100644
--- a/base/logging.cc
+++ b/base/logging.cc
@@ -19,6 +19,7 @@ typedef HANDLE MutexHandle;
#endif
#if defined(OS_POSIX)
+#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
@@ -43,6 +44,111 @@ typedef pthread_mutex_t* MutexHandle;
namespace logging {
+static const int kErrorMessageBufferSize = 256;
+
+#ifdef OS_POSIX
+// Wrapper for strerror_r functions that implement the POSIX interface. POSIX
+// does not define the behaviour for some of the edge cases, so we wrap it to
+// guarantee that they are handled. This is used on all POSIX platforms, but
+// on Linux there are some more cases (see below).
+static void wrap_posix_strerror_r(int err, char* buf, size_t len) {
+ int old_errno = errno;
+ // Have to cast since otherwise we get an error if this is the GNU version
+ // (but in such a scenario this function is never called). Sadly we can't use
+ // C++-style casts because the appropriate one is reinterpret_cast but it's
+ // considered illegal to reinterpret_cast a type to itself, so we get an
+ // error in the opposite case.
+ int result = (int)strerror_r(err, buf, len);
+ if (result == 0) {
+ // POSIX is vague about whether the string will be terminated, although
+ // it indirectly implies that typically ERANGE will be returned, instead
+ // of truncating the string. We play it safe by always terminating the
+ // string explicitly.
+ buf[len - 1] = '\0';
+ } else {
+ // Error. POSIX is vague about whether the return value is itself a system
+ // error code or something else. On Linux currently it is -1 and errno is
+ // set. On BSD-derived systems it is a system error and errno is unchanged.
+ // We try and detect which case it is so as to put as much useful info as
+ // we can into our message.
+ int error;
+ int new_errno = errno;
+ if (new_errno != old_errno) {
+ // errno was changed, so probably the return value is just -1 or something
+ // else that doesn't provide any info, and errno is the error.
+ error = new_errno;
+ } else {
+ // Either the error from strerror_r was the same as the previous value, or
+ // errno wasn't used. Assume the latter.
+ error = result;
+ }
+ // snprintf truncates and always null-terminates.
+ snprintf(buf, len, "Error %d while retrieving error %d", error, err);
+ }
+ errno = old_errno;
+}
+
+#ifdef OS_LINUX
+// glibc has two strerror_r functions: a historical GNU-specific one that
+// returns type char *, and a POSIX.1-2001 compliant one available since 2.3.4
+// that returns int. This detects which one has been selected at compile-time
+// and wraps the GNU one so that it behaves like the POSIX one.
+template <class T>
+class GlibcStrErrorRWrapper;
+
+template <>
+class GlibcStrErrorRWrapper<char*> {
+ public:
+ static void WrapStrErrorR(int err, char* buf, size_t len) {
+ // strerror_r is the GNU version.
+ // See note in wrap_posix_strerror_r about the casting.
+ char* rc = (char*)strerror_r(err, buf, len);
+ if (rc != buf) {
+ // glibc did not use buf and returned a static string instead. Copy it
+ // into buf.
+ buf[0] = '\0';
+ strncat(buf, rc, len - 1);
+ }
+ // The GNU version never fails. Unknown errors get an
+ // "unknown error message". The result is always null terminated.
+ }
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(GlibcStrErrorRWrapper);
+};
+
+template <>
+class GlibcStrErrorRWrapper<int> {
+ public:
+ static void WrapStrErrorR(int err, char* buf, size_t len) {
+ // strerror_r is the POSIX version. Defer to our global function shared on
+ // all POSIX platforms.
+ wrap_posix_strerror_r(err, buf, len);
+ }
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(GlibcStrErrorRWrapper);
+};
+#endif // OS_LINUX
+
+// Thread-safe strerror function with dependable semantics that never fails.
+// It will write the string form of error "err" to buffer buf of length len.
+// If there is an error calling the OS's strerror_r() function then a message to
+// that effect will be printed into buf, truncating if necessary. The final
+// result is always null-terminated. The value of errno is never changed.
+static void safe_strerror_r(int err, char *buf, size_t len) {
+ if (buf == NULL || len <= 0) {
+ return;
+ }
+#ifdef OS_LINUX
+ GlibcStrErrorRWrapper<typeof(strerror_r(err, buf, len))>
+ ::WrapStrErrorR(err, buf, len);
+#else
+ wrap_posix_strerror_r(err, buf, len);
+#endif
+}
+#endif // OS_POSIX
+
bool g_enable_dcheck = false;
const char* const log_severity_names[LOG_NUM_SEVERITIES] = {
@@ -567,6 +673,95 @@ LogMessage::~LogMessage() {
}
}
+#if defined(OS_WIN)
+// This has already been defined in the header, but defining it again as DWORD
+// ensures that the type used in the header is equivalent to DWORD. If not,
+// the redefinition is a compile error.
+typedef DWORD SystemErrorCode;
+#endif
+
+SystemErrorCode GetLastSystemErrorCode() {
+#if defined(OS_WIN)
+ return ::GetLastError();
+#elif defined(OS_POSIX)
+ return errno;
+#else
+#error Not implemented
+#endif
+}
+
+#if defined(OS_WIN)
+Win32ErrorLogMessage::Win32ErrorLogMessage(const char* file,
+ int line,
+ LogSeverity severity,
+ SystemErrorCode err,
+ const char* module)
+ : err_(err),
+ module_(module),
+ log_message_(file, line, severity) {
+}
+
+Win32ErrorLogMessage::Win32ErrorLogMessage(const char* file,
+ int line,
+ LogSeverity severity,
+ SystemErrorCode err)
+ : err_(err),
+ module_(NULL),
+ log_message_(file, line, severity) {
+}
+
+Win32ErrorLogMessage::~Win32ErrorLogMessage() {
+ char msgbuf[kErrorMessageBufferSize];
+ DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM;
+ HMODULE hmod;
+ if (module_) {
+ hmod = GetModuleHandleA(module_);
+ if (hmod) {
+ flags |= FORMAT_MESSAGE_FROM_HMODULE;
+ } else {
+ // This makes a nested Win32ErrorLogMessage. It will have module_ of NULL
+ // so it will not call GetModuleHandle, so recursive errors are
+ // impossible.
+ DPLOG(WARNING) << "Couldn't open module " << module_
+ << " for error message query";
+ }
+ } else {
+ hmod = NULL;
+ }
+ DWORD len = FormatMessageA(flags,
+ hmod,
+ err_,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ msgbuf,
+ sizeof(msgbuf) / sizeof(msgbuf[0]),
+ NULL);
+ if (len) {
+ while ((len > 0) &&
+ isspace(static_cast<unsigned char>(msgbuf[len - 1]))) {
+ msgbuf[--len] = 0;
+ }
+ stream() << " [" << msgbuf << "]";
+ } else {
+ stream() << " [Error " << GetLastError() << " while retrieving error "
+ << err_ << "]";
+ }
+}
+#elif defined(OS_POSIX)
+ErrnoLogMessage::ErrnoLogMessage(const char* file,
+ int line,
+ LogSeverity severity,
+ SystemErrorCode err)
+ : err_(err),
+ log_message_(file, line, severity) {
+}
+
+ErrnoLogMessage::~ErrnoLogMessage() {
+ char msgbuf[kErrorMessageBufferSize];
+ safe_strerror_r(err_, msgbuf, sizeof(msgbuf));
+ stream() << " [" << msgbuf << "]";
+}
+#endif // OS_WIN
+
void CloseLogFile() {
if (!log_file)
return;
diff --git a/base/logging.h b/base/logging.h
index a1d439c..f117200 100644
--- a/base/logging.h
+++ b/base/logging.h
@@ -75,6 +75,14 @@
//
// We also override the standard 'assert' to use 'DLOG_ASSERT'.
//
+// Lastly, there is:
+//
+// PLOG(ERROR) << "Couldn't do foo";
+// DPLOG(ERROR) << "Couldn't do foo";
+//
+// which appends the last system error to the message in string form (taken from
+// GetLastError() on Windows and errno on POSIX).
+//
// The supported severity levels for macros that allow you to specify one
// are (in increasing order of severity) INFO, WARNING, ERROR, ERROR_REPORT,
// and FATAL.
@@ -192,18 +200,33 @@ const LogSeverity LOG_DFATAL_LEVEL = LOG_FATAL;
// A few definitions of macros that don't generate much code. These are used
// by LOG() and LOG_IF, etc. Since these are used all over our code, it's
// better to have compact code for these operations.
+#define COMPACT_GOOGLE_LOG_EX_INFO(ClassName, ...) \
+ logging::ClassName(__FILE__, __LINE__, logging::LOG_INFO , ##__VA_ARGS__)
+#define COMPACT_GOOGLE_LOG_EX_WARNING(ClassName, ...) \
+ logging::ClassName(__FILE__, __LINE__, logging::LOG_WARNING , ##__VA_ARGS__)
+#define COMPACT_GOOGLE_LOG_EX_ERROR(ClassName, ...) \
+ logging::ClassName(__FILE__, __LINE__, logging::LOG_ERROR , ##__VA_ARGS__)
+#define COMPACT_GOOGLE_LOG_EX_ERROR_REPORT(ClassName, ...) \
+ logging::ClassName(__FILE__, __LINE__, \
+ logging::LOG_ERROR_REPORT , ##__VA_ARGS__)
+#define COMPACT_GOOGLE_LOG_EX_FATAL(ClassName, ...) \
+ logging::ClassName(__FILE__, __LINE__, logging::LOG_FATAL , ##__VA_ARGS__)
+#define COMPACT_GOOGLE_LOG_EX_DFATAL(ClassName, ...) \
+ logging::ClassName(__FILE__, __LINE__, \
+ logging::LOG_DFATAL_LEVEL , ##__VA_ARGS__)
+
#define COMPACT_GOOGLE_LOG_INFO \
- logging::LogMessage(__FILE__, __LINE__)
+ COMPACT_GOOGLE_LOG_EX_INFO(LogMessage)
#define COMPACT_GOOGLE_LOG_WARNING \
- logging::LogMessage(__FILE__, __LINE__, logging::LOG_WARNING)
+ COMPACT_GOOGLE_LOG_EX_WARNING(LogMessage)
#define COMPACT_GOOGLE_LOG_ERROR \
- logging::LogMessage(__FILE__, __LINE__, logging::LOG_ERROR)
+ COMPACT_GOOGLE_LOG_EX_ERROR(LogMessage)
#define COMPACT_GOOGLE_LOG_ERROR_REPORT \
- logging::LogMessage(__FILE__, __LINE__, logging::LOG_ERROR_REPORT)
+ COMPACT_GOOGLE_LOG_EX_ERROR_REPORT(LogMessage)
#define COMPACT_GOOGLE_LOG_FATAL \
- logging::LogMessage(__FILE__, __LINE__, logging::LOG_FATAL)
+ COMPACT_GOOGLE_LOG_EX_FATAL(LogMessage)
#define COMPACT_GOOGLE_LOG_DFATAL \
- logging::LogMessage(__FILE__, __LINE__, logging::LOG_DFATAL_LEVEL)
+ COMPACT_GOOGLE_LOG_EX_DFATAL(LogMessage)
// wingdi.h defines ERROR to be 0. When we call LOG(ERROR), it gets
// substituted with 0, and it expands to COMPACT_GOOGLE_LOG_0. To allow us
@@ -211,8 +234,9 @@ const LogSeverity LOG_DFATAL_LEVEL = LOG_FATAL;
// as COMPACT_GOOGLE_LOG_ERROR, and also define ERROR the same way that
// the Windows SDK does for consistency.
#define ERROR 0
-#define COMPACT_GOOGLE_LOG_0 \
- logging::LogMessage(__FILE__, __LINE__, logging::LOG_ERROR)
+#define COMPACT_GOOGLE_LOG_EX_0(ClassName, ...) \
+ COMPACT_GOOGLE_LOG_EX_ERROR(ClassName , ##__VA_ARGS__)
+#define COMPACT_GOOGLE_LOG_0 COMPACT_GOOGLE_LOG_ERROR
// We use the preprocessor's merging operator, "##", so that, e.g.,
// LOG(INFO) becomes the token COMPACT_GOOGLE_LOG_INFO. There's some funny
@@ -235,6 +259,26 @@ const LogSeverity LOG_DFATAL_LEVEL = LOG_FATAL;
#define SYSLOG_ASSERT(condition) \
SYSLOG_IF(FATAL, !(condition)) << "Assert failed: " #condition ". "
+#if defined(OS_WIN)
+#define LOG_GETLASTERROR(severity) \
+ COMPACT_GOOGLE_LOG_EX_ ## severity(Win32ErrorLogMessage, \
+ ::logging::GetLastSystemErrorCode()).stream()
+#define LOG_GETLASTERROR_MODULE(severity, module) \
+ COMPACT_GOOGLE_LOG_EX_ ## severity(Win32ErrorLogMessage, \
+ ::logging::GetLastSystemErrorCode(), module).stream()
+// PLOG is the usual error logging macro for each platform.
+#define PLOG(severity) LOG_GETLASTERROR(severity)
+#define DPLOG(severity) DLOG_GETLASTERROR(severity)
+#elif defined(OS_POSIX)
+#define LOG_ERRNO(severity) \
+ COMPACT_GOOGLE_LOG_EX_ ## severity(ErrnoLogMessage, \
+ ::logging::GetLastSystemErrorCode()).stream()
+// PLOG is the usual error logging macro for each platform.
+#define PLOG(severity) LOG_ERRNO(severity)
+#define DPLOG(severity) DLOG_ERRNO(severity)
+// TODO(tschmelcher): Should we add OSStatus logging for Mac?
+#endif
+
// CHECK dies with a fatal error if condition is not true. It is *not*
// controlled by NDEBUG, so the check will be executed regardless of
// compilation mode.
@@ -301,6 +345,17 @@ std::string* MakeCheckOpString(const int& v1,
#define DLOG_ASSERT(condition) \
true ? (void) 0 : LOG_ASSERT(condition)
+#if defined(OS_WIN)
+#define DLOG_GETLASTERROR(severity) \
+ true ? (void) 0 : logging::LogMessageVoidify() & LOG_GETLASTERROR(severity)
+#define DLOG_GETLASTERROR_MODULE(severity, module) \
+ true ? (void) 0 : logging::LogMessageVoidify() & \
+ LOG_GETLASTERROR_MODULE(severity, module)
+#elif defined(OS_POSIX)
+#define DLOG_ERRNO(severity) \
+ true ? (void) 0 : logging::LogMessageVoidify() & LOG_ERRNO(severity)
+#endif
+
enum { DEBUG_MODE = 0 };
// This macro can be followed by a sequence of stream parameters in
@@ -354,6 +409,14 @@ enum { DEBUG_MODE = 0 };
#define DLOG_IF(severity, condition) LOG_IF(severity, condition)
#define DLOG_ASSERT(condition) LOG_ASSERT(condition)
+#if defined(OS_WIN)
+#define DLOG_GETLASTERROR(severity) LOG_GETLASTERROR(severity)
+#define DLOG_GETLASTERROR_MODULE(severity, module) \
+ LOG_GETLASTERROR_MODULE(severity, module)
+#elif defined(OS_POSIX)
+#define DLOG_ERRNO(severity) LOG_ERRNO(severity)
+#endif
+
// debug-only checking. not executed in NDEBUG mode.
enum { DEBUG_MODE = 1 };
#define DCHECK(condition) \
@@ -413,6 +476,17 @@ DECLARE_DCHECK_STROP_IMPL(_stricmp, false)
#define DLOG_ASSERT(condition) \
true ? (void) 0 : LOG_ASSERT(condition)
+#if defined(OS_WIN)
+#define DLOG_GETLASTERROR(severity) \
+ true ? (void) 0 : logging::LogMessageVoidify() & LOG_GETLASTERROR(severity)
+#define DLOG_GETLASTERROR_MODULE(severity, module) \
+ true ? (void) 0 : logging::LogMessageVoidify() & \
+ LOG_GETLASTERROR_MODULE(severity, module)
+#elif defined(OS_POSIX)
+#define DLOG_ERRNO(severity) \
+ true ? (void) 0 : logging::LogMessageVoidify() & LOG_ERRNO(severity)
+#endif
+
enum { DEBUG_MODE = 0 };
// This macro can be followed by a sequence of stream parameters in
@@ -597,6 +671,66 @@ class LogMessageVoidify {
void operator&(std::ostream&) { }
};
+#if defined(OS_WIN)
+typedef unsigned long SystemErrorCode;
+#elif defined(OS_POSIX)
+typedef int SystemErrorCode;
+#endif
+
+// Alias for ::GetLastError() on Windows and errno on POSIX. Avoids having to
+// pull in windows.h just for GetLastError() and DWORD.
+SystemErrorCode GetLastSystemErrorCode();
+
+#if defined(OS_WIN)
+// Appends a formatted system message of the GetLastError() type.
+class Win32ErrorLogMessage {
+ public:
+ Win32ErrorLogMessage(const char* file,
+ int line,
+ LogSeverity severity,
+ SystemErrorCode err,
+ const char* module);
+
+ Win32ErrorLogMessage(const char* file,
+ int line,
+ LogSeverity severity,
+ SystemErrorCode err);
+
+ std::ostream& stream() { return log_message_.stream(); }
+
+ // Appends the error message before destructing the encapsulated class.
+ ~Win32ErrorLogMessage();
+
+ private:
+ SystemErrorCode err_;
+ // Optional name of the module defining the error.
+ const char* module_;
+ LogMessage log_message_;
+
+ DISALLOW_COPY_AND_ASSIGN(Win32ErrorLogMessage);
+};
+#elif defined(OS_POSIX)
+// Appends a formatted system message of the errno type
+class ErrnoLogMessage {
+ public:
+ ErrnoLogMessage(const char* file,
+ int line,
+ LogSeverity severity,
+ SystemErrorCode err);
+
+ std::ostream& stream() { return log_message_.stream(); }
+
+ // Appends the error message before destructing the encapsulated class.
+ ~ErrnoLogMessage();
+
+ private:
+ SystemErrorCode err_;
+ LogMessage log_message_;
+
+ DISALLOW_COPY_AND_ASSIGN(ErrnoLogMessage);
+};
+#endif // OS_WIN
+
// Closes the log file explicitly if open.
// NOTE: Since the log file is opened as necessary by the action of logging
// statements, there's no guarantee that it will stay closed