Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5ea3279ad1 |
@ -1,8 +1,8 @@
|
|||||||
---
|
---
|
||||||
tasks:
|
tasks:
|
||||||
ubuntu1804:
|
ubuntu1804:
|
||||||
name: "Ubuntu 22.04"
|
name: "Ubuntu 18.04"
|
||||||
platform: ubuntu2204
|
platform: ubuntu1804
|
||||||
build_flags:
|
build_flags:
|
||||||
- "--features=layering_check"
|
- "--features=layering_check"
|
||||||
- "--copt=-Werror"
|
- "--copt=-Werror"
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
Checks: 'clang-diagnostic-*,clang-analyzer-*,google-*,modernize-*,-modernize-use-trailing-return-type,readability-*,portability-*,performance-*,bugprone-*,android-*,darwin-*,clang-analyzer-*'
|
Checks: 'clang-diagnostic-*,clang-analyzer-*,google-*,modernize-*,-modernize-use-trailing-return-type,readability-*,portability-*,performance-*,bugprone-*,android-*,darwin-*,clang-analyzer-*'
|
||||||
WarningsAsErrors: ''
|
WarningsAsErrors: ''
|
||||||
HeaderFilterRegex: ''
|
HeaderFilterRegex: ''
|
||||||
|
AnalyzeTemporaryDtors: false
|
||||||
FormatStyle: file
|
FormatStyle: file
|
||||||
CheckOptions:
|
CheckOptions:
|
||||||
- key: cert-dcl16-c.NewSuffixes
|
- key: cert-dcl16-c.NewSuffixes
|
||||||
|
|||||||
32
.github/workflows/linux.yml
vendored
32
.github/workflows/linux.yml
vendored
@ -31,20 +31,42 @@ jobs:
|
|||||||
- name: Setup Dependencies
|
- name: Setup Dependencies
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
DEBIAN_FRONTEND=noninteractive sudo apt-get install -y --no-install-suggests --no-install-recommends \
|
DEBIAN_FRONTEND=noninteractive sudo apt-get install -y \
|
||||||
g++ \
|
build-essential \
|
||||||
cmake \
|
cmake \
|
||||||
gcovr \
|
gcovr \
|
||||||
libgflags-dev \
|
libgflags-dev \
|
||||||
libgmock-dev \
|
|
||||||
libgtest-dev \
|
|
||||||
libunwind-dev \
|
libunwind-dev \
|
||||||
ninja-build
|
ninja-build
|
||||||
|
|
||||||
|
- name: Cache GTest
|
||||||
|
id: cache-gtest
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: gtest/
|
||||||
|
key: ${{runner.os}}-gtest-1.11
|
||||||
|
|
||||||
|
- name: Download GTest
|
||||||
|
if: steps.cache-gtest.outputs.cache-hit != 'true'
|
||||||
|
run: |
|
||||||
|
wget https://github.com/google/googletest/archive/refs/tags/release-1.11.0.tar.gz
|
||||||
|
tar xvf release-1.11.0.tar.gz
|
||||||
|
|
||||||
|
- name: Build GTest
|
||||||
|
if: steps.cache-gtest.outputs.cache-hit != 'true'
|
||||||
|
run: |
|
||||||
|
cmake -S googletest-release-1.11.0 -B build-googletest \
|
||||||
|
-DBUILD_SHARED_LIBS=${{matrix.shared}} \
|
||||||
|
-DCMAKE_BUILD_TYPE=${{matrix.build_type}} \
|
||||||
|
-DCMAKE_INSTALL_PREFIX=${{github.workspace}}/gtest \
|
||||||
|
-G Ninja
|
||||||
|
cmake --build build-googletest --target install
|
||||||
|
|
||||||
- name: Setup Environment
|
- name: Setup Environment
|
||||||
if: matrix.build_type == 'Debug'
|
if: matrix.build_type == 'Debug'
|
||||||
run: |
|
run: |
|
||||||
echo 'CXXFLAGS=--coverage' >> $GITHUB_ENV
|
echo 'CXXFLAGS=--coverage' >> $GITHUB_ENV
|
||||||
|
echo 'GTest_ROOT=${{github.workspace}}/gtest' >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Configure
|
- name: Configure
|
||||||
env:
|
env:
|
||||||
@ -109,7 +131,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Upload Coverage to Codecov
|
- name: Upload Coverage to Codecov
|
||||||
if: matrix.build_type == 'Debug'
|
if: matrix.build_type == 'Debug'
|
||||||
uses: codecov/codecov-action@v5
|
uses: codecov/codecov-action@v3
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
files: build_${{matrix.build_type}}/coverage.xml
|
files: build_${{matrix.build_type}}/coverage.xml
|
||||||
|
|||||||
2
.github/workflows/macos.yml
vendored
2
.github/workflows/macos.yml
vendored
@ -68,7 +68,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Upload Coverage to Codecov
|
- name: Upload Coverage to Codecov
|
||||||
if: matrix.build_type == 'Debug'
|
if: matrix.build_type == 'Debug'
|
||||||
uses: codecov/codecov-action@v5
|
uses: codecov/codecov-action@v3
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
files: build_${{matrix.build_type}}/coverage.xml
|
files: build_${{matrix.build_type}}/coverage.xml
|
||||||
|
|||||||
16
.github/workflows/windows.yml
vendored
16
.github/workflows/windows.yml
vendored
@ -42,21 +42,21 @@ jobs:
|
|||||||
|
|
||||||
- name: Cache GTest
|
- name: Cache GTest
|
||||||
id: cache-gtest
|
id: cache-gtest
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v3
|
||||||
with:
|
with:
|
||||||
path: gtest/
|
path: gtest/
|
||||||
key: ${{runner.os}}-gtest-1.14-${{matrix.lib}}-${{matrix.arch}}-${{matrix.build_type}}
|
key: ${{runner.os}}-gtest-1.11-${{matrix.lib}}-${{matrix.arch}}-${{matrix.build_type}}
|
||||||
|
|
||||||
- name: Download GTest
|
- name: Download GTest
|
||||||
if: steps.cache-gtest.outputs.cache-hit != 'true'
|
if: steps.cache-gtest.outputs.cache-hit != 'true'
|
||||||
run: |
|
run: |
|
||||||
(New-Object System.Net.WebClient).DownloadFile("https://github.com/google/googletest/archive/refs/tags/v1.14.0.zip", "v1.14.0.zip")
|
(New-Object System.Net.WebClient).DownloadFile("https://github.com/google/googletest/archive/refs/tags/release-1.11.0.zip", "release-1.11.0.zip")
|
||||||
Expand-Archive v1.14.0.zip .
|
Expand-Archive release-1.11.0.zip .
|
||||||
|
|
||||||
- name: Build GTest
|
- name: Build GTest
|
||||||
if: steps.cache-gtest.outputs.cache-hit != 'true'
|
if: steps.cache-gtest.outputs.cache-hit != 'true'
|
||||||
run: |
|
run: |
|
||||||
cmake -S googletest-1.14.0 -B build-googletest `
|
cmake -S googletest-release-1.11.0 -B build-googletest `
|
||||||
-A ${{matrix.arch}} `
|
-A ${{matrix.arch}} `
|
||||||
-DBUILD_SHARED_LIBS=${{matrix.lib == 'shared'}} `
|
-DBUILD_SHARED_LIBS=${{matrix.lib == 'shared'}} `
|
||||||
-Dgtest_force_shared_crt=ON `
|
-Dgtest_force_shared_crt=ON `
|
||||||
@ -67,7 +67,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Cache gflags
|
- name: Cache gflags
|
||||||
id: cache-gflags
|
id: cache-gflags
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v3
|
||||||
with:
|
with:
|
||||||
path: gflags/
|
path: gflags/
|
||||||
key: ${{runner.os}}-gflags-2.2.2-${{matrix.lib}}-${{matrix.arch}}-${{matrix.build_type}}
|
key: ${{runner.os}}-gflags-2.2.2-${{matrix.lib}}-${{matrix.arch}}-${{matrix.build_type}}
|
||||||
@ -188,7 +188,7 @@ jobs:
|
|||||||
- name: Setup Coverage Dependencies
|
- name: Setup Coverage Dependencies
|
||||||
if: matrix.build_type == 'Debug'
|
if: matrix.build_type == 'Debug'
|
||||||
run: |
|
run: |
|
||||||
pip install 'gcovr==7.0'
|
pip install 'gcovr==6.0'
|
||||||
|
|
||||||
- name: Setup Environment
|
- name: Setup Environment
|
||||||
if: matrix.build_type == 'Debug'
|
if: matrix.build_type == 'Debug'
|
||||||
@ -234,7 +234,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Upload Coverage to Codecov
|
- name: Upload Coverage to Codecov
|
||||||
if: matrix.build_type == 'Debug'
|
if: matrix.build_type == 'Debug'
|
||||||
uses: codecov/codecov-action@v5
|
uses: codecov/codecov-action@v3
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
files: build_${{matrix.build_type}}/coverage.xml
|
files: build_${{matrix.build_type}}/coverage.xml
|
||||||
|
|||||||
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,6 +1,3 @@
|
|||||||
*.orig
|
*.orig
|
||||||
/build*/
|
/build*/
|
||||||
/site/
|
|
||||||
bazel-*
|
bazel-*
|
||||||
# Bzlmod lockfile
|
|
||||||
/MODULE.bazel.lock
|
|
||||||
|
|||||||
@ -17,6 +17,6 @@ platform(
|
|||||||
constraint_values = [
|
constraint_values = [
|
||||||
"@platforms//cpu:x86_64",
|
"@platforms//cpu:x86_64",
|
||||||
"@platforms//os:windows",
|
"@platforms//os:windows",
|
||||||
"@rules_cc//cc/private/toolchain:clang-cl",
|
"@bazel_tools//tools/cpp:clang-cl",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
cmake_minimum_required (VERSION 3.22)
|
cmake_minimum_required (VERSION 3.22)
|
||||||
project (glog
|
project (glog
|
||||||
VERSION 0.8.0
|
VERSION 0.7.0
|
||||||
DESCRIPTION "C++ implementation of the Google logging module"
|
DESCRIPTION "C++ implementation of the Google logging module"
|
||||||
HOMEPAGE_URL https://github.com/google/glog
|
HOMEPAGE_URL https://github.com/google/glog
|
||||||
LANGUAGES CXX
|
LANGUAGES CXX
|
||||||
@ -18,6 +18,7 @@ list (APPEND CMAKE_MODULE_PATH ${glog_SOURCE_DIR}/cmake)
|
|||||||
include (CheckCXXSourceCompiles)
|
include (CheckCXXSourceCompiles)
|
||||||
include (CheckCXXSourceRuns)
|
include (CheckCXXSourceRuns)
|
||||||
include (CheckCXXSymbolExists)
|
include (CheckCXXSymbolExists)
|
||||||
|
include (CheckFunctionExists)
|
||||||
include (CheckIncludeFileCXX)
|
include (CheckIncludeFileCXX)
|
||||||
include (CheckStructHasMember)
|
include (CheckStructHasMember)
|
||||||
include (CheckTypeSize)
|
include (CheckTypeSize)
|
||||||
@ -32,7 +33,6 @@ include (GetCacheVariables)
|
|||||||
include (GNUInstallDirs)
|
include (GNUInstallDirs)
|
||||||
|
|
||||||
option (BUILD_SHARED_LIBS "Build shared libraries" ON)
|
option (BUILD_SHARED_LIBS "Build shared libraries" ON)
|
||||||
option (BUILD_EXAMPLES "Build examples" ON)
|
|
||||||
option (PRINT_UNSYMBOLIZED_STACK_TRACES
|
option (PRINT_UNSYMBOLIZED_STACK_TRACES
|
||||||
"Print file offsets in traces instead of symbolizing" OFF)
|
"Print file offsets in traces instead of symbolizing" OFF)
|
||||||
option (WITH_GFLAGS "Use gflags" ON)
|
option (WITH_GFLAGS "Use gflags" ON)
|
||||||
@ -64,7 +64,7 @@ set (CMAKE_VISIBILITY_INLINES_HIDDEN ON)
|
|||||||
|
|
||||||
set (CMAKE_DEBUG_POSTFIX d)
|
set (CMAKE_DEBUG_POSTFIX d)
|
||||||
|
|
||||||
find_package (GTest 1.11 COMPONENTS GTest OPTIONAL_COMPONENTS GMock NO_MODULE)
|
find_package (GTest NO_MODULE)
|
||||||
|
|
||||||
if (GTest_FOUND)
|
if (GTest_FOUND)
|
||||||
set (HAVE_LIB_GTEST 1)
|
set (HAVE_LIB_GTEST 1)
|
||||||
@ -126,11 +126,8 @@ if (Unwind_FOUND)
|
|||||||
endif (Unwind_FOUND)
|
endif (Unwind_FOUND)
|
||||||
|
|
||||||
check_include_file_cxx (dlfcn.h HAVE_DLFCN_H)
|
check_include_file_cxx (dlfcn.h HAVE_DLFCN_H)
|
||||||
check_include_file_cxx (elf.h HAVE_ELF_H)
|
|
||||||
check_include_file_cxx (glob.h HAVE_GLOB_H)
|
check_include_file_cxx (glob.h HAVE_GLOB_H)
|
||||||
check_include_file_cxx (link.h HAVE_LINK_H)
|
|
||||||
check_include_file_cxx (pwd.h HAVE_PWD_H)
|
check_include_file_cxx (pwd.h HAVE_PWD_H)
|
||||||
check_include_file_cxx (sys/exec_elf.h HAVE_SYS_EXEC_ELF_H)
|
|
||||||
check_include_file_cxx (sys/syscall.h HAVE_SYS_SYSCALL_H)
|
check_include_file_cxx (sys/syscall.h HAVE_SYS_SYSCALL_H)
|
||||||
check_include_file_cxx (sys/time.h HAVE_SYS_TIME_H)
|
check_include_file_cxx (sys/time.h HAVE_SYS_TIME_H)
|
||||||
check_include_file_cxx (sys/types.h HAVE_SYS_TYPES_H)
|
check_include_file_cxx (sys/types.h HAVE_SYS_TYPES_H)
|
||||||
@ -144,13 +141,12 @@ check_include_file_cxx (unistd.h HAVE_UNISTD_H)
|
|||||||
check_type_size (mode_t HAVE_MODE_T LANGUAGE CXX)
|
check_type_size (mode_t HAVE_MODE_T LANGUAGE CXX)
|
||||||
check_type_size (ssize_t HAVE_SSIZE_T LANGUAGE CXX)
|
check_type_size (ssize_t HAVE_SSIZE_T LANGUAGE CXX)
|
||||||
|
|
||||||
check_cxx_symbol_exists (dladdr dlfcn.h HAVE_DLADDR)
|
check_function_exists (dladdr HAVE_DLADDR)
|
||||||
check_cxx_symbol_exists (fcntl fcntl.h HAVE_FCNTL)
|
check_function_exists (fcntl HAVE_FCNTL)
|
||||||
check_cxx_symbol_exists (posix_fadvise fcntl.h HAVE_POSIX_FADVISE)
|
check_function_exists (pread HAVE_PREAD)
|
||||||
check_cxx_symbol_exists (pread unistd.h HAVE_PREAD)
|
check_function_exists (pwrite HAVE_PWRITE)
|
||||||
check_cxx_symbol_exists (pwrite unistd.h HAVE_PWRITE)
|
check_function_exists (sigaction HAVE_SIGACTION)
|
||||||
check_cxx_symbol_exists (sigaction csignal HAVE_SIGACTION)
|
check_function_exists (sigaltstack HAVE_SIGALTSTACK)
|
||||||
check_cxx_symbol_exists (sigaltstack csignal HAVE_SIGALTSTACK)
|
|
||||||
|
|
||||||
check_cxx_symbol_exists (backtrace execinfo.h HAVE_EXECINFO_BACKTRACE)
|
check_cxx_symbol_exists (backtrace execinfo.h HAVE_EXECINFO_BACKTRACE)
|
||||||
check_cxx_symbol_exists (backtrace_symbols execinfo.h
|
check_cxx_symbol_exists (backtrace_symbols execinfo.h
|
||||||
@ -168,15 +164,6 @@ if (WITH_FUZZING STREQUAL none)
|
|||||||
check_cxx_symbol_exists (abi::__cxa_demangle cxxabi.h HAVE___CXA_DEMANGLE)
|
check_cxx_symbol_exists (abi::__cxa_demangle cxxabi.h HAVE___CXA_DEMANGLE)
|
||||||
endif (WITH_FUZZING STREQUAL none)
|
endif (WITH_FUZZING STREQUAL none)
|
||||||
|
|
||||||
check_cxx_symbol_exists (__argv cstdlib HAVE___ARGV)
|
|
||||||
check_cxx_symbol_exists (getprogname cstdlib HAVE_GETPROGNAME)
|
|
||||||
check_cxx_symbol_exists (program_invocation_short_name cerrno HAVE_PROGRAM_INVOCATION_SHORT_NAME)
|
|
||||||
check_cxx_source_compiles ([=[
|
|
||||||
#include <cstdlib>
|
|
||||||
extern char* __progname;
|
|
||||||
int main() { return __progname != nullptr ? EXIT_SUCCESS : EXIT_FAILURE; }
|
|
||||||
]=] HAVE___PROGNAME)
|
|
||||||
|
|
||||||
if (WITH_TLS)
|
if (WITH_TLS)
|
||||||
set (GLOG_THREAD_LOCAL_STORAGE 1)
|
set (GLOG_THREAD_LOCAL_STORAGE 1)
|
||||||
endif (WITH_TLS)
|
endif (WITH_TLS)
|
||||||
@ -278,12 +265,12 @@ if (WITH_SYMBOLIZE)
|
|||||||
if (HAVE_SYMBOLIZE)
|
if (HAVE_SYMBOLIZE)
|
||||||
set (HAVE_STACKTRACE 1)
|
set (HAVE_STACKTRACE 1)
|
||||||
endif (HAVE_SYMBOLIZE)
|
endif (HAVE_SYMBOLIZE)
|
||||||
|
elseif (UNIX)
|
||||||
|
cmake_push_check_state (RESET)
|
||||||
|
check_cxx_symbol_exists (__ELF__ "" HAVE_SYMBOLIZE)
|
||||||
|
cmake_pop_check_state ()
|
||||||
elseif (APPLE AND HAVE_DLADDR)
|
elseif (APPLE AND HAVE_DLADDR)
|
||||||
set (HAVE_SYMBOLIZE 1)
|
set (HAVE_SYMBOLIZE 1)
|
||||||
elseif (UNIX)
|
|
||||||
if (HAVE_ELF_H OR HAVE_SYS_EXEC_ELF_H)
|
|
||||||
set (HAVE_SYMBOLIZE 1)
|
|
||||||
endif (HAVE_ELF_H OR HAVE_SYS_EXEC_ELF_H)
|
|
||||||
endif (WIN32 OR CYGWIN)
|
endif (WIN32 OR CYGWIN)
|
||||||
endif (WITH_SYMBOLIZE)
|
endif (WITH_SYMBOLIZE)
|
||||||
|
|
||||||
@ -369,8 +356,6 @@ set (GLOG_SRCS
|
|||||||
src/logging.cc
|
src/logging.cc
|
||||||
src/raw_logging.cc
|
src/raw_logging.cc
|
||||||
src/signalhandler.cc
|
src/signalhandler.cc
|
||||||
src/stacktrace.cc
|
|
||||||
src/stacktrace.h
|
|
||||||
src/symbolize.cc
|
src/symbolize.cc
|
||||||
src/symbolize.h
|
src/symbolize.h
|
||||||
src/utilities.cc
|
src/utilities.cc
|
||||||
@ -467,7 +452,7 @@ if (ANDROID)
|
|||||||
endif (ANDROID)
|
endif (ANDROID)
|
||||||
|
|
||||||
set_target_properties (glog PROPERTIES VERSION ${glog_VERSION})
|
set_target_properties (glog PROPERTIES VERSION ${glog_VERSION})
|
||||||
set_target_properties (glog PROPERTIES SOVERSION 3)
|
set_target_properties (glog PROPERTIES SOVERSION 1)
|
||||||
|
|
||||||
if (CYGWIN OR WIN32)
|
if (CYGWIN OR WIN32)
|
||||||
target_compile_definitions (glog PUBLIC GLOG_NO_ABBREVIATED_SEVERITIES)
|
target_compile_definitions (glog PUBLIC GLOG_NO_ABBREVIATED_SEVERITIES)
|
||||||
@ -961,11 +946,6 @@ if (BUILD_TESTING)
|
|||||||
)
|
)
|
||||||
endif (BUILD_TESTING)
|
endif (BUILD_TESTING)
|
||||||
|
|
||||||
if (BUILD_EXAMPLES)
|
|
||||||
add_executable (custom_sink_example examples/custom_sink.cc)
|
|
||||||
target_link_libraries (custom_sink_example PRIVATE glog::glog)
|
|
||||||
endif (BUILD_EXAMPLES)
|
|
||||||
|
|
||||||
install (TARGETS glog
|
install (TARGETS glog
|
||||||
EXPORT glog-targets
|
EXPORT glog-targets
|
||||||
RUNTIME DESTINATION ${_glog_CMake_BINDIR}
|
RUNTIME DESTINATION ${_glog_CMake_BINDIR}
|
||||||
|
|||||||
@ -1,18 +1,19 @@
|
|||||||
Copyright © 2024, Google Inc.
|
Copyright (c) 2024, Google Inc.
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
modification, are permitted provided that the following conditions are
|
modification, are permitted provided that the following conditions are
|
||||||
met:
|
met:
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright notice, this
|
* Redistributions of source code must retain the above copyright
|
||||||
list of conditions and the following disclaimer.
|
notice, this list of conditions and the following disclaimer.
|
||||||
* Redistributions in binary form must reproduce the above copyright notice, this
|
* Redistributions in binary form must reproduce the above
|
||||||
list of conditions and the following disclaimer in the documentation and/or
|
copyright notice, this list of conditions and the following disclaimer
|
||||||
other materials provided with the distribution.
|
in the documentation and/or other materials provided with the
|
||||||
* Neither the name of Google Inc. nor the names of its contributors may be used
|
distribution.
|
||||||
to endorse or promote products derived from this software without specific
|
* Neither the name of Google Inc. nor the names of its
|
||||||
prior written permission.
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
10
ChangeLog
10
ChangeLog
@ -1,13 +1,3 @@
|
|||||||
2024-06-08 Google Inc. <opensource@google.com>
|
|
||||||
|
|
||||||
* google-glog: version 0.7.1.
|
|
||||||
* See git log for the details.
|
|
||||||
|
|
||||||
2024-02-17 Google Inc. <opensource@google.com>
|
|
||||||
|
|
||||||
* google-glog: version 0.7.0.
|
|
||||||
* See git log for the details.
|
|
||||||
|
|
||||||
2022-04-05 Google Inc. <opensource@google.com>
|
2022-04-05 Google Inc. <opensource@google.com>
|
||||||
|
|
||||||
* google-glog: version 0.6.0.
|
* google-glog: version 0.6.0.
|
||||||
|
|||||||
13
MODULE.bazel
13
MODULE.bazel
@ -1,13 +0,0 @@
|
|||||||
module(
|
|
||||||
name = "glog",
|
|
||||||
compatibility_level = 1,
|
|
||||||
)
|
|
||||||
|
|
||||||
bazel_dep(name = "gflags", version = "2.2.2")
|
|
||||||
bazel_dep(name = "googletest", version = "1.14.0", dev_dependency = True)
|
|
||||||
bazel_dep(name = "platforms", version = "0.0.10")
|
|
||||||
bazel_dep(name = "rules_cc", version = "0.0.12")
|
|
||||||
|
|
||||||
# Required for Windows clang-cl build: --extra_toolchains=@local_config_cc//:cc-toolchain-arm64_windows
|
|
||||||
cc_configure = use_extension("@rules_cc//cc:extensions.bzl", "cc_configure_extension")
|
|
||||||
use_repo(cc_configure, "local_config_cc")
|
|
||||||
874
README.rst
874
README.rst
@ -7,10 +7,882 @@ Google Logging (glog) is a C++14 library that implements application-level
|
|||||||
logging. The library provides logging APIs based on C++-style streams and
|
logging. The library provides logging APIs based on C++-style streams and
|
||||||
various helper macros.
|
various helper macros.
|
||||||
|
|
||||||
|
.. role:: cmake(code)
|
||||||
|
:language: cmake
|
||||||
|
|
||||||
|
.. role:: cmd(code)
|
||||||
|
:language: bash
|
||||||
|
|
||||||
|
.. role:: cpp(code)
|
||||||
|
:language: cpp
|
||||||
|
|
||||||
|
.. role:: bazel(code)
|
||||||
|
:language: starlark
|
||||||
|
|
||||||
|
|
||||||
Getting Started
|
Getting Started
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
Please refer to project's `documentation <https://google.github.io/glog/>`_.
|
You can log a message by simply streaming things to ``LOG``\ (<a
|
||||||
|
particular `severity level <#severity-levels>`__>), e.g.,
|
||||||
|
|
||||||
|
.. code:: cpp
|
||||||
|
|
||||||
|
#include <glog/logging.h>
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
// Initialize Google’s logging library.
|
||||||
|
google::InitGoogleLogging(argv[0]);
|
||||||
|
|
||||||
|
// ...
|
||||||
|
LOG(INFO) << "Found " << num_cookies << " cookies";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
The library can be installed using various package managers or compiled from
|
||||||
|
`source <#building-from-source>`__. For a detailed overview of glog features and
|
||||||
|
their usage, please refer to the `user guide <#user-guide>`__.
|
||||||
|
|
||||||
|
.. pull-quote::
|
||||||
|
[!IMPORTANT]
|
||||||
|
|
||||||
|
The above example requires further Bazel or CMake setup for
|
||||||
|
`use in own projects <#usage-in-projects>`__.
|
||||||
|
|
||||||
|
|
||||||
|
.. contents:: Table of Contents
|
||||||
|
|
||||||
|
|
||||||
|
Usage in Projects
|
||||||
|
~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Assuming that glog was previously `built glog using CMake <#cmake>`__ or
|
||||||
|
installed using a package manager, you can use the CMake command
|
||||||
|
:cmake:`find_package` to build against glog in your CMake project as follows:
|
||||||
|
|
||||||
|
.. code:: cmake
|
||||||
|
|
||||||
|
cmake_minimum_required (VERSION 3.16)
|
||||||
|
project (myproj VERSION 1.0)
|
||||||
|
|
||||||
|
find_package (glog 0.6.0 REQUIRED)
|
||||||
|
|
||||||
|
add_executable (myapp main.cpp)
|
||||||
|
target_link_libraries (myapp glog::glog)
|
||||||
|
|
||||||
|
|
||||||
|
Compile definitions and options will be added automatically to your
|
||||||
|
target as needed.
|
||||||
|
|
||||||
|
Alternatively, glog can be incorporated into using the CMake command
|
||||||
|
:cmake:`add_subdirectory` to include glog directly from a subdirectory of your
|
||||||
|
project by replacing the :cmake:`find_package` call from the previous snippet by
|
||||||
|
:cmake:`add_subdirectory`. The :cmake:`glog::glog` target is in this case an
|
||||||
|
:cmake:`ALIAS` library target for the ``glog`` library target.
|
||||||
|
|
||||||
|
Building from Source
|
||||||
|
~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Bazel
|
||||||
|
^^^^^
|
||||||
|
|
||||||
|
To use glog within a project which uses the
|
||||||
|
`Bazel <https://bazel.build/>`__ build tool, add the following lines to
|
||||||
|
your ``WORKSPACE`` file:
|
||||||
|
|
||||||
|
.. code:: bazel
|
||||||
|
|
||||||
|
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
|
||||||
|
|
||||||
|
http_archive(
|
||||||
|
name = "com_github_gflags_gflags",
|
||||||
|
sha256 = "34af2f15cf7367513b352bdcd2493ab14ce43692d2dcd9dfc499492966c64dcf",
|
||||||
|
strip_prefix = "gflags-2.2.2",
|
||||||
|
urls = ["https://github.com/gflags/gflags/archive/v2.2.2.tar.gz"],
|
||||||
|
)
|
||||||
|
|
||||||
|
http_archive(
|
||||||
|
name = "com_github_google_glog",
|
||||||
|
sha256 = "122fb6b712808ef43fbf80f75c52a21c9760683dae470154f02bddfc61135022",
|
||||||
|
strip_prefix = "glog-0.6.0",
|
||||||
|
urls = ["https://github.com/google/glog/archive/v0.6.0.zip"],
|
||||||
|
)
|
||||||
|
|
||||||
|
You can then add :bazel:`@com_github_google_glog//:glog` to the deps section
|
||||||
|
of a :bazel:`cc_binary` or :bazel:`cc_library` rule, and :code:`#include <glog/logging.h>`
|
||||||
|
to include it in your source code. Here’s a simple example:
|
||||||
|
|
||||||
|
.. code:: bazel
|
||||||
|
|
||||||
|
cc_binary(
|
||||||
|
name = "main",
|
||||||
|
srcs = ["main.cc"],
|
||||||
|
deps = ["@com_github_google_glog//:glog"],
|
||||||
|
)
|
||||||
|
|
||||||
|
CMake
|
||||||
|
^^^^^
|
||||||
|
|
||||||
|
glog can be compiled using `CMake <http://www.cmake.org>`__ on a wide range of
|
||||||
|
platforms. The typical workflow for building glog on a Unix-like system with
|
||||||
|
GNU Make as build tool is as follows:
|
||||||
|
|
||||||
|
1. Clone the repository and change into source directory.
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
git clone https://github.com/google/glog.git
|
||||||
|
cd glog
|
||||||
|
|
||||||
|
2. Run CMake to configure the build tree.
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
cmake -S . -B build -G "Unix Makefiles"
|
||||||
|
|
||||||
|
CMake provides different generators, and by default will pick the most
|
||||||
|
relevant one to your environment. If you need a specific version of Visual
|
||||||
|
Studio, use :cmd:`cmake . -G <generator-name>`, and see :cmd:`cmake --help`
|
||||||
|
for the available generators. Also see :cmd:`-T <toolset-name>`, which can
|
||||||
|
be used to request the native x64 toolchain with :cmd:`-T host=x64`.
|
||||||
|
|
||||||
|
3. Afterwards, generated files can be used to compile the project.
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
cmake --build build
|
||||||
|
|
||||||
|
4. Test the build software (optional).
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
cmake --build build --target test
|
||||||
|
|
||||||
|
5. Install the built files (optional).
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
cmake --build build --target install
|
||||||
|
|
||||||
|
|
||||||
|
Once successfully built, glog can be
|
||||||
|
`integrated into own projects <#usage-in-projects>`__.
|
||||||
|
|
||||||
|
|
||||||
|
conan
|
||||||
|
~~~~~
|
||||||
|
|
||||||
|
You can download and install glog using the `conan
|
||||||
|
<https://conan.io>`__ package manager:
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
pip install conan
|
||||||
|
conan install -r conancenter glog/<glog-version>@
|
||||||
|
|
||||||
|
The glog recipe in conan center is kept up to date by conan center index community
|
||||||
|
contributors. If the version is out of date, please create an
|
||||||
|
issue or pull request on the `conan-center-index
|
||||||
|
<https://github.com/conan-io/conan-center-index>`__ repository.
|
||||||
|
|
||||||
|
vcpkg
|
||||||
|
~~~~~
|
||||||
|
|
||||||
|
You can download and install glog using the `vcpkg
|
||||||
|
<https://github.com/Microsoft/vcpkg>`__ dependency manager:
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
git clone https://github.com/Microsoft/vcpkg.git
|
||||||
|
cd vcpkg
|
||||||
|
./bootstrap-vcpkg.sh
|
||||||
|
./vcpkg integrate install
|
||||||
|
./vcpkg install glog
|
||||||
|
|
||||||
|
The glog port in vcpkg is kept up to date by Microsoft team members and
|
||||||
|
community contributors. If the version is out of date, please create an
|
||||||
|
issue or pull request on the vcpkg repository.
|
||||||
|
|
||||||
|
User Guide
|
||||||
|
----------
|
||||||
|
|
||||||
|
glog defines a series of macros that simplify many common logging tasks.
|
||||||
|
You can log messages by severity level, control logging behavior from
|
||||||
|
the command line, log based on conditionals, abort the program when
|
||||||
|
expected conditions are not met, introduce your own verbose logging
|
||||||
|
levels, customize the prefix attached to log messages, and more.
|
||||||
|
|
||||||
|
Following sections describe the functionality supported by glog. Please note
|
||||||
|
this description may not be complete but limited to the most useful ones. If you
|
||||||
|
want to find less common features, please check header files under `src/glog
|
||||||
|
<src/glog>`__ directory.
|
||||||
|
|
||||||
|
Severity Levels
|
||||||
|
~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
You can specify one of the following severity levels (in increasing
|
||||||
|
order of severity): ``INFO``, ``WARNING``, ``ERROR``, and ``FATAL``.
|
||||||
|
Logging a ``FATAL`` message terminates the program (after the message is
|
||||||
|
logged). Note that messages of a given severity are logged not only in
|
||||||
|
the logfile for that severity, but also in all logfiles of lower
|
||||||
|
severity. E.g., a message of severity ``FATAL`` will be logged to the
|
||||||
|
logfiles of severity ``FATAL``, ``ERROR``, ``WARNING``, and ``INFO``.
|
||||||
|
|
||||||
|
The ``DFATAL`` severity logs a ``FATAL`` error in debug mode (i.e.,
|
||||||
|
there is no ``NDEBUG`` macro defined), but avoids halting the program in
|
||||||
|
production by automatically reducing the severity to ``ERROR``.
|
||||||
|
|
||||||
|
Unless otherwise specified, glog writes to the filename
|
||||||
|
``/tmp/\<program name\>.\<hostname\>.\<user name\>.log.\<severity level\>.\<date\>-\<time\>.\<pid\>``
|
||||||
|
(e.g.,
|
||||||
|
``/tmp/hello_world.example.com.hamaji.log.INFO.20080709-222411.10474``).
|
||||||
|
By default, glog copies the log messages of severity level ``ERROR`` or
|
||||||
|
``FATAL`` to standard error (``stderr``) in addition to log files.
|
||||||
|
|
||||||
|
Setting Flags
|
||||||
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Several flags influence glog’s output behavior. If the `Google gflags library
|
||||||
|
<https://github.com/gflags/gflags>`__ is installed on your machine, the build
|
||||||
|
system will automatically detect and use it, allowing you to pass flags on the
|
||||||
|
command line. For example, if you want to turn the flag :cmd:`--logtostderr` on,
|
||||||
|
you can start your application with the following command line:
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
./your_application --logtostderr=1
|
||||||
|
|
||||||
|
If the Google gflags library isn’t installed, you set flags via
|
||||||
|
environment variables, prefixing the flag name with ``GLOG_``, e.g.,
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
GLOG_logtostderr=1 ./your_application
|
||||||
|
|
||||||
|
The following flags are most commonly used:
|
||||||
|
|
||||||
|
``logtostderr`` (``bool``, default=\ ``false``)
|
||||||
|
Log messages to ``stderr`` instead of logfiles. Note: you can set
|
||||||
|
binary flags to ``true`` by specifying ``1``, ``true``, or ``yes``
|
||||||
|
(case insensitive). Also, you can set binary flags to ``false`` by
|
||||||
|
specifying ``0``, ``false``, or ``no`` (again, case insensitive).
|
||||||
|
|
||||||
|
``stderrthreshold`` (``int``, default=2, which is ``ERROR``)
|
||||||
|
Copy log messages at or above this level to stderr in addition to
|
||||||
|
logfiles. The numbers of severity levels ``INFO``, ``WARNING``,
|
||||||
|
``ERROR``, and ``FATAL`` are 0, 1, 2, and 3, respectively.
|
||||||
|
|
||||||
|
``minloglevel`` (``int``, default=0, which is ``INFO``)
|
||||||
|
Log messages at or above this level. Again, the numbers of severity
|
||||||
|
levels ``INFO``, ``WARNING``, ``ERROR``, and ``FATAL`` are 0, 1, 2,
|
||||||
|
and 3, respectively.
|
||||||
|
|
||||||
|
``log_dir`` (``string``, default="")
|
||||||
|
If specified, logfiles are written into this directory instead of the
|
||||||
|
default logging directory.
|
||||||
|
|
||||||
|
``v`` (``int``, default=0)
|
||||||
|
Show all ``VLOG(m)`` messages for ``m`` less or equal the value of
|
||||||
|
this flag. Overridable by :cmd:`--vmodule`. See `the section about
|
||||||
|
verbose logging <#verbose-logging>`__ for more detail.
|
||||||
|
|
||||||
|
``vmodule`` (``string``, default="")
|
||||||
|
Per-module verbose level. The argument has to contain a
|
||||||
|
comma-separated list of <module name>=<log level>. <module name> is a
|
||||||
|
glob pattern (e.g., ``gfs*`` for all modules whose name starts with
|
||||||
|
"gfs"), matched against the filename base (that is, name ignoring
|
||||||
|
.cc/.h./-inl.h). <log level> overrides any value given by :cmd:`--v`.
|
||||||
|
See also `the section about verbose logging <#verbose-logging>`__.
|
||||||
|
|
||||||
|
There are some other flags defined in logging.cc. Please grep the source
|
||||||
|
code for ``DEFINE_`` to see a complete list of all flags.
|
||||||
|
|
||||||
|
You can also modify flag values in your program by modifying global
|
||||||
|
variables ``FLAGS_*`` . Most settings start working immediately after
|
||||||
|
you update ``FLAGS_*`` . The exceptions are the flags related to
|
||||||
|
destination files. For example, you might want to set ``FLAGS_log_dir``
|
||||||
|
before calling :cpp:`google::InitGoogleLogging` . Here is an example:
|
||||||
|
|
||||||
|
.. code:: cpp
|
||||||
|
|
||||||
|
LOG(INFO) << "file";
|
||||||
|
// Most flags work immediately after updating values.
|
||||||
|
FLAGS_logtostderr = 1;
|
||||||
|
LOG(INFO) << "stderr";
|
||||||
|
FLAGS_logtostderr = 0;
|
||||||
|
// This won’t change the log destination. If you want to set this
|
||||||
|
// value, you should do this before google::InitGoogleLogging .
|
||||||
|
FLAGS_log_dir = "/some/log/directory";
|
||||||
|
LOG(INFO) << "the same file";
|
||||||
|
|
||||||
|
Conditional / Occasional Logging
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Sometimes, you may only want to log a message under certain conditions.
|
||||||
|
You can use the following macros to perform conditional logging:
|
||||||
|
|
||||||
|
.. code:: cpp
|
||||||
|
|
||||||
|
LOG_IF(INFO, num_cookies > 10) << "Got lots of cookies";
|
||||||
|
|
||||||
|
The "Got lots of cookies" message is logged only when the variable
|
||||||
|
``num_cookies`` exceeds 10. If a line of code is executed many times, it
|
||||||
|
may be useful to only log a message at certain intervals. This kind of
|
||||||
|
logging is most useful for informational messages.
|
||||||
|
|
||||||
|
.. code:: cpp
|
||||||
|
|
||||||
|
LOG_EVERY_N(INFO, 10) << "Got the " << google::COUNTER << "th cookie";
|
||||||
|
|
||||||
|
The above line outputs a log messages on the 1st, 11th, 21st, ... times
|
||||||
|
it is executed. Note that the special ``google::COUNTER`` value is used
|
||||||
|
to identify which repetition is happening.
|
||||||
|
|
||||||
|
You can combine conditional and occasional logging with the following
|
||||||
|
macro.
|
||||||
|
|
||||||
|
.. code:: cpp
|
||||||
|
|
||||||
|
LOG_IF_EVERY_N(INFO, (size > 1024), 10) << "Got the " << google::COUNTER
|
||||||
|
<< "th big cookie";
|
||||||
|
|
||||||
|
Instead of outputting a message every nth time, you can also limit the
|
||||||
|
output to the first n occurrences:
|
||||||
|
|
||||||
|
.. code:: cpp
|
||||||
|
|
||||||
|
LOG_FIRST_N(INFO, 20) << "Got the " << google::COUNTER << "th cookie";
|
||||||
|
|
||||||
|
Outputs log messages for the first 20 times it is executed. Again, the
|
||||||
|
``google::COUNTER`` identifier indicates which repetition is happening.
|
||||||
|
|
||||||
|
Other times, it is desired to only log a message periodically based on a time.
|
||||||
|
So for example, to log a message every 10ms:
|
||||||
|
|
||||||
|
.. code:: cpp
|
||||||
|
|
||||||
|
LOG_EVERY_T(INFO, 0.01) << "Got a cookie";
|
||||||
|
|
||||||
|
Or every 2.35s:
|
||||||
|
|
||||||
|
.. code:: cpp
|
||||||
|
|
||||||
|
LOG_EVERY_T(INFO, 2.35) << "Got a cookie";
|
||||||
|
|
||||||
|
Debug Mode Support
|
||||||
|
~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Special "debug mode" logging macros only have an effect in debug mode
|
||||||
|
and are compiled away to nothing for non-debug mode compiles. Use these
|
||||||
|
macros to avoid slowing down your production application due to
|
||||||
|
excessive logging.
|
||||||
|
|
||||||
|
.. code:: cpp
|
||||||
|
|
||||||
|
DLOG(INFO) << "Found cookies";
|
||||||
|
DLOG_IF(INFO, num_cookies > 10) << "Got lots of cookies";
|
||||||
|
DLOG_EVERY_N(INFO, 10) << "Got the " << google::COUNTER << "th cookie";
|
||||||
|
DLOG_FIRST_N(INFO, 10) << "Got the " << google::COUNTER << "th cookie";
|
||||||
|
DLOG_EVERY_T(INFO, 0.01) << "Got a cookie";
|
||||||
|
|
||||||
|
|
||||||
|
``CHECK`` Macros
|
||||||
|
~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
It is a good practice to check expected conditions in your program
|
||||||
|
frequently to detect errors as early as possible. The ``CHECK`` macro
|
||||||
|
provides the ability to abort the application when a condition is not
|
||||||
|
met, similar to the ``assert`` macro defined in the standard C library.
|
||||||
|
|
||||||
|
``CHECK`` aborts the application if a condition is not true. Unlike
|
||||||
|
``assert``, it is \*not\* controlled by ``NDEBUG``, so the check will be
|
||||||
|
executed regardless of compilation mode. Therefore, ``fp->Write(x)`` in
|
||||||
|
the following example is always executed:
|
||||||
|
|
||||||
|
.. code:: cpp
|
||||||
|
|
||||||
|
CHECK(fp->Write(x) == 4) << "Write failed!";
|
||||||
|
|
||||||
|
There are various helper macros for equality/inequality checks -
|
||||||
|
``CHECK_EQ``, ``CHECK_NE``, ``CHECK_LE``, ``CHECK_LT``, ``CHECK_GE``,
|
||||||
|
and ``CHECK_GT``. They compare two values, and log a ``FATAL`` message
|
||||||
|
including the two values when the result is not as expected. The values
|
||||||
|
must have :cpp:`operator<<(ostream, ...)` defined.
|
||||||
|
|
||||||
|
You may append to the error message like so:
|
||||||
|
|
||||||
|
.. code:: cpp
|
||||||
|
|
||||||
|
CHECK_NE(1, 2) << ": The world must be ending!";
|
||||||
|
|
||||||
|
We are very careful to ensure that each argument is evaluated exactly
|
||||||
|
once, and that anything which is legal to pass as a function argument is
|
||||||
|
legal here. In particular, the arguments may be temporary expressions
|
||||||
|
which will end up being destroyed at the end of the apparent statement,
|
||||||
|
for example:
|
||||||
|
|
||||||
|
.. code:: cpp
|
||||||
|
|
||||||
|
CHECK_EQ(string("abc")[1], ’b’);
|
||||||
|
|
||||||
|
The compiler reports an error if one of the arguments is a pointer and the other
|
||||||
|
is :cpp:`nullptr`. To work around this, simply :cpp:`static_cast` :cpp:`nullptr` to
|
||||||
|
the type of the desired pointer.
|
||||||
|
|
||||||
|
.. code:: cpp
|
||||||
|
|
||||||
|
CHECK_EQ(some_ptr, static_cast<SomeType*>(nullptr));
|
||||||
|
|
||||||
|
Better yet, use the ``CHECK_NOTNULL`` macro:
|
||||||
|
|
||||||
|
.. code:: cpp
|
||||||
|
|
||||||
|
CHECK_NOTNULL(some_ptr);
|
||||||
|
some_ptr->DoSomething();
|
||||||
|
|
||||||
|
Since this macro returns the given pointer, this is very useful in
|
||||||
|
constructor initializer lists.
|
||||||
|
|
||||||
|
.. code:: cpp
|
||||||
|
|
||||||
|
struct S {
|
||||||
|
S(Something* ptr) : ptr_(CHECK_NOTNULL(ptr)) {}
|
||||||
|
Something* ptr_;
|
||||||
|
};
|
||||||
|
|
||||||
|
Note that you cannot use this macro as a C++ stream due to this feature.
|
||||||
|
Please use ``CHECK_EQ`` described above to log a custom message before
|
||||||
|
aborting the application.
|
||||||
|
|
||||||
|
If you are comparing C strings (:cpp:`char *`), a handy set of macros performs
|
||||||
|
case sensitive as well as case insensitive comparisons - ``CHECK_STREQ``,
|
||||||
|
``CHECK_STRNE``, ``CHECK_STRCASEEQ``, and ``CHECK_STRCASENE``. The CASE versions
|
||||||
|
are case-insensitive. You can safely pass :cpp:`nullptr` pointers for this macro. They
|
||||||
|
treat :cpp:`nullptr` and any non-:cpp:`nullptr` string as not equal. Two :cpp:`nullptr`\
|
||||||
|
s are equal.
|
||||||
|
|
||||||
|
Note that both arguments may be temporary strings which are destructed
|
||||||
|
at the end of the current "full expression" (e.g.,
|
||||||
|
:cpp:`CHECK_STREQ(Foo().c_str(), Bar().c_str())` where ``Foo`` and ``Bar``
|
||||||
|
return C++’s :cpp:`std::string`).
|
||||||
|
|
||||||
|
The ``CHECK_DOUBLE_EQ`` macro checks the equality of two floating point
|
||||||
|
values, accepting a small error margin. ``CHECK_NEAR`` accepts a third
|
||||||
|
floating point argument, which specifies the acceptable error margin.
|
||||||
|
|
||||||
|
Verbose Logging
|
||||||
|
~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
When you are chasing difficult bugs, thorough log messages are very useful.
|
||||||
|
However, you may want to ignore too verbose messages in usual development. For
|
||||||
|
such verbose logging, glog provides the ``VLOG`` macro, which allows you to
|
||||||
|
define your own numeric logging levels. The :cmd:`--v` command line option
|
||||||
|
controls which verbose messages are logged:
|
||||||
|
|
||||||
|
.. code:: cpp
|
||||||
|
|
||||||
|
VLOG(1) << "I’m printed when you run the program with --v=1 or higher";
|
||||||
|
VLOG(2) << "I’m printed when you run the program with --v=2 or higher";
|
||||||
|
|
||||||
|
With ``VLOG``, the lower the verbose level, the more likely messages are to be
|
||||||
|
logged. For example, if :cmd:`--v==1`, ``VLOG(1)`` will log, but ``VLOG(2)``
|
||||||
|
will not log. This is opposite of the severity level, where ``INFO`` is 0, and
|
||||||
|
``ERROR`` is 2. :cmd:`--minloglevel` of 1 will log ``WARNING`` and above. Though
|
||||||
|
you can specify any integers for both ``VLOG`` macro and :cmd:`--v` flag, the
|
||||||
|
common values for them are small positive integers. For example, if you write
|
||||||
|
``VLOG(0)``, you should specify :cmd:`--v=-1` or lower to silence it. This is
|
||||||
|
less useful since we may not want verbose logs by default in most cases. The
|
||||||
|
``VLOG`` macros always log at the ``INFO`` log level (when they log at all).
|
||||||
|
|
||||||
|
Verbose logging can be controlled from the command line on a per-module
|
||||||
|
basis:
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
--vmodule=mapreduce=2,file=1,gfs*=3 --v=0
|
||||||
|
|
||||||
|
will:
|
||||||
|
|
||||||
|
(a) Print ``VLOG(2)`` and lower messages from mapreduce.{h,cc}
|
||||||
|
(b) Print ``VLOG(1)`` and lower messages from file.{h,cc}
|
||||||
|
(c) Print ``VLOG(3)`` and lower messages from files prefixed with "gfs"
|
||||||
|
(d) Print ``VLOG(0)`` and lower messages from elsewhere
|
||||||
|
|
||||||
|
The wildcarding functionality shown by (c) supports both ’*’ (matches 0
|
||||||
|
or more characters) and ’?’ (matches any single character) wildcards.
|
||||||
|
Please also check the section about `command line flags <#setting-flags>`__.
|
||||||
|
|
||||||
|
There’s also ``VLOG_IS_ON(n)`` "verbose level" condition macro. This
|
||||||
|
macro returns true when the :cmd:`--v` is equal or greater than ``n``. To
|
||||||
|
be used as
|
||||||
|
|
||||||
|
.. code:: cpp
|
||||||
|
|
||||||
|
if (VLOG_IS_ON(2)) {
|
||||||
|
// do some logging preparation and logging
|
||||||
|
// that can’t be accomplished with just VLOG(2) << ...;
|
||||||
|
}
|
||||||
|
|
||||||
|
Verbose level condition macros ``VLOG_IF``, ``VLOG_EVERY_N`` and
|
||||||
|
``VLOG_IF_EVERY_N`` behave analogous to ``LOG_IF``, ``LOG_EVERY_N``,
|
||||||
|
``LOF_IF_EVERY``, but accept a numeric verbosity level as opposed to a
|
||||||
|
severity level.
|
||||||
|
|
||||||
|
.. code:: cpp
|
||||||
|
|
||||||
|
VLOG_IF(1, (size > 1024))
|
||||||
|
<< "I’m printed when size is more than 1024 and when you run the "
|
||||||
|
"program with --v=1 or more";
|
||||||
|
VLOG_EVERY_N(1, 10)
|
||||||
|
<< "I’m printed every 10th occurrence, and when you run the program "
|
||||||
|
"with --v=1 or more. Present occurrence is " << google::COUNTER;
|
||||||
|
VLOG_IF_EVERY_N(1, (size > 1024), 10)
|
||||||
|
<< "I’m printed on every 10th occurrence of case when size is more "
|
||||||
|
" than 1024, when you run the program with --v=1 or more. ";
|
||||||
|
"Present occurrence is " << google::COUNTER;
|
||||||
|
|
||||||
|
|
||||||
|
Custom Log Prefix Format
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
glog supports changing the format of the prefix attached to log messages by
|
||||||
|
receiving a user-provided callback to be used to generate such strings.
|
||||||
|
|
||||||
|
For each log entry, the callback will be invoked with a ``LogMessageInfo``
|
||||||
|
struct containing the severity, filename, line number, thread ID, and time of
|
||||||
|
the event. It will also be given a reference to the output stream, whose
|
||||||
|
contents will be prepended to the actual message in the final log line.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
.. code:: cpp
|
||||||
|
|
||||||
|
/* This function writes a prefix that matches glog's default format.
|
||||||
|
* (The third parameter can be used to receive user-supplied data, and is
|
||||||
|
* nullptr by default.)
|
||||||
|
*/
|
||||||
|
void CustomPrefix(std::ostream &s, const LogMessageInfo &l, void*) {
|
||||||
|
s << l.severity[0]
|
||||||
|
<< setw(4) << 1900 + l.time.year()
|
||||||
|
<< setw(2) << 1 + l.time.month()
|
||||||
|
<< setw(2) << l.time.day()
|
||||||
|
<< ' '
|
||||||
|
<< setw(2) << l.time.hour() << ':'
|
||||||
|
<< setw(2) << l.time.min() << ':'
|
||||||
|
<< setw(2) << l.time.sec() << "."
|
||||||
|
<< setw(6) << l.time.usec()
|
||||||
|
<< ' '
|
||||||
|
<< setfill(' ') << setw(5)
|
||||||
|
<< l.thread_id << setfill('0')
|
||||||
|
<< ' '
|
||||||
|
<< l.filename << ':' << l.line_number << "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
To enable the use of ``CustomPrefix()``, simply give glog a pointer to it
|
||||||
|
during initialization: ``InitGoogleLogging(argv[0], &CustomPrefix);``.
|
||||||
|
|
||||||
|
Optionally, ``InitGoogleLogging()`` takes a third argument of type ``void*``
|
||||||
|
to pass on to the callback function.
|
||||||
|
|
||||||
|
Failure Signal Handler
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The library provides a convenient signal handler that will dump useful
|
||||||
|
information when the program crashes on certain signals such as ``SIGSEGV``. The
|
||||||
|
signal handler can be installed by :cpp:`google::InstallFailureSignalHandler()`.
|
||||||
|
The following is an example of output from the signal handler.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
*** Aborted at 1225095260 (unix time) try "date -d @1225095260" if you are using GNU date ***
|
||||||
|
*** SIGSEGV (@0x0) received by PID 17711 (TID 0x7f893090a6f0) from PID 0; stack trace: ***
|
||||||
|
PC: @ 0x412eb1 TestWaitingLogSink::send()
|
||||||
|
@ 0x7f892fb417d0 (unknown)
|
||||||
|
@ 0x412eb1 TestWaitingLogSink::send()
|
||||||
|
@ 0x7f89304f7f06 google::LogMessage::SendToLog()
|
||||||
|
@ 0x7f89304f35af google::LogMessage::Flush()
|
||||||
|
@ 0x7f89304f3739 google::LogMessage::~LogMessage()
|
||||||
|
@ 0x408cf4 TestLogSinkWaitTillSent()
|
||||||
|
@ 0x4115de main
|
||||||
|
@ 0x7f892f7ef1c4 (unknown)
|
||||||
|
@ 0x4046f9 (unknown)
|
||||||
|
|
||||||
|
By default, the signal handler writes the failure dump to the standard
|
||||||
|
error. You can customize the destination by :cpp:`InstallFailureWriter()`.
|
||||||
|
|
||||||
|
Performance of Messages
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The conditional logging macros provided by glog (e.g., ``CHECK``,
|
||||||
|
``LOG_IF``, ``VLOG``, etc.) are carefully implemented and don’t execute
|
||||||
|
the right hand side expressions when the conditions are false. So, the
|
||||||
|
following check may not sacrifice the performance of your application.
|
||||||
|
|
||||||
|
.. code:: cpp
|
||||||
|
|
||||||
|
CHECK(obj.ok) << obj.CreatePrettyFormattedStringButVerySlow();
|
||||||
|
|
||||||
|
User-defined Failure Function
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
``FATAL`` severity level messages or unsatisfied ``CHECK`` condition
|
||||||
|
terminate your program. You can change the behavior of the termination
|
||||||
|
by :cpp:`InstallFailureFunction`.
|
||||||
|
|
||||||
|
.. code:: cpp
|
||||||
|
|
||||||
|
void YourFailureFunction() {
|
||||||
|
// Reports something...
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
google::InstallFailureFunction(&YourFailureFunction);
|
||||||
|
}
|
||||||
|
|
||||||
|
By default, glog tries to dump stacktrace and makes the program exit
|
||||||
|
with status 1. The stacktrace is produced only when you run the program
|
||||||
|
on an architecture for which glog supports stack tracing (as of
|
||||||
|
September 2008, glog supports stack tracing for x86 and x86_64).
|
||||||
|
|
||||||
|
Raw Logging
|
||||||
|
~~~~~~~~~~~
|
||||||
|
|
||||||
|
The header file ``<glog/raw_logging.h>`` can be used for thread-safe logging,
|
||||||
|
which does not allocate any memory or acquire any locks. Therefore, the macros
|
||||||
|
defined in this header file can be used by low-level memory allocation and
|
||||||
|
synchronization code. Please check `src/glog/raw_logging.h.in
|
||||||
|
<src/glog/raw_logging.h.in>`__ for detail.
|
||||||
|
|
||||||
|
Google Style ``perror()``
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
``PLOG()`` and ``PLOG_IF()`` and ``PCHECK()`` behave exactly like their
|
||||||
|
``LOG*`` and ``CHECK`` equivalents with the addition that they append a
|
||||||
|
description of the current state of errno to their output lines. E.g.
|
||||||
|
|
||||||
|
.. code:: cpp
|
||||||
|
|
||||||
|
PCHECK(write(1, nullptr, 2) >= 0) << "Write nullptr failed";
|
||||||
|
|
||||||
|
This check fails with the following error message.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
F0825 185142 test.cc:22] Check failed: write(1, nullptr, 2) >= 0 Write nullptr failed: Bad address [14]
|
||||||
|
|
||||||
|
Syslog
|
||||||
|
~~~~~~
|
||||||
|
|
||||||
|
``SYSLOG``, ``SYSLOG_IF``, and ``SYSLOG_EVERY_N`` macros are available.
|
||||||
|
These log to syslog in addition to the normal logs. Be aware that
|
||||||
|
logging to syslog can drastically impact performance, especially if
|
||||||
|
syslog is configured for remote logging! Make sure you understand the
|
||||||
|
implications of outputting to syslog before you use these macros. In
|
||||||
|
general, it’s wise to use these macros sparingly.
|
||||||
|
|
||||||
|
Strip Logging Messages
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Strings used in log messages can increase the size of your binary and
|
||||||
|
present a privacy concern. You can therefore instruct glog to remove all
|
||||||
|
strings which fall below a certain severity level by using the
|
||||||
|
``GOOGLE_STRIP_LOG`` macro:
|
||||||
|
|
||||||
|
If your application has code like this:
|
||||||
|
|
||||||
|
.. code:: cpp
|
||||||
|
|
||||||
|
#define GOOGLE_STRIP_LOG 1 // this must go before the #include!
|
||||||
|
#include <glog/logging.h>
|
||||||
|
|
||||||
|
The compiler will remove the log messages whose severities are less than
|
||||||
|
the specified integer value. Since ``VLOG`` logs at the severity level
|
||||||
|
``INFO`` (numeric value ``0``), setting ``GOOGLE_STRIP_LOG`` to 1 or
|
||||||
|
greater removes all log messages associated with ``VLOG``\ s as well as
|
||||||
|
``INFO`` log statements.
|
||||||
|
|
||||||
|
Automatically Remove Old Logs
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
To enable the log cleaner:
|
||||||
|
|
||||||
|
.. code:: cpp
|
||||||
|
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
google::EnableLogCleaner(24h * 3); // keep your logs for 3 days
|
||||||
|
|
||||||
|
|
||||||
|
In C++20 (and later) this can be shortened to:
|
||||||
|
|
||||||
|
.. code:: cpp
|
||||||
|
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
google::EnableLogCleaner(3d); // keep your logs for 3 days
|
||||||
|
|
||||||
|
And then glog will check if there are overdue logs whenever a flush is
|
||||||
|
performed. In this example, any log file from your project whose last
|
||||||
|
modified time is greater than 3 days will be unlink()ed.
|
||||||
|
|
||||||
|
This feature can be disabled at any time (if it has been enabled)
|
||||||
|
|
||||||
|
.. code:: cpp
|
||||||
|
|
||||||
|
google::DisableLogCleaner();
|
||||||
|
|
||||||
|
Notes for Windows Users
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
glog defines a severity level ``ERROR``, which is also defined in
|
||||||
|
``windows.h`` . You can make glog not define ``INFO``, ``WARNING``,
|
||||||
|
``ERROR``, and ``FATAL`` by defining ``GLOG_NO_ABBREVIATED_SEVERITIES``
|
||||||
|
before including ``glog/logging.h`` . Even with this macro, you can
|
||||||
|
still use the iostream like logging facilities:
|
||||||
|
|
||||||
|
.. code:: cpp
|
||||||
|
|
||||||
|
#define GLOG_NO_ABBREVIATED_SEVERITIES
|
||||||
|
#include <windows.h>
|
||||||
|
#include <glog/logging.h>
|
||||||
|
|
||||||
|
// ...
|
||||||
|
|
||||||
|
LOG(ERROR) << "This should work";
|
||||||
|
LOG_IF(ERROR, x > y) << "This should be also OK";
|
||||||
|
|
||||||
|
However, you cannot use ``INFO``, ``WARNING``, ``ERROR``, and ``FATAL``
|
||||||
|
anymore for functions defined in ``glog/logging.h`` .
|
||||||
|
|
||||||
|
.. code:: cpp
|
||||||
|
|
||||||
|
#define GLOG_NO_ABBREVIATED_SEVERITIES
|
||||||
|
#include <windows.h>
|
||||||
|
#include <glog/logging.h>
|
||||||
|
|
||||||
|
// ...
|
||||||
|
|
||||||
|
// This won’t work.
|
||||||
|
// google::FlushLogFiles(google::ERROR);
|
||||||
|
|
||||||
|
// Use this instead.
|
||||||
|
google::FlushLogFiles(google::GLOG_ERROR);
|
||||||
|
|
||||||
|
If you don’t need ``ERROR`` defined by ``windows.h``, there are a couple
|
||||||
|
of more workarounds which sometimes don’t work:
|
||||||
|
|
||||||
|
- ``#define WIN32_LEAN_AND_MEAN`` or ``NOGDI`` **before** you
|
||||||
|
``#include windows.h``.
|
||||||
|
- ``#undef ERROR`` **after** you ``#include windows.h`` .
|
||||||
|
|
||||||
|
See `this
|
||||||
|
issue <http://code.google.com/p/google-glog/issues/detail?id=33>`__ for
|
||||||
|
more detail.
|
||||||
|
|
||||||
|
|
||||||
|
Installation Notes for 64-bit Linux Systems
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The glibc built-in stack-unwinder on 64-bit systems has some problems with glog.
|
||||||
|
(In particular, if you are using :cpp:`InstallFailureSignalHandler()`, the
|
||||||
|
signal may be raised in the middle of malloc, holding some malloc-related locks
|
||||||
|
when they invoke the stack unwinder. The built-in stack unwinder may call malloc
|
||||||
|
recursively, which may require the thread to acquire a lock it already holds:
|
||||||
|
deadlock.)
|
||||||
|
|
||||||
|
For that reason, if you use a 64-bit system and you need
|
||||||
|
:cpp:`InstallFailureSignalHandler()`, we strongly recommend you install
|
||||||
|
``libunwind`` before trying to configure or install google glog.
|
||||||
|
libunwind can be found
|
||||||
|
`here <http://download.savannah.nongnu.org/releases/libunwind/libunwind-snap-070410.tar.gz>`__.
|
||||||
|
|
||||||
|
Even if you already have ``libunwind`` installed, you will probably
|
||||||
|
still need to install from the snapshot to get the latest version.
|
||||||
|
|
||||||
|
Caution: if you install libunwind from the URL above, be aware that you
|
||||||
|
may have trouble if you try to statically link your binary with glog:
|
||||||
|
that is, if you link with ``gcc -static -lgcc_eh ...``. This is because
|
||||||
|
both ``libunwind`` and ``libgcc`` implement the same C++ exception
|
||||||
|
handling APIs, but they implement them differently on some platforms.
|
||||||
|
This is not likely to be a problem on ia64, but may be on x86-64.
|
||||||
|
|
||||||
|
Also, if you link binaries statically, make sure that you add
|
||||||
|
:cmd:`-Wl,--eh-frame-hdr` to your linker options. This is required so that
|
||||||
|
``libunwind`` can find the information generated by the compiler required for
|
||||||
|
stack unwinding.
|
||||||
|
|
||||||
|
Using :cmd:`-static` is rare, though, so unless you know this will affect you it
|
||||||
|
probably won’t.
|
||||||
|
|
||||||
|
If you cannot or do not wish to install libunwind, you can still try to
|
||||||
|
use two kinds of stack-unwinder: 1. glibc built-in stack-unwinder and 2.
|
||||||
|
frame pointer based stack-unwinder.
|
||||||
|
|
||||||
|
1. As we already mentioned, glibc’s unwinder has a deadlock issue.
|
||||||
|
However, if you don’t use :cpp:`InstallFailureSignalHandler()` or you
|
||||||
|
don’t worry about the rare possibilities of deadlocks, you can use
|
||||||
|
this stack-unwinder. If you specify no options and ``libunwind``
|
||||||
|
isn’t detected on your system, the configure script chooses this
|
||||||
|
unwinder by default.
|
||||||
|
|
||||||
|
2. The frame pointer based stack unwinder requires that your
|
||||||
|
application, the glog library, and system libraries like libc, all be
|
||||||
|
compiled with a frame pointer. This is *not* the default for x86-64.
|
||||||
|
|
||||||
|
|
||||||
|
How to Contribute
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
We’d love to accept your patches and contributions to this project.
|
||||||
|
There are a just a few small guidelines you need to follow.
|
||||||
|
|
||||||
|
Contributor License Agreement (CLA)
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Contributions to any Google project must be accompanied by a Contributor
|
||||||
|
License Agreement. This is not a copyright **assignment**, it simply
|
||||||
|
gives Google permission to use and redistribute your contributions as
|
||||||
|
part of the project.
|
||||||
|
|
||||||
|
* If you are an individual writing original source code and you’re sure
|
||||||
|
you own the intellectual property, then you’ll need to sign an
|
||||||
|
`individual
|
||||||
|
CLA <https://developers.google.com/open-source/cla/individual>`__.
|
||||||
|
* If you work for a company that wants to allow you to contribute your
|
||||||
|
work, then you’ll need to sign a `corporate
|
||||||
|
CLA <https://developers.google.com/open-source/cla/corporate>`__.
|
||||||
|
|
||||||
|
You generally only need to submit a CLA once, so if you’ve already
|
||||||
|
submitted one (even if it was for a different project), you probably
|
||||||
|
don’t need to do it again.
|
||||||
|
|
||||||
|
Once your CLA is submitted (or if you already submitted one for another
|
||||||
|
Google project), make a commit adding yourself to the
|
||||||
|
`AUTHORS <./AUTHORS>`__ and `CONTRIBUTORS <./CONTRIBUTORS>`__ files. This
|
||||||
|
commit can be part of your first `pull
|
||||||
|
request <https://help.github.com/articles/creating-a-pull-request>`__.
|
||||||
|
|
||||||
|
Submitting a Patch
|
||||||
|
~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
1. It’s generally best to start by opening a new issue describing the
|
||||||
|
bug or feature you’re intending to fix. Even if you think it’s
|
||||||
|
relatively minor, it’s helpful to know what people are working on.
|
||||||
|
Mention in the initial issue that you are planning to work on that
|
||||||
|
bug or feature so that it can be assigned to you.
|
||||||
|
2. Follow the normal process of
|
||||||
|
`forking <https://help.github.com/articles/fork-a-repo>`__ the
|
||||||
|
project, and setup a new branch to work in. It’s important that each
|
||||||
|
group of changes be done in separate branches in order to ensure that
|
||||||
|
a pull request only includes the commits related to that bug or
|
||||||
|
feature.
|
||||||
|
3. Do your best to have `well-formed commit
|
||||||
|
messages <http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html>`__
|
||||||
|
for each change. This provides consistency throughout the project,
|
||||||
|
and ensures that commit messages are able to be formatted properly by
|
||||||
|
various git tools.
|
||||||
|
4. Finally, push the commits to your fork and submit a `pull
|
||||||
|
request <https://help.github.com/articles/creating-a-pull-request>`__.
|
||||||
|
|
||||||
|
|
||||||
.. |Linux Github actions| image:: https://github.com/google/glog/actions/workflows/linux.yml/badge.svg
|
.. |Linux Github actions| image:: https://github.com/google/glog/actions/workflows/linux.yml/badge.svg
|
||||||
|
|||||||
18
WORKSPACE
Normal file
18
WORKSPACE
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
|
||||||
|
|
||||||
|
http_archive(
|
||||||
|
name = "com_github_gflags_gflags",
|
||||||
|
sha256 = "34af2f15cf7367513b352bdcd2493ab14ce43692d2dcd9dfc499492966c64dcf",
|
||||||
|
strip_prefix = "gflags-2.2.2",
|
||||||
|
urls = [
|
||||||
|
"https://mirror.bazel.build/github.com/gflags/gflags/archive/v2.2.2.tar.gz",
|
||||||
|
"https://github.com/gflags/gflags/archive/v2.2.2.tar.gz",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
http_archive(
|
||||||
|
name = "com_github_google_googletest",
|
||||||
|
sha256 = "258f33ab1a8ee17adf48ec65e821d0ea9eafcbedeff6110f9eaed78472e73dde",
|
||||||
|
strip_prefix = "googletest-15460959cbbfa20e66ef0b5ab497367e47fc0a04",
|
||||||
|
urls = ["https://github.com/google/googletest/archive/15460959cbbfa20e66ef0b5ab497367e47fc0a04.tar.gz"],
|
||||||
|
)
|
||||||
@ -1 +0,0 @@
|
|||||||
# WORKSPACE marker file needed by Bazel
|
|
||||||
@ -4,6 +4,6 @@ cc_test(
|
|||||||
srcs = ["main.cc"],
|
srcs = ["main.cc"],
|
||||||
deps = [
|
deps = [
|
||||||
"//:glog",
|
"//:glog",
|
||||||
"@gflags//:gflags",
|
"@com_github_gflags_gflags//:gflags",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|||||||
@ -48,6 +48,8 @@ def glog_library(with_gflags = 1, **kwargs):
|
|||||||
|
|
||||||
common_copts = [
|
common_copts = [
|
||||||
"-std=c++14",
|
"-std=c++14",
|
||||||
|
"-DGLOG_BAZEL_BUILD",
|
||||||
|
"-DHAVE_STRING_H",
|
||||||
"-I%s/glog_internal" % gendir,
|
"-I%s/glog_internal" % gendir,
|
||||||
] + (["-DGLOG_USE_GFLAGS"] if with_gflags else [])
|
] + (["-DGLOG_USE_GFLAGS"] if with_gflags else [])
|
||||||
|
|
||||||
@ -61,28 +63,23 @@ def glog_library(with_gflags = 1, **kwargs):
|
|||||||
"-DHAVE_SYS_UTSNAME_H",
|
"-DHAVE_SYS_UTSNAME_H",
|
||||||
# For src/utilities.cc.
|
# For src/utilities.cc.
|
||||||
"-DHAVE_SYS_TIME_H",
|
"-DHAVE_SYS_TIME_H",
|
||||||
# NOTE: users could optionally patch -DHAVE_UNWIND off if
|
|
||||||
# stacktrace dumping is not needed
|
|
||||||
"-DHAVE_UNWIND",
|
"-DHAVE_UNWIND",
|
||||||
# Enable dumping stacktrace upon sigaction.
|
# Enable dumping stacktrace upon sigaction.
|
||||||
"-DHAVE_SIGACTION",
|
"-DHAVE_SIGACTION",
|
||||||
# For logging.cc.
|
# For logging.cc.
|
||||||
"-DHAVE_PREAD",
|
"-DHAVE_PREAD",
|
||||||
# -DHAVE_MODE_T prevent repeated typedef mode_t leading
|
|
||||||
# to emcc compilation failure
|
|
||||||
"-DHAVE_MODE_T",
|
|
||||||
"-DHAVE_UNISTD_H",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
linux_or_darwin_copts = wasm_copts + [
|
linux_or_darwin_copts = wasm_copts + [
|
||||||
"-DGLOG_EXPORT=__attribute__((visibility(\\\"default\\\")))",
|
"-DGLOG_EXPORT=__attribute__((visibility(\\\"default\\\")))",
|
||||||
"-DGLOG_NO_EXPORT=__attribute__((visibility(\\\"default\\\")))",
|
"-DGLOG_NO_EXPORT=__attribute__((visibility(\\\"default\\\")))",
|
||||||
"-DHAVE_POSIX_FADVISE",
|
"-DHAVE_MODE_T",
|
||||||
"-DHAVE_SSIZE_T",
|
"-DHAVE_SSIZE_T",
|
||||||
"-DHAVE_SYS_TYPES_H",
|
"-DHAVE_SYS_TYPES_H",
|
||||||
# For src/utilities.cc.
|
# For src/utilities.cc.
|
||||||
"-DHAVE_SYS_SYSCALL_H",
|
"-DHAVE_SYS_SYSCALL_H",
|
||||||
# For src/logging.cc to create symlinks.
|
# For src/logging.cc to create symlinks.
|
||||||
|
"-DHAVE_UNISTD_H",
|
||||||
"-fvisibility-inlines-hidden",
|
"-fvisibility-inlines-hidden",
|
||||||
"-fvisibility=hidden",
|
"-fvisibility=hidden",
|
||||||
]
|
]
|
||||||
@ -90,15 +87,11 @@ def glog_library(with_gflags = 1, **kwargs):
|
|||||||
freebsd_only_copts = [
|
freebsd_only_copts = [
|
||||||
# Enable declaration of _Unwind_Backtrace
|
# Enable declaration of _Unwind_Backtrace
|
||||||
"-D_GNU_SOURCE",
|
"-D_GNU_SOURCE",
|
||||||
"-DHAVE_LINK_H",
|
|
||||||
"-DHAVE_SYMBOLIZE", # Supported by <link.h>
|
|
||||||
]
|
]
|
||||||
|
|
||||||
linux_only_copts = [
|
linux_only_copts = [
|
||||||
# For utilities.h.
|
# For utilities.h.
|
||||||
"-DHAVE_EXECINFO_H",
|
"-DHAVE_EXECINFO_H",
|
||||||
"-DHAVE_LINK_H",
|
|
||||||
"-DHAVE_SYMBOLIZE", # Supported by <link.h>
|
|
||||||
]
|
]
|
||||||
|
|
||||||
darwin_only_copts = [
|
darwin_only_copts = [
|
||||||
@ -109,11 +102,10 @@ def glog_library(with_gflags = 1, **kwargs):
|
|||||||
windows_only_copts = [
|
windows_only_copts = [
|
||||||
# Override -DGLOG_EXPORT= from the cc_library's defines.
|
# Override -DGLOG_EXPORT= from the cc_library's defines.
|
||||||
"-DGLOG_EXPORT=__declspec(dllexport)",
|
"-DGLOG_EXPORT=__declspec(dllexport)",
|
||||||
"-DGLOG_NO_ABBREVIATED_SEVERITIES",
|
|
||||||
"-DGLOG_NO_EXPORT=",
|
"-DGLOG_NO_EXPORT=",
|
||||||
|
"-DGLOG_NO_ABBREVIATED_SEVERITIES",
|
||||||
"-DGLOG_USE_WINDOWS_PORT",
|
"-DGLOG_USE_WINDOWS_PORT",
|
||||||
"-DHAVE__CHSIZE_S",
|
"-DHAVE__CHSIZE_S",
|
||||||
"-DHAVE_DBGHELP",
|
|
||||||
"-I" + src_windows,
|
"-I" + src_windows,
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -123,26 +115,25 @@ def glog_library(with_gflags = 1, **kwargs):
|
|||||||
]
|
]
|
||||||
|
|
||||||
windows_only_srcs = [
|
windows_only_srcs = [
|
||||||
|
"src/glog/log_severity.h",
|
||||||
"src/windows/dirent.h",
|
"src/windows/dirent.h",
|
||||||
"src/windows/port.cc",
|
"src/windows/port.cc",
|
||||||
"src/windows/port.h",
|
"src/windows/port.h",
|
||||||
]
|
]
|
||||||
|
|
||||||
gflags_deps = ["@gflags//:gflags"] if with_gflags else []
|
gflags_deps = ["@com_github_gflags_gflags//:gflags"] if with_gflags else []
|
||||||
|
|
||||||
final_lib_defines = select({
|
final_lib_defines = select({
|
||||||
# GLOG_EXPORT is normally set by export.h, but that's not
|
# GLOG_EXPORT is normally set by export.h, but that's not
|
||||||
# generated for Bazel.
|
# generated for Bazel.
|
||||||
"@bazel_tools//src/conditions:windows": [
|
"@bazel_tools//src/conditions:windows": [
|
||||||
"GLOG_DEPRECATED=__declspec(deprecated)",
|
|
||||||
"GLOG_EXPORT=",
|
"GLOG_EXPORT=",
|
||||||
|
"GLOG_DEPRECATED=__declspec(deprecated)",
|
||||||
"GLOG_NO_ABBREVIATED_SEVERITIES",
|
"GLOG_NO_ABBREVIATED_SEVERITIES",
|
||||||
"GLOG_NO_EXPORT=",
|
|
||||||
],
|
],
|
||||||
"//conditions:default": [
|
"//conditions:default": [
|
||||||
"GLOG_DEPRECATED=__attribute__((deprecated))",
|
"GLOG_DEPRECATED=__attribute__((deprecated))",
|
||||||
"GLOG_EXPORT=__attribute__((visibility(\\\"default\\\")))",
|
"GLOG_EXPORT=__attribute__((visibility(\\\"default\\\")))",
|
||||||
"GLOG_NO_EXPORT=__attribute__((visibility(\\\"default\\\")))",
|
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -180,8 +171,6 @@ def glog_library(with_gflags = 1, **kwargs):
|
|||||||
"src/logging.cc",
|
"src/logging.cc",
|
||||||
"src/raw_logging.cc",
|
"src/raw_logging.cc",
|
||||||
"src/signalhandler.cc",
|
"src/signalhandler.cc",
|
||||||
"src/stacktrace.cc",
|
|
||||||
"src/stacktrace.h",
|
|
||||||
"src/stacktrace_generic-inl.h",
|
"src/stacktrace_generic-inl.h",
|
||||||
"src/stacktrace_libunwind-inl.h",
|
"src/stacktrace_libunwind-inl.h",
|
||||||
"src/stacktrace_powerpc-inl.h",
|
"src/stacktrace_powerpc-inl.h",
|
||||||
@ -191,7 +180,6 @@ def glog_library(with_gflags = 1, **kwargs):
|
|||||||
"src/symbolize.cc",
|
"src/symbolize.cc",
|
||||||
"src/symbolize.h",
|
"src/symbolize.h",
|
||||||
"src/utilities.cc",
|
"src/utilities.cc",
|
||||||
"src/utilities.h",
|
|
||||||
"src/vlog_is_on.cc",
|
"src/vlog_is_on.cc",
|
||||||
] + select({
|
] + select({
|
||||||
"@bazel_tools//src/conditions:windows": windows_only_srcs,
|
"@bazel_tools//src/conditions:windows": windows_only_srcs,
|
||||||
@ -222,10 +210,6 @@ def glog_library(with_gflags = 1, **kwargs):
|
|||||||
"@bazel_tools//src/conditions:windows": [":strip_include_prefix_hack"],
|
"@bazel_tools//src/conditions:windows": [":strip_include_prefix_hack"],
|
||||||
"//conditions:default": [],
|
"//conditions:default": [],
|
||||||
}),
|
}),
|
||||||
linkopts = select({
|
|
||||||
"@bazel_tools//src/conditions:windows": ["dbghelp.lib"],
|
|
||||||
"//conditions:default": [],
|
|
||||||
}),
|
|
||||||
**kwargs
|
**kwargs
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -261,7 +245,7 @@ def glog_library(with_gflags = 1, **kwargs):
|
|||||||
copts = final_lib_copts + test_only_copts,
|
copts = final_lib_copts + test_only_copts,
|
||||||
deps = gflags_deps + [
|
deps = gflags_deps + [
|
||||||
":glog",
|
":glog",
|
||||||
"@googletest//:gtest",
|
"@com_github_google_googletest//:gtest",
|
||||||
],
|
],
|
||||||
**kwargs
|
**kwargs
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,66 +0,0 @@
|
|||||||
# Building from Source
|
|
||||||
|
|
||||||
## Bazel
|
|
||||||
|
|
||||||
To use glog within a project which uses the [Bazel](https://bazel.build/) build
|
|
||||||
tool, add the following lines to your `MODULE.bazel` file:
|
|
||||||
|
|
||||||
``` bazel title="MODULE.bazel"
|
|
||||||
bazel_dep(name = "glog")
|
|
||||||
|
|
||||||
archive_override(
|
|
||||||
module_name = "glog",
|
|
||||||
urls = "https://github.com/google/glog/archive/cc0de6c200375b33d907ee7632eee2f173b33a09.tar.gz",
|
|
||||||
strip_prefix = "glog-cc0de6c200375b33d907ee7632eee2f173b33a09", # Latest commit as of 2024-06-08.
|
|
||||||
integrity = "sha256-rUrv4EBkdc+4Wbhfxp+KoRstlj2Iw842/OpLfDq0ivg=",
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
You can then add `@glog//:glog` to
|
|
||||||
the deps section of a `cc_binary` or
|
|
||||||
`cc_library` rule, and `#!cpp #include <glog/logging.h>` to
|
|
||||||
include it in your source code.
|
|
||||||
|
|
||||||
!!! example "Using glog in a Bazel project"
|
|
||||||
``` bazel
|
|
||||||
cc_binary(
|
|
||||||
name = "main",
|
|
||||||
srcs = ["main.cc"],
|
|
||||||
deps = ["@glog//:glog"],
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## CMake
|
|
||||||
|
|
||||||
glog can be compiled using [CMake](http://www.cmake.org) on a wide range of
|
|
||||||
platforms. The typical workflow for building glog on a Unix-like system with GNU
|
|
||||||
Make as build tool is as follows:
|
|
||||||
|
|
||||||
1. Clone the repository and change into source directory.
|
|
||||||
``` bash
|
|
||||||
git clone https://github.com/google/glog.git
|
|
||||||
cd glog
|
|
||||||
```
|
|
||||||
2. Run CMake to configure the build tree.
|
|
||||||
``` bash
|
|
||||||
cmake -S . -B build -G "Unix Makefiles"
|
|
||||||
```
|
|
||||||
CMake provides different generators, and by default will pick the most
|
|
||||||
relevant one to your environment. If you need a specific version of Visual
|
|
||||||
Studio, use `#!bash cmake . -G <generator-name>`, and see `#!bash cmake
|
|
||||||
--help` for the available generators. Also see `-T <toolset-name>`, which can
|
|
||||||
be used to request the native x64 toolchain with `-T host=x64`.
|
|
||||||
3. Afterwards, generated files can be used to compile the project.
|
|
||||||
``` bash
|
|
||||||
cmake --build build
|
|
||||||
```
|
|
||||||
4. Test the build software (optional).
|
|
||||||
``` bash
|
|
||||||
cmake --build build --target test
|
|
||||||
```
|
|
||||||
5. Install the built files (optional).
|
|
||||||
``` bash
|
|
||||||
cmake --build build --target install
|
|
||||||
```
|
|
||||||
|
|
||||||
Once successfully built, glog can be [integrated into own projects](usage.md).
|
|
||||||
@ -1,50 +0,0 @@
|
|||||||
# How to Contribute
|
|
||||||
|
|
||||||
We'd love to accept your patches and contributions to this project.
|
|
||||||
There are a just a few small guidelines you need to follow.
|
|
||||||
|
|
||||||
## Contributor License Agreement (CLA)
|
|
||||||
|
|
||||||
Contributions to any Google project must be accompanied by a Contributor
|
|
||||||
License Agreement. This is not a copyright **assignment**, it simply
|
|
||||||
gives Google permission to use and redistribute your contributions as
|
|
||||||
part of the project.
|
|
||||||
|
|
||||||
- If you are an individual writing original source code and you're
|
|
||||||
sure you own the intellectual property, then you'll need to sign an
|
|
||||||
[individual
|
|
||||||
CLA](https://developers.google.com/open-source/cla/individual).
|
|
||||||
- If you work for a company that wants to allow you to contribute your
|
|
||||||
work, then you'll need to sign a [corporate
|
|
||||||
CLA](https://developers.google.com/open-source/cla/corporate).
|
|
||||||
|
|
||||||
You generally only need to submit a CLA once, so if you've already
|
|
||||||
submitted one (even if it was for a different project), you probably
|
|
||||||
don't need to do it again.
|
|
||||||
|
|
||||||
Once your CLA is submitted (or if you already submitted one for another Google
|
|
||||||
project), make a commit adding yourself to the
|
|
||||||
[AUTHORS](https://github.com/google/glog/blob/master/AUTHORS) and
|
|
||||||
[CONTRIBUTORS](https://github.com/google/glog/blob/master/CONTRIBUTORS) files.
|
|
||||||
This commit can be part of your first [pull
|
|
||||||
request](https://help.github.com/articles/creating-a-pull-request).
|
|
||||||
|
|
||||||
## Submitting a Patch
|
|
||||||
|
|
||||||
1. It's generally best to start by opening a new issue describing the
|
|
||||||
bug or feature you're intending to fix. Even if you think it's
|
|
||||||
relatively minor, it's helpful to know what people are working on.
|
|
||||||
Mention in the initial issue that you are planning to work on that
|
|
||||||
bug or feature so that it can be assigned to you.
|
|
||||||
2. Follow the normal process of
|
|
||||||
[forking](https://help.github.com/articles/fork-a-repo) the project,
|
|
||||||
and setup a new branch to work in. It's important that each group of
|
|
||||||
changes be done in separate branches in order to ensure that a pull
|
|
||||||
request only includes the commits related to that bug or feature.
|
|
||||||
3. Do your best to have [well-formed commit
|
|
||||||
messages](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)
|
|
||||||
for each change. This provides consistency throughout the project,
|
|
||||||
and ensures that commit messages are able to be formatted properly
|
|
||||||
by various git tools.
|
|
||||||
4. Finally, push the commits to your fork and submit a [pull
|
|
||||||
request](https://help.github.com/articles/creating-a-pull-request).
|
|
||||||
@ -1,73 +0,0 @@
|
|||||||
# Failure Signal Handler
|
|
||||||
|
|
||||||
## Stacktrace as Default Failure Handler
|
|
||||||
|
|
||||||
The library provides a convenient signal handler that will dump useful
|
|
||||||
information when the program crashes on certain signals such as `SIGSEGV`. The
|
|
||||||
signal handler can be installed by `#!cpp
|
|
||||||
google::InstallFailureSignalHandler()`. The following is an example of output
|
|
||||||
from the signal handler.
|
|
||||||
|
|
||||||
*** Aborted at 1225095260 (unix time) try "date -d @1225095260" if you are using GNU date ***
|
|
||||||
*** SIGSEGV (@0x0) received by PID 17711 (TID 0x7f893090a6f0) from PID 0; stack trace: ***
|
|
||||||
PC: @ 0x412eb1 TestWaitingLogSink::send()
|
|
||||||
@ 0x7f892fb417d0 (unknown)
|
|
||||||
@ 0x412eb1 TestWaitingLogSink::send()
|
|
||||||
@ 0x7f89304f7f06 google::LogMessage::SendToLog()
|
|
||||||
@ 0x7f89304f35af google::LogMessage::Flush()
|
|
||||||
@ 0x7f89304f3739 google::LogMessage::~LogMessage()
|
|
||||||
@ 0x408cf4 TestLogSinkWaitTillSent()
|
|
||||||
@ 0x4115de main
|
|
||||||
@ 0x7f892f7ef1c4 (unknown)
|
|
||||||
@ 0x4046f9 (unknown)
|
|
||||||
|
|
||||||
|
|
||||||
## Customizing Handler Output
|
|
||||||
|
|
||||||
By default, the signal handler writes the failure dump to the standard error.
|
|
||||||
However, it is possible to customize the destination by installing a callback
|
|
||||||
using the `#!cpp google::InstallFailureWriter()` function. The function expects
|
|
||||||
a pointer to a function with the following signature:
|
|
||||||
|
|
||||||
``` cpp
|
|
||||||
void YourFailureWriter(const char* message/* (1)! */, std::size_t length/* (2)! */);
|
|
||||||
```
|
|
||||||
|
|
||||||
1. The pointer references the start of the failure message.
|
|
||||||
|
|
||||||
!!! danger
|
|
||||||
The string is **not null-terminated**.
|
|
||||||
|
|
||||||
2. The message length in characters.
|
|
||||||
|
|
||||||
!!! warning "Possible overflow errors"
|
|
||||||
Users should not expect the `message` string to be null-terminated.
|
|
||||||
|
|
||||||
## User-defined Failure Function
|
|
||||||
|
|
||||||
`FATAL` severity level messages or unsatisfied `CHECK` condition
|
|
||||||
terminate your program. You can change the behavior of the termination
|
|
||||||
by `google::InstallFailureFunction`.
|
|
||||||
|
|
||||||
``` cpp
|
|
||||||
void YourFailureFunction() {
|
|
||||||
// Reports something...
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
|
||||||
google::InstallFailureFunction(&YourFailureFunction);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
By default, glog tries to dump the stacktrace and calls `#!cpp std::abort`. The
|
|
||||||
stacktrace is generated only when running the application on a system
|
|
||||||
supported[^1] by glog.
|
|
||||||
|
|
||||||
[^1]: To extract the stack trace, glog currently supports the following targets:
|
|
||||||
|
|
||||||
* x86, x86_64,
|
|
||||||
* PowerPC architectures,
|
|
||||||
* `libunwind`,
|
|
||||||
* and the Debug Help Library (`dbghelp`) on Windows.
|
|
||||||
|
|
||||||
@ -1,93 +0,0 @@
|
|||||||
# Adjusting Output
|
|
||||||
|
|
||||||
Several flags influence glog's output behavior.
|
|
||||||
|
|
||||||
## Using Command-line Parameters and Environment Variables
|
|
||||||
|
|
||||||
If the [Google gflags
|
|
||||||
library](https://github.com/gflags/gflags) is installed on your machine,
|
|
||||||
the build system will automatically detect and use it, allowing you to
|
|
||||||
pass flags on the command line.
|
|
||||||
|
|
||||||
!!! example "Activate `--logtostderr` in an application from the command line"
|
|
||||||
A binary `you_application` that uses glog can be started using
|
|
||||||
``` bash
|
|
||||||
./your_application --logtostderr=1
|
|
||||||
```
|
|
||||||
to log to `stderr` instead of writing the output to a log file.
|
|
||||||
|
|
||||||
!!! tip
|
|
||||||
You can set boolean flags to `true` by specifying `1`, `true`, or `yes`. To
|
|
||||||
set boolean flags to `false`, specify `0`, `false`, or `no`. In either case
|
|
||||||
the spelling is case-insensitive.
|
|
||||||
|
|
||||||
|
|
||||||
If the Google gflags library isn't installed, you set flags via
|
|
||||||
environment variables, prefixing the flag name with `GLOG_`, e.g.,
|
|
||||||
|
|
||||||
!!! example "Activate `logtostderr` without gflags"
|
|
||||||
``` bash
|
|
||||||
GLOG_logtostderr=1 ./your_application
|
|
||||||
```
|
|
||||||
|
|
||||||
The following flags are most commonly used:
|
|
||||||
|
|
||||||
`logtostderr` (`bool`, default=`false`)
|
|
||||||
|
|
||||||
: Log messages to `stderr` instead of logfiles.
|
|
||||||
|
|
||||||
`stderrthreshold` (`int`, default=2, which is `ERROR`)
|
|
||||||
|
|
||||||
: Copy log messages at or above this level to `stderr` in addition to
|
|
||||||
logfiles. The numbers of severity levels `INFO`, `WARNING`, `ERROR`,
|
|
||||||
and `FATAL` are 0, 1, 2, and 3, respectively.
|
|
||||||
|
|
||||||
`minloglevel` (`int`, default=0, which is `INFO`)
|
|
||||||
|
|
||||||
: Log messages at or above this level. Again, the numbers of severity
|
|
||||||
levels `INFO`, `WARNING`, `ERROR`, and `FATAL` are 0, 1, 2, and 3,
|
|
||||||
respectively.
|
|
||||||
|
|
||||||
`log_dir` (`string`, default="")
|
|
||||||
|
|
||||||
: If specified, logfiles are written into this directory instead of
|
|
||||||
the default logging directory.
|
|
||||||
|
|
||||||
`v` (`int`, default=0)
|
|
||||||
|
|
||||||
: Show all `#!cpp VLOG(m)` messages for `m` less or equal the value of this
|
|
||||||
flag. Overridable by `#!bash --vmodule`. Refer to [verbose
|
|
||||||
logging](logging.md#verbose-logging) for more detail.
|
|
||||||
|
|
||||||
`vmodule` (`string`, default="")
|
|
||||||
|
|
||||||
: Per-module verbose level. The argument has to contain a
|
|
||||||
comma-separated list of `<module name>=<log level>`. `<module name>` is a
|
|
||||||
glob pattern (e.g., `gfs*` for all modules whose name starts with "gfs"),
|
|
||||||
matched against the filename base (that is, name ignoring .cc/.h./-inl.h).
|
|
||||||
`<log level>` overrides any value given by `--v`. See also [verbose
|
|
||||||
logging](logging.md#verbose-logging) for more details.
|
|
||||||
|
|
||||||
Additional flags are defined in
|
|
||||||
[flags.cc](https://github.com/google/glog/blob/master/src/flags.cc). Please see
|
|
||||||
the source for their complete list.
|
|
||||||
|
|
||||||
## Modifying Flags Programmatically
|
|
||||||
|
|
||||||
You can also modify flag values in your program by modifying global variables
|
|
||||||
`FLAGS_*`. Most settings start working immediately after you update `FLAGS_*`.
|
|
||||||
The exceptions are the flags related to destination files. For instance, you
|
|
||||||
might want to set `FLAGS_log_dir` before calling `google::InitGoogleLogging`.
|
|
||||||
|
|
||||||
!!! example "Setting `log_dir` at runtime"
|
|
||||||
``` cpp
|
|
||||||
LOG(INFO) << "file";
|
|
||||||
// Most flags work immediately after updating values.
|
|
||||||
FLAGS_logtostderr = 1;
|
|
||||||
LOG(INFO) << "stderr";
|
|
||||||
FLAGS_logtostderr = 0;
|
|
||||||
// This won’t change the log destination. If you want to set this
|
|
||||||
// value, you should do this before google::InitGoogleLogging .
|
|
||||||
FLAGS_log_dir = "/some/log/directory";
|
|
||||||
LOG(INFO) << "the same file";
|
|
||||||
```
|
|
||||||
@ -1,30 +0,0 @@
|
|||||||
# Google Logging Library
|
|
||||||
|
|
||||||
Google Logging (glog) is a C++14 library that implements application-level
|
|
||||||
logging. The library provides logging APIs based on C++-style streams and
|
|
||||||
various helper macros.
|
|
||||||
|
|
||||||
# How to Use
|
|
||||||
|
|
||||||
You can log a message by simply streaming things to `LOG`(<a particular
|
|
||||||
[severity level](logging.md#severity-levels)\>), e.g.,
|
|
||||||
|
|
||||||
``` cpp title="main.cpp"
|
|
||||||
#include <glog/logging.h>
|
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
|
||||||
google::InitGoogleLogging(argv[0]); // (1)!
|
|
||||||
LOG(INFO) << "Found " << num_cookies << " cookies"; // (2)!
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
1. Initialize the Google Logging Library
|
|
||||||
2. Log a message with informational severity
|
|
||||||
|
|
||||||
The library can be installed using various [package managers](packages.md) or
|
|
||||||
compiled [from source](build.md). For a detailed overview of glog features and
|
|
||||||
their usage, please refer to the [user guide](logging.md).
|
|
||||||
|
|
||||||
!!! warning
|
|
||||||
The above example requires further [Bazel](build.md#bazel) or
|
|
||||||
[CMake](usage.md) setup for use in own projects.
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
# The 3-Clause BSD License
|
|
||||||
|
|
||||||
--8<-- "LICENSE.md"
|
|
||||||
@ -1,24 +0,0 @@
|
|||||||
# Automatically Remove Old Logs
|
|
||||||
|
|
||||||
To enable the log cleaner:
|
|
||||||
|
|
||||||
``` cpp
|
|
||||||
using namespace std::chrono_literals;
|
|
||||||
google::EnableLogCleaner(24h * 3); // keep your logs for 3 days
|
|
||||||
```
|
|
||||||
|
|
||||||
In C++20 (and later) this can be shortened to:
|
|
||||||
|
|
||||||
``` cpp
|
|
||||||
using namespace std::chrono_literals;
|
|
||||||
google::EnableLogCleaner(3d); // keep your logs for 3 days
|
|
||||||
```
|
|
||||||
|
|
||||||
And then glog will check if there are overdue logs whenever a flush is
|
|
||||||
performed. In this example, any log file from your project whose last
|
|
||||||
modified time is greater than 3 days will be `unlink`()ed.
|
|
||||||
|
|
||||||
This feature can be disabled at any time (if it has been enabled) using
|
|
||||||
``` cpp
|
|
||||||
google::DisableLogCleaner();
|
|
||||||
```
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
# Strip Logging Messages
|
|
||||||
|
|
||||||
Strings used in log messages can increase the size of your binary and
|
|
||||||
present a privacy concern. You can therefore instruct glog to remove all
|
|
||||||
strings which fall below a certain severity level by using the
|
|
||||||
`GOOGLE_STRIP_LOG` macro:
|
|
||||||
|
|
||||||
If your application has code like this:
|
|
||||||
|
|
||||||
``` cpp
|
|
||||||
#define GOOGLE_STRIP_LOG 1 // this must go before the #include!
|
|
||||||
#include <glog/logging.h>
|
|
||||||
```
|
|
||||||
|
|
||||||
The compiler will remove the log messages whose severities are less than
|
|
||||||
the specified integer value. Since `VLOG` logs at the severity level
|
|
||||||
`INFO` (numeric value `0`), setting `GOOGLE_STRIP_LOG` to 1 or greater
|
|
||||||
removes all log messages associated with `VLOG`s as well as `INFO` log
|
|
||||||
statements.
|
|
||||||
|
|
||||||
424
docs/logging.md
424
docs/logging.md
@ -1,424 +0,0 @@
|
|||||||
# Logging
|
|
||||||
|
|
||||||
glog defines a series of macros that simplify many common logging tasks. You can
|
|
||||||
log messages by [severity level](#severity-levels), [control logging](flags.md)
|
|
||||||
behavior from the command line, log based on
|
|
||||||
[conditionals](#conditional-occasional-logging), abort the program when
|
|
||||||
[expected conditions](#runtime-checks) are not met, introduce your [own logging
|
|
||||||
levels](#verbose-logging), [customize the prefix](#format-customization)
|
|
||||||
attached to log messages, and more.
|
|
||||||
|
|
||||||
|
|
||||||
## Severity Levels
|
|
||||||
|
|
||||||
You can specify one of the following severity levels (in increasing order of
|
|
||||||
severity):
|
|
||||||
|
|
||||||
1. `INFO`,
|
|
||||||
2. `WARNING`,
|
|
||||||
3. `ERROR`, and
|
|
||||||
4. `FATAL`.
|
|
||||||
|
|
||||||
Logging a `FATAL` message terminates the program (after the message is logged).
|
|
||||||
|
|
||||||
!!! note
|
|
||||||
Messages of a given severity are logged not only to corresponding severity
|
|
||||||
logfile but also to other logfiles of lower severity. For instance, a
|
|
||||||
message of severity `FATAL` will be logged to logfiles of severity `FATAL`,
|
|
||||||
`ERROR`, `WARNING`, and `INFO`.
|
|
||||||
|
|
||||||
The `DFATAL` severity logs a `FATAL` error in [debug mode](#debugging-support)
|
|
||||||
(i.e., there is no `NDEBUG` macro defined), but avoids halting the program in
|
|
||||||
production by automatically reducing the severity to `ERROR`.
|
|
||||||
|
|
||||||
## Log Files
|
|
||||||
|
|
||||||
Unless otherwise specified, glog uses the format
|
|
||||||
|
|
||||||
<tmp>/<program name>.<hostname>.<user name>.log.<severity level>.<date>-<time>.<pid>
|
|
||||||
|
|
||||||
for log filenames written to a directory designated as `<tmp>` and
|
|
||||||
determined according to the following rules.
|
|
||||||
|
|
||||||
**Windows**
|
|
||||||
|
|
||||||
: glog uses the
|
|
||||||
[GetTempPathA](https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettemppatha)
|
|
||||||
API function to retrieve the directory for temporary files with a
|
|
||||||
fallback to
|
|
||||||
|
|
||||||
1. `C:\TMP\`
|
|
||||||
2. `C:\TEMP\`
|
|
||||||
|
|
||||||
(in the order given.)
|
|
||||||
|
|
||||||
**non-Windows**
|
|
||||||
|
|
||||||
: The directory is determined by referencing the environment variables
|
|
||||||
|
|
||||||
1. `TMPDIR`
|
|
||||||
2. `TMP`
|
|
||||||
|
|
||||||
if set with a fallback to `/tmp/`.
|
|
||||||
|
|
||||||
The default path to a log file on Linux, for instance, could be
|
|
||||||
|
|
||||||
/tmp/hello_world.example.com.hamaji.log.INFO.20080709-222411.10474
|
|
||||||
|
|
||||||
By default, glog echos `ERROR` and `FATAL` messages to standard error in
|
|
||||||
addition to log files.
|
|
||||||
|
|
||||||
## Log Line Prefix Format
|
|
||||||
|
|
||||||
Log lines have this form:
|
|
||||||
|
|
||||||
Lyyyymmdd hh:mm:ss.uuuuuu threadid file:line] msg...
|
|
||||||
|
|
||||||
where the fields are defined as follows:
|
|
||||||
|
|
||||||
| Placeholder | Meaning |
|
|
||||||
| ------------------- | ----------------------------------------------------------------------|
|
|
||||||
| `L` | A single character, representing the log level (e.g., `I` for `INFO`) |
|
|
||||||
| `yyyy` | The year |
|
|
||||||
| `mm` | The month (zero padded; i.e., May is `05`) |
|
|
||||||
| `dd` | The day (zero padded) |
|
|
||||||
| `hh:mm:ss.uuuuuu` | Time in hours, minutes and fractional seconds |
|
|
||||||
| `threadid` | The space-padded thread ID |
|
|
||||||
| `file` | The file name |
|
|
||||||
| `line` | The line number |
|
|
||||||
| `msg` | The user-supplied message |
|
|
||||||
|
|
||||||
!!! example "Default log line prefix format"
|
|
||||||
|
|
||||||
```
|
|
||||||
I1103 11:57:31.739339 24395 google.cc:2341] Command line: ./some_prog
|
|
||||||
I1103 11:57:31.739403 24395 google.cc:2342] Process id 24395
|
|
||||||
```
|
|
||||||
|
|
||||||
!!! note
|
|
||||||
Although microseconds are useful for comparing events on a single machine,
|
|
||||||
clocks on different machines may not be well synchronized. Hence, use with
|
|
||||||
caution when comparing the low bits of timestamps from different machines.
|
|
||||||
|
|
||||||
### Format Customization
|
|
||||||
|
|
||||||
The predefined log line prefix can be replaced using a user-provided callback
|
|
||||||
that formats the corresponding output.
|
|
||||||
|
|
||||||
For each log entry, the callback will be invoked with a reference to a
|
|
||||||
`google::LogMessage` instance containing the severity, filename, line
|
|
||||||
number, thread ID, and time of the event. It will also be given a
|
|
||||||
reference to the output stream, whose contents will be prepended to the actual
|
|
||||||
message in the final log line.
|
|
||||||
|
|
||||||
To enable the use of a prefix formatter, use the
|
|
||||||
|
|
||||||
``` cpp
|
|
||||||
google::InstallPrefixFormatter(&MyPrefixFormatter);
|
|
||||||
```
|
|
||||||
|
|
||||||
function to pass a pointer to the corresponding `MyPrefixFormatter` callback
|
|
||||||
during initialization. `InstallPrefixFormatter` takes a second optional argument
|
|
||||||
of type `#!cpp void*` that allows supplying user data to the callback.
|
|
||||||
|
|
||||||
!!! example "Custom prefix formatter"
|
|
||||||
The following function outputs a prefix that matches glog's default format.
|
|
||||||
The third parameter `data` can be used to access user-supplied data which
|
|
||||||
unless specified defaults to `#!cpp nullptr`.
|
|
||||||
|
|
||||||
``` cpp
|
|
||||||
void MyPrefixFormatter(std::ostream& s, const google::LogMessage& m, void* /*data*/) {
|
|
||||||
s << google::GetLogSeverityName(m.severity())[0]
|
|
||||||
<< setw(4) << 1900 + m.time().year()
|
|
||||||
<< setw(2) << 1 + m.time().month()
|
|
||||||
<< setw(2) << m.time().day()
|
|
||||||
<< ' '
|
|
||||||
<< setw(2) << m.time().hour() << ':'
|
|
||||||
<< setw(2) << m.time().min() << ':'
|
|
||||||
<< setw(2) << m.time().sec() << "."
|
|
||||||
<< setw(6) << m.time().usec()
|
|
||||||
<< ' '
|
|
||||||
<< setfill(' ') << setw(5)
|
|
||||||
<< m.thread_id() << setfill('0')
|
|
||||||
<< ' '
|
|
||||||
<< m.basename() << ':' << m.line() << "]";
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Conditional / Occasional Logging
|
|
||||||
|
|
||||||
Sometimes, you may only want to log a message under certain conditions.
|
|
||||||
You can use the following macros to perform conditional logging:
|
|
||||||
|
|
||||||
``` cpp
|
|
||||||
LOG_IF(INFO, num_cookies > 10) << "Got lots of cookies";
|
|
||||||
```
|
|
||||||
|
|
||||||
The "Got lots of cookies" message is logged only when the variable
|
|
||||||
`num_cookies` exceeds 10. If a line of code is executed many times, it may be
|
|
||||||
useful to only log a message at certain intervals. This kind of logging is most
|
|
||||||
useful for informational messages.
|
|
||||||
|
|
||||||
``` cpp
|
|
||||||
LOG_EVERY_N(INFO, 10) << "Got the " << google::COUNTER << "th cookie";
|
|
||||||
```
|
|
||||||
|
|
||||||
The above line outputs a log messages on the 1st, 11th, 21st, ... times
|
|
||||||
it is executed.
|
|
||||||
|
|
||||||
!!! note
|
|
||||||
The placeholder `#!cpp google::COUNTER` identifies the recurring repetition.
|
|
||||||
|
|
||||||
You can combine conditional and occasional logging with the following
|
|
||||||
macro.
|
|
||||||
|
|
||||||
``` cpp
|
|
||||||
LOG_IF_EVERY_N(INFO, (size > 1024), 10) << "Got the " << google::COUNTER
|
|
||||||
<< "th big cookie";
|
|
||||||
```
|
|
||||||
|
|
||||||
Instead of outputting a message every nth time, you can also limit the
|
|
||||||
output to the first n occurrences:
|
|
||||||
|
|
||||||
``` cpp
|
|
||||||
LOG_FIRST_N(INFO, 20) << "Got the " << google::COUNTER << "th cookie";
|
|
||||||
```
|
|
||||||
|
|
||||||
Outputs log messages for the first 20 times it is executed. The `#!cpp
|
|
||||||
google::COUNTER` identifier indicates which repetition is happening.
|
|
||||||
|
|
||||||
Other times, it is desired to only log a message periodically based on a
|
|
||||||
time. For instance, to log a message every 10ms:
|
|
||||||
|
|
||||||
``` cpp
|
|
||||||
LOG_EVERY_T(INFO, 0.01) << "Got a cookie";
|
|
||||||
```
|
|
||||||
|
|
||||||
Or every 2.35s:
|
|
||||||
|
|
||||||
``` cpp
|
|
||||||
LOG_EVERY_T(INFO, 2.35) << "Got a cookie";
|
|
||||||
```
|
|
||||||
|
|
||||||
## Verbose Logging
|
|
||||||
|
|
||||||
When you are chasing difficult bugs, thorough log messages are very
|
|
||||||
useful. However, you may want to ignore too verbose messages in usual
|
|
||||||
development. For such verbose logging, glog provides the `VLOG` macro, which
|
|
||||||
allows you to define your own numeric logging levels.
|
|
||||||
|
|
||||||
The `#!bash --v` command line option controls which verbose messages are logged:
|
|
||||||
|
|
||||||
``` cpp
|
|
||||||
VLOG(1) << "I’m printed when you run the program with --v=1 or higher";
|
|
||||||
VLOG(2) << "I’m printed when you run the program with --v=2 or higher";
|
|
||||||
```
|
|
||||||
|
|
||||||
With `VLOG`, the lower the verbose level, the more likely messages are to be
|
|
||||||
logged. For example, if `#!bash --v==1`, `#!cpp VLOG(1)` will log, but `#!cpp
|
|
||||||
VLOG(2)` will not log.
|
|
||||||
|
|
||||||
!!! warning
|
|
||||||
The `VLOG` behavior is opposite of the severity level logging, where
|
|
||||||
`INFO`, `ERROR`, etc. are defined in increasing order and thus
|
|
||||||
`#!bash --minloglevel` of 1 will only log `WARNING` and above.
|
|
||||||
|
|
||||||
Though you can specify any integers for both `VLOG` macro and `--v` flag, the
|
|
||||||
common values for them are small positive integers. For example, if you write
|
|
||||||
`#!cpp VLOG(0)`, you should specify `--v=-1` or lower to silence it. This is less
|
|
||||||
useful since we may not want verbose logs by default in most cases. The `VLOG`
|
|
||||||
macros always log at the `INFO` log level (when they log at all).
|
|
||||||
|
|
||||||
Verbose logging can be controlled from the command line on a per-module basis:
|
|
||||||
|
|
||||||
``` bash
|
|
||||||
--vmodule=mapreduce=2,file=1,gfs*=3 --v=0
|
|
||||||
```
|
|
||||||
|
|
||||||
Specifying these options will specifically:
|
|
||||||
|
|
||||||
1. Print `#!cpp VLOG(2)` and lower messages from mapreduce.{h,cc}
|
|
||||||
2. Print `#!cpp VLOG(1)` and lower messages from file.{h,cc}
|
|
||||||
3. Print `#!cpp VLOG(3)` and lower messages from files prefixed with "gfs"
|
|
||||||
4. Print `#!cpp VLOG(0)` and lower messages from elsewhere
|
|
||||||
|
|
||||||
The wildcarding functionality 3. supports both `*` (matches 0 or more
|
|
||||||
characters) and `?` (matches any single character) wildcards. Please also refer
|
|
||||||
to [command line flags](flags.md) for more information.
|
|
||||||
|
|
||||||
There's also `#!cpp VLOG_IS_ON(n)` "verbose level" condition macro. This macro
|
|
||||||
returns `#!cpp true` when the `--v` is equal to or greater than `n`. The macro can be
|
|
||||||
used as follows:
|
|
||||||
|
|
||||||
``` cpp
|
|
||||||
if (VLOG_IS_ON(2)) {
|
|
||||||
// (1)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
1. Here we can perform some logging preparation and logging that can’t be
|
|
||||||
accomplished with just `#!cpp VLOG(2) << "message ...";`
|
|
||||||
|
|
||||||
Verbose level condition macros `VLOG_IF`, `VLOG_EVERY_N` and `VLOG_IF_EVERY_N`
|
|
||||||
behave analogous to `LOG_IF`, `LOG_EVERY_N`, `LOG_IF_EVERY_N`, but accept a
|
|
||||||
numeric verbosity level as opposed to a severity level.
|
|
||||||
|
|
||||||
``` cpp
|
|
||||||
VLOG_IF(1, (size > 1024))
|
|
||||||
<< "I’m printed when size is more than 1024 and when you run the "
|
|
||||||
"program with --v=1 or more";
|
|
||||||
VLOG_EVERY_N(1, 10)
|
|
||||||
<< "I’m printed every 10th occurrence, and when you run the program "
|
|
||||||
"with --v=1 or more. Present occurrence is " << google::COUNTER;
|
|
||||||
VLOG_IF_EVERY_N(1, (size > 1024), 10)
|
|
||||||
<< "I’m printed on every 10th occurrence of case when size is more "
|
|
||||||
" than 1024, when you run the program with --v=1 or more. ";
|
|
||||||
"Present occurrence is " << google::COUNTER;
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
!!! info "Performance"
|
|
||||||
The conditional logging macros provided by glog (e.g., `CHECK`, `LOG_IF`,
|
|
||||||
`VLOG`, etc.) are carefully implemented and don't execute the right hand
|
|
||||||
side expressions when the conditions are false. So, the following check may
|
|
||||||
not sacrifice the performance of your application.
|
|
||||||
|
|
||||||
``` cpp
|
|
||||||
CHECK(obj.ok) << obj.CreatePrettyFormattedStringButVerySlow();
|
|
||||||
```
|
|
||||||
|
|
||||||
## Debugging Support
|
|
||||||
|
|
||||||
Special debug mode logging macros only have an effect in debug mode and are
|
|
||||||
compiled away to nothing for non-debug mode compiles. Use these macros to avoid
|
|
||||||
slowing down your production application due to excessive logging.
|
|
||||||
|
|
||||||
``` cpp
|
|
||||||
DLOG(INFO) << "Found cookies";
|
|
||||||
DLOG_IF(INFO, num_cookies > 10) << "Got lots of cookies";
|
|
||||||
DLOG_EVERY_N(INFO, 10) << "Got the " << google::COUNTER << "th cookie";
|
|
||||||
DLOG_FIRST_N(INFO, 10) << "Got the " << google::COUNTER << "th cookie";
|
|
||||||
DLOG_EVERY_T(INFO, 0.01) << "Got a cookie";
|
|
||||||
```
|
|
||||||
|
|
||||||
## Runtime Checks
|
|
||||||
|
|
||||||
It is a good practice to check expected conditions in your program
|
|
||||||
frequently to detect errors as early as possible. The `CHECK` macro
|
|
||||||
provides the ability to abort the application when a condition is not met,
|
|
||||||
similar to the `assert` macro defined in the standard C library.
|
|
||||||
|
|
||||||
`CHECK` aborts the application if a condition is not true. Unlike
|
|
||||||
`assert`, it is **not** controlled by `NDEBUG`, so the check will be executed
|
|
||||||
regardless of compilation mode. Therefore, `fp->Write(x)` in the following
|
|
||||||
example is always executed:
|
|
||||||
|
|
||||||
``` cpp
|
|
||||||
CHECK(fp->Write(x) == 4) << "Write failed!";
|
|
||||||
```
|
|
||||||
|
|
||||||
There are various helper macros for equality/inequality checks
|
|
||||||
-`CHECK_EQ`, `CHECK_NE`, `CHECK_LE`, `CHECK_LT`, `CHECK_GE`, and
|
|
||||||
`CHECK_GT`. They compare two values, and log a `FATAL` message including the two
|
|
||||||
values when the result is not as expected. The values must have
|
|
||||||
`#!cpp operator<<(ostream, ...)` defined.
|
|
||||||
|
|
||||||
You may append to the error message like so:
|
|
||||||
|
|
||||||
``` cpp
|
|
||||||
CHECK_NE(1, 2) << ": The world must be ending!";
|
|
||||||
```
|
|
||||||
|
|
||||||
We are very careful to ensure that each argument is evaluated exactly
|
|
||||||
once, and that anything which is legal to pass as a function argument is legal
|
|
||||||
here. In particular, the arguments may be temporary expressions which will end
|
|
||||||
up being destroyed at the end of the apparent statement, for example:
|
|
||||||
|
|
||||||
``` cpp
|
|
||||||
CHECK_EQ(string("abc")[1], ’b’);
|
|
||||||
```
|
|
||||||
|
|
||||||
The compiler reports an error if one of the arguments is a pointer and the other
|
|
||||||
is `#!cpp nullptr`. To work around this, simply `#!cpp static_cast` `#!cpp
|
|
||||||
nullptr` to the type of the desired pointer.
|
|
||||||
|
|
||||||
``` cpp
|
|
||||||
CHECK_EQ(some_ptr, static_cast<SomeType*>(nullptr));
|
|
||||||
```
|
|
||||||
|
|
||||||
Better yet, use the `CHECK_NOTNULL` macro:
|
|
||||||
|
|
||||||
``` cpp
|
|
||||||
CHECK_NOTNULL(some_ptr);
|
|
||||||
some_ptr->DoSomething();
|
|
||||||
```
|
|
||||||
|
|
||||||
Since this macro returns the given pointer, this is very useful in
|
|
||||||
constructor initializer lists.
|
|
||||||
|
|
||||||
``` cpp
|
|
||||||
struct S {
|
|
||||||
S(Something* ptr) : ptr_(CHECK_NOTNULL(ptr)) {}
|
|
||||||
Something* ptr_;
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
!!! warning
|
|
||||||
Due to the argument forwarding, `CHECK_NOTNULL` cannot be used to
|
|
||||||
simultaneously stream an additional custom message. To provide a custom
|
|
||||||
message, one can use the macro `CHECK_EQ` prior to the failing check.
|
|
||||||
|
|
||||||
If you are comparing C strings (`#!cpp char *`), a handy set of macros performs
|
|
||||||
both case sensitive and insensitive comparisons - `CHECK_STREQ`, `CHECK_STRNE`,
|
|
||||||
`CHECK_STRCASEEQ`, and `CHECK_STRCASENE`. The `CHECK_*CASE*` macro variants are
|
|
||||||
case-insensitive. You can safely pass `#!cpp nullptr` pointers to this macro.
|
|
||||||
They treat `#!cpp nullptr` and any non-`#!cpp nullptr` string as not equal. Two
|
|
||||||
`#!cpp nullptr`s are equal.
|
|
||||||
|
|
||||||
!!! note
|
|
||||||
Both arguments may be temporary objects which are destructed at the
|
|
||||||
end of the current *full expression*, such as
|
|
||||||
|
|
||||||
``` cpp
|
|
||||||
CHECK_STREQ(Foo().c_str(), Bar().c_str());
|
|
||||||
```
|
|
||||||
|
|
||||||
where `Foo` and `Bar` return `std::string`.
|
|
||||||
|
|
||||||
The `CHECK_DOUBLE_EQ` macro checks the equality of two floating point values,
|
|
||||||
accepting a small error margin. `CHECK_NEAR` accepts a third floating point
|
|
||||||
argument, which specifies the acceptable error margin.
|
|
||||||
|
|
||||||
|
|
||||||
## Raw Logging
|
|
||||||
|
|
||||||
The header file `<glog/raw_logging.h>` can be used for thread-safe logging,
|
|
||||||
which does not allocate any memory or acquire any locks. Therefore, the macros
|
|
||||||
defined in this header file can be used by low-level memory allocation and
|
|
||||||
synchronization code. Please check
|
|
||||||
[src/glog/raw_logging.h](https://github.com/google/glog/blob/master/src/glog/raw_logging.h)
|
|
||||||
for detail.
|
|
||||||
|
|
||||||
## Google Style `perror()`
|
|
||||||
|
|
||||||
`PLOG()` and `PLOG_IF()` and `PCHECK()` behave exactly like their `LOG*` and
|
|
||||||
`CHECK` equivalents with the addition that they append a description of the
|
|
||||||
current state of `errno` to their output lines. E.g.
|
|
||||||
|
|
||||||
``` cpp
|
|
||||||
PCHECK(write(1, nullptr, 2) >= 0) << "Write nullptr failed";
|
|
||||||
```
|
|
||||||
|
|
||||||
This check fails with the following error message.
|
|
||||||
|
|
||||||
F0825 185142 test.cc:22] Check failed: write(1, nullptr, 2) >= 0 Write nullptr failed: Bad address [14]
|
|
||||||
|
|
||||||
## Syslog
|
|
||||||
|
|
||||||
`SYSLOG`, `SYSLOG_IF`, and `SYSLOG_EVERY_N` macros are available. These log to
|
|
||||||
syslog in addition to the normal logs. Be aware that logging to syslog can
|
|
||||||
drastically impact performance, especially if syslog is configured for remote
|
|
||||||
logging! Make sure you understand the implications of outputting to syslog
|
|
||||||
before you use these macros. In general, it's wise to use these macros
|
|
||||||
sparingly.
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
{% extends "base.html" %}
|
|
||||||
|
|
||||||
{% block outdated %}
|
|
||||||
You're not viewing the latest version.
|
|
||||||
<a href="{{ '../' ~ base_url }}">
|
|
||||||
<strong>Click here to go to latest.</strong>
|
|
||||||
</a>
|
|
||||||
{% endblock %}
|
|
||||||
@ -1,34 +0,0 @@
|
|||||||
# Installation using Package Managers
|
|
||||||
|
|
||||||
## conan
|
|
||||||
|
|
||||||
You can download and install glog using the [conan](https://conan.io)
|
|
||||||
package manager:
|
|
||||||
|
|
||||||
``` bash
|
|
||||||
pip install conan
|
|
||||||
conan install -r conancenter glog/<glog-version>@
|
|
||||||
```
|
|
||||||
|
|
||||||
The glog recipe in conan center is kept up to date by conan center index
|
|
||||||
community contributors. If the version is out of date, please create an
|
|
||||||
issue or pull request on the
|
|
||||||
[conan-center-index](https://github.com/conan-io/conan-center-index)
|
|
||||||
repository.
|
|
||||||
|
|
||||||
## vcpkg
|
|
||||||
|
|
||||||
You can download and install glog using the
|
|
||||||
[vcpkg](https://github.com/Microsoft/vcpkg) dependency manager:
|
|
||||||
|
|
||||||
``` bash
|
|
||||||
git clone https://github.com/Microsoft/vcpkg.git
|
|
||||||
cd vcpkg
|
|
||||||
./bootstrap-vcpkg.sh
|
|
||||||
./vcpkg integrate install
|
|
||||||
./vcpkg install glog
|
|
||||||
```
|
|
||||||
|
|
||||||
The glog port in vcpkg is kept up to date by Microsoft team members and
|
|
||||||
community contributors. If the version is out of date, please create an
|
|
||||||
issue or pull request on the vcpkg repository.
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
mike>=2.1.1
|
|
||||||
mkdocs-git-committers-plugin-2>=2.3.0
|
|
||||||
mkdocs-git-revision-date-localized-plugin>=1.2.6
|
|
||||||
mkdocs-material-extensions>=1.3.1
|
|
||||||
mkdocs-material>=9.5.26
|
|
||||||
mkdocs>=1.6.0
|
|
||||||
@ -1,84 +0,0 @@
|
|||||||
# Custom Sinks
|
|
||||||
|
|
||||||
Under certain circumstances, it is useful to send the log output to a
|
|
||||||
destination other than a file, `stderr` and/or `stdout`. In case, the library
|
|
||||||
provides the `#!cpp google::LogSink` interface whose implementations can be used
|
|
||||||
to write the log output to arbitrary locations.
|
|
||||||
|
|
||||||
## Basic Interface
|
|
||||||
|
|
||||||
The sink interface is defined as follows:
|
|
||||||
|
|
||||||
``` cpp
|
|
||||||
class LogSink {
|
|
||||||
public:
|
|
||||||
virtual void send(LogSeverity severity, const char* full_filename,
|
|
||||||
const char* base_filename, int line,
|
|
||||||
const LogMessageTime& time, const char* message,
|
|
||||||
size_t message_len);
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
The user must implement `#!cpp google::LogSink::send`, which is called by the
|
|
||||||
library every time a message is logged.
|
|
||||||
|
|
||||||
!!! warning "Possible deadlock due to nested logging"
|
|
||||||
This method can't use `LOG()` or `CHECK()` as logging system mutex(s) are
|
|
||||||
held during this call.
|
|
||||||
|
|
||||||
## Registering Log Sinks
|
|
||||||
|
|
||||||
To use the custom sink and instance of the above interface implementation must
|
|
||||||
be registered using `google::AddLogSink` which expects a pointer to the
|
|
||||||
`google::LogSink` instance. To unregister use `google::RemoveLogSink`. Both
|
|
||||||
functions are thread-safe.
|
|
||||||
|
|
||||||
!!! danger "`LogSink` ownership"
|
|
||||||
The `google::LogSink` instance must not be destroyed until the referencing
|
|
||||||
pointer is unregistered.
|
|
||||||
|
|
||||||
## Direct Logging
|
|
||||||
|
|
||||||
Instead of registering the sink, we can directly use to log messages. While `#!
|
|
||||||
LOG_TO_SINK(sink, severity)` allows to log both to the sink and to a global log
|
|
||||||
registry, e.g., a file, `#!cpp LOG_TO_SINK_BUT_NOT_TO_LOGFILE(sink, severity)`
|
|
||||||
will avoid the latter.
|
|
||||||
|
|
||||||
!!! example "Using a custom sink"
|
|
||||||
``` cpp title="custom_sink.cc"
|
|
||||||
-8<- "examples/custom_sink.cc:33:"
|
|
||||||
```
|
|
||||||
|
|
||||||
1. `MySink` implements a custom sink that sends log messages to `std::cout`.
|
|
||||||
2. The custom sink must be registered to for use with existing logging
|
|
||||||
macros.
|
|
||||||
3. Once the custom sink is no longer needed we remove it from the registry.
|
|
||||||
4. A sink does not need to be registered globally. However, then, messages
|
|
||||||
must be logged using dedicated macros.
|
|
||||||
|
|
||||||
Running the above example as `#!bash GLOG_log_dir=. ./custom_sink_example`
|
|
||||||
will produce
|
|
||||||
|
|
||||||
<div class="annotate" markdown>
|
|
||||||
|
|
||||||
``` title="Custom sink output"
|
|
||||||
INFO custom_sink.cc:63 logging to MySink
|
|
||||||
INFO custom_sink.cc:68 direct logging
|
|
||||||
INFO custom_sink.cc:69 direct logging but not to file (1)
|
|
||||||
```
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
1. This line is not present in the log file because we used
|
|
||||||
`LOG_TO_SINK_BUT_NOT_TO_LOGFILE` to log the message.
|
|
||||||
|
|
||||||
and the corresponding log file will contain
|
|
||||||
|
|
||||||
``` title="Log file generated with the custom sink"
|
|
||||||
Log file created at: 2024/06/11 13:24:27
|
|
||||||
Running on machine: pc
|
|
||||||
Running duration (h:mm:ss): 0:00:00
|
|
||||||
Log line format: [IWEF]yyyymmdd hh:mm:ss.uuuuuu threadid file:line] msg
|
|
||||||
I20240611 13:24:27.476620 126237946035776 custom_sink.cc:63] logging to MySink
|
|
||||||
I20240611 13:24:27.476796 126237946035776 custom_sink.cc:68] direct logging
|
|
||||||
```
|
|
||||||
@ -1,57 +0,0 @@
|
|||||||
# Installation Notes for 64-bit Linux Systems
|
|
||||||
|
|
||||||
!!! note
|
|
||||||
The description on this page is possibly not up-to-date.
|
|
||||||
|
|
||||||
The [glibc built-in stack-unwinder](#glibc-built-in-stack-unwinder) on 64-bit
|
|
||||||
systems has some problems with glog. In particular, if you are using
|
|
||||||
[`InstallFailureSignalHandler()`](failures.md), the signal may be raised in the
|
|
||||||
middle of `malloc`, holding some `malloc`-related locks when they invoke the
|
|
||||||
stack unwinder. The built-in stack unwinder may call `malloc` recursively, which
|
|
||||||
may require the thread to acquire a lock it already holds resulting in a
|
|
||||||
deadlock.
|
|
||||||
|
|
||||||
## Recommended Approach: `libunwind`
|
|
||||||
|
|
||||||
For above reason, if you use a 64-bit system and you need
|
|
||||||
`InstallFailureSignalHandler()`, we strongly recommend you install `libunwind`
|
|
||||||
before trying to configure or install google glog. libunwind can be found
|
|
||||||
[here](http://download.savannah.nongnu.org/releases/libunwind/libunwind-snap-070410.tar.gz).
|
|
||||||
|
|
||||||
Even if you already have `libunwind` installed, you will probably still need to
|
|
||||||
install from the snapshot to get the latest version.
|
|
||||||
|
|
||||||
!!! warning
|
|
||||||
If you install libunwind from the URL above, be aware that you may have
|
|
||||||
trouble if you try to statically link your binary with glog: that is, if you
|
|
||||||
link with `gcc -static -lgcc_eh ...`. This is because both `libunwind` and
|
|
||||||
`libgcc` implement the same C++ exception handling APIs, but they implement
|
|
||||||
them differently on some platforms. This is not likely to be a problem on
|
|
||||||
ia64, but may be on x86-64.
|
|
||||||
|
|
||||||
Also, if you link binaries statically, make sure that you add
|
|
||||||
`-Wl,--eh-frame-hdr` to your linker options. This is required so that
|
|
||||||
`libunwind` can find the information generated by the compiler required for
|
|
||||||
stack unwinding.
|
|
||||||
|
|
||||||
Using `-static` is rare, though, so unless you know this will affect you it
|
|
||||||
probably won't.
|
|
||||||
|
|
||||||
## Alternative Stack-unwinder
|
|
||||||
|
|
||||||
If you cannot or do not wish to install `libunwind`, you can still try to use
|
|
||||||
two kinds of stack-unwinder:
|
|
||||||
|
|
||||||
### glibc Built-in Stack-unwinder
|
|
||||||
|
|
||||||
As we already mentioned, glibc's unwinder has a deadlock issue. However, if you
|
|
||||||
don't use `InstallFailureSignalHandler()` or you don't worry about the rare
|
|
||||||
possibilities of deadlocks, you can use this stack-unwinder. If you specify no
|
|
||||||
options and `libunwind` isn't detected on your system, the configure script
|
|
||||||
chooses this unwinder by default.
|
|
||||||
|
|
||||||
### Frame Pointer based Stack-unwinder
|
|
||||||
|
|
||||||
The frame pointer based stack unwinder requires that your application, the glog
|
|
||||||
library, and system libraries like libc, all be compiled with a frame pointer.
|
|
||||||
This is *not* the default for x86-64.
|
|
||||||
@ -1,24 +0,0 @@
|
|||||||
# Using glog in a CMake Project
|
|
||||||
|
|
||||||
Assuming that glog was previously [built using CMake](build.md#cmake) or
|
|
||||||
installed using a package manager, you can use the CMake command `#!cmake
|
|
||||||
find_package` to build against glog in your CMake project as follows:
|
|
||||||
|
|
||||||
``` cmake title="CMakeLists.txt"
|
|
||||||
cmake_minimum_required (VERSION 3.16)
|
|
||||||
project (myproj VERSION 1.0)
|
|
||||||
|
|
||||||
find_package (glog 0.8.0 REQUIRED)
|
|
||||||
|
|
||||||
add_executable (myapp main.cpp)
|
|
||||||
target_link_libraries (myapp glog::glog)
|
|
||||||
```
|
|
||||||
|
|
||||||
Compile definitions and options will be added automatically to your target as
|
|
||||||
needed.
|
|
||||||
|
|
||||||
Alternatively, glog can be incorporated into using the CMake command `#!cmake
|
|
||||||
add_subdirectory` to include glog directly from a subdirectory of your project
|
|
||||||
by replacing the `#!cmake find_package` call from the previous snippet by
|
|
||||||
`add_subdirectory`. The `#!cmake glog::glog` target is in this case an `#!cmake
|
|
||||||
ALIAS` library target for the `glog` library target.
|
|
||||||
@ -1,44 +0,0 @@
|
|||||||
# Notes for Windows Users
|
|
||||||
|
|
||||||
glog defines the severity level `ERROR`, which is also defined by `windows.h`.
|
|
||||||
You can make glog not define `INFO`, `WARNING`, `ERROR`, and `FATAL` by defining
|
|
||||||
`GLOG_NO_ABBREVIATED_SEVERITIES` before including `glog/logging.h`. Even with
|
|
||||||
this macro, you can still use the iostream like logging facilities:
|
|
||||||
|
|
||||||
``` cpp
|
|
||||||
#define GLOG_NO_ABBREVIATED_SEVERITIES
|
|
||||||
#include <windows.h>
|
|
||||||
#include <glog/logging.h>
|
|
||||||
|
|
||||||
// ...
|
|
||||||
|
|
||||||
LOG(ERROR) << "This should work";
|
|
||||||
LOG_IF(ERROR, x > y) << "This should be also OK";
|
|
||||||
```
|
|
||||||
|
|
||||||
However, you cannot use `INFO`, `WARNING`, `ERROR`, and `FATAL` anymore for
|
|
||||||
functions defined in `glog/logging.h`.
|
|
||||||
|
|
||||||
``` cpp
|
|
||||||
#define GLOG_NO_ABBREVIATED_SEVERITIES
|
|
||||||
#include <windows.h>
|
|
||||||
#include <glog/logging.h>
|
|
||||||
|
|
||||||
// ...
|
|
||||||
|
|
||||||
// This won’t work.
|
|
||||||
// google::FlushLogFiles(google::ERROR);
|
|
||||||
|
|
||||||
// Use this instead.
|
|
||||||
google::FlushLogFiles(google::GLOG_ERROR);
|
|
||||||
```
|
|
||||||
|
|
||||||
If you don't need `ERROR` defined by `windows.h`, there are a couple of more
|
|
||||||
workarounds which sometimes don't work[^1]:
|
|
||||||
|
|
||||||
- `#!cpp #define WIN32_LEAN_AND_MEAN` or `NOGDI` **before**
|
|
||||||
`#!cpp #include <windows.h>`.
|
|
||||||
- `#!cpp #undef ERROR` **after** `#!cpp #include <windows.h>`.
|
|
||||||
|
|
||||||
[^1]: For more information refer to [this
|
|
||||||
issue](http://code.google.com/p/google-glog/issues/detail?id=33).
|
|
||||||
@ -1,71 +0,0 @@
|
|||||||
// Copyright (c) 2024, Google Inc.
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
//
|
|
||||||
// Author: Sergiu Deitsch
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <glog/logging.h>
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cstddef>
|
|
||||||
#include <iostream>
|
|
||||||
#include <iterator>
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
struct MyLogSink : google::LogSink { // (1)!
|
|
||||||
void send(google::LogSeverity severity, const char* /*full_filename*/,
|
|
||||||
const char* base_filename, int line,
|
|
||||||
const google::LogMessageTime& /*time*/, const char* message,
|
|
||||||
std::size_t message_len) override {
|
|
||||||
std::cout << google::GetLogSeverityName(severity) << ' ' << base_filename
|
|
||||||
<< ':' << line << ' ';
|
|
||||||
std::copy_n(message, message_len,
|
|
||||||
std::ostreambuf_iterator<char>{std::cout});
|
|
||||||
std::cout << '\n';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
int main(int /*argc*/, char** argv) {
|
|
||||||
google::InitGoogleLogging(argv[0]);
|
|
||||||
|
|
||||||
MyLogSink sink;
|
|
||||||
google::AddLogSink(&sink); // (2)!
|
|
||||||
|
|
||||||
LOG(INFO) << "logging to MySink";
|
|
||||||
|
|
||||||
google::RemoveLogSink(&sink); // (3)!
|
|
||||||
|
|
||||||
// We can directly log to a sink without registering it
|
|
||||||
LOG_TO_SINK(&sink, INFO) << "direct logging"; // (4)!
|
|
||||||
LOG_TO_SINK_BUT_NOT_TO_LOGFILE(&sink, INFO)
|
|
||||||
<< "direct logging but not to file";
|
|
||||||
}
|
|
||||||
123
mkdocs.yml
123
mkdocs.yml
@ -1,123 +0,0 @@
|
|||||||
---
|
|
||||||
site_name: Google Logging Library
|
|
||||||
site_url: https://google.github.io/glog/
|
|
||||||
repo_url: https://github.com/google/glog
|
|
||||||
repo_name: google/glog
|
|
||||||
edit_uri: edit/master/docs/
|
|
||||||
copyright: Copyright © 2024 Google Inc. & contributors - <a href="#__consent">Change cookie settings</a>
|
|
||||||
markdown_extensions:
|
|
||||||
- admonition
|
|
||||||
- attr_list
|
|
||||||
- def_list
|
|
||||||
- footnotes
|
|
||||||
- md_in_html
|
|
||||||
- pymdownx.details
|
|
||||||
- pymdownx.highlight:
|
|
||||||
anchor_linenums: true
|
|
||||||
line_spans: __span
|
|
||||||
pygments_lang_class: true
|
|
||||||
- pymdownx.inlinehilite
|
|
||||||
- pymdownx.snippets:
|
|
||||||
base_path:
|
|
||||||
- '.'
|
|
||||||
check_paths: true
|
|
||||||
- pymdownx.superfences
|
|
||||||
- tables
|
|
||||||
- toc:
|
|
||||||
permalink: true
|
|
||||||
theme:
|
|
||||||
name: material
|
|
||||||
custom_dir: docs/overrides
|
|
||||||
icon:
|
|
||||||
annotation: material/chevron-right-circle
|
|
||||||
edit: material/pencil
|
|
||||||
repo: fontawesome/brands/git-alt
|
|
||||||
view: material/eye
|
|
||||||
language: en
|
|
||||||
features:
|
|
||||||
- content.action.edit
|
|
||||||
- content.code.annotate
|
|
||||||
- content.code.copy
|
|
||||||
- content.code.select
|
|
||||||
- header.autohide
|
|
||||||
- navigation.expand
|
|
||||||
- navigation.instant.preview
|
|
||||||
- navigation.instant.progress
|
|
||||||
- navigation.prune
|
|
||||||
- navigation.indexes
|
|
||||||
- toc.follow
|
|
||||||
- navigation.top
|
|
||||||
- navigation.path
|
|
||||||
# - navigation.sections
|
|
||||||
# - navigation.tabs
|
|
||||||
# - navigation.tabs.sticky
|
|
||||||
- navigation.tracking
|
|
||||||
- search.highlight
|
|
||||||
- search.share
|
|
||||||
- search.suggest
|
|
||||||
palette:
|
|
||||||
# Palette toggle for automatic mode
|
|
||||||
- media: "(prefers-color-scheme)"
|
|
||||||
toggle:
|
|
||||||
icon: material/brightness-auto
|
|
||||||
name: Switch to light mode
|
|
||||||
# Palette toggle for light mode
|
|
||||||
- media: "(prefers-color-scheme: light)"
|
|
||||||
scheme: default
|
|
||||||
primary: teal
|
|
||||||
accent: green
|
|
||||||
toggle:
|
|
||||||
icon: material/brightness-7
|
|
||||||
name: Switch to dark mode
|
|
||||||
# Palette toggle for dark mode
|
|
||||||
- media: "(prefers-color-scheme: dark)"
|
|
||||||
scheme: slate
|
|
||||||
primary: black
|
|
||||||
toggle:
|
|
||||||
icon: material/brightness-4
|
|
||||||
name: Switch to system preference
|
|
||||||
plugins:
|
|
||||||
- git-revision-date-localized:
|
|
||||||
enable_creation_date: true
|
|
||||||
- git-committers:
|
|
||||||
repository: google/glog
|
|
||||||
branch: master
|
|
||||||
- privacy
|
|
||||||
- search
|
|
||||||
- tags
|
|
||||||
extra:
|
|
||||||
version:
|
|
||||||
alias: true
|
|
||||||
default:
|
|
||||||
- dev
|
|
||||||
- stable
|
|
||||||
provider: mike
|
|
||||||
consent:
|
|
||||||
actions:
|
|
||||||
- manage
|
|
||||||
- accept
|
|
||||||
- reject
|
|
||||||
title: Cookie consent
|
|
||||||
description: >-
|
|
||||||
We use cookies to recognize your repeated visits and preferences, as well
|
|
||||||
as to measure the effectiveness of our documentation and whether users
|
|
||||||
find what they're searching for. With your consent, you're helping us to
|
|
||||||
make our documentation better.
|
|
||||||
nav:
|
|
||||||
- Getting Started:
|
|
||||||
- Overview: index.md
|
|
||||||
- Usage in CMake Projects: usage.md
|
|
||||||
- Building from Source: build.md
|
|
||||||
- Installation using Package Managers: packages.md
|
|
||||||
- User Guide:
|
|
||||||
- Logging: logging.md
|
|
||||||
- Adjusting Output: flags.md
|
|
||||||
- Custom Sinks: sinks.md
|
|
||||||
- Failure Handler: failures.md
|
|
||||||
- Log Removal: log_cleaner.md
|
|
||||||
- Stripping Log Messages: log_stripping.md
|
|
||||||
- System-specific Considerations:
|
|
||||||
- Usage on Windows: windows.md
|
|
||||||
- Linux Unwinder: unwinder.md
|
|
||||||
- Contributing: contribute.md
|
|
||||||
- License: license.md
|
|
||||||
@ -31,9 +31,6 @@
|
|||||||
/* Define if you have the 'pread' function */
|
/* Define if you have the 'pread' function */
|
||||||
#cmakedefine HAVE_PREAD
|
#cmakedefine HAVE_PREAD
|
||||||
|
|
||||||
/* Define if you have the 'posix_fadvise' function in <fcntl.h> */
|
|
||||||
#cmakedefine HAVE_POSIX_FADVISE
|
|
||||||
|
|
||||||
/* Define to 1 if you have the <pwd.h> header file. */
|
/* Define to 1 if you have the <pwd.h> header file. */
|
||||||
#cmakedefine HAVE_PWD_H
|
#cmakedefine HAVE_PWD_H
|
||||||
|
|
||||||
@ -52,15 +49,6 @@
|
|||||||
/* Define to 1 if you have the <syslog.h> header file. */
|
/* Define to 1 if you have the <syslog.h> header file. */
|
||||||
#cmakedefine HAVE_SYSLOG_H
|
#cmakedefine HAVE_SYSLOG_H
|
||||||
|
|
||||||
/* Define to 1 if you have the <elf.h> header file. */
|
|
||||||
#cmakedefine HAVE_ELF_H
|
|
||||||
|
|
||||||
/* Define to 1 if you have the <sys/exec_elf.h> header file. */
|
|
||||||
#cmakedefine HAVE_SYS_EXEC_ELF_H
|
|
||||||
|
|
||||||
/* Define to 1 if you have the <link.h> header file. */
|
|
||||||
#cmakedefine HAVE_LINK_H
|
|
||||||
|
|
||||||
/* Define to 1 if you have the <sys/syscall.h> header file. */
|
/* Define to 1 if you have the <sys/syscall.h> header file. */
|
||||||
#cmakedefine HAVE_SYS_SYSCALL_H
|
#cmakedefine HAVE_SYS_SYSCALL_H
|
||||||
|
|
||||||
@ -127,16 +115,4 @@
|
|||||||
/* define if abi::__cxa_demangle is available in cxxabi.h */
|
/* define if abi::__cxa_demangle is available in cxxabi.h */
|
||||||
#cmakedefine HAVE___CXA_DEMANGLE
|
#cmakedefine HAVE___CXA_DEMANGLE
|
||||||
|
|
||||||
/* define if __argv is available in cstdlib */
|
|
||||||
#cmakedefine HAVE___ARGV
|
|
||||||
|
|
||||||
/* define if __progname is available */
|
|
||||||
#cmakedefine HAVE___PROGNAME
|
|
||||||
|
|
||||||
/* define if getprogname is available in cstdlib */
|
|
||||||
#cmakedefine HAVE_GETPROGNAME
|
|
||||||
|
|
||||||
/* define if program_invocation_short_name is available in cerrno */
|
|
||||||
#cmakedefine HAVE_PROGRAM_INVOCATION_SHORT_NAME
|
|
||||||
|
|
||||||
#endif // GLOG_CONFIG_H
|
#endif // GLOG_CONFIG_H
|
||||||
|
|||||||
@ -37,6 +37,7 @@
|
|||||||
#include "demangle.h"
|
#include "demangle.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <cstddef>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
|
||||||
@ -51,7 +52,6 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace google {
|
namespace google {
|
||||||
inline namespace glog_internal_namespace_ {
|
|
||||||
|
|
||||||
#if !defined(GLOG_OS_WINDOWS) && !defined(HAVE___CXA_DEMANGLE)
|
#if !defined(GLOG_OS_WINDOWS) && !defined(HAVE___CXA_DEMANGLE)
|
||||||
namespace {
|
namespace {
|
||||||
@ -1359,5 +1359,4 @@ bool Demangle(const char* mangled, char* out, size_t out_size) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace glog_internal_namespace_
|
|
||||||
} // namespace google
|
} // namespace google
|
||||||
|
|||||||
@ -67,28 +67,19 @@
|
|||||||
// C++ ABI in the future.
|
// C++ ABI in the future.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef GLOG_INTERNAL_DEMANGLE_H
|
#ifndef BASE_DEMANGLE_H_
|
||||||
#define GLOG_INTERNAL_DEMANGLE_H
|
#define BASE_DEMANGLE_H_
|
||||||
|
|
||||||
#include <cstddef>
|
#include "config.h"
|
||||||
|
#include "glog/logging.h"
|
||||||
#if defined(GLOG_USE_GLOG_EXPORT)
|
|
||||||
# include "glog/export.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined(GLOG_NO_EXPORT)
|
|
||||||
# error "demangle.h" was not included correctly.
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace google {
|
namespace google {
|
||||||
inline namespace glog_internal_namespace_ {
|
|
||||||
|
|
||||||
// Demangle "mangled". On success, return true and write the
|
// Demangle "mangled". On success, return true and write the
|
||||||
// demangled symbol name to "out". Otherwise, return false.
|
// demangled symbol name to "out". Otherwise, return false.
|
||||||
// "out" is modified even if demangling is unsuccessful.
|
// "out" is modified even if demangling is unsuccessful.
|
||||||
bool GLOG_NO_EXPORT Demangle(const char* mangled, char* out, size_t out_size);
|
bool GLOG_EXPORT Demangle(const char* mangled, char* out, size_t out_size);
|
||||||
|
|
||||||
} // namespace glog_internal_namespace_
|
|
||||||
} // namespace google
|
} // namespace google
|
||||||
|
|
||||||
#endif // GLOG_INTERNAL_DEMANGLE_H
|
#endif // BASE_DEMANGLE_H_
|
||||||
|
|||||||
@ -42,7 +42,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(GLOG_EXPORT)
|
#if !defined(GLOG_EXPORT)
|
||||||
# error <glog/flags.h> was not included correctly. See the documentation for how to consume the library.
|
# error <glog/flags.h> was not included correctly. See the documention for how to consume the library.
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "glog/platform.h"
|
#include "glog/platform.h"
|
||||||
|
|||||||
@ -35,7 +35,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(GLOG_EXPORT)
|
#if !defined(GLOG_EXPORT)
|
||||||
# error <glog/log_severity.h> was not included correctly. See the documentation for how to consume the library.
|
# error <glog/log_severity.h> was not included correctly. See the documention for how to consume the library.
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace google {
|
namespace google {
|
||||||
@ -95,6 +95,8 @@ constexpr int NUM_SEVERITIES = 4;
|
|||||||
# define DFATAL_LEVEL FATAL
|
# define DFATAL_LEVEL FATAL
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
extern GLOG_EXPORT const char* const LogSeverityNames[NUM_SEVERITIES];
|
||||||
|
|
||||||
// NDEBUG usage helpers related to (RAW_)DCHECK:
|
// NDEBUG usage helpers related to (RAW_)DCHECK:
|
||||||
//
|
//
|
||||||
// DEBUG_MODE is for small !NDEBUG uses like
|
// DEBUG_MODE is for small !NDEBUG uses like
|
||||||
|
|||||||
@ -56,8 +56,8 @@
|
|||||||
# include "glog/export.h"
|
# include "glog/export.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(GLOG_EXPORT) || !defined(GLOG_NO_EXPORT)
|
#if !defined(GLOG_EXPORT)
|
||||||
# error <glog/logging.h> was not included correctly. See the documentation for how to consume the library.
|
# error <glog/logging.h> was not included correctly. See the documention for how to consume the library.
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "glog/flags.h"
|
#include "glog/flags.h"
|
||||||
@ -68,12 +68,26 @@
|
|||||||
# if __has_attribute(used)
|
# if __has_attribute(used)
|
||||||
# define GLOG_USED __attribute__((used))
|
# define GLOG_USED __attribute__((used))
|
||||||
# endif // __has_attribute(used)
|
# endif // __has_attribute(used)
|
||||||
|
# if __has_attribute(noreturn)
|
||||||
|
# define GLOG_NORETURN __attribute__((noreturn))
|
||||||
|
# endif // __has_attribute(noreturn)
|
||||||
|
# if __has_attribute(noinline)
|
||||||
|
# define GLOG_NOINLINE __attribute__((noinline))
|
||||||
|
# endif // __has_attribute(noinline)
|
||||||
#endif // defined(__has_attribute)
|
#endif // defined(__has_attribute)
|
||||||
|
|
||||||
#if !defined(GLOG_USED)
|
#if !defined(GLOG_USED)
|
||||||
# define GLOG_USED
|
# define GLOG_USED
|
||||||
#endif // !defined(GLOG_USED)
|
#endif // !defined(GLOG_USED)
|
||||||
|
|
||||||
|
#if !defined(GLOG_NORETURN)
|
||||||
|
# define GLOG_NORETURN
|
||||||
|
#endif // !defined(GLOG_NORETURN)
|
||||||
|
|
||||||
|
#if !defined(GLOG_NOINLINE)
|
||||||
|
# define GLOG_NOINLINE
|
||||||
|
#endif // !defined(GLOG_NOINLINE)
|
||||||
|
|
||||||
#include "glog/log_severity.h"
|
#include "glog/log_severity.h"
|
||||||
#include "glog/vlog_is_on.h"
|
#include "glog/vlog_is_on.h"
|
||||||
|
|
||||||
@ -83,6 +97,10 @@ struct GLOG_EXPORT LogMessageTime {
|
|||||||
LogMessageTime();
|
LogMessageTime();
|
||||||
explicit LogMessageTime(std::chrono::system_clock::time_point now);
|
explicit LogMessageTime(std::chrono::system_clock::time_point now);
|
||||||
|
|
||||||
|
[[deprecated("Use LogMessageTime::when() instead.")]] std::time_t timestamp()
|
||||||
|
const noexcept {
|
||||||
|
return std::chrono::system_clock::to_time_t(when());
|
||||||
|
}
|
||||||
const std::chrono::system_clock::time_point& when() const noexcept {
|
const std::chrono::system_clock::time_point& when() const noexcept {
|
||||||
return timestamp_;
|
return timestamp_;
|
||||||
}
|
}
|
||||||
@ -96,10 +114,18 @@ struct GLOG_EXPORT LogMessageTime {
|
|||||||
int dayOfWeek() const noexcept { return tm_.tm_wday; }
|
int dayOfWeek() const noexcept { return tm_.tm_wday; }
|
||||||
int dayInYear() const noexcept { return tm_.tm_yday; }
|
int dayInYear() const noexcept { return tm_.tm_yday; }
|
||||||
int dst() const noexcept { return tm_.tm_isdst; }
|
int dst() const noexcept { return tm_.tm_isdst; }
|
||||||
|
[[deprecated("Use LogMessageTime::gmtoffset() instead.")]] long gmtoff()
|
||||||
|
const noexcept {
|
||||||
|
return gmtoffset_.count();
|
||||||
|
}
|
||||||
std::chrono::seconds gmtoffset() const noexcept { return gmtoffset_; }
|
std::chrono::seconds gmtoffset() const noexcept { return gmtoffset_; }
|
||||||
const std::tm& tm() const noexcept { return tm_; }
|
const std::tm& tm() const noexcept { return tm_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void init(const std::tm& t, std::time_t timestamp,
|
||||||
|
std::chrono::system_clock::time_point now);
|
||||||
|
void CalcGmtOffset(std::time_t t);
|
||||||
|
|
||||||
std::tm tm_{}; // Time of creation of LogMessage
|
std::tm tm_{}; // Time of creation of LogMessage
|
||||||
std::chrono::system_clock::time_point
|
std::chrono::system_clock::time_point
|
||||||
timestamp_; // Time of creation of LogMessage in seconds
|
timestamp_; // Time of creation of LogMessage in seconds
|
||||||
@ -107,6 +133,27 @@ struct GLOG_EXPORT LogMessageTime {
|
|||||||
std::chrono::seconds gmtoffset_;
|
std::chrono::seconds gmtoffset_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct LogMessageInfo {
|
||||||
|
explicit LogMessageInfo(const char* const severity_,
|
||||||
|
const char* const filename_, const int& line_number_,
|
||||||
|
std::thread::id thread_id_,
|
||||||
|
const LogMessageTime& time_)
|
||||||
|
: severity(severity_),
|
||||||
|
filename(filename_),
|
||||||
|
line_number(line_number_),
|
||||||
|
thread_id(thread_id_),
|
||||||
|
time(time_) {}
|
||||||
|
|
||||||
|
const char* const severity;
|
||||||
|
const char* const filename;
|
||||||
|
const int& line_number;
|
||||||
|
std::thread::id thread_id;
|
||||||
|
const LogMessageTime& time;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef void (*CustomPrefixCallback)(std::ostream& s, const LogMessageInfo& l,
|
||||||
|
void* data);
|
||||||
|
|
||||||
} // namespace google
|
} // namespace google
|
||||||
|
|
||||||
// The global value of GOOGLE_STRIP_LOG. All the messages logged to
|
// The global value of GOOGLE_STRIP_LOG. All the messages logged to
|
||||||
@ -456,31 +503,25 @@ namespace google {
|
|||||||
// specified by argv0 in log outputs.
|
// specified by argv0 in log outputs.
|
||||||
GLOG_EXPORT void InitGoogleLogging(const char* argv0);
|
GLOG_EXPORT void InitGoogleLogging(const char* argv0);
|
||||||
|
|
||||||
|
GLOG_EXPORT void InitGoogleLogging(const char* argv0,
|
||||||
|
CustomPrefixCallback prefix_callback,
|
||||||
|
void* prefix_callback_data = nullptr);
|
||||||
|
|
||||||
// Check if google's logging library has been initialized.
|
// Check if google's logging library has been initialized.
|
||||||
GLOG_EXPORT bool IsGoogleLoggingInitialized();
|
GLOG_EXPORT bool IsGoogleLoggingInitialized();
|
||||||
|
|
||||||
// Shutdown google's logging library.
|
// Shutdown google's logging library.
|
||||||
GLOG_EXPORT void ShutdownGoogleLogging();
|
GLOG_EXPORT void ShutdownGoogleLogging();
|
||||||
|
|
||||||
#if defined(__GNUC__)
|
typedef void (*logging_fail_func_t)() GLOG_NORETURN;
|
||||||
typedef void (*logging_fail_func_t)() __attribute__((noreturn));
|
|
||||||
#else
|
|
||||||
typedef void (*logging_fail_func_t)();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class LogMessage;
|
// Install a function which will be called after LOG(FATAL).
|
||||||
|
GLOG_EXPORT void InstallFailureFunction(logging_fail_func_t fail_func);
|
||||||
using PrefixFormatterCallback = void (*)(std::ostream&, const LogMessage&,
|
|
||||||
void*);
|
|
||||||
|
|
||||||
GLOG_EXPORT void InstallPrefixFormatter(PrefixFormatterCallback callback,
|
|
||||||
void* data = nullptr);
|
|
||||||
|
|
||||||
// Install a function which will be called after LOG(FATAL). Returns the
|
|
||||||
// previously set function.
|
|
||||||
GLOG_EXPORT logging_fail_func_t
|
|
||||||
InstallFailureFunction(logging_fail_func_t fail_func);
|
|
||||||
|
|
||||||
|
[[deprecated(
|
||||||
|
"Use the type-safe std::chrono::minutes EnableLogCleaner overload "
|
||||||
|
"instead.")]] GLOG_EXPORT void
|
||||||
|
EnableLogCleaner(unsigned int overdue_days);
|
||||||
// Enable/Disable old log cleaner.
|
// Enable/Disable old log cleaner.
|
||||||
GLOG_EXPORT void EnableLogCleaner(const std::chrono::minutes& overdue);
|
GLOG_EXPORT void EnableLogCleaner(const std::chrono::minutes& overdue);
|
||||||
GLOG_EXPORT void DisableLogCleaner();
|
GLOG_EXPORT void DisableLogCleaner();
|
||||||
@ -531,14 +572,10 @@ class LogSink; // defined below
|
|||||||
|
|
||||||
#define LOG_IF(severity, condition) \
|
#define LOG_IF(severity, condition) \
|
||||||
static_cast<void>(0), \
|
static_cast<void>(0), \
|
||||||
!(condition) \
|
!(condition) ? (void)0 : google::LogMessageVoidify() & LOG(severity)
|
||||||
? (void)0 \
|
|
||||||
: google::logging::internal::LogMessageVoidify() & LOG(severity)
|
|
||||||
#define SYSLOG_IF(severity, condition) \
|
#define SYSLOG_IF(severity, condition) \
|
||||||
static_cast<void>(0), \
|
static_cast<void>(0), \
|
||||||
!(condition) \
|
!(condition) ? (void)0 : google::LogMessageVoidify() & SYSLOG(severity)
|
||||||
? (void)0 \
|
|
||||||
: google::logging::internal::LogMessageVoidify() & SYSLOG(severity)
|
|
||||||
|
|
||||||
#define LOG_ASSERT(condition) \
|
#define LOG_ASSERT(condition) \
|
||||||
LOG_IF(FATAL, !(condition)) << "Assert failed: " #condition
|
LOG_IF(FATAL, !(condition)) << "Assert failed: " #condition
|
||||||
@ -553,17 +590,16 @@ class LogSink; // defined below
|
|||||||
LOG_IF(FATAL, GOOGLE_PREDICT_BRANCH_NOT_TAKEN(!(condition))) \
|
LOG_IF(FATAL, GOOGLE_PREDICT_BRANCH_NOT_TAKEN(!(condition))) \
|
||||||
<< "Check failed: " #condition " "
|
<< "Check failed: " #condition " "
|
||||||
|
|
||||||
namespace logging {
|
|
||||||
namespace internal {
|
|
||||||
|
|
||||||
// A container for a string pointer which can be evaluated to a bool -
|
// A container for a string pointer which can be evaluated to a bool -
|
||||||
// true iff the pointer is nullptr.
|
// true iff the pointer is nullptr.
|
||||||
struct CheckOpString {
|
struct CheckOpString {
|
||||||
CheckOpString(std::unique_ptr<std::string> str) : str_(std::move(str)) {}
|
CheckOpString(std::string* str) : str_(str) {}
|
||||||
explicit operator bool() const noexcept {
|
// No destructor: if str_ is non-nullptr, we're about to LOG(FATAL),
|
||||||
|
// so there's no point in cleaning up str_.
|
||||||
|
operator bool() const {
|
||||||
return GOOGLE_PREDICT_BRANCH_NOT_TAKEN(str_ != nullptr);
|
return GOOGLE_PREDICT_BRANCH_NOT_TAKEN(str_ != nullptr);
|
||||||
}
|
}
|
||||||
std::unique_ptr<std::string> str_;
|
std::string* str_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Function is overloaded for integral types to allow static const
|
// Function is overloaded for integral types to allow static const
|
||||||
@ -590,14 +626,18 @@ inline unsigned long long GetReferenceableValue(unsigned long long t) {
|
|||||||
// This is a dummy class to define the following operator.
|
// This is a dummy class to define the following operator.
|
||||||
struct DummyClassToDefineOperator {};
|
struct DummyClassToDefineOperator {};
|
||||||
|
|
||||||
|
} // namespace google
|
||||||
|
|
||||||
// Define global operator<< to declare using ::operator<<.
|
// Define global operator<< to declare using ::operator<<.
|
||||||
// This declaration will allow use to use CHECK macros for user
|
// This declaration will allow use to use CHECK macros for user
|
||||||
// defined classes which have operator<< (e.g., stl_logging.h).
|
// defined classes which have operator<< (e.g., stl_logging.h).
|
||||||
inline std::ostream& operator<<(std::ostream& out,
|
inline std::ostream& operator<<(std::ostream& out,
|
||||||
const DummyClassToDefineOperator&) {
|
const google::DummyClassToDefineOperator&) {
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace google {
|
||||||
|
|
||||||
// This formats a value for a failing CHECK_XX statement. Ordinarily,
|
// This formats a value for a failing CHECK_XX statement. Ordinarily,
|
||||||
// it uses the definition for operator<<, with a few special cases below.
|
// it uses the definition for operator<<, with a few special cases below.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@ -622,14 +662,10 @@ GLOG_EXPORT void MakeCheckOpValueString(std::ostream* os,
|
|||||||
|
|
||||||
// Build the error message string. Specify no inlining for code size.
|
// Build the error message string. Specify no inlining for code size.
|
||||||
template <typename T1, typename T2>
|
template <typename T1, typename T2>
|
||||||
std::unique_ptr<std::string> MakeCheckOpString(const T1& v1, const T2& v2,
|
std::string* MakeCheckOpString(const T1& v1, const T2& v2,
|
||||||
const char* exprtext)
|
const char* exprtext) GLOG_NOINLINE;
|
||||||
#if defined(__has_attribute)
|
|
||||||
# if __has_attribute(used)
|
namespace base {
|
||||||
__attribute__((noinline))
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
;
|
|
||||||
|
|
||||||
// A helper class for formatting "expr (V1 vs. V2)" in a CHECK_XX
|
// A helper class for formatting "expr (V1 vs. V2)" in a CHECK_XX
|
||||||
// statement. See MakeCheckOpString for sample usage. Other
|
// statement. See MakeCheckOpString for sample usage. Other
|
||||||
@ -648,16 +684,18 @@ class GLOG_EXPORT CheckOpMessageBuilder {
|
|||||||
// For inserting the second variable (adds an intermediate " vs. ").
|
// For inserting the second variable (adds an intermediate " vs. ").
|
||||||
std::ostream* ForVar2();
|
std::ostream* ForVar2();
|
||||||
// Get the result (inserts the closing ")").
|
// Get the result (inserts the closing ")").
|
||||||
std::unique_ptr<std::string> NewString();
|
std::string* NewString();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::ostringstream* stream_;
|
std::ostringstream* stream_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
} // namespace base
|
||||||
|
|
||||||
template <typename T1, typename T2>
|
template <typename T1, typename T2>
|
||||||
std::unique_ptr<std::string> MakeCheckOpString(const T1& v1, const T2& v2,
|
std::string* MakeCheckOpString(const T1& v1, const T2& v2,
|
||||||
const char* exprtext) {
|
const char* exprtext) {
|
||||||
CheckOpMessageBuilder comb(exprtext);
|
base::CheckOpMessageBuilder comb(exprtext);
|
||||||
MakeCheckOpValueString(comb.ForVar1(), v1);
|
MakeCheckOpValueString(comb.ForVar1(), v1);
|
||||||
MakeCheckOpValueString(comb.ForVar2(), v2);
|
MakeCheckOpValueString(comb.ForVar2(), v2);
|
||||||
return comb.NewString();
|
return comb.NewString();
|
||||||
@ -667,31 +705,30 @@ std::unique_ptr<std::string> MakeCheckOpString(const T1& v1, const T2& v2,
|
|||||||
// The (int, int) specialization works around the issue that the compiler
|
// The (int, int) specialization works around the issue that the compiler
|
||||||
// will not instantiate the template version of the function on values of
|
// will not instantiate the template version of the function on values of
|
||||||
// unnamed enum type - see comment below.
|
// unnamed enum type - see comment below.
|
||||||
#define DEFINE_CHECK_OP_IMPL(name, op) \
|
#define DEFINE_CHECK_OP_IMPL(name, op) \
|
||||||
template <typename T1, typename T2> \
|
template <typename T1, typename T2> \
|
||||||
inline std::unique_ptr<std::string> name##Impl(const T1& v1, const T2& v2, \
|
inline std::string* name##Impl(const T1& v1, const T2& v2, \
|
||||||
const char* exprtext) { \
|
const char* exprtext) { \
|
||||||
if (GOOGLE_PREDICT_TRUE(v1 op v2)) { \
|
if (GOOGLE_PREDICT_TRUE(v1 op v2)) \
|
||||||
return nullptr; \
|
return nullptr; \
|
||||||
} \
|
else \
|
||||||
return MakeCheckOpString(v1, v2, exprtext); \
|
return MakeCheckOpString(v1, v2, exprtext); \
|
||||||
} \
|
} \
|
||||||
inline std::unique_ptr<std::string> name##Impl(int v1, int v2, \
|
inline std::string* name##Impl(int v1, int v2, const char* exprtext) { \
|
||||||
const char* exprtext) { \
|
return name##Impl<int, int>(v1, v2, exprtext); \
|
||||||
return name##Impl<int, int>(v1, v2, exprtext); \
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We use the full name Check_EQ, Check_NE, etc. in case the file including
|
// We use the full name Check_EQ, Check_NE, etc. in case the file including
|
||||||
// base/logging.h provides its own #defines for the simpler names EQ, NE, etc.
|
// base/logging.h provides its own #defines for the simpler names EQ, NE, etc.
|
||||||
// This happens if, for example, those are used as token names in a
|
// This happens if, for example, those are used as token names in a
|
||||||
// yacc grammar.
|
// yacc grammar.
|
||||||
DEFINE_CHECK_OP_IMPL(Check_EQ, ==)
|
DEFINE_CHECK_OP_IMPL(Check_EQ,
|
||||||
DEFINE_CHECK_OP_IMPL(Check_NE, !=)
|
==) // Compilation error with CHECK_EQ(nullptr, x)?
|
||||||
|
DEFINE_CHECK_OP_IMPL(Check_NE, !=) // Use CHECK(x == nullptr) instead.
|
||||||
DEFINE_CHECK_OP_IMPL(Check_LE, <=)
|
DEFINE_CHECK_OP_IMPL(Check_LE, <=)
|
||||||
DEFINE_CHECK_OP_IMPL(Check_LT, <)
|
DEFINE_CHECK_OP_IMPL(Check_LT, <)
|
||||||
DEFINE_CHECK_OP_IMPL(Check_GE, >=)
|
DEFINE_CHECK_OP_IMPL(Check_GE, >=)
|
||||||
DEFINE_CHECK_OP_IMPL(Check_GT, >)
|
DEFINE_CHECK_OP_IMPL(Check_GT, >)
|
||||||
|
|
||||||
#undef DEFINE_CHECK_OP_IMPL
|
#undef DEFINE_CHECK_OP_IMPL
|
||||||
|
|
||||||
// Helper macro for binary operators.
|
// Helper macro for binary operators.
|
||||||
@ -710,25 +747,19 @@ DEFINE_CHECK_OP_IMPL(Check_GT, >)
|
|||||||
// with other string implementations that get defined after this
|
// with other string implementations that get defined after this
|
||||||
// file is included). Save the current meaning now and use it
|
// file is included). Save the current meaning now and use it
|
||||||
// in the macro.
|
// in the macro.
|
||||||
using _Check_string = std::string;
|
typedef std::string _Check_string;
|
||||||
# define CHECK_OP_LOG(name, op, val1, val2, log) \
|
# define CHECK_OP_LOG(name, op, val1, val2, log) \
|
||||||
while (std::unique_ptr<google::logging::internal::_Check_string> _result = \
|
while (google::_Check_string* _result = google::Check##name##Impl( \
|
||||||
google::logging::internal::Check##name##Impl( \
|
google::GetReferenceableValue(val1), \
|
||||||
google::logging::internal::GetReferenceableValue(val1), \
|
google::GetReferenceableValue(val2), #val1 " " #op " " #val2)) \
|
||||||
google::logging::internal::GetReferenceableValue(val2), \
|
log(__FILE__, __LINE__, google::CheckOpString(_result)).stream()
|
||||||
#val1 " " #op " " #val2)) \
|
|
||||||
log(__FILE__, __LINE__, \
|
|
||||||
google::logging::internal::CheckOpString(std::move(_result))) \
|
|
||||||
.stream()
|
|
||||||
#else
|
#else
|
||||||
// In optimized mode, use CheckOpString to hint to compiler that
|
// In optimized mode, use CheckOpString to hint to compiler that
|
||||||
// the while condition is unlikely.
|
// the while condition is unlikely.
|
||||||
# define CHECK_OP_LOG(name, op, val1, val2, log) \
|
# define CHECK_OP_LOG(name, op, val1, val2, log) \
|
||||||
while (google::logging::internal::CheckOpString _result = \
|
while (google::CheckOpString _result = google::Check##name##Impl( \
|
||||||
google::logging::internal::Check##name##Impl( \
|
google::GetReferenceableValue(val1), \
|
||||||
google::logging::internal::GetReferenceableValue(val1), \
|
google::GetReferenceableValue(val2), #val1 " " #op " " #val2)) \
|
||||||
google::logging::internal::GetReferenceableValue(val2), \
|
|
||||||
#val1 " " #op " " #val2)) \
|
|
||||||
log(__FILE__, __LINE__, _result).stream()
|
log(__FILE__, __LINE__, _result).stream()
|
||||||
#endif // STATIC_ANALYSIS, DCHECK_IS_ON()
|
#endif // STATIC_ANALYSIS, DCHECK_IS_ON()
|
||||||
|
|
||||||
@ -768,33 +799,27 @@ using _Check_string = std::string;
|
|||||||
// Check that the input is non nullptr. This very useful in constructor
|
// Check that the input is non nullptr. This very useful in constructor
|
||||||
// initializer lists.
|
// initializer lists.
|
||||||
|
|
||||||
#define CHECK_NOTNULL(val) \
|
#define CHECK_NOTNULL(val) \
|
||||||
google::logging::internal::CheckNotNull( \
|
google::CheckNotNull(__FILE__, __LINE__, "'" #val "' Must be non nullptr", \
|
||||||
__FILE__, __LINE__, "'" #val "' Must be non nullptr", (val))
|
(val))
|
||||||
|
|
||||||
// Helper functions for string comparisons.
|
// Helper functions for string comparisons.
|
||||||
// To avoid bloat, the definitions are in logging.cc.
|
// To avoid bloat, the definitions are in logging.cc.
|
||||||
#define DECLARE_CHECK_STROP_IMPL(func, expected) \
|
#define DECLARE_CHECK_STROP_IMPL(func, expected) \
|
||||||
GLOG_EXPORT std::unique_ptr<std::string> Check##func##expected##Impl( \
|
GLOG_EXPORT std::string* Check##func##expected##Impl( \
|
||||||
const char* s1, const char* s2, const char* names);
|
const char* s1, const char* s2, const char* names);
|
||||||
|
|
||||||
DECLARE_CHECK_STROP_IMPL(strcmp, true)
|
DECLARE_CHECK_STROP_IMPL(strcmp, true)
|
||||||
DECLARE_CHECK_STROP_IMPL(strcmp, false)
|
DECLARE_CHECK_STROP_IMPL(strcmp, false)
|
||||||
DECLARE_CHECK_STROP_IMPL(strcasecmp, true)
|
DECLARE_CHECK_STROP_IMPL(strcasecmp, true)
|
||||||
DECLARE_CHECK_STROP_IMPL(strcasecmp, false)
|
DECLARE_CHECK_STROP_IMPL(strcasecmp, false)
|
||||||
|
|
||||||
} // namespace internal
|
|
||||||
} // namespace logging
|
|
||||||
|
|
||||||
#undef DECLARE_CHECK_STROP_IMPL
|
#undef DECLARE_CHECK_STROP_IMPL
|
||||||
|
|
||||||
// Helper macro for string comparisons.
|
// Helper macro for string comparisons.
|
||||||
// Don't use this macro directly in your code, use CHECK_STREQ et al below.
|
// Don't use this macro directly in your code, use CHECK_STREQ et al below.
|
||||||
#define CHECK_STROP(func, op, expected, s1, s2) \
|
#define CHECK_STROP(func, op, expected, s1, s2) \
|
||||||
while (google::logging::internal::CheckOpString _result = \
|
while (google::CheckOpString _result = google::Check##func##expected##Impl( \
|
||||||
google::logging::internal::Check##func##expected##Impl( \
|
(s1), (s2), #s1 " " #op " " #s2)) \
|
||||||
(s1), (s2), #s1 " " #op " " #s2)) \
|
LOG(FATAL) << *_result.str_
|
||||||
LOG(FATAL) << (*_result.str_)
|
|
||||||
|
|
||||||
// String (char*) equality/inequality checks.
|
// String (char*) equality/inequality checks.
|
||||||
// CASE versions are case-insensitive.
|
// CASE versions are case-insensitive.
|
||||||
@ -837,9 +862,7 @@ DECLARE_CHECK_STROP_IMPL(strcasecmp, false)
|
|||||||
|
|
||||||
#define PLOG_IF(severity, condition) \
|
#define PLOG_IF(severity, condition) \
|
||||||
static_cast<void>(0), \
|
static_cast<void>(0), \
|
||||||
!(condition) \
|
!(condition) ? (void)0 : google::LogMessageVoidify() & PLOG(severity)
|
||||||
? (void)0 \
|
|
||||||
: google::logging::internal::LogMessageVoidify() & PLOG(severity)
|
|
||||||
|
|
||||||
// A CHECK() macro that postpends errno if the condition is false. E.g.
|
// A CHECK() macro that postpends errno if the condition is false. E.g.
|
||||||
//
|
//
|
||||||
@ -951,13 +974,12 @@ namespace google {
|
|||||||
LOG_OCCURRENCES, &what_to_do) \
|
LOG_OCCURRENCES, &what_to_do) \
|
||||||
.stream()
|
.stream()
|
||||||
|
|
||||||
namespace logging {
|
namespace glog_internal_namespace_ {
|
||||||
namespace internal {
|
|
||||||
template <bool>
|
template <bool>
|
||||||
struct CompileAssert {};
|
struct CompileAssert {};
|
||||||
struct CrashReason;
|
struct CrashReason;
|
||||||
} // namespace internal
|
|
||||||
} // namespace logging
|
} // namespace glog_internal_namespace_
|
||||||
|
|
||||||
#define LOG_EVERY_N(severity, n) \
|
#define LOG_EVERY_N(severity, n) \
|
||||||
SOME_KIND_OF_LOG_EVERY_N(severity, (n), google::LogMessage::SendToLog)
|
SOME_KIND_OF_LOG_EVERY_N(severity, (n), google::LogMessage::SendToLog)
|
||||||
@ -1038,41 +1060,34 @@ constexpr LogSeverity GLOG_0 = GLOG_ERROR;
|
|||||||
|
|
||||||
# define DLOG(severity) \
|
# define DLOG(severity) \
|
||||||
static_cast<void>(0), \
|
static_cast<void>(0), \
|
||||||
true ? (void)0 \
|
true ? (void)0 : google::LogMessageVoidify() & LOG(severity)
|
||||||
: google::logging::internal::LogMessageVoidify() & LOG(severity)
|
|
||||||
|
|
||||||
# define DVLOG(verboselevel) \
|
# define DVLOG(verboselevel) \
|
||||||
static_cast<void>(0), \
|
static_cast<void>(0), (true || !VLOG_IS_ON(verboselevel)) \
|
||||||
(true || !VLOG_IS_ON(verboselevel)) \
|
? (void)0 \
|
||||||
? (void)0 \
|
: google::LogMessageVoidify() & LOG(INFO)
|
||||||
: google::logging::internal::LogMessageVoidify() & LOG(INFO)
|
|
||||||
|
|
||||||
# define DLOG_IF(severity, condition) \
|
# define DLOG_IF(severity, condition) \
|
||||||
static_cast<void>(0), \
|
static_cast<void>(0), (true || !(condition)) \
|
||||||
(true || !(condition)) \
|
? (void)0 \
|
||||||
? (void)0 \
|
: google::LogMessageVoidify() & LOG(severity)
|
||||||
: google::logging::internal::LogMessageVoidify() & LOG(severity)
|
|
||||||
|
|
||||||
# define DLOG_EVERY_N(severity, n) \
|
# define DLOG_EVERY_N(severity, n) \
|
||||||
static_cast<void>(0), \
|
static_cast<void>(0), \
|
||||||
true ? (void)0 \
|
true ? (void)0 : google::LogMessageVoidify() & LOG(severity)
|
||||||
: google::logging::internal::LogMessageVoidify() & LOG(severity)
|
|
||||||
|
|
||||||
# define DLOG_IF_EVERY_N(severity, condition, n) \
|
# define DLOG_IF_EVERY_N(severity, condition, n) \
|
||||||
static_cast<void>(0), \
|
static_cast<void>(0), (true || !(condition)) \
|
||||||
(true || !(condition)) \
|
? (void)0 \
|
||||||
? (void)0 \
|
: google::LogMessageVoidify() & LOG(severity)
|
||||||
: google::logging::internal::LogMessageVoidify() & LOG(severity)
|
|
||||||
|
|
||||||
# define DLOG_FIRST_N(severity, n) \
|
# define DLOG_FIRST_N(severity, n) \
|
||||||
static_cast<void>(0), \
|
static_cast<void>(0), \
|
||||||
true ? (void)0 \
|
true ? (void)0 : google::LogMessageVoidify() & LOG(severity)
|
||||||
: google::logging::internal::LogMessageVoidify() & LOG(severity)
|
|
||||||
|
|
||||||
# define DLOG_EVERY_T(severity, T) \
|
# define DLOG_EVERY_T(severity, T) \
|
||||||
static_cast<void>(0), \
|
static_cast<void>(0), \
|
||||||
true ? (void)0 \
|
true ? (void)0 : google::LogMessageVoidify() & LOG(severity)
|
||||||
: google::logging::internal::LogMessageVoidify() & LOG(severity)
|
|
||||||
|
|
||||||
# define DLOG_ASSERT(condition) \
|
# define DLOG_ASSERT(condition) \
|
||||||
static_cast<void>(0), true ? (void)0 : (LOG_ASSERT(condition))
|
static_cast<void>(0), true ? (void)0 : (LOG_ASSERT(condition))
|
||||||
@ -1161,12 +1176,6 @@ class GLOG_EXPORT LogStreamBuf : public std::streambuf {
|
|||||||
|
|
||||||
} // namespace base_logging
|
} // namespace base_logging
|
||||||
|
|
||||||
namespace logging {
|
|
||||||
namespace internal {
|
|
||||||
struct GLOG_NO_EXPORT LogMessageData;
|
|
||||||
} // namespace internal
|
|
||||||
} // namespace logging
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// This class more or less represents a particular log message. You
|
// This class more or less represents a particular log message. You
|
||||||
// create an instance of LogMessage and then stream stuff to it.
|
// create an instance of LogMessage and then stream stuff to it.
|
||||||
@ -1283,10 +1292,9 @@ class GLOG_EXPORT LogMessage {
|
|||||||
std::string* message);
|
std::string* message);
|
||||||
|
|
||||||
// A special constructor used for check failures
|
// A special constructor used for check failures
|
||||||
LogMessage(const char* file, int line,
|
LogMessage(const char* file, int line, const CheckOpString& result);
|
||||||
const logging::internal::CheckOpString& result);
|
|
||||||
|
|
||||||
~LogMessage() noexcept(false);
|
~LogMessage();
|
||||||
|
|
||||||
// Flush a buffered message to the sink set in the constructor. Always
|
// Flush a buffered message to the sink set in the constructor. Always
|
||||||
// called by the destructor, it may also be called from elsewhere if
|
// called by the destructor, it may also be called from elsewhere if
|
||||||
@ -1312,15 +1320,14 @@ class GLOG_EXPORT LogMessage {
|
|||||||
// Must be called without the log_mutex held. (L < log_mutex)
|
// Must be called without the log_mutex held. (L < log_mutex)
|
||||||
static int64 num_messages(int severity);
|
static int64 num_messages(int severity);
|
||||||
|
|
||||||
LogSeverity severity() const noexcept;
|
[[deprecated("Use LogMessage::time() instead.")]] const LogMessageTime&
|
||||||
int line() const noexcept;
|
getLogMessageTime() const {
|
||||||
const std::thread::id& thread_id() const noexcept;
|
return time();
|
||||||
const char* fullname() const noexcept;
|
}
|
||||||
const char* basename() const noexcept;
|
|
||||||
const LogMessageTime& time() const noexcept;
|
|
||||||
|
|
||||||
LogMessage(const LogMessage&) = delete;
|
const LogMessageTime& time() const;
|
||||||
LogMessage& operator=(const LogMessage&) = delete;
|
|
||||||
|
struct LogMessageData;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Fully internal SendMethod cases:
|
// Fully internal SendMethod cases:
|
||||||
@ -1336,18 +1343,21 @@ class GLOG_EXPORT LogMessage {
|
|||||||
void (LogMessage::*send_method)());
|
void (LogMessage::*send_method)());
|
||||||
|
|
||||||
// Used to fill in crash information during LOG(FATAL) failures.
|
// Used to fill in crash information during LOG(FATAL) failures.
|
||||||
void RecordCrashReason(logging::internal::CrashReason* reason);
|
void RecordCrashReason(glog_internal_namespace_::CrashReason* reason);
|
||||||
|
|
||||||
// Counts of messages sent at each priority:
|
// Counts of messages sent at each priority:
|
||||||
static int64 num_messages_[NUM_SEVERITIES]; // under log_mutex
|
static int64 num_messages_[NUM_SEVERITIES]; // under log_mutex
|
||||||
|
|
||||||
// We keep the data in a separate struct so that each instance of
|
// We keep the data in a separate struct so that each instance of
|
||||||
// LogMessage uses less stack space.
|
// LogMessage uses less stack space.
|
||||||
logging::internal::LogMessageData* allocated_;
|
LogMessageData* allocated_;
|
||||||
logging::internal::LogMessageData* data_;
|
LogMessageData* data_;
|
||||||
LogMessageTime time_;
|
LogMessageTime time_;
|
||||||
|
|
||||||
friend class LogDestination;
|
friend class LogDestination;
|
||||||
|
|
||||||
|
LogMessage(const LogMessage&);
|
||||||
|
void operator=(const LogMessage&);
|
||||||
};
|
};
|
||||||
|
|
||||||
// This class happens to be thread-hostile because all instances share
|
// This class happens to be thread-hostile because all instances share
|
||||||
@ -1356,9 +1366,8 @@ class GLOG_EXPORT LogMessage {
|
|||||||
class GLOG_EXPORT LogMessageFatal : public LogMessage {
|
class GLOG_EXPORT LogMessageFatal : public LogMessage {
|
||||||
public:
|
public:
|
||||||
LogMessageFatal(const char* file, int line);
|
LogMessageFatal(const char* file, int line);
|
||||||
LogMessageFatal(const char* file, int line,
|
LogMessageFatal(const char* file, int line, const CheckOpString& result);
|
||||||
const logging::internal::CheckOpString& result);
|
[[noreturn]] ~LogMessageFatal();
|
||||||
[[noreturn]] ~LogMessageFatal() noexcept(false);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// A non-macro interface to the log facility; (useful
|
// A non-macro interface to the log facility; (useful
|
||||||
@ -1374,6 +1383,22 @@ inline void LogAtLevel(LogSeverity severity, std::string const& msg) {
|
|||||||
#define LOG_AT_LEVEL(severity) \
|
#define LOG_AT_LEVEL(severity) \
|
||||||
google::LogMessage(__FILE__, __LINE__, severity).stream()
|
google::LogMessage(__FILE__, __LINE__, severity).stream()
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
// Allow folks to put a counter in the LOG_EVERY_X()'ed messages. This
|
// 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
|
// only works if ostream is a LogStream. If the ostream is not a
|
||||||
// LogStream you'll get an assert saying as much at runtime.
|
// LogStream you'll get an assert saying as much at runtime.
|
||||||
@ -1397,34 +1422,14 @@ class GLOG_EXPORT ErrnoLogMessage : public LogMessage {
|
|||||||
// logging macros. This avoids compiler warnings like "value computed
|
// logging macros. This avoids compiler warnings like "value computed
|
||||||
// is not used" and "statement has no effect".
|
// is not used" and "statement has no effect".
|
||||||
|
|
||||||
namespace logging {
|
class GLOG_EXPORT LogMessageVoidify {
|
||||||
namespace internal {
|
public:
|
||||||
|
LogMessageVoidify() {}
|
||||||
// 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, std::make_unique<std::string>(names));
|
|
||||||
}
|
|
||||||
return std::forward<T>(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct LogMessageVoidify {
|
|
||||||
// This has to be an operator with a precedence lower than << but
|
// This has to be an operator with a precedence lower than << but
|
||||||
// higher than ?:
|
// higher than ?:
|
||||||
constexpr void operator&(std::ostream&) const noexcept {}
|
void operator&(std::ostream&) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace internal
|
|
||||||
} // namespace logging
|
|
||||||
|
|
||||||
// Flushes all log files that contains messages that are at least of
|
// Flushes all log files that contains messages that are at least of
|
||||||
// the specified severity level. Thread-safe.
|
// the specified severity level. Thread-safe.
|
||||||
GLOG_EXPORT void FlushLogFiles(LogSeverity min_severity);
|
GLOG_EXPORT void FlushLogFiles(LogSeverity min_severity);
|
||||||
@ -1465,8 +1470,13 @@ class GLOG_EXPORT LogSink {
|
|||||||
// during this call.
|
// during this call.
|
||||||
virtual void send(LogSeverity severity, const char* full_filename,
|
virtual void send(LogSeverity severity, const char* full_filename,
|
||||||
const char* base_filename, int line,
|
const char* base_filename, int line,
|
||||||
const LogMessageTime& time, const char* message,
|
const LogMessageTime& logmsgtime, const char* message,
|
||||||
size_t message_len) = 0;
|
size_t message_len);
|
||||||
|
// Provide an overload for compatibility purposes
|
||||||
|
GLOG_DEPRECATED
|
||||||
|
virtual void send(LogSeverity severity, const char* full_filename,
|
||||||
|
const char* base_filename, int line, const std::tm* t,
|
||||||
|
const char* message, size_t message_len);
|
||||||
|
|
||||||
// Redefine this to implement waiting for
|
// Redefine this to implement waiting for
|
||||||
// the sink's logging logic to complete.
|
// the sink's logging logic to complete.
|
||||||
@ -1486,8 +1496,8 @@ class GLOG_EXPORT LogSink {
|
|||||||
// Returns the normal text output of the log message.
|
// Returns the normal text output of the log message.
|
||||||
// Can be useful to implement send().
|
// Can be useful to implement send().
|
||||||
static std::string ToString(LogSeverity severity, const char* file, int line,
|
static std::string ToString(LogSeverity severity, const char* file, int line,
|
||||||
const LogMessageTime& time, const char* message,
|
const LogMessageTime& logmsgtime,
|
||||||
size_t message_len);
|
const char* message, size_t message_len);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add or remove a LogSink as a consumer of logging data. Thread-safe.
|
// Add or remove a LogSink as a consumer of logging data. Thread-safe.
|
||||||
@ -1578,9 +1588,15 @@ class GLOG_EXPORT Logger {
|
|||||||
// appropriate by the higher level logging facility. For example,
|
// appropriate by the higher level logging facility. For example,
|
||||||
// textual log messages already contain timestamps, and the
|
// textual log messages already contain timestamps, and the
|
||||||
// file:linenumber header.
|
// file:linenumber header.
|
||||||
|
[[deprecated(
|
||||||
|
"Logger::Write accepting a std::time_t timestamp is provided for "
|
||||||
|
"compatibility purposes only. New code should implement the "
|
||||||
|
"std::chrono::system_clock::time_point overload.")]] virtual void
|
||||||
|
Write(bool force_flush, time_t timestamp, const char* message,
|
||||||
|
size_t message_len);
|
||||||
virtual void Write(bool force_flush,
|
virtual void Write(bool force_flush,
|
||||||
const std::chrono::system_clock::time_point& timestamp,
|
const std::chrono::system_clock::time_point& timestamp,
|
||||||
const char* message, size_t message_len) = 0;
|
const char* message, size_t message_len);
|
||||||
|
|
||||||
// Flush any buffered messages
|
// Flush any buffered messages
|
||||||
virtual void Flush() = 0;
|
virtual void Flush() = 0;
|
||||||
@ -1612,7 +1628,7 @@ class GLOG_EXPORT NullStream : public LogMessage::LogStream {
|
|||||||
// the overloaded NullStream::operator<< will not be invoked.
|
// the overloaded NullStream::operator<< will not be invoked.
|
||||||
NullStream();
|
NullStream();
|
||||||
NullStream(const char* /*file*/, int /*line*/,
|
NullStream(const char* /*file*/, int /*line*/,
|
||||||
const logging::internal::CheckOpString& /*result*/);
|
const CheckOpString& /*result*/);
|
||||||
NullStream& stream();
|
NullStream& stream();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -1671,9 +1687,6 @@ GLOG_EXPORT bool IsFailureSignalHandlerInstalled();
|
|||||||
GLOG_EXPORT void InstallFailureWriter(void (*writer)(const char* data,
|
GLOG_EXPORT void InstallFailureWriter(void (*writer)(const char* data,
|
||||||
size_t size));
|
size_t size));
|
||||||
|
|
||||||
// Dump stack trace as a string.
|
|
||||||
GLOG_EXPORT std::string GetStackTrace();
|
|
||||||
|
|
||||||
} // namespace google
|
} // namespace google
|
||||||
|
|
||||||
#endif // GLOG_LOGGING_H
|
#endif // GLOG_LOGGING_H
|
||||||
|
|||||||
@ -39,9 +39,8 @@
|
|||||||
#elif defined(__CYGWIN__) || defined(__CYGWIN32__)
|
#elif defined(__CYGWIN__) || defined(__CYGWIN32__)
|
||||||
# define GLOG_OS_CYGWIN
|
# define GLOG_OS_CYGWIN
|
||||||
#elif defined(linux) || defined(__linux) || defined(__linux__)
|
#elif defined(linux) || defined(__linux) || defined(__linux__)
|
||||||
# define GLOG_OS_LINUX
|
# ifndef GLOG_OS_LINUX
|
||||||
# if defined(__ANDROID__)
|
# define GLOG_OS_LINUX
|
||||||
# define GLOG_OS_ANDROID
|
|
||||||
# endif
|
# endif
|
||||||
#elif defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__)
|
#elif defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__)
|
||||||
# define GLOG_OS_MACOSX
|
# define GLOG_OS_MACOSX
|
||||||
|
|||||||
@ -41,7 +41,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(GLOG_EXPORT)
|
#if !defined(GLOG_EXPORT)
|
||||||
# error <glog/raw_logging.h> was not included correctly. See the documentation for how to consume the library.
|
# error <glog/raw_logging.h> was not included correctly. See the documention for how to consume the library.
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "glog/log_severity.h"
|
#include "glog/log_severity.h"
|
||||||
|
|||||||
@ -68,7 +68,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(GLOG_EXPORT)
|
#if !defined(GLOG_EXPORT)
|
||||||
# error <glog/vlog_is_on.h> was not included correctly. See the documentation for how to consume the library.
|
# error <glog/vlog_is_on.h> was not included correctly. See the documention for how to consume the library.
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "glog/flags.h"
|
#include "glog/flags.h"
|
||||||
|
|||||||
126
src/googletest.h
126
src/googletest.h
@ -1,4 +1,4 @@
|
|||||||
// Copyright (c) 2024, Google Inc.
|
// Copyright (c) 2009, Google Inc.
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// Redistribution and use in source and binary forms, with or without
|
// Redistribution and use in source and binary forms, with or without
|
||||||
@ -51,16 +51,12 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "config.h"
|
#include "utilities.h"
|
||||||
#ifdef HAVE_UNISTD_H
|
#ifdef HAVE_UNISTD_H
|
||||||
# include <unistd.h>
|
# include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(GLOG_USE_WINDOWS_PORT)
|
|
||||||
# include "port.h"
|
|
||||||
#endif // defined(GLOG_USE_WINDOWS_PORT)
|
|
||||||
#include "base/commandlineflags.h"
|
#include "base/commandlineflags.h"
|
||||||
#include "utilities.h"
|
|
||||||
|
|
||||||
#if __cplusplus < 201103L && !defined(_MSC_VER)
|
#if __cplusplus < 201103L && !defined(_MSC_VER)
|
||||||
# define GOOGLE_GLOG_THROW_BAD_ALLOC throw(std::bad_alloc)
|
# define GOOGLE_GLOG_THROW_BAD_ALLOC throw(std::bad_alloc)
|
||||||
@ -74,17 +70,17 @@ using std::vector;
|
|||||||
|
|
||||||
namespace google {
|
namespace google {
|
||||||
extern void (*g_logging_fail_func)();
|
extern void (*g_logging_fail_func)();
|
||||||
extern void GetExistingTempDirectories(std::vector<std::string>& list);
|
extern void GetExistingTempDirectories(vector<string>* list);
|
||||||
extern int posix_strerror_r(int err, char* buf, size_t len);
|
extern int posix_strerror_r(int err, char* buf, size_t len);
|
||||||
extern std::string StrError(int err);
|
extern std::string StrError(int err);
|
||||||
} // namespace google
|
}
|
||||||
|
|
||||||
#undef GLOG_EXPORT
|
#undef GLOG_EXPORT
|
||||||
#define GLOG_EXPORT
|
#define GLOG_EXPORT
|
||||||
|
|
||||||
static inline string GetTempDir() {
|
static inline string GetTempDir() {
|
||||||
vector<string> temp_directories_list;
|
vector<string> temp_directories_list;
|
||||||
google::GetExistingTempDirectories(temp_directories_list);
|
google::GetExistingTempDirectories(&temp_directories_list);
|
||||||
|
|
||||||
if (temp_directories_list.empty()) {
|
if (temp_directories_list.empty()) {
|
||||||
fprintf(stderr, "No temporary directory found\n");
|
fprintf(stderr, "No temporary directory found\n");
|
||||||
@ -196,19 +192,8 @@ void InitGoogleTest(int*, char**) {}
|
|||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
# define EXPECT_THROW(statement, exception) \
|
|
||||||
do { \
|
|
||||||
try { \
|
|
||||||
statement; \
|
|
||||||
} catch (const exception&) { \
|
|
||||||
printf("ok\n"); \
|
|
||||||
} catch (...) { \
|
|
||||||
fprintf(stderr, "%s\n", "Unexpected exception thrown"); \
|
|
||||||
exit(EXIT_FAILURE); \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
vector<void (*)()> g_testlist; // the tests to run
|
vector<void (*)()> g_testlist; // the tests to run
|
||||||
|
|
||||||
# define TEST(a, b) \
|
# define TEST(a, b) \
|
||||||
struct Test_##a##_##b { \
|
struct Test_##a##_##b { \
|
||||||
Test_##a##_##b() { g_testlist.push_back(&Run); } \
|
Test_##a##_##b() { g_testlist.push_back(&Run); } \
|
||||||
@ -315,59 +300,66 @@ static inline void RunSpecifiedBenchmarks() {
|
|||||||
class CapturedStream {
|
class CapturedStream {
|
||||||
public:
|
public:
|
||||||
CapturedStream(int fd, string filename)
|
CapturedStream(int fd, string filename)
|
||||||
: fd_(fd), filename_(std::move(filename)) {
|
: fd_(fd),
|
||||||
|
|
||||||
|
filename_(std::move(filename)) {
|
||||||
Capture();
|
Capture();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
~CapturedStream() {
|
||||||
|
if (uncaptured_fd_ != -1) {
|
||||||
|
CHECK(close(uncaptured_fd_) != -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Start redirecting output to a file
|
// Start redirecting output to a file
|
||||||
void Capture() {
|
void Capture() {
|
||||||
// Keep original stream for later
|
// Keep original stream for later
|
||||||
CHECK(!uncaptured_fd_) << ", Stream " << fd_ << " already captured!";
|
CHECK(uncaptured_fd_ == -1) << ", Stream " << fd_ << " already captured!";
|
||||||
uncaptured_fd_.reset(dup(fd_));
|
uncaptured_fd_ = dup(fd_);
|
||||||
CHECK(uncaptured_fd_);
|
CHECK(uncaptured_fd_ != -1);
|
||||||
|
|
||||||
// Open file to save stream to
|
// Open file to save stream to
|
||||||
FileDescriptor cap_fd{open(filename_.c_str(), O_CREAT | O_TRUNC | O_WRONLY,
|
int cap_fd = open(filename_.c_str(), O_CREAT | O_TRUNC | O_WRONLY,
|
||||||
S_IRUSR | S_IWUSR)};
|
S_IRUSR | S_IWUSR);
|
||||||
CHECK(cap_fd);
|
CHECK(cap_fd != -1);
|
||||||
|
|
||||||
// Send stdout/stderr to this file
|
// Send stdout/stderr to this file
|
||||||
fflush(nullptr);
|
fflush(nullptr);
|
||||||
CHECK(dup2(cap_fd.get(), fd_) != -1);
|
CHECK(dup2(cap_fd, fd_) != -1);
|
||||||
CHECK(cap_fd.close() != -1);
|
CHECK(close(cap_fd) != -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove output redirection
|
// Remove output redirection
|
||||||
void StopCapture() {
|
void StopCapture() {
|
||||||
// Restore original stream
|
// Restore original stream
|
||||||
if (uncaptured_fd_) {
|
if (uncaptured_fd_ != -1) {
|
||||||
fflush(nullptr);
|
fflush(nullptr);
|
||||||
CHECK(dup2(uncaptured_fd_.get(), fd_) != -1);
|
CHECK(dup2(uncaptured_fd_, fd_) != -1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const string& filename() const { return filename_; }
|
const string& filename() const { return filename_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int fd_; // file descriptor being captured
|
int fd_; // file descriptor being captured
|
||||||
FileDescriptor
|
int uncaptured_fd_{-1}; // where the stream was originally being sent to
|
||||||
uncaptured_fd_; // where the stream was originally being sent to
|
string filename_; // file where stream is being saved
|
||||||
string filename_; // file where stream is being saved
|
|
||||||
};
|
};
|
||||||
static std::map<int, std::unique_ptr<CapturedStream>> s_captured_streams;
|
static CapturedStream* s_captured_streams[STDERR_FILENO + 1];
|
||||||
// Redirect a file descriptor to a file.
|
// Redirect a file descriptor to a file.
|
||||||
// fd - Should be stdout or stderr
|
// fd - Should be STDOUT_FILENO or STDERR_FILENO
|
||||||
// filename - File where output should be stored
|
// filename - File where output should be stored
|
||||||
static inline void CaptureTestOutput(int fd, const string& filename) {
|
static inline void CaptureTestOutput(int fd, const string& filename) {
|
||||||
CHECK((fd == fileno(stdout)) || (fd == fileno(stderr)));
|
CHECK((fd == STDOUT_FILENO) || (fd == STDERR_FILENO));
|
||||||
CHECK(s_captured_streams.find(fd) == s_captured_streams.end());
|
CHECK(s_captured_streams[fd] == nullptr);
|
||||||
s_captured_streams[fd] = std::make_unique<CapturedStream>(fd, filename);
|
s_captured_streams[fd] = new CapturedStream(fd, filename);
|
||||||
}
|
}
|
||||||
static inline void CaptureTestStdout() {
|
static inline void CaptureTestStdout() {
|
||||||
CaptureTestOutput(fileno(stdout), FLAGS_test_tmpdir + "/captured.out");
|
CaptureTestOutput(STDOUT_FILENO, FLAGS_test_tmpdir + "/captured.out");
|
||||||
}
|
}
|
||||||
static inline void CaptureTestStderr() {
|
static inline void CaptureTestStderr() {
|
||||||
CaptureTestOutput(fileno(stderr), FLAGS_test_tmpdir + "/captured.err");
|
CaptureTestOutput(STDERR_FILENO, FLAGS_test_tmpdir + "/captured.err");
|
||||||
}
|
}
|
||||||
// Return the size (in bytes) of a file
|
// Return the size (in bytes) of a file
|
||||||
static inline size_t GetFileSize(FILE* file) {
|
static inline size_t GetFileSize(FILE* file) {
|
||||||
@ -377,7 +369,7 @@ static inline size_t GetFileSize(FILE* file) {
|
|||||||
// Read the entire content of a file as a string
|
// Read the entire content of a file as a string
|
||||||
static inline string ReadEntireFile(FILE* file) {
|
static inline string ReadEntireFile(FILE* file) {
|
||||||
const size_t file_size = GetFileSize(file);
|
const size_t file_size = GetFileSize(file);
|
||||||
std::vector<char> content(file_size);
|
char* const buffer = new char[file_size];
|
||||||
|
|
||||||
size_t bytes_last_read = 0; // # of bytes read in the last fread()
|
size_t bytes_last_read = 0; // # of bytes read in the last fread()
|
||||||
size_t bytes_read = 0; // # of bytes read so far
|
size_t bytes_read = 0; // # of bytes read so far
|
||||||
@ -388,32 +380,38 @@ static inline string ReadEntireFile(FILE* file) {
|
|||||||
// pre-determined file size is reached.
|
// pre-determined file size is reached.
|
||||||
do {
|
do {
|
||||||
bytes_last_read =
|
bytes_last_read =
|
||||||
fread(content.data() + bytes_read, 1, file_size - bytes_read, file);
|
fread(buffer + bytes_read, 1, file_size - bytes_read, file);
|
||||||
bytes_read += bytes_last_read;
|
bytes_read += bytes_last_read;
|
||||||
} while (bytes_last_read > 0 && bytes_read < file_size);
|
} while (bytes_last_read > 0 && bytes_read < file_size);
|
||||||
|
|
||||||
return std::string(content.data(), bytes_read);
|
const string content = string(buffer, buffer + bytes_read);
|
||||||
|
delete[] buffer;
|
||||||
|
|
||||||
|
return content;
|
||||||
}
|
}
|
||||||
// Get the captured stdout or stderr as a string
|
// Get the captured stdout (when fd is STDOUT_FILENO) or stderr (when
|
||||||
|
// fd is STDERR_FILENO) as a string
|
||||||
static inline string GetCapturedTestOutput(int fd) {
|
static inline string GetCapturedTestOutput(int fd) {
|
||||||
CHECK((fd == fileno(stdout)) || (fd == fileno(stderr)));
|
CHECK(fd == STDOUT_FILENO || fd == STDERR_FILENO);
|
||||||
std::unique_ptr<CapturedStream> cap = std::move(s_captured_streams.at(fd));
|
CapturedStream* const cap = s_captured_streams[fd];
|
||||||
s_captured_streams.erase(fd);
|
|
||||||
CHECK(cap) << ": did you forget CaptureTestStdout() or CaptureTestStderr()?";
|
CHECK(cap) << ": did you forget CaptureTestStdout() or CaptureTestStderr()?";
|
||||||
|
|
||||||
// Make sure everything is flushed.
|
// Make sure everything is flushed.
|
||||||
cap->StopCapture();
|
cap->StopCapture();
|
||||||
|
|
||||||
// Read the captured file.
|
// Read the captured file.
|
||||||
std::unique_ptr<FILE> file{fopen(cap->filename().c_str(), "r")};
|
FILE* const file = fopen(cap->filename().c_str(), "r");
|
||||||
const string content = ReadEntireFile(file.get());
|
const string content = ReadEntireFile(file);
|
||||||
file.reset();
|
fclose(file);
|
||||||
|
|
||||||
|
delete cap;
|
||||||
|
s_captured_streams[fd] = nullptr;
|
||||||
|
|
||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
// Get the captured stderr of a test as a string.
|
// Get the captured stderr of a test as a string.
|
||||||
static inline string GetCapturedTestStderr() {
|
static inline string GetCapturedTestStderr() {
|
||||||
return GetCapturedTestOutput(fileno(stderr));
|
return GetCapturedTestOutput(STDERR_FILENO);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const std::size_t kLoggingPrefixLength = 9;
|
static const std::size_t kLoggingPrefixLength = 9;
|
||||||
@ -481,11 +479,11 @@ static inline void StringReplace(string* str, const string& oldsub,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline string Munge(const string& filename) {
|
static inline string Munge(const string& filename) {
|
||||||
std::unique_ptr<FILE> fp{fopen(filename.c_str(), "rb")};
|
FILE* fp = fopen(filename.c_str(), "rb");
|
||||||
CHECK(fp != nullptr) << filename << ": couldn't open";
|
CHECK(fp != nullptr) << filename << ": couldn't open";
|
||||||
char buf[4096];
|
char buf[4096];
|
||||||
string result;
|
string result;
|
||||||
while (fgets(buf, 4095, fp.get())) {
|
while (fgets(buf, 4095, fp)) {
|
||||||
string line = MungeLine(buf);
|
string line = MungeLine(buf);
|
||||||
const size_t str_size = 256;
|
const size_t str_size = 256;
|
||||||
char null_str[str_size];
|
char null_str[str_size];
|
||||||
@ -504,19 +502,19 @@ static inline string Munge(const string& filename) {
|
|||||||
StringReplace(&line, "__ENOEXEC__", StrError(ENOEXEC));
|
StringReplace(&line, "__ENOEXEC__", StrError(ENOEXEC));
|
||||||
result += line + "\n";
|
result += line + "\n";
|
||||||
}
|
}
|
||||||
|
fclose(fp);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void WriteToFile(const string& body, const string& file) {
|
static inline void WriteToFile(const string& body, const string& file) {
|
||||||
std::unique_ptr<FILE> fp{fopen(file.c_str(), "wb")};
|
FILE* fp = fopen(file.c_str(), "wb");
|
||||||
fwrite(body.data(), 1, body.size(), fp.get());
|
fwrite(body.data(), 1, body.size(), fp);
|
||||||
|
fclose(fp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool MungeAndDiffTest(const string& golden_filename,
|
static inline bool MungeAndDiffTest(const string& golden_filename,
|
||||||
CapturedStream* cap) {
|
CapturedStream* cap) {
|
||||||
auto pos = s_captured_streams.find(fileno(stdout));
|
if (cap == s_captured_streams[STDOUT_FILENO]) {
|
||||||
|
|
||||||
if (pos != s_captured_streams.end() && cap == pos->second.get()) {
|
|
||||||
CHECK(cap) << ": did you forget CaptureTestStdout()?";
|
CHECK(cap) << ": did you forget CaptureTestStdout()?";
|
||||||
} else {
|
} else {
|
||||||
CHECK(cap) << ": did you forget CaptureTestStderr()?";
|
CHECK(cap) << ": did you forget CaptureTestStderr()?";
|
||||||
@ -551,13 +549,11 @@ static inline bool MungeAndDiffTest(const string& golden_filename,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline bool MungeAndDiffTestStderr(const string& golden_filename) {
|
static inline bool MungeAndDiffTestStderr(const string& golden_filename) {
|
||||||
return MungeAndDiffTest(golden_filename,
|
return MungeAndDiffTest(golden_filename, s_captured_streams[STDERR_FILENO]);
|
||||||
s_captured_streams.at(fileno(stderr)).get());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool MungeAndDiffTestStdout(const string& golden_filename) {
|
static inline bool MungeAndDiffTestStdout(const string& golden_filename) {
|
||||||
return MungeAndDiffTest(golden_filename,
|
return MungeAndDiffTest(golden_filename, s_captured_streams[STDOUT_FILENO]);
|
||||||
s_captured_streams.at(fileno(stdout)).get());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save flags used from logging_unittest.cc.
|
// Save flags used from logging_unittest.cc.
|
||||||
|
|||||||
518
src/logging.cc
518
src/logging.cc
@ -38,21 +38,21 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <memory>
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <shared_mutex>
|
#include <shared_mutex>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <tuple>
|
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "glog/platform.h"
|
|
||||||
#include "glog/raw_logging.h"
|
#include "glog/raw_logging.h"
|
||||||
#include "stacktrace.h"
|
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
|
|
||||||
|
#ifdef HAVE_STACKTRACE
|
||||||
|
# include "stacktrace.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef GLOG_OS_WINDOWS
|
#ifdef GLOG_OS_WINDOWS
|
||||||
# include "windows/dirent.h"
|
# include "windows/dirent.h"
|
||||||
#else
|
#else
|
||||||
@ -86,6 +86,10 @@
|
|||||||
# include <syslog.h>
|
# include <syslog.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
# include <android/log.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_SYS_TYPES_H
|
#ifdef HAVE_SYS_TYPES_H
|
||||||
# include <sys/types.h>
|
# include <sys/types.h>
|
||||||
#endif
|
#endif
|
||||||
@ -215,7 +219,6 @@ static bool TerminalSupportsColor() {
|
|||||||
|
|
||||||
namespace google {
|
namespace google {
|
||||||
|
|
||||||
GLOG_NO_EXPORT
|
|
||||||
std::string StrError(int err);
|
std::string StrError(int err);
|
||||||
|
|
||||||
enum GLogColor { COLOR_DEFAULT, COLOR_RED, COLOR_GREEN, COLOR_YELLOW };
|
enum GLogColor { COLOR_DEFAULT, COLOR_RED, COLOR_GREEN, COLOR_YELLOW };
|
||||||
@ -282,15 +285,13 @@ static uint32 MaxLogSize() {
|
|||||||
// is so that streaming can be done more efficiently.
|
// is so that streaming can be done more efficiently.
|
||||||
const size_t LogMessage::kMaxLogMessageLen = 30000;
|
const size_t LogMessage::kMaxLogMessageLen = 30000;
|
||||||
|
|
||||||
namespace logging {
|
struct LogMessage::LogMessageData {
|
||||||
namespace internal {
|
|
||||||
struct LogMessageData {
|
|
||||||
LogMessageData();
|
LogMessageData();
|
||||||
|
|
||||||
int preserved_errno_; // preserved errno
|
int preserved_errno_; // preserved errno
|
||||||
// Buffer space; contains complete message text.
|
// Buffer space; contains complete message text.
|
||||||
char message_text_[LogMessage::kMaxLogMessageLen + 1];
|
char message_text_[LogMessage::kMaxLogMessageLen + 1];
|
||||||
LogMessage::LogStream stream_;
|
LogStream stream_;
|
||||||
LogSeverity severity_; // What level is this LogMessage logged at?
|
LogSeverity severity_; // What level is this LogMessage logged at?
|
||||||
int line_; // line number where logging call is.
|
int line_; // line number where logging call is.
|
||||||
void (LogMessage::*send_method_)(); // Call this in destructor to send
|
void (LogMessage::*send_method_)(); // Call this in destructor to send
|
||||||
@ -307,13 +308,11 @@ struct LogMessageData {
|
|||||||
const char* fullname_; // fullname of file that called LOG
|
const char* fullname_; // fullname of file that called LOG
|
||||||
bool has_been_flushed_; // false => data has not been flushed
|
bool has_been_flushed_; // false => data has not been flushed
|
||||||
bool first_fatal_; // true => this was first fatal msg
|
bool first_fatal_; // true => this was first fatal msg
|
||||||
std::thread::id thread_id_;
|
|
||||||
|
|
||||||
|
private:
|
||||||
LogMessageData(const LogMessageData&) = delete;
|
LogMessageData(const LogMessageData&) = delete;
|
||||||
LogMessageData& operator=(const LogMessageData&) = delete;
|
void operator=(const LogMessageData&) = delete;
|
||||||
};
|
};
|
||||||
} // namespace internal
|
|
||||||
} // namespace logging
|
|
||||||
|
|
||||||
// A mutex that allows only one thread to log at a time, to keep things from
|
// A mutex that allows only one thread to log at a time, to keep things from
|
||||||
// getting jumbled. Some other very uncommon logging operations (like
|
// getting jumbled. Some other very uncommon logging operations (like
|
||||||
@ -328,7 +327,8 @@ int64 LogMessage::num_messages_[NUM_SEVERITIES] = {0, 0, 0, 0};
|
|||||||
// Globally disable log writing (if disk is full)
|
// Globally disable log writing (if disk is full)
|
||||||
static bool stop_writing = false;
|
static bool stop_writing = false;
|
||||||
|
|
||||||
const char* const LogSeverityNames[] = {"INFO", "WARNING", "ERROR", "FATAL"};
|
const char* const LogSeverityNames[NUM_SEVERITIES] = {"INFO", "WARNING",
|
||||||
|
"ERROR", "FATAL"};
|
||||||
|
|
||||||
// Has the user called SetExitOnDFatal(true)?
|
// Has the user called SetExitOnDFatal(true)?
|
||||||
static bool exit_on_dfatal = true;
|
static bool exit_on_dfatal = true;
|
||||||
@ -342,38 +342,37 @@ static bool SendEmailInternal(const char* dest, const char* subject,
|
|||||||
|
|
||||||
base::Logger::~Logger() = default;
|
base::Logger::~Logger() = default;
|
||||||
|
|
||||||
|
void base::Logger::Write(bool /*force_flush*/, time_t /*timestamp*/,
|
||||||
|
const char* /*message*/, size_t /*message_len*/) {}
|
||||||
|
|
||||||
|
void base::Logger::Write(bool force_flush,
|
||||||
|
const std::chrono::system_clock::time_point& timestamp,
|
||||||
|
const char* message, size_t message_len) {
|
||||||
|
#if defined(__GNUG__)
|
||||||
|
# pragma GCC diagnostic push
|
||||||
|
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
# pragma warning(push)
|
||||||
|
# pragma warning(disable : 4996)
|
||||||
|
#endif // __GNUG__
|
||||||
|
return Write(force_flush, std::chrono::system_clock::to_time_t(timestamp),
|
||||||
|
message, message_len);
|
||||||
|
#if defined(__GNUG__)
|
||||||
|
# pragma GCC diagnostic pop
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
# pragma warning(pop)
|
||||||
|
#endif // __GNUG__
|
||||||
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr std::intmax_t kSecondsInDay = 60 * 60 * 24;
|
constexpr std::intmax_t kSecondsInDay = 60 * 60 * 24;
|
||||||
constexpr std::intmax_t kSecondsInWeek = kSecondsInDay * 7;
|
constexpr std::intmax_t kSecondsInWeek = kSecondsInDay * 7;
|
||||||
|
|
||||||
// Optional user-configured callback to print custom prefixes.
|
// Optional user-configured callback to print custom prefixes.
|
||||||
class PrefixFormatter {
|
CustomPrefixCallback custom_prefix_callback = nullptr;
|
||||||
public:
|
// User-provided data to pass to the callback:
|
||||||
PrefixFormatter(PrefixFormatterCallback callback, void* data) noexcept
|
void* custom_prefix_callback_data = nullptr;
|
||||||
: version{V2}, callback_v2{callback}, data{data} {}
|
|
||||||
|
|
||||||
void operator()(std::ostream& s, const LogMessage& message) const {
|
|
||||||
switch (version) {
|
|
||||||
case V2:
|
|
||||||
callback_v2(s, message, data);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PrefixFormatter(const PrefixFormatter& other) = delete;
|
|
||||||
PrefixFormatter& operator=(const PrefixFormatter& other) = delete;
|
|
||||||
|
|
||||||
private:
|
|
||||||
enum Version { V2 } version;
|
|
||||||
union {
|
|
||||||
PrefixFormatterCallback callback_v2;
|
|
||||||
};
|
|
||||||
// User-provided data to pass to the callback:
|
|
||||||
void* data;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::unique_ptr<PrefixFormatter> g_prefix_formatter;
|
|
||||||
|
|
||||||
// Encapsulates all file-system related state
|
// Encapsulates all file-system related state
|
||||||
class LogFileObject : public base::Logger {
|
class LogFileObject : public base::Logger {
|
||||||
@ -414,7 +413,7 @@ class LogFileObject : public base::Logger {
|
|||||||
string base_filename_;
|
string base_filename_;
|
||||||
string symlink_basename_;
|
string symlink_basename_;
|
||||||
string filename_extension_; // option users can specify (eg to add port#)
|
string filename_extension_; // option users can specify (eg to add port#)
|
||||||
std::unique_ptr<FILE> file_;
|
FILE* file_{nullptr};
|
||||||
LogSeverity severity_;
|
LogSeverity severity_;
|
||||||
uint32 bytes_since_flush_{0};
|
uint32 bytes_since_flush_{0};
|
||||||
uint32 dropped_mem_length_{0};
|
uint32 dropped_mem_length_{0};
|
||||||
@ -545,12 +544,12 @@ class LogDestination {
|
|||||||
// Send logging info to all registered sinks.
|
// Send logging info to all registered sinks.
|
||||||
static void LogToSinks(LogSeverity severity, const char* full_filename,
|
static void LogToSinks(LogSeverity severity, const char* full_filename,
|
||||||
const char* base_filename, int line,
|
const char* base_filename, int line,
|
||||||
const LogMessageTime& time, const char* message,
|
const LogMessageTime& logmsgtime, const char* message,
|
||||||
size_t message_len);
|
size_t message_len);
|
||||||
|
|
||||||
// Wait for all registered sinks via WaitTillSent
|
// Wait for all registered sinks via WaitTillSent
|
||||||
// including the optional one in "data".
|
// including the optional one in "data".
|
||||||
static void WaitForSinks(logging::internal::LogMessageData* data);
|
static void WaitForSinks(LogMessage::LogMessageData* data);
|
||||||
|
|
||||||
static LogDestination* log_destination(LogSeverity severity);
|
static LogDestination* log_destination(LogSeverity severity);
|
||||||
|
|
||||||
@ -786,9 +785,24 @@ inline void LogDestination::MaybeLogToStderr(LogSeverity severity,
|
|||||||
size_t prefix_len) {
|
size_t prefix_len) {
|
||||||
if ((severity >= FLAGS_stderrthreshold) || FLAGS_alsologtostderr) {
|
if ((severity >= FLAGS_stderrthreshold) || FLAGS_alsologtostderr) {
|
||||||
ColoredWriteToStderr(severity, message, message_len);
|
ColoredWriteToStderr(severity, message, message_len);
|
||||||
AlsoErrorWrite(severity,
|
#ifdef GLOG_OS_WINDOWS
|
||||||
glog_internal_namespace_::ProgramInvocationShortName(),
|
(void)prefix_len;
|
||||||
message + prefix_len);
|
// On Windows, also output to the debugger
|
||||||
|
::OutputDebugStringA(message);
|
||||||
|
#elif defined(__ANDROID__)
|
||||||
|
// On Android, also output to logcat
|
||||||
|
const int android_log_levels[NUM_SEVERITIES] = {
|
||||||
|
ANDROID_LOG_INFO,
|
||||||
|
ANDROID_LOG_WARN,
|
||||||
|
ANDROID_LOG_ERROR,
|
||||||
|
ANDROID_LOG_FATAL,
|
||||||
|
};
|
||||||
|
__android_log_write(android_log_levels[severity],
|
||||||
|
glog_internal_namespace_::ProgramInvocationShortName(),
|
||||||
|
message + prefix_len);
|
||||||
|
#else
|
||||||
|
(void)prefix_len;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -845,20 +859,19 @@ inline void LogDestination::LogToAllLogfiles(
|
|||||||
inline void LogDestination::LogToSinks(LogSeverity severity,
|
inline void LogDestination::LogToSinks(LogSeverity severity,
|
||||||
const char* full_filename,
|
const char* full_filename,
|
||||||
const char* base_filename, int line,
|
const char* base_filename, int line,
|
||||||
const LogMessageTime& time,
|
const LogMessageTime& logmsgtime,
|
||||||
const char* message,
|
const char* message,
|
||||||
size_t message_len) {
|
size_t message_len) {
|
||||||
std::shared_lock<SinkMutex> l{sink_mutex_};
|
std::shared_lock<SinkMutex> l{sink_mutex_};
|
||||||
if (sinks_) {
|
if (sinks_) {
|
||||||
for (size_t i = sinks_->size(); i-- > 0;) {
|
for (size_t i = sinks_->size(); i-- > 0;) {
|
||||||
(*sinks_)[i]->send(severity, full_filename, base_filename, line, time,
|
(*sinks_)[i]->send(severity, full_filename, base_filename, line,
|
||||||
message, message_len);
|
logmsgtime, message, message_len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void LogDestination::WaitForSinks(
|
inline void LogDestination::WaitForSinks(LogMessage::LogMessageData* data) {
|
||||||
logging::internal::LogMessageData* data) {
|
|
||||||
std::shared_lock<SinkMutex> l{sink_mutex_};
|
std::shared_lock<SinkMutex> l{sink_mutex_};
|
||||||
if (sinks_) {
|
if (sinks_) {
|
||||||
for (size_t i = sinks_->size(); i-- > 0;) {
|
for (size_t i = sinks_->size(); i-- > 0;) {
|
||||||
@ -933,7 +946,10 @@ LogFileObject::LogFileObject(LogSeverity severity, const char* base_filename)
|
|||||||
|
|
||||||
LogFileObject::~LogFileObject() {
|
LogFileObject::~LogFileObject() {
|
||||||
std::lock_guard<std::mutex> l{mutex_};
|
std::lock_guard<std::mutex> l{mutex_};
|
||||||
file_ = nullptr;
|
if (file_ != nullptr) {
|
||||||
|
fclose(file_);
|
||||||
|
file_ = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LogFileObject::SetBasename(const char* basename) {
|
void LogFileObject::SetBasename(const char* basename) {
|
||||||
@ -942,6 +958,7 @@ void LogFileObject::SetBasename(const char* basename) {
|
|||||||
if (base_filename_ != basename) {
|
if (base_filename_ != basename) {
|
||||||
// Get rid of old log file since we are changing names
|
// Get rid of old log file since we are changing names
|
||||||
if (file_ != nullptr) {
|
if (file_ != nullptr) {
|
||||||
|
fclose(file_);
|
||||||
file_ = nullptr;
|
file_ = nullptr;
|
||||||
rollover_attempt_ = kRolloverAttemptFrequency - 1;
|
rollover_attempt_ = kRolloverAttemptFrequency - 1;
|
||||||
}
|
}
|
||||||
@ -954,6 +971,7 @@ void LogFileObject::SetExtension(const char* ext) {
|
|||||||
if (filename_extension_ != ext) {
|
if (filename_extension_ != ext) {
|
||||||
// Get rid of old log file since we are changing names
|
// Get rid of old log file since we are changing names
|
||||||
if (file_ != nullptr) {
|
if (file_ != nullptr) {
|
||||||
|
fclose(file_);
|
||||||
file_ = nullptr;
|
file_ = nullptr;
|
||||||
rollover_attempt_ = kRolloverAttemptFrequency - 1;
|
rollover_attempt_ = kRolloverAttemptFrequency - 1;
|
||||||
}
|
}
|
||||||
@ -974,7 +992,7 @@ void LogFileObject::Flush() {
|
|||||||
void LogFileObject::FlushUnlocked(
|
void LogFileObject::FlushUnlocked(
|
||||||
const std::chrono::system_clock::time_point& now) {
|
const std::chrono::system_clock::time_point& now) {
|
||||||
if (file_ != nullptr) {
|
if (file_ != nullptr) {
|
||||||
fflush(file_.get());
|
fflush(file_);
|
||||||
bytes_since_flush_ = 0;
|
bytes_since_flush_ = 0;
|
||||||
}
|
}
|
||||||
// Figure out when we are due for another flush.
|
// Figure out when we are due for another flush.
|
||||||
@ -995,28 +1013,12 @@ bool LogFileObject::CreateLogfile(const string& time_pid_string) {
|
|||||||
if (FLAGS_timestamp_in_logfile_name) {
|
if (FLAGS_timestamp_in_logfile_name) {
|
||||||
// demand that the file is unique for our timestamp (fail if it exists).
|
// demand that the file is unique for our timestamp (fail if it exists).
|
||||||
flags = flags | O_EXCL;
|
flags = flags | O_EXCL;
|
||||||
} else {
|
|
||||||
// logs are written to a single file, where: a log file is created for the
|
|
||||||
// the first time or a file is being recreated due to exceeding max size
|
|
||||||
|
|
||||||
struct stat statbuf;
|
|
||||||
if (stat(filename, &statbuf) == 0) {
|
|
||||||
// truncate the file if it exceeds the max size
|
|
||||||
if ((static_cast<uint32>(statbuf.st_size) >> 20U) >= MaxLogSize()) {
|
|
||||||
flags |= O_TRUNC;
|
|
||||||
}
|
|
||||||
|
|
||||||
// update file length to sync file size
|
|
||||||
file_length_ = static_cast<uint32>(statbuf.st_size);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
int fd = open(filename, flags, static_cast<mode_t>(FLAGS_logfile_mode));
|
||||||
FileDescriptor fd{
|
if (fd == -1) return false;
|
||||||
open(filename, flags, static_cast<mode_t>(FLAGS_logfile_mode))};
|
|
||||||
if (!fd) return false;
|
|
||||||
#ifdef HAVE_FCNTL
|
#ifdef HAVE_FCNTL
|
||||||
// Mark the file close-on-exec. We don't really care if this fails
|
// Mark the file close-on-exec. We don't really care if this fails
|
||||||
fcntl(fd.get(), F_SETFD, FD_CLOEXEC);
|
fcntl(fd, F_SETFD, FD_CLOEXEC);
|
||||||
|
|
||||||
// Mark the file as exclusive write access to avoid two clients logging to the
|
// Mark the file as exclusive write access to avoid two clients logging to the
|
||||||
// same file. This applies particularly when !FLAGS_timestamp_in_logfile_name
|
// same file. This applies particularly when !FLAGS_timestamp_in_logfile_name
|
||||||
@ -1035,15 +1037,17 @@ bool LogFileObject::CreateLogfile(const string& time_pid_string) {
|
|||||||
w_lock.l_whence = SEEK_SET;
|
w_lock.l_whence = SEEK_SET;
|
||||||
w_lock.l_len = 0;
|
w_lock.l_len = 0;
|
||||||
|
|
||||||
int wlock_ret = fcntl(fd.get(), F_SETLK, &w_lock);
|
int wlock_ret = fcntl(fd, F_SETLK, &w_lock);
|
||||||
if (wlock_ret == -1) {
|
if (wlock_ret == -1) {
|
||||||
|
close(fd); // as we are failing already, do not check errors here
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// fdopen in append mode so if the file exists it will fseek to the end
|
// fdopen in append mode so if the file exists it will fseek to the end
|
||||||
file_.reset(fdopen(fd.release(), "a")); // Make a FILE*.
|
file_ = fdopen(fd, "a"); // Make a FILE*.
|
||||||
if (file_ == nullptr) { // Man, we're screwed!
|
if (file_ == nullptr) { // Man, we're screwed!
|
||||||
|
close(fd);
|
||||||
if (FLAGS_timestamp_in_logfile_name) {
|
if (FLAGS_timestamp_in_logfile_name) {
|
||||||
unlink(filename); // Erase the half-baked evidence: an unusable log file,
|
unlink(filename); // Erase the half-baked evidence: an unusable log file,
|
||||||
// only if we just created it.
|
// only if we just created it.
|
||||||
@ -1054,7 +1058,7 @@ bool LogFileObject::CreateLogfile(const string& time_pid_string) {
|
|||||||
// https://github.com/golang/go/issues/27638 - make sure we seek to the end to
|
// https://github.com/golang/go/issues/27638 - make sure we seek to the end to
|
||||||
// append empirically replicated with wine over mingw build
|
// append empirically replicated with wine over mingw build
|
||||||
if (!FLAGS_timestamp_in_logfile_name) {
|
if (!FLAGS_timestamp_in_logfile_name) {
|
||||||
if (fseek(file_.get(), 0, SEEK_END) != 0) {
|
if (fseek(file_, 0, SEEK_END) != 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1123,6 +1127,7 @@ void LogFileObject::Write(
|
|||||||
ScopedExit<decltype(cleanupLogs)> cleanupAtEnd{cleanupLogs};
|
ScopedExit<decltype(cleanupLogs)> cleanupAtEnd{cleanupLogs};
|
||||||
|
|
||||||
if (file_length_ >> 20U >= MaxLogSize() || PidHasChanged()) {
|
if (file_length_ >> 20U >= MaxLogSize() || PidHasChanged()) {
|
||||||
|
if (file_ != nullptr) fclose(file_);
|
||||||
file_ = nullptr;
|
file_ = nullptr;
|
||||||
file_length_ = bytes_since_flush_ = dropped_mem_length_ = 0;
|
file_length_ = bytes_since_flush_ = dropped_mem_length_ = 0;
|
||||||
rollover_attempt_ = kRolloverAttemptFrequency - 1;
|
rollover_attempt_ = kRolloverAttemptFrequency - 1;
|
||||||
@ -1240,7 +1245,7 @@ void LogFileObject::Write(
|
|||||||
const string& file_header_string = file_header_stream.str();
|
const string& file_header_string = file_header_stream.str();
|
||||||
|
|
||||||
const size_t header_len = file_header_string.size();
|
const size_t header_len = file_header_string.size();
|
||||||
fwrite(file_header_string.data(), 1, header_len, file_.get());
|
fwrite(file_header_string.data(), 1, header_len, file_);
|
||||||
file_length_ += header_len;
|
file_length_ += header_len;
|
||||||
bytes_since_flush_ += header_len;
|
bytes_since_flush_ += header_len;
|
||||||
}
|
}
|
||||||
@ -1254,7 +1259,7 @@ void LogFileObject::Write(
|
|||||||
// 4096 bytes. fwrite() returns 4096 for message lengths that are
|
// 4096 bytes. fwrite() returns 4096 for message lengths that are
|
||||||
// greater than 4096, thereby indicating an error.
|
// greater than 4096, thereby indicating an error.
|
||||||
errno = 0;
|
errno = 0;
|
||||||
fwrite(message, 1, message_len, file_.get());
|
fwrite(message, 1, message_len, file_);
|
||||||
if (FLAGS_stop_logging_if_full_disk &&
|
if (FLAGS_stop_logging_if_full_disk &&
|
||||||
errno == ENOSPC) { // disk full, stop writing to disk
|
errno == ENOSPC) { // disk full, stop writing to disk
|
||||||
stop_writing = true; // until the disk is
|
stop_writing = true; // until the disk is
|
||||||
@ -1285,10 +1290,13 @@ void LogFileObject::Write(
|
|||||||
uint32 this_drop_length = total_drop_length - dropped_mem_length_;
|
uint32 this_drop_length = total_drop_length - dropped_mem_length_;
|
||||||
if (this_drop_length >= (2U << 20U)) {
|
if (this_drop_length >= (2U << 20U)) {
|
||||||
// Only advise when >= 2MiB to drop
|
// Only advise when >= 2MiB to drop
|
||||||
# if defined(HAVE_POSIX_FADVISE)
|
# if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21)
|
||||||
posix_fadvise(
|
// 'posix_fadvise' introduced in API 21:
|
||||||
fileno(file_.get()), static_cast<off_t>(dropped_mem_length_),
|
// * https://android.googlesource.com/platform/bionic/+/6880f936173081297be0dc12f687d341b86a4cfa/libc/libc.map.txt#732
|
||||||
static_cast<off_t>(this_drop_length), POSIX_FADV_DONTNEED);
|
# else
|
||||||
|
posix_fadvise(fileno(file_), static_cast<off_t>(dropped_mem_length_),
|
||||||
|
static_cast<off_t>(this_drop_length),
|
||||||
|
POSIX_FADV_DONTNEED);
|
||||||
# endif
|
# endif
|
||||||
dropped_mem_length_ = total_drop_length;
|
dropped_mem_length_ = total_drop_length;
|
||||||
}
|
}
|
||||||
@ -1502,10 +1510,10 @@ bool LogCleaner::IsLogLastModifiedOver(
|
|||||||
// for exclusive use by the first thread, and one for shared use by
|
// for exclusive use by the first thread, and one for shared use by
|
||||||
// all other threads.
|
// all other threads.
|
||||||
static std::mutex fatal_msg_lock;
|
static std::mutex fatal_msg_lock;
|
||||||
static logging::internal::CrashReason crash_reason;
|
static CrashReason crash_reason;
|
||||||
static bool fatal_msg_exclusive = true;
|
static bool fatal_msg_exclusive = true;
|
||||||
static logging::internal::LogMessageData fatal_msg_data_exclusive;
|
static LogMessage::LogMessageData fatal_msg_data_exclusive;
|
||||||
static logging::internal::LogMessageData fatal_msg_data_shared;
|
static LogMessage::LogMessageData fatal_msg_data_shared;
|
||||||
|
|
||||||
#ifdef GLOG_THREAD_LOCAL_STORAGE
|
#ifdef GLOG_THREAD_LOCAL_STORAGE
|
||||||
// Static thread-local log data space to use, because typically at most one
|
// Static thread-local log data space to use, because typically at most one
|
||||||
@ -1515,16 +1523,16 @@ static thread_local bool thread_data_available = true;
|
|||||||
|
|
||||||
# if defined(__cpp_lib_byte) && __cpp_lib_byte >= 201603L
|
# if defined(__cpp_lib_byte) && __cpp_lib_byte >= 201603L
|
||||||
// std::aligned_storage is deprecated in C++23
|
// std::aligned_storage is deprecated in C++23
|
||||||
alignas(logging::internal::LogMessageData) static thread_local std::byte
|
alignas(LogMessage::LogMessageData) static thread_local std::byte
|
||||||
thread_msg_data[sizeof(logging::internal::LogMessageData)];
|
thread_msg_data[sizeof(LogMessage::LogMessageData)];
|
||||||
# else // !(defined(__cpp_lib_byte) && __cpp_lib_byte >= 201603L)
|
# else // !(defined(__cpp_lib_byte) && __cpp_lib_byte >= 201603L)
|
||||||
static thread_local std::aligned_storage<
|
static thread_local std::aligned_storage<
|
||||||
sizeof(logging::internal::LogMessageData),
|
sizeof(LogMessage::LogMessageData),
|
||||||
alignof(logging::internal::LogMessageData)>::type thread_msg_data;
|
alignof(LogMessage::LogMessageData)>::type thread_msg_data;
|
||||||
# endif // defined(__cpp_lib_byte) && __cpp_lib_byte >= 201603L
|
# endif // defined(__cpp_lib_byte) && __cpp_lib_byte >= 201603L
|
||||||
#endif // defined(GLOG_THREAD_LOCAL_STORAGE)
|
#endif // defined(GLOG_THREAD_LOCAL_STORAGE)
|
||||||
|
|
||||||
logging::internal::LogMessageData::LogMessageData()
|
LogMessage::LogMessageData::LogMessageData()
|
||||||
: stream_(message_text_, LogMessage::kMaxLogMessageLen, 0) {}
|
: stream_(message_text_, LogMessage::kMaxLogMessageLen, 0) {}
|
||||||
|
|
||||||
LogMessage::LogMessage(const char* file, int line, LogSeverity severity,
|
LogMessage::LogMessage(const char* file, int line, LogSeverity severity,
|
||||||
@ -1534,8 +1542,7 @@ LogMessage::LogMessage(const char* file, int line, LogSeverity severity,
|
|||||||
data_->stream_.set_ctr(ctr);
|
data_->stream_.set_ctr(ctr);
|
||||||
}
|
}
|
||||||
|
|
||||||
LogMessage::LogMessage(const char* file, int line,
|
LogMessage::LogMessage(const char* file, int line, const CheckOpString& result)
|
||||||
const logging::internal::CheckOpString& result)
|
|
||||||
: allocated_(nullptr) {
|
: allocated_(nullptr) {
|
||||||
Init(file, line, GLOG_FATAL, &LogMessage::SendToLog);
|
Init(file, line, GLOG_FATAL, &LogMessage::SendToLog);
|
||||||
stream() << "Check failed: " << (*result.str_) << " ";
|
stream() << "Check failed: " << (*result.str_) << " ";
|
||||||
@ -1581,13 +1588,13 @@ void LogMessage::Init(const char* file, int line, LogSeverity severity,
|
|||||||
// No need for locking, because this is thread local.
|
// No need for locking, because this is thread local.
|
||||||
if (thread_data_available) {
|
if (thread_data_available) {
|
||||||
thread_data_available = false;
|
thread_data_available = false;
|
||||||
data_ = new (&thread_msg_data) logging::internal::LogMessageData;
|
data_ = new (&thread_msg_data) LogMessageData;
|
||||||
} else {
|
} else {
|
||||||
allocated_ = new logging::internal::LogMessageData();
|
allocated_ = new LogMessageData();
|
||||||
data_ = allocated_;
|
data_ = allocated_;
|
||||||
}
|
}
|
||||||
#else // !defined(GLOG_THREAD_LOCAL_STORAGE)
|
#else // !defined(GLOG_THREAD_LOCAL_STORAGE)
|
||||||
allocated_ = new logging::internal::LogMessageData();
|
allocated_ = new LogMessageData();
|
||||||
data_ = allocated_;
|
data_ = allocated_;
|
||||||
#endif // defined(GLOG_THREAD_LOCAL_STORAGE)
|
#endif // defined(GLOG_THREAD_LOCAL_STORAGE)
|
||||||
data_->first_fatal_ = false;
|
data_->first_fatal_ = false;
|
||||||
@ -1618,7 +1625,6 @@ void LogMessage::Init(const char* file, int line, LogSeverity severity,
|
|||||||
data_->basename_ = const_basename(file);
|
data_->basename_ = const_basename(file);
|
||||||
data_->fullname_ = file;
|
data_->fullname_ = file;
|
||||||
data_->has_been_flushed_ = false;
|
data_->has_been_flushed_ = false;
|
||||||
data_->thread_id_ = std::this_thread::get_id();
|
|
||||||
|
|
||||||
// If specified, prepend a prefix to each line. For example:
|
// If specified, prepend a prefix to each line. For example:
|
||||||
// I20201018 160715 f5d4fbb0 logging.cc:1153]
|
// I20201018 160715 f5d4fbb0 logging.cc:1153]
|
||||||
@ -1628,7 +1634,7 @@ void LogMessage::Init(const char* file, int line, LogSeverity severity,
|
|||||||
std::ios saved_fmt(nullptr);
|
std::ios saved_fmt(nullptr);
|
||||||
saved_fmt.copyfmt(stream());
|
saved_fmt.copyfmt(stream());
|
||||||
stream().fill('0');
|
stream().fill('0');
|
||||||
if (g_prefix_formatter == nullptr) {
|
if (custom_prefix_callback == nullptr) {
|
||||||
stream() << LogSeverityNames[severity][0];
|
stream() << LogSeverityNames[severity][0];
|
||||||
if (FLAGS_log_year_in_prefix) {
|
if (FLAGS_log_year_in_prefix) {
|
||||||
stream() << setw(4) << 1900 + time_.year();
|
stream() << setw(4) << 1900 + time_.year();
|
||||||
@ -1637,10 +1643,14 @@ void LogMessage::Init(const char* file, int line, LogSeverity severity,
|
|||||||
<< setw(2) << time_.hour() << ':' << setw(2) << time_.min()
|
<< setw(2) << time_.hour() << ':' << setw(2) << time_.min()
|
||||||
<< ':' << setw(2) << time_.sec() << "." << setw(6)
|
<< ':' << setw(2) << time_.sec() << "." << setw(6)
|
||||||
<< time_.usec() << ' ' << setfill(' ') << setw(5)
|
<< time_.usec() << ' ' << setfill(' ') << setw(5)
|
||||||
<< data_->thread_id_ << setfill('0') << ' ' << data_->basename_
|
<< std::this_thread::get_id() << setfill('0') << ' '
|
||||||
<< ':' << data_->line_ << "] ";
|
<< data_->basename_ << ':' << data_->line_ << "] ";
|
||||||
} else {
|
} else {
|
||||||
(*g_prefix_formatter)(stream(), *this);
|
custom_prefix_callback(
|
||||||
|
stream(),
|
||||||
|
LogMessageInfo(LogSeverityNames[severity], data_->basename_,
|
||||||
|
data_->line_, std::this_thread::get_id(), time_),
|
||||||
|
custom_prefix_callback_data);
|
||||||
stream() << " ";
|
stream() << " ";
|
||||||
}
|
}
|
||||||
stream().copyfmt(saved_fmt);
|
stream().copyfmt(saved_fmt);
|
||||||
@ -1652,26 +1662,18 @@ void LogMessage::Init(const char* file, int line, LogSeverity severity,
|
|||||||
std::snprintf(fileline, sizeof(fileline), "%s:%d", data_->basename_, line);
|
std::snprintf(fileline, sizeof(fileline), "%s:%d", data_->basename_, line);
|
||||||
#ifdef HAVE_STACKTRACE
|
#ifdef HAVE_STACKTRACE
|
||||||
if (FLAGS_log_backtrace_at == fileline) {
|
if (FLAGS_log_backtrace_at == fileline) {
|
||||||
string stacktrace = GetStackTrace();
|
string stacktrace;
|
||||||
|
DumpStackTraceToString(&stacktrace);
|
||||||
stream() << " (stacktrace:\n" << stacktrace << ") ";
|
stream() << " (stacktrace:\n" << stacktrace << ") ";
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LogSeverity LogMessage::severity() const noexcept { return data_->severity_; }
|
const LogMessageTime& LogMessage::time() const { return time_; }
|
||||||
|
|
||||||
int LogMessage::line() const noexcept { return data_->line_; }
|
LogMessage::~LogMessage() {
|
||||||
const std::thread::id& LogMessage::thread_id() const noexcept {
|
|
||||||
return data_->thread_id_;
|
|
||||||
}
|
|
||||||
const char* LogMessage::fullname() const noexcept { return data_->fullname_; }
|
|
||||||
const char* LogMessage::basename() const noexcept { return data_->basename_; }
|
|
||||||
const LogMessageTime& LogMessage::time() const noexcept { return time_; }
|
|
||||||
|
|
||||||
LogMessage::~LogMessage() noexcept(false) {
|
|
||||||
Flush();
|
Flush();
|
||||||
bool fail = data_->severity_ == GLOG_FATAL && exit_on_dfatal;
|
|
||||||
#ifdef GLOG_THREAD_LOCAL_STORAGE
|
#ifdef GLOG_THREAD_LOCAL_STORAGE
|
||||||
if (data_ == static_cast<void*>(&thread_msg_data)) {
|
if (data_ == static_cast<void*>(&thread_msg_data)) {
|
||||||
data_->~LogMessageData();
|
data_->~LogMessageData();
|
||||||
@ -1682,26 +1684,6 @@ LogMessage::~LogMessage() noexcept(false) {
|
|||||||
#else // !defined(GLOG_THREAD_LOCAL_STORAGE)
|
#else // !defined(GLOG_THREAD_LOCAL_STORAGE)
|
||||||
delete allocated_;
|
delete allocated_;
|
||||||
#endif // defined(GLOG_THREAD_LOCAL_STORAGE)
|
#endif // defined(GLOG_THREAD_LOCAL_STORAGE)
|
||||||
//
|
|
||||||
|
|
||||||
if (fail) {
|
|
||||||
const char* message = "*** Check failure stack trace: ***\n";
|
|
||||||
if (write(fileno(stderr), message, strlen(message)) < 0) {
|
|
||||||
// Ignore errors.
|
|
||||||
}
|
|
||||||
AlsoErrorWrite(GLOG_FATAL,
|
|
||||||
glog_internal_namespace_::ProgramInvocationShortName(),
|
|
||||||
message);
|
|
||||||
#if defined(__cpp_lib_uncaught_exceptions) && \
|
|
||||||
(__cpp_lib_uncaught_exceptions >= 201411L)
|
|
||||||
if (std::uncaught_exceptions() == 0)
|
|
||||||
#else
|
|
||||||
if (!std::uncaught_exception())
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
Fail();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int LogMessage::preserved_errno() const { return data_->preserved_errno_; }
|
int LogMessage::preserved_errno() const { return data_->preserved_errno_; }
|
||||||
@ -1861,11 +1843,30 @@ void LogMessage::SendToLog() EXCLUSIVE_LOCKS_REQUIRED(log_mutex) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// release the lock that our caller (directly or indirectly)
|
||||||
|
// LogMessage::~LogMessage() grabbed so that signal handlers
|
||||||
|
// can use the logging facility. Alternately, we could add
|
||||||
|
// an entire unsafe logging interface to bypass locking
|
||||||
|
// for signal handlers but this seems simpler.
|
||||||
|
log_mutex.unlock();
|
||||||
LogDestination::WaitForSinks(data_);
|
LogDestination::WaitForSinks(data_);
|
||||||
|
|
||||||
|
const char* message = "*** Check failure stack trace: ***\n";
|
||||||
|
if (write(STDERR_FILENO, message, strlen(message)) < 0) {
|
||||||
|
// Ignore errors.
|
||||||
|
}
|
||||||
|
#if defined(__ANDROID__)
|
||||||
|
// ANDROID_LOG_FATAL as this message is of FATAL severity.
|
||||||
|
__android_log_write(ANDROID_LOG_FATAL,
|
||||||
|
glog_internal_namespace_::ProgramInvocationShortName(),
|
||||||
|
message);
|
||||||
|
#endif
|
||||||
|
Fail();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LogMessage::RecordCrashReason(logging::internal::CrashReason* reason) {
|
void LogMessage::RecordCrashReason(
|
||||||
|
glog_internal_namespace_::CrashReason* reason) {
|
||||||
reason->filename = fatal_msg_data_exclusive.fullname_;
|
reason->filename = fatal_msg_data_exclusive.fullname_;
|
||||||
reason->line_number = fatal_msg_data_exclusive.line_;
|
reason->line_number = fatal_msg_data_exclusive.line_;
|
||||||
reason->message = fatal_msg_data_exclusive.message_text_ +
|
reason->message = fatal_msg_data_exclusive.message_text_ +
|
||||||
@ -1878,12 +1879,12 @@ void LogMessage::RecordCrashReason(logging::internal::CrashReason* reason) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
GLOG_NO_EXPORT logging_fail_func_t g_logging_fail_func =
|
GLOG_EXPORT logging_fail_func_t g_logging_fail_func =
|
||||||
reinterpret_cast<logging_fail_func_t>(&abort);
|
reinterpret_cast<logging_fail_func_t>(&abort);
|
||||||
|
|
||||||
NullStream::NullStream() : LogMessage::LogStream(message_buffer_, 2, 0) {}
|
NullStream::NullStream() : LogMessage::LogStream(message_buffer_, 2, 0) {}
|
||||||
NullStream::NullStream(const char* /*file*/, int /*line*/,
|
NullStream::NullStream(const char* /*file*/, int /*line*/,
|
||||||
const logging::internal::CheckOpString& /*result*/)
|
const CheckOpString& /*result*/)
|
||||||
: LogMessage::LogStream(message_buffer_, 2, 0) {}
|
: LogMessage::LogStream(message_buffer_, 2, 0) {}
|
||||||
NullStream& NullStream::stream() { return *this; }
|
NullStream& NullStream::stream() { return *this; }
|
||||||
|
|
||||||
@ -1893,8 +1894,8 @@ NullStreamFatal::~NullStreamFatal() {
|
|||||||
std::abort();
|
std::abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
logging_fail_func_t InstallFailureFunction(logging_fail_func_t fail_func) {
|
void InstallFailureFunction(logging_fail_func_t fail_func) {
|
||||||
return std::exchange(g_logging_fail_func, fail_func);
|
g_logging_fail_func = fail_func;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LogMessage::Fail() { g_logging_fail_func(); }
|
void LogMessage::Fail() { g_logging_fail_func(); }
|
||||||
@ -2028,25 +2029,58 @@ void SetLogSymlink(LogSeverity severity, const char* symlink_basename) {
|
|||||||
|
|
||||||
LogSink::~LogSink() = default;
|
LogSink::~LogSink() = default;
|
||||||
|
|
||||||
|
void LogSink::send(LogSeverity severity, const char* full_filename,
|
||||||
|
const char* base_filename, int line,
|
||||||
|
const LogMessageTime& time, const char* message,
|
||||||
|
size_t message_len) {
|
||||||
|
#if defined(__GNUG__)
|
||||||
|
# pragma GCC diagnostic push
|
||||||
|
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
# pragma warning(push)
|
||||||
|
# pragma warning(disable : 4996)
|
||||||
|
#endif // __GNUG__
|
||||||
|
send(severity, full_filename, base_filename, line, &time.tm(), message,
|
||||||
|
message_len);
|
||||||
|
#if defined(__GNUG__)
|
||||||
|
# pragma GCC diagnostic pop
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
# pragma warning(pop)
|
||||||
|
#endif // __GNUG__
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogSink::send(LogSeverity severity, const char* full_filename,
|
||||||
|
const char* base_filename, int line, const std::tm* t,
|
||||||
|
const char* message, size_t message_len) {
|
||||||
|
(void)severity;
|
||||||
|
(void)full_filename;
|
||||||
|
(void)base_filename;
|
||||||
|
(void)line;
|
||||||
|
(void)t;
|
||||||
|
(void)message;
|
||||||
|
(void)message_len;
|
||||||
|
}
|
||||||
|
|
||||||
void LogSink::WaitTillSent() {
|
void LogSink::WaitTillSent() {
|
||||||
// noop default
|
// noop default
|
||||||
}
|
}
|
||||||
|
|
||||||
string LogSink::ToString(LogSeverity severity, const char* file, int line,
|
string LogSink::ToString(LogSeverity severity, const char* file, int line,
|
||||||
const LogMessageTime& time, const char* message,
|
const LogMessageTime& logmsgtime, const char* message,
|
||||||
size_t message_len) {
|
size_t message_len) {
|
||||||
ostringstream stream;
|
ostringstream stream;
|
||||||
stream.fill('0');
|
stream.fill('0');
|
||||||
|
|
||||||
stream << LogSeverityNames[severity][0];
|
stream << LogSeverityNames[severity][0];
|
||||||
if (FLAGS_log_year_in_prefix) {
|
if (FLAGS_log_year_in_prefix) {
|
||||||
stream << setw(4) << 1900 + time.year();
|
stream << setw(4) << 1900 + logmsgtime.year();
|
||||||
}
|
}
|
||||||
stream << setw(2) << 1 + time.month() << setw(2) << time.day() << ' '
|
stream << setw(2) << 1 + logmsgtime.month() << setw(2) << logmsgtime.day()
|
||||||
<< setw(2) << time.hour() << ':' << setw(2) << time.min() << ':'
|
<< ' ' << setw(2) << logmsgtime.hour() << ':' << setw(2)
|
||||||
<< setw(2) << time.sec() << '.' << setw(6) << time.usec() << ' '
|
<< logmsgtime.min() << ':' << setw(2) << logmsgtime.sec() << '.'
|
||||||
<< setfill(' ') << setw(5) << std::this_thread::get_id()
|
<< setw(6) << logmsgtime.usec() << ' ' << setfill(' ') << setw(5)
|
||||||
<< setfill('0') << ' ' << file << ':' << line << "] ";
|
<< std::this_thread::get_id() << setfill('0') << ' ' << file << ':'
|
||||||
|
<< line << "] ";
|
||||||
|
|
||||||
// A call to `write' is enclosed in parenthneses to prevent possible macro
|
// A call to `write' is enclosed in parenthneses to prevent possible macro
|
||||||
// expansion. On Windows, `write' could be a macro defined for portability.
|
// expansion. On Windows, `write' could be a macro defined for portability.
|
||||||
@ -2258,17 +2292,19 @@ bool SendEmail(const char* dest, const char* subject, const char* body) {
|
|||||||
return SendEmailInternal(dest, subject, body, true);
|
return SendEmailInternal(dest, subject, body, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void GetTempDirectories(vector<string>& list) {
|
static void GetTempDirectories(vector<string>* list) {
|
||||||
list.clear();
|
list->clear();
|
||||||
#ifdef GLOG_OS_WINDOWS
|
#ifdef GLOG_OS_WINDOWS
|
||||||
// On windows we'll try to find a directory in this order:
|
// On windows we'll try to find a directory in this order:
|
||||||
// C:/Documents & Settings/whomever/TEMP (or whatever GetTempPath() is)
|
// C:/Documents & Settings/whomever/TEMP (or whatever GetTempPath() is)
|
||||||
// C:/TMP/
|
// C:/TMP/
|
||||||
// C:/TEMP/
|
// C:/TEMP/
|
||||||
|
// C:/WINDOWS/ or C:/WINNT/
|
||||||
|
// .
|
||||||
char tmp[MAX_PATH];
|
char tmp[MAX_PATH];
|
||||||
if (GetTempPathA(MAX_PATH, tmp)) list.push_back(tmp);
|
if (GetTempPathA(MAX_PATH, tmp)) list->push_back(tmp);
|
||||||
list.push_back("C:\\TMP\\");
|
list->push_back("C:\\tmp\\");
|
||||||
list.push_back("C:\\TEMP\\");
|
list->push_back("C:\\temp\\");
|
||||||
#else
|
#else
|
||||||
// Directories, in order of preference. If we find a dir that
|
// Directories, in order of preference. If we find a dir that
|
||||||
// exists, we stop adding other less-preferred dirs
|
// exists, we stop adding other less-preferred dirs
|
||||||
@ -2292,7 +2328,7 @@ static void GetTempDirectories(vector<string>& list) {
|
|||||||
if (dstr[dstr.size() - 1] != '/') {
|
if (dstr[dstr.size() - 1] != '/') {
|
||||||
dstr += "/";
|
dstr += "/";
|
||||||
}
|
}
|
||||||
list.push_back(dstr);
|
list->push_back(dstr);
|
||||||
|
|
||||||
struct stat statbuf;
|
struct stat statbuf;
|
||||||
if (!stat(d, &statbuf) && S_ISDIR(statbuf.st_mode)) {
|
if (!stat(d, &statbuf) && S_ISDIR(statbuf.st_mode)) {
|
||||||
@ -2300,15 +2336,16 @@ static void GetTempDirectories(vector<string>& list) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::unique_ptr<std::vector<std::string>> logging_directories_list;
|
static vector<string>* logging_directories_list;
|
||||||
|
|
||||||
const vector<string>& GetLoggingDirectories() {
|
const vector<string>& GetLoggingDirectories() {
|
||||||
// Not strictly thread-safe but we're called early in InitGoogle().
|
// Not strictly thread-safe but we're called early in InitGoogle().
|
||||||
if (logging_directories_list == nullptr) {
|
if (logging_directories_list == nullptr) {
|
||||||
logging_directories_list = std::make_unique<std::vector<std::string>>();
|
logging_directories_list = new vector<string>;
|
||||||
|
|
||||||
if (!FLAGS_log_dir.empty()) {
|
if (!FLAGS_log_dir.empty()) {
|
||||||
// Ensure the specified path ends with a directory delimiter.
|
// Ensure the specified path ends with a directory delimiter.
|
||||||
@ -2320,7 +2357,7 @@ const vector<string>& GetLoggingDirectories() {
|
|||||||
logging_directories_list->push_back(FLAGS_log_dir);
|
logging_directories_list->push_back(FLAGS_log_dir);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
GetTempDirectories(*logging_directories_list);
|
GetTempDirectories(logging_directories_list);
|
||||||
#ifdef GLOG_OS_WINDOWS
|
#ifdef GLOG_OS_WINDOWS
|
||||||
char tmp[MAX_PATH];
|
char tmp[MAX_PATH];
|
||||||
if (GetWindowsDirectoryA(tmp, MAX_PATH))
|
if (GetWindowsDirectoryA(tmp, MAX_PATH))
|
||||||
@ -2338,14 +2375,14 @@ const vector<string>& GetLoggingDirectories() {
|
|||||||
// subset of the directories returned by GetLoggingDirectories().
|
// subset of the directories returned by GetLoggingDirectories().
|
||||||
// Thread-safe.
|
// Thread-safe.
|
||||||
GLOG_NO_EXPORT
|
GLOG_NO_EXPORT
|
||||||
void GetExistingTempDirectories(vector<string>& list) {
|
void GetExistingTempDirectories(vector<string>* list) {
|
||||||
GetTempDirectories(list);
|
GetTempDirectories(list);
|
||||||
auto i_dir = list.begin();
|
auto i_dir = list->begin();
|
||||||
while (i_dir != list.end()) {
|
while (i_dir != list->end()) {
|
||||||
// zero arg to access means test for existence; no constant
|
// zero arg to access means test for existence; no constant
|
||||||
// defined on windows
|
// defined on windows
|
||||||
if (access(i_dir->c_str(), 0)) {
|
if (access(i_dir->c_str(), 0)) {
|
||||||
i_dir = list.erase(i_dir);
|
i_dir = list->erase(i_dir);
|
||||||
} else {
|
} else {
|
||||||
++i_dir;
|
++i_dir;
|
||||||
}
|
}
|
||||||
@ -2366,8 +2403,8 @@ void TruncateLogFile(const char* path, uint64 limit, uint64 keep) {
|
|||||||
if (strncmp(procfd_prefix, path, strlen(procfd_prefix))) flags |= O_NOFOLLOW;
|
if (strncmp(procfd_prefix, path, strlen(procfd_prefix))) flags |= O_NOFOLLOW;
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
FileDescriptor fd{open(path, flags)};
|
int fd = open(path, flags);
|
||||||
if (!fd) {
|
if (fd == -1) {
|
||||||
if (errno == EFBIG) {
|
if (errno == EFBIG) {
|
||||||
// The log file in question has got too big for us to open. The
|
// The log file in question has got too big for us to open. The
|
||||||
// real fix for this would be to compile logging.cc (or probably
|
// real fix for this would be to compile logging.cc (or probably
|
||||||
@ -2375,7 +2412,7 @@ void TruncateLogFile(const char* path, uint64 limit, uint64 keep) {
|
|||||||
// rather scary.
|
// rather scary.
|
||||||
// Instead just truncate the file to something we can manage
|
// Instead just truncate the file to something we can manage
|
||||||
# ifdef HAVE__CHSIZE_S
|
# ifdef HAVE__CHSIZE_S
|
||||||
if (_chsize_s(fd.get(), 0) != 0) {
|
if (_chsize_s(fd, 0) != 0) {
|
||||||
# else
|
# else
|
||||||
if (truncate(path, 0) == -1) {
|
if (truncate(path, 0) == -1) {
|
||||||
# endif
|
# endif
|
||||||
@ -2389,16 +2426,16 @@ void TruncateLogFile(const char* path, uint64 limit, uint64 keep) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fstat(fd.get(), &statbuf) == -1) {
|
if (fstat(fd, &statbuf) == -1) {
|
||||||
PLOG(ERROR) << "Unable to fstat()";
|
PLOG(ERROR) << "Unable to fstat()";
|
||||||
return;
|
goto out_close_fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
// See if the path refers to a regular file bigger than the
|
// See if the path refers to a regular file bigger than the
|
||||||
// specified limit
|
// specified limit
|
||||||
if (!S_ISREG(statbuf.st_mode)) return;
|
if (!S_ISREG(statbuf.st_mode)) goto out_close_fd;
|
||||||
if (statbuf.st_size <= static_cast<off_t>(limit)) return;
|
if (statbuf.st_size <= static_cast<off_t>(limit)) goto out_close_fd;
|
||||||
if (statbuf.st_size <= static_cast<off_t>(keep)) return;
|
if (statbuf.st_size <= static_cast<off_t>(keep)) goto out_close_fd;
|
||||||
|
|
||||||
// This log file is too large - we need to truncate it
|
// This log file is too large - we need to truncate it
|
||||||
LOG(INFO) << "Truncating " << path << " to " << keep << " bytes";
|
LOG(INFO) << "Truncating " << path << " to " << keep << " bytes";
|
||||||
@ -2407,10 +2444,8 @@ void TruncateLogFile(const char* path, uint64 limit, uint64 keep) {
|
|||||||
read_offset = statbuf.st_size - static_cast<off_t>(keep);
|
read_offset = statbuf.st_size - static_cast<off_t>(keep);
|
||||||
write_offset = 0;
|
write_offset = 0;
|
||||||
ssize_t bytesin, bytesout;
|
ssize_t bytesin, bytesout;
|
||||||
while ((bytesin = pread(fd.get(), copybuf, sizeof(copybuf), read_offset)) >
|
while ((bytesin = pread(fd, copybuf, sizeof(copybuf), read_offset)) > 0) {
|
||||||
0) {
|
bytesout = pwrite(fd, copybuf, static_cast<size_t>(bytesin), write_offset);
|
||||||
bytesout =
|
|
||||||
pwrite(fd.get(), copybuf, static_cast<size_t>(bytesin), write_offset);
|
|
||||||
if (bytesout == -1) {
|
if (bytesout == -1) {
|
||||||
PLOG(ERROR) << "Unable to write to " << path;
|
PLOG(ERROR) << "Unable to write to " << path;
|
||||||
break;
|
break;
|
||||||
@ -2426,13 +2461,15 @@ void TruncateLogFile(const char* path, uint64 limit, uint64 keep) {
|
|||||||
// end of the file after our last read() above, we lose their latest
|
// end of the file after our last read() above, we lose their latest
|
||||||
// data. Too bad ...
|
// data. Too bad ...
|
||||||
# ifdef HAVE__CHSIZE_S
|
# ifdef HAVE__CHSIZE_S
|
||||||
if (_chsize_s(fd.get(), write_offset) != 0) {
|
if (_chsize_s(fd, write_offset) != 0) {
|
||||||
# else
|
# else
|
||||||
if (ftruncate(fd.get(), write_offset) == -1) {
|
if (ftruncate(fd, write_offset) == -1) {
|
||||||
# endif
|
# endif
|
||||||
PLOG(ERROR) << "Unable to truncate " << path;
|
PLOG(ERROR) << "Unable to truncate " << path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
out_close_fd:
|
||||||
|
close(fd);
|
||||||
#else
|
#else
|
||||||
LOG(ERROR) << "No log truncation support.";
|
LOG(ERROR) << "No log truncation support.";
|
||||||
#endif
|
#endif
|
||||||
@ -2449,21 +2486,19 @@ void TruncateStdoutStderr() {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace logging {
|
|
||||||
namespace internal {
|
|
||||||
// Helper functions for string comparisons.
|
// Helper functions for string comparisons.
|
||||||
#define DEFINE_CHECK_STROP_IMPL(name, func, expected) \
|
#define DEFINE_CHECK_STROP_IMPL(name, func, expected) \
|
||||||
std::unique_ptr<string> Check##func##expected##Impl( \
|
string* Check##func##expected##Impl(const char* s1, const char* s2, \
|
||||||
const char* s1, const char* s2, const char* names) { \
|
const char* names) { \
|
||||||
bool equal = s1 == s2 || (s1 && s2 && !func(s1, s2)); \
|
bool equal = s1 == s2 || (s1 && s2 && !func(s1, s2)); \
|
||||||
if (equal == (expected)) \
|
if (equal == expected) \
|
||||||
return nullptr; \
|
return nullptr; \
|
||||||
else { \
|
else { \
|
||||||
ostringstream ss; \
|
ostringstream ss; \
|
||||||
if (!s1) s1 = ""; \
|
if (!s1) s1 = ""; \
|
||||||
if (!s2) s2 = ""; \
|
if (!s2) s2 = ""; \
|
||||||
ss << #name " failed: " << names << " (" << s1 << " vs. " << s2 << ")"; \
|
ss << #name " failed: " << names << " (" << s1 << " vs. " << s2 << ")"; \
|
||||||
return std::make_unique<std::string>(ss.str()); \
|
return new string(ss.str()); \
|
||||||
} \
|
} \
|
||||||
}
|
}
|
||||||
DEFINE_CHECK_STROP_IMPL(CHECK_STREQ, strcmp, true)
|
DEFINE_CHECK_STROP_IMPL(CHECK_STREQ, strcmp, true)
|
||||||
@ -2471,8 +2506,6 @@ DEFINE_CHECK_STROP_IMPL(CHECK_STRNE, strcmp, false)
|
|||||||
DEFINE_CHECK_STROP_IMPL(CHECK_STRCASEEQ, strcasecmp, true)
|
DEFINE_CHECK_STROP_IMPL(CHECK_STRCASEEQ, strcasecmp, true)
|
||||||
DEFINE_CHECK_STROP_IMPL(CHECK_STRCASENE, strcasecmp, false)
|
DEFINE_CHECK_STROP_IMPL(CHECK_STRCASENE, strcasecmp, false)
|
||||||
#undef DEFINE_CHECK_STROP_IMPL
|
#undef DEFINE_CHECK_STROP_IMPL
|
||||||
} // namespace internal
|
|
||||||
} // namespace logging
|
|
||||||
|
|
||||||
// glibc has traditionally implemented two incompatible versions of
|
// glibc has traditionally implemented two incompatible versions of
|
||||||
// strerror_r(). There is a poorly defined convention for picking the
|
// strerror_r(). There is a poorly defined convention for picking the
|
||||||
@ -2539,6 +2572,7 @@ int posix_strerror_r(int err, char* buf, size_t len) {
|
|||||||
|
|
||||||
// A thread-safe replacement for strerror(). Returns a string describing the
|
// A thread-safe replacement for strerror(). Returns a string describing the
|
||||||
// given POSIX error code.
|
// given POSIX error code.
|
||||||
|
GLOG_NO_EXPORT
|
||||||
string StrError(int err) {
|
string StrError(int err) {
|
||||||
char buf[100];
|
char buf[100];
|
||||||
int rc = posix_strerror_r(err, buf, sizeof(buf));
|
int rc = posix_strerror_r(err, buf, sizeof(buf));
|
||||||
@ -2552,16 +2586,15 @@ LogMessageFatal::LogMessageFatal(const char* file, int line)
|
|||||||
: LogMessage(file, line, GLOG_FATAL) {}
|
: LogMessage(file, line, GLOG_FATAL) {}
|
||||||
|
|
||||||
LogMessageFatal::LogMessageFatal(const char* file, int line,
|
LogMessageFatal::LogMessageFatal(const char* file, int line,
|
||||||
const logging::internal::CheckOpString& result)
|
const CheckOpString& result)
|
||||||
: LogMessage(file, line, result) {}
|
: LogMessage(file, line, result) {}
|
||||||
|
|
||||||
LogMessageFatal::~LogMessageFatal() noexcept(false) {
|
LogMessageFatal::~LogMessageFatal() {
|
||||||
Flush();
|
Flush();
|
||||||
LogMessage::Fail();
|
LogMessage::Fail();
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace logging {
|
namespace base {
|
||||||
namespace internal {
|
|
||||||
|
|
||||||
CheckOpMessageBuilder::CheckOpMessageBuilder(const char* exprtext)
|
CheckOpMessageBuilder::CheckOpMessageBuilder(const char* exprtext)
|
||||||
: stream_(new ostringstream) {
|
: stream_(new ostringstream) {
|
||||||
@ -2575,11 +2608,13 @@ ostream* CheckOpMessageBuilder::ForVar2() {
|
|||||||
return stream_;
|
return stream_;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<string> CheckOpMessageBuilder::NewString() {
|
string* CheckOpMessageBuilder::NewString() {
|
||||||
*stream_ << ")";
|
*stream_ << ")";
|
||||||
return std::make_unique<std::string>(stream_->str());
|
return new string(stream_->str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace base
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
void MakeCheckOpValueString(std::ostream* os, const char& v) {
|
void MakeCheckOpValueString(std::ostream* os, const char& v) {
|
||||||
if (v >= 32 && v <= 126) {
|
if (v >= 32 && v <= 126) {
|
||||||
@ -2612,24 +2647,22 @@ void MakeCheckOpValueString(std::ostream* os, const std::nullptr_t& /*v*/) {
|
|||||||
(*os) << "nullptr";
|
(*os) << "nullptr";
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace internal
|
void InitGoogleLogging(const char* argv0) {
|
||||||
} // namespace logging
|
glog_internal_namespace_::InitGoogleLoggingUtilities(argv0);
|
||||||
|
}
|
||||||
|
|
||||||
void InitGoogleLogging(const char* argv0) { InitGoogleLoggingUtilities(argv0); }
|
void InitGoogleLogging(const char* argv0, CustomPrefixCallback prefix_callback,
|
||||||
|
void* prefix_callback_data) {
|
||||||
void InstallPrefixFormatter(PrefixFormatterCallback callback, void* data) {
|
custom_prefix_callback = prefix_callback;
|
||||||
if (callback != nullptr) {
|
custom_prefix_callback_data = prefix_callback_data;
|
||||||
g_prefix_formatter = std::make_unique<PrefixFormatter>(callback, data);
|
InitGoogleLogging(argv0);
|
||||||
} else {
|
|
||||||
g_prefix_formatter = nullptr;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShutdownGoogleLogging() {
|
void ShutdownGoogleLogging() {
|
||||||
ShutdownGoogleLoggingUtilities();
|
glog_internal_namespace_::ShutdownGoogleLoggingUtilities();
|
||||||
LogDestination::DeleteLogDestinations();
|
LogDestination::DeleteLogDestinations();
|
||||||
|
delete logging_directories_list;
|
||||||
logging_directories_list = nullptr;
|
logging_directories_list = nullptr;
|
||||||
g_prefix_formatter = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EnableLogCleaner(unsigned int overdue_days) {
|
void EnableLogCleaner(unsigned int overdue_days) {
|
||||||
@ -2646,82 +2679,39 @@ void DisableLogCleaner() { log_cleaner.Disable(); }
|
|||||||
|
|
||||||
LogMessageTime::LogMessageTime() = default;
|
LogMessageTime::LogMessageTime() = default;
|
||||||
|
|
||||||
namespace {
|
LogMessageTime::LogMessageTime(std::chrono::system_clock::time_point now)
|
||||||
|
: timestamp_{now} {
|
||||||
template <class... Args>
|
|
||||||
struct void_impl {
|
|
||||||
using type = void;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class... Args>
|
|
||||||
using void_t = typename void_impl<Args...>::type;
|
|
||||||
|
|
||||||
template <class T, class E = void>
|
|
||||||
struct has_member_tm_gmtoff : std::false_type {};
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
struct has_member_tm_gmtoff<T, void_t<decltype(&T::tm_gmtoff)>>
|
|
||||||
: std::true_type {};
|
|
||||||
|
|
||||||
template <class T = std::tm>
|
|
||||||
auto Breakdown(const std::chrono::system_clock::time_point& now)
|
|
||||||
-> std::enable_if_t<!has_member_tm_gmtoff<T>::value,
|
|
||||||
std::tuple<std::tm, std::time_t, std::chrono::hours>> {
|
|
||||||
std::time_t timestamp = std::chrono::system_clock::to_time_t(now);
|
std::time_t timestamp = std::chrono::system_clock::to_time_t(now);
|
||||||
std::tm tm_local;
|
|
||||||
std::tm tm_utc;
|
|
||||||
int isdst = 0;
|
|
||||||
|
|
||||||
if (FLAGS_log_utc_time) {
|
if (FLAGS_log_utc_time) {
|
||||||
gmtime_r(×tamp, &tm_local);
|
gmtime_r(×tamp, &tm_);
|
||||||
localtime_r(×tamp, &tm_utc);
|
|
||||||
isdst = tm_utc.tm_isdst;
|
|
||||||
tm_utc = tm_local;
|
|
||||||
} else {
|
} else {
|
||||||
localtime_r(×tamp, &tm_local);
|
localtime_r(×tamp, &tm_);
|
||||||
isdst = tm_local.tm_isdst;
|
}
|
||||||
gmtime_r(×tamp, &tm_utc);
|
usecs_ = std::chrono::duration_cast<std::chrono::microseconds>(
|
||||||
|
now - std::chrono::system_clock::from_time_t(timestamp));
|
||||||
|
CalcGmtOffset(timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogMessageTime::CalcGmtOffset(std::time_t t) {
|
||||||
|
std::tm gmt_struct;
|
||||||
|
int isDst = 0;
|
||||||
|
if (FLAGS_log_utc_time) {
|
||||||
|
localtime_r(&t, &gmt_struct);
|
||||||
|
isDst = gmt_struct.tm_isdst;
|
||||||
|
gmt_struct = tm_;
|
||||||
|
} else {
|
||||||
|
isDst = tm_.tm_isdst;
|
||||||
|
gmtime_r(&t, &gmt_struct);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::time_t gmt_sec = std::mktime(&tm_utc);
|
time_t gmt_sec = mktime(&gmt_struct);
|
||||||
|
|
||||||
// If the Daylight Saving Time(isDst) is active subtract an hour from the
|
// If the Daylight Saving Time(isDst) is active subtract an hour from the
|
||||||
// current timestamp.
|
// current timestamp.
|
||||||
using namespace std::chrono_literals;
|
using namespace std::chrono_literals;
|
||||||
const auto gmtoffset = std::chrono::duration_cast<std::chrono::hours>(
|
gmtoffset_ = std::chrono::duration_cast<std::chrono::seconds>(
|
||||||
now - std::chrono::system_clock::from_time_t(gmt_sec) +
|
timestamp_ - std::chrono::system_clock::from_time_t(gmt_sec) +
|
||||||
(isdst ? 1h : 0h));
|
(isDst ? 1h : 0h));
|
||||||
|
|
||||||
return std::make_tuple(tm_local, timestamp, gmtoffset);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T = std::tm>
|
|
||||||
auto Breakdown(const std::chrono::system_clock::time_point& now)
|
|
||||||
-> std::enable_if_t<has_member_tm_gmtoff<T>::value,
|
|
||||||
std::tuple<std::tm, std::time_t, std::chrono::hours>> {
|
|
||||||
std::time_t timestamp = std::chrono::system_clock::to_time_t(now);
|
|
||||||
T tm;
|
|
||||||
|
|
||||||
if (FLAGS_log_utc_time) {
|
|
||||||
gmtime_r(×tamp, &tm);
|
|
||||||
} else {
|
|
||||||
localtime_r(×tamp, &tm);
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto gmtoffset = std::chrono::duration_cast<std::chrono::hours>(
|
|
||||||
std::chrono::seconds{tm.tm_gmtoff});
|
|
||||||
|
|
||||||
return std::make_tuple(tm, timestamp, gmtoffset);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
LogMessageTime::LogMessageTime(std::chrono::system_clock::time_point now)
|
|
||||||
: timestamp_{now} {
|
|
||||||
std::time_t timestamp;
|
|
||||||
std::tie(tm_, timestamp, gmtoffset_) = Breakdown(now);
|
|
||||||
usecs_ = std::chrono::duration_cast<std::chrono::microseconds>(
|
|
||||||
now - std::chrono::system_clock::from_time_t(timestamp));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace google
|
} // namespace google
|
||||||
|
|||||||
@ -31,22 +31,8 @@
|
|||||||
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <fstream>
|
|
||||||
#include <iomanip>
|
|
||||||
#include <iostream>
|
|
||||||
#include <memory>
|
|
||||||
#include <mutex>
|
|
||||||
#include <queue>
|
|
||||||
#include <sstream>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <string>
|
|
||||||
#include <thread>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
#include "utilities.h"
|
||||||
#ifdef HAVE_GLOB_H
|
#ifdef HAVE_GLOB_H
|
||||||
# include <glob.h>
|
# include <glob.h>
|
||||||
#endif
|
#endif
|
||||||
@ -58,12 +44,24 @@
|
|||||||
# include <sys/wait.h>
|
# include <sys/wait.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <queue>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "base/commandlineflags.h"
|
#include "base/commandlineflags.h"
|
||||||
#include "glog/logging.h"
|
#include "glog/logging.h"
|
||||||
#include "glog/raw_logging.h"
|
#include "glog/raw_logging.h"
|
||||||
#include "googletest.h"
|
#include "googletest.h"
|
||||||
#include "stacktrace.h"
|
|
||||||
#include "utilities.h"
|
|
||||||
|
|
||||||
#ifdef GLOG_USE_GFLAGS
|
#ifdef GLOG_USE_GFLAGS
|
||||||
# include <gflags/gflags.h>
|
# include <gflags/gflags.h>
|
||||||
@ -110,7 +108,6 @@ static void TestLogSinkWaitTillSent();
|
|||||||
static void TestCHECK();
|
static void TestCHECK();
|
||||||
static void TestDCHECK();
|
static void TestDCHECK();
|
||||||
static void TestSTREQ();
|
static void TestSTREQ();
|
||||||
static void TestMaxLogSizeWhenNoTimestamp();
|
|
||||||
static void TestBasename();
|
static void TestBasename();
|
||||||
static void TestBasenameAppendWhenNoTimestamp();
|
static void TestBasenameAppendWhenNoTimestamp();
|
||||||
static void TestTwoProcessesWrite();
|
static void TestTwoProcessesWrite();
|
||||||
@ -187,27 +184,23 @@ static void BM_vlog(int n) {
|
|||||||
}
|
}
|
||||||
BENCHMARK(BM_vlog)
|
BENCHMARK(BM_vlog)
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
// Dynamically generate a prefix using the default format and write it to the
|
// Dynamically generate a prefix using the default format and write it to the
|
||||||
// stream.
|
// stream.
|
||||||
void PrefixAttacher(std::ostream& s, const LogMessage& m, void* data) {
|
void PrefixAttacher(std::ostream& s, const LogMessageInfo& l, void* data) {
|
||||||
// Assert that `data` contains the expected contents before producing the
|
// Assert that `data` contains the expected contents before producing the
|
||||||
// prefix (otherwise causing the tests to fail):
|
// prefix (otherwise causing the tests to fail):
|
||||||
if (data == nullptr || *static_cast<string*>(data) != "good data") {
|
if (data == nullptr || *static_cast<string*>(data) != "good data") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
s << GetLogSeverityName(m.severity())[0] << setw(4) << 1900 + m.time().year()
|
s << l.severity[0] << setw(4) << 1900 + l.time.year() << setw(2)
|
||||||
<< setw(2) << 1 + m.time().month() << setw(2) << m.time().day() << ' '
|
<< 1 + l.time.month() << setw(2) << l.time.day() << ' ' << setw(2)
|
||||||
<< setw(2) << m.time().hour() << ':' << setw(2) << m.time().min() << ':'
|
<< l.time.hour() << ':' << setw(2) << l.time.min() << ':' << setw(2)
|
||||||
<< setw(2) << m.time().sec() << "." << setw(6) << m.time().usec() << ' '
|
<< l.time.sec() << "." << setw(6) << l.time.usec() << ' ' << setfill(' ')
|
||||||
<< setfill(' ') << setw(5) << m.thread_id() << setfill('0') << ' '
|
<< setw(5) << l.thread_id << setfill('0') << ' ' << l.filename << ':'
|
||||||
<< m.basename() << ':' << m.line() << "]";
|
<< l.line_number << "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
FLAGS_colorlogtostderr = false;
|
FLAGS_colorlogtostderr = false;
|
||||||
FLAGS_timestamp_in_logfile_name = true;
|
FLAGS_timestamp_in_logfile_name = true;
|
||||||
@ -228,8 +221,8 @@ int main(int argc, char** argv) {
|
|||||||
// Setting a custom prefix generator (it will use the default format so that
|
// Setting a custom prefix generator (it will use the default format so that
|
||||||
// the golden outputs can be reused):
|
// the golden outputs can be reused):
|
||||||
string prefix_attacher_data = "good data";
|
string prefix_attacher_data = "good data";
|
||||||
InitGoogleLogging(argv[0]);
|
InitGoogleLogging(argv[0], &PrefixAttacher,
|
||||||
InstallPrefixFormatter(&PrefixAttacher, &prefix_attacher_data);
|
static_cast<void*>(&prefix_attacher_data));
|
||||||
|
|
||||||
EXPECT_TRUE(IsGoogleLoggingInitialized());
|
EXPECT_TRUE(IsGoogleLoggingInitialized());
|
||||||
|
|
||||||
@ -289,7 +282,6 @@ int main(int argc, char** argv) {
|
|||||||
MungeAndDiffTestStdout(FLAGS_test_srcdir + "/src/logging_unittest.out"));
|
MungeAndDiffTestStdout(FLAGS_test_srcdir + "/src/logging_unittest.out"));
|
||||||
FLAGS_logtostdout = false;
|
FLAGS_logtostdout = false;
|
||||||
|
|
||||||
TestMaxLogSizeWhenNoTimestamp();
|
|
||||||
TestBasename();
|
TestBasename();
|
||||||
TestBasenameAppendWhenNoTimestamp();
|
TestBasenameAppendWhenNoTimestamp();
|
||||||
TestTwoProcessesWrite();
|
TestTwoProcessesWrite();
|
||||||
@ -793,62 +785,23 @@ static void CheckFile(const string& name, const string& expected_string,
|
|||||||
GetFiles(name + "*", &files);
|
GetFiles(name + "*", &files);
|
||||||
CHECK_EQ(files.size(), 1UL);
|
CHECK_EQ(files.size(), 1UL);
|
||||||
|
|
||||||
std::unique_ptr<std::FILE> file{fopen(files[0].c_str(), "r")};
|
FILE* file = fopen(files[0].c_str(), "r");
|
||||||
CHECK(file != nullptr) << ": could not open " << files[0];
|
CHECK(file != nullptr) << ": could not open " << files[0];
|
||||||
char buf[1000];
|
char buf[1000];
|
||||||
while (fgets(buf, sizeof(buf), file.get()) != nullptr) {
|
while (fgets(buf, sizeof(buf), file) != nullptr) {
|
||||||
char* first = strstr(buf, expected_string.c_str());
|
char* first = strstr(buf, expected_string.c_str());
|
||||||
// if first == nullptr, not found.
|
// if first == nullptr, not found.
|
||||||
// Terser than if (checkInFileOrNot && first != nullptr || !check...
|
// Terser than if (checkInFileOrNot && first != nullptr || !check...
|
||||||
if (checkInFileOrNot != (first == nullptr)) {
|
if (checkInFileOrNot != (first == nullptr)) {
|
||||||
|
fclose(file);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fclose(file);
|
||||||
LOG(FATAL) << "Did " << (checkInFileOrNot ? "not " : "") << "find "
|
LOG(FATAL) << "Did " << (checkInFileOrNot ? "not " : "") << "find "
|
||||||
<< expected_string << " in " << files[0];
|
<< expected_string << " in " << files[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
static void TestMaxLogSizeWhenNoTimestamp() {
|
|
||||||
fprintf(stderr, "==== Test setting max log size without timestamp\n");
|
|
||||||
const string dest = FLAGS_test_tmpdir + "/logging_test_max_log_size";
|
|
||||||
DeleteFiles(dest + "*");
|
|
||||||
|
|
||||||
auto original_max_log_size = FLAGS_max_log_size;
|
|
||||||
auto original_timestamp_in_logfile_name = FLAGS_timestamp_in_logfile_name;
|
|
||||||
|
|
||||||
FLAGS_max_log_size = 1; // Set max log size to 1MB
|
|
||||||
FLAGS_timestamp_in_logfile_name = false;
|
|
||||||
|
|
||||||
// Set log destination
|
|
||||||
SetLogDestination(GLOG_INFO, dest.c_str());
|
|
||||||
|
|
||||||
// 1e4 info logs -> is about 772 KB in size
|
|
||||||
// 2e4 info logs -> is around 1500 KB in size -> 1.5MB
|
|
||||||
// If our max_log_size constraint is respected, it will truncate earlier logs
|
|
||||||
// and the file size will be lesser than 1MB (around 0.5MB)
|
|
||||||
const int num_logs = 2e4;
|
|
||||||
for (int i = 0; i < num_logs; i++) {
|
|
||||||
LOG(INFO) << "Hello world";
|
|
||||||
}
|
|
||||||
FlushLogFiles(GLOG_INFO);
|
|
||||||
|
|
||||||
// Check log file size
|
|
||||||
struct stat statbuf;
|
|
||||||
stat(dest.c_str(), &statbuf);
|
|
||||||
|
|
||||||
// Verify file size is less than the max log size limit
|
|
||||||
CHECK_LT(static_cast<unsigned int>(statbuf.st_size),
|
|
||||||
FLAGS_max_log_size << 20U);
|
|
||||||
|
|
||||||
// Reset flag values to their original values
|
|
||||||
FLAGS_max_log_size = original_max_log_size;
|
|
||||||
FLAGS_timestamp_in_logfile_name = original_timestamp_in_logfile_name;
|
|
||||||
|
|
||||||
// Release file handle for the destination file to unlock the file in Windows.
|
|
||||||
LogToStderr();
|
|
||||||
DeleteFiles(dest + "*");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void TestBasename() {
|
static void TestBasename() {
|
||||||
fprintf(stderr, "==== Test setting log file basename\n");
|
fprintf(stderr, "==== Test setting log file basename\n");
|
||||||
const string dest = FLAGS_test_tmpdir + "/logging_test_basename";
|
const string dest = FLAGS_test_tmpdir + "/logging_test_basename";
|
||||||
@ -1024,8 +977,8 @@ static void TestErrno() {
|
|||||||
|
|
||||||
static void TestOneTruncate(const char* path, uint64 limit, uint64 keep,
|
static void TestOneTruncate(const char* path, uint64 limit, uint64 keep,
|
||||||
size_t dsize, size_t ksize, size_t expect) {
|
size_t dsize, size_t ksize, size_t expect) {
|
||||||
FileDescriptor fd{open(path, O_RDWR | O_CREAT | O_TRUNC, 0600)};
|
int fd;
|
||||||
CHECK_ERR(fd);
|
CHECK_ERR(fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0600));
|
||||||
|
|
||||||
const char *discardstr = "DISCARDME!", *keepstr = "KEEPME!";
|
const char *discardstr = "DISCARDME!", *keepstr = "KEEPME!";
|
||||||
const size_t discard_size = strlen(discardstr), keep_size = strlen(keepstr);
|
const size_t discard_size = strlen(discardstr), keep_size = strlen(keepstr);
|
||||||
@ -1034,13 +987,13 @@ static void TestOneTruncate(const char* path, uint64 limit, uint64 keep,
|
|||||||
size_t written = 0;
|
size_t written = 0;
|
||||||
while (written < dsize) {
|
while (written < dsize) {
|
||||||
size_t bytes = min(dsize - written, discard_size);
|
size_t bytes = min(dsize - written, discard_size);
|
||||||
CHECK_ERR(write(fd.get(), discardstr, bytes));
|
CHECK_ERR(write(fd, discardstr, bytes));
|
||||||
written += bytes;
|
written += bytes;
|
||||||
}
|
}
|
||||||
written = 0;
|
written = 0;
|
||||||
while (written < ksize) {
|
while (written < ksize) {
|
||||||
size_t bytes = min(ksize - written, keep_size);
|
size_t bytes = min(ksize - written, keep_size);
|
||||||
CHECK_ERR(write(fd.get(), keepstr, bytes));
|
CHECK_ERR(write(fd, keepstr, bytes));
|
||||||
written += bytes;
|
written += bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1048,22 +1001,25 @@ static void TestOneTruncate(const char* path, uint64 limit, uint64 keep,
|
|||||||
|
|
||||||
// File should now be shorter
|
// File should now be shorter
|
||||||
struct stat statbuf;
|
struct stat statbuf;
|
||||||
CHECK_ERR(fstat(fd.get(), &statbuf));
|
CHECK_ERR(fstat(fd, &statbuf));
|
||||||
CHECK_EQ(static_cast<size_t>(statbuf.st_size), expect);
|
CHECK_EQ(static_cast<size_t>(statbuf.st_size), expect);
|
||||||
CHECK_ERR(lseek(fd.get(), 0, SEEK_SET));
|
CHECK_ERR(lseek(fd, 0, SEEK_SET));
|
||||||
|
|
||||||
// File should contain the suffix of the original file
|
// File should contain the suffix of the original file
|
||||||
const size_t buf_size = static_cast<size_t>(statbuf.st_size) + 1;
|
const size_t buf_size = static_cast<size_t>(statbuf.st_size) + 1;
|
||||||
std::vector<char> buf(buf_size);
|
char* buf = new char[buf_size];
|
||||||
CHECK_ERR(read(fd.get(), buf.data(), buf_size));
|
memset(buf, 0, buf_size);
|
||||||
|
CHECK_ERR(read(fd, buf, buf_size));
|
||||||
|
|
||||||
const char* p = buf.data();
|
const char* p = buf;
|
||||||
size_t checked = 0;
|
size_t checked = 0;
|
||||||
while (checked < expect) {
|
while (checked < expect) {
|
||||||
size_t bytes = min(expect - checked, keep_size);
|
size_t bytes = min(expect - checked, keep_size);
|
||||||
CHECK(!memcmp(p, keepstr, bytes));
|
CHECK(!memcmp(p, keepstr, bytes));
|
||||||
checked += bytes;
|
checked += bytes;
|
||||||
}
|
}
|
||||||
|
close(fd);
|
||||||
|
delete[] buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void TestTruncate() {
|
static void TestTruncate() {
|
||||||
@ -1199,11 +1155,13 @@ static void TestLogPeriodically() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
namespace google {
|
namespace google {
|
||||||
inline namespace glog_internal_namespace_ {
|
namespace glog_internal_namespace_ {
|
||||||
// in logging.cc
|
extern // in logging.cc
|
||||||
extern bool SafeFNMatch_(const char* pattern, size_t patt_len, const char* str,
|
bool
|
||||||
size_t str_len);
|
SafeFNMatch_(const char* pattern, size_t patt_len, const char* str,
|
||||||
|
size_t str_len);
|
||||||
} // namespace glog_internal_namespace_
|
} // namespace glog_internal_namespace_
|
||||||
|
using glog_internal_namespace_::SafeFNMatch_;
|
||||||
} // namespace google
|
} // namespace google
|
||||||
|
|
||||||
static bool WrapSafeFNMatch(string pattern, string str) {
|
static bool WrapSafeFNMatch(string pattern, string str) {
|
||||||
@ -1394,26 +1352,28 @@ static void TestLogSinkWaitTillSent() {
|
|||||||
|
|
||||||
TEST(Strerror, logging) {
|
TEST(Strerror, logging) {
|
||||||
int errcode = EINTR;
|
int errcode = EINTR;
|
||||||
std::string msg = strerror(errcode);
|
char* msg = strdup(strerror(errcode));
|
||||||
const size_t buf_size = msg.size() + 1;
|
const size_t buf_size = strlen(msg) + 1;
|
||||||
std::vector<char> buf(buf_size);
|
char* buf = new char[buf_size];
|
||||||
CHECK_EQ(posix_strerror_r(errcode, nullptr, 0), -1);
|
CHECK_EQ(posix_strerror_r(errcode, nullptr, 0), -1);
|
||||||
buf[0] = 'A';
|
buf[0] = 'A';
|
||||||
CHECK_EQ(posix_strerror_r(errcode, buf.data(), 0), -1);
|
CHECK_EQ(posix_strerror_r(errcode, buf, 0), -1);
|
||||||
CHECK_EQ(buf[0], 'A');
|
CHECK_EQ(buf[0], 'A');
|
||||||
CHECK_EQ(posix_strerror_r(errcode, nullptr, buf_size), -1);
|
CHECK_EQ(posix_strerror_r(errcode, nullptr, buf_size), -1);
|
||||||
#if defined(GLOG_OS_MACOSX) || defined(GLOG_OS_FREEBSD) || \
|
#if defined(GLOG_OS_MACOSX) || defined(GLOG_OS_FREEBSD) || \
|
||||||
defined(GLOG_OS_OPENBSD)
|
defined(GLOG_OS_OPENBSD)
|
||||||
// MacOSX or FreeBSD considers this case is an error since there is
|
// MacOSX or FreeBSD considers this case is an error since there is
|
||||||
// no enough space.
|
// no enough space.
|
||||||
CHECK_EQ(posix_strerror_r(errcode, buf.data(), 1), -1);
|
CHECK_EQ(posix_strerror_r(errcode, buf, 1), -1);
|
||||||
#else
|
#else
|
||||||
CHECK_EQ(posix_strerror_r(errcode, buf.data(), 1), 0);
|
CHECK_EQ(posix_strerror_r(errcode, buf, 1), 0);
|
||||||
#endif
|
#endif
|
||||||
CHECK_STREQ(buf.data(), "");
|
CHECK_STREQ(buf, "");
|
||||||
CHECK_EQ(posix_strerror_r(errcode, buf.data(), buf_size), 0);
|
CHECK_EQ(posix_strerror_r(errcode, buf, buf_size), 0);
|
||||||
CHECK_STREQ(buf.data(), msg.c_str());
|
CHECK_STREQ(buf, msg);
|
||||||
|
delete[] buf;
|
||||||
CHECK_EQ(msg, StrError(errcode));
|
CHECK_EQ(msg, StrError(errcode));
|
||||||
|
free(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Simple routines to look at the sizes of generated code for LOG(FATAL) and
|
// Simple routines to look at the sizes of generated code for LOG(FATAL) and
|
||||||
@ -1577,12 +1537,12 @@ TEST(LogMsgTime, gmtoff) {
|
|||||||
* */
|
* */
|
||||||
google::LogMessage log_obj(__FILE__, __LINE__);
|
google::LogMessage log_obj(__FILE__, __LINE__);
|
||||||
|
|
||||||
std::chrono::seconds gmtoff = log_obj.time().gmtoffset();
|
std::chrono::seconds nGmtOff = log_obj.time().gmtoffset();
|
||||||
// GMT offset ranges from UTC-12:00 to UTC+14:00
|
// GMT offset ranges from UTC-12:00 to UTC+14:00
|
||||||
using namespace std::chrono_literals;
|
using namespace std::chrono_literals;
|
||||||
constexpr std::chrono::hours utc_min_offset = -12h;
|
const std::chrono::hours utc_min_offset = -12h;
|
||||||
constexpr std::chrono::hours utc_max_offset = +14h;
|
const std::chrono::hours utc_max_offset = 14h;
|
||||||
EXPECT_TRUE((gmtoff >= utc_min_offset) && (gmtoff <= utc_max_offset));
|
EXPECT_TRUE((nGmtOff >= utc_min_offset) && (nGmtOff <= utc_max_offset));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(EmailLogging, ValidAddress) {
|
TEST(EmailLogging, ValidAddress) {
|
||||||
@ -1615,17 +1575,3 @@ TEST(EmailLogging, MaliciousAddress) {
|
|||||||
EXPECT_FALSE(
|
EXPECT_FALSE(
|
||||||
SendEmail("!/bin/true@example.com", "Example subject", "Example body"));
|
SendEmail("!/bin/true@example.com", "Example subject", "Example body"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Logging, FatalThrow) {
|
|
||||||
auto const fail_func =
|
|
||||||
InstallFailureFunction(+[]()
|
|
||||||
#if defined(__has_attribute)
|
|
||||||
# if __has_attribute(noreturn)
|
|
||||||
__attribute__((noreturn))
|
|
||||||
# endif // __has_attribute(noreturn)
|
|
||||||
#endif // defined(__has_attribute)
|
|
||||||
{ throw std::logic_error{"fail"}; });
|
|
||||||
auto restore_fail = [fail_func] { InstallFailureFunction(fail_func); };
|
|
||||||
ScopedExit<decltype(restore_fail)> restore{restore_fail};
|
|
||||||
EXPECT_THROW({ LOG(FATAL) << "must throw to fail"; }, std::logic_error);
|
|
||||||
}
|
|
||||||
|
|||||||
@ -31,20 +31,32 @@
|
|||||||
//
|
//
|
||||||
// logging_unittest.cc covers the functionality herein
|
// logging_unittest.cc covers the functionality herein
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
#include <cstdarg>
|
#include <cstdarg>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstring>
|
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <streambuf>
|
#include <streambuf>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
#include "config.h"
|
#include "utilities.h"
|
||||||
|
|
||||||
#ifdef HAVE_UNISTD_H
|
#ifdef HAVE_UNISTD_H
|
||||||
# include <unistd.h> // for close() and write()
|
# include <unistd.h> // for close() and write()
|
||||||
#endif
|
#endif
|
||||||
|
#include <fcntl.h> // for open()
|
||||||
|
|
||||||
|
#include <ctime>
|
||||||
|
|
||||||
|
#include "base/commandlineflags.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "glog/logging.h" // To pick up flag settings etc.
|
||||||
|
#include "glog/raw_logging.h"
|
||||||
|
|
||||||
|
#ifdef HAVE_STACKTRACE
|
||||||
|
# include "stacktrace.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(HAVE_SYSCALL_H)
|
#if defined(HAVE_SYSCALL_H)
|
||||||
# include <syscall.h> // for syscall()
|
# include <syscall.h> // for syscall()
|
||||||
#elif defined(HAVE_SYS_SYSCALL_H)
|
#elif defined(HAVE_SYS_SYSCALL_H)
|
||||||
@ -53,12 +65,6 @@
|
|||||||
#ifdef HAVE_UNISTD_H
|
#ifdef HAVE_UNISTD_H
|
||||||
# include <unistd.h>
|
# include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
#include <fcntl.h> // for open()
|
|
||||||
|
|
||||||
#include "glog/logging.h"
|
|
||||||
#include "glog/raw_logging.h"
|
|
||||||
#include "stacktrace.h"
|
|
||||||
#include "utilities.h"
|
|
||||||
|
|
||||||
#if (defined(HAVE_SYSCALL_H) || defined(HAVE_SYS_SYSCALL_H)) && \
|
#if (defined(HAVE_SYSCALL_H) || defined(HAVE_SYS_SYSCALL_H)) && \
|
||||||
(!(defined(GLOG_OS_MACOSX)) && !(defined(GLOG_OS_OPENBSD))) && \
|
(!(defined(GLOG_OS_MACOSX)) && !(defined(GLOG_OS_OPENBSD))) && \
|
||||||
@ -120,7 +126,7 @@ inline static bool VADoRawLog(char** buf, size_t* size, const char* format,
|
|||||||
|
|
||||||
static const int kLogBufSize = 3000;
|
static const int kLogBufSize = 3000;
|
||||||
static std::once_flag crashed;
|
static std::once_flag crashed;
|
||||||
static logging::internal::CrashReason crash_reason;
|
static CrashReason crash_reason;
|
||||||
static char crash_buf[kLogBufSize + 1] = {0}; // Will end in '\0'
|
static char crash_buf[kLogBufSize + 1] = {0}; // Will end in '\0'
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@ -168,7 +174,7 @@ void RawLog__(LogSeverity severity, const char* file, int line,
|
|||||||
|
|
||||||
// NOTE: this format should match the specification in base/logging.h
|
// NOTE: this format should match the specification in base/logging.h
|
||||||
DoRawLog(&buf, &size, "%c00000000 00:00:00.000000 %s %s:%d] RAW: ",
|
DoRawLog(&buf, &size, "%c00000000 00:00:00.000000 %s %s:%d] RAW: ",
|
||||||
GetLogSeverityName(severity)[0], sbuf.data(),
|
LogSeverityNames[severity][0], sbuf.data(),
|
||||||
const_basename(const_cast<char*>(file)), line);
|
const_basename(const_cast<char*>(file)), line);
|
||||||
|
|
||||||
// Record the position and size of the buffer after the prefix
|
// Record the position and size of the buffer after the prefix
|
||||||
@ -188,7 +194,7 @@ void RawLog__(LogSeverity severity, const char* file, int line,
|
|||||||
// avoiding FILE buffering (to avoid invoking malloc()), and bypassing
|
// avoiding FILE buffering (to avoid invoking malloc()), and bypassing
|
||||||
// libc (to side-step any libc interception).
|
// libc (to side-step any libc interception).
|
||||||
// We write just once to avoid races with other invocations of RawLog__.
|
// We write just once to avoid races with other invocations of RawLog__.
|
||||||
safe_write(fileno(stderr), buffer, strlen(buffer));
|
safe_write(STDERR_FILENO, buffer, strlen(buffer));
|
||||||
if (severity == GLOG_FATAL) {
|
if (severity == GLOG_FATAL) {
|
||||||
std::call_once(crashed, [file, line, msg_start, msg_size] {
|
std::call_once(crashed, [file, line, msg_start, msg_size] {
|
||||||
crash_reason.filename = file;
|
crash_reason.filename = file;
|
||||||
|
|||||||
@ -33,15 +33,12 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <csignal>
|
#include <csignal>
|
||||||
#include <cstring>
|
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "glog/logging.h"
|
#include "glog/logging.h"
|
||||||
#include "glog/platform.h"
|
|
||||||
#include "stacktrace.h"
|
#include "stacktrace.h"
|
||||||
#include "symbolize.h"
|
#include "symbolize.h"
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
@ -55,10 +52,6 @@
|
|||||||
#ifdef HAVE_UNISTD_H
|
#ifdef HAVE_UNISTD_H
|
||||||
# include <unistd.h>
|
# include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
#if defined(HAVE_SYS_SYSCALL_H) && defined(HAVE_SYS_TYPES_H)
|
|
||||||
# include <sys/syscall.h>
|
|
||||||
# include <sys/types.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace google {
|
namespace google {
|
||||||
|
|
||||||
@ -162,7 +155,7 @@ class MinimalFormatter {
|
|||||||
|
|
||||||
// Writes the given data with the size to the standard error.
|
// Writes the given data with the size to the standard error.
|
||||||
void WriteToStderr(const char* data, size_t size) {
|
void WriteToStderr(const char* data, size_t size) {
|
||||||
if (write(fileno(stderr), data, size) < 0) {
|
if (write(STDERR_FILENO, data, size) < 0) {
|
||||||
// Ignore errors.
|
// Ignore errors.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -220,14 +213,8 @@ void DumpSignalInfo(int signal_number, siginfo_t* siginfo) {
|
|||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
oss << std::showbase << std::hex << std::this_thread::get_id();
|
oss << std::showbase << std::hex << std::this_thread::get_id();
|
||||||
formatter.AppendString(oss.str().c_str());
|
formatter.AppendString(oss.str().c_str());
|
||||||
# if defined(GLOG_OS_LINUX) && defined(HAVE_SYS_SYSCALL_H) && \
|
|
||||||
defined(HAVE_SYS_TYPES_H)
|
|
||||||
pid_t tid = syscall(SYS_gettid);
|
|
||||||
formatter.AppendString(" LWP ");
|
|
||||||
formatter.AppendUint64(static_cast<uint64>(tid), 10);
|
|
||||||
# endif
|
|
||||||
formatter.AppendString(") ");
|
|
||||||
|
|
||||||
|
formatter.AppendString(") ");
|
||||||
// Only linux has the PID of the signal sender in si_pid.
|
// Only linux has the PID of the signal sender in si_pid.
|
||||||
# ifdef GLOG_OS_LINUX
|
# ifdef GLOG_OS_LINUX
|
||||||
formatter.AppendString("from PID ");
|
formatter.AppendString("from PID ");
|
||||||
@ -244,7 +231,6 @@ void DumpSignalInfo(int signal_number, siginfo_t* siginfo) {
|
|||||||
void DumpStackFrameInfo(const char* prefix, void* pc) {
|
void DumpStackFrameInfo(const char* prefix, void* pc) {
|
||||||
// Get the symbol name.
|
// Get the symbol name.
|
||||||
const char* symbol = "(unknown)";
|
const char* symbol = "(unknown)";
|
||||||
#if defined(HAVE_SYMBOLIZE)
|
|
||||||
char symbolized[1024]; // Big enough for a sane symbol.
|
char symbolized[1024]; // Big enough for a sane symbol.
|
||||||
// Symbolizes the previous address of pc because pc may be in the
|
// Symbolizes the previous address of pc because pc may be in the
|
||||||
// next function.
|
// next function.
|
||||||
@ -252,10 +238,6 @@ void DumpStackFrameInfo(const char* prefix, void* pc) {
|
|||||||
sizeof(symbolized))) {
|
sizeof(symbolized))) {
|
||||||
symbol = symbolized;
|
symbol = symbolized;
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
# pragma message( \
|
|
||||||
"Symbolize functionality is not available for target platform: stack dump will contain empty frames.")
|
|
||||||
#endif // defined(HAVE_SYMBOLIZE)
|
|
||||||
|
|
||||||
char buf[1024]; // Big enough for stack frame info.
|
char buf[1024]; // Big enough for stack frame info.
|
||||||
MinimalFormatter formatter(buf, sizeof(buf));
|
MinimalFormatter formatter(buf, sizeof(buf));
|
||||||
|
|||||||
@ -39,21 +39,13 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "glog/logging.h"
|
#include "glog/logging.h"
|
||||||
#include "stacktrace.h"
|
#include "utilities.h"
|
||||||
#include "symbolize.h"
|
|
||||||
|
|
||||||
#if defined(HAVE_UNISTD_H)
|
|
||||||
# include <unistd.h>
|
|
||||||
#endif
|
|
||||||
#ifdef GLOG_USE_GFLAGS
|
#ifdef GLOG_USE_GFLAGS
|
||||||
# include <gflags/gflags.h>
|
# include <gflags/gflags.h>
|
||||||
using namespace GFLAGS_NAMESPACE;
|
using namespace GFLAGS_NAMESPACE;
|
||||||
#endif
|
#endif
|
||||||
#if defined(_MSC_VER)
|
|
||||||
# include <io.h> // write
|
|
||||||
#endif
|
|
||||||
|
|
||||||
using namespace google;
|
using namespace google;
|
||||||
|
|
||||||
@ -67,7 +59,7 @@ static void DieInThread(int* a) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void WriteToStdout(const char* data, size_t size) {
|
static void WriteToStdout(const char* data, size_t size) {
|
||||||
if (write(fileno(stdout), data, size) < 0) {
|
if (write(STDOUT_FILENO, data, size) < 0) {
|
||||||
// Ignore errors.
|
// Ignore errors.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,38 +0,0 @@
|
|||||||
// Copyright (c) 2024, Google Inc.
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
//
|
|
||||||
// Routines to extract the current stack trace. These functions are
|
|
||||||
// thread-safe.
|
|
||||||
|
|
||||||
#include "stacktrace.h"
|
|
||||||
|
|
||||||
// Make an implementation of stacktrace compiled.
|
|
||||||
#if defined(STACKTRACE_H)
|
|
||||||
# include STACKTRACE_H
|
|
||||||
#endif
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
// Copyright (c) 2024, Google Inc.
|
// Copyright (c) 2000 - 2007, Google Inc.
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// Redistribution and use in source and binary forms, with or without
|
// Redistribution and use in source and binary forms, with or without
|
||||||
@ -30,46 +30,13 @@
|
|||||||
// Routines to extract the current stack trace. These functions are
|
// Routines to extract the current stack trace. These functions are
|
||||||
// thread-safe.
|
// thread-safe.
|
||||||
|
|
||||||
#ifndef GLOG_INTERNAL_STACKTRACE_H
|
#ifndef BASE_STACKTRACE_H_
|
||||||
#define GLOG_INTERNAL_STACKTRACE_H
|
#define BASE_STACKTRACE_H_
|
||||||
|
|
||||||
#include "glog/platform.h"
|
|
||||||
|
|
||||||
#if defined(GLOG_USE_GLOG_EXPORT)
|
|
||||||
# include "glog/export.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined(GLOG_NO_EXPORT)
|
|
||||||
# error "stacktrace.h" was not included correctly.
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#if defined(HAVE_LIBUNWIND)
|
#include "glog/logging.h"
|
||||||
# define STACKTRACE_H "stacktrace_libunwind-inl.h"
|
|
||||||
#elif defined(HAVE_UNWIND)
|
|
||||||
# define STACKTRACE_H "stacktrace_unwind-inl.h"
|
|
||||||
#elif !defined(NO_FRAME_POINTER)
|
|
||||||
# if defined(__i386__) && __GNUC__ >= 2
|
|
||||||
# define STACKTRACE_H "stacktrace_x86-inl.h"
|
|
||||||
# elif (defined(__ppc__) || defined(__PPC__)) && __GNUC__ >= 2
|
|
||||||
# define STACKTRACE_H "stacktrace_powerpc-inl.h"
|
|
||||||
# elif defined(GLOG_OS_WINDOWS)
|
|
||||||
# define STACKTRACE_H "stacktrace_windows-inl.h"
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined(STACKTRACE_H) && defined(HAVE_EXECINFO_BACKTRACE)
|
|
||||||
# define STACKTRACE_H "stacktrace_generic-inl.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(STACKTRACE_H)
|
|
||||||
# define HAVE_STACKTRACE
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace google {
|
namespace google {
|
||||||
inline namespace glog_internal_namespace_ {
|
|
||||||
|
|
||||||
#if defined(HAVE_STACKTRACE)
|
|
||||||
|
|
||||||
// This is similar to the GetStackFrames routine, except that it returns
|
// This is similar to the GetStackFrames routine, except that it returns
|
||||||
// the stack trace only, and not the stack frame sizes as well.
|
// the stack trace only, and not the stack frame sizes as well.
|
||||||
@ -87,11 +54,8 @@ inline namespace glog_internal_namespace_ {
|
|||||||
// .... ...
|
// .... ...
|
||||||
//
|
//
|
||||||
// "result" must not be nullptr.
|
// "result" must not be nullptr.
|
||||||
GLOG_NO_EXPORT int GetStackTrace(void** result, int max_depth, int skip_count);
|
GLOG_EXPORT int GetStackTrace(void** result, int max_depth, int skip_count);
|
||||||
|
|
||||||
#endif // defined(HAVE_STACKTRACE)
|
|
||||||
|
|
||||||
} // namespace glog_internal_namespace_
|
|
||||||
} // namespace google
|
} // namespace google
|
||||||
|
|
||||||
#endif // GLOG_INTERNAL_STACKTRACE_H
|
#endif // BASE_STACKTRACE_H_
|
||||||
|
|||||||
@ -38,7 +38,6 @@
|
|||||||
#include "stacktrace.h"
|
#include "stacktrace.h"
|
||||||
|
|
||||||
namespace google {
|
namespace google {
|
||||||
inline namespace glog_internal_namespace_ {
|
|
||||||
|
|
||||||
// If you change this function, also change GetStackFrames below.
|
// If you change this function, also change GetStackFrames below.
|
||||||
int GetStackTrace(void** result, int max_depth, int skip_count) {
|
int GetStackTrace(void** result, int max_depth, int skip_count) {
|
||||||
@ -62,5 +61,4 @@ int GetStackTrace(void** result, int max_depth, int skip_count) {
|
|||||||
return result_count;
|
return result_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace glog_internal_namespace_
|
|
||||||
} // namespace google
|
} // namespace google
|
||||||
|
|||||||
@ -41,7 +41,6 @@ extern "C" {
|
|||||||
#include "stacktrace.h"
|
#include "stacktrace.h"
|
||||||
|
|
||||||
namespace google {
|
namespace google {
|
||||||
inline namespace glog_internal_namespace_ {
|
|
||||||
|
|
||||||
// Sometimes, we can try to get a stack trace from within a stack
|
// Sometimes, we can try to get a stack trace from within a stack
|
||||||
// trace, because libunwind can call mmap (maybe indirectly via an
|
// trace, because libunwind can call mmap (maybe indirectly via an
|
||||||
@ -91,5 +90,4 @@ int GetStackTrace(void** result, int max_depth, int skip_count) {
|
|||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace glog_internal_namespace_
|
|
||||||
} // namespace google
|
} // namespace google
|
||||||
|
|||||||
@ -41,7 +41,6 @@
|
|||||||
#include "stacktrace.h"
|
#include "stacktrace.h"
|
||||||
|
|
||||||
namespace google {
|
namespace google {
|
||||||
inline namespace glog_internal_namespace_ {
|
|
||||||
|
|
||||||
// Given a pointer to a stack frame, locate and return the calling
|
// Given a pointer to a stack frame, locate and return the calling
|
||||||
// stackframe, or return nullptr if no stackframe can be found. Perform sanity
|
// stackframe, or return nullptr if no stackframe can be found. Perform sanity
|
||||||
@ -132,5 +131,4 @@ int GetStackTrace(void** result, int max_depth, int skip_count) {
|
|||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace glog_internal_namespace_
|
|
||||||
} // namespace google
|
} // namespace google
|
||||||
|
|||||||
@ -41,6 +41,8 @@
|
|||||||
# include <execinfo.h>
|
# include <execinfo.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
using namespace google;
|
||||||
|
|
||||||
#ifdef HAVE_STACKTRACE
|
#ifdef HAVE_STACKTRACE
|
||||||
|
|
||||||
// Obtain a backtrace, verify that the expected callers are present in the
|
// Obtain a backtrace, verify that the expected callers are present in the
|
||||||
@ -130,7 +132,7 @@ static void ATTRIBUTE_NOINLINE CheckStackTraceLeaf() {
|
|||||||
ADJUST_ADDRESS_RANGE_FROM_RA(&expected_range[1]);
|
ADJUST_ADDRESS_RANGE_FROM_RA(&expected_range[1]);
|
||||||
INIT_ADDRESS_RANGE(CheckStackTraceLeaf, start, end, &expected_range[0]);
|
INIT_ADDRESS_RANGE(CheckStackTraceLeaf, start, end, &expected_range[0]);
|
||||||
DECLARE_ADDRESS_LABEL(start);
|
DECLARE_ADDRESS_LABEL(start);
|
||||||
size = google::GetStackTrace(stack, STACK_LEN, 0);
|
size = GetStackTrace(stack, STACK_LEN, 0);
|
||||||
printf("Obtained %d stack frames.\n", size);
|
printf("Obtained %d stack frames.\n", size);
|
||||||
CHECK_GE(size, 1);
|
CHECK_GE(size, 1);
|
||||||
CHECK_LE(size, STACK_LEN);
|
CHECK_LE(size, STACK_LEN);
|
||||||
@ -229,7 +231,7 @@ static
|
|||||||
|
|
||||||
int main(int, char** argv) {
|
int main(int, char** argv) {
|
||||||
FLAGS_logtostderr = true;
|
FLAGS_logtostderr = true;
|
||||||
google::InitGoogleLogging(argv[0]);
|
InitGoogleLogging(argv[0]);
|
||||||
|
|
||||||
CheckStackTrace(0);
|
CheckStackTrace(0);
|
||||||
|
|
||||||
|
|||||||
@ -36,7 +36,6 @@
|
|||||||
#include "stacktrace.h"
|
#include "stacktrace.h"
|
||||||
|
|
||||||
namespace google {
|
namespace google {
|
||||||
inline namespace glog_internal_namespace_ {
|
|
||||||
|
|
||||||
struct trace_arg_t {
|
struct trace_arg_t {
|
||||||
void** result;
|
void** result;
|
||||||
@ -102,5 +101,4 @@ int GetStackTrace(void** result, int max_depth, int skip_count) {
|
|||||||
return targ.count;
|
return targ.count;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace glog_internal_namespace_
|
|
||||||
} // namespace google
|
} // namespace google
|
||||||
|
|||||||
@ -31,13 +31,15 @@
|
|||||||
//
|
//
|
||||||
// Windows implementation - just use CaptureStackBackTrace
|
// Windows implementation - just use CaptureStackBackTrace
|
||||||
|
|
||||||
// clang-format off
|
|
||||||
#include <windows.h> // Must come before <dbghelp.h>
|
|
||||||
#include <dbghelp.h>
|
#include <dbghelp.h>
|
||||||
// clang-format on
|
|
||||||
|
#include "config.h"
|
||||||
|
#if defined(GLOG_USE_WINDOWS_PORT)
|
||||||
|
# include "port.h"
|
||||||
|
#endif
|
||||||
|
#include "stacktrace.h"
|
||||||
|
|
||||||
namespace google {
|
namespace google {
|
||||||
inline namespace glog_internal_namespace_ {
|
|
||||||
|
|
||||||
int GetStackTrace(void** result, int max_depth, int skip_count) {
|
int GetStackTrace(void** result, int max_depth, int skip_count) {
|
||||||
if (max_depth > 64) {
|
if (max_depth > 64) {
|
||||||
@ -49,5 +51,4 @@ int GetStackTrace(void** result, int max_depth, int skip_count) {
|
|||||||
static_cast<DWORD>(max_depth), result, nullptr);
|
static_cast<DWORD>(max_depth), result, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace glog_internal_namespace_
|
|
||||||
} // namespace google
|
} // namespace google
|
||||||
|
|||||||
@ -43,7 +43,6 @@
|
|||||||
#include "stacktrace.h"
|
#include "stacktrace.h"
|
||||||
|
|
||||||
namespace google {
|
namespace google {
|
||||||
inline namespace glog_internal_namespace_ {
|
|
||||||
|
|
||||||
// Given a pointer to a stack frame, locate and return the calling
|
// Given a pointer to a stack frame, locate and return the calling
|
||||||
// stackframe, or return nullptr if no stackframe can be found. Perform sanity
|
// stackframe, or return nullptr if no stackframe can be found. Perform sanity
|
||||||
@ -156,5 +155,5 @@ int GetStackTrace(void** result, int max_depth, int skip_count) {
|
|||||||
}
|
}
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
} // namespace glog_internal_namespace_
|
|
||||||
} // namespace google
|
} // namespace google
|
||||||
|
|||||||
317
src/symbolize.cc
317
src/symbolize.cc
@ -1,4 +1,4 @@
|
|||||||
// Copyright (c) 2024, Google Inc.
|
// Copyright (c) 2023, Google Inc.
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// Redistribution and use in source and binary forms, with or without
|
// Redistribution and use in source and binary forms, with or without
|
||||||
@ -52,68 +52,71 @@
|
|||||||
# include GLOG_BUILD_CONFIG_INCLUDE
|
# include GLOG_BUILD_CONFIG_INCLUDE
|
||||||
#endif // GLOG_BUILD_CONFIG_INCLUDE
|
#endif // GLOG_BUILD_CONFIG_INCLUDE
|
||||||
|
|
||||||
#include "symbolize.h"
|
|
||||||
|
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
|
|
||||||
#if defined(HAVE_SYMBOLIZE)
|
#if defined(HAVE_SYMBOLIZE)
|
||||||
|
|
||||||
# include <algorithm>
|
# include <algorithm>
|
||||||
# include <cstdlib>
|
|
||||||
# include <cstring>
|
# include <cstring>
|
||||||
# include <limits>
|
# include <limits>
|
||||||
|
|
||||||
# include "demangle.h"
|
# include "demangle.h"
|
||||||
|
# include "symbolize.h"
|
||||||
|
|
||||||
|
namespace google {
|
||||||
|
|
||||||
// We don't use assert() since it's not guaranteed to be
|
// We don't use assert() since it's not guaranteed to be
|
||||||
// async-signal-safe. Instead we define a minimal assertion
|
// async-signal-safe. Instead we define a minimal assertion
|
||||||
// macro. So far, we don't need pretty printing for __FILE__, etc.
|
// macro. So far, we don't need pretty printing for __FILE__, etc.
|
||||||
# define GLOG_SAFE_ASSERT(expr) ((expr) ? 0 : (std::abort(), 0))
|
|
||||||
|
|
||||||
namespace google {
|
// A wrapper for abort() to make it callable in ? :.
|
||||||
inline namespace glog_internal_namespace_ {
|
static int AssertFail() {
|
||||||
|
abort();
|
||||||
namespace {
|
return 0; // Should not reach.
|
||||||
|
|
||||||
SymbolizeCallback g_symbolize_callback = nullptr;
|
|
||||||
SymbolizeOpenObjectFileCallback g_symbolize_open_object_file_callback = nullptr;
|
|
||||||
|
|
||||||
// This function wraps the Demangle function to provide an interface
|
|
||||||
// where the input symbol is demangled in-place.
|
|
||||||
// To keep stack consumption low, we would like this function to not
|
|
||||||
// get inlined.
|
|
||||||
ATTRIBUTE_NOINLINE
|
|
||||||
void DemangleInplace(char* out, size_t out_size) {
|
|
||||||
char demangled[256]; // Big enough for sane demangled symbols.
|
|
||||||
if (Demangle(out, demangled, sizeof(demangled))) {
|
|
||||||
// Demangling succeeded. Copy to out if the space allows.
|
|
||||||
size_t len = strlen(demangled);
|
|
||||||
if (len + 1 <= out_size) { // +1 for '\0'.
|
|
||||||
GLOG_SAFE_ASSERT(len < sizeof(demangled));
|
|
||||||
memmove(out, demangled, len + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
# define SAFE_ASSERT(expr) ((expr) ? 0 : AssertFail())
|
||||||
|
|
||||||
|
static SymbolizeCallback g_symbolize_callback = nullptr;
|
||||||
void InstallSymbolizeCallback(SymbolizeCallback callback) {
|
void InstallSymbolizeCallback(SymbolizeCallback callback) {
|
||||||
g_symbolize_callback = callback;
|
g_symbolize_callback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static SymbolizeOpenObjectFileCallback g_symbolize_open_object_file_callback =
|
||||||
|
nullptr;
|
||||||
void InstallSymbolizeOpenObjectFileCallback(
|
void InstallSymbolizeOpenObjectFileCallback(
|
||||||
SymbolizeOpenObjectFileCallback callback) {
|
SymbolizeOpenObjectFileCallback callback) {
|
||||||
g_symbolize_open_object_file_callback = callback;
|
g_symbolize_open_object_file_callback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace glog_internal_namespace_
|
// This function wraps the Demangle function to provide an interface
|
||||||
|
// where the input symbol is demangled in-place.
|
||||||
|
// To keep stack consumption low, we would like this function to not
|
||||||
|
// get inlined.
|
||||||
|
static ATTRIBUTE_NOINLINE void DemangleInplace(char* out, size_t out_size) {
|
||||||
|
char demangled[256]; // Big enough for sane demangled symbols.
|
||||||
|
if (Demangle(out, demangled, sizeof(demangled))) {
|
||||||
|
// Demangling succeeded. Copy to out if the space allows.
|
||||||
|
size_t len = strlen(demangled);
|
||||||
|
if (len + 1 <= out_size) { // +1 for '\0'.
|
||||||
|
SAFE_ASSERT(len < sizeof(demangled));
|
||||||
|
memmove(out, demangled, len + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace google
|
} // namespace google
|
||||||
|
|
||||||
# if defined(HAVE_LINK_H)
|
# if defined(__ELF__)
|
||||||
|
|
||||||
# if defined(HAVE_DLFCN_H)
|
# if defined(HAVE_DLFCN_H)
|
||||||
# include <dlfcn.h>
|
# include <dlfcn.h>
|
||||||
# endif
|
# endif
|
||||||
|
# if defined(GLOG_OS_OPENBSD)
|
||||||
|
# include <sys/exec_elf.h>
|
||||||
|
# else
|
||||||
|
# include <elf.h>
|
||||||
|
# endif
|
||||||
# include <fcntl.h>
|
# include <fcntl.h>
|
||||||
# include <sys/stat.h>
|
# include <sys/stat.h>
|
||||||
# include <sys/types.h>
|
# include <sys/types.h>
|
||||||
@ -131,24 +134,12 @@ void InstallSymbolizeOpenObjectFileCallback(
|
|||||||
# include "glog/raw_logging.h"
|
# include "glog/raw_logging.h"
|
||||||
# include "symbolize.h"
|
# include "symbolize.h"
|
||||||
|
|
||||||
|
// Re-runs fn until it doesn't cause EINTR.
|
||||||
|
# define NO_INTR(fn) \
|
||||||
|
do { \
|
||||||
|
} while ((fn) < 0 && errno == EINTR)
|
||||||
|
|
||||||
namespace google {
|
namespace google {
|
||||||
inline namespace glog_internal_namespace_ {
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
// Re-runs run until it doesn't cause EINTR.
|
|
||||||
// Similar to the TEMP_FAILURE_RETRY macro from GNU C.
|
|
||||||
template <class Functor>
|
|
||||||
auto FailureRetry(Functor run, int error = EINTR) noexcept(noexcept(run())) {
|
|
||||||
decltype(run()) result;
|
|
||||||
|
|
||||||
while ((result = run()) == -1 && errno == error) {
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
// Read up to "count" bytes from "offset" in the file pointed by file
|
// Read up to "count" bytes from "offset" in the file pointed by file
|
||||||
// descriptor "fd" into the buffer starting at "buf" while handling short reads
|
// descriptor "fd" into the buffer starting at "buf" while handling short reads
|
||||||
@ -156,16 +147,15 @@ auto FailureRetry(Functor run, int error = EINTR) noexcept(noexcept(run())) {
|
|||||||
// -1.
|
// -1.
|
||||||
static ssize_t ReadFromOffset(const int fd, void* buf, const size_t count,
|
static ssize_t ReadFromOffset(const int fd, void* buf, const size_t count,
|
||||||
const size_t offset) {
|
const size_t offset) {
|
||||||
GLOG_SAFE_ASSERT(fd >= 0);
|
SAFE_ASSERT(fd >= 0);
|
||||||
GLOG_SAFE_ASSERT(count <=
|
SAFE_ASSERT(count <=
|
||||||
static_cast<size_t>(std::numeric_limits<ssize_t>::max()));
|
static_cast<size_t>(std::numeric_limits<ssize_t>::max()));
|
||||||
char* buf0 = reinterpret_cast<char*>(buf);
|
char* buf0 = reinterpret_cast<char*>(buf);
|
||||||
size_t num_bytes = 0;
|
size_t num_bytes = 0;
|
||||||
while (num_bytes < count) {
|
while (num_bytes < count) {
|
||||||
ssize_t len = FailureRetry([fd, p = buf0 + num_bytes, n = count - num_bytes,
|
ssize_t len;
|
||||||
m = static_cast<off_t>(offset + num_bytes)] {
|
NO_INTR(len = pread(fd, buf0 + num_bytes, count - num_bytes,
|
||||||
return pread(fd, p, n, m);
|
static_cast<off_t>(offset + num_bytes)));
|
||||||
});
|
|
||||||
if (len < 0) { // There was an error other than EINTR.
|
if (len < 0) { // There was an error other than EINTR.
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -174,7 +164,7 @@ static ssize_t ReadFromOffset(const int fd, void* buf, const size_t count,
|
|||||||
}
|
}
|
||||||
num_bytes += static_cast<size_t>(len);
|
num_bytes += static_cast<size_t>(len);
|
||||||
}
|
}
|
||||||
GLOG_SAFE_ASSERT(num_bytes <= count);
|
SAFE_ASSERT(num_bytes <= count);
|
||||||
return static_cast<ssize_t>(num_bytes);
|
return static_cast<ssize_t>(num_bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -221,9 +211,9 @@ static ATTRIBUTE_NOINLINE bool GetSectionHeaderByType(const int fd,
|
|||||||
if (len == -1) {
|
if (len == -1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
GLOG_SAFE_ASSERT(static_cast<size_t>(len) % sizeof(buf[0]) == 0);
|
SAFE_ASSERT(static_cast<size_t>(len) % sizeof(buf[0]) == 0);
|
||||||
const size_t num_headers_in_buf = static_cast<size_t>(len) / sizeof(buf[0]);
|
const size_t num_headers_in_buf = static_cast<size_t>(len) / sizeof(buf[0]);
|
||||||
GLOG_SAFE_ASSERT(num_headers_in_buf <= sizeof(buf) / sizeof(buf[0]));
|
SAFE_ASSERT(num_headers_in_buf <= sizeof(buf) / sizeof(buf[0]));
|
||||||
for (size_t j = 0; j < num_headers_in_buf; ++j) {
|
for (size_t j = 0; j < num_headers_in_buf; ++j) {
|
||||||
if (buf[j].sh_type == type) {
|
if (buf[j].sh_type == type) {
|
||||||
*out = buf[j];
|
*out = buf[j];
|
||||||
@ -317,9 +307,9 @@ static ATTRIBUTE_NOINLINE bool FindSymbol(uint64_t pc, const int fd, char* out,
|
|||||||
size_t num_symbols_to_read = std::min(NUM_SYMBOLS, num_symbols - i);
|
size_t num_symbols_to_read = std::min(NUM_SYMBOLS, num_symbols - i);
|
||||||
const ssize_t len =
|
const ssize_t len =
|
||||||
ReadFromOffset(fd, &buf, sizeof(buf[0]) * num_symbols_to_read, offset);
|
ReadFromOffset(fd, &buf, sizeof(buf[0]) * num_symbols_to_read, offset);
|
||||||
GLOG_SAFE_ASSERT(static_cast<size_t>(len) % sizeof(buf[0]) == 0);
|
SAFE_ASSERT(static_cast<size_t>(len) % sizeof(buf[0]) == 0);
|
||||||
const size_t num_symbols_in_buf = static_cast<size_t>(len) / sizeof(buf[0]);
|
const size_t num_symbols_in_buf = static_cast<size_t>(len) / sizeof(buf[0]);
|
||||||
GLOG_SAFE_ASSERT(num_symbols_in_buf <= num_symbols_to_read);
|
SAFE_ASSERT(num_symbols_in_buf <= num_symbols_to_read);
|
||||||
for (unsigned j = 0; j < num_symbols_in_buf; ++j) {
|
for (unsigned j = 0; j < num_symbols_in_buf; ++j) {
|
||||||
const ElfW(Sym)& symbol = buf[j];
|
const ElfW(Sym)& symbol = buf[j];
|
||||||
uint64_t start_address = symbol.st_value;
|
uint64_t start_address = symbol.st_value;
|
||||||
@ -386,6 +376,22 @@ static bool GetSymbolFromObjectFile(const int fd, uint64_t pc, char* out,
|
|||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
// Thin wrapper around a file descriptor so that the file descriptor
|
||||||
|
// gets closed for sure.
|
||||||
|
struct FileDescriptor {
|
||||||
|
const int fd_;
|
||||||
|
explicit FileDescriptor(int fd) : fd_(fd) {}
|
||||||
|
~FileDescriptor() {
|
||||||
|
if (fd_ >= 0) {
|
||||||
|
close(fd_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int get() { return fd_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
FileDescriptor(const FileDescriptor&) = delete;
|
||||||
|
void operator=(const FileDescriptor&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
// Helper class for reading lines from file.
|
// Helper class for reading lines from file.
|
||||||
//
|
//
|
||||||
@ -418,8 +424,8 @@ class LineReader {
|
|||||||
eod_ = buf_ + num_bytes;
|
eod_ = buf_ + num_bytes;
|
||||||
bol_ = buf_;
|
bol_ = buf_;
|
||||||
} else {
|
} else {
|
||||||
bol_ = eol_ + 1; // Advance to the next line in the buffer.
|
bol_ = eol_ + 1; // Advance to the next line in the buffer.
|
||||||
GLOG_SAFE_ASSERT(bol_ <= eod_); // "bol_" can point to "eod_".
|
SAFE_ASSERT(bol_ <= eod_); // "bol_" can point to "eod_".
|
||||||
if (!HasCompleteLine()) {
|
if (!HasCompleteLine()) {
|
||||||
const auto incomplete_line_length = static_cast<size_t>(eod_ - bol_);
|
const auto incomplete_line_length = static_cast<size_t>(eod_ - bol_);
|
||||||
// Move the trailing incomplete line to the beginning.
|
// Move the trailing incomplete line to the beginning.
|
||||||
@ -494,7 +500,7 @@ static char* GetHex(const char* start, const char* end, uint64_t* hex) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GLOG_SAFE_ASSERT(p <= end);
|
SAFE_ASSERT(p <= end);
|
||||||
return const_cast<char*>(p);
|
return const_cast<char*>(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -506,33 +512,34 @@ static char* GetHex(const char* start, const char* end, uint64_t* hex) {
|
|||||||
// file is opened successfully, returns the file descriptor. Otherwise,
|
// file is opened successfully, returns the file descriptor. Otherwise,
|
||||||
// returns -1. |out_file_name_size| is the size of the file name buffer
|
// returns -1. |out_file_name_size| is the size of the file name buffer
|
||||||
// (including the null-terminator).
|
// (including the null-terminator).
|
||||||
static ATTRIBUTE_NOINLINE FileDescriptor
|
static ATTRIBUTE_NOINLINE int OpenObjectFileContainingPcAndGetStartAddress(
|
||||||
OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc,
|
uint64_t pc, uint64_t& start_address, uint64_t& base_address,
|
||||||
uint64_t& start_address,
|
char* out_file_name, size_t out_file_name_size) {
|
||||||
uint64_t& base_address,
|
int object_fd;
|
||||||
char* out_file_name,
|
|
||||||
size_t out_file_name_size) {
|
int maps_fd;
|
||||||
FileDescriptor maps_fd{
|
NO_INTR(maps_fd = open("/proc/self/maps", O_RDONLY));
|
||||||
FailureRetry([] { return open("/proc/self/maps", O_RDONLY); })};
|
FileDescriptor wrapped_maps_fd(maps_fd);
|
||||||
if (!maps_fd) {
|
if (wrapped_maps_fd.get() < 0) {
|
||||||
return nullptr;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
FileDescriptor mem_fd{
|
int mem_fd;
|
||||||
FailureRetry([] { return open("/proc/self/mem", O_RDONLY); })};
|
NO_INTR(mem_fd = open("/proc/self/mem", O_RDONLY));
|
||||||
if (!mem_fd) {
|
FileDescriptor wrapped_mem_fd(mem_fd);
|
||||||
return nullptr;
|
if (wrapped_mem_fd.get() < 0) {
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iterate over maps and look for the map containing the pc. Then
|
// Iterate over maps and look for the map containing the pc. Then
|
||||||
// look into the symbol tables inside.
|
// look into the symbol tables inside.
|
||||||
char buf[1024]; // Big enough for line of sane /proc/self/maps
|
char buf[1024]; // Big enough for line of sane /proc/self/maps
|
||||||
LineReader reader(maps_fd.get(), buf, sizeof(buf), 0);
|
LineReader reader(wrapped_maps_fd.get(), buf, sizeof(buf), 0);
|
||||||
while (true) {
|
while (true) {
|
||||||
const char* cursor;
|
const char* cursor;
|
||||||
const char* eol;
|
const char* eol;
|
||||||
if (!reader.ReadLine(&cursor, &eol)) { // EOF or malformed line.
|
if (!reader.ReadLine(&cursor, &eol)) { // EOF or malformed line.
|
||||||
return nullptr;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start parsing line in /proc/self/maps. Here is an example:
|
// Start parsing line in /proc/self/maps. Here is an example:
|
||||||
@ -545,7 +552,7 @@ OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc,
|
|||||||
// Read start address.
|
// Read start address.
|
||||||
cursor = GetHex(cursor, eol, &start_address);
|
cursor = GetHex(cursor, eol, &start_address);
|
||||||
if (cursor == eol || *cursor != '-') {
|
if (cursor == eol || *cursor != '-') {
|
||||||
return nullptr; // Malformed line.
|
return -1; // Malformed line.
|
||||||
}
|
}
|
||||||
++cursor; // Skip '-'.
|
++cursor; // Skip '-'.
|
||||||
|
|
||||||
@ -553,7 +560,7 @@ OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc,
|
|||||||
uint64_t end_address;
|
uint64_t end_address;
|
||||||
cursor = GetHex(cursor, eol, &end_address);
|
cursor = GetHex(cursor, eol, &end_address);
|
||||||
if (cursor == eol || *cursor != ' ') {
|
if (cursor == eol || *cursor != ' ') {
|
||||||
return nullptr; // Malformed line.
|
return -1; // Malformed line.
|
||||||
}
|
}
|
||||||
++cursor; // Skip ' '.
|
++cursor; // Skip ' '.
|
||||||
|
|
||||||
@ -564,15 +571,14 @@ OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc,
|
|||||||
}
|
}
|
||||||
// We expect at least four letters for flags (ex. "r-xp").
|
// We expect at least four letters for flags (ex. "r-xp").
|
||||||
if (cursor == eol || cursor < flags_start + 4) {
|
if (cursor == eol || cursor < flags_start + 4) {
|
||||||
return nullptr; // Malformed line.
|
return -1; // Malformed line.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine the base address by reading ELF headers in process memory.
|
// Determine the base address by reading ELF headers in process memory.
|
||||||
ElfW(Ehdr) ehdr;
|
ElfW(Ehdr) ehdr;
|
||||||
// Skip non-readable maps.
|
// Skip non-readable maps.
|
||||||
if (flags_start[0] == 'r' &&
|
if (flags_start[0] == 'r' &&
|
||||||
ReadFromOffsetExact(mem_fd.get(), &ehdr, sizeof(ElfW(Ehdr)),
|
ReadFromOffsetExact(mem_fd, &ehdr, sizeof(ElfW(Ehdr)), start_address) &&
|
||||||
start_address) &&
|
|
||||||
memcmp(ehdr.e_ident, ELFMAG, SELFMAG) == 0) {
|
memcmp(ehdr.e_ident, ELFMAG, SELFMAG) == 0) {
|
||||||
switch (ehdr.e_type) {
|
switch (ehdr.e_type) {
|
||||||
case ET_EXEC:
|
case ET_EXEC:
|
||||||
@ -591,7 +597,7 @@ OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc,
|
|||||||
for (unsigned i = 0; i != ehdr.e_phnum; ++i) {
|
for (unsigned i = 0; i != ehdr.e_phnum; ++i) {
|
||||||
ElfW(Phdr) phdr;
|
ElfW(Phdr) phdr;
|
||||||
if (ReadFromOffsetExact(
|
if (ReadFromOffsetExact(
|
||||||
mem_fd.get(), &phdr, sizeof(phdr),
|
mem_fd, &phdr, sizeof(phdr),
|
||||||
start_address + ehdr.e_phoff + i * sizeof(phdr)) &&
|
start_address + ehdr.e_phoff + i * sizeof(phdr)) &&
|
||||||
phdr.p_type == PT_LOAD && phdr.p_offset == 0) {
|
phdr.p_type == PT_LOAD && phdr.p_offset == 0) {
|
||||||
base_address = start_address - phdr.p_vaddr;
|
base_address = start_address - phdr.p_vaddr;
|
||||||
@ -607,7 +613,7 @@ OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check start and end addresses.
|
// Check start and end addresses.
|
||||||
if (start_address > pc || pc >= end_address) {
|
if (!(start_address <= pc && pc < end_address)) {
|
||||||
continue; // We skip this map. PC isn't in this map.
|
continue; // We skip this map. PC isn't in this map.
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -621,7 +627,7 @@ OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc,
|
|||||||
uint64_t file_offset;
|
uint64_t file_offset;
|
||||||
cursor = GetHex(cursor, eol, &file_offset);
|
cursor = GetHex(cursor, eol, &file_offset);
|
||||||
if (cursor == eol || *cursor != ' ') {
|
if (cursor == eol || *cursor != ' ') {
|
||||||
return nullptr; // Malformed line.
|
return -1; // Malformed line.
|
||||||
}
|
}
|
||||||
++cursor; // Skip ' '.
|
++cursor; // Skip ' '.
|
||||||
|
|
||||||
@ -639,16 +645,20 @@ OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc,
|
|||||||
++cursor;
|
++cursor;
|
||||||
}
|
}
|
||||||
if (cursor == eol) {
|
if (cursor == eol) {
|
||||||
return nullptr; // Malformed line.
|
return -1; // Malformed line.
|
||||||
}
|
}
|
||||||
|
|
||||||
strncpy(out_file_name, cursor, out_file_name_size);
|
|
||||||
// Making sure |out_file_name| is always null-terminated.
|
|
||||||
out_file_name[out_file_name_size - 1] = '\0';
|
|
||||||
|
|
||||||
// Finally, "cursor" now points to file name of our interest.
|
// Finally, "cursor" now points to file name of our interest.
|
||||||
return FileDescriptor{
|
NO_INTR(object_fd = open(cursor, O_RDONLY));
|
||||||
FailureRetry([cursor] { return open(cursor, O_RDONLY); })};
|
if (object_fd < 0) {
|
||||||
|
// Failed to open object file. Copy the object file name to
|
||||||
|
// |out_file_name|.
|
||||||
|
strncpy(out_file_name, cursor, out_file_name_size);
|
||||||
|
// Making sure |out_file_name| is always null-terminated.
|
||||||
|
out_file_name[out_file_name_size - 1] = '\0';
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return object_fd;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -712,7 +722,7 @@ static char* itoa_r(uintptr_t i, char* buf, size_t sz, unsigned base,
|
|||||||
// buffer size |dest_size| and guarantees that |dest| is null-terminated.
|
// buffer size |dest_size| and guarantees that |dest| is null-terminated.
|
||||||
static void SafeAppendString(const char* source, char* dest, size_t dest_size) {
|
static void SafeAppendString(const char* source, char* dest, size_t dest_size) {
|
||||||
size_t dest_string_length = strlen(dest);
|
size_t dest_string_length = strlen(dest);
|
||||||
GLOG_SAFE_ASSERT(dest_string_length < dest_size);
|
SAFE_ASSERT(dest_string_length < dest_size);
|
||||||
dest += dest_string_length;
|
dest += dest_string_length;
|
||||||
dest_size -= dest_string_length;
|
dest_size -= dest_string_length;
|
||||||
strncpy(dest, source, dest_size);
|
strncpy(dest, source, dest_size);
|
||||||
@ -737,12 +747,12 @@ static void SafeAppendHexNumber(uint64_t value, char* dest, size_t dest_size) {
|
|||||||
// and "out" is used as its output.
|
// and "out" is used as its output.
|
||||||
// To keep stack consumption low, we would like this function to not
|
// To keep stack consumption low, we would like this function to not
|
||||||
// get inlined.
|
// get inlined.
|
||||||
static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(
|
static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void* pc, char* out,
|
||||||
void* pc, char* out, size_t out_size, SymbolizeOptions /*options*/) {
|
size_t out_size) {
|
||||||
auto pc0 = reinterpret_cast<uintptr_t>(pc);
|
auto pc0 = reinterpret_cast<uintptr_t>(pc);
|
||||||
uint64_t start_address = 0;
|
uint64_t start_address = 0;
|
||||||
uint64_t base_address = 0;
|
uint64_t base_address = 0;
|
||||||
FileDescriptor object_fd;
|
int object_fd = -1;
|
||||||
|
|
||||||
if (out_size < 1) {
|
if (out_size < 1) {
|
||||||
return false;
|
return false;
|
||||||
@ -751,18 +761,20 @@ static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(
|
|||||||
SafeAppendString("(", out, out_size);
|
SafeAppendString("(", out, out_size);
|
||||||
|
|
||||||
if (g_symbolize_open_object_file_callback) {
|
if (g_symbolize_open_object_file_callback) {
|
||||||
object_fd.reset(g_symbolize_open_object_file_callback(
|
object_fd = g_symbolize_open_object_file_callback(
|
||||||
pc0, start_address, base_address, out + 1, out_size - 1));
|
pc0, start_address, base_address, out + 1, out_size - 1);
|
||||||
} else {
|
} else {
|
||||||
object_fd = OpenObjectFileContainingPcAndGetStartAddress(
|
object_fd = OpenObjectFileContainingPcAndGetStartAddress(
|
||||||
pc0, start_address, base_address, out + 1, out_size - 1);
|
pc0, start_address, base_address, out + 1, out_size - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FileDescriptor wrapped_object_fd(object_fd);
|
||||||
|
|
||||||
# if defined(PRINT_UNSYMBOLIZED_STACK_TRACES)
|
# if defined(PRINT_UNSYMBOLIZED_STACK_TRACES)
|
||||||
{
|
{
|
||||||
# else
|
# else
|
||||||
// Check whether a file name was returned.
|
// Check whether a file name was returned.
|
||||||
if (!object_fd) {
|
if (object_fd < 0) {
|
||||||
# endif
|
# endif
|
||||||
if (out[1]) {
|
if (out[1]) {
|
||||||
// The object file containing PC was determined successfully however the
|
// The object file containing PC was determined successfully however the
|
||||||
@ -778,7 +790,7 @@ static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(
|
|||||||
// Failed to determine the object file containing PC. Bail out.
|
// Failed to determine the object file containing PC. Bail out.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
int elf_type = FileGetElfType(object_fd.get());
|
int elf_type = FileGetElfType(wrapped_object_fd.get());
|
||||||
if (elf_type == -1) {
|
if (elf_type == -1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -787,14 +799,14 @@ static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(
|
|||||||
// Note: relocation (and much of the rest of this code) will be
|
// Note: relocation (and much of the rest of this code) will be
|
||||||
// wrong for prelinked shared libraries and PIE executables.
|
// wrong for prelinked shared libraries and PIE executables.
|
||||||
uint64_t relocation = (elf_type == ET_DYN) ? start_address : 0;
|
uint64_t relocation = (elf_type == ET_DYN) ? start_address : 0;
|
||||||
int num_bytes_written =
|
int num_bytes_written = g_symbolize_callback(wrapped_object_fd.get(), pc,
|
||||||
g_symbolize_callback(object_fd.get(), pc, out, out_size, relocation);
|
out, out_size, relocation);
|
||||||
if (num_bytes_written > 0) {
|
if (num_bytes_written > 0) {
|
||||||
out += static_cast<size_t>(num_bytes_written);
|
out += static_cast<size_t>(num_bytes_written);
|
||||||
out_size -= static_cast<size_t>(num_bytes_written);
|
out_size -= static_cast<size_t>(num_bytes_written);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!GetSymbolFromObjectFile(object_fd.get(), pc0, out, out_size,
|
if (!GetSymbolFromObjectFile(wrapped_object_fd.get(), pc0, out, out_size,
|
||||||
base_address)) {
|
base_address)) {
|
||||||
if (out[1] && !g_symbolize_callback) {
|
if (out[1] && !g_symbolize_callback) {
|
||||||
// The object file containing PC was opened successfully however the
|
// The object file containing PC was opened successfully however the
|
||||||
@ -815,7 +827,6 @@ static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace glog_internal_namespace_
|
|
||||||
} // namespace google
|
} // namespace google
|
||||||
|
|
||||||
# elif defined(GLOG_OS_MACOSX) && defined(HAVE_DLADDR)
|
# elif defined(GLOG_OS_MACOSX) && defined(HAVE_DLADDR)
|
||||||
@ -825,10 +836,9 @@ static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(
|
|||||||
# include <cstring>
|
# include <cstring>
|
||||||
|
|
||||||
namespace google {
|
namespace google {
|
||||||
inline namespace glog_internal_namespace_ {
|
|
||||||
|
|
||||||
static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(
|
static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void* pc, char* out,
|
||||||
void* pc, char* out, size_t out_size, SymbolizeOptions /*options*/) {
|
size_t out_size) {
|
||||||
Dl_info info;
|
Dl_info info;
|
||||||
if (dladdr(pc, &info)) {
|
if (dladdr(pc, &info)) {
|
||||||
if (info.dli_sname) {
|
if (info.dli_sname) {
|
||||||
@ -843,7 +853,6 @@ static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace glog_internal_namespace_
|
|
||||||
} // namespace google
|
} // namespace google
|
||||||
|
|
||||||
# elif defined(GLOG_OS_WINDOWS) || defined(GLOG_OS_CYGWIN)
|
# elif defined(GLOG_OS_WINDOWS) || defined(GLOG_OS_CYGWIN)
|
||||||
@ -851,22 +860,24 @@ static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(
|
|||||||
# include <dbghelp.h>
|
# include <dbghelp.h>
|
||||||
# include <windows.h>
|
# include <windows.h>
|
||||||
|
|
||||||
|
# ifdef _MSC_VER
|
||||||
|
# pragma comment(lib, "dbghelp")
|
||||||
|
# endif
|
||||||
|
|
||||||
namespace google {
|
namespace google {
|
||||||
inline namespace glog_internal_namespace_ {
|
|
||||||
|
|
||||||
namespace {
|
class SymInitializer {
|
||||||
|
|
||||||
class SymInitializer final {
|
|
||||||
public:
|
public:
|
||||||
HANDLE process;
|
HANDLE process;
|
||||||
bool ready;
|
bool ready;
|
||||||
SymInitializer() : process(GetCurrentProcess()), ready(false) {
|
SymInitializer() : process(nullptr), ready(false) {
|
||||||
// Initialize the symbol handler.
|
// Initialize the symbol handler.
|
||||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms680344(v=vs.85).aspx
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms680344(v=vs.85).aspx
|
||||||
|
process = GetCurrentProcess();
|
||||||
// Defer symbol loading.
|
// Defer symbol loading.
|
||||||
// We do not request undecorated symbols with SYMOPT_UNDNAME
|
// We do not request undecorated symbols with SYMOPT_UNDNAME
|
||||||
// because the mangling library calls UnDecorateSymbolName.
|
// because the mangling library calls UnDecorateSymbolName.
|
||||||
SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES);
|
SymSetOptions(SYMOPT_DEFERRED_LOADS);
|
||||||
if (SymInitialize(process, nullptr, true)) {
|
if (SymInitialize(process, nullptr, true)) {
|
||||||
ready = true;
|
ready = true;
|
||||||
}
|
}
|
||||||
@ -876,17 +887,13 @@ class SymInitializer final {
|
|||||||
// We do not need to close `HANDLE process` because it's a "pseudo handle."
|
// We do not need to close `HANDLE process` because it's a "pseudo handle."
|
||||||
}
|
}
|
||||||
|
|
||||||
SymInitializer(const SymInitializer&) = delete;
|
private:
|
||||||
SymInitializer& operator=(const SymInitializer&) = delete;
|
SymInitializer(const SymInitializer&);
|
||||||
SymInitializer(SymInitializer&&) = delete;
|
SymInitializer& operator=(const SymInitializer&);
|
||||||
SymInitializer& operator=(SymInitializer&&) = delete;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void* pc, char* out,
|
static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void* pc, char* out,
|
||||||
size_t out_size,
|
size_t out_size) {
|
||||||
SymbolizeOptions options) {
|
|
||||||
const static SymInitializer symInitializer;
|
const static SymInitializer symInitializer;
|
||||||
if (!symInitializer.ready) {
|
if (!symInitializer.ready) {
|
||||||
return false;
|
return false;
|
||||||
@ -901,49 +908,17 @@ static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void* pc, char* out,
|
|||||||
// This could break if a symbol has Unicode in it.
|
// This could break if a symbol has Unicode in it.
|
||||||
BOOL ret = SymFromAddr(symInitializer.process, reinterpret_cast<DWORD64>(pc),
|
BOOL ret = SymFromAddr(symInitializer.process, reinterpret_cast<DWORD64>(pc),
|
||||||
0, symbol);
|
0, symbol);
|
||||||
std::size_t namelen = static_cast<size_t>(symbol->NameLen);
|
if (ret == 1 && static_cast<ssize_t>(symbol->NameLen) < out_size) {
|
||||||
if (ret && namelen < out_size) {
|
// `NameLen` does not include the null terminating character.
|
||||||
std::strncpy(out, symbol->Name, namelen);
|
strncpy(out, symbol->Name, static_cast<size_t>(symbol->NameLen) + 1);
|
||||||
out[namelen] = '\0';
|
out[static_cast<size_t>(symbol->NameLen)] = '\0';
|
||||||
|
|
||||||
DWORD displacement;
|
|
||||||
IMAGEHLP_LINE64 line{sizeof(IMAGEHLP_LINE64)};
|
|
||||||
|
|
||||||
BOOL found = FALSE;
|
|
||||||
|
|
||||||
if ((options & SymbolizeOptions::kNoLineNumbers) !=
|
|
||||||
SymbolizeOptions::kNoLineNumbers) {
|
|
||||||
found = SymGetLineFromAddr64(symInitializer.process,
|
|
||||||
reinterpret_cast<DWORD64>(pc), &displacement,
|
|
||||||
&line);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Symbolization succeeded. Now we try to demangle the symbol.
|
// Symbolization succeeded. Now we try to demangle the symbol.
|
||||||
DemangleInplace(out, out_size);
|
DemangleInplace(out, out_size);
|
||||||
out_size -= std::strlen(out);
|
|
||||||
|
|
||||||
if (found) {
|
|
||||||
std::size_t fnlen = std::strlen(line.FileName);
|
|
||||||
// Determine the number of digits (base 10) necessary to represent the
|
|
||||||
// line number
|
|
||||||
std::size_t digits = 1; // At least one digit required
|
|
||||||
for (DWORD value = line.LineNumber; (value /= 10) != 0; ++digits) {
|
|
||||||
}
|
|
||||||
constexpr std::size_t extralen = 4; // space + parens () + :
|
|
||||||
const std::size_t suffixlen = fnlen + extralen + fnlen + digits;
|
|
||||||
|
|
||||||
if (suffixlen < out_size) {
|
|
||||||
out_size -= std::snprintf(out + namelen, out_size, " (%s:%lu)",
|
|
||||||
line.FileName, line.LineNumber);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace glog_internal_namespace_
|
|
||||||
} // namespace google
|
} // namespace google
|
||||||
|
|
||||||
# else
|
# else
|
||||||
@ -951,13 +926,27 @@ static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void* pc, char* out,
|
|||||||
# endif
|
# endif
|
||||||
|
|
||||||
namespace google {
|
namespace google {
|
||||||
inline namespace glog_internal_namespace_ {
|
|
||||||
|
|
||||||
bool Symbolize(void* pc, char* out, size_t out_size, SymbolizeOptions options) {
|
bool Symbolize(void* pc, char* out, size_t out_size) {
|
||||||
return SymbolizeAndDemangle(pc, out, out_size, options);
|
return SymbolizeAndDemangle(pc, out, out_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace google
|
||||||
|
|
||||||
|
#else /* HAVE_SYMBOLIZE */
|
||||||
|
|
||||||
|
# include <cassert>
|
||||||
|
|
||||||
|
# include "config.h"
|
||||||
|
|
||||||
|
namespace google {
|
||||||
|
|
||||||
|
// TODO: Support other environments.
|
||||||
|
bool Symbolize(void* /*pc*/, char* /*out*/, size_t /*out_size*/) {
|
||||||
|
assert(0);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace glog_internal_namespace_
|
|
||||||
} // namespace google
|
} // namespace google
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
114
src/symbolize.h
114
src/symbolize.h
@ -1,4 +1,4 @@
|
|||||||
// Copyright (c) 2024, Google Inc.
|
// Copyright (c) 2006, Google Inc.
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// Redistribution and use in source and binary forms, with or without
|
// Redistribution and use in source and binary forms, with or without
|
||||||
@ -51,58 +51,35 @@
|
|||||||
// malloc() and other unsafe operations. It should be both
|
// malloc() and other unsafe operations. It should be both
|
||||||
// thread-safe and async-signal-safe.
|
// thread-safe and async-signal-safe.
|
||||||
|
|
||||||
#ifndef GLOG_INTERNAL_SYMBOLIZE_H
|
#ifndef BASE_SYMBOLIZE_H_
|
||||||
#define GLOG_INTERNAL_SYMBOLIZE_H
|
#define BASE_SYMBOLIZE_H_
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <type_traits>
|
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "glog/platform.h"
|
#include "glog/logging.h"
|
||||||
|
#include "utilities.h"
|
||||||
#if defined(HAVE_LINK_H)
|
|
||||||
# include <link.h> // For ElfW() macro.
|
|
||||||
#elif defined(HAVE_ELF_H)
|
|
||||||
# include <elf.h>
|
|
||||||
#elif defined(HAVE_SYS_EXEC_ELF_H)
|
|
||||||
# include <sys/exec_elf.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(GLOG_USE_GLOG_EXPORT)
|
|
||||||
# include "glog/export.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined(GLOG_NO_EXPORT)
|
|
||||||
# error "symbolize.h" was not included correctly.
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// We prefer to let the build system detect the availability of certain features
|
|
||||||
// such as symbolization support. HAVE_SYMBOLIZE should therefore be defined by
|
|
||||||
// the build system in general unless there is a good reason to perform the
|
|
||||||
// detection using the preprocessor.
|
|
||||||
#ifndef GLOG_NO_SYMBOLIZE_DETECTION
|
|
||||||
# ifndef HAVE_SYMBOLIZE
|
|
||||||
// defined by gcc
|
|
||||||
# if defined(HAVE_ELF_H) || defined(HAVE_SYS_EXEC_ELF_H)
|
|
||||||
# define HAVE_SYMBOLIZE
|
|
||||||
# elif defined(GLOG_OS_MACOSX) && defined(HAVE_DLADDR)
|
|
||||||
// Use dladdr to symbolize.
|
|
||||||
# define HAVE_SYMBOLIZE
|
|
||||||
# elif defined(GLOG_OS_WINDOWS)
|
|
||||||
// Use DbgHelp to symbolize
|
|
||||||
# define HAVE_SYMBOLIZE
|
|
||||||
# endif
|
|
||||||
# endif // !defined(HAVE_SYMBOLIZE)
|
|
||||||
#endif // !defined(GLOG_NO_SYMBOLIZE_DETECTION)
|
|
||||||
|
|
||||||
#ifdef HAVE_SYMBOLIZE
|
#ifdef HAVE_SYMBOLIZE
|
||||||
|
|
||||||
# if !defined(SIZEOF_VOID_P) && defined(__SIZEOF_POINTER__)
|
# if defined(__ELF__) // defined by gcc
|
||||||
# define SIZEOF_VOID_P __SIZEOF_POINTER__
|
# if defined(__OpenBSD__)
|
||||||
# endif
|
# include <sys/exec_elf.h>
|
||||||
|
# else
|
||||||
|
# include <elf.h>
|
||||||
|
# endif
|
||||||
|
|
||||||
# if defined(HAVE_ELF_H) || defined(HAVE_SYS_EXEC_ELF_H)
|
# if !defined(ANDROID)
|
||||||
|
# include <link.h> // For ElfW() macro.
|
||||||
|
# endif
|
||||||
|
|
||||||
|
// For systems where SIZEOF_VOID_P is not defined, determine it
|
||||||
|
// based on __LP64__ (defined by gcc on 64-bit systems)
|
||||||
|
# if !defined(SIZEOF_VOID_P)
|
||||||
|
# if defined(__LP64__)
|
||||||
|
# define SIZEOF_VOID_P 8
|
||||||
|
# else
|
||||||
|
# define SIZEOF_VOID_P 4
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
|
||||||
// If there is no ElfW macro, let's define it by ourself.
|
// If there is no ElfW macro, let's define it by ourself.
|
||||||
# ifndef ElfW
|
# ifndef ElfW
|
||||||
@ -116,21 +93,17 @@
|
|||||||
# endif
|
# endif
|
||||||
|
|
||||||
namespace google {
|
namespace google {
|
||||||
inline namespace glog_internal_namespace_ {
|
|
||||||
|
|
||||||
// Gets the section header for the given name, if it exists. Returns true on
|
// Gets the section header for the given name, if it exists. Returns true on
|
||||||
// success. Otherwise, returns false.
|
// success. Otherwise, returns false.
|
||||||
GLOG_NO_EXPORT
|
|
||||||
bool GetSectionHeaderByName(int fd, const char* name, size_t name_len,
|
bool GetSectionHeaderByName(int fd, const char* name, size_t name_len,
|
||||||
ElfW(Shdr) * out);
|
ElfW(Shdr) * out);
|
||||||
|
|
||||||
} // namespace glog_internal_namespace_
|
|
||||||
} // namespace google
|
} // namespace google
|
||||||
|
|
||||||
# endif
|
# endif /* __ELF__ */
|
||||||
|
|
||||||
namespace google {
|
namespace google {
|
||||||
inline namespace glog_internal_namespace_ {
|
|
||||||
|
|
||||||
// Restrictions on the callbacks that follow:
|
// Restrictions on the callbacks that follow:
|
||||||
// - The callbacks must not use heaps but only use stacks.
|
// - The callbacks must not use heaps but only use stacks.
|
||||||
@ -144,7 +117,7 @@ inline namespace glog_internal_namespace_ {
|
|||||||
// and return the size of the output written. On error, the callback
|
// and return the size of the output written. On error, the callback
|
||||||
// function should return -1.
|
// function should return -1.
|
||||||
using SymbolizeCallback = int (*)(int, void*, char*, size_t, uint64_t);
|
using SymbolizeCallback = int (*)(int, void*, char*, size_t, uint64_t);
|
||||||
GLOG_NO_EXPORT
|
GLOG_EXPORT
|
||||||
void InstallSymbolizeCallback(SymbolizeCallback callback);
|
void InstallSymbolizeCallback(SymbolizeCallback callback);
|
||||||
|
|
||||||
// Installs a callback function, which will be called instead of
|
// Installs a callback function, which will be called instead of
|
||||||
@ -159,52 +132,21 @@ void InstallSymbolizeCallback(SymbolizeCallback callback);
|
|||||||
// (including the null-terminator).
|
// (including the null-terminator).
|
||||||
using SymbolizeOpenObjectFileCallback = int (*)(uint64_t, uint64_t&, uint64_t&,
|
using SymbolizeOpenObjectFileCallback = int (*)(uint64_t, uint64_t&, uint64_t&,
|
||||||
char*, size_t);
|
char*, size_t);
|
||||||
GLOG_NO_EXPORT
|
|
||||||
void InstallSymbolizeOpenObjectFileCallback(
|
void InstallSymbolizeOpenObjectFileCallback(
|
||||||
SymbolizeOpenObjectFileCallback callback);
|
SymbolizeOpenObjectFileCallback callback);
|
||||||
|
|
||||||
} // namespace glog_internal_namespace_
|
|
||||||
} // namespace google
|
} // namespace google
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace google {
|
namespace google {
|
||||||
inline namespace glog_internal_namespace_ {
|
|
||||||
|
|
||||||
#if defined(HAVE_SYMBOLIZE)
|
|
||||||
|
|
||||||
enum class SymbolizeOptions {
|
|
||||||
// No additional options.
|
|
||||||
kNone = 0,
|
|
||||||
// Do not display source and line numbers in the symbolized output.
|
|
||||||
kNoLineNumbers = 1
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr SymbolizeOptions operator&(SymbolizeOptions lhs,
|
|
||||||
SymbolizeOptions rhs) noexcept {
|
|
||||||
return static_cast<SymbolizeOptions>(
|
|
||||||
static_cast<std::underlying_type_t<SymbolizeOptions>>(lhs) &
|
|
||||||
static_cast<std::underlying_type_t<SymbolizeOptions>>(rhs));
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr SymbolizeOptions operator|(SymbolizeOptions lhs,
|
|
||||||
SymbolizeOptions rhs) noexcept {
|
|
||||||
return static_cast<SymbolizeOptions>(
|
|
||||||
static_cast<std::underlying_type_t<SymbolizeOptions>>(lhs) |
|
|
||||||
static_cast<std::underlying_type_t<SymbolizeOptions>>(rhs));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Symbolizes a program counter. On success, returns true and write the
|
// Symbolizes a program counter. On success, returns true and write the
|
||||||
// symbol name to "out". The symbol name is demangled if possible
|
// symbol name to "out". The symbol name is demangled if possible
|
||||||
// (supports symbols generated by GCC 3.x or newer). Otherwise,
|
// (supports symbols generated by GCC 3.x or newer). Otherwise,
|
||||||
// returns false.
|
// returns false.
|
||||||
GLOG_NO_EXPORT bool Symbolize(
|
GLOG_EXPORT bool Symbolize(void* pc, char* out, size_t out_size);
|
||||||
void* pc, char* out, size_t out_size,
|
|
||||||
SymbolizeOptions options = SymbolizeOptions::kNone);
|
|
||||||
|
|
||||||
#endif // defined(HAVE_SYMBOLIZE)
|
|
||||||
|
|
||||||
} // namespace glog_internal_namespace_
|
|
||||||
} // namespace google
|
} // namespace google
|
||||||
|
|
||||||
#endif // GLOG_INTERNAL_SYMBOLIZE_H
|
#endif // BASE_SYMBOLIZE_H_
|
||||||
|
|||||||
@ -40,7 +40,6 @@
|
|||||||
#include "glog/logging.h"
|
#include "glog/logging.h"
|
||||||
#include "googletest.h"
|
#include "googletest.h"
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
#include "stacktrace.h"
|
|
||||||
|
|
||||||
#ifdef GLOG_USE_GFLAGS
|
#ifdef GLOG_USE_GFLAGS
|
||||||
# include <gflags/gflags.h>
|
# include <gflags/gflags.h>
|
||||||
@ -61,13 +60,11 @@ using namespace google;
|
|||||||
|
|
||||||
# define always_inline
|
# define always_inline
|
||||||
|
|
||||||
# if defined(HAVE_ELF_H) || defined(HAVE_SYS_EXEC_ELF_H) || \
|
# if defined(__ELF__) || defined(GLOG_OS_WINDOWS) || defined(GLOG_OS_CYGWIN)
|
||||||
defined(GLOG_OS_WINDOWS) || defined(GLOG_OS_CYGWIN)
|
|
||||||
// A wrapper function for Symbolize() to make the unit test simple.
|
// A wrapper function for Symbolize() to make the unit test simple.
|
||||||
static const char* TrySymbolize(void* pc, google::SymbolizeOptions options =
|
static const char* TrySymbolize(void* pc) {
|
||||||
google::SymbolizeOptions::kNone) {
|
|
||||||
static char symbol[4096];
|
static char symbol[4096];
|
||||||
if (Symbolize(pc, symbol, sizeof(symbol), options)) {
|
if (Symbolize(pc, symbol, sizeof(symbol))) {
|
||||||
return symbol;
|
return symbol;
|
||||||
} else {
|
} else {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -75,7 +72,8 @@ static const char* TrySymbolize(void* pc, google::SymbolizeOptions options =
|
|||||||
}
|
}
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# if defined(HAVE_ELF_H) || defined(HAVE_SYS_EXEC_ELF_H)
|
# if defined(__ELF__)
|
||||||
|
|
||||||
// This unit tests make sense only with GCC.
|
// This unit tests make sense only with GCC.
|
||||||
// Uses lots of GCC specific features.
|
// Uses lots of GCC specific features.
|
||||||
# if defined(__GNUC__) && !defined(__OPENCC__)
|
# if defined(__GNUC__) && !defined(__OPENCC__)
|
||||||
@ -396,8 +394,7 @@ static void ATTRIBUTE_NOINLINE TestWithPCInsideInlineFunction() {
|
|||||||
static void ATTRIBUTE_NOINLINE TestWithReturnAddress() {
|
static void ATTRIBUTE_NOINLINE TestWithReturnAddress() {
|
||||||
# if defined(HAVE_ATTRIBUTE_NOINLINE)
|
# if defined(HAVE_ATTRIBUTE_NOINLINE)
|
||||||
void* return_address = __builtin_return_address(0);
|
void* return_address = __builtin_return_address(0);
|
||||||
const char* symbol =
|
const char* symbol = TrySymbolize(return_address);
|
||||||
TrySymbolize(return_address, google::SymbolizeOptions::kNoLineNumbers);
|
|
||||||
|
|
||||||
# if !defined(_MSC_VER) || !defined(NDEBUG)
|
# if !defined(_MSC_VER) || !defined(NDEBUG)
|
||||||
CHECK(symbol != nullptr);
|
CHECK(symbol != nullptr);
|
||||||
@ -442,23 +439,22 @@ __declspec(noinline) void TestWithReturnAddress() {
|
|||||||
_ReturnAddress()
|
_ReturnAddress()
|
||||||
# endif
|
# endif
|
||||||
;
|
;
|
||||||
const char* symbol =
|
const char* symbol = TrySymbolize(return_address);
|
||||||
TrySymbolize(return_address, google::SymbolizeOptions::kNoLineNumbers);
|
|
||||||
# if !defined(_MSC_VER) || !defined(NDEBUG)
|
# if !defined(_MSC_VER) || !defined(NDEBUG)
|
||||||
CHECK(symbol != nullptr);
|
CHECK(symbol != nullptr);
|
||||||
CHECK_STREQ(symbol, "main");
|
CHECK_STREQ(symbol, "main");
|
||||||
# endif
|
# endif
|
||||||
cout << "Test case TestWithReturnAddress passed." << endl;
|
cout << "Test case TestWithReturnAddress passed." << endl;
|
||||||
}
|
}
|
||||||
# endif
|
# endif // __ELF__
|
||||||
#endif // HAVE_STACKTRACE
|
#endif // HAVE_STACKTRACE
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
FLAGS_logtostderr = true;
|
FLAGS_logtostderr = true;
|
||||||
InitGoogleLogging(argv[0]);
|
InitGoogleLogging(argv[0]);
|
||||||
InitGoogleTest(&argc, argv);
|
InitGoogleTest(&argc, argv);
|
||||||
#if defined(HAVE_SYMBOLIZE) && defined(HAVE_STACKTRACE)
|
#if defined(HAVE_SYMBOLIZE) && defined(HAVE_STACKTRACE)
|
||||||
# if defined(HAVE_ELF_H) || defined(HAVE_SYS_EXEC_ELF_H)
|
# if defined(__ELF__)
|
||||||
// We don't want to get affected by the callback interface, that may be
|
// We don't want to get affected by the callback interface, that may be
|
||||||
// used to install some callback function at InitGoogle() time.
|
// used to install some callback function at InitGoogle() time.
|
||||||
InstallSymbolizeCallback(nullptr);
|
InstallSymbolizeCallback(nullptr);
|
||||||
@ -473,7 +469,7 @@ int main(int argc, char** argv) {
|
|||||||
# else // GLOG_OS_WINDOWS
|
# else // GLOG_OS_WINDOWS
|
||||||
printf("PASS (no symbolize_unittest support)\n");
|
printf("PASS (no symbolize_unittest support)\n");
|
||||||
return 0;
|
return 0;
|
||||||
# endif // defined(HAVE_ELF_H) || defined(HAVE_SYS_EXEC_ELF_H)
|
# endif // __ELF__
|
||||||
#else
|
#else
|
||||||
printf("PASS (no symbolize support)\n");
|
printf("PASS (no symbolize support)\n");
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
130
src/utilities.cc
130
src/utilities.cc
@ -29,26 +29,17 @@
|
|||||||
//
|
//
|
||||||
// Author: Shinichiro Hamaji
|
// Author: Shinichiro Hamaji
|
||||||
|
|
||||||
#define _GNU_SOURCE 1
|
|
||||||
|
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <cerrno>
|
|
||||||
#include <csignal>
|
#include <csignal>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
#include <ctime>
|
||||||
|
|
||||||
#include "base/googleinit.h"
|
#include "base/googleinit.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "glog/flags.h"
|
|
||||||
#include "glog/logging.h"
|
|
||||||
#include "stacktrace.h"
|
|
||||||
#include "symbolize.h"
|
|
||||||
|
|
||||||
#ifdef GLOG_OS_ANDROID
|
|
||||||
# include <android/log.h>
|
|
||||||
#endif
|
|
||||||
#ifdef HAVE_SYS_TIME_H
|
#ifdef HAVE_SYS_TIME_H
|
||||||
# include <sys/time.h>
|
# include <sys/time.h>
|
||||||
#endif
|
#endif
|
||||||
@ -66,9 +57,8 @@
|
|||||||
#ifdef HAVE_PWD_H
|
#ifdef HAVE_PWD_H
|
||||||
# include <pwd.h>
|
# include <pwd.h>
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef __ANDROID__
|
||||||
#if defined(HAVE___PROGNAME)
|
# include <android/log.h>
|
||||||
extern char* __progname;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
using std::string;
|
using std::string;
|
||||||
@ -81,35 +71,6 @@ bool IsGoogleLoggingInitialized() {
|
|||||||
return g_program_invocation_short_name != nullptr;
|
return g_program_invocation_short_name != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline namespace glog_internal_namespace_ {
|
|
||||||
|
|
||||||
constexpr int FileDescriptor::InvalidHandle;
|
|
||||||
|
|
||||||
void AlsoErrorWrite(LogSeverity severity, const char* tag,
|
|
||||||
const char* message) noexcept {
|
|
||||||
#if defined(GLOG_OS_WINDOWS)
|
|
||||||
(void)severity;
|
|
||||||
(void)tag;
|
|
||||||
// On Windows, also output to the debugger
|
|
||||||
::OutputDebugStringA(message);
|
|
||||||
#elif defined(GLOG_OS_ANDROID)
|
|
||||||
constexpr int android_log_levels[] = {
|
|
||||||
ANDROID_LOG_INFO,
|
|
||||||
ANDROID_LOG_WARN,
|
|
||||||
ANDROID_LOG_ERROR,
|
|
||||||
ANDROID_LOG_FATAL,
|
|
||||||
};
|
|
||||||
|
|
||||||
__android_log_write(android_log_levels[severity], tag, message);
|
|
||||||
#else
|
|
||||||
(void)severity;
|
|
||||||
(void)tag;
|
|
||||||
(void)message;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace glog_internal_namespace_
|
|
||||||
|
|
||||||
} // namespace google
|
} // namespace google
|
||||||
|
|
||||||
// The following APIs are all internal.
|
// The following APIs are all internal.
|
||||||
@ -129,11 +90,15 @@ static const int kPrintfPointerFieldWidth = 2 + 2 * sizeof(void*);
|
|||||||
|
|
||||||
static void DebugWriteToStderr(const char* data, void*) {
|
static void DebugWriteToStderr(const char* data, void*) {
|
||||||
// This one is signal-safe.
|
// This one is signal-safe.
|
||||||
if (write(fileno(stderr), data, strlen(data)) < 0) {
|
if (write(STDERR_FILENO, data, strlen(data)) < 0) {
|
||||||
// Ignore errors.
|
// Ignore errors.
|
||||||
}
|
}
|
||||||
AlsoErrorWrite(GLOG_FATAL,
|
# if defined(__ANDROID__)
|
||||||
glog_internal_namespace_::ProgramInvocationShortName(), data);
|
// ANDROID_LOG_FATAL as fatal error occurred and now is dumping call stack.
|
||||||
|
__android_log_write(ANDROID_LOG_FATAL,
|
||||||
|
glog_internal_namespace_::ProgramInvocationShortName(),
|
||||||
|
data);
|
||||||
|
# endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static void DebugWriteToString(const char* data, void* arg) {
|
static void DebugWriteToString(const char* data, void* arg) {
|
||||||
@ -185,9 +150,7 @@ static void DumpStackTrace(int skip_count, DebugWriter* writerfn, void* arg) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# ifdef __GNUC__
|
GLOG_NORETURN
|
||||||
__attribute__((noreturn))
|
|
||||||
# endif
|
|
||||||
static void
|
static void
|
||||||
DumpStackTraceAndExit() {
|
DumpStackTraceAndExit() {
|
||||||
DumpStackTrace(1, DebugWriteToStderr, nullptr);
|
DumpStackTrace(1, DebugWriteToStderr, nullptr);
|
||||||
@ -216,31 +179,15 @@ DumpStackTraceAndExit() {
|
|||||||
|
|
||||||
namespace google {
|
namespace google {
|
||||||
|
|
||||||
inline namespace glog_internal_namespace_ {
|
namespace glog_internal_namespace_ {
|
||||||
|
|
||||||
const char* const_basename(const char* filepath) {
|
|
||||||
const char* base = strrchr(filepath, '/');
|
|
||||||
#ifdef GLOG_OS_WINDOWS // Look for either path separator in Windows
|
|
||||||
if (!base) base = strrchr(filepath, '\\');
|
|
||||||
#endif
|
|
||||||
return base ? (base + 1) : filepath;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* ProgramInvocationShortName() {
|
const char* ProgramInvocationShortName() {
|
||||||
if (g_program_invocation_short_name != nullptr) {
|
if (g_program_invocation_short_name != nullptr) {
|
||||||
return g_program_invocation_short_name;
|
return g_program_invocation_short_name;
|
||||||
|
} else {
|
||||||
|
// TODO(hamaji): Use /proc/self/cmdline and so?
|
||||||
|
return "UNKNOWN";
|
||||||
}
|
}
|
||||||
#if defined(HAVE_PROGRAM_INVOCATION_SHORT_NAME)
|
|
||||||
return program_invocation_short_name;
|
|
||||||
#elif defined(HAVE_GETPROGNAME)
|
|
||||||
return getprogname();
|
|
||||||
#elif defined(HAVE___PROGNAME)
|
|
||||||
return __progname;
|
|
||||||
#elif defined(HAVE___ARGV)
|
|
||||||
return const_basename(__argv[0]);
|
|
||||||
#else
|
|
||||||
return "UNKNOWN";
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int32 g_main_thread_pid = getpid();
|
static int32 g_main_thread_pid = getpid();
|
||||||
@ -255,6 +202,14 @@ bool PidHasChanged() {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* const_basename(const char* filepath) {
|
||||||
|
const char* base = strrchr(filepath, '/');
|
||||||
|
#ifdef GLOG_OS_WINDOWS // Look for either path separator in Windows
|
||||||
|
if (!base) base = strrchr(filepath, '\\');
|
||||||
|
#endif
|
||||||
|
return base ? (base + 1) : filepath;
|
||||||
|
}
|
||||||
|
|
||||||
static string g_my_user_name;
|
static string g_my_user_name;
|
||||||
const string& MyUserName() { return g_my_user_name; }
|
const string& MyUserName() { return g_my_user_name; }
|
||||||
static void MyUserNameInitializer() {
|
static void MyUserNameInitializer() {
|
||||||
@ -287,19 +242,29 @@ static void MyUserNameInitializer() {
|
|||||||
}
|
}
|
||||||
REGISTER_MODULE_INITIALIZER(utilities, MyUserNameInitializer())
|
REGISTER_MODULE_INITIALIZER(utilities, MyUserNameInitializer())
|
||||||
|
|
||||||
|
#ifdef HAVE_STACKTRACE
|
||||||
|
void DumpStackTraceToString(string* stacktrace) {
|
||||||
|
DumpStackTrace(1, DebugWriteToString, stacktrace);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// We use an atomic operation to prevent problems with calling CrashReason
|
// We use an atomic operation to prevent problems with calling CrashReason
|
||||||
// from inside the Mutex implementation (potentially through RAW_CHECK).
|
// from inside the Mutex implementation (potentially through RAW_CHECK).
|
||||||
static std::atomic<const logging::internal::CrashReason*> g_reason{nullptr};
|
static std::atomic<const CrashReason*> g_reason{nullptr};
|
||||||
|
|
||||||
void SetCrashReason(const logging::internal::CrashReason* r) {
|
void SetCrashReason(const CrashReason* r) {
|
||||||
const logging::internal::CrashReason* expected = nullptr;
|
const CrashReason* expected = nullptr;
|
||||||
g_reason.compare_exchange_strong(expected, r);
|
g_reason.compare_exchange_strong(expected, r);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InitGoogleLoggingUtilities(const char* argv0) {
|
void InitGoogleLoggingUtilities(const char* argv0) {
|
||||||
CHECK(!IsGoogleLoggingInitialized())
|
CHECK(!IsGoogleLoggingInitialized())
|
||||||
<< "You called InitGoogleLogging() twice!";
|
<< "You called InitGoogleLogging() twice!";
|
||||||
g_program_invocation_short_name = const_basename(argv0);
|
const char* slash = strrchr(argv0, '/');
|
||||||
|
#ifdef GLOG_OS_WINDOWS
|
||||||
|
if (!slash) slash = strrchr(argv0, '\\');
|
||||||
|
#endif
|
||||||
|
g_program_invocation_short_name = slash ? slash + 1 : argv0;
|
||||||
|
|
||||||
#ifdef HAVE_STACKTRACE
|
#ifdef HAVE_STACKTRACE
|
||||||
InstallFailureFunction(&DumpStackTraceAndExit);
|
InstallFailureFunction(&DumpStackTraceAndExit);
|
||||||
@ -318,12 +283,17 @@ void ShutdownGoogleLoggingUtilities() {
|
|||||||
|
|
||||||
} // namespace glog_internal_namespace_
|
} // namespace glog_internal_namespace_
|
||||||
|
|
||||||
#ifdef HAVE_STACKTRACE
|
|
||||||
std::string GetStackTrace() {
|
|
||||||
std::string stacktrace;
|
|
||||||
DumpStackTrace(1, DebugWriteToString, &stacktrace);
|
|
||||||
return stacktrace;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
} // namespace google
|
} // namespace google
|
||||||
|
|
||||||
|
// Make an implementation of stacktrace compiled.
|
||||||
|
#ifdef STACKTRACE_H
|
||||||
|
# include STACKTRACE_H
|
||||||
|
# if 0
|
||||||
|
// For include scanners which can't handle macro expansions.
|
||||||
|
# include "stacktrace_generic-inl.h"
|
||||||
|
# include "stacktrace_libunwind-inl.h"
|
||||||
|
# include "stacktrace_powerpc-inl.h"
|
||||||
|
# include "stacktrace_x86-inl.h"
|
||||||
|
# include "stacktrace_x86_64-inl.h"
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|||||||
197
src/utilities.h
197
src/utilities.h
@ -32,15 +32,8 @@
|
|||||||
//
|
//
|
||||||
// Define utilities for glog internal usage.
|
// Define utilities for glog internal usage.
|
||||||
|
|
||||||
#ifndef GLOG_INTERNAL_UTILITIES_H
|
#ifndef UTILITIES_H__
|
||||||
#define GLOG_INTERNAL_UTILITIES_H
|
#define UTILITIES_H__
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
#include <type_traits>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
// printf macros for size_t, in the style of inttypes.h
|
// printf macros for size_t, in the style of inttypes.h
|
||||||
#ifdef _LP64
|
#ifdef _LP64
|
||||||
@ -60,14 +53,22 @@
|
|||||||
#define PRIXS __PRIS_PREFIX "X"
|
#define PRIXS __PRIS_PREFIX "X"
|
||||||
#define PRIoS __PRIS_PREFIX "o"
|
#define PRIoS __PRIS_PREFIX "o"
|
||||||
|
|
||||||
#include "config.h"
|
#include <string>
|
||||||
#include "glog/platform.h"
|
#include <thread>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include "glog/logging.h"
|
||||||
|
|
||||||
#if defined(GLOG_USE_WINDOWS_PORT)
|
#if defined(GLOG_USE_WINDOWS_PORT)
|
||||||
# include "port.h"
|
# include "port.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
#if defined(HAVE_UNISTD_H)
|
#if defined(HAVE_UNISTD_H)
|
||||||
# include <unistd.h>
|
# include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(HAVE_SSIZE_T)
|
#if !defined(HAVE_SSIZE_T)
|
||||||
# if defined(GLOG_OS_WINDOWS)
|
# if defined(GLOG_OS_WINDOWS)
|
||||||
# include <basetsd.h>
|
# include <basetsd.h>
|
||||||
@ -77,9 +78,6 @@ using ssize_t = std::ptrdiff_t;
|
|||||||
# endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "glog/log_severity.h"
|
|
||||||
#include "glog/types.h"
|
|
||||||
|
|
||||||
// There are three different ways we can try to get the stack trace:
|
// There are three different ways we can try to get the stack trace:
|
||||||
//
|
//
|
||||||
// 1) The libunwind library. This is still in development, and as a
|
// 1) The libunwind library. This is still in development, and as a
|
||||||
@ -102,6 +100,43 @@ using ssize_t = std::ptrdiff_t;
|
|||||||
// correctly when GetStackTrace() is called with max_depth == 0.
|
// correctly when GetStackTrace() is called with max_depth == 0.
|
||||||
// Some code may do that.
|
// Some code may do that.
|
||||||
|
|
||||||
|
#if defined(HAVE_LIBUNWIND)
|
||||||
|
# define STACKTRACE_H "stacktrace_libunwind-inl.h"
|
||||||
|
#elif defined(HAVE_UNWIND)
|
||||||
|
# define STACKTRACE_H "stacktrace_unwind-inl.h"
|
||||||
|
#elif !defined(NO_FRAME_POINTER)
|
||||||
|
# if defined(__i386__) && __GNUC__ >= 2
|
||||||
|
# define STACKTRACE_H "stacktrace_x86-inl.h"
|
||||||
|
# elif (defined(__ppc__) || defined(__PPC__)) && __GNUC__ >= 2
|
||||||
|
# define STACKTRACE_H "stacktrace_powerpc-inl.h"
|
||||||
|
# elif defined(GLOG_OS_WINDOWS)
|
||||||
|
# define STACKTRACE_H "stacktrace_windows-inl.h"
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(STACKTRACE_H) && defined(HAVE_EXECINFO_BACKTRACE)
|
||||||
|
# define STACKTRACE_H "stacktrace_generic-inl.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(STACKTRACE_H)
|
||||||
|
# define HAVE_STACKTRACE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef GLOG_NO_SYMBOLIZE_DETECTION
|
||||||
|
# ifndef HAVE_SYMBOLIZE
|
||||||
|
// defined by gcc
|
||||||
|
# if defined(__ELF__) && defined(GLOG_OS_LINUX)
|
||||||
|
# define HAVE_SYMBOLIZE
|
||||||
|
# elif defined(GLOG_OS_MACOSX) && defined(HAVE_DLADDR)
|
||||||
|
// Use dladdr to symbolize.
|
||||||
|
# define HAVE_SYMBOLIZE
|
||||||
|
# elif defined(GLOG_OS_WINDOWS)
|
||||||
|
// Use DbgHelp to symbolize
|
||||||
|
# define HAVE_SYMBOLIZE
|
||||||
|
# endif
|
||||||
|
# endif // !defined(HAVE_SYMBOLIZE)
|
||||||
|
#endif // !defined(GLOG_NO_SYMBOLIZE_DETECTION)
|
||||||
|
|
||||||
#ifndef ARRAYSIZE
|
#ifndef ARRAYSIZE
|
||||||
// There is a better way, but this is good enough for our purpose.
|
// There is a better way, but this is good enough for our purpose.
|
||||||
# define ARRAYSIZE(a) (sizeof(a) / sizeof(*(a)))
|
# define ARRAYSIZE(a) (sizeof(a) / sizeof(*(a)))
|
||||||
@ -109,26 +144,7 @@ using ssize_t = std::ptrdiff_t;
|
|||||||
|
|
||||||
namespace google {
|
namespace google {
|
||||||
|
|
||||||
namespace logging {
|
namespace glog_internal_namespace_ {
|
||||||
namespace internal {
|
|
||||||
|
|
||||||
struct CrashReason {
|
|
||||||
CrashReason() = default;
|
|
||||||
|
|
||||||
const char* filename{nullptr};
|
|
||||||
int line_number{0};
|
|
||||||
const char* message{nullptr};
|
|
||||||
|
|
||||||
// We'll also store a bit of stack trace context at the time of crash as
|
|
||||||
// it may not be available later on.
|
|
||||||
void* stack[32];
|
|
||||||
int depth{0};
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace internal
|
|
||||||
} // namespace logging
|
|
||||||
|
|
||||||
inline namespace glog_internal_namespace_ {
|
|
||||||
|
|
||||||
#if defined(__has_attribute)
|
#if defined(__has_attribute)
|
||||||
# if __has_attribute(noinline)
|
# if __has_attribute(noinline)
|
||||||
@ -148,9 +164,6 @@ inline namespace glog_internal_namespace_ {
|
|||||||
# define ATTRIBUTE_NOINLINE
|
# define ATTRIBUTE_NOINLINE
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void AlsoErrorWrite(LogSeverity severity, const char* tag,
|
|
||||||
const char* message) noexcept;
|
|
||||||
|
|
||||||
const char* ProgramInvocationShortName();
|
const char* ProgramInvocationShortName();
|
||||||
|
|
||||||
int32 GetMainThreadPid();
|
int32 GetMainThreadPid();
|
||||||
@ -162,7 +175,22 @@ const std::string& MyUserName();
|
|||||||
// (Doesn't modify filepath, contrary to basename() in libgen.h.)
|
// (Doesn't modify filepath, contrary to basename() in libgen.h.)
|
||||||
const char* const_basename(const char* filepath);
|
const char* const_basename(const char* filepath);
|
||||||
|
|
||||||
void SetCrashReason(const logging::internal::CrashReason* r);
|
void DumpStackTraceToString(std::string* stacktrace);
|
||||||
|
|
||||||
|
struct CrashReason {
|
||||||
|
CrashReason() = default;
|
||||||
|
|
||||||
|
const char* filename{nullptr};
|
||||||
|
int line_number{0};
|
||||||
|
const char* message{nullptr};
|
||||||
|
|
||||||
|
// We'll also store a bit of stack trace context at the time of crash as
|
||||||
|
// it may not be available later on.
|
||||||
|
void* stack[32];
|
||||||
|
int depth{0};
|
||||||
|
};
|
||||||
|
|
||||||
|
void SetCrashReason(const CrashReason* r);
|
||||||
|
|
||||||
void InitGoogleLoggingUtilities(const char* argv0);
|
void InitGoogleLoggingUtilities(const char* argv0);
|
||||||
void ShutdownGoogleLoggingUtilities();
|
void ShutdownGoogleLoggingUtilities();
|
||||||
@ -185,99 +213,10 @@ class ScopedExit final {
|
|||||||
Functor functor_;
|
Functor functor_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Thin wrapper around a file descriptor so that the file descriptor
|
|
||||||
// gets closed for sure.
|
|
||||||
class GLOG_NO_EXPORT FileDescriptor final {
|
|
||||||
static constexpr int InvalidHandle = -1;
|
|
||||||
|
|
||||||
public:
|
|
||||||
constexpr FileDescriptor() noexcept : FileDescriptor{nullptr} {}
|
|
||||||
constexpr explicit FileDescriptor(int fd) noexcept : fd_{fd} {}
|
|
||||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
|
||||||
constexpr FileDescriptor(std::nullptr_t) noexcept : fd_{InvalidHandle} {}
|
|
||||||
|
|
||||||
FileDescriptor(const FileDescriptor& other) = delete;
|
|
||||||
FileDescriptor& operator=(const FileDescriptor& other) = delete;
|
|
||||||
|
|
||||||
FileDescriptor(FileDescriptor&& other) noexcept : fd_{other.release()} {}
|
|
||||||
FileDescriptor& operator=(FileDescriptor&& other) noexcept {
|
|
||||||
// Close the file descriptor being held and assign a new file descriptor
|
|
||||||
// previously held by 'other' without closing it.
|
|
||||||
reset(other.release());
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr explicit operator bool() const noexcept {
|
|
||||||
return fd_ != InvalidHandle;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr int get() const noexcept { return fd_; }
|
|
||||||
|
|
||||||
int release() noexcept { return std::exchange(fd_, InvalidHandle); }
|
|
||||||
void reset(std::nullptr_t) noexcept { safe_close(); }
|
|
||||||
void reset() noexcept { reset(nullptr); }
|
|
||||||
void reset(int fd) noexcept {
|
|
||||||
reset();
|
|
||||||
fd_ = fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
int close() noexcept { return unsafe_close(); }
|
|
||||||
|
|
||||||
~FileDescriptor() { safe_close(); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
int unsafe_close() noexcept { return ::close(release()); }
|
|
||||||
void safe_close() noexcept {
|
|
||||||
if (*this) {
|
|
||||||
unsafe_close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int fd_;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Provide variants of (in)equality comparison operators to avoid constructing
|
|
||||||
// temporaries.
|
|
||||||
|
|
||||||
constexpr bool operator==(const FileDescriptor& lhs, int rhs) noexcept {
|
|
||||||
return lhs.get() == rhs;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr bool operator==(int lhs, const FileDescriptor& rhs) noexcept {
|
|
||||||
return rhs == lhs;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr bool operator!=(const FileDescriptor& lhs, int rhs) noexcept {
|
|
||||||
return !(lhs == rhs);
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr bool operator!=(int lhs, const FileDescriptor& rhs) noexcept {
|
|
||||||
return !(lhs == rhs);
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr bool operator==(const FileDescriptor& lhs, std::nullptr_t) noexcept {
|
|
||||||
return !lhs;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr bool operator==(std::nullptr_t, const FileDescriptor& rhs) noexcept {
|
|
||||||
return !rhs;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr bool operator!=(const FileDescriptor& lhs, std::nullptr_t) noexcept {
|
|
||||||
return static_cast<bool>(lhs);
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr bool operator!=(std::nullptr_t, const FileDescriptor& rhs) noexcept {
|
|
||||||
return static_cast<bool>(rhs);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace glog_internal_namespace_
|
} // namespace glog_internal_namespace_
|
||||||
|
|
||||||
} // namespace google
|
} // namespace google
|
||||||
|
|
||||||
template <>
|
using namespace google::glog_internal_namespace_;
|
||||||
struct std::default_delete<std::FILE> {
|
|
||||||
void operator()(FILE* p) const noexcept { fclose(p); }
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // GLOG_INTERNAL_UTILITIES_H
|
#endif // UTILITIES_H__
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
// Copyright (c) 2024, Google Inc.
|
// Copyright (c) 1999, 2007, Google Inc.
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// Redistribution and use in source and binary forms, with or without
|
// Redistribution and use in source and binary forms, with or without
|
||||||
@ -39,7 +39,9 @@
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include "glog/logging.h"
|
||||||
#include "glog/raw_logging.h"
|
#include "glog/raw_logging.h"
|
||||||
|
#include "utilities.h"
|
||||||
|
|
||||||
// glog doesn't have annotation
|
// glog doesn't have annotation
|
||||||
#define ANNOTATE_BENIGN_RACE(address, description)
|
#define ANNOTATE_BENIGN_RACE(address, description)
|
||||||
@ -48,14 +50,18 @@ using std::string;
|
|||||||
|
|
||||||
namespace google {
|
namespace google {
|
||||||
|
|
||||||
inline namespace glog_internal_namespace_ {
|
namespace glog_internal_namespace_ {
|
||||||
|
|
||||||
|
// Used by logging_unittests.cc so can't make it static here.
|
||||||
|
GLOG_EXPORT bool SafeFNMatch_(const char* pattern, size_t patt_len,
|
||||||
|
const char* str, size_t str_len);
|
||||||
|
|
||||||
// Implementation of fnmatch that does not need 0-termination
|
// Implementation of fnmatch that does not need 0-termination
|
||||||
// of arguments and does not allocate any memory,
|
// of arguments and does not allocate any memory,
|
||||||
// but we only support "*" and "?" wildcards, not the "[...]" patterns.
|
// but we only support "*" and "?" wildcards, not the "[...]" patterns.
|
||||||
// It's not a static function for the unittest.
|
// It's not a static function for the unittest.
|
||||||
GLOG_NO_EXPORT bool SafeFNMatch_(const char* pattern, size_t patt_len,
|
GLOG_EXPORT bool SafeFNMatch_(const char* pattern, size_t patt_len,
|
||||||
const char* str, size_t str_len) {
|
const char* str, size_t str_len) {
|
||||||
size_t p = 0;
|
size_t p = 0;
|
||||||
size_t s = 0;
|
size_t s = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
|
|||||||
@ -6,8 +6,8 @@
|
|||||||
* under the MIT license. For all details and documentation, see
|
* under the MIT license. For all details and documentation, see
|
||||||
* https://github.com/tronkko/dirent
|
* https://github.com/tronkko/dirent
|
||||||
*/
|
*/
|
||||||
#ifndef GLOG_INTERNAL_WINDOWS_DIRENT_H
|
#ifndef DIRENT_H
|
||||||
#define GLOG_INTERNAL_WINDOWS_DIRENT_H
|
#define DIRENT_H
|
||||||
|
|
||||||
/* Hide warnings about unreferenced local functions */
|
/* Hide warnings about unreferenced local functions */
|
||||||
#if defined(__clang__)
|
#if defined(__clang__)
|
||||||
@ -35,7 +35,6 @@
|
|||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <cwchar>
|
#include <cwchar>
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
/* Indicates that d_type field is available in dirent structure */
|
/* Indicates that d_type field is available in dirent structure */
|
||||||
#define _DIRENT_HAVE_D_TYPE
|
#define _DIRENT_HAVE_D_TYPE
|
||||||
@ -623,6 +622,8 @@ static WIN32_FIND_DATAW* dirent_next(_WDIR* dirp) {
|
|||||||
* Open directory stream using plain old C-string.
|
* Open directory stream using plain old C-string.
|
||||||
*/
|
*/
|
||||||
static DIR* opendir(const char* dirname) {
|
static DIR* opendir(const char* dirname) {
|
||||||
|
struct DIR* dirp;
|
||||||
|
|
||||||
/* Must have directory name */
|
/* Must have directory name */
|
||||||
if (dirname == nullptr || dirname[0] == '\0') {
|
if (dirname == nullptr || dirname[0] == '\0') {
|
||||||
dirent_set_errno(ENOENT);
|
dirent_set_errno(ENOENT);
|
||||||
@ -630,9 +631,7 @@ static DIR* opendir(const char* dirname) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate memory for DIR structure */
|
/* Allocate memory for DIR structure */
|
||||||
std::unique_ptr<DIR, decltype(&free)> dirp{
|
dirp = (DIR*)malloc(sizeof(struct DIR));
|
||||||
static_cast<DIR*>(malloc(sizeof(struct DIR))), &free};
|
|
||||||
|
|
||||||
if (!dirp) {
|
if (!dirp) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@ -650,18 +649,23 @@ static DIR* opendir(const char* dirname) {
|
|||||||
* the output buffer is too small to contain the resulting
|
* the output buffer is too small to contain the resulting
|
||||||
* string.
|
* string.
|
||||||
*/
|
*/
|
||||||
return nullptr;
|
goto exit_free;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Open directory stream using wide-character name */
|
/* Open directory stream using wide-character name */
|
||||||
dirp->wdirp = _wopendir(wname);
|
dirp->wdirp = _wopendir(wname);
|
||||||
if (!dirp->wdirp) {
|
if (!dirp->wdirp) {
|
||||||
return nullptr;
|
goto exit_free;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Success */
|
/* Success */
|
||||||
return dirp.release();
|
return dirp;
|
||||||
|
|
||||||
|
/* Failure */
|
||||||
|
exit_free:
|
||||||
|
free(dirp);
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1037,4 +1041,4 @@ static void dirent_set_errno(int error) {
|
|||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#endif /*GLOG_INTERNAL_WINDOWS_DIRENT_H*/
|
#endif /*DIRENT_H*/
|
||||||
|
|||||||
@ -42,9 +42,6 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
namespace google {
|
|
||||||
inline namespace glog_internal_namespace_ {
|
|
||||||
|
|
||||||
#ifndef HAVE_LOCALTIME_R
|
#ifndef HAVE_LOCALTIME_R
|
||||||
struct tm* localtime_r(const std::time_t* timep, std::tm* result) {
|
struct tm* localtime_r(const std::time_t* timep, std::tm* result) {
|
||||||
localtime_s(result, timep);
|
localtime_s(result, timep);
|
||||||
@ -57,6 +54,3 @@ struct tm* gmtime_r(const std::time_t* timep, std::tm* result) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
#endif // not HAVE_GMTIME_R
|
#endif // not HAVE_GMTIME_R
|
||||||
|
|
||||||
} // namespace glog_internal_namespace_
|
|
||||||
} // namespace google
|
|
||||||
|
|||||||
@ -43,14 +43,6 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#if defined(GLOG_USE_GLOG_EXPORT)
|
|
||||||
# include "glog/export.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined(GLOG_EXPORT)
|
|
||||||
# error "port.h" was not included correctly.
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
||||||
# ifndef WIN32_LEAN_AND_MEAN
|
# ifndef WIN32_LEAN_AND_MEAN
|
||||||
@ -70,6 +62,8 @@
|
|||||||
* used by both C and C++ code, so we put all the C++ together.
|
* used by both C and C++ code, so we put all the C++ together.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
# include "glog/logging.h"
|
||||||
|
|
||||||
# ifdef _MSC_VER
|
# ifdef _MSC_VER
|
||||||
|
|
||||||
/* 4244: otherwise we get problems when substracting two size_t's to an int
|
/* 4244: otherwise we get problems when substracting two size_t's to an int
|
||||||
@ -95,6 +89,9 @@
|
|||||||
# define O_CREAT _O_CREAT
|
# define O_CREAT _O_CREAT
|
||||||
# define O_EXCL _O_EXCL
|
# define O_EXCL _O_EXCL
|
||||||
|
|
||||||
|
# ifndef __MINGW32__
|
||||||
|
enum { STDIN_FILENO = 0, STDOUT_FILENO = 1, STDERR_FILENO = 2 };
|
||||||
|
# endif
|
||||||
# define S_IRUSR S_IREAD
|
# define S_IRUSR S_IREAD
|
||||||
# define S_IWUSR S_IWRITE
|
# define S_IWUSR S_IWRITE
|
||||||
|
|
||||||
@ -119,23 +116,24 @@
|
|||||||
|
|
||||||
# endif // _MSC_VER
|
# endif // _MSC_VER
|
||||||
|
|
||||||
namespace google {
|
|
||||||
inline namespace glog_internal_namespace_ {
|
|
||||||
# ifndef HAVE_LOCALTIME_R
|
# ifndef HAVE_LOCALTIME_R
|
||||||
GLOG_NO_EXPORT std::tm* localtime_r(const std::time_t* timep, std::tm* result);
|
extern GLOG_EXPORT std::tm* localtime_r(const std::time_t* timep,
|
||||||
|
std::tm* result);
|
||||||
# endif // not HAVE_LOCALTIME_R
|
# endif // not HAVE_LOCALTIME_R
|
||||||
|
|
||||||
# ifndef HAVE_GMTIME_R
|
# ifndef HAVE_GMTIME_R
|
||||||
GLOG_NO_EXPORT std::tm* gmtime_r(const std::time_t* timep, std::tm* result);
|
extern GLOG_EXPORT std::tm* gmtime_r(const std::time_t* timep, std::tm* result);
|
||||||
# endif // not HAVE_GMTIME_R
|
# endif // not HAVE_GMTIME_R
|
||||||
|
|
||||||
GLOG_NO_EXPORT
|
|
||||||
inline char* strerror_r(int errnum, char* buf, std::size_t buflen) {
|
inline char* strerror_r(int errnum, char* buf, std::size_t buflen) {
|
||||||
strerror_s(buf, buflen, errnum);
|
strerror_s(buf, buflen, errnum);
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
} // namespace glog_internal_namespace_
|
|
||||||
} // namespace google
|
# ifndef __cplusplus
|
||||||
|
/* I don't see how to get inlining for C code in MSVC. Ah well. */
|
||||||
|
# define inline
|
||||||
|
# endif
|
||||||
|
|
||||||
#endif /* _WIN32 */
|
#endif /* _WIN32 */
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user