buildstats.sh 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. #!/bin/bash
  2. #
  3. # Copyright (c) 2011, Intel Corporation.
  4. #
  5. # SPDX-License-Identifier: GPL-2.0-or-later
  6. #
  7. # DESCRIPTION
  8. # Given 'buildstats' data (generate by bitbake when setting
  9. # USER_CLASSES ?= "buildstats" on local.conf), task names and a stats values
  10. # (these are the ones preset on the buildstats files), outputs
  11. # '<task> <recipe> <value_1> <value_2> ... <value_n>'. The units are the ones
  12. # defined at buildstats, which in turn takes data from /proc/[pid] files
  13. #
  14. # Some useful pipelines
  15. #
  16. # 1. Tasks with largest stime (Amount of time that this process has been scheduled
  17. # in kernel mode) values
  18. # $ buildstats.sh -b <buildstats> -s stime | sort -k3 -n -r | head
  19. #
  20. # 2. Min, max, sum utime (Amount of time that this process has been scheduled
  21. # in user mode) per task (in needs GNU datamash)
  22. # $ buildstats.sh -b <buildstats> -s utime | datamash -t' ' -g1 min 3 max 3 sum 3 | sort -k4 -n -r
  23. #
  24. # AUTHORS
  25. # Leonardo Sandoval <leonardo.sandoval.gonzalez@linux.intel.com>
  26. #
  27. # Stats, by type
  28. TIME="utime:stime:cutime:cstime"
  29. IO="IO wchar:IO write_bytes:IO syscr:IO read_bytes:IO rchar:IO syscw:IO cancelled_write_bytes"
  30. RUSAGE="rusage ru_utime:rusage ru_stime:rusage ru_maxrss:rusage ru_minflt:rusage ru_majflt:\
  31. rusage ru_inblock:rusage ru_oublock:rusage ru_nvcsw:rusage ru_nivcsw"
  32. CHILD_RUSAGE="Child rusage ru_utime:Child rusage ru_stime:Child rusage ru_maxrss:Child rusage ru_minflt:\
  33. Child rusage ru_majflt:Child rusage ru_inblock:Child rusage ru_oublock:Child rusage ru_nvcsw:\
  34. Child rusage ru_nivcsw"
  35. BS_DIR="tmp/buildstats"
  36. RECIPE=""
  37. TASKS="compile:configure:fetch:install:patch:populate_lic:populate_sysroot:unpack"
  38. STATS="$TIME"
  39. ACCUMULATE=""
  40. HEADER="" # No header by default
  41. function usage {
  42. CMD=$(basename $0)
  43. cat <<EOM
  44. Usage: $CMD [-b buildstats_dir] [-t do_task]
  45. -b buildstats The path where the folder resides
  46. (default: "$BS_DIR")
  47. -r recipe The recipe to be computed
  48. -t tasks The tasks to be computed
  49. (default: "$TASKS")
  50. -s stats The stats to be matched. Options: TIME, IO, RUSAGE, CHILD_RUSAGE
  51. or any other defined buildstat separated by colons, i.e. stime:utime
  52. (default: "$STATS")
  53. Default stat sets:
  54. TIME=$TIME
  55. IO=$IO
  56. RUSAGE=$RUSAGE
  57. CHILD_RUSAGE=$CHILD_RUSAGE
  58. -a Accumulate all stats values for found recipes
  59. -h Display this help message
  60. EOM
  61. }
  62. # Parse and validate arguments
  63. while getopts "b:r:t:s:aHh" OPT; do
  64. case $OPT in
  65. b)
  66. BS_DIR="$OPTARG"
  67. ;;
  68. r)
  69. RECIPE="$OPTARG"
  70. ;;
  71. t)
  72. TASKS="$OPTARG"
  73. ;;
  74. s)
  75. STATS="$OPTARG"
  76. ;;
  77. a)
  78. ACCUMULATE="y"
  79. ;;
  80. H)
  81. HEADER="y"
  82. ;;
  83. h)
  84. usage
  85. exit 0
  86. ;;
  87. *)
  88. usage
  89. exit 1
  90. ;;
  91. esac
  92. done
  93. # Ensure the buildstats folder exists
  94. if [ ! -d "$BS_DIR" ]; then
  95. echo "ERROR: $BS_DIR does not exist"
  96. usage
  97. exit 1
  98. fi
  99. stats=""
  100. IFS=":"
  101. for stat in ${STATS}; do
  102. case $stat in
  103. TIME)
  104. stats="${stats}:${TIME}"
  105. ;;
  106. IO)
  107. stats="${stats}:${IO}"
  108. ;;
  109. RUSAGE)
  110. stats="${stats}:${RUSAGE}"
  111. ;;
  112. CHILD_RUSAGE)
  113. stats="${stats}:${CHILD_RUSAGE}"
  114. ;;
  115. *)
  116. stats="${STATS}"
  117. ;;
  118. esac
  119. done
  120. # remove possible colon at the beginning
  121. stats="$(echo "$stats" | sed -e 's/^://1')"
  122. # Provide a header if required by the user
  123. if [ -n "$HEADER" ] ; then
  124. if [ -n "$ACCUMULATE" ]; then
  125. echo "task:recipe:accumulated(${stats//:/;})"
  126. else
  127. echo "task:recipe:$stats"
  128. fi
  129. fi
  130. for task in ${TASKS}; do
  131. task="do_${task}"
  132. for file in $(find ${BS_DIR} -type f -path *${RECIPE}*/${task} | awk 'BEGIN{ ORS=""; OFS=":" } { print $0,"" }'); do
  133. recipe="$(basename $(dirname $file))"
  134. times=""
  135. for stat in ${stats}; do
  136. [ -z "$stat" ] && { echo "empty stats"; }
  137. time=$(sed -n -e "s/^\($stat\): \\(.*\\)/\\2/p" $file)
  138. # in case the stat is not present, set the value as NA
  139. [ -z "$time" ] && { time="NA"; }
  140. # Append it to times
  141. if [ -z "$times" ]; then
  142. times="${time}"
  143. else
  144. times="${times} ${time}"
  145. fi
  146. done
  147. if [ -n "$ACCUMULATE" ]; then
  148. IFS=' '; valuesarray=(${times}); IFS=':'
  149. times=0
  150. for value in "${valuesarray[@]}"; do
  151. [ "$value" == "NA" ] && { echo "ERROR: stat is not present."; usage; exit 1; }
  152. times=$(( $times + $value ))
  153. done
  154. fi
  155. echo "${task} ${recipe} ${times}"
  156. done
  157. done