#!/bin/sh # # american fuzzy lop++ - status check tool # ---------------------------------------- # # Originally written by Michal Zalewski # # Copyright 2015 Google Inc. All rights reserved. # Copyright 2019-2023 AFLplusplus Project. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # https://www.apache.org/licenses/LICENSE-2.0 # # This tool summarizes the status of any locally-running synchronized # instances of afl-fuzz. # echo "$0 status check tool for afl-fuzz by Michal Zalewski" echo test "$1" = "-h" -o "$1" = "-hh" && { echo "Usage: $0 [-s] [-d] afl_output_directory" echo echo Options: echo " -s - skip details and output summary results only" echo " -d - include dead fuzzer stats" echo exit 1 } unset SUMMARY_ONLY unset PROCESS_DEAD while [ "$1" = "-s" -o "$1" = "-d" ]; do if [ "$1" = "-s" ]; then SUMMARY_ONLY=1 fi if [ "$1" = "-d" ]; then PROCESS_DEAD=1 fi shift done DIR="$1" if [ "$DIR" = "" ]; then echo "Usage: $0 [-s] [-d] afl_output_directory" 1>&2 echo 1>&2 echo Options: 1>&2 echo " -s - skip details and output summary results only" 1>&2 echo " -d - include dead fuzzer stats" 1>&2 echo 1>&2 exit 1 fi cd "$DIR" || exit 1 if [ -d queue ]; then echo "[-] Error: parameter is an individual output directory, not a sync dir." 1>&2 exit 1 fi RED=`tput setaf 9 1 1 2>/dev/null` GREEN=`tput setaf 2 1 1 2>/dev/null` BLUE=`tput setaf 4 1 1 2>/dev/null` YELLOW=`tput setaf 11 1 1 2>/dev/null` NC=`tput sgr0` RESET="$NC" CUR_TIME=`date +%s` TMP=`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 ALIVE_CNT=0 DEAD_CNT=0 TOTAL_TIME=0 TOTAL_EXECS=0 TOTAL_EPS=0 TOTAL_CRASHES=0 TOTAL_HANGS=0 TOTAL_PFAV=0 TOTAL_PENDING=0 # Time since last find / crash / hang, formatted as string FMT_TIME="0 days 0 hours" FMT_FIND="${RED}none seen yet${NC}" FMT_CRASH="none seen yet" FMT_HANG="none seen yet" if [ "$SUMMARY_ONLY" = "" ]; then echo "Individual fuzzers" echo "==================" echo fi fmt_duration() { DUR_STRING= if [ $1 -le 0 ]; then return 1 fi local duration=$((CUR_TIME - $1)) local days=$((duration / 60 / 60 / 24)) local hours=$(((duration / 60 / 60) % 24)) local minutes=$(((duration / 60) % 60)) local seconds=$((duration % 60)) if [ $duration -le 0 ]; then DUR_STRING="0 seconds" elif [ $duration -eq 1 ]; then DUR_STRING="1 second" elif [ $days -gt 0 ]; then DUR_STRING="$days days, $hours hours" elif [ $hours -gt 0 ]; then DUR_STRING="$hours hours, $minutes minutes" elif [ $minutes -gt 0 ]; then DUR_STRING="$minutes minutes, $seconds seconds" else DUR_STRING="$seconds seconds" fi } FIRST=true TOTAL_WCOP= TOTAL_LAST_FIND=0 for i in `find . -maxdepth 2 -iname fuzzer_stats | sort`; do sed 's/^command_line.*$/_skip:1/;s/[ ]*:[ ]*/="/;s/$/"/' "$i" >"$TMP" . "$TMP" DIR=$(dirname "$i") DIR=${DIR##*/} RUN_UNIX=$run_time RUN_DAYS=$((RUN_UNIX / 60 / 60 / 24)) RUN_HRS=$(((RUN_UNIX / 60 / 60) % 24)) test -n "$cycles_wo_finds" && { test -z "$FIRST" && TOTAL_WCOP="${TOTAL_WCOP}/" TOTAL_WCOP="${TOTAL_WCOP}${cycles_wo_finds}" FIRST= } if [ "$SUMMARY_ONLY" = "" ]; then echo ">>> $afl_banner instance: $DIR ($RUN_DAYS days, $RUN_HRS hrs) fuzzer PID: $fuzzer_pid <<<" echo fi if ! kill -0 "$fuzzer_pid" 2>/dev/null; then if [ "$SUMMARY_ONLY" = "" ]; then echo " Instance is dead or running remotely, skipping." echo fi DEAD_CNT=$((DEAD_CNT + 1)) last_find=0 if [ "$PROCESS_DEAD" = "" ]; then continue fi fi ALIVE_CNT=$((ALIVE_CNT + 1)) EXEC_SEC=0 test -z "$RUN_UNIX" -o "$RUN_UNIX" = 0 || EXEC_SEC=$((execs_done / RUN_UNIX)) PATH_PERC=$((cur_item * 100 / corpus_count)) TOTAL_TIME=$((TOTAL_TIME + RUN_UNIX)) TOTAL_EPS=$((TOTAL_EPS + EXEC_SEC)) TOTAL_EXECS=$((TOTAL_EXECS + execs_done)) TOTAL_CRASHES=$((TOTAL_CRASHES + saved_crashes)) TOTAL_HANGS=$((TOTAL_HANGS + saved_hangs)) TOTAL_PENDING=$((TOTAL_PENDING + pending_total)) TOTAL_PFAV=$((TOTAL_PFAV + pending_favs)) if [ "$last_find" -gt "$TOTAL_LAST_FIND" ]; then TOTAL_LAST_FIND=$last_find fi if [ "$SUMMARY_ONLY" = "" ]; then # Warnings in red TIMEOUT_PERC=$((exec_timeout * 100 / execs_done)) if [ $TIMEOUT_PERC -ge 10 ]; then echo " ${RED}timeout_ratio $TIMEOUT_PERC%${NC}" fi if [ $EXEC_SEC -eq 0 ]; then echo " ${YELLOW}no data yet, 0 execs/sec${NC}" elif [ $EXEC_SEC -lt 100 ]; then echo " ${RED}slow execution, $EXEC_SEC execs/sec${NC}" fi fmt_duration $last_find && FMT_FIND=$DUR_STRING fmt_duration $last_crash && FMT_CRASH=$DUR_STRING fmt_duration $last_hang && FMT_HANG=$DUR_STRING FMT_CWOP="not available" test -n "$cycles_wo_finds" && { test "$cycles_wo_finds" = 0 && FMT_CWOP="$cycles_wo_finds" test "$cycles_wo_finds" -gt 10 && FMT_CWOP="${YELLOW}$cycles_wo_finds${NC}" test "$cycles_wo_finds" -gt 50 && FMT_CWOP="${RED}$cycles_wo_finds${NC}" } echo " last_find : $FMT_FIND" echo " last_crash : $FMT_CRASH" echo " last_hang : $FMT_HANG" echo " cycles_wo_finds : $FMT_CWOP" CPU_USAGE=$(ps aux | grep $fuzzer_pid | grep -v grep | awk '{print $3}') MEM_USAGE=$(ps aux | grep $fuzzer_pid | grep -v grep | awk '{print $4}') echo " cpu usage $CPU_USAGE%, memory usage $MEM_USAGE%" echo " cycles $((cycles_done + 1)), lifetime speed $EXEC_SEC execs/sec, items $cur_item/$corpus_count (${PATH_PERC}%)" if [ "$saved_crashes" = "0" ]; then echo " pending $pending_favs/$pending_total, coverage $bitmap_cvg, no crashes yet" else echo " pending $pending_favs/$pending_total, coverage $bitmap_cvg, crashes saved $saved_crashes (!)" fi echo fi done # Formatting for total time, time since last find, crash, and hang fmt_duration $((CUR_TIME - TOTAL_TIME)) && FMT_TIME=$DUR_STRING # Formatting for total execution FMT_EXECS="0 millions" EXECS_MILLION=$((TOTAL_EXECS / 1000 / 1000)) EXECS_THOUSAND=$((TOTAL_EXECS / 1000 % 1000)) if [ $EXECS_MILLION -gt 9 ]; then FMT_EXECS="$EXECS_MILLION millions" elif [ $EXECS_MILLION -gt 0 ]; then FMT_EXECS="$EXECS_MILLION millions, $EXECS_THOUSAND thousands" else FMT_EXECS="$EXECS_THOUSAND thousands" fi rm -f "$TMP" TOTAL_DAYS=$((TOTAL_TIME / 60 / 60 / 24)) TOTAL_HRS=$(((TOTAL_TIME / 60 / 60) % 24)) test -z "$TOTAL_WCOP" && TOTAL_WCOP="not available" fmt_duration $TOTAL_LAST_FIND && TOTAL_LAST_FIND=$DUR_STRING test "$TOTAL_TIME" = "0" && TOTAL_TIME=1 if [ "$PROCESS_DEAD" = "" ]; then TXT="excluded from stats" else TXT="included in stats" ALIVE_CNT=$(($ALIVE_CNT - $DEAD_CNT)) fi echo "Summary stats" echo "=============" echo echo " Fuzzers alive : $ALIVE_CNT" if [ ! "$DEAD_CNT" = "0" ]; then echo " Dead or remote : $DEAD_CNT ($TXT)" fi echo " Total run time : $FMT_TIME" echo " Total execs : $FMT_EXECS" echo " Cumulative speed : $TOTAL_EPS execs/sec" if [ "$ALIVE_CNT" -gt "0" ]; then echo " Average speed : $((TOTAL_EPS / ALIVE_CNT)) execs/sec" fi echo " Pending items : $TOTAL_PFAV faves, $TOTAL_PENDING total" if [ "$ALIVE_CNT" -gt "1" ]; then echo " Pending per fuzzer : $((TOTAL_PFAV/ALIVE_CNT)) faves, $((TOTAL_PENDING/ALIVE_CNT)) total (on average)" fi echo " Crashes saved : $TOTAL_CRASHES" echo " Hangs saved : $TOTAL_HANGS" echo "Cycles without finds : $TOTAL_WCOP" echo " Time without finds : $TOTAL_LAST_FIND" echo exit 0