Compare commits

..

1 Commits

Author SHA1 Message Date
Sergiu Deitsch
5ea3279ad1
wip 2024-01-05 23:16:18 +01:00
67 changed files with 1930 additions and 2663 deletions

View File

@ -1,8 +1,8 @@
---
tasks:
ubuntu1804:
name: "Ubuntu 22.04"
platform: ubuntu2204
name: "Ubuntu 18.04"
platform: ubuntu1804
build_flags:
- "--features=layering_check"
- "--copt=-Werror"

View File

@ -2,6 +2,7 @@
Checks: 'clang-diagnostic-*,clang-analyzer-*,google-*,modernize-*,-modernize-use-trailing-return-type,readability-*,portability-*,performance-*,bugprone-*,android-*,darwin-*,clang-analyzer-*'
WarningsAsErrors: ''
HeaderFilterRegex: ''
AnalyzeTemporaryDtors: false
FormatStyle: file
CheckOptions:
- key: cert-dcl16-c.NewSuffixes

View File

@ -31,20 +31,42 @@ jobs:
- name: Setup Dependencies
run: |
sudo apt-get update
DEBIAN_FRONTEND=noninteractive sudo apt-get install -y --no-install-suggests --no-install-recommends \
g++ \
DEBIAN_FRONTEND=noninteractive sudo apt-get install -y \
build-essential \
cmake \
gcovr \
libgflags-dev \
libgmock-dev \
libgtest-dev \
libunwind-dev \
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
if: matrix.build_type == 'Debug'
run: |
echo 'CXXFLAGS=--coverage' >> $GITHUB_ENV
echo 'GTest_ROOT=${{github.workspace}}/gtest' >> $GITHUB_ENV
- name: Configure
env:
@ -109,7 +131,7 @@ jobs:
- name: Upload Coverage to Codecov
if: matrix.build_type == 'Debug'
uses: codecov/codecov-action@v5
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: build_${{matrix.build_type}}/coverage.xml

View File

@ -68,7 +68,7 @@ jobs:
- name: Upload Coverage to Codecov
if: matrix.build_type == 'Debug'
uses: codecov/codecov-action@v5
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: build_${{matrix.build_type}}/coverage.xml

View File

@ -42,21 +42,21 @@ jobs:
- name: Cache GTest
id: cache-gtest
uses: actions/cache@v4
uses: actions/cache@v3
with:
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
if: steps.cache-gtest.outputs.cache-hit != 'true'
run: |
(New-Object System.Net.WebClient).DownloadFile("https://github.com/google/googletest/archive/refs/tags/v1.14.0.zip", "v1.14.0.zip")
Expand-Archive 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 release-1.11.0.zip .
- name: Build GTest
if: steps.cache-gtest.outputs.cache-hit != 'true'
run: |
cmake -S googletest-1.14.0 -B build-googletest `
cmake -S googletest-release-1.11.0 -B build-googletest `
-A ${{matrix.arch}} `
-DBUILD_SHARED_LIBS=${{matrix.lib == 'shared'}} `
-Dgtest_force_shared_crt=ON `
@ -67,7 +67,7 @@ jobs:
- name: Cache gflags
id: cache-gflags
uses: actions/cache@v4
uses: actions/cache@v3
with:
path: gflags/
key: ${{runner.os}}-gflags-2.2.2-${{matrix.lib}}-${{matrix.arch}}-${{matrix.build_type}}
@ -188,7 +188,7 @@ jobs:
- name: Setup Coverage Dependencies
if: matrix.build_type == 'Debug'
run: |
pip install 'gcovr==7.0'
pip install 'gcovr==6.0'
- name: Setup Environment
if: matrix.build_type == 'Debug'
@ -234,7 +234,7 @@ jobs:
- name: Upload Coverage to Codecov
if: matrix.build_type == 'Debug'
uses: codecov/codecov-action@v5
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: build_${{matrix.build_type}}/coverage.xml

3
.gitignore vendored
View File

@ -1,6 +1,3 @@
*.orig
/build*/
/site/
bazel-*
# Bzlmod lockfile
/MODULE.bazel.lock

View File

@ -17,6 +17,6 @@ platform(
constraint_values = [
"@platforms//cpu:x86_64",
"@platforms//os:windows",
"@rules_cc//cc/private/toolchain:clang-cl",
"@bazel_tools//tools/cpp:clang-cl",
],
)

View File

@ -1,6 +1,6 @@
cmake_minimum_required (VERSION 3.22)
project (glog
VERSION 0.8.0
VERSION 0.7.0
DESCRIPTION "C++ implementation of the Google logging module"
HOMEPAGE_URL https://github.com/google/glog
LANGUAGES CXX
@ -18,6 +18,7 @@ list (APPEND CMAKE_MODULE_PATH ${glog_SOURCE_DIR}/cmake)
include (CheckCXXSourceCompiles)
include (CheckCXXSourceRuns)
include (CheckCXXSymbolExists)
include (CheckFunctionExists)
include (CheckIncludeFileCXX)
include (CheckStructHasMember)
include (CheckTypeSize)
@ -32,7 +33,6 @@ include (GetCacheVariables)
include (GNUInstallDirs)
option (BUILD_SHARED_LIBS "Build shared libraries" ON)
option (BUILD_EXAMPLES "Build examples" ON)
option (PRINT_UNSYMBOLIZED_STACK_TRACES
"Print file offsets in traces instead of symbolizing" OFF)
option (WITH_GFLAGS "Use gflags" ON)
@ -64,7 +64,7 @@ set (CMAKE_VISIBILITY_INLINES_HIDDEN ON)
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)
set (HAVE_LIB_GTEST 1)
@ -126,11 +126,8 @@ if (Unwind_FOUND)
endif (Unwind_FOUND)
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 (link.h HAVE_LINK_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/time.h HAVE_SYS_TIME_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 (ssize_t HAVE_SSIZE_T LANGUAGE CXX)
check_cxx_symbol_exists (dladdr dlfcn.h HAVE_DLADDR)
check_cxx_symbol_exists (fcntl fcntl.h HAVE_FCNTL)
check_cxx_symbol_exists (posix_fadvise fcntl.h HAVE_POSIX_FADVISE)
check_cxx_symbol_exists (pread unistd.h HAVE_PREAD)
check_cxx_symbol_exists (pwrite unistd.h HAVE_PWRITE)
check_cxx_symbol_exists (sigaction csignal HAVE_SIGACTION)
check_cxx_symbol_exists (sigaltstack csignal HAVE_SIGALTSTACK)
check_function_exists (dladdr HAVE_DLADDR)
check_function_exists (fcntl HAVE_FCNTL)
check_function_exists (pread HAVE_PREAD)
check_function_exists (pwrite HAVE_PWRITE)
check_function_exists (sigaction HAVE_SIGACTION)
check_function_exists (sigaltstack HAVE_SIGALTSTACK)
check_cxx_symbol_exists (backtrace execinfo.h HAVE_EXECINFO_BACKTRACE)
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)
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)
set (GLOG_THREAD_LOCAL_STORAGE 1)
endif (WITH_TLS)
@ -278,12 +265,12 @@ if (WITH_SYMBOLIZE)
if (HAVE_SYMBOLIZE)
set (HAVE_STACKTRACE 1)
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)
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 (WITH_SYMBOLIZE)
@ -369,8 +356,6 @@ set (GLOG_SRCS
src/logging.cc
src/raw_logging.cc
src/signalhandler.cc
src/stacktrace.cc
src/stacktrace.h
src/symbolize.cc
src/symbolize.h
src/utilities.cc
@ -467,7 +452,7 @@ if (ANDROID)
endif (ANDROID)
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)
target_compile_definitions (glog PUBLIC GLOG_NO_ABBREVIATED_SEVERITIES)
@ -961,11 +946,6 @@ if (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
EXPORT glog-targets
RUNTIME DESTINATION ${_glog_CMake_BINDIR}

View File

@ -1,18 +1,19 @@
Copyright © 2024, Google Inc.
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.
* 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

View File

@ -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>
* google-glog: version 0.6.0.

View File

@ -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")

View File

@ -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
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
---------------
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 Googles 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. Heres 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 glogs 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 isnt 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 wont 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) << "Im printed when you run the program with --v=1 or higher";
VLOG(2) << "Im 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>`__.
Theres 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 cant 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))
<< "Im printed when size is more than 1024 and when you run the "
"program with --v=1 or more";
VLOG_EVERY_N(1, 10)
<< "Im 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)
<< "Im 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 dont 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, its 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 wont work.
// google::FlushLogFiles(google::ERROR);
// Use this instead.
google::FlushLogFiles(google::GLOG_ERROR);
If you dont need ``ERROR`` defined by ``windows.h``, there are a couple
of more workarounds which sometimes dont 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 wont.
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, glibcs unwinder has a deadlock issue.
However, if you dont use :cpp:`InstallFailureSignalHandler()` or you
dont worry about the rare possibilities of deadlocks, you can use
this stack-unwinder. If you specify no options and ``libunwind``
isnt 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
-----------------
Wed 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 youre sure
you own the intellectual property, then youll 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 youll 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 youve already
submitted one (even if it was for a different project), you probably
dont 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. Its generally best to start by opening a new issue describing the
bug or feature youre intending to fix. Even if you think its
relatively minor, its 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. Its 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

18
WORKSPACE Normal file
View 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"],
)

View File

@ -1 +0,0 @@
# WORKSPACE marker file needed by Bazel

View File

@ -4,6 +4,6 @@ cc_test(
srcs = ["main.cc"],
deps = [
"//:glog",
"@gflags//:gflags",
"@com_github_gflags_gflags//:gflags",
],
)

View File

@ -48,6 +48,8 @@ def glog_library(with_gflags = 1, **kwargs):
common_copts = [
"-std=c++14",
"-DGLOG_BAZEL_BUILD",
"-DHAVE_STRING_H",
"-I%s/glog_internal" % gendir,
] + (["-DGLOG_USE_GFLAGS"] if with_gflags else [])
@ -61,28 +63,23 @@ def glog_library(with_gflags = 1, **kwargs):
"-DHAVE_SYS_UTSNAME_H",
# For src/utilities.cc.
"-DHAVE_SYS_TIME_H",
# NOTE: users could optionally patch -DHAVE_UNWIND off if
# stacktrace dumping is not needed
"-DHAVE_UNWIND",
# Enable dumping stacktrace upon sigaction.
"-DHAVE_SIGACTION",
# For logging.cc.
"-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 + [
"-DGLOG_EXPORT=__attribute__((visibility(\\\"default\\\")))",
"-DGLOG_NO_EXPORT=__attribute__((visibility(\\\"default\\\")))",
"-DHAVE_POSIX_FADVISE",
"-DHAVE_MODE_T",
"-DHAVE_SSIZE_T",
"-DHAVE_SYS_TYPES_H",
# For src/utilities.cc.
"-DHAVE_SYS_SYSCALL_H",
# For src/logging.cc to create symlinks.
"-DHAVE_UNISTD_H",
"-fvisibility-inlines-hidden",
"-fvisibility=hidden",
]
@ -90,15 +87,11 @@ def glog_library(with_gflags = 1, **kwargs):
freebsd_only_copts = [
# Enable declaration of _Unwind_Backtrace
"-D_GNU_SOURCE",
"-DHAVE_LINK_H",
"-DHAVE_SYMBOLIZE", # Supported by <link.h>
]
linux_only_copts = [
# For utilities.h.
"-DHAVE_EXECINFO_H",
"-DHAVE_LINK_H",
"-DHAVE_SYMBOLIZE", # Supported by <link.h>
]
darwin_only_copts = [
@ -109,11 +102,10 @@ def glog_library(with_gflags = 1, **kwargs):
windows_only_copts = [
# Override -DGLOG_EXPORT= from the cc_library's defines.
"-DGLOG_EXPORT=__declspec(dllexport)",
"-DGLOG_NO_ABBREVIATED_SEVERITIES",
"-DGLOG_NO_EXPORT=",
"-DGLOG_NO_ABBREVIATED_SEVERITIES",
"-DGLOG_USE_WINDOWS_PORT",
"-DHAVE__CHSIZE_S",
"-DHAVE_DBGHELP",
"-I" + src_windows,
]
@ -123,26 +115,25 @@ def glog_library(with_gflags = 1, **kwargs):
]
windows_only_srcs = [
"src/glog/log_severity.h",
"src/windows/dirent.h",
"src/windows/port.cc",
"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({
# GLOG_EXPORT is normally set by export.h, but that's not
# generated for Bazel.
"@bazel_tools//src/conditions:windows": [
"GLOG_DEPRECATED=__declspec(deprecated)",
"GLOG_EXPORT=",
"GLOG_DEPRECATED=__declspec(deprecated)",
"GLOG_NO_ABBREVIATED_SEVERITIES",
"GLOG_NO_EXPORT=",
],
"//conditions:default": [
"GLOG_DEPRECATED=__attribute__((deprecated))",
"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/raw_logging.cc",
"src/signalhandler.cc",
"src/stacktrace.cc",
"src/stacktrace.h",
"src/stacktrace_generic-inl.h",
"src/stacktrace_libunwind-inl.h",
"src/stacktrace_powerpc-inl.h",
@ -191,7 +180,6 @@ def glog_library(with_gflags = 1, **kwargs):
"src/symbolize.cc",
"src/symbolize.h",
"src/utilities.cc",
"src/utilities.h",
"src/vlog_is_on.cc",
] + select({
"@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"],
"//conditions:default": [],
}),
linkopts = select({
"@bazel_tools//src/conditions:windows": ["dbghelp.lib"],
"//conditions:default": [],
}),
**kwargs
)
@ -261,7 +245,7 @@ def glog_library(with_gflags = 1, **kwargs):
copts = final_lib_copts + test_only_copts,
deps = gflags_deps + [
":glog",
"@googletest//:gtest",
"@com_github_google_googletest//:gtest",
],
**kwargs
)

