blob: 8a5f1dc84c94369a16d403d237bd73fe9cf6b8ab [file] [log] [blame]
#!/usr/bin/env bash
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
REAPER_MODE=off # off, report, kill
declare -i REAPER_TOTAL_COUNT=0
REAPER_DOCKER_ONLY=true
REAPER_ZOMBIE_MODULES=()
REAPER_ZOMBIE_LOGS=()
declare -a REAPER_NAMES
## @description Add a regex to the reaper's checklist
## @description NOTE: Users WILL override anything added before
## @description argument parsing!
## @stability evolving
## @audience public
## @replaceable no
function reaper_add_name
{
yetus_add_array_element REAPER_NAMES "$1"
}
## @description Reaper-specific usage
## @stability stable
## @audience private
## @replaceable no
function reaper_usage
{
yetus_add_option "--reapermode={off,report,kill}" "Set unit test reaper mode (default: '${REAPER_MODE}')"
yetus_add_option "--reaperdockeronly=<bool>" "Only run the reaper in --docker (default: ${REAPER_DOCKER_ONLY})"
yetus_add_option "--reapernames=<list>" "List of regexs to search (default build tool dependent)"
}
## @description Reaper-specific argument parsing
## @stability stable
## @audience private
## @replaceable no
## @params arguments
function reaper_parse_args
{
declare i
for i in "$@"; do
case ${i} in
--reapermode=*)
REAPER_MODE=${i#*=}
;;
--reaperdockeronly=*)
REAPER_DOCKER_ONLY=${i#*=}
;;
--reapernames=*)
yetus_comma_to_array REAPER_NAMES "${i#*=}"
;;
esac
done
# Running the reaper outside of Dockermode is very dangerous
if [[ "${REAPER_DOCKER_ONLY}" = "true" && ${DOCKERMODE} = "false" ]]; then
REAPER_MODE="off"
return
fi
# make sure REAPER_MODE is something valid and turn us on
# as necessary
if [[ "${REAPER_MODE}" = "report" || "${REAPER_MODE}" = "kill" ]]; then
add_test_format reaper
yetus_add_entry EXEC_MODES Reaper
else
REAPER_MODE="off"
fi
}
## @description Initialize the reaper
## @stability stable
## @audience private
## @replaceable yes
## @params arguments
function reaper_initialize
{
determine_user
}
## @description Reaper coprocessor function that
## @description runs outside the law
## @stability evolving
## @audience private
## @replaceable yes
function reaper_coproc_func
{
declare line
declare i
declare module
declare filefrag
declare cmd
declare args
declare pid
declare -a pidlist
declare -i count
echo "Reaper watching for: ${REAPER_NAMES[*]}" >> "${PATCH_DIR}/reaper.txt"
while true; do
read -r cmd
case ${cmd} in
reap)
read -r module
read -r logfile
while read -r line; do
((count=count+1))
for i in "${REAPER_NAMES[@]}"; do
echo "${line}" | ${GREP} -E "${i}" >> "${PATCH_DIR}/${logfile}"
done
done < <(ps -u "${USER_ID}" -o pid= -o args=)
pidlist=()
count=0
while read -r line; do
((count=count+1))
pid=$(echo "${line}" | cut -f1 -d' ')
args=$(echo "${line}" | cut -f2- -d' ')
if [[ "${REAPER_MODE}" = "kill" ]]; then
pidlist+=("${pid}")
echo "Killing ${pid} ${args}" >> "${PATCH_DIR}/reaper.txt" 2>&1
fi
done < <(cat "${PATCH_DIR}/${logfile}")
# tell our parent how many
# doing this now means killing in the background
echo ${count}
if [[ ${count} -eq 0 ]]; then
rm "${PATCH_DIR}/${logfile}"
fi
for i in "${pidlist[@]}"; do
if [[ "${REAPER_MODE}" = "kill" ]]; then
pid_kill "${i}" >> "${PATCH_DIR}/reaper.txt" 2>&1
fi
done
;;
exit)
exit 0
;;
esac
done
}
## @description Run the reaper
## @stability evolving
## @audience private
## @replaceable yes
## @params module
## @params testlog
## @params testfrag
function reaper_post_exec
{
declare module=$1
declare filefrag=$2
declare count
declare myfile="${filefrag}-reaper.txt"
declare killmsg=""
case "${REAPER_MODE}" in
off)
return 0
;;
kill)
killmsg=" and killed"
;;
esac
yetus_debug "Checking for unreaped processes:"
# give some time for things to die naturally
sleep 2
#shellcheck disable=SC2154,SC2086
printf "reap\n%s\n%s\n" "${module}" "${myfile}" >&${reaper_coproc[1]}
#shellcheck disable=SC2154,SC2086
read -r count <&${reaper_coproc[0]}
if [[ ${count} -gt 0 ]]; then
((REAPER_TOTAL_COUNT=REAPER_TOTAL_COUNT+count))
printf "\nFound%s %s left over processes\n\n" "${killmsg}" "${count}"
REAPER_ZOMBIE_MODULES+=("${module}:${count}")
REAPER_ZOMBIE_LOGS+=("@@BASE@@/${myfile}")
return 1
fi
return 0
}
## @description Reaper output to the user
## @stability evolving
## @audience private
## @replaceable yes
## @params jdkname
function reaper_finalize_results
{
declare jdk=$1
declare fn
if [[ "${REAPER_MODE}" = "off" ]]; then
return 0
fi
if [[ ${#REAPER_ZOMBIE_MODULES[@]} -gt 0 ]] ; then
populate_test_table "${jdk}Unreaped Processes" "${REAPER_ZOMBIE_MODULES[@]}"
for fn in "${REAPER_ZOMBIE_LOGS[@]}"; do
add_footer_table "Unreaped Processes Log" "${fn}"
done
REAPER_ZOMBIE_MODULES=()
REAPER_ZOMBIE_LOGS=()
fi
}
## @description Reaper output to the user
## @stability evolving
## @audience private
## @replaceable yes
## @params jdkname
function reaper_total_count
{
if [[ "${REAPER_MODE}" = "off" ]]; then
return 0
fi
if [[ ${REAPER_TOTAL_COUNT} -gt 0 ]]; then
add_vote_table -0 reaper "Unreaped process count: ${REAPER_TOTAL_COUNT}"
fi
}