]> git.uio.no Git - u/mrichter/AliRoot.git/blobdiff - PWGPP/scripts/utilities.sh
Merge branch 'master' of https://git.cern.ch/reps/AliRoot
[u/mrichter/AliRoot.git] / PWGPP / scripts / utilities.sh
old mode 100644 (file)
new mode 100755 (executable)
index 6200ba5..5de647e
@@ -1,3 +1,4 @@
+#!/usr/bin/env bash
 #library of useful PWGPP related bash functions
 #it REQUIRES BASH 4 !!!!
 #blame: Mikolaj Krzewicki, mkrzewic@cern.ch
@@ -11,20 +12,27 @@ PWGPP_runMap="
 2010 108350 139517
 2011 140441 170593
 2012 171590 193766
-2013 194482 197692
-2014 197693 999999
+2013 194308 199146
+2014 202369 206695
+2015 999999 999999
+2016 999999 999999
 "
 
 parseConfig()
 {
-  local args=("$@")
-
-  #first, check if the config file is configured
-  #is yes - source it so that other options can override it
+  #parse command line arguments, they have to be in the form
+  #  option=value
+  #they are then set in the environment
+  #
+  #optionally a config file can be specified in the arguments:
+  #  configFile=<someFile>
+  #config file sets variables: option=value
+  #command line options override config file
   #
   #recommended way of using (at the beginning of your funcion/script):
   #  if ! parseConfig "${@}"; then return; fi
   
+  local args=("$@")
   local opt=""
   
   #first check if we will need to decode spaces
@@ -66,6 +74,7 @@ parseConfig()
 guessRunData()
 {
   #guess the period from the path, pick the rightmost one
+  #if $ocdbStorage is set it will be reset to the anchorYear (for MC)
   period=""
   runNumber=""
   year=""
@@ -79,6 +88,7 @@ guessRunData()
 
   local oldIFS=${IFS}
   local IFS="/"
+  [[ -z ${1} ]] && return 1
   declare -a path=( $1 )
   IFS="${oldIFS}"
   local dirDepth=$(( ${#path[*]}-1 ))
@@ -96,25 +106,28 @@ guessRunData()
     [[ ${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 
+
+  if [[ ${dataType} =~ sim ]]; then
+    [[ -n ${shortRunNumber} && -z ${runNumber} ]] && runNumber=${shortRunNumber}
+    pass="passMC"
+    originalPass="" #for MC not from lego, the runNumber is identified as lego train number, thus needs to be nulled
     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
+    #modify the OCDB: set the year
     ocdbStorage=$(setYear ${anchorYear} ${ocdbStorage})
   else
     ocdbStorage=$(setYear ${year} ${ocdbStorage})
   fi
 
+  [[ -n ${shortRunNumber} && -z ${runNumber} && -z {dataType} ]] && runNumber=${shortRunNumber}
+  [[ -n ${shortRunNumber} && "${legoTrainRunNumber}" =~ ${shortRunNumber} ]] && legoTrainRunNumber=""
+  [[ -z ${legoTrainRunNumber} && ${dataType} == "data" ]] && pass=${path[$((dirDepth-1))]}
+  [[ -n ${legoTrainRunNumber} ]] && pass+="_lego${legoTrainRunNumber}"
+  
   #if [[ -z ${dataType} || -z ${year} || -z ${period} || -z ${runNumber}} || -z ${pass} ]];
   if [[ -z ${runNumber} ]]
   then
@@ -126,29 +139,69 @@ guessRunData()
   return 0
 }
 
+guessRunNumber()
+(
+  #guess the run number from the path, pick the rightmost one
+  if guessRunData "${1}"; then
+    echo ${runNumber}
+    return 0
+  fi
+  return 1
+)
+
+guessYear()
+(
+  #guess the year from the path, pick the rightmost one
+  if guessRunData "${1}"; then
+    echo ${year}
+    return 0
+  fi
+  return 1
+)
+
+guessPeriod()
+(
+  #guess the period from the path, pick the rightmost one
+  if guessRunData "${1}"; then
+    echo ${period}
+    return 0
+  fi
+  return 1
+)
+
 setYear()
 {
-  #set the year
+  #set the year in the string
+  #usualy used to modify the year in $ocdbStorage
   #  ${1} - year to be set
   #  ${2} - where to set the year
-  local year1=$(guessYear ${1})
-  local year2=$(guessYear ${2})
+  #if AUTOYEAR is present in target - it will be replaced by the year
+  local yearSource=$(guessYearFast ${1})
+  local yearTarget=$(guessYearFast ${2})
   local path=${2}
-  [[ ${year1} -ne ${year2} && -n ${year2} && -n ${year1} ]] && path=${2/\/${year2}\//\/${year1}\/}
-  echo ${path}
+  [[ ${yearSource} -ne ${yearTarget} && -n ${yearTarget} && -n ${yearSource} ]] \
+    && path=${2/\/"${yearTarget}"/\/"${yearSource}"}
+  path=${path/\/\//\/}
+  # The previous line would transform raw:// in raw:/
+  # The next fixes this
+  echo ${path/%:\//:\/\/}
   return 0
 }
 
-guessYear()
+guessYearFast()
 {
   #guess the year from the path, pick the rightmost one
+  #is string AUTOYEAR present, will be returned
   local IFS="/"
   declare -a pathArray=( ${1} )
-  local field
-  local year
+  local field=""
+  local year=""
+  local autoYear=""
   for field in ${pathArray[@]}; do
-    [[ ${field} =~ ^20[0-9][0-9]$ ]] && year=${field}
+    [[ ${field} =~ ^20[0-9][0-9]$ ]] && year="${field}"
+    [[ ${field} == AUTOYEAR ]] && autoYear="${field}"
   done
+  [[ -n ${autoYear} ]] && year="${autoYear}"
   echo ${year}
   return 0
 }
@@ -243,6 +296,7 @@ summarizeLogs()
   #input is a list of logs, or a glob:
   #example (summarizes logs in current and subdirs):
   #  summarizeLogs * */*
+  #if no args given, process all files in PWD
   #exit code 1 if some logs are not validated
 
   #print a summary of logs
@@ -254,12 +308,12 @@ summarizeLogs()
   
   #double inclusion protection+make full paths
   for file in "${input[@]}"; do
-    [[ ! "${file}" =~ ^/ ]] && file="${PWD}"/"${file}"
+    [[ ! "${file}" =~ ^/ ]] && file="${PWD}/${file}"
     files["${file}"]="${file}"
   done
 
   local logFiles
-  logFiles=".*log|stdout|stderr"
+  logFiles="\.*log$|^stdout$|^stderr$"
 
   #check logs
   local logStatus=0
@@ -269,8 +323,8 @@ summarizeLogs()
   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
+    [[ "${file##*/}" =~ ^core$ ]] && coreFiles[${file}]="${file}" && continue
+    [[ ! "${file##*/}" =~ ${logFiles} ]] && continue
     errorSummary=$(validateLog ${file})
     validationStatus=$?
     [[ validationStatus -ne 0 ]] && logStatus=1
@@ -288,13 +342,17 @@ summarizeLogs()
   for x in "${coreFiles[@]}"; do
     echo ${x}
     chmod 644 ${x}
-    gdb --batch --quiet -ex "bt" -ex "quit" aliroot ${x} > stacktrace_${x//\//_}.log
+    #gdb --batch --quiet -ex "bt" -ex "quit" aliroot ${x} > stacktrace_${x//\//_}.log
+    gdb --batch --quiet -ex "bt" -ex "quit" aliroot ${x} > stacktrace.log
     local nLines[2]
-    nLines=($(wc -l stacktrace_${x//\//_}.log))
+    #nLines=($(wc -l stacktrace_${x//\//_}.log))
+    nLines=($(wc -l stacktrace.log))
     if [[ ${nLines[0]} -eq 0 ]]; then
-      rm stacktrace_${x//\//_}.log
+      #rm stacktrace_${x//\//_}.log
+      rm stacktrace.log
     else
       logStatus=1
+      echo "${x%/*}/stacktrace.log"
     fi
   done
 
@@ -316,12 +374,13 @@ validateLog()
             's_err_syswatch_'
             'Thread [0-9]* (Thread'
             'AliFatal'
-            'core dumped'
             '\.C.*error:.*\.h: No such file'
             'segmentation'
+            'Segmentation fault'
             'Interpreter error recovered'
             ': command not found'
             ': comando non trovato'
+            'core dumped'
   )
 
   warningConditions=(
@@ -331,17 +390,20 @@ validateLog()
   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}
+  local errorCondition=""
+  for errorCondition in "${errorConditions[@]}"; do
+    local tmp=$(grep -m1 -e "${errorCondition}" ${log})
+    local error=""
+    [[ -n ${tmp} ]] && error=" : ${errorCondition}"
+    errorSummary+=${error}
   done
 
-  for ((i=0; i<${#warningConditions[@]};i++)); do
-    local tmp=$(grep -m1 -e "${warningConditions[${i}]}" ${log})
-    [[ -n ${tmp} ]] && tmp+=" : "
-    warningSummary+=${tmp}
+  local warningCondition=""
+  for warningCondition in "${warningConditions[@]}"; do
+    local tmp=$(grep -m1 -e "${warningCondition}" ${log})
+    local warning=""
+    [[ -n ${tmp} ]] && warning=" : ${warningCondition}"
+    warningSummary+=${warning}
   done
 
   if [[ -n ${errorSummary} ]]; then 
@@ -397,9 +459,10 @@ stackTraceTree()
     echo 'benchmark.sh stackTraceTree `cat file.list`'
     return 0
   fi
+  #cat "${@}" | gawk '
   gawk '
        BEGIN { 
-               print "frame/I:method/C:line/C:cpass/I:aliroot/I:file/C";
+       print "frame/I:method/C:line/C:cpass/I:aliroot/I:file/C";
                RS="#[0-9]*";
                aliroot=0;
                read=1;
@@ -411,7 +474,27 @@ stackTraceTree()
                gsub("#","",RT); 
                if ($NF!="" && RT!="" && $3!="") print RT" "$3" "$NF" "0" "aliroot" "FILENAME
              }
-      ' "$@" 2>/dev/null
+      ' "${@}" 2>/dev/null
+}
+
+plotStackTraceTree()
+{
+  #plot the stacktrace tree,
+  #first arg    is the text file in the root tree format
+  #second arg   is optional: a plot is written to file instead of screen
+  #third arg    is optional: selection for plotting, default skip G_ stuff
+  local tree=$1
+  local plot=${2:-"crashes.png"}
+  local selection=${3:-'!strstr(method,\"G__\")'}
+  [[ ! -f ${tree} ]] && echo "plotStackTraceTree: no input file given" && return 1
+  aliroot -b <<EOF
+TTree* t=AliSysInfo::MakeTree("${tree}");
+TCanvas* canvas = new TCanvas("QA crashes","QA crashes",1);
+t->Draw("method","${selection}","");
+canvas->SaveAs("${plot}");
+.q
+EOF
+  return 0
 }
 
 encSpaces() 
@@ -424,3 +507,245 @@ decSpaces()
   echo "${1//±@@±/ }" 
 }
 
+get_realpath() 
+{
+  if [[ $# -lt 1 ]]; then
+    echo "print the full path of a file or directory, like \"readlink -f\" on linux"
+    echo "Usage:"
+    echo "  get_realpath <someFileOrDir>"
+    return 0
+  fi
+  if [[ -f "$1" ]]
+  then
+    # file *must* exist
+    if cd "$(echo "${1%/*}")" &>/dev/null
+    then
+      # file *may* not be local
+      # exception is ./file.ext
+      # try 'cd .; cd -;' *works!*
+      local tmppwd="$PWD"
+      cd - &>/dev/null
+    else
+      # file *must* be local
+      local tmppwd="$PWD"
+    fi
+  elif [[ -d "$1" ]]; then
+    if cd "$1" &>/dev/null; then
+      local tmppwd="$PWD"
+      cd - &>/dev/null
+      echo "$tmppwd"
+      return 0
+    else
+      return 1
+    fi
+  else
+    # file *cannot* exist
+    return 1 # failure
+  fi
+  # reassemble realpath
+  echo "$tmppwd"/"${1##*/}"
+  return 0 # success
+}
+
+printLogStatistics()
+{
+  #this function processes the summary logs and prints some stats
+  #relies on the summary log format produced by summarizeLogs()
+  # - how many checked logs in total
+  # - number of each type of problem
+  # example usage:
+  #   printLogStatistics */*.log
+  [[ ! -f $1 ]] && return 1
+  echo "log statistics from: ${1%/*}"
+  #cat "${@}" | awk '
+  awk '
+  BEGIN {nOK=0; nCores=0; nStackTraces=0;}
+  /\/core/ {nCores++}
+  /\/stacktrace.log/ {nStackTraces++}
+  /OK/ {nOK++; nLogs++;}
+  /BAD/ {
+    nLogs++
+    err=""
+    write=0
+    for (i=3; i<=NF; i++)
+    { 
+      if ($i ~ /^\:$/) 
+        write=1
+      else
+        write=0
+
+      if (write==0)
+      {
+        if (err=="") err=$i
+        else err=(err FS $i)
+      }
+
+      if (err != "" && (write==1 || i==NF))
+      {
+        sumBAD[err]++
+        err=""
+      }
+    }
+  } 
+  END {
+    print ("number of succesful jobs: " nOK" out of "nLogs )
+    for (key in sumBAD)
+    {
+      print key": "sumBAD[key]
+    }
+    if (nCores>0) print "core files: "nCores", stack traces: "nStackTraces 
+  }
+  ' "${@}"
+}
+
+createUniquePID()
+{
+  #create a unique ID for jobs running in parallel
+  #consists of the ip address of the default network interface, PID,
+  #if an argument is given, append it (e.g. a production ID)
+  #the fields are space separated with a tag for easy parsing
+  #spaces in the productionID will be encoded using encSpaces()
+  local productionID=""
+  [[ -n "${1}" ]] && productionID=$(encSpaces "${1}")
+  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/')
+  local id="ip:${defaultIP} pid:${BASHPID}"
+  [[ -n "${productionID}" ]] && id+=" prod:${productionID}"
+  echo "${id}"
+}
+
+copyFileToLocal()
+(
+  #copies a single file to a local destination: the file may either come from
+  #a local filesystem or from a remote location (whose protocol must be
+  #supported)
+  #copy is "robust" and it is repeated some times in case of failure before
+  #giving up (1 is returned in that case)
+  #origin: Dario Berzano, dario.berzano@cern.ch
+  src="$1"
+  dst="$2"
+  ok=0
+  [[ -z "${maxCopyTries}" ]] && maxCopyTries=10
+
+  proto="${src%%://*}"
+
+  echo "copy file to local dest started: $src -> $dst"
+
+  for (( i=1 ; i<=maxCopyTries ; i++ )) ; do
+
+    echo "...attempt $i of $maxCopyTries"
+    rm -f "$dst"
+
+    if [[ "$proto" == "$src" ]]; then
+      cp "$src" "$dst"
+    else
+      case "$proto" in
+        root)
+          xrdcp -f "$src" "$dst"
+        ;;
+        http)
+          curl -L "$src" -O "$dst"
+        ;;
+        *)
+          echo "protocol not supported: $proto"
+          return 2
+        ;;
+      esac
+    fi
+
+    if [ $? == 0 ] ; then
+      ok=1
+      break
+    fi
+
+  done
+
+  if [[ "$ok" == 1 ]] ; then
+    echo "copy file to local dest OK after $i attempt(s): $src -> $dst"
+    return 0
+  fi
+
+  echo "copy file to local dest FAILED after $maxCopyTries attempt(s): $src -> $dst"
+  return 1
+)
+
+paranoidCp()
+(
+  #recursively copy files and directories
+  #if target is a directory - it must exist!
+  #to avoid using find and the like as they kill
+  #the performance on some cluster file systems
+  #does not copy links to avoid problems
+  sourceFiles=("${@}")
+  destination="${sourceFiles[@]:(-1)}" #last element
+  unset sourceFiles[${#sourceFiles[@]}-1] #remove last element (dst)
+  #[[ ! -f "${destination}" ]] 
+  for src in "${sourceFiles[@]}"; do
+    if [[ -f "${src}" && ! -h  "${src}" ]]; then
+      paranoidCopyFile "${src}" "${destination}"
+    elif [[ -d "${src}" && ! -h "${src}" ]]; then
+      src="${src%/}"
+      dst="${destination}/${src##*/}"
+      mkdir -p "${dst}"
+      paranoidCp "${src}"/* "${dst}"
+    fi
+  done
+)
+
+paranoidCopyFile()
+(
+  #copy a single file to a target in an existing dir
+  #repeat a few times if copy fails
+  #returns 1 on failure, 0 on success
+  src=$(get_realpath "${1}")
+  dst=$(get_realpath "${2}")
+  [[ -d "${dst}" ]] && dst="${dst}/${src##*/}"
+  #some sanity check
+  [[ -z "${src}" ]] && echo "$1 does not exist" && return 1
+  [[ -z "${dst}" ]] && echo "$2 does not exist" && return 1
+  #check if we are not trying to copy to the same file
+  [[ "${src}" == "${dst}" ]] && echo "$dst==$src, not copying" && return 0
+  ok=0
+  [[ -z "${maxCopyTries}" ]] && maxCopyTries=10
+
+  echo "paranoid copy started: $src -> $dst"
+  for (( i=1 ; i<=maxCopyTries ; i++ )) ; do
+
+    echo "...attempt $i of $maxCopyTries"
+    rm -f "$dst"
+    cp "$src" "$dst"
+
+    cmp -s "$src" "$dst"
+    if [ $? == 0 ] ; then
+      ok=1
+      break
+    fi
+
+  done
+
+  if [[ "$ok" == 1 ]] ; then
+    echo "paranoid copy OK after $i attempt(s): $src -> $dst"
+    return 0
+  fi
+
+  echo "paranoid copy FAILED after $maxCopyTries attempt(s): $src -> $dst"
+  return 1
+)
+
+generPWD(){ 
+  #
+  # generate semirandom pwd using 2 keys 
+  # Example usage:
+  # generPWD  myserviceaccount10 key11
+  key0=$1
+  key1=$2
+  heslo0=`md5sum <<< "$key0 $key1" | cut -c 1-16`
+  heslo=`echo $heslo0 | cut -c 1-8| awk '{print toupper($0)}'`
+  heslo=$heslo`echo $heslo0 | cut -c 8-15| awk '{print tolower($0)}'`%
+  echo $heslo;
+}
+
+#this makes debugging easier:
+#executes the command given as an argument in this environment
+#use case:
+#  bashdb utilities.sh summarizeLogs * */*
+[[ $# != 0 ]] && eval "$@"