use gcov to measure the code coverage and return the measured data back
to the host

Change-Id: Ia9393ccb8dd3f3321f545abb9d20a6d37de5765f
diff --git a/sysfuzzer/common/Android.mk b/sysfuzzer/common/Android.mk
index 4f1ddbc..2f5b005 100644
--- a/sysfuzzer/common/Android.mk
+++ b/sysfuzzer/common/Android.mk
@@ -39,6 +39,7 @@
   system/extras \
   external/protobuf/src \
   frameworks/native/include \
+  test/vts/sysfuzzer/libcodecoverage \
   system/core/include
 
 LOCAL_SHARED_LIBRARIES := \
@@ -48,6 +49,7 @@
   liblog \
   libdl \
   libandroid_runtime \
+  libvts_codecoverage \
 
 LOCAL_PROTOC_OPTIMIZE_TYPE := full
 
diff --git a/sysfuzzer/common/component_loader/DllLoader.cpp b/sysfuzzer/common/component_loader/DllLoader.cpp
index a13745e..8dc3966 100644
--- a/sysfuzzer/common/component_loader/DllLoader.cpp
+++ b/sysfuzzer/common/component_loader/DllLoader.cpp
@@ -109,5 +109,50 @@
   return func;
 }
 
+bool DllLoader::SancovResetCoverage() {
+  const char* error;
+  void (*func)();
+
+  func = (void (*)()) dlsym(handle_, "__sanitizer_reset_coverage");
+  if ((error = dlerror()) != NULL)  {
+    fputs(error, stderr);
+    cerr << __FUNCTION__ << ": Can't find __sanitizer_reset_coverage" << endl;
+    return false;
+  }
+  func();
+  return true;
+}
+
+
+bool DllLoader::GcovInit(writeout_fn wfn, flush_fn ffn) {
+  const char* error;
+  void (*func)(writeout_fn, flush_fn);
+
+  func = (void (*)(writeout_fn, flush_fn)) dlsym(handle_, "llvm_gcov_init");
+  if ((error = dlerror()) != NULL)  {
+    fputs(error, stderr);
+    cerr << __FUNCTION__ << ": Can't find llvm_gcov_init" << endl;
+    return false;
+  }
+  func(wfn, ffn);
+  return true;
+}
+
+
+bool DllLoader::GcovFlush() {
+  const char* error;
+  void (*func)();
+
+  func = (void (*)()) dlsym(handle_, "__gcov_flush");
+  if ((error = dlerror()) != NULL)  {
+    fputs(error, stderr);
+    cerr << __FUNCTION__ << ": Can't find __gcov_flush" << endl;
+    return false;
+  }
+  func();
+
+  return true;
+}
+
 }  // namespace vts
 }  // namespace android
diff --git a/sysfuzzer/common/component_loader/DllLoader.h b/sysfuzzer/common/component_loader/DllLoader.h
index 1db0624..334ce3e 100644
--- a/sysfuzzer/common/component_loader/DllLoader.h
+++ b/sysfuzzer/common/component_loader/DllLoader.h
@@ -28,6 +28,8 @@
 
 // Pointer type for a function in a loaded component.
 typedef FuzzerBase* (*loader_function)();
+typedef void (*writeout_fn)();
+typedef void (*flush_fn)();
 
 
 // Component loader implementation for a DLL file.
@@ -48,6 +50,15 @@
   // Returns NULL if not found.
   loader_function GetLoaderFunction(const char* function_name);
 
+  // (for sancov) Reset coverage data.
+  bool SancovResetCoverage();
+
+  // (for gcov) initialize.
+  bool GcovInit(writeout_fn wfn, flush_fn ffn);
+
+  // (for gcov) flush to file(s).
+  bool GcovFlush();
+
  private:
   // pointer to a handle of the loaded DLL file.
   void* handle_;
diff --git a/sysfuzzer/common/fuzz_tester/FuzzerBase.cpp b/sysfuzzer/common/fuzz_tester/FuzzerBase.cpp
index 5a7513f..f1b5915 100644
--- a/sysfuzzer/common/fuzz_tester/FuzzerBase.cpp
+++ b/sysfuzzer/common/fuzz_tester/FuzzerBase.cpp
@@ -16,27 +16,263 @@
 
 #include "fuzz_tester/FuzzerBase.h"
 
-#include <string>
+#include <dirent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
 #include <iostream>
+#include <string>
+#include <vector>
 
 #include "test/vts/sysfuzzer/common/proto/InterfaceSpecificationMessage.pb.h"
 
 #include "component_loader/DllLoader.h"
 #include "utils/InterfaceSpecUtil.h"
 
+#include "gcda_parser.h"
+
 using namespace std;
 using namespace android;
 
