From 743ef441c2a7dcb9b0c7300c10abb1cba839ffa1 Mon Sep 17 00:00:00 2001 From: Xuehai Pan Date: Sat, 9 Nov 2024 23:50:21 +0800 Subject: [PATCH] feat: show backtraces on Windows --- .github/workflows/tests-with-pydebug.yml | 90 +++++++++++++++++------- .github/workflows/tests.yml | 85 +++++++++++++++++----- .gitignore | 2 + Makefile | 5 +- 4 files changed, 137 insertions(+), 45 deletions(-) diff --git a/.github/workflows/tests-with-pydebug.yml b/.github/workflows/tests-with-pydebug.yml index 391eb0b6..293e99e8 100644 --- a/.github/workflows/tests-with-pydebug.yml +++ b/.github/workflows/tests-with-pydebug.yml @@ -41,7 +41,7 @@ jobs: runs-on: ${{ matrix.runner }} strategy: matrix: - runner: [ubuntu-latest, windows-latest, macos-latest] + runner: [ubuntu-latest, macos-latest, windows-latest] python-version: - "3.7" - "3.8" @@ -203,18 +203,29 @@ jobs: pyenv shims echo "::endgroup::" + PYTHON_EXE="${{ env.PYTHON }}" if [[ "${{ runner.os }}" == 'Windows' ]]; then if [[ "${{ matrix.python-abiflags }}" == *t* ]]; then - PYTHON="${PYTHON}${{ matrix.python-version }}t" + PYTHON_EXE="${PYTHON_EXE}${{ matrix.python-version }}t" fi if [[ "${{ matrix.python-abiflags }}" == *d* ]]; then - PYTHON="${PYTHON}_d" + PYTHON_EXE="${PYTHON_EXE}_d" + export PYTHON="${PYTHON}_d" + echo "PYTHON=${PYTHON}" | tee -a "${GITHUB_ENV}" + else + echo "CMAKE_BUILD_TYPE=Release" | tee -a "${GITHUB_ENV}" fi - export PYTHON="${PYTHON}" - echo "PYTHON=${PYTHON}" | tee -a "${GITHUB_ENV}" fi - "${PYTHON}" -m venv venv # PATH is already updated in step setup-pyenv + "${PYTHON_EXE}" -m venv venv # PATH is already updated in step setup-pyenv + echo "::group::Python executables" + if [[ "${{ runner.os }}" != 'Windows' ]]; then + ls -alh venv/bin + else + ls -alh venv/Scripts + fi + echo "::endgroup::" + "${PYTHON}" --version echo "::group::Upgrade pip" "${PYTHON}" -m pip install --upgrade pip setuptools wheel rich @@ -233,21 +244,40 @@ jobs: )${{ matrix.python-abiflags }}" echo "PYTHON_TAG=${PYTHON_TAG}" | tee -a "${GITHUB_ENV}" - if [[ "${{ runner.os }}" == 'Linux' ]]; then - sudo sysctl -w kernel.core_pattern="core.${PYTHON_TAG}.%P" - sudo sysctl -w kernel.core_uses_pid=0 - sudo sysctl -w fs.suid_dumpable=1 - sysctl kernel.core_pattern kernel.core_uses_pid fs.suid_dumpable - elif [[ "${{ runner.os }}" == 'macOS' ]]; then - sudo sysctl -w kern.corefile="core.${PYTHON_TAG}.%P" - sudo sysctl -w kern.coredump=1 - sudo sysctl -w kern.sugid_coredump=1 - sysctl kern.corefile kern.coredump kern.sugid_coredump - elif [[ "${{ runner.os }}" == 'Windows' ]]; then - if [[ "${{ matrix.python-abiflags }}" != *d* ]]; then - echo "CMAKE_BUILD_TYPE=Release" | tee -a "${GITHUB_ENV}" - fi - fi + - name: Enable core dump generation (Linux) + if: runner.os == 'Linux' + run: | + sudo sysctl -w kernel.core_pattern="core.${PYTHON_TAG}.%P" + sudo sysctl -w kernel.core_uses_pid=0 + sudo sysctl -w fs.suid_dumpable=1 + sysctl kernel.core_pattern kernel.core_uses_pid fs.suid_dumpable + + - name: Enable core dump generation (macOS) + if: runner.os == 'macOS' + run: | + sudo sysctl -w kern.corefile="core.${PYTHON_TAG}.%P" + sudo sysctl -w kern.coredump=1 + sudo sysctl -w kern.sugid_coredump=1 + sysctl kern.corefile kern.coredump kern.sugid_coredump + + - name: Enable core dump generation (Windows) + if: runner.os == 'Windows' + run: | + $pwd = Get-Location + $Env:_NT_SOURCE_PATH = "$pwd;${{ env.PYENV_ROOT }}\pyenv-win\versions\${{ env.PYTHON_VERSION }}" + $Env:_NT_SYMBOL_PATH = "cache*$pwd\.symcache;$Env:_NT_SOURCE_PATH;srv*https://msdl.microsoft.com/download/symbols" + Get-ChildItem -Path "C:\Program Files (x86)\Windows Kits" -Directory | Sort-Object -Property Name + $WindowsKitsDir = "C:\Program Files (x86)\Windows Kits\10" + $DebuggersDir = "$WindowsKitsDir\Debuggers\x64" + Get-ChildItem -Path $DebuggersDir + $Env:PATH = "$DebuggersDir;$Env:PATH" + $PYTEST = 'cdb -gG -o -c ".dump /ma /u core.dmp; !py; g; q" ${{ env.PYTHON }} -X dev -m pytest' + Write-Output "_NT_SOURCE_PATH=$Env:_NT_SOURCE_PATH" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append + Write-Output "_NT_SYMBOL_PATH=$Env:_NT_SYMBOL_PATH" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append + Write-Output "PATH=$Env:PATH" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append + Write-Output "PYTEST=$PYTEST" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append + + cdb -version - name: Install OpTree run: | @@ -265,6 +295,9 @@ jobs: "--junit-xml=junit-${{ env.PYTHON_TAG }}-${{ runner.os }}.xml" ) make test PYTESTOPTS="${PYTESTOPTS[*]}" + if [[ -n "$(find . -iname "core.*.[1-9]*" -o -iname "core_*.dmp")" ]]; then + exit 1 + fi - name: List generated files if: ${{ !cancelled() }} @@ -280,9 +313,9 @@ jobs: if: ${{ !cancelled() }} shell: bash run: | - if [[ -n "$(find . -iname "core.*.[1-9]*")" ]]; then + if [[ -n "$(find . -iname "core.*.[1-9]*" -o -iname "core_*.dmp")" ]]; then echo "Found core dumps:" - ls -alh $(find . -iname "core.*.[1-9]*") + ls -alh $(find . -iname "core.*.[1-9]*" -o -iname "core_*.dmp") BACKTRACE_COMMAND="" if [[ "${{ runner.os }}" == 'Linux' ]]; then echo "::group::Install GDB" @@ -297,6 +330,8 @@ jobs: brew install llvm --quiet echo "::endgroup::" BACKTRACE_COMMAND="lldb --file ${{ env.PYTHON }} --core '{}' -o 'bt' -o 'q'" + elif [[ "${{ runner.os }}" == 'Windows' ]]; then + BACKTRACE_COMMAND="cdb -z '{}' -c \"!py; !analyze -vv; .ecxr; kP; q\"" fi if [[ -n "${BACKTRACE_COMMAND}" ]]; then echo "Collecting backtraces:" @@ -305,6 +340,11 @@ jobs: ${BACKTRACE_COMMAND}; echo '::endgroup::'; " ';' + find . -iname "core_*.dmp" -exec bash -xc " + echo '::group::backtrace from: {}'; + ${BACKTRACE_COMMAND}; + echo '::endgroup::'; + " ';' fi fi @@ -324,5 +364,7 @@ jobs: uses: actions/upload-artifact@v4 with: name: coredump-${{ env.PYTHON_TAG }}-${{ runner.os }} - path: tests/core.* + path: | + tests/core.* + tests/core_*.dmp if-no-files-found: ignore diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e5bf0fd6..eec058b1 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -40,7 +40,7 @@ jobs: runs-on: ${{ matrix.runner }} strategy: matrix: - runner: [ubuntu-latest, windows-latest, macos-latest] + runner: [ubuntu-latest, macos-latest, windows-latest] python-version: - "3.7" - "3.8" @@ -75,6 +75,9 @@ jobs: run: | set -x + if [[ "${{ runner.os }}" == 'Windows' ]]; then + echo "CMAKE_BUILD_TYPE=Release" | tee -a "${GITHUB_ENV}" + fi ${{ env.PYTHON }} --version echo "::group::Upgrade pip" ${{ env.PYTHON }} -m pip install --upgrade pip setuptools wheel rich @@ -94,19 +97,40 @@ jobs: )" echo "PYTHON_TAG=${PYTHON_TAG}" | tee -a "${GITHUB_ENV}" - if [[ "${{ runner.os }}" == 'Linux' ]]; then - sudo sysctl -w kernel.core_pattern="core.${PYTHON_TAG}.%P" - sudo sysctl -w kernel.core_uses_pid=0 - sudo sysctl -w fs.suid_dumpable=1 - sysctl kernel.core_pattern kernel.core_uses_pid fs.suid_dumpable - elif [[ "${{ runner.os }}" == 'macOS' ]]; then - sudo sysctl -w kern.corefile="core.${PYTHON_TAG}.%P" - sudo sysctl -w kern.coredump=1 - sudo sysctl -w kern.sugid_coredump=1 - sysctl kern.corefile kern.coredump kern.sugid_coredump - elif [[ "${{ runner.os }}" == 'Windows' ]]; then - echo "CMAKE_BUILD_TYPE=Release" | tee -a "${GITHUB_ENV}" - fi + - name: Enable core dump generation (Linux) + if: runner.os == 'Linux' + run: | + sudo sysctl -w kernel.core_pattern="core.${PYTHON_TAG}.%P" + sudo sysctl -w kernel.core_uses_pid=0 + sudo sysctl -w fs.suid_dumpable=1 + sysctl kernel.core_pattern kernel.core_uses_pid fs.suid_dumpable + + - name: Enable core dump generation (macOS) + if: runner.os == 'macOS' + run: | + sudo sysctl -w kern.corefile="core.${PYTHON_TAG}.%P" + sudo sysctl -w kern.coredump=1 + sudo sysctl -w kern.sugid_coredump=1 + sysctl kern.corefile kern.coredump kern.sugid_coredump + + - name: Enable core dump generation (Windows) + if: runner.os == 'Windows' + run: | + $pwd = Get-Location + $Env:_NT_SOURCE_PATH = "$pwd" + $Env:_NT_SYMBOL_PATH = "cache*$pwd\.symcache;$Env:_NT_SOURCE_PATH;srv*https://msdl.microsoft.com/download/symbols" + Get-ChildItem -Path "C:\Program Files (x86)\Windows Kits" -Directory | Sort-Object -Property Name + $WindowsKitsDir = "C:\Program Files (x86)\Windows Kits\10" + $DebuggersDir = "$WindowsKitsDir\Debuggers\x64" + Get-ChildItem -Path $DebuggersDir + $Env:PATH = "$DebuggersDir;$Env:PATH" + $PYTEST = 'cdb -gG -o -c ".dump /ma /u core.dmp; !py; g; q" ${{ env.PYTHON }} -X dev -m pytest' + Write-Output "_NT_SOURCE_PATH=$Env:_NT_SOURCE_PATH" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append + Write-Output "_NT_SYMBOL_PATH=$Env:_NT_SYMBOL_PATH" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append + Write-Output "PATH=$Env:PATH" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append + Write-Output "PYTEST=$PYTEST" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append + + cdb -version - name: Install test dependencies shell: bash @@ -138,6 +162,9 @@ jobs: "--junit-xml=junit-${{ env.PYTHON_TAG }}-${{ runner.os }}.xml" ) make test PYTESTOPTS="${PYTESTOPTS[*]}" + if [[ -n "$(find . -iname "core.*.[1-9]*" -o -iname "core_*.dmp")" ]]; then + exit 1 + fi - name: List generated files if: ${{ !cancelled() }} @@ -153,18 +180,36 @@ jobs: if: ${{ !cancelled() }} shell: bash run: | - if [[ -n "$(find . -iname "core.*.[1-9]*")" ]]; then + if [[ -n "$(find . -iname "core.*.[1-9]*" -o -iname "core_*.dmp")" ]]; then + echo "Found core dumps:" + ls -alh $(find . -iname "core.*.[1-9]*" -o -iname "core_*.dmp") + BACKTRACE_COMMAND="" if [[ "${{ runner.os }}" == 'Linux' ]]; then + echo "::group::Install GDB" ( export DEBIAN_FRONTEND=noninteractive sudo apt-get update -qq && sudo apt-get install -yqq gdb ) - echo "Found core dumps:" - ls -alh $(find . -iname "core.*.[1-9]*") + echo "::endgroup::" + BACKTRACE_COMMAND="gdb --exec ${{ env.PYTHON }} --core '{}' -ex 'bt' -ex 'q'" + elif [[ "${{ runner.os }}" == 'macOS' ]]; then + echo "::group::Install LLDB" + brew install llvm --quiet + echo "::endgroup::" + BACKTRACE_COMMAND="lldb --file ${{ env.PYTHON }} --core '{}' -o 'bt' -o 'q'" + elif [[ "${{ runner.os }}" == 'Windows' ]]; then + BACKTRACE_COMMAND="cdb -z '{}' -c \"!py; !analyze -vv; .ecxr; kP; q\"" + fi + if [[ -n "${BACKTRACE_COMMAND}" ]]; then echo "Collecting backtraces:" find . -iname "core.*.[1-9]*" -exec bash -xc " echo '::group::backtrace from: {}'; - gdb ${{ env.PYTHON }} '{}' -ex 'backtrace' -ex 'quit'; + ${BACKTRACE_COMMAND}; + echo '::endgroup::'; + " ';' + find . -iname "core_*.dmp" -exec bash -xc " + echo '::group::backtrace from: {}'; + ${BACKTRACE_COMMAND}; echo '::endgroup::'; " ';' fi @@ -189,7 +234,9 @@ jobs: uses: actions/upload-artifact@v4 with: name: coredump-${{ env.PYTHON_TAG }}-${{ runner.os }} - path: tests/core.* + path: | + tests/core.* + tests/core_*.dmp if-no-files-found: ignore upload-coverage: diff --git a/.gitignore b/.gitignore index 814d1327..9f4457ba 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,8 @@ __pycache__/ # Core dump files core.[1-9]* core.*.[1-9]* +core.dmp +core_*.dmp # Distribution / packaging .Python diff --git a/Makefile b/Makefile index 19f93f8c..cfeae541 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,7 @@ GOPATH ?= $(HOME)/go GOBIN ?= $(GOPATH)/bin PATH := $(PATH):$(GOBIN) PYTHON ?= $(shell command -v python3 || command -v python) +PYTEST ?= $(PYTHON) -X dev -m pytest PYTESTOPTS ?= CMAKE_CXX_STANDARD ?= 20 OPTREE_CXX_WERROR ?= ON @@ -139,10 +140,10 @@ addlicense-install: go-install .PHONY: pytest test pytest test: pytest-install - $(PYTHON) -m pytest --version + $(PYTEST) --version cd tests && $(PYTHON) -X dev -W 'always' -W 'error' -c 'import $(PROJECT_PATH)' && \ $(PYTHON) -X dev -W 'always' -W 'error' -c 'import $(PROJECT_PATH)._C; print(f"GLIBCXX_USE_CXX11_ABI={$(PROJECT_PATH)._C.GLIBCXX_USE_CXX11_ABI}")' && \ - $(PYTHON) -X dev -m pytest --verbose --color=yes --durations=10 --showlocals \ + $(PYTEST) --verbose --color=yes --durations=10 --showlocals \ --cov="$(PROJECT_PATH)" --cov-config=.coveragerc --cov-report=xml --cov-report=term-missing \ $(PYTESTOPTS) .