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