diff options
Diffstat (limited to 'src/afl-fuzz-stats.c')
-rw-r--r-- | src/afl-fuzz-stats.c | 754 |
1 files changed, 754 insertions, 0 deletions
diff --git a/src/afl-fuzz-stats.c b/src/afl-fuzz-stats.c new file mode 100644 index 00000000..5dbd59ac --- /dev/null +++ b/src/afl-fuzz-stats.c @@ -0,0 +1,754 @@ +/* + american fuzzy lop - fuzzer code + -------------------------------- + + Written and maintained by Michal Zalewski <lcamtuf@google.com> + + Forkserver design by Jann Horn <jannhorn@googlemail.com> + + Copyright 2013, 2014, 2015, 2016, 2017 Google Inc. 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: + + http://www.apache.org/licenses/LICENSE-2.0 + + This is the real deal: the program takes an instrumented binary and + attempts a variety of basic fuzzing tricks, paying close attention to + how they affect the execution path. + + */ + +#include "afl-fuzz.h" + +/* Update stats file for unattended monitoring. */ + +void write_stats_file(double bitmap_cvg, double stability, double eps) { + + static double last_bcvg, last_stab, last_eps; + static struct rusage usage; + + u8* fn = alloc_printf("%s/fuzzer_stats", out_dir); + s32 fd; + FILE* f; + + fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0600); + + if (fd < 0) PFATAL("Unable to create '%s'", fn); + + ck_free(fn); + + f = fdopen(fd, "w"); + + if (!f) PFATAL("fdopen() failed"); + + /* Keep last values in case we're called from another context + where exec/sec stats and such are not readily available. */ + + if (!bitmap_cvg && !stability && !eps) { + bitmap_cvg = last_bcvg; + stability = last_stab; + eps = last_eps; + } else { + last_bcvg = bitmap_cvg; + last_stab = stability; + last_eps = eps; + } + + fprintf(f, "start_time : %llu\n" + "last_update : %llu\n" + "fuzzer_pid : %d\n" + "cycles_done : %llu\n" + "execs_done : %llu\n" + "execs_per_sec : %0.02f\n" + "paths_total : %u\n" + "paths_favored : %u\n" + "paths_found : %u\n" + "paths_imported : %u\n" + "max_depth : %u\n" + "cur_path : %u\n" /* Must match find_start_position() */ + "pending_favs : %u\n" + "pending_total : %u\n" + "variable_paths : %u\n" + "stability : %0.02f%%\n" + "bitmap_cvg : %0.02f%%\n" + "unique_crashes : %llu\n" + "unique_hangs : %llu\n" + "last_path : %llu\n" + "last_crash : %llu\n" + "last_hang : %llu\n" + "execs_since_crash : %llu\n" + "exec_timeout : %u\n" + "slowest_exec_ms : %llu\n" + "peak_rss_mb : %lu\n" + "afl_banner : %s\n" + "afl_version : " VERSION "\n" + "target_mode : %s%s%s%s%s%s%s%s\n" + "command_line : %s\n", + start_time / 1000, get_cur_time() / 1000, getpid(), + queue_cycle ? (queue_cycle - 1) : 0, total_execs, eps, + queued_paths, queued_favored, queued_discovered, queued_imported, + max_depth, current_entry, pending_favored, pending_not_fuzzed, + queued_variable, stability, bitmap_cvg, unique_crashes, + unique_hangs, last_path_time / 1000, last_crash_time / 1000, + last_hang_time / 1000, total_execs - last_crash_execs, + exec_tmout, slowest_exec_ms, (unsigned long int)usage.ru_maxrss, use_banner, + unicorn_mode ? "unicorn" : "", qemu_mode ? "qemu " : "", dumb_mode ? " dumb " : "", + no_forkserver ? "no_forksrv " : "", crash_mode ? "crash " : "", + persistent_mode ? "persistent " : "", deferred_mode ? "deferred " : "", + (unicorn_mode || qemu_mode || dumb_mode || no_forkserver || crash_mode || + persistent_mode || deferred_mode) ? "" : "default", + orig_cmdline); + /* ignore errors */ + + fclose(f); + +} + + +/* Update the plot file if there is a reason to. */ + +void maybe_update_plot_file(double bitmap_cvg, double eps) { + + static u32 prev_qp, prev_pf, prev_pnf, prev_ce, prev_md; + static u64 prev_qc, prev_uc, prev_uh; + + if (prev_qp == queued_paths && prev_pf == pending_favored && + prev_pnf == pending_not_fuzzed && prev_ce == current_entry && + prev_qc == queue_cycle && prev_uc == unique_crashes && + prev_uh == unique_hangs && prev_md == max_depth) return; + + prev_qp = queued_paths; + prev_pf = pending_favored; + prev_pnf = pending_not_fuzzed; + prev_ce = current_entry; + prev_qc = queue_cycle; + prev_uc = unique_crashes; + prev_uh = unique_hangs; + prev_md = max_depth; + + /* Fields in the file: + + unix_time, cycles_done, cur_path, paths_total, paths_not_fuzzed, + favored_not_fuzzed, unique_crashes, unique_hangs, max_depth, + execs_per_sec */ + + fprintf(plot_file, + "%llu, %llu, %u, %u, %u, %u, %0.02f%%, %llu, %llu, %u, %0.02f\n", + get_cur_time() / 1000, queue_cycle - 1, current_entry, queued_paths, + pending_not_fuzzed, pending_favored, bitmap_cvg, unique_crashes, + unique_hangs, max_depth, eps); /* ignore errors */ + + fflush(plot_file); + +} + + +/* Check terminal dimensions after resize. */ + +static void check_term_size(void) { + + struct winsize ws; + + term_too_small = 0; + + if (ioctl(1, TIOCGWINSZ, &ws)) return; + + if (ws.ws_row == 0 || ws.ws_col == 0) return; + if (ws.ws_row < 24 || ws.ws_col < 79) term_too_small = 1; + +} + + +/* A spiffy retro stats screen! This is called every stats_update_freq + execve() calls, plus in several other circumstances. */ + +void show_stats(void) { + + static u64 last_stats_ms, last_plot_ms, last_ms, last_execs; + static double avg_exec; + double t_byte_ratio, stab_ratio; + + u64 cur_ms; + u32 t_bytes, t_bits; + + u32 banner_len, banner_pad; + u8 tmp[256]; + + cur_ms = get_cur_time(); + + /* If not enough time has passed since last UI update, bail out. */ + + if (cur_ms - last_ms < 1000 / UI_TARGET_HZ) return; + + /* Check if we're past the 10 minute mark. */ + + if (cur_ms - start_time > 10 * 60 * 1000) run_over10m = 1; + + /* Calculate smoothed exec speed stats. */ + + if (!last_execs) { + + avg_exec = ((double)total_execs) * 1000 / (cur_ms - start_time); + + } else { + + double cur_avg = ((double)(total_execs - last_execs)) * 1000 / + (cur_ms - last_ms); + + /* If there is a dramatic (5x+) jump in speed, reset the indicator + more quickly. */ + + if (cur_avg * 5 < avg_exec || cur_avg / 5 > avg_exec) + avg_exec = cur_avg; + + avg_exec = avg_exec * (1.0 - 1.0 / AVG_SMOOTHING) + + cur_avg * (1.0 / AVG_SMOOTHING); + + } + + last_ms = cur_ms; + last_execs = total_execs; + + /* Tell the callers when to contact us (as measured in execs). */ + + stats_update_freq = avg_exec / (UI_TARGET_HZ * 10); + if (!stats_update_freq) stats_update_freq = 1; + + /* Do some bitmap stats. */ + + t_bytes = count_non_255_bytes(virgin_bits); + t_byte_ratio = ((double)t_bytes * 100) / MAP_SIZE; + + if (t_bytes) + stab_ratio = 100 - ((double)var_byte_count) * 100 / t_bytes; + else + stab_ratio = 100; + + /* Roughly every minute, update fuzzer stats and save auto tokens. */ + + if (cur_ms - last_stats_ms > STATS_UPDATE_SEC * 1000) { + + last_stats_ms = cur_ms; + write_stats_file(t_byte_ratio, stab_ratio, avg_exec); + save_auto(); + write_bitmap(); + + } + + /* Every now and then, write plot data. */ + + if (cur_ms - last_plot_ms > PLOT_UPDATE_SEC * 1000) { + + last_plot_ms = cur_ms; + maybe_update_plot_file(t_byte_ratio, avg_exec); + + } + + /* Honor AFL_EXIT_WHEN_DONE and AFL_BENCH_UNTIL_CRASH. */ + + if (!dumb_mode && cycles_wo_finds > 100 && !pending_not_fuzzed && + getenv("AFL_EXIT_WHEN_DONE")) stop_soon = 2; + + if (total_crashes && getenv("AFL_BENCH_UNTIL_CRASH")) stop_soon = 2; + + /* If we're not on TTY, bail out. */ + + if (not_on_tty) return; + + /* Compute some mildly useful bitmap stats. */ + + t_bits = (MAP_SIZE << 3) - count_bits(virgin_bits); + + /* Now, for the visuals... */ + + if (clear_screen) { + + SAYF(TERM_CLEAR CURSOR_HIDE); + clear_screen = 0; + + check_term_size(); + + } + + SAYF(TERM_HOME); + + if (term_too_small) { + + SAYF(cBRI "Your terminal is too small to display the UI.\n" + "Please resize terminal window to at least 79x24.\n" cRST); + + return; + + } + + /* Let's start by drawing a centered banner. */ + + banner_len = (crash_mode ? 24 : 22) + strlen(VERSION) + strlen(use_banner) + strlen(power_name) + 3 + 5; + banner_pad = (79 - banner_len) / 2; + memset(tmp, ' ', banner_pad); + +#ifdef HAVE_AFFINITY + sprintf(tmp + banner_pad, "%s " cLCY VERSION cLGN + " (%s) " cPIN "[%s]" cBLU " {%d}", crash_mode ? cPIN "peruvian were-rabbit" : + cYEL "american fuzzy lop", use_banner, power_name, cpu_aff); +#else + sprintf(tmp + banner_pad, "%s " cLCY VERSION cLGN + " (%s) " cPIN "[%s]", crash_mode ? cPIN "peruvian were-rabbit" : + cYEL "american fuzzy lop", use_banner, power_name); +#endif /* HAVE_AFFINITY */ + + SAYF("\n%s\n", tmp); + + /* "Handy" shortcuts for drawing boxes... */ + +#define bSTG bSTART cGRA +#define bH2 bH bH +#define bH5 bH2 bH2 bH +#define bH10 bH5 bH5 +#define bH20 bH10 bH10 +#define bH30 bH20 bH10 +#define SP5 " " +#define SP10 SP5 SP5 +#define SP20 SP10 SP10 + + /* Lord, forgive me this. */ + + SAYF(SET_G1 bSTG bLT bH bSTOP cCYA " process timing " bSTG bH30 bH5 bH bHB + bH bSTOP cCYA " overall results " bSTG bH2 bH2 bRT "\n"); + + if (dumb_mode) { + + strcpy(tmp, cRST); + + } else { + + u64 min_wo_finds = (cur_ms - last_path_time) / 1000 / 60; + + /* First queue cycle: don't stop now! */ + if (queue_cycle == 1 || min_wo_finds < 15) strcpy(tmp, cMGN); else + + /* Subsequent cycles, but we're still making finds. */ + if (cycles_wo_finds < 25 || min_wo_finds < 30) strcpy(tmp, cYEL); else + + /* No finds for a long time and no test cases to try. */ + if (cycles_wo_finds > 100 && !pending_not_fuzzed && min_wo_finds > 120) + strcpy(tmp, cLGN); + + /* Default: cautiously OK to stop? */ + else strcpy(tmp, cLBL); + + } + + SAYF(bV bSTOP " run time : " cRST "%-33s " bSTG bV bSTOP + " cycles done : %s%-5s " bSTG bV "\n", + DTD(cur_ms, start_time), tmp, DI(queue_cycle - 1)); + + /* We want to warn people about not seeing new paths after a full cycle, + except when resuming fuzzing or running in non-instrumented mode. */ + + if (!dumb_mode && (last_path_time || resuming_fuzz || queue_cycle == 1 || + in_bitmap || crash_mode)) { + + SAYF(bV bSTOP " last new path : " cRST "%-33s ", + DTD(cur_ms, last_path_time)); + + } else { + + if (dumb_mode) + + SAYF(bV bSTOP " last new path : " cPIN "n/a" cRST + " (non-instrumented mode) "); + + else + + SAYF(bV bSTOP " last new path : " cRST "none yet " cLRD + "(odd, check syntax!) "); + + } + + SAYF(bSTG bV bSTOP " total paths : " cRST "%-5s " bSTG bV "\n", + DI(queued_paths)); + + /* Highlight crashes in red if found, denote going over the KEEP_UNIQUE_CRASH + limit with a '+' appended to the count. */ + + sprintf(tmp, "%s%s", DI(unique_crashes), + (unique_crashes >= KEEP_UNIQUE_CRASH) ? "+" : ""); + + SAYF(bV bSTOP " last uniq crash : " cRST "%-33s " bSTG bV bSTOP + " uniq crashes : %s%-6s" bSTG bV "\n", + DTD(cur_ms, last_crash_time), unique_crashes ? cLRD : cRST, + tmp); + + sprintf(tmp, "%s%s", DI(unique_hangs), + (unique_hangs >= KEEP_UNIQUE_HANG) ? "+" : ""); + + SAYF(bV bSTOP " last uniq hang : " cRST "%-33s " bSTG bV bSTOP + " uniq hangs : " cRST "%-6s" bSTG bV "\n", + DTD(cur_ms, last_hang_time), tmp); + + SAYF(bVR bH bSTOP cCYA " cycle progress " bSTG bH10 bH5 bH2 bH2 bHB bH bSTOP cCYA + " map coverage " bSTG bH bHT bH20 bH2 bVL "\n"); + + /* This gets funny because we want to print several variable-length variables + together, but then cram them into a fixed-width field - so we need to + put them in a temporary buffer first. */ + + sprintf(tmp, "%s%s%u (%0.02f%%)", DI(current_entry), + queue_cur->favored ? "." : "*", queue_cur->fuzz_level, + ((double)current_entry * 100) / queued_paths); + + SAYF(bV bSTOP " now processing : " cRST "%-16s " bSTG bV bSTOP, tmp); + + sprintf(tmp, "%0.02f%% / %0.02f%%", ((double)queue_cur->bitmap_size) * + 100 / MAP_SIZE, t_byte_ratio); + + SAYF(" map density : %s%-21s" bSTG bV "\n", t_byte_ratio > 70 ? cLRD : + ((t_bytes < 200 && !dumb_mode) ? cPIN : cRST), tmp); + + sprintf(tmp, "%s (%0.02f%%)", DI(cur_skipped_paths), + ((double)cur_skipped_paths * 100) / queued_paths); + + SAYF(bV bSTOP " paths timed out : " cRST "%-16s " bSTG bV, tmp); + + sprintf(tmp, "%0.02f bits/tuple", + t_bytes ? (((double)t_bits) / t_bytes) : 0); + + SAYF(bSTOP " count coverage : " cRST "%-21s" bSTG bV "\n", tmp); + + SAYF(bVR bH bSTOP cCYA " stage progress " bSTG bH10 bH5 bH2 bH2 bX bH bSTOP cCYA + " findings in depth " bSTG bH10 bH5 bH2 bH2 bVL "\n"); + + sprintf(tmp, "%s (%0.02f%%)", DI(queued_favored), + ((double)queued_favored) * 100 / queued_paths); + + /* Yeah... it's still going on... halp? */ + + SAYF(bV bSTOP " now trying : " cRST "%-20s " bSTG bV bSTOP + " favored paths : " cRST "%-22s" bSTG bV "\n", stage_name, tmp); + + if (!stage_max) { + + sprintf(tmp, "%s/-", DI(stage_cur)); + + } else { + + sprintf(tmp, "%s/%s (%0.02f%%)", DI(stage_cur), DI(stage_max), + ((double)stage_cur) * 100 / stage_max); + + } + + SAYF(bV bSTOP " stage execs : " cRST "%-20s " bSTG bV bSTOP, tmp); + + sprintf(tmp, "%s (%0.02f%%)", DI(queued_with_cov), + ((double)queued_with_cov) * 100 / queued_paths); + + SAYF(" new edges on : " cRST "%-22s" bSTG bV "\n", tmp); + + sprintf(tmp, "%s (%s%s unique)", DI(total_crashes), DI(unique_crashes), + (unique_crashes >= KEEP_UNIQUE_CRASH) ? "+" : ""); + + if (crash_mode) { + + SAYF(bV bSTOP " total execs : " cRST "%-20s " bSTG bV bSTOP + " new crashes : %s%-22s" bSTG bV "\n", DI(total_execs), + unique_crashes ? cLRD : cRST, tmp); + + } else { + + SAYF(bV bSTOP " total execs : " cRST "%-20s " bSTG bV bSTOP + " total crashes : %s%-22s" bSTG bV "\n", DI(total_execs), + unique_crashes ? cLRD : cRST, tmp); + + } + + /* Show a warning about slow execution. */ + + if (avg_exec < 100) { + + sprintf(tmp, "%s/sec (%s)", DF(avg_exec), avg_exec < 20 ? + "zzzz..." : "slow!"); + + SAYF(bV bSTOP " exec speed : " cLRD "%-20s ", tmp); + + } else { + + sprintf(tmp, "%s/sec", DF(avg_exec)); + SAYF(bV bSTOP " exec speed : " cRST "%-20s ", tmp); + + } + + sprintf(tmp, "%s (%s%s unique)", DI(total_tmouts), DI(unique_tmouts), + (unique_hangs >= KEEP_UNIQUE_HANG) ? "+" : ""); + + SAYF (bSTG bV bSTOP " total tmouts : " cRST "%-22s" bSTG bV "\n", tmp); + + /* Aaaalmost there... hold on! */ + + SAYF(bVR bH cCYA bSTOP " fuzzing strategy yields " bSTG bH10 bHT bH10 + bH5 bHB bH bSTOP cCYA " path geometry " bSTG bH5 bH2 bVL "\n"); + + if (skip_deterministic) { + + strcpy(tmp, "n/a, n/a, n/a"); + + } else { + + sprintf(tmp, "%s/%s, %s/%s, %s/%s", + DI(stage_finds[STAGE_FLIP1]), DI(stage_cycles[STAGE_FLIP1]), + DI(stage_finds[STAGE_FLIP2]), DI(stage_cycles[STAGE_FLIP2]), + DI(stage_finds[STAGE_FLIP4]), DI(stage_cycles[STAGE_FLIP4])); + + } + + SAYF(bV bSTOP " bit flips : " cRST "%-36s " bSTG bV bSTOP " levels : " + cRST "%-10s" bSTG bV "\n", tmp, DI(max_depth)); + + if (!skip_deterministic) + sprintf(tmp, "%s/%s, %s/%s, %s/%s", + DI(stage_finds[STAGE_FLIP8]), DI(stage_cycles[STAGE_FLIP8]), + DI(stage_finds[STAGE_FLIP16]), DI(stage_cycles[STAGE_FLIP16]), + DI(stage_finds[STAGE_FLIP32]), DI(stage_cycles[STAGE_FLIP32])); + + SAYF(bV bSTOP " byte flips : " cRST "%-36s " bSTG bV bSTOP " pending : " + cRST "%-10s" bSTG bV "\n", tmp, DI(pending_not_fuzzed)); + + if (!skip_deterministic) + sprintf(tmp, "%s/%s, %s/%s, %s/%s", + DI(stage_finds[STAGE_ARITH8]), DI(stage_cycles[STAGE_ARITH8]), + DI(stage_finds[STAGE_ARITH16]), DI(stage_cycles[STAGE_ARITH16]), + DI(stage_finds[STAGE_ARITH32]), DI(stage_cycles[STAGE_ARITH32])); + + SAYF(bV bSTOP " arithmetics : " cRST "%-36s " bSTG bV bSTOP " pend fav : " + cRST "%-10s" bSTG bV "\n", tmp, DI(pending_favored)); + + if (!skip_deterministic) + sprintf(tmp, "%s/%s, %s/%s, %s/%s", + DI(stage_finds[STAGE_INTEREST8]), DI(stage_cycles[STAGE_INTEREST8]), + DI(stage_finds[STAGE_INTEREST16]), DI(stage_cycles[STAGE_INTEREST16]), + DI(stage_finds[STAGE_INTEREST32]), DI(stage_cycles[STAGE_INTEREST32])); + + SAYF(bV bSTOP " known ints : " cRST "%-36s " bSTG bV bSTOP " own finds : " + cRST "%-10s" bSTG bV "\n", tmp, DI(queued_discovered)); + + if (!skip_deterministic) + sprintf(tmp, "%s/%s, %s/%s, %s/%s", + DI(stage_finds[STAGE_EXTRAS_UO]), DI(stage_cycles[STAGE_EXTRAS_UO]), + DI(stage_finds[STAGE_EXTRAS_UI]), DI(stage_cycles[STAGE_EXTRAS_UI]), + DI(stage_finds[STAGE_EXTRAS_AO]), DI(stage_cycles[STAGE_EXTRAS_AO])); + + SAYF(bV bSTOP " dictionary : " cRST "%-36s " bSTG bV bSTOP + " imported : " cRST "%-10s" bSTG bV "\n", tmp, + sync_id ? DI(queued_imported) : (u8*)"n/a"); + + sprintf(tmp, "%s/%s, %s/%s, %s/%s", + DI(stage_finds[STAGE_HAVOC]), DI(stage_cycles[STAGE_HAVOC]), + DI(stage_finds[STAGE_SPLICE]), DI(stage_cycles[STAGE_SPLICE]), + DI(stage_finds[STAGE_PYTHON]), DI(stage_cycles[STAGE_PYTHON])); + + SAYF(bV bSTOP " havoc : " cRST "%-36s " bSTG bV bSTOP, tmp); + + if (t_bytes) sprintf(tmp, "%0.02f%%", stab_ratio); + else strcpy(tmp, "n/a"); + + SAYF(" stability : %s%-10s" bSTG bV "\n", (stab_ratio < 85 && var_byte_count > 40) + ? cLRD : ((queued_variable && (!persistent_mode || var_byte_count > 20)) + ? cMGN : cRST), tmp); + + if (!bytes_trim_out) { + + sprintf(tmp, "n/a, "); + + } else { + + sprintf(tmp, "%0.02f%%/%s, ", + ((double)(bytes_trim_in - bytes_trim_out)) * 100 / bytes_trim_in, + DI(trim_execs)); + + } + + if (!blocks_eff_total) { + + u8 tmp2[128]; + + sprintf(tmp2, "n/a"); + strcat(tmp, tmp2); + + } else { + + u8 tmp2[128]; + + sprintf(tmp2, "%0.02f%%", + ((double)(blocks_eff_total - blocks_eff_select)) * 100 / + blocks_eff_total); + + strcat(tmp, tmp2); + + } + if (custom_mutator) { + sprintf(tmp, "%s/%s", DI(stage_finds[STAGE_CUSTOM_MUTATOR]), DI(stage_cycles[STAGE_CUSTOM_MUTATOR])); + SAYF(bV bSTOP " custom mut. : " cRST "%-36s " bSTG bVR bH20 bH2 bH bRB "\n" + bLB bH30 bH20 bH2 bH bRB bSTOP cRST RESET_G1, tmp); + } else { + SAYF(bV bSTOP " trim : " cRST "%-36s " bSTG bVR bH20 bH2 bH bRB "\n" + bLB bH30 bH20 bH2 bRB bSTOP cRST RESET_G1, tmp); + } + + /* Provide some CPU utilization stats. */ + + if (cpu_core_count) { + + double cur_runnable = get_runnable_processes(); + u32 cur_utilization = cur_runnable * 100 / cpu_core_count; + + u8* cpu_color = cCYA; + + /* If we could still run one or more processes, use green. */ + + if (cpu_core_count > 1 && cur_runnable + 1 <= cpu_core_count) + cpu_color = cLGN; + + /* If we're clearly oversubscribed, use red. */ + + if (!no_cpu_meter_red && cur_utilization >= 150) cpu_color = cLRD; + +#ifdef HAVE_AFFINITY + + if (cpu_aff >= 0) { + + SAYF(SP10 cGRA "[cpu%03u:%s%3u%%" cGRA "]\r" cRST, + MIN(cpu_aff, 999), cpu_color, + MIN(cur_utilization, 999)); + + } else { + + SAYF(SP10 cGRA " [cpu:%s%3u%%" cGRA "]\r" cRST, + cpu_color, MIN(cur_utilization, 999)); + + } + +#else + + SAYF(SP10 cGRA " [cpu:%s%3u%%" cGRA "]\r" cRST, + cpu_color, MIN(cur_utilization, 999)); + +#endif /* ^HAVE_AFFINITY */ + + } else SAYF("\r"); + + /* Hallelujah! */ + + fflush(0); + +} + + +/* Display quick statistics at the end of processing the input directory, + plus a bunch of warnings. Some calibration stuff also ended up here, + along with several hardcoded constants. Maybe clean up eventually. */ + +void show_init_stats(void) { + + struct queue_entry* q = queue; + u32 min_bits = 0, max_bits = 0; + u64 min_us = 0, max_us = 0; + u64 avg_us = 0; + u32 max_len = 0; + + if (total_cal_cycles) avg_us = total_cal_us / total_cal_cycles; + + while (q) { + + if (!min_us || q->exec_us < min_us) min_us = q->exec_us; + if (q->exec_us > max_us) max_us = q->exec_us; + + if (!min_bits || q->bitmap_size < min_bits) min_bits = q->bitmap_size; + if (q->bitmap_size > max_bits) max_bits = q->bitmap_size; + + if (q->len > max_len) max_len = q->len; + + q = q->next; + + } + + SAYF("\n"); + + if (avg_us > ((qemu_mode || unicorn_mode) ? 50000 : 10000)) + WARNF(cLRD "The target binary is pretty slow! See %s/perf_tips.txt.", + doc_path); + + /* Let's keep things moving with slow binaries. */ + + if (avg_us > 50000) havoc_div = 10; /* 0-19 execs/sec */ + else if (avg_us > 20000) havoc_div = 5; /* 20-49 execs/sec */ + else if (avg_us > 10000) havoc_div = 2; /* 50-100 execs/sec */ + + if (!resuming_fuzz) { + + if (max_len > 50 * 1024) + WARNF(cLRD "Some test cases are huge (%s) - see %s/perf_tips.txt!", + DMS(max_len), doc_path); + else if (max_len > 10 * 1024) + WARNF("Some test cases are big (%s) - see %s/perf_tips.txt.", + DMS(max_len), doc_path); + + if (useless_at_start && !in_bitmap) + WARNF(cLRD "Some test cases look useless. Consider using a smaller set."); + + if (queued_paths > 100) + WARNF(cLRD "You probably have far too many input files! Consider trimming down."); + else if (queued_paths > 20) + WARNF("You have lots of input files; try starting small."); + + } + + OKF("Here are some useful stats:\n\n" + + cGRA " Test case count : " cRST "%u favored, %u variable, %u total\n" + cGRA " Bitmap range : " cRST "%u to %u bits (average: %0.02f bits)\n" + cGRA " Exec timing : " cRST "%s to %s us (average: %s us)\n", + queued_favored, queued_variable, queued_paths, min_bits, max_bits, + ((double)total_bitmap_size) / (total_bitmap_entries ? total_bitmap_entries : 1), + DI(min_us), DI(max_us), DI(avg_us)); + + if (!timeout_given) { + + /* Figure out the appropriate timeout. The basic idea is: 5x average or + 1x max, rounded up to EXEC_TM_ROUND ms and capped at 1 second. + + If the program is slow, the multiplier is lowered to 2x or 3x, because + random scheduler jitter is less likely to have any impact, and because + our patience is wearing thin =) */ + + if (avg_us > 50000) exec_tmout = avg_us * 2 / 1000; + else if (avg_us > 10000) exec_tmout = avg_us * 3 / 1000; + else exec_tmout = avg_us * 5 / 1000; + + exec_tmout = MAX(exec_tmout, max_us / 1000); + exec_tmout = (exec_tmout + EXEC_TM_ROUND) / EXEC_TM_ROUND * EXEC_TM_ROUND; + + if (exec_tmout > EXEC_TIMEOUT) exec_tmout = EXEC_TIMEOUT; + + ACTF("No -t option specified, so I'll use exec timeout of %u ms.", + exec_tmout); + + timeout_given = 1; + + } else if (timeout_given == 3) { + + ACTF("Applying timeout settings from resumed session (%u ms).", exec_tmout); + + } + + /* In dumb mode, re-running every timing out test case with a generous time + limit is very expensive, so let's select a more conservative default. */ + + if (dumb_mode && !getenv("AFL_HANG_TMOUT")) + hang_tmout = MIN(EXEC_TIMEOUT, exec_tmout * 2 + 100); + + OKF("All set and ready to roll!"); + +} + |