Skip to content

Sanitizer test

Sanitizer test #182

# Run regression tests under memory sanitizer
name: Sanitizer test
"on":
schedule:
# run daily 0:00 on main branch
- cron: '0 0 * * *'
push:
branches:
- main
- prerelease_test
env:
name: "Sanitizer"
PG_SRC_DIR: "pgbuild"
PG_INSTALL_DIR: "postgresql"
MAKE_JOBS: 6
# llvm-symbolizer crashes so we don't use clang until we resolve that
extra_packages: "clang-10 llvm-10 llvm-10-dev llvm-10-tools"
llvm_config: "llvm-config-10"
CLANG: "clang-10"
# gcc-10 is the minimum version that can parse the suppression files barely adequately
CC: "gcc-10"
CXX: "g++-10"
# gcc CFLAGS, disable inlining for function name pattern matching to work for suppressions
CFLAGS: "-g -fsanitize=address,undefined -fno-omit-frame-pointer -O1 -fno-inline"
CXXFLAGS: "-g -fsanitize=address,undefined -fno-omit-frame-pointer -O1 -fno-inline"
# clang CFLAGS
#CFLAGS: "-g -fsanitize=address,undefined -fno-omit-frame-pointer -O1 -fno-inline-functions"
#CXXFLAGS: "-g -fsanitize=address,undefined -fno-omit-frame-pointer -O1 -fno-inline-functions"
# We do not link libasan dynamically to avoid problems with libdl and our libraries.
# clang does this by default, but we need to explicitly state that for gcc.
# static gcc LDFLAGS
LDFLAGS: "-fsanitize=address,undefined -static-libasan -static-liblsan -static-libubsan"
# static sanitizer clang LDFLAGS or dynamic sanitizer gcc LDFLAGS
#LDFLAGS: "-fsanitize=address,undefined"
ASAN_OPTIONS: suppressions=${{ github.workspace }}/scripts/suppressions/suppr_asan.txt
detect_odr_violation=0 log_path=${{ github.workspace }}/sanitizer/sanitizer
log_exe_name=true print_suppressions=false exitcode=27
external_symbolizer_path=/usr/lib/llvm-10/bin/llvm-symbolizer
LSAN_OPTIONS: suppressions=${{ github.workspace }}/scripts/suppressions/suppr_leak.txt
print_suppressions=0 log_path=${{ github.workspace }}/sanitizer/sanitizer
log_exe_name=true print_suppressions=false exitcode=27
external_symbolizer_path=/usr/lib/llvm-10/bin/llvm-symbolizer
UBSAN_OPTIONS: suppressions=${{ github.workspace }}/scripts/suppressions/suppr_ub.txt
print_stacktrace=1 halt_on_error=1 log_path=${{ github.workspace }}/sanitizer/sanitizer
log_exe_name=true print_suppressions=false exitcode=27
external_symbolizer_path=/usr/lib/llvm-10/bin/llvm-symbolizer
IGNORES: "bgw_db_scheduler bgw_db_scheduler_fixed"
SKIPS: "remote_txn"
jobs:
config:
runs-on: ubuntu-latest
outputs:
pg_latest: ${{ steps.setter.outputs.PG_LATEST }}
pg15_latest: ${{ steps.setter.outputs.PG15_LATEST }}
steps:
- name: Checkout source code
uses: actions/checkout@v3
- name: Read configuration
id: setter
run: python .github/gh_config_reader.py
sanitizer:
# Change the JOB_NAME variable below when changing the name.
# Don't use the env variable here because the env context is not accessible.
name: PG${{ matrix.pg }} Sanitizer ${{ matrix.os }}
runs-on: ${{ matrix.os }}
needs: config
strategy:
fail-fast: false
matrix:
# "os" has to be in the matrix due to a bug in "env": https://github.community/t/how-to-use-env-context/16975
os: ["ubuntu-20.04"]
pg: ${{ fromJson(needs.config.outputs.pg_latest) }}
include:
- pg: ${{ fromJson(needs.config.outputs.pg15_latest) }}
# partialize_finalize is ignored in PG15 due to #4937
ignores_version: partialize_finalize
skips_version: 002_replication_telemetry 003_connections_privs 004_multinode_rdwr_1pc
steps:
- name: Install Linux Dependencies
run: |
sudo apt-get update
sudo apt-get install flex bison lcov systemd-coredump gdb libipc-run-perl \
libtest-most-perl ${{ env.extra_packages }}
- name: Checkout TimescaleDB
uses: actions/checkout@v3
# We are going to rebuild Postgres daily, so that it doesn't suddenly break
# ages after the original problem.
- name: Get date for build caching
id: get-date
run: |
echo "date=$(date +"%d")" >> $GITHUB_OUTPUT
# Create a directory for sanitizer logs. This directory is referenced by
# ASAN_OPTIONS, LSAN_OPTIONS, and UBSAN_OPTIONS
- name: Create sanitizer log directory
run: |
mkdir ${{ github.workspace }}/sanitizer
# we cache the build directory instead of the install directory here
# because extension installation will write files to install directory
# leading to a tainted cache
- name: Cache PostgreSQL ${{ matrix.pg }}
id: cache-postgresql
uses: actions/cache@v3
with:
path: ~/${{ env.PG_SRC_DIR }}
key: "${{ matrix.os }}-${{ env.name }}-postgresql-${{ matrix.pg }}-${{ env.CC }}\
-${{ steps.get-date.outputs.date }}-${{ hashFiles('.github/**') }}"
- name: Build PostgreSQL ${{ matrix.pg }} if not in cache
if: steps.cache-postgresql.outputs.cache-hit != 'true'
run: |
wget -q -O postgresql.tar.bz2 \
https://ftp.postgresql.org/pub/source/v${{ matrix.pg }}/postgresql-${{ matrix.pg }}.tar.bz2
mkdir -p ~/$PG_SRC_DIR
tar --extract --file postgresql.tar.bz2 --directory ~/$PG_SRC_DIR --strip-components 1
cd ~/$PG_SRC_DIR
./configure --prefix=$HOME/$PG_INSTALL_DIR --enable-debug --enable-cassert \
--with-openssl --without-readline --without-zlib --without-libxml
make -j $MAKE_JOBS
make -j $MAKE_JOBS -C src/test/isolation
make -j $MAKE_JOBS -C contrib/postgres_fdw
- name: Upload config.log
if: always() && steps.cache-postgresql.outputs.cache-hit != 'true'
uses: actions/upload-artifact@v3
with:
name: config.log for PostgreSQL ${{ matrix.os }} ${{ matrix.name }} ${{ matrix.pg }}
path: ~/${{ env.PG_SRC_DIR }}/config.log
- name: Install PostgreSQL ${{ matrix.pg }}
run: |
make -C ~/$PG_SRC_DIR install
make -C ~/$PG_SRC_DIR/contrib/postgres_fdw install
- name: Build TimescaleDB
run: |
./bootstrap -DCMAKE_BUILD_TYPE=Debug -DPG_SOURCE_DIR=~/$PG_SRC_DIR \
-DPG_PATH=~/$PG_INSTALL_DIR -DCODECOVERAGE=OFF -DREQUIRE_ALL_TESTS=ON -DTEST_GROUP_SIZE=5
make -j $MAKE_JOBS -C build
make -C build install
- name: make installcheck
run: |
set -o pipefail
# IGNORE some test since they fail under ASAN. At least the remote_txn
# test seems to fail due to a PostgreSQL bug where AbortStartTime in
# postmaster.c is not atomic but read/written across signal handlers
# and ServerLoop.
make -k -C build installcheck SKIPS="${SKIPS} ${{matrix.skips_version}}" \
IGNORES="${IGNORES} ${{matrix.ignores_version}}" | tee installcheck.log
- name: Show regression diffs
if: always()
id: collectlogs
run: |
find . -name regression.diffs -exec cat {} + > regression.log
find . -name postmaster.log -exec cat {} + > postgres.log
if [[ "${{ runner.os }}" == "Linux" ]] ; then
# wait in case there are in-progress coredumps
sleep 10
if coredumpctl -q list >/dev/null; then echo "coredumps=true" >>$GITHUB_OUTPUT; fi
# print OOM killer information
sudo journalctl --system -q --facility=kern --grep "Killed process" || true
fi
if [[ -s regression.log ]]; then echo "regression_diff=true" >>$GITHUB_OUTPUT; fi
grep -e 'FAILED' -e 'failed (ignored)' installcheck.log || true
cat regression.log
- name: Save regression diffs
if: always() && steps.collectlogs.outputs.regression_diff == 'true'
uses: actions/upload-artifact@v3
with:
name: Regression diff ${{ matrix.os }} ${{ env.name }} ${{ matrix.pg }}
path: |
regression.log
installcheck.log
- name: Save PostgreSQL log
if: always()
uses: actions/upload-artifact@v3
with:
name: PostgreSQL log ${{ matrix.os }} ${{ env.name }} ${{ matrix.pg }}
path: postgres.log
- name: Stack trace
if: always() && steps.collectlogs.outputs.coredumps == 'true'
run: |
sudo coredumpctl gdb <<<"
set verbose on
set trace-commands on
show debug-file-directory
printf "'"'"query = '%s'\n\n"'"'", debug_query_string
frame function ExceptionalCondition
printf "'"'"condition = '%s'\n"'"'", conditionName
up 1
l
info args
info locals
bt full
" 2>&1 | tee stacktrace.log
./scripts/bundle_coredumps.sh
grep -C40 "was terminated by signal" postgres.log > postgres-failure.log ||:
- name: Coredumps
if: always() && steps.collectlogs.outputs.coredumps == 'true'
uses: actions/upload-artifact@v3
with:
name: Coredumps ${{ matrix.os }} ${{ env.name }} ${{ matrix.pg }}
path: coredumps
- name: sanitizer logs
if: always()
uses: actions/upload-artifact@v3
with:
name: sanitizer logs ${{ matrix.os }} ${{ env.name }} ${{ matrix.pg }}
path: ${{ github.workspace }}/sanitizer
- name: Upload test results to the database
if: always()
env:
# GitHub Actions allow you neither to use the env context for the job name,
# nor to access the job name from the step context, so we have to
# duplicate it to work around this nonsense.
JOB_NAME: PG${{ matrix.pg }} ${{ env.name }} ${{ matrix.os }}
CI_STATS_DB: ${{ secrets.CI_STATS_DB }}
GITHUB_EVENT_NAME: ${{ github.event_name }}
GITHUB_REF_NAME: ${{ github.ref_name }}
GITHUB_REPOSITORY: ${{ github.repository }}
GITHUB_RUN_ATTEMPT: ${{ github.run_attempt }}
GITHUB_RUN_ID: ${{ github.run_id }}
GITHUB_RUN_NUMBER: ${{ github.run_number }}
JOB_STATUS: ${{ job.status }}
run: |
if [[ "${{ github.event_name }}" == "pull_request" ]] ;
then
GITHUB_PR_NUMBER="${{ github.event.number }}"
else
GITHUB_PR_NUMBER=0
fi
export GITHUB_PR_NUMBER
scripts/upload_ci_stats.sh