first version of a bash utilities lib
authormkrzewic <mikolaj.krzewicki@cern.ch>
Thu, 27 Nov 2014 15:41:58 +0000 (16:41 +0100)
committermkrzewic <mikolaj.krzewicki@cern.ch>
Thu, 27 Nov 2014 15:45:23 +0000 (16:45 +0100)
functions used in the runQA.sh and benchmark.sh are put in.
functionality includes robust extraction of run data (run number, data
type, year, etc....)
also some provisions for "clean" bash programming like the parseConfig()
function, which makes it possible to use named parameters to
scripts/functions.

PWGPP/scripts/utilities.sh [new file with mode: 0644]

diff --git a/PWGPP/scripts/utilities.sh b/PWGPP/scripts/utilities.sh
new file mode 100644 (file)
index 0000000..6200ba5
--- /dev/null
@@ -0,0 +1,426 @@
+#library of useful PWGPP related bash functions
+#it REQUIRES BASH 4 !!!!
+#blame: Mikolaj Krzewicki, mkrzewic@cern.ch
+
+if [ ${BASH_VERSINFO} -lt 4 ]; then
+  echo "bash version >= 4 needed, you have ${BASH_VERSION}, exiting..."
+  exit 1
+fi
+
+PWGPP_runMap="
+2010 108350 139517
+2011 140441 170593
+2012 171590 193766
+2013 194482 197692
+2014 197693 999999
+"
+
+parseConfig()
+{
+  local args=("$@")
+
+  #first, check if the config file is configured
+  #is yes - source it so that other options can override it
+  #
+  #recommended way of using (at the beginning of your funcion/script):
+  #  if ! parseConfig "${@}"; then return; fi
+  
+  local opt=""
+  
+  #first check if we will need to decode spaces
+  local encodedSpaces=""
+  for opt in "${args[@]}"; do
+    [[ "${opt}" =~ encodedSpaces=.* ]] \
+      && encodedSpaces=1 \
+      && break
+  done
+
+  #then look for a configFile (if any)
+  for opt in "${args[@]}"; do
+    if [[ ${opt} =~ configFile=.* ]]; then
+      eval "${opt}"
+      [[ ! -f ${configFile} ]] \
+        && echo "configFile ${configFile} not found, exiting..." \
+        && return 1
+      echo "using config file: ${configFile}"
+      source "${configFile}"
+      break
+    fi
+  done
+
+  #then, parse the options as they override the options from configFile
+  for opt in "${args[@]}"; do
+    [[ -n ${encodedSpaces} ]] && opt="$(decSpaces ${opt})"
+    if [[ ! "${opt}" =~ .*=.* ]]; then
+      echo "badly formatted option ${var}, should be: option=value, stopping..."
+      return 1
+    fi
+    local var="${opt%%=*}"
+    local value="${opt#*=}"
+    #echo "${var}=${value}"
+    export ${var}="${value}"
+  done
+  return 0
+}
+
+guessRunData()
+{
+  #guess the period from the path, pick the rightmost one
+  period=""
+  runNumber=""
+  year=""
+  pass=""
+  legoTrainRunNumber=""
+  dataType=""
+  originalPass=""
+  originalPeriod=""
+  anchorYear=""
+  shortRunNumber=""
+
+  local oldIFS=${IFS}
+  local IFS="/"
+  declare -a path=( $1 )
+  IFS="${oldIFS}"
+  local dirDepth=$(( ${#path[*]}-1 ))
+  for ((x=${dirDepth};x>=0;x--)); do
+
+    [[ $((x-1)) -ge 0 ]] && local fieldPrev=${path[$((x-1))]}
+    local field=${path[${x}]}
+    local fieldNext=${path[$((x+1))]}
+
+    [[ ${field} =~ ^[0-9]*$ && ${fieldNext} =~ (.*\.zip$|.*\.root$) ]] && legoTrainRunNumber=${field}
+    [[ -n ${legoTrainRunNumber} && -z ${pass} ]] && pass=${fieldPrev}
+    [[ ${field} =~ ^LHC[0-9][0-9][a-z].*$ ]] && period=${field%_*} && originalPeriod=${field}
+    [[ ${field} =~ ^000[0-9][0-9][0-9][0-9][0-9][0-9]$ ]] && runNumber=${field#000}
+    [[ ${field} =~ ^[0-9][0-9][0-9][0-9][0-9][0-9]$ ]] && shortRunNumber=${field}
+    [[ ${field} =~ ^20[0-9][0-9]$ ]] && year=${field}
+    [[ ${field} =~ ^(^sim$|^data$) ]] && dataType=${field}
+  done
+
+  originalPass=${pass}
+  [[ -n ${shortRunNumber} && "${legoTrainRunNumber}" =~ ${shortRunNumber} ]] && legoTrainRunNumber=""
+  [[ -z ${legoTrainRunNumber} ]] && pass=${path[$((dirDepth-1))]}
+  [[ "${dataType}" =~ ^sim$ ]] && pass="passMC" && runNumber=${shortRunNumber} && originalPass="" #for MC not from lego, the runnumber is identified as lego train number, thus needs to be nulled
+  [[ -n ${legoTrainRunNumber} ]] && pass+="_lego${legoTrainRunNumber}"
+  
+  #modify the OCDB: set the year
+  if [[ ${dataType} =~ sim ]]; then 
+    anchorYear=$(run2year $runNumber)
+    if [[ -z "${anchorYear}" ]]; then
+      echo "WARNING: anchorYear not available for this production: ${originalPeriod}, runNumber: ${runNumber}. Cannot set the OCDB."
+      return 1
+    fi
+    ocdbStorage=$(setYear ${anchorYear} ${ocdbStorage})
+  else
+    ocdbStorage=$(setYear ${year} ${ocdbStorage})
+  fi
+
+  #if [[ -z ${dataType} || -z ${year} || -z ${period} || -z ${runNumber}} || -z ${pass} ]];
+  if [[ -z ${runNumber} ]]
+  then
+    #error condition
+    return 1
+  fi
+  
+  #ALL OK
+  return 0
+}
+
+setYear()
+{
+  #set the year
+  #  ${1} - year to be set
+  #  ${2} - where to set the year
+  local year1=$(guessYear ${1})
+  local year2=$(guessYear ${2})
+  local path=${2}
+  [[ ${year1} -ne ${year2} && -n ${year2} && -n ${year1} ]] && path=${2/\/${year2}\//\/${year1}\/}
+  echo ${path}
+  return 0
+}
+
+guessYear()
+{
+  #guess the year from the path, pick the rightmost one
+  local IFS="/"
+  declare -a pathArray=( ${1} )
+  local field
+  local year
+  for field in ${pathArray[@]}; do
+    [[ ${field} =~ ^20[0-9][0-9]$ ]] && year=${field}
+  done
+  echo ${year}
+  return 0
+}
+
+run2year()
+{
+  #for a given run print the year.
+  #the run-year table is ${PWGPP_runMap} (a string)
+  #one line per year, format: year runMin runMax
+  local run=$1
+  [[ -z ${run} ]] && return 1
+  local year=""
+  local runMin=""
+  local runMax=""
+  while read year runMin runMax; do
+    [[ -z ${year} || -z ${runMin} || -z ${runMax} ]] && continue
+    [[ ${run} -ge ${runMin} && ${run} -le ${runMax} ]] && echo ${year} && break
+  done < <(echo "${PWGPP_runMap}")
+  return 0
+}
+
+hostInfo(){
+#
+# Hallo world -  Print AliRoot/Root/Alien system info
+#
+
+#
+# HOST info
+#
+    echo --------------------------------------
+        echo 
+        echo HOSTINFO
+        echo 
+        echo HOSTINFO HOSTNAME"      "$HOSTNAME
+        echo HOSTINFO DATE"          "`date`
+        echo HOSTINFO gccpath"       "`which gcc` 
+        echo HOSTINFO gcc version"   "`gcc --version | grep gcc`
+        echo --------------------------------------    
+
+#
+# ROOT info
+#
+        echo --------------------------------------
+        echo
+        echo ROOTINFO
+        echo 
+        echo ROOTINFO ROOT"           "`which root`
+        echo ROOTINFO VERSION"        "`root-config --version`
+        echo 
+        echo --------------------------------------
+
+
+#
+# ALIROOT info
+#
+        echo --------------------------------------
+        echo
+        echo ALIROOTINFO
+        echo 
+        echo ALIROOTINFO ALIROOT"        "`which aliroot`
+        echo ALIROOTINFO VERSION"        "`echo $ALICE_LEVEL`
+        echo ALIROOTINFO TARGET"         "`echo $ALICE_TARGET`
+        echo 
+        echo --------------------------------------
+
+#
+# Alien info
+#
+#echo --------------------------------------
+#echo
+#echo ALIENINFO
+#for a in `alien --printenv`; do echo ALIENINFO $a; done 
+#echo
+#echo --------------------------------------
+
+#
+# Local Info
+#
+        echo PWD `pwd`
+        echo Dir 
+        ls -al
+        echo
+        echo
+        echo
+  
+  return 0
+}
+
+summarizeLogs()
+{
+  #validate and summarize the status of logs
+  #input is a list of logs, or a glob:
+  #example (summarizes logs in current and subdirs):
+  #  summarizeLogs * */*
+  #exit code 1 if some logs are not validated
+
+  #print a summary of logs
+  local input
+  local file=""
+  declare -A files
+  input=("${@}")
+  [[ -z "${input[*]}" ]] && input=( "${PWD}"/* )
+  
+  #double inclusion protection+make full paths
+  for file in "${input[@]}"; do
+    [[ ! "${file}" =~ ^/ ]] && file="${PWD}"/"${file}"
+    files["${file}"]="${file}"
+  done
+
+  local logFiles
+  logFiles=".*log|stdout|stderr"
+
+  #check logs
+  local logStatus=0
+  local errorSummary=""
+  local validationStatus=""
+  declare -A coreFiles
+  for file in "${files[@]}"; do
+    [[ ! -f ${file} ]] && continue
+    #keep track of core files for later processing
+    [[ "${file}" =~ core$ ]] && coreFiles[${file}]="${file}" && continue
+    [[ ! "${file}" =~ ${logFiles} ]] && continue
+    errorSummary=$(validateLog ${file})
+    validationStatus=$?
+    [[ validationStatus -ne 0 ]] && logStatus=1
+    if [[ ${validationStatus} -eq 0 ]]; then 
+      #in pretend mode randomly report an error in rec.log some cases
+      echo "${file} OK"
+    elif [[ ${validationStatus} -eq 1 ]]; then
+      echo "${file} BAD ${errorSummary}"
+    elif [[ ${validationStatus} -eq 2 ]]; then
+      echo "${file} OK MWAH ${errorSummary}"
+    fi
+  done
+
+  #report core files
+  for x in "${coreFiles[@]}"; do
+    echo ${x}
+    chmod 644 ${x}
+    gdb --batch --quiet -ex "bt" -ex "quit" aliroot ${x} > stacktrace_${x//\//_}.log
+    local nLines[2]
+    nLines=($(wc -l stacktrace_${x//\//_}.log))
+    if [[ ${nLines[0]} -eq 0 ]]; then
+      rm stacktrace_${x//\//_}.log
+    else
+      logStatus=1
+    fi
+  done
+
+  return ${logStatus}
+}
+
+validateLog()
+{
+  #validate one log file
+  #input is path to log file
+  #output an error summary on stdout
+  #exit code is 0 if validated, 1 otherwise
+  log=${1}
+  errorConditions=(
+            'There was a crash'
+            'floating'
+            'error while loading shared libraries'
+            'std::bad_alloc'
+            's_err_syswatch_'
+            'Thread [0-9]* (Thread'
+            'AliFatal'
+            'core dumped'
+            '\.C.*error:.*\.h: No such file'
+            'segmentation'
+            'Interpreter error recovered'
+            ': command not found'
+            ': comando non trovato'
+  )
+
+  warningConditions=(
+            'This is serious'
+  )
+
+  local logStatus=0
+  local errorSummary=""
+  local warningSummary=""
+
+  for ((i=0; i<${#errorConditions[@]};i++)); do
+    local tmp=$(grep -m1 -e "${errorConditions[${i}]}" ${log})
+    [[ -n ${tmp} ]] && tmp+=" : "
+    errorSummary+=${tmp}
+  done
+
+  for ((i=0; i<${#warningConditions[@]};i++)); do
+    local tmp=$(grep -m1 -e "${warningConditions[${i}]}" ${log})
+    [[ -n ${tmp} ]] && tmp+=" : "
+    warningSummary+=${tmp}
+  done
+
+  if [[ -n ${errorSummary} ]]; then 
+    echo "${errorSummary}"
+    return 1
+  fi
+
+  if [[ -n ${warningSummary} ]]; then
+    echo "${warningSummary}"
+    return 2
+  fi
+
+  return 0
+}
+
+mergeSysLogs()
+{
+  if [[ $# -lt 2 ]]; then
+    echo 'merge syslogs to an output file'
+    echo 'usage:'
+    echo 'mergeSysLogs outputFile inputFile1 inputFile2 ...'
+    return 0
+  fi
+  local outputFile
+  local inputFiles
+  local i
+  local x
+  local runNumber
+  outputFile=${1}
+  shift
+  inputFiles="$@"
+  i=0
+  if ! ls -1 ${inputFiles} &>/dev/null; then echo "the files dont exist!: ${inputFiles}"; return 1; fi
+  while read x; do 
+    runNumber=$(guessRunNumber ${x})
+    [[ -z ${runNumber} ]] && echo "run number cannot be guessed for ${x}" && continue
+    awk -v run=${runNumber} -v i=${i} 'NR > 1 {print run" "$0} NR==1 && i==0 {print "run/I:"$0}' ${x}
+    (( i++ ))
+  done < <(ls -1 ${inputFiles}) > ${outputFile}
+  return 0
+}
+
+stackTraceTree()
+{
+  if [[ $# -lt 1 ]]; then
+    echo 'make stacktrace processing  in case of standard root crash log'
+    echo 'input is a (list of) text files with the stack trace (either gdb aoutput'
+    echo 'produced with e.g. gdb --batch --quiet -ex "bt" -ex "quit" aliroot core,'
+    echo 'or the root crash log), output is a TTree formatted table.'
+    echo 'example usage:'
+    echo 'benchmark.sh stackTraceTree /foo/*/rec.log'
+    echo 'benchmark.sh stackTraceTree $(cat file.list)'
+    echo 'benchmark.sh stackTraceTree `cat file.list`'
+    return 0
+  fi
+  gawk '
+       BEGIN { 
+               print "frame/I:method/C:line/C:cpass/I:aliroot/I:file/C";
+               RS="#[0-9]*";
+               aliroot=0;
+               read=1;
+             } 
+      /There was a crash/ {read=1;}
+      /The lines below might hint at the cause of the crash/ {read=0;}
+      read==1 { 
+               if ($3 ~ /Ali*/) aliroot=1; else aliroot=0;
+               gsub("#","",RT); 
+               if ($NF!="" && RT!="" && $3!="") print RT" "$3" "$NF" "0" "aliroot" "FILENAME
+             }
+      ' "$@" 2>/dev/null
+}
+
+encSpaces() 
+{ 
+  echo "${1// /±@@±}" 
+}
+
+decSpaces() 
+{ 
+  echo "${1//±@@±/ }" 
+}
+