#!/bin/sh # Copyright (c) 2016, Perforce Software, 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: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # 2. 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. # # 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 PERFORCE SOFTWARE, INC. 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. RCSCHANGE="$Change: 1350954 $" RCSDATE="$Date: 2016/02/16 $" cp=cp cut=cut date=date echo=echo exit=exit expr=expr grep=grep head=head hostname=hostname id=id mkdir=mkdir mv=mv p4=p4 p4d=p4d printf=printf ps=ps pwd=pwd rm=rm scp=scp sed=sed sh=sh sleep=sleep sort=sort ssh=ssh tail=tail tee=tee test=test touch=touch tr=tr uname=uname uniq=uniq wait=wait wc=wc BENCHMARK=`$echo $0 | $sed 's/^.*\/\([^\/]*\)$/\1/' | $sed 's/^\(.*\)\.sh$/\1/'` BENCHROOT=${BENCHROOT:-`pwd`} cd $BENCHROOT p4=$BENCHROOT/bin/p4 p4d=$BENCHROOT/bin/p4d # Make p4d distinct so that we can test for it via ps etc and not worry about others on the same server if [ -L $BENCHROOT/bin/p4d-$BENCHMARK ]; then rm $BENCHROOT/bin/p4d-$BENCHMARK fi ln -s $BENCHROOT/bin/p4d $BENCHROOT/bin/p4d-$BENCHMARK p4d=$BENCHROOT/bin/p4d-$BENCHMARK CHILD=$BENCHMARK\child BINPATH=bin. DATETIME=`$date +%Y%m%d%H%M%S` # # Host entries are of the form: # # <host>=<platform>[,[<children>][,[<browses>][,[<connection>][,[<seed>][,[<user-offset>]]]]]] # #HOSTS="client1name=platform,32,1024 client2name=platform,32,1024" HOSTS="achilles=linux26x86_64,32,1024,,12073" LICENSE=$BENCHROOT/licenses/license P4PORT=3697; export P4PORT # must NOT be an existing server! P4ROOT=$BENCHROOT/root/$BENCHMARK # must NOT be an existing installation! P4JOURNALdir=$BENCHROOT/journals/$BENCHMARK P4JOURNAL=$P4JOURNALdir/journal # must NOT be an existing journal! P4LOGdir=$BENCHROOT/logs/$BENCHMARK P4LOG=$P4LOGdir/log # must NOT be an existing log! SIDENTITY=browse SOPTIONS="" SOPTIONS="$SOPTIONS -o BatchMode=yes" SOPTIONS="$SOPTIONS -o LogLevel=ERROR" SOPTIONS="$SOPTIONS -o StrictHostKeyChecking=no" SOPTIONS="$SOPTIONS -o UserKnownHostsFile=/dev/null" SUSER=`$id | $sed 's/^[^(]*(\([^)]*\)).*$/\1/'` TARGETPORT=$P4PORT # for connecting through proxy and/or broker TMPPOSIX=/tmp/ TMPWINDOWS=\$TMP/ YAMLFILE=$BENCHMARK.$DATETIME.yml YAMLINDENT=" " ZCKP=$BENCHROOT/p4/ckps/reference01.2016.1.ckp.gz # # If "date +%N" returns nanoseconds on the platform, then toggling the # guard below might result in more accurate measurements. # if [ 0 -eq 1 ] then granularity=centiseconds else granularity=seconds fi args=$* if [ "$args" = "" ] then # # No arguments provided; use default. # args="setup runme cleanup" fi printf2() { # # printf to file descriptor 2. # $printf "$@" >&2 } getrcsvalue() { # # Return an RCS value from a string. The RCS value is the # substring between the first and last space characters. # $echo $1 | $sed 's/^[^ ]* \(.*\) [^ ]*$/\1/' } createyamlfile() { $touch $YAMLFILE } writeyamlfile() { # # Indent the number of YAML levels desired. # i=$1 while [ $i -ne 0 ] do $printf "$YAMLINDENT" >> $YAMLFILE i=`$expr $i - 1` done # # Write the desired text. # shift 1 $printf "%s\n" "`$echo $*`" >> $YAMLFILE } chgcase() { # # Change the first byte of each word in a string to uppercase and # change the remaining bytes of each word to lowercase. Whitespace # between words is preserved. Examples of the behavior of this # function are: # # chgcase foobar returns: Foobar # chgcase "foo bAR" returns: Foo Bar # chgcase "foo bA/R" returns: Foo Ba/r # # For each word in the string, build up a sed command to # substitute the word with the desired case and return # the string after all words have been substituted. # str=$1 for w in $1 do # # Start building the sed command to substitute the word with the # desired case. The "|" character is used as the substitution # delimter since it's unlikely that it will be in the string. # wsed="$sed 's|$w|" # # Append the uppercased first byte of # the substituted word to the sed command. # wsed="$wsed`$echo $w | $cut -b1 | $tr [:lower:] [:upper:]`" # # Append the lowercased remaining bytes of # the substituted word to the sed command. # wsed="$wsed`$echo $w | $cut -b2- | $tr [:upper:] [:lower:]`" # # Finish building the sed command. # wsed="$wsed|'" # # Execute the built sed command to substitute the word # with the desired case. # str=`$echo "$echo \"$str\" | $wsed" | $sh` # # Echo out of this subshell the modified string as words are # substituted. Only the last string echoed will be returned. # $echo "$str" done \ | $tail -1 # return modified string after all words substituted. } absfilepath() { raw="$@" if [ `$echo "$raw" | $cut -b1` != "/" ] then # # Raw filepath is relative to the current working directory; # prepend the current working directory to the raw filepath. # raw="`$pwd`/$raw" fi # # Build the absolute filepath by evaluating each element. # abs="" while [ "$raw" != "" ] do elem=`$echo "$raw" | $sed 's/^\(\/[^\/]*\).*$/\1/'` # element raw=`$echo "$raw" | $sed 's/^\/[^\/]*\(.*\)$/\1/'` # remainder if [ "$elem" = "/.." ] then # # Back up one element. # abs=`$echo "$abs" | $sed 's/^\(.*\)\/[^/]*$/\1/'` elif [ \( "$elem" != "/" -o "$raw" = "" \) -a "$elem" != "/." ] then # # Element is not a single "/" (or it is a trailing "/", which # is allowed) and it's not a trivial "." path element; # append it to the absolute filepath being built. # abs="$abs$elem" fi done $echo "$abs" } nop4ds() { # # Since ps is used to determine when the server has completely # stopped, ensure that ps does not show any p4d processes # associated with this process. # if $ps | $grep p4d | $grep -v $grep then printf2 "ps shows related p4d processes.\n" printf2 "The above p4d processes must be stopped.\n" $exit 1 fi } computeseconds() { # # Compute and return the number of seconds from an "hhmmss" string. # tmp=`$echo $1 | $cut -b1-2` tmp=`$expr \`$expr $tmp \* 60\` + \`$echo $1 | $cut -b3-4\`` $expr `$expr $tmp \* 60` + `$echo $1 | $cut -b5-6` } getseconds() { # # Compute and return the number of seconds since midnight. # computeseconds `$date +%H%M%S` } getcentiseconds() { # # Compute and return the number of centiseconds since midnight. # Centiseconds are rounded from milliseconds. # raw=`$date +%H%M%S%N` tmp=`computeseconds $raw` tmp=`$expr \`$expr $tmp \* 1000\` + \`$echo $raw | $cut -b7-9\`` $expr `$expr $tmp + 5` / 10 } getelapsed() { # # Compute and return the elapsed time (in seconds or centiseconds) # between the starting number of seconds or centiseconds since # midnight, passed as an argument, and the ending number of # seconds or centiseconds since midnight, which is either # passed as an argument or computed as of now. The longest # elapsed time that can be correctly computed is 86,399 seconds or # 8,639,999 centiseconds (one second or centisecond short of 24 hours). # if [ $3 ] then # # Use the provided ending number of seconds or centiseconds # since midnight. # end=$3 else # # Get the number of seconds or centiseconds # since midnight as of now. # end=`get$1` fi if [ $end -lt $2 ] then # # Start time was sometime yesterday; adjust for it. # if [ $1 = "seconds" ] then end=`$expr $end + 86400` else end=`$expr $end + 8640000` fi fi # # Return the elapsed time. # $expr $end - $2 } hundredths() { # # Format centiseconds into seconds and hundredths of seconds. # $printf %d.%02d `$expr $1 / 100` `$expr $1 % 100` } getlocalhost() { # # Return the local host name without the domain. # $hostname | $sed 's/^\([^\.]*\).*$/\1/' } getremotehost() { # # Parse out and return the remote host name. # $echo $1 | $sed "s/^\(.*\)=.*$/\1/" } getplatform() { # # Parse out and return the platform. # $echo $1 | $sed "s/^.*=\([^,]*\).*$/\1/" } getchildren() { # # Parse out and return the (default, if unspecified) number of children. # children=`$echo $1 | $sed "s/^.*=[^,]*,\{0,1\}\([^,]*\).*$/\1/"` if [ -z "$children" ] then children=32 fi $echo $children } getoptions() { # # Parse out any specified child options. The child will provide # the defaults for the unspecified options. # browses=`$echo $1 | $sed "s/^.*=[^,]*,\{0,1\}[^,]*,\{0,1\}\([^,]*\).*$/\1/"` connect=`$echo $1 | $sed "s/^.*=[^,]*,\{0,1\}[^,]*,\{0,1\}[^,]*,\{0,1\}\([^,]*\).*$/\1/"` seed=`$echo $1 | $sed "s/^.*=[^,]*,\{0,1\}[^,]*,\{0,1\}[^,]*,\{0,1\}[^,]*,\{0,1\}\([^,]*\).*$/\1/"` } getuseroffset() { $echo $1 | $sed "s/^.*=[^,]*,\{0,1\}[^,]*,\{0,1\}[^,]*,\{0,1\}[^,]*,\{0,1\}[^,]*,\{0,1\}\([^,]*\).*$/\1/" } overlap() { # # Check for non-trivial overlap, where: # # $1: beginning of the first range # $2: number in the first range # $3: beginning of the second range # $4: number in the second range # $test $1 -lt `$expr $3 + $4` -a $3 -lt `$expr $1 + $2` \ -a $2 -ne 0 -a $4 -ne 0 } usersfit() { # # Determine if the users specified by the number of children and # the user offsets defined for the hosts will result in unique users. # Since the users for each pair of hosts need checked only once, # only n(n-1)/2 overlap checks are needed. # ihost=1 # start at second host while [ $ihost -lt $NHOSTS ] do ihost=`$expr $ihost + 1` useroffsetihost=`eval $echo $\{USEROFFSET_$ihost\}` if [ -z "$useroffsetihost" ] then continue fi nchildrenihost=`eval $echo $\{NCHILDREN_$ihost\}` # # Check the users of this host for overlaps with # the users of only the prior hosts. # jhost=$ihost while [ $jhost -gt 1 ] do jhost=`$expr $jhost - 1` useroffsetjhost=`eval $echo $\{USEROFFSET_$jhost\}` if [ -z "$useroffsetjhost" ] then continue fi nchildrenjhost=`eval $echo $\{NCHILDREN_$jhost\}` if overlap \ $useroffsetihost $nchildrenihost \ $useroffsetjhost $nchildrenjhost then # # The users defined by the number of children and # the user offsets for these two hosts overlap. # return 1 fi done done # # No overlaps were found. # return 0 } makeuseroffset() { # # Make a user offset that for the passed number of children, # there will be no overlap with users of any other host. # nchildrenihost=$1 candidate=0 # first candidate user offset while [ 1 ] # exit only when user offset doesn't overlap do jhost=0 while [ $jhost -lt $NHOSTS ] do jhost=`$expr $jhost + 1` useroffsetjhost=`eval $echo $\{USEROFFSET_$jhost\}` if [ -z "$useroffsetjhost" ] then continue fi nchildrenjhost=`eval $echo $\{NCHILDREN_$jhost\}` if overlap \ $candidate $nchildrenihost \ $useroffsetjhost $nchildrenjhost then # # The candidate user offset and the passed # number of children overlap the users of this host; # set a new candidate user offset to just after the # users of this host. # candidate=`$expr $useroffsetjhost + $nchildrenjhost` # # Begin the next iteration of the outer loop to # start checking the new candidate user offset. # continue 2 fi done # # The candidate user offset and the passed number of children # do not overlap the users of any host; exit the outer loop. # break done $echo $candidate # return the successful candidate user offset } fituseroffsets() { # # For the hosts in the HOSTS definition that did not have # a user offset specified, make user offsets such that # all resulting users will be unique. # ihost=0 while [ $ihost -lt $NHOSTS ] do ihost=`$expr $ihost + 1` if [ -z "`eval $echo $\{USEROFFSET_$ihost\}`" ] then eval USEROFFSET_$ihost=`makeuseroffset \ \`eval $echo $\{NCHILDREN_$ihost\}\`` fi done } checkssh() { # # Check ssh connectivity between the server and client machines. # If there is a lack of connectivity anywhere, bail. # printf2 "Checking ssh connectivity...\n" localhost=`getlocalhost` bail=0 for raw in $HOSTS do host=`getremotehost $raw` printf2 " between $localhost and $host..." # # Check ssh connectivity with a simple echo command. # reply=`"$ssh" $SOPTIONS -i"$SIDENTITY" "$SUSER@$host" \ $echo Hello World! 2>&1` if [ "$reply" = "Hello World!" ] then printf2 " passed.\n" else printf2 " FAILED!\n" printf2 " $reply\n" bail=1 fi done if [ $bail -eq 1 ] then # # There was at least one ssh connectivity failure; bail. # printf2 "Correct ssh connectivity before continuing.\n" $exit 1 fi } islistening() { # # Determine if the server is listening by returning the status # from executing the "p4 info" command. # "$p4" -p `$echo $P4PORT | sed 's/^.*:\([0-9]*\)$/\1/'` \ info > /dev/null 2>&1 } waitlistener() { # # Wait until the parent p4d process is listening. # until islistening do $sleep 1 done } waitchildren() { # # Wait for all the server's child processes to exit by # periodically checking for a single p4d process. The # remaining p4d process is the server parent process. # while [ `$ps | $grep p4d | $grep -v $grep | $wc -l` -ne 1 ] do $sleep 1 done } waitstopped() { # # Wait for the server to completely stop by # periodically checking for related p4d processes. # while $ps | $grep p4d | $grep -v $grep > /dev/null do $sleep 1 done } startserver() { # # Start the server and wait for it to listen and quiesce. # printf2 "Starting server..." "$p4d" -d -p `$echo $P4PORT | sed 's/^.*:\([0-9]*\)$/\1/'` \ -r "$P4ROOT" -J "$P4JOURNAL" -q -L "$P4LOG" -v server=1 -v track=0 waitlistener $sleep 10 printf2 " done.\n" } stopserver() { # # Waiting for all the server's child processes to exit is # possible if the server is still associated with this process. # Otherwise, the child processes for this server are not easily # distinguishable from those of other servers on the machine. # if $ps | $grep p4d | $grep -v $grep > /dev/null then # # Wait for all the server's child processes to exit. This is not # required in a production environment since the server has the # logic to wait for any remaining child processes that might # be updating metadata. But this script waits for all the child # processes to completely exit so that statistics including child # exiting activities (such as fsync() calls) are available and # the server log is pristine (on Solaris). # printf2 "Waiting for any server child processes..." waitchildren printf2 " done.\n" fi if islistening then # # Stop the server and wait for it to completely stop and quiesce. # printf2 "Stopping server..." "$p4" -p 127.0.0.1`$echo $P4PORT | sed 's/^.*\(:[0-9]*\)$/\1/'` \ admin stop waitstopped $sleep 10 printf2 " done.\n" else printf2 "Server not listening. (Already stopped?)\n" fi } setup() { # # Check ssh connectivity between the server and client machines # before any other setup steps are executed. # checkssh # # Ensure that there are no p4d processes associated with this process. # nop4ds # # Create the directories. # printf2 "Creating directories as needed..." $mkdir -p "$P4ROOT" $mkdir -p "$P4JOURNALdir" $mkdir -p "$P4LOGdir" printf2 " done.\n" # # Populate the db.* files by recovering from a checkpoint. # printf2 "Replaying checkpoint..." start=`getseconds` "$p4d" -r "$P4ROOT" -z -jr "$ZCKP" > /dev/null elapsed=`getelapsed seconds $start` printf2 " done.\n" printf2 " checkpoint replay duration: %d seconds\n" $elapsed writeyamlfile $yamllevel \ checkpointName: `$echo $ZCKP | $sed 's/.*\/\([^\/]*\)/\1/'` writeyamlfile $yamllevel "checkpointReplay: $elapsed" # # Copy the license file into place. # printf2 "Copying license..." $cp "$LICENSE" "$P4ROOT" printf2 " done.\n" # # Start the server. # startserver } iswindows() { # # Return true iff the passed platform is Windows. # $test `$echo $1 | $cut -b1-2` = "nt" } iscygwin() { # # Return true iff the passed platform is Cygwin. # $test `$echo $1 | $cut -b1-6` = "cygwin" } childfile() { # # Return the name of the child binary for the passed platform. # name=$CHILD if iswindows $1 || iscygwin $1 then name=$name.exe fi $echo $name } remotechild() { # # Return the absolute path to the child binary on the passed # remote platform. The absolute path to the child binary is based # upon a temporary location commonly used on the remote platform, and # qualified by the name of the local host and the PID of this script # execution (in case the remote host is used concurrently by more # than one execution of this script, perhaps from more than one host). # if iswindows $1 || iscygwin $1 then $echo "$TMPWINDOWS$CHILD.`getlocalhost`.$$.exe" else $echo "$TMPPOSIX$CHILD.`getlocalhost`.$$" fi } copychildfiles() { printf2 "Copying child files to hosts..." # # Copy (using scp) the platform-specific child binary to each of # the remote hosts. The child binaries are copied from the $BINPATH # platform-specific directories on the local host to a temporary # location on the remote hosts. # for raw in $HOSTS do host=`getremotehost $raw` platform=`getplatform $raw` "$cp" \ "$BINPATH$platform/`childfile $platform`" \ "`remotechild $platform`" #"$scp" -q $SOPTIONS -i"$SIDENTITY" \ # "$BINPATH$platform/`childfile $platform`" \ # "$SUSER@$host:\"`remotechild $platform`\"" done printf2 " done.\n" } removelog() { printf2 "Removing server log..." # # Remove the server log. It will be recreated by the server # as the children start browsing. # $rm -f "$P4LOG" printf2 " done.\n" } makecommand() { # # Make the command to run the child. # # Start with the absolute path to the child binary on the remote # platform and the target port for connecting to the server # (perhaps through a proxy and/or broker). # command="`remotechild $platform` -p $TARGETPORT" # # Append "-u user<ichild + user-offset>" (zero-filled) to the command. # browseuser=$1 if [ ! -z "$useroffset" ] then browseuser=`$expr $browseuser + $useroffset` fi command="$command -u user`$printf %.5d \`$expr $browseuser % 100000\``" # # If the number of browses per child on the host was specified, # append "-n <browses>" to the command. # if [ ! -z "$browses" ] then command="$command -n $browses" fi # # If the type of connection each child on the host is to use while # browsing was specified, append "-c <connection>" to the command. # if [ ! -z "$connect" ] then command="$command -c $connect" fi # # If the base of the pseudo-random number generator seeds was specified, # compute the seed for this child using the passed child number on the # host and append "-s <computed-seed>" to the command. # if [ ! -z "$seed" ] then command="$command -s `$expr $seed + \`$expr $1 \* 173\``" fi # # Return the command to run the child. # $echo $command } runchildren() { totalhosts=0 # total hosts on which children are launched totalchildren=0 # total children launched over all hosts printf2 "Running children on hosts..." start=`get$granularity` # # Launch children on all hosts. # for raw in $HOSTS do host=`getremotehost $raw` platform=`getplatform $raw` ichild=`getchildren $raw` getoptions $raw totalhosts=`$expr $totalhosts + 1` totalchildren=`$expr $totalchildren + $ichild` useroffset=`eval $echo $\{USEROFFSET_$totalhosts\}` # # For each host, launch the children from a background subshell. # This allows each host to ramp up simultaneously. # { while [ $ichild -ne 0 ] do #$sleep 1 # some sshd can't handle being slammed all at once #"$ssh" $SOPTIONS -i"$SIDENTITY" "$SUSER@$host" \ # `makecommand $ichild` & ccmd=`makecommand $ichild` $ccmd & ichild=`$expr $ichild - 1` done # # Wait for all children on the host to finish. This subshell # must continue to exist until the children finish since # the wait in the outer loop waits for only these # subshells to finish and not the children. # $wait } & done # # Wait for all subshells to finish. Each subshell waits for # the children it has launched on a host to finish. # $wait elapsed=`getelapsed $granularity $start` printf2 " done.\n" } getcount() { # # Return the count from the passed aggregated line of the form: # # count yyyy/mm/dd hh:mm:ss # $sed "s/^[^0-9]*\([0-9]*\)[^0-9]*[0-9]*\/[0-9]*\/[0-9]* [0-9]*:[0-9]*:[0-9]*$/\1/" } getts() { # # Return the date and time from the passed aggregated line of the form: # # count yyyy/mm/dd hh:mm:ss # $sed "s/^[^0-9]*[0-9]*[^0-9]*\([0-9]*\/[0-9]*\/[0-9]* [0-9]*:[0-9]*:[0-9]*\)$/\1/" } TMPCONCURRENT=/tmp/concurrent.$$ # concurrent children each second makeconcurrent() { # # Make a file containing the number of concurrent children seen # each second in the server log. # # Extract the date, time, and user from all user command start server # log entries, refine to just the unique date, time, and user entries, # extract the date and time from the unique entries, and aggregate by # the date and time. # $grep user- "$P4LOG" \ | $cut -d@ -f1 \ | $cut -d" " -f1-2,5 \ | $cut -f2 \ | $sort -u \ | $cut -d" " -f1-2 \ | $uniq -c > $TMPCONCURRENT } maxconcurrent() { # # Determine the maximum number of concurrent children seen in a # second and the period of time during which this maximum number # of concurrent children were seen. Note that the number of # concurrent children is not necessarily constant throughout # the period of time the maximum number of concurrent children # were seen. # TMPMAXCONCURRENT=/tmp/maxconcurrent # descending number of children # # Sort descending by the number of concurrent children # and then ascending by the date and time. # $sort -k 1rn -k 2,3 $TMPCONCURRENT > $TMPMAXCONCURRENT # # Extract from the first line the maximum number of concurrent # children and the first time this maximum number of concurrent # children was seen. # line=`$head -1 $TMPMAXCONCURRENT` max=`$echo $line | getcount` tsfrom=`$echo $line | getts` # # Extract from the last line with the maximum number of concurrent # children the last time the maximum number of concurrent # children was seen. # tsto=`$grep "^[^0-9]*$max[^0-9]*.*$" $TMPMAXCONCURRENT \ | $tail -1 \ | getts` $rm $TMPMAXCONCURRENT } getconcurrent() { # # Return the number of concurrent children seen in the server log # for the passed date and time. # $grep "$1" $TMPCONCURRENT | getcount } maxforcommand() { # # Print the maximum number of the command started in a second and the # date and time with the maximum number of concurrent children at # which the maximum number of the command started in a second # occurs. That is, there may be several seconds at which the same # maximum number of the command were started; of these, the date # and time printed is the first that has the maximum number of # concurrent children. # max=0 # maximum number of the command started in a second # # Extract the date and time from the command start server log entries, # aggregate by the date and time, and then sort descending on the # aggregated count. The sorted aggregated lines are then passed # into the loop. # $grep user-$1 "$P4LOG" \ | $cut -d" " -f1-2 \ | $cut -f2 \ | $sort \ | $uniq -c \ | $sort -k 1rn -k 2,3 \ | while read line do if [ $max -ne 0 ] then # # Already iterated at least once; check to see if we're done. # if [ `$echo $line | getcount` -lt $max ] then # # The aggregated count has fallen below the maximum number # of the command started in a second, so we're done. # Print what we've found and break from the loop. # printf2 " maximum %s/second: %d at %s" $1 $max "$maxts" printf2 " (%d concurrent children).\n" $maxconcurrent p4cmd=`chgcase "$1"` writeyamlfile $yamllevel "max${p4cmd}Second: $max" writeyamlfile $yamllevel "max${p4cmd}Timestamp: $maxts" writeyamlfile $yamllevel "max${p4cmd}Concurrent: $maxconcurrent" break fi else # # First iteration. The aggregated count on the first line is # the maximum number of the command started in a second since # the lines are sorted in descending order on the count # aggregated by date and time. # max=`$echo $line | getcount` maxconcurrent=0 # maximum number of concurrent children fi # # Get the number of concurrent children at the date and time # of this occurrence of the maximum number of the command # started in a second. # ts=`$echo $line | getts` concurrent=`getconcurrent "$ts"` if [ $concurrent -gt $maxconcurrent ] then # # The number of concurrent children at the date and time of # this occurrence of the maximum number of the command started # in a second is the greatest seen thus far; save it and the # date and time. # maxconcurrent=$concurrent maxts=$ts fi done } countbrowses() { # # Determine the total number of browses and the number of unique # filepaths browsed. Since each browse ends with a 'filelog' command, # both the total number of browses and the number of unique filepaths # browsed can be determined by considering only the filepaths of the # 'filelog' commands (which in this benchmark, only have a single # argument). # TMPCOUNTBROWSES=/tmp/countbrowses.$$ # 'filelog' filepaths # # Determine the total number of browses by extracting the filepaths of # the 'filelog' commands and counting the resultant number of lines. # The filepaths of the 'filelog' commands are saved along the way. # totalbrowses=`$grep user-filelog "$P4LOG" \ | $cut -d"'" -f2- \ | $cut -d" " -f3- \ | $sed "s/'$//" \ | $tee $TMPCOUNTBROWSES \ | $wc -l` # # Determine the number of unique filepaths browsed by refining the # filepaths of the 'filelog' commands to just the unique filepaths # and counting the resultant unique filepaths. # uniquefilepaths=`$sort -u $TMPCOUNTBROWSES | $wc -l` $rm $TMPCOUNTBROWSES } summarizelog() { printf2 "Summarizing server log...\n" if [ $granularity = "centiseconds" ] then elapsed=`hundredths $elapsed` fi # # Print the total number of children launched and the total number # of hosts over which the children were launched. While these could be # (possibly more accurately) computed from the server log, it's much # faster to use the values computed when the children were launched. # printf2 " %d children over %d hosts browsed for %s seconds.\n" \ $totalchildren $totalhosts $elapsed writeyamlfile $yamllevel "totalChildren: $totalchildren" writeyamlfile $yamllevel "totalHosts: $totalhosts" writeyamlfile $yamllevel "totalSeconds: $elapsed" # # Ensure there's a server log that can be summarized. # if [ ! -r "$P4LOG" ] then printf2 "Log file: %s doesn't exist or is unreadable\n" "$P4LOG" printf2 "ABORTING TEST\n" $exit 1 fi # # Make the file containing the number of concurrent children # seen each second in the server log. # makeconcurrent # # Determine and print the maximum number of concurrent children seen # in a second and the period of time during which this maximum number # of concurrent children were seen. # maxconcurrent printf2 " %d children ran concurrently from %s to %s.\n" \ $max "$tsfrom" "$tsto" writeyamlfile $yamllevel "concurrentChildren: $max" writeyamlfile $yamllevel "concurrentFrom: $tsfrom" writeyamlfile $yamllevel "concurrentTo: $tsto" # # Print the maximum number of each interesting command # started in a second. # maxforcommand dirs maxforcommand fstat maxforcommand filelog $rm $TMPCONCURRENT # # Determine and print the total number of browses and # the number of unique filepaths browsed. # countbrowses printf2 " %d total browses browsed %d unique filepaths.\n" \ $totalbrowses $uniquefilepaths writeyamlfile $yamllevel "totalBrowses: $totalbrowses" writeyamlfile $yamllevel "uniquePaths: $uniquefilepaths" } removechildfiles() { printf2 "Removing child files from hosts..." # # Remove the child binaries from the remote hosts. # for raw in $HOSTS do host=`getremotehost $raw` platform=`getplatform $raw` # # It appears that SSH implementations on Windows understand # the "rm" command (but surprisingly, don't understand # the "del" command!). # "$ssh" $SOPTIONS -i"$SIDENTITY" "$SUSER@$host" \ $rm \"`remotechild $platform`\" done printf2 " done.\n" } movelog() { # # Move the server log out of the way so that # it is not truncated by subsequent runs. # target=`$pwd`/$BENCHMARK.$DATETIME.log printf2 "Moving server log to %s..." "$target" $mv "$P4LOG" "$target" printf2 " done.\n" } runme() { yamllevel=$1 # # Check ssh connectivity between the server and client machines # again; bad things might have happened since the setup phase # if it was executed separately. # #checkssh # # Ensure that the server is listening. # islistening || { printf2 "Perforce server is not listening!\n" printf2 "%s %s\n" \ "Run the \"setup\" phase if it has not been run," \ "or run the \"startserver\" phase." $exit 1 } # # Get the number of children specified (or the default) and # the user offsets specified in the HOSTS definition. # NHOSTS=0 for raw in $HOSTS do NHOSTS=`$expr $NHOSTS + 1` eval NCHILDREN_$NHOSTS=`getchildren $raw` eval USEROFFSET_$NHOSTS=`getuseroffset $raw` done # # Ensure that the number of children and user offsets specified # in the HOSTS definition will result in unique users. # usersfit || { printf2 "Users in the HOSTS definition overlap!\n" printf2 "Correct the number of children and/or the\n" printf2 "user offsets in the HOSTS definition\n" printf2 "and restart the \"runme\" phase.\n" $exit 1 } # # Make user offsets for those that were not specified in # the HOSTS definition such that all resulting users will be unique. # fituseroffsets # # Copy child files to temporary locations on the hosts. # copychildfiles # # Remove the server log so that the summarized results # reflect only this run. # removelog # # Run children on the hosts. # runchildren # # Summarize the results for this run. # summarizelog # # Remove child files from the hosts. # #removechildfiles # # Save the server log for later review. # movelog } cleanup() { # # Stop the server. # stopserver # # Remove most of the files created by this benchmark. # printf2 "Removing db.* files, license, journal file, and server log..." $rm -f "$P4ROOT"/db.* "$P4ROOT"/license "$P4JOURNAL" "$P4LOG" printf2 " done.\n" if [ -d "$P4ROOT"/server.locks ] then printf2 "Removing server.locks subdirectory..." $rm -rf "$P4ROOT"/server.locks printf2 " done.\n" fi } rcschange=`getrcsvalue "$RCSCHANGE"` rcsdate=`getrcsvalue "$RCSDATE"` p4dversion=`$p4d -V | $grep '^Rev\..*$' | $sed 's/^Rev\. \(.*\)\.$/\1/'` machinfo=`uname -a | sed "s/\`uname -n | sed 's/\./\\./g'\`/\`uname -n | sed 's/^\([^\.]*\)\..*$/\1/'\`/g"` printf2 "Using versions:\n" printf2 " BROWSE.SH/%s (%s)\n" $rcschange $rcsdate printf2 " $p4dversion\n" printf2 "On machine:\n" printf2 " `$echo "$machinfo" | $cut -b-78`\n" createyamlfile printf2 "BRDB file:\n" printf2 " `absfilepath $YAMLFILE`\n" yamllevel=0 writeyamlfile $yamllevel "benchmark: $BENCHMARK" writeyamlfile $yamllevel "args: $args" writeyamlfile $yamllevel "testInfo:" yamllevel=`$expr $yamllevel + 1` writeyamlfile $yamllevel "rcsChange: $rcschange" writeyamlfile $yamllevel "rcsDate: $rcsdate" writeyamlfile $yamllevel `"$p4" -p $P4PORT info | grep "Server root"` yamllevel=`$expr $yamllevel - 1` writeyamlfile $yamllevel "versions:" yamllevel=`$expr $yamllevel + 1` writeyamlfile $yamllevel "p4d: $p4dversion" yamllevel=`$expr $yamllevel - 1` writeyamlfile $yamllevel "uname: \"$machinfo\"" # # Run phases in the order specified. # irunme=0 for arg in $args do if [ $arg = "setup" -o $arg = "runme" -o $arg = "cleanup" -o \ $arg = "checkssh" -o $arg = "startserver" -o $arg = "stopserver" ] then printf2 "\nStarting %s %s phase...\n" $BENCHMARK $arg if [ $arg = "runme" ] then if [ $irunme -eq 0 ] then writeyamlfile $yamllevel "runme:" yamllevel=`$expr $yamllevel + 1` fi irunme=`$expr $irunme + 1` writeyamlfile $yamllevel "$irunme:" yamllevel=`$expr $yamllevel + 1` fi # # Execute phase in a subshell so that our context # (e.g. yamllevel) is not disturbed. # ( $arg $yamllevel ) if [ $arg = "runme" ] then yamllevel=`$expr $yamllevel - 1` fi else printf2 "\nPhase \"%s\" unknown! Terminating.\n" $arg $exit 1 fi done $exit 0
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#4 | 19171 | Robert Cowham |
Add a seed. Copy in new run_p4d to container. |
||
#3 | 19166 | Robert Cowham | Remove sleep for children as not using ssh | ||
#2 | 19133 | Robert Cowham | Seems to more or less work for localhost | ||
#1 | 19132 | Robert Cowham | New benchmark |