1
0
Fork 0
mirror of git://git.code.sf.net/p/cdesktopenv/code synced 2025-03-09 15:50:02 +00:00
cde/src/cmd/ksh93/tests/leaks.sh

490 lines
13 KiB
Bash
Executable file

########################################################################
# #
# This software is part of the ast package #
# Copyright (c) 1982-2012 AT&T Intellectual Property #
# Copyright (c) 2020-2021 Contributors to ksh 93u+m #
# and is licensed under the #
# Eclipse Public License, Version 1.0 #
# by AT&T Intellectual Property #
# #
# A copy of the License is available at #
# http://www.eclipse.org/org/documents/epl-v10.html #
# (with md5 checksum b35adb5213ca9657e911e9befb180842) #
# #
# Information and Software Systems Research #
# AT&T Research #
# Florham Park NJ #
# #
# David Korn <dgk@research.att.com> #
# #
########################################################################
. "${SHTESTS_COMMON:-${0%/*}/_common}"
# Determine method for running tests.
# The 'vmstate' builtin can be used if ksh was compiled with vmalloc.
if builtin vmstate 2>/dev/null &&
n=$(vmstate --format='%(busy_size)u') &&
let "($n) == ($n) && n > 0" # non-zero number?
then N=512 # number of iterations for each test
unit=bytes
tolerance=$((4*N)) # tolerate 4 bytes per iteration to account for vmalloc artefacts
function getmem
{
vmstate --format='%(busy_size)u'
}
# On Linux, we can use /proc to get byte granularity for vsize (field 23).
elif [[ -f /proc/$$/stat && $(uname) == Linux ]]
then N=1024 # number of iterations for each test
unit=bytes
tolerance=$((4*N)) # tolerate 4 bytes per iteration to account for malloc artefacts
function getmem
{
cut -f 23 -d ' ' </proc/$$/stat
}
# On UnixWare, read the process virtual size with ps
elif [[ $(uname) == UnixWare ]] &&
n=$(ps -o vsz= -p "$$" 2>/dev/null) &&
let "($n) == ($n) && n > 0"
then N=16384
unit=KiB
tolerance=$((4*N/1024)) # tolerate 4 bytes per iteration to account for malloc artefacts
function getmem
{
ps -o vsz= -p "$$"
}
# Otherwise, make do with the nonstandard 'rss' (real resident size) keyword
# of the 'ps' command (the standard 'vsz', virtual size, is not usable).
elif n=$(ps -o rss= -p "$$" 2>/dev/null) &&
let "($n) == ($n) && n > 0"
then N=16384
unit=KiB
tolerance=$((8*N/1024)) # tolerate 8 bytes per iteration to account for malloc/ps artefacts
function getmem
{
ps -o rss= -p "$$"
}
else warning 'cannot find method to measure memory usage; skipping tests'
exit 0
fi
# test for variable reset leak #
function test_reset
{
integer i N=$1
for ((i = 0; i < N; i++))
do u=$i
done
}
# Initialise variables used below to avoid false leaks
before=0 after=0 i=0 u=0
# Check results.
# The function has 'err_exit' in the name so that shtests counts each call as at test.
function err_exit_if_leak
{
if ((after > before + tolerance))
then err\_exit "$1" "$2 (leaked approx $((after - before)) $unit after $N iterations)"
fi
}
alias err_exit_if_leak='err_exit_if_leak "$LINENO"'
# one round to get to steady state -- sensitive to -x
test_reset $N
test_reset $N
before=$(getmem)
test_reset $N
after=$(getmem)
err_exit_if_leak "variable value reset memory leak"
# buffer boundary tests
for exp in 65535 65536
do got=$($SHELL -c 'x=$(printf "%.*c" '$exp' x); print ${#x}' 2>&1)
[[ $got == $exp ]] || err_exit "large command substitution failed -- expected $exp, got $got"
done
data="(v=;sid=;di=;hi=;ti='1328244300';lv='o';id='172.3.161.178';var=(k='conn_num._total';u=;fr=;l='Number of Connections';n='22';t='number';))"
read -C stat <<< "$data"
for ((i=0; i < 8; i++)) # steady state first
do print -r -- "$data" | while read -u$n -C stat; do :; done {n}<&0-
done
before=$(getmem)
for ((i=0; i < N; i++))
do print -r -- "$data"
done | while read -u$n -C stat
do :
done {n}<&0-
after=$(getmem)
err_exit_if_leak "memory leak with read -C when deleting compound variable"
# extra 'read's to get to steady state
for ((i=0; i < 10; i++))
do read -C stat <<< "$data"
done
before=$(getmem)
for ((i=0; i < N; i++))
do read -C stat <<< "$data"
done
after=$(getmem)
err_exit_if_leak "memory leak with read -C when using <<<"
# ======
# Unsetting an associative array shouldn't cause a memory leak
# See https://www.mail-archive.com/ast-users@lists.research.att.com/msg01016.html
typeset -A stuff
before=$(getmem)
for (( i=0; i < N; i++ ))
do
unset stuff[xyz]
typeset -A stuff[xyz]
stuff[xyz][elem0]="data0"
stuff[xyz][elem1]="data1"
stuff[xyz][elem2]="data2"
stuff[xyz][elem3]="data3"
stuff[xyz][elem4]="data4"
done
unset stuff
after=$(getmem)
err_exit_if_leak 'unset of associative array causes memory leak'
# ======
# Memory leak when resetting PATH and clearing hash table
# ...steady memory state:
command -v ls >/dev/null # add something to hash table
PATH=/dev/null true # set/restore PATH & clear hash table
# ...test for leak:
before=$(getmem)
for ((i=0; i < N; i++))
do PATH=/dev/null true # set/restore PATH & clear hash table
command -v ls # do PATH search, add to hash table
done >/dev/null
after=$(getmem)
err_exit_if_leak 'memory leak on PATH reset before PATH search'
# ...test for another leak that only shows up when building with nmake:
before=$(getmem)
for ((i=0; i < N; i++))
do PATH=/dev/null true # set/restore PATH & clear hash table
done >/dev/null
after=$(getmem)
err_exit_if_leak 'memory leak on PATH reset'
# ======
# Defining a function in a virtual subshell
# https://github.com/ksh93/ksh/issues/114
unset -f foo
before=$(getmem)
for ((i=0; i < N; i++))
do (function foo { :; }; foo)
done
after=$(getmem)
err_exit_if_leak 'ksh function defined in virtual subshell'
typeset -f foo >/dev/null && err_exit 'ksh function leaks out of subshell'
unset -f foo
before=$(getmem)
for ((i=0; i < N; i++))
do (foo() { :; }; foo)
done
after=$(getmem)
err_exit_if_leak 'POSIX function defined in virtual subshell'
typeset -f foo >/dev/null && err_exit 'POSIX function leaks out of subshell'
# Unsetting a function in a virtual subshell
function foo { echo bar; }
before=$(getmem)
for ((i=0; i < N; i++))
do (unset -f foo)
done
after=$(getmem)
err_exit_if_leak 'ksh function unset in virtual subshell'
typeset -f foo >/dev/null || err_exit 'ksh function unset in subshell was unset in main shell'
foo() { echo bar; }
before=$(getmem)
for ((i=0; i < N; i++))
do (unset -f foo)
done
after=$(getmem)
err_exit_if_leak 'POSIX function unset in virtual subshell'
typeset -f foo >/dev/null || err_exit 'POSIX function unset in subshell was unset in main shell'
before=$(getmem)
for ((i=0; i < N; i++))
do (function foo { echo baz; }; unset -f foo)
done
after=$(getmem)
err_exit_if_leak 'ksh function defined and unset in virtual subshell'
before=$(getmem)
for ((i=0; i < N; i++))
do (foo() { echo baz; }; unset -f foo)
done
after=$(getmem)
err_exit_if_leak 'POSIX function defined and unset in virtual subshell'
# ======
# Sourcing a dot script in a virtual subshell
echo 'echo "$@"' > $tmp/dot.sh
before=$(getmem)
for ((i=0; i < N; i++))
do (. "$tmp/dot.sh" dot one two three >/dev/null)
done
after=$(getmem)
err_exit_if_leak 'script dotted in virtual subshell'
echo 'echo "$@"' > $tmp/dot.sh
before=$(getmem)
for ((i=0; i < N; i++))
do (source "$tmp/dot.sh" source four five six >/dev/null)
done
after=$(getmem)
err_exit_if_leak 'script sourced in virtual subshell'
# ======
# Multiple leaks when using arrays in functions (Red Hat #921455)
# Fix based on: https://src.fedoraproject.org/rpms/ksh/blob/642af4d6/f/ksh-20120801-memlik.patch
# TODO: both of these tests still leak (although much less after the patch) when run in a non-C locale.
saveLANG=$LANG; LANG=C # comment out to test remaining leak (1/2)
function _hash
{
typeset w=([abc]=1 [def]=31534 [xyz]=42)
print -u2 $w 2>&-
# accessing the var will leak
}
before=$(getmem)
for ((i=0; i < N; i++))
do _hash
done
after=$(getmem)
err_exit_if_leak 'associative array in function'
function _array
{
typeset w=(1 31534 42)
print -u2 $w 2>&-
# unset w will prevent leak
}
before=$(getmem)
for ((i=0; i < N; i++))
do _array
done
after=$(getmem)
err_exit_if_leak 'indexed array in function'
LANG=$saveLANG # comment out to test remaining leak (2/2)
# ======
# Memory leak in typeset (Red Hat #1036470)
# Fix based on: https://src.fedoraproject.org/rpms/ksh/blob/642af4d6/f/ksh-20120801-memlik3.patch
# The fix was backported from ksh 93v- beta.
function myFunction
{
typeset toPrint="something"
echo "${toPrint}"
}
state=$(myFunction)
before=$(getmem)
for ((i=0; i < N; i++))
do state=$(myFunction)
done
after=$(getmem)
err_exit_if_leak 'typeset in function called by command substitution'
# ======
# Check that unsetting an alias frees both the node and its value
before=$(getmem)
for ((i=0; i < N; i++))
do alias "test$i=command$i"
unalias "test$i"
done
after=$(getmem)
err_exit_if_leak 'unalias'
# ======
# Red Hat bug rhbz#982142: command substitution leaks
# case1: Nested command substitutions
# (reportedly already fixed in 93u+, but let's keep the test)
before=$(getmem)
for ((i=0; i < N; i++))
do a=`true 1 + \`true 1 + 1\`` # was: a=`expr 1 + \`expr 1 + 1\``
done
after=$(getmem)
err_exit_if_leak 'nested command substitutions'
# case2: Command alias
alias ls='true -ltr' # was: alias ls='ls -ltr'
before=$(getmem)
for ((i=0; i < N; i++))
do eval 'a=`ls`'
done
after=$(getmem)
unalias ls
err_exit_if_leak 'alias in command substitution'
# case3: Function call via autoload
cat >$tmp/func1 <<\EOF
function func1
{
echo "func1 call";
}
EOF
FPATH=$tmp
autoload func1
a=`func1` # steady memory state
before=$(getmem)
for ((i=0; i < N; i++))
do a=`func1`
done
after=$(getmem)
unset -f func1
unset -v FPATH
err_exit_if_leak 'function call via autoload in command substitution'
# ======
# add some random utilities to the hash table to detect memory leak on hash table reset when changing PATH
random_utils=(chmod cp mv awk sed diff comm cut sort uniq date env find mkdir rmdir pr sleep)
save_PATH=$PATH
hash "${random_utils[@]}"
before=$(getmem)
for ((i=0; i < N; i++))
do hash -r
hash "${random_utils[@]}"
done
after=$(getmem)
err_exit_if_leak 'clear hash table (hash -r) in main shell'
before=$(getmem)
for ((i=0; i < N; i++))
do PATH=/dev/null
PATH=$save_PATH
hash "${random_utils[@]}"
done
after=$(getmem)
err_exit_if_leak 'set PATH value in main shell'
before=$(getmem)
for ((i=0; i < N; i++))
do PATH=/dev/null command true
done
after=$(getmem)
err_exit_if_leak 'run command with preceding PATH assignment in main shell'
: <<'disabled' # TODO: known leak (approx 73552 bytes after 512 iterations)
before=$(getmem)
for ((i=0; i < N; i++))
do typeset -A PATH
unset PATH
PATH=$save_PATH
hash "${random_utils[@]}"
done
after=$(getmem)
err_exit_if_leak 'set PATH attribute in main shell'
disabled
: <<'disabled' # TODO: known leak (approx 99568 bytes after 512 iterations)
before=$(getmem)
for ((i=0; i < N; i++))
do unset PATH
PATH=$save_PATH
hash "${random_utils[@]}"
done
after=$(getmem)
err_exit_if_leak 'unset PATH in main shell'
disabled
hash "${random_utils[@]}"
before=$(getmem)
for ((i=0; i < N; i++))
do (hash -r)
done
after=$(getmem)
err_exit_if_leak 'clear hash table (hash -r) in subshell'
: <<'disabled' # TODO: known leak (approx 123520 bytes after 512 iterations)
before=$(getmem)
for ((i=0; i < N; i++))
do (PATH=/dev/null)
done
after=$(getmem)
err_exit_if_leak 'set PATH value in subshell'
disabled
: <<'disabled' # TODO: known leak (approx 24544 bytes after 512 iterations)
before=$(getmem)
for ((i=0; i < N; i++))
do (PATH=/dev/null command true)
done
after=$(getmem)
err_exit_if_leak 'run command with preceding PATH assignment in subshell'
disabled
: <<'disabled' # TODO: known leak (approx 131200 bytes after 512 iterations)
before=$(getmem)
for ((i=0; i < N; i++))
do (readonly PATH)
done
after=$(getmem)
err_exit_if_leak 'set PATH attribute in subshell'
disabled
: <<'disabled' # TODO: known leak (approx 229440 bytes after 512 iterations)
before=$(getmem)
for ((i=0; i < N; i++))
do (unset PATH)
done
after=$(getmem)
err_exit_if_leak 'unset PATH in subshell'
disabled
# ======
# Test for a memory leak after 'cd' (in relation to $PWD and $OLDPWD)
original_pwd=$PWD
before=$(getmem)
for ((i=0; i < N; i++))
do cd /tmp
cd - > /dev/null
PWD=/foo
OLDPWD=/bar
cd /bin
cd /usr
cd /dev
cd /dev
cd - > /dev/null
unset OLDPWD PWD
cd /bin
cd /tmp
done
after=$(getmem)
err_exit_if_leak 'PWD and/or OLDPWD changed by cd'
cd $original_pwd
# ======
# https://github.com/ksh93/ksh/issues/253#issuecomment-815308466
: <<'disabled' # TODO: known leak(s) (approx 1008 KiB after 16384 iterations)
before=$(getmem)
for ((i=0; i < N; i++))
do
(SECONDS=1; LANG=C)
done
after=$(getmem)
err_exit_if_leak 'Variable with discipline function in subshell causes memory leak'
disabled
# ======
exit $((Errors<125?Errors:125))