diff --git a/3rdparty/glog b/3rdparty/glog index 523d98170..d980f45ad 120000 --- a/3rdparty/glog +++ b/3rdparty/glog @@ -1 +1 @@ -glog-0.6.0 \ No newline at end of file +glog-0.7.0 \ No newline at end of file diff --git a/3rdparty/glog-0.7.0/.bazelci/presubmit.yml b/3rdparty/glog-0.7.0/.bazelci/presubmit.yml new file mode 100644 index 000000000..87bd2a042 --- /dev/null +++ b/3rdparty/glog-0.7.0/.bazelci/presubmit.yml @@ -0,0 +1,62 @@ +--- +tasks: + ubuntu1804: + name: "Ubuntu 22.04" + platform: ubuntu2204 + build_flags: + - "--features=layering_check" + - "--copt=-Werror" + build_targets: + - "//..." + test_flags: + - "--features=layering_check" + - "--copt=-Werror" + test_targets: + - "//..." + macos: + name: "macOS: latest Xcode" + platform: macos + build_flags: + - "--features=layering_check" + - "--copt=-Werror" + build_targets: + - "//..." + test_flags: + - "--features=layering_check" + - "--copt=-Werror" + test_targets: + - "//..." + windows-msvc: + name: "Windows: MSVC 2017" + platform: windows + environment: + BAZEL_VC: "C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\BuildTools\\VC" + build_flags: + - "--features=layering_check" + - "--copt=/WX" + build_targets: + - "//..." + test_flags: + - "--features=layering_check" + - "--copt=/WX" + test_targets: + - "//..." + windows-clang-cl: + name: "Windows: Clang" + platform: windows + environment: + BAZEL_VC: "C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\BuildTools\\VC" + build_flags: + - "--extra_toolchains=@local_config_cc//:cc-toolchain-x64_windows-clang-cl" + - "--extra_execution_platforms=//:x64_windows-clang-cl" + - "--compiler=clang-cl" + - "--features=layering_check" + build_targets: + - "//..." + test_flags: + - "--extra_toolchains=@local_config_cc//:cc-toolchain-x64_windows-clang-cl" + - "--extra_execution_platforms=//:x64_windows-clang-cl" + - "--compiler=clang-cl" + - "--features=layering_check" + test_targets: + - "//..." diff --git a/3rdparty/glog-0.7.0/.clang-format b/3rdparty/glog-0.7.0/.clang-format new file mode 100644 index 000000000..d064d8afe --- /dev/null +++ b/3rdparty/glog-0.7.0/.clang-format @@ -0,0 +1,168 @@ +--- +Language: Cpp +# BasedOnStyle: Google +AccessModifierOffset: -1 +AlignAfterOpenBracket: Align +AlignConsecutiveMacros: false +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Left +AlignOperands: true +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true +AllowAllConstructorInitializersOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortLambdasOnASingleLine: All +AllowShortIfStatementsOnASingleLine: WithoutElse +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Attach +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 80 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DeriveLineEnding: true +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '^' + Priority: 2 + SortPriority: 0 + - Regex: '^<.*\.h>' + Priority: 1 + SortPriority: 0 + - Regex: '^<.*' + Priority: 2 + SortPriority: 0 + - Regex: '.*' + Priority: 3 + SortPriority: 0 +IncludeIsMainRegex: '([-_](test|unittest))?$' +IncludeIsMainSourceRegex: '' +IndentCaseLabels: true +IndentGotoLabels: true +IndentPPDirectives: AfterHash +IndentWidth: 2 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Never +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 1 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 200 +PointerAlignment: Left +RawStringFormats: + - Language: Cpp + Delimiters: + - cc + - CC + - cpp + - Cpp + - CPP + - 'c++' + - 'C++' + CanonicalDelimiter: '' + BasedOnStyle: google + - Language: TextProto + Delimiters: + - pb + - PB + - proto + - PROTO + EnclosingFunctions: + - EqualsProto + - EquivToProto + - PARSE_PARTIAL_TEXT_PROTO + - PARSE_TEST_PROTO + - PARSE_TEXT_PROTO + - ParseTextOrDie + - ParseTextProtoOrDie + CanonicalDelimiter: '' + BasedOnStyle: google +ReflowComments: true +SortIncludes: true +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: false +SpacesInConditionalStatement: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +SpaceBeforeSquareBrackets: false +Standard: c++14 +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 8 +UseCRLF: false +UseTab: Never +... + diff --git a/3rdparty/glog-0.7.0/.clang-tidy b/3rdparty/glog-0.7.0/.clang-tidy new file mode 100644 index 000000000..1f4ea16fd --- /dev/null +++ b/3rdparty/glog-0.7.0/.clang-tidy @@ -0,0 +1,59 @@ +--- +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 + value: 'L;LL;LU;LLU' + - key: cert-oop54-cpp.WarnOnlyIfThisHasSuspiciousField + value: '0' + - key: cppcoreguidelines-explicit-virtual-functions.IgnoreDestructors + value: '1' + - key: cppcoreguidelines-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic + value: '1' + - key: google-build-namespaces.HeaderFileExtensions + value: ',h,hh,hpp,hxx' + - key: google-global-names-in-headers.HeaderFileExtensions + value: ',h,hh,hpp,hxx' + - key: google-readability-braces-around-statements.ShortStatementLines + value: '1' + - key: google-readability-function-size.BranchThreshold + value: '4294967295' + - key: google-readability-function-size.LineThreshold + value: '4294967295' + - key: google-readability-function-size.NestingThreshold + value: '4294967295' + - key: google-readability-function-size.ParameterThreshold + value: '4294967295' + - key: google-readability-function-size.StatementThreshold + value: '800' + - key: google-readability-function-size.VariableThreshold + value: '4294967295' + - key: google-readability-namespace-comments.ShortNamespaceLines + value: '10' + - key: google-readability-namespace-comments.SpacesBeforeComments + value: '2' + - key: google-runtime-int.SignedTypePrefix + value: int + - key: google-runtime-int.TypeSuffix + value: '' + - key: google-runtime-int.UnsignedTypePrefix + value: uint + - key: google-runtime-references.WhiteListTypes + value: '' + - key: modernize-loop-convert.MaxCopySize + value: '16' + - key: modernize-loop-convert.MinConfidence + value: reasonable + - key: modernize-loop-convert.NamingStyle + value: CamelCase + - key: modernize-pass-by-value.IncludeStyle + value: llvm + - key: modernize-replace-auto-ptr.IncludeStyle + value: llvm + - key: modernize-use-nullptr.NullMacros + value: 'NULL' +... + diff --git a/3rdparty/glog-0.7.0/.gitattributes b/3rdparty/glog-0.7.0/.gitattributes new file mode 100644 index 000000000..2f6d49472 --- /dev/null +++ b/3rdparty/glog-0.7.0/.gitattributes @@ -0,0 +1 @@ +*.h linguist-language=C++ diff --git a/3rdparty/glog-0.7.0/.github/dependabot.yml b/3rdparty/glog-0.7.0/.github/dependabot.yml new file mode 100644 index 000000000..dfd0e3086 --- /dev/null +++ b/3rdparty/glog-0.7.0/.github/dependabot.yml @@ -0,0 +1,10 @@ +# Set update schedule for GitHub Actions + +version: 2 +updates: + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + # Check for updates to GitHub Actions every week + interval: "weekly" diff --git a/3rdparty/glog-0.7.0/.github/workflows/android.yml b/3rdparty/glog-0.7.0/.github/workflows/android.yml new file mode 100644 index 000000000..4625e5cc3 --- /dev/null +++ b/3rdparty/glog-0.7.0/.github/workflows/android.yml @@ -0,0 +1,69 @@ +name: Android + +on: [push, pull_request] + +jobs: + build-android: + name: NDK-C++${{matrix.std}}-${{matrix.abi}}-${{matrix.build_type}} + runs-on: ubuntu-22.04 + permissions: + actions: read + contents: read + security-events: write + defaults: + run: + shell: bash + env: + NDK_VERSION: 26.0.10792818 + strategy: + fail-fast: true + matrix: + std: [14, 17, 20] + abi: [arm64-v8a, armeabi-v7a, x86_64, x86] + build_type: [Debug, Release] + + steps: + - uses: actions/checkout@v4 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: cpp + + - name: Setup Dependencies + run: | + sudo apt-get update + DEBIAN_FRONTEND=noninteractive sudo apt-get install -y \ + cmake \ + ninja-build + + - name: Setup NDK + env: + ANDROID_SDK_ROOT: /usr/local/lib/android/sdk + run: | + echo 'y' | ${{env.ANDROID_SDK_ROOT}}/cmdline-tools/latest/bin/sdkmanager --install 'ndk;${{env.NDK_VERSION}}' + + - name: Configure + env: + CXXFLAGS: -Wall -Wextra -Wpedantic -Wsign-conversion -Wtautological-compare -Wformat-nonliteral -Wundef -Werror ${{env.CXXFLAGS}} + run: | + cmake -S . -B build_${{matrix.abi}} \ + -DCMAKE_ANDROID_API=28 \ + -DCMAKE_ANDROID_ARCH_ABI=${{matrix.abi}} \ + -DCMAKE_ANDROID_NDK=/usr/local/lib/android/sdk/ndk/${{env.NDK_VERSION}} \ + -DCMAKE_ANDROID_STL_TYPE=c++_shared \ + -DCMAKE_BUILD_TYPE=${{matrix.build_type}} \ + -DCMAKE_CXX_EXTENSIONS=OFF \ + -DCMAKE_CXX_STANDARD=${{matrix.std}} \ + -DCMAKE_CXX_STANDARD_REQUIRED=ON \ + -DCMAKE_SYSTEM_NAME=Android \ + -G Ninja \ + -Werror + + - name: Build + run: | + cmake --build build_${{matrix.abi}} \ + --config ${{matrix.build_type}} + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 diff --git a/3rdparty/glog-0.7.0/.github/workflows/cifuzz.yml b/3rdparty/glog-0.7.0/.github/workflows/cifuzz.yml new file mode 100644 index 000000000..edb09491b --- /dev/null +++ b/3rdparty/glog-0.7.0/.github/workflows/cifuzz.yml @@ -0,0 +1,26 @@ +name: CIFuzz +on: [pull_request] +jobs: + Fuzzing: + runs-on: ubuntu-latest + steps: + - name: Build Fuzzers + id: build + uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master + with: + oss-fuzz-project-name: 'glog' + dry-run: false + language: c++ + - name: Run Fuzzers + uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master + with: + oss-fuzz-project-name: 'glog' + fuzz-seconds: 60 + dry-run: false + language: c++ + - name: Upload Crash + uses: actions/upload-artifact@v4 + if: failure() && steps.build.outcome == 'success' + with: + name: artifacts + path: ./out/artifacts diff --git a/3rdparty/glog-0.7.0/.github/workflows/emscripten.yml b/3rdparty/glog-0.7.0/.github/workflows/emscripten.yml new file mode 100644 index 000000000..90125f0ac --- /dev/null +++ b/3rdparty/glog-0.7.0/.github/workflows/emscripten.yml @@ -0,0 +1,57 @@ +name: Emscripten + +on: [push, pull_request] + +jobs: + build-linux: + defaults: + run: + shell: bash + name: Emscripten-C++${{matrix.std}}-${{matrix.build_type}}-${{matrix.lib}} + runs-on: ubuntu-22.04 + permissions: + actions: read + contents: read + security-events: write + container: emscripten/emsdk + strategy: + fail-fast: true + matrix: + build_type: [Release, Debug] + lib: [static] + std: [14, 17, 20, 23] + + steps: + - uses: actions/checkout@v4 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: cpp + + - name: Setup Dependencies + run: | + sudo apt-get update + DEBIAN_FRONTEND=noninteractive sudo apt-get install -y \ + cmake \ + ninja-build + + - name: Configure + env: + CXXFLAGS: -Wall -Wextra -Wsign-conversion -Wtautological-compare -Wformat-nonliteral -Wundef -Werror -Wno-error=wasm-exception-spec ${{env.CXXFLAGS}} + run: | + emcmake cmake -S . -B build_${{matrix.build_type}} \ + -DBUILD_SHARED_LIBS=${{matrix.lib == 'shared'}} \ + -DCMAKE_CXX_STANDARD=${{matrix.std}} \ + -DCMAKE_CXX_STANDARD_REQUIRED=ON \ + -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/install \ + -G Ninja \ + -Werror + + - name: Build + run: | + cmake --build build_${{matrix.build_type}} \ + --config ${{matrix.build_type}} + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 diff --git a/3rdparty/glog-0.7.0/.github/workflows/linux.yml b/3rdparty/glog-0.7.0/.github/workflows/linux.yml new file mode 100644 index 000000000..0dd243d51 --- /dev/null +++ b/3rdparty/glog-0.7.0/.github/workflows/linux.yml @@ -0,0 +1,142 @@ +name: Linux + +on: [push, pull_request] + +jobs: + build-linux: + defaults: + run: + shell: bash + name: GCC-C++${{matrix.std}}-${{matrix.build_type}}-${{matrix.lib}} + runs-on: ubuntu-22.04 + permissions: + actions: read + contents: read + security-events: write + strategy: + fail-fast: true + matrix: + build_type: [Release, Debug] + lib: [shared, static] + std: [14, 17, 20, 23] + + steps: + - uses: actions/checkout@v4 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: cpp + + - name: Setup Dependencies + run: | + sudo apt-get update + DEBIAN_FRONTEND=noninteractive sudo apt-get install -y \ + build-essential \ + cmake \ + gcovr \ + libgflags-dev \ + libunwind-dev \ + ninja-build + + - name: Cache GTest + id: cache-gtest + uses: actions/cache@v4 + 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: + CXXFLAGS: -Wall -Wextra -Wsign-conversion -Wtautological-compare -Wformat-nonliteral -Wundef -Werror ${{env.CXXFLAGS}} + run: | + cmake -S . -B build_${{matrix.build_type}} \ + -DBUILD_SHARED_LIBS=${{matrix.lib == 'shared'}} \ + -DCMAKE_CXX_STANDARD=${{matrix.std}} \ + -DCMAKE_CXX_STANDARD_REQUIRED=ON \ + -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/install \ + -G Ninja \ + -Werror + + - name: Build + run: | + cmake --build build_${{matrix.build_type}} \ + --config ${{matrix.build_type}} + + - name: Install + run: | + cmake --build build_${{matrix.build_type}} \ + --config ${{matrix.build_type}} \ + --target install + + cmake build_${{matrix.build_type}} \ + -DCMAKE_INSTALL_INCLUDEDIR=${{runner.workspace}}/foo/include \ + -DCMAKE_INSTALL_LIBDIR=${{runner.workspace}}/foo/lib \ + -DCMAKE_INSTALL_DATAROOTDIR=${{runner.workspace}}/foo/share + cmake --build build_${{matrix.build_type}} \ + --config ${{matrix.build_type}} \ + --target install + + - name: Test CMake Package (relative GNUInstallDirs) + run: | + cmake -S src/package_config_unittest/working_config \ + -B build_${{matrix.build_type}}_package \ + -DCMAKE_BUILD_TYPE=${{matrix.build_type}} \ + -DCMAKE_PREFIX_PATH=${{github.workspace}}/install \ + -G Ninja + cmake --build build_${{matrix.build_type}}_package \ + --config ${{matrix.build_type}} + + - name: Test CMake Package (absolute GNUInstallDirs) + run: | + cmake -S src/package_config_unittest/working_config \ + -B build_${{matrix.build_type}}_package_foo \ + -DCMAKE_BUILD_TYPE=${{matrix.build_type}} \ + -DCMAKE_PREFIX_PATH=${{runner.workspace}}/foo \ + -G Ninja + cmake --build build_${{matrix.build_type}}_package_foo \ + --config ${{matrix.build_type}} + + - name: Test + run: | + ctest --test-dir build_${{matrix.build_type}} -j$(nproc) --output-on-failure + + - name: Generate Coverage + if: matrix.build_type == 'Debug' + run: | + cd build_${{matrix.build_type}} + gcovr -r .. . -s --xml coverage.xml + + - name: Upload Coverage to Codecov + if: matrix.build_type == 'Debug' + uses: codecov/codecov-action@v3 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: build_${{matrix.build_type}}/coverage.xml + fail_ci_if_error: true + verbose: true + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 diff --git a/3rdparty/glog-0.7.0/.github/workflows/macos.yml b/3rdparty/glog-0.7.0/.github/workflows/macos.yml new file mode 100644 index 000000000..7fc296678 --- /dev/null +++ b/3rdparty/glog-0.7.0/.github/workflows/macos.yml @@ -0,0 +1,76 @@ +name: macOS + +on: [push, pull_request] + +jobs: + build-macos: + name: AppleClang-C++${{matrix.std}}-${{matrix.build_type}} + runs-on: macos-12 + permissions: + actions: read + contents: read + security-events: write + strategy: + fail-fast: true + matrix: + std: [14, 17, 20, 23] + include: + - generator: Xcode + - build_type: Debug + + steps: + - uses: actions/checkout@v4 + + - name: Setup Dependencies + run: | + brew install ninja + + - name: Setup Coverage Dependencies + if: matrix.build_type == 'Debug' + run: | + brew install gcovr + + - name: Setup Environment + if: matrix.build_type == 'Debug' + run: | + echo 'CXXFLAGS=--coverage' >> $GITHUB_ENV + echo 'LDFLAGS=--coverage' >> $GITHUB_ENV + + - name: Configure + shell: bash + env: + CXXFLAGS: -Wall -Wextra -Wsign-conversion -Wtautological-compare -Wformat-nonliteral -Wundef -Werror -pedantic-errors ${{env.CXXFLAGS}} + run: | + cmake -S . -B build_${{matrix.build_type}} \ + -DCMAKE_CXX_EXTENSIONS=OFF \ + -DCMAKE_CXX_STANDARD=${{matrix.std}} \ + -DCMAKE_CXX_STANDARD_REQUIRED=ON \ + -G "${{matrix.generator}}" \ + -Werror + + - name: Build + run: | + cmake --build build_${{matrix.build_type}} \ + --config ${{matrix.build_type}} + + - name: Test + run: | + ctest --test-dir build_${{matrix.build_type}} \ + --build-config ${{matrix.build_type}} \ + --output-on-failure + + - name: Generate Coverage + if: matrix.build_type == 'Debug' + run: | + cd build_${{matrix.build_type}} + rm -r Tests/ + gcovr -r .. . -s --cobertura coverage.xml + + - name: Upload Coverage to Codecov + if: matrix.build_type == 'Debug' + uses: codecov/codecov-action@v3 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: build_${{matrix.build_type}}/coverage.xml + fail_ci_if_error: true + verbose: true diff --git a/3rdparty/glog-0.7.0/.github/workflows/windows.yml b/3rdparty/glog-0.7.0/.github/workflows/windows.yml new file mode 100644 index 000000000..4131270ad --- /dev/null +++ b/3rdparty/glog-0.7.0/.github/workflows/windows.yml @@ -0,0 +1,245 @@ +name: Windows + +on: [push, pull_request] + +jobs: + build-msvc: + name: ${{matrix.msvc}}-${{matrix.arch}}-C++${{matrix.std}}-${{matrix.build_type}}-${{matrix.lib}} + runs-on: ${{matrix.os}} + permissions: + actions: read + contents: read + security-events: write + defaults: + run: + shell: powershell + env: + CL: /MP + CXXFLAGS: /WX /permissive- + strategy: + fail-fast: true + matrix: + arch: [Win32, x64] + build_type: [Debug, Release] + lib: [shared, static] + msvc: [VS-16-2019, VS-17-2022] + std: [14, 17, 20, 23] + include: + - msvc: VS-16-2019 + os: windows-2019 + generator: 'Visual Studio 16 2019' + - msvc: VS-17-2022 + os: windows-2022 + generator: 'Visual Studio 17 2022' + + steps: + - uses: actions/checkout@v4 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: cpp + + - name: Cache GTest + id: cache-gtest + uses: actions/cache@v4 + with: + path: gtest/ + 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/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-release-1.11.0 -B build-googletest ` + -A ${{matrix.arch}} ` + -DBUILD_SHARED_LIBS=${{matrix.lib == 'shared'}} ` + -Dgtest_force_shared_crt=ON ` + -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/gtest + cmake --build build-googletest ` + --config ${{matrix.build_type}} ` + --target install + + - name: Cache gflags + id: cache-gflags + uses: actions/cache@v4 + with: + path: gflags/ + key: ${{runner.os}}-gflags-2.2.2-${{matrix.lib}}-${{matrix.arch}}-${{matrix.build_type}} + + - name: Download gflags + if: steps.cache-gflags.outputs.cache-hit != 'true' + run: | + (New-Object System.Net.WebClient).DownloadFile("https://github.com/gflags/gflags/archive/refs/tags/v2.2.2.zip", "v2.2.2.zip") + Expand-Archive v2.2.2.zip . + + - name: Build gflags + if: steps.cache-gflags.outputs.cache-hit != 'true' + run: | + cmake -S gflags-2.2.2 -B build-gflags ` + -A ${{matrix.arch}} ` + -DBUILD_SHARED_LIBS=${{matrix.lib == 'shared'}} ` + -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/gflags + cmake --build build-gflags ` + --config ${{matrix.build_type}} ` + --target install + + - name: Setup Environment + run: | + echo "GTest_ROOT=$((Get-Item .).FullName)/gtest" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + echo "gflags_ROOT=$((Get-Item .).FullName)/gflags" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + echo "${{github.workspace}}/gtest/bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + echo "${{github.workspace}}/gflags/bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + + - name: Setup Release Environment + if: matrix.build_type != 'Debug' + run: | + echo "CXXFLAGS=/Zi ${{env.CXXFLAGS}}" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + + - name: Configure + run: | + cmake -S . -B build_${{matrix.build_type}} ` + -A ${{matrix.arch}} ` + -DBUILD_SHARED_LIBS=${{matrix.lib == 'shared'}} ` + -DCMAKE_CXX_EXTENSIONS=OFF ` + -DCMAKE_CXX_STANDARD=${{matrix.std}} ` + -DCMAKE_CXX_STANDARD_REQUIRED=ON ` + -DCMAKE_EXE_LINKER_FLAGS='/NOIMPLIB' ` + -DCMAKE_EXE_LINKER_FLAGS_RELEASE='/INCREMENTAL:NO /DEBUG' ` + -DCMAKE_INSTALL_PREFIX:PATH=./install ` + -DCMAKE_MSVC_RUNTIME_LIBRARY='MultiThreaded$<$:Debug>DLL' ` + -G "${{matrix.generator}}" ` + -Werror + + - name: Build + run: cmake --build build_${{matrix.build_type}} ` + --config ${{matrix.build_type}} + + - name: Test + env: + CTEST_OUTPUT_ON_FAILURE: 1 + run: | + cmake --build build_${{matrix.build_type}}/ ` + --config ${{matrix.build_type}} ` + --target RUN_TESTS + + - name: Install + run: | + cmake --build build_${{matrix.build_type}}/ ` + --config ${{matrix.build_type}} ` + --target install + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: language:cpp + + build-mingw: + name: ${{matrix.sys}}-${{matrix.env}}-C++${{matrix.std}}-${{matrix.build_type}}-${{matrix.lib}} + runs-on: windows-2022 + permissions: + actions: read + contents: read + security-events: write + env: + BUILDDIR: 'build_${{matrix.sys}}-${{matrix.env}}-C++${{matrix.std}}-${{matrix.build_type}}-${{matrix.lib}}' + defaults: + run: + shell: msys2 {0} + strategy: + fail-fast: true + matrix: + build_type: [Debug] + lib: [shared, static] + std: [14, 17, 20, 23] + sys: [mingw32, mingw64] + include: + - sys: mingw32 + env: i686 + - sys: mingw64 + env: x86_64 + + steps: + - uses: actions/checkout@v4 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: cpp + + - uses: msys2/setup-msys2@v2 + with: + msystem: ${{matrix.sys}} + install: >- + mingw-w64-${{matrix.env}}-cmake + mingw-w64-${{matrix.env}}-gcc + mingw-w64-${{matrix.env}}-gflags + mingw-w64-${{matrix.env}}-ninja + mingw-w64-${{matrix.env}}-python-jinja + mingw-w64-${{matrix.env}}-python-lxml + mingw-w64-${{matrix.env}}-python-pip + mingw-w64-${{matrix.env}}-python-pygments + + - name: Setup Coverage Dependencies + if: matrix.build_type == 'Debug' + run: | + pip install 'gcovr==7.0' + + - name: Setup Environment + if: matrix.build_type == 'Debug' + run: | + echo 'CXXFLAGS=--coverage ${{env.CXXFLAGS}}' >> $GITHUB_ENV + + - name: Configure + env: + CXXFLAGS: -Wall -Wextra -Wpedantic -Wsign-conversion -Wtautological-compare -Wformat-nonliteral -Wundef -Werror ${{env.CXXFLAGS}} + run: | + cmake -S . -B build_${{matrix.build_type}}/ \ + -DBUILD_SHARED_LIBS=${{matrix.lib == 'shared'}} \ + -DCMAKE_BUILD_TYPE=${{matrix.build_type}} \ + -DCMAKE_CXX_EXTENSIONS=OFF \ + -DCMAKE_CXX_STANDARD=${{matrix.std}} \ + -DCMAKE_CXX_STANDARD_REQUIRED=ON \ + -DCMAKE_INSTALL_PREFIX:PATH=./install \ + -G Ninja \ + -Werror + + - name: Build + run: | + cmake --build build_${{matrix.build_type}}/ --config ${{matrix.build_type}} + + - name: Test + env: + CTEST_OUTPUT_ON_FAILURE: 1 + run: | + cmake --build build_${{matrix.build_type}}/ --config ${{matrix.build_type}} \ + --target test + + - name: Install + run: | + cmake --build build_${{matrix.build_type}}/ \ + --config ${{matrix.build_type}} \ + --target install + + - name: Generate Coverage + if: matrix.build_type == 'Debug' + run: | + cd build_${{matrix.build_type}} + gcovr -r .. . -s --cobertura coverage.xml + + - name: Upload Coverage to Codecov + if: matrix.build_type == 'Debug' + uses: codecov/codecov-action@v3 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: build_${{matrix.build_type}}/coverage.xml + fail_ci_if_error: true + verbose: true + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 diff --git a/3rdparty/glog-0.7.0/.gitignore b/3rdparty/glog-0.7.0/.gitignore new file mode 100644 index 000000000..2678271dd --- /dev/null +++ b/3rdparty/glog-0.7.0/.gitignore @@ -0,0 +1,3 @@ +*.orig +/build*/ +bazel-* diff --git a/3rdparty/glog-0.7.0/AUTHORS b/3rdparty/glog-0.7.0/AUTHORS new file mode 100644 index 000000000..4b5ff0201 --- /dev/null +++ b/3rdparty/glog-0.7.0/AUTHORS @@ -0,0 +1,30 @@ +# This is the official list of glog authors for copyright purposes. +# This file is distinct from the CONTRIBUTORS files. +# See the latter for an explanation. +# +# Names should be added to this file as: +# Name or Organization +# The email address is not required for organizations. +# +# Please keep the list sorted. + +Abhishek Dasgupta +Abhishek Parmar +Andrew Schwartzmeyer +Andy Ying +Brian Silverman +Dmitriy Arbitman +Google Inc. +Guillaume Dumont +LingBin +Marco Wang +Michael Tanner +MiniLight +romange +Roman Perepelitsa +Sergiu Deitsch +tbennun +Teddy Reed +Vijaymahantesh Sattigeri +Zhongming Qu +Zhuoran Shen diff --git a/3rdparty/glog-0.7.0/BUILD.bazel b/3rdparty/glog-0.7.0/BUILD.bazel new file mode 100644 index 000000000..0acdc72b6 --- /dev/null +++ b/3rdparty/glog-0.7.0/BUILD.bazel @@ -0,0 +1,22 @@ +licenses(["notice"]) + +exports_files(["COPYING"]) + +load(":bazel/glog.bzl", "glog_library") + +glog_library() + +# platform() to build with clang-cl on Bazel CI. This is enabled with +# the flags in .bazelci/presubmit.yml: +# +# --incompatible_enable_cc_toolchain_resolution +# --extra_toolchains=@local_config_cc//:cc-toolchain-x64_windows-clang-cl +# --extra_execution_platforms=//:x64_windows-clang-cl +platform( + name = "x64_windows-clang-cl", + constraint_values = [ + "@platforms//cpu:x86_64", + "@platforms//os:windows", + "@bazel_tools//tools/cpp:clang-cl", + ], +) diff --git a/3rdparty/glog-0.7.0/CMakeLists.txt b/3rdparty/glog-0.7.0/CMakeLists.txt new file mode 100644 index 000000000..b787631ce --- /dev/null +++ b/3rdparty/glog-0.7.0/CMakeLists.txt @@ -0,0 +1,1053 @@ +cmake_minimum_required (VERSION 3.22) +project (glog + VERSION 0.7.0 + DESCRIPTION "C++ implementation of the Google logging module" + HOMEPAGE_URL https://github.com/google/glog + LANGUAGES CXX +) + +set (CPACK_PACKAGE_NAME glog) +set (CPACK_PACKAGE_DESCRIPTION_SUMMARY "Google logging library") +set (CPACK_PACKAGE_VERSION_MAJOR ${glog_VERSION_MAJOR}) +set (CPACK_PACKAGE_VERSION_MINOR ${glog_VERSION_MINOR}) +set (CPACK_PACKAGE_VERSION_PATCH ${glog_VERSION_PATCH}) +set (CPACK_PACKAGE_VERSION ${glog_VERSION}) + +list (APPEND CMAKE_MODULE_PATH ${glog_SOURCE_DIR}/cmake) + +include (CheckCXXSourceCompiles) +include (CheckCXXSourceRuns) +include (CheckCXXSymbolExists) +include (CheckIncludeFileCXX) +include (CheckStructHasMember) +include (CheckTypeSize) +include (CMakeDependentOption) +include (CMakePackageConfigHelpers) +include (CMakePushCheckState) +include (CPack) +include (CTest) +include (DetermineGflagsNamespace) +include (GenerateExportHeader) +include (GetCacheVariables) +include (GNUInstallDirs) + +option (BUILD_SHARED_LIBS "Build shared libraries" ON) +option (PRINT_UNSYMBOLIZED_STACK_TRACES + "Print file offsets in traces instead of symbolizing" OFF) +option (WITH_GFLAGS "Use gflags" ON) +option (WITH_GTEST "Use Google Test" ON) +option (WITH_PKGCONFIG "Enable pkg-config support" OFF) +option (WITH_SYMBOLIZE "Enable symbolize module" ON) +option (WITH_TLS "Enable Thread Local Storage (TLS) support" ON) + +set (WITH_UNWIND libunwind CACHE STRING "unwind driver") +set_property (CACHE WITH_UNWIND PROPERTY STRINGS none unwind libunwind) + +cmake_dependent_option (WITH_GMOCK "Use Google Mock" ON WITH_GTEST OFF) + +set (WITH_FUZZING none CACHE STRING "Fuzzing engine") +set_property (CACHE WITH_FUZZING PROPERTY STRINGS none libfuzzer ossfuzz) + +if (WITH_UNWIND STREQUAL none) + set (CMAKE_DISABLE_FIND_PACKAGE_Unwind ON) +endif (WITH_UNWIND STREQUAL none) + +if (NOT WITH_GTEST) + set (CMAKE_DISABLE_FIND_PACKAGE_GTest ON) +endif (NOT WITH_GTEST) + +set (CMAKE_C_VISIBILITY_PRESET hidden) +set (CMAKE_CXX_VISIBILITY_PRESET hidden) +set (CMAKE_POSITION_INDEPENDENT_CODE ON) +set (CMAKE_VISIBILITY_INLINES_HIDDEN ON) + +set (CMAKE_DEBUG_POSTFIX d) + +find_package (GTest NO_MODULE) + +if (GTest_FOUND) + set (HAVE_LIB_GTEST 1) +endif (GTest_FOUND) + +if (WITH_GMOCK AND TARGET GTest::gmock) + set (HAVE_LIB_GMOCK 1) +endif (WITH_GMOCK AND TARGET GTest::gmock) + +if (WITH_GFLAGS) + find_package (gflags 2.2.2) + + if (gflags_FOUND) + set (HAVE_LIB_GFLAGS 1) + determine_gflags_namespace (gflags_NAMESPACE) + endif (gflags_FOUND) +endif (WITH_GFLAGS) + +find_package (Threads REQUIRED) +find_package (Unwind) + +if (Unwind_FOUND) + cmake_push_check_state (RESET) + set (CMAKE_REQUIRED_LIBRARIES unwind::unwind) + + # Check whether linking actually succeeds. ARM toolchains of LLVM unwind + # implementation do not necessarily provide the _Unwind_Backtrace function + # which causes the previous check to succeed but the linking to fail. + check_cxx_symbol_exists (_Unwind_Backtrace unwind.h HAVE__UNWIND_BACKTRACE) + check_cxx_symbol_exists (_Unwind_GetIP unwind.h HAVE__UNWIND_GETIP) + + check_cxx_symbol_exists (unw_get_reg libunwind.h HAVE_UNW_GET_REG) + check_cxx_symbol_exists (unw_getcontext libunwind.h HAVE_UNW_GETCONTEXT) + check_cxx_symbol_exists (unw_init_local libunwind.h HAVE_UNW_INIT_LOCAL) + check_cxx_symbol_exists (unw_step libunwind.h HAVE_UNW_STEP) + + if (HAVE__UNWIND_BACKTRACE AND HAVE__UNWIND_GETIP) + set (_HAVE_UNWIND 1) + endif (HAVE__UNWIND_BACKTRACE AND HAVE__UNWIND_GETIP) + + if (HAVE_UNW_GET_REG AND HAVE_UNW_GETCONTEXT AND HAVE_UNW_INIT_LOCAL AND HAVE_UNW_STEP) + set (_HAVE_LIBUNWIND 1) + endif (HAVE_UNW_GET_REG AND HAVE_UNW_GETCONTEXT AND HAVE_UNW_INIT_LOCAL AND HAVE_UNW_STEP) + + if (WITH_UNWIND STREQUAL unwind) + if (_HAVE_UNWIND) + set (HAVE_UNWIND 1) + endif (_HAVE_UNWIND) + elseif (WITH_UNWIND STREQUAL libunwind) + if (_HAVE_LIBUNWIND) + set (HAVE_LIBUNWIND 1) + endif (_HAVE_LIBUNWIND) + endif (WITH_UNWIND STREQUAL unwind) + + unset (_HAVE_LIBUNWIND) + unset (_HAVE_UNWIND) + + cmake_pop_check_state () +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) +check_include_file_cxx (sys/utsname.h HAVE_SYS_UTSNAME_H) +check_include_file_cxx (sys/wait.h HAVE_SYS_WAIT_H) +check_include_file_cxx (syscall.h HAVE_SYSCALL_H) +check_include_file_cxx (syslog.h HAVE_SYSLOG_H) +check_include_file_cxx (ucontext.h HAVE_UCONTEXT_H) +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_cxx_symbol_exists (backtrace execinfo.h HAVE_EXECINFO_BACKTRACE) +check_cxx_symbol_exists (backtrace_symbols execinfo.h + HAVE_EXECINFO_BACKTRACE_SYMBOLS) +check_cxx_symbol_exists (_chsize_s io.h HAVE__CHSIZE_S) + +cmake_push_check_state (RESET) +set (CMAKE_REQUIRED_LIBRARIES dbghelp) +check_cxx_symbol_exists (UnDecorateSymbolName "windows.h;dbghelp.h" HAVE_DBGHELP) +cmake_pop_check_state () + +if (WITH_FUZZING STREQUAL none) + # Disable compiler demangler if fuzzing is active; we only want to use the + # glog demangler then. + 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 +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) + +set (_PC_FIELDS + "uc_mcontext.gregs[REG_PC]" # Solaris x86 (32 + 64 bit) + "uc_mcontext.gregs[REG_EIP]" # Linux (i386) + "uc_mcontext.gregs[REG_RIP]" # Linux (x86_64) + "uc_mcontext.sc_ip" # Linux (ia64) + "uc_mcontext.pc" # Linux (mips) + "uc_mcontext.uc_regs->gregs[PT_NIP]" # Linux (ppc) + "uc_mcontext.gregs[R15]" # Linux (arm old [untested]) + "uc_mcontext.arm_pc" # Linux (arm arch 5) + "uc_mcontext.gp_regs[PT_NIP]" # Suse SLES 11 (ppc64) + "uc_mcontext.mc_eip" # FreeBSD (i386) + "uc_mcontext.mc_rip" # FreeBSD (x86_64 [untested]) + "uc_mcontext.__gregs[_REG_EIP]" # NetBSD (i386) + "uc_mcontext.__gregs[_REG_RIP]" # NetBSD (x86_64) + "uc_mcontext->ss.eip" # OS X (i386, <=10.4) + "uc_mcontext->__ss.__eip" # OS X (i386, >=10.5) + "uc_mcontext->ss.rip" # OS X (x86_64) + "uc_mcontext->__ss.__rip" # OS X (>=10.5 [untested]) + "uc_mcontext->ss.srr0" # OS X (ppc, ppc64 [untested]) + "uc_mcontext->__ss.__srr0" # OS X (>=10.5 [untested]) +) + +if (HAVE_UCONTEXT_H AND NOT DEFINED PC_FROM_UCONTEXT) + cmake_push_check_state (RESET) + + set (CMAKE_REQUIRED_DEFINITIONS -D_GNU_SOURCE) + set (_PC_HEADERS ucontext.h signal.h) + + foreach (_PC_FIELD IN LISTS _PC_FIELDS) + foreach (_PC_HEADER IN LISTS _PC_HEADERS) + # Replace non-alphanumeric characters by underscores since the name will be + # used as preprocessor definition. + string (REGEX REPLACE "[^a-zA-Z0-9]" "_" HAVE_UCONTEXT_FIELD_NAME + "HAVE_PC_FROM_UCONTEXT_${_PC_FIELD}") + # Strip trailing underscores for readability + string (REGEX REPLACE "_+$" "" HAVE_UCONTEXT_FIELD_NAME + "${HAVE_UCONTEXT_FIELD_NAME}") + + check_struct_has_member (ucontext_t ${_PC_FIELD} ${_PC_HEADER} + ${HAVE_UCONTEXT_FIELD_NAME} LANGUAGE CXX) + + if (${HAVE_UCONTEXT_FIELD_NAME}) + set (PC_FROM_UCONTEXT ${_PC_FIELD} CACHE STRING + "<${_PC_HEADER}> ucontext_t PC member") + mark_as_advanced (PC_FROM_UCONTEXT) + break () + endif (${HAVE_UCONTEXT_FIELD_NAME}) + endforeach (_PC_HEADER) + + if (${HAVE_UCONTEXT_FIELD_NAME}) + break () + endif (${HAVE_UCONTEXT_FIELD_NAME}) + endforeach (_PC_FIELD) + + cmake_pop_check_state () +endif (HAVE_UCONTEXT_H AND NOT DEFINED PC_FROM_UCONTEXT) + +if (HAVE_EXECINFO_BACKTRACE AND HAVE_EXECINFO_BACKTRACE_SYMBOLS) + set (HAVE_STACKTRACE 1) +endif (HAVE_EXECINFO_BACKTRACE AND HAVE_EXECINFO_BACKTRACE_SYMBOLS) + +if (WITH_SYMBOLIZE) + if (WIN32 OR CYGWIN) + cmake_push_check_state (RESET) + set (CMAKE_REQUIRED_LIBRARIES DbgHelp) + + check_cxx_source_runs ([=[ + #include + #include + #include + + void foobar() { } + + int main() + { + HANDLE process = GetCurrentProcess(); + + if (!SymInitialize(process, NULL, TRUE)) + return EXIT_FAILURE; + + char buf[sizeof(SYMBOL_INFO) + MAX_SYM_NAME]; + SYMBOL_INFO *symbol = reinterpret_cast(buf); + symbol->SizeOfStruct = sizeof(SYMBOL_INFO); + symbol->MaxNameLen = MAX_SYM_NAME; + + void* const pc = reinterpret_cast(&foobar); + BOOL ret = SymFromAddr(process, reinterpret_cast(pc), 0, symbol); + + return ret ? EXIT_SUCCESS : EXIT_FAILURE; + } + ]=] HAVE_SYMBOLIZE) + + cmake_pop_check_state () + + if (HAVE_SYMBOLIZE) + set (HAVE_STACKTRACE 1) + endif (HAVE_SYMBOLIZE) + 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) + elseif (APPLE AND HAVE_DLADDR) + set (HAVE_SYMBOLIZE 1) + endif (WIN32 OR CYGWIN) +endif (WITH_SYMBOLIZE) + +# CMake manages symbolize availability. The definition is necessary only when +# building the library. +add_compile_definitions (GLOG_NO_SYMBOLIZE_DETECTION) + +check_cxx_symbol_exists (gmtime_r "cstdlib;ctime" HAVE_GMTIME_R) +check_cxx_symbol_exists (localtime_r "cstdlib;ctime" HAVE_LOCALTIME_R) + +set (SIZEOF_VOID_P ${CMAKE_SIZEOF_VOID_P}) + +# fopen/open on Cygwin can not handle unix-type paths like /home/.... +# therefore we translate TEST_SRC_DIR to windows-path. +if (CYGWIN) + execute_process (COMMAND cygpath.exe -m ${glog_SOURCE_DIR} + OUTPUT_STRIP_TRAILING_WHITESPACE + OUTPUT_VARIABLE TEST_SRC_DIR) + set (TEST_SRC_DIR \"${TEST_SRC_DIR}\") +else (CYGWIN) + set (TEST_SRC_DIR \"${glog_SOURCE_DIR}\") +endif (CYGWIN) + +configure_file (src/config.h.cmake.in config.h) + +set (_glog_CMake_BINDIR ${CMAKE_INSTALL_BINDIR}) +set (_glog_CMake_INCLUDE_DIR ${CMAKE_INSTALL_INCLUDEDIR}) +set (_glog_CMake_LIBDIR ${CMAKE_INSTALL_LIBDIR}) +set (_glog_CMake_INSTALLDIR ${_glog_CMake_LIBDIR}/cmake/glog) + +set (_glog_CMake_DIR glog/cmake) +set (_glog_CMake_DATADIR ${CMAKE_INSTALL_DATAROOTDIR}/${_glog_CMake_DIR}) +set (_glog_BINARY_CMake_DATADIR + ${glog_BINARY_DIR}/${_glog_CMake_DATADIR}) + +# Add additional CMake find modules here. +set (_glog_CMake_MODULES) + +if (Unwind_FOUND) + # Copy the module only if libunwind is actually used. + list (APPEND _glog_CMake_MODULES ${glog_SOURCE_DIR}/cmake/FindUnwind.cmake) +endif (Unwind_FOUND) + +# Generate file name for each module in the binary directory +foreach (_file ${_glog_CMake_MODULES}) + get_filename_component (_module "${_file}" NAME) + + list (APPEND _glog_BINARY_CMake_MODULES + ${_glog_BINARY_CMake_DATADIR}/${_module}) +endforeach (_file) + +if (_glog_CMake_MODULES) + # Copy modules to binary directory during the build + add_custom_command (OUTPUT ${_glog_BINARY_CMake_MODULES} + COMMAND ${CMAKE_COMMAND} -E make_directory + ${_glog_BINARY_CMake_DATADIR} + COMMAND ${CMAKE_COMMAND} -E copy ${_glog_CMake_MODULES} + ${_glog_BINARY_CMake_DATADIR} + DEPENDS ${_glog_CMake_MODULES} + COMMENT "Copying find modules..." + ) +endif (_glog_CMake_MODULES) + +set (GLOG_PUBLIC_H + ${glog_BINARY_DIR}/glog/export.h + src/glog/log_severity.h + src/glog/logging.h + src/glog/platform.h + src/glog/raw_logging.h + src/glog/stl_logging.h + src/glog/types.h + src/glog/flags.h + src/glog/vlog_is_on.h +) + +set (GLOG_SRCS + ${GLOG_PUBLIC_H} + src/base/commandlineflags.h + src/base/googleinit.h + src/demangle.cc + src/demangle.h + src/flags.cc + 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 + src/utilities.h + src/vlog_is_on.cc +) + +# NOTE MSYS2 defines both WIN32 and UNIX. Do not use windows port in this case. +if ((CYGWIN OR WIN32) AND NOT UNIX) + list (APPEND GLOG_SRCS + src/windows/port.cc + src/windows/port.h + ) + set (_glog_USE_WINDOWS_PORT TRUE) +endif ((CYGWIN OR WIN32) AND NOT UNIX) + +add_library (glog_internal OBJECT + ${_glog_BINARY_CMake_MODULES} + ${GLOG_SRCS} +) +target_compile_features (glog_internal PUBLIC $) +set_target_properties (glog_internal PROPERTIES DEFINE_SYMBOL GOOGLE_GLOG_IS_A_DLL) + +# Some generators (such as Xcode) do not generate any output if the target does +# not reference at least one source file. +set (_glog_EMPTY_SOURCE ${glog_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/glog.cc) + +add_custom_command ( + OUTPUT ${_glog_EMPTY_SOURCE} + COMMAND ${CMAKE_COMMAND} -E touch ${_glog_EMPTY_SOURCE} +) + +add_library (glog + $ + ${_glog_EMPTY_SOURCE} +) +target_compile_features (glog PUBLIC cxx_std_14) + +add_library (glog::glog ALIAS glog) + +set (glog_libraries_options_for_static_linking) + +# CMake always uses the generated export header +target_compile_definitions (glog PUBLIC GLOG_USE_GLOG_EXPORT) + +if (_glog_USE_WINDOWS_PORT) + target_compile_definitions (glog PRIVATE GLOG_USE_WINDOWS_PORT) +endif (_glog_USE_WINDOWS_PORT) + +unset (_glog_USE_WINDOWS_PORT) + +if (WIN32) + # Do not define min and max as macros + target_compile_definitions (glog PRIVATE NOMINMAX) + # Exclude unnecessary funcitonality + target_compile_definitions (glog PRIVATE WIN32_LEAN_AND_MEAN) +endif (WIN32) + +if (HAVE_LIB_GFLAGS) + target_compile_definitions (glog PUBLIC GLOG_USE_GFLAGS) +endif (HAVE_LIB_GFLAGS) + +if (Unwind_FOUND) + target_link_libraries (glog PRIVATE unwind::unwind) + set (glog_libraries_options_for_static_linking "${glog_libraries_options_for_static_linking} -lunwind") + set (Unwind_DEPENDENCY "find_dependency (Unwind ${Unwind_VERSION})") +endif (Unwind_FOUND) + +if (HAVE_DBGHELP) + target_link_libraries (glog PRIVATE dbghelp) + set (glog_libraries_options_for_static_linking "${glog_libraries_options_for_static_linking} -ldbghelp") +endif (HAVE_DBGHELP) + +target_link_libraries (glog PRIVATE Threads::Threads) + +if (CMAKE_THREAD_LIBS_INIT) + set (glog_libraries_options_for_static_linking "${glog_libraries_options_for_static_linking} ${CMAKE_THREAD_LIBS_INIT}") +endif (CMAKE_THREAD_LIBS_INIT) + +if (gflags_FOUND) + # Prefer the gflags target that uses double colon convention + if (TARGET gflags::gflags) + target_link_libraries (glog PUBLIC gflags::gflags) + else (TARGET gflags::gflags) + target_link_libraries (glog PUBLIC gflags) + endif (TARGET gflags::gflags) + + set (glog_libraries_options_for_static_linking "${glog_libraries_options_for_static_linking} -lgflags") +endif (gflags_FOUND) + +if (ANDROID) + target_link_libraries (glog PRIVATE log) + set (glog_libraries_options_for_static_linking "${glog_libraries_options_for_static_linking} -llog") +endif (ANDROID) + +set_target_properties (glog PROPERTIES VERSION ${glog_VERSION}) +set_target_properties (glog PROPERTIES SOVERSION 2) + +if (CYGWIN OR WIN32) + target_compile_definitions (glog PUBLIC GLOG_NO_ABBREVIATED_SEVERITIES) +endif (CYGWIN OR WIN32) + +set_target_properties (glog PROPERTIES PUBLIC_HEADER "${GLOG_PUBLIC_H}") + +target_include_directories (glog BEFORE PUBLIC + "$" + "$" + "$" + PRIVATE ${glog_BINARY_DIR} + PRIVATE ${glog_SOURCE_DIR}/src) + +if (CYGWIN OR WIN32) + target_include_directories (glog_internal PUBLIC + "$" + PRIVATE ${glog_SOURCE_DIR}/src/windows) + + target_include_directories (glog PUBLIC + "$" + PRIVATE ${glog_SOURCE_DIR}/src/windows) +endif (CYGWIN OR WIN32) + +set_target_properties (glog PROPERTIES DEFINE_SYMBOL GOOGLE_GLOG_IS_A_DLL) + +target_include_directories (glog_internal PUBLIC + $) +target_compile_definitions (glog_internal PUBLIC + $ + PRIVATE GOOGLE_GLOG_IS_A_DLL) + +generate_export_header (glog + EXPORT_MACRO_NAME GLOG_EXPORT + EXPORT_FILE_NAME ${glog_BINARY_DIR}/glog/export.h) + +string (STRIP "${glog_libraries_options_for_static_linking}" glog_libraries_options_for_static_linking) + +if (WITH_PKGCONFIG) + set (VERSION ${glog_VERSION}) + set (prefix ${CMAKE_INSTALL_PREFIX}) + set (exec_prefix ${CMAKE_INSTALL_FULL_BINDIR}) + set (libdir ${CMAKE_INSTALL_FULL_LIBDIR}) + set (includedir ${CMAKE_INSTALL_FULL_INCLUDEDIR}) + + configure_file ( + "${glog_SOURCE_DIR}/libglog.pc.in" + "${glog_BINARY_DIR}/libglog.pc" + @ONLY + ) + + unset (VERSION) + unset (prefix) + unset (exec_prefix) + unset (libdir) + unset (includedir) +endif (WITH_PKGCONFIG) + +# Unit testing + +if (NOT WITH_FUZZING STREQUAL "none") + add_executable (fuzz_demangle + src/fuzz_demangle.cc + ) + + if (WITH_FUZZING STREQUAL "ossfuzz") + set (LIB_FUZZING_ENGINE $ENV{LIB_FUZZING_ENGINE}) + target_link_libraries (fuzz_demangle PRIVATE glog ${LIB_FUZZING_ENGINE}) + elseif (WITH_FUZZING STREQUAL "libfuzzer") + target_compile_options (fuzz_demangle PRIVATE -fsanitize=fuzzer) + target_link_libraries (fuzz_demangle PRIVATE glog) + else (WITH_FUZZING STREQUAL "libfuzzer") + message (FATAL_ERROR "Unsupported fuzzing engine ${WITH_FUZZING}") + endif (WITH_FUZZING STREQUAL "ossfuzz") +endif (NOT WITH_FUZZING STREQUAL "none") + +if (BUILD_TESTING) + add_library (glog_test INTERFACE) + target_link_libraries (glog_test INTERFACE $ $) + target_compile_definitions (glog_test INTERFACE GLOG_STATIC_DEFINE $) + target_include_directories (glog_test INTERFACE $) + + if (HAVE_LIB_GTEST) + target_link_libraries (glog_test INTERFACE GTest::gtest) + endif (HAVE_LIB_GTEST) + + if (HAVE_LIB_GMOCK) + target_link_libraries (glog_test INTERFACE GTest::gmock) + endif (HAVE_LIB_GMOCK) + + add_executable (logging_unittest + src/logging_unittest.cc + ) + + target_link_libraries (logging_unittest PRIVATE glog_test) + + add_executable (stl_logging_unittest + src/stl_logging_unittest.cc + ) + + target_link_libraries (stl_logging_unittest PRIVATE glog_test) + + if (HAVE_SYMBOLIZE) + add_executable (symbolize_unittest + src/symbolize_unittest.cc + ) + + target_link_libraries (symbolize_unittest PRIVATE glog_test) + endif (HAVE_SYMBOLIZE) + + add_executable (demangle_unittest + src/demangle_unittest.cc + ) + + target_link_libraries (demangle_unittest PRIVATE glog_test) + + add_test (NAME demangle COMMAND demangle_unittest) + + if (HAVE___CXA_DEMANGLE) + # Demangle tests use a different (reduced) representation of symbols + set_tests_properties (demangle PROPERTIES DISABLED ON) + endif (HAVE___CXA_DEMANGLE) + + if (HAVE_STACKTRACE) + add_executable (stacktrace_unittest + src/stacktrace_unittest.cc + ) + + target_link_libraries (stacktrace_unittest PRIVATE glog_test) + endif (HAVE_STACKTRACE) + + add_executable (utilities_unittest + src/utilities_unittest.cc + ) + + target_link_libraries (utilities_unittest PRIVATE glog_test) + + if (HAVE_STACKTRACE AND HAVE_SYMBOLIZE) + add_executable (signalhandler_unittest + src/signalhandler_unittest.cc + ) + + target_link_libraries (signalhandler_unittest PRIVATE glog_test) + endif (HAVE_STACKTRACE AND HAVE_SYMBOLIZE) + + add_test (NAME logging COMMAND logging_unittest) + + set_tests_properties (logging PROPERTIES TIMEOUT 30) + # MacOS diff is not deterministic: use the output to determine whether the + # test passed. + set_tests_properties (logging PROPERTIES PASS_REGULAR_EXPRESSION ".*\nPASS\n.*") + + # FIXME: Skip flaky test + set_tests_properties (logging PROPERTIES SKIP_REGULAR_EXPRESSION + "Check failed: time_ns within LogTimes::LOG_PERIOD_TOL_NS of LogTimes::LOG_PERIOD_NS") + + if (APPLE) + # FIXME: Skip flaky test + set_property (TEST logging APPEND PROPERTY SKIP_REGULAR_EXPRESSION + "unexpected new.*PASS\nTest with golden file failed. We'll try to show the diff:") + endif (APPLE) + + if (TARGET signalhandler_unittest) + add_test (NAME signalhandler COMMAND signalhandler_unittest) + endif (TARGET signalhandler_unittest) + + if (TARGET stacktrace_unittest) + add_test (NAME stacktrace COMMAND stacktrace_unittest) + set_tests_properties (stacktrace PROPERTIES TIMEOUT 30) + endif (TARGET stacktrace_unittest) + + add_test (NAME stl_logging COMMAND stl_logging_unittest) + + if (TARGET symbolize_unittest) + add_test (NAME symbolize COMMAND symbolize_unittest) + + # FIXME: Skip flaky test when compiled in C++20 mode + set_tests_properties (symbolize PROPERTIES SKIP_REGULAR_EXPRESSION + [=[Check failed: streq\("nonstatic_func"\, TrySymbolize\(\(void \*\)\(&nonstatic_func\)\)\)]=]) + endif (TARGET symbolize_unittest) + + if (HAVE_LIB_GMOCK) + add_executable (mock-log_unittest + src/mock-log_unittest.cc + src/mock-log.h + ) + + target_link_libraries (mock-log_unittest PRIVATE glog_test) + + add_test (NAME mock-log COMMAND mock-log_unittest) + endif (HAVE_LIB_GMOCK) + + # Generate an initial cache + + get_cache_variables (_CACHEVARS) + + set (_INITIAL_CACHE + ${glog_BINARY_DIR}/test_package_config/glog_package_config_initial_cache.cmake) + + # Package config test + + add_test (NAME cmake_package_config_init COMMAND ${CMAKE_COMMAND} + -DTEST_BINARY_DIR=${glog_BINARY_DIR}/test_package_config + -DINITIAL_CACHE=${_INITIAL_CACHE} + -DCACHEVARS=${_CACHEVARS} + -P ${glog_SOURCE_DIR}/cmake/TestInitPackageConfig.cmake + ) + + add_test (NAME cmake_package_config_generate COMMAND ${CMAKE_COMMAND} + -DGENERATOR=${CMAKE_GENERATOR} + -DGENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM} + -DGENERATOR_TOOLSET=${CMAKE_GENERATOR_TOOLSET} + -DINITIAL_CACHE=${_INITIAL_CACHE} + -DPACKAGE_DIR=${glog_BINARY_DIR} + -DPATH=$ENV{PATH} + -DSOURCE_DIR=${glog_SOURCE_DIR}/src/package_config_unittest/working_config + -DTEST_BINARY_DIR=${glog_BINARY_DIR}/test_package_config/working_config + -P ${glog_SOURCE_DIR}/cmake/TestPackageConfig.cmake + ) + + add_test (NAME cmake_package_config_build COMMAND + ${CMAKE_COMMAND} --build ${glog_BINARY_DIR}/test_package_config/working_config + --config $ + ) + + add_test (NAME cmake_package_config_cleanup COMMAND ${CMAKE_COMMAND} -E + remove_directory + ${glog_BINARY_DIR}/test_package_config + ) + + # Fixtures setup + set_tests_properties (cmake_package_config_init PROPERTIES FIXTURES_SETUP + cmake_package_config) + set_tests_properties (cmake_package_config_generate PROPERTIES FIXTURES_SETUP + cmake_package_config_working) + + # Fixtures cleanup + set_tests_properties (cmake_package_config_cleanup PROPERTIES FIXTURES_CLEANUP + cmake_package_config) + + # Fixture requirements + set_tests_properties (cmake_package_config_generate PROPERTIES + FIXTURES_REQUIRED cmake_package_config) + set_tests_properties (cmake_package_config_build PROPERTIES + FIXTURES_REQUIRED "cmake_package_config;cmake_package_config_working") + + add_executable (cleanup_immediately_unittest + src/cleanup_immediately_unittest.cc) + + target_link_libraries (cleanup_immediately_unittest PRIVATE glog_test) + + add_executable (cleanup_with_absolute_prefix_unittest + src/cleanup_with_absolute_prefix_unittest.cc) + + target_link_libraries (cleanup_with_absolute_prefix_unittest PRIVATE glog_test) + + add_executable (cleanup_with_relative_prefix_unittest + src/cleanup_with_relative_prefix_unittest.cc) + + target_link_libraries (cleanup_with_relative_prefix_unittest PRIVATE glog_test) + + set (CLEANUP_LOG_DIR ${glog_BINARY_DIR}/cleanup_tests) + + add_test (NAME cleanup_init COMMAND + ${CMAKE_COMMAND} -E make_directory ${CLEANUP_LOG_DIR}) + add_test (NAME cleanup_logdir COMMAND + ${CMAKE_COMMAND} -E remove_directory ${CLEANUP_LOG_DIR}) + add_test (NAME cleanup_immediately COMMAND + ${CMAKE_COMMAND} + -DLOGCLEANUP=$ + # NOTE The trailing slash is important + -DTEST_DIR=${CLEANUP_LOG_DIR}/ + -P ${glog_SOURCE_DIR}/cmake/RunCleanerTest1.cmake + WORKING_DIRECTORY ${glog_BINARY_DIR}) + add_test (NAME cleanup_with_absolute_prefix COMMAND + ${CMAKE_COMMAND} + -DLOGCLEANUP=$ + -DTEST_DIR=${glog_BINARY_DIR}/ + -P ${glog_SOURCE_DIR}/cmake/RunCleanerTest2.cmake + WORKING_DIRECTORY ${glog_BINARY_DIR}) + add_test (NAME cleanup_with_relative_prefix COMMAND + ${CMAKE_COMMAND} + -DLOGCLEANUP=$ + -DTEST_DIR=${glog_BINARY_DIR}/ + -DTEST_SUBDIR=test_subdir/ + -P ${glog_SOURCE_DIR}/cmake/RunCleanerTest3.cmake + WORKING_DIRECTORY ${glog_BINARY_DIR}) + + # Fixtures setup + set_tests_properties (cleanup_init PROPERTIES FIXTURES_SETUP logcleanuptest) + ## Fixtures cleanup + set_tests_properties (cleanup_logdir PROPERTIES FIXTURES_CLEANUP logcleanuptest) + # Fixture requirements + set_tests_properties (cleanup_immediately PROPERTIES FIXTURES_REQUIRED logcleanuptest) + set_tests_properties (cleanup_with_absolute_prefix PROPERTIES FIXTURES_REQUIRED logcleanuptest) + set_tests_properties (cleanup_with_relative_prefix PROPERTIES FIXTURES_REQUIRED logcleanuptest) + + add_executable (striplog0_unittest + src/striplog_unittest.cc + ) + target_compile_definitions (striplog0_unittest PRIVATE GOOGLE_STRIP_LOG=0) + target_link_libraries (striplog0_unittest PRIVATE glog_test) + + add_test (NAME striplog0 COMMAND striplog0_unittest) + + add_executable (striplog2_unittest + src/striplog_unittest.cc + ) + target_compile_definitions (striplog2_unittest PRIVATE GOOGLE_STRIP_LOG=2) + target_link_libraries (striplog2_unittest PRIVATE glog_test) + + add_test (NAME striplog2 COMMAND striplog2_unittest) + + add_executable (striplog10_unittest + src/striplog_unittest.cc + ) + target_compile_definitions (striplog10_unittest PRIVATE GOOGLE_STRIP_LOG=10) + target_link_libraries (striplog10_unittest PRIVATE glog_test) + + add_test (NAME striplog10 COMMAND striplog10_unittest) + + set_tests_properties ( + striplog0 + striplog2 + striplog10 + PROPERTIES WILL_FAIL ON + ) + + add_test (NAME log_severity_constants COMMAND ${CMAKE_CTEST_COMMAND} + --build-config $ + --build-and-test + "${glog_SOURCE_DIR}/src/log_severity_unittest" + "${glog_BINARY_DIR}/Tests/log_severity_constants" + --build-generator ${CMAKE_GENERATOR} + --build-makeprogram ${CMAKE_MAKE_PROGRAM} + --build-target glog_log_severity_constants + --build-options + -DCMAKE_BUILD_TYPE=$ + -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} + -DCMAKE_GENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM} + -DCMAKE_GENERATOR_TOOLSET=${CMAKE_GENERATOR_TOOLSET} + -Dglog_DIR=${glog_BINARY_DIR} + ) + set_tests_properties (log_severity_constants PROPERTIES + PASS_REGULAR_EXPRESSION "COMPACT_GOOGLE_LOG_[1-3]" + ) + + add_test (NAME log_severity_conversion COMMAND ${CMAKE_CTEST_COMMAND} + --build-config $ + --build-and-test + "${glog_SOURCE_DIR}/src/log_severity_unittest" + "${glog_BINARY_DIR}/Tests/log_severity_conversion" + --build-generator ${CMAKE_GENERATOR} + --build-makeprogram ${CMAKE_MAKE_PROGRAM} + --build-target glog_log_severity_conversion + --build-options + -DCMAKE_BUILD_TYPE=$ + -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} + -DCMAKE_GENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM} + -DCMAKE_GENERATOR_TOOLSET=${CMAKE_GENERATOR_TOOLSET} + -Dglog_DIR=${glog_BINARY_DIR} + ) + + if (CMAKE_COMPILER_IS_GNUCXX) + set_tests_properties (log_severity_conversion PROPERTIES + PASS_REGULAR_EXPRESSION "error: invalid conversion from (‘|')int(’|')" + ) + elseif (CMAKE_CXX_COMPILER_ID MATCHES Clang) + set_tests_properties (log_severity_conversion PROPERTIES + PASS_REGULAR_EXPRESSION "no known conversion from 'int'" + ) + elseif (MSVC) + set_tests_properties (log_severity_conversion PROPERTIES + PASS_REGULAR_EXPRESSION "error C2440" + ) + else (CMAKE_COMPILER_IS_GNUCXX) + message (AUTHOR_WARNING + "Unsupported C++ compiler ${CMAKE_CXX_COMPILER_ID}: " + "log_severity_conversion test will be disabled" + ) + set_tests_properties (log_severity_conversion DISABLED ON) + endif (CMAKE_COMPILER_IS_GNUCXX) + + add_test (NAME includes_logging COMMAND ${CMAKE_CTEST_COMMAND} + --build-config $ + --build-and-test + "${glog_SOURCE_DIR}/src/includes_unittest" + "${glog_BINARY_DIR}/Tests/includes_logging" + --build-generator ${CMAKE_GENERATOR} + --build-makeprogram ${CMAKE_MAKE_PROGRAM} + --build-target glog_includes_logging + --build-options + -DCMAKE_BUILD_TYPE=$ + -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} + -DCMAKE_GENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM} + -DCMAKE_GENERATOR_TOOLSET=${CMAKE_GENERATOR_TOOLSET} + -Dglog_DIR=${glog_BINARY_DIR} + ) + + add_test (NAME includes_vlog_is_on COMMAND ${CMAKE_CTEST_COMMAND} + --build-config $ + --build-and-test + "${glog_SOURCE_DIR}/src/includes_unittest" + "${glog_BINARY_DIR}/Tests/includes_vlog_is_on" + --build-generator ${CMAKE_GENERATOR} + --build-makeprogram ${CMAKE_MAKE_PROGRAM} + --build-target glog_includes_vlog_is_on + --build-options + -DCMAKE_BUILD_TYPE=$ + -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} + -DCMAKE_GENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM} + -DCMAKE_GENERATOR_TOOLSET=${CMAKE_GENERATOR_TOOLSET} + -Dglog_DIR=${glog_BINARY_DIR} + ) + + add_test (NAME includes_raw_logging COMMAND ${CMAKE_CTEST_COMMAND} + --build-config $ + --build-and-test + "${glog_SOURCE_DIR}/src/includes_unittest" + "${glog_BINARY_DIR}/Tests/includes_raw_logging" + --build-generator ${CMAKE_GENERATOR} + --build-makeprogram ${CMAKE_MAKE_PROGRAM} + --build-target glog_includes_raw_logging + --build-options + -DCMAKE_BUILD_TYPE=$ + -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} + -DCMAKE_GENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM} + -DCMAKE_GENERATOR_TOOLSET=${CMAKE_GENERATOR_TOOLSET} + -Dglog_DIR=${glog_BINARY_DIR} + ) + + add_test (NAME includes_stl_logging COMMAND ${CMAKE_CTEST_COMMAND} + --build-config $ + --build-and-test + "${glog_SOURCE_DIR}/src/includes_unittest" + "${glog_BINARY_DIR}/Tests/includes_stl_logging" + --build-generator ${CMAKE_GENERATOR} + --build-makeprogram ${CMAKE_MAKE_PROGRAM} + --build-target glog_includes_stl_logging + --build-options + -DCMAKE_BUILD_TYPE=$ + -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} + -DCMAKE_GENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM} + -DCMAKE_GENERATOR_TOOLSET=${CMAKE_GENERATOR_TOOLSET} + -Dglog_DIR=${glog_BINARY_DIR} + ) + + add_test (NAME dcheck_on COMMAND ${CMAKE_CTEST_COMMAND} + --build-config Debug + --build-and-test + "${glog_SOURCE_DIR}/src/dcheck_unittest" + "${glog_BINARY_DIR}/Tests/dcheck_on" + --build-generator ${CMAKE_GENERATOR} + --build-generator-platform "${CMAKE_GENERATOR_PLATFORM}" + --build-generator-toolset "${CMAKE_GENERATOR_TOOLSET}" + --build-makeprogram ${CMAKE_MAKE_PROGRAM} + --build-target glog_dcheck + --build-options + -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} + -DCMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY=ON + -Dglog_DIR=${glog_BINARY_DIR} + --test-command glog_dcheck + ) + set_tests_properties (dcheck_on PROPERTIES + DISABLED $> + ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$" + PASS_REGULAR_EXPRESSION "Assert failed: false" + ) + + add_test (NAME dcheck_off COMMAND ${CMAKE_CTEST_COMMAND} + --build-config Release + --build-and-test + "${glog_SOURCE_DIR}/src/dcheck_unittest" + "${glog_BINARY_DIR}/Tests/dcheck_off" + --build-generator ${CMAKE_GENERATOR} + --build-generator-platform "${CMAKE_GENERATOR_PLATFORM}" + --build-generator-toolset "${CMAKE_GENERATOR_TOOLSET}" + --build-makeprogram ${CMAKE_MAKE_PROGRAM} + --build-target glog_dcheck + --build-options + -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} + -DCMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY=ON + -Dglog_DIR=${glog_BINARY_DIR} + --test-command glog_dcheck + ) + # There should be no output + set_tests_properties (dcheck_off PROPERTIES + DISABLED $> + ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$" + PASS_REGULAR_EXPRESSION "" + ) +endif (BUILD_TESTING) + +install (TARGETS glog + EXPORT glog-targets + RUNTIME DESTINATION ${_glog_CMake_BINDIR} + PUBLIC_HEADER DESTINATION ${_glog_CMake_INCLUDE_DIR}/glog + LIBRARY DESTINATION ${_glog_CMake_LIBDIR} + ARCHIVE DESTINATION ${_glog_CMake_LIBDIR}) + +if (WITH_PKGCONFIG) + install ( + FILES "${glog_BINARY_DIR}/libglog.pc" + DESTINATION "${_glog_CMake_LIBDIR}/pkgconfig" + ) +endif (WITH_PKGCONFIG) + +set (glog_CMake_VERSION 3.0) + +if (gflags_FOUND) + # Ensure clients locate only the package config and not third party find + # modules having the same name. This avoid cmake_policy PUSH/POP errors. + if (CMAKE_VERSION VERSION_LESS 3.9) + set (gflags_DEPENDENCY "find_dependency (gflags ${gflags_VERSION})") + else (CMAKE_VERSION VERSION_LESS 3.9) + # Passing additional find_package arguments to find_dependency is possible + # starting with CMake 3.9. + set (glog_CMake_VERSION 3.9) + set (gflags_DEPENDENCY "find_dependency (gflags ${gflags_VERSION} NO_MODULE)") + endif (CMAKE_VERSION VERSION_LESS 3.9) +endif (gflags_FOUND) + +configure_package_config_file (glog-config.cmake.in + ${glog_BINARY_DIR}/glog-config.cmake + INSTALL_DESTINATION ${_glog_CMake_INSTALLDIR} + NO_CHECK_REQUIRED_COMPONENTS_MACRO) + +write_basic_package_version_file ( + ${glog_BINARY_DIR}/glog-config-version.cmake + COMPATIBILITY SameMajorVersion) + +export (TARGETS glog NAMESPACE glog:: FILE glog-targets.cmake) +export (PACKAGE glog) + +get_filename_component (_PREFIX "${CMAKE_INSTALL_PREFIX}" ABSOLUTE) + +# Directory containing the find modules relative to the config install +# directory. +file (RELATIVE_PATH glog_REL_CMake_MODULES + ${_PREFIX}/${_glog_CMake_INSTALLDIR} + ${_PREFIX}/${_glog_CMake_DATADIR}/glog-modules.cmake) + +get_filename_component (glog_REL_CMake_DATADIR ${glog_REL_CMake_MODULES} + DIRECTORY) + +set (glog_FULL_CMake_DATADIR + ${glog_BINARY_DIR}/${_glog_CMake_DATADIR}) + +configure_file (glog-modules.cmake.in + ${glog_BINARY_DIR}/glog-modules.cmake @ONLY) + +install (CODE +" +set (glog_FULL_CMake_DATADIR \"\\\${CMAKE_CURRENT_LIST_DIR}/${glog_REL_CMake_DATADIR}\") +set (glog_DATADIR_DESTINATION ${_glog_CMake_INSTALLDIR}) + +if (NOT IS_ABSOLUTE ${_glog_CMake_INSTALLDIR}) + set (glog_DATADIR_DESTINATION \"\${CMAKE_INSTALL_PREFIX}/\${glog_DATADIR_DESTINATION}\") +endif (NOT IS_ABSOLUTE ${_glog_CMake_INSTALLDIR}) + +configure_file (\"${glog_SOURCE_DIR}/glog-modules.cmake.in\" + \"${glog_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/glog-modules.cmake\" @ONLY) +file (INSTALL + \"${glog_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/glog-modules.cmake\" + DESTINATION + \"\${glog_DATADIR_DESTINATION}\") +" + COMPONENT Development +) + +install (FILES + ${glog_BINARY_DIR}/glog-config.cmake + ${glog_BINARY_DIR}/glog-config-version.cmake + DESTINATION ${_glog_CMake_INSTALLDIR}) + +# Find modules in share/glog/cmake +install (DIRECTORY ${_glog_BINARY_CMake_DATADIR} + DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/glog + COMPONENT Development + FILES_MATCHING PATTERN "*.cmake" +) + +install (EXPORT glog-targets NAMESPACE glog:: DESTINATION + ${_glog_CMake_INSTALLDIR}) diff --git a/3rdparty/glog-0.7.0/CONTRIBUTORS b/3rdparty/glog-0.7.0/CONTRIBUTORS new file mode 100644 index 000000000..82aead939 --- /dev/null +++ b/3rdparty/glog-0.7.0/CONTRIBUTORS @@ -0,0 +1,54 @@ +# People who have agreed to one of the CLAs and can contribute patches. +# The AUTHORS file lists the copyright holders; this file +# lists people. For example, Google employees are listed here +# but not in AUTHORS, because Google holds the copyright. +# +# Names should be added to this file only after verifying that +# the individual or the individual's organization has agreed to +# the appropriate Contributor License Agreement, found here: +# +# https://developers.google.com/open-source/cla/individual +# https://developers.google.com/open-source/cla/corporate +# +# The agreement for individuals can be filled out on the web. +# +# When adding J Random Contributor's name to this file, +# either J's name or J's organization's name should be +# added to the AUTHORS file, depending on whether the +# individual or corporate CLA was used. +# +# Names should be added to this file as: +# Name +# +# Please keep the list sorted. + +Abhishek Dasgupta +Abhishek Parmar +Andrew Schwartzmeyer +Andy Ying +Bret McKee +Brian Silverman +Dmitriy Arbitman +Eric Kilmer +Fumitoshi Ukai +Guillaume Dumont +Håkan L. S. Younes +Ivan Penkov +Jacob Trimble +Jim Ray +LingBin +Marco Wang +Michael Darr +Michael Tanner +MiniLight +Peter Collingbourne +Rodrigo Queiro +romange +Roman Perepelitsa +Sergiu Deitsch +Shinichiro Hamaji +tbennun +Teddy Reed +Vijaymahantesh Sattigeri +Zhongming Qu +Zhuoran Shen diff --git a/3rdparty/glog-0.7.0/COPYING b/3rdparty/glog-0.7.0/COPYING new file mode 100644 index 000000000..ac95ad875 --- /dev/null +++ b/3rdparty/glog-0.7.0/COPYING @@ -0,0 +1,28 @@ +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. diff --git a/3rdparty/glog-0.7.0/ChangeLog b/3rdparty/glog-0.7.0/ChangeLog new file mode 100644 index 000000000..9f7409410 --- /dev/null +++ b/3rdparty/glog-0.7.0/ChangeLog @@ -0,0 +1,114 @@ +2024-02-17 Google Inc. + + * google-glog: version 0.7.0. + * See git log for the details. + +2022-04-05 Google Inc. + + * google-glog: version 0.6.0. + * See git log for the details. + +2021-05-08 Google Inc. + + * google-glog: version 0.5.0. + * See git log for the details. + +2019-01-22 Google Inc. + + * google-glog: version 0.4.0. + * See git log for the details. + +2017-05-09 Google Inc. + + * google-glog: version 0.3.5 + * See git log for the details. + +2015-03-09 Google Inc. + + * google-glog: version 0.3.4 + * See git log for the details. + +2013-02-01 Google Inc. + + * google-glog: version 0.3.3 + * Add --disable-rtti option for configure. + * Visual Studio build and test fix. + * QNX build fix (thanks vanuan). + * Reduce warnings. + * Fixed LOG_SYSRESULT (thanks ukai). + * FreeBSD build fix (thanks yyanagisawa). + * Clang build fix. + * Now users can re-initialize glog after ShutdownGoogleLogging. + * Color output support by GLOG_colorlogtostderr (thanks alexs). + * Now glog's ABI around flags are compatible with gflags. + * Document mentions how to modify flags from user programs. + +2012-01-12 Google Inc. + + * google-glog: version 0.3.2 + * Clang support. + * Demangler and stacktrace improvement for newer GCCs. + * Now fork(2) doesn't mess up log files. + * Make valgrind happier. + * Reduce warnings for more -W options. + * Provide a workaround for ERROR defined by windows.h. + +2010-06-15 Google Inc. + + * google-glog: version 0.3.1 + * GLOG_* environment variables now work even when gflags is installed. + * Snow leopard support. + * Now we can build and test from out side tree. + * Add DCHECK_NOTNULL. + * Add ShutdownGoogleLogging to close syslog (thanks DGunchev) + * Fix --enable-frame-pointers option (thanks kazuki.ohta) + * Fix libunwind detection (thanks giantchen) + +2009-07-30 Google Inc. + + * google-glog: version 0.3.0 + * Fix a deadlock happened when user uses glog with recent gflags. + * Suppress several unnecessary warnings (thanks keir). + * NetBSD and OpenBSD support. + * Use Win32API GetComputeNameA properly (thanks magila). + * Fix user name detection for Windows (thanks ademin). + * Fix several minor bugs. + +2009-04-10 Google Inc. + * google-glog: version 0.2.1 + * Fix timestamps of VC++ version. + * Add pkg-config support (thanks Tomasz) + * Fix build problem when building with gtest (thanks Michael) + * Add --with-gflags option for configure (thanks Michael) + * Fixes for GCC 4.4 (thanks John) + +2009-01-23 Google Inc. + * google-glog: version 0.2 + * Add initial Windows VC++ support. + * Google testing/mocking frameworks integration. + * Link pthread library automatically. + * Flush logs in signal handlers. + * Add macros LOG_TO_STRING, LOG_AT_LEVEL, DVLOG, and LOG_TO_SINK_ONLY. + * Log microseconds. + * Add --log_backtrace_at option. + * Fix some minor bugs. + +2008-11-18 Google Inc. + * google-glog: version 0.1.2 + * Add InstallFailureSignalHandler(). (satorux) + * Re-organize the way to produce stacktraces. + * Don't define unnecessary macro DISALLOW_EVIL_CONSTRUCTORS. + +2008-10-15 Google Inc. + * google-glog: version 0.1.1 + * Support symbolize for MacOSX 10.5. + * BUG FIX: --vmodule didn't work with gflags. + * BUG FIX: symbolize_unittest failed with GCC 4.3. + * Several fixes on the document. + +2008-10-07 Google Inc. + + * google-glog: initial release: + The glog package contains a library that implements application-level + logging. This library provides logging APIs based on C++-style + streams and various helper macros. diff --git a/3rdparty/glog-0.7.0/MODULE.bazel b/3rdparty/glog-0.7.0/MODULE.bazel new file mode 100644 index 000000000..04668eda7 --- /dev/null +++ b/3rdparty/glog-0.7.0/MODULE.bazel @@ -0,0 +1,6 @@ +module( + name = "glog", + compatibility_level = 1, +) + +bazel_dep(name = "gflags", version = "2.2.2", repo_name = "com_github_gflags_gflags") diff --git a/3rdparty/glog-0.7.0/README.rst b/3rdparty/glog-0.7.0/README.rst new file mode 100644 index 000000000..026cbb521 --- /dev/null +++ b/3rdparty/glog-0.7.0/README.rst @@ -0,0 +1,1011 @@ +Google Logging Library +====================== + +|Linux Github actions| |Windows Github actions| |macOS Github actions| |Codecov| + +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 +--------------- + +You can log a message by simply streaming things to ``LOG``\ (`__>), e.g., + +.. code:: cpp + + #include + + int main(int argc, char* argv[]) { + // Initialize Google’s logging library. + google::InitGoogleLogging(argv[0]); + + // ... + LOG(INFO) << "Found " << num_cookies << " cookies"; + } + + +The library can be installed using various package managers or compiled from +`source <#building-from-source>`__. For a detailed overview of glog features and +their usage, please refer to the `user guide <#user-guide>`__. + +.. pull-quote:: + [!IMPORTANT] + + The above example requires further `Bazel <#bazel>`__ or + `CMake <#usage-in-projects>`__ setup for use in own 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.7.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 `__ 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 ` +to include it in your source code. Here’s a simple example: + +.. code:: bazel + + cc_binary( + name = "main", + srcs = ["main.cc"], + deps = ["@com_github_google_glog//:glog"], + ) + +CMake +^^^^^ + +glog can be compiled using `CMake `__ 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 `, and see :cmd:`cmake --help` + for the available generators. Also see :cmd:`-T `, 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 +`__ package manager: + +.. code:: bash + + pip install conan + conan install -r conancenter glog/@ + +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 +`__ repository. + +vcpkg +~~~~~ + +You can download and install glog using the `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 +`__ directory. + +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). + +.. pull-quote:: + [!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 (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 uses the format + +:: + + /...log..-). E.g., +// +// LOG(INFO) << "Found " << num_cookies << " cookies"; +// +// You can capture log messages in a string, rather than reporting them +// immediately: +// +// vector errors; +// LOG_STRING(ERROR, &errors) << "Couldn't parse cookie #" << cookie_num; +// +// This pushes back the new error onto 'errors'; if given a nullptr pointer, +// it reports the error via LOG(ERROR). +// +// You can also do conditional logging: +// +// LOG_IF(INFO, num_cookies > 10) << "Got lots of cookies"; +// +// You can also do occasional logging (log every n'th occurrence of an +// event): +// +// LOG_EVERY_N(INFO, 10) << "Got the " << google::COUNTER << "th cookie"; +// +// The above will cause log messages to be output 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 also do occasional conditional logging (log every n'th +// occurrence of an event, when condition is satisfied): +// +// LOG_IF_EVERY_N(INFO, (size > 1024), 10) << "Got the " << google::COUNTER +// << "th big cookie"; +// +// You can log messages the first N times your code executes a line. E.g. +// +// LOG_FIRST_N(INFO, 20) << "Got the " << google::COUNTER << "th cookie"; +// +// Outputs log messages for the first 20 times it is executed. +// +// Analogous SYSLOG, SYSLOG_IF, and SYSLOG_EVERY_N macros are available. +// These log to syslog as well as to the normal logs. If you use these at +// all, you need to be aware that syslog can drastically reduce performance, +// especially if it is configured for remote logging! Don't use these +// unless you fully understand this and have a concrete need to use them. +// Even then, try to minimize your use of them. +// +// There are also "debug mode" logging macros like the ones above: +// +// 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"; +// +// All "debug mode" logging is compiled away to nothing for non-debug mode +// compiles. +// +// We also have +// +// LOG_ASSERT(assertion); +// DLOG_ASSERT(assertion); +// +// which is syntactic sugar for {,D}LOG_IF(FATAL, assert fails) << assertion; +// +// There are "verbose level" logging macros. They look like +// +// VLOG(1) << "I'm printed when you run the program with --v=1 or more"; +// VLOG(2) << "I'm printed when you run the program with --v=2 or more"; +// +// These always log at the INFO log level (when they log at all). +// The verbose logging can also be turned on module-by-module. For instance, +// --vmodule=mapreduce=2,file=1,gfs*=3 --v=0 +// will cause: +// a. VLOG(2) and lower messages to be printed from mapreduce.{h,cc} +// b. VLOG(1) and lower messages to be printed from file.{h,cc} +// c. VLOG(3) and lower messages to be printed from files prefixed with "gfs" +// d. VLOG(0) and lower messages to be printed from elsewhere +// +// The wildcarding functionality shown by (c) supports both '*' (match +// 0 or more characters) and '?' (match any single character) wildcards. +// +// There's also VLOG_IS_ON(n) "verbose level" condition macro. To be used as +// +// if (VLOG_IS_ON(2)) { +// // do some logging preparation and logging +// // that can't be accomplished with just VLOG(2) << ...; +// } +// +// There are also VLOG_IF, VLOG_EVERY_N and VLOG_IF_EVERY_N "verbose level" +// condition macros for sample cases, when some extra computation and +// preparation for logs is not needed. +// VLOG_IF(1, (size > 1024)) +// << "I'm printed when size is more than 1024 and when you run the " +// "program with --v=1 or more"; +// VLOG_EVERY_N(1, 10) +// << "I'm printed every 10th occurrence, and when you run the program " +// "with --v=1 or more. Present occurrence is " << google::COUNTER; +// VLOG_IF_EVERY_N(1, (size > 1024), 10) +// << "I'm printed on every 10th occurrence of case when size is more " +// " than 1024, when you run the program with --v=1 or more. "; +// "Present occurrence is " << google::COUNTER; +// +// The supported severity levels for macros that allow you to specify one +// are (in increasing order of severity) INFO, WARNING, ERROR, and FATAL. +// 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. +// +// There is also the special severity of DFATAL, which logs FATAL in +// debug mode, ERROR in normal mode. +// +// Very important: logging a message at the FATAL severity level causes +// the program to terminate (after the message is logged). +// +// Unless otherwise specified, logs will be written to the filename +// "...log..", followed +// by the date, time, and pid (you can't prevent the date, time, and pid +// from being in the filename). +// +// The logging code takes two flags: +// --v=# set the verbose level +// --logtostderr log all the messages to stderr instead of to logfiles + +// 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: +// +// L A single character, representing the log level +// (eg 'I' for INFO) +// yyyy The year +// mm The month (zero padded; ie 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 as returned by GetTID() +// (this matches the PID on Linux) +// file The file name +// line The line number +// msg The user-supplied message +// +// Example: +// +// 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 the microseconds are useful for comparing events on +// a single machine, clocks on different machines may not be well +// synchronized. Hence, use caution when comparing the low bits of +// timestamps from different machines. + +// Log messages below the GOOGLE_STRIP_LOG level will be compiled away for +// security reasons. See LOG(severity) below. + +// A few definitions of macros that don't generate much code. Since +// LOG(INFO) and its ilk are used all over our code, it's +// better to have compact code for these operations. + +#if GOOGLE_STRIP_LOG == 0 +# define COMPACT_GOOGLE_LOG_INFO google::LogMessage(__FILE__, __LINE__) +# define LOG_TO_STRING_INFO(message) \ + google::LogMessage(__FILE__, __LINE__, google::GLOG_INFO, message) +#else +# define COMPACT_GOOGLE_LOG_INFO google::NullStream() +# define LOG_TO_STRING_INFO(message) google::NullStream() +#endif + +#if GOOGLE_STRIP_LOG <= 1 +# define COMPACT_GOOGLE_LOG_WARNING \ + google::LogMessage(__FILE__, __LINE__, google::GLOG_WARNING) +# define LOG_TO_STRING_WARNING(message) \ + google::LogMessage(__FILE__, __LINE__, google::GLOG_WARNING, message) +#else +# define COMPACT_GOOGLE_LOG_WARNING google::NullStream() +# define LOG_TO_STRING_WARNING(message) google::NullStream() +#endif + +#if GOOGLE_STRIP_LOG <= 2 +# define COMPACT_GOOGLE_LOG_ERROR \ + google::LogMessage(__FILE__, __LINE__, google::GLOG_ERROR) +# define LOG_TO_STRING_ERROR(message) \ + google::LogMessage(__FILE__, __LINE__, google::GLOG_ERROR, message) +#else +# define COMPACT_GOOGLE_LOG_ERROR google::NullStream() +# define LOG_TO_STRING_ERROR(message) google::NullStream() +#endif + +#if GOOGLE_STRIP_LOG <= 3 +# define COMPACT_GOOGLE_LOG_FATAL google::LogMessageFatal(__FILE__, __LINE__) +# define LOG_TO_STRING_FATAL(message) \ + google::LogMessage(__FILE__, __LINE__, google::GLOG_FATAL, message) +#else +# define COMPACT_GOOGLE_LOG_FATAL google::NullStreamFatal() +# define LOG_TO_STRING_FATAL(message) google::NullStreamFatal() +#endif + +#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON) +# define DCHECK_IS_ON() 0 +#else +# define DCHECK_IS_ON() 1 +#endif + +// For DFATAL, we want to use LogMessage (as opposed to +// LogMessageFatal), to be consistent with the original behavior. +#if !DCHECK_IS_ON() +# define COMPACT_GOOGLE_LOG_DFATAL COMPACT_GOOGLE_LOG_ERROR +#elif GOOGLE_STRIP_LOG <= 3 +# define COMPACT_GOOGLE_LOG_DFATAL \ + google::LogMessage(__FILE__, __LINE__, google::GLOG_FATAL) +#else +# define COMPACT_GOOGLE_LOG_DFATAL google::NullStreamFatal() +#endif + +#define GOOGLE_LOG_INFO(counter) \ + google::LogMessage(__FILE__, __LINE__, google::GLOG_INFO, counter, \ + &google::LogMessage::SendToLog) +#define SYSLOG_INFO(counter) \ + google::LogMessage(__FILE__, __LINE__, google::GLOG_INFO, counter, \ + &google::LogMessage::SendToSyslogAndLog) +#define GOOGLE_LOG_WARNING(counter) \ + google::LogMessage(__FILE__, __LINE__, google::GLOG_WARNING, counter, \ + &google::LogMessage::SendToLog) +#define SYSLOG_WARNING(counter) \ + google::LogMessage(__FILE__, __LINE__, google::GLOG_WARNING, counter, \ + &google::LogMessage::SendToSyslogAndLog) +#define GOOGLE_LOG_ERROR(counter) \ + google::LogMessage(__FILE__, __LINE__, google::GLOG_ERROR, counter, \ + &google::LogMessage::SendToLog) +#define SYSLOG_ERROR(counter) \ + google::LogMessage(__FILE__, __LINE__, google::GLOG_ERROR, counter, \ + &google::LogMessage::SendToSyslogAndLog) +#define GOOGLE_LOG_FATAL(counter) \ + google::LogMessage(__FILE__, __LINE__, google::GLOG_FATAL, counter, \ + &google::LogMessage::SendToLog) +#define SYSLOG_FATAL(counter) \ + google::LogMessage(__FILE__, __LINE__, google::GLOG_FATAL, counter, \ + &google::LogMessage::SendToSyslogAndLog) +#define GOOGLE_LOG_DFATAL(counter) \ + google::LogMessage(__FILE__, __LINE__, google::DFATAL_LEVEL, counter, \ + &google::LogMessage::SendToLog) +#define SYSLOG_DFATAL(counter) \ + google::LogMessage(__FILE__, __LINE__, google::DFATAL_LEVEL, counter, \ + &google::LogMessage::SendToSyslogAndLog) + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || \ + defined(__CYGWIN__) || defined(__CYGWIN32__) +// A very useful logging macro to log windows errors: +# define LOG_SYSRESULT(result) \ + if (FAILED(HRESULT_FROM_WIN32(result))) { \ + LPSTR message = nullptr; \ + LPSTR msg = reinterpret_cast(&message); \ + DWORD message_length = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | \ + FORMAT_MESSAGE_FROM_SYSTEM | \ + FORMAT_MESSAGE_IGNORE_INSERTS, \ + 0, result, 0, msg, 100, nullptr); \ + std::unique_ptr release{message, \ + &LocalFree}; \ + if (message_length > 0) { \ + google::LogMessage(__FILE__, __LINE__, google::GLOG_ERROR, 0, \ + &google::LogMessage::SendToLog) \ + .stream() \ + << reinterpret_cast(message); \ + } \ + } +#endif + +// We use the preprocessor's merging operator, "##", so that, e.g., +// LOG(INFO) becomes the token GOOGLE_LOG_INFO. There's some funny +// subtle difference between ostream member streaming functions (e.g., +// ostream::operator<<(int) and ostream non-member streaming functions +// (e.g., ::operator<<(ostream&, string&): it turns out that it's +// impossible to stream something like a string directly to an unnamed +// ostream. We employ a neat hack by calling the stream() member +// function of LogMessage which seems to avoid the problem. +#define LOG(severity) COMPACT_GOOGLE_LOG_##severity.stream() +#define SYSLOG(severity) SYSLOG_##severity(0).stream() + +namespace google { + +// Initialize google's logging library. You will see the program name +// specified by argv0 in log outputs. +GLOG_EXPORT void InitGoogleLogging(const char* argv0); + +class LogMessage; + +#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__ +using CustomPrefixCallback + [[deprecated("Use PrefixFormatterCallback instead.")]] = + void (*)(std::ostream&, const LogMessageInfo&, void*); +#if defined(__GNUG__) +# pragma GCC diagnostic pop +#elif defined(_MSC_VER) +# pragma warning(pop) +#endif // __GNUG__ +[[deprecated("Use InstallPrefixFormatter instead.")]] 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 + +using PrefixFormatterCallback = void (*)(std::ostream&, const LogMessage&, + void*); + +GLOG_EXPORT void InstallPrefixFormatter(PrefixFormatterCallback callback, + void* data = nullptr); + +// Install a function which will be called after LOG(FATAL). Returns the +// previously set function. +GLOG_EXPORT logging_fail_func_t +InstallFailureFunction(logging_fail_func_t fail_func); + +[[deprecated( + "Use the type-safe std::chrono::minutes EnableLogCleaner overload " + "instead.")]] GLOG_EXPORT void +EnableLogCleaner(unsigned int overdue_days); +// Enable/Disable old log cleaner. +GLOG_EXPORT void EnableLogCleaner(const std::chrono::minutes& overdue); +GLOG_EXPORT void DisableLogCleaner(); +GLOG_EXPORT void SetApplicationFingerprint(const std::string& fingerprint); + +class LogSink; // defined below + +// If a non-nullptr sink pointer is given, we push this message to that sink. +// For LOG_TO_SINK we then do normal LOG(severity) logging as well. +// This is useful for capturing messages and passing/storing them +// somewhere more specific than the global log of the process. +// Argument types: +// LogSink* sink; +// LogSeverity severity; +// The cast is to disambiguate nullptr arguments. +#define LOG_TO_SINK(sink, severity) \ + google::LogMessage(__FILE__, __LINE__, google::GLOG_##severity, \ + static_cast(sink), true) \ + .stream() +#define LOG_TO_SINK_BUT_NOT_TO_LOGFILE(sink, severity) \ + google::LogMessage(__FILE__, __LINE__, google::GLOG_##severity, \ + static_cast(sink), false) \ + .stream() + +// If a non-nullptr string pointer is given, we write this message to that +// string. We then do normal LOG(severity) logging as well. This is useful for +// capturing messages and storing them somewhere more specific than the global +// log of the process. Argument types: +// string* message; +// LogSeverity severity; +// The cast is to disambiguate nullptr arguments. +// NOTE: LOG(severity) expands to LogMessage().stream() for the specified +// severity. +#define LOG_TO_STRING(severity, message) \ + LOG_TO_STRING_##severity(static_cast(message)).stream() + +// If a non-nullptr pointer is given, we push the message onto the end +// of a vector of strings; otherwise, we report it with LOG(severity). +// This is handy for capturing messages and perhaps passing them back +// to the caller, rather than reporting them immediately. +// Argument types: +// LogSeverity severity; +// vector *outvec; +// The cast is to disambiguate nullptr arguments. +#define LOG_STRING(severity, outvec) \ + LOG_TO_STRING_##severity(static_cast*>(outvec)) \ + .stream() + +#define LOG_IF(severity, condition) \ + static_cast(0), \ + !(condition) \ + ? (void)0 \ + : google::logging::internal::LogMessageVoidify() & LOG(severity) +#define SYSLOG_IF(severity, condition) \ + static_cast(0), \ + !(condition) \ + ? (void)0 \ + : google::logging::internal::LogMessageVoidify() & SYSLOG(severity) + +#define LOG_ASSERT(condition) \ + LOG_IF(FATAL, !(condition)) << "Assert failed: " #condition +#define SYSLOG_ASSERT(condition) \ + SYSLOG_IF(FATAL, !(condition)) << "Assert failed: " #condition + +// CHECK dies with a fatal error if condition is not true. It is *not* +// controlled by DCHECK_IS_ON(), so the check will be executed regardless of +// compilation mode. Therefore, it is safe to do things like: +// CHECK(fp->Write(x) == 4) +#define CHECK(condition) \ + 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 str) : str_(std::move(str)) {} + explicit operator bool() const noexcept { + return GOOGLE_PREDICT_BRANCH_NOT_TAKEN(str_ != nullptr); + } + std::unique_ptr str_; +}; + +// Function is overloaded for integral types to allow static const +// integrals declared in classes and not defined to be used as arguments to +// CHECK* macros. It's not encouraged though. +template +inline const T& GetReferenceableValue(const T& t) { + return t; +} +inline char GetReferenceableValue(char t) { return t; } +inline unsigned char GetReferenceableValue(unsigned char t) { return t; } +inline signed char GetReferenceableValue(signed char t) { return t; } +inline short GetReferenceableValue(short t) { return t; } +inline unsigned short GetReferenceableValue(unsigned short t) { return t; } +inline int GetReferenceableValue(int t) { return t; } +inline unsigned int GetReferenceableValue(unsigned int t) { return t; } +inline long GetReferenceableValue(long t) { return t; } +inline unsigned long GetReferenceableValue(unsigned long t) { return t; } +inline long long GetReferenceableValue(long long t) { return t; } +inline unsigned long long GetReferenceableValue(unsigned long long t) { + return t; +} + +// This is a dummy class to define the following operator. +struct DummyClassToDefineOperator {}; + +// 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&) { + return out; +} + +// This formats a value for a failing CHECK_XX statement. Ordinarily, +// it uses the definition for operator<<, with a few special cases below. +template +inline void MakeCheckOpValueString(std::ostream* os, const T& v) { + (*os) << v; +} + +// Overrides for char types provide readable values for unprintable +// characters. +template <> +GLOG_EXPORT void MakeCheckOpValueString(std::ostream* os, const char& v); +template <> +GLOG_EXPORT void MakeCheckOpValueString(std::ostream* os, const signed char& v); +template <> +GLOG_EXPORT void MakeCheckOpValueString(std::ostream* os, + const unsigned char& v); + +// Provide printable value for nullptr_t +template <> +GLOG_EXPORT void MakeCheckOpValueString(std::ostream* os, + const std::nullptr_t& v); + +// Build the error message string. Specify no inlining for code size. +template +std::unique_ptr MakeCheckOpString(const T1& v1, const T2& v2, + const char* exprtext) +#if defined(__has_attribute) +# if __has_attribute(used) + __attribute__((noinline)) +# endif +#endif + ; + +// A helper class for formatting "expr (V1 vs. V2)" in a CHECK_XX +// statement. See MakeCheckOpString for sample usage. Other +// approaches were considered: use of a template method (e.g., +// base::BuildCheckOpString(exprtext, base::Print, &v1, +// base::Print, &v2), however this approach has complications +// related to volatile arguments and function-pointer arguments). +class GLOG_EXPORT CheckOpMessageBuilder { + public: + // Inserts "exprtext" and " (" to the stream. + explicit CheckOpMessageBuilder(const char* exprtext); + // Deletes "stream_". + ~CheckOpMessageBuilder(); + // For inserting the first variable. + std::ostream* ForVar1() { return stream_; } + // For inserting the second variable (adds an intermediate " vs. "). + std::ostream* ForVar2(); + // Get the result (inserts the closing ")"). + std::unique_ptr NewString(); + + private: + std::ostringstream* stream_; +}; + +template +std::unique_ptr MakeCheckOpString(const T1& v1, const T2& v2, + const char* exprtext) { + CheckOpMessageBuilder comb(exprtext); + MakeCheckOpValueString(comb.ForVar1(), v1); + MakeCheckOpValueString(comb.ForVar2(), v2); + return comb.NewString(); +} + +// Helper functions for CHECK_OP macro. +// 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 \ + inline std::unique_ptr 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 name##Impl(int v1, int v2, \ + const char* exprtext) { \ + return name##Impl(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_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. +// Don't use this macro directly in your code, use CHECK_EQ et al below. + +#if defined(STATIC_ANALYSIS) +// Only for static analysis tool to know that it is equivalent to assert +# define CHECK_OP_LOG(name, op, val1, val2, log) CHECK((val1)op(val2)) +#elif DCHECK_IS_ON() +// In debug mode, avoid constructing CheckOpStrings if possible, +// to reduce the overhead of CHECK statements by 2x. +// Real DCHECK-heavy tests have seen 1.5x speedups. + +// The meaning of "string" might be different between now and +// when this macro gets invoked (e.g., if someone is experimenting +// 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 _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() +#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)) \ + log(__FILE__, __LINE__, _result).stream() +#endif // STATIC_ANALYSIS, DCHECK_IS_ON() + +#if GOOGLE_STRIP_LOG <= 3 +# define CHECK_OP(name, op, val1, val2) \ + CHECK_OP_LOG(name, op, val1, val2, google::LogMessageFatal) +#else +# define CHECK_OP(name, op, val1, val2) \ + CHECK_OP_LOG(name, op, val1, val2, google::NullStreamFatal) +#endif // STRIP_LOG <= 3 + +// Equality/Inequality checks - compare two values, and log a FATAL message +// including the two values when the result is not as expected. The values +// must have operator<<(ostream, ...) defined. +// +// You may append to the error message like so: +// 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: +// CHECK_EQ(string("abc")[1], 'b'); +// +// WARNING: These don't compile correctly if one of the arguments is a pointer +// and the other is nullptr. To work around this, simply static_cast nullptr to +// the type of the desired pointer. + +#define CHECK_EQ(val1, val2) CHECK_OP(_EQ, ==, val1, val2) +#define CHECK_NE(val1, val2) CHECK_OP(_NE, !=, val1, val2) +#define CHECK_LE(val1, val2) CHECK_OP(_LE, <=, val1, val2) +#define CHECK_LT(val1, val2) CHECK_OP(_LT, <, val1, val2) +#define CHECK_GE(val1, val2) CHECK_OP(_GE, >=, val1, val2) +#define CHECK_GT(val1, val2) CHECK_OP(_GT, >, val1, val2) + +// 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)) + +// 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 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_) + +// String (char*) equality/inequality checks. +// CASE versions are case-insensitive. +// +// Note that "s1" and "s2" may be temporary strings which are destroyed +// by the compiler at the end of the current "full expression" +// (e.g. CHECK_STREQ(Foo().c_str(), Bar().c_str())). + +#define CHECK_STREQ(s1, s2) CHECK_STROP(strcmp, ==, true, s1, s2) +#define CHECK_STRNE(s1, s2) CHECK_STROP(strcmp, !=, false, s1, s2) +#define CHECK_STRCASEEQ(s1, s2) CHECK_STROP(strcasecmp, ==, true, s1, s2) +#define CHECK_STRCASENE(s1, s2) CHECK_STROP(strcasecmp, !=, false, s1, s2) + +#define CHECK_INDEX(I, A) CHECK(I < (sizeof(A) / sizeof(A[0]))) +#define CHECK_BOUND(B, A) CHECK(B <= (sizeof(A) / sizeof(A[0]))) + +#define CHECK_DOUBLE_EQ(val1, val2) \ + do { \ + CHECK_LE((val1), (val2) + 0.000000000000001L); \ + CHECK_GE((val1), (val2)-0.000000000000001L); \ + } while (0) + +#define CHECK_NEAR(val1, val2, margin) \ + do { \ + CHECK_LE((val1), (val2) + (margin)); \ + CHECK_GE((val1), (val2) - (margin)); \ + } while (0) + +// perror()..googly style! +// +// PLOG() and PLOG_IF() and PCHECK() behave exactly like their LOG* and +// CHECK equivalents with the addition that they postpend a description +// of the current state of errno to their output lines. + +#define PLOG(severity) GOOGLE_PLOG(severity, 0).stream() + +#define GOOGLE_PLOG(severity, counter) \ + google::ErrnoLogMessage(__FILE__, __LINE__, google::GLOG_##severity, \ + counter, &google::LogMessage::SendToLog) + +#define PLOG_IF(severity, condition) \ + static_cast(0), \ + !(condition) \ + ? (void)0 \ + : google::logging::internal::LogMessageVoidify() & PLOG(severity) + +// A CHECK() macro that postpends errno if the condition is false. E.g. +// +// if (poll(fds, nfds, timeout) == -1) { PCHECK(errno == EINTR); ... } +#define PCHECK(condition) \ + PLOG_IF(FATAL, GOOGLE_PREDICT_BRANCH_NOT_TAKEN(!(condition))) \ + << "Check failed: " #condition " " + +// A CHECK() macro that lets you assert the success of a function that +// returns -1 and sets errno in case of an error. E.g. +// +// CHECK_ERR(mkdir(path, 0700)); +// +// or +// +// int fd = open(filename, flags); CHECK_ERR(fd) << ": open " << filename; +#define CHECK_ERR(invocation) \ + PLOG_IF(FATAL, GOOGLE_PREDICT_BRANCH_NOT_TAKEN((invocation) == -1)) \ + << #invocation + +// Use macro expansion to create, for each use of LOG_EVERY_N(), static +// variables with the __LINE__ expansion as part of the variable name. +#define LOG_EVERY_N_VARNAME(base, line) LOG_EVERY_N_VARNAME_CONCAT(base, line) +#define LOG_EVERY_N_VARNAME_CONCAT(base, line) base##line + +#define LOG_OCCURRENCES LOG_EVERY_N_VARNAME(occurrences_, __LINE__) +#define LOG_OCCURRENCES_MOD_N LOG_EVERY_N_VARNAME(occurrences_mod_n_, __LINE__) + +#define LOG_TIME_PERIOD LOG_EVERY_N_VARNAME(timePeriod_, __LINE__) +#define LOG_PREVIOUS_TIME_RAW LOG_EVERY_N_VARNAME(previousTimeRaw_, __LINE__) +#define LOG_TIME_DELTA LOG_EVERY_N_VARNAME(deltaTime_, __LINE__) +#define LOG_CURRENT_TIME LOG_EVERY_N_VARNAME(currentTime_, __LINE__) +#define LOG_PREVIOUS_TIME LOG_EVERY_N_VARNAME(previousTime_, __LINE__) + +} // namespace google + +namespace google { + +#define SOME_KIND_OF_LOG_EVERY_T(severity, seconds) \ + constexpr std::chrono::nanoseconds LOG_TIME_PERIOD = \ + std::chrono::duration_cast( \ + std::chrono::duration(seconds)); \ + static std::atomic LOG_PREVIOUS_TIME_RAW; \ + GLOG_IFDEF_THREAD_SANITIZER(AnnotateBenignRaceSized( \ + __FILE__, __LINE__, &LOG_TIME_PERIOD, sizeof(google::int64), "")); \ + GLOG_IFDEF_THREAD_SANITIZER(AnnotateBenignRaceSized( \ + __FILE__, __LINE__, &LOG_PREVIOUS_TIME_RAW, sizeof(google::int64), "")); \ + const auto LOG_CURRENT_TIME = \ + std::chrono::duration_cast( \ + std::chrono::steady_clock::now().time_since_epoch()); \ + const auto LOG_PREVIOUS_TIME = \ + LOG_PREVIOUS_TIME_RAW.load(std::memory_order_relaxed); \ + const auto LOG_TIME_DELTA = \ + LOG_CURRENT_TIME - std::chrono::nanoseconds(LOG_PREVIOUS_TIME); \ + if (LOG_TIME_DELTA > LOG_TIME_PERIOD) \ + LOG_PREVIOUS_TIME_RAW.store( \ + std::chrono::duration_cast(LOG_CURRENT_TIME) \ + .count(), \ + std::memory_order_relaxed); \ + if (LOG_TIME_DELTA > LOG_TIME_PERIOD) \ + google::LogMessage(__FILE__, __LINE__, google::GLOG_##severity).stream() + +#define SOME_KIND_OF_LOG_EVERY_N(severity, n, what_to_do) \ + static std::atomic LOG_OCCURRENCES(0), LOG_OCCURRENCES_MOD_N(0); \ + GLOG_IFDEF_THREAD_SANITIZER(AnnotateBenignRaceSized( \ + __FILE__, __LINE__, &LOG_OCCURRENCES, sizeof(int), "")); \ + GLOG_IFDEF_THREAD_SANITIZER(AnnotateBenignRaceSized( \ + __FILE__, __LINE__, &LOG_OCCURRENCES_MOD_N, sizeof(int), "")); \ + ++LOG_OCCURRENCES; \ + if (++LOG_OCCURRENCES_MOD_N > n) LOG_OCCURRENCES_MOD_N -= n; \ + if (LOG_OCCURRENCES_MOD_N == 1) \ + google::LogMessage(__FILE__, __LINE__, google::GLOG_##severity, \ + LOG_OCCURRENCES, &what_to_do) \ + .stream() + +#define SOME_KIND_OF_LOG_IF_EVERY_N(severity, condition, n, what_to_do) \ + static std::atomic LOG_OCCURRENCES(0), LOG_OCCURRENCES_MOD_N(0); \ + GLOG_IFDEF_THREAD_SANITIZER(AnnotateBenignRaceSized( \ + __FILE__, __LINE__, &LOG_OCCURRENCES, sizeof(int), "")); \ + GLOG_IFDEF_THREAD_SANITIZER(AnnotateBenignRaceSized( \ + __FILE__, __LINE__, &LOG_OCCURRENCES_MOD_N, sizeof(int), "")); \ + ++LOG_OCCURRENCES; \ + if ((condition) && \ + ((LOG_OCCURRENCES_MOD_N = (LOG_OCCURRENCES_MOD_N + 1) % n) == (1 % n))) \ + google::LogMessage(__FILE__, __LINE__, google::GLOG_##severity, \ + LOG_OCCURRENCES, &what_to_do) \ + .stream() + +#define SOME_KIND_OF_PLOG_EVERY_N(severity, n, what_to_do) \ + static std::atomic LOG_OCCURRENCES(0), LOG_OCCURRENCES_MOD_N(0); \ + GLOG_IFDEF_THREAD_SANITIZER(AnnotateBenignRaceSized( \ + __FILE__, __LINE__, &LOG_OCCURRENCES, sizeof(int), "")); \ + GLOG_IFDEF_THREAD_SANITIZER(AnnotateBenignRaceSized( \ + __FILE__, __LINE__, &LOG_OCCURRENCES_MOD_N, sizeof(int), "")); \ + ++LOG_OCCURRENCES; \ + if (++LOG_OCCURRENCES_MOD_N > n) LOG_OCCURRENCES_MOD_N -= n; \ + if (LOG_OCCURRENCES_MOD_N == 1) \ + google::ErrnoLogMessage(__FILE__, __LINE__, google::GLOG_##severity, \ + LOG_OCCURRENCES, &what_to_do) \ + .stream() + +#define SOME_KIND_OF_LOG_FIRST_N(severity, n, what_to_do) \ + static std::atomic LOG_OCCURRENCES(0); \ + GLOG_IFDEF_THREAD_SANITIZER(AnnotateBenignRaceSized( \ + __FILE__, __LINE__, &LOG_OCCURRENCES, sizeof(int), "")); \ + if (LOG_OCCURRENCES <= n) ++LOG_OCCURRENCES; \ + if (LOG_OCCURRENCES <= n) \ + google::LogMessage(__FILE__, __LINE__, google::GLOG_##severity, \ + LOG_OCCURRENCES, &what_to_do) \ + .stream() + +namespace logging { +namespace internal { +template +struct CompileAssert {}; +struct CrashReason; +} // namespace internal +} // namespace logging + +#define LOG_EVERY_N(severity, n) \ + SOME_KIND_OF_LOG_EVERY_N(severity, (n), google::LogMessage::SendToLog) + +#define LOG_EVERY_T(severity, T) SOME_KIND_OF_LOG_EVERY_T(severity, (T)) + +#define SYSLOG_EVERY_N(severity, n) \ + SOME_KIND_OF_LOG_EVERY_N(severity, (n), \ + google::LogMessage::SendToSyslogAndLog) + +#define PLOG_EVERY_N(severity, n) \ + SOME_KIND_OF_PLOG_EVERY_N(severity, (n), google::LogMessage::SendToLog) + +#define LOG_FIRST_N(severity, n) \ + SOME_KIND_OF_LOG_FIRST_N(severity, (n), google::LogMessage::SendToLog) + +#define LOG_IF_EVERY_N(severity, condition, n) \ + SOME_KIND_OF_LOG_IF_EVERY_N(severity, (condition), (n), \ + google::LogMessage::SendToLog) + +// We want the special COUNTER value available for LOG_EVERY_X()'ed messages +struct Counter_t {}; +GLOG_INLINE_VARIABLE constexpr Counter_t COUNTER{}; + +#ifdef GLOG_NO_ABBREVIATED_SEVERITIES +// wingdi.h defines ERROR to be 0. When we call LOG(ERROR), it gets +// substituted with 0, and it expands to COMPACT_GOOGLE_LOG_0. To allow us +// to keep using this syntax, we define this macro to do the same thing +// as COMPACT_GOOGLE_LOG_ERROR. +# define COMPACT_GOOGLE_LOG_0 COMPACT_GOOGLE_LOG_ERROR +# define SYSLOG_0 SYSLOG_ERROR +# define LOG_TO_STRING_0 LOG_TO_STRING_ERROR +// Needed for LOG_IS_ON(ERROR). +GLOG_INLINE_VARIABLE +constexpr LogSeverity GLOG_0 = GLOG_ERROR; +#else +// Users may include windows.h after logging.h without +// GLOG_NO_ABBREVIATED_SEVERITIES nor WIN32_LEAN_AND_MEAN. +// For this case, we cannot detect if ERROR is defined before users +// actually use ERROR. Let's make an undefined symbol to warn users. +# define GLOG_ERROR_MSG \ + ERROR_macro_is_defined_Define_GLOG_NO_ABBREVIATED_SEVERITIES_before_including_logging_h_See_the_document_for_detail +# define COMPACT_GOOGLE_LOG_0 GLOG_ERROR_MSG +# define SYSLOG_0 GLOG_ERROR_MSG +# define LOG_TO_STRING_0 GLOG_ERROR_MSG +# define GLOG_0 GLOG_ERROR_MSG +#endif + +// Plus some debug-logging macros that get compiled to nothing for production + +#if DCHECK_IS_ON() + +# define DLOG(severity) LOG(severity) +# define DVLOG(verboselevel) VLOG(verboselevel) +# define DLOG_IF(severity, condition) LOG_IF(severity, condition) +# define DLOG_EVERY_N(severity, n) LOG_EVERY_N(severity, n) +# define DLOG_IF_EVERY_N(severity, condition, n) \ + LOG_IF_EVERY_N(severity, condition, n) +# define DLOG_FIRST_N(severity, n) LOG_FIRST_N(severity, n) +# define DLOG_EVERY_T(severity, T) LOG_EVERY_T(severity, T) +# define DLOG_ASSERT(condition) LOG_ASSERT(condition) + +// debug-only checking. executed if DCHECK_IS_ON(). +# define DCHECK(condition) CHECK(condition) +# define DCHECK_EQ(val1, val2) CHECK_EQ(val1, val2) +# define DCHECK_NE(val1, val2) CHECK_NE(val1, val2) +# define DCHECK_LE(val1, val2) CHECK_LE(val1, val2) +# define DCHECK_LT(val1, val2) CHECK_LT(val1, val2) +# define DCHECK_GE(val1, val2) CHECK_GE(val1, val2) +# define DCHECK_GT(val1, val2) CHECK_GT(val1, val2) +# define DCHECK_NOTNULL(val) CHECK_NOTNULL(val) +# define DCHECK_STREQ(str1, str2) CHECK_STREQ(str1, str2) +# define DCHECK_STRCASEEQ(str1, str2) CHECK_STRCASEEQ(str1, str2) +# define DCHECK_STRNE(str1, str2) CHECK_STRNE(str1, str2) +# define DCHECK_STRCASENE(str1, str2) CHECK_STRCASENE(str1, str2) + +#else // !DCHECK_IS_ON() + +# define DLOG(severity) \ + static_cast(0), \ + true ? (void)0 \ + : google::logging::internal::LogMessageVoidify() & LOG(severity) + +# define DVLOG(verboselevel) \ + static_cast(0), \ + (true || !VLOG_IS_ON(verboselevel)) \ + ? (void)0 \ + : google::logging::internal::LogMessageVoidify() & LOG(INFO) + +# define DLOG_IF(severity, condition) \ + static_cast(0), \ + (true || !(condition)) \ + ? (void)0 \ + : google::logging::internal::LogMessageVoidify() & LOG(severity) + +# define DLOG_EVERY_N(severity, n) \ + static_cast(0), \ + true ? (void)0 \ + : google::logging::internal::LogMessageVoidify() & LOG(severity) + +# define DLOG_IF_EVERY_N(severity, condition, n) \ + static_cast(0), \ + (true || !(condition)) \ + ? (void)0 \ + : google::logging::internal::LogMessageVoidify() & LOG(severity) + +# define DLOG_FIRST_N(severity, n) \ + static_cast(0), \ + true ? (void)0 \ + : google::logging::internal::LogMessageVoidify() & LOG(severity) + +# define DLOG_EVERY_T(severity, T) \ + static_cast(0), \ + true ? (void)0 \ + : google::logging::internal::LogMessageVoidify() & LOG(severity) + +# define DLOG_ASSERT(condition) \ + static_cast(0), true ? (void)0 : (LOG_ASSERT(condition)) + +// MSVC warning C4127: conditional expression is constant +# define DCHECK(condition) \ + GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \ + while (false) GLOG_MSVC_POP_WARNING() CHECK(condition) + +# define DCHECK_EQ(val1, val2) \ + GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \ + while (false) GLOG_MSVC_POP_WARNING() CHECK_EQ(val1, val2) + +# define DCHECK_NE(val1, val2) \ + GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \ + while (false) GLOG_MSVC_POP_WARNING() CHECK_NE(val1, val2) + +# define DCHECK_LE(val1, val2) \ + GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \ + while (false) GLOG_MSVC_POP_WARNING() CHECK_LE(val1, val2) + +# define DCHECK_LT(val1, val2) \ + GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \ + while (false) GLOG_MSVC_POP_WARNING() CHECK_LT(val1, val2) + +# define DCHECK_GE(val1, val2) \ + GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \ + while (false) GLOG_MSVC_POP_WARNING() CHECK_GE(val1, val2) + +# define DCHECK_GT(val1, val2) \ + GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \ + while (false) GLOG_MSVC_POP_WARNING() CHECK_GT(val1, val2) + +// You may see warnings in release mode if you don't use the return +// value of DCHECK_NOTNULL. Please just use DCHECK for such cases. +# define DCHECK_NOTNULL(val) (val) + +# define DCHECK_STREQ(str1, str2) \ + GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \ + while (false) GLOG_MSVC_POP_WARNING() CHECK_STREQ(str1, str2) + +# define DCHECK_STRCASEEQ(str1, str2) \ + GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \ + while (false) GLOG_MSVC_POP_WARNING() CHECK_STRCASEEQ(str1, str2) + +# define DCHECK_STRNE(str1, str2) \ + GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \ + while (false) GLOG_MSVC_POP_WARNING() CHECK_STRNE(str1, str2) + +# define DCHECK_STRCASENE(str1, str2) \ + GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \ + while (false) GLOG_MSVC_POP_WARNING() CHECK_STRCASENE(str1, str2) + +#endif // DCHECK_IS_ON() + +// Log only in verbose mode. + +#define VLOG(verboselevel) LOG_IF(INFO, VLOG_IS_ON(verboselevel)) + +#define VLOG_IF(verboselevel, condition) \ + LOG_IF(INFO, (condition) && VLOG_IS_ON(verboselevel)) + +#define VLOG_EVERY_N(verboselevel, n) \ + LOG_IF_EVERY_N(INFO, VLOG_IS_ON(verboselevel), n) + +#define VLOG_IF_EVERY_N(verboselevel, condition, n) \ + LOG_IF_EVERY_N(INFO, (condition) && VLOG_IS_ON(verboselevel), n) + +namespace base_logging { + +// LogMessage::LogStream is a std::ostream backed by this streambuf. +// This class ignores overflow and leaves two bytes at the end of the +// buffer to allow for a '\n' and '\0'. +class GLOG_EXPORT LogStreamBuf : public std::streambuf { + public: + // REQUIREMENTS: "len" must be >= 2 to account for the '\n' and '\0'. + LogStreamBuf(char* buf, int len) { setp(buf, buf + len - 2); } + + // This effectively ignores overflow. + int_type overflow(int_type ch) { return ch; } + + // Legacy public ostrstream method. + size_t pcount() const { return static_cast(pptr() - pbase()); } + char* pbase() const { return std::streambuf::pbase(); } +}; + +} // 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. +// When you finish streaming to it, ~LogMessage is called and the +// full message gets streamed to the appropriate destination. +// +// You shouldn't actually use LogMessage's constructor to log things, +// though. You should use the LOG() macro (and variants thereof) +// above. +class GLOG_EXPORT LogMessage { + public: + enum { + // Passing kNoLogPrefix for the line number disables the + // log-message prefix. Useful for using the LogMessage + // infrastructure as a printing utility. See also the --log_prefix + // flag for controlling the log-message prefix on an + // application-wide basis. + kNoLogPrefix = -1 + }; + + // LogStream inherit from non-DLL-exported class (std::ostrstream) + // and VC++ produces a warning for this situation. + // However, MSDN says "C4275 can be ignored in Microsoft Visual C++ + // 2005 if you are deriving from a type in the Standard C++ Library" + // http://msdn.microsoft.com/en-us/library/3tdb471s(VS.80).aspx + // Let's just ignore the warning. + GLOG_MSVC_PUSH_DISABLE_WARNING(4275) + class GLOG_EXPORT LogStream : public std::ostream { + GLOG_MSVC_POP_WARNING() + public: + // In some cases, like when compiling glog as a static library with GCC and + // linking against a Clang-built executable, this constructor will be + // removed by the linker. We use this attribute to prevent the linker from + // discarding it. + GLOG_USED + LogStream(char* buf, int len, int64 ctr) + : std::ostream(nullptr), streambuf_(buf, len), ctr_(ctr), self_(this) { + rdbuf(&streambuf_); + } + + LogStream(LogStream&& other) noexcept + : std::ostream(nullptr), + streambuf_(std::move(other.streambuf_)), + ctr_(std::exchange(other.ctr_, 0)), + self_(this) { + rdbuf(&streambuf_); + } + + LogStream& operator=(LogStream&& other) noexcept { + streambuf_ = std::move(other.streambuf_); + ctr_ = std::exchange(other.ctr_, 0); + rdbuf(&streambuf_); + return *this; + } + + int64 ctr() const { return ctr_; } + void set_ctr(int64 ctr) { ctr_ = ctr; } + LogStream* self() const { return self_; } + + // Legacy std::streambuf methods. + size_t pcount() const { return streambuf_.pcount(); } + char* pbase() const { return streambuf_.pbase(); } + char* str() const { return pbase(); } + + LogStream(const LogStream&) = delete; + LogStream& operator=(const LogStream&) = delete; + + private: + base_logging::LogStreamBuf streambuf_; + int64 ctr_; // Counter hack (for the LOG_EVERY_X() macro) + LogStream* self_; // Consistency check hack + }; + + public: + // icc 8 requires this typedef to avoid an internal compiler error. + typedef void (LogMessage::*SendMethod)(); + + LogMessage(const char* file, int line, LogSeverity severity, int64 ctr, + SendMethod send_method); + + // Two special constructors that generate reduced amounts of code at + // LOG call sites for common cases. + + // Used for LOG(INFO): Implied are: + // severity = INFO, ctr = 0, send_method = &LogMessage::SendToLog. + // + // Using this constructor instead of the more complex constructor above + // saves 19 bytes per call site. + LogMessage(const char* file, int line); + + // Used for LOG(severity) where severity != INFO. Implied + // are: ctr = 0, send_method = &LogMessage::SendToLog + // + // Using this constructor instead of the more complex constructor above + // saves 17 bytes per call site. + LogMessage(const char* file, int line, LogSeverity severity); + + // Constructor to log this message to a specified sink (if not nullptr). + // Implied are: ctr = 0, send_method = &LogMessage::SendToSinkAndLog if + // also_send_to_log is true, send_method = &LogMessage::SendToSink otherwise. + LogMessage(const char* file, int line, LogSeverity severity, LogSink* sink, + bool also_send_to_log); + + // Constructor where we also give a vector pointer + // for storing the messages (if the pointer is not nullptr). + // Implied are: ctr = 0, send_method = &LogMessage::SaveOrSendToLog. + LogMessage(const char* file, int line, LogSeverity severity, + std::vector* outvec); + + // Constructor where we also give a string pointer for storing the + // message (if the pointer is not nullptr). Implied are: ctr = 0, + // send_method = &LogMessage::WriteToStringAndLog. + LogMessage(const char* file, int line, LogSeverity severity, + std::string* message); + + // A special constructor used for check failures + LogMessage(const char* file, int line, + const logging::internal::CheckOpString& result); + + ~LogMessage() noexcept(false); + + // Flush a buffered message to the sink set in the constructor. Always + // called by the destructor, it may also be called from elsewhere if + // needed. Only the first call is actioned; any later ones are ignored. + void Flush(); + + // An arbitrary limit on the length of a single log message. This + // is so that streaming can be done more efficiently. + static const size_t kMaxLogMessageLen; + + // These should not be called directly outside of logging.*, + // only passed as SendMethod arguments to other LogMessage methods: + void SendToLog(); // Actually dispatch to the logs + void SendToSyslogAndLog(); // Actually dispatch to syslog and the logs + + // Call abort() or similar to perform LOG(FATAL) crash. + [[noreturn]] static void Fail(); + + std::ostream& stream(); + + int preserved_errno() const; + + // Must be called without the log_mutex held. (L < log_mutex) + static int64 num_messages(int severity); + + [[deprecated("Use LogMessage::time() instead.")]] const LogMessageTime& + getLogMessageTime() const { + return time(); + } + + 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; + + LogMessage(const LogMessage&) = delete; + LogMessage& operator=(const LogMessage&) = delete; + + private: + // Fully internal SendMethod cases: + void SendToSinkAndLog(); // Send to sink if provided and dispatch to the logs + void SendToSink(); // Send to sink if provided, do nothing otherwise. + + // Write to string if provided and dispatch to the logs. + void WriteToStringAndLog(); + + void SaveOrSendToLog(); // Save to stringvec if provided, else to logs + + void Init(const char* file, int line, LogSeverity severity, + void (LogMessage::*send_method)()); + + // Used to fill in crash information during LOG(FATAL) failures. + void RecordCrashReason(logging::internal::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_; + LogMessageTime time_; + + friend class LogDestination; +}; + +// This class happens to be thread-hostile because all instances share +// a single data buffer, but since it can only be created just before +// the process dies, we don't worry so much. +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); +}; + +// A non-macro interface to the log facility; (useful +// when the logging level is not a compile-time constant). +inline void LogAtLevel(LogSeverity severity, std::string const& msg) { + LogMessage(__FILE__, __LINE__, severity).stream() << msg; +} + +// A macro alternative of LogAtLevel. New code may want to use this +// version since there are two advantages: 1. this version outputs the +// file name and the line number where this macro is put like other +// LOG macros, 2. this macro can be used as C++ stream. +#define LOG_AT_LEVEL(severity) \ + google::LogMessage(__FILE__, __LINE__, severity).stream() + +// 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. +GLOG_EXPORT std::ostream& operator<<(std::ostream& os, const Counter_t&); + +// Derived class for PLOG*() above. +class GLOG_EXPORT ErrnoLogMessage : public LogMessage { + public: + ErrnoLogMessage(const char* file, int line, LogSeverity severity, int64 ctr, + void (LogMessage::*send_method)()); + + // Postpends ": strerror(errno) [errno]". + ~ErrnoLogMessage(); + + private: + ErrnoLogMessage(const ErrnoLogMessage&); + void operator=(const ErrnoLogMessage&); +}; + +// This class is used to explicitly ignore values in the conditional +// 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 +T CheckNotNull(const char* file, int line, const char* names, T&& t) { + if (t == nullptr) { + LogMessageFatal(file, line, std::make_unique(names)); + } + return std::forward(t); +} + +struct LogMessageVoidify { + // This has to be an operator with a precedence lower than << but + // higher than ?: + void operator&(std::ostream&) noexcept {} +}; + +} // 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); + +// Flushes all log files that contains messages that are at least of +// the specified severity level. Thread-hostile because it ignores +// locking -- used for catastrophic failures. +GLOG_EXPORT void FlushLogFilesUnsafe(LogSeverity min_severity); + +// +// Set the destination to which a particular severity level of log +// messages is sent. If base_filename is "", it means "don't log this +// severity". Thread-safe. +// +GLOG_EXPORT void SetLogDestination(LogSeverity severity, + const char* base_filename); + +// +// Set the basename of the symlink to the latest log file at a given +// severity. If symlink_basename is empty, do not make a symlink. If +// you don't call this function, the symlink basename is the +// invocation name of the program. Thread-safe. +// +GLOG_EXPORT void SetLogSymlink(LogSeverity severity, + const char* symlink_basename); + +// +// Used to send logs to some other kind of destination +// Users should subclass LogSink and override send to do whatever they want. +// Implementations must be thread-safe because a shared instance will +// be called from whichever thread ran the LOG(XXX) line. +class GLOG_EXPORT LogSink { + public: + virtual ~LogSink(); + + // Sink's logging logic (message_len is such as to exclude '\n' at the end). + // This method can't use LOG() or CHECK() as logging system mutex(s) are held + // 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); + // 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. + // It will be called after each send() returns, + // but before that LogMessage exits or crashes. + // By default this function does nothing. + // Using this function one can implement complex logic for send() + // that itself involves logging; and do all this w/o causing deadlocks and + // inconsistent rearrangement of log messages. + // E.g. if a LogSink has thread-specific actions, the send() method + // can simply add the message to a queue and wake up another thread that + // handles real logging while itself making some LOG() calls; + // WaitTillSent() can be implemented to wait for that logic to complete. + // See our unittest for an example. + virtual void WaitTillSent(); + + // 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); +}; + +// Add or remove a LogSink as a consumer of logging data. Thread-safe. +GLOG_EXPORT void AddLogSink(LogSink* destination); +GLOG_EXPORT void RemoveLogSink(LogSink* destination); + +// +// Specify an "extension" added to the filename specified via +// SetLogDestination. This applies to all severity levels. It's +// often used to append the port we're listening on to the logfile +// name. Thread-safe. +// +GLOG_EXPORT void SetLogFilenameExtension(const char* filename_extension); + +// +// Make it so that all log messages of at least a particular severity +// are logged to stderr (in addition to logging to the usual log +// file(s)). Thread-safe. +// +GLOG_EXPORT void SetStderrLogging(LogSeverity min_severity); + +// +// Make it so that all log messages go only to stderr. Thread-safe. +// +GLOG_EXPORT void LogToStderr(); + +// +// Make it so that all log messages of at least a particular severity are +// logged via email to a list of addresses (in addition to logging to the +// usual log file(s)). The list of addresses is just a string containing +// the email addresses to send to (separated by spaces, say). Thread-safe. +// +GLOG_EXPORT void SetEmailLogging(LogSeverity min_severity, + const char* addresses); + +// A simple function that sends email. dest is a comma-separated +// list of addresses. Thread-safe. +GLOG_EXPORT bool SendEmail(const char* dest, const char* subject, + const char* body); + +GLOG_EXPORT const std::vector& GetLoggingDirectories(); + +// Print any fatal message again -- useful to call from signal handler +// so that the last thing in the output is the fatal message. +// Thread-hostile, but a race is unlikely. +GLOG_EXPORT void ReprintFatalMessage(); + +// Truncate a log file that may be the append-only output of multiple +// processes and hence can't simply be renamed/reopened (typically a +// stdout/stderr). If the file "path" is > "limit" bytes, copy the +// last "keep" bytes to offset 0 and truncate the rest. Since we could +// be racing with other writers, this approach has the potential to +// lose very small amounts of data. For security, only follow symlinks +// if the path is /proc/self/fd/* +GLOG_EXPORT void TruncateLogFile(const char* path, uint64 limit, uint64 keep); + +// Truncate stdout and stderr if they are over the value specified by +// --max_log_size; keep the final 1MB. This function has the same +// race condition as TruncateLogFile. +GLOG_EXPORT void TruncateStdoutStderr(); + +// Return the string representation of the provided LogSeverity level. +// Thread-safe. +GLOG_EXPORT const char* GetLogSeverityName(LogSeverity severity); + +// --------------------------------------------------------------------- +// Implementation details that are not useful to most clients +// --------------------------------------------------------------------- + +// A Logger is the interface used by logging modules to emit entries +// to a log. A typical implementation will dump formatted data to a +// sequence of files. We also provide interfaces that will forward +// the data to another thread so that the invoker never blocks. +// Implementations should be thread-safe since the logging system +// will write to them from multiple threads. + +namespace base { + +class GLOG_EXPORT Logger { + public: + virtual ~Logger(); + + // Writes "message[0,message_len-1]" corresponding to an event that + // occurred at "timestamp". If "force_flush" is true, the log file + // is flushed immediately. + // + // The input message has already been formatted as deemed + // 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); + + // Flush any buffered messages + virtual void Flush() = 0; + + // Get the current LOG file size. + // The returned value is approximate since some + // logged data may not have been flushed to disk yet. + virtual uint32 LogSize() = 0; +}; + +// Get the logger for the specified severity level. The logger +// remains the property of the logging module and should not be +// deleted by the caller. Thread-safe. +extern GLOG_EXPORT Logger* GetLogger(LogSeverity level); + +// Set the logger for the specified severity level. The logger +// becomes the property of the logging module and should not +// be deleted by the caller. Thread-safe. +extern GLOG_EXPORT void SetLogger(LogSeverity level, Logger* logger); + +} // namespace base + +// A class for which we define operator<<, which does nothing. +class GLOG_EXPORT NullStream : public LogMessage::LogStream { + public: + // Initialize the LogStream so the messages can be written somewhere + // (they'll never be actually displayed). This will be needed if a + // NullStream& is implicitly converted to LogStream&, in which case + // the overloaded NullStream::operator<< will not be invoked. + NullStream(); + NullStream(const char* /*file*/, int /*line*/, + const logging::internal::CheckOpString& /*result*/); + NullStream& stream(); + + private: + // A very short buffer for messages (which we discard anyway). This + // will be needed if NullStream& converted to LogStream& (e.g. as a + // result of a conditional expression). + char message_buffer_[3]; +}; + +// Do nothing. This operator is inline, allowing the message to be +// compiled away. The message will not be compiled away if we do +// something like (flag ? LOG(INFO) : LOG(ERROR)) << message; when +// SKIP_LOG=WARNING. In those cases, NullStream will be implicitly +// converted to LogStream and the message will be computed and then +// quietly discarded. +template +inline NullStream& operator<<(NullStream& str, const T&) { + return str; +} + +// Similar to NullStream, but aborts the program (without stack +// trace), like LogMessageFatal. +class GLOG_EXPORT NullStreamFatal : public NullStream { + public: + using NullStream::NullStream; + [[noreturn]] + // Prevent the linker from discarding the destructor. + GLOG_USED ~NullStreamFatal(); +}; + +// Install a signal handler that will dump signal information and a stack +// trace when the program crashes on certain signals. We'll install the +// signal handler for the following signals. +// +// SIGSEGV, SIGILL, SIGFPE, SIGABRT, SIGBUS, and SIGTERM. +// +// By default, the signal handler will write the failure dump to the +// standard error. You can customize the destination by installing your +// own writer function by InstallFailureWriter() below. +// +// Note on threading: +// +// The function should be called before threads are created, if you want +// to use the failure signal handler for all threads. The stack trace +// will be shown only for the thread that receives the signal. In other +// words, stack traces of other threads won't be shown. +GLOG_EXPORT void InstallFailureSignalHandler(); + +// Returns true if FailureSignalHandler is installed. +GLOG_EXPORT bool IsFailureSignalHandlerInstalled(); + +// Installs a function that is used for writing the failure dump. "data" +// is the pointer to the beginning of a message to be written, and "size" +// is the size of the message. You should not expect the data is +// terminated with '\0'. +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 diff --git a/3rdparty/glog-0.7.0/src/glog/platform.h b/3rdparty/glog-0.7.0/src/glog/platform.h new file mode 100644 index 000000000..4aaf5b08f --- /dev/null +++ b/3rdparty/glog-0.7.0/src/glog/platform.h @@ -0,0 +1,62 @@ +// 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: Shinichiro Hamaji +// +// Detect supported platforms. + +#ifndef GLOG_PLATFORM_H +#define GLOG_PLATFORM_H + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) +# define GLOG_OS_WINDOWS +#elif defined(__CYGWIN__) || defined(__CYGWIN32__) +# define GLOG_OS_CYGWIN +#elif defined(linux) || defined(__linux) || defined(__linux__) +# ifndef GLOG_OS_LINUX +# define GLOG_OS_LINUX +# endif +#elif defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__) +# define GLOG_OS_MACOSX +#elif defined(__FreeBSD__) +# define GLOG_OS_FREEBSD +#elif defined(__NetBSD__) +# define GLOG_OS_NETBSD +#elif defined(__OpenBSD__) +# define GLOG_OS_OPENBSD +#elif defined(__EMSCRIPTEN__) +# define GLOG_OS_EMSCRIPTEN +#elif defined(__ANDROID__) +# define GLOG_OS_ANDROID +#else +// TODO(hamaji): Add other platforms. +#error Platform not supported by glog. Please consider to contribute platform information by submitting a pull request on Github. +#endif + +#endif // GLOG_PLATFORM_H diff --git a/3rdparty/glog-0.7.0/src/glog/raw_logging.h b/3rdparty/glog-0.7.0/src/glog/raw_logging.h new file mode 100644 index 000000000..195a1b9c7 --- /dev/null +++ b/3rdparty/glog-0.7.0/src/glog/raw_logging.h @@ -0,0 +1,176 @@ +// 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: Maxim Lifantsev +// +// Thread-safe logging routines that do not allocate any memory or +// acquire any locks, and can therefore be used by low-level memory +// allocation and synchronization code. + +#ifndef GLOG_RAW_LOGGING_H +#define GLOG_RAW_LOGGING_H + +#if defined(GLOG_USE_GLOG_EXPORT) +# include "glog/export.h" +#endif + +#if !defined(GLOG_EXPORT) +# error was not included correctly. See the documention for how to consume the library. +#endif + +#include "glog/log_severity.h" +#include "glog/vlog_is_on.h" + +namespace google { + +// This is similar to LOG(severity) << format... and VLOG(level) << format.., +// but +// * it is to be used ONLY by low-level modules that can't use normal LOG() +// * it is desiged to be a low-level logger that does not allocate any +// memory and does not need any locks, hence: +// * it logs straight and ONLY to STDERR w/o buffering +// * it uses an explicit format and arguments list +// * it will silently chop off really long message strings +// Usage example: +// RAW_LOG(ERROR, "Failed foo with %i: %s", status, error); +// RAW_VLOG(3, "status is %i", status); +// These will print an almost standard log lines like this to stderr only: +// E20200821 211317 file.cc:123] RAW: Failed foo with 22: bad_file +// I20200821 211317 file.cc:142] RAW: status is 20 +#define RAW_LOG(severity, ...) \ + do { \ + switch (google::GLOG_##severity) { \ + case 0: \ + RAW_LOG_INFO(__VA_ARGS__); \ + break; \ + case 1: \ + RAW_LOG_WARNING(__VA_ARGS__); \ + break; \ + case 2: \ + RAW_LOG_ERROR(__VA_ARGS__); \ + break; \ + case 3: \ + RAW_LOG_FATAL(__VA_ARGS__); \ + break; \ + default: \ + break; \ + } \ + } while (0) + +// The following STRIP_LOG testing is performed in the header file so that it's +// possible to completely compile out the logging code and the log messages. +#if !defined(STRIP_LOG) || STRIP_LOG == 0 +# define RAW_VLOG(verboselevel, ...) \ + do { \ + if (VLOG_IS_ON(verboselevel)) { \ + RAW_LOG_INFO(__VA_ARGS__); \ + } \ + } while (0) +#else +# define RAW_VLOG(verboselevel, ...) RawLogStub__(0, __VA_ARGS__) +#endif // STRIP_LOG == 0 + +#if !defined(STRIP_LOG) || STRIP_LOG == 0 +# define RAW_LOG_INFO(...) \ + google::RawLog__(google::GLOG_INFO, __FILE__, __LINE__, __VA_ARGS__) +#else +# define RAW_LOG_INFO(...) google::RawLogStub__(0, __VA_ARGS__) +#endif // STRIP_LOG == 0 + +#if !defined(STRIP_LOG) || STRIP_LOG <= 1 +# define RAW_LOG_WARNING(...) \ + google::RawLog__(google::GLOG_WARNING, __FILE__, __LINE__, __VA_ARGS__) +#else +# define RAW_LOG_WARNING(...) google::RawLogStub__(0, __VA_ARGS__) +#endif // STRIP_LOG <= 1 + +#if !defined(STRIP_LOG) || STRIP_LOG <= 2 +# define RAW_LOG_ERROR(...) \ + google::RawLog__(google::GLOG_ERROR, __FILE__, __LINE__, __VA_ARGS__) +#else +# define RAW_LOG_ERROR(...) google::RawLogStub__(0, __VA_ARGS__) +#endif // STRIP_LOG <= 2 + +#if !defined(STRIP_LOG) || STRIP_LOG <= 3 +# define RAW_LOG_FATAL(...) \ + google::RawLog__(google::GLOG_FATAL, __FILE__, __LINE__, __VA_ARGS__) +#else +# define RAW_LOG_FATAL(...) \ + do { \ + google::RawLogStub__(0, __VA_ARGS__); \ + exit(EXIT_FAILURE); \ + } while (0) +#endif // STRIP_LOG <= 3 + +// Similar to CHECK(condition) << message, +// but for low-level modules: we use only RAW_LOG that does not allocate memory. +// We do not want to provide args list here to encourage this usage: +// if (!cond) RAW_LOG(FATAL, "foo ...", hard_to_compute_args); +// so that the args are not computed when not needed. +#define RAW_CHECK(condition, message) \ + do { \ + if (!(condition)) { \ + RAW_LOG(FATAL, "Check %s failed: %s", #condition, message); \ + } \ + } while (0) + +// Debug versions of RAW_LOG and RAW_CHECK +#ifndef NDEBUG + +# define RAW_DLOG(severity, ...) RAW_LOG(severity, __VA_ARGS__) +# define RAW_DCHECK(condition, message) RAW_CHECK(condition, message) + +#else // NDEBUG + +# define RAW_DLOG(severity, ...) \ + while (false) RAW_LOG(severity, __VA_ARGS__) +# define RAW_DCHECK(condition, message) \ + while (false) RAW_CHECK(condition, message) + +#endif // NDEBUG + +// Stub log function used to work around for unused variable warnings when +// building with STRIP_LOG > 0. +static inline void RawLogStub__(int /* ignored */, ...) {} + +// Helper function to implement RAW_LOG and RAW_VLOG +// Logs format... at "severity" level, reporting it +// as called from file:line. +// This does not allocate memory or acquire locks. +GLOG_EXPORT void RawLog__(LogSeverity severity, const char* file, int line, + const char* format, ...) +#if defined(__has_attribute) +# if __has_attribute(used) + __attribute__((__format__(__printf__, 4, 5))) +# endif +#endif + ; +} // namespace google + +#endif // GLOG_RAW_LOGGING_H diff --git a/3rdparty/glog-0.7.0/src/glog/stl_logging.h b/3rdparty/glog-0.7.0/src/glog/stl_logging.h new file mode 100644 index 000000000..8dd7707e0 --- /dev/null +++ b/3rdparty/glog-0.7.0/src/glog/stl_logging.h @@ -0,0 +1,168 @@ +// Copyright (c) 2023, 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. +// +// Stream output operators for STL containers; to be used for logging *only*. +// Inclusion of this file lets you do: +// +// list x; +// LOG(INFO) << "data: " << x; +// vector v1, v2; +// CHECK_EQ(v1, v2); +// + +#ifndef GLOG_STL_LOGGING_H +#define GLOG_STL_LOGGING_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Forward declare these two, and define them after all the container streams +// operators so that we can recurse from pair -> container -> container -> pair +// properly. +template +std::ostream& operator<<(std::ostream& out, const std::pair& p); + +namespace google { + +template +void PrintSequence(std::ostream& out, Iter begin, Iter end); + +} +#define OUTPUT_TWO_ARG_CONTAINER(Sequence) \ + template \ + inline std::ostream& operator<<(std::ostream& out, \ + const Sequence& seq) { \ + google::PrintSequence(out, seq.begin(), seq.end()); \ + return out; \ + } + +OUTPUT_TWO_ARG_CONTAINER(std::vector) +OUTPUT_TWO_ARG_CONTAINER(std::deque) +OUTPUT_TWO_ARG_CONTAINER(std::list) +#undef OUTPUT_TWO_ARG_CONTAINER + +#define OUTPUT_THREE_ARG_CONTAINER(Sequence) \ + template \ + inline std::ostream& operator<<(std::ostream& out, \ + const Sequence& seq) { \ + google::PrintSequence(out, seq.begin(), seq.end()); \ + return out; \ + } + +OUTPUT_THREE_ARG_CONTAINER(std::set) +OUTPUT_THREE_ARG_CONTAINER(std::multiset) + +#undef OUTPUT_THREE_ARG_CONTAINER + +#define OUTPUT_FOUR_ARG_CONTAINER(Sequence) \ + template \ + inline std::ostream& operator<<(std::ostream& out, \ + const Sequence& seq) { \ + google::PrintSequence(out, seq.begin(), seq.end()); \ + return out; \ + } + +OUTPUT_FOUR_ARG_CONTAINER(std::map) +OUTPUT_FOUR_ARG_CONTAINER(std::multimap) +OUTPUT_FOUR_ARG_CONTAINER(std::unordered_set) +OUTPUT_FOUR_ARG_CONTAINER(std::unordered_multiset) +#undef OUTPUT_FOUR_ARG_CONTAINER + +#define OUTPUT_FIVE_ARG_CONTAINER(Sequence) \ + template \ + inline std::ostream& operator<<(std::ostream& out, \ + const Sequence& seq) { \ + google::PrintSequence(out, seq.begin(), seq.end()); \ + return out; \ + } + +OUTPUT_FIVE_ARG_CONTAINER(std::unordered_map) +OUTPUT_FIVE_ARG_CONTAINER(std::unordered_multimap) + +#undef OUTPUT_FIVE_ARG_CONTAINER + +template +inline std::ostream& operator<<(std::ostream& out, + const std::pair& p) { + out << '(' << p.first << ", " << p.second << ')'; + return out; +} + +namespace google { + +template +inline void PrintSequence(std::ostream& out, Iter begin, Iter end) { + // Output at most 100 elements -- appropriate if used for logging. + for (int i = 0; begin != end && i < 100; ++i, ++begin) { + if (i > 0) out << ' '; + out << *begin; + } + if (begin != end) { + out << " ..."; + } +} + +} // namespace google + +// Note that this is technically undefined behavior! We are adding things into +// the std namespace for a reason though -- we are providing new operations on +// types which are themselves defined with this namespace. Without this, these +// operator overloads cannot be found via ADL. If these definitions are not +// found via ADL, they must be #included before they're used, which requires +// this header to be included before apparently independent other headers. +// +// For example, base/logging.h defines various template functions to implement +// CHECK_EQ(x, y) and stream x and y into the log in the event the check fails. +// It does so via the function template MakeCheckOpValueString: +// template +// void MakeCheckOpValueString(strstream* ss, const T& v) { +// (*ss) << v; +// } +// Because 'glog/logging.h' is included before 'glog/stl_logging.h', +// subsequent CHECK_EQ(v1, v2) for vector<...> typed variable v1 and v2 can only +// find these operator definitions via ADL. +// +// Even this solution has problems -- it may pull unintended operators into the +// namespace as well, allowing them to also be found via ADL, and creating code +// that only works with a particular order of includes. Long term, we need to +// move all of the *definitions* into namespace std, bet we need to ensure no +// one references them first. This lets us take that step. We cannot define them +// in both because that would create ambiguous overloads when both are found. +namespace std { +using ::operator<<; +} + +#endif // GLOG_STL_LOGGING_H diff --git a/3rdparty/glog-0.7.0/src/glog/types.h b/3rdparty/glog-0.7.0/src/glog/types.h new file mode 100644 index 000000000..dca2f06f8 --- /dev/null +++ b/3rdparty/glog-0.7.0/src/glog/types.h @@ -0,0 +1,81 @@ + +// 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. +// + +#ifndef GLOG_TYPES_H +#define GLOG_TYPES_H + +#include +#include + +namespace google { + +using int32 = std::int32_t; +using uint32 = std::uint32_t; +using int64 = std::int64_t; +using uint64 = std::uint64_t; + +} // namespace google + +#if defined(__has_feature) +# if __has_feature(thread_sanitizer) +# define GLOG_SANITIZE_THREAD 1 +# endif +#endif + +#if !defined(GLOG_SANITIZE_THREAD) && defined(__SANITIZE_THREAD__) && \ + __SANITIZE_THREAD__ +# define GLOG_SANITIZE_THREAD 1 +#endif + +#if defined(GLOG_SANITIZE_THREAD) +# define GLOG_IFDEF_THREAD_SANITIZER(X) X +#else +# define GLOG_IFDEF_THREAD_SANITIZER(X) +#endif + +#if defined(_MSC_VER) +# define GLOG_MSVC_PUSH_DISABLE_WARNING(n) \ + __pragma(warning(push)) __pragma(warning(disable : n)) +# define GLOG_MSVC_POP_WARNING() __pragma(warning(pop)) +#else +# define GLOG_MSVC_PUSH_DISABLE_WARNING(n) +# define GLOG_MSVC_POP_WARNING() +#endif + +#if defined(GLOG_SANITIZE_THREAD) +// We need to identify the static variables as "benign" races +// to avoid noisy reports from TSAN. +extern "C" void AnnotateBenignRaceSized(const char* file, int line, + const volatile void* mem, size_t size, + const char* description); +#endif // defined(GLOG_SANITIZE_THREAD) + +#endif // GLOG_TYPES_H diff --git a/3rdparty/glog-0.7.0/src/glog/vlog_is_on.h b/3rdparty/glog-0.7.0/src/glog/vlog_is_on.h new file mode 100644 index 000000000..f94b0fb8f --- /dev/null +++ b/3rdparty/glog-0.7.0/src/glog/vlog_is_on.h @@ -0,0 +1,136 @@ +// 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: Ray Sidney and many others +// +// Defines the VLOG_IS_ON macro that controls the variable-verbosity +// conditional logging. +// +// It's used by VLOG and VLOG_IF in logging.h +// and by RAW_VLOG in raw_logging.h to trigger the logging. +// +// It can also be used directly e.g. like this: +// if (VLOG_IS_ON(2)) { +// // do some logging preparation and logging +// // that can't be accomplished e.g. via just VLOG(2) << ...; +// } +// +// The truth value that VLOG_IS_ON(level) returns is determined by +// the three verbosity level flags: +// --v= Gives the default maximal active V-logging level; +// 0 is the default. +// Normally positive values are used for V-logging levels. +// --vmodule= Gives the per-module maximal V-logging levels to override +// the value given by --v. +// E.g. "my_module=2,foo*=3" would change the logging level +// for all code in source files "my_module.*" and "foo*.*" +// ("-inl" suffixes are also disregarded for this matching). +// +// SetVLOGLevel helper function is provided to do limited dynamic control over +// V-logging by overriding the per-module settings given via --vmodule flag. +// +// CAVEAT: --vmodule functionality is not available in non gcc compilers. +// + +#ifndef GLOG_VLOG_IS_ON_H +#define GLOG_VLOG_IS_ON_H + +#include + +#if defined(GLOG_USE_GLOG_EXPORT) +# include "glog/export.h" +#endif + +#if !defined(GLOG_EXPORT) +# error was not included correctly. See the documention for how to consume the library. +#endif + +#include "glog/flags.h" +#include "glog/types.h" + +#if defined(__GNUC__) +// We emit an anonymous static int* variable at every VLOG_IS_ON(n) site. +// (Normally) the first time every VLOG_IS_ON(n) site is hit, +// we determine what variable will dynamically control logging at this site: +// it's either FLAGS_v or an appropriate internal variable +// matching the current source file that represents results of +// parsing of --vmodule flag and/or SetVLOGLevel calls. +# define VLOG_IS_ON(verboselevel) \ + __extension__({ \ + static google::SiteFlag vlocal__ = {nullptr, nullptr, 0, nullptr}; \ + GLOG_IFDEF_THREAD_SANITIZER(AnnotateBenignRaceSized( \ + __FILE__, __LINE__, &vlocal__, sizeof(google::SiteFlag), "")); \ + google::int32 verbose_level__ = (verboselevel); \ + (vlocal__.level == nullptr \ + ? google::InitVLOG3__(&vlocal__, &FLAGS_v, __FILE__, \ + verbose_level__) \ + : *vlocal__.level >= verbose_level__); \ + }) +#else +// GNU extensions not available, so we do not support --vmodule. +// Dynamic value of FLAGS_v always controls the logging level. +# define VLOG_IS_ON(verboselevel) (FLAGS_v >= (verboselevel)) +#endif + +namespace google { + +// Set VLOG(_IS_ON) level for module_pattern to log_level. +// This lets us dynamically control what is normally set by the --vmodule flag. +// Returns the level that previously applied to module_pattern. +// NOTE: To change the log level for VLOG(_IS_ON) sites +// that have already executed after/during InitGoogleLogging, +// one needs to supply the exact --vmodule pattern that applied to them. +// (If no --vmodule pattern applied to them +// the value of FLAGS_v will continue to control them.) +extern GLOG_EXPORT int SetVLOGLevel(const char* module_pattern, int log_level); + +// Various declarations needed for VLOG_IS_ON above: ========================= + +struct SiteFlag { + int32* level; + const char* base_name; + std::size_t base_len; + SiteFlag* next; +}; + +// Helper routine which determines the logging info for a particular VLOG site. +// site_flag is the address of the site-local pointer to the controlling +// verbosity level +// site_default is the default to use for *site_flag +// fname is the current source file name +// verbose_level is the argument to VLOG_IS_ON +// We will return the return value for VLOG_IS_ON +// and if possible set *site_flag appropriately. +extern GLOG_EXPORT bool InitVLOG3__(SiteFlag* site_flag, + int32* site_default, + const char* fname, + int32 verbose_level); +} // namespace google + +#endif // GLOG_VLOG_IS_ON_H diff --git a/3rdparty/glog-0.7.0/src/googletest.h b/3rdparty/glog-0.7.0/src/googletest.h new file mode 100644 index 000000000..33af3feb6 --- /dev/null +++ b/3rdparty/glog-0.7.0/src/googletest.h @@ -0,0 +1,618 @@ +// 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: Shinichiro Hamaji +// (based on googletest: http://code.google.com/p/googletest/) + +#ifdef GOOGLETEST_H__ +# error You must not include this file twice. +#endif +#define GOOGLETEST_H__ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#ifdef HAVE_UNISTD_H +# include +#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) +#else +# define GOOGLE_GLOG_THROW_BAD_ALLOC +#endif + +using std::map; +using std::string; +using std::vector; + +namespace google { +extern void (*g_logging_fail_func)(); +extern void GetExistingTempDirectories(std::vector& 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 temp_directories_list; + google::GetExistingTempDirectories(temp_directories_list); + + if (temp_directories_list.empty()) { + fprintf(stderr, "No temporary directory found\n"); + exit(EXIT_FAILURE); + } + + // Use first directory from list of existing temporary directories. + return temp_directories_list.front(); +} + +#if defined(GLOG_OS_WINDOWS) && defined(_MSC_VER) && !defined(TEST_SRC_DIR) +// The test will run in glog/vsproject/ +// (e.g., glog/vsproject/logging_unittest). +static const char TEST_SRC_DIR[] = "../.."; +#elif !defined(TEST_SRC_DIR) +# warning TEST_SRC_DIR should be defined in config.h +static const char TEST_SRC_DIR[] = "."; +#endif + +static const uint32_t PTR_TEST_VALUE = 0x12345678; + +DEFINE_string(test_tmpdir, GetTempDir(), "Dir we use for temp files"); +DEFINE_string(test_srcdir, TEST_SRC_DIR, + "Source-dir root, needed to find glog_unittest_flagfile"); +DEFINE_bool(run_benchmark, false, "If true, run benchmarks"); +#ifdef NDEBUG +DEFINE_int32(benchmark_iters, 100000000, "Number of iterations per benchmark"); +#else +DEFINE_int32(benchmark_iters, 100000, "Number of iterations per benchmark"); +#endif + +#ifdef HAVE_LIB_GTEST +# include +// Use our ASSERT_DEATH implementation. +# undef ASSERT_DEATH +# undef ASSERT_DEBUG_DEATH +using testing::InitGoogleTest; +#else + +namespace google { + +void InitGoogleTest(int*, char**); + +void InitGoogleTest(int*, char**) {} + +// The following is some bare-bones testing infrastructure + +# define EXPECT_NEAR(val1, val2, abs_error) \ + do { \ + if (abs(val1 - val2) > abs_error) { \ + fprintf(stderr, "Check failed: %s within %s of %s\n", #val1, \ + #abs_error, #val2); \ + exit(EXIT_FAILURE); \ + } \ + } while (0) + +# define EXPECT_TRUE(cond) \ + do { \ + if (!(cond)) { \ + fprintf(stderr, "Check failed: %s\n", #cond); \ + exit(EXIT_FAILURE); \ + } \ + } while (0) + +# define EXPECT_FALSE(cond) EXPECT_TRUE(!(cond)) + +# define EXPECT_OP(op, val1, val2) \ + do { \ + if (!((val1)op(val2))) { \ + fprintf(stderr, "Check failed: %s %s %s\n", #val1, #op, #val2); \ + exit(EXIT_FAILURE); \ + } \ + } while (0) + +# define EXPECT_EQ(val1, val2) EXPECT_OP(==, val1, val2) +# define EXPECT_NE(val1, val2) EXPECT_OP(!=, val1, val2) +# define EXPECT_GT(val1, val2) EXPECT_OP(>, val1, val2) +# define EXPECT_LT(val1, val2) EXPECT_OP(<, val1, val2) + +# define EXPECT_NAN(arg) \ + do { \ + if (!isnan(arg)) { \ + fprintf(stderr, "Check failed: isnan(%s)\n", #arg); \ + exit(EXIT_FAILURE); \ + } \ + } while (0) + +# define EXPECT_INF(arg) \ + do { \ + if (!isinf(arg)) { \ + fprintf(stderr, "Check failed: isinf(%s)\n", #arg); \ + exit(EXIT_FAILURE); \ + } \ + } while (0) + +# define EXPECT_DOUBLE_EQ(val1, val2) \ + do { \ + if (((val1) < (val2)-0.001 || (val1) > (val2) + 0.001)) { \ + fprintf(stderr, "Check failed: %s == %s\n", #val1, #val2); \ + exit(EXIT_FAILURE); \ + } \ + } while (0) + +# define EXPECT_STREQ(val1, val2) \ + do { \ + if (strcmp((val1), (val2)) != 0) { \ + fprintf(stderr, "Check failed: streq(%s, %s)\n", #val1, #val2); \ + exit(EXIT_FAILURE); \ + } \ + } 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 g_testlist; // the tests to run +# define TEST(a, b) \ + struct Test_##a##_##b { \ + Test_##a##_##b() { g_testlist.push_back(&Run); } \ + static void Run() { \ + FlagSaver fs; \ + RunTest(); \ + } \ + static void RunTest(); \ + }; \ + static Test_##a##_##b g_test_##a##_##b; \ + void Test_##a##_##b::RunTest() + +static inline int RUN_ALL_TESTS() { + vector::const_iterator it; + for (it = g_testlist.begin(); it != g_testlist.end(); ++it) { + (*it)(); + } + fprintf(stderr, "Passed %d tests\n\nPASS\n", + static_cast(g_testlist.size())); + return 0; +} + +} // namespace google + +#endif // ! HAVE_LIB_GTEST + +namespace google { + +static bool g_called_abort; +static jmp_buf g_jmp_buf; +static inline void CalledAbort() { + g_called_abort = true; + longjmp(g_jmp_buf, 1); +} + +#ifdef GLOG_OS_WINDOWS +// TODO(hamaji): Death test somehow doesn't work in Windows. +# define ASSERT_DEATH(fn, msg) +#else +# define ASSERT_DEATH(fn, msg) \ + do { \ + g_called_abort = false; \ + /* in logging.cc */ \ + void (*original_logging_fail_func)() = g_logging_fail_func; \ + g_logging_fail_func = &CalledAbort; \ + if (!setjmp(g_jmp_buf)) fn; \ + /* set back to their default */ \ + g_logging_fail_func = original_logging_fail_func; \ + if (!g_called_abort) { \ + fprintf(stderr, "Function didn't die (%s): %s\n", msg, #fn); \ + exit(EXIT_FAILURE); \ + } \ + } while (0) +#endif + +#ifdef NDEBUG +# define ASSERT_DEBUG_DEATH(fn, msg) +#else +# define ASSERT_DEBUG_DEATH(fn, msg) ASSERT_DEATH(fn, msg) +#endif // NDEBUG + +// Benchmark tools. + +#define BENCHMARK(n) static BenchmarkRegisterer __benchmark_##n(#n, &n); + +map g_benchlist; // the benchmarks to run + +class BenchmarkRegisterer { + public: + BenchmarkRegisterer(const char* name, void (*function)(int iters)) { + EXPECT_TRUE(g_benchlist.insert(std::make_pair(name, function)).second); + } +}; + +static inline void RunSpecifiedBenchmarks() { + if (!FLAGS_run_benchmark) { + return; + } + + int iter_cnt = FLAGS_benchmark_iters; + puts("Benchmark\tTime(ns)\tIterations"); + for (auto& iter : g_benchlist) { + clock_t start = clock(); + iter.second(iter_cnt); + double elapsed_ns = (static_cast(clock()) - start) / + CLOCKS_PER_SEC * 1000 * 1000 * 1000; +#if defined(__GNUC__) && !defined(__clang__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wformat=" +#endif + printf("%s\t%8.2lf\t%10d\n", iter.first.c_str(), elapsed_ns / iter_cnt, + iter_cnt); +#if defined(__GNUC__) && !defined(__clang__) +# pragma GCC diagnostic pop +#endif + } + puts(""); +} + +// ---------------------------------------------------------------------- +// Golden file functions +// ---------------------------------------------------------------------- + +class CapturedStream { + public: + CapturedStream(int fd, string filename) + : fd_(fd), filename_(std::move(filename)) { + Capture(); + } + + // 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_); + + // 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); + + // Send stdout/stderr to this file + fflush(nullptr); + CHECK(dup2(cap_fd.get(), fd_) != -1); + CHECK(cap_fd.close() != -1); + } + + // Remove output redirection + void StopCapture() { + // Restore original stream + if (uncaptured_fd_) { + fflush(nullptr); + CHECK(dup2(uncaptured_fd_.get(), 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 +}; +static std::map> s_captured_streams; +// Redirect a file descriptor to a file. +// fd - Should be stdout or stderr +// 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(fd, filename); +} +static inline void CaptureTestStdout() { + CaptureTestOutput(fileno(stdout), FLAGS_test_tmpdir + "/captured.out"); +} +static inline void CaptureTestStderr() { + CaptureTestOutput(fileno(stderr), FLAGS_test_tmpdir + "/captured.err"); +} +// Return the size (in bytes) of a file +static inline size_t GetFileSize(FILE* file) { + fseek(file, 0, SEEK_END); + return static_cast(ftell(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 content(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 + + fseek(file, 0, SEEK_SET); + + // Keep reading the file until we cannot read further or the + // pre-determined file size is reached. + do { + bytes_last_read = + fread(content.data() + 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); +} +// Get the captured stdout or stderr as a string +static inline string GetCapturedTestOutput(int fd) { + CHECK((fd == fileno(stdout)) || (fd == fileno(stderr))); + std::unique_ptr cap = std::move(s_captured_streams.at(fd)); + s_captured_streams.erase(fd); + CHECK(cap) << ": did you forget CaptureTestStdout() or CaptureTestStderr()?"; + + // Make sure everything is flushed. + cap->StopCapture(); + + // Read the captured file. + std::unique_ptr file{fopen(cap->filename().c_str(), "r")}; + const string content = ReadEntireFile(file.get()); + file.reset(); + + return content; +} +// Get the captured stderr of a test as a string. +static inline string GetCapturedTestStderr() { + return GetCapturedTestOutput(fileno(stderr)); +} + +static const std::size_t kLoggingPrefixLength = 9; + +// Check if the string is [IWEF](\d{8}|YEARDATE) +static inline bool IsLoggingPrefix(const string& s) { + if (s.size() != kLoggingPrefixLength) { + return false; + } + if (!strchr("IWEF", s[0])) return false; + for (size_t i = 1; i <= 8; ++i) { + if (!isdigit(s[i]) && s[i] != "YEARDATE"[i - 1]) return false; + } + return true; +} + +// Convert log output into normalized form. +// +// Example: +// I20200102 030405 logging_unittest.cc:345] RAW: vlog -1 +// => IYEARDATE TIME__ logging_unittest.cc:LINE] RAW: vlog -1 +static inline string MungeLine(const string& line) { + string before, logcode_date, time, thread_lineinfo; + std::size_t begin_of_logging_prefix = 0; + for (; begin_of_logging_prefix + kLoggingPrefixLength < line.size(); + ++begin_of_logging_prefix) { + if (IsLoggingPrefix( + line.substr(begin_of_logging_prefix, kLoggingPrefixLength))) { + break; + } + } + if (begin_of_logging_prefix + kLoggingPrefixLength >= line.size()) { + return line; + } else if (begin_of_logging_prefix > 0) { + before = line.substr(0, begin_of_logging_prefix - 1); + } + std::istringstream iss(line.substr(begin_of_logging_prefix)); + iss >> logcode_date; + iss >> time; + iss >> thread_lineinfo; + CHECK(!thread_lineinfo.empty()); + if (thread_lineinfo[thread_lineinfo.size() - 1] != ']') { + // We found thread ID. + string tmp; + iss >> tmp; + CHECK(!tmp.empty()); + CHECK_EQ(']', tmp[tmp.size() - 1]); + thread_lineinfo = "THREADID " + tmp; + } + size_t index = thread_lineinfo.find(':'); + CHECK_NE(string::npos, index); + thread_lineinfo = thread_lineinfo.substr(0, index + 1) + "LINE]"; + string rest; + std::getline(iss, rest); + return (before + logcode_date[0] + "YEARDATE TIME__ " + thread_lineinfo + + MungeLine(rest)); +} + +static inline void StringReplace(string* str, const string& oldsub, + const string& newsub) { + size_t pos = str->find(oldsub); + if (pos != string::npos) { + str->replace(pos, oldsub.size(), newsub); + } +} + +static inline string Munge(const string& filename) { + std::unique_ptr fp{fopen(filename.c_str(), "rb")}; + CHECK(fp != nullptr) << filename << ": couldn't open"; + char buf[4096]; + string result; + while (fgets(buf, 4095, fp.get())) { + string line = MungeLine(buf); + const size_t str_size = 256; + char null_str[str_size]; + char ptr_str[str_size]; + std::snprintf(null_str, str_size, "%p", static_cast(nullptr)); + std::snprintf(ptr_str, str_size, "%p", + reinterpret_cast(PTR_TEST_VALUE)); + + StringReplace(&line, "__NULLP__", null_str); + StringReplace(&line, "__PTRTEST__", ptr_str); + + StringReplace(&line, "__SUCCESS__", StrError(0)); + StringReplace(&line, "__ENOENT__", StrError(ENOENT)); + StringReplace(&line, "__EINTR__", StrError(EINTR)); + StringReplace(&line, "__ENXIO__", StrError(ENXIO)); + StringReplace(&line, "__ENOEXEC__", StrError(ENOEXEC)); + result += line + "\n"; + } + return result; +} + +static inline void WriteToFile(const string& body, const string& file) { + std::unique_ptr fp{fopen(file.c_str(), "wb")}; + fwrite(body.data(), 1, body.size(), fp.get()); +} + +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()) { + CHECK(cap) << ": did you forget CaptureTestStdout()?"; + } else { + CHECK(cap) << ": did you forget CaptureTestStderr()?"; + } + + cap->StopCapture(); + + // Run munge + const string captured = Munge(cap->filename()); + const string golden = Munge(golden_filename); + if (captured != golden) { + fprintf(stderr, + "Test with golden file failed. We'll try to show the diff:\n"); + string munged_golden = golden_filename + ".munged"; + WriteToFile(golden, munged_golden); + string munged_captured = cap->filename() + ".munged"; + WriteToFile(captured, munged_captured); +#ifdef GLOG_OS_WINDOWS + string diffcmd("fc " + munged_golden + " " + munged_captured); +#else + string diffcmd("diff -u " + munged_golden + " " + munged_captured); +#endif + if (system(diffcmd.c_str()) != 0) { + fprintf(stderr, "diff command was failed.\n"); + } + unlink(munged_golden.c_str()); + unlink(munged_captured.c_str()); + return false; + } + LOG(INFO) << "Diff was successful"; + return true; +} + +static inline bool MungeAndDiffTestStderr(const string& golden_filename) { + return MungeAndDiffTest(golden_filename, + s_captured_streams.at(fileno(stderr)).get()); +} + +static inline bool MungeAndDiffTestStdout(const string& golden_filename) { + return MungeAndDiffTest(golden_filename, + s_captured_streams.at(fileno(stdout)).get()); +} + +// Save flags used from logging_unittest.cc. +#ifndef GLOG_USE_GFLAGS +struct FlagSaver { + FlagSaver() + : v_(FLAGS_v), + stderrthreshold_(FLAGS_stderrthreshold), + logtostderr_(FLAGS_logtostderr), + alsologtostderr_(FLAGS_alsologtostderr), + logmailer_(FLAGS_logmailer) {} + ~FlagSaver() { + FLAGS_v = v_; + FLAGS_stderrthreshold = stderrthreshold_; + FLAGS_logtostderr = logtostderr_; + FLAGS_alsologtostderr = alsologtostderr_; + FLAGS_logmailer = logmailer_; + } + int v_; + int stderrthreshold_; + bool logtostderr_; + bool alsologtostderr_; + std::string logmailer_; +}; +#endif + +// Add hook for operator new to ensure there are no memory allocation. + +void (*g_new_hook)() = nullptr; + +} // namespace google + +void* operator new(size_t size, const std::nothrow_t&) noexcept { + if (google::g_new_hook) { + google::g_new_hook(); + } + return malloc(size); +} + +void* operator new(size_t size) GOOGLE_GLOG_THROW_BAD_ALLOC { + void* p = ::operator new(size, std::nothrow); + if (p == nullptr) { + throw std::bad_alloc{}; + } + return p; +} + +void* operator new[](size_t size) GOOGLE_GLOG_THROW_BAD_ALLOC { + return ::operator new(size); +} + +void operator delete(void* p) noexcept { free(p); } + +void operator delete(void* p, size_t) noexcept { ::operator delete(p); } + +void operator delete[](void* p) noexcept { ::operator delete(p); } + +void operator delete[](void* p, size_t) noexcept { ::operator delete(p); } diff --git a/3rdparty/glog-0.7.0/src/includes_unittest/CMakeLists.txt b/3rdparty/glog-0.7.0/src/includes_unittest/CMakeLists.txt new file mode 100644 index 000000000..8f70fe916 --- /dev/null +++ b/3rdparty/glog-0.7.0/src/includes_unittest/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required (VERSION 3.16) +project (glog_includes LANGUAGES CXX) + +find_package (glog REQUIRED NO_MODULE) + +add_executable (glog_includes_logging glog_includes_logging.cc) +target_link_libraries (glog_includes_logging PRIVATE glog::glog) + +add_executable (glog_includes_vlog_is_on glog_includes_vlog_is_on.cc) +target_link_libraries (glog_includes_vlog_is_on PRIVATE glog::glog) + +add_executable (glog_includes_raw_logging glog_includes_raw_logging.cc) +target_link_libraries (glog_includes_raw_logging PRIVATE glog::glog) + +add_executable (glog_includes_stl_logging glog_includes_stl_logging.cc) +target_link_libraries (glog_includes_stl_logging PRIVATE glog::glog) diff --git a/3rdparty/glog-0.7.0/src/includes_unittest/glog_includes_logging.cc b/3rdparty/glog-0.7.0/src/includes_unittest/glog_includes_logging.cc new file mode 100644 index 000000000..bde0834f9 --- /dev/null +++ b/3rdparty/glog-0.7.0/src/includes_unittest/glog_includes_logging.cc @@ -0,0 +1,39 @@ +// 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 + +int main() { + LOG(INFO) << "info"; + LOG(WARNING) << "warning"; + LOG(ERROR) << "error"; + LOG(FATAL) << "fatal"; +} diff --git a/3rdparty/glog-0.7.0/src/includes_unittest/glog_includes_raw_logging.cc b/3rdparty/glog-0.7.0/src/includes_unittest/glog_includes_raw_logging.cc new file mode 100644 index 000000000..c9d08c1b4 --- /dev/null +++ b/3rdparty/glog-0.7.0/src/includes_unittest/glog_includes_raw_logging.cc @@ -0,0 +1,39 @@ +// 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 + +int main() { + RAW_LOG(INFO, "info"); + RAW_LOG(WARNING, "warning"); + RAW_LOG(ERROR, "error"); + RAW_LOG(FATAL, "fatal"); +} diff --git a/3rdparty/glog-0.7.0/src/includes_unittest/glog_includes_stl_logging.cc b/3rdparty/glog-0.7.0/src/includes_unittest/glog_includes_stl_logging.cc new file mode 100644 index 000000000..1d366bb26 --- /dev/null +++ b/3rdparty/glog-0.7.0/src/includes_unittest/glog_includes_stl_logging.cc @@ -0,0 +1,34 @@ +// 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 + +int main() {} diff --git a/3rdparty/glog-0.7.0/src/includes_unittest/glog_includes_vlog_is_on.cc b/3rdparty/glog-0.7.0/src/includes_unittest/glog_includes_vlog_is_on.cc new file mode 100644 index 000000000..164b0e234 --- /dev/null +++ b/3rdparty/glog-0.7.0/src/includes_unittest/glog_includes_vlog_is_on.cc @@ -0,0 +1,34 @@ +// 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 + +int main() { VLOG_IS_ON(0); } diff --git a/3rdparty/glog-0.7.0/src/log_severity_unittest/CMakeLists.txt b/3rdparty/glog-0.7.0/src/log_severity_unittest/CMakeLists.txt new file mode 100644 index 000000000..4d8412cd3 --- /dev/null +++ b/3rdparty/glog-0.7.0/src/log_severity_unittest/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required (VERSION 3.16) +project (glog_log_severity LANGUAGES CXX) + +find_package (glog REQUIRED NO_MODULE) + +add_executable (glog_log_severity_constants glog_log_severity_constants.cc) +target_link_libraries (glog_log_severity_constants PRIVATE glog::glog) + +add_executable (glog_log_severity_conversion glog_log_severity_conversion.cc) +target_link_libraries (glog_log_severity_conversion PRIVATE glog::glog) diff --git a/3rdparty/glog-0.7.0/src/log_severity_unittest/glog_log_severity_constants.cc b/3rdparty/glog-0.7.0/src/log_severity_unittest/glog_log_severity_constants.cc new file mode 100644 index 000000000..3775e083a --- /dev/null +++ b/3rdparty/glog-0.7.0/src/log_severity_unittest/glog_log_severity_constants.cc @@ -0,0 +1,40 @@ +// 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 + +int main() { + // Must not compile + LOG(0) << "type unsafe info"; + LOG(1) << "type unsafe info"; + LOG(2) << "type unsafe info"; + LOG(3) << "type unsafe info"; +} diff --git a/3rdparty/glog-0.7.0/src/log_severity_unittest/glog_log_severity_conversion.cc b/3rdparty/glog-0.7.0/src/log_severity_unittest/glog_log_severity_conversion.cc new file mode 100644 index 000000000..53f6d7fbb --- /dev/null +++ b/3rdparty/glog-0.7.0/src/log_severity_unittest/glog_log_severity_conversion.cc @@ -0,0 +1,42 @@ +// 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 + +int main() { + // Must not compile + google::LogMessage{__FILE__, __LINE__, -1}; + // Cast to int to avoid implicit conversoin to nullptr + google::LogMessage{__FILE__, __LINE__, static_cast(0)}; + google::LogMessage{__FILE__, __LINE__, 1}; + google::LogMessage{__FILE__, __LINE__, 2}; + google::LogMessage{__FILE__, __LINE__, 3}; +} diff --git a/3rdparty/glog-0.7.0/src/logging.cc b/3rdparty/glog-0.7.0/src/logging.cc new file mode 100644 index 000000000..fc6399529 --- /dev/null +++ b/3rdparty/glog-0.7.0/src/logging.cc @@ -0,0 +1,2835 @@ +// 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. + +#define _GNU_SOURCE 1 // needed for O_NOFOLLOW and pread()/pwrite() + +#include "glog/logging.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "glog/platform.h" +#include "glog/raw_logging.h" +#include "stacktrace.h" +#include "utilities.h" + +#ifdef GLOG_OS_WINDOWS +# include "windows/dirent.h" +#else +# include // for automatic removal of old logs +#endif + +#include +#include + +#include // for std::isspace +#include // for errno +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE__CHSIZE_S +# include // for truncate log file +#endif +#ifdef HAVE_PWD_H +# include +#endif +#ifdef HAVE_SYS_UTSNAME_H +# include // For uname. +#endif +#ifdef HAVE_SYSLOG_H +# include +#endif + +#ifdef HAVE_SYS_TYPES_H +# include +#endif + +#ifdef HAVE_UNISTD_H +# include +#endif + +#ifndef HAVE_MODE_T +typedef int mode_t; +#endif + +using std::dec; +using std::hex; +using std::min; +using std::ostream; +using std::ostringstream; +using std::setfill; +using std::setw; +using std::string; +using std::vector; + +using std::fclose; +using std::fflush; +using std::FILE; +using std::fprintf; +using std::fwrite; +using std::perror; + +#ifdef __QNX__ +using std::fdopen; +#endif + +// There is no thread annotation support. +#define EXCLUSIVE_LOCKS_REQUIRED(mu) + +// TODO(hamaji): consider windows +enum { PATH_SEPARATOR = '/' }; + +#ifndef HAVE_PREAD +static ssize_t pread(int fd, void* buf, size_t count, off_t offset) { + off_t orig_offset = lseek(fd, 0, SEEK_CUR); + if (orig_offset == (off_t)-1) return -1; + if (lseek(fd, offset, SEEK_CUR) == (off_t)-1) return -1; + ssize_t len = read(fd, buf, count); + if (len < 0) return len; + if (lseek(fd, orig_offset, SEEK_SET) == (off_t)-1) return -1; + return len; +} +#endif // !HAVE_PREAD + +#ifndef HAVE_PWRITE +static ssize_t pwrite(int fd, void* buf, size_t count, off_t offset) { + off_t orig_offset = lseek(fd, 0, SEEK_CUR); + if (orig_offset == (off_t)-1) return -1; + if (lseek(fd, offset, SEEK_CUR) == (off_t)-1) return -1; + ssize_t len = write(fd, buf, count); + if (len < 0) return len; + if (lseek(fd, orig_offset, SEEK_SET) == (off_t)-1) return -1; + return len; +} +#endif // !HAVE_PWRITE + +static void GetHostName(string* hostname) { +#if defined(HAVE_SYS_UTSNAME_H) + struct utsname buf; + if (uname(&buf) < 0) { + // ensure null termination on failure + *buf.nodename = '\0'; + } + *hostname = buf.nodename; +#elif defined(GLOG_OS_WINDOWS) + char buf[MAX_COMPUTERNAME_LENGTH + 1]; + DWORD len = MAX_COMPUTERNAME_LENGTH + 1; + if (GetComputerNameA(buf, &len)) { + *hostname = buf; + } else { + hostname->clear(); + } +#else +# warning There is no way to retrieve the host name. + *hostname = "(unknown)"; +#endif +} + +// Returns true iff terminal supports using colors in output. +static bool TerminalSupportsColor() { + bool term_supports_color = false; +#ifdef GLOG_OS_WINDOWS + // on Windows TERM variable is usually not set, but the console does + // support colors. + term_supports_color = true; +#else + // On non-Windows platforms, we rely on the TERM variable. + const char* const term = getenv("TERM"); + if (term != nullptr && term[0] != '\0') { + term_supports_color = + !strcmp(term, "xterm") || !strcmp(term, "xterm-color") || + !strcmp(term, "xterm-256color") || !strcmp(term, "screen-256color") || + !strcmp(term, "konsole") || !strcmp(term, "konsole-16color") || + !strcmp(term, "konsole-256color") || !strcmp(term, "screen") || + !strcmp(term, "linux") || !strcmp(term, "cygwin"); + } +#endif + return term_supports_color; +} + +#if defined(__cpp_lib_unreachable) && (__cpp_lib_unreachable >= 202202L) +# define GLOG_UNREACHABLE std::unreachable() +#elif !defined(NDEBUG) +# define GLOG_UNREACHABLE assert(false) +#else +# if defined(_MSC_VER) +# define GLOG_UNREACHABLE __assume(false) +# elif defined(__has_builtin) +# if __has_builtin(unreachable) +# define GLOG_UNREACHABLE __builtin_unreachable() +# endif +# endif +# if !defined(GLOG_UNREACHABLE) && defined(__GNUG__) +# define GLOG_UNREACHABLE __builtin_unreachable() +# endif +# if !defined(GLOG_UNREACHABLE) +# define GLOG_UNREACHABLE +# endif +#endif + +namespace google { + +GLOG_NO_EXPORT +std::string StrError(int err); + +enum GLogColor { COLOR_DEFAULT, COLOR_RED, COLOR_GREEN, COLOR_YELLOW }; + +static GLogColor SeverityToColor(LogSeverity severity) { + switch (severity) { + case GLOG_INFO: + return COLOR_DEFAULT; + case GLOG_WARNING: + return COLOR_YELLOW; + case GLOG_ERROR: + case GLOG_FATAL: + return COLOR_RED; + } + + // should never get here. + GLOG_UNREACHABLE; +} + +#ifdef GLOG_OS_WINDOWS + +// Returns the character attribute for the given color. +static WORD GetColorAttribute(GLogColor color) { + switch (color) { + case COLOR_RED: + return FOREGROUND_RED; + case COLOR_GREEN: + return FOREGROUND_GREEN; + case COLOR_YELLOW: + return FOREGROUND_RED | FOREGROUND_GREEN; + case COLOR_DEFAULT: + break; + } + return 0; +} + +#else + +// Returns the ANSI color code for the given color. +static const char* GetAnsiColorCode(GLogColor color) { + switch (color) { + case COLOR_RED: + return "1"; + case COLOR_GREEN: + return "2"; + case COLOR_YELLOW: + return "3"; + case COLOR_DEFAULT: + return ""; + }; + return nullptr; // stop warning about return type. +} + +#endif // GLOG_OS_WINDOWS + +// Safely get max_log_size, overriding to 1 if it somehow gets defined as 0 +static uint32 MaxLogSize() { + return (FLAGS_max_log_size > 0 && FLAGS_max_log_size < 4096 + ? FLAGS_max_log_size + : 1); +} + +// An arbitrary limit on the length of a single log message. This +// is so that streaming can be done more efficiently. +const size_t LogMessage::kMaxLogMessageLen = 30000; + +namespace logging { +namespace internal { +struct LogMessageData { + LogMessageData(); + + int preserved_errno_; // preserved errno + // Buffer space; contains complete message text. + char message_text_[LogMessage::kMaxLogMessageLen + 1]; + LogMessage::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 + union { // At most one of these is used: union to keep the size low. + LogSink* sink_; // nullptr or sink to send message to + std::vector* + outvec_; // nullptr or vector to push message onto + std::string* message_; // nullptr or string to write message into + }; + size_t num_prefix_chars_; // # of chars of prefix in this message + size_t num_chars_to_log_; // # of chars of msg to send to log + size_t num_chars_to_syslog_; // # of chars of msg to send to syslog + const char* basename_; // basename of file that called LOG + const char* fullname_; // fullname of file that called LOG + bool has_been_flushed_; // false => data has not been flushed + bool first_fatal_; // true => this was first fatal msg + std::thread::id thread_id_; + + LogMessageData(const LogMessageData&) = delete; + LogMessageData& 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 +// changing the destination file for log messages of a given severity) also +// lock this mutex. Please be sure that anybody who might possibly need to +// lock it does so. +static std::mutex log_mutex; + +// Number of messages sent at each severity. Under log_mutex. +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"}; + +// Has the user called SetExitOnDFatal(true)? +static bool exit_on_dfatal = true; + +const char* GetLogSeverityName(LogSeverity severity) { + return LogSeverityNames[severity]; +} + +static bool SendEmailInternal(const char* dest, const char* subject, + const char* body, bool use_logging); + +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: +#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__ + PrefixFormatter(CustomPrefixCallback callback, void* data) noexcept + : version{V1}, callback_v1{callback}, data{data} {} +#if defined(__GNUG__) +# pragma GCC diagnostic pop +#elif defined(_MSC_VER) +# pragma warning(pop) +#endif // __GNUG__ + 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 V1: +#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__ + callback_v1(s, + LogMessageInfo(LogSeverityNames[message.severity()], + message.basename(), message.line(), + message.thread_id(), message.time()), + data); +#if defined(__GNUG__) +# pragma GCC diagnostic pop +#elif defined(_MSC_VER) +# pragma warning(pop) +#endif // __GNUG__ + break; + case V2: + callback_v2(s, message, data); + break; + } + } + + PrefixFormatter(const PrefixFormatter& other) = delete; + PrefixFormatter& operator=(const PrefixFormatter& other) = delete; + + private: + enum Version { V1, V2 } version; + union { +#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__ + CustomPrefixCallback callback_v1; +#if defined(__GNUG__) +# pragma GCC diagnostic pop +#elif defined(_MSC_VER) +# pragma warning(pop) +#endif // __GNUG__ + PrefixFormatterCallback callback_v2; + }; + // User-provided data to pass to the callback: + void* data; +}; + +std::unique_ptr g_prefix_formatter; + +// Encapsulates all file-system related state +class LogFileObject : public base::Logger { + public: + LogFileObject(LogSeverity severity, const char* base_filename); + ~LogFileObject() override; + + void Write(bool force_flush, // Should we force a flush here? + const std::chrono::system_clock::time_point& + timestamp, // Timestamp for this entry + const char* message, size_t message_len) override; + + // Configuration options + void SetBasename(const char* basename); + void SetExtension(const char* ext); + void SetSymlinkBasename(const char* symlink_basename); + + // Normal flushing routine + void Flush() override; + + // It is the actual file length for the system loggers, + // i.e., INFO, ERROR, etc. + uint32 LogSize() override { + std::lock_guard l{mutex_}; + return file_length_; + } + + // Internal flush routine. Exposed so that FlushLogFilesUnsafe() + // can avoid grabbing a lock. Usually Flush() calls it after + // acquiring lock_. + void FlushUnlocked(const std::chrono::system_clock::time_point& now); + + private: + static const uint32 kRolloverAttemptFrequency = 0x20; + + std::mutex mutex_; + bool base_filename_selected_; + string base_filename_; + string symlink_basename_; + string filename_extension_; // option users can specify (eg to add port#) + std::unique_ptr file_; + LogSeverity severity_; + uint32 bytes_since_flush_{0}; + uint32 dropped_mem_length_{0}; + uint32 file_length_{0}; + unsigned int rollover_attempt_; + std::chrono::system_clock::time_point + next_flush_time_; // cycle count at which to flush log + std::chrono::system_clock::time_point start_time_; + + // Actually create a logfile using the value of base_filename_ and the + // optional argument time_pid_string + // REQUIRES: lock_ is held + bool CreateLogfile(const string& time_pid_string); +}; + +// Encapsulate all log cleaner related states +class LogCleaner { + public: + LogCleaner(); + + // Setting overdue to 0 days will delete all logs. + void Enable(const std::chrono::minutes& overdue); + void Disable(); + + void Run(const std::chrono::system_clock::time_point& current_time, + bool base_filename_selected, const string& base_filename, + const string& filename_extension); + + bool enabled() const { return enabled_; } + + private: + vector GetOverdueLogNames( + string log_directory, + const std::chrono::system_clock::time_point& current_time, + const string& base_filename, const string& filename_extension) const; + + bool IsLogFromCurrentProject(const string& filepath, + const string& base_filename, + const string& filename_extension) const; + + bool IsLogLastModifiedOver( + const string& filepath, + const std::chrono::system_clock::time_point& current_time) const; + + bool enabled_{false}; + std::chrono::minutes overdue_{ + std::chrono::duration>{1}}; + std::chrono::system_clock::time_point + next_cleanup_time_; // cycle count at which to clean overdue log +}; + +LogCleaner log_cleaner; + +} // namespace + +class LogDestination { + public: + friend class LogMessage; + friend void ReprintFatalMessage(); + friend base::Logger* base::GetLogger(LogSeverity); + friend void base::SetLogger(LogSeverity, base::Logger*); + + // These methods are just forwarded to by their global versions. + static void SetLogDestination(LogSeverity severity, + const char* base_filename); + static void SetLogSymlink(LogSeverity severity, const char* symlink_basename); + static void AddLogSink(LogSink* destination); + static void RemoveLogSink(LogSink* destination); + static void SetLogFilenameExtension(const char* filename_extension); + static void SetStderrLogging(LogSeverity min_severity); + static void SetEmailLogging(LogSeverity min_severity, const char* addresses); + static void LogToStderr(); + // Flush all log files that are at least at the given severity level + static void FlushLogFiles(int min_severity); + static void FlushLogFilesUnsafe(int min_severity); + + // we set the maximum size of our packet to be 1400, the logic being + // to prevent fragmentation. + // Really this number is arbitrary. + static const int kNetworkBytes = 1400; + + static const string& hostname(); + static const bool& terminal_supports_color() { + return terminal_supports_color_; + } + + static void DeleteLogDestinations(); + LogDestination(LogSeverity severity, const char* base_filename); + + private: +#if defined(__cpp_lib_shared_mutex) && (__cpp_lib_shared_mutex >= 201505L) + // Use untimed shared mutex + using SinkMutex = std::shared_mutex; + using SinkLock = std::lock_guard; +#else // !(defined(__cpp_lib_shared_mutex) && (__cpp_lib_shared_mutex >= + // 201505L)) Fallback to timed shared mutex + using SinkMutex = std::shared_timed_mutex; + using SinkLock = std::unique_lock; +#endif // defined(__cpp_lib_shared_mutex) && (__cpp_lib_shared_mutex >= + // 201505L) + + friend std::default_delete; + ~LogDestination(); + + // Take a log message of a particular severity and log it to stderr + // iff it's of a high enough severity to deserve it. + static void MaybeLogToStderr(LogSeverity severity, const char* message, + size_t message_len, size_t prefix_len); + + // Take a log message of a particular severity and log it to email + // iff it's of a high enough severity to deserve it. + static void MaybeLogToEmail(LogSeverity severity, const char* message, + size_t len); + // Take a log message of a particular severity and log it to a file + // iff the base filename is not "" (which means "don't log to me") + static void MaybeLogToLogfile( + LogSeverity severity, + const std::chrono::system_clock::time_point& timestamp, + const char* message, size_t len); + // Take a log message of a particular severity and log it to the file + // for that severity and also for all files with severity less than + // this severity. + static void LogToAllLogfiles( + LogSeverity severity, + const std::chrono::system_clock::time_point& timestamp, + const char* message, size_t len); + + // 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, + 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 LogDestination* log_destination(LogSeverity severity); + + base::Logger* GetLoggerImpl() const { return logger_; } + void SetLoggerImpl(base::Logger* logger); + void ResetLoggerImpl() { SetLoggerImpl(&fileobject_); } + + LogFileObject fileobject_; + base::Logger* logger_; // Either &fileobject_, or wrapper around it + + static std::unique_ptr log_destinations_[NUM_SEVERITIES]; + static std::underlying_type_t email_logging_severity_; + static string addresses_; + static string hostname_; + static bool terminal_supports_color_; + + // arbitrary global logging destinations. + static std::unique_ptr> sinks_; + + // Protects the vector sinks_, + // but not the LogSink objects its elements reference. + static SinkMutex sink_mutex_; + + // Disallow + LogDestination(const LogDestination&) = delete; + LogDestination& operator=(const LogDestination&) = delete; +}; + +// Errors do not get logged to email by default. +std::underlying_type_t LogDestination::email_logging_severity_ = + 99999; + +string LogDestination::addresses_; +string LogDestination::hostname_; + +std::unique_ptr> LogDestination::sinks_; +LogDestination::SinkMutex LogDestination::sink_mutex_; +bool LogDestination::terminal_supports_color_ = TerminalSupportsColor(); + +/* static */ +const string& LogDestination::hostname() { + if (hostname_.empty()) { + GetHostName(&hostname_); + if (hostname_.empty()) { + hostname_ = "(unknown)"; + } + } + return hostname_; +} + +LogDestination::LogDestination(LogSeverity severity, const char* base_filename) + : fileobject_(severity, base_filename), logger_(&fileobject_) {} + +LogDestination::~LogDestination() { ResetLoggerImpl(); } + +void LogDestination::SetLoggerImpl(base::Logger* logger) { + if (logger_ == logger) { + // Prevent releasing currently held sink on reset + return; + } + + if (logger_ && logger_ != &fileobject_) { + // Delete user-specified logger set via SetLogger(). + delete logger_; + } + logger_ = logger; +} + +inline void LogDestination::FlushLogFilesUnsafe(int min_severity) { + // assume we have the log_mutex or we simply don't care + // about it + std::for_each(std::next(std::begin(log_destinations_), min_severity), + std::end(log_destinations_), + [now = std::chrono::system_clock::now()]( + std::unique_ptr& log) { + if (log != nullptr) { + // Flush the base fileobject_ logger directly instead of + // going through any wrappers to reduce chance of deadlock. + log->fileobject_.FlushUnlocked(now); + } + }); +} + +inline void LogDestination::FlushLogFiles(int min_severity) { + // Prevent any subtle race conditions by wrapping a mutex lock around + // all this stuff. + std::lock_guard l{log_mutex}; + for (int i = min_severity; i < NUM_SEVERITIES; i++) { + LogDestination* log = log_destination(static_cast(i)); + if (log != nullptr) { + log->logger_->Flush(); + } + } +} + +inline void LogDestination::SetLogDestination(LogSeverity severity, + const char* base_filename) { + // Prevent any subtle race conditions by wrapping a mutex lock around + // all this stuff. + std::lock_guard l{log_mutex}; + log_destination(severity)->fileobject_.SetBasename(base_filename); +} + +inline void LogDestination::SetLogSymlink(LogSeverity severity, + const char* symlink_basename) { + CHECK_GE(severity, 0); + CHECK_LT(severity, NUM_SEVERITIES); + std::lock_guard l{log_mutex}; + log_destination(severity)->fileobject_.SetSymlinkBasename(symlink_basename); +} + +inline void LogDestination::AddLogSink(LogSink* destination) { + // Prevent any subtle race conditions by wrapping a mutex lock around + // all this stuff. + SinkLock l{sink_mutex_}; + if (sinks_ == nullptr) sinks_ = std::make_unique>(); + sinks_->push_back(destination); +} + +inline void LogDestination::RemoveLogSink(LogSink* destination) { + // Prevent any subtle race conditions by wrapping a mutex lock around + // all this stuff. + SinkLock l{sink_mutex_}; + // This doesn't keep the sinks in order, but who cares? + if (sinks_) { + sinks_->erase(std::remove(sinks_->begin(), sinks_->end(), destination), + sinks_->end()); + } +} + +inline void LogDestination::SetLogFilenameExtension(const char* ext) { + // Prevent any subtle race conditions by wrapping a mutex lock around + // all this stuff. + std::lock_guard l{log_mutex}; + for (int severity = 0; severity < NUM_SEVERITIES; ++severity) { + log_destination(static_cast(severity)) + ->fileobject_.SetExtension(ext); + } +} + +inline void LogDestination::SetStderrLogging(LogSeverity min_severity) { + // Prevent any subtle race conditions by wrapping a mutex lock around + // all this stuff. + std::lock_guard l{log_mutex}; + FLAGS_stderrthreshold = min_severity; +} + +inline void LogDestination::LogToStderr() { + // *Don't* put this stuff in a mutex lock, since SetStderrLogging & + // SetLogDestination already do the locking! + SetStderrLogging(GLOG_INFO); // thus everything is "also" logged to stderr + for (int i = 0; i < NUM_SEVERITIES; ++i) { + SetLogDestination(static_cast(i), + ""); // "" turns off logging to a logfile + } +} + +inline void LogDestination::SetEmailLogging(LogSeverity min_severity, + const char* addresses) { + // Prevent any subtle race conditions by wrapping a mutex lock around + // all this stuff. + std::lock_guard l{log_mutex}; + LogDestination::email_logging_severity_ = min_severity; + LogDestination::addresses_ = addresses; +} + +static void ColoredWriteToStderrOrStdout(FILE* output, LogSeverity severity, + const char* message, size_t len) { + bool is_stdout = (output == stdout); + const GLogColor color = (LogDestination::terminal_supports_color() && + ((!is_stdout && FLAGS_colorlogtostderr) || + (is_stdout && FLAGS_colorlogtostdout))) + ? SeverityToColor(severity) + : COLOR_DEFAULT; + + // Avoid using cerr from this module since we may get called during + // exit code, and cerr may be partially or fully destroyed by then. + if (COLOR_DEFAULT == color) { + fwrite(message, len, 1, output); + return; + } +#ifdef GLOG_OS_WINDOWS + const HANDLE output_handle = + GetStdHandle(is_stdout ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE); + + // Gets the current text color. + CONSOLE_SCREEN_BUFFER_INFO buffer_info; + GetConsoleScreenBufferInfo(output_handle, &buffer_info); + const WORD old_color_attrs = buffer_info.wAttributes; + + // We need to flush the stream buffers into the console before each + // SetConsoleTextAttribute call lest it affect the text that is already + // printed but has not yet reached the console. + fflush(output); + SetConsoleTextAttribute(output_handle, + GetColorAttribute(color) | FOREGROUND_INTENSITY); + fwrite(message, len, 1, output); + fflush(output); + // Restores the text color. + SetConsoleTextAttribute(output_handle, old_color_attrs); +#else + fprintf(output, "\033[0;3%sm", GetAnsiColorCode(color)); + fwrite(message, len, 1, output); + fprintf(output, "\033[m"); // Resets the terminal to default. +#endif // GLOG_OS_WINDOWS +} + +static void ColoredWriteToStdout(LogSeverity severity, const char* message, + size_t len) { + FILE* output = stdout; + // We also need to send logs to the stderr when the severity is + // higher or equal to the stderr threshold. + if (severity >= FLAGS_stderrthreshold) { + output = stderr; + } + ColoredWriteToStderrOrStdout(output, severity, message, len); +} + +static void ColoredWriteToStderr(LogSeverity severity, const char* message, + size_t len) { + ColoredWriteToStderrOrStdout(stderr, severity, message, len); +} + +static void WriteToStderr(const char* message, size_t len) { + // Avoid using cerr from this module since we may get called during + // exit code, and cerr may be partially or fully destroyed by then. + fwrite(message, len, 1, stderr); +} + +inline void LogDestination::MaybeLogToStderr(LogSeverity severity, + const char* message, + size_t message_len, + size_t prefix_len) { + if ((severity >= FLAGS_stderrthreshold) || FLAGS_alsologtostderr) { + ColoredWriteToStderr(severity, message, message_len); + AlsoErrorWrite(severity, + glog_internal_namespace_::ProgramInvocationShortName(), + message + prefix_len); + } +} + +inline void LogDestination::MaybeLogToEmail(LogSeverity severity, + const char* message, size_t len) { + if (severity >= email_logging_severity_ || severity >= FLAGS_logemaillevel) { + string to(FLAGS_alsologtoemail); + if (!addresses_.empty()) { + if (!to.empty()) { + to += ","; + } + to += addresses_; + } + const string subject( + string("[LOG] ") + LogSeverityNames[severity] + ": " + + glog_internal_namespace_::ProgramInvocationShortName()); + string body(hostname()); + body += "\n\n"; + body.append(message, len); + + // should NOT use SendEmail(). The caller of this function holds the + // log_mutex and SendEmail() calls LOG/VLOG which will block trying to + // acquire the log_mutex object. Use SendEmailInternal() and set + // use_logging to false. + SendEmailInternal(to.c_str(), subject.c_str(), body.c_str(), false); + } +} + +inline void LogDestination::MaybeLogToLogfile( + LogSeverity severity, + const std::chrono::system_clock::time_point& timestamp, const char* message, + size_t len) { + const bool should_flush = severity > FLAGS_logbuflevel; + LogDestination* destination = log_destination(severity); + destination->logger_->Write(should_flush, timestamp, message, len); +} + +inline void LogDestination::LogToAllLogfiles( + LogSeverity severity, + const std::chrono::system_clock::time_point& timestamp, const char* message, + size_t len) { + if (FLAGS_logtostdout) { // global flag: never log to file + ColoredWriteToStdout(severity, message, len); + } else if (FLAGS_logtostderr) { // global flag: never log to file + ColoredWriteToStderr(severity, message, len); + } else { + for (int i = severity; i >= 0; --i) { + LogDestination::MaybeLogToLogfile(static_cast(i), timestamp, + message, len); + } + } +} + +inline void LogDestination::LogToSinks(LogSeverity severity, + const char* full_filename, + const char* base_filename, int line, + const LogMessageTime& time, + const char* message, + size_t message_len) { + std::shared_lock 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); + } + } +} + +inline void LogDestination::WaitForSinks( + logging::internal::LogMessageData* data) { + std::shared_lock l{sink_mutex_}; + if (sinks_) { + for (size_t i = sinks_->size(); i-- > 0;) { + (*sinks_)[i]->WaitTillSent(); + } + } + const bool send_to_sink = + (data->send_method_ == &LogMessage::SendToSink) || + (data->send_method_ == &LogMessage::SendToSinkAndLog); + if (send_to_sink && data->sink_ != nullptr) { + data->sink_->WaitTillSent(); + } +} + +std::unique_ptr + LogDestination::log_destinations_[NUM_SEVERITIES]; + +inline LogDestination* LogDestination::log_destination(LogSeverity severity) { + if (log_destinations_[severity] == nullptr) { + log_destinations_[severity] = + std::make_unique(severity, nullptr); + } + return log_destinations_[severity].get(); +} + +void LogDestination::DeleteLogDestinations() { + for (auto& log_destination : log_destinations_) { + log_destination.reset(); + } + SinkLock l{sink_mutex_}; + sinks_.reset(); +} + +namespace { + +std::string g_application_fingerprint; + +} // namespace + +void SetApplicationFingerprint(const std::string& fingerprint) { + g_application_fingerprint = fingerprint; +} + +namespace { + +// Directory delimiter; Windows supports both forward slashes and backslashes +#ifdef GLOG_OS_WINDOWS +const char possible_dir_delim[] = {'\\', '/'}; +#else +const char possible_dir_delim[] = {'/'}; +#endif + +string PrettyDuration(const std::chrono::duration& secs) { + std::stringstream result; + int mins = secs.count() / 60; + int hours = mins / 60; + mins = mins % 60; + int s = secs.count() % 60; + result.fill('0'); + result << hours << ':' << setw(2) << mins << ':' << setw(2) << s; + return result.str(); +} + +LogFileObject::LogFileObject(LogSeverity severity, const char* base_filename) + : base_filename_selected_(base_filename != nullptr), + base_filename_((base_filename != nullptr) ? base_filename : ""), + symlink_basename_(glog_internal_namespace_::ProgramInvocationShortName()), + filename_extension_(), + severity_(severity), + rollover_attempt_(kRolloverAttemptFrequency - 1), + start_time_(std::chrono::system_clock::now()) {} + +LogFileObject::~LogFileObject() { + std::lock_guard l{mutex_}; + file_ = nullptr; +} + +void LogFileObject::SetBasename(const char* basename) { + std::lock_guard l{mutex_}; + base_filename_selected_ = true; + if (base_filename_ != basename) { + // Get rid of old log file since we are changing names + if (file_ != nullptr) { + file_ = nullptr; + rollover_attempt_ = kRolloverAttemptFrequency - 1; + } + base_filename_ = basename; + } +} + +void LogFileObject::SetExtension(const char* ext) { + std::lock_guard l{mutex_}; + if (filename_extension_ != ext) { + // Get rid of old log file since we are changing names + if (file_ != nullptr) { + file_ = nullptr; + rollover_attempt_ = kRolloverAttemptFrequency - 1; + } + filename_extension_ = ext; + } +} + +void LogFileObject::SetSymlinkBasename(const char* symlink_basename) { + std::lock_guard l{mutex_}; + symlink_basename_ = symlink_basename; +} + +void LogFileObject::Flush() { + std::lock_guard l{mutex_}; + FlushUnlocked(std::chrono::system_clock::now()); +} + +void LogFileObject::FlushUnlocked( + const std::chrono::system_clock::time_point& now) { + if (file_ != nullptr) { + fflush(file_.get()); + bytes_since_flush_ = 0; + } + // Figure out when we are due for another flush. + next_flush_time_ = + now + std::chrono::duration_cast( + std::chrono::duration{FLAGS_logbufsecs}); +} + +bool LogFileObject::CreateLogfile(const string& time_pid_string) { + string string_filename = base_filename_; + if (FLAGS_timestamp_in_logfile_name) { + string_filename += time_pid_string; + } + string_filename += filename_extension_; + const char* filename = string_filename.c_str(); + // only write to files, create if non-existant. + int flags = O_WRONLY | O_CREAT; + if (FLAGS_timestamp_in_logfile_name) { + // demand that the file is unique for our timestamp (fail if it exists). + flags = flags | O_EXCL; + } + FileDescriptor fd{ + open(filename, flags, static_cast(FLAGS_logfile_mode))}; + if (!fd) 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); + + // 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 + // (otherwise open would fail because the O_EXCL flag on similar filename). + // locks are released on unlock or close() automatically, only after log is + // released. + // This will work after a fork as it is not inherited (not stored in the fd). + // Lock will not be lost because the file is opened with exclusive lock + // (write) and we will never read from it inside the process. + // TODO: windows implementation of this (as flock is not available on + // mingw). + static struct flock w_lock; + + w_lock.l_type = F_WRLCK; + w_lock.l_start = 0; + w_lock.l_whence = SEEK_SET; + w_lock.l_len = 0; + + int wlock_ret = fcntl(fd.get(), F_SETLK, &w_lock); + if (wlock_ret == -1) { + 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! + if (FLAGS_timestamp_in_logfile_name) { + unlink(filename); // Erase the half-baked evidence: an unusable log file, + // only if we just created it. + } + return false; + } +#ifdef GLOG_OS_WINDOWS + // 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) { + return false; + } + } +#endif + // We try to create a symlink called ., + // which is easier to use. (Every time we create a new logfile, + // we destroy the old symlink and create a new one, so it always + // points to the latest logfile.) If it fails, we're sad but it's + // no error. + if (!symlink_basename_.empty()) { + // take directory from filename + const char* slash = strrchr(filename, PATH_SEPARATOR); + const string linkname = + symlink_basename_ + '.' + LogSeverityNames[severity_]; + string linkpath; + if (slash) + linkpath = string( + filename, static_cast(slash - filename + 1)); // get dirname + linkpath += linkname; + unlink(linkpath.c_str()); // delete old one if it exists + +#if defined(GLOG_OS_WINDOWS) + // TODO(hamaji): Create lnk file on Windows? +#elif defined(HAVE_UNISTD_H) + // We must have unistd.h. + // Make the symlink be relative (in the same dir) so that if the + // entire log directory gets relocated the link is still valid. + const char* linkdest = slash ? (slash + 1) : filename; + if (symlink(linkdest, linkpath.c_str()) != 0) { + // silently ignore failures + } + + // Make an additional link to the log file in a place specified by + // FLAGS_log_link, if indicated + if (!FLAGS_log_link.empty()) { + linkpath = FLAGS_log_link + "/" + linkname; + unlink(linkpath.c_str()); // delete old one if it exists + if (symlink(filename, linkpath.c_str()) != 0) { + // silently ignore failures + } + } +#endif + } + + return true; // Everything worked +} + +void LogFileObject::Write( + bool force_flush, const std::chrono::system_clock::time_point& timestamp, + const char* message, size_t message_len) { + std::lock_guard l{mutex_}; + + // We don't log if the base_name_ is "" (which means "don't write") + if (base_filename_selected_ && base_filename_.empty()) { + return; + } + + auto cleanupLogs = [this, current_time = timestamp] { + if (log_cleaner.enabled()) { + log_cleaner.Run(current_time, base_filename_selected_, base_filename_, + filename_extension_); + } + }; + + // Remove old logs + ScopedExit cleanupAtEnd{cleanupLogs}; + + if (file_length_ >> 20U >= MaxLogSize() || PidHasChanged()) { + file_ = nullptr; + file_length_ = bytes_since_flush_ = dropped_mem_length_ = 0; + rollover_attempt_ = kRolloverAttemptFrequency - 1; + } + + // If there's no destination file, make one before outputting + if (file_ == nullptr) { + // Try to rollover the log file every 32 log messages. The only time + // this could matter would be when we have trouble creating the log + // file. If that happens, we'll lose lots of log messages, of course! + if (++rollover_attempt_ != kRolloverAttemptFrequency) return; + rollover_attempt_ = 0; + + struct ::tm tm_time; + std::time_t t = std::chrono::system_clock::to_time_t(timestamp); + + if (FLAGS_log_utc_time) { + gmtime_r(&t, &tm_time); + } else { + localtime_r(&t, &tm_time); + } + + // The logfile's filename will have the date/time & pid in it + ostringstream time_pid_stream; + time_pid_stream.fill('0'); + time_pid_stream << 1900 + tm_time.tm_year << setw(2) << 1 + tm_time.tm_mon + << setw(2) << tm_time.tm_mday << '-' << setw(2) + << tm_time.tm_hour << setw(2) << tm_time.tm_min << setw(2) + << tm_time.tm_sec << '.' << GetMainThreadPid(); + const string& time_pid_string = time_pid_stream.str(); + + if (base_filename_selected_) { + if (!CreateLogfile(time_pid_string)) { + perror("Could not create log file"); + fprintf(stderr, "COULD NOT CREATE LOGFILE '%s'!\n", + time_pid_string.c_str()); + return; + } + } else { + // If no base filename for logs of this severity has been set, use a + // default base filename of + // "...log..". So + // logfiles will have names like + // webserver.examplehost.root.log.INFO.19990817-150000.4354, where + // 19990817 is a date (1999 August 17), 150000 is a time (15:00:00), + // and 4354 is the pid of the logging process. The date & time reflect + // when the file was created for output. + // + // Where does the file get put? Successively try the directories + // "/tmp", and "." + string stripped_filename( + glog_internal_namespace_::ProgramInvocationShortName()); + string hostname; + GetHostName(&hostname); + + string uidname = MyUserName(); + // We should not call CHECK() here because this function can be + // called after holding on to log_mutex. We don't want to + // attempt to hold on to the same mutex, and get into a + // deadlock. Simply use a name like invalid-user. + if (uidname.empty()) uidname = "invalid-user"; + + stripped_filename = stripped_filename + '.' + hostname + '.' + uidname + + ".log." + LogSeverityNames[severity_] + '.'; + // We're going to (potentially) try to put logs in several different dirs + const vector& log_dirs = GetLoggingDirectories(); + + // Go through the list of dirs, and try to create the log file in each + // until we succeed or run out of options + bool success = false; + for (const auto& log_dir : log_dirs) { + base_filename_ = log_dir + "/" + stripped_filename; + if (CreateLogfile(time_pid_string)) { + success = true; + break; + } + } + // If we never succeeded, we have to give up + if (success == false) { + perror("Could not create logging file"); + fprintf(stderr, "COULD NOT CREATE A LOGGINGFILE %s!", + time_pid_string.c_str()); + return; + } + } + + // Write a header message into the log file + if (FLAGS_log_file_header) { + ostringstream file_header_stream; + file_header_stream.fill('0'); + file_header_stream << "Log file created at: " << 1900 + tm_time.tm_year + << '/' << setw(2) << 1 + tm_time.tm_mon << '/' + << setw(2) << tm_time.tm_mday << ' ' << setw(2) + << tm_time.tm_hour << ':' << setw(2) << tm_time.tm_min + << ':' << setw(2) << tm_time.tm_sec + << (FLAGS_log_utc_time ? " UTC\n" : "\n") + << "Running on machine: " << LogDestination::hostname() + << '\n'; + + if (!g_application_fingerprint.empty()) { + file_header_stream << "Application fingerprint: " + << g_application_fingerprint << '\n'; + } + const char* const date_time_format = FLAGS_log_year_in_prefix + ? "yyyymmdd hh:mm:ss.uuuuuu" + : "mmdd hh:mm:ss.uuuuuu"; + file_header_stream + << "Running duration (h:mm:ss): " + << PrettyDuration( + std::chrono::duration_cast>( + timestamp - start_time_)) + << '\n' + << "Log line format: [IWEF]" << date_time_format << " " + << "threadid file:line] msg" << '\n'; + 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()); + file_length_ += header_len; + bytes_since_flush_ += header_len; + } + } + + // Write to LOG file + if (!stop_writing) { + // fwrite() doesn't return an error when the disk is full, for + // messages that are less than 4096 bytes. When the disk is full, + // it returns the message length for messages that are less than + // 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()); + if (FLAGS_stop_logging_if_full_disk && + errno == ENOSPC) { // disk full, stop writing to disk + stop_writing = true; // until the disk is + return; + } else { + file_length_ += message_len; + bytes_since_flush_ += message_len; + } + } else { + if (timestamp >= next_flush_time_) { + stop_writing = false; // check to see if disk has free space. + } + return; // no need to flush + } + + // See important msgs *now*. Also, flush logs at least every 10^6 chars, + // or every "FLAGS_logbufsecs" seconds. + if (force_flush || (bytes_since_flush_ >= 1000000) || + (timestamp >= next_flush_time_)) { + FlushUnlocked(timestamp); +#ifdef GLOG_OS_LINUX + // Only consider files >= 3MiB + if (FLAGS_drop_log_memory && file_length_ >= (3U << 20U)) { + // Don't evict the most recent 1-2MiB so as not to impact a tailer + // of the log file and to avoid page rounding issue on linux < 4.7 + uint32 total_drop_length = + (file_length_ & ~((1U << 20U) - 1U)) - (1U << 20U); + 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(dropped_mem_length_), + static_cast(this_drop_length), POSIX_FADV_DONTNEED); +# endif + dropped_mem_length_ = total_drop_length; + } + } +#endif + } +} + +LogCleaner::LogCleaner() = default; + +void LogCleaner::Enable(const std::chrono::minutes& overdue) { + enabled_ = true; + overdue_ = overdue; +} + +void LogCleaner::Disable() { enabled_ = false; } + +void LogCleaner::Run(const std::chrono::system_clock::time_point& current_time, + bool base_filename_selected, const string& base_filename, + const string& filename_extension) { + assert(enabled_); + assert(!base_filename_selected || !base_filename.empty()); + + // avoid scanning logs too frequently + if (current_time < next_cleanup_time_) { + return; + } + + next_cleanup_time_ = + current_time + + std::chrono::duration_cast( + std::chrono::duration{FLAGS_logcleansecs}); + + vector dirs; + + if (!base_filename_selected) { + dirs = GetLoggingDirectories(); + } else { + size_t pos = base_filename.find_last_of(possible_dir_delim, string::npos, + sizeof(possible_dir_delim)); + if (pos != string::npos) { + string dir = base_filename.substr(0, pos + 1); + dirs.push_back(dir); + } else { + dirs.emplace_back("."); + } + } + + for (const std::string& dir : dirs) { + vector logs = GetOverdueLogNames(dir, current_time, base_filename, + filename_extension); + for (const std::string& log : logs) { + // NOTE May fail on Windows if the file is still open + int result = unlink(log.c_str()); + if (result != 0) { + perror(("Could not remove overdue log " + log).c_str()); + } + } + } +} + +vector LogCleaner::GetOverdueLogNames( + string log_directory, + const std::chrono::system_clock::time_point& current_time, + const string& base_filename, const string& filename_extension) const { + // The names of overdue logs. + vector overdue_log_names; + + // Try to get all files within log_directory. + DIR* dir; + struct dirent* ent; + + if ((dir = opendir(log_directory.c_str()))) { + while ((ent = readdir(dir))) { + if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) { + continue; + } + + string filepath = ent->d_name; + const char* const dir_delim_end = + possible_dir_delim + sizeof(possible_dir_delim); + + if (!log_directory.empty() && + std::find(possible_dir_delim, dir_delim_end, + log_directory[log_directory.size() - 1]) != dir_delim_end) { + filepath = log_directory + filepath; + } + + if (IsLogFromCurrentProject(filepath, base_filename, + filename_extension) && + IsLogLastModifiedOver(filepath, current_time)) { + overdue_log_names.push_back(filepath); + } + } + closedir(dir); + } + + return overdue_log_names; +} + +bool LogCleaner::IsLogFromCurrentProject( + const string& filepath, const string& base_filename, + const string& filename_extension) const { + // We should remove duplicated delimiters from `base_filename`, e.g., + // before: "/tmp//.." + // after: "/tmp/.." + string cleaned_base_filename; + + const char* const dir_delim_end = + possible_dir_delim + sizeof(possible_dir_delim); + + size_t real_filepath_size = filepath.size(); + for (char c : base_filename) { + if (cleaned_base_filename.empty()) { + cleaned_base_filename += c; + } else if (std::find(possible_dir_delim, dir_delim_end, c) == + dir_delim_end || + (!cleaned_base_filename.empty() && + c != cleaned_base_filename[cleaned_base_filename.size() - 1])) { + cleaned_base_filename += c; + } + } + + // Return early if the filename doesn't start with `cleaned_base_filename`. + if (filepath.find(cleaned_base_filename) != 0) { + return false; + } + + // Check if in the string `filename_extension` is right next to + // `cleaned_base_filename` in `filepath` if the user + // has set a custom filename extension. + if (!filename_extension.empty()) { + if (cleaned_base_filename.size() >= real_filepath_size) { + return false; + } + // for origin version, `filename_extension` is middle of the `filepath`. + string ext = filepath.substr(cleaned_base_filename.size(), + filename_extension.size()); + if (ext == filename_extension) { + cleaned_base_filename += filename_extension; + } else { + // for new version, `filename_extension` is right of the `filepath`. + if (filename_extension.size() >= real_filepath_size) { + return false; + } + real_filepath_size = filepath.size() - filename_extension.size(); + if (filepath.substr(real_filepath_size) != filename_extension) { + return false; + } + } + } + + // The characters after `cleaned_base_filename` should match the format: + // YYYYMMDD-HHMMSS.pid + for (size_t i = cleaned_base_filename.size(); i < real_filepath_size; i++) { + const char& c = filepath[i]; + + if (i <= cleaned_base_filename.size() + 7) { // 0 ~ 7 : YYYYMMDD + if (c < '0' || c > '9') { + return false; + } + + } else if (i == cleaned_base_filename.size() + 8) { // 8: - + if (c != '-') { + return false; + } + + } else if (i <= cleaned_base_filename.size() + 14) { // 9 ~ 14: HHMMSS + if (c < '0' || c > '9') { + return false; + } + + } else if (i == cleaned_base_filename.size() + 15) { // 15: . + if (c != '.') { + return false; + } + + } else if (i >= cleaned_base_filename.size() + 16) { // 16+: pid + if (c < '0' || c > '9') { + return false; + } + } + } + + return true; +} + +bool LogCleaner::IsLogLastModifiedOver( + const string& filepath, + const std::chrono::system_clock::time_point& current_time) const { + // Try to get the last modified time of this file. + struct stat file_stat; + + if (stat(filepath.c_str(), &file_stat) == 0) { + const auto last_modified_time = + std::chrono::system_clock::from_time_t(file_stat.st_mtime); + const auto diff = current_time - last_modified_time; + return diff >= overdue_; + } + + // If failed to get file stat, don't return true! + return false; +} + +} // namespace + +// Static log data space to avoid alloc failures in a LOG(FATAL) +// +// Since multiple threads may call LOG(FATAL), and we want to preserve +// the data from the first call, we allocate two sets of space. One +// 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 bool fatal_msg_exclusive = true; +static logging::internal::LogMessageData fatal_msg_data_exclusive; +static logging::internal::LogMessageData fatal_msg_data_shared; + +#ifdef GLOG_THREAD_LOCAL_STORAGE +// Static thread-local log data space to use, because typically at most one +// LogMessageData object exists (in this case glog makes zero heap memory +// allocations). +static 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)]; +# 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; +# endif // defined(__cpp_lib_byte) && __cpp_lib_byte >= 201603L +#endif // defined(GLOG_THREAD_LOCAL_STORAGE) + +logging::internal::LogMessageData::LogMessageData() + : stream_(message_text_, LogMessage::kMaxLogMessageLen, 0) {} + +LogMessage::LogMessage(const char* file, int line, LogSeverity severity, + int64 ctr, void (LogMessage::*send_method)()) + : allocated_(nullptr) { + Init(file, line, severity, send_method); + data_->stream_.set_ctr(ctr); +} + +LogMessage::LogMessage(const char* file, int line, + const logging::internal::CheckOpString& result) + : allocated_(nullptr) { + Init(file, line, GLOG_FATAL, &LogMessage::SendToLog); + stream() << "Check failed: " << (*result.str_) << " "; +} + +LogMessage::LogMessage(const char* file, int line) : allocated_(nullptr) { + Init(file, line, GLOG_INFO, &LogMessage::SendToLog); +} + +LogMessage::LogMessage(const char* file, int line, LogSeverity severity) + : allocated_(nullptr) { + Init(file, line, severity, &LogMessage::SendToLog); +} + +LogMessage::LogMessage(const char* file, int line, LogSeverity severity, + LogSink* sink, bool also_send_to_log) + : allocated_(nullptr) { + Init(file, line, severity, + also_send_to_log ? &LogMessage::SendToSinkAndLog + : &LogMessage::SendToSink); + data_->sink_ = sink; // override Init()'s setting to nullptr +} + +LogMessage::LogMessage(const char* file, int line, LogSeverity severity, + vector* outvec) + : allocated_(nullptr) { + Init(file, line, severity, &LogMessage::SaveOrSendToLog); + data_->outvec_ = outvec; // override Init()'s setting to nullptr +} + +LogMessage::LogMessage(const char* file, int line, LogSeverity severity, + string* message) + : allocated_(nullptr) { + Init(file, line, severity, &LogMessage::WriteToStringAndLog); + data_->message_ = message; // override Init()'s setting to nullptr +} + +void LogMessage::Init(const char* file, int line, LogSeverity severity, + void (LogMessage::*send_method)()) { + allocated_ = nullptr; + if (severity != GLOG_FATAL || !exit_on_dfatal) { +#ifdef GLOG_THREAD_LOCAL_STORAGE + // No need for locking, because this is thread local. + if (thread_data_available) { + thread_data_available = false; + data_ = new (&thread_msg_data) logging::internal::LogMessageData; + } else { + allocated_ = new logging::internal::LogMessageData(); + data_ = allocated_; + } +#else // !defined(GLOG_THREAD_LOCAL_STORAGE) + allocated_ = new logging::internal::LogMessageData(); + data_ = allocated_; +#endif // defined(GLOG_THREAD_LOCAL_STORAGE) + data_->first_fatal_ = false; + } else { + std::lock_guard l{fatal_msg_lock}; + if (fatal_msg_exclusive) { + fatal_msg_exclusive = false; + data_ = &fatal_msg_data_exclusive; + data_->first_fatal_ = true; + } else { + data_ = &fatal_msg_data_shared; + data_->first_fatal_ = false; + } + } + + data_->preserved_errno_ = errno; + data_->severity_ = severity; + data_->line_ = line; + data_->send_method_ = send_method; + data_->sink_ = nullptr; + data_->outvec_ = nullptr; + + const auto now = std::chrono::system_clock::now(); + time_ = LogMessageTime(now); + + data_->num_chars_to_log_ = 0; + data_->num_chars_to_syslog_ = 0; + 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] + // (log level, GMT year, month, date, time, thread_id, file basename, line) + // We exclude the thread_id for the default thread. + if (FLAGS_log_prefix && (line != kNoLogPrefix)) { + std::ios saved_fmt(nullptr); + saved_fmt.copyfmt(stream()); + stream().fill('0'); + if (g_prefix_formatter == nullptr) { + stream() << LogSeverityNames[severity][0]; + if (FLAGS_log_year_in_prefix) { + stream() << setw(4) << 1900 + time_.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) + << data_->thread_id_ << setfill('0') << ' ' << data_->basename_ + << ':' << data_->line_ << "] "; + } else { + (*g_prefix_formatter)(stream(), *this); + stream() << " "; + } + stream().copyfmt(saved_fmt); + } + data_->num_prefix_chars_ = data_->stream_.pcount(); + + if (!FLAGS_log_backtrace_at.empty()) { + char fileline[128]; + std::snprintf(fileline, sizeof(fileline), "%s:%d", data_->basename_, line); +#ifdef HAVE_STACKTRACE + if (FLAGS_log_backtrace_at == fileline) { + string stacktrace = GetStackTrace(); + stream() << " (stacktrace:\n" << stacktrace << ") "; + } +#endif + } +} + +LogSeverity LogMessage::severity() const noexcept { return data_->severity_; } + +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) { + Flush(); + bool fail = data_->severity_ == GLOG_FATAL && exit_on_dfatal; +#ifdef GLOG_THREAD_LOCAL_STORAGE + if (data_ == static_cast(&thread_msg_data)) { + data_->~LogMessageData(); + thread_data_available = true; + } else { + delete allocated_; + } +#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_; } + +ostream& LogMessage::stream() { return data_->stream_; } + +// Flush buffered message, called by the destructor, or any other function +// that needs to synchronize the log. +void LogMessage::Flush() { + if (data_->has_been_flushed_ || data_->severity_ < FLAGS_minloglevel) { + return; + } + + data_->num_chars_to_log_ = data_->stream_.pcount(); + data_->num_chars_to_syslog_ = + data_->num_chars_to_log_ - data_->num_prefix_chars_; + + // Do we need to add a \n to the end of this message? + bool append_newline = + (data_->message_text_[data_->num_chars_to_log_ - 1] != '\n'); + char original_final_char = '\0'; + + // If we do need to add a \n, we'll do it by violating the memory of the + // ostrstream buffer. This is quick, and we'll make sure to undo our + // modification before anything else is done with the ostrstream. It + // would be preferable not to do things this way, but it seems to be + // the best way to deal with this. + if (append_newline) { + original_final_char = data_->message_text_[data_->num_chars_to_log_]; + data_->message_text_[data_->num_chars_to_log_++] = '\n'; + } + data_->message_text_[data_->num_chars_to_log_] = '\0'; + + // Prevent any subtle race conditions by wrapping a mutex lock around + // the actual logging action per se. + { + std::lock_guard l{log_mutex}; + (this->*(data_->send_method_))(); + ++num_messages_[static_cast(data_->severity_)]; + } + LogDestination::WaitForSinks(data_); + + if (append_newline) { + // Fix the ostrstream back how it was before we screwed with it. + // It's 99.44% certain that we don't need to worry about doing this. + data_->message_text_[data_->num_chars_to_log_ - 1] = original_final_char; + } + + // If errno was already set before we enter the logging call, we'll + // set it back to that value when we return from the logging call. + // It happens often that we log an error message after a syscall + // failure, which can potentially set the errno to some other + // values. We would like to preserve the original errno. + if (data_->preserved_errno_ != 0) { + errno = data_->preserved_errno_; + } + + // Note that this message is now safely logged. If we're asked to flush + // again, as a result of destruction, say, we'll do nothing on future calls. + data_->has_been_flushed_ = true; +} + +// Copy of first FATAL log message so that we can print it out again +// after all the stack traces. To preserve legacy behavior, we don't +// use fatal_msg_data_exclusive. +static std::chrono::system_clock::time_point fatal_time; +static char fatal_message[256]; + +void ReprintFatalMessage() { + if (fatal_message[0]) { + const size_t n = strlen(fatal_message); + if (!FLAGS_logtostderr) { + // Also write to stderr (don't color to avoid terminal checks) + WriteToStderr(fatal_message, n); + } + LogDestination::LogToAllLogfiles(GLOG_ERROR, fatal_time, fatal_message, n); + } +} + +// L >= log_mutex (callers must hold the log_mutex). +void LogMessage::SendToLog() EXCLUSIVE_LOCKS_REQUIRED(log_mutex) { + static bool already_warned_before_initgoogle = false; + + RAW_DCHECK(data_->num_chars_to_log_ > 0 && + data_->message_text_[data_->num_chars_to_log_ - 1] == '\n', + ""); + + // Messages of a given severity get logged to lower severity logs, too + + if (!already_warned_before_initgoogle && !IsGoogleLoggingInitialized()) { + const char w[] = + "WARNING: Logging before InitGoogleLogging() is " + "written to STDERR\n"; + WriteToStderr(w, strlen(w)); + already_warned_before_initgoogle = true; + } + + // global flag: never log to file if set. Also -- don't log to a + // file if we haven't parsed the command line flags to get the + // program name. + if (FLAGS_logtostderr || FLAGS_logtostdout || !IsGoogleLoggingInitialized()) { + if (FLAGS_logtostdout) { + ColoredWriteToStdout(data_->severity_, data_->message_text_, + data_->num_chars_to_log_); + } else { + ColoredWriteToStderr(data_->severity_, data_->message_text_, + data_->num_chars_to_log_); + } + + // this could be protected by a flag if necessary. + LogDestination::LogToSinks( + data_->severity_, data_->fullname_, data_->basename_, data_->line_, + time_, data_->message_text_ + data_->num_prefix_chars_, + (data_->num_chars_to_log_ - data_->num_prefix_chars_ - 1)); + } else { + // log this message to all log files of severity <= severity_ + LogDestination::LogToAllLogfiles(data_->severity_, time_.when(), + data_->message_text_, + data_->num_chars_to_log_); + + LogDestination::MaybeLogToStderr(data_->severity_, data_->message_text_, + data_->num_chars_to_log_, + data_->num_prefix_chars_); + LogDestination::MaybeLogToEmail(data_->severity_, data_->message_text_, + data_->num_chars_to_log_); + LogDestination::LogToSinks( + data_->severity_, data_->fullname_, data_->basename_, data_->line_, + time_, data_->message_text_ + data_->num_prefix_chars_, + (data_->num_chars_to_log_ - data_->num_prefix_chars_ - 1)); + // NOTE: -1 removes trailing \n + } + + // If we log a FATAL message, flush all the log destinations, then toss + // a signal for others to catch. We leave the logs in a state that + // someone else can use them (as long as they flush afterwards) + if (data_->severity_ == GLOG_FATAL && exit_on_dfatal) { + if (data_->first_fatal_) { + // Store crash information so that it is accessible from within signal + // handlers that may be invoked later. + RecordCrashReason(&crash_reason); + SetCrashReason(&crash_reason); + + // Store shortened fatal message for other logs and GWQ status + const size_t copy = + min(data_->num_chars_to_log_, sizeof(fatal_message) - 1); + memcpy(fatal_message, data_->message_text_, copy); + fatal_message[copy] = '\0'; + fatal_time = time_.when(); + } + + if (!FLAGS_logtostderr && !FLAGS_logtostdout) { + for (auto& log_destination : LogDestination::log_destinations_) { + if (log_destination) { + log_destination->logger_->Write( + true, std::chrono::system_clock::time_point{}, "", 0); + } + } + } + + LogDestination::WaitForSinks(data_); + } +} + +void LogMessage::RecordCrashReason(logging::internal::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_ + + fatal_msg_data_exclusive.num_prefix_chars_; +#ifdef HAVE_STACKTRACE + // Retrieve the stack trace, omitting the logging frames that got us here. + reason->depth = GetStackTrace(reason->stack, ARRAYSIZE(reason->stack), 4); +#else + reason->depth = 0; +#endif +} + +GLOG_NO_EXPORT logging_fail_func_t g_logging_fail_func = + reinterpret_cast(&abort); + +NullStream::NullStream() : LogMessage::LogStream(message_buffer_, 2, 0) {} +NullStream::NullStream(const char* /*file*/, int /*line*/, + const logging::internal::CheckOpString& /*result*/) + : LogMessage::LogStream(message_buffer_, 2, 0) {} +NullStream& NullStream::stream() { return *this; } + +NullStreamFatal::~NullStreamFatal() { + // Cannot use g_logging_fail_func here as it may output the backtrace which + // would be inconsistent with NullStream behavior. + std::abort(); +} + +logging_fail_func_t InstallFailureFunction(logging_fail_func_t fail_func) { + return std::exchange(g_logging_fail_func, fail_func); +} + +void LogMessage::Fail() { g_logging_fail_func(); } + +// L >= log_mutex (callers must hold the log_mutex). +void LogMessage::SendToSink() EXCLUSIVE_LOCKS_REQUIRED(log_mutex) { + if (data_->sink_ != nullptr) { + RAW_DCHECK(data_->num_chars_to_log_ > 0 && + data_->message_text_[data_->num_chars_to_log_ - 1] == '\n', + ""); + data_->sink_->send( + data_->severity_, data_->fullname_, data_->basename_, data_->line_, + time_, data_->message_text_ + data_->num_prefix_chars_, + (data_->num_chars_to_log_ - data_->num_prefix_chars_ - 1)); + } +} + +// L >= log_mutex (callers must hold the log_mutex). +void LogMessage::SendToSinkAndLog() EXCLUSIVE_LOCKS_REQUIRED(log_mutex) { + SendToSink(); + SendToLog(); +} + +// L >= log_mutex (callers must hold the log_mutex). +void LogMessage::SaveOrSendToLog() EXCLUSIVE_LOCKS_REQUIRED(log_mutex) { + if (data_->outvec_ != nullptr) { + RAW_DCHECK(data_->num_chars_to_log_ > 0 && + data_->message_text_[data_->num_chars_to_log_ - 1] == '\n', + ""); + // Omit prefix of message and trailing newline when recording in outvec_. + const char* start = data_->message_text_ + data_->num_prefix_chars_; + size_t len = data_->num_chars_to_log_ - data_->num_prefix_chars_ - 1; + data_->outvec_->push_back(string(start, len)); + } else { + SendToLog(); + } +} + +void LogMessage::WriteToStringAndLog() EXCLUSIVE_LOCKS_REQUIRED(log_mutex) { + if (data_->message_ != nullptr) { + RAW_DCHECK(data_->num_chars_to_log_ > 0 && + data_->message_text_[data_->num_chars_to_log_ - 1] == '\n', + ""); + // Omit prefix of message and trailing newline when writing to message_. + const char* start = data_->message_text_ + data_->num_prefix_chars_; + size_t len = data_->num_chars_to_log_ - data_->num_prefix_chars_ - 1; + data_->message_->assign(start, len); + } + SendToLog(); +} + +// L >= log_mutex (callers must hold the log_mutex). +void LogMessage::SendToSyslogAndLog() { +#ifdef HAVE_SYSLOG_H + // Before any calls to syslog(), make a single call to openlog() + static bool openlog_already_called = false; + if (!openlog_already_called) { + openlog(glog_internal_namespace_::ProgramInvocationShortName(), + LOG_CONS | LOG_NDELAY | LOG_PID, LOG_USER); + openlog_already_called = true; + } + + // This array maps Google severity levels to syslog levels + const int SEVERITY_TO_LEVEL[] = {LOG_INFO, LOG_WARNING, LOG_ERR, LOG_EMERG}; + syslog(LOG_USER | SEVERITY_TO_LEVEL[static_cast(data_->severity_)], + "%.*s", static_cast(data_->num_chars_to_syslog_), + data_->message_text_ + data_->num_prefix_chars_); + SendToLog(); +#else + LOG(ERROR) << "No syslog support: message=" << data_->message_text_; +#endif +} + +base::Logger* base::GetLogger(LogSeverity severity) { + std::lock_guard l{log_mutex}; + return LogDestination::log_destination(severity)->GetLoggerImpl(); +} + +void base::SetLogger(LogSeverity severity, base::Logger* logger) { + std::lock_guard l{log_mutex}; + LogDestination::log_destination(severity)->SetLoggerImpl(logger); +} + +// L < log_mutex. Acquires and releases mutex_. +int64 LogMessage::num_messages(int severity) { + std::lock_guard l{log_mutex}; + return num_messages_[severity]; +} + +// Output the COUNTER value. This is only valid if ostream is a +// LogStream. +ostream& operator<<(ostream& os, const Counter_t&) { +#ifdef DISABLE_RTTI + LogMessage::LogStream* log = static_cast(&os); +#else + auto* log = dynamic_cast(&os); +#endif + CHECK(log && log == log->self()) + << "You must not use COUNTER with non-glog ostream"; + os << log->ctr(); + return os; +} + +ErrnoLogMessage::ErrnoLogMessage(const char* file, int line, + LogSeverity severity, int64 ctr, + void (LogMessage::*send_method)()) + : LogMessage(file, line, severity, ctr, send_method) {} + +ErrnoLogMessage::~ErrnoLogMessage() { + // Don't access errno directly because it may have been altered + // while streaming the message. + stream() << ": " << StrError(preserved_errno()) << " [" << preserved_errno() + << "]"; +} + +void FlushLogFiles(LogSeverity min_severity) { + LogDestination::FlushLogFiles(min_severity); +} + +void FlushLogFilesUnsafe(LogSeverity min_severity) { + LogDestination::FlushLogFilesUnsafe(min_severity); +} + +void SetLogDestination(LogSeverity severity, const char* base_filename) { + LogDestination::SetLogDestination(severity, base_filename); +} + +void SetLogSymlink(LogSeverity severity, const char* symlink_basename) { + LogDestination::SetLogSymlink(severity, 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, + 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(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 << "] "; + + // A call to `write' is enclosed in parenthneses to prevent possible macro + // expansion. On Windows, `write' could be a macro defined for portability. + (stream.write)(message, static_cast(message_len)); + return stream.str(); +} + +void AddLogSink(LogSink* destination) { + LogDestination::AddLogSink(destination); +} + +void RemoveLogSink(LogSink* destination) { + LogDestination::RemoveLogSink(destination); +} + +void SetLogFilenameExtension(const char* ext) { + LogDestination::SetLogFilenameExtension(ext); +} + +void SetStderrLogging(LogSeverity min_severity) { + LogDestination::SetStderrLogging(min_severity); +} + +void SetEmailLogging(LogSeverity min_severity, const char* addresses) { + LogDestination::SetEmailLogging(min_severity, addresses); +} + +void LogToStderr() { LogDestination::LogToStderr(); } + +namespace base { +namespace internal { + +bool GetExitOnDFatal(); +bool GetExitOnDFatal() { + std::lock_guard l{log_mutex}; + return exit_on_dfatal; +} + +// Determines whether we exit the program for a LOG(DFATAL) message in +// debug mode. It does this by skipping the call to Fail/FailQuietly. +// This is intended for testing only. +// +// This can have some effects on LOG(FATAL) as well. Failure messages +// are always allocated (rather than sharing a buffer), the crash +// reason is not recorded, the "gwq" status message is not updated, +// and the stack trace is not recorded. The LOG(FATAL) *will* still +// exit the program. Since this function is used only in testing, +// these differences are acceptable. +void SetExitOnDFatal(bool value); +void SetExitOnDFatal(bool value) { + std::lock_guard l{log_mutex}; + exit_on_dfatal = value; +} + +} // namespace internal +} // namespace base + +#ifndef GLOG_OS_EMSCRIPTEN +// Shell-escaping as we need to shell out ot /bin/mail. +static const char kDontNeedShellEscapeChars[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+-_.=/:,@"; + +static string ShellEscape(const string& src) { + string result; + if (!src.empty() && // empty string needs quotes + src.find_first_not_of(kDontNeedShellEscapeChars) == string::npos) { + // only contains chars that don't need quotes; it's fine + result.assign(src); + } else if (src.find_first_of('\'') == string::npos) { + // no single quotes; just wrap it in single quotes + result.assign("'"); + result.append(src); + result.append("'"); + } else { + // needs double quote escaping + result.assign("\""); + for (size_t i = 0; i < src.size(); ++i) { + switch (src[i]) { + case '\\': + case '$': + case '"': + case '`': + result.append("\\"); + } + result.append(src, i, 1); + } + result.append("\""); + } + return result; +} + +// Trim whitespace from both ends of the provided string. +static inline void trim(std::string& s) { + const auto toRemove = [](char ch) { return std::isspace(ch) == 0; }; + s.erase(s.begin(), std::find_if(s.begin(), s.end(), toRemove)); + s.erase(std::find_if(s.rbegin(), s.rend(), toRemove).base(), s.end()); +} +#endif + +// use_logging controls whether the logging functions LOG/VLOG are used +// to log errors. It should be set to false when the caller holds the +// log_mutex. +static bool SendEmailInternal(const char* dest, const char* subject, + const char* body, bool use_logging) { +#ifndef GLOG_OS_EMSCRIPTEN + if (dest && *dest) { + // Split the comma-separated list of email addresses, validate each one and + // build a sanitized new comma-separated string without whitespace. + std::istringstream ss(dest); + std::ostringstream sanitized_dests; + std::string s; + while (std::getline(ss, s, ',')) { + trim(s); + if (s.empty()) { + continue; + } + // We validate the provided email addresses using the same regular + // expression that HTML5 uses[1], except that we require the address to + // start with an alpha-numeric character. This is because we don't want to + // allow email addresses that start with a special character, such as a + // pipe or dash, which could be misunderstood as a command-line flag by + // certain versions of `mail` that are vulnerable to command injection.[2] + // [1] + // https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address + // [2] e.g. https://nvd.nist.gov/vuln/detail/CVE-2004-2771 + if (!std::regex_match( + s, + std::regex("^[a-zA-Z0-9]" + "[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]*@[a-zA-Z0-9]" + "(?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9]" + "(?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$"))) { + if (use_logging) { + VLOG(1) << "Invalid destination email address:" << s; + } else { + fprintf(stderr, "Invalid destination email address: %s\n", s.c_str()); + } + return false; + } + if (!sanitized_dests.str().empty()) { + sanitized_dests << ","; + } + sanitized_dests << s; + } + // Avoid dangling reference + const std::string& tmp = sanitized_dests.str(); + dest = tmp.c_str(); + + if (use_logging) { + VLOG(1) << "Trying to send TITLE:" << subject << " BODY:" << body + << " to " << dest; + } else { + fprintf(stderr, "Trying to send TITLE: %s BODY: %s to %s\n", subject, + body, dest); + } + + string logmailer; + + if (FLAGS_logmailer.empty()) { + // Don't need to shell escape the literal string + logmailer = "/bin/mail"; + } else { + logmailer = ShellEscape(FLAGS_logmailer); + } + + string cmd = + logmailer + " -s" + ShellEscape(subject) + " " + ShellEscape(dest); + if (use_logging) { + VLOG(4) << "Mailing command: " << cmd; + } + + FILE* pipe = popen(cmd.c_str(), "w"); + if (pipe != nullptr) { + // Add the body if we have one + if (body) { + fwrite(body, sizeof(char), strlen(body), pipe); + } + bool ok = pclose(pipe) != -1; + if (!ok) { + if (use_logging) { + LOG(ERROR) << "Problems sending mail to " << dest << ": " + << StrError(errno); + } else { + fprintf(stderr, "Problems sending mail to %s: %s\n", dest, + StrError(errno).c_str()); + } + } + return ok; + } else { + if (use_logging) { + LOG(ERROR) << "Unable to send mail to " << dest; + } else { + fprintf(stderr, "Unable to send mail to %s\n", dest); + } + } + } +#else + (void)dest; + (void)subject; + (void)body; + (void)use_logging; + LOG(WARNING) << "Email support not available; not sending message"; +#endif + return false; +} + +bool SendEmail(const char* dest, const char* subject, const char* body) { + return SendEmailInternal(dest, subject, body, true); +} + +static void GetTempDirectories(vector& 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/ + char tmp[MAX_PATH]; + 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 + const char* candidates[] = { + // Non-null only during unittest/regtest + getenv("TEST_TMPDIR"), + + // Explicitly-supplied temp dirs + getenv("TMPDIR"), + getenv("TMP"), + + // If all else fails + "/tmp", + }; + + for (auto d : candidates) { + if (!d) continue; // Empty env var + + // Make sure we don't surprise anyone who's expecting a '/' + string dstr = d; + if (dstr[dstr.size() - 1] != '/') { + dstr += "/"; + } + list.push_back(dstr); + + struct stat statbuf; + if (!stat(d, &statbuf) && S_ISDIR(statbuf.st_mode)) { + // We found a dir that exists - we're done. + return; + } + } +#endif +} + +static std::unique_ptr> logging_directories_list; + +const vector& GetLoggingDirectories() { + // Not strictly thread-safe but we're called early in InitGoogle(). + if (logging_directories_list == nullptr) { + logging_directories_list = std::make_unique>(); + + if (!FLAGS_log_dir.empty()) { + // Ensure the specified path ends with a directory delimiter. + if (std::find(std::begin(possible_dir_delim), + std::end(possible_dir_delim), + FLAGS_log_dir.back()) == std::end(possible_dir_delim)) { + logging_directories_list->push_back(FLAGS_log_dir + "/"); + } else { + logging_directories_list->push_back(FLAGS_log_dir); + } + } else { + GetTempDirectories(*logging_directories_list); +#ifdef GLOG_OS_WINDOWS + char tmp[MAX_PATH]; + if (GetWindowsDirectoryA(tmp, MAX_PATH)) + logging_directories_list->push_back(tmp); + logging_directories_list->push_back(".\\"); +#else + logging_directories_list->push_back("./"); +#endif + } + } + return *logging_directories_list; +} + +// Returns a set of existing temporary directories, which will be a +// subset of the directories returned by GetLoggingDirectories(). +// Thread-safe. +GLOG_NO_EXPORT +void GetExistingTempDirectories(vector& list) { + GetTempDirectories(list); + 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); + } else { + ++i_dir; + } + } +} + +void TruncateLogFile(const char* path, uint64 limit, uint64 keep) { +#if defined(HAVE_UNISTD_H) || defined(HAVE__CHSIZE_S) + struct stat statbuf; + const int kCopyBlockSize = 8 << 10; + char copybuf[kCopyBlockSize]; + off_t read_offset, write_offset; + // Don't follow symlinks unless they're our own fd symlinks in /proc + int flags = O_RDWR; + // TODO(hamaji): Support other environments. +# ifdef GLOG_OS_LINUX + const char* procfd_prefix = "/proc/self/fd/"; + if (strncmp(procfd_prefix, path, strlen(procfd_prefix))) flags |= O_NOFOLLOW; +# endif + + FileDescriptor fd{open(path, flags)}; + if (!fd) { + 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 + // all of base/...) with -D_FILE_OFFSET_BITS=64 but that's + // rather scary. + // Instead just truncate the file to something we can manage +# ifdef HAVE__CHSIZE_S + if (_chsize_s(fd.get(), 0) != 0) { +# else + if (truncate(path, 0) == -1) { +# endif + PLOG(ERROR) << "Unable to truncate " << path; + } else { + LOG(ERROR) << "Truncated " << path << " due to EFBIG error"; + } + } else { + PLOG(ERROR) << "Unable to open " << path; + } + return; + } + + if (fstat(fd.get(), &statbuf) == -1) { + PLOG(ERROR) << "Unable to fstat()"; + return; + } + + // 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(limit)) return; + if (statbuf.st_size <= static_cast(keep)) return; + + // This log file is too large - we need to truncate it + LOG(INFO) << "Truncating " << path << " to " << keep << " bytes"; + + // Copy the last "keep" bytes of the file to the beginning of the file + read_offset = statbuf.st_size - static_cast(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(bytesin), write_offset); + if (bytesout == -1) { + PLOG(ERROR) << "Unable to write to " << path; + break; + } else if (bytesout != bytesin) { + LOG(ERROR) << "Expected to write " << bytesin << ", wrote " << bytesout; + } + read_offset += bytesin; + write_offset += bytesout; + } + if (bytesin == -1) PLOG(ERROR) << "Unable to read from " << path; + + // Truncate the remainder of the file. If someone else writes to the + // 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) { +# else + if (ftruncate(fd.get(), write_offset) == -1) { +# endif + PLOG(ERROR) << "Unable to truncate " << path; + } + +#else + LOG(ERROR) << "No log truncation support."; +#endif +} + +void TruncateStdoutStderr() { +#ifdef HAVE_UNISTD_H + uint64 limit = MaxLogSize() << 20U; + uint64 keep = 1U << 20U; + TruncateLogFile("/proc/self/fd/1", limit, keep); + TruncateLogFile("/proc/self/fd/2", limit, keep); +#else + LOG(ERROR) << "No log truncation support."; +#endif +} + +namespace logging { +namespace internal { +// Helper functions for string comparisons. +#define DEFINE_CHECK_STROP_IMPL(name, func, expected) \ + std::unique_ptr 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)) \ + return nullptr; \ + else { \ + ostringstream ss; \ + if (!s1) s1 = ""; \ + if (!s2) s2 = ""; \ + ss << #name " failed: " << names << " (" << s1 << " vs. " << s2 << ")"; \ + return std::make_unique(ss.str()); \ + } \ + } +DEFINE_CHECK_STROP_IMPL(CHECK_STREQ, strcmp, true) +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 +// version that we want, but it is not clear whether it even works with +// all versions of glibc. +// So, instead, we provide this wrapper that automatically detects the +// version that is in use, and then implements POSIX semantics. +// N.B. In addition to what POSIX says, we also guarantee that "buf" will +// be set to an empty string, if this function failed. This means, in most +// cases, you do not need to check the error code and you can directly +// use the value of "buf". It will never have an undefined value. +// DEPRECATED: Use StrError(int) instead. +GLOG_NO_EXPORT +int posix_strerror_r(int err, char* buf, size_t len) { + // Sanity check input parameters + if (buf == nullptr || len <= 0) { + errno = EINVAL; + return -1; + } + + // Reset buf and errno, and try calling whatever version of strerror_r() + // is implemented by glibc + buf[0] = '\000'; + int old_errno = errno; + errno = 0; + char* rc = reinterpret_cast(strerror_r(err, buf, len)); + + // Both versions set errno on failure + if (errno) { + // Should already be there, but better safe than sorry + buf[0] = '\000'; + return -1; + } + errno = old_errno; + + // POSIX is vague about whether the string will be terminated, although + // is indirectly implies that typically ERANGE will be returned, instead + // of truncating the string. This is different from the GNU implementation. + // We play it safe by always terminating the string explicitly. + buf[len - 1] = '\000'; + + // If the function succeeded, we can use its exit code to determine the + // semantics implemented by glibc + if (!rc) { + return 0; + } else { + // GNU semantics detected + if (rc == buf) { + return 0; + } else { + buf[0] = '\000'; +#if defined(GLOG_OS_MACOSX) || defined(GLOG_OS_FREEBSD) || \ + defined(GLOG_OS_OPENBSD) + if (reinterpret_cast(rc) < sys_nerr) { + // This means an error on MacOSX or FreeBSD. + return -1; + } +#endif + strncat(buf, rc, len - 1); + return 0; + } + } +} + +// A thread-safe replacement for strerror(). Returns a string describing the +// given POSIX error code. +string StrError(int err) { + char buf[100]; + int rc = posix_strerror_r(err, buf, sizeof(buf)); + if ((rc < 0) || (buf[0] == '\000')) { + std::snprintf(buf, sizeof(buf), "Error number %d", err); + } + return buf; +} + +LogMessageFatal::LogMessageFatal(const char* file, int line) + : LogMessage(file, line, GLOG_FATAL) {} + +LogMessageFatal::LogMessageFatal(const char* file, int line, + const logging::internal::CheckOpString& result) + : LogMessage(file, line, result) {} + +LogMessageFatal::~LogMessageFatal() noexcept(false) { + Flush(); + LogMessage::Fail(); +} + +namespace logging { +namespace internal { + +CheckOpMessageBuilder::CheckOpMessageBuilder(const char* exprtext) + : stream_(new ostringstream) { + *stream_ << exprtext << " ("; +} + +CheckOpMessageBuilder::~CheckOpMessageBuilder() { delete stream_; } + +ostream* CheckOpMessageBuilder::ForVar2() { + *stream_ << " vs. "; + return stream_; +} + +std::unique_ptr CheckOpMessageBuilder::NewString() { + *stream_ << ")"; + return std::make_unique(stream_->str()); +} + +template <> +void MakeCheckOpValueString(std::ostream* os, const char& v) { + if (v >= 32 && v <= 126) { + (*os) << "'" << v << "'"; + } else { + (*os) << "char value " << static_cast(v); + } +} + +template <> +void MakeCheckOpValueString(std::ostream* os, const signed char& v) { + if (v >= 32 && v <= 126) { + (*os) << "'" << v << "'"; + } else { + (*os) << "signed char value " << static_cast(v); + } +} + +template <> +void MakeCheckOpValueString(std::ostream* os, const unsigned char& v) { + if (v >= 32 && v <= 126) { + (*os) << "'" << v << "'"; + } else { + (*os) << "unsigned char value " << static_cast(v); + } +} + +template <> +void MakeCheckOpValueString(std::ostream* os, const std::nullptr_t& /*v*/) { + (*os) << "nullptr"; +} + +} // namespace internal +} // namespace logging + +void InitGoogleLogging(const char* argv0) { InitGoogleLoggingUtilities(argv0); } + +#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__ +void InitGoogleLogging(const char* argv0, CustomPrefixCallback prefix_callback, + void* prefix_callback_data) { + if (prefix_callback != nullptr) { + g_prefix_formatter = std::make_unique( + prefix_callback, prefix_callback_data); + } else { + g_prefix_formatter = nullptr; + } + InitGoogleLogging(argv0); +} +#if defined(__GNUG__) +# pragma GCC diagnostic pop +#elif defined(_MSC_VER) +# pragma warning(pop) +#endif // __GNUG__ + +void InstallPrefixFormatter(PrefixFormatterCallback callback, void* data) { + if (callback != nullptr) { + g_prefix_formatter = std::make_unique(callback, data); + } else { + g_prefix_formatter = nullptr; + } +} + +void ShutdownGoogleLogging() { + ShutdownGoogleLoggingUtilities(); + LogDestination::DeleteLogDestinations(); + logging_directories_list = nullptr; + g_prefix_formatter = nullptr; +} + +void EnableLogCleaner(unsigned int overdue_days) { + log_cleaner.Enable(std::chrono::duration_cast( + std::chrono::duration>{ + overdue_days})); +} + +void EnableLogCleaner(const std::chrono::minutes& overdue) { + log_cleaner.Enable(overdue); +} + +void DisableLogCleaner() { log_cleaner.Disable(); } + +LogMessageTime::LogMessageTime() = default; + +namespace { + +template +struct void_impl { + using type = void; +}; + +template +using void_t = typename void_impl::type; + +template +struct has_member_tm_gmtoff : std::false_type {}; + +template +struct has_member_tm_gmtoff> + : std::true_type {}; + +template +auto Breakdown(const std::chrono::system_clock::time_point& now) + -> std::enable_if_t::value, + std::tuple> { + 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(×tamp, &tm_local); + localtime_r(×tamp, &tm_utc); + isdst = tm_utc.tm_isdst; + tm_utc = tm_local; + } else { + localtime_r(×tamp, &tm_local); + isdst = tm_local.tm_isdst; + gmtime_r(×tamp, &tm_utc); + } + + std::time_t gmt_sec = std::mktime(&tm_utc); + + // 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( + now - std::chrono::system_clock::from_time_t(gmt_sec) + + (isdst ? 1h : 0h)); + + return std::make_tuple(tm_local, timestamp, gmtoffset); +} + +template +auto Breakdown(const std::chrono::system_clock::time_point& now) + -> std::enable_if_t::value, + std::tuple> { + std::time_t timestamp = std::chrono::system_clock::to_time_t(now); + T tm; + + if (FLAGS_log_utc_time) { + gmtime_r(×tamp, &tm); + } else { + localtime_r(×tamp, &tm); + } + + const auto gmtoffset = std::chrono::duration_cast( + std::chrono::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( + now - std::chrono::system_clock::from_time_t(timestamp)); +} + +} // namespace google diff --git a/3rdparty/glog-0.7.0/src/logging_striplog_test.sh b/3rdparty/glog-0.7.0/src/logging_striplog_test.sh new file mode 100755 index 000000000..bb6d0e2b1 --- /dev/null +++ b/3rdparty/glog-0.7.0/src/logging_striplog_test.sh @@ -0,0 +1,80 @@ +#! /bin/sh +# +# Copyright (c) 2023, 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: Sergey Ioffe + +get_strings () { + if test -e "$1"; then + binary="$1" + elif test -e "$1.exe"; then + binary="$1.exe" + else + echo "We coundn't find $1 binary." + exit 1 + fi + + strings -n 10 $binary | sort | awk '/TESTMESSAGE/ {printf "%s ", $2}' +} + +# Die if "$1" != "$2", print $3 as death reason +check_eq () { + if [ "$1" != "$2" ]; then + echo "Check failed: '$1' == '$2' ${3:+ ($3)}" + exit 1 + fi +} + +die () { + echo $1 + exit 1 +} + +# Check that the string literals are appropriately stripped. This will +# not be the case in debug mode. + +mode=`GLOG_check_mode=1 ./striplog0_unittest 2> /dev/null` +echo $mode +if [ "$mode" = "opt" ]; +then + echo "In OPT mode" + check_eq "`get_strings striplog0_unittest`" "COND ERROR FATAL INFO WARNING " + check_eq "`get_strings striplog2_unittest`" "COND ERROR FATAL " + check_eq "`get_strings striplog10_unittest`" "" +else + echo "In DBG mode; not checking strings" +fi + +# Check that LOG(FATAL) aborts even for large STRIP_LOG + +./striplog2_unittest 2>/dev/null && die "Did not abort for STRIP_LOG=2" +./striplog10_unittest 2>/dev/null && die "Did not abort for STRIP_LOG=10" + +echo "PASS" diff --git a/3rdparty/glog-0.7.0/src/logging_unittest.cc b/3rdparty/glog-0.7.0/src/logging_unittest.cc new file mode 100644 index 000000000..321f38fa3 --- /dev/null +++ b/3rdparty/glog-0.7.0/src/logging_unittest.cc @@ -0,0 +1,1588 @@ +// 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: Ray Sidney + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#ifdef HAVE_GLOB_H +# include +#endif +#include +#ifdef HAVE_UNISTD_H +# include +#endif +#ifdef HAVE_SYS_WAIT_H +# include +#endif + +#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 +using namespace GFLAGS_NAMESPACE; +#endif + +#ifdef HAVE_LIB_GMOCK +# include + +# include "mock-log.h" +// Introduce several symbols from gmock. +using google::glog_testing::ScopedMockLog; +using testing::_; +using testing::AllOf; +using testing::AnyNumber; +using testing::HasSubstr; +using testing::InitGoogleMock; +using testing::StrictMock; +using testing::StrNe; +#endif + +using namespace std; +using namespace google; + +// Some non-advertised functions that we want to test or use. +namespace google { +namespace base { +namespace internal { +bool GetExitOnDFatal(); +void SetExitOnDFatal(bool value); +} // namespace internal +} // namespace base +} // namespace google + +static void TestLogging(bool check_counts); +static void TestRawLogging(); +static void LogWithLevels(int v, int severity, bool err, bool alsoerr); +static void TestLoggingLevels(); +static void TestVLogModule(); +static void TestLogString(); +static void TestLogSink(); +static void TestLogToString(); +static void TestLogSinkWaitTillSent(); +static void TestCHECK(); +static void TestDCHECK(); +static void TestSTREQ(); +static void TestBasename(); +static void TestBasenameAppendWhenNoTimestamp(); +static void TestTwoProcessesWrite(); +static void TestSymlink(); +static void TestExtension(); +static void TestWrapper(); +static void TestErrno(); +static void TestTruncate(); +static void TestCustomLoggerDeletionOnShutdown(); +static void TestLogPeriodically(); + +static int x = -1; +static void BM_Check1(int n) { + while (n-- > 0) { + CHECK_GE(n, x); + CHECK_GE(n, x); + CHECK_GE(n, x); + CHECK_GE(n, x); + CHECK_GE(n, x); + CHECK_GE(n, x); + CHECK_GE(n, x); + CHECK_GE(n, x); + } +} +BENCHMARK(BM_Check1) + +static void CheckFailure(int a, int b, const char* file, int line, + const char* msg); +static void BM_Check3(int n) { + while (n-- > 0) { + if (n < x) CheckFailure(n, x, __FILE__, __LINE__, "n < x"); + if (n < x) CheckFailure(n, x, __FILE__, __LINE__, "n < x"); + if (n < x) CheckFailure(n, x, __FILE__, __LINE__, "n < x"); + if (n < x) CheckFailure(n, x, __FILE__, __LINE__, "n < x"); + if (n < x) CheckFailure(n, x, __FILE__, __LINE__, "n < x"); + if (n < x) CheckFailure(n, x, __FILE__, __LINE__, "n < x"); + if (n < x) CheckFailure(n, x, __FILE__, __LINE__, "n < x"); + if (n < x) CheckFailure(n, x, __FILE__, __LINE__, "n < x"); + } +} +BENCHMARK(BM_Check3) + +static void BM_Check2(int n) { + if (n == 17) { + x = 5; + } + while (n-- > 0) { + CHECK(n >= x); + CHECK(n >= x); + CHECK(n >= x); + CHECK(n >= x); + CHECK(n >= x); + CHECK(n >= x); + CHECK(n >= x); + CHECK(n >= x); + } +} +BENCHMARK(BM_Check2) + +static void CheckFailure(int, int, const char* /* file */, int /* line */, + const char* /* msg */) {} + +static void BM_logspeed(int n) { + while (n-- > 0) { + LOG(INFO) << "test message"; + } +} +BENCHMARK(BM_logspeed) + +static void BM_vlog(int n) { + while (n-- > 0) { + VLOG(1) << "test message"; + } +} +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) { + // Assert that `data` contains the expected contents before producing the + // prefix (otherwise causing the tests to fail): + if (data == nullptr || *static_cast(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() << "]"; +} + +} // namespace + +int main(int argc, char** argv) { + FLAGS_colorlogtostderr = false; + FLAGS_timestamp_in_logfile_name = true; + + // Make sure stderr is not buffered as stderr seems to be buffered + // on recent windows. + setbuf(stderr, nullptr); + + // Test some basics before InitGoogleLogging: + CaptureTestStderr(); + LogWithLevels(FLAGS_v, FLAGS_stderrthreshold, FLAGS_logtostderr, + FLAGS_alsologtostderr); + LogWithLevels(0, 0, false, false); // simulate "before global c-tors" + const string early_stderr = GetCapturedTestStderr(); + + EXPECT_FALSE(IsGoogleLoggingInitialized()); + + // 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); + + EXPECT_TRUE(IsGoogleLoggingInitialized()); + + RunSpecifiedBenchmarks(); + + FLAGS_logtostderr = true; + + InitGoogleTest(&argc, argv); +#ifdef HAVE_LIB_GMOCK + InitGoogleMock(&argc, argv); +#endif + +#ifdef GLOG_USE_GFLAGS + ParseCommandLineFlags(&argc, &argv, true); +#endif + + // so that death tests run before we use threads + CHECK_EQ(RUN_ALL_TESTS(), 0); + + CaptureTestStderr(); + + // re-emit early_stderr + LogMessage("dummy", LogMessage::kNoLogPrefix, GLOG_INFO).stream() + << early_stderr; + + TestLogging(true); + TestRawLogging(); + TestLoggingLevels(); + TestVLogModule(); + TestLogString(); + TestLogSink(); + TestLogToString(); + TestLogSinkWaitTillSent(); + TestCHECK(); + TestDCHECK(); + TestSTREQ(); + + // TODO: The golden test portion of this test is very flakey. + EXPECT_TRUE( + MungeAndDiffTestStderr(FLAGS_test_srcdir + "/src/logging_unittest.err")); + + FLAGS_logtostderr = false; + + FLAGS_logtostdout = true; + FLAGS_stderrthreshold = NUM_SEVERITIES; + CaptureTestStdout(); + TestRawLogging(); + TestLoggingLevels(); + TestLogString(); + TestLogSink(); + TestLogToString(); + TestLogSinkWaitTillSent(); + TestCHECK(); + TestDCHECK(); + TestSTREQ(); + EXPECT_TRUE( + MungeAndDiffTestStdout(FLAGS_test_srcdir + "/src/logging_unittest.out")); + FLAGS_logtostdout = false; + + TestBasename(); + TestBasenameAppendWhenNoTimestamp(); + TestTwoProcessesWrite(); + TestSymlink(); + TestExtension(); + TestWrapper(); + TestErrno(); + TestTruncate(); + TestCustomLoggerDeletionOnShutdown(); + TestLogPeriodically(); + + fprintf(stdout, "PASS\n"); + return 0; +} + +void TestLogging(bool check_counts) { + int64 base_num_infos = LogMessage::num_messages(GLOG_INFO); + int64 base_num_warning = LogMessage::num_messages(GLOG_WARNING); + int64 base_num_errors = LogMessage::num_messages(GLOG_ERROR); + + LOG(INFO) << string("foo ") << "bar " << 10 << ' ' << 3.4; + for (int i = 0; i < 10; ++i) { + int old_errno = errno; + errno = i; + PLOG_EVERY_N(ERROR, 2) << "Plog every 2, iteration " << COUNTER; + errno = old_errno; + + LOG_EVERY_N(ERROR, 3) << "Log every 3, iteration " << COUNTER << endl; + LOG_EVERY_N(ERROR, 4) << "Log every 4, iteration " << COUNTER << endl; + + LOG_IF_EVERY_N(WARNING, true, 5) << "Log if every 5, iteration " << COUNTER; + LOG_IF_EVERY_N(WARNING, false, 3) + << "Log if every 3, iteration " << COUNTER; + LOG_IF_EVERY_N(INFO, true, 1) << "Log if every 1, iteration " << COUNTER; + LOG_IF_EVERY_N(ERROR, (i < 3), 2) + << "Log if less than 3 every 2, iteration " << COUNTER; + } + LOG_IF(WARNING, true) << "log_if this"; + LOG_IF(WARNING, false) << "don't log_if this"; + + char s[] = "array"; + LOG(INFO) << s; + const char const_s[] = "const array"; + LOG(INFO) << const_s; + int j = 1000; + LOG(ERROR) << string("foo") << ' ' << j << ' ' << setw(10) << j << " " + << setw(1) << hex << j; + LOG(INFO) << "foo " << std::setw(10) << 1.0; + + { + google::LogMessage outer(__FILE__, __LINE__, GLOG_ERROR); + outer.stream() << "outer"; + + LOG(ERROR) << "inner"; + } + + LogMessage("foo", LogMessage::kNoLogPrefix, GLOG_INFO).stream() + << "no prefix"; + + if (check_counts) { + CHECK_EQ(base_num_infos + 15, LogMessage::num_messages(GLOG_INFO)); + CHECK_EQ(base_num_warning + 3, LogMessage::num_messages(GLOG_WARNING)); + CHECK_EQ(base_num_errors + 17, LogMessage::num_messages(GLOG_ERROR)); + } +} + +static void NoAllocNewHook() { LOG(FATAL) << "unexpected new"; } + +struct NewHook { + NewHook() { g_new_hook = &NoAllocNewHook; } + ~NewHook() { g_new_hook = nullptr; } +}; + +namespace { +int* allocInt() { return new int; } +} // namespace + +TEST(DeathNoAllocNewHook, logging) { + // tests that NewHook used below works + NewHook new_hook; + // Avoid unused warnings under MinGW + // + // NOTE MSVC produces warning C4551 here if we do not take the address of the + // function explicitly. + (void)&allocInt; + ASSERT_DEATH({ allocInt(); }, "unexpected new"); +} + +void TestRawLogging() { + auto* foo = new string("foo "); + string huge_str(50000, 'a'); + + FlagSaver saver; + + // Check that RAW logging does not use mallocs. + NewHook new_hook; + + RAW_LOG(INFO, "%s%s%d%c%f", foo->c_str(), "bar ", 10, ' ', 3.4); + char s[] = "array"; + RAW_LOG(WARNING, "%s", s); + const char const_s[] = "const array"; + RAW_LOG(INFO, "%s", const_s); + void* p = reinterpret_cast(PTR_TEST_VALUE); + RAW_LOG(INFO, "ptr %p", p); + p = nullptr; + RAW_LOG(INFO, "ptr %p", p); + int j = 1000; + RAW_LOG(ERROR, "%s%d%c%010d%s%1x", foo->c_str(), j, ' ', j, " ", j); + RAW_VLOG(0, "foo %d", j); + +#if defined(NDEBUG) + RAW_LOG(INFO, "foo %d", j); // so that have same stderr to compare +#else + RAW_DLOG(INFO, "foo %d", j); // test RAW_DLOG in debug mode +#endif + + // test how long messages are chopped: + RAW_LOG(WARNING, "Huge string: %s", huge_str.c_str()); + RAW_VLOG(0, "Huge string: %s", huge_str.c_str()); + + FLAGS_v = 0; + RAW_LOG(INFO, "log"); + RAW_VLOG(0, "vlog 0 on"); + RAW_VLOG(1, "vlog 1 off"); + RAW_VLOG(2, "vlog 2 off"); + RAW_VLOG(3, "vlog 3 off"); + FLAGS_v = 2; + RAW_LOG(INFO, "log"); + RAW_VLOG(1, "vlog 1 on"); + RAW_VLOG(2, "vlog 2 on"); + RAW_VLOG(3, "vlog 3 off"); + +#if defined(NDEBUG) + RAW_DCHECK(1 == 2, " RAW_DCHECK's shouldn't be compiled in normal mode"); +#endif + + RAW_CHECK(1 == 1, "should be ok"); + RAW_DCHECK(true, "should be ok"); + + delete foo; +} + +void LogWithLevels(int v, int severity, bool err, bool alsoerr) { + RAW_LOG(INFO, + "Test: v=%d stderrthreshold=%d logtostderr=%d alsologtostderr=%d", v, + severity, err, alsoerr); + + FlagSaver saver; + + FLAGS_v = v; + FLAGS_stderrthreshold = severity; + FLAGS_logtostderr = err; + FLAGS_alsologtostderr = alsoerr; + + RAW_VLOG(-1, "vlog -1"); + RAW_VLOG(0, "vlog 0"); + RAW_VLOG(1, "vlog 1"); + RAW_LOG(INFO, "log info"); + RAW_LOG(WARNING, "log warning"); + RAW_LOG(ERROR, "log error"); + + VLOG(-1) << "vlog -1"; + VLOG(0) << "vlog 0"; + VLOG(1) << "vlog 1"; + LOG(INFO) << "log info"; + LOG(WARNING) << "log warning"; + LOG(ERROR) << "log error"; + + VLOG_IF(-1, true) << "vlog_if -1"; + VLOG_IF(-1, false) << "don't vlog_if -1"; + VLOG_IF(0, true) << "vlog_if 0"; + VLOG_IF(0, false) << "don't vlog_if 0"; + VLOG_IF(1, true) << "vlog_if 1"; + VLOG_IF(1, false) << "don't vlog_if 1"; + LOG_IF(INFO, true) << "log_if info"; + LOG_IF(INFO, false) << "don't log_if info"; + LOG_IF(WARNING, true) << "log_if warning"; + LOG_IF(WARNING, false) << "don't log_if warning"; + LOG_IF(ERROR, true) << "log_if error"; + LOG_IF(ERROR, false) << "don't log_if error"; + + int c; + c = 1; + VLOG_IF(100, c -= 2) << "vlog_if 100 expr"; + EXPECT_EQ(c, -1); + c = 1; + VLOG_IF(0, c -= 2) << "vlog_if 0 expr"; + EXPECT_EQ(c, -1); + c = 1; + LOG_IF(INFO, c -= 2) << "log_if info expr"; + EXPECT_EQ(c, -1); + c = 1; + LOG_IF(ERROR, c -= 2) << "log_if error expr"; + EXPECT_EQ(c, -1); + c = 2; + VLOG_IF(0, c -= 2) << "don't vlog_if 0 expr"; + EXPECT_EQ(c, 0); + c = 2; + LOG_IF(ERROR, c -= 2) << "don't log_if error expr"; + EXPECT_EQ(c, 0); + + c = 3; + LOG_IF_EVERY_N(INFO, c -= 4, 1) << "log_if info every 1 expr"; + EXPECT_EQ(c, -1); + c = 3; + LOG_IF_EVERY_N(ERROR, c -= 4, 1) << "log_if error every 1 expr"; + EXPECT_EQ(c, -1); + c = 4; + LOG_IF_EVERY_N(ERROR, c -= 4, 3) << "don't log_if info every 3 expr"; + EXPECT_EQ(c, 0); + c = 4; + LOG_IF_EVERY_N(ERROR, c -= 4, 3) << "don't log_if error every 3 expr"; + EXPECT_EQ(c, 0); + c = 5; + VLOG_IF_EVERY_N(0, c -= 4, 1) << "vlog_if 0 every 1 expr"; + EXPECT_EQ(c, 1); + c = 5; + VLOG_IF_EVERY_N(100, c -= 4, 3) << "vlog_if 100 every 3 expr"; + EXPECT_EQ(c, 1); + c = 6; + VLOG_IF_EVERY_N(0, c -= 6, 1) << "don't vlog_if 0 every 1 expr"; + EXPECT_EQ(c, 0); + c = 6; + VLOG_IF_EVERY_N(100, c -= 6, 3) << "don't vlog_if 100 every 1 expr"; + EXPECT_EQ(c, 0); +} + +void TestLoggingLevels() { + LogWithLevels(0, GLOG_INFO, false, false); + LogWithLevels(1, GLOG_INFO, false, false); + LogWithLevels(-1, GLOG_INFO, false, false); + LogWithLevels(0, GLOG_WARNING, false, false); + LogWithLevels(0, GLOG_ERROR, false, false); + LogWithLevels(0, GLOG_FATAL, false, false); + LogWithLevels(0, GLOG_FATAL, true, false); + LogWithLevels(0, GLOG_FATAL, false, true); + LogWithLevels(1, GLOG_WARNING, false, false); + LogWithLevels(1, GLOG_FATAL, false, true); +} + +int TestVlogHelper() { + if (VLOG_IS_ON(1)) { + return 1; + } + return 0; +} + +void TestVLogModule() { + int c = TestVlogHelper(); + EXPECT_EQ(0, c); + +#if defined(__GNUC__) + EXPECT_EQ(0, SetVLOGLevel("logging_unittest", 1)); + c = TestVlogHelper(); + EXPECT_EQ(1, c); +#endif +} + +TEST(DeathRawCHECK, logging) { + ASSERT_DEATH(RAW_CHECK(false, "failure 1"), + "RAW: Check false failed: failure 1"); + ASSERT_DEBUG_DEATH(RAW_DCHECK(1 == 2, "failure 2"), + "RAW: Check 1 == 2 failed: failure 2"); +} + +void TestLogString() { + vector errors; + vector* no_errors = nullptr; + + LOG_STRING(INFO, &errors) << "LOG_STRING: " + << "collected info"; + LOG_STRING(WARNING, &errors) << "LOG_STRING: " + << "collected warning"; + LOG_STRING(ERROR, &errors) << "LOG_STRING: " + << "collected error"; + + LOG_STRING(INFO, no_errors) << "LOG_STRING: " + << "reported info"; + LOG_STRING(WARNING, no_errors) << "LOG_STRING: " + << "reported warning"; + LOG_STRING(ERROR, nullptr) << "LOG_STRING: " + << "reported error"; + + for (auto& error : errors) { + LOG(INFO) << "Captured by LOG_STRING: " << error; + } +} + +void TestLogToString() { + string error; + string* no_error = nullptr; + + LOG_TO_STRING(INFO, &error) << "LOG_TO_STRING: " + << "collected info"; + LOG(INFO) << "Captured by LOG_TO_STRING: " << error; + LOG_TO_STRING(WARNING, &error) << "LOG_TO_STRING: " + << "collected warning"; + LOG(INFO) << "Captured by LOG_TO_STRING: " << error; + LOG_TO_STRING(ERROR, &error) << "LOG_TO_STRING: " + << "collected error"; + LOG(INFO) << "Captured by LOG_TO_STRING: " << error; + + LOG_TO_STRING(INFO, no_error) << "LOG_TO_STRING: " + << "reported info"; + LOG_TO_STRING(WARNING, no_error) << "LOG_TO_STRING: " + << "reported warning"; + LOG_TO_STRING(ERROR, nullptr) << "LOG_TO_STRING: " + << "reported error"; +} + +class TestLogSinkImpl : public LogSink { + public: + vector errors; + void send(LogSeverity severity, const char* /* full_filename */, + const char* base_filename, int line, + const LogMessageTime& logmsgtime, const char* message, + size_t message_len) override { + errors.push_back(ToString(severity, base_filename, line, logmsgtime, + message, message_len)); + } +}; + +void TestLogSink() { + TestLogSinkImpl sink; + LogSink* no_sink = nullptr; + + LOG_TO_SINK(&sink, INFO) << "LOG_TO_SINK: " + << "collected info"; + LOG_TO_SINK(&sink, WARNING) << "LOG_TO_SINK: " + << "collected warning"; + LOG_TO_SINK(&sink, ERROR) << "LOG_TO_SINK: " + << "collected error"; + + LOG_TO_SINK(no_sink, INFO) << "LOG_TO_SINK: " + << "reported info"; + LOG_TO_SINK(no_sink, WARNING) << "LOG_TO_SINK: " + << "reported warning"; + LOG_TO_SINK(nullptr, ERROR) << "LOG_TO_SINK: " + << "reported error"; + + LOG_TO_SINK_BUT_NOT_TO_LOGFILE(&sink, INFO) + << "LOG_TO_SINK_BUT_NOT_TO_LOGFILE: " + << "collected info"; + LOG_TO_SINK_BUT_NOT_TO_LOGFILE(&sink, WARNING) + << "LOG_TO_SINK_BUT_NOT_TO_LOGFILE: " + << "collected warning"; + LOG_TO_SINK_BUT_NOT_TO_LOGFILE(&sink, ERROR) + << "LOG_TO_SINK_BUT_NOT_TO_LOGFILE: " + << "collected error"; + + LOG_TO_SINK_BUT_NOT_TO_LOGFILE(no_sink, INFO) + << "LOG_TO_SINK_BUT_NOT_TO_LOGFILE: " + << "thrashed info"; + LOG_TO_SINK_BUT_NOT_TO_LOGFILE(no_sink, WARNING) + << "LOG_TO_SINK_BUT_NOT_TO_LOGFILE: " + << "thrashed warning"; + LOG_TO_SINK_BUT_NOT_TO_LOGFILE(nullptr, ERROR) + << "LOG_TO_SINK_BUT_NOT_TO_LOGFILE: " + << "thrashed error"; + + LOG(INFO) << "Captured by LOG_TO_SINK:"; + for (auto& error : sink.errors) { + LogMessage("foo", LogMessage::kNoLogPrefix, GLOG_INFO).stream() << error; + } +} + +// For testing using CHECK*() on anonymous enums. +enum { CASE_A, CASE_B }; + +void TestCHECK() { + // Tests using CHECK*() on int values. + CHECK(1 == 1); + CHECK_EQ(1, 1); + CHECK_NE(1, 2); + CHECK_GE(1, 1); + CHECK_GE(2, 1); + CHECK_LE(1, 1); + CHECK_LE(1, 2); + CHECK_GT(2, 1); + CHECK_LT(1, 2); + + // Tests using CHECK*() on anonymous enums. + // Apple's GCC doesn't like this. +#if !defined(GLOG_OS_MACOSX) + CHECK_EQ(CASE_A, CASE_A); + CHECK_NE(CASE_A, CASE_B); + CHECK_GE(CASE_A, CASE_A); + CHECK_GE(CASE_B, CASE_A); + CHECK_LE(CASE_A, CASE_A); + CHECK_LE(CASE_A, CASE_B); + CHECK_GT(CASE_B, CASE_A); + CHECK_LT(CASE_A, CASE_B); +#endif +} + +void TestDCHECK() { +#if defined(NDEBUG) + DCHECK(1 == 2) << " DCHECK's shouldn't be compiled in normal mode"; +#endif + DCHECK(1 == 1); + DCHECK_EQ(1, 1); + DCHECK_NE(1, 2); + DCHECK_GE(1, 1); + DCHECK_GE(2, 1); + DCHECK_LE(1, 1); + DCHECK_LE(1, 2); + DCHECK_GT(2, 1); + DCHECK_LT(1, 2); + + auto* orig_ptr = new int64; + int64* ptr = DCHECK_NOTNULL(orig_ptr); + CHECK_EQ(ptr, orig_ptr); + delete orig_ptr; +} + +void TestSTREQ() { + CHECK_STREQ("this", "this"); + CHECK_STREQ(nullptr, nullptr); + CHECK_STRCASEEQ("this", "tHiS"); + CHECK_STRCASEEQ(nullptr, nullptr); + CHECK_STRNE("this", "tHiS"); + CHECK_STRNE("this", nullptr); + CHECK_STRCASENE("this", "that"); + CHECK_STRCASENE(nullptr, "that"); + CHECK_STREQ((string("a") + "b").c_str(), "ab"); + CHECK_STREQ(string("test").c_str(), (string("te") + string("st")).c_str()); +} + +TEST(DeathSTREQ, logging) { + ASSERT_DEATH(CHECK_STREQ(nullptr, "this"), ""); + ASSERT_DEATH(CHECK_STREQ("this", "siht"), ""); + ASSERT_DEATH(CHECK_STRCASEEQ(nullptr, "siht"), ""); + ASSERT_DEATH(CHECK_STRCASEEQ("this", "siht"), ""); + ASSERT_DEATH(CHECK_STRNE(nullptr, nullptr), ""); + ASSERT_DEATH(CHECK_STRNE("this", "this"), ""); + ASSERT_DEATH(CHECK_STREQ((string("a") + "b").c_str(), "abc"), ""); +} + +TEST(CheckNOTNULL, Simple) { + int64 t; + void* ptr = static_cast(&t); + void* ref = CHECK_NOTNULL(ptr); + EXPECT_EQ(ptr, ref); + CHECK_NOTNULL(reinterpret_cast(ptr)); + CHECK_NOTNULL(reinterpret_cast(ptr)); + CHECK_NOTNULL(reinterpret_cast(ptr)); + CHECK_NOTNULL(reinterpret_cast(ptr)); +} + +TEST(DeathCheckNN, Simple) { + ASSERT_DEATH(CHECK_NOTNULL(static_cast(nullptr)), ""); +} + +// Get list of file names that match pattern +static void GetFiles(const string& pattern, vector* files) { + files->clear(); +#if defined(HAVE_GLOB_H) + glob_t g; + const int r = glob(pattern.c_str(), 0, nullptr, &g); + CHECK((r == 0) || (r == GLOB_NOMATCH)) << ": error matching " << pattern; + for (size_t i = 0; i < g.gl_pathc; i++) { + files->push_back(string(g.gl_pathv[i])); + } + globfree(&g); +#elif defined(GLOG_OS_WINDOWS) + WIN32_FIND_DATAA data; + HANDLE handle = FindFirstFileA(pattern.c_str(), &data); + size_t index = pattern.rfind('\\'); + if (index == string::npos) { + LOG(FATAL) << "No directory separator."; + } + const string dirname = pattern.substr(0, index + 1); + if (handle == INVALID_HANDLE_VALUE) { + // Finding no files is OK. + return; + } + do { + files->push_back(dirname + data.cFileName); + } while (FindNextFileA(handle, &data)); + if (!FindClose(handle)) { + LOG_SYSRESULT(GetLastError()); + } +#else +# error There is no way to do glob. +#endif +} + +// Delete files patching pattern +static void DeleteFiles(const string& pattern) { + vector files; + GetFiles(pattern, &files); + for (auto& file : files) { + CHECK(unlink(file.c_str()) == 0) << ": " << strerror(errno); + } +} + +// check string is in file (or is *NOT*, depending on optional checkInFileOrNot) +static void CheckFile(const string& name, const string& expected_string, + const bool checkInFileOrNot = true) { + vector files; + GetFiles(name + "*", &files); + CHECK_EQ(files.size(), 1UL); + + std::unique_ptr 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) { + char* first = strstr(buf, expected_string.c_str()); + // if first == nullptr, not found. + // Terser than if (checkInFileOrNot && first != nullptr || !check... + if (checkInFileOrNot != (first == nullptr)) { + return; + } + } + LOG(FATAL) << "Did " << (checkInFileOrNot ? "not " : "") << "find " + << expected_string << " in " << files[0]; +} + +static void TestBasename() { + fprintf(stderr, "==== Test setting log file basename\n"); + const string dest = FLAGS_test_tmpdir + "/logging_test_basename"; + DeleteFiles(dest + "*"); + + SetLogDestination(GLOG_INFO, dest.c_str()); + LOG(INFO) << "message to new base"; + FlushLogFiles(GLOG_INFO); + + CheckFile(dest, "message to new base"); + + // Release file handle for the destination file to unlock the file in Windows. + LogToStderr(); + DeleteFiles(dest + "*"); +} + +static void TestBasenameAppendWhenNoTimestamp() { + fprintf(stderr, + "==== Test setting log file basename without timestamp and appending " + "properly\n"); + const string dest = + FLAGS_test_tmpdir + "/logging_test_basename_append_when_no_timestamp"; + DeleteFiles(dest + "*"); + + ofstream out(dest.c_str()); + out << "test preexisting content" << endl; + out.close(); + + CheckFile(dest, "test preexisting content"); + + FLAGS_timestamp_in_logfile_name = false; + SetLogDestination(GLOG_INFO, dest.c_str()); + LOG(INFO) << "message to new base, appending to preexisting file"; + FlushLogFiles(GLOG_INFO); + FLAGS_timestamp_in_logfile_name = true; + + // if the logging overwrites the file instead of appending it will fail. + CheckFile(dest, "test preexisting content"); + CheckFile(dest, "message to new base, appending to preexisting file"); + + // Release file handle for the destination file to unlock the file in Windows. + LogToStderr(); + DeleteFiles(dest + "*"); +} + +static void TestTwoProcessesWrite() { +// test only implemented for platforms with fork & wait; the actual +// implementation relies on flock +#if defined(HAVE_SYS_WAIT_H) && defined(HAVE_UNISTD_H) && defined(HAVE_FCNTL) + fprintf(stderr, + "==== Test setting log file basename and two processes writing - " + "second should fail\n"); + const string dest = + FLAGS_test_tmpdir + "/logging_test_basename_two_processes_writing"; + DeleteFiles(dest + "*"); + + // make both processes write into the same file (easier test) + FLAGS_timestamp_in_logfile_name = false; + SetLogDestination(GLOG_INFO, dest.c_str()); + LOG(INFO) << "message to new base, parent"; + FlushLogFiles(GLOG_INFO); + + pid_t pid = fork(); + CHECK_ERR(pid); + if (pid == 0) { + LOG(INFO) << "message to new base, child - should only appear on STDERR " + "not on the file"; + ShutdownGoogleLogging(); // for children proc + exit(EXIT_SUCCESS); + } else if (pid > 0) { + wait(nullptr); + } + FLAGS_timestamp_in_logfile_name = true; + + CheckFile(dest, "message to new base, parent"); + CheckFile(dest, + "message to new base, child - should only appear on STDERR not on " + "the file", + false); + + // Release + LogToStderr(); + DeleteFiles(dest + "*"); +#endif +} + +static void TestSymlink() { +#ifndef GLOG_OS_WINDOWS + fprintf(stderr, "==== Test setting log file symlink\n"); + string dest = FLAGS_test_tmpdir + "/logging_test_symlink"; + string sym = FLAGS_test_tmpdir + "/symlinkbase"; + DeleteFiles(dest + "*"); + DeleteFiles(sym + "*"); + + SetLogSymlink(GLOG_INFO, "symlinkbase"); + SetLogDestination(GLOG_INFO, dest.c_str()); + LOG(INFO) << "message to new symlink"; + FlushLogFiles(GLOG_INFO); + CheckFile(sym, "message to new symlink"); + + DeleteFiles(dest + "*"); + DeleteFiles(sym + "*"); +#endif +} + +static void TestExtension() { + fprintf(stderr, "==== Test setting log file extension\n"); + string dest = FLAGS_test_tmpdir + "/logging_test_extension"; + DeleteFiles(dest + "*"); + + SetLogDestination(GLOG_INFO, dest.c_str()); + SetLogFilenameExtension("specialextension"); + LOG(INFO) << "message to new extension"; + FlushLogFiles(GLOG_INFO); + CheckFile(dest, "message to new extension"); + + // Check that file name ends with extension + vector filenames; + GetFiles(dest + "*", &filenames); + CHECK_EQ(filenames.size(), 1UL); + CHECK(strstr(filenames[0].c_str(), "specialextension") != nullptr); + + // Release file handle for the destination file to unlock the file in Windows. + LogToStderr(); + DeleteFiles(dest + "*"); +} + +struct MyLogger : public base::Logger { + string data; + + explicit MyLogger(bool* set_on_destruction) + : set_on_destruction_(set_on_destruction) {} + + ~MyLogger() override { *set_on_destruction_ = true; } + + void Write(bool /* should_flush */, + const std::chrono::system_clock::time_point& /* timestamp */, + const char* message, size_t length) override { + data.append(message, length); + } + + void Flush() override {} + + uint32 LogSize() override { return static_cast(data.length()); } + + private: + bool* set_on_destruction_; +}; + +static void TestWrapper() { + fprintf(stderr, "==== Test log wrapper\n"); + + bool custom_logger_deleted = false; + auto* my_logger = new MyLogger(&custom_logger_deleted); + base::Logger* old_logger = base::GetLogger(GLOG_INFO); + base::SetLogger(GLOG_INFO, my_logger); + LOG(INFO) << "Send to wrapped logger"; + CHECK(strstr(my_logger->data.c_str(), "Send to wrapped logger") != nullptr); + FlushLogFiles(GLOG_INFO); + + EXPECT_FALSE(custom_logger_deleted); + base::SetLogger(GLOG_INFO, old_logger); + EXPECT_TRUE(custom_logger_deleted); +} + +static void TestErrno() { + fprintf(stderr, "==== Test errno preservation\n"); + + errno = ENOENT; + TestLogging(false); + CHECK_EQ(errno, ENOENT); +} + +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); + + const char *discardstr = "DISCARDME!", *keepstr = "KEEPME!"; + const size_t discard_size = strlen(discardstr), keep_size = strlen(keepstr); + + // Fill the file with the requested data; first discard data, then kept data + size_t written = 0; + while (written < dsize) { + size_t bytes = min(dsize - written, discard_size); + CHECK_ERR(write(fd.get(), discardstr, bytes)); + written += bytes; + } + written = 0; + while (written < ksize) { + size_t bytes = min(ksize - written, keep_size); + CHECK_ERR(write(fd.get(), keepstr, bytes)); + written += bytes; + } + + TruncateLogFile(path, limit, keep); + + // File should now be shorter + struct stat statbuf; + CHECK_ERR(fstat(fd.get(), &statbuf)); + CHECK_EQ(static_cast(statbuf.st_size), expect); + CHECK_ERR(lseek(fd.get(), 0, SEEK_SET)); + + // File should contain the suffix of the original file + const size_t buf_size = static_cast(statbuf.st_size) + 1; + std::vector buf(buf_size); + CHECK_ERR(read(fd.get(), buf.data(), buf_size)); + + const char* p = buf.data(); + size_t checked = 0; + while (checked < expect) { + size_t bytes = min(expect - checked, keep_size); + CHECK(!memcmp(p, keepstr, bytes)); + checked += bytes; + } +} + +static void TestTruncate() { +#ifdef HAVE_UNISTD_H + fprintf(stderr, "==== Test log truncation\n"); + string path = FLAGS_test_tmpdir + "/truncatefile"; + + // Test on a small file + TestOneTruncate(path.c_str(), 10, 10, 10, 10, 10); + + // And a big file (multiple blocks to copy) + TestOneTruncate(path.c_str(), 2U << 20U, 4U << 10U, 3U << 20U, 4U << 10U, + 4U << 10U); + + // Check edge-case limits + TestOneTruncate(path.c_str(), 10, 20, 0, 20, 20); + TestOneTruncate(path.c_str(), 10, 0, 0, 0, 0); + TestOneTruncate(path.c_str(), 10, 50, 0, 10, 10); + TestOneTruncate(path.c_str(), 50, 100, 0, 30, 30); + + // MacOSX 10.4 doesn't fail in this case. + // Windows doesn't have symlink. + // Let's just ignore this test for these cases. +# if !defined(GLOG_OS_MACOSX) && !defined(GLOG_OS_WINDOWS) + // Through a symlink should fail to truncate + string linkname = path + ".link"; + unlink(linkname.c_str()); + CHECK_ERR(symlink(path.c_str(), linkname.c_str())); + TestOneTruncate(linkname.c_str(), 10, 10, 0, 30, 30); +# endif + + // The /proc/self path makes sense only for linux. +# if defined(GLOG_OS_LINUX) + // Through an open fd symlink should work + int fd; + CHECK_ERR(fd = open(path.c_str(), O_APPEND | O_WRONLY)); + char fdpath[64]; + std::snprintf(fdpath, sizeof(fdpath), "/proc/self/fd/%d", fd); + TestOneTruncate(fdpath, 10, 10, 10, 10, 10); +# endif + +#endif +} + +struct RecordDeletionLogger : public base::Logger { + RecordDeletionLogger(bool* set_on_destruction, base::Logger* wrapped_logger) + : set_on_destruction_(set_on_destruction), + wrapped_logger_(wrapped_logger) { + *set_on_destruction_ = false; + } + ~RecordDeletionLogger() override { *set_on_destruction_ = true; } + void Write(bool force_flush, + const std::chrono::system_clock::time_point& timestamp, + const char* message, size_t length) override { + wrapped_logger_->Write(force_flush, timestamp, message, length); + } + void Flush() override { wrapped_logger_->Flush(); } + uint32 LogSize() override { return wrapped_logger_->LogSize(); } + + private: + bool* set_on_destruction_; + base::Logger* wrapped_logger_; +}; + +static void TestCustomLoggerDeletionOnShutdown() { + bool custom_logger_deleted = false; + base::SetLogger(GLOG_INFO, + new RecordDeletionLogger(&custom_logger_deleted, + base::GetLogger(GLOG_INFO))); + EXPECT_TRUE(IsGoogleLoggingInitialized()); + ShutdownGoogleLogging(); + EXPECT_TRUE(custom_logger_deleted); + EXPECT_FALSE(IsGoogleLoggingInitialized()); +} + +namespace LogTimes { +// Log a "message" every 10ms, 10 times. These numbers are nice compromise +// between total running time of 100ms and the period of 10ms. The period is +// large enough such that any CPU and OS scheduling variation shouldn't affect +// the results from the ideal case by more than 5% (500us or 0.5ms) +constexpr int64_t LOG_PERIOD_NS = 10000000; // 10ms +constexpr int64_t LOG_PERIOD_TOL_NS = 500000; // 500us + +// Set an upper limit for the number of times the stream operator can be +// called. Make sure not to exceed this number of times the stream operator is +// called, since it is also the array size and will be indexed by the stream +// operator. +constexpr size_t MAX_CALLS = 10; +} // namespace LogTimes + +struct LogTimeRecorder { + LogTimeRecorder() = default; + size_t m_streamTimes{0}; + std::chrono::steady_clock::time_point m_callTimes[LogTimes::MAX_CALLS]; +}; +// The stream operator is called by LOG_EVERY_T every time a logging event +// occurs. Make sure to save the times for each call as they will be used later +// to verify the time delta between each call. +std::ostream& operator<<(std::ostream& stream, LogTimeRecorder& t) { + t.m_callTimes[t.m_streamTimes++] = std::chrono::steady_clock::now(); + return stream; +} +// get elapsed time in nanoseconds +int64 elapsedTime_ns(const std::chrono::steady_clock::time_point& begin, + const std::chrono::steady_clock::time_point& end) { + return std::chrono::duration_cast((end - begin)) + .count(); +} + +static void TestLogPeriodically() { + fprintf(stderr, "==== Test log periodically\n"); + + LogTimeRecorder timeLogger; + + constexpr double LOG_PERIOD_SEC = LogTimes::LOG_PERIOD_NS * 1e-9; + + while (timeLogger.m_streamTimes < LogTimes::MAX_CALLS) { + LOG_EVERY_T(INFO, LOG_PERIOD_SEC) + << timeLogger << "Timed Message #" << timeLogger.m_streamTimes; + } + + // Calculate time between each call in nanoseconds for higher resolution to + // minimize error. + int64 nsBetweenCalls[LogTimes::MAX_CALLS - 1]; + for (size_t i = 1; i < LogTimes::MAX_CALLS; ++i) { + nsBetweenCalls[i - 1] = elapsedTime_ns(timeLogger.m_callTimes[i - 1], + timeLogger.m_callTimes[i]); + } + + for (long time_ns : nsBetweenCalls) { + EXPECT_NEAR(time_ns, LogTimes::LOG_PERIOD_NS, LogTimes::LOG_PERIOD_TOL_NS); + } +} + +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_ +} // namespace google + +static bool WrapSafeFNMatch(string pattern, string str) { + pattern += "abc"; + str += "defgh"; + return SafeFNMatch_(pattern.data(), pattern.size() - 3, str.data(), + str.size() - 5); +} + +TEST(SafeFNMatch, logging) { + CHECK(WrapSafeFNMatch("foo", "foo")); + CHECK(!WrapSafeFNMatch("foo", "bar")); + CHECK(!WrapSafeFNMatch("foo", "fo")); + CHECK(!WrapSafeFNMatch("foo", "foo2")); + CHECK(WrapSafeFNMatch("bar/foo.ext", "bar/foo.ext")); + CHECK(WrapSafeFNMatch("*ba*r/fo*o.ext*", "bar/foo.ext")); + CHECK(!WrapSafeFNMatch("bar/foo.ext", "bar/baz.ext")); + CHECK(!WrapSafeFNMatch("bar/foo.ext", "bar/foo")); + CHECK(!WrapSafeFNMatch("bar/foo.ext", "bar/foo.ext.zip")); + CHECK(WrapSafeFNMatch("ba?/*.ext", "bar/foo.ext")); + CHECK(WrapSafeFNMatch("ba?/*.ext", "baZ/FOO.ext")); + CHECK(!WrapSafeFNMatch("ba?/*.ext", "barr/foo.ext")); + CHECK(!WrapSafeFNMatch("ba?/*.ext", "bar/foo.ext2")); + CHECK(WrapSafeFNMatch("ba?/*", "bar/foo.ext2")); + CHECK(WrapSafeFNMatch("ba?/*", "bar/")); + CHECK(!WrapSafeFNMatch("ba?/?", "bar/")); + CHECK(!WrapSafeFNMatch("ba?/*", "bar")); +} + +// TestWaitingLogSink will save messages here +// No lock: Accessed only by TestLogSinkWriter thread +// and after its demise by its creator. +static vector global_messages; + +// helper for TestWaitingLogSink below. +// Thread that does the logic of TestWaitingLogSink +// It's free to use LOG() itself. +class TestLogSinkWriter { + public: + TestLogSinkWriter() : t_{&TestLogSinkWriter::Run, this} {} + + // Just buffer it (can't use LOG() here). + void Buffer(const string& message) { + mutex_.lock(); + RAW_LOG(INFO, "Buffering"); + messages_.push(message); + mutex_.unlock(); + RAW_LOG(INFO, "Buffered"); + } + + // Wait for the buffer to clear (can't use LOG() here). + void Wait() { + using namespace std::chrono_literals; + RAW_LOG(INFO, "Waiting"); + mutex_.lock(); + while (!NoWork()) { + mutex_.unlock(); + std::this_thread::sleep_for(1ms); + mutex_.lock(); + } + RAW_LOG(INFO, "Waited"); + mutex_.unlock(); + } + + // Trigger thread exit. + void Stop() { + std::lock_guard l(mutex_); + should_exit_ = true; + } + + void Join() { t_.join(); } + + private: + // helpers --------------- + + // For creating a "Condition". + bool NoWork() { return messages_.empty(); } + bool HaveWork() { return !messages_.empty() || should_exit_; } + + // Thread body; CAN use LOG() here! + void Run() { + using namespace std::chrono_literals; + while (true) { + mutex_.lock(); + while (!HaveWork()) { + mutex_.unlock(); + std::this_thread::sleep_for(1ms); + mutex_.lock(); + } + if (should_exit_ && messages_.empty()) { + mutex_.unlock(); + break; + } + // Give the main thread time to log its message, + // so that we get a reliable log capture to compare to golden file. + // Same for the other sleep below. + std::this_thread::sleep_for(20ms); + RAW_LOG(INFO, "Sink got a messages"); // only RAW_LOG under mutex_ here + string message = messages_.front(); + messages_.pop(); + // Normally this would be some more real/involved logging logic + // where LOG() usage can't be eliminated, + // e.g. pushing the message over with an RPC: + size_t messages_left = messages_.size(); + mutex_.unlock(); + std::this_thread::sleep_for(20ms); + // May not use LOG while holding mutex_, because Buffer() + // acquires mutex_, and Buffer is called from LOG(), + // which has its own internal mutex: + // LOG()->LogToSinks()->TestWaitingLogSink::send()->Buffer() + LOG(INFO) << "Sink is sending out a message: " << message; + LOG(INFO) << "Have " << messages_left << " left"; + global_messages.push_back(message); + } + } + + // data --------------- + + std::thread t_; + std::mutex mutex_; + bool should_exit_{false}; + queue messages_; // messages to be logged +}; + +// A log sink that exercises WaitTillSent: +// it pushes data to a buffer and wakes up another thread to do the logging +// (that other thread can than use LOG() itself), +class TestWaitingLogSink : public LogSink { + public: + TestWaitingLogSink() { + tid_ = std::this_thread::get_id(); // for thread-specific behavior + AddLogSink(this); + } + ~TestWaitingLogSink() override { + RemoveLogSink(this); + writer_.Stop(); + writer_.Join(); + } + + // (re)define LogSink interface + + void send(LogSeverity severity, const char* /* full_filename */, + const char* base_filename, int line, + const LogMessageTime& logmsgtime, const char* message, + size_t message_len) override { + // Push it to Writer thread if we are the original logging thread. + // Note: Something like ThreadLocalLogSink is a better choice + // to do thread-specific LogSink logic for real. + if (tid_ == std::this_thread::get_id()) { + writer_.Buffer(ToString(severity, base_filename, line, logmsgtime, + message, message_len)); + } + } + + void WaitTillSent() override { + // Wait for Writer thread if we are the original logging thread. + if (tid_ == std::this_thread::get_id()) writer_.Wait(); + } + + private: + std::thread::id tid_; + TestLogSinkWriter writer_; +}; + +// Check that LogSink::WaitTillSent can be used in the advertised way. +// We also do golden-stderr comparison. +static void TestLogSinkWaitTillSent() { + // Clear global_messages here to make sure that this test case can be + // reentered + global_messages.clear(); + { + using namespace std::chrono_literals; + TestWaitingLogSink sink; + // Sleeps give the sink threads time to do all their work, + // so that we get a reliable log capture to compare to the golden file. + LOG(INFO) << "Message 1"; + std::this_thread::sleep_for(60ms); + LOG(ERROR) << "Message 2"; + std::this_thread::sleep_for(60ms); + LOG(WARNING) << "Message 3"; + std::this_thread::sleep_for(60ms); + } + for (auto& global_message : global_messages) { + LOG(INFO) << "Sink capture: " << global_message; + } + CHECK_EQ(global_messages.size(), 3UL); +} + +TEST(Strerror, logging) { + int errcode = EINTR; + std::string msg = strerror(errcode); + const size_t buf_size = msg.size() + 1; + std::vector buf(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(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); +#else + CHECK_EQ(posix_strerror_r(errcode, buf.data(), 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_EQ(msg, StrError(errcode)); +} + +// Simple routines to look at the sizes of generated code for LOG(FATAL) and +// CHECK(..) via objdump +/* +static void MyFatal() { + LOG(FATAL) << "Failed"; +} +static void MyCheck(bool a, bool b) { + CHECK_EQ(a, b); +} +*/ +#ifdef HAVE_LIB_GMOCK + +TEST(DVLog, Basic) { + ScopedMockLog log; + +# if defined(NDEBUG) + // We are expecting that nothing is logged. + EXPECT_CALL(log, Log(_, _, _)).Times(0); +# else + EXPECT_CALL(log, Log(GLOG_INFO, __FILE__, "debug log")); +# endif + + FLAGS_v = 1; + DVLOG(1) << "debug log"; +} + +TEST(DVLog, V0) { + ScopedMockLog log; + + // We are expecting that nothing is logged. + EXPECT_CALL(log, Log(_, _, _)).Times(0); + + FLAGS_v = 0; + DVLOG(1) << "debug log"; +} + +TEST(LogAtLevel, Basic) { + ScopedMockLog log; + + // The function version outputs "logging.h" as a file name. + EXPECT_CALL(log, Log(GLOG_WARNING, StrNe(__FILE__), "function version")); + EXPECT_CALL(log, Log(GLOG_INFO, __FILE__, "macro version")); + + LogSeverity severity = GLOG_WARNING; + LogAtLevel(severity, "function version"); + + severity = GLOG_INFO; + // We can use the macro version as a C++ stream. + LOG_AT_LEVEL(severity) << "macro" << ' ' << "version"; +} + +TEST(TestExitOnDFatal, ToBeOrNotToBe) { + // Check the default setting... + EXPECT_TRUE(base::internal::GetExitOnDFatal()); + + // Turn off... + base::internal::SetExitOnDFatal(false); + EXPECT_FALSE(base::internal::GetExitOnDFatal()); + + // We don't die. + { + ScopedMockLog log; + // EXPECT_CALL(log, Log(_, _, _)).Times(AnyNumber()); + // LOG(DFATAL) has severity FATAL if debugging, but is + // downgraded to ERROR if not debugging. + const LogSeverity severity = +# if defined(NDEBUG) + GLOG_ERROR; +# else + GLOG_FATAL; +# endif + EXPECT_CALL(log, Log(severity, __FILE__, "This should not be fatal")); + LOG(DFATAL) << "This should not be fatal"; + } + + // Turn back on... + base::internal::SetExitOnDFatal(true); + EXPECT_TRUE(base::internal::GetExitOnDFatal()); + +# ifdef GTEST_HAS_DEATH_TEST + // Death comes on little cats' feet. + EXPECT_DEBUG_DEATH({ LOG(DFATAL) << "This should be fatal in debug mode"; }, + "This should be fatal in debug mode"); +# endif +} + +# ifdef HAVE_STACKTRACE + +static void BacktraceAtHelper() { + LOG(INFO) << "Not me"; + + // The vertical spacing of the next 3 lines is significant. + LOG(INFO) << "Backtrace me"; +} +static int kBacktraceAtLine = __LINE__ - 2; // The line of the LOG(INFO) above + +TEST(LogBacktraceAt, DoesNotBacktraceWhenDisabled) { + StrictMock log; + + FLAGS_log_backtrace_at = ""; + + EXPECT_CALL(log, Log(_, _, "Backtrace me")); + EXPECT_CALL(log, Log(_, _, "Not me")); + + BacktraceAtHelper(); +} + +TEST(LogBacktraceAt, DoesBacktraceAtRightLineWhenEnabled) { + StrictMock log; + + char where[100]; + std::snprintf(where, 100, "%s:%d", const_basename(__FILE__), + kBacktraceAtLine); + FLAGS_log_backtrace_at = where; + + // The LOG at the specified line should include a stacktrace which includes + // the name of the containing function, followed by the log message. + // We use HasSubstr()s instead of ContainsRegex() for environments + // which don't have regexp. + EXPECT_CALL( + log, Log(_, _, + AllOf(HasSubstr("stacktrace:"), HasSubstr("BacktraceAtHelper"), + HasSubstr("main"), HasSubstr("Backtrace me")))); + // Other LOGs should not include a backtrace. + EXPECT_CALL(log, Log(_, _, "Not me")); + + BacktraceAtHelper(); +} + +# endif // HAVE_STACKTRACE + +#endif // HAVE_LIB_GMOCK + +struct UserDefinedClass { + bool operator==(const UserDefinedClass&) const { return true; } +}; + +inline ostream& operator<<(ostream& out, const UserDefinedClass&) { + out << "OK"; + return out; +} + +TEST(UserDefinedClass, logging) { + UserDefinedClass u; + vector buf; + LOG_STRING(INFO, &buf) << u; + CHECK_EQ(1UL, buf.size()); + CHECK(buf[0].find("OK") != string::npos); + + // We must be able to compile this. + CHECK_EQ(u, u); +} + +TEST(LogMsgTime, gmtoff) { + /* + * Unit test for GMT offset API + * TODO: To properly test this API, we need a platform independent way to set + * time-zone. + * */ + google::LogMessage log_obj(__FILE__, __LINE__); + + std::chrono::seconds gmtoff = 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)); +} + +TEST(EmailLogging, ValidAddress) { + FlagSaver saver; + FLAGS_logmailer = "/usr/bin/true"; + + EXPECT_TRUE( + SendEmail("example@example.com", "Example subject", "Example body")); +} + +TEST(EmailLogging, MultipleAddresses) { + FlagSaver saver; + FLAGS_logmailer = "/usr/bin/true"; + + EXPECT_TRUE(SendEmail("example@example.com,foo@bar.com", "Example subject", + "Example body")); +} + +TEST(EmailLogging, InvalidAddress) { + FlagSaver saver; + FLAGS_logmailer = "/usr/bin/true"; + + EXPECT_FALSE(SendEmail("hello world@foo", "Example subject", "Example body")); +} + +TEST(EmailLogging, MaliciousAddress) { + FlagSaver saver; + FLAGS_logmailer = "/usr/bin/true"; + + 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 restore{restore_fail}; + EXPECT_THROW({ LOG(FATAL) << "must throw to fail"; }, std::logic_error); +} diff --git a/3rdparty/glog-0.7.0/src/logging_unittest.err b/3rdparty/glog-0.7.0/src/logging_unittest.err new file mode 100644 index 000000000..21517cbf7 --- /dev/null +++ b/3rdparty/glog-0.7.0/src/logging_unittest.err @@ -0,0 +1,308 @@ +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Test: v=0 stderrthreshold=2 logtostderr=0 alsologtostderr=0 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: vlog -1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: vlog 0 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log info +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log error +WARNING: Logging before InitGoogleLogging() is written to STDERR +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog -1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog 0 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log info +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log error +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if -1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info expr +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info every 1 expr +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error every 1 expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 every 1 expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Test: v=0 stderrthreshold=0 logtostderr=0 alsologtostderr=0 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: vlog -1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: vlog 0 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log info +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log error +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog -1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog 0 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log info +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log error +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if -1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info expr +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info every 1 expr +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error every 1 expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 every 1 expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] foo bar 10 3.4 +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Plog every 2, iteration 1: __SUCCESS__ [0] +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Log every 3, iteration 1 +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Log every 4, iteration 1 +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Log if every 5, iteration 1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Log if every 1, iteration 1 +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Log if less than 3 every 2, iteration 1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Log if every 1, iteration 2 +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Plog every 2, iteration 3: __ENOENT__ [2] +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Log if every 1, iteration 3 +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Log if less than 3 every 2, iteration 3 +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Log every 3, iteration 4 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Log if every 1, iteration 4 +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Plog every 2, iteration 5: __EINTR__ [4] +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Log every 4, iteration 5 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Log if every 1, iteration 5 +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Log if every 5, iteration 6 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Log if every 1, iteration 6 +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Plog every 2, iteration 7: __ENXIO__ [6] +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Log every 3, iteration 7 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Log if every 1, iteration 7 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Log if every 1, iteration 8 +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Plog every 2, iteration 9: __ENOEXEC__ [8] +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Log every 4, iteration 9 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Log if every 1, iteration 9 +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Log every 3, iteration 10 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Log if every 1, iteration 10 +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if this +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] array +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] const array +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] foo 1000 1000 3e8 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] foo 1 +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] inner +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] outer +no prefix +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: foo bar 10 3.400000 +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: array +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: const array +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: ptr __PTRTEST__ +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: ptr __NULLP__ +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: foo 1000 0000001000 3e8 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: foo 1000 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: foo 1000 +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: RAW_LOG ERROR: The Message was too long! +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: RAW_LOG ERROR: The Message was too long! +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: vlog 0 on +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: vlog 1 on +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: vlog 2 on +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Test: v=0 stderrthreshold=0 logtostderr=0 alsologtostderr=0 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: vlog -1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: vlog 0 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log info +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log error +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog -1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog 0 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log info +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log error +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if -1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info expr +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info every 1 expr +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error every 1 expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 every 1 expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Test: v=1 stderrthreshold=0 logtostderr=0 alsologtostderr=0 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: vlog -1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: vlog 0 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: vlog 1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log info +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log error +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog -1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog 0 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog 1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log info +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log error +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if -1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info expr +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info every 1 expr +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error every 1 expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 every 1 expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Test: v=-1 stderrthreshold=0 logtostderr=0 alsologtostderr=0 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: vlog -1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log info +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log error +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog -1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log info +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log error +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if -1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info expr +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info every 1 expr +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error every 1 expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Test: v=0 stderrthreshold=1 logtostderr=0 alsologtostderr=0 +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log error +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log error +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error expr +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error every 1 expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Test: v=0 stderrthreshold=2 logtostderr=0 alsologtostderr=0 +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log error +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log error +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error expr +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error every 1 expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Test: v=0 stderrthreshold=3 logtostderr=0 alsologtostderr=0 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Test: v=0 stderrthreshold=3 logtostderr=1 alsologtostderr=0 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: vlog -1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: vlog 0 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log info +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log error +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog -1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog 0 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log info +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log error +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if -1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info expr +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info every 1 expr +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error every 1 expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 every 1 expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Test: v=0 stderrthreshold=3 logtostderr=0 alsologtostderr=1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: vlog -1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: vlog 0 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log info +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log error +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog -1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog 0 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log info +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log error +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if -1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info expr +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info every 1 expr +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error every 1 expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 every 1 expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Test: v=1 stderrthreshold=1 logtostderr=0 alsologtostderr=0 +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log error +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log error +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error expr +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error every 1 expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Test: v=1 stderrthreshold=3 logtostderr=0 alsologtostderr=1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: vlog -1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: vlog 0 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: vlog 1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log info +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: log error +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog -1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog 0 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog 1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log info +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log error +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if -1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info expr +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info every 1 expr +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error every 1 expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 every 1 expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_STRING: reported info +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_STRING: reported warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_STRING: reported error +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Captured by LOG_STRING: LOG_STRING: collected info +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Captured by LOG_STRING: LOG_STRING: collected warning +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Captured by LOG_STRING: LOG_STRING: collected error +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_SINK: collected info +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_SINK: collected warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_SINK: collected error +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_SINK: reported info +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_SINK: reported warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_SINK: reported error +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Captured by LOG_TO_SINK: +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_SINK: collected info +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_SINK: collected warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_SINK: collected error +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_SINK_BUT_NOT_TO_LOGFILE: collected info +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_SINK_BUT_NOT_TO_LOGFILE: collected warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_SINK_BUT_NOT_TO_LOGFILE: collected error +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_STRING: collected info +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Captured by LOG_TO_STRING: LOG_TO_STRING: collected info +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_STRING: collected warning +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Captured by LOG_TO_STRING: LOG_TO_STRING: collected warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_STRING: collected error +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Captured by LOG_TO_STRING: LOG_TO_STRING: collected error +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_STRING: reported info +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_STRING: reported warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_STRING: reported error +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Message 1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Buffering +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Buffered +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Waiting +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Sink got a messages +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Waited +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Sink is sending out a message: IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Message 1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Have 0 left +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Message 2 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Buffering +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Buffered +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Waiting +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Sink got a messages +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Waited +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Sink is sending out a message: EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Message 2 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Have 0 left +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Message 3 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Buffering +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Buffered +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Waiting +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Sink got a messages +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] RAW: Waited +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Sink is sending out a message: WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Message 3 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Have 0 left +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Sink capture: IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Message 1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Sink capture: EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Message 2 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Sink capture: WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Message 3 diff --git a/3rdparty/glog-0.7.0/src/logging_unittest.out b/3rdparty/glog-0.7.0/src/logging_unittest.out new file mode 100644 index 000000000..18795e196 --- /dev/null +++ b/3rdparty/glog-0.7.0/src/logging_unittest.out @@ -0,0 +1,150 @@ +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog -1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog 0 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog 1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log info +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if -1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info every 1 expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 every 1 expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog -1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog 0 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog 1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log info +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log warning +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if -1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if warning +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info every 1 expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 every 1 expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog -1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog 0 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog 1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log info +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log error +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if -1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info expr +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info every 1 expr +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error every 1 expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 every 1 expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog -1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog 0 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog 1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log info +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log error +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if -1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info expr +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info every 1 expr +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error every 1 expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 every 1 expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog -1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog 0 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog 1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log info +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log error +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if -1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info expr +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info every 1 expr +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error every 1 expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 every 1 expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog -1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog 0 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog 1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log info +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if -1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info every 1 expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 every 1 expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog -1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog 0 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog 1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log info +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log error +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if -1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info expr +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if info every 1 expr +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] log_if error every 1 expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] vlog_if 0 every 1 expr +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_STRING: reported info +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_STRING: reported warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_STRING: reported error +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Captured by LOG_STRING: LOG_STRING: collected info +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Captured by LOG_STRING: LOG_STRING: collected warning +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Captured by LOG_STRING: LOG_STRING: collected error +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_SINK: collected info +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_SINK: collected warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_SINK: collected error +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_SINK: reported info +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_SINK: reported warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_SINK: reported error +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Captured by LOG_TO_SINK: +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_SINK: collected info +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_SINK: collected warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_SINK: collected error +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_SINK_BUT_NOT_TO_LOGFILE: collected info +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_SINK_BUT_NOT_TO_LOGFILE: collected warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_SINK_BUT_NOT_TO_LOGFILE: collected error +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_STRING: collected info +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Captured by LOG_TO_STRING: LOG_TO_STRING: collected info +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_STRING: collected warning +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Captured by LOG_TO_STRING: LOG_TO_STRING: collected warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_STRING: collected error +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Captured by LOG_TO_STRING: LOG_TO_STRING: collected error +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_STRING: reported info +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_STRING: reported warning +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] LOG_TO_STRING: reported error +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Message 1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Sink is sending out a message: IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Message 1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Have 0 left +EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Message 2 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Sink is sending out a message: EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Message 2 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Have 0 left +WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Message 3 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Sink is sending out a message: WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Message 3 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Have 0 left +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Sink capture: IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Message 1 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Sink capture: EYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Message 2 +IYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Sink capture: WYEARDATE TIME__ THREADID logging_unittest.cc:LINE] Message 3 diff --git a/3rdparty/glog-0.7.0/src/mock-log.h b/3rdparty/glog-0.7.0/src/mock-log.h new file mode 100644 index 000000000..317e0e709 --- /dev/null +++ b/3rdparty/glog-0.7.0/src/mock-log.h @@ -0,0 +1,154 @@ +// Copyright (c) 2007, 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: Zhanyong Wan +// +// Defines the ScopedMockLog class (using Google C++ Mocking +// Framework), which is convenient for testing code that uses LOG(). + +#ifndef GLOG_SRC_MOCK_LOG_H_ +#define GLOG_SRC_MOCK_LOG_H_ + +// For google. This must go first so we get _XOPEN_SOURCE. +#include + +#include + +#include "glog/logging.h" +#include "utilities.h" + +namespace google { +namespace glog_testing { + +// A ScopedMockLog object intercepts LOG() messages issued during its +// lifespan. Using this together with Google C++ Mocking Framework, +// it's very easy to test how a piece of code calls LOG(). The +// typical usage: +// +// TEST(FooTest, LogsCorrectly) { +// ScopedMockLog log; +// +// // We expect the WARNING "Something bad!" exactly twice. +// EXPECT_CALL(log, Log(WARNING, _, "Something bad!")) +// .Times(2); +// +// // We allow foo.cc to call LOG(INFO) any number of times. +// EXPECT_CALL(log, Log(INFO, HasSubstr("/foo.cc"), _)) +// .Times(AnyNumber()); +// +// Foo(); // Exercises the code under test. +// } +class ScopedMockLog : public google::LogSink { + public: + // When a ScopedMockLog object is constructed, it starts to + // intercept logs. + ScopedMockLog() { AddLogSink(this); } + + // When the object is destructed, it stops intercepting logs. + ~ScopedMockLog() override { RemoveLogSink(this); } + + // Implements the mock method: + // + // void Log(LogSeverity severity, const string& file_path, + // const string& message); + // + // The second argument to Send() is the full path of the source file + // in which the LOG() was issued. + // + // Note, that in a multi-threaded environment, all LOG() messages from a + // single thread will be handled in sequence, but that cannot be guaranteed + // for messages from different threads. In fact, if the same or multiple + // expectations are matched on two threads concurrently, their actions will + // be executed concurrently as well and may interleave. + MOCK_METHOD3(Log, + void(google::LogSeverity severity, const std::string& file_path, + const std::string& message)); + + private: + // Implements the send() virtual function in class LogSink. + // Whenever a LOG() statement is executed, this function will be + // invoked with information presented in the LOG(). + // + // The method argument list is long and carries much information a + // test usually doesn't care about, so we trim the list before + // forwarding the call to Log(), which is much easier to use in + // tests. + // + // We still cannot call Log() directly, as it may invoke other LOG() + // messages, either due to Invoke, or due to an error logged in + // Google C++ Mocking Framework code, which would trigger a deadlock + // since a lock is held during send(). + // + // Hence, we save the message for WaitTillSent() which will be called after + // the lock on send() is released, and we'll call Log() inside + // WaitTillSent(). Since while a single send() call may be running at a + // time, multiple WaitTillSent() calls (along with the one send() call) may + // be running simultaneously, we ensure thread-safety of the exchange between + // send() and WaitTillSent(), and that for each message, LOG(), send(), + // WaitTillSent() and Log() are executed in the same thread. + void send(google::LogSeverity severity, const char* full_filename, + const char* /*base_filename*/, int /*line*/, + const LogMessageTime& /*logmsgtime*/, const char* message, + size_t message_len) override { + // We are only interested in the log severity, full file name, and + // log message. + message_info_.severity = severity; + message_info_.file_path = full_filename; + message_info_.message = std::string(message, message_len); + } + + // Implements the WaitTillSent() virtual function in class LogSink. + // It will be executed after send() and after the global logging lock is + // released, so calls within it (or rather within the Log() method called + // within) may also issue LOG() statements. + // + // LOG(), send(), WaitTillSent() and Log() will occur in the same thread for + // a given log message. + void WaitTillSent() override { + // First, and very importantly, we save a copy of the message being + // processed before calling Log(), since Log() may indirectly call send() + // and WaitTillSent() in the same thread again. + MessageInfo message_info = message_info_; + Log(message_info.severity, message_info.file_path, message_info.message); + } + + // All relevant information about a logged message that needs to be passed + // from send() to WaitTillSent(). + struct MessageInfo { + google::LogSeverity severity; + std::string file_path; + std::string message; + }; + MessageInfo message_info_; +}; + +} // namespace glog_testing +} // namespace google + +#endif // GLOG_SRC_MOCK_LOG_H_ diff --git a/3rdparty/glog-0.7.0/src/mock-log_unittest.cc b/3rdparty/glog-0.7.0/src/mock-log_unittest.cc new file mode 100644 index 000000000..97b83c922 --- /dev/null +++ b/3rdparty/glog-0.7.0/src/mock-log_unittest.cc @@ -0,0 +1,103 @@ +// Copyright (c) 2022, 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: Zhanyong Wan + +// Tests the ScopedMockLog class. + +#include "mock-log.h" + +#include +#include + +#include + +namespace { + +using google::GLOG_ERROR; +using google::GLOG_INFO; +using google::GLOG_WARNING; +using google::glog_testing::ScopedMockLog; +using std::string; +using testing::_; +using testing::EndsWith; +using testing::InSequence; +using testing::InvokeWithoutArgs; + +// Tests that ScopedMockLog intercepts LOG()s when it's alive. +TEST(ScopedMockLogTest, InterceptsLog) { + ScopedMockLog log; + + InSequence s; + EXPECT_CALL(log, + Log(GLOG_WARNING, EndsWith("mock-log_unittest.cc"), "Fishy.")); + EXPECT_CALL(log, Log(GLOG_INFO, _, "Working...")).Times(2); + EXPECT_CALL(log, Log(GLOG_ERROR, _, "Bad!!")); + + LOG(WARNING) << "Fishy."; + LOG(INFO) << "Working..."; + LOG(INFO) << "Working..."; + LOG(ERROR) << "Bad!!"; +} + +void LogBranch() { LOG(INFO) << "Logging a branch..."; } + +void LogTree() { LOG(INFO) << "Logging the whole tree..."; } + +void LogForest() { + LOG(INFO) << "Logging the entire forest."; + LOG(INFO) << "Logging the entire forest.."; + LOG(INFO) << "Logging the entire forest..."; +} + +// The purpose of the following test is to verify that intercepting logging +// continues to work properly if a LOG statement is executed within the scope +// of a mocked call. +TEST(ScopedMockLogTest, LogDuringIntercept) { + ScopedMockLog log; + InSequence s; + EXPECT_CALL(log, Log(GLOG_INFO, __FILE__, "Logging a branch...")) + .WillOnce(InvokeWithoutArgs(LogTree)); + EXPECT_CALL(log, Log(GLOG_INFO, __FILE__, "Logging the whole tree...")) + .WillOnce(InvokeWithoutArgs(LogForest)); + EXPECT_CALL(log, Log(GLOG_INFO, __FILE__, "Logging the entire forest.")); + EXPECT_CALL(log, Log(GLOG_INFO, __FILE__, "Logging the entire forest..")); + EXPECT_CALL(log, Log(GLOG_INFO, __FILE__, "Logging the entire forest...")); + LogBranch(); +} + +} // namespace + +int main(int argc, char** argv) { + google::InitGoogleLogging(argv[0]); + testing::InitGoogleTest(&argc, argv); + testing::InitGoogleMock(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/3rdparty/glog-0.7.0/src/package_config_unittest/working_config/CMakeLists.txt b/3rdparty/glog-0.7.0/src/package_config_unittest/working_config/CMakeLists.txt new file mode 100644 index 000000000..5fcfe7fd0 --- /dev/null +++ b/3rdparty/glog-0.7.0/src/package_config_unittest/working_config/CMakeLists.txt @@ -0,0 +1,8 @@ +cmake_minimum_required (VERSION 3.16) +project (glog_package_config LANGUAGES CXX) + +find_package (glog REQUIRED NO_MODULE) + +add_executable (glog_package_config glog_package_config.cc) + +target_link_libraries (glog_package_config PRIVATE glog::glog) diff --git a/3rdparty/glog-0.7.0/src/package_config_unittest/working_config/glog_package_config.cc b/3rdparty/glog-0.7.0/src/package_config_unittest/working_config/glog_package_config.cc new file mode 100644 index 000000000..463aa9be2 --- /dev/null +++ b/3rdparty/glog-0.7.0/src/package_config_unittest/working_config/glog_package_config.cc @@ -0,0 +1,3 @@ +#include "glog/logging.h" + +int main(int /*argc*/, char** argv) { google::InitGoogleLogging(argv[0]); } diff --git a/3rdparty/glog-0.7.0/src/raw_logging.cc b/3rdparty/glog-0.7.0/src/raw_logging.cc new file mode 100644 index 000000000..e1510cd82 --- /dev/null +++ b/3rdparty/glog-0.7.0/src/raw_logging.cc @@ -0,0 +1,210 @@ +// 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: Maxim Lifantsev +// +// logging_unittest.cc covers the functionality herein + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" + +#ifdef HAVE_UNISTD_H +# include // for close() and write() +#endif +#if defined(HAVE_SYSCALL_H) +# include // for syscall() +#elif defined(HAVE_SYS_SYSCALL_H) +# include // for syscall() +#endif +#ifdef HAVE_UNISTD_H +# include +#endif +#include // 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))) && \ + !defined(GLOG_OS_EMSCRIPTEN) +# define safe_write(fd, s, len) syscall(SYS_write, fd, s, len) +#else +// Not so safe, but what can you do? +# define safe_write(fd, s, len) write(fd, s, len) +#endif + +namespace google { + +#if defined(__GNUC__) +# define GLOG_ATTRIBUTE_FORMAT(archetype, stringIndex, firstToCheck) \ + __attribute__((format(archetype, stringIndex, firstToCheck))) +# define GLOG_ATTRIBUTE_FORMAT_ARG(stringIndex) \ + __attribute__((format_arg(stringIndex))) +#else +# define GLOG_ATTRIBUTE_FORMAT(archetype, stringIndex, firstToCheck) +# define GLOG_ATTRIBUTE_FORMAT_ARG(stringIndex) +#endif + +// CAVEAT: std::vsnprintf called from *DoRawLog below has some (exotic) code +// paths that invoke malloc() and getenv() that might acquire some locks. If +// this becomes a problem we should reimplement a subset of std::vsnprintf that +// does not need locks and malloc. + +// Helper for RawLog__ below. +// *DoRawLog writes to *buf of *size and move them past the written portion. +// It returns true iff there was no overflow or error. +GLOG_ATTRIBUTE_FORMAT(printf, 3, 4) +static bool DoRawLog(char** buf, size_t* size, const char* format, ...) { + va_list ap; + va_start(ap, format); + int n = std::vsnprintf(*buf, *size, format, ap); + va_end(ap); + if (n < 0 || static_cast(n) > *size) return false; + *size -= static_cast(n); + *buf += n; + return true; +} + +// Helper for RawLog__ below. +inline static bool VADoRawLog(char** buf, size_t* size, const char* format, + va_list ap) { +#if defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif + int n = std::vsnprintf(*buf, *size, format, ap); +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + if (n < 0 || static_cast(n) > *size) return false; + *size -= static_cast(n); + *buf += n; + return true; +} + +static const int kLogBufSize = 3000; +static std::once_flag crashed; +static logging::internal::CrashReason crash_reason; +static char crash_buf[kLogBufSize + 1] = {0}; // Will end in '\0' + +namespace { +template +class StaticStringBuf : public std::streambuf { + public: + StaticStringBuf() { + setp(std::begin(data_), std::end(data_)); + setg(std::begin(data_), std::begin(data_), std::end(data_)); + } + const char* data() noexcept { + if (pptr() != pbase() && pptr() != epptr() && *(pptr() - 1) != '\0') { + sputc('\0'); + } + return data_; + } + + private: + char data_[N]; +}; +} // namespace + +GLOG_ATTRIBUTE_FORMAT(printf, 4, 5) +void RawLog__(LogSeverity severity, const char* file, int line, + const char* format, ...) { + if (!(FLAGS_logtostdout || FLAGS_logtostderr || + severity >= FLAGS_stderrthreshold || FLAGS_alsologtostderr || + !IsGoogleLoggingInitialized())) { + return; // this stderr log message is suppressed + } + + // We do not have any any option other that string streams to obtain the + // thread identifier as the corresponding value is not convertible to an + // integer. Use a statically allocated buffer to avoid dynamic memory + // allocations. + StaticStringBuf sbuf; + std::ostream oss(&sbuf); + + oss << std::setw(5) << std::this_thread::get_id(); + + // can't call localtime_r here: it can allocate + char buffer[kLogBufSize]; + char* buf = buffer; + size_t size = sizeof(buffer); + + // 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(), + const_basename(const_cast(file)), line); + + // Record the position and size of the buffer after the prefix + const char* msg_start = buf; + const size_t msg_size = size; + + va_list ap; + va_start(ap, format); + bool no_chop = VADoRawLog(&buf, &size, format, ap); + va_end(ap); + if (no_chop) { + DoRawLog(&buf, &size, "\n"); + } else { + DoRawLog(&buf, &size, "RAW_LOG ERROR: The Message was too long!\n"); + } + // We make a raw syscall to write directly to the stderr file descriptor, + // 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)); + if (severity == GLOG_FATAL) { + std::call_once(crashed, [file, line, msg_start, msg_size] { + crash_reason.filename = file; + crash_reason.line_number = line; + memcpy(crash_buf, msg_start, msg_size); // Don't include prefix + crash_reason.message = crash_buf; +#ifdef HAVE_STACKTRACE + crash_reason.depth = + GetStackTrace(crash_reason.stack, ARRAYSIZE(crash_reason.stack), 1); +#else + crash_reason.depth = 0; +#endif + SetCrashReason(&crash_reason); + }); + LogMessage::Fail(); // abort() + } +} + +} // namespace google diff --git a/3rdparty/glog-0.7.0/src/signalhandler.cc b/3rdparty/glog-0.7.0/src/signalhandler.cc new file mode 100644 index 000000000..c5bae598d --- /dev/null +++ b/3rdparty/glog-0.7.0/src/signalhandler.cc @@ -0,0 +1,402 @@ +// 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: Satoru Takabayashi +// +// Implementation of InstallFailureSignalHandler(). + +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "glog/logging.h" +#include "glog/platform.h" +#include "stacktrace.h" +#include "symbolize.h" +#include "utilities.h" + +#ifdef HAVE_UCONTEXT_H +# include +#endif +#ifdef HAVE_SYS_UCONTEXT_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif + +namespace google { + +namespace { + +// We'll install the failure signal handler for these signals. We could +// use strsignal() to get signal names, but we don't use it to avoid +// introducing yet another #ifdef complication. +// +// The list should be synced with the comment in signalhandler.h. +const struct { + int number; + const char* name; +} kFailureSignals[] = { + {SIGSEGV, "SIGSEGV"}, {SIGILL, "SIGILL"}, + {SIGFPE, "SIGFPE"}, {SIGABRT, "SIGABRT"}, +#if !defined(GLOG_OS_WINDOWS) + {SIGBUS, "SIGBUS"}, +#endif + {SIGTERM, "SIGTERM"}, +}; + +static bool kFailureSignalHandlerInstalled = false; + +#if !defined(GLOG_OS_WINDOWS) +// Returns the program counter from signal context, nullptr if unknown. +void* GetPC(void* ucontext_in_void) { +# if (defined(HAVE_UCONTEXT_H) || defined(HAVE_SYS_UCONTEXT_H)) && \ + defined(PC_FROM_UCONTEXT) + if (ucontext_in_void != nullptr) { + ucontext_t* context = reinterpret_cast(ucontext_in_void); + return (void*)context->PC_FROM_UCONTEXT; + } +# else + (void)ucontext_in_void; +# endif + return nullptr; +} +#endif + +// The class is used for formatting error messages. We don't use printf() +// as it's not async signal safe. +class MinimalFormatter { + public: + MinimalFormatter(char* buffer, size_t size) + : buffer_(buffer), cursor_(buffer), end_(buffer + size) {} + + // Returns the number of bytes written in the buffer. + std::size_t num_bytes_written() const { + return static_cast(cursor_ - buffer_); + } + + // Appends string from "str" and updates the internal cursor. + void AppendString(const char* str) { + ptrdiff_t i = 0; + while (str[i] != '\0' && cursor_ + i < end_) { + cursor_[i] = str[i]; + ++i; + } + cursor_ += i; + } + + // Formats "number" in "radix" and updates the internal cursor. + // Lowercase letters are used for 'a' - 'z'. + void AppendUint64(uint64 number, unsigned radix) { + unsigned i = 0; + while (cursor_ + i < end_) { + const uint64 tmp = number % radix; + number /= radix; + cursor_[i] = static_cast(tmp < 10 ? '0' + tmp : 'a' + tmp - 10); + ++i; + if (number == 0) { + break; + } + } + // Reverse the bytes written. + std::reverse(cursor_, cursor_ + i); + cursor_ += i; + } + + // Formats "number" as hexadecimal number, and updates the internal + // cursor. Padding will be added in front if needed. + void AppendHexWithPadding(uint64 number, int width) { + char* start = cursor_; + AppendString("0x"); + AppendUint64(number, 16); + // Move to right and add padding in front if needed. + if (cursor_ < start + width) { + const int64 delta = start + width - cursor_; + std::copy(start, cursor_, start + delta); + std::fill(start, start + delta, ' '); + cursor_ = start + width; + } + } + + private: + char* buffer_; + char* cursor_; + const char* const end_; +}; + +// 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) { + // Ignore errors. + } +} + +// The writer function can be changed by InstallFailureWriter(). +void (*g_failure_writer)(const char* data, size_t size) = WriteToStderr; + +// Dumps time information. We don't dump human-readable time information +// as localtime() is not guaranteed to be async signal safe. +void DumpTimeInfo() { + time_t time_in_sec = time(nullptr); + char buf[256]; // Big enough for time info. + MinimalFormatter formatter(buf, sizeof(buf)); + formatter.AppendString("*** Aborted at "); + formatter.AppendUint64(static_cast(time_in_sec), 10); + formatter.AppendString(" (unix time)"); + formatter.AppendString(" try \"date -d @"); + formatter.AppendUint64(static_cast(time_in_sec), 10); + formatter.AppendString("\" if you are using GNU date ***\n"); + g_failure_writer(buf, formatter.num_bytes_written()); +} + +// TODO(hamaji): Use signal instead of sigaction? +#if defined(HAVE_STACKTRACE) && defined(HAVE_SIGACTION) + +// Dumps information about the signal to STDERR. +void DumpSignalInfo(int signal_number, siginfo_t* siginfo) { + // Get the signal name. + const char* signal_name = nullptr; + for (auto kFailureSignal : kFailureSignals) { + if (signal_number == kFailureSignal.number) { + signal_name = kFailureSignal.name; + } + } + + char buf[256]; // Big enough for signal info. + MinimalFormatter formatter(buf, sizeof(buf)); + + formatter.AppendString("*** "); + if (signal_name) { + formatter.AppendString(signal_name); + } else { + // Use the signal number if the name is unknown. The signal name + // should be known, but just in case. + formatter.AppendString("Signal "); + formatter.AppendUint64(static_cast(signal_number), 10); + } + formatter.AppendString(" (@0x"); + formatter.AppendUint64(reinterpret_cast(siginfo->si_addr), 16); + formatter.AppendString(")"); + formatter.AppendString(" received by PID "); + formatter.AppendUint64(static_cast(getpid()), 10); + formatter.AppendString(" (TID "); + + std::ostringstream oss; + oss << std::showbase << std::hex << std::this_thread::get_id(); + formatter.AppendString(oss.str().c_str()); + + formatter.AppendString(") "); + // Only linux has the PID of the signal sender in si_pid. +# ifdef GLOG_OS_LINUX + formatter.AppendString("from PID "); + formatter.AppendUint64(static_cast(siginfo->si_pid), 10); + formatter.AppendString("; "); +# endif + formatter.AppendString("stack trace: ***\n"); + g_failure_writer(buf, formatter.num_bytes_written()); +} + +#endif // HAVE_SIGACTION + +// Dumps information about the stack frame to STDERR. +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. + if (Symbolize(reinterpret_cast(pc) - 1, symbolized, + 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)); + + formatter.AppendString(prefix); + formatter.AppendString("@ "); + const int width = 2 * sizeof(void*) + 2; // + 2 for "0x". + formatter.AppendHexWithPadding(reinterpret_cast(pc), width); + formatter.AppendString(" "); + formatter.AppendString(symbol); + formatter.AppendString("\n"); + g_failure_writer(buf, formatter.num_bytes_written()); +} + +// Invoke the default signal handler. +void InvokeDefaultSignalHandler(int signal_number) { +#ifdef HAVE_SIGACTION + struct sigaction sig_action; + memset(&sig_action, 0, sizeof(sig_action)); + sigemptyset(&sig_action.sa_mask); + sig_action.sa_handler = SIG_DFL; + sigaction(signal_number, &sig_action, nullptr); + kill(getpid(), signal_number); +#elif defined(GLOG_OS_WINDOWS) + signal(signal_number, SIG_DFL); + raise(signal_number); +#endif +} + +// This variable is used for protecting FailureSignalHandler() from dumping +// stuff while another thread is doing it. Our policy is to let the first +// thread dump stuff and let other threads do nothing. +// See also comments in FailureSignalHandler(). +static std::once_flag signaled; + +static void HandleSignal(int signal_number +#if !defined(GLOG_OS_WINDOWS) + , + siginfo_t* signal_info, void* ucontext +#endif +) { + + // This is the first time we enter the signal handler. We are going to + // do some interesting stuff from here. + // TODO(satorux): We might want to set timeout here using alarm(), but + // mixing alarm() and sleep() can be a bad idea. + + // First dump time info. + DumpTimeInfo(); + +#if !defined(GLOG_OS_WINDOWS) + // Get the program counter from ucontext. + void* pc = GetPC(ucontext); + DumpStackFrameInfo("PC: ", pc); +#endif + +#ifdef HAVE_STACKTRACE + // Get the stack traces. + void* stack[32]; + // +1 to exclude this function. + const int depth = GetStackTrace(stack, ARRAYSIZE(stack), 1); +# ifdef HAVE_SIGACTION + DumpSignalInfo(signal_number, signal_info); +# elif !defined(GLOG_OS_WINDOWS) + (void)signal_info; +# endif + // Dump the stack traces. + for (int i = 0; i < depth; ++i) { + DumpStackFrameInfo(" ", stack[i]); + } +#elif !defined(GLOG_OS_WINDOWS) + (void)signal_info; +#endif + + // *** TRANSITION *** + // + // BEFORE this point, all code must be async-termination-safe! + // (See WARNING above.) + // + // AFTER this point, we do unsafe things, like using LOG()! + // The process could be terminated or hung at any time. We try to + // do more useful things first and riskier things later. + + // Flush the logs before we do anything in case 'anything' + // causes problems. + FlushLogFilesUnsafe(GLOG_INFO); + + // Kill ourself by the default signal handler. + InvokeDefaultSignalHandler(signal_number); +} + +// Dumps signal and stack frame information, and invokes the default +// signal handler once our job is done. +#if defined(GLOG_OS_WINDOWS) +void FailureSignalHandler(int signal_number) +#else +void FailureSignalHandler(int signal_number, siginfo_t* signal_info, + void* ucontext) +#endif +{ + std::call_once(signaled, &HandleSignal, signal_number +#if !defined(GLOG_OS_WINDOWS) + , + signal_info, ucontext +#endif + ); +} + +} // namespace + +bool IsFailureSignalHandlerInstalled() { +#ifdef HAVE_SIGACTION + // TODO(andschwa): Return kFailureSignalHandlerInstalled? + struct sigaction sig_action; + memset(&sig_action, 0, sizeof(sig_action)); + sigemptyset(&sig_action.sa_mask); + sigaction(SIGABRT, nullptr, &sig_action); + if (sig_action.sa_sigaction == &FailureSignalHandler) { + return true; + } +#elif defined(GLOG_OS_WINDOWS) + return kFailureSignalHandlerInstalled; +#endif // HAVE_SIGACTION + return false; +} + +void InstallFailureSignalHandler() { +#ifdef HAVE_SIGACTION + // Build the sigaction struct. + struct sigaction sig_action; + memset(&sig_action, 0, sizeof(sig_action)); + sigemptyset(&sig_action.sa_mask); + sig_action.sa_flags |= SA_SIGINFO; + sig_action.sa_sigaction = &FailureSignalHandler; + + for (auto kFailureSignal : kFailureSignals) { + CHECK_ERR(sigaction(kFailureSignal.number, &sig_action, nullptr)); + } + kFailureSignalHandlerInstalled = true; +#elif defined(GLOG_OS_WINDOWS) + for (size_t i = 0; i < ARRAYSIZE(kFailureSignals); ++i) { + CHECK_NE(signal(kFailureSignals[i].number, &FailureSignalHandler), SIG_ERR); + } + kFailureSignalHandlerInstalled = true; +#endif // HAVE_SIGACTION +} + +void InstallFailureWriter(void (*writer)(const char* data, size_t size)) { +#if defined(HAVE_SIGACTION) || defined(GLOG_OS_WINDOWS) + g_failure_writer = writer; +#endif // HAVE_SIGACTION +} + +} // namespace google diff --git a/3rdparty/glog-0.7.0/src/signalhandler_unittest.cc b/3rdparty/glog-0.7.0/src/signalhandler_unittest.cc new file mode 100644 index 000000000..d15aace76 --- /dev/null +++ b/3rdparty/glog-0.7.0/src/signalhandler_unittest.cc @@ -0,0 +1,109 @@ +// 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: Satoru Takabayashi +// +// This is a helper binary for testing signalhandler.cc. The actual test +// is done in signalhandler_unittest.sh. + +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "glog/logging.h" +#include "stacktrace.h" +#include "symbolize.h" + +#if defined(HAVE_UNISTD_H) +# include +#endif +#ifdef GLOG_USE_GFLAGS +# include +using namespace GFLAGS_NAMESPACE; +#endif +#if defined(_MSC_VER) +# include // write +#endif + +using namespace google; + +static void DieInThread(int* a) { + std::ostringstream oss; + oss << std::showbase << std::hex << std::this_thread::get_id(); + + fprintf(stderr, "%s is dying\n", oss.str().c_str()); + int b = 1 / *a; + fprintf(stderr, "We should have died: b=%d\n", b); +} + +static void WriteToStdout(const char* data, size_t size) { + if (write(fileno(stdout), data, size) < 0) { + // Ignore errors. + } +} + +int main(int argc, char** argv) { +#if defined(HAVE_STACKTRACE) && defined(HAVE_SYMBOLIZE) + InitGoogleLogging(argv[0]); +# ifdef GLOG_USE_GFLAGS + ParseCommandLineFlags(&argc, &argv, true); +# endif + InstallFailureSignalHandler(); + const std::string command = argc > 1 ? argv[1] : "none"; + if (command == "segv") { + // We'll check if this is outputted. + LOG(INFO) << "create the log file"; + LOG(INFO) << "a message before segv"; + // We assume 0xDEAD is not writable. + int* a = (int*)0xDEAD; + *a = 0; + } else if (command == "loop") { + fprintf(stderr, "looping\n"); + while (true) + ; + } else if (command == "die_in_thread") { + std::thread t{&DieInThread, nullptr}; + t.join(); + } else if (command == "dump_to_stdout") { + InstallFailureWriter(WriteToStdout); + abort(); + } else if (command == "installed") { + fprintf(stderr, "signal handler installed: %s\n", + IsFailureSignalHandlerInstalled() ? "true" : "false"); + } else { + // Tell the shell script + puts("OK"); + } +#endif + return 0; +} diff --git a/3rdparty/glog-0.7.0/src/signalhandler_unittest.sh b/3rdparty/glog-0.7.0/src/signalhandler_unittest.sh new file mode 100755 index 000000000..265cd458c --- /dev/null +++ b/3rdparty/glog-0.7.0/src/signalhandler_unittest.sh @@ -0,0 +1,131 @@ +#! /bin/sh +# +# Copyright (c) 2008, 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: Satoru Takabayashi +# +# Unit tests for signalhandler.cc. + +die () { + echo $1 + exit 1 +} + +BINDIR=".libs" +LIBGLOG="$BINDIR/libglog.so" + +BINARY="$BINDIR/signalhandler_unittest" +LOG_INFO="./signalhandler_unittest.INFO" + +# Remove temporary files. +rm -f signalhandler.out* + +if test -e "$BINARY"; then + # We need shared object. + export LD_LIBRARY_PATH=$BINDIR + export DYLD_LIBRARY_PATH=$BINDIR +else + # For windows + BINARY="./signalhandler_unittest.exe" + if ! test -e "$BINARY"; then + echo "We coundn't find demangle_unittest binary." + exit 1 + fi +fi + +if [ x`$BINARY` != 'xOK' ]; then + echo "PASS (No stacktrace support. We don't run this test.)" + exit 0 +fi + +# The PC cannot be obtained in signal handlers on PowerPC correctly. +# We just skip the test for PowerPC. +if [ x`uname -p` = x"powerpc" ]; then + echo "PASS (We don't test the signal handler on PowerPC.)" + exit 0 +fi + +# Test for a case the program kills itself by SIGSEGV. +GOOGLE_LOG_DIR=. $BINARY segv 2> signalhandler.out1 +for pattern in SIGSEGV 0xdead main "Aborted at [0-9]"; do + if ! grep --quiet "$pattern" signalhandler.out1; then + die "'$pattern' should appear in the output" + fi +done +if ! grep --quiet "a message before segv" $LOG_INFO; then + die "'a message before segv' should appear in the INFO log" +fi +rm -f $LOG_INFO + +# Test for a case the program is killed by this shell script. +# $! = the process id of the last command run in the background. +# $$ = the process id of this shell. +$BINARY loop 2> signalhandler.out2 & +# Wait until "looping" is written in the file. This indicates the program +# is ready to accept signals. +while true; do + if grep --quiet looping signalhandler.out2; then + break + fi +done +kill -TERM $! +wait $! + +from_pid='' +# Only linux has the process ID of the signal sender. +if [ x`uname` = "xLinux" ]; then + from_pid="from PID $$" +fi +for pattern in SIGTERM "by PID $!" "$from_pid" main "Aborted at [0-9]"; do + if ! grep --quiet "$pattern" signalhandler.out2; then + die "'$pattern' should appear in the output" + fi +done + +# Test for a case the program dies in a non-main thread. +$BINARY die_in_thread 2> signalhandler.out3 +EXPECTED_TID="`sed 's/ .*//; q' signalhandler.out3`" + +for pattern in SIGFPE DieInThread "TID $EXPECTED_TID" "Aborted at [0-9]"; do + if ! grep --quiet "$pattern" signalhandler.out3; then + die "'$pattern' should appear in the output" + fi +done + +# Test for a case the program installs a custom failure writer that writes +# stuff to stdout instead of stderr. +$BINARY dump_to_stdout 1> signalhandler.out4 +for pattern in SIGABRT main "Aborted at [0-9]"; do + if ! grep --quiet "$pattern" signalhandler.out4; then + die "'$pattern' should appear in the output" + fi +done + +echo PASS diff --git a/3rdparty/glog-0.7.0/src/stacktrace.cc b/3rdparty/glog-0.7.0/src/stacktrace.cc new file mode 100644 index 000000000..6ba178e7e --- /dev/null +++ b/3rdparty/glog-0.7.0/src/stacktrace.cc @@ -0,0 +1,38 @@ +// 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 diff --git a/3rdparty/glog-0.7.0/src/stacktrace.h b/3rdparty/glog-0.7.0/src/stacktrace.h new file mode 100644 index 000000000..fbbe92d67 --- /dev/null +++ b/3rdparty/glog-0.7.0/src/stacktrace.h @@ -0,0 +1,97 @@ +// 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. + +#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 + +#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 + +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. +// Example: +// main() { foo(); } +// foo() { bar(); } +// bar() { +// void* result[10]; +// int depth = GetStackFrames(result, 10, 1); +// } +// +// This produces: +// result[0] foo +// result[1] main +// .... ... +// +// "result" must not be nullptr. +GLOG_NO_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 diff --git a/3rdparty/glog-0.7.0/src/stacktrace_generic-inl.h b/3rdparty/glog-0.7.0/src/stacktrace_generic-inl.h new file mode 100644 index 000000000..e3a392665 --- /dev/null +++ b/3rdparty/glog-0.7.0/src/stacktrace_generic-inl.h @@ -0,0 +1,66 @@ +// Copyright (c) 2000 - 2007, 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. +// +// Portable implementation - just use glibc +// +// Note: The glibc implementation may cause a call to malloc. +// This can cause a deadlock in HeapProfiler. +#include + +#include + +#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) { + static const int kStackLength = 64; + void* stack[kStackLength]; + int size; + + size = backtrace(stack, kStackLength); + skip_count++; // we want to skip the current frame as well + int result_count = size - skip_count; + if (result_count < 0) { + result_count = 0; + } + if (result_count > max_depth) { + result_count = max_depth; + } + for (int i = 0; i < result_count; i++) { + result[i] = stack[i + skip_count]; + } + + return result_count; +} + +} // namespace glog_internal_namespace_ +} // namespace google diff --git a/3rdparty/glog-0.7.0/src/stacktrace_libunwind-inl.h b/3rdparty/glog-0.7.0/src/stacktrace_libunwind-inl.h new file mode 100644 index 000000000..71c69ad08 --- /dev/null +++ b/3rdparty/glog-0.7.0/src/stacktrace_libunwind-inl.h @@ -0,0 +1,95 @@ +// Copyright (c) 2005 - 2007, 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: Arun Sharma +// +// Produce stack trace using libunwind + +#include "utilities.h" + +extern "C" { +#define UNW_LOCAL_ONLY +#include +} +#include "glog/raw_logging.h" +#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 +// internal mmap based memory allocator), and that mmap gets trapped +// and causes a stack-trace request. If were to try to honor that +// recursive request, we'd end up with infinite recursion or deadlock. +// Luckily, it's safe to ignore those subsequent traces. In such +// cases, we return 0 to indicate the situation. +// We can use the GCC __thread syntax here since libunwind is not supported on +// Windows. +static __thread bool g_tl_entered; // Initialized to false. + +// If you change this function, also change GetStackFrames below. +int GetStackTrace(void** result, int max_depth, int skip_count) { + void* ip; + int n = 0; + unw_cursor_t cursor; + unw_context_t uc; + + if (g_tl_entered) { + return 0; + } + g_tl_entered = true; + + unw_getcontext(&uc); + RAW_CHECK(unw_init_local(&cursor, &uc) >= 0, "unw_init_local failed"); + skip_count++; // Do not include the "GetStackTrace" frame + + while (n < max_depth) { + int ret = + unw_get_reg(&cursor, UNW_REG_IP, reinterpret_cast(&ip)); + if (ret < 0) { + break; + } + if (skip_count > 0) { + skip_count--; + } else { + result[n++] = ip; + } + ret = unw_step(&cursor); + if (ret <= 0) { + break; + } + } + + g_tl_entered = false; + return n; +} + +} // namespace glog_internal_namespace_ +} // namespace google diff --git a/3rdparty/glog-0.7.0/src/stacktrace_powerpc-inl.h b/3rdparty/glog-0.7.0/src/stacktrace_powerpc-inl.h new file mode 100644 index 000000000..52bfd0230 --- /dev/null +++ b/3rdparty/glog-0.7.0/src/stacktrace_powerpc-inl.h @@ -0,0 +1,136 @@ +// Copyright (c) 2007, 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: Craig Silverstein +// +// Produce stack trace. I'm guessing (hoping!) the code is much like +// for x86. For apple machines, at least, it seems to be; see +// http://developer.apple.com/documentation/mac/runtimehtml/RTArch-59.html +// http://www.linux-foundation.org/spec/ELF/ppc64/PPC-elf64abi-1.9.html#STACK +// Linux has similar code: http://patchwork.ozlabs.org/linuxppc/patch?id=8882 + +#include // for uintptr_t +#include + +#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 +// checks (the strictness of which is controlled by the boolean parameter +// "STRICT_UNWINDING") to reduce the chance that a bad pointer is returned. +template +static void** NextStackFrame(void** old_sp) { + void** new_sp = static_cast(*old_sp); + + // Check that the transition from frame pointer old_sp to frame + // pointer new_sp isn't clearly bogus + if (STRICT_UNWINDING) { + // With the stack growing downwards, older stack frame must be + // at a greater address that the current one. + if (new_sp <= old_sp) return nullptr; + // Assume stack frames larger than 100,000 bytes are bogus. + if ((uintptr_t)new_sp - (uintptr_t)old_sp > 100000) return nullptr; + } else { + // In the non-strict mode, allow discontiguous stack frames. + // (alternate-signal-stacks for example). + if (new_sp == old_sp) return nullptr; + // And allow frames upto about 1MB. + if ((new_sp > old_sp) && + ((uintptr_t)new_sp - (uintptr_t)old_sp > 1000000)) { + return nullptr; + } + } + if ((uintptr_t)new_sp & (sizeof(void*) - 1)) return nullptr; + return new_sp; +} + +// This ensures that GetStackTrace stes up the Link Register properly. +void StacktracePowerPCDummyFunction() __attribute__((noinline)); +void StacktracePowerPCDummyFunction() { __asm__ volatile(""); } + +// If you change this function, also change GetStackFrames below. +int GetStackTrace(void** result, int max_depth, int skip_count) { + void** sp; + // Apple OS X uses an old version of gnu as -- both Darwin 7.9.0 (Panther) + // and Darwin 8.8.1 (Tiger) use as 1.38. This means we have to use a + // different asm syntax. I don't know quite the best way to discriminate + // systems using the old as from the new one; I've gone with __APPLE__. +#ifdef __APPLE__ + __asm__ volatile("mr %0,r1" : "=r"(sp)); +#else + __asm__ volatile("mr %0,1" : "=r"(sp)); +#endif + + // On PowerPC, the "Link Register" or "Link Record" (LR), is a stack + // entry that holds the return address of the subroutine call (what + // instruction we run after our function finishes). This is the + // same as the stack-pointer of our parent routine, which is what we + // want here. While the compiler will always(?) set up LR for + // subroutine calls, it may not for leaf functions (such as this one). + // This routine forces the compiler (at least gcc) to push it anyway. + StacktracePowerPCDummyFunction(); + + // The LR save area is used by the callee, so the top entry is bogus. + skip_count++; + + int n = 0; + while (sp && n < max_depth) { + if (skip_count > 0) { + skip_count--; + } else { + // PowerPC has 3 main ABIs, which say where in the stack the + // Link Register is. For DARWIN and AIX (used by apple and + // linux ppc64), it's in sp[2]. For SYSV (used by linux ppc), + // it's in sp[1]. +#if defined(_CALL_AIX) || defined(_CALL_DARWIN) + result[n++] = *(sp + 2); +#elif defined(_CALL_SYSV) + result[n++] = *(sp + 1); +#elif defined(__APPLE__) || \ + ((defined(__linux) || defined(__linux__)) && defined(__PPC64__)) + // This check is in case the compiler doesn't define _CALL_AIX/etc. + result[n++] = *(sp + 2); +#elif defined(__linux) || defined(__OpenBSD__) + // This check is in case the compiler doesn't define _CALL_SYSV. + result[n++] = *(sp + 1); +#else +# error Need to specify the PPC ABI for your architecture. +#endif + } + // Use strict unwinding rules. + sp = NextStackFrame(sp); + } + return n; +} + +} // namespace glog_internal_namespace_ +} // namespace google diff --git a/3rdparty/glog-0.7.0/src/stacktrace_unittest.cc b/3rdparty/glog-0.7.0/src/stacktrace_unittest.cc new file mode 100644 index 000000000..6a3253fe6 --- /dev/null +++ b/3rdparty/glog-0.7.0/src/stacktrace_unittest.cc @@ -0,0 +1,251 @@ +// Copyright (c) 2004, 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. + +#include "stacktrace.h" + +#include +#include + +#include "base/commandlineflags.h" +#include "config.h" +#include "glog/logging.h" +#include "utilities.h" + +#ifdef HAVE_EXECINFO_BACKTRACE_SYMBOLS +# include +#endif + +#ifdef HAVE_STACKTRACE + +// Obtain a backtrace, verify that the expected callers are present in the +// backtrace, and maybe print the backtrace to stdout. + +// The sequence of functions whose return addresses we expect to see in the +// backtrace. +const int BACKTRACE_STEPS = 6; + +struct AddressRange { + const void *start, *end; +}; + +// Expected function [start,end] range. +AddressRange expected_range[BACKTRACE_STEPS]; + +# if __GNUC__ +// Using GCC extension: address of a label can be taken with '&&label'. +// Start should be a label somewhere before recursive call, end somewhere +// after it. +# define INIT_ADDRESS_RANGE(fn, start_label, end_label, prange) \ + do { \ + (prange)->start = &&start_label; \ + (prange)->end = &&end_label; \ + CHECK_LT((prange)->start, (prange)->end); \ + } while (0) +// This macro expands into "unmovable" code (opaque to GCC), and that +// prevents GCC from moving a_label up or down in the code. +// Without it, there is no code following the 'end' label, and GCC +// (4.3.1, 4.4.0) thinks it safe to assign &&end an address that is before +// the recursive call. +# define DECLARE_ADDRESS_LABEL(a_label) \ + a_label: \ + do { \ + __asm__ __volatile__(""); \ + } while (0) +// Gcc 4.4.0 may split function into multiple chunks, and the chunk +// performing recursive call may end up later in the code then the return +// instruction (this actually happens with FDO). +// Adjust function range from __builtin_return_address. +# define ADJUST_ADDRESS_RANGE_FROM_RA(prange) \ + do { \ + void* ra = __builtin_return_address(0); \ + CHECK_LT((prange)->start, ra); \ + if (ra > (prange)->end) { \ + printf("Adjusting range from %p..%p to %p..%p\n", (prange)->start, \ + (prange)->end, (prange)->start, ra); \ + (prange)->end = ra; \ + } \ + } while (0) +# else +// Assume the Check* functions below are not longer than 256 bytes. +# define INIT_ADDRESS_RANGE(fn, start_label, end_label, prange) \ + do { \ + (prange)->start = reinterpret_cast(&fn); \ + (prange)->end = reinterpret_cast(&fn) + 256; \ + } while (0) +# define DECLARE_ADDRESS_LABEL(a_label) \ + do { \ + } while (0) +# define ADJUST_ADDRESS_RANGE_FROM_RA(prange) \ + do { \ + } while (0) +# endif // __GNUC__ + +//-----------------------------------------------------------------------// + +static void CheckRetAddrIsInFunction(void* ret_addr, + const AddressRange& range) { + CHECK_GE(ret_addr, range.start); + CHECK_LE(ret_addr, range.end); +} + +//-----------------------------------------------------------------------// + +# if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wgnu-label-as-value" +# endif + +void ATTRIBUTE_NOINLINE CheckStackTrace(int); +static void ATTRIBUTE_NOINLINE CheckStackTraceLeaf() { + const int STACK_LEN = 10; + void* stack[STACK_LEN]; + int size; + + 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); + printf("Obtained %d stack frames.\n", size); + CHECK_GE(size, 1); + CHECK_LE(size, STACK_LEN); + + if (true) { +# ifdef HAVE_EXECINFO_BACKTRACE_SYMBOLS + char** strings = backtrace_symbols(stack, size); + printf("Obtained %d stack frames.\n", size); + for (int i = 0; i < size; i++) { + printf("%s %p\n", strings[i], stack[i]); + } + + union { + void (*p1)(int); + void* p2; + } p = {&CheckStackTrace}; + + printf("CheckStackTrace() addr: %p\n", p.p2); + free(strings); +# endif + } + for (int i = 0; i < BACKTRACE_STEPS; i++) { + printf("Backtrace %d: expected: %p..%p actual: %p ... ", i, + expected_range[i].start, expected_range[i].end, stack[i]); + fflush(stdout); + CheckRetAddrIsInFunction(stack[i], expected_range[i]); + printf("OK\n"); + } + DECLARE_ADDRESS_LABEL(end); +} + +//-----------------------------------------------------------------------// + +/* Dummy functions to make the backtrace more interesting. */ +static void ATTRIBUTE_NOINLINE CheckStackTrace4(int i) { + ADJUST_ADDRESS_RANGE_FROM_RA(&expected_range[2]); + INIT_ADDRESS_RANGE(CheckStackTrace4, start, end, &expected_range[1]); + DECLARE_ADDRESS_LABEL(start); + for (int j = i; j >= 0; j--) { + CheckStackTraceLeaf(); + } + DECLARE_ADDRESS_LABEL(end); +} +static void ATTRIBUTE_NOINLINE CheckStackTrace3(int i) { + ADJUST_ADDRESS_RANGE_FROM_RA(&expected_range[3]); + INIT_ADDRESS_RANGE(CheckStackTrace3, start, end, &expected_range[2]); + DECLARE_ADDRESS_LABEL(start); + for (int j = i; j >= 0; j--) { + CheckStackTrace4(j); + } + DECLARE_ADDRESS_LABEL(end); +} +static void ATTRIBUTE_NOINLINE CheckStackTrace2(int i) { + ADJUST_ADDRESS_RANGE_FROM_RA(&expected_range[4]); + INIT_ADDRESS_RANGE(CheckStackTrace2, start, end, &expected_range[3]); + DECLARE_ADDRESS_LABEL(start); + for (int j = i; j >= 0; j--) { + CheckStackTrace3(j); + } + DECLARE_ADDRESS_LABEL(end); +} +static void ATTRIBUTE_NOINLINE CheckStackTrace1(int i) { + ADJUST_ADDRESS_RANGE_FROM_RA(&expected_range[5]); + INIT_ADDRESS_RANGE(CheckStackTrace1, start, end, &expected_range[4]); + DECLARE_ADDRESS_LABEL(start); + for (int j = i; j >= 0; j--) { + CheckStackTrace2(j); + } + DECLARE_ADDRESS_LABEL(end); +} + +# ifndef __GNUC__ +// On non-GNU environment, we use the address of `CheckStackTrace` to +// guess the address range of this function. This guess is wrong for +// non-static function on Windows. This is probably because +// `&CheckStackTrace` returns the address of a trampoline like PLT, +// not the actual address of `CheckStackTrace`. +// See https://github.com/google/glog/issues/421 for the detail. +static +# endif + void ATTRIBUTE_NOINLINE + CheckStackTrace(int i) { + INIT_ADDRESS_RANGE(CheckStackTrace, start, end, &expected_range[5]); + DECLARE_ADDRESS_LABEL(start); + for (int j = i; j >= 0; j--) { + CheckStackTrace1(j); + } + DECLARE_ADDRESS_LABEL(end); +} + +# if defined(__clang__) +# pragma clang diagnostic pop +# endif + +//-----------------------------------------------------------------------// + +int main(int, char** argv) { + FLAGS_logtostderr = true; + google::InitGoogleLogging(argv[0]); + + CheckStackTrace(0); + + printf("PASS\n"); + return 0; +} + +#else +int main() { + +# ifdef GLOG_BAZEL_BUILD + printf("HAVE_STACKTRACE is expected to be defined in Bazel tests\n"); + exit(EXIT_FAILURE); +# endif // GLOG_BAZEL_BUILD + + printf("PASS (no stacktrace support)\n"); + return 0; +} +#endif // HAVE_STACKTRACE diff --git a/3rdparty/glog-0.7.0/src/stacktrace_unwind-inl.h b/3rdparty/glog-0.7.0/src/stacktrace_unwind-inl.h new file mode 100644 index 000000000..894f1af93 --- /dev/null +++ b/3rdparty/glog-0.7.0/src/stacktrace_unwind-inl.h @@ -0,0 +1,106 @@ +// Copyright (c) 2023, 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: Arun Sharma +// +// Produce stack trace using libgcc + +#include // ABI defined unwinder + +#include "stacktrace.h" + +namespace google { +inline namespace glog_internal_namespace_ { + +struct trace_arg_t { + void** result; + int max_depth; + int skip_count; + int count; +}; + +// Workaround for the malloc() in _Unwind_Backtrace() issue. +static _Unwind_Reason_Code nop_backtrace(struct _Unwind_Context* /*uc*/, + void* /*opq*/) { + return _URC_NO_REASON; +} + +// This code is not considered ready to run until +// static initializers run so that we are guaranteed +// that any malloc-related initialization is done. +static bool ready_to_run = false; +class StackTraceInit { + public: + StackTraceInit() { + // Extra call to force initialization + _Unwind_Backtrace(nop_backtrace, nullptr); + ready_to_run = true; + } +}; + +static StackTraceInit module_initializer; // Force initialization + +static _Unwind_Reason_Code GetOneFrame(struct _Unwind_Context* uc, void* opq) { + auto* targ = static_cast(opq); + + if (targ->skip_count > 0) { + targ->skip_count--; + } else { + targ->result[targ->count++] = reinterpret_cast(_Unwind_GetIP(uc)); + } + + if (targ->count == targ->max_depth) { + return _URC_END_OF_STACK; + } + + return _URC_NO_REASON; +} + +// If you change this function, also change GetStackFrames below. +int GetStackTrace(void** result, int max_depth, int skip_count) { + if (!ready_to_run) { + return 0; + } + + trace_arg_t targ; + + skip_count += 1; // Do not include the "GetStackTrace" frame + + targ.result = result; + targ.max_depth = max_depth; + targ.skip_count = skip_count; + targ.count = 0; + + _Unwind_Backtrace(GetOneFrame, &targ); + + return targ.count; +} + +} // namespace glog_internal_namespace_ +} // namespace google diff --git a/3rdparty/glog-0.7.0/src/stacktrace_windows-inl.h b/3rdparty/glog-0.7.0/src/stacktrace_windows-inl.h new file mode 100644 index 000000000..26d66c501 --- /dev/null +++ b/3rdparty/glog-0.7.0/src/stacktrace_windows-inl.h @@ -0,0 +1,53 @@ +// Copyright (c) 2000 - 2007, 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: Andrew Schwartzmeyer +// +// Windows implementation - just use CaptureStackBackTrace + +// clang-format off +#include // Must come before +#include +// clang-format on + +namespace google { +inline namespace glog_internal_namespace_ { + +int GetStackTrace(void** result, int max_depth, int skip_count) { + if (max_depth > 64) { + max_depth = 64; + } + skip_count++; // we want to skip the current frame as well + // This API is thread-safe (moreover it walks only the current thread). + return CaptureStackBackTrace(static_cast(skip_count), + static_cast(max_depth), result, nullptr); +} + +} // namespace glog_internal_namespace_ +} // namespace google diff --git a/3rdparty/glog-0.7.0/src/stacktrace_x86-inl.h b/3rdparty/glog-0.7.0/src/stacktrace_x86-inl.h new file mode 100644 index 000000000..ac0e55c87 --- /dev/null +++ b/3rdparty/glog-0.7.0/src/stacktrace_x86-inl.h @@ -0,0 +1,160 @@ +// Copyright (c) 2000 - 2007, 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. +// +// Produce stack trace + +#include // for uintptr_t + +#include "utilities.h" // for OS_* macros + +#if !defined(GLOG_OS_WINDOWS) +# include +# include +#endif + +#include // for nullptr + +#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 +// checks (the strictness of which is controlled by the boolean parameter +// "STRICT_UNWINDING") to reduce the chance that a bad pointer is returned. +template +static void** NextStackFrame(void** old_sp) { + void** new_sp = static_cast(*old_sp); + + // Check that the transition from frame pointer old_sp to frame + // pointer new_sp isn't clearly bogus + if (STRICT_UNWINDING) { + // With the stack growing downwards, older stack frame must be + // at a greater address that the current one. + if (new_sp <= old_sp) return nullptr; + // Assume stack frames larger than 100,000 bytes are bogus. + if (reinterpret_cast(new_sp) - + reinterpret_cast(old_sp) > + 100000) { + return nullptr; + } + } else { + // In the non-strict mode, allow discontiguous stack frames. + // (alternate-signal-stacks for example). + if (new_sp == old_sp) return nullptr; + // And allow frames upto about 1MB. + if ((new_sp > old_sp) && (reinterpret_cast(new_sp) - + reinterpret_cast(old_sp) > + 1000000)) { + return nullptr; + } + } + if (reinterpret_cast(new_sp) & (sizeof(void*) - 1)) { + return nullptr; + } +#ifdef __i386__ + // On 64-bit machines, the stack pointer can be very close to + // 0xffffffff, so we explicitly check for a pointer into the + // last two pages in the address space + if ((uintptr_t)new_sp >= 0xffffe000) return nullptr; +#endif +#if !defined(GLOG_OS_WINDOWS) + if (!STRICT_UNWINDING) { + // Lax sanity checks cause a crash in 32-bit tcmalloc/crash_reason_test + // on AMD-based machines with VDSO-enabled kernels. + // Make an extra sanity check to insure new_sp is readable. + // Note: NextStackFrame() is only called while the program + // is already on its last leg, so it's ok to be slow here. + static int page_size = getpagesize(); + void* new_sp_aligned = + reinterpret_cast(reinterpret_cast(new_sp) & + static_cast(~(page_size - 1))); + if (msync(new_sp_aligned, static_cast(page_size), MS_ASYNC) == -1) { + return nullptr; + } + } +#endif + return new_sp; +} + +// If you change this function, also change GetStackFrames below. +int GetStackTrace(void** result, int max_depth, int skip_count) { + void** sp; + +#ifdef __GNUC__ +# if __GNUC__ * 100 + __GNUC_MINOR__ >= 402 +# define USE_BUILTIN_FRAME_ADDRESS +# endif +#endif + +#ifdef USE_BUILTIN_FRAME_ADDRESS + sp = reinterpret_cast(__builtin_frame_address(0)); +#elif defined(__i386__) + // Stack frame format: + // sp[0] pointer to previous frame + // sp[1] caller address + // sp[2] first argument + // ... + sp = (void**)&result - 2; +#elif defined(__x86_64__) + // __builtin_frame_address(0) can return the wrong address on gcc-4.1.0-k8 + unsigned long rbp; + // Move the value of the register %rbp into the local variable rbp. + // We need 'volatile' to prevent this instruction from getting moved + // around during optimization to before function prologue is done. + // An alternative way to achieve this + // would be (before this __asm__ instruction) to call Noop() defined as + // static void Noop() __attribute__ ((noinline)); // prevent inlining + // static void Noop() { asm(""); } // prevent optimizing-away + __asm__ volatile("mov %%rbp, %0" : "=r"(rbp)); + // Arguments are passed in registers on x86-64, so we can't just + // offset from &result + sp = (void**)rbp; +#endif + + int n = 0; + while (sp && n < max_depth) { + if (*(sp + 1) == nullptr) { + // In 64-bit code, we often see a frame that + // points to itself and has a return address of 0. + break; + } + if (skip_count > 0) { + skip_count--; + } else { + result[n++] = *(sp + 1); + } + // Use strict unwinding rules. + sp = NextStackFrame(sp); + } + return n; +} +} // namespace glog_internal_namespace_ +} // namespace google diff --git a/3rdparty/glog-0.7.0/src/stl_logging_unittest.cc b/3rdparty/glog-0.7.0/src/stl_logging_unittest.cc new file mode 100644 index 000000000..5a8ae2aed --- /dev/null +++ b/3rdparty/glog-0.7.0/src/stl_logging_unittest.cc @@ -0,0 +1,114 @@ +// Copyright (c) 2003, 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. + +#include "glog/stl_logging.h" + +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "glog/logging.h" +#include "googletest.h" + +using namespace std; + +struct user_hash { + size_t operator()(int x) const { return static_cast(x); } +}; + +static void TestSTLLogging() { + { + // Test a sequence. + vector v; + v.push_back(10); + v.push_back(20); + v.push_back(30); + ostringstream ss; + ss << v; + EXPECT_EQ(ss.str(), "10 20 30"); + vector copied_v(v); + CHECK_EQ(v, copied_v); // This must compile. + } + + { + // Test a sorted pair associative container. + map m; + m[20] = "twenty"; + m[10] = "ten"; + m[30] = "thirty"; + ostringstream ss; + ss << m; + EXPECT_EQ(ss.str(), "(10, ten) (20, twenty) (30, thirty)"); + map copied_m(m); + CHECK_EQ(m, copied_m); // This must compile. + } + + { + // Test a long sequence. + vector v; + string expected; + for (int i = 0; i < 100; i++) { + v.push_back(i); + if (i > 0) expected += ' '; + const size_t buf_size = 256; + char buf[buf_size]; + std::snprintf(buf, buf_size, "%d", i); + expected += buf; + } + v.push_back(100); + expected += " ..."; + ostringstream ss; + ss << v; + CHECK_EQ(ss.str(), expected.c_str()); + } + + { + // Test a sorted pair associative container. + // Use a non-default comparison functor. + map> m; + m[20] = "twenty"; + m[10] = "ten"; + m[30] = "thirty"; + ostringstream ss; + ss << m; + EXPECT_EQ(ss.str(), "(30, thirty) (20, twenty) (10, ten)"); + map> copied_m(m); + CHECK_EQ(m, copied_m); // This must compile. + } +} + +int main(int, char**) { + TestSTLLogging(); + std::cout << "PASS\n"; + return 0; +} diff --git a/3rdparty/glog-0.7.0/src/striplog_unittest.cc b/3rdparty/glog-0.7.0/src/striplog_unittest.cc new file mode 100644 index 000000000..086ea3159 --- /dev/null +++ b/3rdparty/glog-0.7.0/src/striplog_unittest.cc @@ -0,0 +1,87 @@ +// Copyright (c) 2023, 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: Sergey Ioffe + +// The common part of the striplog tests. + +#include +#include +#include +#include +#include + +#include "base/commandlineflags.h" +#include "config.h" +#include "glog/logging.h" + +GLOG_DEFINE_bool(check_mode, false, "Prints 'opt' or 'dbg'"); + +using std::string; +using namespace google; + +int CheckNoReturn(bool b) { + string s; + if (b) { + LOG(FATAL) << "Fatal"; + return 0; // Workaround for MSVC warning C4715 + } else { + return 0; + } +} + +struct A {}; +std::ostream& operator<<(std::ostream& str, const A&) { return str; } + +namespace { +void handle_abort(int /*code*/) { std::exit(EXIT_FAILURE); } +} // namespace + +int main(int, char* argv[]) { +#if defined(_MSC_VER) + // Avoid presenting an interactive dialog that will cause the test to time + // out. + _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); +#endif // defined(_MSC_VER) + std::signal(SIGABRT, handle_abort); + + FLAGS_logtostderr = true; + InitGoogleLogging(argv[0]); + if (FLAGS_check_mode) { + printf("%s\n", DEBUG_MODE ? "dbg" : "opt"); + return 0; + } + LOG(INFO) << "TESTMESSAGE INFO"; + LOG(WARNING) << 2 << "something" + << "TESTMESSAGE WARNING" << 1 << 'c' << A() << std::endl; + LOG(ERROR) << "TESTMESSAGE ERROR"; + bool flag = true; + (flag ? LOG(INFO) : LOG(ERROR)) << "TESTMESSAGE COND"; + LOG(FATAL) << "TESTMESSAGE FATAL"; +} diff --git a/3rdparty/glog-0.7.0/src/symbolize.cc b/3rdparty/glog-0.7.0/src/symbolize.cc new file mode 100644 index 000000000..11a9872a0 --- /dev/null +++ b/3rdparty/glog-0.7.0/src/symbolize.cc @@ -0,0 +1,968 @@ +// 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: Satoru Takabayashi +// Stack-footprint reduction work done by Raksit Ashok +// +// Implementation note: +// +// We don't use heaps but only use stacks. We want to reduce the +// stack consumption so that the symbolizer can run on small stacks. +// +// Here are some numbers collected with GCC 4.1.0 on x86: +// - sizeof(Elf32_Sym) = 16 +// - sizeof(Elf32_Shdr) = 40 +// - sizeof(Elf64_Sym) = 24 +// - sizeof(Elf64_Shdr) = 64 +// +// This implementation is intended to be async-signal-safe but uses +// some functions which are not guaranteed to be so, such as memchr() +// and memmove(). We assume they are async-signal-safe. +// +// Additional header can be specified by the GLOG_BUILD_CONFIG_INCLUDE +// macro to add platform specific defines (e.g. GLOG_OS_OPENBSD). + +#ifdef GLOG_BUILD_CONFIG_INCLUDE +# include GLOG_BUILD_CONFIG_INCLUDE +#endif // GLOG_BUILD_CONFIG_INCLUDE + +#include "symbolize.h" + +#include "utilities.h" + +#if defined(HAVE_SYMBOLIZE) + +# include +# include +# include +# include + +# include "demangle.h" + +// 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); + } + } +} + +} // namespace + +void InstallSymbolizeCallback(SymbolizeCallback callback) { + g_symbolize_callback = callback; +} + +void InstallSymbolizeOpenObjectFileCallback( + SymbolizeOpenObjectFileCallback callback) { + g_symbolize_open_object_file_callback = callback; +} + +} // namespace glog_internal_namespace_ +} // namespace google + +# if defined(HAVE_LINK_H) + +# if defined(HAVE_DLFCN_H) +# include +# endif +# include +# include +# include +# include + +# include +# include +# include +# include +# include +# include +# include + +# include "config.h" +# include "glog/raw_logging.h" +# include "symbolize.h" + +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 +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 +// and EINTR. On success, return the number of bytes read. Otherwise, return +// -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(std::numeric_limits::max())); + char* buf0 = reinterpret_cast(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(offset + num_bytes)] { + return pread(fd, p, n, m); + }); + if (len < 0) { // There was an error other than EINTR. + return -1; + } + if (len == 0) { // Reached EOF. + break; + } + num_bytes += static_cast(len); + } + GLOG_SAFE_ASSERT(num_bytes <= count); + return static_cast(num_bytes); +} + +// Try reading exactly "count" bytes from "offset" bytes in a file +// pointed by "fd" into the buffer starting at "buf" while handling +// short reads and EINTR. On success, return true. Otherwise, return +// false. +static bool ReadFromOffsetExact(const int fd, void* buf, const size_t count, + const size_t offset) { + ssize_t len = ReadFromOffset(fd, buf, count, offset); + return static_cast(len) == count; +} + +// Returns elf_header.e_type if the file pointed by fd is an ELF binary. +static int FileGetElfType(const int fd) { + ElfW(Ehdr) elf_header; + if (!ReadFromOffsetExact(fd, &elf_header, sizeof(elf_header), 0)) { + return -1; + } + if (memcmp(elf_header.e_ident, ELFMAG, SELFMAG) != 0) { + return -1; + } + return elf_header.e_type; +} + +// Read the section headers in the given ELF binary, and if a section +// of the specified type is found, set the output to this section header +// and return true. Otherwise, return false. +// To keep stack consumption low, we would like this function to not get +// inlined. +static ATTRIBUTE_NOINLINE bool GetSectionHeaderByType(const int fd, + ElfW(Half) sh_num, + const size_t sh_offset, + ElfW(Word) type, + ElfW(Shdr) * out) { + // Read at most 16 section headers at a time to save read calls. + ElfW(Shdr) buf[16]; + for (size_t i = 0; i < sh_num;) { + const size_t num_bytes_left = (sh_num - i) * sizeof(buf[0]); + const size_t num_bytes_to_read = + (sizeof(buf) > num_bytes_left) ? num_bytes_left : sizeof(buf); + const ssize_t len = ReadFromOffset(fd, buf, num_bytes_to_read, + sh_offset + i * sizeof(buf[0])); + if (len == -1) { + return false; + } + GLOG_SAFE_ASSERT(static_cast(len) % sizeof(buf[0]) == 0); + const size_t num_headers_in_buf = static_cast(len) / sizeof(buf[0]); + GLOG_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]; + return true; + } + } + i += num_headers_in_buf; + } + return false; +} + +// There is no particular reason to limit section name to 63 characters, +// but there has (as yet) been no need for anything longer either. +const int kMaxSectionNameLen = 64; + +// name_len should include terminating '\0'. +bool GetSectionHeaderByName(int fd, const char* name, size_t name_len, + ElfW(Shdr) * out) { + ElfW(Ehdr) elf_header; + if (!ReadFromOffsetExact(fd, &elf_header, sizeof(elf_header), 0)) { + return false; + } + + ElfW(Shdr) shstrtab; + size_t shstrtab_offset = + (elf_header.e_shoff + static_cast(elf_header.e_shentsize) * + static_cast(elf_header.e_shstrndx)); + if (!ReadFromOffsetExact(fd, &shstrtab, sizeof(shstrtab), shstrtab_offset)) { + return false; + } + + for (size_t i = 0; i < elf_header.e_shnum; ++i) { + size_t section_header_offset = + (elf_header.e_shoff + elf_header.e_shentsize * i); + if (!ReadFromOffsetExact(fd, out, sizeof(*out), section_header_offset)) { + return false; + } + char header_name[kMaxSectionNameLen]; + if (sizeof(header_name) < name_len) { + RAW_LOG(WARNING, + "Section name '%s' is too long (%zu); " + "section will not be found (even if present).", + name, name_len); + // No point in even trying. + return false; + } + size_t name_offset = shstrtab.sh_offset + out->sh_name; + ssize_t n_read = ReadFromOffset(fd, &header_name, name_len, name_offset); + if (n_read == -1) { + return false; + } else if (static_cast(n_read) != name_len) { + // Short read -- name could be at end of file. + continue; + } + if (memcmp(header_name, name, name_len) == 0) { + return true; + } + } + return false; +} + +// Read a symbol table and look for the symbol containing the +// pc. Iterate over symbols in a symbol table and look for the symbol +// containing "pc". On success, return true and write the symbol name +// to out. Otherwise, return false. +// To keep stack consumption low, we would like this function to not get +// inlined. +static ATTRIBUTE_NOINLINE bool FindSymbol(uint64_t pc, const int fd, char* out, + size_t out_size, + uint64_t symbol_offset, + const ElfW(Shdr) * strtab, + const ElfW(Shdr) * symtab) { + if (symtab == nullptr) { + return false; + } + const size_t num_symbols = symtab->sh_size / symtab->sh_entsize; + for (unsigned i = 0; i < num_symbols;) { + size_t offset = symtab->sh_offset + i * symtab->sh_entsize; + + // If we are reading Elf64_Sym's, we want to limit this array to + // 32 elements (to keep stack consumption low), otherwise we can + // have a 64 element Elf32_Sym array. +# if defined(__WORDSIZE) && __WORDSIZE == 64 + const size_t NUM_SYMBOLS = 32U; +# else + const size_t NUM_SYMBOLS = 64U; +# endif + + // Read at most NUM_SYMBOLS symbols at once to save read() calls. + ElfW(Sym) buf[NUM_SYMBOLS]; + 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(len) % sizeof(buf[0]) == 0); + const size_t num_symbols_in_buf = static_cast(len) / sizeof(buf[0]); + GLOG_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; + start_address += symbol_offset; + uint64_t end_address = start_address + symbol.st_size; + if (symbol.st_value != 0 && // Skip null value symbols. + symbol.st_shndx != 0 && // Skip undefined symbols. + start_address <= pc && pc < end_address) { + ssize_t len1 = ReadFromOffset(fd, out, out_size, + strtab->sh_offset + symbol.st_name); + if (len1 <= 0 || memchr(out, '\0', out_size) == nullptr) { + memset(out, 0, out_size); + return false; + } + return true; // Obtained the symbol name. + } + } + i += num_symbols_in_buf; + } + return false; +} + +// Get the symbol name of "pc" from the file pointed by "fd". Process +// both regular and dynamic symbol tables if necessary. On success, +// write the symbol name to "out" and return true. Otherwise, return +// false. +static bool GetSymbolFromObjectFile(const int fd, uint64_t pc, char* out, + size_t out_size, uint64_t base_address) { + // Read the ELF header. + ElfW(Ehdr) elf_header; + if (!ReadFromOffsetExact(fd, &elf_header, sizeof(elf_header), 0)) { + return false; + } + + ElfW(Shdr) symtab, strtab; + + // Consult a regular symbol table first. + if (GetSectionHeaderByType(fd, elf_header.e_shnum, elf_header.e_shoff, + SHT_SYMTAB, &symtab)) { + if (!ReadFromOffsetExact( + fd, &strtab, sizeof(strtab), + elf_header.e_shoff + symtab.sh_link * sizeof(symtab))) { + return false; + } + if (FindSymbol(pc, fd, out, out_size, base_address, &strtab, &symtab)) { + return true; // Found the symbol in a regular symbol table. + } + } + + // If the symbol is not found, then consult a dynamic symbol table. + if (GetSectionHeaderByType(fd, elf_header.e_shnum, elf_header.e_shoff, + SHT_DYNSYM, &symtab)) { + if (!ReadFromOffsetExact( + fd, &strtab, sizeof(strtab), + elf_header.e_shoff + symtab.sh_link * sizeof(symtab))) { + return false; + } + if (FindSymbol(pc, fd, out, out_size, base_address, &strtab, &symtab)) { + return true; // Found the symbol in a dynamic symbol table. + } + } + + return false; +} + +namespace { + +// Helper class for reading lines from file. +// +// Note: we don't use ProcMapsIterator since the object is big (it has +// a 5k array member) and uses async-unsafe functions such as sscanf() +// and std::snprintf(). +class LineReader { + public: + explicit LineReader(int fd, char* buf, size_t buf_len, size_t offset) + : fd_(fd), + buf_(buf), + buf_len_(buf_len), + offset_(offset), + bol_(buf), + eol_(buf), + eod_(buf) {} + + // Read '\n'-terminated line from file. On success, modify "bol" + // and "eol", then return true. Otherwise, return false. + // + // Note: if the last line doesn't end with '\n', the line will be + // dropped. It's an intentional behavior to make the code simple. + bool ReadLine(const char** bol, const char** eol) { + if (BufferIsEmpty()) { // First time. + const ssize_t num_bytes = ReadFromOffset(fd_, buf_, buf_len_, offset_); + if (num_bytes <= 0) { // EOF or error. + return false; + } + offset_ += static_cast(num_bytes); + 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_". + if (!HasCompleteLine()) { + const auto incomplete_line_length = static_cast(eod_ - bol_); + // Move the trailing incomplete line to the beginning. + memmove(buf_, bol_, incomplete_line_length); + // Read text from file and append it. + char* const append_pos = buf_ + incomplete_line_length; + const size_t capacity_left = buf_len_ - incomplete_line_length; + const ssize_t num_bytes = + ReadFromOffset(fd_, append_pos, capacity_left, offset_); + if (num_bytes <= 0) { // EOF or error. + return false; + } + offset_ += static_cast(num_bytes); + eod_ = append_pos + num_bytes; + bol_ = buf_; + } + } + eol_ = FindLineFeed(); + if (eol_ == nullptr) { // '\n' not found. Malformed line. + return false; + } + *eol_ = '\0'; // Replace '\n' with '\0'. + + *bol = bol_; + *eol = eol_; + return true; + } + + // Beginning of line. + const char* bol() { return bol_; } + + // End of line. + const char* eol() { return eol_; } + + private: + LineReader(const LineReader&) = delete; + void operator=(const LineReader&) = delete; + + char* FindLineFeed() { + return reinterpret_cast( + memchr(bol_, '\n', static_cast(eod_ - bol_))); + } + + bool BufferIsEmpty() { return buf_ == eod_; } + + bool HasCompleteLine() { + return !BufferIsEmpty() && FindLineFeed() != nullptr; + } + + const int fd_; + char* const buf_; + const size_t buf_len_; + size_t offset_; + char* bol_; + char* eol_; + const char* eod_; // End of data in "buf_". +}; +} // namespace + +// Place the hex number read from "start" into "*hex". The pointer to +// the first non-hex character or "end" is returned. +static char* GetHex(const char* start, const char* end, uint64_t* hex) { + *hex = 0; + const char* p; + for (p = start; p < end; ++p) { + int ch = *p; + if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F') || + (ch >= 'a' && ch <= 'f')) { + *hex = (*hex << 4U) | + (ch < 'A' ? static_cast(ch - '0') : (ch & 0xF) + 9U); + } else { // Encountered the first non-hex character. + break; + } + } + GLOG_SAFE_ASSERT(p <= end); + return const_cast(p); +} + +// Searches for the object file (from /proc/self/maps) that contains +// the specified pc. If found, sets |start_address| to the start address +// of where this object file is mapped in memory, sets the module base +// address into |base_address|, copies the object file name into +// |out_file_name|, and attempts to open the object file. If the object +// 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; + } + + FileDescriptor mem_fd{ + FailureRetry([] { return open("/proc/self/mem", O_RDONLY); })}; + if (!mem_fd) { + return nullptr; + } + + // 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); + while (true) { + const char* cursor; + const char* eol; + if (!reader.ReadLine(&cursor, &eol)) { // EOF or malformed line. + return nullptr; + } + + // Start parsing line in /proc/self/maps. Here is an example: + // + // 08048000-0804c000 r-xp 00000000 08:01 2142121 /bin/cat + // + // We want start address (08048000), end address (0804c000), flags + // (r-xp) and file name (/bin/cat). + + // Read start address. + cursor = GetHex(cursor, eol, &start_address); + if (cursor == eol || *cursor != '-') { + return nullptr; // Malformed line. + } + ++cursor; // Skip '-'. + + // Read end address. + uint64_t end_address; + cursor = GetHex(cursor, eol, &end_address); + if (cursor == eol || *cursor != ' ') { + return nullptr; // Malformed line. + } + ++cursor; // Skip ' '. + + // Read flags. Skip flags until we encounter a space or eol. + const char* const flags_start = cursor; + while (cursor < eol && *cursor != ' ') { + ++cursor; + } + // We expect at least four letters for flags (ex. "r-xp"). + if (cursor == eol || cursor < flags_start + 4) { + return nullptr; // 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) && + memcmp(ehdr.e_ident, ELFMAG, SELFMAG) == 0) { + switch (ehdr.e_type) { + case ET_EXEC: + base_address = 0; + break; + case ET_DYN: + // Find the segment containing file offset 0. This will correspond + // to the ELF header that we just read. Normally this will have + // virtual address 0, but this is not guaranteed. We must subtract + // the virtual address from the address where the ELF header was + // mapped to get the base address. + // + // If we fail to find a segment for file offset 0, use the address + // of the ELF header as the base address. + base_address = start_address; + for (unsigned i = 0; i != ehdr.e_phnum; ++i) { + ElfW(Phdr) phdr; + if (ReadFromOffsetExact( + mem_fd.get(), &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; + break; + } + } + break; + default: + // ET_REL or ET_CORE. These aren't directly executable, so they don't + // affect the base address. + break; + } + } + + // Check start and end addresses. + if (start_address > pc || pc >= end_address) { + continue; // We skip this map. PC isn't in this map. + } + + // Check flags. We are only interested in "r*x" maps. + if (flags_start[0] != 'r' || flags_start[2] != 'x') { + continue; // We skip this map. + } + ++cursor; // Skip ' '. + + // Read file offset. + uint64_t file_offset; + cursor = GetHex(cursor, eol, &file_offset); + if (cursor == eol || *cursor != ' ') { + return nullptr; // Malformed line. + } + ++cursor; // Skip ' '. + + // Skip to file name. "cursor" now points to dev. We need to + // skip at least two spaces for dev and inode. + int num_spaces = 0; + while (cursor < eol) { + if (*cursor == ' ') { + ++num_spaces; + } else if (num_spaces >= 2) { + // The first non-space character after skipping two spaces + // is the beginning of the file name. + break; + } + ++cursor; + } + if (cursor == eol) { + return nullptr; // Malformed line. + } + + // Finally, "cursor" now points to file name of our interest. + FileDescriptor object_fd{ + FailureRetry([cursor] { return open(cursor, O_RDONLY); })}; + if (!object_fd) { + // 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 nullptr; + } + return object_fd; + } +} + +// POSIX doesn't define any async-signal safe function for converting +// an integer to ASCII. We'll have to define our own version. +// itoa_r() converts an (unsigned) integer to ASCII. It returns "buf", if the +// conversion was successful or nullptr otherwise. It never writes more than +// "sz" bytes. Output will be truncated as needed, and a NUL character is always +// appended. +// NOTE: code from sandbox/linux/seccomp-bpf/demo.cc. +static char* itoa_r(uintptr_t i, char* buf, size_t sz, unsigned base, + size_t padding) { + // Make sure we can write at least one NUL byte. + size_t n = 1; + if (n > sz) { + return nullptr; + } + + if (base < 2 || base > 16) { + buf[0] = '\000'; + return nullptr; + } + + char* start = buf; + + // Loop until we have converted the entire number. Output at least one + // character (i.e. '0'). + char* ptr = start; + do { + // Make sure there is still enough space left in our output buffer. + if (++n > sz) { + buf[0] = '\000'; + return nullptr; + } + + // Output the next digit. + *ptr++ = "0123456789abcdef"[i % base]; + i /= base; + + if (padding > 0) { + padding--; + } + } while (i > 0 || padding > 0); + + // Terminate the output with a NUL character. + *ptr = '\000'; + + // Conversion to ASCII actually resulted in the digits being in reverse + // order. We can't easily generate them in forward order, as we can't tell + // the number of characters needed until we are done converting. + // So, now, we reverse the string (except for the possible "-" sign). + while (--ptr > start) { + char ch = *ptr; + *ptr = *start; + *start++ = ch; + } + return buf; +} + +// Safely appends string |source| to string |dest|. Never writes past the +// 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); + dest += dest_string_length; + dest_size -= dest_string_length; + strncpy(dest, source, dest_size); + // Making sure |dest| is always null-terminated. + dest[dest_size - 1] = '\0'; +} + +// Converts a 64-bit value into a hex string, and safely appends it to |dest|. +// Never writes past the buffer size |dest_size| and guarantees that |dest| is +// null-terminated. +static void SafeAppendHexNumber(uint64_t value, char* dest, size_t dest_size) { + // 64-bit numbers in hex can have up to 16 digits. + char buf[17] = {'\0'}; + SafeAppendString(itoa_r(value, buf, sizeof(buf), 16, 0), dest, dest_size); +} + +// The implementation of our symbolization routine. If it +// successfully finds the symbol containing "pc" and obtains the +// symbol name, returns true and write the symbol name to "out". +// Otherwise, returns false. If Callback function is installed via +// InstallSymbolizeCallback(), the function is also called in this function, +// 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*/) { + auto pc0 = reinterpret_cast(pc); + uint64_t start_address = 0; + uint64_t base_address = 0; + FileDescriptor object_fd; + + if (out_size < 1) { + return false; + } + out[0] = '\0'; + 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)); + } else { + object_fd = OpenObjectFileContainingPcAndGetStartAddress( + pc0, start_address, base_address, out + 1, out_size - 1); + } + +# if defined(PRINT_UNSYMBOLIZED_STACK_TRACES) + { +# else + // Check whether a file name was returned. + if (!object_fd) { +# endif + if (out[1]) { + // The object file containing PC was determined successfully however the + // object file was not opened successfully. This is still considered + // success because the object file name and offset are known and tools + // like asan_symbolize.py can be used for the symbolization. + out[out_size - 1] = '\0'; // Making sure |out| is always null-terminated. + SafeAppendString("+0x", out, out_size); + SafeAppendHexNumber(pc0 - base_address, out, out_size); + SafeAppendString(")", out, out_size); + return true; + } + // Failed to determine the object file containing PC. Bail out. + return false; + } + int elf_type = FileGetElfType(object_fd.get()); + if (elf_type == -1) { + return false; + } + if (g_symbolize_callback) { + // Run the call back if it's installed. + // 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); + if (num_bytes_written > 0) { + out += static_cast(num_bytes_written); + out_size -= static_cast(num_bytes_written); + } + } + if (!GetSymbolFromObjectFile(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 + // symbol was not found. The object may have been stripped. This is still + // considered success because the object file name and offset are known + // and tools like asan_symbolize.py can be used for the symbolization. + out[out_size - 1] = '\0'; // Making sure |out| is always null-terminated. + SafeAppendString("+0x", out, out_size); + SafeAppendHexNumber(pc0 - base_address, out, out_size); + SafeAppendString(")", out, out_size); + return true; + } + return false; + } + + // Symbolization succeeded. Now we try to demangle the symbol. + DemangleInplace(out, out_size); + return true; +} + +} // namespace glog_internal_namespace_ +} // namespace google + +# elif defined(GLOG_OS_MACOSX) && defined(HAVE_DLADDR) + +# include + +# include + +namespace google { +inline namespace glog_internal_namespace_ { + +static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle( + void* pc, char* out, size_t out_size, SymbolizeOptions /*options*/) { + Dl_info info; + if (dladdr(pc, &info)) { + if (info.dli_sname) { + if (strlen(info.dli_sname) < out_size) { + strcpy(out, info.dli_sname); + // Symbolization succeeded. Now we try to demangle the symbol. + DemangleInplace(out, out_size); + return true; + } + } + } + return false; +} + +} // namespace glog_internal_namespace_ +} // namespace google + +# elif defined(GLOG_OS_WINDOWS) || defined(GLOG_OS_CYGWIN) + +# include +# include + +namespace google { +inline namespace glog_internal_namespace_ { + +namespace { + +class SymInitializer final { + public: + HANDLE process; + bool ready; + SymInitializer() : process(GetCurrentProcess()), ready(false) { + // Initialize the symbol handler. + // https://msdn.microsoft.com/en-us/library/windows/desktop/ms680344(v=vs.85).aspx + // 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); + if (SymInitialize(process, nullptr, true)) { + ready = true; + } + } + ~SymInitializer() { + SymCleanup(process); + // 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; +}; + +} // namespace + +static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void* pc, char* out, + size_t out_size, + SymbolizeOptions options) { + const static SymInitializer symInitializer; + if (!symInitializer.ready) { + return false; + } + // Resolve symbol information from address. + // https://msdn.microsoft.com/en-us/library/windows/desktop/ms680578(v=vs.85).aspx + char buf[sizeof(SYMBOL_INFO) + MAX_SYM_NAME]; + SYMBOL_INFO* symbol = reinterpret_cast(buf); + symbol->SizeOfStruct = sizeof(SYMBOL_INFO); + symbol->MaxNameLen = MAX_SYM_NAME; + // We use the ANSI version to ensure the string type is always `char *`. + // This could break if a symbol has Unicode in it. + BOOL ret = SymFromAddr(symInitializer.process, reinterpret_cast(pc), + 0, symbol); + std::size_t namelen = static_cast(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(pc), &displacement, + &line); + } + + // 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 +# error BUG: HAVE_SYMBOLIZE was wrongly set +# 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); +} + +} // namespace glog_internal_namespace_ +} // namespace google + +#endif diff --git a/3rdparty/glog-0.7.0/src/symbolize.h b/3rdparty/glog-0.7.0/src/symbolize.h new file mode 100644 index 000000000..d9ca52ddc --- /dev/null +++ b/3rdparty/glog-0.7.0/src/symbolize.h @@ -0,0 +1,206 @@ +// 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: Satoru Takabayashi +// +// This library provides Symbolize() function that symbolizes program +// counters to their corresponding symbol names on linux platforms. +// This library has a minimal implementation of an ELF symbol table +// reader (i.e. it doesn't depend on libelf, etc.). +// +// The algorithm used in Symbolize() is as follows. +// +// 1. Go through a list of maps in /proc/self/maps and find the map +// containing the program counter. +// +// 2. Open the mapped file and find a regular symbol table inside. +// Iterate over symbols in the symbol table and look for the symbol +// containing the program counter. If such a symbol is found, +// obtain the symbol name, and demangle the symbol if possible. +// If the symbol isn't found in the regular symbol table (binary is +// stripped), try the same thing with a dynamic symbol table. +// +// Note that Symbolize() is originally implemented to be used in +// FailureSignalHandler() in base/google.cc. Hence it doesn't use +// 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 +#include +#include + +#include "config.h" +#include "glog/platform.h" + +#if defined(HAVE_LINK_H) +# include // For ElfW() macro. +#elif defined(HAVE_ELF_H) +# include +#elif defined(HAVE_SYS_EXEC_ELF_H) +# include +#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 + +#ifndef GLOG_NO_SYMBOLIZE_DETECTION +# ifndef HAVE_SYMBOLIZE +// defined by gcc +# if defined(HAVE_ELF_H) || defined(HAVE_SYS_EXEC_ELF_H) +# define HAVE_SYMBOLIZE +# elif defined(GLOG_OS_MACOSX) && defined(HAVE_DLADDR) +// Use dladdr to symbolize. +# define HAVE_SYMBOLIZE +# elif defined(GLOG_OS_WINDOWS) +// Use DbgHelp to symbolize +# define HAVE_SYMBOLIZE +# endif +# endif // !defined(HAVE_SYMBOLIZE) +#endif // !defined(GLOG_NO_SYMBOLIZE_DETECTION) + +#ifdef HAVE_SYMBOLIZE + +# if !defined(SIZEOF_VOID_P) && defined(__SIZEOF_POINTER__) +# define SIZEOF_VOID_P __SIZEOF_POINTER__ +# endif + +# if defined(HAVE_ELF_H) || defined(HAVE_SYS_EXEC_ELF_H) + +// If there is no ElfW macro, let's define it by ourself. +# ifndef ElfW +# if SIZEOF_VOID_P == 4 +# define ElfW(type) Elf32_##type +# elif SIZEOF_VOID_P == 8 +# define ElfW(type) Elf64_##type +# else +# error "Unknown sizeof(void *)" +# endif +# 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 + +namespace google { +inline namespace glog_internal_namespace_ { + +// Restrictions on the callbacks that follow: +// - The callbacks must not use heaps but only use stacks. +// - The callbacks must be async-signal-safe. + +// Installs a callback function, which will be called right before a symbol name +// is printed. The callback is intended to be used for showing a file name and a +// line number preceding a symbol name. +// "fd" is a file descriptor of the object file containing the program +// counter "pc". The callback function should write output to "out" +// 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 +void InstallSymbolizeCallback(SymbolizeCallback callback); + +// Installs a callback function, which will be called instead of +// OpenObjectFileContainingPcAndGetStartAddress. The callback is expected +// to searches for the object file (from /proc/self/maps) that contains +// the specified pc. If found, sets |start_address| to the start address +// of where this object file is mapped in memory, sets the module base +// address into |base_address|, copies the object file name into +// |out_file_name|, and attempts to open the object file. If the object +// 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). +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( + static_cast>(lhs) & + static_cast>(rhs)); +} + +constexpr SymbolizeOptions operator|(SymbolizeOptions lhs, + SymbolizeOptions rhs) noexcept { + return static_cast( + static_cast>(lhs) | + static_cast>(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); + +#endif // defined(HAVE_SYMBOLIZE) + +} // namespace glog_internal_namespace_ +} // namespace google + +#endif // GLOG_INTERNAL_SYMBOLIZE_H diff --git a/3rdparty/glog-0.7.0/src/symbolize_unittest.cc b/3rdparty/glog-0.7.0/src/symbolize_unittest.cc new file mode 100644 index 000000000..f07484e63 --- /dev/null +++ b/3rdparty/glog-0.7.0/src/symbolize_unittest.cc @@ -0,0 +1,485 @@ +// 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: Satoru Takabayashi +// +// Unit tests for functions in symbolize.cc. + +#include "symbolize.h" + +#include +#include + +#include "config.h" +#include "glog/logging.h" +#include "googletest.h" +#include "utilities.h" +#include "stacktrace.h" + +#ifdef GLOG_USE_GFLAGS +# include +using namespace GFLAGS_NAMESPACE; +#endif + +using namespace std; +using namespace google; + +// Avoid compile error due to "cast between pointer-to-function and +// pointer-to-object is an extension" warnings. +#if defined(__GNUG__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wpedantic" +#endif + +#if defined(HAVE_STACKTRACE) + +# define always_inline + +# if defined(HAVE_ELF_H) || defined(HAVE_SYS_EXEC_ELF_H) || \ + 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 char symbol[4096]; + if (Symbolize(pc, symbol, sizeof(symbol), options)) { + return symbol; + } else { + return nullptr; + } +} +# endif + +# if defined(HAVE_ELF_H) || defined(HAVE_SYS_EXEC_ELF_H) +// This unit tests make sense only with GCC. +// Uses lots of GCC specific features. +# if defined(__GNUC__) && !defined(__OPENCC__) +# if __GNUC__ >= 4 +# define TEST_WITH_MODERN_GCC +# if defined(__i386__) && __i386__ // always_inline isn't supported for + // x86_64 with GCC 4.1.0. +# undef always_inline +# define always_inline __attribute__((always_inline)) +# define HAVE_ALWAYS_INLINE +# endif // __i386__ +# else +# endif // __GNUC__ >= 4 +# define TEST_WITH_LABEL_ADDRESSES +# endif + +// Make them C linkage to avoid mangled names. +extern "C" { +void nonstatic_func(); +void nonstatic_func() { + volatile int a = 0; + // NOTE: In C++20, increment of object of volatile-qualified type is + // deprecated. + a = a + 1; +} + +static void static_func() { + volatile int a = 0; + // NOTE: In C++20, increment of object of volatile-qualified type is + // deprecated. + a = a + 1; +} +} + +TEST(Symbolize, Symbolize) { + // We do C-style cast since GCC 2.95.3 doesn't allow + // reinterpret_cast(&func). + + // Compilers should give us pointers to them. + EXPECT_STREQ("nonstatic_func", TrySymbolize((void*)(&nonstatic_func))); + + // The name of an internal linkage symbol is not specified; allow either a + // mangled or an unmangled name here. + const char* static_func_symbol = + TrySymbolize(reinterpret_cast(&static_func)); + +# if !defined(_MSC_VER) || !defined(NDEBUG) + CHECK(nullptr != static_func_symbol); + EXPECT_TRUE(strcmp("static_func", static_func_symbol) == 0 || + strcmp("static_func()", static_func_symbol) == 0); +# endif + + EXPECT_TRUE(nullptr == TrySymbolize(nullptr)); +} + +struct Foo { + static void func(int x); +}; + +void ATTRIBUTE_NOINLINE Foo::func(int x) { + volatile int a = x; + // NOTE: In C++20, increment of object of volatile-qualified type is + // deprecated. + a = a + 1; +} + +// With a modern GCC, Symbolize() should return demangled symbol +// names. Function parameters should be omitted. +# ifdef TEST_WITH_MODERN_GCC +TEST(Symbolize, SymbolizeWithDemangling) { + Foo::func(100); +# if !defined(_MSC_VER) || !defined(NDEBUG) +# if defined(HAVE___CXA_DEMANGLE) + EXPECT_STREQ("Foo::func(int)", TrySymbolize((void*)(&Foo::func))); +# else + EXPECT_STREQ("Foo::func()", TrySymbolize((void*)(&Foo::func))); +# endif +# endif +} +# endif + +// Tests that verify that Symbolize footprint is within some limit. + +// To measure the stack footprint of the Symbolize function, we create +// a signal handler (for SIGUSR1 say) that calls the Symbolize function +// on an alternate stack. This alternate stack is initialized to some +// known pattern (0x55, 0x55, 0x55, ...). We then self-send this signal, +// and after the signal handler returns, look at the alternate stack +// buffer to see what portion has been touched. +// +// This trick gives us the the stack footprint of the signal handler. +// But the signal handler, even before the call to Symbolize, consumes +// some stack already. We however only want the stack usage of the +// Symbolize function. To measure this accurately, we install two signal +// handlers: one that does nothing and just returns, and another that +// calls Symbolize. The difference between the stack consumption of these +// two signals handlers should give us the Symbolize stack foorprint. + +static void* g_pc_to_symbolize; +static char g_symbolize_buffer[4096]; +static char* g_symbolize_result; + +static void EmptySignalHandler(int /*signo*/) {} + +static void SymbolizeSignalHandler(int /*signo*/) { + if (Symbolize(g_pc_to_symbolize, g_symbolize_buffer, + sizeof(g_symbolize_buffer))) { + g_symbolize_result = g_symbolize_buffer; + } else { + g_symbolize_result = nullptr; + } +} + +const int kAlternateStackSize = 8096; +const char kAlternateStackFillValue = 0x55; + +// These helper functions look at the alternate stack buffer, and figure +// out what portion of this buffer has been touched - this is the stack +// consumption of the signal handler running on this alternate stack. +static ATTRIBUTE_NOINLINE bool StackGrowsDown(int* x) { + int y; + return &y < x; +} +static int GetStackConsumption(const char* alt_stack) { + int x; + if (StackGrowsDown(&x)) { + for (int i = 0; i < kAlternateStackSize; i++) { + if (alt_stack[i] != kAlternateStackFillValue) { + return (kAlternateStackSize - i); + } + } + } else { + for (int i = (kAlternateStackSize - 1); i >= 0; i--) { + if (alt_stack[i] != kAlternateStackFillValue) { + return i; + } + } + } + return -1; +} + +# ifdef HAVE_SIGALTSTACK + +// Call Symbolize and figure out the stack footprint of this call. +static const char* SymbolizeStackConsumption(void* pc, int* stack_consumed) { + g_pc_to_symbolize = pc; + + // The alt-signal-stack cannot be heap allocated because there is a + // bug in glibc-2.2 where some signal handler setup code looks at the + // current stack pointer to figure out what thread is currently running. + // Therefore, the alternate stack must be allocated from the main stack + // itself. + char altstack[kAlternateStackSize]; + memset(altstack, kAlternateStackFillValue, kAlternateStackSize); + + // Set up the alt-signal-stack (and save the older one). + stack_t sigstk; + memset(&sigstk, 0, sizeof(stack_t)); + stack_t old_sigstk; + sigstk.ss_sp = altstack; + sigstk.ss_size = kAlternateStackSize; + sigstk.ss_flags = 0; + CHECK_ERR(sigaltstack(&sigstk, &old_sigstk)); + + // Set up SIGUSR1 and SIGUSR2 signal handlers (and save the older ones). + struct sigaction sa; + memset(&sa, 0, sizeof(struct sigaction)); + struct sigaction old_sa1, old_sa2; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_ONSTACK; + + // SIGUSR1 maps to EmptySignalHandler. + sa.sa_handler = EmptySignalHandler; + CHECK_ERR(sigaction(SIGUSR1, &sa, &old_sa1)); + + // SIGUSR2 maps to SymbolizeSignalHanlder. + sa.sa_handler = SymbolizeSignalHandler; + CHECK_ERR(sigaction(SIGUSR2, &sa, &old_sa2)); + + // Send SIGUSR1 signal and measure the stack consumption of the empty + // signal handler. + CHECK_ERR(kill(getpid(), SIGUSR1)); + int stack_consumption1 = GetStackConsumption(altstack); + + // Send SIGUSR2 signal and measure the stack consumption of the symbolize + // signal handler. + CHECK_ERR(kill(getpid(), SIGUSR2)); + int stack_consumption2 = GetStackConsumption(altstack); + + // The difference between the two stack consumption values is the + // stack footprint of the Symbolize function. + if (stack_consumption1 != -1 && stack_consumption2 != -1) { + *stack_consumed = stack_consumption2 - stack_consumption1; + } else { + *stack_consumed = -1; + } + + // Log the stack consumption values. + LOG(INFO) << "Stack consumption of empty signal handler: " + << stack_consumption1; + LOG(INFO) << "Stack consumption of symbolize signal handler: " + << stack_consumption2; + LOG(INFO) << "Stack consumption of Symbolize: " << *stack_consumed; + + // Now restore the old alt-signal-stack and signal handlers. + CHECK_ERR(sigaltstack(&old_sigstk, nullptr)); + CHECK_ERR(sigaction(SIGUSR1, &old_sa1, nullptr)); + CHECK_ERR(sigaction(SIGUSR2, &old_sa2, nullptr)); + + return g_symbolize_result; +} + +# if !defined(HAVE___CXA_DEMANGLE) +# ifdef __ppc64__ +// Symbolize stack consumption should be within 4kB. +constexpr int kStackConsumptionUpperLimit = 4096; +# else +// Symbolize stack consumption should be within 2kB. +constexpr int kStackConsumptionUpperLimit = 2048; +# endif +# endif + +TEST(Symbolize, SymbolizeStackConsumption) { + int stack_consumed; + const char* symbol; + + symbol = SymbolizeStackConsumption(reinterpret_cast(&nonstatic_func), + &stack_consumed); + EXPECT_STREQ("nonstatic_func", symbol); + EXPECT_GT(stack_consumed, 0); +# if !defined(HAVE___CXA_DEMANGLE) + EXPECT_LT(stack_consumed, kStackConsumptionUpperLimit); +# endif + + // The name of an internal linkage symbol is not specified; allow either a + // mangled or an unmangled name here. + symbol = SymbolizeStackConsumption(reinterpret_cast(&static_func), + &stack_consumed); + CHECK(nullptr != symbol); + EXPECT_TRUE(strcmp("static_func", symbol) == 0 || + strcmp("static_func()", symbol) == 0); + EXPECT_GT(stack_consumed, 0); +# if !defined(HAVE___CXA_DEMANGLE) + EXPECT_LT(stack_consumed, kStackConsumptionUpperLimit); +# endif +} + +# if defined(TEST_WITH_MODERN_GCC) && !defined(HAVE___CXA_DEMANGLE) +TEST(Symbolize, SymbolizeWithDemanglingStackConsumption) { + Foo::func(100); + int stack_consumed; + const char* symbol; + + symbol = SymbolizeStackConsumption(reinterpret_cast(&Foo::func), + &stack_consumed); + +# if defined(HAVE___CXA_DEMANGLE) + EXPECT_STREQ("Foo::func(int)", symbol); +# else + EXPECT_STREQ("Foo::func()", symbol); +# endif + EXPECT_GT(stack_consumed, 0); + EXPECT_LT(stack_consumed, kStackConsumptionUpperLimit); +} +# endif + +# endif // HAVE_SIGALTSTACK + +// x86 specific tests. Uses some inline assembler. +extern "C" { +inline void* always_inline inline_func() { + void* pc = nullptr; +# ifdef TEST_WITH_LABEL_ADDRESSES + pc = &&curr_pc; +curr_pc: +# endif + return pc; +} + +void* ATTRIBUTE_NOINLINE non_inline_func(); +void* ATTRIBUTE_NOINLINE non_inline_func() { + void* pc = nullptr; +# ifdef TEST_WITH_LABEL_ADDRESSES + pc = &&curr_pc; +curr_pc: +# endif + return pc; +} + +static void ATTRIBUTE_NOINLINE TestWithPCInsideNonInlineFunction() { +# if defined(TEST_WITH_LABEL_ADDRESSES) && defined(HAVE_ATTRIBUTE_NOINLINE) + void* pc = non_inline_func(); + const char* symbol = TrySymbolize(pc); + +# if !defined(_MSC_VER) || !defined(NDEBUG) + CHECK(symbol != nullptr); + CHECK_STREQ(symbol, "non_inline_func"); +# endif + cout << "Test case TestWithPCInsideNonInlineFunction passed." << endl; +# endif +} + +static void ATTRIBUTE_NOINLINE TestWithPCInsideInlineFunction() { +# if defined(TEST_WITH_LABEL_ADDRESSES) && defined(HAVE_ALWAYS_INLINE) + void* pc = inline_func(); // Must be inlined. + const char* symbol = TrySymbolize(pc); + +# if !defined(_MSC_VER) || !defined(NDEBUG) + CHECK(symbol != nullptr); + CHECK_STREQ(symbol, __FUNCTION__); +# endif + cout << "Test case TestWithPCInsideInlineFunction passed." << endl; +# endif +} +} + +// Test with a return address. +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); + +# if !defined(_MSC_VER) || !defined(NDEBUG) + CHECK(symbol != nullptr); + CHECK_STREQ(symbol, "main"); +# endif + cout << "Test case TestWithReturnAddress passed." << endl; +# endif +} + +# elif defined(GLOG_OS_WINDOWS) || defined(GLOG_OS_CYGWIN) + +# ifdef _MSC_VER +# include +# pragma intrinsic(_ReturnAddress) +# endif + +struct Foo { + static void func(int x); +}; + +__declspec(noinline) void Foo::func(int x) { + volatile int a = x; + // NOTE: In C++20, increment of object of volatile-qualified type is + // deprecated. + a = a + 1; +} + +TEST(Symbolize, SymbolizeWithDemangling) { + Foo::func(100); + const char* ret = TrySymbolize((void*)(&Foo::func)); + +# if defined(HAVE_DBGHELP) && !defined(NDEBUG) + EXPECT_STREQ("public: static void __cdecl Foo::func(int)", ret); +# endif +} + +__declspec(noinline) void TestWithReturnAddress() { + void* return_address = +# ifdef __GNUC__ // Cygwin and MinGW support + __builtin_return_address(0) +# else + _ReturnAddress() +# endif + ; + const char* symbol = + TrySymbolize(return_address, google::SymbolizeOptions::kNoLineNumbers); +# if !defined(_MSC_VER) || !defined(NDEBUG) + CHECK(symbol != nullptr); + CHECK_STREQ(symbol, "main"); +# endif + cout << "Test case TestWithReturnAddress passed." << endl; +} +# endif +#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) + // 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); + + TestWithPCInsideInlineFunction(); + TestWithPCInsideNonInlineFunction(); + TestWithReturnAddress(); + return RUN_ALL_TESTS(); +# elif defined(GLOG_OS_WINDOWS) || defined(GLOG_OS_CYGWIN) + TestWithReturnAddress(); + return RUN_ALL_TESTS(); +# else // GLOG_OS_WINDOWS + printf("PASS (no symbolize_unittest support)\n"); + return 0; +# endif // defined(HAVE_ELF_H) || defined(HAVE_SYS_EXEC_ELF_H) +#else + printf("PASS (no symbolize support)\n"); + return 0; +#endif // HAVE_SYMBOLIZE +} + +#if defined(__GNUG__) +# pragma GCC diagnostic pop +#endif diff --git a/3rdparty/glog-0.7.0/src/utilities.cc b/3rdparty/glog-0.7.0/src/utilities.cc new file mode 100644 index 000000000..92bc5e541 --- /dev/null +++ b/3rdparty/glog-0.7.0/src/utilities.cc @@ -0,0 +1,329 @@ +// 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: Shinichiro Hamaji + +#define _GNU_SOURCE 1 + +#include "utilities.h" + +#include +#include +#include +#include +#include + +#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 +#endif +#ifdef HAVE_SYS_TIME_H +# include +#endif +#if defined(HAVE_SYSCALL_H) +# include // for syscall() +#elif defined(HAVE_SYS_SYSCALL_H) +# include // for syscall() +#endif +#ifdef HAVE_SYSLOG_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include // For geteuid. +#endif +#ifdef HAVE_PWD_H +# include +#endif + +#if defined(HAVE___PROGNAME) +extern char* __progname; +#endif + +using std::string; + +namespace google { + +static const char* g_program_invocation_short_name = nullptr; + +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. +#ifdef HAVE_STACKTRACE + +# include "base/commandlineflags.h" +# include "stacktrace.h" +# include "symbolize.h" + +namespace google { + +using DebugWriter = void(const char*, void*); + +// The %p field width for printf() functions is two characters per byte. +// For some environments, add two extra bytes for the leading "0x". +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) { + // Ignore errors. + } + AlsoErrorWrite(GLOG_FATAL, + glog_internal_namespace_::ProgramInvocationShortName(), data); +} + +static void DebugWriteToString(const char* data, void* arg) { + reinterpret_cast(arg)->append(data); +} + +# ifdef HAVE_SYMBOLIZE +// Print a program counter and its symbol name. +static void DumpPCAndSymbol(DebugWriter* writerfn, void* arg, void* pc, + const char* const prefix) { + char tmp[1024]; + const char* symbol = "(unknown)"; + // Symbolizes the previous address of pc because pc may be in the + // next function. The overrun happens when the function ends with + // a call to a function annotated noreturn (e.g. CHECK). + if (Symbolize(reinterpret_cast(pc) - 1, tmp, sizeof(tmp))) { + symbol = tmp; + } + char buf[1024]; + std::snprintf(buf, sizeof(buf), "%s@ %*p %s\n", prefix, + kPrintfPointerFieldWidth, pc, symbol); + writerfn(buf, arg); +} +# endif + +static void DumpPC(DebugWriter* writerfn, void* arg, void* pc, + const char* const prefix) { + char buf[100]; + std::snprintf(buf, sizeof(buf), "%s@ %*p\n", prefix, kPrintfPointerFieldWidth, + pc); + writerfn(buf, arg); +} + +// Dump current stack trace as directed by writerfn +static void DumpStackTrace(int skip_count, DebugWriter* writerfn, void* arg) { + // Print stack trace + void* stack[32]; + int depth = GetStackTrace(stack, ARRAYSIZE(stack), skip_count + 1); + for (int i = 0; i < depth; i++) { +# if defined(HAVE_SYMBOLIZE) + if (FLAGS_symbolize_stacktrace) { + DumpPCAndSymbol(writerfn, arg, stack[i], " "); + } else { + DumpPC(writerfn, arg, stack[i], " "); + } +# else + DumpPC(writerfn, arg, stack[i], " "); +# endif + } +} + +# ifdef __GNUC__ +__attribute__((noreturn)) +# endif +static void +DumpStackTraceAndExit() { + DumpStackTrace(1, DebugWriteToStderr, nullptr); + + // TODO(hamaji): Use signal instead of sigaction? + if (IsFailureSignalHandlerInstalled()) { + // Set the default signal handler for SIGABRT, to avoid invoking our + // own signal handler installed by InstallFailureSignalHandler(). +# ifdef HAVE_SIGACTION + struct sigaction sig_action; + memset(&sig_action, 0, sizeof(sig_action)); + sigemptyset(&sig_action.sa_mask); + sig_action.sa_handler = SIG_DFL; + sigaction(SIGABRT, &sig_action, nullptr); +# elif defined(GLOG_OS_WINDOWS) + signal(SIGABRT, SIG_DFL); +# endif // HAVE_SIGACTION + } + + abort(); +} + +} // namespace google + +#endif // HAVE_STACKTRACE + +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; +} + +const char* ProgramInvocationShortName() { + if (g_program_invocation_short_name != nullptr) { + return g_program_invocation_short_name; + } +#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(); +int32 GetMainThreadPid() { return g_main_thread_pid; } + +bool PidHasChanged() { + int32 pid = getpid(); + if (g_main_thread_pid == pid) { + return false; + } + g_main_thread_pid = pid; + return true; +} + +static string g_my_user_name; +const string& MyUserName() { return g_my_user_name; } +static void MyUserNameInitializer() { + // TODO(hamaji): Probably this is not portable. +#if defined(GLOG_OS_WINDOWS) + const char* user = getenv("USERNAME"); +#else + const char* user = getenv("USER"); +#endif + if (user != nullptr) { + g_my_user_name = user; + } else { +#if defined(HAVE_PWD_H) && defined(HAVE_UNISTD_H) + struct passwd pwd; + struct passwd* result = nullptr; + char buffer[1024] = {'\0'}; + uid_t uid = geteuid(); + int pwuid_res = getpwuid_r(uid, &pwd, buffer, sizeof(buffer), &result); + if (pwuid_res == 0 && result) { + g_my_user_name = pwd.pw_name; + } else { + std::snprintf(buffer, sizeof(buffer), "uid%d", uid); + g_my_user_name = buffer; + } +#endif + if (g_my_user_name.empty()) { + g_my_user_name = "invalid-user"; + } + } +} +REGISTER_MODULE_INITIALIZER(utilities, MyUserNameInitializer()) + +// We use an atomic operation to prevent problems with calling CrashReason +// from inside the Mutex implementation (potentially through RAW_CHECK). +static std::atomic g_reason{nullptr}; + +void SetCrashReason(const logging::internal::CrashReason* r) { + const logging::internal::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); + +#ifdef HAVE_STACKTRACE + InstallFailureFunction(&DumpStackTraceAndExit); +#endif +} + +void ShutdownGoogleLoggingUtilities() { + CHECK(IsGoogleLoggingInitialized()) + << "You called ShutdownGoogleLogging() without calling " + "InitGoogleLogging() first!"; + g_program_invocation_short_name = nullptr; +#ifdef HAVE_SYSLOG_H + closelog(); +#endif +} + +} // namespace glog_internal_namespace_ + +#ifdef HAVE_STACKTRACE +std::string GetStackTrace() { + std::string stacktrace; + DumpStackTrace(1, DebugWriteToString, &stacktrace); + return stacktrace; +} +#endif + +} // namespace google diff --git a/3rdparty/glog-0.7.0/src/utilities.h b/3rdparty/glog-0.7.0/src/utilities.h new file mode 100644 index 000000000..8096f1c6c --- /dev/null +++ b/3rdparty/glog-0.7.0/src/utilities.h @@ -0,0 +1,283 @@ +// 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: Shinichiro Hamaji +// Sergiu Deitsch +// +// Define utilities for glog internal usage. + +#ifndef GLOG_INTERNAL_UTILITIES_H +#define GLOG_INTERNAL_UTILITIES_H + +#include +#include +#include +#include +#include +#include + +// printf macros for size_t, in the style of inttypes.h +#ifdef _LP64 +# define __PRIS_PREFIX "z" +#else +# define __PRIS_PREFIX +#endif + +// Use these macros after a % in a printf format string +// to get correct 32/64 bit behavior, like this: +// size_t size = records.size(); +// printf("%"PRIuS"\n", size); + +#define PRIdS __PRIS_PREFIX "d" +#define PRIxS __PRIS_PREFIX "x" +#define PRIuS __PRIS_PREFIX "u" +#define PRIXS __PRIS_PREFIX "X" +#define PRIoS __PRIS_PREFIX "o" + +#include "config.h" +#include "glog/platform.h" +#if defined(GLOG_USE_WINDOWS_PORT) +# include "port.h" +#endif +#if defined(HAVE_UNISTD_H) +# include +#endif +#if !defined(HAVE_SSIZE_T) +# if defined(GLOG_OS_WINDOWS) +# include +using ssize_t = SSIZE_T; +# else +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 +// separate library adds a new dependency, but doesn't need a frame +// pointer. It also doesn't call malloc. +// +// 2) Our hand-coded stack-unwinder. This depends on a certain stack +// layout, which is used by gcc (and those systems using a +// gcc-compatible ABI) on x86 systems, at least since gcc 2.95. +// It uses the frame pointer to do its work. +// +// 3) The gdb unwinder -- also the one used by the c++ exception code. +// It's obviously well-tested, but has a fatal flaw: it can call +// malloc() from the unwinder. This is a problem because we're +// trying to use the unwinder to instrument malloc(). +// +// 4) The Windows API CaptureStackTrace. +// +// Note: if you add a new implementation here, make sure it works +// correctly when GetStackTrace() is called with max_depth == 0. +// Some code may do that. + +#ifndef ARRAYSIZE +// There is a better way, but this is good enough for our purpose. +# define ARRAYSIZE(a) (sizeof(a) / sizeof(*(a))) +#endif + +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_ { + +#if defined(__has_attribute) +# if __has_attribute(noinline) +# define ATTRIBUTE_NOINLINE __attribute__((noinline)) +# define HAVE_ATTRIBUTE_NOINLINE +# endif +#endif + +#if !defined(HAVE_ATTRIBUTE_NOINLINE) +# if defined(GLOG_OS_WINDOWS) +# define ATTRIBUTE_NOINLINE __declspec(noinline) +# define HAVE_ATTRIBUTE_NOINLINE +# endif +#endif + +#if !defined(HAVE_ATTRIBUTE_NOINLINE) +# define ATTRIBUTE_NOINLINE +#endif + +void AlsoErrorWrite(LogSeverity severity, const char* tag, + const char* message) noexcept; + +const char* ProgramInvocationShortName(); + +int32 GetMainThreadPid(); +bool PidHasChanged(); + +const std::string& MyUserName(); + +// Get the part of filepath after the last path separator. +// (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 InitGoogleLoggingUtilities(const char* argv0); +void ShutdownGoogleLoggingUtilities(); + +template +class ScopedExit final { + public: + template ::value>* = nullptr> + constexpr explicit ScopedExit(F&& functor) noexcept( + std::is_nothrow_constructible::value) + : functor_{std::forward(functor)} {} + ~ScopedExit() noexcept(noexcept(std::declval()())) { functor_(); } + ScopedExit(const ScopedExit& other) = delete; + ScopedExit& operator=(const ScopedExit& other) = delete; + ScopedExit(ScopedExit&& other) noexcept = delete; + ScopedExit& operator=(ScopedExit&& other) noexcept = delete; + + private: + 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(lhs); +} + +constexpr bool operator!=(std::nullptr_t, const FileDescriptor& rhs) noexcept { + return static_cast(rhs); +} + +} // namespace glog_internal_namespace_ + +} // namespace google + +template <> +struct std::default_delete { + void operator()(FILE* p) const noexcept { fclose(p); } +}; + +#endif // GLOG_INTERNAL_UTILITIES_H diff --git a/3rdparty/glog-0.7.0/src/utilities_unittest.cc b/3rdparty/glog-0.7.0/src/utilities_unittest.cc new file mode 100644 index 000000000..18184795a --- /dev/null +++ b/3rdparty/glog-0.7.0/src/utilities_unittest.cc @@ -0,0 +1,52 @@ +// Copyright (c) 2008, 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: Shinichiro Hamaji +#include "utilities.h" + +#include "glog/logging.h" +#include "googletest.h" + +#ifdef GLOG_USE_GFLAGS +# include +using namespace GFLAGS_NAMESPACE; +#endif + +using namespace google; + +TEST(utilities, InitGoogleLoggingDeathTest) { + ASSERT_DEATH(InitGoogleLogging("foobar"), ""); +} + +int main(int argc, char** argv) { + InitGoogleLogging(argv[0]); + InitGoogleTest(&argc, argv); + + CHECK_EQ(RUN_ALL_TESTS(), 0); +} diff --git a/3rdparty/glog-0.7.0/src/vlog_is_on.cc b/3rdparty/glog-0.7.0/src/vlog_is_on.cc new file mode 100644 index 000000000..60dfae660 --- /dev/null +++ b/3rdparty/glog-0.7.0/src/vlog_is_on.cc @@ -0,0 +1,278 @@ +// 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: Ray Sidney and many others +// +// Broken out from logging.cc by Soren Lassen +// logging_unittest.cc covers the functionality herein + +#include +#include +#include +#include +#include +#include + +#include "glog/raw_logging.h" + +// glog doesn't have annotation +#define ANNOTATE_BENIGN_RACE(address, description) + +using std::string; + +namespace google { + +inline namespace glog_internal_namespace_ { + +// 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) { + size_t p = 0; + size_t s = 0; + while (true) { + if (p == patt_len && s == str_len) return true; + if (p == patt_len) return false; + if (s == str_len) return p + 1 == patt_len && pattern[p] == '*'; + if (pattern[p] == str[s] || pattern[p] == '?') { + p += 1; + s += 1; + continue; + } + if (pattern[p] == '*') { + if (p + 1 == patt_len) return true; + do { + if (SafeFNMatch_(pattern + (p + 1), patt_len - (p + 1), str + s, + str_len - s)) { + return true; + } + s += 1; + } while (s != str_len); + return false; + } + return false; + } +} + +} // namespace glog_internal_namespace_ + +using glog_internal_namespace_::SafeFNMatch_; + +// List of per-module log levels from FLAGS_vmodule. +// Once created each element is never deleted/modified +// except for the vlog_level: other threads will read VModuleInfo blobs +// w/o locks and we'll store pointers to vlog_level at VLOG locations +// that will never go away. +// We can't use an STL struct here as we wouldn't know +// when it's safe to delete/update it: other threads need to use it w/o locks. +struct VModuleInfo { + string module_pattern; + mutable int32 vlog_level; // Conceptually this is an AtomicWord, but it's + // too much work to use AtomicWord type here + // w/o much actual benefit. + const VModuleInfo* next; +}; + +// This protects the following global variables. +static std::mutex vmodule_mutex; +// Pointer to head of the VModuleInfo list. +// It's a map from module pattern to logging level for those module(s). +static VModuleInfo* vmodule_list = nullptr; +static SiteFlag* cached_site_list = nullptr; + +// Boolean initialization flag. +static bool inited_vmodule = false; + +// L >= vmodule_mutex. +static void VLOG2Initializer() { + // Can now parse --vmodule flag and initialize mapping of module-specific + // logging levels. + inited_vmodule = false; + const char* vmodule = FLAGS_vmodule.c_str(); + const char* sep; + VModuleInfo* head = nullptr; + VModuleInfo* tail = nullptr; + while ((sep = strchr(vmodule, '=')) != nullptr) { + string pattern(vmodule, static_cast(sep - vmodule)); + int module_level; + if (sscanf(sep, "=%d", &module_level) == 1) { + auto* info = new VModuleInfo; + info->module_pattern = pattern; + info->vlog_level = module_level; + if (head) { + tail->next = info; + } else { + head = info; + } + tail = info; + } + // Skip past this entry + vmodule = strchr(sep, ','); + if (vmodule == nullptr) break; + vmodule++; // Skip past "," + } + if (head) { // Put them into the list at the head: + tail->next = vmodule_list; + vmodule_list = head; + } + inited_vmodule = true; +} + +// This can be called very early, so we use SpinLock and RAW_VLOG here. +int SetVLOGLevel(const char* module_pattern, int log_level) { + int result = FLAGS_v; + size_t const pattern_len = strlen(module_pattern); + bool found = false; + { + std::lock_guard l( + vmodule_mutex); // protect whole read-modify-write + for (const VModuleInfo* info = vmodule_list; info != nullptr; + info = info->next) { + if (info->module_pattern == module_pattern) { + if (!found) { + result = info->vlog_level; + found = true; + } + info->vlog_level = log_level; + } else if (!found && SafeFNMatch_(info->module_pattern.c_str(), + info->module_pattern.size(), + module_pattern, pattern_len)) { + result = info->vlog_level; + found = true; + } + } + if (!found) { + auto* info = new VModuleInfo; + info->module_pattern = module_pattern; + info->vlog_level = log_level; + info->next = vmodule_list; + vmodule_list = info; + + SiteFlag** item_ptr = &cached_site_list; + SiteFlag* item = cached_site_list; + + // We traverse the list fully because the pattern can match several items + // from the list. + while (item) { + if (SafeFNMatch_(module_pattern, pattern_len, item->base_name, + item->base_len)) { + // Redirect the cached value to its module override. + item->level = &info->vlog_level; + *item_ptr = item->next; // Remove the item from the list. + } else { + item_ptr = &item->next; + } + item = *item_ptr; + } + } + } + RAW_VLOG(1, "Set VLOG level for \"%s\" to %d", module_pattern, log_level); + return result; +} + +// NOTE: Individual VLOG statements cache the integer log level pointers. +// NOTE: This function must not allocate memory or require any locks. +bool InitVLOG3__(SiteFlag* site_flag, int32* level_default, const char* fname, + int32 verbose_level) { + std::lock_guard l(vmodule_mutex); + bool read_vmodule_flag = inited_vmodule; + if (!read_vmodule_flag) { + VLOG2Initializer(); + } + + // protect the errno global in case someone writes: + // VLOG(..) << "The last error was " << strerror(errno) + int old_errno = errno; + + // site_default normally points to FLAGS_v + int32* site_flag_value = level_default; + + // Get basename for file + const char* base = strrchr(fname, '/'); + +#ifdef _WIN32 + if (!base) { + base = strrchr(fname, '\\'); + } +#endif + + base = base ? (base + 1) : fname; + const char* base_end = strchr(base, '.'); + size_t base_length = + base_end ? static_cast(base_end - base) : strlen(base); + + // Trim out trailing "-inl" if any + if (base_length >= 4 && (memcmp(base + base_length - 4, "-inl", 4) == 0)) { + base_length -= 4; + } + + // TODO: Trim out _unittest suffix? Perhaps it is better to have + // the extra control and just leave it there. + + // find target in vector of modules, replace site_flag_value with + // a module-specific verbose level, if any. + for (const VModuleInfo* info = vmodule_list; info != nullptr; + info = info->next) { + if (SafeFNMatch_(info->module_pattern.c_str(), info->module_pattern.size(), + base, base_length)) { + site_flag_value = &info->vlog_level; + // value at info->vlog_level is now what controls + // the VLOG at the caller site forever + break; + } + } + + // Cache the vlog value pointer if --vmodule flag has been parsed. + ANNOTATE_BENIGN_RACE(site_flag, + "*site_flag may be written by several threads," + " but the value will be the same"); + if (read_vmodule_flag) { + site_flag->level = site_flag_value; + // If VLOG flag has been cached to the default site pointer, + // we want to add to the cached list in order to invalidate in case + // SetVModule is called afterwards with new modules. + // The performance penalty here is neglible, because InitVLOG3__ is called + // once per site. + if (site_flag_value == level_default && !site_flag->base_name) { + site_flag->base_name = base; + site_flag->base_len = base_length; + site_flag->next = cached_site_list; + cached_site_list = site_flag; + } + } + + // restore the errno in case something recoverable went wrong during + // the initialization of the VLOG mechanism (see above note "protect the..") + errno = old_errno; + return *site_flag_value >= verbose_level; +} + +} // namespace google diff --git a/3rdparty/glog-0.7.0/src/windows/dirent.h b/3rdparty/glog-0.7.0/src/windows/dirent.h new file mode 100644 index 000000000..316726440 --- /dev/null +++ b/3rdparty/glog-0.7.0/src/windows/dirent.h @@ -0,0 +1,1040 @@ +/* + * Dirent interface for Microsoft Visual Studio + * + * Copyright (C) 1998-2019 Toni Ronkko + * This file is part of dirent. Dirent may be freely distributed + * 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 + +/* Hide warnings about unreferenced local functions */ +#if defined(__clang__) +# pragma clang diagnostic ignored "-Wunused-function" +#elif defined(_MSC_VER) +# pragma warning(disable : 4505) +#elif defined(__GNUC__) +# pragma GCC diagnostic ignored "-Wunused-function" +#endif + +/* + * Include windows.h without Windows Sockets 1.1 to prevent conflicts with + * Windows Sockets 2.0. + */ +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/* Indicates that d_type field is available in dirent structure */ +#define _DIRENT_HAVE_D_TYPE + +/* Indicates that d_namlen field is available in dirent structure */ +#define _DIRENT_HAVE_D_NAMLEN + +/* Entries missing from MSVC 6.0 */ +#if !defined(FILE_ATTRIBUTE_DEVICE) +# define FILE_ATTRIBUTE_DEVICE 0x40 +#endif + +/* File type and permission flags for stat(), general mask */ +#if !defined(S_IFMT) +# define S_IFMT _S_IFMT +#endif + +/* Directory bit */ +#if !defined(S_IFDIR) +# define S_IFDIR _S_IFDIR +#endif + +/* Character device bit */ +#if !defined(S_IFCHR) +# define S_IFCHR _S_IFCHR +#endif + +/* Pipe bit */ +#if !defined(S_IFFIFO) +# define S_IFFIFO _S_IFFIFO +#endif + +/* Regular file bit */ +#if !defined(S_IFREG) +# define S_IFREG _S_IFREG +#endif + +/* Read permission */ +#if !defined(S_IREAD) +# define S_IREAD _S_IREAD +#endif + +/* Write permission */ +#if !defined(S_IWRITE) +# define S_IWRITE _S_IWRITE +#endif + +/* Execute permission */ +#if !defined(S_IEXEC) +# define S_IEXEC _S_IEXEC +#endif + +/* Pipe */ +#if !defined(S_IFIFO) +# define S_IFIFO _S_IFIFO +#endif + +/* Block device */ +#if !defined(S_IFBLK) +# define S_IFBLK 0 +#endif + +/* Link */ +#if !defined(S_IFLNK) +# define S_IFLNK 0 +#endif + +/* Socket */ +#if !defined(S_IFSOCK) +# define S_IFSOCK 0 +#endif + +/* Read user permission */ +#if !defined(S_IRUSR) +# define S_IRUSR S_IREAD +#endif + +/* Write user permission */ +#if !defined(S_IWUSR) +# define S_IWUSR S_IWRITE +#endif + +/* Execute user permission */ +#if !defined(S_IXUSR) +# define S_IXUSR 0 +#endif + +/* Read group permission */ +#if !defined(S_IRGRP) +# define S_IRGRP 0 +#endif + +/* Write group permission */ +#if !defined(S_IWGRP) +# define S_IWGRP 0 +#endif + +/* Execute group permission */ +#if !defined(S_IXGRP) +# define S_IXGRP 0 +#endif + +/* Read others permission */ +#if !defined(S_IROTH) +# define S_IROTH 0 +#endif + +/* Write others permission */ +#if !defined(S_IWOTH) +# define S_IWOTH 0 +#endif + +/* Execute others permission */ +#if !defined(S_IXOTH) +# define S_IXOTH 0 +#endif + +/* Maximum length of file name */ +#if !defined(PATH_MAX) +# define PATH_MAX MAX_PATH +#endif +#if !defined(FILENAME_MAX) +# define FILENAME_MAX MAX_PATH +#endif +#if !defined(NAME_MAX) +# define NAME_MAX FILENAME_MAX +#endif + +/* File type flags for d_type */ +#define DT_UNKNOWN 0 +#define DT_REG S_IFREG +#define DT_DIR S_IFDIR +#define DT_FIFO S_IFIFO +#define DT_SOCK S_IFSOCK +#define DT_CHR S_IFCHR +#define DT_BLK S_IFBLK +#define DT_LNK S_IFLNK + +/* Macros for converting between st_mode and d_type */ +#define IFTODT(mode) ((mode)&S_IFMT) +#define DTTOIF(type) (type) + +/* + * File type macros. Note that block devices, sockets and links cannot be + * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are + * only defined for compatibility. These macros should always return false + * on Windows. + */ +#if !defined(S_ISFIFO) +# define S_ISFIFO(mode) (((mode)&S_IFMT) == S_IFIFO) +#endif +#if !defined(S_ISDIR) +# define S_ISDIR(mode) (((mode)&S_IFMT) == S_IFDIR) +#endif +#if !defined(S_ISREG) +# define S_ISREG(mode) (((mode)&S_IFMT) == S_IFREG) +#endif +#if !defined(S_ISLNK) +# define S_ISLNK(mode) (((mode)&S_IFMT) == S_IFLNK) +#endif +#if !defined(S_ISSOCK) +# define S_ISSOCK(mode) (((mode)&S_IFMT) == S_IFSOCK) +#endif +#if !defined(S_ISCHR) +# define S_ISCHR(mode) (((mode)&S_IFMT) == S_IFCHR) +#endif +#if !defined(S_ISBLK) +# define S_ISBLK(mode) (((mode)&S_IFMT) == S_IFBLK) +#endif + +/* Return the exact length of the file name without zero terminator */ +#define _D_EXACT_NAMLEN(p) ((p)->d_namlen) + +/* Return the maximum size of a file name */ +#define _D_ALLOC_NAMLEN(p) ((PATH_MAX) + 1) + +#ifdef __cplusplus +extern "C" { +#endif + +/* Wide-character version */ +struct _wdirent { + /* Always zero */ + long d_ino; + + /* File position within stream */ + long d_off; + + /* Structure size */ + unsigned short d_reclen; + + /* Length of name without \0 */ + size_t d_namlen; + + /* File type */ + int d_type; + + /* File name */ + wchar_t d_name[PATH_MAX + 1]; +}; + +struct _WDIR { + /* Current directory entry */ + _wdirent ent; + + /* Private file data */ + WIN32_FIND_DATAW data; + + /* True if data is valid */ + int cached; + + /* Win32 search handle */ + HANDLE handle; + + /* Initial directory name */ + wchar_t* patt; +}; + +/* Multi-byte character version */ +struct dirent { + /* Always zero */ + long d_ino; + + /* File position within stream */ + long d_off; + + /* Structure size */ + unsigned short d_reclen; + + /* Length of name without \0 */ + size_t d_namlen; + + /* File type */ + int d_type; + + /* File name */ + char d_name[PATH_MAX + 1]; +}; + +struct DIR { + struct dirent ent; + struct _WDIR* wdirp; +}; + +/* Dirent functions */ +static DIR* opendir(const char* dirname); +static _WDIR* _wopendir(const wchar_t* dirname); + +static struct dirent* readdir(DIR* dirp); +static struct _wdirent* _wreaddir(_WDIR* dirp); + +static int readdir_r(DIR* dirp, struct dirent* entry, struct dirent** result); +static int _wreaddir_r(_WDIR* dirp, struct _wdirent* entry, + struct _wdirent** result); + +static int closedir(DIR* dirp); +static int _wclosedir(_WDIR* dirp); + +static void rewinddir(DIR* dirp); +static void _wrewinddir(_WDIR* dirp); + +static int scandir(const char* dirname, struct dirent*** namelist, + int (*filter)(const struct dirent*), + int (*compare)(const struct dirent**, + const struct dirent**)); + +static int alphasort(const struct dirent** a, const struct dirent** b); + +static int versionsort(const struct dirent** a, const struct dirent** b); + +/* For compatibility with Symbian */ +#define wdirent _wdirent +#define WDIR _WDIR +#define wopendir _wopendir +#define wreaddir _wreaddir +#define wclosedir _wclosedir +#define wrewinddir _wrewinddir + +/* Internal utility functions */ +static WIN32_FIND_DATAW* dirent_first(_WDIR* dirp); +static WIN32_FIND_DATAW* dirent_next(_WDIR* dirp); + +static int dirent_mbstowcs_s(size_t* pReturnValue, wchar_t* wcstr, + size_t sizeInWords, const char* mbstr, + size_t count); + +static int dirent_wcstombs_s(size_t* pReturnValue, char* mbstr, + size_t sizeInBytes, const wchar_t* wcstr, + size_t count); + +static void dirent_set_errno(int error); + +/* + * Open directory stream DIRNAME for read and return a pointer to the + * internal working area that is used to retrieve individual directory + * entries. + */ +static _WDIR* _wopendir(const wchar_t* dirname) { + _WDIR* dirp; + DWORD n; + wchar_t* p; + + /* Must have directory name */ + if (dirname == nullptr || dirname[0] == '\0') { + dirent_set_errno(ENOENT); + return nullptr; + } + + /* Allocate new _WDIR structure */ + dirp = (_WDIR*)malloc(sizeof(struct _WDIR)); + if (!dirp) { + return nullptr; + } + + /* Reset _WDIR structure */ + dirp->handle = INVALID_HANDLE_VALUE; + dirp->patt = nullptr; + dirp->cached = 0; + + /* + * Compute the length of full path plus zero terminator + * + * Note that on WinRT there's no way to convert relative paths + * into absolute paths, so just assume it is an absolute path. + */ +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + /* Desktop */ + n = GetFullPathNameW(dirname, 0, nullptr, nullptr); +#else + /* WinRT */ + n = wcslen(dirname); +#endif + + /* Allocate room for absolute directory name and search pattern */ + dirp->patt = (wchar_t*)malloc(sizeof(wchar_t) * n + 16); + if (dirp->patt == nullptr) { + goto exit_closedir; + } + + /* + * Convert relative directory name to an absolute one. This + * allows rewinddir() to function correctly even when current + * working directory is changed between opendir() and rewinddir(). + * + * Note that on WinRT there's no way to convert relative paths + * into absolute paths, so just assume it is an absolute path. + */ +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + /* Desktop */ + n = GetFullPathNameW(dirname, n, dirp->patt, nullptr); + if (n <= 0) { + goto exit_closedir; + } +#else + /* WinRT */ + wcsncpy_s(dirp->patt, n + 1, dirname, n); +#endif + + /* Append search pattern \* to the directory name */ + p = dirp->patt + n; + switch (p[-1]) { + case '\\': + case '/': + case ':': + /* Directory ends in path separator, e.g. c:\temp\ */ + /*NOP*/; + break; + + default: + /* Directory name doesn't end in path separator */ + *p++ = '\\'; + } + *p++ = '*'; + *p = '\0'; + + /* Open directory stream and retrieve the first entry */ + if (!dirent_first(dirp)) { + goto exit_closedir; + } + + /* Success */ + return dirp; + + /* Failure */ +exit_closedir: + _wclosedir(dirp); + return nullptr; +} + +/* + * Read next directory entry. + * + * Returns pointer to static directory entry which may be overwritten by + * subsequent calls to _wreaddir(). + */ +static struct _wdirent* _wreaddir(_WDIR* dirp) { + struct _wdirent* entry; + + /* + * Read directory entry to buffer. We can safely ignore the return value + * as entry will be set to nullptr in case of error. + */ + (void)_wreaddir_r(dirp, &dirp->ent, &entry); + + /* Return pointer to statically allocated directory entry */ + return entry; +} + +/* + * Read next directory entry. + * + * Returns zero on success. If end of directory stream is reached, then sets + * result to nullptr and returns zero. + */ +static int _wreaddir_r(_WDIR* dirp, struct _wdirent* entry, + struct _wdirent** result) { + WIN32_FIND_DATAW* datap; + + /* Read next directory entry */ + datap = dirent_next(dirp); + if (datap) { + size_t n; + DWORD attr; + + /* + * Copy file name as wide-character string. If the file name is too + * long to fit in to the destination buffer, then truncate file name + * to PATH_MAX characters and zero-terminate the buffer. + */ + n = 0; + while (n < PATH_MAX && datap->cFileName[n] != 0) { + entry->d_name[n] = datap->cFileName[n]; + n++; + } + entry->d_name[n] = 0; + + /* Length of file name excluding zero terminator */ + entry->d_namlen = n; + + /* File type */ + attr = datap->dwFileAttributes; + if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { + entry->d_type = DT_CHR; + } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { + entry->d_type = DT_DIR; + } else { + entry->d_type = DT_REG; + } + + /* Reset dummy fields */ + entry->d_ino = 0; + entry->d_off = 0; + entry->d_reclen = sizeof(struct _wdirent); + + /* Set result address */ + *result = entry; + + } else { + /* Return nullptr to indicate end of directory */ + *result = nullptr; + } + + return /*OK*/ 0; +} + +/* + * Close directory stream opened by opendir() function. This invalidates the + * DIR structure as well as any directory entry read previously by + * _wreaddir(). + */ +static int _wclosedir(_WDIR* dirp) { + int ok; + if (dirp) { + /* Release search handle */ + if (dirp->handle != INVALID_HANDLE_VALUE) { + FindClose(dirp->handle); + } + + /* Release search pattern */ + free(dirp->patt); + + /* Release directory structure */ + free(dirp); + ok = /*success*/ 0; + + } else { + /* Invalid directory stream */ + dirent_set_errno(EBADF); + ok = /*failure*/ -1; + } + return ok; +} + +/* + * Rewind directory stream such that _wreaddir() returns the very first + * file name again. + */ +static void _wrewinddir(_WDIR* dirp) { + if (dirp) { + /* Release existing search handle */ + if (dirp->handle != INVALID_HANDLE_VALUE) { + FindClose(dirp->handle); + } + + /* Open new search handle */ + dirent_first(dirp); + } +} + +/* Get first directory entry (internal) */ +static WIN32_FIND_DATAW* dirent_first(_WDIR* dirp) { + WIN32_FIND_DATAW* datap; + DWORD error; + + /* Open directory and retrieve the first entry */ + dirp->handle = FindFirstFileExW(dirp->patt, FindExInfoStandard, &dirp->data, + FindExSearchNameMatch, nullptr, 0); + if (dirp->handle != INVALID_HANDLE_VALUE) { + /* a directory entry is now waiting in memory */ + datap = &dirp->data; + dirp->cached = 1; + + } else { + /* Failed to open directory: no directory entry in memory */ + dirp->cached = 0; + datap = nullptr; + + /* Set error code */ + error = GetLastError(); + switch (error) { + case ERROR_ACCESS_DENIED: + /* No read access to directory */ + dirent_set_errno(EACCES); + break; + + case ERROR_DIRECTORY: + /* Directory name is invalid */ + dirent_set_errno(ENOTDIR); + break; + + case ERROR_PATH_NOT_FOUND: + default: + /* Cannot find the file */ + dirent_set_errno(ENOENT); + } + } + return datap; +} + +/* + * Get next directory entry (internal). + * + * Returns + */ +static WIN32_FIND_DATAW* dirent_next(_WDIR* dirp) { + WIN32_FIND_DATAW* p; + + /* Get next directory entry */ + if (dirp->cached != 0) { + /* A valid directory entry already in memory */ + p = &dirp->data; + dirp->cached = 0; + + } else if (dirp->handle != INVALID_HANDLE_VALUE) { + /* Get the next directory entry from stream */ + if (FindNextFileW(dirp->handle, &dirp->data) != FALSE) { + /* Got a file */ + p = &dirp->data; + } else { + /* The very last entry has been processed or an error occurred */ + FindClose(dirp->handle); + dirp->handle = INVALID_HANDLE_VALUE; + p = nullptr; + } + + } else { + /* End of directory stream reached */ + p = nullptr; + } + + return p; +} + +/* + * Open directory stream using plain old C-string. + */ +static DIR* opendir(const char* dirname) { + /* Must have directory name */ + if (dirname == nullptr || dirname[0] == '\0') { + dirent_set_errno(ENOENT); + return nullptr; + } + + /* Allocate memory for DIR structure */ + std::unique_ptr dirp{ + static_cast(malloc(sizeof(struct DIR))), &free}; + + if (!dirp) { + return nullptr; + } + { + int error; + wchar_t wname[PATH_MAX + 1]; + size_t n; + + /* Convert directory name to wide-character string */ + error = dirent_mbstowcs_s(&n, wname, PATH_MAX + 1, dirname, PATH_MAX + 1); + if (error) { + /* + * Cannot convert file name to wide-character string. This + * occurs if the string contains invalid multi-byte sequences or + * the output buffer is too small to contain the resulting + * string. + */ + return nullptr; + } + + /* Open directory stream using wide-character name */ + dirp->wdirp = _wopendir(wname); + if (!dirp->wdirp) { + return nullptr; + } + } + + /* Success */ + return dirp.release(); +} + +/* + * Read next directory entry. + */ +static struct dirent* readdir(DIR* dirp) { + struct dirent* entry; + + /* + * Read directory entry to buffer. We can safely ignore the return value + * as entry will be set to nullptr in case of error. + */ + (void)readdir_r(dirp, &dirp->ent, &entry); + + /* Return pointer to statically allocated directory entry */ + return entry; +} + +/* + * Read next directory entry into called-allocated buffer. + * + * Returns zero on success. If the end of directory stream is reached, then + * sets result to nullptr and returns zero. + */ +static int readdir_r(DIR* dirp, struct dirent* entry, struct dirent** result) { + WIN32_FIND_DATAW* datap; + + /* Read next directory entry */ + datap = dirent_next(dirp->wdirp); + if (datap) { + size_t n; + int error; + + /* Attempt to convert file name to multi-byte string */ + error = dirent_wcstombs_s(&n, entry->d_name, PATH_MAX + 1, datap->cFileName, + PATH_MAX + 1); + + /* + * If the file name cannot be represented by a multi-byte string, + * then attempt to use old 8+3 file name. This allows traditional + * Unix-code to access some file names despite of unicode + * characters, although file names may seem unfamiliar to the user. + * + * Be ware that the code below cannot come up with a short file + * name unless the file system provides one. At least + * VirtualBox shared folders fail to do this. + */ + if (error && datap->cAlternateFileName[0] != '\0') { + error = dirent_wcstombs_s(&n, entry->d_name, PATH_MAX + 1, + datap->cAlternateFileName, PATH_MAX + 1); + } + + if (!error) { + DWORD attr; + + /* Length of file name excluding zero terminator */ + entry->d_namlen = n - 1; + + /* File attributes */ + attr = datap->dwFileAttributes; + if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { + entry->d_type = DT_CHR; + } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { + entry->d_type = DT_DIR; + } else { + entry->d_type = DT_REG; + } + + /* Reset dummy fields */ + entry->d_ino = 0; + entry->d_off = 0; + entry->d_reclen = sizeof(struct dirent); + + } else { + /* + * Cannot convert file name to multi-byte string so construct + * an erroneous directory entry and return that. Note that + * we cannot return nullptr as that would stop the processing + * of directory entries completely. + */ + entry->d_name[0] = '?'; + entry->d_name[1] = '\0'; + entry->d_namlen = 1; + entry->d_type = DT_UNKNOWN; + entry->d_ino = 0; + entry->d_off = -1; + entry->d_reclen = 0; + } + + /* Return pointer to directory entry */ + *result = entry; + + } else { + /* No more directory entries */ + *result = nullptr; + } + + return /*OK*/ 0; +} + +/* + * Close directory stream. + */ +static int closedir(DIR* dirp) { + int ok; + if (dirp) { + /* Close wide-character directory stream */ + ok = _wclosedir(dirp->wdirp); + dirp->wdirp = nullptr; + + /* Release multi-byte character version */ + free(dirp); + + } else { + /* Invalid directory stream */ + dirent_set_errno(EBADF); + ok = /*failure*/ -1; + } + return ok; +} + +/* + * Rewind directory stream to beginning. + */ +static void rewinddir(DIR* dirp) { + /* Rewind wide-character string directory stream */ + _wrewinddir(dirp->wdirp); +} + +/* + * Scan directory for entries. + */ +static int scandir(const char* dirname, struct dirent*** namelist, + int (*filter)(const struct dirent*), + int (*compare)(const struct dirent**, + const struct dirent**)) { + struct dirent** files = nullptr; + size_t size = 0; + size_t allocated = 0; + const size_t init_size = 1; + DIR* dir = nullptr; + struct dirent* entry; + struct dirent* tmp = nullptr; + size_t i; + int result = 0; + + /* Open directory stream */ + dir = opendir(dirname); + if (dir) { + /* Read directory entries to memory */ + while (1) { + /* Enlarge pointer table to make room for another pointer */ + if (size >= allocated) { + void* p; + size_t num_entries; + + /* Compute number of entries in the enlarged pointer table */ + if (size < init_size) { + /* Allocate initial pointer table */ + num_entries = init_size; + } else { + /* Double the size */ + num_entries = size * 2; + } + + /* Allocate first pointer table or enlarge existing table */ + p = realloc(files, sizeof(void*) * num_entries); + if (p != nullptr) { + /* Got the memory */ + files = (dirent**)p; + allocated = num_entries; + } else { + /* Out of memory */ + result = -1; + break; + } + } + + /* Allocate room for temporary directory entry */ + if (tmp == nullptr) { + tmp = (struct dirent*)malloc(sizeof(struct dirent)); + if (tmp == nullptr) { + /* Cannot allocate temporary directory entry */ + result = -1; + break; + } + } + + /* Read directory entry to temporary area */ + if (readdir_r(dir, tmp, &entry) == /*OK*/ 0) { + /* Did we get an entry? */ + if (entry != nullptr) { + int pass; + + /* Determine whether to include the entry in result */ + if (filter) { + /* Let the filter function decide */ + pass = filter(tmp); + } else { + /* No filter function, include everything */ + pass = 1; + } + + if (pass) { + /* Store the temporary entry to pointer table */ + files[size++] = tmp; + tmp = nullptr; + + /* Keep up with the number of files */ + result++; + } + + } else { + /* + * End of directory stream reached => sort entries and + * exit. + */ + qsort(files, size, sizeof(void*), + (int (*)(const void*, const void*))compare); + break; + } + + } else { + /* Error reading directory entry */ + result = /*Error*/ -1; + break; + } + } + + } else { + /* Cannot open directory */ + result = /*Error*/ -1; + } + + /* Release temporary directory entry */ + free(tmp); + + /* Release allocated memory on error */ + if (result < 0) { + for (i = 0; i < size; i++) { + free(files[i]); + } + free(files); + files = nullptr; + } + + /* Close directory stream */ + if (dir) { + closedir(dir); + } + + /* Pass pointer table to caller */ + if (namelist) { + *namelist = files; + } + return result; +} + +/* Alphabetical sorting */ +static int alphasort(const struct dirent** a, const struct dirent** b) { + return strcoll((*a)->d_name, (*b)->d_name); +} + +/* Sort versions */ +static int versionsort(const struct dirent** a, const struct dirent** b) { + /* FIXME: implement strverscmp and use that */ + return alphasort(a, b); +} + +/* Convert multi-byte string to wide character string */ +static int dirent_mbstowcs_s(size_t* pReturnValue, wchar_t* wcstr, + size_t sizeInWords, const char* mbstr, + size_t count) { + int error; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 or later */ + error = mbstowcs_s(pReturnValue, wcstr, sizeInWords, mbstr, count); + +#else + + /* Older Visual Studio or non-Microsoft compiler */ + size_t n; + + /* Convert to wide-character string (or count characters) */ + n = mbstowcs(wcstr, mbstr, sizeInWords); + if (!wcstr || n < count) { + /* Zero-terminate output buffer */ + if (wcstr && sizeInWords) { + if (n >= sizeInWords) { + n = sizeInWords - 1; + } + wcstr[n] = 0; + } + + /* Length of resulting multi-byte string WITH zero terminator */ + if (pReturnValue) { + *pReturnValue = n + 1; + } + + /* Success */ + error = 0; + + } else { + /* Could not convert string */ + error = 1; + } + +#endif + return error; +} + +/* Convert wide-character string to multi-byte string */ +static int dirent_wcstombs_s(size_t* pReturnValue, char* mbstr, + size_t sizeInBytes, /* max size of mbstr */ + const wchar_t* wcstr, size_t count) { + int error; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 or later */ + error = wcstombs_s(pReturnValue, mbstr, sizeInBytes, wcstr, count); + +#else + + /* Older Visual Studio or non-Microsoft compiler */ + size_t n; + + /* Convert to multi-byte string (or count the number of bytes needed) */ + n = wcstombs(mbstr, wcstr, sizeInBytes); + if (!mbstr || n < count) { + /* Zero-terminate output buffer */ + if (mbstr && sizeInBytes) { + if (n >= sizeInBytes) { + n = sizeInBytes - 1; + } + mbstr[n] = '\0'; + } + + /* Length of resulting multi-bytes string WITH zero-terminator */ + if (pReturnValue) { + *pReturnValue = n + 1; + } + + /* Success */ + error = 0; + + } else { + /* Cannot convert string */ + error = 1; + } + +#endif + return error; +} + +/* Set errno variable */ +static void dirent_set_errno(int error) { +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 and later */ + _set_errno(error); + +#else + + /* Non-Microsoft compiler or older Microsoft compiler */ + errno = error; + +#endif +} + +#ifdef __cplusplus +} +#endif +#endif /*GLOG_INTERNAL_WINDOWS_DIRENT_H*/ diff --git a/3rdparty/glog-0.7.0/src/windows/port.cc b/3rdparty/glog-0.7.0/src/windows/port.cc new file mode 100644 index 000000000..35e556a32 --- /dev/null +++ b/3rdparty/glog-0.7.0/src/windows/port.cc @@ -0,0 +1,62 @@ +/* Copyright (c) 2023, 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: Craig Silverstein + * Copied from google-perftools and modified by Shinichiro Hamaji + */ + +#ifndef _WIN32 +# error You should only be including windows/port.cc in a windows environment! +#endif + +#include "port.h" + +#include + +#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); + return result; +} +#endif // not HAVE_LOCALTIME_R +#ifndef HAVE_GMTIME_R +struct tm* gmtime_r(const std::time_t* timep, std::tm* result) { + gmtime_s(result, timep); + return result; +} +#endif // not HAVE_GMTIME_R + +} // namespace glog_internal_namespace_ +} // namespace google diff --git a/3rdparty/glog-0.7.0/src/windows/port.h b/3rdparty/glog-0.7.0/src/windows/port.h new file mode 100644 index 000000000..8344e5ae8 --- /dev/null +++ b/3rdparty/glog-0.7.0/src/windows/port.h @@ -0,0 +1,142 @@ +/* Copyright (c) 2023, 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: Craig Silverstein + * Copied from google-perftools and modified by Shinichiro Hamaji + * + * These are some portability typedefs and defines to make it a bit + * easier to compile this code under VC++. + * + * Several of these are taken from glib: + * http://developer.gnome.org/doc/API/glib/glib-windows-compatability-functions.html + */ + +#ifndef CTEMPLATE_WINDOWS_PORT_H_ +#define CTEMPLATE_WINDOWS_PORT_H_ + +#include "config.h" + +#if defined(GLOG_USE_GLOG_EXPORT) +# include "glog/export.h" +#endif + +#if !defined(GLOG_EXPORT) +# error "port.h" was not included correctly. +#endif + +#ifdef _WIN32 + +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN /* We always want minimal includes */ +# endif + +# include /* for _getcwd() */ +# include /* because we so often use open/close/etc */ +# include /* for _getpid() */ +# include +# include /* for gethostname */ + +# include /* template_dictionary.cc uses va_copy */ +# include /* for _strnicmp(), strerror_s() */ +# include /* for localtime_s() */ +/* Note: the C++ #includes are all together at the bottom. This file is + * used by both C and C++ code, so we put all the C++ together. + */ + +# ifdef _MSC_VER + +/* 4244: otherwise we get problems when substracting two size_t's to an int + * 4251: it's complaining about a private struct I've chosen not to dllexport + * 4355: we use this in a constructor, but we do it safely + * 4715: for some reason VC++ stopped realizing you can't return after abort() + * 4800: we know we're casting ints/char*'s to bools, and we're ok with that + * 4996: Yes, we're ok using "unsafe" functions like fopen() and strerror() + * 4312: Converting uint32_t to a pointer when testing %p + * 4267: also subtracting two size_t to int + * 4722: Destructor never returns due to abort() + */ +# pragma warning(disable : 4244 4251 4355 4715 4800 4996 4267 4312 4722) + +/* file I/O */ +# define PATH_MAX 1024 +# define popen _popen +# define pclose _pclose +# define R_OK 04 /* read-only (for access()) */ +# define S_ISDIR(m) (((m)&_S_IFMT) == _S_IFDIR) + +# define O_WRONLY _O_WRONLY +# define O_CREAT _O_CREAT +# define O_EXCL _O_EXCL + +# define S_IRUSR S_IREAD +# define S_IWUSR S_IWRITE + +/* Not quite as lightweight as a hard-link, but more than good enough for us. */ +# define link(oldpath, newpath) CopyFileA(oldpath, newpath, false) + +# define strcasecmp _stricmp +# define strncasecmp _strnicmp + +/* In windows-land, hash<> is called hash_compare<> (from xhash.h) */ +/* VC11 provides std::hash */ +# if defined(_MSC_VER) && (_MSC_VER < 1700) +# define hash hash_compare +# endif + +/* Windows doesn't support specifying the number of buckets as a + * hash_map constructor arg, so we leave this blank. + */ +# define CTEMPLATE_SMALL_HASHTABLE + +# define DEFAULT_TEMPLATE_ROOTDIR ".." + +# 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); +# endif // not HAVE_LOCALTIME_R + +# ifndef HAVE_GMTIME_R +GLOG_NO_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 + +#endif /* _WIN32 */ + +#endif /* CTEMPLATE_WINDOWS_PORT_H_ */ diff --git a/configure b/configure index d17c2c3cc..a3d275126 100755 --- a/configure +++ b/configure @@ -240,7 +240,7 @@ if ! [[ "$SPEC" =~ "wasm" ]]; then if [ -z "$ENABLE_SHARED_GLOG" ]; then echo echo -n "Compiling glog ... " - $CMAKE -S . -B build -G "Unix Makefiles" >/dev/null 2>&1 + $CMAKE -S . -B build -G "Unix Makefiles" -DBUILD_SHARED_LIBS=OFF -DCMAKE_CXX_STANDARD=14 >/dev/null 2>&1 make -j${NPROC} -C build >/dev/null 2>&1 RET=$? if [ $RET != 0 ]; then diff --git a/configure.bat b/configure.bat index 7a90dae0e..61e8553e2 100644 --- a/configure.bat +++ b/configure.bat @@ -5,7 +5,7 @@ set VERSION=2.8.0 set TFDIR=C:\TreeFrog\%VERSION% set MONBOC_VERSION=1.21.2 set LZ4_VERSION=1.9.4 -set GLOG_VERSION=0.6.0 +set GLOG_VERSION=0.7.0 set BASEDIR=%~dp0 set CL=/MP diff --git a/src/test/queue/queue.pro b/src/test/queue/queue.pro index 846b8e54b..756c25a11 100644 --- a/src/test/queue/queue.pro +++ b/src/test/queue/queue.pro @@ -1,3 +1,4 @@ include(../test.pri) TARGET = queue -SOURCES += main.cpp +SOURCES = main.cpp +DEFINES += GLOG_USE_GLOG_EXPORT diff --git a/src/test/stack/stack.pro b/src/test/stack/stack.pro index 00411b6e3..f156f16c2 100644 --- a/src/test/stack/stack.pro +++ b/src/test/stack/stack.pro @@ -1,3 +1,4 @@ include(../test.pri) TARGET = stack SOURCES = main.cpp +DEFINES += GLOG_USE_GLOG_EXPORT diff --git a/tools/tfserver/tfserver.pro b/tools/tfserver/tfserver.pro index bb718bce6..7b24d432e 100644 --- a/tools/tfserver/tfserver.pro +++ b/tools/tfserver/tfserver.pro @@ -14,7 +14,7 @@ lessThan(QT_MAJOR_VERSION, 6) { } DEFINES *= QT_USE_QSTRINGBUILDER -DEFINES += TF_DLL +DEFINES += TF_DLL GLOG_USE_GLOG_EXPORT INCLUDEPATH += $$header.path include(../../tfbase.pri)