Compare commits
No commits in common. "main" and "v0.1.1" have entirely different histories.
@ -1 +0,0 @@
|
|||||||
USE_BAZEL_VERSION=7.2.1
|
|
||||||
6
.clang-tidy
Normal file
6
.clang-tidy
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
Checks: '-*,clang-diagnostic-*,clang-analyzer-*,bugprone-*,cert-*,clang-analyzer-*,concurrency-*,cppcoreguidelines-*,misc-*,modernize-*,performance-*,portability-*,readability-*,-cppcoreguidelines-macro-usage,-modernize-use-trailing-return-type,-misc-non-private-member-variables-in-classes,-cppcoreguidelines-pro-type-reinterpret-cast,-cppcoreguidelines-pro-type-vararg,-cppcoreguidelines-avoid-c-arrays,-modernize-avoid-c-arrays,-cppcoreguidelines-pro-bounds-pointer-arithmetic,-readability-else-after-return,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-cert-dcl50-cpp,-cppcoreguidelines-init-variables,-readability-implicit-bool-conversion,-cppcoreguidelines-pro-bounds-constant-array-index,-cppcoreguidelines-owning-memory,-cppcoreguidelines-pro-type-member-init,-readability-isolate-declaration,-cppcoreguidelines-avoid-magic-numbers,-readability-magic-numbers,-cppcoreguidelines-pro-type-union-access,-cppcoreguidelines-pro-type-cstyle-cast,-readability-named-parameter,-cppcoreguidelines-avoid-goto,-readability-uppercase-literal-suffix,-performance-avoid-endl,-bugprone-easily-swappable-parameters'
|
||||||
|
WarningsAsErrors: '*'
|
||||||
|
HeaderFilterRegex: ''
|
||||||
|
AnalyzeTemporaryDtors: false
|
||||||
|
FormatStyle: none
|
||||||
55
.github/workflows/build.yml
vendored
Normal file
55
.github/workflows/build.yml
vendored
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
name: build
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-linux:
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: dependencies
|
||||||
|
run: sudo apt install gcc-10 g++-10 libgcc-10-dev
|
||||||
|
- name: build
|
||||||
|
run: |
|
||||||
|
pip3 install colorama
|
||||||
|
python3 ci/build-in-all-configs.py
|
||||||
|
build-macos:
|
||||||
|
runs-on: macos-13
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: build
|
||||||
|
run: |
|
||||||
|
pip3 install colorama
|
||||||
|
python3 ci/build-in-all-configs.py
|
||||||
|
build-windows-msvc:
|
||||||
|
runs-on: windows-2019
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Enable Developer Command Prompt
|
||||||
|
uses: ilammy/msvc-dev-cmd@v1.10.0
|
||||||
|
- name: build
|
||||||
|
run: |
|
||||||
|
pip3 install colorama
|
||||||
|
python3 ci/build-in-all-configs.py --msvc-only
|
||||||
|
build-windows-clang:
|
||||||
|
runs-on: windows-2019
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Enable Developer Command Prompt
|
||||||
|
uses: ilammy/msvc-dev-cmd@v1.10.0
|
||||||
|
- name: build
|
||||||
|
run: |
|
||||||
|
pip3 install colorama
|
||||||
|
python3 ci/build-in-all-configs.py --clang-only
|
||||||
|
build-windows-mingw:
|
||||||
|
runs-on: windows-2019
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Enable Developer Command Prompt
|
||||||
|
uses: ilammy/msvc-dev-cmd@v1.10.0
|
||||||
|
- name: build
|
||||||
|
run: |
|
||||||
|
pip3 install colorama
|
||||||
|
python3 ci/build-in-all-configs.py --mingw-only
|
||||||
652
.github/workflows/ci.yml
vendored
652
.github/workflows/ci.yml
vendored
@ -1,652 +0,0 @@
|
|||||||
name: ci
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
pull_request:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
test-linux:
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
compiler: [gcc, clang]
|
|
||||||
shared: [--shared, ""]
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: dependencies
|
|
||||||
run: |
|
|
||||||
sudo apt install gcc-10 g++-10 libgcc-10-dev libunwind8-dev
|
|
||||||
pip3 install colorama
|
|
||||||
- name: libdwarf
|
|
||||||
run: |
|
|
||||||
cd ..
|
|
||||||
cpptrace/ci/setup-prerequisites.sh
|
|
||||||
- name: build and test
|
|
||||||
run: |
|
|
||||||
python3 ci/test-all-configs.py --${{matrix.compiler}} --default-config
|
|
||||||
test-linux-arm:
|
|
||||||
runs-on: ubuntu-22.04-arm
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
compiler: [gcc, clang]
|
|
||||||
shared: [--shared, ""]
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: dependencies
|
|
||||||
run: |
|
|
||||||
sudo apt install gcc-10 g++-10 libgcc-10-dev libunwind8-dev
|
|
||||||
pip3 install colorama
|
|
||||||
- name: libdwarf
|
|
||||||
run: |
|
|
||||||
cd ..
|
|
||||||
cpptrace/ci/setup-prerequisites.sh
|
|
||||||
- name: build and test
|
|
||||||
run: |
|
|
||||||
python3 ci/test-all-configs.py --${{matrix.compiler}} --default-config
|
|
||||||
test-macos:
|
|
||||||
runs-on: macos-14
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
compiler: [gcc, clang]
|
|
||||||
shared: [--shared, ""]
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: libdwarf
|
|
||||||
run: |
|
|
||||||
cd ..
|
|
||||||
cpptrace/ci/setup-prerequisites.sh
|
|
||||||
- name: dependencies
|
|
||||||
run: |
|
|
||||||
python3 -m venv env
|
|
||||||
env/bin/pip install colorama
|
|
||||||
- name: build and test
|
|
||||||
run: |
|
|
||||||
env/bin/python ci/test-all-configs.py --${{matrix.compiler}} --default-config
|
|
||||||
test-windows:
|
|
||||||
runs-on: windows-2022
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
compiler: [msvc, clang, gcc]
|
|
||||||
shared: [--shared, ""]
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: Enable Developer Command Prompt
|
|
||||||
uses: ilammy/msvc-dev-cmd@v1.13.0
|
|
||||||
- name: dependencies
|
|
||||||
run: |
|
|
||||||
pip3 install colorama
|
|
||||||
- name: libdwarf
|
|
||||||
run: |
|
|
||||||
if("${{matrix.compiler}}" -eq "gcc") {
|
|
||||||
cd ..
|
|
||||||
cpptrace/ci/setup-prerequisites-mingw.ps1
|
|
||||||
}
|
|
||||||
- name: build and test
|
|
||||||
run: |
|
|
||||||
python3 ci/test-all-configs.py --${{matrix.compiler}} --default-config
|
|
||||||
test-windows-old:
|
|
||||||
runs-on: windows-2022
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
compiler: [msvc]
|
|
||||||
shared: [--shared, ""]
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: Enable Developer Command Prompt
|
|
||||||
uses: ilammy/msvc-dev-cmd@v1.13.0
|
|
||||||
with:
|
|
||||||
toolset: 14.29 # vc 2019
|
|
||||||
- name: dependencies
|
|
||||||
run: |
|
|
||||||
pip3 install colorama
|
|
||||||
- name: libdwarf
|
|
||||||
run: |
|
|
||||||
if("${{matrix.compiler}}" -eq "gcc") {
|
|
||||||
cd ..
|
|
||||||
cpptrace/ci/setup-prerequisites-mingw.ps1
|
|
||||||
}
|
|
||||||
- name: build and test
|
|
||||||
run: |
|
|
||||||
python3 ci/test-all-configs.py --${{matrix.compiler}} --default-config
|
|
||||||
test-linux-all-configurations:
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
compiler: [gcc, clang]
|
|
||||||
shared: [--shared, ""]
|
|
||||||
needs: test-linux
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: dependencies
|
|
||||||
run: |
|
|
||||||
sudo apt install gcc-10 g++-10 libgcc-10-dev libunwind8-dev
|
|
||||||
pip3 install colorama
|
|
||||||
- name: libdwarf
|
|
||||||
run: |
|
|
||||||
cd ..
|
|
||||||
cpptrace/ci/setup-prerequisites.sh
|
|
||||||
- name: build and test
|
|
||||||
run: |
|
|
||||||
python3 ci/test-all-configs.py --${{matrix.compiler}}
|
|
||||||
test-macos-all-configurations:
|
|
||||||
runs-on: macos-14
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
compiler: [gcc, clang]
|
|
||||||
shared: [--shared, ""]
|
|
||||||
needs: test-macos
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: libdwarf
|
|
||||||
run: |
|
|
||||||
cd ..
|
|
||||||
cpptrace/ci/setup-prerequisites.sh
|
|
||||||
- name: dependencies
|
|
||||||
run: |
|
|
||||||
python3 -m venv env
|
|
||||||
env/bin/pip install colorama
|
|
||||||
- name: build and test
|
|
||||||
run: |
|
|
||||||
env/bin/python ci/test-all-configs.py --${{matrix.compiler}}
|
|
||||||
test-windows-all-configurations:
|
|
||||||
runs-on: windows-2022
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
compiler: [msvc, clang, gcc]
|
|
||||||
shared: [--shared, ""]
|
|
||||||
needs: test-windows
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: Enable Developer Command Prompt
|
|
||||||
uses: ilammy/msvc-dev-cmd@v1.13.0
|
|
||||||
- name: dependencies
|
|
||||||
run: |
|
|
||||||
pip3 install colorama
|
|
||||||
- name: libdwarf
|
|
||||||
run: |
|
|
||||||
if("${{matrix.compiler}}" -eq "gcc") {
|
|
||||||
cd ..
|
|
||||||
cpptrace/ci/setup-prerequisites-mingw.ps1
|
|
||||||
}
|
|
||||||
- name: build and test
|
|
||||||
run: |
|
|
||||||
python3 ci/test-all-configs.py --${{matrix.compiler}}
|
|
||||||
|
|
||||||
|
|
||||||
build-linux-all-remaining-configurations:
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
compiler: [gcc, clang]
|
|
||||||
needs: test-linux-all-configurations
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: dependencies
|
|
||||||
run: |
|
|
||||||
sudo apt install gcc-10 g++-10 libgcc-10-dev libunwind8-dev ninja-build
|
|
||||||
pip3 install colorama
|
|
||||||
- name: libdwarf
|
|
||||||
run: |
|
|
||||||
cd ..
|
|
||||||
cpptrace/ci/setup-prerequisites.sh
|
|
||||||
- name: build
|
|
||||||
run: |
|
|
||||||
python3 ci/build-in-all-remaining-configs.py --${{matrix.compiler}}
|
|
||||||
build-macos-all-remaining-configurations:
|
|
||||||
runs-on: macos-14
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
compiler: [gcc, clang]
|
|
||||||
needs: test-macos-all-configurations
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: dependencies
|
|
||||||
run: |
|
|
||||||
brew install ninja
|
|
||||||
python3 -m venv env
|
|
||||||
env/bin/pip install colorama
|
|
||||||
- name: libdwarf
|
|
||||||
run: |
|
|
||||||
cd ..
|
|
||||||
cpptrace/ci/setup-prerequisites.sh
|
|
||||||
- name: build
|
|
||||||
run: |
|
|
||||||
env/bin/python ci/build-in-all-remaining-configs.py --${{matrix.compiler}}
|
|
||||||
build-windows-all-remaining-configurations:
|
|
||||||
runs-on: windows-2022
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
compiler: [msvc, clang, gcc]
|
|
||||||
needs: test-windows-all-configurations
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: Enable Developer Command Prompt
|
|
||||||
uses: ilammy/msvc-dev-cmd@v1.13.0
|
|
||||||
- name: dependencies
|
|
||||||
run: |
|
|
||||||
pip3 install colorama
|
|
||||||
- name: libdwarf
|
|
||||||
run: |
|
|
||||||
if("${{matrix.compiler}}" -eq "gcc") {
|
|
||||||
cd ..
|
|
||||||
cpptrace/ci/setup-prerequisites-mingw.ps1
|
|
||||||
}
|
|
||||||
- name: build
|
|
||||||
run: |
|
|
||||||
python3 ci/build-in-all-remaining-configs.py --${{matrix.compiler}}
|
|
||||||
|
|
||||||
|
|
||||||
performancetest-linux:
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
compiler: [g++-11, clang++-14]
|
|
||||||
config: [
|
|
||||||
-DSPEEDTEST_DWARF4=On,
|
|
||||||
-DSPEEDTEST_DWARF5=On
|
|
||||||
]
|
|
||||||
needs: test-linux-all-configurations
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: dependencies
|
|
||||||
run: sudo apt install gcc-11 g++-11 libgcc-11-dev
|
|
||||||
- name: build
|
|
||||||
run: |
|
|
||||||
mkdir -p build
|
|
||||||
cd build
|
|
||||||
cmake .. -DCMAKE_CXX_COMPILER=${{matrix.compiler}} -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=foo
|
|
||||||
make -j
|
|
||||||
make install
|
|
||||||
mkdir -p ../test/speedtest/build
|
|
||||||
cd ../test/speedtest/build
|
|
||||||
cmake .. \
|
|
||||||
-DCMAKE_BUILD_TYPE=Debug \
|
|
||||||
${{matrix.config}}
|
|
||||||
make -j
|
|
||||||
- name: speedtest
|
|
||||||
working-directory: test/speedtest/build
|
|
||||||
run: |
|
|
||||||
./speedtest | python3 ../../../ci/speedtest.py ${{matrix.compiler}} ${{matrix.config}}
|
|
||||||
|
|
||||||
|
|
||||||
test-linux-fetchcontent:
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
shared: [On, Off]
|
|
||||||
needs: test-linux-all-configurations
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: test
|
|
||||||
run: |
|
|
||||||
tag=$(git rev-parse --abbrev-ref HEAD)
|
|
||||||
echo $tag
|
|
||||||
cd ..
|
|
||||||
cp -rv cpptrace/test/fetchcontent-integration .
|
|
||||||
mkdir fetchcontent-integration/build
|
|
||||||
cd fetchcontent-integration/build
|
|
||||||
cmake .. -DCMAKE_BUILD_TYPE=Debug -DCPPTRACE_TAG=$tag -DBUILD_SHARED_LIBS=${{matrix.shared}} -DCPPTRACE_WERROR_BUILD=On
|
|
||||||
make
|
|
||||||
./main
|
|
||||||
test-linux-findpackage:
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
shared: [On, Off]
|
|
||||||
needs: test-linux-all-configurations
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: test
|
|
||||||
run: |
|
|
||||||
tag=$(git rev-parse --abbrev-ref HEAD)
|
|
||||||
mkdir build
|
|
||||||
cd build
|
|
||||||
cmake .. -DCMAKE_BUILD_TYPE=Debug -DBUILD_SHARED_LIBS=${{matrix.shared}} -DCPPTRACE_WERROR_BUILD=On
|
|
||||||
sudo make -j install
|
|
||||||
cd ../..
|
|
||||||
cp -rv cpptrace/test/findpackage-integration .
|
|
||||||
mkdir findpackage-integration/build
|
|
||||||
cd findpackage-integration/build
|
|
||||||
cmake .. -DCMAKE_BUILD_TYPE=Debug
|
|
||||||
make
|
|
||||||
./main
|
|
||||||
test-linux-add_subdirectory:
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
shared: [On, Off]
|
|
||||||
needs: test-linux-all-configurations
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: build
|
|
||||||
run: |
|
|
||||||
cd ..
|
|
||||||
cp -rv cpptrace/test/add_subdirectory-integration .
|
|
||||||
cp -rv cpptrace add_subdirectory-integration
|
|
||||||
mkdir add_subdirectory-integration/build
|
|
||||||
cd add_subdirectory-integration/build
|
|
||||||
cmake .. -DCMAKE_BUILD_TYPE=Debug -DBUILD_SHARED_LIBS=${{matrix.shared}} -DCPPTRACE_WERROR_BUILD=On
|
|
||||||
make
|
|
||||||
./main
|
|
||||||
|
|
||||||
test-macos-fetchcontent:
|
|
||||||
runs-on: macos-14
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
shared: [On, Off]
|
|
||||||
needs: test-macos-all-configurations
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: test
|
|
||||||
run: |
|
|
||||||
tag=$(git rev-parse --abbrev-ref HEAD)
|
|
||||||
echo $tag
|
|
||||||
cd ..
|
|
||||||
cp -rv cpptrace/test/fetchcontent-integration .
|
|
||||||
mkdir fetchcontent-integration/build
|
|
||||||
cd fetchcontent-integration/build
|
|
||||||
cmake .. -DCMAKE_BUILD_TYPE=Debug -DCPPTRACE_TAG=$tag -DBUILD_SHARED_LIBS=${{matrix.shared}} -DCPPTRACE_WERROR_BUILD=On
|
|
||||||
make
|
|
||||||
./main
|
|
||||||
test-macos-findpackage:
|
|
||||||
runs-on: macos-14
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
shared: [On, Off]
|
|
||||||
needs: test-macos-all-configurations
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: test
|
|
||||||
run: |
|
|
||||||
tag=$(git rev-parse --abbrev-ref HEAD)
|
|
||||||
echo $tag
|
|
||||||
mkdir build
|
|
||||||
cd build
|
|
||||||
cmake .. -DCMAKE_BUILD_TYPE=Debug -DBUILD_SHARED_LIBS=${{matrix.shared}} -DCPPTRACE_WERROR_BUILD=On
|
|
||||||
sudo make -j install
|
|
||||||
cd ../..
|
|
||||||
cp -rv cpptrace/test/findpackage-integration .
|
|
||||||
mkdir findpackage-integration/build
|
|
||||||
cd findpackage-integration/build
|
|
||||||
cmake .. -DCMAKE_BUILD_TYPE=Debug
|
|
||||||
make
|
|
||||||
./main
|
|
||||||
test-macos-add_subdirectory:
|
|
||||||
runs-on: macos-14
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
shared: [On, Off]
|
|
||||||
needs: test-macos-all-configurations
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: test
|
|
||||||
run: |
|
|
||||||
cd ..
|
|
||||||
cp -rv cpptrace/test/add_subdirectory-integration .
|
|
||||||
cp -rv cpptrace add_subdirectory-integration
|
|
||||||
mkdir add_subdirectory-integration/build
|
|
||||||
cd add_subdirectory-integration/build
|
|
||||||
cmake .. -DCMAKE_BUILD_TYPE=Debug -DBUILD_SHARED_LIBS=${{matrix.shared}} -DCPPTRACE_WERROR_BUILD=On
|
|
||||||
make
|
|
||||||
./main
|
|
||||||
|
|
||||||
test-mingw-fetchcontent:
|
|
||||||
runs-on: windows-2022
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
shared: [On, Off]
|
|
||||||
needs: test-windows-all-configurations
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: test
|
|
||||||
run: |
|
|
||||||
$tag=$(git rev-parse --abbrev-ref HEAD)
|
|
||||||
echo $tag
|
|
||||||
cd ..
|
|
||||||
cp -Recurse cpptrace/test/fetchcontent-integration .
|
|
||||||
mkdir fetchcontent-integration/build
|
|
||||||
cd fetchcontent-integration/build
|
|
||||||
cmake .. -DCMAKE_BUILD_TYPE=Debug -DCPPTRACE_TAG="$tag" "-GUnix Makefiles" -DBUILD_SHARED_LIBS=${{matrix.shared}} -DCPPTRACE_WERROR_BUILD=On
|
|
||||||
make
|
|
||||||
.\main.exe
|
|
||||||
test-mingw-findpackage:
|
|
||||||
runs-on: windows-2022
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
shared: [On, Off]
|
|
||||||
needs: test-windows-all-configurations
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: test
|
|
||||||
run: |
|
|
||||||
$tag=$(git rev-parse --abbrev-ref HEAD)
|
|
||||||
echo $tag
|
|
||||||
mkdir build
|
|
||||||
cd build
|
|
||||||
cmake .. -DCMAKE_BUILD_TYPE=Debug -DBUILD_SHARED_LIBS=${{matrix.shared}} "-GUnix Makefiles" -DCMAKE_INSTALL_PREFIX=C:/foo -DCPPTRACE_WERROR_BUILD=On
|
|
||||||
make -j install
|
|
||||||
cd ../..
|
|
||||||
mv cpptrace/test/findpackage-integration .
|
|
||||||
mkdir findpackage-integration/build
|
|
||||||
cd findpackage-integration/build
|
|
||||||
cmake .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_PREFIX_PATH=C:/foo "-GUnix Makefiles"
|
|
||||||
make
|
|
||||||
./main
|
|
||||||
test-mingw-add_subdirectory:
|
|
||||||
runs-on: windows-2022
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
shared: [On, Off]
|
|
||||||
needs: test-windows-all-configurations
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: test
|
|
||||||
run: |
|
|
||||||
cd ..
|
|
||||||
cp -Recurse cpptrace/test/add_subdirectory-integration .
|
|
||||||
cp -Recurse cpptrace add_subdirectory-integration
|
|
||||||
mkdir add_subdirectory-integration/build
|
|
||||||
cd add_subdirectory-integration/build
|
|
||||||
cmake .. -DCMAKE_BUILD_TYPE=Debug "-GUnix Makefiles" -DBUILD_SHARED_LIBS=${{matrix.shared}} -DCPPTRACE_WERROR_BUILD=On
|
|
||||||
make
|
|
||||||
.\main.exe
|
|
||||||
test-windows-fetchcontent:
|
|
||||||
runs-on: windows-2022
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
shared: [On, Off]
|
|
||||||
needs: test-windows-all-configurations
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: Enable Developer Command Prompt
|
|
||||||
uses: ilammy/msvc-dev-cmd@v1.13.0
|
|
||||||
- name: test
|
|
||||||
run: |
|
|
||||||
$tag=$(git rev-parse --abbrev-ref HEAD)
|
|
||||||
echo $tag
|
|
||||||
cd ..
|
|
||||||
cp -Recurse cpptrace/test/fetchcontent-integration .
|
|
||||||
mkdir fetchcontent-integration/build
|
|
||||||
cd fetchcontent-integration/build
|
|
||||||
cmake .. -DCMAKE_BUILD_TYPE=Debug -DCPPTRACE_TAG="$tag" -DBUILD_SHARED_LIBS=${{matrix.shared}} -DCPPTRACE_WERROR_BUILD=On
|
|
||||||
msbuild demo_project.sln
|
|
||||||
.\Debug\main.exe
|
|
||||||
test-windows-findpackage:
|
|
||||||
runs-on: windows-2022
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
shared: [On, Off]
|
|
||||||
needs: test-windows-all-configurations
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: Enable Developer Command Prompt
|
|
||||||
uses: ilammy/msvc-dev-cmd@v1.13.0
|
|
||||||
- name: test
|
|
||||||
run: |
|
|
||||||
$tag=$(git rev-parse --abbrev-ref HEAD)
|
|
||||||
echo $tag
|
|
||||||
mkdir build
|
|
||||||
cd build
|
|
||||||
cmake .. -DCMAKE_BUILD_TYPE=Debug -DBUILD_SHARED_LIBS=${{matrix.shared}} -DCMAKE_INSTALL_PREFIX=C:/foo -DCPPTRACE_WERROR_BUILD=On
|
|
||||||
msbuild cpptrace.sln
|
|
||||||
msbuild INSTALL.vcxproj
|
|
||||||
cd ../..
|
|
||||||
mv cpptrace/test/findpackage-integration .
|
|
||||||
mkdir findpackage-integration/build
|
|
||||||
cd findpackage-integration/build
|
|
||||||
cmake .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_PREFIX_PATH=C:/foo
|
|
||||||
msbuild demo_project.sln
|
|
||||||
.\Debug\main.exe
|
|
||||||
test-windows-add_subdirectory:
|
|
||||||
runs-on: windows-2022
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
shared: [On, Off]
|
|
||||||
needs: test-windows-all-configurations
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: Enable Developer Command Prompt
|
|
||||||
uses: ilammy/msvc-dev-cmd@v1.13.0
|
|
||||||
- name: test
|
|
||||||
run: |
|
|
||||||
cd ..
|
|
||||||
cp -Recurse cpptrace/test/add_subdirectory-integration .
|
|
||||||
cp -Recurse cpptrace add_subdirectory-integration
|
|
||||||
mkdir add_subdirectory-integration/build
|
|
||||||
cd add_subdirectory-integration/build
|
|
||||||
cmake .. -DCMAKE_BUILD_TYPE=Debug -DBUILD_SHARED_LIBS=${{matrix.shared}} -DCPPTRACE_WERROR_BUILD=On
|
|
||||||
msbuild demo_project.sln
|
|
||||||
.\Debug\main.exe
|
|
||||||
|
|
||||||
|
|
||||||
unittest-linux:
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
compiler: [g++-10, clang++-18]
|
|
||||||
stdlib: [libstdc++, libc++]
|
|
||||||
dwarf_version: [4, 5]
|
|
||||||
split_dwarf: [OFF, ON]
|
|
||||||
exclude:
|
|
||||||
- compiler: g++-10
|
|
||||||
stdlib: libc++
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: dependencies
|
|
||||||
run: |
|
|
||||||
sudo apt install gcc-10 g++-10 libgcc-10-dev ninja-build libc++-dev
|
|
||||||
cd ..
|
|
||||||
cpptrace/ci/setup-prerequisites-unittest.sh
|
|
||||||
- name: build and test
|
|
||||||
run: |
|
|
||||||
python3 ci/unittest.py \
|
|
||||||
--slice=compiler:${{matrix.compiler}} \
|
|
||||||
--slice=stdlib:${{matrix.stdlib}} \
|
|
||||||
--slice=dwarf_version:${{matrix.dwarf_version}} \
|
|
||||||
--slice=split_dwarf:${{matrix.split_dwarf}}
|
|
||||||
unittest-linux-bazel:
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: dependencies
|
|
||||||
run: |
|
|
||||||
sudo apt install -y libtool libncurses5
|
|
||||||
- name: test dbg
|
|
||||||
run: |
|
|
||||||
bazel test //... -c dbg
|
|
||||||
- name: test opt
|
|
||||||
run: |
|
|
||||||
bazel test //... -c opt
|
|
||||||
unittest-linux-arm:
|
|
||||||
runs-on: ubuntu-24.04-arm
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
compiler: [g++-10, clang++-18]
|
|
||||||
stdlib: [libstdc++, libc++]
|
|
||||||
dwarf_version: [4, 5]
|
|
||||||
split_dwarf: [OFF, ON]
|
|
||||||
exclude:
|
|
||||||
- compiler: g++-10
|
|
||||||
stdlib: libc++
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: dependencies
|
|
||||||
run: |
|
|
||||||
sudo apt install gcc-10 g++-10 libgcc-10-dev ninja-build libc++-dev
|
|
||||||
cd ..
|
|
||||||
cpptrace/ci/setup-prerequisites-unittest.sh
|
|
||||||
- name: build and test
|
|
||||||
run: |
|
|
||||||
python3 ci/unittest.py \
|
|
||||||
--slice=compiler:${{matrix.compiler}} \
|
|
||||||
--slice=stdlib:${{matrix.stdlib}} \
|
|
||||||
--slice=dwarf_version:${{matrix.dwarf_version}} \
|
|
||||||
--slice=split_dwarf:${{matrix.split_dwarf}}
|
|
||||||
unittest-macos:
|
|
||||||
runs-on: macos-14
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: maxim-lobanov/setup-xcode@v1
|
|
||||||
with:
|
|
||||||
xcode-version: "15.4"
|
|
||||||
- name: dependencies
|
|
||||||
run: |
|
|
||||||
brew install ninja
|
|
||||||
python3 -m venv env
|
|
||||||
env/bin/pip install colorama
|
|
||||||
cd ..
|
|
||||||
cpptrace/ci/setup-prerequisites-unittest-macos.sh
|
|
||||||
- name: build and test
|
|
||||||
run: |
|
|
||||||
env/bin/python ci/unittest.py
|
|
||||||
unittest-windows:
|
|
||||||
runs-on: windows-2022
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
compiler: [cl, clang++]
|
|
||||||
shared: [OFF] # TODO: Re-enable shared
|
|
||||||
build_type: [Debug, RelWithDebInfo]
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: Enable Developer Command Prompt
|
|
||||||
uses: ilammy/msvc-dev-cmd@v1.13.0
|
|
||||||
- name: build and test
|
|
||||||
run: |
|
|
||||||
mkdir build
|
|
||||||
cd build
|
|
||||||
cmake .. `
|
|
||||||
-DCMAKE_CXX_COMPILER=${{matrix.compiler}} `
|
|
||||||
-DCMAKE_C_COMPILER=${{matrix.compiler == 'clang++' && 'clang' || matrix.compiler}} `
|
|
||||||
-DBUILD_SHARED_LIBS=${{matrix.shared}} `
|
|
||||||
-DCPPTRACE_WERROR_BUILD=On `
|
|
||||||
-DCPPTRACE_BUILD_TESTING=On
|
|
||||||
cmake --build . --config ${{matrix.build_type}}
|
|
||||||
./${{matrix.build_type}}/unittest
|
|
||||||
# TODO: Macos, mingw
|
|
||||||
161
.github/workflows/cmake-integration.yml
vendored
Normal file
161
.github/workflows/cmake-integration.yml
vendored
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
name: cmake-integration
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test-linux-fetchcontent:
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: test
|
||||||
|
run: |
|
||||||
|
tag=$(git rev-parse --abbrev-ref HEAD)
|
||||||
|
cd ..
|
||||||
|
cp -rv cpptrace/test/fetchcontent-integration .
|
||||||
|
mkdir fetchcontent-integration/build
|
||||||
|
cd fetchcontent-integration/build
|
||||||
|
cmake .. -DCMAKE_BUILD_TYPE=Debug -DCPPTRACE_TAG=$tag
|
||||||
|
make
|
||||||
|
./main
|
||||||
|
test-linux-findpackage:
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: build
|
||||||
|
run: |
|
||||||
|
tag=$(git rev-parse --abbrev-ref HEAD)
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -DCMAKE_BUILD_TYPE=Debug
|
||||||
|
sudo make -j install
|
||||||
|
cd ../..
|
||||||
|
cp -rv cpptrace/test/findpackage-integration .
|
||||||
|
mkdir findpackage-integration/build
|
||||||
|
cd findpackage-integration/build
|
||||||
|
cmake .. -DCMAKE_BUILD_TYPE=Debug
|
||||||
|
make
|
||||||
|
./main
|
||||||
|
test-linux-add_subdirectory:
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: build
|
||||||
|
run: |
|
||||||
|
cd ..
|
||||||
|
cp -rv cpptrace/test/add_subdirectory-integration .
|
||||||
|
cp -rv cpptrace add_subdirectory-integration
|
||||||
|
mkdir add_subdirectory-integration/build
|
||||||
|
cd add_subdirectory-integration/build
|
||||||
|
cmake .. -DCMAKE_BUILD_TYPE=Debug
|
||||||
|
make
|
||||||
|
./main
|
||||||
|
|
||||||
|
test-macos-fetchcontent:
|
||||||
|
runs-on: macos-13
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: test
|
||||||
|
run: |
|
||||||
|
tag=$(git rev-parse --abbrev-ref HEAD)
|
||||||
|
cd ..
|
||||||
|
cp -rv cpptrace/test/fetchcontent-integration .
|
||||||
|
mkdir fetchcontent-integration/build
|
||||||
|
cd fetchcontent-integration/build
|
||||||
|
cmake .. -DCMAKE_BUILD_TYPE=Debug -DCPPTRACE_TAG=$tag
|
||||||
|
make
|
||||||
|
./main
|
||||||
|
test-macos-findpackage:
|
||||||
|
runs-on: macos-13
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: build
|
||||||
|
run: |
|
||||||
|
tag=$(git rev-parse --abbrev-ref HEAD)
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -DCMAKE_BUILD_TYPE=Debug
|
||||||
|
sudo make -j install
|
||||||
|
cd ../..
|
||||||
|
cp -rv cpptrace/test/findpackage-integration .
|
||||||
|
mkdir findpackage-integration/build
|
||||||
|
cd findpackage-integration/build
|
||||||
|
cmake .. -DCMAKE_BUILD_TYPE=Debug
|
||||||
|
make
|
||||||
|
./main
|
||||||
|
test-macos-add_subdirectory:
|
||||||
|
runs-on: macos-13
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: test
|
||||||
|
run: |
|
||||||
|
cd ..
|
||||||
|
cp -rv cpptrace/test/add_subdirectory-integration .
|
||||||
|
cp -rv cpptrace add_subdirectory-integration
|
||||||
|
mkdir add_subdirectory-integration/build
|
||||||
|
cd add_subdirectory-integration/build
|
||||||
|
cmake .. -DCMAKE_BUILD_TYPE=Debug
|
||||||
|
make
|
||||||
|
./main
|
||||||
|
|
||||||
|
test-mingw-fetchcontent:
|
||||||
|
runs-on: windows-2019
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: test
|
||||||
|
run: |
|
||||||
|
$tag=$(git rev-parse --abbrev-ref HEAD)
|
||||||
|
cd ..
|
||||||
|
cp -Recurse cpptrace/test/fetchcontent-integration .
|
||||||
|
mkdir fetchcontent-integration/build
|
||||||
|
cd fetchcontent-integration/build
|
||||||
|
cmake .. -DCMAKE_BUILD_TYPE=Debug -DCPPTRACE_TAG="$tag" -DCMAKE_BUILD_TYPE=g++ "-GUnix Makefiles"
|
||||||
|
make
|
||||||
|
.\main.exe
|
||||||
|
test-mingw-add_subdirectory:
|
||||||
|
runs-on: windows-2019
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: test
|
||||||
|
run: |
|
||||||
|
cd ..
|
||||||
|
cp -Recurse cpptrace/test/add_subdirectory-integration .
|
||||||
|
cp -Recurse cpptrace add_subdirectory-integration
|
||||||
|
mkdir add_subdirectory-integration/build
|
||||||
|
cd add_subdirectory-integration/build
|
||||||
|
cmake .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_BUILD_TYPE=g++ "-GUnix Makefiles"
|
||||||
|
make
|
||||||
|
.\main.exe
|
||||||
|
test-windows-fetchcontent:
|
||||||
|
runs-on: windows-2019
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Enable Developer Command Prompt
|
||||||
|
uses: ilammy/msvc-dev-cmd@v1.10.0
|
||||||
|
- name: test
|
||||||
|
run: |
|
||||||
|
$tag=$(git rev-parse --abbrev-ref HEAD)
|
||||||
|
cd ..
|
||||||
|
cp -Recurse cpptrace/test/fetchcontent-integration .
|
||||||
|
mkdir fetchcontent-integration/build
|
||||||
|
cd fetchcontent-integration/build
|
||||||
|
cmake .. -DCMAKE_BUILD_TYPE=Debug -DCPPTRACE_TAG="$tag"
|
||||||
|
msbuild demo_project.sln
|
||||||
|
.\Debug\main.exe
|
||||||
|
test-windows-add_subdirectory:
|
||||||
|
runs-on: windows-2019
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Enable Developer Command Prompt
|
||||||
|
uses: ilammy/msvc-dev-cmd@v1.10.0
|
||||||
|
- name: test
|
||||||
|
run: |
|
||||||
|
cd ..
|
||||||
|
cp -Recurse cpptrace/test/add_subdirectory-integration .
|
||||||
|
cp -Recurse cpptrace add_subdirectory-integration
|
||||||
|
mkdir add_subdirectory-integration/build
|
||||||
|
cd add_subdirectory-integration/build
|
||||||
|
cmake .. -DCMAKE_BUILD_TYPE=Debug
|
||||||
|
msbuild demo_project.sln
|
||||||
|
.\Debug\main.exe
|
||||||
18
.github/workflows/lint.yml
vendored
Normal file
18
.github/workflows/lint.yml
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
name: lint
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
lint:
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt install clang-tidy
|
||||||
|
- name: clang-tidy
|
||||||
|
run: |
|
||||||
|
chmod +x lint.sh
|
||||||
|
./lint.sh -DCPPTRACE_BACKTRACE_PATH=\"/usr/lib/gcc/x86_64-linux-gnu/10/include/backtrace.h\"
|
||||||
74
.github/workflows/performance-tests.yml
vendored
Normal file
74
.github/workflows/performance-tests.yml
vendored
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
name: performance-test
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
performancetest-linux:
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
compiler: [g++-11, clang++-14]
|
||||||
|
target: [Debug]
|
||||||
|
std: [11, 20]
|
||||||
|
config: [
|
||||||
|
"-DCPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE=On",
|
||||||
|
"-DCPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE=On -DCPPTRACE_BUILD_SPEEDTEST_DWARF4=On",
|
||||||
|
"-DCPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE=On -DCPPTRACE_BUILD_SPEEDTEST_DWARF5=On"
|
||||||
|
]
|
||||||
|
exclude:
|
||||||
|
- config: -DCPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE=On -DCPPTRACE_BUILD_SPEEDTEST_DWARF5=On
|
||||||
|
compiler: g++-11
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: dependencies
|
||||||
|
run: sudo apt install gcc-11 g++-11 libgcc-11-dev
|
||||||
|
- name: build
|
||||||
|
run: |
|
||||||
|
mkdir -p build
|
||||||
|
cd build
|
||||||
|
cmake .. \
|
||||||
|
-DCMAKE_BUILD_TYPE=${{matrix.target}} \
|
||||||
|
-DCMAKE_CXX_COMPILER=${{matrix.compiler}} \
|
||||||
|
-DCMAKE_CXX_STANDARD=${{matrix.std}} \
|
||||||
|
${{matrix.config}} \
|
||||||
|
-DCPPTRACE_BACKTRACE_PATH=/usr/lib/gcc/x86_64-linux-gnu/11/include/backtrace.h \
|
||||||
|
-DCPPTRACE_BUILD_SPEEDTEST=On
|
||||||
|
make -j
|
||||||
|
- name: test
|
||||||
|
working-directory: build
|
||||||
|
run: |
|
||||||
|
./speedtest | python3 ../ci/speedtest.py ${{matrix.compiler}} ${{matrix.config}}
|
||||||
|
# TODO: For some reason this is slow on github's runner
|
||||||
|
#performancetest-windows:
|
||||||
|
# runs-on: windows-2019
|
||||||
|
# strategy:
|
||||||
|
# fail-fast: false
|
||||||
|
# matrix:
|
||||||
|
# compiler: [cl, clang++]
|
||||||
|
# target: [Debug]
|
||||||
|
# std: [11, 20]
|
||||||
|
# config: [
|
||||||
|
# "-DCPPTRACE_GET_SYMBOLS_WITH_DBGHELP=On"
|
||||||
|
# ]
|
||||||
|
# steps:
|
||||||
|
# - uses: actions/checkout@v2
|
||||||
|
# - name: Enable Developer Command Prompt
|
||||||
|
# uses: ilammy/msvc-dev-cmd@v1.10.0
|
||||||
|
# - name: build
|
||||||
|
# run: |
|
||||||
|
# mkdir -p build
|
||||||
|
# cd build
|
||||||
|
# cmake .. `
|
||||||
|
# -DCMAKE_BUILD_TYPE=Debug `
|
||||||
|
# -DCMAKE_CXX_COMPILER=${{matrix.compiler}} `
|
||||||
|
# -DCMAKE_CXX_STANDARD=${{matrix.std}} `
|
||||||
|
# ${{matrix.config}} `
|
||||||
|
# -DCPPTRACE_BUILD_SPEEDTEST=On
|
||||||
|
# msbuild .\cpptrace.sln
|
||||||
|
# - name: test
|
||||||
|
# working-directory: build
|
||||||
|
# run: |
|
||||||
|
# .\${{matrix.target}}\speedtest.exe | python3 ../ci/speedtest.py ${{matrix.config}}
|
||||||
32
.github/workflows/sonarlint.yml
vendored
32
.github/workflows/sonarlint.yml
vendored
@ -1,32 +0,0 @@
|
|||||||
on:
|
|
||||||
# Trigger analysis when pushing in master or pull requests, and when creating
|
|
||||||
# a pull request.
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
- dev
|
|
||||||
pull_request:
|
|
||||||
types: [opened, synchronize, reopened]
|
|
||||||
name: sonarlint
|
|
||||||
jobs:
|
|
||||||
sonarcloud:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
# Disabling shallow clone is recommended for improving relevancy of reporting
|
|
||||||
fetch-depth: 0
|
|
||||||
- name: Install sonar-scanner and build-wrapper
|
|
||||||
uses: sonarsource/sonarcloud-github-c-cpp@v2
|
|
||||||
- name: Run build-wrapper
|
|
||||||
run: |
|
|
||||||
mkdir build
|
|
||||||
cd build
|
|
||||||
cmake .. -DCMAKE_BUILD_TYPE=Debug -DCPPTRACE_BUILD_TESTING=On -DCMAKE_EXPORT_COMPILE_COMMANDS=On -DCMAKE_CXX_STANDARD=11
|
|
||||||
make -j
|
|
||||||
cd ..
|
|
||||||
- name: Run sonar-scanner
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
|
||||||
run: sonar-scanner
|
|
||||||
57
.github/workflows/test.yml
vendored
Normal file
57
.github/workflows/test.yml
vendored
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
name: test
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
# TODO: Test statically linked
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test-linux:
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: dependencies
|
||||||
|
run: sudo apt install gcc-10 g++-10 libgcc-10-dev
|
||||||
|
- name: build
|
||||||
|
run: |
|
||||||
|
pip3 install colorama
|
||||||
|
python3 ci/test-all-configs.py
|
||||||
|
test-macos:
|
||||||
|
runs-on: macos-13
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: build
|
||||||
|
run: |
|
||||||
|
pip3 install colorama
|
||||||
|
python3 ci/test-all-configs.py
|
||||||
|
test-windows-msvc:
|
||||||
|
runs-on: windows-2019
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Enable Developer Command Prompt
|
||||||
|
uses: ilammy/msvc-dev-cmd@v1.10.0
|
||||||
|
- name: build
|
||||||
|
run: |
|
||||||
|
pip3 install colorama
|
||||||
|
python3 ci/test-all-configs.py --msvc-only
|
||||||
|
test-windows-clang:
|
||||||
|
runs-on: windows-2019
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Enable Developer Command Prompt
|
||||||
|
uses: ilammy/msvc-dev-cmd@v1.10.0
|
||||||
|
- name: build
|
||||||
|
run: |
|
||||||
|
pip3 install colorama
|
||||||
|
python3 ci/test-all-configs.py --clang-only
|
||||||
|
test-windows-mingw:
|
||||||
|
runs-on: windows-2019
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Enable Developer Command Prompt
|
||||||
|
uses: ilammy/msvc-dev-cmd@v1.10.0
|
||||||
|
- name: build
|
||||||
|
run: |
|
||||||
|
pip3 install colorama
|
||||||
|
python3 ci/test-all-configs.py --mingw-only
|
||||||
10
.gitignore
vendored
10
.gitignore
vendored
@ -1,11 +1,5 @@
|
|||||||
.vscode/
|
.vscode
|
||||||
.idea/
|
|
||||||
|
|
||||||
a.out
|
a.out
|
||||||
build*/
|
build*/
|
||||||
repro*/
|
repro*/
|
||||||
__pycache__/
|
__pycache__
|
||||||
scratch
|
|
||||||
tmp/
|
|
||||||
bazel-*/
|
|
||||||
cmake-build-*/
|
|
||||||
|
|||||||
34
BUILD.bazel
34
BUILD.bazel
@ -1,34 +0,0 @@
|
|||||||
cc_library(
|
|
||||||
name = "cpptrace",
|
|
||||||
srcs = glob([
|
|
||||||
"src/**/*.hpp",
|
|
||||||
"src/**/*.cpp",
|
|
||||||
]),
|
|
||||||
local_defines = [
|
|
||||||
"CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF",
|
|
||||||
"CPPTRACE_DEMANGLE_WITH_CXXABI",
|
|
||||||
"CPPTRACE_UNWIND_WITH_LIBUNWIND"
|
|
||||||
],
|
|
||||||
hdrs = glob([
|
|
||||||
"include/cpptrace/*.hpp",
|
|
||||||
"include/ctrace/*.h",
|
|
||||||
]),
|
|
||||||
includes = [
|
|
||||||
"include",
|
|
||||||
"src"
|
|
||||||
],
|
|
||||||
deps = [
|
|
||||||
"@libdwarf//:libdwarf",
|
|
||||||
"@libunwind//:libunwind"
|
|
||||||
],
|
|
||||||
copts = [
|
|
||||||
"-Wall",
|
|
||||||
"-Wextra",
|
|
||||||
"-Werror=return-type",
|
|
||||||
"-Wundef",
|
|
||||||
"-Wuninitialized",
|
|
||||||
"-fPIC",
|
|
||||||
"-std=c++11"
|
|
||||||
],
|
|
||||||
visibility = ["//visibility:public"],
|
|
||||||
)
|
|
||||||
415
CHANGELOG.md
415
CHANGELOG.md
@ -1,415 +0,0 @@
|
|||||||
# Changelog
|
|
||||||
|
|
||||||
- [Changelog](#changelog)
|
|
||||||
- [v0.8.2](#v082)
|
|
||||||
- [v0.8.1](#v081)
|
|
||||||
- [v0.8.0](#v080)
|
|
||||||
- [v0.7.5](#v075)
|
|
||||||
- [v0.7.4](#v074)
|
|
||||||
- [v0.7.3](#v073)
|
|
||||||
- [v0.7.2](#v072)
|
|
||||||
- [v0.7.1](#v071)
|
|
||||||
- [v0.7.0](#v070)
|
|
||||||
- [v0.6.3](#v063)
|
|
||||||
- [v0.6.2](#v062)
|
|
||||||
- [v0.6.1](#v061)
|
|
||||||
- [v0.6.0](#v060)
|
|
||||||
- [v0.5.4](#v054)
|
|
||||||
- [v0.5.3](#v053)
|
|
||||||
- [v0.5.2](#v052)
|
|
||||||
- [v0.5.1](#v051)
|
|
||||||
- [v0.5.0](#v050)
|
|
||||||
- [v0.4.1](#v041)
|
|
||||||
- [v0.4.0](#v040)
|
|
||||||
- [v0.3.1](#v031)
|
|
||||||
- [v0.3.0](#v030)
|
|
||||||
- [v0.2.1](#v021)
|
|
||||||
- [v0.2.0](#v020)
|
|
||||||
- [v0.1.1](#v011)
|
|
||||||
- [v0.1](#v01)
|
|
||||||
|
|
||||||
# v0.8.2
|
|
||||||
|
|
||||||
Fixed:
|
|
||||||
- Fixed printing of internal error messages when an object file can't be loaded, mainly affecting MacOS https://github.com/jeremy-rifkin/cpptrace/issues/217
|
|
||||||
|
|
||||||
Other:
|
|
||||||
- Bumped zstd via FetchContent to 1.5.7
|
|
||||||
|
|
||||||
# v0.8.1
|
|
||||||
|
|
||||||
Fixed:
|
|
||||||
- Fixed compile error on msvc https://github.com/jeremy-rifkin/cpptrace/issues/215
|
|
||||||
|
|
||||||
Added:
|
|
||||||
- Added `cpptrace::can_get_safe_object_frame()`
|
|
||||||
|
|
||||||
Breaking changes:
|
|
||||||
- Renamed ctrace's `can_signal_safe_unwind` to `ctrace_can_signal_safe_unwind`. This was an oversight. Apologies for
|
|
||||||
including a breaking change in a patch release. Github code search suggests this API isn't used in public code, at
|
|
||||||
least.
|
|
||||||
|
|
||||||
Other:
|
|
||||||
- Added CI workflow to test on old msvc
|
|
||||||
- Made some internal improvements on robustness and cleanliness
|
|
||||||
|
|
||||||
# v0.8.0
|
|
||||||
|
|
||||||
Added:
|
|
||||||
- Added support for resolving symbols from elf and mach-o symbol tables, allowing function names to be resolved even in
|
|
||||||
a build that doesn't include debug information https://github.com/jeremy-rifkin/cpptrace/issues/201
|
|
||||||
- Added a configurable stack trace formatter https://github.com/jeremy-rifkin/cpptrace/issues/164
|
|
||||||
- Added configuration options for the libdwarf back-end that can be used to lower memory usage on memory-constrained
|
|
||||||
systems https://github.com/jeremy-rifkin/cpptrace/issues/193
|
|
||||||
- Added `cpptrace::nullable<T>::null_value`
|
|
||||||
- Made `cpptrace::nullable<T>` member functions conditionally `constexpr` where possible
|
|
||||||
|
|
||||||
Fixed:
|
|
||||||
- Fixed handling of `SymInitialize` when other code has already called `SymInitialize`. `SymInitialize` must only be
|
|
||||||
called once per handle and cpptrace now attempts to duplicate the current process handle to avoid conflicts.
|
|
||||||
https://github.com/jeremy-rifkin/cpptrace/issues/204
|
|
||||||
- Fixed a couple of locking edge cases surrounding dbghelp functions
|
|
||||||
- Fixed improper deallocation of `dwarf_errmsg` in the libdwarf back-end
|
|
||||||
|
|
||||||
Breaking changes:
|
|
||||||
- `cpptrace::get_snippet` previously included a newline at the end but it now does not. This also affects the behavior
|
|
||||||
of trace formatting with snippets enabled.
|
|
||||||
|
|
||||||
Other:
|
|
||||||
- Significantly improved memory usage and performance of the libdwarf back-end
|
|
||||||
- Improved implementation and organization of internal utility types, such as `optional` and `Result`
|
|
||||||
- Improved trace printing and formatting implementation
|
|
||||||
- Added unit tests for library internal utilities
|
|
||||||
- Added logic to the cxxabi demangler to ensure external names begin with `_Z` or `__Z` before attempting to demangle
|
|
||||||
- Added various internal tools and abstractions to improve maintainability and clarity
|
|
||||||
- Various internal improvements for robustness
|
|
||||||
- Added a small handful of utility tool programs that are useful for continued development, maintenance, and debugging
|
|
||||||
- Improved library CI setup
|
|
||||||
- Marked the `CPPTRACE_BUILD_BENCHMARK` option as advanced
|
|
||||||
|
|
||||||
# v0.7.5
|
|
||||||
|
|
||||||
Fixed:
|
|
||||||
- Fixed missing `<typeinfo>` include https://github.com/jeremy-rifkin/cpptrace/pull/202
|
|
||||||
- Added `__cdecl` to a terminate handler to appease MSVC under some configurations https://github.com/jeremy-rifkin/cpptrace/issues/197
|
|
||||||
- Set C++ standard for cmake support checks https://github.com/jeremy-rifkin/cpptrace/issues/200
|
|
||||||
- Changed hyphens to underscores for cmake component names due to cpack issue https://github.com/jeremy-rifkin/cpptrace/issues/203
|
|
||||||
|
|
||||||
# v0.7.4
|
|
||||||
|
|
||||||
Added:
|
|
||||||
- Added `<cpptrace/version.hpp>` header with version macros
|
|
||||||
|
|
||||||
Fixes:
|
|
||||||
- Bumped libdwarf to 0.11.0 which fixes a number of dwarf 5 debug fission issues
|
|
||||||
|
|
||||||
Other:
|
|
||||||
- Various improvements to internal testing setup
|
|
||||||
|
|
||||||
# v0.7.3
|
|
||||||
|
|
||||||
Fixed:
|
|
||||||
- Fixed missing include affecting macos https://github.com/jeremy-rifkin/cpptrace/pull/183
|
|
||||||
- Fixed issue with cmake not using the ccache program found by `find_program` https://github.com/jeremy-rifkin/cpptrace/pull/184
|
|
||||||
- Fixed missing include and warnings affecting mingw https://github.com/jeremy-rifkin/cpptrace/pull/186
|
|
||||||
- Fixed issue with identifying inlined call frames when the `DW_TAG_inlined_subroutine` is under a `DW_TAG_lexical_block`
|
|
||||||
- Fixed a typo in the README
|
|
||||||
- Improved unittest support on various configurations
|
|
||||||
- Improved unittest robustness under LTO
|
|
||||||
- Fixed bug signal_demo in the event `fork()` fails
|
|
||||||
|
|
||||||
Added:
|
|
||||||
- Added color overload for `stacktrace_frame::to_string`
|
|
||||||
- Added CMake `export()` definition for cpptrace as well as a definition for libdwarf which currently doesn't provide one
|
|
||||||
|
|
||||||
Changed:
|
|
||||||
- Updated documentation surrounding the signal safe API
|
|
||||||
|
|
||||||
# v0.7.2
|
|
||||||
|
|
||||||
Changes:
|
|
||||||
- Better support for older CMake with using `FetchContent_Declare` from a URL https://github.com/jeremy-rifkin/cpptrace/pull/176
|
|
||||||
- Better portability for page size detection https://github.com/jeremy-rifkin/cpptrace/pull/177
|
|
||||||
- Improved compile times https://github.com/jeremy-rifkin/cpptrace/pull/172
|
|
||||||
- Split up `cpptrace.hpp` into finer-grained headers for lower compile time impact
|
|
||||||
- Some minor readme restructuring
|
|
||||||
|
|
||||||
# v0.7.1
|
|
||||||
|
|
||||||
Added
|
|
||||||
- Better support for finding libunwind on macos https://github.com/jeremy-rifkin/cpptrace/pull/162
|
|
||||||
- Support for libbacktrace under mingw https://github.com/jeremy-rifkin/cpptrace/pull/166
|
|
||||||
|
|
||||||
Fixed
|
|
||||||
- Computation of object address for safe object frames https://github.com/jeremy-rifkin/cpptrace/issues/169
|
|
||||||
- Nested microfmt in cpptrace's namespace due to an ODR problem with libassert https://github.com/jeremy-rifkin/libassert/issues/103
|
|
||||||
- Compilation on iOS https://github.com/jeremy-rifkin/cpptrace/pull/167
|
|
||||||
- Compilation on old MSVC https://github.com/jeremy-rifkin/cpptrace/pull/165
|
|
||||||
- Dbghelp use on 32 bit https://github.com/jeremy-rifkin/cpptrace/issues/170
|
|
||||||
- Warning in brand new cmake due to `FetchContent_Populate` being deprecated https://github.com/jeremy-rifkin/cpptrace/issues/171
|
|
||||||
|
|
||||||
Other changes
|
|
||||||
- Bumped the buffer size for execinfo and CaptureStackBackTrace to 400 frames
|
|
||||||
- Switched to execinfo.h for unwinding on clang/apple clang on macos due to `_Unwind` not working with `-fno-exceptions` https://github.com/jeremy-rifkin/cpptrace/issues/161
|
|
||||||
|
|
||||||
# v0.7.0
|
|
||||||
|
|
||||||
Added
|
|
||||||
- Added `cpptrace::from_current_exception()` and associated exception handler macros to allow tracing of all exceptions,
|
|
||||||
even without cpptrace traced exception objects.
|
|
||||||
|
|
||||||
Fixes:
|
|
||||||
- Fixed issue with using `resolve_safe_object_frame` on `safe_object_frame`s with empty paths
|
|
||||||
- Fixed handling of dwarf 4 rangelist base addresses when a `DW_AT_low_pc` is not present
|
|
||||||
- Fixed use of `-g` with MSVC
|
|
||||||
|
|
||||||
Other changes:
|
|
||||||
- Bazel is now supported on linux (https://github.com/jeremy-rifkin/cpptrace/pull/153)
|
|
||||||
- More work on testing
|
|
||||||
- Some internal refactoring
|
|
||||||
|
|
||||||
# v0.6.3
|
|
||||||
|
|
||||||
Added:
|
|
||||||
- Added a flag to disable inclusion of `<format>` by cpptrace.hpp and the definition of formatter specializations
|
|
||||||
|
|
||||||
Fixes:
|
|
||||||
- Fixed use after free during cleanup of split dwarf information https://github.com/jeremy-rifkin/cpptrace/issues/141
|
|
||||||
- Fixed an issue with TCO by clang on arm interfering with unwinding skip counts for internal methods
|
|
||||||
- Fixed issue with incorrect object addresses being reported on macos when debug maps are used
|
|
||||||
- Fixed issue with handling of split dwarf emitted by clang under dwarf4 mode
|
|
||||||
|
|
||||||
Other changes:
|
|
||||||
- Added note about signal-safe tracing requiring `_dl_find_object` to documentation and fixed errors in the signal-safe
|
|
||||||
tracing docs
|
|
||||||
- Added more configurations to unittest ci setup
|
|
||||||
- Optimized unittest ci matrix setup
|
|
||||||
- Added options for zstd and libdwarf sources if FetchContent is being used to bring the dependencies in
|
|
||||||
- Optimized includes in cpptrace.hpp
|
|
||||||
|
|
||||||
# v0.6.2
|
|
||||||
|
|
||||||
Fixes:
|
|
||||||
- Fix an issue with unwinding to collect stack traces during exception creation on arm https://github.com/jeremy-rifkin/cpptrace/issues/134
|
|
||||||
- Fix issue where `dladdr1` wasn't being used even when detected
|
|
||||||
|
|
||||||
Robustness:
|
|
||||||
- Setup more robust unit tests and added them to CI
|
|
||||||
|
|
||||||
# v0.6.1
|
|
||||||
|
|
||||||
Fixes:
|
|
||||||
- Fix for detection of `dladdr1` and `_dl_find_object` support
|
|
||||||
|
|
||||||
# v0.6.0
|
|
||||||
|
|
||||||
New:
|
|
||||||
- Added a `cpptrace::system_error` utility
|
|
||||||
- Added support for musl https://github.com/jeremy-rifkin/cpptrace/issues/128
|
|
||||||
- Added support for split dwarf / debug fission
|
|
||||||
|
|
||||||
Fixes:
|
|
||||||
- Fixed address formatting in stack traces
|
|
||||||
- Fixed frame pointer calculation for signal frames from libunwind https://github.com/jeremy-rifkin/cpptrace/issues/123
|
|
||||||
- Fixed dwarf_ranges handling of lowpc == pc causing erroneous symbol resolution
|
|
||||||
- Fixed implementation of the exception helper system/reference implementation's `lazy_trace_holder`
|
|
||||||
|
|
||||||
# v0.5.4
|
|
||||||
|
|
||||||
Fixes:
|
|
||||||
- Fixed bug with resolving object information when `dladdr` is used and an unexpected `argv[0]` is provided to the
|
|
||||||
binary.
|
|
||||||
|
|
||||||
# v0.5.3
|
|
||||||
|
|
||||||
Fixes:
|
|
||||||
- Fixed bug with formatting of hex values on MSVC
|
|
||||||
- Fixed error handling for libbacktrace back-end when debug info is not present
|
|
||||||
- Fixed bug with cmake resolution of zstd when no zstd cmake config file is installed
|
|
||||||
|
|
||||||
Other changes:
|
|
||||||
- Added error handling for an edge case in the signal tracing demo
|
|
||||||
- Updated conan recipe to allow libunwind to be chosen
|
|
||||||
- Improved msvc support in internal formatting system
|
|
||||||
- Bumped libdwarf to 0.9.2
|
|
||||||
|
|
||||||
# v0.5.2
|
|
||||||
|
|
||||||
Fixes:
|
|
||||||
- Fixed bug with resolution of inlined calls
|
|
||||||
|
|
||||||
Other changes:
|
|
||||||
- Improved internal string formatting
|
|
||||||
- Improved internal error handling
|
|
||||||
|
|
||||||
# v0.5.1
|
|
||||||
|
|
||||||
Fixes:
|
|
||||||
- Fix MSVC warning treated as error for 32-bit windows
|
|
||||||
- Fix MSVC issue with min/max macros
|
|
||||||
- Fix potential null dereference issue identified by eyalgolan1337
|
|
||||||
|
|
||||||
# v0.5.0
|
|
||||||
|
|
||||||
New:
|
|
||||||
- Traces with source code snippets with `cpptrace::stacktrace::print_with_snippets`
|
|
||||||
- Added `cpptrace::get_snippet` utility
|
|
||||||
- Added `cpptrace::can_signal_safe_unwind` utility
|
|
||||||
- Added `stacktrace_frame::get_object_info`
|
|
||||||
|
|
||||||
Changes:
|
|
||||||
- The library is now compiled with position-independent code by default
|
|
||||||
|
|
||||||
Fixes:
|
|
||||||
- Fixed issue with `_dl_find_object` implementation
|
|
||||||
|
|
||||||
Misc:
|
|
||||||
- Various refactoring, cleanup, and improvements
|
|
||||||
|
|
||||||
# v0.4.1
|
|
||||||
|
|
||||||
Changes:
|
|
||||||
- Renamed `stacktrace_frame.address` -> `stacktrace_frame.raw_address`
|
|
||||||
- Added `stacktrace_frame.object_address`
|
|
||||||
- Fixed segfault due to an edge case with dwarf file table indices
|
|
||||||
- For the libdwarf back-end: At least show object frame information if resolution fails
|
|
||||||
- Extremely small performance improvements
|
|
||||||
- Small documentation updates
|
|
||||||
- Small fix for conan
|
|
||||||
- Updated cmake to not FetchContent zstd when using CPPTRACE_USE_EXTERNAL_LIBDWARF
|
|
||||||
- CI improvements
|
|
||||||
- Test the default configuration first before doing the exhaustive and slow matrix of all configurations.
|
|
||||||
- Cleanup of duplicated prerequisite installation code
|
|
||||||
- Cleanup of built and test python scripts
|
|
||||||
|
|
||||||
# v0.4.0
|
|
||||||
|
|
||||||
What's new:
|
|
||||||
- Cpptrace now has a C API! 🎉
|
|
||||||
- Cpptrace is now able to parse macOS debug maps and resolve stack traces without dSYM files
|
|
||||||
|
|
||||||
Most notable improvements:
|
|
||||||
- Updated cpptrace exception objects to generate traces at the callsite for improved consistency with trace output. As
|
|
||||||
part of this cpptrace exception objects have had their constructors updated.
|
|
||||||
- Improved dwarf back-end robustness
|
|
||||||
- Fallback to the compilation-unit cache or walking compilation-units if aranges lookup fails
|
|
||||||
- Eliminated reliance on a CMake-generated export header
|
|
||||||
- Added a configuration to control resolution of inlined function calls
|
|
||||||
- Made architecture selection in Mach-O universal binaries
|
|
||||||
|
|
||||||
Other improvements:
|
|
||||||
- Improved documentation for installation and usage
|
|
||||||
- Generally improved README content and organization
|
|
||||||
- Fixed an MSVC workaround producing dozens of warnings
|
|
||||||
- Better handle compiler color diagnostic arguments if compiler families differ
|
|
||||||
- Improvements for handling libdwarf's header placement
|
|
||||||
- Fixed issue with libunwind resolution
|
|
||||||
- `-Werror` is now used in CI
|
|
||||||
- More library configurations are now tested in CI
|
|
||||||
- Updated to libdwarf 9
|
|
||||||
- Updated the library's CMake to acquire zstd through FetchContent
|
|
||||||
- Fixed minor issue with stacktrace printing always trying to enable virtual terminal processing, even when not actually
|
|
||||||
printing to the terminal.
|
|
||||||
|
|
||||||
# v0.3.1
|
|
||||||
|
|
||||||
Tiny patch:
|
|
||||||
- Fix `CPPTRACE_EXPORT` annotations
|
|
||||||
- Add workaround for [msvc bug][msvc bug] affecting msvc 19.38.
|
|
||||||
|
|
||||||
[msvc bug]: https://developercommunity.visualstudio.com/t/MSVC-1938331290-preview-fails-to-comp/10505565
|
|
||||||
|
|
||||||
# v0.3.0
|
|
||||||
|
|
||||||
Interface Changes:
|
|
||||||
- Overhauled the API for traced `cpptrace::exception` objects
|
|
||||||
- Added `cpptrace::isatty` utility
|
|
||||||
- Added specialized `std::terminate` handler and `cpptrace::register_terminate_handler` utility
|
|
||||||
- Added `cpptrace::frame_ptr` as an alias for the appropriate type capable of representing an instruction pointer
|
|
||||||
- Added signal-safe tracing support and a guide for [how to trace safely](signal-safe-tracing.md)
|
|
||||||
- Added `cpptrace::nullable<T>` utility for better indicating when line / column information is not present
|
|
||||||
- Added `CPPTRACE_FORCE_NO_INLINE` utility macro to cpptrace.hpp
|
|
||||||
- Added `CPPTRACE_WRAP` and `CPPTRACE_WRAP_BLOCK` utilities to catch non-`cpptrace::exception`s and rethrow wrapped in a
|
|
||||||
traced exception.
|
|
||||||
- Updated `cpptrace::stacktrace::to_string` to take a `bool color` parameter
|
|
||||||
- Eliminated uses of `std::uint_least32_t` in favor of other types
|
|
||||||
- Updated `object_frame` data member names
|
|
||||||
|
|
||||||
Other changes:
|
|
||||||
- Added object resolution with `_dl_find_object` which is much faster than `dladdr`
|
|
||||||
- Added column support for dwarf
|
|
||||||
- Added inlined call resolution
|
|
||||||
- Added `cpptrace::stacktrace_frame::is_inline`
|
|
||||||
- Added libunwind as a back-end
|
|
||||||
- Unbundled libdwarf
|
|
||||||
- Increased hard max frame count, used by some back-end requiring fixed buffers, from 100 to 200
|
|
||||||
- Improved libgcc unwind backend
|
|
||||||
- Improved trace output when information is missing
|
|
||||||
- Added a lookup table for faster dwarf line information lookup
|
|
||||||
|
|
||||||
News:
|
|
||||||
- The library is now on conan and vcpkg
|
|
||||||
|
|
||||||
Minor changes:
|
|
||||||
- Assorted bug fixes
|
|
||||||
- Various code quality improvements
|
|
||||||
- CI improvements
|
|
||||||
- Documentation improvements
|
|
||||||
- CMake improvements
|
|
||||||
- Internal refactoring
|
|
||||||
|
|
||||||
# v0.2.1
|
|
||||||
|
|
||||||
Patches:
|
|
||||||
- Fixed uintptr_t implicit conversion issue for msvc
|
|
||||||
- Better handling for PIC and static linkage in CMake
|
|
||||||
- Added gcc 5 support
|
|
||||||
- Various warning fixes
|
|
||||||
- Added stackwalk64 support for 32-bit x86 mingw/clang and architecture detection
|
|
||||||
- Added check for stackwalk64 support and CaptureStackBacktrace as a fallback
|
|
||||||
- Various cmake cleanup and changes to use cpptrace through package managers
|
|
||||||
- Added sonarlint and implemented some sonarlint fixes
|
|
||||||
|
|
||||||
# v0.2.0
|
|
||||||
|
|
||||||
Key changes:
|
|
||||||
- Added libdwarf as a back-end so cpptrace doesn't have to rely on addr2line or libbacktrace
|
|
||||||
- Overhauled library's public-facing interface to make the library more useful
|
|
||||||
- Added `raw_trace` interface
|
|
||||||
- Added `object_trace` interface
|
|
||||||
- Added `stacktrace` interface
|
|
||||||
- Updated `generate_trace` to return a `stacktrace` rather than a vector of frames
|
|
||||||
- Added `generate_trace` counterparts for raw and object traces
|
|
||||||
- Added `generate_trace` overloads with max_depth
|
|
||||||
- Added interface for internal demangling utility
|
|
||||||
- Added cache mode configuration
|
|
||||||
- Added option to absorb internal trace exceptions (by default it absorbs)
|
|
||||||
- Added `cpptrace::exception`, which automatically generates and stores a stacktrace when thrown
|
|
||||||
- Added `exception_with_message`
|
|
||||||
- Added traced analogs for stdexcept errors: `logic_error`, `domain_error`, `invalid_argument`, `length_error`,
|
|
||||||
`out_of_range`, `runtime_error`, `range_error`, `overflow_error`, and `underflow_error`.
|
|
||||||
|
|
||||||
Other changes:
|
|
||||||
- Bundled libdwarf with cpptrace so the library can essentially be self-contained and not have to rely on libraries that
|
|
||||||
might not already be on a system
|
|
||||||
- Added StackWalk64 as an unwinding back-end on windows
|
|
||||||
- Added system for multiple symbol back-ends to be used, mainly for more complete stack traces on mingw
|
|
||||||
- Fixed sporadic line number reporting errors due to not adjusting the program counter from the unwinder
|
|
||||||
- Improved addr2line/atos invocation back-end on macos
|
|
||||||
- Lots of error handling improvements
|
|
||||||
- Performance improvements
|
|
||||||
- Updated default back-ends for most systems
|
|
||||||
- Removed full tracing backends
|
|
||||||
- Cleaned up library cmake
|
|
||||||
- Lots of internal cleanup and refactoring
|
|
||||||
- Improved library usage instructions in README
|
|
||||||
|
|
||||||
# v0.1.1
|
|
||||||
|
|
||||||
Fixed:
|
|
||||||
- Handle errors when object files don't exist or can't be opened for reading
|
|
||||||
- Handle paths with spaces when using addr2line on windows
|
|
||||||
|
|
||||||
# v0.1
|
|
||||||
|
|
||||||
Initial release of the library 🎉
|
|
||||||
788
CMakeLists.txt
788
CMakeLists.txt
@ -1,55 +1,48 @@
|
|||||||
cmake_minimum_required(VERSION 3.14)
|
cmake_minimum_required(VERSION 3.4)
|
||||||
|
|
||||||
include(cmake/PreventInSourceBuilds.cmake)
|
if(${CMAKE_VERSION} VERSION_LESS 3.12)
|
||||||
|
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
||||||
# ---- Initialize Project ----
|
endif()
|
||||||
|
|
||||||
# Used to support find_package
|
|
||||||
set(package_name "cpptrace")
|
|
||||||
|
|
||||||
project(
|
project(
|
||||||
cpptrace
|
cpptrace
|
||||||
VERSION 0.8.2
|
VERSION 0.1.0
|
||||||
DESCRIPTION "Simple, portable, and self-contained stacktrace library for C++11 and newer "
|
LANGUAGES CXX
|
||||||
HOMEPAGE_URL "https://github.com/jeremy-rifkin/cpptrace"
|
|
||||||
LANGUAGES C CXX
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Don't change include order, OptionVariables checks if project is top level
|
|
||||||
include(cmake/ProjectIsTopLevel.cmake)
|
|
||||||
include(cmake/OptionVariables.cmake)
|
|
||||||
|
|
||||||
include(GNUInstallDirs)
|
include(GNUInstallDirs)
|
||||||
include(CheckCXXSourceCompiles)
|
include(CheckCXXSourceCompiles)
|
||||||
include(CheckCXXCompilerFlag)
|
include(CheckCXXCompilerFlag)
|
||||||
|
|
||||||
if(PROJECT_IS_TOP_LEVEL)
|
option(CPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE "" OFF)
|
||||||
find_program(CCACHE_PROGRAM ccache)
|
option(CPPTRACE_FULL_TRACE_WITH_STACKTRACE "" OFF)
|
||||||
if(CCACHE_PROGRAM)
|
|
||||||
set(CMAKE_C_COMPILER_LAUNCHER ${CCACHE_PROGRAM})
|
|
||||||
set(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE_PROGRAM})
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(PROJECT_IS_TOP_LEVEL)
|
option(CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE "" OFF)
|
||||||
if(CMAKE_GENERATOR STREQUAL "Ninja")
|
option(CPPTRACE_GET_SYMBOLS_WITH_LIBDL "" OFF)
|
||||||
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
option(CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE "" OFF)
|
||||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdiagnostics-color=always")
|
option(CPPTRACE_GET_SYMBOLS_WITH_DBGHELP "" OFF)
|
||||||
elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang")
|
option(CPPTRACE_GET_SYMBOLS_WITH_NOTHING "" OFF)
|
||||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcolor-diagnostics")
|
|
||||||
endif()
|
|
||||||
if("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
|
|
||||||
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fdiagnostics-color=always")
|
|
||||||
elseif("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang")
|
|
||||||
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fcolor-diagnostics")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(CPPTRACE_SANITIZER_BUILD)
|
option(CPPTRACE_UNWIND_WITH_UNWIND "" OFF)
|
||||||
add_compile_options(-fsanitize=address)
|
option(CPPTRACE_UNWIND_WITH_EXECINFO "" OFF)
|
||||||
add_link_options(-fsanitize=address)
|
option(CPPTRACE_UNWIND_WITH_WINAPI "" OFF)
|
||||||
endif()
|
option(CPPTRACE_UNWIND_WITH_NOTHING "" OFF)
|
||||||
|
|
||||||
|
option(CPPTRACE_DEMANGLE_WITH_CXXABI "" OFF)
|
||||||
|
option(CPPTRACE_DEMANGLE_WITH_NOTHING "" OFF)
|
||||||
|
|
||||||
|
set(CPPTRACE_BACKTRACE_PATH "" CACHE STRING "Path to backtrace.h, if the compiler doesn't already know it. Check /usr/lib/gcc/x86_64-linux-gnu/*/include.")
|
||||||
|
set(CPPTRACE_HARD_MAX_FRAMES "" CACHE STRING "Hard limit on unwinding depth. Default is 100.")
|
||||||
|
set(CPPTRACE_ADDR2LINE_PATH "" CACHE STRING "Absolute path to the addr2line executable you want to use.")
|
||||||
|
option(CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH "" OFF)
|
||||||
|
|
||||||
|
option(CPPTRACE_BUILD_TEST "" OFF)
|
||||||
|
option(CPPTRACE_BUILD_DEMO "" OFF)
|
||||||
|
option(CPPTRACE_BUILD_TEST_RDYNAMIC "" OFF)
|
||||||
|
|
||||||
|
option(CPPTRACE_BUILD_SPEEDTEST "" OFF)
|
||||||
|
option(CPPTRACE_BUILD_SPEEDTEST_DWARF4 "" OFF)
|
||||||
|
option(CPPTRACE_BUILD_SPEEDTEST_DWARF5 "" OFF)
|
||||||
|
|
||||||
if(NOT "${CPPTRACE_BACKTRACE_PATH}" STREQUAL "")
|
if(NOT "${CPPTRACE_BACKTRACE_PATH}" STREQUAL "")
|
||||||
# quotes used over <> because of a macro substitution issue where
|
# quotes used over <> because of a macro substitution issue where
|
||||||
@ -65,222 +58,264 @@ else()
|
|||||||
set(CPPTRACE_BACKTRACE_PATH_DEFINITION "")
|
set(CPPTRACE_BACKTRACE_PATH_DEFINITION "")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# ========================================== Platform Support and Auto-config ==========================================
|
function(check_support var source includes libraries definitions)
|
||||||
include(cmake/Autoconfig.cmake)
|
set(CMAKE_REQUIRED_INCLUDES "${includes}")
|
||||||
|
list(APPEND CMAKE_REQUIRED_INCLUDES "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||||
|
set(CMAKE_REQUIRED_LIBRARIES "${libraries}")
|
||||||
|
set(CMAKE_REQUIRED_DEFINITIONS "${definitions}")
|
||||||
|
string(CONCAT full_source "#include \"${source}\"" ${nonce})
|
||||||
|
check_cxx_source_compiles(${full_source} ${var})
|
||||||
|
set(${var} ${${var}} PARENT_SCOPE)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
# =================================================== Library Setup ====================================================
|
if(MINGW OR NOT WIN32) # No need to bother checking in msvc, but do check in minngw
|
||||||
|
check_support(HAS_UNWIND has_unwind.cpp "" "" "")
|
||||||
if(NOT CPPTRACE_BUILD_NO_SYMBOLS)
|
check_support(HAS_EXECINFO has_execinfo.cpp "" "" "")
|
||||||
set(
|
check_support(HAS_BACKTRACE has_backtrace.cpp "" "backtrace" "${CPPTRACE_BACKTRACE_PATH_DEFINITION}")
|
||||||
debug
|
check_support(HAS_DL has_dl.cpp "" "dl" "")
|
||||||
$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-g>
|
check_support(HAS_CXXABI has_cxxabi.cpp "" "" "")
|
||||||
$<$<CXX_COMPILER_ID:MSVC>:/DEBUG>
|
if(NOT MSVC)
|
||||||
)
|
set(STACKTRACE_LINK_LIB "stdc++_libbacktrace")
|
||||||
else()
|
else()
|
||||||
add_compile_options($<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-g0>)
|
set(STACKTRACE_LINK_LIB "")
|
||||||
set(
|
endif()
|
||||||
debug
|
check_support(HAS_STACKTRACE has_stacktrace.cpp "" "${STACKTRACE_LINK_LIB}" "")
|
||||||
)
|
if(APPLE)
|
||||||
|
find_program(ADDR2LINE_PATH atos PATHS ENV PATH)
|
||||||
|
else()
|
||||||
|
find_program(ADDR2LINE_PATH addr2line PATHS ENV PATH)
|
||||||
|
endif()
|
||||||
|
if("${ADDR2LINE_PATH}" STREQUAL "ADDR2LINE_PATH-NOTFOUND")
|
||||||
|
set(HAS_ADDR2LINE FALSE)
|
||||||
|
else()
|
||||||
|
set(HAS_ADDR2LINE TRUE)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Target that we can modify (can't modify ALIAS targets)
|
# =============================================== Autoconfig full dump ===============================================
|
||||||
# Target name should not be the same as ${PROJECT_NAME}, causes add_subdirectory issues
|
# If nothing is specified, attempt to use libbacktrace's full dump
|
||||||
set(target_name "cpptrace-lib")
|
if(
|
||||||
add_library(${target_name} ${build_type})
|
NOT (
|
||||||
|
CPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE OR
|
||||||
|
CPPTRACE_FULL_TRACE_WITH_STACKTRACE OR
|
||||||
|
CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE OR
|
||||||
|
CPPTRACE_GET_SYMBOLS_WITH_LIBDL OR
|
||||||
|
CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE OR
|
||||||
|
CPPTRACE_GET_SYMBOLS_WITH_DBGHELP OR
|
||||||
|
CPPTRACE_GET_SYMBOLS_WITH_NOTHING OR
|
||||||
|
CPPTRACE_UNWIND_WITH_UNWIND OR
|
||||||
|
CPPTRACE_UNWIND_WITH_EXECINFO OR
|
||||||
|
CPPTRACE_UNWIND_WITH_WINAPI OR
|
||||||
|
CPPTRACE_UNWIND_WITH_NOTHING
|
||||||
|
)
|
||||||
|
)
|
||||||
|
# Attempt to auto-config
|
||||||
|
if(MINGW OR NOT WIN32) # Our trace is better than msvc's <stacktrace>
|
||||||
|
if(HAS_STACKTRACE)
|
||||||
|
set(CPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE On)
|
||||||
|
message(STATUS "Cpptrace auto config: Using C++23 <stacktrace> for the full trace")
|
||||||
|
elseif(HAS_BACKTRACE AND NOT WIN32) # Mingw libbacktrace doesn't seem to be working
|
||||||
|
set(CPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE On)
|
||||||
|
message(STATUS "Cpptrace auto config: Using libbacktrace for the full trace")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
# Alias to cause error at configuration time instead of link time if target is missing
|
# =============================================== Autoconfig unwinding ===============================================
|
||||||
add_library(cpptrace::cpptrace ALIAS ${target_name})
|
# Unwind back-ends (If not doing CPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE)
|
||||||
|
if(
|
||||||
|
NOT (
|
||||||
|
CPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE OR
|
||||||
|
CPPTRACE_FULL_TRACE_WITH_STACKTRACE OR
|
||||||
|
CPPTRACE_UNWIND_WITH_UNWIND OR
|
||||||
|
CPPTRACE_UNWIND_WITH_EXECINFO OR
|
||||||
|
CPPTRACE_UNWIND_WITH_WINAPI OR
|
||||||
|
CPPTRACE_UNWIND_WITH_NOTHING
|
||||||
|
)
|
||||||
|
)
|
||||||
|
# Attempt to auto-config
|
||||||
|
if(UNIX)
|
||||||
|
if(HAS_UNWIND)
|
||||||
|
set(CPPTRACE_UNWIND_WITH_UNWIND On)
|
||||||
|
message(STATUS "Cpptrace auto config: Using libgcc unwind for unwinding")
|
||||||
|
elseif(HAS_EXECINFO)
|
||||||
|
set(CPPTRACE_UNWIND_WITH_EXECINFO On)
|
||||||
|
message(STATUS "Cpptrace auto config: Using execinfo.h for unwinding")
|
||||||
|
else()
|
||||||
|
set(CPPTRACE_UNWIND_WITH_NOTHING On)
|
||||||
|
message(FATAL_ERROR "Cpptrace auto config: No unwinding back-end seems to be supported, stack tracing will not work. To compile anyway set CPPTRACE_UNWIND_WITH_NOTHING.")
|
||||||
|
endif()
|
||||||
|
elseif(MINGW)
|
||||||
|
if(HAS_UNWIND)
|
||||||
|
set(CPPTRACE_UNWIND_WITH_UNWIND On)
|
||||||
|
message(STATUS "Cpptrace auto config: Using libgcc unwind for unwinding")
|
||||||
|
elseif(HAS_EXECINFO)
|
||||||
|
set(CPPTRACE_UNWIND_WITH_EXECINFO On)
|
||||||
|
message(STATUS "Cpptrace auto config: Using execinfo.h for unwinding")
|
||||||
|
else()
|
||||||
|
set(CPPTRACE_UNWIND_WITH_WINAPI On)
|
||||||
|
message(STATUS "Cpptrace auto config: Using winapi for unwinding")
|
||||||
|
endif()
|
||||||
|
elseif(WIN32)
|
||||||
|
set(CPPTRACE_UNWIND_WITH_WINAPI On)
|
||||||
|
message(STATUS "Cpptrace auto config: Using winapi for unwinding")
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
#message(STATUS "MANUAL CONFIG SPECIFIED")
|
||||||
|
endif()
|
||||||
|
|
||||||
# Add /include files to target
|
# =============================================== Autoconfig symbols ===============================================
|
||||||
# This is solely for IDE benefit, doesn't affect building
|
# Symbol back-ends (If not doing CPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE)
|
||||||
target_sources(
|
if(
|
||||||
${target_name} PRIVATE
|
NOT (
|
||||||
include/cpptrace/cpptrace.hpp
|
CPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE OR
|
||||||
include/ctrace/ctrace.h
|
CPPTRACE_FULL_TRACE_WITH_STACKTRACE OR
|
||||||
|
CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE OR
|
||||||
|
CPPTRACE_GET_SYMBOLS_WITH_LIBDL OR
|
||||||
|
CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE OR
|
||||||
|
CPPTRACE_GET_SYMBOLS_WITH_DBGHELP OR
|
||||||
|
CPPTRACE_GET_SYMBOLS_WITH_NOTHING
|
||||||
|
)
|
||||||
|
)
|
||||||
|
# Attempt to auto-config
|
||||||
|
if(UNIX OR MINGW)
|
||||||
|
if(HAS_BACKTRACE AND NOT MINGW) # not working on mingw at the moment
|
||||||
|
set(CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE On)
|
||||||
|
message(STATUS "Cpptrace auto config: Using libbacktrace for symbols")
|
||||||
|
elseif(HAS_ADDR2LINE)
|
||||||
|
set(CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE On)
|
||||||
|
message(STATUS "Cpptrace auto config: Using addr2line for symbols")
|
||||||
|
elseif(HAS_DL)
|
||||||
|
set(CPPTRACE_GET_SYMBOLS_WITH_LIBDL On)
|
||||||
|
message(STATUS "Cpptrace auto config: Using libdl for symbols")
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "Cpptrace auto config: No symbol back-end could be automatically configured. To compile anyway set CPPTRACE_GET_SYMBOLS_WITH_NOTHING.")
|
||||||
|
endif()
|
||||||
|
elseif(WIN32)
|
||||||
|
set(CPPTRACE_GET_SYMBOLS_WITH_DBGHELP On)
|
||||||
|
message(STATUS "Cpptrace auto config: Using dbghelp for symbols")
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
#message(STATUS "MANUAL CONFIG SPECIFIED")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# =============================================== Autoconfig demangling ===============================================
|
||||||
|
# Handle demangle configuration
|
||||||
|
if(
|
||||||
|
NOT (
|
||||||
|
CPPTRACE_DEMANGLE_WITH_CXXABI OR
|
||||||
|
CPPTRACE_DEMANGLE_WITH_NOTHING
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if(HAS_CXXABI)
|
||||||
|
set(CPPTRACE_DEMANGLE_WITH_CXXABI On)
|
||||||
|
else()
|
||||||
|
set(CPPTRACE_DEMANGLE_WITH_NOTHING On)
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
#message(STATUS "Manual demangling back-end specified")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# =============================================== Now define the library ===============================================
|
||||||
|
|
||||||
|
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
|
||||||
|
|
||||||
|
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||||
|
|
||||||
|
set(
|
||||||
|
sources
|
||||||
|
src/cpptrace.cpp
|
||||||
|
src/demangle/demangle_with_cxxabi.cpp
|
||||||
|
src/demangle/demangle_with_nothing.cpp
|
||||||
|
src/full/full_trace_with_libbacktrace.cpp
|
||||||
|
src/full/full_trace_with_stacktrace.cpp
|
||||||
|
src/symbols/symbols_with_addr2line.cpp
|
||||||
|
src/symbols/symbols_with_dbghelp.cpp
|
||||||
|
src/symbols/symbols_with_dl.cpp
|
||||||
|
src/symbols/symbols_with_libbacktrace.cpp
|
||||||
|
src/symbols/symbols_with_nothing.cpp
|
||||||
|
src/unwind/unwind_with_execinfo.cpp
|
||||||
|
src/unwind/unwind_with_nothing.cpp
|
||||||
|
src/unwind/unwind_with_unwind.cpp
|
||||||
|
src/unwind/unwind_with_winapi.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
# add /src files to target
|
# TODO: This feels like a hack.
|
||||||
target_sources(
|
if(CPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE OR CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE)
|
||||||
${target_name} PRIVATE
|
add_library(cpptrace SHARED ${sources} include/cpptrace/cpptrace.hpp)
|
||||||
# src
|
else()
|
||||||
src/binary/elf.cpp
|
add_library(cpptrace ${sources} include/cpptrace/cpptrace.hpp)
|
||||||
src/binary/mach-o.cpp
|
endif()
|
||||||
src/binary/module_base.cpp
|
|
||||||
src/binary/object.cpp
|
|
||||||
src/binary/pe.cpp
|
|
||||||
src/binary/safe_dl.cpp
|
|
||||||
src/cpptrace.cpp
|
|
||||||
src/ctrace.cpp
|
|
||||||
src/exceptions.cpp
|
|
||||||
src/from_current.cpp
|
|
||||||
src/formatting.cpp
|
|
||||||
src/options.cpp
|
|
||||||
src/utils.cpp
|
|
||||||
src/demangle/demangle_with_cxxabi.cpp
|
|
||||||
src/demangle/demangle_with_nothing.cpp
|
|
||||||
src/demangle/demangle_with_winapi.cpp
|
|
||||||
src/snippets/snippet.cpp
|
|
||||||
src/symbols/dwarf/debug_map_resolver.cpp
|
|
||||||
src/symbols/dwarf/dwarf_options.cpp
|
|
||||||
src/symbols/dwarf/dwarf_resolver.cpp
|
|
||||||
src/symbols/symbols_core.cpp
|
|
||||||
src/symbols/symbols_with_addr2line.cpp
|
|
||||||
src/symbols/symbols_with_dbghelp.cpp
|
|
||||||
src/symbols/symbols_with_dl.cpp
|
|
||||||
src/symbols/symbols_with_libbacktrace.cpp
|
|
||||||
src/symbols/symbols_with_libdwarf.cpp
|
|
||||||
src/symbols/symbols_with_nothing.cpp
|
|
||||||
src/unwind/unwind_with_dbghelp.cpp
|
|
||||||
src/unwind/unwind_with_execinfo.cpp
|
|
||||||
src/unwind/unwind_with_libunwind.cpp
|
|
||||||
src/unwind/unwind_with_nothing.cpp
|
|
||||||
src/unwind/unwind_with_unwind.cpp
|
|
||||||
src/unwind/unwind_with_winapi.cpp
|
|
||||||
src/utils/microfmt.cpp
|
|
||||||
src/utils/utils.cpp
|
|
||||||
src/platform/dbghelp_utils.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
target_include_directories(
|
target_include_directories(
|
||||||
${target_name}
|
cpptrace
|
||||||
PUBLIC
|
PUBLIC
|
||||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/>
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/>
|
||||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/cpptrace/>
|
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/cpptrace/>
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(
|
# TODO
|
||||||
${target_name}
|
target_compile_features(cpptrace PRIVATE cxx_range_for cxx_constexpr cxx_nullptr cxx_static_assert)
|
||||||
PRIVATE
|
|
||||||
src
|
|
||||||
)
|
|
||||||
|
|
||||||
set(
|
set_target_properties(
|
||||||
warning_options
|
cpptrace
|
||||||
$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wall -Wextra -Werror=return-type -Wundef>
|
PROPERTIES
|
||||||
$<$<CXX_COMPILER_ID:GNU>:-Wuseless-cast -Wmaybe-uninitialized>
|
CXX_STANDARD_REQUIRED TRUE
|
||||||
$<$<CXX_COMPILER_ID:MSVC>:/W4 /permissive->
|
CXX_EXTENSIONS OFF
|
||||||
)
|
)
|
||||||
|
|
||||||
if(CPPTRACE_WERROR_BUILD)
|
|
||||||
set(
|
|
||||||
warning_options
|
|
||||||
${warning_options}
|
|
||||||
$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Werror -Wpedantic>
|
|
||||||
$<$<CXX_COMPILER_ID:MSVC>:/WX>
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
target_compile_options(
|
target_compile_options(
|
||||||
${target_name}
|
cpptrace
|
||||||
PRIVATE
|
PRIVATE
|
||||||
${warning_options}
|
$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wall -Wextra -Werror=return-type -Wshadow -Wundef>
|
||||||
|
$<$<CXX_COMPILER_ID:GNU>:-Wuseless-cast -Wnonnull-compare>
|
||||||
|
$<$<CXX_COMPILER_ID:MSVC>:/W4 /WX /permissive->
|
||||||
)
|
)
|
||||||
|
|
||||||
set(CPPTRACE_VERSION_MAJOR ${CMAKE_PROJECT_VERSION_MAJOR})
|
# =============================================== Apply options to build ===============================================
|
||||||
set(CPPTRACE_VERSION_MINOR ${CMAKE_PROJECT_VERSION_MINOR})
|
|
||||||
set(CPPTRACE_VERSION_PATCH ${CMAKE_PROJECT_VERSION_PATCH})
|
|
||||||
configure_file("${PROJECT_SOURCE_DIR}/cmake/in/version-hpp.in" "${PROJECT_BINARY_DIR}/include/cpptrace/version.hpp")
|
|
||||||
|
|
||||||
# ---- Generate Build Info Headers ----
|
function(check_backtrace_error)
|
||||||
|
if(NOT HAS_BACKTRACE)
|
||||||
|
if(NOT "${CPPTRACE_BACKTRACE_PATH}" STREQUAL "")
|
||||||
|
message(WARNING "Cpptrace: CPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE specified but libbacktrace doesn't appear installed or configured properly.")
|
||||||
|
else()
|
||||||
|
message(WARNING "Cpptrace: CPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE specified but libbacktrace doesn't appear installed or configured properly. You may need to specify CPPTRACE_BACKTRACE_PATH.")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
if(build_type STREQUAL "STATIC")
|
# Full
|
||||||
target_compile_definitions(${target_name} PUBLIC CPPTRACE_STATIC_DEFINE)
|
if(CPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE)
|
||||||
set(CPPTRACE_STATIC_DEFINE TRUE)
|
check_backtrace_error()
|
||||||
|
target_compile_definitions(cpptrace PUBLIC CPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE)
|
||||||
|
target_link_libraries(cpptrace PRIVATE backtrace)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# ---- Library Properties ----
|
if(CPPTRACE_FULL_TRACE_WITH_STACKTRACE)
|
||||||
|
if(NOT HAS_STACKTRACE)
|
||||||
# Hide all symbols by default
|
message(WARNING "Cpptrace: CPPTRACE_FULL_TRACE_WITH_STACKTRACE specified but <stacktrace> doesn't seem to be available.")
|
||||||
# Use SameMajorVersion versioning for shared library runtime linker lookup
|
endif()
|
||||||
set_target_properties(
|
target_compile_definitions(cpptrace PUBLIC CPPTRACE_FULL_TRACE_WITH_STACKTRACE)
|
||||||
${target_name} PROPERTIES
|
target_link_libraries(cpptrace PRIVATE "${STACKTRACE_LINK_LIB}")
|
||||||
CXX_VISIBILITY_PRESET hidden
|
|
||||||
VISIBILITY_INLINES_HIDDEN YES
|
|
||||||
VERSION "${PROJECT_VERSION}"
|
|
||||||
SOVERSION "${PROJECT_VERSION_MAJOR}"
|
|
||||||
EXPORT_NAME "cpptrace"
|
|
||||||
OUTPUT_NAME "cpptrace"
|
|
||||||
POSITION_INDEPENDENT_CODE ${CPPTRACE_POSITION_INDEPENDENT_CODE}
|
|
||||||
)
|
|
||||||
|
|
||||||
# Header files generated by CMake
|
|
||||||
target_include_directories(
|
|
||||||
${target_name} SYSTEM PUBLIC
|
|
||||||
"$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Header files from /include
|
|
||||||
target_include_directories(
|
|
||||||
${target_name} ${warning_guard} PUBLIC
|
|
||||||
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Require C++11 support
|
|
||||||
target_compile_features(
|
|
||||||
${target_name}
|
|
||||||
PRIVATE cxx_std_11
|
|
||||||
)
|
|
||||||
|
|
||||||
target_compile_definitions(${target_name} PRIVATE NOMINMAX)
|
|
||||||
|
|
||||||
if(HAS_ATTRIBUTE_PACKED)
|
|
||||||
target_compile_definitions(${target_name} PRIVATE HAS_ATTRIBUTE_PACKED)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NOT CPPTRACE_STD_FORMAT)
|
|
||||||
target_compile_definitions(${target_name} PUBLIC CPPTRACE_NO_STD_FORMAT)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(CPPTRACE_UNPREFIXED_TRY_CATCH)
|
|
||||||
target_compile_definitions(${target_name} PUBLIC CPPTRACE_UNPREFIXED_TRY_CATCH)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
|
|
||||||
SET(CMAKE_C_ARCHIVE_FINISH "<CMAKE_RANLIB> -no_warning_for_no_symbols -c <TARGET>")
|
|
||||||
SET(CMAKE_CXX_ARCHIVE_FINISH "<CMAKE_RANLIB> -no_warning_for_no_symbols -c <TARGET>")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# =================================================== Back-end setup ===================================================
|
|
||||||
|
|
||||||
if(HAS_CXX_EXCEPTION_TYPE)
|
|
||||||
target_compile_definitions(${target_name} PUBLIC CPPTRACE_HAS_CXX_EXCEPTION_TYPE)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(HAS_DL_FIND_OBJECT)
|
|
||||||
target_compile_definitions(${target_name} PUBLIC CPPTRACE_HAS_DL_FIND_OBJECT)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(HAS_DLADDR1)
|
|
||||||
target_compile_definitions(${target_name} PUBLIC CPPTRACE_HAS_DLADDR1)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(HAS_MACH_VM)
|
|
||||||
target_compile_definitions(${target_name} PUBLIC HAS_MACH_VM)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Symbols
|
# Symbols
|
||||||
if(CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE)
|
if(CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE)
|
||||||
if(NOT HAS_BACKTRACE)
|
check_backtrace_error()
|
||||||
if(NOT "${CPPTRACE_BACKTRACE_PATH}" STREQUAL "")
|
target_compile_definitions(cpptrace PUBLIC CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE)
|
||||||
message(WARNING "Cpptrace: Using libbacktrace for symbols but libbacktrace doesn't appear installed or configured properly.")
|
target_link_libraries(cpptrace PRIVATE backtrace)
|
||||||
else()
|
|
||||||
message(WARNING "Cpptrace: Using libbacktrace for symbols but libbacktrace doesn't appear installed or configured properly. You may need to specify CPPTRACE_BACKTRACE_PATH.")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
target_compile_definitions(${target_name} PUBLIC CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE)
|
|
||||||
target_link_libraries(${target_name} PRIVATE backtrace ${CMAKE_DL_LIBS})
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(CPPTRACE_GET_SYMBOLS_WITH_LIBDL)
|
if(CPPTRACE_GET_SYMBOLS_WITH_LIBDL)
|
||||||
target_compile_definitions(${target_name} PUBLIC CPPTRACE_GET_SYMBOLS_WITH_LIBDL)
|
target_compile_definitions(cpptrace PUBLIC CPPTRACE_GET_SYMBOLS_WITH_LIBDL)
|
||||||
target_link_libraries(${target_name} PRIVATE ${CMAKE_DL_LIBS})
|
target_link_libraries(cpptrace PRIVATE dl)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE)
|
if(CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE)
|
||||||
# set(CPPTRACE_ADDR2LINE_PATH "" CACHE STRING "Absolute path to the addr2line executable you want to use.")
|
# set(CPPTRACE_ADDR2LINE_PATH "" CACHE STRING "Absolute path to the addr2line executable you want to use.")
|
||||||
# option(CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH "" OFF)
|
# option(CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH "" OFF)
|
||||||
if(CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH)
|
if(CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH)
|
||||||
target_compile_definitions(${target_name} PUBLIC CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH)
|
target_compile_definitions(cpptrace PUBLIC CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH)
|
||||||
else()
|
else()
|
||||||
if("${CPPTRACE_ADDR2LINE_PATH}" STREQUAL "")
|
if("${CPPTRACE_ADDR2LINE_PATH}" STREQUAL "")
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
@ -292,132 +327,21 @@ if(CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE)
|
|||||||
set(CPPTRACE_ADDR2LINE_PATH_FINAL "${CPPTRACE_ADDR2LINE_PATH}")
|
set(CPPTRACE_ADDR2LINE_PATH_FINAL "${CPPTRACE_ADDR2LINE_PATH}")
|
||||||
endif()
|
endif()
|
||||||
message(STATUS "Cpptrace: Using ${CPPTRACE_ADDR2LINE_PATH_FINAL} for addr2line path")
|
message(STATUS "Cpptrace: Using ${CPPTRACE_ADDR2LINE_PATH_FINAL} for addr2line path")
|
||||||
target_compile_definitions(${target_name} PUBLIC CPPTRACE_ADDR2LINE_PATH="${CPPTRACE_ADDR2LINE_PATH_FINAL}")
|
target_compile_definitions(cpptrace PUBLIC CPPTRACE_ADDR2LINE_PATH="${CPPTRACE_ADDR2LINE_PATH_FINAL}")
|
||||||
endif()
|
endif()
|
||||||
target_compile_definitions(${target_name} PUBLIC CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE)
|
target_compile_definitions(cpptrace PUBLIC CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE)
|
||||||
if(UNIX)
|
if(UNIX)
|
||||||
target_link_libraries(${target_name} PRIVATE ${CMAKE_DL_LIBS})
|
target_link_libraries(cpptrace PRIVATE dl)
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF)
|
|
||||||
target_compile_definitions(${target_name} PUBLIC CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF)
|
|
||||||
if(CPPTRACE_USE_EXTERNAL_LIBDWARF)
|
|
||||||
if(NOT CPPTRACE_FIND_LIBDWARF_WITH_PKGCONFIG)
|
|
||||||
find_package(libdwarf REQUIRED)
|
|
||||||
else()
|
|
||||||
find_package(PkgConfig)
|
|
||||||
pkg_check_modules(LIBDWARF REQUIRED libdwarf)
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
include(FetchContent)
|
|
||||||
# First, dependencies: Zstd and zlib (currently relying on system zlib)
|
|
||||||
if(CPPTRACE_USE_EXTERNAL_ZSTD)
|
|
||||||
find_package(zstd)
|
|
||||||
else()
|
|
||||||
cmake_policy(SET CMP0074 NEW)
|
|
||||||
set(ZSTD_BUILD_PROGRAMS OFF)
|
|
||||||
set(ZSTD_BUILD_CONTRIB OFF)
|
|
||||||
set(ZSTD_BUILD_TESTS OFF)
|
|
||||||
set(ZSTD_BUILD_STATIC ON)
|
|
||||||
set(ZSTD_BUILD_SHARED OFF)
|
|
||||||
set(ZSTD_LEGACY_SUPPORT OFF)
|
|
||||||
FetchContent_Declare(
|
|
||||||
zstd
|
|
||||||
SOURCE_SUBDIR build/cmake
|
|
||||||
DOWNLOAD_EXTRACT_TIMESTAMP TRUE
|
|
||||||
URL "${CPPTRACE_ZSTD_URL}"
|
|
||||||
)
|
|
||||||
FetchContent_MakeAvailable(zstd)
|
|
||||||
endif()
|
|
||||||
# Libdwarf itself
|
|
||||||
set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
|
|
||||||
set(PIC_ALWAYS TRUE)
|
|
||||||
set(BUILD_DWARFDUMP FALSE)
|
|
||||||
FetchContent_Declare(
|
|
||||||
libdwarf
|
|
||||||
GIT_REPOSITORY ${CPPTRACE_LIBDWARF_REPO}
|
|
||||||
GIT_TAG ${CPPTRACE_LIBDWARF_TAG}
|
|
||||||
GIT_SHALLOW ${CPPTRACE_LIBDWARF_SHALLOW}
|
|
||||||
)
|
|
||||||
FetchContent_MakeAvailable(libdwarf)
|
|
||||||
target_include_directories(
|
|
||||||
dwarf
|
|
||||||
PRIVATE
|
|
||||||
${zstd_SOURCE_DIR}/lib
|
|
||||||
)
|
|
||||||
if(CPPTRACE_PROVIDE_EXPORT_SET_FOR_LIBDWARF)
|
|
||||||
export(
|
|
||||||
TARGETS dwarf
|
|
||||||
NAMESPACE libdwarf::
|
|
||||||
FILE "${PROJECT_BINARY_DIR}/libdwarf-targets.cmake"
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
if(CPPTRACE_CONAN)
|
|
||||||
set(dwarf_lib libdwarf::libdwarf)
|
|
||||||
target_link_libraries(${target_name} PRIVATE libdwarf::libdwarf)
|
|
||||||
elseif(CPPTRACE_VCPKG)
|
|
||||||
set(dwarf_lib libdwarf::dwarf)
|
|
||||||
target_link_libraries(${target_name} PRIVATE libdwarf::dwarf)
|
|
||||||
elseif(CPPTRACE_USE_EXTERNAL_LIBDWARF)
|
|
||||||
if(DEFINED LIBDWARF_LIBRARIES)
|
|
||||||
set(dwarf_lib ${LIBDWARF_LIBRARIES})
|
|
||||||
target_link_libraries(${target_name} PRIVATE ${LIBDWARF_LIBRARIES})
|
|
||||||
else()
|
|
||||||
# if LIBDWARF_LIBRARIES wasn't set by find_package, try looking for libdwarf::dwarf-static,
|
|
||||||
# libdwarf::dwarf-shared, libdwarf::dwarf, then libdwarf
|
|
||||||
# libdwarf v0.8.0 installs with the target libdwarf::dwarf somehow, despite creating libdwarf::dwarf-static or
|
|
||||||
# libdwarf::dwarf-shared under fetchcontent
|
|
||||||
if(TARGET libdwarf::dwarf-static)
|
|
||||||
set(LIBDWARF_LIBRARIES libdwarf::dwarf-static)
|
|
||||||
elseif(TARGET libdwarf::dwarf-shared)
|
|
||||||
set(LIBDWARF_LIBRARIES libdwarf::dwarf-shared)
|
|
||||||
elseif(TARGET libdwarf::dwarf)
|
|
||||||
set(LIBDWARF_LIBRARIES libdwarf::dwarf)
|
|
||||||
elseif(TARGET libdwarf)
|
|
||||||
set(LIBDWARF_LIBRARIES libdwarf)
|
|
||||||
else()
|
|
||||||
message(FATAL_ERROR "Couldn't find libdwarf target name to link against")
|
|
||||||
endif()
|
|
||||||
set(dwarf_lib ${LIBDWARF_LIBRARIES})
|
|
||||||
target_link_libraries(${target_name} PRIVATE ${LIBDWARF_LIBRARIES})
|
|
||||||
endif()
|
|
||||||
# There seems to be no consistency at all about where libdwarf decides to place its headers........ Figure out if
|
|
||||||
# it's libdwarf/libdwarf.h and libdwarf/dwarf.h or just libdwarf.h and dwarf.h
|
|
||||||
include(CheckIncludeFileCXX)
|
|
||||||
# libdwarf's cmake doesn't properly set variables to indicate where its libraries live
|
|
||||||
if(NOT CPPTRACE_FIND_LIBDWARF_WITH_PKGCONFIG)
|
|
||||||
get_target_property(LIBDWARF_INCLUDE_DIRS ${LIBDWARF_LIBRARIES} INTERFACE_INCLUDE_DIRECTORIES)
|
|
||||||
else()
|
|
||||||
target_include_directories(${target_name} PRIVATE ${LIBDWARF_INCLUDE_DIRS})
|
|
||||||
endif()
|
|
||||||
set(CMAKE_REQUIRED_INCLUDES ${LIBDWARF_INCLUDE_DIRS})
|
|
||||||
CHECK_INCLUDE_FILE_CXX("libdwarf/libdwarf.h" LIBDWARF_IS_NESTED)
|
|
||||||
CHECK_INCLUDE_FILE_CXX("libdwarf.h" LIBDWARF_IS_NOT_NESTED)
|
|
||||||
# check_include_file("libdwarf/libdwarf.h" LIBDWARF_IS_NESTED)
|
|
||||||
# check_support(LIBDWARF_IS_NESTED nested_libdwarf_include.cpp "" "" "")
|
|
||||||
if(${LIBDWARF_IS_NESTED})
|
|
||||||
target_compile_definitions(${target_name} PRIVATE CPPTRACE_USE_NESTED_LIBDWARF_HEADER_PATH)
|
|
||||||
elseif(NOT LIBDWARF_IS_NOT_NESTED)
|
|
||||||
message(FATAL_ERROR "Couldn't find libdwarf.h")
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
set(dwarf_lib libdwarf::dwarf-static)
|
|
||||||
target_link_libraries(${target_name} PRIVATE libdwarf::dwarf-static)
|
|
||||||
endif()
|
|
||||||
if(UNIX)
|
|
||||||
target_link_libraries(${target_name} PRIVATE ${CMAKE_DL_LIBS})
|
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(CPPTRACE_GET_SYMBOLS_WITH_DBGHELP)
|
if(CPPTRACE_GET_SYMBOLS_WITH_DBGHELP)
|
||||||
target_compile_definitions(${target_name} PUBLIC CPPTRACE_GET_SYMBOLS_WITH_DBGHELP)
|
target_compile_definitions(cpptrace PUBLIC CPPTRACE_GET_SYMBOLS_WITH_DBGHELP)
|
||||||
target_link_libraries(${target_name} PRIVATE dbghelp)
|
target_link_libraries(cpptrace PRIVATE dbghelp)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(CPPTRACE_GET_SYMBOLS_WITH_NOTHING)
|
if(CPPTRACE_GET_SYMBOLS_WITH_NOTHING)
|
||||||
target_compile_definitions(${target_name} PUBLIC CPPTRACE_GET_SYMBOLS_WITH_NOTHING)
|
target_compile_definitions(cpptrace PUBLIC CPPTRACE_GET_SYMBOLS_WITH_NOTHING)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Unwinding
|
# Unwinding
|
||||||
@ -425,67 +349,22 @@ if(CPPTRACE_UNWIND_WITH_UNWIND)
|
|||||||
if(NOT HAS_UNWIND)
|
if(NOT HAS_UNWIND)
|
||||||
message(WARNING "Cpptrace: CPPTRACE_UNWIND_WITH_UNWIND specified but libgcc unwind doesn't seem to be available.")
|
message(WARNING "Cpptrace: CPPTRACE_UNWIND_WITH_UNWIND specified but libgcc unwind doesn't seem to be available.")
|
||||||
endif()
|
endif()
|
||||||
target_compile_definitions(${target_name} PUBLIC CPPTRACE_UNWIND_WITH_UNWIND)
|
target_compile_definitions(cpptrace PUBLIC CPPTRACE_UNWIND_WITH_UNWIND)
|
||||||
endif()
|
|
||||||
|
|
||||||
if(CPPTRACE_UNWIND_WITH_LIBUNWIND)
|
|
||||||
find_package(PkgConfig)
|
|
||||||
if(PkgConfig_FOUND)
|
|
||||||
pkg_check_modules(LIBUNWIND QUIET libunwind)
|
|
||||||
if(libunwind_FOUND)
|
|
||||||
target_compile_options(${target_name} PRIVATE ${LIBUNWIND_CFLAGS_OTHER})
|
|
||||||
target_include_directories(${target_name} PRIVATE ${LIBUNWIND_INCLUDE_DIRS})
|
|
||||||
target_link_libraries(${target_name} PRIVATE ${LIBUNWIND_LDFLAGS})
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
if(NOT libunwind_FOUND)
|
|
||||||
if (NOT APPLE)
|
|
||||||
# set_property(GLOBAL PROPERTY FIND_LIBRARY_USE_LIB64_PATHS ON)
|
|
||||||
# set_property(GLOBAL PROPERTY FIND_LIBRARY_USE_LIB32_PATHS ON)
|
|
||||||
find_path(LIBUNWIND_INCLUDE_DIRS NAMES "libunwind.h")
|
|
||||||
find_library(LIBUNWIND NAMES unwind libunwind libunwind8 libunwind.so.8 REQUIRED PATHS "/usr/lib/x86_64-linux-gnu/")
|
|
||||||
if(LIBUNWIND)
|
|
||||||
set(libunwind_FOUND TRUE)
|
|
||||||
endif()
|
|
||||||
if(NOT libunwind_FOUND)
|
|
||||||
# message(FATAL_ERROR "Unable to locate libunwind")
|
|
||||||
# Try to link with it if it's where it should be
|
|
||||||
# This path can be entered if libunwind was installed via the system package manager, sometimes. I probably messed
|
|
||||||
# up the find_library above.
|
|
||||||
set(LIBUNWIND_LDFLAGS "-lunwind")
|
|
||||||
endif()
|
|
||||||
if(NOT LIBUNWIND_LDFLAGS)
|
|
||||||
set(LIBUNWIND_LDFLAGS "${LIBUNWIND}")
|
|
||||||
endif()
|
|
||||||
target_compile_options(${target_name} PRIVATE ${LIBUNWIND_CFLAGS_OTHER})
|
|
||||||
target_include_directories(${target_name} PRIVATE ${LIBUNWIND_INCLUDE_DIRS})
|
|
||||||
target_link_libraries(${target_name} PRIVATE ${LIBUNWIND_LDFLAGS})
|
|
||||||
endif()
|
|
||||||
target_compile_definitions(${target_name} PUBLIC CPPTRACE_UNWIND_WITH_LIBUNWIND UNW_LOCAL_ONLY)
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(CPPTRACE_UNWIND_WITH_EXECINFO)
|
if(CPPTRACE_UNWIND_WITH_EXECINFO)
|
||||||
if(NOT HAS_EXECINFO)
|
if(NOT HAS_EXECINFO)
|
||||||
message(WARNING "Cpptrace: CPPTRACE_UNWIND_WITH_EXECINFO specified but execinfo.h doesn't seem to be available.")
|
message(WARNING "Cpptrace: CPPTRACE_UNWIND_WITH_EXECINFO specified but execinfo.h doesn't seem to be available.")
|
||||||
endif()
|
endif()
|
||||||
target_compile_definitions(${target_name} PUBLIC CPPTRACE_UNWIND_WITH_EXECINFO)
|
target_compile_definitions(cpptrace PUBLIC CPPTRACE_UNWIND_WITH_EXECINFO)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(CPPTRACE_UNWIND_WITH_WINAPI)
|
if(CPPTRACE_UNWIND_WITH_WINAPI)
|
||||||
target_compile_definitions(${target_name} PUBLIC CPPTRACE_UNWIND_WITH_WINAPI)
|
target_compile_definitions(cpptrace PUBLIC CPPTRACE_UNWIND_WITH_WINAPI)
|
||||||
endif()
|
|
||||||
|
|
||||||
if(CPPTRACE_UNWIND_WITH_DBGHELP)
|
|
||||||
if(NOT HAS_STACKWALK)
|
|
||||||
message(WARNING "Cpptrace: CPPTRACE_UNWIND_WITH_DBGHELP specified but dbghelp stackwalk64 doesn't seem to be available.")
|
|
||||||
endif()
|
|
||||||
target_compile_definitions(${target_name} PUBLIC CPPTRACE_UNWIND_WITH_DBGHELP)
|
|
||||||
target_link_libraries(${target_name} PRIVATE dbghelp)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(CPPTRACE_UNWIND_WITH_NOTHING)
|
if(CPPTRACE_UNWIND_WITH_NOTHING)
|
||||||
target_compile_definitions(${target_name} PUBLIC CPPTRACE_UNWIND_WITH_NOTHING)
|
target_compile_definitions(cpptrace PUBLIC CPPTRACE_UNWIND_WITH_NOTHING)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Demangling
|
# Demangling
|
||||||
@ -493,45 +372,144 @@ if(CPPTRACE_DEMANGLE_WITH_CXXABI)
|
|||||||
if(NOT HAS_CXXABI)
|
if(NOT HAS_CXXABI)
|
||||||
message(WARNING "Cpptrace: CPPTRACE_DEMANGLE_WITH_CXXABI specified but cxxabi.h doesn't seem to be available.")
|
message(WARNING "Cpptrace: CPPTRACE_DEMANGLE_WITH_CXXABI specified but cxxabi.h doesn't seem to be available.")
|
||||||
endif()
|
endif()
|
||||||
target_compile_definitions(${target_name} PUBLIC CPPTRACE_DEMANGLE_WITH_CXXABI)
|
target_compile_definitions(cpptrace PUBLIC CPPTRACE_DEMANGLE_WITH_CXXABI)
|
||||||
endif()
|
|
||||||
|
|
||||||
if(CPPTRACE_DEMANGLE_WITH_WINAPI)
|
|
||||||
target_compile_definitions(${target_name} PUBLIC CPPTRACE_DEMANGLE_WITH_WINAPI)
|
|
||||||
target_link_libraries(${target_name} PRIVATE dbghelp)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(CPPTRACE_DEMANGLE_WITH_NOTHING)
|
if(CPPTRACE_DEMANGLE_WITH_NOTHING)
|
||||||
target_compile_definitions(${target_name} PUBLIC CPPTRACE_DEMANGLE_WITH_NOTHING)
|
target_compile_definitions(cpptrace PUBLIC CPPTRACE_DEMANGLE_WITH_NOTHING)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT "${CPPTRACE_BACKTRACE_PATH}" STREQUAL "")
|
if(NOT "${CPPTRACE_BACKTRACE_PATH}" STREQUAL "")
|
||||||
target_compile_definitions(${target_name} PUBLIC CPPTRACE_BACKTRACE_PATH=${CPPTRACE_BACKTRACE_PATH})
|
target_compile_definitions(cpptrace PUBLIC CPPTRACE_BACKTRACE_PATH=${CPPTRACE_BACKTRACE_PATH})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT "${CPPTRACE_HARD_MAX_FRAMES}" STREQUAL "")
|
if(NOT "${CPPTRACE_HARD_MAX_FRAMES}" STREQUAL "")
|
||||||
target_compile_definitions(${target_name} PUBLIC CPPTRACE_HARD_MAX_FRAMES=${CPPTRACE_HARD_MAX_FRAMES})
|
target_compile_definitions(cpptrace PUBLIC CPPTRACE_HARD_MAX_FRAMES=${CPPTRACE_HARD_MAX_FRAMES})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# ====================================================== Install =======================================================
|
# ======================================================================================================================
|
||||||
|
|
||||||
|
if(CMAKE_BUILD_TYPE STREQUAL "")
|
||||||
|
message(FATAL_ERROR "Setting CMAKE_BUILD_TYPE is required")
|
||||||
|
endif()
|
||||||
|
|
||||||
if(NOT CMAKE_SKIP_INSTALL_RULES)
|
if(NOT CMAKE_SKIP_INSTALL_RULES)
|
||||||
include(cmake/InstallRules.cmake)
|
include(CMakePackageConfigHelpers)
|
||||||
|
|
||||||
|
install(
|
||||||
|
TARGETS cpptrace
|
||||||
|
EXPORT cpptrace_targets
|
||||||
|
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
|
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||||
|
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
|
RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
install(
|
||||||
|
FILES
|
||||||
|
include/cpptrace/cpptrace.hpp
|
||||||
|
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/cpptrace
|
||||||
|
)
|
||||||
|
|
||||||
|
export(
|
||||||
|
EXPORT cpptrace_targets
|
||||||
|
FILE ${CMAKE_CURRENT_BINARY_DIR}/cpptrace/cpptrace_targets.cmake
|
||||||
|
NAMESPACE cpptrace::
|
||||||
|
)
|
||||||
|
|
||||||
|
configure_package_config_file(
|
||||||
|
cmake/cpptrace-config.cmake.in
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/cpptrace/cpptrace-config.cmake
|
||||||
|
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/cpptrace
|
||||||
|
)
|
||||||
|
|
||||||
|
write_basic_package_version_file(
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/cpptrace/cpptrace-config-version.cmake
|
||||||
|
VERSION ${PACKAGE_VERSION}
|
||||||
|
COMPATIBILITY SameMajorVersion
|
||||||
|
)
|
||||||
|
|
||||||
|
install(
|
||||||
|
EXPORT cpptrace_targets
|
||||||
|
FILE cpptrace_targets.cmake
|
||||||
|
NAMESPACE cpptrace::
|
||||||
|
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/cpptrace
|
||||||
|
)
|
||||||
|
|
||||||
|
install(
|
||||||
|
FILES
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/cpptrace/cpptrace-config.cmake
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/cpptrace/cpptrace-config-version.cmake
|
||||||
|
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/cpptrace
|
||||||
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# ================================================== Demo/test/tools ===================================================
|
if(CPPTRACE_BUILD_TEST)
|
||||||
|
add_executable(test test/test.cpp)
|
||||||
if(CPPTRACE_BUILD_TESTING)
|
target_link_libraries(test PRIVATE cpptrace)
|
||||||
if(PROJECT_IS_TOP_LEVEL)
|
# Clang has been fast to adopt dwarf 5, other tools (e.g. addr2line from binutils) have not
|
||||||
enable_testing()
|
check_cxx_compiler_flag("-gdwarf-4" HAS_DWARF4)
|
||||||
|
if(HAS_DWARF4)
|
||||||
|
target_compile_options(test PRIVATE "$<$<CONFIG:Debug>:-gdwarf-4>")
|
||||||
|
endif()
|
||||||
|
if(CPPTRACE_BUILD_TEST_RDYNAMIC)
|
||||||
|
set_property(TARGET test PROPERTY ENABLE_EXPORTS ON)
|
||||||
endif()
|
endif()
|
||||||
add_subdirectory(test)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(CPPTRACE_BUILD_BENCHMARKING)
|
if(CPPTRACE_BUILD_DEMO)
|
||||||
add_subdirectory(benchmarking)
|
add_executable(demo test/demo.cpp)
|
||||||
|
target_link_libraries(demo PRIVATE cpptrace)
|
||||||
|
# Clang has been fast to adopt dwarf 5, other tools (e.g. addr2line from binutils) have not
|
||||||
|
check_cxx_compiler_flag("-gdwarf-4" HAS_DWARF4)
|
||||||
|
if(HAS_DWARF4)
|
||||||
|
target_compile_options(demo PRIVATE "$<$<CONFIG:Debug>:-gdwarf-4>")
|
||||||
|
endif()
|
||||||
|
if(CPPTRACE_BUILD_TEST_RDYNAMIC)
|
||||||
|
set_property(TARGET demo PROPERTY ENABLE_EXPORTS ON)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(CPPTRACE_BUILD_TOOLS)
|
if(CPPTRACE_BUILD_SPEEDTEST)
|
||||||
add_subdirectory(tools)
|
if(CPPTRACE_BUILD_SPEEDTEST_DWARF4)
|
||||||
|
check_cxx_compiler_flag("-gdwarf-4" HAS_DWARF4)
|
||||||
|
if(HAS_DWARF4)
|
||||||
|
add_compile_options("$<$<CONFIG:Debug>:-gdwarf-4>")
|
||||||
|
#target_compile_options(speedtest PRIVATE "$<$<CONFIG:Debug>:-gdwarf-4>")
|
||||||
|
#target_compile_options(googletest INTERFACE "$<$<CONFIG:Debug>:-gdwarf-4>")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
if(CPPTRACE_BUILD_SPEEDTEST_DWARF5)
|
||||||
|
check_cxx_compiler_flag("-gdwarf-5" HAS_DWARF5)
|
||||||
|
if(HAS_DWARF5)
|
||||||
|
add_compile_options("$<$<CONFIG:Debug>:-gdwarf-5>")
|
||||||
|
#target_compile_options(speedtest PRIVATE "$<$<CONFIG:Debug>:-gdwarf-4>")
|
||||||
|
#target_compile_options(googletest INTERFACE "$<$<CONFIG:Debug>:-gdwarf-4>")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
include(FetchContent)
|
||||||
|
FetchContent_Declare(
|
||||||
|
googletest
|
||||||
|
URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip
|
||||||
|
)
|
||||||
|
# For Windows: Prevent overriding the parent project's compiler/linker settings
|
||||||
|
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
||||||
|
FetchContent_MakeAvailable(googletest)
|
||||||
|
|
||||||
|
add_executable(speedtest test/speedtest.cpp)
|
||||||
|
target_link_libraries(
|
||||||
|
speedtest
|
||||||
|
PRIVATE
|
||||||
|
GTest::gtest_main
|
||||||
|
cpptrace
|
||||||
|
)
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
|
add_custom_command(
|
||||||
|
TARGET speedtest POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_RUNTIME_DLLS:speedtest> $<TARGET_FILE_DIR:speedtest>
|
||||||
|
COMMAND_EXPAND_LISTS
|
||||||
|
)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@ -1,37 +0,0 @@
|
|||||||
# Contributing
|
|
||||||
|
|
||||||
Welcome, thank you for your interest in the project!
|
|
||||||
|
|
||||||
## Getting started
|
|
||||||
|
|
||||||
Contributions are always welcome. If you have not already, consider joining the community discord
|
|
||||||
(linked in the README). There is discussion about library development there as well as a development
|
|
||||||
roadmap. Github issues are also a good place to start.
|
|
||||||
|
|
||||||
I'm happy to merge fixes, improvements, and features as well as help with getting pull requests
|
|
||||||
(PRs) over the finish line. That being said, I can't merge stylistic changes,
|
|
||||||
premature-optimizations, or micro-optimizations.
|
|
||||||
|
|
||||||
When contributing, please try to match the current code style in the codebase. Style doesn't matter
|
|
||||||
too much ultimately but consistency within a codebase is important.
|
|
||||||
|
|
||||||
## Local development
|
|
||||||
|
|
||||||
The easiest way to get started with local development is running `make debug` (which automatically configures cmake and
|
|
||||||
builds). Note: This requires ninja at the moment.
|
|
||||||
|
|
||||||
For more control over how the library is built you can manually build with cmake:
|
|
||||||
|
|
||||||
`cmake ..` in a `build/` folder along with any cmake configurations you desire. Then run `make -j` or `ninja` or
|
|
||||||
`msbuild cpptrace.sln`.
|
|
||||||
|
|
||||||
Some useful configurations:
|
|
||||||
- `-DCMAKE_BUILD_TYPE=Debug|Release|RelWithDebInfo`: Build in debug / release / etc.
|
|
||||||
- `-DBUILD_SHARED_LIBS=On`: Build shared library
|
|
||||||
- `-DCPPTRACE_SANITIZER_BUILD=On`: Turn on sanitizers
|
|
||||||
- `-DCPPTRACE_BUILD_TESTING=On`: Build small test and demo program
|
|
||||||
|
|
||||||
## Testing
|
|
||||||
|
|
||||||
Unfortunately because this library is so platform-dependent the best way to test thoroughly is with
|
|
||||||
github's CI. This will happen automatically when you open a PR.
|
|
||||||
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
|||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2023-2024 Jeremy Rifkin
|
Copyright (c) 2023 Jeremy Rifkin
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
||||||
associated documentation files (the "Software"), to deal in the Software without restriction,
|
associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||||
|
|||||||
104
MODULE.bazel
104
MODULE.bazel
@ -1,104 +0,0 @@
|
|||||||
module(
|
|
||||||
name = "cpptrace",
|
|
||||||
)
|
|
||||||
|
|
||||||
bazel_dep(name = "googletest", version = "1.14.0")
|
|
||||||
bazel_dep(name = "bazel_skylib", version = "1.7.1")
|
|
||||||
bazel_dep(name = "rules_foreign_cc", version = "0.11.1")
|
|
||||||
bazel_dep(name = "zstd", version = "1.5.6")
|
|
||||||
bazel_dep(name = "zlib", version = "1.3.1")
|
|
||||||
bazel_dep(name = "xz", version = "5.4.5.bcr.2")
|
|
||||||
bazel_dep(name = "toolchains_llvm", version = "1.1.2")
|
|
||||||
|
|
||||||
# Configure and register the toolchain.
|
|
||||||
llvm = use_extension("@toolchains_llvm//toolchain/extensions:llvm.bzl", "llvm", dev_dependency = True)
|
|
||||||
|
|
||||||
llvm.toolchain(
|
|
||||||
llvm_versions = {
|
|
||||||
"": "18.1.8",
|
|
||||||
},
|
|
||||||
sha256 = {
|
|
||||||
"": "54ec30358afcc9fb8aa74307db3046f5187f9fb89fb37064cdde906e062ebf36",
|
|
||||||
},
|
|
||||||
strip_prefix = {
|
|
||||||
"": "clang+llvm-18.1.8-x86_64-linux-gnu-ubuntu-18.04",
|
|
||||||
},
|
|
||||||
urls = {
|
|
||||||
"": ["https://github.com/llvm/llvm-project/releases/download/llvmorg-18.1.8/clang+llvm-18.1.8-x86_64-linux-gnu-ubuntu-18.04.tar.xz"],
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
use_repo(llvm, "llvm_toolchain")
|
|
||||||
|
|
||||||
register_toolchains("@llvm_toolchain//:all", dev_dependency = True)
|
|
||||||
|
|
||||||
http_archive = use_repo_rule("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
|
|
||||||
|
|
||||||
http_archive(
|
|
||||||
name = "libdwarf",
|
|
||||||
build_file_content =
|
|
||||||
"""
|
|
||||||
package(default_visibility = ["//visibility:public"])
|
|
||||||
load("@rules_foreign_cc//foreign_cc:defs.bzl", "cmake")
|
|
||||||
|
|
||||||
filegroup(
|
|
||||||
name = "sources",
|
|
||||||
srcs = glob(["**/*"]),
|
|
||||||
)
|
|
||||||
cmake(
|
|
||||||
name = "libdwarf",
|
|
||||||
build_args = ["-j12"],
|
|
||||||
lib_source = ":sources",
|
|
||||||
out_static_libs = ["libdwarf.a"],
|
|
||||||
copts = ["-Wall", "-Werror"],
|
|
||||||
deps = [
|
|
||||||
"@zstd",
|
|
||||||
"@zlib"
|
|
||||||
]
|
|
||||||
)
|
|
||||||
""",
|
|
||||||
sha256 = "4ab8ae7b4b7aa42453725054b348f4fdb2460d5ba644199a1305311c718ff416",
|
|
||||||
strip_prefix = "libdwarf-code-0.10.1",
|
|
||||||
url = "https://github.com/davea42/libdwarf-code/archive/refs/tags/v0.10.1.tar.gz",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
http_archive(
|
|
||||||
name = "libunwind",
|
|
||||||
build_file_content =
|
|
||||||
"""
|
|
||||||
package(default_visibility = ["//visibility:public"])
|
|
||||||
load("@rules_foreign_cc//foreign_cc:defs.bzl", "configure_make")
|
|
||||||
|
|
||||||
filegroup(
|
|
||||||
name = "sources",
|
|
||||||
srcs = glob(["**/*"]),
|
|
||||||
)
|
|
||||||
configure_make(
|
|
||||||
name = "libunwind",
|
|
||||||
args = ["-j12"],
|
|
||||||
autoreconf = True,
|
|
||||||
configure_in_place = True,
|
|
||||||
autoreconf_options = [
|
|
||||||
"-i",
|
|
||||||
],
|
|
||||||
lib_source = ":sources",
|
|
||||||
out_static_libs = [
|
|
||||||
"libunwind.a",
|
|
||||||
"libunwind-coredump.a",
|
|
||||||
"libunwind-ptrace.a",
|
|
||||||
"libunwind-x86_64.a",
|
|
||||||
"libunwind-generic.a",
|
|
||||||
"libunwind-setjmp.a"
|
|
||||||
],
|
|
||||||
deps = [
|
|
||||||
"@xz//:lzma"
|
|
||||||
]
|
|
||||||
)
|
|
||||||
""",
|
|
||||||
sha256 = "38833b7b1582db7d76485a62a213706c9252b3dab7380069fea5824e823d8e41",
|
|
||||||
strip_prefix = "libunwind-1.8.1",
|
|
||||||
url = "https://github.com/libunwind/libunwind/archive/refs/tags/v1.8.1.tar.gz",
|
|
||||||
)
|
|
||||||
|
|
||||||
851
MODULE.bazel.lock
generated
851
MODULE.bazel.lock
generated
@ -1,851 +0,0 @@
|
|||||||
{
|
|
||||||
"lockFileVersion": 11,
|
|
||||||
"registryFileHashes": {
|
|
||||||
"https://bcr.bazel.build/bazel_registry.json": "8a28e4aff06ee60aed2a8c281907fb8bcbf3b753c91fb5a5c57da3215d5b3497",
|
|
||||||
"https://bcr.bazel.build/modules/abseil-cpp/20210324.2/MODULE.bazel": "7cd0312e064fde87c8d1cd79ba06c876bd23630c83466e9500321be55c96ace2",
|
|
||||||
"https://bcr.bazel.build/modules/abseil-cpp/20211102.0/MODULE.bazel": "70390338f7a5106231d20620712f7cccb659cd0e9d073d1991c038eb9fc57589",
|
|
||||||
"https://bcr.bazel.build/modules/abseil-cpp/20230125.1/MODULE.bazel": "89047429cb0207707b2dface14ba7f8df85273d484c2572755be4bab7ce9c3a0",
|
|
||||||
"https://bcr.bazel.build/modules/abseil-cpp/20230125.1/source.json": "06cc0842d241da0c5edc755edb3c7d0d008d304330e57ecf2d6449fb0b633a82",
|
|
||||||
"https://bcr.bazel.build/modules/apple_support/1.5.0/MODULE.bazel": "50341a62efbc483e8a2a6aec30994a58749bd7b885e18dd96aa8c33031e558ef",
|
|
||||||
"https://bcr.bazel.build/modules/apple_support/1.5.0/source.json": "eb98a7627c0bc486b57f598ad8da50f6625d974c8f723e9ea71bd39f709c9862",
|
|
||||||
"https://bcr.bazel.build/modules/bazel_features/1.11.0/MODULE.bazel": "f9382337dd5a474c3b7d334c2f83e50b6eaedc284253334cf823044a26de03e8",
|
|
||||||
"https://bcr.bazel.build/modules/bazel_features/1.11.0/source.json": "c9320aa53cd1c441d24bd6b716da087ad7e4ff0d9742a9884587596edfe53015",
|
|
||||||
"https://bcr.bazel.build/modules/bazel_skylib/1.0.3/MODULE.bazel": "bcb0fd896384802d1ad283b4e4eb4d718eebd8cb820b0a2c3a347fb971afd9d8",
|
|
||||||
"https://bcr.bazel.build/modules/bazel_skylib/1.2.1/MODULE.bazel": "f35baf9da0efe45fa3da1696ae906eea3d615ad41e2e3def4aeb4e8bc0ef9a7a",
|
|
||||||
"https://bcr.bazel.build/modules/bazel_skylib/1.3.0/MODULE.bazel": "20228b92868bf5cfc41bda7afc8a8ba2a543201851de39d990ec957b513579c5",
|
|
||||||
"https://bcr.bazel.build/modules/bazel_skylib/1.4.1/MODULE.bazel": "a0dcb779424be33100dcae821e9e27e4f2901d9dfd5333efe5ac6a8d7ab75e1d",
|
|
||||||
"https://bcr.bazel.build/modules/bazel_skylib/1.5.0/MODULE.bazel": "32880f5e2945ce6a03d1fbd588e9198c0a959bb42297b2cfaf1685b7bc32e138",
|
|
||||||
"https://bcr.bazel.build/modules/bazel_skylib/1.6.1/MODULE.bazel": "8fdee2dbaace6c252131c00e1de4b165dc65af02ea278476187765e1a617b917",
|
|
||||||
"https://bcr.bazel.build/modules/bazel_skylib/1.7.1/MODULE.bazel": "3120d80c5861aa616222ec015332e5f8d3171e062e3e804a2a0253e1be26e59b",
|
|
||||||
"https://bcr.bazel.build/modules/bazel_skylib/1.7.1/source.json": "f121b43eeefc7c29efbd51b83d08631e2347297c95aac9764a701f2a6a2bb953",
|
|
||||||
"https://bcr.bazel.build/modules/buildozer/7.1.2/MODULE.bazel": "2e8dd40ede9c454042645fd8d8d0cd1527966aa5c919de86661e62953cd73d84",
|
|
||||||
"https://bcr.bazel.build/modules/buildozer/7.1.2/source.json": "c9028a501d2db85793a6996205c8de120944f50a0d570438fcae0457a5f9d1f8",
|
|
||||||
"https://bcr.bazel.build/modules/googletest/1.11.0/MODULE.bazel": "3a83f095183f66345ca86aa13c58b59f9f94a2f81999c093d4eeaa2d262d12f4",
|
|
||||||
"https://bcr.bazel.build/modules/googletest/1.14.0/MODULE.bazel": "cfbcbf3e6eac06ef9d85900f64424708cc08687d1b527f0ef65aa7517af8118f",
|
|
||||||
"https://bcr.bazel.build/modules/googletest/1.14.0/source.json": "2478949479000fdd7de9a3d0107ba2c85bb5f961c3ecb1aa448f52549ce310b5",
|
|
||||||
"https://bcr.bazel.build/modules/platforms/0.0.4/MODULE.bazel": "9b328e31ee156f53f3c416a64f8491f7eb731742655a47c9eec4703a71644aee",
|
|
||||||
"https://bcr.bazel.build/modules/platforms/0.0.5/MODULE.bazel": "5733b54ea419d5eaf7997054bb55f6a1d0b5ff8aedf0176fef9eea44f3acda37",
|
|
||||||
"https://bcr.bazel.build/modules/platforms/0.0.6/MODULE.bazel": "ad6eeef431dc52aefd2d77ed20a4b353f8ebf0f4ecdd26a807d2da5aa8cd0615",
|
|
||||||
"https://bcr.bazel.build/modules/platforms/0.0.7/MODULE.bazel": "72fd4a0ede9ee5c021f6a8dd92b503e089f46c227ba2813ff183b71616034814",
|
|
||||||
"https://bcr.bazel.build/modules/platforms/0.0.8/MODULE.bazel": "9f142c03e348f6d263719f5074b21ef3adf0b139ee4c5133e2aa35664da9eb2d",
|
|
||||||
"https://bcr.bazel.build/modules/platforms/0.0.9/MODULE.bazel": "4a87a60c927b56ddd67db50c89acaa62f4ce2a1d2149ccb63ffd871d5ce29ebc",
|
|
||||||
"https://bcr.bazel.build/modules/platforms/0.0.9/source.json": "cd74d854bf16a9e002fb2ca7b1a421f4403cda29f824a765acd3a8c56f8d43e6",
|
|
||||||
"https://bcr.bazel.build/modules/protobuf/21.7/MODULE.bazel": "a5a29bb89544f9b97edce05642fac225a808b5b7be74038ea3640fae2f8e66a7",
|
|
||||||
"https://bcr.bazel.build/modules/protobuf/21.7/source.json": "bbe500720421e582ff2d18b0802464205138c06056f443184de39fbb8187b09b",
|
|
||||||
"https://bcr.bazel.build/modules/protobuf/3.19.0/MODULE.bazel": "6b5fbb433f760a99a22b18b6850ed5784ef0e9928a72668b66e4d7ccd47db9b0",
|
|
||||||
"https://bcr.bazel.build/modules/protobuf/3.19.6/MODULE.bazel": "9233edc5e1f2ee276a60de3eaa47ac4132302ef9643238f23128fea53ea12858",
|
|
||||||
"https://bcr.bazel.build/modules/rules_cc/0.0.1/MODULE.bazel": "cb2aa0747f84c6c3a78dad4e2049c154f08ab9d166b1273835a8174940365647",
|
|
||||||
"https://bcr.bazel.build/modules/rules_cc/0.0.2/MODULE.bazel": "6915987c90970493ab97393024c156ea8fb9f3bea953b2f3ec05c34f19b5695c",
|
|
||||||
"https://bcr.bazel.build/modules/rules_cc/0.0.6/MODULE.bazel": "abf360251023dfe3efcef65ab9d56beefa8394d4176dd29529750e1c57eaa33f",
|
|
||||||
"https://bcr.bazel.build/modules/rules_cc/0.0.8/MODULE.bazel": "964c85c82cfeb6f3855e6a07054fdb159aced38e99a5eecf7bce9d53990afa3e",
|
|
||||||
"https://bcr.bazel.build/modules/rules_cc/0.0.9/MODULE.bazel": "836e76439f354b89afe6a911a7adf59a6b2518fafb174483ad78a2a2fde7b1c5",
|
|
||||||
"https://bcr.bazel.build/modules/rules_cc/0.0.9/source.json": "1f1ba6fea244b616de4a554a0f4983c91a9301640c8fe0dd1d410254115c8430",
|
|
||||||
"https://bcr.bazel.build/modules/rules_foreign_cc/0.11.1/MODULE.bazel": "beeb0dd8d488d3cff57fa12ab3378051a7299aa9de2476d61c1d46f664d6398d",
|
|
||||||
"https://bcr.bazel.build/modules/rules_foreign_cc/0.11.1/source.json": "be2106be697115c10c03c6505a07bd4e259719c6608f08a61d600a560b8cf172",
|
|
||||||
"https://bcr.bazel.build/modules/rules_java/4.0.0/MODULE.bazel": "5a78a7ae82cd1a33cef56dc578c7d2a46ed0dca12643ee45edbb8417899e6f74",
|
|
||||||
"https://bcr.bazel.build/modules/rules_java/7.6.5/MODULE.bazel": "481164be5e02e4cab6e77a36927683263be56b7e36fef918b458d7a8a1ebadb1",
|
|
||||||
"https://bcr.bazel.build/modules/rules_java/7.6.5/source.json": "a805b889531d1690e3c72a7a7e47a870d00323186a9904b36af83aa3d053ee8d",
|
|
||||||
"https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/MODULE.bazel": "a56b85e418c83eb1839819f0b515c431010160383306d13ec21959ac412d2fe7",
|
|
||||||
"https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/source.json": "a075731e1b46bc8425098512d038d416e966ab19684a10a34f4741295642fc35",
|
|
||||||
"https://bcr.bazel.build/modules/rules_license/0.0.3/MODULE.bazel": "627e9ab0247f7d1e05736b59dbb1b6871373de5ad31c3011880b4133cafd4bd0",
|
|
||||||
"https://bcr.bazel.build/modules/rules_license/0.0.7/MODULE.bazel": "088fbeb0b6a419005b89cf93fe62d9517c0a2b8bb56af3244af65ecfe37e7d5d",
|
|
||||||
"https://bcr.bazel.build/modules/rules_license/0.0.7/source.json": "355cc5737a0f294e560d52b1b7a6492d4fff2caf0bef1a315df5a298fca2d34a",
|
|
||||||
"https://bcr.bazel.build/modules/rules_pkg/0.7.0/MODULE.bazel": "df99f03fc7934a4737122518bb87e667e62d780b610910f0447665a7e2be62dc",
|
|
||||||
"https://bcr.bazel.build/modules/rules_pkg/0.7.0/source.json": "c2557066e0c0342223ba592510ad3d812d4963b9024831f7f66fd0584dd8c66c",
|
|
||||||
"https://bcr.bazel.build/modules/rules_proto/4.0.0/MODULE.bazel": "a7a7b6ce9bee418c1a760b3d84f83a299ad6952f9903c67f19e4edd964894e06",
|
|
||||||
"https://bcr.bazel.build/modules/rules_proto/5.3.0-21.7/MODULE.bazel": "e8dff86b0971688790ae75528fe1813f71809b5afd57facb44dad9e8eca631b7",
|
|
||||||
"https://bcr.bazel.build/modules/rules_proto/5.3.0-21.7/source.json": "d57902c052424dfda0e71646cb12668d39c4620ee0544294d9d941e7d12bc3a9",
|
|
||||||
"https://bcr.bazel.build/modules/rules_python/0.10.2/MODULE.bazel": "cc82bc96f2997baa545ab3ce73f196d040ffb8756fd2d66125a530031cd90e5f",
|
|
||||||
"https://bcr.bazel.build/modules/rules_python/0.22.1/MODULE.bazel": "26114f0c0b5e93018c0c066d6673f1a2c3737c7e90af95eff30cfee38d0bbac7",
|
|
||||||
"https://bcr.bazel.build/modules/rules_python/0.23.1/MODULE.bazel": "49ffccf0511cb8414de28321f5fcf2a31312b47c40cc21577144b7447f2bf300",
|
|
||||||
"https://bcr.bazel.build/modules/rules_python/0.23.1/source.json": "a6d9965700e3bd75df4e19140c0e651851bb720d8b9eb280ecd1ee44b92d7646",
|
|
||||||
"https://bcr.bazel.build/modules/rules_python/0.4.0/MODULE.bazel": "9208ee05fd48bf09ac60ed269791cf17fb343db56c8226a720fbb1cdf467166c",
|
|
||||||
"https://bcr.bazel.build/modules/stardoc/0.5.1/MODULE.bazel": "1a05d92974d0c122f5ccf09291442580317cdd859f07a8655f1db9a60374f9f8",
|
|
||||||
"https://bcr.bazel.build/modules/stardoc/0.5.1/source.json": "a96f95e02123320aa015b956f29c00cb818fa891ef823d55148e1a362caacf29",
|
|
||||||
"https://bcr.bazel.build/modules/toolchains_llvm/1.1.2/MODULE.bazel": "402101d6f73115ec49a3a765a3361c1dd90ba3959fa688ccdcd465c36dbbbc52",
|
|
||||||
"https://bcr.bazel.build/modules/toolchains_llvm/1.1.2/source.json": "27f3cf531bc654c719b50411cac94613b7676d63e60962243d485af63e13b9ff",
|
|
||||||
"https://bcr.bazel.build/modules/upb/0.0.0-20220923-a547704/MODULE.bazel": "7298990c00040a0e2f121f6c32544bab27d4452f80d9ce51349b1a28f3005c43",
|
|
||||||
"https://bcr.bazel.build/modules/upb/0.0.0-20220923-a547704/source.json": "f1ef7d3f9e0e26d4b23d1c39b5f5de71f584dd7d1b4ef83d9bbba6ec7a6a6459",
|
|
||||||
"https://bcr.bazel.build/modules/xz/5.4.5.bcr.2/MODULE.bazel": "463976fb85f578a2535421ba4c38fe90657ab348e4b5d5404b75c061602705d0",
|
|
||||||
"https://bcr.bazel.build/modules/xz/5.4.5.bcr.2/source.json": "e735da8a3f396bf200ed06c585f670f7667e08c4e1ed2849bae7c2691bcb10cf",
|
|
||||||
"https://bcr.bazel.build/modules/zlib/1.2.11/MODULE.bazel": "07b389abc85fdbca459b69e2ec656ae5622873af3f845e1c9d80fe179f3effa0",
|
|
||||||
"https://bcr.bazel.build/modules/zlib/1.2.12/MODULE.bazel": "3b1a8834ada2a883674be8cbd36ede1b6ec481477ada359cd2d3ddc562340b27",
|
|
||||||
"https://bcr.bazel.build/modules/zlib/1.3.1.bcr.3/MODULE.bazel": "af322bc08976524477c79d1e45e241b6efbeb918c497e8840b8ab116802dda79",
|
|
||||||
"https://bcr.bazel.build/modules/zlib/1.3.1.bcr.3/source.json": "2be409ac3c7601245958cd4fcdff4288be79ed23bd690b4b951f500d54ee6e7d",
|
|
||||||
"https://bcr.bazel.build/modules/zlib/1.3.1/MODULE.bazel": "751c9940dcfe869f5f7274e1295422a34623555916eb98c174c1e945594bf198",
|
|
||||||
"https://bcr.bazel.build/modules/zstd/1.5.6/MODULE.bazel": "471ebe7d3cdd8c6469390fcf623eb4779ff55fbee0a87f1dc57a1def468b96d4",
|
|
||||||
"https://bcr.bazel.build/modules/zstd/1.5.6/source.json": "02010c3333fc89b44fe861db049968decb6e688411f7f9d4f6791d74f9adfb51"
|
|
||||||
},
|
|
||||||
"selectedYankedVersions": {},
|
|
||||||
"moduleExtensions": {
|
|
||||||
"@@apple_support~//crosstool:setup.bzl%apple_cc_configure_extension": {
|
|
||||||
"general": {
|
|
||||||
"bzlTransitiveDigest": "PjIds3feoYE8SGbbIq2SFTZy3zmxeO2tQevJZNDo7iY=",
|
|
||||||
"usagesDigest": "aLmqbvowmHkkBPve05yyDNGN7oh7QE9kBADr3QIZTZs=",
|
|
||||||
"recordedFileInputs": {},
|
|
||||||
"recordedDirentsInputs": {},
|
|
||||||
"envVariables": {},
|
|
||||||
"generatedRepoSpecs": {
|
|
||||||
"local_config_apple_cc": {
|
|
||||||
"bzlFile": "@@apple_support~//crosstool:setup.bzl",
|
|
||||||
"ruleClassName": "_apple_cc_autoconf",
|
|
||||||
"attributes": {}
|
|
||||||
},
|
|
||||||
"local_config_apple_cc_toolchains": {
|
|
||||||
"bzlFile": "@@apple_support~//crosstool:setup.bzl",
|
|
||||||
"ruleClassName": "_apple_cc_autoconf_toolchains",
|
|
||||||
"attributes": {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"recordedRepoMappingEntries": [
|
|
||||||
[
|
|
||||||
"apple_support~",
|
|
||||||
"bazel_tools",
|
|
||||||
"bazel_tools"
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@@platforms//host:extension.bzl%host_platform": {
|
|
||||||
"general": {
|
|
||||||
"bzlTransitiveDigest": "xelQcPZH8+tmuOHVjL9vDxMnnQNMlwj0SlvgoqBkm4U=",
|
|
||||||
"usagesDigest": "meSzxn3DUCcYEhq4HQwExWkWtU4EjriRBQLsZN+Q0SU=",
|
|
||||||
"recordedFileInputs": {},
|
|
||||||
"recordedDirentsInputs": {},
|
|
||||||
"envVariables": {},
|
|
||||||
"generatedRepoSpecs": {
|
|
||||||
"host_platform": {
|
|
||||||
"bzlFile": "@@platforms//host:extension.bzl",
|
|
||||||
"ruleClassName": "host_platform_repo",
|
|
||||||
"attributes": {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"recordedRepoMappingEntries": []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@@rules_foreign_cc~//foreign_cc:extensions.bzl%tools": {
|
|
||||||
"general": {
|
|
||||||
"bzlTransitiveDigest": "5Xt39wqg6Xufojy5gN4ke9V2Mv5ANvdeLlAL1hp6+ic=",
|
|
||||||
"usagesDigest": "WnYOMrYXMz7KcDu0mMpDP5Eue8sHuFMA1dmDT6I124Q=",
|
|
||||||
"recordedFileInputs": {},
|
|
||||||
"recordedDirentsInputs": {},
|
|
||||||
"envVariables": {},
|
|
||||||
"generatedRepoSpecs": {
|
|
||||||
"cmake-3.23.2-linux-aarch64": {
|
|
||||||
"bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
|
|
||||||
"ruleClassName": "http_archive",
|
|
||||||
"attributes": {
|
|
||||||
"urls": [
|
|
||||||
"https://github.com/Kitware/CMake/releases/download/v3.23.2/cmake-3.23.2-linux-aarch64.tar.gz"
|
|
||||||
],
|
|
||||||
"sha256": "f2654bf780b53f170bbbec44d8ac67d401d24788e590faa53036a89476efa91e",
|
|
||||||
"strip_prefix": "cmake-3.23.2-linux-aarch64",
|
|
||||||
"build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"cmake_bin\",\n srcs = [\"bin/cmake\"],\n)\n\nfilegroup(\n name = \"cmake_data\",\n srcs = glob(\n [\n \"**\",\n ],\n exclude = [\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"**/* *\",\n ],\n ),\n)\n\nnative_tool_toolchain(\n name = \"cmake_tool\",\n path = \"bin/cmake\",\n target = \":cmake_data\",\n env = {\"CMAKE\": \"$(execpath :cmake_bin)\"},\n tools = [\":cmake_bin\"],\n)\n"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"rules_foreign_cc_framework_toolchain_macos": {
|
|
||||||
"bzlFile": "@@rules_foreign_cc~//foreign_cc/private/framework:toolchain.bzl",
|
|
||||||
"ruleClassName": "framework_toolchain_repository",
|
|
||||||
"attributes": {
|
|
||||||
"commands_src": "@rules_foreign_cc//foreign_cc/private/framework/toolchains:macos_commands.bzl",
|
|
||||||
"exec_compatible_with": [
|
|
||||||
"@platforms//os:macos"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ninja_1.12.0_mac": {
|
|
||||||
"bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
|
|
||||||
"ruleClassName": "http_archive",
|
|
||||||
"attributes": {
|
|
||||||
"urls": [
|
|
||||||
"https://github.com/ninja-build/ninja/releases/download/v1.12.0/ninja-mac.zip"
|
|
||||||
],
|
|
||||||
"sha256": "19806019c9623a062c3d9fa0d5f45b633a3d150f88e73fbd6c0ff6ea5534df10",
|
|
||||||
"strip_prefix": "",
|
|
||||||
"build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"ninja_bin\",\n srcs = [\"ninja\"],\n)\n\nnative_tool_toolchain(\n name = \"ninja_tool\",\n env = {\"NINJA\": \"$(execpath :ninja_bin)\"},\n path = \"$(execpath :ninja_bin)\",\n target = \":ninja_bin\",\n)\n"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ninja_1.12.0_mac_aarch64": {
|
|
||||||
"bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
|
|
||||||
"ruleClassName": "http_archive",
|
|
||||||
"attributes": {
|
|
||||||
"urls": [
|
|
||||||
"https://github.com/ninja-build/ninja/releases/download/v1.12.0/ninja-mac.zip"
|
|
||||||
],
|
|
||||||
"sha256": "19806019c9623a062c3d9fa0d5f45b633a3d150f88e73fbd6c0ff6ea5534df10",
|
|
||||||
"strip_prefix": "",
|
|
||||||
"build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"ninja_bin\",\n srcs = [\"ninja\"],\n)\n\nnative_tool_toolchain(\n name = \"ninja_tool\",\n env = {\"NINJA\": \"$(execpath :ninja_bin)\"},\n path = \"$(execpath :ninja_bin)\",\n target = \":ninja_bin\",\n)\n"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"gnumake_src": {
|
|
||||||
"bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
|
|
||||||
"ruleClassName": "http_archive",
|
|
||||||
"attributes": {
|
|
||||||
"build_file_content": "filegroup(\n name = \"all_srcs\",\n srcs = glob([\"**\"]),\n visibility = [\"//visibility:public\"],\n)\n",
|
|
||||||
"sha256": "dd16fb1d67bfab79a72f5e8390735c49e3e8e70b4945a15ab1f81ddb78658fb3",
|
|
||||||
"strip_prefix": "make-4.4.1",
|
|
||||||
"urls": [
|
|
||||||
"https://mirror.bazel.build/ftpmirror.gnu.org/gnu/make/make-4.4.1.tar.gz",
|
|
||||||
"http://ftpmirror.gnu.org/gnu/make/make-4.4.1.tar.gz"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"gettext_runtime": {
|
|
||||||
"bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
|
|
||||||
"ruleClassName": "http_archive",
|
|
||||||
"attributes": {
|
|
||||||
"build_file_content": "\ncc_import(\n name = \"gettext_runtime\",\n shared_library = \"bin/libintl-8.dll\",\n visibility = [\"//visibility:public\"],\n)\n ",
|
|
||||||
"sha256": "1f4269c0e021076d60a54e98da6f978a3195013f6de21674ba0edbc339c5b079",
|
|
||||||
"urls": [
|
|
||||||
"https://mirror.bazel.build/download.gnome.org/binaries/win64/dependencies/gettext-runtime_0.18.1.1-2_win64.zip",
|
|
||||||
"https://download.gnome.org/binaries/win64/dependencies/gettext-runtime_0.18.1.1-2_win64.zip"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"cmake_src": {
|
|
||||||
"bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
|
|
||||||
"ruleClassName": "http_archive",
|
|
||||||
"attributes": {
|
|
||||||
"build_file_content": "filegroup(\n name = \"all_srcs\",\n srcs = glob([\"**\"]),\n visibility = [\"//visibility:public\"],\n)\n",
|
|
||||||
"sha256": "f316b40053466f9a416adf981efda41b160ca859e97f6a484b447ea299ff26aa",
|
|
||||||
"strip_prefix": "cmake-3.23.2",
|
|
||||||
"urls": [
|
|
||||||
"https://github.com/Kitware/CMake/releases/download/v3.23.2/cmake-3.23.2.tar.gz"
|
|
||||||
],
|
|
||||||
"patches": [
|
|
||||||
"@@rules_foreign_cc~//toolchains:cmake-c++11.patch"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"bazel_skylib": {
|
|
||||||
"bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
|
|
||||||
"ruleClassName": "http_archive",
|
|
||||||
"attributes": {
|
|
||||||
"urls": [
|
|
||||||
"https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.2.1/bazel-skylib-1.2.1.tar.gz",
|
|
||||||
"https://github.com/bazelbuild/bazel-skylib/releases/download/1.2.1/bazel-skylib-1.2.1.tar.gz"
|
|
||||||
],
|
|
||||||
"sha256": "f7be3474d42aae265405a592bb7da8e171919d74c16f082a5457840f06054728"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"cmake-3.23.2-macos-universal": {
|
|
||||||
"bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
|
|
||||||
"ruleClassName": "http_archive",
|
|
||||||
"attributes": {
|
|
||||||
"urls": [
|
|
||||||
"https://github.com/Kitware/CMake/releases/download/v3.23.2/cmake-3.23.2-macos-universal.tar.gz"
|
|
||||||
],
|
|
||||||
"sha256": "853a0f9af148c5ef47282ffffee06c4c9f257be2635936755f39ca13c3286c88",
|
|
||||||
"strip_prefix": "cmake-3.23.2-macos-universal/CMake.app/Contents",
|
|
||||||
"build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"cmake_bin\",\n srcs = [\"bin/cmake\"],\n)\n\nfilegroup(\n name = \"cmake_data\",\n srcs = glob(\n [\n \"**\",\n ],\n exclude = [\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"**/* *\",\n ],\n ),\n)\n\nnative_tool_toolchain(\n name = \"cmake_tool\",\n path = \"bin/cmake\",\n target = \":cmake_data\",\n env = {\"CMAKE\": \"$(execpath :cmake_bin)\"},\n tools = [\":cmake_bin\"],\n)\n"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ninja_1.12.0_win": {
|
|
||||||
"bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
|
|
||||||
"ruleClassName": "http_archive",
|
|
||||||
"attributes": {
|
|
||||||
"urls": [
|
|
||||||
"https://github.com/ninja-build/ninja/releases/download/v1.12.0/ninja-win.zip"
|
|
||||||
],
|
|
||||||
"sha256": "51d99be9ceea8835edf536d52d47fa4c316aa332e57f71a08df5bd059da11417",
|
|
||||||
"strip_prefix": "",
|
|
||||||
"build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"ninja_bin\",\n srcs = [\"ninja.exe\"],\n)\n\nnative_tool_toolchain(\n name = \"ninja_tool\",\n env = {\"NINJA\": \"$(execpath :ninja_bin)\"},\n path = \"$(execpath :ninja_bin)\",\n target = \":ninja_bin\",\n)\n"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"meson_src": {
|
|
||||||
"bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
|
|
||||||
"ruleClassName": "http_archive",
|
|
||||||
"attributes": {
|
|
||||||
"build_file_content": "exports_files([\"meson.py\"])\n\nfilegroup(\n name = \"runtime\",\n srcs = glob([\"mesonbuild/**\"]),\n visibility = [\"//visibility:public\"],\n)\n",
|
|
||||||
"sha256": "d04b541f97ca439fb82fab7d0d480988be4bd4e62563a5ca35fadb5400727b1c",
|
|
||||||
"strip_prefix": "meson-1.1.1",
|
|
||||||
"urls": [
|
|
||||||
"https://mirror.bazel.build/github.com/mesonbuild/meson/releases/download/1.1.1/meson-1.1.1.tar.gz",
|
|
||||||
"https://github.com/mesonbuild/meson/releases/download/1.1.1/meson-1.1.1.tar.gz"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"rules_foreign_cc_framework_toolchain_freebsd": {
|
|
||||||
"bzlFile": "@@rules_foreign_cc~//foreign_cc/private/framework:toolchain.bzl",
|
|
||||||
"ruleClassName": "framework_toolchain_repository",
|
|
||||||
"attributes": {
|
|
||||||
"commands_src": "@rules_foreign_cc//foreign_cc/private/framework/toolchains:freebsd_commands.bzl",
|
|
||||||
"exec_compatible_with": [
|
|
||||||
"@platforms//os:freebsd"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"rules_foreign_cc_framework_toolchain_linux": {
|
|
||||||
"bzlFile": "@@rules_foreign_cc~//foreign_cc/private/framework:toolchain.bzl",
|
|
||||||
"ruleClassName": "framework_toolchain_repository",
|
|
||||||
"attributes": {
|
|
||||||
"commands_src": "@rules_foreign_cc//foreign_cc/private/framework/toolchains:linux_commands.bzl",
|
|
||||||
"exec_compatible_with": [
|
|
||||||
"@platforms//os:linux"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"rules_python": {
|
|
||||||
"bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
|
|
||||||
"ruleClassName": "http_archive",
|
|
||||||
"attributes": {
|
|
||||||
"sha256": "84aec9e21cc56fbc7f1335035a71c850d1b9b5cc6ff497306f84cced9a769841",
|
|
||||||
"strip_prefix": "rules_python-0.23.1",
|
|
||||||
"url": "https://github.com/bazelbuild/rules_python/archive/refs/tags/0.23.1.tar.gz"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"pkgconfig_src": {
|
|
||||||
"bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
|
|
||||||
"ruleClassName": "http_archive",
|
|
||||||
"attributes": {
|
|
||||||
"build_file_content": "filegroup(\n name = \"all_srcs\",\n srcs = glob([\"**\"]),\n visibility = [\"//visibility:public\"],\n)\n",
|
|
||||||
"sha256": "6fc69c01688c9458a57eb9a1664c9aba372ccda420a02bf4429fe610e7e7d591",
|
|
||||||
"strip_prefix": "pkg-config-0.29.2",
|
|
||||||
"patches": [
|
|
||||||
"@@rules_foreign_cc~//toolchains:pkgconfig-detectenv.patch",
|
|
||||||
"@@rules_foreign_cc~//toolchains:pkgconfig-makefile-vc.patch",
|
|
||||||
"@@rules_foreign_cc~//toolchains:pkgconfig-builtin-glib-int-conversion.patch"
|
|
||||||
],
|
|
||||||
"urls": [
|
|
||||||
"https://pkgconfig.freedesktop.org/releases/pkg-config-0.29.2.tar.gz",
|
|
||||||
"https://mirror.bazel.build/pkgconfig.freedesktop.org/releases/pkg-config-0.29.2.tar.gz"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ninja_build_src": {
|
|
||||||
"bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
|
|
||||||
"ruleClassName": "http_archive",
|
|
||||||
"attributes": {
|
|
||||||
"build_file_content": "filegroup(\n name = \"all_srcs\",\n srcs = glob([\"**\"]),\n visibility = [\"//visibility:public\"],\n)\n",
|
|
||||||
"integrity": "sha256-iyyGzUg9x/y3l1xexzKRNdIQCZqJvH2wWQoHsLv+SaU=",
|
|
||||||
"strip_prefix": "ninja-1.12.0",
|
|
||||||
"urls": [
|
|
||||||
"https://mirror.bazel.build/github.com/ninja-build/ninja/archive/v1.12.0.tar.gz",
|
|
||||||
"https://github.com/ninja-build/ninja/archive/v1.12.0.tar.gz"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"glib_src": {
|
|
||||||
"bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
|
|
||||||
"ruleClassName": "http_archive",
|
|
||||||
"attributes": {
|
|
||||||
"build_file_content": "\ncc_import(\n name = \"msvc_hdr\",\n hdrs = [\"msvc_recommended_pragmas.h\"],\n visibility = [\"//visibility:public\"],\n)\n ",
|
|
||||||
"sha256": "bc96f63112823b7d6c9f06572d2ad626ddac7eb452c04d762592197f6e07898e",
|
|
||||||
"strip_prefix": "glib-2.26.1",
|
|
||||||
"urls": [
|
|
||||||
"https://mirror.bazel.build/download.gnome.org/sources/glib/2.26/glib-2.26.1.tar.gz",
|
|
||||||
"https://download.gnome.org/sources/glib/2.26/glib-2.26.1.tar.gz"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"cmake-3.23.2-windows-x86_64": {
|
|
||||||
"bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
|
|
||||||
"ruleClassName": "http_archive",
|
|
||||||
"attributes": {
|
|
||||||
"urls": [
|
|
||||||
"https://github.com/Kitware/CMake/releases/download/v3.23.2/cmake-3.23.2-windows-x86_64.zip"
|
|
||||||
],
|
|
||||||
"sha256": "2329387f3166b84c25091c86389fb891193967740c9bcf01e7f6d3306f7ffda0",
|
|
||||||
"strip_prefix": "cmake-3.23.2-windows-x86_64",
|
|
||||||
"build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"cmake_bin\",\n srcs = [\"bin/cmake.exe\"],\n)\n\nfilegroup(\n name = \"cmake_data\",\n srcs = glob(\n [\n \"**\",\n ],\n exclude = [\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"**/* *\",\n ],\n ),\n)\n\nnative_tool_toolchain(\n name = \"cmake_tool\",\n path = \"bin/cmake.exe\",\n target = \":cmake_data\",\n env = {\"CMAKE\": \"$(execpath :cmake_bin)\"},\n tools = [\":cmake_bin\"],\n)\n"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"glib_runtime": {
|
|
||||||
"bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
|
|
||||||
"ruleClassName": "http_archive",
|
|
||||||
"attributes": {
|
|
||||||
"build_file_content": "\nexports_files(\n [\n \"bin/libgio-2.0-0.dll\",\n \"bin/libglib-2.0-0.dll\",\n \"bin/libgmodule-2.0-0.dll\",\n \"bin/libgobject-2.0-0.dll\",\n \"bin/libgthread-2.0-0.dll\",\n ],\n visibility = [\"//visibility:public\"],\n)\n ",
|
|
||||||
"sha256": "88d857087e86f16a9be651ee7021880b3f7ba050d34a1ed9f06113b8799cb973",
|
|
||||||
"urls": [
|
|
||||||
"https://mirror.bazel.build/download.gnome.org/binaries/win64/glib/2.26/glib_2.26.1-1_win64.zip",
|
|
||||||
"https://download.gnome.org/binaries/win64/glib/2.26/glib_2.26.1-1_win64.zip"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"rules_foreign_cc_framework_toolchains": {
|
|
||||||
"bzlFile": "@@rules_foreign_cc~//foreign_cc/private/framework:toolchain.bzl",
|
|
||||||
"ruleClassName": "framework_toolchain_repository_hub",
|
|
||||||
"attributes": {}
|
|
||||||
},
|
|
||||||
"glib_dev": {
|
|
||||||
"bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
|
|
||||||
"ruleClassName": "http_archive",
|
|
||||||
"attributes": {
|
|
||||||
"build_file_content": "\nload(\"@rules_cc//cc:defs.bzl\", \"cc_library\")\n\ncc_import(\n name = \"glib_dev\",\n hdrs = glob([\"include/**\"]),\n shared_library = \"@glib_runtime//:bin/libglib-2.0-0.dll\",\n visibility = [\"//visibility:public\"],\n)\n ",
|
|
||||||
"sha256": "bdf18506df304d38be98a4b3f18055b8b8cca81beabecad0eece6ce95319c369",
|
|
||||||
"urls": [
|
|
||||||
"https://mirror.bazel.build/download.gnome.org/binaries/win64/glib/2.26/glib-dev_2.26.1-1_win64.zip",
|
|
||||||
"https://download.gnome.org/binaries/win64/glib/2.26/glib-dev_2.26.1-1_win64.zip"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"cmake_3.23.2_toolchains": {
|
|
||||||
"bzlFile": "@@rules_foreign_cc~//toolchains:prebuilt_toolchains_repository.bzl",
|
|
||||||
"ruleClassName": "prebuilt_toolchains_repository",
|
|
||||||
"attributes": {
|
|
||||||
"repos": {
|
|
||||||
"cmake-3.23.2-linux-aarch64": [
|
|
||||||
"@platforms//cpu:aarch64",
|
|
||||||
"@platforms//os:linux"
|
|
||||||
],
|
|
||||||
"cmake-3.23.2-linux-x86_64": [
|
|
||||||
"@platforms//cpu:x86_64",
|
|
||||||
"@platforms//os:linux"
|
|
||||||
],
|
|
||||||
"cmake-3.23.2-macos-universal": [
|
|
||||||
"@platforms//os:macos"
|
|
||||||
],
|
|
||||||
"cmake-3.23.2-windows-i386": [
|
|
||||||
"@platforms//cpu:x86_32",
|
|
||||||
"@platforms//os:windows"
|
|
||||||
],
|
|
||||||
"cmake-3.23.2-windows-x86_64": [
|
|
||||||
"@platforms//cpu:x86_64",
|
|
||||||
"@platforms//os:windows"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"tool": "cmake"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ninja_1.12.0_linux-aarch64": {
|
|
||||||
"bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
|
|
||||||
"ruleClassName": "http_archive",
|
|
||||||
"attributes": {
|
|
||||||
"urls": [
|
|
||||||
"https://github.com/ninja-build/ninja/releases/download/v1.12.0/ninja-linux-aarch64.zip"
|
|
||||||
],
|
|
||||||
"sha256": "375a49c79095334c88338ff15f90730e08a4d03997ef660f48f11ee7e450db7a",
|
|
||||||
"strip_prefix": "",
|
|
||||||
"build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"ninja_bin\",\n srcs = [\"ninja\"],\n)\n\nnative_tool_toolchain(\n name = \"ninja_tool\",\n env = {\"NINJA\": \"$(execpath :ninja_bin)\"},\n path = \"$(execpath :ninja_bin)\",\n target = \":ninja_bin\",\n)\n"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"cmake-3.23.2-windows-i386": {
|
|
||||||
"bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
|
|
||||||
"ruleClassName": "http_archive",
|
|
||||||
"attributes": {
|
|
||||||
"urls": [
|
|
||||||
"https://github.com/Kitware/CMake/releases/download/v3.23.2/cmake-3.23.2-windows-i386.zip"
|
|
||||||
],
|
|
||||||
"sha256": "6a4fcd6a2315b93cb23c93507efccacc30c449c2bf98f14d6032bb226c582e07",
|
|
||||||
"strip_prefix": "cmake-3.23.2-windows-i386",
|
|
||||||
"build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"cmake_bin\",\n srcs = [\"bin/cmake.exe\"],\n)\n\nfilegroup(\n name = \"cmake_data\",\n srcs = glob(\n [\n \"**\",\n ],\n exclude = [\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"**/* *\",\n ],\n ),\n)\n\nnative_tool_toolchain(\n name = \"cmake_tool\",\n path = \"bin/cmake.exe\",\n target = \":cmake_data\",\n env = {\"CMAKE\": \"$(execpath :cmake_bin)\"},\n tools = [\":cmake_bin\"],\n)\n"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ninja_1.12.0_linux": {
|
|
||||||
"bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
|
|
||||||
"ruleClassName": "http_archive",
|
|
||||||
"attributes": {
|
|
||||||
"urls": [
|
|
||||||
"https://github.com/ninja-build/ninja/releases/download/v1.12.0/ninja-linux.zip"
|
|
||||||
],
|
|
||||||
"sha256": "ddc96efa3c7c9d41de733d15e2eda07a8a212555cb43f35d727e080d2ca687ab",
|
|
||||||
"strip_prefix": "",
|
|
||||||
"build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"ninja_bin\",\n srcs = [\"ninja\"],\n)\n\nnative_tool_toolchain(\n name = \"ninja_tool\",\n env = {\"NINJA\": \"$(execpath :ninja_bin)\"},\n path = \"$(execpath :ninja_bin)\",\n target = \":ninja_bin\",\n)\n"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"cmake-3.23.2-linux-x86_64": {
|
|
||||||
"bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
|
|
||||||
"ruleClassName": "http_archive",
|
|
||||||
"attributes": {
|
|
||||||
"urls": [
|
|
||||||
"https://github.com/Kitware/CMake/releases/download/v3.23.2/cmake-3.23.2-linux-x86_64.tar.gz"
|
|
||||||
],
|
|
||||||
"sha256": "aaced6f745b86ce853661a595bdac6c5314a60f8181b6912a0a4920acfa32708",
|
|
||||||
"strip_prefix": "cmake-3.23.2-linux-x86_64",
|
|
||||||
"build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"cmake_bin\",\n srcs = [\"bin/cmake\"],\n)\n\nfilegroup(\n name = \"cmake_data\",\n srcs = glob(\n [\n \"**\",\n ],\n exclude = [\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n \"BUILD\",\n \"BUILD.bazel\",\n \"**/* *\",\n ],\n ),\n)\n\nnative_tool_toolchain(\n name = \"cmake_tool\",\n path = \"bin/cmake\",\n target = \":cmake_data\",\n env = {\"CMAKE\": \"$(execpath :cmake_bin)\"},\n tools = [\":cmake_bin\"],\n)\n"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ninja_1.12.0_toolchains": {
|
|
||||||
"bzlFile": "@@rules_foreign_cc~//toolchains:prebuilt_toolchains_repository.bzl",
|
|
||||||
"ruleClassName": "prebuilt_toolchains_repository",
|
|
||||||
"attributes": {
|
|
||||||
"repos": {
|
|
||||||
"ninja_1.12.0_linux": [
|
|
||||||
"@platforms//cpu:x86_64",
|
|
||||||
"@platforms//os:linux"
|
|
||||||
],
|
|
||||||
"ninja_1.12.0_linux-aarch64": [
|
|
||||||
"@platforms//cpu:aarch64",
|
|
||||||
"@platforms//os:linux"
|
|
||||||
],
|
|
||||||
"ninja_1.12.0_mac": [
|
|
||||||
"@platforms//cpu:x86_64",
|
|
||||||
"@platforms//os:macos"
|
|
||||||
],
|
|
||||||
"ninja_1.12.0_mac_aarch64": [
|
|
||||||
"@platforms//cpu:aarch64",
|
|
||||||
"@platforms//os:macos"
|
|
||||||
],
|
|
||||||
"ninja_1.12.0_win": [
|
|
||||||
"@platforms//cpu:x86_64",
|
|
||||||
"@platforms//os:windows"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"tool": "ninja"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"rules_foreign_cc_framework_toolchain_windows": {
|
|
||||||
"bzlFile": "@@rules_foreign_cc~//foreign_cc/private/framework:toolchain.bzl",
|
|
||||||
"ruleClassName": "framework_toolchain_repository",
|
|
||||||
"attributes": {
|
|
||||||
"commands_src": "@rules_foreign_cc//foreign_cc/private/framework/toolchains:windows_commands.bzl",
|
|
||||||
"exec_compatible_with": [
|
|
||||||
"@platforms//os:windows"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"recordedRepoMappingEntries": [
|
|
||||||
[
|
|
||||||
"rules_foreign_cc~",
|
|
||||||
"bazel_tools",
|
|
||||||
"bazel_tools"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"rules_foreign_cc~",
|
|
||||||
"rules_foreign_cc",
|
|
||||||
"rules_foreign_cc~"
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@@rules_python~//python/extensions:python.bzl%python": {
|
|
||||||
"general": {
|
|
||||||
"bzlTransitiveDigest": "XaaZIw4dO4l6naftU5IBdrfCE1mOmelaT/Sq9uyBnhs=",
|
|
||||||
"usagesDigest": "XRXGQ1YSlgZzzO0pux+3DEHfP/c70L/kznvRIwakvlw=",
|
|
||||||
"recordedFileInputs": {},
|
|
||||||
"recordedDirentsInputs": {},
|
|
||||||
"envVariables": {},
|
|
||||||
"generatedRepoSpecs": {
|
|
||||||
"python_3_11_aarch64-unknown-linux-gnu": {
|
|
||||||
"bzlFile": "@@rules_python~//python:repositories.bzl",
|
|
||||||
"ruleClassName": "python_repository",
|
|
||||||
"attributes": {
|
|
||||||
"sha256": "debf15783bdcb5530504f533d33fda75a7b905cec5361ae8f33da5ba6599f8b4",
|
|
||||||
"patches": [],
|
|
||||||
"platform": "aarch64-unknown-linux-gnu",
|
|
||||||
"python_version": "3.11.1",
|
|
||||||
"release_filename": "20230116/cpython-3.11.1+20230116-aarch64-unknown-linux-gnu-install_only.tar.gz",
|
|
||||||
"urls": [
|
|
||||||
"https://github.com/indygreg/python-build-standalone/releases/download/20230116/cpython-3.11.1+20230116-aarch64-unknown-linux-gnu-install_only.tar.gz"
|
|
||||||
],
|
|
||||||
"distutils_content": "",
|
|
||||||
"strip_prefix": "python",
|
|
||||||
"ignore_root_user_error": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"python_3_9": {
|
|
||||||
"bzlFile": "@@rules_python~//python/private:toolchains_repo.bzl",
|
|
||||||
"ruleClassName": "toolchain_aliases",
|
|
||||||
"attributes": {
|
|
||||||
"python_version": "3.9.16",
|
|
||||||
"user_repository_name": "python_3_9"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"python_3_11_aarch64-apple-darwin": {
|
|
||||||
"bzlFile": "@@rules_python~//python:repositories.bzl",
|
|
||||||
"ruleClassName": "python_repository",
|
|
||||||
"attributes": {
|
|
||||||
"sha256": "4918cdf1cab742a90f85318f88b8122aeaa2d04705803c7b6e78e81a3dd40f80",
|
|
||||||
"patches": [],
|
|
||||||
"platform": "aarch64-apple-darwin",
|
|
||||||
"python_version": "3.11.1",
|
|
||||||
"release_filename": "20230116/cpython-3.11.1+20230116-aarch64-apple-darwin-install_only.tar.gz",
|
|
||||||
"urls": [
|
|
||||||
"https://github.com/indygreg/python-build-standalone/releases/download/20230116/cpython-3.11.1+20230116-aarch64-apple-darwin-install_only.tar.gz"
|
|
||||||
],
|
|
||||||
"distutils_content": "",
|
|
||||||
"strip_prefix": "python",
|
|
||||||
"ignore_root_user_error": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"pythons_hub": {
|
|
||||||
"bzlFile": "@@rules_python~//python/extensions/private:pythons_hub.bzl",
|
|
||||||
"ruleClassName": "hub_repo",
|
|
||||||
"attributes": {
|
|
||||||
"toolchain_prefixes": [
|
|
||||||
"_0000_python_3_9_",
|
|
||||||
"_0001_python_3_11_"
|
|
||||||
],
|
|
||||||
"toolchain_python_versions": [
|
|
||||||
"3.9",
|
|
||||||
"3.11"
|
|
||||||
],
|
|
||||||
"toolchain_set_python_version_constraints": [
|
|
||||||
"True",
|
|
||||||
"False"
|
|
||||||
],
|
|
||||||
"toolchain_user_repository_names": [
|
|
||||||
"python_3_9",
|
|
||||||
"python_3_11"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"python_3_11_x86_64-pc-windows-msvc": {
|
|
||||||
"bzlFile": "@@rules_python~//python:repositories.bzl",
|
|
||||||
"ruleClassName": "python_repository",
|
|
||||||
"attributes": {
|
|
||||||
"sha256": "edc08979cb0666a597466176511529c049a6f0bba8adf70df441708f766de5bf",
|
|
||||||
"patches": [],
|
|
||||||
"platform": "x86_64-pc-windows-msvc",
|
|
||||||
"python_version": "3.11.1",
|
|
||||||
"release_filename": "20230116/cpython-3.11.1+20230116-x86_64-pc-windows-msvc-shared-install_only.tar.gz",
|
|
||||||
"urls": [
|
|
||||||
"https://github.com/indygreg/python-build-standalone/releases/download/20230116/cpython-3.11.1+20230116-x86_64-pc-windows-msvc-shared-install_only.tar.gz"
|
|
||||||
],
|
|
||||||
"distutils_content": "",
|
|
||||||
"strip_prefix": "python",
|
|
||||||
"ignore_root_user_error": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"python_3_9_aarch64-apple-darwin": {
|
|
||||||
"bzlFile": "@@rules_python~//python:repositories.bzl",
|
|
||||||
"ruleClassName": "python_repository",
|
|
||||||
"attributes": {
|
|
||||||
"sha256": "c1de1d854717a6245f45262ef1bb17b09e2c587590e7e3f406593c143ff875bd",
|
|
||||||
"patches": [],
|
|
||||||
"platform": "aarch64-apple-darwin",
|
|
||||||
"python_version": "3.9.16",
|
|
||||||
"release_filename": "20230507/cpython-3.9.16+20230507-aarch64-apple-darwin-install_only.tar.gz",
|
|
||||||
"urls": [
|
|
||||||
"https://github.com/indygreg/python-build-standalone/releases/download/20230507/cpython-3.9.16+20230507-aarch64-apple-darwin-install_only.tar.gz"
|
|
||||||
],
|
|
||||||
"distutils_content": "",
|
|
||||||
"strip_prefix": "python",
|
|
||||||
"ignore_root_user_error": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"python_3_9_x86_64-pc-windows-msvc": {
|
|
||||||
"bzlFile": "@@rules_python~//python:repositories.bzl",
|
|
||||||
"ruleClassName": "python_repository",
|
|
||||||
"attributes": {
|
|
||||||
"sha256": "cdabb47204e96ce7ea31fbd0b5ed586114dd7d8f8eddf60a509a7f70b48a1c5e",
|
|
||||||
"patches": [],
|
|
||||||
"platform": "x86_64-pc-windows-msvc",
|
|
||||||
"python_version": "3.9.16",
|
|
||||||
"release_filename": "20230507/cpython-3.9.16+20230507-x86_64-pc-windows-msvc-shared-install_only.tar.gz",
|
|
||||||
"urls": [
|
|
||||||
"https://github.com/indygreg/python-build-standalone/releases/download/20230507/cpython-3.9.16+20230507-x86_64-pc-windows-msvc-shared-install_only.tar.gz"
|
|
||||||
],
|
|
||||||
"distutils_content": "",
|
|
||||||
"strip_prefix": "python",
|
|
||||||
"ignore_root_user_error": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"python_3_9_ppc64le-unknown-linux-gnu": {
|
|
||||||
"bzlFile": "@@rules_python~//python:repositories.bzl",
|
|
||||||
"ruleClassName": "python_repository",
|
|
||||||
"attributes": {
|
|
||||||
"sha256": "ff3ac35c58f67839aff9b5185a976abd3d1abbe61af02089f7105e876c1fe284",
|
|
||||||
"patches": [],
|
|
||||||
"platform": "ppc64le-unknown-linux-gnu",
|
|
||||||
"python_version": "3.9.16",
|
|
||||||
"release_filename": "20230507/cpython-3.9.16+20230507-ppc64le-unknown-linux-gnu-install_only.tar.gz",
|
|
||||||
"urls": [
|
|
||||||
"https://github.com/indygreg/python-build-standalone/releases/download/20230507/cpython-3.9.16+20230507-ppc64le-unknown-linux-gnu-install_only.tar.gz"
|
|
||||||
],
|
|
||||||
"distutils_content": "",
|
|
||||||
"strip_prefix": "python",
|
|
||||||
"ignore_root_user_error": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"python_3_9_aarch64-unknown-linux-gnu": {
|
|
||||||
"bzlFile": "@@rules_python~//python:repositories.bzl",
|
|
||||||
"ruleClassName": "python_repository",
|
|
||||||
"attributes": {
|
|
||||||
"sha256": "f629b75ebfcafe9ceee2e796b7e4df5cf8dbd14f3c021afca078d159ab797acf",
|
|
||||||
"patches": [],
|
|
||||||
"platform": "aarch64-unknown-linux-gnu",
|
|
||||||
"python_version": "3.9.16",
|
|
||||||
"release_filename": "20230507/cpython-3.9.16+20230507-aarch64-unknown-linux-gnu-install_only.tar.gz",
|
|
||||||
"urls": [
|
|
||||||
"https://github.com/indygreg/python-build-standalone/releases/download/20230507/cpython-3.9.16+20230507-aarch64-unknown-linux-gnu-install_only.tar.gz"
|
|
||||||
],
|
|
||||||
"distutils_content": "",
|
|
||||||
"strip_prefix": "python",
|
|
||||||
"ignore_root_user_error": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"python_aliases": {
|
|
||||||
"bzlFile": "@@rules_python~//python/private:toolchains_repo.bzl",
|
|
||||||
"ruleClassName": "multi_toolchain_aliases",
|
|
||||||
"attributes": {
|
|
||||||
"python_versions": {
|
|
||||||
"3.9": "python_3_9",
|
|
||||||
"3.11": "python_3_11"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"python_3_11": {
|
|
||||||
"bzlFile": "@@rules_python~//python/private:toolchains_repo.bzl",
|
|
||||||
"ruleClassName": "toolchain_aliases",
|
|
||||||
"attributes": {
|
|
||||||
"python_version": "3.11.1",
|
|
||||||
"user_repository_name": "python_3_11"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"python_3_11_x86_64-apple-darwin": {
|
|
||||||
"bzlFile": "@@rules_python~//python:repositories.bzl",
|
|
||||||
"ruleClassName": "python_repository",
|
|
||||||
"attributes": {
|
|
||||||
"sha256": "20a4203d069dc9b710f70b09e7da2ce6f473d6b1110f9535fb6f4c469ed54733",
|
|
||||||
"patches": [],
|
|
||||||
"platform": "x86_64-apple-darwin",
|
|
||||||
"python_version": "3.11.1",
|
|
||||||
"release_filename": "20230116/cpython-3.11.1+20230116-x86_64-apple-darwin-install_only.tar.gz",
|
|
||||||
"urls": [
|
|
||||||
"https://github.com/indygreg/python-build-standalone/releases/download/20230116/cpython-3.11.1+20230116-x86_64-apple-darwin-install_only.tar.gz"
|
|
||||||
],
|
|
||||||
"distutils_content": "",
|
|
||||||
"strip_prefix": "python",
|
|
||||||
"ignore_root_user_error": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"python_3_9_x86_64-apple-darwin": {
|
|
||||||
"bzlFile": "@@rules_python~//python:repositories.bzl",
|
|
||||||
"ruleClassName": "python_repository",
|
|
||||||
"attributes": {
|
|
||||||
"sha256": "3abc4d5fbbc80f5f848f280927ac5d13de8dc03aabb6ae65d8247cbb68e6f6bf",
|
|
||||||
"patches": [],
|
|
||||||
"platform": "x86_64-apple-darwin",
|
|
||||||
"python_version": "3.9.16",
|
|
||||||
"release_filename": "20230507/cpython-3.9.16+20230507-x86_64-apple-darwin-install_only.tar.gz",
|
|
||||||
"urls": [
|
|
||||||
"https://github.com/indygreg/python-build-standalone/releases/download/20230507/cpython-3.9.16+20230507-x86_64-apple-darwin-install_only.tar.gz"
|
|
||||||
],
|
|
||||||
"distutils_content": "",
|
|
||||||
"strip_prefix": "python",
|
|
||||||
"ignore_root_user_error": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"python_3_9_x86_64-unknown-linux-gnu": {
|
|
||||||
"bzlFile": "@@rules_python~//python:repositories.bzl",
|
|
||||||
"ruleClassName": "python_repository",
|
|
||||||
"attributes": {
|
|
||||||
"sha256": "2b6e146234a4ef2a8946081fc3fbfffe0765b80b690425a49ebe40b47c33445b",
|
|
||||||
"patches": [],
|
|
||||||
"platform": "x86_64-unknown-linux-gnu",
|
|
||||||
"python_version": "3.9.16",
|
|
||||||
"release_filename": "20230507/cpython-3.9.16+20230507-x86_64-unknown-linux-gnu-install_only.tar.gz",
|
|
||||||
"urls": [
|
|
||||||
"https://github.com/indygreg/python-build-standalone/releases/download/20230507/cpython-3.9.16+20230507-x86_64-unknown-linux-gnu-install_only.tar.gz"
|
|
||||||
],
|
|
||||||
"distutils_content": "",
|
|
||||||
"strip_prefix": "python",
|
|
||||||
"ignore_root_user_error": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"python_3_11_x86_64-unknown-linux-gnu": {
|
|
||||||
"bzlFile": "@@rules_python~//python:repositories.bzl",
|
|
||||||
"ruleClassName": "python_repository",
|
|
||||||
"attributes": {
|
|
||||||
"sha256": "02a551fefab3750effd0e156c25446547c238688a32fabde2995c941c03a6423",
|
|
||||||
"patches": [],
|
|
||||||
"platform": "x86_64-unknown-linux-gnu",
|
|
||||||
"python_version": "3.11.1",
|
|
||||||
"release_filename": "20230116/cpython-3.11.1+20230116-x86_64-unknown-linux-gnu-install_only.tar.gz",
|
|
||||||
"urls": [
|
|
||||||
"https://github.com/indygreg/python-build-standalone/releases/download/20230116/cpython-3.11.1+20230116-x86_64-unknown-linux-gnu-install_only.tar.gz"
|
|
||||||
],
|
|
||||||
"distutils_content": "",
|
|
||||||
"strip_prefix": "python",
|
|
||||||
"ignore_root_user_error": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"recordedRepoMappingEntries": [
|
|
||||||
[
|
|
||||||
"rules_python~",
|
|
||||||
"bazel_tools",
|
|
||||||
"bazel_tools"
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@@toolchains_llvm~//toolchain/extensions:llvm.bzl%llvm": {
|
|
||||||
"general": {
|
|
||||||
"bzlTransitiveDigest": "y9h5L2NtWbogyWSOJgqnUaU50MTPWAW+waelXSirMVg=",
|
|
||||||
"usagesDigest": "cWIs+RUBBwv1tE2cNVULmSLCA/wGvHWaQzdrujSEO74=",
|
|
||||||
"recordedFileInputs": {},
|
|
||||||
"recordedDirentsInputs": {},
|
|
||||||
"envVariables": {},
|
|
||||||
"generatedRepoSpecs": {
|
|
||||||
"llvm_toolchain": {
|
|
||||||
"bzlFile": "@@toolchains_llvm~//toolchain:rules.bzl",
|
|
||||||
"ruleClassName": "toolchain",
|
|
||||||
"attributes": {
|
|
||||||
"absolute_paths": false,
|
|
||||||
"archive_flags": {},
|
|
||||||
"compile_flags": {},
|
|
||||||
"coverage_compile_flags": {},
|
|
||||||
"coverage_link_flags": {},
|
|
||||||
"cxx_builtin_include_directories": {},
|
|
||||||
"cxx_flags": {},
|
|
||||||
"cxx_standard": {},
|
|
||||||
"dbg_compile_flags": {},
|
|
||||||
"exec_arch": "",
|
|
||||||
"exec_os": "",
|
|
||||||
"link_flags": {},
|
|
||||||
"link_libs": {},
|
|
||||||
"llvm_versions": {
|
|
||||||
"": "18.1.8"
|
|
||||||
},
|
|
||||||
"opt_compile_flags": {},
|
|
||||||
"opt_link_flags": {},
|
|
||||||
"stdlib": {},
|
|
||||||
"target_settings": {},
|
|
||||||
"unfiltered_compile_flags": {},
|
|
||||||
"toolchain_roots": {},
|
|
||||||
"sysroot": {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"llvm_toolchain_llvm": {
|
|
||||||
"bzlFile": "@@toolchains_llvm~//toolchain:rules.bzl",
|
|
||||||
"ruleClassName": "llvm",
|
|
||||||
"attributes": {
|
|
||||||
"alternative_llvm_sources": [],
|
|
||||||
"auth_patterns": {},
|
|
||||||
"distribution": "auto",
|
|
||||||
"exec_arch": "",
|
|
||||||
"exec_os": "",
|
|
||||||
"llvm_mirror": "",
|
|
||||||
"llvm_version": "",
|
|
||||||
"llvm_versions": {
|
|
||||||
"": "18.1.8"
|
|
||||||
},
|
|
||||||
"netrc": "",
|
|
||||||
"sha256": {
|
|
||||||
"": "54ec30358afcc9fb8aa74307db3046f5187f9fb89fb37064cdde906e062ebf36"
|
|
||||||
},
|
|
||||||
"strip_prefix": {
|
|
||||||
"": "clang+llvm-18.1.8-x86_64-linux-gnu-ubuntu-18.04"
|
|
||||||
},
|
|
||||||
"urls": {
|
|
||||||
"": [
|
|
||||||
"https://github.com/llvm/llvm-project/releases/download/llvmorg-18.1.8/clang+llvm-18.1.8-x86_64-linux-gnu-ubuntu-18.04.tar.xz"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"recordedRepoMappingEntries": [
|
|
||||||
[
|
|
||||||
"toolchains_llvm~",
|
|
||||||
"bazel_skylib",
|
|
||||||
"bazel_skylib~"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"toolchains_llvm~",
|
|
||||||
"bazel_tools",
|
|
||||||
"bazel_tools"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"toolchains_llvm~",
|
|
||||||
"toolchains_llvm",
|
|
||||||
"toolchains_llvm~"
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
63
Makefile
63
Makefile
@ -1,63 +0,0 @@
|
|||||||
default: help
|
|
||||||
|
|
||||||
# The general philosophy and functionality of this makefile is shamelessly stolen from compiler explorer
|
|
||||||
|
|
||||||
help: # with thanks to Ben Rady
|
|
||||||
@grep -E '^[0-9a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
|
|
||||||
|
|
||||||
.PHONY: build
|
|
||||||
build: debug ## build in debug mode
|
|
||||||
|
|
||||||
build/configured-debug:
|
|
||||||
cmake -S . -B build -GNinja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS=On -DCPPTRACE_BUILD_TESTING=On -DCPPTRACE_BUILD_TOOLS=On
|
|
||||||
rm -f build/configured-release
|
|
||||||
touch build/configured-debug
|
|
||||||
|
|
||||||
build/configured-release:
|
|
||||||
cmake -S . -B build -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_EXPORT_COMPILE_COMMANDS=On -DCPPTRACE_BUILD_TESTING=On -DCPPTRACE_BUILD_TOOLS=On
|
|
||||||
rm -f build/configured-debug
|
|
||||||
touch build/configured-release
|
|
||||||
|
|
||||||
.PHONY: configure-debug
|
|
||||||
configure-debug: build/configured-debug
|
|
||||||
|
|
||||||
.PHONY: configure-release
|
|
||||||
configure-release: build/configured-release
|
|
||||||
|
|
||||||
.PHONY: debug
|
|
||||||
debug: configure-debug ## build in debug mode
|
|
||||||
cmake --build build
|
|
||||||
|
|
||||||
.PHONY: release
|
|
||||||
release: configure-release ## build in release mode (with debug info)
|
|
||||||
cmake --build build
|
|
||||||
|
|
||||||
.PHONY: debug-msvc
|
|
||||||
debug-msvc: ## build in debug mode
|
|
||||||
cmake -S . -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=On -DCPPTRACE_BUILD_TESTING=On -DCPPTRACE_BUILD_TOOLS=On
|
|
||||||
cmake --build build --config Debug
|
|
||||||
|
|
||||||
.PHONY: release-msvc
|
|
||||||
release-msvc: ## build in release mode (with debug info)
|
|
||||||
cmake -S . -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=On -DCPPTRACE_BUILD_TESTING=On -DCPPTRACE_BUILD_TOOLS=On
|
|
||||||
cmake --build build --config RelWithDebInfo
|
|
||||||
|
|
||||||
.PHONY: clean
|
|
||||||
clean: ## clean
|
|
||||||
rm -rf build
|
|
||||||
|
|
||||||
.PHONY: test
|
|
||||||
test: debug ## test
|
|
||||||
cd build && ninja test
|
|
||||||
|
|
||||||
.PHONY: test-release
|
|
||||||
test-release: release ## test-release
|
|
||||||
cd build && ninja test
|
|
||||||
|
|
||||||
# .PHONY: test-msvc
|
|
||||||
# test-msvc: debug-msvc ## test
|
|
||||||
# cmake --build build --target RUN_TESTS --config Debug
|
|
||||||
|
|
||||||
# .PHONY: test-msvc-release
|
|
||||||
# test-msvc-release: release-msvc ## test-release
|
|
||||||
# cmake --build build --target RUN_TESTS --config Release
|
|
||||||
@ -6,7 +6,7 @@ We take security seriously and I'm grateful for reports of security vulnerabilit
|
|||||||
|
|
||||||
If the vulnerability can be reported without revealing exploitable specifics, please open an issue.
|
If the vulnerability can be reported without revealing exploitable specifics, please open an issue.
|
||||||
|
|
||||||
If the vulnerability can't be reported publicly without leaving an obvious exploit in the public eye please email me
|
If the vulnerability can't be reported publically without leaving an obvious exploit in the public eye please email me
|
||||||
at jeremy@rifkin.dev or reach out to me on [discord](https://discord.gg/7kv5AuCndG).
|
at jeremy@rifkin.dev or reach out to me on [discord](https://discord.gg/7kv5AuCndG).
|
||||||
|
|
||||||
I will do my best to get back to you within a day.
|
I will do my best to get back to you within a day.
|
||||||
|
|||||||
@ -1,21 +0,0 @@
|
|||||||
include(CTest)
|
|
||||||
|
|
||||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
|
||||||
|
|
||||||
set(
|
|
||||||
warning_options
|
|
||||||
${warning_options} $<$<CXX_COMPILER_ID:GNU>:-Wno-infinite-recursion>
|
|
||||||
)
|
|
||||||
|
|
||||||
include(FetchContent)
|
|
||||||
set(BENCHMARK_ENABLE_TESTING OFF)
|
|
||||||
FetchContent_Declare(
|
|
||||||
googlebench
|
|
||||||
GIT_REPOSITORY "https://github.com/google/benchmark.git"
|
|
||||||
GIT_TAG 12235e24652fc7f809373e7c11a5f73c5763fc4c # v1.9.0
|
|
||||||
)
|
|
||||||
FetchContent_MakeAvailable(googlebench)
|
|
||||||
|
|
||||||
add_executable(benchmark_unwinding unwinding.cpp)
|
|
||||||
target_compile_features(benchmark_unwinding PRIVATE cxx_std_20)
|
|
||||||
target_link_libraries(benchmark_unwinding PRIVATE ${target_name} benchmark::benchmark)
|
|
||||||
@ -1,55 +0,0 @@
|
|||||||
#include <cpptrace/cpptrace.hpp>
|
|
||||||
|
|
||||||
#include <benchmark/benchmark.h>
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
struct unwind_benchmark_info {
|
|
||||||
benchmark::State& state;
|
|
||||||
size_t& stack_depth;
|
|
||||||
};
|
|
||||||
|
|
||||||
void unwind_loop(unwind_benchmark_info info) {
|
|
||||||
auto& [state, depth] = info;
|
|
||||||
depth = cpptrace::generate_raw_trace().frames.size();
|
|
||||||
for(auto _ : state) {
|
|
||||||
benchmark::DoNotOptimize(cpptrace::generate_raw_trace());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void foo(unwind_benchmark_info info, int n) {
|
|
||||||
if(n == 0) {
|
|
||||||
unwind_loop(info);
|
|
||||||
} else {
|
|
||||||
foo(info, n - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
void foo(unwind_benchmark_info info, int, Args... args) {
|
|
||||||
foo(info, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
void function_two(unwind_benchmark_info info, int, float) {
|
|
||||||
foo(info, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
void function_one(unwind_benchmark_info info, int) {
|
|
||||||
function_two(info, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void unwinding(benchmark::State& state) {
|
|
||||||
size_t stack_depth = 0;
|
|
||||||
function_one({state, stack_depth}, 0);
|
|
||||||
static bool did_print = false;
|
|
||||||
if(!did_print) {
|
|
||||||
did_print = true;
|
|
||||||
std::cerr<<"[info] Unwinding benchmark stack depth: "<<stack_depth<<std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register the function as a benchmark
|
|
||||||
BENCHMARK(unwinding);
|
|
||||||
|
|
||||||
// Run the benchmark
|
|
||||||
BENCHMARK_MAIN();
|
|
||||||
279
ci/build-in-all-configs.py
Normal file
279
ci/build-in-all-configs.py
Normal file
@ -0,0 +1,279 @@
|
|||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import platform
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from colorama import Fore, Back, Style
|
||||||
|
|
||||||
|
from util import *
|
||||||
|
|
||||||
|
sys.stdout.reconfigure(encoding='utf-8') # for windows gh runner
|
||||||
|
|
||||||
|
failed = False
|
||||||
|
|
||||||
|
def run_command(*args: List[str]):
|
||||||
|
print(f"{Fore.CYAN}{Style.BRIGHT}Running Command \"{' '.join(args)}\"{Style.RESET_ALL}")
|
||||||
|
p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
|
stdout, stderr = p.communicate()
|
||||||
|
print(Style.RESET_ALL, end="") # makefile in parallel sometimes messes up colors
|
||||||
|
if p.returncode != 0:
|
||||||
|
print(f"{Fore.RED}{Style.BRIGHT}Command failed{Style.RESET_ALL}")
|
||||||
|
print("stdout:")
|
||||||
|
print(stdout.decode("utf-8"), end="")
|
||||||
|
print("stderr:")
|
||||||
|
print(stderr.decode("utf-8"), end="")
|
||||||
|
global failed
|
||||||
|
failed = True
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
print(f"{Fore.GREEN}{Style.BRIGHT}Command succeeded{Style.RESET_ALL}")
|
||||||
|
return True
|
||||||
|
|
||||||
|
def build(matrix):
|
||||||
|
print(f"{Fore.BLUE}{Style.BRIGHT}{'=' * 10} Running build with config {', '.join(matrix.values())} {'=' * 10}{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
if os.path.exists("build"):
|
||||||
|
shutil.rmtree("build")
|
||||||
|
|
||||||
|
os.mkdir("build")
|
||||||
|
os.chdir("build")
|
||||||
|
|
||||||
|
if platform.system() != "Windows":
|
||||||
|
succeeded = run_command(
|
||||||
|
"cmake",
|
||||||
|
"..",
|
||||||
|
f"-DCMAKE_BUILD_TYPE={matrix['target']}",
|
||||||
|
f"-DCMAKE_CXX_COMPILER={matrix['compiler']}",
|
||||||
|
f"-DCMAKE_CXX_STANDARD={matrix['std']}",
|
||||||
|
f"-D{matrix['unwind']}=On",
|
||||||
|
f"-D{matrix['symbols']}=On",
|
||||||
|
f"-D{matrix['demangle']}=On",
|
||||||
|
"-DCPPTRACE_BACKTRACE_PATH=/usr/lib/gcc/x86_64-linux-gnu/10/include/backtrace.h"
|
||||||
|
)
|
||||||
|
if succeeded:
|
||||||
|
run_command("make", "-j")
|
||||||
|
else:
|
||||||
|
args = [
|
||||||
|
"cmake",
|
||||||
|
"..",
|
||||||
|
f"-DCMAKE_BUILD_TYPE={matrix['target']}",
|
||||||
|
f"-DCMAKE_CXX_COMPILER={matrix['compiler']}",
|
||||||
|
f"-DCMAKE_CXX_STANDARD={matrix['std']}",
|
||||||
|
f"-D{matrix['unwind']}=On",
|
||||||
|
f"-D{matrix['symbols']}=On",
|
||||||
|
f"-D{matrix['demangle']}=On"
|
||||||
|
]
|
||||||
|
if matrix["compiler"] == "g++":
|
||||||
|
args.append("-GUnix Makefiles")
|
||||||
|
succeeded = run_command(*args)
|
||||||
|
if succeeded:
|
||||||
|
if matrix["compiler"] == "g++":
|
||||||
|
run_command("make", "-j")
|
||||||
|
else:
|
||||||
|
run_command("msbuild", "cpptrace.sln")
|
||||||
|
|
||||||
|
os.chdir("..")
|
||||||
|
print()
|
||||||
|
|
||||||
|
def build_full_or_auto(matrix):
|
||||||
|
print(f"{Fore.BLUE}{Style.BRIGHT}{'=' * 10} Running build with config {'<auto>' if matrix['config'] == '' else ', '.join(matrix.values())} {'=' * 10}{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
if os.path.exists("build"):
|
||||||
|
shutil.rmtree("build")
|
||||||
|
|
||||||
|
os.mkdir("build")
|
||||||
|
os.chdir("build")
|
||||||
|
|
||||||
|
if platform.system() != "Windows":
|
||||||
|
args = [
|
||||||
|
"cmake",
|
||||||
|
"..",
|
||||||
|
f"-DCMAKE_BUILD_TYPE={matrix['target']}",
|
||||||
|
f"-DCMAKE_CXX_COMPILER={matrix['compiler']}",
|
||||||
|
f"-DCMAKE_CXX_STANDARD={matrix['std']}",
|
||||||
|
f"-DCPPTRACE_BACKTRACE_PATH=/usr/lib/gcc/x86_64-linux-gnu/10/include/backtrace.h",
|
||||||
|
]
|
||||||
|
if matrix["config"] != "":
|
||||||
|
args.append(f"{matrix['config']}")
|
||||||
|
succeeded = run_command(*args)
|
||||||
|
if succeeded:
|
||||||
|
run_command("make", "-j")
|
||||||
|
else:
|
||||||
|
args = [
|
||||||
|
"cmake",
|
||||||
|
"..",
|
||||||
|
f"-DCMAKE_BUILD_TYPE={matrix['target']}",
|
||||||
|
f"-DCMAKE_CXX_COMPILER={matrix['compiler']}",
|
||||||
|
f"-DCMAKE_CXX_STANDARD={matrix['std']}"
|
||||||
|
]
|
||||||
|
if matrix["config"] != "":
|
||||||
|
args.append(f"{matrix['config']}")
|
||||||
|
if matrix["compiler"] == "g++":
|
||||||
|
args.append("-GUnix Makefiles")
|
||||||
|
succeeded = run_command(*args)
|
||||||
|
if succeeded:
|
||||||
|
if matrix["compiler"] == "g++":
|
||||||
|
run_command("make", "-j")
|
||||||
|
else:
|
||||||
|
run_command("msbuild", "cpptrace.sln")
|
||||||
|
|
||||||
|
os.chdir("..")
|
||||||
|
print()
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
prog="Build in all configs",
|
||||||
|
description="Try building the library in all possible configurations for the current host"
|
||||||
|
)
|
||||||
|
|
||||||
|
if platform.system() == "Linux":
|
||||||
|
matrix = {
|
||||||
|
"compiler": ["g++-10", "clang++-14"],
|
||||||
|
"target": ["Debug"],
|
||||||
|
"std": ["11", "20"],
|
||||||
|
"unwind": [
|
||||||
|
"CPPTRACE_UNWIND_WITH_UNWIND",
|
||||||
|
"CPPTRACE_UNWIND_WITH_EXECINFO",
|
||||||
|
"CPPTRACE_UNWIND_WITH_NOTHING",
|
||||||
|
],
|
||||||
|
"symbols": [
|
||||||
|
"CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE",
|
||||||
|
"CPPTRACE_GET_SYMBOLS_WITH_LIBDL",
|
||||||
|
"CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE",
|
||||||
|
"CPPTRACE_GET_SYMBOLS_WITH_NOTHING",
|
||||||
|
],
|
||||||
|
"demangle": [
|
||||||
|
"CPPTRACE_DEMANGLE_WITH_CXXABI",
|
||||||
|
"CPPTRACE_DEMANGLE_WITH_NOTHING",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
exclude = []
|
||||||
|
run_matrix(matrix, exclude, build)
|
||||||
|
matrix = {
|
||||||
|
"compiler": ["g++-10", "clang++-14"],
|
||||||
|
"target": ["Debug"],
|
||||||
|
"std": ["11", "20"],
|
||||||
|
"config": ["-DCPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE=On", ""]
|
||||||
|
}
|
||||||
|
exclude = []
|
||||||
|
run_matrix(matrix, exclude, build_full_or_auto)
|
||||||
|
if platform.system() == "Darwin":
|
||||||
|
matrix = {
|
||||||
|
"compiler": ["g++-13", "clang++"],
|
||||||
|
"target": ["Debug"],
|
||||||
|
"std": ["11", "20"],
|
||||||
|
"unwind": [
|
||||||
|
"CPPTRACE_UNWIND_WITH_UNWIND",
|
||||||
|
"CPPTRACE_UNWIND_WITH_EXECINFO",
|
||||||
|
"CPPTRACE_UNWIND_WITH_NOTHING",
|
||||||
|
],
|
||||||
|
"symbols": [
|
||||||
|
#"CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE",
|
||||||
|
"CPPTRACE_GET_SYMBOLS_WITH_LIBDL",
|
||||||
|
"CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE",
|
||||||
|
"CPPTRACE_GET_SYMBOLS_WITH_NOTHING",
|
||||||
|
],
|
||||||
|
"demangle": [
|
||||||
|
"CPPTRACE_DEMANGLE_WITH_CXXABI",
|
||||||
|
"CPPTRACE_DEMANGLE_WITH_NOTHING",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
exclude = []
|
||||||
|
run_matrix(matrix, exclude, build)
|
||||||
|
matrix = {
|
||||||
|
"compiler": ["g++-13", "clang++"],
|
||||||
|
"target": ["Debug"],
|
||||||
|
"std": ["11", "20"],
|
||||||
|
"config": [""]
|
||||||
|
}
|
||||||
|
exclude = []
|
||||||
|
run_matrix(matrix, exclude, build_full_or_auto)
|
||||||
|
if platform.system() == "Windows":
|
||||||
|
parser.add_argument(
|
||||||
|
"--clang-only",
|
||||||
|
action="store_true"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--msvc-only",
|
||||||
|
action="store_true"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--mingw-only",
|
||||||
|
action="store_true"
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
compilers = ["cl", "clang++", "g++"]
|
||||||
|
if args.clang_only:
|
||||||
|
compilers = ["clang++"]
|
||||||
|
if args.msvc_only:
|
||||||
|
compilers = ["cl"]
|
||||||
|
if args.mingw_only:
|
||||||
|
compilers = ["g++"]
|
||||||
|
|
||||||
|
matrix = {
|
||||||
|
"compiler": compilers,
|
||||||
|
"target": ["Debug"],
|
||||||
|
"std": ["11", "20"],
|
||||||
|
"unwind": [
|
||||||
|
"CPPTRACE_UNWIND_WITH_WINAPI",
|
||||||
|
"CPPTRACE_UNWIND_WITH_UNWIND",
|
||||||
|
"CPPTRACE_UNWIND_WITH_NOTHING",
|
||||||
|
],
|
||||||
|
"symbols": [
|
||||||
|
"CPPTRACE_GET_SYMBOLS_WITH_DBGHELP",
|
||||||
|
"CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE",
|
||||||
|
"CPPTRACE_GET_SYMBOLS_WITH_NOTHING",
|
||||||
|
],
|
||||||
|
"demangle": [
|
||||||
|
#"CPPTRACE_DEMANGLE_WITH_CXXABI",
|
||||||
|
"CPPTRACE_DEMANGLE_WITH_NOTHING",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
exclude = [
|
||||||
|
{
|
||||||
|
"demangle": "CPPTRACE_DEMANGLE_WITH_CXXABI",
|
||||||
|
"compiler": "cl"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"unwind": "CPPTRACE_UNWIND_WITH_UNWIND",
|
||||||
|
"compiler": "cl"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"unwind": "CPPTRACE_UNWIND_WITH_UNWIND",
|
||||||
|
"compiler": "clang++"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"symbols": "CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE",
|
||||||
|
"compiler": "cl"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"symbols": "CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE",
|
||||||
|
"compiler": "clang++"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"symbols": "CPPTRACE_GET_SYMBOLS_WITH_DBGHELP",
|
||||||
|
"compiler": "g++"
|
||||||
|
},
|
||||||
|
]
|
||||||
|
run_matrix(matrix, exclude, build)
|
||||||
|
matrix = {
|
||||||
|
"compiler": compilers,
|
||||||
|
"target": ["Debug"],
|
||||||
|
"std": ["11", "20"],
|
||||||
|
"config": [""]
|
||||||
|
}
|
||||||
|
exclude = [
|
||||||
|
{
|
||||||
|
"config": "-DCPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE=On"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
run_matrix(matrix, exclude, build_full_or_auto)
|
||||||
|
|
||||||
|
global failed
|
||||||
|
if failed:
|
||||||
|
print("🔴 Some checks failed")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
main()
|
||||||
@ -1,195 +0,0 @@
|
|||||||
import argparse
|
|
||||||
import os
|
|
||||||
import platform
|
|
||||||
import shutil
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
from colorama import Fore, Back, Style
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from util import *
|
|
||||||
|
|
||||||
sys.stdout.reconfigure(encoding='utf-8') # for windows gh runner
|
|
||||||
|
|
||||||
def build(runner: MatrixRunner):
|
|
||||||
matrix = runner.current_config()
|
|
||||||
|
|
||||||
if os.path.exists("build"):
|
|
||||||
shutil.rmtree("build", ignore_errors=True)
|
|
||||||
|
|
||||||
os.makedirs("build", exist_ok=True)
|
|
||||||
os.chdir("build")
|
|
||||||
|
|
||||||
if platform.system() != "Windows":
|
|
||||||
succeeded = runner.run_command(
|
|
||||||
"cmake",
|
|
||||||
"..",
|
|
||||||
"-GNinja",
|
|
||||||
f"-DCMAKE_BUILD_TYPE={matrix['target']}",
|
|
||||||
f"-DCMAKE_CXX_COMPILER={matrix['compiler']}",
|
|
||||||
f"-DCMAKE_CXX_STANDARD={matrix['std']}",
|
|
||||||
f"-DCPPTRACE_USE_EXTERNAL_LIBDWARF=On",
|
|
||||||
f"-DCPPTRACE_USE_EXTERNAL_ZSTD=On",
|
|
||||||
f"-DCPPTRACE_WERROR_BUILD=On",
|
|
||||||
f"-D{matrix['unwind']}=On",
|
|
||||||
f"-D{matrix['symbols']}=On",
|
|
||||||
f"-D{matrix['demangle']}=On",
|
|
||||||
"-DCPPTRACE_BACKTRACE_PATH=/usr/lib/gcc/x86_64-linux-gnu/10/include/backtrace.h",
|
|
||||||
)
|
|
||||||
if succeeded:
|
|
||||||
succeeded = runner.run_command("ninja")
|
|
||||||
else:
|
|
||||||
args = [
|
|
||||||
"cmake",
|
|
||||||
"..",
|
|
||||||
f"-DCMAKE_BUILD_TYPE={matrix['target']}",
|
|
||||||
f"-DCMAKE_CXX_COMPILER={matrix['compiler']}",
|
|
||||||
f"-DCMAKE_CXX_STANDARD={matrix['std']}",
|
|
||||||
f"-DCPPTRACE_USE_EXTERNAL_LIBDWARF=On",
|
|
||||||
f"-DCPPTRACE_USE_EXTERNAL_ZSTD=On",
|
|
||||||
f"-DCPPTRACE_WERROR_BUILD=On",
|
|
||||||
f"-D{matrix['unwind']}=On",
|
|
||||||
f"-D{matrix['symbols']}=On",
|
|
||||||
f"-D{matrix['demangle']}=On",
|
|
||||||
]
|
|
||||||
if matrix["compiler"] == "g++":
|
|
||||||
args.append("-GUnix Makefiles")
|
|
||||||
succeeded = runner.run_command(*args)
|
|
||||||
if succeeded:
|
|
||||||
if matrix["compiler"] == "g++":
|
|
||||||
succeeded = runner.run_command("make", "-j", "VERBOSE=1")
|
|
||||||
else:
|
|
||||||
succeeded = runner.run_command("msbuild", "cpptrace.sln")
|
|
||||||
|
|
||||||
os.chdir("..")
|
|
||||||
print()
|
|
||||||
|
|
||||||
return succeeded
|
|
||||||
|
|
||||||
def run_linux_matrix(compilers: list):
|
|
||||||
MatrixRunner(
|
|
||||||
matrix = {
|
|
||||||
"compiler": compilers,
|
|
||||||
"target": ["Debug"],
|
|
||||||
"std": ["11", "20"],
|
|
||||||
"unwind": [
|
|
||||||
# "CPPTRACE_UNWIND_WITH_UNWIND",
|
|
||||||
# "CPPTRACE_UNWIND_WITH_EXECINFO",
|
|
||||||
# "CPPTRACE_UNWIND_WITH_LIBUNWIND",
|
|
||||||
"CPPTRACE_UNWIND_WITH_NOTHING",
|
|
||||||
],
|
|
||||||
"symbols": [
|
|
||||||
"CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE",
|
|
||||||
# "CPPTRACE_GET_SYMBOLS_WITH_LIBDL",
|
|
||||||
# "CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF",
|
|
||||||
# "CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE",
|
|
||||||
"CPPTRACE_GET_SYMBOLS_WITH_NOTHING",
|
|
||||||
],
|
|
||||||
"demangle": [
|
|
||||||
# "CPPTRACE_DEMANGLE_WITH_CXXABI",
|
|
||||||
"CPPTRACE_DEMANGLE_WITH_NOTHING",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
exclude = []
|
|
||||||
).run(build)
|
|
||||||
|
|
||||||
def run_macos_matrix(compilers: list):
|
|
||||||
MatrixRunner(
|
|
||||||
matrix = {
|
|
||||||
"compiler": compilers,
|
|
||||||
"target": ["Debug"],
|
|
||||||
"std": ["11", "20"],
|
|
||||||
"unwind": [
|
|
||||||
# "CPPTRACE_UNWIND_WITH_UNWIND",
|
|
||||||
# "CPPTRACE_UNWIND_WITH_EXECINFO",
|
|
||||||
"CPPTRACE_UNWIND_WITH_NOTHING",
|
|
||||||
],
|
|
||||||
"symbols": [
|
|
||||||
# "CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE",
|
|
||||||
# "CPPTRACE_GET_SYMBOLS_WITH_LIBDL",
|
|
||||||
# "CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF",
|
|
||||||
# "CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE",
|
|
||||||
"CPPTRACE_GET_SYMBOLS_WITH_NOTHING",
|
|
||||||
],
|
|
||||||
"demangle": [
|
|
||||||
# "CPPTRACE_DEMANGLE_WITH_CXXABI",
|
|
||||||
"CPPTRACE_DEMANGLE_WITH_NOTHING",
|
|
||||||
]
|
|
||||||
},
|
|
||||||
exclude = []
|
|
||||||
).run(build)
|
|
||||||
|
|
||||||
def run_windows_matrix(compilers: list):
|
|
||||||
MatrixRunner(
|
|
||||||
matrix = {
|
|
||||||
"compiler": compilers,
|
|
||||||
"target": ["Debug"],
|
|
||||||
"std": ["11", "20"],
|
|
||||||
"unwind": [
|
|
||||||
# "CPPTRACE_UNWIND_WITH_WINAPI",
|
|
||||||
# "CPPTRACE_UNWIND_WITH_DBGHELP",
|
|
||||||
# "CPPTRACE_UNWIND_WITH_UNWIND",
|
|
||||||
"CPPTRACE_UNWIND_WITH_NOTHING",
|
|
||||||
],
|
|
||||||
"symbols": [
|
|
||||||
# "CPPTRACE_GET_SYMBOLS_WITH_DBGHELP",
|
|
||||||
# "CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF",
|
|
||||||
# "CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE",
|
|
||||||
"CPPTRACE_GET_SYMBOLS_WITH_NOTHING",
|
|
||||||
],
|
|
||||||
"demangle": [
|
|
||||||
# "CPPTRACE_DEMANGLE_WITH_CXXABI",
|
|
||||||
"CPPTRACE_DEMANGLE_WITH_NOTHING",
|
|
||||||
]
|
|
||||||
},
|
|
||||||
exclude = []
|
|
||||||
).run(build)
|
|
||||||
|
|
||||||
def main():
|
|
||||||
parser = argparse.ArgumentParser(
|
|
||||||
prog="Build in all configs",
|
|
||||||
description="Try building the library in all possible configurations for the current host"
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--clang",
|
|
||||||
action="store_true"
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--gcc",
|
|
||||||
action="store_true"
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--msvc",
|
|
||||||
action="store_true"
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--all",
|
|
||||||
action="store_true"
|
|
||||||
)
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
if platform.system() == "Linux":
|
|
||||||
compilers = []
|
|
||||||
if args.clang or args.all:
|
|
||||||
compilers.append("clang++-14")
|
|
||||||
if args.gcc or args.all:
|
|
||||||
compilers.append("g++-10")
|
|
||||||
run_linux_matrix(compilers)
|
|
||||||
if platform.system() == "Darwin":
|
|
||||||
compilers = []
|
|
||||||
if args.clang or args.all:
|
|
||||||
compilers.append("clang++")
|
|
||||||
if args.gcc or args.all:
|
|
||||||
compilers.append("g++-12")
|
|
||||||
run_macos_matrix(compilers)
|
|
||||||
if platform.system() == "Windows":
|
|
||||||
compilers = []
|
|
||||||
if args.clang or args.all:
|
|
||||||
compilers.append("clang++")
|
|
||||||
if args.msvc or args.all:
|
|
||||||
compilers.append("cl")
|
|
||||||
if args.gcc or args.all:
|
|
||||||
compilers.append("g++")
|
|
||||||
run_windows_matrix(compilers)
|
|
||||||
|
|
||||||
main()
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
mkdir zstd
|
|
||||||
cd zstd
|
|
||||||
git init
|
|
||||||
git remote add origin https://github.com/facebook/zstd.git
|
|
||||||
git fetch --depth 1 origin 63779c798237346c2b245c546c40b72a5a5913fe # 1.5.5
|
|
||||||
git checkout FETCH_HEAD
|
|
||||||
cd build/cmake
|
|
||||||
mkdir build
|
|
||||||
cd build
|
|
||||||
cmake .. -DZSTD_BUILD_SHARED=On -DZSTD_BUILD_SHARED=Off -DZSTD_LEGACY_SUPPORT=Off -DZSTD_BUILD_PROGRAMS=Off -DZSTD_BUILD_CONTRIB=Off -DZSTD_BUILD_TESTS=Off -G"Unix Makefiles"
|
|
||||||
make -j
|
|
||||||
make install
|
|
||||||
|
|
||||||
cd ../../../..
|
|
||||||
|
|
||||||
mkdir libdwarf
|
|
||||||
cd libdwarf
|
|
||||||
git init
|
|
||||||
git remote add origin https://github.com/jeremy-rifkin/libdwarf-lite.git
|
|
||||||
git fetch --depth 1 origin fe09ca800b988e2ff21225ac5e7468ceade2a30e
|
|
||||||
git checkout FETCH_HEAD
|
|
||||||
mkdir build
|
|
||||||
cd build
|
|
||||||
cmake .. -DPIC_ALWAYS=TRUE -DBUILD_DWARFDUMP=FALSE -G"Unix Makefiles"
|
|
||||||
make -j
|
|
||||||
make install
|
|
||||||
@ -1,44 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
mkdir zstd
|
|
||||||
cd zstd
|
|
||||||
git init
|
|
||||||
git remote add origin https://github.com/facebook/zstd.git
|
|
||||||
git fetch --depth 1 origin 794ea1b0afca0f020f4e57b6732332231fb23c70 # 1.5.6
|
|
||||||
git checkout FETCH_HEAD
|
|
||||||
make -j
|
|
||||||
sudo make install
|
|
||||||
|
|
||||||
cd ..
|
|
||||||
|
|
||||||
mkdir libdwarf
|
|
||||||
cd libdwarf
|
|
||||||
git init
|
|
||||||
git remote add origin https://github.com/jeremy-rifkin/libdwarf-lite.git
|
|
||||||
git fetch --depth 1 origin fe09ca800b988e2ff21225ac5e7468ceade2a30e # 0.11.1
|
|
||||||
git checkout FETCH_HEAD
|
|
||||||
mkdir build
|
|
||||||
cd build
|
|
||||||
cmake .. -GNinja -DPIC_ALWAYS=TRUE -DBUILD_DWARFDUMP=FALSE
|
|
||||||
sudo ninja install
|
|
||||||
|
|
||||||
cd ../..
|
|
||||||
|
|
||||||
mkdir googletest
|
|
||||||
cd googletest
|
|
||||||
git init
|
|
||||||
git remote add origin https://github.com/google/googletest.git
|
|
||||||
git fetch --depth 1 origin f8d7d77c06936315286eb55f8de22cd23c188571
|
|
||||||
git checkout FETCH_HEAD
|
|
||||||
mkdir build
|
|
||||||
cd build
|
|
||||||
cmake .. -GNinja -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_INSTALL_PREFIX=/tmp/gtest_install
|
|
||||||
sudo ninja install
|
|
||||||
rm -rf *
|
|
||||||
# There's a false-positive container-overflow for apple clang/relwithdebinfo/sanitizers=on if gtest isn't built with
|
|
||||||
# sanitizers. https://github.com/google/sanitizers/wiki/AddressSanitizerContainerOverflow#false-positives
|
|
||||||
cmake .. -GNinja -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_INSTALL_PREFIX=/tmp/gtest_asan_install -DCMAKE_CXX_FLAGS=-fsanitize=address
|
|
||||||
sudo ninja install
|
|
||||||
rm -rf *
|
|
||||||
cmake .. -GNinja -DCMAKE_CXX_COMPILER=g++-12 -DCMAKE_INSTALL_PREFIX=/tmp/gtest_install_gcc
|
|
||||||
sudo ninja install
|
|
||||||
@ -1,39 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
mkdir zstd
|
|
||||||
cd zstd
|
|
||||||
git init
|
|
||||||
git remote add origin https://github.com/facebook/zstd.git
|
|
||||||
git fetch --depth 1 origin 794ea1b0afca0f020f4e57b6732332231fb23c70 # 1.5.6
|
|
||||||
git checkout FETCH_HEAD
|
|
||||||
make -j
|
|
||||||
sudo make install
|
|
||||||
|
|
||||||
cd ..
|
|
||||||
|
|
||||||
mkdir libdwarf
|
|
||||||
cd libdwarf
|
|
||||||
git init
|
|
||||||
git remote add origin https://github.com/jeremy-rifkin/libdwarf-lite.git
|
|
||||||
git fetch --depth 1 origin fe09ca800b988e2ff21225ac5e7468ceade2a30e # 0.11.1
|
|
||||||
git checkout FETCH_HEAD
|
|
||||||
mkdir build
|
|
||||||
cd build
|
|
||||||
cmake .. -GNinja -DPIC_ALWAYS=TRUE -DBUILD_DWARFDUMP=FALSE
|
|
||||||
sudo ninja install
|
|
||||||
|
|
||||||
cd ../..
|
|
||||||
|
|
||||||
mkdir googletest
|
|
||||||
cd googletest
|
|
||||||
git init
|
|
||||||
git remote add origin https://github.com/google/googletest.git
|
|
||||||
git fetch --depth 1 origin f8d7d77c06936315286eb55f8de22cd23c188571
|
|
||||||
git checkout FETCH_HEAD
|
|
||||||
mkdir build
|
|
||||||
cd build
|
|
||||||
cmake .. -GNinja -DCMAKE_INSTALL_PREFIX=/tmp/gtest_install
|
|
||||||
sudo ninja install
|
|
||||||
rm -rf *
|
|
||||||
cmake .. -GNinja -DCMAKE_CXX_COMPILER=clang++-18 -DCMAKE_CXX_FLAGS=-stdlib=libc++ -DCMAKE_INSTALL_PREFIX=/tmp/gtest_install_libcxx
|
|
||||||
sudo ninja install
|
|
||||||
@ -1,25 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
sudo apt install libgtest-dev
|
|
||||||
|
|
||||||
mkdir zstd
|
|
||||||
cd zstd
|
|
||||||
git init
|
|
||||||
git remote add origin https://github.com/facebook/zstd.git
|
|
||||||
git fetch --depth 1 origin 794ea1b0afca0f020f4e57b6732332231fb23c70 # 1.5.6
|
|
||||||
git checkout FETCH_HEAD
|
|
||||||
make -j
|
|
||||||
sudo make install
|
|
||||||
|
|
||||||
cd ..
|
|
||||||
|
|
||||||
mkdir libdwarf
|
|
||||||
cd libdwarf
|
|
||||||
git init
|
|
||||||
git remote add origin https://github.com/jeremy-rifkin/libdwarf-lite.git
|
|
||||||
git fetch --depth 1 origin fe09ca800b988e2ff21225ac5e7468ceade2a30e
|
|
||||||
git checkout FETCH_HEAD
|
|
||||||
mkdir build
|
|
||||||
cd build
|
|
||||||
cmake .. -DPIC_ALWAYS=TRUE -DBUILD_DWARFDUMP=FALSE
|
|
||||||
make -j
|
|
||||||
sudo make install
|
|
||||||
@ -1,6 +1,5 @@
|
|||||||
import sys
|
import sys
|
||||||
import re
|
import re
|
||||||
import platform
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
output = sys.stdin.read()
|
output = sys.stdin.read()
|
||||||
@ -16,14 +15,9 @@ def main():
|
|||||||
clang = any(["clang" in arg for arg in sys.argv[1:]])
|
clang = any(["clang" in arg for arg in sys.argv[1:]])
|
||||||
# Somehow -gdwarf-4 clang is fast after a completely unrelated PR? Weird. Something to do with static linking...?
|
# Somehow -gdwarf-4 clang is fast after a completely unrelated PR? Weird. Something to do with static linking...?
|
||||||
# https://github.com/jeremy-rifkin/cpptrace/pull/22
|
# https://github.com/jeremy-rifkin/cpptrace/pull/22
|
||||||
expect_slow = False
|
expect_slow = dwarf4 and not clang
|
||||||
|
|
||||||
if platform.system() == "Windows":
|
threshold = 100 # ms
|
||||||
# For some reason SymInitialize is super slow on github's windows runner and it alone takes 250ms. Nothing we
|
|
||||||
# can do about that.
|
|
||||||
threshold = 350 # ms
|
|
||||||
else:
|
|
||||||
threshold = 100 # ms
|
|
||||||
|
|
||||||
if expect_slow:
|
if expect_slow:
|
||||||
if time > threshold:
|
if time > threshold:
|
||||||
|
|||||||
@ -11,10 +11,9 @@ from util import *
|
|||||||
|
|
||||||
sys.stdout.reconfigure(encoding='utf-8') # for windows gh runner
|
sys.stdout.reconfigure(encoding='utf-8') # for windows gh runner
|
||||||
|
|
||||||
expected_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "../test/expected/")
|
failed = False
|
||||||
|
|
||||||
def get_c_compiler_counterpart(compiler: str) -> str:
|
expected_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "../test/expected/")
|
||||||
return compiler.replace("clang++", "clang").replace("g++", "gcc")
|
|
||||||
|
|
||||||
MAX_LINE_DIFF = 2
|
MAX_LINE_DIFF = 2
|
||||||
|
|
||||||
@ -28,7 +27,7 @@ def similarity(name: str, target: List[str]) -> int:
|
|||||||
return -1
|
return -1
|
||||||
return c
|
return c
|
||||||
|
|
||||||
def output_matches(raw_output: str, params: Tuple[str]):
|
def output_matches(output: str, params: Tuple[str]):
|
||||||
target = []
|
target = []
|
||||||
|
|
||||||
if params[0].startswith("gcc") or params[0].startswith("g++"):
|
if params[0].startswith("gcc") or params[0].startswith("g++"):
|
||||||
@ -70,280 +69,264 @@ def output_matches(raw_output: str, params: Tuple[str]):
|
|||||||
print(f"Reading from {file}")
|
print(f"Reading from {file}")
|
||||||
|
|
||||||
with open(os.path.join(expected_dir, file), "r") as f:
|
with open(os.path.join(expected_dir, file), "r") as f:
|
||||||
raw_expected = f.read()
|
expected = f.read()
|
||||||
|
|
||||||
if raw_output.strip() == "":
|
if output.strip() == "":
|
||||||
print(f"Error: No output from test")
|
print(f"Error: No output from test")
|
||||||
return False
|
sys.exit(1)
|
||||||
|
|
||||||
expected = [line.strip().split("||") for line in raw_expected.split("\n")]
|
expected = [line.strip().split("||") for line in expected.split("\n")]
|
||||||
output = [line.strip().split("||") for line in raw_output.split("\n")]
|
output = [line.strip().split("||") for line in output.split("\n")]
|
||||||
|
|
||||||
max_line_diff = 0
|
max_line_diff = MAX_LINE_DIFF
|
||||||
|
if "CPPTRACE_UNWIND_WITH_UNWIND" in params:
|
||||||
|
max_line_diff = 0
|
||||||
|
if "CPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE" in params:
|
||||||
|
max_line_diff = 0
|
||||||
|
if "CPPTRACE_FULL_TRACE_WITH_STACKTRACE" in params:
|
||||||
|
max_line_diff = 0
|
||||||
|
|
||||||
errored = False
|
errored = False
|
||||||
|
|
||||||
try:
|
for i, ((output_file, output_line, output_symbol), (expected_file, expected_line, expected_symbol)) in enumerate(zip(output, expected)):
|
||||||
for i, ((output_file, output_line, output_symbol), (expected_file, expected_line, expected_symbol)) in enumerate(zip(output, expected)):
|
if output_file != expected_file:
|
||||||
if output_file != expected_file:
|
print(f"Error: File name mismatch on line {i + 1}, found \"{output_file}\" expected \"{expected_file}\"")
|
||||||
print(f"Error: File name mismatch on line {i + 1}, found \"{output_file}\" expected \"{expected_file}\"")
|
errored = True
|
||||||
errored = True
|
if abs(int(output_line) - int(expected_line)) > max_line_diff:
|
||||||
if abs(int(output_line) - int(expected_line)) > max_line_diff:
|
print(f"Error: File line mismatch on line {i + 1}, found {output_line} expected {expected_line}")
|
||||||
print(f"Error: File line mismatch on line {i + 1}, found {output_line} expected {expected_line}")
|
errored = True
|
||||||
errored = True
|
if output_symbol != expected_symbol:
|
||||||
if output_symbol != expected_symbol:
|
print(f"Error: File symbol mismatch on line {i + 1}, found \"{output_symbol}\" expected \"{expected_symbol}\"")
|
||||||
print(f"Error: File symbol mismatch on line {i + 1}, found \"{output_symbol}\" expected \"{expected_symbol}\"")
|
errored = True
|
||||||
errored = True
|
if expected_symbol == "main" or expected_symbol == "main()":
|
||||||
if expected_symbol == "main" or expected_symbol == "main()":
|
break
|
||||||
break
|
|
||||||
except ValueError:
|
|
||||||
print("ValueError during output checking")
|
|
||||||
errored = True
|
|
||||||
|
|
||||||
if errored:
|
|
||||||
print("Output:")
|
|
||||||
print(raw_output)
|
|
||||||
print("Expected:")
|
|
||||||
print(raw_expected)
|
|
||||||
|
|
||||||
return not errored
|
return not errored
|
||||||
|
|
||||||
def run_test(runner: MatrixRunner, test_binary, params: Tuple[str]):
|
def run_command(*args: List[str]):
|
||||||
def output_matcher(output: str):
|
global failed
|
||||||
return output_matches(output, params)
|
print(f"{Fore.CYAN}{Style.BRIGHT}Running Command \"{' '.join(args)}\"{Style.RESET_ALL}")
|
||||||
return runner.run_command(test_binary, output_matcher=output_matcher)
|
p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
|
stdout, stderr = p.communicate()
|
||||||
|
print(Style.RESET_ALL, end="") # makefile in parallel sometimes messes up colors
|
||||||
|
if p.returncode != 0:
|
||||||
|
print(f"{Fore.RED}{Style.BRIGHT}Command failed{Style.RESET_ALL}")
|
||||||
|
print("stdout:")
|
||||||
|
print(stdout.decode("utf-8"), end="")
|
||||||
|
print("stderr:")
|
||||||
|
print(stderr.decode("utf-8"), end="")
|
||||||
|
failed = True
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
print(f"{Fore.GREEN}{Style.BRIGHT}Command succeeded{Style.RESET_ALL}")
|
||||||
|
return True
|
||||||
|
|
||||||
def build(runner: MatrixRunner):
|
def run_test(test_binary, params: Tuple[str]):
|
||||||
matrix = runner.current_config()
|
global failed
|
||||||
|
print(f"{Fore.CYAN}{Style.BRIGHT}Running test{Style.RESET_ALL}")
|
||||||
|
test = subprocess.Popen([test_binary], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
|
test_stdout, test_stderr = test.communicate()
|
||||||
|
print(Style.RESET_ALL, end="") # makefile in parallel sometimes messes up colors
|
||||||
|
|
||||||
|
if test.returncode != 0:
|
||||||
|
print("[🔴 Test command failed]")
|
||||||
|
print("stderr:")
|
||||||
|
print(test_stderr.decode("utf-8"), end="")
|
||||||
|
print("stdout:")
|
||||||
|
print(test_stdout.decode("utf-8"), end="")
|
||||||
|
failed = True
|
||||||
|
else:
|
||||||
|
if len(test_stderr) != 0:
|
||||||
|
print("stderr:")
|
||||||
|
print(test_stderr.decode("utf-8"), end="")
|
||||||
|
if output_matches(test_stdout.decode("utf-8"), params):
|
||||||
|
print(f"{Fore.GREEN}{Style.BRIGHT}Test succeeded{Style.RESET_ALL}")
|
||||||
|
else:
|
||||||
|
print(f"{Fore.RED}{Style.BRIGHT}Test failed{Style.RESET_ALL}")
|
||||||
|
failed = True
|
||||||
|
|
||||||
|
def build(matrix):
|
||||||
if platform.system() != "Windows":
|
if platform.system() != "Windows":
|
||||||
args = [
|
args = [
|
||||||
"cmake",
|
"cmake",
|
||||||
"..",
|
"..",
|
||||||
f"-DCMAKE_BUILD_TYPE={matrix['target']}",
|
f"-DCMAKE_BUILD_TYPE={matrix['target']}",
|
||||||
f"-DCMAKE_CXX_COMPILER={matrix['compiler']}",
|
f"-DCMAKE_CXX_COMPILER={matrix['compiler']}",
|
||||||
f"-DCMAKE_C_COMPILER={get_c_compiler_counterpart(matrix['compiler'])}",
|
|
||||||
f"-DCMAKE_CXX_STANDARD={matrix['std']}",
|
f"-DCMAKE_CXX_STANDARD={matrix['std']}",
|
||||||
f"-DCPPTRACE_USE_EXTERNAL_LIBDWARF=On",
|
|
||||||
f"-DCPPTRACE_USE_EXTERNAL_ZSTD=On",
|
|
||||||
f"-DCPPTRACE_WERROR_BUILD=On",
|
|
||||||
f"-D{matrix['unwind']}=On",
|
f"-D{matrix['unwind']}=On",
|
||||||
f"-D{matrix['symbols']}=On",
|
f"-D{matrix['symbols']}=On",
|
||||||
f"-D{matrix['demangle']}=On",
|
f"-D{matrix['demangle']}=On",
|
||||||
"-DCPPTRACE_BACKTRACE_PATH=/usr/lib/gcc/x86_64-linux-gnu/10/include/backtrace.h",
|
"-DCPPTRACE_BACKTRACE_PATH=/usr/lib/gcc/x86_64-linux-gnu/10/include/backtrace.h",
|
||||||
"-DCPPTRACE_BUILD_TESTING=On",
|
"-DCPPTRACE_BUILD_TEST=On"
|
||||||
"-DCPPTRACE_SKIP_UNIT=On",
|
|
||||||
f"-DBUILD_SHARED_LIBS={matrix['shared']}"
|
|
||||||
]
|
]
|
||||||
if matrix['symbols'] == "CPPTRACE_GET_SYMBOLS_WITH_LIBDL":
|
if matrix['symbols'] == "CPPTRACE_GET_SYMBOLS_WITH_LIBDL":
|
||||||
args.append("-DCPPTRACE_BUILD_TEST_RDYNAMIC=On")
|
args.append("-DCPPTRACE_BUILD_TEST_RDYNAMIC=On")
|
||||||
succeeded = runner.run_command(*args)
|
succeeded = run_command(*args)
|
||||||
if succeeded:
|
if succeeded:
|
||||||
return runner.run_command("make", "-j")
|
return run_command("make", "-j")
|
||||||
else:
|
else:
|
||||||
args = [
|
args = [
|
||||||
"cmake",
|
"cmake",
|
||||||
"..",
|
"..",
|
||||||
f"-DCMAKE_BUILD_TYPE={matrix['target']}",
|
f"-DCMAKE_BUILD_TYPE={matrix['target']}",
|
||||||
f"-DCMAKE_CXX_COMPILER={matrix['compiler']}",
|
f"-DCMAKE_CXX_COMPILER={matrix['compiler']}",
|
||||||
f"-DCMAKE_C_COMPILER={get_c_compiler_counterpart(matrix['compiler'])}",
|
|
||||||
f"-DCMAKE_CXX_STANDARD={matrix['std']}",
|
f"-DCMAKE_CXX_STANDARD={matrix['std']}",
|
||||||
f"-DCPPTRACE_USE_EXTERNAL_LIBDWARF=On",
|
|
||||||
f"-DCPPTRACE_USE_EXTERNAL_ZSTD=On",
|
|
||||||
f"-DCPPTRACE_WERROR_BUILD=On",
|
|
||||||
f"-D{matrix['unwind']}=On",
|
f"-D{matrix['unwind']}=On",
|
||||||
f"-D{matrix['symbols']}=On",
|
f"-D{matrix['symbols']}=On",
|
||||||
f"-D{matrix['demangle']}=On",
|
f"-D{matrix['demangle']}=On",
|
||||||
"-DCPPTRACE_BUILD_TESTING=On",
|
"-DCPPTRACE_BUILD_TEST=On"
|
||||||
"-DCPPTRACE_SKIP_UNIT=On",
|
|
||||||
f"-DBUILD_SHARED_LIBS={matrix['shared']}"
|
|
||||||
]
|
]
|
||||||
if matrix["compiler"] == "g++":
|
if matrix["compiler"] == "g++":
|
||||||
args.append("-GUnix Makefiles")
|
args.append("-GUnix Makefiles")
|
||||||
succeeded = runner.run_command(*args)
|
succeeded = run_command(*args)
|
||||||
if succeeded:
|
if succeeded:
|
||||||
if matrix["compiler"] == "g++":
|
if matrix["compiler"] == "g++":
|
||||||
return runner.run_command("make", "-j")
|
run_command("make", "-j")
|
||||||
else:
|
else:
|
||||||
return runner.run_command("msbuild", "cpptrace.sln")
|
run_command("msbuild", "cpptrace.sln")
|
||||||
return False
|
|
||||||
|
|
||||||
def build_full_or_auto(runner: MatrixRunner):
|
def build_full_or_auto(matrix):
|
||||||
matrix = runner.current_config()
|
|
||||||
if platform.system() != "Windows":
|
if platform.system() != "Windows":
|
||||||
args = [
|
args = [
|
||||||
"cmake",
|
"cmake",
|
||||||
"..",
|
"..",
|
||||||
f"-DCMAKE_BUILD_TYPE={matrix['target']}",
|
f"-DCMAKE_BUILD_TYPE={matrix['target']}",
|
||||||
f"-DCMAKE_CXX_COMPILER={matrix['compiler']}",
|
f"-DCMAKE_CXX_COMPILER={matrix['compiler']}",
|
||||||
f"-DCMAKE_C_COMPILER={get_c_compiler_counterpart(matrix['compiler'])}",
|
|
||||||
f"-DCMAKE_CXX_STANDARD={matrix['std']}",
|
f"-DCMAKE_CXX_STANDARD={matrix['std']}",
|
||||||
f"-DCPPTRACE_USE_EXTERNAL_LIBDWARF=On",
|
|
||||||
f"-DCPPTRACE_USE_EXTERNAL_ZSTD=On",
|
|
||||||
f"-DCPPTRACE_WERROR_BUILD=On",
|
|
||||||
f"-DCPPTRACE_BACKTRACE_PATH=/usr/lib/gcc/x86_64-linux-gnu/10/include/backtrace.h",
|
f"-DCPPTRACE_BACKTRACE_PATH=/usr/lib/gcc/x86_64-linux-gnu/10/include/backtrace.h",
|
||||||
"-DCPPTRACE_BUILD_TESTING=On",
|
"-DCPPTRACE_BUILD_TEST=On"
|
||||||
"-DCPPTRACE_SKIP_UNIT=On",
|
|
||||||
f"-DBUILD_SHARED_LIBS={matrix['shared']}"
|
|
||||||
]
|
]
|
||||||
if matrix["config"] != "":
|
if matrix["config"] != "":
|
||||||
args.append(f"{matrix['config']}")
|
args.append(f"{matrix['config']}")
|
||||||
succeeded = runner.run_command(*args)
|
succeeded = run_command(*args)
|
||||||
if succeeded:
|
if succeeded:
|
||||||
return runner.run_command("make", "-j")
|
return run_command("make", "-j")
|
||||||
else:
|
else:
|
||||||
args = [
|
args = [
|
||||||
"cmake",
|
"cmake",
|
||||||
"..",
|
"..",
|
||||||
f"-DCMAKE_BUILD_TYPE={matrix['target']}",
|
f"-DCMAKE_BUILD_TYPE={matrix['target']}",
|
||||||
f"-DCMAKE_CXX_COMPILER={matrix['compiler']}",
|
f"-DCMAKE_CXX_COMPILER={matrix['compiler']}",
|
||||||
f"-DCMAKE_C_COMPILER={get_c_compiler_counterpart(matrix['compiler'])}",
|
|
||||||
f"-DCMAKE_CXX_STANDARD={matrix['std']}",
|
f"-DCMAKE_CXX_STANDARD={matrix['std']}",
|
||||||
f"-DCPPTRACE_USE_EXTERNAL_LIBDWARF=On",
|
"-DCPPTRACE_BUILD_TEST=On"
|
||||||
f"-DCPPTRACE_USE_EXTERNAL_ZSTD=On",
|
|
||||||
f"-DCPPTRACE_WERROR_BUILD=On",
|
|
||||||
"-DCPPTRACE_BUILD_TESTING=On",
|
|
||||||
"-DCPPTRACE_SKIP_UNIT=On",
|
|
||||||
f"-DBUILD_SHARED_LIBS={matrix['shared']}"
|
|
||||||
]
|
]
|
||||||
if matrix["config"] != "":
|
if matrix["config"] != "":
|
||||||
args.append(f"{matrix['config']}")
|
args.append(f"{matrix['config']}")
|
||||||
if matrix["compiler"] == "g++":
|
if matrix["compiler"] == "g++":
|
||||||
args.append("-GUnix Makefiles")
|
args.append("-GUnix Makefiles")
|
||||||
succeeded = runner.run_command(*args)
|
succeeded = run_command(*args)
|
||||||
if succeeded:
|
if succeeded:
|
||||||
if matrix["compiler"] == "g++":
|
if matrix["compiler"] == "g++":
|
||||||
return runner.run_command("make", "-j")
|
run_command("make", "-j")
|
||||||
else:
|
else:
|
||||||
return runner.run_command("msbuild", "cpptrace.sln")
|
run_command("msbuild", "cpptrace.sln")
|
||||||
return False
|
|
||||||
|
|
||||||
def test(runner: MatrixRunner):
|
def test(matrix):
|
||||||
matrix = runner.current_config()
|
|
||||||
if platform.system() != "Windows":
|
if platform.system() != "Windows":
|
||||||
return run_test(
|
run_test(
|
||||||
runner,
|
"./test",
|
||||||
"./integration",
|
|
||||||
(matrix["compiler"], matrix["unwind"], matrix["symbols"], matrix["demangle"])
|
(matrix["compiler"], matrix["unwind"], matrix["symbols"], matrix["demangle"])
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
if matrix["compiler"] == "g++":
|
if matrix["compiler"] == "g++":
|
||||||
return run_test(
|
run_test(
|
||||||
runner,
|
f".\\test.exe",
|
||||||
f".\\integration.exe",
|
|
||||||
(matrix["compiler"], matrix["unwind"], matrix["symbols"], matrix["demangle"])
|
(matrix["compiler"], matrix["unwind"], matrix["symbols"], matrix["demangle"])
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
return run_test(
|
run_test(
|
||||||
runner,
|
f".\\{matrix['target']}\\test.exe",
|
||||||
f".\\{matrix['target']}\\integration.exe",
|
|
||||||
(matrix["compiler"], matrix["unwind"], matrix["symbols"], matrix["demangle"])
|
(matrix["compiler"], matrix["unwind"], matrix["symbols"], matrix["demangle"])
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_full_or_auto(runner: MatrixRunner):
|
def test_full_or_auto(matrix):
|
||||||
matrix = runner.current_config()
|
|
||||||
if platform.system() != "Windows":
|
if platform.system() != "Windows":
|
||||||
return run_test(
|
run_test(
|
||||||
runner,
|
"./test",
|
||||||
"./integration",
|
|
||||||
(matrix["compiler"],)
|
(matrix["compiler"],)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
if matrix["compiler"] == "g++":
|
if matrix["compiler"] == "g++":
|
||||||
return run_test(
|
run_test(
|
||||||
runner,
|
f".\\test.exe",
|
||||||
f".\\integration.exe",
|
|
||||||
(matrix["compiler"],)
|
(matrix["compiler"],)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
return run_test(
|
run_test(
|
||||||
runner,
|
f".\\{matrix['target']}\\test.exe",
|
||||||
f".\\{matrix['target']}\\integration.exe",
|
|
||||||
(matrix["compiler"],)
|
(matrix["compiler"],)
|
||||||
)
|
)
|
||||||
|
|
||||||
def build_and_test(runner: MatrixRunner):
|
def build_and_test(matrix):
|
||||||
matrix = runner.current_config()
|
print(f"{Fore.BLUE}{Style.BRIGHT}{'=' * 10} Running build and test with config {', '.join(matrix.values())} {'=' * 10}{Style.RESET_ALL}")
|
||||||
|
|
||||||
if os.path.exists("build"):
|
if os.path.exists("build"):
|
||||||
shutil.rmtree("build", ignore_errors=True)
|
shutil.rmtree("build")
|
||||||
|
|
||||||
if not os.path.exists("build"):
|
os.mkdir("build")
|
||||||
os.mkdir("build")
|
|
||||||
os.chdir("build")
|
os.chdir("build")
|
||||||
|
|
||||||
good = False
|
if build(matrix):
|
||||||
if build(runner):
|
test(matrix)
|
||||||
good = test(runner)
|
|
||||||
|
|
||||||
os.chdir("..")
|
os.chdir("..")
|
||||||
print()
|
print()
|
||||||
|
|
||||||
return good
|
def build_and_test_full_or_auto(matrix):
|
||||||
|
print(f"{Fore.BLUE}{Style.BRIGHT}{'=' * 10} Running build and test with config {'<auto>' if matrix['config'] == '' else ', '.join(matrix.values())} {'=' * 10}{Style.RESET_ALL}")
|
||||||
def build_and_test_full_or_auto(runner: MatrixRunner):
|
|
||||||
matrix = runner.current_config()
|
|
||||||
|
|
||||||
if os.path.exists("build"):
|
if os.path.exists("build"):
|
||||||
shutil.rmtree("build", ignore_errors=True)
|
shutil.rmtree("build")
|
||||||
|
|
||||||
if not os.path.exists("build"):
|
os.mkdir("build")
|
||||||
os.mkdir("build")
|
|
||||||
os.chdir("build")
|
os.chdir("build")
|
||||||
|
|
||||||
good = False
|
if build_full_or_auto(matrix):
|
||||||
if build_full_or_auto(runner):
|
test_full_or_auto(matrix)
|
||||||
good = test_full_or_auto(runner)
|
|
||||||
|
|
||||||
os.chdir("..")
|
os.chdir("..")
|
||||||
print()
|
print()
|
||||||
|
|
||||||
return good
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
prog="Build in all configs",
|
||||||
|
description="Try building the library in all possible configurations for the current host"
|
||||||
|
)
|
||||||
|
|
||||||
def run_linux_matrix(compilers: list, shared: bool):
|
if platform.system() == "Linux":
|
||||||
MatrixRunner(
|
|
||||||
matrix = {
|
matrix = {
|
||||||
"compiler": compilers,
|
"compiler": ["g++-10", "clang++-14"],
|
||||||
"target": ["Debug"],
|
"target": ["Debug"],
|
||||||
"std": ["11", "20"],
|
"std": ["11", "20"],
|
||||||
"unwind": [
|
"unwind": [
|
||||||
"CPPTRACE_UNWIND_WITH_EXECINFO",
|
"CPPTRACE_UNWIND_WITH_EXECINFO",
|
||||||
"CPPTRACE_UNWIND_WITH_UNWIND",
|
"CPPTRACE_UNWIND_WITH_UNWIND",
|
||||||
"CPPTRACE_UNWIND_WITH_LIBUNWIND",
|
|
||||||
#"CPPTRACE_UNWIND_WITH_NOTHING",
|
#"CPPTRACE_UNWIND_WITH_NOTHING",
|
||||||
],
|
],
|
||||||
"symbols": [
|
"symbols": [
|
||||||
# Disabled due to libbacktrace bug
|
"CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE",
|
||||||
# "CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE",
|
|
||||||
"CPPTRACE_GET_SYMBOLS_WITH_LIBDL",
|
"CPPTRACE_GET_SYMBOLS_WITH_LIBDL",
|
||||||
"CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE",
|
"CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE",
|
||||||
"CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF",
|
|
||||||
#"CPPTRACE_GET_SYMBOLS_WITH_NOTHING",
|
#"CPPTRACE_GET_SYMBOLS_WITH_NOTHING",
|
||||||
],
|
],
|
||||||
"demangle": [
|
"demangle": [
|
||||||
"CPPTRACE_DEMANGLE_WITH_CXXABI",
|
"CPPTRACE_DEMANGLE_WITH_CXXABI",
|
||||||
#"CPPTRACE_DEMANGLE_WITH_NOTHING",
|
#"CPPTRACE_DEMANGLE_WITH_NOTHING",
|
||||||
],
|
],
|
||||||
"shared": ["On" if shared else "Off"]
|
}
|
||||||
},
|
|
||||||
exclude = []
|
exclude = []
|
||||||
).run(build_and_test)
|
run_matrix(matrix, exclude, build_and_test)
|
||||||
|
|
||||||
def run_linux_default(compilers: list, shared: bool):
|
|
||||||
MatrixRunner(
|
|
||||||
matrix = {
|
matrix = {
|
||||||
"compiler": compilers,
|
"compiler": ["g++-10", "clang++-14"],
|
||||||
"target": ["Debug"],
|
"target": ["Debug"],
|
||||||
"std": ["11", "20"],
|
"std": ["11", "20"],
|
||||||
"config": [""],
|
"config": ["-DCPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE=On", ""]
|
||||||
"shared": ["On" if shared else "Off"]
|
}
|
||||||
},
|
|
||||||
exclude = []
|
exclude = []
|
||||||
).run(build_and_test_full_or_auto)
|
run_matrix(matrix, exclude, build_and_test_full_or_auto)
|
||||||
|
if platform.system() == "Darwin":
|
||||||
def run_macos_matrix(compilers: list, shared: bool):
|
|
||||||
MatrixRunner(
|
|
||||||
matrix = {
|
matrix = {
|
||||||
"compiler": compilers,
|
"compiler": ["g++-13", "clang++"],
|
||||||
"target": ["Debug"],
|
"target": ["Debug"],
|
||||||
"std": ["11", "20"],
|
"std": ["11", "20"],
|
||||||
"unwind": [
|
"unwind": [
|
||||||
@ -355,54 +338,65 @@ def run_macos_matrix(compilers: list, shared: bool):
|
|||||||
#"CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE",
|
#"CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE",
|
||||||
"CPPTRACE_GET_SYMBOLS_WITH_LIBDL",
|
"CPPTRACE_GET_SYMBOLS_WITH_LIBDL",
|
||||||
"CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE",
|
"CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE",
|
||||||
"CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF",
|
|
||||||
#"CPPTRACE_GET_SYMBOLS_WITH_NOTHING",
|
#"CPPTRACE_GET_SYMBOLS_WITH_NOTHING",
|
||||||
],
|
],
|
||||||
"demangle": [
|
"demangle": [
|
||||||
"CPPTRACE_DEMANGLE_WITH_CXXABI",
|
"CPPTRACE_DEMANGLE_WITH_CXXABI",
|
||||||
#"CPPTRACE_DEMANGLE_WITH_NOTHING",
|
#"CPPTRACE_DEMANGLE_WITH_NOTHING",
|
||||||
],
|
]
|
||||||
"shared": ["On" if shared else "Off"]
|
}
|
||||||
},
|
|
||||||
exclude = []
|
exclude = []
|
||||||
).run(build_and_test)
|
run_matrix(matrix, exclude, build_and_test)
|
||||||
|
|
||||||
def run_macos_default(compilers: list, shared: bool):
|
|
||||||
MatrixRunner(
|
|
||||||
matrix = {
|
matrix = {
|
||||||
"compiler": compilers,
|
"compiler": ["g++-13", "clang++"],
|
||||||
"target": ["Debug"],
|
"target": ["Debug"],
|
||||||
"std": ["11", "20"],
|
"std": ["11", "20"],
|
||||||
"config": [""],
|
"config": [""]
|
||||||
"shared": ["On" if shared else "Off"]
|
}
|
||||||
},
|
|
||||||
exclude = []
|
exclude = []
|
||||||
).run(build_and_test_full_or_auto)
|
run_matrix(matrix, exclude, build_and_test_full_or_auto)
|
||||||
|
if platform.system() == "Windows":
|
||||||
|
parser.add_argument(
|
||||||
|
"--clang-only",
|
||||||
|
action="store_true"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--msvc-only",
|
||||||
|
action="store_true"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--mingw-only",
|
||||||
|
action="store_true"
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
compilers = ["cl", "clang++", "g++"]
|
||||||
|
if args.clang_only:
|
||||||
|
compilers = ["clang++"]
|
||||||
|
if args.msvc_only:
|
||||||
|
compilers = ["cl"]
|
||||||
|
if args.mingw_only:
|
||||||
|
compilers = ["g++"]
|
||||||
|
|
||||||
def run_windows_matrix(compilers: list, shared: bool):
|
|
||||||
MatrixRunner(
|
|
||||||
matrix = {
|
matrix = {
|
||||||
"compiler": compilers,
|
"compiler": compilers,
|
||||||
"target": ["Debug"],
|
"target": ["Debug"],
|
||||||
"std": ["11", "20"],
|
"std": ["11", "20"],
|
||||||
"unwind": [
|
"unwind": [
|
||||||
"CPPTRACE_UNWIND_WITH_WINAPI",
|
"CPPTRACE_UNWIND_WITH_WINAPI",
|
||||||
"CPPTRACE_UNWIND_WITH_DBGHELP",
|
"CPPTRACE_UNWIND_WITH_UNWIND",
|
||||||
"CPPTRACE_UNWIND_WITH_UNWIND", # Broken on github actions for some reason
|
|
||||||
#"CPPTRACE_UNWIND_WITH_NOTHING",
|
#"CPPTRACE_UNWIND_WITH_NOTHING",
|
||||||
],
|
],
|
||||||
"symbols": [
|
"symbols": [
|
||||||
"CPPTRACE_GET_SYMBOLS_WITH_DBGHELP",
|
"CPPTRACE_GET_SYMBOLS_WITH_DBGHELP",
|
||||||
"CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE",
|
"CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE",
|
||||||
"CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF",
|
|
||||||
#"CPPTRACE_GET_SYMBOLS_WITH_NOTHING",
|
#"CPPTRACE_GET_SYMBOLS_WITH_NOTHING",
|
||||||
],
|
],
|
||||||
"demangle": [
|
"demangle": [
|
||||||
"CPPTRACE_DEMANGLE_WITH_CXXABI",
|
#"CPPTRACE_DEMANGLE_WITH_CXXABI",
|
||||||
"CPPTRACE_DEMANGLE_WITH_NOTHING",
|
"CPPTRACE_DEMANGLE_WITH_NOTHING",
|
||||||
],
|
]
|
||||||
"shared": ["On" if shared else "Off"]
|
}
|
||||||
},
|
|
||||||
exclude = [
|
exclude = [
|
||||||
{
|
{
|
||||||
"demangle": "CPPTRACE_DEMANGLE_WITH_CXXABI",
|
"demangle": "CPPTRACE_DEMANGLE_WITH_CXXABI",
|
||||||
@ -424,107 +418,28 @@ def run_windows_matrix(compilers: list, shared: bool):
|
|||||||
"symbols": "CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE",
|
"symbols": "CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE",
|
||||||
"compiler": "clang++"
|
"compiler": "clang++"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"symbols": "CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF",
|
|
||||||
"compiler": "cl"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"symbols": "CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF",
|
|
||||||
"compiler": "clang++"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"symbols": "CPPTRACE_GET_SYMBOLS_WITH_DBGHELP",
|
"symbols": "CPPTRACE_GET_SYMBOLS_WITH_DBGHELP",
|
||||||
"compiler": "g++"
|
"compiler": "g++"
|
||||||
},
|
|
||||||
{
|
|
||||||
"symbols": "CPPTRACE_GET_SYMBOLS_WITH_DBGHELP",
|
|
||||||
"demangle": "CPPTRACE_DEMANGLE_WITH_CXXABI"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"symbols": "CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF",
|
|
||||||
"demangle": "CPPTRACE_DEMANGLE_WITH_NOTHING"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"symbols": "CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE",
|
|
||||||
"demangle": "CPPTRACE_DEMANGLE_WITH_NOTHING"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
).run(build_and_test)
|
run_matrix(matrix, exclude, build_and_test)
|
||||||
|
|
||||||
def run_windows_default(compilers: list, shared: bool):
|
|
||||||
MatrixRunner(
|
|
||||||
matrix = {
|
matrix = {
|
||||||
"compiler": compilers,
|
"compiler": compilers,
|
||||||
"target": ["Debug"],
|
"target": ["Debug"],
|
||||||
"std": ["11", "20"],
|
"std": ["11", "20"],
|
||||||
"config": [""],
|
"config": [""]
|
||||||
"shared": ["On" if shared else "Off"]
|
}
|
||||||
},
|
exclude = [
|
||||||
exclude = []
|
{
|
||||||
).run(build_and_test_full_or_auto)
|
"config": "-DCPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE=On"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
run_matrix(matrix, exclude, build_and_test_full_or_auto)
|
||||||
|
|
||||||
def main():
|
global failed
|
||||||
parser = argparse.ArgumentParser(
|
if failed:
|
||||||
prog="Build in all configs",
|
print("🔴 Some checks failed")
|
||||||
description="Try building the library in all possible configurations for the current host"
|
sys.exit(1)
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--clang",
|
|
||||||
action="store_true"
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--gcc",
|
|
||||||
action="store_true"
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--msvc",
|
|
||||||
action="store_true"
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--all",
|
|
||||||
action="store_true"
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--shared",
|
|
||||||
action="store_true"
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--default-config",
|
|
||||||
action="store_true"
|
|
||||||
)
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
if platform.system() == "Linux":
|
|
||||||
compilers = []
|
|
||||||
if args.clang or args.all:
|
|
||||||
compilers.append("clang++-14")
|
|
||||||
if args.gcc or args.all:
|
|
||||||
compilers.append("g++-10")
|
|
||||||
if args.default_config:
|
|
||||||
run_linux_default(compilers, args.shared)
|
|
||||||
else:
|
|
||||||
run_linux_matrix(compilers, args.shared)
|
|
||||||
if platform.system() == "Darwin":
|
|
||||||
compilers = []
|
|
||||||
if args.clang or args.all:
|
|
||||||
compilers.append("clang++")
|
|
||||||
if args.gcc or args.all:
|
|
||||||
compilers.append("g++-12")
|
|
||||||
if args.default_config:
|
|
||||||
run_macos_default(compilers, args.shared)
|
|
||||||
else:
|
|
||||||
run_macos_matrix(compilers, args.shared)
|
|
||||||
if platform.system() == "Windows":
|
|
||||||
compilers = []
|
|
||||||
if args.clang or args.all:
|
|
||||||
compilers.append("clang++")
|
|
||||||
if args.msvc or args.all:
|
|
||||||
compilers.append("cl")
|
|
||||||
if args.gcc or args.all:
|
|
||||||
compilers.append("g++")
|
|
||||||
if args.default_config:
|
|
||||||
run_windows_default(compilers, args.shared)
|
|
||||||
else:
|
|
||||||
run_windows_matrix(compilers, args.shared)
|
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|||||||
168
ci/unittest.py
168
ci/unittest.py
@ -1,168 +0,0 @@
|
|||||||
import argparse
|
|
||||||
import os
|
|
||||||
import platform
|
|
||||||
import shutil
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
from typing import Tuple
|
|
||||||
from colorama import Fore, Back, Style
|
|
||||||
|
|
||||||
from util import *
|
|
||||||
|
|
||||||
sys.stdout.reconfigure(encoding='utf-8') # for windows gh runner
|
|
||||||
|
|
||||||
def get_c_compiler_counterpart(compiler: str) -> str:
|
|
||||||
return compiler.replace("clang++", "clang").replace("g++", "gcc")
|
|
||||||
|
|
||||||
def build(runner: MatrixRunner):
|
|
||||||
if platform.system() == "Linux":
|
|
||||||
matrix = runner.current_config()
|
|
||||||
if "stdlib" in matrix and matrix["stdlib"] == "libc++":
|
|
||||||
gtest_path = "/tmp/gtest_install_libcxx"
|
|
||||||
else:
|
|
||||||
gtest_path = "/tmp/gtest_install"
|
|
||||||
args = [
|
|
||||||
"cmake",
|
|
||||||
"..",
|
|
||||||
"-GNinja",
|
|
||||||
f"-DCMAKE_CXX_COMPILER={matrix['compiler']}",
|
|
||||||
f"-DCMAKE_C_COMPILER={get_c_compiler_counterpart(matrix['compiler'])}",
|
|
||||||
f"-DCMAKE_BUILD_TYPE={matrix['build_type']}",
|
|
||||||
f"-DCPPTRACE_BUILD_SHARED={matrix['shared']}",
|
|
||||||
f"-DHAS_DL_FIND_OBJECT={matrix['has_dl_find_object']}",
|
|
||||||
"-DCPPTRACE_WERROR_BUILD=On",
|
|
||||||
"-DCPPTRACE_STD_FORMAT=Off",
|
|
||||||
"-DCPPTRACE_BUILD_TESTING=On",
|
|
||||||
f"-DCPPTRACE_SANITIZER_BUILD={matrix['sanitizers']}",
|
|
||||||
f"-DCPPTRACE_BUILD_NO_SYMBOLS={matrix['symbols']}",
|
|
||||||
f"-DCPPTRACE_BUILD_TESTING_SPLIT_DWARF={matrix['split_dwarf']}",
|
|
||||||
f"-DCPPTRACE_BUILD_TESTING_DWARF_VERSION={matrix['dwarf_version']}",
|
|
||||||
f"-DCPPTRACE_USE_EXTERNAL_LIBDWARF=On",
|
|
||||||
f"-DCPPTRACE_USE_EXTERNAL_ZSTD=On",
|
|
||||||
f"-DCPPTRACE_USE_EXTERNAL_GTEST=On",
|
|
||||||
f"-DCMAKE_PREFIX_PATH={gtest_path}",
|
|
||||||
*(["-DCMAKE_CXX_FLAGS=-stdlib=libc++"] if "stdlib" in matrix and matrix["stdlib"] == "libc++" else [])
|
|
||||||
]
|
|
||||||
return runner.run_command(*args) and runner.run_command("ninja")
|
|
||||||
elif platform.system() == "Darwin":
|
|
||||||
matrix = runner.current_config()
|
|
||||||
if "clang++" in matrix["compiler"]:
|
|
||||||
gtest_path = "/tmp/gtest_asan_install" if matrix['sanitizers'] == "ON" else "/tmp/gtest_install"
|
|
||||||
else:
|
|
||||||
gtest_path = "/tmp/gtest_install_gcc"
|
|
||||||
args = [
|
|
||||||
"cmake",
|
|
||||||
"..",
|
|
||||||
"-GNinja",
|
|
||||||
f"-DCMAKE_CXX_COMPILER={matrix['compiler']}",
|
|
||||||
f"-DCMAKE_C_COMPILER={get_c_compiler_counterpart(matrix['compiler'])}",
|
|
||||||
f"-DCMAKE_BUILD_TYPE={matrix['build_type']}",
|
|
||||||
f"-DCPPTRACE_BUILD_SHARED={matrix['shared']}",
|
|
||||||
"-DCPPTRACE_WERROR_BUILD=On",
|
|
||||||
"-DCPPTRACE_STD_FORMAT=Off",
|
|
||||||
"-DCPPTRACE_BUILD_TESTING=On",
|
|
||||||
f"-DCPPTRACE_SANITIZER_BUILD={matrix['sanitizers']}",
|
|
||||||
f"-DCPPTRACE_BUILD_NO_SYMBOLS={matrix['symbols']}",
|
|
||||||
# f"-DCPPTRACE_BUILD_TESTING_SPLIT_DWARF={matrix['split_dwarf']}",
|
|
||||||
# f"-DCPPTRACE_BUILD_TESTING_SPLIT_DWARF={matrix['dwarf_version']}",
|
|
||||||
f"-DCPPTRACE_USE_EXTERNAL_LIBDWARF=On",
|
|
||||||
f"-DCPPTRACE_USE_EXTERNAL_ZSTD=On",
|
|
||||||
f"-DCPPTRACE_USE_EXTERNAL_GTEST=On",
|
|
||||||
f"-DCMAKE_PREFIX_PATH={gtest_path}",
|
|
||||||
]
|
|
||||||
return runner.run_command(*args) and runner.run_command("ninja")
|
|
||||||
else:
|
|
||||||
raise ValueError()
|
|
||||||
|
|
||||||
def test(runner: MatrixRunner):
|
|
||||||
if platform.system() == "Linux":
|
|
||||||
return runner.run_command("./unittest") and runner.run_command("bash", "-c", "exec -a u ./unittest")
|
|
||||||
elif platform.system() == "Darwin":
|
|
||||||
if runner.current_config()["dSYM"]:
|
|
||||||
if not runner.run_command("dsymutil", "unittest"):
|
|
||||||
return False
|
|
||||||
good = runner.run_command("./unittest") and runner.run_command("bash", "-c", "exec -a u ./unittest")
|
|
||||||
if runner.current_config()["dSYM"]:
|
|
||||||
shutil.rmtree("unittest.dSYM")
|
|
||||||
return good
|
|
||||||
else:
|
|
||||||
raise ValueError()
|
|
||||||
|
|
||||||
def build_and_test(runner: MatrixRunner):
|
|
||||||
# the build directory has to be purged on compiler or shared change
|
|
||||||
last = runner.last_config()
|
|
||||||
current = runner.current_config()
|
|
||||||
if (
|
|
||||||
last is None
|
|
||||||
or last["compiler"] != current["compiler"]
|
|
||||||
or ("stdlib" in current and last["stdlib"] != current["stdlib"])
|
|
||||||
or (platform.system() == "Darwin" and last["sanitizers"] != current["sanitizers"])
|
|
||||||
) and os.path.exists("build"):
|
|
||||||
shutil.rmtree("build", ignore_errors=True)
|
|
||||||
|
|
||||||
if not os.path.exists("build"):
|
|
||||||
os.mkdir("build")
|
|
||||||
os.chdir("build")
|
|
||||||
|
|
||||||
good = False
|
|
||||||
if build(runner):
|
|
||||||
good = test(runner)
|
|
||||||
|
|
||||||
os.chdir("..")
|
|
||||||
print(flush=True)
|
|
||||||
|
|
||||||
return good
|
|
||||||
|
|
||||||
def run_linux_matrix():
|
|
||||||
MatrixRunner(
|
|
||||||
matrix = {
|
|
||||||
"compiler": ["g++-10", "clang++-18"],
|
|
||||||
"stdlib": ["libstdc++", "libc++"],
|
|
||||||
"sanitizers": ["OFF", "ON"],
|
|
||||||
"build_type": ["Debug", "RelWithDebInfo"],
|
|
||||||
"shared": ["OFF", "ON"],
|
|
||||||
"has_dl_find_object": ["OFF", "ON"],
|
|
||||||
"split_dwarf": ["OFF", "ON"],
|
|
||||||
"dwarf_version": ["4", "5"],
|
|
||||||
"symbols": ["On", "Off"],
|
|
||||||
},
|
|
||||||
exclude = [
|
|
||||||
{
|
|
||||||
"compiler": "g++-10",
|
|
||||||
"stdlib": "libc++",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
# need to workaround https://github.com/llvm/llvm-project/issues/59432 later
|
|
||||||
"stdlib": "libc++",
|
|
||||||
"sanitizers": "ON",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
).run(build_and_test)
|
|
||||||
|
|
||||||
def run_macos_matrix():
|
|
||||||
MatrixRunner(
|
|
||||||
matrix = {
|
|
||||||
"compiler": ["g++-12", "clang++"],
|
|
||||||
"sanitizers": ["OFF", "ON"],
|
|
||||||
"build_type": ["Debug", "RelWithDebInfo"],
|
|
||||||
"shared": ["OFF", "ON"],
|
|
||||||
"dSYM": [True, False],
|
|
||||||
"symbols": ["On", "Off"],
|
|
||||||
},
|
|
||||||
exclude = [
|
|
||||||
{
|
|
||||||
"compiler": "g++-12",
|
|
||||||
"sanitizers": "ON",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
).run(build_and_test)
|
|
||||||
|
|
||||||
def main():
|
|
||||||
if platform.system() == "Linux":
|
|
||||||
run_linux_matrix()
|
|
||||||
if platform.system() == "Darwin":
|
|
||||||
run_macos_matrix()
|
|
||||||
if platform.system() == "Windows":
|
|
||||||
raise ValueError() # run_windows_matrix()
|
|
||||||
|
|
||||||
main()
|
|
||||||
163
ci/util.py
163
ci/util.py
@ -1,159 +1,20 @@
|
|||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import itertools
|
import itertools
|
||||||
from typing import List, Dict
|
from typing import List
|
||||||
from colorama import Fore, Back, Style
|
|
||||||
import re
|
|
||||||
import time
|
|
||||||
|
|
||||||
# https://stackoverflow.com/a/14693789/15675011
|
def do_exclude(matrix_config, exclude):
|
||||||
ansi_escape = re.compile(r'''
|
return all(map(lambda k: matrix_config[k] == exclude[k], exclude.keys()))
|
||||||
\x1B # ESC
|
|
||||||
(?: # 7-bit C1 Fe (except CSI)
|
|
||||||
[@-Z\\-_]
|
|
||||||
| # or [ for CSI, followed by a control sequence
|
|
||||||
\[
|
|
||||||
[0-?]* # Parameter bytes
|
|
||||||
[ -/]* # Intermediate bytes
|
|
||||||
[@-~] # Final byte
|
|
||||||
)
|
|
||||||
''', re.VERBOSE)
|
|
||||||
|
|
||||||
class MatrixRunner:
|
def run_matrix(matrix, exclude, fn):
|
||||||
def __init__(self, matrix, exclude):
|
#print(matrix.values())
|
||||||
self.matrix = matrix
|
for config in itertools.product(*matrix.values()):
|
||||||
self.exclude = exclude
|
#print(config)
|
||||||
self.include = self.parse_includes()
|
|
||||||
self.keys = [*matrix.keys()]
|
|
||||||
self.values = [*matrix.values()]
|
|
||||||
self.results = {} # insertion-ordered
|
|
||||||
self.failed = False
|
|
||||||
self.work = self.get_work()
|
|
||||||
|
|
||||||
self.last_matrix_config = None
|
|
||||||
self.current_matrix_config = None
|
|
||||||
|
|
||||||
def parse_includes(self) -> Dict[str, List[str]]:
|
|
||||||
includes: Dict[str, List[str]] = dict()
|
|
||||||
for arg in sys.argv:
|
|
||||||
if arg.startswith("--slice="):
|
|
||||||
rest = arg[len("--slice="):]
|
|
||||||
key, value = rest.split(":")
|
|
||||||
if key not in includes:
|
|
||||||
includes[key] = []
|
|
||||||
includes[key].append(value)
|
|
||||||
return includes
|
|
||||||
|
|
||||||
def run_command(self, *args: List[str], always_output=False, output_matcher=None) -> bool:
|
|
||||||
self.log(f"{Fore.CYAN}{Style.BRIGHT}Running Command \"{' '.join(args)}\"{Style.RESET_ALL}")
|
|
||||||
start_time = time.time()
|
|
||||||
p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
||||||
stdout, stderr = p.communicate()
|
|
||||||
runtime = time.time() - start_time
|
|
||||||
self.log(Style.RESET_ALL, end="") # makefile in parallel sometimes messes up colors
|
|
||||||
if p.returncode != 0:
|
|
||||||
self.log(f"{Fore.RED}{Style.BRIGHT}Command failed{Style.RESET_ALL} {Fore.MAGENTA}(time: {runtime:.2f}s){Style.RESET_ALL}")
|
|
||||||
self.log("stdout:")
|
|
||||||
self.log(stdout.decode("utf-8"), end="")
|
|
||||||
self.log("stderr:")
|
|
||||||
self.log(stderr.decode("utf-8"), end="")
|
|
||||||
self.failed = True
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
self.log(f"{Fore.GREEN}{Style.BRIGHT}Command succeeded{Style.RESET_ALL} {Fore.MAGENTA}(time: {runtime:.2f}s){Style.RESET_ALL}")
|
|
||||||
if always_output:
|
|
||||||
self.log("stdout:")
|
|
||||||
self.log(stdout.decode("utf-8"), end="")
|
|
||||||
self.log("stderr:")
|
|
||||||
self.log(stderr.decode("utf-8"), end="")
|
|
||||||
elif len(stderr) != 0:
|
|
||||||
self.log("stderr:")
|
|
||||||
self.log(stderr.decode("utf-8"), end="")
|
|
||||||
if output_matcher is not None:
|
|
||||||
if not output_matcher(stdout.decode("utf-8")):
|
|
||||||
self.failed = True
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
def set_fail(self):
|
|
||||||
self.failed = True
|
|
||||||
|
|
||||||
def current_config(self):
|
|
||||||
return self.current_matrix_config
|
|
||||||
|
|
||||||
def last_config(self):
|
|
||||||
return self.last_matrix_config
|
|
||||||
|
|
||||||
def log(self, *args, **kwargs):
|
|
||||||
print(*args, **kwargs, flush=True)
|
|
||||||
|
|
||||||
def do_exclude(self, matrix_config, exclude):
|
|
||||||
return all(map(lambda k: matrix_config[k] == exclude[k], exclude.keys()))
|
|
||||||
|
|
||||||
def do_include(self, matrix_config, include):
|
|
||||||
if len(include) == 0:
|
|
||||||
return True
|
|
||||||
return all(map(lambda k: matrix_config[k] in include[k], include.keys()))
|
|
||||||
|
|
||||||
def assignment_to_matrix_config(self, assignment):
|
|
||||||
matrix_config = {}
|
matrix_config = {}
|
||||||
for k, v in zip(self.matrix.keys(), assignment):
|
for k, v in zip(matrix.keys(), config):
|
||||||
matrix_config[k] = v
|
matrix_config[k] = v
|
||||||
return matrix_config
|
#print(matrix_config)
|
||||||
|
if any(map(lambda ex: do_exclude(matrix_config, ex), exclude)):
|
||||||
def get_work(self):
|
continue
|
||||||
work = []
|
|
||||||
for assignment in itertools.product(*self.matrix.values()):
|
|
||||||
config = self.assignment_to_matrix_config(assignment)
|
|
||||||
if any(map(lambda ex: self.do_exclude(config, ex), self.exclude)):
|
|
||||||
continue
|
|
||||||
if not self.do_include(config, self.include):
|
|
||||||
continue
|
|
||||||
work.append(assignment)
|
|
||||||
return work
|
|
||||||
|
|
||||||
def run(self, fn):
|
|
||||||
for i, assignment in enumerate(self.work):
|
|
||||||
matrix_config = self.assignment_to_matrix_config(assignment)
|
|
||||||
config_tuple = tuple(self.values[i].index(p) for i, p in enumerate(assignment))
|
|
||||||
config_str = ', '.join(map(lambda v: str(v), matrix_config.values()))
|
|
||||||
if config_str == "":
|
|
||||||
self.log(f"{Fore.BLUE}{Style.BRIGHT}{'=' * 10} [{i + 1}/{len(self.work)}] Running with blank config {'=' * 10}{Style.RESET_ALL}")
|
|
||||||
else:
|
|
||||||
self.log(f"{Fore.BLUE}{Style.BRIGHT}{'=' * 10} [{i + 1}/{len(self.work)}] Running with config {config_str} {'=' * 10}{Style.RESET_ALL}")
|
|
||||||
self.last_matrix_config = self.current_matrix_config
|
|
||||||
self.current_matrix_config = matrix_config
|
|
||||||
self.results[config_tuple] = fn(self)
|
|
||||||
self.print_results()
|
|
||||||
if self.failed:
|
|
||||||
self.log("🔴 Some checks failed")
|
|
||||||
sys.exit(1)
|
|
||||||
else:
|
else:
|
||||||
self.log("🟢 All checks passed")
|
fn(matrix_config)
|
||||||
|
|
||||||
def adj_width(self, text):
|
|
||||||
return len(text) - len(ansi_escape.sub("", text))
|
|
||||||
|
|
||||||
def print_table(self, table):
|
|
||||||
columns = len(table[0])
|
|
||||||
column_widths = [1 for _ in range(columns)]
|
|
||||||
for row in table:
|
|
||||||
for i, cell in enumerate(row):
|
|
||||||
column_widths[i] = max(column_widths[i], len(ansi_escape.sub("", cell)))
|
|
||||||
for j, cell in enumerate(table[0]):
|
|
||||||
self.log("| {cell:{width}} ".format(cell=cell, width=column_widths[j] + self.adj_width(cell)), end="")
|
|
||||||
self.log("|")
|
|
||||||
for i, row in enumerate(table[1:]):
|
|
||||||
for j, cell in enumerate(row):
|
|
||||||
self.log("| {cell:{width}} ".format(cell=cell, width=column_widths[j] + self.adj_width(cell)), end="")
|
|
||||||
self.log("|")
|
|
||||||
|
|
||||||
def print_results(self):
|
|
||||||
self.log("Results:")
|
|
||||||
table = [self.keys]
|
|
||||||
for result in self.results:
|
|
||||||
table.append([
|
|
||||||
f"{Fore.GREEN if self.results[result] else Fore.RED}{Style.BRIGHT}{self.values[i][v]}{Style.RESET_ALL}"
|
|
||||||
for i, v in enumerate(result)
|
|
||||||
])
|
|
||||||
self.print_table(table)
|
|
||||||
|
|||||||
@ -1,137 +0,0 @@
|
|||||||
# ================================================== Platform Support ==================================================
|
|
||||||
function(check_support var source includes libraries definitions)
|
|
||||||
set(CMAKE_REQUIRED_INCLUDES "${includes}")
|
|
||||||
list(APPEND CMAKE_REQUIRED_INCLUDES "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
|
||||||
set(CMAKE_REQUIRED_LIBRARIES "${libraries}")
|
|
||||||
set(CMAKE_REQUIRED_DEFINITIONS "${definitions}")
|
|
||||||
set(CMAKE_CXX_STANDARD 11)
|
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
||||||
string(CONCAT full_source "#include \"${source}\"" ${nonce})
|
|
||||||
check_cxx_source_compiles(${full_source} ${var})
|
|
||||||
set(${var} ${${var}} PARENT_SCOPE)
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
|
||||||
check_support(HAS_CXXABI has_cxxabi.cpp "" "" "")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
|
||||||
check_support(HAS_ATTRIBUTE_PACKED has_attribute_packed.cpp "" "" "")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NOT WIN32)
|
|
||||||
check_support(HAS_UNWIND has_unwind.cpp "" "" "")
|
|
||||||
check_support(HAS_EXECINFO has_execinfo.cpp "" "" "")
|
|
||||||
else()
|
|
||||||
check_support(HAS_STACKWALK has_stackwalk.cpp "" "dbghelp" "")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NOT WIN32 OR MINGW)
|
|
||||||
check_support(HAS_BACKTRACE has_backtrace.cpp "" "backtrace" "${CPPTRACE_BACKTRACE_PATH_DEFINITION}")
|
|
||||||
set(STACKTRACE_LINK_LIB "stdc++_libbacktrace")
|
|
||||||
check_support(HAS_CXX_EXCEPTION_TYPE has_cxx_exception_type.cpp "" "" "")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(UNIX AND NOT APPLE)
|
|
||||||
check_support(HAS_DL_FIND_OBJECT has_dl_find_object.cpp "" "dl" "")
|
|
||||||
if(NOT HAS_DL_FIND_OBJECT)
|
|
||||||
check_support(HAS_DLADDR1 has_dladdr1.cpp "" "dl" "")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(APPLE)
|
|
||||||
check_support(HAS_MACH_VM has_mach_vm.cpp "" "" "")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# ================================================ Autoconfig unwinding ================================================
|
|
||||||
# Unwind back-ends
|
|
||||||
if(
|
|
||||||
NOT (
|
|
||||||
CPPTRACE_UNWIND_WITH_UNWIND OR
|
|
||||||
CPPTRACE_UNWIND_WITH_LIBUNWIND OR
|
|
||||||
CPPTRACE_UNWIND_WITH_EXECINFO OR
|
|
||||||
CPPTRACE_UNWIND_WITH_WINAPI OR
|
|
||||||
CPPTRACE_UNWIND_WITH_DBGHELP OR
|
|
||||||
CPPTRACE_UNWIND_WITH_NOTHING
|
|
||||||
)
|
|
||||||
)
|
|
||||||
# Attempt to auto-config
|
|
||||||
if(APPLE AND ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang"))
|
|
||||||
if(HAS_EXECINFO)
|
|
||||||
set(CPPTRACE_UNWIND_WITH_EXECINFO On)
|
|
||||||
message(STATUS "Cpptrace auto config: Using execinfo.h for unwinding")
|
|
||||||
else()
|
|
||||||
set(CPPTRACE_UNWIND_WITH_NOTHING On)
|
|
||||||
message(FATAL_ERROR "Cpptrace auto config: No unwinding back-end seems to be supported, stack tracing will not work. To compile anyway set CPPTRACE_UNWIND_WITH_NOTHING.")
|
|
||||||
endif()
|
|
||||||
elseif(UNIX)
|
|
||||||
if(HAS_UNWIND)
|
|
||||||
set(CPPTRACE_UNWIND_WITH_UNWIND On)
|
|
||||||
message(STATUS "Cpptrace auto config: Using libgcc unwind for unwinding")
|
|
||||||
elseif(HAS_EXECINFO)
|
|
||||||
set(CPPTRACE_UNWIND_WITH_EXECINFO On)
|
|
||||||
message(STATUS "Cpptrace auto config: Using execinfo.h for unwinding")
|
|
||||||
else()
|
|
||||||
set(CPPTRACE_UNWIND_WITH_NOTHING On)
|
|
||||||
message(FATAL_ERROR "Cpptrace auto config: No unwinding back-end seems to be supported, stack tracing will not work. To compile anyway set CPPTRACE_UNWIND_WITH_NOTHING.")
|
|
||||||
endif()
|
|
||||||
elseif(MINGW OR WIN32)
|
|
||||||
if(HAS_STACKWALK)
|
|
||||||
set(CPPTRACE_UNWIND_WITH_DBGHELP On)
|
|
||||||
message(STATUS "Cpptrace auto config: Using dbghelp for unwinding")
|
|
||||||
else()
|
|
||||||
set(CPPTRACE_UNWIND_WITH_WINAPI On)
|
|
||||||
message(STATUS "Cpptrace auto config: Using winapi for unwinding")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
#message(STATUS "MANUAL CONFIG SPECIFIED")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# ================================================= Autoconfig symbols =================================================
|
|
||||||
if(
|
|
||||||
NOT (
|
|
||||||
CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE OR
|
|
||||||
CPPTRACE_GET_SYMBOLS_WITH_LIBDL OR
|
|
||||||
CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE OR
|
|
||||||
CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF OR
|
|
||||||
CPPTRACE_GET_SYMBOLS_WITH_DBGHELP OR
|
|
||||||
CPPTRACE_GET_SYMBOLS_WITH_NOTHING
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if(UNIX)
|
|
||||||
message(STATUS "Cpptrace auto config: Using libdwarf for symbols")
|
|
||||||
set(CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF On)
|
|
||||||
elseif(MINGW)
|
|
||||||
message(STATUS "Cpptrace auto config: Using libdwarf + dbghelp for symbols")
|
|
||||||
# Use both dbghelp and libdwarf under mingw: Some files may use pdb symbols, e.g. system dlls like KERNEL32.dll and
|
|
||||||
# ntdll.dll at the very least, but also other libraries linked with may have pdb symbols.
|
|
||||||
set(CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF On)
|
|
||||||
set(CPPTRACE_GET_SYMBOLS_WITH_DBGHELP On)
|
|
||||||
else()
|
|
||||||
message(STATUS "Cpptrace auto config: Using dbghelp for symbols")
|
|
||||||
set(CPPTRACE_GET_SYMBOLS_WITH_DBGHELP On)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# =============================================== Autoconfig demangling ================================================
|
|
||||||
# Handle demangle configuration
|
|
||||||
if(
|
|
||||||
NOT (
|
|
||||||
CPPTRACE_DEMANGLE_WITH_CXXABI OR
|
|
||||||
CPPTRACE_DEMANGLE_WITH_WINAPI OR
|
|
||||||
CPPTRACE_DEMANGLE_WITH_NOTHING
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if(HAS_CXXABI)
|
|
||||||
message(STATUS "Cpptrace auto config: Using cxxabi for demangling")
|
|
||||||
set(CPPTRACE_DEMANGLE_WITH_CXXABI On)
|
|
||||||
elseif(WIN32 AND NOT MINGW)
|
|
||||||
message(STATUS "Cpptrace auto config: Using dbghelp for demangling")
|
|
||||||
set(CPPTRACE_DEMANGLE_WITH_WINAPI On)
|
|
||||||
else()
|
|
||||||
set(CPPTRACE_DEMANGLE_WITH_NOTHING On)
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
#message(STATUS "Manual demangling back-end specified")
|
|
||||||
endif()
|
|
||||||
@ -1,51 +0,0 @@
|
|||||||
# Libdwarf needs zstd, cpptrace doesn't, and libdwarf has its own Findzstd but it doesn't define zstd::libzstd_static /
|
|
||||||
# zstd::libzstd_shared targets which leads to issues, necessitating a find_dependency(zstd) in cpptrace's cmake config
|
|
||||||
# and in order to support non-cmake-module installs we need to provide a Findzstd script.
|
|
||||||
# https://github.com/jeremy-rifkin/cpptrace/issues/112
|
|
||||||
|
|
||||||
# This will define
|
|
||||||
# zstd_FOUND
|
|
||||||
# zstd_INCLUDE_DIR
|
|
||||||
# zstd_LIBRARY
|
|
||||||
|
|
||||||
find_path(zstd_INCLUDE_DIR NAMES zstd.h)
|
|
||||||
|
|
||||||
find_library(zstd_LIBRARY_DEBUG NAMES zstdd zstd_staticd)
|
|
||||||
find_library(zstd_LIBRARY_RELEASE NAMES zstd zstd_static)
|
|
||||||
|
|
||||||
include(SelectLibraryConfigurations)
|
|
||||||
SELECT_LIBRARY_CONFIGURATIONS(zstd)
|
|
||||||
|
|
||||||
include(FindPackageHandleStandardArgs)
|
|
||||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(
|
|
||||||
zstd DEFAULT_MSG
|
|
||||||
zstd_LIBRARY zstd_INCLUDE_DIR
|
|
||||||
)
|
|
||||||
|
|
||||||
if(zstd_FOUND)
|
|
||||||
message(STATUS "Found Zstd: ${zstd_LIBRARY}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
mark_as_advanced(zstd_INCLUDE_DIR zstd_LIBRARY)
|
|
||||||
|
|
||||||
if(zstd_FOUND)
|
|
||||||
# just defining them the same... cmake will figure it out
|
|
||||||
if(NOT TARGET zstd::libzstd_static)
|
|
||||||
add_library(zstd::libzstd_static UNKNOWN IMPORTED)
|
|
||||||
set_target_properties(
|
|
||||||
zstd::libzstd_static
|
|
||||||
PROPERTIES
|
|
||||||
IMPORTED_LOCATION "${zstd_LIBRARIES}"
|
|
||||||
INTERFACE_INCLUDE_DIRECTORIES "${zstd_INCLUDE_DIR}"
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
if(NOT TARGET zstd::libzstd_shared)
|
|
||||||
add_library(zstd::libzstd_shared UNKNOWN IMPORTED)
|
|
||||||
set_target_properties(
|
|
||||||
zstd::libzstd_shared
|
|
||||||
PROPERTIES
|
|
||||||
IMPORTED_LOCATION "${zstd_LIBRARIES}"
|
|
||||||
INTERFACE_INCLUDE_DIRECTORIES "${zstd_INCLUDE_DIR}"
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
@ -1,86 +0,0 @@
|
|||||||
include(CMakePackageConfigHelpers)
|
|
||||||
|
|
||||||
# copy header files to CMAKE_INSTALL_INCLUDEDIR
|
|
||||||
# don't include third party header files
|
|
||||||
install(
|
|
||||||
DIRECTORY
|
|
||||||
"${PROJECT_SOURCE_DIR}/include/" # our header files
|
|
||||||
"${PROJECT_BINARY_DIR}/include/" # generated header files
|
|
||||||
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
|
|
||||||
COMPONENT ${package_name}_development
|
|
||||||
# PATTERN "**/third_party" EXCLUDE # skip third party directory
|
|
||||||
# PATTERN "**/third_party/**" EXCLUDE # skip third party files
|
|
||||||
)
|
|
||||||
|
|
||||||
# copy target build output artifacts to OS dependent locations
|
|
||||||
# (Except includes, that just sets a compiler flag with the path)
|
|
||||||
install(
|
|
||||||
TARGETS ${target_name}
|
|
||||||
EXPORT ${package_name}-targets
|
|
||||||
RUNTIME #
|
|
||||||
COMPONENT ${package_name}_runtime
|
|
||||||
LIBRARY #
|
|
||||||
COMPONENT ${package_name}_runtime
|
|
||||||
NAMELINK_COMPONENT ${package_name}_development
|
|
||||||
ARCHIVE #
|
|
||||||
COMPONENT ${package_name}_development
|
|
||||||
INCLUDES #
|
|
||||||
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
|
|
||||||
)
|
|
||||||
|
|
||||||
# create config file that points to targets file
|
|
||||||
configure_file(
|
|
||||||
"${PROJECT_SOURCE_DIR}/cmake/in/cpptrace-config-cmake.in"
|
|
||||||
"${PROJECT_BINARY_DIR}/cmake/${package_name}-config.cmake"
|
|
||||||
@ONLY
|
|
||||||
)
|
|
||||||
|
|
||||||
# copy config file for find_package to find
|
|
||||||
install(
|
|
||||||
FILES "${PROJECT_BINARY_DIR}/cmake/${package_name}-config.cmake"
|
|
||||||
DESTINATION "${CPPTRACE_INSTALL_CMAKEDIR}"
|
|
||||||
COMPONENT ${package_name}_development
|
|
||||||
)
|
|
||||||
|
|
||||||
# create version file for consumer to check version in CMake
|
|
||||||
write_basic_package_version_file(
|
|
||||||
"${package_name}-config-version.cmake"
|
|
||||||
COMPATIBILITY SameMajorVersion # a.k.a SemVer
|
|
||||||
)
|
|
||||||
|
|
||||||
# copy version file for find_package to find for version check
|
|
||||||
install(
|
|
||||||
FILES "${PROJECT_BINARY_DIR}/${package_name}-config-version.cmake"
|
|
||||||
DESTINATION "${CPPTRACE_INSTALL_CMAKEDIR}"
|
|
||||||
COMPONENT ${package_name}_development
|
|
||||||
)
|
|
||||||
|
|
||||||
# create targets file included by config file with targets for consumers
|
|
||||||
install(
|
|
||||||
EXPORT ${package_name}-targets
|
|
||||||
NAMESPACE cpptrace::
|
|
||||||
DESTINATION "${CPPTRACE_INSTALL_CMAKEDIR}"
|
|
||||||
COMPONENT ${package_name}_development
|
|
||||||
)
|
|
||||||
|
|
||||||
if(CPPTRACE_PROVIDE_EXPORT_SET)
|
|
||||||
export(
|
|
||||||
TARGETS ${target_name}
|
|
||||||
NAMESPACE cpptrace::
|
|
||||||
FILE "${PROJECT_BINARY_DIR}/${package_name}-targets.cmake"
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Findzstd.cmake
|
|
||||||
# vcpkg doesn't like anything being put in share/, which is where this goes apparently on their setup
|
|
||||||
if(NOT CPPTRACE_VCPKG)
|
|
||||||
install(
|
|
||||||
FILES "${PROJECT_SOURCE_DIR}/cmake/Findzstd.cmake"
|
|
||||||
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${package_name}"
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# support packaging library
|
|
||||||
if(PROJECT_IS_TOP_LEVEL)
|
|
||||||
include(CPack)
|
|
||||||
endif()
|
|
||||||
@ -1,208 +0,0 @@
|
|||||||
# Included further down to avoid interfering with our cache variables
|
|
||||||
# include(GNUInstallDirs)
|
|
||||||
|
|
||||||
# ---- Options Summary ----
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------------------------------
|
|
||||||
# | Option | Availability | Default |
|
|
||||||
# |=================================|===============|===============================================|
|
|
||||||
# | BUILD_SHARED_LIBS | Top-Level | OFF |
|
|
||||||
# | BUILD_TESTING | Top-Level | OFF |
|
|
||||||
# |---------------------------------|---------------|-----------------------------------------------|
|
|
||||||
# | CPPTRACE_BUILD_SHARED | Always | ${BUILD_SHARED_LIBS} |
|
|
||||||
# | CPPTRACE_BUILD_TESTING | Always | ${BUILD_TESTING} AND ${PROJECT_IS_TOP_LEVEL} |
|
|
||||||
# | CPPTRACE_INCLUDES_WITH_SYSTEM | Not Top-Level | ON |
|
|
||||||
# | CPPTRACE_INSTALL_CMAKEDIR | Always | ${CMAKE_INSTALL_LIBDIR}/cmake/${package_name} |
|
|
||||||
# | CPPTRACE_USE_EXTERNAL_LIBDWARF | Always | OFF |
|
|
||||||
# | CPPTRACE_USE_EXTERNAL_ZSTD | Always | OFF |
|
|
||||||
# | ... | | |
|
|
||||||
# ---------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# ---- Build Shared ----
|
|
||||||
|
|
||||||
# Sometimes it's useful to be able to single out a dependency to be built as
|
|
||||||
# static or shared, even if obtained from source
|
|
||||||
if(PROJECT_IS_TOP_LEVEL)
|
|
||||||
option(BUILD_SHARED_LIBS "Build shared libs" OFF)
|
|
||||||
endif()
|
|
||||||
option(
|
|
||||||
CPPTRACE_BUILD_SHARED
|
|
||||||
"Override BUILD_SHARED_LIBS for ${package_name} library"
|
|
||||||
${BUILD_SHARED_LIBS}
|
|
||||||
)
|
|
||||||
mark_as_advanced(CPPTRACE_BUILD_SHARED)
|
|
||||||
set(build_type STATIC)
|
|
||||||
if(CPPTRACE_BUILD_SHARED)
|
|
||||||
set(build_type SHARED)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# ---- Warning Guard ----
|
|
||||||
|
|
||||||
# target_include_directories with SYSTEM modifier will request the compiler to
|
|
||||||
# omit warnings from the provided paths, if the compiler supports that.
|
|
||||||
# This is to provide a user experience similar to find_package when
|
|
||||||
# add_subdirectory or FetchContent is used to consume this project.
|
|
||||||
set(warning_guard )
|
|
||||||
if(NOT PROJECT_IS_TOP_LEVEL)
|
|
||||||
option(
|
|
||||||
CPPTRACE_INCLUDES_WITH_SYSTEM
|
|
||||||
"Use SYSTEM modifier for ${package_name}'s includes, disabling warnings"
|
|
||||||
ON
|
|
||||||
)
|
|
||||||
mark_as_advanced(CPPTRACE_INCLUDES_WITH_SYSTEM)
|
|
||||||
if(CPPTRACE_INCLUDES_WITH_SYSTEM)
|
|
||||||
set(warning_guard SYSTEM)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# ---- Enable Testing ----
|
|
||||||
|
|
||||||
# By default tests aren't enabled even with BUILD_TESTING=ON unless the library
|
|
||||||
# is built as a top level project.
|
|
||||||
# This is in order to cut down on unnecessary compile times, since it's unlikely
|
|
||||||
# for users to want to run the tests of their dependencies.
|
|
||||||
if(PROJECT_IS_TOP_LEVEL)
|
|
||||||
option(BUILD_TESTING "Build tests" OFF)
|
|
||||||
endif()
|
|
||||||
if(PROJECT_IS_TOP_LEVEL AND BUILD_TESTING)
|
|
||||||
set(build_testing ON)
|
|
||||||
endif()
|
|
||||||
option(
|
|
||||||
CPPTRACE_BUILD_TESTING
|
|
||||||
"Override BUILD_TESTING for ${package_name} library"
|
|
||||||
${build_testing}
|
|
||||||
)
|
|
||||||
set(build_testing )
|
|
||||||
mark_as_advanced(CPPTRACE_BUILD_TESTING)
|
|
||||||
|
|
||||||
# ---- Install Include Directory ----
|
|
||||||
|
|
||||||
# Adds an extra directory to the include path by default, so that when you link
|
|
||||||
# against the target, you get `<prefix>/include/<package-X.Y.Z>` added to your
|
|
||||||
# include paths rather than `<prefix>/include`.
|
|
||||||
# This doesn't affect include paths used by consumers of this project, but helps
|
|
||||||
# prevent consumers having access to other projects in the same include
|
|
||||||
# directory (e.g. usr/include).
|
|
||||||
# The variable type is STRING rather than PATH, because otherwise passing
|
|
||||||
# -DCMAKE_INSTALL_INCLUDEDIR=include on the command line would expand to an
|
|
||||||
# absolute path with the base being the current CMake directory, leading to
|
|
||||||
# unexpected errors.
|
|
||||||
# if(PROJECT_IS_TOP_LEVEL)
|
|
||||||
# set(
|
|
||||||
# CMAKE_INSTALL_INCLUDEDIR "include/${package_name}-${PROJECT_VERSION}"
|
|
||||||
# CACHE STRING ""
|
|
||||||
# )
|
|
||||||
# # marked as advanced in GNUInstallDirs version, so we follow their lead
|
|
||||||
# mark_as_advanced(CMAKE_INSTALL_INCLUDEDIR)
|
|
||||||
# endif()
|
|
||||||
|
|
||||||
# do not include earlier or we can't set CMAKE_INSTALL_INCLUDEDIR above
|
|
||||||
# include required for CMAKE_INSTALL_LIBDIR below
|
|
||||||
include(GNUInstallDirs)
|
|
||||||
|
|
||||||
# ---- Install CMake Directory ----
|
|
||||||
|
|
||||||
# This allows package maintainers to freely override the installation path for
|
|
||||||
# the CMake configs.
|
|
||||||
# This doesn't affects include paths used by consumers of this project.
|
|
||||||
# The variable type is STRING rather than PATH, because otherwise passing
|
|
||||||
# -DCPPTRACE_INSTALL_CMAKEDIR=lib/cmake on the command line would expand to an
|
|
||||||
# absolute path with the base being the current CMake directory, leading to
|
|
||||||
# unexpected errors.
|
|
||||||
set(
|
|
||||||
CPPTRACE_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/${package_name}"
|
|
||||||
CACHE STRING "CMake package config location relative to the install prefix"
|
|
||||||
)
|
|
||||||
# depends on CMAKE_INSTALL_LIBDIR which is marked as advanced in GNUInstallDirs
|
|
||||||
mark_as_advanced(CPPTRACE_INSTALL_CMAKEDIR)
|
|
||||||
|
|
||||||
# ---- Symbol Options ----
|
|
||||||
|
|
||||||
option(CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE "" OFF)
|
|
||||||
option(CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF "" OFF)
|
|
||||||
option(CPPTRACE_GET_SYMBOLS_WITH_LIBDL "" OFF)
|
|
||||||
option(CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE "" OFF)
|
|
||||||
option(CPPTRACE_GET_SYMBOLS_WITH_DBGHELP "" OFF)
|
|
||||||
option(CPPTRACE_GET_SYMBOLS_WITH_NOTHING "" OFF)
|
|
||||||
|
|
||||||
# ---- Unwinding Options ----
|
|
||||||
|
|
||||||
option(CPPTRACE_UNWIND_WITH_UNWIND "" OFF)
|
|
||||||
option(CPPTRACE_UNWIND_WITH_LIBUNWIND "" OFF)
|
|
||||||
option(CPPTRACE_UNWIND_WITH_EXECINFO "" OFF)
|
|
||||||
option(CPPTRACE_UNWIND_WITH_WINAPI "" OFF)
|
|
||||||
option(CPPTRACE_UNWIND_WITH_DBGHELP "" OFF)
|
|
||||||
option(CPPTRACE_UNWIND_WITH_NOTHING "" OFF)
|
|
||||||
|
|
||||||
# ---- Demangling Options ----
|
|
||||||
|
|
||||||
option(CPPTRACE_DEMANGLE_WITH_CXXABI "" OFF)
|
|
||||||
option(CPPTRACE_DEMANGLE_WITH_WINAPI "" OFF)
|
|
||||||
option(CPPTRACE_DEMANGLE_WITH_NOTHING "" OFF)
|
|
||||||
|
|
||||||
# ---- Back-end configurations ----
|
|
||||||
|
|
||||||
set(CPPTRACE_BACKTRACE_PATH "" CACHE STRING "Path to backtrace.h, if the compiler doesn't already know it. Check /usr/lib/gcc/x86_64-linux-gnu/*/include.")
|
|
||||||
set(CPPTRACE_HARD_MAX_FRAMES "" CACHE STRING "Hard limit on unwinding depth. Default is 400.")
|
|
||||||
set(CPPTRACE_ADDR2LINE_PATH "" CACHE STRING "Absolute path to the addr2line executable you want to use.")
|
|
||||||
option(CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH "" OFF)
|
|
||||||
|
|
||||||
# ---- Other configurations ----
|
|
||||||
|
|
||||||
if(PROJECT_IS_TOP_LEVEL)
|
|
||||||
option(CPPTRACE_BUILD_TESTING "" OFF)
|
|
||||||
option(CPPTRACE_BUILD_TOOLS "" OFF)
|
|
||||||
option(CPPTRACE_BUILD_BENCHMARK "" OFF)
|
|
||||||
option(CPPTRACE_BUILD_NO_SYMBOLS "" OFF)
|
|
||||||
option(CPPTRACE_BUILD_TESTING_SPLIT_DWARF "" OFF)
|
|
||||||
set(CPPTRACE_BUILD_TESTING_DWARF_VERSION "0" CACHE STRING "")
|
|
||||||
option(CPPTRACE_BUILD_TEST_RDYNAMIC "" OFF)
|
|
||||||
mark_as_advanced(
|
|
||||||
CPPTRACE_BUILD_TESTING
|
|
||||||
CPPTRACE_BUILD_TOOLS
|
|
||||||
CPPTRACE_BUILD_BENCHMARK
|
|
||||||
CPPTRACE_BUILD_NO_SYMBOLS
|
|
||||||
CPPTRACE_BUILD_TESTING_SPLIT_DWARF
|
|
||||||
CPPTRACE_BUILD_TESTING_DWARF_VERSION
|
|
||||||
CPPTRACE_BUILD_TEST_RDYNAMIC
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
option(CPPTRACE_USE_EXTERNAL_LIBDWARF "" OFF)
|
|
||||||
option(CPPTRACE_FIND_LIBDWARF_WITH_PKGCONFIG "" OFF)
|
|
||||||
option(CPPTRACE_USE_EXTERNAL_ZSTD "" OFF)
|
|
||||||
option(CPPTRACE_CONAN "" OFF)
|
|
||||||
option(CPPTRACE_VCPKG "" OFF)
|
|
||||||
option(CPPTRACE_SANITIZER_BUILD "" OFF)
|
|
||||||
option(CPPTRACE_WERROR_BUILD "" OFF)
|
|
||||||
option(CPPTRACE_POSITION_INDEPENDENT_CODE "" ON)
|
|
||||||
option(CPPTRACE_SKIP_UNIT "" OFF)
|
|
||||||
option(CPPTRACE_STD_FORMAT "" ON)
|
|
||||||
option(CPPTRACE_UNPREFIXED_TRY_CATCH "" OFF)
|
|
||||||
option(CPPTRACE_USE_EXTERNAL_GTEST "" OFF)
|
|
||||||
set(CPPTRACE_ZSTD_URL "https://github.com/facebook/zstd/releases/download/v1.5.7/zstd-1.5.7.tar.gz" CACHE STRING "")
|
|
||||||
set(CPPTRACE_LIBDWARF_REPO "https://github.com/jeremy-rifkin/libdwarf-lite.git" CACHE STRING "")
|
|
||||||
set(CPPTRACE_LIBDWARF_TAG "fe09ca800b988e2ff21225ac5e7468ceade2a30e" CACHE STRING "") # v0.11.1
|
|
||||||
set(CPPTRACE_LIBDWARF_SHALLOW "1" CACHE STRING "")
|
|
||||||
option(CPPTRACE_PROVIDE_EXPORT_SET "" ON)
|
|
||||||
option(CPPTRACE_PROVIDE_EXPORT_SET_FOR_LIBDWARF "" OFF)
|
|
||||||
|
|
||||||
mark_as_advanced(
|
|
||||||
CPPTRACE_BACKTRACE_PATH
|
|
||||||
CPPTRACE_ADDR2LINE_PATH
|
|
||||||
CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH
|
|
||||||
CPPTRACE_SANITIZER_BUILD
|
|
||||||
CPPTRACE_WERROR_BUILD
|
|
||||||
CPPTRACE_CONAN
|
|
||||||
CPPTRACE_VCPKG
|
|
||||||
CPPTRACE_SKIP_UNIT
|
|
||||||
CPPTRACE_USE_EXTERNAL_GTEST
|
|
||||||
CPPTRACE_ZSTD_REPO
|
|
||||||
CPPTRACE_ZSTD_TAG
|
|
||||||
CPPTRACE_ZSTD_SHALLOW
|
|
||||||
CPPTRACE_LIBDWARF_REPO
|
|
||||||
CPPTRACE_LIBDWARF_TAG
|
|
||||||
CPPTRACE_LIBDWARF_SHALLOW
|
|
||||||
CPPTRACE_PROVIDE_EXPORT_SET
|
|
||||||
CPPTRACE_PROVIDE_EXPORT_SET_FOR_LIBDWARF
|
|
||||||
)
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
# In-source build guard
|
|
||||||
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
|
|
||||||
message(
|
|
||||||
FATAL_ERROR
|
|
||||||
"In-source builds are not supported. "
|
|
||||||
"You may need to delete 'CMakeCache.txt' and 'CMakeFiles/' before rebuilding this project."
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
# This variable is set by project() in CMake 3.21+
|
|
||||||
string(
|
|
||||||
COMPARE EQUAL
|
|
||||||
"${CMAKE_SOURCE_DIR}" "${PROJECT_SOURCE_DIR}"
|
|
||||||
PROJECT_IS_TOP_LEVEL
|
|
||||||
)
|
|
||||||
3
cmake/cpptrace-config.cmake.in
Normal file
3
cmake/cpptrace-config.cmake.in
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
@PACKAGE_INIT@
|
||||||
|
|
||||||
|
include(${CMAKE_CURRENT_LIST_DIR}/cpptrace_targets.cmake)
|
||||||
@ -1,6 +0,0 @@
|
|||||||
struct __attribute__((packed)) foo {
|
|
||||||
int i;
|
|
||||||
double d;
|
|
||||||
};
|
|
||||||
|
|
||||||
int main() {}
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
#include <cxxabi.h>
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
std::type_info* t = abi::__cxa_current_exception_type();
|
|
||||||
(void*) t;
|
|
||||||
}
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
#include <dlfcn.h>
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
dl_find_object result;
|
|
||||||
_dl_find_object(reinterpret_cast<void*>(main), &result);
|
|
||||||
}
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
#include <dlfcn.h>
|
|
||||||
#include <link.h>
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
Dl_info info;
|
|
||||||
link_map* link_map_info;
|
|
||||||
dladdr1(reinterpret_cast<void*>(&main), &info, reinterpret_cast<void**>(&link_map_info), RTLD_DL_LINKMAP);
|
|
||||||
}
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
#include <mach/mach.h>
|
|
||||||
#include <mach/mach_vm.h>
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
mach_vm_size_t vmsize;
|
|
||||||
uintptr_t addr = reinterpret_cast<uintptr_t>(&vmsize);
|
|
||||||
uintptr_t page_addr = addr & ~(4096 - 1);
|
|
||||||
mach_vm_address_t address = (mach_vm_address_t)page_addr;
|
|
||||||
vm_region_basic_info_data_t info;
|
|
||||||
mach_msg_type_number_t info_count =
|
|
||||||
sizeof(size_t) == 8 ? VM_REGION_BASIC_INFO_COUNT_64 : VM_REGION_BASIC_INFO_COUNT;
|
|
||||||
memory_object_name_t object;
|
|
||||||
mach_vm_region(
|
|
||||||
mach_task_self(),
|
|
||||||
&address,
|
|
||||||
&vmsize,
|
|
||||||
VM_REGION_BASIC_INFO,
|
|
||||||
(vm_region_info_t)&info,
|
|
||||||
&info_count,
|
|
||||||
&object
|
|
||||||
);
|
|
||||||
}
|
|
||||||
8
cmake/has_stacktrace.cpp
Normal file
8
cmake/has_stacktrace.cpp
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#include <stacktrace>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
std::stacktrace trace = std::stacktrace::current();
|
||||||
|
for(const auto entry : trace) {
|
||||||
|
(void)entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,102 +0,0 @@
|
|||||||
#define WIN32_LEAN_AND_MEAN
|
|
||||||
#include <windows.h>
|
|
||||||
#include <dbghelp.h>
|
|
||||||
|
|
||||||
#define IS_CLANG 0
|
|
||||||
#define IS_GCC 0
|
|
||||||
#define IS_MSVC 0
|
|
||||||
|
|
||||||
#if defined(__clang__)
|
|
||||||
#undef IS_CLANG
|
|
||||||
#define IS_CLANG 1
|
|
||||||
#elif defined(__GNUC__) || defined(__GNUG__)
|
|
||||||
#undef IS_GCC
|
|
||||||
#define IS_GCC 1
|
|
||||||
#elif defined(_MSC_VER)
|
|
||||||
#undef IS_MSVC
|
|
||||||
#define IS_MSVC 1
|
|
||||||
#else
|
|
||||||
#error "Unsupported compiler"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
HANDLE proc = GetCurrentProcess();
|
|
||||||
HANDLE thread = GetCurrentThread();
|
|
||||||
// https://jpassing.com/2008/03/12/walking-the-stack-of-the-current-thread/
|
|
||||||
|
|
||||||
// Get current thread context
|
|
||||||
// GetThreadContext cannot be used on the current thread.
|
|
||||||
// RtlCaptureContext doesn't work on i386
|
|
||||||
CONTEXT context;
|
|
||||||
#if defined(_M_IX86) || defined(__i386__)
|
|
||||||
ZeroMemory(&context, sizeof(CONTEXT));
|
|
||||||
context.ContextFlags = CONTEXT_CONTROL;
|
|
||||||
#if IS_MSVC
|
|
||||||
__asm {
|
|
||||||
label:
|
|
||||||
mov [context.Ebp], ebp;
|
|
||||||
mov [context.Esp], esp;
|
|
||||||
mov eax, [label];
|
|
||||||
mov [context.Eip], eax;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
asm(
|
|
||||||
"label:\n\t"
|
|
||||||
"mov{l %%ebp, %[cEbp] | %[cEbp], ebp};\n\t"
|
|
||||||
"mov{l %%esp, %[cEsp] | %[cEsp], esp};\n\t"
|
|
||||||
"mov{l $label, %%eax | eax, OFFSET label};\n\t"
|
|
||||||
"mov{l %%eax, %[cEip] | %[cEip], eax};\n\t"
|
|
||||||
: [cEbp] "=r" (context.Ebp),
|
|
||||||
[cEsp] "=r" (context.Esp),
|
|
||||||
[cEip] "=r" (context.Eip)
|
|
||||||
);
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
RtlCaptureContext(&context);
|
|
||||||
#endif
|
|
||||||
// Setup current frame
|
|
||||||
STACKFRAME64 frame;
|
|
||||||
ZeroMemory(&frame, sizeof(STACKFRAME64));
|
|
||||||
DWORD machine_type;
|
|
||||||
#if defined(_M_IX86) || defined(__i386__)
|
|
||||||
machine_type = IMAGE_FILE_MACHINE_I386;
|
|
||||||
frame.AddrPC.Offset = context.Eip;
|
|
||||||
frame.AddrPC.Mode = AddrModeFlat;
|
|
||||||
frame.AddrFrame.Offset = context.Ebp;
|
|
||||||
frame.AddrFrame.Mode = AddrModeFlat;
|
|
||||||
frame.AddrStack.Offset = context.Esp;
|
|
||||||
frame.AddrStack.Mode = AddrModeFlat;
|
|
||||||
#elif defined(_M_X64) || defined(__x86_64__)
|
|
||||||
machine_type = IMAGE_FILE_MACHINE_AMD64;
|
|
||||||
frame.AddrPC.Offset = context.Rip;
|
|
||||||
frame.AddrPC.Mode = AddrModeFlat;
|
|
||||||
frame.AddrFrame.Offset = context.Rsp;
|
|
||||||
frame.AddrFrame.Mode = AddrModeFlat;
|
|
||||||
frame.AddrStack.Offset = context.Rsp;
|
|
||||||
frame.AddrStack.Mode = AddrModeFlat;
|
|
||||||
#elif defined(_M_IA64) || defined(__aarch64__)
|
|
||||||
machine_type = IMAGE_FILE_MACHINE_IA64;
|
|
||||||
frame.AddrPC.Offset = context.StIIP;
|
|
||||||
frame.AddrPC.Mode = AddrModeFlat;
|
|
||||||
frame.AddrFrame.Offset = context.IntSp;
|
|
||||||
frame.AddrFrame.Mode = AddrModeFlat;
|
|
||||||
frame.AddrBStore.Offset= context.RsBSP;
|
|
||||||
frame.AddrBStore.Mode = AddrModeFlat;
|
|
||||||
frame.AddrStack.Offset = context.IntSp;
|
|
||||||
frame.AddrStack.Mode = AddrModeFlat;
|
|
||||||
#else
|
|
||||||
#error "Cpptrace: StackWalk64 not supported for this platform yet"
|
|
||||||
#endif
|
|
||||||
ZeroMemory(&context, sizeof(CONTEXT));
|
|
||||||
StackWalk64(
|
|
||||||
machine_type,
|
|
||||||
proc,
|
|
||||||
thread,
|
|
||||||
&frame,
|
|
||||||
machine_type == IMAGE_FILE_MACHINE_I386 ? NULL : &context,
|
|
||||||
NULL,
|
|
||||||
SymFunctionTableAccess64,
|
|
||||||
SymGetModuleBase64,
|
|
||||||
NULL
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,32 +0,0 @@
|
|||||||
# Init @ variables before doing anything else
|
|
||||||
@PACKAGE_INIT@
|
|
||||||
|
|
||||||
# Dependencies
|
|
||||||
if(@CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF@)
|
|
||||||
include(CMakeFindDependencyMacro)
|
|
||||||
# we don't go the Findzstd.cmake route on vcpkg
|
|
||||||
if(@CPPTRACE_VCPKG@)
|
|
||||||
find_dependency(zstd CONFIG REQUIRED)
|
|
||||||
else()
|
|
||||||
set(CMAKE_MODULE_PATH_OLD "${CMAKE_MODULE_PATH}")
|
|
||||||
set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH};${CMAKE_CURRENT_LIST_DIR}")
|
|
||||||
find_dependency(zstd)
|
|
||||||
set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH_OLD}")
|
|
||||||
unset(CMAKE_MODULE_PATH_OLD)
|
|
||||||
endif()
|
|
||||||
if(NOT @CPPTRACE_FIND_LIBDWARF_WITH_PKGCONFIG@)
|
|
||||||
find_dependency(libdwarf REQUIRED)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# We cannot modify an existing IMPORT target
|
|
||||||
if(NOT TARGET cpptrace::cpptrace)
|
|
||||||
|
|
||||||
# import targets
|
|
||||||
include("${CMAKE_CURRENT_LIST_DIR}/@package_name@-targets.cmake")
|
|
||||||
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(@CPPTRACE_STATIC_DEFINE@)
|
|
||||||
target_compile_definitions(cpptrace::cpptrace INTERFACE CPPTRACE_STATIC_DEFINE)
|
|
||||||
endif()
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
#ifndef CPPTRACE_VERSION_HPP
|
|
||||||
#define CPPTRACE_VERSION_HPP
|
|
||||||
|
|
||||||
#define CPPTRACE_VERSION_MAJOR @CPPTRACE_VERSION_MAJOR@
|
|
||||||
#define CPPTRACE_VERSION_MINOR @CPPTRACE_VERSION_MINOR@
|
|
||||||
#define CPPTRACE_VERSION_PATCH @CPPTRACE_VERSION_PATCH@
|
|
||||||
|
|
||||||
#define CPPTRACE_TO_VERSION(MAJOR, MINOR, PATCH) ((MAJOR) * 10000 + (MINOR) * 100 + (PATCH))
|
|
||||||
#define CPPTRACE_VERSION CPPTRACE_TO_VERSION(CPPTRACE_VERSION_MAJOR, CPPTRACE_VERSION_MINOR, CPPTRACE_VERSION_PATCH)
|
|
||||||
|
|
||||||
#endif
|
|
||||||
173
docs/c-api.md
173
docs/c-api.md
@ -1,173 +0,0 @@
|
|||||||
# ctrace <!-- omit in toc -->
|
|
||||||
|
|
||||||
Cpptrace provides a C API under the name ctrace, documented below.
|
|
||||||
|
|
||||||
## Table of Contents <!-- omit in toc -->
|
|
||||||
|
|
||||||
- [Documentation](#documentation)
|
|
||||||
- [Stack Traces](#stack-traces)
|
|
||||||
- [Object Traces](#object-traces)
|
|
||||||
- [Raw Traces](#raw-traces)
|
|
||||||
- [Utilities](#utilities)
|
|
||||||
- [Utility types](#utility-types)
|
|
||||||
- [Configuration](#configuration)
|
|
||||||
- [Signal-Safe Tracing](#signal-safe-tracing)
|
|
||||||
|
|
||||||
## Documentation
|
|
||||||
|
|
||||||
All ctrace declarations are in the `ctrace.h` header:
|
|
||||||
|
|
||||||
```c
|
|
||||||
#include <ctrace/ctrace.h>
|
|
||||||
```
|
|
||||||
|
|
||||||
### Stack Traces
|
|
||||||
|
|
||||||
Generate stack traces with `ctrace_generate_trace()`. Often `skip = 0` and `max_depth = SIZE_MAX` is what you want for
|
|
||||||
the parameters.
|
|
||||||
|
|
||||||
`ctrace_stacktrace_to_string` and `ctrace_print_stacktrace` can then be used for output.
|
|
||||||
|
|
||||||
`ctrace_free_stacktrace` must be called when you are done with the trace.
|
|
||||||
|
|
||||||
```c
|
|
||||||
typedef struct ctrace_stacktrace ctrace_stacktrace;
|
|
||||||
|
|
||||||
struct ctrace_stacktrace_frame {
|
|
||||||
ctrace_frame_ptr raw_address;
|
|
||||||
ctrace_frame_ptr object_address;
|
|
||||||
uint32_t line;
|
|
||||||
uint32_t column;
|
|
||||||
const char* filename;
|
|
||||||
const char* symbol;
|
|
||||||
ctrace_bool is_inline;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ctrace_stacktrace {
|
|
||||||
ctrace_stacktrace_frame* frames;
|
|
||||||
size_t count;
|
|
||||||
};
|
|
||||||
|
|
||||||
ctrace_stacktrace ctrace_generate_trace(size_t skip, size_t max_depth);
|
|
||||||
ctrace_owning_string ctrace_stacktrace_to_string(const ctrace_stacktrace* trace, ctrace_bool use_color);
|
|
||||||
void ctrace_print_stacktrace(const ctrace_stacktrace* trace, FILE* to, ctrace_bool use_color);
|
|
||||||
void ctrace_free_stacktrace(ctrace_stacktrace* trace);
|
|
||||||
|
|
||||||
// object_address is stored but if the object_path is needed this can be used
|
|
||||||
ctrace_object_frame ctrace_get_object_info(const ctrace_stacktrace_frame* frame);
|
|
||||||
```
|
|
||||||
|
|
||||||
### Object Traces
|
|
||||||
|
|
||||||
Object traces contain the most basic information needed to construct a stack trace outside the currently running
|
|
||||||
executable. It contains the raw address, the address in the binary (ASLR and the object file's memory space and whatnot
|
|
||||||
is resolved), and the path to the object the instruction pointer is located in.
|
|
||||||
|
|
||||||
`ctrace_free_object_trace` must be called when you are done with the trace.
|
|
||||||
|
|
||||||
```c
|
|
||||||
typedef struct ctrace_object_trace ctrace_object_trace;
|
|
||||||
|
|
||||||
struct ctrace_object_frame {
|
|
||||||
ctrace_frame_ptr raw_address;
|
|
||||||
ctrace_frame_ptr obj_address;
|
|
||||||
const char* obj_path;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ctrace_object_trace {
|
|
||||||
ctrace_object_frame* frames;
|
|
||||||
size_t count;
|
|
||||||
};
|
|
||||||
|
|
||||||
ctrace_object_trace ctrace_generate_object_trace(size_t skip, size_t max_depth);
|
|
||||||
ctrace_stacktrace ctrace_resolve_object_trace(const ctrace_object_trace* trace);
|
|
||||||
void ctrace_free_object_trace(ctrace_object_trace* trace);
|
|
||||||
```
|
|
||||||
|
|
||||||
### Raw Traces
|
|
||||||
|
|
||||||
Raw traces are arrays of program counters. These are ideal for fast and cheap traces you want to resolve later.
|
|
||||||
|
|
||||||
Note it is important executables and shared libraries in memory aren't somehow unmapped otherwise libdl calls (and
|
|
||||||
`GetModuleFileName` in windows) will fail to figure out where the program counter corresponds to.
|
|
||||||
|
|
||||||
`ctrace_free_raw_trace` must be called when you are done with the trace.
|
|
||||||
|
|
||||||
```c
|
|
||||||
typedef struct ctrace_raw_trace ctrace_raw_trace;
|
|
||||||
|
|
||||||
ctrace_raw_trace ctrace_generate_raw_trace(size_t skip, size_t max_depth);
|
|
||||||
ctrace_stacktrace ctrace_resolve_raw_trace(const ctrace_raw_trace* trace);
|
|
||||||
void ctrace_free_raw_trace(ctrace_raw_trace* trace);
|
|
||||||
```
|
|
||||||
|
|
||||||
### Utilities
|
|
||||||
|
|
||||||
`ctrace_demangle`: Helper function for name demangling
|
|
||||||
|
|
||||||
`ctrace_stdin_fileno`, `ctrace_stderr_fileno`, `ctrace_stdout_fileno`: Returns the appropriate file descriptor for the
|
|
||||||
respective standard stream.
|
|
||||||
|
|
||||||
`ctrace_isatty`: Checks if a file descriptor corresponds to a tty device.
|
|
||||||
|
|
||||||
```c
|
|
||||||
ctrace_owning_string ctrace_demangle(const char* mangled);
|
|
||||||
int ctrace_stdin_fileno(void);
|
|
||||||
int ctrace_stderr_fileno(void);
|
|
||||||
int ctrace_stdout_fileno(void);
|
|
||||||
ctrace_bool ctrace_isatty(int fd);
|
|
||||||
```
|
|
||||||
|
|
||||||
### Utility types
|
|
||||||
|
|
||||||
For ABI reasons `ctrace_bool`s are used for bools. `ctrace_owning_string` is a wrapper type which indicates that a
|
|
||||||
string is owned and must be freed.
|
|
||||||
|
|
||||||
```c
|
|
||||||
typedef int8_t ctrace_bool;
|
|
||||||
typedef struct {
|
|
||||||
const char* data;
|
|
||||||
} ctrace_owning_string;
|
|
||||||
ctrace_owning_string ctrace_generate_owning_string(const char* raw_string);
|
|
||||||
void ctrace_free_owning_string(ctrace_owning_string* string);
|
|
||||||
```
|
|
||||||
|
|
||||||
### Configuration
|
|
||||||
|
|
||||||
`experimental::set_cache_mode`: Control time-memory tradeoffs within the library. By default speed is prioritized. If
|
|
||||||
using this function, set the cache mode at the very start of your program before any traces are performed. Note: This
|
|
||||||
API is not set in stone yet and could change in the future.
|
|
||||||
|
|
||||||
`ctrace_enable_inlined_call_resolution`: Configure whether the library will attempt to resolve inlined call information for
|
|
||||||
release builds. Default is true.
|
|
||||||
|
|
||||||
```c
|
|
||||||
typedef enum {
|
|
||||||
/* Only minimal lookup tables */
|
|
||||||
ctrace_prioritize_memory = 0,
|
|
||||||
/* Build lookup tables but don't keep them around between trace calls */
|
|
||||||
ctrace_hybrid = 1,
|
|
||||||
/* Build lookup tables as needed */
|
|
||||||
ctrace_prioritize_speed = 2
|
|
||||||
} ctrace_cache_mode;
|
|
||||||
void ctrace_set_cache_mode(ctrace_cache_mode mode);
|
|
||||||
void ctrace_enable_inlined_call_resolution(ctrace_bool enable);
|
|
||||||
```
|
|
||||||
|
|
||||||
### Signal-Safe Tracing
|
|
||||||
|
|
||||||
For more details on the signal-safe tracing interface please refer to the README and the
|
|
||||||
[signal-safe-tracing.md](signal-safe-tracing.md) guide.
|
|
||||||
|
|
||||||
```c
|
|
||||||
typedef struct ctrace_safe_object_frame ctrace_safe_object_frame;
|
|
||||||
struct ctrace_safe_object_frame {
|
|
||||||
ctrace_frame_ptr raw_address;
|
|
||||||
ctrace_frame_ptr relative_obj_address;
|
|
||||||
char object_path[CTRACE_PATH_MAX + 1];
|
|
||||||
};
|
|
||||||
size_t ctrace_safe_generate_raw_trace(ctrace_frame_ptr* buffer, size_t size, size_t skip, size_t max_depth);
|
|
||||||
void ctrace_get_safe_object_frame(ctrace_frame_ptr address, ctrace_safe_object_frame* out);
|
|
||||||
ctrace_bool ctrace_can_signal_safe_unwind();
|
|
||||||
ctrace_bool ctrace_can_get_safe_object_frame();
|
|
||||||
```
|
|
||||||
@ -1,250 +0,0 @@
|
|||||||
# Signal-Safe Stack Tracing <!-- omit in toc -->
|
|
||||||
|
|
||||||
- [Overview](#overview)
|
|
||||||
- [Big-Picture](#big-picture)
|
|
||||||
- [API](#api)
|
|
||||||
- [Strategy](#strategy)
|
|
||||||
- [Technical Requirements](#technical-requirements)
|
|
||||||
- [Signal-Safe Tracing With `fork()` + `exec()`](#signal-safe-tracing-with-fork--exec)
|
|
||||||
- [In the main program](#in-the-main-program)
|
|
||||||
- [In the tracer program](#in-the-tracer-program)
|
|
||||||
|
|
||||||
# Overview
|
|
||||||
|
|
||||||
Stack traces from signal handlers can provide very helpful information for debugging application crashes, e.g. from
|
|
||||||
SIGSEGV or SIGTRAP handlers. Signal handlers are really restrictive environments as your application could be
|
|
||||||
interrupted by a signal at any point, including in the middle of malloc or buffered IO or while holding a lock.
|
|
||||||
Doing a stack trace in a signal handler is possible but it requires a lot of care. This is difficult to do correctly
|
|
||||||
and most examples online do this incorrectly.
|
|
||||||
|
|
||||||
It is not possible to resolve debug symbols safely in the process from a signal handler without heroic effort. In order
|
|
||||||
to produce a full trace there are three options:
|
|
||||||
1. Carefully save the object trace information to be resolved at a later time outside the signal handler
|
|
||||||
2. Write the object trace information to a file to be resolved later
|
|
||||||
3. Spawn a new process, communicate object trace information to that process, and have that process do the trace
|
|
||||||
resolution
|
|
||||||
|
|
||||||
For traces on segfaults, e.g., only options 2 and 3 are viable. The this guide will go over approach 3 and the cpptrace
|
|
||||||
safe API.
|
|
||||||
|
|
||||||
# Big-Picture
|
|
||||||
|
|
||||||
In order to do this full process safely the way to go is collecting basic information in the signal
|
|
||||||
handler and then either resolving later or handing that information to another process to resolve.
|
|
||||||
|
|
||||||
It's not as simple as calling `cpptrace::generate_trace().print()` but this is what is needed to
|
|
||||||
really do this safely as far as I can tell.
|
|
||||||
|
|
||||||
FAQ: What's the worst that could happen if you call `cpptrace::generate_trace().print()` from a
|
|
||||||
signal handler? In many cases you might be able to get away with it but you risk deadlocking or
|
|
||||||
memory corruption.
|
|
||||||
|
|
||||||
> [!IMPORTANT]
|
|
||||||
> Currently signal-safe stack unwinding is only possible with `libunwind`, more details later.
|
|
||||||
|
|
||||||
> [!IMPORTANT]
|
|
||||||
> `_dl_find_object` is required for signal-safe stack tracing. This is a relatively recent addition to glibc, added in
|
|
||||||
> glibc 2.35.
|
|
||||||
|
|
||||||
> [!CAUTION]
|
|
||||||
> Calls to shared objects can be lazy-loaded where the first call to the shared object invokes non-signal-safe functions
|
|
||||||
> such as `malloc()`. Because of this, the signal safe api must be "warmed up" ahead of a signal handler.
|
|
||||||
|
|
||||||
# API
|
|
||||||
|
|
||||||
Cpptrace provides APIs for generating raw trace information safely and then also safely resolving
|
|
||||||
those raw pointers to the most minimal object information needed to resolve later.
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
namespace cpptrace {
|
|
||||||
// signal-safe
|
|
||||||
std::size_t safe_generate_raw_trace(frame_ptr* buffer, std::size_t size, std::size_t skip = 0);
|
|
||||||
// signal-safe
|
|
||||||
std::size_t safe_generate_raw_trace(frame_ptr* buffer, std::size_t size, std::size_t skip, std::size_t max_depth);
|
|
||||||
|
|
||||||
struct safe_object_frame {
|
|
||||||
frame_ptr raw_address;
|
|
||||||
frame_ptr address_relative_to_object_start;
|
|
||||||
char object_path[CPPTRACE_PATH_MAX + 1];
|
|
||||||
object_frame resolve() const; // To be called outside a signal handler. Not signal safe.
|
|
||||||
};
|
|
||||||
|
|
||||||
// signal-safe
|
|
||||||
void get_safe_object_frame(frame_ptr address, safe_object_frame* out);
|
|
||||||
// signal-safe
|
|
||||||
bool can_signal_safe_unwind();
|
|
||||||
bool can_get_safe_object_frame();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
`safe_generate_raw_trace` produces instruction pointers found while unwinding the stack. These addresses are sufficient
|
|
||||||
information to resolve a stack trace in the currently running process after a signal handler exits, but they aren't
|
|
||||||
sufficient for resolving outside of the currently running process, unless there is no position-independent code or
|
|
||||||
shared-library code.
|
|
||||||
|
|
||||||
To resolve outside the current process `safe_object_frame` information is needed. This contains the path to the
|
|
||||||
object where the address is located as well as the address before address randomization.
|
|
||||||
|
|
||||||
# Strategy
|
|
||||||
|
|
||||||
Signal-safe tracing can be done three ways:
|
|
||||||
- In a signal handler, call `safe_generate_raw_trace` and then outside a signal handler
|
|
||||||
construct a `cpptrace:raw_trace` and resolve.
|
|
||||||
- In a signal handler, call `safe_generate_raw_trace`, then write `cpptrace::safe_object_frame`
|
|
||||||
information to a file to be resolved later.
|
|
||||||
- In a signal handler, call `safe_generate_raw_trace`, `fork()` and `exec()` a process to handle the
|
|
||||||
resolution, pass `cpptrace::safe_object_frame` information to that child through a pipe, and
|
|
||||||
wait for the child to exit.
|
|
||||||
|
|
||||||
It's not as simple as calling `cpptrace::generate_trace().print()`, I know, but these are truly the
|
|
||||||
only ways to do this safely as far as I can tell.
|
|
||||||
|
|
||||||
# Technical Requirements
|
|
||||||
|
|
||||||
**Note:** Not all back-ends and platforms support these interfaces. If signal-safe unwinding isn't supported
|
|
||||||
`safe_generate_raw_trace` will just produce an empty trace and if object information can't be resolved in a signal-safe
|
|
||||||
way then `get_safe_object_frame` will not populate fields beyond the `raw_address`.
|
|
||||||
|
|
||||||
`cpptrace::can_signal_safe_unwind` and `cpptrace::can_get_safe_object_frame` can be used to check for safe tracing
|
|
||||||
support.
|
|
||||||
|
|
||||||
Currently the only back-end that can unwind safely is libunwind. Currently, the only way I know to get `dladdr`'s
|
|
||||||
information in a signal-safe manner is `_dl_find_object`, which doesn't exist on macos (or windows of course). If anyone
|
|
||||||
knows ways to do these safely on other platforms, I'd be much appreciative.
|
|
||||||
|
|
||||||
# Signal-Safe Tracing With `fork()` + `exec()`
|
|
||||||
|
|
||||||
Of the three strategies, `fork()` + `exec()`, is the most technically involved and the only way to resolve while the
|
|
||||||
signal handler is running. I think it's worthwhile to do a deep-dive into how to do this.
|
|
||||||
|
|
||||||
In the source code, [`signal_demo.cpp`](../test/signal_demo.cpp) and [`signal_tracer.cpp`](../test/signal_tracer.cpp)
|
|
||||||
provide a working example for what is described here.
|
|
||||||
|
|
||||||
## In the main program
|
|
||||||
|
|
||||||
The main program handles most of the complexity for tracing from signal handlers:
|
|
||||||
- Collecting a raw trace
|
|
||||||
- Spawning a child process
|
|
||||||
- Resolving raw frame pointers to minimal object frames
|
|
||||||
- Sending that info to the other process
|
|
||||||
|
|
||||||
Also note: A warmup for the library is done in main.
|
|
||||||
|
|
||||||
A basic implementation is as follows:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
#include <sys/wait.h>
|
|
||||||
#include <cstring>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <cpptrace/cpptrace.hpp>
|
|
||||||
|
|
||||||
// This is just a utility I like, it makes the pipe API more expressive.
|
|
||||||
struct pipe_t {
|
|
||||||
union {
|
|
||||||
struct {
|
|
||||||
int read_end;
|
|
||||||
int write_end;
|
|
||||||
};
|
|
||||||
int data[2];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
void do_signal_safe_trace(cpptrace::frame_ptr* buffer, std::size_t count) {
|
|
||||||
// Setup pipe and spawn child
|
|
||||||
pipe_t input_pipe;
|
|
||||||
pipe(input_pipe.data);
|
|
||||||
const pid_t pid = fork();
|
|
||||||
if(pid == -1) {
|
|
||||||
const char* fork_failure_message = "fork() failed\n";
|
|
||||||
write(STDERR_FILENO, fork_failure_message, strlen(fork_failure_message));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(pid == 0) { // child
|
|
||||||
dup2(input_pipe.read_end, STDIN_FILENO);
|
|
||||||
close(input_pipe.read_end);
|
|
||||||
close(input_pipe.write_end);
|
|
||||||
execl("signal_tracer", "signal_tracer", nullptr);
|
|
||||||
const char* exec_failure_message = "exec(signal_tracer) failed: Make sure the signal_tracer executable is in "
|
|
||||||
"the current working directory and the binary's permissions are correct.\n";
|
|
||||||
write(STDERR_FILENO, exec_failure_message, strlen(exec_failure_message));
|
|
||||||
_exit(1);
|
|
||||||
}
|
|
||||||
// Resolve to safe_object_frames and write those to the pipe
|
|
||||||
for(std::size_t i = 0; i < count; i++) {
|
|
||||||
cpptrace::safe_object_frame frame;
|
|
||||||
cpptrace::get_safe_object_frame(buffer[i], &frame);
|
|
||||||
write(input_pipe.write_end, &frame, sizeof(frame));
|
|
||||||
}
|
|
||||||
close(input_pipe.read_end);
|
|
||||||
close(input_pipe.write_end);
|
|
||||||
// Wait for child
|
|
||||||
waitpid(pid, nullptr, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void handler(int signo, siginfo_t* info, void* context) {
|
|
||||||
// Print basic message
|
|
||||||
const char* message = "SIGSEGV occurred:\n";
|
|
||||||
write(STDERR_FILENO, message, strlen(message));
|
|
||||||
// Generate trace
|
|
||||||
constexpr std::size_t N = 100;
|
|
||||||
cpptrace::frame_ptr buffer[N];
|
|
||||||
std::size_t count = cpptrace::safe_generate_raw_trace(buffer, N);
|
|
||||||
do_signal_safe_trace(buffer, count);
|
|
||||||
// Up to you if you want to exit or continue or whatever
|
|
||||||
_exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void warmup_cpptrace() {
|
|
||||||
// This is done for any dynamic-loading shenanigans
|
|
||||||
cpptrace::frame_ptr buffer[10];
|
|
||||||
std::size_t count = cpptrace::safe_generate_raw_trace(buffer, 10);
|
|
||||||
cpptrace::safe_object_frame frame;
|
|
||||||
cpptrace::get_safe_object_frame(buffer[0], &frame);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
warmup_cpptrace();
|
|
||||||
// Setup signal handler
|
|
||||||
struct sigaction action = { 0 };
|
|
||||||
action.sa_flags = 0;
|
|
||||||
action.sa_sigaction = &handler;
|
|
||||||
if (sigaction(SIGSEGV, &action, NULL) == -1) {
|
|
||||||
perror("sigaction");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## In the tracer program
|
|
||||||
|
|
||||||
The tracer program is quite simple. It just has to read `cpptrace::safe_object_frame`s from the pipe, resolve to
|
|
||||||
`cpptrace::object_frame`s, and resolve an `object_trace`.
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
#include <cstdio>
|
|
||||||
#include <iostream>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <cpptrace/cpptrace.hpp>
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
cpptrace::object_trace trace;
|
|
||||||
while(true) {
|
|
||||||
cpptrace::safe_object_frame frame;
|
|
||||||
// fread used over read because a read() from a pipe might not read the full frame
|
|
||||||
std::size_t res = fread(&frame, sizeof(frame), 1, stdin);
|
|
||||||
if(res == 0) {
|
|
||||||
break;
|
|
||||||
} else if(res != 1) {
|
|
||||||
std::cerr<<"Something went wrong while reading from the pipe"<<res<<" "<<std::endl;
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
trace.frames.push_back(frame.resolve());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
trace.resolve().print();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
@ -1,245 +0,0 @@
|
|||||||
#ifndef CPPTRACE_BASIC_HPP
|
|
||||||
#define CPPTRACE_BASIC_HPP
|
|
||||||
|
|
||||||
#include <cpptrace/forward.hpp>
|
|
||||||
|
|
||||||
#include <limits>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include <iosfwd>
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#define CPPTRACE_EXPORT_ATTR __declspec(dllexport)
|
|
||||||
#define CPPTRACE_IMPORT_ATTR __declspec(dllimport)
|
|
||||||
#else
|
|
||||||
#define CPPTRACE_EXPORT_ATTR __attribute__((visibility("default")))
|
|
||||||
#define CPPTRACE_IMPORT_ATTR __attribute__((visibility("default")))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef CPPTRACE_STATIC_DEFINE
|
|
||||||
# define CPPTRACE_EXPORT
|
|
||||||
# define CPPTRACE_NO_EXPORT
|
|
||||||
#else
|
|
||||||
# ifndef CPPTRACE_EXPORT
|
|
||||||
# ifdef cpptrace_lib_EXPORTS
|
|
||||||
/* We are building this library */
|
|
||||||
# define CPPTRACE_EXPORT CPPTRACE_EXPORT_ATTR
|
|
||||||
# else
|
|
||||||
/* We are using this library */
|
|
||||||
# define CPPTRACE_EXPORT CPPTRACE_IMPORT_ATTR
|
|
||||||
# endif
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if __cplusplus >= 201703L
|
|
||||||
#define CONSTEXPR_SINCE_CPP17 constexpr
|
|
||||||
#else
|
|
||||||
#define CONSTEXPR_SINCE_CPP17
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
#define CPPTRACE_FORCE_NO_INLINE __declspec(noinline)
|
|
||||||
#else
|
|
||||||
#define CPPTRACE_FORCE_NO_INLINE __attribute__((noinline))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
#pragma warning(push)
|
|
||||||
// warning C4251: using non-dll-exported type in dll-exported type, firing on std::vector<frame_ptr> and others for some
|
|
||||||
// reason
|
|
||||||
// 4275 is the same thing but for base classes
|
|
||||||
#pragma warning(disable: 4251; disable: 4275)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace cpptrace {
|
|
||||||
struct CPPTRACE_EXPORT raw_trace {
|
|
||||||
std::vector<frame_ptr> frames;
|
|
||||||
static raw_trace current(std::size_t skip = 0);
|
|
||||||
static raw_trace current(std::size_t skip, std::size_t max_depth);
|
|
||||||
object_trace resolve_object_trace() const;
|
|
||||||
stacktrace resolve() const;
|
|
||||||
void clear();
|
|
||||||
bool empty() const noexcept;
|
|
||||||
|
|
||||||
using iterator = std::vector<frame_ptr>::iterator;
|
|
||||||
using const_iterator = std::vector<frame_ptr>::const_iterator;
|
|
||||||
inline iterator begin() noexcept { return frames.begin(); }
|
|
||||||
inline iterator end() noexcept { return frames.end(); }
|
|
||||||
inline const_iterator begin() const noexcept { return frames.begin(); }
|
|
||||||
inline const_iterator end() const noexcept { return frames.end(); }
|
|
||||||
inline const_iterator cbegin() const noexcept { return frames.cbegin(); }
|
|
||||||
inline const_iterator cend() const noexcept { return frames.cend(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CPPTRACE_EXPORT object_frame {
|
|
||||||
frame_ptr raw_address;
|
|
||||||
frame_ptr object_address;
|
|
||||||
std::string object_path;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CPPTRACE_EXPORT object_trace {
|
|
||||||
std::vector<object_frame> frames;
|
|
||||||
static object_trace current(std::size_t skip = 0);
|
|
||||||
static object_trace current(std::size_t skip, std::size_t max_depth);
|
|
||||||
stacktrace resolve() const;
|
|
||||||
void clear();
|
|
||||||
bool empty() const noexcept;
|
|
||||||
|
|
||||||
using iterator = std::vector<object_frame>::iterator;
|
|
||||||
using const_iterator = std::vector<object_frame>::const_iterator;
|
|
||||||
inline iterator begin() noexcept { return frames.begin(); }
|
|
||||||
inline iterator end() noexcept { return frames.end(); }
|
|
||||||
inline const_iterator begin() const noexcept { return frames.begin(); }
|
|
||||||
inline const_iterator end() const noexcept { return frames.end(); }
|
|
||||||
inline const_iterator cbegin() const noexcept { return frames.cbegin(); }
|
|
||||||
inline const_iterator cend() const noexcept { return frames.cend(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
// This represents a nullable integer type
|
|
||||||
// The max value of the type is used as a sentinel
|
|
||||||
// This is used over std::optional because the library is C++11 and also std::optional is a bit heavy-duty for this
|
|
||||||
// use.
|
|
||||||
template<typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
|
|
||||||
struct nullable {
|
|
||||||
T raw_value = null_value();
|
|
||||||
constexpr nullable() noexcept = default;
|
|
||||||
constexpr nullable(T value) noexcept : raw_value(value) {}
|
|
||||||
CONSTEXPR_SINCE_CPP17 nullable& operator=(T value) noexcept {
|
|
||||||
raw_value = value;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
constexpr bool has_value() const noexcept {
|
|
||||||
return raw_value != null_value();
|
|
||||||
}
|
|
||||||
CONSTEXPR_SINCE_CPP17 T& value() noexcept {
|
|
||||||
return raw_value;
|
|
||||||
}
|
|
||||||
constexpr const T& value() const noexcept {
|
|
||||||
return raw_value;
|
|
||||||
}
|
|
||||||
constexpr T value_or(T alternative) const noexcept {
|
|
||||||
return has_value() ? raw_value : alternative;
|
|
||||||
}
|
|
||||||
CONSTEXPR_SINCE_CPP17 void swap(nullable& other) noexcept {
|
|
||||||
std::swap(raw_value, other.raw_value);
|
|
||||||
}
|
|
||||||
CONSTEXPR_SINCE_CPP17 void reset() noexcept {
|
|
||||||
raw_value = (std::numeric_limits<T>::max)();
|
|
||||||
}
|
|
||||||
constexpr bool operator==(const nullable& other) const noexcept {
|
|
||||||
return raw_value == other.raw_value;
|
|
||||||
}
|
|
||||||
constexpr bool operator!=(const nullable& other) const noexcept {
|
|
||||||
return raw_value != other.raw_value;
|
|
||||||
}
|
|
||||||
constexpr static T null_value() noexcept {
|
|
||||||
return (std::numeric_limits<T>::max)();
|
|
||||||
}
|
|
||||||
constexpr static nullable null() noexcept {
|
|
||||||
return { null_value() };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CPPTRACE_EXPORT stacktrace_frame {
|
|
||||||
frame_ptr raw_address;
|
|
||||||
frame_ptr object_address;
|
|
||||||
nullable<std::uint32_t> line;
|
|
||||||
nullable<std::uint32_t> column;
|
|
||||||
std::string filename;
|
|
||||||
std::string symbol;
|
|
||||||
bool is_inline;
|
|
||||||
|
|
||||||
bool operator==(const stacktrace_frame& other) const {
|
|
||||||
return raw_address == other.raw_address
|
|
||||||
&& object_address == other.object_address
|
|
||||||
&& line == other.line
|
|
||||||
&& column == other.column
|
|
||||||
&& filename == other.filename
|
|
||||||
&& symbol == other.symbol;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator!=(const stacktrace_frame& other) const {
|
|
||||||
return !operator==(other);
|
|
||||||
}
|
|
||||||
|
|
||||||
object_frame get_object_info() const;
|
|
||||||
|
|
||||||
std::string to_string() const;
|
|
||||||
std::string to_string(bool color) const;
|
|
||||||
friend std::ostream& operator<<(std::ostream& stream, const stacktrace_frame& frame);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CPPTRACE_EXPORT stacktrace {
|
|
||||||
std::vector<stacktrace_frame> frames;
|
|
||||||
static stacktrace current(std::size_t skip = 0);
|
|
||||||
static stacktrace current(std::size_t skip, std::size_t max_depth);
|
|
||||||
void print() const;
|
|
||||||
void print(std::ostream& stream) const;
|
|
||||||
void print(std::ostream& stream, bool color) const;
|
|
||||||
void print_with_snippets() const;
|
|
||||||
void print_with_snippets(std::ostream& stream) const;
|
|
||||||
void print_with_snippets(std::ostream& stream, bool color) const;
|
|
||||||
void clear();
|
|
||||||
bool empty() const noexcept;
|
|
||||||
std::string to_string(bool color = false) const;
|
|
||||||
friend std::ostream& operator<<(std::ostream& stream, const stacktrace& trace);
|
|
||||||
|
|
||||||
using iterator = std::vector<stacktrace_frame>::iterator;
|
|
||||||
using const_iterator = std::vector<stacktrace_frame>::const_iterator;
|
|
||||||
inline iterator begin() noexcept { return frames.begin(); }
|
|
||||||
inline iterator end() noexcept { return frames.end(); }
|
|
||||||
inline const_iterator begin() const noexcept { return frames.begin(); }
|
|
||||||
inline const_iterator end() const noexcept { return frames.end(); }
|
|
||||||
inline const_iterator cbegin() const noexcept { return frames.cbegin(); }
|
|
||||||
inline const_iterator cend() const noexcept { return frames.cend(); }
|
|
||||||
private:
|
|
||||||
friend void print_terminate_trace();
|
|
||||||
};
|
|
||||||
|
|
||||||
CPPTRACE_EXPORT raw_trace generate_raw_trace(std::size_t skip = 0);
|
|
||||||
CPPTRACE_EXPORT raw_trace generate_raw_trace(std::size_t skip, std::size_t max_depth);
|
|
||||||
CPPTRACE_EXPORT object_trace generate_object_trace(std::size_t skip = 0);
|
|
||||||
CPPTRACE_EXPORT object_trace generate_object_trace(std::size_t skip, std::size_t max_depth);
|
|
||||||
CPPTRACE_EXPORT stacktrace generate_trace(std::size_t skip = 0);
|
|
||||||
CPPTRACE_EXPORT stacktrace generate_trace(std::size_t skip, std::size_t max_depth);
|
|
||||||
|
|
||||||
// Path max isn't so simple, so I'm choosing 4096 which seems to encompass what all major OS's expect and should be
|
|
||||||
// fine in all reasonable cases.
|
|
||||||
// https://eklitzke.org/path-max-is-tricky
|
|
||||||
// https://insanecoding.blogspot.com/2007/11/pathmax-simply-isnt.html
|
|
||||||
#define CPPTRACE_PATH_MAX 4096
|
|
||||||
|
|
||||||
// safe tracing interface
|
|
||||||
// signal-safe
|
|
||||||
CPPTRACE_EXPORT std::size_t safe_generate_raw_trace(
|
|
||||||
frame_ptr* buffer,
|
|
||||||
std::size_t size,
|
|
||||||
std::size_t skip = 0
|
|
||||||
);
|
|
||||||
// signal-safe
|
|
||||||
CPPTRACE_EXPORT std::size_t safe_generate_raw_trace(
|
|
||||||
frame_ptr* buffer,
|
|
||||||
std::size_t size,
|
|
||||||
std::size_t skip,
|
|
||||||
std::size_t max_depth
|
|
||||||
);
|
|
||||||
struct CPPTRACE_EXPORT safe_object_frame {
|
|
||||||
frame_ptr raw_address;
|
|
||||||
// This ends up being the real object address. It was named at a time when I thought the object base address
|
|
||||||
// still needed to be added in
|
|
||||||
frame_ptr address_relative_to_object_start;
|
|
||||||
char object_path[CPPTRACE_PATH_MAX + 1];
|
|
||||||
// To be called outside a signal handler. Not signal safe.
|
|
||||||
object_frame resolve() const;
|
|
||||||
};
|
|
||||||
// signal-safe
|
|
||||||
CPPTRACE_EXPORT void get_safe_object_frame(frame_ptr address, safe_object_frame* out);
|
|
||||||
CPPTRACE_EXPORT bool can_signal_safe_unwind();
|
|
||||||
CPPTRACE_EXPORT bool can_get_safe_object_frame();
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
#pragma warning(pop)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@ -1,9 +1,20 @@
|
|||||||
#ifndef CPPTRACE_HPP
|
#ifndef CPPTRACE_HPP
|
||||||
#define CPPTRACE_HPP
|
#define CPPTRACE_HPP
|
||||||
|
|
||||||
#include <cpptrace/basic.hpp>
|
#include <cstdint>
|
||||||
#include <cpptrace/utils.hpp>
|
#include <string>
|
||||||
#include <cpptrace/exceptions.hpp>
|
#include <vector>
|
||||||
#include <cpptrace/io.hpp>
|
|
||||||
|
namespace cpptrace {
|
||||||
|
struct stacktrace_frame {
|
||||||
|
uintptr_t address;
|
||||||
|
std::uint_least32_t line;
|
||||||
|
std::uint_least32_t col;
|
||||||
|
std::string filename;
|
||||||
|
std::string symbol;
|
||||||
|
};
|
||||||
|
std::vector<stacktrace_frame> generate_trace(std::uint32_t skip = 0);
|
||||||
|
void print_trace(std::uint32_t skip = 0);
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -1,218 +0,0 @@
|
|||||||
#ifndef CPPTRACE_EXCEPTIONS_HPP
|
|
||||||
#define CPPTRACE_EXCEPTIONS_HPP
|
|
||||||
|
|
||||||
#include <cpptrace/basic.hpp>
|
|
||||||
|
|
||||||
#include <exception>
|
|
||||||
#include <system_error>
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
#pragma warning(push)
|
|
||||||
// warning C4251: using non-dll-exported type in dll-exported type, firing on std::vector<frame_ptr> and others for some
|
|
||||||
// reason
|
|
||||||
// 4275 is the same thing but for base classes
|
|
||||||
#pragma warning(disable: 4251; disable: 4275)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace cpptrace {
|
|
||||||
// tracing exceptions:
|
|
||||||
namespace detail {
|
|
||||||
// This is a helper utility, if the library weren't C++11 an std::variant would be used
|
|
||||||
class CPPTRACE_EXPORT lazy_trace_holder {
|
|
||||||
bool resolved;
|
|
||||||
union {
|
|
||||||
raw_trace trace;
|
|
||||||
stacktrace resolved_trace;
|
|
||||||
};
|
|
||||||
public:
|
|
||||||
// constructors
|
|
||||||
lazy_trace_holder() : resolved(false), trace() {}
|
|
||||||
explicit lazy_trace_holder(raw_trace&& _trace) : resolved(false), trace(std::move(_trace)) {}
|
|
||||||
explicit lazy_trace_holder(stacktrace&& _resolved_trace) : resolved(true), resolved_trace(std::move(_resolved_trace)) {}
|
|
||||||
// logistics
|
|
||||||
lazy_trace_holder(const lazy_trace_holder& other);
|
|
||||||
lazy_trace_holder(lazy_trace_holder&& other) noexcept;
|
|
||||||
lazy_trace_holder& operator=(const lazy_trace_holder& other);
|
|
||||||
lazy_trace_holder& operator=(lazy_trace_holder&& other) noexcept;
|
|
||||||
~lazy_trace_holder();
|
|
||||||
// access
|
|
||||||
const raw_trace& get_raw_trace() const;
|
|
||||||
stacktrace& get_resolved_trace();
|
|
||||||
const stacktrace& get_resolved_trace() const;
|
|
||||||
private:
|
|
||||||
void clear();
|
|
||||||
};
|
|
||||||
|
|
||||||
CPPTRACE_EXPORT raw_trace get_raw_trace_and_absorb(std::size_t skip, std::size_t max_depth);
|
|
||||||
CPPTRACE_EXPORT raw_trace get_raw_trace_and_absorb(std::size_t skip = 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Interface for a traced exception object
|
|
||||||
class CPPTRACE_EXPORT exception : public std::exception {
|
|
||||||
public:
|
|
||||||
const char* what() const noexcept override = 0;
|
|
||||||
virtual const char* message() const noexcept = 0;
|
|
||||||
virtual const stacktrace& trace() const noexcept = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Cpptrace traced exception object
|
|
||||||
// I hate to have to expose anything about implementation detail but the idea here is that
|
|
||||||
class CPPTRACE_EXPORT lazy_exception : public exception {
|
|
||||||
mutable detail::lazy_trace_holder trace_holder;
|
|
||||||
mutable std::string what_string;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit lazy_exception(
|
|
||||||
raw_trace&& trace = detail::get_raw_trace_and_absorb()
|
|
||||||
) : trace_holder(std::move(trace)) {}
|
|
||||||
// std::exception
|
|
||||||
const char* what() const noexcept override;
|
|
||||||
// cpptrace::exception
|
|
||||||
const char* message() const noexcept override;
|
|
||||||
const stacktrace& trace() const noexcept override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class CPPTRACE_EXPORT exception_with_message : public lazy_exception {
|
|
||||||
mutable std::string user_message;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit exception_with_message(
|
|
||||||
std::string&& message_arg,
|
|
||||||
raw_trace&& trace = detail::get_raw_trace_and_absorb()
|
|
||||||
) noexcept : lazy_exception(std::move(trace)), user_message(std::move(message_arg)) {}
|
|
||||||
|
|
||||||
const char* message() const noexcept override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class CPPTRACE_EXPORT logic_error : public exception_with_message {
|
|
||||||
public:
|
|
||||||
explicit logic_error(
|
|
||||||
std::string&& message_arg,
|
|
||||||
raw_trace&& trace = detail::get_raw_trace_and_absorb()
|
|
||||||
) noexcept
|
|
||||||
: exception_with_message(std::move(message_arg), std::move(trace)) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class CPPTRACE_EXPORT domain_error : public exception_with_message {
|
|
||||||
public:
|
|
||||||
explicit domain_error(
|
|
||||||
std::string&& message_arg,
|
|
||||||
raw_trace&& trace = detail::get_raw_trace_and_absorb()
|
|
||||||
) noexcept
|
|
||||||
: exception_with_message(std::move(message_arg), std::move(trace)) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class CPPTRACE_EXPORT invalid_argument : public exception_with_message {
|
|
||||||
public:
|
|
||||||
explicit invalid_argument(
|
|
||||||
std::string&& message_arg,
|
|
||||||
raw_trace&& trace = detail::get_raw_trace_and_absorb()
|
|
||||||
) noexcept
|
|
||||||
: exception_with_message(std::move(message_arg), std::move(trace)) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class CPPTRACE_EXPORT length_error : public exception_with_message {
|
|
||||||
public:
|
|
||||||
explicit length_error(
|
|
||||||
std::string&& message_arg,
|
|
||||||
raw_trace&& trace = detail::get_raw_trace_and_absorb()
|
|
||||||
) noexcept
|
|
||||||
: exception_with_message(std::move(message_arg), std::move(trace)) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class CPPTRACE_EXPORT out_of_range : public exception_with_message {
|
|
||||||
public:
|
|
||||||
explicit out_of_range(
|
|
||||||
std::string&& message_arg,
|
|
||||||
raw_trace&& trace = detail::get_raw_trace_and_absorb()
|
|
||||||
) noexcept
|
|
||||||
: exception_with_message(std::move(message_arg), std::move(trace)) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class CPPTRACE_EXPORT runtime_error : public exception_with_message {
|
|
||||||
public:
|
|
||||||
explicit runtime_error(
|
|
||||||
std::string&& message_arg,
|
|
||||||
raw_trace&& trace = detail::get_raw_trace_and_absorb()
|
|
||||||
) noexcept
|
|
||||||
: exception_with_message(std::move(message_arg), std::move(trace)) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class CPPTRACE_EXPORT range_error : public exception_with_message {
|
|
||||||
public:
|
|
||||||
explicit range_error(
|
|
||||||
std::string&& message_arg,
|
|
||||||
raw_trace&& trace = detail::get_raw_trace_and_absorb()
|
|
||||||
) noexcept
|
|
||||||
: exception_with_message(std::move(message_arg), std::move(trace)) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class CPPTRACE_EXPORT overflow_error : public exception_with_message {
|
|
||||||
public:
|
|
||||||
explicit overflow_error(
|
|
||||||
std::string&& message_arg,
|
|
||||||
raw_trace&& trace = detail::get_raw_trace_and_absorb()
|
|
||||||
) noexcept
|
|
||||||
: exception_with_message(std::move(message_arg), std::move(trace)) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class CPPTRACE_EXPORT underflow_error : public exception_with_message {
|
|
||||||
public:
|
|
||||||
explicit underflow_error(
|
|
||||||
std::string&& message_arg,
|
|
||||||
raw_trace&& trace = detail::get_raw_trace_and_absorb()
|
|
||||||
) noexcept
|
|
||||||
: exception_with_message(std::move(message_arg), std::move(trace)) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class CPPTRACE_EXPORT nested_exception : public lazy_exception {
|
|
||||||
std::exception_ptr ptr;
|
|
||||||
mutable std::string message_value;
|
|
||||||
public:
|
|
||||||
explicit nested_exception(
|
|
||||||
const std::exception_ptr& exception_ptr,
|
|
||||||
raw_trace&& trace = detail::get_raw_trace_and_absorb()
|
|
||||||
) noexcept
|
|
||||||
: lazy_exception(std::move(trace)), ptr(exception_ptr) {}
|
|
||||||
|
|
||||||
const char* message() const noexcept override;
|
|
||||||
std::exception_ptr nested_ptr() const noexcept;
|
|
||||||
};
|
|
||||||
|
|
||||||
class CPPTRACE_EXPORT system_error : public runtime_error {
|
|
||||||
std::error_code ec;
|
|
||||||
public:
|
|
||||||
explicit system_error(
|
|
||||||
int error_code,
|
|
||||||
std::string&& message_arg,
|
|
||||||
raw_trace&& trace = detail::get_raw_trace_and_absorb()
|
|
||||||
) noexcept;
|
|
||||||
const std::error_code& code() const noexcept;
|
|
||||||
};
|
|
||||||
|
|
||||||
// [[noreturn]] must come first due to old clang
|
|
||||||
[[noreturn]] CPPTRACE_EXPORT void rethrow_and_wrap_if_needed(std::size_t skip = 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exception wrapper utilities
|
|
||||||
#define CPPTRACE_WRAP_BLOCK(statements) do { \
|
|
||||||
try { \
|
|
||||||
statements \
|
|
||||||
} catch(...) { \
|
|
||||||
::cpptrace::rethrow_and_wrap_if_needed(); \
|
|
||||||
} \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
#define CPPTRACE_WRAP(expression) [&] () -> decltype((expression)) { \
|
|
||||||
try { \
|
|
||||||
return expression; \
|
|
||||||
} catch(...) { \
|
|
||||||
::cpptrace::rethrow_and_wrap_if_needed(1); \
|
|
||||||
} \
|
|
||||||
} ()
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
#pragma warning(pop)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@ -1,74 +0,0 @@
|
|||||||
#ifndef CPPTRACE_FORMATTING_HPP
|
|
||||||
#define CPPTRACE_FORMATTING_HPP
|
|
||||||
|
|
||||||
#include <cpptrace/basic.hpp>
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
namespace cpptrace {
|
|
||||||
class CPPTRACE_EXPORT formatter {
|
|
||||||
class impl;
|
|
||||||
// can't be a std::unique_ptr due to msvc awfulness with dllimport/dllexport and https://stackoverflow.com/q/4145605/15675011
|
|
||||||
impl* pimpl;
|
|
||||||
|
|
||||||
public:
|
|
||||||
formatter();
|
|
||||||
~formatter();
|
|
||||||
|
|
||||||
formatter(formatter&&);
|
|
||||||
formatter(const formatter&);
|
|
||||||
formatter& operator=(formatter&&);
|
|
||||||
formatter& operator=(const formatter&);
|
|
||||||
|
|
||||||
formatter& header(std::string);
|
|
||||||
enum class color_mode {
|
|
||||||
always,
|
|
||||||
none,
|
|
||||||
automatic,
|
|
||||||
};
|
|
||||||
formatter& colors(color_mode);
|
|
||||||
enum class address_mode {
|
|
||||||
raw,
|
|
||||||
object,
|
|
||||||
none,
|
|
||||||
};
|
|
||||||
formatter& addresses(address_mode);
|
|
||||||
enum class path_mode {
|
|
||||||
// full path is used
|
|
||||||
full,
|
|
||||||
// only the file name is used
|
|
||||||
basename,
|
|
||||||
};
|
|
||||||
formatter& paths(path_mode);
|
|
||||||
formatter& snippets(bool);
|
|
||||||
formatter& snippet_context(int);
|
|
||||||
formatter& columns(bool);
|
|
||||||
formatter& filtered_frame_placeholders(bool);
|
|
||||||
formatter& filter(std::function<bool(const stacktrace_frame&)>);
|
|
||||||
|
|
||||||
std::string format(const stacktrace_frame&) const;
|
|
||||||
std::string format(const stacktrace_frame&, bool color) const;
|
|
||||||
|
|
||||||
std::string format(const stacktrace&) const;
|
|
||||||
std::string format(const stacktrace&, bool color) const;
|
|
||||||
|
|
||||||
void print(const stacktrace_frame&) const;
|
|
||||||
void print(const stacktrace_frame&, bool color) const;
|
|
||||||
void print(std::ostream&, const stacktrace_frame&) const;
|
|
||||||
void print(std::ostream&, const stacktrace_frame&, bool color) const;
|
|
||||||
void print(std::FILE*, const stacktrace_frame&) const;
|
|
||||||
void print(std::FILE*, const stacktrace_frame&, bool color) const;
|
|
||||||
|
|
||||||
void print(const stacktrace&) const;
|
|
||||||
void print(const stacktrace&, bool color) const;
|
|
||||||
void print(std::ostream&, const stacktrace&) const;
|
|
||||||
void print(std::ostream&, const stacktrace&, bool color) const;
|
|
||||||
void print(std::FILE*, const stacktrace&) const;
|
|
||||||
void print(std::FILE*, const stacktrace&, bool color) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
CPPTRACE_EXPORT const formatter& get_default_formatter();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
#ifndef CPPTRACE_FORWARD_HPP
|
|
||||||
#define CPPTRACE_FORWARD_HPP
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
namespace cpptrace {
|
|
||||||
// Some type sufficient for an instruction pointer, currently always an alias to std::uintptr_t
|
|
||||||
using frame_ptr = std::uintptr_t;
|
|
||||||
|
|
||||||
struct raw_trace;
|
|
||||||
struct object_trace;
|
|
||||||
struct stacktrace;
|
|
||||||
|
|
||||||
struct object_frame;
|
|
||||||
struct stacktrace_frame;
|
|
||||||
struct safe_object_frame;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@ -1,131 +0,0 @@
|
|||||||
#ifndef CPPTRACE_FROM_CURRENT_HPP
|
|
||||||
#define CPPTRACE_FROM_CURRENT_HPP
|
|
||||||
|
|
||||||
#include <cpptrace/basic.hpp>
|
|
||||||
|
|
||||||
namespace cpptrace {
|
|
||||||
CPPTRACE_EXPORT const raw_trace& raw_trace_from_current_exception();
|
|
||||||
CPPTRACE_EXPORT const stacktrace& from_current_exception();
|
|
||||||
|
|
||||||
namespace detail {
|
|
||||||
// Trace switch is to prevent multiple tracing of stacks on call stacks with multiple catches that don't
|
|
||||||
// immediately match
|
|
||||||
inline bool& get_trace_switch() {
|
|
||||||
static thread_local bool trace_switch = true;
|
|
||||||
return trace_switch;
|
|
||||||
}
|
|
||||||
|
|
||||||
class CPPTRACE_EXPORT try_canary {
|
|
||||||
public:
|
|
||||||
~try_canary() {
|
|
||||||
// Fires when we exit a try block, either via normal means or during unwinding.
|
|
||||||
// Either way: Flip the switch.
|
|
||||||
get_trace_switch() = true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
CPPTRACE_EXPORT CPPTRACE_FORCE_NO_INLINE void collect_current_trace(std::size_t skip);
|
|
||||||
|
|
||||||
// this function can be void, however, a char return is used to prevent TCO of the collect_current_trace
|
|
||||||
CPPTRACE_FORCE_NO_INLINE inline char exception_unwind_interceptor(std::size_t skip) {
|
|
||||||
if(get_trace_switch()) {
|
|
||||||
// Done during a search phase. Flip the switch off, no more traces until an unwind happens
|
|
||||||
get_trace_switch() = false;
|
|
||||||
collect_current_trace(skip + 1);
|
|
||||||
}
|
|
||||||
return 42;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
CPPTRACE_FORCE_NO_INLINE inline int exception_filter() {
|
|
||||||
exception_unwind_interceptor(1);
|
|
||||||
return 0; // EXCEPTION_CONTINUE_SEARCH
|
|
||||||
}
|
|
||||||
CPPTRACE_FORCE_NO_INLINE inline int unconditional_exception_filter() {
|
|
||||||
collect_current_trace(1);
|
|
||||||
return 0; // EXCEPTION_CONTINUE_SEARCH
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
class CPPTRACE_EXPORT unwind_interceptor {
|
|
||||||
public:
|
|
||||||
virtual ~unwind_interceptor();
|
|
||||||
};
|
|
||||||
class CPPTRACE_EXPORT unconditional_unwind_interceptor {
|
|
||||||
public:
|
|
||||||
virtual ~unconditional_unwind_interceptor();
|
|
||||||
};
|
|
||||||
|
|
||||||
CPPTRACE_EXPORT void do_prepare_unwind_interceptor(char(*)(std::size_t));
|
|
||||||
|
|
||||||
#ifndef CPPTRACE_DONT_PREPARE_UNWIND_INTERCEPTOR_ON
|
|
||||||
__attribute__((constructor)) inline void prepare_unwind_interceptor() {
|
|
||||||
// __attribute__((constructor)) inline functions can be called for every source file they're #included in
|
|
||||||
// there is still only one copy of the inline function in the final executable, though
|
|
||||||
// LTO can make the redundant constructs fire only once
|
|
||||||
// do_prepare_unwind_interceptor prevents against multiple preparations however it makes sense to guard
|
|
||||||
// against it here too as a fast path, not that this should matter for performance
|
|
||||||
static bool did_prepare = false;
|
|
||||||
if(!did_prepare) {
|
|
||||||
do_prepare_unwind_interceptor(exception_unwind_interceptor);
|
|
||||||
did_prepare = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
// this awful double-IILE is due to C2713 "You can't use structured exception handling (__try/__except) and C++
|
|
||||||
// exception handling (try/catch) in the same function."
|
|
||||||
#define CPPTRACE_TRY \
|
|
||||||
try { \
|
|
||||||
::cpptrace::detail::try_canary cpptrace_try_canary; \
|
|
||||||
[&]() { \
|
|
||||||
__try { \
|
|
||||||
[&]() {
|
|
||||||
#define CPPTRACE_CATCH(param) \
|
|
||||||
}(); \
|
|
||||||
} __except(::cpptrace::detail::exception_filter()) {} \
|
|
||||||
}(); \
|
|
||||||
} catch(param)
|
|
||||||
#define CPPTRACE_TRYZ \
|
|
||||||
try { \
|
|
||||||
[&]() { \
|
|
||||||
__try { \
|
|
||||||
[&]() {
|
|
||||||
#define CPPTRACE_CATCHZ(param) \
|
|
||||||
}(); \
|
|
||||||
} __except(::cpptrace::detail::unconditional_exception_filter()) {} \
|
|
||||||
}(); \
|
|
||||||
} catch(param)
|
|
||||||
#else
|
|
||||||
#define CPPTRACE_TRY \
|
|
||||||
try { \
|
|
||||||
_Pragma("GCC diagnostic push") \
|
|
||||||
_Pragma("GCC diagnostic ignored \"-Wshadow\"") \
|
|
||||||
::cpptrace::detail::try_canary cpptrace_try_canary; \
|
|
||||||
_Pragma("GCC diagnostic pop") \
|
|
||||||
try {
|
|
||||||
#define CPPTRACE_CATCH(param) \
|
|
||||||
} catch(::cpptrace::detail::unwind_interceptor&) {} \
|
|
||||||
} catch(param)
|
|
||||||
#define CPPTRACE_TRYZ \
|
|
||||||
try { \
|
|
||||||
try {
|
|
||||||
#define CPPTRACE_CATCHZ(param) \
|
|
||||||
} catch(::cpptrace::detail::unconditional_unwind_interceptor&) {} \
|
|
||||||
} catch(param)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define CPPTRACE_CATCH_ALT(param) catch(param)
|
|
||||||
|
|
||||||
#ifdef CPPTRACE_UNPREFIXED_TRY_CATCH
|
|
||||||
#define TRY CPPTRACE_TRY
|
|
||||||
#define CATCH(param) CPPTRACE_CATCH(param)
|
|
||||||
#define TRYZ CPPTRACE_TRYZ
|
|
||||||
#define CATCHZ(param) CPPTRACE_CATCHZ(param)
|
|
||||||
#define CATCH_ALT(param) CPPTRACE_CATCH_ALT(param)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@ -1,52 +0,0 @@
|
|||||||
#ifndef CPPTRACE_IO_HPP
|
|
||||||
#define CPPTRACE_IO_HPP
|
|
||||||
|
|
||||||
#include <cpptrace/basic.hpp>
|
|
||||||
|
|
||||||
#include <iosfwd>
|
|
||||||
|
|
||||||
#ifndef CPPTRACE_NO_STD_FORMAT
|
|
||||||
#if __cplusplus >= 202002L
|
|
||||||
#ifdef __has_include
|
|
||||||
#if __has_include(<format>)
|
|
||||||
#define CPPTRACE_STD_FORMAT
|
|
||||||
#include <format>
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
#pragma warning(push)
|
|
||||||
// warning C4251: using non-dll-exported type in dll-exported type, firing on std::vector<frame_ptr> and others for some
|
|
||||||
// reason
|
|
||||||
// 4275 is the same thing but for base classes
|
|
||||||
#pragma warning(disable: 4251; disable: 4275)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace cpptrace {
|
|
||||||
std::ostream& operator<<(std::ostream& stream, const stacktrace_frame& frame);
|
|
||||||
std::ostream& operator<<(std::ostream& stream, const stacktrace& trace);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(CPPTRACE_STD_FORMAT) && defined(__cpp_lib_format)
|
|
||||||
template <>
|
|
||||||
struct std::formatter<cpptrace::stacktrace_frame> : std::formatter<std::string> {
|
|
||||||
auto format(cpptrace::stacktrace_frame frame, format_context& ctx) const {
|
|
||||||
return formatter<string>::format(frame.to_string(), ctx);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct std::formatter<cpptrace::stacktrace> : std::formatter<std::string> {
|
|
||||||
auto format(cpptrace::stacktrace trace, format_context& ctx) const {
|
|
||||||
return formatter<string>::format(trace.to_string(), ctx);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
#pragma warning(pop)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@ -1,54 +0,0 @@
|
|||||||
#ifndef CPPTRACE_UTILS_HPP
|
|
||||||
#define CPPTRACE_UTILS_HPP
|
|
||||||
|
|
||||||
#include <cpptrace/basic.hpp>
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
#pragma warning(push)
|
|
||||||
// warning C4251: using non-dll-exported type in dll-exported type, firing on std::vector<frame_ptr> and others for some
|
|
||||||
// reason
|
|
||||||
// 4275 is the same thing but for base classes
|
|
||||||
#pragma warning(disable: 4251; disable: 4275)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace cpptrace {
|
|
||||||
CPPTRACE_EXPORT std::string demangle(const std::string& name);
|
|
||||||
CPPTRACE_EXPORT std::string get_snippet(
|
|
||||||
const std::string& path,
|
|
||||||
std::size_t line,
|
|
||||||
std::size_t context_size,
|
|
||||||
bool color = false
|
|
||||||
);
|
|
||||||
CPPTRACE_EXPORT bool isatty(int fd);
|
|
||||||
|
|
||||||
CPPTRACE_EXPORT extern const int stdin_fileno;
|
|
||||||
CPPTRACE_EXPORT extern const int stderr_fileno;
|
|
||||||
CPPTRACE_EXPORT extern const int stdout_fileno;
|
|
||||||
|
|
||||||
CPPTRACE_EXPORT void register_terminate_handler();
|
|
||||||
|
|
||||||
// options:
|
|
||||||
CPPTRACE_EXPORT void absorb_trace_exceptions(bool absorb);
|
|
||||||
CPPTRACE_EXPORT void enable_inlined_call_resolution(bool enable);
|
|
||||||
|
|
||||||
enum class cache_mode {
|
|
||||||
// Only minimal lookup tables
|
|
||||||
prioritize_memory = 0,
|
|
||||||
// Build lookup tables but don't keep them around between trace calls
|
|
||||||
hybrid = 1,
|
|
||||||
// Build lookup tables as needed
|
|
||||||
prioritize_speed = 2
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace experimental {
|
|
||||||
CPPTRACE_EXPORT void set_cache_mode(cache_mode mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
// dwarf options
|
|
||||||
namespace experimental {
|
|
||||||
CPPTRACE_EXPORT void set_dwarf_resolver_line_table_cache_size(nullable<std::size_t> max_entries);
|
|
||||||
CPPTRACE_EXPORT void set_dwarf_resolver_disable_aranges(bool disable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@ -1,164 +0,0 @@
|
|||||||
#ifndef CTRACE_H
|
|
||||||
#define CTRACE_H
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#define CPPTRACE_EXPORT_ATTR __declspec(dllexport)
|
|
||||||
#define CPPTRACE_IMPORT_ATTR __declspec(dllimport)
|
|
||||||
#else
|
|
||||||
#define CPPTRACE_EXPORT_ATTR __attribute__((visibility("default")))
|
|
||||||
#define CPPTRACE_IMPORT_ATTR __attribute__((visibility("default")))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef CPPTRACE_STATIC_DEFINE
|
|
||||||
# define CPPTRACE_EXPORT
|
|
||||||
# define CPPTRACE_NO_EXPORT
|
|
||||||
#else
|
|
||||||
# ifndef CPPTRACE_EXPORT
|
|
||||||
# ifdef cpptrace_lib_EXPORTS
|
|
||||||
/* We are building this library */
|
|
||||||
# define CPPTRACE_EXPORT CPPTRACE_EXPORT_ATTR
|
|
||||||
# else
|
|
||||||
/* We are using this library */
|
|
||||||
# define CPPTRACE_EXPORT CPPTRACE_IMPORT_ATTR
|
|
||||||
# endif
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(__cplusplus)
|
|
||||||
#define CTRACE_BEGIN_DEFINITIONS extern "C" {
|
|
||||||
#define CTRACE_END_DEFINITIONS }
|
|
||||||
#else
|
|
||||||
#define CTRACE_BEGIN_DEFINITIONS
|
|
||||||
#define CTRACE_END_DEFINITIONS
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
#define CTRACE_FORCE_NO_INLINE __declspec(noinline)
|
|
||||||
#else
|
|
||||||
#define CTRACE_FORCE_NO_INLINE __attribute__((noinline))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
#define CTRACE_FORCE_INLINE __forceinline
|
|
||||||
#elif defined(__clang__) || defined(__GNUC__)
|
|
||||||
#define CTRACE_FORCE_INLINE __attribute__((always_inline)) inline
|
|
||||||
#else
|
|
||||||
#define CTRACE_FORCE_INLINE inline
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* See `CPPTRACE_PATH_MAX` for more info. */
|
|
||||||
#define CTRACE_PATH_MAX 4096
|
|
||||||
|
|
||||||
CTRACE_BEGIN_DEFINITIONS
|
|
||||||
|
|
||||||
typedef struct ctrace_raw_trace ctrace_raw_trace;
|
|
||||||
typedef struct ctrace_object_trace ctrace_object_trace;
|
|
||||||
typedef struct ctrace_stacktrace ctrace_stacktrace;
|
|
||||||
|
|
||||||
/* Represents a boolean value, ensures a consistent ABI. */
|
|
||||||
typedef int8_t ctrace_bool;
|
|
||||||
/* A type that can represent a pointer, alias for `uintptr_t`. */
|
|
||||||
typedef uintptr_t ctrace_frame_ptr;
|
|
||||||
typedef struct ctrace_object_frame ctrace_object_frame;
|
|
||||||
typedef struct ctrace_stacktrace_frame ctrace_stacktrace_frame;
|
|
||||||
typedef struct ctrace_safe_object_frame ctrace_safe_object_frame;
|
|
||||||
|
|
||||||
/* Type-safe null-terminated string wrapper */
|
|
||||||
typedef struct {
|
|
||||||
const char* data;
|
|
||||||
} ctrace_owning_string;
|
|
||||||
|
|
||||||
struct ctrace_object_frame {
|
|
||||||
ctrace_frame_ptr raw_address;
|
|
||||||
ctrace_frame_ptr obj_address;
|
|
||||||
const char* obj_path;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ctrace_stacktrace_frame {
|
|
||||||
ctrace_frame_ptr raw_address;
|
|
||||||
ctrace_frame_ptr object_address;
|
|
||||||
uint32_t line;
|
|
||||||
uint32_t column;
|
|
||||||
const char* filename;
|
|
||||||
const char* symbol;
|
|
||||||
ctrace_bool is_inline;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ctrace_safe_object_frame {
|
|
||||||
ctrace_frame_ptr raw_address;
|
|
||||||
ctrace_frame_ptr relative_obj_address;
|
|
||||||
char object_path[CTRACE_PATH_MAX + 1];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ctrace_raw_trace {
|
|
||||||
ctrace_frame_ptr* frames;
|
|
||||||
size_t count;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ctrace_object_trace {
|
|
||||||
ctrace_object_frame* frames;
|
|
||||||
size_t count;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ctrace_stacktrace {
|
|
||||||
ctrace_stacktrace_frame* frames;
|
|
||||||
size_t count;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* ctrace::string: */
|
|
||||||
CPPTRACE_EXPORT ctrace_owning_string ctrace_generate_owning_string(const char* raw_string);
|
|
||||||
CPPTRACE_EXPORT void ctrace_free_owning_string(ctrace_owning_string* string);
|
|
||||||
|
|
||||||
/* ctrace::generation: */
|
|
||||||
CPPTRACE_EXPORT ctrace_raw_trace ctrace_generate_raw_trace(size_t skip, size_t max_depth);
|
|
||||||
CPPTRACE_EXPORT ctrace_object_trace ctrace_generate_object_trace(size_t skip, size_t max_depth);
|
|
||||||
CPPTRACE_EXPORT ctrace_stacktrace ctrace_generate_trace(size_t skip, size_t max_depth);
|
|
||||||
|
|
||||||
/* ctrace::freeing: */
|
|
||||||
CPPTRACE_EXPORT void ctrace_free_raw_trace(ctrace_raw_trace* trace);
|
|
||||||
CPPTRACE_EXPORT void ctrace_free_object_trace(ctrace_object_trace* trace);
|
|
||||||
CPPTRACE_EXPORT void ctrace_free_stacktrace(ctrace_stacktrace* trace);
|
|
||||||
|
|
||||||
/* ctrace::resolve: */
|
|
||||||
CPPTRACE_EXPORT ctrace_stacktrace ctrace_resolve_raw_trace(const ctrace_raw_trace* trace);
|
|
||||||
CPPTRACE_EXPORT ctrace_object_trace ctrace_resolve_raw_trace_to_object_trace(const ctrace_raw_trace* trace);
|
|
||||||
CPPTRACE_EXPORT ctrace_stacktrace ctrace_resolve_object_trace(const ctrace_object_trace* trace);
|
|
||||||
|
|
||||||
/* ctrace::safe: */
|
|
||||||
CPPTRACE_EXPORT size_t ctrace_safe_generate_raw_trace(ctrace_frame_ptr* buffer, size_t size, size_t skip, size_t max_depth);
|
|
||||||
CPPTRACE_EXPORT void ctrace_get_safe_object_frame(ctrace_frame_ptr address, ctrace_safe_object_frame* out);
|
|
||||||
CPPTRACE_EXPORT ctrace_bool ctrace_can_signal_safe_unwind(void);
|
|
||||||
CPPTRACE_EXPORT ctrace_bool ctrace_can_get_safe_object_frame(void);
|
|
||||||
|
|
||||||
/* ctrace::io: */
|
|
||||||
CPPTRACE_EXPORT ctrace_owning_string ctrace_stacktrace_to_string(const ctrace_stacktrace* trace, ctrace_bool use_color);
|
|
||||||
CPPTRACE_EXPORT void ctrace_print_stacktrace(const ctrace_stacktrace* trace, FILE* to, ctrace_bool use_color);
|
|
||||||
|
|
||||||
/* ctrace::utility: */
|
|
||||||
CPPTRACE_EXPORT ctrace_owning_string ctrace_demangle(const char* mangled);
|
|
||||||
CPPTRACE_EXPORT int ctrace_stdin_fileno(void);
|
|
||||||
CPPTRACE_EXPORT int ctrace_stderr_fileno(void);
|
|
||||||
CPPTRACE_EXPORT int ctrace_stdout_fileno(void);
|
|
||||||
CPPTRACE_EXPORT ctrace_bool ctrace_isatty(int fd);
|
|
||||||
|
|
||||||
CPPTRACE_EXPORT ctrace_object_frame ctrace_get_object_info(const ctrace_stacktrace_frame* frame);
|
|
||||||
|
|
||||||
/* ctrace::config: */
|
|
||||||
typedef enum {
|
|
||||||
/* Only minimal lookup tables */
|
|
||||||
ctrace_prioritize_memory = 0,
|
|
||||||
/* Build lookup tables but don't keep them around between trace calls */
|
|
||||||
ctrace_hybrid = 1,
|
|
||||||
/* Build lookup tables as needed */
|
|
||||||
ctrace_prioritize_speed = 2
|
|
||||||
} ctrace_cache_mode;
|
|
||||||
CPPTRACE_EXPORT void ctrace_set_cache_mode(ctrace_cache_mode mode);
|
|
||||||
CPPTRACE_EXPORT void ctrace_enable_inlined_call_resolution(ctrace_bool enable);
|
|
||||||
|
|
||||||
CTRACE_END_DEFINITIONS
|
|
||||||
|
|
||||||
#endif
|
|
||||||
14
lint.sh
Normal file
14
lint.sh
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
status=0
|
||||||
|
while read f
|
||||||
|
do
|
||||||
|
echo checking $f
|
||||||
|
flags="-DCPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE -DCPPTRACE_FULL_TRACE_WITH_STACKTRACE -DCPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE -DCPPTRACE_GET_SYMBOLS_WITH_LIBDL -DCPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE -DCPPTRACE_GET_SYMBOLS_WITH_NOTHING -DCPPTRACE_UNWIND_WITH_EXECINFO -DCPPTRACE_UNWIND_WITH_UNWIND -DCPPTRACE_UNWIND_WITH_NOTHING -DCPPTRACE_DEMANGLE_WITH_CXXABI -DCPPTRACE_DEMANGLE_WITH_NOTHING -DCPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH"
|
||||||
|
clang-tidy $f -- -std=c++11 -Iinclude $@ $flags
|
||||||
|
ret=$?
|
||||||
|
if [ $ret -ne 0 ]; then
|
||||||
|
status=1
|
||||||
|
fi
|
||||||
|
done <<< $(find include src -name "*.hpp" -o -name "*.cpp" -not -path "test/speedtest.cpp" -not -path "src/full/full_trace_with_stacktrace.cpp")
|
||||||
|
exit $status
|
||||||
BIN
res/demo.png
BIN
res/demo.png
Binary file not shown.
|
Before Width: | Height: | Size: 90 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 92 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 35 KiB |
BIN
res/inlining.png
BIN
res/inlining.png
Binary file not shown.
|
Before Width: | Height: | Size: 54 KiB |
BIN
res/screenshot.png
Normal file
BIN
res/screenshot.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 89 KiB |
BIN
res/snippets.png
BIN
res/snippets.png
Binary file not shown.
|
Before Width: | Height: | Size: 23 KiB |
@ -1,8 +0,0 @@
|
|||||||
sonar.organization=jeremy-rifkin
|
|
||||||
sonar.projectKey=jeremy-rifkin_cpptrace
|
|
||||||
|
|
||||||
# relative paths to source directories. More details and properties are described
|
|
||||||
# in https://sonarcloud.io/documentation/project-administration/narrowing-the-focus/
|
|
||||||
sonar.sources=src, include
|
|
||||||
sonar.sourceEncoding=UTF-8
|
|
||||||
sonar.cfamily.compile-commands=build/compile_commands.json
|
|
||||||
@ -1,437 +0,0 @@
|
|||||||
#include "binary/elf.hpp"
|
|
||||||
#include "utils/optional.hpp"
|
|
||||||
|
|
||||||
#if IS_LINUX
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cstring>
|
|
||||||
#include <mutex>
|
|
||||||
#include <type_traits>
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
#include <elf.h>
|
|
||||||
|
|
||||||
namespace cpptrace {
|
|
||||||
namespace detail {
|
|
||||||
elf::elf(
|
|
||||||
file_wrapper file,
|
|
||||||
const std::string& object_path,
|
|
||||||
bool is_little_endian,
|
|
||||||
bool is_64
|
|
||||||
) : file(std::move(file)), object_path(object_path), is_little_endian(is_little_endian), is_64(is_64) {}
|
|
||||||
|
|
||||||
Result<elf, internal_error> elf::open_elf(const std::string& object_path) {
|
|
||||||
auto file = raii_wrap(std::fopen(object_path.c_str(), "rb"), file_deleter);
|
|
||||||
if(file == nullptr) {
|
|
||||||
return internal_error("Unable to read object file {}", object_path);
|
|
||||||
}
|
|
||||||
// Initial checks/metadata
|
|
||||||
auto magic = load_bytes<std::array<char, 4>>(file, 0);
|
|
||||||
if(magic.is_error()) {
|
|
||||||
return std::move(magic).unwrap_error();
|
|
||||||
}
|
|
||||||
if(magic.unwrap_value() != (std::array<char, 4>{0x7F, 'E', 'L', 'F'})) {
|
|
||||||
return internal_error("File is not ELF " + object_path);
|
|
||||||
}
|
|
||||||
auto ei_class = load_bytes<std::uint8_t>(file, 4);
|
|
||||||
if(ei_class.is_error()) {
|
|
||||||
return std::move(ei_class).unwrap_error();
|
|
||||||
}
|
|
||||||
bool is_64 = ei_class.unwrap_value() == 2;
|
|
||||||
auto ei_data = load_bytes<std::uint8_t>(file, 5);
|
|
||||||
if(ei_data.is_error()) {
|
|
||||||
return std::move(ei_data).unwrap_error();
|
|
||||||
}
|
|
||||||
bool is_little_endian = ei_data.unwrap_value() == 1;
|
|
||||||
auto ei_version = load_bytes<std::uint8_t>(file, 6);
|
|
||||||
if(ei_version.is_error()) {
|
|
||||||
return std::move(ei_version).unwrap_error();
|
|
||||||
}
|
|
||||||
if(ei_version.unwrap_value() != 1) {
|
|
||||||
return internal_error("Unexpected ELF version " + object_path);
|
|
||||||
}
|
|
||||||
return elf(std::move(file), object_path, is_little_endian, is_64);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<std::uintptr_t, internal_error> elf::get_module_image_base() {
|
|
||||||
// get image base
|
|
||||||
if(is_64) {
|
|
||||||
return get_module_image_base_impl<64>();
|
|
||||||
} else {
|
|
||||||
return get_module_image_base_impl<32>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<std::size_t Bits>
|
|
||||||
Result<std::uintptr_t, internal_error> elf::get_module_image_base_impl() {
|
|
||||||
static_assert(Bits == 32 || Bits == 64, "Unexpected Bits argument");
|
|
||||||
using PHeader = typename std::conditional<Bits == 32, Elf32_Phdr, Elf64_Phdr>::type;
|
|
||||||
auto header = get_header_info();
|
|
||||||
if(header.is_error()) {
|
|
||||||
return std::move(header).unwrap_error();
|
|
||||||
}
|
|
||||||
const auto& header_info = header.unwrap_value();
|
|
||||||
// PT_PHDR will occur at most once
|
|
||||||
// Should be somewhat reliable https://stackoverflow.com/q/61568612/15675011
|
|
||||||
// It should occur at the beginning but may as well loop just in case
|
|
||||||
for(unsigned i = 0; i < header_info.e_phnum; i++) {
|
|
||||||
auto loaded_ph = load_bytes<PHeader>(file, header_info.e_phoff + header_info.e_phentsize * i);
|
|
||||||
if(loaded_ph.is_error()) {
|
|
||||||
return std::move(loaded_ph).unwrap_error();
|
|
||||||
}
|
|
||||||
const PHeader& program_header = loaded_ph.unwrap_value();
|
|
||||||
if(byteswap_if_needed(program_header.p_type) == PT_PHDR) {
|
|
||||||
return byteswap_if_needed(program_header.p_vaddr) -
|
|
||||||
byteswap_if_needed(program_header.p_offset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Apparently some objects like shared objects can end up missing this header. 0 as a base seems correct.
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
optional<std::string> elf::lookup_symbol(frame_ptr pc) {
|
|
||||||
if(auto symtab = get_symtab()) {
|
|
||||||
if(auto symbol = lookup_symbol(pc, symtab.unwrap_value())) {
|
|
||||||
return symbol;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(auto dynamic_symtab = get_dynamic_symtab()) {
|
|
||||||
if(auto symbol = lookup_symbol(pc, dynamic_symtab.unwrap_value())) {
|
|
||||||
return symbol;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
optional<std::string> elf::lookup_symbol(frame_ptr pc, const optional<symtab_info>& maybe_symtab) {
|
|
||||||
if(!maybe_symtab) {
|
|
||||||
return nullopt;
|
|
||||||
}
|
|
||||||
auto& symtab = maybe_symtab.unwrap();
|
|
||||||
if(symtab.strtab_link == SHN_UNDEF) {
|
|
||||||
return nullopt;
|
|
||||||
}
|
|
||||||
auto strtab_ = get_strtab(symtab.strtab_link);
|
|
||||||
if(strtab_.is_error()) {
|
|
||||||
return nullopt;
|
|
||||||
}
|
|
||||||
auto& strtab = strtab_.unwrap_value();
|
|
||||||
auto it = first_less_than_or_equal(
|
|
||||||
symtab.entries.begin(),
|
|
||||||
symtab.entries.end(),
|
|
||||||
pc,
|
|
||||||
[] (frame_ptr pc, const symtab_entry& entry) {
|
|
||||||
return pc < entry.st_value;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
if(it == symtab.entries.end()) {
|
|
||||||
return nullopt;
|
|
||||||
}
|
|
||||||
if(pc <= it->st_value + it->st_size) {
|
|
||||||
return strtab.data() + it->st_name;
|
|
||||||
}
|
|
||||||
return nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<optional<std::vector<elf::symbol_entry>>, internal_error> elf::get_symtab_entries() {
|
|
||||||
return resolve_symtab_entries(get_symtab());
|
|
||||||
}
|
|
||||||
Result<optional<std::vector<elf::symbol_entry>>, internal_error> elf::get_dynamic_symtab_entries() {
|
|
||||||
return resolve_symtab_entries(get_dynamic_symtab());
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<optional<std::vector<elf::symbol_entry>>, internal_error> elf::resolve_symtab_entries(
|
|
||||||
const Result<const optional<elf::symtab_info> &, internal_error>& symtab
|
|
||||||
) {
|
|
||||||
if(!symtab) {
|
|
||||||
return symtab.unwrap_error();
|
|
||||||
}
|
|
||||||
if(!symtab.unwrap_value()) {
|
|
||||||
return nullopt;
|
|
||||||
}
|
|
||||||
const auto& info = symtab.unwrap_value().unwrap();
|
|
||||||
optional<const std::vector<char>&> strtab;
|
|
||||||
if(info.strtab_link != SHN_UNDEF) {
|
|
||||||
auto strtab_ = get_strtab(info.strtab_link);
|
|
||||||
if(strtab_.is_error()) {
|
|
||||||
return strtab_.unwrap_error();
|
|
||||||
}
|
|
||||||
strtab = strtab_.unwrap_value();
|
|
||||||
}
|
|
||||||
std::vector<symbol_entry> res;
|
|
||||||
for(const auto& entry : info.entries) {
|
|
||||||
res.push_back({
|
|
||||||
strtab.has_value() ? strtab.unwrap().data() + entry.st_name : "<strtab error>",
|
|
||||||
entry.st_shndx,
|
|
||||||
entry.st_value,
|
|
||||||
entry.st_size
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T, typename std::enable_if<std::is_integral<T>::value, int>::type>
|
|
||||||
T elf::byteswap_if_needed(T value) {
|
|
||||||
if(cpptrace::detail::is_little_endian() == is_little_endian) {
|
|
||||||
return value;
|
|
||||||
} else {
|
|
||||||
return byteswap(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<const elf::header_info&, internal_error> elf::get_header_info() {
|
|
||||||
if(header) {
|
|
||||||
Result<const elf::header_info&, internal_error> r = header.unwrap();
|
|
||||||
return std::ref(header.unwrap());
|
|
||||||
}
|
|
||||||
if(tried_to_load_header) {
|
|
||||||
return internal_error("previous header load failed " + object_path);
|
|
||||||
}
|
|
||||||
tried_to_load_header = true;
|
|
||||||
if(is_64) {
|
|
||||||
return get_header_info_impl<64>();
|
|
||||||
} else {
|
|
||||||
return get_header_info_impl<32>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<std::size_t Bits>
|
|
||||||
Result<const elf::header_info&, internal_error> elf::get_header_info_impl() {
|
|
||||||
static_assert(Bits == 32 || Bits == 64, "Unexpected Bits argument");
|
|
||||||
using Header = typename std::conditional<Bits == 32, Elf32_Ehdr, Elf64_Ehdr>::type;
|
|
||||||
auto loaded_header = load_bytes<Header>(file, 0);
|
|
||||||
if(loaded_header.is_error()) {
|
|
||||||
return std::move(loaded_header).unwrap_error();
|
|
||||||
}
|
|
||||||
const Header& file_header = loaded_header.unwrap_value();
|
|
||||||
if(file_header.e_ehsize != sizeof(Header)) {
|
|
||||||
return internal_error("ELF file header size mismatch" + object_path);
|
|
||||||
}
|
|
||||||
header_info info;
|
|
||||||
info.e_phoff = byteswap_if_needed(file_header.e_phoff);
|
|
||||||
info.e_phnum = byteswap_if_needed(file_header.e_phnum);
|
|
||||||
info.e_phentsize = byteswap_if_needed(file_header.e_phentsize);
|
|
||||||
info.e_shoff = byteswap_if_needed(file_header.e_shoff);
|
|
||||||
info.e_shnum = byteswap_if_needed(file_header.e_shnum);
|
|
||||||
info.e_shentsize = byteswap_if_needed(file_header.e_shentsize);
|
|
||||||
header = info;
|
|
||||||
return header.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<const std::vector<elf::section_info>&, internal_error> elf::get_sections() {
|
|
||||||
if(did_load_sections) {
|
|
||||||
return sections;
|
|
||||||
}
|
|
||||||
if(tried_to_load_sections) {
|
|
||||||
return internal_error("previous sections load failed " + object_path);
|
|
||||||
}
|
|
||||||
tried_to_load_sections = true;
|
|
||||||
if(is_64) {
|
|
||||||
return get_sections_impl<64>();
|
|
||||||
} else {
|
|
||||||
return get_sections_impl<32>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<std::size_t Bits>
|
|
||||||
Result<const std::vector<elf::section_info>&, internal_error> elf::get_sections_impl() {
|
|
||||||
static_assert(Bits == 32 || Bits == 64, "Unexpected Bits argument");
|
|
||||||
using SHeader = typename std::conditional<Bits == 32, Elf32_Shdr, Elf64_Shdr>::type;
|
|
||||||
auto header = get_header_info();
|
|
||||||
if(header.is_error()) {
|
|
||||||
return std::move(header).unwrap_error();
|
|
||||||
}
|
|
||||||
const auto& header_info = header.unwrap_value();
|
|
||||||
for(unsigned i = 0; i < header_info.e_shnum; i++) {
|
|
||||||
auto loaded_sh = load_bytes<SHeader>(file, header_info.e_shoff + header_info.e_shentsize * i);
|
|
||||||
if(loaded_sh.is_error()) {
|
|
||||||
return std::move(loaded_sh).unwrap_error();
|
|
||||||
}
|
|
||||||
const SHeader& section_header = loaded_sh.unwrap_value();
|
|
||||||
section_info info;
|
|
||||||
info.sh_type = byteswap_if_needed(section_header.sh_type);
|
|
||||||
info.sh_addr = byteswap_if_needed(section_header.sh_addr);
|
|
||||||
info.sh_offset = byteswap_if_needed(section_header.sh_offset);
|
|
||||||
info.sh_size = byteswap_if_needed(section_header.sh_size);
|
|
||||||
info.sh_entsize = byteswap_if_needed(section_header.sh_entsize);
|
|
||||||
info.sh_link = byteswap_if_needed(section_header.sh_link);
|
|
||||||
sections.push_back(info);
|
|
||||||
}
|
|
||||||
did_load_sections = true;
|
|
||||||
return sections;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<const std::vector<char>&, internal_error> elf::get_strtab(std::size_t index) {
|
|
||||||
auto res = strtab_entries.insert({index, {}});
|
|
||||||
auto it = res.first;
|
|
||||||
auto did_insert = res.second;
|
|
||||||
auto& entry = it->second;
|
|
||||||
if(!did_insert) {
|
|
||||||
if(entry.did_load_strtab) {
|
|
||||||
return entry.data;
|
|
||||||
}
|
|
||||||
if(entry.tried_to_load_strtab) {
|
|
||||||
return internal_error("previous strtab load failed {}", object_path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
entry.tried_to_load_strtab = true;
|
|
||||||
auto sections_ = get_sections();
|
|
||||||
if(sections_.is_error()) {
|
|
||||||
return std::move(sections_).unwrap_error();
|
|
||||||
}
|
|
||||||
const auto& sections = sections_.unwrap_value();
|
|
||||||
if(index >= sections.size()) {
|
|
||||||
return internal_error("requested strtab section index out of range");
|
|
||||||
}
|
|
||||||
const auto& section = sections[index];
|
|
||||||
if(section.sh_type != SHT_STRTAB) {
|
|
||||||
return internal_error("requested strtab section not a strtab (requested {} of {})", index, object_path);
|
|
||||||
}
|
|
||||||
entry.data.resize(section.sh_size + 1);
|
|
||||||
if(std::fseek(file, section.sh_offset, SEEK_SET) != 0) {
|
|
||||||
return internal_error("fseek error while loading elf string table");
|
|
||||||
}
|
|
||||||
if(std::fread(entry.data.data(), sizeof(char), section.sh_size, file) != section.sh_size) {
|
|
||||||
return internal_error("fread error while loading elf string table");
|
|
||||||
}
|
|
||||||
entry.data[section.sh_size] = 0; // just out of an abundance of caution
|
|
||||||
entry.did_load_strtab = true;
|
|
||||||
return entry.data;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<const optional<elf::symtab_info>&, internal_error> elf::get_symtab() {
|
|
||||||
if(did_load_symtab) {
|
|
||||||
return symtab;
|
|
||||||
}
|
|
||||||
if(tried_to_load_symtab) {
|
|
||||||
return internal_error("previous symtab load failed {}", object_path);
|
|
||||||
}
|
|
||||||
tried_to_load_symtab = true;
|
|
||||||
if(is_64) {
|
|
||||||
auto res = get_symtab_impl<64>(false);
|
|
||||||
if(res.has_value()) {
|
|
||||||
symtab = std::move(res).unwrap_value();
|
|
||||||
did_load_symtab = true;
|
|
||||||
return symtab;
|
|
||||||
} else {
|
|
||||||
return std::move(res).unwrap_error();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
auto res = get_symtab_impl<32>(false);
|
|
||||||
if(res.has_value()) {
|
|
||||||
symtab = std::move(res).unwrap_value();
|
|
||||||
did_load_symtab = true;
|
|
||||||
return symtab;
|
|
||||||
} else {
|
|
||||||
return std::move(res).unwrap_error();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<const optional<elf::symtab_info>&, internal_error> elf::get_dynamic_symtab() {
|
|
||||||
if(did_load_dynamic_symtab) {
|
|
||||||
return dynamic_symtab;
|
|
||||||
}
|
|
||||||
if(tried_to_load_dynamic_symtab) {
|
|
||||||
return internal_error("previous dynamic symtab load failed {}", object_path);
|
|
||||||
}
|
|
||||||
tried_to_load_dynamic_symtab = true;
|
|
||||||
if(is_64) {
|
|
||||||
auto res = get_symtab_impl<64>(true);
|
|
||||||
if(res.has_value()) {
|
|
||||||
dynamic_symtab = std::move(res).unwrap_value();
|
|
||||||
did_load_dynamic_symtab = true;
|
|
||||||
return dynamic_symtab;
|
|
||||||
} else {
|
|
||||||
return std::move(res).unwrap_error();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
auto res = get_symtab_impl<32>(true);
|
|
||||||
if(res.has_value()) {
|
|
||||||
dynamic_symtab = std::move(res).unwrap_value();
|
|
||||||
did_load_dynamic_symtab = true;
|
|
||||||
return dynamic_symtab;
|
|
||||||
} else {
|
|
||||||
return std::move(res).unwrap_error();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<std::size_t Bits>
|
|
||||||
Result<optional<elf::symtab_info>, internal_error> elf::get_symtab_impl(bool dynamic) {
|
|
||||||
// https://refspecs.linuxfoundation.org/elf/elf.pdf
|
|
||||||
// page 66: only one sht_symtab and sht_dynsym section per file
|
|
||||||
// page 32: symtab spec
|
|
||||||
static_assert(Bits == 32 || Bits == 64, "Unexpected Bits argument");
|
|
||||||
using SymEntry = typename std::conditional<Bits == 32, Elf32_Sym, Elf64_Sym>::type;
|
|
||||||
auto sections_ = get_sections();
|
|
||||||
if(sections_.is_error()) {
|
|
||||||
return std::move(sections_).unwrap_error();
|
|
||||||
}
|
|
||||||
const auto& sections = sections_.unwrap_value();
|
|
||||||
optional<symtab_info> symbol_table;
|
|
||||||
for(const auto& section : sections) {
|
|
||||||
if(section.sh_type == (dynamic ? SHT_DYNSYM : SHT_SYMTAB)) {
|
|
||||||
if(section.sh_entsize != sizeof(SymEntry)) {
|
|
||||||
return internal_error("elf seems corrupted, sym entry mismatch {}", object_path);
|
|
||||||
}
|
|
||||||
if(section.sh_size % section.sh_entsize != 0) {
|
|
||||||
return internal_error("elf seems corrupted, sym entry vs section size mismatch {}", object_path);
|
|
||||||
}
|
|
||||||
std::vector<SymEntry> buffer(section.sh_size / section.sh_entsize);
|
|
||||||
if(std::fseek(file, section.sh_offset, SEEK_SET) != 0) {
|
|
||||||
return internal_error("fseek error while loading elf symbol table");
|
|
||||||
}
|
|
||||||
if(std::fread(buffer.data(), section.sh_entsize, buffer.size(), file) != buffer.size()) {
|
|
||||||
return internal_error("fread error while loading elf symbol table");
|
|
||||||
}
|
|
||||||
symbol_table = symtab_info{};
|
|
||||||
symbol_table.unwrap().entries.reserve(buffer.size());
|
|
||||||
for(const auto& entry : buffer) {
|
|
||||||
symtab_entry normalized;
|
|
||||||
normalized.st_name = byteswap_if_needed(entry.st_name);
|
|
||||||
normalized.st_info = byteswap_if_needed(entry.st_info);
|
|
||||||
normalized.st_other = byteswap_if_needed(entry.st_other);
|
|
||||||
normalized.st_shndx = byteswap_if_needed(entry.st_shndx);
|
|
||||||
normalized.st_value = byteswap_if_needed(entry.st_value);
|
|
||||||
normalized.st_size = byteswap_if_needed(entry.st_size);
|
|
||||||
symbol_table.unwrap().entries.push_back(normalized);
|
|
||||||
}
|
|
||||||
std::sort(
|
|
||||||
symbol_table.unwrap().entries.begin(),
|
|
||||||
symbol_table.unwrap().entries.end(),
|
|
||||||
[] (const symtab_entry& a, const symtab_entry& b) {
|
|
||||||
return a.st_value < b.st_value;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
symbol_table.unwrap().strtab_link = section.sh_link;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return symbol_table;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<maybe_owned<elf>, internal_error> open_elf_cached(const std::string& object_path) {
|
|
||||||
if(get_cache_mode() == cache_mode::prioritize_memory) {
|
|
||||||
return elf::open_elf(object_path)
|
|
||||||
.transform([](elf&& obj) { return maybe_owned<elf>{detail::make_unique<elf>(std::move(obj))}; });
|
|
||||||
} else {
|
|
||||||
std::mutex m;
|
|
||||||
std::unique_lock<std::mutex> lock{m};
|
|
||||||
// TODO: Re-evaluate storing the error
|
|
||||||
static std::unordered_map<std::string, Result<elf, internal_error>> cache;
|
|
||||||
auto it = cache.find(object_path);
|
|
||||||
if(it == cache.end()) {
|
|
||||||
auto res = cache.insert({ object_path, elf::open_elf(object_path) });
|
|
||||||
VERIFY(res.second);
|
|
||||||
it = res.first;
|
|
||||||
}
|
|
||||||
return it->second.transform([](elf& obj) { return maybe_owned<elf>(&obj); });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@ -1,129 +0,0 @@
|
|||||||
#ifndef ELF_HPP
|
|
||||||
#define ELF_HPP
|
|
||||||
|
|
||||||
#include "utils/common.hpp"
|
|
||||||
#include "utils/utils.hpp"
|
|
||||||
|
|
||||||
#if IS_LINUX
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <string>
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
namespace cpptrace {
|
|
||||||
namespace detail {
|
|
||||||
class elf {
|
|
||||||
file_wrapper file;
|
|
||||||
std::string object_path;
|
|
||||||
bool is_little_endian;
|
|
||||||
bool is_64;
|
|
||||||
|
|
||||||
struct header_info {
|
|
||||||
uint64_t e_phoff;
|
|
||||||
uint32_t e_phnum;
|
|
||||||
uint32_t e_phentsize;
|
|
||||||
uint64_t e_shoff;
|
|
||||||
uint32_t e_shnum;
|
|
||||||
uint32_t e_shentsize;
|
|
||||||
};
|
|
||||||
bool tried_to_load_header = false;
|
|
||||||
optional<header_info> header;
|
|
||||||
|
|
||||||
struct section_info {
|
|
||||||
uint32_t sh_type;
|
|
||||||
uint64_t sh_addr;
|
|
||||||
uint64_t sh_offset;
|
|
||||||
uint64_t sh_size;
|
|
||||||
uint64_t sh_entsize;
|
|
||||||
uint32_t sh_link;
|
|
||||||
};
|
|
||||||
bool tried_to_load_sections = false;
|
|
||||||
bool did_load_sections = false;
|
|
||||||
std::vector<section_info> sections;
|
|
||||||
|
|
||||||
struct strtab_entry {
|
|
||||||
bool tried_to_load_strtab = false;
|
|
||||||
bool did_load_strtab = false;
|
|
||||||
std::vector<char> data;
|
|
||||||
};
|
|
||||||
std::unordered_map<std::size_t, strtab_entry> strtab_entries;
|
|
||||||
|
|
||||||
struct symtab_entry {
|
|
||||||
uint32_t st_name;
|
|
||||||
unsigned char st_info;
|
|
||||||
unsigned char st_other;
|
|
||||||
uint16_t st_shndx;
|
|
||||||
uint64_t st_value;
|
|
||||||
uint64_t st_size;
|
|
||||||
};
|
|
||||||
struct symtab_info {
|
|
||||||
std::vector<symtab_entry> entries;
|
|
||||||
std::size_t strtab_link = 0;
|
|
||||||
};
|
|
||||||
bool tried_to_load_symtab = false;
|
|
||||||
bool did_load_symtab = false;
|
|
||||||
optional<symtab_info> symtab;
|
|
||||||
|
|
||||||
bool tried_to_load_dynamic_symtab = false;
|
|
||||||
bool did_load_dynamic_symtab = false;
|
|
||||||
optional<symtab_info> dynamic_symtab;
|
|
||||||
|
|
||||||
elf(file_wrapper file, const std::string& object_path, bool is_little_endian, bool is_64);
|
|
||||||
|
|
||||||
public:
|
|
||||||
static NODISCARD Result<elf, internal_error> open_elf(const std::string& object_path);
|
|
||||||
|
|
||||||
elf(elf&&) = default;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Result<std::uintptr_t, internal_error> get_module_image_base();
|
|
||||||
private:
|
|
||||||
template<std::size_t Bits>
|
|
||||||
Result<std::uintptr_t, internal_error> get_module_image_base_impl();
|
|
||||||
|
|
||||||
public:
|
|
||||||
optional<std::string> lookup_symbol(frame_ptr pc);
|
|
||||||
private:
|
|
||||||
optional<std::string> lookup_symbol(frame_ptr pc, const optional<symtab_info>& maybe_symtab);
|
|
||||||
|
|
||||||
public:
|
|
||||||
struct symbol_entry {
|
|
||||||
std::string st_name;
|
|
||||||
uint16_t st_shndx;
|
|
||||||
uint64_t st_value;
|
|
||||||
uint64_t st_size;
|
|
||||||
};
|
|
||||||
Result<optional<std::vector<symbol_entry>>, internal_error> get_symtab_entries();
|
|
||||||
Result<optional<std::vector<symbol_entry>>, internal_error> get_dynamic_symtab_entries();
|
|
||||||
private:
|
|
||||||
Result<optional<std::vector<symbol_entry>>, internal_error> resolve_symtab_entries(
|
|
||||||
const Result<const optional<symtab_info> &, internal_error>&
|
|
||||||
);
|
|
||||||
|
|
||||||
private:
|
|
||||||
template<typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
|
|
||||||
T byteswap_if_needed(T value);
|
|
||||||
|
|
||||||
Result<const header_info&, internal_error> get_header_info();
|
|
||||||
template<std::size_t Bits>
|
|
||||||
Result<const header_info&, internal_error> get_header_info_impl();
|
|
||||||
|
|
||||||
Result<const std::vector<section_info>&, internal_error> get_sections();
|
|
||||||
template<std::size_t Bits>
|
|
||||||
Result<const std::vector<section_info>&, internal_error> get_sections_impl();
|
|
||||||
|
|
||||||
Result<const std::vector<char>&, internal_error> get_strtab(std::size_t index);
|
|
||||||
|
|
||||||
Result<const optional<symtab_info>&, internal_error> get_symtab();
|
|
||||||
Result<const optional<symtab_info>&, internal_error> get_dynamic_symtab();
|
|
||||||
template<std::size_t Bits>
|
|
||||||
Result<optional<symtab_info>, internal_error> get_symtab_impl(bool dynamic);
|
|
||||||
};
|
|
||||||
|
|
||||||
NODISCARD Result<maybe_owned<elf>, internal_error> open_elf_cached(const std::string& object_path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@ -1,699 +0,0 @@
|
|||||||
#include "binary/mach-o.hpp"
|
|
||||||
|
|
||||||
#include "utils/common.hpp"
|
|
||||||
#include "utils/utils.hpp"
|
|
||||||
|
|
||||||
#if IS_APPLE
|
|
||||||
|
|
||||||
// A number of mach-o functions are deprecated as of macos 13
|
|
||||||
#pragma GCC diagnostic push
|
|
||||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
|
||||||
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cstring>
|
|
||||||
#include <mutex>
|
|
||||||
#include <string>
|
|
||||||
#include <type_traits>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <iomanip>
|
|
||||||
|
|
||||||
#include <mach-o/loader.h>
|
|
||||||
#include <mach-o/swap.h>
|
|
||||||
#include <mach-o/fat.h>
|
|
||||||
#include <crt_externs.h>
|
|
||||||
#include <mach-o/nlist.h>
|
|
||||||
#include <mach-o/stab.h>
|
|
||||||
#include <mach-o/arch.h>
|
|
||||||
|
|
||||||
namespace cpptrace {
|
|
||||||
namespace detail {
|
|
||||||
bool is_mach_o(std::uint32_t magic) {
|
|
||||||
switch(magic) {
|
|
||||||
case FAT_MAGIC:
|
|
||||||
case FAT_CIGAM:
|
|
||||||
case MH_MAGIC:
|
|
||||||
case MH_CIGAM:
|
|
||||||
case MH_MAGIC_64:
|
|
||||||
case MH_CIGAM_64:
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool file_is_mach_o(const std::string& object_path) noexcept {
|
|
||||||
auto file = raii_wrap(std::fopen(object_path.c_str(), "rb"), file_deleter);
|
|
||||||
if(file == nullptr) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
auto magic = load_bytes<std::uint32_t>(file, 0);
|
|
||||||
if(magic) {
|
|
||||||
return is_mach_o(magic.unwrap_value());
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_fat_magic(std::uint32_t magic) {
|
|
||||||
return magic == FAT_MAGIC || magic == FAT_CIGAM;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Based on https://github.com/AlexDenisov/segment_dumper/blob/master/main.c
|
|
||||||
// and https://lowlevelbits.org/parsing-mach-o-files/
|
|
||||||
bool is_magic_64(std::uint32_t magic) {
|
|
||||||
return magic == MH_MAGIC_64 || magic == MH_CIGAM_64;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool should_swap_bytes(std::uint32_t magic) {
|
|
||||||
return magic == MH_CIGAM || magic == MH_CIGAM_64 || magic == FAT_CIGAM;
|
|
||||||
}
|
|
||||||
|
|
||||||
void swap_mach_header(mach_header_64& header) {
|
|
||||||
swap_mach_header_64(&header, NX_UnknownByteOrder);
|
|
||||||
}
|
|
||||||
|
|
||||||
void swap_mach_header(mach_header& header) {
|
|
||||||
swap_mach_header(&header, NX_UnknownByteOrder);
|
|
||||||
}
|
|
||||||
|
|
||||||
void swap_segment_command(segment_command_64& segment) {
|
|
||||||
swap_segment_command_64(&segment, NX_UnknownByteOrder);
|
|
||||||
}
|
|
||||||
|
|
||||||
void swap_segment_command(segment_command& segment) {
|
|
||||||
swap_segment_command(&segment, NX_UnknownByteOrder);
|
|
||||||
}
|
|
||||||
|
|
||||||
void swap_nlist(struct nlist& entry) {
|
|
||||||
swap_nlist(&entry, 1, NX_UnknownByteOrder);
|
|
||||||
}
|
|
||||||
|
|
||||||
void swap_nlist(struct nlist_64& entry) {
|
|
||||||
swap_nlist_64(&entry, 1, NX_UnknownByteOrder);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef __LP64__
|
|
||||||
#define LP(x) x##_64
|
|
||||||
#else
|
|
||||||
#define LP(x) x
|
|
||||||
#endif
|
|
||||||
|
|
||||||
Result<const char*, internal_error> mach_o::symtab_info_data::get_string(std::size_t index) const {
|
|
||||||
if(stringtab && index < symtab.strsize) {
|
|
||||||
return stringtab.unwrap().data() + index;
|
|
||||||
} else {
|
|
||||||
return internal_error("can't retrieve symbol from symtab");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<monostate, internal_error> mach_o::load() {
|
|
||||||
if(magic == FAT_MAGIC || magic == FAT_CIGAM) {
|
|
||||||
return load_fat_mach();
|
|
||||||
} else {
|
|
||||||
fat_index = 0;
|
|
||||||
if(is_magic_64(magic)) {
|
|
||||||
return load_mach<64>();
|
|
||||||
} else {
|
|
||||||
return load_mach<32>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<mach_o, internal_error> mach_o::open_mach_o(const std::string& object_path) {
|
|
||||||
auto file = raii_wrap(std::fopen(object_path.c_str(), "rb"), file_deleter);
|
|
||||||
if(file == nullptr) {
|
|
||||||
return internal_error("Unable to read object file {}", object_path);
|
|
||||||
}
|
|
||||||
auto magic = load_bytes<std::uint32_t>(file, 0);
|
|
||||||
if(!magic) {
|
|
||||||
return magic.unwrap_error();
|
|
||||||
}
|
|
||||||
if(!is_mach_o(magic.unwrap_value())) {
|
|
||||||
return internal_error("File is not mach-o {}", object_path);
|
|
||||||
}
|
|
||||||
mach_o obj(std::move(file), object_path, magic.unwrap_value());
|
|
||||||
auto result = obj.load();
|
|
||||||
if(result.is_error()) {
|
|
||||||
return result.unwrap_error();
|
|
||||||
} else {
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<std::uintptr_t, internal_error> mach_o::get_text_vmaddr() {
|
|
||||||
for(const auto& command : load_commands) {
|
|
||||||
if(command.cmd == LC_SEGMENT_64 || command.cmd == LC_SEGMENT) {
|
|
||||||
auto segment = command.cmd == LC_SEGMENT_64
|
|
||||||
? load_segment_command<64>(command.file_offset)
|
|
||||||
: load_segment_command<32>(command.file_offset);
|
|
||||||
if(segment.is_error()) {
|
|
||||||
return std::move(segment).unwrap_error();
|
|
||||||
}
|
|
||||||
if(std::strcmp(segment.unwrap_value().segname, "__TEXT") == 0) {
|
|
||||||
return segment.unwrap_value().vmaddr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// somehow no __TEXT section was found...
|
|
||||||
return internal_error("Couldn't find __TEXT section while parsing Mach-O object");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::size_t mach_o::get_fat_index() const {
|
|
||||||
VERIFY(fat_index != std::numeric_limits<std::size_t>::max());
|
|
||||||
return fat_index;
|
|
||||||
}
|
|
||||||
|
|
||||||
void mach_o::print_segments() const {
|
|
||||||
int i = 0;
|
|
||||||
for(const auto& command : load_commands) {
|
|
||||||
if(command.cmd == LC_SEGMENT_64 || command.cmd == LC_SEGMENT) {
|
|
||||||
auto segment_load = command.cmd == LC_SEGMENT_64
|
|
||||||
? load_segment_command<64>(command.file_offset)
|
|
||||||
: load_segment_command<32>(command.file_offset);
|
|
||||||
fprintf(stderr, "Load command %d\n", i);
|
|
||||||
if(segment_load.is_error()) {
|
|
||||||
fprintf(stderr, " error\n");
|
|
||||||
segment_load.drop_error();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
auto& segment = segment_load.unwrap_value();
|
|
||||||
fprintf(stderr, " cmd %u\n", segment.cmd);
|
|
||||||
fprintf(stderr, " cmdsize %u\n", segment.cmdsize);
|
|
||||||
fprintf(stderr, " segname %s\n", segment.segname);
|
|
||||||
fprintf(stderr, " vmaddr 0x%llx\n", segment.vmaddr);
|
|
||||||
fprintf(stderr, " vmsize 0x%llx\n", segment.vmsize);
|
|
||||||
fprintf(stderr, " off 0x%llx\n", segment.fileoff);
|
|
||||||
fprintf(stderr, " filesize %llu\n", segment.filesize);
|
|
||||||
fprintf(stderr, " nsects %u\n", segment.nsects);
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<std::reference_wrapper<optional<mach_o::symtab_info_data>>, internal_error> mach_o::get_symtab_info() {
|
|
||||||
if(!symtab_info.has_value() && !tried_to_load_symtab) {
|
|
||||||
// don't try to load the symtab again if for some reason loading here fails
|
|
||||||
tried_to_load_symtab = true;
|
|
||||||
for(const auto& command : load_commands) {
|
|
||||||
if(command.cmd == LC_SYMTAB) {
|
|
||||||
symtab_info_data info;
|
|
||||||
auto symtab = load_symbol_table_command(command.file_offset);
|
|
||||||
if(!symtab) {
|
|
||||||
return std::move(symtab).unwrap_error();
|
|
||||||
}
|
|
||||||
info.symtab = symtab.unwrap_value();
|
|
||||||
auto string = load_string_table(info.symtab.stroff, info.symtab.strsize);
|
|
||||||
if(!string) {
|
|
||||||
return std::move(string).unwrap_error();
|
|
||||||
}
|
|
||||||
info.stringtab = std::move(string).unwrap_value();
|
|
||||||
symtab_info = std::move(info);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return std::reference_wrapper<optional<symtab_info_data>>{symtab_info};
|
|
||||||
}
|
|
||||||
|
|
||||||
void mach_o::print_symbol_table_entry(
|
|
||||||
const nlist_64& entry,
|
|
||||||
const char* stringtab,
|
|
||||||
std::size_t stringsize,
|
|
||||||
std::size_t j
|
|
||||||
) const {
|
|
||||||
const char* type = "";
|
|
||||||
if(entry.n_type & N_STAB) {
|
|
||||||
switch(entry.n_type) {
|
|
||||||
case N_SO: type = "N_SO"; break;
|
|
||||||
case N_OSO: type = "N_OSO"; break;
|
|
||||||
case N_BNSYM: type = "N_BNSYM"; break;
|
|
||||||
case N_ENSYM: type = "N_ENSYM"; break;
|
|
||||||
case N_FUN: type = "N_FUN"; break;
|
|
||||||
}
|
|
||||||
} else if((entry.n_type & N_TYPE) == N_SECT) {
|
|
||||||
type = "N_SECT";
|
|
||||||
}
|
|
||||||
fprintf(
|
|
||||||
stderr,
|
|
||||||
"%5llu %8llx %2llx %7s %2llu %4llx %16llx %s\n",
|
|
||||||
to_ull(j),
|
|
||||||
to_ull(entry.n_un.n_strx),
|
|
||||||
to_ull(entry.n_type),
|
|
||||||
type,
|
|
||||||
to_ull(entry.n_sect),
|
|
||||||
to_ull(entry.n_desc),
|
|
||||||
to_ull(entry.n_value),
|
|
||||||
stringtab == nullptr
|
|
||||||
? "Stringtab error"
|
|
||||||
: entry.n_un.n_strx < stringsize
|
|
||||||
? stringtab + entry.n_un.n_strx
|
|
||||||
: "String index out of bounds"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void mach_o::print_symbol_table() {
|
|
||||||
int i = 0;
|
|
||||||
for(const auto& command : load_commands) {
|
|
||||||
if(command.cmd == LC_SYMTAB) {
|
|
||||||
auto symtab_load = load_symbol_table_command(command.file_offset);
|
|
||||||
fprintf(stderr, "Load command %d\n", i);
|
|
||||||
if(symtab_load.is_error()) {
|
|
||||||
fprintf(stderr, " error\n");
|
|
||||||
symtab_load.drop_error();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
auto& symtab = symtab_load.unwrap_value();
|
|
||||||
fprintf(stderr, " cmd %llu\n", to_ull(symtab.cmd));
|
|
||||||
fprintf(stderr, " cmdsize %llu\n", to_ull(symtab.cmdsize));
|
|
||||||
fprintf(stderr, " symoff 0x%llu\n", to_ull(symtab.symoff));
|
|
||||||
fprintf(stderr, " nsyms %llu\n", to_ull(symtab.nsyms));
|
|
||||||
fprintf(stderr, " stroff 0x%llu\n", to_ull(symtab.stroff));
|
|
||||||
fprintf(stderr, " strsize %llu\n", to_ull(symtab.strsize));
|
|
||||||
auto stringtab = load_string_table(symtab.stroff, symtab.strsize);
|
|
||||||
if(!stringtab) {
|
|
||||||
stringtab.drop_error();
|
|
||||||
}
|
|
||||||
for(std::size_t j = 0; j < symtab.nsyms; j++) {
|
|
||||||
auto entry = bits == 32
|
|
||||||
? load_symtab_entry<32>(symtab.symoff, j)
|
|
||||||
: load_symtab_entry<64>(symtab.symoff, j);
|
|
||||||
if(!entry) {
|
|
||||||
fprintf(stderr, "error loading symtab entry\n");
|
|
||||||
entry.drop_error();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
print_symbol_table_entry(
|
|
||||||
entry.unwrap_value(),
|
|
||||||
stringtab ? stringtab.unwrap_value().data() : nullptr,
|
|
||||||
symtab.strsize,
|
|
||||||
j
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// produce information similar to dsymutil -dump-debug-map
|
|
||||||
Result<mach_o::debug_map, internal_error> mach_o::get_debug_map() {
|
|
||||||
// we have a bunch of symbols in our binary we need to pair up with symbols from various .o files
|
|
||||||
// first collect symbols and the objects they come from
|
|
||||||
debug_map debug_map;
|
|
||||||
auto symtab_info_res = get_symtab_info();
|
|
||||||
if(!symtab_info_res) {
|
|
||||||
return std::move(symtab_info_res).unwrap_error();
|
|
||||||
}
|
|
||||||
if(!symtab_info_res.unwrap_value().get()) {
|
|
||||||
return internal_error("No symtab info");
|
|
||||||
}
|
|
||||||
const auto& symtab_info = symtab_info_res.unwrap_value().get().unwrap();
|
|
||||||
const auto& symtab = symtab_info.symtab;
|
|
||||||
// TODO: Take timestamp into account?
|
|
||||||
std::string current_module;
|
|
||||||
optional<debug_map_entry> current_function;
|
|
||||||
for(std::size_t j = 0; j < symtab.nsyms; j++) {
|
|
||||||
auto load_entry = bits == 32
|
|
||||||
? load_symtab_entry<32>(symtab.symoff, j)
|
|
||||||
: load_symtab_entry<64>(symtab.symoff, j);
|
|
||||||
if(!load_entry) {
|
|
||||||
return std::move(load_entry).unwrap_error();
|
|
||||||
}
|
|
||||||
auto& entry = load_entry.unwrap_value();
|
|
||||||
// entry.n_type & N_STAB indicates symbolic debug info
|
|
||||||
if(!(entry.n_type & N_STAB)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
switch(entry.n_type) {
|
|
||||||
case N_SO:
|
|
||||||
// pass - these encode path and filename for the module, if applicable
|
|
||||||
break;
|
|
||||||
case N_OSO:
|
|
||||||
{
|
|
||||||
// sets the module
|
|
||||||
auto str = symtab_info.get_string(entry.n_un.n_strx);
|
|
||||||
if(!str) {
|
|
||||||
return std::move(str).unwrap_error();
|
|
||||||
}
|
|
||||||
current_module = str.unwrap_value();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case N_BNSYM: break; // pass
|
|
||||||
case N_ENSYM: break; // pass
|
|
||||||
case N_FUN:
|
|
||||||
{
|
|
||||||
auto str = symtab_info.get_string(entry.n_un.n_strx);
|
|
||||||
if(!str) {
|
|
||||||
return std::move(str).unwrap_error();
|
|
||||||
}
|
|
||||||
if(str.unwrap_value()[0] == 0) {
|
|
||||||
// end of function scope
|
|
||||||
if(!current_function) { /**/ }
|
|
||||||
current_function.unwrap().size = entry.n_value;
|
|
||||||
debug_map[current_module].push_back(std::move(current_function).unwrap());
|
|
||||||
} else {
|
|
||||||
current_function = debug_map_entry{};
|
|
||||||
current_function.unwrap().source_address = entry.n_value;
|
|
||||||
current_function.unwrap().name = str.unwrap_value();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return debug_map;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<const std::vector<mach_o::symbol_entry>&, internal_error> mach_o::symbol_table() {
|
|
||||||
if(symbols) {
|
|
||||||
return symbols.unwrap();
|
|
||||||
}
|
|
||||||
if(tried_to_load_symbols) {
|
|
||||||
return internal_error("previous symbol table load failed");
|
|
||||||
}
|
|
||||||
tried_to_load_symbols = true;
|
|
||||||
std::vector<symbol_entry> symbol_table;
|
|
||||||
// we have a bunch of symbols in our binary we need to pair up with symbols from various .o files
|
|
||||||
// first collect symbols and the objects they come from
|
|
||||||
auto symtab_info_res = get_symtab_info();
|
|
||||||
if(!symtab_info_res) {
|
|
||||||
return std::move(symtab_info_res).unwrap_error();
|
|
||||||
}
|
|
||||||
if(!symtab_info_res.unwrap_value().get()) {
|
|
||||||
return internal_error("No symtab info");
|
|
||||||
}
|
|
||||||
const auto& symtab_info = symtab_info_res.unwrap_value().get().unwrap();
|
|
||||||
const auto& symtab = symtab_info.symtab;
|
|
||||||
// TODO: Take timestamp into account?
|
|
||||||
for(std::size_t j = 0; j < symtab.nsyms; j++) {
|
|
||||||
auto load_entry = bits == 32
|
|
||||||
? load_symtab_entry<32>(symtab.symoff, j)
|
|
||||||
: load_symtab_entry<64>(symtab.symoff, j);
|
|
||||||
if(!load_entry) {
|
|
||||||
return std::move(load_entry).unwrap_error();
|
|
||||||
}
|
|
||||||
auto& entry = load_entry.unwrap_value();
|
|
||||||
if(entry.n_type & N_STAB) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if((entry.n_type & N_TYPE) == N_SECT) {
|
|
||||||
auto str = symtab_info.get_string(entry.n_un.n_strx);
|
|
||||||
if(!str) {
|
|
||||||
return std::move(str).unwrap_error();
|
|
||||||
}
|
|
||||||
symbol_table.push_back({
|
|
||||||
entry.n_value,
|
|
||||||
str.unwrap_value()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::sort(
|
|
||||||
symbol_table.begin(),
|
|
||||||
symbol_table.end(),
|
|
||||||
[] (const symbol_entry& a, const symbol_entry& b) { return a.address < b.address; }
|
|
||||||
);
|
|
||||||
symbols = std::move(symbol_table);
|
|
||||||
return symbols.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
optional<std::string> mach_o::lookup_symbol(frame_ptr pc) {
|
|
||||||
auto symtab_ = symbol_table();
|
|
||||||
if(!symtab_) {
|
|
||||||
return nullopt;
|
|
||||||
}
|
|
||||||
const auto& symtab = symtab_.unwrap_value();;
|
|
||||||
auto it = first_less_than_or_equal(
|
|
||||||
symtab.begin(),
|
|
||||||
symtab.end(),
|
|
||||||
pc,
|
|
||||||
[] (frame_ptr pc, const symbol_entry& entry) {
|
|
||||||
return pc < entry.address;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
if(it == symtab.end()) {
|
|
||||||
return nullopt;
|
|
||||||
}
|
|
||||||
ASSERT(pc >= it->address);
|
|
||||||
// TODO: We subtracted one from the address so name + diff won't show up in the objdump, decide if desirable
|
|
||||||
// to have an easier offset to lookup
|
|
||||||
return microfmt::format("{} + {}", it->name, pc - it->address);
|
|
||||||
}
|
|
||||||
|
|
||||||
// produce information similar to dsymutil -dump-debug-map
|
|
||||||
void mach_o::print_debug_map(const debug_map& debug_map) {
|
|
||||||
for(const auto& entry : debug_map) {
|
|
||||||
std::cout<<entry.first<<": "<< '\n';
|
|
||||||
for(const auto& symbol : entry.second) {
|
|
||||||
std::cerr
|
|
||||||
<< " "
|
|
||||||
<< symbol.name
|
|
||||||
<< " "
|
|
||||||
<< std::hex
|
|
||||||
<< symbol.source_address
|
|
||||||
<< " "
|
|
||||||
<< symbol.size
|
|
||||||
<< std::dec
|
|
||||||
<< '\n';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<std::size_t Bits>
|
|
||||||
Result<monostate, internal_error> mach_o::load_mach() {
|
|
||||||
static_assert(Bits == 32 || Bits == 64, "Unexpected Bits argument");
|
|
||||||
bits = Bits;
|
|
||||||
using Mach_Header = typename std::conditional<Bits == 32, mach_header, mach_header_64>::type;
|
|
||||||
std::size_t header_size = sizeof(Mach_Header);
|
|
||||||
auto load_header = load_bytes<Mach_Header>(file, load_base);
|
|
||||||
if(!load_header) {
|
|
||||||
return load_header.unwrap_error();
|
|
||||||
}
|
|
||||||
Mach_Header& header = load_header.unwrap_value();
|
|
||||||
magic = header.magic;
|
|
||||||
if(should_swap()) {
|
|
||||||
swap_mach_header(header);
|
|
||||||
}
|
|
||||||
cputype = header.cputype;
|
|
||||||
cpusubtype = header.cpusubtype;
|
|
||||||
filetype = header.filetype;
|
|
||||||
n_load_commands = header.ncmds;
|
|
||||||
sizeof_load_commands = header.sizeofcmds;
|
|
||||||
flags = header.flags;
|
|
||||||
// handle load commands
|
|
||||||
std::uint32_t ncmds = header.ncmds;
|
|
||||||
std::uint32_t load_commands_offset = load_base + header_size;
|
|
||||||
// iterate load commands
|
|
||||||
std::uint32_t actual_offset = load_commands_offset;
|
|
||||||
for(std::uint32_t i = 0; i < ncmds; i++) {
|
|
||||||
auto load_cmd = load_bytes<load_command>(file, actual_offset);
|
|
||||||
if(!load_cmd) {
|
|
||||||
return load_cmd.unwrap_error();
|
|
||||||
}
|
|
||||||
load_command& cmd = load_cmd.unwrap_value();
|
|
||||||
if(should_swap()) {
|
|
||||||
swap_load_command(&cmd, NX_UnknownByteOrder);
|
|
||||||
}
|
|
||||||
load_commands.push_back({ actual_offset, cmd.cmd, cmd.cmdsize });
|
|
||||||
actual_offset += cmd.cmdsize;
|
|
||||||
}
|
|
||||||
return monostate{};
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<monostate, internal_error> mach_o::load_fat_mach() {
|
|
||||||
std::size_t header_size = sizeof(fat_header);
|
|
||||||
std::size_t arch_size = sizeof(fat_arch);
|
|
||||||
auto load_header = load_bytes<fat_header>(file, 0);
|
|
||||||
if(!load_header) {
|
|
||||||
return load_header.unwrap_error();
|
|
||||||
}
|
|
||||||
fat_header& header = load_header.unwrap_value();
|
|
||||||
if(should_swap()) {
|
|
||||||
swap_fat_header(&header, NX_UnknownByteOrder);
|
|
||||||
}
|
|
||||||
// thread_local static struct LP(mach_header)* mhp = _NSGetMachExecuteHeader();
|
|
||||||
// off_t arch_offset = (off_t)header_size;
|
|
||||||
// for(std::size_t i = 0; i < header.nfat_arch; i++) {
|
|
||||||
// fat_arch arch = load_bytes<fat_arch>(file, arch_offset);
|
|
||||||
// if(should_swap()) {
|
|
||||||
// swap_fat_arch(&arch, 1, NX_UnknownByteOrder);
|
|
||||||
// }
|
|
||||||
// off_t mach_header_offset = (off_t)arch.offset;
|
|
||||||
// arch_offset += arch_size;
|
|
||||||
// std::uint32_t magic = load_bytes<std::uint32_t>(file, mach_header_offset);
|
|
||||||
// std::cerr<<"xxx: "<<arch.cputype<<" : "<<mhp->cputype<<std::endl;
|
|
||||||
// std::cerr<<" "<<arch.cpusubtype<<" : "<<static_cast<cpu_subtype_t>(mhp->cpusubtype & ~CPU_SUBTYPE_MASK)<<std::endl;
|
|
||||||
// if(
|
|
||||||
// arch.cputype == mhp->cputype &&
|
|
||||||
// static_cast<cpu_subtype_t>(mhp->cpusubtype & ~CPU_SUBTYPE_MASK) == arch.cpusubtype
|
|
||||||
// ) {
|
|
||||||
// load_base = mach_header_offset;
|
|
||||||
// fat_index = i;
|
|
||||||
// if(is_magic_64(magic)) {
|
|
||||||
// load_mach<64>(true);
|
|
||||||
// } else {
|
|
||||||
// load_mach<32>(true);
|
|
||||||
// }
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
std::vector<fat_arch> fat_arches;
|
|
||||||
fat_arches.reserve(header.nfat_arch);
|
|
||||||
off_t arch_offset = (off_t)header_size;
|
|
||||||
for(std::size_t i = 0; i < header.nfat_arch; i++) {
|
|
||||||
auto load_arch = load_bytes<fat_arch>(file, arch_offset);
|
|
||||||
if(!load_arch) {
|
|
||||||
return load_arch.unwrap_error();
|
|
||||||
}
|
|
||||||
fat_arch& arch = load_arch.unwrap_value();
|
|
||||||
if(should_swap()) {
|
|
||||||
swap_fat_arch(&arch, 1, NX_UnknownByteOrder);
|
|
||||||
}
|
|
||||||
fat_arches.push_back(arch);
|
|
||||||
arch_offset += arch_size;
|
|
||||||
}
|
|
||||||
thread_local static struct LP(mach_header)* mhp = _NSGetMachExecuteHeader();
|
|
||||||
fat_arch* best = NXFindBestFatArch(
|
|
||||||
mhp->cputype,
|
|
||||||
mhp->cpusubtype,
|
|
||||||
fat_arches.data(),
|
|
||||||
header.nfat_arch
|
|
||||||
);
|
|
||||||
if(best) {
|
|
||||||
off_t mach_header_offset = (off_t)best->offset;
|
|
||||||
auto magic = load_bytes<std::uint32_t>(file, mach_header_offset);
|
|
||||||
if(!magic) {
|
|
||||||
return magic.unwrap_error();
|
|
||||||
}
|
|
||||||
load_base = mach_header_offset;
|
|
||||||
fat_index = best - fat_arches.data();
|
|
||||||
if(is_magic_64(magic.unwrap_value())) {
|
|
||||||
load_mach<64>();
|
|
||||||
} else {
|
|
||||||
load_mach<32>();
|
|
||||||
}
|
|
||||||
return monostate{};
|
|
||||||
}
|
|
||||||
// If this is reached... something went wrong. The cpu we're on wasn't found.
|
|
||||||
return internal_error("Couldn't find appropriate architecture in fat Mach-O");
|
|
||||||
}
|
|
||||||
|
|
||||||
template<std::size_t Bits>
|
|
||||||
Result<segment_command_64, internal_error> mach_o::load_segment_command(std::uint32_t offset) const {
|
|
||||||
using Segment_Command = typename std::conditional<Bits == 32, segment_command, segment_command_64>::type;
|
|
||||||
auto load_segment = load_bytes<Segment_Command>(file, offset);
|
|
||||||
if(!load_segment) {
|
|
||||||
return load_segment.unwrap_error();
|
|
||||||
}
|
|
||||||
Segment_Command& segment = load_segment.unwrap_value();
|
|
||||||
ASSERT(segment.cmd == LC_SEGMENT_64 || segment.cmd == LC_SEGMENT);
|
|
||||||
if(should_swap()) {
|
|
||||||
swap_segment_command(segment);
|
|
||||||
}
|
|
||||||
// fields match just u64 instead of u32
|
|
||||||
segment_command_64 common;
|
|
||||||
common.cmd = segment.cmd;
|
|
||||||
common.cmdsize = segment.cmdsize;
|
|
||||||
static_assert(sizeof common.segname == 16 && sizeof segment.segname == 16, "xx");
|
|
||||||
memcpy(common.segname, segment.segname, 16);
|
|
||||||
common.vmaddr = segment.vmaddr;
|
|
||||||
common.vmsize = segment.vmsize;
|
|
||||||
common.fileoff = segment.fileoff;
|
|
||||||
common.filesize = segment.filesize;
|
|
||||||
common.maxprot = segment.maxprot;
|
|
||||||
common.initprot = segment.initprot;
|
|
||||||
common.nsects = segment.nsects;
|
|
||||||
common.flags = segment.flags;
|
|
||||||
return common;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<symtab_command, internal_error> mach_o::load_symbol_table_command(std::uint32_t offset) const {
|
|
||||||
auto load_symtab = load_bytes<symtab_command>(file, offset);
|
|
||||||
if(!load_symtab) {
|
|
||||||
return load_symtab.unwrap_error();
|
|
||||||
}
|
|
||||||
symtab_command& symtab = load_symtab.unwrap_value();
|
|
||||||
ASSERT(symtab.cmd == LC_SYMTAB);
|
|
||||||
if(should_swap()) {
|
|
||||||
swap_symtab_command(&symtab, NX_UnknownByteOrder);
|
|
||||||
}
|
|
||||||
return symtab;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<std::size_t Bits>
|
|
||||||
Result<nlist_64, internal_error> mach_o::load_symtab_entry(std::uint32_t symbol_base, std::size_t index) const {
|
|
||||||
using Nlist = typename std::conditional<Bits == 32, struct nlist, struct nlist_64>::type;
|
|
||||||
uint32_t offset = load_base + symbol_base + index * sizeof(Nlist);
|
|
||||||
auto load_entry = load_bytes<Nlist>(file, offset);
|
|
||||||
if(!load_entry) {
|
|
||||||
return load_entry.unwrap_error();
|
|
||||||
}
|
|
||||||
Nlist& entry = load_entry.unwrap_value();
|
|
||||||
if(should_swap()) {
|
|
||||||
swap_nlist(entry);
|
|
||||||
}
|
|
||||||
// fields match just u64 instead of u32
|
|
||||||
nlist_64 common;
|
|
||||||
common.n_un.n_strx = entry.n_un.n_strx;
|
|
||||||
common.n_type = entry.n_type;
|
|
||||||
common.n_sect = entry.n_sect;
|
|
||||||
common.n_desc = entry.n_desc;
|
|
||||||
common.n_value = entry.n_value;
|
|
||||||
return common;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<std::vector<char>, internal_error> mach_o::load_string_table(std::uint32_t offset, std::uint32_t byte_count) const {
|
|
||||||
std::vector<char> buffer(byte_count + 1);
|
|
||||||
if(std::fseek(file, load_base + offset, SEEK_SET) != 0) {
|
|
||||||
return internal_error("fseek error while loading mach-o symbol table");
|
|
||||||
}
|
|
||||||
if(std::fread(buffer.data(), sizeof(char), byte_count, file) != byte_count) {
|
|
||||||
return internal_error("fread error while loading mach-o symbol table");
|
|
||||||
}
|
|
||||||
buffer[byte_count] = 0; // just out of an abundance of caution
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool mach_o::should_swap() const {
|
|
||||||
return should_swap_bytes(magic);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<bool, internal_error> macho_is_fat(const std::string& object_path) {
|
|
||||||
auto file = raii_wrap(std::fopen(object_path.c_str(), "rb"), file_deleter);
|
|
||||||
if(file == nullptr) {
|
|
||||||
return internal_error("Unable to read object file {}", object_path);
|
|
||||||
}
|
|
||||||
auto magic = load_bytes<std::uint32_t>(file, 0);
|
|
||||||
if(!magic) {
|
|
||||||
return magic.unwrap_error();
|
|
||||||
} else {
|
|
||||||
return is_fat_magic(magic.unwrap_value());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<maybe_owned<mach_o>, internal_error> open_mach_o_cached(const std::string& object_path) {
|
|
||||||
if(get_cache_mode() == cache_mode::prioritize_memory) {
|
|
||||||
return mach_o::open_mach_o(object_path)
|
|
||||||
.transform([](mach_o&& obj) {
|
|
||||||
return maybe_owned<mach_o>{detail::make_unique<mach_o>(std::move(obj))};
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
std::mutex m;
|
|
||||||
std::unique_lock<std::mutex> lock{m};
|
|
||||||
// TODO: Re-evaluate storing the error
|
|
||||||
static std::unordered_map<std::string, Result<mach_o, internal_error>> cache;
|
|
||||||
auto it = cache.find(object_path);
|
|
||||||
if(it == cache.end()) {
|
|
||||||
auto res = cache.insert({ object_path, mach_o::open_mach_o(object_path) });
|
|
||||||
VERIFY(res.second);
|
|
||||||
it = res.first;
|
|
||||||
}
|
|
||||||
return it->second.transform([](mach_o& obj) { return maybe_owned<mach_o>(&obj); });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma GCC diagnostic pop
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@ -1,147 +0,0 @@
|
|||||||
#ifndef MACHO_HPP
|
|
||||||
#define MACHO_HPP
|
|
||||||
|
|
||||||
#include "utils/common.hpp"
|
|
||||||
#include "utils/utils.hpp"
|
|
||||||
|
|
||||||
#if IS_APPLE
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <limits>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <mach-o/arch.h>
|
|
||||||
#include <mach-o/loader.h>
|
|
||||||
#include <mach-o/nlist.h>
|
|
||||||
|
|
||||||
namespace cpptrace {
|
|
||||||
namespace detail {
|
|
||||||
bool file_is_mach_o(const std::string& object_path) noexcept;
|
|
||||||
|
|
||||||
struct load_command_entry {
|
|
||||||
std::uint32_t file_offset;
|
|
||||||
std::uint32_t cmd;
|
|
||||||
std::uint32_t cmdsize;
|
|
||||||
};
|
|
||||||
|
|
||||||
class mach_o {
|
|
||||||
public:
|
|
||||||
struct debug_map_entry {
|
|
||||||
uint64_t source_address;
|
|
||||||
uint64_t size;
|
|
||||||
std::string name;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct symbol_entry {
|
|
||||||
uint64_t address;
|
|
||||||
std::string name;
|
|
||||||
};
|
|
||||||
|
|
||||||
// map from object file to a vector of symbols to resolve
|
|
||||||
using debug_map = std::unordered_map<std::string, std::vector<debug_map_entry>>;
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
file_wrapper file;
|
|
||||||
std::string object_path;
|
|
||||||
std::uint32_t magic;
|
|
||||||
cpu_type_t cputype;
|
|
||||||
cpu_subtype_t cpusubtype;
|
|
||||||
std::uint32_t filetype;
|
|
||||||
std::uint32_t n_load_commands;
|
|
||||||
std::uint32_t sizeof_load_commands;
|
|
||||||
std::uint32_t flags;
|
|
||||||
std::size_t bits = 0; // 32 or 64 once load_mach is called
|
|
||||||
|
|
||||||
std::size_t load_base = 0;
|
|
||||||
std::size_t fat_index = std::numeric_limits<std::size_t>::max();
|
|
||||||
|
|
||||||
std::vector<load_command_entry> load_commands;
|
|
||||||
|
|
||||||
struct symtab_info_data {
|
|
||||||
symtab_command symtab;
|
|
||||||
optional<std::vector<char>> stringtab;
|
|
||||||
Result<const char*, internal_error> get_string(std::size_t index) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool tried_to_load_symtab = false;
|
|
||||||
optional<symtab_info_data> symtab_info;
|
|
||||||
|
|
||||||
bool tried_to_load_symbols = false;
|
|
||||||
optional<std::vector<symbol_entry>> symbols;
|
|
||||||
|
|
||||||
mach_o(
|
|
||||||
file_wrapper file,
|
|
||||||
const std::string& object_path,
|
|
||||||
std::uint32_t magic
|
|
||||||
) :
|
|
||||||
file(std::move(file)),
|
|
||||||
object_path(object_path),
|
|
||||||
magic(magic) {}
|
|
||||||
|
|
||||||
Result<monostate, internal_error> load();
|
|
||||||
|
|
||||||
public:
|
|
||||||
static NODISCARD Result<mach_o, internal_error> open_mach_o(const std::string& object_path);
|
|
||||||
|
|
||||||
mach_o(mach_o&&) = default;
|
|
||||||
~mach_o() = default;
|
|
||||||
|
|
||||||
Result<std::uintptr_t, internal_error> get_text_vmaddr();
|
|
||||||
|
|
||||||
std::size_t get_fat_index() const;
|
|
||||||
|
|
||||||
void print_segments() const;
|
|
||||||
|
|
||||||
Result<std::reference_wrapper<optional<symtab_info_data>>, internal_error> get_symtab_info();
|
|
||||||
|
|
||||||
void print_symbol_table_entry(
|
|
||||||
const nlist_64& entry,
|
|
||||||
const char* stringtab,
|
|
||||||
std::size_t stringsize,
|
|
||||||
std::size_t j
|
|
||||||
) const;
|
|
||||||
|
|
||||||
void print_symbol_table();
|
|
||||||
|
|
||||||
// produce information similar to dsymutil -dump-debug-map
|
|
||||||
Result<debug_map, internal_error> get_debug_map();
|
|
||||||
|
|
||||||
Result<const std::vector<symbol_entry>&, internal_error> symbol_table();
|
|
||||||
|
|
||||||
optional<std::string> lookup_symbol(frame_ptr pc);
|
|
||||||
|
|
||||||
// produce information similar to dsymutil -dump-debug-map
|
|
||||||
static void print_debug_map(const debug_map& debug_map);
|
|
||||||
|
|
||||||
private:
|
|
||||||
template<std::size_t Bits>
|
|
||||||
Result<monostate, internal_error> load_mach();
|
|
||||||
|
|
||||||
Result<monostate, internal_error> load_fat_mach();
|
|
||||||
|
|
||||||
template<std::size_t Bits>
|
|
||||||
Result<segment_command_64, internal_error> load_segment_command(std::uint32_t offset) const;
|
|
||||||
|
|
||||||
Result<symtab_command, internal_error> load_symbol_table_command(std::uint32_t offset) const;
|
|
||||||
|
|
||||||
template<std::size_t Bits>
|
|
||||||
Result<nlist_64, internal_error> load_symtab_entry(std::uint32_t symbol_base, std::size_t index) const;
|
|
||||||
|
|
||||||
Result<std::vector<char>, internal_error> load_string_table(std::uint32_t offset, std::uint32_t byte_count) const;
|
|
||||||
|
|
||||||
bool should_swap() const;
|
|
||||||
};
|
|
||||||
|
|
||||||
Result<bool, internal_error> macho_is_fat(const std::string& object_path);
|
|
||||||
|
|
||||||
NODISCARD Result<maybe_owned<mach_o>, internal_error> open_mach_o_cached(const std::string& object_path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@ -1,97 +0,0 @@
|
|||||||
#include "binary/module_base.hpp"
|
|
||||||
|
|
||||||
#include "platform/platform.hpp"
|
|
||||||
#include "utils/utils.hpp"
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <mutex>
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
#if IS_LINUX || IS_APPLE
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <dlfcn.h>
|
|
||||||
#if IS_APPLE
|
|
||||||
#include "binary/mach-o.hpp"
|
|
||||||
#else
|
|
||||||
#include "binary/elf.hpp"
|
|
||||||
#endif
|
|
||||||
#elif IS_WINDOWS
|
|
||||||
#include "binary/pe.hpp"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace cpptrace {
|
|
||||||
namespace detail {
|
|
||||||
#if IS_LINUX
|
|
||||||
Result<std::uintptr_t, internal_error> get_module_image_base(const std::string& object_path) {
|
|
||||||
static std::mutex mutex;
|
|
||||||
std::lock_guard<std::mutex> lock(mutex);
|
|
||||||
static std::unordered_map<std::string, std::uintptr_t> cache;
|
|
||||||
auto it = cache.find(object_path);
|
|
||||||
if(it == cache.end()) {
|
|
||||||
// arguably it'd be better to release the lock while computing this, but also arguably it's good to not
|
|
||||||
// have two threads try to do the same computation
|
|
||||||
auto elf_object = open_elf_cached(object_path);
|
|
||||||
// TODO: Cache the error
|
|
||||||
if(!elf_object) {
|
|
||||||
return elf_object.unwrap_error();
|
|
||||||
}
|
|
||||||
auto base = elf_object.unwrap_value()->get_module_image_base();
|
|
||||||
if(base.is_error()) {
|
|
||||||
return std::move(base).unwrap_error();
|
|
||||||
}
|
|
||||||
cache.insert(it, {object_path, base.unwrap_value()});
|
|
||||||
return base;
|
|
||||||
} else {
|
|
||||||
return it->second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#elif IS_APPLE
|
|
||||||
Result<std::uintptr_t, internal_error> get_module_image_base(const std::string& object_path) {
|
|
||||||
// We have to parse the Mach-O to find the offset of the text section.....
|
|
||||||
// I don't know how addresses are handled if there is more than one __TEXT load command. I'm assuming for
|
|
||||||
// now that there is only one, and I'm using only the first section entry within that load command.
|
|
||||||
static std::mutex mutex;
|
|
||||||
std::lock_guard<std::mutex> lock(mutex);
|
|
||||||
static std::unordered_map<std::string, std::uintptr_t> cache;
|
|
||||||
auto it = cache.find(object_path);
|
|
||||||
if(it == cache.end()) {
|
|
||||||
// arguably it'd be better to release the lock while computing this, but also arguably it's good to not
|
|
||||||
// have two threads try to do the same computation
|
|
||||||
auto mach_o_object = open_mach_o_cached(object_path);
|
|
||||||
// TODO: Cache the error
|
|
||||||
if(!mach_o_object) {
|
|
||||||
return mach_o_object.unwrap_error();
|
|
||||||
}
|
|
||||||
auto base = mach_o_object.unwrap_value()->get_text_vmaddr();
|
|
||||||
if(!base) {
|
|
||||||
return std::move(base).unwrap_error();
|
|
||||||
}
|
|
||||||
cache.insert(it, {object_path, base.unwrap_value()});
|
|
||||||
return base;
|
|
||||||
} else {
|
|
||||||
return it->second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else // Windows
|
|
||||||
Result<std::uintptr_t, internal_error> get_module_image_base(const std::string& object_path) {
|
|
||||||
static std::mutex mutex;
|
|
||||||
std::lock_guard<std::mutex> lock(mutex);
|
|
||||||
static std::unordered_map<std::string, std::uintptr_t> cache;
|
|
||||||
auto it = cache.find(object_path);
|
|
||||||
if(it == cache.end()) {
|
|
||||||
// arguably it'd be better to release the lock while computing this, but also arguably it's good to not
|
|
||||||
// have two threads try to do the same computation
|
|
||||||
auto base = pe_get_module_image_base(object_path);
|
|
||||||
// TODO: Cache the error
|
|
||||||
if(!base) {
|
|
||||||
return std::move(base).unwrap_error();
|
|
||||||
}
|
|
||||||
cache.insert(it, {object_path, base.unwrap_value()});
|
|
||||||
return base;
|
|
||||||
} else {
|
|
||||||
return it->second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
#ifndef IMAGE_MODULE_BASE_HPP
|
|
||||||
#define IMAGE_MODULE_BASE_HPP
|
|
||||||
|
|
||||||
#include "utils/utils.hpp"
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace cpptrace {
|
|
||||||
namespace detail {
|
|
||||||
Result<std::uintptr_t, internal_error> get_module_image_base(const std::string& object_path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@ -1,189 +0,0 @@
|
|||||||
#include "binary/object.hpp"
|
|
||||||
|
|
||||||
#include "platform/platform.hpp"
|
|
||||||
#include "utils/utils.hpp"
|
|
||||||
#include "binary/module_base.hpp"
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <system_error>
|
|
||||||
#include <vector>
|
|
||||||
#include <mutex>
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
#if IS_LINUX || IS_APPLE
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <dlfcn.h>
|
|
||||||
#if IS_LINUX
|
|
||||||
#include <link.h> // needed for dladdr1's link_map info
|
|
||||||
#endif
|
|
||||||
#elif IS_WINDOWS
|
|
||||||
#ifndef WIN32_LEAN_AND_MEAN
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
|
||||||
#endif
|
|
||||||
#include <windows.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace cpptrace {
|
|
||||||
namespace detail {
|
|
||||||
#if IS_LINUX || IS_APPLE
|
|
||||||
#if defined(CPPTRACE_HAS_DL_FIND_OBJECT) || defined(CPPTRACE_HAS_DLADDR1)
|
|
||||||
std::string resolve_l_name(const char* l_name) {
|
|
||||||
if(l_name != nullptr && l_name[0] != 0) {
|
|
||||||
return l_name;
|
|
||||||
} else {
|
|
||||||
// empty l_name, this means it's the currently running executable
|
|
||||||
// TODO: Caching and proper handling
|
|
||||||
char buffer[CPPTRACE_PATH_MAX + 1]{};
|
|
||||||
auto res = readlink("/proc/self/exe", buffer, CPPTRACE_PATH_MAX);
|
|
||||||
if(res == -1) {
|
|
||||||
return ""; // TODO
|
|
||||||
} else {
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
// dladdr queries are needed to get pre-ASLR addresses and targets to run symbol resolution on
|
|
||||||
// _dl_find_object is preferred if at all possible as it is much faster (added in glibc 2.35)
|
|
||||||
// dladdr1 is preferred if possible because it allows for a more accurate object path to be resolved (glibc 2.3.3)
|
|
||||||
#ifdef CPPTRACE_HAS_DL_FIND_OBJECT // we don't even check for this on apple
|
|
||||||
object_frame get_frame_object_info(frame_ptr address) {
|
|
||||||
// Use _dl_find_object when we can, it's orders of magnitude faster
|
|
||||||
object_frame frame;
|
|
||||||
frame.raw_address = address;
|
|
||||||
frame.object_address = 0;
|
|
||||||
dl_find_object result;
|
|
||||||
if(_dl_find_object(reinterpret_cast<void*>(address), &result) == 0) { // thread safe
|
|
||||||
frame.object_path = resolve_l_name(result.dlfo_link_map->l_name);
|
|
||||||
frame.object_address = address - to_frame_ptr(result.dlfo_link_map->l_addr);
|
|
||||||
}
|
|
||||||
return frame;
|
|
||||||
}
|
|
||||||
#elif defined(CPPTRACE_HAS_DLADDR1)
|
|
||||||
object_frame get_frame_object_info(frame_ptr address) {
|
|
||||||
// https://github.com/bminor/glibc/blob/91695ee4598b39d181ab8df579b888a8863c4cab/elf/dl-addr.c#L26
|
|
||||||
Dl_info info;
|
|
||||||
link_map* link_map_info;
|
|
||||||
object_frame frame;
|
|
||||||
frame.raw_address = address;
|
|
||||||
frame.object_address = 0;
|
|
||||||
if(
|
|
||||||
// thread safe
|
|
||||||
dladdr1(reinterpret_cast<void*>(address), &info, reinterpret_cast<void**>(&link_map_info), RTLD_DL_LINKMAP)
|
|
||||||
) {
|
|
||||||
frame.object_path = resolve_l_name(link_map_info->l_name);
|
|
||||||
auto base = get_module_image_base(frame.object_path);
|
|
||||||
if(base.has_value()) {
|
|
||||||
frame.object_address = address
|
|
||||||
- reinterpret_cast<std::uintptr_t>(info.dli_fbase)
|
|
||||||
+ base.unwrap_value();
|
|
||||||
} else {
|
|
||||||
if(!should_absorb_trace_exceptions()) {
|
|
||||||
base.drop_error();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return frame;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
// glibc dladdr may not return an accurate dli_fname as it uses argv[0] for addresses in the main executable
|
|
||||||
// https://github.com/bminor/glibc/blob/caed1f5c0b2e31b5f4e0f21fea4b2c9ecd3b5b30/elf/dl-addr.c#L33-L36
|
|
||||||
// macos doesn't have dladdr1 but its dli_fname behaves more sensibly, same with some other libc's like musl
|
|
||||||
object_frame get_frame_object_info(frame_ptr address) {
|
|
||||||
// reference: https://github.com/bminor/glibc/blob/master/debug/backtracesyms.c
|
|
||||||
Dl_info info;
|
|
||||||
object_frame frame;
|
|
||||||
frame.raw_address = address;
|
|
||||||
frame.object_address = 0;
|
|
||||||
if(dladdr(reinterpret_cast<void*>(address), &info)) { // thread safe
|
|
||||||
frame.object_path = info.dli_fname;
|
|
||||||
auto base = get_module_image_base(info.dli_fname);
|
|
||||||
if(base.has_value()) {
|
|
||||||
frame.object_address = address
|
|
||||||
- reinterpret_cast<std::uintptr_t>(info.dli_fbase)
|
|
||||||
+ base.unwrap_value();
|
|
||||||
} else {
|
|
||||||
if(!should_absorb_trace_exceptions()) {
|
|
||||||
base.drop_error();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return frame;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
std::string get_module_name(HMODULE handle) {
|
|
||||||
static std::mutex mutex;
|
|
||||||
std::lock_guard<std::mutex> lock(mutex);
|
|
||||||
static std::unordered_map<HMODULE, std::string> cache;
|
|
||||||
auto it = cache.find(handle);
|
|
||||||
if(it == cache.end()) {
|
|
||||||
char path[MAX_PATH];
|
|
||||||
if(GetModuleFileNameA(handle, path, sizeof(path))) {
|
|
||||||
cache.insert(it, {handle, path});
|
|
||||||
return path;
|
|
||||||
} else {
|
|
||||||
std::fprintf(stderr, "%s\n", std::system_error(GetLastError(), std::system_category()).what());
|
|
||||||
cache.insert(it, {handle, ""});
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return it->second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object_frame get_frame_object_info(frame_ptr address) {
|
|
||||||
object_frame frame;
|
|
||||||
frame.raw_address = address;
|
|
||||||
frame.object_address = 0;
|
|
||||||
HMODULE handle;
|
|
||||||
// Multithread safe as long as another thread doesn't come along and free the module
|
|
||||||
if(GetModuleHandleExA(
|
|
||||||
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
|
|
||||||
reinterpret_cast<const char*>(address),
|
|
||||||
&handle
|
|
||||||
)) {
|
|
||||||
frame.object_path = get_module_name(handle);
|
|
||||||
auto base = get_module_image_base(frame.object_path);
|
|
||||||
if(base.has_value()) {
|
|
||||||
frame.object_address = address
|
|
||||||
- reinterpret_cast<std::uintptr_t>(handle)
|
|
||||||
+ base.unwrap_value();
|
|
||||||
} else {
|
|
||||||
if(!should_absorb_trace_exceptions()) {
|
|
||||||
base.drop_error();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
std::fprintf(stderr, "%s\n", std::system_error(GetLastError(), std::system_category()).what());
|
|
||||||
}
|
|
||||||
return frame;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
std::vector<object_frame> get_frames_object_info(const std::vector<frame_ptr>& addresses) {
|
|
||||||
std::vector<object_frame> frames;
|
|
||||||
frames.reserve(addresses.size());
|
|
||||||
for(const frame_ptr address : addresses) {
|
|
||||||
frames.push_back(get_frame_object_info(address));
|
|
||||||
}
|
|
||||||
return frames;
|
|
||||||
}
|
|
||||||
|
|
||||||
object_frame resolve_safe_object_frame(const safe_object_frame& frame) {
|
|
||||||
std::string object_path = frame.object_path;
|
|
||||||
if(object_path.empty()) {
|
|
||||||
return {
|
|
||||||
frame.raw_address,
|
|
||||||
0,
|
|
||||||
""
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
frame.raw_address,
|
|
||||||
frame.address_relative_to_object_start,
|
|
||||||
std::move(object_path)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
#ifndef OBJECT_HPP
|
|
||||||
#define OBJECT_HPP
|
|
||||||
|
|
||||||
#include <cpptrace/forward.hpp>
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
namespace cpptrace {
|
|
||||||
|
|
||||||
namespace detail {
|
|
||||||
object_frame get_frame_object_info(frame_ptr address);
|
|
||||||
|
|
||||||
std::vector<object_frame> get_frames_object_info(const std::vector<frame_ptr>& addresses);
|
|
||||||
|
|
||||||
object_frame resolve_safe_object_frame(const safe_object_frame& frame);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@ -1,97 +0,0 @@
|
|||||||
#include "binary/pe.hpp"
|
|
||||||
|
|
||||||
#include "platform/platform.hpp"
|
|
||||||
#include "utils/error.hpp"
|
|
||||||
#include "utils/utils.hpp"
|
|
||||||
|
|
||||||
#if IS_WINDOWS
|
|
||||||
#include <array>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cstring>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#ifndef WIN32_LEAN_AND_MEAN
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
|
||||||
#endif
|
|
||||||
#include <windows.h>
|
|
||||||
|
|
||||||
namespace cpptrace {
|
|
||||||
namespace detail {
|
|
||||||
template<typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
|
|
||||||
T pe_byteswap_if_needed(T value) {
|
|
||||||
// PE header values are little endian, I think dos e_lfanew should be too
|
|
||||||
if(!is_little_endian()) {
|
|
||||||
return byteswap(value);
|
|
||||||
} else {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<std::uintptr_t, internal_error> pe_get_module_image_base(const std::string& object_path) {
|
|
||||||
// https://drive.google.com/file/d/0B3_wGJkuWLytbnIxY1J5WUs4MEk/view?pli=1&resourcekey=0-n5zZ2UW39xVTH8ZSu6C2aQ
|
|
||||||
// https://0xrick.github.io/win-internals/pe3/
|
|
||||||
// Endianness should always be little for dos and pe headers
|
|
||||||
std::FILE* file_ptr;
|
|
||||||
errno_t ret = fopen_s(&file_ptr, object_path.c_str(), "rb");
|
|
||||||
auto file = raii_wrap(std::move(file_ptr), file_deleter);
|
|
||||||
if(ret != 0 || file == nullptr) {
|
|
||||||
return internal_error("Unable to read object file {}", object_path);
|
|
||||||
}
|
|
||||||
auto magic = load_bytes<std::array<char, 2>>(file, 0);
|
|
||||||
if(!magic) {
|
|
||||||
return std::move(magic).unwrap_error();
|
|
||||||
}
|
|
||||||
if(std::memcmp(magic.unwrap_value().data(), "MZ", 2) != 0) {
|
|
||||||
return internal_error("File is not a PE file {}", object_path);
|
|
||||||
}
|
|
||||||
auto e_lfanew = load_bytes<DWORD>(file, 0x3c); // dos header + 0x3c
|
|
||||||
if(!e_lfanew) {
|
|
||||||
return std::move(e_lfanew).unwrap_error();
|
|
||||||
}
|
|
||||||
DWORD nt_header_offset = pe_byteswap_if_needed(e_lfanew.unwrap_value());
|
|
||||||
auto signature = load_bytes<std::array<char, 4>>(file, nt_header_offset); // nt header + 0
|
|
||||||
if(!signature) {
|
|
||||||
return std::move(signature).unwrap_error();
|
|
||||||
}
|
|
||||||
if(std::memcmp(signature.unwrap_value().data(), "PE\0\0", 4) != 0) {
|
|
||||||
return internal_error("File is not a PE file {}", object_path);
|
|
||||||
}
|
|
||||||
auto size_of_optional_header_raw = load_bytes<WORD>(file, nt_header_offset + 4 + 0x10); // file header + 0x10
|
|
||||||
if(!size_of_optional_header_raw) {
|
|
||||||
return std::move(size_of_optional_header_raw).unwrap_error();
|
|
||||||
}
|
|
||||||
WORD size_of_optional_header = pe_byteswap_if_needed(size_of_optional_header_raw.unwrap_value());
|
|
||||||
if(size_of_optional_header == 0) {
|
|
||||||
return internal_error("Unexpected optional header size for PE file");
|
|
||||||
}
|
|
||||||
auto optional_header_magic_raw = load_bytes<WORD>(file, nt_header_offset + 0x18); // optional header + 0x0
|
|
||||||
if(!optional_header_magic_raw) {
|
|
||||||
return std::move(optional_header_magic_raw).unwrap_error();
|
|
||||||
}
|
|
||||||
WORD optional_header_magic = pe_byteswap_if_needed(optional_header_magic_raw.unwrap_value());
|
|
||||||
VERIFY(
|
|
||||||
optional_header_magic == IMAGE_NT_OPTIONAL_HDR_MAGIC,
|
|
||||||
("PE file does not match expected bit-mode " + object_path).c_str()
|
|
||||||
);
|
|
||||||
// finally get image base
|
|
||||||
if(optional_header_magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
|
|
||||||
// 32 bit
|
|
||||||
auto bytes = load_bytes<DWORD>(file, nt_header_offset + 0x18 + 0x1c); // optional header + 0x1c
|
|
||||||
if(!bytes) {
|
|
||||||
return std::move(bytes).unwrap_error();
|
|
||||||
}
|
|
||||||
return to<std::uintptr_t>(pe_byteswap_if_needed(bytes.unwrap_value()));
|
|
||||||
} else {
|
|
||||||
// 64 bit
|
|
||||||
// I get an "error: 'QWORD' was not declared in this scope" for some reason when using QWORD
|
|
||||||
auto bytes = load_bytes<unsigned __int64>(file, nt_header_offset + 0x18 + 0x18); // optional header + 0x18
|
|
||||||
if(!bytes) {
|
|
||||||
return std::move(bytes).unwrap_error();
|
|
||||||
}
|
|
||||||
return to<std::uintptr_t>(pe_byteswap_if_needed(bytes.unwrap_value()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
#ifndef PE_HPP
|
|
||||||
#define PE_HPP
|
|
||||||
|
|
||||||
#include "platform/platform.hpp"
|
|
||||||
#include "utils/utils.hpp"
|
|
||||||
|
|
||||||
#if IS_WINDOWS
|
|
||||||
#include <cstdint>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace cpptrace {
|
|
||||||
namespace detail {
|
|
||||||
Result<std::uintptr_t, internal_error> pe_get_module_image_base(const std::string& object_path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@ -1,76 +0,0 @@
|
|||||||
#include "binary/safe_dl.hpp"
|
|
||||||
|
|
||||||
#include "utils/common.hpp"
|
|
||||||
#include "utils/utils.hpp"
|
|
||||||
#include "platform/program_name.hpp"
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include <mutex>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <cstring>
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
#ifdef CPPTRACE_HAS_DL_FIND_OBJECT
|
|
||||||
#if IS_LINUX || IS_APPLE
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <dlfcn.h>
|
|
||||||
#include <link.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace cpptrace {
|
|
||||||
namespace detail {
|
|
||||||
void get_safe_object_frame(frame_ptr address, safe_object_frame* out) {
|
|
||||||
out->raw_address = address;
|
|
||||||
dl_find_object result;
|
|
||||||
if(_dl_find_object(reinterpret_cast<void*>(address), &result) == 0) { // thread-safe, signal-safe
|
|
||||||
out->address_relative_to_object_start = address - to_frame_ptr(result.dlfo_link_map->l_addr);
|
|
||||||
if(result.dlfo_link_map->l_name != nullptr && result.dlfo_link_map->l_name[0] != 0) {
|
|
||||||
std::size_t path_length = std::strlen(result.dlfo_link_map->l_name);
|
|
||||||
std::memcpy(
|
|
||||||
out->object_path,
|
|
||||||
result.dlfo_link_map->l_name,
|
|
||||||
std::min(path_length + 1, std::size_t(CPPTRACE_PATH_MAX + 1))
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// empty l_name, this means it's the currently running executable
|
|
||||||
memset(out->object_path, 0, CPPTRACE_PATH_MAX + 1);
|
|
||||||
// signal-safe
|
|
||||||
auto res = readlink("/proc/self/exe", out->object_path, CPPTRACE_PATH_MAX);
|
|
||||||
if(res == -1) {
|
|
||||||
// error handling?
|
|
||||||
}
|
|
||||||
// TODO: Special handling for /proc/pid/exe unlink edge case
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// std::cout<<"error"<<std::endl;
|
|
||||||
out->address_relative_to_object_start = 0;
|
|
||||||
out->object_path[0] = 0;
|
|
||||||
}
|
|
||||||
// TODO: Handle this part of the documentation?
|
|
||||||
// The address can be a code address or data address. On architectures using function descriptors, no attempt is
|
|
||||||
// made to decode the function descriptor. Depending on how these descriptors are implemented, _dl_find_object
|
|
||||||
// may return the object that defines the function descriptor (and not the object that contains the code
|
|
||||||
// implementing the function), or fail to find any object at all.
|
|
||||||
}
|
|
||||||
|
|
||||||
bool has_get_safe_object_frame() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
namespace cpptrace {
|
|
||||||
namespace detail {
|
|
||||||
void get_safe_object_frame(frame_ptr address, safe_object_frame* out) {
|
|
||||||
out->raw_address = address;
|
|
||||||
out->address_relative_to_object_start = 0;
|
|
||||||
out->object_path[0] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool has_get_safe_object_frame() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
@ -1,14 +0,0 @@
|
|||||||
#ifndef SAFE_DL_HPP
|
|
||||||
#define SAFE_DL_HPP
|
|
||||||
|
|
||||||
#include "utils/common.hpp"
|
|
||||||
|
|
||||||
namespace cpptrace {
|
|
||||||
namespace detail {
|
|
||||||
void get_safe_object_frame(frame_ptr address, safe_object_frame* out);
|
|
||||||
|
|
||||||
bool has_get_safe_object_frame();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
411
src/cpptrace.cpp
411
src/cpptrace.cpp
@ -1,340 +1,103 @@
|
|||||||
#include <cpptrace/cpptrace.hpp>
|
#include <cpptrace/cpptrace.hpp>
|
||||||
#include <cpptrace/formatting.hpp>
|
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstdio>
|
|
||||||
#include <cstring>
|
|
||||||
#include <iostream>
|
|
||||||
#include <sstream>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#if !(defined(CPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE) || defined(CPPTRACE_FULL_TRACE_WITH_STACKTRACE))
|
||||||
|
|
||||||
#include "cpptrace/basic.hpp"
|
|
||||||
#include "symbols/symbols.hpp"
|
#include "symbols/symbols.hpp"
|
||||||
#include "unwind/unwind.hpp"
|
#include "unwind/unwind.hpp"
|
||||||
#include "demangle/demangle.hpp"
|
#include "demangle/demangle.hpp"
|
||||||
#include "utils/common.hpp"
|
#include "platform/common.hpp"
|
||||||
#include "utils/microfmt.hpp"
|
|
||||||
#include "utils/utils.hpp"
|
|
||||||
#include "binary/object.hpp"
|
|
||||||
#include "binary/safe_dl.hpp"
|
|
||||||
#include "snippets/snippet.hpp"
|
|
||||||
#include "options.hpp"
|
|
||||||
|
|
||||||
namespace cpptrace {
|
namespace cpptrace {
|
||||||
CPPTRACE_FORCE_NO_INLINE
|
CPPTRACE_FORCE_NO_INLINE
|
||||||
raw_trace raw_trace::current(std::size_t skip) {
|
std::vector<stacktrace_frame> generate_trace(std::uint32_t skip) {
|
||||||
try { // try/catch can never be hit but it's needed to prevent TCO
|
std::vector<void*> frames = detail::capture_frames(skip + 1);
|
||||||
return generate_raw_trace(skip + 1);
|
detail::symbolizer symbolizer;
|
||||||
} catch(...) {
|
std::vector<stacktrace_frame> trace = symbolizer.resolve_frames(frames);
|
||||||
if(!detail::should_absorb_trace_exceptions()) {
|
for(auto& frame : trace) {
|
||||||
throw;
|
frame.symbol = detail::demangle(frame.symbol);
|
||||||
}
|
}
|
||||||
return raw_trace{};
|
return trace;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
// full trace
|
||||||
|
|
||||||
|
#include "full/full_trace.hpp"
|
||||||
|
#include "demangle/demangle.hpp"
|
||||||
|
|
||||||
|
namespace cpptrace {
|
||||||
|
CPPTRACE_FORCE_NO_INLINE
|
||||||
|
std::vector<stacktrace_frame> generate_trace(std::uint32_t skip) {
|
||||||
|
auto trace = detail::generate_trace(skip + 1);
|
||||||
|
for(auto& entry : trace) {
|
||||||
|
entry.symbol = detail::demangle(entry.symbol);
|
||||||
|
}
|
||||||
|
return trace;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define ESC "\033["
|
||||||
|
#define RESET ESC "0m"
|
||||||
|
#define RED ESC "31m"
|
||||||
|
#define GREEN ESC "32m"
|
||||||
|
#define YELLOW ESC "33m"
|
||||||
|
#define BLUE ESC "34m"
|
||||||
|
#define MAGENTA ESC "35m"
|
||||||
|
#define CYAN ESC "36m"
|
||||||
|
|
||||||
|
namespace cpptrace {
|
||||||
|
void print_trace(std::uint32_t skip) {
|
||||||
|
enable_virtual_terminal_processing_if_needed();
|
||||||
|
std::cerr<<"Stack trace (most recent call first):"<<std::endl;
|
||||||
|
std::size_t counter = 0;
|
||||||
|
const auto trace = generate_trace(skip + 1);
|
||||||
|
if(trace.empty()) {
|
||||||
|
std::cerr<<"<empty trace>"<<std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto frame_number_width = n_digits(static_cast<int>(trace.size()) - 1);
|
||||||
|
for(const auto& frame : trace) {
|
||||||
|
std::cerr
|
||||||
|
<< '#'
|
||||||
|
<< std::setw(static_cast<int>(frame_number_width))
|
||||||
|
<< std::left
|
||||||
|
<< counter++
|
||||||
|
<< std::right
|
||||||
|
<< " "
|
||||||
|
<< std::hex
|
||||||
|
<< BLUE
|
||||||
|
<< "0x"
|
||||||
|
<< std::setw(2 * sizeof(uintptr_t))
|
||||||
|
<< std::setfill('0')
|
||||||
|
<< frame.address
|
||||||
|
<< std::dec
|
||||||
|
<< std::setfill(' ')
|
||||||
|
<< RESET
|
||||||
|
<< " in "
|
||||||
|
<< YELLOW
|
||||||
|
<< frame.symbol
|
||||||
|
<< RESET
|
||||||
|
<< " at "
|
||||||
|
<< GREEN
|
||||||
|
<< frame.filename
|
||||||
|
<< RESET
|
||||||
|
<< ":"
|
||||||
|
<< BLUE
|
||||||
|
<< frame.line
|
||||||
|
<< RESET
|
||||||
|
<< (frame.col > 0 ? ":" BLUE + std::to_string(frame.col) + RESET : "")
|
||||||
|
<< std::endl;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
CPPTRACE_FORCE_NO_INLINE
|
|
||||||
raw_trace raw_trace::current(std::size_t skip, std::size_t max_depth) {
|
|
||||||
try { // try/catch can never be hit but it's needed to prevent TCO
|
|
||||||
return generate_raw_trace(skip + 1, max_depth);
|
|
||||||
} catch(...) {
|
|
||||||
if(!detail::should_absorb_trace_exceptions()) {
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
return raw_trace{};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object_trace raw_trace::resolve_object_trace() const {
|
|
||||||
try {
|
|
||||||
return object_trace{detail::get_frames_object_info(frames)};
|
|
||||||
} catch(...) { // NOSONAR
|
|
||||||
if(!detail::should_absorb_trace_exceptions()) {
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
return object_trace{};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stacktrace raw_trace::resolve() const {
|
|
||||||
try {
|
|
||||||
std::vector<stacktrace_frame> trace = detail::resolve_frames(frames);
|
|
||||||
for(auto& frame : trace) {
|
|
||||||
frame.symbol = detail::demangle(frame.symbol, true);
|
|
||||||
}
|
|
||||||
return {std::move(trace)};
|
|
||||||
} catch(...) { // NOSONAR
|
|
||||||
if(!detail::should_absorb_trace_exceptions()) {
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
return stacktrace{};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void raw_trace::clear() {
|
|
||||||
frames.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool raw_trace::empty() const noexcept {
|
|
||||||
return frames.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
CPPTRACE_FORCE_NO_INLINE
|
|
||||||
object_trace object_trace::current(std::size_t skip) {
|
|
||||||
try { // try/catch can never be hit but it's needed to prevent TCO
|
|
||||||
return generate_object_trace(skip + 1);
|
|
||||||
} catch(...) {
|
|
||||||
if(!detail::should_absorb_trace_exceptions()) {
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
return object_trace{};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CPPTRACE_FORCE_NO_INLINE
|
|
||||||
object_trace object_trace::current(std::size_t skip, std::size_t max_depth) {
|
|
||||||
try { // try/catch can never be hit but it's needed to prevent TCO
|
|
||||||
return generate_object_trace(skip + 1, max_depth);
|
|
||||||
} catch(...) {
|
|
||||||
if(!detail::should_absorb_trace_exceptions()) {
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
return object_trace{};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stacktrace object_trace::resolve() const {
|
|
||||||
try {
|
|
||||||
std::vector<stacktrace_frame> trace = detail::resolve_frames(frames);
|
|
||||||
for(auto& frame : trace) {
|
|
||||||
frame.symbol = detail::demangle(frame.symbol, true);
|
|
||||||
}
|
|
||||||
return {std::move(trace)};
|
|
||||||
} catch(...) { // NOSONAR
|
|
||||||
if(!detail::should_absorb_trace_exceptions()) {
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
return stacktrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void object_trace::clear() {
|
|
||||||
frames.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool object_trace::empty() const noexcept {
|
|
||||||
return frames.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
object_frame stacktrace_frame::get_object_info() const {
|
|
||||||
return detail::get_frame_object_info(raw_address);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string stacktrace_frame::to_string() const {
|
|
||||||
return to_string(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string stacktrace_frame::to_string(bool color) const {
|
|
||||||
return get_default_formatter().format(*this, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& stream, const stacktrace_frame& frame) {
|
|
||||||
return stream << frame.to_string();
|
|
||||||
}
|
|
||||||
|
|
||||||
CPPTRACE_FORCE_NO_INLINE
|
|
||||||
stacktrace stacktrace::current(std::size_t skip) {
|
|
||||||
try { // try/catch can never be hit but it's needed to prevent TCO
|
|
||||||
return generate_trace(skip + 1);
|
|
||||||
} catch(...) {
|
|
||||||
if(!detail::should_absorb_trace_exceptions()) {
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
return stacktrace{};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CPPTRACE_FORCE_NO_INLINE
|
|
||||||
stacktrace stacktrace::current(std::size_t skip, std::size_t max_depth) {
|
|
||||||
try { // try/catch can never be hit but it's needed to prevent TCO
|
|
||||||
return generate_trace(skip + 1, max_depth);
|
|
||||||
} catch(...) {
|
|
||||||
if(!detail::should_absorb_trace_exceptions()) {
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
return stacktrace{};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void stacktrace::print() const {
|
|
||||||
get_default_formatter().print(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void stacktrace::print(std::ostream& stream) const {
|
|
||||||
get_default_formatter().print(stream, *this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void stacktrace::print(std::ostream& stream, bool color) const {
|
|
||||||
get_default_formatter().print(stream, *this, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace detail {
|
|
||||||
const formatter& get_default_snippet_formatter() {
|
|
||||||
static formatter snippet_formatter = formatter{}.snippets(true);
|
|
||||||
return snippet_formatter;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void stacktrace::print_with_snippets() const {
|
|
||||||
detail::get_default_snippet_formatter().print(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void stacktrace::print_with_snippets(std::ostream& stream) const {
|
|
||||||
detail::get_default_snippet_formatter().print(stream, *this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void stacktrace::print_with_snippets(std::ostream& stream, bool color) const {
|
|
||||||
detail::get_default_snippet_formatter().print(stream, *this, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
void stacktrace::clear() {
|
|
||||||
frames.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool stacktrace::empty() const noexcept {
|
|
||||||
return frames.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string stacktrace::to_string(bool color) const {
|
|
||||||
return get_default_formatter().format(*this, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& stream, const stacktrace& trace) {
|
|
||||||
get_default_formatter().print(stream, trace);
|
|
||||||
return stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
CPPTRACE_FORCE_NO_INLINE
|
|
||||||
raw_trace generate_raw_trace(std::size_t skip) {
|
|
||||||
try {
|
|
||||||
return raw_trace{detail::capture_frames(skip + 1, SIZE_MAX)};
|
|
||||||
} catch(...) { // NOSONAR
|
|
||||||
if(!detail::should_absorb_trace_exceptions()) {
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
return raw_trace{};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CPPTRACE_FORCE_NO_INLINE
|
|
||||||
raw_trace generate_raw_trace(std::size_t skip, std::size_t max_depth) {
|
|
||||||
try {
|
|
||||||
return raw_trace{detail::capture_frames(skip + 1, max_depth)};
|
|
||||||
} catch(...) { // NOSONAR
|
|
||||||
if(!detail::should_absorb_trace_exceptions()) {
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
return raw_trace{};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CPPTRACE_FORCE_NO_INLINE
|
|
||||||
std::size_t safe_generate_raw_trace(frame_ptr* buffer, std::size_t size, std::size_t skip) {
|
|
||||||
try { // try/catch can never be hit but it's needed to prevent TCO
|
|
||||||
return detail::safe_capture_frames(buffer, size, skip + 1, SIZE_MAX);
|
|
||||||
} catch(...) {
|
|
||||||
if(!detail::should_absorb_trace_exceptions()) {
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CPPTRACE_FORCE_NO_INLINE
|
|
||||||
std::size_t safe_generate_raw_trace(
|
|
||||||
frame_ptr* buffer,
|
|
||||||
std::size_t size,
|
|
||||||
std::size_t skip,
|
|
||||||
std::size_t max_depth
|
|
||||||
) {
|
|
||||||
try { // try/catch can never be hit but it's needed to prevent TCO
|
|
||||||
return detail::safe_capture_frames(buffer, size, skip + 1, max_depth);
|
|
||||||
} catch(...) {
|
|
||||||
if(!detail::should_absorb_trace_exceptions()) {
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CPPTRACE_FORCE_NO_INLINE
|
|
||||||
object_trace generate_object_trace(std::size_t skip) {
|
|
||||||
try {
|
|
||||||
return object_trace{detail::get_frames_object_info(detail::capture_frames(skip + 1, SIZE_MAX))};
|
|
||||||
} catch(...) { // NOSONAR
|
|
||||||
if(!detail::should_absorb_trace_exceptions()) {
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
return object_trace{};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CPPTRACE_FORCE_NO_INLINE
|
|
||||||
object_trace generate_object_trace(std::size_t skip, std::size_t max_depth) {
|
|
||||||
try {
|
|
||||||
return object_trace{detail::get_frames_object_info(detail::capture_frames(skip + 1, max_depth))};
|
|
||||||
} catch(...) { // NOSONAR
|
|
||||||
if(!detail::should_absorb_trace_exceptions()) {
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
return object_trace{};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CPPTRACE_FORCE_NO_INLINE
|
|
||||||
stacktrace generate_trace(std::size_t skip) {
|
|
||||||
try { // try/catch can never be hit but it's needed to prevent TCO
|
|
||||||
return generate_trace(skip + 1, SIZE_MAX);
|
|
||||||
} catch(...) {
|
|
||||||
if(!detail::should_absorb_trace_exceptions()) {
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
return stacktrace{};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CPPTRACE_FORCE_NO_INLINE
|
|
||||||
stacktrace generate_trace(std::size_t skip, std::size_t max_depth) {
|
|
||||||
try {
|
|
||||||
std::vector<frame_ptr> frames = detail::capture_frames(skip + 1, max_depth);
|
|
||||||
std::vector<stacktrace_frame> trace = detail::resolve_frames(frames);
|
|
||||||
for(auto& frame : trace) {
|
|
||||||
frame.symbol = detail::demangle(frame.symbol, true);
|
|
||||||
}
|
|
||||||
return {std::move(trace)};
|
|
||||||
} catch(...) { // NOSONAR
|
|
||||||
if(!detail::should_absorb_trace_exceptions()) {
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
return stacktrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object_frame safe_object_frame::resolve() const {
|
|
||||||
return detail::resolve_safe_object_frame(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void get_safe_object_frame(frame_ptr address, safe_object_frame* out) {
|
|
||||||
detail::get_safe_object_frame(address, out);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool can_signal_safe_unwind() {
|
|
||||||
return detail::has_safe_unwind();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool can_get_safe_object_frame() {
|
|
||||||
return detail::has_get_safe_object_frame();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
446
src/ctrace.cpp
446
src/ctrace.cpp
@ -1,446 +0,0 @@
|
|||||||
#include <ctrace/ctrace.h>
|
|
||||||
#include <cpptrace/cpptrace.hpp>
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
#include "symbols/symbols.hpp"
|
|
||||||
#include "unwind/unwind.hpp"
|
|
||||||
#include "demangle/demangle.hpp"
|
|
||||||
#include "platform/exception_type.hpp"
|
|
||||||
#include "utils/common.hpp"
|
|
||||||
#include "utils/utils.hpp"
|
|
||||||
#include "binary/object.hpp"
|
|
||||||
#include "binary/safe_dl.hpp"
|
|
||||||
|
|
||||||
#define ESC "\033["
|
|
||||||
#define RESET ESC "0m"
|
|
||||||
#define RED ESC "31m"
|
|
||||||
#define GREEN ESC "32m"
|
|
||||||
#define YELLOW ESC "33m"
|
|
||||||
#define BLUE ESC "34m"
|
|
||||||
#define MAGENTA ESC "35m"
|
|
||||||
#define CYAN ESC "36m"
|
|
||||||
|
|
||||||
#if defined(__GNUC__) && ((__GNUC__ > 2) || (__GNUC__ == 2 && __GNUC_MINOR__ >= 6))
|
|
||||||
# define CTRACE_GNU_FORMAT(...) __attribute__((format(__VA_ARGS__)))
|
|
||||||
#elif defined(__clang__)
|
|
||||||
// Probably requires llvm >3.5? Not exactly sure.
|
|
||||||
# define CTRACE_GNU_FORMAT(...) __attribute__((format(__VA_ARGS__)))
|
|
||||||
#else
|
|
||||||
# define CTRACE_GNU_FORMAT(...)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(__clang__)
|
|
||||||
# define CTRACE_FORMAT_PROLOGUE \
|
|
||||||
_Pragma("clang diagnostic push") \
|
|
||||||
_Pragma("clang diagnostic ignored \"-Wformat-security\"")
|
|
||||||
# define CTRACE_FORMAT_EPILOGUE \
|
|
||||||
_Pragma("clang diagnostic pop")
|
|
||||||
#elif defined(__GNUC_MINOR__)
|
|
||||||
# define CTRACE_FORMAT_PROLOGUE \
|
|
||||||
_Pragma("GCC diagnostic push") \
|
|
||||||
_Pragma("GCC diagnostic ignored \"-Wformat-security\"")
|
|
||||||
# define CTRACE_FORMAT_EPILOGUE \
|
|
||||||
_Pragma("GCC diagnostic pop")
|
|
||||||
#else
|
|
||||||
# define CTRACE_FORMAT_PROLOGUE
|
|
||||||
# define CTRACE_FORMAT_EPILOGUE
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace ctrace {
|
|
||||||
static constexpr std::uint32_t invalid_pos = ~0U;
|
|
||||||
|
|
||||||
CTRACE_FORMAT_PROLOGUE
|
|
||||||
template <typename...Args>
|
|
||||||
CTRACE_GNU_FORMAT(printf, 2, 0)
|
|
||||||
static void ffprintf(std::FILE* f, const char fmt[], Args&&...args) {
|
|
||||||
(void)std::fprintf(f, fmt, args...);
|
|
||||||
(void)fflush(f);
|
|
||||||
}
|
|
||||||
CTRACE_FORMAT_EPILOGUE
|
|
||||||
|
|
||||||
static bool is_empty(std::uint32_t pos) noexcept {
|
|
||||||
return pos == invalid_pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool is_empty(const char* str) noexcept {
|
|
||||||
return !str || std::char_traits<char>::length(str) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ctrace_owning_string generate_owning_string(const char* raw_string) noexcept {
|
|
||||||
// Returns length to the null terminator.
|
|
||||||
std::size_t count = std::char_traits<char>::length(raw_string);
|
|
||||||
char* new_string = new char[count + 1];
|
|
||||||
std::char_traits<char>::copy(new_string, raw_string, count);
|
|
||||||
new_string[count] = '\0';
|
|
||||||
return { new_string };
|
|
||||||
}
|
|
||||||
|
|
||||||
static ctrace_owning_string generate_owning_string(const std::string& std_string) {
|
|
||||||
return generate_owning_string(std_string.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
static void free_owning_string(const char* owned_string) noexcept {
|
|
||||||
if(!owned_string) return; // Not necessary but eh
|
|
||||||
delete[] owned_string;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void free_owning_string(ctrace_owning_string& owned_string) noexcept {
|
|
||||||
free_owning_string(owned_string.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ctrace_object_frame convert_object_frame(const cpptrace::object_frame& frame) {
|
|
||||||
const char* new_path = generate_owning_string(frame.object_path).data;
|
|
||||||
return { frame.raw_address, frame.object_address, new_path };
|
|
||||||
}
|
|
||||||
|
|
||||||
static ctrace_object_trace c_convert(const std::vector<cpptrace::object_frame>& trace) {
|
|
||||||
std::size_t count = trace.size();
|
|
||||||
auto* frames = new ctrace_object_frame[count];
|
|
||||||
std::transform(trace.begin(), trace.end(), frames, convert_object_frame);
|
|
||||||
return { frames, count };
|
|
||||||
}
|
|
||||||
|
|
||||||
static ctrace_stacktrace_frame convert_stacktrace_frame(const cpptrace::stacktrace_frame& frame) {
|
|
||||||
ctrace_stacktrace_frame new_frame;
|
|
||||||
new_frame.raw_address = frame.raw_address;
|
|
||||||
new_frame.object_address = frame.object_address;
|
|
||||||
new_frame.line = frame.line.value_or(invalid_pos);
|
|
||||||
new_frame.column = frame.column.value_or(invalid_pos);
|
|
||||||
new_frame.filename = generate_owning_string(frame.filename).data;
|
|
||||||
new_frame.symbol = generate_owning_string(cpptrace::detail::demangle(frame.symbol, true)).data;
|
|
||||||
new_frame.is_inline = ctrace_bool(frame.is_inline);
|
|
||||||
return new_frame;
|
|
||||||
}
|
|
||||||
|
|
||||||
static cpptrace::stacktrace_frame convert_stacktrace_frame(const ctrace_stacktrace_frame& frame) {
|
|
||||||
using nullable_type = cpptrace::nullable<std::uint32_t>;
|
|
||||||
static constexpr auto null_v = nullable_type::null().raw_value;
|
|
||||||
cpptrace::stacktrace_frame new_frame;
|
|
||||||
new_frame.raw_address = frame.raw_address;
|
|
||||||
new_frame.object_address = frame.object_address;
|
|
||||||
new_frame.line = nullable_type{is_empty(frame.line) ? null_v : frame.line};
|
|
||||||
new_frame.column = nullable_type{is_empty(frame.column) ? null_v : frame.column};
|
|
||||||
new_frame.filename = frame.filename;
|
|
||||||
new_frame.symbol = frame.symbol;
|
|
||||||
new_frame.is_inline = bool(frame.is_inline);
|
|
||||||
return new_frame;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ctrace_stacktrace c_convert(const std::vector<cpptrace::stacktrace_frame>& trace) {
|
|
||||||
std::size_t count = trace.size();
|
|
||||||
auto* frames = new ctrace_stacktrace_frame[count];
|
|
||||||
std::transform(
|
|
||||||
trace.begin(),
|
|
||||||
trace.end(), frames,
|
|
||||||
static_cast<ctrace_stacktrace_frame(*)(const cpptrace::stacktrace_frame&)>(convert_stacktrace_frame)
|
|
||||||
);
|
|
||||||
return { frames, count };
|
|
||||||
}
|
|
||||||
|
|
||||||
static cpptrace::stacktrace cpp_convert(const ctrace_stacktrace* ptrace) {
|
|
||||||
if(!ptrace || !ptrace->frames) {
|
|
||||||
return { };
|
|
||||||
}
|
|
||||||
std::vector<cpptrace::stacktrace_frame> new_frames;
|
|
||||||
new_frames.reserve(ptrace->count);
|
|
||||||
for(std::size_t i = 0; i < ptrace->count; ++i) {
|
|
||||||
new_frames.push_back(convert_stacktrace_frame(ptrace->frames[i]));
|
|
||||||
}
|
|
||||||
return cpptrace::stacktrace{std::move(new_frames)};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
// ctrace::string
|
|
||||||
ctrace_owning_string ctrace_generate_owning_string(const char* raw_string) {
|
|
||||||
return ctrace::generate_owning_string(raw_string);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ctrace_free_owning_string(ctrace_owning_string* string) {
|
|
||||||
if(!string) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ctrace::free_owning_string(*string);
|
|
||||||
string->data = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ctrace::generation:
|
|
||||||
CTRACE_FORCE_NO_INLINE
|
|
||||||
ctrace_raw_trace ctrace_generate_raw_trace(size_t skip, size_t max_depth) {
|
|
||||||
try {
|
|
||||||
std::vector<cpptrace::frame_ptr> trace = cpptrace::detail::capture_frames(skip + 1, max_depth);
|
|
||||||
std::size_t count = trace.size();
|
|
||||||
auto* frames = new ctrace_frame_ptr[count];
|
|
||||||
std::copy(trace.data(), trace.data() + count, frames);
|
|
||||||
return { frames, count };
|
|
||||||
} catch(...) {
|
|
||||||
// Don't check rethrow condition, it's risky.
|
|
||||||
return { nullptr, 0 };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CTRACE_FORCE_NO_INLINE
|
|
||||||
ctrace_object_trace ctrace_generate_object_trace(size_t skip, size_t max_depth) {
|
|
||||||
try {
|
|
||||||
std::vector<cpptrace::object_frame> trace = cpptrace::detail::get_frames_object_info(
|
|
||||||
cpptrace::detail::capture_frames(skip + 1, max_depth)
|
|
||||||
);
|
|
||||||
return ctrace::c_convert(trace);
|
|
||||||
} catch(...) { // NOSONAR
|
|
||||||
// Don't check rethrow condition, it's risky.
|
|
||||||
return { nullptr, 0 };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CTRACE_FORCE_NO_INLINE
|
|
||||||
ctrace_stacktrace ctrace_generate_trace(size_t skip, size_t max_depth) {
|
|
||||||
try {
|
|
||||||
std::vector<cpptrace::frame_ptr> frames = cpptrace::detail::capture_frames(skip + 1, max_depth);
|
|
||||||
std::vector<cpptrace::stacktrace_frame> trace = cpptrace::detail::resolve_frames(frames);
|
|
||||||
return ctrace::c_convert(trace);
|
|
||||||
} catch(...) { // NOSONAR
|
|
||||||
// Don't check rethrow condition, it's risky.
|
|
||||||
return { nullptr, 0 };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// ctrace::freeing:
|
|
||||||
void ctrace_free_raw_trace(ctrace_raw_trace* trace) {
|
|
||||||
if(!trace) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ctrace_frame_ptr* frames = trace->frames;
|
|
||||||
delete[] frames;
|
|
||||||
trace->frames = nullptr;
|
|
||||||
trace->count = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ctrace_free_object_trace(ctrace_object_trace* trace) {
|
|
||||||
if(!trace || !trace->frames) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ctrace_object_frame* frames = trace->frames;
|
|
||||||
for(std::size_t i = 0; i < trace->count; ++i) {
|
|
||||||
const char* path = frames[i].obj_path;
|
|
||||||
ctrace::free_owning_string(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
delete[] frames;
|
|
||||||
trace->frames = nullptr;
|
|
||||||
trace->count = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ctrace_free_stacktrace(ctrace_stacktrace* trace) {
|
|
||||||
if(!trace || !trace->frames) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ctrace_stacktrace_frame* frames = trace->frames;
|
|
||||||
for(std::size_t i = 0; i < trace->count; ++i) {
|
|
||||||
ctrace::free_owning_string(frames[i].filename);
|
|
||||||
ctrace::free_owning_string(frames[i].symbol);
|
|
||||||
}
|
|
||||||
|
|
||||||
delete[] frames;
|
|
||||||
trace->frames = nullptr;
|
|
||||||
trace->count = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ctrace::resolve:
|
|
||||||
ctrace_stacktrace ctrace_resolve_raw_trace(const ctrace_raw_trace* trace) {
|
|
||||||
if(!trace || !trace->frames) {
|
|
||||||
return { nullptr, 0 };
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
std::vector<cpptrace::frame_ptr> frames(trace->count, 0);
|
|
||||||
std::copy(trace->frames, trace->frames + trace->count, frames.begin());
|
|
||||||
std::vector<cpptrace::stacktrace_frame> resolved = cpptrace::detail::resolve_frames(frames);
|
|
||||||
return ctrace::c_convert(resolved);
|
|
||||||
} catch(...) { // NOSONAR
|
|
||||||
// Don't check rethrow condition, it's risky.
|
|
||||||
return { nullptr, 0 };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ctrace_object_trace ctrace_resolve_raw_trace_to_object_trace(const ctrace_raw_trace* trace) {
|
|
||||||
if(!trace || !trace->frames) {
|
|
||||||
return { nullptr, 0 };
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
std::vector<cpptrace::frame_ptr> frames(trace->count, 0);
|
|
||||||
std::copy(trace->frames, trace->frames + trace->count, frames.begin());
|
|
||||||
std::vector<cpptrace::object_frame> obj = cpptrace::detail::get_frames_object_info(frames);
|
|
||||||
return ctrace::c_convert(obj);
|
|
||||||
} catch(...) { // NOSONAR
|
|
||||||
// Don't check rethrow condition, it's risky.
|
|
||||||
return { nullptr, 0 };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ctrace_stacktrace ctrace_resolve_object_trace(const ctrace_object_trace* trace) {
|
|
||||||
if(!trace || !trace->frames) {
|
|
||||||
return { nullptr, 0 };
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
std::vector<cpptrace::frame_ptr> frames(trace->count, 0);
|
|
||||||
std::transform(
|
|
||||||
trace->frames,
|
|
||||||
trace->frames + trace->count,
|
|
||||||
frames.begin(),
|
|
||||||
[] (const ctrace_object_frame& frame) -> cpptrace::frame_ptr {
|
|
||||||
return frame.raw_address;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
std::vector<cpptrace::stacktrace_frame> resolved = cpptrace::detail::resolve_frames(frames);
|
|
||||||
return ctrace::c_convert(resolved);
|
|
||||||
} catch(...) { // NOSONAR
|
|
||||||
// Don't check rethrow condition, it's risky.
|
|
||||||
return { nullptr, 0 };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ctrace::safe:
|
|
||||||
size_t ctrace_safe_generate_raw_trace(ctrace_frame_ptr* buffer, size_t size, size_t skip, size_t max_depth) {
|
|
||||||
return cpptrace::safe_generate_raw_trace(buffer, size, skip, max_depth);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ctrace_get_safe_object_frame(ctrace_frame_ptr address, ctrace_safe_object_frame* out) {
|
|
||||||
// TODO: change this?
|
|
||||||
static_assert(sizeof(cpptrace::safe_object_frame) == sizeof(ctrace_safe_object_frame), "");
|
|
||||||
cpptrace::get_safe_object_frame(address, reinterpret_cast<cpptrace::safe_object_frame*>(out));
|
|
||||||
}
|
|
||||||
|
|
||||||
ctrace_bool ctrace_can_signal_safe_unwind() {
|
|
||||||
return cpptrace::can_signal_safe_unwind();
|
|
||||||
}
|
|
||||||
|
|
||||||
ctrace_bool ctrace_can_get_safe_object_frame(void) {
|
|
||||||
return cpptrace::can_get_safe_object_frame();
|
|
||||||
}
|
|
||||||
|
|
||||||
// ctrace::io:
|
|
||||||
ctrace_owning_string ctrace_stacktrace_to_string(const ctrace_stacktrace* trace, ctrace_bool use_color) {
|
|
||||||
if(!trace || !trace->frames) {
|
|
||||||
return ctrace::generate_owning_string("<empty trace>");
|
|
||||||
}
|
|
||||||
auto cpp_trace = ctrace::cpp_convert(trace);
|
|
||||||
std::string trace_string = cpp_trace.to_string(bool(use_color));
|
|
||||||
return ctrace::generate_owning_string(trace_string);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ctrace_print_stacktrace(const ctrace_stacktrace* trace, FILE* to, ctrace_bool use_color) {
|
|
||||||
if(
|
|
||||||
use_color && (
|
|
||||||
(to == stdout && cpptrace::isatty(cpptrace::stdout_fileno)) ||
|
|
||||||
(to == stderr && cpptrace::isatty(cpptrace::stderr_fileno))
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
cpptrace::detail::enable_virtual_terminal_processing_if_needed();
|
|
||||||
}
|
|
||||||
ctrace::ffprintf(to, "Stack trace (most recent call first):\n");
|
|
||||||
if(trace->count == 0 || !trace->frames) {
|
|
||||||
ctrace::ffprintf(to, "<empty trace>\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const auto reset = use_color ? ESC "0m" : "";
|
|
||||||
const auto green = use_color ? ESC "32m" : "";
|
|
||||||
const auto yellow = use_color ? ESC "33m" : "";
|
|
||||||
const auto blue = use_color ? ESC "34m" : "";
|
|
||||||
const auto frame_number_width = cpptrace::detail::n_digits(unsigned(trace->count - 1));
|
|
||||||
ctrace_stacktrace_frame* frames = trace->frames;
|
|
||||||
for(std::size_t i = 0; i < trace->count; ++i) {
|
|
||||||
static constexpr auto ptr_len = 2 * sizeof(cpptrace::frame_ptr);
|
|
||||||
ctrace::ffprintf(to, "#%-*llu ", int(frame_number_width), i);
|
|
||||||
if(frames[i].is_inline) {
|
|
||||||
(void)std::fprintf(to, "%*s",
|
|
||||||
int(ptr_len + 2),
|
|
||||||
"(inlined)");
|
|
||||||
} else {
|
|
||||||
(void)std::fprintf(to, "%s0x%0*llx%s",
|
|
||||||
blue,
|
|
||||||
int(ptr_len),
|
|
||||||
cpptrace::detail::to_ull(frames[i].raw_address),
|
|
||||||
reset);
|
|
||||||
}
|
|
||||||
if(!ctrace::is_empty(frames[i].symbol)) {
|
|
||||||
(void)std::fprintf(to, " in %s%s%s",
|
|
||||||
yellow,
|
|
||||||
frames[i].symbol,
|
|
||||||
reset);
|
|
||||||
}
|
|
||||||
if(!ctrace::is_empty(frames[i].filename)) {
|
|
||||||
(void)std::fprintf(to, " at %s%s%s",
|
|
||||||
green,
|
|
||||||
frames[i].filename,
|
|
||||||
reset);
|
|
||||||
if(ctrace::is_empty(frames[i].line)) {
|
|
||||||
ctrace::ffprintf(to, "\n");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
(void)std::fprintf(to, ":%s%llu%s",
|
|
||||||
blue,
|
|
||||||
cpptrace::detail::to_ull(frames[i].line),
|
|
||||||
reset);
|
|
||||||
if(ctrace::is_empty(frames[i].column)) {
|
|
||||||
ctrace::ffprintf(to, "\n");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
(void)std::fprintf(to, ":%s%llu%s",
|
|
||||||
blue,
|
|
||||||
cpptrace::detail::to_ull(frames[i].column),
|
|
||||||
reset);
|
|
||||||
}
|
|
||||||
// always print newline at end :M
|
|
||||||
ctrace::ffprintf(to, "\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// utility::demangle:
|
|
||||||
ctrace_owning_string ctrace_demangle(const char* mangled) {
|
|
||||||
if(!mangled) {
|
|
||||||
return ctrace::generate_owning_string("");
|
|
||||||
}
|
|
||||||
std::string demangled = cpptrace::demangle(mangled);
|
|
||||||
return ctrace::generate_owning_string(demangled);
|
|
||||||
}
|
|
||||||
|
|
||||||
// utility::io
|
|
||||||
int ctrace_stdin_fileno(void) {
|
|
||||||
return cpptrace::stdin_fileno;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ctrace_stderr_fileno(void) {
|
|
||||||
return cpptrace::stderr_fileno;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ctrace_stdout_fileno(void) {
|
|
||||||
return cpptrace::stdout_fileno;
|
|
||||||
}
|
|
||||||
|
|
||||||
ctrace_bool ctrace_isatty(int fd) {
|
|
||||||
return cpptrace::isatty(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
// utility::cache:
|
|
||||||
void ctrace_set_cache_mode(ctrace_cache_mode mode) {
|
|
||||||
static constexpr auto cache_max = cpptrace::cache_mode::prioritize_speed;
|
|
||||||
if(mode > unsigned(cache_max)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto cache_mode = static_cast<cpptrace::cache_mode>(mode);
|
|
||||||
cpptrace::experimental::set_cache_mode(cache_mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ctrace_enable_inlined_call_resolution(ctrace_bool enable) {
|
|
||||||
cpptrace::enable_inlined_call_resolution(enable);
|
|
||||||
}
|
|
||||||
|
|
||||||
ctrace_object_frame ctrace_get_object_info(const ctrace_stacktrace_frame* frame) {
|
|
||||||
try {
|
|
||||||
cpptrace::object_frame new_frame = cpptrace::detail::get_frame_object_info(frame->raw_address);
|
|
||||||
return ctrace::convert_object_frame(new_frame);
|
|
||||||
} catch(...) {
|
|
||||||
return {0, 0, nullptr};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -4,9 +4,9 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace cpptrace {
|
namespace cpptrace {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
std::string demangle(const std::string& name, bool check_prefix);
|
std::string demangle(const std::string&);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -1,61 +1,28 @@
|
|||||||
#include "utils/microfmt.hpp"
|
|
||||||
#ifdef CPPTRACE_DEMANGLE_WITH_CXXABI
|
#ifdef CPPTRACE_DEMANGLE_WITH_CXXABI
|
||||||
|
|
||||||
#include "demangle/demangle.hpp"
|
#include "demangle.hpp"
|
||||||
|
|
||||||
#include "utils/utils.hpp"
|
|
||||||
|
|
||||||
#include <cxxabi.h>
|
#include <cxxabi.h>
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <functional>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace cpptrace {
|
namespace cpptrace {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
std::string demangle(const std::string& name, bool check_prefix) {
|
std::string demangle(const std::string& name) {
|
||||||
// https://itanium-cxx-abi.github.io/cxx-abi/abi.html#demangler
|
int status;
|
||||||
// Check both _Z and __Z, apple prefixes all symbols with an underscore
|
// presumably thread-safe
|
||||||
if(check_prefix && !(starts_with(name, "_Z") || starts_with(name, "__Z"))) {
|
char* demangled = abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status);
|
||||||
return name;
|
if(demangled) {
|
||||||
}
|
std::string str = demangled;
|
||||||
// Apple clang demangles __Z just fine but gcc doesn't, so just offset the leading underscore
|
// NOLINTNEXTLINE(cppcoreguidelines-no-malloc)
|
||||||
std::size_t offset = 0;
|
free(demangled);
|
||||||
if(starts_with(name, "__Z")) {
|
return str;
|
||||||
offset = 1;
|
} else {
|
||||||
}
|
return name;
|
||||||
// Mangled names don't have spaces, we might add a space and some extra info somewhere but we still want it to
|
|
||||||
// be demanglable. Look for a space, if there is one swap it with a null terminator briefly.
|
|
||||||
auto end = name.find(' ');
|
|
||||||
std::string name_copy;
|
|
||||||
std::reference_wrapper<const std::string> to_demangle = name;
|
|
||||||
std::string rest;
|
|
||||||
if(end != std::string::npos) {
|
|
||||||
name_copy = name.substr(0, end);
|
|
||||||
rest = name.substr(end);
|
|
||||||
to_demangle = name_copy;
|
|
||||||
}
|
|
||||||
// presumably thread-safe
|
|
||||||
// it appears safe to pass nullptr for status however the docs don't explicitly say it's safe so I don't
|
|
||||||
// want to rely on it
|
|
||||||
int status;
|
|
||||||
auto demangled = raii_wrap(
|
|
||||||
abi::__cxa_demangle(to_demangle.get().c_str() + offset, nullptr, nullptr, &status),
|
|
||||||
[] (char* str) { std::free(str); }
|
|
||||||
);
|
|
||||||
// demangled will always be nullptr on non-zero status, and if __cxa_demangle ever fails for any reason
|
|
||||||
// we'll just quietly return the mangled name
|
|
||||||
if(demangled.get()) {
|
|
||||||
std::string str = demangled.get();
|
|
||||||
if(!rest.empty()) {
|
|
||||||
str += rest;
|
|
||||||
}
|
}
|
||||||
return str;
|
|
||||||
} else {
|
|
||||||
return name;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -1,15 +1,15 @@
|
|||||||
#ifdef CPPTRACE_DEMANGLE_WITH_NOTHING
|
#ifdef CPPTRACE_DEMANGLE_WITH_NOTHING
|
||||||
|
|
||||||
#include "demangle/demangle.hpp"
|
#include "demangle.hpp"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace cpptrace {
|
namespace cpptrace {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
std::string demangle(const std::string& name, bool) {
|
std::string demangle(const std::string& name) {
|
||||||
return name;
|
return name;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -1,31 +0,0 @@
|
|||||||
#ifdef CPPTRACE_DEMANGLE_WITH_WINAPI
|
|
||||||
|
|
||||||
#include "demangle/demangle.hpp"
|
|
||||||
#include "platform/dbghelp_utils.hpp"
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#ifndef WIN32_LEAN_AND_MEAN
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
|
||||||
#endif
|
|
||||||
#include <windows.h>
|
|
||||||
#include <dbghelp.h>
|
|
||||||
|
|
||||||
namespace cpptrace {
|
|
||||||
namespace detail {
|
|
||||||
std::string demangle(const std::string& name, bool) {
|
|
||||||
// Dbghelp is is single-threaded, so acquire a lock.
|
|
||||||
auto lock = get_dbghelp_lock();
|
|
||||||
char buffer[500];
|
|
||||||
auto ret = UnDecorateSymbolName(name.c_str(), buffer, sizeof(buffer) - 1, 0);
|
|
||||||
if(ret == 0) {
|
|
||||||
return name;
|
|
||||||
} else {
|
|
||||||
buffer[ret] = 0; // just in case, ms' docs unclear if null terminator inserted
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@ -1,187 +0,0 @@
|
|||||||
#include <cpptrace/exceptions.hpp>
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cstring>
|
|
||||||
#include <new>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "platform/exception_type.hpp"
|
|
||||||
#include "utils/common.hpp"
|
|
||||||
#include "options.hpp"
|
|
||||||
|
|
||||||
namespace cpptrace {
|
|
||||||
namespace detail {
|
|
||||||
lazy_trace_holder::lazy_trace_holder(const lazy_trace_holder& other) : resolved(other.resolved) {
|
|
||||||
if(other.resolved) {
|
|
||||||
new (&resolved_trace) stacktrace(other.resolved_trace);
|
|
||||||
} else {
|
|
||||||
new (&trace) raw_trace(other.trace);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lazy_trace_holder::lazy_trace_holder(lazy_trace_holder&& other) noexcept : resolved(other.resolved) {
|
|
||||||
if(other.resolved) {
|
|
||||||
new (&resolved_trace) stacktrace(std::move(other.resolved_trace));
|
|
||||||
} else {
|
|
||||||
new (&trace) raw_trace(std::move(other.trace));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lazy_trace_holder& lazy_trace_holder::operator=(const lazy_trace_holder& other) {
|
|
||||||
clear();
|
|
||||||
resolved = other.resolved;
|
|
||||||
if(other.resolved) {
|
|
||||||
new (&resolved_trace) stacktrace(other.resolved_trace);
|
|
||||||
} else {
|
|
||||||
new (&trace) raw_trace(other.trace);
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
lazy_trace_holder& lazy_trace_holder::operator=(lazy_trace_holder&& other) noexcept {
|
|
||||||
clear();
|
|
||||||
resolved = other.resolved;
|
|
||||||
if(other.resolved) {
|
|
||||||
new (&resolved_trace) stacktrace(std::move(other.resolved_trace));
|
|
||||||
} else {
|
|
||||||
new (&trace) raw_trace(std::move(other.trace));
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
lazy_trace_holder::~lazy_trace_holder() {
|
|
||||||
clear();
|
|
||||||
}
|
|
||||||
// access
|
|
||||||
const raw_trace& lazy_trace_holder::get_raw_trace() const {
|
|
||||||
if(resolved) {
|
|
||||||
throw std::logic_error(
|
|
||||||
"cpptrace::detail::lazy_trace_holder::get_resolved_trace called on resolved holder"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return trace;
|
|
||||||
}
|
|
||||||
stacktrace& lazy_trace_holder::get_resolved_trace() {
|
|
||||||
if(!resolved) {
|
|
||||||
raw_trace old_trace = std::move(trace);
|
|
||||||
*this = lazy_trace_holder(stacktrace{});
|
|
||||||
try {
|
|
||||||
if(!old_trace.empty()) {
|
|
||||||
resolved_trace = old_trace.resolve();
|
|
||||||
}
|
|
||||||
} catch(const std::exception& e) {
|
|
||||||
if(!detail::should_absorb_trace_exceptions()) {
|
|
||||||
// TODO: Append to message somehow?
|
|
||||||
std::fprintf(
|
|
||||||
stderr,
|
|
||||||
"Exception occurred while resolving trace in cpptrace::detail::lazy_trace_holder:\n%s\n",
|
|
||||||
e.what()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return resolved_trace;
|
|
||||||
}
|
|
||||||
const stacktrace& lazy_trace_holder::get_resolved_trace() const {
|
|
||||||
if(!resolved) {
|
|
||||||
throw std::logic_error(
|
|
||||||
"cpptrace::detail::lazy_trace_holder::get_resolved_trace called on unresolved const holder"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return resolved_trace;
|
|
||||||
}
|
|
||||||
void lazy_trace_holder::clear() {
|
|
||||||
if(resolved) {
|
|
||||||
resolved_trace.~stacktrace();
|
|
||||||
} else {
|
|
||||||
trace.~raw_trace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CPPTRACE_FORCE_NO_INLINE
|
|
||||||
raw_trace get_raw_trace_and_absorb(std::size_t skip, std::size_t max_depth) {
|
|
||||||
try {
|
|
||||||
return generate_raw_trace(skip + 1, max_depth);
|
|
||||||
} catch(const std::exception& e) {
|
|
||||||
if(!detail::should_absorb_trace_exceptions()) {
|
|
||||||
// TODO: Append to message somehow
|
|
||||||
std::fprintf(
|
|
||||||
stderr,
|
|
||||||
"Cpptrace: Exception occurred while resolving trace in cpptrace::exception object:\n%s\n",
|
|
||||||
e.what()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return raw_trace{};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CPPTRACE_FORCE_NO_INLINE
|
|
||||||
raw_trace get_raw_trace_and_absorb(std::size_t skip) {
|
|
||||||
try { // try/catch can never be hit but it's needed to prevent TCO
|
|
||||||
return get_raw_trace_and_absorb(skip + 1, SIZE_MAX);
|
|
||||||
} catch(...) {
|
|
||||||
if(!detail::should_absorb_trace_exceptions()) {
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
return raw_trace{};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* lazy_exception::what() const noexcept {
|
|
||||||
if(what_string.empty()) {
|
|
||||||
what_string = message() + std::string(":\n") + trace_holder.get_resolved_trace().to_string();
|
|
||||||
}
|
|
||||||
return what_string.c_str();
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* lazy_exception::message() const noexcept {
|
|
||||||
return "cpptrace::lazy_exception";
|
|
||||||
}
|
|
||||||
|
|
||||||
const stacktrace& lazy_exception::trace() const noexcept {
|
|
||||||
return trace_holder.get_resolved_trace();
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* exception_with_message::message() const noexcept {
|
|
||||||
return user_message.c_str();
|
|
||||||
}
|
|
||||||
|
|
||||||
system_error::system_error(int error_code, std::string&& message_arg, raw_trace&& trace) noexcept
|
|
||||||
: runtime_error(
|
|
||||||
message_arg + ": " + std::error_code(error_code, std::generic_category()).message(),
|
|
||||||
std::move(trace)
|
|
||||||
),
|
|
||||||
ec(std::error_code(error_code, std::generic_category())) {}
|
|
||||||
|
|
||||||
const std::error_code& system_error::code() const noexcept {
|
|
||||||
return ec;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* nested_exception::message() const noexcept {
|
|
||||||
if(message_value.empty()) {
|
|
||||||
try {
|
|
||||||
std::rethrow_exception(ptr);
|
|
||||||
} catch(std::exception& e) {
|
|
||||||
message_value = std::string("Nested exception: ") + e.what();
|
|
||||||
} catch(...) {
|
|
||||||
message_value = "Nested exception holding instance of " + detail::exception_type_name();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return message_value.c_str();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::exception_ptr nested_exception::nested_ptr() const noexcept {
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
CPPTRACE_FORCE_NO_INLINE
|
|
||||||
void rethrow_and_wrap_if_needed(std::size_t skip) {
|
|
||||||
try {
|
|
||||||
std::rethrow_exception(std::current_exception());
|
|
||||||
} catch(cpptrace::exception&) {
|
|
||||||
throw; // already a cpptrace::exception
|
|
||||||
} catch(...) {
|
|
||||||
throw nested_exception(std::current_exception(), detail::get_raw_trace_and_absorb(skip + 1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,353 +0,0 @@
|
|||||||
#include <cpptrace/formatting.hpp>
|
|
||||||
#include <cpptrace/utils.hpp>
|
|
||||||
|
|
||||||
#include "utils/optional.hpp"
|
|
||||||
#include "utils/utils.hpp"
|
|
||||||
#include "snippets/snippet.hpp"
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <string>
|
|
||||||
#include <functional>
|
|
||||||
#include <iostream>
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
namespace cpptrace {
|
|
||||||
class formatter::impl {
|
|
||||||
struct {
|
|
||||||
std::string header = "Stack trace (most recent call first):";
|
|
||||||
color_mode color = color_mode::automatic;
|
|
||||||
address_mode addresses = address_mode::raw;
|
|
||||||
path_mode paths = path_mode::full;
|
|
||||||
bool snippets = false;
|
|
||||||
int context_lines = 2;
|
|
||||||
bool columns = true;
|
|
||||||
bool show_filtered_frames = true;
|
|
||||||
std::function<bool(const stacktrace_frame&)> filter;
|
|
||||||
} options;
|
|
||||||
|
|
||||||
public:
|
|
||||||
void header(std::string header) {
|
|
||||||
options.header = std::move(header);
|
|
||||||
}
|
|
||||||
void colors(formatter::color_mode mode) {
|
|
||||||
options.color = mode;
|
|
||||||
}
|
|
||||||
void addresses(formatter::address_mode mode) {
|
|
||||||
options.addresses = mode;
|
|
||||||
}
|
|
||||||
void paths(path_mode mode) {
|
|
||||||
options.paths = mode;
|
|
||||||
}
|
|
||||||
void snippets(bool snippets) {
|
|
||||||
options.snippets = snippets;
|
|
||||||
}
|
|
||||||
void snippet_context(int lines) {
|
|
||||||
options.context_lines = lines;
|
|
||||||
}
|
|
||||||
void columns(bool columns) {
|
|
||||||
options.columns = columns;
|
|
||||||
}
|
|
||||||
void filtered_frame_placeholders(bool show) {
|
|
||||||
options.show_filtered_frames = show;
|
|
||||||
}
|
|
||||||
void filter(std::function<bool(const stacktrace_frame&)> filter) {
|
|
||||||
options.filter = filter;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string format(const stacktrace_frame& frame, detail::optional<bool> color_override = detail::nullopt) const {
|
|
||||||
std::ostringstream oss;
|
|
||||||
print_frame_inner(oss, frame, color_override.value_or(options.color == color_mode::always));
|
|
||||||
return std::move(oss).str();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string format(const stacktrace& trace, detail::optional<bool> color_override = detail::nullopt) const {
|
|
||||||
std::ostringstream oss;
|
|
||||||
print_internal(oss, trace, false, color_override);
|
|
||||||
return std::move(oss).str();
|
|
||||||
}
|
|
||||||
|
|
||||||
void print(const stacktrace_frame& frame, detail::optional<bool> color_override = detail::nullopt) const {
|
|
||||||
print(std::cout, frame, color_override);
|
|
||||||
}
|
|
||||||
void print(
|
|
||||||
std::ostream& stream,
|
|
||||||
const stacktrace_frame& frame,
|
|
||||||
detail::optional<bool> color_override = detail::nullopt
|
|
||||||
) const {
|
|
||||||
print_frame_internal(stream, frame, color_override);
|
|
||||||
}
|
|
||||||
void print(
|
|
||||||
std::FILE* file,
|
|
||||||
const stacktrace_frame& frame,
|
|
||||||
detail::optional<bool> color_override = detail::nullopt
|
|
||||||
) const {
|
|
||||||
auto str = format(frame, color_override);
|
|
||||||
std::fwrite(str.data(), 1, str.size(), file);
|
|
||||||
}
|
|
||||||
|
|
||||||
void print(const stacktrace& trace, detail::optional<bool> color_override = detail::nullopt) const {
|
|
||||||
print(std::cout, trace, color_override);
|
|
||||||
}
|
|
||||||
void print(
|
|
||||||
std::ostream& stream,
|
|
||||||
const stacktrace& trace,
|
|
||||||
detail::optional<bool> color_override = detail::nullopt
|
|
||||||
) const {
|
|
||||||
print_internal(stream, trace, true, color_override);
|
|
||||||
}
|
|
||||||
void print(
|
|
||||||
std::FILE* file,
|
|
||||||
const stacktrace& trace,
|
|
||||||
detail::optional<bool> color_override = detail::nullopt
|
|
||||||
) const {
|
|
||||||
auto str = format(trace, color_override);
|
|
||||||
std::fwrite(str.data(), 1, str.size(), file);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool stream_is_tty(std::ostream& stream) const {
|
|
||||||
// not great, but it'll have to do
|
|
||||||
return (&stream == &std::cout && isatty(stdout_fileno))
|
|
||||||
|| (&stream == &std::cerr && isatty(stderr_fileno));
|
|
||||||
}
|
|
||||||
|
|
||||||
void maybe_ensure_virtual_terminal_processing(std::ostream& stream, bool color) const {
|
|
||||||
if(color && stream_is_tty(stream)) {
|
|
||||||
detail::enable_virtual_terminal_processing_if_needed();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool should_do_color(std::ostream& stream, detail::optional<bool> color_override) const {
|
|
||||||
bool do_color = options.color == color_mode::always || color_override.value_or(false);
|
|
||||||
if(
|
|
||||||
(options.color == color_mode::automatic || options.color == color_mode::always) &&
|
|
||||||
(!color_override || color_override.unwrap() != false) &&
|
|
||||||
stream_is_tty(stream)
|
|
||||||
) {
|
|
||||||
detail::enable_virtual_terminal_processing_if_needed();
|
|
||||||
do_color = true;
|
|
||||||
}
|
|
||||||
return do_color;
|
|
||||||
}
|
|
||||||
|
|
||||||
void print_internal(std::ostream& stream, const stacktrace& trace, bool newline_at_end, detail::optional<bool> color_override) const {
|
|
||||||
bool do_color = should_do_color(stream, color_override);
|
|
||||||
maybe_ensure_virtual_terminal_processing(stream, do_color);
|
|
||||||
print_internal(stream, trace, newline_at_end, do_color);
|
|
||||||
}
|
|
||||||
|
|
||||||
void print_internal(std::ostream& stream, const stacktrace& trace, bool newline_at_end, bool color) const {
|
|
||||||
if(!options.header.empty()) {
|
|
||||||
stream << options.header << '\n';
|
|
||||||
}
|
|
||||||
std::size_t counter = 0;
|
|
||||||
const auto& frames = trace.frames;
|
|
||||||
if(frames.empty()) {
|
|
||||||
stream << "<empty trace>\n";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const auto frame_number_width = detail::n_digits(static_cast<int>(frames.size()) - 1);
|
|
||||||
for(const auto& frame : frames) {
|
|
||||||
if(options.filter && !options.filter(frame)) {
|
|
||||||
if(!options.show_filtered_frames) {
|
|
||||||
counter++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
print_placeholder_frame(stream, frame_number_width, counter);
|
|
||||||
} else {
|
|
||||||
print_frame_internal(stream, frame, color, frame_number_width, counter);
|
|
||||||
if(frame.line.has_value() && !frame.filename.empty() && options.snippets) {
|
|
||||||
auto snippet = detail::get_snippet(
|
|
||||||
frame.filename,
|
|
||||||
frame.line.value(),
|
|
||||||
options.context_lines,
|
|
||||||
color
|
|
||||||
);
|
|
||||||
if(!snippet.empty()) {
|
|
||||||
stream << '\n';
|
|
||||||
stream << snippet;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(newline_at_end || &frame != &frames.back()) {
|
|
||||||
stream << '\n';
|
|
||||||
}
|
|
||||||
counter++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void print_frame_internal(
|
|
||||||
std::ostream& stream,
|
|
||||||
const stacktrace_frame& frame,
|
|
||||||
bool color,
|
|
||||||
unsigned frame_number_width,
|
|
||||||
std::size_t counter
|
|
||||||
) const {
|
|
||||||
microfmt::print(stream, "#{<{}} ", frame_number_width, counter);
|
|
||||||
print_frame_inner(stream, frame, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
void print_placeholder_frame(std::ostream& stream, unsigned frame_number_width, std::size_t counter) const {
|
|
||||||
microfmt::print(stream, "#{<{}} (filtered)", frame_number_width, counter);
|
|
||||||
}
|
|
||||||
|
|
||||||
void print_frame_internal(
|
|
||||||
std::ostream& stream,
|
|
||||||
const stacktrace_frame& frame,
|
|
||||||
detail::optional<bool> color_override
|
|
||||||
) const {
|
|
||||||
bool do_color = should_do_color(stream, color_override);
|
|
||||||
maybe_ensure_virtual_terminal_processing(stream, do_color);
|
|
||||||
print_frame_inner(stream, frame, do_color);
|
|
||||||
}
|
|
||||||
|
|
||||||
void print_frame_inner(std::ostream& stream, const stacktrace_frame& frame, bool color) const {
|
|
||||||
const auto reset = color ? RESET : "";
|
|
||||||
const auto green = color ? GREEN : "";
|
|
||||||
const auto yellow = color ? YELLOW : "";
|
|
||||||
const auto blue = color ? BLUE : "";
|
|
||||||
if(frame.is_inline) {
|
|
||||||
microfmt::print(stream, "{<{}} ", 2 * sizeof(frame_ptr) + 2, "(inlined)");
|
|
||||||
} else if(options.addresses != address_mode::none) {
|
|
||||||
auto address = options.addresses == address_mode::raw ? frame.raw_address : frame.object_address;
|
|
||||||
microfmt::print(stream, "{}0x{>{}:0h}{} ", blue, 2 * sizeof(frame_ptr), address, reset);
|
|
||||||
}
|
|
||||||
if(!frame.symbol.empty()) {
|
|
||||||
microfmt::print(stream, "in {}{}{}", yellow, frame.symbol, reset);
|
|
||||||
}
|
|
||||||
if(!frame.filename.empty()) {
|
|
||||||
microfmt::print(
|
|
||||||
stream,
|
|
||||||
"{}at {}{}{}",
|
|
||||||
frame.symbol.empty() ? "" : " ",
|
|
||||||
green,
|
|
||||||
options.paths == path_mode::full ? frame.filename : detail::basename(frame.filename, true),
|
|
||||||
reset
|
|
||||||
);
|
|
||||||
if(frame.line.has_value()) {
|
|
||||||
microfmt::print(stream, ":{}{}{}", blue, frame.line.value(), reset);
|
|
||||||
if(frame.column.has_value() && options.columns) {
|
|
||||||
microfmt::print(stream, ":{}{}{}", blue, frame.column.value(), reset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
formatter::formatter() : pimpl(new impl) {}
|
|
||||||
formatter::~formatter() {
|
|
||||||
delete pimpl;
|
|
||||||
}
|
|
||||||
|
|
||||||
formatter::formatter(formatter&& other) : pimpl(detail::exchange(other.pimpl, nullptr)) {}
|
|
||||||
formatter::formatter(const formatter& other) : pimpl(new impl(*other.pimpl)) {}
|
|
||||||
formatter& formatter::operator=(formatter&& other) {
|
|
||||||
if(pimpl) {
|
|
||||||
delete pimpl;
|
|
||||||
}
|
|
||||||
pimpl = detail::exchange(other.pimpl, nullptr);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
formatter& formatter::operator=(const formatter& other) {
|
|
||||||
if(pimpl) {
|
|
||||||
delete pimpl;
|
|
||||||
}
|
|
||||||
pimpl = new impl(*other.pimpl);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
formatter& formatter::header(std::string header) {
|
|
||||||
pimpl->header(std::move(header));
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
formatter& formatter::colors(color_mode mode) {
|
|
||||||
pimpl->colors(mode);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
formatter& formatter::addresses(address_mode mode) {
|
|
||||||
pimpl->addresses(mode);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
formatter& formatter::paths(path_mode mode) {
|
|
||||||
pimpl->paths(mode);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
formatter& formatter::snippets(bool snippets) {
|
|
||||||
pimpl->snippets(snippets);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
formatter& formatter::snippet_context(int lines) {
|
|
||||||
pimpl->snippet_context(lines);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
formatter& formatter::columns(bool columns) {
|
|
||||||
pimpl->columns(columns);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
formatter& formatter::filtered_frame_placeholders(bool show) {
|
|
||||||
pimpl->filtered_frame_placeholders(show);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
formatter& formatter::filter(std::function<bool(const stacktrace_frame&)> filter) {
|
|
||||||
pimpl->filter(std::move(filter));
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string formatter::format(const stacktrace_frame& frame) const {
|
|
||||||
return pimpl->format(frame);
|
|
||||||
}
|
|
||||||
std::string formatter::format(const stacktrace_frame& frame, bool color) const {
|
|
||||||
return pimpl->format(frame, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string formatter::format(const stacktrace& trace) const {
|
|
||||||
return pimpl->format(trace);
|
|
||||||
}
|
|
||||||
std::string formatter::format(const stacktrace& trace, bool color) const {
|
|
||||||
return pimpl->format(trace, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
void formatter::print(const stacktrace& trace) const {
|
|
||||||
pimpl->print(trace);
|
|
||||||
}
|
|
||||||
void formatter::print(const stacktrace& trace, bool color) const {
|
|
||||||
pimpl->print(trace, color);
|
|
||||||
}
|
|
||||||
void formatter::print(std::ostream& stream, const stacktrace& trace) const {
|
|
||||||
pimpl->print(stream, trace);
|
|
||||||
}
|
|
||||||
void formatter::print(std::ostream& stream, const stacktrace& trace, bool color) const {
|
|
||||||
pimpl->print(stream, trace, color);
|
|
||||||
}
|
|
||||||
void formatter::print(std::FILE* file, const stacktrace& trace) const {
|
|
||||||
pimpl->print(file, trace);
|
|
||||||
}
|
|
||||||
void formatter::print(std::FILE* file, const stacktrace& trace, bool color) const {
|
|
||||||
pimpl->print(file, trace, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
void formatter::print(const stacktrace_frame& frame) const {
|
|
||||||
pimpl->print(frame);
|
|
||||||
}
|
|
||||||
void formatter::print(const stacktrace_frame& frame, bool color) const {
|
|
||||||
pimpl->print(frame, color);
|
|
||||||
}
|
|
||||||
void formatter::print(std::ostream& stream, const stacktrace_frame& frame) const {
|
|
||||||
pimpl->print(stream, frame);
|
|
||||||
}
|
|
||||||
void formatter::print(std::ostream& stream, const stacktrace_frame& frame, bool color) const {
|
|
||||||
pimpl->print(stream, frame, color);
|
|
||||||
}
|
|
||||||
void formatter::print(std::FILE* file, const stacktrace_frame& frame) const {
|
|
||||||
pimpl->print(file, frame);
|
|
||||||
}
|
|
||||||
void formatter::print(std::FILE* file, const stacktrace_frame& frame, bool color) const {
|
|
||||||
pimpl->print(file, frame, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
const formatter& get_default_formatter() {
|
|
||||||
static formatter formatter;
|
|
||||||
return formatter;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,327 +0,0 @@
|
|||||||
#include <cpptrace/cpptrace.hpp>
|
|
||||||
#define CPPTRACE_DONT_PREPARE_UNWIND_INTERCEPTOR_ON
|
|
||||||
#include <cpptrace/from_current.hpp>
|
|
||||||
|
|
||||||
#include <system_error>
|
|
||||||
#include <typeinfo>
|
|
||||||
|
|
||||||
#include "platform/platform.hpp"
|
|
||||||
#include "utils/microfmt.hpp"
|
|
||||||
|
|
||||||
#ifndef _MSC_VER
|
|
||||||
#include <string.h>
|
|
||||||
#if IS_WINDOWS
|
|
||||||
#ifndef WIN32_LEAN_AND_MEAN
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
|
||||||
#endif
|
|
||||||
#include <windows.h>
|
|
||||||
#else
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#if IS_APPLE
|
|
||||||
#include <mach/mach.h>
|
|
||||||
#ifdef HAS_MACH_VM
|
|
||||||
#include <mach/mach_vm.h>
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
#include <fstream>
|
|
||||||
#include <ios>
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace cpptrace {
|
|
||||||
namespace detail {
|
|
||||||
thread_local lazy_trace_holder current_exception_trace;
|
|
||||||
|
|
||||||
CPPTRACE_FORCE_NO_INLINE void collect_current_trace(std::size_t skip) {
|
|
||||||
current_exception_trace = lazy_trace_holder(cpptrace::generate_raw_trace(skip + 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef _MSC_VER
|
|
||||||
// set only once by do_prepare_unwind_interceptor
|
|
||||||
char (*intercept_unwind_handler)(std::size_t) = nullptr;
|
|
||||||
|
|
||||||
CPPTRACE_FORCE_NO_INLINE
|
|
||||||
bool intercept_unwind(const std::type_info*, const std::type_info*, void**, unsigned) {
|
|
||||||
if(intercept_unwind_handler) {
|
|
||||||
intercept_unwind_handler(1);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
CPPTRACE_FORCE_NO_INLINE
|
|
||||||
bool unconditional_exception_unwind_interceptor(const std::type_info*, const std::type_info*, void**, unsigned) {
|
|
||||||
collect_current_trace(1);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
using do_catch_fn = decltype(intercept_unwind);
|
|
||||||
|
|
||||||
unwind_interceptor::~unwind_interceptor() = default;
|
|
||||||
unconditional_unwind_interceptor::~unconditional_unwind_interceptor() = default;
|
|
||||||
|
|
||||||
#if IS_LIBSTDCXX
|
|
||||||
constexpr size_t vtable_size = 11;
|
|
||||||
#elif IS_LIBCXX
|
|
||||||
constexpr size_t vtable_size = 10;
|
|
||||||
#else
|
|
||||||
#warning "Cpptrace from_current: Unrecognized C++ standard library, from_current() won't be supported"
|
|
||||||
constexpr size_t vtable_size = 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if IS_WINDOWS
|
|
||||||
int get_page_size() {
|
|
||||||
SYSTEM_INFO info;
|
|
||||||
GetSystemInfo(&info);
|
|
||||||
return info.dwPageSize;
|
|
||||||
}
|
|
||||||
constexpr auto memory_readonly = PAGE_READONLY;
|
|
||||||
constexpr auto memory_readwrite = PAGE_READWRITE;
|
|
||||||
int mprotect_page_and_return_old_protections(void* page, int page_size, int protections) {
|
|
||||||
DWORD old_protections;
|
|
||||||
if(!VirtualProtect(page, page_size, protections, &old_protections)) {
|
|
||||||
throw std::runtime_error(
|
|
||||||
microfmt::format(
|
|
||||||
"VirtualProtect call failed: {}",
|
|
||||||
std::system_error(GetLastError(), std::system_category()).what()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return old_protections;
|
|
||||||
}
|
|
||||||
void mprotect_page(void* page, int page_size, int protections) {
|
|
||||||
mprotect_page_and_return_old_protections(page, page_size, protections);
|
|
||||||
}
|
|
||||||
void* allocate_page(int page_size) {
|
|
||||||
auto page = VirtualAlloc(nullptr, page_size, MEM_COMMIT | MEM_RESERVE, memory_readwrite);
|
|
||||||
if(!page) {
|
|
||||||
throw std::runtime_error(
|
|
||||||
microfmt::format(
|
|
||||||
"VirtualAlloc call failed: {}",
|
|
||||||
std::system_error(GetLastError(), std::system_category()).what()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return page;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
int get_page_size() {
|
|
||||||
#if defined(_SC_PAGESIZE)
|
|
||||||
return sysconf(_SC_PAGESIZE);
|
|
||||||
#else
|
|
||||||
return getpagesize();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
constexpr auto memory_readonly = PROT_READ;
|
|
||||||
constexpr auto memory_readwrite = PROT_READ | PROT_WRITE;
|
|
||||||
#if IS_APPLE
|
|
||||||
int get_page_protections(void* page) {
|
|
||||||
// https://stackoverflow.com/a/12627784/15675011
|
|
||||||
#ifdef HAS_MACH_VM
|
|
||||||
mach_vm_size_t vmsize;
|
|
||||||
mach_vm_address_t address = (mach_vm_address_t)page;
|
|
||||||
#else
|
|
||||||
vm_size_t vmsize;
|
|
||||||
vm_address_t address = (vm_address_t)page;
|
|
||||||
#endif
|
|
||||||
vm_region_basic_info_data_t info;
|
|
||||||
mach_msg_type_number_t info_count =
|
|
||||||
sizeof(size_t) == 8 ? VM_REGION_BASIC_INFO_COUNT_64 : VM_REGION_BASIC_INFO_COUNT;
|
|
||||||
memory_object_name_t object;
|
|
||||||
kern_return_t status =
|
|
||||||
#ifdef HAS_MACH_VM
|
|
||||||
mach_vm_region
|
|
||||||
#else
|
|
||||||
vm_region_64
|
|
||||||
#endif
|
|
||||||
(
|
|
||||||
mach_task_self(),
|
|
||||||
&address,
|
|
||||||
&vmsize,
|
|
||||||
VM_REGION_BASIC_INFO,
|
|
||||||
(vm_region_info_t)&info,
|
|
||||||
&info_count,
|
|
||||||
&object
|
|
||||||
);
|
|
||||||
if(status == KERN_INVALID_ADDRESS) {
|
|
||||||
throw std::runtime_error("vm_region failed with KERN_INVALID_ADDRESS");
|
|
||||||
}
|
|
||||||
int perms = 0;
|
|
||||||
if(info.protection & VM_PROT_READ) {
|
|
||||||
perms |= PROT_READ;
|
|
||||||
}
|
|
||||||
if(info.protection & VM_PROT_WRITE) {
|
|
||||||
perms |= PROT_WRITE;
|
|
||||||
}
|
|
||||||
if(info.protection & VM_PROT_EXECUTE) {
|
|
||||||
perms |= PROT_EXEC;
|
|
||||||
}
|
|
||||||
return perms;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
int get_page_protections(void* page) {
|
|
||||||
auto page_addr = reinterpret_cast<uintptr_t>(page);
|
|
||||||
std::ifstream stream("/proc/self/maps");
|
|
||||||
stream>>std::hex;
|
|
||||||
while(!stream.eof()) {
|
|
||||||
uintptr_t start;
|
|
||||||
uintptr_t stop;
|
|
||||||
stream>>start;
|
|
||||||
stream.ignore(1); // dash
|
|
||||||
stream>>stop;
|
|
||||||
if(stream.eof()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if(stream.fail()) {
|
|
||||||
throw std::runtime_error("Failure reading /proc/self/maps");
|
|
||||||
}
|
|
||||||
if(page_addr >= start && page_addr < stop) {
|
|
||||||
stream.ignore(1); // space
|
|
||||||
char r, w, x; // there's a private/shared flag after these but we don't need it
|
|
||||||
stream>>r>>w>>x;
|
|
||||||
if(stream.fail() || stream.eof()) {
|
|
||||||
throw std::runtime_error("Failure reading /proc/self/maps");
|
|
||||||
}
|
|
||||||
int perms = 0;
|
|
||||||
if(r == 'r') {
|
|
||||||
perms |= PROT_READ;
|
|
||||||
}
|
|
||||||
if(w == 'w') {
|
|
||||||
perms |= PROT_WRITE;
|
|
||||||
}
|
|
||||||
if(x == 'x') {
|
|
||||||
perms |= PROT_EXEC;
|
|
||||||
}
|
|
||||||
// std::cerr<<"--parsed: "<<std::hex<<start<<" "<<stop<<" "<<r<<w<<x<<std::endl;
|
|
||||||
return perms;
|
|
||||||
}
|
|
||||||
stream.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
|
|
||||||
}
|
|
||||||
throw std::runtime_error("Failed to find mapping with page in /proc/self/maps");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
void mprotect_page(void* page, int page_size, int protections) {
|
|
||||||
if(mprotect(page, page_size, protections) != 0) {
|
|
||||||
throw std::runtime_error(microfmt::format("mprotect call failed: {}", strerror(errno)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int mprotect_page_and_return_old_protections(void* page, int page_size, int protections) {
|
|
||||||
auto old_protections = get_page_protections(page);
|
|
||||||
mprotect_page(page, page_size, protections);
|
|
||||||
return old_protections;
|
|
||||||
}
|
|
||||||
void* allocate_page(int page_size) {
|
|
||||||
auto page = mmap(nullptr, page_size, memory_readwrite, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
|
||||||
if(page == MAP_FAILED) {
|
|
||||||
throw std::runtime_error(microfmt::format("mmap call failed: {}", strerror(errno)));
|
|
||||||
}
|
|
||||||
return page;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void perform_typeinfo_surgery(const std::type_info& info, do_catch_fn* do_catch_function) {
|
|
||||||
if(vtable_size == 0) { // set to zero if we don't know what standard library we're working with
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
void* type_info_pointer = const_cast<void*>(static_cast<const void*>(&info));
|
|
||||||
void* type_info_vtable_pointer = *static_cast<void**>(type_info_pointer);
|
|
||||||
// the type info vtable pointer points to two pointers inside the vtable, adjust it back
|
|
||||||
type_info_vtable_pointer = static_cast<void*>(static_cast<void**>(type_info_vtable_pointer) - 2);
|
|
||||||
|
|
||||||
// for libstdc++ the class type info vtable looks like
|
|
||||||
// 0x7ffff7f89d18 <_ZTVN10__cxxabiv117__class_type_infoE>: 0x0000000000000000 0x00007ffff7f89d00
|
|
||||||
// [offset ][typeinfo pointer ]
|
|
||||||
// 0x7ffff7f89d28 <_ZTVN10__cxxabiv117__class_type_infoE+16>: 0x00007ffff7dd65a0 0x00007ffff7dd65c0
|
|
||||||
// [base destructor ][deleting dtor ]
|
|
||||||
// 0x7ffff7f89d38 <_ZTVN10__cxxabiv117__class_type_infoE+32>: 0x00007ffff7dd8f10 0x00007ffff7dd8f10
|
|
||||||
// [__is_pointer_p ][__is_function_p ]
|
|
||||||
// 0x7ffff7f89d48 <_ZTVN10__cxxabiv117__class_type_infoE+48>: 0x00007ffff7dd6640 0x00007ffff7dd6500
|
|
||||||
// [__do_catch ][__do_upcast ]
|
|
||||||
// 0x7ffff7f89d58 <_ZTVN10__cxxabiv117__class_type_infoE+64>: 0x00007ffff7dd65e0 0x00007ffff7dd66d0
|
|
||||||
// [__do_upcast ][__do_dyncast ]
|
|
||||||
// 0x7ffff7f89d68 <_ZTVN10__cxxabiv117__class_type_infoE+80>: 0x00007ffff7dd6580 0x00007ffff7f8abe8
|
|
||||||
// [__do_find_public_src][other ]
|
|
||||||
// In libc++ the layout is
|
|
||||||
// [offset ][typeinfo pointer ]
|
|
||||||
// [base destructor ][deleting dtor ]
|
|
||||||
// [noop1 ][noop2 ]
|
|
||||||
// [can_catch ][search_above_dst ]
|
|
||||||
// [search_below_dst ][has_unambiguous_public_base]
|
|
||||||
// Relevant documentation/implementation:
|
|
||||||
// https://itanium-cxx-abi.github.io/cxx-abi/abi.html
|
|
||||||
// libstdc++
|
|
||||||
// https://github.com/gcc-mirror/gcc/blob/b13e34699c7d27e561fcfe1b66ced1e50e69976f/libstdc%252B%252B-v3/libsupc%252B%252B/typeinfo
|
|
||||||
// https://github.com/gcc-mirror/gcc/blob/b13e34699c7d27e561fcfe1b66ced1e50e69976f/libstdc%252B%252B-v3/libsupc%252B%252B/class_type_info.cc
|
|
||||||
// libc++
|
|
||||||
// https://github.com/llvm/llvm-project/blob/648f4d0658ab00cf1e95330c8811aaea9481a274/libcxx/include/typeinfo
|
|
||||||
// https://github.com/llvm/llvm-project/blob/648f4d0658ab00cf1e95330c8811aaea9481a274/libcxxabi/src/private_typeinfo.h
|
|
||||||
|
|
||||||
// shouldn't be anything other than 4096 but out of an abundance of caution
|
|
||||||
auto page_size = get_page_size();
|
|
||||||
if(page_size <= 0 && (page_size & (page_size - 1)) != 0) {
|
|
||||||
throw std::runtime_error(
|
|
||||||
microfmt::format("getpagesize() is not a power of 2 greater than zero (was {})", page_size)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// allocate a page for the new vtable so it can be made read-only later
|
|
||||||
// the OS cleans this up, no cleanup done here for it
|
|
||||||
void* new_vtable_page = allocate_page(page_size);
|
|
||||||
// make our own copy of the vtable
|
|
||||||
memcpy(new_vtable_page, type_info_vtable_pointer, vtable_size * sizeof(void*));
|
|
||||||
// ninja in the custom __do_catch interceptor
|
|
||||||
auto new_vtable = static_cast<void**>(new_vtable_page);
|
|
||||||
new_vtable[6] = reinterpret_cast<void*>(do_catch_function);
|
|
||||||
// make the page read-only
|
|
||||||
mprotect_page(new_vtable_page, page_size, memory_readonly);
|
|
||||||
|
|
||||||
// make the vtable pointer for unwind_interceptor's type_info point to the new vtable
|
|
||||||
auto type_info_addr = reinterpret_cast<uintptr_t>(type_info_pointer);
|
|
||||||
auto page_addr = type_info_addr & ~(page_size - 1);
|
|
||||||
// make sure the memory we're going to set is within the page
|
|
||||||
if(type_info_addr - page_addr + sizeof(void*) > static_cast<unsigned>(page_size)) {
|
|
||||||
throw std::runtime_error("pointer crosses page boundaries");
|
|
||||||
}
|
|
||||||
auto old_protections = mprotect_page_and_return_old_protections(
|
|
||||||
reinterpret_cast<void*>(page_addr),
|
|
||||||
page_size,
|
|
||||||
memory_readwrite
|
|
||||||
);
|
|
||||||
*static_cast<void**>(type_info_pointer) = static_cast<void*>(new_vtable + 2);
|
|
||||||
mprotect_page(reinterpret_cast<void*>(page_addr), page_size, old_protections);
|
|
||||||
}
|
|
||||||
|
|
||||||
void do_prepare_unwind_interceptor(char(*intercept_unwind_handler)(std::size_t)) {
|
|
||||||
static bool did_prepare = false;
|
|
||||||
if(!did_prepare) {
|
|
||||||
cpptrace::detail::intercept_unwind_handler = intercept_unwind_handler;
|
|
||||||
try {
|
|
||||||
perform_typeinfo_surgery(typeid(cpptrace::detail::unwind_interceptor), intercept_unwind);
|
|
||||||
perform_typeinfo_surgery(
|
|
||||||
typeid(cpptrace::detail::unconditional_unwind_interceptor),
|
|
||||||
unconditional_exception_unwind_interceptor
|
|
||||||
);
|
|
||||||
} catch(std::exception& e) {
|
|
||||||
std::fprintf(
|
|
||||||
stderr,
|
|
||||||
"Cpptrace: Exception occurred while preparing from_current support: %s\n",
|
|
||||||
e.what()
|
|
||||||
);
|
|
||||||
} catch(...) {
|
|
||||||
std::fprintf(stderr, "Cpptrace: Unknown exception occurred while preparing from_current support\n");
|
|
||||||
}
|
|
||||||
did_prepare = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
const raw_trace& raw_trace_from_current_exception() {
|
|
||||||
return detail::current_exception_trace.get_raw_trace();
|
|
||||||
}
|
|
||||||
|
|
||||||
const stacktrace& from_current_exception() {
|
|
||||||
return detail::current_exception_trace.get_resolved_trace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
17
src/full/full_trace.hpp
Normal file
17
src/full/full_trace.hpp
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#ifndef FULL_TRACE_HPP
|
||||||
|
#define FULL_TRACE_HPP
|
||||||
|
|
||||||
|
#include <cpptrace/cpptrace.hpp>
|
||||||
|
#include "../platform/common.hpp"
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace cpptrace {
|
||||||
|
namespace detail {
|
||||||
|
CPPTRACE_FORCE_NO_INLINE
|
||||||
|
std::vector<stacktrace_frame> generate_trace(size_t skip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
93
src/full/full_trace_with_libbacktrace.cpp
Normal file
93
src/full/full_trace_with_libbacktrace.cpp
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
#ifdef CPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE
|
||||||
|
|
||||||
|
#include <cpptrace/cpptrace.hpp>
|
||||||
|
#include "../platform/program_name.hpp"
|
||||||
|
#include "../platform/common.hpp"
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <mutex>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#ifdef CPPTRACE_BACKTRACE_PATH
|
||||||
|
#include CPPTRACE_BACKTRACE_PATH
|
||||||
|
#else
|
||||||
|
#include <backtrace.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace cpptrace {
|
||||||
|
namespace detail {
|
||||||
|
struct trace_data {
|
||||||
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-const-or-ref-data-members)
|
||||||
|
std::vector<stacktrace_frame>& frames;
|
||||||
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-const-or-ref-data-members)
|
||||||
|
size_t& skip;
|
||||||
|
};
|
||||||
|
|
||||||
|
int full_callback(void* data_pointer, uintptr_t address, const char* file, int line, const char* symbol) {
|
||||||
|
trace_data& data = *reinterpret_cast<trace_data*>(data_pointer);
|
||||||
|
if(data.skip > 0) {
|
||||||
|
data.skip--;
|
||||||
|
} else if(address == uintptr_t(-1)) {
|
||||||
|
// sentinel for libbacktrace, stop tracing
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
data.frames.push_back({
|
||||||
|
address,
|
||||||
|
static_cast<std::uint_least32_t>(line),
|
||||||
|
0,
|
||||||
|
file ? file : "",
|
||||||
|
symbol ? symbol : ""
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void syminfo_callback(void* data, uintptr_t, const char* symbol, uintptr_t, uintptr_t) {
|
||||||
|
stacktrace_frame& frame = *static_cast<stacktrace_frame*>(data);
|
||||||
|
frame.symbol = symbol ? symbol : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
void error_callback(void*, const char* msg, int errnum) {
|
||||||
|
fprintf(stderr, "Libbacktrace error: %s, code %d\n", msg, errnum);
|
||||||
|
}
|
||||||
|
|
||||||
|
backtrace_state* get_backtrace_state() {
|
||||||
|
static std::mutex mutex;
|
||||||
|
const std::lock_guard<std::mutex> lock(mutex);
|
||||||
|
// backtrace_create_state must be called only one time per program
|
||||||
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
|
static backtrace_state* state = nullptr;
|
||||||
|
static bool called = false;
|
||||||
|
if(!called) {
|
||||||
|
state = backtrace_create_state(nullptr, true, error_callback, nullptr);
|
||||||
|
called = true;
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
CPPTRACE_FORCE_NO_INLINE
|
||||||
|
std::vector<stacktrace_frame> generate_trace(size_t skip) {
|
||||||
|
std::vector<stacktrace_frame> frames;
|
||||||
|
skip++; // add one for this call
|
||||||
|
trace_data data { frames, skip };
|
||||||
|
backtrace_full(get_backtrace_state(), 0, full_callback, error_callback, &data);
|
||||||
|
for(auto& frame : frames) {
|
||||||
|
if(frame.symbol.empty()) {
|
||||||
|
// fallback, try to at least recover the symbol name with backtrace_syminfo
|
||||||
|
backtrace_syminfo(
|
||||||
|
get_backtrace_state(),
|
||||||
|
frame.address,
|
||||||
|
syminfo_callback,
|
||||||
|
error_callback,
|
||||||
|
&frame
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return frames;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
30
src/full/full_trace_with_stacktrace.cpp
Normal file
30
src/full/full_trace_with_stacktrace.cpp
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#ifdef CPPTRACE_FULL_TRACE_WITH_STACKTRACE
|
||||||
|
|
||||||
|
#include <cpptrace/cpptrace.hpp>
|
||||||
|
#include "full_trace.hpp"
|
||||||
|
#include "../platform/common.hpp"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <stacktrace>
|
||||||
|
|
||||||
|
namespace cpptrace {
|
||||||
|
namespace detail {
|
||||||
|
CPPTRACE_FORCE_NO_INLINE
|
||||||
|
std::vector<stacktrace_frame> generate_trace(size_t skip) {
|
||||||
|
std::vector<stacktrace_frame> frames;
|
||||||
|
std::stacktrace trace = std::stacktrace::current(skip + 1);
|
||||||
|
for(const auto entry : trace) {
|
||||||
|
frames.push_back({
|
||||||
|
entry.native_handle(),
|
||||||
|
entry.source_line(),
|
||||||
|
0,
|
||||||
|
entry.source_file(),
|
||||||
|
entry.description()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return frames;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -1,41 +0,0 @@
|
|||||||
#include <cpptrace/basic.hpp>
|
|
||||||
|
|
||||||
#include "options.hpp"
|
|
||||||
|
|
||||||
#include <atomic>
|
|
||||||
|
|
||||||
namespace cpptrace {
|
|
||||||
namespace detail {
|
|
||||||
std::atomic_bool absorb_trace_exceptions(true); // NOSONAR
|
|
||||||
std::atomic_bool resolve_inlined_calls(true); // NOSONAR
|
|
||||||
std::atomic<cache_mode> current_cache_mode(cache_mode::prioritize_speed); // NOSONAR
|
|
||||||
}
|
|
||||||
|
|
||||||
void absorb_trace_exceptions(bool absorb) {
|
|
||||||
detail::absorb_trace_exceptions = absorb;
|
|
||||||
}
|
|
||||||
|
|
||||||
void enable_inlined_call_resolution(bool enable) {
|
|
||||||
detail::resolve_inlined_calls = enable;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace experimental {
|
|
||||||
void set_cache_mode(cache_mode mode) {
|
|
||||||
detail::current_cache_mode = mode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace detail {
|
|
||||||
bool should_absorb_trace_exceptions() {
|
|
||||||
return absorb_trace_exceptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool should_resolve_inlined_calls() {
|
|
||||||
return resolve_inlined_calls;
|
|
||||||
}
|
|
||||||
|
|
||||||
cache_mode get_cache_mode() {
|
|
||||||
return current_cache_mode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
#ifndef OPTIONS_HPP
|
|
||||||
#define OPTIONS_HPP
|
|
||||||
|
|
||||||
#include <cpptrace/utils.hpp>
|
|
||||||
|
|
||||||
namespace cpptrace {
|
|
||||||
namespace detail {
|
|
||||||
// exported for test purposes
|
|
||||||
CPPTRACE_EXPORT bool should_absorb_trace_exceptions();
|
|
||||||
bool should_resolve_inlined_calls();
|
|
||||||
cache_mode get_cache_mode();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
273
src/platform/common.hpp
Normal file
273
src/platform/common.hpp
Normal file
@ -0,0 +1,273 @@
|
|||||||
|
#ifndef COMMON_HPP
|
||||||
|
#define COMMON_HPP
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#define CPPTRACE_FORCE_NO_INLINE __declspec(noinline)
|
||||||
|
#define CPPTRACE_PFUNC __FUNCSIG__
|
||||||
|
#define CPPTRACE_MAYBE_UNUSED
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable: 4505) // Unused local function
|
||||||
|
#else
|
||||||
|
#define CPPTRACE_FORCE_NO_INLINE __attribute__((noinline))
|
||||||
|
#define CPPTRACE_PFUNC __extension__ __PRETTY_FUNCTION__
|
||||||
|
#define CPPTRACE_MAYBE_UNUSED __attribute__((unused))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <exception>
|
||||||
|
#include <ios>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#define IS_WINDOWS 0
|
||||||
|
#define IS_LINUX 0
|
||||||
|
#define IS_APPLE 0
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
#undef IS_WINDOWS
|
||||||
|
#define IS_WINDOWS 1
|
||||||
|
#elif defined(__linux)
|
||||||
|
#undef IS_LINUX
|
||||||
|
#define IS_LINUX 1
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
#undef IS_APPLE
|
||||||
|
#define IS_APPLE 1
|
||||||
|
#else
|
||||||
|
#error "Unexpected platform"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define IS_CLANG 0
|
||||||
|
#define IS_GCC 0
|
||||||
|
#define IS_MSVC 0
|
||||||
|
|
||||||
|
#if defined(__clang__)
|
||||||
|
#undef IS_CLANG
|
||||||
|
#define IS_CLANG 1
|
||||||
|
#elif defined(__GNUC__) || defined(__GNUG__)
|
||||||
|
#undef IS_GCC
|
||||||
|
#define IS_GCC 1
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
#undef IS_MSVC
|
||||||
|
#define IS_MSVC 1
|
||||||
|
#else
|
||||||
|
#error "Unsupported compiler"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if IS_WINDOWS
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Lightweight std::source_location.
|
||||||
|
struct source_location {
|
||||||
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-const-or-ref-data-members)
|
||||||
|
const char* const file;
|
||||||
|
//const char* const function; // disabled for now due to static constexpr restrictions
|
||||||
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-const-or-ref-data-members)
|
||||||
|
const int line;
|
||||||
|
constexpr source_location(
|
||||||
|
//const char* _function /*= __builtin_FUNCTION()*/,
|
||||||
|
const char* _file = __builtin_FILE(),
|
||||||
|
int _line = __builtin_LINE()
|
||||||
|
) : file(_file), /*function(_function),*/ line(_line) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
CPPTRACE_MAYBE_UNUSED
|
||||||
|
static void primitive_assert_impl(
|
||||||
|
bool condition,
|
||||||
|
bool verify,
|
||||||
|
const char* expression,
|
||||||
|
const char* signature,
|
||||||
|
source_location location,
|
||||||
|
const char* message = nullptr
|
||||||
|
) {
|
||||||
|
if(!condition) {
|
||||||
|
const char* action = verify ? "verification" : "assertion";
|
||||||
|
const char* name = verify ? "verify" : "assert";
|
||||||
|
if(message == nullptr) {
|
||||||
|
(void) fprintf(
|
||||||
|
stderr,
|
||||||
|
"Cpptrace %s failed at %s:%d: %s\n",
|
||||||
|
action, location.file, location.line, signature
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
(void) fprintf(
|
||||||
|
stderr,
|
||||||
|
"Cpptrace %s failed at %s:%d: %s: %s\n",
|
||||||
|
action, location.file, location.line, signature, message
|
||||||
|
);
|
||||||
|
}
|
||||||
|
(void) fprintf(stderr, " primitive_%s(%s);\n", name, expression);
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void nothing() {}
|
||||||
|
#define PHONY_USE(E) (nothing<decltype(E)>())
|
||||||
|
|
||||||
|
// Still present in release mode, nonfatal
|
||||||
|
#define internal_verify(c, ...) primitive_assert_impl(c, true, #c, CPPTRACE_PFUNC, {}, ##__VA_ARGS__)
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
#define CPPTRACE_PRIMITIVE_ASSERT(c, ...) \
|
||||||
|
primitive_assert_impl(c, false, #c, CPPTRACE_PFUNC, {}, ##__VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define CPPTRACE_PRIMITIVE_ASSERT(c, ...) PHONY_USE(c)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
CPPTRACE_MAYBE_UNUSED
|
||||||
|
static std::vector<std::string> split(const std::string& str, const std::string& delims) {
|
||||||
|
std::vector<std::string> vec;
|
||||||
|
size_t old_pos = 0;
|
||||||
|
size_t pos = 0;
|
||||||
|
while((pos = str.find_first_of(delims, old_pos)) != std::string::npos) {
|
||||||
|
vec.emplace_back(str.substr(old_pos, pos - old_pos));
|
||||||
|
old_pos = pos + 1;
|
||||||
|
}
|
||||||
|
vec.emplace_back(str.substr(old_pos));
|
||||||
|
return vec;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename C>
|
||||||
|
CPPTRACE_MAYBE_UNUSED
|
||||||
|
static std::string join(const C& container, const std::string& delim) {
|
||||||
|
auto iter = std::begin(container);
|
||||||
|
auto end = std::end(container);
|
||||||
|
std::string str;
|
||||||
|
if(std::distance(iter, end) > 0) {
|
||||||
|
str += *iter;
|
||||||
|
while(++iter != end) {
|
||||||
|
str += delim;
|
||||||
|
str += *iter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr const char* const whitespace = " \t\n\r\f\v";
|
||||||
|
|
||||||
|
CPPTRACE_MAYBE_UNUSED
|
||||||
|
static std::string trim(const std::string& str) {
|
||||||
|
if(str.empty()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
const size_t left = str.find_first_not_of(whitespace);
|
||||||
|
const size_t right = str.find_last_not_of(whitespace) + 1;
|
||||||
|
return str.substr(left, right - left);
|
||||||
|
}
|
||||||
|
|
||||||
|
CPPTRACE_MAYBE_UNUSED
|
||||||
|
static std::string to_hex(uintptr_t addr) {
|
||||||
|
std::stringstream sstream;
|
||||||
|
sstream<<std::hex<<addr;
|
||||||
|
return std::move(sstream).str();
|
||||||
|
}
|
||||||
|
|
||||||
|
CPPTRACE_MAYBE_UNUSED
|
||||||
|
static bool is_little_endian() {
|
||||||
|
uint16_t num = 0x1;
|
||||||
|
auto* ptr = (uint8_t*)#
|
||||||
|
return ptr[0] == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modified from
|
||||||
|
// https://stackoverflow.com/questions/105252/how-do-i-convert-between-big-endian-and-little-endian-values-in-c
|
||||||
|
template<typename T, size_t N>
|
||||||
|
struct byte_swapper;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct byte_swapper<T, 1> {
|
||||||
|
T operator()(T val) {
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct byte_swapper<T, 2> {
|
||||||
|
T operator()(T val) {
|
||||||
|
return ((((val) >> 8) & 0xff) | (((val) & 0xff) << 8));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct byte_swapper<T, 4> {
|
||||||
|
T operator()(T val) {
|
||||||
|
return ((((val) & 0xff000000) >> 24) |
|
||||||
|
(((val) & 0x00ff0000) >> 8) |
|
||||||
|
(((val) & 0x0000ff00) << 8) |
|
||||||
|
(((val) & 0x000000ff) << 24));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct byte_swapper<T, 8> {
|
||||||
|
T operator()(T val) {
|
||||||
|
return ((((val) & 0xff00000000000000ull) >> 56) |
|
||||||
|
(((val) & 0x00ff000000000000ull) >> 40) |
|
||||||
|
(((val) & 0x0000ff0000000000ull) >> 24) |
|
||||||
|
(((val) & 0x000000ff00000000ull) >> 8 ) |
|
||||||
|
(((val) & 0x00000000ff000000ull) << 8 ) |
|
||||||
|
(((val) & 0x0000000000ff0000ull) << 24) |
|
||||||
|
(((val) & 0x000000000000ff00ull) << 40) |
|
||||||
|
(((val) & 0x00000000000000ffull) << 56));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
|
||||||
|
T byteswap(T value) {
|
||||||
|
return byte_swapper<T, sizeof(T)>{}(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
CPPTRACE_MAYBE_UNUSED
|
||||||
|
inline void enable_virtual_terminal_processing_if_needed() {
|
||||||
|
// enable colors / ansi processing if necessary
|
||||||
|
#if IS_WINDOWS
|
||||||
|
// https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences#example-of-enabling-virtual-terminal-processing
|
||||||
|
#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
|
||||||
|
constexpr DWORD ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x4;
|
||||||
|
#endif
|
||||||
|
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||||
|
DWORD dwMode = 0;
|
||||||
|
if(hOut == INVALID_HANDLE_VALUE) return;
|
||||||
|
if(!GetConsoleMode(hOut, &dwMode)) return;
|
||||||
|
if(dwMode != (dwMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING))
|
||||||
|
if(!SetConsoleMode(hOut, dwMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING)) return;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
CPPTRACE_MAYBE_UNUSED
|
||||||
|
// NOLINTNEXTLINE(misc-no-recursion)
|
||||||
|
inline constexpr unsigned n_digits(unsigned value) {
|
||||||
|
return value < 10 ? 1 : 1 + n_digits(value / 10);
|
||||||
|
}
|
||||||
|
static_assert(n_digits(1) == 1, "n_digits utility producing the wrong result");
|
||||||
|
static_assert(n_digits(9) == 1, "n_digits utility producing the wrong result");
|
||||||
|
static_assert(n_digits(10) == 2, "n_digits utility producing the wrong result");
|
||||||
|
static_assert(n_digits(11) == 2, "n_digits utility producing the wrong result");
|
||||||
|
static_assert(n_digits(1024) == 4, "n_digits utility producing the wrong result");
|
||||||
|
|
||||||
|
// TODO: Re-evaluate use of off_t
|
||||||
|
template<typename T, typename std::enable_if<std::is_pod<T>::value, int>::type = 0>
|
||||||
|
T load_bytes(FILE* obj_file, off_t offset) {
|
||||||
|
T object;
|
||||||
|
internal_verify(fseek(obj_file, offset, SEEK_SET) == 0, "fseek error");
|
||||||
|
internal_verify(fread(&object, sizeof(T), 1, obj_file) == 1, "fread error");
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
|
class file_error : std::exception {
|
||||||
|
const char* what() const noexcept override {
|
||||||
|
return "Unable to read file";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -1,149 +0,0 @@
|
|||||||
#include "platform/platform.hpp"
|
|
||||||
|
|
||||||
#if IS_WINDOWS
|
|
||||||
|
|
||||||
#include "platform/dbghelp_utils.hpp"
|
|
||||||
|
|
||||||
#if defined(CPPTRACE_UNWIND_WITH_DBGHELP) \
|
|
||||||
|| defined(CPPTRACE_GET_SYMBOLS_WITH_DBGHELP) \
|
|
||||||
|| defined(CPPTRACE_DEMANGLE_WITH_WINAPI)
|
|
||||||
|
|
||||||
#include "utils/error.hpp"
|
|
||||||
#include "utils/microfmt.hpp"
|
|
||||||
#include "utils/utils.hpp"
|
|
||||||
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
#ifndef WIN32_LEAN_AND_MEAN
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
|
||||||
#endif
|
|
||||||
#include <windows.h>
|
|
||||||
#include <dbghelp.h>
|
|
||||||
|
|
||||||
namespace cpptrace {
|
|
||||||
namespace detail {
|
|
||||||
dbghelp_syminit_info::dbghelp_syminit_info(void* handle, bool should_sym_cleanup, bool should_close_handle)
|
|
||||||
: handle(handle), should_sym_cleanup(should_sym_cleanup), should_close_handle(should_close_handle) {}
|
|
||||||
|
|
||||||
dbghelp_syminit_info::~dbghelp_syminit_info() {
|
|
||||||
release();
|
|
||||||
}
|
|
||||||
|
|
||||||
void dbghelp_syminit_info::release() {
|
|
||||||
if(!handle) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(should_sym_cleanup) {
|
|
||||||
if(!SymCleanup(handle)) {
|
|
||||||
throw internal_error("SymCleanup failed with code {}\n", GetLastError());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(should_close_handle) {
|
|
||||||
if(!CloseHandle(handle)) {
|
|
||||||
throw internal_error("CloseHandle failed with code {}\n", GetLastError());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dbghelp_syminit_info dbghelp_syminit_info::make_not_owned(void* handle) {
|
|
||||||
return dbghelp_syminit_info(handle, false, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
dbghelp_syminit_info dbghelp_syminit_info::make_owned(void* handle, bool should_close_handle) {
|
|
||||||
return dbghelp_syminit_info(handle, true, should_close_handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
dbghelp_syminit_info::dbghelp_syminit_info(dbghelp_syminit_info&& other) {
|
|
||||||
handle = exchange(other.handle, nullptr);
|
|
||||||
should_sym_cleanup = other.should_sym_cleanup;
|
|
||||||
should_close_handle = other.should_close_handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
dbghelp_syminit_info& dbghelp_syminit_info::operator=(dbghelp_syminit_info&& other) {
|
|
||||||
release();
|
|
||||||
handle = exchange(other.handle, nullptr);
|
|
||||||
should_sym_cleanup = other.should_sym_cleanup;
|
|
||||||
should_close_handle = other.should_close_handle;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void* dbghelp_syminit_info::get_process_handle() const {
|
|
||||||
return handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
dbghelp_syminit_info dbghelp_syminit_info::make_non_owning_view() const {
|
|
||||||
return make_not_owned(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unordered_map<HANDLE, dbghelp_syminit_info>& get_syminit_cache() {
|
|
||||||
static std::unordered_map<HANDLE, dbghelp_syminit_info> syminit_cache;
|
|
||||||
return syminit_cache;
|
|
||||||
}
|
|
||||||
|
|
||||||
dbghelp_syminit_info ensure_syminit() {
|
|
||||||
auto lock = get_dbghelp_lock(); // locking around the entire access of the cache unordered_map
|
|
||||||
HANDLE proc = GetCurrentProcess();
|
|
||||||
if(get_cache_mode() == cache_mode::prioritize_speed) {
|
|
||||||
auto& syminit_cache = get_syminit_cache();
|
|
||||||
auto it = syminit_cache.find(proc);
|
|
||||||
if(it != syminit_cache.end()) {
|
|
||||||
return it->second.make_non_owning_view();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto duplicated_handle = raii_wrap<void*>(nullptr, [] (void* handle) {
|
|
||||||
if(handle) {
|
|
||||||
if(!CloseHandle(handle)) {
|
|
||||||
throw internal_error("CloseHandle failed with code {}\n", GetLastError());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// https://github.com/jeremy-rifkin/cpptrace/issues/204
|
|
||||||
// https://github.com/jeremy-rifkin/cpptrace/pull/206
|
|
||||||
// https://learn.microsoft.com/en-us/windows/win32/debug/initializing-the-symbol-handler
|
|
||||||
// Apparently duplicating the process handle is the idiomatic thing to do and this avoids issues of
|
|
||||||
// SymInitialize being called twice.
|
|
||||||
// DuplicateHandle requires the PROCESS_DUP_HANDLE access right. If for some reason DuplicateHandle we fall back
|
|
||||||
// to calling SymInitialize on the process handle.
|
|
||||||
optional<DWORD> maybe_duplicate_handle_error_code;
|
|
||||||
if(!DuplicateHandle(proc, proc, proc, &duplicated_handle.get(), 0, FALSE, DUPLICATE_SAME_ACCESS)) {
|
|
||||||
maybe_duplicate_handle_error_code = GetLastError();
|
|
||||||
}
|
|
||||||
if(!SymInitialize(maybe_duplicate_handle_error_code ? proc : duplicated_handle.get(), NULL, TRUE)) {
|
|
||||||
if(maybe_duplicate_handle_error_code) {
|
|
||||||
throw internal_error(
|
|
||||||
"SymInitialize failed with error code {} after DuplicateHandle failed with error code {}",
|
|
||||||
GetLastError(),
|
|
||||||
maybe_duplicate_handle_error_code.unwrap()
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
throw internal_error("SymInitialize failed with error code {}", GetLastError());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto info = dbghelp_syminit_info::make_owned(
|
|
||||||
maybe_duplicate_handle_error_code ? proc : exchange(duplicated_handle.get(), nullptr),
|
|
||||||
!maybe_duplicate_handle_error_code
|
|
||||||
);
|
|
||||||
// either cache and return a view or return the owning wrapper
|
|
||||||
if(get_cache_mode() == cache_mode::prioritize_speed) {
|
|
||||||
auto& syminit_cache = get_syminit_cache();
|
|
||||||
auto pair = syminit_cache.insert({proc, std::move(info)});
|
|
||||||
VERIFY(pair.second);
|
|
||||||
return pair.first->second.make_non_owning_view();
|
|
||||||
} else {
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::recursive_mutex dbghelp_lock;
|
|
||||||
|
|
||||||
std::unique_lock<std::recursive_mutex> get_dbghelp_lock() {
|
|
||||||
return std::unique_lock<std::recursive_mutex>{dbghelp_lock};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@ -1,50 +0,0 @@
|
|||||||
#ifndef DBGHELP_UTILS_HPP
|
|
||||||
#define DBGHELP_UTILS_HPP
|
|
||||||
|
|
||||||
#if defined(CPPTRACE_UNWIND_WITH_DBGHELP) \
|
|
||||||
|| defined(CPPTRACE_GET_SYMBOLS_WITH_DBGHELP) \
|
|
||||||
|| defined(CPPTRACE_DEMANGLE_WITH_WINAPI)
|
|
||||||
|
|
||||||
#include "utils/common.hpp"
|
|
||||||
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <mutex>
|
|
||||||
|
|
||||||
namespace cpptrace {
|
|
||||||
namespace detail {
|
|
||||||
class dbghelp_syminit_info {
|
|
||||||
// `void*` is used to avoid including the (expensive) windows.h header here
|
|
||||||
void* handle = nullptr;
|
|
||||||
bool should_sym_cleanup; // true if cleanup is not managed by the syminit cache
|
|
||||||
bool should_close_handle; // true if cleanup is not managed by the syminit cache and the handle was duplicated
|
|
||||||
dbghelp_syminit_info(void* handle, bool should_sym_cleanup, bool should_close_handle);
|
|
||||||
public:
|
|
||||||
~dbghelp_syminit_info();
|
|
||||||
void release();
|
|
||||||
|
|
||||||
NODISCARD static dbghelp_syminit_info make_not_owned(void* handle);
|
|
||||||
NODISCARD static dbghelp_syminit_info make_owned(void* handle, bool should_close_handle);
|
|
||||||
|
|
||||||
dbghelp_syminit_info(const dbghelp_syminit_info&) = delete;
|
|
||||||
dbghelp_syminit_info(dbghelp_syminit_info&&);
|
|
||||||
dbghelp_syminit_info& operator=(const dbghelp_syminit_info&) = delete;
|
|
||||||
dbghelp_syminit_info& operator=(dbghelp_syminit_info&&);
|
|
||||||
|
|
||||||
void* get_process_handle() const;
|
|
||||||
dbghelp_syminit_info make_non_owning_view() const;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Ensure SymInitialize is called on the process. This function either
|
|
||||||
// - Finds that SymInitialize has been called for a handle to the current process already, in which case it returns
|
|
||||||
// a non-owning dbghelp_syminit_info instance holding the handle
|
|
||||||
// - Calls SymInitialize a handle to the current process, caches it, and returns a non-owning dbghelp_syminit_info
|
|
||||||
// - Calls SymInitialize and returns an owning dbghelp_syminit_info which will handle cleanup
|
|
||||||
dbghelp_syminit_info ensure_syminit();
|
|
||||||
|
|
||||||
NODISCARD std::unique_lock<std::recursive_mutex> get_dbghelp_lock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
93
src/platform/elf.hpp
Normal file
93
src/platform/elf.hpp
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
#ifndef ELF_HPP
|
||||||
|
#define ELF_HPP
|
||||||
|
|
||||||
|
#include "common.hpp"
|
||||||
|
|
||||||
|
#if IS_LINUX
|
||||||
|
#include <array>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include <elf.h>
|
||||||
|
|
||||||
|
template<typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
|
||||||
|
T elf_byteswap_if_needed(T value, bool elf_is_little) {
|
||||||
|
if(is_little_endian() == elf_is_little) {
|
||||||
|
return value;
|
||||||
|
} else {
|
||||||
|
return byteswap(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Address code duplication here. Do we actually have to care about 32-bit if the library is compiled as 64-bit?
|
||||||
|
// I think probably not...
|
||||||
|
|
||||||
|
// TODO: Re-evaluate use of off_t
|
||||||
|
// I think we can rely on PT_PHDR https://stackoverflow.com/q/61568612/15675011...
|
||||||
|
static uintptr_t elf_get_module_image_base_from_program_table(
|
||||||
|
FILE* file,
|
||||||
|
bool is_64,
|
||||||
|
bool is_little_endian,
|
||||||
|
off_t e_phoff,
|
||||||
|
off_t e_phentsize,
|
||||||
|
int e_phnum
|
||||||
|
) {
|
||||||
|
for(int i = 0; i < e_phnum; i++) {
|
||||||
|
if(is_64) {
|
||||||
|
Elf64_Phdr program_header = load_bytes<Elf64_Phdr>(file, e_phoff + e_phentsize * i);
|
||||||
|
if(elf_byteswap_if_needed(program_header.p_type, is_little_endian) == PT_PHDR) {
|
||||||
|
return elf_byteswap_if_needed(program_header.p_vaddr, is_little_endian)
|
||||||
|
- elf_byteswap_if_needed(program_header.p_offset, is_little_endian);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Elf32_Phdr program_header = load_bytes<Elf32_Phdr>(file, e_phoff + e_phentsize * i);
|
||||||
|
if(elf_byteswap_if_needed(program_header.p_type, is_little_endian) == PT_PHDR) {
|
||||||
|
return elf_byteswap_if_needed(program_header.p_vaddr, is_little_endian)
|
||||||
|
- elf_byteswap_if_needed(program_header.p_offset, is_little_endian);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uintptr_t elf_get_module_image_base(const std::string& obj_path) {
|
||||||
|
FILE* file = fopen(obj_path.c_str(), "rb");
|
||||||
|
if(file == nullptr) {
|
||||||
|
throw file_error();
|
||||||
|
}
|
||||||
|
// Initial checks/metadata
|
||||||
|
auto magic = load_bytes<std::array<char, 4>>(file, 0);
|
||||||
|
internal_verify(magic == (std::array<char, 4>{0x7F, 'E', 'L', 'F'}));
|
||||||
|
bool is_64 = load_bytes<uint8_t>(file, 4) == 2;
|
||||||
|
bool is_little_endian = load_bytes<uint8_t>(file, 5) == 1;
|
||||||
|
internal_verify(load_bytes<uint8_t>(file, 6) == 1, "Unexpected ELF version");
|
||||||
|
//
|
||||||
|
if(is_64) {
|
||||||
|
Elf64_Ehdr file_header = load_bytes<Elf64_Ehdr>(file, 0);
|
||||||
|
internal_verify(file_header.e_ehsize == sizeof(Elf64_Ehdr));
|
||||||
|
return elf_get_module_image_base_from_program_table(
|
||||||
|
file,
|
||||||
|
is_64,
|
||||||
|
is_little_endian,
|
||||||
|
elf_byteswap_if_needed(file_header.e_phoff, is_little_endian),
|
||||||
|
elf_byteswap_if_needed(file_header.e_phentsize, is_little_endian),
|
||||||
|
elf_byteswap_if_needed(file_header.e_phnum, is_little_endian)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
Elf32_Ehdr file_header = load_bytes<Elf32_Ehdr>(file, 0);
|
||||||
|
internal_verify(file_header.e_ehsize == sizeof(Elf32_Ehdr));
|
||||||
|
return elf_get_module_image_base_from_program_table(
|
||||||
|
file,
|
||||||
|
is_64,
|
||||||
|
is_little_endian,
|
||||||
|
elf_byteswap_if_needed(file_header.e_phoff, is_little_endian),
|
||||||
|
elf_byteswap_if_needed(file_header.e_phentsize, is_little_endian),
|
||||||
|
elf_byteswap_if_needed(file_header.e_phnum, is_little_endian)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -1,28 +0,0 @@
|
|||||||
#ifndef EXCEPTION_TYPE_HPP
|
|
||||||
#define EXCEPTION_TYPE_HPP
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "platform/platform.hpp"
|
|
||||||
|
|
||||||
// libstdc++ and libc++
|
|
||||||
#if defined(CPPTRACE_HAS_CXX_EXCEPTION_TYPE) && (IS_LIBSTDCXX || IS_LIBCXX)
|
|
||||||
#include <typeinfo>
|
|
||||||
#include <cxxabi.h>
|
|
||||||
#include "demangle/demangle.hpp"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace cpptrace {
|
|
||||||
namespace detail {
|
|
||||||
inline std::string exception_type_name() {
|
|
||||||
#if defined(CPPTRACE_HAS_CXX_EXCEPTION_TYPE) && (IS_LIBSTDCXX || IS_LIBCXX)
|
|
||||||
const std::type_info* t = abi::__cxa_current_exception_type();
|
|
||||||
return t ? detail::demangle(t->name(), false) : "<unknown>";
|
|
||||||
#else
|
|
||||||
return "<unknown>";
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user