1#!/bin/bash 2# 3# Runs a CUJ multiple times and generates KPIs. 4 5set -E 6function trap_error() { 7 local return_value=$? 8 local line_no=$1 9 echo "Error at line ${line_no}: \"${BASH_COMMAND}\"" | tee -a ${OUTPUT_DIR}/fatal_log.txt 10 echo "Desc output ${desc}" | tee -a ${OUTPUT_DIR}/fatal_log.txt 11 pkill -P $$ 12} 13trap 'trap_error $LINENO' ERR 14 15function clean_exit() { 16 adb wait-for-device 17 adb logcat -G 64K 18 adb shell setprop persist.logd.logpersistd \\\"\\\" 19 adb shell setprop persist.logd.logpersistd.enable false 20} 21trap 'clean_exit' EXIT 22 23readonly PSI_MONITOR_REPO_DIR="packages/services/Car/tools/psi_monitor" 24readonly ADB_WAIT_FOR_DEVICE_SCRIPT="adb_wait_for_device.sh" 25readonly RUN_CUJ_SCRIPT="run_cuj.sh" 26readonly GENERATE_KPI_STATS_SCRIPT="generate_kpi_stats.py" 27function setup_local_dir() { 28 if [[ -z ${ANDROID_BUILD_TOP} ]]; then 29 readonly LOCAL_SCRIPT_DIR=$(dirname ${0}) 30 else 31 readonly LOCAL_SCRIPT_DIR=${ANDROID_BUILD_TOP}/${PSI_MONITOR_REPO_DIR} 32 fi 33 if [[ ! -f ${LOCAL_SCRIPT_DIR}/${ADB_WAIT_FOR_DEVICE_SCRIPT} ]]; then 34 ehco -e "${ADB_WAIT_FOR_DEVICE_SCRIPT} script not found in ${LOCAL_SCRIPT_DIR}" >&2 35 exit 1 36 fi 37 if [[ ! -f ${LOCAL_SCRIPT_DIR}/${RUN_CUJ_SCRIPT} ]]; then 38 echo -e "PSI monitor script ${RUN_CUJ_SCRIPT} not found in ${LOCAL_SCRIPT_DIR}" >&2 39 exit 1 40 fi 41 if [[ ! -f ${LOCAL_SCRIPT_DIR}/${GENERATE_KPI_STATS_SCRIPT} ]]; then 42 echo -e "KPI stats script ${GENERATE_KPI_STATS_SCRIPT} not found in ${LOCAL_SCRIPT_DIR}" >&2 43 exit 1 44 fi 45} 46 47setup_local_dir 48source ${LOCAL_SCRIPT_DIR}/adb_wait_for_device.sh 49 50OUTPUT_DIR=${PWD}/out 51CUJ_RUNS_OUTPUT_DIR_PREFIX="" 52RUN_PREFIX=run 53RUN_CUJ_ARGS="" 54ITERATIONS=0 55TOTAL_FAILURE_RETRIES=0 56KPI_CSV_FILE_LOCATION="processed/kpis.csv" 57 58function print_log() { 59 echo -e "[$(date +'%Y-%m-%d %H:%M:%S%z')] $@" | tee -a ${OUTPUT_DIR}/log.txt 60} 61 62function err() { 63 echo -e "[$(date +'%Y-%m-%d %H:%M:%S%z')] $@" >&2 | tee -a ${OUTPUT_DIR}/err.txt 64} 65 66function usage() { 67 echo "Runs a CUJ multiple times and generates KPIs." 68 echo "Usage: multi_run_cuj.sh [-o|--out_dir=<output_dir>]"\ 69 "--run_cuj_args <run_cuj.sh args> [-i|--iterations] <Number of iterations>"\ 70 "[--retry_any_failure] <Number of times to retry failures>" 71 72 echo "-o|--out_dir: Location to output the psi dump, CSV files, and KPIs" 73 echo "--run_cuj_args: Args to pass down to the run_cuj.sh script except output dir argument" 74 echo "-i|--iterations: Number of iterations to run the CUJ with run_cuj.sh script" 75 echo "--retry_any_failure: Number of times to retry any failure" 76 echo -e "\nExample commands:" 77 echo -e "\t1. ./multi_run_cuj.sh -o boot_with_threshold_met_app_launch --run_cuj_args \"-d 90 "\ 78 "-g 20 -t 20 --bootup_with_app_launch threshold_met\" -i 20 --retry_any_failure 5" 79 echo -e "\t1. ./multi_run_cuj.sh -o app_launch --run_cuj_args \"-d 90 "\ 80 "-g 20 -t 5 -a com.android.contacts --app_startup_times 10\" -i 20" 81} 82 83function usage_err() { 84 err "$@\n" 85 usage 86} 87 88function parse_arguments() { 89 while [[ $# > 0 ]]; do 90 key="$1" 91 case $key in 92 -h|--help) 93 usage 94 exit 1;; 95 -o|--out_dir) 96 OUTPUT_DIR=${2} 97 shift;; 98 --run_cuj_args) 99 RUN_CUJ_ARGS=${2} 100 shift;; 101 -i|--iterations) 102 ITERATIONS=${2} 103 shift;; 104 --retry_any_failure) 105 TOTAL_FAILURE_RETRIES=${2} 106 shift;; 107 *) 108 echo "${0}: Invalid option ${1}" 109 usage 110 exit 1;; 111 esac 112 shift # past argument or value 113 done 114} 115 116function check_arguments() { 117 readonly OUTPUT_DIR 118 mkdir -p ${OUTPUT_DIR} 119 if [[ ! -d ${OUTPUT_DIR} ]]; then 120 err "Out dir ${OUTPUT_DIR} does not exist" 121 exit 1 122 fi 123 124 readonly RUN_CUJ_ARGS 125 if [[ -z ${RUN_CUJ_ARGS} ]]; then 126 echo "Must provide run_cuj.sh arguments with the --run_cuj_args option" 127 exit 1 128 fi 129 130 readonly ITERATIONS 131 if [[ ${ITERATIONS} != +([[:digit:]]) || ${ITERATIONS} -le 0 || ${ITERATIONS} -gt 100 ]]; then 132 echo "Iterations ${ITERATIONS} is not a valid number. The values should be between 0 and 100" 133 fi 134 135 readonly TOTAL_FAILURE_RETRIES 136 if [[ ${TOTAL_FAILURE_RETRIES} != +([[:digit:]]) ]]; then 137 echo "Total failure retries ${TOTAL_FAILURE_RETRIES} is not a valid number" 138 fi 139} 140 141function setup() { 142 readonly CUJ_RUNS_OUTPUT_DIR_PREFIX=${OUTPUT_DIR}/"cuj_out/run_" 143 adb shell setprop persist.logd.logpersistd logcatd 144 adb shell setprop persist.logd.logpersistd.enable true 145 # Capture a bugreport for tracking the build details. 146 # adb bugreport ${OUTPUT_DIR} 147 adb_wait_with_recovery 148 fetch_device_info 149} 150 151function run_cuj_iterations() { 152 total_failure_count=0 153 for i in $(seq 1 ${ITERATIONS}); do 154 adb_wait_with_recovery 155 out_dir=${CUJ_RUNS_OUTPUT_DIR_PREFIX}${i} 156 print_log "\n\nRunning iteration ${i}. Output dir ${out_dir}" 157 while : ; do 158 mkdir -p ${out_dir} 159 (./run_cuj.sh ${RUN_CUJ_ARGS} -o ${out_dir} --no_show_plots) 160 if [ $? -eq 0 ]; then 161 break; 162 else 163 # Capture a bugreport for investigation purposes. 164 adb bugreport ${out_dir} 165 mv ${out_dir} ${out_dir}_failure_${total_failure_count} 166 pkill -P $$ 167 if [[ ${total_failure_count} -ge ${TOTAL_FAILURE_RETRIES} ]]; then 168 print_log "CUJ run ${i} failed... Total remaining retries is"\ 169 "((${TOTAL_FAILURE_RETRIES}-${total_failure_count})). Exiting"; 170 return; 171 else 172 print_log "CUJ run ${i} failed... Total remaining retries is"\ 173 "((${TOTAL_FAILURE_RETRIES}-${total_failure_count})). Retrying current failure" 174 total_failure_count=$((${total_failure_count}+1)) 175 fi 176 fi 177 done 178 done 179} 180 181function generate_kpi_stats() { 182 kpi_csv_files="" 183 for i in $(seq 1 ${ITERATIONS}); do 184 kpi_csv_file=${CUJ_RUNS_OUTPUT_DIR_PREFIX}${i}/${KPI_CSV_FILE_LOCATION} 185 if [[ ! -f ${kpi_csv_file} ]]; then 186 err "Expected '${out_dir}' file doesn't exist. Skipping run iteration ${i}" 187 continue 188 fi 189 if [ -z ${kpi_csv_files} ]; then 190 kpi_csv_files=${kpi_csv_file} 191 else 192 kpi_csv_files+=",${kpi_csv_file}" 193 fi 194 done 195 196 echo -e "#!/bin/bash\n\n${LOCAL_SCRIPT_DIR}/${GENERATE_KPI_STATS_SCRIPT}"\ 197 "--kpi_csv_files ${kpi_csv_files} --run_id_re \".*\/run_(\d+)\/processed\/kpis.csv\""\ 198 "--out_file ${OUTPUT_DIR}/kpi_stats.csv" > ${OUTPUT_DIR}/generate_kpi_stats_cmd.sh 199 chmod +x ${OUTPUT_DIR}/generate_kpi_stats_cmd.sh 200 201 ${LOCAL_SCRIPT_DIR}/${GENERATE_KPI_STATS_SCRIPT} --kpi_csv_files "${kpi_csv_files}"\ 202 --run_id_re ".*\/run_(\d+)\/processed\/kpis.csv" --out_file "${OUTPUT_DIR}/kpi_stats.csv" 203 204# Sample command for manual run: 205# (export IN_DIR=~/post_boot/cuj_out; export KPIS_CSV=processed/kpis.csv; \ 206# ~/android/main/packages/services/Car/tools/psi_monitor/generate_kpi_stats.py --kpi_csv_files \ 207# ${IN_DIR}/run_1/${KPIS_CSV},${IN_DIR}/run_2/${KPIS_CSV},${IN_DIR}/run_3/${KPIS_CSV},\ 208# ${IN_DIR}/run_4/${KPIS_CSV},${IN_DIR}/run_5/${KPIS_CSV},${IN_DIR}/run_6/${KPIS_CSV},\ 209# ${IN_DIR}/run_7/${KPIS_CSV},${IN_DIR}/run_8/${KPIS_CSV},${IN_DIR}/run_9/${KPIS_CSV},\ 210# ${IN_DIR}/run_10/${KPIS_CSV},${IN_DIR}/run_11/${KPIS_CSV},${IN_DIR}/run_12/${KPIS_CSV},\ 211# ${IN_DIR}/run_13/${KPIS_CSV},${IN_DIR}/run_14/${KPIS_CSV},${IN_DIR}/run_15/${KPIS_CSV},\ 212# ${IN_DIR}/run_16/${KPIS_CSV},${IN_DIR}/run_17/${KPIS_CSV},${IN_DIR}/run_18/${KPIS_CSV},\ 213# ${IN_DIR}/run_19/${KPIS_CSV} --run_id_re ".*\/run_(\d+)\/processed\/kpis.csv" \ 214# --out_file ${IN_DIR}/kpi_stats.csv) 215} 216 217function main() { 218 set -e 219 parse_arguments "$@" 220 check_arguments 221 222 setup 223 224 desc=$(${LOCAL_SCRIPT_DIR}/${RUN_CUJ_SCRIPT} ${RUN_CUJ_ARGS} --print_cuj_desc 2>&1) 225 print_log "Running CUJ '${desc}' for ${ITERATIONS} times" 226 227 set +e 228 run_cuj_iterations 229 set -e 230 231 generate_kpi_stats 232 233 adb shell setprop persist.logd.logpersistd "" 234 adb shell setprop persist.logd.logpersistd.enable false 235} 236 237main "$@" 238