[postgis-tickets] r16895 - [travis] re-enable logbt on dockerized builds
Darafei
komzpa at gmail.com
Sun Oct 14 04:06:22 PDT 2018
Author: komzpa
Date: 2018-10-14 16:06:22 -0700 (Sun, 14 Oct 2018)
New Revision: 16895
Added:
trunk/ci/travis/logbt
Modified:
trunk/.travis.yml
trunk/ci/travis/run_tests.sh
Log:
[travis] re-enable logbt on dockerized builds
Modified: trunk/.travis.yml
===================================================================
--- trunk/.travis.yml 2018-10-14 13:38:16 UTC (rev 16894)
+++ trunk/.travis.yml 2018-10-14 23:06:22 UTC (rev 16895)
@@ -1,7 +1,7 @@
services:
- docker
-sudo: false
+sudo: true
language: c
env:
@@ -17,6 +17,7 @@
- env: tag=latest
script:
+ - echo "/tmp/logbt-coredumps/core.%p.%E" | sudo tee /proc/sys/kernel/core_pattern
- echo "FROM postgis/postgis-build-env:${tag}" > Dockerfile
- echo "ADD --chown=postgres:postgres . /src/postgis" >> Dockerfile
- echo "CMD bash ci/travis/run_tests.sh && bash .github/codecov.bash" >> Dockerfile
Added: trunk/ci/travis/logbt
===================================================================
--- trunk/ci/travis/logbt (rev 0)
+++ trunk/ci/travis/logbt 2018-10-14 23:06:22 UTC (rev 16895)
@@ -0,0 +1,429 @@
+#!/usr/bin/env bash
+
+set -eu
+set -o pipefail
+# This setting enables the desired behavior in the "for corefile in ${core_directory}/${SEARCH_PATTERN_NON_TRACKED}; do" loop
+# more details at https://gist.github.com/springmeyer/6dd234ff89ba306a73608a6f45cb5506
+shopt -s nullglob
+
+PLATFORM_UNAME=$(uname -s)
+REQUIRED_FILENAME="core"
+LOGBT_VERSION="v2.0.3"
+BASE_CORE_DIRECTORY=/tmp/logbt-coredumps
+
+if [[ ${PLATFORM_UNAME} == "Linux" ]]; then
+ REQUIRED_PATTERN="${REQUIRED_FILENAME}.%p.%E"
+ DEBUGGER="gdb"
+elif [[ ${PLATFORM_UNAME} == "Darwin" ]]; then
+ # Recommend running with the following setting to only show crashes
+ # in the notification center
+ # defaults write com.apple.CrashReporter UseUNC 1
+ REQUIRED_PATTERN="${REQUIRED_FILENAME}.%P"
+ DEBUGGER="lldb"
+else
+ error "Unsupported platform: ${PLATFORM_UNAME}"
+fi
+
+function error() {
+ >&2 echo "[logbt] $@"
+ exit 1
+}
+
+function process_core() {
+ local program=${1}
+ local corefile=${2}
+ local debugger=${3}
+ if [[ ${debugger} =~ "lldb" ]]; then
+ lldb --core ${corefile} --batch -o "thread backtrace all" -o "quit"
+ else
+ gdb ${program} --core ${corefile} -ex "set pagination 0" -ex "thread apply all bt full" --batch
+ fi
+ # note: on OS X the -f avoids a hang on prompt "remove write-protected regular file?"
+ rm -f ${corefile}
+}
+
+function find_core_by_pid() {
+ local program=${1}
+ local core_directory=${2}
+ local debugger=${3}
+ local child_pid=${4}
+ if [[ ${PLATFORM_UNAME} == "Darwin" ]]; then
+ local single_corefile="${core_directory}/${REQUIRED_FILENAME}.${child_pid}"
+ if [ -e ${single_corefile} ]; then
+ echo "[logbt] Found corefile at ${single_corefile}"
+ process_core ${program} ${single_corefile} ${debugger}
+ fi
+ else
+ local SEARCH_PATTERN_BY_PID="${REQUIRED_FILENAME}.${child_pid}.*"
+ # note: this for loop depends on the `shopt -s nullglob` above
+ for corefile in ${core_directory}/${SEARCH_PATTERN_BY_PID}; do
+ echo "[logbt] Found corefile at ${corefile}"
+ # extract program name from corefile
+ filename=$(basename "${corefile}")
+ binary_program=/$(echo ${filename##*.\!} | tr "!" "/")
+ process_core ${binary_program} ${corefile} ${debugger}
+ done
+ fi
+}
+
+function find_remaining_cores() {
+ local program=${1}
+ local core_directory=${2}
+ local debugger=${3}
+ local SEARCH_PATTERN_NON_TRACKED="${REQUIRED_FILENAME}.*"
+ local hit=false
+ for corefile in ${core_directory}/${SEARCH_PATTERN_NON_TRACKED}; do
+ echo "[logbt] Found corefile (non-tracked) at ${corefile}"
+ hit=true
+ done
+ if [[ ${hit} == true ]]; then
+ echo "[logbt] Processing cores..."
+ fi
+ for corefile in ${core_directory}/${SEARCH_PATTERN_NON_TRACKED}; do
+ # below two lines are linux specific, but harmless to run on osx
+ filename=$(basename "${corefile}")
+ binary_program=/$(echo ${filename##*.\!} | tr "!" "/")
+ process_core ${binary_program} ${corefile} ${debugger}
+ done
+}
+
+function snapshot {
+ local program=${1}
+ local debugger=${2}
+ local child_pid=${3}
+ echo "[logbt] snapshotting ${program} (${child_pid})"
+ if [[ ${debugger} =~ "lldb" ]]; then
+ lldb -p ${child_pid} --batch -o "thread backtrace all" -o "quit"
+ else
+ gdb --pid ${child_pid} -ex "set pagination 0" -ex "thread apply all bt full" --batch
+ fi
+}
+
+function backtrace {
+ local program=${1}
+ local core_directory=${2}
+ local debugger=${3}
+ local child_pid=${4}
+ local child_return=${5}
+ find_core_by_pid ${program} ${core_directory} ${debugger} ${child_pid}
+ find_remaining_cores ${program} ${core_directory} ${debugger}
+}
+
+function warn_on_existing_cores() {
+ local core_directory=${1}
+ local SEARCH_PATTERN_NON_TRACKED="${REQUIRED_FILENAME}.*"
+ for corefile in ${core_directory}/${SEARCH_PATTERN_NON_TRACKED}; do
+ echo "[logbt] WARNING: Found corefile (existing) at ${corefile}"
+ done
+}
+
+function error_on_existing_cores() {
+ local core_directory=${1}
+ local SEARCH_PATTERN_NON_TRACKED="${REQUIRED_FILENAME}.*"
+ for corefile in ${core_directory}/${SEARCH_PATTERN_NON_TRACKED}; do
+ error "Error: Found corefile (unexpected) at ${corefile}"
+ done
+}
+
+function ensure_directory_is_writeable() {
+ # ensure we can write to the directory, otherwise
+ # core files might not be able to be written
+ WRITE_RETURN=0
+ touch ${core_directory}/test.txt || WRITE_RETURN=$?
+ if [[ ${WRITE_RETURN} != 0 ]]; then
+ error "Permissions problem: unable to write to ${core_directory} (exited with ${WRITE_RETURN})"
+ else
+ # cleanup from test
+ rm ${core_directory}/test.txt
+ fi
+}
+
+function get_target_core_pattern() {
+ echo ${BASE_CORE_DIRECTORY}/${REQUIRED_PATTERN}
+}
+
+function get_core_pattern() {
+ if [[ ${PLATFORM_UNAME} == "Linux" ]]; then
+ local core_pattern=$(cat /proc/sys/kernel/core_pattern)
+ elif [[ ${PLATFORM_UNAME} == "Darwin" ]]; then
+ # Recommend running with the following setting to only show crashes
+ # in the notification center
+ # defaults write com.apple.CrashReporter UseUNC 1
+ local core_pattern=$(sysctl -n kern.corefile)
+ fi
+ echo ${core_pattern}
+}
+
+function validate_core_pattern() {
+ local core_pattern=${1}
+ if [[ ! ${core_pattern} =~ ${REQUIRED_PATTERN} ]]; then
+ error "unexpected core_pattern: ${core_pattern}"
+ fi
+}
+
+function generic_signal_handler() {
+ local code=$?
+ local program=${1}
+ local child_pid=${2}
+ local sig=${3}
+ # Bug note: On darwin ${code} will be incorrectly 0 after snapshot here
+ # so we ignore ${code} and instead get it from the signal
+ code=$(($(kill -l ${sig})+128))
+ echo "[logbt] received signal:${code} (${sig})"
+ echo "[logbt] sending SIGTERM to ${program} (${child_pid})"
+ # sleep here to help the stdout show in the right
+ # order (accounts for an intermittant case where the above lines print after
+ # the child outputs stdout during shutdown)
+ sleep 1
+ KILL_RETURN=0
+ kill -TERM ${child_pid} || KILL_RETURN=$?
+ if [[ ${KILL_RETURN} != 0 ]]; then
+ echo "[logbt] could not terminate child process (kill returned ${KILL_RETURN})"
+ else
+ CHILD_EXIT=0
+ wait ${child_pid} || CHILD_EXIT=$?
+ if [[ ${CHILD_EXIT} != 143 ]]; then
+ error "child process exited abnormally: ${CHILD_EXIT}"
+ fi
+ fi
+ echo "[logbt] exiting with ${code}"
+ exit ${code}
+}
+
+
+: '
+NOTE: SIGINT (aka ctrl-c) is special. First of all SIGINT is sent to the whole process group
+automatically in bash (http://stackoverflow.com/a/6804155). This means we do not need to reap
+the child process manually. And if logbt is a "foreground" process then SIGINT is ignored if
+sent directly with ./bin/logbt <args> & kill -INT $! (http://stackoverflow.com/a/14697034).
+So the only way to send SIGINT is with ctrl-c or via another terminal that has a different
+process group.
+'
+
+function sigint_handler() {
+ local code=$?
+ local program=${1}
+ local child_pid=${2}
+ local sig=${3}
+ echo "[logbt] received signal:${code} (${sig})"
+ CHILD_EXIT=0
+ wait ${child_pid} || CHILD_EXIT=$?
+ if [[ ${CHILD_EXIT} != 130 ]]; then
+ echo "[logbt] child process exited with:${CHILD_EXIT}"
+ fi
+ exit ${code}
+}
+
+function launch_and_wait() {
+
+ local program=${1}
+ local core_pattern=$(get_core_pattern)
+ validate_core_pattern ${core_pattern}
+
+ local core_directory=$(dirname ${core_pattern})
+ echo "[logbt] using corefile location: ${core_directory}"
+ echo "[logbt] using core_pattern: $(basename ${core_pattern})"
+
+ # ensure we have a debugger installed
+ if ! which ${DEBUGGER} > /dev/null; then
+ error "Could not find required command '${DEBUGGER}'"
+ fi
+
+ if [[ ! -d ${core_directory} ]]; then
+ echo "[logbt] creating directory for core files at '${core_directory}'"
+ mkdir -p -m a+w ${core_directory}
+ fi
+
+ ensure_directory_is_writeable ${core_directory}
+
+ warn_on_existing_cores ${core_directory}
+
+ # Enable corefile generation
+ ulimit -c unlimited
+
+ # Run the child process in a background process
+ # in order to get the PID
+ if [[ ${LD_PRELOAD:-} ]] && [[ ${PLATFORM_UNAME} == 'Darwin' ]]; then
+ # on os x DYLD_INSERT_LIBRARIES is blocked from being inherited
+ # so we accept LD_PRELOAD and foreward along
+ DYLD_INSERT_LIBRARIES=${LD_PRELOAD} LOGBT_PID=$$ "$@" & CHILD_PID=$!
+ else
+ LOGBT_PID=$$ "$@" & CHILD_PID=$!
+ fi
+
+ # Hook up function to run when logbt received signal
+ trap "snapshot ${program} ${DEBUGGER} ${CHILD_PID}" USR1
+ trap "generic_signal_handler ${program} ${CHILD_PID} TERM" TERM
+ trap "generic_signal_handler ${program} ${CHILD_PID} HUP" HUP
+ trap "sigint_handler ${program} ${CHILD_PID} INT" INT
+
+ # Wait for child and attempt to generate a backtrace if child exits in non-zero way
+ wait_for_child ${program} ${core_directory} ${DEBUGGER} ${CHILD_PID}
+}
+
+function wait_for_child() {
+ local program=${1}
+ local core_directory=${2}
+ local debugger=${3}
+ local child_pid=${4}
+ CHILD_RETURN=0
+ wait ${child_pid} || CHILD_RETURN=$?
+ # Bug note: on linux USR1 will trigger an exit which makes it looks like the child
+ # has returned when it has not. So the below code ensures we stay alive and watching
+ # the child if USR1 is hit
+ if [[ $(uname -s) == 'Linux' ]] && [[ $(kill -l ${CHILD_RETURN}) == USR1 ]]; then
+ wait_for_child ${program} ${core_directory} ${debugger} ${child_pid}
+ fi
+ if [[ ${CHILD_RETURN} == 127 ]]; then
+ # command not found : http://www.tldp.org/LDP/abs/html/exitcodes.html
+ echo "[logbt] command not found: ${program}"
+ elif [[ ${CHILD_RETURN} != 0 ]]; then
+ local exit_msg="saw '${program}' exit with code:${CHILD_RETURN}"
+ exit_msg="${exit_msg} ($(kill -l ${CHILD_RETURN}))"
+ echo "[logbt] ${exit_msg}"
+ backtrace ${program} ${core_directory} ${DEBUGGER} ${child_pid} ${CHILD_RETURN}
+ fi
+ # exit logbt with the same code as the child
+ exit ${CHILD_RETURN}
+}
+
+function setup_logbt() {
+ local settable_core_pattern=$(get_target_core_pattern)
+ if [[ ${PLATFORM_UNAME} == "Linux" ]]; then
+ echo "[logbt] setting $(cat /proc/sys/kernel/core_pattern) -> ${settable_core_pattern}"
+ # write new value to /proc/sys/kernel/core_pattern
+ echo "${settable_core_pattern}" > /proc/sys/kernel/core_pattern
+ elif [[ ${PLATFORM_UNAME} == "Darwin" ]]; then
+ echo "[logbt] setting $(sysctl -n kern.corefile) -> ${settable_core_pattern}"
+ sysctl kern.corefile=${settable_core_pattern}
+ fi
+
+ local core_pattern=$(get_core_pattern)
+ validate_core_pattern ${core_pattern}
+ local core_directory=$(dirname ${core_pattern})
+
+ if [[ ! -d ${core_directory} ]]; then
+ echo "[logbt] creating directory for core files at '${core_directory}'"
+ mkdir -p -m a+w ${core_directory}
+ fi
+
+ error_on_existing_cores ${core_directory}
+
+}
+
+function test_logbt() {
+ ulimit -c unlimited
+ # First we create a program that crashes itself
+ # We use bash to avoid needing an external dep on some runtime
+
+ # Due to https://github.com/mapbox/logbt/issues/29 we need to copy the bash
+ # exe on OS X to a new location since coredumps are disabled for /bin/bash for
+ # reasons I don't understand
+ if [[ ${PLATFORM_UNAME} == "Darwin" ]]; then
+ # first touch file to create it with writable permissions for this user
+ # such that we can cleanup after
+ # then copy the system bash there
+ cp --no-preserve=all $(which bash) /tmp/tmp-bash
+ chmod +x /tmp/tmp-bash
+ echo '#!/tmp/tmp-bash' > /tmp/crasher.sh
+ else
+ # on linux the default bash is okay
+ echo '#!/usr/bin/env bash' > /tmp/crasher.sh
+ fi
+ echo 'kill -SIGSEGV $$' >> /tmp/crasher.sh
+ chmod +x /tmp/crasher.sh
+ # run it in logbt
+ RETURN=0
+ ${BASH_SOURCE} -- /tmp/crasher.sh >/tmp/logbt-stdout 2>/tmp/logbt-stderr || RETURN=$?
+ local err_message
+ if [[ ${RETURN} != 139 ]] || [[ ! $(cat /tmp/logbt-stdout) =~ "Found corefile at" ]]; then
+ cat /tmp/logbt-stdout
+ cat /tmp/logbt-stderr
+ err_message="Expected return code of 139 and a corefile to be generated"
+ fi
+ if [[ ! $(cat /tmp/logbt-stdout) =~ "Found corefile at" ]]; then
+ cat /tmp/logbt-stdout
+ cat /tmp/logbt-stderr
+ err_message="Expected a corefile to be generated"
+ fi
+ # cleanup
+ rm -f /tmp/logbt-stderr
+ rm -f /tmp/logbt-stdout
+ rm -f /tmp/crasher.sh
+ rm -f /tmp/tmp-bash
+ if [[ ${err_message:-} ]]; then
+ error ${err_message}
+ else
+ echo "[logbt] test success (coredumps are working with core pattern: '$(get_core_pattern)')"
+ fi
+}
+
+function usage() {
+ >&2 echo "Usage for logbt:"
+ >&2 echo ""
+ >&2 echo "Setup logbt (requires root privileges):"
+ >&2 echo ""
+ >&2 echo "$ sudo logbt --setup"
+ >&2 echo ""
+ >&2 echo "Test logbt is setup correctly"
+ >&2 echo ""
+ >&2 echo "$ logbt --test"
+ >&2 echo ""
+ >&2 echo "Launch a program with logbt:"
+ >&2 echo ""
+ >&2 echo "$ logbt -- ./program"
+ >&2 echo ""
+ >&2 echo "Other commands are:"
+ >&2 echo ""
+ >&2 echo " --current-pattern"
+ >&2 echo " --target-pattern"
+ >&2 echo " --version"
+ exit 1
+}
+
+function get_version() {
+ echo ${LOGBT_VERSION}
+}
+
+if [[ ! ${1:-} ]]; then
+ usage
+fi
+
+# https://stackoverflow.com/questions/192249/how-do-i-parse-command-line-arguments-in-bash
+for i in "$@"
+do
+case $i in
+ --)
+ if [[ ! ${2:-} ]]; then
+ usage
+ fi
+ shift
+ launch_and_wait "$@"
+ ;;
+ --setup)
+ setup_logbt
+ shift
+ ;;
+ --test)
+ test_logbt
+ shift
+ ;;
+ --current-pattern)
+ get_core_pattern
+ ;;
+ --target-pattern)
+ get_target_core_pattern
+ ;;
+ -v | --version)
+ get_version
+ shift
+ ;;
+ -h | --help)
+ usage
+ shift
+ ;;
+ *)
+ usage
+ ;;
+esac
+done
Modified: trunk/ci/travis/run_tests.sh
===================================================================
--- trunk/ci/travis/run_tests.sh 2018-10-14 13:38:16 UTC (rev 16894)
+++ trunk/ci/travis/run_tests.sh 2018-10-14 23:06:22 UTC (rev 16895)
@@ -10,13 +10,11 @@
CFLAGS_COV="-g -O0 --coverage"
LDFLAGS_COV="--coverage"
-/usr/local/pgsql/bin/pg_ctl -l /tmp/logfile start
+/usr/local/pgsql/bin/pg_ctl -c -l /tmp/logfile start
./autogen.sh
-CFLAGS="${CFLAGS_STD}" LDFLAGS="${LDFLAGS_COV}" ./configure
-make -j
-RUNTESTFLAGS="-v" make check
+./configure CFLAGS="${CFLAGS_STD}" LDFLAGS="${LDFLAGS_STD}"
+bash ./ci/travis/logbt -- make -j check RUNTESTFLAGS=--verbose
-CFLAGS="${CFLAGS_COV}" LDFLAGS="${LDFLAGS_COV}" ./configure
-make -j
-make check
+./configure CFLAGS="${CFLAGS_COV}" LDFLAGS="${LDFLAGS_COV}"
+make -j check
More information about the postgis-tickets
mailing list