Merge pull request #158 from sergiud/zero-allocation
authorShinichiro Hamaji <shinichiro.hamaji@gmail.com>
Thu, 6 Jul 2017 05:58:37 +0000 (14:58 +0900)
committerGitHub <noreply@github.com>
Thu, 6 Jul 2017 05:58:37 +0000 (14:58 +0900)
[RFC] reduce heap memory allocations to zero

1  2 
CMakeLists.txt
src/glog/logging.h.in
src/logging.cc
src/logging_unittest.cc

diff --combined CMakeLists.txt
@@@ -14,7 -14,7 +14,7 @@@ enable_testing (
  
  set (GLOG_MAJOR_VERSION 0)
  set (GLOG_MINOR_VERSION 3)
 -set (GLOG_PATCH_VERSION 4)
 +set (GLOG_PATCH_VERSION 5)
  
  set (GLOG_VERSION
    ${GLOG_MAJOR_VERSION}.${GLOG_MINOR_VERSION}.${GLOG_PATCH_VERSION})
@@@ -28,9 -28,12 +28,10 @@@ set (CPACK_PACKAGE_VERSION ${GLOG_VERSI
  
  option (WITH_GFLAGS "Use gflags" ON)
  option (WITH_THREADS "Enable multithreading support" ON)
+ option (WITH_TLS "Enable Thread Local Storage (TLS) support" ON)
  
  list (APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
  
 -include (CMakePackageConfigHelpers)
 -include (CPack)
  include (CheckCSourceCompiles)
  include (CheckCXXCompilerFlag)
  include (CheckCXXSourceCompiles)
@@@ -41,15 -44,12 +42,15 @@@ include (CheckLibraryExists
  include (CheckStructHasMember)
  include (CheckSymbolExists)
  include (CheckTypeSize)
 +include (CMakePackageConfigHelpers)
 +include (CPack)
 +include (CTest)
  include (DetermineGflagsNamespace)
  
  set (CMAKE_THREAD_PREFER_PTHREAD 1)
  
  if (WITH_GFLAGS)
 -  find_package (gflags)
 +  find_package (gflags 2.2.0)
  
    if (gflags_FOUND)
      set (HAVE_LIB_GFLAGS 1)
@@@ -102,14 -102,8 +103,14 @@@ check_function_exists (pwrite HAVE_PWRI
  check_function_exists (sigaction HAVE_SIGACTION)
  check_function_exists (sigaltstack HAVE_SIGALSTACK)
  
 -check_cxx_compiler_flag (-Wno-deprecated HAVE_NO_DEPRECATED)
 -check_cxx_compiler_flag (-Wno-unnamed-type-template-args
 +# NOTE gcc does not fail if you pass a non-existent -Wno-* option as an
 +# argument. However, it will happily fail if you pass the corresponding -W*
 +# option. So, we check whether options that disable warnings exist by testing
 +# the availability of the corresponding option that enables the warning. This
 +# eliminates the need to check for compiler for several (mainly Clang) options.
 +
 +check_cxx_compiler_flag (-Wdeprecated HAVE_NO_DEPRECATED)
 +check_cxx_compiler_flag (-Wunnamed-type-template-args
      HAVE_NO_UNNAMED_TYPE_TEMPLATE_ARGS)
  
  # NOTE: Cannot use check_function_exists here since >=vc-14.0 can define
@@@ -190,6 -184,31 +191,31 @@@ using namespace Outer::Inner;
  int main() { return i; }
  " HAVE_NAMESPACES)
  
+ check_cxx_source_compiles ("
+ __declspec(thread) int tls;
+ int main() { }
+ " HAVE_MSVC_TLS)
+ check_cxx_source_compiles ("
+ thread_local int tls;
+ int main() { }
+ " HAVE_CXX11_TLS)
+ check_cxx_source_compiles ("
+ __attribute__((thread)) int tls;
+ int main() { }
+ " HAVE_CYGWIN_TLS)
+ if (WITH_TLS)
+   if (HAVE_CYGWIN_TLS)
+     set (GLOG_THREAD_LOCAL_STORAGE "__attribute__((thread))")
+   elseif (HAVE_MSVC_TLS)
+     set (GLOG_THREAD_LOCAL_STORAGE "__declspec(thread)")
+   elseif (HAVE_CXX11_TLS)
+     set (GLOG_THREAD_LOCAL_STORAGE thread_local)
+   endif (HAVE_CYGWIN_TLS)
+ endif (WITH_TLS)
  set (_PC_FIELDS
    "gregs[REG_PC]"
    "gregs[REG_EIP]"
@@@ -364,9 -383,9 +390,9 @@@ set (GLOG_SRC
    src/vlog_is_on.cc
  )
  
 -if (HAVE_PTHREAD)
 +if (HAVE_PTHREAD OR WIN32)
    list (APPEND GLOG_SRCS src/signalhandler.cc)
 -endif (HAVE_PTHREAD)
 +endif (HAVE_PTHREAD OR WIN32)
  
  if (WIN32)
    list (APPEND GLOG_SRCS
    )
  endif (WIN32)
  
 +add_compile_options ($<$<BOOL:${HAVE_NO_UNNAMED_TYPE_TEMPLATE_ARGS}>:-Wno-unnamed-type-template-args>)
 +
  add_library (glog
    ${GLOG_SRCS}
  )
  
 +set_target_properties (glog PROPERTIES POSITION_INDEPENDENT_CODE ON)
 +
  if (UNWIND_LIBRARY)
    target_link_libraries (glog PUBLIC ${UNWIND_LIBRARY})
  endif (UNWIND_LIBRARY)
@@@ -396,8 -411,13 +422,8 @@@ if (WIN32 AND HAVE_SNPRINTF
      HAVE_SNPRINTF)
  endif (WIN32 AND HAVE_SNPRINTF)
  
 -if (HAVE_NO_UNNAMED_TYPE_TEMPLATE_ARGS)
 -  target_compile_options (glog PUBLIC -Wno-unnamed-type-template-args)
 -endif (HAVE_NO_UNNAMED_TYPE_TEMPLATE_ARGS)
 -
  if (gflags_FOUND)
 -  target_include_directories (glog PUBLIC ${gflags_INCLUDE_DIR})
 -  target_link_libraries (glog PUBLIC ${gflags_LIBRARIES})
 +  target_link_libraries (glog PUBLIC gflags)
  
    if (NOT BUILD_SHARED_LIBS)
      # Don't use __declspec(dllexport|dllimport) if this is a static build
@@@ -454,108 -474,101 +480,108 @@@ if (HAVE_EXECINFO_H
    set (HAVE_STACKTRACE 1)
  endif (HAVE_EXECINFO_H)
  
 +if (WIN32)
 +  set (HAVE_STACKTRACE 1)
 +  set (HAVE_SYMBOLIZE 1)
 +endif (WIN32)
 +
  if (UNIX OR (APPLE AND HAVE_DLADDR))
    set (HAVE_SYMBOLIZE 1)
  endif (UNIX OR (APPLE AND HAVE_DLADDR))
  
  # Unit testing
  
 -add_executable (logging_unittest
 -  src/logging_unittest.cc
 -)
 -
 -target_link_libraries (logging_unittest PRIVATE glog)
 -
 -add_executable (stl_logging_unittest
 -  src/stl_logging_unittest.cc
 -)
 +if (BUILD_TESTING)
 +  add_executable (logging_unittest
 +    src/logging_unittest.cc
 +  )
  
 -target_link_libraries (stl_logging_unittest PRIVATE glog)
 +  target_link_libraries (logging_unittest PRIVATE glog)
  
 -if (HAVE_NO_DEPRECATED)
 -  set_property (TARGET stl_logging_unittest APPEND PROPERTY COMPILE_OPTIONS
 -    -Wno-deprecated)
 -endif (HAVE_NO_DEPRECATED)
 +  add_executable (stl_logging_unittest
 +    src/stl_logging_unittest.cc
 +  )
  
 -if (HAVE_UNORDERED_MAP AND HAVE_UNORDERED_SET)
 -  target_compile_definitions (stl_logging_unittest PRIVATE
 -    GLOG_STL_LOGGING_FOR_UNORDERED)
 -endif (HAVE_UNORDERED_MAP AND HAVE_UNORDERED_SET)
 +  target_link_libraries (stl_logging_unittest PRIVATE glog)
  
 -if (HAVE_TR1_UNORDERED_MAP AND HAVE_TR1_UNORDERED_SET)
 -  target_compile_definitions (stl_logging_unittest PRIVATE
 -    GLOG_STL_LOGGING_FOR_TR1_UNORDERED)
 -endif (HAVE_TR1_UNORDERED_MAP AND HAVE_TR1_UNORDERED_SET)
 +  if (HAVE_NO_DEPRECATED)
 +    set_property (TARGET stl_logging_unittest APPEND PROPERTY COMPILE_OPTIONS
 +      -Wno-deprecated)
 +  endif (HAVE_NO_DEPRECATED)
  
 -if (HAVE_EXT_HASH_MAP AND HAVE_EXT_HASH_SET)
 -  target_compile_definitions (stl_logging_unittest PRIVATE
 -    GLOG_STL_LOGGING_FOR_EXT_HASH)
 -endif (HAVE_EXT_HASH_MAP AND HAVE_EXT_HASH_SET)
 +  if (HAVE_UNORDERED_MAP AND HAVE_UNORDERED_SET)
 +    target_compile_definitions (stl_logging_unittest PRIVATE
 +      GLOG_STL_LOGGING_FOR_UNORDERED)
 +  endif (HAVE_UNORDERED_MAP AND HAVE_UNORDERED_SET)
  
 -if (HAVE_EXT_SLIST)
 -  target_compile_definitions (stl_logging_unittest PRIVATE
 -    GLOG_STL_LOGGING_FOR_EXT_SLIST)
 -endif (HAVE_EXT_SLIST)
 +  if (HAVE_TR1_UNORDERED_MAP AND HAVE_TR1_UNORDERED_SET)
 +    target_compile_definitions (stl_logging_unittest PRIVATE
 +      GLOG_STL_LOGGING_FOR_TR1_UNORDERED)
 +  endif (HAVE_TR1_UNORDERED_MAP AND HAVE_TR1_UNORDERED_SET)
  
 -if (HAVE_SYMBOLIZE)
 -  add_executable (symbolize_unittest
 -    src/symbolize_unittest.cc
 -  )
 +  if (HAVE_EXT_HASH_MAP AND HAVE_EXT_HASH_SET)
 +    target_compile_definitions (stl_logging_unittest PRIVATE
 +      GLOG_STL_LOGGING_FOR_EXT_HASH)
 +  endif (HAVE_EXT_HASH_MAP AND HAVE_EXT_HASH_SET)
  
 -  target_link_libraries (symbolize_unittest PRIVATE glog)
 -endif (HAVE_SYMBOLIZE)
 +  if (HAVE_EXT_SLIST)
 +    target_compile_definitions (stl_logging_unittest PRIVATE
 +      GLOG_STL_LOGGING_FOR_EXT_SLIST)
 +  endif (HAVE_EXT_SLIST)
  
 -add_executable (demangle_unittest
 -  src/demangle_unittest.cc
 -)
 +  if (HAVE_SYMBOLIZE)
 +    add_executable (symbolize_unittest
 +      src/symbolize_unittest.cc
 +    )
  
 -target_link_libraries (demangle_unittest PRIVATE glog)
 +    target_link_libraries (symbolize_unittest PRIVATE glog)
 +  endif (HAVE_SYMBOLIZE)
  
 -if (HAVE_STACKTRACE)
 -  add_executable (stacktrace_unittest
 -    src/stacktrace_unittest.cc
 +  add_executable (demangle_unittest
 +    src/demangle_unittest.cc
    )
  
 -  target_link_libraries (stacktrace_unittest PRIVATE glog)
 -endif (HAVE_STACKTRACE)
 +  target_link_libraries (demangle_unittest PRIVATE glog)
  
 -add_executable (utilities_unittest
 -  src/utilities_unittest.cc
 -)
 +  if (HAVE_STACKTRACE)
 +    add_executable (stacktrace_unittest
 +      src/stacktrace_unittest.cc
 +    )
  
 -target_link_libraries (utilities_unittest PRIVATE glog)
 +    target_link_libraries (stacktrace_unittest PRIVATE glog)
 +  endif (HAVE_STACKTRACE)
  
 -if (HAVE_STACKTRACE AND HAVE_SYMBOLIZE)
 -  add_executable (signalhandler_unittest
 -    src/signalhandler_unittest.cc
 +  add_executable (utilities_unittest
 +    src/utilities_unittest.cc
    )
  
 -  target_link_libraries (signalhandler_unittest PRIVATE glog)
 -endif (HAVE_STACKTRACE AND HAVE_SYMBOLIZE)
 +  target_link_libraries (utilities_unittest PRIVATE glog)
 +
 +  if (HAVE_STACKTRACE AND HAVE_SYMBOLIZE)
 +    add_executable (signalhandler_unittest
 +      src/signalhandler_unittest.cc
 +    )
  
 -add_test (NAME demangle COMMAND demangle_unittest)
 -add_test (NAME logging COMMAND logging_unittest)
 +    target_link_libraries (signalhandler_unittest PRIVATE glog)
 +  endif (HAVE_STACKTRACE AND HAVE_SYMBOLIZE)
  
 -if (TARGET signalhandler_unittest)
 -  add_test (NAME signalhandler COMMAND signalhandler_unittest)
 -endif (TARGET signalhandler_unittest)
 +  add_test (NAME demangle COMMAND demangle_unittest)
 +  add_test (NAME logging COMMAND logging_unittest)
  
 -if (TARGET stacktrace_unittest)
 -  add_test (NAME stacktrace COMMAND stacktrace_unittest)
 -endif (TARGET stacktrace_unittest)
 +  if (TARGET signalhandler_unittest)
 +    add_test (NAME signalhandler COMMAND signalhandler_unittest)
 +  endif (TARGET signalhandler_unittest)
  
 -add_test (NAME stl_logging COMMAND stl_logging_unittest)
 +  if (TARGET stacktrace_unittest)
 +    add_test (NAME stacktrace COMMAND stacktrace_unittest)
 +  endif (TARGET stacktrace_unittest)
  
 -if (TARGET symbolize_unittest)
 -  add_test (NAME symbolize COMMAND symbolize_unittest)
 -endif (TARGET symbolize_unittest)
 +  add_test (NAME stl_logging COMMAND stl_logging_unittest)
 +
 +  if (TARGET symbolize_unittest)
 +    add_test (NAME symbolize COMMAND symbolize_unittest)
 +  endif (TARGET symbolize_unittest)
 +endif (BUILD_TESTING)
  
  install (TARGETS glog
    EXPORT glog-targets
    LIBRARY DESTINATION lib
    ARCHIVE DESTINATION lib)
  
 -# Build tree config
 -
 -set (glog_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src)
 -set (glog_PACKAGE_DEPS)
 -
  if (gflags_FOUND)
 -  set (glog_PACKAGE_DEPS
 -"
 -include (CMakeFindDependencyMacro)
 -
 -find_dependency (gflags ${gflags_VERSION})
 -")
 +  set (gflags_DEPENDENCY "find_dependency (gflags ${gflags_VERSION})")
  endif (gflags_FOUND)
  
  configure_package_config_file (glog-config.cmake.in
 -  ${CMAKE_CURRENT_BINARY_DIR}/glog-config.cmake INSTALL_DESTINATION
 -  lib/cmake/glog PATH_VARS glog_INCLUDE_DIR
 +  ${CMAKE_CURRENT_BINARY_DIR}/glog-config.cmake
 +  INSTALL_DESTINATION lib/cmake/glog
    NO_CHECK_REQUIRED_COMPONENTS_MACRO)
  
 -# The version file is the same both for build tree and install mode config
  write_basic_package_version_file (glog-config-version.cmake VERSION
    ${GLOG_VERSION} COMPATIBILITY SameMajorVersion)
  
 -# Install config
 -
 -set (glog_INCLUDE_DIR include)
 -
 -configure_package_config_file (glog-config.cmake.in
 -  ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/glog-config.cmake
 -  INSTALL_DESTINATION lib/cmake/glog PATH_VARS glog_INCLUDE_DIR
 -  NO_CHECK_REQUIRED_COMPONENTS_MACRO)
 -
 -export (TARGETS glog FILE glog-targets.cmake)
 +export (TARGETS glog NAMESPACE glog:: FILE glog-targets.cmake)
  export (PACKAGE glog)
  
  install (FILES
 -  ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/glog-config.cmake
 +  ${CMAKE_CURRENT_BINARY_DIR}/glog-config.cmake
    ${CMAKE_CURRENT_BINARY_DIR}/glog-config-version.cmake
    DESTINATION lib/cmake/glog)
  
 -install (EXPORT glog-targets DESTINATION lib/cmake/glog)
 +install (EXPORT glog-targets NAMESPACE glog:: DESTINATION lib/cmake/glog)
diff --combined src/glog/logging.h.in
@@@ -431,15 -431,9 +431,15 @@@ DECLARE_bool(stop_logging_if_full_disk)
  #define LOG_TO_STRING_FATAL(message) @ac_google_namespace@::NullStreamFatal()
  #endif
  
 +#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
 +#define DCHECK_IS_ON() 0
 +#else
 +#define DCHECK_IS_ON() 1
 +#endif
 +
  // For DFATAL, we want to use LogMessage (as opposed to
  // LogMessageFatal), to be consistent with the original behavior.
 -#ifdef NDEBUG
 +#if !DCHECK_IS_ON()
  #define COMPACT_GOOGLE_LOG_DFATAL COMPACT_GOOGLE_LOG_ERROR
  #elif GOOGLE_STRIP_LOG <= 3
  #define COMPACT_GOOGLE_LOG_DFATAL @ac_google_namespace@::LogMessage( \
@@@ -578,7 -572,7 +578,7 @@@ class LogSink;  // defined belo
    SYSLOG_IF(FATAL, !(condition)) << "Assert failed: " #condition
  
  // 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
 +// controlled by DCHECK_IS_ON(), so the check will be executed regardless of
  // compilation mode.  Therefore, it is safe to do things like:
  //    CHECK(fp->Write(x) == 4)
  #define CHECK(condition)  \
@@@ -728,7 -722,7 +728,7 @@@ DEFINE_CHECK_OP_IMPL(Check_GT, > 
  #if defined(STATIC_ANALYSIS)
  // Only for static analysis tool to know that it is equivalent to assert
  #define CHECK_OP_LOG(name, op, val1, val2, log) CHECK((val1) op (val2))
 -#elif !defined(NDEBUG)
 +#elif DCHECK_IS_ON()
  // In debug mode, avoid constructing CheckOpStrings if possible,
  // to reduce the overhead of CHECK statments by 2x.
  // Real DCHECK-heavy tests have seen 1.5x speedups.
@@@ -757,7 -751,7 +757,7 @@@ typedef std::string _Check_string
               @ac_google_namespace@::GetReferenceableValue(val2),        \
               #val1 " " #op " " #val2))                                  \
      log(__FILE__, __LINE__, _result).stream()
 -#endif  // STATIC_ANALYSIS, !NDEBUG
 +#endif  // STATIC_ANALYSIS, DCHECK_IS_ON()
  
  #if GOOGLE_STRIP_LOG <= 3
  #define CHECK_OP(name, op, val1, val2) \
@@@ -982,7 -976,7 +982,7 @@@ const LogSeverity GLOG_0 = GLOG_ERROR
  
  // Plus some debug-logging macros that get compiled to nothing for production
  
 -#ifndef NDEBUG
 +#if DCHECK_IS_ON()
  
  #define DLOG(severity) LOG(severity)
  #define DVLOG(verboselevel) VLOG(verboselevel)
    LOG_IF_EVERY_N(severity, condition, n)
  #define DLOG_ASSERT(condition) LOG_ASSERT(condition)
  
 -// debug-only checking.  not executed in NDEBUG mode.
 +// debug-only checking.  executed if DCHECK_IS_ON().
  #define DCHECK(condition) CHECK(condition)
  #define DCHECK_EQ(val1, val2) CHECK_EQ(val1, val2)
  #define DCHECK_NE(val1, val2) CHECK_NE(val1, val2)
  #define DCHECK_STRNE(str1, str2) CHECK_STRNE(str1, str2)
  #define DCHECK_STRCASENE(str1, str2) CHECK_STRCASENE(str1, str2)
  
 -#else  // NDEBUG
 +#else  // !DCHECK_IS_ON()
  
  #define DLOG(severity) \
    true ? (void) 0 : @ac_google_namespace@::LogMessageVoidify() & LOG(severity)
    while (false) \
      GLOG_MSVC_POP_WARNING() CHECK_STRCASENE(str1, str2)
  
 -#endif  // NDEBUG
 +#endif  // DCHECK_IS_ON()
  
  // Log only in verbose mode.
  
@@@ -1113,6 -1107,12 +1113,12 @@@ class GOOGLE_GLOG_DLL_DECL LogStreamBu
    LogStreamBuf(char *buf, int len) {
      setp(buf, buf + len - 2);
    }
+   // Resets the buffer. Useful if we reuse it by means of TLS.
+   void reset() {
+      setp(pbase(), epptr());
+   }
    // This effectively ignores overflow.
    virtual int_type overflow(int_type ch) {
      return ch;
@@@ -1175,6 -1175,7 +1181,7 @@@ public
      size_t pcount() const { return streambuf_.pcount(); }
      char* pbase() const { return streambuf_.pbase(); }
      char* str() const { return pbase(); }
+     void reset() { streambuf_.reset(); }
  
    private:
      LogStream(const LogStream&);
@@@ -1309,35 -1310,6 +1316,35 @@@ inline void LogAtLevel(int const severi
  // LOG macros, 2. this macro can be used as C++ stream.
  #define LOG_AT_LEVEL(severity) @ac_google_namespace@::LogMessage(__FILE__, __LINE__, severity).stream()
  
 +// Check if it's compiled in C++11 mode.
 +//
 +// GXX_EXPERIMENTAL_CXX0X is defined by gcc and clang up to at least
 +// gcc-4.7 and clang-3.1 (2011-12-13).  __cplusplus was defined to 1
 +// in gcc before 4.7 (Crosstool 16) and clang before 3.1, but is
 +// defined according to the language version in effect thereafter.
 +// Microsoft Visual Studio 14 (2015) sets __cplusplus==199711 despite
 +// reasonably good C++11 support, so we set LANG_CXX for it and
 +// newer versions (_MSC_VER >= 1900).
 +#if (defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L || \
 +     (defined(_MSC_VER) && _MSC_VER >= 1900))
 +// Helper for CHECK_NOTNULL().
 +//
 +// In C++11, all cases can be handled by a single function. Since the value
 +// category of the argument is preserved (also for rvalue references),
 +// member initializer lists like the one below will compile correctly:
 +//
 +//   Foo()
 +//     : x_(CHECK_NOTNULL(MethodReturningUniquePtr())) {}
 +template <typename T>
 +T CheckNotNull(const char* file, int line, const char* names, T&& t) {
 + if (t == nullptr) {
 +   LogMessageFatal(file, line, new std::string(names));
 + }
 + return std::forward<T>(t);
 +}
 +
 +#else
 +
  // A small helper for CHECK_NOTNULL().
  template <typename T>
  T* CheckNotNull(const char *file, int line, const char *names, T* t) {
    }
    return t;
  }
 +#endif
  
  // Allow folks to put a counter in the LOG_EVERY_X()'ed messages. This
  // only works if ostream is a LogStream. If the ostream is not a
diff --combined src/logging.cc
@@@ -114,6 -114,11 +114,6 @@@ GLOG_DEFINE_bool(drop_log_memory, true
                   "Logs can grow very quickly and they are rarely read before they "
                   "need to be evicted from memory. Instead, drop them from memory "
                   "as soon as they are flushed to disk.");
 -_START_GOOGLE_NAMESPACE_
 -namespace logging {
 -static const int64 kPageSize = getpagesize();
 -}
 -_END_GOOGLE_NAMESPACE_
  #endif
  
  // By default, errors (including fatal errors) get logged to stderr as
@@@ -331,6 -336,7 +331,7 @@@ const size_t LogMessage::kMaxLogMessage
  
  struct LogMessage::LogMessageData  {
    LogMessageData();
+   void reset();
  
    int preserved_errno_;      // preserved errno
    // Buffer space; contains complete message text.
@@@ -433,7 -439,6 +434,7 @@@ class LogFileObject : public base::Logg
    FILE* file_;
    LogSeverity severity_;
    uint32 bytes_since_flush_;
 +  uint32 dropped_mem_length_;
    uint32 file_length_;
    unsigned int rollover_attempt_;
    int64 next_flush_time_;         // cycle count at which to flush log
@@@ -821,7 -826,6 +822,7 @@@ void LogDestination::DeleteLogDestinati
    }
    MutexLock l(&sink_mutex_);
    delete sinks_;
 +  sinks_ = NULL;
  }
  
  namespace {
@@@ -835,7 -839,6 +836,7 @@@ LogFileObject::LogFileObject(LogSeverit
      file_(NULL),
      severity_(severity),
      bytes_since_flush_(0),
 +    dropped_mem_length_(0),
      file_length_(0),
      rollover_attempt_(kRolloverAttemptFrequency-1),
      next_flush_time_(0) {
@@@ -973,7 -976,7 +974,7 @@@ void LogFileObject::Write(bool force_fl
        PidHasChanged()) {
      if (file_ != NULL) fclose(file_);
      file_ = NULL;
 -    file_length_ = bytes_since_flush_ = 0;
 +    file_length_ = bytes_since_flush_ = dropped_mem_length_ = 0;
      rollover_attempt_ = kRolloverAttemptFrequency-1;
    }
  
         (CycleClock_Now() >= next_flush_time_) ) {
      FlushUnlocked();
  #ifdef OS_LINUX
 -    if (FLAGS_drop_log_memory) {
 -      if (file_length_ >= logging::kPageSize) {
 -        // don't evict the most recent page
 -        uint32 len = file_length_ & ~(logging::kPageSize - 1);
 -        posix_fadvise(fileno(file_), 0, len, POSIX_FADV_DONTNEED);
 +    // Only consider files >= 3MiB
 +    if (FLAGS_drop_log_memory && file_length_ >= (3 << 20)) {
 +      // Don't evict the most recent 1-2MiB so as not to impact a tailer
 +      // of the log file and to avoid page rounding issue on linux < 4.7
 +      uint32 total_drop_length = (file_length_ & ~((1 << 20) - 1)) - (1 << 20);
 +      uint32 this_drop_length = total_drop_length - dropped_mem_length_;
 +      if (this_drop_length >= (2 << 20)) {
 +        // Only advise when >= 2MiB to drop
 +        posix_fadvise(fileno(file_), dropped_mem_length_, this_drop_length,
 +                      POSIX_FADV_DONTNEED);
 +        dropped_mem_length_ = total_drop_length;
        }
      }
  #endif
@@@ -1145,10 -1142,22 +1146,22 @@@ static bool fatal_msg_exclusive = true
  static LogMessage::LogMessageData fatal_msg_data_exclusive;
  static LogMessage::LogMessageData fatal_msg_data_shared;
  
+ #ifdef GLOG_THREAD_LOCAL_STORAGE
+ // Static thread-local log data space to use, because typically at most one
+ // LogMessageData object exists (in this case glog makes zero heap memory
+ // allocations).
+ static GLOG_THREAD_LOCAL_STORAGE bool thread_data_available = true;
+ static GLOG_THREAD_LOCAL_STORAGE LogMessage::LogMessageData thread_msg_data;
+ #endif // defined(GLOG_THREAD_LOCAL_STORAGE)
  LogMessage::LogMessageData::LogMessageData()
    : stream_(message_text_, LogMessage::kMaxLogMessageLen, 0) {
  }
  
+ void LogMessage::LogMessageData::reset() {
+     stream_.reset();
+ }
  LogMessage::LogMessage(const char* file, int line, LogSeverity severity,
                         int ctr, void (LogMessage::*send_method)())
      : allocated_(NULL) {
@@@ -1201,8 -1210,22 +1214,22 @@@ void LogMessage::Init(const char* file
                        void (LogMessage::*send_method)()) {
    allocated_ = NULL;
    if (severity != GLOG_FATAL || !exit_on_dfatal) {
+ #ifdef GLOG_THREAD_LOCAL_STORAGE
+     // No need for locking, because this is thread local.
+     if (thread_data_available) {
+       thread_data_available = false;
+       data_ = &thread_msg_data;
+       // Make sure to clear log data since it may have been used and filled with
+       // data. We do not want to append the new message to the previous one.
+       data_->reset();
+     } else {
+       allocated_ = new LogMessageData();
+       data_ = allocated_;
+     }
+ #else // !defined(GLOG_THREAD_LOCAL_STORAGE)
      allocated_ = new LogMessageData();
      data_ = allocated_;
+ #endif // defined(GLOG_THREAD_LOCAL_STORAGE)
      data_->first_fatal_ = false;
    } else {
      MutexLock l(&fatal_msg_lock);
  
  LogMessage::~LogMessage() {
    Flush();
+ #ifdef GLOG_THREAD_LOCAL_STORAGE
+   if (data_ == &thread_msg_data)
+     thread_data_available = true;
+ #endif // defined(GLOG_THREAD_LOCAL_STORAGE)
    delete allocated_;
  }
  
@@@ -1466,13 -1493,16 +1497,13 @@@ void LogMessage::RecordCrashReason
  # define ATTRIBUTE_NORETURN
  #endif
  
 +#if defined(OS_WINDOWS)
 +__declspec(noreturn)
 +#endif
  static void logging_fail() ATTRIBUTE_NORETURN;
  
  static void logging_fail() {
 -#if defined(_DEBUG) && defined(_MSC_VER)
 -  // When debugging on windows, avoid the obnoxious dialog and make
 -  // it possible to continue past a LOG(FATAL) in the debugger
 -  __debugbreak();
 -#else
    abort();
 -#endif
  }
  
  typedef void (*logging_fail_func_t)() ATTRIBUTE_NORETURN;
diff --combined src/logging_unittest.cc
@@@ -272,17 -272,19 +272,19 @@@ void TestLogging(bool check_counts) 
    LOG(ERROR) << string("foo") << ' '<< j << ' ' << setw(10) << j << " "
               << setw(1) << hex << j;
  
+   LOG(ERROR) << (&LOG(ERROR) && 0) << " nested LOG";
    LogMessage("foo", LogMessage::kNoLogPrefix, GLOG_INFO).stream() << "no prefix";
  
    if (check_counts) {
      CHECK_EQ(base_num_infos   + 14, LogMessage::num_messages(GLOG_INFO));
      CHECK_EQ(base_num_warning + 3,  LogMessage::num_messages(GLOG_WARNING));
-     CHECK_EQ(base_num_errors  + 15, LogMessage::num_messages(GLOG_ERROR));
+     CHECK_EQ(base_num_errors  + 17, LogMessage::num_messages(GLOG_ERROR));
    }
  }
  
  static void NoAllocNewHook() {
 -  CHECK(false) << "unexpected new";
 +  LOG(FATAL) << "unexpected new";
  }
  
  struct NewHook {