View File

@ -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).

View File

@ -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).

View File

@ -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.

View File

@ -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 wont 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";
```

View 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.

View File

@ -1,3 +0,0 @@
# The 3-Clause BSD License
--8<-- "LICENSE.md"

View File

@ -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();
```

View File

@ -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.

View File

@ -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) << "Im printed when you run the program with --v=1 or higher";
VLOG(2) << "Im 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 cant 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))
<< "Im printed when size is more than 1024 and when you run the "
"program with --v=1 or more";
VLOG_EVERY_N(1, 10)
<< "Im 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)
<< "Im 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.

View File

@ -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 %}

View File

@ -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.

View File

@ -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

View File

@ -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
```

View File

@ -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.

View File

@ -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.

View File

@ -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 wont 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).

View File

@ -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";
}

View File

@ -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 &copy; 2024 Google Inc. &amp; 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

View File

@ -31,9 +31,6 @@
/* Define if you have the 'pread' function */
#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. */
#cmakedefine HAVE_PWD_H
@ -52,15 +49,6 @@
/* Define to 1 if you have the <syslog.h> header file. */
#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. */
#cmakedefine HAVE_SYS_SYSCALL_H
@ -127,16 +115,4 @@
/* define if abi::__cxa_demangle is available in cxxabi.h */
#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

View File

@ -37,6 +37,7 @@
#include "demangle.h"
#include <algorithm>
#include <cstddef>
#include <cstdlib>
#include <limits>
@ -51,7 +52,6 @@
#endif
namespace google {
inline namespace glog_internal_namespace_ {
#if !defined(GLOG_OS_WINDOWS) && !defined(HAVE___CXA_DEMANGLE)
namespace {
@ -1359,5 +1359,4 @@ bool Demangle(const char* mangled, char* out, size_t out_size) {
#endif
}
} // namespace glog_internal_namespace_
} // namespace google

View File

@ -67,28 +67,19 @@
// C++ ABI in the future.
//
#ifndef GLOG_INTERNAL_DEMANGLE_H
#define GLOG_INTERNAL_DEMANGLE_H
#ifndef BASE_DEMANGLE_H_
#define BASE_DEMANGLE_H_
#include <cstddef>
#if defined(GLOG_USE_GLOG_EXPORT)
# include "glog/export.h"
#endif
#if !defined(GLOG_NO_EXPORT)
# error "demangle.h" was not included correctly.
#endif
#include "config.h"
#include "glog/logging.h"
namespace google {
inline namespace glog_internal_namespace_ {
// Demangle "mangled". On success, return true and write the
// demangled symbol name to "out". Otherwise, return false.
// "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
#endif // GLOG_INTERNAL_DEMANGLE_H
#endif // BASE_DEMANGLE_H_

View File

@ -42,7 +42,7 @@
#endif
#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
#include "glog/platform.h"

View File

@ -35,7 +35,7 @@
#endif
#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
namespace google {
@ -95,6 +95,8 @@ constexpr int NUM_SEVERITIES = 4;
# define DFATAL_LEVEL FATAL
#endif
extern GLOG_EXPORT const char* const LogSeverityNames[NUM_SEVERITIES];
// NDEBUG usage helpers related to (RAW_)DCHECK:
//
// DEBUG_MODE is for small !NDEBUG uses like

View File

@ -56,8 +56,8 @@
# include "glog/export.h"
#endif
#if !defined(GLOG_EXPORT) || !defined(GLOG_NO_EXPORT)
# error <glog/logging.h> was not included correctly. See the documentation for how to consume the library.
#if !defined(GLOG_EXPORT)
# error <glog/logging.h> was not included correctly. See the documention for how to consume the library.
#endif
#include "glog/flags.h"
@ -68,12 +68,26 @@
# if __has_attribute(used)
# define GLOG_USED __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)
#if !defined(GLOG_USED)
# define 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/vlog_is_on.h"
@ -83,6 +97,10 @@ struct GLOG_EXPORT LogMessageTime {
LogMessageTime();
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 {
return timestamp_;
}
@ -96,10 +114,18 @@ struct GLOG_EXPORT LogMessageTime {
int dayOfWeek() const noexcept { return tm_.tm_wday; }
int dayInYear() const noexcept { return tm_.tm_yday; }
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_; }
const std::tm& tm() const noexcept { return tm_; }
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::chrono::system_clock::time_point
timestamp_; // Time of creation of LogMessage in seconds
@ -107,6 +133,27 @@ struct GLOG_EXPORT LogMessageTime {
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
// The global value of GOOGLE_STRIP_LOG. All the messages logged to
@ -456,31 +503,25 @@ namespace google {
// specified by argv0 in log outputs.
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.
GLOG_EXPORT bool IsGoogleLoggingInitialized();
// Shutdown google's logging library.
GLOG_EXPORT void ShutdownGoogleLogging();
#if defined(__GNUC__)
typedef void (*logging_fail_func_t)() __attribute__((noreturn));
#else
typedef void (*logging_fail_func_t)();
#endif
typedef void (*logging_fail_func_t)() GLOG_NORETURN;
class LogMessage;
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);
// Install a function which will be called after LOG(FATAL).
GLOG_EXPORT void 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.
GLOG_EXPORT void EnableLogCleaner(const std::chrono::minutes& overdue);
GLOG_EXPORT void DisableLogCleaner();
@ -531,14 +572,10 @@ class LogSink; // defined below
#define LOG_IF(severity, condition) \
static_cast<void>(0), \
!(condition) \
? (void)0 \
: google::logging::internal::LogMessageVoidify() & LOG(severity)
!(condition) ? (void)0 : google::LogMessageVoidify() & LOG(severity)
#define SYSLOG_IF(severity, condition) \
static_cast<void>(0), \
!(condition) \
? (void)0 \
: google::logging::internal::LogMessageVoidify() & SYSLOG(severity)
!(condition) ? (void)0 : google::LogMessageVoidify() & SYSLOG(severity)
#define LOG_ASSERT(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))) \
<< "Check failed: " #condition " "
namespace logging {
namespace internal {
// A container for a string pointer which can be evaluated to a bool -
// true iff the pointer is nullptr.
struct CheckOpString {
CheckOpString(std::unique_ptr<std::string> str) : str_(std::move(str)) {}
explicit operator bool() const noexcept {
CheckOpString(std::string* str) : str_(str) {}
// 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);
}
std::unique_ptr<std::string> str_;
std::string* str_;
};
// 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.
struct DummyClassToDefineOperator {};
} // namespace google
// Define global operator<< to declare using ::operator<<.
// This declaration will allow use to use CHECK macros for user
// defined classes which have operator<< (e.g., stl_logging.h).
inline std::ostream& operator<<(std::ostream& out,
const DummyClassToDefineOperator&) {
const google::DummyClassToDefineOperator&) {
return out;
}
namespace google {
// This formats a value for a failing CHECK_XX statement. Ordinarily,
// it uses the definition for operator<<, with a few special cases below.
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.
template <typename T1, typename T2>
std::unique_ptr<std::string> MakeCheckOpString(const T1& v1, const T2& v2,
const char* exprtext)
#if defined(__has_attribute)
# if __has_attribute(used)
__attribute__((noinline))
# endif
#endif
;
std::string* MakeCheckOpString(const T1& v1, const T2& v2,
const char* exprtext) GLOG_NOINLINE;
namespace base {
// A helper class for formatting "expr (V1 vs. V2)" in a CHECK_XX
// statement. See MakeCheckOpString for sample usage. Other
@ -648,16 +684,18 @@ class GLOG_EXPORT CheckOpMessageBuilder {
// For inserting the second variable (adds an intermediate " vs. ").
std::ostream* ForVar2();
// Get the result (inserts the closing ")").
std::unique_ptr<std::string> NewString();
std::string* NewString();
private:
std::ostringstream* stream_;
};
} // namespace base
template <typename T1, typename T2>
std::unique_ptr<std::string> MakeCheckOpString(const T1& v1, const T2& v2,
const char* exprtext) {
CheckOpMessageBuilder comb(exprtext);
std::string* MakeCheckOpString(const T1& v1, const T2& v2,
const char* exprtext) {
base::CheckOpMessageBuilder comb(exprtext);
MakeCheckOpValueString(comb.ForVar1(), v1);
MakeCheckOpValueString(comb.ForVar2(), v2);
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
// will not instantiate the template version of the function on values of
// unnamed enum type - see comment below.
#define DEFINE_CHECK_OP_IMPL(name, op) \
template <typename T1, typename T2> \
inline std::unique_ptr<std::string> name##Impl(const T1& v1, const T2& v2, \
const char* exprtext) { \
if (GOOGLE_PREDICT_TRUE(v1 op v2)) { \
return nullptr; \
} \
return MakeCheckOpString(v1, v2, exprtext); \
} \
inline std::unique_ptr<std::string> name##Impl(int v1, int v2, \
const char* exprtext) { \
return name##Impl<int, int>(v1, v2, exprtext); \
#define DEFINE_CHECK_OP_IMPL(name, op) \
template <typename T1, typename T2> \
inline std::string* name##Impl(const T1& v1, const T2& v2, \
const char* exprtext) { \
if (GOOGLE_PREDICT_TRUE(v1 op v2)) \
return nullptr; \
else \
return MakeCheckOpString(v1, v2, exprtext); \
} \
inline std::string* name##Impl(int v1, int v2, const char* exprtext) { \
return name##Impl<int, int>(v1, v2, exprtext); \
}
// 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.
// This happens if, for example, those are used as token names in a
// yacc grammar.
DEFINE_CHECK_OP_IMPL(Check_EQ, ==)
DEFINE_CHECK_OP_IMPL(Check_NE, !=)
DEFINE_CHECK_OP_IMPL(Check_EQ,
==) // 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_LT, <)
DEFINE_CHECK_OP_IMPL(Check_GE, >=)
DEFINE_CHECK_OP_IMPL(Check_GT, >)
#undef DEFINE_CHECK_OP_IMPL
// Helper macro for binary operators.
@ -710,25 +747,19 @@ DEFINE_CHECK_OP_IMPL(Check_GT, >)
// with other string implementations that get defined after this
// file is included). Save the current meaning now and use it
// in the macro.
using _Check_string = std::string;
# define CHECK_OP_LOG(name, op, val1, val2, log) \
while (std::unique_ptr<google::logging::internal::_Check_string> _result = \
google::logging::internal::Check##name##Impl( \
google::logging::internal::GetReferenceableValue(val1), \
google::logging::internal::GetReferenceableValue(val2), \
#val1 " " #op " " #val2)) \
log(__FILE__, __LINE__, \
google::logging::internal::CheckOpString(std::move(_result))) \
.stream()
typedef std::string _Check_string;
# define CHECK_OP_LOG(name, op, val1, val2, log) \
while (google::_Check_string* _result = google::Check##name##Impl( \
google::GetReferenceableValue(val1), \
google::GetReferenceableValue(val2), #val1 " " #op " " #val2)) \
log(__FILE__, __LINE__, google::CheckOpString(_result)).stream()
#else
// In optimized mode, use CheckOpString to hint to compiler that
// the while condition is unlikely.
# define CHECK_OP_LOG(name, op, val1, val2, log) \
while (google::logging::internal::CheckOpString _result = \
google::logging::internal::Check##name##Impl( \
google::logging::internal::GetReferenceableValue(val1), \
google::logging::internal::GetReferenceableValue(val2), \
#val1 " " #op " " #val2)) \
# define CHECK_OP_LOG(name, op, val1, val2, log) \
while (google::CheckOpString _result = google::Check##name##Impl( \
google::GetReferenceableValue(val1), \
google::GetReferenceableValue(val2), #val1 " " #op " " #val2)) \
log(__FILE__, __LINE__, _result).stream()
#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
// initializer lists.
#define CHECK_NOTNULL(val) \
google::logging::internal::CheckNotNull( \
__FILE__, __LINE__, "'" #val "' Must be non nullptr", (val))
#define CHECK_NOTNULL(val) \
google::CheckNotNull(__FILE__, __LINE__, "'" #val "' Must be non nullptr", \
(val))
// Helper functions for string comparisons.
// To avoid bloat, the definitions are in logging.cc.
#define DECLARE_CHECK_STROP_IMPL(func, expected) \
GLOG_EXPORT std::unique_ptr<std::string> Check##func##expected##Impl( \
#define DECLARE_CHECK_STROP_IMPL(func, expected) \
GLOG_EXPORT std::string* Check##func##expected##Impl( \
const char* s1, const char* s2, const char* names);
DECLARE_CHECK_STROP_IMPL(strcmp, true)
DECLARE_CHECK_STROP_IMPL(strcmp, false)
DECLARE_CHECK_STROP_IMPL(strcasecmp, true)
DECLARE_CHECK_STROP_IMPL(strcasecmp, false)
} // namespace internal
} // namespace logging
#undef DECLARE_CHECK_STROP_IMPL
// Helper macro for string comparisons.
// Don't use this macro directly in your code, use CHECK_STREQ et al below.
#define CHECK_STROP(func, op, expected, s1, s2) \
while (google::logging::internal::CheckOpString _result = \
google::logging::internal::Check##func##expected##Impl( \
(s1), (s2), #s1 " " #op " " #s2)) \
LOG(FATAL) << (*_result.str_)
#define CHECK_STROP(func, op, expected, s1, s2) \
while (google::CheckOpString _result = google::Check##func##expected##Impl( \
(s1), (s2), #s1 " " #op " " #s2)) \
LOG(FATAL) << *_result.str_
// String (char*) equality/inequality checks.
// CASE versions are case-insensitive.
@ -837,9 +862,7 @@ DECLARE_CHECK_STROP_IMPL(strcasecmp, false)
#define PLOG_IF(severity, condition) \
static_cast<void>(0), \
!(condition) \
? (void)0 \
: google::logging::internal::LogMessageVoidify() & PLOG(severity)
!(condition) ? (void)0 : google::LogMessageVoidify() & PLOG(severity)
// A CHECK() macro that postpends errno if the condition is false. E.g.
//
@ -951,13 +974,12 @@ namespace google {
LOG_OCCURRENCES, &what_to_do) \
.stream()
namespace logging {
namespace internal {
namespace glog_internal_namespace_ {
template <bool>
struct CompileAssert {};
struct CrashReason;
} // namespace internal
} // namespace logging
} // namespace glog_internal_namespace_
#define LOG_EVERY_N(severity, n) \
SOME_KIND_OF_LOG_EVERY_N(severity, (n), google::LogMessage::SendToLog)
@ -1038,41 +1060,34 @@ constexpr LogSeverity GLOG_0 = GLOG_ERROR;
# define DLOG(severity) \
static_cast<void>(0), \
true ? (void)0 \
: google::logging::internal::LogMessageVoidify() & LOG(severity)
true ? (void)0 : google::LogMessageVoidify() & LOG(severity)
# define DVLOG(verboselevel) \
static_cast<void>(0), \
(true || !VLOG_IS_ON(verboselevel)) \
? (void)0 \
: google::logging::internal::LogMessageVoidify() & LOG(INFO)
# define DVLOG(verboselevel) \
static_cast<void>(0), (true || !VLOG_IS_ON(verboselevel)) \
? (void)0 \
: google::LogMessageVoidify() & LOG(INFO)
# define DLOG_IF(severity, condition) \
static_cast<void>(0), \
(true || !(condition)) \
? (void)0 \
: google::logging::internal::LogMessageVoidify() & LOG(severity)
# define DLOG_IF(severity, condition) \
static_cast<void>(0), (true || !(condition)) \
? (void)0 \
: google::LogMessageVoidify() & LOG(severity)
# define DLOG_EVERY_N(severity, n) \
static_cast<void>(0), \
true ? (void)0 \
: google::logging::internal::LogMessageVoidify() & LOG(severity)
true ? (void)0 : google::LogMessageVoidify() & LOG(severity)
# define DLOG_IF_EVERY_N(severity, condition, n) \
static_cast<void>(0), \
(true || !(condition)) \
? (void)0 \
: google::logging::internal::LogMessageVoidify() & LOG(severity)
static_cast<void>(0), (true || !(condition)) \
? (void)0 \
: google::LogMessageVoidify() & LOG(severity)
# define DLOG_FIRST_N(severity, n) \
static_cast<void>(0), \
true ? (void)0 \
: google::logging::internal::LogMessageVoidify() & LOG(severity)
true ? (void)0 : google::LogMessageVoidify() & LOG(severity)
# define DLOG_EVERY_T(severity, T) \
static_cast<void>(0), \
true ? (void)0 \
: google::logging::internal::LogMessageVoidify() & LOG(severity)
true ? (void)0 : google::LogMessageVoidify() & LOG(severity)
# define DLOG_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 logging {
namespace internal {
struct GLOG_NO_EXPORT LogMessageData;
} // namespace internal
} // namespace logging
//
// This class more or less represents a particular log message. You
// create an instance of LogMessage and then stream stuff to it.
@ -1283,10 +1292,9 @@ class GLOG_EXPORT LogMessage {
std::string* message);
// A special constructor used for check failures
LogMessage(const char* file, int line,
const logging::internal::CheckOpString& result);
LogMessage(const char* file, int line, const CheckOpString& result);
~LogMessage() noexcept(false);
~LogMessage();
// Flush a buffered message to the sink set in the constructor. Always
// 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)
static int64 num_messages(int severity);
LogSeverity severity() const noexcept;
int line() const noexcept;
const std::thread::id& thread_id() const noexcept;
const char* fullname() const noexcept;
const char* basename() const noexcept;
const LogMessageTime& time() const noexcept;
[[deprecated("Use LogMessage::time() instead.")]] const LogMessageTime&
getLogMessageTime() const {
return time();
}
LogMessage(const LogMessage&) = delete;
LogMessage& operator=(const LogMessage&) = delete;
const LogMessageTime& time() const;
struct LogMessageData;
private:
// Fully internal SendMethod cases:
@ -1336,18 +1343,21 @@ class GLOG_EXPORT LogMessage {
void (LogMessage::*send_method)());
// 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:
static int64 num_messages_[NUM_SEVERITIES]; // under log_mutex
// We keep the data in a separate struct so that each instance of
// LogMessage uses less stack space.
logging::internal::LogMessageData* allocated_;
logging::internal::LogMessageData* data_;
LogMessageData* allocated_;
LogMessageData* data_;
LogMessageTime time_;
friend class LogDestination;
LogMessage(const LogMessage&);
void operator=(const LogMessage&);
};
// 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 {
public:
LogMessageFatal(const char* file, int line);
LogMessageFatal(const char* file, int line,
const logging::internal::CheckOpString& result);
[[noreturn]] ~LogMessageFatal() noexcept(false);
LogMessageFatal(const char* file, int line, const CheckOpString& result);
[[noreturn]] ~LogMessageFatal();
};
// 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) \
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
// only works if ostream is a LogStream. If the ostream is not a
// 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
// is not used" and "statement has no effect".
namespace logging {
namespace internal {
// 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 {
class GLOG_EXPORT LogMessageVoidify {
public:
LogMessageVoidify() {}
// This has to be an operator with a precedence lower than << but
// 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
// the specified severity level. Thread-safe.
GLOG_EXPORT void FlushLogFiles(LogSeverity min_severity);
@ -1465,8 +1470,13 @@ class GLOG_EXPORT LogSink {
// during this call.
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) = 0;
const LogMessageTime& logmsgtime, const char* message,
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
// the sink's logging logic to complete.
@ -1486,8 +1496,8 @@ class GLOG_EXPORT LogSink {
// Returns the normal text output of the log message.
// Can be useful to implement send().
static std::string ToString(LogSeverity severity, const char* file, int line,
const LogMessageTime& time, const char* message,
size_t message_len);
const LogMessageTime& logmsgtime,
const char* message, size_t message_len);
};
// 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,
// textual log messages already contain timestamps, and the
// 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,
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
virtual void Flush() = 0;
@ -1612,7 +1628,7 @@ class GLOG_EXPORT NullStream : public LogMessage::LogStream {
// the overloaded NullStream::operator<< will not be invoked.
NullStream();
NullStream(const char* /*file*/, int /*line*/,
const logging::internal::CheckOpString& /*result*/);
const CheckOpString& /*result*/);
NullStream& stream();
private:
@ -1671,9 +1687,6 @@ GLOG_EXPORT bool IsFailureSignalHandlerInstalled();
GLOG_EXPORT void InstallFailureWriter(void (*writer)(const char* data,
size_t size));
// Dump stack trace as a string.
GLOG_EXPORT std::string GetStackTrace();
} // namespace google
#endif // GLOG_LOGGING_H

View File

@ -39,9 +39,8 @@
#elif defined(__CYGWIN__) || defined(__CYGWIN32__)
# define GLOG_OS_CYGWIN
#elif defined(linux) || defined(__linux) || defined(__linux__)
# define GLOG_OS_LINUX
# if defined(__ANDROID__)
# define GLOG_OS_ANDROID
# ifndef GLOG_OS_LINUX
# define GLOG_OS_LINUX
# endif
#elif defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__)
# define GLOG_OS_MACOSX

View File

@ -41,7 +41,7 @@
#endif
#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
#include "glog/log_severity.h"

View File

@ -68,7 +68,7 @@
#endif
#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
#include "glog/flags.h"

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024, Google Inc.
// Copyright (c) 2009, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@ -51,16 +51,12 @@
#include <utility>
#include <vector>
#include "config.h"
#include "utilities.h"
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#if defined(GLOG_USE_WINDOWS_PORT)
# include "port.h"
#endif // defined(GLOG_USE_WINDOWS_PORT)
#include "base/commandlineflags.h"
#include "utilities.h"
#if __cplusplus < 201103L && !defined(_MSC_VER)
# define GOOGLE_GLOG_THROW_BAD_ALLOC throw(std::bad_alloc)
@ -74,17 +70,17 @@ using std::vector;
namespace google {
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 std::string StrError(int err);
} // namespace google
}
#undef GLOG_EXPORT
#define GLOG_EXPORT
static inline string GetTempDir() {
vector<string> temp_directories_list;
google::GetExistingTempDirectories(temp_directories_list);
google::GetExistingTempDirectories(&temp_directories_list);
if (temp_directories_list.empty()) {
fprintf(stderr, "No temporary directory found\n");
@ -196,19 +192,8 @@ void InitGoogleTest(int*, char**) {}
} \
} 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
# define TEST(a, b) \
struct Test_##a##_##b { \
Test_##a##_##b() { g_testlist.push_back(&Run); } \
@ -315,59 +300,66 @@ static inline void RunSpecifiedBenchmarks() {
class CapturedStream {
public:
CapturedStream(int fd, string filename)
: fd_(fd), filename_(std::move(filename)) {
: fd_(fd),
filename_(std::move(filename)) {
Capture();
}
~CapturedStream() {
if (uncaptured_fd_ != -1) {
CHECK(close(uncaptured_fd_) != -1);
}
}
// Start redirecting output to a file
void Capture() {
// Keep original stream for later
CHECK(!uncaptured_fd_) << ", Stream " << fd_ << " already captured!";
uncaptured_fd_.reset(dup(fd_));
CHECK(uncaptured_fd_);
CHECK(uncaptured_fd_ == -1) << ", Stream " << fd_ << " already captured!";
uncaptured_fd_ = dup(fd_);
CHECK(uncaptured_fd_ != -1);
// Open file to save stream to
FileDescriptor cap_fd{open(filename_.c_str(), O_CREAT | O_TRUNC | O_WRONLY,
S_IRUSR | S_IWUSR)};
CHECK(cap_fd);
int cap_fd = open(filename_.c_str(), O_CREAT | O_TRUNC | O_WRONLY,
S_IRUSR | S_IWUSR);
CHECK(cap_fd != -1);
// Send stdout/stderr to this file
fflush(nullptr);
CHECK(dup2(cap_fd.get(), fd_) != -1);
CHECK(cap_fd.close() != -1);
CHECK(dup2(cap_fd, fd_) != -1);
CHECK(close(cap_fd) != -1);
}
// Remove output redirection
void StopCapture() {
// Restore original stream
if (uncaptured_fd_) {
if (uncaptured_fd_ != -1) {
fflush(nullptr);
CHECK(dup2(uncaptured_fd_.get(), fd_) != -1);
CHECK(dup2(uncaptured_fd_, fd_) != -1);
}
}
const string& filename() const { return filename_; }
private:
int fd_; // file descriptor being captured
FileDescriptor
uncaptured_fd_; // where the stream was originally being sent to
string filename_; // file where stream is being saved
int fd_; // file descriptor being captured
int uncaptured_fd_{-1}; // where the stream was originally being sent to
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.
// fd - Should be stdout or stderr
// fd - Should be STDOUT_FILENO or STDERR_FILENO
// filename - File where output should be stored
static inline void CaptureTestOutput(int fd, const string& filename) {
CHECK((fd == fileno(stdout)) || (fd == fileno(stderr)));
CHECK(s_captured_streams.find(fd) == s_captured_streams.end());
s_captured_streams[fd] = std::make_unique<CapturedStream>(fd, filename);
CHECK((fd == STDOUT_FILENO) || (fd == STDERR_FILENO));
CHECK(s_captured_streams[fd] == nullptr);
s_captured_streams[fd] = new CapturedStream(fd, filename);
}
static inline void CaptureTestStdout() {
CaptureTestOutput(fileno(stdout), FLAGS_test_tmpdir + "/captured.out");
CaptureTestOutput(STDOUT_FILENO, FLAGS_test_tmpdir + "/captured.out");
}
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
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
static inline string ReadEntireFile(FILE* 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_read = 0; // # of bytes read so far
@ -388,32 +380,38 @@ static inline string ReadEntireFile(FILE* file) {
// pre-determined file size is reached.
do {
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;
} 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) {
CHECK((fd == fileno(stdout)) || (fd == fileno(stderr)));
std::unique_ptr<CapturedStream> cap = std::move(s_captured_streams.at(fd));
s_captured_streams.erase(fd);
CHECK(fd == STDOUT_FILENO || fd == STDERR_FILENO);
CapturedStream* const cap = s_captured_streams[fd];
CHECK(cap) << ": did you forget CaptureTestStdout() or CaptureTestStderr()?";
// Make sure everything is flushed.
cap->StopCapture();
// Read the captured file.
std::unique_ptr<FILE> file{fopen(cap->filename().c_str(), "r")};
const string content = ReadEntireFile(file.get());
file.reset();
FILE* const file = fopen(cap->filename().c_str(), "r");
const string content = ReadEntireFile(file);
fclose(file);
delete cap;
s_captured_streams[fd] = nullptr;
return content;
}
// Get the captured stderr of a test as a string.
static inline string GetCapturedTestStderr() {
return GetCapturedTestOutput(fileno(stderr));
return GetCapturedTestOutput(STDERR_FILENO);
}
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) {
std::unique_ptr<FILE> fp{fopen(filename.c_str(), "rb")};
FILE* fp = fopen(filename.c_str(), "rb");
CHECK(fp != nullptr) << filename << ": couldn't open";
char buf[4096];
string result;
while (fgets(buf, 4095, fp.get())) {
while (fgets(buf, 4095, fp)) {
string line = MungeLine(buf);
const size_t str_size = 256;
char null_str[str_size];
@ -504,19 +502,19 @@ static inline string Munge(const string& filename) {
StringReplace(&line, "__ENOEXEC__", StrError(ENOEXEC));
result += line + "\n";
}
fclose(fp);
return result;
}
static inline void WriteToFile(const string& body, const string& file) {
std::unique_ptr<FILE> fp{fopen(file.c_str(), "wb")};
fwrite(body.data(), 1, body.size(), fp.get());
FILE* fp = fopen(file.c_str(), "wb");
fwrite(body.data(), 1, body.size(), fp);
fclose(fp);
}
static inline bool MungeAndDiffTest(const string& golden_filename,
CapturedStream* cap) {
auto pos = s_captured_streams.find(fileno(stdout));
if (pos != s_captured_streams.end() && cap == pos->second.get()) {
if (cap == s_captured_streams[STDOUT_FILENO]) {
CHECK(cap) << ": did you forget CaptureTestStdout()?";
} else {
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) {
return MungeAndDiffTest(golden_filename,
s_captured_streams.at(fileno(stderr)).get());
return MungeAndDiffTest(golden_filename, s_captured_streams[STDERR_FILENO]);
}
static inline bool MungeAndDiffTestStdout(const string& golden_filename) {
return MungeAndDiffTest(golden_filename,
s_captured_streams.at(fileno(stdout)).get());
return MungeAndDiffTest(golden_filename, s_captured_streams[STDOUT_FILENO]);
}
// Save flags used from logging_unittest.cc.

View File

@ -38,21 +38,21 @@
#include <cstdint>
#include <iomanip>
#include <iterator>
#include <memory>
#include <mutex>
#include <shared_mutex>
#include <string>
#include <thread>
#include <tuple>
#include <type_traits>
#include <utility>
#include "config.h"
#include "glog/platform.h"
#include "glog/raw_logging.h"
#include "stacktrace.h"
#include "utilities.h"
#ifdef HAVE_STACKTRACE
# include "stacktrace.h"
#endif
#ifdef GLOG_OS_WINDOWS
# include "windows/dirent.h"
#else
@ -86,6 +86,10 @@
# include <syslog.h>
#endif
#ifdef __ANDROID__
# include <android/log.h>
#endif
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
@ -215,7 +219,6 @@ static bool TerminalSupportsColor() {
namespace google {
GLOG_NO_EXPORT
std::string StrError(int err);
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.
const size_t LogMessage::kMaxLogMessageLen = 30000;
namespace logging {
namespace internal {
struct LogMessageData {
struct LogMessage::LogMessageData {
LogMessageData();
int preserved_errno_; // preserved errno
// Buffer space; contains complete message text.
char message_text_[LogMessage::kMaxLogMessageLen + 1];
LogMessage::LogStream stream_;
LogStream stream_;
LogSeverity severity_; // What level is this LogMessage logged at?
int line_; // line number where logging call is.
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
bool has_been_flushed_; // false => data has not been flushed
bool first_fatal_; // true => this was first fatal msg
std::thread::id thread_id_;
private:
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
// 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)
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)?
static bool exit_on_dfatal = true;
@ -342,38 +342,37 @@ static bool SendEmailInternal(const char* dest, const char* subject,
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 {
constexpr std::intmax_t kSecondsInDay = 60 * 60 * 24;
constexpr std::intmax_t kSecondsInWeek = kSecondsInDay * 7;
// Optional user-configured callback to print custom prefixes.
class PrefixFormatter {
public:
PrefixFormatter(PrefixFormatterCallback callback, void* data) noexcept
: 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;
CustomPrefixCallback custom_prefix_callback = nullptr;
// User-provided data to pass to the callback:
void* custom_prefix_callback_data = nullptr;
// Encapsulates all file-system related state
class LogFileObject : public base::Logger {
@ -414,7 +413,7 @@ class LogFileObject : public base::Logger {
string base_filename_;
string symlink_basename_;
string filename_extension_; // option users can specify (eg to add port#)
std::unique_ptr<FILE> file_;
FILE* file_{nullptr};
LogSeverity severity_;
uint32 bytes_since_flush_{0};
uint32 dropped_mem_length_{0};
@ -545,12 +544,12 @@ class LogDestination {
// Send logging info to all registered sinks.
static void LogToSinks(LogSeverity severity, const char* full_filename,
const char* base_filename, int line,
const LogMessageTime& time, const char* message,
const LogMessageTime& logmsgtime, const char* message,
size_t message_len);
// Wait for all registered sinks via WaitTillSent
// 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);
@ -786,9 +785,24 @@ inline void LogDestination::MaybeLogToStderr(LogSeverity severity,
size_t prefix_len) {
if ((severity >= FLAGS_stderrthreshold) || FLAGS_alsologtostderr) {
ColoredWriteToStderr(severity, message, message_len);
AlsoErrorWrite(severity,
glog_internal_namespace_::ProgramInvocationShortName(),
message + prefix_len);
#ifdef GLOG_OS_WINDOWS
(void)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,
const char* full_filename,
const char* base_filename, int line,
const LogMessageTime& time,
const LogMessageTime& logmsgtime,
const char* message,
size_t message_len) {
std::shared_lock<SinkMutex> l{sink_mutex_};
if (sinks_) {
for (size_t i = sinks_->size(); i-- > 0;) {
(*sinks_)[i]->send(severity, full_filename, base_filename, line, time,
message, message_len);
(*sinks_)[i]->send(severity, full_filename, base_filename, line,
logmsgtime, message, message_len);
}
}
}
inline void LogDestination::WaitForSinks(
logging::internal::LogMessageData* data) {
inline void LogDestination::WaitForSinks(LogMessage::LogMessageData* data) {
std::shared_lock<SinkMutex> l{sink_mutex_};
if (sinks_) {
for (size_t i = sinks_->size(); i-- > 0;) {
@ -933,7 +946,10 @@ LogFileObject::LogFileObject(LogSeverity severity, const char* base_filename)
LogFileObject::~LogFileObject() {
std::lock_guard<std::mutex> l{mutex_};
file_ = nullptr;
if (file_ != nullptr) {
fclose(file_);
file_ = nullptr;
}
}
void LogFileObject::SetBasename(const char* basename) {
@ -942,6 +958,7 @@ void LogFileObject::SetBasename(const char* basename) {
if (base_filename_ != basename) {
// Get rid of old log file since we are changing names
if (file_ != nullptr) {
fclose(file_);
file_ = nullptr;
rollover_attempt_ = kRolloverAttemptFrequency - 1;
}
@ -954,6 +971,7 @@ void LogFileObject::SetExtension(const char* ext) {
if (filename_extension_ != ext) {
// Get rid of old log file since we are changing names
if (file_ != nullptr) {
fclose(file_);
file_ = nullptr;
rollover_attempt_ = kRolloverAttemptFrequency - 1;
}
@ -974,7 +992,7 @@ void LogFileObject::Flush() {
void LogFileObject::FlushUnlocked(
const std::chrono::system_clock::time_point& now) {
if (file_ != nullptr) {
fflush(file_.get());
fflush(file_);
bytes_since_flush_ = 0;
}
// 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) {
// demand that the file is unique for our timestamp (fail if it exists).
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);
}
}
FileDescriptor fd{
open(filename, flags, static_cast<mode_t>(FLAGS_logfile_mode))};
if (!fd) return false;
int fd = open(filename, flags, static_cast<mode_t>(FLAGS_logfile_mode));
if (fd == -1) return false;
#ifdef HAVE_FCNTL
// 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
// 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_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) {
close(fd); // as we are failing already, do not check errors here
return false;
}
#endif
// fdopen in append mode so if the file exists it will fseek to the end
file_.reset(fdopen(fd.release(), "a")); // Make a FILE*.
if (file_ == nullptr) { // Man, we're screwed!
file_ = fdopen(fd, "a"); // Make a FILE*.
if (file_ == nullptr) { // Man, we're screwed!
close(fd);
if (FLAGS_timestamp_in_logfile_name) {
unlink(filename); // Erase the half-baked evidence: an unusable log file,
// 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
// append empirically replicated with wine over mingw build
if (!FLAGS_timestamp_in_logfile_name) {
if (fseek(file_.get(), 0, SEEK_END) != 0) {
if (fseek(file_, 0, SEEK_END) != 0) {
return false;
}
}
@ -1123,6 +1127,7 @@ void LogFileObject::Write(
ScopedExit<decltype(cleanupLogs)> cleanupAtEnd{cleanupLogs};
if (file_length_ >> 20U >= MaxLogSize() || PidHasChanged()) {
if (file_ != nullptr) fclose(file_);
file_ = nullptr;
file_length_ = bytes_since_flush_ = dropped_mem_length_ = 0;
rollover_attempt_ = kRolloverAttemptFrequency - 1;
@ -1240,7 +1245,7 @@ void LogFileObject::Write(
const string& file_header_string = file_header_stream.str();
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;
bytes_since_flush_ += header_len;
}
@ -1254,7 +1259,7 @@ void LogFileObject::Write(
// 4096 bytes. fwrite() returns 4096 for message lengths that are
// greater than 4096, thereby indicating an error.
errno = 0;
fwrite(message, 1, message_len, file_.get());
fwrite(message, 1, message_len, file_);
if (FLAGS_stop_logging_if_full_disk &&
errno == ENOSPC) { // disk full, stop writing to disk
stop_writing = true; // until the disk is
@ -1285,10 +1290,13 @@ void LogFileObject::Write(
uint32 this_drop_length = total_drop_length - dropped_mem_length_;
if (this_drop_length >= (2U << 20U)) {
// Only advise when >= 2MiB to drop
# if defined(HAVE_POSIX_FADVISE)
posix_fadvise(
fileno(file_.get()), static_cast<off_t>(dropped_mem_length_),
static_cast<off_t>(this_drop_length), POSIX_FADV_DONTNEED);
# if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21)
// 'posix_fadvise' introduced in API 21:
// * https://android.googlesource.com/platform/bionic/+/6880f936173081297be0dc12f687d341b86a4cfa/libc/libc.map.txt#732
# else
posix_fadvise(fileno(file_), static_cast<off_t>(dropped_mem_length_),
static_cast<off_t>(this_drop_length),
POSIX_FADV_DONTNEED);
# endif
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
// all other threads.
static std::mutex fatal_msg_lock;
static logging::internal::CrashReason crash_reason;
static CrashReason crash_reason;
static bool fatal_msg_exclusive = true;
static logging::internal::LogMessageData fatal_msg_data_exclusive;
static logging::internal::LogMessageData fatal_msg_data_shared;
static LogMessage::LogMessageData fatal_msg_data_exclusive;
static LogMessage::LogMessageData fatal_msg_data_shared;
#ifdef GLOG_THREAD_LOCAL_STORAGE
// Static thread-local log data space to use, because typically at most one
@ -1515,16 +1523,16 @@ static thread_local bool thread_data_available = true;
# if defined(__cpp_lib_byte) && __cpp_lib_byte >= 201603L
// std::aligned_storage is deprecated in C++23
alignas(logging::internal::LogMessageData) static thread_local std::byte
thread_msg_data[sizeof(logging::internal::LogMessageData)];
alignas(LogMessage::LogMessageData) static thread_local std::byte
thread_msg_data[sizeof(LogMessage::LogMessageData)];
# else // !(defined(__cpp_lib_byte) && __cpp_lib_byte >= 201603L)
static thread_local std::aligned_storage<
sizeof(logging::internal::LogMessageData),
alignof(logging::internal::LogMessageData)>::type thread_msg_data;
sizeof(LogMessage::LogMessageData),
alignof(LogMessage::LogMessageData)>::type thread_msg_data;
# endif // defined(__cpp_lib_byte) && __cpp_lib_byte >= 201603L
#endif // defined(GLOG_THREAD_LOCAL_STORAGE)
logging::internal::LogMessageData::LogMessageData()
LogMessage::LogMessageData::LogMessageData()
: stream_(message_text_, LogMessage::kMaxLogMessageLen, 0) {}
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);
}
LogMessage::LogMessage(const char* file, int line,
const logging::internal::CheckOpString& result)
LogMessage::LogMessage(const char* file, int line, const CheckOpString& result)
: allocated_(nullptr) {
Init(file, line, GLOG_FATAL, &LogMessage::SendToLog);
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.
if (thread_data_available) {
thread_data_available = false;
data_ = new (&thread_msg_data) logging::internal::LogMessageData;
data_ = new (&thread_msg_data) LogMessageData;
} else {
allocated_ = new logging::internal::LogMessageData();
allocated_ = new LogMessageData();
data_ = allocated_;
}
#else // !defined(GLOG_THREAD_LOCAL_STORAGE)
allocated_ = new logging::internal::LogMessageData();
allocated_ = new LogMessageData();
data_ = allocated_;
#endif // defined(GLOG_THREAD_LOCAL_STORAGE)
data_->first_fatal_ = false;
@ -1618,7 +1625,6 @@ void LogMessage::Init(const char* file, int line, LogSeverity severity,
data_->basename_ = const_basename(file);
data_->fullname_ = file;
data_->has_been_flushed_ = false;
data_->thread_id_ = std::this_thread::get_id();
// If specified, prepend a prefix to each line. For example:
// 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);
saved_fmt.copyfmt(stream());
stream().fill('0');
if (g_prefix_formatter == nullptr) {
if (custom_prefix_callback == nullptr) {
stream() << LogSeverityNames[severity][0];
if (FLAGS_log_year_in_prefix) {
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_.sec() << "." << setw(6)
<< time_.usec() << ' ' << setfill(' ') << setw(5)
<< data_->thread_id_ << setfill('0') << ' ' << data_->basename_
<< ':' << data_->line_ << "] ";
<< std::this_thread::get_id() << setfill('0') << ' '
<< data_->basename_ << ':' << data_->line_ << "] ";
} 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().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);
#ifdef HAVE_STACKTRACE
if (FLAGS_log_backtrace_at == fileline) {
string stacktrace = GetStackTrace();
string stacktrace;
DumpStackTraceToString(&stacktrace);
stream() << " (stacktrace:\n" << stacktrace << ") ";
}
#endif
}
}
LogSeverity LogMessage::severity() const noexcept { return data_->severity_; }
const LogMessageTime& LogMessage::time() const { return time_; }
int LogMessage::line() const noexcept { return data_->line_; }
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) {
LogMessage::~LogMessage() {
Flush();
bool fail = data_->severity_ == GLOG_FATAL && exit_on_dfatal;
#ifdef GLOG_THREAD_LOCAL_STORAGE
if (data_ == static_cast<void*>(&thread_msg_data)) {
data_->~LogMessageData();
@ -1682,26 +1684,6 @@ LogMessage::~LogMessage() noexcept(false) {
#else // !defined(GLOG_THREAD_LOCAL_STORAGE)
delete allocated_;
#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_; }
@ -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_);
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->line_number = fatal_msg_data_exclusive.line_;
reason->message = fatal_msg_data_exclusive.message_text_ +
@ -1878,12 +1879,12 @@ void LogMessage::RecordCrashReason(logging::internal::CrashReason* reason) {
#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);
NullStream::NullStream() : LogMessage::LogStream(message_buffer_, 2, 0) {}
NullStream::NullStream(const char* /*file*/, int /*line*/,
const logging::internal::CheckOpString& /*result*/)
const CheckOpString& /*result*/)
: LogMessage::LogStream(message_buffer_, 2, 0) {}
NullStream& NullStream::stream() { return *this; }
@ -1893,8 +1894,8 @@ NullStreamFatal::~NullStreamFatal() {
std::abort();
}
logging_fail_func_t InstallFailureFunction(logging_fail_func_t fail_func) {
return std::exchange(g_logging_fail_func, fail_func);
void InstallFailureFunction(logging_fail_func_t fail_func) {
g_logging_fail_func = fail_func;
}
void LogMessage::Fail() { g_logging_fail_func(); }
@ -2028,25 +2029,58 @@ void SetLogSymlink(LogSeverity severity, const char* symlink_basename) {
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() {
// noop default
}
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) {
ostringstream stream;
stream.fill('0');
stream << LogSeverityNames[severity][0];
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() << ' '
<< setw(2) << time.hour() << ':' << setw(2) << time.min() << ':'
<< setw(2) << time.sec() << '.' << setw(6) << time.usec() << ' '
<< setfill(' ') << setw(5) << std::this_thread::get_id()
<< setfill('0') << ' ' << file << ':' << line << "] ";
stream << setw(2) << 1 + logmsgtime.month() << setw(2) << logmsgtime.day()
<< ' ' << setw(2) << logmsgtime.hour() << ':' << setw(2)
<< logmsgtime.min() << ':' << setw(2) << logmsgtime.sec() << '.'
<< setw(6) << logmsgtime.usec() << ' ' << setfill(' ') << setw(5)
<< std::this_thread::get_id() << setfill('0') << ' ' << file << ':'
<< line << "] ";
// A call to `write' is enclosed in parenthneses to prevent possible macro
// 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);
}
static void GetTempDirectories(vector<string>& list) {
list.clear();
static void GetTempDirectories(vector<string>* list) {
list->clear();
#ifdef GLOG_OS_WINDOWS
// On windows we'll try to find a directory in this order:
// C:/Documents & Settings/whomever/TEMP (or whatever GetTempPath() is)
// C:/TMP/
// C:/TEMP/
// C:/WINDOWS/ or C:/WINNT/
// .
char tmp[MAX_PATH];
if (GetTempPathA(MAX_PATH, tmp)) list.push_back(tmp);
list.push_back("C:\\TMP\\");
list.push_back("C:\\TEMP\\");
if (GetTempPathA(MAX_PATH, tmp)) list->push_back(tmp);
list->push_back("C:\\tmp\\");
list->push_back("C:\\temp\\");
#else
// Directories, in order of preference. If we find a dir that
// exists, we stop adding other less-preferred dirs
@ -2292,7 +2328,7 @@ static void GetTempDirectories(vector<string>& list) {
if (dstr[dstr.size() - 1] != '/') {
dstr += "/";
}
list.push_back(dstr);
list->push_back(dstr);
struct stat statbuf;
if (!stat(d, &statbuf) && S_ISDIR(statbuf.st_mode)) {
@ -2300,15 +2336,16 @@ static void GetTempDirectories(vector<string>& list) {
return;
}
}
#endif
}
static std::unique_ptr<std::vector<std::string>> logging_directories_list;
static vector<string>* logging_directories_list;
const vector<string>& GetLoggingDirectories() {
// Not strictly thread-safe but we're called early in InitGoogle().
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()) {
// 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);
}
} else {
GetTempDirectories(*logging_directories_list);
GetTempDirectories(logging_directories_list);
#ifdef GLOG_OS_WINDOWS
char tmp[MAX_PATH];
if (GetWindowsDirectoryA(tmp, MAX_PATH))
@ -2338,14 +2375,14 @@ const vector<string>& GetLoggingDirectories() {
// subset of the directories returned by GetLoggingDirectories().
// Thread-safe.
GLOG_NO_EXPORT
void GetExistingTempDirectories(vector<string>& list) {
void GetExistingTempDirectories(vector<string>* list) {
GetTempDirectories(list);
auto i_dir = list.begin();
while (i_dir != list.end()) {
auto i_dir = list->begin();
while (i_dir != list->end()) {
// zero arg to access means test for existence; no constant
// defined on windows
if (access(i_dir->c_str(), 0)) {
i_dir = list.erase(i_dir);
i_dir = list->erase(i_dir);
} else {
++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;
# endif
FileDescriptor fd{open(path, flags)};
if (!fd) {
int fd = open(path, flags);
if (fd == -1) {
if (errno == EFBIG) {
// 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
@ -2375,7 +2412,7 @@ void TruncateLogFile(const char* path, uint64 limit, uint64 keep) {
// rather scary.
// Instead just truncate the file to something we can manage
# ifdef HAVE__CHSIZE_S
if (_chsize_s(fd.get(), 0) != 0) {
if (_chsize_s(fd, 0) != 0) {
# else
if (truncate(path, 0) == -1) {
# endif
@ -2389,16 +2426,16 @@ void TruncateLogFile(const char* path, uint64 limit, uint64 keep) {
return;
}
if (fstat(fd.get(), &statbuf) == -1) {
if (fstat(fd, &statbuf) == -1) {
PLOG(ERROR) << "Unable to fstat()";
return;
goto out_close_fd;
}
// See if the path refers to a regular file bigger than the
// specified limit
if (!S_ISREG(statbuf.st_mode)) return;
if (statbuf.st_size <= static_cast<off_t>(limit)) return;
if (statbuf.st_size <= static_cast<off_t>(keep)) return;
if (!S_ISREG(statbuf.st_mode)) goto out_close_fd;
if (statbuf.st_size <= static_cast<off_t>(limit)) goto out_close_fd;
if (statbuf.st_size <= static_cast<off_t>(keep)) goto out_close_fd;
// This log file is too large - we need to truncate it
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);
write_offset = 0;
ssize_t bytesin, bytesout;
while ((bytesin = pread(fd.get(), copybuf, sizeof(copybuf), read_offset)) >
0) {
bytesout =
pwrite(fd.get(), copybuf, static_cast<size_t>(bytesin), write_offset);
while ((bytesin = pread(fd, copybuf, sizeof(copybuf), read_offset)) > 0) {
bytesout = pwrite(fd, copybuf, static_cast<size_t>(bytesin), write_offset);
if (bytesout == -1) {
PLOG(ERROR) << "Unable to write to " << path;
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
// data. Too bad ...
# ifdef HAVE__CHSIZE_S
if (_chsize_s(fd.get(), write_offset) != 0) {
if (_chsize_s(fd, write_offset) != 0) {
# else
if (ftruncate(fd.get(), write_offset) == -1) {
if (ftruncate(fd, write_offset) == -1) {
# endif
PLOG(ERROR) << "Unable to truncate " << path;
}
out_close_fd:
close(fd);
#else
LOG(ERROR) << "No log truncation support.";
#endif
@ -2449,21 +2486,19 @@ void TruncateStdoutStderr() {
#endif
}
namespace logging {
namespace internal {
// Helper functions for string comparisons.
#define DEFINE_CHECK_STROP_IMPL(name, func, expected) \
std::unique_ptr<string> Check##func##expected##Impl( \
const char* s1, const char* s2, const char* names) { \
string* Check##func##expected##Impl(const char* s1, const char* s2, \
const char* names) { \
bool equal = s1 == s2 || (s1 && s2 && !func(s1, s2)); \
if (equal == (expected)) \
if (equal == expected) \
return nullptr; \
else { \
ostringstream ss; \
if (!s1) s1 = ""; \
if (!s2) 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)
@ -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_STRCASENE, strcasecmp, false)
#undef DEFINE_CHECK_STROP_IMPL
} // namespace internal
} // namespace logging
// glibc has traditionally implemented two incompatible versions of
// 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
// given POSIX error code.
GLOG_NO_EXPORT
string StrError(int err) {
char buf[100];
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) {}
LogMessageFatal::LogMessageFatal(const char* file, int line,
const logging::internal::CheckOpString& result)
const CheckOpString& result)
: LogMessage(file, line, result) {}
LogMessageFatal::~LogMessageFatal() noexcept(false) {
LogMessageFatal::~LogMessageFatal() {
Flush();
LogMessage::Fail();
}
namespace logging {
namespace internal {
namespace base {
CheckOpMessageBuilder::CheckOpMessageBuilder(const char* exprtext)
: stream_(new ostringstream) {
@ -2575,11 +2608,13 @@ ostream* CheckOpMessageBuilder::ForVar2() {
return stream_;
}
std::unique_ptr<string> CheckOpMessageBuilder::NewString() {
string* CheckOpMessageBuilder::NewString() {
*stream_ << ")";
return std::make_unique<std::string>(stream_->str());
return new string(stream_->str());
}
} // namespace base
template <>
void MakeCheckOpValueString(std::ostream* os, const char& v) {
if (v >= 32 && v <= 126) {
@ -2612,24 +2647,22 @@ void MakeCheckOpValueString(std::ostream* os, const std::nullptr_t& /*v*/) {
(*os) << "nullptr";
}
} // namespace internal
} // namespace logging
void InitGoogleLogging(const char* argv0) {
glog_internal_namespace_::InitGoogleLoggingUtilities(argv0);
}
void InitGoogleLogging(const char* argv0) { InitGoogleLoggingUtilities(argv0); }
void InstallPrefixFormatter(PrefixFormatterCallback callback, void* data) {
if (callback != nullptr) {
g_prefix_formatter = std::make_unique<PrefixFormatter>(callback, data);
} else {
g_prefix_formatter = nullptr;
}
void InitGoogleLogging(const char* argv0, CustomPrefixCallback prefix_callback,
void* prefix_callback_data) {
custom_prefix_callback = prefix_callback;
custom_prefix_callback_data = prefix_callback_data;
InitGoogleLogging(argv0);
}
void ShutdownGoogleLogging() {
ShutdownGoogleLoggingUtilities();
glog_internal_namespace_::ShutdownGoogleLoggingUtilities();
LogDestination::DeleteLogDestinations();
delete logging_directories_list;
logging_directories_list = nullptr;
g_prefix_formatter = nullptr;
}
void EnableLogCleaner(unsigned int overdue_days) {
@ -2646,82 +2679,39 @@ void DisableLogCleaner() { log_cleaner.Disable(); }
LogMessageTime::LogMessageTime() = default;
namespace {
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>> {
LogMessageTime::LogMessageTime(std::chrono::system_clock::time_point now)
: timestamp_{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) {
gmtime_r(&timestamp, &tm_local);
localtime_r(&timestamp, &tm_utc);
isdst = tm_utc.tm_isdst;
tm_utc = tm_local;
gmtime_r(&timestamp, &tm_);
} else {
localtime_r(&timestamp, &tm_local);
isdst = tm_local.tm_isdst;
gmtime_r(&timestamp, &tm_utc);
localtime_r(&timestamp, &tm_);
}
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
// current timestamp.
using namespace std::chrono_literals;
const auto gmtoffset = std::chrono::duration_cast<std::chrono::hours>(
now - std::chrono::system_clock::from_time_t(gmt_sec) +
(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(&timestamp, &tm);
} else {
localtime_r(&timestamp, &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));
gmtoffset_ = std::chrono::duration_cast<std::chrono::seconds>(
timestamp_ - std::chrono::system_clock::from_time_t(gmt_sec) +
(isDst ? 1h : 0h));
}
} // namespace google

View File

@ -31,22 +31,8 @@
#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 "utilities.h"
#ifdef HAVE_GLOB_H
# include <glob.h>
#endif
@ -58,12 +44,24 @@
# include <sys/wait.h>
#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 "glog/logging.h"
#include "glog/raw_logging.h"
#include "googletest.h"
#include "stacktrace.h"
#include "utilities.h"
#ifdef GLOG_USE_GFLAGS
# include <gflags/gflags.h>
@ -110,7 +108,6 @@ static void TestLogSinkWaitTillSent();
static void TestCHECK();
static void TestDCHECK();
static void TestSTREQ();
static void TestMaxLogSizeWhenNoTimestamp();
static void TestBasename();
static void TestBasenameAppendWhenNoTimestamp();
static void TestTwoProcessesWrite();
@ -187,27 +184,23 @@ static void BM_vlog(int n) {
}
BENCHMARK(BM_vlog)
namespace {
// Dynamically generate a prefix using the default format and write it to the
// 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
// prefix (otherwise causing the tests to fail):
if (data == nullptr || *static_cast<string*>(data) != "good data") {
return;
}
s << 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() << "]";
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 << "]";
}
} // namespace
int main(int argc, char** argv) {
FLAGS_colorlogtostderr = false;
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
// the golden outputs can be reused):
string prefix_attacher_data = "good data";
InitGoogleLogging(argv[0]);
InstallPrefixFormatter(&PrefixAttacher, &prefix_attacher_data);
InitGoogleLogging(argv[0], &PrefixAttacher,
static_cast<void*>(&prefix_attacher_data));
EXPECT_TRUE(IsGoogleLoggingInitialized());
@ -289,7 +282,6 @@ int main(int argc, char** argv) {
MungeAndDiffTestStdout(FLAGS_test_srcdir + "/src/logging_unittest.out"));
FLAGS_logtostdout = false;
TestMaxLogSizeWhenNoTimestamp();
TestBasename();
TestBasenameAppendWhenNoTimestamp();
TestTwoProcessesWrite();
@ -793,62 +785,23 @@ static void CheckFile(const string& name, const string& expected_string,
GetFiles(name + "*", &files);
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];
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());
// if first == nullptr, not found.
// Terser than if (checkInFileOrNot && first != nullptr || !check...
if (checkInFileOrNot != (first == nullptr)) {
fclose(file);
return;
}
}
fclose(file);
LOG(FATAL) << "Did " << (checkInFileOrNot ? "not " : "") << "find "
<< 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() {
fprintf(stderr, "==== Test setting log file basename\n");
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,
size_t dsize, size_t ksize, size_t expect) {
FileDescriptor fd{open(path, O_RDWR | O_CREAT | O_TRUNC, 0600)};
CHECK_ERR(fd);
int fd;
CHECK_ERR(fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0600));
const char *discardstr = "DISCARDME!", *keepstr = "KEEPME!";
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;
while (written < dsize) {
size_t bytes = min(dsize - written, discard_size);
CHECK_ERR(write(fd.get(), discardstr, bytes));
CHECK_ERR(write(fd, discardstr, bytes));
written += bytes;
}
written = 0;
while (written < ksize) {
size_t bytes = min(ksize - written, keep_size);
CHECK_ERR(write(fd.get(), keepstr, bytes));
CHECK_ERR(write(fd, keepstr, bytes));
written += bytes;
}
@ -1048,22 +1001,25 @@ static void TestOneTruncate(const char* path, uint64 limit, uint64 keep,
// File should now be shorter
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_ERR(lseek(fd.get(), 0, SEEK_SET));
CHECK_ERR(lseek(fd, 0, SEEK_SET));
// File should contain the suffix of the original file
const size_t buf_size = static_cast<size_t>(statbuf.st_size) + 1;
std::vector<char> buf(buf_size);
CHECK_ERR(read(fd.get(), buf.data(), buf_size));
char* buf = new char[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;
while (checked < expect) {
size_t bytes = min(expect - checked, keep_size);
CHECK(!memcmp(p, keepstr, bytes));
checked += bytes;
}
close(fd);
delete[] buf;
}
static void TestTruncate() {
@ -1199,11 +1155,13 @@ static void TestLogPeriodically() {
}
namespace google {
inline namespace glog_internal_namespace_ {
// in logging.cc
extern bool SafeFNMatch_(const char* pattern, size_t patt_len, const char* str,
size_t str_len);
namespace glog_internal_namespace_ {
extern // in logging.cc
bool
SafeFNMatch_(const char* pattern, size_t patt_len, const char* str,
size_t str_len);
} // namespace glog_internal_namespace_
using glog_internal_namespace_::SafeFNMatch_;
} // namespace google
static bool WrapSafeFNMatch(string pattern, string str) {
@ -1394,26 +1352,28 @@ static void TestLogSinkWaitTillSent() {
TEST(Strerror, logging) {
int errcode = EINTR;
std::string msg = strerror(errcode);
const size_t buf_size = msg.size() + 1;
std::vector<char> buf(buf_size);
char* msg = strdup(strerror(errcode));
const size_t buf_size = strlen(msg) + 1;
char* buf = new char[buf_size];
CHECK_EQ(posix_strerror_r(errcode, nullptr, 0), -1);
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(posix_strerror_r(errcode, nullptr, buf_size), -1);
#if defined(GLOG_OS_MACOSX) || defined(GLOG_OS_FREEBSD) || \
defined(GLOG_OS_OPENBSD)
// MacOSX or FreeBSD considers this case is an error since there is
// no enough space.
CHECK_EQ(posix_strerror_r(errcode, buf.data(), 1), -1);
CHECK_EQ(posix_strerror_r(errcode, buf, 1), -1);
#else
CHECK_EQ(posix_strerror_r(errcode, buf.data(), 1), 0);
CHECK_EQ(posix_strerror_r(errcode, buf, 1), 0);
#endif
CHECK_STREQ(buf.data(), "");
CHECK_EQ(posix_strerror_r(errcode, buf.data(), buf_size), 0);
CHECK_STREQ(buf.data(), msg.c_str());
CHECK_STREQ(buf, "");
CHECK_EQ(posix_strerror_r(errcode, buf, buf_size), 0);
CHECK_STREQ(buf, msg);
delete[] buf;
CHECK_EQ(msg, StrError(errcode));
free(msg);
}
// 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__);
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
using namespace std::chrono_literals;
constexpr std::chrono::hours utc_min_offset = -12h;
constexpr std::chrono::hours utc_max_offset = +14h;
EXPECT_TRUE((gmtoff >= utc_min_offset) && (gmtoff <= utc_max_offset));
const std::chrono::hours utc_min_offset = -12h;
const std::chrono::hours utc_max_offset = 14h;
EXPECT_TRUE((nGmtOff >= utc_min_offset) && (nGmtOff <= utc_max_offset));
}
TEST(EmailLogging, ValidAddress) {
@ -1615,17 +1575,3 @@ TEST(EmailLogging, MaliciousAddress) {
EXPECT_FALSE(
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);
}

View File

@ -31,20 +31,32 @@
//
// logging_unittest.cc covers the functionality herein
#include <atomic>
#include <cstdarg>
#include <cstdio>
#include <cstring>
#include <iomanip>
#include <mutex>
#include <ostream>
#include <streambuf>
#include <thread>
#include "config.h"
#include "utilities.h"
#ifdef HAVE_UNISTD_H
# include <unistd.h> // for close() and write()
#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)
# include <syscall.h> // for syscall()
#elif defined(HAVE_SYS_SYSCALL_H)
@ -53,12 +65,6 @@
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#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)) && \
(!(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 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'
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
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);
// 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
// libc (to side-step any libc interception).
// 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) {
std::call_once(crashed, [file, line, msg_start, msg_size] {
crash_reason.filename = file;

View File

@ -33,15 +33,12 @@
#include <algorithm>
#include <csignal>
#include <cstring>
#include <ctime>
#include <mutex>
#include <sstream>
#include <thread>
#include "config.h"
#include "glog/logging.h"
#include "glog/platform.h"
#include "stacktrace.h"
#include "symbolize.h"
#include "utilities.h"
@ -55,10 +52,6 @@
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#if defined(HAVE_SYS_SYSCALL_H) && defined(HAVE_SYS_TYPES_H)
# include <sys/syscall.h>
# include <sys/types.h>
#endif
namespace google {
@ -162,7 +155,7 @@ class MinimalFormatter {
// Writes the given data with the size to the standard error.
void WriteToStderr(const char* data, size_t size) {
if (write(fileno(stderr), data, size) < 0) {
if (write(STDERR_FILENO, data, size) < 0) {
// Ignore errors.
}
}
@ -220,14 +213,8 @@ void DumpSignalInfo(int signal_number, siginfo_t* siginfo) {
std::ostringstream oss;
oss << std::showbase << std::hex << std::this_thread::get_id();
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.
# ifdef GLOG_OS_LINUX
formatter.AppendString("from PID ");
@ -244,7 +231,6 @@ void DumpSignalInfo(int signal_number, siginfo_t* siginfo) {
void DumpStackFrameInfo(const char* prefix, void* pc) {
// Get the symbol name.
const char* symbol = "(unknown)";
#if defined(HAVE_SYMBOLIZE)
char symbolized[1024]; // Big enough for a sane symbol.
// Symbolizes the previous address of pc because pc may be in the
// next function.
@ -252,10 +238,6 @@ void DumpStackFrameInfo(const char* prefix, void* pc) {
sizeof(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.
MinimalFormatter formatter(buf, sizeof(buf));

View File

@ -39,21 +39,13 @@
#include <string>
#include <thread>
#include "config.h"
#include "glog/logging.h"
#include "stacktrace.h"
#include "symbolize.h"
#include "utilities.h"
#if defined(HAVE_UNISTD_H)
# include <unistd.h>
#endif
#ifdef GLOG_USE_GFLAGS
# include <gflags/gflags.h>
using namespace GFLAGS_NAMESPACE;
#endif
#if defined(_MSC_VER)
# include <io.h> // write
#endif
using namespace google;
@ -67,7 +59,7 @@ static void DieInThread(int* a) {
}
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.
}
}

View File

@ -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

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024, Google Inc.
// Copyright (c) 2000 - 2007, Google Inc.
// All rights reserved.
//
// 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
// thread-safe.
#ifndef GLOG_INTERNAL_STACKTRACE_H
#define GLOG_INTERNAL_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
#ifndef BASE_STACKTRACE_H_
#define BASE_STACKTRACE_H_
#include "config.h"
#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
#include "glog/logging.h"
namespace google {
inline namespace glog_internal_namespace_ {
#if defined(HAVE_STACKTRACE)
// This is similar to the GetStackFrames routine, except that it returns
// 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.
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
#endif // GLOG_INTERNAL_STACKTRACE_H
#endif // BASE_STACKTRACE_H_

View File

@ -38,7 +38,6 @@
#include "stacktrace.h"
namespace google {
inline namespace glog_internal_namespace_ {
// If you change this function, also change GetStackFrames below.
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;
}
} // namespace glog_internal_namespace_
} // namespace google

View File

@ -41,7 +41,6 @@ extern "C" {
#include "stacktrace.h"
namespace google {
inline namespace glog_internal_namespace_ {
// Sometimes, we can try to get a stack trace from within a stack
// 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;
}
} // namespace glog_internal_namespace_
} // namespace google

View File

@ -41,7 +41,6 @@
#include "stacktrace.h"
namespace google {
inline namespace glog_internal_namespace_ {
// Given a pointer to a stack frame, locate and return the calling
// 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;
}
} // namespace glog_internal_namespace_
} // namespace google

View File

@ -41,6 +41,8 @@
# include <execinfo.h>
#endif
using namespace google;
#ifdef HAVE_STACKTRACE
// 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]);
INIT_ADDRESS_RANGE(CheckStackTraceLeaf, start, end, &expected_range[0]);
DECLARE_ADDRESS_LABEL(start);
size = google::GetStackTrace(stack, STACK_LEN, 0);
size = GetStackTrace(stack, STACK_LEN, 0);
printf("Obtained %d stack frames.\n", size);
CHECK_GE(size, 1);
CHECK_LE(size, STACK_LEN);
@ -229,7 +231,7 @@ static
int main(int, char** argv) {
FLAGS_logtostderr = true;
google::InitGoogleLogging(argv[0]);
InitGoogleLogging(argv[0]);
CheckStackTrace(0);

View File

@ -36,7 +36,6 @@
#include "stacktrace.h"
namespace google {
inline namespace glog_internal_namespace_ {
struct trace_arg_t {
void** result;
@ -102,5 +101,4 @@ int GetStackTrace(void** result, int max_depth, int skip_count) {
return targ.count;
}
} // namespace glog_internal_namespace_
} // namespace google

View File

@ -31,13 +31,15 @@
//
// Windows implementation - just use CaptureStackBackTrace
// clang-format off
#include <windows.h> // Must come before <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 {
inline namespace glog_internal_namespace_ {
int GetStackTrace(void** result, int max_depth, int skip_count) {
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);
}
} // namespace glog_internal_namespace_
} // namespace google

View File

@ -43,7 +43,6 @@
#include "stacktrace.h"
namespace google {
inline namespace glog_internal_namespace_ {
// Given a pointer to a stack frame, locate and return the calling
// 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;
}
} // namespace glog_internal_namespace_
} // namespace google

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024, Google Inc.
// Copyright (c) 2023, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@ -52,68 +52,71 @@
# include GLOG_BUILD_CONFIG_INCLUDE
#endif // GLOG_BUILD_CONFIG_INCLUDE
#include "symbolize.h"
#include "utilities.h"
#if defined(HAVE_SYMBOLIZE)
# include <algorithm>
# include <cstdlib>
# include <cstring>
# include <limits>
# include "demangle.h"
# include "symbolize.h"
namespace google {
// We don't use assert() since it's not guaranteed to be
// async-signal-safe. Instead we define a minimal assertion
// macro. So far, we don't need pretty printing for __FILE__, etc.
# define GLOG_SAFE_ASSERT(expr) ((expr) ? 0 : (std::abort(), 0))
namespace google {
inline namespace glog_internal_namespace_ {
namespace {
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);
}
}
// A wrapper for abort() to make it callable in ? :.
static int AssertFail() {
abort();
return 0; // Should not reach.
}
} // namespace
# define SAFE_ASSERT(expr) ((expr) ? 0 : AssertFail())
static SymbolizeCallback g_symbolize_callback = nullptr;
void InstallSymbolizeCallback(SymbolizeCallback callback) {
g_symbolize_callback = callback;
}
static SymbolizeOpenObjectFileCallback g_symbolize_open_object_file_callback =
nullptr;
void InstallSymbolizeOpenObjectFileCallback(
SymbolizeOpenObjectFileCallback 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
# if defined(HAVE_LINK_H)
# if defined(__ELF__)
# if defined(HAVE_DLFCN_H)
# include <dlfcn.h>
# endif
# if defined(GLOG_OS_OPENBSD)
# include <sys/exec_elf.h>
# else
# include <elf.h>
# endif
# include <fcntl.h>
# include <sys/stat.h>
# include <sys/types.h>
@ -131,24 +134,12 @@ void InstallSymbolizeOpenObjectFileCallback(
# include "glog/raw_logging.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 {
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
// 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.
static ssize_t ReadFromOffset(const int fd, void* buf, const size_t count,
const size_t offset) {
GLOG_SAFE_ASSERT(fd >= 0);
GLOG_SAFE_ASSERT(count <=
static_cast<size_t>(std::numeric_limits<ssize_t>::max()));
SAFE_ASSERT(fd >= 0);
SAFE_ASSERT(count <=
static_cast<size_t>(std::numeric_limits<ssize_t>::max()));
char* buf0 = reinterpret_cast<char*>(buf);
size_t num_bytes = 0;
while (num_bytes < count) {
ssize_t len = FailureRetry([fd, p = buf0 + num_bytes, n = count - num_bytes,
m = static_cast<off_t>(offset + num_bytes)] {
return pread(fd, p, n, m);
});
ssize_t len;
NO_INTR(len = pread(fd, buf0 + num_bytes, count - num_bytes,
static_cast<off_t>(offset + num_bytes)));
if (len < 0) { // There was an error other than EINTR.
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);
}
GLOG_SAFE_ASSERT(num_bytes <= count);
SAFE_ASSERT(num_bytes <= count);
return static_cast<ssize_t>(num_bytes);
}
@ -221,9 +211,9 @@ static ATTRIBUTE_NOINLINE bool GetSectionHeaderByType(const int fd,
if (len == -1) {
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]);
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) {
if (buf[j].sh_type == type) {
*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);
const ssize_t len =
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]);
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) {
const ElfW(Sym)& symbol = buf[j];
uint64_t start_address = symbol.st_value;
@ -386,6 +376,22 @@ static bool GetSymbolFromObjectFile(const int fd, uint64_t pc, char* out,
}
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.
//
@ -418,8 +424,8 @@ class LineReader {
eod_ = buf_ + num_bytes;
bol_ = buf_;
} else {
bol_ = eol_ + 1; // Advance to the next line in the buffer.
GLOG_SAFE_ASSERT(bol_ <= eod_); // "bol_" can point to "eod_".
bol_ = eol_ + 1; // Advance to the next line in the buffer.
SAFE_ASSERT(bol_ <= eod_); // "bol_" can point to "eod_".
if (!HasCompleteLine()) {
const auto incomplete_line_length = static_cast<size_t>(eod_ - bol_);
// 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;
}
}
GLOG_SAFE_ASSERT(p <= end);
SAFE_ASSERT(p <= end);
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,
// returns -1. |out_file_name_size| is the size of the file name buffer
// (including the null-terminator).
static ATTRIBUTE_NOINLINE FileDescriptor
OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc,
uint64_t& start_address,
uint64_t& base_address,
char* out_file_name,
size_t out_file_name_size) {
FileDescriptor maps_fd{
FailureRetry([] { return open("/proc/self/maps", O_RDONLY); })};
if (!maps_fd) {
return nullptr;
static ATTRIBUTE_NOINLINE int OpenObjectFileContainingPcAndGetStartAddress(
uint64_t pc, uint64_t& start_address, uint64_t& base_address,
char* out_file_name, size_t out_file_name_size) {
int object_fd;
int maps_fd;
NO_INTR(maps_fd = open("/proc/self/maps", O_RDONLY));
FileDescriptor wrapped_maps_fd(maps_fd);
if (wrapped_maps_fd.get() < 0) {
return -1;
}
FileDescriptor mem_fd{
FailureRetry([] { return open("/proc/self/mem", O_RDONLY); })};
if (!mem_fd) {
return nullptr;
int mem_fd;
NO_INTR(mem_fd = open("/proc/self/mem", O_RDONLY));
FileDescriptor wrapped_mem_fd(mem_fd);
if (wrapped_mem_fd.get() < 0) {
return -1;
}
// Iterate over maps and look for the map containing the pc. Then
// look into the symbol tables inside.
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) {
const char* cursor;
const char* eol;
if (!reader.ReadLine(&cursor, &eol)) { // EOF or malformed line.
return nullptr;
return -1;
}
// Start parsing line in /proc/self/maps. Here is an example:
@ -545,7 +552,7 @@ OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc,
// Read start address.
cursor = GetHex(cursor, eol, &start_address);
if (cursor == eol || *cursor != '-') {
return nullptr; // Malformed line.
return -1; // Malformed line.
}
++cursor; // Skip '-'.
@ -553,7 +560,7 @@ OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc,
uint64_t end_address;
cursor = GetHex(cursor, eol, &end_address);
if (cursor == eol || *cursor != ' ') {
return nullptr; // Malformed line.
return -1; // Malformed line.
}
++cursor; // Skip ' '.
@ -564,15 +571,14 @@ OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc,
}
// We expect at least four letters for flags (ex. "r-xp").
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.
ElfW(Ehdr) ehdr;
// Skip non-readable maps.
if (flags_start[0] == 'r' &&
ReadFromOffsetExact(mem_fd.get(), &ehdr, sizeof(ElfW(Ehdr)),
start_address) &&
ReadFromOffsetExact(mem_fd, &ehdr, sizeof(ElfW(Ehdr)), start_address) &&
memcmp(ehdr.e_ident, ELFMAG, SELFMAG) == 0) {
switch (ehdr.e_type) {
case ET_EXEC:
@ -591,7 +597,7 @@ OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc,
for (unsigned i = 0; i != ehdr.e_phnum; ++i) {
ElfW(Phdr) phdr;
if (ReadFromOffsetExact(
mem_fd.get(), &phdr, sizeof(phdr),
mem_fd, &phdr, sizeof(phdr),
start_address + ehdr.e_phoff + i * sizeof(phdr)) &&
phdr.p_type == PT_LOAD && phdr.p_offset == 0) {
base_address = start_address - phdr.p_vaddr;
@ -607,7 +613,7 @@ OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc,
}
// 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.
}
@ -621,7 +627,7 @@ OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc,
uint64_t file_offset;
cursor = GetHex(cursor, eol, &file_offset);
if (cursor == eol || *cursor != ' ') {
return nullptr; // Malformed line.
return -1; // Malformed line.
}
++cursor; // Skip ' '.
@ -639,16 +645,20 @@ OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc,
++cursor;
}
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.
return FileDescriptor{
FailureRetry([cursor] { return open(cursor, O_RDONLY); })};
NO_INTR(object_fd = 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.
static void SafeAppendString(const char* source, char* dest, size_t dest_size) {
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_size -= dest_string_length;
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.
// To keep stack consumption low, we would like this function to not
// get inlined.
static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(
void* pc, char* out, size_t out_size, SymbolizeOptions /*options*/) {
static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void* pc, char* out,
size_t out_size) {
auto pc0 = reinterpret_cast<uintptr_t>(pc);
uint64_t start_address = 0;
uint64_t base_address = 0;
FileDescriptor object_fd;
int object_fd = -1;
if (out_size < 1) {
return false;
@ -751,18 +761,20 @@ static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(
SafeAppendString("(", out, out_size);
if (g_symbolize_open_object_file_callback) {
object_fd.reset(g_symbolize_open_object_file_callback(
pc0, start_address, base_address, out + 1, out_size - 1));
object_fd = g_symbolize_open_object_file_callback(
pc0, start_address, base_address, out + 1, out_size - 1);
} else {
object_fd = OpenObjectFileContainingPcAndGetStartAddress(
pc0, start_address, base_address, out + 1, out_size - 1);
}
FileDescriptor wrapped_object_fd(object_fd);
# if defined(PRINT_UNSYMBOLIZED_STACK_TRACES)
{
# else
// Check whether a file name was returned.
if (!object_fd) {
if (object_fd < 0) {
# endif
if (out[1]) {
// 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.
return false;
}
int elf_type = FileGetElfType(object_fd.get());
int elf_type = FileGetElfType(wrapped_object_fd.get());
if (elf_type == -1) {
return false;
}
@ -787,14 +799,14 @@ static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(
// Note: relocation (and much of the rest of this code) will be
// wrong for prelinked shared libraries and PIE executables.
uint64_t relocation = (elf_type == ET_DYN) ? start_address : 0;
int num_bytes_written =
g_symbolize_callback(object_fd.get(), pc, out, out_size, relocation);
int num_bytes_written = g_symbolize_callback(wrapped_object_fd.get(), pc,
out, out_size, relocation);
if (num_bytes_written > 0) {
out += 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)) {
if (out[1] && !g_symbolize_callback) {
// The object file containing PC was opened successfully however the
@ -815,7 +827,6 @@ static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(
return true;
}
} // namespace glog_internal_namespace_
} // namespace google
# elif defined(GLOG_OS_MACOSX) && defined(HAVE_DLADDR)
@ -825,10 +836,9 @@ static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(
# include <cstring>
namespace google {
inline namespace glog_internal_namespace_ {
static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(
void* pc, char* out, size_t out_size, SymbolizeOptions /*options*/) {
static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void* pc, char* out,
size_t out_size) {
Dl_info info;
if (dladdr(pc, &info)) {
if (info.dli_sname) {
@ -843,7 +853,6 @@ static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(
return false;
}
} // namespace glog_internal_namespace_
} // namespace google
# elif defined(GLOG_OS_WINDOWS) || defined(GLOG_OS_CYGWIN)
@ -851,22 +860,24 @@ static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(
# include <dbghelp.h>
# include <windows.h>
# ifdef _MSC_VER
# pragma comment(lib, "dbghelp")
# endif
namespace google {
inline namespace glog_internal_namespace_ {
namespace {
class SymInitializer final {
class SymInitializer {
public:
HANDLE process;
bool ready;
SymInitializer() : process(GetCurrentProcess()), ready(false) {
SymInitializer() : process(nullptr), ready(false) {
// Initialize the symbol handler.
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms680344(v=vs.85).aspx
process = GetCurrentProcess();
// Defer symbol loading.
// We do not request undecorated symbols with SYMOPT_UNDNAME
// because the mangling library calls UnDecorateSymbolName.
SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES);
SymSetOptions(SYMOPT_DEFERRED_LOADS);
if (SymInitialize(process, nullptr, true)) {
ready = true;
}
@ -876,17 +887,13 @@ class SymInitializer final {
// We do not need to close `HANDLE process` because it's a "pseudo handle."
}
SymInitializer(const SymInitializer&) = delete;
SymInitializer& operator=(const SymInitializer&) = delete;
SymInitializer(SymInitializer&&) = delete;
SymInitializer& operator=(SymInitializer&&) = delete;
private:
SymInitializer(const SymInitializer&);
SymInitializer& operator=(const SymInitializer&);
};
} // namespace
static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void* pc, char* out,
size_t out_size,
SymbolizeOptions options) {
size_t out_size) {
const static SymInitializer symInitializer;
if (!symInitializer.ready) {
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.
BOOL ret = SymFromAddr(symInitializer.process, reinterpret_cast<DWORD64>(pc),
0, symbol);
std::size_t namelen = static_cast<size_t>(symbol->NameLen);
if (ret && namelen < out_size) {
std::strncpy(out, symbol->Name, namelen);
out[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);
}
if (ret == 1 && static_cast<ssize_t>(symbol->NameLen) < out_size) {
// `NameLen` does not include the null terminating character.
strncpy(out, symbol->Name, static_cast<size_t>(symbol->NameLen) + 1);
out[static_cast<size_t>(symbol->NameLen)] = '\0';
// Symbolization succeeded. Now we try to demangle the symbol.
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 false;
}
} // namespace glog_internal_namespace_
} // namespace google
# else
@ -951,13 +926,27 @@ static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void* pc, char* out,
# endif
namespace google {
inline namespace glog_internal_namespace_ {
bool Symbolize(void* pc, char* out, size_t out_size, SymbolizeOptions options) {
return SymbolizeAndDemangle(pc, out, out_size, options);
bool Symbolize(void* pc, char* out, size_t out_size) {
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
#endif

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024, Google Inc.
// Copyright (c) 2006, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@ -51,58 +51,35 @@
// malloc() and other unsafe operations. It should be both
// thread-safe and async-signal-safe.
#ifndef GLOG_INTERNAL_SYMBOLIZE_H
#define GLOG_INTERNAL_SYMBOLIZE_H
#include <cstddef>
#include <cstdint>
#include <type_traits>
#ifndef BASE_SYMBOLIZE_H_
#define BASE_SYMBOLIZE_H_
#include "config.h"
#include "glog/platform.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)
#include "glog/logging.h"
#include "utilities.h"
#ifdef HAVE_SYMBOLIZE
# if !defined(SIZEOF_VOID_P) && defined(__SIZEOF_POINTER__)
# define SIZEOF_VOID_P __SIZEOF_POINTER__
# endif
# if defined(__ELF__) // defined by gcc
# if defined(__OpenBSD__)
# 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.
# ifndef ElfW
@ -116,21 +93,17 @@
# endif
namespace google {
inline namespace glog_internal_namespace_ {
// Gets the section header for the given name, if it exists. Returns true on
// success. Otherwise, returns false.
GLOG_NO_EXPORT
bool GetSectionHeaderByName(int fd, const char* name, size_t name_len,
ElfW(Shdr) * out);
} // namespace glog_internal_namespace_
} // namespace google
# endif
# endif /* __ELF__ */
namespace google {
inline namespace glog_internal_namespace_ {
// Restrictions on the callbacks that follow:
// - 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
// function should return -1.
using SymbolizeCallback = int (*)(int, void*, char*, size_t, uint64_t);
GLOG_NO_EXPORT
GLOG_EXPORT
void InstallSymbolizeCallback(SymbolizeCallback callback);
// Installs a callback function, which will be called instead of
@ -159,52 +132,21 @@ void InstallSymbolizeCallback(SymbolizeCallback callback);
// (including the null-terminator).
using SymbolizeOpenObjectFileCallback = int (*)(uint64_t, uint64_t&, uint64_t&,
char*, size_t);
GLOG_NO_EXPORT
void InstallSymbolizeOpenObjectFileCallback(
SymbolizeOpenObjectFileCallback callback);
} // namespace glog_internal_namespace_
} // namespace google
#endif
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
// symbol name to "out". The symbol name is demangled if possible
// (supports symbols generated by GCC 3.x or newer). Otherwise,
// returns false.
GLOG_NO_EXPORT bool Symbolize(
void* pc, char* out, size_t out_size,
SymbolizeOptions options = SymbolizeOptions::kNone);
GLOG_EXPORT bool Symbolize(void* pc, char* out, size_t out_size);
#endif // defined(HAVE_SYMBOLIZE)
} // namespace glog_internal_namespace_
} // namespace google
#endif // GLOG_INTERNAL_SYMBOLIZE_H
#endif // BASE_SYMBOLIZE_H_

View File

@ -40,7 +40,6 @@
#include "glog/logging.h"
#include "googletest.h"
#include "utilities.h"
#include "stacktrace.h"
#ifdef GLOG_USE_GFLAGS
# include <gflags/gflags.h>
@ -61,13 +60,11 @@ using namespace google;
# define always_inline
# if defined(HAVE_ELF_H) || defined(HAVE_SYS_EXEC_ELF_H) || \
defined(GLOG_OS_WINDOWS) || defined(GLOG_OS_CYGWIN)
# if defined(__ELF__) || defined(GLOG_OS_WINDOWS) || defined(GLOG_OS_CYGWIN)
// A wrapper function for Symbolize() to make the unit test simple.
static const char* TrySymbolize(void* pc, google::SymbolizeOptions options =
google::SymbolizeOptions::kNone) {
static const char* TrySymbolize(void* pc) {
static char symbol[4096];
if (Symbolize(pc, symbol, sizeof(symbol), options)) {
if (Symbolize(pc, symbol, sizeof(symbol))) {
return symbol;
} else {
return nullptr;
@ -75,7 +72,8 @@ static const char* TrySymbolize(void* pc, google::SymbolizeOptions options =
}
# endif
# if defined(HAVE_ELF_H) || defined(HAVE_SYS_EXEC_ELF_H)
# if defined(__ELF__)
// This unit tests make sense only with GCC.
// Uses lots of GCC specific features.
# if defined(__GNUC__) && !defined(__OPENCC__)
@ -396,8 +394,7 @@ static void ATTRIBUTE_NOINLINE TestWithPCInsideInlineFunction() {
static void ATTRIBUTE_NOINLINE TestWithReturnAddress() {
# if defined(HAVE_ATTRIBUTE_NOINLINE)
void* return_address = __builtin_return_address(0);
const char* symbol =
TrySymbolize(return_address, google::SymbolizeOptions::kNoLineNumbers);
const char* symbol = TrySymbolize(return_address);
# if !defined(_MSC_VER) || !defined(NDEBUG)
CHECK(symbol != nullptr);
@ -442,23 +439,22 @@ __declspec(noinline) void TestWithReturnAddress() {
_ReturnAddress()
# endif
;
const char* symbol =
TrySymbolize(return_address, google::SymbolizeOptions::kNoLineNumbers);
const char* symbol = TrySymbolize(return_address);
# if !defined(_MSC_VER) || !defined(NDEBUG)
CHECK(symbol != nullptr);
CHECK_STREQ(symbol, "main");
# endif
cout << "Test case TestWithReturnAddress passed." << endl;
}
# endif
#endif // HAVE_STACKTRACE
# endif // __ELF__
#endif // HAVE_STACKTRACE
int main(int argc, char** argv) {
FLAGS_logtostderr = true;
InitGoogleLogging(argv[0]);
InitGoogleTest(&argc, argv);
#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
// used to install some callback function at InitGoogle() time.
InstallSymbolizeCallback(nullptr);
@ -473,7 +469,7 @@ int main(int argc, char** argv) {
# else // GLOG_OS_WINDOWS
printf("PASS (no symbolize_unittest support)\n");
return 0;
# endif // defined(HAVE_ELF_H) || defined(HAVE_SYS_EXEC_ELF_H)
# endif // __ELF__
#else
printf("PASS (no symbolize support)\n");
return 0;

View File

@ -29,26 +29,17 @@
//
// Author: Shinichiro Hamaji
#define _GNU_SOURCE 1
#include "utilities.h"
#include <atomic>
#include <cerrno>
#include <csignal>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include "base/googleinit.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
# include <sys/time.h>
#endif
@ -66,9 +57,8 @@
#ifdef HAVE_PWD_H
# include <pwd.h>
#endif
#if defined(HAVE___PROGNAME)
extern char* __progname;
#ifdef __ANDROID__
# include <android/log.h>
#endif
using std::string;
@ -81,35 +71,6 @@ bool IsGoogleLoggingInitialized() {
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
// 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*) {
// This one is signal-safe.
if (write(fileno(stderr), data, strlen(data)) < 0) {
if (write(STDERR_FILENO, data, strlen(data)) < 0) {
// Ignore errors.
}
AlsoErrorWrite(GLOG_FATAL,
glog_internal_namespace_::ProgramInvocationShortName(), data);
# if defined(__ANDROID__)
// 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) {
@ -185,9 +150,7 @@ static void DumpStackTrace(int skip_count, DebugWriter* writerfn, void* arg) {
}
}
# ifdef __GNUC__
__attribute__((noreturn))
# endif
GLOG_NORETURN
static void
DumpStackTraceAndExit() {
DumpStackTrace(1, DebugWriteToStderr, nullptr);
@ -216,31 +179,15 @@ DumpStackTraceAndExit() {
namespace google {
inline 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;
}
namespace glog_internal_namespace_ {
const char* ProgramInvocationShortName() {
if (g_program_invocation_short_name != nullptr) {
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();
@ -255,6 +202,14 @@ bool PidHasChanged() {
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;
const string& MyUserName() { return g_my_user_name; }
static void MyUserNameInitializer() {
@ -287,19 +242,29 @@ static void 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
// 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) {
const logging::internal::CrashReason* expected = nullptr;
void SetCrashReason(const CrashReason* r) {
const CrashReason* expected = nullptr;
g_reason.compare_exchange_strong(expected, r);
}
void InitGoogleLoggingUtilities(const char* argv0) {
CHECK(!IsGoogleLoggingInitialized())
<< "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
InstallFailureFunction(&DumpStackTraceAndExit);
@ -318,12 +283,17 @@ void ShutdownGoogleLoggingUtilities() {
} // namespace glog_internal_namespace_
#ifdef HAVE_STACKTRACE
std::string GetStackTrace() {
std::string stacktrace;
DumpStackTrace(1, DebugWriteToString, &stacktrace);
return stacktrace;
}
#endif
} // 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

View File

@ -32,15 +32,8 @@
//
// Define utilities for glog internal usage.
#ifndef GLOG_INTERNAL_UTILITIES_H
#define GLOG_INTERNAL_UTILITIES_H
#include <cstddef>
#include <cstdio>
#include <memory>
#include <string>
#include <type_traits>
#include <utility>
#ifndef UTILITIES_H__
#define UTILITIES_H__
// printf macros for size_t, in the style of inttypes.h
#ifdef _LP64
@ -60,14 +53,22 @@
#define PRIXS __PRIS_PREFIX "X"
#define PRIoS __PRIS_PREFIX "o"
#include "config.h"
#include "glog/platform.h"
#include <string>
#include <thread>
#include <type_traits>
#include "glog/logging.h"
#if defined(GLOG_USE_WINDOWS_PORT)
# include "port.h"
#endif
#include "config.h"
#if defined(HAVE_UNISTD_H)
# include <unistd.h>
#endif
#if !defined(HAVE_SSIZE_T)
# if defined(GLOG_OS_WINDOWS)
# include <basetsd.h>
@ -77,9 +78,6 @@ using ssize_t = std::ptrdiff_t;
# endif
#endif
#include "glog/log_severity.h"
#include "glog/types.h"
// 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
@ -102,6 +100,43 @@ using ssize_t = std::ptrdiff_t;
// correctly when GetStackTrace() is called with max_depth == 0.
// 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
// There is a better way, but this is good enough for our purpose.
# define ARRAYSIZE(a) (sizeof(a) / sizeof(*(a)))
@ -109,26 +144,7 @@ using ssize_t = std::ptrdiff_t;
namespace google {
namespace logging {
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_ {
namespace glog_internal_namespace_ {
#if defined(__has_attribute)
# if __has_attribute(noinline)
@ -148,9 +164,6 @@ inline namespace glog_internal_namespace_ {
# define ATTRIBUTE_NOINLINE
#endif
void AlsoErrorWrite(LogSeverity severity, const char* tag,
const char* message) noexcept;
const char* ProgramInvocationShortName();
int32 GetMainThreadPid();
@ -162,7 +175,22 @@ const std::string& MyUserName();
// (Doesn't modify filepath, contrary to basename() in libgen.h.)
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 ShutdownGoogleLoggingUtilities();
@ -185,99 +213,10 @@ class ScopedExit final {
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 google
template <>
struct std::default_delete<std::FILE> {
void operator()(FILE* p) const noexcept { fclose(p); }
};
using namespace google::glog_internal_namespace_;
#endif // GLOG_INTERNAL_UTILITIES_H
#endif // UTILITIES_H__

View File

@ -1,4 +1,4 @@
// Copyright (c) 2024, Google Inc.
// Copyright (c) 1999, 2007, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@ -39,7 +39,9 @@
#include <mutex>
#include <string>
#include "glog/logging.h"
#include "glog/raw_logging.h"
#include "utilities.h"
// glog doesn't have annotation
#define ANNOTATE_BENIGN_RACE(address, description)
@ -48,14 +50,18 @@ using std::string;
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
// of arguments and does not allocate any memory,
// but we only support "*" and "?" wildcards, not the "[...]" patterns.
// It's not a static function for the unittest.
GLOG_NO_EXPORT bool SafeFNMatch_(const char* pattern, size_t patt_len,
const char* str, size_t str_len) {
GLOG_EXPORT bool SafeFNMatch_(const char* pattern, size_t patt_len,
const char* str, size_t str_len) {
size_t p = 0;
size_t s = 0;
while (true) {

View File

@ -6,8 +6,8 @@
* under the MIT license. For all details and documentation, see
* https://github.com/tronkko/dirent
*/
#ifndef GLOG_INTERNAL_WINDOWS_DIRENT_H
#define GLOG_INTERNAL_WINDOWS_DIRENT_H
#ifndef DIRENT_H
#define DIRENT_H
/* Hide warnings about unreferenced local functions */
#if defined(__clang__)
@ -35,7 +35,6 @@
#include <cstdlib>
#include <cstring>
#include <cwchar>
#include <memory>
/* Indicates that d_type field is available in dirent structure */
#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.
*/
static DIR* opendir(const char* dirname) {
struct DIR* dirp;
/* Must have directory name */
if (dirname == nullptr || dirname[0] == '\0') {
dirent_set_errno(ENOENT);
@ -630,9 +631,7 @@ static DIR* opendir(const char* dirname) {
}
/* Allocate memory for DIR structure */
std::unique_ptr<DIR, decltype(&free)> dirp{
static_cast<DIR*>(malloc(sizeof(struct DIR))), &free};
dirp = (DIR*)malloc(sizeof(struct DIR));
if (!dirp) {
return nullptr;
}
@ -650,18 +649,23 @@ static DIR* opendir(const char* dirname) {
* the output buffer is too small to contain the resulting
* string.
*/
return nullptr;
goto exit_free;
}
/* Open directory stream using wide-character name */
dirp->wdirp = _wopendir(wname);
if (!dirp->wdirp) {
return nullptr;
goto exit_free;
}
}
/* 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
}
#endif
#endif /*GLOG_INTERNAL_WINDOWS_DIRENT_H*/
#endif /*DIRENT_H*/

View File

@ -42,9 +42,6 @@
#include "config.h"
namespace google {
inline namespace glog_internal_namespace_ {
#ifndef HAVE_LOCALTIME_R
struct tm* localtime_r(const std::time_t* timep, std::tm* result) {
localtime_s(result, timep);
@ -57,6 +54,3 @@ struct tm* gmtime_r(const std::time_t* timep, std::tm* result) {
return result;
}
#endif // not HAVE_GMTIME_R
} // namespace glog_internal_namespace_
} // namespace google

View File

@ -43,14 +43,6 @@
#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
# ifndef WIN32_LEAN_AND_MEAN
@ -70,6 +62,8 @@
* used by both C and C++ code, so we put all the C++ together.
*/
# include "glog/logging.h"
# ifdef _MSC_VER
/* 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_EXCL _O_EXCL
# ifndef __MINGW32__
enum { STDIN_FILENO = 0, STDOUT_FILENO = 1, STDERR_FILENO = 2 };
# endif
# define S_IRUSR S_IREAD
# define S_IWUSR S_IWRITE
@ -119,23 +116,24 @@
# endif // _MSC_VER
namespace google {
inline namespace glog_internal_namespace_ {
# 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
# 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
GLOG_NO_EXPORT
inline char* strerror_r(int errnum, char* buf, std::size_t buflen) {
strerror_s(buf, buflen, errnum);
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 */