about summary refs log tree commit diff
diff options
context:
space:
mode:
authorvan Hauser <vh@thc.org>2020-08-13 21:27:11 +0200
committervan Hauser <vh@thc.org>2020-08-13 21:27:11 +0200
commit212bb990b7579831baad70735b767dbaf89e9e89 (patch)
treefa928bd9f9a0c8723d2db0566b1bd9352b06a1b7
parent8e984c2aa0100e6244fe6f215c88dd8b3bf3abc2 (diff)
downloadafl++-212bb990b7579831baad70735b767dbaf89e9e89.tar.gz
LTO: apply laf-intel+redqueen/cmplog at link time
-rw-r--r--GNUmakefile9
-rw-r--r--README.md27
-rw-r--r--TODO.md1
-rw-r--r--docs/Changelog.md2
-rw-r--r--llvm_mode/afl-clang-fast.c104
-rw-r--r--llvm_mode/cmplog-instructions-pass.cc4
-rw-r--r--llvm_mode/cmplog-routines-pass.cc4
-rw-r--r--llvm_mode/compare-transform-pass.so.cc3
-rw-r--r--llvm_mode/split-compares-pass.so.cc4
-rw-r--r--llvm_mode/split-switches-pass.so.cc4
-rw-r--r--src/afl-showmap.c104
11 files changed, 213 insertions, 53 deletions
diff --git a/GNUmakefile b/GNUmakefile
index fe5f8c03..f9020a90 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -97,7 +97,13 @@ ifneq "$(shell uname -m)" "x86_64"
  endif
 endif
 
-CFLAGS     ?= -O3 -funroll-loops $(CFLAGS_OPT)
+ifdef DEBUG
+  $(info Compiling DEBUG version of binaries)
+  CFLAGS += -ggdb3 -O0 -Wall -Wextra -Werror
+else
+  CFLAGS     ?= -O3 -funroll-loops $(CFLAGS_OPT)
+endif
+
 override CFLAGS += -g -Wno-pointer-sign -Wno-variadic-macros -Wall -Wextra -Wpointer-arith \
 			  -I include/ -DAFL_PATH=\"$(HELPER_PATH)\" \
 			  -DBIN_PATH=\"$(BIN_PATH)\" -DDOC_PATH=\"$(DOC_PATH)\"
@@ -305,6 +311,7 @@ help:
 	@echo "=========================================="
 	@echo STATIC - compile AFL++ static
 	@echo ASAN_BUILD - compiles with memory sanitizer for debug purposes
+	@echo DEBUG - no optimization, -ggdb3, all warnings and -Werror
 	@echo PROFILING - compile afl-fuzz with profiling information
 	@echo AFL_NO_X86 - if compiling on non-intel/amd platforms
 	@echo "=========================================="
diff --git a/README.md b/README.md
index 18983832..97c0a0d7 100644
--- a/README.md
+++ b/README.md
@@ -178,6 +178,7 @@ These build options exist:
 
 * STATIC - compile AFL++ static
 * ASAN_BUILD - compiles with memory sanitizer for debug purposes
+* DEBUG - no optimization, -ggdb3, all warnings and -Werror
 * PROFILING - compile with profiling information (gprof)
 * NO_PYTHON - disable python support
 * AFL_NO_X86 - if compiling on non-intel/amd platforms
@@ -509,8 +510,8 @@ fuzz your target.
 
 On the same machine - due to the design of how afl++ works - there is a maximum
 number of CPU cores/threads that are useful, use more and the overall performance
-degrades instead. This value depends on the target and the limit is between 48
-and 96 cores/threads per machine.
+degrades instead. This value depends on the target, and the limit is between 32
+and 64 cores/threads per machine.
 
 There should be one main fuzzer (`-M main` option) and as many secondary
 fuzzers (eg `-S variant1`) as you have cores that you use.
@@ -562,11 +563,18 @@ To have only the summary use the `-s` switch e.g.: `afl-whatsup -s output/`
 The `paths found` value is a bad indicator how good the coverage is.
 
 A better indicator - if you use default llvm instrumentation with at least
