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* 28*# Stats, by type 29*TIME="utime:stime:cutime:cstime" 30*IO="IO wchar:IO write_bytes:IO syscr:IO read_bytes:IO rchar:IO syscw:IO cancelled_write_bytes" 31*RUSAGE="rusage ru_utime:rusage ru_stime:rusage ru_maxrss:rusage ru_minflt:rusage ru_majflt:\ 32*rusage ru_inblock:rusage ru_oublock:rusage ru_nvcsw:rusage ru_nivcsw" 33* 34*CHILD_RUSAGE="Child rusage ru_utime:Child rusage ru_stime:Child rusage ru_maxrss:Child rusage ru_minflt:\ 35*Child rusage ru_majflt:Child rusage ru_inblock:Child rusage ru_oublock:Child rusage ru_nvcsw:\ 36*Child rusage ru_nivcsw" 37* 38*BS_DIR="tmp/buildstats" 39*RECIPE="" 40*TASKS="compile:configure:fetch:install:patch:populate_lic:populate_sysroot:unpack" 41*STATS="$TIME" 42*ACCUMULATE="" 43*HEADER="" # No header by default 44* 45*function usage { 46*CMD=$(basename $0) 47*cat <<EOM 48*Usage: $CMD [-b buildstats_dir] [-t do_task] 49* -b buildstats The path where the folder resides 50* (default: "$BS_DIR") 51* -r recipe The recipe to be computed 52* -t tasks The tasks to be computed 53* (default: "$TASKS") 54* -s stats The stats to be matched. Options: TIME, IO, RUSAGE, CHILD_RUSAGE 55* or any other defined buildstat separated by colons, i.e. stime:utime 56* (default: "$STATS") 57* Default stat sets: 58* TIME=$TIME 59* IO=$IO 60* RUSAGE=$RUSAGE 61* CHILD_RUSAGE=$CHILD_RUSAGE 62* -a Accumulate all stats values for found recipes 63* -h Display this help message 64*EOM 65*} 66* 67*# Parse and validate arguments 68*while getopts "b:r:t:s:aHh" OPT; do 69* case $OPT in 70* b) 71* BS_DIR="$OPTARG" 72* ;; 73* r) 74* RECIPE="$OPTARG" 75* ;; 76* t) 77* TASKS="$OPTARG" 78* ;; 79* s) 80* STATS="$OPTARG" 81* ;; 82* a) 83* ACCUMULATE="y" 84* ;; 85* H) 86* HEADER="y" 87* ;; 88* h) 89* usage 90* exit 0 91* ;; 92* *) 93* usage 94* exit 1 95* ;; 96* esac 97*done 98* 99*# Ensure the buildstats folder exists 100*if [ ! -d "$BS_DIR" ]; then 101* echo "ERROR: $BS_DIR does not exist" 102* usage 103* exit 1 104*fi 105* 106*stats="" 107*IFS=":" 108*for stat in ${STATS}; do 109* case $stat in 110* TIME) 111* stats="${stats}:${TIME}" 112* ;; 113* IO) 114* stats="${stats}:${IO}" 115* ;; 116* RUSAGE) 117* stats="${stats}:${RUSAGE}" 118* ;; 119* CHILD_RUSAGE) 120* stats="${stats}:${CHILD_RUSAGE}" 121* ;; 122* *) 123* stats="${STATS}" 124* ;; 125* esac 126*done 127* 128*# remove possible colon at the beginning 129*stats="$(echo "$stats" | sed -e 's/^://1')" 130* 131*# Provide a header if required by the user 132*if [ -n "$HEADER" ] ; then 133* if [ -n "$ACCUMULATE" ]; then 134* echo "task:recipe:accumulated(${stats//:/;})" 135* else 136* echo "task:recipe:$stats" 137* fi 138*fi 139* 140*for task in ${TASKS}; do 141* task="do_${task}" 142* for file in $(find ${BS_DIR} -type f -path *${RECIPE}*/${task} | awk 'BEGIN{ ORS=""; OFS=":" } { print $0,"" }'); do 143* recipe="$(basename $(dirname $file))" 144* times="" 145* for stat in ${stats}; do 146* [ -z "$stat" ] && { echo "empty stats"; } 147* time=$(sed -n -e "s/^\($stat\): \\(.*\\)/\\2/p" $file) 148* # in case the stat is not present, set the value as NA 149* [ -z "$time" ] && { time="NA"; } 150* # Append it to times 151* if [ -z "$times" ]; then 152* times="${time}" 153* else 154* times="${times} ${time}" 155* fi 156* done 157* if [ -n "$ACCUMULATE" ]; then 158* IFS=' '; valuesarray=(${times}); IFS=':' 159* times=0 160* for value in "${valuesarray[@]}"; do 161* [ "$value" == "NA" ] && { echo "ERROR: stat is not present."; usage; exit 1; } 162* times=$(( $times + $value )) 163* done 164* fi 165* echo "${task} ${recipe} ${times}" 166* done 167*done 168*