1#!/bin/sh 2 3# Copyright (c) 2005, Google Inc. 4# All rights reserved. 5# 6# Redistribution and use in source and binary forms, with or without 7# modification, are permitted provided that the following conditions are 8# met: 9# 10# * Redistributions of source code must retain the above copyright 11# notice, this list of conditions and the following disclaimer. 12# * Redistributions in binary form must reproduce the above 13# copyright notice, this list of conditions and the following disclaimer 14# in the documentation and/or other materials provided with the 15# distribution. 16# * Neither the name of Google Inc. nor the names of its 17# contributors may be used to endorse or promote products derived from 18# this software without specific prior written permission. 19# 20# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 32# --- 33# Author: Craig Silverstein 34# 35# Runs the 4 profiler unittests and makes sure their profiles look 36# appropriate. We expect two commandline args, as described below. 37# 38# We run under the assumption that if $PROFILER1 is run with no 39# arguments, it prints a usage line of the form 40# USAGE: <actual executable being run> [...] 41# 42# This is because libtool sometimes turns the 'executable' into a 43# shell script which runs an actual binary somewhere else. 44 45# We expect BINDIR and PPROF_PATH to be set in the environment. 46# If not, we set them to some reasonable values 47BINDIR="${BINDIR:-.}" 48PPROF_PATH="${PPROF_PATH:-$BINDIR/src/pprof}" 49 50if [ "x$1" = "x-h" -o "x$1" = "x--help" ]; then 51 echo "USAGE: $0 [unittest dir] [path to pprof]" 52 echo " By default, unittest_dir=$BINDIR, pprof_path=$PPROF_PATH" 53 exit 1 54fi 55 56TMPDIR=/tmp/profile_info 57 58UNITTEST_DIR=${1:-$BINDIR} 59PPROF=${2:-$PPROF_PATH} 60 61# We test the sliding-window functionality of the cpu-profile reader 62# by using a small stride, forcing lots of reads. 63PPROF_FLAGS="--test_stride=128" 64 65PROFILER1="$UNITTEST_DIR/profiler1_unittest" 66PROFILER2="$UNITTEST_DIR/profiler2_unittest" 67PROFILER3="$UNITTEST_DIR/profiler3_unittest" 68PROFILER4="$UNITTEST_DIR/profiler4_unittest" 69 70# Unfortunately, for us, libtool can replace executables with a shell 71# script that does some work before calling the 'real' executable 72# under a different name. We need the 'real' executable name to run 73# pprof on it. We've constructed all the binaries used in this 74# unittest so when they are called with no arguments, they report 75# their argv[0], which is the real binary name. 76Realname() { 77 "$1" 2>&1 | awk '{print $2; exit;}' 78} 79 80PROFILER1_REALNAME=`Realname "$PROFILER1"` 81PROFILER2_REALNAME=`Realname "$PROFILER2"` 82PROFILER3_REALNAME=`Realname "$PROFILER3"` 83PROFILER4_REALNAME=`Realname "$PROFILER4"` 84 85# It's meaningful to the profiler, so make sure we know its state 86unset CPUPROFILE 87 88rm -rf "$TMPDIR" 89mkdir "$TMPDIR" || exit 2 90 91num_failures=0 92 93RegisterFailure() { 94 num_failures=`expr $num_failures + 1` 95} 96 97# Takes two filenames representing profiles, with their executable scripts, 98# and a multiplier, and verifies that the 'contentful' functions in 99# each profile take the same time (possibly scaled by the given 100# multiplier). It used to be "same" meant within 50%, after adding an 101# noise-reducing X units to each value. But even that would often 102# spuriously fail, so now it's "both non-zero". We're pretty forgiving. 103VerifySimilar() { 104 prof1="$TMPDIR/$1" 105 exec1="$2" 106 prof2="$TMPDIR/$3" 107 exec2="$4" 108 mult="$5" 109 110 # We are careful not to put exec1 and exec2 in quotes, because if 111 # they are the empty string, it means we want to use the 1-arg 112 # version of pprof. 113 mthread1=`"$PPROF" $PPROF_FLAGS $exec1 "$prof1" | grep test_main_thread | awk '{print $1}'` 114 mthread2=`"$PPROF" $PPROF_FLAGS $exec2 "$prof2" | grep test_main_thread | awk '{print $1}'` 115 mthread1_plus=`expr $mthread1 + 5` 116 mthread2_plus=`expr $mthread2 + 5` 117 if [ -z "$mthread1" ] || [ -z "$mthread2" ] || \ 118 [ "$mthread1" -le 0 -o "$mthread2" -le 0 ] 119# || [ `expr $mthread1_plus \* $mult` -gt `expr $mthread2_plus \* 2` -o \ 120# `expr $mthread1_plus \* $mult \* 2` -lt `expr $mthread2_plus` ] 121 then 122 echo 123 echo ">>> profile on $exec1 vs $exec2 with multiplier $mult failed:" 124 echo "Actual times (in profiling units) were '$mthread1' vs. '$mthread2'" 125 echo 126 RegisterFailure 127 fi 128} 129 130# Takes two filenames representing profiles, and optionally their 131# executable scripts (these may be empty if the profiles include 132# symbols), and verifies that the two profiles are identical. 133VerifyIdentical() { 134 prof1="$TMPDIR/$1" 135 exec1="$2" 136 prof2="$TMPDIR/$3" 137 exec2="$4" 138 139 # We are careful not to put exec1 and exec2 in quotes, because if 140 # they are the empty string, it means we want to use the 1-arg 141 # version of pprof. 142 "$PPROF" $PPROF_FLAGS $exec1 "$prof1" > "$TMPDIR/out1" 143 "$PPROF" $PPROF_FLAGS $exec2 "$prof2" > "$TMPDIR/out2" 144 diff=`diff "$TMPDIR/out1" "$TMPDIR/out2"` 145 146 if [ ! -z "$diff" ]; then 147 echo 148 echo ">>> profile doesn't match, args: $exec1 $prof1 vs. $exec2 $prof2" 149 echo ">>> Diff:" 150 echo "$diff" 151 echo 152 RegisterFailure 153 fi 154} 155 156# Takes a filename representing a profile, with its executable, 157# and a multiplier, and verifies that the main-thread function takes 158# the same amount of time as the other-threads function (possibly scaled 159# by the given multiplier). Figuring out the multiplier can be tricky, 160# since by design the main thread runs twice as long as each of the 161# 'other' threads! It used to be "same" meant within 50%, after adding an 162# noise-reducing X units to each value. But even that would often 163# spuriously fail, so now it's "both non-zero". We're pretty forgiving. 164VerifyAcrossThreads() { 165 prof1="$TMPDIR/$1" 166 # We need to run the script with no args to get the actual exe name 167 exec1="$2" 168 mult="$3" 169 170 # We are careful not to put exec1 in quotes, because if it is the 171 # empty string, it means we want to use the 1-arg version of pprof. 172 mthread=`$PPROF $PPROF_FLAGS $exec1 "$prof1" | grep test_main_thread | awk '{print $1}'` 173 othread=`$PPROF $PPROF_FLAGS $exec1 "$prof1" | grep test_other_thread | awk '{print $1}'` 174 if [ -z "$mthread" ] || [ -z "$othread" ] || \ 175 [ "$mthread" -le 0 -o "$othread" -le 0 ] 176# || [ `expr $mthread \* $mult \* 3` -gt `expr $othread \* 10` -o \ 177# `expr $mthread \* $mult \* 10` -lt `expr $othread \* 3` ] 178 then 179 echo 180 echo ">>> profile on $exec1 (main vs thread) with multiplier $mult failed:" 181 echo "Actual times (in profiling units) were '$mthread' vs. '$othread'" 182 echo 183 RegisterFailure 184 fi 185} 186 187echo 188echo ">>> WARNING <<<" 189echo "This test looks at timing information to determine correctness." 190echo "If your system is loaded, the test may spuriously fail." 191echo "If the test does fail with an 'Actual times' error, try running again." 192echo 193 194# profiler1 is a non-threaded version 195"$PROFILER1" 50 1 "$TMPDIR/p1" || RegisterFailure 196"$PROFILER1" 100 1 "$TMPDIR/p2" || RegisterFailure 197VerifySimilar p1 "$PROFILER1_REALNAME" p2 "$PROFILER1_REALNAME" 2 198 199# Verify the same thing works if we statically link 200"$PROFILER2" 50 1 "$TMPDIR/p3" || RegisterFailure 201"$PROFILER2" 100 1 "$TMPDIR/p4" || RegisterFailure 202VerifySimilar p3 "$PROFILER2_REALNAME" p4 "$PROFILER2_REALNAME" 2 203 204# Verify the same thing works if we specify via CPUPROFILE 205CPUPROFILE="$TMPDIR/p5" "$PROFILER2" 50 || RegisterFailure 206CPUPROFILE="$TMPDIR/p6" "$PROFILER2" 100 || RegisterFailure 207VerifySimilar p5 "$PROFILER2_REALNAME" p6 "$PROFILER2_REALNAME" 2 208 209CPUPROFILE="$TMPDIR/p5b" "$PROFILER3" 30 || RegisterFailure 210CPUPROFILE="$TMPDIR/p5c" "$PROFILER3" 60 || RegisterFailure 211VerifySimilar p5b "$PROFILER3_REALNAME" p5c "$PROFILER3_REALNAME" 2 212 213# Now try what happens when we use threads 214"$PROFILER3" 30 2 "$TMPDIR/p7" || RegisterFailure 215"$PROFILER3" 60 2 "$TMPDIR/p8" || RegisterFailure 216VerifySimilar p7 "$PROFILER3_REALNAME" p8 "$PROFILER3_REALNAME" 2 217 218"$PROFILER4" 30 2 "$TMPDIR/p9" || RegisterFailure 219"$PROFILER4" 60 2 "$TMPDIR/p10" || RegisterFailure 220VerifySimilar p9 "$PROFILER4_REALNAME" p10 "$PROFILER4_REALNAME" 2 221 222# More threads! 223"$PROFILER4" 25 3 "$TMPDIR/p9" || RegisterFailure 224"$PROFILER4" 50 3 "$TMPDIR/p10" || RegisterFailure 225VerifySimilar p9 "$PROFILER4_REALNAME" p10 "$PROFILER4_REALNAME" 2 226 227# Compare how much time the main thread takes compared to the other threads 228# Recall the main thread runs twice as long as the other threads, by design. 229"$PROFILER4" 20 4 "$TMPDIR/p11" || RegisterFailure 230VerifyAcrossThreads p11 "$PROFILER4_REALNAME" 2 231 232# Test symbol save and restore 233"$PROFILER1" 50 1 "$TMPDIR/p12" || RegisterFailure 234"$PPROF" $PPROF_FLAGS "$PROFILER1_REALNAME" "$TMPDIR/p12" --raw \ 235 >"$TMPDIR/p13" 2>/dev/null || RegisterFailure 236VerifyIdentical p12 "$PROFILER1_REALNAME" p13 "" || RegisterFailure 237 238"$PROFILER3" 30 2 "$TMPDIR/p14" || RegisterFailure 239"$PPROF" $PPROF_FLAGS "$PROFILER3_REALNAME" "$TMPDIR/p14" --raw \ 240 >"$TMPDIR/p15" 2>/dev/null || RegisterFailure 241VerifyIdentical p14 "$PROFILER3_REALNAME" p15 "" || RegisterFailure 242 243# Test using ITIMER_REAL instead of ITIMER_PROF. 244env CPUPROFILE_REALTIME=1 "$PROFILER3" 30 2 "$TMPDIR/p16" || RegisterFailure 245env CPUPROFILE_REALTIME=1 "$PROFILER3" 60 2 "$TMPDIR/p17" || RegisterFailure 246VerifySimilar p16 "$PROFILER3_REALNAME" p17 "$PROFILER3_REALNAME" 2 247 248 249# Make sure that when we have a process with a fork, the profiles don't 250# clobber each other 251CPUPROFILE="$TMPDIR/pfork" "$PROFILER1" 1 -2 || RegisterFailure 252n=`ls $TMPDIR/pfork* | wc -l` 253if [ $n != 3 ]; then 254 echo "FORK test FAILED: expected 3 profiles (for main + 2 children), found $n" 255 num_failures=`expr $num_failures + 1` 256fi 257 258rm -rf "$TMPDIR" # clean up 259 260echo "Tests finished with $num_failures failures" 261exit $num_failures 262