-version 9 - to use `afl-showmap` on the target with all inputs of the
-queue/ directory one after another and collecting the found edge IDs (`-o N.out`),
-removing the counters of the edge IDs, making them unique - and there you have
-the total number of found instrumented edges.
-
+version 9 - is to use `afl-showmap` with the collect coverage option `-C` on
+the output directory:
+```
+$ afl-showmap -C -i out -o /dev/null -- ./target -params @@
+...
+[*] Using SHARED MEMORY FUZZING feature.
+[*] Target map size: 9960
+[+] Processed 7849 input files.
+[+] Captured 4331 tuples (highest value 255, total values 67130596) in '/dev/nul
+l'.
+[+] A coverage of 4331 edges were achieved out of 9960 existing (43.48%) with 7849 input files.
+```
 It is even better to check out the exact lines of code that have been reached -
 and which have not been found so far.
 
@@ -580,6 +588,11 @@ then terminate it. The main node will pick it up and make it available to the
 other secondary nodes over time. Set `export AFL_NO_AFFINITY=1` if you have no
 free core.
 
+Note that you in nearly all cases you can never reach full coverage. A lot of
+functionality is usually behind options that were not activated or fuzz e.g.
+if you fuzz a library to convert image formats and your target is the png to
+tiff API then you will not touch any of the other library APIs and features.
+
 #### e) How long to fuzz a target?
 
 This is a difficult question.
diff --git a/TODO.md b/TODO.md
index f8ef0e0f..e74fa1d5 100644
--- a/TODO.md
+++ b/TODO.md
@@ -4,7 +4,6 @@
 
  - AFL_MAP_SIZE for qemu_mode and unicorn_mode
  - CPU affinity for many cores? There seems to be an issue > 96 cores
- - feature for afl-showmap to generate the coverage for all queue entries
  - afl-plot to support multiple plot_data
 
 ## Further down the road
diff --git a/docs/Changelog.md b/docs/Changelog.md
index 45d640ea..2c57448b 100644
--- a/docs/Changelog.md
+++ b/docs/Changelog.md
@@ -32,6 +32,8 @@ sending a mail to <afl-users+subscribe@googlegroups.com>.
      - skipping ctors and ifuncs for instrumentation
      - LTO: switch default to the dynamic memory map, set AFL_LLVM_MAP_ADDR
             for a fixed map address (eg. 0x10000)
+     - LTO: laf-intel and redqueen/cmplogare are now applied at link time
+            to prevent llvm optimizing away the splits
      - LTO: autodictionary mode is a default
      - LTO: instrim instrumentation disabled, only classic support used
             as it is always better
