2 #library of useful PWGPP related bash functions
3 #it REQUIRES BASH 4 !!!!
4 #blame: Mikolaj Krzewicki, mkrzewic@cern.ch
6 if [ ${BASH_VERSINFO} -lt 4 ]; then
7 echo "bash version >= 4 needed, you have ${BASH_VERSION}, exiting..."
23 #parse command line arguments, they have to be in the form
25 #they are then set in the environment
27 #optionally a config file can be specified in the arguments:
28 # configFile=<someFile>
29 #config file sets variables: option=value
30 #command line options override config file
32 #recommended way of using (at the beginning of your funcion/script):
33 # if ! parseConfig "${@}"; then return; fi
38 #first check if we will need to decode spaces
39 local encodedSpaces=""
40 for opt in "${args[@]}"; do
41 [[ "${opt}" =~ encodedSpaces=.* ]] \
46 #then look for a configFile (if any)
47 for opt in "${args[@]}"; do
48 if [[ ${opt} =~ configFile=.* ]]; then
50 [[ ! -f ${configFile} ]] \
51 && echo "configFile ${configFile} not found, exiting..." \
53 echo "using config file: ${configFile}"
54 source "${configFile}"
59 #then, parse the options as they override the options from configFile
60 for opt in "${args[@]}"; do
61 [[ -n ${encodedSpaces} ]] && opt="$(decSpaces ${opt})"
62 if [[ ! "${opt}" =~ .*=.* ]]; then
63 echo "badly formatted option ${var}, should be: option=value, stopping..."
66 local var="${opt%%=*}"
67 local value="${opt#*=}"
68 #echo "${var}=${value}"
69 export ${var}="${value}"
76 #guess the period from the path, pick the rightmost one
77 #if $ocdbStorage is set it will be reset to the anchorYear (for MC)
91 [[ -z ${1} ]] && return 1
92 declare -a path=( $1 )
94 local dirDepth=$(( ${#path[*]}-1 ))
95 for ((x=${dirDepth};x>=0;x--)); do
97 [[ $((x-1)) -ge 0 ]] && local fieldPrev=${path[$((x-1))]}
98 local field=${path[${x}]}
99 local fieldNext=${path[$((x+1))]}
101 [[ ${field} =~ ^[0-9]*$ && ${fieldNext} =~ (.*\.zip$|.*\.root$) ]] && legoTrainRunNumber=${field}
102 [[ -n ${legoTrainRunNumber} && -z ${pass} ]] && pass=${fieldPrev}
103 [[ ${field} =~ ^LHC[0-9][0-9][a-z].*$ ]] && period=${field%_*} && originalPeriod=${field}
104 [[ ${field} =~ ^000[0-9][0-9][0-9][0-9][0-9][0-9]$ ]] && runNumber=${field#000}
105 [[ ${field} =~ ^[0-9][0-9][0-9][0-9][0-9][0-9]$ ]] && shortRunNumber=${field}
106 [[ ${field} =~ ^20[0-9][0-9]$ ]] && year=${field}
107 [[ ${field} =~ ^(^sim$|^data$) ]] && dataType=${field}
111 if [[ ${dataType} =~ sim ]]; then
112 [[ -n ${shortRunNumber} && -z ${runNumber} ]] && runNumber=${shortRunNumber}
114 originalPass="" #for MC not from lego, the runNumber is identified as lego train number, thus needs to be nulled
115 anchorYear=$(run2year $runNumber)
116 if [[ -z "${anchorYear}" ]]; then
117 echo "WARNING: anchorYear not available for this production: ${originalPeriod}, runNumber: ${runNumber}. Cannot set the OCDB."
120 #modify the OCDB: set the year
121 ocdbStorage=$(setYear ${anchorYear} ${ocdbStorage})
123 ocdbStorage=$(setYear ${year} ${ocdbStorage})
126 [[ -n ${shortRunNumber} && -z ${runNumber} && -z {dataType} ]] && runNumber=${shortRunNumber}
127 [[ -n ${shortRunNumber} && "${legoTrainRunNumber}" =~ ${shortRunNumber} ]] && legoTrainRunNumber=""
128 [[ -z ${legoTrainRunNumber} && ${dataType} == "data" ]] && pass=${path[$((dirDepth-1))]}
129 [[ -n ${legoTrainRunNumber} ]] && pass+="_lego${legoTrainRunNumber}"
131 #if [[ -z ${dataType} || -z ${year} || -z ${period} || -z ${runNumber}} || -z ${pass} ]];
132 if [[ -z ${runNumber} ]]
144 #guess the run number from the path, pick the rightmost one
145 if guessRunData "${1}"; then
154 #guess the year from the path, pick the rightmost one
155 if guessRunData "${1}"; then
164 #guess the period from the path, pick the rightmost one
165 if guessRunData "${1}"; then
174 #set the year in the string
175 #usualy used to modify the year in $ocdbStorage
176 # ${1} - year to be set
177 # ${2} - where to set the year
178 #if AUTOYEAR is present in target - it will be replaced by the year
179 local yearSource=$(guessYearFast ${1})
180 local yearTarget=$(guessYearFast ${2})
182 [[ ${yearSource} -ne ${yearTarget} && -n ${yearTarget} && -n ${yearSource} ]] \
183 && path=${2/\/"${yearTarget}"\//\/"${yearSource}"\/}
190 #guess the year from the path, pick the rightmost one
191 #is string AUTOYEAR present, will be returned
193 declare -a pathArray=( ${1} )
197 for field in ${pathArray[@]}; do
198 [[ ${field} =~ ^20[0-9][0-9]$ ]] && year="${field}"
199 [[ ${field} == AUTOYEAR ]] && autoYear="${field}"
201 [[ -n ${autoYear} ]] && year="${autoYear}"
208 #for a given run print the year.
209 #the run-year table is ${PWGPP_runMap} (a string)
210 #one line per year, format: year runMin runMax
212 [[ -z ${run} ]] && return 1
216 while read year runMin runMax; do
217 [[ -z ${year} || -z ${runMin} || -z ${runMax} ]] && continue
218 [[ ${run} -ge ${runMin} && ${run} -le ${runMax} ]] && echo ${year} && break
219 done < <(echo "${PWGPP_runMap}")
225 # Hallo world - Print AliRoot/Root/Alien system info
231 echo --------------------------------------
235 echo HOSTINFO HOSTNAME" "$HOSTNAME
236 echo HOSTINFO DATE" "`date`
237 echo HOSTINFO gccpath" "`which gcc`
238 echo HOSTINFO gcc version" "`gcc --version | grep gcc`
239 echo --------------------------------------
244 echo --------------------------------------
248 echo ROOTINFO ROOT" "`which root`
249 echo ROOTINFO VERSION" "`root-config --version`
251 echo --------------------------------------
257 echo --------------------------------------
261 echo ALIROOTINFO ALIROOT" "`which aliroot`
262 echo ALIROOTINFO VERSION" "`echo $ALICE_LEVEL`
263 echo ALIROOTINFO TARGET" "`echo $ALICE_TARGET`
265 echo --------------------------------------
270 #echo --------------------------------------
273 #for a in `alien --printenv`; do echo ALIENINFO $a; done
275 #echo --------------------------------------
292 #validate and summarize the status of logs
293 #input is a list of logs, or a glob:
294 #example (summarizes logs in current and subdirs):
295 # summarizeLogs * */*
296 #if no args given, process all files in PWD
297 #exit code 1 if some logs are not validated
299 #print a summary of logs
304 [[ -z "${input[*]}" ]] && input=( "${PWD}"/* )
306 #double inclusion protection+make full paths
307 for file in "${input[@]}"; do
308 [[ ! "${file}" =~ ^/ ]] && file="${PWD}/${file}"
309 files["${file}"]="${file}"
313 logFiles="\.*log$|^stdout$|^stderr$"
317 local errorSummary=""
318 local validationStatus=""
320 for file in "${files[@]}"; do
321 [[ ! -f ${file} ]] && continue
322 #keep track of core files for later processing
323 [[ "${file##*/}" =~ ^core$ ]] && coreFiles[${file}]="${file}" && continue
324 [[ ! "${file##*/}" =~ ${logFiles} ]] && continue
325 errorSummary=$(validateLog ${file})
327 [[ validationStatus -ne 0 ]] && logStatus=1
328 if [[ ${validationStatus} -eq 0 ]]; then
329 #in pretend mode randomly report an error in rec.log some cases
331 elif [[ ${validationStatus} -eq 1 ]]; then
332 echo "${file} BAD ${errorSummary}"
333 elif [[ ${validationStatus} -eq 2 ]]; then
334 echo "${file} OK MWAH ${errorSummary}"
339 for x in "${coreFiles[@]}"; do
342 #gdb --batch --quiet -ex "bt" -ex "quit" aliroot ${x} > stacktrace_${x//\//_}.log
343 gdb --batch --quiet -ex "bt" -ex "quit" aliroot ${x} > stacktrace.log
345 #nLines=($(wc -l stacktrace_${x//\//_}.log))
346 nLines=($(wc -l stacktrace.log))
347 if [[ ${nLines[0]} -eq 0 ]]; then
348 #rm stacktrace_${x//\//_}.log
352 echo "${x%/*}/stacktrace.log"
361 #validate one log file
362 #input is path to log file
363 #output an error summary on stdout
364 #exit code is 0 if validated, 1 otherwise
369 'error while loading shared libraries'
372 'Thread [0-9]* (Thread'
374 '\.C.*error:.*\.h: No such file'
377 'Interpreter error recovered'
378 ': command not found'
379 ': comando non trovato'
388 local errorSummary=""
389 local warningSummary=""
390 local errorCondition=""
391 for errorCondition in "${errorConditions[@]}"; do
392 local tmp=$(grep -m1 -e "${errorCondition}" ${log})
394 [[ -n ${tmp} ]] && error=" : ${errorCondition}"
395 errorSummary+=${error}
398 local warningCondition=""
399 for warningCondition in "${warningConditions[@]}"; do
400 local tmp=$(grep -m1 -e "${warningCondition}" ${log})
402 [[ -n ${tmp} ]] && warning=" : ${warningCondition}"
403 warningSummary+=${warning}
406 if [[ -n ${errorSummary} ]]; then
407 echo "${errorSummary}"
411 if [[ -n ${warningSummary} ]]; then
412 echo "${warningSummary}"
421 if [[ $# -lt 2 ]]; then
422 echo 'merge syslogs to an output file'
424 echo 'mergeSysLogs outputFile inputFile1 inputFile2 ...'
436 if ! ls -1 ${inputFiles} &>/dev/null; then echo "the files dont exist!: ${inputFiles}"; return 1; fi
438 runNumber=$(guessRunNumber ${x})
439 [[ -z ${runNumber} ]] && echo "run number cannot be guessed for ${x}" && continue
440 awk -v run=${runNumber} -v i=${i} 'NR > 1 {print run" "$0} NR==1 && i==0 {print "run/I:"$0}' ${x}
442 done < <(ls -1 ${inputFiles}) > ${outputFile}
448 if [[ $# -lt 1 ]]; then
449 echo 'make stacktrace processing in case of standard root crash log'
450 echo 'input is a (list of) text files with the stack trace (either gdb aoutput'
451 echo 'produced with e.g. gdb --batch --quiet -ex "bt" -ex "quit" aliroot core,'
452 echo 'or the root crash log), output is a TTree formatted table.'
453 echo 'example usage:'
454 echo 'benchmark.sh stackTraceTree /foo/*/rec.log'
455 echo 'benchmark.sh stackTraceTree $(cat file.list)'
456 echo 'benchmark.sh stackTraceTree `cat file.list`'
462 print "frame/I:method/C:line/C:cpass/I:aliroot/I:file/C";
467 /There was a crash/ {read=1;}
468 /The lines below might hint at the cause of the crash/ {read=0;}
470 if ($3 ~ /Ali*/) aliroot=1; else aliroot=0;
472 if ($NF!="" && RT!="" && $3!="") print RT" "$3" "$NF" "0" "aliroot" "FILENAME
479 #plot the stacktrace tree,
480 #first arg is the text file in the root tree format
481 #second arg is optional: a plot is written to file instead of screen
482 #third arg is optional: selection for plotting, default skip G_ stuff
484 local plot=${2:-"crashes.png"}
485 local selection=${3:-'!strstr(method,\"G__\")'}
486 [[ ! -f ${tree} ]] && echo "plotStackTraceTree: no input file given" && return 1
488 TTree* t=AliSysInfo::MakeTree("${tree}");
489 TCanvas* canvas = new TCanvas("QA crashes","QA crashes",1);
490 t->Draw("method","${selection}","");
491 canvas->SaveAs("${plot}");
499 echo "${1// /±@@±}"
504 echo "${1//±@@±/ }"
509 if [[ $# -lt 1 ]]; then
510 echo "print the full path of a file or directory, like \"readlink -f\" on linux"
512 echo " get_realpath <someFileOrDir>"
518 if cd "$(echo "${1%/*}")" &>/dev/null
520 # file *may* not be local
521 # exception is ./file.ext
522 # try 'cd .; cd -;' *works!*
526 # file *must* be local
529 elif [[ -d "$1" ]]; then
530 if cd "$1" &>/dev/null; then
539 # file *cannot* exist
542 # reassemble realpath
543 echo "$tmppwd"/"${1##*/}"
549 #this function processes the summary logs and prints some stats
550 #relies on the summary log format produced by summarizeLogs()
551 # - how many checked logs in total
552 # - number of each type of problem
554 # printLogStatistics */*.log
555 [[ ! -f $1 ]] && return 1
556 echo "log statistics from: ${1%/*}"
559 BEGIN {nOK=0; nCores=0; nStackTraces=0;}
561 /\/stacktrace.log/ {nStackTraces++}
562 /OK/ {nOK++; nLogs++;}
567 for (i=3; i<=NF; i++)
580 if (err != "" && (write==1 || i==NF))
588 print ("number of succesful jobs: " nOK" out of "nLogs )
591 print key": "sumBAD[key]
593 if (nCores>0) print "core files: "nCores", stack traces: "nStackTraces
600 #create a unique ID for jobs running in parallel
601 #consists of the ip address of the default network interface, PID,
602 #if an argument is given, append it (e.g. a production ID)
603 #the fields are space separated with a tag for easy parsing
604 #spaces in the productionID will be encoded using encSpaces()
605 local productionID=""
606 [[ -n "${1}" ]] && productionID=$(encSpaces "${1}")
607 local defaultIP=$(/sbin/route | awk '$1=="default" {print $8}' | xargs /sbin/ifconfig | awk '/inet / {print $2}' | sed 's/.*\([0-9]?\.[0-9]?\.[0-9]?\.[0-9]?\)/$1/')
608 local id="ip:${defaultIP} pid:${BASHPID}"
609 [[ -n "${productionID}" ]] && id+=" prod:${productionID}"
615 #copies a single file to a local destination: the file may either come from
616 #a local filesystem or from a remote location (whose protocol must be
618 #copy is "robust" and it is repeated some times in case of failure before
619 #giving up (1 is returned in that case)
620 #origin: Dario Berzano, dario.berzano@cern.ch
624 [[ -z "${maxCopyTries}" ]] && maxCopyTries=10
628 echo "copy file to local dest started: $src -> $dst"
630 for (( i=1 ; i<=maxCopyTries ; i++ )) ; do
632 echo "...attempt $i of $maxCopyTries"
635 if [[ "$proto" == "$src" ]]; then
640 xrdcp -f "$src" "$dst"
643 curl -L "$src" -O "$dst"
646 echo "protocol not supported: $proto"
652 if [ $? == 0 ] ; then
659 if [[ "$ok" == 1 ]] ; then
660 echo "copy file to local dest OK after $i attempt(s): $src -> $dst"
664 echo "copy file to local dest FAILED after $maxCopyTries attempt(s): $src -> $dst"
670 #recursively copy files and directories
671 #if target is a directory - it must exist!
672 #to avoid using find and the like as they kill
673 #the performance on some cluster file systems
674 #does not copy links to avoid problems
676 destination="${sourceFiles[@]:(-1)}" #last element
677 unset sourceFiles[${#sourceFiles[@]}-1] #remove last element (dst)
678 #[[ ! -f "${destination}" ]]
679 for src in "${sourceFiles[@]}"; do
680 if [[ -f "${src}" && ! -h "${src}" ]]; then
681 paranoidCopyFile "${src}" "${destination}"
682 elif [[ -d "${src}" && ! -h "${src}" ]]; then
684 dst="${destination}/${src##*/}"
686 paranoidCp "${src}"/* "${dst}"
693 #copy a single file to a target in an existing dir
694 #repeat a few times if copy fails
695 #returns 1 on failure, 0 on success
696 src=$(get_realpath "${1}")
697 dst=$(get_realpath "${2}")
698 [[ -d "${dst}" ]] && dst="${dst}/${src##*/}"
700 [[ -z "${src}" ]] && echo "$1 does not exist" && return 1
701 [[ -z "${dst}" ]] && echo "$2 does not exist" && return 1
702 #check if we are not trying to copy to the same file
703 [[ "${src}" == "${dst}" ]] && echo "$dst==$src, not copying" && return 0
705 [[ -z "${maxCopyTries}" ]] && maxCopyTries=10
707 echo "paranoid copy started: $src -> $dst"
708 for (( i=1 ; i<=maxCopyTries ; i++ )) ; do
710 echo "...attempt $i of $maxCopyTries"
715 if [ $? == 0 ] ; then
722 if [[ "$ok" == 1 ]] ; then
723 echo "paranoid copy OK after $i attempt(s): $src -> $dst"
727 echo "paranoid copy FAILED after $maxCopyTries attempt(s): $src -> $dst"
733 # generate semirandom pwd using 2 keys
735 # generPWD myserviceaccount10 key11
738 heslo0=`md5sum <<< "$key0 $key1" | cut -c 1-16`
739 heslo=`echo $heslo0 | cut -c 1-8| awk '{print toupper($0)}'`
740 heslo=$heslo`echo $heslo0 | cut -c 8-15| awk '{print tolower($0)}'`%
744 #this makes debugging easier:
745 #executes the command given as an argument in this environment
747 # bashdb utilities.sh summarizeLogs * */*
748 [[ $# != 0 ]] && eval "$@"