#!/bin/bash
#------------------------------------------------------------------------------
set -u
#------------------------------------------------------------------------------
# Usage:
#
# cd /p4/1/logs
# analyze_p4verify_log.sh
#
# Optionally, skip the cd and specify the path to the log file as the first
# parameter, e.g.:
#
# analyze_p4verify_log.sh /p4/1/logs/my_old_p4verify.logA
#
# Note that analyze_p4verify_log.sh should be in the PATH of the perforce
# OSUSER.
#==============================================================================
# Declarations and Environment
declare -i ErrorCount=0
declare -i WarningCount=0
declare ThisScript=${0##*/}
declare CmdLine="$0 $*"
declare Version=1.1.4
declare P4VerifyLog="${1:-p4verify.log}"
declare -i Debug=${DEBUG:-0}
declare -i NoOp=0
declare Cmd=
declare FileRev=
declare File=
declare ArchiveFile=
declare -A ArchiveFileRefs
declare -A DepotFileRevs
declare -i RefCount
declare -i RevCount
declare -i LoggedVerifyErrorCount=0
declare -i VerifyErrorCount=0
declare -i AlreadyCleanCount=0
declare Rev=
declare TmpFile1=
declare TmpFile2=
declare LastLineNo=
declare Log=
declare H="=============================================================================="
#==============================================================================
# Local Functions
function msg () { echo -e "$*"; }
function dbg () { [[ "$Debug" -ne 0 ]] && echo -e "$*" >&2 ; }
function errmsg () { msg "\\nError: ${1:-Unknown Error}\\n"; ErrorCount+=1; }
function warnmsg () { msg "\\nWarning: ${1:-Unknown Warning}\\n"; WarningCount+=1; }
function bail () { errmsg "${1:-Unknown Error}"; exit "${2:-1}"; }
function usage () { msg "\\nNo docs yet.\\n"; exit 1; }
#------------------------------------------------------------------------------
# Function: terminate
function terminate
{
# Disable signal trapping.
trap - EXIT SIGINT SIGTERM
# Stop logging.
[[ "$Log" == off ]] || msg "Log is: $Log\\n${H}"
# With the trap removed, exit.
exit "$ErrorCount"
}
#==============================================================================
# Command Line Parsing
declare -i shiftArgs=0
set +u
while [[ $# -gt 0 ]]; do
case $1 in
(-h) usage -h;;
(-man) usage -man;;
(-L) Log="$2"; shiftArgs=1;;
(-n) NoOp=1;;
(-d) Debug=1;;
(-D) Debug=1; set -x;; # Debug; use bash 'set -x' extreme debug mode.
(*) usage -h "Unknown arg ($1).";;
esac
# Shift (modify $#) the appropriate number of times.
shift; while [[ $shiftArgs -gt 0 ]]; do
[[ $# -eq 0 ]] && usage -h "Incorrect number of arguments."
shiftArgs=$shiftArgs-1
shift
done
done
set -u
[[ -z "$Log" ]] && Log="${LOGS:-/tmp}/${ThisScript%.sh}.$(date +'%Y%d%d-%H%M').log"
#==============================================================================
# Main Program
trap terminate EXIT SIGINT SIGTERM
if [[ "$Log" != off ]]; then
touch "$Log" || bail "Couldn't touch log file [$Log]."
exec > >(tee "$Log")
exec 2>&1
msg "${H}\\nLog is: $Log"
fi
msg "Started $ThisScript v$Version as $USER@${HOSTNAME%%.*} on $(date) as\\n$CmdLine"
msg "NoOp=$NoOp"
TmpFile1=$(mktemp)
TmpFile2=$(mktemp)
LastLineNo=$(grep -n -m1 '^=== Started verify of shelved changelists' "$P4VerifyLog"|cut -d ':' -f 1)
if [[ -n "$LastLineNo" ]]; then
msg "Using top $((LastLineNo-1)) lines of $P4VerifyLog (ignoring shelves)."
if head -"$((LastLineNo-1))" "$P4VerifyLog" > "$TmpFile1"; then
msg "Dropped shelve chunk of log."
else
warnmsg "Could not separate shelved segment of $P4VerifyLog."
cp "$P4VerifyLog" "$TmpFile1" || bail "Failed to create temp file."
fi
else
warnmsg "Could not find shelved segment of $P4VerifyLog."
cp "$P4VerifyLog" "$TmpFile1" || bail "Failed to create temp file."
fi
msg "Extracting files."
dbg "grep -E '^(error|info): //' \"$TmpFile1\" | sed -E -e 's!^(error|info): !!g' -e 's! - .*\$!!g' > \"$TmpFile2\""
grep -E '^(error|info): //' "$TmpFile1" | sed -E -e 's!^(error|info): !!g' -e 's! - .*$!!g' > "$TmpFile2"
while read -r FileRev; do
LoggedVerifyErrorCount+=1
File="${FileRev%#*}"
Rev="${FileRev#*#}"
msg "F:[$File] R:[$Rev]"
Cmd="p4 -s verify -q \"$File#$Rev,#$Rev\""
msg "Running: $Cmd"
if eval "$Cmd"; then
msg "Skipping already clean file \"$File#$Rev\""
AlreadyCleanCount+=1
continue
fi
VerifyErrorCount+=1
msg "HeadRev for [$File] is: $(p4 -ztag -F %rev% files "$File")"
# Count # of bad revs for each depot file.
if [[ -n "${DepotFileRevs["$File"]:-}" ]]; then
RevCount=${DepotFileRevs["$File"]}
DepotFileRevs["$File"]=$((RevCount+1))
else
DepotFileRevs["$File"]=1
fi
# Count # of refs to each archive file.
ArchiveFile="$(p4 -ztag -F %lbrPath% fstat -Ovb "$File#$Rev")"
if [[ -n "${ArchiveFileRefs["$ArchiveFile"]:-}" ]]; then
RefCount=${ArchiveFileRefs["$ArchiveFile"]}
ArchiveFileRefs["$ArchiveFile"]=$((RefCount+1))
else
ArchiveFileRefs["$ArchiveFile"]=1
fi
done < "$TmpFile2"
msg "${H}\\nDepot Files and Revs:
The are ${#DepotFileRevs[@]} depot files. Revs:"
for File in "${!DepotFileRevs[@]}"; do
RevCount=${DepotFileRevs["$File"]}
printf "%-3d %s\n" "$RevCount" "$File"
done
msg "${H}\\nArchive Files and Refs:
The are ${#ArchiveFileRefs[@]} archive files. References:"
for ArchiveFile in "${!ArchiveFileRefs[@]}"; do
RefCount=${ArchiveFileRefs["$ArchiveFile"]}
printf "%-3d %s\n" "$RefCount" "$ArchiveFile"
done
msg "${H}\\nSummary:
Logged File#Rev Errors: $LoggedVerifyErrorCount
Skipped (already clean): $AlreadyCleanCount
MISSING File#Rev Errors: $VerifyErrorCount
MISSING Depot Files: ${#DepotFileRevs[@]}
MISSING Archive Files: ${#ArchiveFileRefs[@]}
Warning Count: $WarningCount
Error Count: $ErrorCount
"
msg "That took $((SECONDS/3600)) hours $((SECONDS%3600/60)) minutes $((SECONDS%60)) seconds.\\n"
exit "${ErrorCount}"