diff --git a/llvm_mode/afl-clang-fast.c b/llvm_mode/afl-clang-fast.c
index efaba122..10cb3fa3 100644
--- a/llvm_mode/afl-clang-fast.c
+++ b/llvm_mode/afl-clang-fast.c
@@ -246,33 +246,60 @@ static void edit_params(u32 argc, char **argv, char **envp) {
   // laf
   if (getenv("LAF_SPLIT_SWITCHES") || getenv("AFL_LLVM_LAF_SPLIT_SWITCHES")) {
 
-    cc_params[cc_par_cnt++] = "-Xclang";
-    cc_params[cc_par_cnt++] = "-load";
-    cc_params[cc_par_cnt++] = "-Xclang";
-    cc_params[cc_par_cnt++] =
-        alloc_printf("%s/split-switches-pass.so", obj_path);
+    if (lto_mode) {
+
+      cc_params[cc_par_cnt++] =
+          alloc_printf("-Wl,-mllvm=-load=%s/split-switches-pass.so", obj_path);
+
+    } else {
+
+      cc_params[cc_par_cnt++] = "-Xclang";
+      cc_params[cc_par_cnt++] = "-load";
+      cc_params[cc_par_cnt++] = "-Xclang";
+      cc_params[cc_par_cnt++] =
+          alloc_printf("%s/split-switches-pass.so", obj_path);
+
+    }
 
   }
 
   if (getenv("LAF_TRANSFORM_COMPARES") ||
       getenv("AFL_LLVM_LAF_TRANSFORM_COMPARES")) {
 
-    cc_params[cc_par_cnt++] = "-Xclang";
-    cc_params[cc_par_cnt++] = "-load";
-    cc_params[cc_par_cnt++] = "-Xclang";
-    cc_params[cc_par_cnt++] =
-        alloc_printf("%s/compare-transform-pass.so", obj_path);
+    if (lto_mode) {
+
+      cc_params[cc_par_cnt++] = alloc_printf(
+          "-Wl,-mllvm=-load=%s/compare-transform-pass.so", obj_path);
+
+    } else {
+
+      cc_params[cc_par_cnt++] = "-Xclang";
+      cc_params[cc_par_cnt++] = "-load";
+      cc_params[cc_par_cnt++] = "-Xclang";
+      cc_params[cc_par_cnt++] =
+          alloc_printf("%s/compare-transform-pass.so", obj_path);
+
+    }
 
   }
 
   if (getenv("LAF_SPLIT_COMPARES") || getenv("AFL_LLVM_LAF_SPLIT_COMPARES") ||
       getenv("AFL_LLVM_LAF_SPLIT_FLOATS")) {
 
-    cc_params[cc_par_cnt++] = "-Xclang";
-    cc_params[cc_par_cnt++] = "-load";
-    cc_params[cc_par_cnt++] = "-Xclang";
-    cc_params[cc_par_cnt++] =
-        alloc_printf("%s/split-compares-pass.so", obj_path);
+    if (lto_mode) {
+
+      cc_params[cc_par_cnt++] =
+          alloc_printf("-Wl,-mllvm=-load=%s/split-compares-pass.so", obj_path);
+
+    } else {
+
+      cc_params[cc_par_cnt++] = "-Xclang";
+      cc_params[cc_par_cnt++] = "-load";
+      cc_params[cc_par_cnt++] = "-Xclang";
+      cc_params[cc_par_cnt++] =
+          alloc_printf("%s/split-compares-pass.so", obj_path);
+
+    }
 
   }
 
@@ -282,24 +309,37 @@ static void edit_params(u32 argc, char **argv, char **envp) {
   unsetenv("AFL_LD_CALLER");
   if (cmplog_mode) {
 
-    cc_params[cc_par_cnt++] = "-Xclang";
-    cc_params[cc_par_cnt++] = "-load";
-    cc_params[cc_par_cnt++] = "-Xclang";
-    cc_params[cc_par_cnt++] =
-        alloc_printf("%s/cmplog-routines-pass.so", obj_path);
+    if (lto_mode) {
 
-    // reuse split switches from laf
-    cc_params[cc_par_cnt++] = "-Xclang";
-    cc_params[cc_par_cnt++] = "-load";
-    cc_params[cc_par_cnt++] = "-Xclang";
-    cc_params[cc_par_cnt++] =
-        alloc_printf("%s/split-switches-pass.so", obj_path);
+      cc_params[cc_par_cnt++] =
+          alloc_printf("-Wl,-mllvm=-load=%s/cmplog-routines-pass.so", obj_path);
+      cc_params[cc_par_cnt++] =
+          alloc_printf("-Wl,-mllvm=-load=%s/split-switches-pass.so", obj_path);
+      cc_params[cc_par_cnt++] = alloc_printf(
+          "-Wl,-mllvm=-load=%s/cmplog-instructions-pass.so", obj_path);
 
-    cc_params[cc_par_cnt++] = "-Xclang";
-    cc_params[cc_par_cnt++] = "-load";
-    cc_params[cc_par_cnt++] = "-Xclang";
-    cc_params[cc_par_cnt++] =
-        alloc_printf("%s/cmplog-instructions-pass.so", obj_path);
+    } else {
+
+      cc_params[cc_par_cnt++] = "-Xclang";
+      cc_params[cc_par_cnt++] = "-load";
+      cc_params[cc_par_cnt++] = "-Xclang";
+      cc_params[cc_par_cnt++] =
+          alloc_printf("%s/cmplog-routines-pass.so", obj_path);
+
+      // reuse split switches from laf
+      cc_params[cc_par_cnt++] = "-Xclang";
+      cc_params[cc_par_cnt++] = "-load";
+      cc_params[cc_par_cnt++] = "-Xclang";
+      cc_params[cc_par_cnt++] =
+          alloc_printf("%s/split-switches-pass.so", obj_path);
+
+      cc_params[cc_par_cnt++] = "-Xclang";
+      cc_params[cc_par_cnt++] = "-load";
+      cc_params[cc_par_cnt++] = "-Xclang";
+      cc_params[cc_par_cnt++] =
+          alloc_printf("%s/cmplog-instructions-pass.so", obj_path);
+
+    }
 
     cc_params[cc_par_cnt++] = "-fno-inline";
 
@@ -314,6 +354,7 @@ static void edit_params(u32 argc, char **argv, char **envp) {
 
     cc_params[cc_par_cnt++] = alloc_printf("-fuse-ld=%s", AFL_REAL_LD);
     cc_params[cc_par_cnt++] = "-Wl,--allow-multiple-definition";
+
     /*
         The current LTO instrim mode is not good, so we disable it
         if (instrument_mode == INSTRUMENT_CFG)
@@ -321,6 +362,7 @@ static void edit_params(u32 argc, char **argv, char **envp) {
               alloc_printf("-Wl,-mllvm=-load=%s/afl-llvm-lto-instrim.so",
        obj_path); else
     */
+
     cc_params[cc_par_cnt++] = alloc_printf(
         "-Wl,-mllvm=-load=%s/afl-llvm-lto-instrumentation.so", obj_path);
     cc_params[cc_par_cnt++] = lto_flag;
diff --git a/llvm_mode/cmplog-instructions-pass.cc b/llvm_mode/cmplog-instructions-pass.cc
index 7c48d906..6ad7dd06 100644
--- a/llvm_mode/cmplog-instructions-pass.cc
+++ b/llvm_mode/cmplog-instructions-pass.cc
@@ -284,3 +284,7 @@ static RegisterStandardPasses RegisterCmpLogInstructionsPass(
 static RegisterStandardPasses RegisterCmpLogInstructionsPass0(
     PassManagerBuilder::EP_EnabledOnOptLevel0, registerCmpLogInstructionsPass);
 
+static RegisterStandardPasses RegisterCmpLogInstructionsPassLTO(
+    PassManagerBuilder::EP_FullLinkTimeOptimizationLast,
+    registerCmpLogInstructionsPass);
+
diff --git a/llvm_mode/cmplog-routines-pass.cc b/llvm_mode/cmplog-routines-pass.cc
index a0f8f64f..7e5a1ca6 100644
--- a/llvm_mode/cmplog-routines-pass.cc
+++ b/llvm_mode/cmplog-routines-pass.cc
@@ -204,3 +204,7 @@ static RegisterStandardPasses RegisterCmpLogRoutinesPass(
 static RegisterStandardPasses RegisterCmpLogRoutinesPass0(
     PassManagerBuilder::EP_EnabledOnOptLevel0, registerCmpLogRoutinesPass);
 
+static RegisterStandardPasses RegisterCmpLogRoutinesPassLTO(
+    PassManagerBuilder::EP_FullLinkTimeOptimizationLast,
+    registerCmpLogRoutinesPass);
+
diff --git a/llvm_mode/compare-transform-pass.so.cc b/llvm_mode/compare-transform-pass.so.cc
index bed3597a..83885037 100644
--- a/llvm_mode/compare-transform-pass.so.cc
+++ b/llvm_mode/compare-transform-pass.so.cc
@@ -585,3 +585,6 @@ static RegisterStandardPasses RegisterCompTransPass(
 static RegisterStandardPasses RegisterCompTransPass0(
     PassManagerBuilder::EP_EnabledOnOptLevel0, registerCompTransPass);
 
+static RegisterStandardPasses RegisterCompTransPassLTO(
+    PassManagerBuilder::EP_FullLinkTimeOptimizationLast, registerCompTransPass);
+
diff --git a/llvm_mode/split-compares-pass.so.cc b/llvm_mode/split-compares-pass.so.cc
index 3630bd8c..ef3cf7f6 100644
--- a/llvm_mode/split-compares-pass.so.cc
+++ b/llvm_mode/split-compares-pass.so.cc
@@ -1342,3 +1342,7 @@ static RegisterStandardPasses RegisterSplitComparesPass(
 static RegisterStandardPasses RegisterSplitComparesTransPass0(
     PassManagerBuilder::EP_EnabledOnOptLevel0, registerSplitComparesPass);
 
+static RegisterStandardPasses RegisterSplitComparesTransPassLTO(
+    PassManagerBuilder::EP_FullLinkTimeOptimizationLast,
+    registerSplitComparesPass);
+
diff --git a/llvm_mode/split-switches-pass.so.cc b/llvm_mode/split-switches-pass.so.cc
index f025df77..3a464391 100644
--- a/llvm_mode/split-switches-pass.so.cc
+++ b/llvm_mode/split-switches-pass.so.cc
@@ -439,3 +439,7 @@ static RegisterStandardPasses RegisterSplitSwitchesTransPass(
 static RegisterStandardPasses RegisterSplitSwitchesTransPass0(
     PassManagerBuilder::EP_EnabledOnOptLevel0, registerSplitSwitchesTransPass);
 
+static RegisterStandardPasses RegisterSplitSwitchesTransPassLTO(
+    PassManagerBuilder::EP_FullLinkTimeOptimizationLast,
+    registerSplitSwitchesTransPass);
+
diff --git a/src/afl-showmap.c b/src/afl-showmap.c
index 0aa116e5..fa9eedc4 100644
--- a/src/afl-showmap.c
+++ b/src/afl-showmap.c
@@ -68,9 +68,11 @@ static char *stdin_file;               /* stdin file                        */
 static u8 *in_dir = NULL,              /* input folder                      */
     *out_file = NULL, *at_file = NULL;        /* Substitution string for @@ */
 
-static u8 *in_data;                    /* Input data                        */
+static u8 *in_data,                    /* Input data                        */
+    *coverage_map;                     /* Coverage map                      */
 
-static u32 total, highest;             /* tuple content information         */
+static u64 total;                      /* tuple content information         */
+static u32 tcnt, highest;              /* tuple content information         */
 
 static u32 in_len,                     /* Input data length                 */
     arg_offset;                        /* Total number of execs             */
@@ -83,7 +85,8 @@ static u8 quiet_mode,                  /* Hide non-essential messages?      */
     cmin_mode,                         /* Generate output in afl-cmin mode? */
     binary_mode,                       /* Write output as a binary map      */
     keep_cores,                        /* Allow coredumps?                  */
-    remove_shm = 1;                    /* remove shmem?                     */
+    remove_shm = 1,                    /* remove shmem?                     */
+    collect_coverage;                  /* collect coverage                  */
 
 static volatile u8 stop_soon,          /* Ctrl-C pressed?                   */
     child_crashed;                     /* Child crashed?                    */
@@ -175,6 +178,25 @@ static void at_exit_handler(void) {
 
 }
 
+/* Analyze results. */
+
+static void analyze_results(afl_forkserver_t *fsrv) {
+
+  u32 i;
+  for (i = 0; i < map_size; i++) {
+
+    if (fsrv->trace_bits[i]) {
+
+      total += fsrv->trace_bits[i];
+      if (fsrv->trace_bits[i] > highest) highest = fsrv->trace_bits[i];
+      if (!coverage_map[i]) { coverage_map[i] = 1; }
+
+    }
+
+  }
+
+}
+
 /* Write results. */
 
 static u32 write_results_to_file(afl_forkserver_t *fsrv, u8 *outfile) {
@@ -588,9 +610,14 @@ static void usage(u8 *argv0) {
       "                  (Not necessary, here for consistency with other afl-* "
       "tools)\n\n"
       "Other settings:\n"
-      "  -i dir        - process all files in this directory, -o must be a "
+      "  -i dir        - process all files in this directory, must be combined "
+      "with -o.\n"
+      "                  With -C, -o is a file, without -C it must be a "
       "directory\n"
       "                  and each bitmap will be written there individually.\n"
+      "  -C            - collect coverage, writes all edges to -o and gives a "
+      "summary\n"
+      "                  Must be combined with -i.\n"
       "  -q            - sink program's output and don't show messages\n"
       "  -e            - show edge coverage only, ignore hit counts\n"
       "  -r            - show real tuple values instead of AFL filter values\n"
@@ -624,7 +651,6 @@ int main(int argc, char **argv_orig, char **envp) {
 
   s32    opt, i;
   u8     mem_limit_given = 0, timeout_given = 0, unicorn_mode = 0, use_wine = 0;
-  u32    tcnt = 0;
   char **use_argv;
 
   char **argv = argv_cpy_dup(argc, argv_orig);
@@ -639,10 +665,14 @@ int main(int argc, char **argv_orig, char **envp) {
 
   if (getenv("AFL_QUIET") != NULL) { be_quiet = 1; }
 
-  while ((opt = getopt(argc, argv, "+i:o:f:m:t:A:eqZQUWbcrh")) > 0) {
+  while ((opt = getopt(argc, argv, "+i:o:f:m:t:A:eqCZQUWbcrh")) > 0) {
 
     switch (opt) {
 
+      case 'C':
+        collect_coverage = 1;
+        break;
+
       case 'i':
         if (in_dir) { FATAL("Multiple -i options not supported"); }
         in_dir = optarg;
@@ -820,6 +850,13 @@ int main(int argc, char **argv_orig, char **envp) {
 
   if (optind == argc || !out_file) { usage(argv[0]); }
 
+  if (in_dir) {
+
+    if (!out_file && !collect_coverage)
+      FATAL("for -i you need to specify either -C and/or -o");
+
+  }
+
   if (fsrv->qemu_mode && !mem_limit_given) { fsrv->mem_limit = MEM_LIMIT_QEMU; }
   if (unicorn_mode && !mem_limit_given) { fsrv->mem_limit = MEM_LIMIT_UNICORN; }
 
@@ -910,7 +947,7 @@ int main(int argc, char **argv_orig, char **envp) {
 
   if (in_dir) {
 
-    DIR *          dir_in, *dir_out;
+    DIR *          dir_in, *dir_out = NULL;
     struct dirent *dir_ent;
     int            done = 0;
     u8             infile[PATH_MAX], outfile[PATH_MAX];
@@ -924,20 +961,43 @@ int main(int argc, char **argv_orig, char **envp) {
     fsrv->dev_null_fd = open("/dev/null", O_RDWR);
     if (fsrv->dev_null_fd < 0) { PFATAL("Unable to open /dev/null"); }
 
+    // if a queue subdirectory exists switch to that
+    u8 *dn = alloc_printf("%s/queue", in_dir);
+    if ((dir_in = opendir(in_dir))) {
+
+      closedir(dir_in);
+      in_dir = dn;
+
+    } else
+
+      ck_free(dn);
+    if (!be_quiet) ACTF("Reading from directory '%s'...", in_dir);
+
     if (!(dir_in = opendir(in_dir))) {
 
       PFATAL("cannot open directory %s", in_dir);
 
     }
 
-    if (!(dir_out = opendir(out_file))) {
+    if (!collect_coverage) {
+
+      if (!(dir_out = opendir(out_file))) {
+
+        if (mkdir(out_file, 0700)) {
 
-      if (mkdir(out_file, 0700)) {
+          PFATAL("cannot create output directory %s", out_file);
 
-        PFATAL("cannot create output directory %s", out_file);
+        }
 
       }
 
+    } else {
+
+      if ((coverage_map = (u8 *)malloc(map_size)) == NULL)
+        FATAL("coult not grab memory");
+      edges_only = 0;
+      raw_instr_output = 1;
+
     }
 
     u8 *use_dir = ".";
@@ -978,6 +1038,7 @@ int main(int argc, char **argv_orig, char **envp) {
 
     afl_fsrv_start(fsrv, use_argv, &stop_soon,
                    get_afl_env("AFL_DEBUG_CHILD_OUTPUT") ? 1 : 0);
+    map_size = fsrv->map_size;
 
     if (fsrv->support_shmem_fuzz && !fsrv->use_shmem_fuzz)
       shm_fuzz = deinit_shmem(fsrv, shm_fuzz);
@@ -1005,7 +1066,8 @@ int main(int argc, char **argv_orig, char **envp) {
       if (-1 == stat(infile, &statbuf) || !S_ISREG(statbuf.st_mode)) continue;
 #endif
 
-      snprintf(outfile, sizeof(outfile), "%s/%s", out_file, dir_ent->d_name);
+      if (!collect_coverage)
+        snprintf(outfile, sizeof(outfile), "%s/%s", out_file, dir_ent->d_name);
 
       if (read_file(infile)) {
 
@@ -1019,7 +1081,10 @@ int main(int argc, char **argv_orig, char **envp) {
 
         showmap_run_target_forkserver(fsrv, in_data, in_len);
         ck_free(in_data);
-        tcnt = write_results_to_file(fsrv, outfile);
+        if (collect_coverage)
+          analyze_results(fsrv);
+        else
+          tcnt = write_results_to_file(fsrv, outfile);
 
       }
 
@@ -1030,6 +1095,13 @@ int main(int argc, char **argv_orig, char **envp) {
     closedir(dir_in);
     if (dir_out) { closedir(dir_out); }
 
+    if (collect_coverage) {
+
+      memcpy(fsrv->trace_bits, coverage_map, map_size);
+      tcnt = write_results_to_file(fsrv, out_file);
+
+    }
+
   } else {
 
     if (fsrv->support_shmem_fuzz && !fsrv->use_shmem_fuzz)
@@ -1043,8 +1115,14 @@ int main(int argc, char **argv_orig, char **envp) {
   if (!quiet_mode) {
 
     if (!tcnt) { FATAL("No instrumentation detected" cRST); }
-    OKF("Captured %u tuples (highest value %u, total values %u) in '%s'." cRST,
+    OKF("Captured %u tuples (highest value %u, total values %llu) in "
+        "'%s'." cRST,
         tcnt, highest, total, out_file);
+    if (collect_coverage)
+      OKF("A coverage of %u edges were achieved out of %u existing (%.02f%%) "
+          "with %llu input files.",
+          tcnt, map_size, ((float)tcnt * 100) / (float)map_size,
+          fsrv->total_execs);
 
   }