]> git.uio.no Git - u/mrichter/AliRoot.git/blob - PWGPP/scripts/utilities.sh
fix: parse the delimiter correctly
[u/mrichter/AliRoot.git] / PWGPP / scripts / utilities.sh
1 #library of useful PWGPP related bash functions
2 #it REQUIRES BASH 4 !!!!
3 #blame: Mikolaj Krzewicki, mkrzewic@cern.ch
4
5 if [ ${BASH_VERSINFO} -lt 4 ]; then
6   echo "bash version >= 4 needed, you have ${BASH_VERSION}, exiting..."
7   exit 1
8 fi
9
10 PWGPP_runMap="
11 2010 108350 139517
12 2011 140441 170593
13 2012 171590 193766
14 2013 194308 199146
15 2014 202369 206695
16 2015 999999 999999
17 2016 999999 999999
18 "
19
20 parseConfig()
21 {
22   local args=("$@")
23
24   #first, check if the config file is configured
25   #is yes - source it so that other options can override it
26   #
27   #recommended way of using (at the beginning of your funcion/script):
28   #  if ! parseConfig "${@}"; then return; fi
29   
30   local opt=""
31   
32   #first check if we will need to decode spaces
33   local encodedSpaces=""
34   for opt in "${args[@]}"; do
35     [[ "${opt}" =~ encodedSpaces=.* ]] \
36       && encodedSpaces=1 \
37       && break
38   done
39
40   #then look for a configFile (if any)
41   for opt in "${args[@]}"; do
42     if [[ ${opt} =~ configFile=.* ]]; then
43       eval "${opt}"
44       [[ ! -f ${configFile} ]] \
45         && echo "configFile ${configFile} not found, exiting..." \
46         && return 1
47       echo "using config file: ${configFile}"
48       source "${configFile}"
49       break
50     fi
51   done
52
53   #then, parse the options as they override the options from configFile
54   for opt in "${args[@]}"; do
55     [[ -n ${encodedSpaces} ]] && opt="$(decSpaces ${opt})"
56     if [[ ! "${opt}" =~ .*=.* ]]; then
57       echo "badly formatted option ${var}, should be: option=value, stopping..."
58       return 1
59     fi
60     local var="${opt%%=*}"
61     local value="${opt#*=}"
62     #echo "${var}=${value}"
63     export ${var}="${value}"
64   done
65   return 0
66 }
67
68 guessRunData()
69 {
70   #guess the period from the path, pick the rightmost one
71   period=""
72   runNumber=""
73   year=""
74   pass=""
75   legoTrainRunNumber=""
76   dataType=""
77   originalPass=""
78   originalPeriod=""
79   anchorYear=""
80   shortRunNumber=""
81
82   local oldIFS=${IFS}
83   local IFS="/"
84   declare -a path=( $1 )
85   IFS="${oldIFS}"
86   local dirDepth=$(( ${#path[*]}-1 ))
87   for ((x=${dirDepth};x>=0;x--)); do
88
89     [[ $((x-1)) -ge 0 ]] && local fieldPrev=${path[$((x-1))]}
90     local field=${path[${x}]}
91     local fieldNext=${path[$((x+1))]}
92
93     [[ ${field} =~ ^[0-9]*$ && ${fieldNext} =~ (.*\.zip$|.*\.root$) ]] && legoTrainRunNumber=${field}
94     [[ -n ${legoTrainRunNumber} && -z ${pass} ]] && pass=${fieldPrev}
95     [[ ${field} =~ ^LHC[0-9][0-9][a-z].*$ ]] && period=${field%_*} && originalPeriod=${field}
96     [[ ${field} =~ ^000[0-9][0-9][0-9][0-9][0-9][0-9]$ ]] && runNumber=${field#000}
97     [[ ${field} =~ ^[0-9][0-9][0-9][0-9][0-9][0-9]$ ]] && shortRunNumber=${field}
98     [[ ${field} =~ ^20[0-9][0-9]$ ]] && year=${field}
99     [[ ${field} =~ ^(^sim$|^data$) ]] && dataType=${field}
100   done
101
102   originalPass=${pass}
103   [[ -n ${shortRunNumber} && "${legoTrainRunNumber}" =~ ${shortRunNumber} ]] && legoTrainRunNumber=""
104   [[ -z ${legoTrainRunNumber} ]] && pass=${path[$((dirDepth-1))]}
105   [[ "${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
106   [[ -n ${legoTrainRunNumber} ]] && pass+="_lego${legoTrainRunNumber}"
107   
108   #modify the OCDB: set the year
109   if [[ ${dataType} =~ sim ]]; then 
110     anchorYear=$(run2year $runNumber)
111     if [[ -z "${anchorYear}" ]]; then
112       echo "WARNING: anchorYear not available for this production: ${originalPeriod}, runNumber: ${runNumber}. Cannot set the OCDB."
113       return 1
114     fi
115     ocdbStorage=$(setYear ${anchorYear} ${ocdbStorage})
116   else
117     ocdbStorage=$(setYear ${year} ${ocdbStorage})
118   fi
119
120   #if [[ -z ${dataType} || -z ${year} || -z ${period} || -z ${runNumber}} || -z ${pass} ]];
121   if [[ -z ${runNumber} ]]
122   then
123     #error condition
124     return 1
125   fi
126   
127   #ALL OK
128   return 0
129 }
130
131 setYear()
132 {
133   #set the year
134   #  ${1} - year to be set
135   #  ${2} - where to set the year
136   local year1=$(guessYear ${1})
137   local year2=$(guessYear ${2})
138   local path=${2}
139   [[ ${year1} -ne ${year2} && -n ${year2} && -n ${year1} ]] && path=${2/\/${year2}\//\/${year1}\/}
140   echo ${path}
141   return 0
142 }
143
144 guessYear()
145 {
146   #guess the year from the path, pick the rightmost one
147   local IFS="/"
148   declare -a pathArray=( ${1} )
149   local field
150   local year
151   for field in ${pathArray[@]}; do
152     [[ ${field} =~ ^20[0-9][0-9]$ ]] && year=${field}
153   done
154   echo ${year}
155   return 0
156 }
157
158 run2year()
159 {
160   #for a given run print the year.
161   #the run-year table is ${PWGPP_runMap} (a string)
162   #one line per year, format: year runMin runMax
163   local run=$1
164   [[ -z ${run} ]] && return 1
165   local year=""
166   local runMin=""
167   local runMax=""
168   while read year runMin runMax; do
169     [[ -z ${year} || -z ${runMin} || -z ${runMax} ]] && continue
170     [[ ${run} -ge ${runMin} && ${run} -le ${runMax} ]] && echo ${year} && break
171   done < <(echo "${PWGPP_runMap}")
172   return 0
173 }
174
175 hostInfo(){
176 #
177 # Hallo world -  Print AliRoot/Root/Alien system info
178 #
179
180 #
181 # HOST info
182 #
183     echo --------------------------------------
184         echo 
185         echo HOSTINFO
186         echo 
187         echo HOSTINFO HOSTNAME"      "$HOSTNAME
188         echo HOSTINFO DATE"          "`date`
189         echo HOSTINFO gccpath"       "`which gcc` 
190         echo HOSTINFO gcc version"   "`gcc --version | grep gcc`
191         echo --------------------------------------    
192
193 #
194 # ROOT info
195 #
196         echo --------------------------------------
197         echo
198         echo ROOTINFO
199         echo 
200         echo ROOTINFO ROOT"           "`which root`
201         echo ROOTINFO VERSION"        "`root-config --version`
202         echo 
203         echo --------------------------------------
204
205
206 #
207 # ALIROOT info
208 #
209         echo --------------------------------------
210         echo
211         echo ALIROOTINFO
212         echo 
213         echo ALIROOTINFO ALIROOT"        "`which aliroot`
214         echo ALIROOTINFO VERSION"        "`echo $ALICE_LEVEL`
215         echo ALIROOTINFO TARGET"         "`echo $ALICE_TARGET`
216         echo 
217         echo --------------------------------------
218
219 #
220 # Alien info
221 #
222 #echo --------------------------------------
223 #echo
224 #echo ALIENINFO
225 #for a in `alien --printenv`; do echo ALIENINFO $a; done 
226 #echo
227 #echo --------------------------------------
228
229 #
230 # Local Info
231 #
232         echo PWD `pwd`
233         echo Dir 
234         ls -al
235         echo
236         echo
237         echo
238   
239   return 0
240 }
241
242 summarizeLogs()
243 {
244   #validate and summarize the status of logs
245   #input is a list of logs, or a glob:
246   #example (summarizes logs in current and subdirs):
247   #  summarizeLogs * */*
248   #exit code 1 if some logs are not validated
249
250   #print a summary of logs
251   local input
252   local file=""
253   declare -A files
254   input=("${@}")
255   [[ -z "${input[*]}" ]] && input=( "${PWD}"/* )
256   
257   #double inclusion protection+make full paths
258   for file in "${input[@]}"; do
259     [[ ! "${file}" =~ ^/ ]] && file="${PWD}/${file}"
260     files["${file}"]="${file}"
261   done
262
263   local logFiles
264   logFiles=".*log|stdout|stderr"
265
266   #check logs
267   local logStatus=0
268   local errorSummary=""
269   local validationStatus=""
270   declare -A coreFiles
271   for file in "${files[@]}"; do
272     [[ ! -f ${file} ]] && continue
273     #keep track of core files for later processing
274     [[ "${file}" =~ core$ ]] && coreFiles[${file}]="${file}" && continue
275     [[ ! "${file}" =~ ${logFiles} ]] && continue
276     errorSummary=$(validateLog ${file})
277     validationStatus=$?
278     [[ validationStatus -ne 0 ]] && logStatus=1
279     if [[ ${validationStatus} -eq 0 ]]; then 
280       #in pretend mode randomly report an error in rec.log some cases
281       echo "${file} OK"
282     elif [[ ${validationStatus} -eq 1 ]]; then
283       echo "${file} BAD ${errorSummary}"
284     elif [[ ${validationStatus} -eq 2 ]]; then
285       echo "${file} OK MWAH ${errorSummary}"
286     fi
287   done
288
289   #report core files
290   for x in "${coreFiles[@]}"; do
291     echo ${x}
292     chmod 644 ${x}
293     #gdb --batch --quiet -ex "bt" -ex "quit" aliroot ${x} > stacktrace_${x//\//_}.log
294     gdb --batch --quiet -ex "bt" -ex "quit" aliroot ${x} > stacktrace.log
295     local nLines[2]
296     #nLines=($(wc -l stacktrace_${x//\//_}.log))
297     nLines=($(wc -l stacktrace.log))
298     if [[ ${nLines[0]} -eq 0 ]]; then
299       #rm stacktrace_${x//\//_}.log
300       rm stacktrace.log
301     else
302       logStatus=1
303       echo "${x%/*}/stacktrace.log"
304     fi
305   done
306
307   return ${logStatus}
308 }
309
310 validateLog()
311 {
312   #validate one log file
313   #input is path to log file
314   #output an error summary on stdout
315   #exit code is 0 if validated, 1 otherwise
316   log=${1}
317   errorConditions=(
318             'There was a crash'
319             'floating'
320             'error while loading shared libraries'
321             'std::bad_alloc'
322             's_err_syswatch_'
323             'Thread [0-9]* (Thread'
324             'AliFatal'
325             '\.C.*error:.*\.h: No such file'
326             'segmentation'
327             'Segmentation fault'
328             'Interpreter error recovered'
329             ': command not found'
330             ': comando non trovato'
331             'core dumped'
332   )
333
334   warningConditions=(
335             'This is serious'
336   )
337
338   local logStatus=0
339   local errorSummary=""
340   local warningSummary=""
341   local errorCondition=""
342   for errorCondition in "${errorConditions[@]}"; do
343     local tmp=$(grep -m1 -e "${errorCondition}" ${log})
344     local error=""
345     [[ -n ${tmp} ]] && error=" : ${errorCondition}"
346     errorSummary+=${error}
347   done
348
349   local warningCondition=""
350   for warningCondition in "${warningConditions[@]}"; do
351     local tmp=$(grep -m1 -e "${warningCondition}" ${log})
352     local warning=""
353     [[ -n ${tmp} ]] && warning=" : ${warningCondition}"
354     warningSummary+=${warning}
355   done
356
357   if [[ -n ${errorSummary} ]]; then 
358     echo "${errorSummary}"
359     return 1
360   fi
361
362   if [[ -n ${warningSummary} ]]; then
363     echo "${warningSummary}"
364     return 2
365   fi
366
367   return 0
368 }
369
370 mergeSysLogs()
371 {
372   if [[ $# -lt 2 ]]; then
373     echo 'merge syslogs to an output file'
374     echo 'usage:'
375     echo 'mergeSysLogs outputFile inputFile1 inputFile2 ...'
376     return 0
377   fi
378   local outputFile
379   local inputFiles
380   local i
381   local x
382   local runNumber
383   outputFile=${1}
384   shift
385   inputFiles="$@"
386   i=0
387   if ! ls -1 ${inputFiles} &>/dev/null; then echo "the files dont exist!: ${inputFiles}"; return 1; fi
388   while read x; do 
389     runNumber=$(guessRunNumber ${x})
390     [[ -z ${runNumber} ]] && echo "run number cannot be guessed for ${x}" && continue
391     awk -v run=${runNumber} -v i=${i} 'NR > 1 {print run" "$0} NR==1 && i==0 {print "run/I:"$0}' ${x}
392     (( i++ ))
393   done < <(ls -1 ${inputFiles}) > ${outputFile}
394   return 0
395 }
396
397 stackTraceTree()
398 {
399   if [[ $# -lt 1 ]]; then
400     echo 'make stacktrace processing  in case of standard root crash log'
401     echo 'input is a (list of) text files with the stack trace (either gdb aoutput'
402     echo 'produced with e.g. gdb --batch --quiet -ex "bt" -ex "quit" aliroot core,'
403     echo 'or the root crash log), output is a TTree formatted table.'
404     echo 'example usage:'
405     echo 'benchmark.sh stackTraceTree /foo/*/rec.log'
406     echo 'benchmark.sh stackTraceTree $(cat file.list)'
407     echo 'benchmark.sh stackTraceTree `cat file.list`'
408     return 0
409   fi
410   #cat "${@}" | gawk '
411   gawk '
412        BEGIN { 
413        print "frame/I:method/C:line/C:cpass/I:aliroot/I:file/C";
414                RS="#[0-9]*";
415                aliroot=0;
416                read=1;
417              } 
418       /There was a crash/ {read=1;}
419       /The lines below might hint at the cause of the crash/ {read=0;}
420       read==1 { 
421                if ($3 ~ /Ali*/) aliroot=1; else aliroot=0;
422                gsub("#","",RT); 
423                if ($NF!="" && RT!="" && $3!="") print RT" "$3" "$NF" "0" "aliroot" "FILENAME
424              }
425       ' "${@}" 2>/dev/null
426 }
427
428 plotStackTraceTree()
429 {
430   #plot the stacktrace tree,
431   #first arg    is the text file in the root tree format
432   #second arg   is optional: a plot is written to file instead of screen
433   #third arg    is optional: selection for plotting, default skip G_ stuff
434   local tree=$1
435   local plot=${2:-"crashes.png"}
436   local selection=${3:-'!strstr(method,\"G__\")'}
437   echo $selection
438   [[ ! -f ${tree} ]] && echo "no input" && return 1
439   aliroot -b <<EOF
440 TTree* t=AliSysInfo::MakeTree("${tree}");
441 TCanvas* canvas = new TCanvas("QA crashes","QA crashes",1);
442 t->Draw("method","${selection}","");
443 canvas->SaveAs("${plot}");
444 .q
445 EOF
446   return 0
447 }
448
449 encSpaces() 
450
451   echo "${1// /±@@±}" 
452 }
453
454 decSpaces() 
455
456   echo "${1//±@@±/ }" 
457 }
458
459 get_realpath() 
460 {
461   if [[ $# -lt 1 ]]; then
462     echo "print the full path of a file, like \"readlink -f\" on linux"
463     echo "Usage:"
464     echo "  get_realpath <someFile>"
465     return 0
466   fi
467   if [[ -f "$1" ]]
468   then
469     # file *must* exist
470     if cd "$(echo "${1%/*}")" &>/dev/null
471     then
472       # file *may* not be local
473       # exception is ./file.ext
474       # try 'cd .; cd -;' *works!*
475       local tmppwd="$PWD"
476       cd - &>/dev/null
477     else
478       # file *must* be local
479       local tmppwd="$PWD"
480     fi
481   else
482     # file *cannot* exist
483     return 1 # failure
484   fi
485   # reassemble realpath
486   echo "$tmppwd"/"${1##*/}"
487   return 0 # success
488 }
489
490 printLogStatistics()
491 {
492   #this function processes the summary logs and prints some stats
493   #relies on the summary log format produced by summarizeLogs()
494   # - how many checked logs in total
495   # - number of each type of problem
496   # example usage:
497   #   printLogStatistics */*.log
498   [[ ! -f $1 ]] && return 1
499   echo "log statistics from: ${1%/*}"
500   #cat "${@}" | awk '
501   awk '
502   BEGIN {nOK=0; nCores=0; nStackTraces=0;}
503   /\/core/ {nCores++}
504   /\/stacktrace.log/ {nStackTraces++}
505   /OK/ {nOK++; nLogs++;}
506   /BAD/ {
507     nLogs++
508     err=""
509     write=0
510     for (i=3; i<=NF; i++)
511     { 
512       if ($i ~ /^\:$/) 
513         write=1
514       else
515         write=0
516
517       if (write==0)
518       {
519         if (err=="") err=$i
520         else err=(err FS $i)
521       }
522
523       if (err != "" && (write==1 || i==NF))
524       {
525         sumBAD[err]++
526         err=""
527       }
528     }
529   } 
530   END {
531     print ("number of succesful jobs: " nOK" out of "nLogs )
532     for (key in sumBAD)
533     {
534       print key": "sumBAD[key]
535     }
536     if (nCores>0) print "core files: "nCores", stack traces: "nStackTraces 
537   }
538   ' "${@}"
539 }