1#!/bin/sh 2# 3# american fuzzy lop++ - status check tool 4# ---------------------------------------- 5# 6# Originally written by Michal Zalewski 7# 8# Copyright 2015 Google Inc. All rights reserved. 9# Copyright 2019-2022 AFLplusplus Project. All rights reserved. 10# 11# Licensed under the Apache License, Version 2.0 (the "License"); 12# you may not use this file except in compliance with the License. 13# You may obtain a copy of the License at: 14# 15# https://www.apache.org/licenses/LICENSE-2.0 16# 17# This tool summarizes the status of any locally-running synchronized 18# instances of afl-fuzz. 19# 20 21echo "$0 status check tool for afl-fuzz by Michal Zalewski" 22echo 23test "$1" = "-h" -o "$1" = "-hh" && { 24 echo "Usage: $0 [-s] [-d] afl_output_directory" 25 echo 26 echo Options: 27 echo " -s - skip details and output summary results only" 28 echo " -d - include dead fuzzer stats" 29 echo 30 exit 1 31} 32 33unset SUMMARY_ONLY 34unset PROCESS_DEAD 35 36while [ "$1" = "-s" -o "$1" = "-d" ]; do 37 38 if [ "$1" = "-s" ]; then 39 SUMMARY_ONLY=1 40 fi 41 42 if [ "$1" = "-d" ]; then 43 PROCESS_DEAD=1 44 fi 45 46 shift 47 48done 49 50DIR="$1" 51 52if [ "$DIR" = "" ]; then 53 54 echo "Usage: $0 [-s] [-d] afl_output_directory" 1>&2 55 echo 1>&2 56 echo Options: 1>&2 57 echo " -s - skip details and output summary results only" 1>&2 58 echo " -d - include dead fuzzer stats" 1>&2 59 echo 1>&2 60 exit 1 61 62fi 63 64cd "$DIR" || exit 1 65 66if [ -d queue ]; then 67 68 echo "[-] Error: parameter is an individual output directory, not a sync dir." 1>&2 69 exit 1 70 71fi 72 73RED=`tput setaf 9 1 1` 74GREEN=`tput setaf 2 1 1` 75BLUE=`tput setaf 4 1 1` 76YELLOW=`tput setaf 11 1 1` 77NC=`tput sgr0` 78RESET="$NC" 79 80CUR_TIME=`date +%s` 81 82TMP=`mktemp -t .afl-whatsup-XXXXXXXX` || TMP=`mktemp -p /data/local/tmp .afl-whatsup-XXXXXXXX` || TMP=`mktemp -p /data/local/tmp .afl-whatsup-XXXXXXXX` || exit 1 83 84ALIVE_CNT=0 85DEAD_CNT=0 86 87TOTAL_TIME=0 88TOTAL_EXECS=0 89TOTAL_EPS=0 90TOTAL_CRASHES=0 91TOTAL_PFAV=0 92TOTAL_PENDING=0 93 94# Time since last find / crash / hang, formatted as string 95FMT_TIME="0 days 0 hours" 96FMT_FIND="${RED}none seen yet${NC}" 97FMT_CRASH="none seen yet" 98FMT_HANG="none seen yet" 99 100if [ "$SUMMARY_ONLY" = "" ]; then 101 102 echo "Individual fuzzers" 103 echo "==================" 104 echo 105 106fi 107 108fmt_duration() 109{ 110 DUR_STRING= 111 if [ $1 -le 0 ]; then 112 return 1 113 fi 114 115 local duration=$((CUR_TIME - $1)) 116 local days=$((duration / 60 / 60 / 24)) 117 local hours=$(((duration / 60 / 60) % 24)) 118 local minutes=$(((duration / 60) % 60)) 119 local seconds=$((duration % 60)) 120 121 if [ $duration -le 0 ]; then 122 DUR_STRING="0 seconds" 123 elif [ $duration -eq 1 ]; then 124 DUR_STRING="1 second" 125 elif [ $days -gt 0 ]; then 126 DUR_STRING="$days days, $hours hours" 127 elif [ $hours -gt 0 ]; then 128 DUR_STRING="$hours hours, $minutes minutes" 129 elif [ $minutes -gt 0 ]; then 130 DUR_STRING="$minutes minutes, $seconds seconds" 131 else 132 DUR_STRING="$seconds seconds" 133 fi 134} 135 136FIRST=true 137TOTAL_WCOP= 138TOTAL_LAST_FIND=0 139 140for i in `find . -maxdepth 2 -iname fuzzer_stats | sort`; do 141 142 sed 's/^command_line.*$/_skip:1/;s/[ ]*:[ ]*/="/;s/$/"/' "$i" >"$TMP" 143 . "$TMP" 144 145 RUN_UNIX=$run_time 146 RUN_DAYS=$((RUN_UNIX / 60 / 60 / 24)) 147 RUN_HRS=$(((RUN_UNIX / 60 / 60) % 24)) 148 149 test -n "$cycles_wo_finds" && { 150 test -z "$FIRST" && TOTAL_WCOP="${TOTAL_WCOP}/" 151 TOTAL_WCOP="${TOTAL_WCOP}${cycles_wo_finds}" 152 FIRST= 153 } 154 155 if [ "$SUMMARY_ONLY" = "" ]; then 156 157 echo ">>> $afl_banner ($RUN_DAYS days, $RUN_HRS hrs) fuzzer PID: $fuzzer_pid <<<" 158 echo 159 160 fi 161 162 if ! kill -0 "$fuzzer_pid" 2>/dev/null; then 163 164 if [ "$SUMMARY_ONLY" = "" ]; then 165 166 echo " Instance is dead or running remotely, skipping." 167 echo 168 169 fi 170 171 DEAD_CNT=$((DEAD_CNT + 1)) 172 last_find=0 173 174 if [ "$PROCESS_DEAD" = "" ]; then 175 176 continue 177 178 fi 179 180 fi 181 182 ALIVE_CNT=$((ALIVE_CNT + 1)) 183 184 EXEC_SEC=0 185 test -z "$RUN_UNIX" -o "$RUN_UNIX" = 0 || EXEC_SEC=$((execs_done / RUN_UNIX)) 186 PATH_PERC=$((cur_item * 100 / corpus_count)) 187 188 TOTAL_TIME=$((TOTAL_TIME + RUN_UNIX)) 189 TOTAL_EPS=$((TOTAL_EPS + EXEC_SEC)) 190 TOTAL_EXECS=$((TOTAL_EXECS + execs_done)) 191 TOTAL_CRASHES=$((TOTAL_CRASHES + saved_crashes)) 192 TOTAL_PENDING=$((TOTAL_PENDING + pending_total)) 193 TOTAL_PFAV=$((TOTAL_PFAV + pending_favs)) 194 195 if [ "$last_find" -gt "$TOTAL_LAST_FIND" ]; then 196 TOTAL_LAST_FIND=$last_find 197 fi 198 199 if [ "$SUMMARY_ONLY" = "" ]; then 200 201 # Warnings in red 202 TIMEOUT_PERC=$((exec_timeout * 100 / execs_done)) 203 if [ $TIMEOUT_PERC -ge 10 ]; then 204 echo " ${RED}timeout_ratio $TIMEOUT_PERC%${NC}" 205 fi 206 207 if [ $EXEC_SEC -eq 0 ]; then 208 echo " ${YELLOW}no data yet, 0 execs/sec${NC}" 209 elif [ $EXEC_SEC -lt 100 ]; then 210 echo " ${RED}slow execution, $EXEC_SEC execs/sec${NC}" 211 fi 212 213 fmt_duration $last_find && FMT_FIND=$DUR_STRING 214 fmt_duration $last_crash && FMT_CRASH=$DUR_STRING 215 fmt_duration $last_hang && FMT_HANG=$DUR_STRING 216 FMT_CWOP="not available" 217 test -n "$cycles_wo_finds" && { 218 test "$cycles_wo_finds" = 0 && FMT_CWOP="$cycles_wo_finds" 219 test "$cycles_wo_finds" -gt 10 && FMT_CWOP="${YELLOW}$cycles_wo_finds${NC}" 220 test "$cycles_wo_finds" -gt 50 && FMT_CWOP="${RED}$cycles_wo_finds${NC}" 221 } 222 223 echo " last_find : $FMT_FIND" 224 echo " last_crash : $FMT_CRASH" 225 echo " last_hang : $FMT_HANG" 226 echo " cycles_wo_finds : $FMT_CWOP" 227 228 CPU_USAGE=$(ps aux | grep $fuzzer_pid | grep -v grep | awk '{print $3}') 229 MEM_USAGE=$(ps aux | grep $fuzzer_pid | grep -v grep | awk '{print $4}') 230 231 echo " cpu usage $CPU_USAGE%, memory usage $MEM_USAGE%" 232 echo " cycles $((cycles_done + 1)), lifetime speed $EXEC_SEC execs/sec, items $cur_item/$corpus_count (${PATH_PERC}%)" 233 234 if [ "$saved_crashes" = "0" ]; then 235 echo " pending $pending_favs/$pending_total, coverage $bitmap_cvg, no crashes yet" 236 else 237 echo " pending $pending_favs/$pending_total, coverage $bitmap_cvg, crashes saved $saved_crashes (!)" 238 fi 239 240 echo 241 242 fi 243 244done 245 246# Formatting for total time, time since last find, crash, and hang 247fmt_duration $((CUR_TIME - TOTAL_TIME)) && FMT_TIME=$DUR_STRING 248# Formatting for total execution 249FMT_EXECS="0 millions" 250EXECS_MILLION=$((TOTAL_EXECS / 1000 / 1000)) 251EXECS_THOUSAND=$((TOTAL_EXECS / 1000 % 1000)) 252if [ $EXECS_MILLION -gt 9 ]; then 253 FMT_EXECS="$EXECS_MILLION millions" 254elif [ $EXECS_MILLION -gt 0 ]; then 255 FMT_EXECS="$EXECS_MILLION millions, $EXECS_THOUSAND thousands" 256else 257 FMT_EXECS="$EXECS_THOUSAND thousands" 258fi 259 260rm -f "$TMP" 261 262TOTAL_DAYS=$((TOTAL_TIME / 60 / 60 / 24)) 263TOTAL_HRS=$(((TOTAL_TIME / 60 / 60) % 24)) 264 265test -z "$TOTAL_WCOP" && TOTAL_WCOP="not available" 266fmt_duration $TOTAL_LAST_FIND && TOTAL_LAST_FIND=$DUR_STRING 267 268test "$TOTAL_TIME" = "0" && TOTAL_TIME=1 269 270if [ "$PROCESS_DEAD" = "" ]; then 271 272 TXT="excluded from stats" 273 274else 275 276 TXT="included in stats" 277 ALIVE_CNT=$(($ALIVE_CNT - $DEAD_CNT)) 278 279fi 280 281echo "Summary stats" 282echo "=============" 283echo 284echo " Fuzzers alive : $ALIVE_CNT" 285 286if [ ! "$DEAD_CNT" = "0" ]; then 287 echo " Dead or remote : $DEAD_CNT ($TXT)" 288fi 289 290echo " Total run time : $FMT_TIME" 291echo " Total execs : $FMT_EXECS" 292echo " Cumulative speed : $TOTAL_EPS execs/sec" 293if [ "$ALIVE_CNT" -gt "0" ]; then 294 echo " Average speed : $((TOTAL_EPS / ALIVE_CNT)) execs/sec" 295fi 296echo " Pending items : $TOTAL_PFAV faves, $TOTAL_PENDING total" 297 298if [ "$ALIVE_CNT" -gt "1" ]; then 299 echo " Pending per fuzzer : $((TOTAL_PFAV/ALIVE_CNT)) faves, $((TOTAL_PENDING/ALIVE_CNT)) total (on average)" 300fi 301 302echo " Crashes saved : $TOTAL_CRASHES" 303echo "Cycles without finds : $TOTAL_WCOP" 304echo " Time without finds : $TOTAL_LAST_FIND" 305echo 306 307exit 0 308