diff options
author | tschmelcher@google.com <tschmelcher@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-10-06 22:14:38 +0000 |
---|---|---|
committer | tschmelcher@google.com <tschmelcher@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-10-06 22:14:38 +0000 |
commit | 4b4afc9a78dcf44a42cda4ff509299e164acae54 (patch) | |
tree | 3e4971485e1abb8230a66e1ce77b85068a9b9311 | |
parent | 82592da309a05caa5b1be3c83d4a32b739b3cfb9 (diff) | |
download | chromium_src-4b4afc9a78dcf44a42cda4ff509299e164acae54.zip chromium_src-4b4afc9a78dcf44a42cda4ff509299e164acae54.tar.gz chromium_src-4b4afc9a78dcf44a42cda4ff509299e164acae54.tar.bz2 |
Add logging macros that automatically append the last system error in string form.
TESTED=Linux: tested PLOG and DPLOG with both a valid error and invalid error on a dbg build with both the default strerror_r implementation (GNU) and the other one (POSIX) via some throw-away macro evilness, and also tested the default strerror_r again on an opt build to verify DPLOG is a no-op; Windows: tested PLOG and DPLOG with both a valid error and invalid error on a dbg build; also tested LOG_GETLASTERROR_MODULE with winhttp and ERROR_WINHTTP_CANNOT_CONNECT and verified that it prints the correct system message and that it doesn't with PLOG; also tested LOG_GETLASTERROR_MODULE with a bogus module name and verified that it prints an error that it can't find the module, and the original error; Mac: none (implicitly tested via the Linux POSIX tests)
Review URL: http://codereview.chromium.org/255093
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@28166 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | base/logging.cc | 195 | ||||
-rw-r--r-- | base/logging.h | 150 |
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 |