123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269 |
- #!/bin/sh
- # Copyright (c) 2005, Google Inc.
- # All rights reserved.
- #
- # Redistribution and use in source and binary forms, with or without
- # modification, are permitted provided that the following conditions are
- # met:
- #
- # * Redistributions of source code must retain the above copyright
- # notice, this list of conditions and the following disclaimer.
- # * Redistributions in binary form must reproduce the above
- # copyright notice, this list of conditions and the following disclaimer
- # in the documentation and/or other materials provided with the
- # distribution.
- # * Neither the name of Google Inc. nor the names of its
- # contributors may be used to endorse or promote products derived from
- # this software without specific prior written permission.
- #
- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- # ---
- # Author: Craig Silverstein
- #
- # Runs the 4 profiler unittests and makes sure their profiles look
- # appropriate. We expect two commandline args, as described below.
- #
- # We run under the assumption that if $PROFILER1 is run with no
- # arguments, it prints a usage line of the form
- # USAGE: <actual executable being run> [...]
- #
- # This is because libtool sometimes turns the 'executable' into a
- # shell script which runs an actual binary somewhere else.
- # We expect BINDIR and PPROF_PATH to be set in the environment.
- # If not, we set them to some reasonable values
- BINDIR="${BINDIR:-.}"
- PPROF_PATH="${PPROF_PATH:-$BINDIR/src/pprof}"
- if [ "x$1" = "x-h" -o "x$1" = "x--help" ]; then
- echo "USAGE: $0 [unittest dir] [path to pprof]"
- echo " By default, unittest_dir=$BINDIR, pprof_path=$PPROF_PATH"
- exit 1
- fi
- TMPDIR=/tmp/profile_info
- UNITTEST_DIR=${1:-$BINDIR}
- PPROF=${2:-$PPROF_PATH}
- # We test the sliding-window functionality of the cpu-profile reader
- # by using a small stride, forcing lots of reads.
- PPROF_FLAGS="--test_stride=128"
- PROFILER1="$UNITTEST_DIR/profiler1_unittest"
- PROFILER2="$UNITTEST_DIR/profiler2_unittest"
- PROFILER3="$UNITTEST_DIR/profiler3_unittest"
- PROFILER4="$UNITTEST_DIR/profiler4_unittest"
- # Unfortunately, for us, libtool can replace executables with a shell
- # script that does some work before calling the 'real' executable
- # under a different name. We need the 'real' executable name to run
- # pprof on it. We've constructed all the binaries used in this
- # unittest so when they are called with no arguments, they report
- # their argv[0], which is the real binary name.
- Realname() {
- "$1" 2>&1 | awk '{print $2; exit;}'
- }
- PROFILER1_REALNAME=`Realname "$PROFILER1"`
- PROFILER2_REALNAME=`Realname "$PROFILER2"`
- PROFILER3_REALNAME=`Realname "$PROFILER3"`
- PROFILER4_REALNAME=`Realname "$PROFILER4"`
- # It's meaningful to the profiler, so make sure we know its state
- unset CPUPROFILE
- # Some output/logging in the profiler can cause issues when running the unit
- # tests. For example, logging a warning when the profiler is detected as being
- # present but no CPUPROFILE is specified in the environment. Especially when
- # we are checking for a silent run or specific timing constraints are being
- # checked. So set the env variable signifying that we are running in a unit
- # test environment.
- PERFTOOLS_UNITTEST=1
- rm -rf "$TMPDIR"
- mkdir "$TMPDIR" || exit 2
- num_failures=0
- RegisterFailure() {
- num_failures=`expr $num_failures + 1`
- }
- # Takes two filenames representing profiles, with their executable scripts,
- # and a multiplier, and verifies that the 'contentful' functions in each
- # profile take the same time (possibly scaled by the given multiplier). It
- # used to be "same" meant within 50%, after adding an noise-reducing X units
- # to each value. But even that would often spuriously fail, so now it's
- # "both non-zero". We're pretty forgiving.
- VerifySimilar() {
- prof1="$TMPDIR/$1"
- exec1="$2"
- prof2="$TMPDIR/$3"
- exec2="$4"
- mult="$5"
- # We are careful not to put exec1 and exec2 in quotes, because if
- # they are the empty string, it means we want to use the 1-arg
- # version of pprof.
- mthread1=`"$PPROF" $PPROF_FLAGS $exec1 "$prof1" | grep test_main_thread | awk '{print $1}'`
- mthread2=`"$PPROF" $PPROF_FLAGS $exec2 "$prof2" | grep test_main_thread | awk '{print $1}'`
- mthread1_plus=`expr $mthread1 + 5`
- mthread2_plus=`expr $mthread2 + 5`
- if [ -z "$mthread1" ] || [ -z "$mthread2" ] || \
- [ "$mthread1" -le 0 -o "$mthread2" -le 0 ]
- # || [ `expr $mthread1_plus \* $mult` -gt `expr $mthread2_plus \* 2` -o \
- # `expr $mthread1_plus \* $mult \* 2` -lt `expr $mthread2_plus` ]
- then
- echo
- echo ">>> profile on $exec1 vs $exec2 with multiplier $mult failed:"
- echo "Actual times (in profiling units) were '$mthread1' vs. '$mthread2'"
- echo
- RegisterFailure
- fi
- }
- # Takes two filenames representing profiles, and optionally their
- # executable scripts (these may be empty if the profiles include
- # symbols), and verifies that the two profiles are identical.
- VerifyIdentical() {
- prof1="$TMPDIR/$1"
- exec1="$2"
- prof2="$TMPDIR/$3"
- exec2="$4"
- # We are careful not to put exec1 and exec2 in quotes, because if
- # they are the empty string, it means we want to use the 1-arg
- # version of pprof.
- "$PPROF" $PPROF_FLAGS $exec1 "$prof1" > "$TMPDIR/out1"
- "$PPROF" $PPROF_FLAGS $exec2 "$prof2" > "$TMPDIR/out2"
- diff=`diff "$TMPDIR/out1" "$TMPDIR/out2"`
- if [ ! -z "$diff" ]; then
- echo
- echo ">>> profile doesn't match, args: $exec1 $prof1 vs. $exec2 $prof2"
- echo ">>> Diff:"
- echo "$diff"
- echo
- RegisterFailure
- fi
- }
- # Takes a filename representing a profile, with its executable,
- # and a multiplier, and verifies that the main-thread function takes
- # the same amount of time as the other-threads function (possibly scaled
- # by the given multiplier). Figuring out the multiplier can be tricky,
- # since by design the main thread runs twice as long as each of the
- # 'other' threads! It used to be "same" meant within 50%, after adding an
- # noise-reducing X units to each value. But even that would often
- # spuriously fail, so now it's "both non-zero". We're pretty forgiving.
- VerifyAcrossThreads() {
- prof1="$TMPDIR/$1"
- # We need to run the script with no args to get the actual exe name
- exec1="$2"
- mult="$3"
- # We are careful not to put exec1 in quotes, because if it is the
- # empty string, it means we want to use the 1-arg version of pprof.
- mthread=`$PPROF $PPROF_FLAGS $exec1 "$prof1" | grep test_main_thread | awk '{print $1}'`
- othread=`$PPROF $PPROF_FLAGS $exec1 "$prof1" | grep test_other_thread | awk '{print $1}'`
- if [ -z "$mthread" ] || [ -z "$othread" ] || \
- [ "$mthread" -le 0 -o "$othread" -le 0 ]
- # || [ `expr $mthread \* $mult \* 3` -gt `expr $othread \* 10` -o \
- # `expr $mthread \* $mult \* 10` -lt `expr $othread \* 3` ]
- then
- echo
- echo ">>> profile on $exec1 (main vs thread) with multiplier $mult failed:"
- echo "Actual times (in profiling units) were '$mthread' vs. '$othread'"
- echo
- RegisterFailure
- fi
- }
- echo
- echo ">>> WARNING <<<"
- echo "This test looks at timing information to determine correctness."
- echo "If your system is loaded, the test may spuriously fail."
- echo "If the test does fail with an 'Actual times' error, try running again."
- echo
- # profiler1 is a non-threaded version
- "$PROFILER1" 50 1 "$TMPDIR/p1" || RegisterFailure
- "$PROFILER1" 100 1 "$TMPDIR/p2" || RegisterFailure
- VerifySimilar p1 "$PROFILER1_REALNAME" p2 "$PROFILER1_REALNAME" 2
- # Verify the same thing works if we statically link
- "$PROFILER2" 50 1 "$TMPDIR/p3" || RegisterFailure
- "$PROFILER2" 100 1 "$TMPDIR/p4" || RegisterFailure
- VerifySimilar p3 "$PROFILER2_REALNAME" p4 "$PROFILER2_REALNAME" 2
- # Verify the same thing works if we specify via CPUPROFILE
- CPUPROFILE="$TMPDIR/p5" "$PROFILER2" 50 || RegisterFailure
- CPUPROFILE="$TMPDIR/p6" "$PROFILER2" 100 || RegisterFailure
- VerifySimilar p5 "$PROFILER2_REALNAME" p6 "$PROFILER2_REALNAME" 2
- CPUPROFILE="$TMPDIR/p5b" "$PROFILER3" 30 || RegisterFailure
- CPUPROFILE="$TMPDIR/p5c" "$PROFILER3" 60 || RegisterFailure
- VerifySimilar p5b "$PROFILER3_REALNAME" p5c "$PROFILER3_REALNAME" 2
- # Now try what happens when we use threads
- "$PROFILER3" 30 2 "$TMPDIR/p7" || RegisterFailure
- "$PROFILER3" 60 2 "$TMPDIR/p8" || RegisterFailure
- VerifySimilar p7 "$PROFILER3_REALNAME" p8 "$PROFILER3_REALNAME" 2
- "$PROFILER4" 30 2 "$TMPDIR/p9" || RegisterFailure
- "$PROFILER4" 60 2 "$TMPDIR/p10" || RegisterFailure
- VerifySimilar p9 "$PROFILER4_REALNAME" p10 "$PROFILER4_REALNAME" 2
- # More threads!
- "$PROFILER4" 25 3 "$TMPDIR/p9" || RegisterFailure
- "$PROFILER4" 50 3 "$TMPDIR/p10" || RegisterFailure
- VerifySimilar p9 "$PROFILER4_REALNAME" p10 "$PROFILER4_REALNAME" 2
- # Compare how much time the main thread takes compared to the other threads
- # Recall the main thread runs twice as long as the other threads, by design.
- "$PROFILER4" 20 4 "$TMPDIR/p11" || RegisterFailure
- VerifyAcrossThreads p11 "$PROFILER4_REALNAME" 2
- # Test symbol save and restore
- "$PROFILER1" 50 1 "$TMPDIR/p12" || RegisterFailure
- "$PPROF" $PPROF_FLAGS "$PROFILER1_REALNAME" "$TMPDIR/p12" --raw \
- >"$TMPDIR/p13" 2>/dev/null || RegisterFailure
- VerifyIdentical p12 "$PROFILER1_REALNAME" p13 "" || RegisterFailure
- "$PROFILER3" 30 2 "$TMPDIR/p14" || RegisterFailure
- "$PPROF" $PPROF_FLAGS "$PROFILER3_REALNAME" "$TMPDIR/p14" --raw \
- >"$TMPDIR/p15" 2>/dev/null || RegisterFailure
- VerifyIdentical p14 "$PROFILER3_REALNAME" p15 "" || RegisterFailure
- # Test using ITIMER_REAL instead of ITIMER_PROF.
- env CPUPROFILE_REALTIME=1 "$PROFILER3" 30 2 "$TMPDIR/p16" || RegisterFailure
- env CPUPROFILE_REALTIME=1 "$PROFILER3" 60 2 "$TMPDIR/p17" || RegisterFailure
- VerifySimilar p16 "$PROFILER3_REALNAME" p17 "$PROFILER3_REALNAME" 2
- # Make sure that when we have a process with a fork, the profiles don't
- # clobber each other
- CPUPROFILE="$TMPDIR/pfork" "$PROFILER1" 1 -2 || RegisterFailure
- n=`ls $TMPDIR/pfork* | wc -l`
- if [ $n != 3 ]; then
- echo "FORK test FAILED: expected 3 profiles (for main + 2 children), found $n"
- num_failures=`expr $num_failures + 1`
- fi
- rm -rf "$TMPDIR" # clean up
- echo "Tests finished with $num_failures failures"
- exit $num_failures
|