+#define USE_GCOV 1
+
+#if SANCOV
+extern "C" {
+typedef unsigned long uptr;
+
+#define SANITIZER_INTERFACE_ATTRIBUTE __attribute__((visibility("default")))
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_cov(uint32_t *guard) {
+  printf("sancov\n");
+  coverage_data.Add(StackTrace::GetPreviousInstructionPc(GET_CALLER_PC()),
+                    guard);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void *__asan_memset(void *block, int c, uptr size) {
+  ASAN_MEMSET_IMPL(nullptr, block, c, size);
+  return block;
+}
+
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __asan_register_globals() { }
+SANITIZER_INTERFACE_ATTRIBUTE
+void __asan_unregister_globals() { }
+SANITIZER_INTERFACE_ATTRIBUTE
+void __asan_handle_no_return() { }
+SANITIZER_INTERFACE_ATTRIBUTE
+void __asan_report_load1() { }
+SANITIZER_INTERFACE_ATTRIBUTE
+void __asan_report_load2() { }
+SANITIZER_INTERFACE_ATTRIBUTE
+void __asan_report_load4() { }
+SANITIZER_INTERFACE_ATTRIBUTE
+void __asan_report_load8() { }
+SANITIZER_INTERFACE_ATTRIBUTE
+void __asan_report_load16() { }
+SANITIZER_INTERFACE_ATTRIBUTE
+void __asan_report_store1() { }
+SANITIZER_INTERFACE_ATTRIBUTE
+void __asan_report_store2() { }
+SANITIZER_INTERFACE_ATTRIBUTE
+void __asan_report_store4() { }
+SANITIZER_INTERFACE_ATTRIBUTE
+void __asan_report_store8() { }
+SANITIZER_INTERFACE_ATTRIBUTE
+void __asan_report_store16() { }
+SANITIZER_INTERFACE_ATTRIBUTE
+void __asan_set_error_report_callback() { }
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __asan_stack_malloc_1(uptr size, uptr real_stack) {
+  return (uptr)malloc(size);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __asan_stack_malloc_2(uptr size, uptr real_stack) {
+  return (uptr)malloc(size);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __asan_stack_malloc_3(uptr size, uptr real_stack) {
+  return (uptr)malloc(size);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __asan_stack_malloc_4(uptr size, uptr real_stack) {
+  return (uptr)malloc(size);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __asan_stack_malloc_5(uptr size, uptr real_stack) {
+  return (uptr)malloc(size);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __asan_stack_malloc_6(uptr size, uptr real_stack) {
+  return (uptr)malloc(size);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __asan_stack_malloc_7(uptr size, uptr real_stack) {
+  return (uptr)malloc(size);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __asan_stack_malloc_8(uptr size, uptr real_stack) {
+  return(uptr) malloc(size);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __asan_stack_malloc_9(uptr size, uptr real_stack) {
+  return (uptr)malloc(size);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __asan_stack_malloc_10(uptr size, uptr real_stack) {
+  return (uptr)malloc(size);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void  __asan_stack_free_1(uptr ptr, uptr size, uptr real_stack) {
+  free((void*)ptr);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void  __asan_stack_free_2(uptr ptr, uptr size, uptr real_stack) {
+  free((void*)ptr);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void  __asan_stack_free_3(uptr ptr, uptr size, uptr real_stack) {
+  free((void*)ptr);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void  __asan_stack_free_4(uptr ptr, uptr size, uptr real_stack) {
+  free((void*)ptr);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void  __asan_stack_free_5(uptr ptr, uptr size, uptr real_stack) {
+  free((void*)ptr);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void  __asan_stack_free_6(uptr ptr, uptr size, uptr real_stack) {
+  free((void*)ptr);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void  __asan_stack_free_7(uptr ptr, uptr size, uptr real_stack) {
+  free((void*)ptr);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void  __asan_stack_free_8(uptr ptr, uptr size, uptr real_stack) {
+  free((void*)ptr);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void  __asan_stack_free_9(uptr ptr, uptr size, uptr real_stack) {
+  free((void*)ptr);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void  __asan_stack_free_10(uptr ptr, uptr size, uptr real_stack) {
+  free((void*)ptr);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __asan_version_mismatch_check_v6() {
+  // Do nothing.
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE void
+__sanitizer_cov_module_init(int32_t *guards, uptr npcs, uint8_t *counters,
+                            const char *comp_unit_name) {
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __asan_init() {
+  static int inited = 0;
+  if (inited) return;
+  inited = 1;
+#if __WORDSIZE == 64
+  unsigned long start = 0x100000000000;
+  unsigned long size  = 0x100000000000;
+#else
+  unsigned long start = 0x20000000;
+  unsigned long size = 0x20000000;
+#endif
+  void *res = mmap((void*)start, size,
+                   PROT_READ | PROT_WRITE,
+                   MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE,
+                   0, 0);
+  if (res == (void*)start) {
+    fprintf(stderr, "Fake AddressSanitizer run-time initialized ok at %p\n",
+            res);
+  } else {
+    fprintf(stderr, "Fake AddressSanitizer run-time failed to initialize.\n"
+            "You have been warned. Aborting.");
+    abort();
+  }
+}
+
+}
+#endif
+
 namespace android {
 namespace vts {
 
+static void RemoveDir(char *path) {
+  struct dirent* entry = NULL;
+  DIR* dir = opendir(path);
+
+  while ((entry = readdir(dir)) != NULL) {
+    DIR* sub_dir = NULL;
+    FILE* file = NULL;
+    char abs_path[4096] = {0};
+
+    if (*(entry->d_name) != '.') {
+      sprintf(abs_path, "%s/%s", path, entry->d_name);
+      if ((sub_dir = opendir(abs_path)) != NULL) {
+        closedir(sub_dir);
+        RemoveDir(abs_path);
+      } else if((file = fopen(abs_path, "r")) != NULL) {
+        fclose(file);
+        remove(abs_path);
+      }
+    }
+  }
+  remove(path);
+}
+
+
 FuzzerBase::FuzzerBase(int target_class)
-    : target_class_(target_class) {}
+    : target_class_(target_class),
+      component_filename_(NULL),
+      gcov_output_basepath_(NULL) {}
 
 
-FuzzerBase::~FuzzerBase() {}
+FuzzerBase::~FuzzerBase() {
+  free(component_filename_);
+}
 
 
+void wfn() {
+  cout << "wfn" << endl;
+}
+
+void ffn() {
+  cout << "ffn" << endl;
+}
+
 bool FuzzerBase::LoadTargetComponent(
     const char* target_dll_path, const char* module_name) {
   cout << __FUNCTION__ << ":" << __LINE__ << " " << "LoadTargetCompooent entry" << endl;
@@ -47,6 +283,28 @@
   cout << __FUNCTION__ << ": loaded a non-legacy HAL file." << endl;
   device_ = target_loader_.GetHWDevice(module_name);
   cout << __FUNCTION__ << ": device_ " << device_ << endl;
+#if SANCOV
+  cout << __FUNCTION__ << "sancov reset " << target_loader_.SancovResetCoverage() << endl;;
+#endif
+
+  if (target_dll_path_) {
+    string target_path(target_dll_path_);
+
+    size_t offset = target_path.rfind("/", target_path.length());
+    if (offset != string::npos) {
+      string filename = target_path.substr(offset + 1,
+                                           target_path.length() - offset);
+      filename = filename.substr(0, filename.length() - 3 /* for .so */);
+      component_filename_ = (char*) malloc(filename.length() + 1);
+      strcpy(component_filename_, filename.c_str());
+      cout << "module file name: " << component_filename_ << endl;
+    }
+    cout << "module_name " << target_dll_path_ << endl;
+  }
+
+#if USE_GCOV
+  cout << __FUNCTION__ << ": gcov init " << target_loader_.GcovInit(wfn, ffn) << endl;
+#endif
   return (device_ != NULL);
 }
 
@@ -66,5 +324,156 @@
   return true;
 }
 
+
+void FuzzerBase::FunctionCallBegin() {
+  char product_path[4096];
+  char product[128];
+  char module_basepath[4096];
+  char cwd[4096];
+
+  if (getcwd(cwd, 4096)) {
+    cout << cwd << endl;
+    int n = snprintf(product_path, 4096, "%s/out/target/product", cwd);
+    if (n <= 0 || n >= 4096) {
+      cerr << "couln't get product_path" << endl;
+      return;
+    }
+    DIR* srcdir = opendir(product_path);
+    if (!srcdir) {
+      cerr << "couln't open " << product_path << endl;
+      return;
+    }
+
+    int dir_count = 0;
+    struct dirent* dent;
+    while ((dent = readdir(srcdir)) != NULL) {
+      struct stat st;
+      if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0) {
+        continue;
+      }
+      if (fstatat(dirfd(srcdir), dent->d_name, &st, 0) < 0) {
+        cerr << "error " << dent->d_name << endl;
+        continue;
+      }
+      if (S_ISDIR(st.st_mode)) {
+        cout << dent->d_name << endl;
+        strcpy(product, dent->d_name);
+        dir_count++;
+      }
+    }
+    closedir(srcdir);
+    if (dir_count != 1) {
+      cerr << "more than one product dir found." << endl;
+      return;
+    }
+
+    n = snprintf(module_basepath, 4096, "%s/%s/obj/SHARED_LIBRARIES",
+                 product_path, product);
+    if (n <= 0 || n >= 4096) {
+      cerr << "couln't get module_basepath" << endl;
+      return;
+    }
+    srcdir = opendir(module_basepath);
+    if (!srcdir) {
+      cerr << "couln't open " << module_basepath << endl;
+      return;
+    }
+
+    dir_count = 0;
+    string target = string(component_filename_) + "_intermediates";
+    bool hit = false;
+    while ((dent = readdir(srcdir)) != NULL) {
+      struct stat st;
+      if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0) {
+        continue;
+      }
+      if (fstatat(dirfd(srcdir), dent->d_name, &st, 0) < 0) {
+        cerr << "error " << dent->d_name << endl;
+        continue;
+      }
+      if (S_ISDIR(st.st_mode)) {
+        cout << "module_basepath " << string(dent->d_name) << endl;
+        if (string(dent->d_name) == target) {
+          cout << "hit" << endl;
+          hit = true;
+        }
+        dir_count++;
+      }
+    }
+    if (hit) {
+      if (gcov_output_basepath_) {
+        free(gcov_output_basepath_);
+      }
+      gcov_output_basepath_ = (char*) malloc(strlen(module_basepath) + target.length() + 2);
+      if (!gcov_output_basepath_) {
+        cerr << __FUNCTION__ << ": couldn't alloc memory" << endl;
+        return;
+      }
+      sprintf(gcov_output_basepath_, "%s/%s", module_basepath, target.c_str());
+      RemoveDir(gcov_output_basepath_);
+    }
+    closedir(srcdir);
+  } else {
+    cerr << __FUNCTION__ << ": couldn't get the pwd." << endl;
+  }
+}
+
+
+vector<unsigned>* FuzzerBase::FunctionCallEnd() {
+  cout << __FUNCTION__ << ": gcov flush " << endl;
+  std::vector<unsigned>* result = NULL;
+#if USE_GCOV
+  target_loader_.GcovFlush();
+  // find the file.
+  if (!gcov_output_basepath_) {
+    cerr << __FUNCTION__ << ": no gcov basepath set" << endl;
+    return NULL;
+  }
+  DIR* srcdir = opendir(gcov_output_basepath_);
+  if (!srcdir) {
+    cerr << "couln't open " << gcov_output_basepath_ << endl;
+    return NULL;
+  }
+
+  int dir_count = 0;
+  struct dirent* dent;
+  while ((dent = readdir(srcdir)) != NULL) {
+    struct stat st;
+    if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0) {
+      continue;
+    }
+    if (fstatat(dirfd(srcdir), dent->d_name, &st, 0) < 0) {
+      cerr << "error " << dent->d_name << endl;
+      continue;
+    }
+    if (!S_ISDIR(st.st_mode)) {
+      cout << dent->d_name << endl;
+      if (string(dent->d_name).rfind(".gcda") != string::npos) {
+        char* buffer;
+        buffer = (char*) malloc(strlen(gcov_output_basepath_) + strlen(dent->d_name) + 2);
+        if (!buffer) {
+          cerr << __FUNCTION__ << ": OOM" << endl;
+          closedir(srcdir);
+          return NULL;
+        }
+        sprintf(buffer, "%s/%s", gcov_output_basepath_, dent->d_name);
+        result = android::vts::parse_gcda_file(buffer);
+#if USE_GCOV_DEBUG
+        if (result) {
+          for (unsigned int index = 0; index < result->size(); index++) {
+            cout << result->at(index) << endl;
+          }
+        }
+#endif
+        free(buffer);
+        break;
+      }
+    }
+  }
+  closedir(srcdir);
+#endif
+  return result;
+}
+
 }  // namespace vts
 }  // namespace android
diff --git a/sysfuzzer/common/fuzz_tester/FuzzerBase.h b/sysfuzzer/common/fuzz_tester/FuzzerBase.h
index 77a33b1..6e1c8a2 100644
--- a/sysfuzzer/common/fuzz_tester/FuzzerBase.h
+++ b/sysfuzzer/common/fuzz_tester/FuzzerBase.h
@@ -49,6 +49,13 @@
     return false;
   };
 
+  // Called before calling a target function.
+  void FunctionCallBegin();
+
+  // Called after calling a target function. Returns a vector which contains
+  // the code coverage info.
+  vector<unsigned>* FunctionCallEnd();
+
  protected:
   // a pointer to a HAL data structure of the loaded component.
   struct hw_device_t* device_;
@@ -65,6 +72,12 @@
 
   // target class
   const int target_class_;
+
+  // target component file name (without extension)
+  char* component_filename_;
+
+  // path to store the gcov output files.
+  char* gcov_output_basepath_;
 };
 
 }  // namespace vts
diff --git a/sysfuzzer/common/proto/InterfaceSpecificationMessage.proto b/sysfuzzer/common/proto/InterfaceSpecificationMessage.proto
index dbe4a26..56417ab 100644
--- a/sysfuzzer/common/proto/InterfaceSpecificationMessage.proto
+++ b/sysfuzzer/common/proto/InterfaceSpecificationMessage.proto
@@ -78,6 +78,12 @@
 
   // a specification of the call flows of the function.
   repeated CallFlowSpecificationMessage callflow = 31;
+
+  // profiling data.
+  repeated float profiling_data = 101;
+
+  // coverage measurement data.
+  repeated uint32 coverage_data = 201;
 }
 
 
diff --git a/sysfuzzer/common/specification_parser/SpecificationBuilder.cpp b/sysfuzzer/common/specification_parser/SpecificationBuilder.cpp
index b216115..72ef0c9 100644
--- a/sysfuzzer/common/specification_parser/SpecificationBuilder.cpp
+++ b/sysfuzzer/common/specification_parser/SpecificationBuilder.cpp
@@ -167,7 +167,8 @@
 
 const string empty_string = string();
 
-const string& SpecificationBuilder::CallFunction(FunctionSpecificationMessage* func_msg) {
+const string& SpecificationBuilder::CallFunction(
+    FunctionSpecificationMessage* func_msg) {
   if (!wrapper_.LoadInterfaceSpecificationLibrary(spec_lib_file_path_)) {
     return empty_string;
   }
@@ -182,8 +183,19 @@
   }
 
   void* result;
+  func_fuzzer->FunctionCallBegin();
   cout << "Call Function " << func_msg->name() << endl;
   func_fuzzer->Fuzz(*func_msg, &result);
+  cout << __FUNCTION__ << ": called" << endl;
+
+  // set coverage data.
+  vector<unsigned>* coverage = func_fuzzer->FunctionCallEnd();
+  if (coverage && coverage->size() > 0) {
+    for (unsigned int index = 0; index < coverage->size(); index++) {
+      func_msg->mutable_coverage_data()->Add(coverage->at(index));
+    }
+  }
+
   if (func_msg->return_type().aggregate_type().size() > 0) {
     // TODO: actually handle this case.
     if (result != NULL) {
@@ -193,7 +205,10 @@
     } else {
       cout << __FUNCTION__ << " return value = NULL" << endl;
     }
-    return *(new string("todo: support aggregate"));
+    cerr << __FUNCTION__ << " todo: support aggregate" << endl;
+    string* output = new string();
+    google::protobuf::TextFormat::PrintToString(*func_msg, output);
+    return *output;
   } else if (func_msg->return_type().primitive_type().size() > 0) {
     // TODO handle when the size > 1.
     if (!strcmp(func_msg->return_type().primitive_type(0).c_str(), "int32_t")) {
@@ -202,8 +217,7 @@
       cout << "result " << endl;
       // todo handle more types;
       string* output = new string();
-      google::protobuf::TextFormat::PrintToString(func_msg->return_type(),
-                                                  output);
+      google::protobuf::TextFormat::PrintToString(*func_msg, output);
       return *output;
     }
   }
diff --git a/sysfuzzer/framework/VtsFuzzerMain.cpp b/sysfuzzer/framework/VtsFuzzerMain.cpp
index 7bda41b..9eb1cf2 100644
--- a/sysfuzzer/framework/VtsFuzzerMain.cpp
+++ b/sysfuzzer/framework/VtsFuzzerMain.cpp
@@ -19,6 +19,10 @@
  *  $ fuzzer --class=hal --type=light --version=1.0 /system/lib64/hw/lights.angler.so
  *  $ fuzzer --class=hal --type=gps --version=1.0 /system/lib64/hw/gps.msm8994.so
  *
+ *  $ LD_LIBRARY_PATH=/data/local/tmp ./fuzzer --class=hal --type=light \
+ *    --version=1.0 --spec_dir=/data/local/tmp/spec \
+ *    /data/local/tmp/hw64/lights.bullhead-vts.so
+ *
  * Example usage (for GCE virtual devices):
  *  $ fuzzer --class=hal --type=light --version=1.0 /system/lib/hw/lights.gce_x86.so
  *  $ fuzzer --class=hal --type=gps --version=1.0 /system/lib/hw/gps.gce_x86.so
diff --git a/sysfuzzer/libcodecoverage/Android.mk b/sysfuzzer/libcodecoverage/Android.mk
new file mode 100644
index 0000000..85b3726
--- /dev/null
+++ b/sysfuzzer/libcodecoverage/Android.mk
@@ -0,0 +1,35 @@
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# 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
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libvts_codecoverage
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := \
+  gcda_parser.cpp \
+  gcov_basic_io.cpp \
+
+LOCAL_C_INCLUDES := \
+  bionic \
+  libcore \
+
+LOCAL_SHARED_LIBRARIES := \
+  libcutils \
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/sysfuzzer/libcodecoverage/gcda_parser.cpp b/sysfuzzer/libcodecoverage/gcda_parser.cpp
new file mode 100644
index 0000000..b0b27a5
--- /dev/null
+++ b/sysfuzzer/libcodecoverage/gcda_parser.cpp
@@ -0,0 +1,160 @@
+#include "gcda_parser.h"
+
+#include <vector>
+#include <iostream>
+
+#include "gcov_basic_io.h"
+
+using namespace std;
+
+namespace android {
+namespace vts {
+
+static const tag_format_t tag_table[] = {
+  {0, "NOP", NULL},
+  {0, "UNKNOWN", NULL},
+  {0, "COUNTERS", tag_counters},
+  {GCOV_TAG_FUNCTION, "FUNCTION", tag_function},
+  {GCOV_TAG_BLOCKS, "BLOCKS", tag_blocks},
+  {GCOV_TAG_ARCS, "ARCS", tag_arcs},
+  {GCOV_TAG_LINES, "LINES", tag_lines},
+  {0, NULL, NULL}
+};
+
+
+void tag_counters(const char* filename, unsigned tag, unsigned length,
+                  vector<unsigned>* result) {
+  unsigned n_counts = GCOV_TAG_COUNTER_NUM (length);
+  printf("%s: %d counts\n", __FUNCTION__, n_counts);
+}
+
+
+void tag_function(const char* filename, unsigned tag, unsigned length,
+                  vector<unsigned>* result) {
+  unsigned long pos = gcov_position();
+
+  if (length) {
+    gcov_read_unsigned();  // ident %u
+    unsigned lineno_checksum = gcov_read_unsigned();
+    result->push_back(lineno_checksum);
+    gcov_read_unsigned();  // cfg_checksum 0x%08x
+  }
+}
+
+
+void tag_blocks(const char* filename, unsigned tag, unsigned length,
+                vector<unsigned>* result) {
+  unsigned n_blocks = GCOV_TAG_BLOCKS_NUM(length);
+  printf("%s: %u blocks\n", __FUNCTION__, n_blocks);
+}
+
+
+void tag_arcs(const char* filename, unsigned tag, unsigned length,
+              vector<unsigned>* result) {
+  unsigned n_arcs = GCOV_TAG_ARCS_NUM(length);
+  printf("%s: %u arcs\n", __FUNCTION__, n_arcs);
+}
+
+
+void tag_lines(const char* filename, unsigned tag, unsigned length,
+               vector<unsigned>* result) {
+  printf("%s\n", __FUNCTION__);
+}
+
+
+vector<unsigned>* parse_gcda_file(const char* filename) {
+  unsigned tags[4];
+  unsigned depth = 0;
+  vector<unsigned>* result;
+
+  result = new vector<unsigned>();
+  if (!gcov_open(filename, 1)) {
+    fprintf(stderr, "%s:cannot open\n", filename);
+    return result;
+  }
+
+  /* magic */
+  {
+    unsigned magic = gcov_read_unsigned ();
+    unsigned version;
+    const char* type = NULL;
+    int endianness = 0;
+    char m[4], v[4];
+
+    if ((endianness = gcov_magic (magic, GCOV_DATA_MAGIC))) {
+      type = "data";
+    } else {
+      printf ("%s:not a gcov file\n", filename);
+      gcov_close();
+      return result;
+    }
+    version = gcov_read_unsigned();
+    GCOV_UNSIGNED2STRING(v, version);
+    GCOV_UNSIGNED2STRING(m, magic);
+    if (version != GCOV_VERSION) {
+      char e[4];
+      GCOV_UNSIGNED2STRING (e, GCOV_VERSION);
+    }
+  }
+
+  gcov_read_unsigned();  // stamp
+  int cnt = 0;
+  bool found;
+  while (1) {
+    unsigned base, position = gcov_position();
+    unsigned tag, length;
+    tag_format_t const* format;
+    unsigned tag_depth;
+    int error;
+    unsigned mask;
+
+    tag = gcov_read_unsigned();
+    if (!tag)
+      break;
+
+    length = gcov_read_unsigned();
+    base = gcov_position();
+    mask = GCOV_TAG_MASK(tag) >> 1;
+    for (tag_depth = 4; mask; mask >>= 8) {
+      if ((mask & 0xff) != 0xff) {
+        printf ("%s:tag `%08x' is invalid\n", filename, tag);
+        break;
+      }
+      tag_depth--;
+    }
+    found = false;
+    for (format = tag_table; format->name; format++) {
+      if (format->tag == tag) {
+        found = true;
+        break;
+      }
+    }
+    if (!found) format = &tag_table[GCOV_TAG_IS_COUNTER (tag) ? 2 : 1];
+
+    if (tag) {
+      if (depth && depth < tag_depth) {
+        if (!GCOV_TAG_IS_SUBTAG (tags[depth - 1], tag))
+          printf ("%s:tag `%08x' is incorrectly nested\n",
+                  filename, tag);
+      }
+      depth = tag_depth;
+      tags[depth - 1] = tag;
+    }
+    if (format->proc) {
+      (*format->proc) (filename, tag, length, result);
+    }
+    gcov_sync (base, length);
+    if ((error = gcov_is_error ())) {
+      printf(error < 0 ? "%s:counter overflow at %lu\n" :
+             "%s:read error at %lu\n", filename,
+             (long unsigned) gcov_position());
+      break;
+    }
+  }
+  gcov_close();
+  return result;
+}
+
+}  // namespace vts
+}  // namespace android
+
diff --git a/sysfuzzer/libcodecoverage/gcda_parser.h b/sysfuzzer/libcodecoverage/gcda_parser.h
new file mode 100644
index 0000000..599e03c
--- /dev/null
+++ b/sysfuzzer/libcodecoverage/gcda_parser.h
@@ -0,0 +1,28 @@
+#ifndef __VTS_SYSFUZZER_LIBMEASUREMENT_GCDA_PARSER_H__
+#define __VTS_SYSFUZZER_LIBMEASUREMENT_GCDA_PARSER_H__
+
+#include <vector>
+
+using namespace std;
+
+namespace android {
+namespace vts {
+
+typedef struct tag_format {
+  unsigned tag;
+  char const* name;
+  void (*proc)(const char*, unsigned, unsigned, vector<unsigned>*);
+} tag_format_t;
+
+void tag_counters(const char* filename, unsigned tag, unsigned length, vector<unsigned>* result);
+void tag_function(const char* filename, unsigned tag, unsigned length, vector<unsigned>* result);
+void tag_blocks(const char* filename, unsigned tag, unsigned length, vector<unsigned>* result);
+void tag_arcs(const char* filename, unsigned tag, unsigned length, vector<unsigned>* result);
+void tag_lines(const char* filename, unsigned tag, unsigned length, vector<unsigned>* result);
+
+vector<unsigned>* parse_gcda_file(const char* filename);
+
+}  // namespace vts
+}  // namespace android
+
+#endif
diff --git a/sysfuzzer/libcodecoverage/gcda_parser_test.cpp b/sysfuzzer/libcodecoverage/gcda_parser_test.cpp
new file mode 100644
index 0000000..36a191c
--- /dev/null
+++ b/sysfuzzer/libcodecoverage/gcda_parser_test.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * 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
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "gcda_parser.h"
+#include "gcda_basic_io.h"
+
+#include <vector>
+#include <iostream>
+
+/*
+ * To test locally:
+ * $ rm a.out; gcc gcda_parser.cpp gcov_basic_io.cpp -lstdc++; ./a.out
+ */
+
+using namespace std;
+
+int main() {
+  std::vector<unsigned>* result = android::vts::parse_gcda_file("testdata/lights.gcda");
+  if (result) {
+    for (unsigned int index = 0; index < result->size(); index++) {
+      cout << result->at(index) << endl;
+    }
+    delete result;
+  }
+  return 0;
+}
diff --git a/sysfuzzer/libcodecoverage/gcov_basic_io.cpp b/sysfuzzer/libcodecoverage/gcov_basic_io.cpp
new file mode 100644
index 0000000..0dc8c7b
--- /dev/null
+++ b/sysfuzzer/libcodecoverage/gcov_basic_io.cpp
@@ -0,0 +1,202 @@
+#include "gcov_basic_io.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <inttypes.h>
+
+namespace android {
+namespace vts {
+
+struct gcov_var_t gcov_var;
+
+
+unsigned gcov_position (void) {
+  return gcov_var.start + gcov_var.offset;
+}
+
+
+int gcov_is_error() {
+  return gcov_var.file ? gcov_var.error : 1;
+}
+
+
+static inline unsigned from_file(unsigned value) {
+  if (gcov_var.endian) {
+    value = (value >> 16) | (value << 16);
+    value = ((value & 0xff00ff) << 8) | ((value >> 8) & 0xff00ff);
+  }
+  return value;
+}
+
+
+unsigned gcov_read_string_array(
+    char **string_array, unsigned num_strings) {
+  unsigned i, j, len = 0;
+
+  for (j = 0; j < num_strings; j++) {
+    unsigned string_len = gcov_read_unsigned ();
+    string_array[j] =
+      (char *) malloc (string_len * sizeof (unsigned));  // xmalloc
+    for (i = 0; i < string_len; i++)
+      ((unsigned *) string_array[j])[i] = gcov_read_unsigned ();
+    len += (string_len + 1);
+  }
+  return len;
+}
+
+
+unsigned gcov_read_unsigned() {
+  unsigned value;
+  const unsigned* buffer = gcov_read_words(1);
+
+  if (!buffer) return 0;
+  value = from_file(buffer[0]);
+  return value;
+}
+
+
+void gcov_allocate(unsigned length) {
+  size_t new_size = gcov_var.alloc;
+
+  if (!new_size) new_size = GCOV_BLOCK_SIZE;
+  new_size += length;
+  new_size *= 2;
+  gcov_var.alloc = new_size;
+  gcov_var.buffer = (unsigned*) realloc(gcov_var.buffer, new_size << 2);
+}
+
+
+const unsigned* gcov_read_words(unsigned words) {
+  const unsigned* result;
+  unsigned excess = gcov_var.length - gcov_var.offset;
+
+  assert(gcov_var.mode > 0);
+  if (excess < words) {
+    gcov_var.start += gcov_var.offset;
+    memmove(gcov_var.buffer, gcov_var.buffer + gcov_var.offset, excess * 4);
+    gcov_var.offset = 0;
+    gcov_var.length = excess;
+    if (gcov_var.length + words > gcov_var.alloc) {
+      gcov_allocate (gcov_var.length + words);
+    }
+    excess = gcov_var.alloc - gcov_var.length;
+    excess = fread(gcov_var.buffer + gcov_var.length, 1, excess << 2,
+                   gcov_var.file) >> 2;
+    gcov_var.length += excess;
+    if (gcov_var.length < words) {
+      gcov_var.overread += words - gcov_var.length;
+      gcov_var.length = 0;
+      return 0;
+    }
+  }
+  result = &gcov_var.buffer[gcov_var.offset];
+  gcov_var.offset += words;
+  return result;
+}
+
+
+int gcov_magic(unsigned magic, unsigned expected) {
+  if (magic == expected) return 1;
+  magic = (magic >> 16) | (magic << 16);
+  magic = ((magic & 0xff00ff) << 8) | ((magic >> 8) & 0xff00ff);
+  if (magic == expected) {
+    gcov_var.endian = 1;
+    return -1;
+  }
+  return 0;
+}
+
+
+gcov_type gcov_read_counter() {
+  gcov_type value;
+  const unsigned* buffer = gcov_read_words (2);
+  if (!buffer) return 0;
+  value = from_file (buffer[0]);
+  if (sizeof (value) > sizeof (unsigned)) {
+    value |= ((gcov_type) from_file (buffer[1])) << 32;
+  } else if (buffer[1]) {
+    gcov_var.error = -1;
+  }
+  return value;
+}
+
+
+void gcov_write_block(unsigned size) {
+  if (fwrite (gcov_var.buffer, size << 2, 1, gcov_var.file) != 1) {
+    gcov_var.error = 1;
+  }
+  gcov_var.start += size;
+  gcov_var.offset -= size;
+}
+
+
+const char* gcov_read_string() {
+  unsigned length = gcov_read_unsigned();
+  if (!length) return 0;
+  return (const char*) gcov_read_words(length);
+}
+
+
+bool gcov_open(const char* name, int mode) {
+  assert(!gcov_var.file);
+
+  gcov_var.start = 0;
+  gcov_var.offset = 0;
+  gcov_var.length = 0;
+  gcov_var.overread = -1u;
+  gcov_var.error = 0;
+  gcov_var.endian = 0;
+
+  if (mode >= 0) {
+    gcov_var.file = fopen(name, (mode > 0) ? "rb" : "r+b");
+  }
+
+  if (gcov_var.file) {
+    gcov_var.mode = 1;
+  } else if (mode <= 0) {
+    gcov_var.file = fopen(name, "w+b");
+    if (gcov_var.file) {
+      gcov_var.mode = mode * 2 + 1;
+    }
+  }
+  if (!gcov_var.file) return false;
+
+  setbuf(gcov_var.file, (char*)0);
+  return true;
+}
+
+
+void gcov_sync(unsigned base, unsigned length) {
+  assert(gcov_var.mode > 0);
+  base += length;
+  if (base - gcov_var.start <= gcov_var.length) {
+    gcov_var.offset = base - gcov_var.start;
+  } else {
+    gcov_var.offset = gcov_var.length = 0;
+    fseek(gcov_var.file, base << 2, SEEK_SET);
+    gcov_var.start = ftell(gcov_var.file) >> 2;
+  }
+}
+
+
+int gcov_close() {
+  if (gcov_var.file) {
+    if (gcov_var.offset && gcov_var.mode < 0) {
+      gcov_write_block(gcov_var.offset);
+    }
+    fclose(gcov_var.file);
+    gcov_var.file = 0;
+    gcov_var.length = 0;
+  }
+  free(gcov_var.buffer);
+  gcov_var.alloc = 0;
+  gcov_var.buffer = 0;
+  gcov_var.mode = 0;
+  return gcov_var.error;
+}
+
+}  // namespace vts
+}  // namespace android
diff --git a/sysfuzzer/libcodecoverage/gcov_basic_io.h b/sysfuzzer/libcodecoverage/gcov_basic_io.h
new file mode 100644
index 0000000..551d67e
--- /dev/null
+++ b/sysfuzzer/libcodecoverage/gcov_basic_io.h
@@ -0,0 +1,164 @@
+#ifndef __VTS_SYSFUZZER_LIBMEASUREMENT_GCOV_BASIC_IO_H__
+#define __VTS_SYSFUZZER_LIBMEASUREMENT_GCOV_BASIC_IO_H__
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <inttypes.h>
+
+namespace android {
+namespace vts {
+
+struct block_info;
+struct source_info;
+
+#define GCOV_DATA_MAGIC ((unsigned)0x67636461) /* "gcda" */
+
+#define GCOV_TAG_FUNCTION ((unsigned int) 0x01000000)
+#define GCOV_TAG_FUNCTION_LENGTH (3)   /* or 2 */
+#define GCOV_COUNTER_ARCS   0
+#define GCOV_TAG_ARCS    ((unsigned)0x01430000)
+#define GCOV_TAG_ARCS_LENGTH(NUM)  (1 + (NUM) * 2)
+#define GCOV_TAG_ARCS_NUM(LENGTH)  (((LENGTH) - 1) / 2)
+#define GCOV_TAG_LINES ((unsigned)0x01450000)
+#define GCOV_TAG_BLOCKS ((unsigned)0x01410000)
+#define GCOV_TAG_BLOCKS_LENGTH(NUM) (NUM)
+#define GCOV_TAG_BLOCKS_NUM(LENGTH) (LENGTH)
+#define GCOV_TAG_OBJECT_SUMMARY  ((unsigned)0xa1000000)
+#define GCOV_TAG_COUNTER_NUM(LENGTH) ((LENGTH) / 2)
+
+#define GCOV_HISTOGRAM_SIZE 252
+#define GCOV_HISTOGRAM_BITVECTOR_SIZE (GCOV_HISTOGRAM_SIZE + 31) / 32
+#define GCOV_COUNTERS_SUMMABLE (GCOV_COUNTER_ARCS + 1)
+
+#define GCOV_TAG_IS_COUNTER(TAG) \
+  (!((TAG) & 0xFFFF) && GCOV_COUNTER_FOR_TAG (TAG) < GCOV_COUNTERS)
+
+#define GCOV_TAG_MASK(TAG) (((TAG) - 1) ^ (TAG))
+
+/* Return nonzero if SUB is an immediate subtag of TAG.  */
+#define GCOV_TAG_IS_SUBTAG(TAG,SUB) \
+  (GCOV_TAG_MASK (TAG) >> 8 == GCOV_TAG_MASK (SUB) \
+   && !(((SUB) ^ (TAG)) & ~GCOV_TAG_MASK (TAG)))
+
+/* Return nonzero if SUB is at a sublevel to TAG. */
+#define GCOV_TAG_IS_SUBLEVEL(TAG,SUB) \
+    (GCOV_TAG_MASK (TAG) > GCOV_TAG_MASK (SUB))
+
+
+typedef long long gcov_type;
+
+enum {
+GCOV_COUNTERS
+};
+
+
+#define assert(EXPR) { if (!(EXPR)) exit(-1); }
+
+#define GCOV_VERSION ((unsigned)0x34303670)  /* 406p */
+
+#define GCOV_TAG_BUILD_INFO ((unsigned)0xa7000000)
+#define GCOV_TAG_PROGRAM_SUMMARY ((unsigned)0xa3000000)
+
+#define XCNEWVEC(T, N) ((T*) calloc((N), sizeof(T)))
+
+#define GCOV_TAG_COUNTER_BASE ((unsigned) 0x01a10000)
+#define GCOV_TAG_COUNTER_LENGTH(NUM) ((NUM) * 2)
+
+/* Convert a counter index to a tag. */
+#define GCOV_TAG_FOR_COUNTER(COUNT) \
+  (GCOV_TAG_COUNTER_BASE + ((unsigned)(COUNT) << 17))
+
+/* Convert a tag to a counter. */
+#define GCOV_COUNTER_FOR_TAG(TAG) \
+  ((unsigned)(((TAG) - GCOV_TAG_COUNTER_BASE) >> 17))
+
+/* Check whether a tag is a counter tag.  */
+#define GCOV_TAG_IS_COUNTER(TAG) \
+  (!((TAG) & 0xFFFF) && GCOV_COUNTER_FOR_TAG (TAG) < GCOV_COUNTERS)
+
+#define GCOV_UNSIGNED2STRING(ARRAY,VALUE) \
+  ((ARRAY)[0] = (char)((VALUE) >> 24), \
+   (ARRAY)[1] = (char)((VALUE) >> 16), \
+   (ARRAY)[2] = (char)((VALUE) >> 8), \
+   (ARRAY)[3] = (char)((VALUE) >> 0))
+
+#define GCOV_BLOCK_SIZE (1 << 10)
+
+
+typedef struct arc_info {
+  /* source and destination blocks.  */
+  struct block_info *src;
+  struct block_info *dst;
+
+  /* transition counts.  */
+  gcov_type count;
+  /* used in cycle search, so that we do not clobber original counts.  */
+  gcov_type cs_count;
+
+  unsigned int count_valid : 1;
+  unsigned int on_tree : 1;
+  unsigned int fake : 1;
+  unsigned int fall_through : 1;
+
+  /* Arc to a catch handler. */
+  unsigned int is_throw : 1;
+
+  /* Arc is for a function that abnormally returns.  */
+  unsigned int is_call_non_return : 1;
+
+  /* Arc is for catch/setjmp. */
+  unsigned int is_nonlocal_return : 1;
+
+  /* Is an unconditional branch. */
+  unsigned int is_unconditional : 1;
+
+  /* Loop making arc.  */
+  unsigned int cycle : 1;
+
+  /* Next branch on line.  */
+  struct arc_info *line_next;
+
+  /* Links to next arc on src and dst lists.  */
+  struct arc_info *succ_next;
+  struct arc_info *pred_next;
+} arc_t;
+
+
+struct gcov_var_t
+{
+  FILE *file;
+  unsigned start;  /* Position of first byte of block */
+  unsigned offset;    /* Read/write position within the block.  */
+  unsigned length;    /* Read limit in the block.  */
+  unsigned overread;    /* Number of words overread.  */
+  int error;      /* < 0 overflow, > 0 disk error.  */
+  int mode;                 /* < 0 writing, > 0 reading */
+  int endian;     /* Swap endianness.  */
+  /* Holds a variable length block, as the compiler can write
+     strings and needs to backtrack.  */
+  size_t alloc;
+  unsigned *buffer;
+};
+
+
+unsigned gcov_position (void);
+int gcov_is_error();
+unsigned gcov_read_string_array(char **string_array, unsigned num_strings);
+unsigned gcov_read_unsigned();
+void gcov_allocate(unsigned length);
+const unsigned* gcov_read_words(unsigned words);
+int gcov_magic(unsigned magic, unsigned expected);
+gcov_type gcov_read_counter();
+void gcov_write_block(unsigned size);
+const char* gcov_read_string();
+bool gcov_open(const char *name, int mode);
+void gcov_sync(unsigned base, unsigned length);
+int gcov_close();
+
+}  // namespace vts
+}  // namespace android
+
+#endif
diff --git a/sysfuzzer/libcodecoverage/testdata/lights.gcda b/sysfuzzer/libcodecoverage/testdata/lights.gcda
new file mode 100644
index 0000000..d200289
--- /dev/null
+++ b/sysfuzzer/libcodecoverage/testdata/lights.gcda
Binary files differ
diff --git a/sysfuzzer/libcodecoverage/testdata/lights.gcno b/sysfuzzer/libcodecoverage/testdata/lights.gcno
new file mode 100644
index 0000000..608d0fb
--- /dev/null
+++ b/sysfuzzer/libcodecoverage/testdata/lights.gcno
Binary files differ
diff --git a/sysfuzzer/libmeasurement/vts_measurement.cpp b/sysfuzzer/libmeasurement/vts_measurement.cpp
index 3664dc3..deb8e6e 100644
--- a/sysfuzzer/libmeasurement/vts_measurement.cpp
+++ b/sysfuzzer/libmeasurement/vts_measurement.cpp
@@ -23,15 +23,59 @@
 #endif
 #include <stdlib.h>
 
+#include <iostream>
+
+#define COVERAGE_SANCOV 0
+
+#if COVERAGE_SANCOV
+#include <sanitizer/coverage_interface.h>
+#endif
+
 #include <vector>
 
 using namespace std;
 
+#if COVERAGE_SANCOV
+
+extern "C" {
+// Re-declare some of the sanitizer functions as "weak" so that
+// libFuzzer can be linked w/o the sanitizers and sanitizer-coverage
+// (in which case it will complain at start-up time).
+__attribute__((weak)) void __sanitizer_print_stack_trace();
+__attribute__((weak)) void __sanitizer_reset_coverage();
+__attribute__((weak)) size_t __sanitizer_get_total_unique_caller_callee_pairs();
+__attribute__((weak)) size_t __sanitizer_get_total_unique_coverage();
+__attribute__((weak))
+void __sanitizer_set_death_callback(void (*callback)(void));
+__attribute__((weak)) size_t __sanitizer_get_number_of_counters();
+__attribute__((weak))
+uintptr_t __sanitizer_update_counter_bitset_and_clear_counters(uint8_t *bitset);
+}
+
+#define CHECK_WEAK_API_FUNCTION(fn)                                            \
+  do {                                                                         \
+    if (!fn)                                                                   \
+      MissingWeakApiFunction(#fn);                                             \
+  } while (false)
+
+static void MissingWeakApiFunction(const char *FnName) {
+  cerr << "ERROR: " << FnName << " is not defined. Exiting.\n"
+      << "Did you use -fsanitize-coverage=... to build your code?" << endl;
+  exit(1);
+}
+#endif
 
 namespace android {
 namespace vts {
 
 void VtsMeasurement::Start() {
+#if COVERAGE_SANCOV
+  cout << "reset coverage";
+  CHECK_WEAK_API_FUNCTION(__sanitizer_reset_coverage);
+  __sanitizer_reset_coverage();
+  cout << endl;
+#endif
+
 #if USE_CTIME
   gettimeofday(&tv_, NULL);
 #else
@@ -65,6 +109,11 @@
   result->push_back(stop_time_usecs - start_time_usecs);
 #endif
 
+#if COVERAGE_SANCOV
+  cout << "coverage: ";
+  cout << __sanitizer_get_total_unique_caller_callee_pairs() << endl;
+#endif
+
   return result;
 }