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-2024 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 21test "$1" = "-h" -o "$1" = "-hh" && { 22 echo "$0 status check tool for afl-fuzz by Michal Zalewski" 23 echo 24 echo "Usage: $0 [-s] [-d] afl_output_directory" 25 echo 26 echo Options: 27 echo " -d - include dead fuzzer stats" 28 echo " -m - just show minimal stats" 29 echo " -n - no color output" 30 echo " -s - skip details and output summary results only" 31 echo 32 exit 1 33} 34 35unset MINIMAL_ONLY 36unset NO_COLOR 37unset PROCESS_DEAD 38unset SUMMARY_ONLY 39unset RED 40unset GREEN 41unset YELLOW 42unset BLUE 43unset NC 44unset RESET 45 46if [ -z "$TERM" ]; then export TERM=vt220; fi 47 48while [ "$1" = "-d" -o "$1" = "-m" -o "$1" = "-n" -o "$1" = "-s" ]; do 49 50 if [ "$1" = "-d" ]; then 51 PROCESS_DEAD=1 52 fi 53 54 if [ "$1" = "-m" ]; then 55 MINIMAL_ONLY=1 56 fi 57 58 if [ "$1" = "-n" ]; then 59 NO_COLOR=1 60 fi 61 62 if [ "$1" = "-s" ]; then 63 SUMMARY_ONLY=1 64 fi 65 66 shift 67 68done 69 70DIR="$1" 71 72if [ "$DIR" = "" -o "$DIR" = "-h" -o "$DIR" = "--help" ]; then 73 74 echo "$0 status check tool for afl-fuzz by Michal Zalewski" 1>&2 75 echo 1>&2 76 echo "Usage: $0 [-d] [-m] [-n] [-s] afl_output_directory" 1>&2 77 echo 1>&2 78 echo Options: 1>&2 79 echo " -d - include dead fuzzer stats" 1>&2 80 echo " -m - just show minimal stats" 1>&2 81 echo " -n - no color output" 1>&2 82 echo " -s - skip details and output summary results only" 1>&2 83 echo 1>&2 84 exit 1 85 86fi 87 88if [ -z "$MINIMAL_ONLY" ]; then 89 echo "$0 status check tool for afl-fuzz by Michal Zalewski" 90 echo 91fi 92 93cd "$DIR" || exit 1 94 95if [ -d queue ]; then 96 97 echo "[-] Error: parameter is an individual output directory, not a sync dir." 1>&2 98 exit 1 99 100fi 101 102BC=`which bc 2>/dev/null` 103FUSER=`which fuser 2>/dev/null` 104 105if [ -z "$NO_COLOR" ]; then 106 RED=`tput setaf 9 1 1 2>/dev/null` 107 GREEN=`tput setaf 2 1 1 2>/dev/null` 108 BLUE=`tput setaf 4 1 1 2>/dev/null` 109 YELLOW=`tput setaf 11 1 1 2>/dev/null` 110 NC=`tput sgr0` 111 RESET="$NC" 112fi 113 114CUR_TIME=`date +%s` 115 116TMP=`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 117 118ALIVE_CNT=0 119DEAD_CNT=0 120START_CNT=0 121 122TOTAL_TIME=0 123TOTAL_EXECS=0 124TOTAL_EPS=0 125TOTAL_CRASHES=0 126TOTAL_HANGS=0 127TOTAL_PFAV=0 128TOTAL_PENDING=0 129TOTAL_COVERAGE= 130 131# Time since last find / crash / hang, formatted as string 132FMT_TIME="0 days 0 hours" 133FMT_FIND="${RED}none seen yet${NC}" 134FMT_CRASH="none seen yet" 135FMT_HANG="none seen yet" 136 137if [ "$SUMMARY_ONLY" = "" ]; then 138 139 echo "Individual fuzzers" 140 echo "==================" 141 echo 142 143fi 144 145fmt_duration() 146{ 147 DUR_STRING= 148 if [ $1 -le 0 ]; then 149 return 1 150 fi 151 152 local duration=$((CUR_TIME - $1)) 153 local days=$((duration / 60 / 60 / 24)) 154 local hours=$(((duration / 60 / 60) % 24)) 155 local minutes=$(((duration / 60) % 60)) 156 local seconds=$((duration % 60)) 157 158 if [ $duration -le 0 ]; then 159 DUR_STRING="0 seconds" 160 elif [ $duration -eq 1 ]; then 161 DUR_STRING="1 second" 162 elif [ $days -gt 0 ]; then 163 DUR_STRING="$days days, $hours hours" 164 elif [ $hours -gt 0 ]; then 165 DUR_STRING="$hours hours, $minutes minutes" 166 elif [ $minutes -gt 0 ]; then 167 DUR_STRING="$minutes minutes, $seconds seconds" 168 else 169 DUR_STRING="$seconds seconds" 170 fi 171} 172 173FIRST=true 174TOTAL_WCOP= 175TOTAL_LAST_FIND=0 176 177for j in `find . -maxdepth 2 -iname fuzzer_setup | sort`; do 178 179 DIR=$(dirname "$j") 180 i=$DIR/fuzzer_stats 181 182 if [ -f "$i" ]; then 183 184 sed 's/^command_line.*$/_skip:1/;s/[ ]*:[ ]*/="/;s/$/"/' "$i" >"$TMP" 185 . "$TMP" 186 DIRECTORY=$DIR 187 DIR=${DIR##*/} 188 RUN_UNIX=$run_time 189 RUN_DAYS=$((RUN_UNIX / 60 / 60 / 24)) 190 RUN_HRS=$(((RUN_UNIX / 60 / 60) % 24)) 191 COVERAGE=$(echo $bitmap_cvg|tr -d %) 192 if [ -n "$TOTAL_COVERAGE" -a -n "$COVERAGE" -a -n "$BC" ]; then 193 if [ "$(echo "$TOTAL_COVERAGE < $COVERAGE" | bc)" -eq 1 ]; then 194 TOTAL_COVERAGE=$COVERAGE 195 fi 196 fi 197 if [ -z "$TOTAL_COVERAGE" ]; then TOTAL_COVERAGE=$COVERAGE ; fi 198 199 test -n "$cycles_wo_finds" && { 200 test -z "$FIRST" && TOTAL_WCOP="${TOTAL_WCOP}/" 201 TOTAL_WCOP="${TOTAL_WCOP}${cycles_wo_finds}" 202 FIRST= 203 } 204 205 if [ "$SUMMARY_ONLY" = "" ]; then 206 207 echo ">>> $afl_banner instance: $DIR ($RUN_DAYS days, $RUN_HRS hrs) fuzzer PID: $fuzzer_pid <<<" 208 echo 209 210 fi 211 212 if ! kill -0 "$fuzzer_pid" 2>/dev/null; then 213 214 IS_STARTING= 215 IS_DEAD= 216 217 if [ -e "$i" ] && [ -e "$j" ] && [ -n "$FUSER" ]; then 218 219 if [ "$i" -ot "$j" ]; then 220 221 # fuzzer_setup is newer than fuzzer_stats, maybe the instance is starting? 222 TMP_PID=`fuser -v "$DIRECTORY" 2>&1 | grep afl-fuzz` 223 224 if [ -n "$TMP_PID" ]; then 225 226 if [ "$SUMMARY_ONLY" = "" ]; then 227 228 echo " Instance is still starting up, skipping." 229 echo 230 231 fi 232 233 START_CNT=$((START_CNT + 1)) 234 last_find=0 235 IS_STARTING=1 236 237 if [ "$PROCESS_DEAD" = "" ]; then 238 239 continue 240 241 fi 242 243 fi 244 245 fi 246 247 fi 248 249 if [ -z "$IS_STARTING" ]; then 250 251 if [ "$SUMMARY_ONLY" = "" ]; then 252 253 echo " Instance is dead or running remotely, skipping." 254 echo 255 256 fi 257 258 DEAD_CNT=$((DEAD_CNT + 1)) 259 IS_DEAD=1 260 last_find=0 261 262 if [ "$PROCESS_DEAD" = "" ]; then 263 264 continue 265 266 fi 267 268 fi 269 270 fi 271 272 ALIVE_CNT=$((ALIVE_CNT + 1)) 273 274 EXEC_SEC=0 275 test -z "$RUN_UNIX" -o "$RUN_UNIX" = 0 || EXEC_SEC=$((execs_done / RUN_UNIX)) 276 PATH_PERC=$((cur_item * 100 / corpus_count)) 277 278 TOTAL_TIME=$((TOTAL_TIME + RUN_UNIX)) 279 TOTAL_EPS=$((TOTAL_EPS + EXEC_SEC)) 280 TOTAL_EXECS=$((TOTAL_EXECS + execs_done)) 281 TOTAL_CRASHES=$((TOTAL_CRASHES + saved_crashes)) 282 TOTAL_HANGS=$((TOTAL_HANGS + saved_hangs)) 283 TOTAL_PENDING=$((TOTAL_PENDING + pending_total)) 284 TOTAL_PFAV=$((TOTAL_PFAV + pending_favs)) 285 286 if [ "$last_find" -gt "$TOTAL_LAST_FIND" ]; then 287 TOTAL_LAST_FIND=$last_find 288 fi 289 290 if [ "$SUMMARY_ONLY" = "" ]; then 291 292 # Warnings in red 293 TIMEOUT_PERC=$((exec_timeout * 100 / execs_done)) 294 if [ $TIMEOUT_PERC -ge 10 ]; then 295 echo " ${RED}timeout_ratio $TIMEOUT_PERC%${NC}" 296 fi 297 298 if [ $EXEC_SEC -eq 0 ]; then 299 echo " ${YELLOW}no data yet, 0 execs/sec${NC}" 300 elif [ $EXEC_SEC -lt 100 ]; then 301 echo " ${RED}slow execution, $EXEC_SEC execs/sec${NC}" 302 fi 303 304 fmt_duration $last_find && FMT_FIND=$DUR_STRING 305 fmt_duration $last_crash && FMT_CRASH=$DUR_STRING 306 fmt_duration $last_hang && FMT_HANG=$DUR_STRING 307 FMT_CWOP="not available" 308 test -n "$cycles_wo_finds" && { 309 test "$cycles_wo_finds" = 0 && FMT_CWOP="$cycles_wo_finds" 310 test "$cycles_wo_finds" -gt 10 && FMT_CWOP="${YELLOW}$cycles_wo_finds${NC}" 311 test "$cycles_wo_finds" -gt 50 && FMT_CWOP="${RED}$cycles_wo_finds${NC}" 312 } 313 314 echo " last_find : $FMT_FIND" 315 echo " last_crash : $FMT_CRASH" 316 if [ -z "$MINIMAL_ONLY" ]; then 317 echo " last_hang : $FMT_HANG" 318 echo " cycles_wo_finds : $FMT_CWOP" 319 fi 320 echo " coverage : $COVERAGE%" 321 322 if [ -z "$MINIMAL_ONLY" ]; then 323 324 CPU_USAGE=$(ps aux | grep -w $fuzzer_pid | grep -v grep | awk '{print $3}') 325 MEM_USAGE=$(ps aux | grep -w $fuzzer_pid | grep -v grep | awk '{print $4}') 326 327 echo " cpu usage $CPU_USAGE%, memory usage $MEM_USAGE%" 328 329 fi 330 331 echo " cycles $((cycles_done + 1)), lifetime speed $EXEC_SEC execs/sec, items $cur_item/$corpus_count (${PATH_PERC}%)" 332 333 if [ "$saved_crashes" = "0" ]; then 334 echo " pending $pending_favs/$pending_total, coverage $bitmap_cvg, no crashes yet" 335 else 336 echo " pending $pending_favs/$pending_total, coverage $bitmap_cvg, crashes saved $saved_crashes (!)" 337 fi 338 339 echo 340 341 fi 342 343 else 344 345 if [ ! -e "$i" -a -e "$j" ]; then 346 347 if [ '!' "$PROCESS_DEAD" = "" ]; then 348 ALIVE_CNT=$((ALIVE_CNT + 1)) 349 fi 350 START_CNT=$((START_CNT + 1)) 351 last_find=0 352 IS_STARTING=1 353 354 fi 355 356 fi 357 358done 359 360# Formatting for total time, time since last find, crash, and hang 361fmt_duration $((CUR_TIME - TOTAL_TIME)) && FMT_TIME=$DUR_STRING 362# Formatting for total execution 363FMT_EXECS="0 millions" 364EXECS_MILLION=$((TOTAL_EXECS / 1000 / 1000)) 365EXECS_THOUSAND=$((TOTAL_EXECS / 1000 % 1000)) 366if [ $EXECS_MILLION -gt 9 ]; then 367 FMT_EXECS="$EXECS_MILLION millions" 368 elif [ $EXECS_MILLION -gt 0 ]; then 369 FMT_EXECS="$EXECS_MILLION millions, $EXECS_THOUSAND thousands" 370else 371 FMT_EXECS="$EXECS_THOUSAND thousands" 372fi 373 374rm -f "$TMP" 375 376TOTAL_DAYS=$((TOTAL_TIME / 60 / 60 / 24)) 377TOTAL_HRS=$(((TOTAL_TIME / 60 / 60) % 24)) 378 379test -z "$TOTAL_WCOP" && TOTAL_WCOP="not available" 380fmt_duration $TOTAL_LAST_FIND && TOTAL_LAST_FIND=$DUR_STRING 381 382test "$TOTAL_TIME" = "0" && TOTAL_TIME=1 383 384if [ "$PROCESS_DEAD" = "" ]; then 385 386 TXT="excluded from stats" 387 388else 389 390 TXT="included in stats" 391 ALIVE_CNT=$(($ALIVE_CNT - $DEAD_CNT - $START_CNT)) 392 393fi 394 395echo "Summary stats" 396echo "=============" 397if [ -z "$SUMMARY_ONLY" -o -z "$MINIMAL_ONLY" ]; then 398 echo 399fi 400 401echo " Fuzzers alive : $ALIVE_CNT" 402 403if [ ! "$START_CNT" = "0" ]; then 404 echo " Starting up : $START_CNT ($TXT)" 405fi 406 407if [ ! "$DEAD_CNT" = "0" ]; then 408 echo " Dead or remote : $DEAD_CNT ($TXT)" 409fi 410 411echo " Total run time : $FMT_TIME" 412if [ -z "$MINIMAL_ONLY" ]; then 413 echo " Total execs : $FMT_EXECS" 414 echo " Cumulative speed : $TOTAL_EPS execs/sec" 415fi 416if [ "$ALIVE_CNT" -gt "0" ]; then 417 echo " Average speed : $((TOTAL_EPS / ALIVE_CNT)) execs/sec" 418fi 419if [ -z "$MINIMAL_ONLY" ]; then 420 echo " Pending items : $TOTAL_PFAV faves, $TOTAL_PENDING total" 421fi 422 423if [ "$ALIVE_CNT" -gt "1" -o -n "$MINIMAL_ONLY" ]; then 424 if [ "$ALIVE_CNT" -gt "0" ]; then 425 echo " Pending per fuzzer : $((TOTAL_PFAV/ALIVE_CNT)) faves, $((TOTAL_PENDING/ALIVE_CNT)) total (on average)" 426 fi 427fi 428 429echo " Coverage reached : ${TOTAL_COVERAGE}%" 430echo " Crashes saved : $TOTAL_CRASHES" 431if [ -z "$MINIMAL_ONLY" ]; then 432 echo " Hangs saved : $TOTAL_HANGS" 433 echo "Cycles without finds : $TOTAL_WCOP" 434fi 435echo " Time without finds : $TOTAL_LAST_FIND" 436echo 437 438exit 0 439