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}"}
185 # The previous line would transform raw:// in raw:/
186 # The next fixes this
187 echo ${path/%:\//:\/\/}
193 #guess the year from the path, pick the rightmost one
194 #is string AUTOYEAR present, will be returned
196 declare -a pathArray=( ${1} )
200 for field in ${pathArray[@]}; do
201 [[ ${field} =~ ^20[0-9][0-9]$ ]] && year="${field}"
202 [[ ${field} == AUTOYEAR ]] && autoYear="${field}"
204 [[ -n ${autoYear} ]] && year="${autoYear}"
211 #for a given run print the year.
212 #the run-year table is ${PWGPP_runMap} (a string)
213 #one line per year, format: year runMin runMax
215 [[ -z ${run} ]] && return 1
219 while read year runMin runMax; do
220 [[ -z ${year} || -z ${runMin} || -z ${runMax} ]] && continue
221 [[ ${run} -ge ${runMin} && ${run} -le ${runMax} ]] && echo ${year} && break
222 done < <(echo "${PWGPP_runMap}")
228 # Hallo world - Print AliRoot/Root/Alien system info
234 echo --------------------------------------
238 echo HOSTINFO HOSTNAME" "$HOSTNAME
239 echo HOSTINFO DATE" "`date`
240 echo HOSTINFO gccpath" "`which gcc`
241 echo HOSTINFO gcc version" "`gcc --version | grep gcc`
242 echo --------------------------------------
247 echo --------------------------------------
251 echo ROOTINFO ROOT" "`which root`
252 echo ROOTINFO VERSION" "`root-config --version`
254 echo --------------------------------------
260 echo --------------------------------------
264 echo ALIROOTINFO ALIROOT" "`which aliroot`
265 echo ALIROOTINFO VERSION" "`echo $ALICE_LEVEL`
266 echo ALIROOTINFO TARGET" "`echo $ALICE_TARGET`
268 echo --------------------------------------
273 #echo --------------------------------------
276 #for a in `alien --printenv`; do echo ALIENINFO $a; done
278 #echo --------------------------------------
295 #validate and summarize the status of logs
296 #input is a list of logs, or a glob:
297 #example (summarizes logs in current and subdirs):
298 # summarizeLogs * */*
299 #if no args given, process all files in PWD
300 #exit code 1 if some logs are not validated
302 #print a summary of logs
307 [[ -z "${input[*]}" ]] && input=( "${PWD}"/* )
309 #double inclusion protection+make full paths
310 for file in "${input[@]}"; do
311 [[ ! "${file}" =~ ^/ ]] && file="${PWD}/${file}"
312 files["${file}"]="${file}"
316 logFiles="\.*log$|^stdout$|^stderr$"
320 local errorSummary=""
321 local validationStatus=""
323 for file in "${files[@]}"; do
324 [[ ! -f ${file} ]] && continue
325 #keep track of core files for later processing
326 [[ "${file##*/}" =~ ^core$ ]] && coreFiles[${file}]="${file}" && continue
327 [[ ! "${file##*/}" =~ ${logFiles} ]] && continue
328 errorSummary=$(validateLog ${file})
330 [[ validationStatus -ne 0 ]] && logStatus=1
331 if [[ ${validationStatus} -eq 0 ]]; then
332 #in pretend mode randomly report an error in rec.log some cases
334 elif [[ ${validationStatus} -eq 1 ]]; then
335 echo "${file} BAD ${errorSummary}"
336 elif [[ ${validationStatus} -eq 2 ]]; then
337 echo "${file} OK MWAH ${errorSummary}"
342 for x in "${coreFiles[@]}"; do
345 #gdb --batch --quiet -ex "bt" -ex "quit" aliroot ${x} > stacktrace_${x//\//_}.log
346 gdb --batch --quiet -ex "bt" -ex "quit" aliroot ${x} > stacktrace.log
348 #nLines=($(wc -l stacktrace_${x//\//_}.log))
349 nLines=($(wc -l stacktrace.log))
350 if [[ ${nLines[0]} -eq 0 ]]; then
351 #rm stacktrace_${x//\//_}.log
355 echo "${x%/*}/stacktrace.log"
364 #validate one log file
365 #input is path to log file
366 #output an error summary on stdout
367 #exit code is 0 if validated, 1 otherwise
372 'error while loading shared libraries'
375 'Thread [0-9]* (Thread'
377 '\.C.*error:.*\.h: No such file'
380 'Interpreter error recovered'
381 ': command not found'
382 ': comando non trovato'
391 local errorSummary=""
392 local warningSummary=""
393 local errorCondition=""
394 for errorCondition in "${errorConditions[@]}"; do
395 local tmp=$(grep -m1 -e "${errorCondition}" ${log})
397 [[ -n ${tmp} ]] && error=" : ${errorCondition}"
398 errorSummary+=${error}
401 local warningCondition=""
402 for warningCondition in "${warningConditions[@]}"; do
403 local tmp=$(grep -m1 -e "${warningCondition}" ${log})
405 [[ -n ${tmp} ]] && warning=" : ${warningCondition}"
406 warningSummary+=${warning}
409 if [[ -n ${errorSummary} ]]; then
410 echo "${errorSummary}"
414 if [[ -n ${warningSummary} ]]; then
415 echo "${warningSummary}"
424 if [[ $# -lt 2 ]]; then
425 echo 'merge syslogs to an output file'
427 echo 'mergeSysLogs outputFile inputFile1 inputFile2 ...'
439 if ! ls -1 ${inputFiles} &>/dev/null; then echo "the files dont exist!: ${inputFiles}"; return 1; fi
441 runNumber=$(guessRunNumber ${x})
442 [[ -z ${runNumber} ]] && echo "run number cannot be guessed for ${x}" && continue
443 awk -v run=${runNumber} -v i=${i} 'NR > 1 {print run" "$0} NR==1 && i==0 {print "run/I:"$0}' ${x}
445 done < <(ls -1 ${inputFiles}) > ${outputFile}
451 if [[ $# -lt 1 ]]; then
452 echo 'make stacktrace processing in case of standard root crash log'
453 echo 'input is a (list of) text files with the stack trace (either gdb aoutput'
454 echo 'produced with e.g. gdb --batch --quiet -ex "bt" -ex "quit" aliroot core,'
455 echo 'or the root crash log), output is a TTree formatted table.'
456 echo 'example usage:'
457 echo 'benchmark.sh stackTraceTree /foo/*/rec.log'
458 echo 'benchmark.sh stackTraceTree $(cat file.list)'
459 echo 'benchmark.sh stackTraceTree `cat file.list`'
465 print "frame/I:method/C:line/C:cpass/I:aliroot/I:file/C";
470 /There was a crash/ {read=1;}
471 /The lines below might hint at the cause of the crash/ {read=0;}
473 if ($3 ~ /Ali*/) aliroot=1; else aliroot=0;
475 if ($NF!="" && RT!="" && $3!="") print RT" "$3" "$NF" "0" "aliroot" "FILENAME
482 #plot the stacktrace tree,
483 #first arg is the text file in the root tree format
484 #second arg is optional: a plot is written to file instead of screen
485 #third arg is optional: selection for plotting, default skip G_ stuff
487 local plot=${2:-"crashes.png"}
488 local selection=${3:-'!strstr(method,\"G__\")'}
489 [[ ! -f ${tree} ]] && echo "plotStackTraceTree: no input file given" && return 1
491 TTree* t=AliSysInfo::MakeTree("${tree}");
492 TCanvas* canvas = new TCanvas("QA crashes","QA crashes",1);
493 t->Draw("method","${selection}","");
494 canvas->SaveAs("${plot}");
512 if [[ $# -lt 1 ]]; then
513 echo "print the full path of a file or directory, like \"readlink -f\" on linux"
515 echo " get_realpath <someFileOrDir>"
521 if cd "$(echo "${1%/*}")" &>/dev/null
523 # file *may* not be local
524 # exception is ./file.ext
525 # try 'cd .; cd -;' *works!*
529 # file *must* be local
532 elif [[ -d "$1" ]]; then
533 if cd "$1" &>/dev/null; then
542 # file *cannot* exist
545 # reassemble realpath
546 echo "$tmppwd"/"${1##*/}"
552 #this function processes the summary logs and prints some stats
553 #relies on the summary log format produced by summarizeLogs()
554 # - how many checked logs in total
555 # - number of each type of problem
557 # printLogStatistics */*.log
558 [[ ! -f $1 ]] && return 1
559 echo "log statistics from: ${1%/*}"
562 BEGIN {nOK=0; nCores=0; nStackTraces=0;}
564 /\/stacktrace.log/ {nStackTraces++}
565 /OK/ {nOK++; nLogs++;}
570 for (i=3; i<=NF; i++)
583 if (err != "" && (write==1 || i==NF))
591 print ("number of succesful jobs: " nOK" out of "nLogs )
594 print key": "sumBAD[key]
596 if (nCores>0) print "core files: "nCores", stack traces: "nStackTraces
603 #create a unique ID for jobs running in parallel
604 #consists of the ip address of the default network interface, PID,
605 #if an argument is given, append it (e.g. a production ID)
606 #the fields are space separated with a tag for easy parsing
607 #spaces in the productionID will be encoded using encSpaces()
608 local productionID=""
609 [[ -n "${1}" ]] && productionID=$(encSpaces "${1}")
610 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/')
611 local id="ip:${defaultIP} pid:${BASHPID}"
612 [[ -n "${productionID}" ]] && id+=" prod:${productionID}"
618 #copies a single file to a local destination: the file may either come from
619 #a local filesystem or from a remote location (whose protocol must be
621 #copy is "robust" and it is repeated some times in case of failure before
622 #giving up (1 is returned in that case)
623 #origin: Dario Berzano, dario.berzano@cern.ch
627 [[ -z "${maxCopyTries}" ]] && maxCopyTries=10
631 echo "copy file to local dest started: $src -> $dst"
633 for (( i=1 ; i<=maxCopyTries ; i++ )) ; do
635 echo "...attempt $i of $maxCopyTries"
638 if [[ "$proto" == "$src" ]]; then
643 xrdcp -f "$src" "$dst"
646 curl -L "$src" -O "$dst"
649 echo "protocol not supported: $proto"
655 if [ $? == 0 ] ; then
662 if [[ "$ok" == 1 ]] ; then
663 echo "copy file to local dest OK after $i attempt(s): $src -> $dst"
667 echo "copy file to local dest FAILED after $maxCopyTries attempt(s): $src -> $dst"
673 #recursively copy files and directories
674 #if target is a directory - it must exist!
675 #to avoid using find and the like as they kill
676 #the performance on some cluster file systems
677 #does not copy links to avoid problems
679 destination="${sourceFiles[@]:(-1)}" #last element
680 unset sourceFiles[${#sourceFiles[@]}-1] #remove last element (dst)
681 #[[ ! -f "${destination}" ]]
682 for src in "${sourceFiles[@]}"; do
683 if [[ -f "${src}" && ! -h "${src}" ]]; then
684 paranoidCopyFile "${src}" "${destination}"
685 elif [[ -d "${src}" && ! -h "${src}" ]]; then
687 dst="${destination}/${src##*/}"
689 paranoidCp "${src}"/* "${dst}"
696 #copy a single file to a target in an existing dir
697 #repeat a few times if copy fails
698 #returns 1 on failure, 0 on success
699 src=$(get_realpath "${1}")
700 dst=$(get_realpath "${2}")
701 [[ -d "${dst}" ]] && dst="${dst}/${src##*/}"
703 [[ -z "${src}" ]] && echo "$1 does not exist" && return 1
704 [[ -z "${dst}" ]] && echo "$2 does not exist" && return 1
705 #check if we are not trying to copy to the same file
706 [[ "${src}" == "${dst}" ]] && echo "$dst==$src, not copying" && return 0
708 [[ -z "${maxCopyTries}" ]] && maxCopyTries=10
710 echo "paranoid copy started: $src -> $dst"
711 for (( i=1 ; i<=maxCopyTries ; i++ )) ; do
713 echo "...attempt $i of $maxCopyTries"
718 if [ $? == 0 ] ; then
725 if [[ "$ok" == 1 ]] ; then
726 echo "paranoid copy OK after $i attempt(s): $src -> $dst"
730 echo "paranoid copy FAILED after $maxCopyTries attempt(s): $src -> $dst"
736 # generate semirandom pwd using 2 keys
738 # generPWD myserviceaccount10 key11
741 heslo0=`md5sum <<< "$key0 $key1" | cut -c 1-16`
742 heslo=`echo $heslo0 | cut -c 1-8| awk '{print toupper($0)}'`
743 heslo=$heslo`echo $heslo0 | cut -c 8-15| awk '{print tolower($0)}'`%
747 #this makes debugging easier:
748 #executes the command given as an argument in this environment
750 # bashdb utilities.sh summarizeLogs * */*
751 [[ $# != 0 ]] && eval "$@"