xref: /aosp_15_r20/external/AFLplusplus/afl-whatsup (revision 08b48e0b10e97b33e7b60c5b6e2243bd915777f2)
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